Zigbee之旅(十):综合小实验——基于CC2430的温度监测系统

 在3月3日写完“zigbee之旅(九)”后,笔者本打算立即着手“温度监测系统”小实验的编写,以作为对之前一系列零散知识点的总结。然而我又意识到,前面的几个小实验虽然每一篇都讲得较为详细,但是其代码的规范性、结构性,可以说是不堪入目的。既然是小结,就应当在原来的基础上有所进步,而不是机械地把前面的小知识点拼凑起来了事。因此,我暂停了原来的计划,抽出时间去学习了一下嵌入式开发的通用技巧,写下了两篇随笔《嵌入式C51编程规范》和《嵌入式项目代码结构的分层》。本篇日志,既是Zigbee首次旅行的一个阶段性小结,也融入了笔者近几天的学习心得,希望能对Zigbee初学者有所帮助。

  全文按软件开发的基本流程来组织:需求分析、概要设计、详细设计、编码实现、测试。

一、需求分析

  经“客户”与“开发者”共同商讨,确定了如下的系统功能描述:

  ☻ 使用基于CC2430的节点采集当前室温,并可通过PC监测其温度数值

  ☻ CC2430节点本身需具备一定的稳定性,可自动恢复正常状态

  ☻ 可通过PC来控制节点的采样间隔与电源管理

二、概要设计

  根据上述需求分析,我们可以把系统分为两大模块:CC2430节点 与 PC机

Zigbee之旅(十):综合小实验——基于CC2430的温度监测系统_第1张图片

  [CC2430节点]  

   ☻ 可定时采集外部参数,并发送至PC端

   ☻ 停机时自动复位

   ☻ 可接收来自PC机的指令,并作出相应处理:改变采样间隔/电源管理

  [PC机]  

   ☻ PC机通过串口工具接收数据并显示

   ☻ 可通过串口工具向单片机发送指令,控制其采样速度,电源管理

三、详细设计

(1)代码结构

  本系统代码结构的分层,其实已在随笔《嵌入式项目代码结构的分层》中提到了,现copy如下:

  (1)硬件抽象层

      [ioCC2430.h](系统自带)定义了CC2430的所有SFR 、中断向量    

      [hal.h] 包括常用类型定义、常用赋值宏、以及CC2430片上资源的通用配置(I/O、串口通讯、ADC、定时器、电源管理等)

  (2)功能模块层

      [module.h] 定义了片内资源(定时器、I/O)片外扩展模块(LED),以及相关函数的声明

      [module.c] 实现各模块(LED)的初始化

  (3)应用程序层

      [main.c] 引用 hal.h、ioCC2430.h 与 module.h,实现温度采集、与PC互通信、停机复位等具体的应用需求

(2)各模块实现方法

  根据概要设计中所划分的模块包括,本性系统可分为两大模块:CC2430节点 和 PC机

  由于PC机上已有串口通信工具,其功能已能满足要求,所以PC这一部分我们不需要做,没必要对其分析。下面谈一下CC2430节
点的各子功能的实现方法:

  ☻ 利用定时器的计数溢出中断,来触发定时采样

  ☻ 使用串口的 UART0 模式将温度数据传送至PC

  ☻ 利用CC2430自带的看门狗电路,实现系统的停机自动复位功能

  ☻ 利用串口接收中断,来实现对来自PC端的控制指令的捕获与响应

    1) 若接收到 @ 字符,则为采样间隔控制命令,后面紧跟的一个数字表示采样间隔:0——0.5s、1——1s、2——2s
     如:@0,表示每隔0.5秒采样一次。
    2) 若接收到 $  字符,则为睡眠控制命令,后面紧跟的一个数字表示电源模式
     如:$3,表示使系统进入电源模式3。

(3)程序流程图

四、编码实现

(1)硬件抽象层

  硬件抽象层包括 ioCC2430.h 和 hal.h。由于前者系统自带,就不列出来了。

  下面把 hal.h 的全部内容列出来(由于这个文件太长,看起来不方便,我就分模块展示):

/* **********************************************************
*文件名称: hal.h
*作 者: hustlzp
*日 期: 2011/3/8
*版 本: 1.1
*功能说明: 硬件抽象层
*修改记录:
**********************************************************
*/
 

#ifndef HAL_H
#define HAL_H
 

#include
< ioCC2430.h >
 

/* **********************************************************
常用类型定义
**********************************************************
*/
typedef unsigned
char BYTE;
typedef unsigned
int WORD;
typedef unsigned
long DWORD;


/* **********************************************************
常用宏定义
**********************************************************
*/

// 高8位
#define HIGH_BYTE(a) ((BYTE) (((WORD)(a)) >> 8))
 

// 低8位
#define LOW_BYTE(a) ((BYTE) ((WORD)(a)))
 

