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

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是符号°。

结尾

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

#include "mbed.h"
DigitalOut D_4(D6);
DigitalOut D_5(D5);
DigitalOut D_6(D3);
DigitalOut D_7(D2);


DigitalOut E(D9);
DigitalOut RW(D10);
DigitalOut RS(D11);

DigitalInOut dht11(D12);
DigitalOut Vcc(D13);
unsigned char rec_dat[11]={0};
unsigned char RH,RL,TH,TL;

BusOut data(D6,D5,D3,D2);//4位数据传输
void DHT11_start()
{
   dht11.output();
   dht11=1;
   wait_us(2);
   dht11=0;
   wait_us(20000);   //延时18ms以上
   dht11=1;
   wait_us(30);
   dht11.input();
}
unsigned char DHT11_rec_byte()      //接收一个字节
{
    unsigned char dat=0;
    for(int i=0;i<8;i++)    //从高到低依次接收8位数据
    {        
        while(dht11);  
        while(!dht11);  // 等待80us低电平过去
        wait_us(30);     //延时60us,如果还为高则数据为1,否则为0 
        dat<<=1;           //移位使正确接收8位数据,数据为0时直接移位
        
        if(dht11==1)    //数据为1时,使dat加1来接收数据1
        {
            dat+=1;
        }   
        while(dht11);  //等待数据线拉低    
    }  
    return dat;
}
void DHT11_receive()      //接收40位的数据
{
    unsigned char R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise; 

    DHT11_start();

    if(dht11==0)
    {
        while(dht11==0);   //等待拉高     
        wait_us(80);  //拉高后延时80us
        
        R_H=DHT11_rec_byte();    //接收湿度高八位  
        R_L=DHT11_rec_byte();    //接收湿度低八位  
        T_H=DHT11_rec_byte();    //接收温度高八位  
        T_L=DHT11_rec_byte();    //接收温度低八位
        revise=DHT11_rec_byte(); //接收校正位

        wait_us(25);    //结束

        if((R_H+R_L+T_H+T_L==revise))
        {
            RH=R_H;
            RL=R_L;
            TH=T_H;
            TL=T_L;
        }
            
        
        /*数据处理,方便显示*/
        
        rec_dat[0]='0'+(RH/10);
        rec_dat[1]='0'+(RL%10);
        
        rec_dat[2]='R';
        rec_dat[3]='H';
        rec_dat[4]=' ';
        rec_dat[5]=' ';
        rec_dat[6]='0'+(TH/10);
        rec_dat[7]='0'+(TH%10);
        rec_dat[8]='.';
        rec_dat[9]='0'+(TL/10);
        rec_dat[10]=0xDF;

        
        
    }

}

void isbusy()
{
    unsigned char temp=0x80;
    RW=1;
    RS=0;
    data=0xff;
    do {
    
    E=1;
    temp=data;//读取端口
    E=0;
    }while (temp&0x80);
}
void enable()
{
    E=1;//拉到高电平
    thread_sleep_for(2);
    E=0;//拉到低电平
}

void display_to_COM(unsigned char value)
{
    
    isbusy();
    RS = 0;// 设置为低电平来写命令
    RW = 0;// 设置为写
    E=0;
    data = value/16;// 数值右移4位,发送高四位到引脚上
    thread_sleep_for(2);//延时一小会儿,让1602准备接收
    enable();// 发送

	data = value%16;// 数值跟0x0F相与,发送低四位
    thread_sleep_for(2);
    enable();// 发送

}

void display_to_LCD(unsigned char value)
{
    
    isbusy();
    RS = 1;// 设置为高电平来写数据
    RW = 0;// 设置为写
    E=0;
    data = value/16;// 数值右移4位,发送高四位
    thread_sleep_for(2);
    enable();// 发送
	data = value%16;// 数值跟0x0F相与,发送低四位
    thread_sleep_for(2);
    enable();// 发送
}

void LcdSetCursor(unsigned char x,unsigned char y)
{
    unsigned char addr;
    if(y==0)//由输入的屏幕坐标计算地址
        addr=0x00+x;
    if(y==1)
        addr=0x40+x;
    display_to_COM(addr|0x80);//设置RAM地址
}

void lcdShowStr(unsigned char x,unsigned char y, unsigned char *str)
{
    LcdSetCursor(x,y);//设置起始地址
    while(*str!='\0')
    {
        display_to_LCD(*str ++);//连续写入字符串数据,直到检测到结束符
    }
}

/*区域清除,清除从(x,y)坐标起始的len个字符位*/
void LcdAreaClear(unsigned char x,unsigned char y,unsigned char len)
{
    LcdSetCursor(x,y);
    while(len--)
    {
        display_to_LCD(' ');
    }
}
//整屏清除
void LcdFullClear()
{
    display_to_LCD(0x01);
}

void LCD_init()
{
    display_to_COM(0x28);//16*2显示,5*8点阵,4位数据接口
    thread_sleep_for(20);
    display_to_COM(0x0f);//显示器开,光标开,闪烁开
    thread_sleep_for(20);
    display_to_COM(0x06);//文字不动,地址自动+1
    thread_sleep_for(20);
    display_to_COM(0x01);//清屏
    thread_sleep_for(20);
}
int main()
{
    Vcc=1;
    LCD_init();
    while(1)
   {   
        
       thread_sleep_for(15000);    //DHT11上电后要等待1S以越过不稳定状态在此期间不能发送任何指令
        DHT11_receive();
       thread_sleep_for(1500);

           lcdShowStr(0,0,rec_dat);
           thread_sleep_for(15000);
           
       
       
        thread_sleep_for(20);                  
   }
}

结果是这样的

作者:\"五言\"

物联沃分享整理
物联沃-IOTWORD物联网 » 自制温湿度计——STM32-L432KC驱动lcd1602和dht11温湿度显示

发表回复