KDocs 研发资料库
  • IOT(BLE-AD-MCU)资料链接入口汇总
  • BLE硬件设计指南
  • BLE 认证汇总
  • AD系列入门到进阶开发指导文档
  • AC75开发指导文档
  • AW31开发指导文档
  • AW33开发指导文档
  • AC63NN开发指导文档
  • HID相关资料入口
  • HID问题汇总

AC75开发指导文档

📎 原始文档:https://www.kdocs.cn/l/cbp5xjYrUCfe AC75开发指导文档

  1. 文档汇总 项目帮助文档非常重要,一定要去阅读!!!!
    image C:\JLStudio\standard\电机控制\AC75N\1.0.0\docs\html\index.html
    imageimageimage 【AC75N用户手册V1.0.pdf】 方案商需要找代理商拿,代理商找杰理助理申请。

二、环境介绍

  1. SDK下载 可视化工具下载
    Git-2.38.1-64-bit.zip
    JLStudio_4.7.0_20251203.zip
    需要先安装 git,再安装 JLStudio
    imageimageimageJLstudio可视化工具使用说明.otl
    JLStudio的账户和SDK权限,方案商需要找代理商申请,代理商再找杰理助理申请。
  2. 下载介绍 杰理强升工具使用说明.pdf
  • AC75是串口下载,强升工具拨拨码开关第2,7档,并且要与虚拟串口绑定,虚拟串口绑定相关看上面的PDF
  • 以 AC75N 开发板为例,将工具的USB母头连接到 PC 主机。
  • 工具 的USB公头直接连接到开发板进行供电,如果是不带usb口的板子,需要从强升工具飞电源线到芯片VPWR脚。
  • 将工具的TX脚与开发板PC10(update io)相连。
  • 做完以上操作,编译sdk完成后会自动进入下载 image

芯片选型

image

三、开发介绍

1 普通外设

1.1UART

芯片共2 路串口: uart0 无dma , uart1 全功能
AC75-V150-UART0收到的数据UART1发出去.7z
AC75UART1-收发TEST.7z

1.2 SPI

AC75-全双工SPI测试demo.7z
AC75-V151Beta_SPI从机发送测试.7z

1.3 I2C

1.4 CAN

1.5 FAQ

1.5.1 运放默认态导致I/O耦合问题以及高压IO做ADC测量相关

【金山文档 | WPS云文档】 AC75N系列问题及处理方法汇总
https://www.kdocs.cn/l/chR0Wfy1Hjj4

2电机驱动会用到的外设资源

1.1简介

最高系统频率 : 192M
RAM + CACHE : 28KB + 8KB
2 x ADV_TIMER多功能高级定时器
1 x 12bit GPADC Up to 4MSPS in 11-bit
2 x 10bit GPDAC
4 x OPA运放
4 x CMP比较器
2 x BEMF Resistor 虚拟中点
2 x VBUF 电压跟随器

1.2 GPADC模/数转换

image GPADC输入通道选择如上图,可以是运放输出,也可以是GPIO单端或差分输入或其他多种模拟输入。
GPADC既可以用来做三相电流的获取,也可以用作普通ADC检测。工作模式分为DMA模式和CPU模式,两者不得同时使用。通常FOC工程是DMA模式,BLDC工程是CPU模式,两种模式添加通道方法是不一样的,不要搞错初始化添加通道的位置。
DMA模式可以软件触发,或通过trigo2触发,trigo2 又和ADV_TIMER的比较输出通道相连。
具体寄存器描述看AC75用户手册。
驱动代码在gpadc.c
应用配置看gpadc_demo.c,或实际工程的adc_cfg_init()。

