单片机 :STM32F407
开发板:DMF407电机开发板
平台:keil   V5.31

HSE 为8MHZ
HSI为16MHZ

原理图:

一、单通道ADC采集实验:

主函数

int main(void)
{     
    uint16_t adcx;
    float temp;
    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    adc_init();                             /* 初始化ADC */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "ADC TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH3_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH3_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    while (1)
    {
        adcx = adc_get_result_average(ADC_ADCX_CHY, 10);                /* 获取ADC通道的转换值,10次取平均 */
        lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE);                  /* 显示ADC采样后的平均值 */
 
        temp = (float)adcx * (3.3 / 4096);                              /* 获取计算后的带小数的实际电压值,比如3.1111 */
        adcx = temp;                                                    /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
        lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);                  /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */

        temp -= adcx;                                                   /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111 - 3 = 0.1111 */
        temp *= 1000;                                                   /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数 */
        lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);               /* 显示小数部分(前面转换为了整形显示),这里显示的就是111 */

        LED0_TOGGLE();
        delay_ms(100);
    }
}

配置:

/* ADC及引脚 定义 */

#define ADC_ADCX_CHY_GPIO_PORT              GPIOA
#define ADC_ADCX_CHY_GPIO_PIN               GPIO_PIN_3
#define ADC_ADCX_CHY_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)          /* PA口时钟使能 */

#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CHY                        ADC_CHANNEL_3
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)           /* ADC1 时钟使能 */

/******************************************************************************************/
void adc_init(void)
{
    g_adc_handle.Instance = ADC_ADCX;
    g_adc_handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;            /* 4分频,21Mhz */
    g_adc_handle.Init.Resolution = ADC_RESOLUTION_12B;                          /* 12位模式 */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                          /* 右对齐 */
    g_adc_handle.Init.ScanConvMode = DISABLE;                                   /* 非扫描模式 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;                             /* 关闭连续转换 */
    g_adc_handle.Init.NbrOfConversion = 1;                                      /* 本实验只使用到一个规则序列 */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;                          /* 禁止不连续采样模式 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;                                  /* 不连续采样通道数为0 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                    /* 软件触发 */
    g_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;     /* 使用软件触发 */
    g_adc_handle.Init.DMAContinuousRequests = DISABLE;                          /* 关闭DMA请求 */
    HAL_ADC_Init(&g_adc_handle);                                                /* 初始化ADC */
}

void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
    if(hadc->Instance == ADC_ADCX)
    {
        GPIO_InitTypeDef gpio_init_struct;
        
        ADC_ADCX_CHY_CLK_ENABLE();                                                  /* 使能ADCx时钟 */
        ADC_ADCX_CHY_GPIO_CLK_ENABLE();                                             /* 开启GPIO时钟 */

        gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;                               /* ADC采集对应IO */
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;                                   /* 模拟输入 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                        /* 上拉 */
        HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);                   /* 初始化IO */
    }
}

void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    ADC_ChannelConfTypeDef adc_channel;                                 /* 配置对应ADC通道 */

    adc_channel.Channel = ch;                                           /* ADC通道 */
    adc_channel.Rank = rank;                                            /* 设置采样序列 */
    adc_channel.SamplingTime = stime;                                   /* 设置采样时间 */
    HAL_ADC_ConfigChannel( adc_handle, &adc_channel);   
}

uint32_t adc_get_result(uint32_t ch)
{
    adc_channel_set(&g_adc_handle , ch, 1, ADC_SAMPLETIME_480CYCLES);   /* 设置通道,序列和采样时间 */

    HAL_ADC_Start(&g_adc_handle);                                       /* 开启ADC */

    HAL_ADC_PollForConversion(&g_adc_handle, 10);                       /* 轮询转换 */

    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);                   /* 返回最近一次ADC1规则组的转换结果 */
}

uint32_t adc_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;
    uint8_t t;

    for (t = 0; t < times; t++)                 /* 获取times次数据 */
    {
        temp_val += adc_get_result(ch);         /* 累加 */
        delay_ms(5);
    }
    return temp_val / times;                    /* 返回平均值 */
}

测试结果:

PA3悬空时,读出电压为0.5V

PA3接GND时,接近0V

接3.3V

二、但通道ADC采集(使用DMA)

主函数

int main(void)
{
    uint16_t i, adcx;
    uint32_t sum;
    float temp;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */

    adc_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */
    
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "ADC DMA TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH3_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH3_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    adc_dma_enable(ADC_DMA_BUF_SIZE);       /* 启动ADC DMA采集 */

    while (1)
    {
        if (g_adc_dma_sta == 1)
        {
            /* 计算DMA 采集到的ADC数据的平均值 */
            sum = 0;

            for (i = 0; i < ADC_DMA_BUF_SIZE; i++)              /* 累加 */
            {
                sum += g_adc_dma_buf[i];
            }

            adcx = sum / ADC_DMA_BUF_SIZE;                      /* 取平均值 */

            /* 显示结果 */
            lcd_show_xnum(134, 110, adcx, 4, 16, 0, BLUE);      /* 显示ADC采样后的原始值 */

            temp = (float)adcx * (3.3 / 4096);                  /* 获取计算后的带小数的实际电压值,比如3.1111 */
            adcx = temp;                                        /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
            lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);      /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */

            temp -= adcx;                                       /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
            temp *= 1000;                                       /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数 */
            lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);   /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */

            g_adc_dma_sta = 0;                                  /* 清除DMA采集完成状态标志 */
            adc_dma_enable(ADC_DMA_BUF_SIZE);                   /* 启动下一次ADC DMA采集 */
        }

        LED0_TOGGLE();
        delay_ms(100);
    }
}

配置

/******************************************************************************************/
/* ADC及引脚 定义 */

#define ADC_ADCX_CHY_GPIO_PORT              GPIOA
#define ADC_ADCX_CHY_GPIO_PIN               GPIO_PIN_3
#define ADC_ADCX_CHY_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)             /* PA口时钟使能 */

#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CHY                        ADC_CHANNEL_3
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)              /* ADC1 时钟使能 */

/* ADC单通道/多通道 DMA采集 DMA及通道 定义 */
#define ADC_ADCX_DMASx                      DMA2_Stream4
#define ADC_ADCX_DMASx_Chanel               DMA_CHANNEL_0
#define ADC_ADCX_DMASx_IRQn                 DMA2_Stream4_IRQn
#define ADC_ADCX_DMASx_IRQHandler           DMA2_Stream4_IRQHandler

#define ADC_ADCX_DMASx_IS_TC()              ( __HAL_DMA_GET_FLAG(&g_dma_adc_handle, DMA_FLAG_TCIF0_4) )  /* 判断DMA2 Stream4传输完成标志, 这是一个假函数形式,
                                                                                                          * 不能当函数使用, 只能用在if等语句里面 
                                                                                                          */
#define ADC_ADCX_DMASx_CLR_TC()             do{ __HAL_DMA_CLEAR_FLAG(&g_dma_adc_handle, DMA_FLAG_TCIF0_4); }while(0)   /* 清除DMA2 Stream4传输完成标志 */

/******************************************************************************************/
void adc_dma_init(uint32_t mar)
{
    GPIO_InitTypeDef gpio_init_struct;
    ADC_ChannelConfTypeDef adc_ch_conf = {0};

    ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */
    ADC_ADCX_CHY_GPIO_CLK_ENABLE();                                         /* 开启GPIO时钟 */

    if ((uint32_t)ADC_ADCX_DMASx > (uint32_t)DMA2)                          /* 大于DMA2的基地址, 则为DMA2的数据流通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                                        /* DMA2时钟使能 */
    }
    else 
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                                        /* DMA1时钟使能 */
    }

    /* 设置AD采集通道对应IO引脚工作模式 */
    gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);
    
    /* 初始化DMA */
    g_dma_adc_handle.Instance = ADC_ADCX_DMASx;                             /* 设置DMA数据流 */
    g_dma_adc_handle.Init.Channel = DMA_CHANNEL_0;                          /* 设置DMA通道 */
    g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 从外设到存储器模式 */
    g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
    g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
    g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
    g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
    g_dma_adc_handle.Init.Mode = DMA_NORMAL;                                /* 外设流控模式 */
    g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
    HAL_DMA_Init(&g_dma_adc_handle);
    
    g_adc_dma_handle.Instance = ADC_ADCX;
    g_adc_dma_handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;    /* 4分频,21Mhz */
    g_adc_dma_handle.Init.Resolution = ADC_RESOLUTION_12B;                  /* 12位模式 */
    g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                  /* 右对齐 */
    g_adc_dma_handle.Init.ScanConvMode = DISABLE;                           /* 非扫描模式 */
    g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;                      /* 开启连续转换 */
    g_adc_dma_handle.Init.NbrOfConversion = 1;                              /* 本实验用到1个规则通道序列 */
    g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;                  /* 禁止不连续采样模式 */
    g_adc_dma_handle.Init.NbrOfDiscConversion = 0;                          /* 不连续采样通道数为0 */
    g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;            /* 软件触发 */
    g_adc_dma_handle.Init.DMAContinuousRequests = ENABLE;                   /* 开启DMA请求 */
    HAL_ADC_Init(&g_adc_dma_handle);                                        /* 初始化ADC */
    
    __HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);         /* 把ADC和DMA连接起来 */
    
    /* 配置ADC通道 */
    adc_ch_conf.Channel = ADC_ADCX_CHY;                                     /* 通道 */
    adc_ch_conf.Rank = 1;                                                   /* 序列 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_480CYCLES;                    /* 采样时间 */
    HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);                 /* 通道配置 */

    /* 配置DMA数据流请求中断优先级 */
    HAL_NVIC_SetPriority(ADC_ADCX_DMASx_IRQn, 3, 3);
    HAL_NVIC_EnableIRQ(ADC_ADCX_DMASx_IRQn);
    
    HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0);       /* 启动DMA,并开启中断 */
    HAL_ADC_Start_DMA(&g_adc_dma_handle,&mar,0);                            /* 开启ADC,通过DMA传输结果 */
}
void adc_dma_enable(uint16_t cndtr)
{
    __HAL_ADC_DISABLE(&g_adc_dma_handle);           /* 先关闭ADC */
    
    __HAL_DMA_DISABLE(&g_dma_adc_handle);           /* 关闭DMA传输 */
    g_dma_adc_handle.Instance->NDTR = cndtr;        /* 重设DMA传输数据量 */
    __HAL_DMA_ENABLE(&g_dma_adc_handle);            /* 开启DMA传输 */
    
    __HAL_ADC_ENABLE(&g_adc_dma_handle);            /* 重新启动ADC */
    ADC_ADCX->CR2 |= 1 << 30;                       /* 启动规则转换通道 */
}
void ADC_ADCX_DMASx_IRQHandler(void)
{
    if (ADC_ADCX_DMASx_IS_TC())
    {
        g_adc_dma_sta = 1;                          /* 标记DMA传输完成 */
        ADC_ADCX_DMASx_CLR_TC();                    /* 清除DMA2 数据流4 传输完成中断 */
    }
}

测试结果:

三、多通道ADC采集

主函数

int main(void)
{     
    uint16_t i,j,adcx;
    uint32_t sum;
    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    adc_init();                             /* 初始化ADC */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "ADC TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH3_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH3_VOL:0.000V", BLUE);                 /* 先在固定位置显示小数点 */
    
    lcd_show_string(30, 150, 200, 16, 16, "ADC1_CH4_VAL:", BLUE);
    lcd_show_string(30, 170, 200, 16, 16, "ADC1_CH4_VOL:0.000V", BLUE);                 /* 先在固定位置显示小数点 */
    
    lcd_show_string(30, 190, 200, 16, 16, "ADC1_CH5_VAL:", BLUE);
    lcd_show_string(30, 210, 200, 16, 16, "ADC1_CH5_VOL:0.000V", BLUE);                 /* 先在固定位置显示小数点 */    

    while (1)
    {
        if(g_adc_flag == 1)
        {
            for(j = 0; j<3; j++)
            {
                 sum = 0;                                                               /* 清零 */
                 for (i = 0; i < 10; i++)                                               /* 每个通道采集了10次数据,进行10次累加 */
                 {
                    sum += g_adc_value[(3 * i) + j];                                    /* 相同通道的转换数据累加 */
                 }
                 adcx = sum / (10);                                                     /* 取平均值 */
                
                 lcd_show_xnum(134, 110+(j*40), g_adc_value[j], 5, 16, 0, BLUE);        /* 显示ADCC采样后的原始值 */
                 g_adc_u_value[j] = (float)g_adc_value[j] * 3.3f /4096;
                 
                 adcx = g_adc_u_value[j];
                 lcd_show_xnum(134, 130+(j*40), adcx, 1, 16, 0, BLUE);                  /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */
                 g_adc_u_value[j] -= adcx;                                              /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
                 g_adc_u_value[j] *= 1000;                                              /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */
                 lcd_show_xnum(150, 130+(j*40), g_adc_u_value[j], 3, 16, 0X80, BLUE);   /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */
            }   
            
            g_adc_flag = 0;                                                             /* 清除标志位以便下次接收 */
            HAL_ADC_Start_IT(&g_adc_handle);                                            /* 当三次都转换完成之后重新开启下一次的转换 */ 
        }
       
        LED0_TOGGLE();
        delay_ms(100);
    }
}

配置

/* ADC及引脚 定义 */

#define ADC_ADCX_CH3_GPIO_PORT              GPIOA
#define ADC_ADCX_CH3_GPIO_PIN               GPIO_PIN_3
#define ADC_ADCX_CH3_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)             /* PA口时钟使能 */

#define ADC_ADCX_CH4_GPIO_PORT              GPIOA
#define ADC_ADCX_CH4_GPIO_PIN               GPIO_PIN_4
#define ADC_ADCX_CH4_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)             /* PA口时钟使能 */

#define ADC_ADCX_CH5_GPIO_PORT              GPIOA
#define ADC_ADCX_CH5_GPIO_PIN               GPIO_PIN_5
#define ADC_ADCX_CH5_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)             /* PA口时钟使能 */

