基于ZigBee和STM32的智能家居控制系统的设计与实现(四)

##简述
在前几篇博客中介绍了智能家居系统的整体结构以及各个部分实现原理,感谢好多的朋友前来问候,给与了好多建议与支持,让我有了动力来写这篇博客,这篇博客作为本系统的终结篇,将会介绍剩下的问题。


##实现原理
以下会分别介绍下位机中关键部分的实现技术,包括μC/OS-II多任务实现、串口数据的读取、对数据的解析等。首先看一张下位机整体结构图,这样心里就有了比较清晰的框架了:

这里写图片描述

###下位机硬件资源介绍

  1. zigbee核心板
  2. DS18B20模块(温度传感器)
  3. MQ_2(烟雾传感器)
  4. 客厅LED流水灯(模拟真实环境)
  5. 蜂鸣器
  6. DHT11(温湿度传感器)
  7. 卧室LED灯
  8. 光敏传感器
  9. HCSR501(人体热释电)
  10. 系统检测灯
  11. MCU(STM32F103C8T6)

###μC/OS-II多任务的实现
μC/OS-II是一个可裁剪的、可固话的、可移植的、抢占式实时多任务系统内核,适用于多种微处理器和微控制器,能够移植到超过100多种微处理器应用开发中。本次下位机中就移植了这样一个小型的嵌入式操作系统,使得整个系统显示十分的流畅,而且也很好管理。

μC/OS-II的移植这里就不说了,网上有好多的介绍,其中正点原子和野火的讲的都比较详细,可以去看看,首先说明,我的这个工程也是参考他们的。一下是我的整个工程的结构,整体上还算比较详细的:

这里写图片描述

通过代码可以很清晰的看出来各个传感器的 驱动、数据的读取等细节,这里主要说说系统多的任务的实现。

主函数开始后进行了一些列的初始化操作,随后初始化了μC/OS-II系统,并且创建了一个开始任务:
各个模块初始化操作:

	delay_init();	                //延时初始化	  
	uart_init(115200);              //串口1初始化
    uart3_init(115200);             //串口3初始化(连接ZigBee接口)
    JTAG_Set(JTAG_SWD_DISABLE);     //关闭JTAG接口
	NVIC_Configuration(); 	        //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	BEEP_Init();                    //初始化蜂鸣器接口
	LED_Init();		  	            	//初始化与LED连接的硬件接口
    Scan_Key_Configuration();       //初始化按键接口
    STEP_MOTOR_Start();             //初始化步进电机接口
    Lsens_Init(); 			        		//初始化光敏传感器接口
    MQ_2_Configuration();           //初始化烟雾传感器接口
    HC_SR501_GPIO_Configuration();  //初始化红外热释点接口
    STEP_MOTOR_Configuration();     //初始化步进电机接口
    
    while(DHT11_Init())	            //DHT11初始化,检测不到会卡死在这里	
	 {
#if FLAG_SHOW_VALUE
        printf("\r\nDHT11 Init Error");
#endif
			 delay_ms(600);
	 }
#if FLAG_SHOW_VALUE
	 printf("DHT11 OK\r\n");
#endif
    
    while(DS18B20_Init())           //初始化DS18B20,检测不到会卡死在这里不断检测
    {
        printf("\r\nDS18B20 Init Error");
			  delay_ms(600);
    }
     printf("DS18B20 Init OK\r\n");

创建开始任务:

	OSInit();   
	OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
	OSStart();

开始任务创建成功后便会在开始任务里边进行其他任务的初始化操作:

//开始任务
void start_task(void *pdata)
{
		OS_CPU_SR cpu_sr = 0;
		pdata = pdata; 		  		 			  
		OSStatInit();							//初始化统计任务.这里会延时1秒钟左右	
		OS_ENTER_CRITICAL();			//进入临界区(无法被中断打断) 
    
    
    //红外热释任务
 	OSTaskCreate(hc_sr501_task,(void *)0,
    (OS_STK*)&HC_SR501_TASK_STK[HC_SR501_STK_SIZE-1],HC_SR501_TASK_PRIO);
    
    //创建温湿度任务
    OSTaskCreate(dht11_task,(void *)0,
    (OS_STK*)&DHT11_TASK_STK[DHT11_STK_SIZE - 1],DHT11_TASK_PRIO); 				   				   
    
    //创建温度任务
    OSTaskCreate(ds18b20_task,(void *)0,
    (OS_STK*)&DS18B20_TASK_STK[DS18B20_STK_SIZE-1],DS18B20_TASK_PRIO); 
    
    //创建光敏检测任务
    OSTaskCreate(telesky_task,(void *)0,
    (OS_STK*)&TELESKY_TASK_STK[TELESKY_STK_SIZE-1],TELESKY_TASK_PRIO); 
    
    //创建烟雾检测任务
    OSTaskCreate(mq_2_task,(void *)0,
    (OS_STK*)&MQ_2_TASK_STK[MQ_2_STK_SIZE-1],MQ_2_TASK_PRIO); 
    
    //创建串口3任务,用来进行和上位机通讯
    OSTaskCreate(uart3_task,(void *)0,
    (OS_STK*)&UART3_TASK_STK[UART3_STK_SIZE-1],UART3_TASK_PRIO); 
    
    //创建蜂鸣器任务
    OSTaskCreate(beep_task,(void *)0,
    (OS_STK*)&BEEP_TASK_STK[BEEP_STK_SIZE-1],BEEP_TASK_PRIO);
    
    //按键扫描任务
     OSTaskCreate(key_task,(void *)0,
     (OS_STK*)&KEY_TASK_STK[KEY_STK_SIZE-1],KEY_TASK_PRIO);
    
    //创建步进电机任务
     OSTaskCreate(step_motor_task,(void *)0,
     (OS_STK*)&STEP_MOTOR_TASK_STK[STEP_MOTOR_STK_SIZE-1],STEP_MOTOR_TASK_PRIO);
    
	OSTaskSuspend(START_TASK_PRIO);	//挂起起始任务.
	OS_EXIT_CRITICAL();				//退出临界区(可以被中断打断)
}	  

可以很清楚的看到,开始任务里边创建了多个任务,接着,各个任务创建成功后边会挂起任务,退出临界区,开始各个任务的轮训操作。

上面各个任务创建时,都指定了任务优先级,堆栈大小等信息。在app.h文件中可以看到:

//开始任务
#define START_TASK_PRIO      			             			10     //开始任务的优先级设置为最低
#define START_STK_SIZE  				             				64     //设置任务堆栈大小
OS_STK START_TASK_STK[START_STK_SIZE];                      //创建任务堆栈空间	
void start_task(void *pdata);	                            //任务函数接口

 //创建步进电机任务
#define STEP_MOTOR_TASK_PRIO                        11
#define STEP_MOTOR_STK_SIZE                         64
OS_STK STEP_MOTOR_TASK_STK[STEP_MOTOR_STK_SIZE];
void step_motor_task(void *pdata);

// //按键扫描任务
// #define KEY_TASK_PRIO  10     设置任务优先级
 #define KEY_STK_SIZE  90      //设置任务堆栈大小
OS_STK KEY_TASK_STK[KEY_STK_SIZE];                          //创建任务堆栈空间	
void key_task(void *pdata);                                 //任务函数接口

//蜂鸣器任务--根据各个传感器数据,进行报警
#define BEEP_TASK_PRIO															9
#define BEEP_STK_SIZE																64
OS_STK BEEP_TASK_STK[BEEP_STK_SIZE];
void beep_task(void *pdata);


// //人体感应模块 任务
#define HC_SR501_TASK_PRIO       			         			8 
#define HC_SR501_STK_SIZE  			     	         			64
OS_STK HC_SR501_TASK_STK[HC_SR501_STK_SIZE];
void hc_sr501_task(void *pdata);


//光敏传感器采集任务
#define TELESKY_TASK_PRIO       			       				 7       //设置任务优先级
#define TELESKY_STK_SIZE  					        				 64      //设置任务堆栈大小
OS_STK TELESKY_TASK_STK[TELESKY_STK_SIZE];                  //创建任务堆栈空间	
void telesky_task(void *pdata);                             //任务函数接口

//DHT11任务(温湿度传感器)
#define DHT11_TASK_PRIO       			           		 6       //设置任务优先级
#define DHT11_STK_SIZE  			                		 64      //设置任务堆栈大小
OS_STK DHT11_TASK_STK[DHT11_STK_SIZE];                      //创建任务堆栈空间	
void dht11_task(void *pdata);                               //任务函数接口

