本文作者:李柱明本文链接:https://www.cnblogs.com/lizhuming/p/13907267.html
前言
- SPI 介绍 为搜集百度资料+个人理解
- 其余为原创(有误请指正)
- 集四种模式于一身
笔录草稿
SPI介绍
- SPI 协议简介SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速 全双工 的通信总线。是一个环形总线结构由 ss(cs)、sck、sdi、sdo 构成其时序主要是在 sck 的控制下,两个双向移位寄存器进行数据交换。
- 物理线说明SS从设备选择信号线,常称为片选信号线,也称为NSS、CS。用于选择从机。SCK (Serial Clock)时钟信号线用于通讯数据同步。MOSI (Master Output, Slave Input)主设备输出/从设备输入引脚。主机发出,从机接收。MISO (Master Input,,Slave Output)主设备输入/从设备输出引脚。从机发出,主机接收。
- SPI 四种模式请移步到下面章节学习
- SPI的协议层SPI协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。基本通讯过程图解标号1:NSS信号线由高变低,是SPI通讯的起始信号。标号6:NSS信号由低变高,是SPI通讯的停止信号。
- 简单时序图
- 模式时序图
SPI四种模式 **
- 四种模式由 CPOL 和 CPHA 组合区分
- CPOL时钟极性是指SPI通讯设备处于空闲状态时,SCK信号线的电平信号为 0 时SCK 空闲状态为 低电平为 1 时SCK 空闲状态为 高电平
- CPHA时钟相位是指数据的采样的时刻为 0 时MOSI或MISO数据线上的信号将会在SCK时钟线的“奇数边沿”被采样。(即是第一个边沿)这种模式适合那种从设备一旦被片选后就输出数据到MISO线上。为 1 时数据线在SCK的“偶数边沿”采样。(即是第二个边沿)这种模式适合那种从设备被片选后还需要一个时钟才能 输出数据到MISO线上。
- 四种模式( CPOL, CPHA )模式 0:( 0, 0 )SCK空闲为 低电平 ,数据在SCK的 上升沿 被采样模式 1:( 0, 1 )SCK空闲为 低电平 ,数据在SCK的 下降沿 被采样模式 2:( 1, 0 )SCK空闲为 高电平 ,数据在SCK的 下降沿 被采样模式 3:( 1, 1 )SCK空闲为 高电平 ,数据在SCK的 上升沿 被采样
SPI 驱动框架 **
框架
- 实现方法参考I2C设备驱动拆解
- 自己先在写出四种模式的读写时序,便会发现以下规律
- 读写的逻辑差不多都一样,只是 SCK 信号线出现的位置及高低电平会因不同模式而不同。( 这里我就不分别写出4种模式的单独实现了,直接上规律表,然后实现统一的源码 )
由上规律得出 支持四种模式的 SPI 读写源码
- SPI 写函数
/** * @brief SPI 写函数 * @param * @retval * @author lzm */void spiWriteOneByte(eSPI_ID id, unsigned char data){unsigned char i;const spi_t * spi = &spiDriverElem[id]; // 位置1if(spi->CPHA){spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);}for(i=0; i<8; i++){ // 位置2spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA));if(data & 0x80){spiMosiOutHi(spi);}else{spiMosiOutLo(spi);}data <<= 1;spi->delayUsFun(spi->readDelayUsCnt); // 位置3spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA));} // 位置4if(!(spi->CPHA)){spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);}}
- SPI 读函数
/** * @brief SPI 读函数 * @param * @retval * @author lzm */unsigned char spiReadOneByte(eSPI_ID id){unsigned char i;unsigned char ret;const spi_t * spi = &spiDriverElem[id]; // 位置1 for(i=0; i<8; i++){ // 位置2spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA));ret <<= 1;if(spiMisoIn(spi))ret |= 0x01;elseret &= 0xfe;spi->delayUsFun(spi->readDelayUsCnt); // 位置3spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA));} // 位置4spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);return ret;}
- SPI 读写函数
/** * @brief SPI 读写一体函数 * @param * @retval * @author lzm */unsigned char spiRWOneByte(eSPI_ID id, unsigned char data){unsigned char i;unsigned char ret;const spi_t * spi = &spiDriverElem[id]; // 位置1if(spi->CPHA){spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);}for(i=0; i<8; i++){ // 位置2spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA));if(data & 0x80){spiMosiOutHi(spi);}else{spiMosiOutLo(spi);}data <<= 1;spi->delayUsFun(spi->readDelayUsCnt); // 位置3spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA));ret <<= 1;if(spiMisoIn(spi))ret |= 0x01;elseret &= 0xfe;spi->delayUsFun(spi->readDelayUsCnt);} // 位置4if(!(spi->CPHA)){spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);}}
本文作者:李柱明本文链接:https://www.cnblogs.com/lizhuming/p/13907267.html