OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 591|回复: 0

[国产FPGA] 《ATK-DFPGL22G 之FPGA开发指南》第二十六章 红外遥控实验

[复制链接]

1061

主题

1072

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4407
金钱
4407
注册时间
2019-5-8
在线时间
1196 小时
发表于 2023-11-27 16:56:42 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-11-27 16:56 编辑

第二十六章 红外遥控实验


1)实验平台:正点原子 ATK-DFPGL22G开发板

2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-PGL22G.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)FPGA技术交流QQ群:435699340

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

红外遥控是一种无线、非接触控制技术,具有抗干扰能力强、信息传输可靠、功耗低、易实现等显著特点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。本章我们将使用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所示。                              
image001.png
图 26.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。
image003.png
图 26.1.2 NEC协议的数据传输格式
图 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所示。
image004.png
图 26.1.3 重复码的数据格式
image006.png
图 26.1.4 一直发送重复码
以上部分是对NEC协议的介绍,也就是红外遥控器发送数据时所遵循的协议规范,接下来我们了解下开发板板载的红外接收头,其型号为HS0038B,实物图和结构框图如图 26.1.5和图 26.1.6所示。
image008.png
图 26.1.5  HS0038B实物图
image010.png
图 26.1.6 HS0038B结构框图
红外接收头通常被厂家集成在一个元件中,成为一体化红外接收头。内部集成了红外监测二极管、自动增益放大器(AGC)、带通滤波器(Band Pass)、解调器(Demodulator)等电路。红外遥控器发出的信息经38KHz的载频进行二级调制以提高发射效率,达到降低电源功率的目的,然后再经过红外发射二极管产生红外线向空间中发射。红外接收头通过红外监测二极管,将光信号转换成电信号,经过电路调制之后,最终输出可以被FPGA采集的TTL电平信号。这里要注意的一点是,红外接收头内部的三极管电路具有信号反向的功能,也就是将1变为0,0变为1,那么上面的整个协议则电平反过来接收。9ms本来是高电平,那么将变为低电平,以此类推如图 26.1.7所示,接收解码对应的波形是FPGA最终接收到的红外信号。
image013.png
图 26.1.7 红外接收解码接收图
下图为红外解码接收到的完整波形。
image015.png
图 26.1.8 红外解码接收到的完整波形
从图 26.1.8可以看到,地址码为0,控制码为0x15。在一段时间之后,我们还可以收到几个脉冲,这就是NEC协议规定的重复码(连发码),如果一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,可以通过统计重复码来标记按键按下的长短/次数,下图给出了我们附带的遥控器每个按键的键码。  
image016.jpg image019.png
图 26.1.9 遥控器键码示意图
下面是红外遥控的按键对应的键码值:
QQ截图20231127164449.png
表 26.1.1 红外遥控按键对应的键码值表

1.2 实验任务
本节实验任务是使用ATK-DFPGL22G FPGA开发板接收红外遥控器发出的红外信号,并将数据显示在数码管上;如果监测到重复码,则通过LED灯闪烁指示。

1.3 硬件设计
HS0038电路原理图如图 26.3.1所示,图中REMOTE_IN信号为红外接收头的电平输出端。
image020.png
图 26.3.1 HS0038B电路原理图
本实验的管脚分配如下表所示

QQ截图20231127164514.png
QQ截图20231127164526.png
表 26.3.1 红外遥控数码管显示实验管脚分配

对应的fdc文件如下所示:
  1. define_attribute{p:seg_led[7]} {PAP_IO_DIRECTION} {OUTPUT}
  2. define_attribute{p:seg_led[7]} {PAP_IO_LOC} {H2}
  3. define_attribute{p:seg_led[7]} {PAP_IO_VCCIO} {3.3}
  4. define_attribute{p:seg_led[7]} {PAP_IO_STANDARD} {LVCMOS33}
  5. define_attribute{p:seg_led[7]} {PAP_IO_DRIVE} {4}
  6. define_attribute{p:seg_led[7]} {PAP_IO_NONE} {TRUE}
  7. define_attribute{p:seg_led[7]} {PAP_IO_SLEW} {SLOW}
  8. define_attribute{p:seg_led[6]} {PAP_IO_DIRECTION} {OUTPUT}
  9. define_attribute{p:seg_led[6]} {PAP_IO_LOC} {H1}
  10. define_attribute{p:seg_led[6]} {PAP_IO_VCCIO} {3.3}
  11. define_attribute{p:seg_led[6]} {PAP_IO_STANDARD} {LVCMOS33}
  12. define_attribute{p:seg_led[6]} {PAP_IO_DRIVE} {4}
  13. define_attribute{p:seg_led[6]} {PAP_IO_NONE} {TRUE}
  14. define_attribute{p:seg_led[6]} {PAP_IO_SLEW} {SLOW}
  15. define_attribute{p:seg_led[5]} {PAP_IO_DIRECTION} {OUTPUT}
  16. define_attribute{p:seg_led[5]} {PAP_IO_LOC} {L6}
  17. define_attribute{p:seg_led[5]} {PAP_IO_VCCIO} {3.3}
  18. define_attribute{p:seg_led[5]} {PAP_IO_STANDARD} {LVCMOS33}
  19. define_attribute{p:seg_led[5]} {PAP_IO_DRIVE} {4}
  20. define_attribute{p:seg_led[5]} {PAP_IO_NONE} {TRUE}
  21. define_attribute{p:seg_led[5]} {PAP_IO_SLEW} {SLOW}
  22. define_attribute{p:seg_led[4]} {PAP_IO_DIRECTION} {OUTPUT}
  23. define_attribute{p:seg_led[4]} {PAP_IO_LOC} {K6}
  24. define_attribute{p:seg_led[4]} {PAP_IO_VCCIO} {3.3}
  25. define_attribute{p:seg_led[4]} {PAP_IO_STANDARD} {LVCMOS33}
  26. define_attribute{p:seg_led[4]} {PAP_IO_DRIVE} {4}
  27. define_attribute{p:seg_led[4]} {PAP_IO_NONE} {TRUE}
  28. define_attribute{p:seg_led[4]} {PAP_IO_SLEW} {SLOW}
  29. define_attribute{p:seg_led[3]} {PAP_IO_DIRECTION} {OUTPUT}
  30. define_attribute{p:seg_led[3]} {PAP_IO_LOC} {H3}
  31. define_attribute{p:seg_led[3]} {PAP_IO_VCCIO} {3.3}
  32. define_attribute{p:seg_led[3]} {PAP_IO_STANDARD} {LVCMOS33}
  33. define_attribute{p:seg_led[3]} {PAP_IO_DRIVE} {4}
  34. define_attribute{p:seg_led[3]} {PAP_IO_NONE} {TRUE}
  35. define_attribute{p:seg_led[3]} {PAP_IO_SLEW} {SLOW}
  36. define_attribute{p:seg_led[2]} {PAP_IO_DIRECTION} {OUTPUT}
  37. define_attribute{p:seg_led[2]} {PAP_IO_LOC} {J3}
  38. define_attribute{p:seg_led[2]} {PAP_IO_VCCIO} {3.3}
  39. define_attribute{p:seg_led[2]} {PAP_IO_STANDARD} {LVCMOS33}
  40. define_attribute{p:seg_led[2]} {PAP_IO_DRIVE} {4}
  41. define_attribute{p:seg_led[2]} {PAP_IO_NONE} {TRUE}
  42. define_attribute{p:seg_led[2]} {PAP_IO_SLEW} {SLOW}
  43. define_attribute{p:seg_led[1]} {PAP_IO_DIRECTION} {OUTPUT}
  44. define_attribute{p:seg_led[1]} {PAP_IO_LOC} {J5}
  45. define_attribute{p:seg_led[1]} {PAP_IO_VCCIO} {3.3}
  46. define_attribute{p:seg_led[1]} {PAP_IO_STANDARD} {LVCMOS33}
  47. define_attribute{p:seg_led[1]} {PAP_IO_DRIVE} {4}
  48. define_attribute{p:seg_led[1]} {PAP_IO_NONE} {TRUE}
  49. define_attribute{p:seg_led[1]} {PAP_IO_SLEW} {SLOW}
  50. define_attribute{p:seg_led[0]} {PAP_IO_DIRECTION} {OUTPUT}
  51. define_attribute{p:seg_led[0]} {PAP_IO_LOC} {G2}
  52. define_attribute{p:seg_led[0]} {PAP_IO_VCCIO} {3.3}
  53. define_attribute{p:seg_led[0]} {PAP_IO_STANDARD} {LVCMOS33}
  54. define_attribute{p:seg_led[0]} {PAP_IO_DRIVE} {4}
  55. define_attribute{p:seg_led[0]} {PAP_IO_NONE} {TRUE}
  56. define_attribute{p:seg_led[0]} {PAP_IO_SLEW} {SLOW}
  57. define_attribute{p:led} {PAP_IO_DIRECTION} {OUTPUT}
  58. define_attribute{p:led} {PAP_IO_LOC} {G1}
  59. define_attribute{p:led} {PAP_IO_VCCIO} {3.3}
  60. define_attribute{p:led} {PAP_IO_STANDARD} {LVCMOS33}
  61. define_attribute{p:led} {PAP_IO_DRIVE} {4}
  62. define_attribute{p:led} {PAP_IO_NONE} {TRUE}
  63. define_attribute{p:led} {PAP_IO_SLEW} {SLOW}
  64. define_attribute{p:remote_in} {PAP_IO_DIRECTION} {INPUT}
  65. define_attribute{p:remote_in} {PAP_IO_LOC} {E11}
  66. define_attribute{p:remote_in} {PAP_IO_VCCIO} {3.3}
  67. define_attribute{p:remote_in} {PAP_IO_STANDARD} {LVCMOS33}
  68. define_attribute{p:remote_in} {PAP_IO_NONE} {TRUE}
  69. define_attribute{p:sys_clk} {PAP_IO_DIRECTION} {INPUT}
  70. define_attribute{p:sys_clk} {PAP_IO_LOC} {B5}
  71. define_attribute{p:sys_clk} {PAP_IO_VCCIO} {3.3}
  72. define_attribute{p:sys_clk} {PAP_IO_STANDARD} {LVCMOS33}
  73. define_attribute{p:sys_clk} {PAP_IO_NONE} {TRUE}
  74. define_attribute{p:sys_rst_n} {PAP_IO_DIRECTION} {INPUT}
  75. define_attribute{p:sys_rst_n} {PAP_IO_LOC} {G5}
  76. define_attribute{p:sys_rst_n} {PAP_IO_VCCIO} {1.5}
  77. define_attribute{p:sys_rst_n} {PAP_IO_STANDARD} {LVCMOS15}
  78. define_attribute{p:sys_rst_n} {PAP_IO_NONE} {TRUE}
  79. define_attribute{p:sel[1]} {PAP_IO_DIRECTION} {OUTPUT}
  80. define_attribute{p:sel[1]} {PAP_IO_LOC} {G14}
  81. define_attribute{p:sel[1]} {PAP_IO_VCCIO} {3.3}
  82. define_attribute{p:sel[1]} {PAP_IO_STANDARD} {LVCMOS33}
  83. define_attribute{p:sel[1]} {PAP_IO_DRIVE} {4}
  84. define_attribute{p:sel[1]} {PAP_IO_NONE} {TRUE}
  85. define_attribute{p:sel[1]} {PAP_IO_SLEW} {SLOW}
  86. define_attribute{p:sel[5]} {PAP_IO_DIRECTION} {OUTPUT}
  87. define_attribute{p:sel[5]} {PAP_IO_LOC} {F14}
  88. define_attribute{p:sel[5]} {PAP_IO_VCCIO} {3.3}
  89. define_attribute{p:sel[5]} {PAP_IO_STANDARD} {LVCMOS33}
  90. define_attribute{p:sel[5]} {PAP_IO_DRIVE} {4}
  91. define_attribute{p:sel[5]} {PAP_IO_NONE} {TRUE}
  92. define_attribute{p:sel[5]} {PAP_IO_SLEW} {SLOW}
  93. define_attribute{p:sel[4]} {PAP_IO_DIRECTION} {OUTPUT}
  94. define_attribute{p:sel[4]} {PAP_IO_LOC} {F13}
  95. define_attribute{p:sel[4]} {PAP_IO_VCCIO} {3.3}
  96. define_attribute{p:sel[4]} {PAP_IO_STANDARD} {LVCMOS33}
  97. define_attribute{p:sel[4]} {PAP_IO_DRIVE} {4}
  98. define_attribute{p:sel[4]} {PAP_IO_NONE} {TRUE}
  99. define_attribute{p:sel[4]} {PAP_IO_SLEW} {SLOW}
  100. define_attribute{p:sel[3]} {PAP_IO_DIRECTION} {OUTPUT}
  101. define_attribute{p:sel[3]} {PAP_IO_LOC} {F16}
  102. define_attribute{p:sel[3]} {PAP_IO_VCCIO} {3.3}
  103. define_attribute{p:sel[3]} {PAP_IO_STANDARD} {LVCMOS33}
  104. define_attribute{p:sel[3]} {PAP_IO_DRIVE} {4}
  105. define_attribute{p:sel[3]} {PAP_IO_NONE} {TRUE}
  106. define_attribute{p:sel[3]} {PAP_IO_SLEW} {SLOW}
  107. define_attribute{p:sel[2]} {PAP_IO_DIRECTION} {OUTPUT}
  108. define_attribute{p:sel[2]} {PAP_IO_LOC} {G16}
  109. define_attribute{p:sel[2]} {PAP_IO_VCCIO} {3.3}
  110. define_attribute{p:sel[2]} {PAP_IO_STANDARD} {LVCMOS33}
  111. define_attribute{p:sel[2]} {PAP_IO_DRIVE} {4}
  112. define_attribute{p:sel[2]} {PAP_IO_NONE} {TRUE}
  113. define_attribute{p:sel[2]} {PAP_IO_SLEW} {SLOW}
  114. define_attribute{p:sel[0]} {PAP_IO_DIRECTION} {OUTPUT}
  115. define_attribute{p:sel[0]} {PAP_IO_LOC} {G13}
  116. define_attribute{p:sel[0]} {PAP_IO_VCCIO} {3.3}
  117. define_attribute{p:sel[0]} {PAP_IO_STANDARD} {LVCMOS33}
  118. define_attribute{p:sel[0]} {PAP_IO_DRIVE} {4}
  119. define_attribute{p:sel[0]} {PAP_IO_NONE} {TRUE}
  120. define_attribute{p:sel[0]} {PAP_IO_SLEW} {SLOW}
