STM32F767 Nucleo板PLL时钟配置中电压缩放作用的技术问询
你的困惑非常典型——很多开发者一开始会忽略电压缩放和时钟之间的关联,觉得只要时钟频率没超过最大值就没问题,但STM32F7的PLL和内核运行对供电电压的敏感性比你想象的更高。让我一步步解释清楚:
核心原因:VOS模式直接决定内核供电电压与时钟模块稳定性
STM32F7的电压缩放(VOS)机制是用来匹配内核供电电压和系统/外设时钟频率的,不同的VOS模式对应不同的内核电压等级,而每个等级直接影响PLL(尤其是VCO压控振荡器)的稳定工作范围:
| VOS模式 | 配置值(PWR->CR1_VOS) | 内核电压 | 最大系统时钟 |
|---|---|---|---|
| VOS3 | 00(默认) | ~0.9V | 100MHz |
| VOS2 | 01(你的代码配置) | ~1.0V | 144MHz |
| VOS1 | 10(CubeMX的"低性能"模式) | ~1.1V | 180MHz |
| VOS0 | 11 | ~1.2V | 216MHz |
虽然你的目标系统时钟只有48MHz,远低于所有模式的最大值,但你的PLL VCO输出是288MHz——这个高频信号的生成对供电电压非常敏感。当VOS处于默认的VOS3(最低电压)时,内核供电不足以支撑VCO稳定工作在288MHz,导致VCO实际输出频率偏高,最终让PLL输出的系统时钟(以及APB1定时器时钟)比预期的48MHz更快,这就是定时器周期从20ms变成19.7ms的原因。
为什么设置VOS模式1(VOS1)能让PLL正常工作
CubeMX配置的VOS1模式(VOS=10)提供了更高的内核电压(~1.1V),足以保证VCO稳定锁定到288MHz的目标频率,PLL输出的48MHz系统时钟也就精准无误,定时器周期自然符合预期。
另外还要注意:你的代码里手动设置了FLASH->ACR |= 0x01(FLASH等待周期LATENCY=1),这个值需要和VOS模式匹配——不同电压下FLASH的访问速度不同,错误的等待周期会导致CPU读取FLASH时插入不必要的等待,甚至引发系统不稳定。CubeMX会自动根据你选择的VOS和时钟配置匹配正确的LATENCY,这也是它强制生成VOS配置代码的原因之一。
总结:VOS配置的必要性
哪怕目标时钟频率没超过VOS模式的最大值,正确配置VOS也是必须的:
- 确保PLL的VCO能稳定工作在目标频率,避免时钟偏差
- 为后续时钟频率升级预留空间(比如你之后想把系统时钟升到180MHz,VOS1模式刚好支持)
- 配合FLASH等待周期,保证系统整体运行稳定
附上你的PLL配置代码(格式化后):
void sys_clock_init(void){ int a; //Sets the wait states to 1 FLASH->ACR |= 0x01; a = FLASH->ACR; //Small delay //Enables the power interface (for the power controller) RCC->APB1ENR |= RCC_APB1ENR_PWREN; a = RCC->APB1ENR; //Small delay //Clears the bits for the voltage scaling PWR->CR1 &= ~(PWR_CR1_VOS); //Sets the voltage scaling mode to 3, VOS = 0x1 = b1 PWR->CR1 |= PWR_CR1_VOS_0; a = PWR->CR1; //Small delay //Makes HSI the source of the PLL RCC->PLLCFGR &= ~(0x400000); //Clears the bits for the different factors RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLM); //Sets the PLLM = 0x08 = b100 RCC->PLLCFGR |= (RCC_PLLCFGR_PLLM_3); //Clears the PLLN bits RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLN); //Sets PLLN = 0x90 = b10010000 RCC->PLLCFGR |=(RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_4); //Clears the PLLP bits RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLP); //Sets the PLLP = 0x02 = b10 RCC->PLLCFGR |=(RCC_PLLCFGR_PLLP_1); //Clears the PPRE1 bits RCC->CFGR &= ~(RCC_CFGR_PPRE1_2 | RCC_CFGR_PPRE1_1 | RCC_CFGR_PPRE1_0); //Set bit PPRE1 = 0x02 = b100 RCC->CFGR |= (RCC_CFGR_PPRE1_2);// | RCC_CFGR_PPRE1_0); //Turns the PLL ON RCC->CR |= RCC_CR_PLLON; //Waits for the PLL to be ready while(!((RCC->CR & RCC_CR_PLLRDY) == RCC_CR_PLLRDY)); //Clears the switch bits RCC->CFGR &= ~(RCC_CFGR_SW); //Set the PLL as the System Clock RCC->CFGR |= (RCC_CFGR_SW_1); }
内容的提问来源于stack exchange,提问作者Pedro Santos

