Lightweight Modular Software Architecture for Embedded Systems

Follow+Star public account, don’t miss wonderful content

Lightweight Modular Software Architecture for Embedded Systems

Source | Gitee

Compilation | Embedded Mix
There is no such position as a software architect in embedded systems, but that does not mean embedded development does not require software architecture. A good software framework can improve coding efficiency and make the code more robust, thereby reducing maintenance costs.
Today, I would like to share a lightweight framework designed specifically for embedded systems: mr-library.

Introduction to mr-library

mr-library is a lightweight framework for embedded systems that provides a unified low-level driver device model and basic service functions, characterized by modular design, configurability, and extensibility, helping developers quickly build embedded applications.

mr-library framework supports basic kernel functions such as mutexes and object management. It integrates asynchronous event-driven framework (event), multi-timer software timers (soft-timer), and other services. It provides driver device models for common peripherals such as serial ports, SPI, I2C, ADC/DAC, and accesses low-level hardware devices through a unified driver interface (open, close, ioctl, read, write), decoupling the low-level driver from the application.

Application Scenarios

  • Low-level driver programs developed for MCU.
  • External framework for RTOS real-time operating systems (used as a driver device framework).
  • Rapid development of various IoT and smart hardware products.

Driver Device Framework

Developers can access peripherals in an object-oriented manner, simplifying the development process of driver logic. The framework implements a general driver template for commonly used peripherals, allowing developers to quickly port it to different hardware platforms.

The driver device framework supports a general interface for ordinary devices, automatic bus control for bus devices, and interrupt takeover for various devices.

Driver Device Interface

The device driver framework provides a unified operational interface, and all operations on the device need to be implemented through the following interfaces:

Interface Description
mr_device_add Add device
mr_device_find Find device
mr_device_open Open device
mr_device_close Close device
mr_device_ioctl Control device
mr_device_read Read data from device
mr_device_write Write data to device

Example of Using SPI Device:

/* Define SPI device */
#define SPI_DEVICE0_CS_PIN              10
#define SPI_DEVICE1_CS_PIN              20
struct mr_spi_device spi_device0, spi_device1;

/* Add SPI device */
mr_spi_device_add(&spi_device0, "spi10", SPI_DEVICE0_CS_PIN);
mr_spi_device_add(&spi_device1, "spi11", SPI_DEVICE1_CS_PIN);

/* Find SPI device */
mr_device_t spi0_device = mr_device_find("spi10");
mr_device_t spi1_device = mr_device_find("spi11");

/* Mount bus */
mr_device_ioctl(spi0_device, MR_CTRL_ATTACH, "spi1");
mr_device_ioctl(spi1_device, MR_CTRL_ATTACH, "spi1");

/* Open SPI device in read and write mode */
mr_device_open(spi0_device, MR_OPEN_RDWR);
mr_device_open(spi1_device, MR_OPEN_RDWR);

/* Send data */
char buffer0[] = "hello";
char buffer1[] = "world";
mr_device_write(spi0_device, 0, buffer0, sizeof(buffer0) - 1);
mr_device_write(spi1_device, 0, buffer1, sizeof(buffer1) - 1);

/* Read data */
mr_device_read(spi0_device, 0, buffer0, sizeof(buffer0) - 1);
mr_device_read(spi1_device, 0, buffer1, sizeof(buffer1) - 1);

/* Close device */
mr_device_close(spi0_device);
mr_device_close(spi1_device);

Service Framework

mr-library framework integrates a lightweight service framework for building application services in embedded development, supporting asynchronous event listening, multi-timer software timers, etc. The service framework decouples different applications at the application layer, achieving modularization, cuttable, clear business logic, rapid development, and high code reuse.

Event Service

The event service is an asynchronous event handling mechanism that can effectively improve the system’s asynchronous processing capability, decoupling, and scalability through event dispatching and callbacks.

The event service consists of two parts: event server and event.

  • The event server is used to receive and dispatch events, maintaining an event queue for storing pending events and an event list for storing created events.
  • Events need to be created on the event server and provide a callback function.

When an event occurs, the event server will insert the event into the event queue for caching. The event server periodically takes events from the event queue for dispatching and finds the corresponding event callback for event processing.

Event Service Operation Interface

Interface Description
mr_event_server_find Find event server
mr_event_server_add Add event server
mr_event_server_remove Remove event server
mr_event_server_handle Event server processing
mr_event_create Create event
mr_event_delete Remove event
mr_event_notify Notify event occurrence
mr_event_trigger Trigger event

Example of Using Event Service:

/* Define event */
#define EVENT1                          1
#define EVENT2                          2
#define EVENT3                          3

/* Define event server */
struct mr_event_server event_server;

mr_err_t event1_cb(mr_event_server_t server, void *args)
{
    printf("event1_cb\r\n");
    /* Notify event server that event2 has occurred */
    mr_event_notify(EVENT2, server);
    return MR_ERR_OK;
}

