使用STM32 在实现温湿度LCD显示并蓝牙透传

项目需求

通过温湿度传感器将值传到LCD1602,并实时通过蓝牙透传到手机。

硬件介绍

LCD1602

是一种工业字符型液晶,能够同时显示16x02即32字符 (16列两行)

DHT11

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。

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. 惯例配置 + 开串口1 + GPIO口

注意,DHT11的DATA线既作为输入也作为输出,因此不能在CubeMX中进行配置!

  1. 开启Timer2:

计数一次经过的时间是 (PSC + 1) / Tclk , 因此如果我想要计数1微秒,即0.000001s, 已知Tclk = 72 000 000, 那么PSC就应该设置为 71。然后在main.c中就可以定义出一个实现微秒级延时的函数:

  1. 惯例配置生成代码:

Keil

  1. 打开MICRO-LIB
  1. 编写代码:

关于上面提到的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 函数
cpp 复制代码
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);
}

然后,

就可以在需要往DATA写数据的时候写:DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);

在需要从DATA读数据的时候写:DHT_GPIO_Init(GPIO_MODE_INPUT);
并且,由于89C52和STM32的硬件差别 ,使用STM32来驱动LCD1602时,是不需要检测BUSY信号的!!!

但是32驱动LCD的时候,在写命令或内容时,拉高E和拉低E的间隔也需要从微妙级变成毫秒级,不然无法成功驱动!!!

cpp 复制代码
#include "stdio.h"

char flag_busy = 0;

char buffer;
char datas[5];
char temp[9];
char huma[9];

void TIM2_Delay_us(uint16_t n_us) //使用TIM2来做us级延时函数
{
/* 使能定时器2计数 */
	__HAL_TIM_ENABLE(&htim2);
	__HAL_TIM_SetCounter(&htim2, 0);
	while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );
/* 关闭定时器2计数 */
	__HAL_TIM_DISABLE(&htim2);
}

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);
}


void write(char flag_w, char cmd) //write(1,cmd)代表写内容;write(0,cmd)代表写指令
{
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET); //RW全程置0
	
	if(flag_w == 1){
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); //RS置1写内容
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); //E置0
		TIM2_Delay_us(2);
		
		GPIOA->ODR = cmd; //根据时序图,E为0时开始传内容
		
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET); //E置1
		HAL_Delay(5);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); //E置0
		HAL_Delay(5);
	}
	
	if(flag_w == 0){
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); //RS置0写指令
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); //E置0
		TIM2_Delay_us(2);
		
		GPIOA->ODR = cmd; //根据时序图,E为0时开始传内容
		
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET); //E置1
		HAL_Delay(5);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); //E置0
		HAL_Delay(5);
	}
	
}
 

 
void Init()
{
	HAL_Delay(15);
	write(0,0x38);
	HAL_Delay(5);
	
	//detectBusy();
	write(0,0x38);
	//detectBusy();
	write(0,0x08);
	//detectBusy();
	write(0,0x01);
	//detectBusy();
	write(0,0x06);
	//detectBusy();
	write(0,0x0C);
	
}
 
void showStr(char *msg, char hang, char lie)
{
	char pos;
	
	if(hang == 0){//如果第一行
			pos = 0x80 + 0x00 + lie; 
	}else if(hang == 1){//如果第二行
			pos = 0x80 + 0x40 + lie;
	}
	//detectBusy();
	write(0,pos); //只要定下开始的位置,之后光标会自行移动
	
	while(*msg != '\0'){
		//detectBusy();
		write(1,*msg);
		msg++;
	}
		
}


void Start()
{	
	DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);//设置为输出模式!!
	
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
	HAL_Delay(30);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
	
	
	DHT_GPIO_Init(GPIO_MODE_INPUT);//设置为输入模式!!
	
	while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET);//不断读取直到DHT再次变低,说明模块响应
	while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_RESET); //不断读取直到DHT再次拉高,80us之后,再经过50us低电平,就代表要开始变高并开始传输数据了
	while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET);//不断读取直到DHT再次变低,说明数据传输前的50us低电平开始了
 
}

