详解串行通信协议及其FPGA实现(二)

简介: 详解串行通信协议及其FPGA实现(二)

640.jpg标准串口协议的Verilog实现

基于Verilog实现标准串口协议发送8位数据:起始位 + 8位数据位 + 校验位 + 停止位 = 11位,每1位的时间是16个时钟周期,所以输入时钟应该为:波特率*16,带Busy忙信号输出。实现方法比较简单,数据帧的拼接、计数器计时钟周期,每16个时钟周期输出一位数据即可。


串口发送1个字节实现

/*
串口协议发送:起始位 + 8位数据位 + 校验位 + 停止位 = 11位 * 16 = 176个时钟周期
clk频率 = 波特率 * 16
*/
module uart_tx_8bit(
//input
input clk,                //UART时钟=16*波特率
input rst_n,
input [7:0] data_in,       //需要发送的数据
input trig,                //上升沿发送数据
//output
output busy,                 //高电平忙:数据正在发送中
output reg tx                //发送数据信号
);
reg[7:0] cnt;             //计数器
reg trig_buf;
reg trig_posedge_flag;
// reg trig_negedge_flag;
reg send;
reg [10:0] data_in_buf;     //trig上升沿读取输入的字节,拼接数据帧
wire odd_bit;   //奇校验位 = ~偶校验位
wire even_bit;  //偶校验位 = 各位异或
wire POLARITY_BIT = even_bit;  //偶校验
// wire POLARITY_BIT = odd_bit;   //奇校验
assign even_bit = ^data_in; //一元约简,= data_in[0] ^ data_in[1] ^ .....
assign odd_bit = ~even_bit;
assign busy = send;     //输出的忙信号
//起始位+8位数据位+校验位+停止位 = 11位 * 16 = 176个时钟周期
parameter CNT_MAX = 176;
always @(posedge clk)
begin
    if(!rst_n)
    begin
        trig_buf <= 0;
        trig_posedge_flag <= 0;
        // trig_negedge_flag <= 0;
    end
    else 
    begin
        trig_buf <= trig;
        trig_posedge_flag <= (~trig_buf) & trig; //在trig信号上升沿时产生1个时钟周期的高电平
        // trig_negedge_flag <= trig_buf & (~trig); //在trig信号下降沿时产生1个时钟周期的高电平
    end
end
always @(posedge clk)
begin
    if(!rst_n)
        send <= 0;
    else if (trig_posedge_flag &  (~busy))  //当发送命令有效且线路为空闲时,启动新的数据发送进程
        send <= 1;
    else if(cnt == CNT_MAX)      //一帧资料发送结束
        send <= 0;
end
always @ (posedge clk)
begin
    if(!rst_n)
        data_in_buf <= 11'b0;
    else if(trig_posedge_flag & (~busy))    //只读取一次数据,一帧数据发送过程中,改变输入无效
        data_in_buf <= {1'b1, POLARITY_BIT, data_in[7:0], 1'b0};   //数据帧拼接
end
always @ (posedge clk)
begin
    if(!rst_n)
        cnt <= 0;
    else if(!send || cnt >= CNT_MAX)
        cnt <= 0;
    else if(send)
        cnt <= cnt + 1;
end
always @(posedge clk)
begin
    if(!rst_n)
        tx <= 1;
    else if(send)
    begin
        case(cnt)                 //1位占用16个时钟周期
            0: tx <= data_in_buf[0];           //低位在前,高位在后
            16: tx <= data_in_buf[1];    //bit0,占用第16~31个时钟
            32: tx <= data_in_buf[2];    //bit1,占用第47~32个时钟
            48: tx <= data_in_buf[3];    //bit2,占用第63~48个时钟
            64: tx <= data_in_buf[4];    //bit3,占用第79~64个时钟
            80: tx <= data_in_buf[5];    //bit4,占用第95~80个时钟
            96: tx <= data_in_buf[6];    //bit5,占用第111~96个时钟
            112: tx <= data_in_buf[7];   //bit6,占用第127~112个时钟
            128: tx <= data_in_buf[8];   //bit7,占用第143~128个时钟
            144: tx <= data_in_buf[9];   //发送奇偶校验位,占用第159~144个时钟
            160: tx <= data_in_buf[10];  //发送停止位,占用第160~167个时钟
            CNT_MAX: tx <= 1;            //无空闲位
            default:;
        endcase
    end
    else if(!send)
        tx <= 1;
end
endmodule


仿真波形

640.jpg


串口接收1个字节实现

