浏览代码

fix: 补充笔记07~13/19~20缺失的HAL库版代码

OpenCode 1 天之前
父节点
当前提交
386234d996

+ 30 - 10
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/07-I2C通信与EEPROM 24C02.md

@@ -381,18 +381,38 @@ int main(void)
 
 ---
 
+## 软件设计(HAL 库版 — 硬件 I2C)
+
+HAL 库使用结构体 `I2C_HandleTypeDef` 管理 I2C 外设,CubeMX 图形化配置后调用函数读写 EEPROM。
+
+```c
+// HAL 库 I2C 写 24C02(Mem_Write 自动处理 START+地址+数据+STOP)
+uint8_t data = 0x55;
+HAL_I2C_Mem_Write(&hi2c1, 0xA0, 0x00, I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
+// 参数: hi2c句柄, 设备地址(7位+写), 存储器地址, 地址宽度, 数据指针, 数据长度, 超时ms
+
+// 等待页写入完成(超时重试或轮询 ACK)
+HAL_Delay(10);
+
+// HAL 库 I2C 读 24C02
+uint8_t read_val;
+HAL_I2C_Mem_Read(&hi2c1, 0xA0, 0x00, I2C_MEMADD_SIZE_8BIT, &read_val, 1, 100);
+// 自动处理: START+写地址+寄存器地址+RESTART+读地址+数据+NACK+STOP
+
+printf("HAL I2C Read: 0x%02X\r\n", read_val);
+```
+
 ## 核心函数速查表
 
-| I2C 操作 | 函数 | 说明 |
-|----------|------|------|
-| 初始化 | `I2C_Init()` | PB10(SCL)+PB11(SDA) 开漏输出 |
-| 起始条件 | `I2C_Start()` | SCL高→SDA高→低切换 |
-| 停止条件 | `I2C_Stop()` | SCL高→SDA低→高切换 |
-| 发送字节 | `I2C_SendByte(byte)` | 8 位逐位发送,MSB first |
-| 读取字节 | `I2C_ReadByte()` | 8 位逐位读取 |
-| 等待应答 | `I2C_Wait4Ack()` | 返回 ACK(0) 或 NACK(1) |
-| 发应答 | `I2C_Ack()` | 拉低 SDA 表示继续接收 |
-| 发非应答 | `I2C_NAck()` | 释放 SDA 表示停止接收 |
+| I2C 操作 | 软件模拟 | HAL 库 |
+|----------|---------|--------|
+| 初始化 | `I2C_Init()` | `HAL_I2C_Init()` |
+| 写 EEPROM | `I2C_Start/SendByte/Stop` 组合 | `HAL_I2C_Mem_Write(addr, mem_addr, data, len, timeout)` |
+| 读 EEPROM | `Start+写地址+寄存器地址+RESTART+读地址+数据+NACK+Stop` | `HAL_I2C_Mem_Read(addr, mem_addr, data, len, timeout)` |
+| 发送字节 | `I2C_SendByte(byte)` | —(底层封装在 HAL 中) |
+| 读取字节 | `I2C_ReadByte()` | —(底层封装在 HAL 中) |
+| 等待应答 | `I2C_Wait4Ack()` | —(自动处理) |
+| 发应答/非应答 | `I2C_Ack()` / `I2C_NAck()` | —(自动处理) |
 
 ## 常见问题与避坑
 

+ 34 - 0
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/08-SysTick与通用定时器TIM.md

@@ -266,6 +266,40 @@ void TIM5_IRQHandler(void)
 
 ---
 
+## HAL 库版定时器
+
+HAL 库使用 `TIM_HandleTypeDef` 管理定时器,CubeMX 生成初始化代码后通过宏或 API 控制。
+
+```c
+// CubeMX 生成: tim.c 中的 MX_TIMx_Init()
+TIM_HandleTypeDef htim5;
+
+void MX_TIM5_Init(void)
+{
+    htim5.Instance = TIM5;
+    htim5.Init.Prescaler = 7200 - 1;          // PSC
+    htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
+    htim5.Init.Period = 10000 - 1;             // ARR
+    htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
+    htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
+    HAL_TIM_Base_Init(&htim5);
+}
+
+// HAL 库启动定时器中断
+HAL_TIM_Base_Start_IT(&htim5);
+// 不使用中断: HAL_TIM_Base_Start(&htim5);
+
+// HAL 库定时器中断回调(用户重写此函数)
+void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
+{
+    if (htim->Instance == TIM5)
+    {
+        static uint16_t tick = 0;
+        if (++tick >= 1000) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); tick = 0; }
+    }
+}
+```
+
 ## SysTick vs TIM 对比
 
 | 对比项 | SysTick | TIM 通用定时器 |

+ 33 - 10
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/09-TIM高级应用:PWM与输入捕获.md

@@ -301,18 +301,41 @@ void TIM2_IRQHandler(void)
 
 ---
 
+## HAL 库版 PWM
+
+HAL 库使用 `HAL_TIM_PWM_Start()` 启动 PWM,用宏 `__HAL_TIM_SET_COMPARE()` 修改占空比。
+
+```c
+// CubeMX 生成: MX_TIM5_Init() 配置 PSC/ARR 和 CH1 输出模式
+TIM_HandleTypeDef htim5;
+
+// 启动 PWM 输出
+HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_1);
+// HAL_TIMEx_PWMN_Start(&htim5, TIM_CHANNEL_1); // 互补输出(TIM1 高级定时器)
+
+// 修改占空比(HAL 宏,比直接写寄存器更可移植)
+__HAL_TIM_SET_COMPARE(&htim5, TIM_CHANNEL_1, duty);  // duty = 0~ARR
+
+// HAL 库呼吸灯示例
+uint8_t duty = 0, dir = 0;
+while (1) {
+    if (!dir) { duty++;  if (duty >= 99) dir = 1; }
+    else      { duty--;  if (duty <= 0)  dir = 0; }
+    __HAL_TIM_SET_COMPARE(&htim5, TIM_CHANNEL_1, duty * 10);
+    HAL_Delay(10);
+}
+```
+
 ## 核心速查表
 
-| 操作 | 代码 |
-|------|------|
-| PWM 模式1(CH1) | `CCMR1 |= OC1M_1 + OC1M_2`(=0x60) |
-| PWM 输出使能(CH1) | `CCER |= CC1E` |
-| 设置极性(高有效) | `CCER &= ~CC1P` |
-| 预装载使能 | `CCMR1 |= OC1PE` |
-| 修改占空比 | `CCR1 = value`(0~ARR) |
-| 修改频率(改ARR) | `ARR = new_value`(CR1.ARPE=1) |
-| 输入捕获模式 | `CCMR1 |= CC1S_0` |
-| 捕获使能 | `CCER &= ~CC1P`(上升沿)+ `CCER |= CC1E` |
+| 操作 | 寄存器版 | HAL 库版 |
+|------|---------|---------|
+| PWM 模式1(CH1) | `CCMR1 |= OC1M_1 + OC1M_2` | CubeMX 图形化配置 |
+| PWM 输出使能 | `CCER |= CC1E` | `HAL_TIM_PWM_Start(htim, CH)` |
+| 修改占空比 | `CCR1 = value` | `__HAL_TIM_SET_COMPARE(htim, CH, val)` |
+| 修改频率 | `ARR = new_value` | 改 `htim.Init.Period` 重新 Init |
+| 输入捕获 | `CCMR1 |= CC1S_0` | `HAL_TIM_IC_Start_IT(htim, CH)` |
+| 读取捕获值 | `CCR1` | `__HAL_TIM_GET_COMPARE(htim, CH)` |
 
 | 呼吸灯参数 | 频率 | 占空比范围 | 渐变步长 |
 |-----------|------|-----------|---------|

+ 38 - 6
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/12-SPI通信与FSMC总线.md

@@ -374,14 +374,46 @@ Bank1 分为 4 个子区(片选 NE1~NE4),各 64MB:
 
 ---
 
