【用状态机实现串口连发(FPGA)】

目录

目录

目录

一,串口连发的动机

二,模块构成

三,uart_rx

四,uart_tx

五,bcd模块

六,主模块

七,批评与指正


一,串口连发的动机

 起初显示一些6位8Bit数据都是使用数码管,后来发现实在不够,就改串口PC端显示了。

二,模块构成

 本文基于野火征途PRO板,在野火官方视频代码模块下进行改进,其中使用了野火uart_tx, uart_rx,bcd编码模块和状态机编写,其中有不懂的模块可以自行观看野火视频(我也是初学呜呜)

三,uart_rx

 使用uart_rx模块主要是为了给一个开始发送的信号:当uart_rx接收到数据时,产生一个脉冲信号po_flag通过主模块告知uart_tx开始发送信息(该模块直接写的野火的逻辑没有做更改)。

module  uart_rx//接收数据
#(
parameter   BPS=20'd9600,
parameter   CLK=32'd50_000_000
)
(
input   wire            sys_clk     ,
input   wire            sys_rst_n   ,
input   wire            rx          ,//串口接收信号

output  reg     [7:0]   po_data     ,
output  reg             po_flag     

);

reg                 rx_reg1,rx_reg2;//亚稳态稳定位
reg                 rx_reg3,start_nedge;//找到起始位下降沿标志信号
reg                 work_en;//接受8bit使能信号,排除下一rx下降沿干扰
reg     [15:0]      baud_cnt;//计数中间时刻接收1bit
reg                 bit_flag;//接受bit脉冲
reg     [3:0]       bit_cnt;//计数bit数
reg     [7:0]       rx_data;//组合并行数据
reg                 rx_flag;//赋值脉冲,保证他的上升沿数据赋值完成
//reg                 yah_flag;

localparam  cnt_max=CLK/BPS-1;
localparam  cnt_mid=(cnt_max+1)/2;
//亚稳态
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg1 <= 1'b1;
    else
        rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg2 <= 1'b1;
    else
        rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;        
        
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0)        
        start_nedge<=1'b0;
    else    if((rx_reg2==1'b0)&&(rx_reg3==1'b1))
                start_nedge<=1'b1;
    else    start_nedge<=1'b0;
    
//使能信号       
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0) 
        work_en<=1'b0;
    else    if((start_nedge==1'b1)&&(work_en==1'b0))
                work_en<=1'b1;
    else    if((bit_cnt==4'd8)&&(bit_flag==1'b1))
                work_en<=1'b0;
    else    work_en<=work_en;

//计数得BPS中间时刻
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0) 
        baud_cnt<=16'd0;
    else    if((baud_cnt==cnt_max)||(work_en==1'b0))//或者work_en==1'd0
                baud_cnt<=16'd0;
    else    if(work_en==1'b1)
                baud_cnt<=baud_cnt+1'b1;
                
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0)
        bit_flag<=1'b0;
    else    if(baud_cnt==cnt_mid)
                bit_flag<=1'b1;
    else    bit_flag<=1'b0;

always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0)
        bit_cnt<=4'd0;
    else    if((bit_cnt==4'd8)&&(bit_flag==1'b1))
                bit_cnt<=4'd0;
    else    if(bit_flag==1'b1)
                bit_cnt<=bit_cnt+1'b1;
    else    bit_cnt<=bit_cnt;
      
//data串行数据转并行数据
//组合并行数据
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0) 
        rx_data<=8'd0;
    else    if((bit_cnt>=4'd1)&&(bit_cnt<=4'd8)&&(bit_flag==1'b1))
                rx_data<={rx_reg3,rx_data[7:1]};
                 
//数据形成标志    
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0)    
        rx_flag<=1'b0;
    else    if((bit_cnt==4'd8)&&(bit_flag==1'b1))
                rx_flag<=1'b1;
    else    rx_flag<=1'b0; 
    

always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0)
        po_data<=8'd0;
    else    if(rx_flag==1'b1)
            po_data<=rx_data;
    
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n==1'b0)     
        po_flag<=1'b0;
    else    if(rx_flag==1'b1)
                po_flag<=1'b1;
    else    po_flag<=1'b0;

      

endmodule

四,uart_tx

 对uart_tx模块增加了input  wire tx_down信号,用于告知连发结束;增加了output reg trans_flag信号告知状态机一组数据发送结束,并且注意要trans_flag信号的生成条件(当时这里弄了好久);做完这些后发现串口输出只有第一组数据正确,所以将tx空闲时间拉长,让bit_cnt计数到10,并让此时tx<=1'b1。

module  uart_tx
#(
parameter   BPS=20'd9600,
parameter   CLK=32'd50_000_000  
)
(
input   wire            sys_clk     ,
input   wire            sys_rst_n   ,
input   wire    [7:0]   po_data     ,
input   wire            po_flag     ,
input   wire            tx_down     ,//1:结束输出

output  reg             tx          ,
output  reg             trans_flag   //完成输出一组数据 
);


reg             work_en;
reg     [15:0]  baud_cnt;//计数中间接收
reg             bit_flag;//接收标志
reg     [3:0]   bit_cnt;//计数接收bit数

localparam  cnt_max=CLK/BPS-1;


always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        work_en<=1'b0; 
    else    if(tx_down==1'b1)
                work_en<=1'b0;    
    else    if((bit_cnt==4'd10)&&(bit_flag==1'b1))
                work_en<=1'b0;
    else    if(po_flag==1'b1)
                work_en<=1'b1;
    else    work_en<=work_en;

always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        baud_cnt<=16'd0;
    else    if((baud_cnt==cnt_max)||(work_en==1'b0))
                baud_cnt<=16'd0;
    else    baud_cnt<=baud_cnt+1'b1;

always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        bit_flag<=1'b0;
    else    if(baud_cnt==16'd1)
                bit_flag<=1'b1;
    else    bit_flag<=1'b0;

   
always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        bit_cnt<=4'd0;
    else    if((bit_cnt==4'd10)&&(bit_flag==1'b1))
                bit_cnt<=4'd0;
    else    if((bit_flag==1'b1)&&(work_en==1'b1))//把work_en用起来啊        
        bit_cnt<=bit_cnt+1'b1;
            
always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        tx<=1'b1;//空闲为高电平
    else    if((bit_flag==1'b1)&&(bit_cnt>=4'd0)&&(bit_cnt<=4'd10))
                case(bit_cnt)
                0: tx<=1'b0;      
                1: tx<=po_data[0];
                2: tx<=po_data[1];
                3: tx<=po_data[2];
                4: tx<=po_data[3];
                5: tx<=po_data[4];
                6: tx<=po_data[5];
                7: tx<=po_data[6];
                8: tx<=po_data[7];
                9: tx<=1'b1;
                10:tx<=1'b1;
                default:tx<=1'b1; 
                endcase
                
always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        trans_flag<=1'b0;
    else    if((bit_cnt==4'd10)&&(bit_flag == 1'b1))
                trans_flag<=1'b1;
    else    trans_flag<=1'b0;            

endmodule

五,bcd模块

 因为我是要发送9位十进制数据(增大了data的大小),并且由于野火串口只有ASCII和Hex显示,所以采用bcd_8421编码让HEX与十进制数据相同,方便观察。

module  bcd_8421
(
    input   wire    sys_clk,
    input   wire    sys_rst_n,
    input   wire    [27:0]  data,
    
    output  reg     [3:0]   unit,
    output  reg     [3:0]   ten,
    output  reg     [3:0]   hum,        
    output  reg     [3:0]   tho,
    output  reg     [3:0]   t_tho,
    output  reg     [3:0]   h_hum,
    output  reg     [3:0]   M_unit,    
    output  reg     [3:0]   M_ten,
    output  reg     [3:0]   M_hum  
);    
    
reg     [5:0]   cnt_shift;
reg     [63:0]  data_shift;
reg     shift_flag;

always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
       shift_flag<=1'b0;
    else    shift_flag<=~shift_flag;

always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        cnt_shift<=5'd0;
    else    if((cnt_shift==5'd29)&&(shift_flag==1'b1))//改动cnt_shift_max
                cnt_shift<=5'd0;
    else    if(shift_flag==1'b1)
                cnt_shift<=cnt_shift+5'd1;
    else    cnt_shift<=cnt_shift;

always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        data_shift<=64'b0;
    else    if(cnt_shift==5'd0)
                data_shift<={36'b0,data};//补零
    else    if((cnt_shift<=28)&&(shift_flag==1'b0))//改动cnt_shif<=20
    begin//改动
                data_shift[31:28]<=(data_shift[31:28]>4)?(data_shift[31:28]+2'd3):(data_shift[31:28]);
                data_shift[35:32]<=(data_shift[35:32]>4)?(data_shift[35:32]+2'd3):(data_shift[35:32]);
                data_shift[39:36]<=(data_shift[39:36]>4)?(data_shift[39:36]+2'd3):(data_shift[39:36]);
                data_shift[43:40]<=(data_shift[43:40]>4)?(data_shift[43:40]+2'd3):(data_shift[43:40]);
                data_shift[47:44]<=(data_shift[47:44]>4)?(data_shift[47:44]+2'd3):(data_shift[47:44]);
                data_shift[51:48]<=(data_shift[51:48]>4)?(data_shift[51:48]+2'd3):(data_shift[51:48]);
                data_shift[55:52]<=(data_shift[55:52]>4)?(data_shift[55:52]+2'd3):(data_shift[55:52]);
                data_shift[59:56]<=(data_shift[59:56]>4)?(data_shift[59:56]+2'd3):(data_shift[59:56]);
                data_shift[63:60]<=(data_shift[63:60]>4)?(data_shift[63:60]+2'd3):(data_shift[63:60]);
                
                end
    else    if((cnt_shift<=28)&&(shift_flag==1'b1))//改动
                data_shift<=data_shift<<1;
    else    data_shift<=data_shift;

always@(posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        begin
           unit    <=4'b0;
           ten     <=4'b0;
           hum     <=4'b0;
           tho     <=4'b0;
           t_tho   <=4'b0;
           h_hum   <=4'b0;
           M_unit  <=4'b0;
           M_ten   <=4'b0;
           M_hum   <=4'b0;
        end
    else    if(cnt_shift==5'd29)//21-29
        begin
            unit    <=data_shift[31:28];//个
            ten     <=data_shift[35:32];
            hum     <=data_shift[39:36];
            tho     <=data_shift[43:40];//K
            t_tho   <=data_shift[47:44];
            h_hum   <=data_shift[51:48];
            M_unit  <=data_shift[55:52];//个
            M_ten   <=data_shift[59:56];
            M_hum   <=data_shift[63:60];
        end                    
        
endmodule

六,主模块

 编写状态机,不会的可以看野火状态机视频哦。

module  rs232
(
    input   wire    sys_clk             ,
    input   wire    sys_rst_n           ,
    input   wire    rx                  ,
    input   wire    [27:0]   data        ,//freq

    output  wire    tx
);

parameter   BPS=20'd9600;
parameter   CLK=32'd50_000_000;

wire [7:0]   po_data;//接收的信号
wire         po_flag;
reg  [7:0]   pi_data;//发送的信号
reg          pi_flag;
wire [3:0]   unit  ;//个
wire [3:0]   ten   ;//十
wire [3:0]   hum   ;//百
wire [3:0]   tho   ;//千K
wire [3:0]   t_tho ;//万
wire [3:0]   h_hum ;//十万
wire [3:0]   M_unit;//M
wire [3:0]   M_ten ;
wire [3:0]   M_hum ;

wire         trans_flag;
reg          tx_down;
//4;独热码
parameter   IDLE=5'b00001;
parameter   HUN_THO=5'b00100;
parameter   HIGH_HT=5'b00010;
parameter   M_U_T=5'b01000;
parameter   M_ax=5'b10000;
parameter   STOP=5'b0000;

reg [4:0]   state;
reg [7:0]   re_data;

always@(posedge sys_clk or negedge  sys_rst_n)
        if(sys_rst_n==1'b0)
            begin   state<=IDLE;
                    re_data<=8'd0;
                    tx_down<=1'b0;
            end
        else    case(state)
                IDLE:if(po_flag==1'b1)//进入触发转换
                        begin
                        re_data<={4'd0,M_hum}; 
                        pi_flag<=1'b1;
                        state<=M_U_T;
                        tx_down<=1'b0;
                        end
                    else    begin
                            re_data<=re_data;
                            pi_flag<=1'b0;
                            state<=IDLE;
                            end
                    
                M_U_T:if(trans_flag==1'b1)
                        begin
                        re_data<={M_ten,M_unit};
                        pi_flag<=1'b1;
                        state<=HIGH_HT;
                        end
                    else   begin  
                            pi_flag<=1'b0;
                            state<=M_U_T;
                            re_data<=re_data;
                            end
                    
                HIGH_HT:if(trans_flag==1'b1)
                        begin
                        re_data<={h_hum,t_tho};
                        pi_flag<=1'b1;
                        state<=HUN_THO;
                        end                       
                    else   begin  
                            pi_flag<=1'b0;
                            re_data<=re_data;
                            state<=HIGH_HT;                
                            end
                HUN_THO:if(trans_flag==1'b1)
                        begin
                        re_data<={tho,hum};
                        pi_flag<=1'b1;
                        state<=M_ax;
                        end                       
                    else   begin  
                            pi_flag<=1'b0;
                            re_data<=re_data;
                            state<=HUN_THO;                
                            end  
                M_ax:if(trans_flag==1'b1)
                        begin
                        re_data<={ten,unit};
                        pi_flag<=1'b1;
                        state<=STOP;
                        end                       
                    else   begin  
                            pi_flag<=1'b0;
                            re_data<=re_data;
                            state<=M_ax;                
                            end 
                         
                STOP:if(trans_flag==1'b1)
                       begin
                       tx_down<=1'b1; 
                       pi_flag<=1'b0;
                       state<=IDLE;
                       end//复原
                    else    begin
                            re_data<=re_data;
                            pi_flag<=1'b0;
                            state<=STOP;
                            end
                default:begin
                state<=IDLE;
                end
                endcase
             
    
uart_tx//再发送信号
#(
.   BPS(BPS),
.   CLK(CLK)
)
uart_tx0
(
. sys_clk     (sys_clk),
. sys_rst_n   (sys_rst_n),
. po_data     (re_data),
. po_flag     (pi_flag),
. tx_down     (tx_down),

. tx          (tx),
. trans_flag  (trans_flag)

);


uart_rx//先接收外界信号
#(
.   BPS(BPS),
.   CLK(CLK)
)
uart_rx
(
. sys_clk     (sys_clk),
. sys_rst_n   (sys_rst_n),
. rx          (rx),//串口接收信号

. po_data     (po_data),
. po_flag     (po_flag)

);

bcd_8421    bcd_84211
(
    .sys_clk     (sys_clk),
    .sys_rst_n   (sys_rst_n),
    .data        (data),

    .unit        (unit),//十万
    .ten         (ten),//万
    .hum         (hum),//千 
    .tho         (tho),//百
    .t_tho       (t_tho),//十
    .h_hum       (h_hum),//个
    .M_unit      (M_unit),
    .M_ten       (M_ten),
    .M_hum       (M_hum)   
    
); 


endmodule

七,批评与指正

 我也是才开始学FPGA,上述语言有不对的地方可以留言,我一定认真改写;此外,我也是第一次写CSDN博客,所以有什么格式和建议都可以提出,我会认真选取采纳的(玫瑰玫瑰)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值