FPGA与STM32的SPI通信 - FPGA主 STM32从

📅 2026/7/3 3:01:09 👁️ 阅读次数 📝 编程学习
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从机借助SPI+DMA来接收数据
工具:fpga开发板ALINX AX7010 与STM32RCT6
SPI选用模式:mode0


FPGA主机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) ); endmodule

FPGA主机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 <= 5'd0; clk_1m <= 1'b0; end else if (clk_cnt < 26'd24) clk_cnt <= clk_cnt + 1'b1; else begin clk_cnt <= 5'd0; clk_1m <= ~ clk_1m; end end endmodule

FPGA主机SPI通信代码--SPI_MasterToSlave

FPGA主机SPI通信代码--SPI_MasterToSlave

module 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 FPGA(fpga主机-fpga发送给从机的数据) input MISO,//INPUT FPGA(fpga接收从机传来的数据) 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 <= 1'b0; //SCK初始电平为低 CS <= 1'b1; //CS初始电平为高 MOSI <= 1'b0; //MOSI初始电平为低 tx_done <=1'b0; send_done <=1'b0; tx_state <= 4'd0; end else if(tx_en)//产生SPI时序 begin CS <= 0;//CS拉低准备数据传输 case(tx_state) 5'd1,5'd3,5'd5,5'd7,5'd9,5'd11,5'd13,5'd15://每次放置数据完毕后 在此拉高时钟线,便于下次的下降沿产生 begin SCK <= 1'b1;//准备在下降沿放置数据,提前将SCK拉高 tx_state <= tx_state + 4'd1;//切换为数据放置状态(每发完1bit数据进入此一次,将时钟线拉高) tx_done <=1'b0; send_done <=1'b0; end 5'd0://第7位数据发送状态 begin MOSI <= Send_Data[7];//D7数据 SCK <= 1'b0;//在下降沿放置数据 tx_state <= tx_state + 4'd1;//切换状态 tx_done <=1'b0; send_done <=1'b1; end 5'd2://第6位数据发送状态 begin MOSI <= Send_Data[6];//D6数据 SCK <= 1'b0;//在下降沿放置数据 tx_state <= tx_state + 4'd1;//切换状态 tx_done <=1'b0; send_done <=1'b1; end 5'd4://第5位数据发送状态 begin MOSI <= Send_Data[5];//D5数据 SCK <= 1'b0;//在下降沿放置数据 tx_state <= tx_state + 4'd1;//切换状态 tx_done <=1'b0; send_done <=1'b1; end 5'd6://第4位数据发送状态 begin MOSI <= Send_Data[4];//D4数据 SCK <= 1'b0;//在下降沿放置数据 tx_state <= tx_state + 4'd1;//切换状态 tx_done <=1'b0; send_done <=1'b1; end 5'd8://第3位数据发送状态 begin MOSI <= Send_Data[3];//D3数据 SCK <= 1'b0;//在下降沿放置数据 tx_state <= tx_state + 4'd1;//切换状态 tx_done <=1'b0; send_done <=1'b1; end 5'd10://第2位数据发送状态 begin MOSI <= Send_Data[2];//D2数据 SCK <= 1'b0;//在下降沿放置数据 tx_state <= tx_state + 4'd1;//切换状态 tx_done <=1'b0; send_done <=1'b1; end 5'd12://第1位数据发送状态 begin MOSI <= Send_Data[1];//D1数据 SCK <= 1'b0;//在下降沿放置数据 tx_state <= tx_state + 4'd1;//切换状态 tx_done <=1'b0; send_done <=1'b1; end 5'd14://第0位数据发送状态 begin MOSI <= Send_Data[0]; SCK <= 1'b0; tx_state <= tx_state + 4'd1;//4'd15; // 修改为15,继续走一个状态来释放CS,走到16释放CS(目的是实现stm32的SPI通信的硬件控制,稳定传输数据,不然CS一直处于低电平,会一直发送数据给STM32,传输的数据是乱跳的) tx_done <= 1'b1; send_done <= 1'b1; end 5'd16:begin CS <= 1'b1; // 拉高CS,释放总线 tx_state <= 4'd0; // 回到初始状态 tx_done <= 1'b0; send_done <= 1'b0;; end default: begin tx_state <= 4'd0; tx_done <=1'b0; send_done <=1'b0; end endcase end else begin tx_done <=1'b0; tx_state <= 4'd0; CS <= 1'b1; SCK <= 1'b0; MOSI <= 1'b0; send_done <=1'b0; end end endmodule

STM32从机SPI通信代码(接收32位数据):

STM32从机SPI通信代码--fpga.c

STM32从机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.c

STM32从机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部分的代码,实际效果没测试过,需要的朋友自行更改。