#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CH3                        ADC_CHANNEL_3 
#define ADC_ADCX_CH4                        ADC_CHANNEL_4 
#define ADC_ADCX_CH5                        ADC_CHANNEL_5 
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)              /* ADC1 时钟使能 */
void adc_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    ADC_ChannelConfTypeDef adc_ch_conf;
    
    ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */
    ADC_ADCX_CH3_GPIO_CLK_ENABLE();                                         /* 开启通道3的GPIO时钟 */
    ADC_ADCX_CH4_GPIO_CLK_ENABLE();                                         /* 开启通道4的GPIO时钟 */
    ADC_ADCX_CH5_GPIO_CLK_ENABLE();                                         /* 开启通道5的GPIO时钟 */
    
    /* AD采集引脚模式设置,模拟输入 */
    gpio_init_struct.Pin = ADC_ADCX_CH3_GPIO_PIN|ADC_ADCX_CH4_GPIO_PIN|ADC_ADCX_CH5_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(ADC_ADCX_CH3_GPIO_PORT, &gpio_init_struct);
    
    g_adc_handle.Instance = ADC_ADCX;
    g_adc_handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;        /* 4分频,21Mhz */
    g_adc_handle.Init.Resolution = ADC_RESOLUTION_12B;                      /* 12位模式 */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                      /* 右对齐 */
    g_adc_handle.Init.ScanConvMode = ENABLE;                                /* 非扫描模式 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;                         /* 关闭连续转换 */
    g_adc_handle.Init.NbrOfConversion = 3;                                  /* 本实验用到3个规则通道序列 */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;                      /* 禁止不连续采样模式 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;                              /* 不连续采样通道数为0 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                /* 软件触发 */
    g_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* 使用软件触发 */
    g_adc_handle.Init.DMAContinuousRequests = DISABLE;                      /* 关闭DMA请求 */
    g_adc_handle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    HAL_ADC_Init(&g_adc_handle);                                            /* 初始化 */
   
    adc_ch_conf.Channel = ADC_CHANNEL_3;                                    /* ADC通道3 */
    adc_ch_conf.Rank = 1;
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_480CYCLES;
    HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);

    adc_ch_conf.Channel = ADC_CHANNEL_4;                                    /* ADC通道4 */
    adc_ch_conf.Rank = 2;
    HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);

    adc_ch_conf.Channel = ADC_CHANNEL_5;                                    /* ADC通道5 */
    adc_ch_conf.Rank = 3;
    HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);    
    
    HAL_NVIC_SetPriority(ADC_IRQn, 2, 2);                                   /* 抢占优先级2,子优先级2 */
    HAL_NVIC_EnableIRQ(ADC_IRQn);

    HAL_ADC_Start_IT(&g_adc_handle);                                        /* 开启ADC中断 */
}
void ADC_IRQHandler(void)
{
    g_adc_value[g_timer++] = HAL_ADC_GetValue(&g_adc_handle)&0xFFF;         /* 依次存储3个通道的原始值 */
    if(g_timer == 3)
    {
        g_timer = 0;
        g_adc_flag = 1;
    }
}

测试结果:

四、多通道ADC采集(使用DMA)

主函数

int main(void)
{
    uint16_t i,adcx;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    adc_nch_dma_init();                     /* ADC DMA初始化 */
    
    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "ADC 3CH DMA TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH3_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH3_VOL:0.000V", BLUE);             /* 先在固定位置显示小数点 */
    
    lcd_show_string(30, 150, 200, 16, 16, "ADC1_CH4_VAL:", BLUE);
    lcd_show_string(30, 170, 200, 16, 16, "ADC1_CH4_VOL:0.000V", BLUE);             /* 先在固定位置显示小数点 */
   
    lcd_show_string(30, 190, 200, 16, 16, "ADC1_CH5_VAL:", BLUE);
    lcd_show_string(30, 210, 200, 16, 16, "ADC1_CH5_VOL:0.000V", BLUE);             /* 先在固定位置显示小数点 */

    while (1)
    {
        for(i = 0; i<3; i++)
        {
             lcd_show_xnum(134, 110+(i*40), adc[i], 5, 16, 0, BLUE);                /* 显示ADC采样后的平均值 */
             g_adc_u_value[i] = (float)adc[i] * 3.3f /4096;                         /* 计算电压值 */
             
             adcx = g_adc_u_value[i];
             lcd_show_xnum(134, 130+(i*40), adcx, 1, 16, 0, BLUE);                  /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */
             g_adc_u_value[i] -= adcx;                                              /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
             g_adc_u_value[i] *= 1000;                                              /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数 */
             lcd_show_xnum(150, 130+(i*40), g_adc_u_value[i], 3, 16, 0X80, BLUE);   /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */
        }   
        LED1_TOGGLE();
        delay_ms(100);
    }
}

配置

/* ADC及引脚 定义 */

#define ADC_ADCX_CH3_GPIO_PORT              GPIOA
#define ADC_ADCX_CH3_GPIO_PIN               GPIO_PIN_3
#define ADC_ADCX_CH3_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)         /* PA口时钟使能 */

#define ADC_ADCX_CH4_GPIO_PORT              GPIOA
#define ADC_ADCX_CH4_GPIO_PIN               GPIO_PIN_4
#define ADC_ADCX_CH4_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)         /* PA口时钟使能 */

#define ADC_ADCX_CH5_GPIO_PORT              GPIOA
#define ADC_ADCX_CH5_GPIO_PIN               GPIO_PIN_5
#define ADC_ADCX_CH5_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)         /* PA口时钟使能 */

