当前位置:首页 > 新闻资讯 > FPGA之家动态 >

【精品博文】详细解析FPGA与STM32的SPI通信(一)

时间:2024-08-03      来源:网络搜集 关于我们 0

赢一个双肩背包

有多难?

戳一下试试看!

→_→

长摁识别

【主题】:详细解析FPGA与STM32的SPI通信(一)

【作者】:LinCoding

【时间】:2016.11.26

      昨天把SPI彻底的又搞了一遍,感觉之前学STM32时学的SPI只是皮毛,这次学习FPGA时候,才真正算是把SPI吃透了。

      As we all know, SPI有四种模式,但是STM32与FPGA通信的话推荐使用SPI_CPOL_Low和SPI_CPHA_1Edge这个模式,也就是时钟信号线空闲为低,上升沿采样,因为这样更加适合FPGA进行处理。

      使用SPI要注意以下几点:

      1、时钟和片选是由主机提供,从机只负责接收。

      2、对STM32来说,片选可选用硬件或软件,具体有什么区别请看数据手册。

      3、SPI的通信速率不能太快,否则数据会出错。

      4、如果进行双工通信的话,主机必须在发送完数据后多发送一个8位数据,笔者通常发送0xFF,这样从机才能将最后一个有效数据发过来,具体原因请百度,不多解释。

       先说第一点和第二点:

 主机给从机发数据进行单工通信的话很简单,因为时钟和片选是由主机提供的。

      但是,从机给主机发数据进行单工通信的时,由于时钟和片选是由主机提供的,主机并不知道从机什么时候给主机发,也就不知道什么时候给从机提供时钟信号和片选信号,当然了,也不会知道从机什么时候已经发完数据,什么时候停止提供时钟信号和片选信号。那这该怎么办?

 同样,如果是双工通信,且从机所发的数据比主机数量多,主机发完以后如果停止提供片选和时钟信号的话,从机剩余的数据就无法传送过来,那这又该怎么办?

       笔者想到了一个解决办法,就是增加一根从机与主机的连线,普通IO口即可,这根线平时为高,当从机准备向主机发数据时,把这根线拉低,主机检测到这根线拉低,则提供片选和时钟信号,当从机发完数据以后,把这根线拉高,主机检测到这根线拉高,则主机停止提供片选和时钟信号线。

       这样就完美的解决了这一问题。

       再说第三点,速率问题:

       对STM32来说,笔者使用的是SPI2,而SPI2硬件是挂在APB1总线上,也就是36Mhz低速外设总线,这样根据硬件的设定,速率可以在以下这四种中进行选择,

SPI_BaudRatePrescaler_2   2分频   -- 18Mhz SPI_BaudRatePrescaler_8   8分频   -- 4.5Mhz SPI_BaudRatePrescaler_16  16分频  -- 2.25Mhz SPI_BaudRatePrescaler_256 256分频 -- 140.625Khz

 但是要注意,速率太快会出错!笔者FPGA使用100Mhz速率,按说可以采样到18Mhz的数据,但是,当STM32的SPI设置为18Mhz时会出错,4.5Mhz及以下均没有问题。当笔者将FPGA提高到200Mhz时,SPI的18Mhz仍然不行,因此笔者怀疑,频率太高的话对布局布线要求较高,而笔者采用的是杜邦线进行连接,应该是使用杜邦线不能满足那么高频率的要求。

       好了,基本上就是上面四个注意点,剩下的就是时序问题了,下面开始解析整个系统的程序:

       STM32的程序相对简单,那就先说STM32的程序吧。

void SPI2_Init(void) {   GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef  SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,  ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB GPIO_SetBits(GPIOB, GPIO_Pin_12);   //CS GPIO_ResetBits(GPIOB, GPIO_Pin_13); //SCK GPIO_SetBits(GPIOB, GPIO_Pin_14);   //MISO GPIO_ResetBits(GPIOB, GPIO_Pin_15); //MOSI SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;   SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI2, &SPI_InitStructure);  SPI2_SetSpeed(SPI_BaudRatePrescaler_8); SPI_Cmd(SPI2, ENABLE);  }

笔者使用的是原子的例程,使用库函数进行配置是很简单的,注意的是,STM32设置为SPI_Mode_Master,SPI_DataSize_8b,SPI_CPOL_Low,SPI_CPHA_1Edge,SPI_NSS_Soft,还有就是SPI_FirstBit_MSB,这样就OK了!

int main(void) {   u8 i = 0; u8 dataTemp; delay_init();      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); uart_init(9600);   LED_Init();    SPI2_Init();   while(1) { for ( i=0; i < 255; i++ ) { dataTemp = SPI2_ReadWriteByte(i);  printf("%d\r\n",dataTemp); delay_ms(100); } dataTemp = SPI2_ReadWriteByte(0xFF); printf("%d\r\n",dataTemp); delay_ms(100); } }

主函数中就是循环发送0~255,并且接收FPGA发来的数据,打印到串口进行显示。可以看到我在发送完数据以后,特地多发了一个0xFF。

 笔者用示波器将STM32发出的数据抓了一下,见下图:

 为了图片好看,使用STM32只发送了一个0xAA,笔者采用了SPI_BaudRatePrescaler_256 256分频 -- 140.625Khz,可以看到示波器抓到的频率为140.5Khz,没差多少,数据是10101010也就是0xAA啦,下一步,就是使用FPGA模仿这个时序了。

由于内容太多,剩余的内容请看——详细解析FPGA与STM32的SPI通信(二)

未完待续~


注明:本内容来源网络,不用于商业使用,禁止转载,如有侵权,请来信到邮箱:429562386ⓐqq.com 或联系本站客服处理,感谢配合!

用户登陆

    未注册用户登录后会自动为您创建账号

提交留言