131 1300 0010
其他
当前位置: 首页>> 元件技术>>其他>>
  • 导航栏目
  • 二极管
  • 整流桥
  • MOS管
  • 其他
  • STM32单片机SPI总线与FPGA的通信设计
    STM32单片机SPI总线与FPGA的通信设计
  • STM32单片机SPI总线与FPGA的通信设计
  •   发布日期: 2019-06-06  浏览次数: 2,738

    最近在研究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;

    STM32单片机SPI总线与FPGA的通信设计

    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来控制。

    像我所做的项目是使用STM32FPGA通信,而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


  • ·上一篇:
    ·下一篇:
  • 其他关联资讯
    深圳市日月辰科技有限公司
    地址:深圳市宝安区松岗镇潭头第二工业城A区27栋3楼
    电话:0755-2955 6626
    传真:0755-2978 1585
    手机:131 1300 0010
    邮箱:hu@szryc.com

    深圳市日月辰科技有限公司 版权所有:Copyright©2010-2023 www.szryc.com 电话:13113000010 粤ICP备2021111333号