01 - 作业所在路径
ARM裸机1期加强版\源码文档图片\文档图片\第011课_串口(UART)的使用
02 - 作业描述
2.1 - 作业1
putchar
在没有读到数据时会一直死等,实现一个putchar_nowait
函数, 有串口数据就读出来,没有串口数据就立刻返回错误
2.2 - 作业2
printf
函数是把输出信息直接从串口输出了,有时候我们要得到这些信息,比如后面的LCD程序中,想在LCD上显示这些信息时,就要先得到这些信息再一次性的输出到LCD。实现这个函数:int snprintf(char *str, size_t size, const char *format, …);
它会把输出信息先保存在str里。(提示: printf
函数中最终使用__out_putchar
进行打印,你也许可以修改它,把字符保存在str中。)
03 - 作业解答
3.1 - 作业1解答
putchar在没有读到数据时会一直死等,实现一个putchar_nowait()函数, 有串口数据就读出来,没有串口数据就立刻返回错误
添加一个时间变量,在while
中一直递减就可以粗略实现(UART_RREADY_WIDTH和UART_TEMPTY_BIT是自己写的宏,和老师的数值是一样的)
#修改前,阻塞等待
int putchar(int c)
{
while(!(UTRSTAT0&(UART_TEMPTY_WIDTH<<UART_TEMPTY_BIT)));
UTXH0 = (unsigned char)c;
return 0;
}
#修改后,等待一小段时间
int putchar_nowait(void)
{
unsigned int time = 1000000;
unsigned char val;
//在time时间下读数据
while(!(UTRSTAT0&(UART_RREADY_WIDTH<<UART_RREADY_BIT)) && --time);
if(time)
{
val = URXH0;
while(!(UTRSTAT0&(UART_TEMPTY_WIDTH<<UART_TEMPTY_BIT)));
UTXH0 = (unsigned char)val;
return 1;
}
else
return 0;
}
3.2 - 作业2解答
printf函数是把输出信息直接从串口输出了,有时候我们要得到这些信息,比如后面的LCD程序中,想在LCD上显示这些信息时,就要先得到这些信息再一次性的输出到LCD。实现这个函数:int snprintf(char *str, size_t size, const char *format, …);它会把输出信息先保存在str里。(提示: printf函数中最终使用__out_putchar进行打印,你也许可以修改它,把字符保存在str中。)
基本思想是原本输出到串口的内容都输出到str
,其实改动比较大,几乎所有函数都要加上一个char* str
的参数,修改最大的是my_vprintf
函数,在格式化输出的时候需要统计转换到的字符数,改变str
指针位置,同时要检查是否超出长度指定的长度,需要做截断处理,代码太长,只贴出修改后的代码
uart_printf.h
#ifndef _UART_PRINTF_H_
#define _UART_PRINTF_H_
//#define __out_putchar putchar
#define MAX_NUMBER_BYTES 64
typedef char * va_list;
//保证4字节对齐
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
//移动指针
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//取值
#define va_arg(ap,t) ( *(t *)( ap=ap + _INTSIZEOF(t), ap- _INTSIZEOF(t)) )
//赋值,避免野指针
#define va_end(ap) ( ap = (va_list)0 )
int printf(const char *format, ...);
int snprintf(char *str, int size, const char *format, ...);
void printf_test(void);
#endif
uart_printf.c
#include "uart_printf.h"
#include "uart.h"
unsigned char hex_tab[]= {'0','1','2','3','4','5','6','7',\
'8','9','a','b','c','d','e','f'
};
/**
* @brief 把一个字符输出到str
* @param c:待输出的字符
* @param str:待输出的缓冲区
* @retval 返回0
*/
void __out_putchar(char *str,unsigned char c)
{
*str = c;
return ;
}
/**
* @brief 输出一个字符到str中
* @param c:待输出的字符
* @param str:待输出的缓冲区
* @retval 返回0
*/
static int outc(char *str,int c)
{
__out_putchar(str,c);
return 0;
}
/**
* @brief 输出一个字符串到str中
* @param s:待输出的字符串
* @retval 返回输出的字符数
*/
static int outs(char *str, const char *s)
{
int ret = 0;
while (*s != '\0')
{
__out_putchar(str++,*s++);
ret++;
}
return ret;
}
/**
* @brief 利用串口输出一个数字
* @param n :待输出的数字
* @param base :代表进制,8进制/10进制/16进制
* @param lead :前导码
* @param maxwidth:在一行中输出的最大字符数
* @retval 返回输出的字符数
*/
static int out_num(char *str,long n, int base,char lead,int maxwidth)
{
unsigned long m=0;
char buf[MAX_NUMBER_BYTES];
char *s = buf + sizeof(buf);
int count=0,i=0;
*--s = '\0';
if (n < 0)
{
m = -n;
}
else
{
m = n;
}
do
{
*--s = hex_tab[m%base];
count++;
}
while ((m /= base) != 0);
if( maxwidth && count < maxwidth)
{
for (i=maxwidth - count; i; i--)
*--s = lead;
}
if (n < 0)
*--s = '-';
return outs(str,s);
}
/**
* @brief 在ap中按照fmt格式输出size长度的一个字符串到str中
* @param fmt :待输出字符串的格式字符串
* @param ap :待输出的数据缓冲区头指针
* @param size :共输出的字节数
* @param str :输出的缓冲区
* @retval 返回0
*/
static int my_vprintf(char *str,int size,const char *fmt, va_list ap)
{
char lead=' ';
int maxwidth=0;
int s = 0;
for(; *fmt != '\0'; fmt++)
{
if (*fmt != '%')
{
if(++s <= size)
{
outc(str++,*fmt);
continue;
}
else
break;
}
//format : %08d, %8d,%d,%u,%x,%f,%c,%s
fmt++;
if(*fmt == '0')
{
lead = '0';
fmt++;
}
lead=' ';
maxwidth=0;
while(*fmt >= '0' && *fmt <= '9')
{
maxwidth *=10;
maxwidth += (*fmt - '0');
fmt++;
}
int len;
switch (*fmt)
{
case 'd':
len = out_num(str,va_arg(ap, int), 10,lead,maxwidth);
break;
case 'o':
len = out_num(str,va_arg(ap, unsigned int), 8,lead,maxwidth);
break;
case 'u':
len = out_num(str,va_arg(ap, unsigned int), 10,lead,maxwidth);
break;
case 'x':
len = out_num(str,va_arg(ap, unsigned int), 16,lead,maxwidth);
break;
case 'c':
if(++s <= size)
outc(str++,va_arg(ap, int ));
break;
case 's':
len = outs(str,va_arg(ap, char *));
break;
default:
if(++s <= size)
outc(str++,*fmt);
break;
}
//检查输入的字符串是否超出长度
if(len<= (size-s))
{
str+=len;
s+=len;
}
else
{
*(str+size) = '\0';
s = size+1;
}
}
return 0;
}
/**
* @brief 模仿sprint函数,按照format格式,把数据输入到str中
* @param str :缓存
* @param size :缓存的字节数
* @param format :数据格式
* @param ...); :0个或者多个参数
* @retval 返回输出的字符数
*/
int snprintf(char *str, int size, const char *format, ...)
{
va_list ap;
//得到数据缓冲区首地址
va_start(ap, format);
//根据格式+数据进行输出到str
my_vprintf(str, size, format, ap);
//指针赋值,避免野指针
va_end(ap);
return 0;
}
04 - 作业源码分享
百度网盘 提取码:wkcp