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

printf的封装与实现的更多相关文章

  1. 由endl对printf和cout的思考

    [前言]二者的区别就不介绍了.二者使用方法: printf("%s",a); cout<<a<<endl; endl的作用是什么? 一.endl作用 众所周 ...

  2. 计算机网络中的帧封装(C实现)

    这段时间开始复习计算机网络,看到帧封装这一节,结合以前的课程设计,就用C写了个帧封装的程序,说实话C学的确实不怎么样,实现的时候对于文件操作那部分查了好多资料,下面说说帧封装是啥情况. 学过计算机网络 ...

  3. va_list可变参数

    可变参数函数实现 va_list,va_start,va_arg,va_end va可变参数意思,variable-argument. 1. 头文件及实现 linux中定义在gcc头文件中,stdar ...

  4. 写一个函数封装printf用作trace

    转自http://blog.csdn.net/coder_weisong/article/details/10285291     写一个函数封装printf用作trace   方法一:   #inc ...

  5. 1.import和include区别 2.NSLog 和printf区别 3.创建对象做的事情 4. 类和对象方法比较 5 匿名对象优缺点 6. 封装 7.作用域范围 8.id和instancetype 9.自定义构造方法规范 10.nil和Nil及NULL、NSNull区别

    1.import和include的区别: import可以防止头文件的重复包含 2.NSLog 和printf的区别: 1,NSLog可以自动换行, 输出调试信息, printf不能. 2,NSLog ...

  6. 封装自己的printf函数

    #include <stdio.h> #include <stdarg.h> //方式一 #define DBG_PRINT (printf("%s:%u %s:%s ...

  7. wndows程序设计之书籍知识与代码摘录-封装一个类似printf的messagebox

    //----------------------------------------- //本程序展示了如何实现MessageBoxPrintf函数 //本函数能像printf那样格式化输出 //摘录 ...

  8. 变参标准函数的重新封装,如printf

    方法一: #include <stdio.h> #include <stdarg.h> void my_trace(const char *cmd, ...) { printf ...

  9. 用C语言封装OC对象(耐心阅读,非常重要)

    用C语言封装OC对象(耐心阅读,非常重要) 本文的主要内容来自这里 前言 做iOS开发的朋友,对OC肯定非常了解,那么大家有没有想过OC中NSInteger,NSObject,NSString这些对象 ...

随机推荐

  1. 【剑指Offer面试编程题】题目1507:不用加减乘除做加法--九度OJ

    题目描述: 写一个函数,求两个整数之和,要求在函数体内不得使用+.-.*./四则运算符号. 输入: 输入可能包含多个测试样例. 对于每个测试案例,输入为两个整数m和n(1<=m,n<=10 ...

  2. Codeforces Round #594 (Div. 2) - C. Ivan the Fool and the Probability Theory(思维)

    题意:给n*m的网格涂黑白两种颜色,保证每个格子上下左右的四个格子中最多只有一个格子与自己颜色相同,问有多少种涂法?结果$mod1000000007$ 思路:先只考虑一行有多少种涂法 $dp[i][0 ...

  3. vector的clear和swap

    vector的clear()操作只是清空vector的元素,而不会将内存释放掉 vector<int> vec1{ 1,2,3,4,5 }; vec1.clear(); cout<& ...

  4. maven缺失ojdbc6解决方案 :Missing artifact com.oracle:ojdbc6:jar:11.2.0.1.0问题解决 ojdbc包pom.xml出错

    问题已解决,感谢博主,给您磕头了. | | 解决方法就是把缺少的 jar 手动添加到本地仓库中,再重新引入依赖即可.详情请参考以下链接. | | 转发自: https://blog.csdn.net/ ...

  5. 「IOI2014」Wall 砖墙

    题目描述 给定一个初始元素为 \(0\) 的数列,以及 \(K\) 次操作: 将区间 \([L, R]\) 中的元素对 \(h\) 取 \(max\) 将区间 \([L, R]\) 中的元素对 \(h ...

  6. display:flex下子元素宽度无效

    在子元素上设置: width:60px; flex-shrink:0;

  7. 重大消息:华为笔记本电脑开始用LINUX系统

    对华为而言,此举不失为一个明智的抉择.在手机操作系统领域,目前已被苹果的IOS系统和谷歌的安卓系统垄断.而IOS系统是封闭式,只为苹果手机使用:安卓是开放性,当谷歌与华为停止合作后.华为手机将无法使用 ...

  8. Linux centos7 sed工具介绍

    一.sed上 grep工具功能只能实现查找,不能把查找的内容替换. sed本身是一个管道命令,主要是以行为单位进行处理,可以将数据行进行查找.删除.替换字符或字符串.调换字符串位置.直接修改文件内容等 ...

  9. 导弹拦截p1020(LIS问题)

    题目描述(题目链接:https://www.luogu.org/problem/P1020) 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够 ...

  10. 写给java web一年左右工作经验的人

      摘要 大学就开始学习web,磕磕绊绊一路走过来,当中得到过开源社区很多的帮助,总结了这些年来的技术积累,回馈给开源社区. ps:图片都是从网上盗...感谢原作者. ps:文字千真万确都是我自己写的 ...