AM32 电调学习–tenKhzRoutine()
#define INTERVAL_TIMER TIM2 ; 过零时间测量
#define TEN_KHZ_TIMER TIM6 ;tenKhzRoutine frq=1M load=1M/20k
#define UTILITY_TIMER TIM17
#define COM_TIMER TIM14 //PeriodElapsedCallback 过零后等待时间中断
(0)换相过程简述
- 开始启动时:old_routine设置,tenKhzRoutine()任务中检测反相电动势,获取到零点后,执行zcfoundroutine().标志zcfound守护到换相结束。
- zcfoundroutine
(1)六步换相定义
- step1 2 3 4 5 6
-
ba bc ac ab cb ca
一、使用的变量
- armed 电机识别标志
- TLM_ON_INTERVAL dshot需要发送telemetry
- uint8_t telemetry_interval_ms = 30; ms
- uint8_t running = 0;
void startMotor()
{
if (running == 0) {
commutate();
commutation_interval = 10000;
SET_INTERVAL_TIMER_COUNT(5000);
running = 1; //
}
enableCompInterrupts();
}
setInput():
if (input >= 47 + (80 * use_sin_start) && armed) {
if (running == 0) {
allOff();
if (!old_routine) {
startMotor();
}
running = 1;
last_duty_cycle = min_startup_duty;
}
main()
if (desync_check && zero_crosses > 10) {
if ((getAbsDif(last_average_interval, average_interval) > average_interval >> 1) && (average_interval < 2000)) { // throttle resitricted before zc 20.
zero_crosses = 0;
desync_happened++;
if ((!bi_direction && (input > 47)) || commutation_interval > 1000) {
running = 0;
}
old_routine = 1;
if (zero_crosses > 100) {
average_interval = 5000;
}
last_duty_cycle = min_startup_duty / 2;
}
desync_check = 0;
main():
if (LOW_VOLTAGE_CUTOFF) {
if (battery_voltage < (cell_count * low_cell_volt_cutoff)) {
low_voltage_count++;
if (low_voltage_count > (20000 - (stepper_sine * 900))) {
input = 0;
allOff();
maskPhaseInterrupts();
running = 0; ///
zero_input_count = 0;
armed = 0;
}
} else {
low_voltage_count = 0;
}
}
main():
if (INTERVAL_TIMER_COUNT > 45000 && running == 1) {
bemf_timeout_happened++;
maskPhaseInterrupts();
old_routine = 1;
if (input < 48) {
running = 0; //
commutation_interval = 5000;
}
zero_crosses = 0;
zcfoundroutine();
}
main():
if (input > 48 && input < 137) { // sine wave stepper
if (do_once_sinemode) {
// disable commutation interrupt in case set
DISABLE_COM_TIMER_INT();
maskPhaseInterrupts();
SET_DUTY_CYCLE_ALL(0);
allpwm();
do_once_sinemode = 0;
}
advanceincrement();
step_delay = map(input, 48, 120, 7000 / motor_poles, 810 / motor_poles);
delayMicros(step_delay);
e_rpm = 600 / step_delay; // in hundreds so 33 e_rpm is 3300 actual erpm
} else {
do_once_sinemode = 1;
advanceincrement();
if (input > 200) {
phase_A_position = 0;
step_delay = 80;
}
delayMicros(step_delay);
if (phase_A_position == 0) {
stepper_sine = 0;
running = 1; //
old_routine = 1;
commutation_interval = 9000;
average_interval = 9000;
last_average_interval = average_interval;
SET_INTERVAL_TIMER_COUNT(9000);
zero_crosses = 20;
prop_brake_active = 0;
step = changeover_step;
// comStep(step);// rising bemf on a same as position 0.
if (stall_protection) {
last_duty_cycle = stall_protect_minimum_duty;
}
commutate();
generatePwmTimerEvent();
}
}
- char old_routine = 1;
comutate():
if (stall_protection || RC_CAR_REVERSE) {
if (average_interval > 2000) {
old_routine = 1;
}
}
setInput(): 转换方向时
if (!running) {
old_routine = 1;
main():
堵转
if ((getAbsDif(last_average_interval, average_interval) > average_interval >> 1) && (average_interval < 2000)) { // throttle resitricted before zc 20.
zero_crosses = 0;
desync_happened++;
if ((!bi_direction && (input > 47)) || commutation_interval > 1000) {
running = 0;
}
old_routine = 1;
停机:
if (INTERVAL_TIMER_COUNT > 45000 && running == 1) {
bemf_timeout_happened++;
maskPhaseInterrupts();
old_routine = 1;
???
if (phase_A_position == 0) {
stepper_sine = 0;
running = 1;
old_routine = 1;
zcfoundroutine():
if (stall_protection || RC_CAR_REVERSE) {
if (zero_crosses >= 20 && commutation_interval <= 2000) {
old_routine = 0;
enableCompInterrupts(); // enable interrupt
}
} else {
if (zero_crosses > 30) {
old_routine = 0;
enableCompInterrupts(); // enable interrupt
}
}
- uint8_t bemfcounter; 盲启动过零点计数 #define TARGET_MIN_BEMF_COUNTS 3
20khz里检测到三次过零点改变电平,即150us? - int16_t use_current_limit_adjust = 2000; 电流pid输出值,
setInput(): 用于限制最后的占位比
if (use_current_limit) {
if (duty_cycle_setpoint > use_current_limit_adjust) {
duty_cycle_setpoint = use_current_limit_adjust;
}
}
- float stall_protection_adjust = 0; 速度太低时,通过pid提高占空比
uint16_t stall_protect_target_interval = TARGET_STALL_PROTECTION_INTERVAL;
#define TARGET_STALL_PROTECTION_INTERVAL 6500 最低转速
6500= 3250us 则一电子圈时间 3250 * 6 = 20ms,
设极对数7,则一圈时间 20ms*7 =140ms, 则1s转7圈
rpm=428
setInput():
if (stall_protection_adjust > 0 && input > 47) {
duty_cycle_setpoint = duty_cycle_setpoint + (uint16_t)stall_protection_adjust;
}
- float input_override = 0; 速度控制pid的输出值
10.uint16_t target_e_com_time = 0; 转一电子圏 的目标时间
target_e_com_time = 60000000 / map(adjusted_input, 47, 2047, MINIMUM_RPM_SPEED_CONTROL, MAXIMUM_RPM_SPEED_CONTROL) / (motor_poles / 2);
uint32_t MINIMUM_RPM_SPEED_CONTROL = 1000; //最快是500us,pairs7,3.5ms一圈。
uint32_t MAXIMUM_RPM_SPEED_CONTROL = 10000;
- char maximum_throttle_change_ramp = 1;
- uint16_t signaltimeout = 0; 输入信号获取间隔,若大于0.5s,则认为信号丢失。
if (signaltimeout > (LOOP_FREQUENCY_HZ >> 1)) { // half second timeout when armed;
if (armed) {
allOff();
armed = 0;
input = 0;
inputSet = 0;
zero_input_count = 0;
SET_DUTY_CYCLE_ALL(0);
resetInputCaptureTimer();
for (int i = 0; i < 64; i++) {
dma_buffer[i] = 0;
}
NVIC_SystemReset();
}
if (signaltimeout > LOOP_FREQUENCY_HZ << 1) { // 2 second when not armed
allOff();
armed = 0;
input = 0;
inputSet = 0;
zero_input_count = 0;
SET_DUTY_CYCLE_ALL(0);
resetInputCaptureTimer();
for (int i = 0; i < 64; i++) {
dma_buffer[i] = 0;
}
NVIC_SystemReset();
}
二、盲启动结束判断
if (!armed) {
if (cell_count == 0) {
if (inputSet) {
if (adjusted_input == 0) {
armed_timeout_count++;
if (armed_timeout_count > LOOP_FREQUENCY_HZ) { // one second
if (zero_input_count > 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 = battery_voltage / 370;
for (int i = 0; i < cell_count; i++) {
playInputTune();
delayMillis(100);
RELOAD_WATCHDOG_COUNTER();
}
} else {
playInputTune();
}
if (!servoPwm) {
RC_CAR_REVERSE = 0;
}
} else {
inputSet = 0;
armed_timeout_count = 0;
}
}
} else {
armed_timeout_count = 0;
}
}
}
}
三、Telemetry时机判断
四、盲启动过零点判断
if (old_routine && running) {
maskPhaseInterrupts();
getBemfState();
if (!zcfound) {
if (rising) {
if (bemfcounter > min_bemf_counts_up) {
zcfound = 1;
zcfoundroutine();
}
} else {
if (bemfcounter > min_bemf_counts_down) {
zcfound = 1;
zcfoundroutine();
}
}
}
}
五、1khz PID处理
if (one_khz_loop_counter > PID_LOOP_DIVIDER) { // 1khz PID loop
one_khz_loop_counter = 0;
if (use_current_limit && running) {
use_current_limit_adjust -= (int16_t)(doPidCalculations(¤tPid, actual_current,
CURRENT_LIMIT * 100)
/ 10000);
if (use_current_limit_adjust < minimum_duty_cycle) {
use_current_limit_adjust = minimum_duty_cycle;
}
if (use_current_limit_adjust > tim1_arr) {
use_current_limit_adjust = tim1_arr;
}
}
if (stall_protection && running) { // 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))
/ 10000;
if (stall_protection_adjust > 150) {
stall_protection_adjust = 150;
}
if (stall_protection_adjust <= 0) {
stall_protection_adjust = 0;
}
}
if (use_speed_control_loop && running) {
input_override += doPidCalculations(&speedPid, e_com_time, target_e_com_time) / 10000;
if (input_override > 2047) {
input_override = 2047;
}
if (input_override < 0) {
input_override = 0;
}
if (zero_crosses < 100) {
speedPid.integral = 0;
}
}
}
六、最大PWM改变值计算
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 (last_duty_cycle < 150) {
max_duty_cycle_change = RAMP_SPEED_STARTUP;//2
} else {
if (average_interval > 500) {
max_duty_cycle_change = RAMP_SPEED_LOW_RPM;//10
} else {
max_duty_cycle_change = RAMP_SPEED_HIGH_RPM;//40
}
}
#endif
#ifdef CUSTOM_RAMP
max_duty_cycle_change = eepromBuffer[30];
#endif
if ((duty_cycle - last_duty_cycle) > max_duty_cycle_change) {
duty_cycle = last_duty_cycle + max_duty_cycle_change;
if (commutation_interval > 500) {
fast_accel = 1;
} else {
fast_accel = 0;
}
} else if ((last_duty_cycle - duty_cycle) > max_duty_cycle_change) {
duty_cycle = last_duty_cycle - max_duty_cycle_change;
fast_accel = 0;
} else {
fast_accel = 0;
}
}
七、最终占空比调整
if ((armed && running) && input > 47) {
if (VARIABLE_PWM) {
}
adjusted_duty_cycle = ((duty_cycle * tim1_arr) / TIMER1_MAX_ARR) + 1;
} else {
if (prop_brake_active) {
adjusted_duty_cycle = TIMER1_MAX_ARR - ((prop_brake_duty_cycle * tim1_arr) / TIMER1_MAX_ARR) + 1;
} else {
adjusted_duty_cycle = ((duty_cycle * tim1_arr) / TIMER1_MAX_ARR);
}
}
八、PWM输出
last_duty_cycle = duty_cycle;
SET_AUTO_RELOAD_PWM(tim1_arr);
SET_DUTY_CYCLE_ALL(adjusted_duty_cycle);
(a) 电子一周时间(6次换相时间,05us为间隔)
- commutation_intervals[x]以0.5us为单位
- e_com_time 以1us为单位
- 控制输入值,以us为单位?
e_com_time = ((commutation_intervals[0] + commutation_intervals[1] + commutation_intervals[2] + commutation_intervals[3] + commutation_intervals[4] + commutation_intervals[5]) + 4) >> 1; // COMMUTATION INTERVAL IS 0.5US INCREMENTS
average_interval = e_com_time / 3;
duty_cycle = duty_cycle_setpoint;
if (use_sin_start) {
duty_cycle_setpoint = map(input, 137, 2047, minimum_duty_cycle, TIMER1_MAX_ARR);
} else {
duty_cycle_setpoint = map(input, 47, 2047, minimum_duty_cycle, TIMER1_MAX_ARR);
}
uint16_t minimum_duty_cycle = DEAD_TIME;
#define DEAD_TIME 60
uint16_t TIMER1_MAX_ARR = TIM1_AUTORELOAD; // maximum auto reset register value
#define TIM1_AUTORELOAD 2667
(b) 1khz任务 pid
电流环、油门环、速度环
- uint16_t CURRENT_LIMIT = 202; //2.02A
- doPidCalculations(¤tPid, actual_current,CURRENT_LIMIT * 100). //*100, 说明电流值扩大了10000倍
fastPID currentPid = { // 1khz loop time
.Kp = 400,
.Ki = 0,
.Kd = 1000,
.integral_limit = 20000,
.output_limit = 100000
};
- int16_t use_current_limit_adjust = 2000;
- uint16_t minimum_duty_cycle = DEAD_TIME;
- uint16_t tim1_arr = TIM1_AUTORELOAD; // current auto reset value
- #define TIM1_AUTORELOAD 2667
- stall_protection_adjust 转速太低时,需要增加油门的值
- target_e_com_time = 60000000 / map(adjusted_input, 47, 2047, MINIMUM_RPM_SPEED_CONTROL, MAXIMUM_RPM_SPEED_CONTROL) / (motor_poles / 2);
- uint32_t MAXIMUM_RPM_SPEED_CONTROL = 10000;
uint32_t MINIMUM_RPM_SPEED_CONTROL = 1000;
将输入0-2000映射到rpm1000-10000,算出每一转需要多少us
-float input_override += doPidCalculations(&speedPid, e_com_time, target_e_com_time) / 10000;
// assign speed control PID values values are x10000
fastPID speedPid = { // commutation speed loop time
.Kp = 10,
.Ki = 0,
.Kd = 100,
.integral_limit = 10000,
.output_limit = 50000
};
if (one_khz_loop_counter > PID_LOOP_DIVIDER) { // 1khz PID loop
one_khz_loop_counter = 0;
if (use_current_limit && running) {
use_current_limit_adjust -= (int16_t)(doPidCalculations(¤tPid, actual_current,
CURRENT_LIMIT * 100)
/ 10000);
if (use_current_limit_adjust < minimum_duty_cycle) {
use_current_limit_adjust = minimum_duty_cycle;
}
if (use_current_limit_adjust > tim1_arr) {
use_current_limit_adjust = tim1_arr;
}
}
if (stall_protection && running) { // 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))
/ 10000;
if (stall_protection_adjust > 150) {
stall_protection_adjust = 150;
}
if (stall_protection_adjust <= 0) {
stall_protection_adjust = 0;
}
}
if (use_speed_control_loop && running) {
input_override += doPidCalculations(&speedPid, e_com_time, target_e_com_time) / 10000;
if (input_override > 2047) {
input_override = 2047;
}
if (input_override < 0) {
input_override = 0;
}
if (zero_crosses < 100) {
speedPid.integral = 0;
}
}
}
一些定时器的知识
TIMx_PSC 分频寄存器 TIMx_ARR 自动重装载寄存器/TIMx_CCR捕捉寄存器 TIMx_RCR 重复计数寄存器[高级定时器有】)
其中,ARR、CCR寄存器带预装载使能控制位:
TIMx_ARR 带预装载使能控制位 ARPE@TIMx_CR1
TIMx_CCR 带预装载使能控制位 OCxPE@TIMx_CCMR1
当相应预装使能位置1时,即开启预装功能时,此时影子寄存器的数据更新必须且只能通过更新事件实现从预装寄存器到影子寄存器的数据拷贝。
当相应预装使能位置0时,即关闭预装功能时,用户修改预装寄存器的数据后会立即被拷贝进影子寄存器【实际寄存器】。也就是说,此时我们用户操作预装寄存器就相当于访问实际影子寄存器。
另外的PSC、RCR寄存器是不带预装使能控制位的,也就是说,二者的影子寄存器的预装功能始终开启,对于PSC/RCR影子寄存器的数据更新就只能通过更新事件唯一途径实现从预装寄存器到影子寄存器的数据拷贝更新。
那么有哪些操作可以产生更新事件呢?或者有哪些更新事件源呢?
这里把更新操作跟更新事件区别开来,以便于概念上的理解。
更新操作是一种动作,是更新事件的源头,即事件源;
更新事件是基于更新操作所导致的后续影响或结果。
可能的更新操作【事件源】有3类:
1、核心计数器的溢出【上溢或下溢】
2、软件复位操作【对UG@TIMX_EGR置位】:
3、工作在复位模式下的定时器收到触发信号【即复位触发信号】
【特别提醒,对于高级定时器必须发生RCR+1次溢出动作后才可以产生更新事件。对于通用或基本定时器,每溢出一次就可以产生更新事件。】
那么更新操作何时可以升级为更新事件呢?这里涉及到一个控制寄存器的控制位,UDIS@TIMx_CR1
当该控制位UDIS@TIMx_CR1为0时,更新操作升级为更新事件,更新事件会产生如下影响或效果:
1、实现从预装寄存器的数据到影子寄存器的内容拷贝,即完成影子寄存器的内容更新;
2、实现计数器【预分频计数器、核心计数器、重复计数器】的重新初始化;
3、置位状态寄存器的更新中断请求【UIF@TIMx_SR】位,并可以触发定时器更新中断或触发DMA请求;
当该控制位UDIS@TIMx_CR1为1时,更新操作不能升级为更新事件,其相应的结果或影响:
仅限于计数器的重新初始化,影子寄存器不做内容更新;
无更新标志的置位,不触发中断或DMA请求
那么发生更新操作时计数器的重新初始化具体是指什么呢?
1、分频计数器重装为0,然后重新开始计数;
2、重复计数器重装为RCR寄存器里的值,然后重新递减计数;
3、核心计数器的初始化由计数模式来定,如果是向上计数或中心对齐计数模式,CNT归0;如果是向下计数器模式,CNT重装为ARR,然后重新向下计数;
发生更新事件时,影子寄存器的更新与计数器的重装有先后顺序吗?
有!影子寄存器【ARR/CCR….】的更新操作在前,计数器的重装操作在后!
因为这样可以保障计数器的重装值使用更新过的数据。该个细节要特别注意!
最后,不妨做个基于更新事件的案例分享:
问题描述:TIMER初始化阶段,经常有人反馈,不管定时器周期的长短,只要一使能更新中断,就立即进中断服务程序?令人不解,往往给开发带来些困扰,原因可能是什么?如何解决?
我们知道,定时器应用的初始化时,往往需要对有关时基寄存器进行些基本的数据赋值。比方对ARR/PSC/RCR这些寄存器赋予初始值。结合前面的介绍,这些寄存器都是些带预装功能的寄存器,我们用户操作的寄存器都是预装寄存器,还不是实际起作用的影子寄存器。
对于ARR寄存器倒还好,因为芯片复位后默认状态下,ARR寄存器的预装功能是关闭的【CCR寄存器的预装载功能默认条件下也是关闭的】,那么我们用户给ARR赋值就相当于给其实际影子寄存器赋值了。
但PSC/RCR寄存器是不带预装控制位的,前面也说了,它们两个的影子寄存器的更新必须借助于更新事件。所以,在定时器的时基参数的初始化代码里,为了让用户写进预寄存器的数据生效,就用到了上面提到过的软件复位操作,即对UG@TIMx_EGR进行置位而产生更新事件,从而完成影子寄存器的数据更新。
在STM32标准库里的TIM_TimeBaseInit( )函数里都有如下代码
TIMx->EGR =TIM_PSCReloadMode_Immediate;
7 P/ S# S; E0 ?) \$ `& V, ?
& D7 ~' `3 A( T. S7 ~+ S3 g
在Cube库里的HAL_TIM_Base_Init( )函数里的函数有如下代码:
TIMx->EGR = TIM_EGR_UG;
结合前面的介绍,这两行代码使用的软件更新操作产发了更新事件,但它不仅仅实现了影子寄存器的数据更新,同时呢,还置位了状态寄存器的更新中断请求标志位UIF@TIMx_SR。那么,如果在这之后,我们使能定时器更新中断的话,进入更新中断服务程序就再自然不过了。为了规避这个问题,我们在时基参数初始化完成之后、使能定时器更新中断之前,可以先做更新中断标志的清除操作。
基于这个案例,我们可以对定时器的影子特性以及不同影子寄存器的预装特性差异有进一步的了解。总的来讲,STM32定时器的寄存器预装载特性也是其一特色,定时器的更新事件也是个非常重要的事件,更新事件本身及相关中断或DMA功能也是STM32开发应用中常用的工具。