STM32L151的ADC时钟配置爬坑记

目录

一、前言

二、挖坑过程

三、填坑记录


一、前言

首先说明一下,我所指的“爬坑”并不是说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没法用!!!

猜你喜欢

转载自blog.csdn.net/m0_37845735/article/details/105890138