// 赋值
#define SET_WORD(regH,regL,word) \
do { \
(regH)
= HIGH_BYTE(word); \
(regL)
= LOW_BYTE(word); \
}
while ( 0 )
/* **********************************************************
I/O口
**********************************************************
*/
/* 配置I/O口方向
-----------------------------------------
*/
#define IO_DIR_PORT_PIN(port, pin, dir) \
do { \
if (dir == IO_OUT) \
P##port##DIR
|= ( 0x01 << (pin)); \
else \
P##port##DIR
&= ~ ( 0x01 << (pin)); \
}
while ( 0 )


// 其中参数 dir 的取值为:
#define IO_IN 0
#define IO_OUT 1
 

/* 配置I/O口的输入模式
-----------------------------------------
*/
#define IO_IMODE_PORT_PIN(port, pin, imode) \
do { \
if (imode == IO_IMODE_TRI) \
P##port##INP
|= ( 0x01 << (pin)); \
else \
P##port##INP
&= ~ ( 0x01 << (pin)); \
}
while ( 0 )


 

#define IO_PUD_PORT(port, pud) \
do { \
if (pud == IO_PULLDOWN) \
P2INP
|= ( 0x01 << (port + 5 )); \
else \
P2INP
&= ~ ( 0x01 << (port + 5 ));\
}
while ( 0 )


// 其中参数 pud 的取值为:
#define IO_PULLUP 0 // 上拉
#define IO_PULLDOWN 1 // 下拉
 

/* 配置I/O口的功能
-----------------------------------------
*/

#define IO_FUNC_PORT_PIN(port, pin, func) \
do { \
if ((port == 2 ) && (pin == 3 )){ \
if (func) { \
P2SEL
|= 0x02 ; \
}
else { \
P2SEL
&= ~ 0x02 ; \
} \
} \
else if ((port == 2 ) && (pin == 4 )){ \
if (func) { \
P2SEL
|= 0x04 ; \
}
else { \
P2SEL
&= ~ 0x04 ; \
} \
} \
else { \
if (func) { \
P##port##SEL
|= ( 0x01 << (pin)); \
}
else { \
P##port##SEL
&= ~ ( 0x01 << (pin));\
} \
} \
}
while ( 0 )


// 其中参数 func 的取值为:
#define IO_FUNC_GIO 0 // 通用I/O
#define IO_FUNC_PERIPH 1 // 外设I/O
 

// 配置外设I/O的位置
#define IO_PER_LOC_TIMER1_AT_PORT0_PIN234() do { PERCFG = (PERCFG&~0x40)|0x00; } while (0)
#define IO_PER_LOC_TIMER1_AT_PORT1_PIN012() do { PERCFG = (PERCFG&~0x40)|0x40; } while (0)

#define IO_PER_LOC_TIMER3_AT_PORT1_PIN34() do { PERCFG = (PERCFG&~0x20)|0x00; } while (0)
#define IO_PER_LOC_TIMER3_AT_PORT1_PIN67() do { PERCFG = (PERCFG&~0x20)|0x20; } while (0)

#define IO_PER_LOC_TIMER4_AT_PORT1_PIN01() do { PERCFG = (PERCFG&~0x10)|0x00; } while (0)
#define IO_PER_LOC_TIMER4_AT_PORT2_PIN03() do { PERCFG = (PERCFG&~0x10)|0x10; } while (0)

#define IO_PER_LOC_SPI1_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x08)|0x00; } while (0)
#define IO_PER_LOC_SPI1_AT_PORT1_PIN4567() do { PERCFG = (PERCFG&~0x08)|0x08; } while (0)

#define IO_PER_LOC_SPI0_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x04)|0x00; } while (0)
#define IO_PER_LOC_SPI0_AT_PORT1_PIN2345() do { PERCFG = (PERCFG&~0x04)|0x04; } while (0)

#define IO_PER_LOC_UART1_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x02)|0x00; } while (0)
#define IO_PER_LOC_UART1_AT_PORT1_PIN4567() do { PERCFG = (PERCFG&~0x02)|0x02; } while (0)

#define IO_PER_LOC_UART0_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x01)|0x00; } while (0)
#define IO_PER_LOC_UART0_AT_PORT1_PIN2345() do { PERCFG = (PERCFG&~0x01)|0x01; } while (0)
 
//其中参数 imode 的取值为:
#define IO_IMODE_PUD 0 // 上拉/下拉
#define IO_IMODE_TRI 1 // 三态
/* **********************************************************
中断
**********************************************************
*/
// 用于开/关中断
#define INT_ON 1
#define INT_OFF 0
 

// 用于置位/清除中断标志
#define INT_SET 1
#define INT_CLR 0
 

// 全局中断设置
#define INT_GLOBAL_ENABLE(on) EA=(!!on)
 

// 定义中断
#define INUM_RFERR 0
#define INUM_ADC 1
#define INUM_URX0 2
#define INUM_URX1 3
#define INUM_ENC 4
#define INUM_ST 5
#define INUM_P2INT 6
#define INUM_UTX0 7
#define INUM_DMA 8
#define INUM_T1 9
#define INUM_T2 10
#define INUM_T3 11
#define INUM_T4 12
#define INUM_P0INT 13
#define INUM_UTX1 14
#define INUM_P1INT 15
#define INUM_RF 16
#define INUM_WDT 17
 

