本帖最后由 正点原子运营 于 2023-11-27 16:56 编辑
第二十六章 红外遥控实验
1)实验平台:正点原子 ATK-DFPGL22G开发板
2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0
6)FPGA技术交流QQ群:435699340
红外遥控是一种无线、非接触控制技术,具有抗干扰能力强、信息传输可靠、功耗低、易实现等显著特点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。本章我们将使用ATK-DFPGL22G FPGA开发板接收红外遥控器发出的红外信号,并将数据显示在数码管上,如果监测到重复码,则通过LED灯闪烁指示。 本章包括以下几个部分: 1.1简介 1.2实验任务 1.3硬件设计 1.4程序设计 1.5下载验证
1.1 简介红外遥控是一种无线、非接触控制技术,由于它不具有像无线电遥控那样可以穿过障碍物去控制被控对象的能力,所以同类产品的红外遥控器,可以有相同的遥控频率或编码,而不会隔墙控制或干扰邻居的家用电器,这对于大批量生产以及在家用电器上普及红外遥控器提供了极大的方便。红外遥控器发射出的实际上是一种红外光(红外线),其波长范围在1mm到760nm之间,而人眼可见光的波长范围一般在400nm到760nm之间,所以我们并不能看到红外遥控器发出的红外光,因此对环境的影响很小,也不会影响临近的无线电设备。 红外遥控器的编码目前广泛使用的是:NEC协议和Philips RC-5协议。ATK-DFPGL22G FPGA开发板配套的遥控器使用的是NEC协议,其逻辑电平编码格式如图 26.1.1所示。 NEC协议采用PPM调制(Pulse Position Modulation,脉冲位置调制)的形式进行编码,数据的每一位(Bit)脉冲长度为560us,由38KHz的载波脉冲(carrier burst)进行调制,推荐的载波占空比为1/3至1/4。由上图可知,有载波脉冲的地方,其宽度都为560us,而载波脉冲的间隔时间是不同的。逻辑“1”的载波脉冲+载波脉冲间隔时间为2.25ms;逻辑“0”的载波脉冲+载波脉冲间隔时间为逻辑“1”的一半,也就是1.125ms。 图 26.1.2为NEC协议的数据传输格式。由图可知,传输数据时低位在前,图中的地址码(Address)为0x59,控制码(Command)为0x16。一个信息的发送由9ms的AGC(自动增益控制)载波脉冲开始,用于在早期的IR红外接收器中设置增益;紧接着是4.5ms的空闲信号;随后是地址码和控制码。地址码和控制码分别传输了两次,第二次传输的地址码和控制码都是反码,用于对地址码和控制码做校验,当然,也可以直接忽略地址码反码和控制码反码。每次信息都是按照同步码(9ms载波脉冲+4.5ms空闲信号)、地址码、地址反码、控制码和控制反码的格式进行传输,因此,单次信息传输的时间是固定不变的。 当红外遥控器上的按键被一直按下时,红外遥控器只会发送一次完整的信息,其后会每隔110ms发送一次重复码(也叫连发码)。重复码的数据格式比较简单,同样是由9ms的AGC(自动增益控制)载波脉冲开始,紧接着是2.25ms的空闲信号,随后是560us的载波脉冲,重复码的数据格式如图 26.1.3和图 26.1.4所示。 以上部分是对NEC协议的介绍,也就是红外遥控器发送数据时所遵循的协议规范,接下来我们了解下开发板板载的红外接收头,其型号为HS0038B,实物图和结构框图如图 26.1.5和图 26.1.6所示。 红外接收头通常被厂家集成在一个元件中,成为一体化红外接收头。内部集成了红外监测二极管、自动增益放大器(AGC)、带通滤波器(Band Pass)、解调器(Demodulator)等电路。红外遥控器发出的信息经38KHz的载频进行二级调制以提高发射效率,达到降低电源功率的目的,然后再经过红外发射二极管产生红外线向空间中发射。红外接收头通过红外监测二极管,将光信号转换成电信号,经过电路调制之后,最终输出可以被FPGA采集的TTL电平信号。这里要注意的一点是,红外接收头内部的三极管电路具有信号反向的功能,也就是将1变为0,0变为1,那么上面的整个协议则电平反过来接收。9ms本来是高电平,那么将变为低电平,以此类推如图 26.1.7所示,接收解码对应的波形是FPGA最终接收到的红外信号。 下图为红外解码接收到的完整波形。 从图 26.1.8可以看到,地址码为0,控制码为0x15。在一段时间之后,我们还可以收到几个脉冲,这就是NEC协议规定的重复码(连发码),如果一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,可以通过统计重复码来标记按键按下的长短/次数,下图给出了我们附带的遥控器每个按键的键码。 下面是红外遥控的按键对应的键码值:
1.2 实验任务本节实验任务是使用ATK-DFPGL22G FPGA开发板接收红外遥控器发出的红外信号,并将数据显示在数码管上;如果监测到重复码,则通过LED灯闪烁指示。
1.3 硬件设计HS0038电路原理图如图 26.3.1所示,图中REMOTE_IN信号为红外接收头的电平输出端。 本实验的管脚分配如下表所示
对应的fdc文件如下所示: 1.4 程序设计根据实验任务可大致规划出控制流程,红外驱动模块解析红外数据,将控制码输出至数码管驱动模块,重复码有效信号输出至LED控制模块。数码管驱动模块将对应的位选和段选信号发送至数码管,使相应的数字显示在数码管上,LED控制模块根据重复码信号控制LED灯的亮灭。系统框图如下所示。 FPGA顶层(top_remote_rcv)例化了以下两个模块:红外驱动模块(remote_rcv)和数码管动态显示模块(seg_led),实现各模块间信号的交互。 顶层模块代码如下: - 1 module top_remote_rcv(
- 2 input sys_clk , //系统时钟
- 3 input sys_rst_n, //系统复位信号,低电平有效
- 4 input remote_in, //红外接收信号
- 5 output [5:0 sel , //数码管位选信号
- 6 output [7:0 seg_led , //数码管段选信号
- 7 output led //led灯
- 8 );
- 9
- 10 //wire define
- 11 wire [7:0 data ;
- 12 wire repeat_en ;
- 13
- 14 //*****************************************************
- 15 //** main code
- 16 //*****************************************************
- 17
- 18 //数码管显示模块
- 19 seg_led u_seg_led(
- 20 .clk (sys_clk),
- 21 .rst_n (sys_rst_n),
- 22 .sel (sel),
- 23 .seg_led (seg_led),
- 24 .data (data), //红外数据
- 25 .point (6'd0), //无小数点
- 26 .en (1'b1), //使能数码管
- 27 .sign (1'b0) //无符号显示
- 28 );
- 29
- 30 //HS0038B驱动模块
- 31 remote_rcv u_remote_rcv(
- 32 .sys_clk (sys_clk),
- 33 .sys_rst_n (sys_rst_n),
- 34 .remote_in (remote_in),
- 35 .repeat_en (repeat_en),
- 36 .data_en (),
- 37 .data (data)
- 38 );
- 39
- 40 led_ctrl u_led_ctrl(
- 41 .sys_clk (sys_clk),
- 42 .sys_rst_n (sys_rst_n),
- 43 .repeat_en (repeat_en),
- 44 .led (led)
- 45 );
- 46
- 47 endmodule
复制代码顶层模块完成对其他模块的例化,红外驱动模块输出的控制码(data)连接至数码管显示模块,输出的repeat_en(重复码有效信号)连接至LED控制模块。 由本章简介部分介绍的红外传输时序可以发现,红外传输时序非常适合使用状态机来编写。红外驱动模块状态跳转图如下图所示。 红外驱动模块使用三段式状态机来解析红外遥控信号,从上图可以比较直观的看到每个状态实现的功能以及跳转都下一个状态的条件。由于一次完整的红外信息和重复码都是以同步码(9ms的低电平)开始,其空闲信号高电平的时间是不一样的,一次完整的红外信息空闲信号高电平时间是4.5ms,而重复码的空闲信号高电平时间是2.25ms。所以我们在st_start_judge状态判断空闲信号高电平的时间,如果时间是4.5ms,则跳转到st_rec_data状态;如果时间是2.25ms,则跳转到st_repeat状态。 红外驱动模块部分代码如下: 在代码第47行开始的always语句块中,我们对输入的50MHz的时钟进行分频,得到一个周期为0.125ms(8KHz)的时钟,即以8Khz的时钟对红外信号进行采样。这里之所以对时钟进行分频,是因为红外信号接收的过程用时较长,如果使用50Mhz的时钟采样,内部定义的计数器位宽会比较大,所以我们对输入的时钟做了分频的处理,当然分频得到其它频率的时钟也是可以的。 代码中使用三段式状态机对红外信号进行解析。状态机默认是在st_idle(空闲)状态,并且此时time_cnt_clr的值为1,即time_cnt计数器停止计时;当监测到remote_in_d0为低电平之后,time_cnt_clr的值为0,time_cnt计数器开始计时,此时状态机跳转到st_start_low_9ms状态,在这里主要向大家介绍下程序是如何对9ms低电平的同步码进行计数的。在代码的第160行,当检测到pos_remote_in(红外信号上升沿)为高电平时,说明此时红外信号拉高,即同步码低电平结束,此时判断time_cnt的值是否接近9ms,如果接近9ms,此时开始跳转到st_start_judge状态,否则跳转到空闲状态。程序后面对空闲信号、重复码以及数据的检测方法类似,在此不再赘述。 LED控制模块代码如下: - 1 module led_ctrl(
- 2 input sys_clk , //系统时钟
- 3 input sys_rst_n , //系统复位信号,低电平有效
- 4
- 5 input repeat_en , //重复码触发信号
- 6 output reg led //LED灯
- 7 );
- 8
- 9 //reg define
- 10 reg repeat_en_d0 ; //repeat_en信号打拍采沿
- 11 reg repeat_en_d1 ;
- 12 reg [22:0 led_cnt ; //LED灯计数器,用于控制LED灯亮灭
- 13
- 14 //wire define
- 15 wire pos_repeat_en;
- 16
- 17 //*****************************************************
- 18 //** main code
- 19 //*****************************************************
- 20
- 21 assign pos_repeat_en= ~repeat_en_d1 & repeat_en_d0;
- 22
- 23 ////repeat_en信号打拍采沿
- 24 always @(posedge sys_clk or negedge sys_rst_n) begin
- 25 if(!sys_rst_n) begin
- 26 repeat_en_d0 <= 1'b0;
- 27 repeat_en_d1 <= 1'b0;
- 28 end
- 29 else begin
- 30 repeat_en_d0 <= repeat_en;
- 31 repeat_en_d1 <= repeat_en_d0;
- 32 end
- 33 end
- 34
- 35 always @(posedge sys_clk or negedge sys_rst_n) begin
- 36 if(!sys_rst_n) begin
- 37 led_cnt <= 23'd0;
- 38 led <= 1'b0;
- 39 end
- 40 else begin
- 41 if(pos_repeat_en) begin
- 42 led_cnt <= 23'd5_000_000; //单次重复码:亮80ms 灭20ms
- 43 led <= 1'b1; //led亮的时间:4_000_000*20ns=80ms
- 44 end
- 45 else if(led_cnt != 23'd0) begin
- 46 led_cnt <= led_cnt - 23'd1;
- 47 if(led_cnt < 23'd1_000_000) //led灭的时间:1_000_000*20ns=20ms
- 48 led <= 1'b0;
- 49 end
- 50 end
- 51 end
- 52
- 53 endmodule
复制代码LED控制模块代码比较简单,首先检测repeat_en信号的上升沿(如代码的第24行开始的always所示),pos_repeat_en拉高之后,计数器赋值为5_000_000,随后计数器每个周期开始递减1,直到计数到0;在计数器在1_000_000~5_000_000范围内,点亮LED灯,其它情况熄灭LED灯,从而指示红外遥控模块是否检测到重复码。
1.5 下载验证首先我们将下载器与JTAG接口连接,下载器另外一端与电脑连接,连接电源线,打开电源开关,然后回到下载界面,将生成好的sbit流文件下载到板子中去,然后使用红外遥控器按不同的按钮,板子上的数码管会显示对应的键码值,这就说明本次红外遥控实验是成功的。在红外遥控器上按下按键1,数码管上显示键码值22,如下图所示: |