前言
??本教程是基于FPGA的手勢識別實(shí)現(xiàn)教程,使用到的手勢識別模塊是PAJ7620U2,本文主要向各位讀者闡述如何通過I2C協(xié)議去喚醒該模塊,從模塊狀態(tài)轉(zhuǎn)移圖、模塊波形圖的繪制,到最后代碼的編寫及驗(yàn)證,一步一步教會(huì)各位讀者如何利用FPGA去實(shí)現(xiàn)。
??下面我們簡單介紹一下該模塊,該模塊是在正點(diǎn)原子店鋪購買的,他們提供了利用STM32驅(qū)動(dòng)的手冊,但我們要通過FPGA進(jìn)行驅(qū)動(dòng),因此正點(diǎn)原子提供的手冊,我們可以粗略瀏覽一下,提取關(guān)鍵信息。其中,各位讀者需要注意的是:
??I2C接口支持的通信速率最高為400KHZ,我們的系統(tǒng)時(shí)鐘是50MHZ,因此必須進(jìn)行分頻操作。但是為了提高I2C代碼復(fù)用性,我們采用250KHZ速率通信,I2C驅(qū)動(dòng)時(shí)鐘頻率采用1MHZ。同時(shí)我們可以看到,該模塊檢測的手勢姿勢有多種,本次我們檢測上、下、左、右四個(gè)姿勢就可以了,其它姿勢的檢測,讀者有興趣的話可以自行查看數(shù)據(jù)手冊完成配置。
一、喚醒操作簡介
??為什么要進(jìn)行喚醒操作呢?相信各位讀者會(huì)有這個(gè)疑問,我們根據(jù)PAJ7620U2芯片手冊,向各位讀者詳細(xì)闡述。我們先看一下初始化的步驟:
步驟一:上電,Vbus必須在Vdd之前上電;
步驟二:等待700us,讓PAJ7620U2穩(wěn)定;
步驟三:寫入從機(jī)ID或者是I2C讀取指令去喚醒。
??步驟一我們不用關(guān)注,這是生產(chǎn)廠家需要關(guān)注的問題,我們看后面兩個(gè)步驟。步驟二是在寫入前先等待700us,為了更好地提升模塊性能,這里我們等待1000us,因?yàn)樵撃K是通過I2C協(xié)議配置的,我們在初始狀態(tài)下等待1000us后,再跳轉(zhuǎn)到I2C開始狀態(tài)。步驟三,我們根據(jù)數(shù)據(jù)手冊:
根據(jù)這一個(gè)喚醒指令,I2C開始工作后,發(fā)送從機(jī)ID和一個(gè)W(WRITE)指令,從機(jī)ID如下:
因此發(fā)送的8bit指令為8’b1110_0110。
二、喚醒步驟
1.狀態(tài)轉(zhuǎn)移圖繪制
??說明:初始狀態(tài)(IDLE),等待1000us后跳轉(zhuǎn)到開始狀態(tài),在SCL為高電平時(shí)候,檢測到SDA由高電平變?yōu)榈碗娖剑撮_始工作,如圖所示:
??檢測到開始信號后,跳轉(zhuǎn)到發(fā)送從設(shè)備地址狀態(tài),從設(shè)備地址為從設(shè)備ID+寫指令,即8’b1110_0110,該8bit指令發(fā)送完成后,跳轉(zhuǎn)到等待狀態(tài),等待1000us完成,跳轉(zhuǎn)到結(jié)束狀態(tài)。再結(jié)束狀態(tài)下,在SCL為高電平時(shí),檢測到SDA由低電平變?yōu)楦唠娖?,即表示結(jié)束。結(jié)束信號標(biāo)志如下圖所示:
??各位讀者需要注意的是,結(jié)束信號完成后,狀態(tài)由STOP狀態(tài)變?yōu)镮DLE狀態(tài),在空閑狀態(tài)下,因?yàn)镾CL和SDA有外接上拉電阻的作用,電平會(huì)持續(xù)拉高。
2.模塊波形圖繪制
??首先,對系統(tǒng)時(shí)鐘進(jìn)行分頻操作,分頻出1MHZ的時(shí)鐘,用來驅(qū)動(dòng)I2C模塊運(yùn)行:
??i2c_clk就是我們分頻出來的,1MHZ的時(shí)鐘信號。接下來我們利用該時(shí)鐘信號驅(qū)動(dòng)后續(xù)三段式狀態(tài)機(jī)的實(shí)現(xiàn)。IDLE狀態(tài)如下:
??cnt_wait計(jì)數(shù)器計(jì)數(shù)到最大值1000us時(shí),狀態(tài)機(jī)跳轉(zhuǎn)到開始狀態(tài)。START狀態(tài)如下:
??在檢測到i2c_scl信號為高電平,i2c_sda信號下降沿時(shí),狀態(tài)由開始狀態(tài)跳轉(zhuǎn)為發(fā)送從設(shè)備地址狀態(tài)。這里要注意的是,我們不直接用SDA信號的原因是,SDA是一個(gè)inout類型的信號,要借助三態(tài)門完成賦值,將SDA賦值給i2c_sda信號,我們通過對該信號操作,從而間接實(shí)現(xiàn)對SDA信號操作。SALVE_ADDR狀態(tài)如下:
??從設(shè)備地址發(fā)送完成,跳轉(zhuǎn)到等待狀態(tài),等待1000us結(jié)束跳轉(zhuǎn)到STOP狀態(tài),WAIT狀態(tài)和STOP狀態(tài)如下:
??在這里我們必須考慮引入兩個(gè)信號,一個(gè)是結(jié)束標(biāo)志信號i2c_end,一個(gè)是模式信號mode。引入mode信號的原因是,我們需要發(fā)送不同的指令實(shí)現(xiàn)數(shù)據(jù)的接收及發(fā)送,因此這些操作有差異。我們本次的操作為喚醒操作,令mode=0,通過該信號,編寫代碼時(shí),狀態(tài)跳轉(zhuǎn)就按喚醒操作來進(jìn)行,避免后續(xù)狀態(tài)跳轉(zhuǎn)產(chǎn)生影響。
3.上板驗(yàn)證
??在這里我們直接上板抓取信號的波形,觀察波形變化情況,來判斷喚醒操作是否成功。設(shè)置如下:
??我們在這里抓取跳轉(zhuǎn)信號skip_en的上升沿,得到如下波形:
??我們發(fā)現(xiàn),狀態(tài)在WAIT狀態(tài)持續(xù)拉高而不見拉低,因?yàn)樵赪AIT狀態(tài)下,需要延遲1000us,才跳轉(zhuǎn)到STOP狀態(tài)。因此我們需要更改一下觸發(fā)條件:
??在這里,抓取等待信號下降沿,即可查看后續(xù)波形。后續(xù)波形如下:
??結(jié)束信號拉高,模式信號自增1,表示我們喚醒操作結(jié)束,代碼編寫無誤。文章來源:http://www.zghlxwxcb.cn/news/detail-449442.html
4.參考代碼
#(
parameter SLAVE_ID = 7'b111_0011 ,
SYS_CLK_FREQ= 26'd50_000_000 ,
SCL_FREQ = 23'd250_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
output wire scl ,
inout wire sda
);
localparam CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ;
localparam CNT_T1_MAX = 'd1000 ,
CNT_T2_MAX = 'd1000 ;
localparam IDLE = 'd0 ,
START = 'd1 ,
SLAVE_ADDR = 'd2 ,
WAIT = 'd3 ,
STOP = 'd4 ;
reg [4:0] n_state ;
reg [4:0] c_state ;
reg [4:0] cnt_clk ;
reg i2c_clk ;
reg skip_en_1 ;
reg [9:0] cnt_wait ;
reg i2c_scl ;
reg i2c_sda ;
reg i2c_end ;
reg [1:0] cnt_i2c_clk ;
reg [2:0] cnt_bit ;
reg [9:0] cnt_delay ;
reg [2:0] mode ;
reg [7:0] slave_addr ;
reg [7:0] device_addr ;
reg [7:0] wr_addr ;
wire sda_en ;
wire sda_in ;
assign scl = i2c_scl ;
assign sda_in = sda ;
assign sda_en = 1'b1 ;
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
default : begin
slave_addr <= slave_addr ;
device_addr <= device_addr ;
wr_addr <= wr_addr ;
end
endcase
///生成i2c設(shè)備驅(qū)動(dòng)時(shí)鐘/
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 ;
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 ;
///
///狀態(tài)機(jī)第一段
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
c_state <= IDLE ;
else
c_state <= n_state ;
//狀態(tài)機(jī)第二段
always@(*)
case(c_state)
IDLE : if(skip_en_1 == 1'b1)
n_state <= START ;
else
n_state <= IDLE ;
START : if(skip_en_1 == 1'b1)
n_state <= SLAVE_ADDR ;
else
n_state <= START ;
SLAVE_ADDR : if(skip_en_1 == 1'b1)
n_state <= WAIT ;
else
n_state <= SLAVE_ADDR ;
WAIT : if(skip_en_1 == 1'b1)
n_state <= STOP ;
else
n_state <= WAIT ;
STOP : if(skip_en_1 == 1'b1)
n_state <= IDLE ;
else
n_state <= STOP ;
default : n_state <= IDLE ;
endcase
///狀態(tài)機(jī)第三段///
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 ;
cnt_i2c_clk <= 2'd0 ;
cnt_bit <= 3'd0 ;
i2c_end <= 1'b0 ;
mode <= 3'd0 ;
cnt_delay <= 10'd0 ;
end
else
case(c_state)
IDLE :begin
if(cnt_wait == CNT_T1_MAX - 1'b1)
cnt_wait <= 10'd0 ;
else
cnt_wait <= cnt_wait + 1'b1 ;
if((cnt_wait == CNT_T1_MAX - 2'd2)&&(mode == 3'd0))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 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 ;
end
SLAVE_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'd0))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 1'b0 ;
end
WAIT :begin
cnt_delay <= cnt_delay + 1'b1 ;
if(cnt_delay == CNT_T2_MAX - 2'd2)
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 1'b0 ;
end
STOP :begin
cnt_i2c_clk <= cnt_i2c_clk + 1'b1 ;
if(cnt_i2c_clk == 2'd2)
i2c_end <= 1'b1 ;
else
i2c_end <= 1'b0 ;
if((cnt_i2c_clk == 2'd2)&&(mode == 3'd0))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 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 ;
cnt_i2c_clk <= 2'd0 ;
cnt_bit <= 3'd0 ;
i2c_end <= 1'b0 ;
mode <= mode ;
cnt_delay <= 10'd0 ;
end
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:
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_T2_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] ;
WAIT : i2c_sda <= 1'b1 ;
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
總結(jié)
??該部分介紹了如何喚醒PAJ7620U2手勢識別模塊,當(dāng)然這只是配置該模塊其中一個(gè)很小的部分,各位讀者朋友可結(jié)合源碼和本教程波形圖和源碼,一步一步自行嘗試設(shè)計(jì)信號來驅(qū)動(dòng)驗(yàn)證。文章來源地址http://www.zghlxwxcb.cn/news/detail-449442.html
到了這里,關(guān)于PAJ7620U2手勢識別——喚醒操作(1)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!