FPGA 24 工程模块 红外遥控(NEC协议)解码

在这里插入图片描述

FPGA 24 红外遥控(NEC协议)解码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zLl05bG1-1628512535487)(img/blog_img/fpga/image-20210809190920085.png)]

主要功能 :设计了一个红外 NEC协议的解码模块

实现(设计)流程:通过遥控器发送的红外信号,外围红外信号接收传感器对数据进行接收,得到一个在基频上的高低电平的(非方波)的输入信号IIR,fpga内部设计该模块,实现对该信号进行解码,设计一个状态机,最终判断数据解析是否完整接收,进而判断数据是否正确。接收正确以后,数据解析出来的地址和数据,以及本次解析完成的标志信号。

实验目的 : ①解决非方波信号协议的数据解码 ② 状态机判断和时间计数器在状态机中的灵活使用。③标志信号和always 块的相互分离,本次实验中,状态机数据接收的32个bit信号,我们是在外面用了一个always 块来处理数据接收的情况的,时间计数器的使能和不使能在实验中用的也是非常的灵活,这个是我们需要掌握的一些比较好的设计方法,让自己的代码更具有可读性.

实验现象 : 按下红外遥控上的按键,然后在QuartusⅡ软件中使用Iln system sources and probeseditor中观察解码结果,根据解码结果与红外遥控实际发出的数值进行比较从而判断解码的正确性。

HT6221芯片是一款基于NEC红外通信协议的遥控编码芯片,基于HT6221芯片的红外遥控发送一次数据的数据帧定义如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q7Ce0EuP-1628512535490)(E:/Blog_Template/source/_posts/img/blog_img/fpga/image-20210316171155756.png)]

一帧数据由引导码、地址码和数据码以及数据反码组成.

其中,引导码由9ms的载波波形和4.5ms的低电平组成,代表一个数据帧的帧头;

地址码共16位,低位在前,高位在后,因此,该协议理论上支持最局65536个个同的用户;

8位数据码及其反码也是低位在前,高位在后。因此,理论上该协议支持高达256个用户指令。

该协议采用脉冲之间不同时长的时间间隔来区分“1”和“O”,下图为其编码协议中“1”.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bW2NES61-1628512535492)(img/blog_img/fpga/image-20210809195509604.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ls0wx9B6-1628512535494)(E:/Blog_Template/source/_posts/img/blog_img/fpga/image-20210316171914372.png)]

但是(注这里很重要): 而在实际接收时,接收头接收到信号后【输出的波形刚好与此波形反相】。

所以:这里有两种处理方式,①在输入的fpga信号端加入一个非门,最后来回改发送的信号编写状态机的解析协议

② 对原始信号取反,直接对其反相信号进行解码(本次实验使用的是这种方式.)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-akHJqNom-1628512535496)(img/blog_img/fpga/image-20210809195757203.png)]

即实际的接收数据会变成如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OB6pa1dN-1628512535497)(img/blog_img/fpga/image-20210809195956762.png)]

实际接收到信号如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0CyZMMIi-1628512535499)(img/blog_img/fpga/image-20210809200604133.png)]

使用signal_tap-II 真实采样到的数据波形:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jt0dren8-1628512535499)(E:/Blog_Template/source/_posts/img/blog_img/fpga/image-20210317140527946.png)]

IDEL : 空闲状态(IDEL),等待IR接收信号下降沿的到来。表示引导码信号开始到来.进入到LEADER_T9 状态.

LEADER_T9 : 识别9ms 的低电平引导码,识别成功则继续识别4.5ms的高电平引导码状态LEADER_T4_5 ,否则返回空闲态。

LEADER_T4_5 : 识别4.5ms的高电平引导码,识别成功则进入读码(DATE_GET )状态,否则返回空闲态。

DATE_GET : 读码状态,若32个码字已经读完或者读取过程中发生了错误,则返回空闲态(IDEL)。

ir_decode.文件

module ir_decode(
	Clk,
	Rst_n,
	
	iIR,
	
	Get_Flag,
	irData,
	irAddr
);

	input Clk;    //时钟
	input Rst_n;  //复位
	input iIR;	  //红外模块信号输入
	
	output Get_Flag;  //解码成功标志信号
    output [15:0]irData;	//接收的数据 数据核数据反码
    output [15:0]irAddr;    //接收的地址  低8位核高8位
	
	reg [18:0]cnt;//time counter
	reg [3:0]state;
	
    // 定义一个时钟到达的计数器标志位
	reg T9ms_ok;
	reg T4_5ms_ok;
	reg T_56ms_ok;
	reg T1_69ms_ok;
	
	reg Get_Data_Done;
	reg Cnt_en;
	reg timeout;
	reg [5:0]data_cnt;
	
	reg [31:0]data_tmp;
	
	assign irAddr = data_tmp[15:0];
	assign irData = data_tmp[31:16];
	
    //状态机状态参数
	localparam 
		IDEL = 4'b0001,
		LEADER_T9 = 4'b0010,
		LEADER_T4_5 = 4'b0100,
		DATE_GET = 4'b1000;
		
	reg s_IR0,s_IR1;
	
    //消除亚稳态
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		s_IR0 <= 1'b0;
		s_IR1 <= 1'b0;
	end
	else begin
		s_IR0 <= iIR;
		s_IR1 <= s_IR0;
	end
	
	reg s_IR0_Temp,s_IR1_Temp;
	
    // 获取上升沿和下降沿
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		s_IR0_Temp <= 1'b0;
		s_IR1_Temp <= 1'b0;
	end
	else begin
		s_IR0_Temp <= s_IR1;
		s_IR1_Temp <= s_IR0_Temp;
        //    s_IR1_Temp:当前时刻t0   s_IR0_Temp:后一个时刻t1
	end
	
	wire ir_pedge,ir_nedge;
	
	assign ir_pedge = !s_IR1_Temp && s_IR0_Temp;   //上升沿:当前时刻是低电平,后一时刻是高电平
	assign ir_nedge = s_IR1_Temp && !s_IR0_Temp;   //下降沿:当前时刻是高电平,后一时刻是低电平
	
    //时间计数器  cnt_en 使能信号
    always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)	
		cnt <= 19'd0;
	else if(Cnt_en == 1'b1)
		cnt <= cnt + 1'b1;
	else
		cnt <= 19'd0;
    
    // T9ms_ok 标志位
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		T9ms_ok <= 1'b0;
    else if(cnt > 19'd325000 && cnt <19'd495000)  //  8.5ms ~ 9.5ms 
		T9ms_ok <= 1'b1;
	else
		T9ms_ok <= 1'b0;
	
    // T4_5ms_ok标志位  4ms ~ 5ms
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		T4_5ms_ok <= 1'b0;
    else if(cnt > 19'd152500 && cnt <19'd277500) //4ms ~ 5ms
		T4_5ms_ok <= 1'b1;
	else
		T4_5ms_ok <= 1'b0;
	
    // T_56ms_ok标志位  0.5ms ~ 0.6ms
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		T_56ms_ok <= 1'b0;
    else if(cnt > 19'd20000 && cnt <19'd35000)  // 0.5ms ~ 0.6ms
		T_56ms_ok <= 1'b1;
	else
		T_56ms_ok <= 1'b0;
	
    // T1_69ms_ok标志位  1.6ms ~ 1.8ms
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		T1_69ms_ok <= 1'b0;
	else if(cnt > 19'd75000 && cnt <19'd90000)
		T1_69ms_ok <= 1'b1;
	else
		T1_69ms_ok <= 1'b0;
		

	//计数器 timeout 超时处理
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)	
		timeout <= 1'b0;
	else if(cnt >= 19'd500000)
		timeout <= 1'b1;
	else 
		timeout <= 1'b0;
    
	// 
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		state <= IDEL;   //默认空闲状态
		Cnt_en <= 1'b0;
	end
    else if(!timeout  //没有发出超时信号
        begin  
		case(state)
			IDEL:
                if(ir_nedge)begin  		 // ir_nedge :下降沿到来,根据引导码的编码方式
                    			 	    // 它是先是高电平,随后变成低电平的一个信号,所以检测下降沿
					Cnt_en <= 1'b1;     // 如果是检测到下降沿,则Cnt_en=1,计时器开始计时
  					state <= LEADER_T9; // 开始计数,进入 LEADER_T9 (9ms 低电平信号)状态
				end
				else begin			  //位检测到上升沿,保持该状态不变
					state <= IDEL;
					Cnt_en <= 1'b0;
				end
			
			LEADER_T9:
                if(ir_pedge)begin  // 判断是否检测到上升沿,当检测到上升沿时,表示要么进入下一状态,
                    			  // 要么就是数据从出错
                    if(T9ms_ok)begin	//检测到帧头信号9ms的低电平时间在某个区间内 T9ms_ok 就会等于 1
						Cnt_en <= 1'b0;		 // Cnt_en=0 ,主要目的是计数器清零
						state <= LEADER_T4_5; //进入到下一个状态
					end
					else begin			//否则没数据检测错误
						state <= IDEL;	
					end
				end
				else begin		//还未检测到上升沿
					state <= LEADER_T9;	//状态保持
					Cnt_en <= 1'b1;		//计数器继续计数
				end
					
			LEADER_T4_5: 
                if(ir_nedge)begin  		// 判断是否检测到下降沿,当检测到下降沿时,表示要么进入下一状态(数据接收状态),
					if(T4_5ms_ok)begin//检测到帧头信号接收正确的标志位
						Cnt_en <= 1'b0;// Cnt_en=0 ,计数器清零
						state <= DATE_GET;//进入到下一个状态(数据接收状态)
					end
					else begin
						state <= IDEL;	 //否则没数据检测错误
					end
				end
				else begin  //还未检测到上升沿
					state <= LEADER_T4_5;
					Cnt_en <= 1'b1;
				end
					
			DATE_GET:
                if(ir_pedge && !T_56ms_ok)  //当我们接收到上升沿(也就是说是之前是低电平560ms),且低电平时间不等于560us,那么数据接收错误
					state <= IDEL;
            else if(ir_nedge && (!T_56ms_ok && !T1_69ms_ok)) //高电平时间 既不是高电平560us、1.69ms ,那么数据接收错误
					state <= IDEL;			
            else if(Get_Data_Done)	//数据接收完成,进行空闲状态
					state <= IDEL;	
            else if(ir_pedge && T_56ms_ok)begin // ir_pedge && T_56ms_ok 表示前面发送的是560us 低电平信号,进入高电平信号的判断
					Cnt_en <= 1'b0;   //正确接受到560us的低电平信号,时间计数器清零.
			end
            else if(ir_nedge && (T_56ms_ok || T1_69ms_ok))begin //ir_nedge && (T_56ms_ok || T1_69ms_ok) 高电平信号,且正常接收到 560us 的高电平或者 1.69ms 的高电平 
					Cnt_en <= 1'b0;	  //本次bit数据接收完成,时间计数器清零			
			end
		   else
				Cnt_en <= 1'b1;
			default:;
		endcase
		end
	else begin	//超时状态的话,让状态机复位
		Cnt_en <= 1'b0;
		state <= IDEL;	
	end
	
    // 数据计数器模块 
	always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)begin//复位信号  
		Get_Data_Done <= 1'b0;
		data_cnt <= 6'd0;
		data_tmp <= 32'd0;
	end
        else if(state == DATE_GET)begin  //状态机状态 DATE_GET 时候 ,进入数据判断任务状态
            if(ir_pedge && (data_cnt == 6'd32))begin //检测到上升沿的时候,且数据计数到33个上升沿的时候,表明数据已经接收完成
			data_cnt <= 6'd0;
			Get_Data_Done <= 1'b1; //发送 Get_Data_Done
		end
		else begin  //注:下降沿到来,表示数据的高电平时间发送完成
            if(ir_nedge) //只要检测到一个下降沿,数据计数器位+1  ,接收最后一个(第33个)下降沿的时候,变成 data_cnt = 32
				data_cnt <= data_cnt + 1'b1;
            if(ir_nedge && T_56ms_ok)  //检测到下降沿,且计数器时间位 0.56us ,数据为0
				data_tmp[data_cnt] <= 1'b0;
            else if(ir_nedge && T1_69ms_ok) //检测到下降沿,且计数器时间位 0.56us ,数据为0
				data_tmp[data_cnt] <= 1'b1;
			Get_Data_Done <= 1'b0;
		end	
	end
        
	
	assign Get_Flag = Get_Data_Done;

endmodule


ir_decode_tb.v 仿真测试文件

`timescale 1ns/1ns

`define clk_period 20

module ir_decode_tb;
	
	reg Clk;
	reg Rst_n;
	reg iIR;
	
	wire Get_Flag;
	wire [15:0]irData;
	wire [15:0]irAddr;
	
	integer i;

	ir_decode ir_decode(
		.Clk(Clk),
		.Rst_n(Rst_n),
		
		.iIR(iIR),
		
		.Get_Flag(Get_Flag),
		.irData(irData),
		.irAddr(irAddr)
	);
	
	initial Clk = 1'b1;
	always#(`clk_period/2)Clk = ~Clk;

	initial begin
		Rst_n = 1'b0;
		iIR = 1'b1;
		#(`clk_period*10+1'b1);
		Rst_n = 1'b1;
		#2000;
		iIR = 1'b1;
        send_data(1,8'h12); //调用 send_data任务
		#60000000;
        send_data(3,8'heb); //调用 send_data任务
		#60000000;
		$stop	;
	end


	
	task send_data;
		input [15:0]addr;
		input [7:0]data;
		begin
			iIR = 0;#9000000; //发送帧头保持9ms
			iIR = 1;#4500000; //发送高电平保持4.5ms
            for(i=0;i<=15;i=i+1)begin //发送地址,调用 bit_send task任务
				bit_send(addr[i]);		
			end
            for(i=0;i<=7;i=i+1)begin //发送数据
				bit_send(data[i]);		
			end
            for(i=0;i<=7;i=i+1)begin //发送数据反码
				bit_send(~data[i]);		
			end
			iIR = 0;#560000;    //发送停止位数延时0.56ms
			iIR = 1;		   //发送停止位的高电平
		end
	endtask
	
	task bit_send; 
		input one_bit;
		begin
			iIR = 0; #560000; //发送数据0,延时0.56ms
			iIR = 1;
            if(one_bit)   //发送1,高电平维持1.69ms
				#1690000;
			else		//发送0,高电平维持0.56ms
				#560000;
		end	
	endtask

endmodule

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值