STM32F1 外设配置速查(CubeMX 配置 + HAL 调用)
配套软件 I2C 库:
soft_i2c.h/soft_i2c.c(直接拖进工程 Src/Inc 即可)。 通用前提:CubeMX 里先配好时钟树(RCC → HSE Crystal,Clock Config 里 SYSCLK 常拉到 72MHz),SYS → 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(普通输入/输出)
CubeMX:Pinout 视图点引脚 → 选 GPIO_Output 或 GPIO_Input。GPIO 配置页设:
- Output 型:Mode = Push-Pull(普通)/ Open-Drain(如 I2C、驱动开漏器件);Pull;Speed;起个 Label(生成
LED_GPIO_Port/LED_Pin宏,好用)。 - Input 型:Pull = No/Up/Down。
HAL:
HAL_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 外部中断 EXTI(Hall 笔在位/按键)
CubeMX:
- 引脚点成
GPIO_EXTIx。 - GPIO 配置页:Mode 选
External Interrupt Mode with Rising / Falling / Rising-Falling edge;设 Pull。 - NVIC 页勾上对应
EXTIx interrupt(不勾中断进不来!)。
同一 EXTI 线号(如 PA0/PB0 都是 EXTI0)只能用一个引脚。
HAL:IRQ 处理和分发 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 定时器(周期中断 / 时基)
CubeMX:Timers → 选 TIMx → Clock Source = Internal Clock;Parameter Settings 设 Prescaler(PSC)、Counter Period(ARR);NVIC 页勾 TIMx global interrupt(要中断才勾)。
周期 = (PSC+1)·(ARR+1) / TIM时钟。例:72MHz 出 1kHz → PSC=71, ARR=999 → 72e6/72/1000=1kHz。
HAL:
HAL_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. 多通道 PWM
4a. 普通多路 PWM(TIM2/3/4,如多路 LED/风扇)
CubeMX:TIMx → Clock Source = Internal;Channel1~4 各选 PWM Generation CH1~CH4;设 PSC、ARR,每通道 Pulse(CCR)、Mode = PWM1。
频率 = TIM时钟/((PSC+1)(ARR+1));占空比 = CCR/(ARR+1)。四个通道共用同一频率,各自 CCR 独立。
HAL:
HAL_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),配preload
4b. 半桥互补 PWM + 死区(TIM1,无线充电发射端驱动)
CubeMX:TIM1 → Channel1 = PWM Generation CH1 CH1N(同时出互补); Break And Dead Time management 里设 Dead Time(死区,防上下管直通,务必设);设 PSC/ARR/Pulse。
例:72MHz 出 130kHz → ARR = 72e6/130e3 - 1 ≈ 553,CCR = ARR/2(50%)。
HAL:
HAL_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 时钟必须 ≤ 14MHz(Clock Config 里把 ADC Prescaler 设 /6,72MHz→12MHz);② 上电必须校准。
5a. 单/多通道 轮询(不带 DMA)
CubeMX:ADC1 → 勾要用的通道 IN0/IN1...;Parameter:
- 单通道:Scan = Disable,Continuous = Disable。
- 多通道轮询:Scan = Enable,
Number Of Conversion= N,逐个设 Rank 的 Channel 和 Sampling Time(高阻源选长,如 239.5 Cycles)。
HAL:
HAL_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(推荐,多路一次刷完)
CubeMX:ADC1 → Scan = Enable,Continuous = Enable,Number Of Conversion = N,配好各 Rank; DMA Settings → Add → DMA1 通道,Mode = Circular,Data Width = Half Word;DMA Continuous Requests = Enable。
HAL:
uint16_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。要点:
- Mode:
Normal(传一次停)/Circular(循环,ADC 连采、串口连收用它)。 - Data Width:按外设选(ADC 半字 Half Word,USART 字节 Byte)。
- Increment:Memory 侧勾 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. 硬件 I2C
CubeMX:Connectivity → I2C1 → Mode = I2C;Parameter:Speed(Standard 100k / Fast 400k)、7-bit 地址。引脚(PB6=SCL, PB7=SDA)自动出,注意它俩是开漏,硬件上要有 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)
更多推荐



所有评论(0)