1. 双核通信初始化与验证
任务目标
在双核架构伺服驱动系统中,DSP与FPGA之间的数据交换是电流环、速度环高速控制的基础。本案例以TI C2000系列DSP与Xilinx FPGA为例,通过EMIF接口(16位异步模式)建立通信链路,实现DSP向FPGA写入控制字、读取状态字的双向数据通路,并通过回环校验验证通信正确性。
硬件连接与接口约定
- EMIF总线:DSP的EMIF_CS2片选空间映射至FPGA寄存器基地址
0x200000 - 数据宽度:16位,异步读/写时序,无等待状态插入
- FPGA侧寄存器映射:
- 偏移
0x00:控制寄存器(DSP写,FPGA读) - 偏移
0x02:状态寄存器(FPGA写,DSP读) - 偏移
0x04:回环数据寄存器(FPGA内部将写入值原样返回)
- 偏移
DSP端完整实现(C2000)
// emif_test.c - DSP与FPGA EMIF通信验证
#include "F2837xD_device.h"
#include "F2837xD_Examples.h"
// EMIF片选2空间基地址(与FPGA连接)
#define FPGA_BASE 0x200000U
// 寄存器偏移(字节地址)
#define REG_CTRL 0x00U
#define REG_STATUS 0x02U
#define REG_LOOPBACK 0x04U
// 使用volatile防止编译器优化
#define FPGA_CTRL (*(volatile Uint16 *)(FPGA_BASE + REG_CTRL))
#define FPGA_STATUS (*(volatile Uint16 *)(FPGA_BASE + REG_STATUS))
#define FPGA_LOOPBACK (*(volatile Uint16 *)(FPGA_BASE + REG_LOOPBACK))
// EMIF初始化:CS2空间,16位异步接口
void EMIF_Init(void)
{
// 使能EMIF时钟(GPIO时钟已在InitSysCtrl使能)
CpuSysRegs.PCLKCR0.bit.EMIF1 = 1;
// 配置CS2空间:异步模式,16位数据总线
EMIF1CS2CR.all = 0x00000020; // 异步接口,16位总线宽度
// 读时序:建立2周期,选通6周期,保持2周期
EMIF1CS2RCR.all = 0x00020062;
// 写时序:建立2周期,选通6周期,保持2周期
EMIF1CS2WCR.all = 0x00020062;
// 片选空间使能
EMIF1CS2CR.bit.CS_EN = 1;
}
// 回环测试:写入0x5A5A,读取返回值并比较
Uint16 EMIF_LoopbackTest(void)
{
Uint16 testPattern = 0x5A5A;
Uint16 readBack;
// 写控制寄存器(FPGA侧仅存储)
FPGA_CTRL = testPattern;
// 写回环寄存器(FPGA内部将数据原样返回)
FPGA_LOOPBACK = testPattern;
// 读取回环值
readBack = FPGA_LOOPBACK;
// 读取状态寄存器(FPGA上电默认0x0001,表示链路就绪)
Uint16 status = FPGA_STATUS;
// 验证:回环数据一致且状态寄存器bit0=1
if ((readBack == testPattern) && (status & 0x0001))
return 1; // 通信正常
else
return 0; // 通信异常
}
void main(void)
{
// 系统初始化(时钟、GPIO等)
InitSysCtrl();
InitGpio();
// GPIO引脚复用为EMIF功能
// EMIF1_D[0..15], EMIF1_A[0..7], EMIF1_CS2, EMIF1_WE, EMIF1_OE, EMIF1_RNW
// 具体引脚号见F2837xD原理图,此处为关键配置示例:
GPIO_SetupPinMux(0, GPIO_MUX_CPU1, 1); // EMIF1_D0
GPIO_SetupPinMux(1, GPIO_MUX_CPU1, 1); // EMIF1_D1
// ...(其余数据/地址/控制引脚按相同方式配置)
// 初始化EMIF
EMIF_Init();
// 执行回环验证
Uint16 result = EMIF_LoopbackTest();
// 结果指示:GPIO31输出高电平表示成功(需外部LED或示波器观察)
GPIO_SetupPinMux(31, GPIO_MUX_CPU1, 0);
GPIO_SetupPinOptions(31, GPIO_OUTPUT, GPIO_PUSH_PULL);
GPIO_WritePin(31, result ? 1 : 0);
// 程序停留,观察结果
while(1);
}
FPGA端最小逻辑(Verilog)
// fpga_emif_slave.v - EMIF从设备接口
module fpga_emif_slave (
input wire clk, // 50MHz系统时钟
input wire rst_n,
// EMIF接口
input wire cs, // 片选(低有效)
input wire we, // 写使能(低有效)
input wire oe, // 读使能(低有效)
input wire [7:0] addr, // 地址总线
inout wire [15:0] data, // 数据总线
// 内部寄存器
output reg [15:0] ctrl_reg, // 控制寄存器
input wire [15:0] status_reg // 状态寄存器(外部输入)
);
reg [15:0] loopback_reg;
reg [15:0] data_out;
reg data_out_en;
// 写操作
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ctrl_reg <= 16'h0000;
loopback_reg <= 16'h0000;
end else if (!cs && !we) begin
case (addr[3:0])
4'h0: ctrl_reg <= data;
4'h2: status_reg <= data; // 允许外部写状态(测试用)
4'h4: loopback_reg <= data;
endcase
end
end
// 读操作
always @(*) begin
data_out = 16'h0000;
data_out_en = 1'b0;
if (!cs && !oe) begin
case (addr[3:0])
4'h0: begin data_out = ctrl_reg; data_out_en = 1'b1; end
4'h2: begin data_out = status_reg; data_out_en = 1'b1; end
4'h4: begin data_out = loopback_reg; data_out_en = 1'b1; end
endcase
end
end
assign data = data_out_en ? data_out : 16'hZZZZ;
endmodule
运行与验证
- 硬件连接:将DSP EMIF接口与FPGA引脚按原理图连接,确保FPGA程序已烧录且状态寄存器输出
0x0001(bit0=1表示FPGA就绪)。 - 编译烧录:在CCS中编译DSP工程并烧录至DSP,运行程序。
- 验证方法:
- 观察GPIO31输出:高电平表示回环测试通过,低电平表示失败
- 在CCS的Expressions窗口添加
FPGA_CTRL、FPGA_LOOPBACK变量,单步执行后确认写入值与读取值一致 - 使用逻辑分析仪抓取EMIF总线时序,确认CS2、WE、OE信号与读写周期匹配
- 通过标准:回环测试返回1且GPIO31输出高电平,同时状态寄存器bit0为1,即判定通信链路正常。
常见问题
- 数据全为0xFF:检查EMIF片选地址是否映射正确,FPGA侧cs信号是否有效连接
- 写入无响应:确认EMIF时序参数(建立/选通/保持周期)是否满足FPGA侧时序要求,必要时增加等待周期
- GPIO复用冲突:确保所有EMIF引脚已正确配置为外设功能,未被其他外设占用