STM32之IO口模擬SPI


本文介紹如何使用STM32標准外設庫的GPIO端口模擬SPI,本例程使用PA5、PA6和PA7模擬一路SPI。SPI有4種工作模式,模擬SPI使用模式0,即空閑時SCK為低電平,在奇數邊沿采樣。

本文適合對單片機及C語言有一定基礎的開發人員閱讀,MCU使用STM32F103VE系列。

 

1.   簡介

SPI 協議是由摩托羅拉公司提出的通訊協議(Serial Peripheral Interface),即串行外圍設備接口,是一種高速全雙工的通信總線。它被廣泛地使用在要求通訊速率較高的場合。SPI用於多設備之間通訊,分為主機Master和從機Slave,主機只有一個,從機可以有多個,通過片選信號對從機進行選擇,一次只能選擇一個從機。通訊只能由主機發起,支持的操作分為讀取和寫入,即主機讀取從機的數據,以及向從機寫入數據。

SPI一般有4根線,分別是片選線SS、時鍾線SCK、主設備輸出\從設備輸入MOSI、主設備輸入\從設備輸出MISO,其中除MISO對於主機為輸入引腳外,其他引腳對於主機均為輸出引腳。因為有獨立的輸入和輸出引腳,因此SPI支持全雙工工作模式,即可以同時接收和發送。

2.    總線傳輸信號

  • 空閑狀態:片選信號SS低電平有效,那么空閑狀態片選信號SS為高。
  • 開始信號及結束信號:開始信號需要將片選信號SS拉低,結束信號需要將片選信號SS拉高。
  • 通訊模式:SPI有4種通訊模式,分別為0、1、2、3,根據時鍾極性和時鍾相位確定,時鍾極性分別為空閑低電平和空閑高電平,時鍾相位分別為SCK奇數邊沿采樣和偶數邊沿采樣。常用的模式為模式0和模式3。
SPI模式 時鍾極性(空閑時SCK時鍾) 時鍾相位(采樣時刻)
0 低電平 奇數邊沿
1 低電平 偶數邊沿
2 高電平 奇數邊沿
3 高電平 偶數邊沿

 

3.    時序說明

以模式0舉例說明:

  • 空閑狀態:片選信號SS為高,SCK輸出低電平。
  • 開始信號:片選信號SS變低,SCK輸出低電平。
  • 結束信號:片選信號SS變高,SCK輸出低電平。
  • 讀取:SCK由低變高之后,讀取MISO引腳信號。
  • 寫入:SCK輸出低電平,MOSI引腳輸出相應的電平,然后SCK輸出高電平。
  • 一個時鍾周期同時讀取和寫入:SCK輸出低電平,主設備控制MOSI輸出相應電平,從設備控制MISO輸出相應電平,然后SCK輸出高電平,從設備讀取MOSI引腳電平,主設備讀取MISO引腳電平。即無論主設備還是從設備,均在SCK為低電平時輸出信號,在SCK為高電平時讀取信號。

4.    初始化

初始化跟普通GPIO類似,SCK和MOSI設置為推挽輸出,而MISO設置為浮空輸入。

GPIO初始化完成之后,SCK置為低電平,進入空閑狀態。

5.    模擬信號

由於SPI支持一個周期內同時讀取和寫入,因此讀取和寫入操作可以用一個函數實現,而單獨的讀取函數和寫入函數可以通過調用該讀寫函數實現。

 

完整代碼(僅自己編寫的部分)

 1 #define SPI_SCK_1    GPIO_SetBits(GPIOA, GPIO_Pin_5)            /* SCK = 1 */
 2 #define SPI_SCK_0    GPIO_ResetBits(GPIOA, GPIO_Pin_5)        /* SCK = 0 */
 3 
 4 #define SPI_MOSI_1    GPIO_SetBits(GPIOA, GPIO_Pin_7)            /* MOSI = 1 */
 5 #define SPI_MOSI_0    GPIO_ResetBits(GPIOA, GPIO_Pin_7)        /* MOSI = 0 */
 6 
 7 #define SPI_READ_MISO    GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)    /* 讀MISO口線狀態 */
 8 
 9 #define Dummy_Byte    0xFF    //讀取時MISO發送的數據,可以為任意數據
10 
11 
12 //初始化SPI
13 void SPI_IoInit(void)
14 {                         
15     GPIO_InitTypeDef GPIO_InitStructure;
16 
17     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);    
18 
19     //CS引腳初始化
20     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
21     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;           //推挽輸出
22     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
23     GPIO_Init(GPIOC, &GPIO_InitStructure);
24  
25     //SCK和MOSI引腳初始化
26     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
27     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;           //推挽輸出
28     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
29     GPIO_Init(GPIOA, &GPIO_InitStructure);
30  
31     //MISO引腳初始化
32     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
33     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;               //浮空輸入
34     GPIO_Init(GPIOA, &GPIO_InitStructure);
35     
36     SPI_CS_1;
37     SPI_SCK_1;
38 }
39 
40 //SPI可以同時讀取和寫入數據,因此一個函數即可滿足要求
41 uint8_t SPI_ReadWriteByte(uint8_t txData)
42 {
43     uint8_t i;
44     uint8_t rxData = 0;
45 
46     for(i = 0; i < 8; i++)
47     {
48         SPI_SCK_0;
49         delay_us(1);
50         //數據發送
51         if(txData & 0x80){        
52             SPI_MOSI_1;
53         }else{
54             SPI_MOSI_0;
55         }
56         txData <<= 1;
57         delay_us(1);
58 
59         SPI_SCK_1;
60         delay_us(1);
61         //數據接收
62         rxData <<= 1;
63         if(SPI_READ_MISO){
64             rxData |= 0x01;
65         }
66         delay_us(1);
67     }
68     SPI_SCK_0;
69 
70     return rxData;
71 }
72 
73 uint8_t SPI_ReadByte(void)
74 {
75     return SPI_ReadWriteByte(Dummy_Byte);
76 }
77 
78 void SPI_WriteByte(uint8_t txData)
79 {
80     (void)SPI_ReadWriteByte(txData);
81 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM