当前位置:   article > 正文

【红外遥控器】基于FPGA的红外遥控verilog附代码及解析_红外遥控器编码 fpga

红外遥控器编码 fpga

一、软件版本

 Quartus II 13.1 (64-bit)、Modelsim SE-64 10.4、正点原子FPGA开发板开拓者V2

二、红外遥控简介

红外遥控:红外遥控是一种无线、非接触控制技术。

特点:抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等;由于它不能穿过障碍物去控制被控对象的能力,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况

红外光波长为760nm到1mm,人眼可见波长为400nm到760nm。

三、红外遥控

        红外遥控器传输协议的编码目前广泛使用的是: NEC协议和Philips RC-5协议。主流的调制方式有两种:PPM(Pulse Position Modulation,脉冲位置调制)、PWM(Pulse Width Modulation,脉冲宽度调制)。

NEC编码协议

        当按下红外遥控器上的按键时,遥控器会发出一帧红外信号,由引导码、地址码、地址反码、数据码、数码反码和结束位组成,传输时低位在前。

 NEC码位定义

        NEC协议采用PPM调制的形式进行编码,数据的每一位(Bit)脉冲长度为560us,由38KHz的载波脉冲(carrier burst)进行调制。逻辑“1”的载波脉冲+载波脉冲间隔时间为2.25ms;逻辑“0”的载波脉冲+载波脉冲间隔时间为1.12ms

      通过理解这张图我们可以知道数据码是什么,主要是看,数据0和数据1是如何表示的。通过上图可知,数据1是560us的信号,加1.69ms的空闲。数据0是560us的信号,加560us的空闲。由于红外接收头的缘故,接收到信号后输出到 FPGA 的波形刚好与发送的波形相反,最终在波形图上说白了就是,表示1时,是由560us的低电平加1.69ms高电平组合,表示0时,是由560us的低电平和560us的高电平表示,如下图。

数码重复码(连发码)

        当红外遥控器上的按键被一直按下时,红外遥控器只会发送一次完整的信息,其后会每隔110ms发送一次重复码(也叫连发码)。

重复码的数据格式比较简单,同样是由9ms载波脉冲开始,紧接着是2.25ms的空闲信号,随后是560us的载波脉冲。

 红外接收头

        一体化红外接收头,内部集成了红外监测二极管、自动增益控制(AGC)、带通滤波器(Band Pass)、解调器(Demodulator)等电路。

红外接收解码

        

 

完整的传输过程

引导码:表示数据传输的开始。

数据码:分别为地址码,地址反码,数据码,数据反码。传输红外遥控发送的数据,一帧数据共有32bit,4个字节。先发送低位。

停止码:标志着数据传输的完成。(560ms)

重复码:当一直按下某个按键的时候,并不会再次发送数据,而且通过重复码标志按键一直处于按下的状态。如果一直按下的话,约110ms,发送一次重复码。

重复码

四、系统框图

状态转移图

        首先状态机处于空闲状态st_idle,remote_in表示红外信号,当我们检测到remote_in为低电平的时候表示可能收到红外信号,此时由空闲状态转移到st_start_low_ms这一状态,在这一状态我们需要判断低电平持续时间是否为9ms,若为9ms则进入下一状态,判断是完整的数据还是重复码,time_done表示计算结束,judge_flag表示是否为数据或重复码,它是通过计时高电平持续时间判断的,如果为4.5ms就是数据,此时我们进入接收数据状态,当检测到上升沿(pos_remote)data_cnt==32即数据收到了32位,此时完成接收转移到初始状态。

五、RTL代码

1.首先定义各个信号

        输入的由系统时钟、复位、红外信号,输出的repeat_en重复码标志信号,数据有效信号data_en,及红外控制码。

  1. module remote_rcv(
  2. input sys_clk , //系统时钟
  3. input sys_rst_n , //系统复位信号,低电平有效
  4. input remote_in , //红外接收信号
  5. output reg repeat_en , //重复码有效信号
  6. output reg data_en , //数据有效信号
  7. output reg [7:0] data //红外控制码
  8. );

2.定义状态机

        一般用读热码表示每一个状态

  1. //parameter define
  2. parameter st_idle = 5'b0_0001; //空闲状态
  3. parameter st_start_low_9ms = 5'b0_0010; //监测同步码低电平
  4. parameter st_start_judge = 5'b0_0100; //判断重复码和同步码高电平(空闲信号)
  5. parameter st_rec_data = 5'b0_1000; //接收数据
  6. parameter st_repeat_code = 5'b1_0000; //重复码

