[C]
纯文本查看
复制代码
本设计根据网上最多的那个红外解码模块改写而成。其中IR为红外接收头的输入,key_db为按键值输出总线key_int为接收到红外信号后的中断信息,默认时低电平,当开始解码时,则跳至高电平,当解码完成,则跳至低电平。可以用来作为nios II处理器的一个外部中断请求,使用时在只需要在qsys或sopc builder中加入一个带中断输入功能的IO口,IO中断设为下降沿触发即可。当中断到来时,读取key_db总线的值。key_db采用通用的输入PIO,无需再开中断功能。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 endreg key_int; always @ (posedge clk) if (!rst_n) irda_data <= 16'd0; else if ((data_cnt ==6'd32) & irda_reg1) beginkey_db <= get_data[15:8]; //数据码key_int<=1'b0; end else if(data_cnt ==6'd1)key_int<=1'b1; endmodule --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------c程序设计部分//本设计中因为采用了彩屏,所以程序中有部分和彩屏相关的代码,不用,根据你自己的理解,删掉即可。若再有不懂,欢迎加QQ:528369266#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;}
verilog写的红外解码模块,可与nios II接口作为按键输入,也可直接与其他verilog编写的FPGA模块连接.rar(3.16 KB )
0