项目实际图
本次项目需要整合LCD1602、DHT11、HC08、继电器
1.首先是LCD1602显示程序
封装管脚,这样的话写时序的时候不用随时都在哪儿HAL_GPIO_WritePin
#define RS_GPIO_Port GPIOB
#define RW_GPIO_Port GPIOB
#define EN_GPIO_Port GPIOB
#define RS_Pin GPIO_PIN_1
#define RW_Pin GPIO_PIN_2
#define EN_Pin GPIO_PIN_10
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_SET)
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_RESET)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_SET)
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_RESET)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_SET)
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_RESET)
STM32不同于51,STM32的管脚比如PA0-PA7不能以此向直接写入一个字节的数据,必须操作寄存器
GPIOA -> ODR = cmd ;
根据时序图写操作(stm32可以不检查忙)
LCD.c
#define RS_GPIO_Port GPIOB
#define RW_GPIO_Port GPIOB
#define EN_GPIO_Port GPIOB
#define RS_Pin GPIO_PIN_1
#define RW_Pin GPIO_PIN_2
#define EN_Pin GPIO_PIN_10
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_SET)
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, GPIO_PIN_RESET)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_SET)
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port, RW_Pin, GPIO_PIN_RESET)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_SET)
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_RESET)
void Write_Cmd_Func(uint8_t cmd)
{
RS_LOW;
RW_LOW;
EN_LOW;
GPIOA->ODR = cmd;
EN_HIGH;
HAL_Delay(5);
EN_LOW;
HAL_Delay(5);
}
void Write_Data_Func(uint8_t dataShow)
{
RS_LOW;
RW_LOW;
EN_LOW;
GPIOA->ODR = dataShow;
EN_HIGH;
HAL_Delay(5);
EN_LOW;
HAL_Delay(5);
}
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 0:
Write_Cmd_Func(0x80+0x00+col);
while(*string != '\0')
{
Write_Data_Func(*string++);
}
break;
case 1:
Write_Cmd_Func(0x80+0x40+col);
while(*string != '\0')
{
Write_Data_Func(*string++);
}
break;
}
}
2.DHT11温湿度检测程序
具体关于DHT11的硬件结构以及说明可以查看51章节的文章
https://blog.csdn.net/weixin_63303786/article/details/128692300?spm=1001.2014.3001.5502https://blog.csdn.net/weixin_63303786/article/details/128692300?spm=1001.2014.3001.5502
IO口的几种输入输出状态
GPIO_MODE_INPUT 输入
GPIO_MODE_OUTPUT_PP 推挽输出
GPIO_MODE_OUTPUT_OD 开漏输出
GPIO_Mode_AF_OD 复用开漏输出模式
GPIO_Mode_AF_PP 复用推挽输出模式
GPIO_MODE_AF_INPUT 模拟输入(AD用)
1. 四种输入模式
GPIO_Mode_IN_FLOATING 浮空输入模式
GPIO_Mode_IPU 上拉输入模式
GPIO_Mode_IPD 下拉输入模式
GPIO_Mode_AIN 模拟输入模式2. 四种输出模式
GPIO_Mode_Out_OD 开漏输出模式
GPIO_Mode_Out_PP 推挽输出模式
GPIO_Mode_AF_OD 复用开漏输出模式
GPIO_Mode_AF_PP 复用推挽输出模式
根据时序图写出DHT11的读操作
DHT11.c
//首先检查DHT11是否存在,时序图黑色部分需要我们写程序实现,棕色部分则是读IO口实现
//所以DHT11所用IO口有两种状态:输入和输出,所以cubeMX中不配置IO口状态,用程序配置
/*封装引脚*/
#define DHT_HIGHT HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET)
#define DHT_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET)
#define DHT_VALUE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)
unsigned char datas[5]={0};//接收温湿度数据的数组
//可以现在cubeMX中随意配置一个管脚如PA1,然后去工程里找到GPIO初始化程序,模仿写出DHT_GPIO_Init
void DHT_GPIO_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);
}
//根据时序图写出strat(检查DHT11是否存在),读温度
void delay_us(uint16_t cnt)
{
uint8_t i;
while(cnt)
{
for (i = 0; i < 10; i++)
{
}
cnt--;
}
}
void DHT11_Start()
{
DHT_GPIO_Init((GPIO_MODE_OUTPUT_PP);//推挽输出
DHT_LOW;
HAL_Delay(20);
DHT_HIGHT;
DHT_GPIO_Init(GPIO_MODE_INPUT);
while(DHT_VALUE);//卡d点
while(!DHT_VALUE);//卡e点
while(DHT_VALUE);//卡f点
}
void Read_Data_From_DHT()
{
unsigned char flag,temp,i=0,j=0;
DHT11_Start();
DHT_GPIO_Init(GPIO_MODE_INPUT);
while(!DHT_VALUE);//卡g点,因为判断读0还是读1的时序图是IO口自己实现,不需要写程序拉低或者释放
delay_us(40);
for(i=0;i<5;i++)//总共接收5个数据,第0,第1是湿度的整数和小数,第2,第3是温度的整数和小数,第4是校验位
{
for(=0;j<8;j++)//一个数据八位,每次接收一位,接收八次
{
if(DHT_VALUE == 1)
flag = 1;
else if(DHT_VALUE == 0)
flag = 0;
temp <<= 1;
temp |= flag;//如果读0,最低位则为0(因为出初始化temp = 0000 0000),如果读1,则最低位为1,8次左移过后原本的最低位成最高位
}
datas[i] = temp;
}
}
总和LCD1602和DHT11代码完成项目要求
main.c
extern buf;
//重定向printf
int fputc(int ch,FILE*f)
{
unsigned char temp[1] = {ch};
HAL_UART_Transmit(&huart1,temp,strlen(temp),0xffff);
return ch;
}
void main()
{
unsigned char mssage[10];
while(1)
{
HAL_UART_Receive_IT(&huart1, &buf, 1)//开启串口中断
Read_Data_From_DHT();
if(datas[1] >= 30)//如果温度超过30度,继电器闭合
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
memset(message, 0, sizeof(message));
sprintf(mssage,"T:%d.%d",(unsigned int)datas[2],(unsigned int)datas[3]);
LCD1602_showLine(1,0,message);
memset(message, 0, sizeof(message));
sprintf(mssage,"H:%d.%d",(unsigned int)datas[0],(unsigned int)datas[1]);
LCD1602_showLine(2,0,message);
printf("T:%d.%d",(unsigned int)datas[2],(unsigned int)datas[3]);
printf("H:%d.%d",(unsigned int)datas[0],(unsigned int)datas[1]);
HAL_Delay(100);
}
}
uart.c
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART1_RX_STA=0;//串口接收标志位
//重写串口回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//判断是不是串口1产生的中断
if(huart->Instance == USART1)
{
//判断中断是否接收完成,接收完成USART_RX_STA最高位会置1
if((UART1_RX_STA & 0x8000) == 0)//如果没有接收完成就进入接收流程
{
//如果接收到回车:0X0D
if(UART1_RX_STA & 0x4000)//出现的问题!!为真不一定是1
{
if(buf == 0x0a)//如果接收到换行
//如果换行和回车都收到了就说明接收完成,那么手动将USART_RX_STA最高位置1
UART1_RX_STA |= 0x8000;
else
UART1_RX_STA = 0; //如果没有收到换行就置0重新来过
}else//如果没有收到回车
{
//先判断这个字符是不是回车
if(buf == 0x0d)
{
UART1_RX_STA |= 0x4000;//如果是回车,就手动将bit14置1
// 继电器控制指令
if(!strcmp((uint8_t *)UART1_RX_Buffer, "L-1"))
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
if(!strcmp((uint8_t *)UART1_RX_Buffer, "L-0"))
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
UART1_RX_STA = 0;
}
else
{
UART1_RX_Buffer[UART1_RX_STA & 0x3fff] = buf;
UART1_RX_STA++;
if(UART1_RX_STA > UART1_REC_LEN - 1)//如果光标到最后一位那么就光标返回,200-1是因为字符数组最后一位是'\0'
{
UART1_RX_STA = 0;
}
}
}
}
HAL_UART_Receive_IT(&huart1, &buf, 1);//重新开启中断接收
}
}
HC08只需要连接一个IO口,然后手机连接蓝牙,就可以通过手机端app发送接收指令,蓝牙传输就是串口传输
注意:
1.HC08的波特率是9600,工程的波特率记得改为9600
2.printf重定向必须勾选