#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CH3                        ADC_CHANNEL_3                                       /* 通道Y,  0 <= Y <= 17 */ 
#define ADC_ADCX_CH4                        ADC_CHANNEL_4 
#define ADC_ADCX_CH5                        ADC_CHANNEL_5  
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)          /* ADC1 时钟使能 */

#define ADC_CH_NUM                          3                                                   /* 需要转换的通道数目 */
#define ADC_COLL                            1000                                                /* 单采集次数 */
#define ADC_SUM                             ADC_CH_NUM * ADC_COLL                               /* 总采集次数 */

/* ADC单通道/多通道 DMA采集 DMA及通道 定义*/

#define ADC_ADCX_DMASx                      DMA2_Stream4                                        /* DMA2数据流4 */
#define ADC_ADCX_DMASx_Chanel               DMA_CHANNEL_0                                       /* DMA通道0 */
#define ADC_ADCX_DMASx_IRQn                 DMA2_Stream4_IRQn
#define ADC_ADCX_DMASx_IRQHandler           DMA2_Stream4_IRQHandler

#define ADC_ADCX_DMASx_IS_TC()              ( __HAL_DMA_GET_FLAG(&g_dma_nch_adc_handle, DMA_FLAG_TCIF0_4) )  /* 判断DMA2 Stream4传输完成标志, 这是一个假函数形式,
                                                                                                              * 不能当函数使用, 只能用在if等语句里面 
                                                                                                              */
#define ADC_ADCX_DMASx_CLR_TC()             do{ __HAL_DMA_CLEAR_FLAG(&g_dma_nch_adc_handle, DMA_FLAG_TCIF0_4); }while(0)   /* 清除DMA2 Stream4传输完成标志 */
void adc_nch_dma_init(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};
    ADC_ChannelConfTypeDef sConfig = {0};
      
    ADC_ADCX_CHY_CLK_ENABLE();                                  /* 使能ADCx时钟 */
    ADC_ADCX_CH3_GPIO_CLK_ENABLE();                             /* 开启GPIO时钟 */
    ADC_ADCX_CH4_GPIO_CLK_ENABLE();
    ADC_ADCX_CH5_GPIO_CLK_ENABLE();
    
    if ((uint32_t)ADC_ADCX_DMASx > (uint32_t)DMA2)              /* 大于DMA2基地址,则为DMA2的数据流通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                            /* DMA2时钟使能 */
    }
    else 
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                            /* DMA1时钟使能 */
    }
    
    /* 设置ADC1通道3~5对应的IO口模拟输入 */
    gpio_init_struct.Pin = ADC_ADCX_CH3_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    gpio_init_struct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(ADC_ADCX_CH3_GPIO_PORT, &gpio_init_struct);

    gpio_init_struct.Pin = ADC_ADCX_CH4_GPIO_PIN;
    HAL_GPIO_Init(ADC_ADCX_CH4_GPIO_PORT, &gpio_init_struct);
    
    gpio_init_struct.Pin = ADC_ADCX_CH5_GPIO_PIN;   
    HAL_GPIO_Init(ADC_ADCX_CH5_GPIO_PORT, &gpio_init_struct); 

    /* DMA配置 */
    g_dma_nch_adc_handle.Instance = ADC_ADCX_DMASx;                                 /* 设置DMAx */
    g_dma_nch_adc_handle.Init.Channel = DMA_CHANNEL_0;                              /* 设置DMA通道 */
    g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                     /* 外设到存储器模式 */
    g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                         /* 外设非增量模式 */
    g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                             /* 存储器增量模式 */
    g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;        /* 外设数据长度:16位 */
    g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;           /* 存储器数据长度:16位 */
    g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL;                                    /* 外设流控模式 */
    g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                       /* 中等优先级 */
    HAL_DMA_Init(&g_dma_nch_adc_handle);
    
    __HAL_LINKDMA(&g_adc_nch_dma_handle,DMA_Handle,g_dma_nch_adc_handle);           /* 将DMA与adc联系起来 */
    
    g_adc_nch_dma_handle.Instance = ADC_ADCX;
    g_adc_nch_dma_handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;            /* 4分频,21Mhz */
    g_adc_nch_dma_handle.Init.Resolution = ADC_RESOLUTION_12B;                      /* 12位模式 */
    g_adc_nch_dma_handle.Init.ScanConvMode = ENABLE;                                /* 使能扫描模式 */
    g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;                          /* 使能连续转换 */
    g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;                      /* 禁止规则通道组间断模式 */
    g_adc_nch_dma_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* 使用软件触发 */
    g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                /* 软件触发 */
    g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                      /* 右对齐 */
    g_adc_nch_dma_handle.Init.NbrOfConversion = ADC_CH_NUM;                         /* 本实验用到3个规则通道序列 */
    g_adc_nch_dma_handle.Init.DMAContinuousRequests = ENABLE;                       /* 开启DMA连续转换 */
    HAL_ADC_Init(&g_adc_nch_dma_handle);
    
    sConfig.Channel = ADC_ADCX_CH3;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &sConfig);                         /* 配置ADC通道3 */

    sConfig.Channel = ADC_ADCX_CH4;
    sConfig.Rank = 2;
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &sConfig);                         /* 配置ADC通道4 */

    sConfig.Channel = ADC_ADCX_CH5;
    sConfig.Rank = 3;
    HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &sConfig);                         /* 配置ADC通道5 */

    /* 配置中断 */
    HAL_NVIC_SetPriority(ADC_ADCX_DMASx_IRQn, 2, 1);
    HAL_NVIC_EnableIRQ(ADC_ADCX_DMASx_IRQn);

    /* 开启ADC,使用DMA读取 */
    HAL_ADC_Start_DMA(&g_adc_nch_dma_handle,(uint32_t *)g_adc_value,ADC_SUM);
}
void ADC_ADCX_DMASx_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&g_dma_nch_adc_handle);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    HAL_ADC_Stop_DMA(hadc);                             /* 关闭DMA传输 */
  
    adc[0] = adc_get_result_average(0);                 /* 计算ADC平均值 */
    adc[1] = adc_get_result_average(1);
    adc[2] = adc_get_result_average(2);

    HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, (uint32_t*)&g_adc_value, ADC_SUM);
}

uint32_t adc_get_result_average(uint8_t ch)
{
    uint32_t temp_val = 0;
    uint16_t t;

    for (t = ch; t < ADC_SUM; t += ADC_CH_NUM )         /* 获取ADC_SUM次数据 */
    {
        temp_val += g_adc_value[t];
    }

    return temp_val / ADC_COLL;                         /* 返回平均值 */
}

测试结果:

五、内部温度传感器实验

主函数

int main(void)
{     
    short temp;
    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    adc_temperature_init();                 /* 初始化ADC内部温度传感器采集 */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "Temperature TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 120, 200, 16, 16, "TEMPERATE: 00.00C", BLUE);
 
    while (1)
    {
        temp = adc_get_temperature();       /* 得到温度值 */

        if (temp < 0)
        {
            temp = -temp;
            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, "-", BLUE);       /* 显示负号 */
        }
        else
        {
            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, " ", BLUE);       /* 无符号 */
        }
        lcd_show_xnum(30 + 11 * 8, 120, (temp / 100), 2, 16, 0, BLUE);      /* 显示整数部分 */
        lcd_show_xnum(30 + 14 * 8, 120, (temp % 100), 2, 16, 0X80, BLUE);   /* 显示小数部分 */
        
        LED0_TOGGLE();                                                      /* LED0闪烁,提示程序运行 */
        delay_ms(250);
    }
}

配置

/* ADC 定义 */

#define ADC_ADCX                            ADC1 
#define ADC_TEMPSENSOR_CHX                  ADC_CHANNEL_TEMPSENSOR                              /* 内部温度传感器专用通道 */ 
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)          /* ADC1 时钟使能 */
void adc_temperature_init(void)
{   
    ADC_ChannelConfTypeDef adc_ch_conf = {0};
        
    ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */
    
    g_adc_handle.Instance = ADC_ADCX;
    g_adc_handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;        /* 4分频,21Mhz */
    g_adc_handle.Init.Resolution = ADC_RESOLUTION_12B;                      /* 12位模式 */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                      /* 右对齐 */
    g_adc_handle.Init.ScanConvMode = DISABLE;                               /* 非扫描模式 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;                         /* 关闭连续转换 */
    g_adc_handle.Init.NbrOfConversion = 1;                                  /* 本实验用到1个规则通道序列 */
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;                      /* 禁止不连续采样模式 */
    g_adc_handle.Init.NbrOfDiscConversion = 0;                              /* 不连续采样通道数为0 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                /* 软件触发 */
    g_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* 使用软件触发 */
    g_adc_handle.Init.DMAContinuousRequests = DISABLE;                      /* 关闭DMA请求 */
    HAL_ADC_Init(&g_adc_handle);                                            /* 初始化ADC */

    /* 配置ADC通道 */
    adc_ch_conf.Channel = ADC_TEMPSENSOR_CHX;                               /* ADC通道 */
    adc_ch_conf.Rank = 1;                                                   /* 序列 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_480CYCLES;                    /* 采样时间 */
    HAL_ADC_ConfigChannel(&g_adc_handle,&adc_ch_conf);                      /* 通道配置 */
}
uint32_t adc_get_result(uint32_t ch)
{
    HAL_ADC_Start(&g_adc_handle);                                           /* 开启ADC */

    HAL_ADC_PollForConversion(&g_adc_handle, 0xFFFF);                       /* 轮询转换 */

    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);                       /* 返回转换结果 */
    
}
uint32_t adc_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;
    uint8_t t;

    for (t = 0; t < times; t++)                             /* 获取times次数据 */
    {
        temp_val += adc_get_result(ch);
        delay_ms(5);
    }

    return temp_val / times;                                /* 返回平均值 */
}
short adc_get_temperature(void)
{
    uint32_t adcx;
    short result;
    double temperature;

    adcx = adc_get_result_average(ADC_TEMPSENSOR_CHX, 20);  /* 读取内部温度传感器通道,20次取平均 */
    
    temperature = (float)adcx * (3.3 / 4096);               /* 转化为电压值 */
    
    temperature = (temperature - 0.76) / 0.0025 + 25;       /* 计算温度 */
    
    result = temperature *= 100;                            /* 扩大100倍 */
    
    return result;

}

测试结果:

六、定时器触发ADC采集

主函数

int main(void)
{
    uint16_t adcx;
    float temp;
    
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);         /* 设置时钟,168Mhz */
    delay_init(168);                            /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */
    adc_dma_init();                             /* ADC DMA初始化 */
    gtim_timx_int_init(10000-1,8400-1);         /* 10K Hz计数频率,1s溢出一次 */
    
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "ADC DMA TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH3_VAL:", BLUE);
    lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH3_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */

    while (1)
    {
        if(g_adc_dma_sta == 1)
        {
            adcx = g_adc_value[0];                              /* 默认只使用一个ADC通道 */
            
            /* 显示结果 */
            lcd_show_xnum(134, 110, adcx, 4, 16, 0, BLUE);      /* 显示ADCC采样后的原始值 */

            temp = (float)adcx * (3.3 / 4096);                  /* 获取计算后的带小数的实际电压值,比如3.1111 */
            adcx = temp;                                        /* 赋值整数部分给adcx变量,因为adcx为u16整形 */
            lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);      /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */

            temp -= adcx;                                       /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
            temp *= 1000;                                       /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数 */
            lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);   /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */

            g_adc_dma_sta = 0;                                  /* 清除DMA采集完成状态标志 */
        }
        
        LED0_TOGGLE();
        delay_ms(100);
    }
}

配置

/* ADC及引脚 定义 */

#define ADC_ADCX_CHY_GPIO_PORT              GPIOA
#define ADC_ADCX_CHY_GPIO_PIN               GPIO_PIN_3
#define ADC_ADCX_CHY_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)         /* PA口时钟使能 */

#define ADC_ADCX                            ADC1 
#define ADC_ADCX_CHY                        ADC_CHANNEL_3 
#define ADC_ADCX_CHY_CLK_ENABLE()           do{ __HAL_RCC_ADC1_CLK_ENABLE(); }while(0)          /* ADC1 时钟使能 */

#define ADC_CH_NUM                          1                                                   /* 需要转换的通道数目 */

/* ADC单通道/多通道 DMA采集 DMA及通道 定义 */
#define ADC_ADCX_DMASx                      DMA2_Stream4
#define ADC_ADCX_DMASx_Chanel               DMA_CHANNEL_0
#define ADC_ADCX_DMASx_IRQn                 DMA2_Stream4_IRQn
#define ADC_ADCX_DMASx_IRQHandler           DMA2_Stream4_IRQHandler

#define ADC_ADCX_DMASx_IS_TC()              ( __HAL_DMA_GET_FLAG(&g_dma_adc_handle, DMA_FLAG_TCIF0_4) )  /* 判断DMA2 Stream4传输完成标志, 这是一个假函数形式,
                                                                                                          * 不能当函数使用, 只能用在if等语句里面 
                                                                                                          */
#define ADC_ADCX_DMASx_CLR_TC()             do{ __HAL_DMA_CLEAR_FLAG(&g_dma_adc_handle, DMA_FLAG_TCIF0_4); }while(0)   /* 清除DMA2 Stream4传输完成标志 */


/* 通用定时器 定义 */

#define GTIM_TIMX_INT                       TIM3
#define GTIM_TIMX_INT_IRQn                  TIM3_IRQn
#define GTIM_TIMX_INT_IRQHandler            TIM3_IRQHandler
#define GTIM_TIMX_INT_CLK_ENABLE()          do{ __HAL_RCC_TIM3_CLK_ENABLE(); }while(0)      /* TIM3 时钟使能 */
void adc_dma_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    ADC_ChannelConfTypeDef adc_ch_conf = {0};

    ADC_ADCX_CHY_CLK_ENABLE();                                              /* 使能ADCx时钟 */
    ADC_ADCX_CHY_GPIO_CLK_ENABLE();                                         /* 开启GPIO时钟 */

    if ((uint32_t)ADC_ADCX_DMASx > (uint32_t)DMA2)                          /* 大于DMA2的基地址, 则为DMA2的数据流通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                                        /* DMA2时钟使能 */
    }
    else 
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                                        /* DMA1时钟使能 */
    }

    /* 设置AD采集通道对应IO引脚工作模式 */
    gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_ANALOG;
    gpio_init_struct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct);
    
    /* 初始化DMA */
    g_dma_adc_handle.Instance = ADC_ADCX_DMASx;                             /* 设置DMA数据流 */
    g_dma_adc_handle.Init.Channel = DMA_CHANNEL_0;                          /* 设置DMA通道 */
    g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;                 /* 从外设到存储器模式 */
    g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;                     /* 外设非增量模式 */
    g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;                         /* 存储器增量模式 */
    g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    /* 外设数据长度:16位 */
    g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;       /* 存储器数据长度:16位 */
    g_dma_adc_handle.Init.Mode = DMA_CIRCULAR;                              /* 循环模式 */
    g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;                   /* 中等优先级 */
    HAL_DMA_Init(&g_dma_adc_handle);
 
    __HAL_LINKDMA(&g_adc_dma_handle,DMA_Handle,g_dma_adc_handle);
    
    g_adc_dma_handle.Instance = ADC_ADCX;
    g_adc_dma_handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;                /* 4分频,21Mhz */
    g_adc_dma_handle.Init.Resolution = ADC_RESOLUTION_12B;                          /* 12位模式 */
    g_adc_dma_handle.Init.ScanConvMode = DISABLE;                                   /* 非扫描模式  */
    g_adc_dma_handle.Init.ContinuousConvMode = DISABLE;                             /* 关闭连续转换 */
    g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;                          /* 禁止不连续采样模式 */
    g_adc_dma_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;   /* 触发源 */
    g_adc_dma_handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;          /* 定时器触发 */
    g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                          /* 右对齐 */
    g_adc_dma_handle.Init.NbrOfConversion = 1;                                      /* 本实验用到1个规则通道序列 */
    g_adc_dma_handle.Init.DMAContinuousRequests = ENABLE;                           /* 开启DMA请求 */
    HAL_ADC_Init(&g_adc_dma_handle);
    
    /* 配置ADC通道 */
    adc_ch_conf.Channel = ADC_ADCX_CHY;                                             /* 通道 */
    adc_ch_conf.Rank = 1;                                                           /* 序列 */
    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_480CYCLES;                            /* 采样时间 */
    HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf);                         /* 通道配置 */

    /* 配置DMA数据流请求中断优先级 */
    HAL_NVIC_SetPriority(ADC_ADCX_DMASx_IRQn, 3, 3);
    HAL_NVIC_EnableIRQ(ADC_ADCX_DMASx_IRQn);
    
    HAL_ADC_Start_DMA(&g_adc_dma_handle,(uint32_t *)g_adc_value,1);
    __HAL_DMA_ENABLE_IT(&g_dma_adc_handle, DMA_IT_TC);                              /* 使能传输完成中断 */
}
void adc_dma_enable(uint16_t cndtr)
{
    __HAL_ADC_DISABLE(&g_adc_dma_handle);           /* 先关闭ADC */
    
    __HAL_DMA_DISABLE(&g_dma_adc_handle);           /* 关闭DMA传输 */
    g_dma_adc_handle.Instance->NDTR = cndtr;        /* 重设DMA传输数据量 */
    __HAL_DMA_ENABLE(&g_dma_adc_handle);            /* 开启DMA传输 */
    
    __HAL_ADC_ENABLE(&g_adc_dma_handle);            /* 重新启动ADC */
    ADC_ADCX->CR2 |= 1 << 30;                       /* 启动规则转换通道 */
}
void ADC_ADCX_DMASx_IRQHandler(void)
{
    if (ADC_ADCX_DMASx_IS_TC())
    {
        g_adc_dma_sta = 1;                          /* 标记DMA传输完成 */
        ADC_ADCX_DMASx_CLR_TC();                    /* 清除DMA2 数据流4 传输完成中断 */
    }
}
void gtim_timx_int_init(uint16_t arr, uint16_t psc)
{
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    
    GTIM_TIMX_INT_CLK_ENABLE();                                 /* 使能TIMx时钟 */

    g_timx_handle.Instance = GTIM_TIMX_INT;                     /* 通用定时器x */
    g_timx_handle.Init.Prescaler = psc;                         /* 预分频系数 */
    g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        /* 递增计数模式 */
    g_timx_handle.Init.Period = arr;                            /* 自动装载值 */
    HAL_TIM_Base_Init(&g_timx_handle);

    /* 定时器中断触发ADC采集配置 */
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;        /* 每次更新事件触发一次转换 */
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&g_timx_handle, &sMasterConfig);
    
    HAL_NVIC_SetPriority(GTIM_TIMX_INT_IRQn, 1, 3);             /* 设置中断优先级,抢占优先级1,子优先级3 */
    HAL_NVIC_EnableIRQ(GTIM_TIMX_INT_IRQn);                     /* 开启ITMx中断 */

    HAL_TIM_Base_Start_IT(&g_timx_handle);                      /* 使能定时器x和定时器x更新中断 */
}

void GTIM_TIMX_INT_IRQHandler(void)
{
    __HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE);          /* 清除标志位,不做其他处理 */
}

测试结果:

在ADC DMA和定时器中断里增加打印

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