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

news2024/12/23 23:16:14

项目需求

通过温湿度传感器将值传到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中进行配置!

  

2. 开启Timer2:

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

2. 惯例配置生成代码:

 

 

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 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的间隔也需要从微妙级变成毫秒级,不然无法成功驱动!!!

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

}

实现效果

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/772482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云计算名词-IaaS,PaaS,SaaS

在学习分布式的过程中&#xff0c;知道了PaaS这个词儿&#xff0c;但是不知道是什么意思。从网上查询了之后&#xff0c;做了简单的了解。这里简单记录一下&#xff0c;方面之后的查阅。 IAAS&#xff08;Infrastructure-as-a-Service&#xff09;基础设施即服务 网上的定义是…

hdfs的读写流程

hdfs的写入流程 1.client发起文件上传请求&#xff0c;通过rpc与namenode建立通讯&#xff0c;namenode检查目标文件是否已经存在&#xff0c;父目录是否存在&#xff0c;返回是否可以上传 2.client请求第一个block该传输到哪些datanode服务器上 3.namenode根据配置文件中指定…

支付宝支付上线准备工作(商家自研接入)

商家自研流程 1.创建应用 登录支付宝开放平台创建 网页/移动应用 说明&#xff1a;生成的应用唯一标识 APPID 可用于调用开放产品接口。 2.配置应用 2.1 应用详情 2.2 产品绑定 2.3 开发设置 在开发 > 开发设置 中配置应用信息。 2.3.1 接口加密方式 按操作步骤进行…

Mysql——》InnoDB行格式

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…

微服务保护---挑战(雪崩问题/流量控制/隔离和降级)

目录 1.雪崩问题 1.1.解决雪崩问题的常见方式有四种 2.什么是Sentinel 2.1.安装Sentinel控制台 2.2.微服务整合Sentinel 3.流量控制 3.1.簇点链路 3.2.快速入门 3.2.1.示例 3.2.2.利用jmeter测试 3.3.流控模式 3.3.1.关联模式 3.3.2.链路模式 3.3.3.总结 3.4.流控…

数字原生时代,奥哲如何让企业都成为“原住民”?

22年前&#xff0c;美国教育学家马克‧普伦斯基&#xff08;Marc Prensky&#xff09;出版了《数字原生与数字移民》&#xff08;Digital Natives, Digital Immigrants&#xff09;一书&#xff0c;首次提出了“数字原住民”和“数字移民”两大概念&#xff0c;用来定义跨时代的…

ylb-项目简介

1、各模块服务功能 注&#xff1a;其部分实体类、接口、mapper文件由MyBatis逆向工程生成。 2、Maven管理&#xff08;多模块&#xff0c;继承和聚合&#xff09; 2.1 parent模块 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"…

Unity根据目标点的位置计算Input输入

当给一个目标点&#xff0c;如果目标直接去目标点我们可以直接让position指向目标点的position。 如果是转换输入呢&#xff1f; 举例&#xff1a;例如一个人物动画里有两个参数X和Y&#xff0c;X&#xff08;- 1 &#xff0c;1) 表示向左走和向右走&#xff0c;Y (-1 , 1) 向…

vue条件渲染复习

 在某些情况下&#xff0c;我们需要根据当前的条件决定某些元素或组件是否渲染&#xff0c;这个时候我们就需要进行条件判断了。  Vue 提供了下面的指令来进行条件判断&#xff1a;  v-if  v-else  v-else-if  v-show 1.示例 <html lang"en"> &l…

onnx如何改变输入的维度

最近遇到一个难题&#xff0c;就算在用行为识别onnx转rknn的时候提示维度不对&#xff0c;因为行为识别模型是5维的。而rknn只支持4维。

650亿参数,训练飙升38%!LLaMA基础大模型复刻最佳实践开源,GitHub已获30k星

