在STM32G030xx的HAL库中使用DMA采集ADC多通达数据并开启DMA开启全满和半满中断

在STM32G030xx的HAL库中使用DMA采集ADC多通达数据并开启DMA开启全满和半满中断

例程说明

  • 芯片型号STM32G030C8T6
  • 使用DMA1 CH1采集ADC1数据
  • 使用DMA分时采集ADC多通道数据并开启DMA开启全满和半满中断

硬件连接

信号名称 ADC通道 描述
ADC_CH_INTER_VREF ADC_CHANNEL_VREFINT 内部参考电压
ADC_CH_NTC_TEMP ADC_CHANNEL_1 NTC温度检测
ADC_CH_ENV_GRAY ADC_CHANNEL_9 环境光敏电阻
ADC_CH_TXHV ADC_CHANNEL_10 发射高压
ADC_CH_APDHV ADC_CHANNEL_15 接收高压
ADC_CH_BAT ADC_CHANNEL_16 电池电压

一、ADC和DMA初始化

c 复制代码
#define NTC_TEMP_ADC_Pin                    GPIO_PIN_1
#define NTC_TEMP_ADC_GPIO_Port              GPIOA
#define GRAY_ADC_Pin                        GPIO_PIN_1
#define GRAY_ADC_GPIO_Port                  GPIOB
#define TXHV_ADC_Pin                        GPIO_PIN_2
#define TXHV_ADC_GPIO_Port                  GPIOB
#define APDHV_ADC_Pin                       GPIO_PIN_11
#define APDHV_ADC_GPIO_Port                 GPIOB
#define VBAT_ADC_Pin                        GPIO_PIN_12
#define VBAT_ADC_GPIO_Port                  GPIOB


ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;


void MX_ADC1_Init(void)
{
    ADC_ChannelConfTypeDef sConfig = {0};
    

    hadc1.Instance                   = ADC1;
    hadc1.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV2;
    hadc1.Init.Resolution            = ADC_RESOLUTION_12B;
    hadc1.Init.DataAlign             = ADC_DATAALIGN_RIGHT;
    hadc1.Init.ScanConvMode          = ADC_SCAN_SEQ_FIXED;
    hadc1.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;
    hadc1.Init.LowPowerAutoWait      = DISABLE;
    hadc1.Init.LowPowerAutoPowerOff  = DISABLE;
    hadc1.Init.ContinuousConvMode    = ENABLE;
    hadc1.Init.NbrOfConversion       = 1;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv      = ADC_SOFTWARE_START;
    hadc1.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.DMAContinuousRequests = ENABLE;
    hadc1.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;
    hadc1.Init.SamplingTimeCommon1   = ADC_SAMPLETIME_160CYCLES_5;
    hadc1.Init.OversamplingMode      = DISABLE;
    hadc1.Init.TriggerFrequencyMode  = ADC_TRIGGER_FREQ_HIGH;
    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
        Error_Handler();
    }

    if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK)
    {
        Error_Handler();
    }
}


void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if(adcHandle->Instance==ADC1)
    {
        __HAL_RCC_ADC_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        
        GPIO_InitStruct.Pin  = NTC_TEMP_ADC_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(NTC_TEMP_ADC_GPIO_Port, &GPIO_InitStruct);

        GPIO_InitStruct.Pin  = VBAT_ADC_Pin|GRAY_ADC_Pin|TXHV_ADC_Pin|APDHV_ADC_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        hdma_adc1.Instance                 = DMA1_Channel1;
        hdma_adc1.Init.Request             = DMA_REQUEST_ADC1;
        hdma_adc1.Init.Direction           = DMA_PERIPH_TO_MEMORY;
        hdma_adc1.Init.PeriphInc           = DMA_PINC_DISABLE;
        hdma_adc1.Init.MemInc              = DMA_MINC_ENABLE;
        hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
        hdma_adc1.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
        hdma_adc1.Init.Mode                = DMA_CIRCULAR;
        hdma_adc1.Init.Priority            = DMA_PRIORITY_MEDIUM;
        if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
        {
            Error_Handler();
        }

        __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
        __HAL_DMA_CLEAR_FLAG(&hdma_adc1, DMA_FLAG_HT1);
        __HAL_DMA_CLEAR_FLAG(&hdma_adc1, DMA_FLAG_TC1);
        __HAL_DMA_ENABLE_IT(&hdma_adc1, DMA_IT_HT);
        __HAL_DMA_ENABLE_IT(&hdma_adc1, DMA_IT_TC); 
    }
}


