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<280u>::getSwitchTime(int) const:
✓ Decision 'true' taken 731668 times.
MultiChannelStateSequenceWithData<2u>::getSwitchTime(int) const:
✓ Decision 'true' taken 14 times.
|
731682 | float getSwitchTime(int phaseIndex) const override { | |
79 | 731682 | return switchTimes[phaseIndex]; | ||
80 | } | |||
81 | ||||
82 |
0/1✗ Decision 'true' not taken.
|
1709953 | pin_state_t getChannelState(int channelIndex, int phaseIndex) const override { | |
83 | 1709953 | if (channelIndex >= waveCount) { | ||
84 | // todo: would be nice to get this asserting working | |||
85 | //criticalError("channel index %d/%d", channelIndex, waveCount); | |||
86 | } | |||
87 | 1709953 | return ((waveForm[phaseIndex] >> channelIndex) & 1) ? TriggerValue::RISE : TriggerValue::FALL; | ||
88 | } | |||
89 | ||||
90 | 3043 | void reset() { | ||
91 | 3043 | waveCount = 0; | ||
92 | 3043 | } | ||
93 | ||||
94 | 15256 | void setSwitchTime(const int phaseIndex, const float value) { | ||
95 | 15256 | switchTimes[phaseIndex] = value; | ||
96 | 15256 | } | ||
97 | ||||
98 | 44462 | void setChannelState(const int channelIndex, const int phaseIndex, pin_state_t state) { | ||
99 | 44462 | if (channelIndex >= waveCount) { | ||
100 | // todo: would be nice to get this asserting working | |||
101 | //criticalError("channel index %d/%d", channelIndex, waveCount); | |||
102 | } | |||
103 | 44462 | uint8_t & ref = waveForm[phaseIndex]; | ||
104 |
4/4MultiChannelStateSequenceWithData<280u>::setChannelState(int, int, TriggerValue):
✓ Branch 0 taken 14919 times.
✓ Branch 1 taken 29535 times.
MultiChannelStateSequenceWithData<2u>::setChannelState(int, int, TriggerValue):
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
44462 | ref = (ref & ~(1U << channelIndex)) | ((state == TriggerValue::RISE ? 1 : 0) << channelIndex); | |
105 | 44462 | } | ||
106 | ||||
107 | private: | |||
108 | float switchTimes[max_phase]; | |||
109 | uint8_t waveForm[max_phase]; | |||
110 | }; | |||
111 | ||||
112 |