ADC+DMA+TIM

ADC转换数据,定时器触发采集,DMA搬运数据,这样可以在精准的时间,进行高效率的数据采集

如何配置

在这里插入图片描述

  • 首先要知道的是,ADC中有两个寄存器,一个是规则通道数据寄存器,一个是注入通道数据寄存器,他们存放转化之后的数据的地址是不一样的,规则通道组是存放在R32_ADCx_RDATAR(ADCx 规则数据寄存器,x=1,2),而注入通道组是存放在**ADCy 注入数据寄存器 x(ADCy_IDATARx)**中。(手册ADC寄存器部分)
  • 那是不是随便选择自己喜欢的通道呢,那肯定是不是的,此事在手册中亦有记载,如果使用注入组,就DMA就不会搬运数据,更别说进入DMA中断了
    在这里插入图片描述
  • 那定时器为什么要配置成PWM模式呢,此事在手册中亦有记载,只有检测到上升沿的时候,才可以启动转换
    在这里插入图片描述

定时器配置

注意:定时器使能我是放在了初始化函数里面

/*******************************************************
* @function :   TIM_Function_Init
* @param    :   uint16_t arr 自动重装载值
* @param    :   uint16_t psc 预分频系数
* @param    :   uint16_t ccp 比较值
* @retval   :   void
* @brief    :   定时器初始化函数
********************************************************/

void TIM_Function_Init(uint16_t arr, uint16_t psc, uint16_t ccp)
{
    GPIO_InitTypeDef        GPIO_InitStructure = {0};
    TIM_OCInitTypeDef       TIM_OCInitStructure = {0};
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure = {0};

//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_TIM1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    TIM_TimeBaseInitStructure.TIM_Period = arr;
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = ccp;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Disable);
    TIM_ARRPreloadConfig(TIM1, ENABLE);
}

DMA配置
/**
 * *@function :   ADC_DMA_Function_Init
 * @brief   DMA 传输初始化函数
 * @param   DMA_CHx   DMA 通道指针 (DMA_Channel_TypeDef*)
 * @param   memadr    存储器基地址 (uint32_t)
 * @param   bufsize   传输数据大小 (uint16_t),单位为数据单元个数
 * @retval  无
 * @note    调用此函数前,请确保 GPIO 及相关外设已初始化完毕;
 *          本例使用 DMA1,如需使用 DMA2,请相应修改 RCC 时钟和通道参数。
 */

