首页 > 技术文章 > FPGA——串口接收的使用(连续接收5字节并进行处理)

cnlntr 2021-01-24 19:06 原文

一、设计思路

  • led模块的功能是指定时间,控制八个时间段的亮灭,两个输入信号,8位的ctr信号,32位的time信号
    time信号的计算公式:时间(clk),例如,系统时钟为50MHz,想要一段时间为1秒,则有time = 150*10^6;
  • 串口接收后如何对数据进程处理,将40bit的数据,输入到led模块?
    • 引入校验位,例如:只有输入的前两个字节为,0x20,0x21,输入的后一个字节为0xCE,才为正确的数据

二、串口接收以及仿真代码

[https://www.cnblogs.com/cnlntr/p/12483495.html]

三、串口接收的数据处理模块(定义与下一个模块的通信协议)

module uart_tx_cmd(
   clk      ,
   rst_n    ,
   rx_done  ,
   data     ,
   ctr      ,
   tim
);
parameter   DATA_W   =  8;
parameter   CTR_W    =  8;
parameter   TIM_W    =  32;
parameter   CNTD_N   =  8; //一串完整数据所包含字节的个数,两个起始校验字节+5个数据字节+1个终值校验字节
parameter   CNTD_W   =  3; //字节计数器位宽

input                   clk;
input                   rst_n;
input                   rx_done;
input    [DATA_W-1:0]   data;

output   [CTR_W-1:0]    ctr;
output   [TIM_W-1:0]    tim;

reg      [CTR_W-1:0]    ctr;
reg      [TIM_W-1:0]    tim;

reg      [CNTD_W-1:0]   cnt_data;
wire                    add_cnt_data;
wire                    end_cnt_data;

//8个8位的寄存器,用来存放串口发送的完整数据
reg      [7:0]          data_save[7:0];
reg                     data_done;

//串口发送字节计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_data <= 0;
   else if(add_cnt_data)begin
      if(end_cnt_data)
         cnt_data <= 0;
      else
         cnt_data <= cnt_data + 1'b1;
   end
end
assign add_cnt_data = rx_done;
assign end_cnt_data = add_cnt_data && cnt_data == CNTD_N - 1;

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin 
      data_save[0] <= 0;
      data_save[1] <= 0;
      data_save[2] <= 0;
      data_save[3] <= 0;
      data_save[4] <= 0;
      data_save[5] <= 0;
      data_save[6] <= 0;
      data_save[7] <= 0;
   end
   else if(rx_done)
      data_save[cnt_data] <= data;
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      {ctr,tim} <= 40'b0;
   else if(data_done && (data_save[0]== 8'h20 && data_save[1] == 8'h21 && data_save[7] == 8'hCE))begin
      ctr <= data_save[2];
      tim[7:0]    <= data_save[6];
      tim[15:8]   <= data_save[5];
      tim[23:16]  <= data_save[4];
      tim[31:24]  <= data_save[3];
   end
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      data_done <= 0;
   else if(end_cnt_data)
      data_done <= 1;
   else
      data_done <= 0;
end

endmodule

四、led控制模块

module led_beta4(
   clk   ,
   rst_n ,
   ctr   ,
   tim   ,
   led   
);
parameter   CNT_W    =  27;            //计数器位宽
parameter   CTR_W    =  8;             //输入控制信号位宽
parameter   CNT1_W   =  3;             //控制信号计数器位宽
parameter   TIM_W    =  32;            //时间位宽


input                clk;
input                rst_n;
input    [CTR_W-1:0] ctr;
input    [TIM_W-1:0] tim;
output               led;

reg                  led;

reg   [CNT_W-1:0]    cnt;
wire                 add_cnt;
wire                 end_cnt;

reg   [CNT1_W-1:0]   cnt_ctr;
wire                 add_cnt_ctr;
wire                 end_cnt_ctr;
wire                 led_tmp;

//0.25s计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt <= 0;
   else if(add_cnt)begin
      if(end_cnt)
         cnt <= 0;
      else
         cnt <= cnt + 1'b1;
   end
end
assign add_cnt = (tim >= 1);
assign end_cnt = add_cnt && cnt == tim - 1;

//8位控制信号计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_ctr <= 0;
   else if(add_cnt_ctr)begin
      if(end_cnt_ctr)
         cnt_ctr <= 0;
      else
         cnt_ctr <= cnt_ctr + 1'b1;
   end
end
assign add_cnt_ctr = end_cnt;
assign end_cnt_ctr = add_cnt_ctr && cnt_ctr == CTR_W - 1;

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      led <= 1;
   else
      led <= led_tmp;
end

assign led_tmp = ctr[cnt_ctr];

endmodule

五、顶层模块

module uart_rx_ctrl_led(
   clk      ,
   rst_n    ,
   led      ,
   uart_rx  
);

input    clk;
input    rst_n;
input    uart_rx;
output   led;

wire     led;

wire  [31:0]   tim;
wire  [7:0]    ctr;
wire  [7:0]    data;
wire           rx_done;

led_beta4 led_beta4(
   .clk     (clk),
   .rst_n   (rst_n),
   .ctr     (ctr),
   .tim     (tim),
   .led     (led)
);

my_uart_rx my_uart_rx(
   .rst_n      (rst_n),
   .clk        (clk),
   .uart_rx    (uart_rx),
   .baud_set   (4'd4),
   .data       (data),
   .rx_done    (rx_done) 
);

uart_tx_cmd uart_tx_cmd(
   .clk     (clk),
   .rst_n   (rst_n),
   .rx_done (rx_done),
   .data    (data),
   .ctr     (ctr),
   .tim     (tim)
);

endmodule

六、仿真代码

`timescale 1ns / 1ns

module uart_rx_ctrl_led_tb();

parameter CYCLE   =  20;

reg   clk ;
reg   rst_n;
wire  led;
reg   uart_rx;

uart_rx_ctrl_led uart_rx_ctrl_led(
   clk      ,
   rst_n    ,
   led      ,
   uart_rx  
);

initial begin
   clk = 1;
   forever 
   #(CYCLE/2)
   clk = ~clk;
end

initial begin
   rst_n = 1;
   #3
   rst_n = 0;
   #(10*CYCLE)
   rst_n = 1;
end

initial begin
   uart_rx = 1'b1;
   @(posedge rst_n);
   #(8*CYCLE);
   uart_tx(8'h20);
   @(posedge uart_rx_ctrl_led.rx_done);
   uart_tx(8'h21);
   @(posedge uart_rx_ctrl_led.rx_done);
   uart_tx(8'hE8);
   @(posedge uart_rx_ctrl_led.rx_done);
   uart_tx(8'h03);
   @(posedge uart_rx_ctrl_led.rx_done);
   uart_tx(8'h00);
   @(posedge uart_rx_ctrl_led.rx_done);
   uart_tx(8'h00);
   @(posedge uart_rx_ctrl_led.rx_done);
   uart_tx(8'h00);
   @(posedge uart_rx_ctrl_led.rx_done);
   uart_tx(8'hCE);
   @(posedge uart_rx_ctrl_led.rx_done);
   #2000000;
   $stop;
end

task uart_tx;
   input [7:0] data_in;
   begin
      uart_rx = 1'b0;
      #(5208*CYCLE);
      uart_rx = data_in[0];
      #(5208*CYCLE);
      uart_rx = data_in[1];
      #(5208*CYCLE);
      uart_rx = data_in[2];
      #(5208*CYCLE);
      uart_rx = data_in[3];
      #(5208*CYCLE);
      uart_rx = data_in[4];
      #(5208*CYCLE);
      uart_rx = data_in[5];
      #(5208*CYCLE);
      uart_rx = data_in[6];
      #(5208*CYCLE);
      uart_rx = data_in[7];
      #(5208*CYCLE);
      uart_rx = 1'b1;
   end
endtask

endmodule

七、小结

计数器问题
第一个代码:加一条件为1,结束条件是由变量控制的,当变量tim输入为0的时候就变成了,0-1 = FFFF FFFF FFFF FFFF
因此要改动为第二个代码

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt <= 0;
   else if(add_cnt)begin
      if(end_cnt)
         cnt <= 0;
      else
         cnt <= cnt + 1'b1;
   end
end
assign add_cnt = 1;
assign end_cnt = add_cnt && cnt == tim - 1;
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt <= 0;
   else if(add_cnt)begin
      if(end_cnt)
         cnt <= 0;
      else
         cnt <= cnt + 1'b1;
   end
end
assign add_cnt = (tim >= 1);
assign end_cnt = add_cnt && cnt == tim - 1;

推荐阅读