15.3 数字BUCK变换器项目1:开环系统设计

# 15.3 数字BUCK变换器项目1:开环系统设计

本实验通过STM32的高级定时器TIM1输出互补PWM信号驱动同步BUCK电路,结合ADC采样与OLED显示,实现开环电压调节功能。
核心功能

  • 互补PWM驱动:TIM1生成带死区的互补PWM信号,避免上下管直通。
  • 电压/电流采样:ADC采集输出电压电流参数并通过OLED实时显示。
  • 编码器控制:动态调节占空比,精确控制输出电压。

# 15.3.1 STM32CubeMX配置

# 1. 工程基础配置

  • 芯片型号:STM32F103C8T6
  • 主频:72MHz
  • 调试接口:SWD

# 2. TIM1互补PWM配置

  1. 引脚分配
    • PA8 → TIM1_CH1(主PWM输出)
    • PB13 → TIM1_CH1N(互补输出)
  2. 参数设置
    • 频率:100kHz(PSC=0,ARR=720-1)
    • 死区时间:0.138μs(Dead Time=43)
TIM1输出触发事件选择

# 3. ADC与DMA配置

  • ADC通道
    • IN4(电压采样)
    • IN5(电流采样)
  • 采样时间:1.5cycles
  • 触发源:TIM1捕获比较事件(同步PWM触发)
  • DMA模式:循环传输,解放CPU资源
ADC1的外部触发转换设置

# 4. 配置TIM2编码器

  • 配置与13章一样的TIM2的编码器功能,用于控制占空比。

# 5. 配置TIM3

  • 配置TIM3及中断功能,中断周期设置为200ms,并开启中断,用于后续的OLED屏幕刷新及编码获取。

# 6. 配置I2C

  • 开启I2C通信。

# 7. 生成代码

  • 勾选“为每个外设生成独立文件”
  • 保存生成工程代码(快捷键Ctrl+S)

# 15.3.2 开环软件设计

# 1. 引用相应头文件

调用字体库、OLED驱动函数及输入输出库

#include "oled.h"   // 引入OLED显示屏驱动相关的头文件
#include "font.h"   // 引入字体定义文件
#include "stdio.h"  // 包含标准输入输出库,提供sprintf等标准输入输出函数
Copied!
1
2
3

# 2. 初始化变量

添加全局变量声明,用于显示字符串和编码值

//显示变量
uint16_t value[2];//存放adc读取的模拟量 0:采样电流值1:采样电压值
float Vout;          //存放转换后实际的电压值
float Iout;          //存放转换后实际的电流值
char  Vo_str[30];   //存储字符形式的电压显示
char Io_str[30];    //存储字符形式的电流显示
char D_str[30];     //存储字符形式的占空比显示
//编码器变量
int Encoder_cnt=0;  //存储占空比对应原始编码值

Copied!
1
2
3
4
5
6
7
8
9
10

# 3. 启动外设

初始化各部分的外设

OLED_Init();                                        // OLED初始化
HAL_ADCEx_Calibration_Start(&hadc1);                // ADC校准
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)value, 2);    // 启动DMA传输
HAL_TIM_Base_Start_IT(&htim1);                     // 启动TIM1
HAL_TIM_Base_Start_IT(&htim3);                     // 启动TIM3(OLED刷新)
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);          // 启动PWM主通道
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);       // 启动互补通道
HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);    // 启动编码器
Copied!
1
2
3
4
5
6
7
8

# 4. 封装OLED显示代码(代码清单15‑4)

将电压电流的转换代码及OLED显示函数封装成一个函数

void OLED_Disp(void) {
  Vout = value[1] * 13.2 / 4096;    // 电压转换:Vout = ADC值 × (3.3V/4096) × 分压比
  Iout = value[0] * 1.65 / 4096;    // 电流转换:Iout = ADC值 × (3.3V/4096) × 放大倍数
  sprintf(Vo_str, "Vout:%0.2fV", Vout);
  sprintf(Io_str, "Iout:%0.2fA", Iout);
  sprintf(D_str, "Duty:%.2d%%", Encoder_cnt);
  // OLED显示刷新
  OLED_PrintString(45, 0, "BUCK", &font16x16, OLED_COLOR_NORMAL);
  OLED_PrintString(16, 15, Vo_str, &font16x16, OLED_COLOR_NORMAL);
  OLED_PrintString(16, 30, Io_str, &font16x16, OLED_COLOR_NORMAL);
  OLED_PrintString(16, 45, D_str, &font16x16, OLED_COLOR_NORMAL);
  OLED_ShowFrame();
}
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5. 封装编码控制函数(代码清单15‑5)

通过编码器来控制占空比的大小

void Encoder(void) {
  Encoder_cnt = __HAL_TIM_GET_COUNTER(&htim2);  // 读取编码器值
  // 限幅处理:10%~90%
  if (Encoder_cnt <= 10 || Encoder_cnt > 60000) {
    Encoder_cnt = 10;
    __HAL_TIM_SET_COUNTER(&htim2, 10);
  } else if (Encoder_cnt > 90) {
    Encoder_cnt = 90;
    __HAL_TIM_SET_COUNTER(&htim2, 90);
  }
  // 占空比映射:编码器值 → CCR值(比例系数7.2)
  __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, Encoder_cnt * 7.2);
}
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13

# 6. 编写定时器中断回调函数

在TIM3中断判断语句内调用OLED_Disp( )函数以及Encoder( )函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim == &htim3)          //htim3定时器触发
  {
    OLED_Disp();             //调用OLED显示更新函数 200ms刷新一次
    Encoder();               //调用编码函数,用编码值控制占空比
  }
}
Copied!
1
2
3
4
5
6
7
8

# 15.3.3 开环实验验证

# 1. 实验步骤

  1. 硬件连接
    • 输入电源:12V锂电池
    • 负载:10Ω/2W电阻
  2. 占空比计算
    $$ D = \frac{V_{\text{out}}}{V_{\text{in}}} = \frac{3.3}{12} \approx 28% $$
  3. 示波器观测
    • 测量PA8(PWM)与PB13(互补PWM)波形,验证死区时间与频率。

# 2. 调试步骤与流程

  • 观察驱动电路波形

    • 旋转编码器到占空比28%处,观察示波器波形 输出驱动波形图
      实测频率99.79kHz,误差在合理范围内(±1%)。
  • 输出电压

    • 观察嵌入式学习板上的OLED显示,并用万用表测量负载两端电压 OLED显示
      万用表显示
      初始占空比28%时输出电压偏差,调整至31%后输出稳定3.3V。
  • 通过编码器调整输出至3.3V

    • 继续旋转编码器增大占空比,可以看到占空比为31%时,输出电压为3.3V OLED显示
      万用表显示

# 15.3.4 关键说明

  • 死区时间:0.138μs,避免MOS管直通损坏。
  • 编码器映射:10~90编码值对应占空比10%~90%(CCR=72~648)。
  • ADC校准:HAL_ADCEx_Calibration_Start确保采样精度。