SPI实验
目錄
- SPI & ICM-20608 簡介
- SPI 簡介
- SPI四線
- SPI四種工作模式
- SPI時序圖
- I.MX6U ECSPI 簡介
- ICM-20608 簡介
- 硬件原理分析
- 實驗程序編寫
- 編譯下載驗證
- 編寫Makefile 和鏈接腳本
- 編譯下載
同I2C 一樣,SPI 是很常用的通信接口,也可以通過SPI 來連接眾多的傳感器。相比I2C 接口,SPI 接口的通信速度很快,I2C 最多400KHz,但是SPI 可以到達幾十MHz。I.MX6U 也有4 個SPI 接口,可以通過這4 個SPI 接口來連接一些SPI 外設。I.MX6U-ALPHA 使用SPI3 接口連接了一個六軸傳感器ICM-20608,本章我們就來學習如何使用I.MX6U 的SPI 接口來驅動
ICM-20608,讀取ICM-20608 的六軸數據。
SPI & ICM-20608 簡介
SPI 簡介
上一章我們講解了I2C,I2C 是串行通信的一種,只需要兩根線就可以完成主機和從機之間的通信,但是I2C 的速度最高只能到400KHz,如果對于訪問速度要求比價高的話I2C 就不適合了。本章我們就來學習一下另外一個和I2C 一樣廣泛使用的串行通信:SPI。
SPI 全稱是 Serial Perripheral Interface,也就是串行外圍設備接口。 SPI 是 Motorola 公司推出的一種同步串行接口技術,是一種高速、全雙工的同步通信總線, SPI 時鐘頻率相比 I2C 要高很多,最高可以工作在上百 MHz。 SPI 以主從方式工作,通常是有一個主設備和一個或多個從設備,一般 SPI 需要4 根線,但是也可以使用三根線(單向傳輸)。
SPI四線
- ①、 CS/SS, Slave Select/Chip Select,這個是片選信號線,用于選擇需要進行通信的從設備。I2C 主機是通過發送從機設備地址來選擇需要進行通信的從機設備的, SPI 主機不需要發送從機設備,直接將相應的從機設備片選信號拉低即可。
- ②、 SCK, Serial Clock,串行時鐘,和 I2C 的 SCL 一樣,為 SPI 通信提供時鐘。
- ③、 MOSI/SDO, Master Out Slave In/Serial Data Output,簡稱主出從入信號線,這根數據線只能用于主機向從機發送數據,也就是主機輸出,從機輸入。
- ④、 MISO/SDI, Master In Slave Out/Serial Data Input,簡稱主入從出信號線,這根數據線只能用戶從機向主機發送數據,也就是主機輸入,從機輸出。
SPI 通信都是由主機發起的,主機需要提供通信的時鐘信號。主機通過 SPI 線連接多個從設備的結構如下圖所示:
SPI四種工作模式
SPI 有四種工作模式,通過串行時鐘極性(CPOL)和相位(CPHA)的搭配來得到四種工作模式:
- ①、 CPOL=0,串行時鐘空閑狀態為低電平。
- ②、 CPOL=1,串行時鐘空閑狀態為高電平,此時可以通過配置時鐘相位(CPHA)來選擇具體的傳輸協議。
- ③、 CPHA=0,串行時鐘的第一個跳變沿(上升沿或下降沿)采集數據。
- ④、 CPHA=1,串行時鐘的第二個跳變沿(上升沿或下降沿)采集數據。
這四種工作模式如下圖所示:
SPI時序圖
以 CPOL=0, CPHA=0 這個工作模式為例, SPI 進行全雙工通信的時序如下圖所示:
從上圖可以看出, SPI 的時序圖很簡單,不像 I2C 那樣還要分為讀時序和寫時序,因為 SPI 是全雙工的,所以讀寫時序可以一起完成。圖中CS 片選信號先拉低,選中要通信的從設備,然后通過 MOSI 和 MISO 這兩根數據線進行收發數據, MOSI 數據線發出了0XD2 這個數據給從設備,同時從設備也通過 MISO 線給主設備返回了 0X66 這個數據。這個就是 SPI 時序圖。
關于SPI 就講解到這里,接下來我們看一下I.MX6U 自帶的SPI 外設:ECSPI。
I.MX6U ECSPI 簡介
I.MX6U 自帶的SPI 外設叫做ECSPI,全稱是Enhanced Configurable Serial Peripheral Interface,別看前面加了個“EC”就以為和標準SPI 有啥不同的,其實就是SPI。ECSPI 有64 * 32 個接收FIFO(RXFIFO)和64 * 32 個發送FIFO(TXFIFO),ECSPI 特性如下:
①、全雙工同步串行接口。
②、可配置的主/從模式。
③、四個片選信號,支持多從機。
④、發送和接收都有一個32x64 的FIFO。
⑤、片選信號SS/CS,時鐘信號SCLK 極性可配置。
⑥、支持DMA。
I.MX6U 的ECSPI 可以工作在主模式或從模式,本章我們使用主模式,I.MX6U 有4 個ECSPI,每個ECSPI 支持四個片選信號,也就說,如果你要使用ECSPI 的硬件片選信號的話,一個ECSPI 可以支持4 個外設。如果不使用硬件的片選信號就可以支持無數個外設,本章實驗我們不使用硬件片選信號,因為硬件片選信號只能使用指定的片選IO,軟件片選的話可以使用
任意的IO。
我們接下來看一下ECSPI 的幾個重要的寄存器,首先看一下ECSPIx_CONREG(x=1~4)寄存器,這是ECSPI 的控制寄存器,此寄存器結構如圖27.1.2.1 所示:
寄存器ECSPIx_CONREG 各位含義如下:
BURST_LENGTH(bit31:24):突發長度,設置SPI 的突發傳輸數據長度,在一次SPI 發送中最大可以發送2^12bit 數據。可以設置0X000~ 0XFFF,分別對應1~2^12bit。我們一般設置突發長度為一個字節,也就是8bit,BURST_LENGTH=7。
CHANNEL_SELECT(bit19:18):SPI 通道選擇,一個ECSPI 有四個硬件片選信號,每個片選信號是一個硬件通道,雖然我們本章實驗使用的軟件片選,但是SPI 通道還是要選擇的。可設置為0~ 3,分別對應通道0~3。I.MX6U-ALPHA 開發板上的ICM-20608 的片選信號接的是ECSPI3_SS0,也就是ECSPI3 的通道0,所以本章實驗設置為0。
DRCTL(bit17:16):SPI 的SPI_RDY 信號控制位,用于設置SPI_RDY 信號,為0 的話不關心SPI_RDY 信號;為1 的話SPI_RDY 信號為邊沿觸發;為2 的話SPI_DRY 是電平觸發。
PRE_DIVIDER(bit15:12):SPI 預分頻,ECSPI 時鐘頻率使用兩步來完成分頻,此位設置的是第一步,可設置0~ 15,分別對應1~16 分頻。
POST_DIVIDER(bit11:8):SPI 分頻值,ECSPI 時鐘頻率的第二步分頻設置,分頻值為2^POST_DIVIDER。
CHANNEL_MODE(bit7:4):SPI 通道主/從模式設置,HANNEL_MODE[3:0]分別對應SPI通道3~0,為0 的話就是設置為從模式,如果為1 的話就是主模式。比如設置為0X01 的話就是設置通道0 為主模式。
SMC(bit3):開始模式控制,此位只能在主模式下起作用,為0 的話通過XCH 位來開啟SPI突發訪問,為1 的話只要向TXFIFO 寫入數據就開啟SPI 突發訪問。
XCH(bit2):此位只在主模式下起作用,當SMC 為0 的話此位用來控制SPI 突發訪問的開啟。
HT(bit1):HT 模式使能位,I.MX6ULL 不支持。
EN(bit0):SPI 使能位,為0 的話關閉SPI,為1 的話使能SPI。
接下來看一下寄存器ECSPIx_CONFIGREG,這個也是ECSPI 的配置寄存器,此寄存器結構如圖27.1.2.2 所示:
寄存器ECSPIx_CONFIGREG 用到的重要位如下:
HT_LENGTH(bit28:24):HT 模式下的消息長度設置,I.MX6ULL 不支持。
SCLK_CTL(bit23:20):設置SCLK 信號線空閑狀態電平,SCLK_CTL[3:0]分別對應通道3~0,為0 的話SCLK 空閑狀態為低電平,為1 的話SCLK 空閑狀態為高電平。
DATA_CTL(bit19:16):設置DATA 信號線空閑狀態電平,DATA_CTL[3:0]分別對應通道3~0,為0 的話DATA 空閑狀態為高電平,為1 的話DATA 空閑狀態為低電平。
SS_POL(bit15:12):設置SPI 片選信號極性設置,SS_POL[3:0]分別對應通道3~0,為0 的話片選信號低電平有效,為1 的話片選信號高電平有效。
SCLK_POL(bit7:4):SPI 時鐘信號極性設置,也就是CPOL,
SCLK_POL[3:0]分別對應通道3~0,為0 的話SCLK 高電平有效(空閑的時候為低電平),為1 的話SCLK 低電平有效(空閑的時候為高電平)。
SCLK_PHA(bit3:0):SPI時鐘相位設置,也就是CPHA,SCLK_PHA[3:0]分別對應通道3~0,為0 的話串行時鐘的第一個跳變沿(上升沿或下降沿)采集數據,為1 的話串行時鐘的第二個跳變沿(上升沿或下降沿)采集數據。
通過SCLK_POL 和SCLK_PHA 可以設置SPI 的工作模式。
接下來看一下寄存器ECSPIx_PERIODREG,這個是ECSPI 的采樣周期寄存器,此寄存器結構如圖27.1.2.3 所示:
寄存器ECSPIx_PERIODREG 用到的重要位如下:
CSD_CTL(bit21:16):片選信號延時控制位,用于設置片選信號和第一個SPI 時鐘信號之間的時間間隔,范圍為0~63。
CSRC(bit15):SPI 時鐘源選擇,為0 的話選擇SPI CLK 為SPI 的時鐘源,為1 的話選擇32.768KHz 的晶振為SPI 時鐘源。我們一般選擇SPI CLK 作為SPI 時鐘源,SPI CLK 時鐘來源如圖27.1.2.4 所示:
圖27.1.2.4 中各部分含義如下:
①、這是一個選擇器,用于選擇根時鐘源,由寄存器CSCDR2 的位ECSPI_CLK_SEL 來控制,為0 的話選擇pll3_60m 作為ECSPI 根時鐘源。為1 的話選擇osc_clk 作為ECSPI 時鐘源。本章我們選擇pll3_60m 作為ECSPI 根時鐘源。
②、ECSPI 時鐘分頻值,由寄存器CSCDR2 的位ECSPI_CLK_PODF 來控制,分頻值為2^ECSPI_CLK_PODF。本章我們設置為0,也就是1 分頻。
③、最終進入ECSPI 的時鐘,也就是SPI CLK=60MHz。
SAMPLE_PERIO:采樣周期寄存器,可設置為0~0X7FFF 分別對應0~32767 個周期。
接下來看一下寄存器ECSPIx_STATREG,這個是ECSPI 的狀態寄存器,此寄存器結構如圖27.1.2.5 所示:
寄存器ECSPIx_STATREG 用到的重要位如下:
TC(bit7):傳輸完成標志位,為0 表示正在傳輸,為1 表示傳輸完成。
RO(bit6):RXFIFO 溢出標志位,為0 表示RXFIFO 無溢出,為1 表示RXFIFO 溢出。
RF(bit5):RXFIFO 空標志位,為0 表示RXFIFO 不為空,為1 表示RXFIFO 為空。
RDR(bit4):RXFIFO 數據請求標志位,此位為0 表示RXFIFO 里面的數據不大于RX_THRESHOLD,此位為1 的話表示RXFIFO 里面的數據大于RX_THRESHOLD。
RR(bit3):RXFIFO 就緒標志位,為0 的話RXFIFO 沒有數據,為1 的話表示RXFIFO 中至少有一個字的數據。
TF(bit2):TXFIFO 滿標志位,為0 的話表示TXFIFO 不為滿,為1 的話表示TXFIFO 為滿。
TDR(bit1):TXFIFO 數據請求標志位,為0 表示TXFIFO 中的數據大于TX_THRESHOLD,為1 表示TXFIFO 中的數據不大于TX_THRESHOLD。
TE(bit0):TXFIFO 空標志位,為0 表示TXFIFO 中至少有一個字的數據,為1 表示TXFIFO為空。
最后就是兩個數據寄存器,ECSPIx_TXDATA 和ECSPIx_RXDATA,這兩個寄存器都是32位的,如果要發送數據就向寄存器ECSPIx_TXDATA 寫入數據,讀取及存取ECSPIx_RXDATA里面的數據就可以得到剛剛接收到的數據。
關于ECSPI 的寄存器就介紹到這里,關于這些寄存器詳細的描述,請參考《I.MX6ULL 參考手冊》第805 頁的20.7 小節。
ICM-20608 簡介
ICM-20608 是InvenSense 出品的一款6 軸MEMS 傳感器,包括3 軸加速度和3 軸陀螺儀。ICM-20608 尺寸非常小,只有3x3x0.75mm,采用16P 的LGA 封裝。ICM-20608 內部有一個512字節的FIFO。陀螺儀的量程范圍可以編程設置,可選擇±250,±500,±1000 和±2000°/s,加速度的量程范圍也可以編程設置,可選擇±2g,±4g,±8g 和±16g。陀螺儀和加速度計都是16 位的ADC,并且支持I2C 和SPI 兩種協議,使用I2C 接口的話通信速度最高可以達到400KHz,使用SPI 接口的話通信速度最高可達到8MHz。I.MX6U-ALPHA 開發板上的ICM-20608 通過SPI 接口和I.MX6U 連接在一起。ICM-20608 特性如下:
①、陀螺儀支持X,Y 和Z 三軸輸出,內部集成16 位ADC,測量范圍可設置:±250,±500,±1000 和±2000°/s。
②、加速度計支持X,Y 和Z 軸輸出,內部集成16 位ADC,測量范圍可設置:±2g,± 4g,±4g,±8g 和±16g。
③、用戶可編程中斷。
④、內部包含512 字節的FIFO。
⑤、內部包含一個數字溫度傳感器。
⑥、耐10000g 的沖擊。
⑦、支持快速I2C,速度可達400KHz。
⑧、支持SPI,速度可達8MHz。
ICM-20608 的3 軸方向如圖27.1.3.1 所示:
ICM-20608 的結構框圖如圖27.1.3.2 所示:
如果使用IIC 接口的話ICM-20608 的AD0 引腳決定I2C 設備從地址的最后一位,如果AD0為0 的話ICM-20608 從設備地址是0X68,如果AD0 為1 的話ICM-20608 從設備地址為0X69。本章我們使用SPI 接口,跟上一章使用AP3216C 一樣,ICM-20608 也是通過讀寫寄存器來配置和讀取傳感器數據,使用SPI 接口讀寫寄存器需要16 個時鐘或者更多(如果讀寫操作包括多個
字節的話),第一個字節包含要讀寫的寄存器地址,寄存器地址最高位是讀寫標志位,如果是讀的話寄存器地址最高位要為1,如果是寫的話寄存器地址最高位要為0,剩下的7 位才是實際的寄存器地址,寄存器地址后面跟著的就是讀寫的數據。表27.1.3.1 列出了本章實驗用到的一些寄存器和位,關于ICM-20608 的詳細寄存器和位的介紹請參考ICM-20608 的寄存器手冊:
ICM-20608 的介紹就到這里,關于ICM-20608 的詳細介紹請參考ICM-20608 的數據手冊和寄存器手冊。
硬件原理分析
本試驗用到的資源如下:
①、指示燈LED0。
②、RGB LCD 屏幕。
③、ICM20608
④、串口
ICM-20608 是在I.MX6U-ALPHA 開發板底板上,原理圖如圖27.2.1 所示:
實驗程序編寫
本實驗對應的例程路徑為:開發板光盤-> 1、裸機例程-> 18_spi。
本章實驗在上一章例程的基礎上完成,更改工程名字為“icm20608”,然后在bsp 文件夾下創建名為“spi”和“icm20608”的文件。在bsp/spi 中新建bsp_spi.c 和bsp_spi.h 這兩個文件,在bsp/icm20608 中新建bsp_icm20608.c 和bsp_icm20608.h 這兩個文件。bsp_spi.c 和bsp_spi.h是I.MX6U 的SPI 文件,bsp_icm20608.c 和bsp_icm20608.h 是ICM20608 的驅動文件。在bsp_spi.h中輸入如下內容:
1 #ifndef _BSP_SPI_H 2 #define _BSP_SPI_H 3 /*************************************************************** 4 Copyright ? zuozhongkai 5 文件名: bsp_spi.h文件bsp_spi.h 內容很簡單,就是函數聲明。在文件bsp_spi.c 中輸入如下內容:
/*************************************************************** Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 文件名: bsp_spi.c 作者: 左忠凱 版本: V1.0 描述: SPI驅動文件。 其他: 無 論壇: www.openedv.com 日志: 初版V1.0 2019/1/17 左忠凱創建 ***************************************************************/ 1 #include "bsp_spi.h" 2 #include "bsp_gpio.h" 3 #include "stdio.h" 4 5 /* 6 * @description : 初始化SPI 7 * @param - base : 要初始化的SPI 8 * @return : 無 9 */ 10 void spi_init(ECSPI_Type *base) 11 { 12 /* 配置CONREG寄存器 13 * bit0 : 1 使能ECSPI 14 * bit3 : 1 當向TXFIFO寫入數文件bsp_spi.c 中有兩個函數:spi_init 和spich0_readwrite_byte,函數spi_init 是SPI 初始化函數,此函數會初始化SPI 的時鐘,通道等。函數spich0_readwrite_byte 是SPI 收發函數,通過此函數即可完成SPI 的全雙工數據收發。
接下來在文件bsp_icm20608.h 中輸入如下內容:
1 #ifndef _BSP_ICM20608_H 2 #define _BSP_ICM20608_H 3 /*************************************************************** 4 Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 5 文件名: bsp_icm20608.h 6 作者: 左忠凱 7 版本: V1.0 8 描述: ICM20608驅動文件。 9 其他: 無 10 論壇: www.openedv.com 11 日志: 初版V1.0 2019/3/26 左忠凱創建 12 ***************************************************************/ 13 #include "imx6ul.h" 14 #include "bsp_gpio.h" 15 16 /* SPI片選信號*/ 17 #define ICM20608_CSN(n) (n ? gpio_pinwrite(GPIO1, 20, 1) : gpio_pinwrite(GPIO1, 20, 0)) 18 19 #define ICM20608G_ID 0XAF /* ID值*/文件bsp_icm20608.h 里面先定義了一個宏ICM20608_CSN,這個是ICM20608 的SPI 片選引腳。接下來定義了一些ICM20608 的ID 和寄存器地址。第41 行定義了一個結構體icm20608_dev_struc,這個結構體是ICM20608 的設備結構體,里面的成員變量用來保存ICM20608 的原始數據值和經過轉換得到的實際值。實際值是有小數的,本章例程取兩位小數,
為了方便計算,實際值擴大了100 倍,這樣實際值就是整數了,但是在使用的時候要除100 重新得到小數部分。最后就是一些函數聲明,接下來在文件bsp_icm20608.c 中輸入如下所示內容:
文件bsp_imc20608.c 是ICM20608 的驅動文件,里面有7 個函數,我們依次來看一下。第1 個函數是icm20608_init,這個是ICM20608 的初始化函數,此函數先初始化ICM20608 所使用的SPI 引腳,將其復用為ECSPI3。因為我們本章的SPI 片選采用軟件控制的方式,所以SPI片選引腳設置成了普通的輸出模式。設置完SPI 所使用的引腳以后就是調用函數spi_init 來初始化SPI3,最后初始化ICM20608,就是配置ICM20608 的寄存器。第2 個和第3 個函數分別是icm20608_write_reg 和icm20608_read_reg,這兩個函數分別用于寫/讀ICM20608 的指定寄存器。第4 個函數是icm20608_read_len,此函數也是讀取ICM20608 的寄存器值,但是此函數可以讀取連續多個寄存器的值,一般用于讀取ICM20608 傳感器數據。第5 和第6 個函數分別是
icm20608_gyro_scaleget 和icm20608_accel_scaleget,這兩個函數分別用于獲取陀螺儀和加速度計的分辨率,因為陀螺儀和加速度的測量范圍設置的不同,其分辨率就不同,所以在計算實際值的時候要根據實際的量程范圍來得到對應的分辨率。最后一個函數是icm20608_getdata,此函數就是用于獲取ICM20608 的加速度計、陀螺儀和溫度計的數據,并且會根據設置的測量范圍計算出實際的值,比如加速度的g 值、陀螺儀的角速度值和溫度計的溫度值。
最后在main.c 中輸入如下內容:
/************************************************************** Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 文件名: main.c 作者: 左忠凱 版本: V1.0 描述: I.MX6U開發板裸機實驗19 SPI實驗 其他: SPI也是最常用的接口,ALPHA開發板上有一個6軸傳感器ICM20608, 這個六軸傳感器就是SPI接口的,本實驗就來學習如何驅動I.MX6U 的SPI接口,并且通過SPI接口讀取ICM20608的數據值。 論壇: www.openedv.com 日志: 初版V1.0 2019/1/17 左忠凱創建 **************************************************************/ 1 #include "bsp_clk.h" 2 #include "bsp_delay.h文件main.c 一開始有兩個函數integer_display 和decimals_display,這兩個函數用于在LCD上顯示獲取到的ICM20608 數據值,函數integer_display 用于顯示原始數據值,也就是整數值。函數decimals_display 用于顯示實際值,實際值擴大了100 倍,此函數會提取出實際值的整數部分和小數部分并顯示在LCD 上。另一個重要的函數是imx6ul_hardfpu_enable,這個函數用于開啟I.MX6U 的NEON 和硬件FPU(浮點運算單元),因為本章使用到了浮點運算,而I.MX6U 的Cortex-A7 是支持NEON 和FPU(VFPV4_D32)的,但是在使用I.MX6U 的硬件FPU 之前是先要開啟的。
第110 行調用了函數icm20608_init 來初始化ICM20608,如果初始化失敗的話就會在LCD上閃爍提示語句。最后在main 函數的while 循環中不斷的調用函數icm20608_getdata 獲取ICM20608 的傳感器數據,并且顯示在LCD 上。實驗程序編寫就到這里結束了,接下來就是編譯、下載和驗證了。
編譯下載驗證
編寫Makefile 和鏈接腳本
修改Makefile 中的TARGET 為icm20608,然后在在INCDIRS 和SRCDIRS 中加入“bsp/spi”和“bsp/icm20608”,修改后的Makefile 如下:
1 CROSS_COMPILE ?= arm-linux-gnueabihf- 2 TARGET ?= icm20608 3 4 /* 省略掉其它代碼...... */第2 行修改變量TARGET 為“icm20608”,也就是目標名稱為“icm20608”。
第23 和24 行在變量INCDIRS 中添加SPI 和ICM20608 的驅動頭文件(.h)路徑。
第43 和44 行在變量SRCDIRS 中添加SPI 和ICM20608 驅動文件(.c)路徑。
第49 行加入了“-march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard”指令,這些指令用于指定編譯浮點運算的時候使用硬件FPU。因為本章使用到了浮點運算,而I.MX6U 是支持硬件FPU 的,雖然我們在main 函數中已經打開了NEON 和FPU,但是在編譯相應C 文件的時候也要指定使用硬件FPU 來編譯浮點運算。
鏈接腳本保持不變。
編譯下載
使用Make 命令編譯代碼,編譯成功以后使用軟件imxdownload 將編譯完成的icm20608.bin文件下載到SD 卡中,命令如下:
chmod 777 imxdownload //給予imxdownload 可執行權限,一次即可 ./imxdownload icm20608.bin /dev/sdd //燒寫到SD 卡中,不能燒寫到/dev/sda 或sda1 里面!燒寫成功以后將SD 卡插到開發板的SD 卡槽中,然后復位開發板。如果ICM20608 工作正常的話就會在LCD 上顯示獲取到的傳感器數據,如圖27.4.2.1 所示:
在圖27.4.2.1 中可以看到加速度計Z 軸在靜止狀態下是0.98g,這正是重力加速度。溫度傳感器測量到的溫度是31.39°C,這個是芯片內部的溫度,并不是室溫!芯片內部溫度一般要比室溫高。如果動一下開發板的話加速度計和陀螺儀的數據就會變化。
總結
- 上一篇: 阐述linux IPC(五岁以下儿童):
- 下一篇: RSA 算法图解+数学证明