7.1 DHT11温湿度传感器
本小节将讲解如何使用DHT11温湿度传感器来感知环境温湿度,并且把数据在屏幕中显示出来。许多传感器的使用方法都是类似的,通过学习使用DHT11,相信读者可以做到举一反三,能够使用更多种类的传感器。
DHT11 简介
DHT11数字温湿度传感器是一款能够检测温湿度的复合传感器,其内置一个测温元件、一个电阻式感湿元件和一个单片机。以奥松公司生产的DHT11温湿度传感器为例,其有效测量范围为:
- 温度:0~50℃
- 湿度:20~95%
DHT11实物如图所示。
可以看到,DHT11带有4个引脚,其功能说明见下表。
PIN(引脚) | 名称 | 说明 |
---|---|---|
1 | VCC | 供电引脚,3~5.5VDC |
2 | DATA | 温湿度数据输出 |
3 | NC | 空置引脚 |
4 | GND | 地线引脚,接电源负极 |
可以从DATA引脚来获取温湿度数据,下面简单讲解一下其通信协议。
DATA引脚的通信协议分析
从DHT11中获取温湿度数据的方法较比简单,首先是CC2530与DHT11配对(握手),然后按照特定的协议从DATA引脚接收数据。
配对(握手)
在发送数据前,DHT11需要先和CC2530配对,配对的协议如下:
(1)DATA引脚在初始的默认状态时处于高电平(3.3v)。
(2)CC2530拉低DATA引脚的电平18ms毫秒以上,接着拉高电平20~40us,DHT11就会被激活。
(3)DHT11会主动拉低DATA引脚的电平,并且持续80us,表示已经收到了CC2530的指令并且配对成功。
(4)接着DHT11会再次拉高电平,80us后就开始发送温湿度数据给CC2530了。
这个配对过程如图所示。
接收收据
DHT11的温湿度数据是以二进制数据表示的,这些二进制数据是按照一个比特位接着一个比特位这样顺序发送到CC2530的,具体的原理如下:
(1)在发送每个比特位之前,DHT11都会把DATA引脚的电平拉低50us,以此告诉CC2530:“我接着要发送一个比特位了”。
(2)接着,DHT11把DATA引脚的电平拉高,如果持续拉高26~28us,表示发送的是数据0;如果持续拉高70us,表示发送的是数据1,如图所示。
通过这个方式,温湿度数据就发送给了CC2530了。DHT11的通信协议大致上介绍完毕,但还有多个细节还没讲解到,有兴趣的读者可查阅更多相关的资料或仔细研究一下接下来介绍的API的源代码。
DHT11驱动API简介
基于以上原理,笔者设计了一套DHT11驱动API,使用起来非常方便。打开配套工程中的DHT11文件夹,即可找到DHT11驱动API,如图所示。
打开hal_dht11.h文件,可以找到API定义代码:
//2. 51单片机入门/7. 外设实验/7.1 温湿度传感器DHT11/Workspace/code/DHT11_MAIN/hal_dht11.h
/**
* @fn halDHT11Init
*
* @brief 初始化函数,使用DHT11前必须先调用此函数
*/
void halDHT11Init(void);
/**
* @fn halDHT11GetData
*
* @brief 获取DHT11的温湿度数据
*
* @return 温湿度数据值
*/
halDHT11Data_t halDHT11GetData(void);
其中的halDHT11Data_t是一个结构体,用于保存温湿度数据,其定义代码如下:
1./** @brief 用于表示DHT11温湿度数据 */
2.typedef struct {
3. unsigned char ok; //ok的值非0时温湿度数据才有效
4. unsigned char temp; //温度值,取值范围:0~50
5. unsigned char humi; //湿度值,取值范围:20~95
6.} halDHT11Data_t;
使用DHT11驱动API
引脚配置
调用API前,需要先把DATA引脚与CC2530的IO口配对起来。ZigBee标准板是使用CC2530的P0_6引脚与DHT11的DATA引脚连接,可在hal_dht11.h文件中找到如下配置代码:
1.#define HAL_DHT11_PORT 0 //Port0.
2.#define HAL_DHT11_PIN 6 //Pin6.
如果在硬件上DATA引脚需要与CC2530的其他引脚连接的话,只需要在这里修改引脚编号即可。
API调用示例
打开main.c文件,可以看到API调用示例代码:
//2. 51单片机入门/7. 外设实验/7.1 温湿度传感器DHT11/Workspace/code/DHT11_MAIN/main.c
void main(void)
{
halDHT11Data_t dht11Dat;//定义温湿度数据结构体
uint8 tempStr[50], humiStr[50];
setSystemClk32MHZ();//初始化系统时钟频率
//初始化显示器
#ifdef LCD_OLED12864
//初始化OLED12864屏幕
halOLED12864Init()
#else
//初始化TFT屏幕
halTFTInit(HAL_TFT_PIXEL_WHITE);
#endif
halDHT11Init();//初始化DHT11温湿度传感器
while(1)
{
dht11Dat = halDHT11GetData();//获取温湿度数据
if (dht11Dat.ok) {//如果数据正确获取
sprintf((char *)tempStr, "Temp: %d", dht11Dat.temp);//显示温度
sprintf((char *)humiStr, "Humi: %d", dht11Dat.humi);//显示湿度
//把数据显示到屏幕中
#ifdef LCD_OLED12864
//显示到OLED12863屏幕
halOLED12864ShowX16(0,0, tempStr);
halOLED12864ShowX16(1,0, humiStr);
#else
//显示到TFT屏幕
halTFTShowX16(0,0, HAL_TFT_PIXEL_RED, HAL_TFT_PIXEL_WHITE, tempStr);
halTFTShowX16(0,16, HAL_TFT_PIXEL_RED, HAL_TFT_PIXEL_WHITE, humiStr);
#endif
}
delayMs(SYSCLK_32MHZ, 4000);//延迟
} /* while */
}
调试仿真
可以运行本实验代码以观察运行结果,操作步骤如下:
(1)编译链接工程代码后,把程序烧录到配套的ZigBee开发板中。
(2)由于本实验需要用到DHT11温湿度传感器,ZigBee标准板中已经集成了DHT11温湿度传感器,并且DATA引脚默认连接着P0_6引脚,如图所示。
如果使用ZigBee Mini测试,需要在P0_6引脚处外接DHT11的DATA引脚,并且把该DHT11的VCC和GND引脚分别接入接入3.3v~5.5v的电源和地线。
(3)把OLED屏幕插入到标准板或者Mini板中,如图所示。
(4)给开发板上电,屏幕中即会显示环境温湿度数据。
7.2 NorFLASH读写实验
硬件准备
本节课需要用到NOR Flash存储器
- ZigBee标准板带有NOR Flash存储器,如图所示。
按照内部存储结构不同,Flash存储器可分为NOR Flash和NAND Flash两种类型。
NOR Flash
NOR Flash存储器的读取速度快、存储可靠性高、支持使用随机地址访问存储空间支持,但是存储容量小、价格贵,多用于保存电子产品的程序。
NAND Flash
相对于NOR Flash,NAND Flash存储器的容量大、可反复读写次数高和价格便宜,但是读取数据速度慢、不支持随机地址访问存储空间,有点类似于光盘或硬盘,多用于存储卡、U盘中。
M25PE80简介
ZigBee标准板中带有一颗M25PE80芯片,这是一款NOR Flash,其容量是1024KB(8M bit)。另外,CC2530F256的内部也带有Flash存储器,其容量是256KB。也就是说,标准板上共有1024KB+256KB的Flash容量。
- M25PE80实物图:
- M25PE80引脚图:
了解 M25PE80 API
M25PE80的通信协议是SPI。在学习显示器的实验时,已经讲解过SPI驱动API的设计方式了,只需要基于通用SPI驱动API适配出M25PE80的专用SPI驱动API即可。
打开配套工程代码,展开Hal_Flash_Spi,可以看到笔者设计的通用SPI驱动API和M25Exx驱动专用的SPI驱动API,如图所示。
借助此API,M25PE80的使用非常简单,只需要学习一下hal_m25pexx.h/c文件中的3个API即可,其定义如下:
/**
* @fn halM25PExxInit
*
* @brief 初始化M25PExx
*
* @return none
*/
void halM25PExxInit(void);
/**
* @fn halM25PExxRead
*
* @brief 从M25PExx中读取数据
*
* @param addr - 将要读取的数据所在的存储器地址.
* @param pBuf - 变量指针,用于保存储器中读出来的数据.
* @param len - 指定从存储器中读取多少个字节的数据.
*
* @return 如果读取成功,则返回0
*/
int halM25PExxRead(uint32 addr, uint8 *pBuf, uint16 len);
/**
* @fn halM25PExxWrite
*
* @brief 把数据写入到存储器中
*
* @param addr - 说明把数据写入到存储器的哪个地址
* @param pBuf - 变量指针,指向将要写入到存储器的数据
* @param len - 指定把多少个字节的数据写入到存储器中
*
* @return 如果写入成功,则返回0
*/
int halM25PExxWrite(uint32 addr, uint8 *pBuf, uint16 len);
使用 M25PE80 API
打开main.c文件,可以使用 M25PE80 API读写数据的示例代码:
void main(void)
{
uint8 writeVal = 0;//此变量的值将会被写入到存储器中
uint8 readVal = 0;//从存储器读取到的值将会存入此变量中
char str[50];
setSystemClk32MHZ();//初始化系统时钟为32MHz
initUart0(USART_BAUDRATE_115200);//初始化串口0
halM25PExxInit();//初始化M25PE80存储器
//进入到循环中
while(1) {
/* 1.写数据到M25PE80存储器中 */
//串口通信
sprintf(str, "Write: %d\r\n", writeVal);
uart0Send((unsigned char *)str, strlen(str));
//把writeVal的值写入到存储器中地址为0x12345的存储空间中;由于writeVal的类型为uint8,也就1个字节,所以传入的数据长度为1
if (halM25PExxWrite(0x12345, &writeVal, 1) != 0) {
uart0Send("Write Error\r\n", 13);//如果函数返回值不等于0,表示写入错误
continue;
}
writeVal++;//把写入值增加1
delayMs(SYSCLK_32MHZ, 1000);//延迟
/* 2.从M25PE80存储器中读取数据 */
//从halM25PExxRead的0x12345处读取1个字节的数据,并将其保存到readVal中
if (halM25PExxRead(0x12345, &readVal, 1) != 0) {
uart0Send("Read Error\r\n", 12);//如果函数返回值不等于0,表示读取错误
continue;
}
//串口通信
sprintf(str, "Read: %d\r\n", readVal);
uart0Send((uint8 *)str, strlen(str));
delayMs(SYSCLK_32MHZ, 1000);//延迟
}
}
调试仿真
在学习本节课前,需要先掌握基本的程序下载及仿真操作,参考:程序下载及仿真
1.使用仿真器连接ZigBee标准板到电脑中,
2.按一下仿真器中的复位按键
3.打开配套工程,编译源代码并烧录程序到ZigBee标准板中
4.断开仿真器连接,用Micro USB线连接开发板到电脑,并且打开串口调试助手,可以看到写入和读取的过程:
7.3 继电器控制实验
继电器简介
常见的LED小灯、小型电机或小型传感器的工作电压在3.3v左右,故可以让单片机直接控制其开关。但是像家用电灯泡、家用电风扇或电磁锁的工作电压是220v,故单片机无法直接控制其开关。这时候,单片机可以借助继电器来控制高电压用电器的开关。ZigBee标准板集成了继电器,如图所示。
其中的绿色部分是接线端子,共有3接线口,每个接线口的上方均有一个螺丝孔。
以220v家用灯泡为例简单讲解一下继电器的接线方法,用螺丝刀拧开第1、2号口的螺丝,分别塞入零线后再拧紧螺丝,如图所示。此时,继电器充当了一个开关,可以控制零线的断开或者闭合,从而控制灯泡的开关。也可以把零线分别塞入到第2、3号口,区别在与如果介入第1、2号口,那么在默认状态下是断开的;如果接入第2、3号口,那么在默认状态是闭合的。
类似地,使用继电器连接12v电磁锁的示意图如图所示。图中的12v电池是用于给电磁锁供电的。
使用继电器
继电器的使用方式非常简单,控制继电器的控制引脚的电平即可控制其开合。
引脚配置
使用继电器前,需要先把继电器的控制引脚与CC2530的IO口配对起来。ZigBee标准板是使用CC2530的P0_5引脚与继电器的控制引脚连接的。打开本实验代码代码,可以找到ioConfig.c文件,如图所示。
可在本实验代码的ioConfig.c文件中找到如下配置代码:
#define RELAY_PORT 0
#define RELAY_PIN 5
#define RELAY P0_5
P0_5是由头文件ioCC2530.h所定义的,用于表示P0_5引脚,因此RELAY实际上就表示P0_5引脚。如果继电器控制引脚需要与CC2530的其他引脚连接的话,只需要在这里修改引脚映射即可,例如如果需要在P1_2引脚外接继电器控制引脚,代码如下:
#define RELAY_PORT 1
#define RELAY_PIN 2
#define RELAY P1_2
控制继电器
控制继电器的开合,本质上就是控制P0_5引脚的电平,在ioConfig.c中可找到示例代码:
//2. 51单片机入门/7. 外设实验/7.3 继电器开关控制/Workspace/code/ioConfig/ioConfig.c
#include "cc2530_ioctl.h"
#include <stdio.h>
/** @brief GPIO映射定义 */
#define RELAY_PORT 0
#define RELAY_PIN 5
#define RELAY P0_5
/** @brief 继电器开关状态定义 */
#define RELAY_ON 1
#define RELAY_OFF 0
static void delayMs(uint16_t nMs);
static void initRelay(void);
void main()
{
initRelay();
while(1) {
delayMs(1000);//延迟
//反转RELAY引脚的电平状态
RELAY = (RELAY == RELAY_ON)? RELAY_OFF : RELAY_ON;
} /* while */
}
/*
* 延迟指定的时间
*
* @param nMs - 时间长度,单位为微秒
*/
static void delayMs(uint16_t nMs)
{
uint16_t i,j;
for (i = 0; i < nMs; i++)
for (j = 0; j < 535; j++);
}
/*
* 初始化继电器
*/
static void initRelay()
{
CC2530_IOCTL(
RELAY_PORT,
RELAY_PIN,
CC2530_OUTPUT);
RELAY = RELAY_OFF;
}
上述代码实现了不断开关继电器的功能。可见,继电器的使用其实非常简单的。
调试仿真
可以运行本实验代码以观察运行结果,操作步骤如下:
(1)编译链接配套的工程代码后,把程序烧录到配套的ZigBee开发板中。
(2)由于本实验需要用到继电器,ZigBee标准板中已经集成了继电器,并且其控制引脚默认连接着P0_5引脚。如果使用ZigBee Mini板测试,需要在P0_5引脚处外接继电器控制引脚,并且把该继电器的VCC和GND引脚分别接入到5v的电源和地线。
(3)通过MicroUSB线给开发板上电,即可观察到继电器不断的开合。
必须要使用Micro USB线给开发板供电才能使用继电器!