STM32C8T6+DHT11显示温湿度/通过MQTT实现开发板与阿里云服务器收发
本教程基于韦东山百问网出的 DShanMCU-F103开发板 进行编写,需要的朋友可以在这里获取: https://item.taobao.com/item.htm?id=724601559592
配套资料获取:https://rtos.100ask.net/zh/freeRTOS/DShanMCU-F103
freeRTOS系列教程之freeRTOS入门与工程实践章节汇总: https://blog.csdn.net/qq_35181236/article/details/132842016
目录:
- 所需模块
- 接线示意图
- 主要代码部分
1.所需模块
- STM32C8T6开发板
- DHT11温湿度模块
- OLED(0.96四针)屏幕
- ESP8266WIFI模块
- CH340串口模块
- 多功能底板
再此推荐大家使用此底板,能连接很多模块,底板上面都有标注,使用起来很方便,(很适合小白)推荐给大家,开发板底板都是百问网的,推荐给大家 链接:https://item.taobao.com/item.htm?id=724601559592
效果展示给大家看一下,使用起来效果
2.接线示意图
- 开发板连接ST-LINK-V2
- 底板连接CH340串口模块
- 底板连接ESP8266WIFI模块
ESP8266 | DshanMCU_F103底板 |
---|---|
3.3V | 3.3V |
GND | GND |
RX | PA2 |
TX | PA3 |
3.主要代码部分
(1)首先我们先来实现STM32C8T6+DHT11通过OLED显示温湿度
底层驱动我就不放上来啦,详细代码可在文章最后Github获取
-
首先我们先写OLED驱动程序,测试OLED
-
OLED.c 部分代码
//7. 初始化函数 //初始化OLED void OLED_Init(void) { OLED_SetMemAddrMode(PAGE_ADDR_MODE); // 0. 设置地址模式 OLED_SetMuxRatio(0x3F); // 1. 设置多路复用率 OLED_SetDispOffset(0x00); // 2. 设置显示的偏移值 OLED_SetDispStartLine(0x00); // 3. 设置起始行 OLED_SEG_REMAP(); // 4. 行翻转 OLED_SCAN_REMAP(); // 5. 正常扫描 OLED_SetComConfig(COM_PIN_SEQ, COM_NOREMAP); // 6. COM 引脚设置 OLED_SetContrastValue(0x7F); // 7. 设置对比度 ENTIRE_DISP_OFF(); // 8. 全屏点亮/熄灭 DISP_NORMAL(); // 9. 显示模式 OLED_SetDCLK_Freq(0x00, 0x08); // 10. 设置分频系数和频率增值 OLED_SetChargePump(PUMP_ENABLE); // 11. 使能电荷碰撞 OLED_SetComConfig(COM_PIN_ALT, COM_NOREMAP); DISP_ON(); } //8. 基本驱动功能函数 //设置像素显示的起始页和起始列地址 void OLED_SetPosition(uint8_t page, uint8_t col) { OLED_SetPageAddr_PAGE(page); OLED_SetColAddr_PAGE(col); } //清屏函数 void OLED_Clear(void) { uint8_t i = 0; uint8_t buf[128] = {0}; for(i=0; i<8; i++) { OLED_SetPosition(i, 0); OLED_WriteNBytes(&buf[0], 128); } } //显示一个字符 /* * x --> x坐标(0~15) * y --> y坐标(0~7) * c --> 显示的字符 */ void OLED_PutChar(uint8_t x, uint8_t y, char c) { uint8_t page = y; uint8_t col = x*8; if (y > 7 || x > 15) return; OLED_SetPosition(page, col); OLED_WriteNBytes((uint8_t*)&ascii_font[c][0], 8); OLED_SetPosition(page + 1, col); OLED_WriteNBytes((uint8_t*)&ascii_font[c][8], 8); } //显示一个字符串 /*x --> x坐标(0~15) *y --> y坐标(0~7) *str --> 显示的字符串 */ int OLED_PrintString(uint8_t x, uint8_t y, const char *str) { int i = 0; while (str[i]) { OLED_PutChar(x, y, str[i]); x++; if(x > 15) { x = 0; y += 2; } i++; } return i; } /* * 清除一行 * x - 从这里开始 * y - 清除这行 */ void OLED_ClearLine(uint8_t x, uint8_t y) { for (;x < 16; x++) { OLED_PutChar(x, y, ' '); } } /* *以16进制显示数值 *x - x坐标(0~15) * y - y坐标(0~7) * val - 显示的数据 * pre - 非零时显示"0x"前缀 */ int OLED_PrintHex(uint8_t x, uint8_t y, uint32_t val, uint8_t pre) { uint8_t hex_tab[]={'0','1','2','3','4','5','6','7',\ '8','9','a','b','c','d','e','f'}; int i, j; char arr[11]; /* 提取数据 */ arr[0] = '0'; arr[1] = 'x'; arr[10] = '\0'; j = 2; for (i = 7; i >= 0; i--) { arr[j] = hex_tab[(val >> (i*4)) & 0xf]; if ((j != 2) || (arr[j] != '0')) { j++; } } if (j == 2) j++; arr[j] = '\0'; if (pre) { OLED_PrintString(x, y, arr); return j; } else { OLED_PrintString(x, y, arr+2); return j - 2; } } /* * 以10进制显示一个数值 * x --> x坐标(0~15) * y --> y坐标(0~7) * val --> 显示的数据 */ int OLED_PrintSignedVal(uint8_t x, uint8_t y, int32_t val) { char str[16]; char revert_str[16]; int i = 0, j = 0; int mod; if (val < 0) { str[i++] = '-'; val = 0 - val; } else if (val == 0) { str[i++] = '0'; } while (val) { mod = val % 10; revert_str[j++] = mod + '0'; val = val / 10; } while (j) { str[i++] = revert_str[j-1]; j--; } str[i] = '\0'; OLED_PrintString(x, y, str); return i; } //获得OLED的Framebuffer //pXres/pYres/pBpp - 用来保存分辨率、bpp void * OLED_GetFrameBuffer(uint32_t *pXres, uint32_t *pYres, uint32_t *pBpp) { *pXres = FRAME_WIDTH; *pYres = FRAME_HEIGHT; *pBpp = 1; return oledBuffer; } //把Framebuffer的数据全部刷新到OLED void OLED_Flush(void) { uint8_t i; for(i=0;i<8;i++) { OLED_WriteCmd(0xb0+i); //设置行起始地址 OLED_WriteCmd(0x00); //设置低列起始地址 OLED_WriteCmd(0x10); //设置高列起始地址 //for(n=0;n<128;n++) // OLED_WriteData(oledBuffer[128*i+n]); OLED_WriteNBytes(&oledBuffer[128*i], 128); } } //刷新OLED的区域 /* *col- OLED的列,取值范围0~127 *row - OLED的行,取值范围0~63 *width - 宽度 *heigh - 高度,必须是8的整数倍 */ void OLED_FlushRegion(int col, int row, int width, int heigh) { #if 0 uint8_t i; uint8_t page = row / 8; for (i = 0; i < heigh/8; i++) { OLED_SetPosition(page+i, col); OLED_WriteNBytes(&oledBuffer[col + (page + i) * FRAME_WIDTH], width); } #else uint8_t i; uint8_t page = row / 8; uint8_t page_cnt = heigh/8; if (row % 8) { page_cnt++; } for (i = 0; i < page_cnt; i++) { if (page+i < 8) { OLED_SetPosition(page+i, col); OLED_WriteNBytes(&oledBuffer[col + (page + i) * FRAME_WIDTH], width); } } #endif } //把Framebuffer的数据全部清零 void OLED_ClearFrameBuffer(void) { memset(oledBuffer, 0, sizeof(oledBuffer)); }
注意:本工程是通过CubeMX创建
初始化I2C
- 前提: 已经初始化的I2C通道
- 本工程里已经:
- 使用MX_I2C1_Init初始化I2C通道
- 使用HAL_I2C_MspInit初始化I2C引脚
-
OLED.h部分
#ifndef __DRIVER_OLED_H
#define __DRIVER_OLED_H
#include "stm32f1xx_hal.h"
#include <stdint.h>
#define FRAME_WIDTH 128
#define FRAME_HEIGHT 64
#define FRAME_BUFFER_SIZE ((FRAME_WIDTH * FRAME_HEIGHT) / 8)
//初始化OLED
void OLED_Init(void);
//设置像素显示的起始页和起始列地址
void OLED_SetPosition(uint8_t page, uint8_t col);
//清屏函数
void OLED_Clear(void);
//显示一个字符
void OLED_PutChar(uint8_t x, uint8_t y, char c);
//显示一个字符串
int OLED_PrintString(uint8_t x, uint8_t y, const char *str);
///清除一行
void OLED_ClearLine(uint8_t x, uint8_t y);
//以16进制显示数值
int OLED_PrintHex(uint8_t x, uint8_t y, uint32_t val, uint8_t pre);
//获得OLED的Framebuffer
int OLED_PrintSignedVal(uint8_t x, uint8_t y, int32_t val);
//获得OLED的Framebuffer
void * OLED_GetFrameBuffer(uint32_t *pXres, uint32_t *pYres, uint32_t *pBpp);
//把Framebuffer的数据全部刷新到OLED
void OLED_Flush(void);
//刷新OLED的区域
void OLED_FlushRegion(int col, int row, int width, int heigh);
//把Framebuffer的数据全部清零
void OLED_ClearFrameBuffer(void);
//OLED测试程序
void OLED_Test(void);
#endif /* __DRIVER_OLED_H */
-
LCD.c
//初始化LCD void LCD_Init(void) { OLED_Init(); } //清屏函数 void LCD_Clear(void) { OLED_Clear(); } //显示一个字符 void LCD_PutChar(uint8_t x, uint8_t y, char c) { OLED_PutChar(x, y, c); } //显示一个字符串 int LCD_PrintString(uint8_t x, uint8_t y, const char *str) { return OLED_PrintString(x, y, str); } //清除一行 void LCD_ClearLine(uint8_t x, uint8_t y) { OLED_ClearLine(x, y); } //以16进制显示数值 int LCD_PrintHex(uint8_t x, uint8_t y, uint32_t val, uint8_t pre) { return OLED_PrintHex(x, y, val, pre); } //以10进制显示一个数值 int LCD_PrintSignedVal(uint8_t x, uint8_t y, int32_t val) { return OLED_PrintSignedVal(x, y, val); } //获得LCD的Framebuffer void * LCD_GetFrameBuffer(uint32_t *pXres, uint32_t *pYres, uint32_t *pBpp) { return OLED_GetFrameBuffer(pXres, pYres, pBpp); } //把Framebuffer的数据全部刷新到LCD void LCD_Flush(void) { OLED_Flush(); } //刷新LCD的区域 void LCD_FlushRegion(int col, int row, int width, int heigh) { OLED_FlushRegion(col, row, width, heigh); } //把Framebuffer的数据全部清零 void LCD_ClearFrameBuffer(void) { OLED_ClearFrameBuffer(); }
使用LCD封装OLED,以后换屏幕都可以用LCD.c调用具体的屏幕驱动
记得要添加字库文件,然后就可以测试OLED屏幕了
屏幕测试
void OLED_Test(void) { OLED_Init(); // 清屏 OLED_Clear(); while (1) { // 在(0, 0)打印'A' OLED_PutChar(0, 0, 'A'); // 在(1, 0)打印'Y' OLED_PutChar(1, 0, 'Y'); // 在第0列第2页打印一个字符串"Hello World!" OLED_PrintString(0, 2, "Hello World!"); } }
效果展示:
-
接下来是DHT11将温湿度显示在屏幕上
-
DHT11.c 部分代码
//DHT11的初始化函数
void DHT11_Init(void)
{
DHT11_PinCfgAsOutput();
DHT11_PinSet(1);
//mdelay(2000);
}
//读取DHT11的温度/湿度
int DHT11_Read(int *hum, int *temp)
{
unsigned char hum_m, hum_n;
unsigned char temp_m, temp_n;
unsigned char check;
DHT11_Start();
if (0 != DHT11_Wait_Ack())
{
//printf("dht11 not ack, err!\n\r");
return -1;
}
if (0 != DHT11_WaitFor_Val(1, 1000)) /* 等待ACK变为高电平, 超时时间是1000us */
{
//printf("dht11 wait for ack high err!\n\r");
return -1;
}
if (0 != DHT11_WaitFor_Val(0, 1000)) /* 数据阶段: 等待低电平, 超时时间是1000us */
{
//printf("dht11 wait for data low err!\n\r");
return -1;
}
hum_m = DHT11_ReadByte();
hum_n = DHT11_ReadByte();
temp_m = DHT11_ReadByte();
temp_n = DHT11_ReadByte();
check = DHT11_ReadByte();
DHT11_PinCfgAsOutput();
DHT11_PinSet(1);
if (hum_m + hum_n + temp_m + temp_n == check)
{
*hum = hum_m;
*temp = temp_m;
return 0;
}
else
{
//printf("dht11 checksum err!\n\r");
return -1;
}
}
这样我们DHT11也完成啦,最后我们来测试一下
- DHT11测试代码
void DHT11_Test(void)
{
int hum, temp;
int len;
DHT11_Init();
while (1)
{
if (DHT11_Read(&hum, &temp) !=0 )
{
//printf("\n\rdht11 read err!\n\r");
DHT11_Init();
}
else
{
LCD_PrintString(0, 0, "DHT11:");
LCD_PrintString(0, 2, "Humidity Temp");
len = LCD_PrintSignedVal(0, 5, hum);
LCD_PutChar(len, 5, '%');
LCD_PrintSignedVal(11, 5, temp);
}
mdelay(2000); /* 读取周期是2S, 不能读太频繁 */
}
}
最终效果:
(2)接着来实现通过MQTT实现开发板与阿里云服务器收发
由于怕篇幅太长我把这个实验放在下一章