在3月3日写完“zigbee之旅(九)”后,笔者本打算立即着手“温度监测系统”小实验的编写,以作为对之前一系列零散知识点的总结。然而我又意识到,前面的几个小实验虽然每一篇都讲得较为详细,但是其代码的规范性、结构性,可以说是不堪入目的。既然是小结,就应当在原来的基础上有所进步,而不是机械地把前面的小知识点拼凑起来了事。因此,我暂停了原来的计划,抽出时间去学习了一下嵌入式开发的通用技巧,写下了两篇随笔《嵌入式C51编程规范》和《嵌入式项目代码结构的分层》。本篇日志,既是Zigbee首次旅行的一个阶段性小结,也融入了笔者近几天的学习心得,希望能对Zigbee初学者有所帮助。
全文按软件开发的基本流程来组织:需求分析、概要设计、详细设计、编码实现、测试。
一、需求分析
经“客户”与“开发者”共同商讨,确定了如下的系统功能描述:
☻ 使用基于CC2430的节点采集当前室温,并可通过PC监测其温度数值
☻ CC2430节点本身需具备一定的稳定性,可自动恢复正常状态
☻ 可通过PC来控制节点的采样间隔与电源管理
二、概要设计
根据上述需求分析,我们可以把系统分为两大模块:CC2430节点 与 PC机。
[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:
(2)采样间隔控制
在串口工具中输入"@1",然后再测试采样间隔,发现已变为1s;输入"@0",采样间隔已变为0.5s。
(3)睡眠控制
在串口工具中输入"$1",发现 led 全部熄灭,温度采样也已停止:
经测试,本系统工作正常稳定,基本符合要求。
需要源码的同学点此下载
六、结语
本文以一个稍具综合性的小实验为例,展示了如何整合CC2430片上资源,编写出一个比较规范的小系统。过几天我会抽时间为 hal.h 编写一个简单的使用手册,方便自己和大家便捷地操控 CC2430。
接下来,笔者将会结束针对 CC2430 片上资源的研究,全力投入到 TI Z-Stack 协议栈的学习中~
本系列的博文写作暂时结束了,但Zigbee的旅行仍将继续。前方的风景未知,但相信笔者和大家一起披荆斩棘,遍尝酸甜苦辣,定会有所斩获。
敬请期待:"登临 TI Z-Stack" 系列博文!