硬汉嵌入式论坛

 找回密码
 立即注册
查看: 108|回复: 4
收起左侧

[ADC] H743 TIM单次触发双ADC交替采集 采样点不够

[复制链接]

1

主题

4

回帖

7

积分

新手上路

积分
7
发表于 5 天前 | 显示全部楼层 |阅读模式
我使用双ADC交替采集10kHz正弦电压信号。ADC1和ADC2综合采样频率均配置为4.5MHz(H7顶配)。当使用连续模式(不使用TIM)时,采样点数完全正确(一个周期内能采集900个点,且波形正确),而当配置为4MHz的TIM进行单次触发时,在相同条件下单周期仅能采集到400个样本点(理论上不应该是800个吗?),请问这是为什么?有没有有经验的大佬解答一下?


[C] 纯文本查看 复制代码
#ifdef USE_ADC

/* 头文件 */
#include "adc.h"

/* 全局变量 */               
uint32_t WaveShape[BUFF_SIZE] = {0};
/*
*********************************************************************************************************
*	函 数 名: ADC_GPIO_Config
*	功能说明: 配置ADC的GPIO端口并使能
*	形    参: NONE
*	返 回 值: NONE
*********************************************************************************************************
*/
static void ADC12_GPIO_Config(void)
{
    /* GPIO配置 */
    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
    LL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* GPIO时钟使能 */
    LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOC);	 
}

/*
*********************************************************************************************************
*	函 数 名: ADC_CLK_Config
*	功能说明: 配置ADC的时钟源PLL2并使能
*	形    参: NONE
*	返 回 值: NONE
*********************************************************************************************************
*/
static void ADC12_CLK_Config(void)
{
    /* 将PLL2配置为ADC1的时钟源(综合采样频率满配4.5MHz) */
    LL_RCC_PLL2FRACN_Disable();
    LL_RCC_PLL2_SetM(25);
    LL_RCC_PLL2_SetN(504);
    LL_RCC_PLL2_SetP(7);
    LL_RCC_PLL2_SetQ(2);
    LL_RCC_PLL2_SetR(2);
    LL_RCC_PLL2_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4);
    LL_RCC_PLL2_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE);
    LL_RCC_PLL2_Enable();
    while(LL_RCC_PLL2_IsReady() != 1){}
    LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLL2P);
        
    /* 使能ADC1\ADC2时钟 */
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_ADC12);
}


/*
*********************************************************************************************************
*	函 数 名: ADC1_Normal_Config
*	功能说明: 配置ADC1常规寄存器并使能
*	形    参: NONE
*	返 回 值: NONE
*********************************************************************************************************
*/
void ADC12_Normal_Config(void)
{       
    /* 定义ADC配置结构体实例 */
    LL_ADC_InitTypeDef ADC_InitStruct = {0};
    LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
    LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};

    /* ADC常规配置 */
    LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
    LL_ADC_SetBoostMode(ADC1, LL_ADC_BOOST_MODE_20MHZ);
    LL_ADC_SetOverSamplingScope(ADC2, LL_ADC_OVS_DISABLE);
    LL_ADC_SetBoostMode(ADC2, LL_ADC_BOOST_MODE_20MHZ);

    /* 结构体1 */
    /*分辨率*/
    ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B; 
    /*禁用左对齐*/    
    ADC_InitStruct.LeftBitShift = LL_ADC_LEFT_BIT_SHIFT_NONE; 
    /*禁用掉电模式*/
    ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
    /* 配置 */
    LL_ADC_Init(ADC1, &ADC_InitStruct);
    LL_ADC_Init(ADC2, &ADC_InitStruct);

    /* 结构体2 */
    /*TIM3 的TRGO触发*/
    ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_EXT_TIM3_TRGO;
    /*禁用扫描模式*/
    ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;  
    /*禁用队列*/
    ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
    /*ADC单次触发模式*/
    ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS;
    /*转换后的数据->DR*/
    ADC_REG_InitStruct.DataTransferMode =  LL_ADC_REG_DR_TRANSFER ;
    /*数据溢出覆写*/
    ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
    /*上升沿触发*/
    LL_ADC_REG_SetTriggerEdge(ADC1, LL_ADC_REG_TRIG_EXT_RISING); 
    /* 配置 */
    LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);													 								
    LL_ADC_REG_Init(ADC2, &ADC_REG_InitStruct);
    
    /* 结构体3 */
    /*时钟1分频*/
    ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_ASYNC_DIV1;     
    /*单通道-双ADC交替采样*/
    ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_DUAL_REG_INTERL;
    /*配置DMA32位-10位分辨率(直接影响CDR寄存器是否有值,哪怕不启用DMA内存搬运,也要写这行)*/
    ADC_CommonInitStruct.MultiDMATransfer = LL_ADC_MULTI_REG_DMA_RES_32_10B; 
    /*采集时间延迟2.5个周期*/
    ADC_CommonInitStruct.MultiTwoSamplingDelay = LL_ADC_MULTI_TWOSMP_DELAY_2CYCLES_5;
    /* 配置 */
    LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);
    
    /* ADC1电源配置 */
    LL_ADC_DisableDeepPowerDown(ADC1);
    LL_ADC_EnableInternalRegulator(ADC1);
    __IO uint32_t wait_loop_index;
    wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
    while(wait_loop_index != 0){wait_loop_index--;}
    /* ADC2电源配置 */
    LL_ADC_DisableDeepPowerDown(ADC2);
    LL_ADC_EnableInternalRegulator(ADC2);
    __IO uint32_t wait_loop_index2;
    wait_loop_index2 = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
    while(wait_loop_index2 != 0){wait_loop_index2--;}
	
    /* ADC1通道配置 */
    /*rank1, 通道4*/
    LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_4);
    /*采样时间1.5个周期*/
    LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_1CYCLE_5);  
    /*单端模式(禁用差分模式)*/
    LL_ADC_SetChannelSingleDiff(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SINGLE_ENDED);
    /*预选通道4,非常关键*/
	ADC1->PCSEL |= (1UL << (__LL_ADC_CHANNEL_TO_DECIMAL_NB(LL_ADC_CHANNEL_4) & 0x1FUL)); 
    /* ADC2通道配置 */
    /*rank1, 通道4*/
    LL_ADC_REG_SetSequencerRanks(ADC2, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_4);
    /*采样时间1.5个周期*/
    LL_ADC_SetChannelSamplingTime(ADC2, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_1CYCLE_5);  
    /*单端模式(禁用差分模式)*/
    LL_ADC_SetChannelSingleDiff(ADC2, LL_ADC_CHANNEL_4, LL_ADC_SINGLE_ENDED);
    /*预选通道4,非常关键*/
	ADC2->PCSEL |= (1UL << (__LL_ADC_CHANNEL_TO_DECIMAL_NB(LL_ADC_CHANNEL_4) & 0x1FUL));
    
    /* 校准 */
    LL_ADC_StartCalibration(ADC1, LL_ADC_SINGLE_ENDED, LL_ADC_SINGLE_ENDED);      
    while(LL_ADC_IsCalibrationOnGoing(ADC1)){}; 
    /* 校准 */
    LL_ADC_StartCalibration(ADC2, LL_ADC_SINGLE_ENDED, LL_ADC_SINGLE_ENDED);      
    while(LL_ADC_IsCalibrationOnGoing(ADC2)){};        
		
	/* 使能 */
	LL_ADC_Enable(ADC1);
	while(LL_ADC_IsActiveFlag_ADRDY(ADC1) != SET){};
    /* 使能 */
	LL_ADC_Enable(ADC2);
	while(LL_ADC_IsActiveFlag_ADRDY(ADC2) != SET){};
}

