http://blog.csdn.net/xukai871105/article/details/7303584
为了完成课题,最近下定决心学习CC2430。网上的资料很多,重复的部分不做过多的介绍,没有的部分做一下少许的补充,希望对大家有用!【建议各位使用更新的CC2530,该芯片为CC2430的替代版本】
全部代码如下,先看一下整体!
- <span style="font-size:16px;">//头文件
- #include "hal.h"
- #include "stdio.h"
- // 函数声明
- UINT8 UART0_Init();
-
- void main(){
- //使用外部时钟
- SET_MAIN_CLOCK_SOURCE(CRYSTAL);
- //串口初始化
- UART0_Init();
- //字符输出 输出1
- putchar('1');
- //字符串输出 输出Hello CC2430
- printf("Hello CC2430!\n");
- while(1){
- }
- }
-
- int putchar(int c){
- if(c== '\n'){
- while(!UTX0IF);
- UTX0IF = 0;
- U0DBUF = '\r';
- }
- while(!UTX0IF);
- UTX0IF = 0;
- U0DBUF = c;
- return c;
- }
-
- UINT8 UART0_Init(){
- //UART0 IO口定位
- IO_PER_LOC_UART0_AT_PORT0_PIN2345();
- //9600 8 N 1
- UART_SETUP(0,9600,HIGH_STOP);
- UTX0IF = 1;
- return 0;
- }</span>
下面一步一步分析一下
1 头文件
#include "hal.h" 该头文件包括了操作CC2430常用寄存器的宏,书中找到的觉得很实用。
#include "stdio.h" 调用printf函数,重定义putchar函数
2 定义系统时钟 SET_MAIN_CLOCK_SOURCE(CRYSTAL);
设定了CC2430的时钟频率,即使用片外32MHz。根据数据手册,CC2430可以有两种时钟,第一种是片外的32M石英晶振,另一种是片内的16M的RC振荡器。顺便说一下,这个SET_MAIN_CLOCK_SOURCE() 是一个名为hal.h文件中的“动作宏”。我手头有一本书,名为《ZigBee技术实践教程》,其中的代码都使用了这个头文件。该头文件中有很多的API函数,若仔细的查看代码,配合数据手册页可以看懂其中各宏的“原理”。设定时钟的的函数原型是这样的:
- <span style="font-size:16px;">#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)
-
-
- </span>
对应着数据手册,完全可以看懂其中的“玄机”。由于我还处在学习CC2430的初级阶段,以后就根据这个头文件编写程序。至于寄存器的话,大概知道其中的作用即可
领悟到的小技巧:
1 如果需要使用一个复杂的动作宏的话,多行书写使用 \ 。
2 所有的这些动作都包含在 do{} while(0)中。
3 定义IO口IO_PER_LOC_UART0_AT_PORT0_PIN2345();
这句话把UART0的IO口定义到P02 P03 P04 P05。函数原型是这样的:
- <span style="font-size:16px;">#define IO_PER_LOC_UART0_AT_PORT0_PIN2345() do { PERCFG = (PERCFG&~0x01)|0x00; } while (0)</span>
4 设定串口相关参数UART_SETUP(0,9600,HIGH_STOP);
函数原型是这样的:
- <span style="font-size:16px;">#define UART_SETUP(uart, 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##UCR |= ((options) | 0x80); \
- \
- if((options) & TRANSFER_MSB_FIRST){ \
- U##uart##GCR |= 0x20; \
- } \
- } while(0)
- </span>
5 UTX0IF = 1; UART0发送完成标志位1
若串口数据发送完成,该寄存器被置位。若寄存器被置位,需要通过软件清零。这个也是很好理解的。
6 putchar和printf函数
说完了初始化,再来谈谈如何使用putchar和printf函数。Putchar和printf都是C语言标准库函数。Printf调用putchar实现字符数据的传送。翻看IAR的代码,获得了使用putchar函数的信息。IAR中来了一段51单片机的代码。该代码位于EW8051_CompilerReference文件中。其中有这么一段参考代码。
- #include <io8052.h>
- int putchar(int c) {
- if (c == '\n') {
- while (!SCON_bit.TI);
- SCON_bit.TI = 0;
- SBUF = 0x0d;
- }
- while (!SCON_bit.TI);
- SCON_bit.TI = 0;
- return (SBUF = c);
- }
这段代码有点51基础的都可以看懂。唯一需要说明的就是,如果出现转义字符\n(回车)的话,先输出转义字符\t(换行)。简单说,windows喜欢先来换行(\r),再来回车(\n)。而linux中只要一个回车就OK。
同理,CC2430的代码就是可以这样写:
- int putchar(int c){
- if(c== '\n'){
- while(!UTX0IF);
- UTX0IF = 0;
- U0DBUF = '\r';
- }
- while(!UTX0IF);
- UTX0IF = 0;
- U0DBUF = c;
- return c;
- }
7 IAR中printf参数设置
接下来必须说说IAR中相关的设置,在EW8051_UserGuide中,指出了printf和scanf函数的相关设置。printf包含了small medium large三个选项。每个选型包含不同的printf功能,当然功能越全,消耗的资源也就越多。在这里我就先设置成small了。IAR的说明文件中也给出了这个选项参数所提供的功能,如下表所示。
8 实验结果
最后给出实验的结果,虽然代码非常的简单,但是我还是花了一整个下午去调试,后来发现其实是IAR中的link中某参数选择错误了。实验的结果还是预想的那样,使用的时候还是要注意串口的参数设定,CC2430的串口调试助手的参数要一致。
总结
串口的使用非常的重要,它是PC机和单片机的重要桥梁。现在,单片机负责采集数据,而PC机负责分析处理数据。在这个示例代码中,通过使用hal头文件,避免了复杂难记的寄存器操作,从而快速的初始化串口;通过改写putchar函数,写出了适合CC2430串口输出的函数;最后使用了标准的printf函数实现了串口输出。