基于VisionBoard的电机驱动器

通过485串口通讯等多个功能实现电机控制

基于VisionBoard的电机驱动器封面
jf_191409292025-11-03 16:53:10GPL 3.0
414
Star

PCBA

设计文件

KiCad图标RT-Thread.zip10.03MB

EDA查看器

复制嵌入代码

详细介绍

1. 前言

该项目是基于瑞萨 VisionBoard 主控芯片开发的多功能电机驱动器,核心通过 485 串口协议实现外部设备与驱动器的通讯,进而控制电机启停等动作。开发过程中为丰富功能,临时新增了通用隔离输入输出、步进电机驱动等硬件电路,目前已完成电源模块、无刷电机驱动模块、485 通讯模块的功能验证。

这次开发是实践学习的过程:不仅接触了新的 PCB 制图工具完成电路布局与走线(虽布局走线仍有待优化),还学习了 RT-Thread 操作系统的基础使用,同时初步掌握了瑞萨 VisionBoard 芯片及配套开发工具的操作逻辑。(我的这个作品项目主要是我个人学习一下第一次参加比较懵所以整体是一团乱的所以希望大家见谅哈谢谢)

(实力有限我只是把我想尝试的部分先添加进去了看上去很杂后面会重新画板打样,然后功能这次就都一个模块一个模块去实现并没有进行整合,然后另外的BTB拓展板是后面发现RPI的PWM口正常来说应该是三个可以实现PWM互补然后用六步换相吧好像但是工具不是很会用所以就麻烦了点然后不够用所以就临时打样的有加LED数码管和OLED并且把BTB的IO口用排针再引出来)

2. 设计方案

  • 2.1电机模块

  • 2.1.1 电机选择

选用两类电机覆盖不同场景:

  • 无刷电机:用于持续运转的场景;

  • 步进电机:用于高精度位置控制的场景。

  • 2.1.2 无刷电机控制

基于硬件电路实现驱动与监测:

  • 核心驱动:采用 EG2123A 无刷电机驱动芯片(U29),输出电机相驱动信号;

  • 电流监测:通过 INA240 双向电流检测芯片(U25、U26),实时监测无刷电机的相电流;

  • 功率输出:由 NMOS 管组成的功率电路(Q4、Q5 等),放大驱动信号后连接无刷电机(接口 J8)。

    这个是驱动波形(电机转起来好像还是有点问题还得再测一下看看但是电路能正常的驱动电机)

  • 2.1.3 步进电机控制

基于 A4988 芯片实现细分驱动:

  • 驱动核心:采用 A4988 双步进电机驱动芯片(U1、U2),支持 DIR(方向)、STEP(脉冲)控制;

  • 电流监测:通过 INA219 电流检测芯片(U3、U4),监测步进电机工作电流;

接口:通过 J10、J13 接口连接步进电机,传输驱动信号。

  • 2.2 主控板及其按键模块

  • 2.2.1 主控芯片选择

选用瑞萨 VisionBoard 系列芯片作为主控核心,负责:解析 485 通讯指令、输出电机驱动信号、管理各功能模块的逻辑调度。

  • 2.2.2 无线通信模块的选取(调整为:通讯模块(485 串口通讯))

采用 “隔离 + 独立供电” 的 485 通讯方案:

  • 信号隔离:通过 TJA121M31 芯片(U13)实现主控与 485 总线的信号隔离;

  • 总线收发:通过 GM485E 收发器(U16)完成 485 差分信号的转换;

  • 电源隔离:通过 B0505S-1WR3 模块(U14)为 485 电路单独供电,避免共地干扰;

  • 接口:通过 J15 接口连接外部 485 设备。

这些主要是485测试到的波形能够使用但是波形可能不太正常峰值没有到3.3V还得修改。

  • 2.2.3 通用隔离输入输出模块

通过 EL357N 光耦实现信号隔离:

  • 隔离输入:外部信号经 2.2K 电阻限流、1N4148W 防反后,通过光耦(U18、U19)传输至主控(MCU_IN1~IN4,接口 U40);

  • 隔离输出:主控信号(MCU_OUT1~OUT2)经光耦(U20、U21)隔离后输出(接口 U42、U7),配合 4.7K 上拉电阻增强驱动能力。

  • 2.3.5 电源模块

设计多级稳压电路,为各模块供电:

  • +12V 输入:经 U54 芯片稳压后,供给无刷电机驱动等高压模块;

  • +12V 转 + 5V:经 U35 芯片转换,供给 485 通讯、无刷驱动电路;

  • +5V 转 + 3.3V:经 RT90L3-33GB LDO(U36)转换,供给主控、光耦、电流检测芯片等低压模块;

滤波:各电源支路搭配 10uF/0.1uF 电容,实现稳压滤波。

下面这张图是电源的纹波还可以个人感觉可能有机会再调试下电阻电容

下面的这几张是降压后的波形

3. 代码工程

  • 3.1源码分享

  • 分享 RT-Thread 在瑞萨 VisionBoard 的适配代码;

  • 提供 485 驱动、无刷电机驱动、电源监测等模块的源码(含初始化、指令解析逻辑)。

  • rasc配置以及部分代码

    PWM参数死区时间波形方式以及频率等需要按照实际去调
    可参考RT-Thread指南

  • 《RA8D1 Vision Board开发实践指南》上线啦

  • RT-Thread-【Vision Board 创客营】Vision Board上的ADC实践RT-Thread问答社区 - RT-Thread

  • [RT-Thread-Vision Board创客营] PWM模块实践RT-Thread问答社区 - RT-Thread

  • 十三、RA8D1 Vision Board上的IIC实践(欧小龙)

  • motor_ctrl.c

    #include "FOC.h" #include "rtdevice.h"
    
    #define MOTOR_THREAD_STACK_SIZE    2048
    
    #define MOTOR_THREAD_PRIORITY      8
    
    #define MOTOR_THREAD_TIMESLICE     10
    
    #define MAX_TARGET_SPEED_RPM       2400.0f
    
    #define SPEED_RAMP_RATE            100.0f
    
    static float current_target_speed = 0.0f;
    
    static rt_bool_t motor_enabled = RT_FALSE;
    
    static rt_uint16_t motor_pole_pairs = 7;
    
    static float rpm_to_radps(float rpm) {
    
          return rpm  2.0f  PI / 60.0f;
    
    }
    
    static float radps_to_rpm(float radps) {
    
         return radps  60.0f / (2.0f  PI); 
    
    }
    
    static float speed_ramp(float target_speed, float current_speed, float dt) {
    
          float max_delta = SPEED_RAMP_RATE * dt;
    
           if (target_speed > current_speed) {
    
                     current_speed += max_delta; 
    
                        if (current_speed > target_speed) current_speed = target_speed; 
    
            } else {  
    
                      current_speed -= max_delta; 
    
                      if (current_speed < target_speed) current_speed = target_speed; 
    
            } return current_speed; 
    
    }
    
    static void motor_thread_entry(void *parameter) {
    
       static rt_tick_t last_time = 0; 
    
       static float current_speed_rpm = 0.0f;
    
    foc_init(); foc_start(); motor_enabled = RT_TRUE;
    
    while (1) { rt_tick_t current_time = rt_tick_get(); float dt = (current_time - last_time) * 0.001f; if (dt <= 0) dt = 0.001f;
    
    if (motor_enabled) {
        float target_velocity = get_target_velocity();
        float target_speed_rpm = radps_to_rpm(target_velocity);
        
        target_speed_rpm = target_speed_rpm > MAX_TARGET_SPEED_RPM ? MAX_TARGET_SPEED_RPM : target_speed_rpm;
        target_speed_rpm = target_speed_rpm < -MAX_TARGET_SPEED_RPM ? -MAX_TARGET_SPEED_RPM : target_speed_rpm;
        
        current_speed_rpm = speed_ramp(target_speed_rpm, current_speed_rpm, dt);
        
        float target_mech_radps = rpm_to_radps(current_speed_rpm);
        FOC_M0_SET_VEL(target_mech_radps);
        
        foc_loop();
        
        if (current_time - last_time > RT_TICK_PER_SECOND) {
            float actual_vel = getVelocity();
            float actual_rpm = radps_to_rpm(actual_vel);
            rt_kprintf("Motor: Target=%.1fRPM, Actual=%.1fRPM\n", current_speed_rpm, actual_rpm);
            last_time = current_time;
        }
    } else {
        current_speed_rpm = 0;
        FOC_M0_SET_VEL(0);
    }
    
    rt_thread_mdelay(1);
    last_time = current_time;
    
    }
    
    }
    
    void motor_start(void) {
    
         rt_thread_t tid = rt_thread_create("motor", motor_thread_entry, RT_NULL,                                                                                                          MOTOR_THREAD_STACK_SIZE, MOTOR_THREAD_PRIORITY,                                                                          MOTOR_THREAD_TIMESLICE); 
    
         if (tid) { rt_thread_startup(tid); 
    
        }
    
    }
    
    void motor_enable(rt_bool_t enable) {
    
        motor_enabled = enable; 
    
        if (!enable) {
    
              set_target_velocity(0); 
    
         } 
    
    }



    rs485.c

    #include "bsp_rs485.h"
    #include "hal_data.h"
    
    #define RS485_RX_BUF_SIZE 100
    #define RS485_SWITCH_DELAY 10
    
    static uint8_t rs485_rx_data[RS485_RX_BUF_SIZE];
    static volatile uint16_t rs485_rx_datalen;
    static volatile bool rs485_rx_complete;
    static volatile bool rs485_tx_complete;
    
    fsp_err_t RS485_Init(void)
    {
        fsp_err_t err = FSP_SUCCESS;
        err = R_SCI_B_UART_Open(&g_uart2_rs485_ctrl, &g_uart2_rs485_cfg);
        if(FSP_SUCCESS != err) return err;
        err = R_SCI_B_UART_Read(&g_uart2_rs485_ctrl, rs485_rx_data, RS485_RX_BUF_SIZE);
        return err;
    }
    
    void RS485_Print(uint8_t *str, uint32_t strlen)
    {
        RS485_TX_EN;
        R_BSP_SoftwareDelay(RS485_SWITCH_DELAY, BSP_DELAY_UNITS_MILLISECONDS);
        rs485_tx_complete = false;
        R_SCI_B_UART_Write(&g_uart2_rs485_ctrl, str, strlen);
        while(false == rs485_tx_complete);
        R_BSP_SoftwareDelay(RS485_SWITCH_DELAY, BSP_DELAY_UNITS_MILLISECONDS);
        RS485_RX_EN;
    }
    
    uint16_t RS485_GetRxData(uint8_t *buf, uint16_t len)
    {
        uint16_t copy_len = (rs485_rx_datalen < len) ? rs485_rx_datalen : len;
        R_BSP_InterruptsDisable();
        memcpy(buf, rs485_rx_data, copy_len);
        rs485_rx_datalen = 0;
        rs485_rx_complete = false;
        R_BSP_InterruptsEnable();
        R_SCI_B_UART_Read(&g_uart2_rs485_ctrl, rs485_rx_data, RS485_RX_BUF_SIZE);
        return copy_len;
    }
    
    bool RS485_GetRxComplete(void)
    {
        return rs485_rx_complete;
    }
    
    void rs485_uart2_callback(uart_callback_args_t *p_args)
    {
        switch(p_args->event)
        {
            case UART_EVENT_RX_COMPLETE:
                rs485_rx_complete = true;
                break;
            case UART_EVENT_RX_CHAR:
                R_BSP_InterruptsDisable();
                if(rs485_rx_datalen < RS485_RX_BUF_SIZE)
                {
                    rs485_rx_data[rs485_rx_datalen] = (uint8_t)p_args->data;
                    rs485_rx_datalen++;
                }
                R_BSP_InterruptsEnable();
                break;
            case UART_EVENT_TX_COMPLETE:
                rs485_tx_complete = true;
                break;
            default:
                break;
        }
    }