库函数怎么配置寄存器
095.1.6库函数怎么配置寄存器
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
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)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
一、库函数配置寄存器
1.1 寄存器配置
以打开 HSE 为例,代码会逐层展开为:
SET_BIT(RCC->CR, RCC_CR_HSEON);
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
#define RCC_CR_HSEON_Pos (16U)
#define RCC_CR_HSEON_Msk (0x1UL << RCC_CR_HSEON_Pos)
#define RCC_CR_HSEON RCC_CR_HSEON_Msk
代入宏以后,核心运算就是:
RCC->CR |= (1UL << 16);
1UL << 16 的结果为:
二进制:0000 0000 0000 0001 0000 0000 0000 0000
十六进制:0x0001 0000
位号:bit16(若按“第几个”从 1 数,则是第 17 位)
按位或 |= 能把 bit16 置 1,同时保持其他位的当前值。它本质上是“先读整个寄存器、按位或、再写回”的读—改—写操作。对硬件自动变化的状态位、写 1 清零位或并发访问场景,不能不加分析地套用这种形式,必须服从手册规定。
常见位操作宏:
| 宏/写法 | 等价含义 | 作用 |
|---|---|---|
SET_BIT(REG, MASK) | `REG | = MASK` |
CLEAR_BIT(REG, MASK) | REG &= ~MASK | 把掩码覆盖的位清为 0 |
READ_BIT(REG, MASK) | REG & MASK | 读取指定位置;结果不一定等于 1 |
MODIFY_REG(REG,C,S) | 先清 C 再置 S | 修改一个多位字段而保留其余位 |
1.2 寄存器手册
STM32F103 是 32 位 MCU,但“所有寄存器都是 32 位”并不严谨。外设通常通过 32 位地址映射,参考手册也常用 32 位寄存器图表示;某些寄存器只有低 16 位实现,某些位保留,访问宽度也应按手册要求执行。
RCC 时钟控制寄存器 RCC_CR 的关键位如下:
| 位 | 名称 | 访问 | 含义 |
|---|---|---|---|
| 0 | HSION | 读写 | 1:打开内部 8 MHz HSI |
| 1 | HSIRDY | 只读 | 1:HSI 已稳定 |
| 7:3 | HSITRIM | 读写 | HSI 用户微调值 |
| 15:8 | HSICAL | 只读 | HSI 出厂校准值 |
| 16 | HSEON | 读写 | 1:打开 HSE |
| 17 | HSERDY | 只读 | 1:HSE 已稳定 |
| 18 | HSEBYP | 读写 | 1:外部有源时钟旁路;0:晶体/谐振器模式 |
| 19 | CSSON | 读写 | 时钟安全系统使能 |
| 24 | PLLON | 读写 | 1:打开 PLL |
| 25 | PLLRDY | 只读 | 1:PLL 已锁定并稳定 |
HSEON=1 只是发起振荡器启动;代码还应等待 HSERDY=1。若晶体未焊好、负载不匹配或超时,HAL 会返回错误,应用层随后进入 Error_Handler()。
1.3 库函数意义
库函数不是“绕过寄存器”,而是把正确的寄存器配置顺序封装起来:
flowchart LR
APP[应用代码] --> API[HAL_RCC_OscConfig]
API --> CHECK[参数检查/状态判断]
CHECK --> BITS[SET_BIT / CLEAR_BIT]
BITS --> RCCCR[RCC_CR]
RCCCR --> OSC[HSE/PLL 电路]
OSC --> FLAG[HSERDY/PLLRDY]
FLAG --> API
封装带来的价值包括:统一接口、参数合法性检查、等待就绪与超时处理、跨同系列型号复用。代价是代码层次更深、占用量通常大于极简寄存器代码。调试时仍需会沿调用链追到寄存器。
1.4 RCC 寄存器描述
RCC 负责三个彼此相关但不同的动作:
- 时钟源与系统分频:
CR、CFGR等寄存器决定 HSI/HSE/PLL 和各总线频率。 - 外设时钟使能:
AHBENR、APB1ENR、APB2ENR的位像时钟门,打开后外设才获得时钟。 - 外设复位:
APB1RSTR、APB2RSTR等寄存器用相应位把外设保持在复位状态;通常置 1 后还要清 0,才能释放复位。
时钟使能与复位不要混淆。例如 GPIOB 初始化前常见的是设置 RCC_APB2ENR.IOPBEN,不是持续设置 RCC_APB2RSTR.IOPBRST。
1.4.1 APB2外设复位寄存器
STM32F103 高密度器件的 RCC_APB2RSTR 是 APB2 外设复位控制寄存器。常用位如下:
| 位 | 名称 | 对应模块 | 写 1 的效果 |
|---|---|---|---|
| 0 | AFIORST | 复用功能 I/O | 复位 AFIO |
| 2 | IOPARST | GPIOA | 复位 GPIOA |
| 3 | IOPBRST | GPIOB | 复位 GPIOB |
| 4 | IOPCRST | GPIOC | 复位 GPIOC |
| 5 | IOPDRST | GPIOD | 复位 GPIOD |
| 6 | IOPERST | GPIOE | 复位 GPIOE |
| 7 | IOPFRST | GPIOF | 复位 GPIOF |
| 8 | IOPGRST | GPIOG | 复位 GPIOG |
| 9 | ADC1RST | ADC1 | 复位 ADC1 |
| 10 | ADC2RST | ADC2 | 复位 ADC2 |
| 11 | TIM1RST | 高级定时器 1 | 复位 TIM1 |
| 12 | SPI1RST | SPI1 | 复位 SPI1 |
| 13 | TIM8RST | 高级定时器 8 | 复位 TIM8 |
| 14 | USART1RST | USART1 | 复位 USART1 |
| 15 | ADC3RST | ADC3 | 复位 ADC3 |
bit1 和 bit31:16 为保留位,必须保持复位值。ADC3、TIM8 等模块只在具有相应外设的型号上有效;STM32F103ZET6 属于高密度产品,具备 ADC3 和 TIM8。换用其他容量或产品线时要查对应型号章节,不能直接套用这张表。
典型的软件复位操作是脉冲式的:
SET_BIT(RCC->APB2RSTR, RCC_APB2RSTR_IOPBRST); /* 使 GPIOB 进入复位 */
CLEAR_BIT(RCC->APB2RSTR, RCC_APB2RSTR_IOPBRST); /* 释放 GPIOB 复位 */
二、结束
2.1 图形化配置与代码生成
图形界面每次生成代码后,应检查 .ioc、初始化函数和用户代码保护区。想知道一个选项影响了什么,最有效的方法是保存一份可编译基线,只改一个选项,再比较两个工程。
这种“单变量实验”能建立清晰对应关系。例如只关闭 LSE,差异通常会集中在振荡器类型、LSE 状态、RTC 时钟选择和 rtc.c/.h 是否生成等位置。
2.2 代码对比工具的使用方法
安全做法是把两个独立副本放在不含中文和特殊字符的短路径下,并排比较同名文件。也可以使用 Git:每次配置前提交一次,生成代码后运行 git diff。这样不必手工复制大量目录,也能准确还原每次变化。
比较时先过滤 Debug、Objects、构建产物与 IDE 缓存,优先关注:
.ioc配置;Core/Inc、Core/Src;- 工程描述和链接脚本;
- 新增或删除的外设初始化文件。
2.3 例题1: 如何对比新旧代码差异
以启用 RTC 与 LSE 为例,可按以下顺序分析差异:
.ioc是否新增 RTC,并把 LSE 设为晶体模式;main.c是否新增#include "rtc.h"与MX_RTC_Init();- 是否出现完整的
rtc.c、rtc.h; SystemClock_Config()中的振荡器与 RTC 时钟源字段是否变化;- 工程配置是否把新增文件加入编译。
一次只改变一个图形选项,差异才容易归因。若同时改变时钟、GPIO 和 RTC,工具虽然能显示不同,却很难判断每一行由哪个选择产生。
2.4 Beyond Compare软件介绍
Beyond Compare 可以进行目录和文本并排比较,颜色用于标识新增、删除或不同内容。使用时应安装并遵守其许可条款;也可以使用 Git、VS Code 的 Select for Compare、WinMerge 等合法工具完成同类任务。
比较结果本身不是错误结论:时间戳、自动排序、生成器版本变化也会造成差异,仍需结合 .ioc 和代码语义判断。
2.5 代码对比工具的其他应用场景
- 自己的工程无法运行时,与课程中可运行的工程对比,定位漏掉的初始化、宏定义或中断入口。
- 修改 CubeMX 配置后,检查用户代码是否仍位于保护区内。
- 升级 HAL 或 IDE 后,识别生成代码和库版本变化。
- 代码移植到 Keil 时,核对文件、包含路径、宏和启动文件是否齐全。
对比工具负责指出“哪里不同”,编译器、调试器、寄存器状态和原理图负责回答“哪一个差异导致了问题”。