第四章介绍了串口的打印函数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口状态 串口输出 都在下面图里了
