STM32 ADC采样电压(轮询、中断、DMA)HAL库

《DMA配置的一些知识》


DMA的模式:

  1. Normal模式(不循环模式):当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次。
  2. Circular模式(循环模式):传输完成后又重新开始继续传输,不断循环永不停止(一般用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式))。

当开启循环模式之后,数据传输的数目变为0时,将会自动地恢复成配置通道时设置的初值,DMA操作将会继续进行。


data width:数据宽度

byte:字节,通用8位,与u8相同

word:字长,与硬件的位数相同,STM32是32位,所以对应是u32

Half Word:半个字长,所以对应是u16

一、ADC单通道+轮询模式

ADC单通道+单次转换模式(只转换一次)+不扫描模式(只有一个通道)

int main()
{
    // 初始化ADC
    MX_ADC1_Init();
    /* 开启 ADC自动校准 */
    if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
    {
        Error_Handler();
    }

    while (1)
    {
        /* The ADC1 starts collecting the voltage of the supercapacitor */
        HAL_ADC_Start(&hadc1);
        if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
        {
            adcValue = HAL_ADC_GetValue(&hadc1);
            supercapV = (uint32_t) adcValue * 3300 / 0xFFFF;
        }
        HAL_ADC_Stop(&hadc1);
    }
}

二、ADC单通道+中断

uint16_t ADC_Values;

int main()
{
    // 初始化ADC
    MX_ADC1_Init();
    // 开启ADC自动校准
    if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
    {
        Error_Handler();
    }
    //开启ADC使能中断
    HAL_ADC_Start_IT(&hadc1);

    while (1)
    {

    }
}

//ADC转换完成后自动调用ADC中断回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
   //获取ADC值并存储
   ADC_Values=HAL_ADC_GetValue(hadc);
}

三、ADC单通道+DMA采集数据

 ADC的连续转换模式 + ADC的不扫描模式 + DMA的Normal模式

为什么使用不循环模式呢?因为数据搬运到内存之后,需要先对内存进行一个操作,进行LCD显示结果之后,才进行下一轮采样。

方法一(只采样1次)

uint32_t adc_value = 0;
float supercar_voltage = 0.0;
int mian()
{
    HAL_ADC_Start_DMA(&hadc1, adc_value, 1); // 采样一个值

    while (1)
    {
        HAL_ADC_Start(&hadc1);                 // 启动ADC转换
        HAL_ADC_PollForConversion(&hadc1, 10); // 等待转换完成
    
        adc_value = HAL_ADC_GetValue(&hadc1);

        xsprintf(adcString1, "adc:%u ", adc_value);
        LCD_ShowString(4, 22, ST7735Ctx.Width, LCD_FONT_SIZE, LCD_FONT_SIZE, adcString1);                 
        xsprintf(adcString2, "adcV:%.4f ", (float)adc_value * (3.3 / 4096));              
        LCD_ShowString(4, 42, ST7735Ctx.Width, LCD_FONT_SIZE, LCD_FONT_SIZE, adcString2); 

        //设置的是Normal模式,下一次ADC和DMA采集要重新开启
        HAL_ADC_Start_DMA(&hadc1, ADC_Value, 10); 
     }
}

方法二(连续采样10次取平均值)

int main()
{
    int i;
    uint32_t sum;
    uint32_t ADC_Value[10] = {0};

    char adcString1[10] = {0};
    char adcString2[10] = {0};

    // 触发ADC转换,使用DMA传输数据,设置源地址、目标地址、传输数量
    HAL_ADC_Start_DMA(&hadc1, ADC_Value, 10); // 采样10个值存储在ADC_Value[10]数组中

    while (1)
    {
        if (HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1, HAL_ADC_STATE_REG_EOC)))
        {
            sum = 0;
            for (= 0; i < 10; i++)
            {
                sum += ADC_Value[i];
            }
            sum /= 10;

            // LCD显示结果
            xsprintf(adcString1, "adc:%u ", sum);
            LCD_ShowString(4, 22, ST7735Ctx.Width, LCD_FONT_SIZE, LCD_FONT_SIZE, adcString1);
            xsprintf(adcString2, "adcV:%.4f ", (float)sum * (3.3 / 4096));
            LCD_ShowString(4, 42, ST7735Ctx.Width, LCD_FONT_SIZE, LCD_FONT_SIZE, adcString2);

            //设置的是Normal模式,下一次ADC和DMA采集要重新开启
            HAL_ADC_Start_DMA(&hadc1, ADC_Value, 10); 
        }
    }
}

四、ADC多通道+DMA采集数据:

   ADC的连续转换模式 + ADC的扫描模式(多通道)+ DMA的Normal模式

 以下例子是使用6个通道,每个通道采集10次取平均值来减少误差:

int main()
{
    uint32_t ADC_Value[60] = {0};
    uint8_t dmaFlag = 0; // 检查中断服务函数里面DMA搬运完成的标志位
    char adcString1[10] = {0};
    char adcString2[10] = {0};

    // 触发ADC转换,使用DMA传输数据,设置源地址、目标地址、传输数量
    HAL_ADC_Start_DMA(&hadc1, ADC_Value, 10); // 采样10个值存储在ADC_Value[10]数组中

    while (1)
    {
        if (1 == dmaFlag)
        {
            for (int j = 0; j < 6; j++) // 遍历6个通道,轮流取值+显示
            {
                sum = 0;
                if (int i = 0; i < 60; i++)
                {
                    sum + = ADC_Value[(6 * i) + j]; // 每个通道采集l0次数据,进行10次累加
                }
                sum = sum / 10; // 取平均值

                // LCD显示结果
                xsprintf(adcString1, "adc:%u ", sum);
                LCD_ShowString(4, 22, ST7735Ctx.Width, LCD_FONT_SIZE, LCD_FONT_SIZE, adcString1);
                xsprintf(adcString2, "adcV:%.4f ", (float)sum * (3.3f / 4096)); // 计算12位分辨率的实际电压值
                LCD_ShowString(4, 42, ST7735Ctx.Width, LCD_FONT_SIZE, LCD_FONT_SIZE, adcString2);
            }

            dmaFlag = 0;                              // 清除DMA采集完成标志位
            HAL_ADC_Start_DMA(&hadc1, ADC_Value, 10); // 开启下一次ADC和DMA采集
        }
    }
}

 五、ADC单通道+DMA采集数据(把12位转成16位分辨率)

