网站备案添加域名,最新商城系统,做网站新闻移动动态,企业信用公示网上查询平台FPGA串口接收解帧、并逐帧发送有效数据 工程实现的功能#xff1a;FPGA串口接收到串口调试助手发来的数据#xff0c;将其数据解帧。判断到正确的帧头和帧尾之后#xff0c;将有效数据存入rx_data中#xff1b;另一方面发送端将有效数据逐帧发送出去。
参考#xff1a;正…FPGA串口接收解帧、并逐帧发送有效数据 工程实现的功能FPGA串口接收到串口调试助手发来的数据将其数据解帧。判断到正确的帧头和帧尾之后将有效数据存入rx_data中另一方面发送端将有效数据逐帧发送出去。
参考正点原子官方FPGA串口通信实验
模块构成 在原子哥的基础上改的代码。添加了接收状态机模块rx_state_machine修改了串口发送模块uart_send。其余部分代码基本不变只加了例化修改数据位宽
接收状态机模块rx_state_machine——进行解帧处理接收有效数据
假设帧头为AA帧尾为55有效数据为32bit
思路使用三段式状态机
接收状态机标志位是什么
module uart_recv(input sys_clk, //系统时钟input sys_rst_n, //系统复位低电平有效input uart_rxd, //UART接收端口output reg uart_done, //接收一帧数据完成标志output reg rx_flag, //接收过程标志信号output reg [ 3:0] rx_cnt, //接收数据计数器output reg [ 7:0] rxdata,output reg [7:0] uart_data, //接收的数据output reg [31:0] rx_data);在uart_recv中有一个uart_done信号是接收一帧数据完成标志当uart_done拉高表明接收了一帧数据8bit并存到了 uart_data 中。
我们可以用uart_done的上升沿作为状态机跳转的使能信号。
边沿检测如下
//边沿检测 uart_done 信号
always (posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)beginuart_done_prev1 0;uart_done_prev2 0;endelse beginuart_done_prev1 uart_done; uart_done_prev2 uart_done_prev1; //延迟两拍end
end
//上升沿检测
assign uart_done_rise uart_done_prev1 ~uart_done_prev2;检测到uart_done的上升沿时uart_done_rise拉高一个时钟周期。当uart_done_rise拉高时表明接收了一帧数据并存到了 uart_data 中状态机进入跳转判断。
状态机设计
在上面的假设中帧头为AA帧尾为55有效数据为4帧32bit。
所以状态机的状态有 起始状态IDLE 如果接收到AA则进入DATA_RX状态 如果接收的数据4帧 进入下一个状态即帧尾检测状态 如果检测到了帧尾则进入DONE状态表明解帧完成
在接收帧头或帧尾的状态中只要不是AA或55则重新进入起始状态。
localparam SOF 8haa; //帧头
localparam EOF 8h55; //帧尾localparam IDLE 4d0;
localparam DATA_SOF 4d1;
localparam DATA_RX 4d2;
localparam DATA_EOF 4d3;
localparam DONE 4d4;//时序逻辑状态切换
always (posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n) c_status 4d0;elsec_status n_status;
end//组合逻辑状态切换
always (*) begincase(c_status)IDLE : begin //起始状态if(uart_done_rise) beginif(uart_data SOF)n_status DATA_RX; //如果接收到AA则进入DATA_RX状态elsen_status IDLE;endelse n_status IDLE;endDATA_RX: begin //接收有效数据状态if(uart_done_rise) beginif(r_bytecnt 3d3)n_status DATA_EOF;elsen_status DATA_RX;endelse n_status DATA_RX;endDATA_EOF: begin //接收帧尾状态if(uart_done_rise) beginif(uart_data EOF)n_status DONE;elsen_status IDLE;endelse n_status DATA_EOF;endDONE: begin //解帧完成n_status IDLE;enddefault: beginn_status IDLE;endendcase
end
//接收4个有效数据的计数器
always (posedge sys_clk) beginif(c_status DATA_RX) beginif(uart_done_rise) r_bytecnt r_bytecnt1;else ;endelse r_bytecnt 3b0;
end代码解释首先定义帧头SOF 8’haa;start of flag帧尾EOF 8’h55;end of flag以及5个状态在后面设计状态机时发现DATA_SOF状态用不到 第一个always语句块时序逻辑状态切换保证了当前状态c_status和下个状态n_status的切换。 第二个always语句块4个状态所执行的操作。 c_status为IDLEuart_done_rise拉高时状态机进入跳转判断。 如果此时uart_data中的数据是帧头SOF则准备进入下一个状态进行有效数据的接收DATA_RX状态 否则继续停留在IDLE状态。 c_status为DATA_RXuart_done_rise拉高时状态机进入跳转判断。 在DATA_RX状态中如果接收的字节数r_bytecnt 3d3说明已经完整地接收了4帧的有效数据准备进入下个状态进行帧尾检测DATA_EOF 否则说明数据还没有接收完继续停留在DATA_RX状态接收数据。 c_status为DATA_EOFuart_done_rise拉高时状态机进入跳转判断。 如果此时uart_data中的数据是帧尾EOF则进入解帧完成状态DONE 否则说明帧尾不正确该组数据无效状态进入IDLE重新解帧新的数据 第三个always语句中是接收4个有效数据的计数器。c_status处于有效数据的接收状态时每次uart_done_rise拉高表明接收了一帧数据计数器r_bytecnt加1 上面的三个always语句块实现了状态的跳转和判断。接下来需要计算数据并判断什么情况下进行输出。
//将所有接收到的值赋给寄存器 reg_rx_data
always (posedge sys_clk) beginif((c_status DATA_RX) uart_done_rise) begincase(r_bytecnt)3d0: reg_rx_data[31:24] uart_data;3d1: reg_rx_data[23:16] uart_data;3d2: reg_rx_data[15:8] uart_data;3d3: reg_rx_data[7:0] uart_data; default: ;endcaseend
end首先将有效数据存入寄存器reg_rx_data中。
在c_status DATA_RX状态下一共接收了4帧的数据现在把这4帧数据组合成一个32bit的数据。
当r_bytecnt为0时这时候接收的是最高位存入reg_rx_data[31:24]
当r_bytecnt为1时将此时刻接收的数据存入reg_rx_data[23:16]
依此类推。
//判断数据是否有效
always (posedge sys_clk) beginif((c_status DONE)) beginrx_data reg_rx_data;rx_vaild 1b1;rx_error 1b0;endelse if((c_status DATA_EOF) (uart_data ! EOF)) beginrx_data rx_data;rx_error 1b1;rx_vaild 1b0;endelse beginrx_data rx_data;rx_vaild 1b0;rx_error 1b1;end
end接下来判断什么情况下进行输出 / 判断该组数据是否有效
c_status DONE时表明完整地解帧完了一组数据rx_data reg_rx_data;将寄存器reg_rx_data的值赋给输出rx_data同时给两个标志位rx_vaild、rx_error赋值。当解帧的帧尾不等于EOF时接收数据无效rx_data保持不变。 注释至于为什么这里只判断了帧尾没有判断帧头是因为帧头的判断在状态机里就实现了如果帧头不正确则直接回到起始状态进行不到接收有效数据的状态。但是帧尾还需要再判断是因为进行到帧尾的状态时就已经接收完有效数据了并已经存入了reg_rx_data中。所以还要再对帧尾进行判断如果不符合则rx_data不更新认为这个数据无效。
接收解帧结果
ila_0 ila_rx (.clk(sys_clk), // input wire clk.probe0(uart_done), // input wire [0:0] probe0 .probe1(uart_data), // input wire [7:0] probe1 .probe2(rx_vaild), // input wire [0:0] probe2 .probe3(rx_data), // input wire [31:0] probe3 .probe4(uart_done_rise), // input wire [0:0] probe4 .probe5(c_status), // input wire [2:0] probe5 .probe6(r_bytecnt) // input wire [2:0] probe6
);在串口调试助手中我发送的是aa1234567855结果如上图有效数据12345678存入了rx_data。 在下一节继续写逐帧发送部分