历史上的今天
返回首页

历史上的今天

今天是:2025年02月13日(星期四)

正在发生

2019年02月13日 | ARM 平台printf函数定位到uart输出介绍

2019-02-13 来源:eefocus

1.1      ARM 串口输出函数uart_printf


ARM如果能使用C函数库自带的printf函数格式输出,那多方便,但是默认的printf都是定位到stdout终端,而不是串口,本文章讲述的是如何定位到ARM的串口。


1.1.1     函数主要代码


有在Mini2440开发板上验证过


//*****************main.c*******************************


#include"serial.h"


int Main()


{


unsignedint plck_val = 50000000;


unsignedint buad_val = 115200;


unsignedint ch_val = 0;


char*string="Hello,http://blog.csdn.net/wfq0624";


 uart_init();


 uart_printf("\n\r%s\n",string);             


 uart_printf("\rUse uart0\n\rParameter:PCLK is %d,buad is %d,uart_port is %d \n",plck_val,buad_val,ch_val);


 uart_printf("\r该函数不能打印浮点数!");            


 return 0;


}


//***********************serial.c**************************


#include"s3c2440.h"


#include"serial.h"


#include


#include

#define TXD0READY   (1<<2)


#defineRXD0READY   (1)


 


#definePCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz


#defineUART_CLK        PCLK        // UART0的时钟源设为PCLK


#defineUART_BAUD_RATE  115200      // 波特率


#defineUART_BRD        ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)


voiduart_init()


{ //UART初始化:端口使能、功能设定、波特率、设置数据格式


 GPHCON = (GPHCON & ~(0xfff<<4)) |(0xaaa<<4);//端口配置成uart0、uart1,uart3


 GPHUP = 0x38; //端口GPH禁止上拉


 UFCON0 = 0x0; //禁止FIFO


 UMCON0 = 0x0; //禁止AutoFlow Control


 //Normal:No parity:One stop:8-bits 中断响应 UART clock: PCLK


 ULCON0 = 0x03;     // 8N1(8个数据位,无较验,1个停止位)


 UCON0  = 0x05;     // 查询方式,UART时钟源为PCLK


 UBRDIV0 = UART_BRD; // 波特率为115200


}


 


voiduart_send_byte(char data)


{


 while (!(UTRSTAT0 & TXD0READY));


 UTXH0 = data;


}


voiduart_send_string(char *string)


{


 while(*string)


 {


  uart_send_byte(*string++);


 }


}


void uart_printf(char *fmt,...)                                //这个才是本文重点


{


 va_listap;


 charstring[256];


 va_start(ap,fmt);


 vsprintf(string,fmt,ap);


 uart_send_string(string);


 va_end(ap);


}


//***************************************************


1.1.2    uart_printf分析

这里涉及到一个重要概念,变参函数,比如C库函数int printf(char *fmt, ...),就是一个典型的变参函数。


我们即将编写的uart_printf毫无疑问,也是一个变参函数。


 


可变参数入栈顺序


在进程中,堆栈地址是从高到低分配的.当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减,一些黑客就是在堆栈中修改函数返回地址,执行自己的代码来达到执行自己插入的代码段的目的.


总之,函数在堆栈中的分布情况是:地址从高到低,依次是:函数参数列表,函数返回地址,函数执行代码段.


堆栈中,各个函数的分布情况是倒序的.即最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分.参数在堆栈中的分布情况如下:


  最后一个参数


  倒数第二个参数


  ...


  第一个参数


  函数返回地址


  函数代码段


 


va_list/ va_start/ va_arg/va_end 介绍


C语言标准库中头文件stdarg.h索引的接口包含了一组能够遍历变参数列表的宏。主要包含下面几个:


1.va_list:在函数里定义一个va_list型的变量,这个变量是指向参数的指针


2.va_start:用va_start宏初始化刚定义的va_list变量,让它指向可变参数表里面的第一个参数,


3.va_arg:每次调用时都会返回当前指针指向的变量,并将指针挪至下一个位置,参数的类型需要在这个调用的第二个参数来指定,va_arg也是根据这个参数来判断偏移的距离。


