datasheet

使用内部的MSI振荡器给STM32L476RG单片机提供80MHz的时钟

2018-08-19来源: eefocus关键字:MSI振荡器  STM32L476RG  时钟

#include

#include

 

// 1<=nus<=13107

void delay_us(uint16_t nus)

{

if ((RCC->APB1ENR2 & RCC_APB1ENR2_LPTIM2EN) == 0)

{

RCC->APB1ENR2 |= RCC_APB1ENR2_LPTIM2EN;

LPTIM2->CFGR = 4 << LPTIM_CFGR_PRESC_Pos; // 80MHz/16=5MHz

LPTIM2->CR = LPTIM_CR_ENABLE;

}

LPTIM2->ARR = nus * 5 - 1;

LPTIM2->CR |= LPTIM_CR_SNGSTRT;

while ((LPTIM2->ISR & LPTIM_ISR_ARRM) == 0);

LPTIM2->ICR = LPTIM_ICR_ARRMCF;

}

 

int fputc(int ch, FILE *fp)

{

if (fp == stdout)

{

if (ch == '\n')

{

while ((USART2->ISR & USART_ISR_TXE) == 0);

USART2->TDR = '\r';

}

while ((USART2->ISR & USART_ISR_TXE) == 0);

USART2->TDR = ch;

}

return ch;

}

 

void set_clock(void)

{

RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;

PWR->CR1 |= PWR_CR1_DBP; // 允许访问备用区域寄存器

// 打开LSE

if ((RCC->BDCR & RCC_BDCR_LSEON) == 0)

{

RCC->BDCR |= RCC_BDCR_LSEON;

while ((RCC->BDCR & RCC_BDCR_LSERDY) == 0); // 等待LSE稳定

}

RCC->CR |= RCC_CR_MSIPLLEN; // MSI使用LSE校准MSI

// 配置PLL: MSI/M*N/R=4MHz/1*40/2=80MHz

RCC->PLLCFGR = RCC_PLLCFGR_PLLREN | (40 << RCC_PLLCFGR_PLLN_Pos) | RCC_PLLCFGR_PLLSRC_MSI;

RCC->CR |= RCC_CR_PLLON;

while ((RCC->CR & RCC_CR_PLLRDY) == 0); // 等待PLL稳定

// 根据参考手册3.3.3 Read access latency, Table 11. Number of wait states according to CPU clock (HCLK) frequency

// CPU时钟要达到64MHz以上, LATENCY必须为4WS

// 由PWR_CR1_VOS可知当前V_CORE为Range 1 (最高时钟频率80MHz)

FLASH->ACR |= FLASH_ACR_LATENCY_4WS; // 参阅: Increasing the CPU frequency

while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_4WS);

// 将PLL选作系统时钟

RCC->CFGR |= RCC_CFGR_SW;

while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS);

// 刚开始MSI还没有校准, 所以使用115200波特率时必须要延时, 否则前四个字符会乱码

// 延时5个字节的时间, 每个字节69.4us, 共347us

delay_us(347);

// 使用9600波特率时, 发送一个字符就要833.3us, 远远大于这个时间, 所以无需延时

}

 

int main(void)

{

set_clock();

// LED灯配置

RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;

GPIOA->MODER &= ~GPIO_MODER_MODE5_1;

GPIOA->BSRR = GPIO_BSRR_BS5; // LED灯亮

// 配置串口2

RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;

GPIOA->AFR[0] = 7 << GPIO_AFRL_AFSEL2_Pos;

GPIOA->MODER &= ~GPIO_MODER_MODE2_0;

//GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED2;

USART2->BRR = 80000000 / 115200; // 被除数为时钟频率, 除数为波特率

USART2->CR1 = USART_CR1_UE | USART_CR1_TE;

printf("ABCDEFGH\n");

printf("PWR->CR1=0x%08x\n", PWR->CR1);

printf("RCC->BDCR=0x%08x\n", RCC->BDCR);

printf("RCC->CFGR=0x%08x\n", RCC->CFGR);

printf("RCC->CR=0x%08x\n", RCC->CR);

printf("RCC->PLLCFGR=0x%08x\n", RCC->PLLCFGR);

printf("USART2->BRR=%d\n", USART2->BRR);

GPIOA->BRR = GPIO_BRR_BR5; // LED灯灭

while (1);

}

