FPGA主SPI与STM32从机通信

本文介绍了FPGA主SPI与STM32从机的通信。硬件环境为STM32F407VET6和DE0 - nano,用cubemx配置工程,FPGA用Quartus软件。给出了FPGA的SPI主机代码,说明了SCK周期等情况,还阐述了STM32从机的SPI模式配置、参数设置、DMA传输配置及接收代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

概述

FPGA的SPI主机代码

STM32从机

SPI模式配置

SPI参数设置

 SPI的DMA传输配置

STM32从机SPI接收代码


概述

        在这里就不介绍SPI原理了,需要的同学自行查阅。我使用的硬件环境为STM32F407VET6和DE0-nano,如下图。

 使用cubemx配置工程,FPGA使用Quartus软件,时序仿真图如下

FPGA的SPI主机代码

        txd_signal信号为上升沿触发,led0,1,2为调试灯,可去掉。

        一个SCK周期为16个clk周期,我测试时使用50MHz晶振,故SPI时钟为3.125MHz。

        需要改动通信速率可以通过改动cnt1相关值改变。

        时钟相位参考STM32从机配置

//时钟极性高,采样沿上升沿
module SPI_Master(
  clk,        // 系统时钟
  rst,        // 复位,低有效
  txd_flag,   // 高电平表示发送完成处于忙碌状态,低电平表示处于空闲状态
  sck,        // 时钟输出
  cs,         // 片选输出
  mosi,       // 主设备输出信号
  miso,       // 主设备输入信号
  txd_signal, // 发送触发信号,给一个信号发送8bit
  rxd_out,    // 接收的数据
  txd_in,     // 发送的数据
  led0,
  led1,
  led2
);
//调试引脚
output led0,led1,led2;
assign led0=txd_signal;
assign led1=txd_flag;
assign led2=cs;
// 输入信号
input clk, rst, miso, txd_signal;
// 输出信号
output reg mosi, txd_flag;
output wire sck, cs;
output reg [7:0] rxd_out;
input [7:0] txd_in;

// 内部寄存器和变量
reg [2:0] txd_start;
reg [2:0] txd_start_outside;
reg [3:0] cnt;
reg [3:0] cnt1;
reg csr;
assign sck = cnt1[3];
assign cs = csr;

