硬汉嵌入式论坛

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

[ADC] STM32H7 ADC工作在双重模式时,使用同步时钟无法校准

[复制链接]

2

主题

14

回帖

20

积分

新手上路

积分
20
发表于 2019-11-14 09:37:30 | 显示全部楼层 |阅读模式
我在调试ADC的双重模式时,将时钟设置为LL_ADC_CLOCK_SYNC_PCLK_DIV4(200/4=50M),调试发现ADC2程序卡死在了ADC校准完成等待,但是ADC1的初始化没有问题。而且我以独立模式使用时选择这个时钟,程序可以正常使用。我还做了另外一个测试,就是不使用同步时钟而是改成异步时钟(LL_ADC_CLOCK_ASYNC_DIV2),这种情况下,双重模式可以正常使用(波形采集正确)。请问一下产生这种现象的原因可能时什么呢?看手册使用同步时钟可以方便定时器触发采样,但是怎么没法使用呢?




/*
* 函数名称: Samp_Adc_Init
* 函数说明: 采样ADC初始化(DMA方式)
* 输入参数: buff_Addr1, 采样数据缓存地址1
*           buff_Addr2, 采样数据缓存地址2
*           trans_Num, DMA一次传输大小
*           load, 预装载值
* 返回参数: 无
*/
void Samp_Adc_Init(uint32_t buff_Addr1, uint32_t buff_Addr2, uint32_t trans_Num)
{
        uint32_t wait_loop_index;
        LL_GPIO_InitTypeDef LL_GPIO_Struct={0};
        LL_ADC_InitTypeDef ADC_InitStruct = {0};
        LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
        LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
        LL_DMA_InitTypeDef LL_DMA_Struct={0};       
        /* 使能相应时钟 */
        LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
        LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOC);       
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_ADC12);
        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
//        LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_CLKP);        /* 选择ADC的时钟源为per_ck->HSI=64M */       
       
        /* 配置ADC1采样通道 */
        LL_GPIO_Struct.Pin=LL_GPIO_PIN_6;
        LL_GPIO_Struct.Mode=LL_GPIO_MODE_ANALOG;
        LL_GPIO_Struct.Pull=LL_GPIO_PULL_NO;
        LL_GPIO_Init(GPIOA, &LL_GPIO_Struct);       

#if USE_ADC_WORK_MODE       
        /* 配置ADC2采样通道 */       
        LL_GPIO_Struct.Pin=LL_GPIO_PIN_4;
        LL_GPIO_Struct.Mode=LL_GPIO_MODE_ANALOG;
        LL_GPIO_Struct.Pull=LL_GPIO_PULL_NO;
        LL_GPIO_Init(GPIOC, &LL_GPIO_Struct);               
#endif /* USE_ADC_WORK_MODE */

  /* DMA配置 */
        LL_DMA_DeInit(DMA1, LL_DMA_STREAM_0);
        LL_DMA_Struct.PeriphRequest=LL_DMAMUX1_REQ_ADC1;
        LL_DMA_Struct.Direction=LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
        LL_DMA_Struct.Mode=LL_DMA_MODE_CIRCULAR;
        LL_DMA_Struct.Priority=LL_DMA_PRIORITY_MEDIUM;
        LL_DMA_Struct.NbData=trans_Num;
#if USE_ADC_WORK_MODE
        LL_DMA_Struct.PeriphOrM2MSrcAddress=(uint32_t)&ADC12_COMMON->CDR;
        LL_DMA_Struct.MemoryOrM2MDstAddress=(uint32_t)buff_Addr1;
        LL_DMA_Struct.PeriphOrM2MSrcDataSize=LL_DMA_PDATAALIGN_WORD;
        LL_DMA_Struct.MemoryOrM2MDstDataSize=LL_DMA_MDATAALIGN_WORD;
#else
        LL_DMA_Struct.PeriphOrM2MSrcAddress=(uint32_t)&ADC1->DR;
        LL_DMA_Struct.MemoryOrM2MDstAddress=(uint32_t)buff_Addr1;
        LL_DMA_Struct.PeriphOrM2MSrcDataSize=LL_DMA_PDATAALIGN_HALFWORD;
        LL_DMA_Struct.MemoryOrM2MDstDataSize=LL_DMA_MDATAALIGN_HALFWORD;
#endif /* USE_ADC_WORK_MODE */

        LL_DMA_Struct.PeriphOrM2MSrcIncMode=LL_DMA_PERIPH_NOINCREMENT;
        LL_DMA_Struct.MemoryOrM2MDstIncMode=LL_DMA_MEMORY_INCREMENT;
        LL_DMA_Init(DMA1, LL_DMA_STREAM_0, &LL_DMA_Struct);
        /* DMA双缓冲模式配置 */
        LL_DMA_SetMemory1Address(DMA1, LL_DMA_STREAM_0, buff_Addr2);
        LL_DMA_EnableDoubleBufferMode(DMA1, LL_DMA_STREAM_0);       
        /* 开启DMA传输完成中断 */
        LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_0);
        /* 配置中断优先级 */
        NVIC_SetPriority(DMA1_Stream0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
        NVIC_EnableIRQ(DMA1_Stream0_IRQn);
        LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_0);

        /* ADC1初始化 */
        /* 退出掉电模式 */
        LL_ADC_DisableDeepPowerDown(ADC1);
        /* 启动内部稳压器 */
        LL_ADC_EnableInternalRegulator(ADC1);
       /* 等待内部稳压器启动 */
       wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
       while(wait_loop_index != 0)
       {
           wait_loop_index--;
       }         
        /* 设置ADC分频系数 */
       ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_SYNC_PCLK_DIV4;                                   /*  ADC时钟选择  */
#if USE_ADC_WORK_MODE
        ADC_CommonInitStruct.Multimode=LL_ADC_MULTI_DUAL_REG_SIMULT;
        ADC_CommonInitStruct.MultiDMATransfer=LL_ADC_MULTI_REG_DMA_RES_32_10B;
        ADC_CommonInitStruct.MultiTwoSamplingDelay=LL_ADC_MULTI_TWOSMP_DELAY_1CYCLE_5;
#endif /* USE_ADC_WORK_MODE */
        LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);       
        /* BOOST 位控制 */
        LL_ADC_SetBoostMode(ADC1, LL_ADC_BOOST_MODE_50MHZ);
        /* 采样率设置 */
        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_StartCalibration(ADC1, LL_ADC_CALIB_OFFSET, LL_ADC_SINGLE_ENDED);  
        /* 等待校准完成 */
        while(LL_ADC_IsCalibrationOnGoing(ADC1) != 0UL);               
        /* 规则采样参数配置 */
        ADC_REG_InitStruct.TriggerSource=LL_ADC_REG_TRIG_EXT_TIM4_TRGO;
        ADC_REG_InitStruct.DataTransferMode=LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
        ADC_REG_InitStruct.ContinuousMode=LL_ADC_REG_CONV_SINGLE;
        ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;
        ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;       
        ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
        LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
        LL_ADC_REG_SetTriggerEdge(ADC1, LL_ADC_REG_TRIG_EXT_RISING);
        /* 配置规则通道 */
        LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_3);
        LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_3, LL_ADC_SAMPLINGTIME_1CYCLE_5);
        LL_ADC_SetChannelSingleDiff(ADC1, LL_ADC_CHANNEL_3, LL_ADC_SINGLE_ENDED);
        /* 通道预选设置,这个很关键 */
        ADC1->PCSEL |= (1UL << (__LL_ADC_CHANNEL_TO_DECIMAL_NB(LL_ADC_CHANNEL_3) & 0x1FUL));         

#if USE_ADC_WORK_MODE
        /* ADC2初始化 */
        /* 退出掉电模式 */
        LL_ADC_DisableDeepPowerDown(ADC2);
        /* 启动内部稳压器 */
        LL_ADC_EnableInternalRegulator(ADC2);
  /* 等待内部稳压器启动 */
        wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
        while(wait_loop_index != 0)
       {
          wait_loop_index--;
       }         
        /* BOOST 位控制 */
       LL_ADC_SetBoostMode(ADC2, LL_ADC_BOOST_MODE_50MHZ);
        /* 采样率设置 */
       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(ADC2, &ADC_InitStruct);
        /* 启动校准 */
       LL_ADC_StartCalibration(ADC2, LL_ADC_CALIB_OFFSET, LL_ADC_SINGLE_ENDED);  
        /* 等待校准完成 */
        while(LL_ADC_IsCalibrationOnGoing(ADC2) != 0UL);                                                   /*   使用同步时钟会死这里   */
        /* 规则采样参数配置 */
        ADC_REG_InitStruct.TriggerSource=LL_ADC_REG_TRIG_EXT_TIM4_TRGO;
        ADC_REG_InitStruct.DataTransferMode=LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
        ADC_REG_InitStruct.ContinuousMode=LL_ADC_REG_CONV_SINGLE;
        ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;
        ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;       
        ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
        LL_ADC_REG_Init(ADC2, &ADC_REG_InitStruct);
        LL_ADC_REG_SetTriggerEdge(ADC2, LL_ADC_REG_TRIG_EXT_RISING);
        /* 配置规则通道 */
        LL_ADC_REG_SetSequencerRanks(ADC2, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_4);
        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);
        /* 通道预选设置,这个很关键 */
       ADC2->PCSEL |= (1UL << (__LL_ADC_CHANNEL_TO_DECIMAL_NB(LL_ADC_CHANNEL_4) & 0x1FUL));
#endif /* USE_ADC_WORK_MODE */

        /* 初始化采样定时器 */
        Samp_TIM_Init();
}

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106678
QQ
发表于 2019-11-14 09:43:47 | 显示全部楼层
双重模式自己研究下,无论是F4还是H7,我基本不用这种方式。原因是这种方式无法做定时器触发,因为双重模式的的交叉采样的时间间隔是固定的那么几个值。
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-14 09:50:00 | 显示全部楼层
eric2013 发表于 2019-11-14 09:43
双重模式自己研究下,无论是F4还是H7,我基本不用这种方式。原因是这种方式无法做定时器触发,因为双重模式 ...

我现在用的是定时器触发的,看采样的波形感觉没问题,可以定时器触发吧,我没使用交替触发,是两个通道同步采样。我现在的任务是高速同步采样两个通道,我看手册发现H7 ADC有个双重采样,所以就采用了这方式。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106678
QQ
发表于 2019-11-14 09:54:00 | 显示全部楼层
mvvm 发表于 2019-11-14 09:50
我现在用的是定时器触发的,看采样的波形感觉没问题,可以定时器触发吧,我没使用交替触发,是两个通道同 ...

这个也简单,同一个定时器的两个通道,分别触发两个ADC即可。

我的一代示波器,二代示波器都是这种方式。

双重,三重,我基本已经不用了,所以具体你自己研究下吧
回复

使用道具 举报

2

主题

14

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2019-11-14 10:14:18 | 显示全部楼层
eric2013 发表于 2019-11-14 09:54
这个也简单,同一个定时器的两个通道,分别触发两个ADC即可。

我的一代示波器,二代示波器都是这种方 ...

嗯,谢谢了
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2021-6-19 14:26:32 | 显示全部楼层
我也遇到了这个问题,双路同步采样,如果ADC接异步时钟,采样会出错,接同步时钟ADC2无法校准。我个人觉得用一路DMA可能少消耗点资源,但是没想到这么多问题。确实一个定时器事件触发两个ADC也很方便。
回复

使用道具 举报

0

主题

5

回帖

5

积分

新手上路

积分
5
发表于 2023-3-1 16:10:38 | 显示全部楼层
采用交替触发,双重ADC没有问题,很好用,在F407上用过
回复

使用道具 举报

7

主题

21

回帖

42

积分

新手上路

积分
42
发表于 2024-1-10 17:04:57 | 显示全部楼层
三个adc同步采样咋搞
回复

使用道具 举报

1

主题

10

回帖

13

积分

新手上路

积分
13
发表于 2024-1-16 18:27:41 | 显示全部楼层
我在使用双ADC的时候也遇到了这个问题,ADC1正常校准,ADC2卡死
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-4-29 08:48 , Processed in 0.342029 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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