Skip to main content

CMSIS

CMSIS (Common Microcontroller Software Interface Standard) is Arm's vendor- neutral layer for Cortex-M devices. The parts that come up most often:

  • CMSIS-Core — register definitions, intrinsics such as __DSB() and __ISB(), and cache and MPU helpers. See the STM32 initialization page for worked examples.
  • CMSIS-RTOS2 — a common API over an RTOS kernel, usually FreeRTOS or RTX.
  • CMSIS-DSP — optimised fixed- and floating-point signal processing.

CMSIS-RTOS2

Scheduler control

int32_t osKernelLock(void); // suspend the scheduler
int32_t osKernelUnlock(void); // resume the scheduler
int32_t osKernelRestoreLock(int32_t lock); // restore a previously saved state

osKernelLock stops the scheduler from switching threads and returns the previous lock state: 1 if it was already locked, 0 if it was not, or a negative osError* code on failure. osKernelUnlock returns the same information.

Nesting is the reason osKernelRestoreLock exists. Save what osKernelLock returned and restore it, rather than calling osKernelUnlock unconditionally — otherwise an inner critical section re-enables the scheduler while an outer one still expects it to be locked:

int32_t lock = osKernelLock();
/* critical section — no thread switches happen here */
osKernelRestoreLock(lock);

Keep these sections short. The scheduler is suspended, so a higher-priority thread that becomes ready has to wait, which shows up directly as jitter in real-time behaviour.

Neither function may be called from an interrupt handler; both return osErrorISR if you try. Use osKernelGetState() to check whether the kernel is running before calling into it during start-up.

Kernel states

StateMeaning
osKernelInactiveBefore osKernelInitialize()
osKernelReadyInitialised, not yet started
osKernelRunningosKernelStart() has been called; threads are executing
osKernelLockedRunning but the scheduler is suspended
osKernelSuspendedTickless low-power mode
osKernelErrorUnrecoverable error

Typical start-up

int main(void) {
HAL_Init();
SystemClock_Config();

osKernelInitialize();
osThreadNew(app_main, NULL, &app_main_attr);
osKernelStart(); // does not return once the kernel is running

for (;;) { } // reached only if osKernelStart() failed
}

osKernelStart() never returns on success, so anything placed after it — final peripheral setup in particular — never executes. Move that work into a thread.

Delays

osDelay(100); // block this thread for 100 ticks
osDelayUntil(osKernelGetTickCount() + 100); // absolute deadline, no drift

osDelay is relative and accumulates drift across iterations because the execution time of the loop body adds to each period. Use osDelayUntil for periodic tasks.

The tick is not necessarily 1 ms. osKernelGetTickFreq() returns the actual frequency; convert with osKernelGetTickFreq() * ms / 1000U rather than assuming.