void adc_cfg_init(void)
{
    u8 index = 0;
    irq_unmask_set(IRQ_SARADC_IDX);

    struct adc_ext_trig_config trig_config = {
        .tsel = TS_ADVTIMER0_TRGO2,
        .tedge = RISING_EDGE_MODE,
        .trig_wait = 0,
    };
    adc_ext_trig_enable(&trig_config);

    struct adc_dma_config dma_config;
    memset(&dma_config, 0, sizeof(dma_config));
    dma_config.dma_mod = 0;
    dma_config.dma_ie_en = 1;
    dma_config.discen = 0;
    dma_config.adc_dma_irq_callback = dma_adc_get_callback;
    dma_config.ch[index++] = AD_OP0_OUT + IU_OPA;
    dma_config.ch[index++] = AD_OP0_OUT + IV_OPA;
    dma_config.ch[index++] = AD_OP0_OUT + IW_OPA;
    dma_config.ch[index++] = adc_io2ch(TCFG_NTC_PORT);
    dma_config.ch[index++] = adc_io2ch(TCFG_HV_PORT);
#if TCFG_ADKEY_ENABLE
    dma_config.ch[index++] = get_adkey_ad_channel();
#endif
    dma_config.ch_num = index;
    dma_config.buf_len = index;
    dma_ibuf = dma_malloc(index * 2);
    dma_config.dma_buf_adr = (u32)dma_ibuf;
    if (dma_ibuf == NULL) {
        log_error("dma_ibuf malloc error, %d\n", index * 2);
        return;
    }
    adc_enter_dma_sample(&dma_config);

    log_info("adc init end\r\n");
}
void adc_test(void)
{
//过采样测试
#if OV_SAMPLE_TEST
    struct adc_ovsmp_config ovsmp_config = {
        .ovsmp_len = 2,
        .ovsmp_shift = 1,
    };
    adc_ovsmp_enable(&ovsmp_config);
#if 0
    adc_ovsmp_disable();
#endif
#endif

//模拟看门狗测试
#if ADC_WDG_TEST
    struct adc_wdg_config wdg_config = {
        .ie_en = 1,
        .th_h_res = 4094,
        .th_l_res = 0,
        .ch0 = AD_OP3_OUT,
    };
    adc_wdg_enable(&wdg_config);
#if 0
    adc_wdg_disable();
#endif
#endif

//cpu采集测试
#if CPU_SAMPLE_TEST
    adc_add_sample_ch(AD_CH_PA4);
    adc_add_sample_ch(AD_CH_PA5);
    adc_add_sample_ch(AD_CH_PA6);
    adc_add_sample_ch(AD_CH_PB5);
#endif

//dma采集测试
#if DMA_SAMPLE_TEST
    u32 *test_dma_buf = malloc(16 * 2);
    struct adc_dma_config dma_config = {
        .dma_ie_en = 1,
        .dma_mod = 0,
        .discen = 0,
        .ch_num = 4,
        .buf_len = 16,
        .dma_buf_adr = (u32)test_dma_buf,
        .ch[0] = AD_CH_PA4,
        .ch[1] = AD_CH_PA5,
        .ch[2] = AD_CH_PA6,
        .ch[3] = AD_CH_PB5,
        .adc_dma_irq_callback = dma_test_callback,
    };
    adc_enter_dma_sample(&dma_config);
#if 0
    dma_config.ch_num = 6;
    dma_config.ch[4] = AD_CH_PA4;
    dma_config.ch[5] = AD_CH_PA5;
    adc_update_dma_sample(&dma_config);
#endif
#if 0
    mdelay(2000);
    adc_exit_dma_sample();
#endif
#endif
}

1.3 GPDAC数/模转换

image 内嵌2个10位DAC,模拟电压输出范围在0V至IOVDD之间
模拟电压可输出到GPIO,输出等效内阻约4KΩ
GPDAC可以连接电压跟随器和比较器负输入端,通常与比较器一起使用于过流保护
详细描述和寄存器看AC75用户手册GPDAC章节。
驱动代码在gpdac.c,应用代码可参考1.7小节的多外设联动实现的母线过流保护,或实际对应工程的配置。

1.4 ADV_TIMER高级定时器

通常用于输出三相6路互补死区PWM,可以设置起中断的时刻点以及adc采样时刻点并通过trigo2触发AD转换。也可以只设置采样时刻点,软件来触发adc转换。
trigo1是用来实现主从定时器同步,详情用法看手册。
定时器DMA用来在产生更新事件的时候搬运数据改变外设寄存器,如改变CCR寄存器可实现非对称的pwm输出,通常用于foc单电阻采样。
可以跑一下SDK中adv_timer_demo.c里面的例子来熟悉相关配置。ADV_TIMER的驱动是参考STM32H7的HAL库函数写法,寄存器也和STM32H7的类似,可参考AC75的用户手册或STM32H7的资料。相关应用代码在对应工程的adv_timer_cfg_init()或adv_timer_demo.c里面。

// TIM_TimeBase
/*
 * 测试项:时基单元测试
 * 基本配置:系统时钟48MHz,tmr分频时钟1kHz,ARR=1000,RCR = 5,使能更新中断
 * 输出:每隔6s产生一次更新中断,打印一次sr
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    log_info("tim update");
}

static void timebase_test(void)
{
    log_info("%s", __FUNCTION__);

    adv_timer_rst();

    TimHandle0.Instance               = JL_ADV_TIMER0;
    TimHandle0.Init.Period            = 1000 - 1;
    TimHandle0.Init.Prescaler         = (48000000L / 1000) - 1; // 1kHz
    TimHandle0.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
    TimHandle0.Init.CounterMode       = TIM_COUNTERMODE_UP;
    TimHandle0.Init.RepetitionCounter = 5;

    HAL_TIM_Base_Init(&TimHandle0);

    HAL_TIM_Base_Start_IT(&TimHandle0);
}
...
...
void adv_timer_test(void)
{
#if ((TEST_LIST != TEST_HALL_BLDC) && (TEST_LIST != TEST_COMP_BLDC))
    request_irq(IRQ_ADV_TMR0_IDX, 0, adv_timer0_irq_callback, 0);
    request_irq(IRQ_ADV_TMR1_IDX, 0, adv_timer1_irq_callback, 0);
#endif

#if (TEST_LIST == TEST_TIMEBASE)
    timebase_test();
#ellf(....)
    ...
#endif
}
void adv_timer_cfg_init(void)
{
    u32 half_period = clk_get("lsb") / parm.pwmfreq / 2;

    adv_timer_rst();

    adv_timer_dtg_init();

    request_irq(IRQ_ADV_TMR0_IDX, 0, adv_timer0_irq_callback, 0);
    irq_unmask_set(IRQ_ADV_TMR0_IDX);

    gpio_set_function(IO_PORT_SPILT(TCFG_U_H_PORT), PORT_FUNC_TMR0_OC1P);
    gpio_set_function(IO_PORT_SPILT(TCFG_U_L_PORT), PORT_FUNC_TMR0_OC1N);
    gpio_set_function(IO_PORT_SPILT(TCFG_V_H_PORT), PORT_FUNC_TMR0_OC2P);
    gpio_set_function(IO_PORT_SPILT(TCFG_V_L_PORT), PORT_FUNC_TMR0_OC2N);
    gpio_set_function(IO_PORT_SPILT(TCFG_W_H_PORT), PORT_FUNC_TMR0_OC3P);
    gpio_set_function(IO_PORT_SPILT(TCFG_W_L_PORT), PORT_FUNC_TMR0_OC3N);

    TIM_HandleTypeStruct.Instance                 = JL_ADV_TIMER0;
    TIM_HandleTypeStruct.Init.Period            = half_period;
    TIM_HandleTypeStruct.Init.Prescaler         = 0;
    TIM_HandleTypeStruct.Init.CounterMode       = TIM_COUNTERMODE_CENTERALIGNED1;
    TIM_HandleTypeStruct.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

    HAL_TIM_PWM_Init(&TIM_HandleTypeStruct);

    TIM_OC_InitTypeStruct.OCMode         = TIM_OCMODE_PWM1;      // 输出比较模式
    TIM_OC_InitTypeStruct.OCFastMode      = TIM_OCFAST_DISABLE;
    TIM_OC_InitTypeStruct.Pulse = half_period / 2;

    TIM_OC_InitTypeStruct.OCPolarity     = CHANNEL_UH_ON;
    TIM_OC_InitTypeStruct.OCNPolarity     = CHANNEL_UL_ON;
    TIM_OC_InitTypeStruct.OCIdleState      = CHANNEL_UH_OCIDLESTATE;
    TIM_OC_InitTypeStruct.OCNIdleState     = CHANNEL_UL_OCNIDLESTATE;
    HAL_TIM_PWM_ConfigChannel(&TIM_HandleTypeStruct, &TIM_OC_InitTypeStruct, TIM_CHANNEL_1);

    TIM_OC_InitTypeStruct.OCPolarity     = CHANNEL_VH_ON;
    TIM_OC_InitTypeStruct.OCNPolarity     = CHANNEL_VL_ON;
    TIM_OC_InitTypeStruct.OCIdleState      = CHANNEL_VH_OCIDLESTATE;
    TIM_OC_InitTypeStruct.OCNIdleState     = CHANNEL_VL_OCNIDLESTATE;
    HAL_TIM_PWM_ConfigChannel(&TIM_HandleTypeStruct, &TIM_OC_InitTypeStruct, TIM_CHANNEL_2);

    TIM_OC_InitTypeStruct.OCPolarity     = CHANNEL_WH_ON;
    TIM_OC_InitTypeStruct.OCNPolarity     = CHANNEL_WL_ON;
    TIM_OC_InitTypeStruct.OCIdleState      = CHANNEL_WH_OCIDLESTATE;
    TIM_OC_InitTypeStruct.OCNIdleState     = CHANNEL_WL_OCNIDLESTATE;
    HAL_TIM_PWM_ConfigChannel(&TIM_HandleTypeStruct, &TIM_OC_InitTypeStruct, TIM_CHANNEL_3);

    TIM_OC_InitTypeStruct.Pulse = half_period - 1;
    TIM_OC_InitTypeStruct.OCMode = TIM_OCMODE_PWM2;      // 输出比较模式
    HAL_TIM_PWM_ConfigChannel(&TIM_HandleTypeStruct, &TIM_OC_InitTypeStruct, TIM_CHANNEL_4);
    __HAL_TIM_ENABLE_IT(&TIM_HandleTypeStruct, TIM_IT_CC4);

    TIM_OC_InitTypeStruct.Pulse         = 0;
    TIM_OC_InitTypeStruct.OCMode         = TIM_OCMODE_PWM2;      // 输出比较模式
    HAL_TIM_PWM_ConfigChannel(&TIM_HandleTypeStruct, &TIM_OC_InitTypeStruct, TIM_CHANNEL_5);
    TIM_OC_InitTypeStruct.Pulse         = 0;
    TIM_OC_InitTypeStruct.OCMode         = TIM_OCMODE_PWM2;      // 输出比较模式
    HAL_TIM_PWM_ConfigChannel(&TIM_HandleTypeStruct, &TIM_OC_InitTypeStruct, TIM_CHANNEL_6);

    TIM_BreakDeadTimeConfigTypeStruct.BreakState       = TIM_BREAK_ENABLE;
    TIM_BreakDeadTimeConfigTypeStruct.OffStateRunMode  = TIM_OSSR_ENABLE;
    TIM_BreakDeadTimeConfigTypeStruct.OffStateIDLEMode = TIM_OSSI_ENABLE;
    TIM_BreakDeadTimeConfigTypeStruct.LockLevel        = TIM_LOCKLEVEL_OFF;
    TIM_BreakDeadTimeConfigTypeStruct.BreakPolarity    = TIM_BREAKPOLARITY_HIGH;
    TIM_BreakDeadTimeConfigTypeStruct.BreakFilter      = 0b1111;
    TIM_BreakDeadTimeConfigTypeStruct.AutomaticOutput  = TIM_AUTOMATICOUTPUT_DISABLE;
    HAL_TIMEx_ConfigBreakDeadTime(&TIM_HandleTypeStruct, &TIM_BreakDeadTimeConfigTypeStruct);

    TIM_BreakInputConfigStruct.Source  = TIM_BREAKINPUTSOURCE_COMP0 + IBUS_COMP;
    TIM_BreakInputConfigStruct.Enable = TIM_BREAKINPUTSOURCE_ENABLE;
    TIM_BreakInputConfigStruct.Polarity = TIM_BREAKINPUTSOURCE_POLARITY_HIGH;
    HAL_TIMEx_ConfigBreakInput(&TIM_HandleTypeStruct, TIM_BREAKINPUT_BRK, &TIM_BreakInputConfigStruct);

    TIM_MasterConfigTypeStruct.MasterOutputTrigger2 = TIM_TRGO2_OC5REF_RISING_OC6REF_RISING;
    TIM_MasterConfigTypeStruct.MasterSlaveMode      = TIM_MASTERSLAVEMODE_ENABLE;
    HAL_TIMEx_MasterConfigSynchronization(&TIM_HandleTypeStruct, &TIM_MasterConfigTypeStruct);

    TIMDMATypeStructure.DBA = TIM_DMABASE_CCR1;
    TIMDMATypeStructure.OP = TIM_DMA_WRITE;
    TIMDMATypeStructure.DmaRoundEn = TIM_DMA_ROUND_DISABLE;
    TIMDMATypeStructure.DBL = 2;
    TIMDMATypeStructure.ADR = (u32)ccrx_buf;
    TIMDMATypeStructure.DOL = 2;
    TIM_DMA_SetConfig(&TIM_HandleTypeStruct, &TIMDMATypeStructure);
    __HAL_TIM_ENABLE_DMA(&TIM_HandleTypeStruct, TIM_DMA_UPDATE);
}

1.5 COMP模拟比较器

image 通常用于BEMF过零点检测和母线过流保护。
所有比较器的输入和输出都可以连接到GPIO。
比较器的正输入端可连接片内运放的输出信号。
比较器的负输入端可连接GPDAC或BEMF虚拟中点信号。
具体寄存器介绍参考AC75用户手册COMP章节。
应用代码可看comp_demo.c或motor.c 的motor_bemf_init() ,或1.7小节的多外设联动实现的母线过流保护。驱动代码在comp.c里面。

static void comp_test_irq_callback(u8 val)
{
    //比较器回调
    /* printf("1"); */
}

