【SPI】通信协议

一、SPI通信简介
  • SPI Serial Peripheral Interface )是由 Motorola 公司开发的一种通用数据总线
  • 四根通信线:
    • SCK Serial Clock )串行时钟线
    • MOSI Master Output Slave Input )主机输出从机输入(主机向从机发送数据)
    • MISO Master Input Slave Output )主机输入从机输出(主机从从机读取数据)
    • SS Slave Select )从机选择
      • 专门用于指定从机(有几个从机就开几条SS)(但可能造成资源浪费)
  • 同步,全双工
  • 支持总线挂载多设备(一主多从)
//I2C还支持多主多从(多主机模式)

二、硬件电路

  • 所有 SPI 设备的 SCK MOSI MISO 分别连在一起
  • 主机另外引出多条 SS 控制线,分别接到各从机的 SS 引脚
  • 【输出】引脚配置为【推挽输出】,【输入】引脚配置为【浮空或上拉输入】
    • 推挽输出:高低电平均有很强的驱动能力,将使得SPI引脚信号的上升/下降沿非常迅速(因此传输速度更高)
//SPI主机实际引出了6根通信线:因为有3个从机,所以SS线需要3根;再加SCK、MOSI、MISO总共6根通信线
  • SCK时钟线:时钟线完全由主机掌控
    • 对于主机来说时钟线为输出
    • 对于从机来说时钟线为输入
  • MOSI主机输出从机输入:主机通过MOSI输出,从机通过MOSI输入
  • MISO主机输入从机输出:主机通过MISO输入,从机从过MISO输出
    • 当从机的SS引脚为高电平(未被主机选中),从机的MISO引脚必须切换为【高阻态】
//相当于引脚断开,不输出任何电平——防止一条线上有多个输出而导致电平冲突
  • 在从机的SS引脚为低电平(被主机选中),从机的MISO引脚才允许变为【推挽输出】
  • SS从机选择:主机的SS线都是输出,从机的SS线都是输入
    • SS线低电平有效(主机需要指定时,将对应的SS输出线置低电平就行)
    • 主机初始化之后所有SS线输出线都置高电平,即谁也不指定
    • 同一时间主机只能置一个SS为低电平(只能选中一个从机)

三、SPI基本收发电路示意图
  • 移位寄存器的时钟源由主机提供(波特率发生器),波特率发生器产生的时钟驱动主机的移位寄存器进行移位
    • 同时这个时钟也通过【SCK】引脚进行输出,接到从机的移位寄存器里
  • 规定:在波特率发生器时钟的【上升沿】,所有移位寄存器 向左移动一位(移出最高位)放到引脚上
  • 规定:在波特率发生器时钟的【下降沿】,引脚上的位采样输入到移位寄存器的【最低位】
  • SPI为【高位先行】,每来一个时钟,两个移位寄存器都会向左进行移位
  • 【主机移位寄存器】 左边移出去的数据通过【MOSI】引脚输入到【从机移位寄存器】的 右边
  • 【从机移位寄存器】 左边移出去的数据通过【MISO】引脚输入到【主机移位寄存器】的 右边

四、SPI时序基本单元
  • 起始条件: SS 从高电平切换到低电平(下降沿)
  • 终止条件: SS 从低电平切换到高电平(上升沿)
//数据传输基本单元
  • 基于【SPI基本收发电路模型】
  • 什么时候开始移位?上升沿移位 or 下降沿移位?——可自行配置SPI选择(两个配置位,总共可组成4种模式)
    • CPOL(Clock Polarit)时钟极性【1/0】
    • CPHA(Clock Phase)时钟相位【1/0】:决定第几个边沿采样
      • 有模式0、1、2、3;模式虽多,但功能相同
//注意:
  • SS高电平时,MISO用一条中间的线表示【高阻态】
    • 当从机的SS引脚为高电平(未被主机选中),从机的MISO引脚必须切换为【高阻态】
      • 相当于引脚断开,不输出任何电平——防止一条线上有多个输出而导致电平冲突
  • SS低电平时,从机的MISO被允许开启输出
    • 在从机的SS引脚为低电平(被主机选中),从机的MISO引脚才允许变为【推挽输出】

模式0:(应用最多,重点掌握)
  • 交换一个字节(模式 0
  • CPOL=0 :空闲状态时, SCK 为低电平
  • CPHA=0 SCK 第一个边沿移入数据,第二个边沿移出数据
//与【模式1】区别:【模式0】数据移入移出时机提前半个时钟(相位提前)
  • 在SS未被选中时(SS高电平),SCK默认为【低电平】
  • 通信开始前SS为【高电平】,通信过程中SS始终保持【低电平】,通信结束SS恢复【高电平】
//数据在第一个边沿之前就要提前【移出】以确保在SCK第一个边沿(上升沿)能够有空间【移入】数据
  • 移位传输流程
    • 趁SCK没有变化,在SS下降沿时就要立刻将数据【移位输出】
    • SCK 第一个边沿(上升沿)主机和从机同时移入数据 (进行数据采样)
      • 主机通过MOSI移入最低位(B7)
      • 从机通过MISO移入最低位(B7)
    • 第二个边沿(下降沿)主机和从机同时移出数据
      • 主机刚刚移出的B7进入【从机移位寄存器】的最高位
      • 从机刚刚移出的B7进入【主机移位寄存器】的最高位
  • 到此一个时钟脉冲产生完毕,一个数据位传输完毕;反复8次就完成了一个字节的数据交换
//如果主机只想交换一个字节,那么此时置SS为【高电平】就结束通信了
  • 在SCK的下降沿,MOSI还可以再变化一次置默认的高or低电平(SPI未硬性规定)
  • SS高电平时,MISO置【高阻态】
//如果主机想交换多个字节,主机不必将SS置【高电平】,只需重复【SS低电平】之后交换一个字节的时序即可
  • 在SCK的下降沿,MOSI和MISO还需要再变化一次(提前移入下一个字节的B7)
    • 相位提前,因此下个字节的数据会在前一个传输时序的最后露头
      • 方便下一个字节传输时序第一个SCK上升沿来移入数据
模式1:与【三、SPI基本收发电路】模型对应
  • 交换一个字节(模式 1
  • CPOL=0 :空闲状态时, SCK默认 为低电平
  • CPHA=1 SCK 第一个边沿移出数据,第二个边沿移入数据
  • 在SS未被选中时(SS高电平),SCK默认为【低电平】
  • 通信开始前SS为【高电平】,通信过程中SS始终保持【低电平】,通信结束SS恢复【高电平】
  • 移位传输流程
    • SCK 第一个边沿(上升沿)主机和从机同时移出数据
      • 主机通过MOSI移出最高位(B7)
      • 从机通过MISO移出最高位(B7)
    • 第二个边沿(下降沿)主机和从机同时移入数据(进行数据采样)
      • 主机刚刚移出的B7进入【从机移位寄存器】的最低位
      • 从机刚刚移出的B7进入【主机移位寄存器】的最低位
  • 到此一个时钟脉冲产生完毕,一个数据位传输完毕;反复8次就完成了一个字节的数据交换
//如果主机只想交换一个字节,那么此时置SS为【高电平】就结束通信了
  • 在SS的上升沿,MOSI还可以再变化一次置默认的高or低电平(SPI未硬性规定)
  • SS高电平时,MISO置【高阻态】
//注意:
  • SS高电平时,MISO用一条中间的线表示【高阻态】
    • 当从机的SS引脚为高电平(未被主机选中),从机的MISO引脚必须切换为【高阻态】
      • 相当于引脚断开,不输出任何电平——防止一条线上有多个输出而导致电平冲突
  • SS低电平时,从机的MISO被允许开启输出
    • 在从机的SS引脚为低电平(被主机选中),从机的MISO引脚才允许变为【推挽输出】
//如果主机想交换多个字节,主机不必将SS置【高电平】,只需重复【SS低电平】之后交换一个字节的时序即可

模式2
  • 交换一个字节(模式 2
  • CPOL =1 :空闲状态时, SCK 为高电平
  • CPHA =0 SCK 第一个边沿移入数据,第二个边沿移出数据
//与【模式0】相比SCK极性取反
模式3
  • 交换一个字节(模式 3
  • CPOL =1 :空闲状态时, SCK 为高电平
  • CPHA =1 SCK 第一个边沿移出数据,第二个边沿移入数据
//与【模式1】相比SCK极性取反

五、SPI时序
SPI对字节流功能的规定:采用【指令码+读写数据】模型
  • SPI起始后,第一个交换发送给从机的数据叫作【指令码】
    • 在从机中对应有一个【指令集】,在起始后第一个字节发送【指令集】里面的数据,就能指导从机完成相应功能
  • 不同指令可以有不同的数据个数
    • 有的指令只需要一个字节的指令码就可以完成
    • 有的指令后面需要再跟需要读写的数据
//区别I2C:规定有效数据流第一个字节是寄存器地址,之后依次是读写的数据,采用【读写寄存器】模型
基于【SPI模式0】
发送指令
  • SS 指定的设备,发送指令( 0x06
    • 在【W25Q64芯片】中0x06代表写使能
//空闲状态时SS为【高电平】、SCK为【低电平】、MOSI和MISO的默认电平没有严格规定
//SCK低电平是【变化】的时期,SCK高电平是【读取】的时期
  • SS产生下降沿,时序开始
    • 下降沿时刻,MOSI和MISO变换数据
      • MOSI,由于指令码最高位仍然是0,此处保持不变
      • MISO,从机现在没有数据发给主机,因为STM32的MISO是上拉输入,所以此处MISO为高电平
  • SCK第一个上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到0
    • 下降沿时刻,MOSI和MISO变换数据
  • 之后的时序不变,当主机要发送数据1时序开始变化
    • 下降沿时刻数据移出,主机将1移出到MOSI,MOSI为高电平
    • 上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到1
  • 之后的时序不变,当主机要发送数据0时序开始变化
    • 下降沿时刻数据移出,主机将0移出到MOSI,MOSI为低电平
    • 上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到0
  • 总结:主机用【0x06】换来了从机的【0xFF】
    • 实际上从机并没有输出任何东西,【0xFF】是默认的高电平——达到主机写入从机的目的
指定地址写
  • SS 指定的设备,发送写指令( 0x02 ),随后在指定地址(Address[23:0])下,写入指定数据(Data)
//空闲状态时SS为【高电平】、SCK为【低电平】、MOSI和MISO的默认电平没有严格规定
//SCK低电平是【变化】的时期,SCK高电平是【读取】的时期
  • SS产生下降沿,时序开始
    • 下降沿时刻,MOSI和MISO变换数据
      • MOSI,由于指令码最高位仍然是0,此处保持不变
      • MISO,从机现在没有数据发给主机,因为STM32的MISO是上拉输入,所以此处MISO为高电平
  • SCK第一个上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到0
    • 下降沿时刻,MOSI和MISO变换数据
  • 之后的时序不变,当主机要发送数据1时序开始变化
    • 下降沿时刻数据移出,主机将1移出到MOSI,MOSI为高电平
    • 上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到1
  • 之后的时序不变,当主机要发送数据0时序开始变化
    • 下降沿时刻数据移出,主机将0移出到MOSI,MOSI为低电平
    • 上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到0
  • 总结:主机用【0x02】换来了从机的【0xFF】
    • 实际上从机并没有输出任何东西,【0xFF】是默认的高电平——达到主机写入从机的目的
//发出写字节指令【0x02】,之后跟着写字节的地址和具体数据
  • 由于是【模式0】(相位提前)——在最后一个SCK下降沿,将下一个字节的最高位提前放到MOSI上
    • 确保下一个字节时序开始时能直接移入数据
  • 下一个字节的最高位仍然是0
  • SCK第一个上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到0
    • 下降沿时刻,MOSI和MISO变换数据
  • 之后的时序不变,当主机要发送数据1时序开始变化
    • 下降沿时刻数据移出,主机将1移出到MOSI,MOSI为高电平
    • 上升沿进行数据采样(移入数据), 主机输入得到1,从机输入得到1
  • 之后的时序不变,当主机要发送数据0时序开始变化
    • 下降沿时刻数据移出,主机将0移出到MOSI,MOSI为低电平
    • 上升沿进行数据采样(移入数据), 主机输入得到1,从机输入得到0
  • 之后的时序不变,当主机要发送数据0时序开始变化
    • 下降沿时刻数据移出,主机将0移出到MOSI,MOSI为低电平
    • 上升沿进行数据采样(移入数据), 主机输入得到1,从机输入得到1
  • 总结:主机用【0x12】换来了从机的【0xFF】
    • 实际上从机并没有输出任何东西,【0xFF】是默认的高电平——达到主机写入从机的目的
//根据【W25Q64芯片】的规定,写指令之后的字节定义为地址高位, 因此【0x12】表示发送地址的【23~16位】
之后两个字节发送时序分别发送了数据【0x34】、【0x56】
————从机得到指定地址【0x123456】
  • 发送写入从机指定地址的内容
    • 调用发送一个字节数据【0x55】,表示主机在从机【0x123456】地址下写入数据【0x55】
//如果主机只想交换一个字节,那么此时置SS为【高电平】就结束通信了
//如果主机想交换多个字节,主机不必将SS置【高电平】,只需重复【SS低电平】之后交换一个字节的时序即可
  • 在SCK的下降沿,MOSI和MISO还需要再变化一次(提前移入下一个字节的B7)
    • 相位提前,因此下个字节的数据会在前一个传输时序的最后露头
      • 方便下一个字节传输时序第一个SCK上升沿来移入数据
  • SPI里也有和I2C一样的地址指针,每读写一个字节,地址指针自动加1
//发送一个字节不终止,继续发送的字节就会依次写入到后续的存储空间里
//另外可以看到,由于整个流程只需要主机发送功能,并没有主机接收的需求,所以MISO线路始终“挂机”
指定地址读
  • SS 指定的设备,发送读指令( 0x03 ),随后在指定地址(Address[23:0])下,读取从机数据(Data)
//空闲状态时SS为【高电平】、SCK为【低电平】、MOSI和MISO的默认电平没有严格规定
//SCK低电平是【变化】的时期,SCK高电平是【读取】的时期
  • SS产生下降沿,时序开始
    • 下降沿时刻,MOSI和MISO变换数据
      • MOSI,由于指令码最高位仍然是0,此处保持不变
      • MISO,从机现在没有数据发给主机,因为STM32的MISO是上拉输入,所以此处MISO为高电平
  • SCK第一个上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到0
    • 下降沿时刻,MOSI和MISO变换数据
  • 之后的时序不变,当主机要发送数据1时序开始变化
    • 下降沿时刻数据移出,主机将1移出到MOSI,MOSI为高电平
    • 上升沿进行数据采样(移入数据), 主机输入得到1,从机输入得到1
  • 之后的时序不变,当主机要发送数据0时序开始变化
    • 下降沿时刻数据移出,主机将0移出到MOSI,MOSI为低电平
    • 上升沿进行数据采样(移入数据), 主机输入得到1,从机输入得到0
  • 总结:主机用【0x03】换来了从机的【0xFF】
    • 实际上从机并没有输出任何东西,【0xFF】是默认的高电平——达到主机读取从机的目的
//发出读字节指令【0x03】,之后跟着读字节的地址和具体数据
  • 由于是【模式0】(相位提前)——在最后一个SCK下降沿,将下一个字节的最高位提前放到MOSI上
    • 确保下一个字节时序开始时能直接移入数据
  • 下一个字节的最高位仍然是0
  • SCK第一个上升沿进行数据采样(移入数据),主机输入得到1,从机输入得到0
    • 下降沿时刻,MOSI和MISO变换数据
  • 之后的时序不变,当主机要发送数据1时序开始变化
    • 下降沿时刻数据移出,主机将1移出到MOSI,MOSI为高电平
    • 上升沿进行数据采样(移入数据), 主机输入得到1,从机输入得到1
  • 之后的时序不变,当主机要发送数据0时序开始变化
    • 下降沿时刻数据移出,主机将0移出到MOSI,MOSI为低电平
    • 上升沿进行数据采样(移入数据), 主机输入得到1,从机输入得到0
  • 之后的时序不变,当主机要发送数据0时序开始变化
    • 下降沿时刻数据移出,主机将0移出到MOSI,MOSI为低电平
    • 上升沿进行数据采样(移入数据), 主机输入得到1,从机输入得到1
  • 总结:主机用【0x12】换来了从机的【0xFF】
    • 实际上从机并没有输出任何东西,【0xFF】是默认的高电平——达到主机写入从机的目的
//根据【W25Q64芯片】的规定,写指令之后的字节定义为地址高位, 因此【0x12】表示发送地址的【23~16位】
之后两个字节发送时序分别发送了数据【0x34】、【0x56】
————从机得到指定地址【0x123456】
  • 指定地址之后,主机开始接收数据
    • 三个指定地址的字节结束后,主机用【0xFF】换取从机的【0x55】
      • 实际上主机并没有输出任何东西,【0xFF】是默认的高电平——达到主机读取从机的目的
//如果主机需要读取多个从机数据,则继续将MOSI线路拉高以换取从机数据即可
  • 最后数据传输完毕,SS置【高电平】,时序结束

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值