-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathTISM_IRQHandler.c
417 lines (359 loc) · 25.8 KB
/
TISM_IRQHandler.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
/*
IRQHandler.c
============
Routines to process external interrupts (IRQs). Other functions can 'subscribe' to these events, after which IRQHandler will
send messages when events occur. This task uses the global circular buffer IRQHandlerInboundQueue defined in TISM_Definitions.h
The IRQ handler process:
- Task subscribe thenselves to events on GPIOs by using TISM_IRQHandlerSubscribe-function, which sends a
subscription-message to TISM_IRQHandler (type=GPIO number, message=interrupt events).
- TISM_IRQHander registers the subscriptions (using linear list) and the generic eventhandler to the specified GPIO.
- When an interrupt is received TISM_IRQHandler (TISM_IRQHandlerCallback function) writes a message to IRQHandlerInboundQueue.
- TISM_Scheduler continuously checks for the availability of new messages in this queue. If a message is waiting,
TISM_IRQHandler is started.
- Based on the linear list TISM_IRQHandler sends messages to the corresponding tasks via the regular messaging method.
- The subscribed tasks will receive a message coming from TISM_IRQHandler, GPIO number as message type and Event number as
message for further processing.
Copyright (c) 2024 Maarten Klarenbeek (https://github.com/mjklaren)
Distributed under the GPLv3 license
*/
#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "TISM.h"
/*
The internal structures for the IRQ handler.
*/
// Structure of the linear list containing all subscriptions to interrupts; one list per GPIO.
struct TISM_IRQHandlerSubscription
{
uint8_t TaskID;
uint32_t Events;
uint32_t AntiBounceTimeout;
uint64_t LastSuccessfullInterrupt;
struct TISM_IRQHandlerSubscription *NextSubscription;
};
// Structure containing relevevant information for management of the GPIO.
struct TISM_IRQHandlerDataGPIO
{
bool Initialized, GPIOPullDown;
uint32_t EventMask;
struct TISM_IRQHandlerSubscription *Subscriptions;
};
// The structure containing all data for TISM_IRQHandler to run.
struct TISM_IRQHandlerData
{
struct TISM_IRQHandlerDataGPIO GPIO[NUMBER_OF_GPIO_PORTS];
} TISM_IRQHandlerData;
/*
Description
Subscribe a task to a specific event occuring on the specified GPIO by sending a request message
to TISM_IRQHandler. No error checking on the specified GPIOs or Event types. WHen the specified event occurs,
TISM_IRQHandler sends a message (message type is the GPIO number).
Note: when a subscription is handled for the first time for a specific GPIO, the port function is set to
GPIO_FUNC_SIO, direction is set to input and treated as 'pull down' port.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
uint GPIO - GPIO subscription to modify.
uint32_t Events - Events to subscribe to (one or more, by applying bitwise OR '|'):
GPIO_IRQ_LEVEL_LOW
GPIO_IRQ_LEVEL_HIGH
GPIO_IRQ_EDGE_FALL
GPIO_IRQ_EDGE_RISE
IRQ_UNSUBSCRIBE - Unsubscribe from the specified GPIO interrupts
bool GPIOPullDown - Initialize GPIO with pull-down resistor when ´true´. The first registration determines the actual setting.
uint32_t AntiBounceTimeout - Timeout period (in usec) that needs to expire before next similar interrupt is forwarded
(anti-bounce measure). Max. timeout period is 16777215 usec (24 bits); we use the remaining bits
to pass other parameters through the message.
Events: GPIO_IRQ_LEVEL_LOW = 0x1u , GPIO_IRQ_LEVEL_HIGH = 0x2u , GPIO_IRQ_EDGE_FALL = 0x4u , GPIO_IRQ_EDGE_RISE = 0x8u
Return value:
false - Message delivery failed.
true - Request sent.
*/bool TISM_IRQHandlerSubscribe(TISM_Task ThisTask, uint8_t GPIO, uint32_t Events, bool GPIOPullDown, uint32_t AntiBounceTimeout)
{
// Use the Specification-field in the message to capture the AntiBounceTimeout and the GPIOPullDown.
uint32_t CombinedValue=(0xFFFFFF & AntiBounceTimeout)+(GPIOPullDown==true?0x01000000:0);
return(TISM_PostmanWriteMessage(ThisTask,System.TISM_IRQHandlerTaskID,GPIO,Events,CombinedValue));
}
// Calculate the event mask for all registered subscriptions for a specific GPIO by calculating the OR-value of all events.
uint32_t TISM_IRQHandlerCalculateEventsMask(struct TISM_IRQHandlerSubscription *GPIO)
{
uint32_t EventMask=0;
struct TISM_IRQHandlerSubscription *NextGPIO;
// Is the first value not null (no subscription registered for this GPIO)?
if(GPIO!=NULL)
{
// Run through all the registered subscription events and calculate the events mask (OR-function of all registered events).
NextGPIO=GPIO;
do
{
EventMask=EventMask|NextGPIO->Events;
NextGPIO=NextGPIO->NextSubscription;
}
while(NextGPIO!=NULL);
}
else
{
// Current GPIO register is empty; return 0.
return(0);
}
return(EventMask);
}
// The generic interrupt handler; this function is registered for handling of all interrupts. When an interrupt occurs
// the details of the event are written to a circular buffer (IRQHandlerInboundQueue) for future processing.
void TISM_IRQHandlerCallback(uint8_t GPIO,uint32_t Events)
{
// Interrupt received; write the interrupt to the circular buffer IRQHandlerInboundQueue for later processing.
TISM_CircularBufferWrite(&IRQHandlerInboundQueue,System.TISM_IRQHandlerTaskID,System.TISM_IRQHandlerTaskID,GPIO,Events,0);
gpio_acknowledge_irq(GPIO,Events);
}
/*
Description
The main task for the IRQ handler. Handles the initialization and registration of subscriptions to GPIO interrupts.
This function is called by TISM_Scheduler.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
Return value:
OK - Task run completed succesfully.
None-zero value - Error
*/
uint8_t TISM_IRQHandler (TISM_Task ThisTask)
{
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Run starting.");
switch(ThisTask.TaskState)
{
case INIT: // Task required to initialize
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Doing work with priority %d on core %d.", ThisTask.TaskPriority, ThisTask.RunningOnCoreID);
// Init the circular buffer to receive interrupts and clear the subscription-list.
TISM_CircularBufferInit (&IRQHandlerInboundQueue);
for(uint8_t counter=0;counter<NUMBER_OF_GPIO_PORTS;counter++)
{
TISM_IRQHandlerData.GPIO[counter].Initialized=false;
TISM_IRQHandlerData.GPIO[counter].GPIOPullDown=true;
TISM_IRQHandlerData.GPIO[counter].EventMask=0;
TISM_IRQHandlerData.GPIO[counter].Subscriptions=NULL;
}
// Go to sleep; we only wake on incoming messages.
TISM_TaskManagerSetMyTaskAttribute(ThisTask,TISM_SET_TASK_SLEEP,true);
break;
case RUN: // Do the work
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Doing work with priority %d on core %d.", ThisTask.TaskPriority, ThisTask.RunningOnCoreID);
// Are there any interrupts waiting in the circular buffer we need to process?
uint16_t MessageCounter=0;
TISM_Message *MessageToProcess;
while((TISM_CircularBufferMessagesWaiting(&IRQHandlerInboundQueue)>0) && (MessageCounter<MAX_MESSAGES))
{
// Read the next IRQ message from the queue and check which tasks have subscribed to it.
MessageToProcess=TISM_CircularBufferRead(&IRQHandlerInboundQueue);
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Processing interrupt message '%ld' with type %d from the IRQ handler queue.", MessageToProcess->Message, MessageToProcess->MessageType);
if(TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Initialized)
{
struct TISM_IRQHandlerSubscription *SearchPointer=TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions;
while(SearchPointer!=NULL)
{
if(MessageToProcess->Message & SearchPointer->Events)
{
// Is an anti bounce value specified? If so, did we receive a message too soon?
if((SearchPointer->AntiBounceTimeout==0) ||
(MessageToProcess->MessageTimestamp>(SearchPointer->LastSuccessfullInterrupt+SearchPointer->AntiBounceTimeout)))
{
TISM_PostmanWriteMessage(ThisTask,SearchPointer->TaskID,MessageToProcess->MessageType,MessageToProcess->Message,TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].GPIOPullDown);
SearchPointer->LastSuccessfullInterrupt=MessageToProcess->MessageTimestamp;
}
else
{
// Message blocked as it is received within the anti bounce timeout period.
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Interrupt from GPIO %d blocked for task ID %d, within anti bounce timeout (%d).", MessageToProcess->MessageType, SearchPointer->TaskID, SearchPointer->AntiBounceTimeout);
}
}
SearchPointer=SearchPointer->NextSubscription;
}
}
// Processed the message; delete it.
TISM_CircularBufferDelete(&IRQHandlerInboundQueue);
MessageCounter++;
}
// Now check for other pending messages from other tasks.
MessageCounter=0;
while((TISM_PostmanMessagesWaiting(ThisTask)>0) && (MessageCounter<MAX_MESSAGES))
{
MessageToProcess=TISM_PostmanReadMessage(ThisTask);
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Message '%ld' type %d from TaskID %d (%s) received.", MessageToProcess->Message, MessageToProcess->MessageType, MessageToProcess->SenderTaskID, System.Task[MessageToProcess->SenderTaskID].TaskName);
// Processed the message.
switch(MessageToProcess->MessageType)
{
case TISM_PING: // Check if this process is still alive. Reply with a ECHO message type; return same message payload.
TISM_PostmanWriteMessage(ThisTask,MessageToProcess->SenderTaskID,TISM_ECHO,MessageToProcess->Message,0);
break;
case GPIO_0: // GPIO number received as message type - we need to create or adjust a IRQ subscription.
case GPIO_1:
case GPIO_2:
case GPIO_3:
case GPIO_4:
case GPIO_5:
case GPIO_6:
case GPIO_7:
case GPIO_8:
case GPIO_9:
case GPIO_10:
case GPIO_11:
case GPIO_12:
case GPIO_13:
case GPIO_14:
case GPIO_15:
case GPIO_16:
case GPIO_17:
case GPIO_18:
case GPIO_19:
case GPIO_20:
case GPIO_21:
case GPIO_22:
case GPIO_26:
case GPIO_27:
case GPIO_28: // Subscription request received; register or update.
// Is this GPIO already initialized? If not, then this is our first subscription.
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Processing GPIO request.");
struct TISM_IRQHandlerSubscription *SearchPointer, *PreviousSearchPointer;
if(TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Initialized==false)
{
// Bug fix; when we receive an IRQ_UNSUBSCRIBE-request to an uninitialized port.
if(MessageToProcess->Message==IRQ_UNSUBSCRIBE)
{
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_ERROR, "Warning - Unsubscribe request received from %d (%s) for an uninitialized GPIO (%d); ignoring.", MessageToProcess->SenderTaskID, System.Task[MessageToProcess->SenderTaskID].TaskName, MessageToProcess->MessageType);
break;
}
// Initialize the GPIO port for inbound, with the internal pull-down resistor set.
gpio_set_function(MessageToProcess->MessageType, GPIO_FUNC_SIO);
gpio_set_dir(MessageToProcess->MessageType, false);
// Is the 'pull down' bit set in the Specification-field?
if((MessageToProcess->Specification & 0x01000000)==0x01000000)
{
gpio_pull_down(MessageToProcess->MessageType);
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].GPIOPullDown=true;
}
else
{
gpio_pull_up(MessageToProcess->MessageType);
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].GPIOPullDown=false;
}
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "First subscription, GPIO %d initialized (request from Task ID %d, event %d, internal resistor pull-%s).", MessageToProcess->MessageType, MessageToProcess->SenderTaskID, MessageToProcess->Message, (TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].GPIOPullDown==false?"up":"down"));
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Initialized=true;
// Now register the first subscription in the linear list.
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions=(struct TISM_IRQHandlerSubscription *) malloc(sizeof(struct TISM_IRQHandlerSubscription));
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions->TaskID=MessageToProcess->SenderTaskID;
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions->Events=MessageToProcess->Message;
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions->AntiBounceTimeout=(MessageToProcess->Specification & 0xFFFFFF);
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions->LastSuccessfullInterrupt=0;
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions->NextSubscription=NULL;
}
else
{
// There are already tasks subscribed to this GPIO.
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Subscription to GPIO %d being added or modified (Task ID %d, event %d).", MessageToProcess->MessageType, MessageToProcess->SenderTaskID, MessageToProcess->Message);
// Find the entry for this TaskID in the linear list, or create a new one.
SearchPointer=TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions;
PreviousSearchPointer=SearchPointer;
while((SearchPointer!=NULL) && (SearchPointer->TaskID!=MessageToProcess->SenderTaskID))
{
// Not the one weŕe looking for; move to the next entry.
PreviousSearchPointer=SearchPointer;
SearchPointer=SearchPointer->NextSubscription;
}
// Weŕe out of the loop; the pointer is either pointing to an existing record, or at the end of the list.
if(SearchPointer!=NULL)
{
// Delete or update the existing record?
if(MessageToProcess->Message==IRQ_UNSUBSCRIBE)
{
// Unsubscribe = delete the record from the linear list.
if(SearchPointer==PreviousSearchPointer)
{
// The record is the very first in the list.
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions=SearchPointer->NextSubscription;
}
else
{
// Remove the record from within the list.
PreviousSearchPointer->NextSubscription=SearchPointer->NextSubscription;
}
free(SearchPointer);
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Task ID %d unsubscribed from GPIO %d.", MessageToProcess->SenderTaskID, MessageToProcess->MessageType);
// Was this the last subscription to this GPIO? Then we can 'release' this GPIO.
if(TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions==NULL)
{
// No subscriptions.
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "No tasks subscribed to GPIO %d, releasing.", MessageToProcess->MessageType);
// Todo; interrupt handler for GPIO's are never released - not even possible in SDK?
}
}
else
{
// Update the existing record in the list.
SearchPointer->Events=MessageToProcess->Message;
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Subscription to GPIO %d modified.", MessageToProcess->MessageType);
}
}
else
{
// Add a new record to the linear list
struct TISM_IRQHandlerSubscription *NewSubscription=(struct TISM_IRQHandlerSubscription *) malloc(sizeof(struct TISM_IRQHandlerSubscription));
NewSubscription->TaskID=MessageToProcess->SenderTaskID;
NewSubscription->Events=MessageToProcess->Message;
NewSubscription->AntiBounceTimeout=(MessageToProcess->Specification & 0xFFFFFF);
NewSubscription->LastSuccessfullInterrupt=0;
NewSubscription->NextSubscription=NULL;
PreviousSearchPointer->NextSubscription=NewSubscription;
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Task ID %d subscribed to GPIO %d, anti-bounce value %d", MessageToProcess->SenderTaskID, MessageToProcess->MessageType, NewSubscription->AntiBounceTimeout);
}
}
// Recalculate the new eventmask for this GPIO and set it.
if(TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions!=NULL)
{
// There are still subscriptions left; recalculate the event mask and set the interrupt handler.
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].EventMask=TISM_IRQHandlerCalculateEventsMask(TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions);
// (Re)register our interrupt-handler to this GPIO.
gpio_set_irq_enabled_with_callback(MessageToProcess->MessageType,TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].EventMask,true,(void*)&TISM_IRQHandlerCallback);
}
else
{
// No subscriptions left. Set the eventmask to 0 and disable the interrupt handler for this GPIO.
TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].EventMask=0;
//gpio_set_irq_enabled_with_callback(MessageToProcess->MessageType,TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].EventMask,false,(void*)&TISM_IRQHandlerCallback);
}
if (ThisTask.TaskDebug)
{
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Subscriptions list for GPIO %d updated.", MessageToProcess->MessageType);
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Tasks registered to interrupts on this GPIO:");
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "============================================");
SearchPointer=TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].Subscriptions;
while(SearchPointer!=NULL)
{
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Task ID: %d (%s) subscribed to event %d, anti-bounce value %d.", SearchPointer->TaskID, System.Task[SearchPointer->TaskID].TaskName, SearchPointer->Events, SearchPointer->AntiBounceTimeout);
SearchPointer=SearchPointer->NextSubscription;
}
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "List complete. Summary event mask: %ld. GPIO initialized as %s.", TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].EventMask, (TISM_IRQHandlerData.GPIO[MessageToProcess->MessageType].GPIOPullDown?"pull-down":"pull-up"));
}
break;
case GPIO_23: // Power save
case GPIO_24: // VBUS detect
case GPIO_25: // LED port of the Raspberry Pi Pico
default: // Unknown message type - ignore.
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_ERROR, "Warning - Invalid GPIO subscription (%d) requested by %d (%s); ignoring.", MessageToProcess->MessageType, MessageToProcess->SenderTaskID, System.Task[MessageToProcess->SenderTaskID].TaskName);
break;
}
TISM_PostmanDeleteMessage(ThisTask);
MessageCounter++;
}
// Go to sleep; we only wake on incoming messages.
TISM_TaskManagerSetMyTaskAttribute(ThisTask,TISM_SET_TASK_SLEEP,true);
break;
case STOP: // Task required to stop
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Stopping.");
// Tasks for stopping
// Set the task state to DOWN.
TISM_TaskManagerSetMyTaskAttribute(ThisTask,TISM_SET_TASK_STATE,DOWN);
break;
}
// All done.
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Run completed.");
return (OK);
}