在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 基于接近式傳感器的智能接近系統(tǒng)設(shè)計(jì)

            基于接近式傳感器的智能接近系統(tǒng)設(shè)計(jì)

            作者: 時(shí)間:2023-12-14 來(lái)源:電子森林 收藏

            實(shí)驗(yàn)任務(wù)

            • 任務(wù):智能手機(jī)通話,手機(jī)靠近耳朵后關(guān)閉屏顯,基于核心板 和 底板 完成智能接近系統(tǒng)設(shè)計(jì)并觀察調(diào)試結(jié)果
            • 要求:驅(qū)動(dòng)底板上的接近式傳感器APDS-9901獲得接近數(shù)據(jù),控制核心板上LED按能量條方式點(diǎn)亮
            • 解析:通過(guò)編程驅(qū)動(dòng)接近式傳感器APDS-9901,獲取接近距離信息,然后根據(jù)距離信息編碼控制8個(gè)LED燈按能量條方式點(diǎn)亮。

            實(shí)驗(yàn)?zāi)康?/h4>

            本節(jié)實(shí)驗(yàn)主要學(xué)習(xí)I2C總線工作原理、協(xié)議及相關(guān)知識(shí),掌握驅(qū)動(dòng)I2C設(shè)備的原理及方法,了解輸入輸出型端口的模型及控制實(shí)現(xiàn),最終實(shí)現(xiàn)智能接近系統(tǒng)的總體設(shè)計(jì)。

            本文引用地址:http://www.biyoush.com/article/202312/453885.htm
            • 熟悉I2C總線工作原理及通信協(xié)議
            • 完成I2C接口接近式傳感器的驅(qū)動(dòng)
            • 完成智能接近系統(tǒng)設(shè)計(jì)實(shí)現(xiàn)

            設(shè)計(jì)框圖

            根據(jù)前面的實(shí)驗(yàn)解析我們可以得知,該設(shè)計(jì)可以拆分成兩個(gè)功能模塊實(shí)現(xiàn),

            • APDS9901Driver:接近式傳感器APDS-9901芯片I2C總線通信驅(qū)動(dòng)模塊。
            • Decoder:將距離信息轉(zhuǎn)換成能量條數(shù)據(jù)。

            Top-Down層次設(shè)計(jì)

            模塊結(jié)構(gòu)設(shè)計(jì)

            實(shí)驗(yàn)原理

            I2C總線介紹

            I2C總線是由Philips公司開(kāi)發(fā)的一種簡(jiǎn)單、雙向二線制同步串行總線。它只需要兩根線即可在連接于總線上的器件之間傳送信息。

            I2C總線連接

            主器件用于啟動(dòng)總線傳送數(shù)據(jù),并產(chǎn)生時(shí)鐘以開(kāi)放傳送的器件,此時(shí)任何被尋址的器件均被認(rèn)為是從器件.在總線上主和從、發(fā)和收的關(guān)系不是恒定的,而取決于此時(shí)數(shù)據(jù)傳送方向。如果主機(jī)要發(fā)送數(shù)據(jù)給從器件,則主機(jī)首先尋址從器件,然后主動(dòng)發(fā)送數(shù)據(jù)至從器件,最后由主機(jī)終止數(shù)據(jù)傳送;如果主機(jī)要接收從器件的數(shù)據(jù),首先由主器件尋址從器件.然后主機(jī)接收從器件發(fā)送的數(shù)據(jù),最后由主機(jī)終止接收過(guò)程。在這種情況下.主機(jī)負(fù)責(zé)產(chǎn)生定時(shí)時(shí)鐘和終止數(shù)據(jù)傳送。

            I2C通信時(shí)序

            字節(jié)格式

            發(fā)送到SDA 線上的每個(gè)字節(jié)必須為8 位,每次傳輸可以發(fā)送的字節(jié)數(shù)量不受限制。每個(gè)字節(jié)后必須跟一個(gè)響應(yīng)位。首先傳輸?shù)氖菙?shù)據(jù)的最高位(MSB),如果從機(jī)要完成一些其他功能后(例如一個(gè)內(nèi)部中斷服務(wù)程序)才能接收或發(fā)送下一個(gè)完整的數(shù)據(jù)字節(jié),可以使時(shí)鐘線SCL 保持低電平,迫使主機(jī)進(jìn)入等待狀態(tài),當(dāng)從機(jī)準(zhǔn)備好接收下一個(gè)數(shù)據(jù)字節(jié)并釋放時(shí)鐘線SCL 后數(shù)據(jù)傳輸繼續(xù)。

            I2C字節(jié)格式

            啟動(dòng)和停止

            在時(shí)鐘線SCL保持高電平期間,數(shù)據(jù)線SDA上的電平被拉低(即負(fù)跳變),定義為I2C總線總線的啟動(dòng)信號(hào),它標(biāo)志著一次數(shù)據(jù)傳輸?shù)拈_(kāi)始。啟動(dòng)信號(hào)是一種電平跳變時(shí)序信號(hào),而不是一個(gè)電平信號(hào)。啟動(dòng)信號(hào)是由主控器主動(dòng)建立的,在建立該信號(hào)之前I2C總線必須處于空閑狀態(tài)。

            在時(shí)鐘線SCL保持高電平期間,數(shù)據(jù)線SDA被釋放,使得SDA返回高電平(即正跳變),稱為I2C總線的停止信號(hào),它標(biāo)志著一次數(shù)據(jù)傳輸?shù)慕K止。停止信號(hào)也是一種電平跳變時(shí)序信號(hào),而不是一個(gè)電平信號(hào),停止信號(hào)也是由主控器主動(dòng)建立的,建立該信號(hào)之后,I2C總線將返回空閑狀態(tài)。

            啟動(dòng)和停止格式

            應(yīng)答響應(yīng)

            數(shù)據(jù)傳輸必須帶響應(yīng),相關(guān)的響應(yīng)時(shí)鐘脈沖由主機(jī)產(chǎn)生。在響應(yīng)的時(shí)鐘脈沖期間,發(fā)送器釋放SDA 線(上拉電阻拉高),接收器必須將SDA 線拉低,使它在這個(gè)時(shí)鐘脈沖的高電平期間保持穩(wěn)定的低電平,這種情況下是應(yīng)答,如果在這個(gè)時(shí)鐘脈沖的高電平期間SDA線沒(méi)有被拉低則表示沒(méi)有應(yīng)答。通常被尋址的接收器在接收到的每個(gè)字節(jié)后,必須產(chǎn)生一個(gè)應(yīng)答。當(dāng)從機(jī)接收器不應(yīng)答時(shí),主機(jī)產(chǎn)生一個(gè)停止或重復(fù)起始條件。

            應(yīng)答響應(yīng)格式

            通信速率

            常見(jiàn)的I2C總線依傳輸速率的不同而有不同的模式:標(biāo)準(zhǔn)模式(100 Kbit/s)、低速模式(10 Kbit/s),但時(shí)鐘頻率可被允許下降至零,這代表可以暫停通信。而新一代的I2C總線可以和更多的節(jié)點(diǎn)(支持10比特長(zhǎng)度的地址空間)以更快的速率通信:快速模式(400 Kbit/s)、高速模式(3.4 Mbit/s)。

            APDS-9901模塊連接

            底板上的接近光傳感器APDS-9901模塊電路圖如下(上拉電阻未顯示):

            APDS-9901模塊電路

            上圖為接近光傳感器APDS-9901模塊電路,與FPGA硬件接口有I2C總線(SCL、SDA)和中斷信號(hào)INT,APDS-9901是博通公司的集成環(huán)境光ALS、紅外光IR和接近距離傳感器,具有體積小、低功耗等優(yōu)點(diǎn),被大量應(yīng)用于手機(jī)、筆記本、相機(jī)、液晶顯示器等電子產(chǎn)品上,環(huán)境光ALS可以根據(jù)外部環(huán)境調(diào)節(jié)設(shè)備屏幕顯示亮度,接近距離傳感器可以根據(jù)應(yīng)用場(chǎng)景實(shí)現(xiàn)產(chǎn)品對(duì)應(yīng)應(yīng)用,例如接聽(tīng)電話時(shí)控制手機(jī)關(guān)閉顯示等,接口采用I2C總線能夠支持400KHz的I2C快速模式。

            APDS-9901內(nèi)部框圖

            雙向端口設(shè)計(jì)

            可綜合Verilog模塊設(shè)計(jì)中必須有端口存在,端口有輸入input,輸出output,雙向inout,對(duì)于輸入和輸出型端口我們很好理解,我們來(lái)了解一下雙向端口信號(hào)的處理。

            在芯片中為了管腳復(fù)用,很多管腳都是雙向的,既可以輸入也可以輸出。在Verilog中即為inout型端口。Inout端口的實(shí)現(xiàn)是使用三態(tài)門(mén),三態(tài)門(mén)的第三個(gè)狀態(tài)是高阻態(tài)Z。在實(shí)際電路中高阻態(tài)意味著響應(yīng)的管腳懸空、斷開(kāi)。

            FPGA引腳輸入輸出模型

            當(dāng)inout用作輸出時(shí),就像平常一樣。當(dāng)inout用作輸入時(shí),需要設(shè)為高阻態(tài),這樣其電平就可以由外部輸入信號(hào)決定了(這是高阻態(tài)的特性)。

            雙向端口應(yīng)用案例:

            module bid
            (
            input           out_en,
            input           a,
            inout           b,
            output          c
            );  
            assign  b = out_en? a : 1'bz;
            assign  c = b; 
            endmodule
            APDS-9901驅(qū)動(dòng)設(shè)計(jì)

            通過(guò)前面的了解,我們對(duì)于整個(gè)I2C總線的驅(qū)動(dòng)原理有了一定的了解,接下來(lái)我們根據(jù)APDS-9901的芯片手冊(cè)了解其驅(qū)動(dòng)方法及參數(shù)要點(diǎn)。

            APDS-9901時(shí)序

            APDS-9901時(shí)序參數(shù)

            通過(guò)APDS-9901時(shí)序參數(shù)了解,APDS-9901支持I2C通信400KHz快速模式同時(shí)兼容100KHz的標(biāo)準(zhǔn)模式,還有兩種模式下時(shí)序中的各種時(shí)間參數(shù),本例中我們就采用標(biāo)準(zhǔn)模式完成驅(qū)動(dòng)設(shè)計(jì)。

            首先我們分頻得到400KHz的時(shí)鐘,整個(gè)設(shè)計(jì)都基于該時(shí)鐘完成,程序?qū)崿F(xiàn)如下:

            //使用計(jì)數(shù)器分頻產(chǎn)生400KHz時(shí)鐘信號(hào)
            clk_400khzreg                 clk_400khz;
            reg     [9:0]       cnt_400khz;
            always@(posedge clk or negedge rst_n) begin
                if(!rst_n) begin
                    cnt_400khz <= 10'd0; 
                    clk_400khz <= 1'b0;
                end else if(cnt_400khz >= CNT_NUM-1) begin
                    cnt_400khz <= 10'd0; 
                    clk_400khz <= ~clk_400khz;
                end else begin
                    cnt_400khz <= cnt_400khz + 1'b1;
                end
                end

            I2C時(shí)序可以分解成基本單元(啟動(dòng)、停止、發(fā)送、接收、發(fā)應(yīng)答、讀應(yīng)答),整個(gè)I2C通信都是由這些單元按照不同的順序組合,我們?cè)O(shè)計(jì)一個(gè)狀態(tài)機(jī),將這些基本單元做成狀態(tài),控制狀態(tài)機(jī)的跳轉(zhuǎn)就能實(shí)現(xiàn)I2C通信時(shí)序。主機(jī)每次發(fā)送數(shù)據(jù)都要接收判斷從機(jī)的響應(yīng),每次接收數(shù)據(jù)也要向從機(jī)發(fā)送響應(yīng),所以發(fā)送單元和讀應(yīng)答單元可以合并,接收單元和寫(xiě)應(yīng)答單元可以合并。

            啟動(dòng)時(shí)序狀態(tài)設(shè)計(jì)程序?qū)崿F(xiàn)如下:

            START:begin //I2C通信時(shí)序中的起始START
                    if(cnt_start >= 3'd5) cnt_start <= 1'b0;    //對(duì)START中的子狀態(tài)執(zhí)行控制cnt_start
                    else cnt_start <= cnt_start + 1'b1;
                    case(cnt_start)
                        3'd0:   begin sda <= 1'b1; 
                        scl <= 1'b1; 
                        end //將SCL和SDA拉高,保持4.7us以上
                        3'd1:   begin sda <= 1'b1; 
                        scl <= 1'b1; 
                        end //每個(gè)周期2.5us,需要兩個(gè)周期
                        3'd2:   begin sda <= 1'b0; 
                        end  //SDA拉低到SCL拉低,保持4.0us以上
                        3'd3:   begin sda <= 1'b0; 
                        end  //clk_400khz每個(gè)周期2.5us,需要兩個(gè)周期
                        3'd4:   begin scl <= 1'b0; 
                        end  //SCL拉低,保持4.7us以上
                        3'd5:   begin scl <= 1'b0; 
                        state <= state_back; 
                        end //每個(gè)周期2.5us,兩個(gè)周期
                        default: state <= IDLE; //如果程序失控,進(jìn)入IDLE自復(fù)位狀態(tài)
                    endcase
                end

            發(fā)送單元和讀應(yīng)答單元合并,時(shí)序狀態(tài)設(shè)計(jì)程序?qū)崿F(xiàn)如下:

            WRITE:begin //I2C通信時(shí)序中的寫(xiě)操作WRITE和相應(yīng)判斷操作ACK
                    if(cnt <= 3'd6) begin   //共需要發(fā)送8bit的數(shù)據(jù),這里控制循環(huán)的次數(shù)
                        if(cnt_write >= 3'd3) begin cnt_write <= 1'b0; 
                        cnt <= cnt + 1'b1; 
                        end
                        else begin cnt_write <= cnt_write + 1'b1; 
                        cnt <= cnt; 
                        end
                    end else begin
                        if(cnt_write >= 3'd7) begin cnt_write <= 1'b0; 
                        cnt <= 1'b0; 
                        end //復(fù)位變量
                        else begin cnt_write <= cnt_write + 1'b1; 
                        cnt <= cnt; 
                        end
                    end
                    case(cnt_write)
                        //按照I2C的時(shí)序傳輸數(shù)據(jù)
                        3'd0:   begin scl <= 1'b0; 
                        sda <= data_wr[7-cnt]; 
                        end   //SCL拉低,SDA輸出
                        3'd1:   begin scl <= 1'b1; 
                        end  //SCL拉高,保持4.0us以上
                        3'd2:   begin scl <= 1'b1; 
                        end  //clk_400khz每個(gè)周期2.5us,需要兩個(gè)周期
                        3'd3:   begin scl <= 1'b0; 
                        end  //SCL拉低,準(zhǔn)備發(fā)送下1bit的數(shù)據(jù)
                        //獲取從設(shè)備的響應(yīng)信號(hào)并判斷
                        3'd4:   begin sda <= 1'bz; 
                        end  //釋放SDA線,準(zhǔn)備接收從設(shè)備的響應(yīng)信號(hào)
                        3'd5:   begin scl <= 1'b1; 
                        end  //SCL拉高,保持4.0us以上
                        3'd6:   begin ack_flag <= i2c_sda; 
                        end  //獲取從設(shè)備的響應(yīng)信號(hào)
                        3'd7:   begin scl <= 1'b0; 
                        if(ack_flag)state <= state; 
                        else state <= state_back; 
                        end //SCL拉低,如果不應(yīng)答循環(huán)寫(xiě)
                        default: state <= IDLE; //如果程序失控,進(jìn)入IDLE自復(fù)位狀態(tài)
                    endcase
                end

            接收單元和寫(xiě)應(yīng)答單元合并,時(shí)序狀態(tài)設(shè)計(jì)程序?qū)崿F(xiàn)如下:

            READ:begin  //I2C通信時(shí)序中的讀操作READ和返回ACK的操作
                    if(cnt <= 3'd6) begin   //共需要接收8bit的數(shù)據(jù),這里控制循環(huán)的次數(shù)
                        if(cnt_read >= 3'd3) begin cnt_read <= 1'b0; 
                        cnt <= cnt + 1'b1; 
                        end
                        else begin cnt_read <= cnt_read + 1'b1; 
                        cnt <= cnt; 
                        end
                    end else begin
                        if(cnt_read >= 3'd7) begin cnt_read <= 1'b0; 
                        cnt <= 1'b0; 
                        end   //復(fù)位變量值
                        else begin cnt_read <= cnt_read + 1'b1; 
                        cnt <= cnt; 
                        end
                    end
                    case(cnt_read)
                        //按照I2C的時(shí)序接收數(shù)據(jù)
                        3'd0:   begin scl <= 1'b0; 
                        sda <= 1'bz; 
                        end //SCL拉低,釋放SDA線
                        3'd1:   begin scl <= 1'b1; 
                        end  //SCL拉高,保持4.0us以上
                        3'd2:   begin data_r[7-cnt] <= i2c_sda; 
                        end //讀取從設(shè)備返回的數(shù)據(jù)
                        3'd3:   begin scl <= 1'b0; 
                        end  //SCL拉低,準(zhǔn)備接收下1bit的數(shù)據(jù)
                        //向從設(shè)備發(fā)送響應(yīng)信號(hào)
                        3'd4:   begin sda <= ack; 
                        end   //發(fā)送響應(yīng)信號(hào),將前面接收的數(shù)據(jù)鎖存
                        3'd5:   begin scl <= 1'b1; 
                        end  //SCL拉高,保持4.0us以上
                        3'd6:   begin scl <= 1'b1; 
                        end  //SCL拉高,保持4.0us以上
                        3'd7:   begin scl <= 1'b0; 
                        state <= state_back; 
                        end //SCL拉低 
                        default: state <= IDLE; //如果程序失控,進(jìn)入IDLE自復(fù)位狀態(tài)
                    endcase
                end

            停止時(shí)序狀態(tài)設(shè)計(jì)程序?qū)崿F(xiàn)如下:

            STOP:begin  //I2C通信時(shí)序中的結(jié)束STOP
                    if(cnt_stop >= 3'd5) cnt_stop <= 1'b0;  //對(duì)STOP中的子狀態(tài)執(zhí)行控制cnt_stop
                    else cnt_stop <= cnt_stop + 1'b1;
                    case(cnt_stop)
                        3'd0:   begin sda <= 1'b0; 
                        end  //SDA拉低,準(zhǔn)備STOP
                        3'd1:   begin sda <= 1'b0; 
                        end  //SDA拉低,準(zhǔn)備STOP
                        3'd2:   begin scl <= 1'b1; 
                        end  //SCL提前SDA拉高4.0us
                        3'd3:   begin scl <= 1'b1; 
                        end  //SCL提前SDA拉高4.0us
                        3'd4:   begin sda <= 1'b1; 
                        end  //SDA拉高
                        3'd5:   begin sda <= 1'b1; 
                        state <= state_back; 
                        end //完成STOP操作
                        default: state <= IDLE; //如果程序失控,進(jìn)入IDLE自復(fù)位狀態(tài)
                    endcase
                end

            基本單元都有了,接下來(lái)我們需要了解APDS-9901驅(qū)動(dòng)的流程,手冊(cè)上看到APDS-9901芯片有很多寄存器,有的配置工作模式,有的配置功能使能,有的返回結(jié)果數(shù)據(jù),這個(gè)需要大家自己查看芯片手冊(cè),這里不作講解。

            APDS-9901寄存器

            手冊(cè)給用戶提供了基本功能的C實(shí)例代碼,流程如下:

            WriteRegData (0, 0); //Disable and Powerdown 
            WriteRegData (1, 0xff); // 2.7 ms – minimum ALS integration time
            WriteRegData (2, 0xff); // 2.7 ms – minimum Prox integration time
            WriteRegData (3, 0xff); // 2.7 ms – minimum Wait time
            WriteRegData (0xe, 1);  // Minimum prox pulse count
            WriteRegData (0xf, 0x20); // CH1 Diode
            WriteRegData (0,0x0f); //Enable WEN PEN AEN PON
            Wait(12);  //Wait for 12 ms 
            CH0_data = Read_Word(0x14); 
            CH1_data = Read_Word(0x16); 
            Prox_data = Read_Word(0x18);  
            WriteRegData(uint8 reg, uint8 data) 
            { 
                m_I2CBus.WriteI2C(0x39, 0x80 | reg, 1, &data); 
            } 
                uint16 Read_Word(uint8 reg)
            { 
                uint8 barr[2]; 
                m_I2CBus.ReadI2C(0x39, 0xA0 | reg, 2, ref barr); 
                return (uint16)(barr[0] + 256 * barr[1]); 
            }

            根據(jù)手冊(cè)提供的軟件操作流程,我們首先有7次向寄存器寫(xiě)入數(shù)據(jù)的操作,按照時(shí)序

            I2C寫(xiě)操作

            向regaddr地址寄存器中寫(xiě)入數(shù)據(jù)regdata,程序?qū)崿F(xiàn)如下

            4'd0:   begin state <= START; 
            end   //I2C通信時(shí)序中的START
            4'd1:   begin data_wr <= dev_addr<<1; 
            state <= WRITE; 
            end  //設(shè)備地址
            4'd2:   begin data_wr <= reg_addr; 
            state <= WRITE; end  //寄存器地址
            4'd3:   begin data_wr <= reg_data; 
            state <= WRITE; 
            end  //寫(xiě)入數(shù)據(jù)
            4'd4:   begin state <= STOP; 
            end    //I2C通信時(shí)序中的STOP

            7次向寄存器寫(xiě)入數(shù)據(jù)的操作需要7段上面的代碼,羅列起來(lái)程序不易讀,干脆我們將1次寫(xiě)操作做成狀態(tài)機(jī)的一個(gè)狀態(tài),這樣7次向寄存器寫(xiě)入數(shù)據(jù)的操作只需要在這個(gè)狀態(tài)上循環(huán)執(zhí)行7次就好了,單詞寫(xiě)操作狀態(tài)程序?qū)崿F(xiàn)如下:

            MODE1:begin //單次寫(xiě)操作
                    if(cnt_mode1 >= 4'd5) cnt_mode1 <= 1'b0;    //對(duì)START中的子狀態(tài)執(zhí)行控制cnt_start
                    else cnt_mode1 <= cnt_mode1 + 1'b1;
                    state_back <= MODE1;
                    case(cnt_mode1)
                        4'd0:   begin state <= START; end   //I2C通信時(shí)序中的START
                        4'd1:   begin data_wr <= dev_addr<<1; state <= WRITE; end   //設(shè)備地址
                        4'd2:   begin data_wr <= reg_addr; state <= WRITE; end  //寄存器地址
                        4'd3:   begin data_wr <= reg_data; state <= WRITE; end  //寫(xiě)入數(shù)據(jù)
                        4'd4:   begin state <= STOP; end    //I2C通信時(shí)序中的STOP
                        4'd5:   begin state <= MAIN; end    //返回MAIN
                        default: state <= IDLE; //如果程序失控,進(jìn)入IDLE自復(fù)位狀態(tài)
                    endcase
                end

            同理兩字節(jié)數(shù)據(jù)連讀的操作也做成一個(gè)狀態(tài)

            I2C讀字操作

            程序?qū)崿F(xiàn)如下:

            MODE2:begin //兩次讀操作
                    if(cnt_mode2 >= 4'd10) cnt_mode2 <= 1'b0;   //對(duì)START中的子狀態(tài)執(zhí)行控制cnt_start
                    else cnt_mode2 <= cnt_mode2 + 1'b1;
                    state_back <= MODE2;
                    case(cnt_mode2)
                        4'd0:   begin state <= START; end   //I2C通信時(shí)序中的START
                        4'd1:   begin data_wr <= dev_addr<<1; state <= WRITE; end   //設(shè)備地址
                        4'd2:   begin data_wr <= reg_addr; state <= WRITE; end  //寄存器地址
                        4'd3:   begin state <= START; end   //I2C通信時(shí)序中的START
                        4'd4:   begin data_wr <= (dev_addr<<1)|8'h01; state <= WRITE; end//設(shè)備地址
                        4'd5:   begin ack <= ACK; state <= READ; end    //讀寄存器數(shù)據(jù)
                        4'd6:   begin dat_l <= data_r; end
                        4'd7:   begin ack <= NACK; state <= READ; end   //讀寄存器數(shù)據(jù)
                        4'd8:   begin dat_h <= data_r; end
                        4'd9:   begin state <= STOP; end    //I2C通信時(shí)序中的STOP
                        4'd10:  begin state <= MAIN; end    //返回MAIN
                        default: state <= IDLE; //如果程序失控,進(jìn)入IDLE自復(fù)位狀態(tài)
                    endcase
                end

            因?yàn)橛玫窖訒r(shí),也設(shè)計(jì)成一個(gè)狀態(tài),程序?qū)崿F(xiàn)如下:

            DELAY:begin //延時(shí)模塊
                    if(cnt_delay >= num_delay) begin
                        cnt_delay <= 1'b0;
                        state <= MAIN; 
                    end else cnt_delay <= cnt_delay + 1'b1;
                end

            最后我們編程控制狀態(tài)機(jī)按照驅(qū)動(dòng)例程代碼中流程運(yùn)行,程序?qū)崿F(xiàn)如下:

            4'd0:   begin dev_addr<=7'h39;reg_addr<=8'h80|8'h00;reg_data<=8'h00;state<=MODE1; end 
            4'd1:   begin dev_addr<=7'h39;reg_addr<=8'h80|8'h01;reg_data<=8'hff;state<=MODE1; end 
            4'd2:   begin dev_addr<=7'h39;reg_addr<=8'h80|8'h02;reg_data<=8'hff;state<=MODE1; end 
            4'd3:   begin dev_addr<=7'h39;reg_addr<=8'h80|8'h03;reg_data<=8'hff;state<=MODE1; end 
            4'd4:   begin dev_addr<=7'h39;reg_addr<=8'h80|8'h0e;reg_data<=8'h01;state<=MODE1; end 
            4'd5:   begin dev_addr<=7'h39;reg_addr<=8'h80|8'h0f;reg_data<=8'h20;state<=MODE1; end 
            4'd6:   begin dev_addr<=7'h39;reg_addr<=8'h80|8'h00;reg_data<=8'h0f;state<=MODE1; end 
            4'd7:   begin state <= DELAY; dat_valid <= 1'b0; end    //12ms延時(shí)
            4'd8:   begin dev_addr <= 7'h39; reg_addr <= 8'ha0|8'h14;  state <= MODE2; end 
            4'd9:   begin ch0_dat <= {dat_h,dat_l}; end //讀取數(shù)據(jù)
            4'd10:  begin dev_addr <= 7'h39; reg_addr <= 8'ha0|8'h16;  state <= MODE2; end 
            4'd11:  begin ch1_dat <= {dat_h,dat_l}; end //讀取數(shù)據(jù)
            4'd12:  begin dev_addr <= 7'h39; reg_addr <= 8'ha0|8'h18;  state <= MODE2; end  
            4'd13:  begin prox_dat <= {dat_h,dat_l}; end    //讀取數(shù)據(jù)
            4'd14:  begin dat_valid <= 1'b1; end    //讀取數(shù)據(jù)
            系統(tǒng)總體實(shí)現(xiàn)

            程序中我們做了一個(gè)簡(jiǎn)單的濾波處理,為了保證數(shù)據(jù)的有效,將瞬間變化太大的采樣數(shù)據(jù)舍棄,程序?qū)崿F(xiàn)如下:

            reg [15:0] prox_dat0,prox_dat1,prox_dat2;
            always @(posedge dat_valid) begin
                prox_dat0 <= prox_dat;
                prox_dat1 <= prox_dat0;
                if(((prox_dat1-prox_dat0) >= 16'h200)||((prox_dat1-prox_dat0) >= 16'h200)) prox_dat2 <= prox_dat2;
                else prox_dat2 <= prox_dat0;
                end

            我們從傳感器讀取的距離信息為16位數(shù)據(jù),有效范圍Full Scale ADC Counts為0~1023,對(duì)應(yīng)0到16‘h3ff,可以設(shè)置一個(gè)閾值,當(dāng)采樣回來(lái)的數(shù)據(jù)與閾值比較控制手機(jī)屏幕的顯示與否,本實(shí)驗(yàn)要求用能量條的方式顯示距離的遠(yuǎn)近,我們?cè)O(shè)計(jì)一個(gè)編碼器將0到16‘h3ff的范圍控制8個(gè)led燈的控制,程序?qū)崿F(xiàn)如下:

            always@(prox_dat2[9:7])
                case (prox_dat2[9:7])
                    3'b000: Y_out = 8'b11111110;
                    3'b001: Y_out = 8'b11111100;
                    3'b010: Y_out = 8'b11111000;
                    3'b011: Y_out = 8'b11110000;
                    3'b100: Y_out = 8'b11100000;
                    3'b101: Y_out = 8'b11000000;
                    3'b110: Y_out = 8'b10000000;
                    3'b111: Y_out = 8'b00000000;
                    default:Y_out = 8'b11111111;
                endcase

            在頂層設(shè)計(jì)中例化兩個(gè)模塊,將信號(hào)連接,程序?qū)崿F(xiàn)如下:

            wire dat_valid;
            wire [15:0] ch0_dat, ch1_dat, prox_dat;
            APDS_9901_Driver u1(
            .clk            (clk            ),  //系統(tǒng)時(shí)鐘
            .rst_n          (rst_n          ),  //系統(tǒng)復(fù)位,低有效
            .i2c_scl        (i2c_scl        ),  //I2C總線SCL
            .i2c_sda        (i2c_sda        ),  //I2C總線SDA
            .dat_valid      (dat_valid      ),  //數(shù)據(jù)有效脈沖
            .ch0_dat        (ch0_dat        ),  //ALS數(shù)據(jù)
            .ch1_dat        (ch1_dat        ),  //IR數(shù)據(jù)
            .prox_dat       (prox_dat       )   //Prox數(shù)據(jù)
            ); 
            Decoder u2(.dat_valid      (dat_valid      ),
            .prox_dat       (prox_dat       ),
            .Y_out          (led            )
            );

            綜合后的設(shè)計(jì)框圖如下:

            RTL設(shè)計(jì)框圖

            實(shí)驗(yàn)步驟

            1. 雙擊打開(kāi)Quartus Prime工具軟件;
            2. 新建工程:File → New Project Wizard(工程命名,工程目錄選擇,設(shè)備型號(hào)選擇,EDA工具選擇);
            3. 新建文件:File → New → Verilog HDL File,鍵入設(shè)計(jì)代碼并保存;
            4. 設(shè)計(jì)綜合:雙擊Tasks窗口頁(yè)面下的Analysis & Synthesis對(duì)代碼進(jìn)行綜合;
            5. 管腳約束:Assignments → Assignment Editor,根據(jù)項(xiàng)目需求分配管腳;
            6. 設(shè)計(jì)編譯:雙擊Tasks窗口頁(yè)面下的Compile Design對(duì)設(shè)計(jì)進(jìn)行整體編譯并生成配置文件;
            7. 程序燒錄:點(diǎn)擊Tools → Programmer打開(kāi)配置工具,Program進(jìn)行下載;
            8. 觀察設(shè)計(jì)運(yùn)行結(jié)果。

            實(shí)驗(yàn)現(xiàn)象

            將設(shè)計(jì)加載到FPGA,手指在接近光傳感器上下移動(dòng),觀察核心板上8個(gè)LED燈的狀態(tài),APDS-9901還是環(huán)境光傳感器,有興趣的同學(xué)可以嘗試一下其他應(yīng)用。

            實(shí)驗(yàn)現(xiàn)象示意



            評(píng)論


            相關(guān)推薦

            技術(shù)專區(qū)

            關(guān)閉