项目需求
通过温湿度传感器将值传到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_Hvoid 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)延时 15msHAL_Delay(15);//(2)写指令 38H(不检测忙信号)Write_cmd_Func(0x38);//(3)延时 5msHAL_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_Hvoid 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;}}
效果显示