历史上的今天
返回首页

历史上的今天

今天是:2024年10月13日(星期日)

正在发生

2021年10月13日 | S3C2440裸机------从零实现用于裸机调试的printf函数

2021-10-13 来源:eefocus

我们的C语言中有标准的printf,可以很方便的打印一些变量的值用于调试,在嵌入式开发中,我们通过串口实现我们自己的printf函数,将一些变量值通过串口打印出来。


1.printf函数介绍

首先看一下C语言中的printf函数中的格式字符,我们就是要实现下表中的几个格式字符。

然后看一下printf函数的声明,int  printf(const char * format, ...);


format:  表示固定参数,


...:   表示可变参数


2.手动确定可变参数

要实现printf函数,首先我们要知道怎么取到format和...所接受到的参数,我们取参数的依据就是:x86平台,函数调用时参数传递是使用堆栈来实现的,我们的printf的第一个参数是format,然后第二个参数的地址就是&format + sizeof(char *),具体原理如下 。

代码如下:


/*

 *  push_test.c  V1.0 

 *  Copyright (c) 2017 Shenzhen 100ask Technology Co.Ltd.All rights reserved.

 *  http://www.100ask.org

 *  100ask.taobao.com

 *

 *  测试平台:   ubuntu16.04(64位机器)   

 * ubuntu9.10 (32位机器)   

 *  编译器  :   gcc

 */

 

#include

 

struct  person{

char *name;

int  age;

char score;

int  id;

};

/* 

 *int printf(const char *format, ...); 

 *依据:x86平台,函数调用时参数传递是使用堆栈来实现的 

 *目的:将所有传入的参数全部打印出来 

 */ 

int push_test(const char *format, ...)

{

char *p = (char *)&format;

int i;

struct  person per;  

char c;

double d;

printf("arg1 : %sn",format);  

 

    //==============

/*指针对连续空间操作时: 1) 取值  2)移动指针*/  

p = p + sizeof(char *);

i = *((int *)p);

printf("arg2 : %dn",i);   

 

    //==============             

/*指针对连续空间操作时: 1) 取值  2)移动指针*/    

p = p + sizeof(int);  

per = *((struct  person *)p);  

printf("arg3: .name = %s, .age = %d, .socre=%c  .id=%dn",

          per.name,   per.age,   per.score, per.id);   

 

    //==============    

/*指针对连续空间操作时: 1) 取值  2)移动指针*/

p = p + sizeof(struct person);

c = *((char *)p);

printf("arg4: %cn",c);

 

    //==============    

/*指针对连续空间操作时: 1) 取值  2)移动指针*/

p = p + ((sizeof(char) + 3) & ~3);

d = *((double *)p);

printf("arg5: %fn",d);

return 0;

}

 

int main(int argc,char **argv)

{

struct  person per={"www.100ask.org",10,'A',123};

printf("sizeof(char   )=%dn",sizeof(char   ));

printf("sizeof(int    )=%dn",sizeof(int    ));

printf("sizeof(char  *)=%dn",sizeof(char  *));

printf("sizeof(char **)=%dn",sizeof(char **));

printf("sizeof(struct  person)=%dn",sizeof(struct  person));

    //push_test("abcd");

    //push_test("abcd",123);  

    //push_test("abcd",123,per); 

    //push_test("abcd",123,per,'c');   

    push_test("abcd",123,per,'c',2.79);

return 0;

}

 

 

3.自动确定可变参数

上面在修改指针的值以及取值是手动操作的,其实我们可以通过使用宏定义将修改指针的值和取值做成一个循环。

代码如下:


/*

 *  push_test.c  V1.0 

 *  Copyright (c) 2017 Shenzhen 100ask Technology Co.Ltd.All rights reserved.

 *  http://www.100ask.org

 *  100ask.taobao.com

 *

 *  测试平台:   ubuntu16.04(64位机器)  gcc -m32 -o push_test  push_test.c 

 * ubuntu9.10 (32位机器)  gcc      -o push_test  push_test.c 

 *  编译器  :   gcc

 */

 

#include

//#include

 

//下面这几个关于va的宏定义可以在stdarg.h中找到。

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

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

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

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

 

struct  person{

char *name;

int  age;

char score;

int  id;

};

/* 

 *int printf(const char *format, ...); 

 *依据:x86平台,函数调用时参数传递是使用堆栈来实现的 

 *目的:将所有传入的参数全部打印出来 

 */ 

int push_test(const char *format, ...)

{

//char *p = (char *)&format;

int i;

struct  person per;  

char c;

double d;

va_list p;

printf("arg1 : %sn",format);  

 

    //==============

//p = p + sizeof(char *);

va_start(p, format );   

/*指针对连续空间操作时: 1) 取值  2)移动指针*/  

//i = *((int *)p);

//p = p + sizeof(int);

i = va_arg(p,int);

printf("arg2 : %dn",i);   

 

    //==============             

/*指针对连续空间操作时: 1) 取值  2)移动指针*/    

  //per = *((struct  person *)p); 

//p = p + sizeof(struct person);

per = va_arg(p,struct person);

printf("arg3: .name = %s, .age = %d, .socre=%c  .id=%dn",

          per.name,   per.age,   per.score, per.id);   

 

    //==============    

/*指针对连续空间操作时: 1) 取值  2)移动指针*/

//c = *((char *)p);

//p = p + ((sizeof(char) + 3) & ~3);

c = va_arg(p,int);

printf("arg4: %cn",c);

 

    //==============    

/*指针对连续空间操作时: 1) 取值  2)移动指针*/

//d = *((double *)p);

//p = p + sizeof(double);

d = va_arg(p,double);

/*避免"野指针"*/

//p = (char *)0;

va_end( p ); 

printf("arg5: %fn",d);

return 0;

}

 

int main(int argc,char **argv)

{

struct  person per={"www.100ask.org",10,'A',123};

printf("sizeof(char   )=%dn",sizeof(char   ));

printf("sizeof(int    )=%dn",sizeof(int    ));

printf("sizeof(char  *)=%dn",sizeof(char  *));

printf("sizeof(char **)=%dn",sizeof(char **));

printf("sizeof(struct  person)=%dn",sizeof(struct  person));

    //push_test("abcd");

    //push_test("abcd",123);  

    //push_test("abcd",123,per); 

    //push_test("abcd",123,per,'c');   

    push_test("abcd",123,per,'c',2.79);

return 0;

}

 

 

4.printf函数在x86平台的实现

直接看代码::


my_printf.h

 

#ifndef _MY_PRINTF_H

#define _MY_PRINTF_H

 

//#include "uart.h"

#include

#define  __out_putchar  putchar

 

 

#define  MAX_NUMBER_BYTES  64

 

extern int my_printf_test(void);

int printf(const char *fmt, ...);

 

#endif /* _MY_PRINTF_H */

 

my_printf.c

 

#include  "my_printf.h"

 

 

//==================================================================================================

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

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

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

 

//==================================================================================================

unsigned char hex_tab[]={'0','1','2','3','4','5','6','7',

                 '8','9','a','b','c','d','e','f'};

 

static int outc(int c) 

{

__out_putchar(c);

return 0;

}

 

static int outs (const char *s)

{

while (*s != '')

__out_putchar(*s++);

return 0;

}

 

static int out_num(long n, int base,char lead,int maxwidth) 

{

unsigned long m=0;

char buf[MAX_NUMBER_BYTES], *s = buf + sizeof(buf);

int count=0,i=0;

 

*--s = '';

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(s);

}

   

 

/*reference :   int vprintf(const char *format, va_list ap); */

static int my_vprintf(const char *fmt, va_list ap) 

{

char lead=' ';

int  maxwidth=0;

for(; *fmt != ''; fmt++)

{

if (*fmt != '%') {

outc(*fmt);

continue;

}

//format : %08d, %8d,%d,%u,%x,%f,%c,%s 

    fmt++;

if(*fmt == '0'){

lead = '0';

fmt++;

}

while(*fmt >= '0' && *fmt <= '9'){

maxwidth *=10;

maxwidth += (*fmt - '0');

fmt++;

}

switch (*fmt) {

case 'd': out_num(va_arg(ap, int),          10,lead,maxwidth); break;

case 'o': out_num(va_arg(ap, unsigned int),  8,lead,maxwidth); break;

case 'u': out_num(va_arg(ap, unsigned int), 10,lead,maxwidth); break;

case 'x': out_num(va_arg(ap, unsigned int), 16,lead,maxwidth); break;

case 'c': outc(va_arg(ap, int   )); break;

case 's': outs(va_arg(ap, char *)); break;  

default:  

outc(*fmt);

break;

}

}

return 0;

}

 

 

//reference :  int printf(const char *format, ...); 

int printf(const char *fmt, ...) 

{

va_list ap;

 

va_start(ap, fmt);

my_vprintf(fmt, ap);

va_end(ap);

return 0;

}

 

 

int my_printf_test(void)