4.va_end:获取所有的参数之后,我们有必要将这个指针关掉,以免发生危险,方法是调用 va_end,置为 NULL,应该养成获取完参数表之后关闭指针的习惯。


 


void uart_printf(char *fmt,...)  //这个才是本文重点


{


 va_listap;                            //定义一个 va_list 指针来访问参数表


 charstring[256];


 va_start(ap,fmt);                  //初始化 ap,让它指向第一个变参[也就是fmt参数后面的参数]


 vsprintf(string,fmt,ap);         //将带参数的字符串按照参数列表格式化到string中


 uart_send_string(string);


 va_end(ap);                        //结束变量列表,和va_start成对使用 


}


      


可变参数在编译器中的处理


va_list ,va_start,va_arg,va_end是在stdarg.h中被定义成宏的,以VC++中stdarg.h里x86平台的宏定义摘录如


=================================================================


typedef char *  va_list;


#define_INTSIZEOF(n)           ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )


#defineva_start(ap,v)              ( ap = (va_list)&v + _INTSIZEOF(v) )


#defineva_arg(ap,t)                 ( *(t *)((ap +=_INTSIZEOF(t)) - _INTSIZEOF(t)) )


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


=================================================================


è#define _INTSIZEOF(n)             ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )


定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统


我们知道对于X86,sizeof(int)一定是4的整数倍,所以~(sizeof(int) - 1) )的值一定是右面[sizeof(n)-1]/2位为0,整个这个宏也就是保证了右面[sizeof(n)-1]/2位为0,其余位置为1,所以_INTSIZEOF(n)的值只有可能是4,8,16,......等等,实际上是实现了内存对齐。


举例:#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )


目的在于把sizeof(n)的结果变成至少是sizeof(int)的整倍数,这个一般用来在结构中实现按int的倍数对齐。 

如果sizeof(int)是4,那么,当sizeof(n)的结果在1~4之间是,_INTSIZEOF(n)的结果会是4;当sizeof(n)的结果在5~8时,_INTSIZEOF(n)的结果会是8;当sizeof(n)的结果在9~12时,_INTSIZEOF(n)的结果会是12;……总之,会是sizeof(int)的倍数。


 


è#define va_start(ap,v)              ( ap = (va_list)&v + _INTSIZEOF(v) )


ap指向参数v之后的那个参数的地址,即 ap指向第一个可变参数在堆栈的地址。


 


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


取出当前ap指针所指的值,并使ap指向下一个参数。


用va_arg返回可变的参数,va_arg的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用va_arg获取各个参数


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


清空va_list ap.


 


使用VA_LIST应该注意的问题:


   (1)因为va_start,va_arg, va_end等定义成宏,并不能智能地识别不同参数的个数和类型. 也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.这个方法存在漏洞:输入参数的类型随意性,使得参数很容易以一个不正确的类型获取一个值(譬如输入一个float,却以int型去获取他),这样做会出现莫名其妙的运行结果


   (2)另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码。


    (3)由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量,或作为函数或数组类型。

推荐阅读

史海拾趣

Eon公司的发展小趣事

随着全球对环保和可持续发展的关注日益增加,E.ON也开始加快绿色能源转型的步伐。公司加大了对可再生能源的投资力度,积极开发风电、太阳能等清洁能源项目。同时,E.ON还积极推广智能电网技术,提高能源利用效率,降低环境污染。

APM Hexseal公司的发展小趣事

在军队取得初步成功后,APM Hexseal的产品开始进入工业和商业市场。其硅胶密封套系列可与世界各地使用的绝大多数开关、断路器、编码器、电位计和其他旋转设备配合使用,得到了广泛的认可和应用。无论是航空电子、仪器仪表、通讯还是建筑、船舶、医疗等行业,APM Hexseal的产品都发挥着重要的作用。

Euroquartz公司的发展小趣事

Euroquartz Limited成立于1982年,其发展历程中,1987年的一次收购具有重要意义。这一年,Euroquartz收购了Brookes Crystals,这是一家在第二次世界大战期间形成的英国石英晶体制造商。这一收购使Euroquartz在石英晶体领域获得了重要的技术积累和市场地位,成为了英国重要的变频控制产品制造商。

