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

发布者:skyshoucang最新更新时间:2024-01-10 来源: elecfans关键字:一主多从  STM32 手机看文章 扫描二维码
随时随地手机看文章

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


关键字:一主多从  STM32 引用地址:如何实现独立片选一主多从

上一篇:RTOS在STM32中的应用
下一篇:步进电机的常用驱动方式

推荐阅读最新更新时间:2024-11-08 19:34

STM32独立看门狗 学习笔记
一、独立看门狗概述: 独立看门狗其实就是一个独立于主时钟的定时复位狗。一旦开启独立看门狗,它就会根据自己设定的时间不断倒数,倒数到0后就开始复位。 二、关于喂狗: 喂狗就是让狗去吃东西,分心后,这只狗就得重新倒数。 三、stm32看门狗示例程序: #include led.h #include delay.h #include key.h #include sys.h #include usart.h void IWDG_Init(u8 prer,u16 rlr) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使
[单片机]
一文教会你STM32使用内部振荡器及其和外部晶体振荡器的区别和STM32L031性能分析和比较
在STM32上如果不使用外部晶振,OSC_IN和OSC_OUT的接法 如果使用内部RC振荡器而不使用外部晶振,请按照下面方法处理: 1)对于100脚或144脚的产品,OSC_IN应接地,OSC_OUT应悬空。 2)对于少于100脚的产品,有2种接法: 2.1)OSC_IN和OSC_OUT分别通过10K电阻接地。此方法可提高EMC性能。 2.2)分别重映射OSC_IN和OSC_OUT至PD0和PD1,再配置PD0和PD1为推挽输出并输出‘0’。此方法可以减小功耗并(相对上面 首先要明确的是STM32没有内部晶振,HSI是内部RC振荡器。 HSI内部8MHz的RC振荡器的误差在1%左右 内部RC振荡器的精度通常比用HSE(外部晶振
[单片机]
一文教会你<font color='red'>STM32</font>使用内部振荡器及其和外部晶体振荡器的区别和STM32L031性能分析和比较
STM32——硬件IIC机通信
前言:   根据网上的资料,大部分网友表示STM32自带的硬件IIC存在bug,读写时很容易卡死。自己在调试的时候也出现卡死的情况,最后一点一点调试,也还是调通了。本文将记录自己调试STM32硬件IIC从机的一些心得体会。硬件IIC主机通信见另一篇文章:传送门 。 硬件平台:STM32F205 软件平台:keil v5 函数库:标准库 硬件IIC从机初始化 下面看下STM32中IIC的相应设置。 首先是IIC的管脚配置。 /*---------IIC1---------------*/ uint8_t Buffer_Rx_IIC1 ;//接收缓存 uint8_t Rx_Idx_IIC1=0;//接收计数 ui
[单片机]
<font color='red'>STM32</font>——硬件IIC<font color='red'>从</font>机通信
STM32配置时钟时注意设置FLASH等待周期
兴致勃勃地把复位及时钟控制(RCC)的寄存器定义写完了,没得午觉睡倒算了,还累得满头大汗 看来这事有点吃力不讨好,真想用别人现成的头文件算了,最后还是咬咬牙自己写吧,至少可以让自己对寄存器结构熟悉一些。 傍晚回来再写个测试程序,满以为一次就OK的,可是调试时却发现,运行到选择PLL时钟后程序就跑飞 顿时就傻眼了 检查各个寄存器配置,似乎没什么问题啊:选择外部8M时钟,不分频输入到PLL,设置PLL为9倍频(注意实际写入到寄存器中为7),AHB、ABP2不分频,ABP1为2分频(注意设置为8才是2分频),都没有超过最大频率啊。看来想要搞个72M的主频遇到麻烦了 试着将倍频降下来,当主频为48M时,系统可以正常工作了,莫
[单片机]
keil+stm32+JTAG利用swd方式进行printf输出
使用ITM机制实现调试stm32单片机,实现printf与scanf。 1. ITM简介 ITM机制是一种调试机制,是新一代调试方式,在这之前,有一种比较出名的调试方式,称为半主机(semihosting)方式。 在pc上编写过C语言的人都知道,printf可以向控制台输出,scanf可以从控制台获取输入,这里的printf/scanf都是标准库函数,利用操作系统的这些函数,我们可以很方便的调试程序。在嵌入式设备上(如stm32单片机平台上)开发工具(如MDK/IAR)也都提供了标准库函,自然也提供了printf/scanf函数,那么这些函数是否可以使用呢? 问题来了,printf向哪里输出呢?并且大部分情况下,也没有键盘,又如何
[单片机]
keil+stm32+JTAG利用swd方式进行printf输出
STM32之CAN---接收管理分析
1 前言 当bxCAN接收到报文,经过过滤器过滤后,会将报文存储到FIFO中,由http://blog.csdn.net/flydream0/article/details/8148791一文中可知,每个过滤器组都会关联一个FIFO,由此可见,当接收到的报文通过过滤器后会被存储到此过滤器组关联的FIFO中(STM32共两个接收FIFO)。这个FIFO为3级邮箱深度,且完全由硬件来管理,从而节省了CPU的处理负荷,简化了软件并保证了数据的一致性。应用程序只能通过读取FIFO输出邮箱,来读取FIFO中最先收到的报文。 2 什么是FIFO输出邮箱? 在回答这个问题之前,首先要知道一些内容,STM32的bxCAN模式共有两个接收FIFO
[单片机]
<font color='red'>STM32</font>之CAN---接收管理分析
STM32开发笔记65: W5500跨路由不能访问问题的解决方法
单片机型号:STM32L053R8T6 现象描述:使用W5500进行以太网设计,用网线直接连接设备通信正常,使用家用无线路由器(自己用TP-Link做的实验),无论是外网访问内网,还是内网访问外网均正常。但是,在现场环境中,不能跨路由通信,现场环境使用的三层交换机是华为的S5720S。现场有正常能够使用的设备,将自己的设备的IP地址、子网掩码、网关与该设备设置的一致,并用同一个网口都不能正常通信。 问题解决:最后将问题锁定在MAC地址上,下图是我的设备的MAC地址,其关键点在于MAC地址的第1字节,具体解释如下。 MAC地址基本含义 MAC(Medium/Media Access Control)地址,用来表示互联网
[单片机]
<font color='red'>STM32</font>开发笔记65: W5500跨路由不能访问问题的解决方法
STM32 启动步骤和升级方式以及代码跳转的实现
#!/bin/sh #首先把BOOT0/Boot1 设置为 1 0, 即使用 STM32的ISP升级模式 #按下板子的reset, 硬复位进入 SYS ISP 模式(BOOTLOADER) #sudo stm32flash -w F407ZG_New.bin -v -g 0x0 /dev/ttyUSB0 sudo stm32flash -w F407ZG_Old.bin -v -g 0x0 /dev/ttyUSB0 #烧写以及验证完毕后, 自动加载 Flash的程序运行。 #把把BOOT0/Boot1 设置为 0 0, 即使用 STM32的flash模式,即用户程序模式。 #reset按键, 硬复位后自动启动新烧入的程
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved