wgcrdg 发表于 2018-2-9 08:23:46

STM32L053 低功耗采集时,ADC打开与关闭有时出现问题

STM32L053 隔几秒钟采集一次数据。STM32L053 低功耗时,进入停止模式,通过RTC中断唤醒,唤醒后,打开EnableADC()采集数据,采集完成后,关闭DisableADC(),以节省电池电量。但是有时候出现死循环,不知道是怎么回事,也是按参数手册上面做的,还是不行。

void EnableADC(void)
{
ADC1->ISR |= ADC_ISR_ADRDY;
ADC->CCR |= (ADC_CCR_VREFEN|ADC_CCR_TSEN);
ADC1->CR |= ADC_CR_ADEN; //使能ADC
if ((ADC1->CFGR1 &ADC_CFGR1_AUTOFF) == 0)
{
    while ((ADC1->ISR & ADC_ISR_ADRDY) == 0)
    {
      //这里可以添加超时管理
      /*打开时,这个地方有时候出现死循环*/
    }
}
ADC1->CR |= ADC_CR_ADSTART; //启动ADC转换
}


void DisableADC(void)
{
//确保没有转换
if ((ADC1->CR & ADC_CR_ADSTART) != 0)
{
    //停止任何正在进行的转换
    ADC1->CR |= ADC_CR_ADSTP;
}
//等到ADSTP由硬件复位即停止转换
while ((ADC1->CR & ADC_CR_ADSTP) != 0)
{
    //这里可以添加超时管理
}
ADC1->CR |= ADC_CR_ADDIS; //禁用ADC
//等到ADC是完全禁用
while ((ADC1->CR & ADC_CR_ADEN) != 0)
{
    //这里可以添加超时管理
    /*关闭时,这个地方有时候出现死循环*/
}
ADC1->ISR |= ADC_ISR_ADRDY;
ADC->CCR &= ~(ADC_CCR_VREFEN|ADC_CCR_TSEN);
}

wgcrdg 发表于 2018-2-9 09:25:58

本帖最后由 wgcrdg 于 2018-2-9 09:51 编辑

下面是开始程序时的ADC与DMA的初始化部分。

uint16_t ADC_array;
//这个函数配置ADC的DMA来存储结果序列,转换的结果存储在n个元素数组
void ConfigureDMA(void)
{
//使外围时钟DMA
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
//启用DMA传输ADC
ADC1->CFGR1 |= ADC_CFGR1_DMAEN;
//配置外围数据寄存器地址
DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR));
//配置内存地址
DMA1_Channel1->CMAR = (uint32_t)(ADC_array);
//配置待传输的数据数 的数量
DMA1_Channel1->CNDTR = 6;
//配置使能存储器递增模式、存储器16 位模式、外设16 位模式,
//传输错误中断使能和传输完成中断使能
DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 \
    | DMA_CCR_TEIE | DMA_CCR_TCIE;
//使能DMA
DMA1_Channel1->CCR |= DMA_CCR_EN;

//启用中断DMA通道1
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
//设置优先级的DMA通道1
NVIC_SetPriority(DMA1_Channel1_IRQn,2);
}



voidADC_Init(void)
{
//使外围时钟GPIOA GPIOC
//AD1=PA4=IN4AD2=PA5=IN5AD3=PA0=IN0 AD4=PC3=IN13
RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOCEN;
//设置口线为模拟输入
GPIOA->MODER |= (GPIO_MODER_MODE0|GPIO_MODER_MODE4|GPIO_MODER_MODE5);
GPIOC->MODER |= GPIO_MODER_MODE3;
//使外围ADC的时钟
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;   
//ADCCLK(异步时钟模式)
ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE;      
//选择连续转换模式
ADC1->CFGR1 |= ADC_CFGR1_CONT;      
ADC1->CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL4 | ADC_CHSELR_CHSEL13 \
    | ADC_CHSELR_CHSEL5 | ADC_CHSELR_CHSEL17 | ADC_CHSELR_CHSEL18;
//采样时间选择39.5 个 ADC 时钟周期
ADC1->SMPR = ADC_SMPR_SMP_0 | ADC_SMPR_SMP_2;
//采样时间选择79.5 个 ADC 时钟周期
//ADC1->SMPR = ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2;
//使能溢出中断 OVR 位置 1 时产生中断
ADC1->IER = ADC_IER_OVRIE;
//使能 VREFINT,使能温度传感器
ADC->CCR |= (ADC_CCR_VREFEN|ADC_CCR_TSEN);
//ADC上启用中断
NVIC_EnableIRQ(ADC1_COMP_IRQn);   
//为ADC设置中断优先级
NVIC_SetPriority(ADC1_COMP_IRQn,3);
if(vref_vol == 0)
{
    //以下几行为ADC 校准
    //确保 ADEN=0
    if ((ADC1->CR & ADC_CR_ADEN) != 0)
    {
      ADC1->CR &= (uint32_t)(~ADC_CR_ADEN);
    }
    //将 ADCAL 置 1
    ADC1->CR |= ADC_CR_ADCAL;
    //等待,直至 ADCAL=0
    while ((ADC1->ISR & ADC_ISR_EOCAL) == 0)
    {
      
    }
    //校准已完成
    ADC1->ISR |= ADC_ISR_EOCAL;
    /* 计算内部Vref电压(mV) */
    vref_vol = (uint16_t)( 3000.0 * (*(uint16_t *)0x1FF80078) / 4095.0);
    Vrefint_cal=(*(uint16_t *)0x1FF80078);         
}
ConfigureDMA();//函数配置ADC的DMA
}

wgcrdg 发表于 2018-2-9 09:09:36

本帖最后由 wgcrdg 于 2018-2-9 09:17 编辑

ADC采集数据时,单片机是不进入停止模式的,采集完成后,才进入停止模式,单片机采集数据,采用的是ADC+DMA的方式,DMA中断。ADC与DMA的初始化,在开始程序中完成。ADC打开采集与关闭在唤醒后while(1){   }中使用。

Tcreat 发表于 2018-2-9 08:44:26

如果低功耗方案 理论上进入低功耗前外设时钟都会关闭的 你这没有阐述什么级别的功耗模式 也不说明前因后果所以提问的同时也要阐明条件

wenyangzeng 发表于 2018-2-9 09:52:35

唤醒后延时一下再ADC转换试看看

发表于 2018-2-9 09:53:38

在重新开始时,是否重新配置了时钟。

wgcrdg 发表于 2018-2-9 10:32:34

本帖最后由 wgcrdg 于 2018-2-9 10:33 编辑

没有重新配置时钟,时钟配置在开始的程序中,已经配置了。唤醒后,打开EnableADC(); 采集完成后,DisableADC();,然后, __WFI();,周而复始。


wgcrdg 发表于 2018-2-9 10:35:29

这种情况只偶尔的出现,可能几百次里面出现一次,但是一次也不行啊。电池供电的,要确保低功耗。

发表于 2018-2-9 10:38:36

楼主可以试着重新配置一下时钟。我在使用的时候会将时钟关闭,还有很多外设等,用于降低功耗。唤醒后再重新配置时钟。

hello_bug 发表于 2018-2-9 10:44:33

在停止模式下,所有时钟都关闭了吧。你唤醒后,得重新配置时钟,启动外设时钟吧。
页: [1] 2
查看完整版本: STM32L053 低功耗采集时,ADC打开与关闭有时出现问题