void comp_test(void)
{
    //以COMP0为例,当PA4输入电压高于PB9电压时,打印1

    struct compx_config compx = {
        .lock = 0,
        .muxn_en = 1,
        .muxp_en = 1,
        .prail_en = 1,
        .nrail_en = 1,
        .ie_en = 1,
        .comp_sw = 1, //输出到PB8
        .polarity = 0, //输出不反相
        .muxn = CMPxN, //PB9
        .muxp = BEMF_U0,  //PA4
        .compx_irq_callback = comp_test_irq_callback,
        .hyp = HYP_0mV,
    };
    compx_init(COMP0, &compx);

    /* compx_uninit(COMP0); */
}

1.6 VBUF电压跟随器

image 内置2个电压跟随器BUF0和BUF1,通常用于给运算放大器提供偏置电压。
电压跟随器输入端可选择GPDAC或分压器,BUF0输出VREF0提供OP0、OP1和OP2偏置电压,BUF1输出VREF1提供OP3偏置电压。
寄存器介绍在AC75用户手册的MOTOR_CTL章节BUF_CON寄存器。
代码例程参考实际工程或1.7小节的多外设联动实现的母线过流保护。

1.7 OPA运算放大器

image

image 内置输入电阻(约5KΩ)和反馈电阻,支持4档增益调节:2、4、8和16倍。
OP0、OP1和OP2的偏置电压由内部VREF0提供,用于相电流采样信号的放大。
OP3的偏置电压由VREF1提供,用于母线电流采样信号放大。
运放输出信号内部连接到GPADC采样通道和比较器的P输入端。
寄存器介绍在AC75用户手册的MOTOR_CTL章节。
相关驱动代码在mc.c和mc.h中 ,应用代码在mc_demo.c或motor.c
GPDAC、电压跟随器、模拟比较器、OPA、ADV_TIMER联动实现母线过流保护自动BREAK输出的例子如下:

// ------------母线电流配置.json------------
#define IBUS_OPA                                  OPA3             // IBUS_OPA
#define IBUS_DAC                                  DAC0             // IBUS_DAC
#define IBUS_COMP                                 COMP3            // IBUS_COMP
#define IBUS_BUF                                  BUF1             // IBUS_BUF
#define FPNDCURRENT                               2.5              // 硬件过流/A
#define IBUS_OPA_GAIN                             GAIN_X4          // IBUS_OPA_GAIN
#define IBUS_BUF_MUX_SEL                          AVDDREF_DIV2     // IBUS_BUF_MUX_SEL
#define IBUS_OPA_OUT2IO_EN                        0                // IBUS_OPA_OUT2IO_EN
#define IBUS_DAC_OUT2IO_EN                        0                // IBUS_DAC_OUT2IO_EN
#define IBUS_BUF_OUT2IO_EN                        0                // IBUS_BUF_OUT2IO_EN
#define IBUS_COMP_OUT2IO_EN                       0                // IBUS_COMP_OUT2IO_EN
#define IBUS_COMP_IE_EN                           0                // IBUS_COMP_IE_EN
#define IBUS_COMP_POLARITY                        0                // IBUS_COMP_POLARITY
// ------------母线电流配置.json------------
static void motor_ibus_init(void)
{
    struct buf_cfg buf;
    struct opa_cfg opa;

    //vddio电压采集初始化
    current_offset.vddio = (float)adc_get_voltage_by_blocking(AD_CH_IOVDD, 10) / 1000.0;
    log_info("vddio_volt: %.3fV", current_offset.vddio);

    //母线电流BUF初始化
    buf.bypass_en = 0;
    buf.vrefout_en = IBUS_BUF_OUT2IO_EN;
    buf.mux_en = 1;
    buf.sel = IBUS_BUF_MUX_SEL;
    mc_buf_open(IBUS_BUF, &buf);
    log_info("IBUS_BUF: BUF%d", IBUS_BUF);

    //母线电流OPA初始化
    opa.in_sw = NFA;
    opa.o_sw = IBUS_OPA_OUT2IO_EN;
    opa.gain = IBUS_OPA_GAIN;
    mc_opa_open(IBUS_OPA, &opa);
    log_info("IBUS_OPA: OPA%d", IBUS_OPA);

    //母线电流OPA偏置初始化
    current_offset.ibus_offset = adc_get_value_by_blocking(AD_OP0_OUT + IBUS_OPA, 10);
    log_info("ibus offset: %d", current_offset.ibus_offset);

    //母线电流硬件过流GPDAC配置
    current_offset.ibus_dac_data = (IBUS_RSHUNT * (float)FPNDCURRENT * mc_opa_gain_convert(IBUS_OPA_GAIN) \
                                    + (float)current_offset.ibus_offset / AD_MAX_VALUE * current_offset.vddio) / current_offset.vddio * 65535;
    gpdac_open(IBUS_DAC, 1, IBUS_DAC_OUT2IO_EN);
    gpdac_data_set(IBUS_DAC, 65535, 1);
    log_info("IBUS_DAC: DAC%d, DAC_DATA: %d", IBUS_DAC, current_offset.ibus_dac_data);

    //母线电流硬件过流COMP配置
    struct compx_config compx = {
        .lock = 0,
        .muxn_en = 1,
        .muxp_en = 1,
        .prail_en = 1,
        .nrail_en = 1,
    };
    compx.ie_en = 0;
    compx.comp_sw = IBUS_COMP_OUT2IO_EN;
    compx.polarity = IBUS_COMP_POLARITY;
    compx.muxn = DAC0_OUT + IBUS_DAC;
    compx.muxp = OPA0_OUT + IBUS_OPA;
    compx.compx_irq_callback = ibus_comp_irq_callback;
    compx.hyp = HYP_38mV;
    compx_init(IBUS_COMP, &compx);
    log_info("IBUS_COMP: COMP%d", IBUS_COMP);
}

1.8 BEMF Resistor虚拟中点

image 内置2组虚拟中心分压电阻,通常用于BLDC方波驱动BEMF过零检测或顺逆风检测
虚拟中心电位由片内分压电阻(6.8KΩ±20%)产生,并通过BEMF_MIDx连接到比较器的N端输入
外置BEMF虚拟中心分压电阻时,可关断片内BEMF_MIDx接口输入
BEMF输入电压必须限制在0~IOVDD范围内使用,避免过压导致芯片接口损坏
image

3 调试打印相关

3.1 log串口助手打印

// ------------板级配置.json------------
#define TCFG_DEBUG_UART_ENABLE                    1                // 调试串口
#if TCFG_DEBUG_UART_ENABLE
#define TCFG_DEBUG_UART_TX_PIN                    IO_PORTC_00      // 输出IO
#define TCFG_DEBUG_UART_BAUDRATE                  1000000          // 波特率
#define TCFG_EXCEPTION_LOG_ENABLE                 1                // 打印异常信息
#define TCFG_EXCEPTION_RESET_ENABLE               1                // 异常自动复位
#endif // TCFG_DEBUG_UART_ENABLE

3.2虚拟示波器

以串口为桥梁,默认波特率是6000000,以图形化波形的方式输出变量的值,通常用来看电机驱动里面实时性很高的变量,如三相电流,观测器角度,id , iq,omega等等。
驱动代码在virtual_scope.c,配置宏如下:

#define VIRTUAL_SCOPE_EN                          1                // 虚拟示波器使能
#define TCFG_VIRSUAL_SCOPE_BAUD                   6000000          // 虚拟示波器串口波特率
#define TCFG_VIRTUAL_SCOPE_TX                     IO_PORTC_06      // 虚拟示波器TX
#define TCFG_VIRTUAL_SCOPE_RX                     IO_PORTC_07      // 虚拟示波器RX

虚拟示波器上位机安装包,方案商需要找代理商拿,代理商找杰理助理申请。

3.3 注意事项

芯片只有2个uart,sdk默认配置打印用uart0,虚拟示波器用uart1,如果用户需要用到uart1要注意和虚拟示波器做互斥。

4 可视化工程相关

4.1 BLDC和FOC驱动方式的选择

对噪音和振动动要求不高的场景选BLDC方波驱动:如暴力风扇,电钻等。
反之则选FOC驱动:如风扇类、吸尘器、高速风筒等。
电机的反电动势波形呈梯形状选BLDC方波驱动。
电机的反电动势波形呈正弦波两种驱动都可选择。
image

4.2 BLDC方波工程

4.2.1工程创建

image BLDC的驱动算法是全开源的,主要包含开环强拖,BEMF过零检测,6步换相。可视化创建工程时可选择的是violentfan-demo,是一个暴力风扇的实例。

4.2.2 电机配置

image

4.2.2 驱动板硬件配置

image

4.2.3反电动势配置

image

4.2.4 故障配置

image

4.3 FOC 工程

4.3.1工程创建

image FOC驱动算法是封库的,可视化创建工程时,可选择fan或hairdryer demo,fan是针对扇类电机,hairdryer就是标准的高速吹风筒。单电阻或多点阻是在sdk中选择的,电感量小的电机不建议选单电阻,电流会采不准。单电阻的电流重构复杂,不同的硬件环境需要考虑采样窗口是否足够,不够还要调PWM偏移,也就是用到非对称式PWM输出,就会带来一定的运转噪声。

4.3.2 电机配置

image 相电感,相电阻,磁链等参数,具体的参数含义和测量看项目帮助文档或本文后续章节

4.3.3 驱动板硬件配置

image 主要配置6路PWM的引脚和有效电平死区时间等等,不同的MOS、预驱、无预驱等硬件环境会有不同的配置。

4.3.4 母线电流配置

image

4.3.5 算法配置

创建工程时选择fan的话,是非线性磁链观测器,hairdryer是PLL观测器。以下展示的是fan。
imageimageimage 可视化上算法配置对应sdk中如下,下面这些参数在fan_parm.h有详细的注释

struct tfan_parm parm =  {
    .pwmfreq = 20000,
    .speedfreq = 4000,
    .open_ud = 0,
    .open_uq = 0.2,
    .open_add_theta = 10,
    .curloop_idref = 0.0167,
    .curloop_iqref = 0.05,
    .pwmratio = 1,
    .tadjust_time0 = 0,
    .tadjust_time1 = 0,
    .dkp = 0.5923,
    .dki = 0.0428,
    .doutmax = 0.7,
    .qkp = 0.5923,
    .qki = 0.0428,
    .qoutmax = 0.85,
    .wkp = 3.88,
    .wki = 0.001,
    .iqrefoutmax_lowspeed = 0.28,
    .iqrefoutmax_normalspeed = 0.489,
    .pki = 0.00015,
    .spdoutmax = 0.172,
    .lpf_omega_constant = 0.0006,
    .idhandle = 2,
    .askewi = 2,
    .lada = 2,
    .lada1 = 0.9,
    .alpha = 0.1,
    .alpha0 = 0.1,
    .svcm_rs = 1.81,
    .svcm_ld = 2.03,
    .svcm_lq = 2.03,
    .svcm_flux = 0.009722,
    .llpf_a1 = -1.924,
    .llpf_a2 = 0.928,
    .llpf_b0 = 0.000512,
    .llpf_b1 = 0.001024,
    .llpf_b2 = 0.000512,
    .harmonic_kp = 0.005923,
    .harmonic_ki = 0.000428,
    .harmonic_max = 0.7
};

struct tfan_object pmsm =  {
    .addtheta_enable = 0,
    .currentpi_enable = 1,
    .obs_enable = 1,
    .speedpi_enable = 1,
    .svcmobs_thetaskewi_enable = 1,
    .uvdir_switch_enable = 1,
    .curpi_feedforward_enable = 0,
    .svcmobs_alphamode_enable = 0,
    .orderpwm_construct_enable = 0,
    .keep_power_current_enable = 0,
    .de6harmonic_enable = 0,
    .offline_rlflux_enable = 0
};

4.4注意事项

安装VSCODE后,上述有关可视化工具里面的参数有不清楚的都可以直接点击该中文名,会自动弹出sdk中对应宏所在位置,全局搜索该宏,结合代码去理解其作用。

5 FOC工程一般调试方法

AC75 FOC调试方法.otl

6 电机参数测量方法

AC75电机参数测量方法.otl

四、电机驱动原理科普

image

  1. BLDC方波驱动

1.1无感换相基本原理

UVW或ABC三相,每次只给三相中的两相通电如UV、UW、VW、VU、WU、WV轮流导通,无hall传感器的是检测悬空相反电动势过零点延迟30°为换相时刻。有感的则是每次HALL状态发生跳变时换相。
imageimage 上图t0时刻指的是AB相刚导通,t1时刻是AB相通电快结束时的情况,CC'的感生电动势反向改变了,也就时过零点。
假设AA'和BB'各产生5.7V的反电动势,串起来就是11.4V,那么施加在电阻上的电压等效于12V - 11.4V =0.6V。则最终通过绕组AB的电流就是0.6 /(2*0.1) = 3A,这就是为什么电阻这么小,绕组还不会烧的原因。由于各绕组结构相同,所以CC'也会产生5.7V的感生电动势。
由于中点始终为6V,则t0时刻对地测量C点电压其值为6+5.7 =11.7V,t1时刻C点电压值为6-5.7 = 0.3V。所以,只要一直检测电机悬空相的电压,到低于中点电压,就说明转子已经转过30°,只要等30°就可以换相了。

1.2 延迟换相

根据前文所述知道在 C 相的过零点,转子只转过 30°,还需要再转 30°才可换相,那怎么知道转动余下的 30°需要多少时间呢?一种比较简单的做法是近似认为转子转速在这0°~60°的小范围区间内基本是恒定的:从 AB 相开始通电到检测出 C 相过零点的前半段时间,基本等于后半段的时间。所以只要使用定时器计数前半段的时间间隔 T1,等过零事件出现后再等待相同的时间再换相即可。
当然,更直接的方法是监测到过零点后不延迟 30°,而是直接换相。这种方法当然也可行,但是会损失一点效率,且同等情况下转矩也会减小。

