I have a bare metal system based on a 16 bit microcontroller PIC18. The system receives input via 2 Analog inputs, 2 Digital Inputs and via CAN and then runs an algorithm based on these inputs. I have to transmit data via CAN every 10 millisecond based on the analog and digital inputs received, do switch debouncing on received Digital input and turn off or on digital outputs for timed interval of say x seconds while receiving data in CAN and based on the Analog/ digital inputs received. I am able to send and receive data via CAN and able to program the inputs as well as outputs in a while(1) loop I am not sure on how to architect this system keeping the timing constraints. I am using C and have no OS or scheduler for this.
I need to use some kind of timer but not sure on how to create state machines or tasks.
You can go with a very simple scheduler based on a hardware timer. You setup your timer to give you an interrupt every 1 or 10 ms. You can poll the interrupt flag in the while(1)
loop of your main()
, or use a volatile bool is_timer_elapsed
flag as @Lundin suggested.
Every module (CAN, Analog inputs, Digital inputs) has its own tick
function in which it executes its business logic, for example the debouncing. The trick here is to make these functions non-blocking. State machines are very good at it.
When you detect that the hardware timer elapsed, you can call these tick
functions one after the other. After these functions were executed, check that your timer has not elapsed again. If this is the case, you have to do less things in a tick
cycle of your modules, e.g. add an intermediary state to break down a long operation.
Some hints:
void can_tick(void){
switch(can_state){...}
}
// Digital input: detect button press without debouncing
void digital_in_tick(void){
unsigned int duration_counter = 0U;
switch (button_state){
case NOT_PRESSED:
if (gpio_is_set(BUTTON_PIN)){
button_state = PRESSED;
}
break;
case PRESSED:
if (gpio_is_set(BUTTON_PIN)){
duration_counter++;
} else {
handle_button_pressed(duration_counter); // This could be writing a flag into a queue, or toggling a pin, etc.
button_state = NOT_PRESSED;
duration_counter = 0;
}
break;
default: break;
}
}
...
int main(void){
// timer init, etc
while(1){
if (is_timer_elapsed) {
is_timer_elapsed = false;
can_tick();
digital_input_tick();
...
if (is_timer_elapsed){
// This should not happen!
// Rework your tick functions (or reset)
}
}
}