基于FPGA的红外遥控实验

描述

理论基础

可见光中红光的波长范围为620nm ~ 720nm,波长比红光波长还长的的光叫红外线。红外线按波长范围可分为近红外、中红外、远红外、极红外。红外线遥控是利用近红外光传送遥控指令的,波长为0.76um~1 .5um。

红外遥控发射部分由遥控按键、编码以及调制电路、红外发光二极管等组成。红外遥控接收部分由光敏二极管、解调电路等组成。最后将解调的信号输入FPGA内进行解码输出。接收信号是板载的HS0038B,该接收头收到信号后输出的波形刚好与发送的波形相反。发射信号是遥控器,键码如下:

时钟

编码协议NEC

红外遥控的编码协议有NEC、Philips RC-5、Philips RC-6、Sony SIRC等,而使用最多的是 NEC协议 。NEC协议采用的是PPM(Pulse Position Modulation,脉冲位置调制)进行编码。当我们按下遥控器的一个按键时,会发送一帧的数据。这一帧数据由 引导码、地址码、地址反码、数据码、数据反码以及一位结束位 (可忽略)组成。

时钟9ms的高电平+4.5ms的低电平组成了引导码。后面是 地址码和地址反码 ,0和1是由时间间隔区分的(逻辑“1”由560us的高脉冲加上1.69ms的低电平组成,而逻辑“0”由560us的高脉冲加上560us的低电平组成)。如图分别是0000_0000和1111_1111,再后面是 数据码和数据反码 ,如图是1010_1101(注意低位和高位顺序)和0101_0010。由于是正码和反码,因此总共0和1的数量一定都是8个,那么它们的总时长是不变的。最后是562.5um脉冲突发以表示消息传输的结束。

长按时,当发送完数据后,每隔110ms会发送一个重复码,重复码由9ms的高脉冲和2.25ms的低电平以及560us的高脉冲(结束标志)组成。们使用的一体化接收头接收到信号后输出到FPGA的波形刚好与发送的波形 相反 。即发送的高脉冲,接收后输出就为低电平;发送的低电平,接收后输出就为高电平。

设计规划

使用红外遥控器发送红外信号,FPGA开发板上的接收头接收到红外信号后传入FPGA芯片内,FPGA芯片接收到信号后进行解码,将解码后的按键码显示在数码管上。若检测到发送了重复码,则让led闪烁显示,一个重复码闪烁一次。

时钟

一共包括四个模块,红外模块,led模块,数码管动态显示模块和顶层模块。其中数码管动态显示模块可以直接调用以前的,而led模块比较简单,因此重心放在红外模块上。

红外模块

输入是时钟,复位和红外输入信号,输出是数据和重复使能信号。其中data是27位,我们的协议是8位,这里用27位是因为数码管动态显示模块是27位的(显示最大值9999_9999),所以在这里也定义成20位的位宽与之相匹配,防止出现未知的错误。我们收到数据后首先要判断是否按照NEC协议发送的,只有是才能输出相应的数据和重复使能信号。我们知道接收的数据与红外发送的数据是相反的,发送的引导码是9ms的高电平+4.5ms的低电平,那么只要判断接收的引导码是不是9ms的低电平和4.5ms的高电平,接下来的地址码,数据码和重复码也是同理。

用状态图的跳转来展示整个过程:

IDEL状态:初始状态,等待红外信号的到来。当检测到下降沿来临时,表示9ms的低电平已经开始发送,这时我们从IDLE跳转到S_T9状态。

S_T9:检测9ms低电平状态。检测到上升沿时,这中间的低电平如果保持了9ms,跳转到下一个状态,如果不是说明信息有误,回到IDEL状态重新开始。

S_JUDGE:由于9ms低电平后可能是4.5ms高电平(引导码)也可能是2.25ms高电平(重复码),如果是前者,说明后面是地址码、地址反码、数据码和数据反码,跳转到S_IFR_DATA;如果是后者,说明这一段是重复码,跳转到S_REPEAT;若都不是,说明发送错误,回到IDEL。

S_IFR_DATA:地址码、地址反码和数据码、数据反码的接收与发送的电平相反,检测到560us的低电平后,如果接的是1.69ms高电平说明逻辑1,560us高电平说明逻辑0。当接受完32位数据后回到IDEL,如果都不满足说明发送错误,回到IDEL。

