1 Introduction
An OS-less MCU practical software framework includes task polling management, command manager, low power management, circular buffer, and other practical modules. The system extensively utilizes custom segment technology to reduce coupling between various modules, significantly improving program maintainability.
2 Main Features
- Supports automated module management and provides different priority level initialization declaration interfaces.
- Supports task polling management, which can be implemented through simple macro declarations without complex declaration calls.
- Supports low power management, sleep, and wake-up notifications.
- Supports command line parsing, command registration, and execution.
- Supports blink devices, unified management of LEDs, vibration motors, and buzzers.
3 Usage Instructions
The complete code can be referenced in the project files. The system development platform is as follows:MCU: STM32F401RET6IDE: IAR 7.4 or Keil MDK 4.72A
3.1 Task Initialization and Task Polling Management (module)
Before using this module, the system needs to provide a tick timer to drive the task polling operation. (Refer to platform.c)
// Timer interrupt (provides system tick)void SysTick_Handler(void) { systick_increase(SYS_TICK_INTERVAL); // Increase system tick}
Register initialization entry and task (refer to key_task.c)
static void key_init(void) { /* do something */ }static void key_scan(void) { /* do something */ }module_init("key", key_init); // Register key initialization interface
driver_register("key", key_scan, 20); // Register key task (polling once every 20ms)
3.2 Command Manager (cli)
Suitable for online debugging, parameter configuration, etc. (Refer to cli_task.c), users can control device behavior and query device status through command line output via serial port.
Command Format:
The command line format supported by cli is as follows:
<cmdname>< param1>< param2>< paramn>< \r\n ><cmdname> ,< param1>, < param2>, < paramn>, < \r\n >
Each command line contains a command name + command parameters (optional), and the command name and parameters can be separated by spaces or commas.
Default System Commands:
The cli system comes with 2 default commands, namely “?” and “help”. Entering them will list the current system command list as follows:
? - alias for 'help'help - list all commandpm - Low power control commandreset - reset systemsysinfo - show system information.
Adapting Command Manager:
A complete example can be referenced in cli_task.c.
static cli_obj_t cli; /* Command manager object */
/* * @brief Command line task initialization * @return none */static void cli_task_init(void) {cli_port_t p = {tty.write, tty.read}; /* Read and write interface */
cli_init(&cli, &p); /* Initialize command line object */
cli_enable(&cli);
cli_exec_cmd(&cli, "sysinfo"); /* Display system information */}
/* * @brief Command line task processing * @return none */static void cli_task_process(void) { cli_process(&cli);}
module_init("cli", cli_task_init); task_register("cli", cli_task_process, 10); /* Register command line task */
Command Registration:
Taking the reset command as an example (refer to cmd_devinfo.c):
#include "cli.h"//.../* * @brief Reset command */int do_cmd_reset(struct cli_obj *o, int argc, char *argv[]) { NVIC_SystemReset();return 0;}
cmd_register("reset", do_cmd_reset, "reset system");
3.3 Low Power Manager (pm)
Controls intermittent operation to reduce system power consumption. Its basic working principle is to poll whether each module in the system allows the system to enter low power mode. In fact, this is a decision mechanism, where all modules have veto power, meaning that as long as one module does not allow sleep, the system will not enter sleep mode. The pm module will calculate the minimum allowable sleep duration returned by each module before sleeping and will sleep based on the minimum sleep duration.
How to Adapt:
Before use, it needs to be initialized through pm_init and provide the maximum sleep time allowed by the current system, as well as the function interface for entering sleep. The basic interface definition is as follows:
/* Low power adapter ---------------------------------------------------------*/typedef struct {/** * @brief Maximum sleep duration of the system (ms) */unsigned int max_sleep_time;/** * @brief Enter sleep state * @param[in] time - expected sleep duration (ms) * @retval actual sleep duration * @note After sleeping, two things need to be considered: one is to periodically wake up to feed the watchdog, otherwise it will send a restart during sleep; the other is to compensate the sleep time for the system tick clock, otherwise it will cause time inaccuracies. */unsigned int (*goto_sleep)(unsigned int time);} pm_adapter_t;void pm_init(const pm_adapter_t *adt);
void pm_enable(void);
void pm_disable(void);
void pm_process(void);
A complete usage example can be referenced in platform-lowpower.c. By default, the low power function is disabled. Readers can remove the original non-low power version of platform.c from the project and add the platform-lowpower.c file for compilation to use.
Registering Low Power Devices:
Taking key scanning as an example, under normal circumstances, if no key is pressed, the system can enter sleep mode without affecting the key function. If a key is pressed, the system needs to wake up periodically to poll the key task.Therefore, in a low power system, to avoid affecting the real-time performance of the key, two things need to be handled well:
- In sleep mode, if a key is pressed, the system should immediately wake up to handle the subsequent scanning work.
- If a key is pressed, the system can enter sleep mode, but it needs to wake up periodically to poll the key task.
For the first case, configure the key as an edge interrupt to wake up, for example, with STM32F4 (refer to key_task.c), which supports external interrupt wake-up functionality.
/* * @brief Key IO initialization * PC0 -> key; * @return none */static void key_io_init(void) { /* Enable GPIOA clock */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
gpio_conf(GPIOC, GPIO_Mode_IN, GPIO_PuPd_UP, GPIO_Pin_0);
// In low power mode, to detect the key, configure as interrupt wake-up RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource0); exti_conf(EXTI_Line0, EXTI_Trigger_Falling, ENABLE); nvic_conf(EXTI0_IRQn, 0x0F, 0x0F);
key_create(&key, readkey, key_event); /* Create key */}
For the second case, it can be handled by pm_dev_register. When the system requests sleep, if the key is pressed at this time, it will return the next wake-up time, as shown in the example below.
// Refer to key_task.c#include "pm.h"/* * @brief Sleep notification */static unsigned int key_sleep_notify(void) {return key_busy(&key) || readkey() ? 20 : 0; /* If not idle, wake up once every 20ms */} pm_dev_register("key", NULL, key_sleep_notify, NULL);
3.4 Blink Module
Manages devices with blinking characteristics (LED, motor, buzzer):Usage Steps:
- The system needs to provide a tick clock, which is obtained through the get_tick() interface in blick.c, relying on the module module.
- Polling needs to be performed periodically in the task.
Alternatively, it can be implemented through task registration of the “module” module:
task_register("blink", blink_dev_process, 50); // Polling once every 50ms
LED Driver:
blink_dev_t led; // Define LED device
/* *@brief Red LED control (GPIOA.8) *@param[in] on - control on/off */static void led_ctrl(int on) {if (on) GPIOA->ODR |= (1 << 8);else GPIOA->ODR &= ~(1 << 8);}
/* *@brief LED initialization program */void led_init(void) { led_io_init(void); // LED IO initialization blink_dev_create(&led, led_ctrl); // Create LED device
blink_dev_ctrl(&led, 50, 100, 0); // Blink fast (50ms on, 100ms off)}
3.5 Key Management Module
Similar to the blink module, there are two points to note before use:
- The system needs to provide a tick clock, which is obtained through the get_tick() interface in key.c, relying on the module module.
- Polling needs to be performed periodically in the task.
key_t key; // Define key manager
/* *@brief Key event *@param[in] type - Key type (KEY_PRESS, KEY_LONG_DOWN, KEY_LONG_UP) *@param[in] duration - Long press duration */void key_event(int type, unsigned int duration) {if (type == KEY_PRESS) { // Short press
} else if (type == KEY_LONG_DOWN) { // Long press
}}
// Read key value (assuming key output port is STM32 MCU PA8)int read_key(void) {return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8) == Bit_RESET;}
/* *@brief Key initialization */void key_init(void) {// Open GPIO clock RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);// Configure as input mode gpio_conf(GPIOA, GPIO_Mode_IN, GPIO_PuPd_NOPULL, GPIO_Pin_8); // Create a key key_create(&key, read_key, key_event); }
Source: https://gitee.com/moluo-tech/CodeBrick