Skip to content

RTOS Integration

The integration of itemis CREATE with RTOS (Real-Time Operating System) like Zephyr, FreeRTOS, ThreadX (Azure RTOS), or others allows embedded developers to manage state transitions based on real-time events and timer-driven tasks efficiently. By leveraging RTOS tasks or threads, itemis CREATE’s generated code can seamlessly respond to system events and timing requirements.
Note: In the following threads will be used, but it can also be replaced with tasks.

Threads and Queue Flow

RTOS message queues serve as the bridge between RTOS threads and the state machine, ensuring that events and timer updates are processed efficiently without blocking other system operations. Every participant uses the in event queue to store incoming events that will be raised in the state machine. Outgoing events are pushed into a out event queue, which allows the client to process them spearately. The flow is shown in the image below:

RTOS Queues

RTOS Queues

The image shows a flowchart representing the integration of RTOS elements with itemis CREATE’s state machine. Here’s a description of each component:

  • Timer (blue circle): A thread that periodically generates a time event and sends them to the input queue.
  • Input (another blue circle): Represents a thread that handles external inputs and also sent the input events to the input queue. An input could be an asynchronous event like a button click.
  • Input Queue: This queue collects events from both the timer and input thread.
  • State Machine (green circle): The core processing thread, which dispatches the events from the input queue and raising events in the generated state machine code based on the input events.
  • Output Queue: Collects output events or actions from the state machine and forwards them to the output thread.
  • Output (yellow circle): Represents the thread of processed outputs from the output queue. It handles actions that interact with other systems or provide feedback.

This setup illustrates a structured event-driven architecture where inputs are queued, processed by a state machine, and then sent to an output queue.

Example Code

The Example Code section provides a pseudo-implementation of an RTOS setup that integrates with a state machine. Please refer to the linked examples for concrete implementations for freeRTOS and Zephyr. This setup includes separate threads for managing timers, inputs, outputs and the state machine itself. Here’s a breakdown of each thread’s purpose:

Timer Thread

The Timer Thread runs every 100 ms, using a defined interval (TIMER_THREAD_TIME_MS). Within its callback function, it sends a timer event (updateTimerServiceID) to the input queue, which is then processed by the state machine to keep track of timed actions.

#define TIMER_THREAD_TIME_MS 100 // every 100 ms

TimerThread timerThread(timer_thread_callback, TIMER_THREAD_TIME_MS);

void timer_thread_callback()
{
	int event_id = updateTimerServiceID;
	queue_push(&inEventQueue, event_id);
}

Input Thread

The Input Thread continuously monitors for specific events (e.g., event1 and event2). When an event occurs, it pushes the corresponding event ID into the input queue (inEventQueue). This allows the state machine to handle various external inputs as they occur.

Thread input_thread(input_thread_callback);

void input_thread_callback() {
	while (1) {
		if (event1) {
			int event_id = inEvent1ID;
			queue_push(&inEventQueue, eventId);
		}
		if (event2) {
			int event_id = inEvent2ID;
			queue_push(&inEventQueue, eventId);
		}
	}
}

State Machine Thread

The State Machine Thread handles events from the input queue. It retrieves events using queue_pop, and based on the event ID, it either updates the timer service or raises specific events (inEvent1 or inEvent2) in the state machine. This thread allows the state machine to process events sequentially, making it responsive to both time-based and input-triggered events.

Thread statemachine_thread(statemachine_thread_callback, statemachine);

void statemachine_thread_callback(void *arg)
{
	StateMachine *statemachine = (StateMachine *)arg;
	while (1)
	{
		int receivedInEventID = queue_pop(&inEventQueue);
		if (receivedInEventID == updateTimerServiceID)
		{
			sc_timer_service_proceed(&timer_service, TIMER_THREAD_TIME_MS);
		}
		else if (receivedInEventID == inEvent1ID)
		{
			statemachine_raise_inEvent1(&statemachine);
		}
		else if (receivedInEventID == inEvent2ID)
		{
			statemachine_raise_inEvent2(&statemachine);
		}
		yield(); // or sleep
	}
}

Output Thread

