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