void ADC_DMA_Function_Init(DMA_Channel_TypeDef* DMA_CHx, uint32_t memadr, uint16_t bufsize)
{
    DMA_InitTypeDef DMA_InitStructure = {0};  /**< DMA 配置结构体,先全部清零 */
    NVIC_InitTypeDef NVIC_InitStructure = {0};

    /* 1. 使能 DMA1 时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* 2. 复位指定的 DMA 通道,清除之前的状态 */
    DMA_DeInit(DMA_CHx);

    /* 3. 填充 DMA 参数结构体 */
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->RDATAR;       /**< 外设基地址(源地址) */
    DMA_InitStructure.DMA_MemoryBaseAddr     = memadr;                       /**< 存储器基地址(目的地址) */
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;        /**< 数据传输方向:外设 → 内存 */
    DMA_InitStructure.DMA_BufferSize         = bufsize;                      /**< 要传输的数据单元个数 */

    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;    /**< 外设地址不递增 */
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;         /**< 存储器地址递增 */

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /**< 外设数据宽度:半字(16 位) */
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;     /**< 存储器数据宽度:半字(16 位) */

    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;              /**< 循环模式*/
    DMA_InitStructure.DMA_Priority           = DMA_Priority_VeryHigh;       /**< 最高优先级 */
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;             /**< 关闭内存到内存模式 */

    /* 4. 根据上述配置初始化 DMA 通道 */
    DMA_Init(DMA_CHx, &DMA_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

    /* 5. 使能 DMA 通道*/
    DMA_Cmd(DMA_CHx, ENABLE);
}

ADC配置
/*******************************************************
* @function :   ADC_Function_Init
* @param    :   void
* @retval   :   void
* @brief    :   ADC初始化函数
********************************************************/

void ADC_Function_Init(void)
{
    // 定义 ADC 初始化结构体,并清零
    ADC_InitTypeDef  ADC_InitStructure = {0};
    // 定义 GPIO 初始化结构体,并清零
    GPIO_InitTypeDef GPIO_InitStructure = {0};
//    //定义 NVIC 初始化结构体,并清零
//    NVIC_InitTypeDef NVIC_InitStructure = {0};

    // 使能 GPIOA 和 ADC1 的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    // 配置 ADC 时钟分频
    // ADC 的时钟通常来源于 APB2 时钟,这里设置为 PCLK2 的 8 分频
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);

    // 配置 ADC 输入引脚 GPIO
    // 选择 GPIOA 的 Pin 7
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    // 配置为模拟输入模式 (Analog Input)
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    // 初始化 GPIOA 端口的相应引脚
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 复位并去初始化 ADC1
    // 确保 ADC 处于已知状态
    ADC_DeInit(ADC1);
    // 配置 ADC 工作模式为独立模式
    // 表示 ADC1 独立工作,不与其他 ADC 配合使用
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    // 关闭扫描模式
    // 表示每次转换只转换一个通道,而不是按顺序转换多个通道
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    // 关闭连续转换模式
    // 表示完成一次转换后,需要外部触发或软件触发下一次转换
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    // 配置规则通道的外部触发_
    // 这里设置为定时器1的通道1触发 (T1_CC1)
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
    // 配置 ADC 数据对齐方式
    // 转换为12位数据,右对齐存储
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    // 配置 ADC 转换通道数量
    // 这里设置为转换 1个通道
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    // 初始化 ADC1
    ADC_Init(ADC1, &ADC_InitStructure);

    // 配置ADC通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_55Cycles5);

    ADC_DMACmd(ADC1, ENABLE); // 使能ADC的DMA请求功能
    ADC_Cmd(ADC1, ENABLE);

    // 关闭 ADC 内部缓冲(如果存在)
    // 有些芯片有内部缓冲,这里显式关闭
    ADC_BufferCmd(ADC1, DISABLE); //disable buffer

    // ADC 校准过程
    // 复位校准寄存器
    ADC_ResetCalibration(ADC1);
    // 等待校准复位完成
    while(ADC_GetResetCalibrationStatus(ADC1));
    // 启动 ADC 校准
    ADC_StartCalibration(ADC1);
    // 等待校准完成
    while(ADC_GetCalibrationStatus(ADC1));
    // 获取校准值
    // 将校准后的值保存到变量 Calibrattion_Val 中 (Calibrattion_Val 应该在函数外部定义)
    Calibrattion_Val = Get_CalibrationValue(ADC1);

    // 使能ADC1的外部触发转换
    ADC_ExternalTrigConvCmd(ADC1, ENABLE);
}

DMA中断处理函数
void DMA1_Channel1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void DMA1_Channel1_IRQHandler(void)
{
    printf("DMA1 Channel1 IRQHandler");
    // 检查是否是传输完成中断
    if(DMA_GetITStatus(DMA1_IT_TC1)) // 假设是DMA1 Channel1的传输完成中断标志
    {
        // DMA已经将bufsize个数据搬运到了您的缓冲区
        // 在这里处理缓冲区中的数据,例如打印或进行计算
        // 注意:在循环模式下,DMA会自动继续下一轮传输
        printf("DMA Transfer Complete!\r\n");
        // 遍历并打印采集到的数据(示例)
         for(int i = 0; i < 3; i++)
         {
             printf("Sample %d: %04d\r\n", i, Get_ConversionVal(ADC_DMA_Buffer[i])); // 假设 adc_buffer 是您的采集缓冲区
         }

        DMA_ClearITPendingBit(DMA1_IT_TC1); // 清除DMA传输完成中断标志
    }
    // 检查是否是半传输中断 (如果使能了)
    // if(DMA_GetITStatus(DMA1_IT_HT1))
    // {
    //     // 在这里处理缓冲区的前半部分数据
    //     DMA_ClearITPendingBit(DMA1_IT_HT1);
    // }
    // 检查是否有传输错误
    // if(DMA_GetITStatus(DMA1_IT_TE1))
    // {
    //    // 处理错误
    //    DMA_ClearITPendingBit(DMA1_IT_TE1);
    // }
}

