前言
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
第五步,配置超級終端
新建連接時,端口設置選項的數據流控制選“無”。
其它相關設置:
完成並測試
通過以下測試代碼:
#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);
}
}
測試結果如下
以上。