/* 中断允许
-----------------------------------------
*/
#define INT_ENABLE(inum, on) \
do { \
if (inum == INUM_RFERR) { RFERRIE = on; } \
else if (inum == INUM_ADC) { ADCIE = on; } \
else if (inum == INUM_URX0) { URX0IE = on; } \
else if (inum == INUM_URX1) { URX1IE = on; } \
else if (inum == INUM_ENC) { ENCIE = on; } \
else if (inum == INUM_ST) { STIE = on; } \
else if (inum == INUM_P2INT) { (on) ? (IEN2 |= 0x02 ) : (IEN2 &= ~ 0x02 ); } \
else if (inum == INUM_UTX0) { (on) ? (IEN2 |= 0x04 ) : (IEN2 &= ~ 0x04 ); } \
else if (inum == INUM_DMA) { DMAIE = on; } \
else if (inum == INUM_T1) { T1IE = on; } \
else if (inum == INUM_T2) { T2IE = on; } \
else if (inum == INUM_T3) { T3IE = on; } \
else if (inum == INUM_T4) { T4IE = on; } \
else if (inum == INUM_P0INT) { P0IE = on; } \
else if (inum == INUM_UTX1) { (on) ? (IEN2 |= 0x08 ) : (IEN2 &= ~ 0x08 ); } \
else if (inum == INUM_P1INT) { (on) ? (IEN2 |= 0x10 ) : (IEN2 &= ~ 0x10 ); } \
else if (inum == INUM_RF) { (on) ? (IEN2 |= 0x01 ) : (IEN2 &= ~ 0x01 ); } \
else if (inum == INUM_WDT) { (on) ? (IEN2 |= 0x20 ) : (IEN2 &= ~ 0x20 ); } \
}
while ( 0 )


/* 设置中断优先级
-----------------------------------------
*/
#define INT_PRIORITY(group, pri) \
do { \
if (pri == 0 ) { IP0 &= ~ group; IP1 &= ~ group; } \
if (pri == 1 ) { IP0 |= group; IP1 &= ~ group; } \
if (pri == 2 ) { IP0 &= ~ group; IP1 |= group; } \
if (pri == 3 ) { IP0 |= group; IP1 |= group; } \
}
while ( 0 )

// 其中参数 pri 的取值为:0/1/2/3(最高优先级)


// 其中参数 group 的取值为:
#define RFERR_RF_DMA 0x01 // Group IP0
#define ADC_P2INT_T1 0x02 // Group IP1
#define URX0_UTX0_T2 0x04 // Group IP2
#define URX1_UTX1_T3 0x08 // Group IP3
#define ENC_P1INT_T4 0x10 // Group IP4
#define ST_WDT_P0INT 0x20 // Group IP5
 

/* 获取中断标志
-----------------------------------------
*/
#define INT_GETFLAG(inum) ( \
(inum
== INUM_RFERR) ? RFERRIF : \
(inum
== INUM_ADC) ? ADCIF : \
(inum
== INUM_URX0) ? URX0IF : \
(inum
== INUM_URX1) ? URX1IF : \
(inum
== INUM_ENC) ? ENCIF_0 : \
(inum
== INUM_ST) ? STIF : \
(inum
== INUM_P2INT) ? P2IF : \
(inum
== INUM_UTX0) ? UTX0IF : \
(inum
== INUM_DMA) ? DMAIF : \
(inum
== INUM_T1) ? T1IF : \
(inum
== INUM_T2) ? T2IF : \
(inum
== INUM_T3) ? T3IF : \
(inum
== INUM_T4) ? T4IF : \
(inum
== INUM_P0INT) ? P0IF : \
(inum
== INUM_UTX1) ? UTX1IF : \
(inum
== INUM_P1INT) ? P1IF : \
(inum
== INUM_RF) ? S1CON &= ~ 0x03 : \
(inum
== INUM_WDT) ? WDTIF : \
0 \
)


