ADC(二):外部触发

news2025/3/14 0:40:29

有关ADC的基础知识请参考标准库入门教程

ADC(二):外部触发

  • 1、TIM1的CC1事件触发ADC1+DMA重装载
  • 2、TIM3的TRGO事件(的更新事件)触发ADC1+DMA重装载
  • 3、TIM3的TRGO事件(的捕获事件)触发ADC1+DMA重装载
  • 4、优化TIM3的TRGO事件(的捕获事件)触发ADC1+DMA重装载
  • 5、外部中断EXTI11触发ADC1+DMA重装载
  • 6、外部中断EXTI11触发ADC1+DMA重装载+间断模式

在这里插入图片描述【注意】触发ADC的CC事件只包括OC(输出比较),不包括IC(输入捕获),需要输入捕获触发ADC,则使用TRGO事件。
【注意】使用外部触发时,一般关闭ADC的连续转换模式,使用单次转换

1、TIM1的CC1事件触发ADC1+DMA重装载

将TIM1的CC1事件配置为PWM模式1,且PWM周期为1s。则ADC1每1s触发一次。ADC1触发10次后,计数平均值,输出打印电压值。
①ADC.c文件的代码如下

#include "ADC.h"

uint16_t ADC1_Buffer[10] = {0};             //数据缓冲区
/**
 * @brief:ADC1初始化函数
 */
RCC_PeriphCLKInitTypeDef PeriphClkInit; //ADC时钟配置结构体
ADC_HandleTypeDef hadc1;                //ADC1配置结构体
ADC_ChannelConfTypeDef sConfig1;         //规则组通道配置结构体
void ADC_Init(void)
{
    /* 1、ADC的时钟配置 < 14MHz  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; //需要配置的外设:ADC
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;    //设置ADC分频值:6分频 = 12MHz
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
    
    /* 2、使能ADC1的时钟 */
    __HAL_RCC_ADC1_CLK_ENABLE();
    
    /* 3、ADC1的初始化 */
    hadc1.Instance = ADC1;                              //选择ADC1
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;         //数据对齐方式:右对齐
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;         //扫描模式:关闭扫描(仅一个通道)
    hadc1.Init.ContinuousConvMode = DISABLE;            //转换模式:单次转换
  //hadc1.Init.NbrOfConversion =                        //扫描模式下规则组的数量
  //hadc1.Init.DiscontinuousConvMode =                  //扫描模式规则组间断模式
  //hadc1.Init.NbrOfDiscConversion =                    //规则组间断模式下每次间断的通道数
    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;   //ADC触发:TIM1的CC1事件触发
    HAL_ADC_Init(&hadc1);
    
    /* 4、规则组通道配置 */
    sConfig1.Channel = ADC_CHANNEL_0;                    //通道选择:通道0
    sConfig1.Rank = ADC_REGULAR_RANK_1;                  //规则组盒子序号:1号
    sConfig1.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;   //采样时间:41.5T
    HAL_ADC_ConfigChannel(&hadc1, &sConfig1);
    
    /* 5、ADC1校准 */
    HAL_ADCEx_Calibration_Start(&hadc1);                //ADC1自校准
    
    /* 6、开启ADC规则组转换 */
    HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_Buffer,10);//开启转换:DMA方式,转换完成DMA执行搬运,
                                                        //开启了DMA中断,搬运10后执行DMA中断
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,3,0);       //配置优先级
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);             //使能中断源
}

/**
 * @brief:HAL_ADC_Init()调用函数
 */
DMA_HandleTypeDef hdma1_ADC1;               //ADC1的DMA1的通道配置结构体
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    GPIO_InitTypeDef GPIO_Init;             //IO口配置的结构体
    if(hadc->Instance == ADC1)
    {
        /* 配置ADC1通道0:PA0 */
        __HAL_RCC_GPIOA_CLK_ENABLE();       //使能GPIOA时钟
        GPIO_Init.Pin = GPIO_PIN_0;         //选择PA0
        GPIO_Init.Mode = GPIO_MODE_ANALOG;  //选择模拟输入模式
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        /* 1、使能DMA1的时钟 */
        __HAL_RCC_DMA1_CLK_ENABLE();                                    //使能DMA1的时钟

        /* 2、初始化DMA1的通道1配置 */
        hdma1_ADC1.Instance = DMA1_Channel1;                            //选择DMA1的通道1
        hdma1_ADC1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设站点数据宽度:16位
        hdma1_ADC1.Init.PeriphInc = DMA_PINC_DISABLE;                   //外设地址是否递增:选择不自增
        hdma1_ADC1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //内存站点数据宽度:16位
        hdma1_ADC1.Init.MemInc = DMA_MINC_ENABLE;                       //内存地址是否递增:选择自增
        hdma1_ADC1.Init.Direction = DMA_PERIPH_TO_MEMORY;               //传输方向:这里选择外设---->内存
        hdma1_ADC1.Init.Mode = DMA_CIRCULAR;                            //计数器传输模式:自动重装
        hdma1_ADC1.Init.Priority = DMA_PRIORITY_MEDIUM;                 //通道1传输优先级:选择中等
        __HAL_LINKDMA(&hadc1,DMA_Handle,hdma1_ADC1);                    //ADC1和DMA1构建链接
        HAL_DMA_Init(&hdma1_ADC1);
    }
    else if(hadc->Instance == ADC2)
    {
        
    }    
}

②ADC.h文件的代码如下

#ifndef __ADC_H
#define __ADC_H

#include "stm32f1xx_hal.h"
extern uint16_t ADC1_Buffer[10];
extern ADC_HandleTypeDef hadc1;                //ADC1配置结构体
extern DMA_HandleTypeDef hdma1_ADC1;           //ADC1的DMA1的通道配置结构体
void ADC_Init(void);

#endif

③Time.c文件的代码如下

#include "Time.h"

/**
 * 定时器TIM1的配置
 * psc: 时钟预分频数(1~65536)
 * arr: 自动重装值(1~65536)
 * rep: 重复次数(0为不重复,1为重复一次,用于高级定时器)
 */
