一、项目要求
要求用串口实现状态改变,状态1:静态点亮点阵屏,分别输出数字“1”,“2”,“3”。状态2:动态点亮点阵屏,连续输出数字“1”,“2”,“3”,间隔为1s。
二、信号流向图
根据项目要求分析出我们需要的模块和主要信号的流向。(图是比较高清的,可以放大看)
三、程序设计
接收端模块
`timescale 1ns / 1ps
/串口接收端 串行转并行
module uart_rx(
input sys_clk ,
input rst_n ,
input rx_data , //输入串行数据
output reg[7:0] uart_data , // 输出并行数据
output reg rx_done //数据传输完成结束信号
);
parameter SYSCLK = 50_000_000 ;
parameter Baud = 115200 ;
parameter COUNT = SYSCLK/Baud ;//434 传输1比特所需要的时钟周期
parameter MID = COUNT/2 ;
///产生开始信号(检测下降沿-----二级寄存)
reg rx_reg1 ;
reg rx_reg2 ;
wire start_flag ; 开始信号
reg rx_flag ;
reg [3:0] cnt_bit_rx ;///0~9
reg [9:0] cnt ;///434
reg [7:0] data_reg ;
always@(posedge sys_clk)
if(!rst_n)begin
rx_reg1 <= 1 ; //处于空闲位 数据线上无数据
rx_reg2 <= 1 ; //处于空闲位 数据线上无数据
end
else
begin
rx_reg1 <= rx_data ;
rx_reg2 <= rx_reg1 ;
end
assign start_flag = ~rx_reg1 & rx_reg2 ;
//rx_flag
always@(posedge sys_clk )
if(!rst_n)
rx_flag <= 0 ;
else if (start_flag)
rx_flag <= 1 ;
else if ( cnt_bit_rx == 9 && cnt == MID -1 )
rx_flag <= 0 ;
else
rx_flag <= rx_flag ;
cnt 434
always@(posedge sys_clk )
if(!rst_n)
cnt <= 0;
else if ( rx_flag == 1 )begin
if ( cnt == COUNT -1) ///一定要减一,如果不减一,实际会计到435次,反算回去波特率就不是115200了
cnt <= 0;
else
cnt <= cnt +1 ;
end
else
cnt <= 0 ;
/计数器
always@(posedge sys_clk )
if(!rst_n)
cnt_bit_rx <= 0 ;
else if ( rx_flag )begin
if ( cnt == COUNT -1)begin
if(cnt_bit_rx == 9)
cnt_bit_rx <= 0 ;
else
cnt_bit_rx <= cnt_bit_rx +1 ;
end
else
cnt_bit_rx <= cnt_bit_rx ;
end
else
cnt_bit_rx <= 0 ;
[7:0]uart_data
/*
cnt_bit = 0 起始位
cnt_bit = 1-8 数据位
cnt_bit = 9 停止位
但是 [7:0]uart_data 没有uart[8]
所以
if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9 )
uart_data[cnt_bit -1] <= rx_data ;
*/
// always@(posedge sys_clk )
// if(!rst_n)
// uart_data <= 0 ;
// else if ( rx_flag )begin
// if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9 )
// uart_data[cnt_bit -1] <= rx_data ; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
// else
// uart_data <= uart_data ;
// end
// else
// uart_data <= 0 ;
因为这里要让uart_data只在rx_done的时候有值,所以定义一个中间寄存器data_reg
always@(posedge sys_clk )
if(!rst_n)
data_reg <= 0 ;
else if ( rx_flag )begin
if ( cnt == MID -1 && cnt_bit_rx > 0 && cnt_bit_rx < 9 )
data_reg[cnt_bit_rx -1] <= rx_data ; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
else
data_reg <= data_reg ;
end
else
data_reg <= 0 ;
// 其他赋值方法1
// always@(posedge sys_clk )
// if(!rst_n)
// uart_data <= 0;
// else if (cnt == MID -1)begin
// case(cnt_bit)
// 0: uart_data <= 0 ;
// 1: uart_data [0] <= rx_data ;
// 2: uart_data [1] <= rx_data ;
// 3: uart_data [2] <= rx_data ;
// 4: uart_data [3] <= rx_data ;
// 5: uart_data [4] <= rx_data ;
// 6: uart_data [5] <= rx_data ;
// 7: uart_data [6] <= rx_data ;
// 8: uart_data [7] <= rx_data ;
// 9: uart_data <= uart_data ; ///停止位的时候数据传完,保持
// endcase
// end
// else
// uart_data <= uart_data ;
// 其他赋值方法2
// always@(posedge sys_clk )
// if(!rst_n)
// uart_data <= 0 ;
// else if (cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9)
// uart_data <= {rx_data,uart_data[7:1]} ;
// else
// uart_data <= uart_data ;
给uart_data赋值
always@(posedge sys_clk )
if(!rst_n)
uart_data <= 0 ;
else if (rx_flag)begin
if (cnt_bit_rx == 9 && cnt == MID/4 -1)
uart_data <= data_reg ;
else
uart_data <= uart_data ;
end
else
uart_data <= uart_data ; 可以保持到下一个数据到来
uart_data <= 0 ; 只保持在rx_done处于高电平的时候
/rx_done
always@(posedge sys_clk )
if(!rst_n)
rx_done <= 0 ;
else if (rx_flag)begin
if ( cnt_bit_rx == 9 && cnt == MID/2 -1)
rx_done <= 1 ;
else
rx_done <= 0 ;
end
else
rx_done <= 0 ;
endmodule
翻译模块:
`timescale 1ns / 1ps
/*
将ASCII码转换成对应的数字或字母或符号
这种转换模块尽量使用组合逻辑去写,让他尽量不产生延时
*/
module translate(
input sys_clk ,
input rst_n ,
input [7:0] uart_data ,
input rx_done ,
output reg[7:0] trans_data
);
always@(*)
if(!rst_n)
trans_data <= 0 ;
else if (rx_done)
case(uart_data)
8'h30 : trans_data <= 8'd0 ;
8'h31 : trans_data <= 8'd1 ;
8'h32 : trans_data <= 8'd2 ;
8'h33 : trans_data <= 8'd3 ;
8'h41 : trans_data <= "A" ;
8'h40 : trans_data <= "@" ;
endcase
else
trans_data <= trans_data ;
endmodule
动态模块
`timescale 1ns / 1ps
module DT(
input sys_clk ,
input rst_n ,
output reg[63:0] DT_data
);
// wire [7:0] rx_data ,
向串口发送不同数字,点阵屏出现不同图案,状态机实现
reg [3:0] cur_state ;
reg [3:0] next_state ;
reg [26:0] cnt_1s ;
// wire [7:0] rx_data ;
parameter TIME_1s = 26'd50_000_000 ;
localparam IDLE = 3'b000 ;
localparam S0 = 3'b001 ;
localparam S1 = 3'b010 ;
localparam S2 = 3'b100 ;
state 1
always@(posedge sys_clk)
if(!rst_n)
cur_state <= IDLE ;
else
cur_state <= next_state ;
state 2
always@(*)
case(cur_state)
IDLE : next_state <= S0 ;
S0 :
begin
if( cnt_1s == TIME_1s -1 )
next_state <= S1 ;
else
next_state <= cur_state ;
end
S1 :
begin
if( cnt_1s == TIME_1s -1 )
next_state <= S2 ;
else
next_state <= cur_state ;
end
S2 :
begin
if( cnt_1s == TIME_1s -1 )
next_state <= S0 ;
else
next_state <= cur_state ;
end
default:;
endcase
state 3
always@(posedge sys_clk)
if (!rst_n)begin
cnt_1s <= 0 ;
DT_data <= 0 ;
end
else
case(cur_state)
IDLE :
begin
cnt_1s <= 0 ;
DT_data <= 0 ;
end
//0xFF,0xCF,0xEF,0xEF,0xEF,0xEF,0xC7,0xFF,
//0xFF,0xC7,0xF7,0xE7,0xDF,0xDF,0xC7,0xFF,
//0xFF,0xC7,0xF7,0xC7,0xF7,0xF7,0xC7,0xFF,
S0 :begin
DT_data <= 64'hFF_CF_EF_EF_EF_EF_C7_FF ;
if(cnt_1s == TIME_1s -1)
cnt_1s <= 0 ;
else
cnt_1s <= cnt_1s +1;
end
S1 : begin
DT_data <= 64'hFF_C7_F7_E7_DF_DF_C7_FF ;
if(cnt_1s == TIME_1s -1)
cnt_1s <= 0 ;
else
cnt_1s <= cnt_1s +1;
end
S2 : begin
DT_data <= 64'hFF_C7_F7_C7_F7_F7_C7_FF ;
if(cnt_1s == TIME_1s -1)
cnt_1s <= 0 ;
else
cnt_1s <= cnt_1s +1;
end
default:;
endcase
endmodule
控制模块
`timescale 1ns / 1ps
module CTRL(
input sysclk ,
input rst_n ,
input [7:0] trans_data ,
input [63:0] DT_data ,
output reg [63:0] CTRL_data
);
/状态机
localparam IDLE = 3'b001;
localparam JT = 3'b010;
localparam DT = 3'b100;
reg [2:0] cur_state,next_state;
//state1
always@(posedge sysclk)
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
//state2
always@(*)
case(cur_state)
IDLE :begin
if(trans_data == 1 || trans_data == 2 || trans_data == 3)
next_state = JT;
else if(trans_data == "A")
next_state = DT;
else
next_state = cur_state;
end
JT :begin
if(trans_data == "A")
next_state = DT;
else
next_state = cur_state;
end
DT :begin
if(trans_data == 1 || trans_data == 2 || trans_data == 3)
next_state = JT;
else
next_state = cur_state;
end
default:;
endcase
//state3
always@(posedge sysclk)
if(!rst_n)begin
CTRL_data <= 0;
end
else
case(cur_state)
IDLE : CTRL_data <= 0;
JT : begin
case(trans_data)
1: CTRL_data <= 64'hFF_CF_EF_EF_EF_EF_C7_FF ;
2: CTRL_data <= 64'hFF_C7_F7_E7_DF_DF_C7_FF ;
3: CTRL_data <= 64'hFF_C7_F7_C7_F7_F7_C7_FF ;
endcase
end
DT :begin
CTRL_data <= DT_data;
end
endcase
endmodule
ctrl_data的数据优化模块
`timescale 1ns / 1ps
module ctrl_data_translate(
input sys_clk ,
input rst_n ,
input [63:0] CTRL_data ,
output reg[15:0] data_screen
);
行扫描 1ms 数据转换 成16位的输出
parameter TIME_1ms = 50_000 ;
reg [15:0] cnt_1ms ;
reg [7:0] number ;
/计时器
always@(posedge sys_clk)
if(!rst_n)
cnt_1ms <= 0 ;
else if ( cnt_1ms == TIME_1ms -1)
cnt_1ms <= 0 ;
else
cnt_1ms <= cnt_1ms +1 ;
计数器
always@(posedge sys_clk)
if(!rst_n)
number <= 0 ;
else if ( cnt_1ms == TIME_1ms -1 && number == 7 )
number <= 0 ;
else if ( cnt_1ms == TIME_1ms -1)
number <= number +1 ;
else
number <= number ;
data_screen
always@(posedge sys_clk)
if(!rst_n)
data_screen <= 0 ;
else
case(number)
0: data_screen <= {8'b0000_0001 ,CTRL_data[63:56]} ;
1: data_screen <= {8'b0000_0010 ,CTRL_data[55:48]} ;
2: data_screen <= {8'b0000_0100 ,CTRL_data[47:40]} ;
3: data_screen <= {8'b0000_1000 ,CTRL_data[39:32]} ;
4: data_screen <= {8'b0001_0000 ,CTRL_data[31:24]} ;
5: data_screen <= {8'b0010_0000 ,CTRL_data[23:16]} ;
6: data_screen <= {8'b0100_0000 ,CTRL_data[15:8]} ;
7: data_screen <= {8'b1000_0000 ,CTRL_data[7:0]} ;
default:;
endcase
endmodule
HC595模块
`timescale 1ns / 1ps
module HC595(
input sys_clk ,
input rst_n ,
input wire[15:0] data_screen,
output SCK ,
output reg RCK ,
output reg DI
);
wire locked ;
wire en ;
reg [3:0] cnt ;
reg [3:0] cnt_bit ;//16位
assign en = ( locked & rst_n )? 1 : 0 ;
/DI模块 需要赋初值/15位的cnt_bit/
在SCK的上升沿赋值
always@(posedge SCK)
if(!rst_n)
cnt_bit <= 0 ;
else if ( en == 1 )begin
if ( cnt_bit == 4'd15 )
cnt_bit <= 0 ;
else
cnt_bit <= cnt_bit +1 ;
end
else
cnt_bit <= 0 ;
always@(negedge SCK)
if(!rst_n)
DI <= 0 ;
else if ( en == 1 )
DI <= data_screen[ cnt_bit ] ;
else
DI <= 0 ;
///RCK模块,每一个16位数据传输完毕后都会拉高3~5个时钟周期
always@(posedge sys_clk)
if(!rst_n)
cnt <= 0 ;
else if ( en == 1 )begin
if ( cnt_bit == 0 )begin
if ( cnt == 9 )
cnt <= 0 ;
else
cnt <= cnt +1 ;
end
else
cnt <= 0 ;
end
else
cnt <= 0 ;
always@(posedge sys_clk )
if (!rst_n)
RCK <= 0 ;
else if ( en == 1 )begin
if (cnt_bit == 0 && cnt >= 5 && cnt <= 7 )
RCK <= 1 ;
else
RCK <= 0 ;
end
else
RCK <= 0 ;
clk_wiz_0 instance_name
(
// Clock out ports
.clk_5M(SCK), // output clk_5M
// Status and control signals
.resetn(rst_n), // input resetn
.locked(locked), // output locked
// Clock in ports
.sys_clk(sys_clk)); // input sys_clk
endmodule
顶层模块
`timescale 1ns / 1ps
module TOP(
input sys_clk ,
input rst_n ,
input rx_data , //输入串行数据
output SCK ,
output wire RCK ,
output wire DI
);
wire[7:0] uart_data ; // 输出并行数据
wire rx_done ; //数据传输完成结束信号
wire[15:0] data_screen;
wire [7:0] trans_data ;
接收端模块例化
uart_rx uart_rx_u2(
. sys_clk (sys_clk ) ,
. rst_n (rst_n ) ,
. rx_data (rx_data ) , //输入串行数据
. uart_data (uart_data) , // 输出并行数据
. rx_done (rx_done ) //数据传输完成结束信号
);
翻译模块例化
translate translate_u1(
. sys_clk ( sys_clk ) ,
. rst_n ( rst_n ) ,
. uart_data ( uart_data) ,
. rx_done ( rx_done ) ,
. trans_data (trans_data)
);
///动态模块例化
wire[63:0] DT_data ;
DT DT_u1(
. sys_clk (sys_clk) ,
. rst_n (rst_n ) ,
. DT_data (DT_data)
);
CTRL模块例化
wire [63:0] CTRL_data ;
CTRL CTRL_u1(
. sysclk ( sys_clk ) ,
. rst_n ( rst_n ) ,
. trans_data ( trans_data) ,
. DT_data ( DT_data ) ,
. CTRL_data ( CTRL_data )
);
CTRL_data 的翻译模块例化
ctrl_data_translate ctrl_data_translate_u1(
. sys_clk ( sys_clk ) ,
. rst_n ( rst_n ) ,
. CTRL_data ( CTRL_data ) ,
. data_screen ( data_screen)
);
HC595 HC595_u1(
. sys_clk ( sys_clk ) ,
. rst_n ( rst_n ) ,
. data_screen (data_screen) ,
. SCK ( SCK ) ,
. RCK ( RCK ) ,
. DI ( DI )
);
endmodule
四、仿真程序
时间太长了,大家慢慢跑吧。。。
`timescale 1ns / 1ps
module test_bench( );
reg sys_clk ;
reg rst_n ;
reg rx_data ; //输入串行数据
wire SCK ;
wire RCK ;
wire DI ;
parameter SYSCLK = 50_000_000 ;
parameter Baud = 115200 ;
parameter COUNT = SYSCLK/Baud ;//434 传输1比特所需要的时钟周期
parameter MID = COUNT/2 ;
initial
begin
sys_clk = 0 ;
rst_n = 0 ;
#10
rst_n = 1 ;
end
always #1 sys_clk = ~sys_clk ; //2ns
initial
begin
uart_out (8'h31);
uart_out (8'h32);
uart_out (8'h33);
uart_out (8'h41);
uart_out (8'h40);
end
//任务函数
task uart_out ;
input [7:0] DATA ;
begin
rx_data = 1 ;///空闲位初始
#20
rx_data = 0 ;///起始位
///传输1bit的计时次数*1周期时间=总时间
#(COUNT*2) rx_data = DATA[0] ;///数据位第一位
#(COUNT*2) rx_data = DATA[1] ;///数据位第二位
#(COUNT*2) rx_data = DATA[2] ;
#(COUNT*2) rx_data = DATA[3] ;
#(COUNT*2) rx_data = DATA[4] ;
#(COUNT*2) rx_data = DATA[5] ;
#(COUNT*2) rx_data = DATA[6] ;
#(COUNT*2) rx_data = DATA[7] ;
#(COUNT*2) rx_data = 1 ;
#(COUNT*2) ;//停止位也需要时间
#2000_000 ;//2ms数据发送的时间至少要大于点阵屏切换数据的时间(1ms)
end
endtask
TOP TOP_u1(
. sys_clk (sys_clk) ,
. rst_n (rst_n ) ,
. rx_data (rx_data) , //输入串行数据
. SCK (SCK ) ,
. RCK ( RCK ) ,
. DI ( DI )
);
endmodule
五、实验结果:
0621