赞
踩
Quartus II 13.1 (64-bit)、Modelsim SE-64 10.4、正点原子FPGA开发板开拓者V2
红外遥控:红外遥控是一种无线、非接触控制技术。
特点:抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等;由于它不能穿过障碍物去控制被控对象的能力,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。
红外光波长为760nm到1mm,人眼可见波长为400nm到760nm。
红外遥控器传输协议的编码目前广泛使用的是: NEC协议和Philips RC-5协议。主流的调制方式有两种:PPM(Pulse Position Modulation,脉冲位置调制)、PWM(Pulse Width Modulation,脉冲宽度调制)。
当按下红外遥控器上的按键时,遥控器会发出一帧红外信号,由引导码、地址码、地址反码、数据码、数码反码和结束位组成,传输时低位在前。
NEC协议采用PPM调制的形式进行编码,数据的每一位(Bit)脉冲长度为560us,由38KHz的载波脉冲(carrier burst)进行调制。逻辑“1”的载波脉冲+载波脉冲间隔时间为2.25ms;逻辑“0”的载波脉冲+载波脉冲间隔时间为1.12ms。
通过理解这张图我们可以知道数据码是什么,主要是看,数据0和数据1是如何表示的。通过上图可知,数据1是560us的信号,加1.69ms的空闲。数据0是560us的信号,加560us的空闲。由于红外接收头的缘故,接收到信号后输出到 FPGA 的波形刚好与发送的波形相反,最终在波形图上说白了就是,表示1时,是由560us的低电平加1.69ms高电平组合,表示0时,是由560us的低电平和560us的高电平表示,如下图。
当红外遥控器上的按键被一直按下时,红外遥控器只会发送一次完整的信息,其后会每隔110ms发送一次重复码(也叫连发码)。
重复码的数据格式比较简单,同样是由9ms载波脉冲开始,紧接着是2.25ms的空闲信号,随后是560us的载波脉冲。
一体化红外接收头,内部集成了红外监测二极管、自动增益控制(AGC)、带通滤波器(Band Pass)、解调器(Demodulator)等电路。
引导码:表示数据传输的开始。
数据码:分别为地址码,地址反码,数据码,数据反码。传输红外遥控发送的数据,一帧数据共有32bit,4个字节。先发送低位。
停止码:标志着数据传输的完成。(560ms)
重复码:当一直按下某个按键的时候,并不会再次发送数据,而且通过重复码标志按键一直处于按下的状态。如果一直按下的话,约110ms,发送一次重复码。
首先状态机处于空闲状态st_idle,remote_in表示红外信号,当我们检测到remote_in为低电平的时候表示可能收到红外信号,此时由空闲状态转移到st_start_low_ms这一状态,在这一状态我们需要判断低电平持续时间是否为9ms,若为9ms则进入下一状态,判断是完整的数据还是重复码,time_done表示计算结束,judge_flag表示是否为数据或重复码,它是通过计时高电平持续时间判断的,如果为4.5ms就是数据,此时我们进入接收数据状态,当检测到上升沿(pos_remote)data_cnt==32即数据收到了32位,此时完成接收转移到初始状态。
输入的由系统时钟、复位、红外信号,输出的repeat_en重复码标志信号,数据有效信号data_en,及红外控制码。
- module remote_rcv(
- input sys_clk , //系统时钟
- input sys_rst_n , //系统复位信号,低电平有效
-
- input remote_in , //红外接收信号
- output reg repeat_en , //重复码有效信号
- output reg data_en , //数据有效信号
- output reg [7:0] data //红外控制码
- );
一般用读热码表示每一个状态
- //parameter define
- parameter st_idle = 5'b0_0001; //空闲状态
- parameter st_start_low_9ms = 5'b0_0010; //监测同步码低电平
- parameter st_start_judge = 5'b0_0100; //判断重复码和同步码高电平(空闲信号)
- parameter st_rec_data = 5'b0_1000; //接收数据
- parameter st_repeat_code = 5'b1_0000; //重复码
- //reg define
- reg [4:0] cur_state ;
- reg [4:0] next_state ;
-
- reg [11:0] div_cnt ; //分频计数器
- reg div_clk ; //分频时钟
- reg remote_in_d0 ; //对输入的红外信号延时打拍
- reg remote_in_d1 ;
- reg [7:0] time_cnt ; //对红外的各个状态进行计数
-
- reg time_cnt_clr ; //计数器清零信号
- reg time_done ; //计时完成信号
- reg error_en ; //错误信号
- reg judge_flag ; //检测出的标志信号 0:同步码高电平(空闲信号) 1:重复码
- reg [15:0] data_temp ; //暂存收到的控制码和控制反码
- reg [5:0] data_cnt ; //对接收的数据进行计数
-
- //wire define
- wire pos_remote_in ; //输入红外信号的上升沿
- wire neg_remote_in ; //输入红外信号的下降沿
- assign pos_remote_in = (~remote_in_d1) & remote_in_d0;
- assign neg_remote_in = remote_in_d1 & (~remote_in_d0);
-
- //对输入的remote_in信号延时打拍
- always @(posedge div_clk or negedge sys_rst_n) begin
- if(!sys_rst_n) begin
- remote_in_d0 <= 1'b0;
- remote_in_d1 <= 1'b0;
- end
- else begin
- remote_in_d0 <= remote_in;
- remote_in_d1 <= remote_in_d0;
- end
- end
- //时钟分频,50Mhz/(2*(3124+1))=8khz,T=0.125ms
- always @(posedge sys_clk or negedge sys_rst_n ) begin
- if (!sys_rst_n) begin
- div_cnt <= 12'd0;
- div_clk <= 1'b0;
- end
- else if(div_cnt == 12'd3124) begin
- div_cnt <= 12'd0;
- div_clk <= ~div_clk;
- end
- else
- div_cnt <= div_cnt + 12'b1;
- end
通过对分频后的时钟计数判断时间,继而判断各个状态。
- //对红外的各个状态进行计数
- always @(posedge div_clk or negedge sys_rst_n) begin
- if(!sys_rst_n)
- time_cnt <= 8'b0;
- else if(time_cnt_clr)
- time_cnt <= 8'b0;
- else
- time_cnt <= time_cnt + 8'b1;
- end
- always @ (posedge div_clk or negedge sys_rst_n) begin
- if(!sys_rst_n)
- cur_state <= st_idle;
- else
- cur_state <= next_state ;
- end
没什么好说的固定结构 ,如果复位空闲状态给到当前状态,其它情况下一状态给到当前状态。
- always @(*) begin
- next_state = st_idle;
- case(cur_state)
- st_idle : begin //空闲状态
- if(remote_in_d0 == 1'b0)
- next_state = st_start_low_9ms;
- else
- next_state = st_idle;
- end
- st_start_low_9ms : begin //监测同步码低电平
- if(time_done)
- next_state = st_start_judge;
- else if(error_en)
- next_state = st_idle;
- else
- next_state = st_start_low_9ms;
- end
- st_start_judge : begin //判断重复码和同步码高电平(空闲信号)
- if(time_done) begin
- if(judge_flag == 1'b0)
- next_state = st_rec_data;
- else
- next_state = st_repeat_code;
- end
- else if(error_en)
- next_state = st_idle;
- else
- next_state = st_start_judge;
- end
- st_rec_data : begin //接收数据
- if(pos_remote_in && data_cnt == 6'd32)
- next_state = st_idle;
- else
- next_state = st_rec_data;
- end
- st_repeat_code : begin //重复码
- if(pos_remote_in)
- next_state = st_idle;
- else
- next_state = st_repeat_code;
- end
- default : next_state = st_idle;
- endcase
- end
第二段就是根据状态转移图写出各个状态转移的条件,这里当时我不明白的是,当处于st_rec_data即接收数据状态时候,为什么要判断同时判断pos_remote_in和data_cnt,data_cnt==32这表示数据传了32位,没有问题,那上升沿是干什么的呢?后来我通过波形图发现,会有结束吗560ms低电平在拉高,会有上升沿。所以当结束时有两个判断条件,一个是数据计数达到32,一个是上升沿。
- always @(posedge div_clk or negedge sys_rst_n ) begin
- if (!sys_rst_n) begin
- time_cnt_clr <= 1'b0;
- time_done <= 1'b0;
- error_en <= 1'b0;
- judge_flag <= 1'b0;
- data_en <= 1'b0;
- data <= 8'd0;
- repeat_en <= 1'b0;
- data_cnt <= 6'd0;
- data_temp <= 32'd0;
- end
- else begin
- time_cnt_clr <= 1'b0;
- time_done <= 1'b0;
- error_en <= 1'b0;
- repeat_en <= 1'b0;
- data_en <= 1'b0;
- case(cur_state)
- st_idle : begin
- time_cnt_clr <= 1'b1;
- if(remote_in_d0 == 1'b0)
- time_cnt_clr <= 1'b0;
- end
- st_start_low_9ms : begin //9ms/0.125ms = 72
- if(pos_remote_in) begin
- time_cnt_clr <= 1'b1;
- if(time_cnt >= 69 && time_cnt <= 75)
- time_done <= 1'b1;
- else
- error_en <= 1'b1;
- end
- end
- st_start_judge : begin
- if(neg_remote_in) begin
- time_cnt_clr <= 1'b1;
- //重复码高电平2.25ms 2.25/0.125 = 18
- if(time_cnt >= 15 && time_cnt <= 20) begin
- time_done <= 1'b1;
- judge_flag <= 1'b1;
- end
- //同步码高电平4.5ms 4.5/0.125 = 36
- else if(time_cnt >= 33 && time_cnt <= 38) begin
- time_done <= 1'b1;
- judge_flag <= 1'b0;
- end
- else
- error_en <= 1'b1;
- end
- end
- st_rec_data : begin
- if(pos_remote_in) begin
- time_cnt_clr <= 1'b1;
- if(data_cnt == 6'd32) begin
- data_en <= 1'b1;
- data_cnt <= 6'd0;
- data_temp <= 16'd0;
- if(data_temp[7:0] == ~data_temp[15:8]) //校验控制码和控制反码
- data <= data_temp[7:0];
- end
- end
- else if(neg_remote_in) begin
- time_cnt_clr <= 1'b1;
- data_cnt <= data_cnt + 1'b1;
- //解析控制码和控制反码
- if(data_cnt >= 6'd16 && data_cnt <= 6'd31) begin
- if(time_cnt >= 2 && time_cnt <= 6) begin //0.565/0.125 = 4.52
- data_temp <= {1'b0,data_temp[15:1]}; //逻辑“0”
- end
- else if(time_cnt >= 10 && time_cnt <= 15) //1.69/0.125 = 13.52
- data_temp <= {1'b1,data_temp[15:1]}; //逻辑“1”
- end
- end
- end
- st_repeat_code : begin
- if(pos_remote_in) begin
- time_cnt_clr <= 1'b1;
- repeat_en <= 1'b1;
- end
- end
- default : ;
- endcase
- end
- end
- endmodule
最后一段状态机,首先要明白time_cnt是以分频后的时钟计数的,即8KHZ即0.125ms,所以我们到达第二个状态判时候,判断是否为同步码和重复码就是看time_cnt的计数,同步码高电平2.25ms,用2.25ms/0.125ms=18,也就是当计数器数到18就代表重复码,我们为了给他容错只要它的范围在15~20我们都认为为重复码,如果是4.5ms,也就是4.5ms/0.125ms=36,我们认定它范围为33~38,同理最开始的引导码9ms也是这么判断,9ms/0.125ms=72。
当时最让我难以理解的是,处于st_rec_data状态时的代码,这里我们需要知道每当检测到一次上升沿和下降沿,就表示一位数据传输完成,这里从波形图观察一下就可以得出,所以当检测到下降沿data_cnt+1我们就可以理解了,然后就是为什么每当上升沿和下降沿来的时候time_cnt清0,然后在else if中判断time_cnt的计数值。
这里我们要先知道,接收一位数据不管是0还是1,是由一个上升沿和下降沿组成,如传输0,它是560us的低电平和560us高电平,传输1是560us低电平和1.69ms高电平组成,如下图,所以当检测到上升沿对time_cnt清零,只不过是对收到半个0/1的那个前半段的560us清理,对我们判断0/1不影响,因为它们有区别的是后半段的持续时间,后半段0持续560us即560us/0.125ms=4.48,后半段1持续1.69ms/0.125ms=13.52,所以我们只需要判断time_cnt持续时间是否在2~6之内或10~15之内,就能判断它是0还是1。
最后就是那两句赋值语句,data_temp <= {1'b0,data_temp[15:1]}//逻辑0;和data_temp <= {1'b1,data_temp[15:1]}; //逻辑“1”;就是个右移动语句,大家画个图就能明白。图第一行最左边的爱心是第十五位。
- module remote_rcv(
- input sys_clk , //系统时钟
- input sys_rst_n , //系统复位信号,低电平有效
-
- input remote_in , //红外接收信号
- output reg repeat_en , //重复码有效信号
- output reg data_en , //数据有效信号
- output reg [7:0] data //红外控制码
- );
-
- //parameter define
- parameter st_idle = 5'b0_0001; //空闲状态
- parameter st_start_low_9ms = 5'b0_0010; //监测同步码低电平
- parameter st_start_judge = 5'b0_0100; //判断重复码和同步码高电平(空闲信号)
- parameter st_rec_data = 5'b0_1000; //接收数据
- parameter st_repeat_code = 5'b1_0000; //重复码
- //reg define
- reg [4:0] cur_state ;
- reg [4:0] next_state ;
- reg [11:0] div_cnt ; //分频计数器
- reg div_clk ; //分频时钟
- reg remote_in_d0 ; //对输入的红外信号延时打拍
- reg remote_in_d1 ;
- reg [7:0] time_cnt ; //对红外的各个状态进行计数
- reg time_cnt_clr ; //计数器清零信号
- reg time_done ; //计时完成信号
- reg error_en ; //错误信号
- reg judge_flag ; //检测出的标志信号 0:同步码高电平(空闲信号) 1:重复码
- reg [15:0] data_temp ; //暂存收到的控制码和控制反码
- reg [5:0] data_cnt ; //对接收的数据进行计数
- //wire define
- wire pos_remote_in ; //输入红外信号的上升沿
- wire neg_remote_in ; //输入红外信号的下降沿
- //*****************************************************
- //** main code
- //*****************************************************
- assign pos_remote_in = (~remote_in_d1) & remote_in_d0;
- assign neg_remote_in = remote_in_d1 & (~remote_in_d0);
- //时钟分频,50Mhz/(2*(3124+1))=8khz,T=0.125ms
- always @(posedge sys_clk or negedge sys_rst_n ) begin
- if (!sys_rst_n) begin
- div_cnt <= 12'd0;
- div_clk <= 1'b0;
- end
- else if(div_cnt == 12'd3124) begin
- div_cnt <= 12'd0;
- div_clk <= ~div_clk;
- end
- else
- div_cnt <= div_cnt + 12'b1;
- end
-
- //对红外的各个状态进行计数
- always @(posedge div_clk or negedge sys_rst_n) begin
- if(!sys_rst_n)
- time_cnt <= 8'b0;
- else if(time_cnt_clr)
- time_cnt <= 8'b0;
- else
- time_cnt <= time_cnt + 8'b1;
- end
- //对输入的remote_in信号延时打拍
- always @(posedge div_clk or negedge sys_rst_n) begin
- if(!sys_rst_n) begin
- remote_in_d0 <= 1'b0;
- remote_in_d1 <= 1'b0;
- end
- else begin
- remote_in_d0 <= remote_in;
- remote_in_d1 <= remote_in_d0;
- end
- end
- //状态机
- always @ (posedge div_clk or negedge sys_rst_n) begin
- if(!sys_rst_n)
- cur_state <= st_idle;
- else
- cur_state <= next_state ;
- end
- always @(*) begin
- next_state = st_idle;
- case(cur_state)
- st_idle : begin //空闲状态
- if(remote_in_d0 == 1'b0)
- next_state = st_start_low_9ms;
- else
- next_state = st_idle;
- end
- st_start_low_9ms : begin //监测同步码低电平
- if(time_done)
- next_state = st_start_judge;
- else if(error_en)
- next_state = st_idle;
- else
- next_state = st_start_low_9ms;
- end
- st_start_judge : begin //判断重复码和同步码高电平(空闲信号)
- if(time_done) begin
- if(judge_flag == 1'b0)
- next_state = st_rec_data;
- else
- next_state = st_repeat_code;
- end
- else if(error_en)
- next_state = st_idle;
- else
- next_state = st_start_judge;
- end
- st_rec_data : begin //接收数据
- if(pos_remote_in && data_cnt == 6'd32)
- next_state = st_idle;
- else
- next_state = st_rec_data;
- end
- st_repeat_code : begin //重复码
- if(pos_remote_in)
- next_state = st_idle;
- else
- next_state = st_repeat_code;
- end
- default : next_state = st_idle;
- endcase
- end
-
- always @(posedge div_clk or negedge sys_rst_n ) begin
- if (!sys_rst_n) begin
- time_cnt_clr <= 1'b0;
- time_done <= 1'b0;
- error_en <= 1'b0;
- judge_flag <= 1'b0;
- data_en <= 1'b0;
- data <= 8'd0;
- repeat_en <= 1'b0;
- data_cnt <= 6'd0;
- data_temp <= 32'd0;
- end
- else begin
- time_cnt_clr <= 1'b0;
- time_done <= 1'b0;
- error_en <= 1'b0;
- repeat_en <= 1'b0;
- data_en <= 1'b0;
- case(cur_state)
- st_idle : begin
- time_cnt_clr <= 1'b1;
- if(remote_in_d0 == 1'b0)
- time_cnt_clr <= 1'b0;
- end
- st_start_low_9ms : begin //9ms/0.125ms = 72
- if(pos_remote_in) begin
- time_cnt_clr <= 1'b1;
- if(time_cnt >= 69 && time_cnt <= 75)
- time_done <= 1'b1;
- else
- error_en <= 1'b1;
- end
- end
- st_start_judge : begin
- if(neg_remote_in) begin
- time_cnt_clr <= 1'b1;
- //重复码高电平2.25ms 2.25/0.125 = 18
- if(time_cnt >= 15 && time_cnt <= 20) begin
- time_done <= 1'b1;
- judge_flag <= 1'b1;
- end
- //同步码高电平4.5ms 4.5/0.125 = 36
- else if(time_cnt >= 33 && time_cnt <= 38) begin
- time_done <= 1'b1;
- judge_flag <= 1'b0;
- end
- else
- error_en <= 1'b1;
- end
- end
- st_rec_data : begin
- if(pos_remote_in) begin
- time_cnt_clr <= 1'b1;
- if(data_cnt == 6'd32) begin
- data_en <= 1'b1;
- data_cnt <= 6'd0;
- data_temp <= 16'd0;
- if(data_temp[7:0] == ~data_temp[15:8]) //校验控制码和控制反码
- data <= data_temp[7:0];
- end
- end
- else if(neg_remote_in) begin
- time_cnt_clr <= 1'b1;
- data_cnt <= data_cnt + 1'b1;
- //解析控制码和控制反码
- if(data_cnt >= 6'd16 && data_cnt <= 6'd31) begin
- if(time_cnt >= 2 && time_cnt <= 6) begin //0.565/0.125 = 4.52
- data_temp <= {1'b0,data_temp[15:1]}; //逻辑“0”
- end
- else if(time_cnt >= 10 && time_cnt <= 15) //1.69/0.125 = 13.52
- data_temp <= {1'b1,data_temp[15:1]}; //逻辑“1”
- end
- end
- end
- st_repeat_code : begin
- if(pos_remote_in) begin
- time_cnt_clr <= 1'b1;
- repeat_en <= 1'b1;
- end
- end
- default : ;
- endcase
- end
- end
- endmodule
- //数码管显示模块
- seg_led u_seg_led(
- .clk (sys_clk),
- .rst_n (sys_rst_n),
- .seg_sel (sel),
- .seg_led (seg_led),
- .data (data), //红外数据
- .point (6'd0), //无小数点
- .en (1'b1), //使能数码管
- .sign (1'b0) //无符号显示
- );
-
- //HS0038B驱动模块
- remote_rcv u_remote_rcv(
- .sys_clk (sys_clk),
- .sys_rst_n (sys_rst_n),
- .remote_in (remote_in),
- .repeat_en (repeat_en),
- .data_en (),
- .data (data)
- );
-
- led_ctrl u_led_ctrl(
- .sys_clk (sys_clk),
- .sys_rst_n (sys_rst_n),
- .repeat_en (repeat_en),
- .led (led)
- );
-
- endmodule
- module seg_led(
- input clk , // 时钟信号
- input rst_n , // 复位信号
-
- input [19:0] data , // 6位数码管要显示的数值
- input [5:0] point , // 小数点具体显示的位置,从高到低,高电平有效
- input en , // 数码管使能信号
- input sign , // 符号位(高电平显示“-”号)
-
- output reg [5:0] seg_sel, // 数码管位选,最左侧数码管为最高位
- output reg [7:0] seg_led // 数码管段选
- );
-
- //parameter define
- localparam CLK_DIVIDE = 4'd10 ; // 时钟分频系数
- localparam MAX_NUM = 13'd5000 ; // 对数码管驱动时钟(5MHz)计数1ms所需的计数值
-
- //reg define
- reg [ 3:0] clk_cnt ; // 时钟分频计数器
- reg dri_clk ; // 数码管的驱动时钟,5MHz
- reg [23:0] num ; // 24位bcd码寄存器
- reg [12:0] cnt0 ; // 数码管驱动时钟计数器
- reg flag ; // 标志信号(标志着cnt0计数达1ms)
- reg [2:0] cnt_sel ; // 数码管位选计数器
- reg [3:0] num_disp ; // 当前数码管显示的数据
- reg dot_disp ; // 当前数码管显示的小数点
-
- //wire define
- wire [3:0] data0 ; // 个位数
- wire [3:0] data1 ; // 十位数
- wire [3:0] data2 ; // 百位数
- wire [3:0] data3 ; // 千位数
- wire [3:0] data4 ; // 万位数
- wire [3:0] data5 ; // 十万位数
-
- //*****************************************************
- //** main code
- //*****************************************************
-
- //提取显示数值所对应的十进制数的各个位
- assign data0 = data % 4'd10; // 个位数
- assign data1 = data / 4'd10 % 4'd10 ; // 十位数
- assign data2 = data / 7'd100 % 4'd10 ; // 百位数
- assign data3 = data / 10'd1000 % 4'd10 ; // 千位数
- assign data4 = data / 14'd10000 % 4'd10; // 万位数
- assign data5 = data / 17'd100000; // 十万位数
-
- //对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n) begin
- clk_cnt <= 4'd0;
- dri_clk <= 1'b1;
- end
- else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin
- clk_cnt <= 4'd0;
- dri_clk <= ~dri_clk;
- end
- else begin
- clk_cnt <= clk_cnt + 1'b1;
- dri_clk <= dri_clk;
- end
- end
- //将20位2进制数转换为8421bcd码(即使用4位二进制数表示1位十进制数)
- always @ (posedge dri_clk or negedge rst_n) begin
- if (!rst_n)
- num <= 24'b0;
- else begin
- if (data5 || point[5]) begin //如果显示数据为6位十进制数,
- num[23:20] <= data5; //则依次给6位数码管赋值
- num[19:16] <= data4;
- num[15:12] <= data3;
- num[11:8] <= data2;
- num[ 7:4] <= data1;
- num[ 3:0] <= data0;
- end
- else begin
- if (data4 || point[4]) begin //如果显示数据为5位十进制数,则给低5位数码管赋值
- num[19:0] <= {data4,data3,data2,data1,data0};
- if(sign)
- num[23:20] <= 4'd11; //如果需要显示负号,则最高位(第6位)为符号位
- else
- num[23:20] <= 4'd10; //不需要显示负号时,则第6位不显示任何字符
- end
- else begin //如果显示数据为4位十进制数,则给低4位数码管赋值
- if (data3 || point[3]) begin
- num[15: 0] <= {data3,data2,data1,data0};
- num[23:20] <= 4'd10; //第6位不显示任何字符
- if(sign) //如果需要显示负号,则最高位(第5位)为符号位
- num[19:16] <= 4'd11;
- else //不需要显示负号时,则第5位不显示任何字符
- num[19:16] <= 4'd10;
- end
- else begin //如果显示数据为3位十进制数,则给低3位数码管赋值
- if (data2 || point[2]) begin
- num[11: 0] <= {data2,data1,data0};
- //第6、5位不显示任何字符
- num[23:16] <= {2{4'd10}};
- if(sign) //如果需要显示负号,则最高位(第4位)为符号位
- num[15:12] <= 4'd11;
- else //不需要显示负号时,则第4位不显示任何字符
- num[15:12] <= 4'd10;
- end
- else begin //如果显示数据为2位十进制数,则给低2位数码管赋值
- if (data1 || point[1]) begin
- num[ 7: 0] <= {data1,data0};
- //第6、5、4位不显示任何字符
- num[23:12] <= {3{4'd10}};
- if(sign) //如果需要显示负号,则最高位(第3位)为符号位
- num[11:8] <= 4'd11;
- else //不需要显示负号时,则第3位不显示任何字符
- num[11:8] <= 4'd10;
- end
- else begin //如果显示数据为1位十进制数,则给最低位数码管赋值
- num[3:0] <= data0;
- //第6、5位不显示任何字符
- num[23:8] <= {4{4'd10}};
- if(sign) //如果需要显示负号,则最高位(第2位)为符号位
- num[7:4] <= 4'd11;
- else //不需要显示负号时,则第2位不显示任何字符
- num[7:4] <= 4'd10;
- end
- end
- end
- end
- end
- end
- end
-
- //每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号
- always @ (posedge dri_clk or negedge rst_n) begin
- if (rst_n == 1'b0) begin
- cnt0 <= 13'b0;
- flag <= 1'b0;
- end
- else if (cnt0 < MAX_NUM - 1'b1) begin
- cnt0 <= cnt0 + 1'b1;
- flag <= 1'b0;
- end
- else begin
- cnt0 <= 13'b0;
- flag <= 1'b1;
- end
- end
-
- //cnt_sel从0计数到5,用于选择当前处于显示状态的数码管
- always @ (posedge dri_clk or negedge rst_n) begin
- if (rst_n == 1'b0)
- cnt_sel <= 3'b0;
- else if(flag) begin
- if(cnt_sel < 3'd5)
- cnt_sel <= cnt_sel + 1'b1;
- else
- cnt_sel <= 3'b0;
- end
- else
- cnt_sel <= cnt_sel;
- end
- //控制数码管位选信号,使6位数码管轮流显示
- always @ (posedge dri_clk or negedge rst_n) begin
- if(!rst_n) begin
- seg_sel <= 6'b111111; //位选信号低电平有效
- num_disp <= 4'b0;
- dot_disp <= 1'b1; //共阳极数码管,低电平导通
- end
- else begin
- if(en) begin
- case (cnt_sel)
- 3'd0 :begin
- seg_sel <= 6'b111110; //显示数码管最低位
- num_disp <= num[3:0] ; //显示的数据
- dot_disp <= ~point[0]; //显示的小数点
- end
- 3'd1 :begin
- seg_sel <= 6'b111101; //显示数码管第1位
- num_disp <= num[7:4] ;
- dot_disp <= ~point[1];
- end
- 3'd2 :begin
- seg_sel <= 6'b111011; //显示数码管第2位
- num_disp <= num[11:8];
- dot_disp <= ~point[2];
- end
- 3'd3 :begin
- seg_sel <= 6'b110111; //显示数码管第3位
- num_disp <= num[15:12];
- dot_disp <= ~point[3];
- end
- 3'd4 :begin
- seg_sel <= 6'b101111; //显示数码管第4位
- num_disp <= num[19:16];
- dot_disp <= ~point[4];
- end
- 3'd5 :begin
- seg_sel <= 6'b011111; //显示数码管最高位
- num_disp <= num[23:20];
- dot_disp <= ~point[5];
- end
- default :begin
- seg_sel <= 6'b111111;
- num_disp <= 4'b0;
- dot_disp <= 1'b1;
- end
- endcase
- end
- else begin
- seg_sel <= 6'b111111; //使能信号为0时,所有数码管均不显示
- num_disp <= 4'b0;
- dot_disp <= 1'b1;
- end
- end
- end
-
- //控制数码管段选信号,显示字符
- always @ (posedge dri_clk or negedge rst_n) begin
- if (!rst_n)
- seg_led <= 8'hc0;
- else begin
- case (num_disp)
- 4'd0 : seg_led <= {dot_disp,7'b1000000}; //显示数字 0
- 4'd1 : seg_led <= {dot_disp,7'b1111001}; //显示数字 1
- 4'd2 : seg_led <= {dot_disp,7'b0100100}; //显示数字 2
- 4'd3 : seg_led <= {dot_disp,7'b0110000}; //显示数字 3
- 4'd4 : seg_led <= {dot_disp,7'b0011001}; //显示数字 4
- 4'd5 : seg_led <= {dot_disp,7'b0010010}; //显示数字 5
- 4'd6 : seg_led <= {dot_disp,7'b0000010}; //显示数字 6
- 4'd7 : seg_led <= {dot_disp,7'b1111000}; //显示数字 7
- 4'd8 : seg_led <= {dot_disp,7'b0000000}; //显示数字 8
- 4'd9 : seg_led <= {dot_disp,7'b0010000}; //显示数字 9
- 4'd10: seg_led <= 8'b11111111; //不显示任何字符
- 4'd11: seg_led <= 8'b10111111; //显示负号(-)
- default:
- seg_led <= {dot_disp,7'b1000000};
- endcase
- end
- end
-
- endmodule
- module led_ctrl(
- input sys_clk , //系统时钟
- input sys_rst_n , //系统复位信号,低电平有效
-
- input repeat_en , //重复码触发信号
- output reg led //LED灯
- );
-
- //reg define
- reg repeat_en_d0 ; //repeat_en信号打拍采沿
- reg repeat_en_d1 ;
- reg [22:0] led_cnt ; //LED灯计数器,用于控制LED灯亮灭
-
- //wire define
- wire pos_repeat_en;
-
- //*****************************************************
- //** main code
- //*****************************************************
-
- assign pos_repeat_en = ~repeat_en_d1 & repeat_en_d0;
-
- repeat_en信号打拍采沿
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(!sys_rst_n) begin
- repeat_en_d0 <= 1'b0;
- repeat_en_d1 <= 1'b0;
- end
- else begin
- repeat_en_d0 <= repeat_en;
- repeat_en_d1 <= repeat_en_d0;
- end
- end
-
- always @(posedge sys_clk or negedge sys_rst_n) begin
- if(!sys_rst_n) begin
- led_cnt <= 23'd0;
- led <= 1'b0;
- end
- else begin
- if(pos_repeat_en) begin
- led_cnt <= 23'd5_000_000; //单次重复码:亮80ms 灭20ms
- led <= 1'b1; //led亮的时间:4_000_000*20ns=80ms
- end
- else if(led_cnt != 23'd0) begin
- led_cnt <= led_cnt - 23'd1;
- if(led_cnt < 23'd1_000_000) //led灭的时间:1_000_000*20ns=20ms
- led <= 1'b0;
- end
- end
- end
-
- endmodule
TB仿真文件
- `timescale 1ns/1ns
-
- module remote_rcv_tb();
-
- parameter T = 20;
- parameter ADDR_CODE = 8'h5a; // 8'b01011010
- parameter DATA_CODE = 8'h45;
- reg sys_clk;
- reg sys_rst_n;
- reg remote_in;
- reg [7:0] data;
- wire repeat_en;
- wire data_en;
- wire [7:0] remote_data;
- initial begin
- sys_clk = 1'b0;
- sys_rst_n = 1'b0;
- #(T+1)
- sys_rst_n = 1'b1;
-
- end
-
- always #(T/2) sys_clk = ~sys_clk;
-
- initial begin
- remote_in = 1'b1;
- data = 8'd0;
-
- #100_000 remote_in = 1'b0;
- #9_000_000 remote_in = 1'b1;
- #4_500_000 remote_in = 1'b0;
-
- data = ADDR_CODE;
- repeat(8) begin
- #560_000 remote_in = 1'b1;
- if(data[0])
- #1_690_000 remote_in = 1'b0;
- else
- #560_000 remote_in = 1'b0;
- data = data >>1;
- end
-
- data = ~ADDR_CODE;
- repeat(8) begin
- #560_000 remote_in = 1'b1;
- if(data[0])
- #1_690_000 remote_in = 1'b0;
- else
- #560_000 remote_in = 1'b0;
- data = data >>1;
- end
- data = DATA_CODE;
- repeat(8) begin
- #560_000 remote_in = 1'b1;
- if(data[0])
- #1_690_000 remote_in = 1'b0;
- else
- #560_000 remote_in = 1'b0;
- data = data >>1;
- end
-
- data = ~DATA_CODE;
- repeat(8) begin
- #560_000 remote_in = 1'b1;
- if(data[0])
- #1_690_000 remote_in = 1'b0;
- else
- #560_000 remote_in = 1'b0;
- data = data >>1;
- end
- #560_000 remote_in = 1'b1;
- // 重复码
- #50_000_000 remote_in = 1'b0;
- #9_000_000 remote_in = 1'b1;
- #2_250_000 remote_in = 1'b0;
- #560_000 remote_in = 1'b1;
- end
-
- remote_rcv u_remote_rcv(
- .sys_clk (sys_clk),
- .sys_rst_n (sys_rst_n),
-
- .remote_in (remote_in),
- .repeat_en (repeat_en),
- .data_en (data_en),
- .data (remote_data)
-
- );
- endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。