void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
    if(adcHandle->Instance==ADC1)
    {
        __HAL_RCC_ADC_CLK_DISABLE();

        HAL_GPIO_DeInit(NTC_TEMP_ADC_GPIO_Port, NTC_TEMP_ADC_Pin);
        HAL_GPIO_DeInit(GPIOB, VBAT_ADC_Pin|GRAY_ADC_Pin|TXHV_ADC_Pin|APDHV_ADC_Pin);

        HAL_DMA_DeInit(adcHandle->DMA_Handle);
    }
}


void MX_DMA_Init(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();

    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

二、中断函数

c 复制代码
void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma_adc1);
}


void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef * hadc)
{
    if (hadc->Instance == ADC1)
    {
        pADCDMA->State = ADC_DMA_MEM_STA_HFULL;
        __HAL_DMA_CLEAR_FLAG(&hdma_adc1, DMA_FLAG_HT1);
    }
}


void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef * hadc)
{
    if (hadc->Instance == ADC1)
    {
        pADCDMA->State = ADC_DMA_MEM_STA_AFULL;
        __HAL_DMA_CLEAR_FLAG(&hdma_adc1, DMA_FLAG_TC1);
    }
}

三、初始化接收数据缓存

c 复制代码
#define ADC_DMA_MEM_STA_NONE    (0)
#define ADC_DMA_MEM_STA_HFULL   (1)
#define ADC_DMA_MEM_STA_AFULL   (2)
#define ADC_DMA_MEM_SIZE        (32)


typedef enum
{
    ADC_CH_NTC_TEMP = ADC_CHANNEL_1, 
    ADC_CH_ENV_GRAY = ADC_CHANNEL_9, 
    ADC_CH_APDHV    = ADC_CHANNEL_15,
    ADC_CH_TXHV     = ADC_CHANNEL_10,
    ADC_CH_BAT      = ADC_CHANNEL_16,

    ADC_CH_INTER_VBAT = ADC_CHANNEL_VBAT, 
    ADC_CH_INTER_VREF = ADC_CHANNEL_VREFINT, 
    ADC_CH_INTER_TEMP = ADC_CHANNEL_TEMPSENSOR, 
}ADC_CH_ENUM;


typedef struct 
{
    volatile u8 State;
    u16 * pMem;
}ADC_DMA_Transfer_TypeDef;


static ADC_DMA_Transfer_TypeDef ADC_DMA_TypeDef = {0};
ADC_DMA_Transfer_TypeDef * pADCDMA = &ADC_DMA_TypeDef;
static u16 ADC_DMA_MEM[ADC_DMA_MEM_SIZE] = {0};


void vInit_ADC_DMA_Memory(void)
{
    pADCDMA = &ADC_DMA_TypeDef;
    pADCDMA->State = ADC_DMA_MEM_STA_AFULL;
    pADCDMA->pMem  = ADC_DMA_MEM;
}

四、停止及等待DMA

c 复制代码
static void vStop_ADC_DMA(void)
{
    HAL_ADC_Stop_DMA(&hadc1);
    HAL_DMA_Abort(&hdma_adc1);
    HAL_ADC_Stop(&hadc1);
}


static u8 xWait_ADC_DMA(void)
{
    u32 overTime = 0x2FFFF;

    pADCDMA->State = ADC_DMA_MEM_STA_NONE;
    while (pADCDMA->State != ADC_DMA_MEM_STA_AFULL)
    {
        if (overTime-- < 10) 
        {
            return 1;
        }
    }
    
    overTime = 0x2FFFF; 
    while (pADCDMA->State == ADC_DMA_MEM_STA_AFULL)
    {
        if (overTime-- < 10) 
        {
            return 2;
        }
    }

    overTime = 0x2FFFF; 
    while (pADCDMA->State != ADC_DMA_MEM_STA_AFULL)
    {
        if (overTime-- < 10) 
        {
            return 3;
        }
    }

    return 0;
}

五、设置ADC采样通道

c 复制代码
static u8 xSet_Sampling_Channel(u32 channel, u32 memSize)
{
    ADC_ChannelConfTypeDef sConfig = {0};

    vStop_ADC_DMA();
    CLEAR_BIT(ADC1->CHSELR, ADC_CHANNEL_ID_BITFIELD_MASK);
    sConfig.Channel      = channel;
    sConfig.Rank         = ADC_REGULAR_RANK_1; 
    sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        return 1;
    }

    if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK)
    {
        return 2;
    }

    if (HAL_ADC_Start_DMA(&hadc1, (u32 *)pADCDMA->pMem, memSize) != HAL_OK)
    {
        return 3;
    }
    
    return 0;
}

六、获取通道ADC值

c 复制代码
static u16 xGet_Channel_Value(u32 channel)
{
    u8 res = 0;

    if (xSet_Sampling_Channel(channel, ADC_DMA_MEM_SIZE))
    {
        res = 0;
        goto GET_CHX_VALUE_ERROR;
    }

    if (xWait_ADC_DMA()) 
    {
        res = 1;
        goto GET_CHX_VALUE_ERROR;
    }

    if (xWait_ADC_DMA()) 
    {
        res = 2;
        goto GET_CHX_VALUE_ERROR;
    }

    vStop_ADC_DMA();
    if((channel == ADC_CHANNEL_VBAT) || (channel == ADC_CHANNEL_VREFINT) || (channel == ADC_CHANNEL_TEMPSENSOR))
    {
        LL_ADC_SetCommonPathInternalCh(ADC1_COMMON, LL_ADC_PATH_INTERNAL_NONE);
    }

    BubbleSort_u16(pADCDMA->pMem, ADC_DMA_MEM_SIZE);

    #if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
    dprintf("Channel:%08X  Min:%u  Max:%u  ABS:%u\r\n", channel, pADCDMA->pMem[0], pADCDMA->pMem[ADC_DMA_MEM_SIZE - 1], (pADCDMA->pMem[ADC_DMA_MEM_SIZE - 1] - pADCDMA->pMem[0]));
    #endif

    return Average_u16(pADCDMA->pMem, ADC_DMA_MEM_SIZE, (ADC_DMA_MEM_SIZE >> 2));


GET_CHX_VALUE_ERROR:
    vStop_ADC_DMA();
    return res;
}

七、采集各通道数据

c 复制代码
//内部参考电压
void vGet_Inter_Vref(void)
{
    u32 value = 0, cali = 0;

    cali = *(u16 *)(ADDR_VREFCALI);
    LL_ADC_SetCommonPathInternalCh(ADC1_COMMON, LL_ADC_PATH_INTERNAL_VREFINT);
    vNopDelayMS(10);
    value = xGet_Channel_Value(ADC_CH_INTER_VREF);
    LL_ADC_SetCommonPathInternalCh(ADC1_COMMON, LL_ADC_PATH_INTERNAL_NONE);
    pAppSysPar->VRefInt = (cali * 3000) / value;

    #if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
    dprintf("Inter_Vref......cali:%-5u  value:%-5u  VRefInt:%umV\r\n\r\n", cali, value, pAppSysPar->VRefInt);
    #endif
}



//电池电压
u16 xGetBatVal(void)
{
    u64 vol = 0, adc = 0;

    adc = xGet_Channel_Value(ADC_CH_BAT);
    vol = ((adc * pAppSysPar->VRefInt * 151) / (4095 * 51));

    #if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
    dprintf("GetBatVol......REF:%-5u  ADC:%-5u  VBAT:%umV\r\n\r\n", pAppSysPar->VRefInt, (u16)(adc&0xFFFF), (u16)(vol&0xFFFF));
    #endif

    return (u16)vol;
}



//接收高压
u16 usGetAPDHV(void)
{
    u64 adc = 0, vol = 0;

    adc = xGet_Channel_Value(ADC_CH_APDHV);
    vol = (adc * pAppSysPar->VRefInt * 2239) / (39 * 4095);
    vol = (vol + 50) / 100;

    #if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
    dprintf("GetAPDHV......REF:%-5u  ADC:%-5u  APDHV(0.1V):%u\r\n\r\n", pAppSysPar->VRefInt, (u16)(adc&0xFFFF), (u16)(vol&0xFFFF));
    #endif

    return (u16)vol;
}



