MSP430G2553硬件平台之标准输出函数Printf的实现
[复制链接]
串口调试为我们调试单片机程序的时候提供了很大的方便,我们通过他可以时刻监控程序的运行情况。在学C语言的时候有一个这样的典型程序: #include int main(void) { int year=2012; char * str="Hello world !"; printf("year=%d str=%s",year,str); return 0; } 其运行结果如下图1 VC环境运行窗口:
图1 VC环境运行窗口
看看上面这个printf函数,你会不会想,能否在MSP430G2553这个硬件平台上实现类似的函数功能啊?答案是肯定的。今天笔者就借助这个2553的硬件平台来实现这个标准化输出函数printf。
首先让笔者来介绍一下可变参数表的C语言知识。说到可变参数不得不提头文件,该头文件中提供了遍历未知数目和类型的函数参数表功能,假定函数printf带有可变数目的实际参数,lastarg是他最后一个有名称的形式参数。那么在printf内声明一个类型为va_list的变量ap,那么ap将依次指向每个实际的参数。
va_list ap;
在访问任何未命名的参数之前,必须用va_start宏初始化ap一次:
Va_start(va_list ap,lastarg);
此后每次执行完宏va_arg(va_list ap,类型);都将产生一个与下一个未命名的参数具有相同类型和数值的值,同时ap将自动指向下一个实际的参数。在执行完上述过程之后要使用宏va_end(ap);来释放指针ap。
上面的这些C语言知识在网上遍地都是,我在这里知识简短的介绍一下,若有不明白的地方请自己查阅。
接下来让笔者将printf在msp430g2553硬件平台上实现的代码给出一遍大家参考移植。
/ ******************************************************************** * File name : uart.c * Creating time : 2012-7-13 * Author : 李帅 * pen-name : 亦然 * Organization : 济南大学(2013年本科毕业) * Function : 该文件可用作模块儿化编程作为MSP430G2553的调试文件,该文 * 件实现 * 了在串口终端实现标准化输出函数Printf函数。 * Declareing : 如有错误的地方请各位指出,交流。交流使我们共同进步! * Contact way : QQ:1021480125 * ********************************************************************/ #include #include #include #include "uart.h" / ******************************************************************** *静态函数声明 * ********************************************************************/ static void Vs_Printf(char *s,char *format,va_list arg); static void IntToString(int temp,char *pchar); / ******************************************************************** * 函数名 : UartInit * 函数功能 : 初始化msp430g2553的USCI寄存器,使其工作在UART模式 * 形参 : 无 * 返回值 : 无 * ********************************************************************/ void UartInit() { / **************************************************************************** * P1.1设为输入作为uart的数据输入口 * P1.2设为输出作为UART的数据输出口 ****************************************************************************/ P1DIR &=~BIT1; P1DIR |=(BIT2 ); / **************************************************************************** * 将P1.1和P1.2口配置为第三功能口,UART功能 ****************************************************************************/ P1SEL |= BIT1 + BIT2 ; P1SEL2 |= BIT1 + BIT2; / **************************************************************************** * 配置U0CTL1寄存器,在UCSWRST置位的情况下(即保持USCI软件复位的情况下配置各 * 个寄存器的各标志位)置位UCSSEL_2配置时钟源为SMCLK。其他位为0 ****************************************************************************/ U0CTL1 |= UCSWRST; // Set SW Reset U0CTL1 = UCSSEL_2+ UCSWRST; // Use SMCLK, keep SW reset / **************************************************************************** * 配置波特率大小,公式:(U0BR1<<8+U0BR0)=(int)(source_clk/baud rate) * U0MCTL为(source_clk/baud rate)的余数乘以8之后的值对应表中的值,实现控 * 制调制系数 * 大小如表所示: * _______________________________________________________________ * |余数*8 |1 |2 |3 |4 |5 |6 |7 | * |U0MCTL |0x08 |0x88 |0x2A |0x55 |0x6B |0xdd |0xef | ****************************************************************************/ U0BR0 = 0x04; U0BR1 = 0x01; U0MCTL = 0x2A; / **************************************************************** * 解除软件复位,使USCI开始工作 ****************************************************************/ U0CTL1 &= ~UCSWRST; / **************************************************************** * 允许接收中断 ****************************************************************/ UC0IE |= UCA0RXIE; // Enable RX int } / ******************************************************************** * 函数名 : UartPutchar * 函数功能 : 向串口终端发送一个字符 * 形参 : Txdata为待发送的字符 * 返回值 : 无 *******************************************************************/ void UartPutchar(char Txdata) { if(Txdata==''){ while(UCA0STAT & UCBUSY); UCA0TXBUF=''; } while(UCA0STAT & UCBUSY); UCA0TXBUF=Txdata; } / ******************************************************************** * 函数名 : UartSendString * 函数功能 : 向串口终端发送一个字符串 * 形参 : pt为字符串指针。 * 返回值 : 无 *******************************************************************/ void UartSendString(char *pt) { while(*pt) UartPutchar(*pt++); } / ******************************************************************* * 函数名 : IntToString * 函数功能 : 将一个整形的书转化成为一个表现形式一样的字符串 * 说明 : 举例说明功能,若有一个整数为65535,该函数作用就是将 * “65535”其转化为字符串 * 形参 : temp为待装换的整数,pchar为转化成的字符串所存储的地址 * 返回值 : 无 ********************************************************************/ void IntToString(int temp,char *pchar) { char ch,*p=pchar; while(temp!=0){ *pchar++=(char)(temp+0x30); temp/=10; } *pchar--=''; while(pchar>p){ ch=*p; *p++=*pchar; *pchar--=ch; } } / ******************************************************************** * 函数名 : Vs_Printf * 函数功能 : 将参数指针arg所指的不定参数按照格式字符串format中的一一对应的格 * 式转化后生成一个新的字符串存于地址s中。 * 形参 : s为最终形成的字符串存储地址,format为格式字符串,arg为不定参数指 * 针。 * 返回值 : 无 ********************************************************************/ void Vs_Printf(char *s,char *format,va_list arg) { char *pchar; char *temp; for(pchar=format;*pchar;pchar++){ if(*pchar !='%'){ *s++=*pchar; continue; } switch(*++pchar){ case 'd' :{ IntToString(va_arg(arg,int),s); while(*s++); *--s='0'; break; } case 's' :{ temp=va_arg(arg,char *); while(*s++=*temp++); *--s='0'; break; } case 'c' :{ *s++=va_arg(arg,char); break; } default :break; } } *s=''; } / ******************************************************************** * 函数名 : Printf * 函数功能 : 格式化输出 * 形参 : fmt为格式字符串,…为不定(类型不定、个数不定)形参 * 返回值 : 无 *******************************************************************/ void Printf(char *fmt,...) { / ******************************************************** * va_list类型声明一个参数指针ap,该变量将依次引用各个参数 * ******************************************************/ va_list ap; char string[256]; / ******************************************************** * va_start(ap,fmt),该函数将ap指针指向参数列表中的最后一个 * 有名参数fmt * ******************************************************/ va_start(ap,fmt); / ******************************************************** * Vs_Printf,该函数将ap指向的无名参数按照fmt固定的格式转化 * 成为字符串存于string中 * ******************************************************/ Vs_Printf(string,fmt,ap); UartSendString(string); / ******************************************************** * 最后调用va_end(ap)释放指针 * ******************************************************/ va_end(ap); } / ************************ end of file ******************************/
将文件uart.c添加到工程中去,并建立主函数进行验证。笔者的写的测试主函数如下所示:
#include #include "clock.h" #include "uart.h" void main() { int year=2012; WDTCTL = WDTPW + WDTHOLD; Init_Clk(); UartInit(); char *str="Hello World!"; Printf("year=%d str=%s",year,str); } 在MSP430G2553上运行该程序,会得到与图1 VC环境运行窗口相同的功能效果,如下图2串口调试窗口
图2 串口调试窗口
这样有了printf函数以后向串口打印信息就方便多了。也为调试程序铺平了道路。
写到这里笔者有所感悟,顺便谈谈,我身边有51单片机,C8051单片机,凌阳单片机,msp430单片机,飞思卡尔单片机,还有强悍一点儿的就是三星的S3C2440。想想看,这么多单片如果不找到一个合理的、科学的学习方法,我们只能是越学越糊涂。因为我发现我身边的好多同学不管学什么单片机,当他拿到一款陌生的单片机之后,首先就去网上找现成的例程,然后下下来放在自己单片机上调,等调通了,这就算自己的程序了,然后换个内部资源继续这种方法调,不用多长时间下来,这一款单片机就算是学完了,然后再换一款单片机继续前面的做法。周而复始,等把所有的单片机学了一遍了,感觉自己什么也学过了,什么也会了。其实不然,他在这过程中根本没有将这些知识消化为自己的。在这过程中他只是充当了一个操作工的角色,根本没自己的思想与思路在内。这种做法是令人悲哀的。记住,切莫把自己培养成一个操作工。
合理的学习方法就是要做到举一反三。举个例子来说明这个问题,懂微机原理的人明白,我们刚学C语言时写的调用的printf函数是运行在x86硬件平台上的;而今天笔者做的这个程序是运行在MSP430G2553这个平台上的;其实这个函数之前笔者已经在学S3C2440时在arm920的架构下已经实现了,今天这个程序已经算是在移植了。不同的硬件平台实现了相同的函数功能。拿着同一个功能在三种硬件平台上实现。这过程中就是在培养自己的学习思路。这点是最重要的。
分享自:http://t.cn/zW6pgjZ
|