文章目录
一、红外遥控简介
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;
else
infrared_in_d1<=infrared_in;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
infrared_in_d2<=1'b0;
else
infrared_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;
else
cnt<=cnt+1'b1;
S_JUDGE:
if(ifr_in_fall && (flag_4_5ms || flag_2_25ms))
cnt<=1'b0;
else
cnt<=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;
else
cnt<=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;
else
flag_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;
else
flag_1_69ms<=1'b0;
//计数2.25ms
always @(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;
else
flag_2_25ms<=1'b0;
//计数4.5ms
always @(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;
else
flag_4_5ms<=1'b0;
//计数9ms
always @(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;
else
flag_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;
else
state<=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;
else
state<=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;
else
state<=S_REPEAT;
default:
state<=IDLE;
endcase
always @(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;
else
data_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;
else
data_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;
else
repeat_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)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
assign 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;
else
cnt_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))
begin
data_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]);
end
else if(cnt_shift<=5'd20 && shift_flag)
data_shift<=data_shift<<1;
else
data_shift<=data_shift;
always @(posedge clk or negedge rst_n)
if(!rst_n)
shift_flag<=1'b0;
else
shift_flag<=~shift_flag;
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
unit<=1'b0;
ten<=1'b0;
hun<=1'b0;
tho<=1'b0;
t_tho<=1'b0;
h_hun<=1'b0;
end
else if(cnt_shift==5'd21)
begin
unit<=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];
end
endmodule
②数码管控制模块
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};
//计数器计数1ms
always @(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;
else
flag_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;
else
cnt_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;
else
sel_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;
endcase
else
data_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];
else
dot_disp<=dot_disp;
//控制数码管段选信号,显示数字
always @(posedge clk or negedge rst_n)
if(!rst_n)
seg<=8'b1111_1111;
else
case(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;
else
sel<=~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 20
module 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;
initial
begin
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;
endmodule
2.仿真波形图
四、可能遇到的问题
仿真通过了但是按了遥控器之后没有数据显示:可能原因:选用的红外接收头有问题,正常可以使用的红外接收头引脚处还要接有某些电容电路,因此最好使用上面提到的模块进行实验。