串口接收部分的实现,涉及到串口数据的采样,对于MCU来说,不同单片机集成外设的处理方式有所不同,具体采样原理可以参考内核的Reference Manual。以传统51内核为例,按照所设置的波特率,每个位时间被分为16个时间片。UART接收器会在第7、8、9三个时间片进行采样,按照三取二的逻辑获得该位时间内的采样结果。其它一些类型的单片机则可能会更加严苛,例如有些工业单片机会五取三甚至七取五(设置成抗干扰模式时)。


本程序中采用的中间值采样,即取16个时钟周期中的中间位作为当前的采样值。

//Verilog实现串口协议接收,带错误指示,校验错误和停止位错误
/*
16个时钟周期接收1位,中间采样
*/
module my_uart_rx(
input clk,             //采样时钟
input rst_n,
input rx,              //UART数据输入
output reg [7:0] dataout,        //接收数据输出
output reg rx_ok,          //接收数据有效,高说明接收到一个字节
output reg err_check,      //数据出错指示
output reg err_frame     //帧出错指示
);
reg [7:0] cnt;
reg [10:0] dataout_buf;
reg rx_buf;
reg rx_negedge_flag;
reg receive;
wire busy;
wire odd_bit;   //奇校验位 = ~偶校验位
wire even_bit;  //偶校验位 = 各位异或
wire POLARITY_BIT;   //本地计算的奇偶校验
// wire polarity_ok;
// assign polarity_ok = (POLARITY_BIT == dataout_buf[9]) ? 1 : 0; //校验正确=1,否则=0
assign busy = rx_ok;
assign even_bit = ^dataout;     //一元约简,= data_in[0] ^ data_in[1] ^ .....
assign odd_bit = ~even_bit;
assign POLARITY_BIT = even_bit;  //偶校验
// assign POLARITY_BIT = odd_bit;  //奇校验
parameter CNT_MAX = 176;
//rx信号下降沿标志位
always @(posedge clk)   
begin
    if(!rst_n)
    begin
        rx_buf <= 0;
        rx_negedge_flag <= 0;
    end
    else
    begin
        rx_buf <= rx;
        rx_negedge_flag <= rx_buf & (~rx);
    end
end
//在接收期间,保持高电平
always @(posedge clk)
begin
    if(!rst_n)
        receive <= 0;
    else if (rx_negedge_flag && (~busy))  //检测到线路的下降沿并且原先线路为空闲,启动接收数据进程
        receive <= 1;      //开始接收数据
    else if(cnt == CNT_MAX)  //接收数据完成
        receive <= 0;
end
//起始位+8位数据位+校验位+停止位 = 11位 * 16 = 176个时钟周期
always @ (posedge clk)
begin
    if(!rst_n)
        cnt <= 0;
    else if(!receive || cnt >= CNT_MAX)
        cnt <= 0;
    else if(receive)
        cnt <= cnt + 1;
end
//校验错误:奇偶校验不一致
always @ (posedge clk)
begin
    if(!rst_n)
        err_check <= 0;
    else if(cnt == 152)
    begin
        // if(POLARITY_BIT == rx)
        if(POLARITY_BIT != dataout_buf[9])      //奇偶校验正确
            err_check <= 1;         //锁存
        // else
            // err_check <= 1;
    end
end
//帧错误:停止位不为1
always @ (posedge clk)
begin
    if(!rst_n)
        err_frame <= 0;
    else if(cnt == CNT_MAX)
    begin
        if(dataout_buf[10] != 1)        //停止位
            err_frame <= 1;
        // else
            // err_frame <= 1;      //如果没有接收到停止位,表示帧出错
    end
end
always @ (posedge clk)
begin
    if(!rst_n)
        dataout <= 11'h00;
    else if(receive)
    begin
        // if(rx_ok)
        if(cnt >= 137)
            dataout <= dataout_buf[8:1];        //数据位:8-1位
        // else if(!rx_ok)
            // dataout <= 0;
    end
end
always @ (posedge clk)
begin
    if(!rst_n)
        rx_ok <= 0;
    else if(receive)
    begin
        if(cnt >= 137)   //137-169
            rx_ok <= 1;
        else
            rx_ok <= 0;
    end
    else
        rx_ok <= 0;
