#129 huge step forward

This commit is contained in:
rusefi 2018-12-08 22:57:00 -05:00
parent d8b0709dd1
commit 41bcceae40
3 changed files with 125 additions and 20 deletions

View File

@ -28,6 +28,7 @@ void PwmConfig::baseConstructor() {
memset(&safe, 0, sizeof(safe)); memset(&safe, 0, sizeof(safe));
dbgNestingLevel = 0; dbgNestingLevel = 0;
periodNt = NAN; periodNt = NAN;
mode = PM_NORMAL;
memset(&outputPins, 0, sizeof(outputPins)); memset(&outputPins, 0, sizeof(outputPins));
phaseCount = 0; phaseCount = 0;
pwmCycleCallback = NULL; pwmCycleCallback = NULL;
@ -50,6 +51,7 @@ void PwmConfig::init(float *st, single_wave_s *waves) {
/** /**
* This method allows you to change duty cycle on the fly * This method allows you to change duty cycle on the fly
* @param dutyCycle value between 0 and 1 * @param dutyCycle value between 0 and 1
* See also setFrequency
*/ */
void SimplePwm::setSimplePwmDutyCycle(float dutyCycle) { void SimplePwm::setSimplePwmDutyCycle(float dutyCycle) {
if (cisnan(dutyCycle)) { if (cisnan(dutyCycle)) {
@ -60,8 +62,14 @@ void SimplePwm::setSimplePwmDutyCycle(float dutyCycle) {
warning(CUSTOM_ERR_6579, "spwd:dutyCycle %.2f", dutyCycle); warning(CUSTOM_ERR_6579, "spwd:dutyCycle %.2f", dutyCycle);
return; return;
} }
// todo: need to fix PWM so that it supports zero duty cycle if (dutyCycle == 0) {
multiWave.setSwitchTime(0, dutyCycle); mode = PM_ZERO;
} else if (dutyCycle == 1) {
mode = PM_FULL;
} else {
mode = PM_NORMAL;
multiWave.setSwitchTime(0, dutyCycle);
}
} }
/** /**
@ -70,7 +78,8 @@ void SimplePwm::setSimplePwmDutyCycle(float dutyCycle) {
static efitimeus_t getNextSwitchTimeUs(PwmConfig *state) { static efitimeus_t getNextSwitchTimeUs(PwmConfig *state) {
efiAssert(CUSTOM_ERR_ASSERT, state->safe.phaseIndex < PWM_PHASE_MAX_COUNT, "phaseIndex range", 0); efiAssert(CUSTOM_ERR_ASSERT, state->safe.phaseIndex < PWM_PHASE_MAX_COUNT, "phaseIndex range", 0);
int iteration = state->safe.iteration; int iteration = state->safe.iteration;
float switchTime = state->multiWave.getSwitchTime(state->safe.phaseIndex); // we handle PM_ZERO and PM_FULL separately
float switchTime = state->mode == PM_NORMAL ? state->multiWave.getSwitchTime(state->safe.phaseIndex) : 1;
float periodNt = state->safe.periodNt; float periodNt = state->safe.periodNt;
#if DEBUG_PWM #if DEBUG_PWM
scheduleMsg(&logger, "iteration=%d switchTime=%.2f period=%.2f", iteration, switchTime, period); scheduleMsg(&logger, "iteration=%d switchTime=%.2f period=%.2f", iteration, switchTime, period);
@ -132,9 +141,13 @@ efitimeus_t PwmConfig::togglePwmState() {
/** /**
* NaN period means PWM is paused * NaN period means PWM is paused
* TODO: what about pin state? low, high or random? * TODO: what about pin state? low, high or random?
* TODO: cover this by a unit test * TODO: cover this with a unit test
*/ */
return getTimeNowUs() + MS2US(100); return getTimeNowUs() + MS2US(NAN_FREQUENCY_SLEEP_PERIOD_MS);
}
if (mode != PM_NORMAL) {
// in case of ZERO or FULL we are always at starting index
safe.phaseIndex = 0;
} }
if (safe.phaseIndex == 0) { if (safe.phaseIndex == 0) {
@ -144,8 +157,16 @@ efitimeus_t PwmConfig::togglePwmState() {
/** /**
* Here is where the 'business logic' - the actual pin state change is happening * 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;
int cbStateIndex = safe.phaseIndex == 0 ? phaseCount - 1 : safe.phaseIndex - 1; if (mode == PM_NORMAL) {
// callback state index is offset by one. todo: why? can we simplify this?
cbStateIndex = safe.phaseIndex == 0 ? phaseCount - 1 : safe.phaseIndex - 1;
} else if (mode == PM_ZERO) {
cbStateIndex = 0;
} else {
cbStateIndex = 1;
}
stateChangeCallback(this, cbStateIndex); stateChangeCallback(this, cbStateIndex);
efitimeus_t nextSwitchTimeUs = getNextSwitchTimeUs(this); efitimeus_t nextSwitchTimeUs = getNextSwitchTimeUs(this);
@ -168,10 +189,15 @@ efitimeus_t PwmConfig::togglePwmState() {
// } // }
safe.phaseIndex++; safe.phaseIndex++;
if (safe.phaseIndex == phaseCount) { if (safe.phaseIndex == phaseCount || mode != PM_NORMAL) {
safe.phaseIndex = 0; // restart safe.phaseIndex = 0; // restart
safe.iteration++; safe.iteration++;
} }
#if EFI_UNIT_TEST
printf("PWM: nextSwitchTimeUs=%d phaseIndex=%d iteration=%d\r\n", nextSwitchTimeUs,
safe.phaseIndex,
safe.iteration);
#endif /* EFI_UNIT_TEST */
return nextSwitchTimeUs; return nextSwitchTimeUs;
} }
@ -205,7 +231,9 @@ void copyPwmParameters(PwmConfig *state, int phaseCount, float *switchTimes, int
state->multiWave.waves[waveIndex].pinStates[phaseIndex] = pinStates[waveIndex][phaseIndex]; state->multiWave.waves[waveIndex].pinStates[phaseIndex] = pinStates[waveIndex][phaseIndex];
} }
} }
state->multiWave.checkSwitchTimes(phaseCount); if (state->mode == PM_NORMAL) {
state->multiWave.checkSwitchTimes(phaseCount);
}
} }
/** /**
@ -251,6 +279,7 @@ void startSimplePwm(SimplePwm *state, const char *msg, OutputPin *output, float
float switchTimes[] = { dutyCycle, 1 }; float switchTimes[] = { dutyCycle, 1 };
pin_state_t pinStates0[] = { 0, 1 }; pin_state_t pinStates0[] = { 0, 1 };
state->setSimplePwmDutyCycle(dutyCycle);
pin_state_t *pinStates[1] = { pinStates0 }; pin_state_t *pinStates[1] = { pinStates0 };

View File

@ -13,6 +13,8 @@
#include "scheduler.h" #include "scheduler.h"
#include "efiGpio.h" #include "efiGpio.h"
#define NAN_FREQUENCY_SLEEP_PERIOD_MS 100
typedef struct { typedef struct {
/** /**
* a copy so that all phases are executed on the same period, even if another thread * a copy so that all phases are executed on the same period, even if another thread
@ -35,6 +37,12 @@ class PwmConfig;
typedef void (pwm_cycle_callback)(PwmConfig *state); typedef void (pwm_cycle_callback)(PwmConfig *state);
typedef void (pwm_gen_callback)(PwmConfig *state, int stateIndex); typedef void (pwm_gen_callback)(PwmConfig *state, int stateIndex);
typedef enum {
PM_ZERO,
PM_NORMAL,
PM_FULL
} pwm_mode_e;
/** /**
* @brief Multi-channel software PWM output configuration * @brief Multi-channel software PWM output configuration
*/ */
@ -50,6 +58,11 @@ public:
pwm_cycle_callback *pwmCycleCallback, pwm_cycle_callback *pwmCycleCallback,
pwm_gen_callback *callback); pwm_gen_callback *callback);
/**
* We need to handle zero duty cycle and 100% duty cycle in a special way
*/
pwm_mode_e mode;
/** /**
* @param use NAN frequency to pause PWM * @param use NAN frequency to pause PWM
*/ */