开源LLaMA神话再次复现&#xff01;首个开源650亿参数大模型高性能预训练方案&#xff0c;训练加速38%&#xff0c;低成本打造量身大模型。 「百模大战」正风起云涌&#xff0c;AIGC相关企业融资和并购金额也屡创新高&#xff0c;全球科技企业争相入局。 然而&#xff0c;AI大…

关于allure和pycharm的运行模式

案例 新建一个项目allure_mode 新建一个python代码test_allure_001.py 代码如下 import pytest, os def test_001(): assert 1 1 if __name__ __main__: pytest.main([-sv, __file__, --alluredir, ./html, --clean-alluredir]) os.system(fallure se…

试玩python的web框架 flask、fastapi、tornado、django

文章目录 一、Flask入门案例 [官网](https://flask.net.cn/quickstart.html) [其它参考](https://zhuanlan.zhihu.com/p/104273184?utm_id0)二、FastAPI入门案例 [官网](https://fastapi.tiangolo.com/zh/) [w3cschool教程](https://www.w3cschool.cn/fastapi/fastapi-feature…

数据容器入门(dict 字典)

字典的定义&#xff1a; 字典的定义&#xff0c;同样使用{}&#xff0c;不过存储的元素是一个个的&#xff1a;键值对&#xff0c;如下语法&#xff1a; # 定义字典字面量 {key&#xff1a;value, key&#xff1a;value, ........., key&#xff1a;value}# 定义字典变量 my_di…

JVM面试题总结

一.请简述对象的创建过程 对象的new字节码执行指令如下图 首先0指令new出一片内存空间&#xff0c;然后进行对象的初始化&#xff0c;初始化完成前变量都是初始值如m0 然后创建连接&#xff0c;t指向对象。 二.DCL单例要不要加volatile&#xff1f; DCL单例就是要懒汉式从创建…

广州市番禺区委领导一行莅临和鲸科技考察交流

7月18日下午&#xff0c;广州市番禺区区委常委、组织部部长、人才工作局局长唐力明&#xff0c;组织部副部长、两新工委书记罗翌洁及组织部其他相关领导一行莅临和鲸科技开展实地考察与调研&#xff0c;国投科创广州基地负责人、海创人才南方创业服务中心常务副秘书长徐斌&…

中小企业部署MES管理系统需要考虑哪些问题

随着制造业的快速发展&#xff0c;越来越多的中小企业开始意识到数字化管理的重要性。为了提高生产效率、降低成本、提升品质及满足客户需求&#xff0c;部署MES生产管理系统成为了中小企业实现数字化转型的关键一步。然而&#xff0c;在部署MES管理系统时&#xff0c;中小企业…

公开讲座笔记 | 雷达学报 雷达系统微课 - 第三讲 相控阵雷达 与 “1+1=2“

原文链接&#xff1a;https://mp.weixin.qq.com/s?__bizMzg4MjgxMjgyMg&mid2247486366&idx2&sn242bd062b6bcd1d32acba16a148f58c9&chksmcf51b967f8263071d912a178881c7ff3c4143b78201c30723a8c121cc53f47ade584a918648c#rd 第三讲 相控阵雷达 与 “112” 主讲…

JavaScript数组所有方法集合

##方法 1、concat 用于合并两个或多个数组。此方法不会更改现有数组&#xff0c;而是返回一个新数组 2、copyWithin 浅复制数组的一部分到同一数组中的另一个位置&#xff0c;并返回它&#xff0c;不会改变原数组的长度 3、entries 返回一个新的 Array Iterator 对象&#xf…

最新版本的OpenLens,有两个隐藏技能

最新版本的OpenLens v6.4.15&#xff0c;有两个隐藏技能 1、需要添加扩展插件alebcay/openlens-node-pod-menu&#xff0c;查看pod时才会出现进入命令行的按钮 2、测试环境查看pod、node时可能会出现监控数据未显示&#xff0c;点击集群的Setting&#xff0c;在Metrics里选…