S_REPEAT:重复码状态。当上升沿到来的时直接回到IDEL。

通过判断高低电平的持续时间去判断状态,一共涉及到9ms(449_999)、4.5ms(224_999)、2.25ms(112_499)、1.69ms(84_499)、0.56ms(27_999)的时间间隔,因此需要5个计数器去产生fag信号。flag信号在以前章节都是持续一个时钟周期的高电平,这里由于晶振可能不会刚好控制在9ms整,因此需要让它在较大的时间范围保持高电平。

时间信号产生波形图

时钟

接下来要对红外信号进行 采沿 ,采沿的代码在工程上非常常见!!!对需要采沿的信号打拍后经过取反、相与操作即可获得上升沿或下降沿标志信号。用always能检测到信号状态的改变,但是占用资源和时间。

上升沿下降沿信号产生波形图

时钟

ifr_in_rise=infrared_in_d1&&(~infrared_in_d2)

ifr_in_fall=infrared_in_d2&&(~infrared_in_d1)

引导码状态跳转波形图

时钟

代码编写

module infrared_rcv
(
input wire sys_clk , 
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)


 IDLE:
 if(ifr_in_fall == 1'b1)
 state <= S_T9;
 else 
 state <= IDLE;


 S_T9: 
 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: 
 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;
 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:
 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

参数定义:我们需要规定flag信号拉起时的计数值,由于要保持一个时间段,5个计数值都要定义低值和高值

状态定义:5个状态用5位数值表示,每个状态都有一个不同位为1。对于大型状态机而言,如果上一个状态到下一个状态只改变一位会比这种定义好

检测上升沿和下降沿:检测方式是打拍之后取反求与,如果检测到,对应的信号拉高

打拍:在第四节中介绍了阻塞赋值和非阻塞赋值,非阻塞赋值是并行的,always块结束之后再完成赋值行为。所以在波形上看一条非阻塞赋值是有一个时钟周期的延时。这个打拍可以理解为一次延时。

always块中a=din;b=a;c=b; always块中a=| | Q1 | Q2 | Q3 | Q4 | | Q1 | Q2 | Q3 | Q4 |
| ----- | ---- | ---- | ---- | ---- | ----- | ---- | ---- | ---- | ---- |
| din | 0 | 1 | 1 | 0 | din | 1 | 0 | 0 | 0 |
| a | 0 | 1 | 1 | 0 | a | 1 | 0 | 0 | 0 |
| b | 0 | 1 | 1 | 0 | b | 1 | 1 | 0 | 0 |
| c | 0 | 1 | 1 | 0 | c | 1 | 1 | 1 | 0 |

cnt计数:非复位状态时要看状态,IDEL状态cnt一直保持0,S_T9状态要判断是否低电平持续9ms,条件是如果有上升沿的同时9ms的flag拉起,就要归0重新准备计数,否则+1。其他状态也是同理。

flag信号:以1.69ms为例,若复位有效,flag为低电平;如果状态位于S_IFR_DATA且满足1.69ms计数值的低值到高值之间,就拉高,否则维持低电平,其他的flag信号也是同理

状态跳转:复位有效是IDEL状态;当状态处于IDEL时,如果检测到下降沿则跳转到S_T9,否则说明发送错误,维持在IDEL;当状态处于S_T9时,如果检测到上升沿且低电平维持了9ms左右则跳转到S_JUDGE,如果不是维持9ms说明发送错误则跳转到IDEL;当状态处于S_JUDGE时,如果检测到下降沿且高电平维持了2.25ms左右,说明是重复码跳转到S_REPEAT左右,如果维持4.5ms说明后面要接地址码等跳转到S_IFR_DATA状态,如果都不是说明发送错误回到IDEL状态;当状态处于S_IFR_DATA时,如果检测到上升沿但不是维持56ms说明发送错误回到IDEL状态,如果检测到下降沿但是高电平时间不满足逻辑0或1说明发送错误回到IDEL状态,如果检测到上升沿且数据计数器满足32位则接收完毕回到IDEL状态;当状态处于S_REPEAT状态时,如果检测到上升沿直接回到IDEL状态,否则维持原态。case语句需要default。

数据缓存:在S_IFR_DATA状态时接收的32位数据需要区分逻辑0和1,先缓存到data_tmp中。当状态是S_IFR_DATA时,复位有效时32位缓存都为0;当检测到下降沿且高电平维持0.56ms说明是逻辑0,将其写入到对应位的缓存中;维持1.69ms说明是逻辑1,也写入。

data_cnt:数据计数器,因此有32位数据,每计数一次都要写入一位。复位有效时为低电平;检测到上升沿且data_cnt=32说明计满且写满了,归0;检测到下降沿且处于S_IFR_DATA状态,+1;其他情况维持不变

repeat_en:重复使能信号,要传递给Led作为亮灯的控制信号。重复的判断条件是1、处于重复S_REPEAT状态2、确实检测到了重复的数据。复位有效时归0;状态处于重复状态且缓存中高8位和次8位相同时拉高;其他情况归0

数据输出:复位有效时数据所有位都归0;32位数据校验方式是地址码和地址反码是位反的,数据码和数据反码是位反的,数据计数器计满32个数值,如果检验成功,就将数据码进行输出。由于data和之前数码管动态显示中的位数一致是27位,这里输出数据只有8位,因此高位都赋0。数码管只需要显示低8位表示的数值即可

LED灯控制模块

当有一次重复码时,重复使能有效,led闪烁一次。如果让灯在使能有效时亮,使能信号持续时间很短,led亮的时间也短,效果不明显。长按红外遥控按键那么每110ms会发送一次重复码,那么如果让led灯在110ms中亮50ms,led的显示效果比较好。需要一个50ms的计数器,在repeat_en信号检测出上升沿时开始计数,此时led低电平点亮,计数完毕后led熄灭。

时钟

编写代码

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;


 //对repeat_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

参数定义,获取上升沿,打拍

使能信号cnt_en:复位时归0;cnt计数到CNT_MAX-1时归0,上升沿信号repeat_en_rise拉高时拉高

计数器cnt:复位时归0;cnt_en为高电平时计数+1,其他情况归0

led:复位时拉高表示熄灭,当cnt>0(1-250000)时led拉低表示点亮,否则熄灭

顶层模块

module top_infrared_rcv
(
input wire sys_clk ,
input wire sys_rst_n , 
input wire infrared_in , 


output wire stcp , 
output wire shcp , 
output wire ds , 
 output wire oe , 
 output wire led 
 );


 //wire define
 wire repeat_en ; //重复码使能信号
 wire [19:0] data ; //接收的控制码


 infrared_rcv infrared_rcv_inst
 (
 .sys_clk (sys_clk ), 
 .sys_rst_n (sys_rst_n ), 
 .infrared_in (infrared_in), 
 .repeat_en (repeat_en ), 
 .data (data ) 
 );


 led_ctrl led_ctrl_inst
 (
 .sys_clk (sys_clk ) ,
 .sys_rst_n (sys_rst_n) ,
 .repeat_en (repeat_en) , 
 .led (led )
 );


 seg_595_dynamic seg_595_dynamic_inst
 (
 .sys_clk (sys_clk ), 
 .sys_rst_n (sys_rst_n), 
 .data (data ), 
 .point (6'd0 ), 
 .seg_en (1'b1 ), 
 .sign (1'b0 ), 
 .stcp (stcp ), 
 .shcp (shcp ), 
 .ds (ds ), 
 .oe (oe ) 
 );


 endmodule

时钟

testbench

`timescale 1ns/1ns
module tb_top_infrared_rcv();


//wire define
wire led ;
wire stcp ;
wire shcp ;
wire ds ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg 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 ), //串行数据输入
 .led (led ) //led灯控制信号
 );
 endmodule

初始化

引导码:低电平9ms,高电平4.5ms

地址码1001_1001:逻辑1是低电平延迟0.56ms后高电平延迟0.56ms,逻辑0是低电平延迟0.56ms后高电平延迟1.69ms

地址反码0110_0110

数据码0010_0010

数据反码1101_1101,数据反码后要以0.56ms的低电平作为结束标志

重复码:维持一段时间高电平后再发送重复码,低电平延迟9ms再高电平延迟2.25ms,以0.56ms的低电平作为重复码的结束标志

产生时钟,实例化

对比波形

时钟

data是27h'0000022与我们发送的数据一致,重复使能也按照预期实现。

管脚分配

红外模块的管脚图,其他管脚之前都用过不重复

时钟

时钟

全编译后上板验证

长按才亮灯,按键对应的值显示在数码管上,视频中我可能按的比较重。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分