前言
红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机和手机系统中。本文首先介绍了红外遥控模块的基本原理,其次详解阐述了红外遥控模块工作原理,最后介绍了红外遥控的重要环节及应用。
正文
一、红外遥控设计验证
1.项目需求
实验任务:将接收到的遥控数据显示到数码管上,当收到重复码时进行LED闪烁。
2.技术介绍
红外遥控编码协议
NEC协议:采用的是PPM(Pulse Position Modulation,脉冲位置调制)进行编码。当我们按下遥控器的一个按键时,会发送一帧的数据。这一帧数据由引导码、地址码、地址反码、数据码、数据反码以及一位结束位(可忽略)组成。
RC-5协议:它通过脉冲宽度调制技术,使用曼彻斯特编码的方式传输数据,确保了数据传输的准确性和可靠性。RC-5协议的数据包包括起始位、地址位和命令位,能够识别不同的设备并发出具体的操作指令。工作在38kHz的载波频率上,具有良好的抗干扰能力和误码处理能力,广泛应用于电视、音响等遥控控制系统中。
RC-6协议:RC-6协议是飞利浦开发的红外遥控协议,相比于RC-5,它在数据传输中引入了更多的功能扩展和改进的编码方式,以提高可靠性和减少误码率。RC-6使用较低的载波频率和复杂的数据包结构,支持更多的操作模式和功能,同时保持了一定的兼容性,广泛应用于各种高端遥控设备中。
红外线遥控是利用近红外光传送遥控指令的,波长为0.76um~1.5umo。红外发射器件(红外发光管)与红外接收器件(光敏二极管、三极管及光电池)的发光与受光峰值波长一般为0.8um~0.94um,在近红外光波段内,二者的光谱正好重合,能够很好地匹配,可以获得较高的传输效率及较高的可靠性。
红外遥控发射部分由遥控按键、编码与调制电路,红外发光二极管等组成;红外遥控接收部分由光敏二极管、光电放大电路、解调电路等组成;最后将解调的信号输入FPGA内进行解码输出。
当按下遥控器后遥控器会发送一帧数据,引导码由9ms高脉冲+4.5ms低电平组成,地址码为1个8位二进制数据,可以根据地址码判断是哪个设备发送的该帧数据,地址反码是地址码的按位取反,用于验证地址码,数据码是按键的按键码,8位二进制数(低位数据在前),数据反码校验数据码,一位结束位为560us高脉冲。NEC协议中逻辑1用560us高脉冲+1.69ms低电平表示,逻辑0用560us高脉冲+560us低电平表示,后面还有560us高脉冲的结束位
接收到的引导码变为9ms低电平+4.5ms高电平,逻辑1/0分别变为560us低电平+1690us高电平/560低电平+560us高电平,重复码变为9ms低电平+2.25ms高电平+560us低电平,接收波形即fpga接收到的数据。
当按着按键不放时发送的数据码变为重复码,数据帧格式变为重复码:9ms高脉冲+2.25ms低电平+560us结束位,重复码一帧110ms.
接收的数据与发送数据不同,在发送过程中,高脉冲,与低电平会反转,上图为正确接收到的数据格式。
这里要在不同码段进行不同计数判断,采样状态机可完美解决该问题,下图为为该系统设计状态机的状态转移图。
3.顶层架构
4.端口描述
clk | 时钟信号(50Mhz) |
rst_n | 复位信号(低电平有效) |
led | led输出 |
inf_in | 红外接收模块接收到的数据 |
[2:0] sel | 位选信号 |
[7:0] seg | 段选信号 |
二、代码验证
接收数据处理模块
module ho_wai(
input clk ,
input rst_n ,
input inf_in ,
output reg[23:0] data ,
output reg led_en
);
parameter idle = 5'b00001,//等待
time9ms = 5'b00010,//9ms引导
zhocai = 5'b00100,//数据码重复码判断
dataqu = 5'b01000,//数据码
chofu = 5'b10000;//重复码
parameter cnt_560usmin = 19'd20_000,
cnt_560usmax = 19'd35_000,
cnt_1_69msmin = 19'd80_000,
cnt_1_69msmax = 19'd90_000,
cnt_2_25msmin = 19'd100_000,
cnt_2_25msmax = 19'd125_000,
cnt_4_5msmin = 19'd175_000,
cnt_4_5msmax = 19'd275_000,
cnt_9msmin = 19'd400_000,
cnt_9msmax = 19'd490_000;
reg [4:0] stater;//状态寄存器
reg inf_inda1;//输入信号寄存一排
reg inf_inda2;//输入信号寄存一排
wire inf_ifall;//输入信号下降沿检测
reg [18:0] cnt;
wire inf_rise;//输入信号上升沿检测
reg flag_9ms;//9ms到来标志信号
reg flag_4_5ms;//4.5ms到来标志信号
reg [5:0] cnt_data;//数据计数器
reg flag_560us;//560us到来标志信号
reg flag_1_69ms;//1.69ms到来标志信号
reg [31:0] data_reg;
reg flag_2_25ms;//2.25ms到来标志信号
always@(posedge clk,negedge rst_n)//状态跳转
begin
if(rst_n == 0)
stater <= idle;
else
case(stater)
idle: if(inf_ifall == 1'b1)//下降沿
stater <= time9ms;
else
stater <= idle;
time9ms: if((inf_rise == 1'b1)&&(flag_9ms == 1'b1)) // 上升沿并且到9ms
stater <= zhocai;
else
if((inf_rise == 1'b1)&&(flag_9ms == 1'b0))//上升沿并且不到9ms
stater <= idle;
else
stater <= time9ms;
zhocai: if((inf_ifall == 1'b1)&&(flag_2_25ms == 1'b1))//下降沿并且到2.25ms
stater <= chofu;
else
if((inf_ifall == 1'b1)&&(flag_4_5ms == 1'b1))//下降沿并且到4.5ms
stater <= dataqu;
else
if((inf_ifall == 1'b1)&&(flag_4_5ms == 1'b0)&&(flag_2_25ms == 1'b1))
stater <= idle;
else
stater <= zhocai;
dataqu: if((inf_rise == 1'b1)&&(flag_560us == 1'b0))
stater <= idle;
else
if((inf_ifall == 1'b1)&&(flag_560us == 1'b0)&&(flag_1_69ms == 1'b0))
stater <= idle;
else
if((inf_rise == 1'b1)&&(cnt_data == 6'd32))
stater <= idle;
else
stater <= dataqu;
chofu: if(inf_rise == 1'b1)
stater <= idle;
else
stater <= chofu;
default : stater <= idle;
endcase
end
always@(posedge clk,negedge rst_n)//数据打拍
begin
if(rst_n == 0)
inf_inda1 <= 1'b0;
else
inf_inda1 <= inf_in;
end
always@(posedge clk,negedge rst_n)//数据打拍
begin
if(rst_n == 0)
inf_inda2 <= 1'b0;
else
inf_inda2 <= inf_inda1;
end
assign inf_ifall = (inf_inda1 == 1'b0)&&(inf_inda2 == 1'b1);//下降沿检测
assign inf_rise = (inf_inda1 == 1'b1)&&(inf_inda2 == 1'b0);//上升沿检测
always@(posedge clk,negedge rst_n)//计数器驱动
begin
if(rst_n == 0)
cnt <= 19'd0;
else
case(stater)
idle: cnt <= 19'd0;//计数器清零,方便下次计数
time9ms: if((inf_rise == 1'b1)&&(flag_9ms == 1'b1))
cnt <= 19'd0;//计数器清零,方便下次计数
else
cnt <= cnt + 19'd1;
zhocai: if((inf_ifall == 1'b1)&&((flag_2_25ms == 1'b1)||(flag_4_5ms == 1'b1)))
cnt <= 19'd0;//计数器清零,方便下次计数
else
cnt <= cnt + 19'd1;
dataqu: if((inf_rise == 1'b1)&&(flag_560us == 1'b1))
cnt <= 19'd0;//计数器清零,方便下次计数
else
if((inf_ifall == 1'b1)&&((flag_560us == 1'b1)||(flag_1_69ms == 1'b1)))
cnt <= 19'd0;//计数器清零,方便下次计数
else
cnt <= cnt + 19'd1;
chofu: cnt <= 19'd0;
default : cnt <= 19'd0;
endcase
end
always@(posedge clk,negedge rst_n)//flag_9ms到来标准信号使能
begin
if(rst_n == 0)
flag_9ms <= 1'b0;
else
if((stater == time9ms)&&(cnt >= cnt_9msmin)&&(cnt <= cnt_9msmax))
flag_9ms <= 1'b1;
else
flag_9ms <= 1'b0;
end
always@(posedge clk,negedge rst_n)//flag_4_5ms到来标准信号使能
begin
if(rst_n == 0)
flag_4_5ms <= 1'b0;
else
if((stater == zhocai)&&(cnt >= cnt_4_5msmin)&&(cnt <= cnt_4_5msmax))
flag_4_5ms <= 1'b1;
else
flag_4_5ms <= 1'b0;
end
always@(posedge clk,negedge rst_n)//flag_560us到来标准信号使能
begin
if(rst_n == 0)
flag_560us <= 1'b0;
else
if((stater == dataqu)&&(cnt >= cnt_560usmin)&&(cnt <= cnt_560usmax))
flag_560us <= 1'b1;
else
flag_560us <= 1'b0;
end
always@(posedge clk,negedge rst_n)//flag_1_69ms到来标准信号使能
begin
if(rst_n == 0)
flag_1_69ms <= 1'b0;
else
if((stater == dataqu)&&(cnt >= cnt_1_69msmin)&&(cnt <= cnt_1_69msmax))
flag_1_69ms <= 1'b1;
else
flag_1_69ms <= 1'b0;
end
always@(posedge clk,negedge rst_n)//flag_2_25ms到来标准信号使能
begin
if(rst_n == 0)
flag_2_25ms <= 1'b0;
else
if((stater == zhocai)&&(cnt >= cnt_2_25msmin)&&(cnt <= cnt_2_25msmax))
flag_2_25ms <= 1'b1;
else
flag_2_25ms <= 1'b0;
end
always@(posedge clk,negedge rst_n)//数据位计数器驱动模块
begin
if(rst_n == 0)
cnt_data <= 6'd0;
else
if((inf_rise == 1'b1)&&(cnt_data == 6'd32))
cnt_data <= 6'd0;
else
if((inf_ifall == 1'b1)&&(stater == dataqu))//在数据状态下,检测到数据进行自加
cnt_data <= cnt_data + 1'b1;
else
cnt_data <= cnt_data;
end
always@(posedge clk,negedge rst_n)//接收数据码暂存
begin
if(rst_n == 0)
data_reg <= 32'd0;
else
if((stater == dataqu)&&(inf_ifall == 1'b1)&&(flag_560us == 1'b1))
data_reg[cnt_data] <= 1'b0;
else
if((stater == dataqu)&&(inf_ifall == 1'b1)&&(flag_1_69ms == 1'b1))
data_reg[cnt_data] <= 1'b1;
else
data_reg <= data_reg;
end
always@(posedge clk,negedge rst_n)//数据传递
begin
if(rst_n == 0)
data <= 24'd0;
else
if((cnt_data == 6'd32)&&(~data_reg[23:16] == data_reg[31:24])&&(~data_reg[15:8] == data_reg[7:0]))//源码反码验证
data <= {16'b0,data_reg[23:16]};
else
data <= data;
end
always@(posedge clk,negedge rst_n)//重复码下led驱动模块
begin
if(rst_n == 0)
led_en <= 1'b0;
else
if((stater == chofu)&&(~data_reg[23:16] == data_reg[31:24]))
led_en <= 1'b1;
else
led_en <= 1'b0;
end
endmodule
呼吸灯模块
module huxi_led(
input clk ,
input rst_n ,
input led_en ,
output reg led
);
parameter cnt_1s_max = 10'd999;//1s最大计数值
parameter cnt_1ms_max = 10'd999;//1ms最大计数值
parameter cnt_1us_max = 6'd49;//1us最大计数值
reg [9:0]cnt_1s;//1s计数器
reg [9:0]cnt_1ms;//1ms计数器
reg [5:0]cnt_1us;//1us计数器
reg cnt_en;
always @(posedge clk,negedge rst_n)//1us计数原理
begin
if(rst_n == 0)
cnt_1us <= 6'd0;
else
if(cnt_1us == cnt_1us_max)//到达最大计数值计数器清零
cnt_1us <= 6'd0;
else
cnt_1us <= cnt_1us + 6'd1;
end
always @(posedge clk,negedge rst_n)//1ms计数原理
begin
if(rst_n == 0)
cnt_1ms <= 10'd0;
else //1ms计数器到达最大值时需要等待1us计数器记到最大值时进行清零
if((cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1ms <= 10'd0;//到达最大计数值计数器清零
else //1us计数器到最大值时进行累加
if(cnt_1us == cnt_1us_max)
cnt_1ms <= cnt_1ms + 10'd1;
else
cnt_1ms <= cnt_1ms;
end
always @(posedge clk,negedge rst_n)//1s计数原理
begin
if(rst_n == 0)
cnt_1s <= 10'd0;
else //1s计数器记到最大值且1ms计数器到达最大值,等待1us计数器记到最大值时进行清零
if((cnt_1s == cnt_1s_max)&&(cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1s <= 10'd0;//到达最大计数值计数器清零
else //1us计数器和1ms计数器到最大值时进行累加
if((cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1s <= cnt_1s + 10'd1;
else
cnt_1s <= cnt_1s;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_en <= 1'b0;
else //完全点亮或完全熄灭后进行状态反转,模拟出呼吸转换
if((cnt_1s == cnt_1s_max)&&(cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_en <= ~cnt_en;
else
cnt_en <= cnt_en;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
led <= 1'b0;
else //cnt_en 为1时进行呼吸点亮,为0时进行呼吸熄灭
if(led_en == 1'b1)
begin
if(((cnt_en == 1'b0)&&(cnt_1ms <= cnt_1s))||((cnt_en == 1'b1)&&(cnt_1ms > cnt_1s)))
led <= 1'b1;
else
led <= 1'b0;
end
else
led <= 1'b0;
end
endmodule
数码管驱动模块
module seg_driver(
input clk ,
input rst_n ,
input [23:0] data_in ,//接收数字时钟信号
output reg [2:0] sel ,//位选信号
output reg [7:0] seg //段选信号
);
reg [18:0] cnt;//1MS
parameter MAX = 19'd50_000;
//20ns记50000次
//产生1ms的延时
//位选信号1ms变换一次
reg [3:0] temp;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 19'd0;
else
if(cnt < MAX - 1)//产生1ms的延时
cnt <= cnt + 19'd1;
else
cnt <= 19'd0;
end
//数码管位选信号控制逻辑
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
sel <= 3'd0;
else
if(cnt == MAX - 1)//位选信号6位,1ms的延时到变化一次
if(sel < 5)
sel <= sel + 3'd1;
else
sel <= 3'd0;
else
sel <= sel;
end
always @(*) begin
if(rst_n == 0)
temp <= 4'd0;
else
case(sel)//位选信号到将数据输出到对应输出位上
3'd0 : temp <= data_in[23:20];
3'd1 : temp <= data_in[19:16];
3'd2 : temp <= data_in[15:12];
3'd3 : temp <= data_in[11:8];
3'd4 : temp <= data_in[7:4];
3'd5 : temp <= data_in[3:0];
default : temp <= 4'd0;
endcase
end
always @(*) begin
if(rst_n == 0)
seg <= 8'hff;
else
case(temp)//段选信号,对应的数码管信号
4'd0 : seg <= 8'b1100_0000;//0
4'd1 : seg <= 8'b1111_1001;//1
4'd2 : seg <= 8'b1010_0100;//2
4'd3 : seg <= 8'b1011_0000;//3
4'd4 : seg <= 8'b1001_1001;//4
4'd5 : seg <= 8'b1001_0010;//5
4'd6 : seg <= 8'b1000_0010;//6
4'd7 : seg <= 8'b1111_1000;//7
4'd8 : seg <= 8'b1000_0000;//8
4'd9 : seg <= 8'b1001_0000;//9
4'd10 : seg <= 8'b1000_1000;//A
4'd11 : seg <= 8'b1000_0011;//b
4'd12 : seg <= 8'b1100_0110;//C
4'd13 : seg <= 8'b1010_0001;//d
4'd14 : seg <= 8'b1000_0110;//E
4'd15 : seg <= 8'b1000_1110;//F
default : seg <= 8'hff;
endcase
end
endmodule
顶层连线
module top_howai(
input clk ,
input rst_n ,
input inf_in ,
output [2:0] sel ,//位选信号
output [7:0] seg , //段选信号
output led
);
wire led_en ;
wire[23:0] data ;
ho_wai ho_wai_inst(
.clk (clk ),
.rst_n (rst_n ),
.inf_in (inf_in ),
.data (data ),
.led_en (led_en )
);
huxi_led huxi_led_inst(
.clk (clk ),
.rst_n (rst_n ),
.led_en (led_en ),
.led (led )
);
seg_driver seg_driver_inst(
.clk (clk ),
.rst_n (rst_n ),
.data_in (data ),//接收数字时钟信号
.sel (sel ),//位选信号
.seg (seg )//段选信号
);
endmodule
仿真代码
`timescale 1ns/1ps
module top_howai_tb;
reg clk ;
reg rst_n ;
reg inf_in;
wire [7:0] seg ;
wire [2:0] sel ;
wire led ;
defparam top_howai_inst.huxi_led_inst.cnt_1s_max = 5;
defparam top_howai_inst.huxi_led_inst.cnt_1ms_max = 5;
defparam top_howai_inst.huxi_led_inst.cnt_1us_max = 2;
top_howai top_howai_inst(
.clk (clk ),
.rst_n (rst_n ),
.inf_in (inf_in ),
.sel (sel ),//位选信号
.seg (seg ), //段选信号
.led (led )
);
initial clk =1;
always #10 clk = ~clk;
initial begin
rst_n = 0;
inf_in = 1;
#20
rst_n = 1;
#100
inf_in = 0;//引导码
#9000000
inf_in = 1;
#4500000
inf_in = 0;//地址码1110_1010
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;//地址反码(1110_1010---0001_0101)
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;//数据码
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;//数据反码(0100_0100--1011_1011)
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#560000 //逻辑0
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;
#560000
inf_in = 1;
#1690000 //逻辑1
inf_in = 0;//结束位
#560000
inf_in = 1;//高电平保持
#42000000
inf_in = 0;//重复码
#9000000
inf_in = 1;
#2250000
inf_in = 0;//结束位
#560000
inf_in = 1;
#10000
$stop;
end
endmodule
三、仿真验证
运行仿真,调出中间信号进行观察,如下图,数据码可以完整提取出,为01000100,低位在前,即数据为00100010,对应22即按下遥控器上0按键一次。
当通过引导码判别出接收数据为重复码时,led_en拉高,led以呼吸灯形式闪烁。
观察stater状态按照理论进行跳转,实验成功