TIM_HandleTypeDef htim1_OC1;                                //TIM1输出比较的时基单元配置结构体
TIM_OC_InitTypeDef sConfig;                                 //TIM1输出比较的通道配置结构体
void TIM1_Init(uint16_t psc, uint16_t arr, uint8_t rep)
{
    /* 1、使能TIM1的时钟 */
    __HAL_RCC_TIM1_CLK_ENABLE();
    
    /* 2、配置TIM1的时基单元 */
    htim1_OC1.Instance = TIM1;                              //选择定时器TIM1
    htim1_OC1.Init.Prescaler = psc - 1;                     //预分频器
    htim1_OC1.Init.CounterMode = TIM_COUNTERMODE_UP;        //向上计数
    htim1_OC1.Init.Period = arr - 1;                        //自动重装载值
    htim1_OC1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;  //时钟分频因子,用于防干扰,与定时无关
    htim1_OC1.Init.RepetitionCounter = rep;                 //重复计数器
    htim1_OC1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //禁止预加载
    HAL_TIM_OC_Init(&htim1_OC1);
    __HAL_TIM_CLEAR_FLAG(&htim1_OC1,TIM_FLAG_UPDATE);       //清除更新标志位
        
    /* 3、TIM1的OC1配置 */
    sConfig.OCMode = TIM_OCMODE_PWM1;                       //规则模式:PWM1模式(小于比较值输出有效电平)
    sConfig.Pulse = 1000;                                   //比较值:1000
    sConfig.OCPolarity = TIM_OCPOLARITY_LOW;                //有效电平极性:低电平
    HAL_TIM_OC_ConfigChannel(&htim1_OC1, &sConfig, TIM_CHANNEL_1);
    
    /* 4、启动TIM1 */
    HAL_TIM_OC_Start_IT(&htim1_OC1, TIM_CHANNEL_1);             //启动TIM1,开启匹配中断
    HAL_NVIC_SetPriority(TIM1_CC_IRQn,3,0);
    HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);
}

/**
 * @brief:HAL_TIM_OC_Init()调用函数
 */
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_Init;                     //IO口配置的结构体
    if (htim->Instance == TIM1)                   
    {     
        /* TIM1的CH1的引脚的PA8:输入 */
        __HAL_RCC_GPIOA_CLK_ENABLE();               //使能GPIOA时钟
        GPIO_Init.Pin = GPIO_PIN_8;                 //选择PA8
        GPIO_Init.Mode = GPIO_MODE_AF_PP;           //输出模式:复用推挽
        GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM;   //最大输出速度:中等
        HAL_GPIO_Init(GPIOA,&GPIO_Init);
    }else if(htim->Instance == TIM2)
    {
    
    }
}

④Time.h文件的代码如下

#ifndef __Time_H
#define __Time_H

#include "stm32f1xx_hal.h"
extern TIM_HandleTypeDef htim1_OC1;//TIM1输出比较的时基单元配置结构体
void TIM1_Init(uint16_t psc, uint16_t arr, uint8_t rep);

#endif

⑤main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "ADC.h"
#include "Time.h"
#include "UART.h"
 
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    TIM1_Init(7200, 10000, 0);//0.1ms计数一次,周期为1s,即:PWM1s产生一次上升沿
    ADC_Init();
    
	printf("启动判断!\r\n");
    
	while(1){
       
	}	
}

⑥stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "ADC.h" 
#include "Time.h" 
#include "UART.h"

/**
 * @brief:TIM1的CC的中断服务函数
 */
void TIM1_CC_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim1_OC1);//定时器中断服务总函数
}
/**
 * @brief:DMA通道1中断服务函数
 */
void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma1_ADC1);//DMA中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:TIM1输出比较匹配中断服务函数
 */
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM1)
    {
        if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
        {
            printf("匹配成功,产生上升沿!\r\n");
        }else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
        {
            
        }
    }else if(htim->Instance == TIM2)
    {
    
    }
}

/**
 * @brief:DMA通道1传输完成的中断服务函数
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    float ADC_Value = 0;
    uint16_t sum = 0;
    if(hadc->Instance == ADC1)
    {
        for(uint8_t i = 0; i<10; i++)
        {
            sum += ADC1_Buffer[i];
        }
        printf("结果寄存器数值为:%d\r\n",sum/10);
        ADC_Value = (sum/10) * 3.3 / 4095;            //将二进制计数为电压电压值
        printf("电压值 = %f\r\n",ADC_Value);          //输出电压值
    }else if(hadc->Instance == ADC2)
    {
    
    }
}

在这里插入图片描述

2、TIM3的TRGO事件(的更新事件)触发ADC1+DMA重装载

将定时器TIM3配置为主模式,且能够使TRGO产生信号的情况如下:
一般触发ADC:选择更新事件和比较脉冲的输出捕获事件
在这里插入图片描述实验要求:选择TIM3主模式的更新事件产生TRGO信号来触发ADC1的转换
①ADC.c文件的代码如下

#include "ADC.h"

uint16_t ADC1_Buffer[10] = {0};             //数据缓冲区
/**
 * @brief:ADC1初始化函数
 */
RCC_PeriphCLKInitTypeDef PeriphClkInit; //ADC时钟配置结构体
ADC_HandleTypeDef hadc1;                //ADC1配置结构体
ADC_ChannelConfTypeDef sConfig1;         //规则组通道配置结构体
void ADC_Init(void)
{
    /* 1、ADC的时钟配置 < 14MHz  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; //需要配置的外设:ADC
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;    //设置ADC分频值:6分频 = 12MHz
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
    
    /* 2、使能ADC1的时钟 */
    __HAL_RCC_ADC1_CLK_ENABLE();
    
    /* 3、ADC1的初始化 */
    hadc1.Instance = ADC1;                              //选择ADC1
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;         //数据对齐方式:右对齐
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;         //扫描模式:关闭扫描(仅一个通道)
    hadc1.Init.ContinuousConvMode = DISABLE;            //转换模式:单次转换
  //hadc1.Init.NbrOfConversion =                        //扫描模式下规则组的数量
  //hadc1.Init.DiscontinuousConvMode =                  //扫描模式规则组间断模式
  //hadc1.Init.NbrOfDiscConversion =                    //规则组间断模式下每次间断的通道数
    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;   //ADC触发:TIM3的TRGO事件触发
    HAL_ADC_Init(&hadc1);
    
    /* 4、规则组通道配置 */
    sConfig1.Channel = ADC_CHANNEL_0;                    //通道选择:通道0
    sConfig1.Rank = ADC_REGULAR_RANK_1;                  //规则组盒子序号:1号
    sConfig1.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;   //采样时间:41.5T
    HAL_ADC_ConfigChannel(&hadc1, &sConfig1);
    
    /* 5、ADC1校准 */
    HAL_ADCEx_Calibration_Start(&hadc1);                //ADC1自校准
    
    /* 6、开启ADC规则组转换 */
    HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_Buffer,10);//开启转换:DMA方式,转换完成DMA执行搬运,
                                                        //开启了DMA中断,搬运10后执行DMA中断
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,3,0);       //配置优先级
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);             //使能中断源
}

/**
 * @brief:HAL_ADC_Init()调用函数
 */
DMA_HandleTypeDef hdma1_ADC1;               //ADC1的DMA1的通道配置结构体
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    GPIO_InitTypeDef GPIO_Init;             //IO口配置的结构体
    if(hadc->Instance == ADC1)
    {
        /* 配置ADC1通道0:PA0 */
        __HAL_RCC_GPIOA_CLK_ENABLE();       //使能GPIOA时钟
        GPIO_Init.Pin = GPIO_PIN_0;         //选择PA0
        GPIO_Init.Mode = GPIO_MODE_ANALOG;  //选择模拟输入模式
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        /* 1、使能DMA1的时钟 */
        __HAL_RCC_DMA1_CLK_ENABLE();                                    //使能DMA1的时钟

        /* 2、初始化DMA1的通道1配置 */
        hdma1_ADC1.Instance = DMA1_Channel1;                            //选择DMA1的通道1
        hdma1_ADC1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设站点数据宽度:16位
        hdma1_ADC1.Init.PeriphInc = DMA_PINC_DISABLE;                   //外设地址是否递增:选择不自增
        hdma1_ADC1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //内存站点数据宽度:16位
        hdma1_ADC1.Init.MemInc = DMA_MINC_ENABLE;                       //内存地址是否递增:选择自增
        hdma1_ADC1.Init.Direction = DMA_PERIPH_TO_MEMORY;               //传输方向:这里选择外设---->内存
        hdma1_ADC1.Init.Mode = DMA_CIRCULAR;                            //计数器传输模式:自动重装
        hdma1_ADC1.Init.Priority = DMA_PRIORITY_MEDIUM;                 //通道1传输优先级:选择中等
        __HAL_LINKDMA(&hadc1,DMA_Handle,hdma1_ADC1);                    //ADC1和DMA1构建链接
        HAL_DMA_Init(&hdma1_ADC1);
    }
    else if(hadc->Instance == ADC2)
    {
        
    }    
}

②ADC.h文件的代码如下

#ifndef __ADC_H
#define __ADC_H

#include "stm32f1xx_hal.h"
extern uint16_t ADC1_Buffer[10];
extern ADC_HandleTypeDef hadc1;                //ADC1配置结构体
extern DMA_HandleTypeDef hdma1_ADC1;           //ADC1的DMA1的通道配置结构体
void ADC_Init(void);

#endif

③Time.c文件的代码如下

#include "Time.h"

/**
 * 定时器TIM3的配置
 * psc: 时钟预分频数(1~65536)
 * arr: 自动重装值(1~65536)
 */
TIM_HandleTypeDef htim3;                                //TIM3时基单元配置结构体
TIM_MasterConfigTypeDef sMasterConfig;                  //主模式配置结构体
void TIM3_Init(uint16_t psc, uint16_t arr)
{
    /* 1、使能TIM3的时钟 */
    __HAL_RCC_TIM3_CLK_ENABLE();
    
    /* 2、配置TIM3的时基单元 */
    htim3.Instance = TIM3;                              //选择定时器TIM3
    htim3.Init.Prescaler = psc - 1;                     //预分频器
    htim3.Init.CounterMode = TIM_COUNTERMODE_UP;        //向上计数
    htim3.Init.Period = arr - 1;                        //自动重装载值
    htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;  //时钟分频因子,用于防干扰,与定时无关
    htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //禁止预加载
    HAL_TIM_Base_Init(&htim3);
    __HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_UPDATE);       //清除更新标志位
        
    /* 3、TIM3的主模式的配置 */
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;//触发TRGO信号事件:更新事件
    HAL_TIMEx_MasterConfigSynchronization(&htim3,&sMasterConfig);
    
    /* 4、启动TIM3 */
    HAL_TIM_Base_Start_IT(&htim3);                      //启动TIM3,开启更新中断
    HAL_NVIC_SetPriority(TIM3_IRQn,3,0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
}

/**
 * @brief:HAL_TIM_Base_Init()调用函数
 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM1)                   
    {     

    }else if(htim->Instance == TIM2)
    {
    
    }else if(htim->Instance == TIM3)
    {
    
    }
}

④Time.h文件的代码如下

#ifndef __Time_H
#define __Time_H

#include "stm32f1xx_hal.h"
extern TIM_HandleTypeDef htim3;                         //TIM3输出比较的时基单元配置结构体
void TIM3_Init(uint16_t psc, uint16_t arr);

#endif

⑤main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "ADC.h"
#include "Time.h"
#include "UART.h"
 
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    TIM3_Init(7200, 10000);//0.1ms计数一次,周期为1s,即:1s产生一次更新
    ADC_Init();
    
	printf("启动判断!\r\n");
    
	while(1){
       
	}	
}

⑥stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "ADC.h" 
#include "Time.h" 
#include "UART.h"

/**
 * @brief:TIM3中断服务函数
 */
void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim3);//中断服务总函数
}
/**
 * @brief:DMA通道1中断服务函数
 */
void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma1_ADC1);//DMA中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:TIM3更新事件中断服务函数
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM3)
    {
        printf("TIM3更新了,产生TRGO信号了!\r\n");
    }
}
/**
 * @brief:DMA通道1传输完成的中断服务函数
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    float ADC_Value = 0;
    uint16_t sum = 0;
    if(hadc->Instance == ADC1)
    {
        for(uint8_t i = 0; i<10; i++)
        {
            sum += ADC1_Buffer[i];
        }
        printf("结果寄存器数值为:%d\r\n",sum/10);
        ADC_Value = (sum/10) * 3.3 / 4095;            //将二进制计数为电压电压值
        printf("电压值 = %f\r\n",ADC_Value);          //输出电压值
    }else if(hadc->Instance == ADC2)
    {
    
    }
}

在这里插入图片描述

3、TIM3的TRGO事件(的捕获事件)触发ADC1+DMA重装载

【注意】只有定时器的CC1捕获才能触发TRGO信号,所以TIM3的CC1通道引脚是PA6,而ADC1/ADC2的采样通道6的引脚也是PA6。即若只用ADC的通道6进行采样,且由TIM3的输入捕获进行触发转换,则将TIM3的输入捕获引脚进行重映射到PB4即可。
【注意】在使用PB4是需要释放JTAG
①ADC.c文件的代码如下

#include "ADC.h"

uint16_t ADC1_Buffer[10] = {0};             //数据缓冲区
/**
 * @brief:ADC1初始化函数
 */
RCC_PeriphCLKInitTypeDef PeriphClkInit; //ADC时钟配置结构体
ADC_HandleTypeDef hadc1;                //ADC1配置结构体
ADC_ChannelConfTypeDef sConfig1;         //规则组通道配置结构体
void ADC_Init(void)
{
    /* 1、ADC的时钟配置 < 14MHz  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; //需要配置的外设:ADC
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;    //设置ADC分频值:6分频 = 12MHz
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
    
    /* 2、使能ADC1的时钟 */
    __HAL_RCC_ADC1_CLK_ENABLE();
    
    /* 3、ADC1的初始化 */
    hadc1.Instance = ADC1;                              //选择ADC1
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;         //数据对齐方式:右对齐
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;         //扫描模式:关闭扫描(仅一个通道)
    hadc1.Init.ContinuousConvMode = DISABLE;            //转换模式:单次转换
  //hadc1.Init.NbrOfConversion =                        //扫描模式下规则组的数量
  //hadc1.Init.DiscontinuousConvMode =                  //扫描模式规则组间断模式
  //hadc1.Init.NbrOfDiscConversion =                    //规则组间断模式下每次间断的通道数
    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;   //ADC触发:TIM3的TRGO事件触发
    HAL_ADC_Init(&hadc1);
    
    /* 4、规则组通道配置 */
    sConfig1.Channel = ADC_CHANNEL_0;                    //通道选择:通道0
    sConfig1.Rank = ADC_REGULAR_RANK_1;                  //规则组盒子序号:1号
    sConfig1.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;   //采样时间:41.5T
    HAL_ADC_ConfigChannel(&hadc1, &sConfig1);
    
    /* 5、ADC1校准 */
    HAL_ADCEx_Calibration_Start(&hadc1);                //ADC1自校准
    
    /* 6、开启ADC规则组转换 */
    HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_Buffer,10);//开启转换:DMA方式,转换完成DMA执行搬运,
                                                        //开启了DMA中断,搬运10后执行DMA中断
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,3,0);       //配置优先级
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);             //使能中断源
}

/**
 * @brief:HAL_ADC_Init()调用函数
 */
DMA_HandleTypeDef hdma1_ADC1;               //ADC1的DMA1的通道配置结构体
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    GPIO_InitTypeDef GPIO_Init;             //IO口配置的结构体
    if(hadc->Instance == ADC1)
    {
        /* 配置ADC1通道0:PA0 */
        __HAL_RCC_GPIOA_CLK_ENABLE();       //使能GPIOA时钟
        GPIO_Init.Pin = GPIO_PIN_0;         //选择PA0
        GPIO_Init.Mode = GPIO_MODE_ANALOG;  //选择模拟输入模式
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        /* 1、使能DMA1的时钟 */
        __HAL_RCC_DMA1_CLK_ENABLE();                                    //使能DMA1的时钟

        /* 2、初始化DMA1的通道1配置 */
        hdma1_ADC1.Instance = DMA1_Channel1;                            //选择DMA1的通道1
        hdma1_ADC1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设站点数据宽度:16位
        hdma1_ADC1.Init.PeriphInc = DMA_PINC_DISABLE;                   //外设地址是否递增:选择不自增
        hdma1_ADC1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //内存站点数据宽度:16位
        hdma1_ADC1.Init.MemInc = DMA_MINC_ENABLE;                       //内存地址是否递增:选择自增
        hdma1_ADC1.Init.Direction = DMA_PERIPH_TO_MEMORY;               //传输方向:这里选择外设---->内存
        hdma1_ADC1.Init.Mode = DMA_CIRCULAR;                            //计数器传输模式:自动重装
        hdma1_ADC1.Init.Priority = DMA_PRIORITY_MEDIUM;                 //通道1传输优先级:选择中等
        __HAL_LINKDMA(&hadc1,DMA_Handle,hdma1_ADC1);                    //ADC1和DMA1构建链接
        HAL_DMA_Init(&hdma1_ADC1);
    }
    else if(hadc->Instance == ADC2)
    {
        
    }    
}

②ADC.h文件的代码如下

#ifndef __ADC_H
#define __ADC_H

#include "stm32f1xx_hal.h"
extern uint16_t ADC1_Buffer[10];
extern ADC_HandleTypeDef hadc1;                //ADC1配置结构体
extern DMA_HandleTypeDef hdma1_ADC1;           //ADC1的DMA1的通道配置结构体
void ADC_Init(void);

#endif

③Time.c文件的代码如下

#include "Time.h"

/**
 * 定时器TIM3的配置
 * psc: 时钟预分频数(1~65536)
 * arr: 自动重装值(1~65536)
 */
TIM_HandleTypeDef htim3_IC;                             //TIM3输入捕获时基单元配置结构体
TIM_MasterConfigTypeDef sMasterConfig;                  //主模式配置结构体
TIM_IC_InitTypeDef sConfig_IC;                          //捕获通道配置结构体
void TIM3_Init(uint16_t psc, uint16_t arr)
{
    /* 1、使能TIM3的时钟 */
    __HAL_RCC_TIM3_CLK_ENABLE();
    
    /* 2、配置TIM3的时基单元 */
    htim3_IC.Instance = TIM3;                              //选择定时器TIM3
    htim3_IC.Init.Prescaler = psc - 1;                     //预分频器
    htim3_IC.Init.CounterMode = TIM_COUNTERMODE_UP;        //向上计数
    htim3_IC.Init.Period = arr - 1;                        //自动重装载值
    htim3_IC.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;  //时钟分频因子,用于防干扰,与定时无关
    htim3_IC.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //禁止预加载
    HAL_TIM_IC_Init(&htim3_IC);
    __HAL_TIM_CLEAR_FLAG(&htim3_IC,TIM_FLAG_UPDATE);       //清除更新标志位
    
    /* 3、TIM3输出捕获通道1的配置 */
    sConfig_IC.ICPolarity = TIM_ICPOLARITY_RISING;      //捕获边沿:上升沿
    sConfig_IC.ICSelection = TIM_ICSELECTION_DIRECTTI;  //输入对应方式:直接连接
    sConfig_IC.ICPrescaler = TIM_ICPSC_DIV1;            //捕获分频:1分频
    sConfig_IC.ICFilter = 0x8;                          //滤波
    HAL_TIM_IC_ConfigChannel(&htim3_IC, &sConfig_IC, TIM_CHANNEL_1);
        
    /* 4、TIM3的主模式的配置 */
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1;   //触发TRGO信号事件:捕获事件
    HAL_TIMEx_MasterConfigSynchronization(&htim3_IC,&sMasterConfig);
    
    /* 4、启动TIM1 */
    HAL_TIM_IC_Start_IT(&htim3_IC,TIM_CHANNEL_1);       //启动TIM3,开启捕获中断
    HAL_NVIC_SetPriority(TIM3_IRQn,3,0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
}

/**
 * @brief:HAL_TIM_IC_Init()调用函数
 */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_Init;             //IO口配置结构体
    if (htim->Instance == TIM1)                   
    {     

    }else if(htim->Instance == TIM2)
    {
    
    }
    else if(htim->Instance == TIM3)
    {
        /* TIM3的CC1的引脚为PA6,将引脚重映射到PB4 */
        __HAL_RCC_AFIO_CLK_ENABLE();        //使能AFIO的时钟
        __HAL_AFIO_REMAP_TIM3_PARTIAL();    //TIM3引脚部分重映射
        __HAL_RCC_GPIOB_CLK_ENABLE();       //开启GPIOB时钟
        __HAL_AFIO_REMAP_SWJ_NOJTAG();      //关闭JTAG,释放PB4
        
        /* 配置PB4 */
        GPIO_Init.Mode = GPIO_MODE_INPUT;   //模式:输入模式
        GPIO_Init.Pin = GPIO_PIN_4;         //选择PB4
        GPIO_Init.Pull = GPIO_PULLDOWN;     //下拉输入
        HAL_GPIO_Init(GPIOB, &GPIO_Init);
    }
}

④Time.h文件的代码如下

#ifndef __Time_H
#define __Time_H

#include "stm32f1xx_hal.h"
extern TIM_HandleTypeDef htim3_IC;                         //TIM3输出比较的时基单元配置结构体
void TIM3_Init(uint16_t psc, uint16_t arr);

#endif

⑤main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "ADC.h"
#include "Time.h"
#include "UART.h"
 
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    TIM3_Init(7200, 10000);//0.1ms计数一次,周期为1s,即:1s产生一次更新
    ADC_Init();
    
	printf("启动判断!\r\n");
    
	while(1){
       
	}	
}

⑥stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "ADC.h" 
#include "Time.h" 
#include "UART.h"

/**
 * @brief:TIM3中断服务函数
 */
void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim3_IC);//中断服务总函数
}
/**
 * @brief:DMA通道1中断服务函数
 */
void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma1_ADC1);//DMA中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:TIM3捕获事件中断服务函数
 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM3)
    {
        printf("TIM3捕获了,产生TRGO信号了!\r\n");
    }
}

/**
 * @brief:DMA通道1传输完成的中断服务函数
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    float ADC_Value = 0;
    uint16_t sum = 0;
    if(hadc->Instance == ADC1)
    {
        for(uint8_t i = 0; i<10; i++)
        {
            sum += ADC1_Buffer[i];
        }
        printf("结果寄存器数值为:%d\r\n",sum/10);
        ADC_Value = (sum/10) * 3.3 / 4095;            //将二进制计数为电压电压值
        printf("电压值 = %f\r\n",ADC_Value);          //输出电压值
    }else if(hadc->Instance == ADC2)
    {
    
    }
}

在这里插入图片描述

4、优化TIM3的TRGO事件(的捕获事件)触发ADC1+DMA重装载

案列3需要按下按键10次,才会输出测量到的结果。下面进行优化,只需按键按下1次,就会输出测量结果,但是输出的结果依然是10次平均滤波得到的。
修改的代码如下:
①ADC.c文件需要修改的代码如下
开启ADC连续转换模式

hadc1.Init.ContinuousConvMode = ENABLE;             //转换模式:连续转换

②stm32f1xx_it.c文件需要修改的代码如下
在触发中断服务函数中关闭连续转换,ADC采集速度>代码执行到关闭连续转换的速度
在DMA完成中断服务函数中开启连续转换

/**
 * @brief:TIM3捕获事件中断服务函数
 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM3)
    {
        printf("TIM3捕获了,产生TRGO信号了!\r\n");
        ADC1->CR2 &= ~ADC_CR2_CONT; //关闭连续转换
    }
}

/**
 * @brief:DMA通道1传输完成的中断服务函数
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    float ADC_Value = 0;
    uint16_t sum = 0;
    if(hadc->Instance == ADC1)
    {
        for(uint8_t i = 0; i<10; i++)
        {
            sum += ADC1_Buffer[i];
        }
        printf("结果寄存器数值为:%d\r\n",sum/10);
        ADC_Value = (sum/10) * 3.3 / 4095;            //将二进制计数为电压电压值
        printf("电压值 = %f\r\n",ADC_Value);          //输出电压值
        ADC1->CR2 |= ADC_CR2_CONT;                    //开启连续转换
    }else if(hadc->Instance == ADC2)
    {
    
    }
}

在这里插入图片描述

5、外部中断EXTI11触发ADC1+DMA重装载

①ADC.c文件的代码如下

#include "ADC.h"

uint16_t ADC1_Buffer[10] = {0};             //数据缓冲区
/**
 * @brief:ADC1初始化函数
 */
RCC_PeriphCLKInitTypeDef PeriphClkInit; //ADC时钟配置结构体
ADC_HandleTypeDef hadc1;                //ADC1配置结构体
ADC_ChannelConfTypeDef sConfig1;         //规则组通道配置结构体
void ADC_Init(void)
{
    /* 1、ADC的时钟配置 < 14MHz  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; //需要配置的外设:ADC
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;    //设置ADC分频值:6分频 = 12MHz
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
    
    /* 2、使能ADC1的时钟 */
    __HAL_RCC_ADC1_CLK_ENABLE();
    
    /* 3、ADC1的初始化 */
    hadc1.Instance = ADC1;                              //选择ADC1
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;         //数据对齐方式:右对齐
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;         //扫描模式:关闭扫描(仅一个通道)
    hadc1.Init.ContinuousConvMode = ENABLE;             //转换模式:连续转换
  //hadc1.Init.NbrOfConversion =                        //扫描模式下规则组的数量
  //hadc1.Init.DiscontinuousConvMode =                  //扫描模式规则组间断模式
  //hadc1.Init.NbrOfDiscConversion =                    //规则组间断模式下每次间断的通道数
    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_EXT_IT11;   //ADC触发:EXTI11事件触发
    HAL_ADC_Init(&hadc1);
    
    /* 4、规则组通道配置 */
    sConfig1.Channel = ADC_CHANNEL_0;                    //通道选择:通道0
    sConfig1.Rank = ADC_REGULAR_RANK_1;                  //规则组盒子序号:1号
    sConfig1.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;   //采样时间:41.5T
    HAL_ADC_ConfigChannel(&hadc1, &sConfig1);
    
    /* 5、ADC1校准 */
    HAL_ADCEx_Calibration_Start(&hadc1);                //ADC1自校准
    
    /* 6、开启ADC规则组转换 */
    HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_Buffer,10);//开启转换:DMA方式,转换完成DMA执行搬运,
                                                        //开启了DMA中断,搬运10后执行DMA中断
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,3,0);       //配置优先级
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);             //使能中断源
}

/**
 * @brief:HAL_ADC_Init()调用函数
 */
DMA_HandleTypeDef hdma1_ADC1;               //ADC1的DMA1的通道配置结构体
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    GPIO_InitTypeDef GPIO_Init;             //IO口配置的结构体
    if(hadc->Instance == ADC1)
    {
        /* 配置ADC1通道0:PA0 */
        __HAL_RCC_GPIOA_CLK_ENABLE();       //使能GPIOA时钟
        GPIO_Init.Pin = GPIO_PIN_0;         //选择PA0
        GPIO_Init.Mode = GPIO_MODE_ANALOG;  //选择模拟输入模式
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        /* 1、初始化DMA1的通道1配置 */
        __HAL_RCC_DMA1_CLK_ENABLE();                                    //使能DMA1的时钟
        hdma1_ADC1.Instance = DMA1_Channel1;                            //选择DMA1的通道1
        hdma1_ADC1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设站点数据宽度:16位
        hdma1_ADC1.Init.PeriphInc = DMA_PINC_DISABLE;                   //外设地址是否递增:选择不自增
        hdma1_ADC1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //内存站点数据宽度:16位
        hdma1_ADC1.Init.MemInc = DMA_MINC_ENABLE;                       //内存地址是否递增:选择自增
        hdma1_ADC1.Init.Direction = DMA_PERIPH_TO_MEMORY;               //传输方向:这里选择外设---->内存
        hdma1_ADC1.Init.Mode = DMA_CIRCULAR;                            //计数器传输模式:自动重装
        hdma1_ADC1.Init.Priority = DMA_PRIORITY_MEDIUM;                 //通道1传输优先级:选择中等
        __HAL_LINKDMA(&hadc1,DMA_Handle,hdma1_ADC1);                    //ADC1和DMA1构建链接
        HAL_DMA_Init(&hdma1_ADC1);
        
        /* 2、配置EXTI11 */
        __HAL_RCC_AFIO_CLK_ENABLE();            //使能AFIO时钟
        GPIO_Init.Pin = GPIO_PIN_11;            //选择PA11
        GPIO_Init.Mode = GPIO_MODE_IT_RISING;   //选择上升沿触发中断
        GPIO_Init.Pull = GPIO_PULLDOWN;         //选择下拉输入
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        /* 3、配置外部中断EXTI11的NVIC*/
        HAL_NVIC_SetPriority(EXTI15_10_IRQn,3,0);
        HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
        
    }
    else if(hadc->Instance == ADC2)
    {
        
    }    
}

②ADC.h文件的代码如下

#ifndef __ADC_H
#define __ADC_H

#include "stm32f1xx_hal.h"
extern uint16_t ADC1_Buffer[10];
extern ADC_HandleTypeDef hadc1;                //ADC1配置结构体
extern DMA_HandleTypeDef hdma1_ADC1;           //ADC1的DMA1的通道配置结构体
void ADC_Init(void);

#endif

③main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "ADC.h"
#include "Time.h"
#include "UART.h"
 
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    ADC_Init();
    
	printf("启动判断!\r\n");
    
	while(1){
       
	}	
}

④stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "ADC.h" 
#include "UART.h"

/**
 * @brief:EXTI11中断服务函数
 */
void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);//外部中断服务总函数
}
/**
 * @brief:DMA通道1中断服务函数
 */
void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma1_ADC1);//DMA中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:外部中断服务函数
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == GPIO_PIN_11)
    {
        printf("按键11按下了!\r\n");
        ADC1->CR2 &= ~ADC_CR2_CONT; //关闭连续转换
    }
}

/**
 * @brief:DMA通道1传输完成的中断服务函数
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    float ADC_Value = 0;
    uint16_t sum = 0;
    if(hadc->Instance == ADC1)
    {
        for(uint8_t i = 0; i<10; i++)
        {
            sum += ADC1_Buffer[i];
        }
        printf("结果寄存器数值为:%d\r\n",sum/10);
        ADC_Value = (sum/10) * 3.3 / 4095;            //将二进制计数为电压电压值
        printf("电压值 = %f\r\n",ADC_Value);          //输出电压值
        ADC1->CR2 |= ADC_CR2_CONT;                    //开启连续转换
    }else if(hadc->Instance == ADC2)
    {
    
    }
}

在这里插入图片描述

6、外部中断EXTI11触发ADC1+DMA重装载+间断模式

间断模式:就是将ADC规则组中的多个通道分成小组,小组转换完成后则需要重新触发,继续转换下一个小组。所以一般为单次扫描模式+DMA重装载
①ADC.c文件的代码如下

#include "ADC.h"

uint16_t ADC1_Buffer[2] = {0};             //数据缓冲区
/**
 * @brief:ADC1初始化函数
 */
RCC_PeriphCLKInitTypeDef PeriphClkInit; //ADC时钟配置结构体
ADC_HandleTypeDef hadc1;                //ADC1配置结构体
ADC_ChannelConfTypeDef sConfig1;        //规则组通道配置结构体
void ADC_Init(void)
{
    /* 1、ADC的时钟配置 < 14MHz  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; //需要配置的外设:ADC
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;    //设置ADC分频值:6分频 = 12MHz
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
    
    /* 2、使能ADC1的时钟 */
    __HAL_RCC_ADC1_CLK_ENABLE();
    
    /* 3、ADC1的初始化 */
    hadc1.Instance = ADC1;                              //选择ADC1
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;         //数据对齐方式:右对齐
    hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;          //扫描模式:关闭扫描(仅一个通道)
    hadc1.Init.ContinuousConvMode = DISABLE;            //转换模式:连续转换
    hadc1.Init.NbrOfConversion = 4;                     //扫描模式下规则组的数量
    hadc1.Init.DiscontinuousConvMode = ENABLE;          //扫描模式规则组间断模式
    hadc1.Init.NbrOfDiscConversion = 2;                 //规则组间断模式下每次间断的通道数:2队为1组
    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_EXT_IT11;   //ADC触发:EXTI11事件触发
    HAL_ADC_Init(&hadc1);
    
    /* 4、规则组通道配置 */
    sConfig1.Channel = ADC_CHANNEL_0;                    //通道选择:通道0
    sConfig1.Rank = ADC_REGULAR_RANK_1;                  //规则组盒子序号:1号
    sConfig1.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;   //采样时间:41.5T
    HAL_ADC_ConfigChannel(&hadc1, &sConfig1);
    
    sConfig1.Channel = ADC_CHANNEL_1;                    //通道选择:通道1
    sConfig1.Rank = ADC_REGULAR_RANK_2;                  //规则组盒子序号:2号
    sConfig1.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;   //采样时间:41.5T
    HAL_ADC_ConfigChannel(&hadc1, &sConfig1);
    
    sConfig1.Channel = ADC_CHANNEL_2;                    //通道选择:通道2
    sConfig1.Rank = ADC_REGULAR_RANK_3;                  //规则组盒子序号:3号
    sConfig1.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;   //采样时间:41.5T
    HAL_ADC_ConfigChannel(&hadc1, &sConfig1);
    
    sConfig1.Channel = ADC_CHANNEL_3;                    //通道选择:通道3
    sConfig1.Rank = ADC_REGULAR_RANK_4;                  //规则组盒子序号:4号
    sConfig1.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;   //采样时间:41.5T
    HAL_ADC_ConfigChannel(&hadc1, &sConfig1);
    
    /* 5、ADC1校准 */
    HAL_ADCEx_Calibration_Start(&hadc1);                //ADC1自校准
    
    /* 6、开启ADC规则组转换 */
    HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_Buffer,2); //开启转换:DMA方式,转换完成DMA执行搬运,
                                                        //开启了DMA中断,搬运2后执行DMA中断
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,3,0);       //配置优先级
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);             //使能中断源
}

/**
 * @brief:HAL_ADC_Init()调用函数
 */
DMA_HandleTypeDef hdma1_ADC1;               //ADC1的DMA1的通道配置结构体
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    GPIO_InitTypeDef GPIO_Init;             //IO口配置的结构体
    if(hadc->Instance == ADC1)
    {
        /* 配置ADC1通道0:PA0,通道1:PA1,通道2:PA2,通道3:PA3 */
        __HAL_RCC_GPIOA_CLK_ENABLE();       //使能GPIOA时钟
        GPIO_Init.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
        GPIO_Init.Mode = GPIO_MODE_ANALOG;  //选择模拟输入模式
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        /* 1、初始化DMA1的通道1配置 */
        __HAL_RCC_DMA1_CLK_ENABLE();                                    //使能DMA1的时钟
        hdma1_ADC1.Instance = DMA1_Channel1;                            //选择DMA1的通道1
        hdma1_ADC1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //外设站点数据宽度:16位
        hdma1_ADC1.Init.PeriphInc = DMA_PINC_DISABLE;                   //外设地址是否递增:选择不自增
        hdma1_ADC1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     //内存站点数据宽度:16位
        hdma1_ADC1.Init.MemInc = DMA_MINC_ENABLE;                       //内存地址是否递增:选择自增
        hdma1_ADC1.Init.Direction = DMA_PERIPH_TO_MEMORY;               //传输方向:这里选择外设---->内存
        hdma1_ADC1.Init.Mode = DMA_CIRCULAR;                            //计数器传输模式:自动重装
        hdma1_ADC1.Init.Priority = DMA_PRIORITY_MEDIUM;                 //通道1传输优先级:选择中等
        __HAL_LINKDMA(&hadc1,DMA_Handle,hdma1_ADC1);                    //ADC1和DMA1构建链接
        HAL_DMA_Init(&hdma1_ADC1);
        
        /* 2、配置EXTI11 */
        __HAL_RCC_AFIO_CLK_ENABLE();            //使能AFIO时钟
        GPIO_Init.Pin = GPIO_PIN_11;            //选择PA11
        GPIO_Init.Mode = GPIO_MODE_IT_RISING;   //选择上升沿触发中断
        GPIO_Init.Pull = GPIO_PULLDOWN;         //选择下拉输入
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        /* 3、配置外部中断EXTI11的NVIC*/
        HAL_NVIC_SetPriority(EXTI15_10_IRQn,3,0);
        HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
        
    }
    else if(hadc->Instance == ADC2)
    {
        
    }    
}

②ADC.h文件的代码如下

#ifndef __ADC_H
#define __ADC_H

#include "stm32f1xx_hal.h"
extern uint16_t ADC1_Buffer[10];
extern ADC_HandleTypeDef hadc1;                //ADC1配置结构体
extern DMA_HandleTypeDef hdma1_ADC1;           //ADC1的DMA1的通道配置结构体
void ADC_Init(void);

#endif

③main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "ADC.h"
#include "Time.h"
#include "UART.h"
 
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    ADC_Init();
    
	printf("启动判断!\r\n");
    
	while(1){
       
	}	
}

④stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "ADC.h" 
#include "UART.h"

/**
 * @brief:EXTI11中断服务函数
 */
void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);//外部中断服务总函数
}
/**
 * @brief:DMA通道1中断服务函数
 */
void DMA1_Channel1_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma1_ADC1);//DMA中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:外部中断服务函数
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == GPIO_PIN_11)
    {
        printf("按键11按下了!\r\n");
    }
}

/**
 * @brief:DMA通道1传输完成的中断服务函数
 */
