rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
Public Member Functions | Data Fields | Private Attributes
PwmConfig Class Reference

Multi-channel software PWM output configuration. More...

#include <pwm_generator_logic.h>

Inheritance diagram for PwmConfig:
Inheritance graph
[legend]
Collaboration diagram for PwmConfig:
Collaboration graph
[legend]

Public Member Functions

 PwmConfig ()
 
void weComplexInit (Scheduler *executor, MultiChannelStateSequence const *seq, pwm_cycle_callback *pwmCycleCallback, pwm_gen_callback *callback)
 
void setFrequency (float frequency)
 
void handleCycleStart ()
 
efitick_t togglePwmState ()
 
void stop ()
 
void applyPwmValue (OutputPin *output, int stateIndex, int channelIndex=0)
 

Data Fields

Schedulerm_executor = nullptr
 
pwm_mode_e mode
 
bool isStopRequested = false
 
const charm_name
 
OutputPinoutputPins [PWM_PHASE_MAX_WAVE_PER_PWM]
 
MultiChannelStateSequence const * multiChannelStateSequence = nullptr
 
int dbgNestingLevel
 
scheduling_s scheduling
 
pwm_config_safe_state_s safe
 
pwm_cycle_callbackm_pwmCycleCallback = nullptr
 
pwm_gen_callbackm_stateChangeCallback = nullptr
 

Private Attributes

float periodNt
 
bool forceCycleStart = true
 

Detailed Description

Multi-channel software PWM output configuration.

Definition at line 50 of file pwm_generator_logic.h.

Constructor & Destructor Documentation

◆ PwmConfig()

PwmConfig::PwmConfig ( )

Definition at line 34 of file pwm_generator_logic.cpp.

34 {
35 memset((void*)&scheduling, 0, sizeof(scheduling));
36 memset((void*)&safe, 0, sizeof(safe));
38 periodNt = NAN;
40 memset(&outputPins, 0, sizeof(outputPins));
41 m_name = "[noname]";
42}
pwm_mode_e mode
OutputPin * outputPins[PWM_PHASE_MAX_WAVE_PER_PWM]
scheduling_s scheduling
const char * m_name
pwm_config_safe_state_s safe
@ PM_NORMAL

Member Function Documentation

◆ applyPwmValue()

void PwmConfig::applyPwmValue ( OutputPin output,
int  stateIndex,
int  channelIndex = 0 
)

default implementation of pwm_gen_callback which simply toggles the pins

Definition at line 395 of file pwm_generator_logic.cpp.

395 {
396 TriggerValue value = multiChannelStateSequence->getChannelState(channelIndex, stateIndex);
397 output->setValue(value == TriggerValue::RISE);
398}
virtual pin_state_t getChannelState(int channelIndex, int phaseIndex) const =0
void setValue(const char *msg, int logicValue, bool isForce=false)
Definition efi_gpio.cpp:604
MultiChannelStateSequence const * multiChannelStateSequence
TriggerValue
Here is the call graph for this function:

◆ handleCycleStart()

void PwmConfig::handleCycleStart ( )

period length has changed - we need to reset internal state

Definition at line 136 of file pwm_generator_logic.cpp.

136 {
137 if (safe.phaseIndex != 0) {
138 // https://github.com/rusefi/rusefi/issues/1030
140 return;
141 }
142
143 if (m_pwmCycleCallback) {
144 m_pwmCycleCallback(this);
145 }
146
147 // Compute the maximum number of iterations without overflowing a uint32_t worth of timestamp
148 uint32_t iterationLimitInt32 = (0xFFFFFFFF / periodNt) - 2;
149
150 // Maximum number of iterations that don't lose precision due to 32b float (~7 decimal significant figures)
151 // We want at least 0.01% timing precision (aka 1/10000 cycle, 0.072 degree for trigger stimulator), which
152 // means we can't do any more than 2^23 / 10000 cycles = 838 iterations before a reset
153 uint32_t iterationLimitFloat = 838;
154
155 uint32_t iterationLimit = minI(iterationLimitInt32, iterationLimitFloat);
156
157 efiAssertVoid(ObdCode::CUSTOM_ERR_6580, periodNt != 0, "period not initialized");
158 efiAssertVoid(ObdCode::CUSTOM_ERR_6580, iterationLimit > 0, "iterationLimit invalid");
159 if (forceCycleStart || safe.periodNt != periodNt || safe.iteration == iterationLimit) {
160 /**
161 * period length has changed - we need to reset internal state
162 */
164 safe.iteration = 0;
166
167 forceCycleStart = false;
168#if DEBUG_PWM
169 efiPrintf("state reset start=%d iteration=%d", state->safe.start, state->safe.iteration);
170#endif
171 }
172}
pwm_cycle_callback * m_pwmCycleCallback
efitick_t getTimeNowNt()
Definition efitime.cpp:19
void firmwareError(ObdCode code, const char *fmt,...)
@ CUSTOM_PWM_CYCLE_START
@ CUSTOM_ERR_6580
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1871, 1.0, -1.0, -1.0, "")

