本次主要实现红外遥控解码模块的实现,红外模块发送端采用的是HT6221的编码芯片,而在接收端由于红外对管,生成的正好是与发送端电平相反的信号。要实现解码模块程序编写,需先知道协议的过程以及编码的格式,还有模块的输入输出。
1. HT6221介绍
是一款基于红外遥控协议(NEC协议)的遥控编码芯片。一共24个引脚,其PIN定义如下:
When one of the keys (32 or 64 keys) is triggered for over 36ms, the oscillator is enabled and the chip is activated. If the key is pressed and held for 108ms or less, the 108ms transmission codes are enabled and comprised of a header code (9ms), an off code (4.5ms), low byte address codes (9ms~18ms), high byte address codes (9ms~18ms), 8-bit data codes (9ms~18ms), and the inverse codes of the 8-bit data codes (18ms~9ms). After the pressed key is held for 108ms, if the key is still held down, the transmission codes turn out to be a composition of header (9ms) and off codes (2.5ms) only.
To avoid mistakes made by keyboard scanning or simultaneous two-key inputs (except for the three double-key active functions (K21+K22, K21+K23, and K21+K24), the HT6221/HT6222 are facilitated with 36ms starting time. The HT6221/HT6222 also provide three double-key active functions (K21+K22, K21+K23, and K21+K24) for tape deck recording operations.
The double-key operation rules are shown in timing 4 and timing 6. Transmission Codes The transmission codes of the HT6221/HT6222 consist of a 9ms header code, a 4.5ms off code, 16-bit address codes (18ms~36ms), 9ms~18ms 8-bit data codes, and the inverse code of the 8-bit data codes. The following is an illustration of the transmission codes
如上,NEC协议主要时发送9ms的高电平,然后4.5ms的低电平,然后是地址和数据。其中地址和数据的1,0采用PPM编码,即用不同时长的高低电平表示二进制1,0。
2. 红外解码模块设计
模块接口如下,因为实际接收时,接收头接收到的信号后输出的波形刚好与编码产生的波形相反。因此需要decode模块将这个相反的时序识别出来。
1)计数模块
这里需要产生9ms,4.5ms,0.56ms,1.69ms这四个时间点判断,但由于不同硬件电路,晶振,不同实际场景下这个时间可能不会这么精确,因此需要以包含这个时间前后一段范围的时间段进行判断。
2)代码
整个NEC协议,在识别过程中主要是根据上面的时间点判断协议目前在那个状态,如果在数据传输状态则根据高低电平时间判断传输的是1还是0,然后将数据转换后输出。NEC协议只有4个状态,但是在数据传输这个状态下输出和判断信息比较复杂,所以用三段式的状态机实现。
module decoder_top(
input clk,rst_n,
input iIR,
output wire [15:0]irData,
output wire [15:0]irAddr,
output reg end_flag);
reg [19:0]tim_cnt;
reg tim_clear;
reg [3:0]main_state;
parameter IDLE =4'b0000,
ST_high=4'b0001,
ST_low =4'b0010,
ST_data=4'b0100;
reg data_done;
reg [6:0] data_cnt;
reg [31:0]data_r;
assign irAddr=data_r[15:0];
assign irData=data_r[23:16];
/*************************************
parameter time_9ms=20'd900000;
parameter time_4_5ms=20'd450000;
parameter time_0_56ms=20'd56000;
parameter time_1_68ms=20'd168000;
*************************************/
reg [1:0]iIR_r;
wire iIR_pos_flag,iIR_neg_flag;
assign iIR_pos_flag = (~iIR_r[1] & iIR_r[0])?1'b1:1'b0;
assign iIR_neg_flag = (iIR_r[1] & (~iIR_r[0]))?1'b1:1'b0;
always@(posedge clk or negedge rst_n)begin
if(~rst_n) iIR_r<=0;
else begin
iIR_r[0]<=iIR;
iIR_r[1]<=iIR_r[0];
end
end
always@(posedge clk or negedge rst_n)begin
if(~rst_n) tim_cnt<=0;
else if(tim_clear) tim_cnt<=0;
else tim_cnt<=tim_cnt+1'b1;
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
main_state<=IDLE;
tim_clear<=1;
end
else if(tim_cnt<20'd500000) begin
case (main_state)
IDLE:begin
if(iIR_neg_flag) begin
main_state<=ST_high;
tim_clear<=1;
end
else begin
main_state<=IDLE;
tim_clear<=0;
end
end
ST_high:begin
if (iIR_pos_flag)begin
if (tim_cnt>20'd325000 && tim_cnt<20'd495000) begin
main_state<=ST_low;
tim_clear<=1;
end
else begin
main_state<=IDLE;
tim_clear<=0;
end
end
else begin
main_state<=ST_high;
tim_clear<=0;
end
end
ST_low:begin
if(iIR_neg_flag)begin
if(tim_cnt>20'd152500 &&tim_cnt<20'd277500)begin
main_state<=ST_data;
tim_clear<=1;
end
else begin
main_state<=IDLE;
tim_clear<=0;
end
end
else begin
main_state<=ST_low;
tim_clear<=0;
end
end
ST_data:begin
if(data_done) begin
main_state<=IDLE;
tim_clear<=0;
end
else if(iIR_pos_flag)begin
if(tim_cnt>20'd20000 && tim_cnt<20'd35000)begin
tim_clear<=1;
end
else begin
main_state<=IDLE;
tim_clear<=0;
end
end
else if(iIR_neg_flag)begin
if((tim_cnt>20'd20000 && tim_cnt<20'd35000) || (tim_cnt>20'd75000 && tim_cnt<20'd90000))begin
tim_clear<=1;
end
else begin
main_state<=IDLE;
tim_clear<=0;
end
end
else begin
main_state<=ST_data;
tim_clear<=0;
end
end
endcase
end
end
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data_done<=0;
data_cnt<=0;
data_r<=0;
end_flag<=0;
end
else if (main_state==IDLE)begin
data_done<=0;
end_flag<=0;
data_r<=0;
end
else if (main_state==ST_data) begin
if(data_cnt==7'd32) begin
data_cnt<=0;
data_done<=1;
end_flag<=1;
end
else if(iIR_neg_flag )begin
if(tim_cnt>20'd20000 && tim_cnt<20'd35000) data_r[data_cnt]<=0;
if(tim_cnt>20'd75000 && tim_cnt<20'd90000) data_r[data_cnt]<=1;
data_cnt<=data_cnt+1'b1;
end
end
end
endmodule