总体代码
/*
 * app_adc.c
 *
 *  Created on: 2025年5月4日
 *      Author: 86180
 */
#include "app_adc.h"

#define ADC_BUFFER_SIZE 30  //ADC缓冲区的大小

#define CURRENT_ADC_SETTING 2 // 假设当前的 ADC 设置是模式 1

// 定义 ADC_MODE 宏,它检查当前的设置是否与传入的参数匹配
#define ADC_MODE(mode) (CURRENT_ADC_SETTING == (mode))

uint16_t Calibrattion_Val = 0; //校准电压
uint16_t ADC_DMA_Buffer[ADC_BUFFER_SIZE]; //ADC数据缓冲区

float Voltage; //转换的电压值

/*******************************************************
* @function :   Get_ConversionVal
* @param    :   uin16_t val    原始采样值
* @retval   :   uin16_t        校准后的采样值
* @brief    :   对采样值进行校准值补偿并裁剪到[0,4095]范围
********************************************************/
uint16_t Get_ConversionVal(uint16_t val)
{
    if ((val + Calibrattion_Val) < 0) return 0;
    if ((val + Calibrattion_Val) > 4095 || val == 4095) return 4095;
    return (val + Calibrattion_Val);
}

#if ADC_MODE(1)

/*******************************************************
* @function :   Adc_Init_Porc
* @param    :   void
* @retval   :   0 - 初始化完成
* @brief    :   ADC 初始化
*              配置 PA1 为模拟输入;
*              设置 ADC1 独立模式、连续转换、软件触发、右对齐;
*              使能 DMA 请求并执行 ADC 校准
********************************************************/

uint8_t Adc_Init_Porc(void)
{
    ADC_InitTypeDef ADC_InitStructure={0};
    GPIO_InitTypeDef GPIO_InitStructure={0};

    /* 使能 GPIOA 和 ADC1 时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
    /* ADC 时钟分频为 PCLK2/8
     * 96MHz / 8 = 12MHz
     * */
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);

    /* 配置 PA1 为模拟输入 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* ADC 基本配置 */
    ADC_DeInit(ADC1);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;          // 独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;               // 非扫描模式(单通道)
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;          // 连续转换
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;      // 数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1;                     // 通道数:1
    ADC_Init(ADC1, &ADC_InitStructure);

    /* 使能 ADC DMA */
    ADC_DMACmd(ADC1, ENABLE);
    /* 使能 ADC */
    ADC_Cmd(ADC1, ENABLE);

    /* 关闭内部缓冲,执行校准 */
    ADC_BufferCmd(ADC1, DISABLE);
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
    /* 获取校准值 */
    Calibrattion_Val = Get_CalibrationValue(ADC1);

    return 0;
}

/*******************************************************
* @function :   ADC_Get_Channel_Val
* @param    :   uint8_t ch 对应ADC的通道
*               ADC_Channel_0 - ADC Channel0 selected.
*               ADC_Channel_1 - ADC Channel1 selected.
*               ADC_Channel_2 - ADC Channel2 selected.
*               ADC_Channel_3 - ADC Channel3 selected.
*               ADC_Channel_4 - ADC Channel4 selected.
*               ADC_Channel_5 - ADC Channel5 selected.
*               ADC_Channel_6 - ADC Channel6 selected.
*               ADC_Channel_7 - ADC Channel7 selected.
*               ADC_Channel_8 - ADC Channel8 selected.
*               ADC_Channel_9 - ADC Channel9 selected.
*               ADC_Channel_10 - ADC Channel10 selected.
*               ADC_Channel_11 - ADC Channel11 selected.
*               ADC_Channel_12 - ADC Channel12 selected.
*               ADC_Channel_13 - ADC Channel13 selected.
*               ADC_Channel_14 - ADC Channel14 selected.
*               ADC_Channel_15 - ADC Channel15 selected.
*               ADC_Channel_16 - ADC Channel16 selected.
*               ADC_Channel_17 - ADC Channel17 selected.
*
*
* @retval   :   uint16_t val 返回转化结果
* @brief    :   获取指定通道的 ADC 转换值
*              ch -- 要读取的 ADC 通道编号
********************************************************/