Referenced by togglePwmState().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setFrequency()

void PwmConfig::setFrequency ( float  frequency)
Parameters
useNAN frequency to pause PWM

see handleCycleStart() 'periodNt' is below 10 seconds here so we use 32 bit type for performance reasons

Definition at line 119 of file pwm_generator_logic.cpp.

119 {
120 if (std::isnan(frequency)) {
121 // explicit code just to be sure
122 periodNt = NAN;
123 return;
124 }
125 /**
126 * see #handleCycleStart()
127 * 'periodNt' is below 10 seconds here so we use 32 bit type for performance reasons
128 */
129 periodNt = USF2NT(frequency2periodUs(frequency));
130}
static float frequency
Definition init_flex.cpp:21

Referenced by TachometerModule::onFastCallback(), DcHardware::setFrequency(), setTriggerEmulatorRPM(), and speedoUpdate().

Here is the caller graph for this function:

◆ stop()

void PwmConfig::stop ( )

Definition at line 132 of file pwm_generator_logic.cpp.

132 {
133 isStopRequested = true;
134}

Referenced by disableTriggerStimulator().

Here is the caller graph for this function:

◆ togglePwmState()

efitick_t PwmConfig::togglePwmState ( )
Returns
Next time for signal toggle

Here is where the 'business logic' - the actual pin state change is happening

Definition at line 177 of file pwm_generator_logic.cpp.

177 {
178 if (isStopRequested) {
179 return 0;
180 }
181
182#if DEBUG_PWM
183 efiPrintf("togglePwmState phaseIndex=%d iteration=%d", safe.phaseIndex, safe.iteration);
184 efiPrintf("period=%.2f safe.period=%.2f", period, safe.periodNt);
185#endif
186
187 if (std::isnan(periodNt)) {
188 // NaN period means PWM is paused, we also set the pin low
190 m_stateChangeCallback(0, this);
191 }
192
193 return getTimeNowNt() + MS2NT(NAN_FREQUENCY_SLEEP_PERIOD_MS);
194 }
195 if (mode != PM_NORMAL) {
196 // in case of ZERO or FULL we are always at starting index
197 safe.phaseIndex = 0;
198 }
199
200 if (safe.phaseIndex == 0) {
202 }
203
204 /**
205 * Here is where the 'business logic' - the actual pin state change is happening
206 */
207 int cbStateIndex;
208 if (mode == PM_NORMAL) {
209 // callback state index is offset by one. todo: why? can we simplify this?
210 cbStateIndex = safe.phaseIndex == 0 ? multiChannelStateSequence->phaseCount - 1 : safe.phaseIndex - 1;
211 } else if (mode == PM_ZERO) {
212 cbStateIndex = 0;
213 } else {
214 cbStateIndex = 1;
215 }
216
217 {
220 m_stateChangeCallback(cbStateIndex, this);
221 }
222 }
223
224 efitick_t nextSwitchTimeNt = getNextSwitchTimeNt(this);
225#if DEBUG_PWM
226 efiPrintf("%s: nextSwitchTime %d", state->m_name, nextSwitchTime);
227#endif /* DEBUG_PWM */
228
229 // If we're very far behind schedule, restart the cycle fresh to avoid scheduling a huge pile of events all at once
230 // This can happen during config write or debugging where CPU is halted for multiple seconds
231 bool isVeryBehindSchedule = nextSwitchTimeNt < getTimeNowNt() - MS2NT(10);
232
234 if (isVeryBehindSchedule || safe.phaseIndex == multiChannelStateSequence->phaseCount || mode != PM_NORMAL) {
235 safe.phaseIndex = 0; // restart
236 safe.iteration++;
237
238 if (isVeryBehindSchedule) {
239 forceCycleStart = true;
240 }
241 }
242#if EFI_UNIT_TEST
243 printf("PWM: nextSwitchTimeNt=%d phaseIndex=%d iteration=%d\r\n", nextSwitchTimeNt,
246#endif /* EFI_UNIT_TEST */
247 return nextSwitchTimeNt;
248}
beuint32_t period
pwm_gen_callback * m_stateChangeCallback
@ PwmConfigStateChangeCallback
static efitick_t getNextSwitchTimeNt(PwmConfig *state)
printf("\n")
Here is the call graph for this function:

◆ weComplexInit()

void PwmConfig::weComplexInit ( Scheduler executor,
MultiChannelStateSequence const *  seq,
pwm_cycle_callback pwmCycleCallback,
pwm_gen_callback stateChangeCallback 
)

this method also starts the timer cycle See also startSimplePwm

Definition at line 290 of file pwm_generator_logic.cpp.

292 {
293 m_executor = executor;
294 isStopRequested = false;
295
296 // NaN is 'not initialized' but zero is not expected
297 criticalAssertVoid(periodNt != 0, "period is not initialized");
298 criticalAssertVoid(seq->phaseCount != 0, "signal length cannot be zero");
299 criticalAssertVoid(seq->phaseCount <= PWM_PHASE_MAX_COUNT, "too many phases in PWM");
300 criticalAssertVoid(seq->waveCount > 0, "waveCount should be positive");
301
302 m_pwmCycleCallback = pwmCycleCallback;
303 m_stateChangeCallback = stateChangeCallback;
304
305 copyPwmParameters(this, seq);
306
307 safe.phaseIndex = 0;
308 safe.periodNt = -1;
309 safe.iteration = -1;
310
311 // let's start the indefinite callback loop of PWM generation
312 timerCallback(this);
313}
Scheduler * m_executor
static void timerCallback(PwmConfig *state)
void copyPwmParameters(PwmConfig *state, MultiChannelStateSequence const *seq)

Referenced by startSimulatedTriggerSignal().

Here is the call graph for this function:
Here is the caller graph for this function:

Field Documentation

◆ dbgNestingLevel

int PwmConfig::dbgNestingLevel

Definition at line 83 of file pwm_generator_logic.h.

Referenced by PwmConfig().

◆ forceCycleStart

bool PwmConfig::forceCycleStart = true
private

Definition at line 106 of file pwm_generator_logic.h.

Referenced by handleCycleStart(), and togglePwmState().

◆ isStopRequested

bool PwmConfig::isStopRequested = false

◆ m_executor

Scheduler* PwmConfig::m_executor = nullptr

Definition at line 60 of file pwm_generator_logic.h.

Referenced by weComplexInit().

◆ m_name

const char* PwmConfig::m_name

◆ m_pwmCycleCallback

pwm_cycle_callback* PwmConfig::m_pwmCycleCallback = nullptr

this callback is invoked before each wave generation cycle

Definition at line 92 of file pwm_generator_logic.h.

Referenced by handleCycleStart(), and weComplexInit().

◆ m_stateChangeCallback

pwm_gen_callback* PwmConfig::m_stateChangeCallback = nullptr

this main callback is invoked when it's time to switch level on any of the output channels

Definition at line 97 of file pwm_generator_logic.h.

Referenced by SimplePwm::setSimplePwmDutyCycle(), togglePwmState(), and weComplexInit().

◆ mode

pwm_mode_e PwmConfig::mode

We need to handle zero duty cycle and 100% duty cycle in a special way

Definition at line 65 of file pwm_generator_logic.h.

Referenced by PwmConfig(), SimplePwm::setSimplePwmDutyCycle(), and togglePwmState().

◆ multiChannelStateSequence

MultiChannelStateSequence const* PwmConfig::multiChannelStateSequence = nullptr

Definition at line 78 of file pwm_generator_logic.h.

Referenced by applyPwmValue(), and togglePwmState().

◆ outputPins

OutputPin* PwmConfig::outputPins[PWM_PHASE_MAX_WAVE_PER_PWM]

◆ periodNt

float PwmConfig::periodNt
private

float value of PWM period PWM generation is not happening while this value is NAN

Definition at line 103 of file pwm_generator_logic.h.

Referenced by handleCycleStart(), PwmConfig(), setFrequency(), togglePwmState(), and weComplexInit().

◆ safe

pwm_config_safe_state_s PwmConfig::safe

◆ scheduling

scheduling_s PwmConfig::scheduling

Definition at line 85 of file pwm_generator_logic.h.

Referenced by PwmConfig().


The documentation for this class was generated from the following files: