文章目录
- 一、红外遥控简介
- 1.红外遥控系统
- 2.硬件介绍
- 3.红外遥控编码协议
- 4.重复码
- 5.FPGA接收
- 二、程序设计
- 1.实现功能
- 2.红外接收模块
- 3.led灯控制模块
- 4.数码管控制模块
- ①二进制转8421BCD模块
- ②数码管控制模块
- 5.顶层代码
- 6.RTL视图
- 三、仿真测试模块
- 1.仿真测试模块代码
- 2.仿真波形图
- 四、可能遇到的问题
一、红外遥控简介
1.红外遥控系统
红外遥控系统由红外发射部分和红外接收部分组成。发射部分由遥控案件、编码以及调制电路、红外发光二极管等组成。接收部分由光敏二极管、解调电路等组成,最后将解调的信号输入FPGA内进行解码输出。
2.硬件介绍
使用的红外接收头要购买一体化红外接收头
使用的红外遥控器
红外遥控解码图为
3.红外遥控编码协议
红外遥控的编码协议种类繁多,如:NEC、Philips RC-5、Philips RC-6、Sony SIRC等,而使用最多的是 NEC 协议,我们使用的便是NEC协议。
NEC协议使用的是PPM(脉冲位置调制)进行编码,当按下遥控器的一个按键时,会发送一帧的数据,一帧数据由引导码、地址码、地址反码、数据码、数据反码以及一位结束位组成。
引导码:9ms高电平脉冲加4.5ms低电平。
结束位:562.5us脉冲表示消息传输的结束。
地址码、地址反码、数据码、数据反码:均为八位数据,每一对加起来都有八个0和八个1。
4.重复码
如果一直按按键,当发送完数据后,每隔110ms会发送一个重复码,重复码由9ms的高电平和2.25ms的低电平以及560us(结束为)组成。
5.FPGA接收
FPGA接收到的数据波形与发送的波形相反,发送高电平,就接收低电平,发送低电平,就接收高电平。
二、程序设计
1.实现功能
使用红外遥控器发送红外编码,由FPGA连接一体化红外接收头接收数据,将接收到的编码显示在数码管上。如果长按,板载led灯开始闪烁
2.红外接收模块
小提示:使用状态机进行状态跳转
RTL代码:
module infrared_rcv(input wire sys_clk,input wire sys_rst_n,input wire infrared_in,output reg [19:0]data,output reg repeat_en
);parameter CNT_0_56MS_L=20000,CNT_0_56MS_H=35000,CNT_1_69MS_L=80000,CNT_1_69MS_H=90000,CNT_2_25MS_L=100000,CNT_2_25MS_H=125000,CNT_4_5MS_L=175000,CNT_4_5MS_H=275000,CNT_9MS_L=400000,CNT_9MS_H=490000;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 ifr_in_rise;//检测红外信号上升沿wire ifr_in_fall;reg infrared_in_d1;reg infrared_in_d2;reg [18:0]cnt;reg flag_0_56ms;reg flag_1_69ms;reg flag_2_25ms;reg flag_4_5ms;reg flag_9ms;reg [4:0]state;reg [5:0]data_cnt;reg [31:0]data_tmp;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)infrared_in_d1<=1'b0;elseinfrared_in_d1<=infrared_in;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)infrared_in_d2<=1'b0;elseinfrared_in_d2<=infrared_in_d1;//红外信号上升和下降沿assign ifr_in_rise=infrared_in_d1 && ~infrared_in_d2;assign ifr_in_fall=infrared_in_d2 && ~infrared_in_d1;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)cnt<=1'b0;else case(state)IDLE:cnt<=12'b0;S_T9:if(ifr_in_rise && flag_9ms)cnt<=1'b0;elsecnt<=cnt+1'b1;S_JUDGE:if(ifr_in_fall && (flag_4_5ms || flag_2_25ms))cnt<=1'b0;elsecnt<=cnt+1'b1;S_IFR_DATA:if(flag_0_56ms && ifr_in_rise)cnt<=1'b0;else if((flag_0_56ms || flag_1_69ms) && ifr_in_fall)cnt<=1'b0;elsecnt<=cnt+1'b1;default:cnt<=1'b0;endcase//计数到0.56ms范围拉高标志信号always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)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;elseflag_0_56ms<=1'b0;//计数到1.69ms范围拉高标志信号always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)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;elseflag_1_69ms<=1'b0;//计数2.25msalways @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)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;elseflag_2_25ms<=1'b0;//计数4.5msalways @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)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;elseflag_4_5ms<=1'b0;//计数9msalways @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)flag_9ms<=1'b0;else if(state==S_T9 && cnt>=CNT_9MS_L && cnt<=CNT_9MS_H)flag_9ms<=1'b1;elseflag_9ms<=1'b0;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)state<=IDLE;else case(state)IDLE:if(ifr_in_fall)state<=S_T9;elsestate<=IDLE;S_T9:if(ifr_in_rise && flag_9ms)state<=S_JUDGE;else if(ifr_in_rise && !flag_9ms)state<=IDLE;else state<=S_T9;S_JUDGE:if(ifr_in_fall && flag_2_25ms)state<=S_REPEAT;else if(ifr_in_fall && flag_4_5ms)state<=S_IFR_DATA;else if(ifr_in_fall && !flag_2_25ms && !flag_4_5ms)state<=IDLE;elsestate<=S_JUDGE;S_IFR_DATA:if(ifr_in_rise && !flag_0_56ms)state<=IDLE;else if(ifr_in_fall && !flag_0_56ms && !flag_1_69ms)state<=IDLE;else if(ifr_in_rise && data_cnt==6'd32)state<=IDLE;S_REPEAT:if(ifr_in_rise)state<=IDLE;elsestate<=S_REPEAT;default:state<=IDLE;endcasealways @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)data_tmp<=1'b0;else if(state==S_IFR_DATA && ifr_in_fall && flag_0_56ms)data_tmp[data_cnt]<=1'b0;else if(state==S_IFR_DATA && ifr_in_fall && flag_1_69ms)data_tmp[data_cnt]<=1'b1;elsedata_tmp<=data_tmp;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)data_cnt<=1'b0;else if(ifr_in_rise && data_cnt==6'd32)data_cnt<=1'b0;else if(ifr_in_fall && state==S_IFR_DATA)data_cnt<=data_cnt+1'b1;elsedata_cnt<=data_cnt;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)repeat_en<=1'b0;else if(state==S_REPEAT && data_tmp[23:16]==~data_tmp[31:24])repeat_en<=1'b1;elserepeat_en<=1'b0;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)data<=1'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
3.led灯控制模块
module led_ctrl(input wire repeat_en,input wire sys_clk,input wire sys_rst_n,output reg led
);parameter CNT_MAX=2500_000;reg repeat_en_d1;reg repeat_en_d2;reg cnt_en;reg [21:0]cnt;wire repeat_en_rise;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)beginrepeat_en_d1<=1'b0;repeat_en_d2<=1'b0;endelse beginrepeat_en_d1<=repeat_en;repeat_en_d2<=repeat_en_d1;endassign repeat_en_rise=repeat_en_d1 && ~repeat_en_d2;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)cnt<=1'b0;else if(cnt_en)cnt<=cnt+1'b1;else cnt<=1'b0;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)cnt_en<=1'b0;else if(repeat_en_rise)cnt_en<=1'b1;else if(cnt==CNT_MAX-1)cnt_en<=1'b0;always @(posedge sys_clk or negedge sys_rst_n)if(!sys_rst_n)led<=1'b0;else if(cnt>0)led<=1'b1;else led<=1'b0;endmodule
4.数码管控制模块
①二进制转8421BCD模块
module bcd_8421(input wire clk,input wire rst_n,input wire [19:0]data,output reg [3:0]unit,output reg [3:0]ten,output reg [3:0]hun,output reg [3:0]tho,output reg [3:0]t_tho,output reg [3:0]h_hun);reg [4:0]cnt_shift;reg [43:0]data_shift;reg shift_flag;always @(posedge clk or negedge rst_n)if(!rst_n)cnt_shift<=1'b0;else if(cnt_shift==5'd21 && shift_flag)cnt_shift<=1'b0;else if(shift_flag)cnt_shift<=cnt_shift+1'b1;elsecnt_shift<=cnt_shift;always @(posedge clk or negedge rst_n)if(!rst_n)data_shift<=1'b0;else if(!cnt_shift)data_shift<={24'd0,data};else if(cnt_shift<=20 && (!shift_flag))begindata_shift[23:20]<=(data_shift[23:20]>4)? (data_shift[23:20]+2'd3):(data_shift[23:20]);data_shift[27:24]<=(data_shift[27:24]>4)? (data_shift[27:24]+2'd3):(data_shift[27:24]);data_shift[31:28]<=(data_shift[31:28]>4)? (data_shift[31:28]+2'd3):(data_shift[31:28]);data_shift[35:32]<=(data_shift[35:32]>4)? (data_shift[35:32]+2'd3):(data_shift[35:32]);data_shift[39:36]<=(data_shift[39:36]>4)? (data_shift[39:36]+2'd3):(data_shift[39:36]);data_shift[43:40]<=(data_shift[43:40]>4)? (data_shift[43:40]+2'd3):(data_shift[43:40]);endelse if(cnt_shift<=5'd20 && shift_flag)data_shift<=data_shift<<1;elsedata_shift<=data_shift;always @(posedge clk or negedge rst_n)if(!rst_n)shift_flag<=1'b0;elseshift_flag<=~shift_flag;always @(posedge clk or negedge rst_n)if(!rst_n)beginunit<=1'b0;ten<=1'b0;hun<=1'b0;tho<=1'b0;t_tho<=1'b0;h_hun<=1'b0;endelse if(cnt_shift==5'd21)beginunit<=data_shift[23:20];ten<=data_shift[27:24];hun<=data_shift[31:28];tho<=data_shift[35:32];t_tho<=data_shift[39:36];h_hun<=data_shift[43:40];endendmodule
②数码管控制模块
module seg
#(parameter CNT_MAX=16'd49999 //49999
)
(input wire clk,input wire rst_n,input wire [5:0]point,input wire [19:0]data,input wire seg_en,input wire sign,output reg [5:0]sel,output reg [7:0]seg);wire [3:0]unit;wire [3:0]ten;wire [3:0]hun;wire [3:0]tho;wire [3:0]t_tho;wire [3:0]h_hun;bcd_8421 bcd_8421(.clk(clk),.rst_n(rst_n),.data(data),.unit(unit),.ten(ten),.hun(hun),.tho(tho),.t_tho(t_tho),.h_hun(h_hun));reg [23:0]data_reg;reg [15:0]cnt_1ms;reg flag_1ms;reg [2:0]cnt_sel;reg [5:0]sel_reg;reg [3:0]data_disp;reg dot_disp;//控制数码管显示always @(posedge clk or negedge rst_n)if(!rst_n)data_reg<=1'b0;//若显示的十万位为非零数据或需要显示小数点,六个数码管全显示else if(h_hun || point[5])data_reg<={h_hun,t_tho,tho,hun,ten,unit};//若显示的万位数为非零数据或需要显示小数点,数值显示在5个数码管上else if((t_tho || point[4]) && sign)//显示负号data_reg<={4'd10,t_tho,tho,hun,ten,unit};//定义4'd10为显示负号else if((t_tho || point[4]) && !sign)data_reg<={4'd11,t_tho,tho,hun,ten,unit};//定义4‘d11为不显示//若显示的千位数为非零数据或需要显示小数点,数值显示在4个数码管上else if((tho || point[3]) && sign)data_reg<={4'd11,4'd10,tho,hun,ten,unit};else if((tho || point[3]) && !sign)data_reg<={4'd11,4'd11,tho,hun,ten,unit};//若显示的百位数为非零数据或需要显示小数点,数值显示在3个数码管上else if((hun || point[2]) && sign)data_reg<={4'd11,4'd11,4'd10,hun,ten,unit};else if((hun || point[2]) && !sign)data_reg<={4'd11,4'd11,4'd11,hun,ten,unit};//若显示的十位数为非零数据或需要显示小数点,数值显示在2个数码管上else if((ten || point[2]) && sign)data_reg<={4'd11,4'd11,4'd11,4'd10,ten,unit};else if((ten || point[2]) && !sign)data_reg<={4'd11,4'd11,4'd11,4'd11,ten,unit};//若显示的个位数为非零数据或需要显示小数点,数值显示在1个数码管上else if((unit || point[1]) && sign)data_reg<={4'd11,4'd11,4'd11,4'd11,4'd10,unit};else data_reg<={4'd11,4'd11,4'd11,4'd11,4'd11,unit};//计数器计数1msalways @(posedge clk or negedge rst_n)if(!rst_n)cnt_1ms<=1'b0;else if(cnt_1ms==CNT_MAX)cnt_1ms<=1'b0;else cnt_1ms<=cnt_1ms+1'b1;//计数标志位always @(posedge clk or negedge rst_n)if(!rst_n)flag_1ms<=1'b0;else if(cnt_1ms==CNT_MAX-1'b1)flag_1ms<=1'b1;elseflag_1ms<=1'b0;//cnt_sel:从0到5的循环,用于选择当前显示的数码管always @(posedge clk or negedge rst_n)if(!rst_n)cnt_sel<=1'b0;else if(cnt_sel==3'b101 && flag_1ms)cnt_sel<=1'b0;else if(flag_1ms)cnt_sel<=cnt_sel+1'b1;elsecnt_sel<=cnt_sel;//数码管位选信号寄存器always @(posedge clk or negedge rst_n)if(!rst_n)sel_reg<=6'b000_000;else if(!cnt_sel && flag_1ms)sel_reg<=6'b000_001;else if(flag_1ms)sel_reg<=sel_reg<<1;elsesel_reg<=sel_reg;//控制数码管的位选信号,使六个数码管轮流显示always @(posedge clk or negedge rst_n)if(!rst_n)data_disp<=1'b0;else if(seg_en && flag_1ms)case(cnt_sel)3'd0:data_disp<=data_reg[3:0];3'd1:data_disp<=data_reg[7:4];3'd2:data_disp<=data_reg[11:8];3'd3:data_disp<=data_reg[15:12];3'd4:data_disp<=data_reg[19:16];3'd5:data_disp<=data_reg[23:20];default:data_disp<=1'b0;endcaseelsedata_disp<=data_disp;//dot_disp:小数点低电平点亮,需对小数点有效信号取反always @(posedge clk or negedge rst_n)if(!rst_n)dot_disp<=1'b1;else if(flag_1ms)dot_disp<=~point[cnt_sel];elsedot_disp<=dot_disp;//控制数码管段选信号,显示数字always @(posedge clk or negedge rst_n)if(!rst_n)seg<=8'b1111_1111;elsecase(data_disp)4'd0:seg<={dot_disp,7'b100_0000};4'd1:seg<={dot_disp,7'b111_1001};4'd2:seg<={dot_disp,7'b010_0100};4'd3:seg<={dot_disp,7'b011_0000};4'd4:seg<={dot_disp,7'b001_1001};4'd5:seg<={dot_disp,7'b001_0010};4'd6:seg<={dot_disp,7'b000_0010};4'd7:seg<={dot_disp,7'b111_1000};4'd8:seg<={dot_disp,7'b000_0000};4'd9:seg<={dot_disp,7'b001_0000};4'd10:seg<=8'b1011_1111;4'd11:seg<=8'b1111_1111;default:seg<=8'b1100_0000;endcase//sel:数码管位选信号赋值always @(posedge clk or negedge rst_n)if(!rst_n)sel<=6'b000_000;elsesel<=~sel_reg;endmodule
5.顶层代码
module infrared_rcv_top(input wire sys_clk,input wire sys_rst_n,input wire infrared_in,output wire led,output wire [5:0]sel,output wire [7:0]seg
);wire [19:0]data;wire repeat_en;infrared_rcv infrared_rcv_inst(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.infrared_in(infrared_in),.data(data),.repeat_en(repeat_en));led_ctrl led_ctrl_inst(.repeat_en(repeat_en),.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.led(led));seg#(.CNT_MAX(16'd49999) //49999)seg_inst(.clk(sys_clk),.rst_n(sys_rst_n),.point(6'd0),.data(data),.seg_en(1'b1),.sign(1'b0),.sel(sel),.seg(seg));endmodule
6.RTL视图
三、仿真测试模块
1.仿真测试模块代码
`timescale 1ns/1ns
`define clk_period 20module infrared_rcv_top_tb;reg sys_clk;reg sys_rst_n;reg infrared_in;wire led;wire [5:0]sel;wire [7:0]seg;infrared_rcv_top infrared_rcv_top_inst(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.infrared_in(infrared_in),.led(led),.sel(sel),.seg(seg));initial sys_clk=1'b1;always #(`clk_period/2) sys_clk=~sys_clk;initialbeginsys_rst_n <= 1'b0;infrared_in <= 1'b1;#100sys_rst_n <= 1'b1;
//引导码#1000infrared_in <= 1'b0; #9000000infrared_in <= 1'b1; #4500000
//地址码(发送地址码8’h99)
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//地址反码(地址反码为8’h66)
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据码(发送数据码8’h22)
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据反码(数据反码为8’hdd)
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据0infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #560000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//数据1infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #1690000
//重复码infrared_in <= 1'b0; #560000infrared_in <= 1'b1; #42000000infrared_in <= 1'b0; #9000000infrared_in <= 1'b1; #2250000infrared_in <= 1'b0; #560000infrared_in <= 1'b1;endmodule
2.仿真波形图
四、可能遇到的问题
仿真通过了但是按了遥控器之后没有数据显示:可能原因:选用的红外接收头有问题,正常可以使用的红外接收头引脚处还要接有某些电容电路,因此最好使用上面提到的模块进行实验。