STM32的SPI外设片选只有一个怎么破?(转载)

aizhan52022-08-10知乎410

  SPI总线拓扑

  一般地,SPI总线按照下图方式进行连接,一主多从。

  每个从设备都有独立的片选引脚,主机同一时间段内,与一个从设备进行通信,也即选中一个从设备。

  MOSI/MISO/SCLK并联在一起

  MISO须是三态门,当从设备未选中时,该脚须设置为高阻态,而不能是输出态,否则会影响总线!

  对于MOSI/SCLK,虽然并联在一起,但是由于仅一个输出,多输入。

  但是你看STM32的SPI外设,一个SPI仅有一个NSS信号,以STM32F407的SPI2为例:

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

  不错,SPI总线要用GPIO模拟还是很容易的,但是这样做波特率做不高,需要占用CPU时间,效率比较低!而用SPI外设控制器,底层bit流的收发由外设控制器实现,用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的收发函数前后加上片选信号即可:

  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外设就可以控制多个从芯片了。你如果有兴趣,不妨照这个思路试试看。

  嵌入式交流可联系173 6188 7273 扣扣13 18 04 58 38

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。