NUCLEO板载STM32F401芯片的HSE

编程入门 行业动态 更新时间:2024-10-27 14:18:50

NUCLEO<a href=https://www.elefans.com/category/jswz/34/1735294.html style=板载STM32F401芯片的HSE"/>

NUCLEO板载STM32F401芯片的HSE

写在前面

时钟配置很容易走弯路,因为Reference Manual上所提及的部分不足以覆盖所有所需注意的点。此处列举两个我踩了坑的地方:

  1. 时钟的配置必须在SystemInit阶段完成,而不能在main函数中完成,否则会导致USART输出乱码(虽然LED频率似乎是正确的)。
  2. 时钟使能后要循环等待直至其稳定,否则会产生奇怪影响(未等待HSE稳定即启动PLL,会导致系统时钟从配置好的84MHz变成96MHz)。

除此之外还有一些其他莫名其妙的坑点,手册RM0368里的时钟配置流程没有介绍。不过这其实反映出我的手册阅读方法可能存在问题

整个配置过程分为4个步骤:寻找参考,配置HSE、配置PLL、切换时钟源。文中会含有大量的原理图与手册截图,可能需要读者具有一定的英语水平。

寻找参考

对于嵌入式编程而言,芯片、开发板的手册以及开发板的原理图是至关重要的。这些参考材料一般可从购买渠道获取,或是访问相应厂商的官网获取。

例如,我所使用的NUCLEO-F401RE开发板是ST公司的产品,进入ST官网后,点击左上角放大镜输入“NUCLEO-F401RE”即可搜索相关资源。在开发板对应页面中,在下层导航栏中点击Documentation可获取开发板手册,点击CAD Resources可获取开发板原理图与PCB图。

而板上芯片STM32F401RET6也有对应的手册,一般常用的有Reference Manual与Programming Manual,设计外围电路时也会用到Product Specification。

配置HSE

HSE即外部高速晶振,启动HSE的前提是板上存在外部晶振。这一步需要查看所用开发板的原理图与设计手册(我所用的NUCLEO开发板手册号为UM1724),检查外部晶振的参数与连接方式。

UM1724所提及的晶振中,只有第一个是可以直接使用的,其他都不是NUCLEO开发板自带的晶振。经过实板检查,下面罗列的三个配置即是板上默认配置。

MCO即Microcontroller Clock Output,即主芯片STM32F401RET6所用的时钟信号来自板载ST-Link上与ST-Link的STM32F103CBT6直接相连的8MHz晶振。

由于板上X3默认是未焊接的空焊盘,所以MCO可以直接视作HSE时钟信号。

此处有两个需要注意的地方:

  1. 断路器SB50处的“Default: closed”是指其默认连通,而非默认断开。检查开发板背面可发现对应断路器焊有连接件。
  2. RM0368手册中的HSE bypass有一定迷惑性:

    此处的HI-Z是指高阻态,但原理图中作为OSC_OUT的PF1/PD1/PH1管脚是否是高阻态呢?这需要一些数字电路知识,我暂时还没有学。不过,试验表明设置旁路HSE也没有问题。

手册RM0368上提供了一些配置HSE的指导:

中断暂时不用管,上述文字传达给我们两个信息:

  1. RCC_CR的HSEON位控制HSE的使能与否。
  2. RCC_CR的HSERDY位由硬件设置,作为HSE是否达到稳定的指标。

所以配置HSE的代码如下:

RCC->CR |= RCC_CR_HSEON;
while((RCC->CR & RCC_CR_HSERDY) == RESET);

while语句用于等待HSE就绪。要判断HSE就绪有很多指标可以看,但CR寄存器的HSERDY位是最基本的指标。某些写法会用到TIMEOUT,使得芯片在HSE无法就绪时使用HSI。这固然是合乎嵌入式编程要求的,我后续会把这种情况加入进去,现在先把安全性暂时搁置。

这一步尚且可以靠ST官网的资源解决问题,接下来才是重头戏。

配置PLL

PLL(锁相环)分为两种:主PLL和专用PLL(PLLI2S),后者用于为高质量音频传输提供精确时钟。我们暂时只使用主PLL,其特性如下:

  1. 以HSI或HSE的时钟信号为基础,分两条线输出时钟信号:第一条用于生成高速系统时钟(84MHz),第二条用于生成USB接口时钟(48MHz)等。
  2. 需要四个参数:PLL_M,PLL_N,PLL_P,PLL_Q。

用ST官方提供的开发工具STM32CUBEMX可实现可视化的时钟配置。此处不赘述该工具的使用方法,只需根据自己所用的硬件参数设置好HSE的输入频率,然后调整可选输入框,直至所有通道均达到蓝色小字所标明的最大值即可。

此处经调整得出 M = 8,N = 336,P = 4,Q = 7。然后通过阅读手册确定具体的配置方法:

查RCC_PLLCFGR可知其用于配置四个参数和PLL的时钟源(HSI或HSE),其中:

  1. 2 ≤ PLL_Q ≤ 15,且该值的配置必须保证经过Q这条线的所有时钟频率低于48MHz。
  2. PLL_P = 2, 4, 6, 8,置位为 (PLL_P >> 1) - 1。例如要配置其为8,则实际置位为0b11。
  3. 192 ≤ PLL_N ≤ 432,若要配置PLLI2S则有额外注意,此处略去。
  4. 2 ≤ PLL_M ≤ 63,若要配置PLLI2S则有额外注意,此处略去。

于是配置代码如下:

RCC->PLLCFGR = (8 << 0)                  // PLL_M = 8| (336 << 6)                // PLL_N = 336| (((4 >> 1) - 1) << 16)    // PLL_P = 4| (7 << 24)                 // PLL_Q = 7| RCC_PLLCFGR_PLLSRC_HSE;   // 设置HSE为PLL时钟源

查RCC_CFGR可知其用于配置分频和系统时钟切换,根据时钟树图我们可以确定:AHB和APB2不分频,APB1分频系数为2,查手册可得代码如下:

RCC->CFGR |= RCC_CFGR_HPRE_DIV1    // AHB: /1|  RCC_CFGR_PPRE1_DIV2   // APB1: /2|  RCC_CFGR_PPRE2_DIV1;  // APB2: /1

下一点是手册上没有提,但很容易想到的:启动PLL并等待其就绪。

RCC->CR |= RCC_CR_PLLON;
while((RCC->CR & RCC_CR_PLLRDY) == RESET);

切换时钟源

等待结束后什么都没有发生,时钟频率似乎还是HSI的16MHz。通过观察库自带的SystemCoreClockUpdate函数可以看出,系统判断此时使用哪个时钟的依据是RCC_CFGR的SWS位。所以切换时钟源这一步操作虽然看似无厘头,但其实也能够猜到几分。于是我直接进行如下切换:

RCC->CFGR &= ~(RCC_CFGR_SW);    // 清空时钟选择(实际上默认为HSI)
RCC->CFGR |= RCC_CFGR_SW_PLL;   // 选择PLL作为系统时钟源
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);    // 等待PLL被事实上切换

然后寄了,看别人的代码里在这一步之前需要加上:

FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;

这堆玩意完全看不懂。查手册发现了这个:

鉴定为:默认参数无法适应已变化的时钟频率。经过PLL配置,HCLK已经由16MHz变为84MHz。

按照这上面的要求,还需要做两件事:

  1. 调整VOS的置位。查手册可知VOS在PWR_CR中。要设置PWR_CR,需先在RCC中使能PWR(然而这一步跳过似乎也没有引起什么问题)。
  2. 调整wait states的数量。为了保证低电压也能用,还是设置5WS比较保险。不知道是否会影响芯片的性能,以后可能会回来进行修改。

这一段后面紧跟着就是CPU升频所需的操作:

总结来说就是:设置合适的LATENCY(即wait states的数量),再修改RCC_CFGR的SW位。

RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR &= ~(PWR_CR_VOS);
PWR->CR |= PWR_CR_VOS_1;    // 设置VOS[1:0]为0x10,以适应84MHz时钟
FLASH->ACR |= FLASH_ACR_LATENCY_5WS; // 设置6个wait states,以确保低电压下仍能适应84MHz

然而还有一个疑问:若仿照别人的代码将DCEN、ICEN和PRFTEN全部置位,则接收机所接收到的油门和方向PWM脉宽均会长1us(两者都是右摇杆控制)。可能与手柄的微调按钮有关。但这些都是后话了。

总结

全部代码如下:

void SystemInit(void)
{...// 在SystemInit里随便找个位置放进去RCC->CR |= RCC_CR_HSEON;while((RCC->CR & RCC_CR_HSERDY) == RESET);RCC->PLLCFGR = (8 << 0) | (336 << 6) | (((4 >> 1) - 1) << 16)| (7 << 24) | RCC_PLLCFGR_PLLSRC_HSE;RCC->CFGR |= RCC_CFGR_HPRE_DIV1|  RCC_CFGR_PPRE1_DIV2|  RCC_CFGR_PPRE2_DIV1;RCC->CR |= RCC_CR_PLLON;while((RCC->CR & RCC_CR_PLLRDY) == RESET);RCC->APB1ENR |= RCC_APB1ENR_PWREN;PWR->CR &= ~(PWR_CR_VOS);PWR->CR |= PWR_CR_VOS_1;FLASH->ACR |= FLASH_ACR_LATENCY_5WS/*| FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN*/;RCC->CFGR &= ~(RCC_CFGR_SW);RCC->CFGR |= RCC_CFGR_SW_PLL;while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);...
}

需要手册→其他资料→手册式的迭代才能彻底掌握芯片操控的底层逻辑。

更多推荐

NUCLEO板载STM32F401芯片的HSE

本文发布于:2024-03-05 14:38:53,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1712569.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:板载   芯片   NUCLEO   HSE

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!