韦东山ARM第一期作业(二)UART串口


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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
韦东山视频目录+链接整合-1 : 第1课 环境搭建及工具、概念介绍 第2课 GPIO实验 第3课 存储管理器实验 第4课 MMU实验 第5课 NAND FLASH控制器 第6课 中断控制器 第7课 系统时钟和UART实验 第8课 LCD实验 第9课第1节 u-boot分析之编译体验 第9课第2节 u-boot分析之Makefile结构分析 第9课第3节 u-boot分析之源码第1阶段 第9课第3节 u-boot分析之源码第2阶段 第9课第4节 u-boot分析之u-boot命令实现 第9课第5节 u-boot分析_uboot启动内核 第10课第1节 内核启动流程分析之编译体验 第10课第2节 内核启动流程分析之配置 第10课第3节 内核启动流程分析之Makefile 第10课第4节 内核启动流程分析之内核启动 第11课第1节 构建根文件系统之启动第1个程序 第11课第2节 构建根文件系统之init进程分析 第11课第3节 构建根文件系统之busybox 第11课第4节 构建根文件系统之构建根文件系统 第12课第1节 字符设备驱动程序之概念介绍 第12课第2.1节 字符设备驱动程序之LED驱动程序_编写编译 第12课第2.2节 字符设备驱动程序之LED驱动程序_测试改进 第12课第2.3节 字符设备驱动程序之LED驱动程序_操作LED 第12课第3节 字符设备驱动程序之查询方式的按键驱动程序 第12课第4.1节 字符设备驱动程序之中断方式的按键驱动_Linux异常处理结构 第12课第4.2节 字符设备驱动程序之中断方式的按键驱动_Linux中断处理结构 第12课第4.3节 字符设备驱动程序之中断方式的按键驱动_编写代码 第12课第5节 字符设备驱动程序之poll机制 第12课第6节 字符设备驱动程序之异步通知 第12课第7节 字符设备驱动程序之同步互斥阻塞
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值