//MQ-2任务(烟雾传感器)
#define MQ_2_TASK_PRIO       			            		5       //设置任务优先级
#define MQ_2_STK_SIZE  			                    	90      //设置任务堆栈大小
OS_STK MQ_2_TASK_STK[MQ_2_STK_SIZE];                        //创建任务堆栈空间	
void mq_2_task(void *pdata);                                //任务函数接口

//DS18B20任务
#define DS18B20_TASK_PRIO                            4
#define DS18B20_STK_SIZE                             64
OS_STK DS18B20_TASK_STK[DS18B20_STK_SIZE];
void ds18b20_task(void *pdata);

//串口发送数据任务(用来想上位机 实时 传输输出)
#define UART3_TASK_PRIO                             3
#define UART3_STK_SIZE                              90
OS_STK UART3_TASK_STK[UART3_STK_SIZE];      
void uart3_task(void *pdata);

任务的优先级决定了任务在被打断时执行的顺序,优先级越高,越有优先权。


###串口数据的读取
在单片机开发过程中串口的操作可以算是最普通也是最基础的操作了,好多东西都是需要通过串口进行输出,这样可以对程序执行过程中的一些中间数据进行输出。这里主要说说串口数据的读取。

数据的读取使用中断的方式进行交互,这样效率很高。这里执行看核心代码即可:

void recv_zigbee_msg(u8 Res)
{
    if(Res == END_RESD_MSG)
    {
        FLAG_ZIGBEE_RECV_BEGIN = 0;
		USART3_RX_BUF[USART3_RX_STA++&0X3FFF] = Res;
		cpynbyte2buffer(USART3_RX_BUF, USART3_RX_STA);
		USART3_RX_STA = 0;
		memset(USART3_RX_BUF, 0, sizeof(USART3_RX_BUF));
    }else if(Res == BGN_RESD_MSG || FLAG_ZIGBEE_RECV_BEGIN)
	{ 
		FLAG_ZIGBEE_RECV_BEGIN = 1;
		USART3_RX_BUF[USART3_RX_STA++&0X3FFF] = Res;
  	}else
	 {
		FLAG_ZIGBEE_RECV_BEGIN = 0;
		USART3_RX_STA = 0;
		memset(USART3_RX_BUF, 0, sizeof(USART3_RX_BUF));
 	 }
}

这里接收数据时对帧头进行了一个简单的判断,正确后接着接收,碰到帧尾后将数据拷贝到了一个缓冲区中,然后清空接收缓冲。整个过程就这么简单。


###数据的打包和解析操作
####数据解析
前一部分介绍了数据的接收,这里再说说对数据的解析。从上面的代码中可以看出来有个函数:**cpynbyte2buffer(),**该函数是吸纳对数据的解析操作,如下所示:

void cpynbyte2buffer(u8 *data, u8 len)
{
 	int i = 0;
	u8 data_postion = 0;
      
    protocol recvMsg;
    if (BGN_RESD_MSG == data[data_postion++])
    {
        printf("begin\r\n");
        recvMsg.potocol_len = data[data_postion++];
        recvMsg.device = data[data_postion++];
        recvMsg.device_cmd = data[data_postion++];
        recvMsg.data_len = data[data_postion++];
		if (recvMsg.data_len > 0)
		{
			recvMsg.data = (u8 *)malloc(sizeof(u8)*recvMsg.data_len);
		    for (i = 0;i < recvMsg.data_len ;i++)
				{
					recvMsg.data[i] = data[data_postion++];
				}
		}
        
        if (data[data_postion] == END_RESD_MSG)
        {
            printf("end\r\n");
            recv_data(&recvMsg);
        }
    }
}

在上面对数据结构体进行了赋值后便开始了整整的数据或者命令的执行,如 **recv_data(protocol *protocol)**所示:

void recv_data(protocol *protocol)
{
   switch(protocol->device)
   {
			 case MODULE_BEEP:                  exec_module_beep(protocol);   						break;
			 case MODULE_BED_ROOM_LED_LEFT:     exec_module_led(protocol);    						break; 
			 case MODULE_BED_ROOM_LED_RIGHT:    exec_module_led(protocol);    						break; 
			 case MODULE_PARLOUR_LED_MAIN:      exec_module_led(protocol);    						break;
			 case MODULE_PARLOUR_LED_TOP:       exec_module_led(protocol);    						break;
			 case MODULE_PARLOUR_LED_HELP:      exec_module_led(protocol);    						break;
			 case MODULE_KITCHIN_LED:           exec_module_led(protocol);    						break;
			 //case MODULE_CURTAIN:               exec_module_curtain(protocol);						break;
			 case MODULE_ALL_LED:								exec_module_all_led(protocol);						break;
		   //case MODULE_LEAVE_HOME:						exec_module_leave_home(protocol);				break;
			 case MODULE_GO_HOME:								exec_module_go_home(protocol);						break;
			 case MODULE_SMOKE:									exec_module_change_smoke_value(protocol);	break;
			 case MODULE_DS18B20:								exec_module_change_parlour_temp(protocol);break;
			 case MODULE_DHT11_HUM:							exec_module_change_parlour_hum(protocol);	break;
			 default:                                                        				  		break;
    }
}

####数据打包发送

数据发送任务函数:

    pack_send_data(MODULE_DS18B20, PROTOCOL_FULL_DATA, DATA_SIZE,parlour_temp_data);    //客厅温度
    delay_ms(400);
    pack_send_data(MODULE_DHT11_TEMP, PROTOCOL_FULL_DATA, DATA_SIZE, bed_tempture_data);//卧室温度
    delay_ms(400);
    pack_send_data(MODULE_DHT11_HUM,PROTOCOL_FULL_DATA,DATA_SIZE,humidity_data);        //客厅湿度
    delay_ms(400);
    pack_send_data(MODULE_SMOKE,PROTOCOL_FULL_DATA,DATA_SIZE,kitchen_smoke_data);       //厨房浓度值
		delay_ms(400);

上面是数据发送任务里调用的发送函数,该函数中对各个传感器采集到的数据进行了打包并发送操作。可以看打包发送函数:

*void pack_send_data(u8 drive, u8 drive_cmd, u8 data_len, u8 data)

/*
  * @function   pack_send_data
  * @input
  * @output
  * @brief   通讯协议功能的实现.
  *          本文件实现了数据包的发送和接受,以及根据数据类型执行对应的函数
  */
void pack_send_data(u8 drive, u8 drive_cmd, u8 data_len, u8 *data)
{
	protocol Msg;
	Msg.send_begin 		= BGN_RESD_MSG;         //帧头 #

    //若数据长度 > 0,即有数据
    if(data_len > 0)
    {   
        Msg.potocol_len = PROTOCOL_BASIC_SIZE + data_len - 1;
    }else
    {
        Msg.potocol_len = PROTOCOL_BASIC_SIZE;
    }
	
    //填充数据结构体
	Msg.device 	    = drive;
	Msg.device_cmd 	= drive_cmd;
	Msg.data_len 	= data_len;
	Msg.data 		= data;
	Msg.send_end	= END_RESD_MSG;         //帧尾*
	
    //发送数据
	send_data(&Msg);
}

这里列出来核心代码,具体实现代码请看工程。打包发送函数中主要做了2件事情:一:对数据成员初始化,二:判断一帧数据的大小!数据大小值很重要的,这在数据解析过程可需要用到。

至此,整个毕设的东西差不多都介绍完了。zigbee部分 这里不讲解。

忙碌的几个月的生活还是挺美好的!ok
加油吧,未来是美好的!


技术在于交流、分享……
Email:kevinlq0912@163.com
QQ:2313828706
公众号:devstone


这里写图片描述

