首先是修改主频的工程,我们主要的任务是研究一下system_stm32f10x.c和.h这两个文件,看一看这些代码是怎么运作的,顺便再利用里面给我们提供的宏,完成修改主频的任务
首先这两个system文件是用来配置系统时钟的,也就是配置RCC时钟树
左边是四个时钟源,HSI、HSE、LSE、LSI,用于提供时钟,右边是各个外设,就是使用时钟的地方,用的最多的就是AHB时钟,APB1时钟和APB2时钟,另外还有一些时钟,它们的来源不是AHB和APB,比如I2S的时钟,直接来源于SYSCLK,USB的时钟,直接来源于PLL,我们主要要关心的是这个外部8MHz晶振,它如何进行选择,如何倍频才能得到这个72MHz的SYSCLK,系统主频,然后系统主频如何分配才能得到AHB、APB1和APB2的时钟频率,一般默认晶振接的是8MHz,主频是72MHz,AHB和APB2是72MHz,APB1是36MHz,但其实这些并不绝对,可以根据需求进行更改。我们来看看这个文件怎么配置RCC时钟树
system这两个文件提供了两个外部可调用的函数和一个外部可调用的变量,两个函数是SystemInit()和SystemCoreClockUpdate(),一个变量是SystemCoreClock
SystemInit这个函数是用来配置时钟树的,也是整个文件最主要的东西,使用HSE配置主频为72MHz。并且这个函数在复位后,执行main函数之前会在启动文件里自动调用的。
变量SystemCoreClock表示主频率的值,我们想知道目前的主频是多少直接显示一下这个变量就可以了。
最后一个函数,SystemCoreClockUpdate()这个函数是用来更新上面这个变量的,因为这个变量只有最开始的一次赋值,之后如果我们改变了主频频率,这个值不会自动跟着变换,所以我们就要调用一下这个函数,根据当前的时钟配置更新上面这个变量。
下面这块就是用来更改主频的宏定义:
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
#endif
解除对应的注释来选择想要的系统主频,如果使用的是VL这些设备,也就是超值系列,那可选主频就只有两个,HSE的8M和24M,否则的话,可以选择剩下的主频,当前解除注释的是72M,所以主频默认就是72M,这里#if叫做预编译主要用来兼容不同型号的设备。我们简单修改一个宏定义是如何作用与电路的配置的呢
首先我们找到SystemInit函数,这是最先调用的函数,可以看到第一步是开启HSI也就是默认使用内部的8M晶振,之后的操作都是各种Reset,各种Disable作用就是恢复缺省配置,最后恢复完之后调用SystemClock函数,在这个函数里我们就能知道为什么改变宏定义就可以修改主频了
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
/* If none of the define above is enabled, the HSI is used as System clock
source (default after reset) */
}
这个函数其实就是一个分配函数,根据你定义的不同宏定义选择执行不同的配置函数,比如前面解除了这个72M的宏,那它就执行设置时钟到72M这个函数,根据所选的宏,执行不同的函数,我们当前解除的是72M的宏,就继续挖掘这个函数
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration —————————*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL */
/* Enable PLL */
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
到了这里才是正式的时钟配置部分,配置第一步是使能HSE,外部高速时钟,第二步是一个循环,等待HSERDY或者超时退出,接下来根据HSERDY标志位给HSEState置1或0,如果HSEState等于1,表示晶振启动成功进入if,配置HCLK、PCLK2和PCLK1的分频器,HCLK就是AHB的时钟,PCLK就是APB的时钟。函数里分频分别是1、1、2对应到时钟树就SystemClock是72M,AHB和APB2是72M,APB1是36M。跳过#ifdef里无关的内容,可以看到这里配置的是锁相环,PLLCLK = HSE * 9 = 72 MHz ,HSE是8M,锁相环选择9倍频,最终锁相环输出就是72M,之后就是使能锁相环,等待锁相环准备就绪,选择锁相环的输出作为系统时钟,最后等待锁相环成为系统时钟
其他的函数还有SetTo72,还有SetTo56的这里面的执行流程几乎是一样的主要区别就是锁相环的倍频。
最后总结一下:首先是SystemInit函数,进到函数首先启动HSI之后就是各种恢复缺省配置最后调用SetSysClock函数,SetSysClock是一个分配函数,根据我们前面解除注释的宏定义选择执行不同的配置函数,比如SetSysClockTo72、To56、To48等等,最后在这些函数里才是真正的配置,比如To72的配置是,选择HSE作为锁相环输入,之后锁相环进行9倍频再选择锁相环输出作为主频这样主频就是72M了。
接下来我们来完成代码,首先我们前面说过,有个变量可以显示当前主频,我们先显示一下看看:

{
OLED_ShowString(2, 1, “Runing”);
Delay_ms(500);
OLED_ShowString(2, 1, ” “);
Delay_ms(500);
}

接下来我们要实现睡眠模式+串口发送+接收,就是在串口收发的基础上加上低功耗的代码




接下来是停止模式的应用,停止模式只能通过外部中断触发(唤醒),所以和停止模式相关的代码肯定得用到外部中断
void PWR_BackupAccessCmd(FunctionalState NewState);//使能后备区域的访问
void PWR_PVDCmd(FunctionalState NewState);//配置PVD的阈值电压
void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel);//使能PVD功能
void PWR_WakeUpPinCmd(FunctionalState NewState);//使能位于PA0位置的WKUP引脚,这个配合待机模式使用,待机模式可以用WKUP引脚的上升沿唤醒,如果需要开启WKUP引脚唤醒功能的话,就得调用一下这个函数使能一下
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry);//进入停止模式,调用这个函数就可以进入停止模式了
void PWR_EnterSTANDBYMode(void);//进入待机模式,调用这个函数进入待机模式
FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG);//获取标志位
void PWR_ClearFlag(uint32_t PWR_FLAG);//清除标志位

接下来是今天最后一个代码待机模式+RTC实时时钟
RTC_SetAlarm(Alarm);
OLED_ShowNum(2, 6, Alarm, 10);
{
OLED_ShowNum(1, 6, RTC_GetCounter(), 10);
OLED_ShowNum(3, 6, RTC_GetFlagStatus(RTC_FLAG_ALR), 1);
OLED_ShowString(4, 1, “Running”);
Delay_ms(100);
OLED_ShowString(4, 1, ” “);
Delay_ms(100);
}
