stm32cubemx+freertos+dma实现ADC采样和数据处理

本文将使用stm32cubemx+freertos+DMA实现ADC的采样

stm32cubemx配置

此处的设置如下所示:

image-20250605145739256

image-20250605145816345

其他时钟方面的设置按照自己的需求来就好

freertos的设置如下,我这开的是CMSIS_V1,其他特别的配置就没有了

image-20250605151021868

代码

找到下面的这些函数,然后按照相同的方式进行修改即可,这里只提供一个可行的范例,有其他需求可以自己摸索一下进行修改

void StartDefaultTask(void const * argument)
{
  /* init code for USB_DEVICE */
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  // power_adc测量初始化
  HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);
  Start_Power_ADC_DMA(&hadc1);
  osThreadTerminate(NULL);
  /* USER CODE END StartDefaultTask */
}

实现下面这几个函数:

// 启动DMA
void Start_Power_ADC_DMA(ADC_HandleTypeDef* hadc)
{
    Start_ADC_Calibration(hadc);
    HAL_ADC_Start_DMA(hadc, (uint32_t*)adc_val_buf, (ADC_CHANNEL_CNT*ADC_CHANNEL_FRE));
}

// 该函数改为在freertos的任务中手动调用,不再通过中断的方式调用
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)
    {
        uint32_t tmp_value=0;
        for(int i = 0; i < ADC_CHANNEL_FRE; i ++ )
        {
            tmp_value += adc_val_buf[i];
        }
        adc_value = (float)ADC_CHANGE_VALUE * tmp_value / ADC_CHANNEL_FRE + 0.057;  // 需要加0.057补偿一下
        power_adc_power = POWER_CHANGE_VALUE * adc_value;
    }
}
流程图:
StartDefaultTask(调用Start_Power_ADC_DMA) -> HAL_ADC_ConvCpltCallback ---|(一直调用该函数将数据取出来处理即可)
															↑-------------|	

整个代码如下:

// power_adc.h
#ifndef POWER_ADC_H
#define POWER_ADC_H

#include "stm32f1xx_hal.h"

#define ADC_CHANNEL_CNT 1  // 采样通道数
#define ADC_CHANNEL_FRE 3  // 单个通道采样次数,用来取平均值
#define ADC_CHANGE_VALUE 0.00080586  // 右对齐情况下的系数
#define POWER_CHANGE_VALUE 21  // 实际电源和ADC测量值之间的倍数
#define RING_LOW_VOLT 3.75  // 单个电芯报警电压
#define PING_CRIT_VOLT  3.64  // 当个电芯危险电压
#define POWER_S 6  // 电芯数量


void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);  // DMA 转换完成时的回调函数
void Start_Power_ADC_DMA(ADC_HandleTypeDef* hadc);  // 开启DMA测量
void Stop_Power_ADC_DMA(ADC_HandleTypeDef* hadc);  // 关闭DMA测量
void Start_ADC_Calibration(ADC_HandleTypeDef* hadc);  // 开始ADC校准
void Restart_Power_ADC_DMA(void);  // 重启DMA
float Get_Power_ADC_Value(void);  // 返回电源的ADC值,换算过的
float Get_ADC_Value(void);  // 返回adc测量的值,没有换算过的


#endif // POWER_ADC_H
// power_adc.c
#include "power_adc.h"

static uint16_t adc_val_buf[ADC_CHANNEL_CNT*ADC_CHANNEL_FRE];  // 传递给DMA存放多通道采样值的数组
static float adc_value;  // 多通道的平均采样值
static float power_adc_power;  // 电源的电压值,经过计算的
static uint8_t power_low_count = 0;  // 记录低电压的次数
static uint8_t power_crit_count = 0;  // 记录报警电压次数
extern ADC_HandleTypeDef hadc1;
extern DMA_HandleTypeDef hdma_adc1;
extern volatile uint8_t beer_ring_mode;  // 控制蜂鸣器叫的函数

// 该函数改为在freertos的任务中手动调用,不再通过中断的方式调用
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)
    {
        uint32_t tmp_value=0;
        for(int i = 0; i < ADC_CHANNEL_FRE; i ++ )
        {
            tmp_value += adc_val_buf[i];
        }
        adc_value = (float)ADC_CHANGE_VALUE * tmp_value / ADC_CHANNEL_FRE + 0.057;  // 需要加0.057补偿一下
        power_adc_power = POWER_CHANGE_VALUE * adc_value;
        
        // 加一个累计计时,防止上电和下电的时候乱叫
        if(power_adc_power<PING_CRIT_VOLT * POWER_S) 
        {
            power_crit_count ++;
            power_low_count = 0;
        }else if(power_adc_power<RING_LOW_VOLT * POWER_S)
        {
            power_low_count ++;
            power_crit_count = 0;
            
        }else
        {
            power_low_count = 0;
            power_crit_count = 0;
        }
        
        if(power_crit_count > 30)
        {
            beer_ring_mode = 5;
            power_crit_count = 31;
        }else if(power_low_count > 30)
        {
            beer_ring_mode = 3;
            power_low_count = 31;
        }
    }
}

void Start_Power_ADC_DMA(ADC_HandleTypeDef* hadc)
{
    Start_ADC_Calibration(hadc);
    HAL_ADC_Start_DMA(hadc, (uint32_t*)adc_val_buf, (ADC_CHANNEL_CNT*ADC_CHANNEL_FRE));
}

void Stop_Power_ADC_DMA(ADC_HandleTypeDef* hadc)
{
    HAL_ADC_Stop_DMA(hadc);
}

void Restart_Power_ADC_DMA(void)
{
     // 停止当前的 DMA 传输
    HAL_DMA_Abort(&hdma_adc1);
    // 清除 DMA 中断标志
    __HAL_DMA_CLEAR_FLAG(&hdma_adc1, DMA_FLAG_TC1);
    // 重新启动 DMA 传输
    HAL_DMA_Start(&hdma_adc1, (uint32_t)&ADC1->DR, (uint32_t)adc_val_buf, (ADC_CHANNEL_CNT*ADC_CHANNEL_FRE));
}

void Start_ADC_Calibration(ADC_HandleTypeDef* hadc)
{
    HAL_ADCEx_Calibration_Start(hadc);
}

float Get_Power_ADC_Value(void)
{
    return power_adc_power;
}

float Get_ADC_Value(void)
{
    return adc_value;
}
posted @ 2025-06-05 15:19  ihuahua1415  阅读(364)  评论(0)    收藏  举报
*/