uint16_t ADC_Get_Channel_Val(uint8_t ch)
{
    uint16_t val;

    /* 配置规则通道和采样时间 */
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
    /* 软件触发开始转换 */
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    /* 等待转换结束 */
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));
    /* 读取转换结果 */
    val = ADC_GetConversionValue(ADC1);

    return val;
}


/**
 * *@function :   ADC_DMA_Init_Proc
 * @brief   DMA 传输初始化函数
 * @param   DMA_CHx   DMA 通道指针 (DMA_Channel_TypeDef*)
 * @param   ppadr     外设基地址 (uint32_t)
 * @param   memadr    存储器基地址 (uint32_t)
 * @param   bufsize   传输数据大小 (uint16_t),单位为数据单元个数
 * @retval  无
 * @note    调用此函数前,请确保 GPIO 及相关外设已初始化完毕;
 *          本例使用 DMA1,如需使用 DMA2,请相应修改 RCC 时钟和通道参数。
 */

void ADC_DMA_Init_Proc(DMA_Channel_TypeDef* DMA_CHx, uint32_t ppadr, uint32_t memadr, uint16_t bufsize)
{
    DMA_InitTypeDef DMA_InitStructure = {0};  /**< DMA 配置结构体,先全部清零 */

    /* 1. 使能 DMA1 时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* 2. 复位指定的 DMA 通道,清除之前的状态 */
    DMA_DeInit(DMA_CHx);

    /* 3. 填充 DMA 参数结构体 */
    DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;                        /**< 外设基地址(源地址) */
    DMA_InitStructure.DMA_MemoryBaseAddr     = memadr;                       /**< 存储器基地址(目的地址) */
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;        /**< 数据传输方向:外设 → 内存 */
    DMA_InitStructure.DMA_BufferSize         = bufsize;                      /**< 要传输的数据单元个数 */

    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;    /**< 外设地址不递增 */
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;         /**< 存储器地址递增 */

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /**< 外设数据宽度:半字(16 位) */
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;     /**< 存储器数据宽度:半字(16 位) */

    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;              /**< 循环模式*/
    DMA_InitStructure.DMA_Priority           = DMA_Priority_VeryHigh;       /**< 最高优先级 */
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;             /**< 关闭内存到内存模式 */

    /* 4. 根据上述配置初始化 DMA 通道 */
    DMA_Init(DMA_CHx, &DMA_InitStructure);
}

/*******************************************************
* @function :   Adc_Data_Proc
* @param    :   MultiTimer* timer 指向触发回调的定时器实例的指针
* @param    :   void* user_data 用户自定义数据指针
* @retval   :   void
* @brief    :   ADC数据处理函数
*               PB10 - USART3_TX
*               PB11 - USART3_RX
********************************************************/

void Adc_Data_Proc(MultiTimer* timer,void* user_data)
{
    uint32_t sum = 0;
    uint8_t i = 0;
    for (i = 0; i < ADC_BUFFER_SIZE; i++) {
        sum += ADC_DMA_Buffer[i];
    }

    Voltage = (float)sum / 30.0f / 4096.0f * 3.3f;
    multiTimerStart(&adc_proc_time,10,Adc_Data_Proc,NULL);
}

#elif ADC_MODE(2) // ADC+DMA+TIM

void ADC1_2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

/*******************************************************
* @function :   ADC_Function_Init
* @param    :   void
* @retval   :   void
* @brief    :   ADC初始化函数
********************************************************/