+---
+
+## HAL 库版 SPI
+
+HAL 库使用 `SPI_HandleTypeDef` 管理 SPI,提供 `Transmit/Receive` 函数。
+
+```c
+// CubeMX 生成: MX_SPI1_Init()
+SPI_HandleTypeDef hspi1;
+
+// HAL 库发送接收
+uint8_t tx_data = 0x06;           // WREN 指令
+uint8_t rx_data;
+
+HAL_SPI_Transmit(&hspi1, &tx_data, 1, 100);      // 阻塞发送 1 字节
+HAL_SPI_Receive(&hspi1, &rx_data, 1, 100);        // 阻塞接收 1 字节
+HAL_SPI_TransmitReceive(&hspi1, &tx_data, &rx_data, 1, 100);  // 同时收发
+
+// HAL 库中断方式
+HAL_SPI_Transmit_IT(&hspi1, buffer, size);         // 中断发送
+HAL_SPI_Receive_DMA(&hspi1, buffer, size);         // DMA 接收
+
+// HAL 库写 W25Q64 示例
+uint8_t cmd[] = {0x06};              // WREN
+HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
+HAL_SPI_Transmit(&hspi1, cmd, 1, 100);
+HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
+```
+
 ## 核心速查表
 
-| SPI 操作 | 软件模拟 | 硬件 SPI |
-|---------|---------|---------|
-| 初始化 | GPIO 开漏/推挽 | `CR1 |= MSTR + SPE` + GPIO 复用 |
-| 发 1 字节 | 8 次 GPIO 位操作 | `DR = byte; while(!RXNE); val = DR` |
-| 片选 | GPIO 位操作 | 同(NSS 由软件控制) |
-| 速度调整 | 修改延时 | `CR1.BR[2:0]` |
+| SPI 操作 | 软件模拟 | 硬件寄存器 | HAL 库 |
+|---------|---------|-----------|--------|
+| 初始化 | GPIO 开漏/推挽 | `CR1 |= MSTR + SPE` + GPIO 复用 | `MX_SPI1_Init()` |
+| 发 1 字节 | 8 次 GPIO 位操作 | `DR = byte; while(!RXNE); val = DR` | `HAL_SPI_Transmit()` |
+| 收 1 字节 | 同发送(发 0xFF 占位) | 同发送(发占位字节) | `HAL_SPI_Receive()` |
+| 同时收发 | 循环 8 次读写 | `DR = tx; val = DR` | `HAL_SPI_TransmitReceive()` |
+| 中断方式 | — | CR2.TXEIE/RXNEIE | `_IT()` 后缀 |
+| DMA 方式 | — | CR2.TXDMAEN/RXDMAEN | `_DMA()` 后缀 |
+| 片选 | GPIO 位操作 | GPIO 位操作 | `HAL_GPIO_WritePin()` |
 
 | FSMC 操作 | 代码 |
 |----------|------|

+ 53 - 9
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/13-CAN通信协议与bxCAN外设.md

@@ -341,17 +341,61 @@ void CAN_SendMsg(uint16_t stdID, uint8_t *data, uint8_t len)
 
 ---
 
+## HAL 库版 CAN
+
+HAL 库使用 `CAN_HandleTypeDef` 管理 CAN 外设,CubeMX 图形化配置波特率/过滤器/工作模式。
+
+```c
+// CubeMX 生成: MX_CAN1_Init() + 过滤器配置
+CAN_HandleTypeDef hcan1;
+
+// HAL 库发送
+CAN_TxHeaderTypeDef tx_header;
+uint8_t tx_data[8] = "Hello";
+uint32_t tx_mailbox;
+
+tx_header.StdId = 0x066;            // 标准 ID
+tx_header.ExtId = 0;                 // 扩展 ID(标准帧不用)
+tx_header.IDE = CAN_ID_STD;          // 标准帧
+tx_header.RTR = CAN_RTR_DATA;        // 数据帧
+tx_header.DLC = 5;                   // 数据长度
+
+// 添加报文到发送邮箱(非阻塞)
+HAL_CAN_AddTxMessage(&hcan1, &tx_header, tx_data, &tx_mailbox);
+
+// HAL 库接收(轮询)
+CAN_RxHeaderTypeDef rx_header;
+uint8_t rx_data[8];
+if (HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK)
+{
+    printf("Rx ID=0x%03X, Data=%s\r\n", rx_header.StdId, rx_data);
+}
+
+// HAL 库中断方式:使能接收中断并实现回调
+HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
+
+// 中断回调
+void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
+{
+    CAN_RxHeaderTypeDef rx_header;
+    uint8_t rx_data[8];
+    HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data);
+    // 处理收到的报文
+}
+```
+
 ## 核心速查表
 
