历史上的今天
返回首页

历史上的今天

今天是:2024年08月29日(星期四)

正在发生

2019年08月29日 | STM32F429 >> 13. SPI 通讯

2019-08-29 来源:eefocus

本工程板级支持包文件适用于野火stm32f429 开发板。


SPI 物理层

在这里插入图片描述

SPI 通讯使用3 条总线及片选线,3 条总线分别是SCK、MOSI、MISO,片选线为SS,其作用分别为:


SS:片选信号线,也称NSS、CS。当有多个SPI 从设备与SPI 主机相连时,设备的其他信号线 SCK、MOSI及 MISO同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同只使用这 3 条总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。


I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以SPI通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。


SCK:时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为f pclk /2,两个设备之间通讯时,通讯速率受限于低速设备。


MOSI:主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。


MISO:主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

协议层

SPI 基本通讯过程

在这里插入图片描述


SPI 的起始信号:NSS 信号线由高变低。

SPI 使用MOSI 及MISO 信号线传输数据,使用SCK 信号线进行数据同步。MOSI

及 MISO数据线在 SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB先行或 LSB先行并没有作硬性规定,但要保证两个 SPI通讯设备之间使用同样的协定。

MOSI及 MISO的数据在 SCK的上升沿期间变化输出,在 SCK的下降沿时被采样。即在 SCK的下降沿时刻,MOSI及 MISO的数据有效。

SPI每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

SPI 的停止信号:NSS 信号线由低变高。

CPOL/CPHA 及通讯模式

上面讲述的时序只是 SPI中的其中一种通讯模式,SPI一共有四种通讯模式,它们的主要区别是总线空闲时 SCK的时钟状态以及数据采样时刻。

为方便说明,在此引入“时钟极性 CPOL”和“时钟相位 CPHA”的概念。


时钟极性 CPOL是指 SPI通讯设备处于空闲状态时,SCK信号线的电平信号(即 SPI通讯开始前、 NSS 线为高电平时 SCK的状态)。CPOL=0时, SCK在空闲状态时为低电平,CPOL=1 时,则相反。

时钟相位 CPHA是指数据的采样的时刻,当 CPHA=0 时,MOSI或 MISO 数据线上的信号将会在 SCK时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK的“偶数边沿”采样。


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

简单来说就是:

CPOL(0/1)控制时钟信号是正向脉冲还是反向脉冲;CPHA(0/1)控制采样信号是时钟脉冲起始还是时钟脉冲结束。


架构

在这里插入图片描述


通讯引脚

在这里插入图片描述

其中 SPI1、SPI4、SPI5、SPI6是 APB2 上的设备,最高通信速率达 45Mbtis/s,SPI2、SPI3 是 APB1上的设备,最高通信速率为 22.5Mbits/s。其它功能上没有差异。

时钟控制逻辑

SCK线的时钟信号,由波特率发生器根据“控制寄存器 CR1”中的 BR[0:2]位控制,该位是对 f pclk 时钟的分频因子,对 f pclk 的分频结果就是 SCK引脚的输出时钟频率。

在这里插入图片描述

其中的 f pclk 频率是指 SPI所在的 APB总线频率,APB1为 f pclk1 ,APB2为 f pckl2 。

通过配置“控制寄存器 CR”的“CPOL位”及“CPHA”位可以把 SPI设置成前面分析的 4 种 SPI模式。

3. 数据控制逻辑

SPI的 MOSI及 MISO 都连接到数据移位寄存器上,数据移位寄存器的内容来源于接收缓冲区及发送缓冲区以及 MISO、MOSI线。


当向外发送数据的时候,数据移位寄存器以“发送缓冲区”为数据源,把数据一位一位地通过数据线发送出去;当从外部接收数据的时候,数据移位寄存器把数据线采样到的数据一位一位地存储到“接收缓冲区”中。


通过写 SPI的“数据寄存器 DR”把数据填充到发送缓冲区中,通过 “数据寄存器 DR”,可以获取接收缓冲区中的内容。


其中数据帧长度可以通过“控制寄存器 CR1”的“DFF位”配置成 8 位及 16 位模式;置“LSBFIRST 位”可选择 MSB先行还是 LSB先行。

4. 整体控制逻辑

整体控制逻辑负责协调整个 SPI外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变,基本的控制参数包括前面提到的 SPI模式、波特率、LSB先行、主从模式、单双向模式等等。在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器(SR)”,我们只要读取状态寄存器相关的寄存器位,就可以了解 SPI的工作状态了。除此之外,控制逻辑还根据要求,负责控制产生 SPI中断信号、DMA 请求及控制NSS 信号线。


实际应用中,我们一般不使用 STM32 SPI外设的标准 NSS 信号线,而是更简单地使用

