【CC2530入門教程-增強版】基礎技能綜合實訓案例(基礎版)-終端源碼
廣東職業技術學院 歐浩源
一、關於硬件電路
關於這個綜合實訓案例,具體需求詳見《【CC2530入門教程-增強版】基礎技能綜合實訓案例(基礎版)-題目需求》。
我自己實在“全國職業院校技能大賽--物聯網技術應用賽項”的Zigbee模塊上實現的。該模塊的電路應該和TI公司官方評估板的推薦電路差不多,我想現在市面上很多開發板也是參考這樣的電路設計,只要您使用的開發板上有LED燈、按鍵輸入、串口輸出和一路A/D轉換,都可以使用我的源碼實現這個綜合實訓,硬件電路引腳連接如下圖:
二、程序設計思路
整個測控終端的代碼大約有370多行,在這里不能逐一的分析和講述。各個基礎功能模塊的操作和《CC2530入門教材》中的案例沒有太大差異,其核心代碼為主要業務邏輯的處理。這里的主函數不想前面單一功能模塊案例的那么簡單了,實際上它是一個中斷服務與前后台處理的程序結構。既要處理本地的各種功能,又要響應遠程的各種命令,不但要管理各個基本任務,而且還要進行運行狀態的切換。在功能比較復雜的系統中,可能還需要引入基於狀態機思路的事件調度思想或者更高級的程序框架結構。
串口數據接收是測控終端的第一個關鍵地方。在這里不是要接收一個字節或者一串字符,而是要完整正確接收完8個字節的一幀數據,在數據接收完成后通過一個標志F_DataRecv通知主函數一幀數據接收完成可以進行解析和執行了。命令幀的正確解析和順利執行是測控終端運行的樞紐,這個部分沒有難點,主要是根據通信規約來逐一解析。既然測控終端要采集光照數據和記錄設備狀態,那么ADC數據采集部分也是一個核心部分,但是沒有太大的難點。在進行閾值比較,進行燈光自動開關的時候,不要忘記對照明設備狀態的實時登記哦。
三、項目源代碼
#include "ioCC2530.h" #define D_SEC P1_1 #define D_DAT P1_0 #define D_AUTO P1_4 #define D_LIGHT P1_3 #define K_LIGHT P1_2 #define K_ALARM P0_1 unsigned char F_Alarm = 0; unsigned char F_Clear = 0; unsigned char F_02Second = 0; unsigned char F_RecvData = 0; unsigned char F_SendInfo = 0; unsigned char ID = 0x01; unsigned char SendDat[8],RecvDat[8]; unsigned char count_t1,count_recv; /*=======================簡單的延時函數========================*/ void Delay(unsigned int t) { while(t--); } /*======================端口初始化函數========================*/ void Init_Port() { P1SEL &= ~0x1b; //P1_0、P1_1、P1_3和P1_4作為通用I/O端口 P1DIR |= 0x1b; //P1_0、P1_1、P1_3和P1_4端口輸出 P1SEL &= ~0x04; //P1_2作為通用I/O端口 P1DIR &= ~0x04; //P1_2端口輸入 P0SEL &= ~0x02; //P0_1作為通用I/O端口 P0DIR &= ~0x02; //P0_1端口輸入 D_SEC = 0; //秒閃燈關閉 D_DAT = 0; //數據燈關閉 D_AUTO = 0; //應急燈關閉 D_LIGHT = 0; //照明燈關閉 } /*=======================燈光測試函數========================*/ void Check_Light() { D_SEC = 1; Delay(60000); D_DAT = 1; Delay(60000); D_AUTO = 1; Delay(60000); D_LIGHT = 1; Delay(60000); D_SEC = 0; Delay(60000); D_DAT = 0; Delay(60000); D_AUTO = 0; Delay(60000); D_LIGHT = 0; Delay(60000); } /*========================打開照明燈=========================*/ void Open_Light() { D_LIGHT = 1; //打開照明燈 SendDat[5] |= 0x01; //設置照明燈為打開狀態 } /*========================關閉照明燈=========================*/ void Close_Light() { D_LIGHT = 0; //關閉照明燈 SendDat[5] &= ~0x01; //設置照明燈為關閉狀態 } /*======================手動控制照明燈=======================*/ void Control_Light() { if(K_LIGHT == 0) //判斷按鍵的信號 { Delay(200); //去抖動 if(K_LIGHT == 0) //確認按鍵按下 { while(K_LIGHT == 0); //等待按鍵松開 if((SendDat[5]&0x01) == 0x00) //判斷燈光當前狀態 { Open_Light(); } else { Close_Light(); } } } } /*=======================定時器1初始化========================*/ void Init_Timer1() { T1CC0L = 0xd4; //設置最大計數值低8位 T1CC0H = 0x30; //設置最大計數值高8位 T1IE = 1; EA = 1; T1CTL = 0x0f; //分頻系數是128,正計數/倒計數模式 } /*====================定時器1服務函數========================*/ #pragma vector = T1_VECTOR __interrupt void Timer1_int() { T1STAT &= ~0x20; //清除定時器1的溢出中斷標志位 count_t1++; //定時器1溢出一次加1,溢出周期為0.2S F_02Second = 1; if(count_t1 == 5) //定時1秒的時間間隔到。 { D_SEC = ~D_SEC; //切換秒閃燈 count_t1 = 0; } } /*====================ADC0初始化函數========================*/ void Init_ADC0() { P0SEL |= 0x01; //P0_0端口設置為外設功能 P0DIR &= ~0x01; //P0_0端口設置為輸入端口 APCFG |= 0x01; //P0_0作為模擬I/O使用 } /*====================ADC0數據讀取函數=====================*/ void Read_ADC0() { ADCIF = 0; //參考電壓選擇AVDD5引腳,256抽取率,AIN0通道0 ADCCON3 = (0x80 | 0x10 | 0x00); while(!ADCIF); //等待A/D轉換完成, SendDat[3] = ADCH; //讀取ADC數據高位寄存器 SendDat[4] = ADCL; //讀取ADC數據低位寄存器 } /*================判斷光照度並執行應急照明===============*/ void CheckDataAndExceute() { if(SendDat[3] < 0x06) //光照閾值比較 { D_AUTO = 1; //自動點亮應急燈 SendDat[5] |= 0x02; //標志應急燈打開狀態 } else{ D_AUTO = 0; //自動關閉應急燈 SendDat[5] &= ~0x02; //標志應急燈關閉狀態 } } /*=====================串口0初始化函數====================*/ void Init_Uart0() { PERCFG = 0x00; //串口0的引腳映射到位置1,即P0_2和P0_3 P0SEL = 0x0C; //將P0_2和P0_3端口設置成外設功能 U0BAUD = 59; //16MHz的系統時鍾產生9600BPS的波特率 U0GCR = 9; U0UCR |= 0x80; //禁止流控,8位數據,清除緩沖器 U0CSR |= 0xC0; //選擇UART模式,使能接收器 UTX0IF = 0; //清除TX發送中斷標志 URX0IF = 0; //清除RX接收中斷標志 URX0IE = 1; //使能URAT0的接收中斷 EA = 1; //使能總中斷 } /*===================串口0接收中斷服務函數==================*/ #pragma vector = URX0_VECTOR __interrupt void UR0_SendInt() { unsigned char dat; URX0IF = 0; //清除RX接收中斷標志 dat = U0DBUF; //終端接收到一個字節數據 if(dat == 0xaf) //首先判斷是不是0xaf { RecvDat[0] = dat; //如果是0xaf則放到幀頭的位置 } else if(RecvDat[0] == 0xaf) { RecvDat[count_recv+1] = dat; count_recv++; //接收完數據幀剩下的7個字節 if(count_recv == 7) { count_recv = 0; //幀計數清0 F_RecvData = 1; //標志一個8位的數據幀接收完成 } } } /*====================清空接收緩存函數====================*/ void Clear_RecvData() { unsigned char i; for(i = 0; i < 8; i++) { RecvDat[i] = 0; } } /*===================解析並執行上位機命令==================*/ void ExecuteCmd() { if(RecvDat[0] == 0xaf && RecvDat[7] == 0xfa) { if(ID == RecvDat[1]) //首先判斷命令幀的ID和本機ID是否一致 { switch(RecvDat[2]) //然后解析命令域 { case 0x01: //啟動數據采集功能 F_SendInfo = 1; D_DAT = 1; break; case 0x02: //關閉數據采集功能 F_SendInfo = 0; D_DAT = 0; break; case 0x03: //遠程打開照明燈 Open_Light(); break; case 0x04: //遠程關閉照明燈 Close_Light(); break; case 0x0f: //解除現場報警 if(F_Alarm == 1) //只有在已經觸發報警的情況下 { F_Clear = 1; //才會解除現場報警 } break; } } } Clear_RecvData(); //解析命令完成后清空接收數據緩存 } /*====================發送單個字節的函數===================*/ void UR0SendByte(unsigned char dat) { U0DBUF = dat; while(!UTX0IF); UTX0IF = 0; } /*=====================發送數據幀的函數====================*/ void UR0SendString(unsigned char *str, unsigned char num) { unsigned char i; for(i = 0; i < num; i++) { UR0SendByte(str[i]); } } /*====================計算校驗和的函數====================*/ void CheckSum() { unsigned char i; SendDat[6] = 0; for(i = 0; i < 6; i++) SendDat[6] += SendDat[i]; } /*===================初始化發送緩存函數===================*/ void Init_SendData() { SendDat[0] = 0xaf; SendDat[1] = ID; SendDat[2] = 0xff; SendDat[3] = 0x00; SendDat[4] = 0x00; SendDat[5] = 0x00; SendDat[6] = 0x00; SendDat[7] = 0xfa; } /*===================終端上線命令幀函數===================*/ void SendSysOpenCmd() { Init_SendData(); SendDat[2] = 0x10; CheckSum(); UR0SendString(SendDat, 8); } /*===================上傳終端數據的函數===================*/ void Send_Infomation() { SendDat[2] = 0x11; CheckSum(); UR0SendString(SendDat, 8); } /*====================上傳報警幀的函數====================*/ void Send_Alarm() { SendDat[2] = 0x1f; CheckSum(); UR0SendString(SendDat, 8); } /*====================秒時間到服務函數====================*/ void WorkFor02Second() { Read_ADC0(); //每秒鍾采樣一次光照度傳感器的電壓 if(F_SendInfo == 1) //上傳測控終端的數據及設備狀態 { Send_Infomation(); } } /*=======================外部中斷初始化========================*/ void Init_INTP() { P0IE = 1; //端口0中斷使能 P0IEN |= 0x02; //端口P0_1外部中斷使能 PICTL |= 0x01; //端口P0_0到P0_7下降沿觸發 EA = 1; //使能總中斷 } /*====================外部中斷服務函數========================*/ #pragma vector = P0INT_VECTOR //外部中斷0的向量入口 __interrupt void Int0_Sevice() { P0IFG &= ~ 0x02; //軟件清除P0_1端口的標志位 P0IF = 0; //軟件清除P0端口的標志位 F_Alarm = 1; //設置報警標志位 Send_Alarm(); //向上位機發送現場報警命令幀 } /*=======================報警燈光函數========================*/ void Alarm_Light() { D_SEC = 1; D_DAT = 1; D_AUTO = 1; D_LIGHT = 1; Delay(60000); Delay(60000); Delay(60000); D_SEC = 0; D_DAT = 0; D_AUTO = 0; D_LIGHT = 0; Delay(60000); Delay(60000); Delay(60000); if(F_Clear == 1) //上位機解除現場報警 { F_Clear = 0; //恢復報警安全鎖 F_Alarm = 0; //解除報警,恢復正常工作狀態 Check_Light(); //重新檢查照明設備狀態 SendSysOpenCmd(); //重新發送終端上線命令幀 T1CTL = 0x0f; //重新打開定時器 } } /*===================測控終端初始化函數====================*/ void Init_System() { Init_Port(); //初始化通用I/O端口 Init_INTP(); //初始化外部中斷 Init_Timer1(); //初始化定時器1 Init_ADC0(); //初始化ADC0 Init_Uart0(); //初始化串口0 Init_SendData(); //初始化串口發送數據緩存 Clear_RecvData(); //初始化串口接收數據緩存 Check_Light(); //檢測照明設備工作狀態 SendSysOpenCmd(); //向上位機發送終端上線命令幀 } /*=======================主函數===========================*/ void main() { Init_System(); //測控終端初始化 while(1) { if(F_RecvData == 1) //接收到一個完整的數據幀 { ExecuteCmd(); //解析命令幀並執行功能 F_RecvData = 0; //清除命令幀接收完成標志 } if(F_Alarm == 0) //現場正常工作狀態 { Control_Light(); //手動控制照明燈 CheckDataAndExceute(); //自動控制應急燈 if(F_02Second == 1) //定時0.2秒到 { WorkFor02Second(); //執行0.2秒鍾應該做的事情 F_02Second = 0; //清除定時0.2秒到的標志 } } else //現場進入報警工作狀態 { T1CTL = 0x00; //關閉定時器 F_02Second = 0; //清除定時標志 F_SendInfo = 0; //清除數據發送標志 Alarm_Light(); //開啟現場報警燈 } } }