备注:麻烦大家看下文章中说明,要源码要资料的,文章已经说了到哪里可以免费下载所有资料。如果还不知道,可以关注微信公众号:devstone,后台回复关键词下载。

  • 15
    点赞
  • 157
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
上位机使用Qt编写,实现了基本的监控。主要包括监控室内温度、湿度、烟雾浓度,用led灯模拟控制家中的灯。界面良好。 STM32是由意法半导体(STMicroelectronics)公司推出的基于ARM Cortex-M内核的高性能、低功耗、高性价比32位微控制器系列。自其面世以来,STM32凭借其广泛的适用性和卓越的特性,已成为嵌入式系统设计领域的主流选择之一,广泛应用于工业控制、消费电子、物联网、汽车电子、医疗设备、智能家居等多个领域。 内核与架构 STM32产品线采用了不同版本的ARM Cortex-M内核,包括M0、M0+、M3、M4、M7等,分别对应不同级别的性能需求。这些内核提供单周期乘法、硬件除法、DSP指令集、浮点单元(FPU)等功能,以满足不同应用场景中的计算密集型任务需求。处理器架构遵循哈佛结构,具有独立的指令总线和数据总线,确保高效的代码执行和数据访问。 丰富的外设与接口 STM32微控制器集成了丰富的外设资源,以适应各种复杂系统设计。这些外设包括但不限于: 通信接口:如USART、UART、SPI、I2C、CAN、USB(全速/高速)、Ethernet、无线连接模块(如BLE、Wi-Fi)等,用于实现设备间的串行通信和网络连接。 定时器:多种通用定时器、高级定时器、基本定时器以及PWM输出,支持定时、计数、脉冲捕获、电机控制等多种功能。 模拟外设:高精度ADC(模数转换器)、DAC(数模转换器)、比较器、温度传感器等,用于采集和处理模拟信号。 存储器:内置Flash和SRAM,容量从几KB到几MB不等,满足不同应用的数据存储和运行空间需求。部分型号还支持外部存储器接口(如FSMC、Octo-SPI)以扩展存储能力。 安全与保护机制:如加密加速器、安全单元、内存保护单元(MPU)、看门狗定时器、时钟安全系统(CSS)等,保障系统安全稳定运行。 开发环境与生态系统 STM32拥有强大的软件支持和生态系统,简化开发流程并加速产品上市时间: 开发工具:官方提供STM32CubeMX初始化配置工具,帮助开发者快速进行项目设置、外设配置及代码生成。此外,还有STM32CubeIDE集成开发环境,集成了编译器、调试器和仿真器支持。 软件库:STM32Cube软件包包含HAL(硬件抽象层)库和LL(低层)库,前者提供跨平台、跨系列的统一API接口,后者直接面向寄存器提供高效访问。同时,还提供各类外设驱动、中间件组件(如FreeRTOS、FatFS、LwIP等)以及特定应用框架(如STM32Cube.AI for AI推理)。 社区与资源:ST官方社区、论坛、博客、技术文档、培训材料、应用笔记、用户案例等资源丰富,为开发者提供全方位的技术支持和交流平台。 产品线与封装 STM32产品线按性能、功耗、外设组合等特性划分为多个子系列,如STM32F、STM32L、STM32G、STM32H等,每个子系列下又包含多种型号,以适应不同成本、性能、尺寸和功耗要求。封装形式多样,从小型QFN、LQFP到大型BGA,满足不同应用场景的封装密度和散热需求。 综上所述,STM32微控制器以其强大的内核性能、丰富的外设集成、完善的开发支持和广泛的市场应用,为嵌入式系统设计提供了高度灵活且极具竞争力的解决方案
基于ZigbeeSTM32智能家居系统源代码可以分为两个模块:Zigbee通信模块和STM32控制模块。 Zigbee通信模块的主要功能是实现智能家居设备之间的无线通信。在源代码中,首先需要初始化Zigbee模块,并设置通信的参数。然后,通过监听Zigbee网络中的数据包来实现设备之间的通信。例如,当一个设备发送一个控制指令时,其他设备可以接收到该指令并做出相应的反应。此外,Zigbee通信模块还可以处理网络连接的建立和断开等操作。 STM32控制模块的主要功能是控制智能家居设备的操作。在源代码中,首先需要初始化STM32的GPIO口和串口。然后,通过串口接收来自Zigbee模块的指令,并将指令解析为具体的操作。例如,当接收到一个打开灯的指令时,STM32可以控制相关的GPIO口输出高电平,以打开灯。同时,STM32还可以获取各个传感器的数据,并通过Zigbee模块发送到其他设备。 此外,源代码还需要实现一些额外的功能,例如设备的配对和注册等。设备配对的过程中,源代码可以实现设备之间的认证和身份验证等操作。注册功能则可以实现设备的网络注册和信息存储。 综上所述,基于ZigbeeSTM32智能家居系统源代码主要包括Zigbee通信模块和STM32控制模块。Zigbee通信模块用于实现设备之间的无线通信,而STM32控制模块则用于控制智能家居设备的操作。在源代码中还需要实现设备配对和注册等功能。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值