// 任务:根据计数器 cnt1 的值更新时钟状态
task clk_states;
  if (cnt1 == 4'b0000) begin
    cnt1 <= 4'b1111;
    cnt <= cnt + 1'b1;
  end else
    cnt1 <= cnt1 - 1'b1;
endtask

// always 块:根据时钟和复位信号控制发送过程和计数器的更新
always @(posedge clk or negedge rst) begin
  if (!rst) begin
    // 复位所有状态和寄存器
    txd_flag <= 1'b0;
    txd_start <= 3'd1;
    cnt <= 4'd0;
    cnt1 <= 4'b1111;
    csr <= 1'b1;
  end else begin
    if (txd_start == txd_start_outside) begin
      csr <= 1'b0;
      txd_flag <= 1'b1; // txd_flag 信号与 cs 信号相反,电路当处于通信时为高电平,表示忙碌状态
      case (cnt) // 为低电平表示空闲状态
        0: clk_states();//进八次cnt加1,SCK经过一个周期,SCK高电平持续时间为4和CLK
        1: clk_states();
        2: clk_states();
        3: clk_states();
        4: clk_states();
        5: clk_states();
        6: clk_states();
        7: begin
          if (cnt1 == 4'b0000) begin
            cnt1 <= 4'b1111;
            cnt <= 4'd8;
          end else
            cnt1 <= cnt1 - 1'b1;
        end
        8: begin
          // 多加一个状态延长 cs 的低电平时间,给从机足够的时间接收数据
          txd_start <= txd_start + 1'b1;
          cnt <= 4'd0;
        end
      endcase
    end else begin
      csr <= 1'b1;
      txd_flag <= 1'b0;
    end
  end
end

// always 块:外部触发发送脉冲,上升沿触发发送一次
always @(posedge txd_signal or negedge rst) begin
  if (!rst)
    txd_start_outside <= 1'b0;
  else begin
    if (!txd_flag)
      txd_start_outside <= txd_start_outside + 1'b1;
    else;
  end
end

// always 块:sck 上升沿采样数据
reg [7:0] rxd_outr;
reg [2:0] rec_cnt;
always @(posedge sck or negedge rst) begin
  if (!rst) begin
    rxd_out <= 8'h00;
    rec_cnt <= 3'd0;
  end else begin
    case (rec_cnt)
      0: begin
        rxd_outr[7] <= miso;
        rec_cnt <= 3'd1;
      end
      1: begin
        rxd_outr[6] <= miso;
        rec_cnt <= 3'd2;
      end
      2: begin
        rxd_outr[5] <= miso;
        rec_cnt <= 3'd3;
      end
      3: begin
        rxd_outr[4] <= miso;
        rec_cnt <= 3'd4;
      end
      4: begin
        rxd_outr[3] <= miso;
        rec_cnt <= 3'd5;
      end
      5: begin
        rxd_outr[2] <= miso;
        rec_cnt <= 3'd6;
      end
      6: begin
        rxd_outr[1] <= miso;
        rec_cnt <= 3'd7;
      end
      7: begin
        rxd_outr[0] <= miso;
        rxd_out <= {rxd_outr[7:1], miso};
        rec_cnt <= 3'd0;
      end
      default:;
    endcase
  end
end

// always 块:sck 下降沿时发送数据
reg [2:0] send_cnt;
always @(negedge sck or negedge rst) begin
  if (!rst)
    send_cnt <= 3'd0;
  else begin
    case (send_cnt)
      0: begin
        mosi <= txd_in[7];
        send_cnt <= 3'd1;
      end
      1: begin
        mosi <= txd_in[6];
        send_cnt <= 3'd2;
      end
      2: begin
        mosi <= txd_in[5];
        send_cnt <= 3'd3;
      end
      3: begin
        mosi <= txd_in[4];
        send_cnt <= 3'd4;
      end
      4: begin
        mosi <= txd_in[3];
        send_cnt <= 3'd5;
      end
      5: begin
        mosi <= txd_in[2];
        send_cnt <= 3'd6;
      end
      6: begin
        mosi <= txd_in[1];
        send_cnt <= 3'd7;
      end
      7: begin
        mosi <= txd_in[0];
        send_cnt <= 3'd0;
      end
      default:;
    endcase
  end
end

endmodule

STM32从机

SPI模式配置

SPI参数设置

 

 SPI的DMA传输配置

STM32从机SPI接收代码
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	uint8_t data=0;
	HAL_SPI_Receive_DMA(&hspi1,&data,1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		//FPGA RESET
		HAL_GPIO_WritePin(RST_GPIO_Port,RST_Pin,GPIO_PIN_RESET);
		HAL_Delay(10);
		HAL_GPIO_WritePin(RST_GPIO_Port,RST_Pin,GPIO_PIN_SET);
		//等待发送空闲
		while(HAL_GPIO_ReadPin(txd_flag_GPIO_Port,txd_flag_Pin)==GPIO_PIN_SET){
		};
		//延迟便于观察串口输出
		HAL_Delay(2000);
		//上升沿触发FPGA主机发送
		HAL_GPIO_WritePin(txd_signal_GPIO_Port,txd_signal_Pin,GPIO_PIN_SET);
		HAL_Delay(100);
		HAL_GPIO_WritePin(txd_signal_GPIO_Port,txd_signal_Pin,GPIO_PIN_RESET);
		//串口打印data查看数据
		printf("data is %d \r\n",data);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

### FPGASTM32的特点、区别及使用场景 #### 1. 特点对比 ##### FPGA特点 现场可编程门阵列(FPGA)是一种高度灵活的硬件平台,允许用户自定义内部逻辑结构。这种灵活性使得FPGA能够支持复杂的并行处理任务,在信号处理、图像识别等领域表现出色。由于其丰富的I/O资源(可达数十甚至上百个),适合用于需要大量外部连接的应用场合[^1]。 ##### STM32特点 基于ARM架构的微控制器单元(MCU), STM32系列提供了高效的嵌入式解决方案。这类设备集成了多种外设接口和支持实时操作系统(RTOS),适用于各种自动化控制系统的设计。特别是对于那些对成本敏感且功耗要求严格的项目来说,是一个理想的选择。 #### 2. 要差异 | 对比项 | FPGA | STM32 | | --- | --- | --- | | **开发难度** | 需要掌握VHDL/Verilog等硬件描述语言;设计周期较长 | C/C++编程即可满足需求;易于上手快速迭代 | | **性能表现** | 支持大规模并行运算;延迟极低 | 单核或多核处理器按顺序执行指令;响应时间取决于CPU频率| | **功耗情况** | 功率消耗较大,尤其是在高密度计算时 | 较低至中等水平,特别是一些节能模式下的型号 | #### 3. 应用领域划分 - **FPGA适用范围** - 复杂算法加速器构建 - 自定义协议解析引擎创建 - 实现高速数据采集传输 - **STM32应用场景** - 工业控制系统的中心节点管理 - 家电产品智能化改造的核心部件 - IoT终端设备中的控芯片选择 综上所述,虽然两者都可用于电子电路设计当中,但是它们各自擅长的方向有所不同。当面临具体工程项目决策时,应综合考虑项目的特殊性和长远规划来挑选最合适的工具[^2]。 ```cpp // 示例代码展示如何初始化STM32 FSMC接口以FPGA通信 void InitFSMC(void){ // 初始化代码省略... } ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值