原创 FPGA经验分享——通用红外解码模块的实现,可与niosII处理器通过简单的PIO核对接,也可直接与硬件逻辑模块对接,作为通用按键输入使用。

2014-4-29 19:54 3069 26 29 分类: FPGA/CPLD

本设计根据网上最多的那个红外解码模块改写而成。其中IR为红外接收头的输入,key_db为按键值输出总线
key_int为接收到红外信号后的中断信息,默认时低电平,当开始解码时,则跳至高电平,当解码完成,则跳至
低电平。可以用来作为nios II处理器的一个外部中断请求,使用时在只需要在qsys或sopc builder中加入一个带
中断输入功能的IO口,IO中断设为下降沿触发即可。当中断到来时,读取key_db总线的值。key_db采用
通用的输入PIO,无需再开中断功能。

clk_test为调试时作为嵌入式逻辑分析仪的采样信号,实际使用时可将相关代码删去。这里只发代码,若有想要工程的,欢迎索取。

注:clk为50M,若非50M,则无法正确进行红外解码。解决方法:1、用锁相环产生一个50M的时钟;2、修改counter的值,只要保证counter每计数时间到35us清零一次即可。

module my_IR(clk,rst_n,IR,key_db,key_int,clk_test);

  input   clk;
  input   rst_n;
  input   IR;
  output [7:0] key_db;
 output key_int;
 output clk_test;
 
  reg [3:0] led_cs;
  reg [7:0] key_db;
 
  reg [15:0] irda_data;    // save irda data,than send to 7 segment led
  reg [31:0] get_data;     // use for saving 32 bytes irda data
  reg [5:0]  data_cnt;     // 32 bytes irda data counter
  reg [2:0]  cs,ns;
  reg error_flag;          // 32 bytes data期间,数据错误标志

  //----------------------------------------------------------------------------
  reg irda_reg0;       //为了避免亚稳态,避免驱动多个寄存器,这一个不使用。
  reg irda_reg1;       //这个才可以使用,以下程序中代表irda的状态
  reg irda_reg2;       //为了确定irda的边沿,再打一次寄存器,以下程序中代表irda的前一状态
  wire irda_neg_pulse; //确定irda的下降沿
  wire irda_pos_pulse; //确定irda的上升沿
  wire irda_chang;     //确╥rda的跳变沿
 
  reg[15:0] cnt_scan;//扫描频率计数器
  
  always @ (posedge clk) //在此采用跟随寄存器
    if(!rst_n)
      begin
        irda_reg0 <= 1'b0;
        irda_reg1 <= 1'b0;
        irda_reg2 <= 1'b0;
      end
    else
      begin
        led_cs <= 4'b0000; //是数码管的位选择处于导通状态
        irda_reg0 <= IR;
        irda_reg1 <= irda_reg0;
        irda_reg2 <= irda_reg1;
      end
    
  assign irda_chang = irda_neg_pulse | irda_pos_pulse;  //IR接收信号的改变,上升或者下降
  assign irda_neg_pulse = irda_reg2 & (~irda_reg1);  //IR接收信号irda下降沿
  assign irda_pos_pulse = (~irda_reg2) & irda_reg1;      //IR接收信号irda上升沿

  //----------------------------------------------------------------------------
  //设计分频和计数部分:从PT2222的规范中我们发现最小的电平3质奔涫?.56ms,而
  //我们在进行采样时,一般都会对最〉牡缙讲裳?6次。也就是说要对0.56ms最少采样16
  //次。 0.56ms/16=35us

//target="_self">开发板上自带的主频?0MHz,即时钟周期为20ns,所以我们需要的分频次数为:
  //              35000/20=1750
  //在设计中我们利用了两个counter,一个counter用于计1750次时钟主频;
  //一个counter用于计算分频之后,同一种电平所scan到的点数,这个点数最后会用来判断
  //是leader的9ms 还是4.5ms,或是数据的 0 还是 1。
  //----------------------------------------------------------------------------
  reg [10:0] counter;  //分频1750次
  reg [8:0]  counter2; //计数分频后的点数
  wire check_9ms;  // check leader 9ms time
  wire check_4ms;  // check leader 4.5ms time
  wire low;        // check  data="0" time
  wire high;       // check  data="1" time
 
  //----------------------------------------------------------------------------
  //分频1750计数
  always @ (posedge clk)
    if (!rst_n)
      counter <= 11'd0;
    else if (irda_chang)  //irda电平跳变了,就重新开始计数
      counter <= 11'd0;
    else if (counter == 11'd1750)
      counter <= 11'd0;
    else
      counter <= counter + 1'b1;
 
  //----------------------------------------------------------------------------
  always @ (posedge clk)
    if (!rst_n)
      counter2 <= 9'd0;
    else if (irda_chang)  //irda电平跳变了,就重新开始计点
      counter2 <= 9'd0;
    else if (counter == 11'd1750)
      counter2 <= counter2 +1'b1;
 
  reg [10:0]cnt3;
  reg clk_test;
  always@(posedge clk)
  begin
  if(cnt3==11'd1750)begin clk_test<=~clk_test;cnt3<=11'b0;end
  else cnt3<=cnt3+1'b1;
  end
 
 

  assign check_9ms = ((217 < counter2) & (counter2 < 297));
  //257  为了增加稳定性,取一定范围
  assign check_4ms = ((88 < counter2) & (counter2 < 168));  //128
  assign low  = ((6 < counter2) & (counter2 < 26));         // 16
  assign high = ((38 < counter2) & (counter2 < 58));        // 48

  //----------------------------------------------------------------------------
  // generate statemachine  状态机
    parameter IDLE       = 3'b000, //初始状态
              LEADER_9   = 3'b001, //9ms
              LEADER_4   = 3'b010, //4ms
              DATA_STATE = 3'b100; //传输数据
 
  always @ (posedge clk)
    if (!rst_n)
      cs <= IDLE;
    else
      cs <= ns; //状态位
    
  always @ ( * )
    case (cs)
      IDLE:
        if (~irda_reg1)
          ns = LEADER_9;
        else
          ns = IDLE;
  
      LEADER_9:
        if (irda_pos_pulse)   //leader 9ms check
          begin
            if (check_9ms)
              ns = LEADER_4;
            else
              ns = IDLE;
          end
        else  //完备的if---else--- ;防止生成latch
          ns =LEADER_9;
  
      LEADER_4:
        if (irda_neg_pulse)  // leader 4.5ms check
          begin
            if (check_4ms)
              ns = DATA_STATE;
            else
              ns = IDLE;
          end
        else
          ns = LEADER_4;
  
      DATA_STATE:
        if ((data_cnt == 6'd32) & irda_reg2 & irda_reg1)
          ns = IDLE;
        else if (error_flag)
          ns = IDLE;
        else
          ns = DATA_STATE;
      default:
        ns = IDLE;
    endcase

  //状态机中的输出,用时序电路来描述
  always @ (posedge clk)
    if (!rst_n)
      begin
        data_cnt <= 6'd0;
        get_data <= 32'd0;
        error_flag <= 1'b0;
      end
 
    else if (cs == IDLE)
      begin
        data_cnt <= 6'd0;
        get_data <= 32'd0;
        error_flag <= 1'b0;
      end
 
    else if (cs == DATA_STATE)
      begin
        if (irda_pos_pulse)  // low 0.56ms check
          begin
            if (!low)  //error
              error_flag <= 1'b1;
          end
        else if (irda_neg_pulse)  //check 0.56ms/1.68ms data 0/1
          begin
            if (low)
              get_data[0] <= 1'b0;
            else if (high)
              get_data[0] <= 1'b1;
            else
              error_flag <= 1'b1;
            
            get_data[31:1] <= get_data[30:0];
            data_cnt <= data_cnt + 1'b1;
          end
      end
  
reg key_int;
  always @ (posedge clk)
    if (!rst_n)
      irda_data <= 16'd0;
    else if ((data_cnt ==6'd32) & irda_reg1)
   begin
  key_db <= get_data[15:8]; //数据码
  key_int<=1'b0;
   end
   else if(data_cnt ==6'd1)key_int<=1'b1;
 
 
endmodule

----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------
c程序设计部分

//本设计中因为采用了彩屏,所以程序中有部分和彩屏相关的代码,不用,根据你自己的理解,删掉即可。若再有不懂,欢迎交流

#include
#include "my_types.h"
#include "system.h"
#include "stdio.h"
#include "priv/alt_legacy_irq.h"    //alt_irq_register()这个函数是在这个文件中申明的,所以必须包含此文件
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"
#include "lcd.h"


alt_u32 key_word;
alt_u8 w1;
void IR_ISR(void *context,unsigned long id);
int IRQ_Init(void);

 


// 调试信息显示开关
#define ENABLE_APP_DEBUG // turn on debug message
#ifdef ENABLE_APP_DEBUG
    #define APP_DEBUG(x)    DEBUG(x)
#else
    #define APP_DEBUG(x)
#endif


// nIRQ中断初始化
int IRQ_Init(void)
{
  IOWR_ALTERA_AVALON_PIO_IRQ_MASK(IR_EXINT_BASE, 1); // 使能中断
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(IR_EXINT_BASE, 0); // 清中断边沿捕获寄存器
  // 注册ISR
  return alt_irq_register(IR_EXINT_IRQ,NULL,IR_ISR);

}
// 中断服务子函数
void IR_ISR(void *context,unsigned long id)
{
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(IR_EXINT_BASE, 1); // 清中断边沿捕获寄存器
  key_word=IORD_ALTERA_AVALON_PIO_DATA(IR_DATA_BASE);
}

int main(void)
{
  lcd_init();
  if(!IRQ_Init())printf("register succeed!\n");
  else printf("register failed!\n");
  while(1);
  return 0;
}

 

 

 

 

PARTNER CONTENT

文章评论3条评论)

登录后参与讨论

用户1567715 2015-9-25 13:21

谢谢博主

用户1455166 2014-7-7 09:20

FPGA经验分享——通用红外解码模块的实现,对开发一些简单自控有用,谢谢交流分享

用户403664 2014-4-30 09:52

这是参加小组的赠书活动嘛?
相关推荐阅读
小梅哥 2019-09-04 22:10
小梅哥FPGA时序分析笔记(6.2)深入现象看本质——庖丁解牛之FPGA内数据传输模型
通过上一节,我们了解了FPGA内部数据的传输形式,接下来我们就可以根据上一节的内容来总结一下FPGA内部的数据传输模型了。 时钟和数据传输路径 通过上一节内容中,我绘制的那个FPGA内部数据在逻辑...
小梅哥 2019-09-01 21:28
小梅哥FPGA时序分析笔记(6.1)深入现象看本质——庖丁解牛之FPGA可编程原理
上一次发博客,已经是2个月前了,这中间两个月,干了件很有意义的事情,尤其是对于自己来说,感觉学到了非常多的知识和经验,每天都很忙,忙到没时间逛网站博客,终于忙完闲下来了,连载的事情可不能忘,终于可以书...
小梅哥 2019-07-02 08:57
小梅哥FPGA时序分析笔记(五)I/O约束显神威——深入龙潭
大家一定对我上一节的突然结尾表示一脸茫然:我是来学习时序约束的,然后你告诉我时序约束里面IO约束很重要,然我又跟着你的文章继续往下看,本以为你就要讲如何进行IO约束了,结果呢,你一个取反时钟就把我们打...
小梅哥 2019-06-30 11:07
小梅哥FPGA时序分析笔记(四)I/O时序定成败——化险为夷
小梅哥FPGA时序分析从遥望到领悟系列没有遇见过I/O时序问题,没有通过I/O约束方式实际解决过I/O时序问题,就很难明白I/O约束的重要性,也很难相信各种EDA软件真的有那么的傻白甜。 我遇到的最...
小梅哥 2019-06-22 10:32
小梅哥FPGA时序分析笔记(三)时钟约束真重要——事实说话
小梅哥FPGA时序分析从遥望到领悟系列以前,那是在以前,经常有网友(原谅我行文动不动就是网友说,网友问,毕竟我是卖开发板的,正面接触学FPGA的网友相对多一些,所以这些也都是事实存在的事情)问我:小梅...
小梅哥 2019-06-21 10:33
小梅哥FPGA时序分析笔记(二)时钟质量是生命——初遇时序
小梅哥FPGA时序分析从遥望到领悟系列第一次遇到时序问题并通过相应的手段解决问题,算是2年前做百兆以太网图像传输的时候了吧。当时遇到的问题为:同一个工程,每次编译结果的效果都不一样,有的时候编译了,下...
我要评论
3
26
关闭 站长推荐上一条 /3 下一条