当前课程知识点:ARM微控制器与嵌入式系统 > 第五章 ARM微控制器的各种外设 > 5.6.1 ARM微控制器外设:SPI通讯简介 > 5.6.1 ARM微控制器外设:SPI通讯简介
各位同学大家好
我是清华大学工程物理系的曾鸣老师
欢迎大家继续回来我们每周一次的
ARM微控制器与嵌入式系统的MOOC之旅
那么在这门课里我们已经学习了很多种外设
大家用过按键
然后也放过音乐
那么在上一个实验单元里还教大家
点亮了第一种的显示屏
我们能够做的事情已经越来越多
对了 我们还进行了时钟的中断 对吧
那么在这个单元里我们来学习一类新的外设
也是我们所学习的第二类的通讯模块
也就是SPI这样一种同步串行通讯
那么说到通讯我们回顾一下
我们前面学过的通讯模块
比如说UART异步串行通讯
其实我们当时讲了UART异步串行通讯
这样一种模型
当两个芯片当中除了共地之外
就是一个你发我收
一个我发你收这样一个一对的通讯引脚
那么它们当中是没有共用的时钟信号的
是在一个约定的速度下一边发一边收
当时讲过这当中可能会因为时钟的不同步
带来一些误码的问题
那么在异步串行通讯UART的里头
它是怎么解决这个问题的呢
大家回忆一下
是有了这么一个数据帧的概念
在一个不归零码
NRZ编码的下面用每一次的下降沿
产生一个起始位
从起始位处开始对齐
依次发送起始位
八个数据位
可能有校验位乃至停止位
在10到11个比特的宽度里
完成一个数据帧
直到归到逻辑1
到下一次的由1到0的跳变
启动下一个数据帧
于是双方的时钟在一个数据帧里对齐
保证这个数据不会出错就可以了
这就是异步串行通讯UART的数据帧的格式
不知道大家回忆起来了没有
如果没有回忆起来没关系为什么呢
因为大家做过实验
然后如果有兴趣的同学你要做设计
你还会去接GPS接GSM短信模块
你都会用它
那么在这个单元里
我们来学习一类跟它并行的知识
就是同步串行通讯当中最典型一类外设
叫SPI
那么SPI的同步串行通讯这个模块
它是Serial Peripheral Interface的缩写
所以如果你光从这个名字上来看呢
它既没有出现这个同步
也没有出现这个通讯
它是串行外设接口的这样一个词的缩写
那么这个词的意思呢非常的明确
就是通过一种串行的通讯方式
来方便我们Interface
外接很多不同的Peripheral外设 对吧
我们前面学过很多的模块
都是我们ARM微控制器内部的各种外设
时钟 IO啊这都是外设对吧
那么实际上真正的外设外设
指的更多的是我们这样一个微控制器
CPU外面的叫外设
整个微控制器这个芯片外面外接的也叫外设
那是真正的外设
那么SPI给我们提供一个对外连接
这些东西非常灵活的接口
所以我们前面提到的像GPS的模块
像这样的一些模块
有很多除了提供一个
UART的异步串行通讯的接口以外
像U-blox公司的这个GPS
它就还提供一个SPI的接口
方便我们进行外联和外扩
那么实际上呢
我们使用SPI这样一种接口
可以非常容易的在市面上找到各种各样的
具有SPI接口的外扩芯片
比如ADC芯片
比如实时钟
比如液晶屏 比如存储器 比如传感器
所以一个微控制器如果有了SPI接口
我们可以非常容易的
为它扩充一些我们所用的
这个微控制器型号不具备的功能
让它功能变得更强大
那么SPI这样一种接口
就如这两张图所示
它是一个典型的四线串行通讯接口
那么它可能一对一的连接
也可能一对多的连接
待会我们会讲
是哪四根线呢
那么我们具体看一看
这样一种四线的串行通讯
在两个一主一从的设备之间连接的时候
是一个什么样的连接关系
首先呢我们会看到会有MOSI和MISO
这样两根数据线
以及SCK或者CLK这样一根时钟线
和SS这样一根片选信号线这四根信号线
那么大家一定不要忘记的是
我们经常会说四线串行通讯
这个四线指的是这个信号
那么一定不要忘记的是
两个电路连接的时候
它们一定要共地
有一个共同的地平面
所有的信号要有电流的回路
所以其实它们
还有第五根线是地线一定要连在一起
那么具体到这个信号线来讲的时候
我们来看这样一个SPI的串行通讯
大家首先在这个后面内容讲解之前
先初步有一个概念
它是分主从的
所以这两个通讯的设备当中
有一个是Master是主机
有一个Slave是从机
那么因为有了主从
刚才说到的这四根之间的信号线
就会随着主从有进一步的定义
MOSI是Master Output Slave Input
是由主机这一边
向从机发送数据方向的数据线
而MISO Master Input Slave Output
是由从机向主机返回数据的数据线
然后SCK这根时钟线是我们做
同步串行通讯最最重要的一个特征
也就是说由主机将自己所驱动数据
发送的这样一个时钟信号
用一个引脚给出来
提供给从机
对于从机来讲这个时钟信号是输入的
它要根据这个时钟
来看主机发给它的数据
来识别每一个比特
它也要在这个时钟的驱使下
向主机返回它的数据
按照主机的时钟节拍返回数据
也就是说时钟线决定了上面两根数据线
一个比特一个比特的切换的时序
那么最后一个片选信号呢
也是由主机发送给从机
当主机要跟从机进行一次通讯的时候
应该在片选信号线上从高电平
切换给出一个低电平
告诉从机 主机正在召唤你
主机正在选通你
将要跟你进行通讯
这样一系列线的配合
完成一次SPI的同步串行通讯
大家听起来好像觉得比异步串行通讯
好像还要更麻烦
其实不然
各有各的优点和缺点
那么我们来回顾一下这样一个流程
想想它的本质是什么
实际上在主机时钟的驱动下
把自己的数据一个比特一个比特发给从机
而从机必须在同一个时钟驱使下
这个跟异步串行通讯不一样
这是同步串行通讯
同样的一个比特一个比特
把自己的数据立刻返回给主机
所以假设主机跟从机各自有一个Buffer
存储了一个字节的数据
这个时候在一个时钟驱使下
完成一个八比特的同步串行通讯
它的过程是什么样的呢
过程是像这个动画这样的
是分别完成一个比特的交换
然后如此循环反复进行多次
最终的结果是完成了彼此一个字节
在各自的缓存的对换
所以你可以理解为它构建了一个
从主机到从机之间的移位的
这样一种交换机制
这就是SPI通讯在同一个时钟驱使下
它的一种工作模式和基本的原理
那么有了一个大致的概念
我们再回过头来宏观的看看
什么是SPI的这个叫做串行外设接口
或者叫同步串行通讯
它有哪些特征呢
首先它是串行的 对吧
数据是一个比特一个比特的
其次呢 它是同步的
Synchronous
因为它的时钟信号从主机提供给了从机
大家在同一个时钟的驱使下来收和发
还有一个很重要的事情是大家没有意识到的
它是全双工的
也就是说在任何一笔通讯里
主机可以给从机发一个字节
从机也可以同时给主机反一个字节
它们俩是各自独立的线
然后会有一个主机
然后在我们刚才的例子
接了一个从机设备 对吧
所以它是一个主从模式的
有一个Master mode还有一个Slave mode
双方一定是有一个往外给时钟
有一个接收时钟
有一个MOSI是往外输出数据
有一个是MISO才往外输出数据
这样形成了一对
然后它的通讯模式
在我们刚才这个例子里头是点对点的
两个设备一主一从大家通讯
其实也可以是Bus总线的
我们待会会讲一主多从
甚至是多个主一个从
这是有可能的
最后它的
刚才定义这个模式只是笼统讲的
在时钟的驱动下 对不对
时钟怎么驱动
是用电平来决定
还是用上升沿来决定
是上升沿决定还是下降沿决定
其实都没有定死
所以它是非常的Flexible
这个凡是这个词
大家要从两面来理解
我们说它Flexible很灵活
好像是褒义的
反过来呢
SPI通讯
大家如果去做实际的编程和开发
麻烦也就麻烦在这儿
因为它太灵活了
你得想清楚 看清楚手册
你要通讯的对象
它的极性 它的相位是怎么样的
我们待会会讲
最后呢
就是它的这一个数据帧或者完成一次通讯
刚才的例子是八个比特 对吧
我们是八个比特 是四个比特
还是十六个比特
其实在SPI的协议里是可以选可以设的
当然在本门课程范畴里
我们都是以字节为单位来编程的
一般八个比特大概就够了
那么SPI通讯刚才说到它灵活又麻烦
麻烦在哪呢
我们来看一看
就是我们SPI通讯约定好了双方都用SPI
除了那个速率
除了那个波特率
大家学UART时已经有初步概念对吧
我们还是要有一些通讯协议上的约定的
对于SPI来讲
最主要的约定
就是相位和极性
我们先说第一类相位
第一类相位是SPI的第一种工作模式
在这样一种工作模式上整个时序
就如这张图所示
那么时钟永远可以有两种极性
就像我们这个图上面
一个比特一个比特宽这个SCK信号
画了一个由0变1正着的时钟
也画了一个由1变0反着的时钟
那么在任何一种相位里
这个时钟是用正的上升沿触发
还是用下降沿触发
在通讯的时候是可以选的
根据你的外设
根据你的主机你自己编程
可以工作在任意一种模式
所以时钟我们先忽略有两种极性可选
那么第一种相位是什么呢
第一种相位就是
我们每一笔的通讯总由现在蓝箭头所标的
SS片选信号的下降沿来发起
也就是说主机首先对从机
把片选信号由高变低
表示一次通讯开始了
那么这个时候
从机包括主机就各自应该
把自己要给对方的数据
在我们的MOSI和MISO引脚上
大家看这两个六边形的这个长方形的方块
里头把这个数据已经准备就绪了
就蓝箭头所指向的方向
所以这个时候在数据线上数据已经有了
然后时钟开始发生跳变的时候
时钟的第一个沿 上升或者下降 根据极性
就会通知主机和从机
各自观察
各自的输入引脚
把第一个比特的值采下来
大家注意这儿有半个周期的相位差对不对
前半个周期数据准备好了
所以在这个长方块的正中间位置
去看这个数据的值是多少这是很稳妥的
那么到了时钟信号的第二个沿
不管是上升沿还是下降沿
第二个沿
那么主机和从机各自驱动自己的输出引脚
把上面的数据换一换
换成你要输出的第二个比特
换成数据的切换
到第三个沿的时候
这个主机和从机在这第三个沿的驱使下
再来看第二个比特的值是多少
那么这样一种规律是什么呢
总结起来就是
通讯由片选信号的下降沿发起
然后时钟信号的奇数沿
总是用来通知主机和从机
去采集数据信号的数据的值
时钟信号的偶数沿
总是用来驱动我们的主机和从机
去改变数据线上的值
切换到下一个比特
就是这么一种通讯模式
我不知道大家理解没有
那么这样在编程里头
其实还有很多可以灵活的
在这样一种基本的相位模式下
比如说我们刚才说到我们的时钟
是正着的时钟还是反着的时钟
这是可选的 可以灵活的
我们的通讯数据大家回忆回忆UART
UART是约定好了
MSB和LSB谁在前头 对吧
在SPI里头这件事情没有约定好
根据我们通讯的对象或者我们通讯的双方
或者是你要通讯的外设
它的芯片手册根据这个
我们究竟是LSB在前MSB在后
还是反过来MSB在前 LSB在后
也就说比特7和比特0谁先发
也是可以设置和约定的
那么这样一些设置和约定
就导致我们这个
即使在这样一种相位模式下
还可以配置还可以灵活
那么抛开这种相位模式
另外一种相位模式是什么样的呢
就是这一种
这一种我们会发现我们的通讯的发起
不是由片选信号的下降沿产生的
片选信号要变低
否则这个芯片不响应
但是它只要是低电平的状态就可以了
那么通讯是由谁发起呢
通讯是由我们的时钟信号的
第一个沿来发起的
时钟信号的第一个沿就跟刚才不一样了
就会通知我们的主机和从机
把自己的数据更新到数据线上
各自驱动MOSI和MISO引脚
然后第二个沿驱动我们的主机和从机
看这个数据值是多少
第三个沿再来驱动它
改变一次数据值
一言以蔽之就跟刚才相位模式不一样
它变成了通讯是由时钟发起
而与这个片选信号的下降沿无关
片选信号只要还在这个过程当中
一直是低电平就可以了
而时钟信号的奇数沿总是驱动主机从机
改变自己所输出数据的值
时钟信号的第偶数个沿
总是驱动主机和从机
去看自己接收到的数据值是多少
如此周而反复
大家注意刚才是
偶数去这个驱动 奇数去看
现在是奇数驱动 偶数看
这就是两种不同的相位模式
那么在这第二种相位模式下
同样我们的时钟可以有两种极性
我们的LSB和MSB谁先发谁后发
也是可以设置可以选择的
那么在第二种模式下
有很多同学会发现
我们SS片选这个引脚
不必发生沿的跳变来驱动通讯
那意味着什么呢
意味着我们其实极端情况下
在多个字节连续通讯的时候
我们可以SS引脚一直是低电平不变就可以了
8个比特发射完
过一小会儿
背靠背的再来八个比特
这在第二种相位模式下是可以的
而在第一种相位模式下不行
每一笔通讯完
都得发生一次片选信号的跳变
所以大家对于不同的工作模式 相位模式
加以理解
你就会更多的理解我们SPI通讯的灵活性
它的相位可以变
时钟极性可以变
LSB可以变
这一排列组合就有很多种
所以说还是我那句话
灵活灵活在这儿
麻烦也麻烦在这儿
编程的时候要么双方的通讯都是你编程
你选择同一种模式
要么你看芯片的时候
看清楚你编程通讯的对象
它究竟是哪一种相位
哪一种时钟 哪一种极性
哪一种字节序
然后跟它匹配的编程
否则我们做SPI很多时候大家会卡住
说我程序觉得写的是对的
通讯就是不上
拿到的数据是错的
拿不着数据
为什么
大家仔细的抠这些细节
那么大家真的理解了吗
细节抠好了吗
UART的时候我们做过个小游戏
现在我给大家再来做一个SPI的小游戏
那么在我的课上我一般会找个志愿者
既然是MOOC课
大家自己脑补或者找个小伙伴
跟着我的步骤来自己做一做
我们说我们来找一个人
扮演一个SPI的一个Master
扮演主机对吧
然后来发送这个数据
那么在这个过程当中
我们选择了一种通讯模式是什么呢
是每次frame发送八个比特一个字节
我们的相位CPHA相位模式选择1
我们的极性CPOL选0
回到刚才那张图
相位选1
就是刚才最后这个时序相位关系图
极性选零
大家看是正脉冲还是负脉冲
对吧 我们就约定好了这个通讯
那么其实就是片选信号就不用了
就是在时钟的驱动下
来驱动数据就可以了
第一个时钟沿准备数据
第二个时钟沿看数据 对吧
然后我们要完成这个通讯的时候
跟我们做UART的游戏一样
大家自己来扮演举手代表逻辑1
放手代表逻辑0来扮演一个发送器
把一个字节的数据发送出去
让别人扮演这个slave
扮演你的slave
来接收这个数据
但是大家想想这个时候
是同步串行通讯了
一只手够吗
一只手是不够的
所以你必须左手扮演时钟信号
右手扮演Master Output Slave Input的数据信号
那么左手周期的产生时钟
右手伴随时钟的产生数据
那么在这样一种组合下
你必须在左手的一个又一个时钟节拍下
跟它同沿的去切换你右手的数据
我相信它是相当的难的
所以我在这里没有指定速率
没有指定速率有两个问题
第一 比如说我指定波特率为1
每秒一个比特
我相信很多同学的协调性不好
一定做不出来
第二 其实大家慢慢会发现
有了同步串行通讯的时钟下
这个时钟信号的速率是快是慢
其实在约定里头没有那么重要了
我反着跟着你的时钟走不就完了嘛
这就是同步串行通讯的好处
大家试一试这个游戏
也考一考你有没有理解它
那么非常短的时间
如果大家理解完了SPI的基本模式
我们再往后过一点点拔高的内容
那么对于这样一种模式下面
你会发现它有了主 有了从
它们的地位是不一样的
作为一个主机它要完成这么几个任务
它要通过片选信号选择从机
或者给从机一个片选信号为低电平
告诉从机我要与你通讯 对吧
发出片选
其次呢 它要正确的去驱动
它的时钟信号的频率 相位
还有这个极性这些特征
然后产生这样一个时钟信号把它送出去
然后伴随这样一个时钟信号
它要正确的驱动MOSI引脚往外
给主机发给从机的设备
同时在这个时钟的驱使下
一个比特一个比特的check
MISO引脚从机给它返回的数据是什么
所以CPU在我们编程的角度待会会学
总是通过向数据寄存器
写入一个新的字节
来启动一次通讯的过程
那么作为从设备
它在地位上和引脚的极性上
不像原来异步串行通讯是对等的
它也是有差异的
它主要做什么呢
它主要要监听这个片选信号
当它为低电平的时候
表示自己被选中了
然后表示自己要通讯
同时根据约定好的相位 极性这些信息
来接收时钟信号
在主机发给它的时钟信号的驱动下
按照主机要求的速度
来驱动自己MISO引脚
应该给主机发送的数据
同时查看MOSI引脚主机发给它的数据
这个主和从做的事情是不一样的
最大的区别是在片选和时钟信号上
那么某种意义上
还有若干个点
比如说典型的这个SPI
如果是一主一从
它就是一个点对点结构
那么点对点结构通讯的时候
我们刚才已经体会到了
双方要在这么灵活的模式下
约定好时钟的相位 极性 数据帧这些信息
才能保证一个SPI的主和从
能够完美无缺的通讯起来
那么要想完美无缺的通讯起来
我们的主机 我们的从机
都得在一个时钟周期之前
准备好各自要发送的数据
让它顺利的发送
而对于时钟信号
它是整个这个通讯的驱动
所以这个时钟信号
必须比较的干净 不能有毛刺
它的频率必须是双方都能产生
都能接受的范围
比如说从机最多能接受400K赫兹的时钟
那主机给的时钟就不能比它快
那么这样才能保证这个通讯
在主从两边都是正确的
然后多讲一句的是
SPI这种通讯模式除了点对点
能不能总线呢
是可以的
它虽然比较笨
但是是在传统的电子学设计里
常见的一种模式
一个主机连多个SPI从设备
比如温度传感器 湿度传感器
加上存储器连在一个SPI接口上是可以的
使我们的微控制器有非常强的扩充能力
那么我们就应该对于每一个从设备
都有一个独立的片选信号
让每一笔通讯都很清楚的知道
是选中的哪一个设备来跟它通讯
那么在这个引脚上给出低电平
那么这个引脚既可以是SS
SPI模块的引脚
也可以用我们微控制器上
任意一个IO来模拟这个片选
我们只要对IO操作给出低电平就可以了
大家在这个地方不要教条
那么能不能是多个主机呢
在极端情况下也是可以的
比如多主一从
我就不展开讲了
那么最简单的是
在我们这个模块的设计里
在我们的ARM微控制器里
是有这样的模式的
它通过一个主机
自己是主机
又感受到了片选信号被别人拉低
来察觉到有多个主机的存在
并且别人
其他的主机要说话
这样实现之间的互相的校验和避让
实现多个主机
分别对同一个设备进行读取
比如说有一个温度传感器
要被多个微控制器读取
那么极端的情况下可以这么去做
有兴趣的同学可以摸索
那么讲完了SPI的基本通讯模式
和若干个高阶段的内容
相信大家对SPI有了一个宏观的概念
特别是抽点时间玩一玩中间那个小游戏
那么在下个单元我教大家
如何真真正正的
把SPI寄存器编程给用起来
-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 嵌入式系统的实例