HT6221红外遥控解码设计

项目名称

HT6221红外遥控解码设计

具体要求

接收红外按键的数据在ISSP上观察

设计说明

下图为红外遥控器及按键图。红外接收头有三个引脚,电源、地和信号输出。

                                

HT6221芯片的红外遥控发送数据帧定义,一帧数据由引导码、地址码、数据码及数据反码组成。地址码共16位,低位在前,高位在后,8位数据码及其反码也是低位在前,高位在后。

                

HT6221芯片是一款基于NEC红外通信协议的遥控编码芯片,该协议采用脉冲之间不同时长的时间间隔来区分“1”和“0”,下图为其编码协议中“1”和“0”的编码波形,而在实际接收时,接收头接收到信号后输出的波形刚好与此波形反相。

                    

数据1是0.56ms的低电平和1.69ms的高电平,数据0是0.56ms的低电平和0.56ms的高电平。

 代码设计

本次将代码拆开说明,先定义红外解码模块端口列表。iIR为接收头输出数据,irdata为解码后的数据码,iraddr为解码后的地址码,get_flag为解码完成标志信号。

module ir_decode(
	input				clk,
	input				rst_n,
	input				iIR,
	output	[15:0]irdata,
	output	[15:0]iraddr,
	output			get_flag
);

对接收头输出的数据进行同步,并定义一个上升沿和下降沿信号。

reg iIR_s0;
reg iIR_s1;
always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		iIR_s0<=0;
		iIR_s1<=0;
	end 
	else begin
		iIR_s0<=iIR;
		iIR_s1<=iIR_s0;
	end
wire nedge=iIR_s1 && ~iIR_s0;
wire pedge=~iIR_s1 && iIR_s0;

在使能计数的时候开始计数,计数超过10ms,计数器就开始清零。

reg en_cnt;
reg [18:0] cnt;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		cnt<=0;
	else if(en_cnt)
		cnt<=cnt+1;
	else
		cnt<=0;
		
reg timeout;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		timeout<=0;
	else if(cnt>=500000)
		timeout<=1;
	else
		timeout<=0;

定义一个计数到9ms、4.5ms、560us、1690us的标志信号。因为有一定的延迟或者其他原因取这些时间的一个范围。


reg flag_9ms;
reg flag_4_5ms;
reg flag_5_6us;
reg flag_1_6_9ms;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		flag_9ms<=0;
	else if(cnt>400000 && cnt<495000)
		flag_9ms<=1;
	else
		flag_9ms<=0;
		
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		flag_4_5ms<=0;
	else if(cnt>152500 && cnt<277500)
		flag_4_5ms<=1;
	else
		flag_4_5ms<=0;

always@(posedge clk or negedge rst_n)
	if(!rst_n)
		flag_5_6us<=0;
	else if(cnt>20000 && cnt<35000)
		flag_5_6us<=1;
	else
		flag_5_6us<=0;
		
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		flag_1_6_9ms<=0;
	else if(cnt>75000 && cnt<90000)
		flag_1_6_9ms<=1;
	else
		flag_1_6_9ms<=0;

根据帧头数据与数据波形对接收波形采用状态机进行实现.

s_idle为空闲状态,等待接收信号的下降沿。

s_flag_9ms为识别9ms的低电平引导码。

s_flag_4_5ms为识别4.5ms的低电平引导码。

s_get_data为读码状态

reg [3:0] state;
localparam s_idle      =4'b0001;
localparam s_flag_9ms  =4'b0010;
localparam s_flag_4_5ms=4'b0100;
localparam s_get_data  =4'b1000;

reg get_flag_done;

在空闲状态检测到接收信号下降沿跳转下一个状态,并开始使能计数,否则保持在空闲状态。第二个状态开始的时候使能计数,检测到上升沿的时候并且9ms的标志信号为高就开始跳转下一个状态,并让计数器清零,下一个状态重新开始计数,如果没有检测到9ms的标志信号继续等待。第三个状态类似。最后一个状态刚开始的时候使能计数,如果检测到上升沿并且560us的标志信号为低,或者检测到下降沿并且560us的标志信号与1690us的表示信号为低,表明接收数据有误,返回初始状态,数据接收成功后也返回初始状态。如果检测到上升沿并且560us的标志信号为高,或者如果检测到下降沿并且560us的标志信号与1690us的标志信号都为高,计数器清零。根据下图就可以很轻松设计出来。

                          

always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		state<=s_idle;
		en_cnt<=0;
		
	end
	else if(!timeout)
		case(state)
			s_idle		:	if(nedge)begin
									state<=s_flag_9ms;
									en_cnt<=1;
								end
								else begin
									state<=state;
									en_cnt<=0;
								end
			s_flag_9ms  :	if(pedge)begin
									if(flag_9ms)begin
										state<=s_flag_4_5ms;
										en_cnt<=0;
									end
									else 
										state<=s_idle;
								end	
								else begin
									state<=state;
									en_cnt<=1;
								end
			s_flag_4_5ms:	if(nedge)begin
									if(flag_4_5ms)begin
										state<=s_get_data;
										en_cnt<=0;
									end
									else
										state<=s_idle;
								end	
								else begin
									state<=state;
									en_cnt<=1;
								end
			s_get_data  :	if(pedge && !flag_5_6us)
									state<=s_idle;
								else if(nedge && (!flag_5_6us && !flag_1_6_9ms))
									state<=s_idle;
								else if(get_flag_done)
									state<=s_idle;
								else if(pedge && flag_5_6us) 
									en_cnt<=0;
								else if(nedge && (flag_1_6_9ms || flag_5_6us)) 
									en_cnt<=0;
								else
									en_cnt<=1;
		endcase
	else
		begin
			en_cnt<=0;
			state<=s_idle;
		end

最后对数据进行接收,在读码状态如果数据计数到32,解码完成标志信号拉高,数据计数清零,根据上图,如果检测到下降沿,数据计数加一,在检测到下降沿的同时如果1690us的标志信号为高表明解码到数据1,如果560us的标志信号为高表明解码到数据0。

data_temp为总共的数据位数,iraddr为地址,irdata为数据,协议规定先发送低位。所以现将地址位从低位到高位开始发送,再将数据位从低位到高位开始发送。

reg [5:0] data_cnt;
reg [31:0] data_temp;
assign irdata=data_temp[31:16];
assign iraddr=data_temp[15:0];
always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		data_cnt<=0;
		data_temp<=0;
		get_flag_done<=0;
	end
	else if(state==s_get_data)begin
		if( data_cnt==32)begin
			get_flag_done<=1;
			data_cnt<=0;
		end
		else begin
			if(nedge)
				data_cnt<=data_cnt+1;
		   if(nedge && flag_5_6us)
				data_temp[data_cnt]<=0;
			else if(nedge && flag_1_6_9ms)
				data_temp[data_cnt]<=1;
			get_flag_done<=0;
		end
	end
assign get_flag=get_flag_done;

仿真验证

将模块进行例化

`timescale 1ns/1ns
`define clk_period 20

module ir_decode_tb;
	reg				clk;
	reg				rst_n;
	reg				iIR;
	wire	[15:0]irdata;
	wire	[15:0]iraddr;
	wire			get_flag;



ir_decode ir_decode(
	.clk(clk),
	.rst_n(rst_n),
	.iIR(iIR),
	.irdata(irdata),
	.iraddr(iraddr),
	.get_flag(get_flag)
);

 iIR空闲状态为1,发送地址为1数据为 32和地址为2数据为22

initial clk=0;
always #(`clk_period/2) clk=~clk;

initial begin
	rst_n=0;iIR=1;
	#(`clk_period*10+1)
	rst_n=1;
	#2000;
	send_data(1,8'h32);
	#60000000;
	send_data(2,8'h22);
	#60000000;
	$stop;
end

 这里先说明一个小语法,task的用法,定义一个任务。

task task_demo;                //任务定义结构开头,命名为 task_demo
    input  [7:0] x,y;           //输入端口说明
    output [7:0] tmp;           //输出端口说明

begin

end

endtask

任务调用方法,其中端口1,端口2为参数。

task_demo(端口1,  端口 2, ........,  端口 N);  

 

发送引导码, 9ms的低电平和4.5ms的高电平。然后发送数据发送,560us的低电平之后拉高,之后如果iIR是1就延时1690us,否则延迟560us。

integer i;
task send_data;
input	[15:0] addr;
input [7:0] data;
begin
	iIR=0;#9000000;
	iIR=1;#4500000;
	
	for(i=0;i<=15;i=i+1)
		begin
			iIR=0;#560000;
			iIR=1;
			if(addr[i])
			#1690000;
			else
			#560000;
		end
		
	for(i=0;i<=7;i=i+1)
		begin
			iIR=0;#560000;
			iIR=1;
			if(data[i])
			#1690000;
			else
			#560000;
		end
	for(i=0;i<=7;i=i+1)
		begin
			iIR=0;#560000;
			iIR=1;
			if(~addr[i])
			#1690000;
			else
			#560000;
		end
		iIR=0;#560000;iIR=1;
		
end
endtask

仿真结果

issp调试

新建issp的ip核,在顶层模块中例化,将工程下载到板子上。

module decode_top(
	input				clk,
	input				rst_n,
	input				iIR,
	output	[15:0]irdata,
	output	[15:0]iraddr,
	output			get_flag
	
);

issp  issp(
	.probe({irdata,iraddr}),
	.source()
);


ir_decode ir_decode(
	.clk(clk),
	.rst_n(rst_n),
	.iIR(iIR),
	.irdata(irdata),
	.iraddr(iraddr),
	.get_flag(get_flag)
);

endmodule

打开issp,将数据改为16进制,选择连续抓取,按下按键0,可以看到抓取的数据。

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值