1. 程式人生 > >【原創】zynq-7010下運用I2C匯流排完成對LSM303D感測器的資料讀取

【原創】zynq-7010下運用I2C匯流排完成對LSM303D感測器的資料讀取

介紹

這是本人第一次寫部落格,我的畢設在用FPGA去讀取LSM303D感測器的中的三軸的磁場強度資料,這也是我第一次用zynq-7010板子,第一次接觸Vivado,我用了將近兩個月的時間摸索,中間經歷了很多失敗的嘗試,還好最近有了突破,因為目前網上基本沒有關於FPGA讀取LSM303D的資料,所以這也是我寫這篇博文的出發點。

廢話不多說,讓我來一步步介紹如何通過I2C匯流排完成對LSM303D感測器資料的讀取。本篇博文所用到的軟體是Vivado 2014.4和Xilinx SDK 2014.4,用到的開發板是Zybo-7010.
這裡寫圖片描述 這裡寫圖片描述
這裡寫圖片描述

一、在Vivado裡建立工程

首先,我們開啟Vivado軟體(我用的是2014.4版本),主介面如圖1所示。
圖1  開啟vivado軟體

點選“Create New Project”,在彈出的新建工程嚮導中點選“Next”,彈出設定工程名和工程路徑的對話方塊,如圖2所示。在“Project name”項中輸入工程名LSM303D,在“Project location”項中輸入或選擇工程的路徑,而“Create project subdirectory”如果勾選,則會在所選的工程路徑中建立一個名字與工程名一樣的用於存放工程的新檔案,否則直接在所選工程路徑下建立工程。

圖2  設定工程名和工程路徑

點選“Next”,彈出選擇工程型別的對話方塊,如圖3所示。選擇RTL型別,請注意RTL型別下有“Do not specify sources at this time”選項,意思是在新建工程過程中不指定原始檔。

圖3  選擇工程型別

點選“Next”,彈出選擇器件型號的對話方塊,我們在“Search”欄裡輸入xc7z010clg400,選擇xc7z010clg400-1,如圖4所示。

圖4 選擇器件型號

點選“Next”,在彈出的對話方塊中點選“Finish”完成工程的建立。建立工程後的主介面如圖5所示,其中左側面板是設計流程的管理,從原始檔的新增到綜合、模擬、佈局佈線、到最後板級除錯,都可以通過點選相應的按鍵來完成;中間上面的面板是工程檔案的管理;右側面板是工程資訊總結,包括工程的基本資訊、綜合實現所消耗的資源資訊等;底部面板是各種資訊的輸出,包括編譯資訊、報告等。

圖5 Vivado工程主介面

在主介面左側的“IP Integrator”下點選“Create Block Design”,在彈出的“Design name”對話方塊中輸入LSM303D,如圖6所示。

圖6 輸入Design name

點選“OK”,出現如圖7所示介面,此時的Block Design裡還沒有任何的IP核,因此我們要新增IP來完成我們的Block Design。

圖7 新建立的空的Block Design

點選右側Diagram中高亮的“Add IP”來新增IP核,首先我們在輸入框內輸入ZYNQ,在篩選出的結果下選擇第一個“ZYNQ7 Processing System”,Diagram中便會出現ZYNQ7 Processing System IP,如圖8、圖9所示。

圖8 新增ZYNQ7 Processing System IP

圖9 ZYNQ7 Processing System IP新增完成

點選“Run Block Automation”,在彈出的對話方塊中點選“OK”,系統會自動連線,如圖10所示。

圖10 Run Block Automation

接著雙擊ZYNQ7 Processing System IP進行配置,在彈出的對話方塊中點選上方的“Import XPS Settings”,匯入ZYBO_zynq_def.xml 檔案(可自行百度下載),如圖11所示。

圖11 匯入ZYBO_zynq_def.xml 檔案

匯入完成後如圖12所示。

圖12 匯入ZYBO_zynq_def.xml 檔案完成

點選“DDR Configuration”,在“DDR Controller Configuration”中對“Memory Part”選擇“MT41K256M16 HA-125”,如圖13所示。

圖13 DDR Configuration下的配置

配置完成後點選“OK”,退出對ZYNQ7 Proccessing System IP的配置。系統會如圖14所示。

圖14 配置好的ZYNQ7 Proccessing System IP

接著,我們在Diagram中空白處右鍵然後點選“Add IP”,在“Search”欄中搜索IIC,在搜尋結果中點選“AXI IIC”,結果如圖15所示。

圖15 新增AXI IIC IP

點選“Run Connection Automation”,在彈出的對話方塊中給“All Automation”選項打鉤,然後點選“OK”,系統便會將兩個IP自動連線,系統就設計完成了,結果如圖16所示。

圖16 Block Design 完成

我們可以雙擊AXI IIC IP檢視內部配置,如圖17所示,我們可以知道建立的IIC匯流排的SCL時鐘是100KHz,也就是標準傳輸模式,地址模式使用的是7位地址定址模式。

