现在将具体项目是如何完成的,其实非常简单.
1.仅需要的模块是 STM32MCU GPIO RCC SPI Delay OLED 这几个
2.先完成DHT11的库编写,就根据DHT11的用户手册来写就没有问题,先保证DHT11能够正常工作,读取温湿度
3.完成DHT11能够正常的读取温湿度通过USART串口发送到计算机(主要是保证DHT11能够工作)
DHT11.c 代码如下
[cpp] view plain copy
/******************************************************************************************
* DHT11温度传感器的驱动库文件 V1.0 创建人张超 创建日期 2017年7月4日
* 简介:
* 1. 一个很简单的温湿度控制器模块,就根据DHT11官方的使用手册来配置MCU与DHT11联系引脚的电平即可
* 2. 根据其用户手册来说:
* (1)[ MCU为输出模式],拉低引脚的电平超过18ms就提示DHT11开始检测
* (2)MCU拉低电平后再拉高电平20-40us等待DHT11信号
* (3)DHT11检测到MCU的信号后就发送响应信号,此时[ MCU为输入模式]
* (4)DHT11拉低总线保持80us给MCU发送响应信号,再拉高80us表示响应结束准备发送数据
* (5)DHT11没发送的1bit数据都是以50us低电平开始
* (6)DHT11表示0的是 : 拉低50us开始 再拉高26us - 28us 结束本次bit传输
* (7)DHT11表示1的是 : 拉低50us开始 再拉高70us 结束本次bit传输
* (8)调用STM32官方库函数的GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN)来读取DHT11引脚的电平输入就可以知道DHT11的温度检测数值
* (9)DHT11会一次传递5个字节,传递数据的流程分别是: 湿度高位;湿度低位;温度高位;温度低位;前四项的校验和
* (10)把DHT11采集到的温湿度值每次全部都赋值给 定义在头部的u8全局变量
* (11)最后声明两个函数用于最后的温度和湿度数据的收集保证函数的整洁性
* (12)直接在main主函数导入dht11.h的头文件,再调用该dht11.c的相关函数即可使用DHT11的采集数据(因为DHT11采集的是数字信号所以可以直接调用)
* 该说明创建于2017年7月4日,创建人teavamc,未经允许不得使用商业用途
*/
#include "dht11.h" //导入头文件dht11.h
static void DHT11_Input(void); //声明static函数DHT11_Input,用于配置DHT11的引脚为输入模式
static void DHT11_Output(void); //声明static函数DHT11_Output,用于配置DHT11的引脚为输出模式
/*定义全局变量,分别用于统计湿度高位;湿度低位;温度高位;温度低位*/
u8 DHT11_hem_high,DHT11_hem_low,DHT11_temp_high,DHT11_temp_low;
static void DHT11_Input(void) //设置DHT11数据引脚的输入模式的配置
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DHT11_RCC,ENABLE); //开启GPIOD的时钟
GPIO_InitStructure.GPIO_Pin = DHT11_PIN; //设置引脚为PD13
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速率50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置输入模式为上拉输入模式
GPIO_Init(DHT11_GPIO, &GPIO_InitStructure);//把上面的配置初始化
}
static void DHT11_Output(void)//设置DHT11数据引脚的输出模式的配置
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DHT11_RCC,ENABLE);//开启GPIOD时钟
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;//设置引脚为PD13
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//配置为推挽输出
GPIO_Init(DHT11_GPIO, &GPIO_InitStructure);//初始化
}
/*从这里开始就根据DHT11使用说明的流程进行,确保万无一失*/
void DHT11_Start(void)//DHT11的开始程序
{
delay_init();//调用Systick,开启
DHT11_Output();//设置为输出模式
DHT11_hem_high = 0;
DHT11_hem_low = 0;
DHT11_temp_high = 0;
DHT11_temp_low = 0;
}
u16 DHT11_ReadByte(void)//读取DHT11每个字节的函数
{
u8 temp=0,i,cout;//三个变量的含义(1)temp用于储存数据(2)i表示字节的位数(3)cout用于保证执行条件或函数
DHT11_Input();
for(i=0;i<8;i++)//这个for循环的意义就是依次读8位字节,所以只执行8次,
{
cout=1;//确保执行while
while(!DHT11_ReadBit() && cout++);//当 [读取到PE13的电平不为1 且 cout++都为真] 的条件下执行循环,否则跳出循环
delay_us(30);//延时30us,再读取PE13的状态
temp = temp << 1;//表示把上次的temp的值左移一位,因为DHT11读取的数据是先出高值再出低值,
if(DHT11_ReadBit() == Bit_SET)//如果 [ 读取到PE13的电平等于Bit_SET的值 为真 ] ,则执行表达式
{
temp |=1; //temp=temp|1,进行或运算,有1得1
}
//等待输出的电平为低电平,进入下一位数据接收
cout=1;
while(DHT11_ReadBit() && cout++);
}
return temp;//返回接收到的temp
}
u16 DHT11_ReadData(void)//读取DHT11的数据,把读取到的自己整合
{
u16 cout = 1;//用于保证语句执行的变量
u16 temp_high,temp_low,hem_high,hem_low,Check;//用于整合字节的变量,分别是温度高位,温度低位,湿度高位,湿度低位,校验和
DHT11_Output();//把引脚切换为输出模式
DHT11_ResetBit();//拉低引脚电平,就是输出高电平
delay_ms(20);//持续20ms,数据手册上说要至少拉低18ms,保证DHT11能够检测到起始信号
DHT11_SetBit();//抬高引脚的电平;也就是输出低电平
delay_us(30);//持续30us,数据手册上说的区间是20-40us
DHT11_Input();//设置PE13为输入模式
/* 如果MCU采集到PE13的数据输入是低电平的话则执行表达式 */
if(DHT11_ReadBit() == Bit_RESET)
{
cout = 1;//低电平响应信号,等待DHT11响应完毕
while(!DHT11_ReadBit() && cout++);
cout = 1;//高电平传输数据,等待DHT11传输完毕,持续26us - 28us表示的是0;持续70us表示的是1
while(DHT11_ReadBit() && cout++);
/*开始按顺序接收DHT11的回传数据*/
/*根据DHT11的使用说明一共会接收5个字节的数据*/
hem_high = DHT11_ReadByte();//第一个接收到的是湿度的高八位,温度整数数据
hem_low = DHT11_ReadByte();//第二个接收到的是湿度的低八位,温度小数数据
temp_high = DHT11_ReadByte();//第三个接受到的是温度的高八位,湿度整数数据
temp_low = DHT11_ReadByte();//第四个接收到的是温度的低八位,湿度小数数据
Check = DHT11_ReadByte();//最后接收到的是前四位的和,用于校验,确保精度
if(Check == ( temp_high + temp_low + hem_high + hem_low ))//校验和.如果前4个字节加起来的和等于Check
{
DHT11_hem_high = hem_high;//写入湿度整数变量
DHT11_hem_low = hem_low;//写入湿度小数变量
DHT11_temp_high = temp_high;//写入温度整数变量
DHT11_temp_low = temp_low;//写入温度小数变量
return 1;//返回1,确保有效,表示成功采集一次数据
}
else
{
return 0;//若不正确,否则返回0,表示未成功采集数据
}
}
return 0;
}
u16 DHT11_GetTemp(void) //获取温度值
{
/* 这句话的意思是 */
/* [把DHT11_temp_high左移8位] 再与[DHT11_temp_low]进行 或运算,有1得1 */
/* 因为DHT11只能采集整数数据,所以做了或运算后还是原先采集到的整数数据 */
return(DHT11_temp_high <<8 | DHT11_temp_low);
}
u16 DHT11_GetHem(void)//获取湿度值
{
/* [把DHT11_hem_high左移8位] 再与[DHT11_hem_low]进行 或运算,有1得1 */
return(DHT11_hem_high <<8 | DHT11_hem_low);
}
备注已经写的非常详细了,C文件写完再写一个头文件,最后再main.c文件中调用就可以了。
------更新:----
贴出我自己的DHT11.h的头文件
大家只需要更换相对应的引脚即可:
[cpp] view plain copy
#ifndef __DHT11_H //如果未定义__DHT11_H
#define __DHT11_H //初始化__DHT11_H
#include "stm32f10x.h" //调用STM32官方库
#include "stm32f10x_gpio.h" //调用STM32的GPIO库
#include "stm32f10x_rcc.h" //调用STM32的RCC库
#include "stdio.h" //调用C标准库
#include "delay_other.h" //调用延时函数
#define DHT11_GPIO GPIOD //定义DHT11_GPIO的引脚是GPIO_D
#define DHT11_PIN GPIO_Pin_13 //定义DHT11_PIN的引脚是GPIO_Pin_13
#define DHT11_RCC RCC_APB2Periph_GPIOD //定义DHT_RCC的时钟是GPIOD
#define DHT11_SetBit() GPIO_SetBits(DHT11_GPIO,DHT11_PIN) //设置DHT11的引脚输出高电平
#define DHT11_ResetBit() GPIO_ResetBits(DHT11_GPIO,DHT11_PIN) //设置DHT11的引脚输出低电平
#define DHT11_ReadBit() GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) //设置MCU读取DHT11的引脚电平
static void DHT11_Input(void); //声明静态函数DHT11_Input
static void DHT11_Output(void); //声明静态函数DHT11_Output
void DHT11_Start(void); //声明函数DHT11_Start,用于初始化DHT11
u16 DHT11_ReadByte(void); //声明函数DHT11_ReadByte,用于读取一个字节的数据
u16 DHT11_ReadData(void); //声明函数DHT11_ReadData,用于读取DHT11一次的总数据,一共5和字节,每个字节有8个位
u16 DHT11_GetTemp(void); //声明函数DHT11_GetTemp,用于总结温度变量的函数
u16 DHT11_GetHem(void); //声明函数DHT11_GetHem,用于总结湿度变量的函数
#endif //用于结束定义
------更新完毕-------
4,再进行OLED库的编写,其中可以借鉴相关的很多案例,你买OLED应该有商家最开始提供的案例
5,其中OLED库的编写要注意三部分 :
(1)字库,有了字库OLED才知道如何显示,字库的生成用字库软件。
(2) 显示函数, 就根据自己的实际情况来保证显示屏的基本的运行就可以了
(3)OLED显示屏的SPI或者I2C的设置。
这里就要认认真真的配置好SPI或者I2C的通信协议 和 OLED的引脚串口 ,有书的直接看野火或原子的书中相关部分,很容易就能配置好, OLED是配置好才可以点亮, 并不是插电就亮
6, 最后就是在主函数联动 DHT11和OLED了 因为DHT11输入的是数字信号 所以可以直接调用
贴上我的mian.c函数
[cpp] view plain copy
#include "stm32f10x.h" //官方库
#include "usart1.h" //串口库
#include "stdio.h" //C标准库
//#include "systick.h"
#include "dht11.h" //DHT11库
#include "led.h" //LED库
//#include "key.h"
#include "sys_other.h" //时钟库
#include "delay_other.h" //延时库
#include "oled_other.h" //oled库
#include "gpiooled.h" //oled_gpio库
#include "string.h" //C标准库
#include "i2cbh1750.h" //bh1750的i2c配置
#include "bh1750.h" //bh1750的驱动库
#include "pm.h" //导入pm2.5的驱动库
char strTemp[30]; //声明字符数组strTemp,初始化元素30
char strHumi[30]; //声明字符数组strHumi,初始化元素30
char strLux[30]; //声明字符数组strLight,初始化元素30
char strPm[30]; //声明字符数组strPm,初始化元素30
void PIGSys_Init(void) //设置一个初始化OLED获取DHT数据系统的函数
{
delay_init(); //调用延时函数
SPI_OLED_GPIO_Config(); //调用OLED的SPI初始化
DHT11_Start(); //调用DHT11的预备函数
USART1_Config(); //初始化串口(因为没有用到串口所以不需要))
LCD_Init(); //OLED屏幕的初始化
iic_init();//i2c协议的初始化
PM_GP2Y_Init();//pm2.5模块初始化
}
int main(void) //开始主函数
{
PIGSys_Init(); //调用之前的系统初始化函数用于初始化系统
/*这三个可以写进BH1750模块库,不必放在主函数,日后改进,2017-7-7*/
bh_data_send(BHPowOn); //BH1750光照传感器等待检测指令
bh_data_send(BHReset); //BH1750光照传感器总线重置模块寄存器
bh_data_send(BHModeH2);//BH1750光照传感器设置高分辨率模式
delay_ms(500); //延时500毫秒
delay_ms(500); //延时500毫秒
while(1)
{
if(DHT11_ReadData()) //如果DHT11读取到数据了,那么就显示
{
sprintf(strTemp,"温度:%d",DHT11_GetTemp()/256); //调用Sprintf函数把DHT11的温度数据格式化到字符串数组变量strTemp中
sprintf(strHumi,"湿度:%d",DHT11_GetHem()/256); //调用Sprintf函数把DHT11的湿度数据格式化到字符串数组变量strHumi中
sprintf(strLux,"光强:%4.1f",bh_data_read()/1.2); //调用Sprintf函数把BH1750的光照强度数据格式化到字符串数组变量strLux中
sprintf(strPm,"PM值:%5.1f",PM_Get_GP2Y_Data()); //调用Sprintf函数把GP2Y的PM2.5数据格式化到字符串数组变量strPm中
LCD_Print(0, 0, strTemp,TYPE16X16,TYPE8X16); //输出温度
LCD_Print(60,0, "度",TYPE16X16,TYPE8X16); //输出温度单位
LCD_Print(0, 16, strHumi,TYPE16X16,TYPE8X16); //输出湿度
LCD_Print(60,16, "百分比",TYPE16X16,TYPE8X16); //输出百分比单位
LCD_Print(0, 32, strLux,TYPE16X16,TYPE8X16); //输出光照强度
LCD_Print(90,32, "勒",TYPE16X16,TYPE8X16); //输出光照单位
LCD_Print(0, 48, strPm,TYPE16X16,TYPE8X16); //输出PM2.5
LCD_Print(88, 48, "ug/㎡",TYPE16X16,TYPE8X16); //输出PM单位
printf("智慧猪栏环境数据采集\n");
printf("******************************************************************\n");
printf("串口采集的数据:\n");
printf("温度:%d度\n湿度是:%d百分比\n光照强度是:%3.1fLx\nPM2.5是:%8.3fug/每平方米\n",DHT11_GetTemp()/256,DHT11_GetHem()/256,bh_data_read()/1.2,PM_Get_GP2Y_Data()); //用串口USART1显示DHT11的采集数据
printf("******************************************************************\n");
}
else //若DHT11采集不成功,那么就失败
{
LCD_Print(0, 0, "信息采集失败",TYPE16X16,TYPE8X16); //OLED显示失败
printf("系统采集失败,一定是哪个环节出了问题!\n"); //串口显示失败
}
delay_ms(500); //延时
delay_ms(500); //延时
delay_ms(500); //延时,避免刷新DHT11数据太块
}
}