项目需求
通过温湿度传感器将值传到LCD1602,并实时通过蓝牙透传到手机。
硬件介绍
温湿度传感器 DHT11温湿度传感器 DHT11_温湿度传感器数据格式-CSDN博客
LCD1602LCD1602-CSDN博客
HC-01
继电器模块
硬件接线
LCD1602
- D0~D7 --> A0~A7
- VDD, A --> 5v
- VSS, VO, K --> GND
- RS --> B1
- RW --> B2
- E --> B10
DHT11
- VCC --> 5v
- GND --> GND
- DATA --> B7
HC-01(也可不接,和电脑串口显示一样的,透传模式)
- VCC --> 5v
- GND --> GND
- RXD --> TX1
- TXD --> RX1
各个硬件在先前学习了,就不再一一讲述,此项目仅做代码的移植
CubeMX
1.常规配置
SYS->Debug->Serial Wire
RCC->High Speed Clock(HSE)->Crystal/Ceramic Resonator
时钟树HSE、PLLCLK打开,HCLK设置成72MHz
打开GPIO口
打开uart1
框内的是在CubeMx重定义的引脚名称,会在mian.h中显示
其中,DHT11的DATA线既作为输入也作为输出,因此不能在CubeMX中进行配置!
为了方便阅读与编写,可在main.h文件重定义引脚
2.时钟
由于hal_delay()只是ms级别的延时,而DHT11中需要us的延时,则需要重新写延时函数,这里我们可以使用软件延时或者硬件延时
软件延时
void delay_us(uint16_t cnt)
{
uint8_t i;
while(cnt)
{
for (i = 0; i < 10; i++)
{
}
cnt--;
}
}
硬件延时:打开定时器
计数一次经过的时间是 (PSC + 1) / Tclk , 因此如果我想要计数1微秒,即0.000001s, 已知Tclk = 72 000 000, 那么PSC就应该设置为 71。然后在main.c中就可以定义出一个实现微秒级延时的函数:
STM32的HAL库开发各函数意义、笔记_hal_tim_set_counter-CSDN博客
void TIM1_Delay_us(uint16_t n_us) //使用TIM1来做us级延时函数 一个基于STM32 HAL库的TIM1(定时器1)进行微秒级延时的功能。
{
/* 使能定时器1计数 */
__HAL_TIM_ENABLE(&htim1);
__HAL_TIM_SetCounter(&htim1, 0);
while(__HAL_TIM_GetCounter(&htim1) < ((1 * n_us)-1) );
/* 关闭定时器1计数 */
__HAL_TIM_DISABLE(&htim1);
}
3.生成代码
keil
1. 打开MICRO-LIB
2. 编写代码:
关于上面提到的DHT的DATA引脚,由于有时需要作为输入,有时需要输出,所以不能在Cube中进行定义,而是在Keil中自己写代码定义,可以直接参考main函数中main.c的MX_GPIO_Init()函数,经过跳转可以看到详细信息:
如图,需要:
- 定义一个结构体
- 使能时钟
- 赋初值(这步可省略)
- 对于结构体的成员变量进行赋值,分别是GPIO_InitStruct.Pin; GPIO_InitStruct.Mode;
- GPIO_InitStruct.Speed,其中GPIO_InitStruct.Mode就是需要根据情况修改的
- 调用HAL_GPIO_Init 函数
void DHT11_Init(uint32_t Mode) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_7; GPIO_InitStruct.Mode = Mode; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); }
然后,
就可以在需要往DATA写数据的时候写:DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);
在需要从DATA读数据的时候写:DHT_GPIO_Init(GPIO_MODE_INPUT);
并且,由于89C52和STM32的硬件差别,使用STM32来驱动LCD1602时,是不需要检测BUSY信号的!!!
但是32驱动LCD的时候,在写命令或内容时,需要用到us级的延时,故要重新编写延时函数
分文件编写,要将相应的.c和.h文件分别添加到inc和src文件中,在新添加的文件在CubeMx重新更改的配置重新生成的文件并不会消失。
main.h
#define RS_GPIO_Pin GPIO_PIN_1
#define RS_GPIO_GPIO_Port GPIOB
#define RW_GPIO_Pin GPIO_PIN_2
#define RW_GPIO_GPIO_Port GPIOB
#define EN_GPIO_Pin GPIO_PIN_10
#define EN_GPIO_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */
#define DHT_11_GPIO GPIOB
#define DHT_11_PIN GPIO_PIN_7
#define DHT_11_LOW HAL_GPIO_WritePin(DHT_11_GPIO,DHT_11_PIN,GPIO_PIN_RESET)
#define DHT_11_HIGH HAL_GPIO_WritePin(DHT_11_GPIO,DHT_11_PIN,GPIO_PIN_SET)
#define DHT_11_VALUE HAL_GPIO_ReadPin(DHT_11_GPIO,DHT_11_PIN)
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_SET)
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_RESET)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_SET)
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_RESET)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_SET)
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_RESET)
main.c
#include "stdio.h"
#include "lcd1602.h"
#include "dht11.h"
extern char datas[5];
char temperature[9];
char humidity[9];
void Build_datas()
{
humidity[0] = 'H';
humidity[1] = ':';
humidity[2] = datas[0]/10 + 0x30;
humidity[3] = datas[0]%10 + 0x30;
humidity[4] = '.';
humidity[5] = datas[1]/10 + 0x30;
humidity[6] = datas[1]%10 + 0x30;
humidity[7] = '%';
humidity[8] = '\0';
temperature[0] = 'T';
temperature[1] = ':';
temperature[2] = datas[2]/10 + 0x30;
temperature[3] = datas[2]%10 + 0x30;
temperature[4] = '.';
temperature[5] = datas[3]/10 + 0x30;
temperature[6] = datas[3]%10 + 0x30;
temperature[7] = 'C';
temperature[8] = '\0';
}
int fputc(int a, FILE *f) //一个字符一个字符发送
{
unsigned char temp[1] = {a};
HAL_UART_Transmit(&huart1, temp, 1, 0xffff);
return a;
}
printf("code hhh\r\n");
LCD1602_Init();
LCD1602_ShowLine(1,5,"shion");
LCD1602_ShowLine(2,5,"114514");
HAL_Delay(2000);
while (1)
{
Read_data_From_DHT();
printf("Temp ; %d,%d\t",datas[2],datas[3]);
printf("Humi ; %d,%d\r\n",datas[0],datas[1]);
Build_datas();
printf("%s\t",humidity);
printf("%s",temperature);
LCD1602_ShowLine(1,2,humidity);
LCD1602_ShowLine(2,2,temperature);
HAL_Delay(1000);
}
}
LCD1602.h
#ifndef __LCD1602_H
#define __LCD1602_H
void Write_cmd_Func(char cmd);//写入指令的函数
void Write_data_Func(char dataShow);//写入数据的函数
void LCD1602_Init(void);
void LCD1602_ShowLine(char row,char col,char *string);
#endif
LCD1602.c
#include "lcd1602.h"
#include "gpio.h"
#include "main.h"
void Write_cmd_Func(char cmd)//写入指令的函数
{
RS_LOW;//写指令
RW_LOW;
EN_LOW;
HAL_Delay(5);
GPIOA->ODR = cmd;
EN_HIGH;
HAL_Delay(5);
EN_LOW;
}
void Write_data_Func(char dataShow)//写入数据的函数
{
RS_HIGH;//写内容
RW_LOW;
EN_LOW;
HAL_Delay(5);
GPIOA->ODR = dataShow;
EN_HIGH;
HAL_Delay(5);
EN_LOW;
}
void LCD1602_Init(void)
{
//(1)延时 15ms
HAL_Delay(15);
//(2)写指令 38H(不检测忙信号)
Write_cmd_Func(0x38);
//(3)延时 5ms
HAL_Delay(5);
//(4)以后每次写指令,读/写数据操作均需要检测忙信号
//(5)写指令 38H:显示模式设置
Write_cmd_Func(0x38);
//(6)写指令 08H:显示关闭
Write_cmd_Func(0x08);
//(7)写指令 01H:显示清屏
Write_cmd_Func(0x01);
//(8)写指令 06H:显示光标移动设置
Write_cmd_Func(0x06);
//(9)写指令 0CH:显示开及光标设置
Write_cmd_Func(0x0C);
}
void LCD1602_ShowLine(char row,char col,char *string)
{
switch(row){
case 1:
Write_cmd_Func(0x80+col);//只要定下开始的位置,之后光标会自行移动
while(*string){
Write_data_Func(*string);
string++;
}
break;
case 2:
Write_cmd_Func(0x80+0x40+col);//只要定下开始的位置,之后光标会自行移动
while(*string){
Write_data_Func(*string);
string++;
}
break;
}
/*
char pos;
if(hang == 0){//如果第一行
pos = 0x80 + 0x00 + lie;
}else if(hang == 1){//如果第二行
pos = 0x80 + 0x40 + lie;
}
Check_Busy();
Write_data_Func(*string);
while(*string != '\0'){
Check_Busy();
Write_data_Func(*string);
string++;
}
*/
}
DHT11.h
#ifndef __DHT11_H
#define __DHT11_H
void Read_data_From_DHT();
#endif
dht11.c
#include "dht11.h"
#include "gpio.h"
#include "main.h"
uint8_t datas[5];
void delay_us(uint16_t cnt)
{
uint8_t i;
while(cnt)
{
for (i = 0; i < 10; i++)
{
}
cnt--;
}
}
void DHT11_Init(uint32_t Mode)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = Mode;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void DHT_Start()
{
DHT11_Init(GPIO_MODE_OUTPUT_PP);//设置为输出模式!!
DHT_11_HIGH;
DHT_11_LOW;
HAL_Delay(30);
DHT_11_HIGH;
DHT11_Init(GPIO_MODE_INPUT);//设置为输入模式!!
while(DHT_11_VALUE == 1);//不断读取直到DHT再次变低,跳出循环,说明模块响应
while(DHT_11_VALUE == 0);//不断读取直到DHT再次拉高,80us之后,再经过50us后DHT变成低电平,就代表要开始变高并开始传输数据了
while(DHT_11_VALUE == 1);//等待数据传输,不断读取直到DHT再次变低,如退出循环说明数据传输前的50us低电平开始了
}
void Read_data_From_DHT()
{
int i;//轮
int j;//每一轮读多少次
char temp;
char flag;
DHT_Start();
for(i = 0;i < 5; i++)
{//5组数据
for(j =0;j<8;j++)
{//每组数据8位
while(!DHT_11_VALUE);//等待上拉 dht = 1 退出循环
delay_us(40);//高电平持续26-28us是‘0’,高电平持续70us是'1',所以delay40us之后观察还是否是高电平
if(DHT_11_VALUE == 1)
{
flag = 1;
while(DHT_11_VALUE);//不断读取下拉的一瞬间,因为在开始传输数据之前要延迟80us,如果提前结束,下一次开始传输数据就会出错,因为“开始传输数据”是从判断上拉开始的
}
else
{
flag = 0;
}
temp = temp << 1;//temp左移一位,例如temp= 10101011 左移之后变成-》01010110(第一个数移出去,最后一位补零)
temp |= flag;//假如flag=1,temp=01010111
}
datas[i] = temp;
}
}
效果显示