当前位置:   article > 正文

自制温湿度计——STM32-L432KC驱动lcd1602和dht11温湿度显示

l432kc

L432KC介绍

先给大家甩一张图片哈

NUCLEO-L432KC | Mbed (附上网址)

简单说一下这块板子在这个小项目里的小缺点:一、引脚有点少,lcd1602本来有两种传输数据方式,一种是4bit,另一种是8bit,但是这块板子对于这个项目来说引脚有点少,所以建议用4bit模式。然而,在网上是很难找到设置4bit传输模式的lcd1602的,这也是为什么我写这个blog。二、mbed-os有时候会有大大小小的问题,搞人心态是真的。比如库函数离奇失踪,从官网上下载库函数失败,重装也解决不了然后莫名其妙好了。三、引脚明明就少,D7,D8还有一堆负责模拟输入输出的(A开头的)都用不了,真的很重要,不然会出大问题。

(附上个人心得:做任何项目都得细心,接线问题,代码问题,任何一个小问题都有可能导致最后没有任何结果哦~心态很重要!)

Lcd1602介绍

好了,第一个重头戏来了。

lcd1602是一款简易显示器,能够表示绝大多数非常基础的符号(字母,数字,一些文字)。先看看实物:

 依次讲解一下各个针脚:

GND:接地

VDD:Lcd供电源,本应该是5V才可以,但实际上3.3V也行。

VO:对比度调节,接地对比度最大,接高电平对比度最小。(我直接接低省事,但可以用电位计调节,让自己看着更舒服)

RS:数据/指令选择,RS高电平是数据,低电平是指令

RW:读/写选择,RW高电平是读,低电平是写

E:(enable)使能,控制E高低电平变换是通信的关键之一。

D0-D7:数据或指令传输针脚(4bit传输选择D4-D7)

BLA:背光源正极,最好接5V

BLK:背光源负极,接地

针脚清楚之后,建议先不急去了解dht11,可以先测试好lcd1602能不能成功显示。所以我们这里先接lcd1602的线,然后手动在代码中加入想显示的内容以测试lcd1602部分完好。

接线部分

一、GND和BLK还有VO(我自己)都接地,所以可以把板子的GND接出来。

二、D4-D7分别接D6,D5,D3,D2

三、VDD和BLA都需要5V,板子上是有5V的,所以可以把5V接出来。

四、E,RW,RS分别接D9,D10,D11

 图片可能不太清楚,但是线接起来,的确比较乱。

下一步是lcd1602的代码部分,在此之前,需要了解一些lcd1602的代码原理,以及下面这些有可能会用到的模块控制指令。

定义变量

BusOut函数

非常好用的函数,可以一次传输四个数据,注意括号里哦,不要填反了。

忙函数

第九条指令,RW设置高电平,RS设置低电平,若此时数据最高位检测到是高电平,则仍然为忙状态。代码的目的就是,先将RW和RS设置好,然后先将E拉高,使单片机能够开始读,E调回低电平,如果这个时候lcd1602最高位是1,那就继续走循环,不是的话就跳出来,此时lcd1602就是空闲状态。为什么要判断忙不忙,就是为防止时序出现问题,因为lcd1602可能反应有点慢,单片机运作太快了,一条一条指令发给lcd1602可能会导致lcd1602反应不过来,所以相当于是等一下lcd1602进行空闲状态。

发送函数

设置好了读写数据模式之后(RS,RW),再用到发送函数,高电平持续2ms,保证读取时间足够

命令函数

判断完忙之后,可以发现指令有关的RW和RS都设置成低电平,然后指令需要4bit一次传输,传两次

显示函数

和命令函数有点像,想要显示的字符对应的ascii码值分两次传输

视觉坐标和地址匹配函数

设置的地址之所以要与0x80,是因为设置地址的命令函数最高位需要是1

字符串显示函数

这里很好看懂,用了指针,将数组里的内容一一传给LCD显示

初始化函数

初始化是必须的,照抄我的即可,每条指令都是根据手册得到的。

想要更通透了解为什么要有这些函数,以及函数中夹杂的等待语句,仍然需要了解lcd1602的工作时序图,可以参考这篇简介LCD1602屏幕简介(全网最详细教程)_LJX的博客-CSDN博客

其中涉及原理的部分讲的也比较清楚,回头再看我的代码应该就比较清楚了。

lcd1602显示

可以先测试一下lcd部分有没有问题,然后进行下一步,dht11

dht11介绍

