«

使用CubeMX配置STM32F401的ADC+DMA手动触发非连续转换(LL库)

时间:2021-12-16 22:12     作者:izilzty     分类:


1. 期望实现的效果

(a) 手动触发转换 -> ADC开始转换并由DMA传输 -> 一定量数据转换完成 -> 等待下次触发
                |---- 空闲时间执行其他程序 -> 检查是否转换完成 -> 读取数据并处理 -> (a)

2. 配置CubeMX工程

按照个人需求配置完时钟和其他外设后进入ADC配置页面:

关于各种转换选项大致功能描述:

ADC配置完成后点击DMA Settings选项卡进入DMA配置页面:

如果需要在中断内处理数据则需要在NVIC内打开DMA全局中断。这里不使用中断,直接读取DMA的TC标志,所以不配置中断。

完成后生成代码并打开

3. 添加自定义代码

uint16_t volatile ADC_DMA_Array[想要转换的次数 * CubeMX内设置的通道数];
/* 例如转换四次两通道,则内存数据排列为:CH1, CH2, CH1, CH2, CH1, CH2, CH1, CH2 */

void ADC_DMA_Start(void)
{
    volatile uint32_t adc_stable_count; /* ADC开启后等待稳定计数 */

    if (LL_ADC_IsEnabled(ADC1) != SET) /* 首次使用ADC开启并等待稳定 */
    {
        LL_ADC_Enable(ADC1);

        /* stm32f0xx_hal_adc.c */
        adc_stable_count = (3 * (SystemCoreClock / 1000000U));
        while (adc_stable_count != 0)
        {
            adc_stable_count--;
        }

        memset((uint8_t *)&ADC_DMA_Array, 0X00, sizeof(ADC_DMA_Array)); /* volatile显式转换消除警告 */
    }

    /* 停止上一次ADC转换 */
    LL_ADC_REG_SetContinuousMode(ADC1, LL_ADC_REG_CONV_SINGLE); /* 停止ADC连续转换 */
    while (LL_ADC_IsActiveFlag_EOCS(ADC1))                      /* 等待最后一次转换完成,避免数据错位 */
    {
        __NOP();
    }

    /* 配置DMA地址 */
    LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_0); /* DMA配置前需要关闭 */
    LL_DMA_ClearFlag_TC0(DMA2);
    LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_0, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA), (uint32_t)&ADC_DMA_Array, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_0, sizeof(ADC_DMA_Array)/sizeof(*ADC_DMA_Array));
    LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_0); /* 开启DMA */

    /* 准备开始ADC转换 */
    LL_ADC_ClearFlag_EOCS(ADC1);
    LL_ADC_ClearFlag_OVR(ADC1);
    LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);    /* ADC DMA传输完成后需要手动清除 DMA Bit 并且再次设置才可以开始下一次转换 */
    LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_LIMITED); /* 设置 DMA Bit */
    LL_ADC_REG_SetContinuousMode(ADC1, LL_ADC_REG_CONV_CONTINUOUS);   /* 启用ADC连续转换 */

    LL_ADC_REG_StartConversionSWStart(ADC1);    /* 开始转换 */
}

4. 主程序伪代码:

ADC_DMA_Start();
while(1)
{
    空闲时间,运行其他功能();
    if(LL_DMA_IsActiveFlag_TC0(DMA2)) /* 检查DMA传输是否完成 */
    {
        处理ADC数据();
        ADC_DMA_Start();
    }
}