ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

上一章所講的UART通信屬於異步通信,比如計算機發送數據給單片機,計算機只負責把數據通過TXD發送出來,接收數據是單片機自己的事情。而IIC和SPI都屬於同步通信,收發雙方需要一條時鐘線來控制收發雙方的通信節奏。


從應用上來講,UART通信多用於板間通信,比如單片機和計算機,這個設備和另外一個設備之間的通信。而IIC和SPI多用於板內通信,比如使用IIC進行單片機和EEPROM的通信,比如使用SPI進行單片機和FLASH之間的通信。

6.1 IIC總線協議

在硬件上,IIC總線是由時鐘總線SCL和數據總線SDA兩條線構成,其中SCL為時鐘線,SDA為數據線,如圖6-1所示。總線上可以同時連接多個器件,所有器件的SCL都連到一起,所有SDA都連到一起。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6-1 IIC總線時序圖

起始信號:IIC通信的起始信號的定義是SCL為高電平期間,SDA由高電平變化到低電平產生的一個下降沿,表示一次通信過程的開始,如圖6-2中的Start部分所示。

數據傳輸:IIC通信是高位在前,低位在後。IIC通信要求當SCL在低電平的時候,SDA允許變化,也就是說,發送方必須先保持SCL是低電平,才可以改變數據線SDA,輸出要發送的當前數據的一個位;而當SCL在高電平的時候,SDA絕對不可以變化,因為這個時候,接收方要來讀取當前SDA的電平信號是0還是1,因此要保證SDA的穩定,如圖6-1中的每一位數據的變化,都是在SCL的低電平位置。8位數據位後邊跟著的是一位應答位,應答位我們後邊還要具體介紹。

停止信號:IIC通信停止信號的定義是SCL為高電平期間,SDA由低電平向高電平變化產生一個上升沿,表示一次通信過程的結束,如圖6-1中的Stop部分所示。

6.2 IIC尋址模式

上一節介紹的是IIC每一位信號的時序流程,而IIC通信在字節級的傳輸中,也有固定的時序要求。IIC通信的起始信號(Start)後,首先要發送一個從機的地址,這個地址一共有7位,緊跟著的第8位是數據方向位(R/W),“0”表示接下來要發送數據(寫),“1”表示接下來是請求數據(讀)。

Kingst-32F1板子上的EEPROM器件型號是24C02,在24C02的數據手冊3.6節中可查到,24C02的7位地址中,其中高4位是固定的0b1010,而低3位的地址取決於具體電路的設計,由芯片上的A2、A1、A0這3個引腳的實際電平決定。IIC總線器件是開漏引腳,因此外部要添加上拉電阻,保證總線空閒時為高電平。來看一下24C02的電路圖,如圖6-2所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

從圖6-2可以看出來,A2、A1、A0都是接的GND,也就是說都是0,因此24C02的7位地址實際上是二進制的0b1010000,也就是0x50。

IIC通信分為標準模式100kbit/s、快速模式400kbit/s和高速模式3.4Mbit/s。因為所有的IIC器件都支持標準模式,但卻未必支持另外兩種速度,所以作為通用的IIC程序我們選擇100k這個速率來實現,也就是說實際程序產生的時序必須小於等於100k的時序參數,有特殊速度需求的器件再針對性寫高速通信程序。

IIC引腳屬於開漏並聯結構,並且STM32的GPIO端口引腳設置為開漏輸出時,可以直接從輸入數據寄存器獲取I/O電平狀態,因此將IIC引腳配置為開漏輸出模式。由於IIC總線空閒時默認為高,初始化時還需要設置引腳輸出高電平,不過設置引腳輸出高電平並不是在初始化之後,而應該放在初始化之前。這是因為STM32在上電覆位時I/O口為高阻狀態,復位結束後,GPIO端口引腳默認為浮空輸入,由於上拉電阻的存在,IIC引腳被拉高;當程序執行到IIC初始化時又被配置為開漏輸出模式,由於GPIO端口輸出數據寄存器初始值默認全為0,初始化後I/O口輸出低電平,如果初始化之後再設置引腳輸出高電平,勢必會在I/O口上產生一個低電平的毛刺。如果在IIC引腳初始化之前先設置輸出數據寄存器相應位為高,初始化IIC引腳後,I/O口會直接輸出高電平,避免毛刺信號。

以下是IIC總線的驅動程序:

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6.3 初識EEPROM

在實際的應用中,保存在單片機RAM中的數據,掉電後就丟失了,保存在單片機內部FLASH中的數據,又不能隨意改變。但是在某些場合,我們需要記錄下某些數據,而它們還需要時常改變或更新,並且掉電之後數據還不能丟失,比如我們的家用電錶度數,電視機裡邊的頻道記憶,一般都是使用EEPROM來保存數據,特點就是掉電後不丟失。Kingst-32F1板子上使用的這個器件是24C02,是一個容量大小是2Kbits,也就是256個字節的EEPROM。一般情況下,EEPROM擁有30萬到100萬次的擦除壽命,也就是它可以反覆寫入30-100萬次,而讀取次數是無限的。

