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配置
- 引脚分配:
- PA8 → TIM1_CH1(主PWM输出)
- PB13 → TIM1_CH1N(互补输出)
- 参数设置:
- 频率:100kHz(PSC=0,ARR=720-1)
- 死区时间:0.138μs(Dead Time=43)

# 3. ADC与DMA配置
- ADC通道:
- IN4(电压采样)
- IN5(电流采样)
- 采样时间:1.5cycles
- 触发源:TIM1捕获比较事件(同步PWM触发)
- DMA模式:循环传输,解放CPU资源

# 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
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
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
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
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
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
2
3
4
5
6
7
8
# 15.3.3 开环实验验证
# 1. 实验步骤
- 硬件连接:
- 输入电源:12V锂电池
- 负载:10Ω/2W电阻
- 占空比计算:
$$ D = \frac{V_{\text{out}}}{V_{\text{in}}} = \frac{3.3}{12} \approx 28% $$ - 示波器观测:
- 测量PA8(PWM)与PB13(互补PWM)波形,验证死区时间与频率。
# 2. 调试步骤与流程
观察驱动电路波形:
- 旋转编码器到占空比28%处,观察示波器波形
实测频率99.79kHz,误差在合理范围内(±1%)。
- 旋转编码器到占空比28%处,观察示波器波形
输出电压:
- 观察嵌入式学习板上的OLED显示,并用万用表测量负载两端电压
初始占空比28%时输出电压偏差,调整至31%后输出稳定3.3V。
- 观察嵌入式学习板上的OLED显示,并用万用表测量负载两端电压
通过编码器调整输出至3.3V:
- 继续旋转编码器增大占空比,可以看到占空比为31%时,输出电压为3.3V
- 继续旋转编码器增大占空比,可以看到占空比为31%时,输出电压为3.3V
# 15.3.4 关键说明
- 死区时间:0.138μs,避免MOS管直通损坏。
- 编码器映射:10~90编码值对应占空比10%~90%(CCR=72~648)。
- ADC校准:HAL_ADCEx_Calibration_Start确保采样精度。