在之前的教程中,小编带领各位读者完成了手势识别模块PAJ7620U2的基本设置,本章教程通过读取0x00寄存器内的值,判断该值是否为0x20,如果是,则代表唤醒成功,如果不是,则代表唤醒失败,需要重新回到唤醒操作。具体的操作步骤请各位读者继续往下浏览。
相信有仔细阅读前面几节教程的读者,这个问题肯定是难不倒你们的。我们唤醒操作是否成功的标志就是,读取0x00寄存器内的值,看是否是0x20,如果不是则重新唤醒。但是经过小编的测试,如果前面每个模块的Singal Tap波形抓取无误,唤醒操作一般一次就可成功,因此按照小编的思路编写代码,是可以比较顺利实现功能的。
根据官方数据手册:
在上一章教程中,我们看的是前面写入从机地址和写入0x00数据这一部分,现在我们来看后一部分。这一部分的从机地址最后一位会发生变化,变为7位从机ID+1位读操作,从机地址发送完成后,主机放弃对SDA信号线的控制。这时,从机控制SDA信号线,向主机发送ACK响应数据,收到响应后,主机这时还是要将控制权交给从机,从机发送8bit的数据,主机读取这8bit的数据,从高到低依次拼接,看最后拼接成的数据是否为0x20,如果是的话,主机则获取对SDA信号线的控制,并返回一个高电平的NACK信号,发送给从机,最后结束。
如果接收到的数据不是0x20,则代表唤醒失败,代码中的mode信号一定要清零,同时直接由DATA状态跳转到IDLE状态。
结合状态转移图,我们不难发现,多了一个新的状态:NACK,同时,DATA这个状态,在之前的模块中,是可以在这个状态,主机向从机发送数据,我们把这个过程叫做写入。也可以在DATA状态,从机向主机发送数据,我们把主机接收数据这个过程叫做读取。NACK可以理解为,主机收到从机发送的数据后,要给予从机一个回应,像我们向从机发送数据一样,从机也是需要给与主机一个ACK回应。但是ACK和NACK不同的是,从机返回的响应数据是低电平有效,主机返回的响应数据是高电平有效。
这里我们只绘制部分波形即可,前面初始状态和发送从设备地址状态因为在前面章节已经有绘制,在这里我们就不再赘述,如读者有遗忘,请移步前面几章的教程,查看波形图找回记忆。
我们在这个波形图中,多引入了两个信号,一个是error_en错误信号,配合skip_en信号检测读取出来的数据是否是正确的,如果数据是0x20,则skip_en拉高,否则error_en拉高。另一个信号是rec_data信号,除了DATA状态,我们不关心其它时刻它的值为多少,因为不管值为多少,每次读取出来的1bit数据都会一个一个地把原有的数据顶替下去。根据以上的波形图,我们对原有的代码进行更改,然后上板抓取信号波形验证。
上板抓取信号波形,设置skip_en_4上升沿触发:
抓取到的信号如下所示:
如图所示,信号波形从IDLE状态到STOP状态,每个状态都有持续一段时间的高电平,且保持的时间与我们工程中设置的一样,在DATA状态为高电平时,采集到的数据为0x20,代表我们唤醒操作成功。
module i2c_ctrl
(
input wire sys_clk ,
input wire sys_rst_n ,
output wire scl ,
inout wire sda
);
localparam CNT_CLK_MAX = 5'd25 ;
localparam CNT_WAIT_MAX = 10'd1000;
localparam CNT_DELAY_MAX = 10'd1000;
localparam SENSOR_ADDR = 8'hEF ;
localparam SLAVE_ID = 7'h73 ;
localparam RD_ADDR = 8'h43 ;
localparam IDLE = 'd0 ,
START = 'd1 ,
SLAVE_ADDR = 'd2 ,
ACK_1 = 'd3 ,
DEVICE_ADDR = 'd4 ,
ACK_2 = 'd5 ,
DATA = 'd6 ,
ACK_3 = 'd7 ,
STOP = 'd8 ,
WAIT = 'd9 ,
NACK = 'd10 ;
reg [4:0] cnt_clk ; //分频计数器
reg i2c_clk ; //i2c分频后的驱动时钟
reg [9:0] cnt_wait ; //开始状态等待1000us计数器
reg skip_en_1 ; //唤醒状态跳转信号
reg skip_en_2 ;
reg skip_en_3 ;
reg skip_en_4 ;
reg error_en ;
reg [3:0] n_state ; //次态
reg [3:0] c_state ; //现态
reg [1:0] cnt_i2c_clk ; //对i2c_clk分频时钟个数计数
reg [2:0] cnt_bit ; //对传输的8bit数据进行计数
reg i2c_scl ; //就是SCL
reg i2c_sda ; //SDA赋值给i2c_sda
reg [9:0] cnt_delay ; //发送完指令后等待1000us计数器
reg [3:0] mode ; //模式选择
reg i2c_end ; //i2c结束信号
reg [7:0] rec_data ; //接收的数据,用来判断是否为0x20
reg ack ;
reg [7:0] slave_addr ;
reg [7:0] device_addr ;
reg [7:0] wr_addr ;
wire sda_in ;
wire sda_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)) ? 1'b0 : 1'b1 ; //主机控制sda有效
assign sda = (sda_en == 1'b1) ? i2c_sda : 1'bz ;
always@(*)
case(mode)
3'd0 : begin
slave_addr <= {SLAVE_ID,1'b0} ; //激活
device_addr <= 8'd0 ;
wr_addr <= 8'd0 ;
end
3'd1 : begin
slave_addr <= {SLAVE_ID,1'b0} ; //写入0xEF 00
device_addr <= SENSOR_ADDR ;
wr_addr <= 8'd0 ;
end
3'd2 : begin
slave_addr <= {SLAVE_ID,1'b0} ; //写入00寄存器
device_addr <= 8'b0000_0000 ;
end
3'd3 : begin
slave_addr <= {SLAVE_ID,1'b1} ; //读取00寄存器的值
end
endcase
//
//分频计数器进行计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 5'd0 ;
else if(cnt_clk == CNT_CLK_MAX - 1'b1)
cnt_clk <= 5'd0 ;
else
cnt_clk <= cnt_clk + 1'b1 ;
//产生i2c驱动时钟
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_clk <= 1'b0 ;
else if(cnt_clk == CNT_CLK_MAX - 1'b1)
i2c_clk <= ~i2c_clk ;
else
i2c_clk <= i2c_clk ;
//
//状态机第一段
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
c_state <= IDLE ;
else
c_state <= n_state ;
//状态机第二段
always@(*)
case(c_state)
IDLE : if((skip_en_1 == 1'b1)||(skip_en_2 == 1'b1)||(skip_en_3 == 1'b1)||(skip_en_4 == 1'b1))
n_state = START ;
else
n_state = IDLE ;
START : if((skip_en_1 == 1'b1)||(skip_en_2 == 1'b1)||(skip_en_3 == 1'b1)||(skip_en_4 == 1'b1))
n_state = SLAVE_ADDR ;
else
n_state = START ;
SLAVE_ADDR : if(skip_en_1 == 1'b1)
n_state = WAIT ;
else if((skip_en_2 == 1'b1)||(skip_en_3 == 1'b1)||(skip_en_4 == 1'b1))
n_state = ACK_1 ;
else
n_state = SLAVE_ADDR ;
ACK_1 : if((skip_en_2 == 1'b1)||(skip_en_3 == 1'b1))
n_state = DEVICE_ADDR ;
else if(skip_en_4 == 1'b1)
n_state = DATA ;
else
n_state = ACK_1 ;
DEVICE_ADDR : if((skip_en_2 == 1'b1)||(skip_en_3 == 1'b1))
n_state = ACK_2 ;
else
n_state = DEVICE_ADDR ;
ACK_2 : if(skip_en_2 == 1'b1)
n_state = DATA ;
else if(skip_en_3 == 1'b1)
n_state = STOP ;
else
n_state = ACK_2 ;
DATA : if(skip_en_2 == 1'b1)
n_state = ACK_3 ;
else if(skip_en_4 == 1'b1)
n_state = NACK ;
else if(error_en == 1'b1)
n_state = IDLE ;
else
n_state = DATA ;
ACK_3 : if(skip_en_2 == 1'b1)
n_state = STOP ;
else
n_state = ACK_3 ;
WAIT : if(skip_en_1 == 1'b1)
n_state = STOP ;
else
n_state = WAIT ;
NACK : if(skip_en_4 == 1'b1)
n_state = STOP ;
else
n_state = NACK ;
STOP : if((skip_en_1 == 1'b1)||(skip_en_2 == 1'b1)||(skip_en_3 == 1'b1)||(skip_en_4 == 1'b1))
n_state = IDLE ;
else
n_state = STOP ;
default : n_state = IDLE ;
endcase
//状态机第三段
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
cnt_wait <= 10'd0 ;
skip_en_1 <= 1'b0 ;
skip_en_2 <= 1'b0 ;
skip_en_3 <= 1'b0 ;
skip_en_4 <= 1'b0 ;
error_en <= 1'b0 ;
cnt_i2c_clk <= 2'd0 ;
cnt_bit <= 3'd0 ;
cnt_delay <= 10'd0 ;
mode <= 3'd0 ;
i2c_end <= 1'b0 ;
end
else
case(c_state)
IDLE :begin
cnt_wait <= cnt_wait + 1'b1 ;
if((cnt_wait == CNT_WAIT_MAX - 2'd2)&&(mode == 3'd0))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 1'b0 ;
if((cnt_wait == CNT_WAIT_MAX - 2'd2)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
if((cnt_wait == CNT_WAIT_MAX - 2'd2)&&(mode == 3'd2))
skip_en_3 <= 1'b1 ;
else
skip_en_3 <= 1'b0 ;
if((cnt_wait == CNT_WAIT_MAX - 2'd2)&&(mode == 3'd3))
skip_en_4 <= 1'b1 ;
else
skip_en_4 <= 1'b0 ;
end
START :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd0))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd2))
skip_en_3 <= 1'b1 ;
else
skip_en_3 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd3))
skip_en_4 <= 1'b1 ;
else
skip_en_4 <= 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_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd0))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd2))
skip_en_3 <= 1'b1 ;
else
skip_en_3 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd3))
skip_en_4 <= 1'b1 ;
else
skip_en_4 <= 1'b0 ;
end
ACK_1 :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if((ack == 1'b1)&&(cnt_i2c_clk == 2'd2)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
if((ack == 1'b1)&&(cnt_i2c_clk == 2'd2)&&(mode == 3'd2))
skip_en_3 <= 1'b1 ;
else
skip_en_3 <= 1'b0 ;
if((ack == 1'b1)&&(cnt_i2c_clk == 2'd2)&&(mode == 3'd3))
skip_en_4 <= 1'b1 ;
else
skip_en_4 <= 1'b0 ;
end
DEVICE_ADDR :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if((cnt_i2c_clk == 2'd3)&&(cnt_bit == 3'd7))
cnt_bit <= 3'd0 ;
else if(cnt_i2c_clk == 2'd3)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd2))
skip_en_3 <= 1'b1 ;
else
skip_en_3 <= 1'b0 ;
end
ACK_2 :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if((ack == 1'b1)&&(cnt_i2c_clk == 2'd2)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
if((ack == 1'b1)&&(cnt_i2c_clk == 2'd2)&&(mode == 3'd2))
skip_en_3 <= 1'b1 ;
else
skip_en_3 <= 1'b0 ;
end
DATA :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if((cnt_i2c_clk == 2'd3)&&(cnt_bit == 3'd7))
cnt_bit <= 3'd0 ;
else if(cnt_i2c_clk == 2'd3)
cnt_bit <= cnt_bit + 1'b1 ;
else
cnt_bit <= cnt_bit ;
if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd3)&&(rec_data == 8'h20))
skip_en_4 <= 1'b1 ;
else
skip_en_4 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(cnt_bit == 3'd7)&&(mode == 3'd3)&&(rec_data != 8'h20))
begin
error_en <= 1'b1 ;
mode <= 3'd0 ;
end
else
begin
error_en <= 1'b0 ;
mode <= mode ;
end
end
ACK_3 :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if((ack == 1'b1)&&(cnt_i2c_clk == 2'd2)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
end
WAIT :begin
if((cnt_delay == CNT_DELAY_MAX - 2'd2)&&(mode == 3'd0))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 1'b0 ;
cnt_delay <= cnt_delay + 1'b1 ;
end
NACK :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if((ack == 1'b1)&&(cnt_i2c_clk == 2'd2)&&(mode == 3'd3))
skip_en_4 <= 1'b1 ;
else
skip_en_4 <= 1'b0 ;
end
STOP :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd0))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd1))
skip_en_2 <= 1'b1 ;
else
skip_en_2 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd2))
skip_en_3 <= 1'b1 ;
else
skip_en_3 <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd3))
skip_en_4 <= 1'b1 ;
else
skip_en_4 <= 1'b0 ;
if(cnt_i2c_clk == 2'd2)
i2c_end <= 1'b1 ;
else
i2c_end <= 1'b0 ;
if(i2c_end == 1'b1)
mode <= mode + 1'b1 ;
else
mode <= mode ;
end
default :begin
cnt_wait <= 10'd0 ;
skip_en_1 <= 1'b0 ;
skip_en_2 <= 1'b0 ;
skip_en_3 <= 1'b0 ;
skip_en_4 <= 1'b0 ;
error_en <= 1'b0 ;
cnt_i2c_clk <= 2'd0 ;
cnt_bit <= 3'd0 ;
cnt_delay <= 10'd0 ;
mode <= mode ;
i2c_end <= 1'b0 ;
end
endcase
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_data <= 8'd0 ;
else
case(c_state)
DATA : if((mode == 3'd3)&&(cnt_i2c_clk == 2'd1))
rec_data <= {rec_data[6:0],sda_in} ;
else
rec_data <= rec_data ;
default : rec_data <= rec_data ;
endcase
always@(*)
case(c_state)
ACK_1,ACK_2,ACK_3 : ack <= ~sda_in ;
NACK : ack <= i2c_sda ;
default : ack <= 1'b0 ;
endcase
always@(*)
case(c_state)
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'd1)||(cnt_i2c_clk == 2'd2))
i2c_scl <= 1'b1 ;
else
i2c_scl <= 1'b0 ;
WAIT : if((cnt_delay == 10'd0)||(cnt_delay == CNT_DELAY_MAX - 1'b1))
i2c_scl <= 1'b0 ;
else
i2c_scl <= 1'b1 ;
STOP : if(cnt_i2c_clk == 2'd0)
i2c_scl <= 1'b0 ;
else
i2c_scl <= 1'b1 ;
default : i2c_scl <= 1'b1 ;
endcase
always@(*)
case(c_state)
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] ;
ACK_1,ACK_2,ACK_3:
i2c_sda <= 1'b0 ;
WAIT,NACK : i2c_sda <= 1'b1 ;
DEVICE_ADDR : i2c_sda <= device_addr[7-cnt_bit] ;
DATA : if((mode == 3'd3)||(mode == 3'd6))
i2c_sda <= sda_in ;
else
i2c_sda <= wr_addr[7-cnt_bit] ;
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
endmodule
本章教程带领各位读者编写代码读取了0x00寄存器内的数据,根据抓取的波形图来看,读取出来的数据是0x20,因此证明我们的唤醒操作是成功的。本章教程到此结束,下一章教程是:PAJ7620U2手势识别——配置寄存器组(5)。如果各位读者朋友对本教程有疑问,可以将问题留言,小编看到后会及时为大家解答,谢谢大家支持。