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
| State | Meaning |
|---|---|
osKernelInactive | Before osKernelInitialize() |
osKernelReady | Initialised, not yet started |
osKernelRunning | osKernelStart() has been called; threads are executing |
osKernelLocked | Running but the scheduler is suspended |
osKernelSuspended | Tickless low-power mode |
osKernelError | Unrecoverable 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.