你的位置:首页 > 操作系统

[操作系统]基于 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_H10 #define INCLUDES_H11 12 13 #include <ioCC2530.h>14 15 16 #define YLED P1_017 #define BLED P1_118 19 #define LEDON 120 #define LEDOFF 021 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;  //每次累加后除 2106   }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 0xf424133  *  1s 31250 0x7a12134  * 0.5s 15625 0x3d09135 */136 void timer1_init()137 {138   setTempSensor();      // 随带配置温感139   EA = 1;           // 开启系统总中断140   T1IE = 1;          // 开启定时器1中断141   TIMIF |= 0x40;       // Timer 1 overflow interrupt mask142 143   CLKCONCMD &= (~0x38);144   CLKCONCMD |= 0x18;     // 设置定时器1的频率为 4MHz145 146   T1CCTL0 |= 0x44;      // 通道0 比较模式147 148 149   T1CTL = 0x0e;        // 128分频,模模式150   T1STAT |=0x021;       // 通道0,中断有效151 152   T1CC0L = 0x2a;153   T1CC0H = 0xf4;       // 计数 2s154 155   T1IF = 0;          // 清除定时器1的中断标志156   T1STAT = 0x00;       // 清除通道0的中断标志157 }158 159 // 定时器1溢出中断160 // 采集温度并通过串口发送到上位机161 #pragma vector = T1_VECTOR162 __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 1s180                 // mode(2) = 0  WDT mode181   WDCTL |= 0x08;       //  EN(3) = 1182 }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官方的例程的时候,发现人家关于硬件初始化的代码中,使用到了很多的宏,而且可以通用,不像我的代码,泪流满面,惨不忍睹。应该珍惜有限的资源。

 

最重要的体悟


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