end
//起始位+8位数据+奇偶校验位+停止位 = 11 * 16 = 176位
always @(posedge clk)
begin
    if(!rst_n)
        dataout_buf <= 8'h00;
    else if(receive)
    begin
        case (cnt)      //中间采样
            8'd8: dataout_buf[0] <= rx;         //起始位=0
            8'd24: dataout_buf[1] <= rx;        //LSB低位在前
            8'd40: dataout_buf[2] <= rx;
            8'd56: dataout_buf[3] <= rx;
            8'd72: dataout_buf[4] <= rx;
            8'd88: dataout_buf[5] <= rx;
            8'd104: dataout_buf[6] <= rx;
            8'd120: dataout_buf[7] <= rx;
            8'd136: dataout_buf[8] <= rx;       //MSB高位在后
            8'd152: dataout_buf[9] <= rx;       //奇偶校验位
            8'd168: dataout_buf[10] <= rx;      //停止位=1
            default:;
        endcase
    end
end
endmodule


640.jpg


目录
相关文章
|
芯片 异构计算
详解串行通信协议及其FPGA实现(一)
详解串行通信协议及其FPGA实现(一)
220 0
详解串行通信协议及其FPGA实现(一)
|
1月前
|
机器学习/深度学习 算法 异构计算
m基于FPGA的多通道FIR滤波器verilog实现,包含testbench测试文件
本文介绍了使用VIVADO 2019.2仿真的多通道FIR滤波器设计。展示了系统RTL结构图,并简述了FIR滤波器的基本理论,包括单通道和多通道的概念、常见结构及设计方法,如窗函数法、频率采样法、优化算法和机器学习方法。此外,还提供了Verilog核心程序代码,用于实现4通道滤波器模块,包含时钟、复位信号及输入输出接口的定义。
72 7
|
1月前
|
算法 异构计算
基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench
基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench
|
1月前
|
算法 异构计算
m基于FPGA的电子钟verilog实现,可设置闹钟,包含testbench测试文件
该文介绍了基于FPGA的电子钟设计,利用Vivado2019.2平台进行开发并展示测试结果。电子钟设计采用Verilog硬件描述语言,核心包括振荡器、分频器和计数器。时间显示为2个十进制格式,闹钟功能通过存储器和比较器实现,当当前时间等于设定时间时触发。文中给出了Verilog核心程序示例,展示了时钟信号、设置信号及输出的交互。
111 2
|
1月前
|
算法 5G 数据处理
m基于FPGA的PPM光学脉位调制解调系统verilog实现,包含testbench
m基于FPGA的PPM光学脉位调制解调系统verilog实现,包含testbench
65 0
|
1月前
|
算法 异构计算 索引
m基于FPGA的Hamming汉明编译码verilog实现,包含testbench测试文件,不使用IP核
m基于FPGA的Hamming汉明编译码verilog实现,包含testbench测试文件,不使用IP核
71 1
|
1月前
|
编解码 算法 异构计算
基于FPGA的NC图像质量评估verilog实现,包含testbench和MATLAB辅助验证程序
在Vivado 2019.2和Matlab 2022a中测试的图像质量评估算法展示了效果。该算法基于NC指标,衡量图像与原始图像的相似度,关注分辨率、色彩深度和失真。提供的Verilog代码段用于读取并比较两个BMP文件,计算NC值。
|
1月前
|
算法 异构计算
m基于FPGA的MPPT最大功率跟踪算法verilog实现,包含testbench
该内容包括三部分:1) 展示了Vivado 2019.2和Matlab中关于某种算法的仿真结果图像,可能与太阳能光伏系统的最大功率点跟踪(MPPT)相关。2) 简述了MPPT中的爬山法原理,通过调整光伏电池工作点以找到最大功率输出。3) 提供了一个Verilog程序模块`MPPT_test_tops`,用于测试MPPT算法,其中包含`UI_test`和`MPPT_module_U`两个子模块,处理光伏电流和电压信号。
23 1
|
1月前
|
算法 异构计算
m基于FPGA的RS+卷积级联编译码实现,RS用IP核实现,卷积用verilog实现,包含testbench测试文件
m基于FPGA的RS+卷积级联编译码实现,RS用IP核实现,卷积用verilog实现,包含testbench测试文件
27 0
|
19天前
|
算法 计算机视觉 异构计算
基于FPGA的图像直方图均衡化处理verilog实现,包含tb测试文件和MATLAB辅助验证
摘要: 在FPGA上实现了图像直方图均衡化算法,通过MATLAB2022a与Vivado2019.2进行仿真和验证。核心程序涉及灰度直方图计算、累积分布及映射变换。算法旨在提升图像全局对比度,尤其适合低对比度图像。FPGA利用可编程增益器和查表技术加速硬件处理,实现像素灰度的均匀重分布,提升视觉效果。![image preview](https://ucc.alicdn.com/pic/developer-ecology/3tnl7rfrqv6tw_a075525027db4afbb9c0529921fd0152.png)

热门文章

最新文章