“Open Source Selection” is a column for us to share high-quality projects in the open source community such as Github and Gitee, including technical, learning, practical and various interesting content. In this issue, we recommend an OS-free MCU utility software framework, including task polling management, command manager, low-power management, ring buffer and other practical modules. The system makes extensive use of custom segment technology to reduce the coupling relationship between various modules and greatly improve the maintainability of the program.
Key features:
It supports automatic module management and provides interfaces for initialization declarations at different priority levels.
Task polling management is supported, which can be achieved through simple declarations, without the need for complex declaration calls.
Supports low-power management, sleep and wake-up notifications.
Command line parsing, command registration and execution are supported.
Blink device support, unified management of LED, vibration motor, buzzer
Directions for use
The complete code can refer to the engineering file, and the system development platform is as follows:
MCU:STM32F401RET6
IDE: IAR 7.4 or Keil MDK 4.72A
Task Initialization and Task Polling Management (Module)
Before using this module, the system needs to provide a tick timer to drive the task polling job.
Timer Interrupt (Provides System Tick)
void SysTick_Handler(void)
{
systick_increase(SYS_TICK_INTERVAL); Increase system beats
}
Register the initialization entry and tasks
static void key_init(void)
{
/*do something*/
}
static void key_scan(void)
{
/*do something*/
}
module_init(“key”, key_init); Register the button initialization interface
driver_register(“key”, key_scan, 20); Register keystrokes (1 poll in 20ms)
Command Manager
It is suitable for online debugging, parameter configuration, etc. (refer to cli_task.c), and users can control device behavior and query device status through the serial port output command line.
Command format
The CLI supports the following command-line formats:
<cmd name> < param1> < param2> < paramn> < rn > <cmd name> ,< param1>, < param2>, < paramn>, < rn >
The system defaults to commands
The CLI system comes with two default commands, which are “?” With the “help” command, enter a list of commands that they can list the current system contains, as follows:
? – alias for ‘help’
help – list all command.
pm – Low power control command
reset – reset system
sysinfo – show system infomation.
Adaptation to the Command Manager
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/Write Interface */
cli_init(&cli, &p); /*Initialize Command Line Objects */
cli_enable(&cli);
cli_exec_cmd(&cli,”sysinfo”); /*Show 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 a command line task*/
Command registration
#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”);
Low Power Manager
Control intermittent operation to reduce system power consumption. The basic principle of operation is to poll whether the individual modules in the system can allow the system to enter the low power consumption. In fact, this is a judgment mechanism, and all modules have the right of veto, that is, as long as one module is not allowed to hibernate, then the system will not go into hibernation. Before the PM module hibernates, it calculates the minimum allowable sleep duration for each module and hibernates in units of the minimum sleep duration.
How to adapt
Before use, you need to initialize and adapt through the pm_init, provide the maximum sleep time allowed by the current system, and enter the function interface to sleep, the basic interface is defined as follows:
/*Low Power Adapter ———————————————————*/
typedef struct {
/**
* @brief Maximum system sleep duration (ms)
*/
unsigned int max_sleep_time;
/**
* @brief Goes to sleep
* @param[in] time – Expected sleep duration (ms)
* @retval Actual sleep duration
* @note There are two things to consider after hibernation, one is that you need to get up regularly to feed the watchdog, otherwise it will be dormant
* Restart during sending. Another thing is that you need to compensate for the sleep time to tick the clock to the system, otherwise it will
* Caused by inaccurate timing.
*/
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);
The completed use example can refer to platform-lowpower.c, the low-power function is disabled by default, readers can remove the original low-power version of platform.c in the project, and add the platform-lowpower.c file for compilation.
Register low-power devices
Taking key scanning as an example, under normal circumstances, if the key is not pressed, then the system can enter the sleep state, which has no effect on the key function. If the key is pressed, then the system needs to wake up and poll the key task at a regular time.
Therefore, in a low-power system, in order not to affect the real-time performance of the keys, two things need to be handled:
When the system is asleep, if a key is pressed, the system should wake up immediately so that it can process the next scan.
If the key is pressed, the system can go to sleep, but it needs to wake up periodically to poll the key task.
For the first case, it is sufficient to configure the key to wake up with an edge interrupt, taking STM32F4 as an example (refer to key_task.c), which supports the external interrupt wake-up function.
/*
* @brief button 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, in order to be able to detect keystrokes, it is configured to 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 can return to the next wake-up time, as shown in the example below.
Ref. key_task.c
#include “pm.h”
/*
* @brief Sleep notifications
*/
static unsigned int key_sleep_notify(void)
{
return key_busy(&key) || readkey() ? 20 : 0; /* Wake up 1 time in 20ms when not idle*/
} pm_dev_register(“key”, NULL, key_sleep_notify, NULL);
blink module
Management of devices (LED, motor, Buzzer) with flashing characteristics (LED, MOTOR, BUZZER).
Directions of use:
The system needs to provide a ticking clock, which is obtained through the get_tick() interface in blick.c, which depends on the module module
Polling needs to be done at regular intervals in the task
Or through the task registration of the “module” module
task_register(“blink”, blink_dev_process, 50); 50ms polling 1 time
LED driver
blink_dev_t led; Define the LED device
/*
*@brief Red LED control (GPIOA.8)
*@param[in] on—Turns on and off
*/
static void led_ctrl(int on)
{
if (on)
GPIOA->ODR |= (1 << 8);
else
GPIOA->ODR &= ~(1 << 8);
}
/*
*@brief LED initializer
*/
void led_init(void)
{
led_io_init(void); LED IO initialization
blink_dev_create(&led, led_ctrl); Create an LED device
blink_dev_ctrl(&led, 50, 100, 0); Fast flash (50ms on, 100ms off)
}