This article mainly shares an open-source management module that helps achieve layered management of applications and hardware in an MCU project.
Nowadays, project requirements are more complex, which naturally imposes stricter demands than before.
From a technical perspective, I still recommend that everyone should maintain a layered structure (application and driver) in their projects. This not only facilitates maintenance and portability but is also crucial for enhancing one’s technical skills.
Today, I will share with you an open-source, lightweight management module: cola_os
Open-source address:
https://gitee.com/schuck/cola_os
1►
Overview
Taking STM32 as an example, when you open the example programs downloaded from the internet or those that come with the development board, you will find that the application layer contains files like stm32f10x.h or stm32f10x_gpio.h. These files strictly belong to the hardware layer, and having them in the software layer can make things messy.
If you have used Linux, you surely know that the Linux system cannot directly operate on the hardware layer. When you open the Linux or RT-Thread code, you will find that there are source files for devices, which indeed represent the driver layer.

2►
Implementation Principle
The principle is to place all hardware operation interfaces onto the driver linked list and implement operations like device open, read, write, etc., at the driver layer. Of course, there are drawbacks to this approach, such as needing to traverse the driver linked list when finding drivers, which can increase code execution time.
3►
Code Implementation
As per international conventions, we start by writing the header file. In RT-Thread, a doubly linked list is used, but for simplicity, I will only use a singly linked list here. Those interested can study RT-Thread on their own.
Header file interface:
This time, only the following interfaces are implemented; you can explore the remaining interfaces like device_open and device_close on your own. This allows you to implement the following interfaces in the application layer:
/* Driver registration*/int cola_device_register(cola_device_t *dev);/* Driver lookup*/cola_device_t *cola_device_find(const char *name);/* Driver read*/int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size);/* Driver write*/int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size);/* Driver control*/int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg);;
Header file cola_device.h:
#ifndef _COLA_DEVICE_H_#define _COLA_DEVICE_H_ enum LED_state{ LED_OFF, LED_ON, LED_TOGGLE, }; typedef struct cola_device cola_device_t; struct cola_device_ops{ int (*init) (cola_device_t *dev); int (*open) (cola_device_t *dev, int oflag); int (*close) (cola_device_t *dev); int (*read) (cola_device_t *dev, int pos, void *buffer, int size); int (*write) (cola_device_t *dev, int pos, const void *buffer, int size); int (*control)(cola_device_t *dev, int cmd, void *args); }; struct cola_device{ const char * name; struct cola_device_ops *dops; struct cola_device *next;}; /* Driver registration*/int cola_device_register(cola_device_t *dev);/* Driver lookup*/cola_device_t *cola_device_find(const char *name);/* Driver read*/int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size);/* Driver write*/int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size);/* Driver control*/int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg); #endif
Source file cola_device.c:
#include "cola_device.h"#include <string.h>#include <stdbool.h> struct cola_device *device_list = NULL; /* Check if task exists*/static bool cola_device_is_exists( cola_device_t *dev ){ cola_device_t* cur = device_list; while( cur != NULL ) { if( strcmp(cur->name,dev->name)==0) { return true; } cur = cur->next; } return false;} static int device_list_inster(cola_device_t *dev){ cola_device_t *cur = device_list; if(NULL == device_list) { device_list = dev; dev->next = NULL; } else { while(NULL != cur->next) { cur = cur->next; } cur->next = dev; dev->next = NULL; } return 1;} /* Driver registration*/int cola_device_register(cola_device_t *dev){ if((NULL == dev) || (cola_device_is_exists(dev))) { return 0; } if((NULL == dev->name) || (NULL == dev->dops)) { return 0; } return device_list_inster(dev); }/* Driver lookup*/cola_device_t *cola_device_find(const char *name){ cola_device_t* cur = device_list; while( cur != NULL ) { if( strcmp(cur->name,name)==0) { return cur; } cur = cur->next; } return NULL;}/* Driver read*/int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size){ if(dev) { if(dev->dops->read) { return dev->dops->read(dev, pos, buffer, size); } } return 0;}/* Driver write*/int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size){ if(dev) { if(dev->dops->write) { return dev->dops->write(dev, pos, buffer, size); } } return 0;}/* Driver control*/int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg){ if(dev) { if(dev->dops->control) { return dev->dops->control(dev, cmd, arg); } } return 0;}
Hardware registration method: taking LED as an example, the initialization interface void led_register(void) needs to be called during initialization.
#include "stm32f0xx.h"#include "led.h"#include "cola_device.h" #define PORT_GREEN_LED GPIOC #define PIN_GREENLED GPIO_Pin_13 /* LED on, off, toggle */#define LED_GREEN_OFF (PORT_GREEN_LED->BSRR = PIN_GREENLED)#define LED_GREEN_ON (PORT_GREEN_LED->BRR = PIN_GREENLED)#define LED_GREEN_TOGGLE (PORT_GREEN_LED->ODR ^= PIN_GREENLED) static cola_device_t led_dev; static void led_gpio_init(void){ GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = PIN_GREENLED; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(PORT_GREEN_LED, &GPIO_InitStructure); LED_GREEN_OFF;} static int led_ctrl(cola_device_t *dev, int cmd, void *args){ if(LED_TOGGLE == cmd) { LED_GREEN_TOGGLE; } else { } return 1;} static struct cola_device_ops ops ={ .control = led_ctrl,}; void led_register(void){ led_gpio_init(); led_dev.dops = &ops; led_dev.name = "led"; cola_device_register(&led_dev);}
Application layer app code:
#include <string.h>#include "app.h"#include "config.h"#include "cola_device.h"#include "cola_os.h" static task_t timer_500ms;static cola_device_t *app_led_dev; // LED state changes every 500msstatic void timer_500ms_cb(uint32_t event){ cola_device_ctrl(app_led_dev,LED_TOGGLE,0);} void app_init(void){ app_led_dev = cola_device_find("led"); assert(app_led_dev); cola_timer_create(&timer_500ms,timer_500ms_cb); cola_timer_start(&timer_500ms,TIMER_ALWAYS,500);}
Thus, the app.c file does not need to include the led.h header file, which is how RT-Thread is implemented.
Source | Arm Technology Academy
Copyright belongs to the original author. If there is any infringement, please contact to delete.
END
关于安芯教育
安芯教育是聚焦AIoT(人工智能+物联网)的创新教育平台,提供从中小学到高等院校的贯通式AIoT教育解决方案。
安芯教育依托Arm技术,开发了ASC(Arm智能互联)课程及人才培养体系。已广泛应用于高等院校产学研合作及中小学STEM教育,致力于为学校和企业培养适应时代需求的智能互联领域人才。