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

280 lines
8.6 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -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
2018-01-20 17:55:31 -08:00
* @author Andrey Belomutskiy, (c) 2012-2018
2015-07-10 06:01:56 -07:00
*/
2018-09-16 19:26:57 -07:00
#include "global.h"
2015-07-10 06:01:56 -07:00
#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 1000
SimplePwm::SimplePwm() {
waveInstance.init(pinStates);
sr[0] = waveInstance;
init(_switchTimes, sr);
}
2015-09-13 07:01:39 -07:00
2015-07-10 06:01:56 -07:00
void PwmConfig::baseConstructor() {
memset(&scheduling, 0, sizeof(scheduling));
memset(&safe, 0, sizeof(safe));
dbgNestingLevel = 0;
periodNt = NAN;
memset(&outputPins, 0, sizeof(outputPins));
phaseCount = 0;
2018-02-05 14:16:16 -08:00
pwmCycleCallback = NULL;
2015-07-10 06:01:56 -07:00
stateChangeCallback = NULL;
}
PwmConfig::PwmConfig() {
baseConstructor();
}
PwmConfig::PwmConfig(float *st, single_wave_s *waves) {
baseConstructor();
multiWave.init(st, waves);
}
void PwmConfig::init(float *st, single_wave_s *waves) {
multiWave.init(st, waves);
}
/**
2018-01-21 12:28:03 -08:00
* This method allows you to change duty cycle on the fly
2015-07-10 06:01:56 -07:00
* @param dutyCycle value between 0 and 1
*/
void SimplePwm::setSimplePwmDutyCycle(float dutyCycle) {
2018-11-26 17:40:24 -08:00
if (cisnan(dutyCycle)) {
warning(CUSTOM_ERR_6691, "spwd:dutyCycle %.2f", dutyCycle);
return;
}
2018-01-21 13:06:03 -08:00
if (dutyCycle < 0 || dutyCycle > 1) {
2018-10-21 14:45:14 -07:00
warning(CUSTOM_ERR_6579, "spwd:dutyCycle %.2f", dutyCycle);
return;
2018-01-21 13:06:03 -08:00
}
2018-10-21 14:45:14 -07:00
// todo: need to fix PWM so that it supports zero duty cycle
2015-07-10 06:01:56 -07:00
multiWave.setSwitchTime(0, dutyCycle);
}
static efitimeus_t getNextSwitchTimeUs(PwmConfig *state) {
2018-07-25 20:30:00 -07:00
efiAssert(CUSTOM_ERR_ASSERT, state->safe.phaseIndex < PWM_PHASE_MAX_COUNT, "phaseIndex range", 0);
2015-07-10 06:01:56 -07:00
int iteration = state->safe.iteration;
float switchTime = state->multiWave.getSwitchTime(state->safe.phaseIndex);
float periodNt = state->safe.periodNt;
#if DEBUG_PWM
2018-01-23 09:05:14 -08:00
scheduleMsg(&logger, "iteration=%d switchTime=%.2f period=%.2f", iteration, switchTime, period);
2015-07-10 06:01:56 -07:00
#endif
/**
* Once 'iteration' gets relatively high, we might lose calculation precision here.
* This is addressed by ITERATION_LIMIT
*/
efitime_t timeToSwitchNt = (efitime_t) ((iteration + switchTime) * periodNt);
#if DEBUG_PWM
scheduleMsg(&logger, "start=%d timeToSwitch=%d", state->safe.start, timeToSwitch);
#endif
return NT2US(state->safe.startNt + timeToSwitchNt);
}
void PwmConfig::setFrequency(float frequency) {
2018-01-21 12:28:03 -08:00
if (cisnan(frequency)) {
// explicit code just to be sure
periodNt = NAN;
return;
}
/**
* see #handleCycleStart()
*/
periodNt = US2NT(frequency2periodUs(frequency));
}
2015-07-10 06:01:56 -07:00
void PwmConfig::handleCycleStart() {
if (safe.phaseIndex == 0) {
2018-02-05 14:16:16 -08:00
if (pwmCycleCallback != NULL) {
pwmCycleCallback(this);
2015-07-10 06:01:56 -07:00
}
2018-07-25 20:03:04 -07:00
efiAssertVoid(CUSTOM_ERR_6580, periodNt != 0, "period not initialized");
2015-07-10 06:01:56 -07:00
if (safe.periodNt != periodNt || safe.iteration == ITERATION_LIMIT) {
/**
* period length has changed - we need to reset internal state
*/
safe.startNt = getTimeNowNt();
safe.iteration = 0;
safe.periodNt = periodNt;
#if DEBUG_PWM
scheduleMsg(&logger, "state reset start=%d iteration=%d", state->safe.start, state->safe.iteration);
#endif
}
}
}
/**
* @return Next time for signal toggle
*/
2018-01-21 12:31:46 -08:00
efitimeus_t PwmConfig::togglePwmState() {
2015-07-10 06:01:56 -07:00
#if DEBUG_PWM
2018-01-21 12:31:46 -08:00
scheduleMsg(&logger, "togglePwmState phaseIndex=%d iteration=%d", safe.phaseIndex, safe.iteration);
2018-01-23 09:05:14 -08:00
scheduleMsg(&logger, "period=%.2f safe.period=%.2f", period, safe.period);
2015-07-10 06:01:56 -07:00
#endif
2018-01-21 12:31:46 -08:00
if (cisnan(periodNt)) {
2015-07-10 06:01:56 -07:00
/**
* NaN period means PWM is paused
*/
return getTimeNowUs() + MS2US(100);
}
2018-01-21 12:31:46 -08:00
handleCycleStart();
2015-07-10 06:01:56 -07:00
/**
* 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?
2018-01-21 12:31:46 -08:00
int cbStateIndex = safe.phaseIndex == 0 ? phaseCount - 1 : safe.phaseIndex - 1;
stateChangeCallback(this, cbStateIndex);
2015-07-10 06:01:56 -07:00
2018-01-21 12:31:46 -08:00
efitimeus_t nextSwitchTimeUs = getNextSwitchTimeUs(this);
2015-07-10 06:01:56 -07:00
#if DEBUG_PWM
scheduleMsg(&logger, "%s: nextSwitchTime %d", state->name, nextSwitchTime);
2017-05-30 18:56:38 -07:00
#endif /* DEBUG_PWM */
2015-07-10 06:01:56 -07:00
// signed value is needed here
// 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;
// }
2018-01-21 12:31:46 -08:00
safe.phaseIndex++;
if (safe.phaseIndex == phaseCount) {
safe.phaseIndex = 0; // restart
safe.iteration++;
2015-07-10 06:01:56 -07:00
}
return nextSwitchTimeUs;
}
/**
* Main PWM loop: toggle pin & schedule next invocation
2018-09-10 19:43:57 -07:00
*
* First invocation happens on application thread
2015-07-10 06:01:56 -07:00
*/
static void timerCallback(PwmConfig *state) {
state->dbgNestingLevel++;
2018-07-25 20:03:04 -07:00
efiAssertVoid(CUSTOM_ERR_6581, state->dbgNestingLevel < 25, "PWM nesting issue");
2015-07-10 06:01:56 -07:00
2018-01-21 12:31:46 -08:00
efitimeus_t switchTimeUs = state->togglePwmState();
2018-01-28 08:27:33 -08:00
scheduleByTimestamp(&state->scheduling, switchTimeUs, (schfunc_t) timerCallback, state);
2015-07-10 06:01:56 -07:00
state->dbgNestingLevel--;
}
/**
* 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, pin_state_t **pinStates) {
state->phaseCount = phaseCount;
for (int phaseIndex = 0; phaseIndex < phaseCount; phaseIndex++) {
state->multiWave.setSwitchTime(phaseIndex, switchTimes[phaseIndex]);
for (int waveIndex = 0; waveIndex < waveCount; waveIndex++) {
2018-01-23 09:05:14 -08:00
// print("output switch time index (%d/%d) at %.2f to %d\r\n", phaseIndex,waveIndex,
2015-07-10 06:01:56 -07:00
// switchTimes[phaseIndex], pinStates[waveIndex][phaseIndex]);
state->multiWave.waves[waveIndex].pinStates[phaseIndex] = pinStates[waveIndex][phaseIndex];
}
}
}
2018-12-08 12:07:07 -08:00
/**
* this method also starts the timer cycle
* See also startSimplePwm
*/
2015-07-10 06:01:56 -07:00
void PwmConfig::weComplexInit(const char *msg, int phaseCount, float *switchTimes, int waveCount,
2018-02-05 14:16:16 -08:00
pin_state_t **pinStates, pwm_cycle_callback *pwmCycleCallback, pwm_gen_callback *stateChangeCallback) {
2015-07-10 06:01:56 -07:00
2018-07-25 20:03:04 -07:00
efiAssertVoid(CUSTOM_ERR_6582, periodNt != 0, "period is not initialized");
2015-07-10 06:01:56 -07:00
if (phaseCount == 0) {
2016-10-10 13:02:39 -07:00
firmwareError(CUSTOM_ERR_PWM_1, "signal length cannot be zero");
2015-07-10 06:01:56 -07:00
return;
}
if (phaseCount > PWM_PHASE_MAX_COUNT) {
2016-10-10 13:02:39 -07:00
firmwareError(CUSTOM_ERR_PWM_2, "too many phases in PWM");
2015-07-10 06:01:56 -07:00
return;
}
2018-07-25 20:03:04 -07:00
efiAssertVoid(CUSTOM_ERR_6583, waveCount > 0, "waveCount should be positive");
2015-07-10 06:01:56 -07:00
checkSwitchTimes2(phaseCount, switchTimes);
2018-02-05 14:16:16 -08:00
this->pwmCycleCallback = pwmCycleCallback;
2015-07-10 06:01:56 -07:00
this->stateChangeCallback = stateChangeCallback;
multiWave.waveCount = waveCount;
copyPwmParameters(this, phaseCount, switchTimes, waveCount, pinStates);
safe.phaseIndex = 0;
safe.periodNt = -1;
safe.iteration = -1;
// let's start the indefinite callback loop of PWM generation
timerCallback(this);
}
2018-11-02 10:38:31 -07:00
void startSimplePwm(PwmConfig *state, const char *msg, OutputPin *output, float frequency, float dutyCycle, pwm_gen_callback *stateChangeCallback) {
2018-12-08 12:07:07 -08:00
efiAssertVoid(CUSTOM_ERR_6692, state != NULL, "state");
2018-11-02 10:38:31 -07:00
efiAssertVoid(CUSTOM_ERR_6665, dutyCycle >= 0 && dutyCycle <= 1, "dutyCycle");
if (frequency < 1) {
warning(CUSTOM_OBD_LOW_FREQUENCY, "low frequency %.2f", frequency);
return;
}
float switchTimes[] = { dutyCycle, 1 };
pin_state_t pinStates0[] = { 0, 1 };
pin_state_t *pinStates[1] = { pinStates0 };
state->outputPins[0] = output;
state->setFrequency(frequency);
state->weComplexInit(msg, 2, switchTimes, 1, pinStates, NULL, stateChangeCallback);
}
void startSimplePwmExt(PwmConfig *state, const char *msg, brain_pin_e brainPin, OutputPin *output, float frequency,
float dutyCycle, pwm_gen_callback *stateChangeCallback) {
output->initPin(msg, brainPin);
startSimplePwm(state, msg, output, frequency, dutyCycle, stateChangeCallback);
}
2018-11-03 10:45:36 -07:00
/**
* This method controls the actual hardware pins
*
* This method takes ~350 ticks.
*/
void applyPinState(PwmConfig *state, int stateIndex) {
efiAssertVoid(CUSTOM_ERR_6663, stateIndex < PWM_PHASE_MAX_COUNT, "invalid stateIndex");
efiAssertVoid(CUSTOM_ERR_6664, state->multiWave.waveCount <= PWM_PHASE_MAX_WAVE_PER_PWM, "invalid waveCount");
for (int waveIndex = 0; waveIndex < state->multiWave.waveCount; waveIndex++) {
OutputPin *output = state->outputPins[waveIndex];
int value = state->multiWave.waves[waveIndex].pinStates[stateIndex];
output->setValue(value);
}
}