void showDataFromDHT()
{
	int i;//轮
	int j;//每一轮读多少次
  char tmp;
	char flag;
	
	Start();
	for(i= 0;i < 5;i++){ //5组数据
		for(j=0;j<8;j++){ //每组数据8位
			while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_RESET);//等待上拉
			TIM2_Delay_us(40);//高电平持续26-28us是'0',高电平持续70us是'1',所以delay40us之后观察还是否是高电平
			if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET){
				flag = 1;
				while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET);//不断读取下拉的一瞬间,因为要延迟80us,如果提前结束,下一次detect01就会出错,因为detect01是从判断上拉开始的
			}else{
				flag = 0;
			} 
			tmp = tmp << 1; //tmp左移一位,再最右侧空出一位
			tmp |= flag; //将Flag的值写入空出的一位,这样经历8次,就得到了完整的tmp,且第一个读入的数据就是最高位,符合DHT的数据传输逻辑
			//!!使用这种移位再赋值的方法可以得到的一个完整的char型数据,而不是像之前char xxx[8]一样,得到的是8个char型的字符串,不利于数据转化!!
		}
		datas[i] = tmp;
	}
	
}
 
void Build_Datas() //由于刚刚存储的是8位char型的数字,但是不管是蓝牙还是LCD,需要显示的都是字符,而比如数字"9"和字符"9"是不同的二进制表示,所以,需要转化
{
	huma[0] = 'H';
	huma[1] = ':';
	huma[2] = datas[0]/10 + 0x30; //查阅ASCII码可知,数字Y加上0x30就是字符Y
	huma[3] = datas[0]%10 + 0x30; //同时由于不管整数或小数,都使用两位数字来表示,因此需要对'十位'和'个位'分别进行提取再转化成字符
	huma[4] = '.';
	huma[5] = datas[1]/10 + 0x30;
	huma[6] = datas[1]%10 + 0x30;
	huma[7] = '%';
	huma[8] = '\0';
	
	temp[0] = 'T';
	temp[1] = ':';
	temp[2] = datas[2]/10 + 0x30;
	temp[3] = datas[2]%10 + 0x30;
	temp[4] = '.';
	temp[5] = datas[3]/10 + 0x30;
	temp[6] = datas[3]%10 + 0x30;
	temp[7] = 'C';
	temp[8] = '\0';
	
}

int fputc(int a, FILE *f) //一个字符一个字符发送
{
	unsigned char temp[1] = {a};
	HAL_UART_Transmit(&huart1, temp, 1, 0xffff);
	return a;
}

int main(void)
{
	
  Init(); //LCD1602初始化

  while (1)
  {
		HAL_Delay(1000);
		showDataFromDHT();	
		Build_Datas();
		
		printf(temp);
		printf("\r\n");
		printf(huma);
		printf("\r\n");
		
		showStr(temp,0,4);
		showStr(huma,1,4);
  }

}

实现效果

相关推荐
带多刺的玫瑰1 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
美式小田2 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
兰_博2 小时前
51单片机-独立按键与数码管联动
单片机·嵌入式硬件·51单片机
陌小呆^O^2 小时前
Cmakelist.txt之win-c-udp-server
c语言·开发语言·udp
时光の尘2 小时前
C语言菜鸟入门·关键字·float以及double的用法
运维·服务器·c语言·开发语言·stm32·单片机·c
-一杯为品-2 小时前
【51单片机】程序实验5&6.独立按键-矩阵按键
c语言·笔记·学习·51单片机·硬件工程
爱摸鱼的孔乙己3 小时前
【数据结构】链表(leetcode)
c语言·数据结构·c++·链表·csdn
Dola_Pan3 小时前
C语言:数组转换指针的时机
c语言·开发语言·算法
嵌入式大圣4 小时前
单片机结合OpenCV
单片机·嵌入式硬件·opencv
IU宝4 小时前
C/C++内存管理
java·c语言·c++