功能描述:
使用遵循uart协议的接收模块接收控制信号,用来控制led的闪烁。
设计输入:
1.uart输入信号
2.时钟信号
3.复位信号
4.led信号
设计思路:
总体上:前面已经写了串口接收模块,led闪烁模块。现在要把接收到的多个数据对应传送给led闪烁模块的输入(闪烁周期,亮灭模式设置),我们再写一个模块来实现传输的功能,然后在顶层把这三个模块进行连线就可以了。
细节处:单个数据的传输遵循uart协议,而为了确定传送来的数据哪个对应led的时间设置,哪个对应亮灭模式设置,我们也需要人为再设定一个协议,第一,二个数据为某固定值时,视为开端,然后接着是对应数据,最后一个数据是固定值。发送数据端应按照这个协议来发送各个数据,这样子接收端就可以按照这个协议来控制输入led的信号了。
注意
①在代码中用到了延时,如#xx......等,则应在文件第一行写明时间单位和精度:·timescale ns/ns 。
②如果想让某个标志信号siagnl_a 延后一个周期,可以新设定一个信号siagnl_b,让siagnl_b永远等于siagnl_a就行。
③在本次设计中,修正了receive_rx_1代码中把r_data赋给data的时刻以及清零时刻。
学习:
①设计底层模块convert,顶层直接连线很方便。
②对于串口通信的应用,一般都有串口接收模块+移位寄存器+协议+应用功能 四个部分组成,其中,串口模块和功能模块一般已经设计好了,我们要设计移位寄存器和协议,即接收到的数据用来干嘛,怎么排序对应。
③判断条件设定的技巧: if(const <= variable),有两个好处:Ⅰ.常数const不能被变量variabl赋值,以此检查会不会把比较符写成赋值符。Ⅱ.用<=取代 == ,可以避免 == 的时刻已经被跳过,导致判断条件无法实现。
设计:
代码:
`timescale 1ns / 1ns module uart_rx_ctrl_led(//顶层,就是连线 clk, reset, uart_tx, led ); input clk; input reset; input uart_tx; output led; //本模型由uart_receive到led_chang的传输协议为: //0x0f,0xab,tim[31:24] tim[23:16] tim[15:8] tim[7:0] ctrl[7:0] oxa5 //首先例化三个模块进来 wire [2:0]baud_rate ; assign baud_rate = 3'd5 ; wire [7:0]data; wire rx_done; uart_receive_1 uart_receive(//接收模块 clk , reset , baud_rate , uart_tx, data , rx_done ); wire [31:0]tim; wire [7:0]ctrl; uart_led_convert uart_led_convert(//转换模块 data, rx_done, clk, reset, tim, ctrl ); led_change4 led_change4(//led闪烁模块 clk, reset, ctrl, tim, led ); endmodule
module uart_receive_1( clk , reset , baud_rate , uart_tx, data , rx_done ); input clk ; input reset ; input [2:0]baud_rate ; input uart_tx ; output reg [7:0]data ; output reg rx_done ; reg [2:0]r_data[7:0] ;//接收每一位数据 reg [2:0]sta_bit ; reg [2:0]sto_bit ; reg [17:0]bit_tim ;//每一位持续的时间(计数) always@(baud_rate) //在这里一个 码元由一位组成,所以波特率=比特率 begin case(baud_rate) //常见的串口传输波特率 3'd0 : bit_tim = 1000000000/300/20 ; //波特率为300 3'd1 : bit_tim = 1000000000/1200/20 ; //波特率为1200 3'd2 : bit_tim = 1000000000/2400/20 ; //波特率为2400 3'd3 : bit_tim = 1000000000/9600/20 ; //波特率为9600 3'd4 : bit_tim = 1000000000/19200/20 ; //波特率为19200 3'd5 : bit_tim = 1000000000/115200/20 ; //波特率为115200 default bit_tim = 1000000000/9600/20 ; //多余的寄存器位置放什么:默认速率 endcase end wire [17:0]bit_tim_16 ;//每1/16位的持续时间(计数) assign bit_tim_16 = bit_tim / 16; wire [8:0]bit16_mid ; //在中心点产生采样脉冲 assign bit16_mid = bit_tim_16 / 2 ; //边沿检测 reg [1:0]edge_detect ; always @( posedge clk or negedge reset ) begin if (!reset ) edge_detect <= 2'd0 ; else begin edge_detect[0] <= uart_tx ; edge_detect[1] <= edge_detect[0] ; end end wire byte_sta_neg ; assign byte_sta_neg = ( edge_detect == 2'b10 ) ? 1 : 0 ;//输入的数据开始出现下降沿,说明出现了起始位(一直运行?) reg receive_en ;//接收使能端 reg [17:0]div_cnt ;//每1/16bit内的计数 reg [7:0]bit16_cnt ;//计数到了第几个状态(10位,每位分成16份,总共160个状态) always @( posedge clk or negedge reset ) begin if (!reset ) receive_en <= 1'd0 ; else if ( byte_sta_neg ) //检测到下降沿,使能段有效(只要有下降沿就使能?) receive_en <= 1'd1 ; else if ( (rx_done) || (sta_bit >= 3'd4 )) receive_en <= 1'd0 ; //检测到结束信号,使能端无效 else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )//跑完159后re_en置零 receive_en <= 1'd0 ; end always@( posedge clk or negedge reset ) begin if ( ! reset ) div_cnt <= 18'd0 ; else if (receive_en) begin if ( div_cnt == bit_tim_16 - 1'd1 )//计数,每1/16bit清零 div_cnt <= 18'd0 ; else div_cnt <= div_cnt + 1'b1 ; end else div_cnt <= 18'd0 ; end reg bit16_pulse ;//产生采样脉冲 always@( posedge clk or negedge reset ) begin if ( ! reset ) bit16_pulse <= 18'd0 ; else if (receive_en) if ( div_cnt == bit16_mid ) bit16_pulse <= 1'd1 ; else bit16_pulse <= 1'd0 ; else bit16_pulse <= 1'd0 ; end always@( posedge clk or negedge reset ) begin if ( ! reset ) bit16_cnt <= 8'd0 ; else if (receive_en) begin if (( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 )) bit16_cnt <= 8'd0 ; else if ( div_cnt == bit_tim_16 - 1'd1 ) bit16_cnt <= bit16_cnt + 1'b1 ; end end always@(posedge clk or negedge reset) begin if(!reset) begin sta_bit <= 3'd0 ; r_data[0] <= 3'd0 ; r_data[1] <= 3'd0 ; r_data[2] <= 3'd0 ; r_data[3] <= 3'd0 ; r_data[4] <= 3'd0 ; r_data[5] <= 3'd0 ; r_data[6] <= 3'd0 ; r_data[7] <= 3'd0 ; sto_bit <= 3'd0 ; end else if (bit16_pulse)//舍弃前5后4取中7 case(bit16_cnt) 0: begin sta_bit <= 3'd0 ; r_data[0] <= 3'd0 ; r_data[1] <= 3'd0 ; r_data[2] <= 3'd0 ; r_data[3] <= 3'd0 ; r_data[4] <= 3'd0 ; r_data[5] <= 3'd0 ; r_data[6] <= 3'd0 ; r_data[7] <= 3'd0 ; sto_bit <= 3'd0 ; end 5,6,7,8,9,10,11 : sta_bit <= sta_bit + uart_tx ; 21,22,23,24,25,26,27 : r_data[0] <= r_data[0] + uart_tx ; 37,38,39,41,42,43,44 : r_data[1] <= r_data[1] + uart_tx ; 53,54,55,56,57,58,59 : r_data[2] <= r_data[2] + uart_tx ; 69,70,71,72,73,74,75 : r_data[3] <= r_data[3] + uart_tx ; 85,86,87,88,89,90,91 : r_data[4] <= r_data[4] + uart_tx ; 101,102,103,104,105,106,107 : r_data[5] <= r_data[5] + uart_tx ; 117,118,119,120,121,122,123 : r_data[6] <= r_data[6] + uart_tx ; 133,134,135,136,137,138,139 : r_data[7] <= r_data[7] + uart_tx ; 149,150,151,152,153,154,155 : sto_bit <= sto_bit + uart_tx ; default ; endcase end always@( posedge clk or negedge reset ) begin if ( ! reset ) rx_done <= 8'd0 ; else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )//跑完159后产生一个rx_done信号 rx_done <= 8'd1 ; else if (rx_done <= 8'd1 ) rx_done <= 8'd0 ; end always@( posedge clk or negedge reset )//接收完数据发出rx_done后,把数据从r_data传递给data begin if ( ! reset ) data <= 8'd0 ; else if (bit16_pulse)//舍弃前5后4取中7 case(bit16_cnt) 28:data[0] = ( r_data[0] >3 ) ? 1 : 0 ; 44:data[1] = ( r_data[1] >3 ) ? 1 : 0 ; 60:data[2] = ( r_data[2] >3 ) ? 1 : 0 ; 76:data[3] = ( r_data[3] >3 ) ? 1 : 0 ; 92:data[4] = ( r_data[4] >3 ) ? 1 : 0 ; 108:data[5] = ( r_data[5] >3 ) ? 1 : 0 ; 124:data[6] = ( r_data[6] >3 ) ? 1 : 0 ; 140:data[7] = ( r_data[7] >3 ) ? 1 : 0 ; endcase else if(rx_done) data <= 8'd0 ; end endmodule
`timescale 1ns / 1ns module uart_led_convert( data, rx_done, clk, reset, tim, ctrl ); input [7:0]data; input rx_done; input clk; input reset; output reg [31:0]tim; output reg [7:0]ctrl; //设定本模型由uart_receive到led_chang的传输协议为: //0x0f,0xab,tim[31:24] tim[23:16] tim[15:8] tim[7:0] ctrl[7:0] oxa5 //设计一个二维移位寄存器 reg [7:0]data_regist[7:0]; always@(posedge clk or negedge reset) begin if(!reset) begin data_regist[0] <= 0; data_regist[1] <= 0; data_regist[2] <= 0; data_regist[3] <= 0; data_regist[4] <= 0; data_regist[5] <= 0; data_regist[6] <= 0; data_regist[7] <= 0; end else if(rx_done) begin data_regist[0] <= data ; data_regist[1] <= data_regist[0] ; data_regist[2] <= data_regist[1] ; data_regist[3] <= data_regist[2] ; data_regist[4] <= data_regist[3] ; data_regist[5] <= data_regist[4] ; data_regist[6] <= data_regist[5] ; data_regist[7] <= data_regist[6] ; end end always@(posedge clk or negedge reset) begin if(!reset) begin tim <= 0 ; ctrl <= 0 ; end else if ( ( data_regist[0] == 8'ha5 ) && ( data_regist[6] == 8'hab ) && ( data_regist[7] == 8'h0f ) ) begin ctrl[7:0] <= data_regist[1] ; tim[7:0] <= data_regist[2] ; tim[15:8] <= data_regist[3] ; tim[23:16] <= data_regist[4] ; tim[31:24] <= data_regist[5] ; end end endmodule
module led_change4( //以tim*20/8 ns为变化周期,tim*20 ns为一个循环,每个周期的亮灭模式,tim由用户设置。. clk, reset, ctrl, tim, led ); input clk; input reset; input [7:0]ctrl; input [31:0]tim; output reg led ; reg [31:0]counter0; always@( posedge clk or negedge reset ) begin if ( reset == 0 ) counter0 <= 32'b0 ; else if ( tim - 1 <= counter0 ) counter0 <= 0 ; else counter0 <= counter0 + 1'd1; end always@( posedge clk or negedge reset ) begin if ( reset == 0 ) led <= 0 ; else case( counter0 ) //可以用第二个计数器的方法来设置判断条件 tim * 1 / 8 - 1 : led <= ctrl[0] ; tim * 2 / 8 - 1 : led <= ctrl[1] ; tim * 3 / 8 - 1 : led <= ctrl[2] ; tim * 4 / 8 - 1 : led <= ctrl[3] ; tim * 5 / 8 - 1 : led <= ctrl[4] ; tim * 6 / 8 - 1 : led <= ctrl[5] ; tim * 7 / 8 - 1 : led <= ctrl[6] ; tim * 8 / 8 - 1 : led <= ctrl[7] ; default led <= led ; endcase end endmodule
激励代码:
`timescale 1ns / 1ns
module uart_receive_ctrl_led_tb();
reg clk;
reg reset;
reg uart_tx;
wire led;
uart_rx_ctrl_led uart_rx_ctrl_led_sim(
clk,
reset,
uart_tx,
led
);
initial clk = 1 ;
always #10 clk = ! clk ;
initial
begin
reset = 0 ;
#201 ;
reset = 1 ;
#200;
uart_txx(8'h0f);
uart_txx(8'hab);
uart_txx(8'h00);//time
uart_txx(8'h00);//time
uart_txx(8'h65);//time
uart_txx(8'h00);//time
uart_txx(8'h5a);//ctrl
uart_txx(8'ha5);
#900000;
$stop ;
end
task uart_txx;
input [7:0]sim_data ;
begin
uart_tx = 1 ;
#200 ;
uart_tx = 0 ;
#8640 ;
uart_tx = sim_data[0];
#8640 ;
uart_tx = sim_data[1];
#8640 ;
uart_tx = sim_data[2];
#8640 ;
uart_tx = sim_data[3];
#8640 ;
uart_tx = sim_data[4];
#8640 ;
uart_tx = sim_data[5];
#8640 ;
uart_tx = sim_data[6];
#8640 ;
uart_tx = sim_data[7];
#8640 ;
uart_tx = 1 ;
#8640 ;
end
endtask
endmodule