当前课程知识点:ARM微控制器与嵌入式系统 > 第五章 ARM微控制器的各种外设 > 5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下) > 5.2.3 ARM微控制器的外设:异步串行通讯UART的原理(下)
各位同学大家好
我是清华大学工程物理系的曾鸣老师
欢迎大家继续回到我们
ARM微控制器与嵌入式系统的MOOC课堂
那么我们继续这个章节的学习
来学习非常好用
又非常好玩的异步串行通讯
也就是UART
那么UART这样一个典型的异步串行通讯
它有很多用处
在前面两个单元的学习呢
我们已经认识了这样一个
基本的数据帧的格式
它作为一种不归零码的这种形式
在平时是逻辑一
然后会有一个跳0的起始位
然后一个比特一个比特的发送数据
最后可能有一个校验位
然后再归一有一个停止位
那么我们认识了这样一种数据帧
我们就大概知道了在共地的情况下
两个计算机系统或者两个微控制器单元
它们之间进行通讯的时候
数据是怎样在一根线上由0 1的变化
一个比特一个比特给发送出去的
那么这是理想情况
我们当然在这个过程当中
也认识了包括奇偶校验
包括波特率这样一些基本的词汇
那么我们在这个单元里来更加深入的去看一看
异步串行通讯这样一种数据帧
或者这样一种通讯
背后我们还要注意哪些
比如像这样一张图
我们忽略了起始位和停止位
然后我们发送了这么一个字节
这个字节按照发送的顺序是0110 0100
然后我们在一个时钟的驱使下
在这个data的数据线
也就是我们串行通讯的这根引脚上
交替的产生高低电平
把这个数据送上去对吧
那么我们看到这个数据的时候
我先考大家一个问题
看到这张图
我们发送的字节是什么
让大家用16进制来说
然后这个时候大家可能会卡一下
因为这里看到0110 0100这是个二进制
然后我们要算一下它16进制是什么
然后另外大家不要忘了
我们在上一节课讲数据帧的时候
讲过一件什么事呢
它是LSB先发
所以你看到了0110 0100
这样一个二进制数的时候
如果你用人类的语言
或者我们把它写下来的时候应该倒着写
最后发的是最高位对不对
所以它是C语言写的
0b00100110是这个二进制的数
写成16进制的时候是什么呢
8 4 2 1 是0x2 然后4 2 1
0X26是刚才这张图所发送的16进制的数
那么这个数我们以这样一种理想的情况
把它发出去了
如果像这个理想的故事或者怎么讲
我们说这个数据顺利发出去接收方就顺利收到
那在一个异步的时钟驱使下
接收端去看这个数据
一个比特一个比特采
它应该收到就是0110 0100这个数
而在现实生活当中
我们很可能出现这样一张图的结果
因为在异步串行通讯里
发送和接收端只是数据的引脚连在一起
也就是我们看到的这个跳变的TxD
直接通过一根线
接到了接收端的RxD
但是因为这根电缆的存在
和我们在通讯过程当中
不是一个理想的导体或者理想的传输介质
我们会遇到很多奇怪的现象
比如说我们的这个电平在持续为低电平的时候
可能会突然出现一个尖刺
跳变了一下变成高电平
这种现象在什么时候会发生呢
就是当空间有些电磁干扰的时候
比如大家都会体会到一件事
说我这个一个音响放在电脑边
这个时候手机收到了一条短信
短信手机还没响
音响先哗啦啦的响了几声
那么我们可能受到了电磁干扰
得到了在接收端发现多了个小的
窄窄的小跳变
此外我们都学过基本电路知识
我们的电缆它这么长
到一定程度的时候
它是有电容的
所有的信号都可能在拉动一个容性的负载
所以我们的电压的跳变
不会是零秒上升沿的一个理想上升沿
它因为负载端电容的大小
可能会使上升沿变缓
或者下降沿变缓
那么我们在高电平的时候
也可能受到干扰产生一个小小的
窄窄的向下的跳变
等等等等这样一系列的现象
在一个真正使用的通讯里是可能存在的
于是我们异步接收时钟在一个比较准确的
理想的情况下
去查看这个数据
这个时候大家再看这张图的下半截
会发现什么呢
也许我们读到的数据就不再是发送的01100100了
而可能是10110010
然后我们把它还原这个数据的时候
会发现面目全非
那么这就是真正的理想的这个通讯
和现实的通讯当中
我们所遇到的问题
当我们学习嵌入式知识和电子学知识的时候
很多时候我们不能活在理想的模型里
而要考虑真正做出来的电路会遇到什么样的情况
如何才能正常的工作
包括我们做一辆智能车
我们很多同学会与之类似的去做这个脉冲计数
来测车的速度
但是我们可能会遇到与之类似的情况
有没有数漏脉冲数
你脉冲数有没有受到干扰
我们的电路是否鲁棒等等这些特性
会决定我们真正设计的嵌入式系统
是否能够正确的如我们所愿的去工作
那么放到这个问题里
如果大家是一个串行通讯的设计者
你怎么来解决这个问题呢
给大家几秒钟稍微想一想
很好 那么有的同学可能会想
说老师我能不能用多看几次这个数据
也就是所谓的Oversampling过采样来解决这个问题
那么实际上在嵌入式行业里头
我们串行通讯不管谁家做的微控制器
是不是ARM的
普遍都会采用这样一种方式
来使避免串行通讯出错
简单的说什么叫过采样
就是我以一定的速度来发送这个数据
而在接收端我用比约定好的波特率
快一些的速度来看收到的数据
那最典型在我们这个ARM Cortex这个MCU Freescale
这个KL25我们学的这个微控制器里
不管是UART0还是UART1 UART2
默认的速度是16倍的速度
用16倍的速度来查看接收的数据
这也就是我们所说的Oversampling过采样
换言之
一个时钟宽度的发送到接收端
我们把它在这张图里拉的宽宽的
它以16倍的时钟去查看接收到的速度
这样这个数据每切换一个比特
也就是说波特率那一个格子的宽度
实际上在接收端被看了16遍
那么这16遍当中
在我们的微控制器里头会用中间的三个比特
作为最可信的值 然后来投票
什么叫投票呢
也就是这张表
如果中间三个比特采一个值
采到都是1 或者都是0
那么毋庸置疑
这个比特的值一定非常大的概率肯定就是1
因为是干扰是什么
它不会持续这么长时间
那么如果我们采到的值有1有0怎么办呢
那就用这三次采样值投个票
谁多就是谁
比如两次1一次0 那就认为是1
与之相关的
就会在我们数据读出的这个UART的状态寄存器
待会学到会有一个noise flag来置一
告诉你这一个采到的字节的数
是受到了一些噪声干扰的
你可以选择相信它就这么用
或者选择扔掉它
我希望重发一遍
所以有了过采样的技术
就能解决我们刚才所说的
这样一个串行的通讯里头
如果受到了干扰怎么办
那么我们多一个小的问题
说一个微控制器
它内部的时钟
大家记得我们讲这个微控制器内部是有时钟数的
所有的模块的时钟
都是在一个总时钟的基础上分下来的
假设说我们这个微控制器内部
最快的这个时钟就是16兆赫兹
当我们再来做一个异步串行通讯
我们最大的波特率是多少呢
在这个时钟的驱使下还是16兆BPS(Bit per second)吗
一个时钟clock 一个比特吗
大家想一想
那么给点提示就是要考虑刚才讲的过采样
你做完一次通讯
你不仅要保证你能用这个时钟发送
你还要保证能够用一个
相对约定好更快的速度去接收 对吧
那么回过头来我们再来看一看
我们最早讲这样一个异步的串行通讯
两个器件之间用三个引脚共一个地
一个引脚你发我收
一个引脚我发你收
他们不共享时钟的情况下
完成这样一个异步通讯
我们在这个单元的最开始提到过一个概念
就是发送端和接收端
是用各自的时钟来收和发
那么和刚才讲的问题类似
我们刚才是假定时钟是准的
数据受到了干扰
那么现在反过来看另外一个问题
也是最早提出的问题
如果两者的时钟不是严格的同频率
或者叫严格的同步
那会发生什么问题呢
比如像这张图里
如果这个误差慢慢的累计
到了最后一个比特的时候
它已经错位了一个比特了
我们接到的数据同样会面目全非对不对
所以我们反反复复在讲的
是通讯如何能保证正确
那么在上一节课我们讲到
说因为我们有了UART这样一个数据帧的格式
所以我们只要在一个起始位开始
到一个停止位结束的时间内
误差不超过一个比特
就能使接收端每一个时钟沿
每一个clock
都能对准发送端意图当中的每一个比特
保证数据不出错
而因为数据帧的格式的存在
每一次发送这个帧都会在帧头进行对齐
使这个误差不被累计
那么对于时钟的要求就降低了
只要在这个帧内不出错
那么请问这个要求是多少呢
我们现在来算一算
比如说我们这个数据一个比特一个比特地发送
那么在不考虑奇偶校验位的情况下
从发送端的视角看
就是一个开始位
八个数据位 加一个停止位
而从接收端的角度看
从我们刚才所讲的概念
我们会用它16倍的时钟去采样这每一个比特
那么采样的结果就是从接收端看
我采样到最中间那三个比特
或者说是16次采样当中最中间那一次采样
最晚不能晚过最后一个比特在发送端的后沿
最早呢不能早过最后一个比特在发送端的前沿
你不能错位到上一个比特
那么把刚才用人类语言我说的这样一个关系
这个大于小于写成算式
那么就是这样一个算式
也就是说
我们用16乘以1加8倍的时钟
再加上9个采样clock
也就是正中间那个时钟这么总的采样时钟数
它要比我们实际发送端9倍的发送时钟大
比发送端10倍的发送时钟小
那么这两个时钟的频率的误差
写成这样一个表达式以后
把这个不等式解一解
取一个近似值
大家非常好记是什么呢
就是有了数据帧UART这样一种帧格式
发送和接收端大家可以自己去算
它的时钟误差如果小于5%
其实就是可以接受的
那么也就是说当发送和接收以各自的时钟
来把数据排成帧的形式
双方没有共享时钟
只靠那个起始位来对齐的时候
那么只要时钟误差不大于5%
接收端就能正确的把数据帧的值给解出来
那么这样一种概念下面我们很定性的想
假设我发送端的时钟是1000赫兹
也就是1KBPS的波特率
那么也就是说我的每一个比特是一个毫秒的宽度
那么我接收端是什么概念呢
也就是我接收端的时钟频率
只要在950赫兹和1050赫兹之间这个频率
就是正确的了
950和1050赫兹
跟发送端的1000赫兹
1000BPS的误差在5%以内
也就是说只要我们接收端的频率设置完了以后
实际运行的时候
在这个5%的范围内
它能保证从接收端的视角看
采样的最后一个比特
还能正确的在我们黄色区域的小格子里头
正确的识别整个自己的数据
而到下一个数据帧的时候
它又以帧头再一次对齐
这样我们周而复始的通讯
就能一直的正确的进行下去
不出错
有了这样一种数据帧的格式
我们就能从时间这个维度上
保证异步的串行通讯是正确的
所以我们讲了如何在数据电平的视角
保证它是正确的
如何在时间波特率的视角保证它是正确的
这两个约束条件
那会不会还是会出错呢
极端情况下还是有可能的
那么我们人类在做通讯的时候
最重要的一件事情除了快就是不出错
所以还会引入一个新的问题
就是如何对发送的数据进行校验
让我们有更大的概率知道它出错了
比如说波特率也对了
电平也对了 电缆也考虑了
万一还错了怎么办
那么在一些高可靠性的要求里头
我们会启用校验
这也就是在我们一开始
讲这个数据帧格式的时候会说起始位
8个比特的数据位
有一个可选的奇偶校验位
后面才是停止位
如果选择了不做奇偶校验
那么这一位就没有
占的时间就是0
那么如果选了奇偶校验位
它会占到一个比特的宽度
在这里放上一个0或者1
来帮助我们接收端
对于发送端的数据做校验
那么在UART的串行通讯的协议里头
采用的是奇偶校验
我刚才已经说过
那么这是人类最最简单的一类校验
那么什么是校验呢
校验就是通过增加一些比特
来检查我们发送的数据的正确性
那么校验不仅仅有奇偶校验
比如还有ECC校验
在我们日常使用的这个FLASH存储器
我们的手机里其实都有
那么我们校验的算法越复杂
使用的比特越多
我们能够检查出来的错误就越丰富
校验的能力就越强
而显然奇偶校验是最简单的一类校验
它只用一个比特来做校验
那么什么叫奇
什么叫偶校验呢
我们来讲一讲
奇偶校验
比如Even parity check
也就是偶校验
那么它的方法就是我们在一个数据的最后
加上一个0 或者一个1
使1的总数是偶数
听明白了吗
加上一个0或者1
使数据加上这一位
1的总数是偶数
就是偶校验
那么与之相应的Odd parity check
也就是奇校验
那么它是加上一个0或者1
使我们这个数据加上校验位的1的总数是奇数
那么就是奇校验
那么换言之我们举个很简单的例子
比如说我们有这么两个数
我们给10011100这个一个字节的二进制数
加上一个0或者1的校验位
对它进行偶校验
也就是Even parity check
那么请问应该加0还是加1呢
我们做偶校验
希望1的总数是偶数
那么数一数里头已经有几个1了
发现数一数里头刚好是4个1
那我们要希望一的个数是偶数
我们是不是加个0就行了
那么与之相应的
我们如果对01010110这个数
要做一个Odd parity check也就是奇校验
我们应该在后面加个0还是加1呢
加个1就行了对吧
这样让1的总数是5个
所以我们说奇偶校验是一类非常简单的校验
我们刚才用人脑做的这件事
就是我们在UART的串行通讯模块里
电路帮我们实现的
我们对于所有的0和1做一个计算
然后自动的在后面填补一位
然后从发送端放在我们的数据帧的后面
发送出去
那么当我们的接收端
收到了这样一个字节的数据
加上一个比特校验位的时候
接收端就好办了
我们根据约定好做的是奇校验还是偶校验
我来数一数
这个1的个数是偶数还是奇数
于是我们就知道这个字节有没有出错
那么奇偶校验在前面讲的数据帧的电平
这个波特率的时钟误差基础上
能够进一步保证我们数据的通讯是正确的
那么我们想问大家一个问题
说有了奇偶校验能发现所有的通讯错误吗
有同学马上就反应过来说不能
为什么呢
我们做作业经常会出现叫负负得正的现象对吧
在前面一个步骤犯了个错误
中间某个步骤又犯了一个错误
这两个步骤的错误一抵消最后答案还是对的
那么奇偶校验里也会出现这种问题
我如果两个比特同时出现了翻转
那么我们会发现即使校验位加上奇偶校验
仍然是符合的
但是数据错了两个比特
这是第一个问题
校验是一种辅助的检测数据错误的方法
但是至少像奇偶校验甚至包括ECC校验
都不能保证发现所有的错误
为什么呢
因为它是用有限个比特
去检查更多的比特有没有出错
第二个问题是奇偶校验能帮我们纠正错误吗
其实你会发现它能力很有限
它纠正不了
如果我们发现奇数的个数不对的时候
我们并不知道是哪个比特错了
所以我们往往是会在用户层在软件协议层
要求数据重发一遍
那么有一些校验是可以做纠正的
比如ECC校验
用8个比特检查若干个字节的数据错误
能发现一比特的错误并且纠正
能发现两比特的错误但无法纠正
所以我们通过学习通讯
也要对校验留下一个印象
通过有限比特检测更多比特的错误
但是不一定能发现所有的错误
也不一定能做出纠正
好了 讲完这个单元的内容
我们就更加知道了异步串行通讯
那个UART数据帧背后更高阶的一些内容
包括出错怎么办
什么是过采样
时钟究竟要有多同步才叫同步
以及奇偶校验的概念
那么为了检查大家有没有真正理解UART
最最核心的这样一种数据帧的格式
也就是上个单元和这个单元的知识
在我的课堂上我一般会让大家来做个游戏
那么我在这里给大家讲一讲
说我们来做这么一个游戏的时候
大家如果在MOOC的课程之下
你可以找一个你的小伙伴来跟你玩
比如说他来当一个志愿者或者当一个小伙伴
那么我们请他来扮演一个人体UART发送器
大家注意我们讲的是数据帧
我们并没有规定这个数据帧0和1
用什么方式来表征对不对
所以我们完全可以是一个人体
那么这个人体UART发射器呢
我们规定了0和1的表达方式
也就说他把手举起来代表逻辑1
把手放下去代表逻辑0
那他是不是就通过举手 放手发送1或者0
然后我们再约定我们的通讯协议
大家回忆上单元讲的
是1 8N1这样一种数据结构
那么1 8N1考虑到UART数据帧是什么含义
意味着我们波特率是1
也就是说每个比特持续一秒钟
也就是它要发送1就请举手一秒钟
它要发送0就请放平一秒钟
然后数据每个帧发送8个比特的数据
然后呢
没有奇偶校验位 也就是从起始位
8个比特直接到停止位
停止位也占一个比特
那么我们请你的小伙伴
或者请你的朋友试着发送一个字节
或者一个ASCII码字符给你
看你能不能识别出来
那为了给大家参考
比如说我给出这三个字母的ASCII码
大写的U 大写的Z或者小写的e
比如说以大写的Z为例
它的ASCII码写成二进制
就是0b01011010
那么你要依次把这个字节给发送出去
那么在玩这个游戏
我给大家两个提示避免大家出错
或者叫三个提示
第一个提示是注意你的波特率
你不要时快时慢对不对
时钟误差大了
就会像我们刚才讲的有识别的错误
第二呢你要注意我们的frame的帧格式
我们是平时没有数据的时候
是高电平
起始位是变0 终止位要回1
所以大家在发送的时候你要告诉对方等待
是不是你得先把手举起来是逻辑1
开始发送开始回的时候
你首先要放下来发送一个起始位
对吧这是第二个注意事项
那第三个注意事项是什么呢
是我们在发送这个数据帧的时候
大家回忆
是LSB先发呢 还是MSB先发
我们这个字节写成0b01011010的时候
我们应该从哪个比特向哪个比特发
你一定要想明白了
好了 那大家下去自己玩一玩这个游戏吧
相信你玩一玩
虽然它看上去笨笨的 傻傻的
一定会加深你对于UART的数据帧
是一种什么样的感觉
那么这个单元的课就到这儿
相信大家已经很好的理解了
串行通讯的数据帧格式
从下个单元开始
我们真正的编程把它用起来吧
-1.1 课程概览
--1.1 课程概览
-1.2 如何学好嵌入式系统
-2.1 计算机的基本概念、发展历史
-2.2 从晶体管到CPU
-2.3 概念CPU、微控制器MCU和嵌入式系统
-2.4 八卦计算机史
-2.5 不同领域、不同系列的嵌入式系统
-2.6 ARM历史与MKL25Z128 MCU
--2.6 ARM历史与MKL25Z128 MCU【习题】
-3.1 CPU的基本结构和运行机制
-3.2.1 堆栈的概念
--3.2.1 堆栈的概念【习题】
-3.2.2 堆栈的概念-头脑体操
-3.3.1 ARM的体系结构
--3.3.1 ARM的体系结构【习题】
-3.3.2 ARM的体系结构-头脑体操
-3.4.1 中断的概念和机制
-3.4.2 中断子程的概念和编程
-3.5 复位、时钟、存储器和总线
--3.5 复位、时钟、存储器和总线【习题】
-3.6 小结:MCU的总体结构和程序运行机制
--3.6 小结:MCU的总体结构和程序运行机制【习题】
-4.1 第一种外设:IO
-4.2 IO外设的编程实操-点亮LED
-4.3 IO外设的进阶知识
-4.4 嵌入式开发的基本概念与工具链
-4.5 嵌入式开发的进阶知识
-4.6 嵌入式开发中的C语言(上)
--4.6 嵌入式开发中的C语言(上)【习题】
-4.7 嵌入式开发中的C语言(下)
--4.7 嵌入式开发中的C语言(下)【习题】
-E0.1 实验零 开发板的初步认识与工具链的安装
-E0.2 实验零 体验一个例程的编译与下载
-E0.3 实验零 编写第一个程序:点亮核心板LED
-E1 实验一 点灯秘籍
-5.1 ARM微控制器外设学习概述
-5.2.1 ARM微控制器外设:通讯
-5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)
--5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)
--5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)【习题】
-5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下)
--5.2.3 ARM微控制器的外设:异步串行通讯UART的原理(下)
--5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下)【习题】
-5.2.4 ARM微控制器外设:RS-232串口与USB虚拟串口
--5.2.4 ARM微控制器外设:RS-232串口与USB虚拟串口
-5.2.5 ARM微控制器外设:UART的寄存器编程(上)
--5.2.5 ARM微控制器外设:UART的寄存器编程(上)
-5.2.6 ARM微控制器外设:UART的寄存器编程(下)
--5.2.6 ARM微控制器外设:UART的寄存器编程(下)
--5.2.6 ARM微控制器外设:UART的寄存器编程(下)【习题】
-E2 实验二 UART编程实操
-5.3.1 ARM微控制器外设:IO的中断编程(上)
-5.3.2 ARM微控制器外设:IO的中断编程(下)
-5.4.1 ARM微控制器外设:定时器的原理
-5.4.2 ARM微控制器外设:定时器的编程
--5.4.2 ARM微控制器外设:定时器的编程【习题】
-E3 实验三 定时器中断编程实操
-5.5.1 ARM微控制器外设:PWM的原理
-5.5.2 ARM微控制器外设:PWM寄存器与编程
-5.5.3 ARM微控制器外设:PWM编程实例—电子音乐
--5.5.3 ARM微控制器外设:PWM编程实例—电子音乐
-E4 实验四 数码管显示编程实操
-5.6.1 ARM微控制器外设:SPI通讯简介
--5.6.1 ARM微控制器外设:SPI通讯简介【习题】
-5.6.2 ARM微控制器外设:SPI寄存器与编程
-5.6.3 ARM微控制器外设:SPI编程实例—OLED显示屏驱动
--5.6.3 ARM微控制器外设:SPI编程实例—OLED显示屏驱动
-5.7.1 ARM微控制器外设:I2C通讯简介
-5.7.2 ARM微控制器外设:I2C的通讯协议
-5.7.3 ARM微控制器外设:I2C寄存器与编程
--5.7.3 ARM微控制器外设:I2C寄存器与编程【习题】
-5.7.4 ARM微控制器外设:I2C编程实例—加速度传感器
--5.7.4 ARM微控制器外设:I2C编程实例—加速度传感器
-5.8.1 ARM微控制器外设:ADC简介
-5.8.2 ARM微控制器外设:ADC基础
-5.8.3 ARM微控制器外设:ADC寄存器与编程
-E5 实验五 ADC编程实操
-E6 挑战实验
--E6 挑战实验
-6.1 嵌入式系统的接口与设计
-6.2 嵌入式系统的实例