STM32F103XX 05-时钟配置分析与自举程序

时钟配置分析

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结构体

      c 复制代码
      typedef 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结构体

      c 复制代码
      typedef 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

      c 复制代码
       SystemCoreClock = 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》

相关推荐
学嵌入式的小杨同学2 小时前
STM32 入门封神之路(四):GPIO 实战 + 寄存器深度拆解 ——LED 控制 + 按键检测全流程(含位操作 + 面试题)
stm32·单片机·嵌入式硬件·硬件架构·硬件工程·智能硬件·嵌入式实时数据库
撩妹小狗2 小时前
定时器PWM输出功能的使用
单片机·嵌入式硬件
xu_wenming3 小时前
跨文件数据共享模式:通过静态全局变量与访问函数结合
嵌入式硬件·mcu·物联网·设计规范
学嵌入式的小杨同学4 小时前
STM32 进阶封神之路(七):中断核心原理 + NVIC 深度解析 —— 从概念到寄存器配置(面试重点)
stm32·单片机·嵌入式硬件·mcu·硬件架构·pcb·嵌入式实时数据库
不吃鱼的羊4 小时前
CodeMeter Runtime Server was not found on this computter问题解决
单片机
蒙塔基的钢蛋儿4 小时前
使用STM32CUEBEIDE/S32DS 开发时,生成compile_commands.json 方便VSCODE智能提示
vscode·stm32·单片机·json
qq_402995755 小时前
RS485通信设计
stm32·单片机·mcu
电子科技圈5 小时前
IAR扩展嵌入式开发平台,推出面向安全关键型应用的长期支持(LTS)服务
嵌入式硬件·安全·设计模式·软件工程·代码规范·设计规范·代码复审
串口哑火达人5 小时前
(七)RT-Thread物联网实战--MQTT-cJSON-OneNET
c语言·单片机·嵌入式硬件·mcu·物联网