当前位置: 首页 > news >正文

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

http://www.jsqmd.com/news/1112736/

相关文章:

  • 如何3步搞定FOFA资产搜索?网络安全新手快速上手指南
  • 筑牢数字经济的“能源底座”——数据中心综合能效管理方案全解析
  • MCP与Spring AI整合实战:云原生与AI技术融合指南
  • AI辅助项目开发:从技术选型到代码优化的实战指南
  • 大模型微调实战:从LoRA到LLaMA-Factory的完整指南
  • 分享2篇最新Skill+Harness技术,组合无敌
  • 【计算机Java毕业设计案例】基于 SpringBoot 的线上教学资源整合推送系统的设计与实现 基于 SpringBoot 的成人远程继续教育管理平台(程序+文档+讲解+定制)
  • 免费开源项目文档:基于MATLAB图像处理的人脸识别签到系统设计与实现
  • CPT外汇:用视角方式看外汇行业合规表达,更容易形成稳定判断
  • Makefile基础使用
  • TDC7201与TDC7200芯片寄存器功能概述及main.c代码
  • 服务器内存与CPU协同工作知识测试题
  • 阿里terway源码分析
  • likeadmin-api 怎么做计费?从余额查询到点数消耗的接口设计
  • 2026年优选指南:探寻最佳服务的苦荞全麦片品牌
  • HAL库代码基础介绍
  • 每日技术推荐(全栈/游戏/应用开发)
  • 从 has.showToast 看 ASCF 的 API 调用链路
  • 一些碎碎念qjl--6
  • 手写 MCP Server 连数据库:50 行代码让 AI 学会查 SQL
  • 企业AI转型困境与能力建设实战指南
  • 聊一聊 Linux 上对函数进行 hook 的两种方式
  • CPT外汇:注重效率的使用者更在意的工具可用性,这里做个维度观察
  • 交叉熵损失函数实战指南:原理、陷阱与工业级调优
  • 千万不能忽视!选择防盗门时必须知道的5个关键点
  • 好用的看广告供应商哪个公司好
  • foo2zjs打印机驱动架构解析:从ZJ-Stream协议到企业级部署的完整技术方案
  • Windows10Debloater终极指南:轻松打造纯净高效的Windows 10系统
  • Java毕设选题推荐:基于 SpringBoot 的线上选课学习考核教育平台的设计与实现 智慧远程教育资源发布管理系统【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 三进制太玄经·八十一首(坤至乾·每行一卦标准版)