24C02是一個基於IIC通信協議的器件,因此從現在開始,IIC和EEPROM就要合體了。但要分清楚,IIC是一個通信協議,它擁有嚴密的通信時序邏輯要求,而EEPROM是掉電後數據不丟失的一種存儲器件的統稱,24C02就屬於EEPROM,只不過24C02採樣了IIC協議的接口與單片機相連而已,二者並沒有必然的聯繫,EEPROM可以用其它接口,IIC也可以用在其它很多器件上。

6.4 EEPROM單字節讀寫操作時序

STM32F103系列單片機本身自帶硬件IIC模塊,可以類似USART通信那樣,通過配置實現數據的收發。本書對IIC協議的介紹針對的是絕大多數的應用場合,實際上IIC的配置過程比較複雜,比如要充分考慮衝突和仲裁等處理方式,但是那些處理方式在絕大多數場合用不到。STM32F103系列自帶的IIC協議模塊設計的過於複雜,對於實際應用來講實用性不強,因此實際應用IIC時,還是用IO口直接模擬協議。本書原則就是實際開發採用何種用法,就重點介紹何種用法,下面用實例來實現一下EEPROM讀寫的基本流程。

1、EEPROM寫數據流程

第一步,首先是IIC的起始信號,接著跟上首字節,也就是前邊講的IIC的器件地址,並且在讀寫方向上選擇“寫”操作。

第二步,發送數據的存儲地址。24C02一共256個字節的存儲空間,地址從0x00~0xFF,想把數據存儲在哪個位置此刻寫的就是哪個地址。

第三步,發送要存儲的數據,注意在寫數據的過程中,EEPROM每個字節會回應一個“應答位0”,來通知用戶寫EEPROM數據成功,如果沒有回應答位,說明寫入不成功。單字節寫時序如圖6-3所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-3 IIC Byte Write時序圖

2、EEPROM讀數據流程

第一步,首先是IIC的起始信號,接著跟上首字節,也就是前邊講的IIC的器件地址,並且在讀寫方向上選擇“寫”操作。這個地方可能有讀者會詫異,明明是讀數據為何方向也要選“寫”呢?剛才說過了,24C02一共有256個地址,選擇寫操作是為了把所要讀的數據的存儲地址先寫進去,告訴EEPROM將要讀取哪個地址的數據。這就如同打電話,先撥總機號碼(EEPROM器件地址),而後還要繼續撥分機號碼(數據地址),而撥分機號碼這個動作,主機仍然是發送方,方向依然是“寫”。

第二步,發送要讀取的數據的地址,注意是地址而非存在EEPROM中的數據,通知EEPROM要哪個分機的信息。

第三步,重新發送IIC起始信號和器件地址,並且在方向位選擇“讀”操作。

這三步當中,每一個字節實際上都是在“寫”,所以每一個字節EEPROM都會回應一個“應答位0”。

第四步,讀取從器件發回的數據,讀一個字節,如果還想繼續讀下一個字節,就發送一個“應答位ACK(0)”,如果不想讀了,告訴EEPROM,不想要數據了,別再發數據了,那就發送一個“非應答位NAK(1)”。

每讀一個字節,地址會自動加1,那如果想繼續往下讀,給EEPROM一個ACK(0)低電平,那再繼續給SCL完整的時序,EEPROM會繼續往外送數據。如果不想讀了,要告訴EEPROM不要數據了,直接發送一個NAK(1)高電平。24C02讀數據時序如圖6-4所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-4 IIC Read時序圖

利用EEPROM單字節讀寫功能設計了一個記錄開發板復位次數的小程序。由於EEPROM中的數據很容易被擦除或者改寫,為了保證記錄數據的準確性,需要對讀出的數據進行校驗。這裡向大家介紹一種簡單又實用的校驗方法:將復位次數保存在EEPROM地址0x00中,並對復位次數按位取反後保存在地址0x01中。開發板每次復位後先去讀取地址0x00和0x01中的數據,對其進行異或運算,如果運算結果為0xFF,表明數據正確,將地址0x00中的數據加1後,重新寫入到EEPROM中,並通過數碼管顯示讀出的數據;否則記錄的數據被改寫,從0開始重新記錄復位次數。由於板載數碼管僅能顯示兩位數,記錄的最大次數為99,超過99重新開始記錄。具體代碼如下:

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6.5 EEPROM多字節讀寫操作時序

