第四章介紹了串口的打印函數(shù)printf 是如何調(diào)用實(shí)現(xiàn)的。但要使用keil自帶的微庫(kù)microLIB ,那能不能不使用這個(gè)微庫(kù)呢。我參照野火的教程,修改了程序,自己編寫usart_printf()函數(shù)來實(shí)現(xiàn)打印的功能。
USRT1的配置不改變,主要的就是添加打印函數(shù)實(shí)現(xiàn)串口輸出功能。代碼感覺可能很長(zhǎng),但無非就是一些判斷,看看字符串最后一位是不是\0 ,不是的話,遇到轉(zhuǎn)義字符,/n /r 怎么做,以及將數(shù)字轉(zhuǎn)換成字符這些。
這些很多時(shí)候我都沒注意:
1、 這一句while ( *Data != 0) // 判斷是否到達(dá)字符串結(jié)束符
我們平時(shí)不是都用 \0 嗎? 用0開始我還沒反應(yīng)過來。 其實(shí)ASCII的十六進(jìn)制的0 就是\0
如果要使用\0,while ( *Data !='\0') ,主要要加單引號(hào)表字符串,上面沒有加就是十六進(jìn)制,后面的也就能明白了。
2、stdarg.h這個(gè)函數(shù),以前都沒見過,但學(xué)習(xí)就是要學(xué)習(xí)新知識(shí)。 知識(shí)改變命運(yùn),我一直都相信這句!
3、char *itoa(int value, char *string, int radix) 是指針函數(shù),返回值是一個(gè)地址
4、其他的都是涉及指針的操作,所以C指針一定要學(xué)好,沒學(xué)好,不要緊,趁這個(gè)機(jī)會(huì)把它弄明白,當(dāng)我看懂了下面這些,也就明白了。
#ifndef __usart_debug_H
#define __usart_debug_H
#include "stm32f10x.h"
#include //stdarg.h是C語(yǔ)言中C標(biāo)準(zhǔn)函數(shù)庫(kù)的頭文件,目的為讓函數(shù)能夠接收可變參數(shù)。
extern void usart_debug_config(void); //提供給外部函數(shù)調(diào)用usart_debug_config()函數(shù)。
//
// 函數(shù)名:itoa
// 描述 :將整形數(shù)據(jù)轉(zhuǎn)換成字符串
// 輸入 :-radix =10 表示10進(jìn)制,其他結(jié)果為0
// -value 要轉(zhuǎn)換的整形數(shù)
// -buf 轉(zhuǎn)換后的字符串
// -radix = 10
* 輸出 :無
* 返回 :無
* 調(diào)用 :被USART1_printf()調(diào)用
*
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
// 函數(shù)名:USART1_printf
//描述 :格式化輸出,類似于C庫(kù)中的printf,但這里沒有用到C庫(kù)
//輸入 :-USARTx 串口通道,這里只用到了串口1,即USART1
// -Data 要發(fā)送到串口的內(nèi)容的指針
// -... 其他參數(shù)
// 輸出 :無
// 返回 :無
//調(diào)用 :外部調(diào)用
// 典型應(yīng)用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);//具體用法請(qǐng)參照相關(guān)資料。
while ( *Data != 0) // 判斷是否到達(dá)字符串結(jié)束符
{
if ( *Data == 0x5c ) // '\' ASCII表 0x5c就是轉(zhuǎn)義字符'\'
{
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': //十進(jìn)制
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口狀態(tài) 串口輸出 都在下面圖里了
