资讯详情

STM32CubeMX 开发笔记 (Ⅱ)—— ADC过采样与DMA

STM32CubeMX 开发笔记

软件环境:

  • STM32CubeMX 6.4.0
  • Clion 2021.3.2
  • gcc-arm-none-eabi-10.3-2021.10
  • CMake 3.21.1
  • MinGW 5.4

硬件环境:

STM32L432KBU6自制PCB

请添加图片描述

ADC DMA Oversampling

如何使用ADC采样?

第一次选择的时候就是看中了。STM32L432的16位ADC,参考他的系统框图,你可以看到16位ADC。

其实不是真的16位ADC,翻翻手册发现实际上是12ADC从采样到16位使用:

更气人的是Cubemx还单独列出了他:

您选择保存后生成代码:

其实库里根本没有16位ADC宏定义。

FAILED: CMakeFiles/ELE.elf.dir/Core/Src/adc.c.obj  D:\Embedded\STM32\arm_gcc\gcc-arm-none-eabi-10.3-2021.10\bin\arm-none-eabi-gcc.exe -DSTM32L432xx -DUSE_HAL_DRIVER -ID:/Embedded/STM32/Project/ELE/Core/Inc -ID:/Embedded/STM32/Project/ELE/Drivers/STM32L4xx_HAL_Driver/Inc -ID:/Embedded/STM32/Project/ELE/Drivers/STM32L4xx_HAL_Driver/Inc/Legacy -ID:/Embedded/STM32/Project/ELE/Drivers/CMSIS/Device/ST/STM32L4xx/Include -ID:/Embedded/STM32/Project/ELE/Drivers/CMSIS/Include -g -mcpu=cortex-m4 -mthumb -mthumb-interwork -ffunction-sections -fdata-sections -fno-common -fmessage-length=0 -Og -g -std=gnu11 -MD -MT CMakeFiles/ELE.elf.dir/Core/Src/adc.c.obj -MF CMakeFiles\ELE.elf.dir\Core\Src\adc.c.obj.d -o CMakeFiles/ELE.elf.dir/Core/Src/adc.c.obj -c D:/Embedded/STM32/Project/ELE/Core/Src/adc.c D:/Embedded/STM32/Project/ELE/Core/Src/adc.c: In function 'MX_ADC1_Init': D:/Embedded/STM32/Project/ELE/Core/Src/adc.c:47:27: error: 'ADC_RESOLUTION_16B' undeclared (first use in this function); did you mean 'ADC_RESOLUTION_6B'?    47 |   hadc1.Init.Resolution = ADC_RESOLUTION_16B;       |                           ^~~~~~~~~~~~~~~~~~       |                           ADC_RESOLUTION_6B  

我别无选择,只能用采样来尝试,这里记录一下解决方案供大家参考:

首先说一下CubeMX如何配置:

引脚分布如下:

ADC配置如下:

我不会重复其他具体选项的含义。如果需要,可以看:ADC配置信息。

以下是关于采样的配置信息:

  • Oersampling Right Shift:过采样右移位数

此处随着过采样倍数而变化,本文使用的是256倍也就是降采样信息扩大了2^8倍,这样就相当于ADC读到的数据全部左移了8位,但是ADC_DR最大支持16位,所以要再次右移4位。

  • Oversampling Ratio:过采样倍数

根据需要的位数进行倍变。

  • Regular Oversamping Mode:规则通道过采样模式

在注入序列中断时,超采样会暂时停止或重置。如果在常规组和注入组上都启用了超采样,这个参数将被丢弃并强制设置为"ADC_REGOVERSAMPLING_RESUMED_MODE"(在注入序列中,超采样缓冲器被清零)。本文没有用到注入通道所以选择连续模式。

  • Triggered Regular Oversampling:过采样触发模式 这个很好理解,多通道时是一次性全部触发还是一个一个接着触发采样。

关于过采样的原理这里就不再详细介绍,具体可见:过采样原理。

可以看到硬件上完成了过采样操作,直接输出16位的数据到寄存器。

DMA 的配置

因为使能了连续采样模式,所以DMA要开启来配合连续读入:

单次模式执行(当达到转换次数时,DMA传输停止)还是以连续模式执行(无论转换次数多少,DMA传输无限制)。

注意:在连续模式下,DMA必须被配置为循环模式。否则,当达到DMA缓冲区的最大指针时,就会触发超限。

在使能 DMA 模式的情况下(ADC_CR2 寄存器中的 DMA 位置 1),每完成规则通道组中的 一个通道转换后,都会生成一个 DMA 请求。这样便可将转换的数据从 ADC_DR 寄存器传输 到用软件选择的目标位。

注意这里使用的是Word,也就是按字来递增。理论上来说这里变换为short(halfword)类型也是可以的。

代码编写

首先使用的时候要校准一下ADC,然后开启一次DMA转换即可因为开启了ADC连续模式并且开启了DMA,

校准ADC的API:

HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);

开启ADC DMA传输的API:

if ( HAL_ADC_Start_DMA(&hadc1,(uint32_t *)&ADC_Value,1) != HAL_OK)
{
    Error_Handler();
}

最后我们使用串口把数据传输至PC:

可以看到浮空电压不是很稳定,我们测量3.3V试一试:

LDO使用的TI的超低噪声产品,看到电压测量很稳定的:

完整的代码如下:

/* USER CODE BEGIN 2 */
  volatile uint32_t ADC_Value=0;
  char TX_Uart_Buffer[30];
  memset(TX_Uart_Buffer,0,sizeof (TX_Uart_Buffer));
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
  if ( HAL_ADC_Start_DMA(&hadc1,(uint32_t *)&ADC_Value,1) != HAL_OK)
  { 
        
      Error_Handler();
  }
  while (1)
  { 
        
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HAL_Delay(500);
      sprintf(TX_Uart_Buffer , "ADC VALUE IS %lu\r\n", ADC_Value);
      HAL_UART_Transmit_DMA(&huart1, (uint8_t *)TX_Uart_Buffer, sizeof (TX_Uart_Buffer));
      HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
  }
  /* USER CODE END 3 */

一些小问题

首先是串口写的时候有点小问题:

可以看到输出的时候稳定会出现一些字符,出现还很稳定:

I?

很离谱,刚开始没想到,想了一会就想起来了,是初始化的时候没有清空,或者说发送的时候选择长度函数不太对:

实际上应该这样写:

HAL_UART_Transmit_DMA(&huart1, (uint8_t *)TX_Uart_Buffer,  strlen(TX_Uart_Buffer));

原来的写法:

HAL_UART_Transmit_DMA(&huart1, (uint8_t *)TX_Uart_Buffer,  sizeof (TX_Uart_Buffer));

sizeof返回的是char组的长度,可能是写kotlin习惯了直接sizeof,这次就用了sizeof导致没有初始化的部分露出来了,这样写也不是不行,你可以这样:

使用前先初始化一下:

memset(TX_Uart_Buffer,0,sizeof (TX_Uart_Buffer));

ADC DMA的问题

实际上这是一个很小的问题,就是ADC获取到的数据被DMA传输过来以后直接丢到了对应的位置,但是存在一种可能就是,DMA传输了一半数据,这时候被另一个DMA通道传去了串口,造成剩下的一半实际上是上一个数据,所以还是推荐使用单次触发来测量数据,或者增大数据缓冲区来解决冲突访问的问题。

最后的写法:

  /* USER CODE BEGIN 2 */
  volatile uint32_t ADC_Value=0;
  char TX_Uart_Buffer[30];
  memset(TX_Uart_Buffer,0,sizeof (TX_Uart_Buffer));
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
  while (1)
  { 
        
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HAL_Delay(500);
      if ( HAL_ADC_Start_DMA(&hadc1,(uint32_t *)&ADC_Value,1) != HAL_OK)
      { 
        
          Error_Handler();
      }
      sprintf(TX_Uart_Buffer , "ADC VALUE IS %lu\r\n", ADC_Value);
      HAL_UART_Transmit_DMA(&huart1, (uint8_t *)TX_Uart_Buffer,  strlen(TX_Uart_Buffer));
      HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
  }

标签: 16b6荷重传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台