热度 29
2014-4-29 19:54
3028 次阅读|
3 个评论
本设计根据网上最多的那个红外解码模块改写而成。其中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 key_db; output key_int; output clk_test; reg led_cs; reg key_db; reg irda_data; // save irda data,than send to 7 segment led reg get_data; // use for saving 32 bytes irda data reg data_cnt; // 32 bytes irda data counter reg 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 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 counter; //分频1750次 reg 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 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 = 1'b0; else if (high) get_data = 1'b1; else error_flag = 1'b1; get_data = get_data ; 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 ; //数据码 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; }