尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

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

FPGA与STM32的SPI通信 - FPGA主 STM32从
📅 发布时间:2026/7/3 3:01:09

前言

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

相关新闻

  • 【计算机Java毕业设计案例】基于 SpringBoot 的线上教学资源整合推送系统的设计与实现 基于 SpringBoot 的成人远程继续教育管理平台(程序+文档+讲解+定制)
  • 阿里terway源码分析
  • 2026年优选指南:探寻最佳服务的苦荞全麦片品牌

最新新闻

  • CPPM注册职业采购经理怎么报名?报考条件、费用和证书查询一次说清
  • 支付系统重复收费难题:幂等键依赖的四个假设及应对之策
  • 3分钟掌握BurpCrypto插件:实战DES加密登录接口自动化测试
  • Java 必看:如何正确重写 hashCode() 和 equals() 方法?
  • ZCode对接商汤免费模型全流程教程
  • 从提示词工程到 Harness Engineering:打造坚实可靠的 AI 开发系统

日新闻

  • JMeter接口测试实战:从核心元件到复杂场景构建
  • Java Applet版刽子手游戏源码:含完整项目结构、吊杆绘图与胜负逻辑
  • 使用Apache JMeter对RoadRunner PHP应用进行性能测试与调优指南

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号