普通的 GPIO,软件控制它的电平输出,从而产生通讯起始和停止信号。


通讯过程

在这里插入图片描述

此为STM32 作为SPI 通讯的主机端时的数据收发过程 ↑


主模式收发流程及事件说明:

控制NSS 信号线,产生起始信号(图中没有画出);

把要发送的数据写入到“数据寄存器 DR”中,该数据会被存储到发送缓冲区;

通讯开始,SCK时钟开始运行。MOSI把发送缓冲区中的数据一位一位地传输出去;MISO 则把数据一位一位地存储进接收缓冲区中(全双工);

当发送完一帧数据的时候,“状态寄存器 SR”中的“TXE 标志位”会被置 1,表示传输完一帧,发送缓冲区已空;类似地,当接收完一帧数据的时候,“RXNE标志位”会被置 1,表示传输完一帧,接收缓冲区非空;

等待到“TXE标志位”为 1 时,若还要继续发送数据,则再次往“数据寄存器DR”写入数据即可;等待到“RXNE标志位”为 1时,通过读取“数据寄存器DR”可以获取接收缓冲区中的内容。

SPI 读写串行FLASH

FLASH(闪存)与EEPROM 都是掉电后数据不丢失的存储器,但FLASH 的容量普遍大于EEPROM;

存储控制上,最主要的区别是FLASH 芯片只能一大片一大片地擦写,而EEPROM 可以单个字节擦写。

在这里插入图片描述

STM32 的NSS 引脚是一个普通的GPIO,不是SPI 的专用NSS 引脚,需要用软件控制


控制FLASH 的指令

在与EEPROM 进行I²C 通讯时,第一个和第二个字节的数据分别会被视作硬件地址和内部存储矩阵地址。

但在与FLASH 进行SPI 通讯 时,FLASH 芯片自定义了很多指令,对于STM32 主机来说,这些字节就是普通的数据,而对于FLASH 来说,不同的数据将会被翻译成不同的指令。


其常用芯片指令有:

在这里插入图片描述

该表中的第一列为指令名,第二列为指令编码,第三至第 N 列的具体内容根据指令的不同而有不同的含义。

其中带括号的字节参数,方向为 FLASH 向主机传输,即命令响应,不带括号的则为主机向 FLASH 传输。

表中“A0~A23”指 FLASH 芯片内部存储器组织的地址;“M0~M7”为厂商号(MANUFACTURER ID);“ID0-ID15”为 FLASH 芯片的ID;“dummy”指该处可为任意数据;“D0~D7”为 FLASH 内部存储矩阵的内容。


编程要点:


初始化通讯使用的目标引脚及端口时钟;

使能SPI 外设的时钟;

配置SPI 外设的模式、地址、速率等参数并使能SPI 外设;

编写基本SPI 按字节收发的函数;

编写对FLASH 擦除及读写操作的函数;

编写测试程序,对读写数据进行校验。

以下是各个模块的分代码

==============总代码在这


bsp_spi_flash.h

/**

  ******************************************************************************

  * @file    bsp_spi_flash.h

  * @author  Waao

  * @version V1.0.0

  * @date    19-Jan-2019

  * @brief   This file contains some board support package's definitions for the SPI.

  *            

  ******************************************************************************

  * @attention

  *

  * None

*

  ******************************************************************************

  */

#ifndef _BSP_SPI_FLASH_H_

#define _BSP_SPI_FLASH_H_



#include

#include

#include



#define SPIT_FLAG_TIMEOUT          ((uint32_t)0x1000)

#define SPIT_LONG_TIMEOUT          ((uint32_t)(10*SPIT_FLAG_TIMEOUT))


#define SPI_FLASH_PageSize              256

#define SPI_FLASH_PerWritePageSize      256


#define FLASH_ERROR(fmt, arg...)   printf("Error Code:"fmt"n", ##arg)


//============================ SPI =================================

#define SPI_                       SPI5 

#define SPI_CLK                    RCC_APB2Periph_SPI5


//====================== Signal Line GPIO ==========================

#define SPI_GPIO_CLK               RCC_AHB1Periph_GPIOF

#define SPI_GPIO_PORT              GPIOF


//=== NSS ===

#define SPI_NSS_GPIO_PORT          GPIOF          

#define SPI_NSS_GPIO_PIN           GPIO_Pin_6          

#define SPI_NSS_GPIO_RCC           RCC_AHB1Periph_GPIOF  


//=== SCK ===

#define SPI_SCK_GPIO_PORT          GPIOF          

#define SPI_SCK_GPIO_PIN           GPIO_Pin_7          

#define SPI_SCK_GPIO_RCC           RCC_AHB1Periph_GPIOF  

#define SPI_SCK_GPIO_PinSource     GPIO_PinSource7

#define SPI_SCK_GPIO_AF            GPIO_AF_SPI5


