置位函数:
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
第一个参数选择外设中所要修改的寄存器,第二个参数写需要设定的值,以或运算的方式修改寄存器的值;
无用操作:
#define UNUSED(X) (void)X /* To avoid gcc/g++ warnings */ 强制类型转换,如果只是定义了X而不使用编译器就会报错,使用该宏定义就不会报错;
内核中的硬件计数器 Systick:
SysTick是内核定时器用于计时其不能作为时钟源使用它只是一个集成在内核中的硬件计数器。HSI是片上内部晶振用于提供时钟;
上电复位 → HSI硬件自动启动 → 内核获得时钟 → 执行初始化代码 → SysTick 配置启动 → (可选)切换时钟源到 HSE+PLL
Pos和Msk:
RCC_CFGR_PPRE1_Pos 是定义在寄存器CFGR中PPRE1位段的起始偏移位置,是计算Msk的基础,也是新值左移的偏移量;
RCC_CFGR_PPRE1_Msk 是定义在寄存器CFGR中PPRE1位段的掩码范围,由Pos+位宽推导而来,用于清0位段原有值,是除位段位置是1其余位都为0的值;
Debug中的值与宏定义的值不同:
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
宏定义RCC_HCLK_DIV4 = 0x1400U是带 PPRE1 位段偏移的寄存器写入值",用于直接操作RCC->CFGR;
在Debug时显示APB1CLKDivider = 0x08是 "无偏移的分频原始编码值",用于结构体存储或逻辑判断,用来存储分频系数的原始编码;
二者只是 "同一分频规则的不同表示形式",并非赋值错误,是 STM32 库为了 "易用性 + 寄存器操作安全性" 设计的分层语义。
简单来说:调试看到的0x08是 "分频的编码代号",RCC_HCLK_DIV4=0x1400U是 "这个代号对应的寄存器实际写入值",代码会在配置寄存器时完成二者的转换,真正写入到寄存器中的是带偏移的值;
在配置USART中遇到的问题:
1.重定向printf与scanf函数:
用USART发送或接收数据包括:1.中断服务函数接收或发送 2.重定向printf与scanf函数然后使用这两个函数;
但是不能同时使用否则会冲突,比如同时使用中断和printf函数重定向发送数据;
2.stm32的串口外设输出的是TTL电平,要通过电平转换芯片RC340将TTL电平转换成PC端可识别的RS232电平才能正确显示
到上位机,注意究竟是哪一个串口外设连接到了电平转换芯片上;
GPIO_InitTypeDef配置结构体参数:
推挽输出:
输出高电平--主动输出VCC 输出低电平--主动拉到GND
开漏输出:(仅拉低、需外部上拉)
输出高电平--悬空(需外部上拉->高电平) 输出低电平--主动拉到GND
上拉电阻:IO口默认电平为高电平
下拉电阻:IO口默认电平为低电平
io结构体配置的频率是该io口的频率上限,具体频率由所接外设决定;
对IO口的配置可以使用库函数或直接用GPIO_TypeDef结构体操作控制寄存器;GPIO_InitTypeDef 本身不直接 "控制" IO 寄存器,
它只是一个 "配置参数的容器"。真正控制寄存器的,是你调用的初始化函数(如标准库的 GPIO_Init()、HAL 库的 HAL_GPIO_Init())。
简单说:GPIO_InitTypeDef 是 "给寄存器配置写好的'参数清单'",而 GPIO_TypeDef 是 "寄存器本身的地址映射"------ 前者是 "参数集合",
后者是 "操作对象",两者本质完全不同。
复用模式下,引脚的输入 / 输出方向由外设决定,GPIO_OType 只影响输出方向的驱动方式,不影响输入方向的功能。