复制代码

1.4 程序设计
根据实验任务可大致规划出控制流程,红外驱动模块解析红外数据,将控制码输出至数码管驱动模块,重复码有效信号输出至LED控制模块。数码管驱动模块将对应的位选和段选信号发送至数码管,使相应的数字显示在数码管上,LED控制模块根据重复码信号控制LED灯的亮灭。系统框图如下所示。
image023.png
图 26.4.1 红外遥控实验系统框图
FPGA顶层(top_remote_rcv)例化了以下两个模块:红外驱动模块(remote_rcv)和数码管动态显示模块(seg_led),实现各模块间信号的交互。
顶层模块代码如下:
  1. 1 module top_remote_rcv(
  2. 2      input            sys_clk  ,    //系统时钟
  3. 3      input            sys_rst_n,    //系统复位信号,低电平有效
  4. 4      input            remote_in,    //红外接收信号
  5. 5      output     [5:0  sel      ,    //数码管位选信号
  6. 6      output     [7:0  seg_led  ,    //数码管段选信号
  7. 7      output           led           //led灯
  8. 8 );
  9. 9
  10. 10 //wire define
  11. 11 wire  [7:0   data       ;
  12. 12 wire         repeat_en  ;
  13. 13
  14. 14 //*****************************************************
  15. 15 //**                   main code
  16. 16 //*****************************************************
  17. 17
  18. 18 //数码管显示模块
  19. 19 seg_led u_seg_led(
  20. 20     .clk            (sys_clk),   
  21. 21     .rst_n          (sys_rst_n),
  22. 22     .sel            (sel),   
  23. 23     .seg_led        (seg_led),
  24. 24     .data           (data),           //红外数据
  25. 25     .point          (6'd0),           //无小数点
  26. 26     .en             (1'b1),           //使能数码管
  27. 27     .sign           (1'b0)            //无符号显示
  28. 28     );
  29. 29
  30. 30 //HS0038B驱动模块
  31. 31 remote_rcv u_remote_rcv(               
  32. 32     .sys_clk        (sys_clk),  
  33. 33     .sys_rst_n      (sys_rst_n),   
  34. 34     .remote_in      (remote_in),
  35. 35     .repeat_en      (repeat_en),               
  36. 36     .data_en        (),
  37. 37     .data           (data)
  38. 38     );
  39. 39
  40. 40 led_ctrl  u_led_ctrl(
  41. 41     .sys_clk       (sys_clk),
  42. 42     .sys_rst_n     (sys_rst_n),
  43. 43     .repeat_en     (repeat_en),
  44. 44     .led           (led)
  45. 45     );
  46. 46
  47. 47 endmodule
复制代码
顶层模块完成对其他模块的例化,红外驱动模块输出的控制码(data)连接至数码管显示模块,输出的repeat_en(重复码有效信号)连接至LED控制模块。
由本章简介部分介绍的红外传输时序可以发现,红外传输时序非常适合使用状态机来编写。红外驱动模块状态跳转图如下图所示。
QQ截图20231127164600.png
图 26.4.2 红外驱动模块状态跳转图
红外驱动模块使用三段式状态机来解析红外遥控信号,从上图可以比较直观的看到每个状态实现的功能以及跳转都下一个状态的条件。由于一次完整的红外信息和重复码都是以同步码(9ms的低电平)开始,其空闲信号高电平的时间是不一样的,一次完整的红外信息空闲信号高电平时间是4.5ms,而重复码的空闲信号高电平时间是2.25ms。所以我们在st_start_judge状态判断空闲信号高电平的时间,如果时间是4.5ms,则跳转到st_rec_data状态;如果时间是2.25ms,则跳转到st_repeat状态。
红外驱动模块部分代码如下:
  1. 1  module remote_rcv(
  2. 2       input                 sys_clk   ,  //系统时钟
  3. 3       input                 sys_rst_n ,  //系统复位信号,低电平有效
  4. 4      
  5. 5       input                 remote_in ,  //红外接收信号
  6. 6       output    reg         repeat_en ,  //重复码有效信号
  7. 7       output    reg         data_en   ,  //数据有效信号
  8. 8       output    reg  [7:0   data         //红外控制码
  9. 9       );
  10. 10
  11. 11 //parameter define
  12. 12 parameter  st_idle           = 5'b0_0001;  //空闲状态
  13. 13 parameter  st_start_low_9ms  = 5'b0_0010;  //监测同步码低电平
  14. 14 parameter  st_start_judge    = 5'b0_0100;  //判断重复码和同步码高电平(空闲信号)
  15. 15 parameter  st_rec_data       = 5'b0_1000;  //接收数据
  16. 16 parameter  st_repeat_code    = 5'b1_0000;  //重复码
  17. 17
  18. 18 //reg define
  19. 19 reg    [4:0   cur_state      ;
  20. 20 reg    [4:0   next_state     ;
  21. 21
  22. 22 reg    [11:0   div_cnt        ;  //分频计数器
  23. 23 reg             div_clk        ;  //分频时钟
  24. 24 reg             remote_in_d0   ;  //对输入的红外信号延时打拍
  25. 25 reg             remote_in_d1   ;
  26. 26 reg    [7:0   time_cnt       ;  //对红外的各个状态进行计数
  27. 27
  28. 28 reg             time_cnt_clr   ;  //计数器清零信号
  29. 29 reg             time_done      ;  //计时完成信号
  30. 30 reg             error_en       ;  //错误信号
  31. 31 reg             judge_flag     ;  //检测出的标志信号 0:同步码高电平(空闲信号)  1:重复码
  32. 32 reg    [15:0  data_temp      ;  //暂存收到的控制码和控制反码
  33. 33 reg    [5:0   data_cnt       ;  //对接收的数据进行计数      
  34. 34
  35. 35 //wire define
  36. 36 wire            pos_remote_in  ;  //输入红外信号的上升沿
  37. 37 wire            neg_remote_in  ;  //输入红外信号的下降沿
  38. 38
  39. 39 //*****************************************************
  40. 40 //**                    main code
  41. 41 //*****************************************************
  42. 42
  43. 43 assign  pos_remote_in = (~remote_in_d1) & remote_in_d0;
  44. 44 assign  neg_remote_in = remote_in_d1 & (~remote_in_d0);
  45. 45
  46. 46 //时钟分频,50Mhz/(2*(3124+1))=8khz,T=0.125ms
  47. 47 always @(posedge sys_clk or negedge sys_rst_n  ) begin
  48. 48      if (!sys_rst_n) begin
  49. 49          div_cnt <= 12'd0;
  50. 50          div_clk <= 1'b0;
  51. 51      end   
  52. 52      else if(div_cnt == 12'd3124) begin
  53. 53          div_cnt <= 12'd0;
  54. 54          div_clk <= ~div_clk;
  55. 55      end   
  56. 56      else
  57. 57          div_cnt = div_cnt + 12'b1;
  58. 58 end
  59. 59
  60. 60 //对红外的各个状态进行计数
  61. 61 always @(posedge div_clk or negedge sys_rst_n) begin
  62. 62      if(!sys_rst_n)
  63. 63          time_cnt <= 8'b0;
  64. 64      else if(time_cnt_clr)
  65. 65          time_cnt <= 8'b0;
  66. 66      else
  67. 67          time_cnt <= time_cnt + 8'b1;
  68. 68 end
  69. 69
  70. 70 //对输入的remote_in信号延时打拍
  71. 71 always @(posedge div_clk or negedge sys_rst_n) begin
  72. 72      if(!sys_rst_n) begin
  73. 73          remote_in_d0 <= 1'b0;
  74. 74          remote_in_d1 <= 1'b0;
  75. 75      end
  76. 76      else begin
  77. 77          remote_in_d0 <= remote_in;
  78. 78          remote_in_d1 <= remote_in_d0;
  79. 79      end
  80. 80 end
  81. 81
  82. 82 //状态机
  83. 83 always @ (posedge div_clk or negedge sys_rst_n) begin
  84. 84      if(!sys_rst_n)
  85. 85          cur_state <= st_idle;
  86. 86      else
  87. 87          cur_state <= next_state ;
  88. 88 end
  89. 89
  90. 90 always @(*) begin
  91. 91      next_state = st_idle;
  92. 92      case(cur_state)
  93. 93          st_idle : begin                           //空闲状态
  94. 94              if(remote_in_d0 == 1'b0)
  95. 95                  next_state = st_start_low_9ms;
  96. 96              else
  97. 97                  next_state = st_idle;            
  98. 98          end
  99. 99          st_start_low_9ms : begin                  //监测同步码低电平
  100. 100             if(time_done)
  101. 101                 next_state = st_start_judge;
  102. 102             else if(error_en)
  103. 103                 next_state = st_idle;
  104. 104             else
  105. 105                 next_state = st_start_low_9ms;
  106. 106         end
  107. 107         st_start_judge : begin                    //判断重复码和同步码高电平(空闲信号)
  108. 108             if(time_done) begin
  109. 109                 if(judge_flag == 1'b0)
  110. 110                     next_state = st_rec_data;
  111. 111                 else
  112. 112                     next_state = st_repeat_code;
  113. 113             end
  114. 114             else if(error_en)
  115. 115                 next_state = st_idle;
  116. 116             else
  117. 117                 next_state = st_start_judge;
  118. 118         end
  119. 119         st_rec_data : begin                       //接收数据
  120. 120             if(pos_remote_in && data_cnt == 6'd32)
  121. 121                 next_state = st_idle;
  122. 122             else
  123. 123                 next_state = st_rec_data;               
  124. 124         end
  125. 125         st_repeat_code : begin                    //重复码
  126. 126             if(pos_remote_in)
  127. 127                 next_state = st_idle;
  128. 128             else
  129. 129                 next_state = st_repeat_code;   
  130. 130         end   
  131. 131         default : next_state = st_idle;
  132. 132     endcase
  133. 133 end
  134. 134
  135. 135 always @(posedge div_clk or negedge sys_rst_n ) begin
  136. 136     if (!sys_rst_n) begin  
  137. 137         time_cnt_clr <= 1'b0;
  138. 138         time_done <= 1'b0;
  139. 139         error_en <= 1'b0;
  140. 140         judge_flag <= 1'b0;
  141. 141         data_en <= 1'b0;
  142. 142         data <= 8'd0;
  143. 143         repeat_en <= 1'b0;
  144. 144         data_cnt <= 6'd0;
  145. 145         data_temp <= 32'd0;
  146. 146     end
  147. 147     else begin
  148. 148         time_cnt_clr <= 1'b0;
  149. 149         time_done <= 1'b0;
  150. 150         error_en <= 1'b0;
  151. 151         repeat_en <= 1'b0;
  152. 152         data_en <= 1'b0;
  153. 153         case(cur_state)
  154. 154             st_idle           : begin
  155. 155                 time_cnt_clr <= 1'b1;
  156. 156                 if(remote_in_d0 == 1'b0)
  157. 157                     time_cnt_clr <= 1'b0;
  158. 158             end   
  159. 159             st_start_low_9ms  : begin                             //9ms/0.125ms = 72
  160. 160                 if(pos_remote_in) begin  
  161. 161                     time_cnt_clr <= 1'b1;                  
  162. 162                     if(time_cnt >= 69 && time_cnt <= 75)
  163. 163                         time_done <= 1'b1;  
  164. 164                     else
  165. 165                         error_en <= 1'b1;
  166. 166                 end   
  167. 167             end
  168. 168             st_start_judge : begin
  169. 169                 if(neg_remote_in) begin   
  170. 170                     time_cnt_clr <= 1'b1;   
  171. 171                     //重复码高电平2.25ms2.25/0.125 = 18      
  172. 172                     if(time_cnt >= 15 && time_cnt <= 20) begin
  173. 173                         time_done <= 1'b1;
  174. 174                         judge_flag <= 1'b1;
  175. 175                     end   
  176. 176                     //同步码高电平4.5ms4.5/0.125 = 36
  177. 177                     else if(time_cnt >=33 && time_cnt <= 38) begin
  178. 178                         time_done <= 1'b1;
  179. 179                         judge_flag <= 1'b0;                        
  180. 180                     end
  181. 181                     else
  182. 182                         error_en <= 1'b1;
  183. 183                 end                     
  184. 184             end
  185. 185             st_rec_data : begin                                 
  186. 186                 if(pos_remote_in) begin
  187. 187                     time_cnt_clr <= 1'b1;
  188. 188                     if(data_cnt == 6'd32) begin
  189. 189                         data_en <= 1'b1;
  190. 190                         data_cnt <= 6'd0;
  191. 191                         data_temp <= 16'd0;
  192. 192                         if(data_temp[7:0 == ~data_temp[15:8])    //校验控制码和控制反码
  193. 193                             data <= data_temp[7:0];
  194. 194                     end
  195. 195                 end
  196. 196                 else if(neg_remote_in) begin
  197. 197                     time_cnt_clr <= 1'b1;
  198. 198                     data_cnt <= data_cnt + 1'b1;   
  199. 199                     //解析控制码和控制反码        
  200. 200                     if(data_cnt >= 6'd16 && data_cnt <= 6'd31) begin
  201. 201                         if(time_cnt >= 2 && time_cnt <= 6) begin //0.565/0.125 = 4.52
  202. 202                             data_temp <= {1'b0,data_temp[15:1]};  //逻辑“0”
  203. 203                         end
  204. 204                         else if(time_cnt >=10 && time_cnt <= 15) //1.69/0.125= 13.52
  205. 205                             data_temp <= {1'b1,data_temp[15:1]};  //逻辑“1”
  206. 206                     end
  207. 207                 end
  208. 208             end
  209. 209             st_repeat_code : begin                                
  210. 210                 if(pos_remote_in) begin                           
  211. 211                     time_cnt_clr <= 1'b1;
  212. 212                     repeat_en <= 1'b1;
  213. 213                 end
  214. 214             end
  215. 215             default : ;
  216. 216         endcase
  217. 217     end
  218. 218 end
  219. 219
  220. 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状态,否则跳转到空闲状态。程序后面对空闲信号、重复码以及数据的检测方法类似,在此不再赘述。
LED控制模块代码如下:
  1. 1 module led_ctrl(
  2. 2      input            sys_clk   ,  //系统时钟
  3. 3      input            sys_rst_n ,  //系统复位信号,低电平有效
  4. 4      
  5. 5      input            repeat_en ,  //重复码触发信号
  6. 6      output    reg     led          //LED灯
  7. 7      );
  8. 8
  9. 9 //reg define
  10. 10 reg           repeat_en_d0 ;      //repeat_en信号打拍采沿
  11. 11 reg           repeat_en_d1 ;
  12. 12 reg    [22:0  led_cnt      ;      //LED灯计数器,用于控制LED灯亮灭
  13. 13
  14. 14 //wire define
  15. 15 wire          pos_repeat_en;
  16. 16
  17. 17 //*****************************************************
  18. 18 //**                   main code
  19. 19 //*****************************************************
  20. 20
  21. 21 assign  pos_repeat_en= ~repeat_en_d1 & repeat_en_d0;
  22. 22
  23. 23 ////repeat_en信号打拍采沿
  24. 24 always @(posedge sys_clk or negedge sys_rst_n) begin
  25. 25     if(!sys_rst_n) begin
  26. 26         repeat_en_d0 <= 1'b0;
  27. 27         repeat_en_d1 <= 1'b0;
  28. 28     end
  29. 29     else begin
  30. 30         repeat_en_d0 <= repeat_en;
  31. 31         repeat_en_d1 <= repeat_en_d0;
  32. 32     end
  33. 33 end   
  34. 34
  35. 35 always @(posedge sys_clk or negedge sys_rst_n) begin
  36. 36     if(!sys_rst_n) begin
  37. 37         led_cnt <= 23'd0;
  38. 38         led <= 1'b0;
  39. 39     end
  40. 40     else begin
  41. 41         if(pos_repeat_en) begin
  42. 42             led_cnt <= 23'd5_000_000;              //单次重复码:亮80ms 灭20ms
  43. 43             led <= 1'b1;                           //led亮的时间:4_000_000*20ns=80ms
  44. 44         end   
  45. 45         else if(led_cnt != 23'd0) begin
  46. 46             led_cnt <= led_cnt - 23'd1;
  47. 47             if(led_cnt < 23'd1_000_000)            //led灭的时间:1_000_000*20ns=20ms
  48. 48                 led <= 1'b0;
  49. 49         end     
  50. 50     end   
  51. 51 end
  52. 52
  53. 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,如下图所示:
image026.png
图 26.5.1 按下按键“1”
image028.jpg
图 26.5.2 红外遥控现象图
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-5-29 10:09

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表