单片机学习小组
直播中

张昂笙

7年用户 986经验值
私信 关注

STM32L151的ADC时钟配置有哪些注意事项?

STM32L151的ADC时钟配置有哪些注意事项?

回帖(1)

李秀容

2022-2-21 10:26:17
一、前言
首先说明一下,我所指的“爬坑”并不是说STM32L151的时钟有问题哈,STM32L151的时钟肯定是没问题的,只是跟STM32F1或F4的时钟配置有些差别,我正是因为没有注意这些差别,以为L1跟F1的时钟应该是一样的,才有了这篇爬坑记, 自己挖坑自己跳自己填,相当的刺激!还是太年轻啊!


二、挖坑过程
我们在一个项目中使用了L151这个芯片,我是用CubeMAX配置生成的工程,其中有用到L151的ADC用来采集多通道的模拟量,采用的DMA方式。由于为了要低功耗嘛(用这个芯片就是为了低功耗),在时钟初始化中有一个内部的高速时钟(HSI)的配置,由于之前F1系列的单片机用得多一些,F1项目工程中HSI都没有用到过,我以为时钟应该都是一样的嘛,所以自己手动把HSI初始化给屏蔽了。贴上被我屏蔽的HSI配置,SystemClock_Config 是CUberMAX自动生成的


void SystemClock_Config(void)
{
    ....
    LL_RCC_HSI_Enable();
    /* Wait till HSI is ready */
    while(LL_RCC_HSI_IsReady() != 1)
    {
    }
    LL_RCC_HSI_SetCalibTrimming(16);
    ....
}
三、填坑记录
用CubeMAX配置了ADC_IN18用于循环采集该通道的模拟量,重点配置了:


1、DMA:外设地址和内存地址、模式为循环模式(ADC多通道必须)、数据长度。


2、ADC:数据对齐方式为12bit右对齐、开启低功耗模式、开始多通道扫描、开启通道转换为连续模式、失能间断模式、开始DMA传输、设置规则组的通道序列、采样时间。初始化内容如下(没有问题的):


/* ADC init function */
void MX_ADC1_Init(void)
{
        LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
        LL_ADC_InitTypeDef ADC_InitStruct = {0};
        LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};

        LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
        /* Peripheral clock enable */
        LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1);

        LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
        /**ADC GPIO Configuration  
        PB12   ------> ADC_IN18
        */
        GPIO_InitStruct.Pin = LL_GPIO_PIN_12;
        GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
        LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        /* ADC DMA Init */

        /* ADC Init */
        LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_SetPeriphAddress        (DMA1, LL_DMA_CHANNEL_1,(uint32_t)(&ADC1->DR));               
    LL_DMA_SetMemoryAddress        (DMA1, LL_DMA_CHANNEL_1,(uint32_t)(&stADCData.DMA[0]));       
        LL_DMA_SetChannelPriorityLevel (DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
        LL_DMA_SetMode                 (DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
        LL_DMA_SetPeriphIncMode        (DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
        LL_DMA_SetMemoryIncMode        (DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
        LL_DMA_SetPeriphSize           (DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
        LL_DMA_SetMemorySize           (DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
        LL_DMA_DisableChannel          (DMA1, LL_DMA_CHANNEL_1);
        //这里的长度为 MemorySize类型的长度,比如MemorySize=Byte则为字节长度,MemorySize=HALFWORD则为半字长度
        LL_DMA_SetDataLength           (DMA1, LL_DMA_CHANNEL_1, ADC_CH_MAX);
        LL_DMA_EnableChannel           (DMA1, LL_DMA_CHANNEL_1);
       
        /* ADC interrupt Init */
        NVIC_SetPriority(ADC1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
        NVIC_EnableIRQ(ADC1_IRQn);

        /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
        */
        ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_ASYNC_DIV1;
        LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);
        ADC_InitStruct.Resolution         = LL_ADC_RESOLUTION_12B;
        ADC_InitStruct.DataAlignment      = LL_ADC_DATA_ALIGN_RIGHT;
        ADC_InitStruct.LowPowerMode       = LL_ADC_LP_AUTOWAIT|LL_ADC_LP_AUTOPOWEROFF_IDLE_PHASE;
        ADC_InitStruct.SequencersScanMode = LL_ADC_SEQ_SCAN_ENABLE;
        LL_ADC_Init(ADC1, &ADC_InitStruct);
        ADC_REG_InitStruct.TriggerSource    = LL_ADC_REG_TRIG_SOFTWARE;
        ADC_REG_InitStruct.SequencerLength  = LL_ADC_REG_SEQ_SCAN_DISABLE;
        ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
        ADC_REG_InitStruct.ContinuousMode   = LL_ADC_REG_CONV_CONTINUOUS; //LL_ADC_REG_CONV_SINGLE  LL_ADC_REG_CONV_CONTINUOUS
        ADC_REG_InitStruct.DMATransfer      = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;//LL_ADC_REG_DMA_TRANSFER_UNLIMITED LL_ADC_REG_DMA_TRANSFER_NONE
        LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
        LL_ADC_REG_SetFlagEndOfConversion(ADC1, LL_ADC_REG_FLAG_EOC_SEQUENCE_CONV);
        LL_ADC_SetChannelsBank(ADC1, LL_ADC_CHANNELS_BANK_A);
        /** Configure Regular Channel
        */
        LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_18);
        LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_18, LL_ADC_SAMPLINGTIME_384CYCLES);

        LL_ADC_Enable(ADC1);

        //    LL_ADC_EnableIT_EOS(ADC1);//使能转换结束后产生中断

        LL_ADC_REG_StartConversionSWStart(ADC1);
}


按理在这一切配置完成后,ADC应该能采集了才对。but 不好使,我的第一反应就是纳尼??? what???这刚在F1的工程中配置过能使的啊?一脸懵逼。然后开始查硬件,发现引脚是有电压的;还是不行然后开始检查配置,各种对比啊把寄存器跟F103的都对了一遍,发现L151的ADC寄存器跟F103的ADC寄存器有的为是不一样的,但还是没有解决问题;正在开始怀疑人生的时候,突然想起了时钟(因为前两天刚被L151的GPIO时钟配置坑过一把,点灯点了一天,最后发现是时钟配置有问题),然后开始对比F103和L151的时钟树,果然问题就出现时钟上,先附上时钟对比图




问题原因:
F103中ADC时钟来源:HSE ---> PLLCLK ---> HCLK ---> APB2 ---> ADC时钟
L151中ADC时钟来源:HSI ---> ADC时钟

F103中GPIO时钟来源:HSE ---> PLLCLK ---> HCLK ---> PCLK2 ---> APB2外设时钟
L151中GPIO时钟来源:HSE ---> PLLCLK ---> HCLK ---> PCLK1 ---> APB1外设时钟

所以在STM32L151中,必须使能初始化HSI时钟,不然ADC没法用!!!


举报

更多回帖

发帖
×
20
完善资料,
赚取积分