时钟配置分析
SystemInit
SystemInith函数默认在Reset_Handler中断处理函数下调用,具体代码如下:
c
void SystemInit (void)
{
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* Configure the Vector Table location -------------------------------------*/
#if defined(USER_VECT_TAB_ADDRESS)
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#endif /* USER_VECT_TAB_ADDRESS */
}
主要完成ExtSRAM的初始化,中断向量表寄存器的初始化;
寄存器数值
在这个函数中并没有对时钟进行配置,此时时钟的相关寄存器数值如下所示。
-
CR Clock control时钟控制寄存器
-
数值:0x0000_5583(0101 0101 1000 0011)
-
寄存器含义

-
目前HSI时钟准备就绪,且处于开启模式,内部时钟HSI校准的数值为0x55和0x4;
-
-
CFGR 时钟配置寄存器
-
数值:0x0
-
寄存器含义

-
含义:MCO为0代表没有时钟输出,OTGFSPRE代表PLL时钟1.5倍预分频作为USB时钟,PLL 2倍频输出,HSE不分频,HSI振荡器时钟经2分频后作为PLL输入时钟,PCLK2 2分频后作为ADC时钟,APB1/2使用HCLK不分频,AHB使用SYSCLK不分频,HSI作为系统时钟;
-
经过上述配置后,当前PLL时钟为8M,AHB = APB1 = APB2 = 8M, ADC=4M USB=12M
-
-
CIR 时钟中断寄存器
-
数值: 0x0
-
寄存器含义:

-
所有时钟中断均处于关闭状态;
-
-
APB2/1RSTR 外设复位寄存器
- 数值 0x0
- 所有外设复位无效;
-
AHBEMR 外设使能寄存器
-
数值:0x0000 0014(0001 0100)
-
寄存器

-
睡眠模式时闪存接口电路时钟开启,睡眠模式时SRAM时钟开启;
-
-
APB2/1ENR
- 数值:0x0
- 含义:APB1/2下面的外设时钟均处于关闭状态;
-
BDCR 备份域控制寄存器
-
数值:0x0
-
寄存器

-
备份域软件复位未激活,RTC时钟关闭,RTC无时钟,LSE时钟未被旁路,外部32kHz振荡器未就绪,外部32kHz振荡器关闭;
-
-
CSR 控制状态寄存器
-
数值: 0x1C000000(0001 1100)
-
寄存器
-

- 可以查看 低功耗管理复位,看门狗和软件复位,上电/掉电复位、NRST复位,LSI关闭,不处于就绪态;
时钟树状态

main函数下的时钟配置
STM32CubeMX时钟配置

代码
在HAL库中,通过在main函数中调用SystemClock_Config()函数对时钟进行配置,该函数代码如下:
c
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0}; //RCC 内部/外部时钟配置结构体
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; //AHB APB总线时钟配置结构体
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
/* 选择HSE作为主时钟、HSE不分频、打开HSI时钟、打开PLL,PLL时钟源选择HSE,PLL的系数设置为9 */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; //时钟源选择HSE 0x00000001U
RCC_OscInitStruct.HSEState = RCC_HSE_ON; //激活HSE 0x00010000
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; //HSE预分频0
RCC_OscInitStruct.HSIState = RCC_HSI_ON; //0x00000001
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; //0x00000002U
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; //0x00010000
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; //0x001C0000
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) //调用HAL_RCC_OscConfig函数
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) //调用HAL_RCC_OscConfig函数
{
Error_Handler();
}
}
-
首先定义两个结构体,RCC_OscInitTypeDef结构体用于描述RCC 内部/外部时钟的配置情况,RCC_ClkInitTypeDef结构体用于描述AHB APB总线时钟的配置情况;
-
RCC_OscInitTypeDef结构体
ctypedef struct { uint32_t OscillatorType; //时钟的类型,可以选择HSE HSI uint32_t HSEState; //HSE配置的状态 uint32_t HSEPredivValue; //HSE预分配系数 uint32_t LSEState; //LSE配置的状态 uint32_t HSIState; //HSI配置的状态 uint32_t HSICalibrationValue; //HSI校准的数值 uint32_t LSIState; //LSI配置的状态 RCC_PLLInitTypeDef PLL; //PLL的系数 } RCC_OscInitTypeDef; -
RCC_ClkInitTypeDef结构体
ctypedef struct { uint32_t ClockType; //要配置的时钟类型 uint32_t SYSCLKSource; //系统时钟源的选职责 uint32_t AHBCLKDivider; //AHB的分频系数 uint32_t APB1CLKDivider; //APB1的分配系数 uint32_t APB2CLKDivider; //APB2的分频系数 } RCC_ClkInitTypeDef;
-
-
调用HAL_RCC_OscConfig函数传入RCC_OscInitStruct结构体,配置RCC的内部和外部时钟,该函数的执行过程大概包括以下几部分:
- HSE时钟的配置,将HSEState的状态写入RCC_CR寄存器的第16位,等待HSERDY;
- HSI时钟配置, 将HSIState的状态写入RCC_CR寄存器的第0位,等待HSERDY;
- LSI时钟配置,将LSIState的状态写入RCC_CSR寄存器的第0位,等待HSERDY;
- LSE时钟配置,将LSIState的状态写入RCC_BDCR寄存器的第0位,等待HSERDY;
- PLL配置,查看PLL时钟源选择,如果是HSE,则配置RCC_CFGR寄存器的第17位,然后再配置PLL的倍频系数,注意在配置PLL时要先关闭PLL,配置完成后使能PLL;
-
调用HAL_RCC_ClockConfig函数传入RCC_ClkInitStruct结构体,配置AHB APB的时钟,执行过程大概包括以下几部分:
-
HCLK配置,AHB预分配设置(设置不分频),
-
SYSCLK配置,选择RCC_SYSCLKSOURCE_PLLCLK作为系统时钟。配置RCC_CFGR的0和1位;
-
APB1和APB2的分频配置,操作RCC_CFGR的[8:10]和[11:13];
-
更新全局变量SystemCoreClock
cSystemCoreClock = HAL_RCC_GetSysClockFreq() >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos]; //72,000,000(72Mhz) -
配置HAL库的基础时钟源;
-
寄存器数值
- CR Clock control时钟控制寄存器数值:0x03035583 (0011 0000 0011 0101 0101 1000 0011)
- CFGR 时钟配置寄存器数值:0x001d040a(0001 1101 0000 0100 0000 1010)
- AHBENR数值:0x00000014
- APB1ENR数值:0x00000001
- APB2ENR数值:0x10000004
时钟树状态
函数执行完毕后时钟树的状态如下所示,与STM32CubeMX设置的状态一致,当前系统时钟72M,由HSE进行9倍频得到。

自举程序介绍
功能概述

在芯片上电启动过程中,如果BOOT引脚选择为从System Memnory区域启动,则代码执行的PC指针会指向1FFF F000,该区域存储的是ST原厂在芯片生产写入的固定BootLoader,该bootloader可以实现flash的烧写,通过串口/USB等接口将二进制代码烧写至FLASH。具体的执行过程如下所示。

该Bootloader首先会初始化用到的外设,包括时钟、GPIO、IWDG和SysTick。然后进入一个循环,等到是否接收到0x7F 信息,如果接收到0x7F,则关闭所有中断源,配置使用到的串口1,进入命令执行的循环,目前支持的命令如下所示。

基本上命令发送过去之后都会收到来自STM32的回应,如果是ACK命令,对应的十六进制为0x79,如果是NACK命令,对应的十六进制为0x1F。
连接设置
如果要与该Bootloader进行对话,首先要准备一个串口,串口的配置如下图所示,波特率建议选择9600,选择偶校验,数据为8,一个停止位。接收区和发送区设置为HEX显示。

指令实践
启动交互
输入: 7F
输出: 79
Get命令
输入: 00+FF
输出: 79 0B 22 00 01 02 11 21 31 43 63 73 82 92 79
- 版本:0x22
- 支持的命令:0x00 0x01 0x02 0x11 0x21 0x31 0x43 0x44 0x63 0x73 0x82 0x92
Get Version & Read Protection Status
输入: 01 + FE
输出: 79 22 00 00 79
- 版本: 0x22
- 读保护状态: 0x00 0x00 保持与通用自举程序协议的兼容性
Get ID
输入: 02 + FD
输出: 79 01 04 14 79
- 芯片ID : 0x04 0x14
Read Memory
输入:11 + EE
输出: 79
输入:08 00 29 60 41 (addr : 0x08002960)
输出: 79
输入:07 F8 (size)
输出:79 00 12 7A 00 00 00 00 00
- 读取地址0x08002960处的8个字节的数据,返回数值为 00 12 7A 00 00 00 00 00,与实际数值一致;
- 命令框图

Go
输入:21 DE
输出: 79
输入:08 00 00 00 08
输出:79
-
跳转到地址0x08000004处执行代码;
-
注意要重新设置向量表寄存器的数值,方法如下,否则代码无法执行,PC指向又回到0x1Fxxxxxx区域。
c- /* #define USER_VECT_TAB_ADDRESS */ + #define USER_VECT_TAB_ADDRESS /* Configure the Vector Table location -------------------------------------*/ #if defined(USER_VECT_TAB_ADDRESS) SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */ #endif /* USER_VECT_TAB_ADDRESS */
其他与数据读写相关的命令
-
Write Memory 命令用于将数据写入 RAM、Flash 或选项字节区域的任意有效存储器地址;
-
Erase Memory 命令擦除 Flash 页面;
输入:43 BC 输出:79 输入:FF 00擦除前:

擦除后:

-
Extended Erase Memory 命令使用双字节寻址模式擦除 Flash 页面;
-
其他关闭/使能读写保护的命令;
参考
AN3155
AN2606 《Introduction to system memory boot mode on STM32 MCUs》