-| 操作 | 寄存器操作 |
-|------|----------|
-| 初始化模式 | `MCR |= INRQ; while(!MSR.INAK)` |
-| 退出初始化 | `MCR &= ~INRQ; while(MSR.INAK);` |
-| 设置波特率 | `BTR = BRP + (TS1<<16) + (TS2<<20) + (SJW<<24)` |
-| 环回静默 | `BTR |= SILM + LBKM` |
-| 发送 | `TIR = ID<<21 + TXRQ` + `TDTxR = DLC` + `TDxR = data` |
-| 接收 | 读 `sFIFOMailBox[0]` 的 `TIR/TDTxR/TDxR` |
-| 配置过滤器 | `FINIT=1` → 设 FR1/FR2 → `FINIT=0` |
+| 操作 | 寄存器操作 | HAL 库 |
+|------|----------|--------|
+| 初始化模式 | `MCR |= INRQ; while(!MSR.INAK)` | CubeMX 自动处理 |
+| 设置波特率 | `BTR = BRP + (TS1<<16) + (TS2<<20) + (SJW<<24)` | 结构体 `CAN_InitTypeDef` |
+| 环回静默 | `BTR |= SILM + LBKM` | `CAN_MODE_LOOPBACK` |
+| 发送 | 邮箱 TIR/TDTxR/TDxR + TXRQ | `HAL_CAN_AddTxMessage()` |
+| 接收 | 读 FIFO 邮箱寄存器 | `HAL_CAN_GetRxMessage()` |
+| 配置过滤器 | `FINIT=1` → 设 FR1/FR2 → `FINIT=0` | `CAN_FilterTypeDef` 结构体 |
+| 中断接收 | EXTI + NVIC | `HAL_CAN_ActivateNotification()` |
+| 回调 | — | `HAL_CAN_RxFifo0MsgPendingCallback()` |
 
 ## 常见问题与避坑
 

+ 50 - 10
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/19-BKP备份寄存器与RTC实时时钟.md

@@ -256,18 +256,58 @@ void RTC_BKP_Init(void)
 
 ---
 
+## HAL 库版 RTC/BKP
+
+HAL 库使用 `RTC_HandleTypeDef` 管理 RTC,提供 `SetTime/GetTime/SetAlarm` 等高级 API。
+
+```c
+// CubeMX 生成 RTC 配置:LSE 时钟源,启用日历
+RTC_HandleTypeDef hrtc;
+
+// HAL 库设置时间(使用 RTC_TimeTypeDef / RTC_DateTypeDef 结构体)
+RTC_TimeTypeDef sTime = {0};
+RTC_DateTypeDef sDate = {0};
+
+sTime.Hours = 12;
+sTime.Minutes = 0;
+sTime.Seconds = 0;
+sTime.TimeFormat = RTC_HOURFORMAT_24;  // 24 小时制
+sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
+sTime.StoreOperation = RTC_STOREOPERATION_RESET;
+HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN);   // 设置时间
+
+sDate.Year = 26;    // 2026 年
+sDate.Month = 7;
+sDate.Date = 15;
+sDate.WeekDay = RTC_WEEKDAY_WEDNESDAY;
+HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);   // 设置日期
+
+// HAL 库读取时间
+HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);   // 读时间(必须先读时间再读日期!)
+HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);   // 读日期
+
+printf("%04d-%02d-%02d %02d:%02d:%02d\r\n",
+       2000 + sDate.Year, sDate.Month, sDate.Date,
+       sTime.Hours, sTime.Minutes, sTime.Seconds);
+
+// HAL 库读 BKP 寄存器
+uint16_t data = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);
+
+// HAL 库写 BKP 寄存器
+HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x55AA);
+```
+
 ## 核心速查表
 
-| 操作 | 代码 |
-|------|------|
-| 解锁备份域 | `PWR->CR |= PWR_CR_DBP` |
-| 写 BKP 寄存器 | `BKP->DR[i] = data` |
-| 读 BKP 寄存器 | `data = BKP->DR[i]` |
-| 配置 LSE | `RCC->BDCR |= RCC_BDCR_LSEON; while(!LSERDY);` |
-| 选择 RTC 时钟 | `RCC->BDCR |= RCC_BDCR_RTCSEL_0`(LSE) |
-| 使能 RTC | `RCC->BDCR |= RCC_BDCR_RTCEN` |
-| 读 RTC 秒数 | `RTC_GetCounter()` |
-| 写 RTC 秒数 | `RTC_SetCounter(value)`(需 CNF 模式) |
+| 操作 | 寄存器版 | HAL 库 |
+|------|---------|--------|
+| 解锁备份域 | `PWR->CR |= PWR_CR_DBP` | CubeMX 自动处理 |
+| 写 BKP 寄存器 | `BKP->DR[i] = data` | `HAL_RTCEx_BKUPWrite()` |
+| 读 BKP 寄存器 | `data = BKP->DR[i]` | `HAL_RTCEx_BKUPRead()` |
+| 设置时间 | `RTC_SetCounter(秒数)` | `HAL_RTC_SetTime()` + `SetDate()` |
+| 读取时间 | `RTC_GetCounter()` → Unix 转换 | `HAL_RTC_GetTime()` + `GetDate()` |
+| 配置 LSE | `RCC->BDCR` 逐位操作 | CubeMX 自动配置 |
+| 写注意事项 | 需 CNF 模式 + 等待 RTOFF | HAL 库自动处理 |
 
 ## 常见问题与避坑
 

+ 53 - 12
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/STM32学习笔记/20-看门狗IWDG与WWDG.md

@@ -198,20 +198,61 @@ void WWDG_IRQHandler(void)
 
 ---
 
+---
+
+## HAL 库版看门狗
+
+### HAL IWDG
+
+```c
+// HAL 库使用 IWDG_HandleTypeDef
+IWDG_HandleTypeDef hiwdg;
+
+hiwdg.Instance = IWDG;
+hiwdg.Init.Prescaler = IWDG_PRESCALER_32;    // PR=011: 分频32
+hiwdg.Init.Reload = 1000;                     // RLR=1000
+HAL_IWDG_Init(&hiwdg);                        // 相当于 KR=0xCCCC 启动
+
+// HAL 库喂狗
+HAL_IWDG_Refresh(&hiwdg);                     // 相当于 KR=0xAAAA
+```
+
+### HAL WWDG
+
+```c
+WWDG_HandleTypeDef hwwdg;
+
+hwwdg.Instance = WWDG;
+hwwdg.Init.Prescaler = WWDG_PRESCALER_8;     // WDGTB=11
+hwwdg.Init.Window = 0x50;                     // 窗口值
+hwwdg.Init.Counter = 0x7F;                    // 计数值初始值
+hwwdg.Init.EWIMode = WWDG_EWI_ENABLE;         // 使能早期唤醒中断
+HAL_WWDG_Init(&hwwdg);
+
+// HAL 库喂狗(就在主循环更新 CR 值)
+HAL_WWDG_Refresh(&hwwdg);                     // 相当于 CR=0x7F
+
+// HAL WWDG 早期唤醒中断回调
+void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
+{
+    // 距离 WWDG 复位还剩 64 个计数周期,执行紧急备份
+}
+```
+
 ## 核心速查表
 
-| IWDG 操作 | 代码 |
-|-----------|------|
-| 启动 | `KR = 0xCCCC` |
-| 喂狗 | `KR = 0xAAAA` |
-| 配置 PR/RLR | 先 `KR = 0x5555` 解锁 |
-
-| WWDG 操作 | 代码 |
-|-----------|------|
-| 启动 | `CR |= WDGA` |
-| 喂狗 | `CR = 0x7F` |
-| 配窗口 | `CFR.W = window_value` |
-| EWI 中断 | `CFR.EWI=1; NVIC_EnableIRQ(WWDG_IRQn)` |
+| IWDG 操作 | 寄存器 | HAL 库 |
+|-----------|--------|--------|
+| 启动 | `KR = 0xCCCC` | `HAL_IWDG_Init()` |
+| 喂狗 | `KR = 0xAAAA` | `HAL_IWDG_Refresh()` |
+| 配置 | 先 `KR = 0x5555` | 通过 `Init` 结构体 |
+
+| WWDG 操作 | 寄存器 | HAL 库 |
+|-----------|--------|--------|
+| 启动 | `CR |= WDGA` | `HAL_WWDG_Init()` |
+| 喂狗 | `CR = 0x7F` | `HAL_WWDG_Refresh()` |
+| 配窗口 | `CFR.W = value` | `Init.Window = value` |
+| EWI 回调 | 手动中断函数 | `HAL_WWDG_EarlyWakeupCallback()` |
 
 ## 常见问题与避坑