之前用STM32的SPI需要控制很多外部芯片,可是一个SPI的外设只有一个片选,要实现独立片选一主多从,怎么实现呢?
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片选从芯片即可。 上代码看看: - <font face="Tahoma"><font size="3">void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
- {
- GPIO_InitTypeDef GPIO_InitStruct = {<font color="rgb(184, 215, 163)">0</font>};
- <font color="rgb(86, 156, 214)">if</font>(hspi->Instance==SPI1)
- {
- __HAL_RCC_SPI1_CLK_ENABLE();
- __HAL_RCC_GPIOA_CLK_ENABLE();
- <font color="rgb(87, 166, 74)"><i>/**SPI1 GPIO Configuration
- PA5 ------> SPI1_SCK
- PA6 ------> SPI1_MISO
- PA7 ------> SPI1_MOSI
- PA15 ------> SPI1_NSS 但是这里不用
- */</i></font>
- 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);*/
- }
- }
- </font></font><p style="line-height: 26px;"><font color="black"><font face="Tahoma"><font size="3">初始化SPI外设</font></font></font></p><font face="Tahoma"><font size="3"><font color="rgb(155, 155, 155)">#define SPI_CS1 GPIO_PIN_1</font>
- <font color="rgb(155, 155, 155)">#define SPI_CS1_PORT GPIOC</font>
- <font color="rgb(155, 155, 155)">#define SPI_CS2 GPIO_PIN_2</font>
- <font color="rgb(155, 155, 155)">#define SPI_CS2_PORT GPIOC</font>
- <font color="rgb(155, 155, 155)">#define SPI_CS3 GPIO_PIN_3</font>
- <font color="rgb(155, 155, 155)">#define SPI_CS3_PORT GPIOC</font>
- static void init_spi(SPI_HandleTypeDef * spi_handle)
- {
- <font color="rgb(87, 166, 74)"><i>/* SPI1 parameter configuration*/</i></font>
- 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 = <font color="rgb(184, 215, 163)">10</font>;
- 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);
- }
- </font></font><p style="line-height: 26px;"><font color="black"><font face="Tahoma"><font size="3">从而原来SPI的收发函数前后加上片选信号即可:</font></font></font></p><font face="Tahoma"><font size="3"><font color="rgb(86, 156, 214)">typedef</font> <font color="rgb(86, 156, 214)">enum</font>
- {
- SPI_CH_1=<font color="rgb(184, 215, 163)">0</font>,
- SPI_CH_2,
- SPI_CH_3,
- SPI_CH_LAST,
- } SPI_CH;
- static HAL_StatusTypeDef SPI_Select(SPI_CH ch)
- {
- <font color="rgb(86, 156, 214)">switch</font> (ch)
- {
- <font color="rgb(86, 156, 214)">case</font> SPI_CH_1:
- HAL_GPIO_WritePin(SPI_CS1_PORT,SPI_CS1,GPIO_PIN_RESET);
- <font color="rgb(86, 156, 214)">break</font>;
-
- <font color="rgb(86, 156, 214)">case</font> SPI_CH_2:
- HAL_GPIO_WritePin(SPI_CS2_PORT,SPI_CS2,GPIO_PIN_RESET);
- <font color="rgb(86, 156, 214)">break</font>;
-
- <font color="rgb(86, 156, 214)">case</font> SPI_CH_3:
- HAL_GPIO_WritePin(SPI_CS3_PORT,SPI_CS3,GPIO_PIN_RESET);
- <font color="rgb(86, 156, 214)">break</font>;
-
- <font color="rgb(86, 156, 214)">default</font>:
- <font color="rgb(86, 156, 214)">return</font> HAL_ERROR;
- }
- <font color="rgb(86, 156, 214)">return</font> HAL_OK;
- }
- static HAL_StatusTypeDef SPI_DeSelect(SPI_CH ch)
- {
- <font color="rgb(86, 156, 214)">switch</font> (ch)
- {
- <font color="rgb(86, 156, 214)">case</font> SPI_CH_1:
- HAL_GPIO_WritePin(SPI_CS1_PORT,SPI_CS1,GPIO_PIN_SET);
- <font color="rgb(86, 156, 214)">break</font>;
-
- <font color="rgb(86, 156, 214)">case</font> SPI_CH_2:
- HAL_GPIO_WritePin(SPI_CS2_PORT,SPI_CS2,GPIO_PIN_SET);
- <font color="rgb(86, 156, 214)">break</font>;
-
- <font color="rgb(86, 156, 214)">case</font> SPI_CH_3:
- HAL_GPIO_WritePin(SPI_CS3_PORT,SPI_CS3,GPIO_PIN_SET);
- <font color="rgb(86, 156, 214)">break</font>;
-
- <font color="rgb(86, 156, 214)">default</font>:
- <font color="rgb(86, 156, 214)">return</font> HAL_ERROR;
- }
- <font color="rgb(86, 156, 214)">return</font> 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;
- <font color="rgb(86, 156, 214)">if</font>(ch>=SPI_CH_LAST)
- <font color="rgb(86, 156, 214)">return</font> HAL_ERROR;
-
- SPI_Select(ch);
- ret = HAL_SPI_TransmitReceive(hspi,pTxData,pRxData,Size,Timeout);
- SPI_DeSelect(ch);
-
- <font color="rgb(86, 156, 214)">return</font> ret;
- }
- HAL_StatusTypeDef SPI_Transmit(SPI_CH ch,
- SPI_HandleTypeDef *hspi,
- uint8_t *pData,
- uint16_t Size,
- uint32_t Timeout)
- {
- HAL_StatusTypeDef ret;
- <font color="rgb(86, 156, 214)">if</font>(ch>=SPI_CH_LAST)
- <font color="rgb(86, 156, 214)">return</font> HAL_ERROR;
-
- SPI_Select(ch);
- ret = HAL_SPI_Transmit(hspi,pData,Size,Timeout);
- SPI_DeSelect(ch);
-
- <font color="rgb(86, 156, 214)">return</font> ret;
- }</font></font>
复制代码
如此一来,一个SPI外设就可以控制多个从芯片了。你如果有兴趣,不妨照这个思路试试看
|