1. 项目概述
最近在做一个工业控制项目,需要实现DSP28335与FPGA之间的高速数据交换。经过方案对比,最终选择了SPI通信协议。作为嵌入式开发者,SPI接口调试一直是让人又爱又恨的环节——配置简单但时序要求严格。本文将完整记录从硬件连接到软件实现的整个过程,包含实际调试中遇到的坑和解决方案。
2. SPI通信基础解析
2.1 协议核心要点
SPI采用主从架构,四线制通信:
- SCK:同步时钟(主设备产生)
- MOSI:主发从收数据线
- MISO:从发主收数据线
- CS:片选信号(低有效)
注意:实际项目中CS信号常被忽视,但它是保证通信可靠性的关键。我曾遇到因CS信号抖动导致的间歇性通信失败,最终通过增加RC滤波电路解决。
2.2 工作模式选择
SPI有四种工作模式,由CPOL和CPHA决定:
| 模式 | CPOL | CPHA | 时钟特性 |
|---|---|---|---|
| 0 | 0 | 0 | 上升沿采样 |
| 1 | 0 | 1 | 下降沿采样 |
| 2 | 1 | 0 | 下降沿采样 |
| 3 | 1 | 1 | 上升沿采样 |
在DSP28335项目中我们选择模式3(CPOL=1, CPHA=1),这是工业设备中最常用的配置。FPGA端需要严格匹配此时序:
verilog复制always @(posedge SCK) begin // 上升沿采样
if(CPHA) shift_reg <= {MOSI, shift_reg[7:1]};
end
3. DSP28335硬件配置
3.1 时钟树配置
DSP28335的SPI时钟源自SYSCLK,通过SPICLKCR寄存器分频。关键计算公式:
code复制实际SPI时钟 = SYSCLK / (SPIBRR + 1)
建议初始设置为SYSCLK/10(SPIBRR=9),待通信稳定后再逐步提高。我曾盲目追求高速(设置SPIBRR=1),结果导致FPGA端数据采样异常。
3.2 寄存器配置详解
完整初始化代码及注释:
c复制void SPI_Init(void) {
EALLOW;
// 关键配置步骤:
// 1. 关闭SPI模块进行安全配置
SPIRegs.SPICCR.bit.SPISWRESET = 0;
// 2. 设置时钟相位(CPHA=1)
SPIRegs.SPICCR.bit.CLKPOLARITY = 1;
// 3. 16位字符长度(工业常用)
SPIRegs.SPICCR.bit.SPICHAR = 0xF;
// 4. 使能主模式
SPIRegs.SPICTL.bit.MASTER_SLAVE = 1;
// 5. 软件控制CS(重要!)
SPIRegs.SPICTL.bit.TALK = 1;
SPIRegs.SPIPRI.bit.FREE = 1;
// 6. 重新使能SPI
SPIRegs.SPICCR.bit.SPISWRESET = 1;
EDIS;
}
4. FPGA端Verilog实现
4.1 状态机设计
采用三段式状态机实现可靠通信:
verilog复制module spi_slave(
input SCK, MOSI, CS,
output MISO,
output [15:0] rx_data
);
reg [1:0] state;
reg [15:0] shift_reg;
reg [4:0] bit_cnt;
// 状态定义
localparam IDLE = 2'b00;
localparam RECV = 2'b01;
localparam SEND = 2'b10;
always @(posedge SCK or posedge CS) begin
if(CS) begin
state <= IDLE;
bit_cnt <= 0;
end else begin
case(state)
IDLE: if(!CS) state <= RECV;
RECV: begin
shift_reg <= {MOSI, shift_reg[15:1]};
if(bit_cnt==15) state <= SEND;
bit_cnt <= bit_cnt + 1;
end
SEND: state <= IDLE;
endcase
end
end
assign MISO = (state==SEND) ? shift_reg[0] : 1'bz;
assign rx_data = shift_reg;
endmodule
4.2 时序约束要点
必须添加SDC约束保证时序:
code复制create_clock -name SPI_CLK -period 100 [get_ports SCK]
set_input_delay -clock SPI_CLK -max 5 [get_ports MOSI]
set_output_delay -clock SPI_CLK -max 3 [get_ports MISO]
5. 调试实战记录
5.1 常见问题排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 数据错位 | CPOL/CPHA不匹配 | 用逻辑分析仪确认时钟边沿 |
| 间歇性失败 | CS信号毛刺 | 增加RC滤波(典型值100Ω+100pF) |
| 只能收不能发 | MISO引脚配置错误 | 检查FPGA引脚约束文件 |
| 速率上不去 | 走线过长 | 缩短信号线或降低速率 |
5.2 逻辑分析仪使用技巧
推荐使用Saleae Logic Pro 16抓取波形,设置要点:
- 采样率至少5倍于SPI时钟
- 添加SPI协议解码器
- 触发条件设为CS下降沿
实测波形示例:
code复制CS _|¯¯|____|¯¯|__
SCK ___|¯|_|¯|_|¯
MOSI 0xA5 0x3C
MISO 0xFF 0x00
6. 性能优化方案
6.1 DMA传输配置
对于大数据量传输,启用SPI DMA:
c复制void InitDmaSpi(void) {
DmaRegs.DMACTRL.bit.HARDIRQ = 1;
DmaRegs.DMACH1.CONTROL.bit.SYNC = 1; // 同步SPI中断
DmaRegs.DMACH1.MODE.bit.PERINTE = 1; // 使能周期中断
}
6.2 双缓冲机制
FPGA端实现双缓冲避免数据丢失:
verilog复制reg [15:0] buffer[0:1];
reg buf_sel;
always @(posedge data_ready) begin
buffer[buf_sel] <= shift_reg;
buf_sel <= ~buf_sel;
// 触发中断通知DSP读取
end
7. 工程经验总结
-
接地处理:DSP与FPGA必须共地,我曾因接地不良导致通信距离不足30cm,增加地线后稳定传输1.5米
-
上拉电阻:所有SPI信号线建议增加4.7kΩ上拉,特别是CS信号
-
ESD防护:工业现场必须添加TVS二极管,推荐使用SM712系列
-
代码版本:DSP的SPI库有多个版本,确认使用最新版(C28x_SPI_Lib_v3.4+)
这个项目最终实现了10Mbps的稳定通信速率,传输误码率低于1e-9。关键收获是:SPI看似简单,但细节决定成败。建议开发者准备:
- 高质量示波器(带宽≥100MHz)
- 可编程逻辑分析仪
- 各种阻值的端接电阻套件
