STM32 spi与FPGA的通信

本文详细介绍了STM32 SPI总线的初始化配置及与FPGA进行高效数据交互的方法,包括如何通过软件控制CS信号、读取与写入16位数据的过程,并分析了优化数据传输效率的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

STM32 spi总线通信


最近在研究SPI总线,至于协议和硬件描述就不多说了
四线包括时钟、片选、接收、发送


初始化SP
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //全双工
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                       //主模式
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;                  //16bit宽度
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;     //2--18MHz; 4--9MHz;  8--4.5MHz
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                    //高位在前
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(SPIx, &SPI_InitStructure);
  SPI_Cmd(SPIx, ENABLE);
  
SPI不能硬件控制CS,只能软件来控,就是通过将NSS设为外部GPIO来控制。
像我所做的项目是使用STM32与FPGA通信,而FPGA的SPI工作在这种一直状态
作为主设备的STM32,CS在传输数据的时候为低,传输完毕后必须拉高,这样FPGA可以判断出SPI的传输起止状态。


FPGA的数据传输格式是16bit地址+16bit数据
对于读16bit,实现如下


uint16_t spi_read(SPI_TypeDef* SPIx,uint32_t addr)
{  
  uint16_t value;
  uint16_t spi_nss;
  uint16_t add;
  uint32_t level;


  if(SPI1 == SPIx)
    spi_nss = SPI1_PIN_NSS;
  else if(SPI2 == SPIx)
    spi_nss = SPI2_PIN_NSS;


  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
  GPIO_ResetBits(GPIOA, spi_nss);  
  SPI_I2S_SendData(SPIx, addr); //0xf014 >> 2


  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
  SPI_I2S_SendData(SPIx, 0x0);


  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
  SPI_I2S_ReceiveData(SPIx);


  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
  GPIO_SetBits(GPIOA, spi_nss);
     
  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
  value = SPI_I2S_ReceiveData(SPIx);


  return value;
}
  
写函数


void spi_write(SPI_TypeDef* SPIx,uint32_t addr, uint16_t value)
{


  uint16_t spi_nss;


  uint32_t level;


  if(SPI1 == SPIx)
    spi_nss = SPI1_PIN_NSS;
  else if(SPI2 == SPIx)
    spi_nss = SPI2_PIN_NSS;



  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
  GPIO_ResetBits(GPIOA, spi_nss);  
  SPI_I2S_SendData(SPIx, addr); 


  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
  SPI_I2S_SendData(SPIx, value); 


  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
  SPI_I2S_ReceiveData(SPIx);


  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
  GPIO_SetBits(GPIOA, spi_nss);


  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
  SPI_I2S_ReceiveData(SPIx);


}  
拿write函数举例
只所以这么设计是因为
如果是函数一开始就将NSS脚拉低,然后再去send,如下
  GPIO_ResetBits(GPIOA, spi_nss);  
  while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
  SPI_I2S_SendData(SPIx, addr); 
  
这样在CS拉低一段时间后(时间大概有16个时钟周期),才有CLK,这样延时就会降低SPI的传输效率  
之前那种方式会在CS拉底后很快就有clk时钟出来


之所以写两次再读两次而不是读一次写一次也是考虑到效率的问题


如果先写一次再读一次,看波形每个数据之间有比较大的空隙是没有clk的,就是说在传输完一个数据后再
传第二个会要等一段时间,这个对速度要求比较高的设备是不允许的


还有值得注意的是:
如果SPI是主模式,那么GPIO设置为
NSS是GPIO_Mode_Out_PP
CLK是GPIO_Mode_AF_PP
MOSI是GPIO_Mode_AF_PP
MISO是GPIO_Mode_IN_FLOATING


如果SPI是从模式,那么GPIO设置为
NSS是GPIO_Mode_Out_PP
CLK是GPIO_Mode_IN_FLOATING
MOSI是GPIO_Mode_IN_FLOATING
MISO是GPIO_Mode_AF_PP
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值