https://pan.baidu.com/s/1rDsLAXGj8WbX82teSkhuIw?pwd=1234
这份FPGA 系统学习详细资料包是个人花大量时间精心整理的,超多干货全覆盖,从基础到实战一站式搞定,不用再到处薅资料!网盘链接随时可能失效,提取码 1234,先保存再学习,别等失效拍大腿!🔗链接:https://pan.baidu.com/s/1rDsLAXGj8WbX82teSkhuIw?pwd=1234
工业常用接口:FPGA的SPI总线多从机通信设计与时序优化
SPI(Serial Peripheral Interface)是工业应用中最常见的同步串行接口之一。相比UART,它能提供更高的传输速率和全双工通信能力,非常适合FPGA与ADC、DAC、传感器、存储器等设备的数据交换。当系统中存在多个从机时,设计挑战会显著增加。下面我将从实战角度,详细讲解如何在FPGA上实现高效可靠的多从机SPI通信。
1. SPI多从机通信的两种主流架构
在开始设计之前,我们需要先选择合适的多从机连接方式。根据工业应用场景,主要有两种架构:
| 架构类型 | 连接方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 独立片选模式 | 每个从机有独立的CS线,共享SCLK、MOSI、MISO | 通信独立,速率高,调试简单 | 占用GPIO多,从机数量受引脚限制 | 少量高速设备(如SPI Flash+显示屏) |
| 菊花链模式 | 所有从机共享CS和SCLK,数据依次传递 | 节省GPIO,布线简单 | 通信效率低,需从机支持链式功能 | LED驱动、多通道ADC/DAC级联 |
独立片选模式是工业现场最常用的方案。主设备通过拉低对应从机的CS线来选中目标,未选中的从机必须将MISO引脚置为高阻态,避免总线冲突。
菊花链模式则适用于需要控制大量同类型设备的场景,比如多个LED驱动芯片或移位寄存器级联。数据从第一个从机的MOSI进入,通过内部移位传递到下一个,最后一个从机的MISO返回给主机。
2. FPGA端SPI主控设计(独立片选模式)
作为FPGA工程师,我们通常需要自己实现SPI主控逻辑。下面给出一个可配置的多从机SPI主控模块设计。
2.1 模块接口定义
verilog
module spi_master #(
parameter CLK_FREQ = 50_000_000, // 系统时钟频率 50MHz
parameter SPI_FREQ = 5_000_000, // SPI时钟频率 5MHz
parameter DATA_WIDTH = 8, // 数据宽度 8位
parameter NUM_SLAVES = 4 // 从机数量
)(
input clk, reset_n,
// 用户接口
input [NUM_SLAVES-1:0] cs_sel, // 要选中的从机(独热码)
input [DATA_WIDTH-1:0] tx_data, // 发送数据
output reg [DATA_WIDTH-1:0] rx_data, // 接收数据
input start, // 启动传输
output reg busy, // 忙标志
// SPI物理接口
output reg sclk,
output reg mosi,
input miso,
output reg [NUM_SLAVES-1:0] cs_n // 片选,低有效
);
2.2 核心参数计算
SPI时钟由系统时钟分频产生:
verilog
localparam CLK_DIV = CLK_FREQ / SPI_FREQ / 2; // 半周期计数
// 例如 50MHz/5MHz/2 = 5,即每5个系统时钟翻转一次SCLK
2.3 状态机设计
SPI主控的核心是一个有限状态机,控制片选、时钟和数据传输:
verilog
typedef enum logic [2:0] {IDLE, CS_SETUP, TRANSFER, CS_HOLD} state_t;
state_t state;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
state <= IDLE;
busy <= 1'b0;
cs_n <= {NUM_SLAVES{1'b1}}; // 所有片选无效
sclk <= 1'b0; // 默认模式0:空闲低电平
bit_cnt <= 0;
end else begin
case (state)
IDLE: begin
busy <= 1'b0;
if (start) begin
busy <= 1'b1;
cs_n <= ~cs_sel; // 拉低选中的从机片选
state <= CS_SETUP;
clk_cnt <= 0;
// 锁存发送数据
shift_reg <= tx_data;
end
end
CS_SETUP: begin
// tCSC:片选建立到第一个时钟边沿的延时
if (clk_cnt < CS_SETUP_CYCLES) begin
clk_cnt <= clk_cnt + 1;
end else begin
clk_cnt <= 0;
state <= TRANSFER;
end
end
TRANSFER: begin
// SPI时钟产生逻辑
if (clk_cnt == CLK_DIV-1) begin
clk_cnt <= 0;
sclk <= ~sclk; // 翻转SCLK
// 在SCLK边沿进行数据操作
if (sclk) begin // 下降沿(取决于模式)
// 发送下一位(MSB first)
mosi <= shift_reg[7];
shift_reg <= {shift_reg[6:0], 1'b0};
end else begin // 上升沿
// 采样MISO
rx_temp <= {rx_temp[6:0], miso};
bit_cnt <= bit_cnt + 1;
// 8位传输完成
if (bit_cnt == DATA_WIDTH-1) begin
state <= CS_HOLD;
clk_cnt <= 0;
end
end
end else begin
clk_cnt <= clk_cnt + 1;
end
end
CS_HOLD: begin
// tASC:最后时钟边沿到片选释放的延时
if (clk_cnt < CS_HOLD_CYCLES) begin
clk_cnt <= clk_cnt + 1;
end else begin
cs_n <= {NUM_SLAVES{1'b1}}; // 释放片选
rx_data <= rx_temp; // 锁存接收数据
state <= IDLE;
end
end
endcase
end
end
2.4 支持四种SPI模式
SPI有四种工作模式,由时钟极性(CPOL)和时钟相位(CPHA)决定:
| 模式 | CPOL | CPHA | 空闲时钟 | 数据采样边沿 | 数据变化边沿 |
|---|---|---|---|---|---|
| 模式0 | 0 | 0 | 低电平 | 上升沿 | 下降沿 |
| 模式1 | 0 | 1 | 低电平 | 下降沿 | 上升沿 |
| 模式2 | 1 | 0 | 高电平 | 下降沿 | 上升沿 |
| 模式3 | 1 | 1 | 高电平 | 上升沿 | 下降沿 |
可以通过参数化配置来支持不同从机的需求:
verilog
// 在模块参数中添加
parameter CPOL = 0, CPHA = 0;
// 在状态机中根据CPOL设置空闲电平
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
sclk <= CPOL; // 空闲电平由CPOL决定
end
end
// 在TRANSFER状态中,根据CPHA决定采样边沿
always @(posedge clk) begin
if (state == TRANSFER) begin
if (clk_cnt == CLK_DIV-1) begin
clk_cnt <= 0;
sclk <= ~sclk;
// 根据CPHA决定何时采样和发送
if (CPHA == 0) begin
// CPHA=0:在第一个时钟边沿采样(CS有效后立即准备数据)
if (sclk == CPOL) begin // 第一个边沿
// 采样MISO
end else begin
// 发送MOSI
end
end else begin
// CPHA=1:在第二个时钟边沿采样
if (sclk == CPOL) begin
// 发送MOSI
end else begin
// 采样MISO
end
end
end
end
end
3. 多从机通信的关键设计要点
3.1 片选信号的互斥控制
在独立片选模式下,绝对不允许同时选中多个从机,否则MISO线上的数据会发生冲突。建议在用户接口层做保护:
verilog
// 检查cs_sel是否为独热码(只有一个bit为1)
always @(posedge clk) begin
if (start && ($countones(cs_sel) != 1)) begin
// 错误:同时选中了多个从机
error_flag <= 1'b1;
// 不启动传输
end
end
如果FPGA引脚不够,可以使用3-8译码器(如74HC238)扩展片选信号,只需3个GPIO就能控制8个从机。
3.2 MISO的三态控制
未被选中的从机必须将MISO引脚置为高阻态,否则多个从机同时驱动MISO会导致总线冲突。在FPGA作为从机时,需要实现三态输出:
verilog
// FPGA作为SPI从机时的MISO三态控制
assign miso = (cs_selected && tx_enable) ? tx_data_bit : 1'bz;
3.3 跨从机的模式兼容性
不同从机可能要求不同的SPI模式(CPOL/CPHA组合)。如果系统中有多个模式不兼容的从机,可以在主控中为每个从机独立配置模式:
verilog
// 为每个从机存储配置参数
reg [1:0] spi_mode [0:NUM_SLAVES-1];
// 传输开始前,根据cs_sel加载对应从机的模式
always @(posedge clk) begin
if (start) begin
case (cs_sel)
4'b0001: {cpol, cpha} <= spi_mode[0];
4'b0010: {cpol, cpha} <= spi_mode[1];
4'b0100: {cpol, cpha} <= spi_mode[2];
4'b1000: {cpol, cpha} <= spi_mode[3];
endcase
end
end
4. 时序优化实战技巧
4.1 信号完整性优化
问题:高速SPI通信时,长走线会导致信号反射和过冲。
解决方案:
- 在SCLK、MOSI线上串联22-33Ω的电阻,靠近FPGA引脚放置,匹配阻抗。
- CS线上拉10kΩ电阻到VCC,防止浮空误触发。
- 时钟频率超过10MHz时,建议使用差分SPI或降低速率。
4.2 片选与时钟的时序关系
很多SPI从机对片选建立时间(tCSC)和保持时间(tASC)有要求。默认0延时可能导致数据错乱:
verilog
// 根据从机数据手册配置延时
localparam CS_SETUP_CYCLES = CLK_FREQ / 1_000_000 * 10; // 10us建立时间
localparam CS_HOLD_CYCLES = CLK_FREQ / 1_000_000 * 5; // 5us保持时间
Linux内核的SPI驱动中有一个典型案例:当CS保持有效进行连续传输时,如果没有足够延时,SCK会产生毛刺,导致数据丢失。建议tCSC和tASC至少为半个SCK周期。
4.3 连续传输模式(Burst Mode)
对于需要传输大量数据的场景(如读取ADC采样流),每次传输都重新拉低CS会产生额外开销。可以设计连续模式,让CS保持有效,连续传输多个字节:
verilog
// 在模块中添加cont信号
input cont; // 1:连续传输,保持CS有效
// 在TRANSFER完成后判断
if (bit_cnt == DATA_WIDTH-1) begin
if (cont) begin
// 连续模式:不清除CS,直接开始下一字节
bit_cnt <= 0;
shift_reg <= next_tx_data; // 加载新数据
// 保持state为TRANSFER
end else begin
state <= CS_HOLD;
end
end
4.4 使用FIFO解耦
当SPI速率较高或数据量较大时,可以在主控前级加入FIFO,实现数据缓冲:
verilog
// 例化FIFO用于发送缓冲
fifo #(.WIDTH(8), .DEPTH(16)) tx_fifo (
.clk(clk),
.wr_en(user_tx_valid),
.wr_data(user_tx_data),
.rd_en(spi_tx_ready),
.rd_data(tx_data_to_spi),
.full(tx_fifo_full),
.empty(tx_fifo_empty)
);
这样用户逻辑可以突发写入,SPI主控按自身节奏发送,避免数据断流。
5. 常见问题排查指南
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 数据全为0xFF或0x00 | MISO未正确连接,或从机未选中 | 用示波器测CS是否拉低,测MISO波形 |
| 数据错位(如0x55变成0xAA) | 模式不匹配(CPOL/CPHA错误) | 检查从机手册,调整模式配置 |
| 偶发性数据错误 | 时序裕量不足 | 降低SPI时钟,增加CS建立/保持时间 |
| 多个从机同时响应 | CS控制错误,或从机未释放MISO | 检查CS互斥逻辑,确认从机MISO三态能力 |
| 高速下通信失败 | 信号完整性差 | 串联匹配电阻,缩短走线,降低速率 |
6. 总结
FPGA实现SPI多从机通信的核心要点:
- 架构选择:根据从机数量和性能需求,选择独立片选或菊花链模式。
- 参数可配置:将时钟分频、模式选择、数据宽度等参数化,适应不同从机。
- 时序严谨:严格遵循从机手册的建立/保持时间,必要时增加延时。
- 总线保护:确保MISO三态控制,避免总线冲突。
- 调试先行:用仿真验证波形,再用示波器实测确认。
掌握了这些技巧,你就能在工业现场从容应对各种SPI多从机通信需求,构建稳定可靠的FPGA控制系统。