在前幾篇博客中介紹了智能家居系統的整體結構和各個部份實現原理,感謝好多的朋友前來問候,給與了好多建議與支持,讓我有了動力來寫這篇博客,這篇博客作為本系統的終結篇,將會介紹剩下的問題。
以下會分別介紹下位機中關鍵部份的實現技術,包括μC/OS-II多任務實現、串口數據的讀取、對數據的解析等。首先看1張下位機整體結構圖,這樣心里就有了比較清晰的框架了:
μC/OS-II是1個可裁剪的、可固話的、可移植的、搶占式實時多任務系統內核,適用于多種微處理器和微控制器,能夠移植到超過100多種微處理器利用開發中。本次下位機中就移植了這樣1個小型的嵌入式操作系統,使得全部系統顯示10分的流暢,而且也很好管理。
μC/OS-II的移植這里就不說了,網上有好多的介紹,其中正點原子和野火的講的都比較詳細,可以去看看,首先說明,我的這個工程也是參考他們的。1下是我的全部工程的結構,整體上還算比較詳細的:
通過代碼可以很清晰的看出來各個傳感器的 驅動、數據的讀取等細節,這里主要說說系統多的任務的實現。
主函數開始落后行了1些列的初始化操作,隨后初始化了μC/OS-II系統,并且創建了1個開始任務:
各個模塊初始化操作:
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⑴],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⑵任務(煙霧傳感器)
#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);
任務的優先級決定了任務在被打斷時履行的順序,優先級越高,越有優先權。
在單片機開發進程中串口的操作可以算是最普通也是最基礎的操作了,好多東西都是需要通過串口進行輸出,這樣可以對程序履行進程中的1些中間數據進行輸出。這里主要說說串口數據的讀取。
數據的讀取使用中斷的方式進行交互,這樣效力很高。這里履行看核心代碼便可:
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));
}
}
這里接收數據時對幀頭進行了1個簡單的判斷,正確后接著接收,碰到幀尾后將數據拷貝到了1個緩沖區中,然后清空接收緩沖。全部進程就這么簡單。
前1部份介紹了數據的接收,這里再說說對數據的解析。從上面的代碼中可以看出來有個函數: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件事情:1:對數據成員初始化,2:判斷1幀數據的大小!數據大小值很重要的,這在數據解析進程可需要用到。
至此,全部畢設的東西差不多都介紹完了。zigbee部份 這里不講授。
繁忙的幾個月的生活還是挺美好的!ok
加油吧,未來是美好的!
技術在于交換、分享……
Email:kevinlq0912@163.com
QQ:936563422