入门篇:CubeMX 配置与 HAL 外设驱动

知识库
知识库文档
/tech-stacks/stm32-hal/tutorial/入门篇:CubeMX 配置与 HAL 外设驱动.md

文档

STM32 HAL 开发实战

本章目标

掌握 STM32CubeMX + HAL 的开发流程,理解时钟树配置与外设初始化,完成定时器 PWM 和 ADC 采集。


1. STM32 家族概览

系列 内核 频率 定位
F0 Cortex-M0 48MHz 低成本替代 8-bit
F1 Cortex-M3 72MHz 经典入门
F4 Cortex-M4 168-180MHz DSP + FPU
F7 Cortex-M7 216MHz 高性能 + Cache
H7 Cortex-M7+M4 480MHz 双核旗舰
G0/G4 Cortex-M0+/M4 64/170MHz 新一代主流
L0/L4 Cortex-M0+/M4 32/80MHz 超低功耗

2. CubeMX 配置流程

步骤

  1. 新建工程 → 选择 MCU 型号(如 STM32F407VGT6)
  2. Pinout → 配置引脚功能:
    • RCC:HSE = Crystal/Ceramic Resonator
    • SYS:Debug = Serial Wire
  3. Clock Configuration → 配置时钟树:
    • HSE 8MHz → PLL /8 ×336 /2 = 168MHz (SYSCLK)
    • APB1 = 42MHz, APB2 = 84MHz
  4. 外设配置(见下方各节)
  5. Project Manager → Toolchain = Makefile(或 CubeIDE)
  6. Generate Code

3. GPIO 最佳实践

// 读取按键(带软件消抖)
uint8_t read_button(void) {
    if (HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin) == GPIO_PIN_RESET) {
        HAL_Delay(20);  // 消抖
        if (HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin) == GPIO_PIN_RESET) {
            while (HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin) == GPIO_PIN_RESET);
            return 1;  // 按键释放后才返回
        }
    }
    return 0;
}

// 翻转 LED
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);

// 原子写入(位带操作,F1 系列不支持)
LED_GPIO_Port->BSRR = LED_Pin;       // 置位
LED_GPIO_Port->BSRR = LED_Pin << 16; // 复位

4. 定时器 PWM

CubeMX 配置

  • TIM3 → Clock Source = Internal Clock
  • Channel 1 → PWM Generation CH1
  • Prescaler = 84-1 (84MHz / 84 = 1MHz)
  • Counter Period = 1000-1 (1MHz / 1000 = 1kHz PWM)
// 初始化
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);

// 设置占空比(0-999)
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 500);  // 50%

// 呼吸灯效果
for (int duty = 0; duty <= 999; duty++) {
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, duty);
    HAL_Delay(1);
}

5. ADC 连续采样

CubeMX 配置

  • ADC1 → IN0 (PA0), Scan Conversion = Enabled
  • DMA2 Stream0 → Circular, Half Word
#define ADC_BUF_SIZE 100
static uint16_t adc_buf[ADC_BUF_SIZE];

void adc_init(void) {
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buf, ADC_BUF_SIZE);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
    // DMA 完成一帧,处理数据
    float avg = 0;
    for (int i = 0; i < ADC_BUF_SIZE; i++) {
        avg += adc_buf[i];
    }
    avg /= ADC_BUF_SIZE;
    float voltage = avg * 3.3f / 4095.0f;
    printf("ADC Average: %.3f V\n", voltage);
}

6. HAL 超时机制

// 所有阻塞型 HAL API 都带超时参数
if (HAL_I2C_Master_Transmit(&hi2c1, addr, data, len, 100) != HAL_OK) {
    // 100ms 超时后返回 HAL_TIMEOUT
    handle_i2c_error();
}

非阻塞 API(_IT_DMA 后缀)不使用超时,依赖回调。


思考题

  1. STM32 的 HSI vs HSE vs PLL 分别用于什么场景?
  2. 为什么 ADC 推荐用 DMA 而非中断方式?
  3. HAL 库和 LL 库各适合什么样的开发场景?

信息

路径
/tech-stacks/stm32-hal/tutorial/入门篇:CubeMX 配置与 HAL 外设驱动.md
更新时间
2026/5/31