0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何实现独立片选一主多从

汽车电子技术 来源:嵌入式客栈 作者:逸珺 2023-01-20 11:53 次阅读

[导读] 大家好,我是逸珺。

之前用STM32的SPI需要控制很多外部芯片,可是一个SPI的外设只有一个片选,要实现独立片选一主多从,怎么实现呢?

SPI总线拓扑

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

微信截图_20230105161930.png

如上图:

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

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

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

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

怎么破呢?

菊花链拓扑

微信截图_20230105161930.png

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

独立片选拓扑

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 芯片
    +关注

    关注

    447

    文章

    47788

    浏览量

    409134
  • STM32
    +关注

    关注

    2239

    文章

    10671

    浏览量

    348744
  • SPI
    SPI
    +关注

    关注

    17

    文章

    1615

    浏览量

    89600
收藏 人收藏

    评论

    相关推荐

    RT-Thread Nano入门:独立看门狗(IWDT)

    本文主要介绍怎么用RT-Thread Nano实现独立看门狗IWDT驱动,创建一个喂狗线程,实现定时喂狗功能。
    的头像 发表于 11-22 11:04 825次阅读
    RT-Thread Nano入门:<b class='flag-5'>独立</b>看门狗(IWDT)

    TLE9867QXA20如何实现

    您好,团队,我在我的应用程序中使用 TLE9867QXA20,,现在我想为我的应用实现
    发表于 03-04 07:26

    SPI如何实现

    本帖最后由 dave520l 于 2014-6-23 17:11 编辑 有没有人知道SPI怎么设计,目前做的个小项目中,ST
    发表于 06-23 17:10

    STM32的SPI要实现独立怎么办呢

    之前用STM32的SPI需要控制很多外部芯片,可是个SPI的外设只有,要实现独立
    发表于 09-21 14:51

    采用ARM与FPGA芯片实现独立视频源LED显示系统的设计

    目前,显示屏按数据的传输方式主要有两类:一类是采用与计算机显示同一内容的实时视频屏;另一类为通过USB、以太网等通信手段把显示内容发给显示屏的独立视频源显示屏,若采用无线通信方式,还可以随时更新显示
    的头像 发表于 12-19 09:34 1378次阅读
    采用ARM与FPGA芯片<b class='flag-5'>实现</b><b class='flag-5'>独立</b>视频源LED显示系统的设计

    使用51单片机实现独立按键与矩阵按键的原理和资料及代码免费下载

    独立按键首先既然是检测输入,对于当然要用到拉电阻,来检测引脚电平变化变化。51单片机中,除了P0口外,P2,P3,P4都是内置上拉电阻的准双向IO口,一般 的 51 P0引脚都外接了上拉电阻,当然
    发表于 09-18 17:21 4次下载
    使用51单片机<b class='flag-5'>实现</b><b class='flag-5'>独立</b>按键与矩阵按键的原理和资料及代码免费下载

    使用51单片机实现独立键盘的简单资料说明

    1. 51单片机的P1、P2、P3口自有上拉电阻,P0口要想当做I/O口使用,必须也加个上拉电阻。 2. 如果用51单片机的I/O口用于输入,必须先将I/O口输出1,即置1。 3. 单片机组成的系统中,用的最多的是非编码键盘,其又分为独立式非编码键盘和行列式非编码键盘。 4. 数字电路中线与、线或的概念:
    发表于 09-09 17:26 0次下载
    使用51单片机<b class='flag-5'>实现</b><b class='flag-5'>独立</b>键盘的简单资料说明

    使用51单片机实现独立按键与矩阵按键控制数码管的程序免费下载

    当按键比较多的时候,用矩阵按钮,因为如果不用矩阵按钮,一个独立按键需要一个IO口,浪费资源。如: 16个独立按键需要16个io口, 而16个矩阵按键(4x4,一共8个管脚)需要8个IO口下面的程序,通过16个矩阵按钮,控制静态数码管,显示0~F
    发表于 09-04 17:27 5次下载
    使用51单片机<b class='flag-5'>实现</b><b class='flag-5'>独立</b>按键与矩阵按键控制数码管的程序免费下载

    使用单片机C语言实现独立按键检测与矩阵键盘操作的资料和程序

    所有的电子产品几乎到涉及到按键操作。所以微控制器是如何识别一个按键是否被按下,按下后又该如何做出反应,又如何防止按键抖动呢?更深入一点,微控制器又是如何识别矩阵键盘的?本文将详细阐述如何用C语言实现
    发表于 07-16 17:39 2次下载
    使用单片机C语言<b class='flag-5'>实现</b><b class='flag-5'>独立</b>按键检测与矩阵键盘操作的资料和程序

    使用STM32单片机实现独立看门狗实验的教程说明

    本文档的主要内容详细介绍的是使用STM32单片机实现独立看门狗实验的教程说明。
    发表于 01-02 08:00 3次下载
    使用STM32单片机<b class='flag-5'>实现</b><b class='flag-5'>独立</b>看门狗实验的教程说明

    使用单片机实现独立按键的程序和工程文件免费下载

    本文档的主要内容详细介绍的是使用单片机实现独立按键的程序和工程文件免费下载。
    发表于 09-25 17:33 13次下载
    使用单片机<b class='flag-5'>实现</b><b class='flag-5'>独立</b>按键的程序和工程文件免费下载

    使用单片机实现独立按键依次输入数据的C语言程序免费下载

    本文档的主要内容详细介绍的是使用单片机实现独立按键依次输入数据的C语言程序免费下载。
    发表于 11-24 17:57 7次下载

    使用单个比较器实现独立于内核的电压窗口信号检测

    本技术简介介绍了一种实现独立于内核的电压窗口信号电平检测(无需软件内核监控,与ADC的情况相同)的替代方法,该方法使用单个比较器以及PIC®单片机的独立于内核的外设(Core Independent
    发表于 03-30 16:36 9次下载
    使用单个比较器<b class='flag-5'>实现</b><b class='flag-5'>独立</b>于内核的电压窗口信号检测

    使用单片机实现独立按键的C语言实例免费下载

    本文档的主要内容详细介绍的是使用单片机实现独立按键的C语言实例免费下载。
    发表于 04-02 10:28 21次下载

    【MCU】基于STM32CubeMX 实现独立看门狗 IWDG

    STM32F10X 独立看门狗 IWDG1. IWDG 简介(1)STM32F10X内置了独立看门狗 IWDG,其可用来检查和解决由软件错误而引起的故障。当其递减计数器到达给定的超时的值时,产生系统
    发表于 11-05 15:51 10次下载
    【MCU】基于STM32CubeMX <b class='flag-5'>实现</b><b class='flag-5'>独立</b>看门狗 IWDG