项目处于开发阶段,尚未完成,仍在不断更新
一、系统功能要求
三、软硬件精选
1、处理器:stm32 / CC2530(常用于做智能家具,支持 Z-Stack 协议栈<基于ZigBee实现的>),stm32W系列的芯片也支持 Z-Stack 协议栈。
我们选用 STM32F103VBT6
2、温湿度传感器:(1)Pt100 (热电偶),使用时需要进行校准,但是精度高。
(2)DS18B20, 单总线的
(3)DHT11:温湿度传感器(精度低) ,学习过程中暂时选用 DHT11
3、存储单元:存储阈值,
外部存储单元:AT24C02,2K存储空间,(EEPROM); Flash,128M。
内部存储单元:ROM(单片机内部的 Flash)。我们选用内部存储单元。
选用存储单元要考虑其寿命,存储单元的寿命与擦写次数有关(一般为10万次)
4、输出设备:(1)数码管:占用 16 个I/O口,显示的内容有限。
(2)LCD1602:占用 11 个I/O口(八根data线,三根电源线),两行,每行显示16个字符,不能显示汉字。
(3)TFT:多彩屏,可现实各种颜色的字以及汉字,有65536中颜色可选
有三种驱动方式:并口驱动,SPI驱动,FSMC驱动(刷屏频率更快)。
(4)LCD12864:每行显示128个字符,共64行(可显示汉字)。
(5)串口屏:串口驱动(三根线:Rx,Tx,GND),工作中使用最多。
5、通信协议:
6、传输介质:IIC、USB、SPI、SDIO 的传输近距离太近,不适合;
CAN 配置比较麻烦,但是更可靠更安全,多用于汽车以及军用;
无线,需要买一个无线模块,然后与 I/O 口连接,可以选用;
这里我们选用有线传输介质:485总线,232总线,先使用232总线,后期换为485总线。
7、输入设备:键盘、触摸屏
8、发声设备:蜂鸣器、喇叭
9、发光设备:LED
10、时钟电路:32内部的RTC
11、操作系统:uC/OS-II,创建多个任务(1、根据协议接收指令;2、指令响应;3、温湿度采样;4、输入的检测,切屏)
四、代码实现
1、由定时器控制串口接收一个字符串,并发送给上位机。
串口、定时器的配置函数略去,,
中断函数:
接收中断:接收数据,打开定时器;
定时器中断:关闭接收中断,关闭定时器,定时器计数值清零,打开发送中断;
发送中断:发送数据,发送完成后,打开接收中断,关闭发送中断。
unsigned char rsvbuff[100];
unsigned char *rsvBufferPoint;
unsigned intrsvbuffcnt;
void USART1_IRQHandler(void)
{
if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) == SET)
{
USART_ClearFlag(USART1, USART_FLAG_ORE);
USART_ReceiveData(USART1);
}
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
rsvbuff[rsvbuffcnt++] = USART_ReceiveData(USART1);
USART_ClearFlag(USART1, USART_IT_RXNE);
TIM_Cmd(TIM2, ENABLE);
rsvBufferPoint = rsvbuff;
}
if (USART_GetITStatus(USART1, USART_IT_TXE) == SET)
{
if (rsvbuffcnt != 0)
{
USART_ClearFlag(USART1, USART_IT_TXE);
USART_SendData(USART1, (char)*rsvBufferPoint);
rsvBufferPoint++;
rsvbuffcnt--;
}else
{
while (USART_GetITStatus(USART1, USART_IT_TC) == SET) ;
USART_ClearFlag(USART1, USART_IT_TC);
USART_ClearFlag(USART1, USART_IT_TXE);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetFlagStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearFlag(TIM2, TIM_IT_Update);
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
TIM_Cmd(TIM2, DISABLE);
TIM_SetCounter(TIM2, 0x0000);
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}
}
2、DHT11 采集温湿度,并显示在 LCD1602 上
配置好 LCD1602、DHT11:
LCD1602的配置:参考《LCD1602数据手册》的时序图,和《LCD1602液晶完整中文资料》的11条指令
void LCD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
//LCD1602
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOD, &GPIO_InitStruct);
while(LCD_ReadStatus() & 0x80)
{
}
LCD_WriteCmd(0x38); //??
// LCD_WriteCmd(0x0F); //???,????,?????
// LCD_WriteCmd(0x06); //??????,???????????
// LCD_WriteCmd(0x14);
LCD_WriteCmd(0x0C);
LCD_WriteCmd(0x06);
LCD_WriteCmd(0x02);
delay(1000);
LCD_WriteCmd(0x01); //??
}
void LCD_WriteCmd(unsigned char Cmd)
{
GPIO_ResetBits(GPIOD, GPIO_Pin_5); //RS = 0
GPIO_ResetBits(GPIOD, GPIO_Pin_6); //RW = 0
//delay(10000);
GPIO_SetBits(GPIOD, GPIO_Pin_7); //EN = 1
GPIO_Write(GPIOC, (GPIO_ReadOutputData(GPIOC) & 0xFF00) | Cmd);
delay(1000);
//GPIOC->ODR = Cmd;
GPIO_ResetBits(GPIOD, GPIO_Pin_7); //EN = 0
}
void LCD_WriteData(unsigned char Data)
{
GPIO_SetBits(GPIOD, GPIO_Pin_5); //RS = 1
GPIO_ResetBits(GPIOD, GPIO_Pin_6); //RW = 0
//GPIOC->ODR = Data;
GPIO_SetBits(GPIOD, GPIO_Pin_7); //EN = 1
GPIO_Write(GPIOC, (GPIO_ReadOutputData(GPIOC) & 0xFF00) | Data);
delay(1000);
//GPIOC->ODR = Data;
GPIO_ResetBits(GPIOD, GPIO_Pin_7); //EN = 0
}
unsigned char LCD_ReadStatus(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
unsigned char tmp = 0;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_ResetBits(GPIOD, GPIO_Pin_5); //RS = 0
GPIO_SetBits(GPIOD, GPIO_Pin_6); //RW = 1
GPIO_ResetBits(GPIOD, GPIO_Pin_7); //EN = 0
GPIO_SetBits(GPIOD, GPIO_Pin_7); //EN = 1
delay(1000);
tmp = GPIO_ReadInputData(GPIOC) & 0x00FF;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStruct);
return tmp;
}
unsigned char LCD_ReadData(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
unsigned char tmp = 0;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_SetBits(GPIOD, GPIO_Pin_5); //RS = 1
GPIO_SetBits(GPIOD, GPIO_Pin_6); //RW = 1
GPIO_ResetBits(GPIOD, GPIO_Pin_7); //EN = 0
GPIO_SetBits(GPIOD, GPIO_Pin_7); //EN = 1
delay(1000);
tmp = GPIO_ReadInputData(GPIOC) & 0x00FF;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStruct);
return tmp;
}
void LCD_ShowString(unsigned char *str, unsigned char m, unsigned char n)
{
if(m == 1)
{
LCD_WriteCmd(0x80 + n);
}
if(m == 2)
{
LCD_WriteCmd(0x80 + n + 0x40);
}
while(*str != '\0')
{
LCD_WriteData(*str);
str++;
}
}
DHT11 的配置:参考《DHT11中文资料及C例程》时序图
*配置函数略去
void DHT11_Read_Data()
{
unsigned int i = 0;
char humidity[9] = "humidity=";
char temputure[10] = "temputure=";
char check[] = "Device Not Available\n";
char error_data[] = "Error Data\n";
unsigned int Data_Buff[5];
unsigned char rcvBuff[30];
DHT11_Rst();
if (DHT11_Check() == 0)
{
while((!DHT11_DQ));
while(DHT11_DQ);
for (i = 0; i < 5; i++)
{
Data_Buff[i] = DHT11_Read_Byte();
}
if ((Data_Buff[0]+Data_Buff[1]+Data_Buff[2]+Data_Buff[3]) == Data_Buff[4])
{
i = 0;
sprintf(rcvBuff, "H: %d T: %d", Data_Buff[0], Data_Buff[2]);
LCD_ShowString(rcvBuff, 1, 0);
}else
{
i = 0;
}
}else
{
// Usart_Send_String(check,21);
// LED2 = 0;
}
}
然后,在主函数调用这个函数即可显示。
3、用芯片内部的RTC,将时间显示在LCD上
//设置初始时间
int main()
{
struct tm tm_ini;
LCD_Init();
DHT11_Init();
tm_ini.tm_year = 2017;
tm_ini.tm_mon = 3;
tm_ini.tm_mday = 18;
tm_ini.tm_hour = 15;
tm_ini.tm_min = 34;
tm_ini.tm_sec = 0;
RTC_Config();
Time_SetCalendarTime(tm_ini);
void show_rtc_lcd(void)
{
struct tm time_my;
unsigned char rtc_ymd_buff[20];
unsigned char rtc_hms_buff[20];
time_my = Time_GetCalendarTime();
time_my.tm_mon += 1;
sprintf(rtc_ymd_buff, "%04d-%02d-%02d.", time_my.tm_year, time_my.tm_mon, time_my.tm_mday);
sprintf(rtc_hms_buff, "%02d:%02d:%02d.", time_my.tm_hour, time_my.tm_min, time_my.tm_sec);
LCD_ShowString(rtc_ymd_buff, 2, 0);
LCD_ShowString(rtc_hms_buff, 2, 11);
}
4、使用EEPROM保存阈值,并可以读出显示在LCD上
void test(void)
{
int i = 0;
u8 read[20];
int len;
th_threvalue th;
th_threvalue *thread;
u8 *th_point = (u8*)&th;
th.hum_threvalue = 50;
th.temp_threvalue =50;
IIC_Configuration();
len = sizeof(th);
for (i = 0; i < len; i++)
{
IIC_Byte_Write(*th_point++, START_ADDR + i);
SysTick_ms(100);
}
for (i = 0; i < len; i++)
{
IIC_Byte_Read( &read[i], START_ADDR + i);
}
thread = (th_threvalue*)read;
sprintf(read, "t:%d h:%d", thread->temp_threvalue, thread->hum_threvalue);
LCD_ShowString(read, 2, 0);
}
使用 char* 指向结构体,每次一个字节的读取结构体的内容。
5、通信协议
室内温湿度监控系统通信协议
通信协议组成
帧头( SHE)+数据部分+校验和(1Byte)+帧尾( END)
说明: 校验和为数据部分所有字节内容相加的结果, 如果校验和的数值超过十六进制的 FF,也就是 255. 就要求其补码(取反加 1)作为校验和。
数据部分格式
数据长度 地址 指令 数据内容
1 Byte 1 Byte 1 Byte n Byte
数据长度: 数据长度 1 Byte+地址 1 Byte+指令 1 Byte+数据内容 n Byte
地 址: 设备本地地址
指令说明:
命令类型 (Bit:7-0)
W 监控平台向某节点设置参数
R 监控平台向某节点读取参数
N 监控平台取消某节点报警功能
Q 监控平台向某节点请求实时数据
E 错误应答