fome-fw/firmware/controllers/system/pwm_generator_logic.cpp

199 lines
6.1 KiB
C++
Raw Normal View History

2014-08-29 07:52:33 -07:00
/**
* @file pwm_generator_logic.cpp
*
* This PWM implementation keep track of when it would be the next time to toggle the signal.
* It constantly sets timer to that next toggle time, then sets the timer again from the callback, and so on.
*
* @date Mar 2, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
*/
#include "main.h"
#include "pwm_generator_logic.h"
/**
* We need to limit the number of iterations in order to avoid precision loss while calculating
* next toggle time
*/
#define ITERATION_LIMIT 10000
SimplePwm::SimplePwm() {
wave.init(pinStates);
sr[0] = wave;
init(_switchTimes, sr);
}
PwmConfig::PwmConfig() {
scheduling.name = "PwmConfig";
}
PwmConfig::PwmConfig(float *st, single_wave_s *waves) {
multiWave.init(st, waves);
scheduling.name = "PwmConfig";
}
void PwmConfig::init(float *st, single_wave_s *waves) {
multiWave.init(st, waves);
}
/**
* @param dutyCycle value between 0 and 1
*/
void SimplePwm::setSimplePwmDutyCycle(float dutyCycle) {
multiWave.setSwitchTime(0, dutyCycle);
}
static uint64_t getNextSwitchTimeUs(PwmConfig *state) {
efiAssert(state->safe.phaseIndex < PWM_PHASE_MAX_COUNT, "phaseIndex range", 0);
int iteration = state->safe.iteration;
float switchTime = state->multiWave.getSwitchTime(state->safe.phaseIndex);
2014-09-12 21:02:43 -07:00
float periodNt = state->safe.periodNt;
2014-08-29 07:52:33 -07:00
#if DEBUG_PWM
scheduleMsg(&logger, "iteration=%d switchTime=%f period=%f", iteration, switchTime, period);
#endif
/**
* Once 'iteration' gets relatively high, we might lose calculation precision here.
* This is addressed by ITERATION_LIMIT
*/
2014-09-12 21:02:43 -07:00
uint64_t timeToSwitchNt = (uint64_t) ((iteration + switchTime) * periodNt);
2014-08-29 07:52:33 -07:00
#if DEBUG_PWM
scheduleMsg(&logger, "start=%d timeToSwitch=%d", state->safe.start, timeToSwitch);
#endif
2014-09-12 21:02:43 -07:00
return NT2US(state->safe.startNt + timeToSwitchNt);
2014-08-29 07:52:33 -07:00
}
void PwmConfig::handleCycleStart() {
if (safe.phaseIndex == 0) {
2014-09-11 22:02:47 -07:00
if (cycleCallback != NULL) {
2014-08-29 07:52:33 -07:00
cycleCallback(this);
2014-09-11 22:02:47 -07:00
}
2014-09-12 21:02:43 -07:00
efiAssertVoid(periodNt != 0, "period not initialized");
if (safe.periodNt != periodNt || safe.iteration == ITERATION_LIMIT) {
2014-08-29 07:52:33 -07:00
/**
* period length has changed - we need to reset internal state
*/
2014-09-12 20:04:25 -07:00
safe.startNt = getTimeNowNt();
2014-08-29 07:52:33 -07:00
safe.iteration = 0;
2014-09-12 21:02:43 -07:00
safe.periodNt = periodNt;
2014-08-29 07:52:33 -07:00
#if DEBUG_PWM
scheduleMsg(&logger, "state reset start=%d iteration=%d", state->safe.start, state->safe.iteration);
#endif
}
}
}
/**
* @return Next time for signal toggle
*/
static uint64_t togglePwmState(PwmConfig *state) {
#if DEBUG_PWM
scheduleMsg(&logger, "togglePwmState phaseIndex=%d iteration=%d", state->safe.phaseIndex, state->safe.iteration);
scheduleMsg(&logger, "state->period=%f state->safe.period=%f", state->period, state->safe.period);
#endif
2014-09-12 21:02:43 -07:00
if (cisnan(state->periodNt)) {
2014-08-29 07:52:33 -07:00
/**
* zero period means PWM is paused
*/
return 1;
}
state->handleCycleStart();
/**
* Here is where the 'business logic' - the actual pin state change is happening
*/
// callback state index is offset by one. todo: why? can we simplify this?
int cbStateIndex = state->safe.phaseIndex == 0 ? state->phaseCount - 1 : state->safe.phaseIndex - 1;
state->stateChangeCallback(state, cbStateIndex);
uint64_t nextSwitchTimeUs = getNextSwitchTimeUs(state);
#if DEBUG_PWM
scheduleMsg(&logger, "%s: nextSwitchTime %d", state->name, nextSwitchTime);
#endif
// signed value is needed here
2014-09-13 11:02:53 -07:00
// int64_t timeToSwitch = nextSwitchTimeUs - getTimeNowUs();
// if (timeToSwitch < 1) {
// /**
// * We are here if we are late for a state transition.
// * At 12000RPM=200Hz with a 60 toothed wheel we need to change state every
// * 1000000 / 200 / 120 = ~41 uS. We are kind of OK.
// *
// * We are also here after a flash write. Flash write freezes the whole chip for a couple of seconds,
// * so PWM generation and trigger simulation generation would have to recover from this time lag.
// */
// //todo: introduce error and test this error handling warning(OBD_PCM_Processor_Fault, "PWM: negative switch time");
// timeToSwitch = 10;
// }
2014-08-29 07:52:33 -07:00
state->safe.phaseIndex++;
if (state->safe.phaseIndex == state->phaseCount) {
state->safe.phaseIndex = 0; // restart
state->safe.iteration++;
}
2014-09-13 11:02:53 -07:00
return nextSwitchTimeUs;
2014-08-29 07:52:33 -07:00
}
/**
* Main PWM loop: toggle pin & schedule next invocation
*/
static void timerCallback(PwmConfig *state) {
2014-09-13 11:02:53 -07:00
uint64_t switchTimeUs = togglePwmState(state);
scheduleTask2("pwm", &state->scheduling, switchTimeUs, (schfunc_t) timerCallback, state);
2014-08-29 07:52:33 -07:00
}
/**
* Incoming parameters are potentially just values on current stack, so we have to copy
* into our own permanent storage, right?
*/
void copyPwmParameters(PwmConfig *state, int phaseCount, float *switchTimes, int waveCount, int **pinStates) {
state->phaseCount = phaseCount;
for (int phaseIndex = 0; phaseIndex < phaseCount; phaseIndex++) {
state->multiWave.setSwitchTime(phaseIndex, switchTimes[phaseIndex]);
for (int waveIndex = 0; waveIndex < waveCount; waveIndex++) {
// print("output switch time index (%d/%d) at %f to %d\r\n", phaseIndex,waveIndex,
// switchTimes[phaseIndex], pinStates[waveIndex][phaseIndex]);
state->multiWave.waves[waveIndex].pinStates[phaseIndex] = pinStates[waveIndex][phaseIndex];
}
}
}
void weComplexInit(const char *msg, PwmConfig *state, int phaseCount, float *switchTimes, int waveCount,
int **pinStates, pwm_cycle_callback *cycleCallback, pwm_gen_callback *stateChangeCallback) {
2014-09-12 21:02:43 -07:00
efiAssertVoid(state->periodNt != 0, "period is not initialized");
2014-08-29 07:52:33 -07:00
if (phaseCount == 0) {
firmwareError("signal length cannot be zero");
return;
}
if (phaseCount > PWM_PHASE_MAX_COUNT) {
firmwareError("too many phases in PWM");
return;
}
if (switchTimes[phaseCount - 1] != 1) {
firmwareError("last switch time has to be 1");
return;
}
efiAssertVoid(waveCount > 0, "waveCount should be positive");
checkSwitchTimes2(phaseCount, switchTimes);
state->multiWave.waveCount = waveCount;
copyPwmParameters(state, phaseCount, switchTimes, waveCount, pinStates);
state->cycleCallback = cycleCallback;
state->stateChangeCallback = stateChangeCallback;
state->safe.phaseIndex = 0;
2014-09-12 21:02:43 -07:00
state->safe.periodNt = -1;
2014-08-29 07:52:33 -07:00
state->safe.iteration = -1;
state->name = msg;
// let's start the indefinite callback loop of PWM generation
timerCallback(state);
}