基于 CC2530 的温度采集系统(未定稿)
2016-05-08 03:20
281 查看
前言
最近在自学 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 ]
/* includes.h */ /* * 1.ioCC2530.h的包含 * 2.全局变量的定义 * 3.所有函数的声明 * */ #ifndef INCLUDES_H #define INCLUDES_H #include <ioCC2530.h> #define YLED P1_0 #define BLED P1_1 #define LEDON 1 #define LEDOFF 0 unsigned char output[6] = {0}; // 温度格式:"12.34\0" unsigned char receive_char; // void xtal_init(void); void io_init(void); void timer1_init(void); void WDT_init(void); void FeetDog(void); void uart0_init(void); void setTempSensor(void); float adc_start(void); void get_temperature(unsigned char *output); void Uart_Send_String(unsigned char *Data); void Delay(unsigned int n); #endif
[ init.h ]
/* init.h */ /* * 硬件的初始化和函数定义 * */ #ifndef INIT_H #define INIT_H #include "includes.h" extern unsigned char output[6]; extern unsigned char receive_char; // 系统时钟初始化 void xtal_init(void) { CLKCONCMD &= ~0x40; // 选择系统时钟源为 32MHz 晶振 while(CLKCONSTA & 0x40); // 等待晶振稳定 CLKCONCMD &= ~0x47; // 设置系统主频为 32MHz } // 设置电源模式,这个函数没有用到 // mode = 0, 1, 2, 3 void setPowerMode(unsigned char mode) { if(mode < 4) { CLKCONCMD &= 0xfc; // CLKCONCMD.mode = 0 CLKCONCMD |= mode; // 设置电源模式 PCON |= 0x01; // 启动设置的PM } } // P0口初始化 void io_init(void) { P1SEL = 0x00; // 通用数字IO P1DIR |= 0x03; // P1_0和P1_1为输出 YLED = LEDOFF; // 灭灯 BLED = LEDOFF; } // 串口0初始化 // 这些函数不通用,而且比宏定义耗资源 void uart0_init() { PERCFG = 0x00; // 位置1 P0口 P0SEL = 0x3c; // P0 用作串口 P2DIR &= ~0xc0; // P0 优先作为 UART0 U0CSR |= 0x80; // uart mode U0GCR = 11; U0BAUD = 216; // 115200 UTX0IF = 1; // UART0 TX 中断标志置位 U0CSR |= 0X40; // 允许接收 IEN0 |= 0x84; // IEN0.URX0IE = 1 } // 串口接收中断 // 这里还没有实现控制 2530 的功能 #pragma vector = URX0_VECTOR __interrupt void UART0_ISP(void) { EA = 0; URX0IF = 0; receive_char = U0DBUF; // y:start n:stop Uart_Send_String(&receive_char); Uart_Send_String("\r\n"); EA = 1; } // 连接 ADC 和温感器 void setTempSensor(void) { TR0 = 0x01; // 连接起来 ATEST = 0x01; // 启动温感 } // 启动 AD 转换 float adc_start() { unsigned int value; ADCCON3 = 0x3e; // 选择 1.25V 为参考电压;14 位分辨率;片内采样 ADCCON1 |= 0x30; // 选择 ADC 的启动模式为手动 ADCCON1 |= 0x40; // 启动 AD 转换 while(!(ADCCON1 & 0x80)); // 等待转换结束 value = ADCL >> 2; // 低 2 位数字无效 value |= (((unsigned int)ADCH) << 6); return ((value>>4) - 315); } void get_temperature(unsigned char *output) { unsigned int i; float AvgTemp = 0; for(i = 0 ; i < 64 ; i++) // 多次采样 { AvgTemp += adc_start(); AvgTemp = AvgTemp/2; //每次累加后除 2 } AvgTemp /= 2; output[0] = (unsigned char)(AvgTemp) / 10 + 48; //十位 output[1] = (unsigned char)(AvgTemp) % 10 + 48; //个位 output[2] = '.'; //小数点 output[3] = (unsigned char)(AvgTemp*10) % 10 + 48; //十分位 output[4] = (unsigned char)(AvgTemp*100) % 10 + 48; //百分位 output[5] = '\0'; //字符串结束符 } // 从串口发送字符串 void Uart_Send_String(unsigned char *Data) { BLED = LEDON; // 发送时蓝灯亮 while(*Data != '\0') { U0DBUF = *Data++; while(UTX0IF == 0); // 等待发送结束 UTX0IF = 0; // 清除发送中断标志 } BLED = LEDOFF; // 发送结束了 } // 定时器1初始化 /* 组合模式: * 2s 62500 0xf424 * 1s 31250 0x7a12 * 0.5s 15625 0x3d09 */ void timer1_init() { setTempSensor(); // 随带配置温感 EA = 1; // 开启系统总中断 T1IE = 1; // 开启定时器1中断 TIMIF |= 0x40; // Timer 1 overflow interrupt mask CLKCONCMD &= (~0x38); CLKCONCMD |= 0x18; // 设置定时器1的频率为 4MHz T1CCTL0 |= 0x44; // 通道0 比较模式 T1CTL = 0x0e; // 128分频,模模式 T1STAT |=0x021; // 通道0,中断有效 T1CC0L = 0x2a; T1CC0H = 0xf4; // 计数 2s T1IF = 0; // 清除定时器1的中断标志 T1STAT = 0x00; // 清除通道0的中断标志 } // 定时器1溢出中断 // 采集温度并通过串口发送到上位机 #pragma vector = T1_VECTOR __interrupt void T1_ISR(void) { YLED = LEDON; // 采集温度时黄灯亮 EA = 0; T1IF = 0; // 清除 T1 中断标志 get_temperature(output); // 获取温度,存在全局变量 output 中 Uart_Send_String(output); // 串口送出 Uart_Send_String("℃\r\n"); // 输出符号和换行 YLED = LEDOFF; // 黄灯熄灭 EA = 1; } // 看门狗模式,1s复位 void WDT_init(void) { WDCTL = 0x00; // INT(10) = 00 1s // mode(2) = 0 WDT mode WDCTL |= 0x08; // EN(3) = 1 } // 喂狗 void FeetDog(void) { WDCTL = 0xa0; WDCTL = 0x50; } // 这个函数没有使用 void Delay(unsigned int n) { unsigned int i; for(i=0;i<n;i++); for(i=0;i<n;i++); for(i=0;i<n;i++); for(i=0;i<n;i++); for(i=0;i<n;i++); } #endif
[ main.c ]
#include <ioCC2530.h> #include "includes.h" #include "init.h" int main( void ) { // 片上资源初始化 xtal_init(); io_init(); uart0_init(); timer1_init(); WDT_init(); // 定时器溢出 -> 采集温度数据 -> 串口输出 // 串口收到字符进入中断... EA = 1; while(1){ FeetDog(); } return 0; }
实验结果
实验中遇到的主要问题
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,中断有效
为什么还是不能溢出呢,我明明“写对”了啊!?后来经过多次尝试和阅读别人的代码,我回去看了手册:
我把 T1CCTL0.IM 置了位,也就是通道0 的中断屏蔽位,但是却粗心地把 mode 给遗忘了,我对不起你啊mode:
T1CCTL0 |= 0x44; // 通道0 比较模式
3)温度的确定,见代码
应该改进的地方
在看TI官方的例程的时候,发现人家关于硬件初始化的代码中,使用到了很多的宏,而且可以通用,不像我的代码,泪流满面,惨不忍睹。应该珍惜有限的资源。最重要的体悟
多去阅读手册和实践,这是多么痛的领悟啊。相关文章推荐
- 扣丁音乐(四)——本地音乐加载
- JAVA学习总结六
- 路径中 斜杠/和反斜杠\ 的区别
- 底部菜单栏之Fragment+PopupWindow实现
- 地图与定位
- 路径中 斜杠/和反斜杠\ 的区别
- Codeforces 674C Levels and Regions
- Codeforces Round #328 (Div. 2) B
- 底部菜单栏之Fragment+FragmentHost实现
- objective c:循环引用
- CC2538内存分配问题
- HDU1262 寻找素数对
- OBJ-C NSString + NSRange + NSMutableString 学习笔记
- OBJ-C block + protocol 学习笔记
- 魔兽世界私服Trinity,从源码开始
- OBJ-C ARC概念及原理+分类 学习笔记
- 扣丁音乐(三)——UI框架的实现
- 关于ubuntu16无线网卡RTL8723BE频繁掉线及信号不足的解决办法
- 结构体和typedef
- java web 100个知识点