欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

学习笔记:FPGA之多终端点歌系统设计二:红外通信协议

程序员文章站 2022-04-30 11:31:52
...

一,红外通信原理

https://blog.csdn.net/linglongqiongge/article/details/46366051

二,红外通信协议

    在同一个遥控电路中通常要使用实现不同的遥控功能或区分不同的机器类型,这样就要求信号按一定的编码传送,编码则会由编码芯片或电路完成。对应于编码芯片通常会有相配对的解码芯片或包含解码模块的应用芯片。我们以NEC协议为例:

学习笔记:FPGA之多终端点歌系统设计二:红外通信协议

    从图中可以看出,NEC协议按键输出有两种方式:一种是每次按键都输出完整的一帧数据,另一种是按下相同的按键后每发送完整的一帧数据后,再发送重复码,直到按键被松开、 

学习笔记:FPGA之多终端点歌系统设计二:红外通信协议

    从图中可以看出,一帧数据主要包括了引导码,用户码,用户反码,数据码和数据反码:

(1)引导码:主要是用来告诉红外接头准备接受红外遥控码;

(2)第一段用户码:主要是用于识别不同的红外遥控器芯片;

(3)第二段用户码:可以在红外遥控器应用电路中设置成第一段用户码的反码;

(4)数据码:主要是用于发送红外遥控器数据的;

(5)数据反码:数据码反相后的编码,编码时可以用于对数据的纠错。

    从图中可以看出,红外遥控器发射代码时,均是高位在前,低位在后,其中引导码高电平位9ms,低电平位4.5ms,"0"高电平为0.56ms,低电平0.565ms。"1"高电平为0.56ms,低电平为1.69ms。

    红外遥控器给红外接收头发送数据,首先经38KHZ的载频进行二次调制以提高发射效率,达到降低电源功耗的目的,然后再通过红外发射二极管产生红外线向空间发射,最后红外接收头接受红外线进行解码。

学习笔记:FPGA之多终端点歌系统设计二:红外通信协议

     从图中可以看出,由于红外遥控器发送出的数据需要进过一次反相器,反相器的作用是0变1,所以接受的数据时反相后的数据,与原协议高低电平相反。

然后看重复码帧:

学习笔记:FPGA之多终端点歌系统设计二:红外通信协议

从图中可以看出,重复码的时序图相对来说就简单多了,首先是一个9ms的高电平,接着是一个2.25ms的低电平,最后再加上一个0.56ms的高电平就完成了一次传输。

三,红外通信的实际应用

(1)发送0-9控制蜂鸣器发出不同的音调

(2)功能模块

学习笔记:FPGA之多终端点歌系统设计二:红外通信协议

 

(3)verilog代码

//红外模块
/*Timing control.
`define HEAD_HIGH 	 24'h1_5F90 // 9.000ms @ 10MHz, standard is 24'h1_5F90
`define HEAD_LOW		 24'hAFC8 	// 4.500ms @ 10MHz, standard is 24'hAFC8
`define BIT_0_HIGH	 24'h15E0   // 0.560ms @ 10MHz, standard is 24'h15E0
`define BIT_0_LOW		 24'h1612   // 0.565ms @ 10MHz, standard is 24'h1612
`define BIT_1_HIGH	 24'h15E0   // 0.560ms @ 10MHz, standard is 24'h15E0
`define BIT_1_LOW		 24'h41D2 	// 1.685ms @ 10MHz, standard is 24'h41D2
`define REP_HEAD_HIGH 24'h1_5F90 // 9.000ms @ 10MHz, standard is 24'h1_5F90
`define REP_HEAD_LOW	 24'h57E4 	// 2.250ms @ 10MHz, standard is 24'h57E4
`define REP_BIT_HIGH	 24'h15E0   // 0.560ms @ 10MHz, standard is 24'h15E0
`define REP_BIT_LOW	 24'h3_0D40 // 20.00ms @ 10MHz, standard is 24'h3_0D40 */

//high_time 为红外引导码9ms低电平,(high_time[23:16] == `HEAD_HIGH) 
//24'h1_5F90 = 0001_0101_1111_1001_0000 的[23:16]是(0001) = 10'h1
//24'h1_5F90 = 0001_0000_0000_0000_0000 = 24'h10000 X 100ns = 6.55ms

`define HEAD_HIGH 	 10'h1		//0001_0101_1111_1001_0000 约 6.55ms
`define HEAD_LOW		 10'h1		//0000_1010_1111_1100_1000 约 3.28ms
`define BIT_0_HIGH	 10'h1		//0000_0001_0101_1110_0000 约 0.41ms
`define BIT_0_LOW		 10'h1		//0000_0001_0110_0001_0010 约 0.41ms
`define BIT_1_HIGH	 10'h1		//0000_0001_0110_0001_0010 约 0.41ms
`define BIT_1_LOW		 10'h1		//0000_0100_0001_1101_0010 约 1.64ms   
`define REP_HEAD_HIGH 10'h1		//0001_0101_1111_1001_0000 约 6.55ms
`define REP_HEAD_LOW	 10'h1		//0000_0101_0111_1110_0100 约 1.64ms
`define REP_BIT_HIGH	 10'h1		//0000_0001_0101_1110_0000 约 0.41ms
`define REP_BIT_LOW	 10'h1		//0011_0000_1101_0100_0000 约13.11ms

module Ir_Module
(
	//输入端口
	CLK_10M,RST_N,IR_DATA,
	//输出端口
	o_ir_data
);

//---------------------------------------------------------------------------
//--	外部端口声明
//---------------------------------------------------------------------------
input						CLK_10M;				//系统时钟
input						RST_N;				//系统复位
input						IR_DATA;				//红外输入管脚
output  	[ 7:0]		o_ir_data;			//从红外读出的数据

//---------------------------------------------------------------------------
//--	内部端口声明
//---------------------------------------------------------------------------
reg		[ 3:0]		ir_fsm_cs;			//状态机的当前状态
reg		[ 3:0]		ir_fsm_ns;			//状态机的下一个状态
reg		[23:0]		time_cnt;			//计时器
reg		[23:0]		time_cnt_n;			//time_cnt的下一个状态
reg		[23:0]		low_time;			//低电平计时器(实际是高电平)
reg		[23:0]		low_time_n;			//low_time的下一个状态
reg		[23:0]		high_time;			//高电平计时器(实际是低电平)
reg		[23:0]		high_time_n;   	//high_time的下一个状态
reg		[ 7:0]		bit_cnt;				//用来记录8位串行红外数据组成一个字节
reg		[ 7:0]		bit_cnt_n;			//bit_cnt的下一个状态
reg		[ 1:0]		detect_edge;		//检测边沿寄存器
wire		[ 1:0]		detect_edge_n;		//detect_edge的下一个状态
reg		[31:0]		ir_data;				//从红外读出的数据
reg		[31:0]		ir_data_n;			//ir_data的下一个状态
reg		[31:0]		ir_data_reg;		//红外数据的缓存寄存器
reg		[31:0]		ir_data_reg_n;		//ir_data_reg的下一个状态
reg						posedge_reg;		//检测上升沿
wire						posedge_reg_n;		//posedge_reg的下一个状态
wire						head_code;			//红外引导码
wire						bit_0_code;			//逻辑0(实际逻辑1)
wire						bit_1_code;			//逻辑1(实际逻辑0)
wire						rep_head_code;		//重复引导码
wire						rep_bit_code; 		//重复码

parameter				FSM_IDLE     		= 4'h0;	//空闲状态
parameter				FSM_DATA 			= 4'h1;	//接收数据状态
parameter				FSM_FINISH	 		= 4'h2;	//完成状态

//时序电路,用来给detect_edge寄存器赋值
always @ (posedge CLK_10M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		detect_edge <= 2'h0;					//初始化detect_edge值
	else
		detect_edge <= detect_edge_n;		//用来给detect_edge赋值
end

//组合电路,检测上升沿
assign detect_edge_n = {detect_edge[0] , {~IR_DATA}};//将红外信号取反并接收

//时序电路,用来给posedge_reg寄存器赋值
always @ (posedge CLK_10M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		posedge_reg <= 1'h0;					//初始化posedge_reg值
	else
		posedge_reg	<= posedge_reg_n;		//用来给posedge_reg赋值
end

//组合电路,判断上升沿,如果detect_edge等于01,posedge_reg_n就置1
assign posedge_reg_n = (detect_edge == 2'b01) ? 1'b1 : 1'b0; 

//时序电路,用来给time_cnt寄存器赋值
always @ (posedge CLK_10M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		time_cnt	<= 24'h0;					//初始化time_cnt值
	else
		time_cnt	<= time_cnt_n;				//用来给time_cnt赋值
end

//组合电路,计数器用于记录高电平或者低电平的脉冲宽度
always @ (*)
begin
	if(detect_edge[0] != detect_edge[1])//判断电平变化
		time_cnt_n = 24'h0;					//如果红外信号发生变化,time_cnt_n就从0开始计数
	else
		time_cnt_n = time_cnt + 24'h1;	//否则,time_cnt就加1
end

//时序电路,用来给high_time寄存器赋值
always @ (posedge CLK_10M or negedge RST_N)
begin	
	if(!RST_N)									//判断复位
		high_time <= 24'h0;					//初始化high_time值
	else
		high_time <= high_time_n;			//用来给high_time赋值
end

//组合电路,实际记录的是IR_DATA上的低电平宽度,因为上面对IR_DATA做了一次取反操作
always @ (*)
begin
	if(detect_edge == 2'b10)				//判断下降沿			
		high_time_n = time_cnt;				//如果判断为下降沿,则开始计数
	else
		high_time_n = high_time;			//否则保持不变
end

//时序电路,用来给low_time寄存器赋值
always @ (posedge CLK_10M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		low_time <= 24'h0;					//初始化low_time值
	else
		low_time <= low_time_n;				//用来给low_time赋值
end

//组合电路,实际记录的是IR_DATA上的高电平宽度,因为上面对IR_DATA做了一次取反操作
always @ (*)
begin
	if(IR_DATA)									//判断高电平
		low_time_n = time_cnt;				//如果判断为高电平,则开始计数
	else
		low_time_n = low_time;				//当IR_DATA变成0时就保持不变
end

//低电平至少6.55ms,高电平至少3.28ms,就被认为是引导码,head_code就为1
assign head_code = (high_time[23:16] == `HEAD_HIGH) && (low_time[23:15] == `HEAD_LOW) && posedge_reg;
//低电平至少0.41ms,高电平至少0.41ms,被认为是逻辑"0",bit_0_code就为1
assign bit_0_code = (high_time[23:12] == `BIT_0_HIGH) && (low_time[23:12] == `BIT_0_LOW) && posedge_reg;
//低电平至少0.41ms,高电平至少1.64ms,被认为是逻辑"1",bit_1_code就为0
assign bit_1_code = (high_time[23:12] == `BIT_1_HIGH) && (low_time[23:14] == `BIT_1_LOW) && posedge_reg;
//重复引导码
assign rep_head_code = (high_time[23:16] == `REP_HEAD_HIGH) && (low_time[23:14] == `REP_HEAD_LOW) && posedge_reg;
//重复码
assign rep_bit_code = (high_time[23:12] == `REP_BIT_HIGH) && (low_time[23:17] == `REP_BIT_LOW) && posedge_reg; 

//时序电路,用来给bit_cnt赋值的
always @ (posedge CLK_10M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		bit_cnt <= 8'h0;						//初始化bit_cnt
	else
		bit_cnt <= bit_cnt_n;				//用来给bit_cnt赋值
end

//组合电路,用来记录8位串行红外数据组成一个字节
always @ (*)
begin
	if(ir_fsm_cs != FSM_DATA)				//判断状态机当前状态是否在接收数据状态
		bit_cnt_n = 8'h0;						//如果不等于,bit_cnt_n则清零
	else if((ir_fsm_cs == FSM_DATA) && posedge_reg)//判断状态机当前状态是否在接收数据状态以及是否在上升沿
		bit_cnt_n = bit_cnt + 8'h1;		//如果条件成立,则记录8位串行红外数据
	else
		bit_cnt_n = bit_cnt;					//否则保持不变
end

//时序电路,用来给ir_fsm_cs赋值的
always @ (posedge CLK_10M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		ir_fsm_cs <= FSM_IDLE;				//初始化ir_fsm_cs的值
	else
		ir_fsm_cs <= ir_fsm_ns;				//用来给ir_fsm_cs赋值
end

//组合电路,状态机的控制核心
always @ (*)
begin
	case(ir_fsm_cs)							//判断当前的状态
	
		FSM_IDLE: 								//空闲状态
			if(head_code)						//判断引导码
				ir_fsm_ns = FSM_DATA;		//进入接收数据状态
			else 
				ir_fsm_ns = ir_fsm_cs;   	//否则保持不变
		FSM_DATA: 								//接收数据状态
			if(bit_cnt == 8'h20)				//接收4个字节(地址码,地址反码,命令码,命令反码)
				ir_fsm_ns = FSM_FINISH;		//接收完毕后,进入完成状态
			else if(rep_head_code || head_code || rep_bit_code)	
				ir_fsm_ns = FSM_IDLE;		//进入空闲状态
			else 
				ir_fsm_ns = ir_fsm_cs;		//否则保持不变
		FSM_FINISH:								//完成状态
				ir_fsm_ns = FSM_IDLE;		//进入空闲状态	
		default:ir_fsm_ns = FSM_IDLE; 
	
	endcase
end

//时序电路,用来给ir_data_reg赋值的
always @ (posedge CLK_10M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		ir_data_reg	<= 32'h0;				//初始化ir_data_reg
	else
		ir_data_reg	<= ir_data_reg_n;		//用来给ir_data_reg赋值的
end

//组合电路,记录接收到的串行码32bit,每接收一位,判断是0还是1后移位保存。
always @ (*)
begin
	if(ir_fsm_cs == FSM_IDLE)				//判断状态机的状态
		ir_data_reg_n = 32'hFFFF;
	else if((ir_fsm_cs == FSM_DATA) && (bit_1_code))
		ir_data_reg_n = {ir_data_reg[30:0] , 1'h1};	
	else if((ir_fsm_cs == FSM_DATA) && (bit_0_code))
		ir_data_reg_n = {ir_data_reg[30:0] , 1'h0};		
	else
		ir_data_reg_n = ir_data_reg;		//否则保持不变
end

//时序电路,用来给ir_data赋值的
always @ (posedge CLK_10M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		ir_data <= 32'h0;						//初始化ir_data
	else
		ir_data <= ir_data_n;				//用来给ir_data赋值
end

//组合电路,解码完成的状态就可以读取值了
always @ (*)
begin
	if(ir_fsm_ns == FSM_FINISH)			//判断状态机的状态
		ir_data_n = ir_data_reg;			//解码完成的状态就可以读取值了
	else
		ir_data_n = ir_data;					//否则保持不变
end

assign o_ir_data = {ir_data[8],ir_data[9],ir_data[10],ir_data[11],ir_data[12],ir_data[13],ir_data[14],ir_data[15]};

endmodule

//
module Beep_Module
(
	//输入端口
	CLK_50M,RST_N,KEY,
	//输出端口
	BEEP
);

//---------------------------------------------------------------------------
//--	外部端口声明
//---------------------------------------------------------------------------
input					CLK_50M;					//时钟的端口,开发板用的50MHz晶振
input					RST_N;					//复位的端口,低电平复位
input 	[ 7:0]	KEY;						//按键端口
output				BEEP;						//蜂鸣器端口

//---------------------------------------------------------------------------
//--	内部端口声明
//---------------------------------------------------------------------------
reg		[15:0]	time_cnt;				//用来控制蜂鸣器发声频率的定时计数器
reg		[15:0]	time_cnt_n;				//time_cnt的下一个状态
reg		[15:0]	freq;						//各种音调的分频值
reg		[15:0]	freq_n;					//各种音调的分频值
reg					beep_reg;				//用来控制蜂鸣器发声的寄存器
reg					beep_reg_n;				//beep_reg的下一个状态

//---------------------------------------------------------------------------
//--	逻辑功能实现	
//---------------------------------------------------------------------------
//时序电路,用来给time_cnt寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		time_cnt <= 16'b0;						//初始化time_cnt值
	else
		time_cnt <= time_cnt_n;				//用来给time_cnt赋值
end

//组合电路,判断频率,让定时器累加 
always @ (*)
begin
	if(time_cnt == freq)						//判断分频值
		time_cnt_n = 16'b0;					//定时器清零操作
	else
		time_cnt_n = time_cnt + 1'b1;		//定时器累加操作

end

//时序电路,用来给beep_reg寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		beep_reg <= 1'b0;						//初始化beep_reg值
	else
		beep_reg <= beep_reg_n;				//用来给beep_reg赋值
end

//组合电路,判断频率,使蜂鸣器发声
always @ (*)
begin
	if(time_cnt == freq)						//判断分频值
		beep_reg_n = ~beep_reg;				//改变蜂鸣器的状态
	else
		beep_reg_n = beep_reg;				//蜂鸣器的状态保持不变
end

//时序电路,用来给beep_reg寄存器赋值
always @ (posedge CLK_50M or negedge RST_N)
begin
	if(!RST_N)									//判断复位
		freq <= 16'b0;							//初始化beep_reg值
	else
		freq <= freq_n;						//用来给beep_reg赋值
end

//组合电路,按键选择分频值来实现蜂鸣器发出不同声音
//中音do的频率为523.3hz,freq = 50 * 10^6 / (523 * 2) = 47774
always @ (*)
begin
	case(KEY)
		8'h16: freq_n = 16'd0;			//没有声音
		8'h0C: freq_n = 16'd47774; 	//中音1的频率值262Hz
		8'h18: freq_n = 16'd42568; 	//中音2的频率值587.3Hz
		8'h5E: freq_n = 16'd37919; 	//中音3的频率值659.3Hz
		8'h08: freq_n = 16'd35791; 	//中音4的频率值698.5Hz
		8'h1C: freq_n = 16'd31888; 	//中音5的频率值784Hz
		8'h5A: freq_n = 16'd28409; 	//中音6的频率值880Hz
		8'h42: freq_n = 16'd25309; 	//中音7的频率值987.8Hz
		8'h52: freq_n = 16'd23889; 	//高音1的频率值1046.5Hz
		8'h4A: freq_n = 16'd21276; 	//高音2的频率值1175Hz
		default	  : freq_n = freq;
	endcase
end

assign BEEP = beep_reg;		//最后,将寄存器的值赋值给端口BEEP

endmodule



 

相关标签: FPGA