mr_err_t event2_cb(mr_event_server_t server, void *args)
{
    printf("event2_cb\r\n");
    /* Notify event server that event3 has occurred */
    mr_event_notify(EVENT3, server);
    return MR_ERR_OK;
}

mr_err_t event3_cb(mr_event_server_t server, void *args)
{
    printf("event3_cb\r\n");
    return MR_ERR_OK;
}

int main(void)
{
    /* Add event server to kernel container */
    mr_event_server_add(&event_server, "server", 4);
    
    /* Create events on server */
    mr_event_create(EVENT1, event1_cb, MR_NULL, &event_server);
    mr_event_create(EVENT2, event2_cb, MR_NULL, &event_server);
    mr_event_create(EVENT3, event3_cb, MR_NULL, &event_server);
    
    /* Notify event server that event1 has occurred */
    mr_event_notify(EVENT1, &event_server);
    
    while (1)
    {
        /* Event server processing */
        mr_event_server_handle(&event_server);
    }
}

Phenomenon:

event1_cb
event2_cb
event3_cb

Software Timer Service

The software timer is a mechanism that implements timing functions at the software level. Through the software timer, specific events can be triggered at specific time points or intervals. Software timers are commonly used to implement periodic tasks, timeout handling, timer interrupts, and other functions.

The software timer consists of two main components: timer server and timer.

  • The timer server is used for time management and timer handling.
  • The timer is used to handle specific timeout processing, needing to be registered with the timer server and provide a callback function.

Software Timer Service Operation Interface

Interface Description
mr_soft_timer_server_find Find timer server
mr_soft_timer_server_add Add timer server
mr_soft_timer_server_remove Remove timer server
mr_soft_timer_server_update Update timer server’s time base signal
mr_soft_timer_server_handle Handle timer server
mr_soft_timer_add Add timer
mr_soft_timer_remove Remove timer
mr_soft_timer_start Start timer
mr_soft_timer_stop Pause timer
mr_soft_timer_add_then_start Add timer and start

Example of Using Software Timer Service:

/* Define timer server and timers */
struct mr_soft_timer_server server;
struct mr_soft_timer timer1, timer2, timer3;

mr_err_t timer1_callback(mr_soft_timer timer, void *args)
{
    printf("timer1_callback\r\n");
    return MR_ERR_OK;
}

mr_err_t timer2_callback(mr_soft_timer timer, void *args)
{
    printf("timer2_callback\r\n");
    return MR_ERR_OK;
}

mr_err_t timer3_callback(mr_soft_timer timer, void *args)
{
    printf("timer3_callback\r\n");
    mr_soft_timer_stop(timer);
    return MR_ERR_OK;
}

int main(void)
{
    /* Add timer server */
    mr_soft_timer_server_add(&server, "soft-timer");

    /* Add timers and start */
    mr_soft_timer_add_then_start(&timer1, 5, timer1_callback, MR_NULL, &server);
    mr_soft_timer_add_then_start(&timer2, 10, timer2_callback, MR_NULL, &server);
    mr_soft_timer_add_then_start(&timer3, 15, timer3_callback, MR_NULL, &server);

    while (1)
    {
        /* Update timer server clock */
        mr_soft_timer_server_update(&server, 1);
        
        /* Timer server processing (where it is placed, the callback will be called there) */
        mr_soft_timer_server_handle(&server);
    }
}

Code Directory

mr-library code directory structure is shown in the table below:

Name Description
bsp Board Support Package
device Device Files
document Documentation
driver Driver Files
include Library Header Files
module Components
package Software Packages
src Library Source Files
  • Kernel Layer: The core part of mr-library, implementing object management, device control, service interfaces, etc.
  • Device Layer: Provides a unified device interface to connect devices to the kernel.
  • Driver Layer: Provides low-level hardware drivers for devices, requiring only modifications to the driver layer when hardware changes.
  • Component Layer: Implements different functions through the APIs provided by the framework. Includes but is not limited to virtual file systems, general sensor modules, network frameworks, etc.
  • Software Package: Can be used independently, a software package without dependencies.

Open Source Address:https://gitee.com/MacRsh/mr-library

Statement: This article’s materials are sourced from the internet, and the copyright belongs to the original author. If there are copyright issues, please contact me to delete.
———— END ————
Lightweight Modular Software Architecture for Embedded Systems
● Column “Embedded Tools”
● Column “Embedded Development”
● Column “Keil Tutorial”
● Selected Tutorials in Embedded Column
Follow the public account reply “Join Group” to join the technical exchange group according to the rules, reply “1024” to see more content.
Lightweight Modular Software Architecture for Embedded Systems
Lightweight Modular Software Architecture for Embedded Systems
Click “Read Original” to see more shares.

Leave a Comment

×