MSI晶振接到PLL上后会通过外部的LSE晶振进行自动校准。校准期间,不精准的MSI时钟频率被PLL放大之后就会显著地影响串口的时钟,导致串口字符乱码,因此时钟切换后串口发送第一个字符之前必须延时,等待MSI时钟校准。


解决串口乱码问题还有一个更简单的方法,无需延时。单片机内部还带有一个16MHz的HSI时钟,在RCC->CCIPR寄存器中将USART2的时钟由与MSI有关的APB1时钟切换到HSI时钟,就能彻底解决问题。


【程序2】


#include

#include

 

int fputc(int ch, FILE *fp)

{

if (fp == stdout)

{

if (ch == '\n')

{

while ((USART2->ISR & USART_ISR_TXE) == 0);

USART2->TDR = '\r';

}

while ((USART2->ISR & USART_ISR_TXE) == 0);

USART2->TDR = ch;

}

return ch;

}

 

void set_clock(void)

{

RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;

PWR->CR1 |= PWR_CR1_DBP; // 允许访问备用区域寄存器

// 打开LSE

if ((RCC->BDCR & RCC_BDCR_LSEON) == 0)

{

RCC->BDCR |= RCC_BDCR_LSEON;

while ((RCC->BDCR & RCC_BDCR_LSERDY) == 0); // 等待LSE稳定

}

RCC->CR |= RCC_CR_MSIPLLEN; // MSI使用LSE校准MSI

// 配置PLL: MSI/M*N/R=4MHz/1*40/2=80MHz

RCC->PLLCFGR = RCC_PLLCFGR_PLLREN | (40 << RCC_PLLCFGR_PLLN_Pos) | RCC_PLLCFGR_PLLSRC_MSI;

RCC->CR |= RCC_CR_PLLON;

while ((RCC->CR & RCC_CR_PLLRDY) == 0); // 等待PLL稳定

// 根据参考手册3.3.3 Read access latency, Table 11. Number of wait states according to CPU clock (HCLK) frequency

// CPU时钟要达到64MHz以上, LATENCY必须为4WS

// 由PWR_CR1_VOS可知当前V_CORE为Range 1 (最高时钟频率80MHz)

FLASH->ACR |= FLASH_ACR_LATENCY_4WS; // 参阅: Increasing the CPU frequency

while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_4WS);

// 将PLL选作系统时钟

RCC->CFGR |= RCC_CFGR_SW;

while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS);

}

 

int main(void)

{

set_clock();

// LED灯配置

RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;

GPIOA->MODER &= ~GPIO_MODER_MODE5_1;

GPIOA->BSRR = GPIO_BSRR_BS5; // LED灯亮

// 配置串口2

RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;

GPIOA->AFR[0] = 7 << GPIO_AFRL_AFSEL2_Pos;

GPIOA->MODER &= ~GPIO_MODER_MODE2_0;

//GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED2;

// 使用HSI16作为USART2的时钟可以避免乱码

RCC->CR |= RCC_CR_HSION;

while ((RCC->CR & RCC_CR_HSIRDY) == 0);

RCC->CCIPR = RCC_CCIPR_USART2SEL_1; // HSI16作为USART2的时钟

USART2->BRR = 16000000 / 115200; // 被除数为时钟频率, 除数为波特率

USART2->CR1 = USART_CR1_UE | USART_CR1_TE;

printf("ABCDEFGH\n");

printf("PWR->CR1=0x%08x\n", PWR->CR1);

printf("RCC->BDCR=0x%08x\n", RCC->BDCR);

printf("RCC->CFGR=0x%08x\n", RCC->CFGR);

printf("RCC->CR=0x%08x\n", RCC->CR);

printf("RCC->PLLCFGR=0x%08x\n", RCC->PLLCFGR);

printf("USART2->BRR=%d\n", USART2->BRR);

GPIOA->BRR = GPIO_BRR_BR5; // LED灯灭

while (1);

}


关键字:MSI振荡器  STM32L476RG  时钟

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/2018/ic-news081940832.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:stm32l476时钟设置
下一篇:STM3210X的外部时钟配置以及倍频的选择

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

基于STM32L476RG的SPI DMA的调试

一、硬件环境:1、NUCLEO-1476RG开发板2、nordic PCA10028开发板二、软件环境1、在STM32CubeMX中配置SPI基本参数和DMA参数;2、生成代码后,定义spi操作函数,如下:/* spi dma完成标记*/static uint8_t s_uSpi1DmaComplete = 1;void drv_spi_dma_write_read(uint8_t *v_puSpiData,uint8_t *v_puSpiRecvData,uint16_t v_uSpiDataLen){HAL_SPI_TransmitReceive_DMA(&hspi1,v_puSpiData
发表于 2018-08-19

STM32为什么必须先配置时钟

首先,任何外设都需要时钟,51单片机,stm32,430等等,因为寄存器是由D触发器组成的,往触发器里面写东西,前提条件是有时钟输入。51单片机不需要配置时钟,是因为一个时钟开了之后所有的功能都可以用了,而这个时钟是默认开启的,比如有一个水库,水库有很多个门,这些门默认是开启的,所以每个门都会出水,我们需要哪个门的水的时候可以直接用,但是也存在一个问题,其他没用到的门也在出水,即也在耗能。这里水库可以认为是能源,门可以认为是每个外设的使用状态,时钟可以认为是门的开关。stm32之所以是低功耗,他将所有的门都默认设置为disable,在你需要用哪个门的时候,开哪个门就可以,也就是说用到什么外设,只要打开对应外设的时钟就可以
发表于 2019-07-19

STM32总结之开启外设时钟

我们知道到,STM32的大多数外设,在使用前都要开启该外设的时钟。下面我们以STM32VET6指南者为例1.点亮LED灯实验时,用到了GPIOB,我们需要开启GPIOB的时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO,ENABLE);//开启GPIOB的时钟N2.使用按键的时候,不论是查询方式,还是中断方式,都用到了GPIOA,所以要开启GPIOA的时钟。但是按键查询方式和中断方式有点不一样。按键查询方式时,GPIO以如下方式开启时钟:    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//这里使用的时
发表于 2019-07-19

STM32查看各个时钟系统--硬件调试结果--附带外设关系图

实时硬件调试结果如下    
发表于 2019-07-19
STM32查看各个时钟系统--硬件调试结果--附带外设关系图

STM32之实时时钟和备份寄存器介绍

在本文中主要讲述RTC寄存器的配置问题。在配置RTC寄存器的时候必须配置RTC_CRL寄存器中的CNF位,使得RTC进入配置模式之后,才能写入RTC_PRL,RTC_CNT,PTR_ALR寄存器。另外很重要的是,在对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行,可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器。配置RTC寄存器的过程如下:1.查询RTOFF位,知道RTOFF的值为1.2.置CNF值为1,进入配置模式。3.对一个或者多个RTC寄存器进行写操作。4.清除CNF标志位,退出配置模式。5.查询RTOFF,直到RTOFF位变1,已
发表于 2019-07-18

编写bootloader(一):编写前的介绍及关闭看门狗和时钟的设置

    我们知道,U-Boot最大的作用就是启动内核。然而,启动内核之前,它也要做许多事情。接下来,我们就自己编写一个bootloader,让它执行启动内核的功能。    对于bootloader,我们知道,它的第一段代码就是Start.S,这是一个汇编文件,在这个文件里面,我们进行一些硬件的初始化,也就是bootloader初始化的第一阶段。在这个初始化里面,我们需要做以下事情:       1.关看门狗    2.设置时钟    3.初始化SDRAM    4.重定位代码对于上面的步骤
发表于 2019-07-17
编写bootloader(一):编写前的介绍及关闭看门狗和时钟的设置

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved