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

測試結果如下
這裏寫圖片描述

以上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章