基於 CC2530 的溫度採集系統(未定稿)

前言


最近在自學 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 }

 

實驗結果


20160508

 

實驗中遇到的主要問題


1)定時器T1 的準肯定時

系統默認主頻是 16MHz,若是使用 128 分頻和自由運行模式,計算下來溢出時間是:

128/16000000*65536 = 0.524288,這個值 ≈ 0.5s,可是不夠精確。

 

因此我採用了模模式,系統主頻 32MHz,定時器 4MHz,128 分頻,計數值從 0x0000 到 0xf424

 

2)本身的粗心

定時器1 採用模模式,使用方法有些許異於自由運行模式,看下面這段我從網上摘來的話:

模模式須要開啓通道0的輸出比較模式,不然計數器只有到了0XFF時纔會產生溢出中斷(相應的產生溢出標誌),

也就是若是沒有設置通道0的輸出比較模式,計數器的值到達T1CC0後,不會產生溢出中斷(相應的溢出標誌不會置1),這點須要特別注意。

難怪我一直不能溢出啊,因而我在 timer_init(void) 定時器1初始化函數中添加了下面的兩句:

T1CCTL0 |= 0x40; // 通道0 比較模式


T1STAT  |=0x021; // 通道0,中斷有效

 

爲何仍是不能溢出呢,我明明「寫對」了啊!?後來通過屢次嘗試和閱讀別人的代碼,我回去看了手冊:

image

我把 T1CCTL0.IM 置了位,也就是通道0 的中斷屏蔽位,可是卻粗心地把 mode 給遺忘了,我對不起你啊mode:

T1CCTL0 |= 0x44; // 通道0 比較模式

 

3)溫度的肯定,見代碼

 

應該改進的地方


在看TI官方的例程的時候,發現人家關於硬件初始化的代碼中,使用到了不少的宏,並且能夠通用,不像個人代碼,淚流滿面,慘不忍睹。應該珍惜有限的資源。

 

最重要的體悟


多去閱讀手冊和實踐,這是多麼痛的領悟啊。

相關文章
相關標籤/搜索