rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
digital_input_exti.cpp
Go to the documentation of this file.
1/*
2 * digital_input_exti.cpp
3 *
4 * Created on: Dec 18, 2018
5 * @author Andrey Belomutskiy, (c) 2012-2021
6 */
7
8#include "pch.h"
9
10#if HAL_USE_PAL && EFI_PROD_CODE
11#include "digital_input_exti.h"
12
13/**
14 * EXTI is a funny thing: you can only use same pin on one port. For example, you can use
15 * PA0 PB5 PE2 PD7
16 * but you cannot use
17 * PA0 PB0 PE2 PD7
18 * because pin '0' would be used on two different ports
19 */
20
21#ifdef STM32_I2C_I2C1_IRQ_PRIORITY
23 nvicEnableVector(I2C1_EV_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY);
24}
25
26struct ExtiChannel
27{
28 ExtiCallback Callback = nullptr;
29 void* CallbackData;
30
31 // Name is also used as an enable bit
32 const char* Name = nullptr;
33};
34
35static ExtiChannel channels[16];
36
37// EXT is not able to give you the front direction but you could read the pin in the callback.
38int efiExtiEnablePin(const char *msg, brain_pin_e brainPin, uint32_t mode, ExtiCallback cb, void *cb_data) {
39 /* paranoid check, in case of Gpio::Unassigned getHwPort will return NULL
40 * and we will fail on next check */
41 if (!isBrainPinValid(brainPin)) {
42 return -1;
43 }
44
45 criticalAssert(msg, "efiExtiEnablePin msg must not be null", -1);
46
47 ioportid_t port = getHwPort(msg, brainPin);
48 if (port == NULL) {
49 return -1;
50 }
51
52 efiSetPadMode(msg, brainPin, PAL_MODE_INPUT);
53
54 int index = getHwPin(msg, brainPin);
55
56 auto& channel = channels[index];
57
58 /* is this index already used? */
59 if (channel.Callback) {
60 firmwareError(ObdCode::CUSTOM_ERR_PIN_ALREADY_USED_2, "%s: pin %s/index %d: exti index already used by %s (stm32 limitation, cannot use those two pins as event inputs simultaneously)",
61 msg,
62 hwPortname(brainPin),
63 index,
64 channel.Name);
65 return -1;
66 }
67
68 channel.Callback = cb;
69 channel.CallbackData = cb_data;
70 channel.Name = msg;
71
72 ioline_t line = PAL_LINE(port, index);
73 palEnableLineEvent(line, mode);
74
75 return 0;
76}
77
79{
80 /* paranoid check, in case of Gpio::Unassigned getHwPort will return NULL
81 * and we will fail on next check */
82 if (!isBrainPinValid(brainPin))
83 return;
84
85 ioportid_t port = getHwPort("exti", brainPin);
86 if (port == NULL)
87 return;
88 brain_pin_markUnused(brainPin);
89
90 int index = getHwPin("exti", brainPin);
91
92 auto& channel = channels[index];
93
94 /* is this index was used? */
95 if (!channel.Callback) {
96 return;
97 }
98
99 ioline_t line = PAL_LINE(port, index);
100 palDisableLineEvent(line);
101
102 /* mark unused */
103 channel.Name = nullptr;
104 channel.Callback = nullptr;
105 channel.CallbackData = nullptr;
106}
107
108static inline void triggerInterrupt() {
109 // Manually fire the I2C1_EV interrupt, it will be queued after this interrupt returns
110 NVIC->STIR = I2C1_EV_IRQn;
111}
112
113struct ExtiQueueEntry {
114 efitick_t Timestamp;
115 uint8_t Channel;
116};
117
118template <typename T, size_t TSize>
119class ExtiQueue {
120public:
121 void push(const T& val) {
122 if ((m_write == m_read - 1) || (m_write == TSize - 1 && m_read == 0)) {
123 // queue full, drop
124 return;
125 }
126
127 arr[m_write] = val;
128 m_write++;
129
130 // wrap end of list
131 if (m_write == TSize) {
132 m_write = 0;
133 }
134 }
135
136 expected<T> pop() {
137 if (m_read == m_write) {
138 // Queue empty
139 return unexpected;
140 }
141
142 T value = arr[m_read];
143 m_read++;
144
145 // wrap end of list
146 if (m_read == TSize) {
147 m_read = 0;
148 }
149
150 return value;
151 }
152
153private:
154 T arr[TSize];
155
156 uint8_t m_read = 0;
157 uint8_t m_write = 0;
158};
159
160static ExtiQueue<ExtiQueueEntry, 32> queue;
161
162static uint8_t overflowCounter = 0;
163
164CH_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) {
165 OSAL_IRQ_PROLOGUE();
166
167 while (true) {
168 // get the timestamp out under lock
169 // todo: lock freeeeee!
170 __disable_irq();
171 auto result = queue.pop();
172 __enable_irq();
173
174 // Queue empty, we're done here.
175 if (!result) {
176 break;
177 }
178
179 auto& entry = result.Value;
180 auto& timestamp = entry.Timestamp;
181
182 if (timestamp != 0) {
183 auto& channel = channels[entry.Channel];
184
185 if (channel.Callback) {
186 channel.Callback(channel.CallbackData, timestamp);
187 }
188 } else {
190 }
191 }
192
193 OSAL_IRQ_EPILOGUE();
194}
195
197 return overflowCounter;
198}
199
200void handleExtiIsr(uint8_t index) {
201 // No need to lock anything, we're already the highest-pri interrupt!
202
203 uint32_t pr;
204 extiGetAndClearGroup1(1U << index, pr);
205
206 if (pr & (1 << index)) {
207 queue.push({getTimeNowNt(), index});
208
210 }
211}
212
214 handleExtiIsr(0);
215}
216
218 handleExtiIsr(1);
219}
220
222 handleExtiIsr(2);
223}
224
226 handleExtiIsr(3);
227}
228
230 handleExtiIsr(4);
231}
232
234 handleExtiIsr(5);
235 handleExtiIsr(6);
236 handleExtiIsr(7);
237 handleExtiIsr(8);
238 handleExtiIsr(9);
239}
240
242 handleExtiIsr(10);
243 handleExtiIsr(11);
244 handleExtiIsr(12);
245 handleExtiIsr(13);
246 handleExtiIsr(14);
247 handleExtiIsr(15);
248}
249
250#else // not STM32
251
252// TODO: non-stm32 exti
253void efiExtiInit() {
254 criticalError("exti not supported");
255}
256
257int efiExtiEnablePin(const char *, brain_pin_e, uint32_t, ExtiCallback, void *)
258{
259 return 0;
260}
262
263uint8_t getExtiOverflowCounter() {
264 return 0;
265}
266
267#endif
268
269#endif /* HAL_USE_PAL && EFI_PROD_CODE */
uint16_t channel
Definition adc_inputs.h:104
void efiSetPadMode(const char *msg, brain_pin_e brainPin, iomode_t mode)
void efiExtiInit()
uint8_t getExtiOverflowCounter()
void efiExtiDisablePin(brain_pin_e brainPin)
void handleExtiIsr(uint8_t index)
static uint8_t overflowCounter
static ExtiChannel channels[16]
CH_FAST_IRQ_HANDLER(Vector58)
int efiExtiEnablePin(const char *msg, brain_pin_e brainPin, uint32_t mode, ExtiCallback cb, void *cb_data)
CH_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER)
static void triggerInterrupt()
static ExtiQueue< ExtiQueueEntry, 32 > queue
void(*)(void *, efitick_t) ExtiCallback
ioportid_t getHwPort(const char *msg, brain_pin_e brainPin)
ioportmask_t getHwPin(const char *msg, brain_pin_e brainPin)
efitick_t getTimeNowNt()
Definition efitime.cpp:19
void firmwareError(ObdCode code, const char *fmt,...)
GPIO_TypeDef * ioportid_t
Port Identifier.
uint32_t ioline_t
Type of an I/O line.
Definition hal_pal_lld.h:88
@ CUSTOM_ERR_PIN_ALREADY_USED_2
const char * hwPortname(brain_pin_e brainPin)
void brain_pin_markUnused(brain_pin_e brainPin)
bool isBrainPinValid(brain_pin_e brainPin)