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