The Output Thread handles events generated by the state machine and stored in the output queue (outEventQueue). This thread listens for outgoing events and takes appropriate actions, such as interacting with actuators.

  • Event Handlers (on_outEvent1 and on_outEvent2): These functions are called when the state machine raises outEvent1 or outEvent2. Each function pushes the corresponding event ID into the output queue for processing by the output thread.
  • Subscription Setup (subscribe_observers): Observers are created and subscribed to the state machine’s output events (outEvent1 and outEvent2). When these events are raised, the observers call the corresponding handler function, which adds the event to the output queue.
  • Output Thread Callback (output_thread_callback): This callback function continuously listens for events in the output queue. When it detects an event, it processes it by performing an action, like controlling an actuator. For example, outEvent1ID might turn on an actuator, while outEvent2ID might turn one off.

This setup allows the system to handle output events asynchronously, ensuring that actions triggered by the state machine’s outputs are executed independently.

void on_outEvent1(StateMachine* handle) {
	int event_id = outEvent1ID;
	queue_push(&outEventQueue, eventId);
}

void on_outEvent2(StateMachine* handle, sc_integer value) {
	int event_id = outEvent2ID;
	queue_push(&outEventQueue, eventId);
}

void subscribe_observers(StateMachine *handle, sc_single_subscription_observer *outEvent1Observer, sc_single_subscription_observer_sc_integer *outEvent2Observer) {
	sc_single_subscription_observer_init(outEvent1Observer, handle, (sc_observer_next_fp) on_outEvent1);
	sc_single_subscription_observer_subscribe(outEvent1Observer, &handle->iface.outEvent1);

	sc_single_subscription_observer_sc_integer_init(outEvent2Observer, handle, (sc_observer_next_fp) on_outEvent2);
	sc_single_subscription_observer_sc_integer_subscribe(outEvent2Observer, &handle->iface.outEvent2);
}

Thread output_thread(output_thread_callback);

void output_thread_callback() {
	while (1)
	{
		int receivedOutEventID = queue_pop(&inEventQueue);
		if (receivedOutEventID == outEvent1ID) {
			// handle outEvent1, e.g. turn on an actuator
		} else if (receivedOutEventID == outEvent2ID) {
			// handle outEvent2, e.g. turn off an actuator
		}
	}
}

Zephyr and FreeRTOS Integration

To demonstrate integrating itemis CREATE’s state machine with an RTOS, there are two examples: one for Zephyr and one for FreeRTOS. These examples show how to set up a complete RTOS-based application with event-driven state machine handling.

The provided examples include a main function that wraps all components together. This main function configures the necessary threads for timer, input, state machine, and output handling. Each thread is assigned specific tasks to manage incoming and outgoing events in real-time, using message queues for efficient communication.

Here’s a brief breakdown of the main components in each example:

  • Input Handling:
    • The input events are simulated using the keyboard, allowing users to trigger specific state machine events manually.
    • The system listens for keypresses, converting them into event IDs and placing them in the input queue for the state machine to process. This setup provides a flexible way to test event handling without specialized hardware.
  • State Machine Processing:
    • The state machine processes events from the input queue and triggers corresponding actions or output events based on the defined state transitions.
    • Timer-based events are also periodically sent to the state machine, allowing it to handle both asynchronous inputs and scheduled tasks.
  • Output Handling:
    • Output events generated by the state machine are pushed into the output queue and displayed on the console.
    • This provides real-time feedback on the system’s state and actions, helping users understand how outputs can be used to drive hardware actions like turning actuators on or off.
  • Platform Compatibility:
    • These examples are designed to run on Linux, macOS, and Windows, making it easy to compile and test on standard operating systems.
    • The cross-platform nature of these examples allows developers to simulate an RTOS environment on their development machine before deploying to embedded hardware.
  • Portability:
    • With the provided structure, users can easily adapt the example to their specific target hardware. The use of threads, queues, and event-driven logic aligns well with typical embedded systems, making this code a practical reference for real-world implementations.
    • By understanding the threading and queue mechanisms demonstrated, users can extend this approach to integrate with various sensors, actuators, and other peripherals in a real-time system.

These examples offer a clear overview of RTOS integration with itemis CREATE, making it straightforward for developers to implement similar architectures on their own targets. The code structure provides a robust foundation for creating responsive, event-driven applications in both Zephyr and FreeRTOS environments.

Add them to itemis CREATE with the example wizard:
File -> New -> Example... -> itemis CREATE Statechart Examples -> Embedded Systems Integration Guide -> Zehpyr/FreeRTOS – RTOS Integration ©
h1{ EARLY_SEPARATOR}.