-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathTISM_SoftwareTimer.c
321 lines (268 loc) · 15 KB
/
TISM_SoftwareTimer.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
/*
TISM_SoftwareTimer.c
====================
Library for setting and triggering timers. This library defines 2 types of timers; virtual and software.
Virtual timers are not 'real' timers but timer values are calculated and to be checked regularly for expiration.
Software timers are registered and a message is sent to the requesting task once the timer has expired.
As this is a software timer it isn´t very accurate; therefore timer values for software timers are specified in milliseconds.
Despite the inaccuracy software timers are still very usefull for scheduling (repetitive) tasks.
Copyright (c) 2024 Maarten Klarenbeek (https://github.com/mjklaren)
Distributed under the GPLv3 license
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "pico/stdlib.h"
#include "TISM.h"
/*
The internal structures containing all data for TISM_SoftwareTimer to run.
*/
// Structure of the linear list containing all software timers.
struct TISM_SoftwareTimerEntry
{
uint8_t TaskID, TimerID;
bool RepetitiveTimer;
uint32_t TimerIntervalMsec;
uint64_t NextTimerEventUsec;
struct TISM_SoftwareTimerEntry *NextEvent;
};
// All internal data for the TISM_SoftwareTimer task
struct TISM_SoftwareTimerData
{
struct TISM_SoftwareTimerEntry *Entries;
uint64_t FirstTimerEventUsec;
} TISM_SoftwareTimerData;
// Internal function - cancel a specific timer - erase the entry from the linear list.
void TISM_SoftwareTimerCancelTimer(uint8_t TaskID, uint8_t TimerID)
{
struct TISM_SoftwareTimerEntry *SearchPointer=TISM_SoftwareTimerData.Entries, *PreviousSearchPointer=SearchPointer;
while(SearchPointer!=NULL)
{
// Search for the timer with the specified Task and timer ID and remove it from the linear list.
if(SearchPointer->TaskID==TaskID && SearchPointer->TimerID==TimerID)
{
// Remove this entry.
// Is this the first record in the linear list?
if(SearchPointer==TISM_SoftwareTimerData.Entries)
{
// First record in the list.
TISM_SoftwareTimerData.Entries=TISM_SoftwareTimerData.Entries->NextEvent;
free(SearchPointer);
SearchPointer=TISM_SoftwareTimerData.Entries;
PreviousSearchPointer=SearchPointer;
}
else
{
// Not the first record.
PreviousSearchPointer->NextEvent=SearchPointer->NextEvent;
free(SearchPointer);
SearchPointer=PreviousSearchPointer->NextEvent;
}
}
else
{
// Evaluate the next record
PreviousSearchPointer=SearchPointer;
SearchPointer=SearchPointer->NextEvent;
}
}
}
/*
Description
Create a virtual software timer. No events, just calculate the value of future timer and return the value.
As the RP2040 doesn´t have a realtime clock the specified time is measured in usec from 'NOW'.
Parameters:
uint64_t TimerUsec - Time in usec when timer should expired.
Return value:
uint64_t - Timestamp; 'NOW' + when the timer should expire.
*/
uint64_t TISM_SoftwareTimerSetVirtual(uint64_t TimerUsec)
{
return(time_us_64()+TimerUsec);
}
/*
Description
Check the status of the virtual software timer - is it expired? If so, return true.
Parameters:
uint64_t TimerUsec - Timer value ('NOW' + timer in usec).
Return value:
true - Timer is expired.
false - Timer hasn´t expired.
*/
bool TISM_SoftwareTimerVirtualExpired(uint64_t TimerUsec)
{
return(time_us_64()>TimerUsec?true:false);
}
/*
Description
Create a SoftwareTimerEntry struct and send a message to TISM_SoftwareTimer in order to register a new timer.
As the RP2040 doesn´t have a realtime clock the specified time is measured in usec from 'NOW'.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
uint8_t TimerID - The identifier for this specific timer (unique for this Task ID).
bool RepetitiveTimer - Repetitive timer (true/false)
uint32_t TimerIntervalMsec - Time in milliseconds when timer should expired (measured from 'NOW').
Return value:
true - Timer set
false - Error setting the timer
*/
bool TISM_SoftwareTimerSet(TISM_Task ThisTask,uint8_t TimerID, bool RepetitiveTimer, uint32_t TimerIntervalMsec)
{
struct TISM_SoftwareTimerEntry *EntryPointer=malloc(sizeof(struct TISM_SoftwareTimerEntry));
EntryPointer->TaskID=ThisTask.TaskID;
EntryPointer->TimerID=(uint8_t)TimerID;
EntryPointer->RepetitiveTimer=RepetitiveTimer;
EntryPointer->TimerIntervalMsec=TimerIntervalMsec;
EntryPointer->NextTimerEventUsec=time_us_64()+(TimerIntervalMsec*1000);
EntryPointer->NextEvent=NULL;
return(TISM_PostmanWriteMessage(ThisTask,System.TISM_SoftwareTimerTaskID,TISM_SET_TIMER,(uint32_t)EntryPointer,0));
}
/*
Description
Cancel a specific timer - erase the entry from the linear list.
Parameters:
uint8_t TaskID - TaskID for which the timer was set.
uint8_t TimerID - ID of the timer to cancel
Return value:
none
*/
bool TISM_SoftwareTimerCancel(TISM_Task ThisTask, uint8_t TimerID)
{
return(TISM_PostmanWriteMessage(ThisTask,System.TISM_SoftwareTimerTaskID,TISM_CANCEL_TIMER,(uint32_t)TimerID,0));
}
/*
Description:
This is the task that is registered in the TISM-system.
This function is called by TISM_Scheduler.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
Return value:
<non zero value> - Task returned an error when executing.
OK - Run succesfully completed.
*/
uint8_t TISM_SoftwareTimer (TISM_Task ThisTask)
{
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Run starting.");
switch(ThisTask.TaskState) // Unknown states are ignored
{
case INIT: // Task required to initialize
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Initializing with priority %d.", ThisTask.TaskPriority);
// Initialize variables
TISM_SoftwareTimerData.Entries=NULL;
TISM_SoftwareTimerData.FirstTimerEventUsec=0;
// Go to sleep; we only wake after 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);
// First check for incoming messages and process these.
uint8_t MessageCounter=0;
TISM_Message *MessageToProcess;
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; delete it.
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 TISM_CANCEL_TIMER: // Cancel an existing timer
if(ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Cancellation received for software timer %d from task ID %d (%s).", MessageToProcess->Message, MessageToProcess->SenderTaskID, System.Task[MessageToProcess->SenderTaskID].TaskName);
if(TISM_SoftwareTimerData.Entries!=NULL)
{
TISM_SoftwareTimerCancelTimer((uint8_t) MessageToProcess->SenderTaskID, (uint8_t) MessageToProcess->Message);
if(ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Software timer %d from task ID %d removed.", MessageToProcess->Message, MessageToProcess->SenderTaskID);
}
else
{
// Cancel-timer message received, but list is empty.
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_ERROR, "Cancellation received for Timer ID %D (Task ID %d, %s) but no timers registered. Ignoring.", MessageToProcess->Message, MessageToProcess->Specification, System.Task[MessageToProcess->SenderTaskID].TaskName);
}
break;
case TISM_SET_TIMER: // Set a new timer. Add the new entry at the beginning of the linear list.
// Warning - no checking for duplicate entries!
if(ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "New software timer entry received from task ID %d (%s).", MessageToProcess->SenderTaskID, System.Task[MessageToProcess->SenderTaskID].TaskName);
struct TISM_SoftwareTimerEntry *SearchPointer=(struct TISM_SoftwareTimerEntry *)MessageToProcess->Message;
SearchPointer->NextEvent=TISM_SoftwareTimerData.Entries;
TISM_SoftwareTimerData.Entries=SearchPointer;
break;
default: // Unknown message type - ignore.
break;
}
TISM_PostmanDeleteMessage(ThisTask);
MessageCounter++;
}
// Work to do in this state.
// Run through the list and check which timers expired and when the next timer expires.
// Set the WakeUpTimer accordingly. Then put the task back to sleep.
if(TISM_SoftwareTimerData.Entries!=NULL)
{
uint64_t RunTimestamp=time_us_64();
struct TISM_SoftwareTimerEntry *SearchPointer=TISM_SoftwareTimerData.Entries;
TISM_SoftwareTimerData.FirstTimerEventUsec=0xFFFFFFFFFFFFFFFF;
while(SearchPointer!=NULL)
{
// Check if the specific timer is expired.
if(SearchPointer->NextTimerEventUsec<RunTimestamp)
{
// Timer expired, send out notification. If it's not repetitive, remove the entry.
if(ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Timer %d expired for task %d, sending message.", SearchPointer->TimerID, SearchPointer->TaskID);
TISM_PostmanWriteMessage(ThisTask,SearchPointer->TaskID,SearchPointer->TimerID,0,0);
if(SearchPointer->RepetitiveTimer)
{
// Repetitive timer, reschedule.
SearchPointer->NextTimerEventUsec+=(SearchPointer->TimerIntervalMsec*1000);
if(SearchPointer->NextTimerEventUsec<TISM_SoftwareTimerData.FirstTimerEventUsec)
TISM_SoftwareTimerData.FirstTimerEventUsec=SearchPointer->NextTimerEventUsec;
if(ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Repetitive timer, rescheduled to %llu.", SearchPointer->NextTimerEventUsec);
}
else
{
// Non-repetitive timer; delete it.
TISM_SoftwareTimerCancelTimer(SearchPointer->TaskID, SearchPointer->TimerID);
if(ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Non-repetitive timer, requested to be deleted.");
}
}
else
{
// When is the next pending event? Search for the lowest (active) event timestamp.
if(SearchPointer->NextTimerEventUsec<TISM_SoftwareTimerData.FirstTimerEventUsec)
TISM_SoftwareTimerData.FirstTimerEventUsec=SearchPointer->NextTimerEventUsec;
}
SearchPointer=SearchPointer->NextEvent;
}
// Set the next wake-up timer according to the first (next) pending event.
System.Task[ThisTask.TaskID].TaskWakeUpTimer=TISM_SoftwareTimerData.FirstTimerEventUsec;
if(ThisTask.TaskDebug)
{
SearchPointer=TISM_SoftwareTimerData.Entries;
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Software timer entries:");
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "=======================");
while(SearchPointer!=NULL)
{
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Task ID: %d (%s), Timer ID %d, %srepetitive, interval %d msec, next event %ld.", SearchPointer->TaskID, System.Task[SearchPointer->TaskID].TaskName, SearchPointer->TimerID, (SearchPointer->RepetitiveTimer?"":"non-"), SearchPointer->TimerIntervalMsec, SearchPointer->NextTimerEventUsec);
SearchPointer=SearchPointer->NextEvent;
}
TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "List complete. Next event: %llu.", TISM_SoftwareTimerData.FirstTimerEventUsec);
}
}
else
{
// No entries - go to sleep.
TISM_TaskManagerSetMyTaskAttribute(ThisTask,TISM_SET_TASK_SLEEP,true);
if(ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "No timers set, returning to sleep.");
}
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);
}