在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 957|回复: 1

[原创] 串行协议及其实现

[复制链接]
发表于 2023-5-1 15:27:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 xxyang2022 于 2023-5-1 17:18 编辑

    1 数据传输结构
    数据传输的波特率,即1秒发送的二进制的位数。



    如果要传输1个字节的数据,实际上不是只传输8位,因为数据传输要有起始位、停止位和校验位,传输结构为:1位起始位+数据位+1位奇偶校验位+1位停止位。起始位一般为0,停止位一般为1,下面介绍奇偶校验:由于数据传输的不稳定性,有时候会把1串成0或者0传成1,那我们怎么判断一段数据是否可靠呢,通过判断一段数据中1或者0的个数可以简单的进行判断。偶校验就是看数据传输前后,1的个数是否为偶数,只需要把各数据位异或即可。奇校验就是看数据传输前后,1的个数是否为奇数,把各数据位异或后取反即可。

    2 代码实现
    1串口发送一个字节,每一位占用16个时钟周期。(可以复制到notpad++等观察代码,会比较清晰)
    module uart_tx_8bit(
input clk,               
input rst_n,
input [7:0] data_in,      
input trig,                //发送数据标志

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 or negedge rst_n) //上升沿信号
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 or negedge rst_n) //send有效
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 or negedge rst_n) //拼接data 起始 数据 校验 停止
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 or negedge rst_n)
begin
    if(!rst_n)
        cnt <= 0;
    else if(!send || cnt >= CNT_MAX)
        cnt <= 0;
    else if(send)
        cnt <= cnt + 1;
end

always @(posedge clk or negedge rst_n)
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
    2 串口接受一个字节
接受字节要有数据有效提示,数据出错提示,其中数据错误分为校验错误和错位。

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 or negedge rst_n)   
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 or negedge rst_n)
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 or negedge rst_n)
begin
    if(!rst_n)
        cnt <= 0;
    else if(!receive || cnt >= CNT_MAX)
        cnt <= 0;
    else if(receive)
        cnt <= cnt + 1;
end
//校验错误:奇偶校验不一致
always @ (posedge clk or negedge rst_n)
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 or negedge rst_n)
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 or negedge rst_n)
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 or negedge rst_n)
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 or negedge rst_n)
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



发表于 2023-5-1 15:58:31 | 显示全部楼层
谢谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条

×

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-5-8 13:22 , Processed in 0.016554 second(s), 6 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表