红外解码编码学习----verilog
在设计中运用红外遥控器可以很好的解决按键缺少的问题,还可以方便的控制产品。
红外发射部分:
红外发射管:
判断红外发射管的好坏
:
电路原理图:
接收部分:
传输的NEC协议:
本实验电路:
verilog 程序:
发送程序:
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :IR_send.v ** CreateDate :2015.06 ** Funtions :红外信号的发送程序,发送格式:引导码+8bit用户码+8bit用户反码(或者用户反码)+8bit数据码+8bit用户反码+结束码 ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: *******************************************************************************/ module IR_send ( clk, rst_n, key_1, key_2, IR_out, led_1, led_2, led_3, led_4 // testdata ); input clk; //24M/20m input rst_n; input key_1; input key_2; output IR_out; output reg led_1; output reg led_2; output led_3; output led_4; // output [7;0] testdata; //-------------------// `define CLK_20M // `define CLK_24M // `define CLK_50M `ifdef CLK_20M parameter t_38k = 10'd526; parameter t_38k_half = 10'd263; parameter t_9ms = 18'd179999; parameter t_4_5ms = 17'd89999; parameter t_13_5ms = 19'd269999; parameter t_560us = 14'd11199; parameter t_1_12ms = 15'd22399; parameter t_1_68ms = 16'd33599; parameter t_2_24ms = 16'd44799; `endif `ifdef CLK_24M parameter t_38k = 10'd630; parameter t_38k_half = 10'd315; parameter t_9ms = 18'd215999; parameter t_4_5ms = 17'd107999; parameter t_13_5ms = 19'd323999; parameter t_560us = 14'd13439; parameter t_1_12ms = 15'd26879; parameter t_1_68ms = 16'd40319; parameter t_2_24ms = 16'd53759; `endif `ifdef CLK_50M parameter t_38k = 11'd1315; parameter t_38k_half = 10'd657; parameter t_9ms = 19'd449999; parameter t_4_5ms = 18'd224999; parameter t_13_5ms = 20'd674999; parameter t_560us = 15'd27999; parameter t_1_12ms = 16'd55999; parameter t_1_68ms = 17'd83999; parameter t_2_24ms = 17'd111999; `endif parameter DATA_USER = 8'h00; //---------------------------------// //分频38Khz时钟 reg [10:0] cnt1; wire clk_38k; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt1 <= 0; end else if(cnt1 == t_38k) begin cnt1 <= 0; end else cnt1 <= cnt1 + 1; end assign clk_38k = (cnt1<t_38k_half)?1:0; //-------------------------------------------// // wire key_1_flg; // wire key_2_flg; // key_shake U1( // .clk_100M(clk), // .rst_n(rst_n), // // .key_in(key_1), // .key_out(key_1_flg) // ); // // key_shake U2( // .clk_100M(clk), // .rst_n(rst_n), // // .key_in(key_2), // .key_out(key_2_flg) // ); reg [2:0] key_1_flag; wire key_1_neg; wire key_1_pos; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_1_flag <= 3'b000; end else begin key_1_flag <= {key_1_flag[1:0],key_1}; end end assign key_1_pos = (key_1_flag[2:1]== 2'b01); reg [2:0] key_2_flag; wire key_2_neg; wire key_2_pos; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_2_flag <= 3'b000; end else begin key_2_flag <= {key_2_flag[1:0],key_2}; end end assign key_2_pos = (key_2_flag[2:1] == 2'b01); //------------------------------------------// parameter IDEL = 3'D0; //初始化状态,等待发送命令 parameter START = 3'D1; //开始发送引导码 parameter SEND_USER = 3'D2; //发送用户码 parameter SEND_UNUSER= 3'D3; //发送用户反码 parameter SEND_DATA = 3'D4; //发送数据 parameter SEND_UNDATA= 3'D5; //发送数据反码 parameter FINISH = 3'D6; //发送结束码 parameter FINISH_1 = 3'D7; //发送结束 reg [2:0] state; reg start_en; wire start_over; reg zero_en; wire zero_over; reg one_en; wire one_over; reg finish_en; wire finish_over; reg sendover; reg [7:0] shiftdata; reg [3:0] i; reg [7:0] DATA; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= IDEL; start_en <= 0; zero_en <= 0; one_en <= 0; finish_en <= 0; sendover <= 0; shiftdata <= 0; i <= 0; DATA <= 8'D0; led_1 <= 1; led_2 <= 1; end else begin case(state) IDEL: begin start_en <= 0; zero_en <= 0; one_en <= 0; finish_en <= 0; sendover <= 0; shiftdata <= 0; i <= 0; DATA <= 8'd0; if(key_1_pos) begin state <= START; DATA <= 8'd1; end else if(key_2_pos) begin state <= START; DATA <= 8'd2; end else state <= IDEL; end START: //发送引导码 begin if(start_over) begin start_en <= 0; state <= SEND_USER; shiftdata <= DATA_USER; end else begin start_en <= 1; state <= START; end end SEND_USER: begin // led_3 <= 1; if((i==7)&&(one_over||zero_over)) //结束位 begin i <=0; shiftdata <= ~DATA_USER; state <= SEND_UNUSER; one_en <= 0; zero_en <= 0; end else begin if(zero_over||one_over) //1bit发送结束 begin i <= i + 1; one_en <= 0; zero_en <= 0; end else if(shiftdata[i]) begin one_en <= 1; end else if(!shiftdata[i]) zero_en <= 1; else begin i <= i ; one_en <= one_en; zero_en <= zero_en; end end end SEND_UNUSER: begin led_1 <= ~led_1; if((i==7)&&(one_over||zero_over)) //结束位 begin i <=0; state <= SEND_DATA; shiftdata <= DATA; one_en <= 0; zero_en <= 0; end else begin if(zero_over||one_over) //1bit发送结束 begin i <= i + 1; one_en <= 0; zero_en <= 0; end else if(shiftdata[i]) begin one_en <= 1; end else if(!shiftdata[i]) zero_en <= 1; else begin i <= i ; one_en <= one_en; zero_en <= zero_en; end end end SEND_DATA: begin led_2 <= ~led_2 ; if((i==7)&&(one_over||zero_over)) //结束位 begin i <=0; state <= SEND_UNDATA; shiftdata <= ~DATA; one_en <= 0; zero_en <= 0; end else begin if(zero_over||one_over) //1bit发送结束 begin i <= i + 1; one_en <= 0; zero_en <= 0; end else if(shiftdata[i]) begin one_en <= 1; end else if(!shiftdata[i]) zero_en <= 1; else begin i <= i ; one_en <= one_en; zero_en <= zero_en; end end end SEND_UNDATA: begin if((i==7)&&(one_over||zero_over)) //结束位 begin i <=0; shiftdata <= 0; state <= FINISH; one_en <= 0; zero_en <= 0; end else begin if(zero_over||one_over) //1bit发送结束 begin i <= i + 1; one_en <= 0; zero_en <= 0; end else if(shiftdata[i]) begin one_en <= 1; end else if(!shiftdata[i]) zero_en <= 1; else begin i <= i ; one_en <= one_en; zero_en <= zero_en; end end end FINISH: begin if(finish_over) begin finish_en <= 0; state <= FINISH_1; end else begin finish_en <= 1; state <= FINISH; end end FINISH_1: begin sendover <= 1; state <= IDEL; end default: state <= IDEL; endcase end end //----------------------------------------------// //引导码,9ms载波加4.5ms空闲 reg [19:0] cnt2; wire start_flag; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt2 <= 0; end else if(start_en) begin if(cnt2 >= t_13_5ms) cnt2 <= t_13_5ms+1; else cnt2 <= cnt2 + 1; end else cnt2 <= 0; end assign start_over = (cnt2 == t_13_5ms)?1:0; assign start_flag = (start_en&&(cnt2 <= t_9ms))?1:0; //----------------------------------------------// //比特0, 560us载波 + 560us空闲 reg [15:0] cnt3; wire zero_flag; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt3 <= 0; end else if(zero_en) begin if(cnt3 >= t_1_12ms) cnt3 <= t_1_12ms+1; else cnt3 <= cnt3 + 1; end else cnt3 <= 0; end assign zero_over = (cnt3 == t_1_12ms)?1:0; assign zero_flag = (zero_en&&(cnt3 <= t_560us))?1:0; //----------------------------------------------// //比特1, 560us载波 + 1.68ms空闲 reg [16:0] cnt4; wire one_flag; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt4 <= 0; end else if(one_en) begin if(cnt4 >= t_2_24ms) cnt4 <= t_2_24ms+1; else cnt4 <= cnt4 + 1; end else cnt4 <= 0; end assign one_over = (cnt4 == t_2_24ms)?1:0; assign one_flag = (one_en&&(cnt4 <= t_560us))?1:0; //----------------------------------------------// //结束码, 560us载波 reg [14:0] cnt5; wire finish_flag; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt5 <= 0; end else if(finish_en) begin if(cnt5 >= t_560us) cnt5 <= t_560us+1; else cnt5 <= cnt5 + 1; end else cnt5 <= 0; end assign finish_over = (cnt5 == t_560us)?1:0; assign finish_flag = (finish_en&&(cnt5 <= t_560us))?1:0; //-----------------------------------// wire ir_out; assign ir_out = start_flag||zero_flag||one_flag||finish_flag; assign IR_out = ir_out&&clk_38k; assign led_3 = i[1]; assign led_4 = i[0]; endmodule
接收程序:
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :ir_resive.v ** CreateDate :2015.06 ** Funtions : 中断接收程序。结束数据为:引导码+用户码+用户反码+数据码+数据反码。 ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: *******************************************************************************/ module IR_resive ( clk, rst_n, ir_in, led_error, led_5, led_6, led_7, test_data ); input clk; input rst_n; input ir_in; output led_error; output led_5; output led_6; output led_7; output [7:0] test_data; //---------------------------------------------// reg [2:0] ir_in_reg; wire ir_in_pos; wire ir_in_neg; wire ir_in_change; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin ir_in_reg <= 3'b111; end else begin ir_in_reg <= {ir_in_reg[1:0],ir_in}; end end assign ir_in_pos = (ir_in_reg[2:1] == 2'b01)?1:0; assign ir_in_neg = (ir_in_reg[2:1] == 2'b10)?1:0; assign ir_in_change = ir_in_pos|ir_in_neg; //------------------------------------// //设计分频和计数部分:从1838T的 技术手册中,可以得出最小的脉冲持续时间为500us,在采样时可以对最小的电平采样16次,即对500us采样16次, //则每次的采样间隔时间是 500us/16=31.25us 时钟频率为FCLK = X MHZ, 则最小采样计数为:N =31.25*X, //然后再用一个计数器计数同一电平的采样计数时间。 //最后判断是leader的9ms 还是4.5ms,或是数据的 0 还是 1。 //--------------------------------------------------------// `define FCLK_20M // `define FCLK_24M ` `ifdef FCLK_20M parameter t_31_25us = 10'd625; parameter t_100k = 200; `endif `ifdef FCLK_24M parameter t_31_25us = 10'd750; // parameter t_100k = 240; `endif `ifdef FCLK_50M parameter t_31_25us = 11'd1562; // parameter t_100k = 500; `endif parameter t_low_H = 26; parameter t_low_L = 13; //16 parameter t_high_H=67; //54 1.7MS左右 parameter t_high_L=35 ; parameter t_9ms_H =9'd398; //288 parameter t_9ms_L =9'd278; parameter t_4_5ms_H =9'd154; //144 parameter t_4_5ms_L =9'd134; parameter t_watch = 9'd500; //定时,计数达到500,则已经跑飞, parameter t_1s = 16'd31999; //---------------------------------------------// reg idel_flag; reg [10:0] cnt; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 0; end else if(idel_flag) cnt <= 0; //空闲状态,不再变化 else if(ir_in_change) cnt <= 0; else if(cnt == t_31_25us) cnt <= 0; else cnt<= cnt + 1; end reg [8:0] cnt1; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt1 <= 0; end else if(idel_flag) cnt1 <= 0; else if(ir_in_change) cnt1 <= 0; else if(cnt == t_31_25us) cnt1 <= cnt1 + 1; else cnt1 <= cnt1; end wire t_9ms_flag; wire t_4_5_ms_flag; wire short_flag; //短电平,可是高电平也可以是低电平 wire long_flag; //长电平,肯定是高电平 assign t_9ms_flag = ((cnt1 > t_9ms_L)&&(cnt1 < t_9ms_H)); assign t_4_5_ms_flag = ((cnt1 > t_4_5ms_L)&&(cnt1 < t_4_5ms_H)); assign long_flag = ((cnt1 > t_high_L)&&(cnt1 < t_high_H)); assign short_flag = ((cnt1 > t_low_L)&&(cnt1 < t_low_H)); wire watchdog; assign watchdog = (cnt1 > t_watch)?1:0; //---------------------------------------------// parameter IDEL = 4'd0; parameter L_9MS = 4'd1; parameter L_4_5MS = 4'd2; parameter DATA_R = 4'd4; parameter FINISH_R= 4'd8; reg [3:0] state; reg [31:0] shiftdata; reg [5:0] n; reg error_flag; reg r_over; reg [31:0] rdata; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= IDEL; shiftdata <= 0; n <= 0; error_flag <= 0; rdata <= 0; r_over<= 0; idel_flag <= 0; // led_5 <= 1; // led_6 <= 1; end // else if(watchdog) state <= IDEL; else begin case(state) IDEL: begin idel_flag <= 1; //空闲状态 shiftdata <= 0; n <= 0; error_flag <= 0; r_over<= 0; if(ir_in_reg[1] ==0) begin state <= L_9MS; //检测到拉低数据线 idel_flag <= 0; end else state <= IDEL; end L_9MS: //9ms为低电平,数据线拉高时结束 begin // led_5 <= 0; if(watchdog) state <= IDEL; else if(ir_in_pos) begin if(t_9ms_flag) state <= L_4_5MS; else begin state <= IDEL; error_flag <= 1; end end else state <= L_9MS; end L_4_5MS: begin if(watchdog) state <= IDEL; else if(ir_in_neg) begin if(t_4_5_ms_flag) state <= DATA_R; else begin state <= IDEL; error_flag <= 1; end end else state <= L_4_5MS; end DATA_R: begin // led_6 <= 0; if(watchdog) state <= IDEL; else if((n == 32)&&(ir_in_reg[2:1] == 2'b11)) state <= FINISH_R; else if(ir_in_pos) begin if(short_flag) state <= DATA_R; else begin state <= IDEL; error_flag <= 1; end end else if(ir_in_neg) begin n<= n + 1; if(short_flag) shiftdata[n] <= 0; //从低位到高位依次接收,这样数据的位置是 相反的 else if(long_flag) shiftdata[n] <= 1; //从原来的{用户码,用户反码,数据,数据反码}变为{数据反码,数据,用户反码,用户码} else //所以要调整数据位置 begin state <= IDEL; error_flag <= 1; end end else state <= DATA_R; end FINISH_R: begin r_over <= 1; rdata <= {shiftdata[7:0],shiftdata[15:8],shiftdata[23:16],shiftdata[31:24]}; //调整位置 state <= IDEL; end default:state <= IDEL; endcase end end //---------------------------------------------// assign led_5 = (rdata[15:8] == 8'h01)?0:1; assign led_6 = (rdata[15:8] == 8'h02)?0:1; assign led_7 = (rdata[31:24] == 8'h00)?0:1; assign test_data = rdata[15:8]; // assign test_data = {error_flag,t_4_5_ms_flag,ir_in_change,ir_in_reg[1],state[3:0]}; endmodule
按键消抖(与以前有修改):
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :key_shake.v ** CreateDate :2015.03 ** Funtions : 按键的消抖操作:在复位之后的100us内,不响应按键的操作,在之后有按键按下后,有20ms的延迟,之后输出按键输出. ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved[F]. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : 2015.04 2015.06 ** Modify Content:V1.1:clk-->clk_100M, 常数声明放到一起,便于修改。 增加实用20Mclk,并将输出修改为连续高电平 *******************************************************************************/ module key_shake ( clk_100M, rst_n, key_in, key_out ); input clk_100M; //100Mhz input rst_n; input key_in; output key_out; //-------------------------------------- //在复位之后的100us内,不响应按键的操作 // `define CLK_100M `define CLK_20M `ifdef CLK_100M parameter t_100us = 14'd9999; parameter t1ms = 17'd99999; //定时1ms parameter t_20ms = 5'd20; `endif `ifdef CLK_20M parameter t_100us = 14'd1999; parameter t1ms = 17'd19999; //定时1ms parameter t_20ms = 5'd20; `endif reg [13:0] cnt; reg key_en; //复位之后允许按键输入标志 always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt <= 0; key_en <=0; end else begin if(cnt == t_100us) begin key_en <= 1; end else begin key_en <= 0; cnt <= cnt + 1; end end end //-------------------------------------------------- wire HtoL_flag; //下降沿标志 wire LtoH_flag; //上升沿标志 reg [2:0] key_reg; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin key_reg <= 3'b111; //默认没按下状态为高,按下之后为低.反之则为3'b000; end else begin key_reg <= {key_reg[1:0],key_in}; end end assign HtoL_flag = key_en?(key_reg[2:1] == 2'b10):0; //下降沿检测,一个时钟的高电平 assign LtoH_flag = key_en?(key_reg[2:1] == 2'b01):0; //上升沿检测,一个时钟的高电平 //--------------------------------------------- reg cnt_en; //计数使能标志 reg [16:0] cnt2; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt2 <= 17'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin cnt2 <= 17'd0; end else if(cnt_en) begin cnt2 <= cnt2 + 17'd1; end else cnt2 <= 17'd0; end reg [4:0] cnt3; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt3 <= 5'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin if(cnt3 == t_20ms ) cnt3 <= t_20ms; else cnt3 <= cnt3 + 1; end else if(!cnt_en) cnt3 <= 5'd0; end //---------------------------------- //按键状态机 reg [2:0] i; reg key_down; //按键按下标志 reg key_up; //按键释放标志 always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin key_down <= 0; key_up <= 0; i <= 0; cnt_en <= 0; end else begin case(i) 'd0: begin key_down <= 0; key_up <= 0; if(HtoL_flag) i <= 'd1; //检测到按下 else if(LtoH_flag) i <= 'd2; //检测到释放按键 else i <= 'd0; end 'd1: begin if(cnt3 == t_20ms ) begin if(!key_in) //检测到按键依然被按下 begin key_down <= 1; //按键按下成功 i <= 'd3; cnt_en <= 0; end else begin key_down <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd2: begin if(cnt3 == t_20ms ) begin if(key_in) //检测到按键被释放 begin key_up <= 1; //按键释放成功 i <= 'd4; cnt_en <= 0; end else begin key_up <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd3: begin if(key_in) begin key_down <= 0; i <= 'd0; end else i <= 'd3; end 'd4: begin if(!key_in) begin key_up <= 0; i <= 'd0; end else i <= 'd4; end default:i <= 'd0; endcase end end assign key_out = key_down; //当按键被按下有效时 // assign key_out = key_up; //当按键被释放后才有效时 endmodule
将两个程序合在一起的顶层文件:
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :IR_TOP.v ** CreateDate :2015.06 ** Funtions : 中断的顶层文件 ** Operate on :M5C06N3F256C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: *******************************************************************************/ module IR_TOP ( clk, rst_n, ir_in, ir_out, key_1, key_2, led_d1, led_d2, led_d3, led_d4, led_d5, led_d6, led_d7, led_d8, test_data ); input clk; input rst_n; input key_1; input key_2; input ir_in; output ir_out; output led_d1; output led_d2; output led_d3; output led_d4; output led_d5; output led_d6; output led_d7; output led_d8; output [7:0] test_data; //---------------------------------------------// wire key_1_flag; wire key_2_flag; key_shake U1( .clk_100M(clk), .rst_n(rst_n), .key_in(key_1), .key_out(key_1_flag) ); key_shake U2( .clk_100M(clk), .rst_n(rst_n), .key_in(key_2), .key_out(key_2_flag) ); wire [7:0] data_t; IR_send u1( .clk(clk), .rst_n(rst_n), .key_1(key_1_flag), .key_2(key_2_flag), .IR_out(ir_out), .led_1(led_d1), .led_2(led_d2), .led_3(led_d3), .led_4(led_d4) ); IR_resive u2( .clk(clk), .rst_n(rst_n), .ir_in(ir_in), .led_error(led_d8), .led_5(led_d5), .led_6(led_d6), .led_7(led_d7), .test_data(data_t) ); //---------------------------------------------// assign test_data = data_t; endmodule
仿真图:
接收仿真图:
发送仿真图: