一、PAJ7620U2手势识别模块
1.基本信息
PAJ7620U2 是原相科技(PixArt)公司推出的一款光学数组式传感器,内置光源和环境光抑制滤波器集成的 LED,镜头和手势感测器在一个小的立方体模组,能在黑暗或低光环境下工作。同时传感器内置手势识别,支持 9 个手势类型和输出的手势中断结果。并且内置接近检测功能,可用于感测物体接近或离开。
PAJ7620U2 的特点包括:
①IIC 接口,支持高达 400Khz 通信速率。
②内置 9 个手势类型(上、下、左、右、前、后、顺时针旋转、逆时针旋转、挥动),支持输出中断。
③支持接近检测功能,检测物体体积大小和亮度。
④待机功耗电流 15uA。
⑤抗灯光干扰
2.工作原理
PAJ7620U2 工作时通过内部 LED 驱动器,驱动红外 LED向外发射红外线信号,当传感器阵列在有效的距离中探测到物体时,目标信息提取阵列会对探测目标进行特征原始数据的获取,获取的数据会存在寄存器中,同时手势识别阵列会对原始数据进行识别处理,最后将手势结果存到寄存器中,用户可根据 I2C 接口对原始数据和手势识别的结果进行读取 。
3.模式寄存器简介
PAJ7620U2 的内部有两个 BANK 寄存器区域,分别是 BANK0和 BANK1。不同的区域用于访问不同的功能寄存器, 但想访问其中的 BANK 区域下的寄存器, 需在访问前发送控制指令进入该寄存器区域, 具体控制指令如下图所示:
进入 BANK0 区域往传感器 0xEF 地址写 0x00 数值,而 BANK1 区域往传感器 0xEF 地址写 0x01 数值
4.工作流程
初始化流程即配置寄存器,直接调用数据手册提供的配置数组并将其写入BANK0
图4 初始化流程 |
4.1 唤醒操作
4.1.1唤醒操作简介
步骤一:上电,Vbus必须在Vdd之前上电;
步骤二:等待700us,让PAJ7620U2稳定;
步骤三:写入从机ID或者是I2C读取指令去唤醒。
步骤一我们不用关注,这是生产厂家需要关注的问题,我们看后面两个步骤。步骤二是在写入前先等待700us,为了更好地提升模块性能,这里我们等待1000us,因为该模块是通过I2C协议配置的,我们在初始状态下等待1000us后,再跳转到I2C开始状态。步骤三,我们根据数据手册:
所以唤醒指令为8'b1110_0111。
4.1.2 唤醒操作步骤
4.1.2.1 状态转移图
4.1.2.2 时序图
在这里我们必须考虑引入两个信号,一个是结束标志信号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 状态转移图
初始状态延迟1000us后,跳转到开始状态,检测到开始信号后,跳转到发送从设备地址状态,从设备地址8'b1110_0110发送成功,返回一个响应值。检测到响应值有效,跳转到发送BANK0地址,BANK0地址8'b1110_1111发送完成之后,又接收到一个响应值。检测到响应值有效,跳转到发送寄存器地址状态,寄存器地址0x00数据发送完成,接收到响应信号,响应信号有效,跳转到停止状态。
4.2.1.2 时序图
4.3 配置0x00寄存器
在我们激活BANK0以后,我们是向里面写入了0x00数据,读者需要注意的是,这里写入的0x00不是寄存器地址,而是数值。如果激活BANK1,写入的数值就是0x01了。因此,我们还需要再次写入0x00数据,这里就表示写入的是0x00寄存器,将0x00寄存器配置好,便于我们后续读取操作。
4.3.1 配置步骤
4.3.1.1 状态转移图
4.4 读取0x00寄存器
这一部分的从机地址最后一位会发生变化,变为7位从机ID+1位读操作,从机地址发送完成后,主机放弃对SDA信号线的控制。这时,从机控制SDA信号线,向主机发送ACK响应数据,收到响应后,主机这时还是要将控制权交给从机,从机发送8bit的数据,主机读取这8bit的数据,从高到低依次拼接,看最后拼接成的数据是否为0x20,如果是的话,主机则获取对SDA信号线的控制,并返回一个高电平的NACK信号,发送给从机,最后结束。
如果接收到的数据不是0x20,则代表唤醒失败,代码中的mode信号一定要清零,同时直接由DATA状态跳转到IDLE状态。
4.4.1 读取步骤
4.4.1.1 状态转移图
4.4.1.2 时序图
在这个波形图中,多引入了两个信号,一个是error_en错误信号,配合skip_en信号检测读取出来的数据是否是正确的,如果数据是0x20,则skip_en拉高,否则error_en拉高。另一个信号是rec_data信号,除了DATA状态,我们不关心其它时刻它的值为多少,因为不管值为多少,每次读取出来的1bit数据都会一个一个地把原有的数据顶替下去。
4.5 配置寄存器组
唤醒操作完成后,即代表可以使用这个模块了。所以,我们需要参考官方数据手册,配置寄存器组,配置完成后才能进行数据的读取。
4.5.1 配置步骤
单个写操作步骤图与前面激活bank0教程的步骤图是相同的。在这里呢,我们对51个寄存器写入数据,不能进行连续写操作,只能单次写入,总共写入51次。将这51个寄存器配置好后,即可进行数据的读取了。
4.5.1.1 状态转移图
从IDLE状态到START状态,我们需要略微做一下修改,状态跳转条件修改为接收到一个开始信号。这个开始信号是从另外一个对51个寄存器寄存器配置的模块传入的,i2c控制模块结束信号就作为寄存器配置模块的开始信号,同时,寄存器配置模块的结束信号也作为i2c控制模块的开始信号。
4.5.1.2 时序图
上述波形图是寄存器配置模块的波形图。
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 手势寄存器配置
图4.6 手势寄存器配置流程 |
我们的挥手动作将会被0x43或0x44这两个寄存器捕捉到,捕捉完成后读取这两个寄存器内的数据即可
下面是0x43寄存器8bit数据每位置1的含义:
在这里读取0x43寄存器即可,0x44寄存器主要是执行中断操作,我们不使用该寄存器,在编写代码时检测到数据变化,做输出即可。
4.6.1 配置步骤
因为要进行连续捕获挥手动作,因此在这里必须要使用到连续读操作;连续读操作流程图如下:
4.6.1.1 状态转移图
4.6.1.2 时序图
4.7 读取手势寄存器数据
在读取手势寄存器中的数据时,不管是采用哪种读方式,读取到数据后,都不会自动停下,这时候结合前面的数据手册,需要我们设置“中断”,当读取到的8位数据不为全0时(即读取到了手势数据),则中断读操作。但是呢,我们采用的是FPGA来配置这个模块,对数据的处理就简单得多,因此只需要检测出该模块数据变化,将变化的数据作为LED灯亮起的触发信号,触发以后LED灯在下次触发信号到来时,一直保持亮起即可。
4.7.1 读取步骤
4.7.1.1 状态转移图
4.7.1.2 时序图
从波形图可以看出,除了跳转信号外,我们还需要引入两路信号,一路是po_data_reg,这个信号主要是在DATA状态下寄存拼接的数据。第二路信号是po_data,这个信号是在po_data_reg信号拼接完成后,读取DATA状态末尾拼接完成的数据。我们取po_data信号低四位,这低四位数据,某位由0变化为1后,则代表上、下、左、右挥手动作被检测出来,我们利用这个变化来驱动LED灯亮起。
二、程序设计
1.程序框图
文章来源:https://www.toymoban.com/news/detail-845512.html
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模板网!