/***************************************************************************************************

 *  使用过采样和求均值的方式提高ADC的分辨率:

 *      例如:F1单片机最高只能采样12位的分辨率数据,但是现在需要采集16位的,为了节省成本而不想

 *            更换成H7单片机来采集16位的分辨率数据。此时,可以使用过采样的方法来提高分辨率。

 *

 *  公式:fos=(4^w)*fs   --->fos是过采样频率,w是希望增加的分辨率位数,fs是初始采样频率要求

 *  例如:本来是只有12位的分辨率:

 *         若提高1位分辨率,4fs,即采样4次后,把4次结果求和,再右移1位,就能得到13位分辨率的结果

 *         若提高2位分辨率,4^2fs=16fs,即采样16次后,把16次结果求和,再右移2位,就能得到14位分辨率的结果

 *         若提高3位分辨率,4^3fs=64fs,即采样64次后,把64次结果求和,再右移3位,就能得到15位分辨率的结果

 *         若提高4位分辨率,4^4fs=256fs,即采样256次后,把256次结果求和,再右移4位,就能得到16位分辨率的结果

 *  注意:提示n位分辨率,需要右移n位

 *

 * 以下例程是:

 *     ADC单通道+DMA采集数据(把12位转成16位分辨率):

 *                 ADC的连续转换模式 + ADC的不扫描模式 + DMA的Normal模式

 ****************************************************************************************************/

int main()
{
    uint32_t ADC_Value[10] = {0};
    unit32_t sum;
    int i;
    char adcString1[10] = {0};
    char adcString2[10] = {0};

    // 触发ADC转换,使用DMA传输数据,设置源地址、目标地址、传输数量
    HAL_ADC_Start_DMA(&hadc1, ADC_Value, 10); // 采样10个值存储在ADC_Value[10]数组中

    while (1)
    {
        if (1 == dmaFlag)
        {
            sum = 0;
            for (i = 0; i < 256 * 10; i++) // 用12位采集256次相当于16位的1次
            {
                sum += ADC_Value[i];
            }
            sum /= 10;
            sum >> = 4; // 得到一个16位分辨率的值

            // LCD显示结果
            xsprintf(adcString1, "adc:%lu ", sum);
            LCD_ShowString(4, 22, ST7735Ctx.Width, LCD_FONT_SIZE, LCD_FONT_SIZE, adcString1);
            xsprintf(adcString2, "adcV:%.4f ", (float)sum * (3.3 / 65536)); // 计算16位分辨率的实际电压值
            LCD_ShowString(4, 42, ST7735Ctx.Width, LCD_FONT_SIZE, LCD_FONT_SIZE, adcString2);

            dmaFlag = 0;                              // 清除DMA采集完成标志位
            HAL_ADC_Start_DMA(&hadc1, ADC_Value, 10); // 开启下一次ADC和DMA采集
        }
    }
}

五、相关回调函数


HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

当 ADC 完成一次转换时,会调用这个回调函数。


HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)

当 ADC 完成半个转换缓冲区的转换时,会调用这个回调函数。注意这个回调函数只在 DMA 转换模式下有效,且需要启用 DMA 半传输完成中断。


HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)

当 ADC 遇到错误时,会调用这个回调函数。


HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc)

当 ADC 的转换结果超出窗口阈值时,会调用这个回调函数。


HAL_ADC_EndOfConversionCallback(ADC_HandleTypeDef* hadc)

当 ADC 完成所有的转换序列时,会调用这个回调函数。


HAL_ADC_InjConvCpltCallback(ADC_HandleTypeDef* hadc)

当注入组转换完成时,会调用这个回调函数。

  • 13
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32ADC采样可以使用HAL库提供的函数进行操作。在启动ADC采样前,通常需要进行校准以提高采样准确性。可以使用函数HAL_ADCEx_Calibration_Start来进行自校准,该函数需要在ADC启动前使用。\[1\] STM32ADC具有1到3个ADC,可以独立使用或者使用双重模式以提高采样率。ADC是12位逐次逼近型的模拟数字转换器,具有18个通道,可以测量16个外部和2个内部信号源。ADC的转换可以以单次、连续、扫描或间断模式执行,并且结果可以以左对齐或右对齐方式存储在16位数据寄存器中。\[2\] HAL库对于STM32ADC采样转换提供了轮询中断DMA三种方式。可以根据需要选择合适的方式进行操作。此外,每个ADC每次处理时只能处理一个通道,所以在多通道采样时还涉及扫描模式。通过组合这些模式,可以满足不同的应用需求。\[3\] #### 引用[.reference_title] - *1* *3* [使用HAL库开发STM32ADC基础使用](https://blog.csdn.net/Naisu_kun/article/details/121532288)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于STM32ADC采样及各式滤波实现(HAL库,含VOFA+教程)](https://blog.csdn.net/black_sneak/article/details/129629485)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值