void ADC_Function_Init(void)
{
    // 定义 ADC 初始化结构体,并清零
    ADC_InitTypeDef  ADC_InitStructure = {0};
    // 定义 GPIO 初始化结构体,并清零
    GPIO_InitTypeDef GPIO_InitStructure = {0};
//    //定义 NVIC 初始化结构体,并清零
//    NVIC_InitTypeDef NVIC_InitStructure = {0};

    // 使能 GPIOA 和 ADC1 的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    // 配置 ADC 时钟分频
    // ADC 的时钟通常来源于 APB2 时钟,这里设置为 PCLK2 的 8 分频
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);

    // 配置 ADC 输入引脚 GPIO
    // 选择 GPIOA 的 Pin 7
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    // 配置为模拟输入模式 (Analog Input)
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    // 初始化 GPIOA 端口的相应引脚
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 复位并去初始化 ADC1
    // 确保 ADC 处于已知状态
    ADC_DeInit(ADC1);
    // 配置 ADC 工作模式为独立模式
    // 表示 ADC1 独立工作,不与其他 ADC 配合使用
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    // 关闭扫描模式
    // 表示每次转换只转换一个通道,而不是按顺序转换多个通道
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    // 关闭连续转换模式
    // 表示完成一次转换后,需要外部触发或软件触发下一次转换
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    // 配置规则通道的外部触发_
    // 这里设置为定时器1的通道1触发 (T1_CC1)
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
    // 配置 ADC 数据对齐方式
    // 转换为12位数据,右对齐存储
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    // 配置 ADC 转换通道数量
    // 这里设置为转换 1个通道
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    // 初始化 ADC1
    ADC_Init(ADC1, &ADC_InitStructure);

    // 配置ADC通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_55Cycles5);

    ADC_DMACmd(ADC1, ENABLE); // 使能ADC的DMA请求功能
    ADC_Cmd(ADC1, ENABLE);

    // 关闭 ADC 内部缓冲(如果存在)
    // 有些芯片有内部缓冲,这里显式关闭
    ADC_BufferCmd(ADC1, DISABLE); //disable buffer

    // ADC 校准过程
    // 复位校准寄存器
    ADC_ResetCalibration(ADC1);
    // 等待校准复位完成
    while(ADC_GetResetCalibrationStatus(ADC1));
    // 启动 ADC 校准
    ADC_StartCalibration(ADC1);
    // 等待校准完成
    while(ADC_GetCalibrationStatus(ADC1));
    // 获取校准值
    // 将校准后的值保存到变量 Calibrattion_Val 中 (Calibrattion_Val 应该在函数外部定义)
    Calibrattion_Val = Get_CalibrationValue(ADC1);

    // 使能ADC1的外部触发转换
    ADC_ExternalTrigConvCmd(ADC1, ENABLE);
}


/*******************************************************
* @function :   TIM_Function_Init
* @param    :   uint16_t arr 自动重装载值
* @param    :   uint16_t psc 预分频系数
* @param    :   uint16_t ccp 比较值
* @retval   :   void
* @brief    :   定时器初始化函数
********************************************************/

void TIM_Function_Init(uint16_t arr, uint16_t psc, uint16_t ccp)
{
    GPIO_InitTypeDef        GPIO_InitStructure = {0};
    TIM_OCInitTypeDef       TIM_OCInitStructure = {0};
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure = {0};

//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_TIM1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    TIM_TimeBaseInitStructure.TIM_Period = arr;
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);

    //ADC1通道7对应的定时器触发通道是TIM1通道1
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = ccp;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Disable);
    TIM_ARRPreloadConfig(TIM1, ENABLE);
//    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
}


/**
 * *@function :   ADC_DMA_Function_Init
 * @brief   DMA 传输初始化函数
 * @param   DMA_CHx   DMA 通道指针 (DMA_Channel_TypeDef*)
 * @param   memadr    存储器基地址 (uint32_t)
 * @param   bufsize   传输数据大小 (uint16_t),单位为数据单元个数
 * @retval  无
 * @note    调用此函数前,请确保 GPIO 及相关外设已初始化完毕;
 *          本例使用 DMA1,如需使用 DMA2,请相应修改 RCC 时钟和通道参数。
 */

void ADC_DMA_Function_Init(DMA_Channel_TypeDef* DMA_CHx, uint32_t memadr, uint16_t bufsize)
{
    DMA_InitTypeDef DMA_InitStructure = {0};  /**< DMA 配置结构体,先全部清零 */
    NVIC_InitTypeDef NVIC_InitStructure = {0};

    /* 1. 使能 DMA1 时钟 */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* 2. 复位指定的 DMA 通道,清除之前的状态 */
    DMA_DeInit(DMA_CHx);

    /* 3. 填充 DMA 参数结构体 */
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->RDATAR;       /**< 外设基地址(源地址) */
    DMA_InitStructure.DMA_MemoryBaseAddr     = memadr;                       /**< 存储器基地址(目的地址) */
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;        /**< 数据传输方向:外设 → 内存 */
    DMA_InitStructure.DMA_BufferSize         = bufsize;                      /**< 要传输的数据单元个数 */

    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;    /**< 外设地址不递增 */
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;         /**< 存储器地址递增 */

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /**< 外设数据宽度:半字(16 位) */
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;     /**< 存储器数据宽度:半字(16 位) */

    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;              /**< 循环模式*/
    DMA_InitStructure.DMA_Priority           = DMA_Priority_VeryHigh;       /**< 最高优先级 */
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;             /**< 关闭内存到内存模式 */

    /* 4. 根据上述配置初始化 DMA 通道 */
    DMA_Init(DMA_CHx, &DMA_InitStructure);

    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* 5. 使能 DMA 通道*/
    DMA_Cmd(DMA_CHx, ENABLE);

    printf("ADC DMA OK!!!\r\n");
}


