STM32WB Bluetooth® LE – Heart Rate Sensor project Migration to Azure RTOS ThreadX OS

Revision as of 07:49, 8 April 2022 by Registered User (→‎Required modifications)

1. Overview of Azure RTOS ThreadX OS

  • Picokernel, preemption-threshold, event-chaining unique features
  • Execution profiling and performance metrics
  • Totally available in source code (ANSI C and assembler)
  • Safety Certifications (TÜV, MISRA, UL)
  • Integrated with other Azure RTOS components:
    • USBX
    • NETX and NETXDUO
    • FILEX
    • LEVELX
    • TRACEX

Fully detailed description of ThreadX OS features and benefits can be found here: Microsoft AzureRTOS ThreadX Documentation

1.1. ThreadX code organization

File:ThreadXStructure.png
ThreadX folders organization

Main folders descriptions

  • cmake: original build system (not mandatory)
  • common: mcu architecture independent source code
  • common_modules: module feature (see: ThreadX Modules)
  • common_smp: Symmetric Multi Processing feature (see: ThreadX SMP)
  • docs: Just a dependency tree of Azure RTOS components
  • ports: mcu architecture dependent (M3, M4, M33,...)
  • ports_module: architecture dependent module code
  • ports_smp: architecture dependent smp code
  • samples: Microsoft example code (demo_threadx.c)
  • utility:
    • benchmarks: Thread-Metric test suite
    • execution_profile_kit: thread execution time tracker
    • low_power: Low power management files
    • rtos_compatibility_layers: adaptation layers (FreeRTOS, Posix, OSEK)








1.2. ThreadX way of working

A typical ThreadX application could be similar to this one (courtesy of Microsoft):

#include "tx_api.h"
unsigned long my_thread_counter = 0;
TX_THREAD my_thread;
main( )
{
    /* Enter the ThreadX kernel. */
    tx_kernel_enter( );
}
void tx_application_define(void *first_unused_memory)
{
    /* Create my_thread! */
    tx_thread_create(&my_thread, "My Thread",
    my_thread_entry, 0x1234, first_unused_memory, 1024,
    3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
}
void my_thread_entry(ULONG thread_input)
{
    /* Enter into a forever loop. */
    while(1)
    {
        /* Increment thread counter. */
        my_thread_counter++;
        /* Sleep for 1 tick. */
        tx_thread_sleep(1);
    }
}

tx_kernel_enter
This API is used to give the control to the OS, tx_application_define is called and the OS scheduler will be in charge to select the first thread (ready) to run.

tx_application_define
Here we have the creation of ThreadX resources (threads, semaphores, mutexes, events, queues,...). At least one thread must be created, other threads and resources could be created later. Please pay attention to how this function is called. Interrupts are disabled before (inside _tx_initialize_low_level) the call to this function and re-enabled just after (inside _tx_thread_schedule). So don't put any code relying on interrupt management inside this function.

1.3. ThreadX important files

  • tx_api.h: C header file containing all public definitions, data structures and service prototypes usable in the application.
    For a list of all available APIs, see: threadx APIs
  • tx_port.h: C header file containing all development-tool and target-specific data definitions and structures.
    For more details on target integration, see: target considerations
  • tx_user.h: Several configuration options can be set when building the ThreadX library and the application. These configurations cane be used to enable smallest code size, fastest execution, performance evaluation, error checking, quality coding rules and generally speaking extra features These options can be defined in the application source, on the command line or inside tx_user.h (in this latter case TX_INCLUDE_USER_DEFINE_FILE has to be defined). A "tx_user_sample.h" template is given for reference.
    For more info see: threadx configuration options
  • tx_initialize_low_level.S: Low-level processor initialization, including setting up interrupt-vectors, setting up a periodic timer interrupt source, saving the system stack pointer for use in ISR processing later and finding the first available RAM memory address for tx_application_define

Description of initialization process (between processor reset and the entry point of the thread scheduling loop) and the ThreadX way of working is fully detailed here: ThreadX way of working. Please have a look at this chapter before porting your application to ThreadX

2. ST Sequencer VS ThreadX

The sequencer executes registered functions one by one. It's able to :

  • Support up to 32 functions
  • Request a function to be executed
  • Pause / Resume the execution of a function
  • Wait for a specific event (might be not blocking)
  • Priority on functions

The sequencer is an optimized packaging of a while loop bare metal classic implementation and it doesn't intend to compete versus standard OS but versus standard bare metal implementation. It allows to avoid race conditions which are most of the time faced in bare metal implementation especially when low power mode are implemented.
For a detailed description of ST Sequencer, please have a look at : AN 5289, paragraph 4.4

2.1. List of Sequencer API used in Heart Rate project

void UTIL_SEQ_Run (UTIL_SEQ_bm_t mask_bm)

Requests the sequencer to execute functions that are pending and enabled in the mask mask_bm.

void UTIL_SEQ_RegTask(UTIL_SEQ_bm_t task_id_bm, uint32_t flags, void (*task)( void ))

Registers a function (task) associated with a signal (task_id_bm) in the sequencer. The task_id_bm must have a single bit set.

void UTIL_SEQ_SetTask( UTIL_SEQ_bm_t task_id_bm,* UTIL_SEQ_PauseTask)

Requests the function associated with the task_id_bm to be executed. The task_prio is evaluated by the sequencer only when a function has finished. If several functions are pending at any one time, the one with the highest priority (0) is executed.

void UTIL_SEQ_PauseTask(UTIL_SEQ_bm_t task_id_bm )

Disables the sequencer to execute the function associated with task_id_bm.

void UTIL_SEQ_ResumeTask(UTIL_SEQ_bm_t task_id_bm )

Enables the sequencer to execute the function associated with task_id_bm.

void UTIL_SEQ_SetEvt(UTIL_SEQ_bm_t evt_id_bm )

Notifies the sequencer that the event evt_id_bm occurred (the event must have been first requested).

void UTIL_SEQ_WaitEvt(UTIL_SEQ_bm_t evt_id_bm )

Requests the sequencer to wait for a specific event evt_id_bm and does not return until the event is set with UTIL_SEQ_SetEvt().

void UTIL_SEQ_EvtIdle(UTIL_SEQ_bm_t task_id_bm, UTIL_SEQ_bm_t evt_waited_bm)

Called while the sequencer is waiting for a specific event.

void UTIL_SEQ_Idle( void )

Function internally called by the Sequencer when there is nothing to execute(typically used to enter in Low Power )

2.2. API mapping between ST Sequencer and ThreadX

2.3. Idle Task and Low Power Management

3. Required modifications

3.1. ThreadX required files

3.2. ThreadX resources declaration

3.3. Synchronization mechanism replacement

3.4. ThreadX configuration

4. References