讀取EEPROM很簡單,EEPROM根據時序直接把數據送出來,但是寫EEPROM卻沒有這麼簡單了。給EEPROM發送數據後,先保存在EEPROM的緩存,EEPROM必須要把緩存中的數據搬移到“非易失”的區域,才能達到掉電不丟失的效果。而往非易失區域寫需要一定的時間,每種器件不完全一樣,ATMEL公司的24C02的這個寫入時間最長不超過5ms。在往非易失區域寫的過程,EEPROM是不會再響應用戶訪問的,不僅接收不到用戶的數據,即使用I2C標準的尋址模式去尋址,EEPROM都不會應答,就如同這個總線上沒有這個器件一樣。數據寫入非易失區域完畢後,EEPROM再次恢復正常,可以正常讀寫了。

在向EEPROM連續寫入多個字節的數據時,如果每寫一個字節都要等待幾ms的話,整體上的寫入效率就太低了。因此EEPROM的廠商就想了一個辦法,把EEPROM分頁管理。24C01、24C02這兩個型號是8個字節一個頁,24C04、24C08、24C16是16個字節一頁。Kingst-32F1開發板上用的型號是24C02,一共是256個字節,8個字節一頁,那麼就一共有32頁。

分配好頁之後,如果在同一個頁內連續寫入幾個字節後,最後再發送停止位的時序。EEPROM檢測到這個停止位後,會一次性把這一頁的數據寫到非易失區域,就不需要寫一個字節檢測一次了,並且頁寫入的時間也不會超過5ms。如果寫入的數據跨頁了,寫完一頁之後要發送一個停止位,然後等待並且檢測EEPROM的空閒模式,一直等到把上一頁數據完全寫到非易失區域後,再進行下一頁的寫入,這樣就可以在很大程度上提高數據的寫入效率,頁寫時序如圖6-5所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-5 EEPROM頁寫時序

本節利用EEPROM多字節讀寫功能設計了一個通過串口發送指令控制EEPROM讀寫數據的例程。該例程只需要下載一次程序,就能通過串口調試助手實現對EEPROM任意地址的讀寫操作,避免重複編程控制EEPROM讀寫數據的方式,具有很高是實用意義。與第五章串口實用例程類似,通過串口調試助手發送控制指令,單片機檢測到指令後做出相應動作。

EEPROM讀數據指令格式:“e2read 地址 字節長度”,其中地址範圍為0~255,e2read、地址、字節長度之間由空格隔開,比如從地址1開始讀取5字節數據:e2read 1 5。單片機收到指令後執行多字節讀操作,通過串口助手返回讀出的數據。

EEPROM寫數據指令格式:“e2write 地址 數據”,地址範圍為0~255,e2write、地址、數據之間同樣由空格隔開,比如從地址1開始寫入hello:e2write 1 hello。單片機收到指令後執行多字節寫操作,寫入成功後通過串口助手返回“e2write done.”

如果發送指令格式錯誤,返回“bad parameter.”,如果發送指令錯誤,將返回發送的指令。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6.6 使用邏輯分析儀測試UART和IIC信號

在實際開發過程中,大多數情況下程序代碼並不是一寫出來就可以正常運行,通常需要查找問題,調試功能。在單片機內部運行的變量和寄存器等參數可以通過在線仿真的方法查看,而單片機外部引腳的運行就要使用示波器或邏輯分析儀來查看了。在分析數字信號以及標準協議信號方面,邏輯分析儀的分析功能比示波器更優一些,下面就採用Kingst LA5016邏輯分析儀,針對6.5節的程序,將UART和I2C信號進行抓取分析。

UART數據抓取和解析解碼的界面如圖6-6所示,從圖上可以看出,單個脈衝T1到T2之間的時間差是104us,時間差的倒數就是波特率,也就是9600。當沒有信號的時候,通道處於高電平,產生第1個低電平就是起始位,而後是8位數據位,最後是停止位,其中數據位的位置軟件都加了白點。最終,軟件根據協議設置選項將數據解析出來是0x31,0x32......。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-6 Kingst LA5016邏輯分析儀解析UART數據

當串口助手發送讀數據指令,讀到的數據不正確的時候,首先我們要判斷一下是單片機發送錯了,還是電腦接收錯了,這個時候可以通過分析儀測量一下UART串口通信波形以及解析的數據來判斷。如果分析儀抓到的都正確,那就說明發送正確,接收方出現問題。

如果分析儀抓取分析的數據,不是單片機想發送的數據,那說明發送錯了。既然發送錯了,要麼是程序問題,要麼是從EEPROM讀取的數據錯了。這個時候需要抓一下從EEPROM讀到的數據。採集分析完從EEPROM讀到的數據,如果數據正確,那問題可能就出在了讀數據後轉成UART發送給電腦的程序過程中;如果數據是錯誤的,那要麼是存入EEPROM本來就是錯誤的,要麼是存入正確,讀取EEPROM的程序是錯誤的,如圖6-7所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-7 Kingst LA5016邏輯分析儀解析IIC數據