圖17 AXI IIC IP 配置

接著我們點選中間的“Source”,可以在“Design Source”資料夾下看到“LSM303D”的Block Design工程,此時還沒有生成v檔案,我們需要右鍵“LSM303D”,點選“Generate Output Products”,在彈出的對話方塊中點選“Generate”,如圖18所示。

圖18 Generate Output Products

再次右鍵“LSM303D”,點選“Create HDL Wrapper”,在彈出的對話方塊中選擇“Let Vivado manage wrapper and auto-update”然後點選“OK”,便會自動生成v檔案,結果如圖19所示。

圖19 生成v檔案

然後在左側的“Flow Navigator”下的“Implementation”中點選“Run Implementation”,在彈出的對話方塊中點選“OK”,然後點選“Save”,系統就開始按流程跑綜合、跑例項了,現在我們能做的就是等它跑完。跑完且不報錯會彈出如圖20所示對話方塊,點選“Open Implementation Design”開始進行管腳約束。

圖20 完成Run Implementation

在開啟的介面中,我們點選下方的“I/O Ports”,對iic_rtl_scl_io和iic_rtl_sda_io進行管腳約束配置,“I/O Std” 選擇LVCMOS33, iic_rtl_scl_io管腳選擇H15、iic_rtl_sda_io 管腳選擇J15,兩者的“Pull Type”均選擇PULLUP,如圖21所示。

圖21 進行管腳約束

然後我們按“Ctrl + S”進行檔案儲存,在彈出對話方塊中點選“OK”之後,在“File name”中輸入LSM303D_Constraints後點擊“OK”,便生成了約束檔案,如圖22所示。

圖22 生成約束檔案

然後在左側的“Flow Navigator”下的“Program and debug”中點選“Generate Bitstream”,在彈出的對話方塊中點選“Yes”系統便開始生成bit檔案,這時我們開始等待系統完成bit檔案的生成。完成且沒有報錯後,在彈出的對話方塊中點選“OK”,接著我們點選左上角的“File”,選擇“Export”,點選“Export Hardware”,在彈出的對話方塊中選中“Include bitstream”後,點選“OK”,如圖23所示。

圖23 Export Hardware

接著再次點選“File”,選擇“Launch SDK”,點選“OK”,將自動開啟SDK軟體。如圖24所示。

圖24 開啟SDK軟體

二、在SDK軟體裡建立工程

進入SDK後,點選左上角的“File”,選擇“New”,點選“Application Project”,在彈出的對話方塊的“Project name”中輸入LSM303D,如圖25所示,然後點選“Next”,選擇“Hello World”模板,如圖26所示,點選“Finish”完成工程建立。

圖25 編輯工程名

圖26 選擇Hello World模板

在左側LSM303D資料夾下開啟“src”資料夾,右鍵“Rename”helloworld.c檔案的檔名,修改成main.c,結果如圖27所示。

圖27 修改helloworld.c檔名

然後我們將main.c中的程式碼修改成如下程式碼:

#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include <xiic.h>
#include <xiic_l.h>
#include  <math.h>


#define BaseAddr 0x41600000
#define Slaveaddress 0x1E


/*
 * ------------------------------------------------
 * Slave Registers Configuration:
                  OUT_X_L_M_Reg = 0x08
                  OUT_Y_L_M_Reg = 0x0A
                  OUT_Z_L_M_Reg = 0x0C
                  CTRL7_Reg     = 0x26 //(Data: 0x00) => Continuious Data
    **Other registers are default**
 * ------------------------------------------------
*/
#define OUT_X_L_M_Reg  0x08
#define OUT_Y_L_M_Reg  0x0A
#define OUT_Z_L_M_Reg  0x0C
#define CTRL7_Reg      0x26


    int t;
    int i;

    u8 H1,L1;
    s16 mdata;
    float mdatax,mdatay,mdataz;



void LSM303D_Config(u32 BaseAddress,u8 Slvaddr, u8 Address, u8 Data);
u8 Read(u32 BaseAddress, u8 Slvaddr,u8 Address);

int main()
{
    init_platform();
    printf("Hello World\n\r");
    LSM303D_Config( BaseAddr,Slaveaddress, CTRL7_Reg, 0x00);//Continuous Conversion Mode.
    while(1)
    {
            printf("Magnetic Data: \r\n");
            //Read X-axis Magnetic Data.
            L1 = Read( BaseAddr,Slaveaddress, OUT_X_L_M_Reg);
            H1 = Read( BaseAddr,Slaveaddress, OUT_X_L_M_Reg + 0x01);
            mdata = (H1 << 8) + L1;
            mdatax = mdata * 0.16;
            printf("X_M= %f \r ",mdatax );

            //Read Y-axis Magnetic Data.
            L1 = Read( BaseAddr,Slaveaddress, OUT_Y_L_M_Reg);
            H1 = Read( BaseAddr,Slaveaddress, OUT_Y_L_M_Reg + 0x01);
            mdata = (H1 << 8) + L1;
            mdatay = mdata *0.16;
            printf("Y_M= %f \r ",mdatay );

            //Read Z-axis Magnetic Data.
            L1 = Read( BaseAddr,Slaveaddress, OUT_Z_L_M_Reg);
            H1 = Read( BaseAddr,Slaveaddress, OUT_Z_L_M_Reg + 0x01);
            mdata = (H1 << 8) + L1;
            mdataz = mdata * 0.16;
            printf("Z_M= %f \r ",mdataz );

            printf("\r\n");
            for(t=0;t<50000000;t++);//Delay
    }
    cleanup_platform();
    return 0;
}

void LSM303D_Config(u32 BaseAddress, u8 Slvaddr, u8 Address, u8 Data)
{
    unsigned Result;
    u8 A[2] = {Address,Data};
    u8 *Ptr = A;
    Result = XIic_Send(BaseAddress, Slvaddr, Ptr, 2, XIIC_STOP);
}

u8 Read(u32 BaseAddress,u8 Slvaddr, u8 Address)
{
    unsigned Result;
    u8 A[1] = {Address};
    u8 *Ptr = A;
    u8 Buffer[1];
    Result = XIic_Send(BaseAddress, Slvaddr, Ptr, 1, XIIC_STOP);
    Result = XIic_Recv( BaseAddress,  Slvaddr, Buffer, 1, XIIC_STOP);
    return Buffer[0];
}
  • 程式碼中的Baseaddr是系統自動分配的首地址,可以在Vivado中的“Block Design”下點選“Address Editor” 檢視,如圖28所示。

圖28 檢視Baseaddr

  • 程式碼中的 XIic_Send函式和 XIic_Recv函式均為板載函式庫裡提供的,需要了解的可以點選左側的LSM303D_bsp資料夾,點選ps7_cortexa9_0資料夾,點選libsrc資料夾,點選iic_v_3_0資料夾,開啟src資料夾即可看到所有I2C相關的函數了,如圖29所示。

圖29 bsp板載函式庫

  • 由於本人的畢設中只需要讀取三軸的磁場強度,所以只對感測器的磁場相關的暫存器做了配置,並且只讀取了輸出磁場強度的暫存器,如果想要讀取加速度,參考LSM303D的Datasheet,並作出相應的配置和讀取即可,本文不再贅述。

輸入並儲存好程式碼後,我們就可以建立嵌入式系統了,首先建立fsbl工程。點選“File”,選擇“New”,點選“Application Project”,在“Project name”中輸入fsbl,點選“Next”選擇“Zynq FSBL”,如圖30所示。

圖30 建立fsbl工程

點選“Finish”,右鍵“fsbl”資料夾,點選“Create Boot Image”,在彈出的對話方塊中選擇“Create Image”,如圖31所示。

圖31 建立fsbl Boot Image

用同樣的方法,對工程資料夾“LSM303D”建立Boot Image,如圖32所示。

圖32 建立LSM303D  Boot Image

三、在Vivado中燒錄程式

我們首先將感測器和板子連線,開發板跳線記得要接QSPI模式,LSM303D感測器上SCL腳接板子的H15口,SDA腳接板子的J15口,SD0/SA0腳接地,CS1腳接地,CS2腳接3.3V高電平,接線如圖33所示。

圖33 感測器與開發板連線

接著,我們開啟Vivado,在左邊的“Program and debug”中點選“Open Hardware Manager”,點選“Open Target”,點選“Open New Target”,點選“Next”,再次點選“Next”,彈出如圖34所示對話方塊,點選“Next”,點選“Finish”。

圖34 完成Open Target

接著我們選擇需要燒錄的flash型號,在“Hardware”介面中右鍵xc7x010_1,點選“Add Configuration Memory Device”,在彈出的對話方塊中的“Search”中輸入s25fl128s,在結果中選擇“s25fl128s-3.3v-qspi-x4-single”,如圖35所示。

圖35 選擇燒錄的flash型號

點選“OK”,再次點選“OK”,在彈出的視窗中的“Configuration file”中選擇工程資料夾下的sdk資料夾,在LSM303D資料夾中開啟bootimage資料夾,找到BOOT.bin檔案並選中,如圖36所示。

圖36 選擇需要燒錄的BOOT.bin檔案

點選“OK”,開始燒錄驅動程式,等待燒錄完成後,我們開啟串列埠除錯工具,我用的是SSCOM32,波特率選擇1152000bps,接著按下開發板上的PGOOD鍵(重啟鍵),就可以在串列埠除錯工具中接收到感測器發來的資料了,如圖37所示。

圖37 接受到感測器發來的資料

以上就是用如何在zynq-7010下運用I2C匯流排完成對LSM303D感測器的資料讀取的步驟,整個流程都是本人歷時近兩個月慢慢摸索出來的。希望能對需要用FPGA來讀取LSM303D感測器的人有所幫助~