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

前言

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
Launchpad 移植printf和scanf,以及对超级终端交互的优化_第1张图片

第五步,配置超级终端

新建连接时,端口设置选项的数据流控制选“无”。
Launchpad 移植printf和scanf,以及对超级终端交互的优化_第2张图片

其它相关设置:
Launchpad 移植printf和scanf,以及对超级终端交互的优化_第3张图片

Launchpad 移植printf和scanf,以及对超级终端交互的优化_第4张图片

完成并测试

通过以下测试代码:

#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);
  }
}

测试结果如下

以上。

你可能感兴趣的:(printf,格式化,终端,移植)