AC75开发指导文档
📎 原始文档:https://www.kdocs.cn/l/cbp5xjYrUCfe AC75开发指导文档
- 文档汇总 项目帮助文档非常重要,一定要去阅读!!!!
C:\JLStudio\standard\电机控制\AC75N\1.0.0\docs\html\index.html

【AC75N用户手册V1.0.pdf】 方案商需要找代理商拿,代理商找杰理助理申请。
二、环境介绍
- SDK下载 可视化工具下载
Git-2.38.1-64-bit.zip
JLStudio_4.7.0_20251203.zip
需要先安装 git,再安装 JLStudio

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

芯片选型

三、开发介绍
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模/数转换
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数/模转换
内嵌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模拟比较器
通常用于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电压跟随器
内置2个电压跟随器BUF0和BUF1,通常用于给运算放大器提供偏置电压。
电压跟随器输入端可选择GPDAC或分压器,BUF0输出VREF0提供OP0、OP1和OP2偏置电压,BUF1输出VREF1提供OP3偏置电压。
寄存器介绍在AC75用户手册的MOTOR_CTL章节BUF_CON寄存器。
代码例程参考实际工程或1.7小节的多外设联动实现的母线过流保护。
1.7 OPA运算放大器

内置输入电阻(约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虚拟中点
内置2组虚拟中心分压电阻,通常用于BLDC方波驱动BEMF过零检测或顺逆风检测
虚拟中心电位由片内分压电阻(6.8KΩ±20%)产生,并通过BEMF_MIDx连接到比较器的N端输入
外置BEMF虚拟中心分压电阻时,可关断片内BEMF_MIDx接口输入
BEMF输入电压必须限制在0~IOVDD范围内使用,避免过压导致芯片接口损坏
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方波驱动。
电机的反电动势波形呈正弦波两种驱动都可选择。
4.2 BLDC方波工程
4.2.1工程创建
BLDC的驱动算法是全开源的,主要包含开环强拖,BEMF过零检测,6步换相。可视化创建工程时可选择的是violentfan-demo,是一个暴力风扇的实例。
4.2.2 电机配置

4.2.2 驱动板硬件配置

4.2.3反电动势配置

4.2.4 故障配置

4.3 FOC 工程
4.3.1工程创建
FOC驱动算法是封库的,可视化创建工程时,可选择fan或hairdryer demo,fan是针对扇类电机,hairdryer就是标准的高速吹风筒。单电阻或多点阻是在sdk中选择的,电感量小的电机不建议选单电阻,电流会采不准。单电阻的电流重构复杂,不同的硬件环境需要考虑采样窗口是否足够,不够还要调PWM偏移,也就是用到非对称式PWM输出,就会带来一定的运转噪声。
4.3.2 电机配置
相电感,相电阻,磁链等参数,具体的参数含义和测量看项目帮助文档或本文后续章节
4.3.3 驱动板硬件配置
主要配置6路PWM的引脚和有效电平死区时间等等,不同的MOS、预驱、无预驱等硬件环境会有不同的配置。
4.3.4 母线电流配置

4.3.5 算法配置
创建工程时选择fan的话,是非线性磁链观测器,hairdryer是PLL观测器。以下展示的是fan。

可视化上算法配置对应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工程一般调试方法
6 电机参数测量方法
四、电机驱动原理科普

- BLDC方波驱动
1.1无感换相基本原理
UVW或ABC三相,每次只给三相中的两相通电如UV、UW、VW、VU、WU、WV轮流导通,无hall传感器的是检测悬空相反电动势过零点延迟30°为换相时刻。有感的则是每次HALL状态发生跳变时换相。
上图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端电位会低于中点,但这一次过零非换相过零,这就是消磁现象,所以软件需要对这段时间做消隐处理。
- FOC驱动 磁场定向控制,使定子合成磁场趋近于圆形,拉动转子转动。
2.1 算法框图

2.2 电流采样及重构


2.2.1 三电阻法
不同的扇区存在某相采样窗口可能太窄,此时要舍弃掉此相的电流,用另外两相重新计算出这一相电流。
2.2.2 双电阻法
固定采集某两相的电流,然后计算第三相。这就需要保证最小的采样窗口要大于采样时间。
2.2.3 单电阻法
在每个pwm周期采样两次,才能重构出三相电流
如上图,此扇区采集到的就是 ia, 和-ic。单电阻法也是存在采样窗口不够的可能,要用到非对称式的PWM对策。
2.3 Clarke变换
将三相电流从自然坐标系ABC变换到静止坐标系α - β的坐标变换称为clarke变换。
再根据基尔霍夫定律ia+ib+ic = 0,可得最终转换公式
2.4 Park变换
将静止坐标系α - β变换到同步旋转坐标系 d - q的坐标变换称为Park 变换,公式如下
2.5 坐标变换波形参考

2.6 电流内环
将电流分量id,iq与给定值进行比较,送入pi控制器,目的是控制转矩,电流环的pi控制参数可以根据相关公式计算出来,但是实际开发中依然需要手动调试
2.7 速度外环
速度环作为电流环的输入,通过与当前速度进行比较,将误差输入给速度PI控制器,输出iqref和idref给到电流环。速度环的调整频率一定要低于电流环频率,否则速度环的调整会无效,造成系统振荡。
2.8 SVPWM
又叫空间矢量脉宽调制,通过改变两个矢量作用时长,可以合成任意位置的矢量,使合成矢量趋近与圆形。(100)代表U相上管导通,VW下管导通,(110)代表U相和V相上管导通,W下管导通。

扇区1的T4、T6作用时长如下
令
令1个上管导通的是矢量A,两个上管导通的是矢量B,则有以下关系
| 扇区 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 矢量TA时长 | U2 | -U2 | U1 | -U1 | U3 | -U3 |
| 矢量TB时长 | U1 | -U3 | U3 | -U2 | U2 | -U1 |
则有
tc = (T/2 - TB/2 - TA/2) / 2
tb = tc + TB/2
ta = tb + TA/2
2.9 无感位置观测器
2.9.1 龙伯格或滑膜

龙伯格和滑膜观测器是基于上面的电机数学模型,通过电压电流观测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是四舍五入