串行外設接口(Serial Peripheral Interface,縮寫為 SPI) 提供了基于SPI 協議的數據發送和接收功能, 可以工作于主機或從機模式。 SPI 接口支持具有硬件 CRC 計算和校驗的全雙工和單工模式。
8.1.SPI 基礎知識
SPI 物理層
SPI接口采用主從模式(Master Slave)架構;支持一主一從模式和一主多從模式,但不支持多主模式。它是一種同步高速全雙工的通信總線,總體結構如下圖常見的SPI通訊系統所示。

一個主機連接四個從機,其中一個SPI總線一般有四個信號分為:
SCLK:時鐘信號,由主機產生并控制。
MOSI:主機數據輸出,從機數據輸入。
MISO:主機數據輸入,從機數據輸出。
SS/NSS:從機片選使能信號,由主機控制。在一主對多從的模式下,每一個從機都需要獨占一個SS,也就是說有多少個從機就有多少個片選信號。
SPI 協議層
SPI的協議定義了通信的起始信號、停止信號、數據有效性、時鐘同步等環節。下面我們分析一下兩個設備通過SPI總線通信的過程,SPI通信時序圖如下圖通訊時序所示:
SPI 通訊時序

這個是一個主機的通信時序,信號線 NSS、SCK、MOSI 都是由主機控制,MISO 是由從機進行控制。其中 MOSI 和 MISO 上的數據僅在 NSS 為低時才有效,并且每個SCK 時鐘周期只交換一位數據。
起始信號和停止信號:如_SPI通訊時序圖_中①和⑥分別表示通信的起始和結束,這些信號的產生是通過主機將片選信號(NSS)置低和置高實現的。NSS 除了是開始和結束信號的產生者,它也是主機和從機通信的選擇者,當一個主機對多個從機通信時,主機通過置低從機的 NSS 信號線來選擇與哪個從機進行通信。
時鐘同步:SPI 總線是一個同步全雙工的通信總線,所以 SPI 的數據傳輸是需要 SCK 時鐘信號嚴格同步的,每一個 SCK 周期只傳輸一位數據,這一個周期里要完成數據的準備和采樣,且數據的輸入和輸出是同時進行的。MSB 先行或 LSB先行協議中是沒有硬性規定,只需通信雙方保持統一即可。SPI 每次數據傳輸可以是 8 位或 16 位為單位,每次傳輸的單位數不受限制。
數據有效性:SPI 在 SCK 時鐘的同步下進行數據的準備和采樣,過程如圖 1-2 的②③④⑤所示。在 NSS 為低的情況時,在 SCK 的上升沿時 MISO 和 MOSI 進行數據準備,SCK 的下降沿時讀取 MISO 和 MOSI 上的數據。在 NSS 為高時,MISO 和MOSI 上的數據無效。
SPI 工作模式:如_SPI通訊時序圖_只是 SPI 的一種工作模式,SPI 一共有四種工作模式。他們的區別是總線空閑時 SCK 的電平狀態和數據采樣時刻。這四種模式的配置是通過配置“時鐘極 性 CKPL”和“時鐘相位 CKPH”的電平來實現的。
CKPL=0 時,SCK 引腳在空閑狀態保持低電平;
CKPL=1 時,SCK 引腳在空閑狀態保持高電平;
CKPH=0 時,SCK 時鐘的第一個邊沿進行采樣;
CKPH =1 時,SCK 時鐘的第二個邊沿進行采樣。
四種模式如下圖SPI通訊模式所示:

SPI 數據傳輸流程
前面我們了解了SPI協議的物理連接方式和SPI的具體協議和SPI的四種工作模式,下面我們從整體上分析一下SPI通信的流程。通過前面的內容可知,在一個SCK周期內,SPI會完成如下操作:
主機通過MOSI線發送1位數據,從機通過該線讀取這1位數據。
從機通過MISO線發送1位數據,主機通過該線讀取這1位數據。
這是通過移位寄存器來實現的。如下圖所示,主機和從機各有一個移位寄存器,且二者連接成環。隨著時鐘脈沖,數據按照從高位到低位的方式依次移出主機寄存器和從機寄存器,并且依次移入從機寄存器和主機寄存器。當寄存器中的內容全部移出時,相當于完成了兩個寄存器內容的交換。通信流程如圖所示:

8.2.GD32 SPI 外設原理簡介
因篇幅有限,本文無法詳細介紹GD32所有系列SPI外設接口,下面以GD32F30x為列,著重介紹下GD32F30x的SPI外設簡介和結構框圖,后介紹下各個系列的差異。
GD32 SPI 主要特性
? 具有全雙工和單工模式的主從操作;
? 16位寬度,獨立的發送和接收緩沖區;
? 8位或16位數據幀格式;
? 低位在前或高位在前的數據位順序;
? 軟件和硬件NSS管理;
? 硬件CRC計算、發送和校驗;
? 發送和接收支持DMA模式;
? 支持SPI TI模式;
? 支持SPI NSS脈沖模式;
? 支持SPI四線功能的主機模式(僅在SPI0中)。
GD32的SPI外設還支持I2S功能,I2S功能是一種音頻串行通訊協議,如果需要學習請參考各個系列的User_Manual,本文不做過多的介紹。
SPI 結構框圖介紹
SPI 通訊模式

通訊引腳:如_SPI通訊模式圖_的①所示,GD32硬件接口SCK、NSS、MOSI、MISO為標準的SPI協議的四條信號線;IO2、IO3為GD32的SPI四線模式使用到的引腳,分別為:發送或接收數據2線和3線(在GD32F30x中僅SPI0支持四線主機模式)。各個系列的SPI個數不同,SPI接口和芯片I/O口的對應關系,可查閱各個系列的Datasheet。
時鐘生成器:如_SPI通訊模式圖_的②所示,SCK線的時鐘信號,是由波特率發生器根據“控制寄存器0(SPI_CTL0)”中的PSC[2:0]位控制的。具體分頻選擇如下
000:PCLK/2 100:PCLK/32
001:PCLK/4 101:PCLK/64
010:PCLK/8 110:PCLK/128
011:PCLK/16 111:PCLK/256
當使用SPI0時,PCLK=PCLK2,當使用SPI1和SPI2時,PCLK=PCLK1。
數據通訊單元:如_SPI通訊模式圖_的③所示,SPI的MOSI及MISO都連接到數據移位寄存器上,數據移位寄存器的數據來源及目標接收、發送緩沖區以及MISO、MOSI線。當向外發送數據的時候,數據移位寄存器以“發送緩沖區”為數據源,把數據一位一位地通過數據線發送出去;當從外部接收數據的時候,數據移位寄存器把數據線采樣到的數據一位一位地存儲到“接收緩沖區”中。
通過寫SPI的“數據寄存器(SPI_DATA)”把數據填充到發送緩沖區中,通訊讀“數據寄存器(SPI_DATA)”,可以獲取接收緩沖區中的內容。其中數據幀長度可以通過“控制寄存器0(SPI_CTL0)”的“FF16位”配置成8位及16位模式;配置“LF位”可選擇MSB先行還是LSB先行。
下面以SPI作為主機MSB先行收發數據來分析一下通訊流程:
控制NSS信號線進入低電平,選中從器件發出通信開始信號;
檢查“發送緩沖區”是否為空(SPI_STAT的TBE是否為1),如果為空,將所需要發送的數據寫入“發送緩沖區”;
“發送緩沖區”里的數據一次性寫入“移位寄存器”,一旦“發送緩沖區”里的數據寫入“移位寄存器”SPI通信正式開始;
“移位寄存器”通過MOSI信號線從高位一位一位的發送到接收方,由于SPI的通信時全雙工的,所以MOSI每發出一位MISO就接收一位存入移位寄存器;
直到一個數據單元發完(數據單元大小8位/16位可配置)。“移位寄存器”里接收回來的數據將一次性寫入“接收緩沖區”,這時SPI_STAT的RBNE位將置1。也就是說“接收緩沖區”已有數 據。這時就可以讀取數據了。
如果要發多組數據或者收多組數據,只需重復第2,3,4,5步。注意如果只收不發時,只需發送0xFF即可;
當所有數據都通信完成控制NSS信號進入高電平,通信正式結束。
各系列 SPI 功能差異
GD32系列MCU有關SPI外設各系列功能差異如下表所示

8.3.硬件連接說明
SPI 串行 Flash 硬件連接圖

如圖所示,為典型的SPI外設硬件連接圖:GD25Q40是一種使用 SPI通訊協議的NOR FLASH存儲器,它的CS/SCLK/SI/SO引腳分別連接到了GD32對應的SPI引腳NSS/SCK/MOSI/MISO上,其中GD32的NSS引腳是一個普通的GPIO,不是SPI的專用NSS引腳,所以程序中我們要使用軟件控制的方式。若硬件設計中為SPI_NSS可以程序里可以配置為硬件控制方式。
讀者可以根據典型硬件連接圖和相應系列的Datasheet設計出自己的硬件連接方式。
8.4.軟件配置說明
本小節講解SPI_Example歷程中SPI模塊的配置說明,主要包括外設時鐘配置、GPIO引腳配置、SPI外設配置、主函數介紹以及運行結果。本例程主要介紹GD32 MCU各系列SPI0模塊的數據發送,有關SPI其他功能例程可參考各系列固件庫例程。
外設時鐘配置
外設時鐘配置如代碼清單例程時鐘配置所示,在GD32全系列MCU中需打開GPIOA和SPI0的時鐘,由于使用到PA3/PA5/PA7引腳以及SPI0模塊,另外,在GD32F10X、GD32F20X、GD32F30X、GD32E10X中需要打開AF時鐘。
void rcu_config(void) { #if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X || GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_SPI0); #if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X rcu_periph_clock_enable(RCU_AF); #elif defined GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X #endif #endif }
GPIO 引腳配置
GPIO引腳配置如代碼清單SPI例程GPIO引腳配置所示,GD32F10X、GD32F30X、GD32F20X、GD32E10X系列GPIO配置相同,PA5、PA7需配置為復用推挽輸出、PA6需配置為浮空輸入、PA3作為片選控制引腳配置為推挽輸出模式;GD32F1X0、GD32F4XX、GD32F3X0、 GD32E23X系列GPIO配置基本相同,不同在于PA5/PA6/PA7引腳的AF復用功能配置不同,在GD32F1X0、GD32F3X0和GD32E23X上,需要配置為AF0模式,在GD32F4XX上需要配置為AF5模式。GPIO配置完成后,例程中將CS片選信號拉高。
void gpio_config(void) { #if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X /* SPI0 GPIO config:SCK/PA5, MISO/PA6, MOSI/PA7 */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7); gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6); /* PA3 as NSS */ gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3); #elif defined GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X #if defined GD32F1X0 || GD32F3X0 || GD32E23X /* SPI0 GPIO config: SCK/PA5, MISO/PA6, MOSI/PA7 */ gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7); #elif defined GD32F4XX gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7); #endif gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7); gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7); gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3); gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3); #endif SET_SPI0_NSS_HIGH }
SPI 外設配置
SPI外設配置如代碼清單SPI例程SPI外設配置所示。GD32全系列MCU中SPI外設配置基本相同,在本例程中,SPI0作為主機全雙工模式,GD32標準庫提供了SPI初始化結構體及初始化函數來配置SPI外設,其初始化結構體說明如表 0-13 SPI初始化結構體說明列表所示,SPI初始化結構體填充完成后,調用spi_init函數進行SPI外設配置,配置完成后,調用spi_enable使能SPI外設。
void spi_config(void) { #if defined GD32F10X_HD|| GD32F30X_HD || GD32F1X0 || GD32F20X_CL || GD32F4XX || GD32F3X0 || GD32E10X || GD32E23X spi_parameter_struct spi_init_struct; /* SPI0 parameter config */ spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; spi_init_struct.device_mode = SPI_MASTER; spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; spi_init_struct.nss = SPI_NSS_SOFT; spi_init_struct.prescale = SPI_PSC_256; spi_init_struct.endian = SPI_ENDIAN_MSB; spi_init(SPI0, &spi_init_struct); spi_enable(SPI0); #endif }
SPI 初始化結構體說明列表

主函數說明
主函數如代碼清單SPI例程主函數所示,該主函數主要分成四部分,RCU時鐘配置、 GPIO 配置、SPI外設配置和while(1)主函數,前三部分已在前三小節介紹,在while(1)主循環中采用查詢的方法循環發送SPI數據,單次循環數據填充完成后,查詢RBNE和TRANS標志位判斷數據發送 完成,然后拉高CS片選,完成單次循環發送。
int main(void) { /* peripheral clock enable */ rcu_config(); /* GPIO config */ gpio_config(); /* SPI config */ spi_config(); while(1) { SET_SPI0_NSS_LOW /* wait for transmit complete */ while(send_n < arraysize){ while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI0, spi0_send_array[send_n++]); } while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE)); while(RESET != spi_i2s_flag_get(SPI0, SPI_FLAG_TRANS)) {} SET_SPI0_NSS_HIGH send_n = 0; } }
運行結果
將SPI_Example例程按照對應的芯片工程編譯完成后,下載到對應芯片中,采用示波器或者邏輯分析儀查看SPI_CS、SPI_CLK、SPI_MOSI引腳波形,如下圖SPI發送邏輯分析儀抓取波形圖所示,通過協議解析后,SPI數據發送正確。

8.5.SPI 使用注意事項
(1) 在切換SPI時鐘前要關閉SPI,切換完成后再使能SPI。
(2) 在采用SPI發送數據時,發送buf空標志TBE置位,并不代表數據發送完成,僅代表數據從發送數據寄存器移到發送移位寄存器中,如果通過查詢TBE標志來拉高CS片選,由于GD32系列MCU代碼執行效率較高,當發送速率較低時可能會出現當TBE置位時,拉高CS片選,此時數據還未完成發送,造成從機接受數據出錯。可以通過查詢接收數據寄存器非空RBNE和TRANS標志位來判斷數據發送完成,然后再拉高CS片選。
(3) SPI的MISO管腳需配置為浮空輸入模式,否則有可能數據接收異常。
-
單片機
+關注
關注
6050文章
44707瀏覽量
641323 -
mcu
+關注
關注
146文章
17486瀏覽量
354653 -
SPI
+關注
關注
17文章
1731瀏覽量
92905
發布評論請先 登錄
相關推薦
GD32 MCU 入門教程】GD32 MCU 常見外設介紹(12)FMC 模塊介紹

《GD32 MCU原理及固件庫開發指南》 + 初讀感悟
兆易創新GD32 MCU選型手冊,適用于GD32全系列MCU
【GD32 MCU 入門教程】一、GD32 MCU 開發環境搭建(1)使用Keil開發GD32

【GD32 MCU 入門教程】一、GD32 MCU 開發環境搭建(2)使用 IAR 開發 GD32

【GD32 MCU 入門教程】一、GD32 MCU 開發環境搭建(3)使用 Embedded Builder 開發 GD32

【GD32 MCU 入門教程】GD32 MCU 常見外設介紹(14)RTC 模塊介紹

【GD32 MCU入門教程】GD32 MCU GPIO 結構與使用注意事項

評論