// 发射高压
u16 usGetTXHV(void)
{
    u64 adc = 0, vol = 0;

    adc = xGet_Channel_Value(ADC_CH_TXHV);
    vol = (adc * pAppSysPar->VRefInt * 2239) / (39 * 4095);
    vol = (vol + 50) / 100;

    #if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
    dprintf("GetTXHV......REF:%-5u  ADC:%-5u  TXHV(0.1V):%u\r\n\r\n", pAppSysPar->VRefInt, (u16)(adc&0xFFFF), (u16)(vol&0xFFFF));
    #endif

    return (u16)vol;
}


// 光敏电阻
u16 usGetGray(void)
{
    u64 adc = 0, vol = 0;

    adc = xGet_Channel_Value(ADC_CH_ENV_GRAY);
    vol = (adc * pAppSysPar->VRefInt / 4095);

    #if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
    dprintf("GetGray......REF:%-5u  ADC:%-5u  Vol(mV):%u\r\n\r\n", pAppSysPar->VRefInt, (u16)(adc&0xFFFF), (u16)(vol&0xFFFF));
    #endif

    return (u16)vol;
}


// NTC温度
s16 ssGetNTC(void)
{
    u32 adc    = 0, vol = 0;
    s16 temper = 0;
    s8  cp     = 0;

    
    adc    = xGet_Channel_Value(ADC_CH_NTC_TEMP);
    vol    = (adc * pAppSysPar->VRefInt / 4095);
    temper = GetTemeratureFromTab(vol);
    cp     = (s8)(0.035019888f * temper - 18.91695514f);

    #if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
    dprintf("GetNTC......REF:%-5u  ADC:%-5u  NTC(0.1C):%d  CP:%d  ", pAppSysPar->VRefInt, (u16)(adc&0xFFFF), temper, cp);
    #endif

    if (cp < -27) cp = -27;
    if (cp > 6)   cp = 6;
    temper = temper - cp;

    #if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
    dprintf("RNTC:%d\r\n\r\n", temper);
    #endif

    return temper;
}

八、测试结果

c 复制代码
static void xsuper_test(void)
{
    InitVref();
    xGetBatVal();
    ssGetNTC();
    usGetAPDHV();
    usGetTXHV();
    usGetGray();
    TimerStart(xsuper_test, 6000);
    dprintf("\r\n\r\n");
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();

    MX_DMA_Init();
    MX_USART1_UART_Init();
    MX_ADC1_Init();

    TimerStart(xsuper_test, 3000);
    while (1)
    {
        SoftTimerHandler();
    }
}

相关推荐
polarislove021425 分钟前
9.6 [定时器]超声波测距实验-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
chushiyunen37 分钟前
快慢双指针算法笔记
数据结构·笔记·算法
@小码农1 小时前
202512 电子学会 Scratch图形化编程等级考试三级真题(附答案)
服务器·开发语言·数据结构·数据库·算法
一路往蓝-Anbo2 小时前
C语言从句柄到对象 (六) —— 继承与 HAL:父类指针访问子类数据
c语言·开发语言·stm32·嵌入式硬件·物联网
报错小能手3 小时前
数据结构 字典树
开发语言·数据结构
XLYcmy3 小时前
高级密码生成器程序详解:专门设计用于生成基于用户个人信息的密码猜测组合
开发语言·数据结构·python·网络安全·数据安全·源代码·口令安全
AI科技星3 小时前
时空的固有脉动:波动方程 ∇²L = (1/c²) ∂²L/∂t² 的第一性原理推导、诠释与验证
数据结构·人工智能·算法·机器学习·重构
2401_841495644 小时前
【LeetCode刷题】寻找重复数
数据结构·python·算法·leetcode·链表·数组·重复数
一路往蓝-Anbo4 小时前
C语言从句柄到对象 (七) —— 给对象加把锁:RTOS 环境下的并发安全
java·c语言·开发语言·stm32·单片机·嵌入式硬件·算法
Joe_Blue_024 小时前
Matlab入门案例介绍—常用的运算符及优先级
开发语言·数据结构·matlab·matlab基础入门案例介绍