Launchpad 移植printf和scanf,以及对超级终端交互的优化

来源:互联网 发布:vc http post json 编辑:程序博客网 时间:2024/05/02 02:57

前言

printf,scanf只负责格式化输入输出的字符,他们分别依靠getchar和putchar函数,只要实现在单片机上的getchar函数和putchar函数,并且头文件包含stdio.h即可正常使用printf函数和scanf函数。

第一步,配置UART及初始化模块

/**UART模块初始化函数*/void Uart_Init(void){  //-----开启IO口的TXD和RXD功能-----  P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD  P1SEL2 = BIT1 + BIT2;  UCA0CTL1 |= UCSWRST;  //-----设置UART时钟源为ACLK-----  UCA0CTL1 |= UCSSEL_2; // CLK = SMCLK  //-----配置波特率参数9600bps-----  UCA0BR0 = 0x41;  UCA0BR1 = 0x03;  UCA0MCTL = 0x00;   //复位及开中断  UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**  IE2 |= UCA0RXIE;                          // Enable USCI_A0 RX interrupt  __bis_SR_register(GIE); }

第二步,编写基本收发函数

/**发送一个字符/数字 八位-一个字节*参数:__Char:字符*/void Send_Char(uint8 Char){    while(!(IFG2 & UCA0TXIFG));//SBUFF为空时触发标志位执行下面语句    UCA0TXBUF=Char; }/**发送一个字符串*参数:__Str:字符*/void Send_String(uchar *Str){  uchar i=0;  while(Str[i]!='\0')  {    Send_Char(Str[i]);    i++;  }  Send_Char(Str[i]);}/**接收一个字符*/uchar get_char(void){  __bis_SR_register(LPM0_bits); //进入低功耗等待接受中断发生  return Receive_Data;   //返回接收中断返回的数据}

对于接收采用中断方式

//响应Rx中断服务#pragma vector=USCIAB0RX_VECTOR__interrupt void USCI0RX_ISR(void){  while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?  Receive_Data=UCA0RXBUF;  IFG2 &= ~UCA0RXIFG;  __bic_SR_register_on_exit(LPM0_bits);}

可以看到在接收中断中使单片机退出低功耗模式,为什么这么做?
当我们在使用scanf函数的时候我们是不是要等待输入呢?这个等待的操作在上面的基本函数get_char中实现:

/**接收一个字符*/uchar get_char(void){  __bis_SR_register(LPM0_bits); //进入低功耗等待接受中断发生  return Receive_Data;   //返回接收中断返回的数据}
  • 当程序中使用scanf函数会调用get_char这时便会进入低功耗模式等待接收中断的发生。
  • 若收到数据,中断发生,此时在中断中将接收到的数据传送给全局变量Receive_Data紧接着退出低功耗模式。
  • 此时程序回到get_char处继续执行return Receive_Data;便实现了返回接收字符的功能。

第三步,移植

对于putchar函数的重定向很简单

/**putchar 重定向*/int putchar(int ch){  if(ch=='\n')    Send_Char('\r');  //回车前添加换行符  Send_Char(ch);  return ch;}

getchar 正常基本功能来讲做到下面就可以了

int getchar(void){    return get_char();}

但是我们想要在超级终端上正常的进行交互,那输入的时候我们想要看到自己输入的字符怎么办?万一输入错一个字符想要删除怎么办?scanf函数本身并不支持删除操作,他只能顺序解析ASCII码字符串,所以为了实现退格操作需要开辟一个缓冲区,先处理键盘的输入与退格操作,等到输入完毕按回车键时,再将缓冲区的内容依次送回。

/**getchar 重定向*/int getchar(void) {    static char io_buffer[LINE_LENGTH + 2]; //Where to put chars    static char ptr; //Pointer in buffer    char c;    while (1) {        if (io_buffer[ptr]) //如果缓冲区有字符        return (io_buffer[ptr++]); //则逐个返回字符        ptr = 0; //直到发送完毕,缓冲区指针归零        while (1) //缓冲区没有字符,则等待字符输入        {            c = get_char(); //等待接收一个字符            if (c == In_EOF && !ptr) //==EOF==  Ctrl+Z             { //只有在未入其他字符时才有效                put_message(Out_EOF); //终端显示EOF符                return EOF; //返回 EOF(-1)            }            if (c == In_DELETE || c == In_BACKSP) //==退格或删除键==            {                if (ptr) //缓冲区有值                {                    ptr--; //从缓冲区移除一个字符                    put_message(Out_DELETE); //同时显示也删掉一个字符                }            } else if (c == In_SKIP) //==取消键 Ctrl+C ==            {                put_message(Out_SKIP); //终端显示跳至下一行                ptr = LINE_LENGTH + 1; //==0 结束符==                break;            } else if (c == In_EOL) //== '\r' 回车==            {                putchar(io_buffer[ptr++] = '\n'); //终端换行                io_buffer[ptr] = 0; //末尾添加结束符(NULL)                ptr = 0; //指针清空                break;            } else if (ptr < LINE_LENGTH) //== 正常字符 ==            {                if (c >=0x20) //删除 0x20以下字符                {                    //存入缓冲区                    putchar(io_buffer[ptr++] = c);                }            } else //缓冲区已满            {                putchar('\7'); //== 0x07 蜂鸣符,PC回响一声            }        }    }}

下面是常用特殊ASCII码的宏定义

//--------------------------------------------------------------------------------#define LINE_LENGTH 20          //行缓冲区大小,决定每行最多输入的字符数/*标准终端设备中,特殊ASCII码定义,请勿修改*/#define In_BACKSP 0x08           //ASCII  <--  (退格键)#define In_DELETE 0x7F           //ASCII <DEL> (DEL 键)#define In_EOL '\r'              //ASCII <CR>  (回车键)#define In_SKIP '\3'             //ASCII control-C#define In_EOF '\x1A'            //ASCII control-Z#define Out_DELETE "\x8 \x8"     //VT100 backspace and clear#define Out_SKIP "^C\n"          //^C and new line#define Out_EOF "^Z"             //^Z and return EOF//---------------------------------------------------------------------------------

第四步,配置IAR环境

至此重定向工作基本算是完成了,只需要在工程中包含stdio.h即可。
当然IAR要做一些设置:
Project–>Options–>General Options–>Library Configuration.将Library设置为CLIB
img

第五步,配置超级终端

新建连接时,端口设置选项的数据流控制选“无”。
img

其它相关设置:
这里写图片描述

这里写图片描述

完成并测试

通过以下测试代码:

#include "includes.h"int a;void main( void ){  Sys_Init();  Uart_Init();  while(1)  {    printf("give a num!\n");    scanf("%d",&a);    printf("the num is %d\n",a);  }}

测试结果如下
这里写图片描述

以上。

1 0
原创粉丝点击