Source Code Generator for Embedded C Tasks.
The main goal is to automate the generation of Standard C source and header files with machine states for an application that runs on a bare-metal embedded software. The user only needs to define names for tasks, subtasks and states in a standard JSON file (as seen in the below example). The output is composed by source and header files with the application organized as a state machine structure ready to implement code.
With few changes, the structure can also be used with other type os applications, such as RTOS's (just skip the tick stuff).
The motivation is to save time avoiding the repetitive manual (and boring) effort of creating organized code. Also, the division into subtasks allow a bigger code to be created. Code with a single switch/case for all application states may be hard to maintain.
- What does this do: generate base code for tasks in Standard C
- How does this do it: the user fills a default JSON with information about the application (see
testdirectory)
python embctask.py [path_to_json]/config.jsonThe output directory will contain all generated code (source and headers).
- There will be generated:
- A pair of source and header with the same name of the main task.
- A single source file for each sub-task.
- A pair of source and header with the same name of the main task with the
privatesuffix. The idea is to protect the internals of the application so it cannot be accessed outside its own scope. It is a way of emulating the useprivateaccess modifier of C++.
Your main code just to:
- Include a single header of the generated code.
- Call one initialization function.
- Call the task executaion once in the main loop.
The generated code contain only the structure with definition of states. User is can use it freely to keep the application organized in an easy way to maintain.
The following is a JSON file used to configure embctask for generating code.
- The application is called
app_complex. Any name could be used since respecting C standards. - The application contain 3 sub-tasks:
init,runandresult. - Each sub-task has their own states.
{
"main_task": "app_complex",
"sub_tasks": [
{
"name": "init",
"states": [
"message",
"do_something_1",
"do_something_2",
"wait"
]
},
{
"name": "run",
"states": [
"read_sensor",
"serial_tx",
"serial_rx",
"do_something_1",
"do_something_2",
"do_something_3",
"do_something_4"
]
},
{
"name": "result",
"states": [
"success",
"error"
]
}
]
}To generate code, run the following command:
python embctask.py test/app_complex.jsonThe source files will be put in the output directory present where the command was executed. The source files can then be included in your C project.
To used it, please do as in the following C code snippet:
- Include the header file with the same name of the main task (
app_complex.hin this example) - Call
app_complex_init()before the main loop - Call
app_complex_task()once inside the main loop
#include <stdlib.h>
#include "app_complex/app_complex.h"
// place other includes here
int main() {
app_complex_init();
// place other initialization calls here
// main forever loop
while(1) {
// clear watchdog?
// call other tasks
app_complex_task();
}
}-
app_complex.c// generated by embctask v0.0.1 #include "main.h" #include "app_complex_private.h" void app_complex_init() { memset(&app_complex, 0, sizeof(app_complex)); /* place other initialization code here */ } void app_complex_main_task() { if (app_complex.flags.bits.timer_skip == false) { if (tick_compare(app_complex.timer) == 0) return; } app_complex.timer_last_value = app_complex.timer; switch(app_complex.state) { case APP_COMPLEX_STATE_INIT: { app_complex_sub_task_init(); break; } case APP_COMPLEX_STATE_RUN: { app_complex_sub_task_run(); break; } case APP_COMPLEX_STATE_RESULT: { app_complex_sub_task_result(); break; } } if (app_complex.timer == app_complex.timer_last_value) app_complex.flags.bits.timer_skip = true; else app_complex.flags.bits.timer_skip = false; }
-
app_complex.h// generated by embctask v0.0.1 #ifndef __APP_COMPLEX__ #define __APP_COMPLEX__ void app_complex_init(); void app_complex_main_task(); #endif
-
app_complex_private.c// generated by embctask v0.0.1 #include "main.h" #include "app_complex_private.h" app_complex_t app_complex; void app_complex_set_main_state(app_complex_state_t new_state) { app_complex.sub_state = 0; app_complex.state = new_state; }
-
app_complex_private.h// generated by embctask v0.0.1 #ifndef __APP_COMPLEX_PRIVATE__ #define __APP_COMPLEX_PRIVATE__ #include "tick.h" typedef enum { APP_COMPLEX_STATE_INIT = 0, APP_COMPLEX_STATE_RUN, APP_COMPLEX_STATE_RESULT, } app_complex_state_t; typedef struct { app_complex_state_t state; int sub_state; tick_t timer; tick_t timer_last_value; union { uint16_t val; struct { bool timer_skip:1; } bits; } flags; } app_complex_t; void app_complex_set_main_state(app_complex_state_t new_state); app_complex_sub_task_init(); app_complex_sub_task_run(); app_complex_sub_task_result(); extern app_complex_t app_complex; #endif
-
app_complex_init.c// generated by embctask v0.0.1 #include "main.h" #include "app_complex_private.h" typedef enum { APP_COMPLEX_SUB_STATE_INIT_MESSAGE = 0, APP_COMPLEX_SUB_STATE_INIT_DO_SOMETHING_1, APP_COMPLEX_SUB_STATE_INIT_DO_SOMETHING_2, APP_COMPLEX_SUB_STATE_INIT_WAIT, } app_complex_sub_state_init_t; void app_complex_sub_task_init() { switch ((app_complex_sub_state_init_t) app_complex.sub_state) { case APP_COMPLEX_SUB_STATE_INIT_MESSAGE: { break; } case APP_COMPLEX_SUB_STATE_INIT_DO_SOMETHING_1: { break; } case APP_COMPLEX_SUB_STATE_INIT_DO_SOMETHING_2: { break; } case APP_COMPLEX_SUB_STATE_INIT_WAIT: { break; } } }
-
app_complex_run.c// generated by embctask v0.0.1 #include "main.h" #include "app_complex_private.h" typedef enum { APP_COMPLEX_SUB_STATE_RUN_READ_SENSOR = 0, APP_COMPLEX_SUB_STATE_RUN_SERIAL_TX, APP_COMPLEX_SUB_STATE_RUN_SERIAL_RX, APP_COMPLEX_SUB_STATE_RUN_DO_SOMETHING_1, APP_COMPLEX_SUB_STATE_RUN_DO_SOMETHING_2, APP_COMPLEX_SUB_STATE_RUN_DO_SOMETHING_3, APP_COMPLEX_SUB_STATE_RUN_DO_SOMETHING_4, } app_complex_sub_state_run_t; void app_complex_sub_task_run() { switch ((app_complex_sub_state_run_t) app_complex.sub_state) { case APP_COMPLEX_SUB_STATE_RUN_READ_SENSOR: { break; } case APP_COMPLEX_SUB_STATE_RUN_SERIAL_TX: { break; } case APP_COMPLEX_SUB_STATE_RUN_SERIAL_RX: { break; } case APP_COMPLEX_SUB_STATE_RUN_DO_SOMETHING_1: { break; } case APP_COMPLEX_SUB_STATE_RUN_DO_SOMETHING_2: { break; } case APP_COMPLEX_SUB_STATE_RUN_DO_SOMETHING_3: { break; } case APP_COMPLEX_SUB_STATE_RUN_DO_SOMETHING_4: { break; } } }
-
app_complex_result.c// generated by embctask v0.0.1 #include "main.h" #include "app_complex_private.h" typedef enum { APP_COMPLEX_SUB_STATE_RESULT_SUCCESS = 0, APP_COMPLEX_SUB_STATE_RESULT_ERROR, } app_complex_sub_state_result_t; void app_complex_sub_task_result() { switch ((app_complex_sub_state_result_t) app_complex.sub_state) { case APP_COMPLEX_SUB_STATE_RESULT_SUCCESS: { break; } case APP_COMPLEX_SUB_STATE_RESULT_ERROR: { break; } } }