uint8_t Num = 0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    float ADC_Value = 0;
    if(hadc->Instance == ADC1)
    {
        ADC_Value = ADC1_Buffer[0] * 3.3 / 4095;            //将二进制计数为电压电压值
        printf("通道%d电压值 = %f\r\n",(Num%2)*2 ,ADC_Value);          //输出电压值
        
        ADC_Value = ADC1_Buffer[1] * 3.3 / 4095;            //将二进制计数为电压电压值
        printf("通道%d电压值 = %f\r\n",(Num%2)*2 + 1,ADC_Value);          //输出电压值
        Num++;
    }else if(hadc->Instance == ADC2)
    {
    
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2267073.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【产品应用】一体化无刷电机在旋转等离子喷枪中的应用

在现代工业制造与加工领域&#xff0c;等离子喷枪凭借其高温、高速的等离子射流&#xff0c;能够实现高效的材料表面处理、切割以及焊接等工艺&#xff0c;在众多行业中发挥着关键作用。而一体化无刷电机的应用&#xff0c;更是为等离子喷枪的性能提升和稳定运行注入了强大动力…

ElasticSearch - 深入解析 Elasticsearch Composite Aggregation 的分页与去重机制

文章目录 Pre概述什么是 composite aggregation&#xff1f;基本结构after 参数的作用问题背景&#xff1a;传统分页的重复问题after 的设计理念响应示例 after 如何确保数据不重复核心机制Example步骤 1: 创建测试数据创建索引插入测试数据 步骤 2: 查询第一页结果查询第一页返…

易基因: BS+ChIP-seq揭示DNA甲基化调控非编码RNA(VIM-AS1)抑制肿瘤侵袭性|Exp Mol Med

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 肝细胞癌&#xff08;hepatocellular carcinoma&#xff0c;HCC&#xff09;早期复发仍然是一个具有挑战性的领域&#xff0c;其中涉及的机制尚未完全被理解。尽管微血管侵犯&#xff08…

顶会评测集解读-AlignBench: 大语言模型中文对齐基准

评测集社区 CompssHub 作为司南 OpenCompass大模型评测体系的重要组成部分&#xff0c;致力于简化并加快研究人员和行业人士搜索和使用评测集的过程。评测集社区 CompssHub 目前已收录了学科、推理、知识、代码等12个方向的评测集&#xff0c;欢迎大家探索。 为了将评测集社区…

量子退火与机器学习(1):少量数据求解未知QUBO矩阵,以少见多

文章目录 前言ー、复习QUBO&#xff1a;中药配伍的复杂性1.QUBO 的介入&#xff1a;寻找最佳药材组合 二、难题&#xff1a;QUBO矩阵未知的问题1.为什么这么难&#xff1f; 三、稀疏建模(Sparse Modeling)1. 欠定系统中的稀疏解2. L1和L2的选择&#xff1a; 三、压缩感知算法(C…

Linux应用软件编程-多任务处理(线程)

线程&#xff1a;轻量级的进程&#xff0c;线程的栈区独立&#xff08;8M&#xff09;&#xff0c;与同一进程中的其他线程共用进程的堆区&#xff0c;数据区&#xff0c;文本区。 进程是操作系统资源分配的最小单位&#xff1b;线程是cpu任务调度的最小单位。 1. 线程的创建…

IKAnalyzer分词组件

IKAnalyzer是一个功能强大、易于使用、性能优异的中文分词工具包&#xff0c;适用于各种需要进行中文分词的场景&#xff0c;如搜索引擎、自然语言处理、文本挖掘等。 Springboot如何集成IKAnalyzer分词组件 引入IKAnalyzer分词组件包 <dependency><groupId>org…

微信小程序 不同角色进入不同页面、呈现不同底部导航栏

遇到这个需求之前一直使用的小程序默认底部导航栏&#xff0c;且小程序默认入口页面为pages/index/index&#xff0c;要使不同角色呈现不同底部导航栏&#xff0c;必须要在不同页面引用不同的自定义导航栏。本篇将结合分包&#xff08;subPackages&#xff09;展开以下三步叙述…

输入框去掉角标

前言 正常情况下&#xff0c;HTML textarea 多行文本输入框会存如下图所示图标&#xff0c; 用户可拉动它改变高度&#xff0c;这是我们不想看到的&#xff0c;所以要去掉它。 去掉后&#xff1a; 解决方案 设置 resize 属性即可&#xff0c;如下代码所示&#xff1a; <…

ping指令的实现与icmp协议的讲解

icmp协议 icmp属于一个网络层的协议&#xff0c;一般被封装到IP报文中&#xff0c;主要功能是如果IP报文出现目的地不可达&#xff0c;时间超过等情况出现时&#xff0c;就会将返回一个差错检测报文&#xff0c;里面包括了IP报文丢失的原因 常见的报错信息有 目的地不可达时间…

Log4j2的Policies详解、SizeBasedTriggeringPolicy、TimeBasedTriggeringPolicy

文章目录 一、Policies二、SizeBasedTriggeringPolicy:基于文件大小的滚动策略2.1、文件达到指定大小就归档 三、TimeBasedTriggeringPolicy&#xff1a;基于时间间隔的滚动策略3.1、验证秒钟归档场景3.2、验证分钟场景3.3、验证小时场景 四、多策略组合使用五、扩展知识5.1、S…

SpringCloudAlibaba实战入门之路由网关Gateway初体验(十一)

Spring Cloud 原先整合 Zuul 作为网关组件,Zuul 由 Netflix 公司提供的,现在已经不维护了。后面 Netflix 公司又出来了一个 Zuul2.0 网关,但由于一直没有发布稳定版本,所以 Spring Cloud 等不及了就自己推出一个网关,已经不打算整合 zuul2.0 了。 一、什么是网关 1、顾明…

#渗透测试#漏洞挖掘#红蓝攻防#常见未授权访问漏洞汇总

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

Fast adaptively balanced min-cut clustering

#0.论文信息 标题&#xff1a;Fast adaptively balanced min-cut clustering期刊&#xff1a;Pattern Recognition作者: Feiping Nie , Fangyuan Xie , Jingyu Wang ,Xuelong Li机构: China Telecom, Northwestern Polytechnic al University.代码链接&#xff1a; #1.摘要 …

【C++】——精细化哈希表架构:理论与实践的综合分析

先找出你的能力在哪里&#xff0c;然后再决定你是谁。 —— 塔拉韦斯特弗 《你当像鸟飞往你的山》 目录 1. C 与哈希表&#xff1a;核心概念与引入 2. 哈希表的底层机制&#xff1a;原理与挑战 2.1 核心功能解析&#xff1a;效率与灵活性的平衡 2.2 哈希冲突的本质&#x…

前端技术(26) : 全年排班日历

来源: 通义千问 效果图 代码 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>年度日历</title><style>body {font-family: Arial, sans-serif;}.calendar-container {margin: 20px au…

QT-------认识QT

QT简介 QT是一个跨平台的C图形用户界面应用程序框架&#xff0c;由挪威Trolltech公司于1991年开发并发布。它为开发者提供了一套丰富的类库和工具&#xff0c;用于创建各种类型的应用程序&#xff0c;包括桌面应用、移动应用、嵌入式系统应用等。QT具有高度的可定制性和可扩展…

Hive 部署

1 下载并安装 1.1 Hadoop安装 参考另一篇博客&#xff1a;Hadoop 部署 1.2 安装包下载 可通过下面网站下载&#xff1a; 官网&#xff1a;https://dlcdn.apache.org/hive/。清华源&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/apache/hive/。 比如下载apache-hive-4…

Linux中QT应用IO状态设置失效问题

问题&#xff1a;在进入freeze休眠前需要设置特定IO关闭电源灯操作&#xff0c;唤醒后需要将特定IO恢复原来正常工作状态&#xff0c;此时出现偶然性&#xff08;概率很低&#xff09;的IO控制失效问题&#xff1b;【平台&#xff1a;君正X1600HN】 一、问题点分析 1、电路 …

empire靶机

打开靶机 我们先查看页面源代码&#xff0c;发现什么也没有 再去用nmap扫描 nmap -sV -p- 192.168.95.144 发现也没什么用 我们在用dirb扫一下 dirb http://192.168.95.144 我们发现了robots.txt并且响应码是200&#xff0c;去访问一下 又得到了一个目录&#xff0c;去访问…