[RA4 & RA6] 【瑞萨RA × Zephyr评测】4、数控电源

[复制链接]
139|8
lulugl 发表于 2026-4-23 12:36 | 显示全部楼层 |阅读模式
, , , 瑞萨RA,

数控直流电源DIY — RA6E2 + INA226 + XL4015 + OLED旋转编码器

一、项目概述

本文介绍如何制作一台小型数控直流电源,具有以下特点:

  • 输出电压:1.0V ~ 5.0V 可调
  • 显示方式:OLED 屏幕,旋转编码器设置
  • 控制方式:PI 闭环自动调节
  • 硬件平台:瑞萨 RA6E2 + TI INA226 电流监测 + XL4015 DC-DC

硬件架构

image.png

硬件清单

器件 说明
RA6E2 瑞萨 32 位 MCU,12-bit DAC
INA226 TI 双向电流/功率监测,I2C 地址 0x40
XL4015 降压 DC-DC 芯片,FB 基准约 0.6V
100mΩ 分流电阻 INA226 电流采样
1N4007 二极管 串联在 DAC 和 FB 之间
SSD1306 OLED 128x64 I2C OLED 屏幕
EN11 旋转编码器 带按钮,用于设置电压

二、硬件连接

2.1 引脚连接

功能 RA6E2 引脚 说明
DAC 输出 P014 连接二极管 → XL4015 FB
I2C SCL P109 SCI9 → OLED / INA226
I2C SDA P110 SCI9 → OLED / INA226
编码器 A P301 旋转编码器 A 相
编码器 B P302 旋转编码器 B 相
编码器 BTN P303 旋转编码器按钮

2.2 控制原理

DAC 输出电压 → 二极管(压降0.7V) → XL4015 FB 引脚
                                           ↓
XL4015 内部基准 ≈ 0.6V ← FB 引脚期望电压
                                           ↓
                    DAC = 0.6V + 0.7V ≈ 1.3V 对应 DAC 值 1613

通过 PI 控制器调节 DAC 电压,从而控制 XL4015 输出稳定在设定值。


三、软件架构

3.1 目录结构

app/ra6e2_dac_ina226/
├── CMakeLists.txt           # 构建配置
├── prj.conf                 # Kconfig 配置
├── boards/
│   └── fpb_ra6e2.overlay   # 设备树覆盖
└── src/
    ├── main.c              # 主程序 + PI 控制器
    ├── encoder.c/h         # 旋转编码器驱动
    ├── ina226.c/h          # INA226 I2C 驱动
    ├── oled.c/h            # SSD1306 OLED 驱动
    └── gui.c/h             # GUI 绘图函数

3.2 系统任务

┌─────────────────────────────────────────────────────────┐
│  control_thread (优先级5, 10ms周期)                     │
│  - 读取 INA226 电压/电流/功率                            │
│  - PI 计算误差并调节 DAC                                 │
│  - 更新全局共享变量                                      │
└─────────────────────────────────────────────────────────┘
         ↑ g_vbus, g_current, g_power, g_dac_val, g_error
         ↓
┌─────────────────────────────────────────────────────────┐
│  encoder_poll (5ms轮询)                                 │
│  - 读取编码器 A/B 相状态                                 │
│  - 检测旋转方向 CW/CCW                                  │
│  - 检测按钮按下/释放/长按                                │
└─────────────────────────────────────────────────────────┘
         ↓ 事件回调
┌─────────────────────────────────────────────────────────┐
│  encoder_callback                                       │
│  - CW/CCW: 调节 g_set_temp_mv (SET模式下)              │
│  - RELEASE: 切换 VIEW↔SET,SET时提交目标电压            │
└─────────────────────────────────────────────────────────┘
         ↓
┌─────────────────────────────────────────────────────────┐
│  display_timer (200ms周期)                              │
│  - display_work_handler                                 │
│  - 显示 DC Power / Set电压 / Vout / [SET] / OK          │
└─────────────────────────────────────────────────────────┘

3.3 菜单状态机

                    ┌─────────────┐
                    │   MENU_VIEW  │◄────────────────┐
                    │  正常显示    │                 │
                    │  PI调节中    │                 │
                    └──────┬──────┘                 │
                           │                        │
                    按下按钮                         │
                           │                        │
                           ▼                        │
                    ┌─────────────┐                 │
              ┌───►│   MENU_SET   │─────────────────┘
              │    │  设置电压    │   按下按钮(退出)
              │    │  可调节目标   │
              │    └─────────────┘
              │           │
              │    旋转编码器
              │           │
              │    ±50mV/格
              │           │
              └───────────┘

四、关键代码

4.1 PI 控制器

#define TARGET_MV      3300    // 目标电压 3.3V
#define KP             80      // 比例系数
#define KI             8       // 积分系数
#define DAC_MIN        500     // DAC 最小值
#define DAC_MAX        3500    // DAC 最大值
#define HYSTERESIS_MV  50      // 滞环 ±25mV

static int dac_val = DAC_INIT_VAL;
static int integral = 0;

static int pi_control(int error_mv)
{
    int p = error_mv * KP;
    integral += error_mv * KI;

    // 积分限幅,防止积分饱和
    if (integral > 500000)  integral = 500000;
    if (integral < -500000) integral = -500000;

    int delta = (p + integral) / 1000;
    int new_dac = dac_val + delta;

    if (new_dac < DAC_MIN) new_dac = DAC_MIN;
    if (new_dac > DAC_MAX) new_dac = DAC_MAX;

    return new_dac;
}

4.2 旋转编码器驱动

使用 GPIO 轮询方式(5ms 间隔),兼容不支持 GPIO 中断的平台:

/* encoder.h */
struct encoder {
    const struct device *gpio_dev;
    gpio_pin_t pin_a, pin_b, pin_btn;
    encoder_callback_t callback;
    void *user_data;
    int8_t last_state;
    int last_btn;
    uint32_t btn_press_time;
    struct k_timer poll_timer;
};

/* 编码器事件 */
enum encoder_event {
    ENCODER_EVENT_CW,         // 顺时针旋转
    ENCODER_EVENT_CCW,        // 逆时针旋转
    ENCODER_EVENT_PRESS,      // 按钮按下
    ENCODER_EVENT_RELEASE,    // 按钮释放(短按)
    ENCODER_EVENT_LONG_PRESS, // 按钮长按(>1s)
};

4.3 编码器回调

static void encoder_callback(enum encoder_event evt, void *user_data)
{
    switch (evt) {
    case ENCODER_EVENT_CW:
        if (g_menu_state == MENU_SET) {
            g_set_temp_mv += 50;  // SET模式下 ±50mV/格
            if (g_set_temp_mv > 5000) g_set_temp_mv = 5000;
        }
        break;

    case ENCODER_EVENT_CCW:
        if (g_menu_state == MENU_SET) {
            g_set_temp_mv -= 50;
            if (g_set_temp_mv < 1000) g_set_temp_mv = 1000;
        }
        break;

    case ENCODER_EVENT_RELEASE:
        // 短按切换 VIEW ↔ SET 模式
        if (g_menu_state == MENU_VIEW) {
            g_menu_state = MENU_SET;
            g_set_temp_mv = g_target_mv;  // 复制当前目标
        } else if (g_menu_state == MENU_SET) {
            g_target_mv = g_set_temp_mv;  // 提交新目标
            g_menu_state = MENU_VIEW;
        }
        break;
    }
}

4.4 OLED 显示

VIEW 模式(正常显示):

┌─────────────────────────────┐
│     RA6E2 DC Power         │  ← 标题居中 (16pt)
│  Vout: 3.298V              │  ← 输出电压 (16pt)
│  Current: 125mA            │  ← 电流 (16pt)
└─────────────────────────────┘

SET 模式(设置电压):

┌─────────────────────────────┐
│     RA6E2 DC Power         │  ← 标题居中 (16pt)
│  Vout: 3.298V              │  ← 输出电压 (16pt)
│  Current: 125mA            │  ← 电流 (16pt)
│  Set: 3.500V              │  ← 目标电压 (16pt, 反白)
└─────────────────────────────┘

显示布局:

Y坐标 内容
0 y=0 标题 "RA6E2 DC Power" 居中显示
1 y=16 Vout: X.XXXV (实测输出电压)
2 y=30 Current: XXXmA (输出电流)
3 y=48 Set: X.XXXV (SET模式下显示, 16pt)

五、INA226 驱动

5.1 寄存器配置

// CONFIG: Shunt+Bus 连续模式, 1100μs 转换时间
INA226_Write_Reg(INA226_REG_CONFIG, 0x4527);

// CAL: Rshunt=100mΩ, Current_LSB=20μA
// CAL = 5120000000 / (20 × 100000) = 2560 = 0x0A00
INA226_Write_Reg(INA226_REG_CALIB, 0x0A00);

5.2 读取函数

// 总线电压: LSB=1.25mV
int INA226_Read_Voltage_mV(i2c_dev);

// 电流: LSB=20μA
int INA226_Read_Current_mA(i2c_dev);

// 功率: LSB=0.5mW
int INA226_Read_Power_mW(i2c_dev);

六、操作说明

6.1 正常使用时

  1. 打开电源,OLED 显示当前输出电压和电流
  2. PI 控制器自动调节,保持输出稳定

6.2 调节电压

  1. 按一下按钮 → 进入设置模式,底部显示 "Set: X.XXXV"
  2. 旋转编码器 → 调节目标电压(±50mV/格)
  3. 再按一下按钮 → 退出设置,新电压生效

6.3 参数调优

现象 调整
响应太慢 ↑ KI
电压振荡 ↓ KP, ↓ KI
超调过大 ↓ KP
频繁调节 ↑ HYSTERESIS_MV

七、总结

本文实现了一个完整的数控电源系统:

  • ✅ DAC + PI 闭环控制输出电压
  • ✅ INA226 实时监测电压/电流
  • ✅ OLED 显示 Vout、Current、设置值
  • ✅ 旋转编码器交互控制
  • ✅ 退出设置时电压才生效(防误触)

八、实验效果

d278736c01ea9931b5caecd901afebfa.jpg

设置界面:

59c5432aae1d74fe8db823c434723bf5.jpg

设置后的效果:

96e9ced270c5fe02bcce9aee8f73884f.jpg

浊发清眸 发表于 2026-4-23 21:41 | 显示全部楼层
RA6E2 的 12 位 DAC 必须在设备树里开启内部 3.3V 参考电压源,不然 DAC 输出范围不对,电压根本调不到满量程;还有 I2C 总线上挂了 INA226 和 SSD1306 两个设备,一定要在 pinctrl 里开启引脚的片内上拉,不然 I2C 通信会丢包,OLED 偶尔花屏,INA226 读出来全是 0
中国英茂科工 发表于 2026-4-24 07:25 | 显示全部楼层
瑞萨不简单,多多开源,感谢分享
中国英茂科工 发表于 2026-4-24 07:26 | 显示全部楼层
博主的屏焊点也是没焊好,我也焊不好
goyhuan 发表于 2026-4-24 08:14 | 显示全部楼层
XL4015对比其它DC to DC有什么优势?
wangshujun 发表于 2026-4-24 08:44 | 显示全部楼层
串联二极管到fb是不对的,直接配一个电阻才对
 楼主| lulugl 发表于 2026-4-24 15:42 | 显示全部楼层
wangshujun 发表于 2026-4-24 08:44
串联二极管到fb是不对的,直接配一个电阻才对

配了一个100欧的电阻的
stb988 发表于 2026-4-24 22:41 | 显示全部楼层
不错,学习了!
wangshujun 发表于 2026-4-27 09:15 | 显示全部楼层
lulugl 发表于 2026-4-24 15:42
配了一个100欧的电阻的

这里要按照运放加法来理解,是可以实现线性、从0起调的
加了二极管带来的非线性和开关压差都会让控制特性变差
您需要登录后才可以回帖 登录 | 注册

本版积分规则

205

主题

908

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部
0