第四章介紹了串口的打印函數printf 是如何調用實現的。但要使用keil自帶的微庫microLIB ,那能不能不使用這個微庫呢。我參照野火的教程,修改了程序,自己編寫usart_printf()函數來實現打印的功能。
USRT1的配置不改變,主要的就是添加打印函數實現串口輸出功能。代碼感覺可能很長,但無非就是一些判斷,看看字符串最后一位是不是\0 ,不是的話,遇到轉義字符,/n /r 怎么做,以及將數字轉換成字符這些。
這些很多時候我都沒注意:
1、 這一句while ( *Data != 0) // 判斷是否到達字符串結束符
我們平時不是都用 \0 嗎? 用0開始我還沒反應過來。 其實ASCII的十六進制的0 就是\0
如果要使用\0,while ( *Data !='\0') ,主要要加單引號表字符串,上面沒有加就是十六進制,后面的也就能明白了。
2、stdarg.h這個函數,以前都沒見過,但學習就是要學習新知識。 知識改變命運,我一直都相信這句!
3、char *itoa(int value, char *string, int radix) 是指針函數,返回值是一個地址
4、其他的都是涉及指針的操作,所以C指針一定要學好,沒學好,不要緊,趁這個機會把它弄明白,當我看懂了下面這些,也就明白了。
#ifndef __usart_debug_H
#define __usart_debug_H
#include "stm32f10x.h"
#include //stdarg.h是C語言中C標準函數庫的頭文件,目的為讓函數能夠接收可變參數。
extern void usart_debug_config(void); //提供給外部函數調用usart_debug_config()函數。
//
// 函數名:itoa
// 描述 :將整形數據轉換成字符串
// 輸入 :-radix =10 表示10進制,其他結果為0
// -value 要轉換的整形數
// -buf 轉換后的字符串
// -radix = 10
* 輸出 :無
* 返回 :無
* 調用 :被USART1_printf()調用
*
static char *itoa(int value, char *string, int radix)
{
int i, d;
int flag = 0;
char *ptr = string;
//This implementation only works for decimal numbers.
if (radix != 10)
{
*ptr = 0;
return string;
}
if (!value)
{
*ptr++ = 0x30;
*ptr = 0;
return string;
}
//if this is a negative value insert the minus sign.
if (value < 0)
{
*ptr++ = '-';
// Make the value positive.
value *= -1;
}
for (i = 10000; i > 0; i /= 10)
{
d = value / i;
if (d || flag)
{
*ptr++ = (char)(d + 0x30);
value -= (d * i);
flag = 1;
}
}
// Null terminate the string.
*ptr = 0;
return string;
} //NCL_Itoa
// 函數名:USART1_printf
//描述 :格式化輸出,類似于C庫中的printf,但這里沒有用到C庫
//輸入 :-USARTx 串口通道,這里只用到了串口1,即USART1
// -Data 要發送到串口的內容的指針
// -... 其他參數
// 輸出 :無
// 返回 :無
//調用 :外部調用
// 典型應用USART1_printf( USART1, "\r\n this is a demo \r\n" );
// USART1_printf( USART1, "\r\n %d \r\n", i );
// USART1_printf( USART1, "\r\n %s \r\n", j );
//
static void USART1_printf(USART_TypeDef* USARTx, uint8_t *Data,...)
{
const char *s;
int d;
char buf[16];
va_list ap; // va_list ap 和 va_start(ap, Data)以及后面的va_arg() 都來自 stdarg.h
va_start(ap, Data);//具體用法請參照相關資料。
while ( *Data != 0) // 判斷是否到達字符串結束符
{
if ( *Data == 0x5c ) // '\' ASCII表 0x5c就是轉義字符'\'
{
switch ( *++Data )
{
case 'r': //回車符
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx, 0x0d);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
Data ++;
break;
case 'n': //換行符
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx, 0x0a);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
Data ++;
break;
default:
Data ++;
break;
}
}
else if ( *Data == '%')
{ //
switch ( *++Data )
{
case 's': //字符串
s = va_arg(ap, const char *);
for ( ; *s; s++)
{
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx,*s);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
}
Data++;
break;
case 'd': //十進制
d = va_arg(ap, int);
itoa(d, buf, 10);
for (s = buf; *s; s++)
{
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx,*s);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
}
Data++;
break;
default:
Data++;
break;
}
} //end of else if
else {
USART_ClearFlag(USART2,USART_FLAG_TC);
USART_SendData(USARTx, *Data++);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
}
while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
}
}
#endif // __USART1_H
軟件仿真中的效果圖:
波形圖 IO口狀態 串口輸出 都在下面圖里了
