at32f403版本的汇川MD500E代码,完全版,有感无感都包含,除了数码管显示屏蔽了,别的都移植过去了。 只适合学习用,不适用于量产

直接上硬货——最近在GitHub上扒到个AT32F403移植的汇川MD500E代码包,这玩意儿有点意思。原厂方案用的GD32F4,现在直接换AT32F403硬刚,关键是把有感无感方案都打通了,连霍尔信号的处理都没落下。数码管显示虽然被注释了,但底层驱动还在,想恢复的话改几行宏定义就能诈尸。

看这个PWM初始化的骚操作:

void hw_pwm_init(void)
{
    gpio_init_type gpio_init_struct;
    crm_clocks_freq_type crm_clocks_freq_struct;
    
    crm_get_clocks_freq(&crm_clocks_freq_struct);
    // 时钟树配置直接暴力上240MHz
    if(crm_clocks_freq_struct.sclk_freq != 240000000)
    {
        hw_clock_config(); // 超频警告!
    }
    
    gpio_init_struct.gpio_pins = GPIO_PINS_6 | GPIO_PINS_7 | GPIO_PINS_8;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_pull = GPIO_PULL_DOWN;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX; // 复用模式直接起飞
    gpio_init(GPIOA, &gpio_init_struct);
    
    tmr_base_init(TMR1, 5999, 239); // 载波频率直接算死
    tmr_cnt_dir_set(TMR1, TMR_COUNT_UP);
    tmr_primary_mode_select(TMR1, TMR_PRIMARY_MMS_RESET);
}

这段代码有几个亮点值得盘:首先时钟配置直接锁死240MHz,完全不给动态调整留余地,典型的实验室作风。GPIO配置里复用模式选择简单粗暴,直接把三个PWM通道拉满。注意那个tmrbaseinit的参数,载波频率算得死死的,明显是手动计算的结果,没走原厂库函数那套复杂的配置流程。

有感方案的处理更带劲,看这个霍尔中断:

__irq void HALL_IRQHandler(void)
{
    static uint8_t last_hall = 0;
    uint8_t current_hall = (gpio_input_data_bit_read(HALL_U_PORT, HALL_U_PIN) << 2) |
                           (gpio_input_data_bit_read(HALL_V_PORT, HALL_V_PIN) << 1) |
                           gpio_input_data_bit_read(HALL_W_PORT, HALL_W_PIN);
    
    if(current_hall != last_hall)
    {
        motor.angle_offset = hall_angle_table[current_hall]; // 查表大法好
        last_hall = current_hall;
        exti_flag_clear(EXTI_LINE_9); // 手动清中断标志
    }
}

这里有个骚操作——用GPIO直接读取霍尔信号,而不是用定时器的编码器接口。中断处理里直接查表获取角度偏移,简单粗暴有效。注意extiflagclear这个函数调用,原厂库函数里其实没有这个命名,明显是移植时自己封装的中断标志清除操作。

at32f403版本的汇川MD500E代码,完全版,有感无感都包含,除了数码管显示屏蔽了,别的都移植过去了。 只适合学习用,不适用于量产

无感方案的反电势检测部分更野:

void bemf_detect(void)
{
    uint16_t phase_voltage[3];
    adc_regular_data_read(&hadc1); // 三路ADC同步采样
    
    phase_voltage[0] = hadc1.regular_data[0] * V_BUS / 4096;
    phase_voltage[1] = hadc1.regular_data[1] * V_BUS / 4096;
    phase_voltage[2] = hadc1.regular_data[2] * V_BUS / 4096;
    
    // 过零检测的暴力算法
    if((phase_voltage[0] - phase_voltage[1]) > 50) motor.zcd_flag |= 0x01;
    if((phase_voltage[1] - phase_voltage[2]) > 50) motor.zcd_flag |= 0x02;
    if((phase_voltage[2] - phase_voltage[0]) > 50) motor.zcd_flag |= 0x04;
}

这里ADC采样直接拿原始数据做电压差比较,阈值硬编码50mV,完全没有滤波处理。motor.zcd_flag的处理方式也够原始,直接按位操作状态标志,这种写法在量产项目里会被锤,但学习时反而容易理解底层逻辑。

注意代码里到处都是这种注释:

// 此处有坑,上电必须延迟500ms以上 
// 寄存器版本和库函数混用,小心!

明显是移植过程中踩坑后的灵魂标记。特别留意motor.c文件里的这个函数:

void dead_time_compensation(float duty)
{
    // TODO: 死区补偿算法未验证
    duty += 0.05f * (motor.temperature - 25); // 拍脑袋的温度补偿
}

温度补偿系数直接写死0.05,温度基准点固定在25度,这种玄学参数一看就是实验室产物。但恰恰是这种不完美的实现,反而更适合学习——你能清楚看到哪些地方需要优化。

移植时最骚的操作在hal_conf.h里:

//#define USE_DISPLAY // 删库保平安
#ifdef USE_DISPLAY
    #include "seg_lcd.h"
#else
    #define seg_lcd_show(...)
#endif

直接把数码管显示功能宏定义成空操作,这种"掩耳盗铃"式的屏蔽虽然简单有效,但会导致编译后的bin文件包含冗余代码。不过对于学习来说无所谓,反而保留了功能恢复的可能性。

最后提醒几点:

  1. 工程里混用寄存器操作和HAL库,中断服务函数里甚至出现直接操作NVIC寄存器的写法
  2. 电机参数结构体里的某些字段从未被调用,可能是预留接口
  3. 无感启动部分的斜坡函数是用查表法实现的,表格数据来自MATLAB生成
  4. 整个工程没有使用RTOS,裸机轮询架构,状态机处理比较原始

这个代码仓库的价值在于它把算法层和硬件层的对接完全暴露出来,特别是传感器信号处理到PWM输出的完整链路。虽然工程规范程度不及量产项目,但正是这些"不完美"让我们有机会看到真实开发中的妥协与权衡。想深入理解FOC驱动开发的话,这个移植版本比那些过度封装的SDK更有嚼头。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