/*
*********************************************************************************************************
*	函 数 名: Threshold_Beyond_Process
*	功能说明: 高频ADC中断时的阈值处理
*	形    参: NONE
*	返 回 值: NONE
*********************************************************************************************************
*/
void ADC12_NVIC_Config(void)
{
    NVIC_SetPriority(ADC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
    NVIC_EnableIRQ(ADC_IRQn);
    
    /* 仅使能ADC2的中断 */
    LL_ADC_EnableIT_EOC(ADC2);
}

/*
*********************************************************************************************************
*	函 数 名: ADC_IRQHandler
*	功能说明: ADC1的中断服务函数;判断输入阈值并做出相应处理
*	形    参: NONE
*	返 回 值: NONE
*********************************************************************************************************
*/
void ADC_IRQHandler(void)
{
    /* 清除标志位 */
    if (LL_ADC_IsActiveFlag_EOC(ADC2))
    {
        LL_ADC_ClearFlag_EOC(ADC2);
    }
    
    /* 阈值超出时的执行逻辑 */
    if ((float)ADC2->DR*3.3/4096 >= 1.0 )
    {
        /* 停止ADC的中断,但EOC仍在发送 */
        LL_ADC_DisableIT_EOC(ADC2);
        /* 局部变量计数指针 */
        uint16_t counter = 0;
        
        /* 将当前ADC寄存器中的值搬运到缓冲区.这建立在CPU中断处理速度大于ADC转换的速度之上 */
        while (counter<BUFF_SIZE)
        {
            /* 轮询检查标志位等待下一次ADC12转换完成 */
            while (LL_ADC_IsActiveFlag_EOC(ADC1) != 1){ }
            /* 轮询通过则将当前寄存器的值搬运至缓冲区 */
            WaveShape[counter] = ADC12_COMMON->CDR;
            /* 清除标志位 */
            LL_ADC_ClearFlag_EOC(ADC1);
            /*自增1*/
            counter++;
        }
        /* 重新使能EOC中断,恢复连续模式 */
        // 调试模式下先不重新使能ADC的中断,避免中断造成CPU的printf的阻塞
        LL_ADC_EnableIT_EOC(ADC2);
    }
}

/*
*********************************************************************************************************
*	函 数 名: User_ADC_Init
*	功能说明: 调用各种配置函数
*	形    参: NONE
*	返 回 值: NONE
*********************************************************************************************************
*/
void User_ADC12_Init(void)
{
    ADC12_GPIO_Config();
    ADC12_CLK_Config();
    ADC12_NVIC_Config();
    ADC12_Normal_Config();
}

/*
*********************************************************************************************************
*	函 数 名: ADC1_Start
*	功能说明: 启动ADC1开始转换
*	形    参: NONE
*	返 回 值: NONE
*********************************************************************************************************
*/
void ADC1_Start(void)
{
    LL_ADC_REG_StartConversion(ADC1);
}


#endif

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
116197
QQ
发表于 4 天前 | 显示全部楼层
这种的交错模式,使用定时器触发效果一般,因为根本原因是交错的间隔是固定的,而你的定时器触发也有一个间隔,他们两个无法做到动态的等间隔。

最后注意两个问题
1、V版硬件芯片的,ADC强制二分频的,这个特别注意。
2、然后就是不同封装的最高支持ADC速度是不同的

STM32H7中ADC功能被ST玩出花,现在分直接通道,快速通道和慢速通道,不同封装最高速不同, LQFP封装速度最低
https://www.armbbs.cn/forum.php? ... 7343&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

1

主题

4

回帖

7

积分

新手上路

积分
7
 楼主| 发表于 4 天前 | 显示全部楼层
但是如果TIM设定为4MHz、主从ADC均设定为4.5MHz/采样时间1.5周期/12位分辨率(6.5个周期),两个ADC之间的采样延迟设定为2.5个时钟周期,则每个可用数据点之间的时间间隔应该为0.55μs/0.58μs的交替(相当于TIM的触发造成了0.03μs的延迟),这样不是也大致能做到等间距吗?
回复

使用道具 举报

1

主题

4

回帖

7

积分

新手上路

积分
7
 楼主| 发表于 4 天前 | 显示全部楼层
本帖最后由 wangzz 于 2025-6-6 10:41 编辑

D:\users\adm\Desktop\微信图片_20250606103143.jpg
类似于这样
微信图片_20250606103143.jpg
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
116197
QQ
发表于 3 天前 | 显示全部楼层
wangzz 发表于 2025-6-6 10:26
但是如果TIM设定为4MHz、主从ADC均设定为4.5MHz/采样时间1.5周期/12位分辨率(6.5个周期),两个ADC之间的采 ...

如果你不需要动态定时触发的话,也行。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-6-10 02:01 , Processed in 0.263254 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表