加入星計劃,您可以享受以下權益:

  • 創(chuàng)作內容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 怎么破呢?
  • 相關推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

STM32的SPI外設片選只有一個怎么破?

2021/12/07
488
閱讀需 16 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大家好,我是逸珺。

之前用STM32的SPI需要控制很多外部芯片,可是一個SPI的外設只有一個片選,要實現(xiàn)獨立片選一主多從,怎么實現(xiàn)呢?

SPI總線拓撲

一般地,SPI總線按照下圖方式進行連接,一主多從。

如上圖:

  • 每個從設備都有獨立的片選引腳,主機同一時間段內,與一個從設備進行通信,也即選中一個從設備。MOSI/MISO/SCLK并聯(lián)在一起MISO須是三態(tài)門,當從設備未選中時,該腳須設置為高阻態(tài),而不能是輸出態(tài),否則會影響總線!對于MOSI/SCLK,雖然并聯(lián)在一起,但是由于僅一個輸出,多輸入。

但是你看STM32的SPI外設,一個SPI僅有一個NSS信號,以STM32F407的SPI2為例:

那么要實現(xiàn)前面說的一主多從,怎么辦呢?有朋友說,直接用GPIO去模擬不就可以了。

不錯,SPI總線要用GPIO模擬還是很容易的,但是這樣做波特率做不高,需要占用CPU時間,效率比較低!而用SPI外設控制器,底層bit流的收發(fā)由外設控制器實現(xiàn),用GPIO模擬則需要CPU參與。

怎么破呢?

菊花鏈拓撲

這種方案,省引腳。但是要移位控制,相對獨立片選效率還是低不少。

獨立片選拓撲

SPI外設的MOSI、MISO、SCK還是照用不誤,但是片選我們不用,設置成通用輸出模式,再用其他的GPIO片選從芯片即可。

上代碼看看:

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi->Instance==SPI1)
  {
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI
    PA15     ------> SPI1_NSS 但是這里不用
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

   /*__HAL_RCC_GPIOC_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);*/ 
  }
}

初始化SPI外設

#define SPI_CS1                        GPIO_PIN_1
#define SPI_CS1_PORT                   GPIOC
#define SPI_CS2                        GPIO_PIN_2
#define SPI_CS2_PORT                   GPIOC
#define SPI_CS3                        GPIO_PIN_3
#define SPI_CS3_PORT                   GPIOC
static void init_spi(SPI_HandleTypeDef * spi_handle)
{
  /* SPI1 parameter configuration*/
  spi_handle->Instance = SPI1;
  spi_handle->Init.Mode = SPI_MODE_MASTER;
  spi_handle->Init.Direction = SPI_DIRECTION_2LINES;
  spi_handle->Init.DataSize = SPI_DATASIZE_8BIT;
  spi_handle->Init.CLKPolarity = SPI_POLARITY_LOW;
  spi_handle->Init.CLKPhase = SPI_PHASE_1EDGE;
  spi_handle->Init.NSS = SPI_NSS_HARD_OUTPUT;
  spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  spi_handle->Init.FirstBit = SPI_FIRSTBIT_MSB;
  spi_handle->Init.TIMode = SPI_TIMODE_DISABLE;
  spi_handle->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  spi_handle->Init.CRCPolynomial = 10;
  ASSERT (HAL_SPI_Init(spi_handle) != HAL_OK);

 GPIO_InitTypeDef  GPIO_InitStructure;

 __HAL_RCC_GPIOC_CLK_ENABLE();

 GPIO_InitStructure.Pin = SPI_CS1;
 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStructure.Pull = GPIO_NOPULL;
 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;
 HAL_GPIO_Init(SPI_CS1_PORT, &GPIO_InitStructure); 
  
 GPIO_InitStructure.Pin = SPI_CS2;
 HAL_GPIO_Init(SPI_CS2_PORT, &GPIO_InitStructure);  
 
 GPIO_InitStructure.Pin = SPI_CS3;
 HAL_GPIO_Init(SPI_CS3_PORT, &GPIO_InitStructure);   
}

從而原來SPI的收發(fā)函數(shù)前后加上片選信號即可:

typedef enum 
{  
 SPI_CH_1=0,
 SPI_CH_2,
 SPI_CH_3,
 SPI_CH_LAST,
} SPI_CH;
static HAL_StatusTypeDef SPI_Select(SPI_CH ch)
{
   switch (ch)
   {
     case SPI_CH_1:
       HAL_GPIO_WritePin(SPI_CS1_PORT,SPI_CS1,GPIO_PIN_RESET);
       break;
       
     case SPI_CH_2:
       HAL_GPIO_WritePin(SPI_CS2_PORT,SPI_CS2,GPIO_PIN_RESET);
       break;
       
     case SPI_CH_3:
       HAL_GPIO_WritePin(SPI_CS3_PORT,SPI_CS3,GPIO_PIN_RESET);
       break;       
     
     default:
       return HAL_ERROR;
   }  
   return HAL_OK;
}
static HAL_StatusTypeDef SPI_DeSelect(SPI_CH ch)
{
   switch (ch)
   {
     case SPI_CH_1:
       HAL_GPIO_WritePin(SPI_CS1_PORT,SPI_CS1,GPIO_PIN_SET);
       break;
       
     case SPI_CH_2:
       HAL_GPIO_WritePin(SPI_CS2_PORT,SPI_CS2,GPIO_PIN_SET);
       break;
       
     case SPI_CH_3:
       HAL_GPIO_WritePin(SPI_CS3_PORT,SPI_CS3,GPIO_PIN_SET);
       break;       
     
     default:
       return HAL_ERROR;
   }
   return HAL_OK;
}

HAL_StatusTypeDef SPI_TransmitReceive(SPI_CH ch,
                    SPI_HandleTypeDef *hspi, 
                    uint8_t *pTxData, 
                    uint8_t *pRxData, 
                    uint16_t Size,
                    uint32_t Timeout)
{
   HAL_StatusTypeDef ret; 
   if(ch>=SPI_CH_LAST)
     return HAL_ERROR;  
    
   SPI_Select(ch);
   ret = HAL_SPI_TransmitReceive(hspi,pTxData,pRxData,Size,Timeout);
   SPI_DeSelect(ch);
   
   return ret;
}

HAL_StatusTypeDef SPI_Transmit(SPI_CH ch,
                 SPI_HandleTypeDef *hspi, 
                 uint8_t *pData, 
                 uint16_t Size, 
                 uint32_t Timeout)
{
   HAL_StatusTypeDef ret; 
   if(ch>=SPI_CH_LAST)
     return HAL_ERROR;  
    
   SPI_Select(ch);
   ret = HAL_SPI_Transmit(hspi,pData,Size,Timeout);
   SPI_DeSelect(ch);
   
   return ret;  
}

如此一來,一個SPI外設就可以控制多個從芯片了。你如果有興趣,不妨照這個思路試試看。

相關推薦

電子產(chǎn)業(yè)圖譜

嵌入式客棧,主要分享嵌入式Linux系統(tǒng)構建、嵌入式Linux驅動開發(fā)、單片機技術、FPGA開發(fā)、信號處理、工業(yè)通訊等技術主題。歡迎關注,一起交流,共同進步!