dht11属于性价比比较高的温湿度传感器,采用单总线通讯,就是包括供电,接地,数据接口在内,一共三个接口。总线一次传输40bit数据,分别是湿度整数部分8bit,湿度小数部分8bit,温度整数部分8bit,温度小数部分8bit,校验位8bit,若校验位等于其他四个8bit的和,那dht11此次采集的数据就可行。

同样,dht11也有对应的工作时序图,可以参考DHT11详细介绍(内含51和STM32代码)_安赫'的博客-CSDN博客

实物如下:

接线部分

具体接法也比较简单,就是需要一个5欧姆的电阻作为上拉电阻,上拉电阻的作用就是保证当数据总线闲置时处于高电平的状态。以下是接线实物图:

上拉电阻的原理图:

图中R1就是上拉电阻,我这里电阻一端接3.3v(板子上自带一个3.3针脚),另一端接到数据总线上。

再看代码

定义变量

针脚D12是作为InOut针脚,可以切换输出或者输入模式,D13作为供电针脚,

定义一个数组用来储存采集的数据,后提供给lcd1602显示

四个unsigned char变量,每一个都是正好8bit大小,便于储存采集数据

启动函数

由单片机向dht11发送高电平,等待2us调为低电平,延时18ms以上再调为高电平,

这个时候等待30us,dht就会开始传输它采集的数据

字节传输函数

一共40bit,每8bit一存储,进入循环,先等待高电平过去,再等待80us低电平过去,这可以理解成dht11的相应信号,也可以写if判断。因为dht11是单总线通讯,所以是通过一根线上的电平变化来表示数据的,50us的低电平加26-28us的高电平表示的就是0,50us的低电平加80us的高电平表示的就是1,这是一位数据。最后数据线拉低回到一开始的状态,等待下一次通讯。

40位数据接收函数

启动dht11之后,dht11会发送低电平给单片机表示准备开始传输数据,然后因为上拉电阻的存在,总线会被拉高,只有拉高之后才能正常开始传输数据。当最后采集的数据校验成功,经过最后的运算,就可以得到湿度的十位和个位,温度的十位和个位,以及温度的小数位。oxDF是符号°。

结尾

如果没有结果的话,检查接线,硬件,代码的问题,一步一步解决,一个问题一个问题地排除,最后一定会成功的。最后,附上本次小项目的全代码。

  1. #include "mbed.h"
  2. DigitalOut D_4(D6);
  3. DigitalOut D_5(D5);
  4. DigitalOut D_6(D3);
  5. DigitalOut D_7(D2);
  6. DigitalOut E(D9);
  7. DigitalOut RW(D10);
  8. DigitalOut RS(D11);
  9. DigitalInOut dht11(D12);
  10. DigitalOut Vcc(D13);
  11. unsigned char rec_dat[11]={0};
  12. unsigned char RH,RL,TH,TL;
  13. BusOut data(D6,D5,D3,D2);//4位数据传输
  14. void DHT11_start()
  15. {
  16. dht11.output();
  17. dht11=1;
  18. wait_us(2);
  19. dht11=0;
  20. wait_us(20000); //延时18ms以上
  21. dht11=1;
  22. wait_us(30);
  23. dht11.input();
  24. }
  25. unsigned char DHT11_rec_byte() //接收一个字节
  26. {
  27. unsigned char dat=0;
  28. for(int i=0;i<8;i++) //从高到低依次接收8位数据
  29. {
  30. while(dht11);
  31. while(!dht11); // 等待80us低电平过去
  32. wait_us(30); //延时60us,如果还为高则数据为1,否则为0
  33. dat<<=1; //移位使正确接收8位数据,数据为0时直接移位
  34. if(dht11==1) //数据为1时,使dat加1来接收数据1
  35. {
  36. dat+=1;
  37. }
  38. while(dht11); //等待数据线拉低
  39. }
  40. return dat;
  41. }
  42. void DHT11_receive() //接收40位的数据
  43. {
  44. unsigned char R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise;
  45. DHT11_start();
  46. if(dht11==0)
  47. {
  48. while(dht11==0); //等待拉高
  49. wait_us(80); //拉高后延时80us
  50. R_H=DHT11_rec_byte(); //接收湿度高八位
  51. R_L=DHT11_rec_byte(); //接收湿度低八位
  52. T_H=DHT11_rec_byte(); //接收温度高八位
  53. T_L=DHT11_rec_byte(); //接收温度低八位
  54. revise=DHT11_rec_byte(); //接收校正位
  55. wait_us(25); //结束
  56. if((R_H+R_L+T_H+T_L==revise))
  57. {
  58. RH=R_H;
  59. RL=R_L;
  60. TH=T_H;
  61. TL=T_L;
  62. }
  63. /*数据处理,方便显示*/
  64. rec_dat[0]='0'+(RH/10);
  65. rec_dat[1]='0'+(RL%10);
  66. rec_dat[2]='R';
  67. rec_dat[3]='H';
  68. rec_dat[4]=' ';
  69. rec_dat[5]=' ';
  70. rec_dat[6]='0'+(TH/10);
  71. rec_dat[7]='0'+(TH%10);
  72. rec_dat[8]='.';
  73. rec_dat[9]='0'+(TL/10);
  74. rec_dat[10]=0xDF;
  75. }
  76. }
  77. void isbusy()
  78. {
  79. unsigned char temp=0x80;
  80. RW=1;
  81. RS=0;
  82. data=0xff;
  83. do {
  84. E=1;
  85. temp=data;//读取端口
  86. E=0;
  87. }while (temp&0x80);
  88. }
  89. void enable()
  90. {
  91. E=1;//拉到高电平
  92. thread_sleep_for(2);
  93. E=0;//拉到低电平
  94. }
  95. void display_to_COM(unsigned char value)
  96. {
  97. isbusy();
  98. RS = 0;// 设置为低电平来写命令
  99. RW = 0;// 设置为写
  100. E=0;
  101. data = value/16;// 数值右移4位,发送高四位到引脚上
  102. thread_sleep_for(2);//延时一小会儿,让1602准备接收
  103. enable();// 发送
  104. data = value%16;// 数值跟0x0F相与,发送低四位
  105. thread_sleep_for(2);
  106. enable();// 发送
  107. }
  108. void display_to_LCD(unsigned char value)
  109. {
  110. isbusy();
  111. RS = 1;// 设置为高电平来写数据
  112. RW = 0;// 设置为写
  113. E=0;
  114. data = value/16;// 数值右移4位,发送高四位
  115. thread_sleep_for(2);
  116. enable();// 发送
  117. data = value%16;// 数值跟0x0F相与,发送低四位
  118. thread_sleep_for(2);
  119. enable();// 发送
  120. }
  121. void LcdSetCursor(unsigned char x,unsigned char y)
  122. {
  123. unsigned char addr;
  124. if(y==0)//由输入的屏幕坐标计算地址
  125. addr=0x00+x;
  126. if(y==1)
  127. addr=0x40+x;
  128. display_to_COM(addr|0x80);//设置RAM地址
  129. }
  130. void lcdShowStr(unsigned char x,unsigned char y, unsigned char *str)
  131. {
  132. LcdSetCursor(x,y);//设置起始地址
  133. while(*str!='\0')
  134. {
  135. display_to_LCD(*str ++);//连续写入字符串数据,直到检测到结束符
  136. }
  137. }
  138. /*区域清除,清除从(x,y)坐标起始的len个字符位*/
  139. void LcdAreaClear(unsigned char x,unsigned char y,unsigned char len)
  140. {
  141. LcdSetCursor(x,y);
  142. while(len--)
  143. {
  144. display_to_LCD(' ');
  145. }
  146. }
  147. //整屏清除
  148. void LcdFullClear()
  149. {
  150. display_to_LCD(0x01);
  151. }
  152. void LCD_init()
  153. {
  154. display_to_COM(0x28);//16*2显示,5*8点阵,4位数据接口
  155. thread_sleep_for(20);
  156. display_to_COM(0x0f);//显示器开,光标开,闪烁开
  157. thread_sleep_for(20);
  158. display_to_COM(0x06);//文字不动,地址自动+1
  159. thread_sleep_for(20);
  160. display_to_COM(0x01);//清屏
  161. thread_sleep_for(20);
  162. }
  163. int main()
  164. {
  165. Vcc=1;
  166. LCD_init();
  167. while(1)
  168. {
  169. thread_sleep_for(15000); //DHT11上电后要等待1S以越过不稳定状态在此期间不能发送任何指令
  170. DHT11_receive();
  171. thread_sleep_for(1500);
  172. lcdShowStr(0,0,rec_dat);
  173. thread_sleep_for(15000);
  174. thread_sleep_for(20);
  175. }
  176. }

结果是这样的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/617828
推荐阅读
相关标签
  

闽ICP备14008679号