当前位置:首页 > 芯闻号 > 充电吧
[导读]在嵌入式开发中,常常会通过串口打印一些信息到PC终端,这就需要实现自己的printf函数。我们先通过man 3 printf来看一下printf函数的声明: int printf(const char

在嵌入式开发中,常常会通过串口打印一些信息到PC终端,这就需要实现自己的printf函数。

我们先通过man 3 printf来看一下printf函数的声明:
int printf(const char *format, ...);
format:固定参数
…   :可变参数(变参) 下面解析printf函数的使用:

x86平台VC6.0编译器中,stdarg.h头文件内变参宏定义如下,本次实现也printf函数也采用此定义:

变参宏根据堆栈生长方向和参数入栈特点,从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。

typedef char * va_list;

#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, type)    ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )

#define va_end(ap)             ( ap = (va_list)0 )

各宏的含义如下:
_INTSIZEOF宏考虑到某些系统需要内存地址对齐。从宏名看应按照sizeof(int)即堆栈粒度对齐,即参数在内存中的地址均为sizeof(int)=4的倍数。例如,若在1≤sizeof(n)≤4,则_INTSIZEOF(n)=4;若5≤sizeof(n)≤8,则_INTSIZEOF(n)=8。
为便于理解,简化该宏为:

#define _INTSIZEOF(n)  ((sizeof(n) + x) & ~(x))

x = sizeof(int) - 1 = 3 = 0b’0000 0000 0000 0011

~x = 0b’1111 1111 1111 1100

一个数与(~x)相与的结果是sizeof(int)的倍数,即_INTSIZEOF(n)将n圆整为sizeof(int)的倍数。

② va_start宏根据(va_list)&v得到第一个可变参数前的一个固定参数在堆栈中的内存地址,加上_INTSIZEOF(v)即v所占内存大小后,使ap指向固定参数后下个参数(第一个可变参数地址)。
固定参数的地址用于va_start宏,因此不能声明为寄存器变量(地址无效)或作为数组类型(长度难定)。
③va_arg宏取得type类型的可变参数值。首先ap+=_INTSIZEOF(type),即ap跳过当前可变参数而指向下个变参的地址;然后ap-_INTSIZEOF(type)得到当前变参的内存地址,类型转换后返回当前变参值。
va_arg宏的等效实现如下:

#define va_arg(ap,t)    (ap = ap + _INTSIZEOF(t), *(t *)(ap - _INTSIZEOF(t)))

④va_end宏使ap指针指向空,防止野指针

代码下载地址:https://gitee.com/zgdy/0_printf

my_printf.c函数的实现在注释中已经很清晰了。

一、在PC端来测试:


①:pc机中有操作系统所以测试时,my_printf.h中使用stdio.h中的putchar函数

#ifndef _MY_PRINTF_H
#define _MY_PRINTF_H

//#include "uart.h"
#include "stdio.h"
#define  __out_putchar  putchar


#define  MAX_NUMBER_BYTES  64

extern int my_printf_test(void);
int printf(const char *fmt, ...);

#endif /* _MY_PRINTF_H */

② main.c:

#include "my_printf.h"

int main(int argc,char **argv)
{
    printf("CZG~n");
    my_printf_test();
    return 0;

}

结果:

二、在JZ2440上面来测试:

关于为什么要加lib1funcs.S文件说明:
 http://blog.csdn.net/czg13548930186/article/details/78797744

①由于ARM裸机中没有操作系统环境,所以要自己实现putchar函数(并在uart.h中声明):

② 将lib1funcs.S和my_printf.c放在Makefile中一同编译进去:

结果:

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

在实际项目中,我们经常需要提取一个数值的某些位的数码,比如用数码管来显示数值或将一个数值转成字符串,都会涉及到这一操作。

关键字: 数值 数码 printf

最近在忙活搞别的事情(太难受了),严重影响了硪那一颗自由飞翔的芯~~所以今天打算分享一个麻省理工小伙写的printf家族的函数~说是号称目前网上嵌入式最好的printf喔.在嵌入式中printf这种功能强大的函数可谓是c...

关键字: printf 嵌入式

大伙估计在多任务程序中使用printf打印一些信息是非常欢乐的一件事,运气不错的话偶尔错几个数据、乱几个码也不是什么大问题,倒霉点的可能就直接挂机、卡死了,那这些到底是什么原因导致的呢?

关键字: printf 可重入函数

SWO串行线输出是单引脚、异步串行通信,可在Cortex-M3/M4/M7上使用,并由主调试器探测支持,它是利用Cortex内核中ITM模块来实现此功能。

关键字: printf 嵌入式

目的:在串口0上实现printf、scanf等函数,它使用scanf、sscanf、printf等函数从串口接收一个十进制数字序列,然后将它转化为十六进制输出。 大致内容和uart实验类

关键字: printf 函数

使用IAR驱动CC2530的串口0,串口1,实现数据发送以及printf,中断接收数据uart.c/**************************************************

关键字: printf 串口

一、什么是可变参数我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为: int printf( const char* format, ...); 它除了有一个

关键字: C语言 printf 编程

USART1需要事先进行较为麻烦配置,配置之后发现即使用HAL函数发送语句还要事先以字符串方式定义,实在是麻烦,虽然后面另外自己写了一个简单的库来操作串口,但看到了更简单的方法,修改标准库中printf相关的两个函数

关键字: printf STM32 串口通信 usart打印

先说明一下,开发平台win7,工具RVMDK(keil),硬件stm32f103ve,打印到超级终端前两天开始关注一下一直被搁在一边的printf。。。其实应该有一个月前就有看了一下,调用C语言官方库,实现可变参数pri...

关键字: C语言 printf STM32 可变参数

首先,要包含头文件"stdio.h"第二:printf()函数使用了int fputc(int ch,FILE *f)完成其功能。要使用printf,就需要重写这个函数。第三,在工程选项的Target...

关键字: printf STM32 mdk工程
关闭
关闭