赞
踩
先给大家甩一张图片哈
NUCLEO-L432KC | Mbed (附上网址)
简单说一下这块板子在这个小项目里的小缺点:一、引脚有点少,lcd1602本来有两种传输数据方式,一种是4bit,另一种是8bit,但是这块板子对于这个项目来说引脚有点少,所以建议用4bit模式。然而,在网上是很难找到设置4bit传输模式的lcd1602的,这也是为什么我写这个blog。二、mbed-os有时候会有大大小小的问题,搞人心态是真的。比如库函数离奇失踪,从官网上下载库函数失败,重装也解决不了然后莫名其妙好了。三、引脚明明就少,D7,D8还有一堆负责模拟输入输出的(A开头的)都用不了,真的很重要,不然会出大问题。
(附上个人心得:做任何项目都得细心,接线问题,代码问题,任何一个小问题都有可能导致最后没有任何结果哦~心态很重要!)
好了,第一个重头戏来了。
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的代码原理,以及下面这些有可能会用到的模块控制指令。
非常好用的函数,可以一次传输四个数据,注意括号里哦,不要填反了。
第九条指令,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博客
其中涉及原理的部分讲的也比较清楚,回头再看我的代码应该就比较清楚了。
可以先测试一下lcd部分有没有问题,然后进行下一步,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,这是一位数据。最后数据线拉低回到一开始的状态,等待下一次通讯。
启动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);
- }
- }
结果是这样的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。