大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。
今天给大侠带来基于FPGA的红外线遥控系统设计,附源码,获取源码,请在“FPGA技术江湖”公众号内回复“红外线遥控系统设计源码”,可获取源码文件。话不多说,上货。
前言
红外线(Infrared)是波长介乎微波与可见光之间的电磁波,波长在760纳米(nm)至1毫米(mm)之间,比红光长的非可见光。 红外线遥控是目前使用最广泛的一种通信和遥控手段。由于红外线遥控装置具有体积小、功耗低、功能强、成本低等特点,因而,继彩电、录像机之后,在录音机、音响设备、空凋机以及玩具等其它小型电器装置上也纷纷采用红外线遥控。现在工业设备中,也已经广泛在使用。
设计原理
红外遥控系统主要由红外的发送装置和接收装置组成,发送装置可由按键,编码模块,发射电路等组成,接收装置由红外接收电路,遥控,解码模块等组成,此次设计我们用到的硬件平台式是Altera的DE1_SOC,晶振为50MHZ。
在红外的编码中,我们对1 和 0 的编码是通过38KHZ的脉冲来定义的,在红外的的编码中每个脉冲的为256.25us长的38KHZ载波频率(26.3us),对0,1的脉冲的定义的时间如下图:
红外的数据格式为包括引导码,用户码,数据码和数据纠错码,停止位编码总为32位。数据反码是数据码反相后的编码,可用于对数据的纠错。此外第二段的用户码可以在遥控应用电路中设置为第一段用户码的反码。
数据格式如下图:
一帧数据在发送时先发送9MS的高电平,然后发送4.5MS的低电平的起始位,然后发送用户码,数据码,数据反码。然后再发送一位的停止位。不发送数据时数据线一直为低。
发送的时序图如下:
接受的时,接收到的时序和发送的时序恰恰相反,如发送时先发送9ms的高,4.5ms的低,接收为接收9ms的低电平,4.5ms低电平。
接收的控制器我们用的时红外遥控装置,按键发送的数据如下图所示:
设计框架
设计的总框架如下图:
在设计中分频模块提供所需要的38KHZ的时钟,当按键按下时发送我们的发送模块发送一个给定的数值,我用户码为8'b0,第二段用户码为8'hff,然后发送给定的数据码,和数据反码。上电后我们的设计会发一次我们给定的数据码,然后在接受模块会接受到其发送的数据并在数码管上显示出来,之后我们可以用我们我的遥控键盘来发送数据,接收模块接收显示出来,通过验证我们接收和发送的正确。
设计代码
顶层模块infrared代码:
module infrared(clk, rst_n, key, tx, seg1, seg2, rx);input clk, rst_n;input key;output tx;input rx;wire [7:0] show_data;output [7:0] seg1,seg2;wire [31:0] data_n;wire clk_38khz;clk_frep clk_frep_dut(.clk(clk),.rst_n(rst_n),.clk_38khz(clk_38khz));encode encode_dut(.clk(clk_38khz),.rst_n(rst_n),.d_out(data_n),.key(key));tttxxx tx_dut(.clk(clk_38khz),.rst_n(rst_n),.data_n(data_n),.tx(tx),.key(key));seg seg01(.clk(clk),.rst_n(rst_n),.seg7(seg1),.data_in(show_data[3:0]));seg seg02(.clk(clk),.rst_n(rst_n),.seg7(seg2),.data_in(show_data[7:4]));rx_led led_dut(.clk(clk_38khz),.rst_n(rst_n),.rx(rx),.show_data(show_data));endmodule
发送模块tttxxx代码:
module tttxxx(clk, rst_n, data_n, tx, key);input clk, rst_n;input key;input [31:0] data_n;output reg tx;reg [31:0] temp;reg [8:0] count;reg [2:0] state;reg data;reg [13:0] num;reg [31:0] d_data;parameter T9ms = 342; //9000/26.3;parameter T4500us = 171; //4500/26.3;parameter T0 = 21; //(1125-562.25)/26.3;parameter T1 = 63; //(2250-562.25)/26.3;parameter T562us = 21; //562.25/26.3;parameter T = 2666;reg T9_flag;reg T45_flag;reg T0_flag;reg T1_flag;reg T9_down;reg T45_down;reg T0_down;reg T1_down;reg [9:0] cnt9;reg [9:0] cnt45;reg [9:0] cnt0;reg [9:0] cnt1;reg [9:0] cnt562;reg t0_clk, t1_clk;always @ (posedge clk)if(!rst_n)begincount <= 0;state <= 0;tx <= 0;data <= 0;num <= 0;temp <= 0;d_data <= {8'b0,8'hff, 8'b10100010, 8'b01011101};endelsecase (state)0 : if(count < 10)begintx <= 0;count <= count + 1;endelse if(!key)begincount <= 0;state <= 1;T9_flag <= 1;tx <= 1;end1 : if(T9_down)beginstate <= 2;T45_flag <= 1;tx <= 0;T9_flag <= 0;endelsebegintx <= 1;state <= 1;end2 : if(T45_down)beginstate <= 3;tx <= 0;T45_flag <= 0;endelsetx <= 0;3 : if(count < 32)begincount <= count + 1;if(!d_data[31 - count])beginT0_flag <= 1;state <= 4;T1_flag <= 0;endelsebeginT1_flag <= 1;state <= 5;T0_flag <= 0;endendelsebegincount <= 0;state <= 6;T0_flag <= 0;T1_flag <= 0;end4 : if(T0_down)beginstate <= 3;tx <= 0;endelsebegintx <= t0_clk;end5 : if(T1_down)beginstate <= 3;tx <= 0;endelsetx <= t1_clk;6 : if(count < T562us - 1)begincount <= count + 1;tx <= 1;endelsebegintx <= 0;enddefault: state <= 0;endcasealways @ (posedge clk)if(!rst_n)beginT9_down <= 0;cnt9 <= 0;endelse if (T9_flag)beginif(cnt9 < T9ms - 1)beginT9_down <= 0;cnt9 <= cnt9 + 1;endelsebeginT9_down <= 1;cnt9 <= 0;endendalways @ (posedge clk)if(!rst_n)beginT45_down <= 0;cnt45 <= 0;endelse if (T45_flag)beginif(cnt45 < T4500us - 1)beginT45_down <= 0;cnt45 <= cnt45 + 1;endelsebeginT45_down <= 1;cnt45 <= 0;endendreg [9:0] cnt00;always @ (posedge clk)if(!rst_n)begint0_clk <= 0;T0_down <= 0;cnt0 <= 0;cnt00 <= 0;endelse if (T0_flag)beginif(cnt0 < T562us - 1)begint0_clk <= 1;cnt0 <= cnt0 + 1;T0_down <= 0;endelsebeginif(cnt00 < T0 - 1)begincnt00 <= cnt00 + 1;t0_clk <= 0;T0_down <= 0;endelsebeginT0_down <= 1;cnt0 <= 0;cnt00 <= 0;endendendreg [9:0] cnt11;always @ (posedge clk)if(!rst_n)begint1_clk <= 0;T1_down <= 0;cnt1 <= 0;cnt11 <= 0;endelse if (T1_flag)beginif(cnt1 < T562us - 1)begint1_clk <= 1;cnt1 <= cnt1 + 1;T1_down <= 0;endelsebeginif(cnt11 < T1 - 1)begincnt11 <= cnt11 + 1;t1_clk <= 0;T1_down <= 0;endelsebeginT1_down <= 1;cnt1 <= 0;cnt11 <= 0;endendendendmodule
接收模块rx_led代码:
0 module rx_led(clk, rst_n, rx, show_data);12 input clk, rst_n;3 input rx;4 output reg [7:0] show_data;56 reg [1:0] state;7 reg [7:0] cnt;8 reg temp;9 reg [9:0] num;10 reg flag;11 reg [31:0] data;12 reg [1:0] state_s;13 reg flag_x;14 reg [12:0] count;1516 parameter T = 2566; // 一帧数据的时间1718 //这个模块是中因为接受的32位编码数据中,不管是位0还是位1,接受的低电平都是相同的,19 //我们可以通过来判断高电平的时间来确定为位1 还是位0,位’1‘ 1.68MS,位0 562.25us20 always @ (posedge clk)21 if(!rst_n)22 begin23 num <= 0;24 data <= 0;25 state_s <= 0;26 flag_x <= 0;27 count <= 0;28 end29 else30 begin31 case (state_s)32 0 : if(!rx) //判断起始位,是否接受=收数据33 begin34 state_s <= 1;35 flag_x <= 0;36 count <= count + 1;37 end38 else39 begin40 flag_x <= 0;41 state_s <= 0;42 count <= count + 1;43 end4445 1 : if(num < (342 + 171 - 1)) //延迟9ms + 4.5ms的起始时间46 begin47 num <= num + 1;48 state_s <= 1;49 count <= count + 1;50 end51 else52 begin53 num <= 0;54 state_s <= 2;55 count <= count + 1;56 end5758 2 : if(flag && num < 32) //flag来的时候表示接到了位1 ,或者位0,59 //通过移位寄存器来获取32位数据60 begin61 data <= {data[30:0],temp};62 state_s <= 2;63 num <= num + 1;64 count <= count + 1;65 end66 else if(num == 32)67 begin68 state_s <= 3;69 num <= 0;70 count <= count + 1;71 end72 else73 state_s <= 2;7475 3 : if(num < 21 - 1) //延迟结束位的时间76 begin77 num <= num + 1;78 count <= count + 1;79 end80 else81 begin82 if(count == T - 1) //延迟一帧数据的时间后,发送一个标志位83 begin84 num <= 0;85 state_s <= 0;86 flag_x <= 1;87 count <= 0;88 count <= count + 1;89 end90 else91 count <= count + 1;92 end93 default: state_s <= 0;94 endcase95 end9697 always @ (posedge clk)98 if(!rst_n)99 begin100 cnt <= 0;101 state <= 0;102 temp <= 0;103 flag <= 0;104 end105 else106 if(state_s > 1 && state_s < 3)107 case (state)108 0 : if(rx)109 begin110 cnt <= cnt + 1;111 state <= 1;112 flag <= 0;113 end114 else115 begin116 state <= 0;117 flag <= 0;118 end119120 1 : if(!rx)121 begin122 cnt <= cnt;123 state <= 2;124 end125 else126 cnt <= cnt + 1;127128 2 : if(400 < cnt * 26 && cnt * 26 < 600) //判断高电平的时间129 begin130 temp <= 0;131 flag <= 1;132 state <= 0;133 cnt <= 0;134 end135 else if (1400 < cnt * 26 && cnt * 26 < 1700) //判断高电平的时间136 begin137 temp <= 1;138 flag <= 1;139 state <= 0;140 cnt <= 0;141 end142 else143 begin144 state <= 0;145 cnt <= 0;146 end147 default: state <= 0;148 endcase149150 always @ (*) //接收完一帧数据后,当标志位来的时候通过对数据的纠错来捕获数据151 //我们接收的数据用的是左移,而发送的时候先发的是低位152 if(!rst_n)153 show_data <= 0;154 else if((data[7:0] == ~data[15:8]) && (data[31:24] == ~data[23:16]) && flag_x)155 begin156 show_data[0] <= data[15];157 show_data[1] <= data[14];158 show_data[2] <= data[13];159 show_data[3] <= data[12];160 show_data[4] <= data[11];161 show_data[5] <= data[10];162 show_data[6] <= data[9];163 show_data[7] <= data[8];164165 end166 else167 show_data <= show_data;168169 endmodule
数码管seg模块:
0 module seg(clk, rst_n, seg7, data_in);12 input clk;3 input rst_n;4 input [3:0] data_in;56 output reg [7:0] seg7;789 `define T1ms 50_000 //分频出1k的时钟10 //`define T1ms 511 reg [15:0] count;12 reg flag;13 always @ (posedge clk or negedge rst_n)14 if(!rst_n)15 begin16 count <= 15'b0;17 flag <= 1;18 end19 else20 if(count == `T1ms /2 - 1)21 begin22 count <= 15'b0;23 flag <= ~flag;24 end25 else26 begin27 count <= count + 1'b1;28 end2930 always @ (posedge flag)31 if(!rst_n)32 seg7 <= 8'b1010_0100;33 else34 begin35 case (data_in)36 0:seg7 <= 8'b1100_0000;37 1:seg7 <= 8'b1111_1001;38 2:seg7 <= 8'b1010_0100;39 3:seg7 <= 8'b1011_0000;40 4:seg7 <= 8'b1001_1001;41 5:seg7 <= 8'b1001_0010;42 6:seg7 <= 8'b1000_0010;43 7:seg7 <= 8'b1111_1000;44 8:seg7 <= 8'b1000_0000;45 9:seg7 <= 8'b1001_0000;46 10:seg7 <= 8'b1000_1000;47 11:seg7 <= 8'b1000_0011;48 12:seg7 <= 8'b1100_0110;49 13:seg7 <= 8'b1010_0001;50 14:seg7 <= 8'b1000_0110;51 15:seg7 <= 8'b1000_1110;52 default:;53 endcase54 end5556 endmodule
分频模块clk_frep代码:
0 module clk_frep(clk, rst_n, clk_38khz);12 input clk, rst_n;3 output reg clk_38khz;45 reg [9:0] count;67 //分频出红外模块所用的38Khz的时钟8 //也可以用占空比为1:3的38khz的时钟910 always @ (posedge clk or negedge rst_n)11 if(!rst_n)12 begin13 count <= 0;14 clk_38khz <= 1;15 end16 else if(count == (50_000_000 / 38000 / 2 - 1))17 begin18 clk_38khz <= ~clk_38khz;19 count <= 0;20 end21 else22 count <= count + 1'd1;2324 endmodule
仿真测试
测试模块infrared_tb代码:
0 `timescale 1ns/1ps12 module infrared_tb();34 reg clk, rst_n;5 reg key;6 wire tx;7 wire [7:0] show_data;89 //因为我们代码中只发送一次数据,所以可以把key一直拉低1011 initial begin12 clk = 1;13 rst_n = 0;14 key = 1;1516 #100.1 rst_n = 1;1718 #200 key = 0;1920 end2122 always # 10 clk = ~clk;2324 infrared dut(25 .clk(clk),26 .rst_n(rst_n),27 .key(key),28 .tx(tx),29 .rx(rx),30 .seg1(seg1),31 .seg2(seg2)32 );3334 endmodule
仿真图:
仿真中我们可以把数码管模块的计数器的值改小一点,便于仿真。
如图中所示的我们发的是32’h00ffa25d,那么数据为是8’b1010_0010,那么先发送时就时就按下面的序列开始0100_0101接收到的为45,所以工程正确。
558