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

2017-1-10 15:49 1674 12 12

本设计根据网上最多的那个红外解码模块改写而成。其中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

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
12
关闭 站长推荐上一条 /3 下一条