3.其它定义

  1. //reg define
  2. reg [4:0] cur_state ;
  3. reg [4:0] next_state ;
  4. reg [11:0] div_cnt ; //分频计数器
  5. reg div_clk ; //分频时钟
  6. reg remote_in_d0 ; //对输入的红外信号延时打拍
  7. reg remote_in_d1 ;
  8. reg [7:0] time_cnt ; //对红外的各个状态进行计数
  9. reg time_cnt_clr ; //计数器清零信号
  10. reg time_done ; //计时完成信号
  11. reg error_en ; //错误信号
  12. reg judge_flag ; //检测出的标志信号 0:同步码高电平(空闲信号) 1:重复码
  13. reg [15:0] data_temp ; //暂存收到的控制码和控制反码
  14. reg [5:0] data_cnt ; //对接收的数据进行计数
  15. //wire define
  16. wire pos_remote_in ; //输入红外信号的上升沿
  17. wire neg_remote_in ; //输入红外信号的下降沿

4.通过打两拍抓取红外信号上升和下降沿

  1. assign pos_remote_in = (~remote_in_d1) & remote_in_d0;
  2. assign neg_remote_in = remote_in_d1 & (~remote_in_d0);
  3. //对输入的remote_in信号延时打拍
  4. always @(posedge div_clk or negedge sys_rst_n) begin
  5. if(!sys_rst_n) begin
  6. remote_in_d0 <= 1'b0;
  7. remote_in_d1 <= 1'b0;
  8. end
  9. else begin
  10. remote_in_d0 <= remote_in;
  11. remote_in_d1 <= remote_in_d0;
  12. end
  13. end

5.时钟分频

  1. //时钟分频,50Mhz/(2*(3124+1))=8khz,T=0.125ms
  2. always @(posedge sys_clk or negedge sys_rst_n ) begin
  3. if (!sys_rst_n) begin
  4. div_cnt <= 12'd0;
  5. div_clk <= 1'b0;
  6. end
  7. else if(div_cnt == 12'd3124) begin
  8. div_cnt <= 12'd0;
  9. div_clk <= ~div_clk;
  10. end
  11. else
  12. div_cnt <= div_cnt + 12'b1;
  13. end

6.时间计数

        通过对分频后的时钟计数判断时间,继而判断各个状态。

  1. //对红外的各个状态进行计数
  2. always @(posedge div_clk or negedge sys_rst_n) begin
  3. if(!sys_rst_n)
  4. time_cnt <= 8'b0;
  5. else if(time_cnt_clr)
  6. time_cnt <= 8'b0;
  7. else
  8. time_cnt <= time_cnt + 8'b1;
  9. end

7.三段式状态机

7.1第一段

  1. always @ (posedge div_clk or negedge sys_rst_n) begin
  2. if(!sys_rst_n)
  3. cur_state <= st_idle;
  4. else
  5. cur_state <= next_state ;
  6. end

        没什么好说的固定结构 ,如果复位空闲状态给到当前状态,其它情况下一状态给到当前状态。

7.2第二段

  1. always @(*) begin
  2. next_state = st_idle;
  3. case(cur_state)
  4. st_idle : begin //空闲状态
  5. if(remote_in_d0 == 1'b0)
  6. next_state = st_start_low_9ms;
  7. else
  8. next_state = st_idle;
  9. end
  10. st_start_low_9ms : begin //监测同步码低电平
  11. if(time_done)
  12. next_state = st_start_judge;
  13. else if(error_en)
  14. next_state = st_idle;
  15. else
  16. next_state = st_start_low_9ms;
  17. end
  18. st_start_judge : begin //判断重复码和同步码高电平(空闲信号)
  19. if(time_done) begin
  20. if(judge_flag == 1'b0)
  21. next_state = st_rec_data;
  22. else
  23. next_state = st_repeat_code;
  24. end
  25. else if(error_en)
  26. next_state = st_idle;
  27. else
  28. next_state = st_start_judge;
  29. end
  30. st_rec_data : begin //接收数据
  31. if(pos_remote_in && data_cnt == 6'd32)
  32. next_state = st_idle;
  33. else
  34. next_state = st_rec_data;
  35. end
  36. st_repeat_code : begin //重复码
  37. if(pos_remote_in)
  38. next_state = st_idle;
  39. else
  40. next_state = st_repeat_code;
  41. end
  42. default : next_state = st_idle;
  43. endcase
  44. end

         第二段就是根据状态转移图写出各个状态转移的条件,这里当时我不明白的是,当处于st_rec_data即接收数据状态时候,为什么要判断同时判断pos_remote_in和data_cnt,data_cnt==32这表示数据传了32位,没有问题,那上升沿是干什么的呢?后来我通过波形图发现,会有结束吗560ms低电平在拉高,会有上升沿。所以当结束时有两个判断条件,一个是数据计数达到32,一个是上升沿。

7.3第三段

  1. always @(posedge div_clk or negedge sys_rst_n ) begin
  2. if (!sys_rst_n) begin
  3. time_cnt_clr <= 1'b0;
  4. time_done <= 1'b0;
  5. error_en <= 1'b0;
  6. judge_flag <= 1'b0;
  7. data_en <= 1'b0;
  8. data <= 8'd0;
  9. repeat_en <= 1'b0;
  10. data_cnt <= 6'd0;
  11. data_temp <= 32'd0;
  12. end
  13. else begin
  14. time_cnt_clr <= 1'b0;
  15. time_done <= 1'b0;
  16. error_en <= 1'b0;
  17. repeat_en <= 1'b0;
  18. data_en <= 1'b0;
  19. case(cur_state)
  20. st_idle : begin
  21. time_cnt_clr <= 1'b1;
  22. if(remote_in_d0 == 1'b0)
  23. time_cnt_clr <= 1'b0;
  24. end
  25. st_start_low_9ms : begin //9ms/0.125ms = 72
  26. if(pos_remote_in) begin
  27. time_cnt_clr <= 1'b1;
  28. if(time_cnt >= 69 && time_cnt <= 75)
  29. time_done <= 1'b1;
  30. else
  31. error_en <= 1'b1;
  32. end
  33. end
  34. st_start_judge : begin
  35. if(neg_remote_in) begin
  36. time_cnt_clr <= 1'b1;
  37. //重复码高电平2.25ms 2.25/0.125 = 18
  38. if(time_cnt >= 15 && time_cnt <= 20) begin
  39. time_done <= 1'b1;
  40. judge_flag <= 1'b1;
  41. end
  42. //同步码高电平4.5ms 4.5/0.125 = 36
  43. else if(time_cnt >= 33 && time_cnt <= 38) begin
  44. time_done <= 1'b1;
  45. judge_flag <= 1'b0;
  46. end
  47. else
  48. error_en <= 1'b1;
  49. end
  50. end
  51. st_rec_data : begin
  52. if(pos_remote_in) begin
  53. time_cnt_clr <= 1'b1;
  54. if(data_cnt == 6'd32) begin
  55. data_en <= 1'b1;
  56. data_cnt <= 6'd0;
  57. data_temp <= 16'd0;
  58. if(data_temp[7:0] == ~data_temp[15:8]) //校验控制码和控制反码
  59. data <= data_temp[7:0];
  60. end
  61. end
  62. else if(neg_remote_in) begin
  63. time_cnt_clr <= 1'b1;
  64. data_cnt <= data_cnt + 1'b1;
  65. //解析控制码和控制反码
  66. if(data_cnt >= 6'd16 && data_cnt <= 6'd31) begin
  67. if(time_cnt >= 2 && time_cnt <= 6) begin //0.565/0.125 = 4.52
  68. data_temp <= {1'b0,data_temp[15:1]}; //逻辑“0
  69. end
  70. else if(time_cnt >= 10 && time_cnt <= 15) //1.69/0.125 = 13.52
  71. data_temp <= {1'b1,data_temp[15:1]}; //逻辑“1”
  72. end
  73. end
  74. end
  75. st_repeat_code : begin
  76. if(pos_remote_in) begin
  77. time_cnt_clr <= 1'b1;
  78. repeat_en <= 1'b1;
  79. end
  80. end
  81. default : ;
  82. endcase
  83. end
  84. end
  85. endmodule

        最后一段状态机,首先要明白time_cnt是以分频后的时钟计数的,即8KHZ即0.125ms,所以我们到达第二个状态判时候,判断是否为同步码和重复码就是看time_cnt的计数,同步码高电平2.25ms,用2.25ms/0.125ms=18,也就是当计数器数到18就代表重复码,我们为了给他容错只要它的范围在15~20我们都认为为重复码,如果是4.5ms,也就是4.5ms/0.125ms=36,我们认定它范围为33~38,同理最开始的引导码9ms也是这么判断,9ms/0.125ms=72。

        当时最让我难以理解的是,处于st_rec_data状态时的代码,这里我们需要知道每当检测到一次上升沿和下降沿,就表示一位数据传输完成,这里从波形图观察一下就可以得出,所以当检测到下降沿data_cnt+1我们就可以理解了,然后就是为什么每当上升沿和下降沿来的时候time_cnt清0,然后在else if中判断time_cnt的计数值。

        这里我们要先知道,接收一位数据不管是0还是1,是由一个上升沿和下降沿组成,如传输0,它是560us的低电平和560us高电平,传输1是560us低电平和1.69ms高电平组成,如下图,所以当检测到上升沿对time_cnt清零,只不过是对收到半个0/1的那个前半段的560us清理,对我们判断0/1不影响,因为它们有区别的是后半段的持续时间,后半段0持续560us即560us/0.125ms=4.48,后半段1持续1.69ms/0.125ms=13.52,所以我们只需要判断time_cnt持续时间是否在2~6之内或10~15之内,就能判断它是0还是1。

        最后就是那两句赋值语句,data_temp <= {1'b0,data_temp[15:1]}//逻辑0;和data_temp <= {1'b1,data_temp[15:1]};  //逻辑“1”;就是个右移动语句,大家画个图就能明白。图第一行最左边的爱心是第十五位。

7.4完整代码

  1. module remote_rcv(
  2. input sys_clk , //系统时钟
  3. input sys_rst_n , //系统复位信号,低电平有效
  4. input remote_in , //红外接收信号
  5. output reg repeat_en , //重复码有效信号
  6. output reg data_en , //数据有效信号
  7. output reg [7:0] data //红外控制码
  8. );
  9. //parameter define
  10. parameter st_idle = 5'b0_0001; //空闲状态
  11. parameter st_start_low_9ms = 5'b0_0010; //监测同步码低电平
  12. parameter st_start_judge = 5'b0_0100; //判断重复码和同步码高电平(空闲信号)
  13. parameter st_rec_data = 5'b0_1000; //接收数据
  14. parameter st_repeat_code = 5'b1_0000; //重复码
  15. //reg define
  16. reg [4:0] cur_state ;
  17. reg [4:0] next_state ;
  18. reg [11:0] div_cnt ; //分频计数器
  19. reg div_clk ; //分频时钟
  20. reg remote_in_d0 ; //对输入的红外信号延时打拍
  21. reg remote_in_d1 ;
  22. reg [7:0] time_cnt ; //对红外的各个状态进行计数
  23. reg time_cnt_clr ; //计数器清零信号
  24. reg time_done ; //计时完成信号
  25. reg error_en ; //错误信号
  26. reg judge_flag ; //检测出的标志信号 0:同步码高电平(空闲信号) 1:重复码
  27. reg [15:0] data_temp ; //暂存收到的控制码和控制反码
  28. reg [5:0] data_cnt ; //对接收的数据进行计数
  29. //wire define
  30. wire pos_remote_in ; //输入红外信号的上升沿
  31. wire neg_remote_in ; //输入红外信号的下降沿
  32. //*****************************************************
  33. //** main code
  34. //*****************************************************
  35. assign pos_remote_in = (~remote_in_d1) & remote_in_d0;
  36. assign neg_remote_in = remote_in_d1 & (~remote_in_d0);
  37. //时钟分频,50Mhz/(2*(3124+1))=8khz,T=0.125ms
  38. always @(posedge sys_clk or negedge sys_rst_n ) begin
  39. if (!sys_rst_n) begin
  40. div_cnt <= 12'd0;
  41. div_clk <= 1'b0;
  42. end
  43. else if(div_cnt == 12'd3124) begin
  44. div_cnt <= 12'd0;
  45. div_clk <= ~div_clk;
  46. end
  47. else
  48. div_cnt <= div_cnt + 12'b1;
  49. end
  50. //对红外的各个状态进行计数
  51. always @(posedge div_clk or negedge sys_rst_n) begin
  52. if(!sys_rst_n)
  53. time_cnt <= 8'b0;
  54. else if(time_cnt_clr)
  55. time_cnt <= 8'b0;
  56. else
  57. time_cnt <= time_cnt + 8'b1;
  58. end
  59. //对输入的remote_in信号延时打拍
  60. always @(posedge div_clk or negedge sys_rst_n) begin
  61. if(!sys_rst_n) begin
  62. remote_in_d0 <= 1'b0;
  63. remote_in_d1 <= 1'b0;
  64. end
  65. else begin
  66. remote_in_d0 <= remote_in;
  67. remote_in_d1 <= remote_in_d0;
  68. end
  69. end
  70. //状态机
  71. always @ (posedge div_clk or negedge sys_rst_n) begin
  72. if(!sys_rst_n)
  73. cur_state <= st_idle;
  74. else
  75. cur_state <= next_state ;
  76. end
  77. always @(*) begin
  78. next_state = st_idle;
  79. case(cur_state)
  80. st_idle : begin //空闲状态
  81. if(remote_in_d0 == 1'b0)
  82. next_state = st_start_low_9ms;
  83. else
  84. next_state = st_idle;
  85. end
  86. st_start_low_9ms : begin //监测同步码低电平
  87. if(time_done)
  88. next_state = st_start_judge;
  89. else if(error_en)
  90. next_state = st_idle;
  91. else
  92. next_state = st_start_low_9ms;
  93. end
  94. st_start_judge : begin //判断重复码和同步码高电平(空闲信号)
  95. if(time_done) begin
  96. if(judge_flag == 1'b0)
  97. next_state = st_rec_data;
  98. else
  99. next_state = st_repeat_code;
  100. end
  101. else if(error_en)
  102. next_state = st_idle;
  103. else
  104. next_state = st_start_judge;
  105. end
  106. st_rec_data : begin //接收数据
  107. if(pos_remote_in && data_cnt == 6'd32)
  108. next_state = st_idle;
  109. else
  110. next_state = st_rec_data;
  111. end
  112. st_repeat_code : begin //重复码
  113. if(pos_remote_in)
  114. next_state = st_idle;
  115. else
  116. next_state = st_repeat_code;
  117. end
  118. default : next_state = st_idle;
  119. endcase
  120. end
  121. always @(posedge div_clk or negedge sys_rst_n ) begin
  122. if (!sys_rst_n) begin
  123. time_cnt_clr <= 1'b0;
  124. time_done <= 1'b0;
  125. error_en <= 1'b0;
  126. judge_flag <= 1'b0;
  127. data_en <= 1'b0;
  128. data <= 8'd0;
  129. repeat_en <= 1'b0;
  130. data_cnt <= 6'd0;
  131. data_temp <= 32'd0;
  132. end
  133. else begin
  134. time_cnt_clr <= 1'b0;
  135. time_done <= 1'b0;
  136. error_en <= 1'b0;
  137. repeat_en <= 1'b0;
  138. data_en <= 1'b0;
  139. case(cur_state)
  140. st_idle : begin
  141. time_cnt_clr <= 1'b1;
  142. if(remote_in_d0 == 1'b0)
  143. time_cnt_clr <= 1'b0;
  144. end
  145. st_start_low_9ms : begin //9ms/0.125ms = 72
  146. if(pos_remote_in) begin
  147. time_cnt_clr <= 1'b1;
  148. if(time_cnt >= 69 && time_cnt <= 75)
  149. time_done <= 1'b1;
  150. else
  151. error_en <= 1'b1;
  152. end
  153. end
  154. st_start_judge : begin
  155. if(neg_remote_in) begin
  156. time_cnt_clr <= 1'b1;
  157. //重复码高电平2.25ms 2.25/0.125 = 18
  158. if(time_cnt >= 15 && time_cnt <= 20) begin
  159. time_done <= 1'b1;
  160. judge_flag <= 1'b1;
  161. end
  162. //同步码高电平4.5ms 4.5/0.125 = 36
  163. else if(time_cnt >= 33 && time_cnt <= 38) begin
  164. time_done <= 1'b1;
  165. judge_flag <= 1'b0;
  166. end
  167. else
  168. error_en <= 1'b1;
  169. end
  170. end
  171. st_rec_data : begin
  172. if(pos_remote_in) begin
  173. time_cnt_clr <= 1'b1;
  174. if(data_cnt == 6'd32) begin
  175. data_en <= 1'b1;
  176. data_cnt <= 6'd0;
  177. data_temp <= 16'd0;
  178. if(data_temp[7:0] == ~data_temp[15:8]) //校验控制码和控制反码
  179. data <= data_temp[7:0];
  180. end
  181. end
  182. else if(neg_remote_in) begin
  183. time_cnt_clr <= 1'b1;
  184. data_cnt <= data_cnt + 1'b1;
  185. //解析控制码和控制反码
  186. if(data_cnt >= 6'd16 && data_cnt <= 6'd31) begin
  187. if(time_cnt >= 2 && time_cnt <= 6) begin //0.565/0.125 = 4.52
  188. data_temp <= {1'b0,data_temp[15:1]}; //逻辑“0
  189. end
  190. else if(time_cnt >= 10 && time_cnt <= 15) //1.69/0.125 = 13.52
  191. data_temp <= {1'b1,data_temp[15:1]}; //逻辑“1”
  192. end
  193. end
  194. end
  195. st_repeat_code : begin
  196. if(pos_remote_in) begin
  197. time_cnt_clr <= 1'b1;
  198. repeat_en <= 1'b1;
  199. end
  200. end
  201. default : ;
  202. endcase
  203. end
  204. end
  205. endmodule
顶层代码
  1. //数码管显示模块
  2. seg_led u_seg_led(
  3. .clk (sys_clk),
  4. .rst_n (sys_rst_n),
  5. .seg_sel (sel),
  6. .seg_led (seg_led),
  7. .data (data), //红外数据
  8. .point (6'd0), //无小数点
  9. .en (1'b1), //使能数码管
  10. .sign (1'b0) //无符号显示
  11. );
  12. //HS0038B驱动模块
  13. remote_rcv u_remote_rcv(
  14. .sys_clk (sys_clk),
  15. .sys_rst_n (sys_rst_n),
  16. .remote_in (remote_in),
  17. .repeat_en (repeat_en),
  18. .data_en (),
  19. .data (data)
  20. );
  21. led_ctrl u_led_ctrl(
  22. .sys_clk (sys_clk),
  23. .sys_rst_n (sys_rst_n),
  24. .repeat_en (repeat_en),
  25. .led (led)
  26. );
  27. endmodule
数码管
  1. module seg_led(
  2. input clk , // 时钟信号
  3. input rst_n , // 复位信号
  4. input [19:0] data , // 6位数码管要显示的数值
  5. input [5:0] point , // 小数点具体显示的位置,从高到低,高电平有效
  6. input en , // 数码管使能信号
  7. input sign , // 符号位(高电平显示“-”号)
  8. output reg [5:0] seg_sel, // 数码管位选,最左侧数码管为最高位
  9. output reg [7:0] seg_led // 数码管段选
  10. );
  11. //parameter define
  12. localparam CLK_DIVIDE = 4'd10 ; // 时钟分频系数
  13. localparam MAX_NUM = 13'd5000 ; // 对数码管驱动时钟(5MHz)计数1ms所需的计数值
  14. //reg define
  15. reg [ 3:0] clk_cnt ; // 时钟分频计数器
  16. reg dri_clk ; // 数码管的驱动时钟,5MHz
  17. reg [23:0] num ; // 24位bcd码寄存器
  18. reg [12:0] cnt0 ; // 数码管驱动时钟计数器
  19. reg flag ; // 标志信号(标志着cnt0计数达1ms)
  20. reg [2:0] cnt_sel ; // 数码管位选计数器
  21. reg [3:0] num_disp ; // 当前数码管显示的数据
  22. reg dot_disp ; // 当前数码管显示的小数点
  23. //wire define
  24. wire [3:0] data0 ; // 个位数
  25. wire [3:0] data1 ; // 十位数
  26. wire [3:0] data2 ; // 百位数
  27. wire [3:0] data3 ; // 千位数
  28. wire [3:0] data4 ; // 万位数
  29. wire [3:0] data5 ; // 十万位数
  30. //*****************************************************
  31. //** main code
  32. //*****************************************************
  33. //提取显示数值所对应的十进制数的各个位
  34. assign data0 = data % 4'd10; // 个位数
  35. assign data1 = data / 4'd10 % 4'd10 ; // 十位数
  36. assign data2 = data / 7'd100 % 4'd10 ; // 百位数
  37. assign data3 = data / 10'd1000 % 4'd10 ; // 千位数
  38. assign data4 = data / 14'd10000 % 4'd10; // 万位数
  39. assign data5 = data / 17'd100000; // 十万位数
  40. //对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk
  41. always @(posedge clk or negedge rst_n) begin
  42. if(!rst_n) begin
  43. clk_cnt <= 4'd0;
  44. dri_clk <= 1'b1;
  45. end
  46. else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin
  47. clk_cnt <= 4'd0;
  48. dri_clk <= ~dri_clk;
  49. end
  50. else begin
  51. clk_cnt <= clk_cnt + 1'b1;
  52. dri_clk <= dri_clk;
  53. end
  54. end
  55. //将20位2进制数转换为8421bcd码(即使用4位二进制数表示1位十进制数)
  56. always @ (posedge dri_clk or negedge rst_n) begin
  57. if (!rst_n)
  58. num <= 24'b0;
  59. else begin
  60. if (data5 || point[5]) begin //如果显示数据为6位十进制数,
  61. num[23:20] <= data5; //则依次给6位数码管赋值
  62. num[19:16] <= data4;
  63. num[15:12] <= data3;
  64. num[11:8] <= data2;
  65. num[ 7:4] <= data1;
  66. num[ 3:0] <= data0;
  67. end
  68. else begin
  69. if (data4 || point[4]) begin //如果显示数据为5位十进制数,则给低5位数码管赋值
  70. num[19:0] <= {data4,data3,data2,data1,data0};
  71. if(sign)
  72. num[23:20] <= 4'd11; //如果需要显示负号,则最高位(第6位)为符号位
  73. else
  74. num[23:20] <= 4'd10; //不需要显示负号时,则第6位不显示任何字符
  75. end
  76. else begin //如果显示数据为4位十进制数,则给低4位数码管赋值
  77. if (data3 || point[3]) begin
  78. num[15: 0] <= {data3,data2,data1,data0};
  79. num[23:20] <= 4'd10; //第6位不显示任何字符
  80. if(sign) //如果需要显示负号,则最高位(第5位)为符号位
  81. num[19:16] <= 4'd11;
  82. else //不需要显示负号时,则第5位不显示任何字符
  83. num[19:16] <= 4'd10;
  84. end
  85. else begin //如果显示数据为3位十进制数,则给低3位数码管赋值
  86. if (data2 || point[2]) begin
  87. num[11: 0] <= {data2,data1,data0};
  88. //第6、5位不显示任何字符
  89. num[23:16] <= {2{4'd10}};
  90. if(sign) //如果需要显示负号,则最高位(第4位)为符号位
  91. num[15:12] <= 4'd11;
  92. else //不需要显示负号时,则第4位不显示任何字符
  93. num[15:12] <= 4'd10;
  94. end
  95. else begin //如果显示数据为2位十进制数,则给低2位数码管赋值
  96. if (data1 || point[1]) begin
  97. num[ 7: 0] <= {data1,data0};
  98. //654位不显示任何字符
  99. num[23:12] <= {3{4'd10}};
  100. if(sign) //如果需要显示负号,则最高位(第3位)为符号位
  101. num[11:8] <= 4'd11;
  102. else //不需要显示负号时,则第3位不显示任何字符
  103. num[11:8] <= 4'd10;
  104. end
  105. else begin //如果显示数据为1位十进制数,则给最低位数码管赋值
  106. num[3:0] <= data0;
  107. //第6、5位不显示任何字符
  108. num[23:8] <= {4{4'd10}};
  109. if(sign) //如果需要显示负号,则最高位(第2位)为符号位
  110. num[7:4] <= 4'd11;
  111. else //不需要显示负号时,则第2位不显示任何字符
  112. num[7:4] <= 4'd10;
  113. end
  114. end
  115. end
  116. end
  117. end
  118. end
  119. end
  120. //每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号
  121. always @ (posedge dri_clk or negedge rst_n) begin
  122. if (rst_n == 1'b0) begin
  123. cnt0 <= 13'b0;
  124. flag <= 1'b0;
  125. end
  126. else if (cnt0 < MAX_NUM - 1'b1) begin
  127. cnt0 <= cnt0 + 1'b1;
  128. flag <= 1'b0;
  129. end
  130. else begin
  131. cnt0 <= 13'b0;
  132. flag <= 1'b1;
  133. end
  134. end
  135. //cnt_sel从0计数到5,用于选择当前处于显示状态的数码管
  136. always @ (posedge dri_clk or negedge rst_n) begin
  137. if (rst_n == 1'b0)
  138. cnt_sel <= 3'b0;
  139. else if(flag) begin
  140. if(cnt_sel < 3'd5)
  141. cnt_sel <= cnt_sel + 1'b1;
  142. else
  143. cnt_sel <= 3'b0;
  144. end
  145. else
  146. cnt_sel <= cnt_sel;
  147. end
  148. //控制数码管位选信号,使6位数码管轮流显示
  149. always @ (posedge dri_clk or negedge rst_n) begin
  150. if(!rst_n) begin
  151. seg_sel <= 6'b111111; //位选信号低电平有效
  152. num_disp <= 4'b0;
  153. dot_disp <= 1'b1; //共阳极数码管,低电平导通
  154. end
  155. else begin
  156. if(en) begin
  157. case (cnt_sel)
  158. 3'd0 :begin
  159. seg_sel <= 6'b111110; //显示数码管最低位
  160. num_disp <= num[3:0] ; //显示的数据
  161. dot_disp <= ~point[0]; //显示的小数点
  162. end
  163. 3'd1 :begin
  164. seg_sel <= 6'b111101; //显示数码管第1
  165. num_disp <= num[7:4] ;
  166. dot_disp <= ~point[1];
  167. end
  168. 3'd2 :begin
  169. seg_sel <= 6'b111011; //显示数码管第2
  170. num_disp <= num[11:8];
  171. dot_disp <= ~point[2];
  172. end
  173. 3'd3 :begin
  174. seg_sel <= 6'b110111; //显示数码管第3
  175. num_disp <= num[15:12];
  176. dot_disp <= ~point[3];
  177. end
  178. 3'd4 :begin
  179. seg_sel <= 6'b101111; //显示数码管第4
  180. num_disp <= num[19:16];
  181. dot_disp <= ~point[4];
  182. end
  183. 3'd5 :begin
  184. seg_sel <= 6'b011111; //显示数码管最高位
  185. num_disp <= num[23:20];
  186. dot_disp <= ~point[5];
  187. end
  188. default :begin
  189. seg_sel <= 6'b111111;
  190. num_disp <= 4'b0;
  191. dot_disp <= 1'b1;
  192. end
  193. endcase
  194. end
  195. else begin
  196. seg_sel <= 6'b111111; //使能信号为0时,所有数码管均不显示
  197. num_disp <= 4'b0;
  198. dot_disp <= 1'b1;
  199. end
  200. end
  201. end
  202. //控制数码管段选信号,显示字符
  203. always @ (posedge dri_clk or negedge rst_n) begin
  204. if (!rst_n)
  205. seg_led <= 8'hc0;
  206. else begin
  207. case (num_disp)
  208. 4'd0 : seg_led <= {dot_disp,7'b1000000}; //显示数字 0
  209. 4'd1 : seg_led <= {dot_disp,7'b1111001}; //显示数字 1
  210. 4'd2 : seg_led <= {dot_disp,7'b0100100}; //显示数字 2
  211. 4'd3 : seg_led <= {dot_disp,7'b0110000}; //显示数字 3
  212. 4'd4 : seg_led <= {dot_disp,7'b0011001}; //显示数字 4
  213. 4'd5 : seg_led <= {dot_disp,7'b0010010}; //显示数字 5
  214. 4'd6 : seg_led <= {dot_disp,7'b0000010}; //显示数字 6
  215. 4'd7 : seg_led <= {dot_disp,7'b1111000}; //显示数字 7
  216. 4'd8 : seg_led <= {dot_disp,7'b0000000}; //显示数字 8
  217. 4'd9 : seg_led <= {dot_disp,7'b0010000}; //显示数字 9
  218. 4'd10: seg_led <= 8'b11111111; //不显示任何字符
  219. 4'd11: seg_led <= 8'b10111111; //显示负号(-)
  220. default:
  221. seg_led <= {dot_disp,7'b1000000};
  222. endcase
  223. end
  224. end
  225. endmodule
led灯
  1. module led_ctrl(
  2. input sys_clk , //系统时钟
  3. input sys_rst_n , //系统复位信号,低电平有效
  4. input repeat_en , //重复码触发信号
  5. output reg led //LED灯
  6. );
  7. //reg define
  8. reg repeat_en_d0 ; //repeat_en信号打拍采沿
  9. reg repeat_en_d1 ;
  10. reg [22:0] led_cnt ; //LED灯计数器,用于控制LED灯亮灭
  11. //wire define
  12. wire pos_repeat_en;
  13. //*****************************************************
  14. //** main code
  15. //*****************************************************
  16. assign pos_repeat_en = ~repeat_en_d1 & repeat_en_d0;
  17. repeat_en信号打拍采沿
  18. always @(posedge sys_clk or negedge sys_rst_n) begin
  19. if(!sys_rst_n) begin
  20. repeat_en_d0 <= 1'b0;
  21. repeat_en_d1 <= 1'b0;
  22. end
  23. else begin
  24. repeat_en_d0 <= repeat_en;
  25. repeat_en_d1 <= repeat_en_d0;
  26. end
  27. end
  28. always @(posedge sys_clk or negedge sys_rst_n) begin
  29. if(!sys_rst_n) begin
  30. led_cnt <= 23'd0;
  31. led <= 1'b0;
  32. end
  33. else begin
  34. if(pos_repeat_en) begin
  35. led_cnt <= 23'd5_000_000; //单次重复码:亮80ms 灭20ms
  36. led <= 1'b1; //led亮的时间:4_000_000*20ns=80ms
  37. end
  38. else if(led_cnt != 23'd0) begin
  39. led_cnt <= led_cnt - 23'd1;
  40. if(led_cnt < 23'd1_000_000) //led灭的时间:1_000_000*20ns=20ms
  41. led <= 1'b0;
  42. end
  43. end
  44. end
  45. endmodule

TB仿真文件

  1. `timescale 1ns/1ns
  2. module remote_rcv_tb();
  3. parameter T = 20;
  4. parameter ADDR_CODE = 8'h5a; // 8'b01011010
  5. parameter DATA_CODE = 8'h45;
  6. reg sys_clk;
  7. reg sys_rst_n;
  8. reg remote_in;
  9. reg [7:0] data;
  10. wire repeat_en;
  11. wire data_en;
  12. wire [7:0] remote_data;
  13. initial begin
  14. sys_clk = 1'b0;
  15. sys_rst_n = 1'b0;
  16. #(T+1)
  17. sys_rst_n = 1'b1;
  18. end
  19. always #(T/2) sys_clk = ~sys_clk;
  20. initial begin
  21. remote_in = 1'b1;
  22. data = 8'd0;
  23. #100_000 remote_in = 1'b0;
  24. #9_000_000 remote_in = 1'b1;
  25. #4_500_000 remote_in = 1'b0;
  26. data = ADDR_CODE;
  27. repeat(8) begin
  28. #560_000 remote_in = 1'b1;
  29. if(data[0])
  30. #1_690_000 remote_in = 1'b0;
  31. else
  32. #560_000 remote_in = 1'b0;
  33. data = data >>1;
  34. end
  35. data = ~ADDR_CODE;
  36. repeat(8) begin
  37. #560_000 remote_in = 1'b1;
  38. if(data[0])
  39. #1_690_000 remote_in = 1'b0;
  40. else
  41. #560_000 remote_in = 1'b0;
  42. data = data >>1;
  43. end
  44. data = DATA_CODE;
  45. repeat(8) begin
  46. #560_000 remote_in = 1'b1;
  47. if(data[0])
  48. #1_690_000 remote_in = 1'b0;
  49. else
  50. #560_000 remote_in = 1'b0;
  51. data = data >>1;
  52. end
  53. data = ~DATA_CODE;
  54. repeat(8) begin
  55. #560_000 remote_in = 1'b1;
  56. if(data[0])
  57. #1_690_000 remote_in = 1'b0;
  58. else
  59. #560_000 remote_in = 1'b0;
  60. data = data >>1;
  61. end
  62. #560_000 remote_in = 1'b1;
  63. // 重复码
  64. #50_000_000 remote_in = 1'b0;
  65. #9_000_000 remote_in = 1'b1;
  66. #2_250_000 remote_in = 1'b0;
  67. #560_000 remote_in = 1'b1;
  68. end
  69. remote_rcv u_remote_rcv(
  70. .sys_clk (sys_clk),
  71. .sys_rst_n (sys_rst_n),
  72. .remote_in (remote_in),
  73. .repeat_en (repeat_en),
  74. .data_en (data_en),
  75. .data (remote_data)
  76. );
  77. endmodule

六、上板子验证

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/360541
推荐阅读
相关标签
  

闽ICP备14008679号