配套软件 I2C 库soft_i2c.h/soft_i2c.c直接拖进工程 Src/Inc 即可。 通用前提CubeMX 里先配好时钟树RCC → HSE CrystalClock Config 里 SYSCLK 常拉到 72MHzSYS → Debug 选Serial Wire否则烧一次就锁 SWD。0. 软件 I2C本库CubeMX只需把 SCL、SDA 两个引脚点成GPIO_Output在 GPIO 配置里设Output Open Drain Pull-up。也可以不在 CubeMX 配SI2C_Init()里已经自己初始化了。调用#include soft_i2c.h SI2C_Init(); if (SI2C_ScanAck(0x28)) printf(device online\r\n); // 传7位地址 uint8_t id; SI2C_ReadReg (0x28, 0x00, id); // 读寄存器0x00 SI2C_WriteReg(0x28, 0x10, 0x55); // 写0x55到寄存器0x10 uint8_t buf[4]; SI2C_ReadBuf (0x28, 0x20, buf, 4); // 连读4字节硬件 I2C 卡死排不掉时切软件 I2C 是最快的救场手段。1. GPIO普通输入/输出CubeMXPinout 视图点引脚 → 选GPIO_Output或GPIO_Input。GPIO 配置页设Output 型Mode Push-Pull普通/ Open-Drain如 I2C、驱动开漏器件PullSpeed起个 Label生成LED_GPIO_Port/LED_Pin宏好用。Input 型Pull No/Up/Down。HALHAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); // 置高 HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); // 置低 HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 翻转 GPIO_PinState s HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin); // 读2. GPIO 外部中断 EXTIHall 笔在位/按键CubeMX引脚点成GPIO_EXTIx。GPIO 配置页Mode 选External Interrupt Mode with Rising / Falling / Rising-Falling edge设 Pull。NVIC 页勾上对应EXTIx interrupt不勾中断进不来。同一 EXTI 线号如 PA0/PB0 都是 EXTI0只能用一个引脚。HALIRQ 处理和分发 CubeMX 已生成你只需在main.c里重写回调void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin HALL_Pin) { g_pen_present HAL_GPIO_ReadPin(HALL_GPIO_Port, HALL_Pin); // 注意中断里别做耗时事置个标志到主循环处理消抖也放主循环 } }3. TIM 定时器周期中断 / 时基CubeMXTimers → 选 TIMx → Clock Source Internal ClockParameter Settings 设Prescaler(PSC)、Counter Period(ARR)NVIC 页勾TIMx global interrupt要中断才勾。周期 (PSC1)·(ARR1) / TIM时钟。例72MHz 出 1kHz → PSC71, ARR999 → 72e6/72/10001kHz。HALHAL_TIM_Base_Start_IT(htim4); // 启动带中断的定时器 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) // 溢出回调 { if (htim-Instance TIM4) { // 1kHz 周期任务放这里 } }只要计数不要中断HAL_TIM_Base_Start(htim4)然后读__HAL_TIM_GET_COUNTER(htim4)。4. 多通道 PWM4a. 普通多路 PWMTIM2/3/4如多路 LED/风扇CubeMXTIMx → Clock Source InternalChannel1~4 各选PWM Generation CH1~CH4设 PSC、ARR每通道Pulse(CCR)、Mode PWM1。频率 TIM时钟/((PSC1)(ARR1))占空比 CCR/(ARR1)。四个通道共用同一频率各自 CCR 独立。HALHAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_3); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_4); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 300); // 改CH1占空比(改CCR) __HAL_TIM_SET_AUTORELOAD(htim3, 999); // 改频率(改ARR)配preload4b. 半桥互补 PWM 死区TIM1无线充电发射端驱动CubeMXTIM1 → Channel1 PWM Generation CH1 CH1N同时出互补Break And Dead Time management里设Dead Time死区防上下管直通务必设设 PSC/ARR/Pulse。例72MHz 出 130kHz → ARR 72e6/130e3 - 1 ≈ 553CCR ARR/250%。HALHAL_TIM_PWM_Start (htim1, TIM_CHANNEL_1); // 高侧 CH1 HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_1); // 互补低侧 CH1N注意是 PWMN __HAL_TIM_SET_AUTORELOAD(htim1, arr); // 改频率做扫频调功率 __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, arr/2);改 ARR 会变频CubeMX 里 TIM1 的Auto-reload preload Enable否则改频率中途会出毛刺脉冲冲击 MOS。5. ADC多通道采样⚠️ F1 两个致命点① ADC 时钟必须 ≤14MHzClock Config 里把 ADC Prescaler 设 /672MHz→12MHz② 上电必须校准。5a. 单/多通道 轮询不带 DMACubeMXADC1 → 勾要用的通道IN0/IN1...Parameter单通道Scan DisableContinuous Disable。多通道轮询Scan EnableNumber Of Conversion N逐个设 Rank 的 Channel 和Sampling Time高阻源选长如 239.5 Cycles。HALHAL_ADCEx_Calibration_Start(hadc1); // 开机先校准F1 必做 // 单通道 HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); uint16_t v HAL_ADC_GetValue(hadc1); HAL_ADC_Stop(hadc1);多通道且没用 DMA 时切通道要用HAL_ADC_ConfigChannel()重配 Rank1否则每次读的都是同一路见踩坑文档坑位。5b. 多通道 DMA推荐多路一次刷完CubeMXADC1 → Scan EnableContinuous EnableNumber Of Conversion N配好各 Rank DMA Settings →Add→ DMA1 通道Mode CircularData Width Half WordDMA Continuous Requests Enable。HALuint16_t adc_buf[3]; // 长度通道数顺序对应 Rank1,2,3 HAL_ADCEx_Calibration_Start(hadc1); HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_buf, 3); // 启动后自动循环填充 // 之后随时直接读 adc_buf[0/1/2]DMA 在后台刷新无需再 Start/Stop uint16_t vbus_raw adc_buf[0];6. DMA通用CubeMX一般不单独配在具体外设ADC/USART/SPI的DMA Settings页 Add Request。要点ModeNormal传一次停/Circular循环ADC 连采、串口连收用它。Data Width按外设选ADC 半字 Half WordUSART 字节 Byte。IncrementMemory 侧勾 Increment写进数组Peripheral 侧一般不勾。NVIC 里对应 DMA 中断通常自动勾上用回调就需要。HAL走外设的_DMA版函数回调在对应外设的完成回调里HAL_ADC_Start_DMA(hadc1, (uint32_t*)buf, N); void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { /* 一轮采完 */ } HAL_UART_Transmit_DMA(huart1, data, len); void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { /* 发完 */ }7. 硬件 I2CCubeMXConnectivity → I2C1 → Mode I2CParameterSpeedStandard 100k / Fast 400k、7-bit 地址。引脚PB6SCL, PB7SDA自动出注意它俩是开漏硬件上要有 4.7k 上拉。⚠️ 地址左移datasheet 给的是 7 位地址HAL 要传8 位7位 1。HAL寄存器型器件最常用 Mem 系列#define IC_ADDR (0x28 1) // 7位地址左移1位 uint8_t val; HAL_I2C_Mem_Read (hi2c1, IC_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, val, 1, 100); // 读寄存器 HAL_I2C_Mem_Write(hi2c1, IC_ADDR, 0x10, I2C_MEMADD_SIZE_8BIT, val, 1, 100); // 写寄存器 // 探测在线返回 HAL_OK 表示有应答 if (HAL_I2C_IsDeviceReady(hi2c1, IC_ADDR, 2, 100) HAL_OK) { /* online */ } // 裸收发非寄存器型 HAL_I2C_Master_Transmit(hi2c1, IC_ADDR, txbuf, len, 100); HAL_I2C_Master_Receive (hi2c1, IC_ADDR, rxbuf, len, 100);卡死恢复SDA 被从机拉死见备赛文档坑2切 GPIO 手动打 9 个 SCL STOP。实在排不掉就直接换上面的软件 I2C。附三个最容易忘的 F1 开机动作HAL_ADCEx_Calibration_Start(hadc1); // 1. ADC 必校准 // 2. ADC 时钟 Prescaler /6 让它 ≤14MHz在 CubeMX Clock Config 做 // 3. 硬件 I2C 地址一律 (addr7 1)