View File

@ -13,7 +13,7 @@
extern EventQueue schedulingQueue; extern EventQueue schedulingQueue;
extern int timeNowUs; extern int timeNowUs;
static int expectedTimeOfNextEvent = 0; static int expectedTimeOfNextEvent;
static int pinValue = -1; static int pinValue = -1;
static void testApplyPinState(PwmConfig *state, int stateIndex) { static void testApplyPinState(PwmConfig *state, int stateIndex) {
@ -38,15 +38,76 @@ static void assertNextEvent(const char *msg, int expectedPinState) {
assertEqualsM("PWM_test: queue.size", 1, schedulingQueue.size()); assertEqualsM("PWM_test: queue.size", 1, schedulingQueue.size());
} }
static void test100dutyCycle() {
print("*************************************** test100dutyCycle\r\n");
expectedTimeOfNextEvent = timeNowUs = 0;
SimplePwm pwm;
OutputPin pin;
schedulingQueue.clear();
startSimplePwm(&pwm, "unit_test",
&pin,
1000 /* frequency */,
1.0 /* duty cycle */,
&testApplyPinState);
expectedTimeOfNextEvent += 1000;
assertEqualsM2("1@1000/100", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
assertNextEvent("exec@100", 1);
expectedTimeOfNextEvent += 1000;
assertNextEvent("exec2@100", 1);
expectedTimeOfNextEvent += 1000;
assertNextEvent("exec3@100", 1);
}
static void testSwitchToNanPeriod() {
print("*************************************** testSwitchToNanPeriod\r\n");
expectedTimeOfNextEvent = timeNowUs = 0;
SimplePwm pwm;
OutputPin pin;
schedulingQueue.clear();
startSimplePwm(&pwm, "unit_test",
&pin,
1000 /* frequency */,
0.60 /* duty cycle */,
&testApplyPinState);
expectedTimeOfNextEvent += 600;
assertEqualsM2("1@1000/70", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
assertNextEvent("exec@70", 0);
assertEqualsM("time1", 600, timeNowUs);
expectedTimeOfNextEvent += 400;
assertNextEvent("exec2@70", 1);
pwm.setFrequency(NAN);
expectedTimeOfNextEvent += 600;
assertEqualsM2("1@1000/NAN", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
assertNextEvent("exec2@70", 1);
expectedTimeOfNextEvent += MS2US(NAN_FREQUENCY_SLEEP_PERIOD_MS);
assertEqualsM2("2@1000/NAN", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
assertNextEvent("exec3@NAN", 1);
}
void testPwmGenerator() { void testPwmGenerator() {
test100dutyCycle();
testSwitchToNanPeriod();
print("*************************************** testPwmGenerator\r\n"); print("*************************************** testPwmGenerator\r\n");
expectedTimeOfNextEvent = timeNowUs = 0;
SimplePwm pwm; SimplePwm pwm;
OutputPin pin; OutputPin pin;
schedulingQueue.clear(); schedulingQueue.clear();
timeNowUs = 0;
startSimplePwm(&pwm, "unit_test", startSimplePwm(&pwm, "unit_test",
&pin, &pin,
@ -68,27 +129,29 @@ void testPwmGenerator() {
pwm.setSimplePwmDutyCycle(0); pwm.setSimplePwmDutyCycle(0);
assertEqualsM2("2@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0); assertEqualsM2("2@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
assertNextEvent("exec@1", 1); assertNextEvent("exec@1", 0);
assertEqualsM("time2", 1000, timeNowUs); assertEqualsM("time2", 1000, timeNowUs);
expectedTimeOfNextEvent += 1000;
assertEqualsM2("3@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0); assertEqualsM2("3@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
assertNextEvent("exec@2", 0 /* pin value */); assertNextEvent("exec@2", 0 /* pin value */);
assertEqualsM("time3", 1000, timeNowUs); assertEqualsM("time3", 2000, timeNowUs);
expectedTimeOfNextEvent += 1000; expectedTimeOfNextEvent += 1000;
assertEqualsM2("4@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0); assertEqualsM2("4@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
// todo: this is bad - pin is high with zero duty cycle assertNextEvent("exec@3", 0 /* pin value */);
assertNextEvent("exec@3", 1 /* pin value */); assertEqualsM("time4", 3000, timeNowUs);
assertEqualsM("time4", 2000, timeNowUs); expectedTimeOfNextEvent += 1000;
assertEqualsM2("5@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0); assertEqualsM2("5@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
assertNextEvent("exec@4", 0 /* pin value */); assertNextEvent("exec@4", 0 /* pin value */);
expectedTimeOfNextEvent += 1000; expectedTimeOfNextEvent += 1000;
assertEqualsM2("6@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0); assertEqualsM2("6@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);
// todo: this is bad - pin is high with zero duty cycle assertNextEvent("exec@5", 0 /* pin value */);
assertNextEvent("exec@5", 1 /* pin value */); expectedTimeOfNextEvent += 1000;
assertEqualsM("time4", 5000, timeNowUs);
assertEqualsM2("7@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0); assertEqualsM2("7@1000/0", expectedTimeOfNextEvent, schedulingQueue.getForUnitText(0)->momentX, 0);