• printf的封装与实现


    1 UART通信协议

    1.1 UART通信的物理连接

        图1 UART的物理连接

    1.2 逻辑电平

    用电平表示逻辑1和逻辑0,逻辑1和逻辑0用来组织计算机层面的数据。

    1.3 电平标准

    根据通讯使用的电平标准不同,串口通讯可分为 TTL标准及 RS-232 标准。

    1.4 协议解析

    通讯双方需要约定波特率,并约定一致的数据包格式才能保证正常收发数据。

    1.4.1 波特率(bps)

    单位时间内,发送数据的位数。

    1.4.2 数据格式

    串口通信一般以起始位作为一帧数据传输的开始,以结束位表示一帧数据传输的结束。每一帧数据一般由起始位、数据位、停止位、校验位组成。

    起始位:由1个逻辑0的数据位表示;

    停止位:由 0.5、1、1.5或 2个逻辑 1的数据位表示;

    校验位(奇校验/偶校验):当为奇校验时,数据位和校验位中,逻辑1的数据位的个数为奇数个;当为偶校验时,数据位和校验位中,逻辑1的数据位的个数为偶数个。

    图2 115200,8n1; send 0b01000001

    如图2所示,115200bps,则1/115200spb,即每传输一位需要1/115200秒,数据在(1/115200)/2处采样。

    2 printf的实现

    2.1 标准库中的printf

    函数原型:

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

    返回值:

    成功返回实际输出字符数,失败返回-1;

    传入参数说明:

    format:     固定参数

    ...:       可变参数;参数的个数不确定,类型不确定;

    2.2 可变参数的实现原理

    调用子函数,函数的参数最终会以被压入栈中,被函数使用;通过格式控制符,实现对栈中传入参数的读取和使用。

    在标准库中的实现:

    typedef char* va_list;

    #define _INTSIZEOF(n)    ((sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1))       //保证4字节对齐

    #define va_start(ap, v)    (ap = (va_list)&v + _INTSIZEOF(v))               //获取第一个变参在栈中的地址

    #define va_arg(ap, t)    (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))     //获取ap所指向的数据,并把ap偏移至下一个变参的地址

    #define va_end(ap)      (ap = (va_list)0)                          //使ap指向空,避免野指针

      

    printf(format, arg1, arg2, arg3);参数在栈中的存放

    2.3.1 对_INTSIZEOF(n)分析

    栈指针总是4字节对齐的,因此使用_INTSIZEOF(n),使变量的大小是4的倍数(实际变量在栈中占据的空间)。

    #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1)),对这个宏定义有一个形象的比喻:

      比方说有一个箱子可以装4个瓶子,

      如果我有8个瓶子 ,那么我需要2个箱子;

      如果我有10个瓶子呢,我不能说我需要10除4,需要2.5个箱子吧,实际上我需要3个箱子;

      那怎么求我实际需要的箱子数呢?

       用一个容易理解的公式来求上述问题:

      设我的瓶子数为B,我需要的箱子数为C,一个箱子最多可以装A个瓶子。

    公式:C =(B+A-1)/ A (舍去余数)

      因此,((sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1))相当于C * A。

    2.3.2 对va_arg(ap, t)分析

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

    这个宏定义实现了两个功能:

    a、ap += _INTSIZEOF(t) —— 求下一个参数的指针

    b、(*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) —— 取当前参数值,这个宏定义将返回的就是当前参数值

    附录:源代码

    uart.c —— 波特率:115200

    debug_printf.c

  • 相关阅读:
    备忘:java在cmd中编译运行
    Java基础学习 —— 对象的克隆
    将博客搬至CSDN
    jenkins节点管理中没有默认没有Launch agent via Java Web Start
    postman 集成ci
    postman初级使用
    jmeter分布式压测实践及踩坑记录(linux压力机)
    jmeter监控服务器cpu、内存等
    jmeter跨线程组传递token实践
    初识jmeter
  • 原文地址:https://www.cnblogs.com/lilto/p/10920080.html
Copyright © 2020-2023  润新知