硬件环境
本次实验所使用的硬件环境如下:
-
STM32F103C8T6开发板
-
DHT11温湿度传感器
-
OLED12864显示屏
-
杜邦线若干
-
软件环境
本次实验所使用的软件环境如下: -
Keil C51编译器
-
ST-LINK V2下载器
-
ST-LINK Utility软件
实验流程
硬件连接
首先,将STM32F103C8T6开发板、DHT11温湿度传感器和OLED12864显示屏分别连接在一起。
将DHT11温湿度传感器的VCC引脚连接到STM32F103C8T6开发板的5V引脚上。
将DHT11温湿度传感器的GND引脚连接到STM32F103C8T6开发板的GND引脚上。
将DHT11温湿度传感器的DATA引脚连接到STM32F103C8T6开发板的PB0引脚上。
将OLED12864显示屏的SDA引脚连接到STM32F103C8T6开发板的PA6引脚上。
将OLED12864显示屏的SCL引脚连接到STM32F103C8T6开发板的PA7引脚上。
将OLED12864显示屏的VCC引脚连接到STM32F103C8T6开发板的5V引脚上。
将OLED12864显示屏的GND引脚连接到STM32F103C8T6开发板的GND引脚上。
软件编写
DHT11温湿度传感器的驱动
本次实验所使用的DHT11温湿度传感器的驱动程序如下:
#include "dht11.h"
/* 定义DHT11相关的宏 */
#define DHT11_PORT GPIOB
#define DHT11_PIN GPIO_Pin_0
/* 定义DHT11初始化函数 */
void DHT11_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = DHT11_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
GPIO_SetBits(DHT11_PORT, DHT11_PIN);
}
/* 定义延时函数 */
void delay_us(uint32_t us)
{
uint32_t i;
while(us--)
{
i = 10;
while(i--);
}
}
/* 定义读取数据函数 */
uint8_t DHT11_Read_Byte(void)
{
uint8_t i, byte = 0;
for(i = 0; i < 8; i++)
{
/* 等待50us */
delay_us(50);
/* 将DHT11_PIN置为高电平,等待DHT11的响应 */
GPIO_SetBits(DHT11_PORT, DHT11_PIN);
delay_us(30);
/* 判断DHT11的响应是否正确 */
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == RESET)
{
/* 等待DHT11拉低总线 */
delay_us(80);
/* 判断DHT11拉低总线的时间长短 */
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == SET)
{
byte |= (1 << (7 - i));
}
}
}
return byte;
}
/* 定义读取数据函数 */
uint8_t DHT11_Read_Data(uint8_t *temp, uint8_t *humi)
{
uint8_t buf[5], i;
/* 发送起始信号 */
GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
delay_ms(18);
GPIO_SetBits(DHT11_PORT, DHT11_PIN);
/* 等待DHT11的响应 */
delay_us(40);
/* 判断DHT11的响应是否正确 */
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == RESET)
{
/* 等待DHT11拉低总线 */
delay_us(80);
/* 判断DHT11拉低总线的时间长短 */
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == SET)
{
/* 等待DHT11将总线拉高 */
delay_us(80);
/* 读取40位数据 */
for(i = 0; i < 5; i++)
{
buf[i] = DHT11_Read_Byte();
}
/* 判断校验和是否正确 */
if
((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
{
*humi = buf[0];
*temp = buf[2];
return 0;
}
}
}
return 1;
}
以上是DHT11的驱动程序,通过以上的程序,我们可以将DHT11温湿度传感器的数据读取出来并进行校验,确保数据的正确性。下面是OLED 128x64的驱动程序:
#include "oled.h"
#include "i2c.h"
/* 定义OLED相关的宏 */
#define OLED_I2C_ADDRESS 0x78
#define OLED_CMD_MODE 0x00
#define OLED_DATA_MODE 0x40
/* 定义OLED写入函数 */
void OLED_Write(uint8_t reg, uint8_t data)
{
uint8_t buf[2];
buf[0] = reg;
buf[1] = data;
HAL_I2C_Master_Transmit(&hi2c1, OLED_I2C_ADDRESS, buf, 2, 1000);
}
/* 定义OLED初始化函数 */
void OLED_Init(void)
{
/* 复位OLED */
HAL_GPIO_WritePin(OLED_RST_GPIO_Port, OLED_RST_Pin, GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(OLED_RST_GPIO_Port, OLED_RST_Pin, GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(OLED_RST_GPIO_Port, OLED_RST_Pin, GPIO_PIN_SET);
/* 设置OLED显示参数 */
OLED_Write(OLED_CMD_MODE, 0xAE); // 关闭OLED显示
OLED_Write(OLED_CMD_MODE, 0xD5); // 设置时钟分频因子,震荡频率
OLED_Write(OLED_CMD_MODE, 0x80); // 分频因子=1,震荡频率=1000Hz
OLED_Write(OLED_CMD_MODE, 0xA8); // 设置驱动路数
OLED_Write(OLED_CMD_MODE, 0x3F); // 默认值0x3F(1/64)
OLED_Write(OLED_CMD_MODE, 0xD3); // 设置显示偏移
OLED_Write(OLED_CMD_MODE, 0x00); // 默认值0x00
OLED_Write(OLED_CMD_MODE, 0x40); // 设置显示开始行 [5:0],默认值0
OLED_Write(OLED_CMD_MODE, 0x8D); // 电荷泵设置
OLED_Write(OLED_CMD_MODE, 0x14); // bit2,开启/关闭
OLED_Write(OLED_CMD_MODE, 0x20); // 设置内存地址模式
OLED_Write(OLED_CMD_MODE, 0x02); // 默认值0x02,水平地址模式
OLED_Write(OLED_CMD_MODE, 0xA1); // 设置段重映射,bit0=0,左右反转
OLED_Write(OLED_CMD_MODE, 0xC8); // 设置行重映射,bit3=0,上下反转
OLED_Write(OLED_CMD_MODE, 0xDA); // 设置COM硬件引脚配置
OLED_Write(OLED_CMD_MODE, 0x12); // bit5=1,使能COM左右反转
/* 清空OLED显示 */
OLED_Clear();
}
/* 定义OLED清屏函数 */
void OLED_Clear(void)
{
uint8_t i, j;
for (i = 0; i < 8; i++)
{
OLED_Write(OLED_CMD_MODE, 0xB0 + i); // 设置页地址
OLED_Write(OLED_CMD_MODE, 0x00); // 设置列地址的高位
OLED_Write(OLED_CMD_MODE, 0x10); // 设置列地址的低位
for (j = 0; j < 128; j++)
{
OLED_Write(OLED_DATA_MODE, 0x00); // 清空列数据
}
}
}
/* 定义OLED显示字符函数 */
void OLED_ShowChar(uint8_t x, uint8_t y, char ch)
{
uint8_t i, j;
ch = ch - ' '; // 获取字模偏移量
if (x > 120 || y > 56) // 判断是否越界
return;
for (i = 0; i < 6; i++)
{
uint8_t byte = Font6x8[ch][i];
for (j = 0; j < 8; j++)
{
if ((byte >> j) & 0x01) // 判断是否需要点亮
{
OLED_DrawPixel(x + i, y + j, OLED_PIXEL_ON);
}
else
{
OLED_DrawPixel(x + i, y + j, OLED_PIXEL_OFF);
}
}
}
}
/* 定义OLED画点函数 */
void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t pixel)
{
uint8_t page = y / 8; // 获取所在页地址
uint8_t column = x; // 获取所在列地址
uint8_t data;
OLED_Write(OLED_CMD_MODE, 0xB0 + page); // 设置页地址
OLED_Write(OLED_CMD_MODE, (column >> 4) | 0x10); // 设置列地址的高位
OLED_Write(OLED_CMD_MODE, column & 0x0F); // 设置列地址的低位
data = OLED_ReadByte(); // 读取原来的数据
if (pixel == OLED_PIXEL_ON)
{
data |= 1 << (y % 8); // 点亮像素
}
else
{
data &= ~(1 << (y % 8)); // 关闭像素
}
OLED_Write(OLED_DATA_MODE, data); // 写入新数据
}
/* 定义OLED读取一个字节的函数 */
uint8_t OLED_ReadByte(void)
{
uint8_t data;
HAL_I2C_Master_Receive(&hi2c1, OLED_I2C_ADDRESS, &data, 1, 1000);
return data;
}
完整代码实现
上述代码只是OLED12864显示屏的部分驱动程序,完整代码如下:
#include "dht11.h"
#include "oled12864.h"
int main(void)
{
uint8_t temp, humi;
char str_temp[10], str_humi[10];
DHT11_Init();
OLED_Init();
OLED_Clear();
OLED_ShowString(0, 0, "DHT11 Test:");
OLED_ShowString(0, 2, "Temp:");
OLED_ShowString(0, 4, "Humi:");
while(1)
{
if(DHT11_Read_Data(&temp, &humi) == SUCCESS)
{
OLED_ShowString(48, 2, str_temp);
OLED_ShowString(48, 4, str_humi);
delay_ms(1000);
}
}
return 0;
}
在主函数中,我们先调用DHT11_Init()和OLED_Init()函数来初始化DHT11温湿度传感器和OLED12864显示屏。然后调用OLED_Clear()函数来清屏,并使用OLED_ShowString()函数在OLED屏幕上显示“DHT11 Test:”、“Temp:”和“Humi:”这几个字符串。
之后进入while循环,在循环中调用DHT11_Read_Data()函数来读取温湿度数据,如果读取成功,则将温度和湿度数据转换成字符串格式,并通过OLED_ShowString()函数显示在屏幕上。
在这个示例代码中,我们仅仅显示了温度和湿度数据,并没有将其存储到外部存储器中。如果需要将数据存储到外部存储器中,可以使用SPI、I2C等接口来控制外部存储器,将数据写入其中。
总结
本文介绍了如何使用STM32驱动DHT11温湿度传感器,并通过OLED12864显示屏将读取到的温湿度数据实时显示在屏幕上。在实现过程中,我们需要理解DHT11传感器的工作原理和通信协议,掌握STM32的GPIO控制和定时器中断等技术,并能够使用OLED12864显示屏的驱动程序来控制显示屏。通过本文的学习,我们可以深入理解嵌入式系统中传感器的应用,为以后的项目开发奠定坚实的基础。