在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 (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; \ } \ } \ 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 的取值为: #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 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; } \ 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) /*设置中断优先级 -----------------------------------------*/ #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; } \ 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的值 #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; } \ elseif (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转化 (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; } } } 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; 五、测试吁~代码终于贴完了,真是累死了,下面我们来测试一下这个小系统: (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" 系列博文! |
|