{

printf("This is www.100ask.org   my_printf testnr") ;

printf("test char           =%c,%cnr", 'A','a') ;

printf("test decimal number =%dnr",    123456) ;

printf("test decimal number =%dnr",    -123456) ;

推荐阅读

史海拾趣

COTO TECHNOLOGY公司的发展小趣事

随着电子行业的不断发展,小型化、集成化成为产品设计的趋势。COTO TECHNOLOGY紧跟时代潮流,于1983年成功研发出史上首款表面贴装干簧继电器。这一技术的突破不仅提高了产品的生产效率,更使得COTO的产品在小型化方面走在了行业前列。这一创新不仅增强了COTO在市场上的竞争力,也进一步巩固了其在干簧继电器领域的领导地位。

固驰(GUERTE)公司的发展小趣事

近年来,固驰电子积极响应时代趋势和市场变化,不断加大技术创新力度。同时,作为REFLEK技术公司(又称Reflek恒昼科技)旗下的品牌,固驰(FlexiShield)还启动了品牌重塑计划,旨在以全新面貌引领行业未来发展方向。Reflek恒昼科技在金属隔热原膜及磁控膜领域拥有深厚的技术积累,其打造的FlexiShield固驰窗膜和漆面保护膜品牌迅速在市场上占据一席之地,进一步丰富了固驰电子的产品线。

Hartmann Codier GmbH公司的发展小趣事

近年来,固驰电子积极响应时代趋势和市场变化,不断加大技术创新力度。同时,作为REFLEK技术公司(又称Reflek恒昼科技)旗下的品牌,固驰(FlexiShield)还启动了品牌重塑计划,旨在以全新面貌引领行业未来发展方向。Reflek恒昼科技在金属隔热原膜及磁控膜领域拥有深厚的技术积累,其打造的FlexiShield固驰窗膜和漆面保护膜品牌迅速在市场上占据一席之地,进一步丰富了固驰电子的产品线。

FUJI公司的发展小趣事

近年来,固驰电子积极响应时代趋势和市场变化,不断加大技术创新力度。同时,作为REFLEK技术公司(又称Reflek恒昼科技)旗下的品牌,固驰(FlexiShield)还启动了品牌重塑计划,旨在以全新面貌引领行业未来发展方向。Reflek恒昼科技在金属隔热原膜及磁控膜领域拥有深厚的技术积累,其打造的FlexiShield固驰窗膜和漆面保护膜品牌迅速在市场上占据一席之地,进一步丰富了固驰电子的产品线。

Aptiv公司的发展小趣事

Aptiv公司在电子行业中以其领先的技术实力著称。早在1995年,公司便成立了自动驾驶研究实验室,开始探索自动驾驶技术的可能性。经过多年的研发与试验,Aptiv在自动驾驶领域取得了显著的突破。2004年,公司首次在内华达州成功测试了自动驾驶汽车,获得了州政府的批准,这标志着Aptiv在自动驾驶技术上迈出了坚实的步伐。此后,Aptiv不断推动自动驾驶技术的商业化进程,为全球汽车行业带来了新的发展机遇。

EKIT公司的发展小趣事

2023年10月,华为坤灵(HUAWEI eKit)在德国慕尼黑成功举办了分销品牌发布会,这是其在欧洲市场的首个国家级发布活动。通过简化交易流程、打造高效的平台和工具、整合伙伴体系等措施,HUAWEI eKit为德国及欧洲市场的中小企业提供了灵活易用的产品解决方案。同时,HUAWEI eKit还致力于为中小企业市场的各种业务场景开发易买易卖、易装易维、易学易用的分销产品,赢得了市场的广泛认可。

问答坊 | AI 解惑

ARM_S3C2440的Camera摄像头问题

我的视频源是CABS,分辨率为720*288,通过SAA7113进行转换,然后接到S3C2440的Camera接口上,液晶屏的分辨率为800*480,经过调试后可以在液晶上全屏显示,但是有点小问题,就是液晶的最下面一行总是显示蓝色,并且带有闪烁现象。 各参数设置如下: # ...…

查看全部问答>

谁有tasking C166啊,给兄弟一个,急需啊,多谢!

谁有tasking C166啊,给兄弟一个,急需啊,多谢! luckytigerwood@yahoo.com.cn…

查看全部问答>

吃内存的问题!

在OnTimer中实现如下代码: CString sTm; CTime time; time = CTime::GetCurrentTime(); sTm.Format(_T(\"%d年%d月%d日 %02d:%02d\"),time.GetYear(),time.GetMonth(),time.GetDay(),time.GetHour(),time.GetMinute()); m_ctrlTime.SetWindowT ...…

查看全部问答>

求TC35外围电路连接图及收发短信源代码 !!

求TC35外围电路连接图及收发短信源代码 !! 各位大虾帮帮忙吧,刚接触这东西,急需这些来熟悉熟悉,买了个TC35模块,想自己焊个板子玩下。 如果有的话,麻烦发我邮箱forjobforlife@163.com 谢谢了~…

查看全部问答>

我给大家特别推荐的两款产品,非常棒!!!

1. c8051f 单片机(c2 ,jtag)和at89s5x单片机(isp)二合一串口编程下载线,支持3伏(c8051f 单片机),5伏(at89s5x单片机)电源, 目前通过实际验证可编程下载的芯片:c8051f310、c8051f320、c8051f330D、c8051f340、c8051f350、c8051f360、c805 ...…

查看全部问答>

wince(VS2005,c#) 控件透明怎么实现

wince(VS2005,C#)控件透明怎么实现 我很菜,请说详细点…

查看全部问答>

中断读书笔记(希望大家看了能给补充一下)

刚开完视屏教程,来整理一下自己对51单片机中断的理解,从网上找来一些了资料加以整理,希望能够给更多和我一样的初学者带来便利。 [ 本帖最后由 safe360 于 2011-3-15 09:53 编辑 ]…

查看全部问答>

今天把iar4.42的程序移植到iar5.2,多了个提示

                                 如附图所示,认识上面的英语但是不知道意思,谁来解释一下…

查看全部问答>

开发了一个新的项目贸然使用了STM8

08年开发了一个新的项目贸然使用了STM8,由于全是新的方案和技术,采用了STM8作为MCU器件,虽然当时包括供应商、代理商等的FAE服务及时,但是,大家对这可芯片应用的都不是太熟悉,理论的东西大家都懂会操作。但是遇到了一些问题都很茫然,后 ...…

查看全部问答>

对dim3517板子的遗憾

AM3517是基于armv7架构的高性能、低功耗的mpu,该架构的mpu在现在很多高新的消费电子产品中得以应用和发展,所以该mpu对于要在该领域的开发者来说充满着诱惑,我何尝不是如此对于消费电子,客户群是普通大众,其实他们不注重你用了什么mpu、什么架 ...…

查看全部问答>