/* 设置中断标志
-----------------------------------------
*/
#define INT_SETFLAG(inum, f) \
do { \
if (inum == INUM_RFERR) { RFERRIF = f; } \
else if (inum == INUM_ADC) { ADCIF = f; } \
else if (inum == INUM_URX0) { URX0IF = f; } \
else if (inum == INUM_URX1) { URX1IF = f; } \
else if (inum == INUM_ENC) { ENCIF_1 = ENCIF_0 = f; } \
else if (inum == INUM_ST) { STIF = f; } \
else if (inum == INUM_P2INT) { P2IF = f; } \
else if (inum == INUM_UTX0) { UTX0IF = f; } \
else if (inum == INUM_DMA) { DMAIF = f; } \
else if (inum == INUM_T1) { T1IF = f; } \
else if (inum == INUM_T2) { T2IF = f; } \
else if (inum == INUM_T3) { T3IF = f; } \
else if (inum == INUM_T4) { T4IF = f; } \
else if (inum == INUM_P0INT) { P0IF = f; } \
else if (inum == INUM_UTX1) { UTX1IF = f; } \
else if (inum == INUM_P1INT) { P1IF = f; } \
else if (inum == INUM_RF) { (f) ? (S1CON |= 0x03 ) : (S1CON &= ~ 0x03 ); } \
else if (inum == INUM_WDT) { WDTIF = f; } \
}
while ( 0 )
/* **********************************************************
串口
**********************************************************
*/
// 不同波特率对应的BAUD_E的值
#define BAUD_E(baud, clkDivPow) ( \
(baud
== 2400 ) ? 6 + clkDivPow : \
(baud
== 4800 ) ? 7 + clkDivPow : \
(baud
== 9600 ) ? 8 + clkDivPow : \
(baud
== 14400 ) ? 8 + clkDivPow : \
(baud
== 19200 ) ? 9 + clkDivPow : \
(baud
== 28800 ) ? 9 + clkDivPow : \
(baud
== 38400 ) ? 10 + clkDivPow : \
(baud
== 57600 ) ? 10 + clkDivPow : \
(baud
== 76800 ) ? 11 + clkDivPow : \
(baud
== 115200 ) ? 11 + clkDivPow : \
(baud
== 153600 ) ? 12 + clkDivPow : \
(baud
== 230400 ) ? 12 + clkDivPow : \
(baud
== 307200 ) ? 13 + clkDivPow : \
0 )


// 不同波特率对应的BAUD_M的值
#define BAUD_M(baud) ( \
(baud
== 2400 ) ? 59 : \
(baud
== 4800 ) ? 59 : \
(baud
== 9600 ) ? 59 : \
(baud
== 14400 ) ? 216 : \
(baud
== 19200 ) ? 59 : \
(baud
== 28800 ) ? 216 : \
(baud
== 38400 ) ? 59 : \
(baud
== 57600 ) ? 216 : \
(baud
== 76800 ) ? 59 : \
(baud
== 115200 ) ? 216 : \
(baud
== 153600 ) ? 59 : \
(baud
== 230400 ) ? 216 : \
(baud
== 307200 ) ? 59 : \
0 )


/* UART模式下的串口配置
-----------------------------------------
*/
#define UART_SETUP(uart, receiveEnable, baudRate, options) \
do { \
if ((uart) == 0 ){ \
if (PERCFG & 0x01 ){ \
P1SEL
|= 0x30 ; \
}
else { \
P0SEL
|= 0x0C ; \
} \
} \
else { \
if (PERCFG & 0x02 ){ \
P1SEL
|= 0xC0 ; \
}
else { \
P0SEL
|= 0x30 ; \
} \
} \
\
U##uart##GCR
= BAUD_E((baudRate),CLKSPD); \
U##uart##BAUD
= BAUD_M(baudRate); \
\
U##uart##CSR
|= 0x80 ; \
\
U##uart##CSR
|= receiveEnable; \
\
U##uart##UCR
|= ((options) | 0x80 ); \
}
while ( 0 )

// 其中参数 receiveEnable 的取值:
#define UART_RECEIVE_ENABLE 0x40 // 接收允许
#define UART_RECEIVE_DISABLE 0x00

// 其中参数 options 的取值:
#define FLOW_CONTROL_ENABLE 0x40 // 流控制
#define FLOW_CONTROL_DISABLE 0x00
 

#define EVEN_PARITY 0x20 // 偶校验
#define ODD_PARITY 0x00 // 奇校验
 

#define NINE_BIT_TRANSFER 0x10 // 9字节传输
#define EIGHT_BIT_TRANSFER 0x00 // 8字节传输
 

#define PARITY_ENABLE 0x08 // 奇偶校验使能
#define PARITY_DISABLE 0x00

#define TWO_STOP_BITS 0x04 // 2位停止位
#define ONE_STOP_BITS 0x00 // 1位停止位
 

#define HIGH_STOP 0x02 // 停止位高电平
#define LOW_STOP 0x00 // 停止位低电平

#define HIGH_START 0x01 // 起始位电平高
#define LOW_START 0x00 // 起始位电平低
 

// 串口发送字符
#define UART_SEND(uart,data) \
do { \
while (U##uart##CSR & 0x01 ); \
U##uart##DBUF
= data; \
}
while ( 0 )
#define UART0_SEND(data) UART_SEND(0,data)
#define UART1_SEND(data) UART_SEND(1,data)
 

// 串口接收字符
#define UART_RECEIVE(uart,data) \
do { \
while ( ! (U##uart##CSR & 0x04 )); \
data
= U##uart##DBUF; \
}
while ( 0 )
#define UART0_RECEIVE(data) UART_RECEIVE(0,data)
#define UART1_RECEIVE(data) UART_RECEIVE(1,data)
/* **********************************************************
电源及时钟管理
**********************************************************
*/
// 获取时钟分频
#define CLKSPD (CLKCON & 0x07)
 

// 设置电源模式
#define SET_POWER_MODE(mode) \
do { \
if (mode == 0 ) { SLEEP &= ~ 0x03 ; } \
else if (mode == 3 ) { SLEEP |= 0x03 ; } \
else { SLEEP &= ~ 0x03 ; SLEEP |= mode; } \
PCON
|= 0x01 ; \
asm(
" NOP " ); \
}
while ( 0 )


// 参数 mode 的取值为:
#define POWER_MODE_0 0x00
#define POWER_MODE_1 0x01
#define POWER_MODE_2 0x02
#define POWER_MODE_3 0x03
 

// 用于检测高频RC振荡器的稳定状况
#define HIGH_FREQUENCY_RC_OSC_STABLE (SLEEP & 0x20)
 

// 用于检测晶体振荡器的稳定状况
#define XOSC_STABLE (SLEEP & 0x40)
 

// 获取定时器的tick频率值
#define TICKSPD ((CLKCON & 0x38) >> 3)
 

// 设置主时钟频率
#define SET_MAIN_CLOCK_SOURCE(source) \
do { \
if (source) { \
CLKCON
|= 0x40 ; \
while ( ! HIGH_FREQUENCY_RC_OSC_STABLE); \
if (TICKSPD == 0 ){ \
CLKCON
|= 0x08 ; \
} \
SLEEP
|= 0x04 ; \
} \
else { \
SLEEP
&= ~ 0x04 ; \
while ( ! XOSC_STABLE); \
asm(
" NOP " ); \
CLKCON
&= ~ 0x47 ; \
SLEEP
|= 0x04 ; \
} \
}
while ( 0 )


// 其中参数 source 的取值为:
#define CRYSTAL 0x00 // 晶体振荡器
#define RC 0x01 // RC振荡器
/* **********************************************************
定时器1
**********************************************************
*/
// 定时器1允许计数溢出中断
#define TIMER1_ENABLE_OVERFLOW_INT(val) \
(TIMIF
= (val) ? TIMIF | 0x40 : TIMIF & ~ 0x40 )


// 设置定时器1的溢出中断标志
#define TIMER1_OVERFLOW_INT_SETFLAG(f) (T1CTL= ((T1CTL & (~0x10)) | f))
 

// 定时器1启动
#define TIMER1_RUN(value) (T1CTL = (value) ? T1CTL|0x02 : T1CTL&~0x03)
 

// 设置定时器的时钟分频
#define SET_TIMER_TICK(value) do{ CLKCON = ((CLKCON & (~0x38)) | value);} while(0)
 

// 其中value的取值为:
#define TIMER1_TICK_32M 0x00 // 32MHz
#define TIMER1_TICK_16M 0x08 // 16MHz,系统复位默认值
#define TIMER1_TICK_8M 0x10 // 8MHz
#define TIMER1_TICK_4M 0x18 // 4MHz
#define TIMER1_TICK_2M 0x20 // 2MHz
#define TIMER1_TICK_1M 0x28 // 1MHz
#define TIMER1_TICK_500k 0x30 // 500kHz
#define TIMER1_TICK_250k 0x38 // 250kHz

// 设置定时器1的TICK分频
#define SET_TIMER1_TICKDIV(value) \
do { \
T1CTL
&= ~ 0x0c ; \
T1CTL
|= value; \
}
while ( 0 )

// 其中 value 的取值为:
#define TIMER1_TICKDIV_1 0x00 // 1分频
#define TIMER1_TICKDIV_8 0x04 // 8分频
#define TIMER1_TICKDIV_32 0x08
#define TIMER1_TICKDIV_128 0x0c
 

// 设置定时器溢出周期
#define SET_TIMER1_PERIOD(value) \
do { \
T1CC0H
= HIGH_BYTE(value); \
T1CC0L
= LOW_BYTE(value); \
}
while ( 0 )

// 设置定时器1的运行模式
#define SET_TIMER1_MODE(mode) \
do { \
T1CTL
= ((T1CTL & ( ~ 0x03 )) | mode); \
}
while ( 0 )


// 其中 mode 的取值为:
#define TIMER1_MODE_STOP 0x00
#define TIMER1_MODE_FREE 0x01
#define TIMER1_MODE_MODULE 0x02
#define TIMER1_MODE_UPDOWN 0x03
/* **********************************************************
看门狗
**********************************************************
*/
// 设置看门狗定时器的溢出周期
#define WDT_SET_TIMEOUT_PERIOD(timeout) \
do { WDCTL &= ~ 0x03 ; WDCTL |= timeout; } while ( 0 )


// 其中参数 timeout 的取值为:
#define SEC_1 0x00 // after 1 second
#define M_SEC_250 0x01 // after 250 ms
#define M_SEC_15 0x02 // after 15 ms
#define M_SEC_2 0x03 // after 2 ms
 

// 喂狗程序
#define WDT_RESET() do { \
WDCTL
= (WDCTL & ~ 0xF0 ) | 0xA0 ; \
WDCTL
= (WDCTL & ~ 0xF0 ) | 0x50 ; \
}
while ( 0 )


// 启动/停止看门狗定时器
#define WDT_ENABLE() WDCTL |= 0x08
#define WDT_DISABLE() WDCTL &= ~0x08
/* **********************************************************
ADC
**********************************************************
*/
// 配置单次ADC
#define ADC_SINGLE_CONVERSION(settings) \
do { ADCCON3 = settings; } while ( 0 )

// 其中的参数 setting 由下面的组合构成
// 参考电压
#define ADC_REF_1_25_V 0x00 // 内部 1.25V 参考电压
#define ADC_REF_P0_7 0x40 // AIN7 引脚上的外部参考电压
#define ADC_REF_AVDD 0x80 // AVDD_SOC 引脚
#define ADC_REF_P0_6_P0_7 0xC0 // AIN6-AIN7 差分输入的外部参考电压
 

// 采样速率
#define ADC_8_BIT 0x00 // 8位
#define ADC_10_BIT 0x10 // 10位
#define ADC_12_BIT 0x20 // 12位
#define ADC_14_BIT 0x30 // 14位
 

// 输入频道
#define ADC_AIN0 0x00 // P0_0
#define ADC_AIN1 0x01 // P0_1
#define ADC_AIN2 0x02 // P0_2
#define ADC_AIN3 0x03 // P0_3
#define ADC_AIN4 0x04 // P0_4
#define ADC_AIN5 0x05 // P0_5
#define ADC_AIN6 0x06 // P0_6
#define ADC_AIN7 0x07 // P0_7
#define ADC_GND 0x0C //
#define ADC_TEMP_SENS 0x0E // 片内温度传感器
#define ADC_VDD_3 0x0F // vdd/3
 






// ADC转换完成的标志
#define ADC_SAMPLE_READY() (ADCCON1 & 0x80)

#endif
//启动ADC转化
#define ADC_START() \
do { ADCCON1 |=0x40; } while (0)
// 选择ADC的触发模式为手动(即ADC_SAMPLE_READY)
#define ADC_STOP() \
do { ADCCON1 |=0x30; } while (0)

(2)功能模块层

/* **********************************************************
*文件名称: module.h
*作 者: hustlzp
*日 期: 2011/3/6
*版 本: 1.0
*功能说明: 功能模块层头文件
*函数列表: void led_init()
void timer1_init()
void uart0_init(void);
void Uart0SendString(unsigned char *s);
float adc_start(void)
void get_temperature(unsigned char *output,float temp);
void watchdog_init(void);
*修改记录:
**********************************************************
*/
   
#ifndef MODULE_H
#define MODULE_H
 

#include
" hal.h "
 

/* **********************************************************
LED
**********************************************************
*/
// 定义LED引脚
#define led1 P1_0
#define led2 P1_1
#define led3 P1_2
#define led4 P1_3

// led亮与灭
#define LED_OFF 1
#define LED_ON 0

// LED初始化
void led_init( void );



/* **********************************************************
timer1
**********************************************************
*/
// 用于设置定时器的溢出周期值
#define TIMER1_OVF_2SEC 0xF424 // 2s
#define TIMER1_OVF_1SEC 0x7A12 // 1s
#define TIMER1_OVF_dot5SEC 0x3D09 // 0.5s

// 定时器1初始化
void timer1_init( void );


/* **********************************************************
UART0
**********************************************************
*/
// UART0初始化
void uart0_init( void );

// 串口传送字符串
void Uart0SendString(unsigned char * s);


/* **********************************************************
ADC-14
**********************************************************
*/
// 用于将ADC得到的数据转化为摄氏温度
#define ADC_TO_CELSIUS(temp) (temp * 0.06229 - 311.43)

// 启动ADC转换
float adc_start( void );

// 转换
void get_temperature(unsigned char * output, float temp);


/* **********************************************************
WatchDog
**********************************************************
*/
// 看门狗初始化
void watchdog_init( void );

#endif
/* **********************************************************
*文件名称: module.c
*作 者: hustlzp
*日 期: 2011/3/11
*版 本: 1.0
*功能说明: 功能模块层源文件
*函数列表: (略)
*修改记录:
**********************************************************
*/
 

#include
" module.h "


/* **********************************************************
*函数名称: led_init
*函数功能: LED初始化
*入口参数: 无    
*出口参数: 无  
**********************************************************
*/
void led_init( void )
{
// 配置P1.0 P1.1 P1.2 P1.3 为通用I/O口
IO_FUNC_PORT_PIN( 1 , 0 , IO_FUNC_GIO);
IO_FUNC_PORT_PIN(
1 , 1 , IO_FUNC_GIO);
IO_FUNC_PORT_PIN(
1 , 2 , IO_FUNC_GIO);
IO_FUNC_PORT_PIN(
1 , 3 , IO_FUNC_GIO);

// 配置P1.0 P1.1 P1.2 P1.3 为输出
IO_DIR_PORT_PIN( 1 , 0 , IO_OUT);
IO_DIR_PORT_PIN(
1 , 1 , IO_OUT);
IO_DIR_PORT_PIN(
1 , 2 , IO_OUT);
IO_DIR_PORT_PIN(
1 , 3 , IO_OUT);

led1
= LED_ON;
led2
= LED_OFF;
led3
= LED_OFF;
led4
= LED_OFF;
}


/* **********************************************************
*函数名称: timer1_init
*函数功能: 定时器1初始化
*入口参数: 无    
*出口参数: 无  
**********************************************************
*/
void timer1_init( void )
{
INT_GLOBAL_ENABLE(INT_ON);
// 开全局中断

INT_ENABLE(INUM_T1, INT_ON);
// 开T1中断

TIMER1_ENABLE_OVERFLOW_INT(INT_ON);
// 开T1计数溢出中断

SET_TIMER_TICK(TIMER1_TICK_4M);
// 设置定时器TICK为4MHz

SET_TIMER1_PERIOD(TIMER1_OVF_2SEC);
// 设置T1的计数周期为2s

SET_TIMER1_TICKDIV(TIMER1_TICKDIV_128);
// 设置T1的时钟分频为128

SET_TIMER1_MODE(TIMER1_MODE_MODULE);
// 设置T1的运行模式为module
}


/* **********************************************************
*函数名称: uart0_init
*函数功能: 串口UART0初始化
*入口参数: 无    
*出口参数: 无  
**********************************************************
*/
void uart0_init( void )
{
// 选择uart位置
IO_PER_LOC_UART0_AT_PORT0_PIN2345();

// 配置uart:接收允许,115200bps,一位停止位,无奇偶校验
UART_SETUP( 0 , UART_RECEIVE_ENABLE, 115200 , ONE_STOP_BITS | PARITY_DISABLE);

// 开总中断
INT_GLOBAL_ENABLE(INT_ON);

// 开串口0接收中断
INT_ENABLE(INUM_URX0, INT_ON);
}


/* **********************************************************
*函数名称: Uart0SendString
*函数功能: 定时器1初始化
*入口参数: unsigned char *s
想要发送的字符串    
*出口参数: 无  
**********************************************************
*/
void Uart0SendString(unsigned char * s)
{
while ( * s != 0 )
UART0_SEND(
* s ++ );
}


/* **********************************************************
*函数名称: adc_start
*函数功能: 启动ADC转换
*入口参数: 无    
*出口参数: float
片内的摄氏温度值  
**********************************************************
*/
float adc_start( void )
{
unsigned
int temp;

// 参考电压选择1.25V,采样精度为14位,转换目标为片内温度传感器
ADC_SINGLE_CONVERSION(ADC_REF_1_25_V | ADC_14_BIT | ADC_TEMP_SENS);

ADC_STOP();
// 设置ADC转化的触发方式为手动

ADC_START();
// 启动ADC转化

while ( ! ADC_SAMPLE_READY()); // 等待转化完成

temp
= ADCL >> 2 ; // 将转化结果存入temp中
temp |= (((unsigned int ) ADCH) << 6 );

return ADC_TO_CELSIUS(temp); // 返回转换后的实际温度值
}


/* **********************************************************
*函数名称: get_temperature
*函数功能: 将温度值处理后存入字符数组中,便于串口输出
*入口参数: unsigned char *output
用于存储转换后的温度值
float temp 
摄氏温度值   
*出口参数: 无  
**********************************************************
*/
void get_temperature(unsigned char * output, float temp)
{
output[
0 ] = (unsigned char )(temp) / 10 + 48 ; // 十位
output[ 1 ] = (unsigned char )(temp) % 10 + 48 ; // 个位
output[ 2 ] = ' . ' ; // 小数点
output[ 3 ] = (unsigned char )(temp * 10 ) % 10 + 48 ; // 十分位
output[ 4 ] = (unsigned char )(temp * 100 ) % 10 + 48 ; // 百分位
output[ 5 ] = ' \0 ' ; // 字符串结束符
}


/* **********************************************************
*函数名称: watchdog_init
*函数功能: 看门狗初始化
*入口参数: 无   
*出口参数: 无  
**********************************************************
*/
void watchdog_init( void )
{
WDT_SET_TIMEOUT_PERIOD(SEC_1);
// 设置超时时间为1s
WDT_ENABLE(); // 启动看门狗
}

(3)应用程序层

/* ******************************************************************
文件名称: main.c
作 者: hustlzp
日 期: 2011/3/11
版 本: 1.0
功能说明: 主程序文件
函数列表: (略)
修改记录:
******************************************************************
*/


#include


 


/* *******************************************************************
中断服务程序
*******************************************************************
*/
/* 定时器1溢出中断子程序
-------------------------------------------------------
*/
#pragma vector=T1_VECTOR
__interrupt
void T1_ISR( void )
{
EA
= 0 ; // 关中断

led2
= LED_ON;

get_temperature(output,adc_start());
// 将温度值转换为待输出的字符数组

Uart0SendString(output);
// 输出温度值
Uart0SendString( " ℃\r\n " );


led2


/* 串口接收中断子程序
-------------------------------------------------------
*/
#pragma vector=URX0_VECTOR
__interrupt
void RE_ISR( void )
{
EA
= 0 ;

led3
= LED_ON;

receive
= U0DBUF;

if (type == 1 ) // type=1,表示接收到的字符用于设置定时器溢出周期
{
type
= 0 ;
switch (receive)
{
case ' 0 ' : // 定时器溢出周期为0.5s
{
SET_TIMER1_PERIOD(TIMER1_OVF_dot5SEC);
break ;
}
case ' 1 ' : // 定时器溢出周期为1s
{
SET_TIMER1_PERIOD(TIMER1_OVF_1SEC);
break ;
}
case ' 2 ' : // 定时器溢出周期为2s
{
SET_TIMER1_PERIOD(TIMER1_OVF_2SEC);
break ;
}
}
}
else if (type == 2 ) // type=2,表示接收到的字符用于睡眠控制
{
type
= 0 ;
led1
= LED_OFF;
led2
= LED_OFF;
led3
= LED_OFF;
switch (receive)
{
case ' 1 ' : // 进入电源模式PM1
{
SET_POWER_MODE(
1 );
break ;
}
case ' 2 ' : // 进入电源模式PM2
{
SET_POWER_MODE(
2 );
break ;
}
case ' 3 ' : // 进入电源模式PM3
{
SET_POWER_MODE(
3 );
break ;
}
}
}
else if (type == 0 ) // type=0,表示接收到的字符为控制命令的种类:@ 或 $
{
if (receive == ' @ ' )
{
type
= 1 ; // 接收到'@',表示下一个字符用于设置溢出周期
}
else if (receive == ' $ ' )
{
type
= 2 ; // 接收到'$',表示下一个字符用于系统睡眠控制
}
}

led3
= LED_OFF;

EA
= 1 ;
}
=LED_OFF;

TIMER1_OVERFLOW_INT_SETFLAG(INT_CLR);
//清中断标志

EA
=1; //开中断
}

/* 主函数
-------------------------------------------------------
*/
void main(void)
{
SET_MAIN_CLOCK_SOURCE(CRYSTAL);
//设置系统时钟为32MHz晶振

led_init();
//LED初始化

uart0_init();
//串口UART0初始化

timer1_init();
//定时器1初始化

watchdog_init();
//看门狗初始化

while(1)
{
WDT_RESET();
//不断喂狗
}
}
/********************************************************************
主程序
*******************************************************************
*/
/* 全局变量
-------------------------------------------------------
*/
unsigned
char output[6]={0}; //存储温度数据以便于串口输出
unsigned char receive; //存储接收到的字符
unsigned char type=0; //接收到的字符的类型标志,取值为0/1/2
"module.h"

五、测试

  吁~代码终于贴完了,真是累死了,下面我们来测试一下这个小系统:

(1)定时采样

  打开串口,并启动IAR调试,发现 led1 亮,同时串口工具上不断有温度值产生,采样间隔经测定为2s:

 Zigbee之旅(十):综合小实验——基于CC2430的温度监测系统_第5张图片

(2)采样间隔控制

  在串口工具中输入"@1",然后再测试采样间隔,发现已变为1s;输入"@0",采样间隔已变为0.5s。

Zigbee之旅(十):综合小实验——基于CC2430的温度监测系统_第6张图片

(3)睡眠控制

  在串口工具中输入"$1",发现 led 全部熄灭,温度采样也已停止:

Zigbee之旅(十):综合小实验——基于CC2430的温度监测系统_第7张图片

  经测试,本系统工作正常稳定,基本符合要求。

  需要源码的同学点此下载

六、结语

  本文以一个稍具综合性的小实验为例,展示了如何整合CC2430片上资源,编写出一个比较规范的小系统。过几天我会抽时间为 hal.h 编写一个简单的使用手册,方便自己和大家便捷地操控 CC2430。

  接下来,笔者将会结束针对 CC2430 片上资源的研究,全力投入到 TI Z-Stack 协议栈的学习中~

  本系列的博文写作暂时结束了,但Zigbee的旅行仍将继续。前方的风景未知,但相信笔者和大家一起披荆斩棘,遍尝酸甜苦辣,定会有所斩获。

  敬请期待:"登临 TI Z-Stack" 系列博文!

你可能感兴趣的:(系统)