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

文章目录总结
前言
在前面的教程中呢,小编带领各位读者完成了对所有寄存器的配置,本章教程只需要完成对手势数据寄存器里面的数据读出即可,因为我们只检测上、下、左、右挥手数据,因此用四个led灯作为挥手数据结果指示即可 。本章教程是基于FPGA的手势识别的最后一章教程,具体实现方法请继续往下浏览 。
一、如何读取手势数据寄存器数据?
在上一章教程中,我们采用的是突发读操作的时序图来对模块进行配置的,但是本章教程我们采用单次读操作对模块进行配置,单次读操作与突发读操作在前半段配置方式都是一样的,都是要指定读取的寄存器:
但是在后半段,连续读操作是从DATA状态,跳转到主机返回ACK响应,再从ACK响应跳转到DATA状态,结合官方数据手册:
我们发现,不管是采用哪种读方式,读取到数据后,都不会自动停下,这时候结合前面的数据手册,需要我们设置“中断”,当读取到的8位数据不为全0时,则中断读操作 。但是呢,我们采用的是FPGA来配置这个模块,对数据的处理就简单得多,因此只需要检测出该模块数据变化,将变化的数据作为LED灯亮起的触发信号,触发以后LED灯在下次触发信号到来时,一直保持亮起即可 。
这里呢,我们采用单次读操作,因为后续利用 Tap II触发波形信号的时候,可以观测到从IDLE到STOP一整个的执行周期,因为整个执行周期是非常短暂的,远远小于我们手势变化的时间,因此在这里使用单次读操作是完全可以采集到手势数据的变化情况 。
二、配置步骤 1.模块状态转移图绘制

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

文章插图
从图中可以看出,读取0x43寄存器数值状态转移图与读取0x00寄存器状态转移图绘制方式一样,因此各位读者如果学会了读取0x00寄存器数值操作后,本章教程对大家应该没有难度 。
2.模块波形图绘制
从波形图可以看出,除了跳转信号外,我们还需要引入两路信号,一路是,这个信号主要是在DATA状态下寄存拼接的数据 。第二路信号是,这个信号是在信号拼接完成后,读取DATA状态末尾拼接完成的数据 。我们取信号低四位,这低四位数据,某位由0变化为1后,则代表上、下、左、右挥手动作被检测出来,我们利用这个变化来驱动LED灯亮起 。因为驱动LED灯亮起非常简单,我们可以在顶层文件直接编写代码,就不再进行波形图的绘制 。
3.上板验证
设置信号为触发条件:
使用连续触发,抓取到的信号波形如图所示:
我们发现,在DATA状态下,一直没有采集到数据,并且4个LED灯也一直保持高电平,即熄灭状态 。我们向左挥手,抓取到的信号波形如下:
可以看到,LED灯已经发生了变化,第二位已经由高电平变为低电平,变亮了 。但是为什么DATA状态下,SDA还是为低电平呢?因为我们使用的是连续触发,触发时间非常短暂,采集到数据以后,马上让LED灯点亮,在下次采集数据时,已经归零了,但是LED灯还维持在点亮状态没有改变 。接下来,我们分别朝右、上、下挥手,抓取到的信号波形如下:
LED灯低三位数值都有变化,因此我们代码验证通过,且上板现象与预期一致(具体的实验现象各位读者可自行绑定引脚测试,在这里小编就不做演示了),整个工程验证通过 。
4.参考代码(和)
modulei2c_ctrl(input wiresys_clk,input wiresys_rst_n ,input wire [23:0] cfg_data ,input wirei2c_start ,input wire [5:0] cfg_num,output wirescl,output regcfg_start ,output regi2c_clk,output reg[2:0] mode,output reg[7:0] po_data,inout wiresda);localparam CNT_CLK_MAX= 5'd25 ;localparam CNT_WAIT_MAX = 10'd1000;localparam CNT_DELAY_MAX = 10'd1000;localparam SLAVE_ID= 7'h73 ;localparam SENSOR_ADDR= 8'hEF ;localparam DATA_ADDR= 8'h43 ;localparam IDLE= 4'd0 ,START= 4'd1 ,SLAVE_ADDR = 4'd2 ,WAIT= 4'd3 ,STOP= 4'd4 ,ACK_1= 4'd5 ,DEVICE_ADDR = 4'd6 ,ACK_2= 4'd7 ,DATA= 4'd8 ,ACK_3= 4'd9 ,NACK= 4'd10 ;reg[4:0] cnt_clk; //分频计数器reg[9:0] cnt_wait ; //开始状态等待1000us计数器regskip_en_0 ; //唤醒状态跳转信号regskip_en_1 ; //激活bank0跳转信号regskip_en_2 ; //配置0x00寄存器状态跳转信号regskip_en_3 ; //读取0x00寄存器状态跳转信号regskip_en_4 ; //配置51个操作寄存器regskip_en_5 ; //配置0x43寄存器状态跳转信号regskip_en_6 ; //读取0x43寄存器状态跳转信号regerror_en ; //读取出来的值不是0x20,错误信号reg[3:0] n_state; //次态reg[3:0] c_state; //现态 reg[1:0] cnt_i2c_clk ; //对i2c_clk分频时钟个数计数reg[2:0] cnt_bit; //对传输的8bit数据进行计数 regi2c_scl; //就是SCLregi2c_sda; //SDA赋值给i2c_sdareg[9:0] cnt_delay ; //发送完指令后等待1000us计数器regi2c_end; //i2c结束信号 reg[7:0] po_data_reg ; //采集数据,拼接完成后赋值给po_datareg[7:0] slave_addr ; //不同模式下7'h73+1'bxreg[7:0] device_addr ; //不同模式下寄存器地址变化reg[7:0] wr_data; //向地址写入的数据reg[7:0] rec_data ; //唤醒操作读取0x00寄存器数据寄存regack;wiresda_in;wiresda_en;assign scl= i2c_scl;assign sda_in = sda; //从设备发送到主机的数据assign sda_en = ((c_state == ACK_1)||(c_state == ACK_2)||(c_state == ACK_3)||((c_state == DATA)&&(mode == 3'd3))||((c_state == DATA)&&(mode == 3'd6))) ? 1'b0 : 1'b1; //主机控制sda有效assign sda= (sda_en == 1'b1) ? i2c_sda : 1'bz;always@(posedge i2c_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cfg_start