在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溢出中断程序流程图
  • 串口接收中断程序流程图
 
 

四、编码实现

(1)硬件抽象层

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

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

  • 头部
  • I/O口
  • 中断
  • 串口
  • 电源及时钟管理
  • 定时器
  • 看门狗
  • ADC
/***********************************************************
*文件名称: hal.h
*作 者: hustlzp
*日 期: 2011/3/8
*版 本: 1.1
*功能说明: 硬件抽象层
*修改记录:
***********************************************************/

#ifndef HAL_H
#defineHAL_H

#include <ioCC2430.h>

/***********************************************************
常用类型定义
***********************************************************/
typedef unsigned charBYTE;
typedef unsigned intWORD;
typedef unsigned longDWORD;

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

//高8位
#defineHIGH_BYTE(a) ((BYTE) (((WORD)(a)) >> 8))

//低8位
#defineLOW_BYTE(a) ((BYTE) ((WORD)(a)))

//赋值
#defineSET_WORD(regH,regL,word) \
do{ \
(regH)=HIGH_BYTE(word); \
(regL)=LOW_BYTE(word); \
}while(0)

复制代码

/***********************************************************
I/O口
***********************************************************/
/*配置I/O口方向
-----------------------------------------*/
#defineIO_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 的取值为:
#defineIO_IN 0
#defineIO_OUT 1

/*配置I/O口的输入模式
-----------------------------------------*/
#defineIO_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)

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

//其中参数 pud 的取值为:
#defineIO_PULLUP 0 //上拉
#defineIO_PULLDOWN 1 //下拉

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

#defineIO_FUNC_PORT_PIN(port, pin, func) \
do{ \
if((port ==2) &&(pin ==3)){ \
if(func) { \
P2SEL |=0x02; \
} else{ \
P2SEL &=~0x02; \
} \
} \
elseif((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 的取值为:
#defineIO_FUNC_GIO 0 //通用I/O
#defineIO_FUNC_PERIPH 1 //外设I/O

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

#defineIO_PER_LOC_TIMER3_AT_PORT1_PIN34() do { PERCFG = (PERCFG&~0x20)|0x00; } while (0)
#defineIO_PER_LOC_TIMER3_AT_PORT1_PIN67() do { PERCFG = (PERCFG&~0x20)|0x20; } while (0)

#defineIO_PER_LOC_TIMER4_AT_PORT1_PIN01() do { PERCFG = (PERCFG&~0x10)|0x00; } while (0)
#defineIO_PER_LOC_TIMER4_AT_PORT2_PIN03() do { PERCFG = (PERCFG&~0x10)|0x10; } while (0)

#defineIO_PER_LOC_SPI1_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x08)|0x00; } while (0)
#defineIO_PER_LOC_SPI1_AT_PORT1_PIN4567() do { PERCFG = (PERCFG&~0x08)|0x08; } while (0)

#defineIO_PER_LOC_SPI0_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x04)|0x00; } while (0)
#defineIO_PER_LOC_SPI0_AT_PORT1_PIN2345() do { PERCFG = (PERCFG&~0x04)|0x04; } while (0)

#defineIO_PER_LOC_UART1_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x02)|0x00; } while (0)
#defineIO_PER_LOC_UART1_AT_PORT1_PIN4567() do { PERCFG = (PERCFG&~0x02)|0x02; } while (0)

#defineIO_PER_LOC_UART0_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x01)|0x00; } while (0)
#defineIO_PER_LOC_UART0_AT_PORT1_PIN2345() do { PERCFG = (PERCFG&~0x01)|0x01; } while (0)

//其中参数 imode 的取值为:#define IO_IMODE_PUD  0   // 上拉/下拉#define IO_IMODE_TRI  1   // 三态
复制代码

/***********************************************************
中断
***********************************************************/
//用于开/关中断
#defineINT_ON 1
#defineINT_OFF 0

//用于置位/清除中断标志
#defineINT_SET 1
#defineINT_CLR 0

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

//定义中断
#defineINUM_RFERR 0
#defineINUM_ADC 1
#defineINUM_URX0 2
#defineINUM_URX1 3
#defineINUM_ENC 4
#defineINUM_ST 5
#defineINUM_P2INT 6
#defineINUM_UTX0 7
#defineINUM_DMA 8
#defineINUM_T1 9
#defineINUM_T2 10
#defineINUM_T3 11
#defineINUM_T4 12
#defineINUM_P0INT 13
#defineINUM_UTX1 14
#defineINUM_P1INT 15
#defineINUM_RF 16
#defineINUM_WDT 17

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

/*设置中断优先级
-----------------------------------------*/
#defineINT_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 的取值为:
#defineRFERR_RF_DMA 0x01 //Group IP0
#defineADC_P2INT_T1 0x02 //Group IP1
#defineURX0_UTX0_T2 0x04 //Group IP2
#defineURX1_UTX1_T3 0x08 //Group IP3
#defineENC_P1INT_T4 0x10 //Group IP4
#defineST_WDT_P0INT 0x20 //Group IP5

/*获取中断标志
-----------------------------------------*/
#defineINT_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\
)

/*设置中断标志
-----------------------------------------*/
#defineINT_SETFLAG(inum, f) \
do{ \
if(inum==INUM_RFERR) { RFERRIF=f; } \
elseif(inum==INUM_ADC) { ADCIF =f; } \
elseif(inum==INUM_URX0) { URX0IF =f; } \
elseif(inum==INUM_URX1) { URX1IF =f; } \
elseif(inum==INUM_ENC) { ENCIF_1 =ENCIF_0 =f; } \
elseif(inum==INUM_ST) { STIF =f; } \
elseif(inum==INUM_P2INT) { P2IF =f; } \
elseif(inum==INUM_UTX0) { UTX0IF=f; } \
elseif(inum==INUM_DMA) { DMAIF =f; } \
elseif(inum==INUM_T1) { T1IF =f; } \
elseif(inum==INUM_T2) { T2IF =f; } \
elseif(inum==INUM_T3) { T3IF =f; } \
elseif(inum==INUM_T4) { T4IF =f; } \
elseif(inum==INUM_P0INT) { P0IF =f; } \
elseif(inum==INUM_UTX1) { UTX1IF=f; } \
elseif(inum==INUM_P1INT) { P1IF =f; } \
elseif(inum==INUM_RF) { (f) ?(S1CON |=0x03) : (S1CON &=~0x03); } \
elseif(inum==INUM_WDT) { WDTIF =f; } \
} while(0)

复制代码

/***********************************************************
串口
***********************************************************/
//不同波特率对应的BAUD_E的值
#defineBAUD_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的值
#defineBAUD_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模式下的串口配置
-----------------------------------------*/
#defineUART_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 的取值:
#defineUART_RECEIVE_ENABLE 0x40 //接收允许
#defineUART_RECEIVE_DISABLE 0x00

//其中参数 options 的取值:
#defineFLOW_CONTROL_ENABLE 0x40 //流控制
#defineFLOW_CONTROL_DISABLE 0x00

#defineEVEN_PARITY 0x20 //偶校验
#defineODD_PARITY 0x00 //奇校验

#defineNINE_BIT_TRANSFER 0x10 //9字节传输
#defineEIGHT_BIT_TRANSFER 0x00 //8字节传输

#definePARITY_ENABLE 0x08 //奇偶校验使能
#definePARITY_DISABLE 0x00

#defineTWO_STOP_BITS 0x04 //2位停止位
#defineONE_STOP_BITS 0x00 //1位停止位

#defineHIGH_STOP 0x02 //停止位高电平
#defineLOW_STOP 0x00 //停止位低电平

#defineHIGH_START 0x01 //起始位电平高
#defineLOW_START 0x00 //起始位电平低

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

//串口接收字符
#defineUART_RECEIVE(uart,data) \
do{ \
while(!(U##uart##CSR&0x04)); \
data=U##uart##DBUF; \
} while(0)
#defineUART0_RECEIVE(data) UART_RECEIVE(0,data)
#defineUART1_RECEIVE(data) UART_RECEIVE(1,data)

复制代码

/***********************************************************
电源及时钟管理
***********************************************************/
//获取时钟分频
#defineCLKSPD (CLKCON & 0x07)

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

//参数 mode 的取值为:
#definePOWER_MODE_0 0x00
#definePOWER_MODE_1 0x01
#definePOWER_MODE_2 0x02
#definePOWER_MODE_3 0x03

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

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

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

//设置主时钟频率
#defineSET_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 的取值为:
#defineCRYSTAL 0x00 //晶体振荡器
#defineRC 0x01 //RC振荡器

复制代码

/***********************************************************
定时器1
***********************************************************/
//定时器1允许计数溢出中断
#defineTIMER1_ENABLE_OVERFLOW_INT(val) \
(TIMIF =(val) ?TIMIF |0x40: TIMIF &~0x40)

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

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

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

//其中value的取值为:
#defineTIMER1_TICK_32M 0x00 //32MHz
#defineTIMER1_TICK_16M 0x08 //16MHz,系统复位默认值
#defineTIMER1_TICK_8M 0x10 //8MHz
#defineTIMER1_TICK_4M 0x18 //4MHz
#defineTIMER1_TICK_2M 0x20 //2MHz
#defineTIMER1_TICK_1M 0x28 //1MHz
#defineTIMER1_TICK_500k 0x30 //500kHz
#defineTIMER1_TICK_250k 0x38 //250kHz

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

//其中 value 的取值为:
#defineTIMER1_TICKDIV_1 0x00 //1分频
#defineTIMER1_TICKDIV_8 0x04 //8分频
#defineTIMER1_TICKDIV_32 0x08
#defineTIMER1_TICKDIV_128 0x0c

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

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

//其中 mode 的取值为:
#defineTIMER1_MODE_STOP 0x00
#defineTIMER1_MODE_FREE 0x01
#defineTIMER1_MODE_MODULE 0x02
#defineTIMER1_MODE_UPDOWN 0x03

复制代码

/***********************************************************
看门狗
***********************************************************/
//设置看门狗定时器的溢出周期
#defineWDT_SET_TIMEOUT_PERIOD(timeout) \
do{ WDCTL &=~0x03; WDCTL |=timeout; } while(0)

//其中参数 timeout 的取值为:
#defineSEC_1 0x00 //after 1 second
#defineM_SEC_250 0x01 //after 250 ms
#defineM_SEC_15 0x02 //after 15 ms
#defineM_SEC_2 0x03 //after 2 ms

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

//启动/停止看门狗定时器
#defineWDT_ENABLE() WDCTL |= 0x08
#defineWDT_DISABLE() WDCTL &= ~0x08

复制代码

/***********************************************************
ADC
***********************************************************/
//配置单次ADC
#defineADC_SINGLE_CONVERSION(settings) \
do{ ADCCON3 =settings; }while(0)

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

//采样速率
#defineADC_8_BIT 0x00 //8位
#defineADC_10_BIT 0x10 //10位
#defineADC_12_BIT 0x20 //12位
#defineADC_14_BIT 0x30 //14位

//输入频道
#defineADC_AIN0 0x00 //P0_0
#defineADC_AIN1 0x01 //P0_1
#defineADC_AIN2 0x02 //P0_2
#defineADC_AIN3 0x03 //P0_3
#defineADC_AIN4 0x04 //P0_4
#defineADC_AIN5 0x05 //P0_5
#defineADC_AIN6 0x06 //P0_6
#defineADC_AIN7 0x07 //P0_7
#defineADC_GND 0x0C //地
#defineADC_TEMP_SENS 0x0E //片内温度传感器
#defineADC_VDD_3 0x0F //vdd/3

 

//ADC转换完成的标志
#defineADC_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
  • module.c
/***********************************************************
*文件名称: 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
#defineMODULE_H

#include "hal.h"

/***********************************************************
LED
***********************************************************/
//定义LED引脚
#defineled1 P1_0
#defineled2 P1_1
#defineled3 P1_2
#defineled4 P1_3

//led亮与灭
#defineLED_OFF 1
#defineLED_ON 0

//LED初始化
voidled_init(void);

/***********************************************************
timer1
***********************************************************/
//用于设置定时器的溢出周期值
#defineTIMER1_OVF_2SEC 0xF424 //2s
#defineTIMER1_OVF_1SEC 0x7A12 //1s
#defineTIMER1_OVF_dot5SEC 0x3D09 //0.5s

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

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

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

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

//启动ADC转换
floatadc_start(void);

//转换
voidget_temperature(unsigned char*output,floattemp);

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

#endif

复制代码

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

#include "module.h"

/***********************************************************
*函数名称: led_init
*函数功能: LED初始化
*入口参数: 无    
*出口参数: 无  
***********************************************************/
voidled_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初始化
*入口参数: 无    
*出口参数: 无  
***********************************************************/
voidtimer1_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初始化
*入口参数: 无    
*出口参数: 无  
***********************************************************/
voiduart0_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
想要发送的字符串    
*出口参数: 无  
***********************************************************/
voidUart0SendString(unsigned char*s)
{
while(*s !=0)
UART0_SEND(*s++);
}

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

//参考电压选择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);

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

/***********************************************************
*函数名称: get_temperature
*函数功能: 将温度值处理后存入字符数组中,便于串口输出
*入口参数: unsigned char *output
用于存储转换后的温度值
float temp 
摄氏温度值   
*出口参数: 无  
***********************************************************/
voidget_temperature(unsigned char*output,floattemp)
{
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
*函数功能: 看门狗初始化
*入口参数: 无   
*出口参数: 无  
***********************************************************/
voidwatchdog_init(void)
{
WDT_SET_TIMEOUT_PERIOD(SEC_1); //设置超时时间为1s
WDT_ENABLE(); //启动看门狗
}

复制代码

(3)应用程序层

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

#include

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

led2 =LED_ON;

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

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

led2

/*串口接收中断子程序
-------------------------------------------------------*/
#pragmavector=URX0_VECTOR
__interrupt voidRE_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;
}
}
}
elseif(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;
}
}
}
elseif(type==0) //type=0,表示接收到的字符为控制命令的种类:@ 或 $
{
if(receive=='@')
{
type=1; //接收到'@',表示下一个字符用于设置溢出周期
}
elseif(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" 系列博文!

转载于:https://www.cnblogs.com/zhangleiccst/archive/2011/12/11/2283986.html

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

  1. 基于python的分布式扫描器_基于python的服务器监测系统的设计

    基于 python 的服务器监测系统的设计 高正 ; 徐浩 ; 余曼 [期刊名称] <电脑知识与技术> [年 ( 卷 ), 期] 2017(013)002 [摘要] 本文介绍了一种基于 P ...

  2. 基于单片机的气象监测系统

    设计简介: 本设计是基于单片机的气象监测系统,主要实现以下功能: 可通过LCD1602显示温湿度和空气质量: 可通过按键调整温湿度阈值和空气质量最大值: 可通过ADC0832将MQ-135检测到的模拟 ...

  3. 基于数字电路交通灯信号灯控制系统设计-基于单片机病房温度监测与呼叫系统设计-基于STM32的无线蓝牙心电监护仪系统设计-基于STM32的智能蓝牙温控风扇控制设计-基于STM32的智能温室控制系统设计

    1617基于数字电路交通灯信号灯控制系统设计(仿真电路,论文报告)  摘  要:交通灯控制系统在城市交通控制中发挥着重要的作用,本次课程设计就是以城市交通灯控制系统为背景的,主要通过运用学过的数字电路 ...

  4. 基于树莓派的流星雨监测系统(RMS)的搭建

    本系列第二篇文章:基于树莓派的流星雨监测系统(RMS)的进一步改造(1)_delacroix_xu的专栏-CSDN博客 本系列第三篇文章:基于树莓派的流星雨监测系统(RMS)的进一步改造(2)_del ...

  5. 基于stm32人体健康监测系统

    基于stm32人体健康监测系统 (心率,血氧,体温,语音播报,报警) 本设计采用STM32F103C8T6作为主控 使用MAX30102采集心率和血氧值 使用MLX90614测量体温 OLED显示当前 ...

  6. 基于树莓派的空气监测系统(1)项目介绍

    基于树莓派的空气监测系统(1)项目介绍 项目开发使用的知识点包括:Qt程序设计,Python编程,文件编程,串口编程,树莓派驱动程序开发. 嵌入式系统学习 视频地址 嵌入式学习资料 资料地址 项目参考 ...

  7. 简单物联网应用——基于老人居家声音监测系统(全!含完整源码、详细注释、测试结果、设计过程、视频详细教程)

    简单物联网应用设计与实现--基于老人居家声音监测系统 概述 设计内容: 采用标准的物联网三层架构, 感知层为可以检测声音数据并且实现无线发送的传感器节点: 网络层主要使用无线 WiFi: 应用层中, ...

  8. 树莓派练手小项目---基于树莓派构建天气查询系统,实现内容的网页自动化检索功能

    目录 一.写在前面 二.基于树莓派构建天气查询系统 三.基于树莓派实现网页内容的自动化检索 四.有关于树莓派的其他小提醒,小技巧 Author:qyan.li Date:2022.6.19 Topic ...

  9. 基于STM32-消防栓监测系统毕业设计---论文(附加最全面的从硬件电路设计->驱动程序设计->阿里云物联网搭建->安卓APP设计)

    设计展示视频连接:消防栓监测系统视频 驱动程序工程文件:消防栓监测系统驱动程序工程(全寄存器开发的) 消防栓监测系统所有设计资料:全栈设计,如下图所示 消防栓监测系统论文(图片看不清的可以下载文档看) ...

  10. 基于5G城市道路积水监测系统解决方案

    概述 近年来,雨季暴雨来袭,下穿隧道.下沉式道路突发的积水淹没困在水中的车辆,而且积水难以快速排出,容易形成车内人员来不及逃生,导致死亡的恶性事件,在北京.广州.深圳等人口密集的城市已经发生了多起.所 ...

最新文章

  1. linux里运行windows,在Linux上运行Windows应用程序
  2. 我是LinkedIn的SRE,我把LinkedIn搞挂了
  3. Java 并发编程—有锁互斥机制及AQS理论
  4. C语言中的EOF符号常量
  5. c#中如何实现拷贝对象
  6. mysql 在线语法检查工具_「mysql 管理工具」五大开源MySQL管理工具! - seo实验室
  7. EXCEL等间隔数据处理
  8. 计算机指令vbs,vbs脚本大全,配有实例 DOS命令,批处理 脚本 代码
  9. PR期刊投稿要求(整体要求、Title page、Highlight)
  10. linux下go语言代理
  11. 阿里云域名注册与备案、服务器ECS购买与登录使用
  12. [P1919 【模板】A*B Problem升级版(FFT快速傅里叶)(高精乘板子,FFT板子)
  13. Linux为什么不怕病毒
  14. 录屏状态监听之防录屏 - iOS
  15. 网上传的京东撸货为什么那么火?
  16. android 关于px转化为dp,sp
  17. vw/vh:移动适配之vw/vh(使用方法)
  18. UI设计中标签设计总结
  19. 袁萌:Linux机器人来我家
  20. 软件测试工程师应该怎样规划自己?成为年薪30W+测试工程师(乾坤未定,皆是黑马)

热门文章

  1. R语言分类变量独立性检验(Tests of independence)、使用Fisher精确检验fisher.test函数检验分类变量的独立性( test of independence)
  2. R语言绘制花瓣图flower plot
  3. 乐视第4代超级电视引领行业风向标
  4. git gui here如何汉化_github的git GUI Here的使用,适合新手!!!
  5. NAACL 2022 | TAMT:通过下游任务无关掩码训练搜索可迁移的BERT子网络
  6. 计算机处理器显卡,怎么看处理器cpu和显卡
  7. 显卡更新后重启计算机就没了,电脑重启后显卡出现问题怎么办
  8. 用js转换joson返回数据库的时间格式为/Date(*************)/
  9. .9图片处理报错Error: java.lang.RuntimeException: Crunching Cruncher ic_coupon2.9.png failed, see logs
  10. 猿编程 python_猿编程客户端下载_猿编程(小学阶段编程课程学习专用) 1.5.2 官方版_极速下载站...