在圖6-7當中,其中通道0是SCL,通道1是SDA。圖中可以看出讀寫時序圖,並且可以將讀寫的數據解析出來,用來判斷出從EEPROM讀取的數據是否有錯。

6.7 初識SPI總線協議

SPI(Serial Peripheral Interface)是一種高速的、全雙工、同步通信總線,常用於單片機和EEPROM、FLASH、實時時鐘、數字信號處理器等器件的通信。由於其簡單實用,數據傳輸速率較高,被廣泛應用於外設控制領域。標準的SPI接口一般使用4條線:串行時鐘線(SCK)、主機輸入/從機輸出數據線(MISO)、主機輸出/從機輸入數據線(MOSI)和從機選擇線(CS,也被稱作SSEL或NSS)。SPI總線允許一對一或一對多通信,無論哪種方式,通信線路中只允許有一個主機。一對多通信要求從機共用SCK、MISO、MOSI,通過片選CS(NSS)選擇使能從機設備。從微觀角度上看主機一次只能與一個從機通信,比如主機先與從機1通信,然後再與從機2通信,如此循環,在宏觀角度上看就相當於一個主機與兩個從機實現通信,如圖6-8所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-8 SPI接口連接示意圖

6.7.1 SPI通信原理

SPI總線與前面講的IIC總線類似,都屬於同步通信,即通信雙方時必須使用相同的時鐘信號;都屬於一主多從結構,即總線上只有一個主機,可以掛載多個從機。不同之處在於,IIC屬於單工通信,同一時刻只能收或者發,而SPI可以全雙工通信,同一時刻既能收又能發,因此SPI的通信速率遠遠超過IIC,可以達到幾十Mbps。此外IIC通信可以由主從設備的任意一方發起,而SPI通信只能由主機發起,從機不能主動與主機通信,因為從機不產生同步時鐘信號。

SPI通信本質上是一個串行移位過程。SPI主從設備構成一個環形總線結構,SPI通信的主機一般是單片機,首先主機拉低CS(NSS)信號使能片選,告訴從機開始通信,然後主機開始輸出同步時鐘信號SCK,主機的移位寄存器通過MOSI移出1位數據,從機的移位寄存器通過該線移入這1位數據;同時從機的移位寄存器又通過MISO線移出1位數據,主機的移位寄存器通過該線移入這1位數據,因此在1個時鐘週期內,主從設備的雙向移位寄存器通過MOSI和MISO數據線實現了1 bit數據的交換,即雙方都發出並接收到1 bit數據。

6.7.2 SPI通信模式

I2C總線只有一種通信模式,即當SCL在低電平的時候,向SDA輸出數據,當SCL在高電平的時候,對SDA上的數據進行採樣。與I2C不同的是,SPI總線有四種通信模式,要了解這四種模式,首先我們得學習以下兩個名詞。

CPOL: Clock Polarity,就是時鐘的極性。時鐘的極性是什麼概念呢?通信的整個過程分為空閒時刻和通信時刻,如果CPOL=1,那麼SCLK在數據發送之前和之後的空閒狀態是高電平,如果CPOL=0,那麼SCK煩人空閒狀態就是低電平。

CPHA: Clock Phase,意思是時鐘的相位,就是指數據採樣發生在第幾個時鐘邊沿, CPHA =0對應著第一個時鐘邊沿,CPHA =1對應著第二個時鐘邊沿。

主機和從機要交換數據,就牽涉到一個問題,即主機在什麼時刻輸出數據到MOSI上而從機在什麼時刻採樣這個數據,或者從機在什麼時刻輸出數據到MISO上而主機什麼時刻採樣這個數據。同步通信的一個特點就是所有數據的變化和採樣都是伴隨著時鐘沿進行的,也就是說數據總是在時鐘的邊沿附近輸出或被採樣。而一個時鐘週期必定包含了一個上升沿和一個下降沿,這是週期的定義所決定的,只是這兩個沿的先後並無規定。又因為數據從產生的時刻到其穩定是需要一定時間的,那麼,如果主機在上升沿輸出數據到MOSI上,從機就只能在下降沿去採樣這個數據了。反之如果一方在下降沿輸出數據,那麼另一方就必須在上升沿採樣這個數據。

CPHA=0,就表示數據的採樣是在一個時鐘週期的第一個沿上,至於這個沿是上升沿還是下降沿,這要視CPOL的值而定,CPOL=1那就是下降沿,反之就是上升沿。那麼數據的採樣自然就是在第二個沿上了。

CPHA=1,就表示數據的採樣是在一個時鐘週期的第二個沿上,同樣它是什麼沿由CPOL決定。CPOL=1那就是上升沿,反之就是下降沿。

以CPOL=1/CPHA=1,高位在前為例,把時序圖畫出來給大家看一下,如圖6-9所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-9 SPI通信時序圖(一)

如圖6-10所示,當數據未發送時以及發送完畢後,由於CPOL=1,因此SCK都是高電平。可以看出,在SCK第一個沿的時候,MOSI和MISO會發生變化,同時SCK第二個沿的時候,數據是穩定的,此刻採樣數據是合適的,即一個時鐘週期的後沿鎖存並讀取數據,即CPHA=1。注意最後最隱蔽的SSEL片選,這個引腳通常用來決定主機是和哪個從機進行通信。剩餘的三種模式,直接把圖畫出來,簡化起見把MOSI和MISO合在一起了,可以通過仔細對比加深對SPI通信的理解,SPI剩餘三種模式時序如圖6-8所示。

需要解釋一下CPHA=0時的兩種模式時序圖,圖中數據採樣發生在數據輸出之前,可能會存在疑問,主機和從機還未輸出數據就進行採樣?圖中所示的數據輸出指的是輸出第二位數據,即圖中所示bit 6 位的數據。那麼被採樣的數據位是什麼時候輸出的?其實早在SCK信號由空閒狀態轉變為有效狀態之前,即在SCK的第一個時鐘邊沿還要早半個時鐘週期時,SPI主機就已經開始輸出數據了,但是SPI從機卻是在片選SSEL置低後開始輸出數據,總之SPI設備輸出數據要早於SCK,這也是為什麼SPI通信時要先使能片選。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-10 SPI通信時序圖(二)

6.8 配置STM32的SPI外設

STM32單片機的SPI接口允許芯片與外部設備以半/全雙工、同步、串行方式通信。此接口可以被配置成主模式或從模式,當被配置為主模式時,可以為外部從設備提供通信時鐘信號(SCK)。SPI接口擁有8個主模式波特率預分頻係數(最大為fPCLK/2,fPCLK為掛載SPI外設的APBx的時鐘頻率)。需要說明的一點,由於APB1的最高頻率是36Mhz,APB2的最高頻率是72Mhz,SPI波特率預分頻係數為2時,位於APB2上的SPI外設理論上最大速率是36MHz,但是實際上由於STM32F103的硬件限制,SPI的最大速率只能達到18Mhz(選型手冊也有介紹)。此外,SPI接口還支持硬件的CRC校驗,保證通信的可靠性。

6.8.1 SPI引腳

在配置STM32的SPI模塊為主機模式時,MOSI引腳是數據輸出,而MISO引腳是數據輸入。當被配置為從機模式時 MOSI引腳則是數據輸入,MISO引腳是數據輸出。因此通信時主從設備的MOSI兩兩相連,MISO兩兩相連,此外還需要連接SCK和CS(NSS)引腳。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

注意:SPI3模塊部分引腳與 JTAG引腳共用,這些引腳不受IO控制器控制,它們(復位後)被默認保留為JTAG用途。如果想把引腳配置給SPI3,必須在初始化引腳時關閉JTAG並切換SWD接口,代碼為:

GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

// JTAG-DP 失能 + SW-DP 使能指令。

NSS:從設備選擇。作為“片選引腳”,這是一個可選的引腳,使得主設備可以單獨地與特定從設備通訊,避免數據線上的衝突。從設備的NSS引腳可以由主設備的一個標準I/O引腳來驅動,不一定非要SPI外設的NSS引腳,採用普通IO口作為NSS引腳時需要設置NSS引腳模式為軟件NSS模式。Kingst-32F1開發板SPI2接口與多路設備通信,其從機的NSS引腳就是STM32的幾個普通I/O口,後續用到再詳細介紹。

6.8.2 SPI初始化配置

首先看一下SPI外設的初始化結構體成員及其描述。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

1、SPI_Dirention 設置了SPI是單向通信還是雙向的通信數據模式,設置參數如表6-3所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

2、SPI_Mode設置了SPI 設備是工作在主機模式還是從機模式,設置參數如表6-4所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

3、SPI_DataSize設置了SPI通信時數據幀是8位還是16位,沒有特殊要求,通常配置為8位模式,設置參數如表6-5所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

4、SPI_CPOL設置了SPI串行時鐘SCK的時鐘極性,選擇該參數時需要確保SPI通信雙方保持一致,一般根據SPI從設備支持的模式設定,設置參數如表6-6所示

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

5、SPI_CPHA設置了SPI採樣時發生在SCK的哪個邊沿,該參數同樣需要確保通信雙方保持一致,一般也是根據從設備支持的模式設定,設置參數如表6-7所示

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6、SPI_NSS設置了NSS片選引腳是由選擇硬件模式,還是由軟件模式。採用硬件管理時,片選信號由SPI硬件自動產生,不需要手動設置,而軟件模式需要手動設置片選引腳電平。實際開發中通常設置為軟件模式,即使用普通IO口作為片選引腳。設置參數如表6-8所示

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

7、SPI_BaudRatePrescaler設置了SPI波特率預分頻係數,分頻後的時鐘就是SCK信號線的時鐘頻率。注意SPI正在通信時不能更改波特率預分頻係數,具體設置參數如表6-9所示

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

8、SPI_FirstBit設置了數據傳輸是高位在前(MSB先行)還是低位在前(LSB先行),沒有特殊要求一般設置為高位在前(MSB先行)設置參數如表6-10所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

9、SPI_CRCPolynomial設置是否使用CRC校驗。使用時根據需求設置CRC多項式,不使用時設置其值為7(復位值為0x0007,默認不使用)。無特殊要求通常不設置CRC校驗。

SPI配置流程:

1、使能SPI外設時鐘及SPI接口引腳時鐘

2、初始化SPI引腳——初始化NSS、SCK、MISO、MOSI引腳。

3、初始化SPI外設——根據需求設置SPI初始化結構體成員參數

4、使能SPI外設——SPI使能庫函數為:SPI_Cmd(SPIx, ENABLE);其中SPIx,x為1、

2、3,用來選擇SPI外設。

5、編寫SPI數據發送和接收函數

6.8.3 SPI數據發送和接收

SPI數據發送和接收過程與USART類似,由數據緩衝區和一個8位的雙向移位寄存器構成。SPI的數據緩衝區叫做數據寄存器(SPI_DR),雖然是一個寄存器,但是實質上包含兩個緩衝區:發送緩衝和接收緩衝,分別用於進行寫操作和讀操作。與USART兩個單獨的移位寄存器不同,SPI只有一個移位寄存器且是雙向的,同一時刻既向MOSI上移出要發送的數據,又將MISO上的數據向內移入,這個過程是同步的,SPI傳輸結構如圖6-11所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-11 SPI數據傳輸結構圖

SPI發送數據時只需要將數據寫入到SPI_DR,SPI會自動將其分配到發送緩衝區,然後再將數據從發送緩衝區並行傳送到移位寄存器中,同時設置一個發送緩衝區為空(TXE)的標誌位,最後數據按照設定的數據格式(MSB或LSB)被串行的從MOSI引腳移出。與此同時MISO引腳也會接收到數據,接收到的數據同樣按照相應的格式被串行的移入到移位寄存器,當接收完一幀數據後,移位寄存器將接收到的數據傳送到接收緩衝區中,同時會設置一個接收緩衝區非空(RXNE)的標誌位。使用SPI發送和接收數據時並不需要關心數據是怎麼發送或者接收到的,只需要檢測相應標誌位後向數據寄存器(SPI_DR)寫入要發送的數據或者讀出接收到的數據即可。

TXE標誌位被置位僅表示發送緩衝區為空,可以繼續向SPI_DR寫入數據,但並不代表數據發送完成,這一點一定要搞清楚。向發送緩衝區寫入數據會清除TXE標誌位,如果TXE=0即發生緩衝區非空時,向SPI_DR中寫入數據會覆蓋發送緩衝區中的數據,但不會影響移位寄存器中的數據。RXNE=1表示接收緩衝區非空,即已經接收到一幀數據。讀SPI_DR寄存器硬件會自動清除RXNE標誌位,並返回接收到的數據。當SPI接收到一幀數據時,意味著SPI肯定已經發送完一幀數據,因此判斷一幀數據是否發送完成,可以通過檢測RXNE標誌位。如果設置了SPI_CR1寄存器中的TXEIE位或者SPI_CR2寄存器中的RXNEIE位,將產生對應的中斷。本文主要是以查詢方式介紹SPI如何收發數據。

使用查詢方式檢測TXE和RXNE標誌位時需要用到SPI標誌位檢測函數,如下所示:

SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);其中SPIx中x為1、2、3,用來選擇SPI外設;SPI_I2S_FLAG為被檢測的標誌位,如表6-11所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

同時庫函數中也有對應的SPI數據發送和接收函數,分別為:(固件庫手冊中所示的發送和接收函數有誤,實際以本文為準)

SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);//SPI發送函數

SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);//SPI接收函數

SPI使用查詢方式發送和接收數據時,在發送和接收數據之前需要檢測相應標誌位,然後再調用庫函數發送或者接收數據,這樣操作雖然方便,但是由於函數相互調用會佔用時間,當傳輸數據量較大時會降低SPI整體傳輸效率。為了提高SPI的整體傳輸效率,本文采用寄存器方式操作。另外SPI在接收從機數據時,由於從機自身不產生時鐘信號(SCK),通信時需要主機提供SCK以啟動數據傳輸,因此主機需要通過發送的數據來提供SCK並接收從機的數據,發送的數據並不生效。

