ZYNQ—vitis—网口传输信号波形数据
工程功能:ADC采集信号,将波形数据通过BRAM传输到PS端,然后用UDP以太网发送。(附加:ILA观察信号,发送的数据包含帧头,)
FPGA端——用BRAM将信号传输到PS
BD设计:
上图分析:
一共四个模块构成:
1.bram_ctrl: bram控制写模块
- ADC_A0 是将要传输的数据,也就是ADC采集的信号
- 200M时钟、复位
- bram_data_valid 是输入数据有效信号
- PL_IRQ 是一个输出的中断信号,当检测到输入信号时拉高一拍
代码:
module bram_ctrl(
input clk,
input rst_n,
input valid,
input [31:0] in_data,
output [31:0] addrb,
output reg [31:0] dinb ,
output reg [3:0] web ,
output reg PL_IRQ0
);
/************** **************/
/************** 信号定义 **************/
/************** **************/
//PL写RAM
wire [31:0]AddrEndValueVio; //控制写的地址范围
wire [31:0]dinbValueVio; //控制写的数据数值
wire [3:0] webVio; //控制写的有效字节位
wire [0:0] valid; //启动写数据
reg [0:0]valid1;//对writeEnVio延迟一个clk
reg [0:0]wrState; //写数据状态:0代表IDLE.1代表正在写
reg [31:0]addrbWrite;//写数据地址
//Ohter signals
assign addrb = wrState?addrbWrite:32'd0;
reg [0:0]wrStateReg;
/************** **************/
/************** PL 写入BRAM **************/
/************** **************/
always@(posedge clk)begin
if(!rst_n)begin
valid1 <= 1'b0;
end
else begin
valid1 <= valid;
end
end
always@(posedge clk)begin
if(!rst_n)begin
dinb[31:0] <= 32'd0;
web[3:0] <= 4'd0;
wrState <= 1'b0;
addrbWrite[31:0] <= 32'd0;
end
else begin
case(wrState)
1'b0:
if(valid&~valid1)begin//边沿检测,检测到上升沿启动写过程
wrState <= 1'b1;
web[3:0] <= 4'b1111;
addrbWrite[31:0] <= 32'd0;
dinb[31:0] <= in_data[31:0];
end else begin
wrState <= wrState;
web[3:0] <= 4'd0;
addrbWrite[31:0] <= 32'd0;
dinb[31:0] <= 32'd0;
end
1'b1:
if(valid && addrbWrite[31:0] < 32'hffff)begin
wrState <= wrState;
web[3:0] <= 4'b1111;
addrbWrite[31:0] <= addrbWrite[31:0] + 32'd4;
dinb[31:0] <= in_data[31:0];
end
else begin
wrState <= 1'b0;
web[3:0] <= 4'b0;
addrbWrite[31:0] <= 32'd0;
// dinb[31:0] <= in_data[31:0];//写入每个地址相同数据
//dinb[31:0] <= dinb[31:0] + 32'd1; //写入每个地址数据累加1
dinb[31:0] <= 32'd0; //保持
end
endcase
end
end
//中断
always@(posedge clk)begin
if(!rst_n)begin
PL_IRQ0 <= 1'b0;
end
else if(valid&~valid1) begin
PL_IRQ0 <= 1'b1;
end
else
PL_IRQ0 <= 1'b0;
end
endmodule
2.axi_bram_ctrl: BRAM控制器,现成的IP核
配置:
3.blk_mem_gen: BRAM IP核
配置:
其余没展示的部分默认
其中,xlconstant是一个常数,值为1bit的1
4.ILA: 观察信号
PS端——处理数据
宏定义:我一共用了13个ADC采样,发送13路数据
//中断编号 BRAM地址
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID //中断
#define ADC_A0_ADDR XPAR_ADC_A0_AXI_BRAM_CTRL_0_S_AXI_BASEADDR //BRAM
#define ADC_A1_ADDR XPAR_ADC_A1_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_A2_ADDR XPAR_ADC_A2_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_A3_ADDR XPAR_ADC_A3_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_A4_ADDR XPAR_ADC_A4_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_A5_ADDR XPAR_ADC_A5_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_A6_ADDR XPAR_ADC_A6_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_A7_ADDR XPAR_ADC_A7_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_B1_ADDR XPAR_ADC_B1_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_B2_ADDR XPAR_ADC_B2_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_B3_ADDR XPAR_ADC_B3_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_B4_ADDR XPAR_ADC_B4_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define ADC_B5_ADDR XPAR_ADC_B5_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
关键代码:指定一个地址,给地址赋值,最后用网口把地址发送出去
u8 Ver,BID;
Ver = 0x01;
BID = 0x23;
u32 samp_time = 250;
u32 delay_time;
u32 cnt = 0;
u16 phase_ADC1;
u16 *header = (u16*) 0x10000000;
//拼接
*(header) = Ver | (BID<<8);
*(header+1) = samp_time&0xffff;
*(header+2) = (samp_time&0xffff0000)>>16;
*(header+3) = (delay_time&0xffff);
*(header+4) = (delay_time&0xffff0000)>>16;
*(header+5) = cnt&0xffff;
*(header+6) = (cnt&0xffff0000)>>16;
*(header+7) = phase_ADC1;
*(header+8) = phase_ADC2;
*(header+9) = phase_ADC3;
*(header+10) = phase_ADC4;
*(header+11) = phase_ADC5;
*(header+12) = phase_ADC6;
*(header+13) = phase_ADC7;
*(header+14) = phase_ADC8;
*(header+15) = phase_ADC9;
*(header+16) = phase_ADC10;
*(header+17) = phase_ADC11;
*(header+18) = phase_ADC12;
*(header+19) = phase_ADC13;
*(header+20) = BoTimeSet&0xffff;
*(header+21) = (BoTimeSet&0xffff0000)>>16;
int *data_point = (int*)(0x1000002c);
//将13个通道的数据赋值给指定地址
for (int j = 0; j < BoTimeSet; j++)
{
*(data_point) = Xil_In32(ADC_A0_ADDR + 4*(j));
*(data_point + BoTimeSet * 1) = Xil_In32(ADC_A1_ADDR + 4*(j));
*(data_point + BoTimeSet * 2) = Xil_In32(ADC_A2_ADDR + 4*(j));
*(data_point + BoTimeSet * 3) = Xil_In32(ADC_A3_ADDR + 4*(j));
*(data_point + BoTimeSet * 4) = Xil_In32(ADC_A4_ADDR + 4*(j));
*(data_point + BoTimeSet * 5) = Xil_In32(ADC_A5_ADDR + 4*(j));
*(data_point + BoTimeSet * 6) = Xil_In32(ADC_A6_ADDR + 4*(j));
*(data_point + BoTimeSet * 7) = Xil_In32(ADC_A7_ADDR + 4*(j));
*(data_point + BoTimeSet * 8) = Xil_In32(ADC_B1_ADDR + 4*(j));
*(data_point + BoTimeSet * 9) = Xil_In32(ADC_B2_ADDR + 4*(j));
*(data_point + BoTimeSet * 10) = Xil_In32(ADC_B3_ADDR + 4*(j));
*(data_point + BoTimeSet * 11) = Xil_In32(ADC_B4_ADDR + 4*(j));
*(data_point + BoTimeSet * 12) = Xil_In32(ADC_B5_ADDR + 4*(j));
data_point++;
}
//网口发送
udp_tx_data((u8*)(header),44 + 13 * BoTimeSet * 4);
代码分析:
-
首先,定义一个u16的变量
header
,并且指定它的地址是0x10000000
; -
将需要发送的帧头等依次赋值拼接给
header
:Ver是u8,值为0x01,BID是u8,值为0x23;将它们按照高低位拼接起来,剩下的同理。 -
接着定义
data_point
,指定地址为0x1000002c
。理由为:上面的那些数据长度加起来刚好是0x2c,这样前面的数据发送完接着就是data_point
-
通过 for 循环,将13个通道的数据填到
data_point
对应的地址中。其中,BoTimeSet
是采样时间,即一个通道发送的数据个数 -
最后,用网口发送函数
udp_tx_data
将地址发送出去。起始地址为header
的地址,地址长度为44 + 13 * BoTimeSet * 4
;上面的那些数据长度加起来刚好是0x2c,十进制44,13个通道,每个通道BoTimeSet
个字节。
UDP发送功能函数:
//UDP发送功能函数
void udp_tx_data(u8 *buffer_ptr,unsigned int len)
{
static struct pbuf *ptr;
ptr = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_POOL); /* 申请内存 */
if (ptr)
{
pbuf_take(ptr, buffer_ptr,len); /* 将buffer_ptr中的数据打包进pbuf结构中 */
udp_send(pcb, ptr); /* udp发送数据 */
pbuf_free(ptr); /* 释放内存 */
}
}
调试结果分析
用vitis的debug功能,查看寄存器地址:
再根据上面的代码u8 Ver,BID;Ver = 0x01; BID = 0x23;u32 samp_time = 250;
从 0x10000000
地址开始,注意按照正确的方式,把数据读出来。
从 0x1000002c
开始,就是通道一的波形数据了,我赋上ILA采集的数据:
可以看出,通道一数据正确地传上去了。
通道二数据:
通道三数据:
我再赋上ILA ADC采样数据的末端:
并且相邻通道间起始地址相差 600*4=2400