串行外設接口(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管腳需配置為浮空輸入模式,否則有可能數據接收異常。
-
單片機
+關注
關注
6037文章
44558瀏覽量
635355 -
mcu
+關注
關注
146文章
17148瀏覽量
351213 -
SPI
+關注
關注
17文章
1706瀏覽量
91590
發布評論請先 登錄
相關推薦
評論