使用uart串口接收模块接收信号,控制led灯闪烁
功能描述:
使用遵循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

浙公网安备 33010602011771号