///*******************************************************
//* @function :   ADC1_2_IRQHandler
//* @param    :   void
//* @retval   :   void
//* @brief    :   ADC1中断函数
//********************************************************/
//
void ADC1_2_IRQHandler(void)
{
    uint16_t ADC_val;

    if(ADC_GetITStatus(ADC1, ADC_IT_EOC))
    {
        printf("ADC Extline trigger conversion...\r\n");
        ADC_val = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);

        printf("JADC %04d\r\n", Get_ConversionVal(ADC_val));
    }

    ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
}


void DMA1_Channel1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void DMA1_Channel1_IRQHandler(void)
{
    printf("DMA1 Channel1 IRQHandler");
    // 检查是否是传输完成中断
    if(DMA_GetITStatus(DMA1_IT_TC1)) // 假设是DMA1 Channel1的传输完成中断标志
    {
        // DMA已经将bufsize个数据搬运到了您的缓冲区
        // 在这里处理缓冲区中的数据,例如打印或进行计算
        // 注意:在循环模式下,DMA会自动继续下一轮传输
        printf("DMA Transfer Complete!\r\n");
        // 遍历并打印采集到的数据(示例)
         for(int i = 0; i < 3; i++)
         {
             printf("Sample %d: %04d\r\n", i, Get_ConversionVal(ADC_DMA_Buffer[i])); // 假设 adc_buffer 是您的采集缓冲区
         }

        DMA_ClearITPendingBit(DMA1_IT_TC1); // 清除DMA传输完成中断标志
    }
    // 检查是否是半传输中断 (如果使能了)
    // if(DMA_GetITStatus(DMA1_IT_HT1))
    // {
    //     // 在这里处理缓冲区的前半部分数据
    //     DMA_ClearITPendingBit(DMA1_IT_HT1);
    // }
    // 检查是否有传输错误
    // if(DMA_GetITStatus(DMA1_IT_TE1))
    // {
    //    // 处理错误
    //    DMA_ClearITPendingBit(DMA1_IT_TE1);
    // }
}


#endif

/*******************************************************
* @function :   ADC_Init_Func
* @param    :   void
* @retval   :   void
* @brief    :   ADC初始化函数
********************************************************/

void ADC_Init_Func(void)
{
#if ADC_MODE(1)
    uint8_t i = 0;

    Adc_Init_Porc();//ADC配置初始化
    printf("CalibrattionValue:%d\n", Calibrattion_Val);//打印校准值


    ADC_DMA_Init_Proc(DMA1_Channel1,(uint32_t)&ADC1->RDATAR,(uint32_t)ADC_DMA_Buffer,ADC_BUFFER_SIZE); //初始化DMA
    DMA_Cmd(DMA1_Channel1, ENABLE);

    /* 配置ADC通道并启动连续转换 */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_239Cycles5);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    Delay_Ms(50);
//    ADC_SoftwareStartConvCmd(ADC1, DISABLE);

    /* 打印前30次采样值 */
    printf("%04d\r\n", Get_ConversionVal(ADC_DMA_Buffer[i]));

#elif ADC_MODE(2)
    ADC_DMA_Function_Init(DMA1_Channel1,(uint32_t)ADC_DMA_Buffer,ADC_BUFFER_SIZE);
    ADC_Function_Init();
    TIM_Function_Init(1000 - 1,96 - 1,500 - 1);

    TIM_Cmd(TIM1, ENABLE);
#endif
}

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