粤翔(FlyWin)公司的发展小趣事
如电压比较器(如LM393),用于实时监测系统电压。
GTE Microcircuits公司的发展小趣事
复合放大器因其高性能特点,在通信、音频、医疗、测试测量等多个领域有广泛应用。例如,在通信系统中用于信号放大和处理;在音频设备中用于提高音质和音量;在医疗设备中用于生物电信号的放大等。
Harris公司的发展小趣事

二战后,随着全球经济的复苏和电气产品的爆炸性需求,Hammond迎来了前所未有的发展机遇。在1950年代和1960年代,Hammond积极扩展产品线,成为变压器、机架、机柜以及电气和电子行业外壳的领先供应商。同时,公司还进行了多次战略性收购,进一步巩固了其在北美电气OEM市场的地位,成为磁性材料的主要供应商之一。

问答坊 | AI 解惑

Error:Non-input node'txd' assigned to dedicated input是什么意思?

Error:Non-input node\'txd\' assigned to dedicated input是什么意思?…

查看全部问答>

数字对讲机

小弟是通信方面的新手 最近要做一个数字对讲机方面的课题 设计一个短信收发的应用层软件   想请教各位高手 有没有做过或者了解这方面的 给我一点资料或者指导   不甚感激…

查看全部问答>

SDIO接口输出用几条线呀?

背景:想把SMD解密卡嵌到PCB板上,此SMD的接口是SDIO方式的。 问题:SDIO接口一般使用几条线呀,都输出什么信号。 例如: SPI接口一般使用4条线:串行时钟线(SCK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOSI和低电平有效的从 ...…

查看全部问答>

软件开发与软件工程联盟群成员列表

软件开发与软件工程联盟群成员列表 本帖被 supersoft 从 软件开发与软件工程社区管理版块 移动到本区(2007-03-08) 联盟群宗旨:建设合谐型软件开发与工程的社区-程序员的社区 社区主页:http://supersoft.ttsite.com 计算机专业书讯交流(计算机 ...…

查看全部问答>

谁知道74LV04DB,118这个型号的相关资料数据啊~!

谁知道74LV04DB,118这个型号的相关资料数据啊~!…

查看全部问答>

怎样在Linux里面通过C语言程序了,输出嘟嘟的报警声???

在做一个网络传输的项目,功能是在收到网络消息的时候,在屏幕上面打印出消息的同时,可以听到报警提示的声音。大家有什么好的方法吗??…

查看全部问答>

dm9000a数据发送问题

我让dm9000a发送的数据用网络捕捉工具一看,发现数据顺序都乱了,但数据是对的,应该是tx缓冲区指针乱了,谁知道原因呀?比如我发送:0xff,0xff,0xff,0xff,0xff,0xff,0x12,0x34,0x56,0x78,0x9a,0xbc,0x08,0x06,0x00,0x01,捕捉结果有可能是0x12,0x34 ...…

查看全部问答>

Raw event 篇

Event 整体来说是API是比较难以理解的,通常需要实践加上原理一起理解,最好是阅读相应代码。但是event 相当强大,一个event 有32个事件,对于任务同步控制是非常强大的。 1 RAW_U16 raw_event_create(RAW_EVENT *event_ptr, RAW_U8 *name_ptr, RA ...…

查看全部问答>

多级放大电路的动态分析

1、多级放大器的级间关系:在多级放大器中,后级电路相当于前级的负载,前级负载是后级放大器的输入电阻;前级相当后级的信号源,后级信号源内阻为前级的输出电阻。2、n级放大器的动态指标a、总电压放大倍数 :可见, n级放大器的总电压放大倍数 ...…

查看全部问答>

竞赛,单片机(处理器)怎么选取??

本帖最后由 paulhyde 于 2014-9-15 04:14 编辑 竞赛,单片机(处理器)怎么选取?是带队老师建议?是芯片厂商推荐?还是自己一个个测试? 当然,2013年全国大学生电子设计竞赛竞赛题目及要求中有以下说明: 竞赛题目包括 ...…

查看全部问答>