以下是配置SPI2為主模式,8位數據格式,CPOL=1,CPHA=1,波特率2分頻,MSB先行,不使用CRC校驗,使用時可根據具體要求進行修改。由於SPI外設掛載了多個從機設備,為了方便移植,初始化SPI時並未設置片選引腳,而是選擇將片選引腳與SPI從設備放在一起配置,具體代碼如下:

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6.9 SPI讀寫Flash存儲芯片——W25Q128

6.9.1 W25Q128簡介

Flash是一種掉電不丟數據的非易失存儲設備,生活中常見的閃存存儲設備包塊固態硬盤、U盤、SD卡以及單片機內部的存儲設備等。Flash具有存儲空間更大,讀取速率快、可靠性高等特點。本節所要介紹的W25Q128就是Flash的一種,只不過它是以芯片的形式存在。 W25Q128是華邦公司推出的一款SPI接口的NOR Flash芯片,其存儲空間為128Mbit,相當於16M字節。W25Q128可以支持SPI的模式0和模式3,也就是CPOL=0/CPHA=0和CPOL=1/CPHA=1這兩種模式。

往Flash寫入數據時,需要注意以下兩個重要問題:

1、Flash寫入數據時和EEPROM類似,不能跨頁寫入,一次最多寫入一頁,W25Q128的一頁是256字節。寫入數據一旦跨頁,必須在寫滿上一頁的時候,等待Flash將數據從緩存搬移到非易失區,重新再次往裡寫。

2、Flash有一個特點,就是可以將1寫成0,但是不能將0寫成1,要想將0寫成1,必須進行擦除操作。因此通常要改寫某部分空間的數據,必須首先進行一定物理存儲空間擦除,最小的擦除空間,通常稱之為扇區,扇區擦除就是將這整個扇區每個字節全部變成0xFF。每款Flash的扇區大小不一定相同,W25Q128的一個扇區是4096字節。為了提高擦除效率,使用不同的擦除指令還可以一次性進行32K(8個扇區)、64K(16個扇區)以及整片擦除。

Kingst-32F1開發板中W25Q128對應的管腳連接關係如圖6-10所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6.9.2 W25Q128讀寫操作

W25Q128內部有一個“SPI Command & Control Logic”,可以通過SPI接口向其發送指令,從而執行相應操作。指令的長度是不定的,有單字節的,也有多字節的,W25Qxx一共具有34個操作指令,在此只列舉常用的12個,具體如表6-13所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

下面將對W25Q128的常用操作方式進行介紹。

1、只發送指令——寫使能、寫失能

寫使能是指狀態寄存器中的WEL位置1(發送寫使能指令後硬件自動設置),失能就是清零操作。在發送頁寫,扇區擦除,塊擦除,片擦除,寫狀態寄存器,擦寫安全寄存器指令之前,必須先等待WEL 位置1 ,這些操作完成後,WEL會自動硬件清零。寫失能是將狀態寄存器中的WEL軟件清零,寫失能這個操作很少用到,大多數情況都是硬件自動完成。

根據SPI的操作時序,將指令(06h或04h)通過DI(MOSI)發送給W25Q128,期間DO(MISO)處於高阻狀態,時序圖如圖6-11和圖6-12所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-13 寫使能時序

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-14 寫失能時序

2、發送指令,返回數據——讀狀態寄存器

讀狀態寄存器指令允許讀8位狀態寄存器位。讀狀態寄存指令在任何時間都可使用,可以用05H/35H/15H分別讀取W25Q128的三個狀態寄存器。實際上用的最多的只有05H讀取狀態寄存器1的第0位(BUSY位),用來查看相應指令週期是否結束,芯片是否可以接收新的指令。

根據SPI的操作時序,將指令05H通過DI發送,而後通過DO讀回狀態寄存器的值,時序圖如圖6-15所示。注意通常我們只讀一個字節,時序圖後續字節為芯片備用擴展的。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-15 讀狀態寄存器時序圖

3、發送指令+數據——寫狀態寄存器

寫狀態寄存器的作用主要實現對Flash某些區域的數據或者狀態保護,一旦寫入保護狀態,在狀態解除之前,這塊區域是禁止寫入和擦除的。這部分在本教程中沒有應用,有興趣的讀者可以根據時序圖和手冊資料瞭解一下,時序圖如圖6-16所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-16 寫狀態寄存器時序圖

4、讀數據

讀數據指令允許從存儲器讀一個字節和連續多個字節。

首先要確認BUSY位為0,然後根據SPI的操作時序,首先寫入指令代碼03H,而後緊跟3個字節的地址。當W25Q128收到地址後,會將相應地址處的數據根據SPI時序輸出來;如果連續讀多個字節,那每經過8個時鐘週期地址自動加1,並且輸出相應數據,一直到CS拉高,時序圖如圖6-17所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-17 讀數據時序圖

