| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file state_sequence.h | |||
| 3 | * | |||
| 4 | * @date May 18, 2014 | |||
| 5 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 6 | */ | |||
| 7 | ||||
| 8 | #pragma once | |||
| 9 | ||||
| 10 | #include <stdint.h> | |||
| 11 | #include "rusefi_enums.h" | |||
| 12 | #include <rusefi/expected.h> | |||
| 13 | ||||
| 14 | enum class TriggerValue : uint8_t { | |||
| 15 | FALL = 0, | |||
| 16 | RISE = 1 | |||
| 17 | }; | |||
| 18 | ||||
| 19 | // see also 'HW_EVENT_TYPES' | |||
| 20 | typedef enum { | |||
| 21 | SHAFT_PRIMARY_FALLING = 0, | |||
| 22 | SHAFT_PRIMARY_RISING = 1, | |||
| 23 | SHAFT_SECONDARY_FALLING = 2, | |||
| 24 | SHAFT_SECONDARY_RISING = 3, | |||
| 25 | } trigger_event_e; | |||
| 26 | ||||
| 27 | /** | |||
| 28 | * This layer has two primary usages: | |||
| 29 | * 1) 'simple' PWM generation is used to produce actuator square control wave | |||
| 30 | * 2) 'complex' PWM generation is used for trigger simulator. | |||
| 31 | * Some triggers like Nissan 360 slot optical wheel need a lot of points to describe the shape of the wave. | |||
| 32 | * Looks like 252 is explained by 60 tooth * 2 (number of fronts) * 2 (number of crank rotations within engine cycle) | |||
| 33 | */ | |||
| 34 | #ifndef PWM_PHASE_MAX_COUNT | |||
| 35 | // as of April 2020, trigger which requires most array length is REMIX_66_2_2_2 | |||
| 36 | // we can probably reduce RAM usage if we have more custom logic of triggers with large number of tooth while | |||
| 37 | // pretty easy logic. like we do not need to REALLY have an array to remember the shape of evenly spaces 360 or 60/2 trigger :) | |||
| 38 | // todo https://github.com/rusefi/rusefi/issues/3003 | |||
| 39 | #define PWM_PHASE_MAX_COUNT 280 | |||
| 40 | #endif /* PWM_PHASE_MAX_COUNT */ | |||
| 41 | // todo: rename to TRIGGER_CHANNEL_COUNT? | |||
| 42 | #define PWM_PHASE_MAX_WAVE_PER_PWM 2 | |||
| 43 | ||||
| 44 | typedef TriggerValue pin_state_t; | |||
| 45 | ||||
| 46 | /** | |||
| 47 | * This class represents multi-channel logical signals with shared time axis | |||
| 48 | * | |||
| 49 | * This is a semi-abstract interface so that implementations can exist for either regularized | |||
| 50 | * patterns (60-2, etc) or completely arbitrary patterns stored in arrays. | |||
| 51 | */ | |||
| 52 | class MultiChannelStateSequence { | |||
| 53 | public: | |||
| 54 | /** | |||
| 55 | * values in the (0..1] range which refer to points within the period at at which pin state | |||
| 56 | * should be changed So, in the simplest case we turn pin off at 0.3 and turn it on at 1 - | |||
| 57 | * that would give us a 70% duty cycle PWM | |||
| 58 | */ | |||
| 59 | virtual float getSwitchTime(int phaseIndex) const = 0; | |||
| 60 | virtual pin_state_t getChannelState(int channelIndex, int phaseIndex) const = 0; | |||
| 61 | ||||
| 62 | // Make sure the switch times are in order and end at the very end. | |||
| 63 | void checkSwitchTimes(float scale) const; | |||
| 64 | ||||
| 65 | // Find the exact angle, or unexpected if it doesn't exist | |||
| 66 | expected<int> findAngleMatch(float angle) const; | |||
| 67 | ||||
| 68 | // returns the index at which given value would need to be inserted into sorted array | |||
| 69 | int findInsertionAngle(float angle) const; | |||
| 70 | ||||
| 71 | uint16_t phaseCount = 0; // Number of timestamps | |||
| 72 | uint16_t waveCount = 0; // Number of waveforms | |||
| 73 | }; | |||
| 74 | ||||
| 75 | template<unsigned max_phase> | |||
| 76 | class MultiChannelStateSequenceWithData : public MultiChannelStateSequence { | |||
| 77 | public: | |||
| 78 |
2/2MultiChannelStateSequenceWithData<2u>::getSwitchTime(int) const:
✓ Decision 'true' taken 14 times.
MultiChannelStateSequenceWithData<280u>::getSwitchTime(int) const:
✓ Decision 'true' taken 732389 times.
|
732403 | float getSwitchTime(int phaseIndex) const override { | |
| 79 | 732403 | return switchTimes[phaseIndex]; | ||
| 80 | } | |||
| 81 | ||||
| 82 |
0/1✗ Decision 'true' not taken.
|
1712956 | pin_state_t getChannelState(int channelIndex, int phaseIndex) const override { | |
| 83 | 1712956 | if (channelIndex >= waveCount) { | ||
| 84 | // todo: would be nice to get this asserting working | |||
| 85 | //criticalError("channel index %d/%d", channelIndex, waveCount); | |||
| 86 | } | |||
| 87 | 1712956 | return ((waveForm[phaseIndex] >> channelIndex) & 1) ? TriggerValue::RISE : TriggerValue::FALL; | ||
| 88 | } | |||
| 89 | ||||
| 90 | 3051 | void reset() { | ||
| 91 | 3051 | waveCount = 0; | ||
| 92 | 3051 | } | ||
| 93 | ||||
| 94 | 15305 | void setSwitchTime(const int phaseIndex, const float value) { | ||
| 95 | 15305 | switchTimes[phaseIndex] = value; | ||
| 96 | 15305 | } | ||
| 97 | ||||
| 98 | 44594 | void setChannelState(const int channelIndex, const int phaseIndex, pin_state_t state) { | ||
| 99 | 44594 | if (channelIndex >= waveCount) { | ||
| 100 | // todo: would be nice to get this asserting working | |||
| 101 | //criticalError("channel index %d/%d", channelIndex, waveCount); | |||
| 102 | } | |||
| 103 | 44594 | uint8_t & ref = waveForm[phaseIndex]; | ||
| 104 |
4/4MultiChannelStateSequenceWithData<280u>::setChannelState(int, int, TriggerValue):
✓ Branch 0 taken 14963 times.
✓ Branch 1 taken 29623 times.
MultiChannelStateSequenceWithData<2u>::setChannelState(int, int, TriggerValue):
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
44594 | ref = (ref & ~(1U << channelIndex)) | ((state == TriggerValue::RISE ? 1 : 0) << channelIndex); | |
| 105 | 44594 | } | ||
| 106 | ||||
| 107 | private: | |||
| 108 | float switchTimes[max_phase]; | |||
| 109 | uint8_t waveForm[max_phase]; | |||
| 110 | }; | |||
| 111 | ||||
| 112 |