1.3 消磁现象

在实际换相过程中,例如AB切换到AC,B相电流不会突然消失,自身电感续流,电动势方向和原来相反,叠在中点之上,当续流结束后,B端电位会低于中点,但这一次过零非换相过零,这就是消磁现象,所以软件需要对这段时间做消隐处理。
image

  1. FOC驱动 磁场定向控制,使定子合成磁场趋近于圆形,拉动转子转动。

2.1 算法框图

image

2.2 电流采样及重构

imageimage

2.2.1 三电阻法

不同的扇区存在某相采样窗口可能太窄,此时要舍弃掉此相的电流,用另外两相重新计算出这一相电流。

2.2.2 双电阻法

固定采集某两相的电流,然后计算第三相。这就需要保证最小的采样窗口要大于采样时间。

2.2.3 单电阻法

在每个pwm周期采样两次,才能重构出三相电流
image 如上图,此扇区采集到的就是 ia, 和-ic。单电阻法也是存在采样窗口不够的可能,要用到非对称式的PWM对策。
image

2.3 Clarke变换

image 将三相电流从自然坐标系ABC变换到静止坐标系α - β的坐标变换称为clarke变换。
image 再根据基尔霍夫定律ia+ib+ic = 0,可得最终转换公式
image

2.4 Park变换

image 将静止坐标系α - β变换到同步旋转坐标系 d - q的坐标变换称为Park 变换,公式如下
image

2.5 坐标变换波形参考

image

2.6 电流内环

将电流分量id,iq与给定值进行比较,送入pi控制器,目的是控制转矩,电流环的pi控制参数可以根据相关公式计算出来,但是实际开发中依然需要手动调试
image

2.7 速度外环

速度环作为电流环的输入,通过与当前速度进行比较,将误差输入给速度PI控制器,输出iqref和idref给到电流环。速度环的调整频率一定要低于电流环频率,否则速度环的调整会无效,造成系统振荡。

2.8 SVPWM

又叫空间矢量脉宽调制,通过改变两个矢量作用时长,可以合成任意位置的矢量,使合成矢量趋近与圆形。(100)代表U相上管导通,VW下管导通,(110)代表U相和V相上管导通,W下管导通。
imageimageimage 扇区1的T4、T6作用时长如下
image 令
image

令1个上管导通的是矢量A,两个上管导通的是矢量B,则有以下关系

扇区123456
矢量TA时长U2-U2U1-U1U3-U3
矢量TB时长U1-U3U3-U2U2-U1

则有
tc = (T/2 - TB/2 - TA/2) / 2
tb = tc + TB/2
ta = tb + TA/2

2.9 无感位置观测器

2.9.1 龙伯格或滑膜

imageimage 龙伯格和滑膜观测器是基于上面的电机数学模型,通过电压电流观测Es,反正切得出角度θ,再换算出当前的速度omega。电机未运转起来是观测不到Es,所以需要三段式启动,预定位 + 强拖 + 切换闭环

2.9.2 PLL锁相环

PLL是通过上面电机模型,计算出Ed,做为误差输入PI控制器,PI控制器的输出累加后就是角度,需要一定收敛时间。和龙伯格或滑膜一样,未转起来计算不出Ed,且角度收敛也需要时间。也是要三段式启动

2.9.3 非线性磁链

非线性磁链观测器的核心思想是直接估计定子磁链,并通过非线性项来增强观测器的收敛性和鲁棒性。与龙伯格观测器不同,它通常在静止坐标系(αβ)下直接对磁链进行积分重构,同时引入与电流和磁链相关的非线性阻尼项(或补偿项),以抑制积分漂移并改善低速性能。其基本数学模型可简化为:
ψ_α = ∫(v_α - R·i_α) dt + 非线性补偿项
ψ_β = ∫(v_~~β ~~- R·i_β) dt + 非线性补偿项
计算非线性补偿项,一种是基于电流和当前磁链的叉乘项[citation:5],另一种是强制磁链赋值收敛[citation:9],这有助于抑制误差和抖振。非线性磁链观测器适合做零速启动。

2.10 归一化定点小数

#define Q15(x) ((x < 0.0) ? (s32)(32768 * (x) - 0.5) : (s32)(32767 * (x) + 0.5))
这个宏的意义是,以32767为1,-32768为-1,来表示 -1 到 1之间的小数,加减0.5是四舍五入

本文档包含以下子页面

  • AC75N系列问题及处理方法汇总
  • AC75 FOC调试方法
  • AC75电机参数测量方法
Prev
AD系列入门到进阶开发指导文档
Next
AW31开发指导文档