完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html 第二十四章 红外遥控实验 红外遥控是一种无线、非接触控制技术,具有抗干扰能力强、信息传输可靠、功耗低、易 实现等显著特点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统 中。本章我们将使用开拓者FPGA开发板接收红外遥控器发出的红外信号,并将数据显示在数码 管上,如果监测到重复码,则通过LED灯闪烁指示。 本章分为以下几个章节: 24.1 红外遥控简介 24.2 实验任务 24.3 硬件设计 24.4 程序设计 24.5 下载验证 红外遥控简介 红外遥控是一种无线、非接触控制技术,由于它不具有像无线电遥控那样可以穿过障碍物 去控制被控对象的能力,所以同类产品的红外遥控器,可以有相同的遥控频率或编码,而不会 隔墙控制或干扰邻居的家用电器,这对于大批量生产以及在家用电器上普及红外遥控器提供了 极大的方便。红外遥控器发射出的实际上是一种红外光(红外线),其波长范围在1mm到760nm 之间,而人眼可见光的波长范围一般在400nm到760nm之间,所以我们并不能看到红外遥控器发 出的红外光,因此对环境的影响很小,也不会影响临近的无线电设备。 红外遥控器的编码目前广泛使用的是:NEC协议和Philips RC-5协议。开拓者FPGA开发板 配套的遥控器使用的是NEC协议,其逻辑电平编码格式如图 24.1.1所示。 图 24.1.1 NEC协议逻辑电平编码格式 NEC协议采用PPM调制(Pulse Position Modulation,脉冲位置调制)的形式进行编码, 数据的每一位(Bit)脉冲长度为560us,由38KHz的载波脉冲(carrier burst)进行调制,推 荐的载波占空比为1/3至1/4。由上图可知,有载波脉冲的地方,其宽度都为560us,而载波脉 冲的间隔时间是不同的。逻辑“1”的载波脉冲+载波脉冲间隔时间为2.25ms;逻辑“0”的载 波脉冲+载波脉冲间隔时间为逻辑“1”的一半,也就是1.125ms。 图 24.1.2 NEC协议的数据传输格式 图 24.1.2为NEC协议的数据传输格式。由图可知,传输数据时低位在前,图中的地址码 (Address)为0x59,控制码(Command)为0x16。一个信息的发送由9ms的AGC(自动增益控制) 载波脉冲开始,用于在早期的IR红外接收器中设置增益;紧接着是4.5ms的空闲信号;随后是地址码和控制码。地址码和控制码分别传输了两次,第二次传输的地址码和控制码都是反码, 用于对地址码和控制码做校验,当然,也可以直接忽略地址码反码和控制码反码。每次信息都 是按照同步码(9ms载波脉冲+4.5ms空闲信号)、地址码、地址反码、控制码和控制反码的格 式进行传输,因此,单次信息传输的时间是固定不变的。 当红外遥控器上的按键被一直按下时,红外遥控器只会发送一次完整的信息,其后会每隔 110ms发送一次重复码(也叫连发码)。重复码的数据格式比较简单,同样是由9ms的AGC(自 动增益控制)载波脉冲开始,紧接着是2.25ms的空闲信号,随后是560us的载波脉冲,重复码 的数据格式如图 24.1.3和图 24.1.4所示。 图 24.1.3 重复码的数据格式 图 24.1.4 一直发送重复码 以上部分是对NEC协议的介绍,也就是红外遥控器发送数据时所遵循的协议规范,接下来 我们了解下开发板板载的红外接收头,其型号为HS0038B,实物图和结构框图如图 24.1.5和图 24.1.6所示。 图 24.1.5 HS0038B实物图 图 24.1.6 HS0038B结构框图 红外接收头通常被厂家集成在一个元件中,成为一体化红外接收头。内部集成了红外监测 二极管、自动增益放大器(AGC)、带通滤波器(Band Pass)、解调器(Demodulator)等电 路。红外遥控器发出的信息经38KHz的载频进行二级调制以提高发射效率,达到降低电源功率 的目的,然后再经过红外发射二极管产生红外线向空间中发射。红外接收头通过红外监测二极 管,将光信号转换成电信号,经过电路调制之后,最终输出可以被FPGA采集的TTL电平信号。 这里要注意的一点是,红外接收头内部的三极管电路具有信号反向的功能,也就是将1变为0, 0变为1,那么上面的整个协议则电平反过来接收。9ms本来是高电平,那么将变为低电平,以 此类推如图 24.1.7所示,接收解码对应的波形是FPGA最终接收到的红外信号。 图 24.1.7 红外接收解码接收图 下图为红外解码接收到的完整波形。 图 24.1.8 红外解码接收到的完整波形 从图 24.1.8可以看到,地址码为0,控制码为0x15。在一段时间之后,我们还可以收到几 个脉冲,这就是NEC协议规定的重复码(连发码),如果一帧数据发送完毕之后,按键仍然没 有放开,则发射重复码,可以通过统计重复码来标记按键按下的长短/次数。 实验任务 本节实验任务是使用开拓者FPGA开发板接收红外遥控器发出的红外信号,并将数据显示在 数码管上;如果监测到重复码,则通过LED灯闪烁指示。 硬件设计 HS0038电路原理图如图 24.3.1所示,图中REMOTE_IN信号为红外接收头的电平输出端。 图 24.3.1 HS0038B电路原理图 本实验的管脚分配如下表所示 表 24.3.1 红外遥控数码管显示实验管脚分配 程序设计 根据实验任务可大致规划出控制流程,红外驱动模块解析红外数据,将控制码输出至数码 管驱动模块,重复码有效信号输出至LED控制模块。数码管驱动模块将对应的位选和段选信号 发送至数码管,使相应的数字显示在数码管上,LED控制模块根据重复码信号控制LED灯的亮灭。 系统框图如下所示。 图 24.4.1 红外遥控实验系统框图 顶层模块原理图如下所示 图 24.4.2 顶层模块原理图 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控制模块。 由本章简介部分介绍的红外传输时序可以发现,红外传输时序非常适合使用状态机来编写。 红外驱动模块状态跳转图如下图所示。 图 24.4.3 红外驱动模块状态跳转图 红外驱动模块使用三段式状态机来解析红外遥控信号,从上图可以比较直观的看到每个状 态实现的功能以及跳转都下一个状态的条件。由于一次完整的红外信息和重复码都是以同步码 (9ms的低电平)开始,其空闲信号高电平的时间是不一样的,一次完整的红外信息空闲信号 高电平时间是4.5ms,而重复码的空闲信号高电平时间是2.25ms。所以我们在st_start_judge 状态判断空闲信号高电平的时间,如果时间是4.5ms,则跳转到st_rec_data状态;如果时间是 2.25ms,则跳转到st_repeat状态。 红外驱动模块部分代码如下: 1 module remote_rcv( 2 input sys_clk , //系统时钟 3 input sys_rst_n , //系统复位信号,低电平有效 4 5 input remote_in , //红外接收信号 6 output reg repeat_en , //重复码有效信号 7 output reg data_en , //数据有效信号 8 output reg [7:0] data //红外控制码 9 ); 10 11 //parameter define 12 parameter st_idle = 5'b0_0001; //空闲状态 13 parameter st_start_low_9ms = 5'b0_0010; //监测同步码低电平 14 parameter st_start_judge = 5'b0_0100; //判断重复码和同步码高电平(空闲信号) 15 parameter st_rec_data = 5'b0_1000; //接收数据 16 parameter st_repeat_code = 5'b1_0000; //重复码 17 18 //reg define 19 reg [4:0] cur_state ; 20 reg [4:0] next_state ; 21 22 reg [11:0] div_cnt ; //分频计数器 23 reg div_clk ; //分频时钟 24 reg remote_in_d0 ; //对输入的红外信号延时打拍 25 reg remote_in_d1 ; 26 reg [7:0] time_cnt ; //对红外的各个状态进行计数 27 28 reg time_cnt_clr ; //计数器清零信号 29 reg time_done ; //计时完成信号 30 reg error_en ; //错误信号 31 reg judge_flag ; //检测出的标志信号 0:同步码高电平(空闲信号) 1:重复码 32 reg [15:0] data_temp ; //暂存收到的控制码和控制反码 33 reg [5:0] data_cnt ; //对接收的数据进行计数 34 35 //wire define 36 wire pos_remote_in ; //输入红外信号的上升沿 37 wire neg_remote_in ; //输入红外信号的下降沿 38 39 //***************************************************** 40 //** main code 41 //***************************************************** 42 43 assign pos_remote_in = (~remote_in_d1) & remote_in_d0; 44 assign neg_remote_in = remote_in_d1 & (~remote_in_d0); 45 46 //时钟分频,50Mhz/(2*(3124+1))=8khz,T=0.125ms 47 always @(posedge sys_clk or negedge sys_rst_n ) begin 48 if (!sys_rst_n) begin 49 div_cnt <= 12'd0; 50 div_clk <= 1'b0; 51 end 52 else if(div_cnt == 12'd3124) begin 53 div_cnt <= 12'd0; 54 div_clk <= ~div_clk; 55 end 56 else 57 div_cnt = div_cnt + 12'b1; 58 end 59 60 //对红外的各个状态进行计数 61 always @(posedge div_clk or negedge sys_rst_n) begin 62 if(!sys_rst_n) 63 time_cnt <= 8'b0; 64 else if(time_cnt_clr) 65 time_cnt <= 8'b0; 66 else 67 time_cnt <= time_cnt + 8'b1; 68 end 69 70 //对输入的remote_in信号延时打拍 71 always @(posedge div_clk or negedge sys_rst_n) begin 72 if(!sys_rst_n) begin 73 remote_in_d0 <= 1'b0; 74 remote_in_d1 <= 1'b0; 75 end 76 else begin 77 remote_in_d0 <= remote_in; 78 remote_in_d1 <= remote_in_d0; 79 end 80 end 81 82 //状态机 83 always @ (posedge div_clk or negedge sys_rst_n) begin 84 if(!sys_rst_n) 85 cur_state <= st_idle; 86 else 87 cur_state <= next_state ; 88 end 89 90 always @(*) begin 91 next_state = st_idle; 92 case(cur_state) 93 st_idle : begin //空闲状态 94 if(remote_in_d0 == 1'b0) 95 next_state = st_start_low_9ms; 96 else 97 next_state = st_idle; 98 end 99 st_start_low_9ms : begin //监测同步码低电平 100 if(time_done) 101 next_state = st_start_judge; 102 else if(error_en) 103 next_state = st_idle; 104 else 105 next_state = st_start_low_9ms; 106 end 107 st_start_judge : begin //判断重复码和同步码高电平(空闲信号) 108 if(time_done) begin 109 if(judge_flag == 1'b0) 110 next_state = st_rec_data; 111 else 112 next_state = st_repeat_code; 113 end 114 else if(error_en) 115 next_state = st_idle; 116 else 117 next_state = st_start_judge; 118 end 119 st_rec_data : begin //接收数据 120 if(pos_remote_in && data_cnt == 6'd32) 121 next_state = st_idle; 122 else 123 next_state = st_rec_data; 124 end 125 st_repeat_code : begin //重复码 126 if(pos_remote_in) 127 next_state = st_idle; 128 else 129 next_state = st_repeat_code; 130 end 131 default : next_state = st_idle; 132 endcase 133 end 134 135 always @(posedge div_clk or negedge sys_rst_n ) begin 136 if (!sys_rst_n) begin 137 time_cnt_clr <= 1'b0; 138 time_done <= 1'b0; 139 error_en <= 1'b0; 140 judge_flag <= 1'b0; 141 data_en <= 1'b0; 142 data <= 8'd0; 143 repeat_en <= 1'b0; 144 data_cnt <= 6'd0; 145 data_temp <= 32'd0; 146 end 147 else begin 148 time_cnt_clr <= 1'b0; 149 time_done <= 1'b0; 150 error_en <= 1'b0; 151 repeat_en <= 1'b0; 152 data_en <= 1'b0; 153 case(cur_state) 154 st_idle : begin 155 time_cnt_clr <= 1'b1; 156 if(remote_in_d0 == 1'b0) 157 time_cnt_clr <= 1'b0; 158 end 159 st_start_low_9ms : begin //9ms/0.125ms = 72 160 if(pos_remote_in) begin 161 time_cnt_clr <= 1'b1; 162 if(time_cnt >= 69 && time_cnt <= 75) 163 time_done <= 1'b1; 164 else 165 error_en <= 1'b1; 166 end 167 end 168 st_start_judge : begin 169 if(neg_remote_in) begin 170 time_cnt_clr <= 1'b1; 171 //重复码高电平2.25ms 2.25/0.125 = 18 172 if(time_cnt >= 15 && time_cnt <= 20) begin 173 time_done <= 1'b1; 174 judge_flag <= 1'b1; 175 end 176 //同步码高电平4.5ms 4.5/0.125 = 36 177 else if(time_cnt >= 33 && time_cnt <= 38) begin 178 time_done <= 1'b1; 179 judge_flag <= 1'b0; 180 end 181 else 182 error_en <= 1'b1; 183 end 184 end 185 st_rec_data : begin 186 if(pos_remote_in) begin 187 time_cnt_clr <= 1'b1; 188 if(data_cnt == 6'd32) begin 189 data_en <= 1'b1; 190 data_cnt <= 6'd0; 191 data_temp <= 16'd0; 192 if(data_temp[7:0] == ~data_temp[15:8]) //校验控制码和控制反码 193 data <= data_temp[7:0]; 194 end 195 end 196 else if(neg_remote_in) begin 197 time_cnt_clr <= 1'b1; 198 data_cnt <= data_cnt + 1'b1; 199 //解析控制码和控制反码 200 if(data_cnt >= 6'd16 && data_cnt <= 6'd31) begin 201 if(time_cnt >= 2 && time_cnt <= 6) begin //0.565/0.125 = 4.52 202 data_temp <= {1'b0,data_temp[15:1]}; //逻辑“0” 203 end 204 else if(time_cnt >= 10 && time_cnt <= 15) //1.69/0.125 = 13.52 205 data_temp <= {1'b1,data_temp[15:1]}; //逻辑“1” 206 end 207 end 208 end 209 st_repeat_code : begin 210 if(pos_remote_in) begin 211 time_cnt_clr <= 1'b1; 212 repeat_en <= 1'b1; 213 end 214 end 215 default : ; 216 endcase 217 end 218 end 219 220 endmodule 在代码第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状态,否则跳转到空闲状态。程序后面对空闲信号、重复码以及数 据的检测方法类似,在此不再赘述。 图 24.4.4为SignalTap抓取的波形图,从图中可以清晰的看到红外驱动模块各个状态跳转 的波形图。可以观察到空闲状态时总线为高电平,按下遥控器按键后,发出9ms低电平的同步 码和4.5ms高电平的空闲信号,然后发出00000000的地址码和11111111的地址反码;接下来发 送10100010的控制码和01011101的控制反码。需要注意的是,红外遥控先发送的是数据的低位, 所以控制码为8’b01000101(8’d69),和图中的data(控制码)保持一致。在波形图的最后, 接收到了红外遥控器发出的重复码,当程序检测到重复码之后,repeat_en发出一次脉冲信号。 图 24.4.4 SignalTap抓取的波形图 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 灯,从而指示红外遥控模块是否检测到重复码。 下载验证 首先我们打开红外遥控工程,在工程所在的路径下打开top_remote_rcv/par文件夹,在里 面找到“top_remote_rcv.qpf”并双击打开。注意工程所在的路径名只能由字母、数字以及下 划线组成,不能出现中文、空格以及特殊字符等。工程打开后如图 24.5.1所示。 图 24.5.1 打开工程 工程打开后通过点击工具栏中的“Programmer”图标(图中红框位置)打开下载界面,通过 “Add File” 按 钮 选择串 口工程 中 top_remote_rcv/par/output_files 目 录 下 “top_remote_rcv.sof”文件,下载界面如图 24.5.2所示(图中已下载完毕)。 图 24.5.2 下载界面 将下载器一端连电脑,另一端与开发板上对应端口连接,然后连接电源线并打开电源开关。 开发板电源打开后,在程序下载界面点击“Hardware Setup”, 在弹出的对话框中选择 当前的硬件连接为“USB-Blaster”。然后点击“Start”将工程编译完成后得到的sof文件下 载到开发板中。 下载完成后,按下遥控器上任意按键,就可以观察数码管上显示的数据了;长按按键的话, 可以观察到LED在不停地闪烁。需要注意的是,使用遥控器之前需要先将遥控器后部的塑料绝 缘片拔出,否则遥控器无法正常使用。 |
|
相关推荐
|
|
如何配置Linux操作系统设备树让我的开发板可以将板子上的GPIO接口用作 I2S输出??
1675 浏览 1 评论
1450 浏览 0 评论
2078 浏览 0 评论
2019 浏览 2 评论
1118 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-8-20 02:44 , Processed in 0.599292 second(s), Total 62, Slave 45 queries .
Powered by 电子发烧友网
© 2015 www.ws-dc.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号