//=== MISO ===

#define SPI_MISO_GPIO_PORT         GPIOF          

#define SPI_MISO_GPIO_PIN          GPIO_Pin_8          

#define SPI_MISO_GPIO_RCC          RCC_AHB1Periph_GPIOF  

#define SPI_MISO_GPIO_PinSource    GPIO_PinSource8

#define SPI_MISO_GPIO_AF           GPIO_AF_SPI5

  

//=== MOSI ===

#define SPI_MOSI_GPIO_PORT         GPIOF          

#define SPI_MOSI_GPIO_PIN          GPIO_Pin_9          

#define SPI_MOSI_GPIO_RCC          RCC_AHB1Periph_GPIOF          

#define SPI_MOSI_GPIO_PinSource    GPIO_PinSource9

#define SPI_MOSI_GPIO_AF           GPIO_AF_SPI5


//=== CS/NSS Control ===

#define SPI_FLASH_CS_1             {SPI_NSS_GPIO_PORT->BSRRL=SPI_NSS_GPIO_PIN;}

#define SPI_FLASH_CS_0             {SPI_NSS_GPIO_PORT->BSRRH=SPI_NSS_GPIO_PIN;}

//================================================================


//=== Commonly Used Command ===

#define W25X_WriteEnable        0x06 

#define W25X_WriteDisable        0x04 

#define W25X_ReadStatusReg      0x05 

#define W25X_WriteStatusReg      0x01 

#define W25X_ReadData          0x03 

#define W25X_FastReadData        0x0B 

#define W25X_FastReadDual        0x3B 

#define W25X_PageProgram        0x02 

#define W25X_BlockErase        0xD8 

#define W25X_SectorErase        0x20 

#define W25X_ChipErase        0xC7 

#define W25X_PowerDown        0xB9 

#define W25X_ReleasePowerDown      0xAB 

#define W25X_DeviceID          0xAB 

#define W25X_ManufactDeviceID    0x90 

#define W25X_JedecDeviceID      0x9F 


#define WIP_Flag                   0x01  /* Write In Progress (WIP) flag */

#define Dummy_Byte                 0xFF



void SPI_GPIO_Config(void);

void SPI_Config(void);

uint8_t SPI_FLASH_SendByte(uint8_t byte);

uint8_t SPI_FLASH_ReadByte(void);

uint32_t SPI_FLASH_ReadID(void);

u32 SPI_FLASH_ReadDeviceID(void);

void SPI_FLASH_WriteEnable(void);

u8 SPI_FLASH_WaitForWriteEnd(void);

void SPI_FLASH_SectorErase(u32 SectorAddr);

void SPI_FLASH_PageWrite(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite);

void SPI_FLASH_BufferWrite(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite);

void SPI_FLASH_BufferRead(u8 *pBuffer, u32 ReadAddr, u16 NumByteToWrite);

uint8_t SPI_TIMEOUT_Callback(uint8_t errorcode);



#endif



bsp_spi_flash.c

/**

  ******************************************************************************

  * @file    bsp_spi_flash.c

  * @author  Waao

  * @version V1.0.0

  * @date    19-Jan-2019

  * @brief   This file contains some board support package's functions for the SPI.

  *            

  ******************************************************************************

  * @attention

  *

  * None

*

  ******************************************************************************

  */


#include



static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT;

static uint32_t WAITING_TIME = SPIT_FLAG_TIMEOUT;


/**

  * @brief  Initialize the SPI_GPIO.

  * @param  None  

  * @retval None

  */

void SPI_GPIO_Config(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_AHB1PeriphClockCmd(SPI_GPIO_CLK, ENABLE);

GPIO_PinAFConfig(SPI_SCK_GPIO_PORT, SPI_SCK_GPIO_PinSource, SPI_SCK_GPIO_AF);

推荐阅读

史海拾趣

AMRI Enterprise Co Ltd公司的发展小趣事

面对国内市场的饱和竞争,AMRI Enterprise Co Ltd积极实施国际化战略,拓展海外市场。公司通过参加国际电子展会、建立海外销售渠道等方式,不断提升品牌知名度和产品影响力。同时,AMRI还针对不同国家和地区的市场需求,调整产品策略,提供定制化的解决方案。这一战略的实施,使得AMRI的产品成功打入多个国际市场,实现了业务的快速增长。

Hi-Tron Semiconductor Corp公司的发展小趣事

品质是AMRI Enterprise Co Ltd的生命线。公司始终坚持品质至上的原则,通过严格的质量管理体系和持续改进的工艺流程,确保产品的稳定性和可靠性。同时,AMRI还注重品牌建设,通过提升品牌形象和塑造企业文化,增强客户对公司的信任度和忠诚度。这些努力使得AMRI的产品在市场上赢得了良好的口碑和广泛的认可。

全智景(Allvision)公司的发展小趣事

随着技术的不断进步,全智景公司开始寻求跨界合作的机会,以拓展其技术的应用领域。公司与多家汽车制造商达成了合作协议,将其电子视觉技术应用于汽车自动驾驶系统。通过精准的图像识别和处理,全智景的技术帮助汽车实现了更高级别的自动驾驶功能,提升了行车安全性和驾驶体验。这一跨界合作不仅拓宽了全智景公司的业务范围,也为其带来了更多的商业机会。

Hind Rectifiers Ltd公司的发展小趣事

在追求经济效益的同时,全智景公司也积极履行社会责任,致力于可持续发展。公司注重环保和节能技术的研发和应用,推出了多款绿色环保的产品。同时,全智景公司还积极参与社会公益事业,捐款捐物支持灾区重建和贫困地区的教育事业。这些举措不仅彰显了公司的社会责任感,也为公司的可持续发展奠定了坚实的基础。

通过以上五个故事,我们可以看到全智景公司在电子行业里的发展历程。从技术突破引领市场,到跨界合作拓展应用;从持续创新引领潮流,到全球化战略助力发展;再到履行社会责任与可持续发展。全智景公司以其坚定的信念和不懈的努力,在电子行业中取得了显著的成就。

Exar公司的发展小趣事

Exar公司通过收购和兼并多家公司,实现了技术融合和产品线拓展。其中,Neterion和Hifn的收购是Exar发展历程中的重要里程碑。Neterion在万兆以太网数据传输技术和虚拟化IO技术方面处于领先地位,而Hifn则在数据精简、加密、压缩技术方面拥有领先技术。Exar将这两家公司的技术融合在一起,推出了一系列高性能的数据传输和存储产品,进一步巩固了其在行业中的领先地位。

DMEL Inc公司的发展小趣事

为了提升产品质量和用户体验,DMEL Inc不断加大对生产过程的管控力度。公司引进了先进的生产设备和管理系统,建立了严格的质量检测体系。同时,DMEL Inc还加强了与供应商的合作,确保原材料的质量和供应的稳定性。这些措施的实施,使得DMEL Inc的产品质量得到了显著提升,赢得了消费者的信赖和好评。

问答坊 | AI 解惑

手机电池驱动

请问哪位大侠对手机的电池驱动熟啊,电池充电是怎么实现的。可不可以发个最简单的源程序并说明下。不需要能运行,只要说明下原理的那种。…

查看全部问答>

VS2008+DDK+DDKWizard编译得不到SYS怎么办!?

1>------ 已启动生成: 项目: EmptyDriver1.WXP, 配置: WXP checked Win32 ------ 1>正在执行生成文件项目操作 1>OSR DDKBUILD.CMD V7.3/r27 (2008-09-06) - OSR, Open Systems Resources, Inc. 1>DDKBLD: >> Setting custom environment vari ...…

查看全部问答>

windows mobile中CString长度问题!

为什么在wince中CString的长度只有1024呢,有没什么办法可以突破这个限制呢? 在网上搜索了下没有找到结局方法,有知道的麻烦给我说下 ,谢谢了。…

查看全部问答>

再问一个问题?

我把memory空间设置成long memory后,cosmic会不会还生成stack空间还有,下图,我把ram设置成0x0100到0x027f,会不会和其他地址有冲突…

查看全部问答>

请问USB字符串描述符

                                 请问在USB设备向主机发送字符串描述符时,怎么定义一个从指定FLASH空间地址中读取的变量数据呢?如何将读取的内容 ...…

查看全部问答>

stm8的电机库哪里有下载?

你可以看看這裡是不是你需要的 http://www.st.com/mcu/modules.ph ... ;DEV=STM8/128-MCKIT…

查看全部问答>

为什么stm32cpu老烧啊

自己做的一块板,用J-link下载,有时候用几次后,有时候用好长时间后,发现芯片发烫,断电一测,cpu的3.3v和地之间的电阻开始慢慢变小,芯片烧了。已经发生3次了。芯片型号是stm32f103vBT6. 是否和带着J-link,然后,我经常按reset健有关?…

查看全部问答>

MSP430f149供电问题

请问一下,如果JTAG和5V开关电源同时供电的话会有什么问题呢? 我的5V开关电源烧掉了,是因为JTAG的原因吗?…

查看全部问答>

入手LM3S8962后自己写的第一个程序,为什么编译不通?求高人指点。

程序编译效果见附件。我的问题如下:1、这个程序里还需要include哪些文件?2、这个程序已经include的文件,哪些是多余的?3、已经include的文件,都是做什么用的?(例如:inc/hw_types.h是干什么用的,为什么要这个文件)?4、我怎么知道我需要incl ...…

查看全部问答>