在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();
    }
}

相关推荐
遗憾是什么.2 小时前
数据结构 -- 链表
数据结构·链表
hweiyu002 小时前
数据结构:无向图
数据结构
alan07212 小时前
【mysql存储引擎为什么选择B+树】
数据结构
小龙报3 小时前
【算法通关指南:算法基础篇(四)】二维差分专题:1.【模板】差分 2.地毯
c语言·数据结构·c++·深度学习·神经网络·算法·自然语言处理
立志成为大牛的小牛3 小时前
数据结构——五十八、希尔排序(Shell Sort)(王道408)
数据结构·学习·程序人生·考研·算法·排序算法
IT阳晨。3 小时前
【STM32】RTC的使用和实时时钟项目
stm32·单片机·嵌入式硬件
国科安芯3 小时前
车规MCU在农业无人机电机驱动中的可靠性分析
单片机·嵌入式硬件·性能优化·无人机·安全威胁分析·安全性测试
TangDuoduo00053 小时前
【UART控制器HAL库常用接口】
单片机·嵌入式硬件