5、寫入數據(頁編程)

W25Q128一次寫入的數據只能小於等於256字節,並且不能一次性跨頁寫入。當遇到跨頁時,應先寫滿一頁,等待BUSY位為0,再次往下一頁寫。(如果寫滿一頁繼續往下寫則會跳到緩存區的頁首位置開始寫,之前數據會被覆蓋)

在寫入數據之前,該頁必須被擦除過,然後根據寫入地址和寫入的字節數計算是否跨頁以及頁數。根據SPI的操作時序,首先通過寫使能將WEL置1,然後寫入指令代碼02H,而後緊跟著3個字節的地址,而後接著發送要存儲的數據,時序圖如圖6-18所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-18 頁編程時序圖

寫入數據完畢後,W25Q128將數據從緩存搬移到非易失區所消耗的時間要了解一下,在手冊的84頁有介紹,大概是(30+(x-1)*2.5)us ~ (50+(x-1)*12) us之間(x為寫入的字節),搬移完成後,WEL位會自動清零,BUSY自動清零。

6、扇區擦除、塊擦除

可以使用20H、52H和D8H分別對扇區擦除、32K塊擦除和64K塊擦除。根據SPI的操作時序,在寫入擦除指令之前首先通過寫使能將WEL置1,然後寫入指令代碼20H/52H/D8H中的一個,而後緊跟著3個字節的擦除首地址。

地址發送完畢後,必須將CS拉高,擦除指令才開始執行,並且需要一定的擦除時間,在這個時間內,只能讀狀態寄存器,其他操作均不能進行。扇區、32K塊和64K的擦除典型時間分別是100ms、120ms和150ms,最大時間分別是400ms、1.6s、2s。當擦除完成,WEL和BUSY位自動清零,就可以再次接收新的操作指令,時序圖如圖6-19所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-19 扇區和塊擦除時序圖

7、全片擦除

可以使用C7H/60H指令對整片進行擦除操作。根據SPI的操作時序,在寫入擦除指令之前首先通過寫使能將WEL置1,然後寫入指令代碼C7H或者60H,發送完畢拉高CS後,擦除指令開始執行。

整片擦除過程中,只能讀狀態寄存器,其他操作均不能進行。整片擦除的典型時間是40秒,最大時間是200秒。當擦除完成後,WEL和BUSY位自動清零,就可以再次接收新的操作指令,時序圖如圖6-20所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-20 全片擦除時序圖

接下來根據W25Q128的時序編寫驅動程序,該驅動文件可以驅動W25Qxx系列的Flash存儲芯片,不同型號的ID不同,W25Qxx初始化時會通過判斷芯片ID來識別是否通信成功。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6.9.3 串口控制Flash讀寫實驗

由於Flash與EEPROM實現的功能類似,本節通過改寫串口發送指令控制EEPROM讀寫數據的例程,設計了串口發送指令控制Flash讀寫。幫助大家更好的體會串口實用例程以及Flash讀寫流程。

Flash讀數據指令格式:“f-read 地址 字節長度”,其中地址範圍為0~16*1024*1024,e2read、地址、字節長度之間由空格隔開,比如從地址1開始讀取5字節數據:f-read 1 5。單片機收到指令後執行多字節讀操作,通過串口助手返回讀出的數據。

Flash寫數據指令格式:“f-write 地址 數據”,地址範圍為0~16*1024*1024,f-write、地址、數據之間同樣由空格隔開,比如從地址1開始寫入hello:f-write 1 hello。單片機收到指令後執行多字節寫操作,寫入成功後通過串口助手返回“f-write done.”

如果發送指令格式錯誤,返回“bad parameter.”,如果發送指令錯誤,將返回發送的數據。由於程序中設定的串口接收和發送緩衝區最大為256字節,因此該實驗單次讀取或者寫入的字節數應小於256字節。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

6.10 使用邏輯分析儀測試SPI信號

當進行SPI通信出現異常時,可以通過邏輯分析儀進行通信時序上的問題查找,如圖6-18所示。從圖上可以看出SCK空閒時是高電平,即CPOL=1;從CLK的跳沿箭頭上可以看出,是後沿讀取數據,即CPHA=1。數據分析可以通過MISO和MOSI解析後的數據判斷出。通過分析儀的數據解析功能,可以直觀看到STM32與Flash之間的通信數據,從而進一步確定問題所在。

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

圖6-21 Kingst LA5016邏輯分析儀解析SPI數據

ARM嵌入式編程與實戰應用(STM32F1系列)第6章 IIC總線和SPI總線

長按識別關注

知識共享|助力夢想


分享到:


相關文章: