基于 CC2530 的温度采集系统(未定稿)

前言


最近在自学 Zigbee,每天的主要是任务是:看博客,看 CC2530 的 datasheet 和实践,熟悉片上的 SFR 以及控制板子。

学和做内容包括:IO、外部中断、Timer1/3/4、串口实验、ADC温度的转换、看门狗、Sleep Timer 和 DMA。

之后做了一个综合的小实验,基于 CC2530 的温度监测系统,关于协议栈的部分还在学习,所以这个实验没有使用到协议栈。

 

实验目的


检验学习成果,熟悉 sfr 的配置和片上资源的使用。

 

实验工具


硬件;CC2530、CCDebug、串口线

软件:IAR Embedded Workbench、串口调试助手

 

要实现的功能


1. 系统每 2s 统计一次温度,由定时器1 来精确定时;

2. 温度需要通过多次采样减少误差;

3. 得到温度后通过串口发送给上位机;

4. 有看门狗复位的功能;

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官方的例程的时候,发现人家关于硬件初始化的代码中,使用到了很多的宏,而且可以通用,不像我的代码,泪流满面,惨不忍睹。应该珍惜有限的资源。

 

最重要的体悟


多去阅读手册和实践,这是多么痛的领悟啊。

 

posted @ 2016-05-08 03:20  luoxu34  阅读(3236)  评论(0编辑  收藏  举报