基于FPGA的手势识别(PAJ7620U2)

这篇具有很好参考价值的文章主要介绍了基于FPGA的手势识别(PAJ7620U2)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、PAJ7620U2手势识别模块

1.基本信息

        PAJ7620U2 是原相科技(PixArt)公司推出的一款光学数组式传感器,内置光源和环境光抑制滤波器集成的 LED,镜头和手势感测器在一个小的立方体模组,能在黑暗或低光环境下工作。同时传感器内置手势识别,支持 9 个手势类型和输出的手势中断结果。并且内置接近检测功能,可用于感测物体接近或离开。

        PAJ7620U2 的特点包括:
①IIC 接口,支持高达 400Khz 通信速率。
②内置 9 个手势类型(上、下、左、右、前、后、顺时针旋转、逆时针旋转、挥动),支持输出中断
③支持接近检测功能,检测物体体积大小和亮度。
④待机功耗电流 15uA。
⑤抗灯光干扰

2.工作原理

        PAJ7620U2 工作时通过内部 LED 驱动器,驱动红外 LED向外发射红外线信号,当传感器阵列在有效的距离中探测到物体时,目标信息提取阵列会对探测目标进行特征原始数据的获取,获取的数据会存在寄存器中,同时手势识别阵列会对原始数据进行识别处理,最后将手势结果存到寄存器中,用户可根据 I2C 接口对原始数据和手势识别的结果进行读取 。

3.模式寄存器简介

        PAJ7620U2 的内部有两个 BANK 寄存器区域,分别是 BANK0和 BANK1。不同的区域用于访问不同的功能寄存器, 但想访问其中的 BANK 区域下的寄存器, 需在访问前发送控制指令进入该寄存器区域, 具体控制指令如下图所示:

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

        进入 BANK0 区域往传感器 0xEF 地址写 0x00 数值,而 BANK1 区域往传感器 0xEF 地址写 0x01 数值

4.工作流程

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

初始化流程即配置寄存器,直接调用数据手册提供的配置数组并将其写入BANK0

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

图4 初始化流程

4.1 唤醒操作
4.1.1唤醒操作简介

步骤一:上电,Vbus必须在Vdd之前上电;
步骤二:等待700us,让PAJ7620U2稳定
步骤三:写入从机ID或者是I2C读取指令去唤醒

步骤一我们不用关注,这是生产厂家需要关注的问题,我们看后面两个步骤。步骤二是在写入前先等待700us,为了更好地提升模块性能,这里我们等待1000us,因为该模块是通过I2C协议配置的,我们在初始状态下等待1000us后,再跳转到I2C开始状态。步骤三,我们根据数据手册:

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

所以唤醒指令为8'b1110_0111

4.1.2 唤醒操作步骤
4.1.2.1 状态转移图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.1.2.2 时序图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

        在这里我们必须考虑引入两个信号,一个是结束标志信号i2c_end,一个是模式信号mode。引入mode信号的原因是,我们需要发送不同的指令实现数据的接收及发送,因此这些操作有差异。我们本次的操作为唤醒操作,令mode=0,通过该信号,编写代码时,状态跳转就按唤醒操作来进行,避免后续状态跳转产生影响。

4.2 激活BANK0

        唤醒操作结束后,需要访问0x00寄存器,读取其内部数据,看读取出来的数据是否为0x20。如果为0x20的话,则唤醒操作成功,如果不为0x20,则代表唤醒失败,继续重新唤醒。但是0x00寄存器,归属于BANK0区域,上电后BANK0区域是未被激活的,因此首先要激活BANK0区域,激活后才可以访问内部的0x00寄存器,从而根据返回值来判断是否激活成功。

4.2.1 激活BANK0操作步骤
4.2.1.1 状态转移图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

        初始状态延迟1000us后,跳转到开始状态,检测到开始信号后,跳转到发送从设备地址状态,从设备地址8'b1110_0110发送成功,返回一个响应值。检测到响应值有效,跳转到发送BANK0地址,BANK0地址8'b1110_1111发送完成之后,又接收到一个响应值。检测到响应值有效,跳转到发送寄存器地址状态,寄存器地址0x00数据发送完成,接收到响应信号,响应信号有效,跳转到停止状态。

4.2.1.2 时序图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.3 配置0x00寄存器

        在我们激活BANK0以后,我们是向里面写入了0x00数据,读者需要注意的是,这里写入的0x00不是寄存器地址,而是数值。如果激活BANK1,写入的数值就是0x01了。因此,我们还需要再次写入0x00数据,这里就表示写入的是0x00寄存器,将0x00寄存器配置好,便于我们后续读取操作。

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.3.1 配置步骤
4.3.1.1 状态转移图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.4 读取0x00寄存器

        这一部分的从机地址最后一位会发生变化,变为7位从机ID+1位读操作,从机地址发送完成后,主机放弃对SDA信号线的控制。这时,从机控制SDA信号线,向主机发送ACK响应数据,收到响应后,主机这时还是要将控制权交给从机,从机发送8bit的数据,主机读取这8bit的数据,从高到低依次拼接,看最后拼接成的数据是否为0x20,如果是的话,主机则获取对SDA信号线的控制,并返回一个高电平的NACK信号,发送给从机,最后结束。

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发


        如果接收到的数据不是0x20,则代表唤醒失败,代码中的mode信号一定要清零,同时直接由DATA状态跳转到IDLE状态。

4.4.1 读取步骤
4.4.1.1 状态转移图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.4.1.2 时序图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

        在这个波形图中,多引入了两个信号,一个是error_en错误信号,配合skip_en信号检测读取出来的数据是否是正确的,如果数据是0x20,则skip_en拉高,否则error_en拉高。另一个信号是rec_data信号,除了DATA状态,我们不关心其它时刻它的值为多少,因为不管值为多少,每次读取出来的1bit数据都会一个一个地把原有的数据顶替下去。

4.5 配置寄存器组

        唤醒操作完成后,即代表可以使用这个模块了。所以,我们需要参考官方数据手册,配置寄存器组,配置完成后才能进行数据的读取。

4.5.1 配置步骤

        单个写操作步骤图与前面激活bank0教程的步骤图是相同的。在这里呢,我们对51个寄存器写入数据,不能进行连续写操作,只能单次写入,总共写入51次。将这51个寄存器配置好后,即可进行数据的读取了。

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.5.1.1 状态转移图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

        从IDLE状态到START状态,我们需要略微做一下修改,状态跳转条件修改为接收到一个开始信号。这个开始信号是从另外一个对51个寄存器寄存器配置的模块传入的,i2c控制模块结束信号就作为寄存器配置模块的开始信号,同时,寄存器配置模块的结束信号也作为i2c控制模块的开始信号

4.5.1.2 时序图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发


上述波形图是寄存器配置模块的波形图。

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.5.2 配置寄存器组代码设计
//paj7620寄存器配置模块
module  prj7620_cfg
(
	input	wire			i2c_clk		,
	input	wire			sys_rst_n	,
	input	wire			cfg_start	,
	input	wire	[2:0]	mode		,
	
	output	wire	[23:0]	cfg_data	,
	output	reg				i2c_start	,
	output	reg		[5:0]	reg_num		
);

wire	[23:0]	cfg_data_reg[50:0]	;

always@(posedge i2c_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		reg_num  <=  6'd0  ;
	else  if((cfg_start == 1'b1)&&(mode == 3'd4))
		reg_num  <=  reg_num + 1'b1  ;
	else
		reg_num  <=  reg_num  ;
		
always@(posedge i2c_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		i2c_start  <=  1'b0  ;
	else  if((cfg_start == 1'b1)&&(mode == 3'd4))
		i2c_start  <=  1'b1  ;
	else
		i2c_start  <=  1'b0  ;
		
assign  cfg_data = (mode != 3'd4) ? 24'd0 :cfg_data_reg[reg_num - 1'b1]  ;	
assign  cfg_data_reg[00]	=	{8'b1110_0110,8'hEF,8'h00}  ;
assign  cfg_data_reg[01]	=	{8'b1110_0110,8'h37,8'h07}  ;
assign  cfg_data_reg[02]	=	{8'b1110_0110,8'h38,8'h17}  ;
assign  cfg_data_reg[03]	=	{8'b1110_0110,8'h39,8'h06}  ;
assign  cfg_data_reg[04]	=	{8'b1110_0110,8'h42,8'h01}  ;
assign  cfg_data_reg[05]	=	{8'b1110_0110,8'h46,8'h2D}  ;
assign  cfg_data_reg[06]	=	{8'b1110_0110,8'h47,8'h0F}  ;
assign  cfg_data_reg[07]	=	{8'b1110_0110,8'h48,8'h3C}  ;
assign  cfg_data_reg[08]	=	{8'b1110_0110,8'h49,8'h00}  ;
assign  cfg_data_reg[09]	=	{8'b1110_0110,8'h4A,8'h1E}  ;
assign  cfg_data_reg[10]	=	{8'b1110_0110,8'h4C,8'h20}  ;
assign  cfg_data_reg[11]	=	{8'b1110_0110,8'h51,8'h10}  ;
assign  cfg_data_reg[12]	=	{8'b1110_0110,8'h5E,8'h10}  ;
assign  cfg_data_reg[13]	=	{8'b1110_0110,8'h60,8'h27}  ;
assign  cfg_data_reg[14]	=	{8'b1110_0110,8'h80,8'h42}  ;
assign  cfg_data_reg[15]	=	{8'b1110_0110,8'h81,8'h44}  ;
assign  cfg_data_reg[16]	=	{8'b1110_0110,8'h82,8'h04}  ;
assign  cfg_data_reg[17]	=	{8'b1110_0110,8'h8B,8'h01}  ;
assign  cfg_data_reg[18]	=	{8'b1110_0110,8'h90,8'h06}  ;
assign  cfg_data_reg[19]	=	{8'b1110_0110,8'h95,8'h0A}  ;
assign  cfg_data_reg[20]	=	{8'b1110_0110,8'h96,8'h0C}  ;
assign  cfg_data_reg[21]	=	{8'b1110_0110,8'h97,8'h05}  ;
assign  cfg_data_reg[22]	=	{8'b1110_0110,8'h9A,8'h14}  ;
assign  cfg_data_reg[23]	=	{8'b1110_0110,8'h9C,8'h3F}  ;
assign  cfg_data_reg[24]	=	{8'b1110_0110,8'hA5,8'h19}  ;
assign  cfg_data_reg[25]	=	{8'b1110_0110,8'hCC,8'h19}  ;
assign  cfg_data_reg[26]	=	{8'b1110_0110,8'hCD,8'h0B}  ;
assign  cfg_data_reg[27]	=	{8'b1110_0110,8'hCE,8'h13}  ;
assign  cfg_data_reg[28]	=	{8'b1110_0110,8'hCF,8'h64}  ;
assign  cfg_data_reg[29]	=	{8'b1110_0110,8'hD0,8'h21}  ;
assign  cfg_data_reg[30]	=	{8'b1110_0110,8'hEF,8'h01}  ;
assign  cfg_data_reg[31]	=	{8'b1110_0110,8'h02,8'h0F}  ;	
assign  cfg_data_reg[32]	=	{8'b1110_0110,8'h03,8'h10}  ;
assign  cfg_data_reg[33]	=	{8'b1110_0110,8'h04,8'h02}  ;
assign  cfg_data_reg[34]	=	{8'b1110_0110,8'h25,8'h01}  ;
assign  cfg_data_reg[35]	=	{8'b1110_0110,8'h27,8'h39}  ;
assign  cfg_data_reg[36]	=	{8'b1110_0110,8'h28,8'h7F}  ;
assign  cfg_data_reg[37]	=	{8'b1110_0110,8'h29,8'h08}  ;
assign  cfg_data_reg[38]	=	{8'b1110_0110,8'h3E,8'hFF}  ;
assign  cfg_data_reg[39]	=	{8'b1110_0110,8'h5E,8'h3D}  ;
assign  cfg_data_reg[40]	=	{8'b1110_0110,8'h65,8'h96}  ;
assign  cfg_data_reg[41]	=	{8'b1110_0110,8'h67,8'h97}  ;
assign  cfg_data_reg[42]	=	{8'b1110_0110,8'h69,8'hCD}  ;
assign  cfg_data_reg[43]	=	{8'b1110_0110,8'h6A,8'h01}  ;
assign  cfg_data_reg[44]	=	{8'b1110_0110,8'h6D,8'h2C}  ;
assign  cfg_data_reg[45]	=	{8'b1110_0110,8'h6E,8'h01}  ;
assign  cfg_data_reg[46]	=	{8'b1110_0110,8'h72,8'h01}  ;
assign  cfg_data_reg[47]	=	{8'b1110_0110,8'h73,8'h35}  ;
assign  cfg_data_reg[48]	=	{8'b1110_0110,8'h74,8'h00}  ;
assign  cfg_data_reg[49]	=	{8'b1110_0110,8'h77,8'h01}  ;
assign  cfg_data_reg[50]	=	{8'b1110_0110,8'hEF,8'h00}  ;

endmodule
4.6 手势寄存器配置

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

图4.6 手势寄存器配置流程

        我们的挥手动作将会被0x43或0x44这两个寄存器捕捉到,捕捉完成后读取这两个寄存器内的数据即可

        下面是0x43寄存器8bit数据每位置1的含义:

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

        在这里读取0x43寄存器即可,0x44寄存器主要是执行中断操作,我们不使用该寄存器,在编写代码时检测到数据变化,做输出即可。

4.6.1 配置步骤

因为要进行连续捕获挥手动作,因此在这里必须要使用到连续读操作;连续读操作流程图如下:

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.6.1.1 状态转移图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.6.1.2 时序图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.7 读取手势寄存器数据

在读取手势寄存器中的数据时,不管是采用哪种读方式,读取到数据后,都不会自动停下,这时候结合前面的数据手册,需要我们设置“中断”,当读取到的8位数据不为全0时(即读取到了手势数据),则中断读操作。但是呢,我们采用的是FPGA来配置这个模块,对数据的处理就简单得多,因此只需要检测出该模块数据变化,将变化的数据作为LED灯亮起的触发信号,触发以后LED灯在下次触发信号到来时,一直保持亮起即可。

4.7.1 读取步骤
4.7.1.1 状态转移图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

4.7.1.2 时序图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发


        从波形图可以看出,除了跳转信号外,我们还需要引入两路信号,一路是po_data_reg,这个信号主要是在DATA状态下寄存拼接的数据。第二路信号是po_data,这个信号是在po_data_reg信号拼接完成后,读取DATA状态末尾拼接完成的数据。我们取po_data信号低四位,这低四位数据,某位由0变化为1后,则代表上、下、左、右挥手动作被检测出来,我们利用这个变化来驱动LED灯亮起。

二、程序设计

1.程序框图

基于FPGA的手势识别(PAJ7620U2),FPGA学习,fpga,fpga开发

2.程序设计

module  prj7620_top
  (
    input	wire			clk		,
    input	wire			rst_n	,
    output	wire			scl		,
    output  reg  [3:0]      led     ,
    inout	wire			sda		,
    output  wire  [5:0]  	sel     ,  //  片选
    output  reg   [7:0]  	dig     ,  //  段选
    output  wire			buzzer	  
  );

  parameter ZERO = 7'b1000_000,
  ONE  = 7'b1111_001,
  TWO  = 7'b0100_100,
  THR  = 7'b0110_000,
  FOU  = 7'b0011_001;

  wire	[15:0]	cfg_data	;
  wire			i2c_start	;
  wire	[5:0]	reg_num		;
  wire			cfg_start	;
  wire	[3:0]	mode		;
  wire			i2c_clk		;
  wire    [7:0]   po_data     ;
  reg				en;
  wire			pwm;

  assign	buzzer = en || pwm;
  assign	sel = 6'b111_110    ;		

  /*从高到低,依次分别是右、左、下、上*/
  always@(posedge i2c_clk or negedge rst_n)begin
    if(!rst_n )begin
      led  <=  4'b0000  ;
      dig <= {1'b1,ZERO};
      en <= 1'b1	  ;
    end
    else  if(po_data[3:0] == 4'b0001)begin		//上
      led  <=  4'b0001  ;
      dig <= {1'b1,ONE}  ;
      en <= 1'b0	;
    end
    else  if(po_data[3:0] == 4'b0010)begin		//下
      led  <=  4'b0010  ;
      dig <= {1'b1,TWO}  ;
      en <= 1'b1 	;
    end
    else  if(po_data[3:0] == 4'b0100)begin		//左
      led  <=  4'b0100  ;	
      dig <= {1'b1,THR}  ;
      en <= 1'b0 ;
    end
    else  if(po_data[3:0] == 4'b1000)begin		//右
      led  <=  4'b1000  ;
      dig <= {1'b1,FOU}  ;
      en <= 1'b1 	;
    end
    else begin
      led  <=  led  ;
    end
  end
  i2c_ctrl  i2c_ctrl_inst
  (
    .clk	    (clk		),
    .rst_n	    (rst_n		),
    .cfg_data	(cfg_data	),
    .i2c_start	(i2c_start	),
    .reg_num	(reg_num	),
    .scl		(scl		),
    .cfg_start	(cfg_start	),
    .i2c_clk	(i2c_clk	),
    .mode		(mode		),
    .sda        (sda        ),
    .po_data    (po_data    )
  );

  prj7620_cfg  prj7620_cfg_inst
  (
    .i2c_clk	(i2c_clk	),
    .rst_n	    (rst_n	    ),
    .cfg_start	(cfg_start	),
    .mode		(mode		),
    .cfg_data	(cfg_data	),
    .i2c_start	(i2c_start	),
    .reg_num	(reg_num	)	
  );


  beep beep_inst(
    .clk	    (clk		),
    .rst_n	    (rst_n		),
    .pwm		(pwm)
  );

endmodule
module prj7620_cfg
(
    input	wire			i2c_clk		,
	input	wire			rst_n	,
	input	wire			cfg_start	,
	input	wire	[3:0]	mode		,
	
	output	wire	[15:0]	cfg_data	,
	output	reg				i2c_start	,
	output	reg		[5:0]	reg_num			//寄存器读取计数个数	
);

    wire    [15:0]      cfg_data_reg[50:0];

    always@(posedge i2c_clk or negedge rst_n)
	if(!rst_n)
		reg_num  <=  6'd0  ;
	else  if((cfg_start == 1'b1)&&(mode == 4'd4))
		reg_num  <=  reg_num + 1'b1  ;
	else
		reg_num  <=  reg_num  ;
		
always@(posedge i2c_clk or negedge rst_n)
	if(!rst_n)
		i2c_start  <=  1'b0  ;
	else  if((cfg_start == 1'b1)&&(mode == 4'd4))
		i2c_start  <=  1'b1  ;
	else
		i2c_start  <=  1'b0  ;

    assign cfg_data = (mode != 4'd4) ? 15'd0 : cfg_data_reg[reg_num - 1'b1];

	assign  cfg_data_reg[00]	=	{8'hEF,8'h00}  ;
	assign  cfg_data_reg[01]	=	{8'h37,8'h07}  ;
	assign  cfg_data_reg[02]	=	{8'h38,8'h17}  ;
	assign  cfg_data_reg[03]	=	{8'h39,8'h06}  ;
	assign  cfg_data_reg[04]	=	{8'h42,8'h01}  ;
	assign  cfg_data_reg[05]	=	{8'h46,8'h2D}  ;
	assign  cfg_data_reg[06]	=	{8'h47,8'h0F}  ;
	assign  cfg_data_reg[07]	=	{8'h48,8'h3C}  ;
	assign  cfg_data_reg[08]	=	{8'h49,8'h00}  ;
	assign  cfg_data_reg[09]	=	{8'h4A,8'h1E}  ;
	assign  cfg_data_reg[10]	=	{8'h4C,8'h20}  ;
	assign  cfg_data_reg[11]	=	{8'h51,8'h10}  ;
	assign  cfg_data_reg[12]	=	{8'h5E,8'h10}  ;
	assign  cfg_data_reg[13]	=	{8'h60,8'h27}  ;
	assign  cfg_data_reg[14]	=	{8'h80,8'h42}  ;
	assign  cfg_data_reg[15]	=	{8'h81,8'h44}  ;
	assign  cfg_data_reg[16]	=	{8'h82,8'h04}  ;
	assign  cfg_data_reg[17]	=	{8'h8B,8'h01}  ;
	assign  cfg_data_reg[18]	=	{8'h90,8'h06}  ;
	assign  cfg_data_reg[19]	=	{8'h95,8'h0A}  ;
	assign  cfg_data_reg[20]	=	{8'h96,8'h0C}  ;
	assign  cfg_data_reg[21]	=	{8'h97,8'h05}  ;
	assign  cfg_data_reg[22]	=	{8'h9A,8'h14}  ;
	assign  cfg_data_reg[23]	=	{8'h9C,8'h3F}  ;
	assign  cfg_data_reg[24]	=	{8'hA5,8'h19}  ;
	assign  cfg_data_reg[25]	=	{8'hCC,8'h19}  ;
	assign  cfg_data_reg[26]	=	{8'hCD,8'h0B}  ;
	assign  cfg_data_reg[27]	=	{8'hCE,8'h13}  ;
	assign  cfg_data_reg[28]	=	{8'hCF,8'h64}  ;
	assign  cfg_data_reg[29]	=	{8'hD0,8'h21}  ;
	assign  cfg_data_reg[30]	=	{8'hEF,8'h01}  ;
	assign  cfg_data_reg[31]	=	{8'h02,8'h0F}  ;
	assign  cfg_data_reg[32]	=	{8'h03,8'h10}  ;
	assign  cfg_data_reg[33]	=	{8'h04,8'h02}  ;
	assign  cfg_data_reg[34]	=	{8'h25,8'h01}  ;
	assign  cfg_data_reg[35]	=	{8'h27,8'h39}  ;
	assign  cfg_data_reg[36]	=	{8'h28,8'h7F}  ;
	assign  cfg_data_reg[37]	=	{8'h29,8'h08}  ;
	assign  cfg_data_reg[38]	=	{8'h3E,8'hFF}  ;
	assign  cfg_data_reg[39]	=	{8'h5E,8'h3D}  ;
	assign  cfg_data_reg[40]	=	{8'h65,8'h96}  ;
	assign  cfg_data_reg[41]	=	{8'h67,8'h97}  ;
	assign  cfg_data_reg[42]	=	{8'h69,8'hCD}  ;
	assign  cfg_data_reg[43]	=	{8'h6A,8'h01}  ;
	assign  cfg_data_reg[44]	=	{8'h6D,8'h2C}  ;
	assign  cfg_data_reg[45]	=	{8'h6E,8'h01}  ;
	assign  cfg_data_reg[46]	=	{8'h72,8'h01}  ;
	assign  cfg_data_reg[47]	=	{8'h73,8'h35}  ;
	assign  cfg_data_reg[48]	=	{8'h74,8'h00}  ;
	assign  cfg_data_reg[49]	=	{8'h77,8'h01}  ;
	assign  cfg_data_reg[50]	=	{8'hEF,8'h00}  ;

endmodule
module i2c_ctrl (
    input               clk         ,
    input               rst_n       ,
    input  [15:0]       cfg_data    ,
    input               i2c_start   ,
    input  [5:0]        reg_num     ,
    output              scl         ,
    output  reg         cfg_start   ,
    output  reg         i2c_clk     ,   //分频后的时钟
    output  reg [3:0]   mode        ,
    output  reg [7:0]   po_data     ,
    inout               sda     
);
 

    parameter   CNT_CLK_MAX = 25,         //分频计数
                CNT_DELAY_MAX = 1000  , //计数1000us
                SLAVE_ID = 7'b111_0011,
                BANK_ADDR  = 8'hEF,
                DARA_ADDR = 8'h43;

    parameter IDLE          =4'd0,
              START         =4'd1,
              SLAVE_ADDR    =4'd2,
              WAIT          =4'd3,
              STOP          =4'd4,
              ACK_1         =4'd5,
              ACK_2         =4'd6,
              ACK_3         =4'd7,
              DATA          =4'd8,
              DEVICE_ADDR   =4'd9,
              NACK          =4'd10;


    reg     [4:0]       cnt_clk     ;//分频计数器                

    reg     [3:0]       state_n     ;// 次态
    reg     [3:0]       state_c     ;//现态
    reg                 skip_en_0   ;//唤醒状态跳转条件
    reg                 skip_en_1   ;//激活bank0跳转信号
    reg                 skip_en_2   ;//配置0x00寄存器状态跳转信号
    reg                 skip_en_3   ;//读取0x00寄存器状态跳转信号
    reg                 skip_en_4   ;//配置51个操作寄存器
    reg                 skip_en_5   ;//配置0x43寄存器状态跳转信号
    reg                 skip_en_6   ;//读取0x43寄存器状态跳转信号
    reg                 error_en    ;//读取出来的值不是0x20,错误信号     
    reg     [9:0]       cnt_wait    ;//等待1000us
    reg     [2:0]       cnt_bit     ;//计数传输数据8bit
    reg     [9:0]       cnt_delay   ;// 等待1000us
    reg     [1:0]       cnt_i2c_clk ;
    reg                 i2c_scl     ;
    reg                 i2c_end     ;  
    reg		[7:0]	    po_data_reg	;//采集数据,拼接完成后赋值给po_data     
    reg     [7:0]       slave_addr  ;//不同模式下7'h73+1'bx
    reg                 ACK         ;//应答信号 高电平有效 ~sda_in
    reg     [7:0]       device_addr ;//不同模式下寄存器地址变化
    reg     [7:0]       data        ;  
    reg     [7:0]       re_data    ;//唤醒操作读取0x00寄存器数据寄存

    wire                sda_in      ;
    wire                sda_en      ;  //主机控制sda线有效
    reg                 i2c_sda     ;  //在响应状态下,从机发送给主机的数据  

    assign  scl = i2c_scl;
    assign  sda_in = sda ;
    assign  sda = sda_en ? i2c_sda : 1'bz; //高阻态,让从机控制sda  
    assign  sda_en = (state_c == ACK_1 || state_c == ACK_2 || state_c == ACK_3 || ((state_c == DATA) && mode == 4'd3) || ((state_c == DATA) && mode == 4'd6)) ? 1'b0 : 1'b1; //主机控制sda有效

    always @(posedge i2c_clk or negedge rst_n)begin 
        if(!rst_n)begin
            cfg_start <= 0;
        end 
        else
            cfg_start <= i2c_end;
    end


/**************************************************************
引入mode信号的原因是,我们需要发送不同的指令实现数据的接收及发送,因此这些操作有差异。
通过该信号,编写代码时,mode对应模式执行来相应操作,避免后续状态跳转产生影响                                 
**************************************************************/
//利用位拼接,提高了slave_addr和device_addr复用性
    always @(*)begin 
     case(mode)
     0 : slave_addr  = {SLAVE_ID,1'b0};  //唤醒操作
     1 : begin
         slave_addr  = {SLAVE_ID,1'b0};  //激活Bank0
         device_addr = BANK_ADDR;
         data        = 8'b0000_0000;
     end
     2 : begin                           //配置0x00寄存器
         slave_addr  = {SLAVE_ID,1'b0};
         device_addr = 8'b0000_0000;
         data        = 8'b0000_0000;
     end
     3 : begin                          //读取0x00寄存器
         slave_addr  = {SLAVE_ID,1'b1};   
         data       = 8'b0000_0000;
     end
     4 : begin                          //配置操作寄存器,寄存器高8位储存地址,低8位储存数据
         slave_addr  = {SLAVE_ID,1'b0};
         device_addr = cfg_data[15:8] ;
         data        = cfg_data[7:0]  ;
     end
     5 : begin                          //配置手势寄存器
         slave_addr  = {SLAVE_ID,1'b0};
         device_addr = DARA_ADDR;       //DARA_ADDR = 8'h43为储存手势数据地址
     end
     6 : begin                          //读取手势寄存器
         slave_addr  = {SLAVE_ID,1'b1};
     end
     default : ;   
     endcase
    end

//分频计数器进行计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_clk <= 5'b0;
        end 
        else if(cnt_clk == CNT_CLK_MAX - 1)begin 
            cnt_clk <= 5'b0;
        end 
        else begin 
            cnt_clk <= cnt_clk + 1'b1;
        end 
    end 

//产生i2c驱动时钟
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            i2c_clk <= 1'b1;
        end     
        else if(cnt_clk == CNT_CLK_MAX - 1)begin 
            i2c_clk <= ~i2c_clk;
        end 
        else begin 
            i2c_clk <= i2c_clk;
        end 
    end

//状态机第一段
    always @(posedge i2c_clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE;
        end 
        else begin 
            state_c <= state_n;
        end 
    end

//状态机第二段
    always @(*)begin 
        case(state_c)
        IDLE        : begin
                        if(skip_en_0 || skip_en_1 || skip_en_2 || skip_en_3 || skip_en_4 || skip_en_5 || skip_en_6)
                            state_n = START;
                            else
                            state_n = state_c;
                    end
        START       : begin
                        if(skip_en_0 || skip_en_1 || skip_en_2 || skip_en_3 || skip_en_4 || skip_en_5 || skip_en_6)
                            state_n = SLAVE_ADDR;
                            else
                            state_n = state_c;
                    end
        SLAVE_ADDR  : begin
                        if(skip_en_0)
                            state_n = WAIT;
                        else if(skip_en_1 || skip_en_2 || skip_en_3 || skip_en_4 || skip_en_5 || skip_en_6)
                                state_n = ACK_1;
                        else
                            state_n = state_c;
                    end
        ACK_1       : begin
                        if(skip_en_1 || skip_en_2 || skip_en_4 || skip_en_5)
                            state_n = DEVICE_ADDR;
                        else if(skip_en_3 || skip_en_6)
                            state_n = DATA ;
                        else
                            state_n = state_c;
                    end
        DEVICE_ADDR : begin
                        if(skip_en_1 || skip_en_2 || skip_en_4 || skip_en_5 )
                            state_n = ACK_2;
                        else
                            state_n = state_c;
                   end
        ACK_2       : begin
                        if(skip_en_1 || skip_en_4)
                            state_n = DATA;
                        else if(skip_en_2 || skip_en_5)
                            state_n = STOP;
                        else
                            state_n = state_c;
                    end
        DATA        : begin
                        if(skip_en_1 || skip_en_4)
                            state_n = ACK_3;
                        else if(skip_en_3 || skip_en_6)
                            state_n = NACK;
                        else if(error_en)
                            state_n = IDLE;
                        else
                            state_n = state_c;
                    end
        ACK_3       : begin
                        if(skip_en_1 || skip_en_4)
                            state_n = STOP;
                        else
                            state_n = state_c;
                    end
        NACK        : begin
                        if(skip_en_3 || skip_en_6)
                            state_n = STOP;
                        else
                            state_n = state_c;
                      end
        WAIT        : begin
                        if(skip_en_0)
                            state_n = STOP;
                            else
                            state_n = state_c;
                    end
        STOP        : begin
                        if(skip_en_0 || skip_en_1 || skip_en_2 || skip_en_3 || skip_en_4 || skip_en_5 || skip_en_6)
                            state_n = IDLE;
                            else
                            state_n = state_c;
                    end
        default :   state_n = state_c;
        endcase
    end

//状态机第三段
    always @(posedge i2c_clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_wait    <= 10'b0;
            skip_en_0   <= 1'b0 ;
            skip_en_1   <= 1'b0 ;
            skip_en_2   <= 1'b0 ;
            skip_en_3   <= 1'b0 ;
            skip_en_4   <= 1'b0 ;
            skip_en_5   <= 1'b0 ;
            skip_en_6   <= 1'b0 ;
            error_en    <= 1'b0 ;
            cnt_bit     <= 3'b0 ;   
            cnt_i2c_clk <= 2'b0 ;
            cnt_delay   <= 10'b0;
            mode        <= 4'b0 ;
            i2c_end     <= 1'b0 ;
        end
        else begin 
            case(state_c)
            IDLE        :  begin
                            if(cnt_wait == CNT_DELAY_MAX - 1)
                                cnt_wait <= 10'b0;
                            else
                                cnt_wait <= cnt_wait + 1'b1;
                            if((cnt_wait == CNT_DELAY_MAX - 2'd2) && (mode == 4'd0))
                                skip_en_0 <= 1'b1;
                            else
                                skip_en_0 <= 1'b0;
                            if((cnt_wait == CNT_DELAY_MAX - 2'd2) && (mode == 4'd1))
                                skip_en_1 <= 1'b1;
                            else
                                skip_en_1 <= 1'b0; 
                            if((cnt_wait == CNT_DELAY_MAX - 2'd2) && (mode == 4'd2))
                                skip_en_2 <= 1'b1;
                            else
                                skip_en_2 <= 1'b0; 
                            if((cnt_wait == CNT_DELAY_MAX - 2'd2) && (mode == 4'd3))
                                skip_en_3 <= 1'b1;
                            else
                                skip_en_3 <= 1'b0; 
                            if((i2c_start == 1'b1) && (mode == 4'd4))
                                skip_en_4 <= 1'b1;
                            else
                                skip_en_4 <= 1'b0; 
                            if((cnt_wait == CNT_DELAY_MAX - 2'd2) && (mode == 4'd5))
                                skip_en_5 <= 1'b1;
                            else
                                skip_en_5 <= 1'b0; 
                            if((cnt_wait == CNT_DELAY_MAX - 2'd2) && (mode == 4'd6))
                                skip_en_6 <= 1'b1;
                            else
                                skip_en_6 <= 1'b0; 
                          end
            START       :  begin
                            cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if((cnt_i2c_clk == 2'd2) && (mode == 4'd0))
                                skip_en_0 <= 1'b1;
                            else 
                                skip_en_0 <= 1'b0;
                            if((cnt_i2c_clk == 2'd2) && (mode == 4'd1))
                                skip_en_1 <= 1'b1;
                            else 
                                skip_en_1 <= 1'b0;
                              if((cnt_i2c_clk == 2'd2) && (mode == 4'd2))
                                skip_en_2 <= 1'b1;
                            else 
                                skip_en_2 <= 1'b0;
                            if((cnt_i2c_clk == 2'd2) && (mode == 4'd3))
                                skip_en_3 <= 1'b1;
                            else 
                                skip_en_3 <= 1'b0;
                             if((cnt_i2c_clk == 2'd2) && (mode == 4'd4))
                                skip_en_4 <= 1'b1;
                             else 
                                skip_en_4 <= 1'b0;
                             if((cnt_i2c_clk == 2'd2) && (mode == 4'd5))
                                 skip_en_5 <= 1'b1;
                            else 
                                skip_en_5 <= 1'b0;
                            if((cnt_i2c_clk == 2'd2) && (mode == 4'd6))
                                skip_en_6 <= 1'b1;
                             else 
                                skip_en_6 <= 1'b0;
                          end
            SLAVE_ADDR  :  begin
                            cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if(cnt_i2c_clk == 2'd3)
                                cnt_bit <= cnt_bit +1'b1;
                            else 
                                cnt_bit <= cnt_bit;
                            if((cnt_bit == 3'd7) && (mode == 4'd0) && (cnt_i2c_clk == 2'd2))
                                skip_en_0 <= 1'b1;
                            else
                                skip_en_0 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd1) && (cnt_i2c_clk == 2'd2))
                                skip_en_1 <= 1'b1;
                            else
                                skip_en_1 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd2) && (cnt_i2c_clk == 2'd2))
                                skip_en_2 <= 1'b1;
                            else
                                skip_en_2 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd3) && (cnt_i2c_clk == 2'd2))
                                skip_en_3 <= 1'b1;
                            else
                                skip_en_3 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd4) && (cnt_i2c_clk == 2'd2))
                                skip_en_4 <= 1'b1;
                            else
                                skip_en_4 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd5) && (cnt_i2c_clk == 2'd2))
                                skip_en_5 <= 1'b1;
                            else
                                skip_en_5 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd6) && (cnt_i2c_clk == 2'd2))
                                skip_en_6 <= 1'b1;
                            else
                                skip_en_6 <= 1'b0;
                            end
            ACK_1       :  begin
                            cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd1)
                                skip_en_1 <= 1'b1;
                            else
                                skip_en_1 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd2)
                                skip_en_2 <= 1'b1;
                            else
                                skip_en_2 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd3)
                                skip_en_3 <= 1'b1;
                            else
                                skip_en_3 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd4)
                                skip_en_4 <= 1'b1;
                            else
                                skip_en_4 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd5)
                                skip_en_5 <= 1'b1;
                            else
                                skip_en_5 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd6)
                                skip_en_6 <= 1'b1;
                            else
                                skip_en_6 <= 1'b0;
                           end
            DEVICE_ADDR :  begin
                           cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if(cnt_i2c_clk == 2'd3)
                                cnt_bit <= cnt_bit +1'b1;
                            else 
                                cnt_bit <= cnt_bit;
                            if((cnt_bit == 3'd7) && (mode == 4'd1) && (cnt_i2c_clk == 2'd2))
                                skip_en_1 <= 1'b1;
                            else
                                skip_en_1 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd2) && (cnt_i2c_clk == 2'd2))
                                skip_en_2 <= 1'b1;
                           else
                               skip_en_2 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd4) && (cnt_i2c_clk == 2'd2))
                                skip_en_4 <= 1'b1;
                            else
                                skip_en_4 <= 1'b0;
                            if((cnt_bit == 3'd7) && (mode == 4'd5) && (cnt_i2c_clk == 2'd2))
                                skip_en_5 <= 1'b1;
                            else
                                skip_en_5 <= 1'b0;
                           end
            ACK_2       :  begin
                            cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd1)
                                skip_en_1 <= 1'b1;
                            else
                                skip_en_1 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd2)
                                skip_en_2 <= 1'b1;
                            else
                                skip_en_2 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd4)
                                skip_en_4 <= 1'b1;
                            else
                                skip_en_4 <= 1'b0;
                           if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd5)
                               skip_en_5 <= 1'b1;
                            else
                               skip_en_5 <= 1'b0;
                           end
            DATA        :  begin
                            cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if(cnt_i2c_clk == 2'd3)
                                cnt_bit <= cnt_bit +1'b1;
                            else
                                cnt_bit <= cnt_bit;
                            if(cnt_bit ==3'd7 && cnt_i2c_clk == 2'd2 && mode == 4'd1)
                                skip_en_1 <= 1'b1;
                            else
                                skip_en_1 <= 1'b0;
                            if(cnt_bit ==3'd7 && cnt_i2c_clk == 2'd2 && mode == 4'd3 && re_data == 8'h20)
                                skip_en_3 <= 1'b1;
                            else
                                skip_en_3 <= 1'b0;
                            if(cnt_bit ==3'd7 && cnt_i2c_clk == 2'd2 && mode == 4'd3 && re_data != 8'h20)
                            begin
                                error_en <= 1'b1;
                                mode     <= 4'd0; 
                            end
                            else begin
                                    error_en <= 1'b0;
                                    mode     <= mode;
                            end
							if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 4'd4))
								skip_en_4  <=  1'b1  ;
							else
							    skip_en_4  <=  1'b0  ;
							if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 4'd6))
								skip_en_6  <=  1'b1  ;
							else
								skip_en_6  <=  1'b0  ;										
                           end
            NACK        :  begin
                            cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if(ACK == 1'b1 && cnt_i2c_clk == 2'd2 && mode == 4'd3)
                                skip_en_3 <= 1'b1;
                            else 
                                skip_en_3 <= 1'b0;
                            if(ACK == 1'b1 && cnt_i2c_clk == 2'd2 && mode == 4'd6)
                                skip_en_6 <= 1'b1;
                            else 
                                skip_en_6 <= 1'b0;
                           end
            ACK_3       :  begin
                            cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd1)
                                skip_en_1 <= 1'b1;
                            else
                                skip_en_1 <= 1'b0;  
                            if(cnt_i2c_clk == 2'd2 && ACK && mode == 4'd4)
                                skip_en_4 <= 1'b1;
                            else
                                skip_en_4 <= 1'b0;  
                           end
            WAIT        :  begin
                            if(cnt_delay == CNT_DELAY_MAX - 1)
                                cnt_delay <= 10'b0;
                            else
                                cnt_delay <= cnt_delay + 1'b1;
                            if((cnt_delay == CNT_DELAY_MAX -2) && (mode == 4'd0))
                                skip_en_0 <= 1'b1;
                            else
                                skip_en_0 <= 1'b0;
                            end
            STOP        :  begin
                            cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
                            if(cnt_i2c_clk == 2'd2 && mode == 4'd0)
                                skip_en_0 <= 1'b1;
                            else 
                                skip_en_0 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && mode == 4'd1)
                                skip_en_1 <= 1'b1;
                            else
                                skip_en_1 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && mode == 4'd2)
                                skip_en_2 <= 1'b1;
                            else
                                skip_en_2 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && mode == 4'd3)
                                skip_en_3 <= 1'b1;
                            else
                                skip_en_3 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && mode == 4'd4)
                                skip_en_4 <= 1'b1;
                            else
                                skip_en_4 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && mode == 4'd5)
                                skip_en_5 <= 1'b1;
                            else
                                skip_en_5 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2 && mode == 4'd6)
                                skip_en_6 <= 1'b1;
                            else
                                skip_en_6 <= 1'b0;
                            if(cnt_i2c_clk == 2'd2)
                                i2c_end <= 1'b1;
                            else
                                i2c_end <= 1'b0;
                            if(i2c_end && (mode <= 4'd3))
                                mode <= mode + 1'b1;
                            else if(mode == 4'd4 && i2c_end == 1'b1 && reg_num == 6'd51)
                                mode <= mode + 1'b1;
                            else if((mode ==4'd5)&&(i2c_end == 1'b1))
                                mode <= mode + 1'b1;
                            else 
                                mode <= mode;
                            end
            default     :  begin
                            cnt_wait    <= 10'b0;
                            skip_en_0   <= 1'b0 ;
                            skip_en_1   <= 1'b0 ;
                            skip_en_2   <= 1'b0 ;
                            skip_en_3   <= 1'b0 ;
                            skip_en_4   <= 1'b0 ;
                            skip_en_5   <= 1'b0 ;
                            skip_en_6   <= 1'b0 ;
                            error_en    <= 1'b0 ;
                            cnt_bit     <= 3'b0 ;
                            cnt_i2c_clk <= 2'b0 ;
                            cnt_delay   <= 10'b0;
                            mode        <= mode ;
                            i2c_end     <= 1'b0 ;
                        end
            endcase
    end
    end

    always @(posedge i2c_clk or negedge rst_n)begin 
    if(rst_n == 1'b0)
        re_data <= 8'b0;
    else
        case(state_c)
            DATA    :   if((mode ==4'd3) && cnt_i2c_clk == 2'd1)
                           re_data <= {re_data[6:0],sda_in};
                        else
                            re_data <= re_data;
                        default : re_data <= re_data;
         endcase
    end

    always @(posedge i2c_clk or negedge rst_n)begin 
    if(rst_n == 1'b0)
        po_data_reg <= 8'd0;
    else
        case(state_c)
            DATA    :   if((mode ==4'd6) && cnt_i2c_clk == 2'd1)
                           po_data_reg <= {po_data_reg[6:0],sda_in};//MSB 串并转换
                        else
                           po_data_reg <= po_data_reg;
                        default : po_data_reg <= po_data_reg ;
         endcase
    end

    always@(posedge i2c_clk or negedge rst_n)
	if(!rst_n)
		po_data  <=  8'd0  ;
	else  if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 4'd6))
		po_data  <=  po_data_reg  ;
	else
		po_data  <=  po_data  ;

    always @(*)begin 
        case(state_c)
        IDLE , START , SLAVE_ADDR , DEVICE_ADDR , DATA , STOP : ACK =1'b0;
        ACK_1 ,ACK_2 , ACK_3 : ACK = ~sda_in;
        NACK                 : ACK = i2c_sda;
        default : ACK  = 1'b0;
        endcase 
    end 


//scl
    always @(*)begin 
        case(state_c)
        IDLE    :   i2c_scl = 1'b1;
        START   :   if(cnt_i2c_clk == 2'd3)
                        i2c_scl = 1'b0;
                    else
                        i2c_scl = 1'b1;
        SLAVE_ADDR , ACK_1 , DEVICE_ADDR , ACK_2 , DATA , ACK_3 , NACK  :
                    if(cnt_i2c_clk == 2'd0 || cnt_i2c_clk == 2'd3)
                        i2c_scl = 1'b0;
                    else
                        i2c_scl = 1'b1;
        WAIT    :   i2c_scl = 1'b0;
        STOP    :   if(cnt_i2c_clk == 2'd0)
                        i2c_scl = 1'b0;
                    else
                        i2c_scl = 1'b1;
        default :   i2c_scl = 1'b1;
           endcase
    end
//sda
    always @(*)begin 
        case(state_c)
        IDLE    :   i2c_sda = 1'b1;
        START   :   if(cnt_i2c_clk == 2'd0)
                        i2c_sda = 1'b1;
                    else
                        i2c_sda = 1'b0;
        SLAVE_ADDR  : i2c_sda = slave_addr[7-cnt_bit]; //从MSB开始传
        DEVICE_ADDR : i2c_sda = device_addr[7-cnt_bit];
        DATA        : i2c_sda = data[7-cnt_bit];
        ACK_1 , ACK_2 , ACK_3 : i2c_sda = 1'b0;
        NACK        : i2c_sda = 1'b1;
        WAIT    :   i2c_sda = 1'b0;
        STOP    :   if(cnt_i2c_clk == 2'd0 || cnt_i2c_clk == 2'd1)
                        i2c_sda = 1'b0;
                    else
                        i2c_sda = 1'b1;
        default :   i2c_sda = 1'b1;
        endcase
    end

endmodule   
module beep#( parameter CLK_PRE = 50_000_000, TIME_300MS = 15_000_000)(  //频率控制音色 占空比控制音量 占空比越小 低电平占比越大 音量就越大
    input           clk     ,
    input           rst_n   ,
    output reg      pwm     
);

    parameter   DO = CLK_PRE / 523,  // 该频率需要多少个系统时钟周期
                RE = CLK_PRE / 587,   
                MI = CLK_PRE / 659,   
                FA = CLK_PRE / 698,    
                SO = CLK_PRE / 784,   
                LA = CLK_PRE / 880,   
                SI = CLK_PRE / 988;   

    reg         [16:0]      cnt1        ;  //计数频率
    wire                    add_cnt1    ;
    wire                    end_cnt1    ;
    reg         [16:0]      X           ; //cnt1最大值 表示当前为哪一个频率

    reg         [23:0]      cnt2        ; //记300ms  每一个音符放300ms
    wire                    add_cnt2    ;
    wire                    end_cnt2    ;

    reg         [5:0]       cnt3        ; //计数乐谱48个
    wire                    add_cnt3    ;
    wire                    end_cnt3    ;

    reg                     pwm_ctrl    ; //为1时 不发声音  让后25%不发声

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt1 <= 17'b0;
        end
        else if(end_cnt2)begin  //300ms计数器完了 重新开始记新的一个频率
            cnt1 <= 17'b0;
        end
        else if(add_cnt1)begin
            if(end_cnt1)begin
                cnt1 <= 17'b0;
            end
            else begin
                cnt1 <= cnt1 + 1'b1;
            end
        end
        else begin
            cnt1 <= cnt1;
        end
    end

    assign add_cnt1 = 1;
    assign end_cnt1 = add_cnt1 && cnt1 == X - 1; 

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt2 <= 24'b0;
        end
        else if(add_cnt2)begin
            if(end_cnt2)begin
                cnt2 <= 24'b0;
            end
            else begin
                cnt2 <= cnt2 + 1'b1;
            end
        end
        else begin
            cnt2 <= cnt2;
        end
    end

    assign add_cnt2 = 1;
    assign end_cnt2 = add_cnt2 && cnt2 == TIME_300MS - 1;

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt3 <= 6'b0;
        end
        else if(add_cnt3)begin
            if(end_cnt3)begin
                cnt3 <= 6'b0;
            end
            else begin
                cnt3 <= cnt3 + 1'b1;
            end
        end
        else begin
            cnt3 <= cnt3;
        end
    end

    assign add_cnt3 = end_cnt2;
    assign end_cnt3 = add_cnt3 && cnt3 == 48 - 1;

    always @(*)begin
        case(cnt3)
            0   :   X = DO;
            1   :   X = RE; 
            2   :   X = MI;
            3   :   X = DO;

            4   :   X = DO;
            5   :   X = RE;
            6   :   X = MI;
            7   :   X = DO;

            8   :   X = MI;    
            9   :   X = FA;    
            10  :   X = SO;
            11  :   X = SO;

            12  :   X = MI;
            13  :   X = FA;
            14  :   X = SO;
            15  :   X = SO;

            16  :   X = SO;
            17  :   X = SI;
            18  :   X = SO;
            19  :   X = FA;

            20  :   X = MI;
            21  :   X = MI;
            22  :   X = DO;
            23  :   X = DO;

            24  :   X = SO;
            25  :   X = SI;
            26  :   X = SO;
            27  :   X = FA;

            28  :   X = MI;
            29  :   X = MI;
            30  :   X = DO;
            31  :   X = DO;

            32  :   X = RE;
            33  :   X = RE;
            34  :   X = SO;
            35  :   X = SO;

            36  :   X = DO;
            37  :   X = DO;
            38  :   X = 1 ;
            39  :   X = 1 ;

            40  :   X = RE;
            41  :   X = RE;
            42  :   X = SO;
            43  :   X = SO;

            44  :   X = DO;
            45  :   X = DO;
            46  :   X = 1 ;
            47  :   X = 1 ;
            default : X = 1 ;
        endcase
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            pwm_ctrl <= 1'b0;
        end
        else if(X == 1)begin
            pwm_ctrl <= 1'b1;
        end
        else if(cnt2 > ((TIME_300MS >> 2) + (TIME_300MS >> 1)))begin // 1/2 + 1/4 表示后25%
            pwm_ctrl <= 1'b1;
        end
        else begin
            pwm_ctrl <= 1'b0;
        end
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            pwm <= 1'b1;
        end
        else if(pwm_ctrl == 1'b1)begin
            pwm <= 1'b1;
        end
        else if(cnt1 <(X >> 5))begin
            pwm <= 1'b0;
        end
        else begin
            pwm <= 1'b1;
        end
    end


endmodule

         本文参考了,FPGA中国创新中心的博客,博主写的十分详细,需要更深入的了解的,可以移步看一下博主的博客:link。文章来源地址https://www.toymoban.com/news/detail-845512.html

到了这里,关于基于FPGA的手势识别(PAJ7620U2)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • PAJ7620U2手势识别——读取手势数据寄存器数据与LED指示(完)

      在前面的教程中呢,小编带领各位读者完成了对所有寄存器的配置,本章教程只需要完成对手势数据寄存器里面的数据读出即可,因为我们只检测上、下、左、右挥手数据,因此用四个led灯作为挥手数据结果指示即可。本章教程是基于FPGA的PAJ7620U2手势识别的最后一章教程

    2024年02月07日
    浏览(43)
  • PAJ7620U2手势识别——配置0x00寄存器(3)

      在前面的教程中,小编带领各位读者学习了如何通过I2C协议去唤醒PAJ7620U2,如何激活BANK0。本章教程,小编会继续一步一步带领各位读者,继续学习如何配置0x00寄存器,具体操作请仔细阅读本章教程。   在回答这个问题之前,我们先看一下正点原子给的该模块的文档:

    2024年02月04日
    浏览(41)
  • PAJ7620U2手势识别——读取0x00寄存器数据(4)

      在之前的教程中,小编带领各位读者完成了手势识别模块PAJ7620U2的基本设置,本章教程通过读取0x00寄存器内的值,判断该值是否为0x20,如果是,则代表唤醒成功,如果不是,则代表唤醒失败,需要重新回到唤醒操作。具体的操作步骤请各位读者继续往下浏览。   相信

    2024年02月05日
    浏览(91)
  • STM32 手势识别传感器模块(PAJ7620)学习

    目录 模块介绍: 基本部分: 引脚配置: 工作原理: 展示部分: 代码部分展示(在正点的基础上加了一个读手势去控制舵机): 视频展示: 基本部分: 手势模块搭载的芯片是PAJ7620,无论是正点原子的还是别的手势模块的底层是一致的,甚至代码也是通用的。 芯片内部集成了

    2024年02月07日
    浏览(48)
  • 基于FPGA的手势识别

    使用正点原子开拓者开发板,预定义三种手势:石头(0)、剪刀(2)、布(5)。通过 OV5640 摄像头套件对手势图像进行采集,LCD 显示屏(显示屏用的正点原子的 7 寸 RGB_LCD,分辨率为 1024×600)对系统处理后的手势进行实时显示,根据预定义手势的面积周长比判断手势,最终

    2024年02月11日
    浏览(41)
  • 学习笔记(四):基于FPGA的颜色识别

         从对角线向O看下去的图。 色调:俯视图顺时针看去,不同的角度色调不一样。 饱和度:两边向中间 亮度:对角线看去 一、首先摄像头采集到的是RGB的格式,所以要将RGB色散空间转变为HSV色散空间,首先要完成将H分量提取出来的功能,提取H分量后就可以对H分量 进行

    2024年01月17日
    浏览(31)
  • 基于OpenCV的手势识别系统设计与开发

    随着计算机技术与信息处理技术迅速发展,智能化电子设备逐渐进入到日常的生产和生活中,与此同时,人们对电子设备操作过程的便捷化也提出了新的要求,这也促使计算机进行图像处理的技术也得到了发展。近些年兴起的模式识别技术为操作便捷化提供了新的研究方向和

    2024年02月03日
    浏览(47)
  • 基于FPGA的车牌识别

    基于FPGA进行车牌识别 1. 文件说明 2. 程序移植说明 3. 小小的编程感想 本项目的原理讲解视频已经上传到B站“基于FPGA进行车牌识别”。 本项目全部开源,见我本人的Github仓库“License-Plate-Recognition-FPGA”。 小技巧:下载整个Github文件夹:http://tool.mkblog.cn/downgit/#/home 1. 工程及源

    2024年01月20日
    浏览(38)
  • 基于matlab的视频和深度学习的手势识别

    此示例首先演示如何使用预训练的SlowFast视频分类器执行手势识别,然后演示如何使用迁移学习在自定义手势识别数据集上训练分类器。 基于视觉的人类手势识别涉及使用一组视频帧预测手势,例如挥手打招呼、手语手势或鼓掌。手势识别的一个吸引人的特点是,它们使人类

    2024年02月22日
    浏览(139)
  • 计算机竞赛 题目:基于深度学习的手势识别实现

    🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的手势识别实现 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/postgraduate 手势识别在深度学习项目是算是比较简单的。这里为了给大家会更好的训练。其中

    2024年02月07日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包