• 基于 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"
    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("
    ");
     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] = '';                                       //字符串结束符
    115 }
    116 
    117 // 从串口发送字符串
    118 void Uart_Send_String(unsigned char *Data)
    119 {
    120     BLED = LEDON;               // 发送时蓝灯亮
    121     while(*Data != '')
    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(""); // 输出符号和换行
    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官方的例程的时候,发现人家关于硬件初始化的代码中,使用到了很多的宏,而且可以通用,不像我的代码,泪流满面,惨不忍睹。应该珍惜有限的资源。

    最重要的体悟


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

  • 相关阅读:
    JAVA——俄罗斯方块
    JAVA——简单科学计算器设计
    标准9*9数独破解器
    k短路算法(A*)
    洛谷2939 分层图模板
    PCA算法
    coursera-斯坦福-机器学习-吴恩达-笔记week4
    coursera-斯坦福-机器学习-吴恩达-笔记week3
    coursera-斯坦福-机器学习-吴恩达-笔记week2
    coursera-斯坦福-机器学习-吴恩达-笔记week1
  • 原文地址:https://www.cnblogs.com/luoxu34/p/5469673.html
Copyright © 2020-2023  润新知