基於STM32F1與NRF24L01模塊的SPI簡單通信

  • 2019 年 10 月 18 日
  • 筆記

一、前言

1.簡介:

    本文是基於STM32F1,將數據發送至NRF模塊的寄存器,並將數據重新讀取,通過串口發送出來的簡單SPI單通信。

 

 

 

2.SPI簡介:

    調過STM8的都已經對SPI有所了解,調法都一致,這裡就不做詳細的講解。

 

 

 

3.準備工作:

    軟件層:  Keil5                 STMCubeMX5.1.0版本

           鏈接:點擊下載                              鏈接:點擊下載
      提取碼:wrt9             提取碼:20xs

   硬件層:STM32F1ZE開發板                                    NRF24L01模塊

                                    

 

 

                                     (什麼型號的都可以,只要能在CUbe配置好型號就行)

 

 

二、SPI詳解

         SPI是串行外設接口(Serial Peripheral Interface)的縮寫。是 Motorola 公司推出的一 
  種同步串行接口技術,是一種高速的,全雙工,同步的通信總線。因為其沒有指定的流控制,沒有應答機制確認是否接收                到數據,所以跟IIC總線協議比較在數據可靠性上有一定的缺陷。

1.硬件接線:

       VCC、GND不用講。下面的四條線是完成通信的最基本線,作用已簡略給出,需要進一步了解的可以詳看數據手冊。

大家可以按照相應線用示波器觀察對應波形,看時序是否正確。

 

CSN:從設備使能信號,由主設備控制。

SCK:時鐘信號,由主設備產生。

MISO:主設備數據輸入,從設備數據輸出。

MOSI:主設備數據輸出,從設備數據輸入。

(在這裡提示用示波器看的同學,記得看讀取到數據的波形時,切換到MISO線,在MOSI線上是觀測不到讀取回來的數據            的)

                       

 

 

 

 2.Cube配置問題:

                 一開始調試時用的TIM7來配置Cube,但是一直無法正確讀到波形,通過看數據手冊和部分歷程,我將時鐘配置成                       TIM2,就能正常收發了。其他配置跟平常串口配置一樣就行了,沒有特別需要注意的。

                 

 

 

 3.寄存器地址和驅動讀寫問題:

不同於IIC讀取時鐘模塊,這裡只需要寄存器的地址,同時要查詢到讀指令和寫指令來驅動設備讀與寫的功能。

             

#define CONFIG 0x00  //配置寄存器地址;    //寄存器命令:  #define READ_REG_NRF    0x00  //讀配置寄存器,低5位為寄存器地址  #define WRITE_REG_NRF   0x20  //寫配置寄存器,低5位為寄存器地址  

 4.本文部分代碼:

最後貼出一些主要的代碼,這些都是最基礎的代碼,看着數據手冊寫就行了。但是這裡我在讀取與寫入寄存器函數中並沒有使用延時,但是也能成功輸出。

SPI_test.c

void
delay2ms()//開機延時2MS,實測 { unsigned char a,b,c; for(c=5;c>0;c--) for(b=68;b>0;b--) for(a=31;a>0;a--); } //這個函數是將讀取/寫入一個位元組合二為一的基礎函數代碼。 uint32_t uSPI_RW_Byte(uint32_t uByte) { uint32_t uBits; for(uBits=0; uBits<8; uBits++) //8次循環 { //1.判斷為1或0 if(uByte & 0x80) PIN_MOSI_H; //該位為1則置1 else PIN_MOSI_L; //該位為0則置0 uByte <<= 1; //左移一位,可讀取1位,並輸出下一位 //2.時序線拉高 PIN_SCK_H; //拉高時序線,開始發送數據 //delay2ms(); //延時到中間,穩定數據 //3. if(MISO) //判斷MISO電平 uByte|=0x01; //若為1則賦值到相應的位上 //4. // delay2ms(); PIN_SCK_L; //結束該Byte數據的發送 } return uByte; }
如果看不懂這個函數,想分開的函數,可以用下面這兩個
/* ** 函數名 : SPI_Read_OneByte ** 返回值 : temp--SPI讀取的一位元組數據 ** 參 數 : None ** 描 述 : 下降沿讀數據,每次讀取 1 bit */ uint32_t SPI_Read_OneByte(void) { uint32_t i; uint32_t temp = 0; for(i=0;i<8;i++) { temp <<= 1; //讀取MISO 8次輸入的值,存入temp。之所以不放在“SCK = 0”語句之後的位置是因為: //讀取最後1byte的最後一位(即LSB)之後,不能再左移了 PIN_SCK_H; if(MISO) //讀取最高位,保存至最末尾,通過左移位完成讀整個位元組 temp |= 0x01; else temp &= ~0x01; PIN_SCK_L; //下降沿來了(SCK從1-->0),MISO上的數據將發生改變,穩定後讀取存入temp } return temp; } /* ** 函數名 : SPI_Write_OneByte ** 返回值 : None ** 參 數 : u8_writedata--SPI寫入的一位元組數據 ** 描 述 : 上升沿寫數據,每次寫入 1 bit */ void SPI_Write_OneByte(uint32_t u8_writedata) { uint32_t i; for(i=0;i<8;i++) { if(u8_writedata & 0x80) //判斷最高位,總是發送最高位 PIN_MOSI_H; //MOSI輸出1,數據總線準備數據1 else PIN_MOSI_L; //MOSI輸出0,數據總線準備數據0 PIN_SCK_H; //上升沿來了(SCK從0-->1),數據總線上的數據寫入到器件 u8_writedata <<= 1; //左移拋棄已經輸出的最高位 PIN_SCK_L; //拉低SCK信號,初始化為0 } } /* ** 函數名: nRF24L01_WriteReg ** 返回值: None ** 參 數 : (1)uint8 addr--寄存器地址 ** (2)uint8 value--寫入值 ** 說 明 : nRF24L01寄存器寫函數 */ void nRF24L01_WriteReg(uint32_t addr, uint32_t value) { PIN_CSN_L; //CS片選拉低 uSPI_RW_Byte(WRITE_REG_NRF+addr); uSPI_RW_Byte(value); PIN_CSN_H; //CS片選拉高 } /* ** 函數名: nRF24L01_ReadReg ** 返回值: value--讀取寄存器值 ** 參 數 : addr--寄存器地址 ** 說 明 : nRF24L01寄存器讀函數 */ uint32_t nRF24L01_ReadReg(uint8_t addr) { uint8_t value; PIN_CSN_L; //CS片選拉低 uSPI_RW_Byte(READ_REG_NRF+addr); //SPI讀數據 value=uSPI_RW_Byte(0); PIN_CSN_H; //CS片選拉高 return value; }

以下貼出的是SPI_test.h的部分代碼:是對上面的一些定義函數的補充

SPI_test.h    #define PIN_CSN_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET)  #define PIN_CSN_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET)    #define PIN_MISO_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET)  #define PIN_MISO_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET)    #define PIN_SCK_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET)  #define PIN_SCK_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET)    #define PIN_MOSI_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET)  #define PIN_MOSI_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET)    #define MISO HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) //判斷0或1

最後貼出主函數的調用函數,這裡是用調用HAL庫函數的串口直接發送的方法:如圖

 

 

 5.輸出結果驗證:

驗證是否讀取到數值,可以直接在示波器看波形,也可以採用直接在串口調試器看。但是個人建議先觀察示波器波形,比較容易找到問題所在。

 

 

 三、總結:

   相對於STM8,在STM32上來調試時,最主要是要注意時鐘配置問題,其他都比較方便,也沒有特別需要注意的問題。有不對的地方可以留言指出或提問。