最近在自學 Zigbee,天天的主要是任務是:看博客,看 CC2530 的 datasheet 和實踐,熟悉片上的 SFR 以及控制板子。函數
學和作內容包括:IO、外部中斷、Timer1/3/四、串口實驗、ADC溫度的轉換、看門狗、Sleep Timer 和 DMA。工具
以後作了一個綜合的小實驗,基於 CC2530 的溫度監測系統,關於協議棧的部分還在學習,因此這個實驗沒有使用到協議棧。學習
檢驗學習成果,熟悉 sfr 的配置和片上資源的使用。編碼
硬件;CC2530、CCDebug、串口線spa
軟件:IAR Embedded Workbench、串口調試助手設計
1. 系統每 2s 統計一次溫度,由定時器1 來精肯定時;3d
2. 溫度須要經過屢次採樣減小偏差;調試
3. 獲得溫度後經過串口發送給上位機;code
4. 有看門狗復位的功能;blog
5. 採集溫度和發送數據時都有指示燈。
主要分 3 個文件:includes.h、init.h 和 main.c
[ includes.h ]
1 /* includes.h */ 2 /* 3 * 1.ioCC2530.h的包含 4 * 2.全局變量的定義 5 * 3.全部函數的聲明 6 * 7 */ 8 9 #ifndef INCLUDES_H 10 #define INCLUDES_H 11 12 13 #include <ioCC2530.h> 14 15 16 #define YLED P1_0 17 #define BLED P1_1 18 19 #define LEDON 1 20 #define LEDOFF 0 21 22 23 unsigned char output[6] = {0}; // 溫度格式:"12.34\0" 24 unsigned char receive_char; // 25 26 27 void xtal_init(void); 28 29 void io_init(void); 30 31 void timer1_init(void); 32 33 void WDT_init(void); 34 35 void FeetDog(void); 36 37 void uart0_init(void); 38 39 void setTempSensor(void); 40 41 float adc_start(void); 42 43 void get_temperature(unsigned char *output); 44 45 void Uart_Send_String(unsigned char *Data); 46 47 void Delay(unsigned int n); 48 49 #endif
[ init.h ]
1 /* init.h */ 2 /* 3 * 硬件的初始化和函數定義 4 * 5 */ 6 7 #ifndef INIT_H 8 #define INIT_H 9 10 11 #include "includes.h" 12 13 extern unsigned char output[6]; 14 extern unsigned char receive_char; 15 16 17 // 系統時鐘初始化 18 void xtal_init(void) 19 { 20 CLKCONCMD &= ~0x40; // 選擇系統時鐘源爲 32MHz 晶振 21 while(CLKCONSTA & 0x40); // 等待晶振穩定 22 CLKCONCMD &= ~0x47; // 設置系統主頻爲 32MHz 23 } 24 25 // 設置電源模式,這個函數沒有用到 26 // mode = 0, 1, 2, 3 27 void setPowerMode(unsigned char mode) 28 { 29 if(mode < 4) 30 { 31 CLKCONCMD &= 0xfc; // CLKCONCMD.mode = 0 32 CLKCONCMD |= mode; // 設置電源模式 33 PCON |= 0x01; // 啓動設置的PM 34 } 35 } 36 37 // P0口初始化 38 void io_init(void) 39 { 40 P1SEL = 0x00; // 通用數字IO 41 P1DIR |= 0x03; // P1_0和P1_1爲輸出 42 YLED = LEDOFF; // 滅燈 43 BLED = LEDOFF; 44 } 45 46 // 串口0初始化 47 // 這些函數不通用,並且比宏定義耗資源 48 void uart0_init() 49 { 50 PERCFG = 0x00; // 位置1 P0口 51 P0SEL = 0x3c; // P0 用做串口 52 P2DIR &= ~0xc0; // P0 優先做爲 UART0 53 54 U0CSR |= 0x80; // uart mode 55 U0GCR = 11; 56 U0BAUD = 216; // 115200 57 58 UTX0IF = 1; // UART0 TX 中斷標誌置位 59 U0CSR |= 0X40; // 容許接收 60 IEN0 |= 0x84; // IEN0.URX0IE = 1 61 } 62 63 // 串口接收中斷 64 // 這裏尚未實現控制 2530 的功能 65 #pragma vector = URX0_VECTOR 66 __interrupt void UART0_ISP(void) 67 { 68 EA = 0; 69 URX0IF = 0; 70 receive_char = U0DBUF; // y:start n:stop 71 Uart_Send_String(&receive_char); 72 Uart_Send_String("\r\n"); 73 EA = 1; 74 } 75 76 // 鏈接 ADC 和溫感器 77 void setTempSensor(void) 78 { 79 TR0 = 0x01; // 鏈接起來 80 ATEST = 0x01; // 啓動溫感 81 } 82 83 // 啓動 AD 轉換 84 float adc_start() 85 { 86 unsigned int value; 87 ADCCON3 = 0x3e; // 選擇 1.25V 爲參考電壓;14 位分辨率;片內採樣 88 ADCCON1 |= 0x30; // 選擇 ADC 的啓動模式爲手動 89 ADCCON1 |= 0x40; // 啓動 AD 轉換 90 while(!(ADCCON1 & 0x80)); // 等待轉換結束 91 92 value = ADCL >> 2; // 低 2 位數字無效 93 value |= (((unsigned int)ADCH) << 6); 94 95 return ((value>>4) - 315); 96 } 97 98 void get_temperature(unsigned char *output) 99 { 100 unsigned int i; 101 float AvgTemp = 0; 102 for(i = 0 ; i < 64 ; i++) // 屢次採樣 103 { 104 AvgTemp += adc_start(); 105 AvgTemp = AvgTemp/2; //每次累加後除 2 106 } 107 AvgTemp /= 2; 108 109 output[0] = (unsigned char)(AvgTemp) / 10 + 48; //十位 110 output[1] = (unsigned char)(AvgTemp) % 10 + 48; //個位 111 output[2] = '.'; //小數點 112 output[3] = (unsigned char)(AvgTemp*10) % 10 + 48; //十分位 113 output[4] = (unsigned char)(AvgTemp*100) % 10 + 48; //百分位 114 output[5] = '\0'; //字符串結束符 115 } 116 117 // 從串口發送字符串 118 void Uart_Send_String(unsigned char *Data) 119 { 120 BLED = LEDON; // 發送時藍燈亮 121 while(*Data != '\0') 122 { 123 U0DBUF = *Data++; 124 while(UTX0IF == 0); // 等待發送結束 125 UTX0IF = 0; // 清除發送中斷標誌 126 } 127 BLED = LEDOFF; // 發送結束了 128 } 129 130 // 定時器1初始化 131 /* 組合模式: 132 * 2s 62500 0xf424 133 * 1s 31250 0x7a12 134 * 0.5s 15625 0x3d09 135 */ 136 void timer1_init() 137 { 138 setTempSensor(); // 隨帶配置溫感 139 EA = 1; // 開啓系統總中斷 140 T1IE = 1; // 開啓定時器1中斷 141 TIMIF |= 0x40; // Timer 1 overflow interrupt mask 142 143 CLKCONCMD &= (~0x38); 144 CLKCONCMD |= 0x18; // 設置定時器1的頻率爲 4MHz 145 146 T1CCTL0 |= 0x44; // 通道0 比較模式 147 148 149 T1CTL = 0x0e; // 128分頻,模模式 150 T1STAT |=0x021; // 通道0,中斷有效 151 152 T1CC0L = 0x2a; 153 T1CC0H = 0xf4; // 計數 2s 154 155 T1IF = 0; // 清除定時器1的中斷標誌 156 T1STAT = 0x00; // 清除通道0的中斷標誌 157 } 158 159 // 定時器1溢出中斷 160 // 採集溫度並經過串口發送到上位機 161 #pragma vector = T1_VECTOR 162 __interrupt void T1_ISR(void) 163 { 164 YLED = LEDON; // 採集溫度時黃燈亮 165 EA = 0; 166 T1IF = 0; // 清除 T1 中斷標誌 167 168 get_temperature(output); // 獲取溫度,存在全局變量 output 中 169 Uart_Send_String(output); // 串口送出 170 Uart_Send_String("℃\r\n"); // 輸出符號和換行 171 172 YLED = LEDOFF; // 黃燈熄滅 173 EA = 1; 174 } 175 176 // 看門狗模式,1s復位 177 void WDT_init(void) 178 { 179 WDCTL = 0x00; // INT(10) = 00 1s 180 // mode(2) = 0 WDT mode 181 WDCTL |= 0x08; // EN(3) = 1 182 } 183 184 // 喂狗 185 void FeetDog(void) 186 { 187 WDCTL = 0xa0; 188 WDCTL = 0x50; 189 } 190 191 // 這個函數沒有使用 192 void Delay(unsigned int n) 193 { 194 unsigned int i; 195 for(i=0;i<n;i++); 196 for(i=0;i<n;i++); 197 for(i=0;i<n;i++); 198 for(i=0;i<n;i++); 199 for(i=0;i<n;i++); 200 } 201 202 #endif
[ main.c ]
1 #include <ioCC2530.h> 2 #include "includes.h" 3 #include "init.h" 4 5 6 int main( void ) 7 { 8 // 片上資源初始化 9 xtal_init(); 10 io_init(); 11 uart0_init(); 12 timer1_init(); 13 WDT_init(); 14 15 // 定時器溢出 -> 採集溫度數據 -> 串口輸出 16 // 串口收到字符進入中斷... 17 18 EA = 1; 19 20 while(1){ 21 FeetDog(); 22 } 23 return 0; 24 }
系統默認主頻是 16MHz,若是使用 128 分頻和自由運行模式,計算下來溢出時間是:
128/16000000*65536 = 0.524288,這個值 ≈ 0.5s,可是不夠精確。
因此我採用了模模式,系統主頻 32MHz,定時器 4MHz,128 分頻,計數值從 0x0000 到 0xf424
定時器1 採用模模式,使用方法有些許異於自由運行模式,看下面這段我從網上摘來的話:
模模式須要開啓通道0的輸出比較模式,不然計數器只有到了0XFF時纔會產生溢出中斷(相應的產生溢出標誌),
也就是若是沒有設置通道0的輸出比較模式,計數器的值到達T1CC0後,不會產生溢出中斷(相應的溢出標誌不會置1),這點須要特別注意。
難怪我一直不能溢出啊,因而我在 timer_init(void) 定時器1初始化函數中添加了下面的兩句:
T1CCTL0 |= 0x40; // 通道0 比較模式 T1STAT |=0x021; // 通道0,中斷有效
爲何仍是不能溢出呢,我明明「寫對」了啊!?後來通過屢次嘗試和閱讀別人的代碼,我回去看了手冊:
我把 T1CCTL0.IM 置了位,也就是通道0 的中斷屏蔽位,可是卻粗心地把 mode 給遺忘了,我對不起你啊mode:
在看TI官方的例程的時候,發現人家關於硬件初始化的代碼中,使用到了不少的宏,並且能夠通用,不像個人代碼,淚流滿面,慘不忍睹。應該珍惜有限的資源。
多去閱讀手冊和實踐,這是多麼痛的領悟啊。