使用 FPGA 开发板配套的红外遥控器发送红外信号, FPGA 开发板上的一
体化接收头接收到红外信号后传入 FPGA 芯片内, FPGA 芯片接收到信号后进行解码,将解码后的按键码显示在数码管上。若检测到发送了重复码,则让 led 闪烁显示,一个重复码闪烁一次。web
来看红外接收模块:
tcp
`timescale 1ns/1ns module infrared_rcv ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低有效 input wire infrared_in , //红外接受信号 output reg repeat_en , //重复码使能信号 output reg [19:0] data //接收的控制码 ); //parameter define parameter CNT_0_56MS_L = 20000 , //0.56ms计数为0-27999 CNT_0_56MS_H = 35000 , CNT_1_69MS_L = 80000 , //1.69ms计数为0-84499 CNT_1_69MS_H = 90000 , CNT_2_25MS_L = 100000, //2.25ms计数为0-112499 CNT_2_25MS_H = 125000, CNT_4_5MS_L = 175000, //4.5ms计数为0-224999 CNT_4_5MS_H = 275000, CNT_9MS_L = 400000, //9ms计数为0-449999 CNT_9MS_H = 490000; //state parameter IDLE = 5'b0_0001, //空闲状态 S_T9 = 5'b0_0010, //监测同步码低电平 S_JUDGE = 5'b0_0100, //判断重复码和同步码高电平 S_IFR_DATA = 5'b0_1000, //接收数据 S_REPEAT = 5'b1_0000; //重复码 //wire define wire ifr_in_rise ; //检测红外信号的上升沿 wire ifr_in_fall ; //检测红外信号的降低沿 //reg define reg infrared_in_d1 ; //对infrared_in信号打一拍 reg infrared_in_d2 ; //对infrared_in信号打两拍 reg [18:0] cnt ; //计数器 reg flag_0_56ms ; //0.56ms计数完成标志信号 reg flag_1_69ms ; //1.69ms计数完成标志信号 reg flag_2_25ms ; //2.25ms计数完成标志信号 reg flag_4_5ms ; //4.5ms计数完成标志信号 reg flag_9ms ; //0.56ms计数完成标志信号 reg [4:0] state ; //状态机状态 reg [5:0] data_cnt ; //数据计数器 reg [31:0] data_tmp ; //数据寄存器 //检测红外信号的上升沿和降低沿 assign ifr_in_rise = (~infrared_in_d2) & (infrared_in_d1) ; assign ifr_in_fall = (infrared_in_d2) & (~infrared_in_d1) ; //对infrared_in信号打拍 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin infrared_in_d1 <= 1'b0; infrared_in_d2 <= 1'b0; end else begin infrared_in_d1 <= infrared_in; infrared_in_d2 <= infrared_in_d1; end //cnt always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt <= 19'd0; else case(state) IDLE: cnt <= 19'd0; S_T9: if((ifr_in_rise==1'b1) && (flag_9ms==1'b1)) cnt <= 19'd0; else cnt <= cnt + 1; S_JUDGE:if((ifr_in_fall==1'b1) && (flag_2_25ms==1'b1 || flag_4_5ms==1'b1)) cnt <= 19'd0; else cnt <= cnt + 1; S_IFR_DATA: if((flag_0_56ms == 1'b1) && (ifr_in_rise==1'b1)) cnt <= 19'd0; else if(((flag_0_56ms==1'b1) || (flag_1_69ms==1'b1)) && (ifr_in_fall==1'b1)) cnt <= 19'd0; else cnt <= cnt + 1; default:cnt <= 19'd0; endcase //flag_0_56ms:计数到0.56ms范围拉高标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) flag_0_56ms <= 1'b0; else if((state == S_IFR_DATA) && (cnt >= CNT_0_56MS_L) && (cnt <= CNT_0_56MS_H)) flag_0_56ms <= 1'b1; else flag_0_56ms <= 1'b0; //flag_1_69ms:计数到1.69ms范围拉高标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) flag_1_69ms <= 1'b0; else if((state == S_IFR_DATA) && (cnt >= CNT_1_69MS_L) && (cnt <= CNT_1_69MS_H)) flag_1_69ms <= 1'b1; else flag_1_69ms <= 1'b0; //flag_2_25ms:计数到2.25ms范围拉高标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) flag_2_25ms <= 1'b0; else if((state == S_JUDGE) && (cnt >= CNT_2_25MS_L) && (cnt <= CNT_2_25MS_H)) flag_2_25ms <= 1'b1; else flag_2_25ms <= 1'b0; //flag_4_5ms:计数到4.5ms范围拉高标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) flag_4_5ms <= 1'b0; else if((state == S_JUDGE) && (cnt >= CNT_4_5MS_L) && (cnt <= CNT_4_5MS_H)) flag_4_5ms <= 1'b1; else flag_4_5ms <= 1'b0; //flag_9ms:计数到9ms范围拉高标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) flag_9ms <= 1'b0; else if((state == S_T9) && (cnt >= CNT_9MS_L) && (cnt <= CNT_9MS_H)) flag_9ms <= 1'b1; else flag_9ms <= 1'b0; //状态机:状态跳转 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= IDLE; else case(state) //若检测到红外信号降低沿到来跳转到S_T9状态 IDLE: if(ifr_in_fall == 1'b1) state <= S_T9; else //若没检测到红外信号的降低沿,则让其保持在IDLE状态 state <= IDLE; S_T9: //若检测到红外信号上升沿到来,则判断flag_9ms是否为1 //若检测到时间接近9ms,则跳转到S_judje状态 if((ifr_in_rise == 1'b1) && (flag_9ms == 1'b1)) state <= S_JUDGE; else if((ifr_in_rise == 1'b1) && (flag_9ms == 1'b0)) state <= IDLE; else state <= S_T9; S_JUDGE: //若检测到红外信号降低沿到来,则判断flag_2_25ms是否为1 //若检测到时间接近2.25ms,则跳转重复码状态 if((ifr_in_fall == 1'b1) && (flag_2_25ms == 1'b1)) state <= S_REPEAT; else if((ifr_in_fall == 1'b1) && (flag_4_5ms == 1'b1)) state <= S_IFR_DATA; else if((ifr_in_fall == 1'b1) && (flag_2_25ms == 1'b0) && (flag_4_5ms == 1'b0)) state <= IDLE; else state <= S_JUDGE; S_IFR_DATA: //若上升沿到来,低电平保持时间不知足编码协议,则回到空闲状态 if(ifr_in_rise == 1'b1 && flag_0_56ms == 1'b0) state <= IDLE; //若降低沿到来,高电平保持时间不知足编码0或1,则回到空闲状态 else if(ifr_in_fall == 1'b1 && (flag_0_56ms == 1'b0 && flag_1_69ms == 1'b0)) state <= IDLE; //数据接收完毕以后回到空闲状态,等待下一个指令的到来 else if(ifr_in_rise == 1'b1 && data_cnt == 6'd32) state <= IDLE; S_REPEAT: /*若上升沿到来,不管时间是否到了0.56ms, 状态机都跳回IDLE状态等待下一数据码或重复码的到来*/ if(ifr_in_rise == 1'b1) state <= IDLE; else state <= S_REPEAT; default: state <= IDLE; endcase //data_tmp always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_tmp <= 32'b0; else if(state == S_IFR_DATA && ifr_in_fall == 1'b1 && flag_0_56ms == 1'b1) data_tmp[data_cnt] <= 1'b0; else if(state == S_IFR_DATA && ifr_in_fall == 1'b1 && flag_1_69ms == 1'b1) data_tmp[data_cnt] <= 1'b1; else data_tmp <= data_tmp; //data_cnt always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_cnt <= 1'b0; else if(ifr_in_rise == 1'b1 && data_cnt == 6'd32) data_cnt <= 1'b0; else if(ifr_in_fall == 1'b1 && state == S_IFR_DATA) data_cnt <= data_cnt + 1'b1; else data_cnt <= data_cnt; //repeat_en always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) repeat_en <= 1'b0; else if(state == S_REPEAT && (data_tmp[23:16] == ~data_tmp[31:24])) repeat_en <= 1'b1; else repeat_en <= 1'b0; always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data <= 20'b0; //数据接收完以后若数据校验正确,则输出数据码的数据 else if(data_tmp[23:16] == ~data_tmp[31:24] && data_tmp[7:0] == ~data_tmp [15:8] && data_cnt==6'd32) data <= {12'b0,data_tmp[23:16]}; endmodule
接着是LED控制模块
svg
`timescale 1ns/1ns module led_ctrl ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低有效 input wire repeat_en , //重复码使能信号 output reg led //输出led灯信号 ); //parameter define parameter CNT_MAX = 2500_000; //wire define wire repeat_en_rise ; //重复码使能信号上升沿 //reg define reg repeat_en_d1; //重复码使能信号打一拍 reg repeat_en_d2; //重复码使能信号打两拍 reg cnt_en ; //计数器使能信号 reg [21:0] cnt ; //计数器 //得到repeat_en上升沿信号 assign repeat_en_rise = repeat_en_d1 & ~repeat_en_d2; //对reeat_en打两拍 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin repeat_en_d1 <= 1'b0; repeat_en_d2 <= 1'b0; end else begin repeat_en_d1 <= repeat_en; repeat_en_d2 <= repeat_en_d1; end //当重复码使能信号上升沿来到,拉高计数器使能信号,计到50ms后拉低 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_en <= 1'b0; else if(cnt == CNT_MAX - 1) cnt_en <= 1'b0; else if(repeat_en_rise == 1'b1) cnt_en <= 1'b1; //当计数器使能信号为高时让计数器开始计数,为低时计数器清零 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt <= 22'b0; else if(cnt_en == 1'b1) cnt <= cnt + 1; else cnt <= 22'b0; //当计数器大于0时,点亮led灯,也就是当使能信号到来,led灯会亮50ms always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) led <= 1'b1; else if(cnt > 0) led <= 1'b0; else led <= 1'b1; endmodule
最后获得RTL视图。
而后进行仿真验证。编码
`timescale 1ns/1ns module tb_top_infrared_rcv(); //wire define wire led ; wire stcp ; wire shcp ; wire ds ; wire oe ; //reg define reg sys_clk ; reg sys_rst_n ; reg infrared_in ; //对sys_clk,sys_rst_n,infrared_in赋值 initial begin sys_clk = 1'b1; sys_rst_n <= 1'b0; infrared_in <= 1'b1; #100 sys_rst_n <= 1'b1; //引导码 #1000 infrared_in <= 1'b0; #9000000 infrared_in <= 1'b1; #4500000 //地址码(发送地址码8’h99) //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //地址反码(地址反码为8’h66) //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据码(发送数据码8’h22) //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据反码(数据反码为8’hdd) //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据0 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #560000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //数据1 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #1690000 //重复码 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; #42000000 infrared_in <= 1'b0; #9000000 infrared_in <= 1'b1; #2250000 infrared_in <= 1'b0; #560000 infrared_in <= 1'b1; end //clk:产生时钟 always #10 sys_clk <= ~sys_clk; top_infrared_rcv top_infrared_rcv_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .infrared_in (infrared_in), //红外接收信号 .stcp (stcp ), //输出数据存储寄时钟 .shcp (shcp ), //移位寄存器的时钟输入 .ds (ds ), //串行数据输入 .oe (oe ), //输出使能信号 .led (led ) //led灯控制信号 ); endmodule