最近在学习AM32电调的2.18版本的源码,我用的硬件是AT32F421,整理了部分流程处理,内容的颗粒度是按自己的需要整理的,发出来给有需要的人参考。按自己的理解整理的,技术能力有限,可能理解有误,欢迎纠正。
注:lida2003博主是个大牛。写的无刷电调的理论和AM32相关知识点介绍的比较系统,介绍的很详细,有需要的同学可以去参考。我对电调这块是外行,只是刚入门学习。这里重点是代码理解的整理分析,他哪里写了很多原理性的知识。
https://blog.csdn.net/lida2003/category_12753961.html?spm=1001.2014.3001.5482
这是第五篇,介绍tenKhzRoutine函数,原来这个函数是10Khz的,2.18版本实际上是10Khz,只是名称未修改。这个函数主要处理:油门置低解锁、串口上报标志、启动阶段反电动势判断换相、PID调速、加速度处理、adjusted_duty_cycle生效、油门超时计数等功能。
流程图
代码
void tenKhzRoutine() //20khz 函数
{ // 20khz as of 2.00 to be renamed
duty_cycle = duty_cycle_setpoint;
tenkhzcounter++; //计数器的递增
ledcounter++;
one_khz_loop_counter++;
if (!armed)//输入信号未解锁
{
if (cell_count == 0)//还未计算出电池数量
{
if (inputSet)
{
if (adjusted_input == 0)
{
armed_timeout_count++;
if (armed_timeout_count > LOOP_FREQUENCY_HZ) //油门置低超过1秒
{ // one second
if (zero_input_count > 30)//油门置低连续30次
{
armed = 1; //置解锁标志位
#ifdef USE_LED_STRIP
// send_LED_RGB(0,0,0);
delayMicros(1000);
send_LED_RGB(0, 255, 0);
#endif
#ifdef USE_RGB_LED
GPIOB->BRR = LL_GPIO_PIN_3; // turn on green
GPIOB->BSRR = LL_GPIO_PIN_8; // turn on green
GPIOB->BSRR = LL_GPIO_PIN_5;
#endif
if ((cell_count == 0) && LOW_VOLTAGE_CUTOFF) //只有开启了低电压保护并且没计算过cell_count,才需要计算
{
cell_count = battery_voltage / 370; //battery_voltage单位是0.01V,一个电池按3.7V算,计算出电池数量cell_count
for (int i = 0; i < cell_count; i++) //有几个电池响几下
{
playInputTune();
delayMillis(100);
RELOAD_WATCHDOG_COUNTER(); //时间比较长,这里需要喂狗,否则可能复位
}
}
else
{
#ifdef MCU_AT415
play_tone_flag = 4;
#else
playInputTune(); //没开低电压保护则发一个音
#endif
}
if (!servoPwm)//如果不是servoPwm信号则关闭双向车模式
{
eepromBuffer.rc_car_reverse = 0;
}
}
else
{
inputSet = 0;//解锁失败,清在位状态,重新检测
armed_timeout_count = 0;
}
}
}
else
{
armed_timeout_count = 0;
}
}
}
}
if (eepromBuffer.telemetry_on_interval)//开启了串口上报功能
{
telem_ms_count++;
if (telem_ms_count > ((telemetry_interval_ms - 1 + eepromBuffer.telemetry_on_interval) * 20))//这里根据telemetry_on_interval参数,实现多个ESC错开上报
{
// telemetry_on_interval = 1 is a boolean, but it can also be 2 or more to indicate an identifier
// by making the interval just slightly different with an unique identifier, we can guarantee that many ESCs can communicate on just one signal
// there will be some collisions but not as many as if two ESCs always tried to talk at once.
send_telemetry = 1;
telem_ms_count = 0;
}
}
#ifndef BRUSHED_MODE
if (!stepper_sine)
{
#ifndef CUSTOM_RAMP
if (old_routine && running)//在tenKhzRoutine函数中50us定时调用,运转时同步驱动阶段定时读取比较器的反电动势状态
{
// send_LED_RGB(255, 0, 0);
maskPhaseInterrupts();//启动阶段不使用比较器中断判断过零点,所以需要禁用比较器中断
getBemfState(); //读取比较器输出计算bemfcounter
if (!zcfound)
{
if (rising)
{
if (bemfcounter > min_bemf_counts_up) //bemfcounter满足门限,认为是过零点
{
GPIOB->scr=GPIO_PINS_2;
GPIOB->clr=GPIO_PINS_2;
zcfound = 1; //找到过零点了,可以换相了
zcfoundroutine(); //换相操作
}
}
else
{
if (bemfcounter > min_bemf_counts_down)//bemfcounter满足门限,认为是过零点
{
GPIOB->scr=GPIO_PINS_2;
GPIOB->clr=GPIO_PINS_2;
zcfound = 1;//找到过零点了,可以换相了
zcfoundroutine(); //换相操作
}
}
}
}
#endif
if (one_khz_loop_counter > PID_LOOP_DIVIDER)//1毫秒PID调速
{ // 1khz PID loop
one_khz_loop_counter = 0;
if (use_current_limit && running) //限流PID调速
{
use_current_limit_adjust -= (int16_t) (doPidCalculations(¤tPid, actual_current,
eepromBuffer.limits.current * 2 * 100) / 10000);
if (use_current_limit_adjust < minimum_duty_cycle)
{
use_current_limit_adjust = minimum_duty_cycle;
}
if (use_current_limit_adjust > 2000)
{
use_current_limit_adjust = 2000;
}
}
if (eepromBuffer.stall_protection && running)//失速PID调速
{ // this boosts throttle as the rpm gets lower, for crawlers
// and rc cars only, do not use for multirotors.
stall_protection_adjust += (doPidCalculations(&stallPid, commutation_interval,
stall_protect_target_interval));
if (stall_protection_adjust > 150 * 10000)
{
stall_protection_adjust = 150 * 10000;
}
if (stall_protection_adjust <= 0)
{
stall_protection_adjust = 0;
}
}
if (use_speed_control_loop && running) //恒速PID调速
{
input_override += doPidCalculations(&speedPid, e_com_time, target_e_com_time);
if (input_override > 2047 * 10000)
{
input_override = 2047 * 10000;
}
if (input_override < 0)
{
input_override = 0;
}
if (zero_crosses < 100)
{
speedPid.integral = 0;
}
}
}
if (maximum_throttle_change_ramp)//加速度处理
{
// max_duty_cycle_change = map(k_erpm, low_rpm_level,
// high_rpm_level, 1, 40);
#ifdef VOLTAGE_BASED_RAMP //基于电压的加速度,电压越高加速度需要越低。特别对于大功率电调
uint16_t voltage_based_max_change = map(battery_voltage, 800, 2200, 10, 1);
if (average_interval > 200) //转速越高加速度越大
{
max_duty_cycle_change = voltage_based_max_change;
}
else
{
max_duty_cycle_change = voltage_based_max_change * 3;
}
#else
if (zero_crosses < 150 || last_duty_cycle < 150)//启动阶段加速度
{
max_duty_cycle_change = RAMP_SPEED_STARTUP;
}
else //转速越高加速度越大
{
if (average_interval > 500)
{
max_duty_cycle_change = RAMP_SPEED_LOW_RPM; //低转速加速度
}
else
{
max_duty_cycle_change = RAMP_SPEED_HIGH_RPM; //高转速加速度
}
}
#endif
#ifdef CUSTOM_RAMP
// max_duty_cycle_change = eepromBuffer[30];
#endif
//根据加速度调整duty_cycle
if ((duty_cycle - last_duty_cycle) > max_duty_cycle_change)
{
duty_cycle = last_duty_cycle + max_duty_cycle_change; //duty_cycle变化太大了需要限制
if (commutation_interval > 500)
{
fast_accel = 1; //早期版本的变量,最新版本已经不用了,原来的用法是在低转速的时候减小检测到过零点到换相的等待时间
temp_advance = eepromBuffer.advance_level; //目前temp_advance在代码中没有变化,这里不需要再几个分支都赋值
}
else
{
fast_accel = 0;
}
}
else if ((last_duty_cycle - duty_cycle) > max_duty_cycle_change)
{
duty_cycle = last_duty_cycle - max_duty_cycle_change; //duty_cycle变化太大了需要限制
fast_accel = 0;
temp_advance = eepromBuffer.advance_level;
}
else
{
if (duty_cycle < 300 && commutation_interval < 300)
{
temp_advance = eepromBuffer.advance_level;
}
else
{
temp_advance = eepromBuffer.advance_level;
}
fast_accel = 0;
//duty_cycle变化在范围内不需要限制
}
}
//根据duty_cycle计算adjusted_duty_cycle
if ((armed && running) && input > 47)
{
if (eepromBuffer.variable_pwm) //变频场景adjusted_duty_cycle计算
{
//tim1_arr的变频映射处理,在Main函数中了,这里不需要执行了
}
adjusted_duty_cycle = ((duty_cycle * tim1_arr) / 2000) + 1;
}
else
{
if (prop_brake_active)
{
adjusted_duty_cycle = TIMER1_MAX_ARR - ((prop_brake_duty_cycle * tim1_arr) / 2000) + 1; //制动激活adjusted_duty_cycle计算
}
else
{
adjusted_duty_cycle = ((duty_cycle * tim1_arr) / 2000); //制动未激活adjusted_duty_cycle计算
}
}
last_duty_cycle = duty_cycle;
//计算的tim1_arr、adjusted_duty_cycle值生效
SET_AUTO_RELOAD_PWM(tim1_arr);
SET_DUTY_CYCLE_ALL(adjusted_duty_cycle);
}
#endif // ndef brushed_mode
#if defined(FIXED_DUTY_MODE) || defined(FIXED_SPEED_MODE)
//恒定Duty模式和恒定转速模式,在检测到配置线插入一段时间后复位MCU
if (getInputPinState())
{
signaltimeout++;
if (signaltimeout > LOOP_FREQUENCY_HZ)
{
NVIC_SystemReset();
}
}
else
{
signaltimeout = 0;
}
#else
signaltimeout++; //其他模式这里只做计数器累计,复位处理移到Main函数中
#endif
}