FPGA与STM32的SPI通信 - FPGA主 STM32从 前言最近项目需要从FPGA向STM32传输数据选用SPI通信传输传输数据为32位后改为8位。之前写了个stm32从机32位数据接收的因个人能力不足没成功改成接收8位数据的代码于是直接让从机接收32位数据主机传8位数据取第一组8位数据得了。具体SPI通信原理就不赘述了网上很多大神有详细讲解过此处只贴上自己项目关于SPI通信的代码作学习记录给有需要的朋友参考。本人水平不足代码写的冗余复杂见谅目的fpga与stm32通过spi通信进行32位数据传输fpga--主机stm32--从机SPI2端口方法fpga与stm32分别编写SPI通信模块stm32从机借助SPIDMA来接收数据工具fpga开发板ALINX AX7010 与STM32RCT6SPI选用模式mode0FPGA主机SPI通信代码发送8位可自行更改为发送32位FPGA主机SPI通信代码--顶层模块FPGA主机SPI通信代码--顶层模块module va_sine_wave( input sys_clk,///系统时钟 input rst_n, input [23:0] F,//上位机传进fpga的数据 output spi_sck,//spi通讯 output spi_cs, output spi_mosi ); //clk_1m时钟生成 wire clk_1m; clk_1m clk_1m_inst( .sys_clk(sys_clk ), .rst_n(rst_n), .clk_1m(clk_1m) ); //数据转换 wire [7:0] F_data; F_Convert F_Convert_inst( .F(F), .data(F_data) ); //SPI-test wire send_done; wire rx_done,rx_en; wire tx_done,tx_en; reg spi_miso; //assign rx_en 1; SPI_MasterToSlave SPI_MasterToSlave_inst( .CLK(clk_1m ),//1MHz的时钟 .RST_N(rst_n), .Send_Data(F_data),//要传输给stm32的数据 .tx_en(d1), .SCK(spi_sck), .CS(spi_cs), .MOSI(spi_mosi), .MISO(spi_miso), .tx_done(tx_done), .send_done(send_done) ); endmoduleFPGA主机SPI通信代码--1Mhz分频时钟FPGA主机SPI通信代码--1Mhz分频时钟module clk_1m( input sys_clk, input rst_n, output reg clk_1m ); reg [25:0] clk_cnt ; //分频计数器 //1Mhz分频时钟 always (posedge sys_clk or negedge rst_n) begin if (!rst_n) begin clk_cnt 5d0; clk_1m 1b0; end else if (clk_cnt 26d24) clk_cnt clk_cnt 1b1; else begin clk_cnt 5d0; clk_1m ~ clk_1m; end end endmoduleFPGA主机SPI通信代码--SPI_MasterToSlaveFPGA主机SPI通信代码--SPI_MasterToSlavemodule SPI_MasterToSlave( input CLK, input RST_N, input [7:0] Send_Data,//需要spi发送给从机的数据-8位 input tx_en,//spi发送使能 input rx_en,//spi接收使能 output reg SCK, output reg CS, output reg MOSI,//OUTPUT FPGAfpga主机-fpga发送给从机的数据 input MISO,//INPUT FPGAfpga接收从机传来的数据 output reg tx_done,//发送完成标志 output reg send_done//每位数据发送完成标志 ); reg [4:0] tx_state;//这里修改一下位数可以改为发送32位数据 always(posedge CLK or negedge RST_N) begin if(RST_N 0)//复位 begin SCK 1b0; //SCK初始电平为低 CS 1b1; //CS初始电平为高 MOSI 1b0; //MOSI初始电平为低 tx_done 1b0; send_done 1b0; tx_state 4d0; end else if(tx_en)//产生SPI时序 begin CS 0;//CS拉低准备数据传输 case(tx_state) 5d1,5d3,5d5,5d7,5d9,5d11,5d13,5d15://每次放置数据完毕后 在此拉高时钟线便于下次的下降沿产生 begin SCK 1b1;//准备在下降沿放置数据提前将SCK拉高 tx_state tx_state 4d1;//切换为数据放置状态(每发完1bit数据进入此一次将时钟线拉高) tx_done 1b0; send_done 1b0; end 5d0://第7位数据发送状态 begin MOSI Send_Data[7];//D7数据 SCK 1b0;//在下降沿放置数据 tx_state tx_state 4d1;//切换状态 tx_done 1b0; send_done 1b1; end 5d2://第6位数据发送状态 begin MOSI Send_Data[6];//D6数据 SCK 1b0;//在下降沿放置数据 tx_state tx_state 4d1;//切换状态 tx_done 1b0; send_done 1b1; end 5d4://第5位数据发送状态 begin MOSI Send_Data[5];//D5数据 SCK 1b0;//在下降沿放置数据 tx_state tx_state 4d1;//切换状态 tx_done 1b0; send_done 1b1; end 5d6://第4位数据发送状态 begin MOSI Send_Data[4];//D4数据 SCK 1b0;//在下降沿放置数据 tx_state tx_state 4d1;//切换状态 tx_done 1b0; send_done 1b1; end 5d8://第3位数据发送状态 begin MOSI Send_Data[3];//D3数据 SCK 1b0;//在下降沿放置数据 tx_state tx_state 4d1;//切换状态 tx_done 1b0; send_done 1b1; end 5d10://第2位数据发送状态 begin MOSI Send_Data[2];//D2数据 SCK 1b0;//在下降沿放置数据 tx_state tx_state 4d1;//切换状态 tx_done 1b0; send_done 1b1; end 5d12://第1位数据发送状态 begin MOSI Send_Data[1];//D1数据 SCK 1b0;//在下降沿放置数据 tx_state tx_state 4d1;//切换状态 tx_done 1b0; send_done 1b1; end 5d14://第0位数据发送状态 begin MOSI Send_Data[0]; SCK 1b0; tx_state tx_state 4d1;//4d15; // 修改为15继续走一个状态来释放CS,走到16释放CS(目的是实现stm32的SPI通信的硬件控制稳定传输数据不然CS一直处于低电平会一直发送数据给STM32,传输的数据是乱跳的 tx_done 1b1; send_done 1b1; end 5d16:begin CS 1b1; // 拉高CS释放总线 tx_state 4d0; // 回到初始状态 tx_done 1b0; send_done 1b0;; end default: begin tx_state 4d0; tx_done 1b0; send_done 1b0; end endcase end else begin tx_done 1b0; tx_state 4d0; CS 1b1; SCK 1b0; MOSI 1b0; send_done 1b0; end end endmoduleSTM32从机SPI通信代码接收32位数据STM32从机SPI通信代码--fpga.cSTM32从机SPI通信代码--fpga.c#include stm32f10x.h #include fpga.h #include delay.h #include stm32f10x_spi.h #include stm32f10x_dma.h // SPI2_SCK - PB13 // SPI2_MISO - PB14 // SPI2_MOSI - PB15 // SPI2_NSS - PB12 static uint8_t spi_buf[4] {0}; volatile uint8_t spi_data_ready 0; volatile uint32_t g_frequency_data 0; void SPI2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; DMA_InitTypeDef DMA_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE ); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // SCK GPIO_InitStructure.GPIO_Pin GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, GPIO_InitStructure); // MISO GPIO_InitStructure.GPIO_Pin GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // MOSI GPIO_InitStructure.GPIO_Pin GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, GPIO_InitStructure); // NSS GPIO_InitStructure.GPIO_Pin SPI2_CS_GPIO_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(SPI2_CS_GPIO_PORT, GPIO_InitStructure); SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Slave;//SPI--从机模式 SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; //SPI的模式mode0 SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge;//SPI的模式mode0 SPI_InitStructure.SPI_NSS SPI_NSS_Hard;//硬件控制 SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_16; SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI2, SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); // enableSPI2 DMA_DeInit(DMA1_Channel4); // SPI2_RX--DMA1_Channel4 DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)(SPI2-DR); DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)spi_buf[0];//如果不是传输8位数据这里应该要改改为spi_buf DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize 4; // 1byte DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode DMA_Mode_Normal;//SPI--普通模式 DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel4, DMA_InitStructure); DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); //中断优先级设置 NVIC_InitStructure.NVIC_IRQChannel DMA1_Channel4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel SPI2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); //开启DMA SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE); DMA_ClearFlag(DMA1_FLAG_TC4); DMA_Cmd(DMA1_Channel4, DISABLE); DMA_SetCurrDataCounter(DMA1_Channel4, 4); DMA_Cmd(DMA1_Channel4, ENABLE); } void DMA1_Channel4_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC4) ! RESET) { DMA_ClearITPendingBit(DMA1_IT_TC4); // g_frequency_data ((uint32_t)spi_buf[0] 24) | // ((uint32_t)spi_buf[1] 16) | // ((uint32_t)spi_buf[2] 8) | // ((uint32_t)spi_buf[3]); //此处可取32位数据但需要fpga那边自己也发送32位数据(这里记得修改一下) g_frequency_data spi_buf[0]; //因fpga只传输8位所以只取第一个数组内数据 //串口打印 printf(spi_buf[0] 0x%02X\r\n, spi_buf[0]);//串口打印 printf(spi_buf[1] 0x%02X\r\n, spi_buf[1]); printf(spi_buf[2] 0x%02X\r\n, spi_buf[2]); printf(spi_buf[3] 0x%02X\r\n, spi_buf[3]); printf(Received 32-bit data: 0x%08lX\r\n, g_frequency_data); spi_data_ready 1; } }STM32从机SPI通信代码--main.cSTM32从机SPI通信代码--main.c#include stm32f10x.h #include stm32f10x_rcc.h #include Delay.h #include PeripheralInit.h #include fpga.h #include stdio.h #include stm32f10x_spi.h #include stm32f10x_dma.h int main (void) { unsigned long FREQ; SPI2_Init(); PeripheralInit(); // 外设初始化 printf(STM32 SPI2 Slave Ready to Receive...\r\n);//串口打印 while (1) { if(spi_data_ready){ if(spi_data_ready){ FREQ g_frequency_data * 10000;//SPI收到的数据在这里使用 printf(FREQ %d\r\n, FREQ); spi_data_ready 0; Delay_5ms(10); DMA_Cmd(DMA1_Channel4, DISABLE); DMA_SetCurrDataCounter(DMA1_Channel4, 4); DMA_Cmd(DMA1_Channel4, ENABLE); } } } }效果图1.FPGA发送数据2.STM32串口打印出的数据注1.贴上去的代码仅为项目spi部分的代码实际效果没测试过需要的朋友自行更改。