STM32 HAL库 SPI主从双机通信

本文描述了作者在STM32F407VGT6和STM32F103C8T6平台通过SPI进行主从通信时遇到的问题,如数据乱码、移位寄存器干扰等,并提供了相应的解决方法,包括硬件电路接线、外设配置和清除移位寄存器技巧。
摘要由CSDN通过智能技术生成

一、简介

最近因为项目需求,需要在一块板子内实现一个主机和五个从机的通信;
主机平台选用的是STM32F407VGT6,从机平台选用的是STM32F103C8T6;通信总线选用的是SPI总线。在构想是觉得采用SPI进行主从通信会很简单,但在实际开发的过程中,各种坑,通信时而正常时而混乱。不过在不断探究中,也逐渐发现了,各种问题所在,借此记录下来,希望能帮助一些兄弟在开发中避免一些坑。
本次实现的平台如下:
通信主机:

  • 芯片:STM32F103RCT6
  • 硬件平台:野火mini开发板

通信从机:

  • 芯片:STM32F103C8T6
  • 硬件平台:淘宝STM32F103C8T6最小系统开发板

软件:

  • HAL库
  • MDK
  • STM32CubeMx

二、开发过程中遇到的问题

以下是我在开发中遇到问题:

1. 相同的数据,每次发送,主从接收到都是不同的乱码
开发板之间没共地,或者供地接触不够好;更换质量较好的杜邦线,线材很影响信号质量。
2. 通信不正常,很没规律的不正常
检查主从机的SPI外设配置,接线等。
3. 数据出现移位
SPI总线的时钟质量不好,出现不该出现的高低电平,让从机认为这是一bit数据,出现移位寄存器移位,例如原本是8bit数据,现在由于干扰从机接收到的可能是9bit或者10bit数据,而从机实际接收到的数据只是最先传入的8bit数据。
要保证良好的时钟信号,同时也可以将SPI的数据采集触发改成时钟下降沿触发(好像是下降沿的信号质量要比上升沿的信号质量要更好),降低SPI通信速度。
在保证时钟的稳定的情况下,可通过复位从机的SPI外设来解决偶然发生的数据移位问题。
4.从机spi启动比主机慢
在主机发出片选信号都需要加一段延时,以确保从机的SPI外设比主机先启动。
在这里插入图片描述

三、硬件电路接线

NSS片选我们使用软件控制方式:
在这里插入图片描述
在这里插入图片描述
所以我们主从机的SPI通信接线就直接按照手册对接就行。
片选信号根据自己需求设置GPIO口,通过软件控制,有效电平和标准SPI协议保持一样就好,空闲高,有效低。

四、主从机SPI外设配置

4.1、主机配置

在这里插入图片描述
其中只有PA4、PA5、PA6、PA7是我们需要关注的,PA4是片选脚

4.2、从机配置

在这里插入图片描述
其中只有PA15、PB3、PB4、PB5是我们需要关注的,PA15是片选管脚。

4.3、接线

PA4 -> PA15(片选Nss)
PA5 -> PB3(SCK)
PA6 -> PB4(MISO)
PA7 -> PB5(MOSI)

五、如何清除移位寄存器

通过RCC寄存器复位SPI1外设,在从新初始化SPI1外设完成移位寄存器清除

    if(SPI1->SR != 0x02)    //当BSY为1时,表示SPI正在忙于通信,但在通信还未开始的时候BSY为1就可以表示移位寄存器存在干扰数据
    {
        //只用通过RCC复位整个SPI外设后重新初始化,才能清除spi移位寄存器中的残留数据
        __HAL_RCC_SPI1_FORCE_RESET();
        __HAL_RCC_SPI1_RELEASE_RESET();
        MX_SPI1_Init();
        printf("SPI复位,清空移位寄存器残留数据\n");
        HAL_Delay(10);
    }

六、代码

6.1、主机代码

uint8_t rx_buffer[22]={'1','2','3',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
uint8_t tx_buffer[22]={0x00,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16};
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("主从测试开始\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    //SPI通信
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
    HAL_Delay(2);      //通过延时保证从机SPI外设比主机先启动
    HAL_SPI_TransmitReceive(&hspi1,tx_buffer,rx_buffer,8,100);
    HAL_Delay(10);
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
    printf("接收到的数据:");
    for(num = 0;num < 8;num++)
    {
        printf("%#x ",rx_buffer[num]);
    }
    memset(rx_buffer,0,8);
    printf("\n");
    HAL_Delay(5000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
//重映射printf函数
int fputc(int ch,FILE*stream)
{
uint8_t c =ch;
HAL_UART_Transmit(&huart1,&c,1,50);
return ch;
}

6.2、从机代码

uint8_t Tx_data[8]={0x87,0xa2,0x41,0x02,0x93,0x04,0x05,0x06};
uint8_t Rx_data[8]={0};
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    bsp_spi_eeror(&hspi1);  //通信错误判断
    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_15) == 0)    //识别NSS线(低电平有效)
    {
    HAL_SPI_TransmitReceive(&hspi1,Tx_data,Rx_data,8,100);
        
    while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_15) == 0){};    //等待 主机 释放 片选   

    printf("spi从机Rx_dete:");
    for(int i=0 ; i < 8;i++)
    {
        printf("%#x ",Rx_data[i]);
    }
    memset(Rx_data,0,8);
    printf("\n主机以释放nss线\n");
    }

    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

七、效果展示

左侧为主机串口显示,右侧为从机串口显示;与程序中的数据对比可发现收发数据传输均正确
在这里插入图片描述

逻辑分析仪显示数据:由于中间片选出现了一次高电平干扰,导致后部分的数据分析异常,但实际传输的数据是正常的。
在这里插入图片描述

### 回答1: 很高兴为您服务!STM32 HAL库SPI DMA功能可帮助您在不使用CPU的情况下转移数据,从而节省CPU的资源。DMA可以自动地将数据从SPI口发送到内存,而无需CPU的参与。它可以提高系统的性能和数据传输速度。 ### 回答2: STM32 HAL库中的SPI DMA是指通过直接内存访问(DMA)来实现SPI数据传输的功能。 SPI (串行外设接口)是一种常用的串行通信协议,它可以连接MCU与外部设备进行通信。而DMA是一种直接内存访问技术,它可以在不经过CPU的干预下,直接将数据从一个地方传输到另一个地方。通过使用DMA进行SPI数据传输,可以提高效率和性能。 在STM32 HAL库中,使用SPI DMA可以通过以下步骤来实现SPI数据传输: 1. 配置SPI外设和DMA通道:首先要配置SPI外设的参数,例如设置SPI通信模式、数据大小、时钟极性和相位等。然后要配置DMA通道的参数,例如选择DMA传输方向、传输数据大小和MODE模式等。 2. 配置DMA传输缓冲区:为SPI和DMA设置传输缓冲区,这些缓冲区将用于存储传输的数据。 3. 启动DMA传输:使用HAL库中的函数来启动DMA传输。这将使DMA开始从传输缓冲区中读取数据并将其传输到SPI外设中。 4. 等待传输完成:使用HAL库中的函数来等待DMA传输完成。一旦传输完成,可以触发一个传输完成中断。 通过以上步骤,我们可以在STM32 HAL库中实现SPI DMA数据传输。这种方式可以使CPU更有效地利用时间来执行其他任务,提高系统的性能和效率。同时,SPI DMA也可以更好地支持大容量数据传输,减少了CPU的负载,提供了更好的实时性能。 ### 回答3: STM32 HAL库提供了一种简化SPI DMA(Direct Memory Access,直接内存存取)传输的方法。SPI是串行外设接口的一种通信协议,它能够同时传输和接收数据,而DMA是一种无需CPU参与的数据传输方法,可以提高数据传输效率和系统性能。 STM32 HAL库中的SPI DMA功能主要通过一系列API函数来实现。首先,我们需要初始化SPI外设的基本参数,包括通信模式(全双工、单工等)、时钟极性和相位配置、主从模式等。然后,开启DMA传输和中断以及SPI外设本身。接下来,通过调用HAL_SPI_Transmit_DMA和HAL_SPI_Receive_DMA函数来配置DMA传输缓冲区和传输长度,并启动传输。在传输完成后,通过检查相关中断标志位或回调函数来检测传输状态。 SPI DMA的优点在于减少了CPU的工作负担,使得CPU在数据传输期间可以处理其他任务,提高了系统的实时性和效率。同时,DMA传输还可以减少因CPU参与而产生的传输延迟,加快数据传输速度。此外,SPI DMA应用于具有高速数据传输需求的场景,如存储器读写、音频、图形显示等,能够更好地满足系统对快速、稳定数据传输的要求。 需要注意的是,在使用SPI DMA时,需要仔细考虑数据的传输顺序和互斥访问问题,以免造成数据冲突和错误。此外,DMA传输涉及到DMA缓冲区的管理和回调函数的设置,需要仔细调试和测试,以确保数据的正确传输和处理。 总之,STM32 HAL库提供了一种方便简化的SPI DMA传输方法,通过充分利用DMA传输的特点,可以提高系统性能和数据传输效率,适用于高速数据传输的应用场景。
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值