打印
[应用相关]

STM32的SPI外设片选只有一个怎么破?

[复制链接]
190|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ST, pi
之前用STM32的SPI需要控制很多外部芯片,可是一个SPI的外设只有一个片选,要实现独立片选一主多从,怎么实现呢?

使用特权

评论回复
沙发
可怜的小弗朗士|  楼主 | 2022-8-31 14:04 | 只看该作者
SPI总线拓扑
一般地,SPI总线按照下图方式进行连接,一主多从。

如上图:

每个从设备都有独立的片选引脚,主机同一时间段内,与一个从设备进行通信,也即选中一个从设备。
MOSI/MISO/SCLK并联在一起
MISO须是三态门,当从设备未选中时,该脚须设置为高阻态,而不能是输出态,否则会影响总线!
对于MOSI/SCLK,虽然并联在一起,但是由于仅一个输出,多输入。
但是你看STM32的SPI外设,一个SPI仅有一个NSS信号,以STM32F407的SPI2为例:


那么要实现前面说的一主多从,怎么办呢?有朋友说,直接用GPIO去模拟不就可以了。

不错,SPI总线要用GPIO模拟还是很容易的,但是这样做波特率做不高,需要占用CPU时间,效率比较低!而用SPI外设控制器,底层bit流的收发由外设控制器实现,用GPIO模拟则需要CPU参与。

使用特权

评论回复
板凳
可怜的小弗朗士|  楼主 | 2022-8-31 14:05 | 只看该作者
怎么破呢?
菊花链拓扑



这种方案,省引脚。但是要移位控制,相对独立片选效率还是低不少。

使用特权

评论回复
地板
可怜的小弗朗士|  楼主 | 2022-8-31 14:05 | 只看该作者
独立片选拓扑
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的收发函数前后加上片选信号即可:
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;  
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

99

主题

742

帖子

0

粉丝