您的位置:首页 > 其它

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

最重要的体悟

多去阅读手册和实践,这是多么痛的领悟啊。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: