换到了新的平台,因为L0以超低功耗出名,而且价格也不贵.另外因为有内部Vref.可以反推出VDD,用的也是ST的官方板子,Nucleo-F011测试的,官方板子用的LDO参数也挺不错的.
首先测试一下官方LDO的数值.
然后说明一下我的测试条件:
- ADC读取内部Vref的标准数值并保存.
- ADC读取内部Vref的参考数值,并倒推VCC电压.
- ADC通过改变采样率查看是否影响精度,低的采样率不能捕捉快速变化的信号,高的采样率容易产生错误.
- ADC通过改变输入时钟查看是否影响精度.输入时钟低时间长,功耗低,采样率也低.
测试程序提纲:
#include "stm32l0xx.h" __IO uint32_t uwTick; void SysTick_Delay(__IO uint32_t Delay) { uint32_t tickstart = 0U; tickstart = uwTick; while((uwTick - tickstart) < Delay) { } } void RCC_Init(void) { /* LSE OFF,LSI DEFAULT,MSI OFF,HSI ON,PLL ON,SYSCLK = 32MHz,CK_PWR = FCLK = HCLK = SysTick = APB1 = APB2 = 32MHz.*/ FLASH->ACR = FLASH_ACR_PRE_READ | FLASH_ACR_PRFTEN; MODIFY_REG(PWR->CR, PWR_CR_VOS, (PWR_CR_VOS_0)); MODIFY_REG(RCC->CR, RCC_CR_HSION | RCC_CR_HSIDIVEN , RCC_CR_HSION); while(READ_BIT(RCC->CR, RCC_CR_HSIRDY) == RESET); MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSITRIM, (uint32_t)(16) << 8U); CLEAR_BIT(RCC->CR, RCC_CR_PLLON); while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) != RESET); MODIFY_REG(RCC->CFGR, RCC_CFGR_PLLMUL | RCC_CFGR_PLLDIV | RCC_CFGR_PLLSRC, (uint32_t)((RCC_CFGR_PLLMUL4) | (RCC_CFGR_PLLDIV2) | (RCC_CFGR_PLLSRC_HSI))); SET_BIT(RCC->CR, RCC_CR_PLLON); while(READ_BIT(RCC->CR, RCC_CR_PLLRDY) == RESET); MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, (uint32_t)(0x01U)); MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_CFGR_HPRE_DIV1); MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL); while ((uint32_t)(RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_CFGR_PPRE1_DIV1); MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_CFGR_PPRE1_DIV1) << 3)); CLEAR_BIT(RCC->CR, RCC_CR_MSION); /* SysTick CLK = 4MHz,Reload = 1kHz. */ SysTick->LOAD = (uint32_t)0x00000F9FUL; NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); SysTick->VAL = 0UL; SysTick->CTRL = SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* SysTick->CTRL = SysTick_CTRL_TICKINT_Msk; */ } uint16_t ADC_GetVDD_Voltage(void) { static uint16_t vref_vol = 0; uint16_t vdd_vol = 0; /* 第一次数值,一旦VREF电压计算后,就不会为0. */ if(vref_vol == 0) { /* 初始化时钟 */ SET_BIT(RCC->APB2ENR, (RCC_APB2ENR_ADC1EN)); /* 初始化相关寄存器 */ ADC1->CFGR1 = 0x00000000; ADC1->CFGR2 = 0x40000000; ADC->CCR = 0x00400000; ADC1->SMPR = 0x00000000; ADC1->CHSELR = 0x00020000; /* 校准ADC */ SET_BIT(ADC1->CR, ADC_CR_ADCAL); while(READ_BIT(ADC1->CR, ADC_CR_ADCAL)); /* 计算内部Vref电压(mV) */ vref_vol = 3000.0 * (*(uint16_t *)0x1FF80078) / 4095; } /* 转换当前ADC并计算当前VDD电压 */ SET_BIT(ADC1->CR, ADC_CR_ADEN | ADC_CR_ADSTART); /* 等待转换完成 */ while(READ_BIT(ADC1->ISR, ADC_ISR_EOC)); /* 倒推VDD电压 */ vdd_vol = vref_vol / ((float)(ADC1->DR) / 4095); /* 关闭ADC节能 */ CLEAR_BIT(ADC1->CR, ADC_CR_ADEN | ADC_CR_ADSTART); return vdd_vol; } uint16_t vdd_mv[50]; uint8_t i = 0; int main(void) { RCC_Init(); while (1) { for(i = 0; i < 50; i++) { vdd_mv[i] = ADC_GetVDD_Voltage(); SysTick_Delay(10); } } }
下面固定条件是16MHz下,最短采样时间,得到1.14Msps采样率(可见电压整理偏高了).
依然是16MHz时钟,但是现在是1Msps采样率(基本上已经正确了).
但是事实上我们好像有点不理解了.现在是下面一些测试数据,因为都是这样贴图,我就不再贴了.
- 16MHz@1.14Msps = 3625mV
- 16MHz@1Msps = 3302mV
- 16MHz@800Ksps = 3190mV
- 16MHz@640ksps = 3244mV
- 16MHz@500Ksps = 3317mV
- 16MHz@308Ksps = 3302mV
- 16MHz@174Ksps = 3392mV
- 16MHz@92Ksps = 3302mV
- 8MHz@570Ksps = 3615mV
- 8MHz@500Ksps = 3302mV
- 8MHz@400Ksps = 3193mV
- 8MHz@320Ksps = 3248mV
- 8MHz@250Ksps = 3250mV
- 8MHz@154Ksps = 3316mV
- 8MHz@87Ksps = 3305mV
- 8MHz@46Ksps = 3305mV
- 250kHz@17Ksps = 3615mV
- 250kHz@1.4Ksps = 3305mV
从18个实验可以看出,不是说采样率越低准确度就越高,如果这一条件要成立,那么时钟一定要相等,比如1.14Msps和17Ksps结果都是测出了3600mV以上,这个跟万用表测试的差距太大,但是降低时钟肯定会降低功耗,如果知道自己的所需采样率,我觉得还是提高时钟然后用最接近的来采样,精度比较高,执行时间也短,执行时间短也是节省功耗的一个办法.终究没得出3307mV,有几个原因,走线损耗,无论是PCB还是芯片内部,还有可能我万用表不准了.140KHz ~ 16MHz是ADC的工作频率,推算出来,最快是1.14Msps,最慢是包含256X过采样,还有173Cycle,配140KHz,大概是3次每秒,是够慢的.