Simple dispatcher for an embedded system
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
This code is something that I have used for about 3-4 simple embedded systems projects.
The idea is something like this:
- This is non-OS based implementation, everything is done in an outer infinite loop. No dynamic memory involved or advised.
- It's an interrupt-driven system, polling for peripherals is not used anywhere.
- Peripherals like Timer, UART, I2C, etc loads a particular value based on the event, in a Queue-like variable.
- Non-trivial tasks like blinking an LED is done in a common
Idle
task. - Dispatcher loads the function handlers based on the event added in the queue.
- (UPDATE) All read/write activities on CPU registers are managed in the respective Interrupt Service Routine. Adding the event in queue is possibly the last thing the code does in the ISR.
Enum for possible events:
typedef enum
TASK_EMPTY = 0,
TASK_INITIALIZE = 1,
TASK_IDLE,
TASK_SOMETHING_1,
TASK_SOMETHING_2,
TASK_SOMETHING_3,
TASK_SOMETHING_4,
TASK_SOMETHING_N,
MAX_TASKS,
TASK_INVALID = -1,
eTasks_t ;
A constant array which contains the event mapped to the handler:
const stTask_t GlobalTasksList[MAX_TASKS] =
TASK_EMPTY, Placeholder ,
TASK_INITIALIZE, SystemInitialization ,
TASK_IDLE, Idle ,
TASK_SOMETHING_1, Handler_1 ,
TASK_SOMETHING_2, Handler_2 ,
TASK_SOMETHING_3, Handler_3 ,
TASK_SOMETHING_4, Handler_4 ,
TASK_SOMETHING_N, Handler_N ,
;
I talked about a Queue
:
eTasks_t Queue[MAX_TASKS];
eTasks_t qTasksToAdd[MAX_TASKS * 2];
There is a structure which binds an event and it's handler together:
typedef struct stTask
eTasks_t TaskID;
bool (*handler) (void); // Would return true for success, and false for failure
stTask_t;
stTask_t Task;
A Dispatcher
function will take care of queued events:
void Dispatcher(void)
while(1)
// Assign the task handler
Task.handler = GlobalTasksList[Task.TaskID].handler;
//Invoke handler
if(FAILURE == Task.handler())
//Something went wrong with handler, Error!
// Error handling
//Does the task needs a retry, or is a recurring task?
// Reload if yes, else discard and load the next
if(bTaskRetry == FALSE)
Queue_DiscardTask0();
//Assign the 0th task from Queue
Task.TaskID = Queue[0];
Let's look at an example of adding the event in the Queue from an ISR:
#pragma vector=PORT2_VECTOR
__interrupt void PORT2_Interrupt(void)
// Clear interrupt
CLR_BIT(P2IFG, 2); //P2IFG is a CPU register which indicates the Port pin that has generated an interrupt.
// Check if the queue is overflowing
if(qIndexTasksToAdd < MAX_TASKS-1)
// Add event
qTasksToAdd[qIndexTasksToAdd++] = TASK_SOMETHING_1;
As of now, this strategy works fine without any questionable latency.
I have used the same skeleton for systems that could have as many as 20 events lined-up, and it works as per requirement to say the least.
Another advantage of this implementation that I felt is in regards with the efficiency this has when I am debugging an issue. Everything is routed through the handler/dispatcher, so I precisely know where to have a debug point/print setup.
The concern that I have with this implementation is the fixed memory footprint is has. For example, this won't be memory efficient for a LED blinking project.
Any thoughts where I could improve on this?
c embedded
add a comment |Â
up vote
1
down vote
favorite
This code is something that I have used for about 3-4 simple embedded systems projects.
The idea is something like this:
- This is non-OS based implementation, everything is done in an outer infinite loop. No dynamic memory involved or advised.
- It's an interrupt-driven system, polling for peripherals is not used anywhere.
- Peripherals like Timer, UART, I2C, etc loads a particular value based on the event, in a Queue-like variable.
- Non-trivial tasks like blinking an LED is done in a common
Idle
task. - Dispatcher loads the function handlers based on the event added in the queue.
- (UPDATE) All read/write activities on CPU registers are managed in the respective Interrupt Service Routine. Adding the event in queue is possibly the last thing the code does in the ISR.
Enum for possible events:
typedef enum
TASK_EMPTY = 0,
TASK_INITIALIZE = 1,
TASK_IDLE,
TASK_SOMETHING_1,
TASK_SOMETHING_2,
TASK_SOMETHING_3,
TASK_SOMETHING_4,
TASK_SOMETHING_N,
MAX_TASKS,
TASK_INVALID = -1,
eTasks_t ;
A constant array which contains the event mapped to the handler:
const stTask_t GlobalTasksList[MAX_TASKS] =
TASK_EMPTY, Placeholder ,
TASK_INITIALIZE, SystemInitialization ,
TASK_IDLE, Idle ,
TASK_SOMETHING_1, Handler_1 ,
TASK_SOMETHING_2, Handler_2 ,
TASK_SOMETHING_3, Handler_3 ,
TASK_SOMETHING_4, Handler_4 ,
TASK_SOMETHING_N, Handler_N ,
;
I talked about a Queue
:
eTasks_t Queue[MAX_TASKS];
eTasks_t qTasksToAdd[MAX_TASKS * 2];
There is a structure which binds an event and it's handler together:
typedef struct stTask
eTasks_t TaskID;
bool (*handler) (void); // Would return true for success, and false for failure
stTask_t;
stTask_t Task;
A Dispatcher
function will take care of queued events:
void Dispatcher(void)
while(1)
// Assign the task handler
Task.handler = GlobalTasksList[Task.TaskID].handler;
//Invoke handler
if(FAILURE == Task.handler())
//Something went wrong with handler, Error!
// Error handling
//Does the task needs a retry, or is a recurring task?
// Reload if yes, else discard and load the next
if(bTaskRetry == FALSE)
Queue_DiscardTask0();
//Assign the 0th task from Queue
Task.TaskID = Queue[0];
Let's look at an example of adding the event in the Queue from an ISR:
#pragma vector=PORT2_VECTOR
__interrupt void PORT2_Interrupt(void)
// Clear interrupt
CLR_BIT(P2IFG, 2); //P2IFG is a CPU register which indicates the Port pin that has generated an interrupt.
// Check if the queue is overflowing
if(qIndexTasksToAdd < MAX_TASKS-1)
// Add event
qTasksToAdd[qIndexTasksToAdd++] = TASK_SOMETHING_1;
As of now, this strategy works fine without any questionable latency.
I have used the same skeleton for systems that could have as many as 20 events lined-up, and it works as per requirement to say the least.
Another advantage of this implementation that I felt is in regards with the efficiency this has when I am debugging an issue. Everything is routed through the handler/dispatcher, so I precisely know where to have a debug point/print setup.
The concern that I have with this implementation is the fixed memory footprint is has. For example, this won't be memory efficient for a LED blinking project.
Any thoughts where I could improve on this?
c embedded
Blinking a LED seems like a trivial task to me.
â Reinderien
May 11 at 20:07
What's your microcontroller? This much pointer math and function indirection is usually a bad idea in the inside of an ISR. Have you measured the latency incurred?
â Reinderien
May 11 at 20:20
This ISR that I have shared is for a TI based MSP430 microcontroller. Note that no indirection is involved in tbe ISR. ISR only sets an event in queue. The indirection and invokation happens in Dispatcher function.
â WedaPashi
May 12 at 13:10
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
This code is something that I have used for about 3-4 simple embedded systems projects.
The idea is something like this:
- This is non-OS based implementation, everything is done in an outer infinite loop. No dynamic memory involved or advised.
- It's an interrupt-driven system, polling for peripherals is not used anywhere.
- Peripherals like Timer, UART, I2C, etc loads a particular value based on the event, in a Queue-like variable.
- Non-trivial tasks like blinking an LED is done in a common
Idle
task. - Dispatcher loads the function handlers based on the event added in the queue.
- (UPDATE) All read/write activities on CPU registers are managed in the respective Interrupt Service Routine. Adding the event in queue is possibly the last thing the code does in the ISR.
Enum for possible events:
typedef enum
TASK_EMPTY = 0,
TASK_INITIALIZE = 1,
TASK_IDLE,
TASK_SOMETHING_1,
TASK_SOMETHING_2,
TASK_SOMETHING_3,
TASK_SOMETHING_4,
TASK_SOMETHING_N,
MAX_TASKS,
TASK_INVALID = -1,
eTasks_t ;
A constant array which contains the event mapped to the handler:
const stTask_t GlobalTasksList[MAX_TASKS] =
TASK_EMPTY, Placeholder ,
TASK_INITIALIZE, SystemInitialization ,
TASK_IDLE, Idle ,
TASK_SOMETHING_1, Handler_1 ,
TASK_SOMETHING_2, Handler_2 ,
TASK_SOMETHING_3, Handler_3 ,
TASK_SOMETHING_4, Handler_4 ,
TASK_SOMETHING_N, Handler_N ,
;
I talked about a Queue
:
eTasks_t Queue[MAX_TASKS];
eTasks_t qTasksToAdd[MAX_TASKS * 2];
There is a structure which binds an event and it's handler together:
typedef struct stTask
eTasks_t TaskID;
bool (*handler) (void); // Would return true for success, and false for failure
stTask_t;
stTask_t Task;
A Dispatcher
function will take care of queued events:
void Dispatcher(void)
while(1)
// Assign the task handler
Task.handler = GlobalTasksList[Task.TaskID].handler;
//Invoke handler
if(FAILURE == Task.handler())
//Something went wrong with handler, Error!
// Error handling
//Does the task needs a retry, or is a recurring task?
// Reload if yes, else discard and load the next
if(bTaskRetry == FALSE)
Queue_DiscardTask0();
//Assign the 0th task from Queue
Task.TaskID = Queue[0];
Let's look at an example of adding the event in the Queue from an ISR:
#pragma vector=PORT2_VECTOR
__interrupt void PORT2_Interrupt(void)
// Clear interrupt
CLR_BIT(P2IFG, 2); //P2IFG is a CPU register which indicates the Port pin that has generated an interrupt.
// Check if the queue is overflowing
if(qIndexTasksToAdd < MAX_TASKS-1)
// Add event
qTasksToAdd[qIndexTasksToAdd++] = TASK_SOMETHING_1;
As of now, this strategy works fine without any questionable latency.
I have used the same skeleton for systems that could have as many as 20 events lined-up, and it works as per requirement to say the least.
Another advantage of this implementation that I felt is in regards with the efficiency this has when I am debugging an issue. Everything is routed through the handler/dispatcher, so I precisely know where to have a debug point/print setup.
The concern that I have with this implementation is the fixed memory footprint is has. For example, this won't be memory efficient for a LED blinking project.
Any thoughts where I could improve on this?
c embedded
This code is something that I have used for about 3-4 simple embedded systems projects.
The idea is something like this:
- This is non-OS based implementation, everything is done in an outer infinite loop. No dynamic memory involved or advised.
- It's an interrupt-driven system, polling for peripherals is not used anywhere.
- Peripherals like Timer, UART, I2C, etc loads a particular value based on the event, in a Queue-like variable.
- Non-trivial tasks like blinking an LED is done in a common
Idle
task. - Dispatcher loads the function handlers based on the event added in the queue.
- (UPDATE) All read/write activities on CPU registers are managed in the respective Interrupt Service Routine. Adding the event in queue is possibly the last thing the code does in the ISR.
Enum for possible events:
typedef enum
TASK_EMPTY = 0,
TASK_INITIALIZE = 1,
TASK_IDLE,
TASK_SOMETHING_1,
TASK_SOMETHING_2,
TASK_SOMETHING_3,
TASK_SOMETHING_4,
TASK_SOMETHING_N,
MAX_TASKS,
TASK_INVALID = -1,
eTasks_t ;
A constant array which contains the event mapped to the handler:
const stTask_t GlobalTasksList[MAX_TASKS] =
TASK_EMPTY, Placeholder ,
TASK_INITIALIZE, SystemInitialization ,
TASK_IDLE, Idle ,
TASK_SOMETHING_1, Handler_1 ,
TASK_SOMETHING_2, Handler_2 ,
TASK_SOMETHING_3, Handler_3 ,
TASK_SOMETHING_4, Handler_4 ,
TASK_SOMETHING_N, Handler_N ,
;
I talked about a Queue
:
eTasks_t Queue[MAX_TASKS];
eTasks_t qTasksToAdd[MAX_TASKS * 2];
There is a structure which binds an event and it's handler together:
typedef struct stTask
eTasks_t TaskID;
bool (*handler) (void); // Would return true for success, and false for failure
stTask_t;
stTask_t Task;
A Dispatcher
function will take care of queued events:
void Dispatcher(void)
while(1)
// Assign the task handler
Task.handler = GlobalTasksList[Task.TaskID].handler;
//Invoke handler
if(FAILURE == Task.handler())
//Something went wrong with handler, Error!
// Error handling
//Does the task needs a retry, or is a recurring task?
// Reload if yes, else discard and load the next
if(bTaskRetry == FALSE)
Queue_DiscardTask0();
//Assign the 0th task from Queue
Task.TaskID = Queue[0];
Let's look at an example of adding the event in the Queue from an ISR:
#pragma vector=PORT2_VECTOR
__interrupt void PORT2_Interrupt(void)
// Clear interrupt
CLR_BIT(P2IFG, 2); //P2IFG is a CPU register which indicates the Port pin that has generated an interrupt.
// Check if the queue is overflowing
if(qIndexTasksToAdd < MAX_TASKS-1)
// Add event
qTasksToAdd[qIndexTasksToAdd++] = TASK_SOMETHING_1;
As of now, this strategy works fine without any questionable latency.
I have used the same skeleton for systems that could have as many as 20 events lined-up, and it works as per requirement to say the least.
Another advantage of this implementation that I felt is in regards with the efficiency this has when I am debugging an issue. Everything is routed through the handler/dispatcher, so I precisely know where to have a debug point/print setup.
The concern that I have with this implementation is the fixed memory footprint is has. For example, this won't be memory efficient for a LED blinking project.
Any thoughts where I could improve on this?
c embedded
edited May 12 at 18:04
Jamalâ¦
30.1k11114225
30.1k11114225
asked May 11 at 10:11
WedaPashi
1414
1414
Blinking a LED seems like a trivial task to me.
â Reinderien
May 11 at 20:07
What's your microcontroller? This much pointer math and function indirection is usually a bad idea in the inside of an ISR. Have you measured the latency incurred?
â Reinderien
May 11 at 20:20
This ISR that I have shared is for a TI based MSP430 microcontroller. Note that no indirection is involved in tbe ISR. ISR only sets an event in queue. The indirection and invokation happens in Dispatcher function.
â WedaPashi
May 12 at 13:10
add a comment |Â
Blinking a LED seems like a trivial task to me.
â Reinderien
May 11 at 20:07
What's your microcontroller? This much pointer math and function indirection is usually a bad idea in the inside of an ISR. Have you measured the latency incurred?
â Reinderien
May 11 at 20:20
This ISR that I have shared is for a TI based MSP430 microcontroller. Note that no indirection is involved in tbe ISR. ISR only sets an event in queue. The indirection and invokation happens in Dispatcher function.
â WedaPashi
May 12 at 13:10
Blinking a LED seems like a trivial task to me.
â Reinderien
May 11 at 20:07
Blinking a LED seems like a trivial task to me.
â Reinderien
May 11 at 20:07
What's your microcontroller? This much pointer math and function indirection is usually a bad idea in the inside of an ISR. Have you measured the latency incurred?
â Reinderien
May 11 at 20:20
What's your microcontroller? This much pointer math and function indirection is usually a bad idea in the inside of an ISR. Have you measured the latency incurred?
â Reinderien
May 11 at 20:20
This ISR that I have shared is for a TI based MSP430 microcontroller. Note that no indirection is involved in tbe ISR. ISR only sets an event in queue. The indirection and invokation happens in Dispatcher function.
â WedaPashi
May 12 at 13:10
This ISR that I have shared is for a TI based MSP430 microcontroller. Note that no indirection is involved in tbe ISR. ISR only sets an event in queue. The indirection and invokation happens in Dispatcher function.
â WedaPashi
May 12 at 13:10
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
3
down vote
.... have as many as 20 events lined-up, and it works as per requirement
Any thoughts where I could improve on this?
Stress test
The queue approach is non-deterministic and would benefit in knowing how much extra CPU is left.
Presently the processor is ample and the cumulative task list is low. There is spare CPU power. Yet let us stress test this design to find out how close and how code handles falling behind.
Add code to detect when Dispatcher()
is starving. Did every task get serviced at least once every 0.1s say?
Append a do-nothing task that consumes a x% of CPU time, call it Administratium. Did the queue ever fail to add because it was full? What was the maximum queue usage? Was task latency too great?
Armed with this adjustable idle task, preliminary testing can gauge the degree of margin in the design.
Thanks for your inputs. "Add code to detect when Dispatcher() is starving. Did every task get serviced at least once every 0.1s say?", I'd say no, I have never made theDispatcher()
to starve. So far the most frequent event that it deals with is an ADC measurement, which is at every 50 msec, LED blinks at every 1 sec, so I'll test it for events lined up with a faster rate w.r.t to 100 msec as you discussed above. About latency, I check various events by toggling a bit, used an oscilliscope, the lag is inconsistent and maximum of range 5-8 microseconds :-) Thank you.
â WedaPashi
May 14 at 7:35
add a comment |Â
up vote
2
down vote
Why do you need the stTask_t
translation? Can't you directly store function pointers?
Why do you use typedef enum
? AFAIK in C enum names live in the same namespace as other types.
How do you pass parameters to event handlers? If you expect the handler to read some register in order to learn what is happening, I can see the following problem. In a long queue of events, later event handlers can read hardware registers after those have changed. I have seen this handled with something like struct params int id; union... ;
.
You have not shown the code for adding and removing events from the queue. There could be problems with reentrancy there.
This is minor nitpicking. Function names are supposed to be verbs.
Adding _Noreturn
specifier would make the dispatcher easier to read.
Hey, thanks for attempting it. I have update a section on question, which may make your this concern inapplicable "In a long queue of events, later event handlers can read hardware registers after those have changed.", and I am sorry about that.
â WedaPashi
May 11 at 10:49
Thank you for pointing out one hurdle that event handlers can not take arguments.
â WedaPashi
May 11 at 10:51
@WedaPashi suppose the interrupt event was more complex than a pin interrupt. For exampleADC conversion complete
. You want to immediately read the value (as you are doing) and pass it somehow to the handler.
â Vorac
May 11 at 10:55
Indeed, very valid. Right now the way this is handled is by a global structure that contains ADC measurements, and ADC ISR simply read the counts and load it into the global variables. The event is then added in the queue and the handler converts counts into an engineering value.
â WedaPashi
May 11 at 11:01
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
.... have as many as 20 events lined-up, and it works as per requirement
Any thoughts where I could improve on this?
Stress test
The queue approach is non-deterministic and would benefit in knowing how much extra CPU is left.
Presently the processor is ample and the cumulative task list is low. There is spare CPU power. Yet let us stress test this design to find out how close and how code handles falling behind.
Add code to detect when Dispatcher()
is starving. Did every task get serviced at least once every 0.1s say?
Append a do-nothing task that consumes a x% of CPU time, call it Administratium. Did the queue ever fail to add because it was full? What was the maximum queue usage? Was task latency too great?
Armed with this adjustable idle task, preliminary testing can gauge the degree of margin in the design.
Thanks for your inputs. "Add code to detect when Dispatcher() is starving. Did every task get serviced at least once every 0.1s say?", I'd say no, I have never made theDispatcher()
to starve. So far the most frequent event that it deals with is an ADC measurement, which is at every 50 msec, LED blinks at every 1 sec, so I'll test it for events lined up with a faster rate w.r.t to 100 msec as you discussed above. About latency, I check various events by toggling a bit, used an oscilliscope, the lag is inconsistent and maximum of range 5-8 microseconds :-) Thank you.
â WedaPashi
May 14 at 7:35
add a comment |Â
up vote
3
down vote
.... have as many as 20 events lined-up, and it works as per requirement
Any thoughts where I could improve on this?
Stress test
The queue approach is non-deterministic and would benefit in knowing how much extra CPU is left.
Presently the processor is ample and the cumulative task list is low. There is spare CPU power. Yet let us stress test this design to find out how close and how code handles falling behind.
Add code to detect when Dispatcher()
is starving. Did every task get serviced at least once every 0.1s say?
Append a do-nothing task that consumes a x% of CPU time, call it Administratium. Did the queue ever fail to add because it was full? What was the maximum queue usage? Was task latency too great?
Armed with this adjustable idle task, preliminary testing can gauge the degree of margin in the design.
Thanks for your inputs. "Add code to detect when Dispatcher() is starving. Did every task get serviced at least once every 0.1s say?", I'd say no, I have never made theDispatcher()
to starve. So far the most frequent event that it deals with is an ADC measurement, which is at every 50 msec, LED blinks at every 1 sec, so I'll test it for events lined up with a faster rate w.r.t to 100 msec as you discussed above. About latency, I check various events by toggling a bit, used an oscilliscope, the lag is inconsistent and maximum of range 5-8 microseconds :-) Thank you.
â WedaPashi
May 14 at 7:35
add a comment |Â
up vote
3
down vote
up vote
3
down vote
.... have as many as 20 events lined-up, and it works as per requirement
Any thoughts where I could improve on this?
Stress test
The queue approach is non-deterministic and would benefit in knowing how much extra CPU is left.
Presently the processor is ample and the cumulative task list is low. There is spare CPU power. Yet let us stress test this design to find out how close and how code handles falling behind.
Add code to detect when Dispatcher()
is starving. Did every task get serviced at least once every 0.1s say?
Append a do-nothing task that consumes a x% of CPU time, call it Administratium. Did the queue ever fail to add because it was full? What was the maximum queue usage? Was task latency too great?
Armed with this adjustable idle task, preliminary testing can gauge the degree of margin in the design.
.... have as many as 20 events lined-up, and it works as per requirement
Any thoughts where I could improve on this?
Stress test
The queue approach is non-deterministic and would benefit in knowing how much extra CPU is left.
Presently the processor is ample and the cumulative task list is low. There is spare CPU power. Yet let us stress test this design to find out how close and how code handles falling behind.
Add code to detect when Dispatcher()
is starving. Did every task get serviced at least once every 0.1s say?
Append a do-nothing task that consumes a x% of CPU time, call it Administratium. Did the queue ever fail to add because it was full? What was the maximum queue usage? Was task latency too great?
Armed with this adjustable idle task, preliminary testing can gauge the degree of margin in the design.
answered May 12 at 14:10
chux
11.4k11238
11.4k11238
Thanks for your inputs. "Add code to detect when Dispatcher() is starving. Did every task get serviced at least once every 0.1s say?", I'd say no, I have never made theDispatcher()
to starve. So far the most frequent event that it deals with is an ADC measurement, which is at every 50 msec, LED blinks at every 1 sec, so I'll test it for events lined up with a faster rate w.r.t to 100 msec as you discussed above. About latency, I check various events by toggling a bit, used an oscilliscope, the lag is inconsistent and maximum of range 5-8 microseconds :-) Thank you.
â WedaPashi
May 14 at 7:35
add a comment |Â
Thanks for your inputs. "Add code to detect when Dispatcher() is starving. Did every task get serviced at least once every 0.1s say?", I'd say no, I have never made theDispatcher()
to starve. So far the most frequent event that it deals with is an ADC measurement, which is at every 50 msec, LED blinks at every 1 sec, so I'll test it for events lined up with a faster rate w.r.t to 100 msec as you discussed above. About latency, I check various events by toggling a bit, used an oscilliscope, the lag is inconsistent and maximum of range 5-8 microseconds :-) Thank you.
â WedaPashi
May 14 at 7:35
Thanks for your inputs. "Add code to detect when Dispatcher() is starving. Did every task get serviced at least once every 0.1s say?", I'd say no, I have never made the
Dispatcher()
to starve. So far the most frequent event that it deals with is an ADC measurement, which is at every 50 msec, LED blinks at every 1 sec, so I'll test it for events lined up with a faster rate w.r.t to 100 msec as you discussed above. About latency, I check various events by toggling a bit, used an oscilliscope, the lag is inconsistent and maximum of range 5-8 microseconds :-) Thank you.â WedaPashi
May 14 at 7:35
Thanks for your inputs. "Add code to detect when Dispatcher() is starving. Did every task get serviced at least once every 0.1s say?", I'd say no, I have never made the
Dispatcher()
to starve. So far the most frequent event that it deals with is an ADC measurement, which is at every 50 msec, LED blinks at every 1 sec, so I'll test it for events lined up with a faster rate w.r.t to 100 msec as you discussed above. About latency, I check various events by toggling a bit, used an oscilliscope, the lag is inconsistent and maximum of range 5-8 microseconds :-) Thank you.â WedaPashi
May 14 at 7:35
add a comment |Â
up vote
2
down vote
Why do you need the stTask_t
translation? Can't you directly store function pointers?
Why do you use typedef enum
? AFAIK in C enum names live in the same namespace as other types.
How do you pass parameters to event handlers? If you expect the handler to read some register in order to learn what is happening, I can see the following problem. In a long queue of events, later event handlers can read hardware registers after those have changed. I have seen this handled with something like struct params int id; union... ;
.
You have not shown the code for adding and removing events from the queue. There could be problems with reentrancy there.
This is minor nitpicking. Function names are supposed to be verbs.
Adding _Noreturn
specifier would make the dispatcher easier to read.
Hey, thanks for attempting it. I have update a section on question, which may make your this concern inapplicable "In a long queue of events, later event handlers can read hardware registers after those have changed.", and I am sorry about that.
â WedaPashi
May 11 at 10:49
Thank you for pointing out one hurdle that event handlers can not take arguments.
â WedaPashi
May 11 at 10:51
@WedaPashi suppose the interrupt event was more complex than a pin interrupt. For exampleADC conversion complete
. You want to immediately read the value (as you are doing) and pass it somehow to the handler.
â Vorac
May 11 at 10:55
Indeed, very valid. Right now the way this is handled is by a global structure that contains ADC measurements, and ADC ISR simply read the counts and load it into the global variables. The event is then added in the queue and the handler converts counts into an engineering value.
â WedaPashi
May 11 at 11:01
add a comment |Â
up vote
2
down vote
Why do you need the stTask_t
translation? Can't you directly store function pointers?
Why do you use typedef enum
? AFAIK in C enum names live in the same namespace as other types.
How do you pass parameters to event handlers? If you expect the handler to read some register in order to learn what is happening, I can see the following problem. In a long queue of events, later event handlers can read hardware registers after those have changed. I have seen this handled with something like struct params int id; union... ;
.
You have not shown the code for adding and removing events from the queue. There could be problems with reentrancy there.
This is minor nitpicking. Function names are supposed to be verbs.
Adding _Noreturn
specifier would make the dispatcher easier to read.
Hey, thanks for attempting it. I have update a section on question, which may make your this concern inapplicable "In a long queue of events, later event handlers can read hardware registers after those have changed.", and I am sorry about that.
â WedaPashi
May 11 at 10:49
Thank you for pointing out one hurdle that event handlers can not take arguments.
â WedaPashi
May 11 at 10:51
@WedaPashi suppose the interrupt event was more complex than a pin interrupt. For exampleADC conversion complete
. You want to immediately read the value (as you are doing) and pass it somehow to the handler.
â Vorac
May 11 at 10:55
Indeed, very valid. Right now the way this is handled is by a global structure that contains ADC measurements, and ADC ISR simply read the counts and load it into the global variables. The event is then added in the queue and the handler converts counts into an engineering value.
â WedaPashi
May 11 at 11:01
add a comment |Â
up vote
2
down vote
up vote
2
down vote
Why do you need the stTask_t
translation? Can't you directly store function pointers?
Why do you use typedef enum
? AFAIK in C enum names live in the same namespace as other types.
How do you pass parameters to event handlers? If you expect the handler to read some register in order to learn what is happening, I can see the following problem. In a long queue of events, later event handlers can read hardware registers after those have changed. I have seen this handled with something like struct params int id; union... ;
.
You have not shown the code for adding and removing events from the queue. There could be problems with reentrancy there.
This is minor nitpicking. Function names are supposed to be verbs.
Adding _Noreturn
specifier would make the dispatcher easier to read.
Why do you need the stTask_t
translation? Can't you directly store function pointers?
Why do you use typedef enum
? AFAIK in C enum names live in the same namespace as other types.
How do you pass parameters to event handlers? If you expect the handler to read some register in order to learn what is happening, I can see the following problem. In a long queue of events, later event handlers can read hardware registers after those have changed. I have seen this handled with something like struct params int id; union... ;
.
You have not shown the code for adding and removing events from the queue. There could be problems with reentrancy there.
This is minor nitpicking. Function names are supposed to be verbs.
Adding _Noreturn
specifier would make the dispatcher easier to read.
answered May 11 at 10:36
Vorac
25517
25517
Hey, thanks for attempting it. I have update a section on question, which may make your this concern inapplicable "In a long queue of events, later event handlers can read hardware registers after those have changed.", and I am sorry about that.
â WedaPashi
May 11 at 10:49
Thank you for pointing out one hurdle that event handlers can not take arguments.
â WedaPashi
May 11 at 10:51
@WedaPashi suppose the interrupt event was more complex than a pin interrupt. For exampleADC conversion complete
. You want to immediately read the value (as you are doing) and pass it somehow to the handler.
â Vorac
May 11 at 10:55
Indeed, very valid. Right now the way this is handled is by a global structure that contains ADC measurements, and ADC ISR simply read the counts and load it into the global variables. The event is then added in the queue and the handler converts counts into an engineering value.
â WedaPashi
May 11 at 11:01
add a comment |Â
Hey, thanks for attempting it. I have update a section on question, which may make your this concern inapplicable "In a long queue of events, later event handlers can read hardware registers after those have changed.", and I am sorry about that.
â WedaPashi
May 11 at 10:49
Thank you for pointing out one hurdle that event handlers can not take arguments.
â WedaPashi
May 11 at 10:51
@WedaPashi suppose the interrupt event was more complex than a pin interrupt. For exampleADC conversion complete
. You want to immediately read the value (as you are doing) and pass it somehow to the handler.
â Vorac
May 11 at 10:55
Indeed, very valid. Right now the way this is handled is by a global structure that contains ADC measurements, and ADC ISR simply read the counts and load it into the global variables. The event is then added in the queue and the handler converts counts into an engineering value.
â WedaPashi
May 11 at 11:01
Hey, thanks for attempting it. I have update a section on question, which may make your this concern inapplicable "In a long queue of events, later event handlers can read hardware registers after those have changed.", and I am sorry about that.
â WedaPashi
May 11 at 10:49
Hey, thanks for attempting it. I have update a section on question, which may make your this concern inapplicable "In a long queue of events, later event handlers can read hardware registers after those have changed.", and I am sorry about that.
â WedaPashi
May 11 at 10:49
Thank you for pointing out one hurdle that event handlers can not take arguments.
â WedaPashi
May 11 at 10:51
Thank you for pointing out one hurdle that event handlers can not take arguments.
â WedaPashi
May 11 at 10:51
@WedaPashi suppose the interrupt event was more complex than a pin interrupt. For example
ADC conversion complete
. You want to immediately read the value (as you are doing) and pass it somehow to the handler.â Vorac
May 11 at 10:55
@WedaPashi suppose the interrupt event was more complex than a pin interrupt. For example
ADC conversion complete
. You want to immediately read the value (as you are doing) and pass it somehow to the handler.â Vorac
May 11 at 10:55
Indeed, very valid. Right now the way this is handled is by a global structure that contains ADC measurements, and ADC ISR simply read the counts and load it into the global variables. The event is then added in the queue and the handler converts counts into an engineering value.
â WedaPashi
May 11 at 11:01
Indeed, very valid. Right now the way this is handled is by a global structure that contains ADC measurements, and ADC ISR simply read the counts and load it into the global variables. The event is then added in the queue and the handler converts counts into an engineering value.
â WedaPashi
May 11 at 11:01
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194186%2fsimple-dispatcher-for-an-embedded-system%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Blinking a LED seems like a trivial task to me.
â Reinderien
May 11 at 20:07
What's your microcontroller? This much pointer math and function indirection is usually a bad idea in the inside of an ISR. Have you measured the latency incurred?
â Reinderien
May 11 at 20:20
This ISR that I have shared is for a TI based MSP430 microcontroller. Note that no indirection is involved in tbe ISR. ISR only sets an event in queue. The indirection and invokation happens in Dispatcher function.
â WedaPashi
May 12 at 13:10