Panel For Example Panel For Example Panel For Example

Open-source Framework Zorb for Bare-Metal Microcontrollers

Author : Adrian September 18, 2025

Introduction

Many times, when developing microcontroller projects, performance and memory constraints prevent running large general-purpose frameworks. Lightweight software frameworks become especially important. This article introduces an open-source software framework suitable for bare-metal microcontrollers: Zorb.

Zorb Overview

The Zorb Framework is a lightweight embedded framework built with object-oriented design principles.

Zorb was created to enable rapid application development on chips that cannot run Linux, avoiding repeated reimplementation of common functions.

Initial Design Features

  1. Time system: zf_time
  2. Ring buffer: zf_buffer
  3. List utilities: zf_list
  4. State machine: zf_fsm
  5. Event system: zf_event
  6. Timer: zf_timer
  7. Task: zf_task

The first six features enable a purely event-driven program and can generally meet the needs of small to medium embedded applications. The task feature is added to meet higher real-time requirements in some applications.

Development Environment

An STM32 F429 development board is used as the hardware platform. The hardware resources used are serial port 1 (USART1) and SysTick; serial port 1 provides debug printing, and SysTick provides system time counting.

Hardware environment

Hardware setup is not described in detail here; refer to the development board examples. Board-level initialization completes the debug serial port and SysTick initialization.

/******************************************************************************
 * Description : Hardware environment initialization
 * Parameters  : none
 * Return      : none
******************************************************************************/
void BSP_init(void)
{
    /* Nested Vectored Interrupt Controller priority group selection */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    /* Initialize debug serial port */
    Debug_USART_init();
    /* Initialize SysTick */
    SystemTick_init();
}

Debugging

When developing a program, the first and most important step is setting up the debug environment. We use serial port 1 as debug output (printf mapping). Debug messages are divided into three levels; a host tool can later highlight messages by level.

/******************************************************************************
 * Description : Debug output header file
 * Parameters  : none
 * Return      : none
******************************************************************************/
#ifndef __ZF_DEBUG_H__
#define __ZF_DEBUG_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "stdio.h"
#include "stdbool.h"
#define LOG_D 0; /* Log level: normal */
#define LOG_W 1; /* Log level: warning */
#define LOG_E 2; /* Log level: error */
#define _ZF_DEBUG             /* Define debug feature */
#define ZF_DEBUG_ON true      /* Enable debug feature */
#ifdef _ZF_DEBUG
    #if ZF_DEBUG_ON
        #define ZF_DEBUG(rank, x...) do
        {
            char code[10] = "[rank=0]";
            code[6] = '0' + (char)rank;
            if (code[6] != '0')
            {
                printf("%s", code);
            }
            printf(x);
        } while(0)
    #else
        #define ZF_DEBUG(rank, x...)
    #endif /* ZF_DEBUG_ON */
#endif /* _ZF_DEBUG */
#ifdef __cplusplus
}
#endif
#endif /* __ZF_DEBUG_H__ */

Assertions

During development, placing assertions at key locations helps locate bugs.

/******************************************************************************
 * Description : Assertion header file
 * Parameters  : none
 * Return      : none
******************************************************************************/
#ifndef __ZF_ASSERT_H__
#define __ZF_ASSERT_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"
#define _ZF_ASSERT              /* Define assertion feature */
#define ZF_ASSERT_ON true       /* Enable assertions */
#ifdef _ZF_ASSERT
    #if ZF_ASSERT_ON
         #define ZF_ASSERT(expression_) ((expression_) ?
            (void)0 : ZF_assertHandle((uint8_t*)__FILE__, (int)__LINE__));
    #else
         #define ZF_ASSERT(expression_)
    #endif /* ZF_ASSERT_ON */
#endif /* _ZF_ASSERT */
/* Handler when an assertion occurs */
void ZF_assertHandle(uint8_t *pFileName, int line);
#ifdef __cplusplus
}
#endif
#endif /* __ZF_ASSERT_H__ */

The assertion handler is simple: it reports the file and line where the assertion failed. Implementation follows.

/******************************************************************************
 * Description : Assertion implementation
 * Parameters  : none
 * Return      : none
******************************************************************************/
#include "zf_assert.h"
#include "zf_debug.h"
/*****************************************************************************/
void ZF_assertHandle(uint8_t *pFileName, int line)
{
    ZF_DEBUG(LOG_E, "file:%s line:%d:asserted\n", pFileName, line);
    while (1);
}

Scheduling Timing

To reduce resource consumption, the framework's base timing period is initially set to 1 ms. Therefore, SysTick is configured with a 1 ms period, and each interrupt increments the framework's timing counter.

/******************************************************************************
 * Description : SysTick interrupt service routine
 * Parameters  : none
 * Return      : none
******************************************************************************/
void SysTick_Handler(void)
{
    /* Provide timing for Zorb framework */
    ZF_timeTick();
}

The current time system provides only basic functions: system tick counting and busy-wait delays. The time system will be extended when implementing timer and task features.

Conclusion

This article implements basic functionality that forms the foundation of the framework. All future extensions will be developed on this environment.

Setting up a good debug output environment helps quickly locate bugs and improve development efficiency.