commit 7b86aa3362ea5e420511baa15ab782512fd142c3 Author: LokLiang Date: Thu Mar 28 12:19:52 2024 +0800 参考代码 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..baf1911 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(project) diff --git a/app/app_main.c b/app/app_main.c new file mode 100644 index 0000000..b80f1d1 --- /dev/null +++ b/app/app_main.c @@ -0,0 +1,79 @@ +#include "app_main.h" + +/* 标准库 */ +#include +#include +#include + +/* 配置 */ +#include "config/board_config.h" + +/* SDK */ +#include "esp_err.h" + +/* 通用模块 */ +#include "button/button_event.h" + +/* 应用模块 */ + +/* 自定义框架抽象层 */ +#include "os/os.h" +#include "os/os_common.h" +#include "console.h" +#include "shell/sh_vset.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_DBG +#define SYS_LOG_DOMAIN "MAIN" +#include "sys_log.h" + +nvs_handle g_nvs_hdl; // nvs 句柄 +os_work_q_t g_work_q_hdl_low; // 低于 default_os_work_q_hdl 优先级的工作队列 + +static void _init_nvs(void); +static int _change_mode_event_button(const button_event_t *event); +static void _vset_cb(sh_t *sh_hdl); + +void work_app_main(void *arg) +{ + /* 初始化 SDK 的 nvs 模块 */ + _init_nvs(); + + /* 初始化按键检测和控制 */ + button_init(PIN_BIT(g_cfg_board->key_boot.pin), g_cfg_board->key_boot.en_lev); + + button_event_add_callback(g_cfg_board->key_boot.pin, _change_mode_event_button); + + /* 启动 shell */ + SYS_LOG_INF("app start"); + vset_init(&g_uart_handle_vt100, _vset_cb); + os_thread_sleep(100); + shell_start(); +} + +static void _init_nvs(void) +{ +#define _NVS_NAME "nvs-app" + if (g_nvs_hdl == 0) + { + ESP_ERROR_CHECK(nvs_open(_NVS_NAME, NVS_READWRITE, &g_nvs_hdl)); + } +} + +static int _change_mode_event_button(const button_event_t *event) +{ + static const char *const stat_tab[] = { + [BUTTON_UP] = "up", + [BUTTON_DOWN] = "down", + [BUTTON_HELD] = "held", + }; + if (event->event < __ARRAY_SIZE(stat_tab)) + { + SYS_LOG_DBG("button stat: %s", stat_tab[event->event]); + } + + return 0; +} + +static void _vset_cb(sh_t *sh_hdl) +{ +} diff --git a/app/app_main.h b/app/app_main.h new file mode 100644 index 0000000..3c531f2 --- /dev/null +++ b/app/app_main.h @@ -0,0 +1,7 @@ +#pragma once + +#include "nvs.h" +#include "os/os.h" + +extern nvs_handle g_nvs_hdl; // nvs 句柄 +extern os_work_q_t g_work_q_hdl_low; // 低于 default_os_work_q_hdl 优先级的工作队列 diff --git a/app/button/button_event.c b/app/button/button_event.c new file mode 100644 index 0000000..400cec5 --- /dev/null +++ b/app/button/button_event.c @@ -0,0 +1,395 @@ +#include +#include +#include +#include + +#include "driver/gpio.h" + +#include "os/os.h" + +#include "button_event.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN +#define SYS_LOG_DOMAIN "BUTTON" +#include "sys_log.h" + +#define VOLTAGE_HIGH 1 // 高电平有效 +#define VOLTAGE_LOW 0 // 高电平有效 + +#define DEBOUNCE_CHECK_VOLTAGE_CHANGED 1 +#define DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED 0 + +#define DEBOUCE_CHECK_MAX_TIMES 5 + +typedef struct +{ + uint8_t pin; + uint8_t is_inverted; // 是否反向,如果为真的话,则当按钮按下后,引脚为低电平 + uint16_t history; + uint32_t down_time; + uint32_t next_long_time; + int current_voltage; // 当前引脚电平 + int debounce_check_counter; + uint8_t allow_multiple_long_press_event; + uint8_t long_press_event_sent; +} debounce_t; + +int pin_count = -1; +debounce_t *debounce; +static os_queue_t s_event_queue; // 当出现按键变化时被传送到的队列对象 +static button_callback_t *g_callback_list_head = NULL; +static button_callback_t *g_callback_list_tail = NULL; +static os_work_t s_work_hdl; + +static os_queue_t *_pulled_button_init(unsigned long long pin_select, gpio_pull_mode_t pull_mode); + +static uint32_t millis() +{ + return os_get_sys_time(); +} + +static int send_event(debounce_t *db, button_event_e ev) +{ + static char *const ev_tbl[] = { + [BUTTON_UP] = "release", + [BUTTON_DOWN] = "press", + [BUTTON_HELD] = "held"}; + SYS_ASSERT(ev < sizeof(ev_tbl), "ev: %d", ev); + SYS_LOG_DBG("event happend: pinnum: %d, ev: %s", db->pin, ev_tbl[ev]); + + button_event_t event = { + .pin = db->pin, + .event = ev, + }; + + os_queue_send(&s_event_queue, &event, 0); + + button_callback_t *tmp = g_callback_list_head; + button_callback_t *callback_list = NULL; + button_callback_t *callback_list_it = NULL; + while (tmp != NULL) + { + if (tmp->pin == db->pin) + { + button_callback_t *new_callback = NULL; + if (callback_list == NULL) + { + callback_list = os_malloc(sizeof(button_callback_t)); + callback_list_it = callback_list; + new_callback = callback_list_it; + } + else + { + new_callback = os_malloc(sizeof(button_callback_t)); + } + new_callback->pin = db->pin; + new_callback->event_callback = tmp->event_callback; + new_callback->next = NULL; + + if (new_callback != callback_list_it) + { + callback_list_it->next = new_callback; + callback_list_it = new_callback; + } + } + tmp = tmp->next; + } + + tmp = callback_list; + int stop_forward_event = 0; + while (tmp != NULL) + { + if (tmp->pin == db->pin) + { + SYS_LOG_DBG("do call back: pinnum: %d, ev: %s", db->pin, ev_tbl[ev]); + stop_forward_event = tmp->event_callback(&event); + if (stop_forward_event) + { + break; + } + } + tmp = tmp->next; + } + + // 释放临时遍历对象 + tmp = callback_list; + while (tmp) + { + button_callback_t *next_obj = tmp->next; + + os_free(tmp); + tmp = next_obj; + } + + return stop_forward_event; +} + +static int update_debounce_counter(int pin_index) +{ + debounce_t *button_event_info = &debounce[pin_index]; + int level = gpio_get_level(button_event_info->pin); + if (button_event_info->current_voltage != level) + { + button_event_info->debounce_check_counter += 1; + } + else if (button_event_info->current_voltage == level && button_event_info->debounce_check_counter > 0) + { + button_event_info->debounce_check_counter -= 1; + } + + // 防抖检测已经连续多次检测到相同的电平,则改变按钮当前的电平 + if (button_event_info->debounce_check_counter == DEBOUCE_CHECK_MAX_TIMES) + { + button_event_info->current_voltage = level; + button_event_info->debounce_check_counter = 0; + SYS_LOG_DBG("button voltage changed, pin:%d, voltage:%d", button_event_info->pin, level); + return DEBOUNCE_CHECK_VOLTAGE_CHANGED; + } + + return DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED; +} + +static bool is_button_down(int pin_index) +{ + debounce_t *button_event_info = &debounce[pin_index]; + + if (button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_LOW) + { + // 如果电平是反向的,并且当前是低电平,则认为按钮按下了 + return true; + } + else if (!button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_HIGH) + { + // 如果电平不是反向的,并且当前是高电平,则认为按钮按下了 + return true; + } + + return false; +} + +static bool is_button_up(int pin_index) +{ + debounce_t *button_event_info = &debounce[pin_index]; + + if (button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_HIGH) + { + // 如果电平是反向的,并且当前是高电平,则认为按钮释放了 + return true; + } + else if (!button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_LOW) + { + // 如果电平不是反向的,并且当前是低电平,则认为按钮释放了 + return true; + } + + return false; +} + +static void _work_handler_button(void *pvParameter) +{ + static uint8_t cancel_laster_event = 1; + + for (int idx = 0; idx < pin_count; idx++) + { + debounce_t *button_event_info = &debounce[idx]; + + int check_result = update_debounce_counter(idx); // 防抖处理 + if (check_result == DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED) + { + // 如果allow_multiple_long_press_event为false,则一次长按无论长按多久,只会触发一次长按事件 + if (!button_event_info->allow_multiple_long_press_event && button_event_info->long_press_event_sent != 0) + { + continue; + } + + // 电平没有变化,则检查按钮是否已经按下,以开始检查长按操作 + if (button_event_info->down_time && millis() >= button_event_info->next_long_time) + { + button_event_info->next_long_time = millis() + LONG_PRESS_REPEAT; + button_event_info->long_press_event_sent = 1; + cancel_laster_event = send_event(&debounce[idx], BUTTON_HELD); + if (cancel_laster_event) + { + button_event_info->down_time = 0; + } + } + } + else + { + // 电平有变化,检查是按下还是松开 + if (button_event_info->down_time == 0 && is_button_down(idx)) + { + cancel_laster_event = 0; + SYS_LOG_DBG("update downtime for pin:%d", button_event_info->pin); + button_event_info->down_time = millis(); + button_event_info->next_long_time = button_event_info->down_time + LONG_PRESS_DURATION; + cancel_laster_event = send_event(&debounce[idx], BUTTON_DOWN); + if (cancel_laster_event) + { + button_event_info->down_time = 0; + } + } + else if (button_event_info->down_time && is_button_up(idx)) + { + button_event_info->down_time = 0; + button_event_info->long_press_event_sent = 0; + cancel_laster_event = send_event(&debounce[idx], BUTTON_UP); + if (cancel_laster_event) + { + button_event_info->down_time = 0; + } + } + } + } + + os_work_later(10); +} + +/** + * @brief 初始化按电平式的按键检测。 + * 这将创建一个用于扫描按键的线程,当检测到按键变化时,将在线程中产生两种通知: + * 1. 发送到内部创建的队列中(提示:如果使用了 os_queue_regist() 注册工作项,该工作项将被自动唤醒) + * 2. 执行由 button_event_add_callback() 设置的回调函数(提示:同一引脚可设置多个回调) + * + * @param pin_select 按位表示 MCU 的引脚呈 + * @param en_lev 触发电平 + * @return os_queue_t* 由内部创建的消息队列 + */ +os_queue_t *button_init(unsigned long long pin_select, uint8_t en_lev) +{ + return _pulled_button_init(pin_select, en_lev == 0 ? GPIO_PULLUP_ONLY : GPIO_PULLDOWN_ONLY); +} + +static os_queue_t *_pulled_button_init(unsigned long long pin_select, gpio_pull_mode_t pull_mode) +{ + if (pin_count != -1) + { + SYS_LOG_WRN("Already initialized"); + return NULL; + } + + // Configure the pins + gpio_config_t io_conf; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = (pull_mode == GPIO_PULLUP_ONLY || pull_mode == GPIO_PULLUP_PULLDOWN); + io_conf.pull_down_en = (pull_mode == GPIO_PULLDOWN_ONLY || pull_mode == GPIO_PULLUP_PULLDOWN); + io_conf.pin_bit_mask = pin_select; + io_conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&io_conf); + + // Scan the pin map to determine number of pins + pin_count = 0; + int pin = 0; + for (pin = 0; pin < sizeof(pin_select) * 8; pin++) + { + if ((1ULL << pin) & pin_select) + { + pin_count++; + } + } + + // Initialize global state and s_event_queue + debounce = os_calloc(sizeof(debounce_t) * pin_count); + // s_event_queue = xQueueCreate(4, sizeof(button_event_t)); + os_queue_create(&s_event_queue, 4, sizeof(button_event_t)); + + // Scan the pin map to determine each pin number, populate the state + uint32_t idx = 0; + for (pin = 0; pin < sizeof(pin_select) * 8; pin++) + { + if ((1ULL << pin) & pin_select) + { + SYS_LOG_DBG("Registering button input: %d", pin); + debounce_t *button_event_info = &debounce[idx]; + + button_event_info->pin = pin; + button_event_info->down_time = 0; + button_event_info->allow_multiple_long_press_event = 1; + button_event_info->is_inverted = (pull_mode == GPIO_PULLUP_ONLY); + idx++; + } + } + + os_work_create(&s_work_hdl, "", _work_handler_button, NULL, OS_PRIORITY_HIGHEST); + os_work_submit(default_os_work_q_hdl, &s_work_hdl, 1000); + + SYS_LOG_INF("Initialized"); + + return &s_event_queue; +} + +/** + * @brief 添加回调函数:当由 button_init() 设置的对应的引脚号产生按键状态变化时,由内部线程创建执行的回调函数。 + * + * @param pin 0..n + * @param callback int (*)(const button_event_t *event) + */ +void button_event_add_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback) +{ + button_callback_t *callback_obj = os_malloc(sizeof(button_callback_t)); + callback_obj->pin = pin; + callback_obj->event_callback = callback; + callback_obj->next = NULL; + if (g_callback_list_head == NULL) + { + g_callback_list_head = callback_obj; + g_callback_list_tail = g_callback_list_head; + } + else + { + g_callback_list_tail->next = callback_obj; + g_callback_list_tail = callback_obj; + } +} + +void button_event_remove_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback) +{ + button_callback_t *tmp = g_callback_list_head; + button_callback_t *previous_item = NULL; + SYS_LOG_DBG("remove pin:%d", pin); + while (tmp != NULL) + { + if (tmp->event_callback == callback && tmp->pin == pin) + { + if (previous_item) + { + previous_item->next = tmp->next; // 断开当前点节,并将前一个和后一个连接起来 + os_free(tmp); + SYS_LOG_DBG("found item"); + tmp = previous_item->next; + } + else + { + // 没有上一个节点,则将头部节点,设置为待移除节点的下一个节点 + if (tmp->next) + { + g_callback_list_head = tmp->next; + } + else + { + g_callback_list_head = NULL; + SYS_LOG_DBG("####!!!!#####!!!!"); + } + os_free(tmp); + tmp = g_callback_list_head; + SYS_LOG_DBG("case111"); + } + SYS_LOG_DBG("2222"); + } + else + { + previous_item = tmp; + tmp = tmp->next; + SYS_LOG_DBG("1111"); + } + } + + // 更新g_callback_list_tail指针 + while (tmp != NULL) + { + g_callback_list_tail = tmp; + tmp = tmp->next; + } + + SYS_LOG_DBG("doneeee1"); +} \ No newline at end of file diff --git a/app/button/button_event.h b/app/button/button_event.h new file mode 100644 index 0000000..cf9ae36 --- /dev/null +++ b/app/button/button_event.h @@ -0,0 +1,47 @@ +/** + * @file button_event.h + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2023-09-04 + * + * @copyright Copyright (c) 2023 + * + */ + +#pragma once + +#include "os/os.h" +#include "driver/gpio.h" + +#define PIN_BIT(x) (1ULL << x) + +#define LONG_PRESS_DURATION (1000) +#define LONG_PRESS_REPEAT (200) + +typedef enum +{ + BUTTON_UP, + BUTTON_DOWN, + BUTTON_HELD +} button_event_e; + +typedef struct +{ + uint32_t pin; + button_event_e event; +} button_event_t; + +typedef int (*BUTTON_EVENT_CALLBACK)(const button_event_t *event); + +typedef struct button_callback_s +{ + struct button_callback_s *next; + uint32_t pin; + BUTTON_EVENT_CALLBACK event_callback; +} button_callback_t; + +os_queue_t *button_init(unsigned long long pin_select, uint8_t en_lev); + +void button_event_add_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback); +void button_event_remove_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback); diff --git a/app/config/board_config.c b/app/config/board_config.c new file mode 100644 index 0000000..5a945f3 --- /dev/null +++ b/app/config/board_config.c @@ -0,0 +1,27 @@ +#include "board_config.h" + +#include "driver/uart.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_DBG +#define SYS_LOG_DOMAIN "CFG" +#define CONS_ABORT() +#include "sys_log.h" + +static cfg_board_t const s_cfg_board_default = { + /* 控制台串口 */ + .uart_console = { + .pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_0, + .irq_prior = 0, + .br = 115200, + }, + + /* 启动按键 */ + .key_boot = { + .pin = 9, // 用于切换灯效 + .en_lev = 0, // 用于切换灯效 + }, +}; + +const cfg_board_t *g_cfg_board = &s_cfg_board_default; diff --git a/app/config/board_config.h b/app/config/board_config.h new file mode 100644 index 0000000..78428b0 --- /dev/null +++ b/app/config/board_config.h @@ -0,0 +1,40 @@ +/** + * @file board_config.h + * @author LokLiang + * @brief 统一板载硬件描述数据结构 + * @version 0.1 + * @date 2023-11-24 + * + * @copyright Copyright (c) 2023 + * + * 目的与作用场景: + * 使同类型的固件适应不同的硬件版本。 + * 例如同一类型产品,即使某些功能引脚改动,依然可以根据配置文件正确引导程序,而不需要新增特定的固件版本,旧的产品依然得到长期的支持。 + * + * 数据设置: + * 只能在工厂中配置,除此之外不允许任何手段尝试修改。 + * 数据地址固定为分区配置中 + * + */ + +#pragma once + +#include "drivers/chip/_hal.h" + +typedef struct // 按键配置 +{ + uint8_t pin; // 引脚号 + uint8_t en_lev; // 触发电平 +} cfg_board_key_t; + +typedef struct // 数据结构一旦定下不可随意变更 +{ + /* 硬件描述类 */ + hal_uart_hdl_t uart_console; // 控制台 + cfg_board_key_t key_boot; // 启动按键 + + /* 产品功能描述类 */ + +} cfg_board_t; + +extern const cfg_board_t *g_cfg_board; // 配置数据 diff --git a/app/config/board_config/bec_led_strip_esp32c3.h b/app/config/board_config/bec_led_strip_esp32c3.h new file mode 100644 index 0000000..3365542 --- /dev/null +++ b/app/config/board_config/bec_led_strip_esp32c3.h @@ -0,0 +1,40 @@ +#if (CONFIG_BOARD_NAME_BEC_LED_STRIP_ESP32C3) + +#define CONFIG_IDF_TARGET "esp32c3" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */ + +static cfg_board_t const s_cfg_board_default = { + .firmware_str = PRODUCT_TYPE, + .platform_str = CONFIG_IDF_TARGET, + .board_name = "bec_led_strip_esp32c3", + + /* 控制台串口 */ + .uart_console = { + .pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_0, + .irq_prior = 0, + .br = 115200, + }, + + /* 数据透传串口 */ + .uart_fc = { + .pin_txd = {3, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {5, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_1, + .irq_prior = 24, + .br = 115200, + }, + + .led_spi = { + .spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI + .pin = 18, // 模拟 PWM 输出引脚 + }, + + /* 启动按键 */ + .key_boot = { + .pin = 7, // 用于切换灯效 + .en_lev = 1, // 用于切换灯效 + }, +}; + +#endif diff --git a/app/config/board_config/devkit_esp32c2.h b/app/config/board_config/devkit_esp32c2.h new file mode 100644 index 0000000..6b88bce --- /dev/null +++ b/app/config/board_config/devkit_esp32c2.h @@ -0,0 +1,40 @@ +#if (CONFIG_BOARD_NAME_DEVKIT_ESP32C2) + +#define CONFIG_IDF_TARGET "esp32c2" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */ + +static cfg_board_t const s_cfg_board_default = { + .firmware_str = PRODUCT_TYPE, + .platform_str = CONFIG_IDF_TARGET, + .board_name = "devkit_esp32c2", + + /* 控制台串口 */ + .uart_console = { + .pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_0, + .irq_prior = 0, + .br = 115200, + }, + + /* 数据透传串口 */ + .uart_fc = { + .pin_txd = {7, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {6, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_1, + .irq_prior = 24, + .br = 115200, + }, + + .led_spi = { + .spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI + .pin = 1, // 模拟 PWM 输出引脚 + }, + + /* 启动按键 */ + .key_boot = { + .pin = 9, // 用于切换灯效 + .en_lev = 0, // 用于切换灯效 + }, +}; + +#endif diff --git a/app/config/board_config/devkit_esp32c3.h b/app/config/board_config/devkit_esp32c3.h new file mode 100644 index 0000000..fdcee61 --- /dev/null +++ b/app/config/board_config/devkit_esp32c3.h @@ -0,0 +1,40 @@ +#if (CONFIG_BOARD_NAME_DEVKIT_ESP32C3) + +#define CONFIG_IDF_TARGET "esp32c3" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */ + +static cfg_board_t const s_cfg_board_default = { + .firmware_str = PRODUCT_TYPE, + .platform_str = CONFIG_IDF_TARGET, + .board_name = "devkit_esp32c3", + + /* 控制台串口 */ + .uart_console = { + .pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_0, + .irq_prior = 0, + .br = 115200, + }, + + /* 数据透传串口 */ + .uart_fc = { + .pin_txd = {7, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {6, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_1, + .irq_prior = 24, + .br = 115200, + }, + + .led_spi = { + .spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI + .pin = 10, // 模拟 PWM 输出引脚 + }, + + /* 启动按键 */ + .key_boot = { + .pin = 9, // 用于切换灯效 + .en_lev = 0, // 用于切换灯效 + }, +}; + +#endif diff --git a/app/config/board_config/fixedwing_new.h b/app/config/board_config/fixedwing_new.h new file mode 100644 index 0000000..2ef6f39 --- /dev/null +++ b/app/config/board_config/fixedwing_new.h @@ -0,0 +1,40 @@ +#if (CONFIG_BOARD_NAME_FIXEDWING_NEW) + +#define CONFIG_IDF_TARGET "esp32c2" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */ + +static cfg_board_t const s_cfg_board_default = { + .firmware_str = PRODUCT_TYPE, + .platform_str = CONFIG_IDF_TARGET, + .board_name = "fixedwing_new", + + /* 控制台串口 */ + .uart_console = { + .pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_0, + .irq_prior = 0, + .br = 115200, + }, + + /* 数据透传串口 */ + .uart_fc = { + .pin_txd = {7, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {6, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_1, + .irq_prior = 24, + .br = 115200, + }, + + .led_spi = { + .spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI + .pin = 1, // 模拟 PWM 输出引脚 + }, + + /* 启动按键 */ + .key_boot = { + .pin = 3, // 用于切换灯效 + .en_lev = 1, // 用于切换灯效 + }, +}; + +#endif diff --git a/app/config/board_config/fixedwing_old.h b/app/config/board_config/fixedwing_old.h new file mode 100644 index 0000000..03b6d80 --- /dev/null +++ b/app/config/board_config/fixedwing_old.h @@ -0,0 +1,40 @@ +#if (CONFIG_BOARD_NAME_FIXEDWING_OLD) + +#define CONFIG_IDF_TARGET "esp32c2" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */ + +static cfg_board_t const s_cfg_board_default = { + .firmware_str = PRODUCT_TYPE, + .platform_str = CONFIG_IDF_TARGET, + .board_name = "fixedwing_old", + + /* 控制台串口 */ + .uart_console = { + .pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_0, + .irq_prior = 0, + .br = 115200, + }, + + /* 数据透传串口 */ + .uart_fc = { + .pin_txd = {8, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, + .pin_rxd = {7, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP}, + .id = UART_NUM_1, + .irq_prior = 24, + .br = 115200, + }, + + .led_spi = { + .spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI + .pin = 1, // 模拟 PWM 输出引脚 + }, + + /* 启动按键 */ + .key_boot = { + .pin = 3, // 用于切换灯效 + .en_lev = 1, // 用于切换灯效 + }, +}; + +#endif diff --git a/app/console.c b/app/console.c new file mode 100644 index 0000000..ec3735c --- /dev/null +++ b/app/console.c @@ -0,0 +1,158 @@ +#include "driver/uart.h" +#include "console.h" +#include "shell/sh.h" +#include "os/os.h" +#include "drivers/chip/uart.h" +#include "config/board_config.h" +#include "sys_log.h" + +static shell_input_cb s_input_cb; + +static void initialize_console(void) +{ + /* Drain stdout before reconfiguring it */ + fflush(stdout); + + /* Disable buffering on stdin */ + setvbuf(stdin, NULL, _IONBF, 0); + + /* Configure UART. Note that REF_TICK is used so that the baud rate remains + * correct while APB frequency is changing in light sleep mode. + */ + const uart_config_t uart_config = { + .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 + .source_clk = UART_SCLK_REF_TICK, +#else + .source_clk = UART_SCLK_XTAL, +#endif + }; + /* Install UART driver for interrupt-driven reads and writes */ + ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, + 256, 0, 0, NULL, 0)); + ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config)); +} + +static void _work_sh(void *arg) +{ + char c; + int flag = 0; + shell_input_cb input_cb = s_input_cb; + + if (input_cb) + { + static char last_key; + while (drv_uart_poll_read(g_cfg_board->uart_console.id, &c) > 0) + { + if (c != '\n' || last_key != '\r') + { + input_cb(&g_uart_handle_vt100, c); + flag = 1; + } + last_key = c; + } + } + else + { + while (drv_uart_poll_read(g_cfg_board->uart_console.id, &c) > 0) + { + sh_putc(&g_uart_handle_vt100, c); + flag = 1; + } + } + + if (flag) + { + fflush(stdout); + } + + os_work_later(10); +} + +static int _port_sh_vprint_fn(const char *format, va_list va) +{ + return vprintf(format, va); +} + +/** + * @brief 启动 shell. + * 这将: + * 1. 执行对应的初始化 + * 2. 自动默认工作队列中创建一个解析数据的后台 + */ +void shell_start(void) +{ + static uint8_t start_flag = 0; + + if (start_flag == 0) + { + /* 初始化控制台串口 */ + initialize_console(); + + /* 执行 shell 内部的初始化 */ + sh_register_external(_port_sh_vprint_fn); + + /* 注册系统命令 */ + extern void soc_shell_register(void); + soc_shell_register(); + + /* 创建 shell 的接收任务 _work_sh() */ + static os_work_t work_handler_shell; + os_work_create(&work_handler_shell, "work-shell", _work_sh, NULL, 1); + os_work_submit(default_os_work_q_hdl, &work_handler_shell, 0); + + /* 设置 shell 的提示符 */ + g_uart_handle_vt100.disable_echo = !CONFIG_SYS_LOG_CONS_ON; // 关闭回显 + sh_putstr_quiet(&g_uart_handle_vt100, "sh version\r"); + sh_set_prompt(&g_uart_handle_vt100, "esp32:/> "); + sh_putc(&g_uart_handle_vt100, '\r'); + fflush(stdout); + + start_flag = 1; + } +} + +/** + * @brief 临时改变终端的输入接口。 + * 使用 shell_input_restore() 可恢复预设的接口 + * + * @param cb 输入接口 + */ +void shell_input_set(shell_input_cb cb) +{ + s_input_cb = cb; +} + +/** + * @brief 恢复 shell_input_set() 被执行前的设置 + * + */ +void shell_input_restore(void) +{ + s_input_cb = NULL; +} + +/** + * @brief 在执行命令的过程中使用。 + * 若被执行的命令需要循环执行时,使用这个函数将读取控制台收到的数据, + * 当收到 ESC 按键时返回真值,此时可退出该命令的执行函数 + * + * @retval true 按下了 ESC 键 + * @retval false 未按下 ESC 键 + */ +bool shell_is_aobrt(void) +{ + char c; + if (drv_uart_poll_read(g_cfg_board->uart_console.id, &c) > 0) + { + if (c == '\x1b') + { + fflush(stdout); + return true; + } + } + return false; +} diff --git a/app/console.h b/app/console.h new file mode 100644 index 0000000..1b5e85c --- /dev/null +++ b/app/console.h @@ -0,0 +1,16 @@ +#ifndef __CONSOLE_H__ +#define __CONSOLE_H__ + +#include +#include "shell/sh.h" + +typedef void (*shell_input_cb)(sh_t *sh_hdl, char c); + +void shell_start(); + +void shell_input_set(shell_input_cb cb); +void shell_input_restore(void); + +bool shell_is_aobrt(void); + +#endif diff --git a/app/drivers/data_port/sb_data_port.c b/app/drivers/data_port/sb_data_port.c new file mode 100755 index 0000000..2c4ffde --- /dev/null +++ b/app/drivers/data_port/sb_data_port.c @@ -0,0 +1,249 @@ +#include "drivers/data_port/sb_data_port.h" + +#include "os/os.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF +#define SYS_LOG_DOMAIN "DATA" +#define CONS_ABORT() +#include "sys_log.h" + +/** + * @brief 由具体驱动实现的,以达到节省资源为主要目的,启动数据接口 + * + * @param port 由对应的驱动提供的绑定接口获得的句柄 + * @retval 0 成功 + */ +int sb_data_port_start(sb_data_port_t *port) +{ + if (sb_data_port_is_started(port)) + { + SYS_LOG_WRN("Data interface repeat started"); + return 0; + } + if (port == NULL || port->vtable == NULL) + { + return false; + } + + if (port->vtable->start) + { + return port->vtable->start(port); + } + else + { + SYS_LOG_ERR("The data interface has not been defined"); + return -1; + } +} + +/** + * @brief 由具体驱动实现的,以达到节省资源为主要目的,关闭数据接口 + * + * @param port 由对应的驱动提供的绑定接口获得的句柄 + * @retval 0 成功 + */ +int sb_data_port_stop(sb_data_port_t *port) +{ + if (!sb_data_port_is_started(port)) + { + SYS_LOG_WRN("The data interface has not been started yet"); + return 0; + } + + if (port->vtable->stop) + { + return port->vtable->stop(port); + } + else + { + SYS_LOG_ERR("The data interface has not been defined"); + return -1; + } +} + +/** + * @brief 由具体驱动实现的,写数据到对应的接口。 + * 建议这些数据将写入到驱动的缓存中,如果 wait_ms 为 0 立即返回, + * 如果 wait_ms > 0 则这个时间由本函数计时并处理,但期间可能出现堵塞。 + * 注意如果有选择 wait_ms > 0 的情况,驱动中收取缓存的线程与应用不能是同一线程。 + * + * @param port 由对应的驱动提供的绑定接口获得的句柄 + * @param data 来源数据 + * @param size 数据长度(字节) + * @param wait_ms 超时时间(毫秒) + * @retval < 0 操作失败或在指定的时间内未满足指定的操作长度 + * @retval >= 0 实际已缓存的长度 + */ +int sb_data_port_write(sb_data_port_t *port, const void *data, uint32_t size, uint32_t wait_ms) +{ + if (!sb_data_port_is_started(port)) + { + SYS_LOG_WRN("The data interface has not been started yet"); + return -1; + } + + if (port->vtable->write) + { + int ret = 0; + int cmp_time = os_get_sys_time(); + while (size) + { + int curr_time = os_get_sys_time(); + int wsize = port->vtable->write(port, data, size); + if (wsize > 0) + { + cmp_time = curr_time; + data = &((uint8_t *)data)[wsize]; + size -= wsize; + ret += wsize; + } + else if (wsize == 0) + { + if (wait_ms == 0) + { + break; + } + else if (curr_time - cmp_time > wait_ms) + { + return -1; + } + else + { + os_thread_sleep(1); + continue; + } + } + else + { + return -1; + } + } + + return ret; + } + else + { + SYS_LOG_ERR("The data interface has not been defined"); + return -1; + } +} + +/** + * @brief 由具体驱动实现的,从数据接口中读取已缓存的数据。 + * 如果 wait_ms 为 0 将立即返回, + * 如果 wait_ms > 0 则这个时间由本函数计时并处理,期间可能出现堵塞。 + * 注意如果有选择 wait_ms > 0 的情况,驱动中收取缓存的线程与应用不能是同一线程。 + * + * @param port 由对应的驱动提供的绑定接口获得的句柄 + * @param buffer[out] 保存到目标内存。值为 NULL 表示仅释放空间 + * @param length 目标内存长度(字节) + * @param wait_ms 超时时间(毫秒) + * @retval < 0 操作失败或在指定的时间内未满足指定的操作长度 + * @retval >= 0 实际已读取的长度 + */ +int sb_data_port_read(sb_data_port_t *port, void *buffer, uint32_t length, uint32_t wait_ms) +{ + if (!sb_data_port_is_started(port)) + { + SYS_LOG_WRN("The data interface has not been started yet"); + return -1; + } + + if (port->vtable->read) + { + int ret = 0; + int cmp_time = os_get_sys_time(); + while (length) + { + int curr_time = os_get_sys_time(); + int rsize = port->vtable->read(port, buffer, length); + if (rsize > 0) + { + cmp_time = curr_time; + buffer = &((uint8_t *)buffer)[rsize]; + length -= rsize; + ret += rsize; + } + else if (rsize == 0) + { + if (wait_ms == 0) + { + break; + } + else if (curr_time - cmp_time > wait_ms) + { + return -1; + } + else + { + os_thread_sleep(1); + continue; + } + } + else + { + return -1; + } + } + + return ret; + } + else + { + SYS_LOG_ERR("The data interface has not been defined"); + return -1; + } +} + +/** + * @brief 由具体驱动实现的,获取当前数据接口是否可用(是否已启动)。 + * 本套接口内部的所有其他接口内部都会优先经过这个接口确认状态,驱动内部的其他接口不需要二次确认。 + * + * @param port 由对应的驱动提供的绑定接口获得的句柄 + * @retval true 接口已启动并可用 + * @retval false 接口未启动,不可用 + */ +bool sb_data_port_is_started(sb_data_port_t *port) +{ + SYS_ASSERT(port, "Data interface not bound"); + SYS_ASSERT(port->vtable, "Data interface not bound"); + + if (port == NULL || port->vtable == NULL) + { + return false; + } + + if (port->vtable->is_started) + { + return port->vtable->is_started(port); + } + else + { + SYS_LOG_ERR("The data interface has not been defined"); + return false; + } +} + +/** + * @brief 由具体驱动实现的,获取当前数据接口的本次可读长度 + * + * @param port 由对应的驱动提供的绑定接口获得的句柄 + * @retval uint32_t sb_data_port_read() 当前可读取的接收缓存长度 + */ +uint32_t sb_data_port_get_rx_length(sb_data_port_t *port) +{ + if (!sb_data_port_is_started(port)) + { + return 0; + } + + if (port->vtable->get_rx_length) + { + return port->vtable->get_rx_length(port); + } + else + { + SYS_LOG_ERR("The data interface has not been defined"); + return -1; + } +} diff --git a/app/drivers/data_port/sb_data_port.h b/app/drivers/data_port/sb_data_port.h new file mode 100755 index 0000000..d8f7c6b --- /dev/null +++ b/app/drivers/data_port/sb_data_port.h @@ -0,0 +1,42 @@ +/** + * @file sb_data_port.h + * @author LokLiang + * @brief 统一的数据接口定义 + * @version 0.1 + * @date 2023-11-14 + * + * @copyright Copyright (c) 2023 + * + */ + +#pragma once + +#include +#include + +typedef struct sb_data_port_s sb_data_port_t; + +typedef struct +{ + int (*start)(sb_data_port_t *port); // 由具体驱动实现的,以达到节省资源为主要目的,启动数据接口 + int (*stop)(sb_data_port_t *port); // 由具体驱动实现的,以达到节省资源为主要目的,关闭数据接口 + int (*write)(sb_data_port_t *port, const void *data, uint32_t size); // 由具体驱动实现的,写数据到对应的接口。 + int (*read)(sb_data_port_t *port, void *buffer, uint32_t length); // 由具体驱动实现的,从数据接口中读取已缓存的数据。 + bool (*is_started)(sb_data_port_t *port); // 由具体驱动实现的,获取当前数据接口是否可用(是否已启动) + uint32_t (*get_rx_length)(sb_data_port_t *port); // 由具体驱动实现的,获取当前数据接口的本次可读长度 +} sb_data_port_vtable_t; + +struct sb_data_port_s +{ + const sb_data_port_vtable_t *vtable; // 接口 + void *data; // 数据 +}; + +int sb_data_port_start(sb_data_port_t *port); +int sb_data_port_stop(sb_data_port_t *port); + +int sb_data_port_write(sb_data_port_t *port, const void *data, uint32_t size, uint32_t wait_ms); +int sb_data_port_read(sb_data_port_t *port, void *buffer, uint32_t length, uint32_t wait_ms); + +bool sb_data_port_is_started(sb_data_port_t *port); +uint32_t sb_data_port_get_rx_length(sb_data_port_t *port); diff --git a/app/drivers/data_port/uart/uart_port.c b/app/drivers/data_port/uart/uart_port.c new file mode 100755 index 0000000..d885d65 --- /dev/null +++ b/app/drivers/data_port/uart/uart_port.c @@ -0,0 +1,486 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "os/os.h" + +#include "uart_port.h" + +#include "driver/uart.h" + +#include "drivers/data_port/sb_data_port.h" + +#define CONFIG_SYS_LOG_DUMP_ON 0 +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN +#define SYS_LOG_DOMAIN "UART" +#include "sys_log.h" + +#define _MAX_UART_NUM 3 + +#define UART_IN_TAST_STK_SIZE 2048 + +typedef struct +{ + uint16_t size; + uint8_t data[0]; +} __fifo_data_t; + +typedef struct +{ + int uart_num; + int baudrate; + int tx_pin; + int rx_pin; + int rx_task_priority; + uint16_t buffer_size; + uint8_t frame_ms; + volatile uint16_t buffer_used; + os_work_t *rx_resume_work; + QueueHandle_t event_queue; + os_thread_t rx_thread_hdl; + union + { + os_pipe_t rx_pipe; + os_fifo_t rx_fifo; + }; +} __uart_data_t; + +static void _uart_rx_thread(void *parmas) +{ + uart_event_t event; + int ret = 0; + int len = 0; + int rx_len = 0; + __uart_data_t *uart_data = ((sb_data_port_t *)parmas)->data; + const uint32_t TMP_BUF_LEN = 1024; + uint8_t tmpbuffer[TMP_BUF_LEN]; + TickType_t tick = portMAX_DELAY; + uint16_t recv_total = 0; + os_time_t rx_time = 0; + + for (;;) + { + if (xQueueReceive(uart_data->event_queue, (void *)&event, tick)) + { + switch (event.type) + { + case UART_DATA: + { + rx_len = event.size; + while (rx_len > 0) + { + if (uart_data->frame_ms == 0) + { + len = (rx_len > TMP_BUF_LEN) ? TMP_BUF_LEN : rx_len; + ret = uart_read_bytes(uart_data->uart_num, tmpbuffer, len, portMAX_DELAY); + if (ret <= 0) + { + break; + } + rx_len -= ret; + + // 将数据写入ringbuffer,由外部轮询ringbuffer + uint8_t *p = tmpbuffer; + for (int i = 0; i < 100 && ret != 0; i++) + { + int write_result = os_pipe_fifo_fill(&uart_data->rx_pipe, p, ret); + p = &((uint8_t *)p)[write_result]; + ret -= write_result; + if (ret != 0) + { + os_thread_sleep(10); + } + } + if (ret != 0) + { + SYS_LOG_ERR("write data to rx_pipe_obj failed, remain: %d bytes", ret); + } + } + else + { + len = sizeof(tmpbuffer) - recv_total; + if (len > rx_len) + { + len = rx_len; + } + ret = uart_read_bytes(uart_data->uart_num, &tmpbuffer[recv_total], len, portMAX_DELAY); + if (ret <= 0) + { + break; + } + + rx_len -= ret; + recv_total += ret; + if (recv_total >= sizeof(tmpbuffer)) + { + rx_time = os_get_sys_time() - uart_data->frame_ms - 1; // 使退出 switch 后的超时条件必定成立而立即保存数据 + break; + } + rx_time = os_get_sys_time(); + } + } + } + break; + + // Event of HW FIFO overflow detected + case UART_FIFO_OVF: + SYS_LOG_WRN("hw fifo overflow"); + // If fifo overflow happened, you should consider adding flow control for your application. + // The ISR has already reset the rx FIFO, + uart_flush_input(uart_data->uart_num); + xQueueReset(uart_data->event_queue); + break; + // Event of UART ring buffer full + case UART_BUFFER_FULL: + SYS_LOG_WRN("ring buffer full"); + // If buffer full happened, you should consider encreasing your buffer size + uart_flush_input(uart_data->uart_num); + xQueueReset(uart_data->event_queue); + break; + // Event of UART RX break detected + case UART_BREAK: + SYS_LOG_WRN("uart rx break"); + break; + // Event of UART parity check error + case UART_PARITY_ERR: + SYS_LOG_WRN("uart parity error"); + break; + // Event of UART frame error + case UART_FRAME_ERR: + SYS_LOG_WRN("uart frame error"); + break; + // Others + default: + SYS_LOG_WRN("uart event type: %d", event.type); + break; + } + } + + if (uart_data->frame_ms != 0) + { + if (os_get_sys_time() - rx_time > uart_data->frame_ms) + { + if (recv_total > 0) + { + while (uart_data->buffer_used >= uart_data->buffer_size) + { + os_thread_sleep(1); + } + + __fifo_data_t *fifo_data = os_fifo_alloc(sizeof(__fifo_data_t) + recv_total); + if (fifo_data) + { + os_scheduler_suspend(); + uart_data->buffer_used += recv_total + sizeof(((__fifo_data_t *)0)->size); + os_scheduler_resume(); + + fifo_data->size = recv_total; + memcpy(fifo_data->data, tmpbuffer, recv_total); + os_fifo_put(&uart_data->rx_fifo, fifo_data); + } + + recv_total = 0; + tick = 1; + } + else + { + tick = portMAX_DELAY; + } + } + else + { + tick = 1; + } + } + } +} + +static void uart_port_initialize(sb_data_port_t *port) +{ + __uart_data_t *uart_data = port->data; + uart_config_t uart_config; + + memset(&uart_config, 0, sizeof(uart_config)); + uart_config.baud_rate = uart_data->baudrate; + uart_config.data_bits = UART_DATA_8_BITS; + uart_config.parity = UART_PARITY_DISABLE; + uart_config.stop_bits = UART_STOP_BITS_1; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + + esp_err_t ret = uart_set_pin(uart_data->uart_num, uart_data->tx_pin, uart_data->rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (ret != ESP_OK) + { + SYS_LOG_ERR("failed to set uart pin:%d, %d", uart_data->uart_num, ret); + return; + } + + ret = uart_param_config(uart_data->uart_num, &uart_config); + if (ret != ESP_OK) + { + SYS_LOG_ERR("failed to set uart param:%d, %d", uart_data->uart_num, ret); + return; + } + + ret = uart_driver_install(uart_data->uart_num, 0x400, 0x400, 3, &(uart_data->event_queue), 0); + if (ret != ESP_OK) + { + SYS_LOG_ERR("failed to uart_driver_install:%d, %d", uart_data->uart_num, ret); + return; + } + + SYS_LOG_INF("uart_driver_install ok. (%d, %d, %d, %d, %d, %d)", uart_data->uart_num, 0x400, 0x400, 3, (uint32_t)&uart_data->event_queue, 0); +} + +static int _uart_port_start(sb_data_port_t *port) +{ + __uart_data_t *uart_data = port->data; + + SYS_LOG_INF("uart start, uartnum:%d", uart_data->uart_num); + + if (uart_data->frame_ms == 0) + { + if (!os_pipe_is_valid(&uart_data->rx_pipe)) + { + if (os_pipe_create(&uart_data->rx_pipe, uart_data->buffer_size) != OS_OK) + { + port->vtable->stop(port); + return -1; + } + os_pipe_regist(&uart_data->rx_pipe, uart_data->rx_resume_work, 0); + } + } + else + { + if (!os_fifo_q_is_valid(&uart_data->rx_fifo)) + { + if (os_fifo_q_create(&uart_data->rx_fifo) != OS_OK) + { + port->vtable->stop(port); + return -1; + } + os_fifo_q_regist(&uart_data->rx_fifo, uart_data->rx_resume_work, 0); + } + uart_data->buffer_used = 0; + } + + if (!uart_is_driver_installed(uart_data->uart_num)) + { + uart_port_initialize(port); + } + + if (!os_thread_is_valid(&uart_data->rx_thread_hdl)) + { + if (os_thread_create(&uart_data->rx_thread_hdl, + "uart_rx", + _uart_rx_thread, + port, + UART_IN_TAST_STK_SIZE, + uart_data->rx_task_priority) != OS_OK) + { + SYS_LOG_WRN("task create fail"); + port->vtable->stop(port); + return -1; + } + } + + return 0; +} + +static int _uart_port_stop(sb_data_port_t *port) +{ + __uart_data_t *uart_data = port->data; + + if (os_thread_is_valid(&uart_data->rx_thread_hdl)) + { + os_thread_delete(&uart_data->rx_thread_hdl); + } + + if (uart_is_driver_installed(uart_data->uart_num)) + { + uart_driver_delete(uart_data->uart_num); + } + + if (uart_data->frame_ms == 0) + { + if (os_pipe_is_valid(&uart_data->rx_pipe)) + { + os_pipe_delete(&uart_data->rx_pipe); + } + } + else + { + if (os_fifo_q_is_valid(&uart_data->rx_fifo)) + { + os_fifo_q_delete(&uart_data->rx_fifo); + } + } + + return 0; +} + +static int _uart_port_write(sb_data_port_t *port, const void *data, uint32_t size) +{ + __uart_data_t *uart_data = port->data; + int ret = uart_write_bytes(uart_data->uart_num, data, size); + if (ret > 0) + { + SYS_LOG_DUMP(data, ret, 0, 0); + } + return ret; +} + +static int _uart_port_read(sb_data_port_t *port, void *data, uint32_t size) +{ + __uart_data_t *uart_data = port->data; + int ret; + if (uart_data->frame_ms == 0) + { + ret = os_pipe_fifo_read(&uart_data->rx_pipe, data, size); + } + else + { + __fifo_data_t *fifo_data = os_fifo_take(&uart_data->rx_fifo, 0); + if (fifo_data) + { + ret = size < fifo_data->size ? size : fifo_data->size; + if (data) + { + memcpy(data, fifo_data->data, ret); + } + os_fifo_free(fifo_data); + + os_scheduler_suspend(); + uart_data->buffer_used -= ret + sizeof(((__fifo_data_t *)0)->size); + os_scheduler_resume(); + } + else + { + ret = 0; + } + } + if (ret > 0) + { + SYS_LOG_DUMP(data, ret, 0, 0); + } + return ret; +} + +static bool _uart_port_is_tart(sb_data_port_t *port) +{ + if (port == NULL) + { + return false; + } + __uart_data_t *uart_data = port->data; + if (uart_data == NULL) + { + return false; + } + + if (uart_data->frame_ms == 0) + { + if (!os_pipe_is_valid(&uart_data->rx_pipe)) + { + return false; + } + } + else + { + if (!os_fifo_q_is_valid(&uart_data->rx_fifo)) + { + return false; + } + } + + return true; +} + +static uint32_t _uart_port_get_rx_length(sb_data_port_t *port) +{ + __uart_data_t *uart_data = port->data; + if (uart_data->frame_ms == 0) + { + return os_pipe_get_valid_size(&uart_data->rx_pipe); + } + else + { + __fifo_data_t *fifo_data = os_fifo_peek_head(&uart_data->rx_fifo); + if (fifo_data) + { + return fifo_data->size; + } + else + { + return 0; + } + } +} + +static sb_data_port_vtable_t const s_uart_vtable = { + .start = _uart_port_start, + .stop = _uart_port_stop, + .write = _uart_port_write, + .read = _uart_port_read, + .is_started = _uart_port_is_tart, + .get_rx_length = _uart_port_get_rx_length, +}; + +int sb_uart_port_init(void) +{ + return 0; +} + +/** + * @brief + * + * @param uart_num 串口号 0..n + * @param baudrate 波特率 + * @param tx_pin TXD 引脚 + * @param rx_pin RXD 引脚 + * @param rx_task_priority 内部接收任务的优先级 + * @param buffer_size 接收缓存大小 + * @param frame_ms 0: 数据流, > 0: 最少连续有 n 毫秒没有收到数据时成为一帧 + * @param rx_resume_work 当收到新数据时唤醒的工作项,可设置为 NULL + * @return sb_data_port_t* + */ +sb_data_port_t *sb_uart_port_bind(int uart_num, + int baudrate, + int tx_pin, + int rx_pin, + int rx_task_priority, + uint16_t buffer_size, + uint8_t frame_ms, + os_work_t *rx_resume_work) +{ + static sb_data_port_t s_uart_port[_MAX_UART_NUM]; + static __uart_data_t s_uart_data[_MAX_UART_NUM]; + + sb_data_port_t *port = &s_uart_port[uart_num]; + __uart_data_t *uart_data = &s_uart_data[uart_num]; + + SYS_ASSERT(uart_num < _MAX_UART_NUM, ""); + SYS_ASSERT(port->data == NULL, "The interface has already been bound"); + + uart_data->uart_num = uart_num; + uart_data->baudrate = baudrate; + uart_data->tx_pin = tx_pin; + uart_data->rx_pin = rx_pin; + uart_data->rx_task_priority = rx_task_priority; + uart_data->buffer_size = buffer_size; + uart_data->frame_ms = frame_ms; + uart_data->rx_resume_work = rx_resume_work; + + port->vtable = &s_uart_vtable; + port->data = uart_data; + + return port; +} + +void sb_uart_port_unbind(sb_data_port_t *port) +{ + if (_uart_port_is_tart(port)) + { + _uart_port_stop(port); + } + port->data = NULL; +} diff --git a/app/drivers/data_port/uart/uart_port.h b/app/drivers/data_port/uart/uart_port.h new file mode 100755 index 0000000..3310977 --- /dev/null +++ b/app/drivers/data_port/uart/uart_port.h @@ -0,0 +1,28 @@ +/** + * @file uart_port.h + * @author LokLiang + * @brief + * @version 0.1 + * @date 2023-09-04 + * + * @copyright Copyright (c) 2023 + * + */ + +#pragma once + +#include "../sb_data_port.h" +#include "os/os.h" + +int sb_uart_port_init(void); + +sb_data_port_t *sb_uart_port_bind(int uartNum, + int baudrate, + int tx_pin, + int rx_pin, + int rx_task_priority, + uint16_t buffer_size, + uint8_t frame_ms, + os_work_t *rx_resume_work); + +void sb_uart_port_unbind(sb_data_port_t *port); diff --git a/components/system/include/drivers/chip/_hal.h b/components/system/include/drivers/chip/_hal.h new file mode 100755 index 0000000..f628325 --- /dev/null +++ b/components/system/include/drivers/chip/_hal.h @@ -0,0 +1,103 @@ +#ifndef ___HAL_H__ +#define ___HAL_H__ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef uint8_t hal_id_t; + +#ifdef __cplusplus +} +#endif + +#include "drivers/chip/gpio.h" +#include "drivers/chip/clk.h" +#include "drivers/chip/irq.h" +#include "drivers/chip/uart.h" +#include "drivers/chip/i2c.h" +#include "drivers/chip/spi.h" +#include "drivers/chip/phy.h" +#include "drivers/chip/adc.h" +#include "drivers/chip/cmp.h" +#include "drivers/chip/dac.h" +#include "drivers/chip/dma.h" +#include "drivers/chip/tim.h" +#include "drivers/chip/tick.h" +#include "drivers/chip/fmc.h" +#include "drivers/chip/wdt.h" +#include "drivers/chip/lpm.h" +#include "drivers/chip/misc.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct +{ + uint8_t pin; + gpio_dir_t dir; + gpio_pud_t pull; +} hal_pin_hdl_t; + +typedef struct +{ + hal_pin_hdl_t pin_txd; + hal_pin_hdl_t pin_rxd; + hal_id_t id; + uint8_t irq_prior; + uint32_t br; +} hal_uart_hdl_t; + +typedef struct +{ + hal_pin_hdl_t pin_sck; + hal_pin_hdl_t pin_sda; + hal_id_t id; + uint8_t irq_prior; + uint32_t freq; +} hal_i2c_hdl_t; + +typedef struct +{ + hal_pin_hdl_t pin_sck; + hal_pin_hdl_t pin_mosi; + hal_pin_hdl_t pin_miso; + hal_id_t id; + spi_mode_t mode; + uint8_t irq_prior; + uint32_t freq; +} hal_spi_hdl_t; + +typedef struct +{ + hal_id_t id; + uint16_t vref_mv; + uint32_t value_max; + uint32_t channel_mask; +} hal_adc_hdl_t; + +void drv_hal_init(void); +void drv_hal_deinit(void); + +int drv_hal_gpio_pin_init(const hal_pin_hdl_t *gpio_hdl); +int drv_hal_uart_init(const hal_uart_hdl_t *uart_hdl); +int drv_hal_i2c_init(const hal_i2c_hdl_t *i2c_hdl); +int drv_hal_spi_init(const hal_spi_hdl_t *spi_hdl); +int drv_hal_drv_adc_init(const hal_adc_hdl_t *adc_hdl); + +void drv_hal_debug_enable(void); +void drv_hal_debug_disable(void); + +void drv_hal_sys_reset(void); +void drv_hal_sys_exit(int status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/adc.h b/components/system/include/drivers/chip/adc.h new file mode 100755 index 0000000..e05e6d8 --- /dev/null +++ b/components/system/include/drivers/chip/adc.h @@ -0,0 +1,40 @@ +#ifndef __ADC_H__ +#define __ADC_H__ + +#include "drivers/chip/_hal.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void (*adc_isr_cb_fn)(uint8_t channel_id); + +void drv_adc_pin_configure(hal_id_t id, uint8_t pin); + +void drv_adc_enable(hal_id_t id); +void drv_adc_disable(hal_id_t id); + +void drv_adc_init(hal_id_t id, unsigned channel_mask, uint32_t sampling_length); +void drv_adc_deinit(hal_id_t id, unsigned channel_mask); + +int drv_adc_irq_callback_enable(hal_id_t id, uint8_t channel_id, adc_isr_cb_fn cb); +int drv_adc_irq_callback_disable(hal_id_t id, uint8_t channel_id); + +void drv_adc_irq_enable(hal_id_t id, int priority); +void drv_adc_irq_disable(hal_id_t id); + +void drv_adc_start(hal_id_t id, unsigned channel_mask); +void drv_adc_stop(hal_id_t id, unsigned channel_mask); + +bool drv_adc_is_busy(hal_id_t id, uint8_t channel_id); +bool drv_adc_is_ready(hal_id_t id, uint8_t channel_id); + +int drv_adc_read(hal_id_t id, uint8_t channel_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/clk.h b/components/system/include/drivers/chip/clk.h new file mode 100755 index 0000000..7b538d7 --- /dev/null +++ b/components/system/include/drivers/chip/clk.h @@ -0,0 +1,17 @@ +#ifndef __CLK_H__ +#define __CLK_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +unsigned drv_clk_get_cpu_clk(void); + +int drv_clk_calibration(unsigned tar_clk, unsigned cur_clk); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/cmp.h b/components/system/include/drivers/chip/cmp.h new file mode 100755 index 0000000..dbee8eb --- /dev/null +++ b/components/system/include/drivers/chip/cmp.h @@ -0,0 +1,57 @@ +#ifndef __HAL_CMP_H__ +#define __HAL_CMP_H__ + +#include "drivers/chip/_hal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void (*cmp_isr_cb_fn)(void); + +typedef enum __packed +{ + _CMP_OUTPUT_NONE, // 没有任何输出动作 + _CMP_OUTPUT_IO, // 输出到 IO + _CMP_OUTPUT_EXTI, // 触发 中断线 + _CMP_OUTPUT_TIM, // 触发 定时器的输入 +} cmp_output_t; + +typedef enum __packed +{ + _CMP_INT_EDGE_NONE, + _CMP_INT_EDGE_FALL, + _CMP_INT_EDGE_RISE, + _CMP_INT_EDGE_BOTH, +} cmp_int_edge_t; + +void drv_cmp_pin_configure_in(hal_id_t id, uint8_t pin); +void drv_cmp_pin_configure_out(hal_id_t id, uint8_t pin); + +void drv_cmp_enable(hal_id_t id); +void drv_cmp_disable(hal_id_t id); + +void drv_cmp_init(hal_id_t id, cmp_output_t output); +void drv_cmp_deinit(hal_id_t id); + +int drv_cmp_get_out_value(hal_id_t id); + +int drv_cmp_irq_callback_enable(hal_id_t id, cmp_isr_cb_fn cb); +int drv_cmp_irq_callback_disable(hal_id_t id); + +void drv_cmp_irq_enable(hal_id_t id, cmp_int_edge_t edge, int priority); +void drv_cmp_irq_disable(hal_id_t id); + +void drv_cmp_start(hal_id_t id); +void drv_cmp_stop(hal_id_t id); + +void drv_cmp_set_cmp_value(hal_id_t id, uint16_t value); + +uint8_t drv_cmp_read(hal_id_t id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/dac.h b/components/system/include/drivers/chip/dac.h new file mode 100755 index 0000000..d2a3bf4 --- /dev/null +++ b/components/system/include/drivers/chip/dac.h @@ -0,0 +1,33 @@ +#ifndef __DAC_H__ +#define __DAC_H__ + +#include "drivers/chip/_hal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void (*dac_isr_cb_fn)(void); + +void drv_dac_pin_configure(hal_id_t id, uint8_t pin); + +void drv_dac_enable(hal_id_t id); +void drv_dac_disable(hal_id_t id); + +int drv_dac_init(hal_id_t id, unsigned channel_mask, unsigned bit_width); +int drv_dac_deinit(hal_id_t id, unsigned channel_mask); + +int drv_dac_irq_callback_enable(hal_id_t id, dac_isr_cb_fn cb); +int drv_dac_irq_callback_disable(hal_id_t id); + +void drv_dac_irq_enable(hal_id_t id, int priority); +void drv_dac_irq_disable(hal_id_t id); + +void drv_dac_write(hal_id_t id, unsigned channel_id, int value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/dma.h b/components/system/include/drivers/chip/dma.h new file mode 100755 index 0000000..c2f15f9 --- /dev/null +++ b/components/system/include/drivers/chip/dma.h @@ -0,0 +1,42 @@ +#ifndef __DMA_H__ +#define __DMA_H__ + +#include "drivers/chip/_hal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum __packed +{ + _DMA_DIR_MEM_TO_MEM, + _DMA_DIR_PER_TO_MEM, + _DMA_DIR_MEM_TO_PER, +} dma_dir_t; + +typedef void (*dma_isr_cb_fn)(void); + +void drv_dma_enable(hal_id_t id); +void drv_dma_disable(hal_id_t id); + +int drv_dma_init(hal_id_t id, unsigned channel, unsigned prio_level); +int drv_dma_deinit(hal_id_t id, unsigned channel); + +int drv_dma_irq_callback_enable(hal_id_t id, unsigned channel, dma_isr_cb_fn cb); +int drv_dma_irq_callback_disable(hal_id_t id, unsigned channel); + +void drv_dma_irq_enable(hal_id_t id, unsigned channel, int priority); +void drv_dma_irq_disable(hal_id_t id, unsigned channel); + +void drv_dma_set(hal_id_t id, unsigned channel, void *dest, unsigned dest_bit_width, bool dest_inc_mode, const void *src, unsigned src_bit_width, bool src_inc_mode, unsigned count, dma_dir_t dir); +void drv_dma_start(hal_id_t id, unsigned channel); +void drv_dma_stop(hal_id_t id, unsigned channel); + +unsigned drv_dma_get_remain(hal_id_t id, unsigned channel); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/fmc.h b/components/system/include/drivers/chip/fmc.h new file mode 100755 index 0000000..2edd9f7 --- /dev/null +++ b/components/system/include/drivers/chip/fmc.h @@ -0,0 +1,40 @@ +#ifndef __FMC_H__ +#define __FMC_H__ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +void drv_fmc_enable(void); +void drv_fmc_disable(void); + +void drv_fmc_lock(void); +void drv_fmc_unlock(void); + +unsigned drv_fmc_get_rom_base(void); +unsigned drv_fmc_get_rom_size(void); +unsigned drv_fmc_get_sector_base(unsigned addr); +unsigned drv_fmc_get_sector_size(unsigned addr); + +int drv_fmc_sector_erase(unsigned addr); +int drv_fmc_data_write(unsigned addr, const void *src, unsigned size); +int drv_fmc_data_read(void *dest, unsigned addr, unsigned size); + +bool drv_fmc_is_busy(void); + +int drv_fmc_set_read_protection(bool enable); +int drv_fmc_set_write_protection(bool enable, unsigned start_offset, unsigned size); + +bool drv_fmc_is_read_protection(void); +bool drv_fmc_is_write_protection(unsigned addr); + +int drv_fmc_set_wdt(unsigned time_ms); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/gpio.h b/components/system/include/drivers/chip/gpio.h new file mode 100755 index 0000000..77b59bd --- /dev/null +++ b/components/system/include/drivers/chip/gpio.h @@ -0,0 +1,60 @@ +#ifndef __GPIO_H__ +#define __GPIO_H__ + +#include +#include + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum __packed +{ + _GPIO_DIR_IN, + _GPIO_DIR_ANALOG, + _GPIO_DIR_OUT, + _GPIO_DIR_OPEN_DRAIN, +} gpio_dir_t; + +typedef enum __packed +{ + _GPIO_PUD_NONE, + _GPIO_PUD_PULL_UP, + _GPIO_PUD_PULL_DOWN, +} gpio_pud_t; + +typedef enum __packed +{ + _GPIO_EXTI_EDGE_NONE, + _GPIO_EXTI_EDGE_FALL, + _GPIO_EXTI_EDGE_RISE, + _GPIO_EXTI_EDGE_BOTH, +} gpio_exti_edge_t; + +typedef void (*gpio_exti_cb_fn)(void); + +void drv_gpio_enable_all(void); +void drv_gpio_disable_all(void); + +int drv_gpio_pin_configure(uint8_t pin, gpio_dir_t dir, gpio_pud_t pull); +int drv_gpio_pin_read(uint8_t pin); +void drv_gpio_pin_write(uint8_t pin, int value); + +void drv_gpio_exti_callback_enable(gpio_exti_cb_fn cb); +void drv_gpio_exti_callback_disable(void); + +void drv_gpio_exti_irq_enable(uint8_t pin, gpio_exti_edge_t polarity, int priority); +void drv_gpio_exti_irq_disable(uint8_t pin); + +int drv_gpio_exti_get_pin(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/i2c.h b/components/system/include/drivers/chip/i2c.h new file mode 100755 index 0000000..ce1e8a3 --- /dev/null +++ b/components/system/include/drivers/chip/i2c.h @@ -0,0 +1,38 @@ +#ifndef __I2C_H__ +#define __I2C_H__ + +#include "drivers/chip/_hal.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void (*i2c_isr_cb_fn)(void); + +void drv_i2c_pin_configure_sck(hal_id_t id, uint8_t pin); +void drv_i2c_pin_configure_sda(hal_id_t id, uint8_t pin); + +void drv_i2c_enable(hal_id_t id); +void drv_i2c_disable(hal_id_t id); + +void drv_i2c_init(hal_id_t id, uint32_t freq); +void drv_i2c_deinit(hal_id_t id); + +int drv_i2c_write(hal_id_t id, char dev_addr, const void *src, unsigned count); +int drv_i2c_read(hal_id_t id, void *dest, char dev_addr, unsigned count); + +int drv_i2c_irq_callback_enable(hal_id_t id, i2c_isr_cb_fn cb); +int drv_i2c_irq_callback_disable(hal_id_t id); + +void drv_i2c_irq_enable(hal_id_t id, int priority); +void drv_i2c_irq_disable(hal_id_t id); + +bool drv_i2c_is_busy(hal_id_t id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/irq.h b/components/system/include/drivers/chip/irq.h new file mode 100755 index 0000000..ac85ae7 --- /dev/null +++ b/components/system/include/drivers/chip/irq.h @@ -0,0 +1,23 @@ +#ifndef __IRQ_H__ +#define __IRQ_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +unsigned drv_irq_disable(void); +void drv_irq_enable(unsigned nest); + +unsigned drv_irq_get_nest(void); + +void drv_fiq_disable(void); +void drv_fiq_enable(void); + +void drv_irq_hardfault_callback(void ((*cb)(void))); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/lpm.h b/components/system/include/drivers/chip/lpm.h new file mode 100755 index 0000000..33f1add --- /dev/null +++ b/components/system/include/drivers/chip/lpm.h @@ -0,0 +1,19 @@ +#ifndef __LPM_H__ +#define __LPM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +void drv_lpm_wkup_enable(unsigned id); +void drv_lpm_wkup_disable(unsigned id); + +void drv_lpm_sleep(void); +void drv_lpm_deep_sleep(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/misc.h b/components/system/include/drivers/chip/misc.h new file mode 100755 index 0000000..e8a486f --- /dev/null +++ b/components/system/include/drivers/chip/misc.h @@ -0,0 +1,68 @@ +#ifndef __MISC_H__ +#define __MISC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief 让 CPU 空转延时 + * + * @param us 微秒 + */ +void drv_misc_busy_wait(unsigned us); + +/** + * @brief 获取变量的位带映射地址 + * + * @param mem 变量地址 + * @return 位带映射地址。如无返回 NULL + */ +unsigned *drv_misc_bitband(void *mem); + +/** + * @brief 读取 MCU 身份信息的 MD5 值 + * + * @param out[out] 输出 + * @retval 0 成功 + * @retval -1 失败 + */ +int drv_misc_read_id_md5(unsigned char out[16]); + +/** + * @brief 设置中断向量地址 + * + * @param vector 中断向量地址 + */ +void drv_misc_set_vector(void *vector); + +/** + * @brief 获取中断向量地址 + * + * @return void* 中断向量地址 + */ +void *drv_misc_get_vector(void); + +/** + * @brief 根据具体平台,以尽可能快的速度把内存置0 + * + * @param src 内存地址 + * @param len 内存长度 + */ +void mem_reset(void *src, unsigned len); + +/** + * @brief 根据具体平台,以尽可能快的速度复制内存数据 + * + * @param dest[out] 目录内存 + * @param src 源内存 + * @param len 长度 + */ +void mem_cpy(void *dest, const void *src, unsigned len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/phy.h b/components/system/include/drivers/chip/phy.h new file mode 100755 index 0000000..74a403e --- /dev/null +++ b/components/system/include/drivers/chip/phy.h @@ -0,0 +1,48 @@ +#ifndef __PHY_H__ +#define __PHY_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Defines the PHY link speed. This is align with the speed for MAC. */ +enum phy_speed +{ + PHY_SPEED_10M = 0U, /* PHY 10M speed. */ + PHY_SPEED_100M /* PHY 100M speed. */ +}; + +/* Defines the PHY link duplex. */ +enum phy_duplex +{ + PHY_HALF_DUPLEX = 0U, /* PHY half duplex. */ + PHY_FULL_DUPLEX /* PHY full duplex. */ +}; + +/*! @brief Defines the PHY loopback mode. */ +enum phy_loop +{ + PHY_LOCAL_LOOP = 0U, /* PHY local loopback. */ + PHY_REMOTE_LOOP /* PHY remote loopback. */ +}; + +typedef struct +{ + enum phy_speed speed; + enum phy_duplex duplex; + enum phy_loop loop; +} phy_param_t; + +int drv_phy_init(unsigned phy_addr, unsigned src_clock_hz, phy_param_t *param); +int drv_phy_read(unsigned reg, unsigned *data); +int drv_phy_write(unsigned reg, unsigned data); +int drv_phy_loopback(unsigned mode, unsigned speed, unsigned enable); +int drv_phy_get_link_status(unsigned *status); +int drv_phy_get_link_speed_duplex(unsigned *speed, unsigned *duplex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/spi.h b/components/system/include/drivers/chip/spi.h new file mode 100755 index 0000000..38ca0a1 --- /dev/null +++ b/components/system/include/drivers/chip/spi.h @@ -0,0 +1,74 @@ +#ifndef __SPI_H__ +#define __SPI_H__ + +#include "drivers/chip/_hal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum __packed +{ + _SPI_MODE_0, // clock polarity is low level and phase is first edge + _SPI_MODE_1, // clock polarity is low level and phase is second edge + _SPI_MODE_2, // clock polarity is high level and phase is first edge + _SPI_MODE_3, // clock polarity is high level and phase is second edge +} spi_mode_t; + +typedef struct +{ + bool ms; // false: master, true: slave + spi_mode_t mode; + bool quad_mode; + bool lsb_first; + unsigned freq; +} spi_init_t; + +typedef void (*spi_isr_cb_fn)(void); + +void drv_spi_pin_configure_nss(hal_id_t id, uint8_t pin); +void drv_spi_pin_configure_sck(hal_id_t id, uint8_t pin); +void drv_spi_pin_configure_mosi(hal_id_t id, uint8_t pin); +void drv_spi_pin_configure_miso(hal_id_t id, uint8_t pin); +void drv_spi_pin_configure_io2(hal_id_t id, uint8_t pin); +void drv_spi_pin_configure_io3(hal_id_t id, uint8_t pin); + +void drv_spi_enable(hal_id_t id); +void drv_spi_disable(hal_id_t id); + +void drv_spi_init(hal_id_t id, const spi_init_t *param); +void drv_spi_deinit(hal_id_t id); + +int drv_spi_poll_read(hal_id_t id, void *dest); +int drv_spi_poll_write(hal_id_t id, uint8_t data); + +int drv_spi_irq_callback_enable(hal_id_t id, spi_isr_cb_fn cb); +int drv_spi_irq_callback_disable(hal_id_t id); + +void drv_spi_irq_enable(hal_id_t id, bool rx, bool tx, int priority); +void drv_spi_irq_disable(hal_id_t id, bool rx, bool tx); + +bool drv_spi_is_tx_ready(hal_id_t id); +bool drv_spi_is_rx_ready(hal_id_t id); + +void drv_spi_dma_enable(hal_id_t id, bool tx_dma, bool rx_dma, unsigned prio_level); +void drv_spi_dma_disable(hal_id_t id, bool tx_dma, bool rx_dma); + +int drv_spi_dma_irq_callback_enable(hal_id_t id, spi_isr_cb_fn cb); +int drv_spi_dma_irq_callback_disable(hal_id_t id); + +void drv_spi_dma_irq_enable(hal_id_t id, int priority); +void drv_spi_dma_irq_disable(hal_id_t id); + +int drv_spi_dma_set_read(hal_id_t id, void *dest, unsigned len); +int drv_spi_dma_set_write(hal_id_t id, const void *src, unsigned len); +void drv_spi_dma_start(hal_id_t id); + +bool drv_spi_dma_is_busy(hal_id_t id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/tick.h b/components/system/include/drivers/chip/tick.h new file mode 100755 index 0000000..03ff8b8 --- /dev/null +++ b/components/system/include/drivers/chip/tick.h @@ -0,0 +1,18 @@ +#ifndef __TICK_H__ +#define __TICK_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +void drv_tick_enable(unsigned freq); +void drv_tick_disable(void); + +unsigned drv_tick_get_counter(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/tim.h b/components/system/include/drivers/chip/tim.h new file mode 100755 index 0000000..669c21f --- /dev/null +++ b/components/system/include/drivers/chip/tim.h @@ -0,0 +1,136 @@ +#ifndef __TIM_H__ +#define __TIM_H__ + +#include "drivers/chip/_hal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @param channel_id: 0..n + * @param channel_mask: 1 << 0, 1 << 1, ... 1 << n + */ + +typedef void (*tim_isr_cb_fn)(void); + +/** @defgroup time base + * @{ + */ + +void drv_tim_enable(hal_id_t id); +void drv_tim_disable(hal_id_t id); + +void drv_tim_init(hal_id_t id, unsigned period_ns, unsigned reload_enable); +void drv_tim_deinit(hal_id_t id); + +int drv_tim_irq_callback_enable(hal_id_t id, tim_isr_cb_fn cb); +int drv_tim_irq_callback_disable(hal_id_t id); + +void drv_tim_irq_enable(hal_id_t id, int priority); +void drv_tim_irq_disable(hal_id_t id); + +void drv_tim_base_start(hal_id_t id); +void drv_tim_base_stop(hal_id_t id); + +unsigned drv_tim_get_period_ns(hal_id_t id); +unsigned drv_tim_get_cost_ns(hal_id_t id); +unsigned drv_tim_get_remain_ns(hal_id_t id); + +/** + * @} + */ + +/** @defgroup PWM + * @{ + */ + +void drv_pwm_pin_configure(hal_id_t id, uint8_t pin); + +void drv_pwm_enable(hal_id_t id); +void drv_pwm_disable(hal_id_t id); + +void drv_pwm_init(hal_id_t id, unsigned period_ns, unsigned reload_enable); +void drv_pwm_deinit(hal_id_t id); + +int drv_pwm_irq_callback_enable(hal_id_t id, tim_isr_cb_fn cb); +int drv_pwm_irq_callback_disable(hal_id_t id); + +void drv_pwm_irq_enable(hal_id_t id, unsigned channel_mask, int priority); +void drv_pwm_irq_disable(hal_id_t id, unsigned channel_mask); + +void drv_pwm_start(hal_id_t id, unsigned channel_mask, unsigned active_level, unsigned pulse_ns); +void drv_pwm_stop(hal_id_t id, unsigned channel_mask, unsigned active_level); + +bool drv_pwm_is_busy(hal_id_t id); + +/** + * @} + */ + +/** @defgroup one pluse + * @{ + */ + +void drv_pluse_pin_configure(hal_id_t id, uint8_t pin); + +void drv_pluse_enable(hal_id_t id); +void drv_pluse_disable(hal_id_t id); + +void drv_pluse_init(hal_id_t id); +void drv_pluse_deinit(hal_id_t id); + +int drv_pluse_irq_callback_enable(hal_id_t id, tim_isr_cb_fn cb); +int drv_pluse_irq_callback_disable(hal_id_t id); + +void drv_pluse_irq_enable(hal_id_t id, unsigned channel_mask, int priority); +void drv_pluse_irq_disable(hal_id_t id, unsigned channel_mask); + +void drv_pluse_set(hal_id_t id, unsigned channel_mask, unsigned active_level, unsigned period_ns, unsigned pulse_ns); + +bool drv_pluse_is_busy(hal_id_t id, unsigned channel); + +/** + * @} + */ + +/** @defgroup input capture + * @{ + */ + +typedef enum __packed +{ + _IC_EDGE_RISING, + _IC_EDGE_FALLING, + _IC_EDGE_BOTH, +} capture_edge_t; + +void drv_capture_pin_configure(hal_id_t id, uint8_t pin); + +void drv_capture_enable(hal_id_t id); +void drv_capture_disable(hal_id_t id); + +void drv_capture_init(hal_id_t id, unsigned channel_mask, capture_edge_t polarity); +void drv_capture_deinit(hal_id_t id); + +int drv_capture_irq_callback_enable(hal_id_t id, tim_isr_cb_fn cb); +int drv_capture_irq_callback_disable(hal_id_t id); + +void drv_capture_irq_enable(hal_id_t id, unsigned channel_mask, int priority); +void drv_capture_irq_disable(hal_id_t id, unsigned channel_mask); + +void drv_capture_start(hal_id_t id, unsigned channel_mask); +void drv_capture_stop(hal_id_t id, unsigned channel_mask); + +unsigned drv_capture_get_cap_value(hal_id_t id, uint8_t channel_id); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/uart.h b/components/system/include/drivers/chip/uart.h new file mode 100755 index 0000000..0992a1e --- /dev/null +++ b/components/system/include/drivers/chip/uart.h @@ -0,0 +1,56 @@ +#ifndef __UART_H__ +#define __UART_H__ + +#include "drivers/chip/_hal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct +{ + uint32_t br; + uint8_t length; /* 7..9 */ + uint8_t stop_bit; /* 1 or 2 */ + uint8_t parity; /* 0: none 1: even 2: odd */ + uint8_t flow_ctrl; /* 0: none 1: RTS 2: CTS 3: RTS+CTS */ + +} uart_param_t; + +typedef void (*uart_isr_cb_fn)(void); + +void drv_uart_pin_configure_txd(hal_id_t id, uint8_t pin); +void drv_uart_pin_configure_rxd(hal_id_t id, uint8_t pin); + +void drv_uart_enable(hal_id_t id); +void drv_uart_disable(hal_id_t id); + +void drv_uart_init(hal_id_t id, const uart_param_t *param); +void drv_uart_deinit(hal_id_t id); + +void drv_uart_set_br(hal_id_t id, unsigned clk, unsigned baudrate); +unsigned drv_uart_get_br(hal_id_t id, unsigned clk); + +int drv_uart_poll_read(hal_id_t id, void *data); +int drv_uart_poll_write(hal_id_t id, uint8_t data); + +int drv_uart_fifo_read(hal_id_t id, void *data, int size); +int drv_uart_fifo_fill(hal_id_t id, const void *data, int size); + +int drv_uart_irq_callback_enable(hal_id_t id, uart_isr_cb_fn cb); +int drv_uart_irq_callback_disable(hal_id_t id); + +void drv_uart_irq_enable(hal_id_t id, bool rx, bool tx, int priority); +void drv_uart_irq_disable(hal_id_t id, bool rx, bool tx); + +bool drv_uart_wait_tx_busy(hal_id_t id); + +bool drv_uart_is_tx_ready(hal_id_t id); +bool drv_uart_is_rx_ready(hal_id_t id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/drivers/chip/wdt.h b/components/system/include/drivers/chip/wdt.h new file mode 100755 index 0000000..ac87f6c --- /dev/null +++ b/components/system/include/drivers/chip/wdt.h @@ -0,0 +1,32 @@ +#ifndef __WDT_H__ +#define __WDT_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum +{ + _WDT_MODE_RESET, + _WDT_MODE_INTERRUPT_RESET +} wdt_mode_t; + +void wdt_enable(void); +void wdt_disable(void); + +void wdt_irq_enable(int priority); +void wdt_irq_disable(void); + +void wdt_set_config(wdt_mode_t mode, int int_ms, int reset_ms); + +void wdt_reload(void); +unsigned wdt_get_time_cost(void); + +void wdt_clear_int_flag(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/list/dlist.h b/components/system/include/list/dlist.h new file mode 100755 index 0000000..98897b8 --- /dev/null +++ b/components/system/include/list/dlist.h @@ -0,0 +1,285 @@ +/** + * @file dlist.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __DUPLEXLIST_H__ +#define __DUPLEXLIST_H__ + +#include "sys_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef LIST_NODE_TYPE +#define LIST_NODE_TYPE 0 /* 0 -- 使用指针连结(推荐);1 -- 使用偏移量连结,内存的地址允许被迁移并继续管理(推荐) */ +#endif + +#if LIST_NODE_TYPE == 1 +typedef int list_node_t; +#else +typedef void * list_node_t; +#endif + +typedef struct +{ + list_node_t next; + list_node_t prev; +} dlist_node_t; +typedef struct +{ + list_node_t head; +} dlist_t; + +__static_inline void dlist_init_list (dlist_t *list); +__static_inline void dlist_init_node (dlist_node_t *node); + +__static_inline int dlist_insert_font (dlist_t *list, dlist_node_t *node); +__static_inline int dlist_insert_tail (dlist_t *list, dlist_node_t *node); +__static_inline int dlist_remove (dlist_t *list, dlist_node_t *node); + +__static_inline int dlist_insert_next (dlist_t *list, dlist_node_t *tar_node, dlist_node_t *new_node); +__static_inline int dlist_insert_prev (dlist_t *list, dlist_node_t *tar_node, dlist_node_t *new_node); + +__static_inline void dlist_set_next (dlist_t *list); + +__static_inline dlist_node_t *dlist_take_head (dlist_t *list); +__static_inline dlist_node_t *dlist_take_tail (dlist_t *list); + +__static_inline dlist_node_t *dlist_peek_head (dlist_t *list); +__static_inline dlist_node_t *dlist_peek_tail (dlist_t *list); + +__static_inline dlist_node_t *dlist_peek_next (dlist_t *list, dlist_node_t *node); +__static_inline dlist_node_t *dlist_peek_prev (dlist_t *list, dlist_node_t *node); + +__static_inline dlist_node_t *dlist_peek_next_loop (dlist_t *list, dlist_node_t *node); +__static_inline dlist_node_t *dlist_peek_prev_loop (dlist_t *list, dlist_node_t *node); + +__static_inline int dlist_is_pending (dlist_node_t *node); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if LIST_NODE_TYPE == 1 +#define DLIST_SET(Value1, Value2) (Value1 = Value2 == 0 ? 0 : ((int)Value2 - (int)&Value1) | 1) +#define DLIST_GET(Value) ((dlist_node_t*)(Value == 0 ? 0 : ((int)&Value + Value) & ~1)) +#else +#define DLIST_SET(Value1, Value2) (Value1 = (list_node_t)Value2) +#define DLIST_GET(Value) ((dlist_node_t*)(Value)) +#endif + +__static_inline void dlist_init_list(dlist_t *list) +{ + DLIST_SET(list->head, 0); +} + +__static_inline void dlist_init_node(dlist_node_t *node) +{ + DLIST_SET(node->next, 0); + DLIST_SET(node->prev, 0); +} + +__static_inline int dlist_insert_font(dlist_t *list, dlist_node_t *node) +{ + if (dlist_insert_tail(list, node) == 0) + { + DLIST_SET(list->head, node); + return 0; + } + else + { + return -1; + } +} + +__static_inline int dlist_insert_tail(dlist_t *list, dlist_node_t *node) +{ + if (DLIST_GET(node->next) != NULL || DLIST_GET(node->prev) != NULL) + { + return -1; + } + + dlist_node_t *first_node = DLIST_GET(list->head); + if (first_node == NULL) + { + /* 直接设置链头 */ + DLIST_SET(node->next, node); + DLIST_SET(node->prev, node); + DLIST_SET(list->head, node); + } + else + { + dlist_node_t *first_node_prev = DLIST_GET(first_node->prev); + + DLIST_SET(node->next, first_node); + DLIST_SET(node->prev, first_node_prev); + DLIST_SET(first_node_prev->next, node); + DLIST_SET(first_node->prev, node); + } + + return 0; +} + +__static_inline int dlist_remove(dlist_t *list, dlist_node_t *node) +{ + if (node == NULL || list == NULL) + { + return -1; + } + dlist_node_t *first_node = DLIST_GET(list->head); + dlist_node_t *node_next = DLIST_GET(node->next); + if (node_next == NULL || first_node == NULL) + { + return -1; + } + + if (node_next == node) // 是最后一节点 + { + DLIST_SET(list->head, 0); + } + else // 不是最后一节点 + { + dlist_node_t *node_prev = DLIST_GET(node->prev); + if (first_node == node) + { + DLIST_SET(list->head, node_next); // 链头指向下一节点 + } + DLIST_SET(node_prev->next, node_next); + DLIST_SET(node_next->prev, node_prev); + } + + DLIST_SET(node->next, 0); + DLIST_SET(node->prev, 0); + + return 0; +} + +__static_inline int dlist_insert_next(dlist_t *list, dlist_node_t *tar_node, dlist_node_t *new_node) +{ + dlist_node_t *tar_node_next = DLIST_GET(tar_node->next); + if (DLIST_GET(list->head) == NULL || tar_node_next == NULL || DLIST_GET(new_node->next) != NULL) + { + return -1; + } + + DLIST_SET(new_node->next, tar_node_next); + DLIST_SET(new_node->prev, tar_node); + DLIST_SET(tar_node_next->prev, new_node); + DLIST_SET(tar_node->next, new_node); + + return 0; +} + +__static_inline int dlist_insert_prev(dlist_t *list, dlist_node_t *tar_node, dlist_node_t *new_node) +{ + dlist_node_t *list_head = DLIST_GET(list->head); + if (list_head == NULL || DLIST_GET(tar_node->next) == NULL || DLIST_GET(new_node->next) != NULL) + { + return -1; + } + + DLIST_SET(new_node->next, tar_node); + DLIST_SET(new_node->prev, DLIST_GET(tar_node->prev)); + DLIST_SET(DLIST_GET(tar_node->prev)->next, new_node); + DLIST_SET(tar_node->prev, new_node); + if (tar_node == list_head) // tar_node 是首个节点 + { + DLIST_SET(list->head, new_node); + } + + return 0; +} + +__static_inline void dlist_set_next(dlist_t *list) +{ + if (list != NULL) + { + dlist_node_t *node = DLIST_GET(list->head); + if (node != NULL) + { + DLIST_SET(list->head, DLIST_GET(node->next)); // 链头指向下一节点 + } + } +} + +__static_inline dlist_node_t *dlist_take_head(dlist_t *list) +{ + dlist_node_t *ret = DLIST_GET(list->head); + dlist_remove(list, ret); + return ret; +} + +__static_inline dlist_node_t *dlist_take_tail(dlist_t *list) +{ + dlist_node_t *ret = dlist_peek_tail(list); + dlist_remove(list, ret); + return ret; +} + +__static_inline dlist_node_t *dlist_peek_head(dlist_t *list) +{ + return DLIST_GET(list->head); +} + +__static_inline dlist_node_t *dlist_peek_tail(dlist_t *list) +{ + dlist_node_t *list_head = DLIST_GET(list->head); + if (list_head == NULL) + { + return NULL; + } + else + { + return DLIST_GET(list_head->prev); + } +} + +__static_inline dlist_node_t *dlist_peek_next(dlist_t *list, dlist_node_t *node) +{ + dlist_node_t *ret = DLIST_GET(node->next); + if (dlist_peek_head(list) == ret) + { + ret = NULL; + } + return ret; +} + +__static_inline dlist_node_t *dlist_peek_prev(dlist_t *list, dlist_node_t *node) +{ + dlist_node_t *ret = DLIST_GET(node->prev); + if (dlist_peek_tail(list) == ret) + { + ret = NULL; + } + return ret; +} + +__static_inline dlist_node_t *dlist_peek_next_loop(dlist_t *list, dlist_node_t *node) +{ + (void)list; + return DLIST_GET(node->next); +} + +__static_inline dlist_node_t *dlist_peek_prev_loop(dlist_t *list, dlist_node_t *node) +{ + (void)list; + return DLIST_GET(node->prev); +} + +__static_inline int dlist_is_pending(dlist_node_t *node) +{ + return (DLIST_GET(node->next) != NULL); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/list/flist.h b/components/system/include/list/flist.h new file mode 100755 index 0000000..9228965 --- /dev/null +++ b/components/system/include/list/flist.h @@ -0,0 +1,373 @@ +/** + * @file flist.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __FULLLIST_H__ +#define __FULLLIST_H__ + +#include "sys_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef LIST_NODE_TYPE +#define LIST_NODE_TYPE 0 /* 0 -- 使用指针连结(推荐);1 -- 使用偏移量连结,内存的地址允许被迁移并继续管理(推荐) */ +#endif + +#if LIST_NODE_TYPE == 1 +typedef int list_node_t; +#else +typedef void * list_node_t; +#endif + +typedef struct +{ + list_node_t next; + list_node_t prev; + list_node_t list; +} flist_node_t; +typedef struct +{ + list_node_t head; +} flist_t; + +__static_inline void flist_init_list (flist_t *list); +__static_inline void flist_init_node (flist_node_t *node); + +__static_inline int flist_insert_font (flist_t *list, flist_node_t *node); +__static_inline int flist_insert_tail (flist_t *list, flist_node_t *node); +__static_inline int flist_remove (flist_t *list, flist_node_t *node); + +__static_inline int flist_insert_next (flist_node_t *tar_node, flist_node_t *new_node); +__static_inline int flist_insert_prev (flist_node_t *tar_node, flist_node_t *new_node); +__static_inline int flist_node_free (flist_node_t *node); + +__static_inline void flist_set_next (flist_t *list); + +__static_inline flist_node_t *flist_take_head (flist_t *list); +__static_inline flist_node_t *flist_take_tail (flist_t *list); + +__static_inline flist_node_t *flist_peek_head (flist_t *list); +__static_inline flist_node_t *flist_peek_tail (flist_t *list); + +__static_inline flist_t *flist_peek_list (flist_node_t *node); +__static_inline flist_node_t *flist_peek_first (flist_node_t *node); +__static_inline flist_node_t *flist_peek_next (flist_node_t *node); +__static_inline flist_node_t *flist_peek_prev (flist_node_t *node); + +__static_inline flist_node_t *flist_peek_next_loop (flist_node_t *node); +__static_inline flist_node_t *flist_peek_prev_loop (flist_node_t *node); + +__static_inline int flist_is_pending (flist_node_t *node); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if LIST_NODE_TYPE == 1 +#define FLIST_SET(Value1, Value2) (Value1 = Value2 == 0 ? 0 : ((int)Value2 - (int)&Value1) | 1) +#define FLIST_GET(Value) ((flist_node_t*)(Value == 0 ? 0 : ((int)&Value + Value) & ~1)) +#else +#define FLIST_SET(Value1, Value2) (Value1 = (list_node_t)Value2) +#define FLIST_GET(Value) ((flist_node_t*)(Value)) +#endif + +__static_inline void flist_init_list(flist_t *list) +{ + FLIST_SET(list->head, 0); +} + +__static_inline void flist_init_node(flist_node_t *node) +{ + FLIST_SET(node->list, 0); + FLIST_SET(node->next, 0); + FLIST_SET(node->prev, 0); +} + +__static_inline int flist_insert_font(flist_t *list, flist_node_t *node) +{ + flist_insert_tail(list, node); + FLIST_SET(list->head, node); + return 0; +} + +__static_inline int flist_insert_tail(flist_t *list, flist_node_t *node) +{ + if (FLIST_GET(node->list) != NULL) + { + flist_node_free(node); + } + + FLIST_SET(node->list, list); + + flist_node_t *first_node = FLIST_GET(list->head); + if (first_node == NULL) + { + /* 直接设置链头 */ + FLIST_SET(node->next, node); + FLIST_SET(node->prev, node); + FLIST_SET(list->head, node); + } + else + { + flist_node_t *first_node_prev = FLIST_GET(first_node->prev); + FLIST_SET(node->next, first_node); + FLIST_SET(node->prev, first_node_prev); + FLIST_SET(first_node_prev->next, node); + FLIST_SET(first_node->prev, node); + } + + return 0; +} + +__static_inline int flist_remove(flist_t *list, flist_node_t *node) +{ + if (node == NULL || list == NULL) + { + return -1; + } + + flist_node_t *first_node = FLIST_GET(list->head); + flist_node_t *node_next = FLIST_GET(node->next); + if (node_next == NULL || first_node == NULL) + { + return -1; + } + + if (node_next == node) // 是最后一节点 + { + FLIST_SET(list->head, 0); + } + else // 不是最后一节点 + { + if (first_node == node) + { + FLIST_SET(list->head, node_next); // 链头指向下一节点 + } + flist_node_t *node_prev = FLIST_GET(node->prev); + FLIST_SET(node_prev->next, node_next); + FLIST_SET(node_next->prev, node_prev); + } + + FLIST_SET(node->list, 0); + FLIST_SET(node->next, 0); + FLIST_SET(node->prev, 0); + + return 0; +} + +__static_inline int flist_insert_next(flist_node_t *tar_node, flist_node_t *new_node) +{ + flist_node_t *tar_node_list = FLIST_GET(tar_node->list); + if (tar_node_list == NULL) + { + return -1; + } + + if (tar_node == new_node) + { + flist_node_t *src_node_next = FLIST_GET(new_node->next); + if (src_node_next != new_node) // 不是最后一节点 + { + flist_t *list = (flist_t *)FLIST_GET(new_node->list); + flist_node_t *first_node = FLIST_GET(list->head); + if (first_node == new_node) + { + FLIST_SET(list->head, src_node_next); // 链头指向下一节点 + } + } + } + else + { + if (FLIST_GET(new_node->list) != NULL) + { + flist_node_free(new_node); + } + flist_node_t *tar_node_next = FLIST_GET(tar_node->next); + FLIST_SET(new_node->list, tar_node_list); + FLIST_SET(new_node->next, tar_node_next); + FLIST_SET(new_node->prev, tar_node); + FLIST_SET(tar_node_next->prev, new_node); + FLIST_SET(tar_node->next, new_node); + } + + return 0; +} + +__static_inline int flist_insert_prev(flist_node_t *tar_node, flist_node_t *new_node) +{ + flist_node_t *tar_node_list = FLIST_GET(tar_node->list); + if (tar_node_list == NULL) + { + return -1; + } + + if (tar_node == new_node) + { + flist_node_t *src_node_next = FLIST_GET(new_node->next); + if (src_node_next != new_node) // 不是最后一节点 + { + flist_t *list = (flist_t *)FLIST_GET(new_node->list); + flist_node_t *first_node = (flist_node_t *)FLIST_GET(list->head); + if (first_node == new_node) + { + FLIST_SET(list->head, src_node_next); // 链头指向下一节点 + } + } + } + else + { + if (FLIST_GET(new_node->list) != NULL) + { + flist_node_free(new_node); + } + flist_node_t *tar_node_prev = FLIST_GET(tar_node->prev); + FLIST_SET(new_node->list, tar_node_list); + FLIST_SET(new_node->next, tar_node); + FLIST_SET(new_node->prev, tar_node_prev); + FLIST_SET(tar_node_prev->next, new_node); + FLIST_SET(tar_node->prev, new_node); + flist_t *list = (flist_t *)tar_node_list; + if (tar_node == FLIST_GET(list->head)) // tar_node 是首个节点 + { + FLIST_SET(list->head, new_node); + } + } + + return 0; +} + +__static_inline int flist_node_free(flist_node_t *node) +{ + if (node == NULL) + { + return -1; + } + return flist_remove((flist_t *)FLIST_GET(node->list), node); +} + +__static_inline void flist_set_next(flist_t *list) +{ + if (list != NULL) + { + flist_node_t *node = FLIST_GET(list->head); + if (node != NULL) + { + FLIST_SET(list->head, FLIST_GET(node->next)); // 链头指向下一节点 + } + } +} + +__static_inline flist_node_t *flist_take_head(flist_t *list) +{ + flist_node_t *ret = FLIST_GET(list->head); + flist_remove(list, ret); + return ret; +} + +__static_inline flist_node_t *flist_take_tail(flist_t *list) +{ + flist_node_t *ret = flist_peek_tail(list); + flist_remove(list, ret); + return ret; +} + +__static_inline flist_node_t *flist_peek_head(flist_t *list) +{ + return FLIST_GET(list->head); +} + +__static_inline flist_node_t *flist_peek_tail(flist_t *list) +{ + flist_node_t *list_head = FLIST_GET(list->head); + if (list_head == NULL) + { + return NULL; + } + else + { + return FLIST_GET(list_head->prev); + } +} + +__static_inline flist_t *flist_peek_list(flist_node_t *node) +{ + return (flist_t *)FLIST_GET(node->list); +} + +__static_inline flist_node_t *flist_peek_first(flist_node_t *node) +{ + flist_node_t *node_list = FLIST_GET(node->list); + if (node_list == NULL) + { + return NULL; + } + else + { + return FLIST_GET(((flist_t *)node_list)->head); + } +} + +__static_inline flist_node_t *flist_peek_next(flist_node_t *node) +{ + flist_t *pFlist = (flist_t *)FLIST_GET(node->list); + if (pFlist == NULL) + { + return NULL; + } + else + { + flist_node_t *ret = FLIST_GET(node->next); + if (flist_peek_head(pFlist) == ret) + { + ret = NULL; + } + return ret; + } +} + +__static_inline flist_node_t *flist_peek_prev(flist_node_t *node) +{ + flist_t *pFlist = (flist_t *)FLIST_GET(node->list); + if (pFlist == NULL) + { + return NULL; + } + else + { + flist_node_t *ret = FLIST_GET(node->prev); + if (flist_peek_tail(pFlist) == ret) + { + ret = NULL; + } + return ret; + } +} + +__static_inline flist_node_t *flist_peek_next_loop(flist_node_t *node) +{ + return FLIST_GET(node->next); +} + +__static_inline flist_node_t *flist_peek_prev_loop(flist_node_t *node) +{ + return FLIST_GET(node->prev); +} + +__static_inline int flist_is_pending(flist_node_t *node) +{ + return (FLIST_GET(node->next) != NULL); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/list/pslist.h b/components/system/include/list/pslist.h new file mode 100755 index 0000000..e38ec54 --- /dev/null +++ b/components/system/include/list/pslist.h @@ -0,0 +1,248 @@ +/** + * @file pslist.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __PSLIST_H__ +#define __PSLIST_H__ + +#include "sys_types.h" + +typedef struct _psnode sys_psnode_t; + +typedef struct _pslist sys_pslist_t; + +struct _psnode +{ + sys_psnode_t *const *next; +}; + +struct _pslist +{ + sys_psnode_t *const *head; + sys_psnode_t *const *tail; +}; + +#define _CONTAINER_OF(PTR, TYPE, MEMBER) ((TYPE *)&((uint8_t *)PTR)[-(int)&((TYPE *)0)->MEMBER]) + +#define SYS_PSLIST_FOR_EACH_NODE(__sl, __sn) \ + for (__sn = sys_pslist_peek_head(__sl); __sn; \ + __sn = sys_pslist_peek_next(__sn)) + +#define SYS_PSLIST_ITERATE_FROM_NODE(__sl, __sn) \ + for (__sn = __sn ? sys_pslist_peek_next_no_check(__sn) \ + : sys_pslist_peek_head(__sl); \ + __sn; \ + __sn = sys_pslist_peek_next(__sn)) + +#define SYS_PSLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \ + for (__sn = sys_pslist_peek_head(__sl), \ + __sns = sys_pslist_peek_next(__sn); \ + __sn; \ + __sn = __sns, __sns = sys_pslist_peek_next(__sn)) + +#define SYS_PSLIST_CONTAINER(__ln, __cn, __n) \ + ((__ln) ? _CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL) + +#define SYS_PSLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n) \ + SYS_PSLIST_CONTAINER(sys_pslist_peek_head(__sl), __cn, __n) + +#define SYS_PSLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n) \ + SYS_PSLIST_CONTAINER(sys_pslist_peek_tail(__sl), __cn, __n) + +#define SYS_PSLIST_PEEK_NEXT_CONTAINER(__cn, __n) \ + ((__cn) ? SYS_PSLIST_CONTAINER(sys_pslist_peek_next(&((__cn)->__n)), \ + __cn, __n) \ + : NULL) + +#define SYS_PSLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \ + for (__cn = SYS_PSLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); \ + __cn; \ + __cn = SYS_PSLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +#define SYS_PSLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n) \ + for (__cn = SYS_PSLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \ + __cns = SYS_PSLIST_PEEK_NEXT_CONTAINER(__cn, __n); \ + __cn; \ + __cn = __cns, __cns = SYS_PSLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +__static_inline void sys_pslist_init(sys_pslist_t *list) +{ + list->head = NULL; + list->tail = NULL; +} + +#define SYS_PSLIST_STATIC_INIT(ptr_to_list) \ + { \ + NULL, NULL \ + } + +__static_inline bool sys_pslist_is_empty(sys_pslist_t *list) +{ + return (!list->head); +} + +__static_inline sys_psnode_t *const *sys_pslist_peek_head(sys_pslist_t *list) +{ + return list->head; +} + +__static_inline sys_psnode_t *const *sys_pslist_peek_tail(sys_pslist_t *list) +{ + return list->tail; +} + +__static_inline sys_psnode_t *const *sys_pslist_peek_next_no_check(sys_psnode_t *const *node) +{ + return (*node)->next; +} + +__static_inline sys_psnode_t *const *sys_pslist_peek_next(sys_psnode_t *const *node) +{ + return node ? sys_pslist_peek_next_no_check(node) : NULL; +} + +__static_inline void sys_pslist_prepend(sys_pslist_t *list, + sys_psnode_t *const *node) +{ + (*node)->next = list->head; + list->head = node; + + if (!list->tail) + { + list->tail = list->head; + } +} + +__static_inline void sys_pslist_append(sys_pslist_t *list, + sys_psnode_t *const *node) +{ + (*node)->next = NULL; + + if (!list->tail) + { + list->tail = node; + list->head = node; + } + else + { + (*(list->tail))->next = node; + list->tail = node; + } +} + +__static_inline void sys_pslist_append_list(sys_pslist_t *list, + sys_psnode_t *const *head, sys_psnode_t *const *tail) +{ + if (!list->tail) + { + list->head = head; + list->tail = tail; + } + else + { + (*(list->tail))->next = head; + list->tail = tail; + } +} + +__static_inline void sys_pslist_merge_pslist(sys_pslist_t *list, + sys_pslist_t *list_to_append) +{ + sys_pslist_append_list(list, list_to_append->head, + list_to_append->tail); + sys_pslist_init(list_to_append); +} + +__static_inline void sys_pslist_insert(sys_pslist_t *list, + sys_psnode_t *const *prev, + sys_psnode_t *const *node) +{ + if (!prev) + { + sys_pslist_prepend(list, node); + } + else if (!(*prev)->next) + { + sys_pslist_append(list, node); + } + else + { + (*node)->next = (*prev)->next; + (*prev)->next = node; + } +} + +__static_inline sys_psnode_t *const *sys_pslist_get_not_empty(sys_pslist_t *list) +{ + sys_psnode_t *const *node = list->head; + + list->head = (*node)->next; + if (list->tail == node) + { + list->tail = list->head; + } + + return node; +} + +__static_inline sys_psnode_t *const *sys_pslist_get(sys_pslist_t *list) +{ + return sys_pslist_is_empty(list) ? NULL : sys_pslist_get_not_empty(list); +} + +__static_inline void sys_pslist_remove(sys_pslist_t *list, + sys_psnode_t *const *prev_node, + sys_psnode_t *const *node) +{ + if (!prev_node) + { + list->head = (*node)->next; + + /* Was node also the tail? */ + if (list->tail == node) + { + list->tail = list->head; + } + } + else + { + (*prev_node)->next = (*node)->next; + + /* Was node the tail? */ + if (list->tail == node) + { + list->tail = prev_node; + } + } + + (*node)->next = NULL; +} + +__static_inline bool sys_pslist_find_and_remove(sys_pslist_t *list, + sys_psnode_t *const *node) +{ + sys_psnode_t *const *prev = NULL; + sys_psnode_t *const *test; + + SYS_PSLIST_FOR_EACH_NODE(list, test) + { + if (test == node) + { + sys_pslist_remove(list, prev, node); + return true; + } + + prev = test; + } + + return false; +} + +#endif diff --git a/components/system/include/list/slist.h b/components/system/include/list/slist.h new file mode 100755 index 0000000..09c76f8 --- /dev/null +++ b/components/system/include/list/slist.h @@ -0,0 +1,412 @@ +/** + * @file slist.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __SINGLELIST_H__ +#define __SINGLELIST_H__ + +#include "sys_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef LIST_NODE_TYPE +#define LIST_NODE_TYPE 0 /* 0 -- 使用指针连结(推荐);1 -- 使用偏移量连结,内存的地址允许被迁移并继续管理(推荐) */ +#endif + +#if LIST_NODE_TYPE == 1 +typedef int list_node_t; +#else +typedef void * list_node_t; +#endif + +typedef struct +{ + list_node_t next; +} slist_node_t; +typedef struct +{ + list_node_t head; + list_node_t tail; +} slist_t; + +__static_inline void slist_init_list (slist_t *list); +__static_inline void slist_init_node (slist_node_t *node); + +__static_inline int slist_insert_font (slist_t *list, slist_node_t *node); +__static_inline int slist_insert_tail (slist_t *list, slist_node_t *node); +__static_inline int slist_remove (slist_t *list, slist_node_t *node); +__static_inline int slist_remove_next (slist_t *list, slist_node_t *prev_node, slist_node_t *remove_node); + +__static_inline int slist_insert_next (slist_t *list, slist_node_t *tar_node, slist_node_t *new_node); +__static_inline int slist_insert_prev (slist_t *list, slist_node_t *tar_node, slist_node_t *new_node); + +__static_inline slist_node_t *slist_take_head (slist_t *list); +__static_inline slist_node_t *slist_take_tail (slist_t *list); + +__static_inline slist_node_t *slist_peek_head (slist_t *list); +__static_inline slist_node_t *slist_peek_tail (slist_t *list); + +__static_inline slist_node_t *slist_peek_next (slist_node_t *node); +__static_inline slist_node_t *slist_peek_prev (slist_t *list, slist_node_t *node); + +__static_inline slist_node_t *slist_peek_next_loop (slist_t *list, slist_node_t *node); +__static_inline slist_node_t *slist_peek_prev_loop (slist_t *list, slist_node_t *node); + +__static_inline int slist_is_pending (slist_node_t *node); + +#define SLIST_CONTAINER_OF(PTR, TYPE, MEMBER) ((TYPE *)&((uint8_t *)PTR)[-(int)&((TYPE *)0)->MEMBER]) + +#define SLIST_FOR_EACH_NODE(__sl, __sn) \ + for (__sn = slist_peek_head(__sl); __sn; \ + __sn = slist_peek_next(__sn)) + +#define SLIST_ITERATE_FROM_NODE(__sl, __sn) \ + for (__sn = __sn ? slist_peek_next_no_check(__sn) \ + : slist_peek_head(__sl); \ + __sn; \ + __sn = slist_peek_next(__sn)) + +#define SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \ + for (__sn = slist_peek_head(__sl), \ + __sns = slist_peek_next(__sn); \ + __sn; \ + __sn = __sns, __sns = slist_peek_next(__sn)) + +#define SLIST_CONTAINER(__ln, __cn, __n) \ + ((__ln) ? SLIST_CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL) + +#define SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n) \ + SLIST_CONTAINER(slist_peek_head(__sl), __cn, __n) + +#define SLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n) \ + SLIST_CONTAINER(slist_peek_tail(__sl), __cn, __n) + +#define SLIST_PEEK_NEXT_CONTAINER(__cn, __n) \ + ((__cn) ? SLIST_CONTAINER(slist_peek_next(&((__cn)->__n)), __cn, __n) : NULL) + +#define SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \ + for (__cn = SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); \ + __cn; \ + __cn = SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +#define SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n) \ + for (__cn = SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \ + __cns = SLIST_PEEK_NEXT_CONTAINER(__cn, __n); \ + __cn; \ + __cn = __cns, __cns = SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if LIST_NODE_TYPE == 1 +#define SLIST_SET(Value1, Value2) (Value1 = Value2 == 0 ? 0 : ((int)Value2 - (int)&Value1) | 1) +#define SLIST_GET(Value) ((slist_node_t*)(Value == 0 ? 0 : ((int)&Value + Value) & ~1)) +#else +#define SLIST_SET(Value1, Value2) (Value1 = (list_node_t)Value2) +#define SLIST_GET(Value) ((slist_node_t*)(Value)) +#endif + +__static_inline void slist_init_list(slist_t *list) +{ + SLIST_SET(list->head, 0); + SLIST_SET(list->tail, 0); +} +__static_inline void slist_init_node(slist_node_t *node) +{ + SLIST_SET(node->next, 0); +} +__static_inline int slist_insert_font(slist_t *list, slist_node_t *node) +{ + if (SLIST_GET(node->next) != NULL) + { + // SYS_LOG_WRN("Node is pending"); + return -1; + } + + slist_node_t *head_node = SLIST_GET(list->head); + if (head_node) + { + SLIST_SET(node->next, head_node); + } + else + { + SLIST_SET(node->next, node); + SLIST_SET(list->tail, node); + } + SLIST_SET(list->head, node); + + return 0; +} +__static_inline int slist_insert_tail(slist_t *list, slist_node_t *node) +{ + if (SLIST_GET(node->next) != NULL) + { + // SYS_LOG_WRN("Node is pending"); + return -1; + } + + SLIST_SET(node->next, node); + if (SLIST_GET(list->head) == NULL) + { + SLIST_SET(list->head, node); + } + else + { + slist_node_t *tail_node = (slist_node_t *)SLIST_GET(list->tail); + + SLIST_SET(tail_node->next, node); + } + SLIST_SET(list->tail, node); + + return 0; +} +__static_inline int slist_remove(slist_t *list, slist_node_t *node) +{ + if (node == NULL || list == NULL) + { + return -1; + } + + slist_node_t *list_head = SLIST_GET(list->head); + slist_node_t *node_next = SLIST_GET(node->next); + if (node_next == NULL || list_head == NULL) + { + return -1; + } + + if (node == list_head) // node 是首个节点 + { + if (node == SLIST_GET(list->tail)) // node 是最后一个节点 + { + SLIST_SET(list->head, 0); + SLIST_SET(list->tail, 0); + } + else + { + SLIST_SET(list->head, node_next); + } + } + else // node 不是首个节点 + { + slist_node_t *prev_node = slist_peek_prev(list, node); + if (prev_node == NULL) + { + return -1; + } + + if (node == SLIST_GET(list->tail)) // node 是最后一个节点 + { + SLIST_SET(list->tail, prev_node); + SLIST_SET(prev_node->next, prev_node); + } + else + { + SLIST_SET(prev_node->next, node_next); + } + } + + SLIST_SET(node->next, 0); + + return 0; +} +__static_inline int slist_remove_next(slist_t *list, slist_node_t *prev_node, slist_node_t *remove_node) +{ + if (remove_node == NULL || list == NULL) + { + return -1; + } + + slist_node_t *list_head = SLIST_GET(list->head); + slist_node_t *node_next = SLIST_GET(remove_node->next); + if (node_next == NULL || list_head == NULL) + { + return -1; + } + + if (remove_node == list_head) // remove_node 是首个节点 + { + if (remove_node == SLIST_GET(list->tail)) // remove_node 是最后一个节点 + { + SLIST_SET(list->head, 0); + SLIST_SET(list->tail, 0); + } + else + { + SLIST_SET(list->head, node_next); + } + } + else // remove_node 不是首个节点 + { + if (prev_node == NULL) + { + prev_node = slist_peek_prev(list, remove_node); + if (prev_node == NULL) + { + return -1; + } + } + + if (remove_node == SLIST_GET(list->tail)) // remove_node 是最后一个节点 + { + SLIST_SET(list->tail, prev_node); + SLIST_SET(prev_node->next, prev_node); + } + else + { + SLIST_SET(prev_node->next, node_next); + } + } + + SLIST_SET(remove_node->next, 0); + + return 0; +} +__static_inline int slist_insert_next(slist_t *list, slist_node_t *tar_node, slist_node_t *new_node) +{ + slist_node_t *tar_node_next = SLIST_GET(tar_node->next); + if (SLIST_GET(list->head) == NULL || tar_node_next == NULL || SLIST_GET(new_node->next) != NULL) + { + return -1; + } + + if (tar_node == SLIST_GET(list->tail)) // tar_node 是最后一个节点 + { + SLIST_SET(new_node->next, new_node); + SLIST_SET(tar_node->next, new_node); + SLIST_SET(list->tail, new_node); + } + else + { + SLIST_SET(new_node->next, tar_node_next); + SLIST_SET(tar_node->next, new_node); + } + + return 0; +} +__static_inline int slist_insert_prev(slist_t *list, slist_node_t *tar_node, slist_node_t *new_node) +{ + slist_node_t *list_head = SLIST_GET(list->head); + if (list_head == NULL || SLIST_GET(tar_node->next) == NULL || SLIST_GET(new_node->next) != NULL) + { + return -1; + } + + if (tar_node == list_head) // tar_node 是首个节点 + { + SLIST_SET(new_node->next, list_head); + SLIST_SET(list->head, new_node); + } + else + { + slist_node_t *prev_node = slist_peek_prev(list, tar_node); + if (prev_node == NULL) + { + return -1; + } + + SLIST_SET(new_node->next, tar_node); + SLIST_SET(prev_node->next, new_node); + } + + return 0; +} + +__static_inline slist_node_t *slist_take_head(slist_t *list) +{ + slist_node_t *ret = SLIST_GET(list->head); + slist_remove(list, ret); + return ret; +} +__static_inline slist_node_t *slist_take_tail(slist_t *list) +{ + slist_node_t *ret = SLIST_GET(list->tail); + slist_remove(list, ret); + return ret; +} + +__static_inline slist_node_t *slist_peek_head(slist_t *list) +{ + return SLIST_GET(list->head); +} +__static_inline slist_node_t *slist_peek_tail(slist_t *list) +{ + return SLIST_GET(list->tail); +} + +__static_inline slist_node_t *slist_peek_next(slist_node_t *node) +{ + slist_node_t *next_node = SLIST_GET(node->next); + if (next_node == node) + { + return NULL; + } + else + { + return next_node; + } +} +__static_inline slist_node_t *slist_peek_prev(slist_t *list, slist_node_t *node) +{ + slist_node_t *prev_node = NULL; + slist_node_t *test_node = SLIST_GET(list->head); + while (test_node && test_node != node && prev_node != test_node) + { + prev_node = test_node; + test_node = SLIST_GET(test_node->next); + } + if (test_node == node) + { + return prev_node; + } + + return NULL; +} + +__static_inline slist_node_t *slist_peek_next_loop(slist_t *list, slist_node_t *node) +{ + slist_node_t *next_node = SLIST_GET(node->next); + if (next_node == node) + { + return SLIST_GET(list->head); + } + else + { + return next_node; + } +} +__static_inline slist_node_t *slist_peek_prev_loop(slist_t *list, slist_node_t *node) +{ + slist_node_t *prev_node = SLIST_GET(list->tail); + slist_node_t *test_node = SLIST_GET(list->head); + while (test_node && test_node != node && prev_node != test_node) + { + prev_node = test_node; + test_node = SLIST_GET(test_node->next); + } + if (test_node == node) + { + return prev_node; + } + + return NULL; +} + +__static_inline int slist_is_pending(slist_node_t *node) +{ + return (SLIST_GET(node->next) != NULL); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/os/os.h b/components/system/include/os/os.h new file mode 100755 index 0000000..d432ebb --- /dev/null +++ b/components/system/include/os/os.h @@ -0,0 +1,24 @@ +/** + * @file os.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_H__ +#define __OS_H__ + +#include "os/os_thread.h" +#include "os/os_timer.h" +#include "os/os_work.h" +#include "os/os_ipc.h" +#include "os/os_semaphore.h" +#include "os/os_mutex.h" +#include "os/os_service.h" +#include "os/os_heap.h" + +#endif /* __OS_H__ */ diff --git a/components/system/include/os/os_common.h b/components/system/include/os/os_common.h new file mode 100755 index 0000000..cd36fde --- /dev/null +++ b/components/system/include/os/os_common.h @@ -0,0 +1,68 @@ +/** + * @file os_common.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_COMMON_H__ +#define __OS_COMMON_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + OS_PRIORITY_IDLE = 0, + OS_PRIORITY_LOWEST = 1, + OS_PRIORITY_LOWER = 2, + OS_PRIORITY_LOW = 3, + OS_PRIORITY_NORMAL = 4, + OS_PRIORITY_HIGH = 5, + OS_PRIORITY_HIGHER = 6, + OS_PRIORITY_HIGHEST = 7 +} os_priority; + +typedef enum { + OS_OK = 0, /* success */ + OS_FAIL = -1, /* general failure */ + OS_E_NOMEM = -2, /* out of memory */ + OS_E_PARAM = -3, /* invalid parameter */ + OS_E_TIMEOUT = -4, /* operation timeout */ + OS_E_ISR = -5, /* not allowed in ISR context */ +} os_state; + +typedef void * os_handle_t; + +typedef size_t os_time_t; + +#define OS_INVALID_HANDLE NULL /* OS invalid handle */ +#define OS_WAIT_FOREVER 0xffffffffU /* Wait forever timeout value */ +#define OS_SEMAPHORE_MAX_COUNT 0xffffffffU /* Maximum count value for semaphore */ + +#define OS_MSEC_PER_SEC 1000U /* milliseconds per second */ +#define OS_USEC_PER_MSEC 1000U /* microseconds per millisecond */ +#define OS_USEC_PER_SEC 1000000U /* microseconds per second */ + +#define OS_TICK (OS_USEC_PER_SEC / OS_TICK_RATE) /* microseconds per tick */ + +#define OS_SecsToTicks(sec) ((os_time_t)(sec) * OS_TICK_RATE) +#define OS_MSecsToTicks(msec) ((os_time_t)(OS_TICK_RATE < OS_MSEC_PER_SEC ? (msec) / (OS_TICK / OS_USEC_PER_MSEC) : (msec) * (OS_USEC_PER_MSEC / OS_TICK))) +#define OS_TicksToMSecs(t) ((os_time_t)(OS_TICK_RATE < OS_MSEC_PER_SEC ? (t) * (OS_TICK / OS_USEC_PER_MSEC) : (t) / (OS_USEC_PER_MSEC / OS_TICK))) +#define OS_TicksToSecs(t) ((os_time_t)(t) / (OS_USEC_PER_SEC / OS_TICK)) + +#define OS_CONTAINER_OF(PTR, TYPE, MEMBER) ((TYPE *)&((uint8_t *)PTR)[-(int)&((TYPE *)0)->MEMBER]) + +#ifdef __cplusplus +} +#endif + +#endif /* __OS_COMMON_H__ */ diff --git a/components/system/include/os/os_heap.h b/components/system/include/os/os_heap.h new file mode 100755 index 0000000..528eaaf --- /dev/null +++ b/components/system/include/os/os_heap.h @@ -0,0 +1,33 @@ +/** + * @file os_heap.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_HEAP_H__ +#define __OS_HEAP_H__ + +#include "os/os_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +void *os_malloc(size_t size); +void *os_calloc(size_t size); +void *os_realloc(void *p, size_t size); +void os_free(void *p); + +void os_heap_info(size_t *used_size, size_t *free_size, size_t *max_block_size); + +#ifdef __cplusplus +} +#endif + +#endif /* __OS_HEAP_H__ */ diff --git a/components/system/include/os/os_ipc.h b/components/system/include/os/os_ipc.h new file mode 100755 index 0000000..8b45fc1 --- /dev/null +++ b/components/system/include/os/os_ipc.h @@ -0,0 +1,103 @@ +/** + * @file os_ipc.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_IPC_H__ +#define __OS_IPC_H__ + +#include "os/os_work.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* #ifdef __cplusplus */ + +typedef os_handle_t os_fifo_handle_t; + +typedef struct +{ + os_fifo_handle_t handle; +} os_fifo_t; + +os_state os_fifo_q_create(os_fifo_t *fifo_handle); +os_state os_fifo_q_delete(os_fifo_t *fifo_handle); +os_state os_fifo_q_clr(os_fifo_t *fifo_handle); + +void os_fifo_q_regist(os_fifo_t *fifo_handle, os_work_t *work_handle, int delay_ticks); +void os_fifo_q_unregist(os_fifo_t *fifo_handle); + +void *os_fifo_alloc(size_t size); +os_state os_fifo_put(os_fifo_t *fifo_handle, void *fifo_data); + +void *os_fifo_take(os_fifo_t *fifo_handle, os_time_t wait_ms); +os_state os_fifo_free(void *fifo_data); + +void *os_fifo_peek_head(os_fifo_t *fifo_handle); +void *os_fifo_peek_tail(os_fifo_t *fifo_handle); + +bool os_fifo_q_is_valid(os_fifo_t *fifo_handle); + +typedef os_handle_t os_queue_handle_t; + +typedef struct +{ + os_queue_handle_t handle; +} os_queue_t; + +os_state os_queue_create(os_queue_t *queue_handle, size_t queueLen, size_t itemSize); +os_state os_queue_delete(os_queue_t *queue_handle); +os_state os_queue_clr(os_queue_t *queue_handle); + +void os_queue_regist(os_queue_t *queue_handle, os_work_t *work_handle, int delay_ticks); +void os_queue_unregist(os_queue_t *queue_handle); + +os_state os_queue_send(os_queue_t *queue_handle, const void *item, os_time_t wait_ms); +os_state os_queue_recv(os_queue_t *queue_handle, void *item, os_time_t wait_ms); + +void *os_queue_peek_head(os_queue_t *queue_handle); +void *os_queue_peek_tail(os_queue_t *queue_handle); + +size_t os_queue_get_item_size(os_queue_t *queue_handle); + +bool os_queue_is_valid(os_queue_t *queue_handle); + +typedef os_handle_t os_pipe_handle_t; + +typedef struct +{ + os_pipe_handle_t handle; +} os_pipe_t; + +os_state os_pipe_create(os_pipe_t *pipe_handle, size_t pipe_size); +os_state os_pipe_delete(os_pipe_t *pipe_handle); +os_state os_pipe_clr(os_pipe_t *pipe_handle); + +void os_pipe_regist(os_pipe_t *pipe_handle, os_work_t *work_handle, int delay_ticks); +void os_pipe_unregist(os_pipe_t *pipe_handle); + +size_t os_pipe_poll_write(os_pipe_t *pipe_handle, uint8_t data); +size_t os_pipe_fifo_fill(os_pipe_t *pipe_handle, const void *data, size_t size); +size_t os_pipe_poll_read(os_pipe_t *pipe_handle, uint8_t *data); +size_t os_pipe_fifo_read(os_pipe_t *pipe_handle, void *data, size_t size); + +bool os_pipe_is_ne(os_pipe_t *pipe_handle); +size_t os_pipe_get_valid_size(os_pipe_t *pipe_handle); +size_t os_pipe_get_empty_size(os_pipe_t *pipe_handle); + +void os_pipe_peek_valid(os_pipe_t *pipe_handle, void **dst_base, size_t *dst_size); +void os_pipe_peek_empty(os_pipe_t *pipe_handle, void **dst_base, size_t *dst_size); + +bool os_pipe_is_valid(os_pipe_t *pipe_handle); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* #ifdef __cplusplus */ + +#endif /* __OS_IPC_H__ */ diff --git a/components/system/include/os/os_mutex.h b/components/system/include/os/os_mutex.h new file mode 100755 index 0000000..2153451 --- /dev/null +++ b/components/system/include/os/os_mutex.h @@ -0,0 +1,44 @@ +/** + * @file os_mutex.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_MUTEX_H__ +#define __OS_MUTEX_H__ + +#include "os/os_common.h" +#include "os/os_thread.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef os_handle_t os_mutex_handle_t; + +typedef struct +{ + os_mutex_handle_t handle; +} os_mutex_t; + +os_state os_mutex_create(os_mutex_t *mutex); +os_state os_mutex_delete(os_mutex_t *mutex); + +os_state os_mutex_lock(os_mutex_t *mutex, os_time_t wait_ms); +os_state os_mutex_unlock(os_mutex_t *mutex); + +os_thread_handle_t os_mutex_get_holder(os_mutex_t *mutex); + +bool os_mutex_is_valid(os_mutex_t *mutex); + +#ifdef __cplusplus +} +#endif + +#endif /* __OS_MUTEX_H__ */ diff --git a/components/system/include/os/os_semaphore.h b/components/system/include/os/os_semaphore.h new file mode 100755 index 0000000..8b08c8e --- /dev/null +++ b/components/system/include/os/os_semaphore.h @@ -0,0 +1,41 @@ +/** + * @file os_semaphore.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_SEMAPHORE_H__ +#define __OS_SEMAPHORE_H__ + +#include "os/os_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef os_handle_t os_sem_handle_t; + +typedef struct +{ + os_sem_handle_t handle; +} os_sem_t; + +os_state os_sem_create(os_sem_t *sem, size_t init_value, size_t max_value); +os_state os_sem_delete(os_sem_t *sem); + +os_state os_sem_take(os_sem_t *sem, os_time_t wait_ms); +os_state os_sem_release(os_sem_t *sem); + +bool os_sem_is_valid(os_sem_t *sem); + +#ifdef __cplusplus +} +#endif + +#endif /* __OS_SEMAPHORE_H__ */ diff --git a/components/system/include/os/os_service.h b/components/system/include/os/os_service.h new file mode 100755 index 0000000..a6fb3ac --- /dev/null +++ b/components/system/include/os/os_service.h @@ -0,0 +1,56 @@ +/** + * @file os_service.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_SERVER_H__ +#define __OS_SERVER_H__ + +#include "os/os_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +int os_start(void *heap_mem, size_t heap_size); + +void os_int_entry(void); +void os_int_exit(void); + +bool os_is_isr_context(void); + +void os_interrupt_disable(void); +void os_interrupt_enable(void); + +void os_scheduler_suspend(void); +void os_scheduler_resume(void); + +bool os_scheduler_is_running(void); + +void os_sys_print_info(void); + +os_time_t os_get_sys_time(void); + +size_t os_get_sys_ticks(void); + +os_time_t os_calc_ticks_to_msec(size_t ticks); +size_t os_calc_msec_to_ticks(os_time_t msec); + +size_t os_cpu_usage(void); + +int os_get_err(void); + +void os_set_err(int err); + +#ifdef __cplusplus +} +#endif + +#endif /* __OS_SERVER_H__ */ diff --git a/components/system/include/os/os_thread.h b/components/system/include/os/os_thread.h new file mode 100755 index 0000000..5f6629c --- /dev/null +++ b/components/system/include/os/os_thread.h @@ -0,0 +1,68 @@ +/** + * @file os_thread.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_THREAD_H__ +#define __OS_THREAD_H__ + +#include "os/os_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef os_handle_t os_thread_handle_t; + +typedef struct +{ + os_thread_handle_t handle; +} os_thread_t; + +typedef void (*os_thread_entry_t)(void *); + +os_state os_thread_init(os_thread_t *thread, + const char *name, + os_thread_entry_t entry, + void *arg, + void *stack_base, + size_t stack_size, + os_priority priority); + +os_state os_thread_create(os_thread_t *thread, + const char *name, + os_thread_entry_t entry, + void *arg, + size_t stack_size, + os_priority priority); + +os_state os_thread_delete(os_thread_t *thread); + +void os_thread_sleep(os_time_t msec); + +void os_thread_yield(void); + +void os_thread_suspend(os_thread_t *thread); + +void os_thread_resume(os_thread_t *thread); + +os_thread_handle_t os_thread_get_self(void); + +const char *os_thread_get_name(os_thread_t *thread); + +size_t os_thread_stack_min(os_thread_t *thread); + +bool os_thread_is_valid(os_thread_t *thread); + +#ifdef __cplusplus +} +#endif + +#endif /* __OS_THREAD_H__ */ diff --git a/components/system/include/os/os_timer.h b/components/system/include/os/os_timer.h new file mode 100755 index 0000000..534ed70 --- /dev/null +++ b/components/system/include/os/os_timer.h @@ -0,0 +1,55 @@ +/** + * @file os_timer.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_TIMER_H__ +#define __OS_TIMER_H__ + +#include "os/os_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef os_handle_t os_timer_handle_t; + +typedef struct +{ + os_timer_handle_t handle; +} os_timer_t; + +typedef enum +{ + OS_TIMER_ONCE = 0, /* one shot timer */ + OS_TIMER_PERIODIC = 1 /* periodic timer */ +} os_timer_type_t; + +typedef void (*os_timer_cb_fn)(void *arg); + +os_state os_timer_create(os_timer_t *timer, os_timer_cb_fn cb, void *arg); + +os_state os_timer_delete(os_timer_t *timer); + +os_state os_timer_set_period(os_timer_t *timer, os_timer_type_t type, os_time_t period_ms); + +os_state os_timer_start(os_timer_t *timer); + +os_state os_timer_stop(os_timer_t *timer); + +bool os_timer_is_pending(os_timer_t *timer); + +bool os_timer_is_valid(os_timer_t *timer); + +#ifdef __cplusplus +} +#endif + +#endif /* __OS_TIMER_H__ */ diff --git a/components/system/include/os/os_work.h b/components/system/include/os/os_work.h new file mode 100755 index 0000000..f0ff94d --- /dev/null +++ b/components/system/include/os/os_work.h @@ -0,0 +1,71 @@ +/** + * @file os_work.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __OS_WORK_H__ +#define __OS_WORK_H__ + +#include "os/os_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* #ifdef __cplusplus */ + +typedef os_handle_t os_work_q_handle_t; + +typedef struct +{ + os_work_q_handle_t handle; +} os_work_q_t; + +extern os_work_q_t *default_os_work_q_hdl; + +os_state os_work_q_create(os_work_q_t *work_q_handle, + const char *name, + size_t stack_size, + os_priority priority); + +os_state os_work_q_delete(os_work_q_t *work_q_handle); + +bool os_work_q_is_valid(os_work_q_t *work_q_handle); +bool os_work_q_delayed_state(os_work_q_t *work_q_handle); +bool os_work_q_ready_state(os_work_q_t *work_q_handle); + +typedef os_handle_t os_work_handle_t; + +typedef struct +{ + os_work_handle_t handle; +} os_work_t; + +typedef void (*os_work_fn)(void *arg); + +os_state os_work_create(os_work_t *work_handle, const char *name, os_work_fn work_route, void *arg, uint8_t sub_prior); +void os_work_delete(os_work_t *work_handle); + +bool os_work_is_valid(os_work_t *work_handle); +bool os_work_is_pending(os_work_t *work_handle); +os_time_t os_work_time_remain(os_work_t *work_handle); + +void os_work_submit(os_work_q_t *work_q_handle, os_work_t *work_handle, os_time_t delay_ms); +void os_work_resume(os_work_t *work_handle, os_time_t delay_ms); +void os_work_suspend(os_work_t *work_handle); + +void os_work_yield(os_time_t ms); +void os_work_sleep(os_time_t ms); +void os_work_later(os_time_t ms); +void os_work_later_until(os_time_t ms); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* #ifdef __cplusplus */ + +#endif diff --git a/components/system/include/sys/features.h b/components/system/include/sys/features.h new file mode 100755 index 0000000..b5930c2 --- /dev/null +++ b/components/system/include/sys/features.h @@ -0,0 +1,75 @@ +/** + * @file features.h + * @author + * @brief + * In order to redefine int32_t to int when compiles using GCC, this file is + * added to override of GCC. + * @version 0.1 + * @date 2021-12-09 + * + * @copyright Copyright (c) 2021 + * + */ + +#ifndef _LIBC_SYS_FEATURES_H_ +#define _LIBC_SYS_FEATURES_H_ + +/* + * Redefine some macros defined by GCC + */ +#ifdef __INT32_MAX__ +#undef __INT32_MAX__ +#endif +#define __INT32_MAX__ 2147483647 + +#ifdef __UINT32_MAX__ +#undef __UINT32_MAX__ +#endif +#define __UINT32_MAX__ 4294967295U + +#ifdef __INT_LEAST32_MAX__ +#undef __INT_LEAST32_MAX__ +#endif +#define __INT_LEAST32_MAX__ 2147483647 + +#ifdef __UINT_LEAST32_MAX__ +#undef __UINT_LEAST32_MAX__ +#endif +#define __UINT_LEAST32_MAX__ 4294967295U + +#ifdef __INT_LEAST32_TYPE__ +#undef __INT_LEAST32_TYPE__ +#endif +#define __INT_LEAST32_TYPE__ int + +#ifdef __UINT_LEAST32_TYPE__ +#undef __UINT_LEAST32_TYPE__ +#endif +#define __UINT_LEAST32_TYPE__ unsigned int + +#ifdef __INT32_C +#undef __INT32_C +#endif +#define __INT32_C(c) c + +#ifdef __UINT32_C +#undef __UINT32_C +#endif +#define __UINT32_C(c) c ## U + +#ifdef __INT32_TYPE__ +#undef __INT32_TYPE__ +#endif +#define __INT32_TYPE__ int + +#ifdef __UINT32_TYPE__ +#undef __UINT32_TYPE__ +#endif +#define __UINT32_TYPE__ unsigned int + +/* + * Include of compiler + */ +#include_next + +#endif /* _LIBC_SYS_FEATURES_H_ */ diff --git a/components/system/include/sys/reent.h b/components/system/include/sys/reent.h new file mode 100644 index 0000000..4df3d53 --- /dev/null +++ b/components/system/include/sys/reent.h @@ -0,0 +1,2 @@ +#pragma once +#include_next diff --git a/components/system/include/sys_init.h b/components/system/include/sys_init.h new file mode 100755 index 0000000..7491f39 --- /dev/null +++ b/components/system/include/sys_init.h @@ -0,0 +1,400 @@ +/** + * @file sys_init.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-06-01 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __SYS_INIT_H__ +#define __SYS_INIT_H__ + +#include "sys_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* #ifdef __cplusplus */ + +/* initialize function -------------------------------------------------------- */ + +#define INIT_EXPORT_BOARD(FN) _INIT_EXPORT(FN, 0, 1) // 定义自动初始化函数:非常早期的初始化,此时调度器还未启动 +#define INIT_EXPORT_PREV(FN) _INIT_EXPORT(FN, 1, 1) // 定义自动初始化函数:主要是用于纯软件的初始化、没有太多依赖的函数 +#define INIT_EXPORT_DEVICE(FN) _INIT_EXPORT(FN, 2, 1) // 定义自动初始化函数:外设驱动初始化相关,比如网卡设备 +#define INIT_EXPORT_COMPONENT(FN) _INIT_EXPORT(FN, 3, 1) // 定义自动初始化函数:组件初始化,比如文件系统或者 LWIP +#define INIT_EXPORT_ENV(FN) _INIT_EXPORT(FN, 4, 1) // 定义自动初始化函数:系统环境初始化,比如挂载文件系统 +#define INIT_EXPORT_APP(FN, PRIOR) _INIT_EXPORT(FN, 5, PRIOR) // 定义自动初始化函数:应用初始化,比如 GUI 应用(PRIOR 建议取值为 0 或 10..99) + +/* module define -------------------------------------------------------------- */ + +#define OBJ_EXPORT_DEVICE(OBJECT, NAME) _MODULE_EXPORT(OBJECT, NAME, MODULE_TYPE_DEVICE) +#define OBJ_EXPORT_COMPONENT(OBJECT, NAME) _MODULE_EXPORT(OBJECT, NAME, MODULE_TYPE_COMPONENT) + +/* module api------------------------------------------------------------------ */ + +__static_inline const void *device_binding(const char *name); +__static_inline const char *device_name(const void *object); +__static_inline const char *device_search(const char *prefix_name, unsigned int num); + +__static_inline const void *component_binding(const char *name); +__static_inline const char *component_name(const void *object); +__static_inline const char *component_search(const char *prefix_name, unsigned int num); + +/* code ----------------------------------------------------------------------- */ + +/** @defgroup initialize function + * @{ + */ + +typedef struct +{ + int (*fn)(void); // 初始化函数地址 + const char *fn_name; // debug: 函数关联名 + const char *file; // debug: 所在文件名 + uint16_t line; // debug: 所在文件行 + uint8_t level; // debug: 初始化等级 + uint8_t prior; // debug: 初始化优先级 +} sys_init_t; + +/** + * @def _INIT_EXPORT + * + * @brief 用于如下宏: + * INIT_EXPORT_BOARD + * INIT_EXPORT_PREV + * INIT_EXPORT_DEVICE + * INIT_EXPORT_COMPONENT + * INIT_EXPORT_ENV + * INIT_EXPORT_APP + * 输出一个 sys_init_t 类型的数据。 + * 这些数据将被归为 "(.s_sys_init_t.*)" 段中,编译阶段被排序并链接。 + * 需要将文件 "sys_init.ld" 添加到链接脚本。 + * 用于自动初始化。 + * + * @param FN 待函数 + * + * @param LEVEL 按功能划分的优先级 + * + * @param PRIOR 同一功能等级中的优先级,0..99,数值越小越优早执行 + * + * 举例: + * @verbatim + * static int _init_env_template(void) + * { + * SYS_LOG_INF("done.\r\n"); + * return 0; + * } + * INIT_EXPORT_ENV(_init_env_template); + * + * static int _init_app_template1(void) + * { + * SYS_LOG_INF("done.\r\n"); + * return 0; + * } + * static int _init_app_template2(void) + * { + * SYS_LOG_INF("done.\r\n"); + * return 0; + * } + * INIT_EXPORT_APP(_init_app_template1, 0); + * INIT_EXPORT_APP(_init_app_template2, 1); + * @endverbatim + */ +#define _INIT_EXPORT(FN, LEVEL, PRIOR) \ + __used __section(".s_sys_init_t." #LEVEL "." #PRIOR) static sys_init_t const \ + __sys_init_##FN = { \ + .fn = FN, \ + .fn_name = #FN, \ + .line = __LINE__, \ + .file = __FILE__, \ + .level = LEVEL, \ + .prior = PRIOR, \ + } + +/** + * @} + */ + +/** @defgroup initialize module + * @{ + */ + +typedef enum __packed // 定义模块的类型 +{ + MODULE_TYPE_DEVICE, // 模块类型为驱动 + MODULE_TYPE_COMPONENT, // 模块类型为组件 + // ... +} module_type_t; + +typedef struct // 模块数据结构 +{ + const char *name; // 模块数据关联名 + const void *obj; // 模块数据地址 + module_type_t type; // 模块数据类型 + uint16_t line; // debug: 所在文件行 + const char *file; // debug: 所在文件名 +} module_t; + +/** + * @def _MODULE_EXPORT + * + * @brief 输出一个指定模块数据结构。用于如下宏: + * OBJ_EXPORT_DEVICE + * OBJ_EXPORT_COMPONENT + * 这些数据结构将被归为 ".sys_module_data" 段中,编译阶段被链接。 + * 需要将文件 "sys_init.ld" 添加到链接脚本。 + * 使用 sys_module_binding() 可找到模块数据结构。 + * + * @param OBJECT 子对象(符号) + * + * @param OBJ_NAME 子对象关联名称(字符串)。使用 sys_module_search() 可遍历已有对象关联名称 + * + * @param MODULE_TYPE 模块类型 @ref module_type_t + * + * 举例: + * @verbatim + * #include "device_template.h" + * + * static void _set_data(device_template_t dev, int data); + * static int _get_data(device_template_t dev); + * + * static device_template_config_t config; + * + * static struct device_template const device = { + * .api = { + * .set_data = _set_data, + * .get_data = _get_data, + * }, + * .config = &config, + * }; + * OBJ_EXPORT_DEVICE(device, "DRV1"); + * + * static void _set_data(device_template_t dev, int data) + * { + * dev->config->data = data; + * } + * + * static int _get_data(device_template_t dev) + * { + * return dev->config->data; + * } + * @endverbatim + * + */ +#define _MODULE_EXPORT(OBJECT, OBJ_NAME, MODULE_TYPE) \ + __used __section(".sys_module_data.1") static module_t const \ + __sys_module_##OBJECT = { \ + .name = OBJ_NAME, \ + .obj = &OBJECT, \ + .type = MODULE_TYPE, \ + .line = __LINE__, \ + .file = __FILE__, \ + } + +/** + * @brief 结合类型和名称,精确匹配查找个由 _MODULE_EXPORT 输出的模块对象。 + * + * @param type 由 _MODULE_EXPORT 输出的模块类型 + * @param name 由 _MODULE_EXPORT 输出的模块名 + * @return module_t * 由 _MODULE_EXPORT 输出的模块对象 + */ +__static_inline const module_t *sys_module_binding(module_type_t type, const char *name) +{ + if (name) + { + extern const module_t __sys_module_leader_0; + extern const module_t __sys_module_leader_9; + const module_t *module = &__sys_module_leader_0; + const module_t *end = &__sys_module_leader_9; + while (module < end) + { + if (module->type == type) + { + int i; + for (i = 0; 1; i++) + { + if (name[i] != module->name[i]) + break; + if (name[i] == '\0') + return module; + } + } + module = &module[1]; + } + } + return NULL; +} + +/** + * @brief 结合类型和名称,查找个由 _MODULE_EXPORT 输出的模块对象名。 + * + * @param type 由 _MODULE_EXPORT 输出的模块类型 + * @param prefix_name 由 _MODULE_EXPORT 输出的模块名的前缀。例如: prefix_name 为 "UART" ,可能查找到 "UART1", "UART2"... + * @param num 匹配到 prefix_name 的第几次时返回,范围 0..n + * @return const char * 匹配到的第 num 个名称为 prefix_name.* 的完整对象名,这个对象名可用于 sys_module_binding() 的参数。 + * @return NULL 表示没对象 + */ +__static_inline const char *sys_module_search(module_type_t type, const char *prefix_name, unsigned int num) +{ + if (prefix_name) + { + extern const module_t __sys_module_leader_0; + extern const module_t __sys_module_leader_9; + const module_t *module = &__sys_module_leader_0; + const module_t *end = &__sys_module_leader_9; + unsigned int match = 0; + while (module < end) + { + if (module->type == type) + { + int i; + for (i = 0; 1; i++) + { + if (prefix_name[i] == '\0') + { + if (match < num) + { + match++; + break; + } + else + { + return module->name; + } + } + if (prefix_name[i] != module->name[i]) + break; + } + } + module = &module[1]; + } + } + return NULL; +} + +/** + * @brief 获取已绑定的对象的关联名 + * + * @param object 由 sys_module_binding() 取得的对象 + * @return const char * 对象的关联名 + */ +__static_inline const char *sys_module_name(const void *object) +{ + if (object) + { + extern const module_t __sys_module_leader_0; + extern const module_t __sys_module_leader_9; + const module_t *module = &__sys_module_leader_0; + const module_t *end = &__sys_module_leader_9; + while (module < end) + { + if (module->obj == object) + { + return module->name; + } + module = &module[1]; + } + } + return ""; +} + +/** + * @} + */ + +/** @defgroup module + * @{ + */ + +/** + * @brief 绑定一个由 OBJ_EXPORT_DEVICE 输出的设备对象 + * + * @param name 由 OBJ_EXPORT_DEVICE 输出的设备名 + * @return const void * 由 OBJ_EXPORT_DEVICE 输出的设备对象 + */ +__static_inline const void *device_binding(const char *name) +{ + const module_t *module = sys_module_binding(MODULE_TYPE_DEVICE, name); + if (module) + return module->obj; + else + return NULL; +} + +/** + * @brief 获取已绑定的对象的关联名 + * + * @param object 由 device_binding() 取得的对象 + * @return const char * 对象的关联名 + */ +__static_inline const char *device_name(const void *object) +{ + return sys_module_name(object); +} + +/** + * @brief 查找 device_binding() 可用的模块对象名。 + * + * @param prefix_name 由 _MODULE_EXPORT 输出的模块名的前缀。例如: prefix_name 为 "UART" ,可能查找到 "UART1", "UART2"... + * @param num 匹配到 prefix_name 的第几次时返回,范围 0..n + * @return const char * 匹配到的第 num 个名称为 prefix_name.* 的完整对象名 + * @return NULL 表示没对象 + */ +__static_inline const char *device_search(const char *prefix_name, unsigned int num) +{ + return sys_module_search(MODULE_TYPE_DEVICE, prefix_name, num); +} + +/** + * @brief 绑定一个由 OBJ_EXPORT_COMPONENT 输出的设备对象 + * + * @param name 由 OBJ_EXPORT_COMPONENT 输出的设备名 + * @return const void * 由 OBJ_EXPORT_COMPONENT 输出的设备对象 + */ +__static_inline const void *component_binding(const char *name) +{ + const module_t *module = sys_module_binding(MODULE_TYPE_COMPONENT, name); + if (module) + return module->obj; + else + return NULL; +} + + +/** + * @brief 获取已绑定的对象的关联名 + * + * @param object 由 component_binding() 取得的对象 + * @return const char * 对象的关联名 + */ +__static_inline const char *component_name(const void *object) +{ + return sys_module_name(object); +} + +/** + * @brief 查找 component_binding() 可用的模块对象名。 + * + * @param prefix_name 由 _MODULE_EXPORT 输出的模块名的前缀。例如: prefix_name 为 "UART" ,可能查找到 "UART1", "UART2"... + * @param num 匹配到 prefix_name 的第几次时返回,范围 0..n + * @return const char * 匹配到的第 num 个名称为 prefix_name.* 的完整对象名 + * @return NULL 表示没对象 + */ +__static_inline const char *component_search(const char *prefix_name, unsigned int num) +{ + return sys_module_search(MODULE_TYPE_COMPONENT, prefix_name, num); +} + +/** + * @} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* #ifdef __cplusplus */ + +#endif diff --git a/components/system/include/sys_log.h b/components/system/include/sys_log.h new file mode 100755 index 0000000..123bb49 --- /dev/null +++ b/components/system/include/sys_log.h @@ -0,0 +1,436 @@ +/** + * @file sys_log.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __SYS_LOG_H__ +#define __SYS_LOG_H__ + +#include "sys_types.h" +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef SYS_LOG_DOMAIN +#define SYS_LOG_DOMAIN "general" +#endif + +#ifndef CONFIG_SYS_LOG_LEVEL +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF /* 配置日志打印的等级 */ +#endif + +#define SYS_LOG_LEVEL_OFF 0 +#define SYS_LOG_LEVEL_ERR 1 +#define SYS_LOG_LEVEL_WRN 2 +#define SYS_LOG_LEVEL_INF 3 +#define SYS_LOG_LEVEL_DBG 4 + +#ifndef CONFIG_SYS_LOG_COLOR_ON +#define CONFIG_SYS_LOG_COLOR_ON 1 /* 使能打印颜色 */ +#endif + +#ifndef CONFIG_SYS_LOG_LOCAL_ON +#define CONFIG_SYS_LOG_LOCAL_ON 1 /* 强制使能打印所在文件位置 */ +#endif + +#define SYS_PRINT printf +#define SYS_VPRINT vprintf +#define SYS_SPRINT sprintf +#define SYS_SNPRINT snprintf +#define SYS_VSNPRINT vsnprintf + +#ifndef CONFIG_SYS_LOG_CONS_ON +#define CONFIG_SYS_LOG_CONS_ON (CONFIG_SYS_LOG_LEVEL > SYS_LOG_LEVEL_OFF) /* 打印控制总开关 */ +#endif + +#ifndef CONFIG_SYS_LOG_DUMP_ON +#define CONFIG_SYS_LOG_DUMP_ON CONFIG_SYS_LOG_DBG_ON /* 使能打印二进制和十六进制类型 */ +#endif + +#ifndef CONFIG_SYS_LOG_DBG_ON +#define CONFIG_SYS_LOG_DBG_ON (CONFIG_SYS_LOG_LEVEL >= SYS_LOG_LEVEL_DBG) /* 使能打印调试类型*/ +#endif + +#ifndef CONFIG_SYS_LOG_INF_ON +#define CONFIG_SYS_LOG_INF_ON (CONFIG_SYS_LOG_LEVEL >= SYS_LOG_LEVEL_INF) /* 使能打印信息类型 */ +#endif + +#ifndef CONFIG_SYS_LOG_WRN_ON +#define CONFIG_SYS_LOG_WRN_ON (CONFIG_SYS_LOG_LEVEL >= SYS_LOG_LEVEL_WRN) /* 使能打印警告类型 */ +#endif + +#ifndef CONFIG_SYS_LOG_ERR_ON +#define CONFIG_SYS_LOG_ERR_ON (CONFIG_SYS_LOG_LEVEL >= SYS_LOG_LEVEL_ERR) /* 使能打印错误类型 */ +#endif + +#ifndef CONFIG_SYS_LOG_ASS_ON +#define CONFIG_SYS_LOG_ASS_ON (CONFIG_SYS_LOG_LEVEL >= SYS_LOG_LEVEL_INF) /* 使能有效性检测 */ +#endif + +#define CONS_PRINT(FMT, ...) \ + do \ + { \ + if (CONFIG_SYS_LOG_CONS_ON) \ + { \ + SYS_PRINT(FMT, ##__VA_ARGS__); \ + } \ + } while (0) + +#ifndef CONS_ABORT +#define CONS_ABORT() \ + do \ + { \ + volatile int FLAG = 0; \ + do \ + { \ + } while (FLAG == 0); \ + } while (0) +#endif + +#if (CONFIG_SYS_LOG_COLOR_ON == 1) +#define _COLOR_R "\033[31m" /* 红 RED */ +#define _COLOR_G "\033[32m" /* 绿 GREEN */ +#define _COLOR_Y "\033[33m" /* 黄 YELLOW */ +#define _COLOR_B "\033[34m" /* 蓝 BLUE */ +#define _COLOR_P "\033[35m" /* 紫 PURPLE */ +#define _COLOR_C "\033[36m" /* 青 CYAN */ +#define _COLOR_RY "\033[41;33m" /* 红底黄字 */ +#define _COLOR_END "\033[0m" /* 结束 */ +#else +#define _COLOR_R "" /* 红 */ +#define _COLOR_G "" /* 绿 */ +#define _COLOR_Y "" /* 黄 */ +#define _COLOR_B "" /* 蓝 */ +#define _COLOR_P "" /* 紫 */ +#define _COLOR_C "" /* 青 */ +#define _COLOR_RY "" /* 红底黄字 */ +#define _COLOR_END "" /* 结束 */ +#endif + +#define _DO_SYS_LOG(FLAG, FMT, ARG...) \ + do \ + { \ + if (FLAG) \ + { \ + CONS_PRINT(FMT, ##ARG); \ + } \ + } while (0) + +#define _FILENAME(FILE) (strrchr(FILE, '/') ? (strrchr(FILE, '/') + 1) : (strrchr(FILE, '\\') ? (strrchr(FILE, '\\') + 1) : FILE)) + +#define _GEN_DOMAIN "[" SYS_LOG_DOMAIN "] " + +#define _SYS_LOG(FLAG, INFO, FMT, ...) _DO_SYS_LOG(FLAG, INFO "%s:%u \t%s =>\t" FMT "\r\n", \ + _FILENAME(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__) + +#if (CONFIG_SYS_LOG_LOCAL_ON == 1 || CONFIG_SYS_LOG_DBG_ON == 1) +#define _SYS_LOG_COLOR(FLAG, INFO, COLOR, FMT, ...) _DO_SYS_LOG(FLAG, INFO "%s:%u \t%s =>\t" COLOR FMT _COLOR_END "\r\n", \ + _FILENAME(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__) +#else +#define _SYS_LOG_COLOR(FLAG, INFO, COLOR, FMT, ...) _DO_SYS_LOG(FLAG, INFO "%s =>\t" COLOR FMT _COLOR_END "\r\n", \ + __FUNCTION__, ##__VA_ARGS__) +#endif + +#define SYS_LOG_DBG(FMT, ...) _SYS_LOG(CONFIG_SYS_LOG_DBG_ON, "[DBG] " _GEN_DOMAIN, FMT, ##__VA_ARGS__) +#define SYS_LOG_INF(FMT, ...) _SYS_LOG_COLOR(CONFIG_SYS_LOG_INF_ON, "[INF] | " _GEN_DOMAIN, _COLOR_G, FMT, ##__VA_ARGS__) +#define SYS_LOG_WRN(FMT, ...) _SYS_LOG_COLOR(CONFIG_SYS_LOG_WRN_ON, "[WRN] * " _GEN_DOMAIN, _COLOR_Y, FMT, ##__VA_ARGS__) +#define SYS_LOG_ERR(FMT, ...) \ + do \ + { \ + if (CONFIG_SYS_LOG_ERR_ON) \ + { \ + _SYS_LOG_COLOR(1, "[ERR] - " _GEN_DOMAIN, _COLOR_R, FMT, ##__VA_ARGS__); \ + CONS_ABORT(); \ + } \ + } while (0) + +#define SYS_LOG(FMT, ...) \ + do \ + { \ + if (CONFIG_SYS_LOG_CONS_ON) \ + { \ + CONS_PRINT(_GEN_DOMAIN "- %s:%u, %s =>\t" _COLOR_B FMT _COLOR_END "\r\n", \ + _FILENAME(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__); \ + } \ + } while (0) + +#define SYS_ASSERT_FALSE(EXP, FMT, ...) \ + do \ + { \ + if (CONFIG_SYS_LOG_ASS_ON && (EXP)) \ + { \ + CONS_PRINT("[ASS] # " _GEN_DOMAIN "%s:%u \t%s =>\t" _COLOR_RY "%s" _COLOR_END "; ## " FMT "\r\n", \ + _FILENAME(__FILE__), __LINE__, __FUNCTION__, #EXP, ##__VA_ARGS__); \ + CONS_ABORT(); \ + } \ + } while (0) + +#define SYS_ASSERT(EXP, FMT, ...) \ + do \ + { \ + if (CONFIG_SYS_LOG_ASS_ON && !(EXP)) \ + { \ + CONS_PRINT("[ASS] # " _GEN_DOMAIN "%s:%u \t%s =>\t" _COLOR_RY "!(%s)" _COLOR_END "; ## " FMT "\r\n", \ + _FILENAME(__FILE__), __LINE__, __FUNCTION__, #EXP, ##__VA_ARGS__); \ + CONS_ABORT(); \ + } \ + } while (0) + +/** + * @brief Print data in hexadecima + * + * @param P point to data base + * @param SIZE data size (bytes) + * @param ALIGN word length, 1, 2, 4, 8 + * @param ENDIAN 0 -- little-endian, 1 -- big-endian + */ +#define SYS_LOG_DUMP(P, SIZE, ALIGN, ENDIAN) \ + do \ + { \ + if (CONFIG_SYS_LOG_CONS_ON && CONFIG_SYS_LOG_DUMP_ON) \ + { \ + _SYS_LOG_COLOR(1, "[HEX]: " _GEN_DOMAIN, _COLOR_C, #P ": %d bytes%s", \ + SIZE, \ + ALIGN <= 1 ? "" : ENDIAN ? ", big-endian" \ + : ", little-endian"); \ + _sys_log_dump((unsigned)P, P, SIZE, ALIGN, ENDIAN); \ + } \ + } while (0) + +/** + * @brief Print data in binary + * + * @param P point to data base + * @param SIZE data size (bytes) + * @param ALIGN word length, 1, 2, 4, 8 + * @param ENDIAN 0 -- little-endian, 1 -- big-endian + */ +#define SYS_LOG_BIN(P, SIZE, ALIGN, ENDIAN) \ + do \ + { \ + if (CONFIG_SYS_LOG_CONS_ON && CONFIG_SYS_LOG_DUMP_ON) \ + { \ + _SYS_LOG_COLOR(1, "[BIN]: " _GEN_DOMAIN, _COLOR_C, #P ": %d bytes%s", \ + SIZE, \ + ALIGN <= 1 ? "" : ENDIAN ? ", big-endian" \ + : ", little-endian"); \ + _sys_log_bin((unsigned)P, P, SIZE, ALIGN, ENDIAN); \ + } \ + } while (0) + + __static_inline void _sys_log_dump(unsigned addr, + const void *p, + int size, + int width, + int endian) + { +#define BYTES_PRE_LINE 16 + if (size) + { + int i, ii, j; + char buf[4 + 4 * BYTES_PRE_LINE]; + char *ascii; + int len1; + int len2; + int lenmax; + width = (width <= 1 ? 1 : (width <= 2 ? 2 : (width <= 4 ? 4 : 8))); + size = (size + width - 1) / width; + len1 = (1 + width * 2) * ((BYTES_PRE_LINE + width - 1) / width) - 1; + len2 = 3 + (BYTES_PRE_LINE + width - 1) / width * width; + lenmax = len1 + len2; + ascii = &buf[len1 + 2]; + for (i = 0; i < 6 + sizeof(void *) * 2; i++) + { + SYS_PRINT("-"); + } + for (i = 0; i < lenmax; i++) + { + buf[i] = '-'; + } + buf[i] = '\0'; + SYS_PRINT("%s\r\n", buf); + ii = (BYTES_PRE_LINE + width - 1) / width; + while (size) + { + int index1 = 0; + int index2 = 0; + SYS_PRINT(_COLOR_Y "[" _COLOR_END "0x%08X" _COLOR_Y "]" _COLOR_END ": ", (unsigned)addr); + for (i = 0; i < ii;) + { + for (j = 0; j < width; j++) + { + if (endian) + { + index1 += SYS_SPRINT(&buf[index1], "%02x", ((uint8_t *)p)[j]); + } + else + { + index1 += SYS_SPRINT(&buf[index1], "%02x", ((uint8_t *)p)[width - j - 1]); + } + if (((uint8_t *)p)[j] >= ' ' && ((uint8_t *)p)[j] < 0x7f) + { + index2 += SYS_SPRINT(&ascii[index2], "%c", ((uint8_t *)p)[j]); + } + else + { + index2 += SYS_SPRINT(&ascii[index2], "."); + } + } + addr += width; + p = &((uint8_t *)p)[width]; + if (--size > 0 && ++i < ii) + { + index1 += SYS_SPRINT(&buf[index1], " "); + } + else + { + while (index1 < len1) + { + buf[index1++] = ' '; + } + buf[index1++] = ' '; + buf[index1++] = '|'; + index1 += index2; + while (index1 < lenmax - 1) + { + buf[index1++] = ' '; + } + buf[index1++] = '\0'; + SYS_PRINT("%s|\r\n", buf); + } + if (size == 0) + { + for (i = 0; i < 6 + sizeof(void *) * 2; i++) + { + SYS_PRINT("="); + } + for (i = 0; i < lenmax; i++) + { + buf[i] = '='; + } + buf[i] = '\0'; + SYS_PRINT("%s\r\n", buf); + break; + } + } + } + } +#undef BYTES_PRE_LINE + } + + __static_inline void _sys_log_bin(unsigned addr, + const void *p, + int size, + int width, + int endian) + { +#define BITS_PRE_LINE 64 + if (size) + { + uint8_t value[8]; + int i, j; + int lenmax; + width = (width <= 1 ? 1 : (width <= 2 ? 2 : (width <= 4 ? 4 : 8))); + width = width < BITS_PRE_LINE ? width : BITS_PRE_LINE; + size = (size + width - 1) / width * width; + lenmax = (1 + 3 * 8) * width; + for (i = 0; i < 9 + sizeof(void *) * 2 + width * 2 + width * 8 / 4 + lenmax; i++) + { + SYS_PRINT("-"); + } + SYS_PRINT("\r\n"); + for (i = 0; i < 8 + sizeof(void *) * 2 + width * 2; i++) + { + SYS_PRINT(" "); + } + for (i = 0; i < 8 * width; i++) + { + if (i % 4 == 0) + { + if (i % 8 == 0) + { + SYS_PRINT(" "); + } + SYS_PRINT(" "); + } + SYS_PRINT("%3d", 8 * width - 1 - i); + } + SYS_PRINT("\r\n"); + for (i = 0; i < 9 + sizeof(void *) * 2 + width * 2 + width * 8 / 4 + lenmax; i++) + { + SYS_PRINT("~"); + } + SYS_PRINT("\r\n"); + while (size) + { + SYS_PRINT(_COLOR_Y "[" _COLOR_END "0x%08X" _COLOR_Y "]" _COLOR_END ": 0x", (unsigned)addr); + if (endian) + { + for (i = 0; i < width; i++) + { + value[i] = ((uint8_t *)p)[i]; + SYS_PRINT("%02X", value[i]); + } + } + else + { + for (i = 0; i < width; i++) + { + value[i] = ((uint8_t *)p)[width - 1 - i]; + SYS_PRINT("%02X", value[i]); + } + } + for (i = 0; i < width; i++) + { + for (j = 0; j < 8; j++) + { + if (j % 4 == 0) + { + SYS_PRINT(" "); + if (j % 8 == 0) + { + SYS_PRINT(" "); + } + } + if (value[i] >> (7 - j) & 1) + { + SYS_PRINT(" 1"); + } + else + { + SYS_PRINT(" 0"); + } + } + } + SYS_PRINT("\n"); + addr += width; + p = &((uint8_t *)p)[width]; + size -= width; + if (size == 0) + { + for (i = 0; i < 9 + sizeof(void *) * 2 + width * 2 + width * 8 / 4 + lenmax; i++) + { + SYS_PRINT("="); + } + SYS_PRINT("\r\n"); + } + } + } +#undef BITS_PRE_LINE + } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/include/sys_types.h b/components/system/include/sys_types.h new file mode 100755 index 0000000..6830ec9 --- /dev/null +++ b/components/system/include/sys_types.h @@ -0,0 +1,301 @@ +/** + * @file sys_types.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-05-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __SYS_TYPES_H__ +#define __SYS_TYPES_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* #ifdef __cplusplus */ + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#if defined(__CC_ARM) + +#ifndef __static_inline +#define __static_inline static __inline +#endif + +#ifndef __section +#define __section(X) __attribute__((section(X))) +#endif + +#ifndef __used +#define __used __attribute__((used)) +#endif + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +#ifndef __naked +#define __naked __attribute__((naked)) +#endif + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +#ifndef __aligned +#define __aligned(X) __attribute__((aligned(X))) +#endif + +#ifndef __likely +#define __likely(X) __builtin_expect(!!(X), 1) +#endif + +#ifndef __unlikely +#define __unlikely(X) __builtin_expect(!!(X), 0) +#endif + +#ifndef FALLTHROUGH +#define FALLTHROUGH +#endif + +#ifndef __nop +#define __nop() do { __asm { nop; } } while (0) +#endif + +#ifndef __no_optimize +#define __no_optimize +#endif + +#ifndef __clz +#define __clz(VAL) __builtin_clz(VAL); +#endif + +#ifndef __typeof +#define __typeof(VAR) __typeof__((VAR)) +#endif + +#ifndef __optimize +#define __optimize(X) __attribute__((optimize("O"#X))) +#endif + +#elif defined(__GNUC__) /* #if defined(__CC_ARM) */ + +#ifndef __static_inline +#define __static_inline static inline +#endif + +#ifndef __section +#define __section(X) __attribute__((section(X))) +#endif + +#ifndef __used +#define __used __attribute__((used)) +#endif + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +#ifndef __naked +#define __naked __attribute__((naked)) +#endif + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +#ifndef __aligned +#define __aligned(X) __attribute__((__aligned__(X))) +#endif + +#ifndef __likely +#define __likely(X) __builtin_expect(!!(X), 1) +#endif + +#ifndef __unlikely +#define __unlikely(X) __builtin_expect(!!(X), 0) +#endif + +#ifndef FALLTHROUGH +#define FALLTHROUGH __attribute__((fallthrough)) +#endif + +#ifndef __nop +#define __nop() __asm volatile("nop") +#endif + +#ifndef __no_optimize +#define __no_optimize __attribute__((optimize("O0"))) +#endif + +#ifndef __clz +#define __clz(VAL) ((VAL) == 0 ? 32 : __builtin_clz(VAL)) +#endif + +#ifndef __typeof +#define __typeof(VAR) __typeof__((VAR)) +#endif + +#ifndef __optimize +#define __optimize(X) __attribute__((optimize("O"#X))) +#endif + +#else /* #elif defined(__GNUC__) */ + +#define __static_inline static inline + +#define __no_init + +#define __nop() __nop + +#define __no_optimize + +#define __clz(VAL) __builtin_clz(VAL); + +#endif /* #elif defined(__GNUC__) */ + +#ifndef __ARRAY_SIZE +#define __ARRAY_SIZE(ARRAY) (sizeof(ARRAY) / sizeof((ARRAY)[0])) +#endif + +#ifndef __no_init +#define __no_init __section(".noinitialized") +#endif + +#ifndef __ram_text +#define __ram_text __section(".ram_text") +#endif + +#ifndef __IM +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#endif +#ifndef __OM +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#endif +#ifndef __IOM +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ +#endif + +#ifndef sys_le16_to_cpu +#define _b_swap_16(X) ((u16_t)((((X) >> 8) & 0xff) | (((X)&0xff) << 8))) + +#define _b_swap_32(X) ((u32_t)((((X) >> 24) & 0xff) | \ + (((X) >> 8) & 0xff00) | \ + (((X)&0xff00) << 8) | \ + (((X)&0xff) << 24))) + +#define _b_swap_64(X) ((u64_t)((((X) >> 56) & 0xff) | \ + (((X) >> 40) & 0xff00) | \ + (((X) >> 24) & 0xff0000) | \ + (((X) >> 8) & 0xff000000) | \ + (((X)&0xff000000) << 8) | \ + (((X)&0xff0000) << 24) | \ + (((X)&0xff00) << 40) | \ + (((X)&0xff) << 56))) + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define sys_le16_to_cpu(VAL) (VAL) +#define sys_le32_to_cpu(VAL) (VAL) +#define sys_le64_to_cpu(VAL) (VAL) +#define sys_cpu_to_le16(VAL) (VAL) +#define sys_cpu_to_le32(VAL) (VAL) +#define sys_cpu_to_le64(VAL) (VAL) +#define sys_be16_to_cpu(VAL) _b_swap_16(VAL) +#define sys_be32_to_cpu(VAL) _b_swap_32(VAL) +#define sys_be64_to_cpu(VAL) _b_swap_64(VAL) +#define sys_cpu_to_be16(VAL) _b_swap_16(VAL) +#define sys_cpu_to_be32(VAL) _b_swap_32(VAL) +#define sys_cpu_to_be64(VAL) _b_swap_64(VAL) +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define sys_le16_to_cpu(VAL) _b_swap_16(VAL) +#define sys_le32_to_cpu(VAL) _b_swap_32(VAL) +#define sys_le64_to_cpu(VAL) _b_swap_64(VAL) +#define sys_cpu_to_le16(VAL) _b_swap_16(VAL) +#define sys_cpu_to_le32(VAL) _b_swap_32(VAL) +#define sys_cpu_to_le64(VAL) _b_swap_64(VAL) +#define sys_be16_to_cpu(VAL) (VAL) +#define sys_be32_to_cpu(VAL) (VAL) +#define sys_be64_to_cpu(VAL) (VAL) +#define sys_cpu_to_be16(VAL) (VAL) +#define sys_cpu_to_be32(VAL) (VAL) +#define sys_cpu_to_be64(VAL) (VAL) +#else +#error "Unknown byte order" +#endif +#endif + +typedef uint8_t u8_t ; +typedef uint16_t u16_t ; +typedef uint32_t u32_t ; +typedef int8_t s8_t ; +typedef int16_t s16_t ; +typedef int32_t s32_t ; +typedef int64_t s64_t ; +typedef volatile uint8_t vu8_t ; +typedef volatile uint16_t vu16_t ; +typedef volatile uint32_t vu32_t ; +typedef volatile uint64_t vu64_t ; +typedef volatile int8_t vs8_t ; +typedef volatile int16_t vs16_t ; +typedef volatile int32_t vs32_t ; +typedef volatile int64_t vs64_t ; +typedef volatile signed vint ; +typedef unsigned uint ; +typedef volatile unsigned vuint ; + +typedef enum +{ + TYPES_NO_ERR = 0, + TYPES_TIMEOUT, + TYPES_NO_MEM, + TYPES_HAL_ERR, + TYPES_ERROR = -1, +} types_err_t; + +typedef enum +{ + TYPES_RESET = 0, + TYPES_SET = 1, +} types_flag_t; + +typedef enum +{ + TYPES_DISABLE = 0, + TYPES_ENABLE = 1, +} types_ctrl_t; + +#ifndef __WRITEBITS +#define __WRITEBITS(P, INDEX, WIDE, VALUE) (*(vuint *)(P) = (*(vuint *)(P) & ~(~(-1u << (WIDE)) << (INDEX))) ^ (((VALUE) & ~(-1u << (WIDE))) << (INDEX))) +#endif + +#ifndef __READBITS +#define __READBITS(VAR, INDEX, WIDE) (((VAR) >> (INDEX)) & ~(-1u << (WIDE))) +#endif + +__static_inline void writebits(volatile void *dest, u8_t index, u8_t bitwide, uint value) +{ + while(index + bitwide > sizeof(vuint) * 8); + while(value >= 1 << bitwide); + __WRITEBITS(dest, index, bitwide, value); +} + +__static_inline uint readbits(uint var, u8_t index, u8_t bitwide) +{ + return __READBITS(var, index, bitwide); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* #ifdef __cplusplus */ + +#endif diff --git a/components/system/source/k_kit/README.md b/components/system/source/k_kit/README.md new file mode 100755 index 0000000..dcff331 --- /dev/null +++ b/components/system/source/k_kit/README.md @@ -0,0 +1,109 @@ +[简要](../../../../README.md) + +# 微内核 k_kit + + > k_kit 以下简称 kit。kit 可以理解成是一个实时内核中的软定时器模块的强化版。 + 基于此设计的程序就是由所有软定时器组成的集合,这些集合既可作为一个完整的工程,也可以作为一个线程模块,使用非常灵活。 + +## 特点 + - 是一个极小的,由事件驱动的,管理任务的微内核; + - 介于裸机与实时内核之间,适用于资源受限的微型系统的同时,尽可能提供类似实时内核接口和管理方式; + - 极低的跨平台需求,只需要实现几个简单的接口,即可在任何平台上运行; + - 所有功能模块都有独立的对象,所有对象动通过态申请和销毁; + - 极短的、固定的关中断时间,有效保障硬实时性; + - 基于 kit 设计的工程可无缝扩充到 RTK 实时内核中 [点击查看 RTK](../../README.md); + +## 文件结构 +```bash +... +└── kit # 本目录 + ├── k_kit.c # kit 的所有实现代码。 + ├── k_kit.h # 完整的 kit 的功能接口 + └── kk.h # 跨内核接口,与实时内核中 RTK 中的 `rtk_kk.h` 保持一致 +``` + +## 模块简介 + +### `work` + - work 是任务管理的对象,数据结构由 k_work_t 封装,在创建对象时设置实现函数; + - 任务拥有: 运行、延时、挂起、堵塞 的能力; + - 所有任务都由队列函数 k_work_q_handler() 执行调用管管理; + - 所有任务可设置由通讯模块唤醒; + - 所有任务默认附带一个接收邮箱; + - 所有任务函数必须返回; + - 所有任务都允许设置优先级,但不可抢断; + +#### *mbox* + - mbox 是 work 附带的一个功能的扩展功能,用法与 fifo 类似; + - 邮箱在使用前需要被创建,每个任务最多只能有一个邮箱; + - 邮件内存从堆中申请,传递的是指针,与 fifo 不可混用; + - 一个新邮件被提交时,对应的任务一定被唤醒; + - 提取后的邮件在任务函数退出后被自动删除; + +### `timer` + - timer 是软定时器的管理对象,数据结构由 k_timer_t 封装,在创建对象时设置实现函数; + - 软定时器的本质是在 work 的基础上添加一些时间特性的任务; + - 软定时器拥有: 运行、延时、挂起 的能力; + - 所有软定时器都由队列函数 k_timer_q_handler() 执行调用管管理; + - 所有软定时器函数必须返回; + - 所有软定时器都允许设置优先级,但不可抢断; + +### work 与 timer 比较 +| | work | timer | +| :------: | :----------------: | :-----------------: | +| 入口 | k_work_q_handler() | k_timer_q_handler() | +| 运行 | 是 | 是 | +| 延时 | 是 | 是 | +| 挂起 | 是 | 是 | +| 堵塞 | 是 | 不支持 | +| 优先级 | 0 .. 7 | 0 .. 7 | +| mbox | 可用,默认唤醒 | 仅发送 | +| fifo | 可用,注册唤醒 | 可用,不支持唤醒 | +| queue | 可用,注册唤醒 | 可用,不支持唤醒 | +| pipe | 可用,注册唤醒 | 可用,不支持唤醒 | +| log | 可用 | 不可用 | + +### *IPC* + + - kit 目前包含了三种常用的独立对象的通讯模块 `fifo`, `queue`, `pipe` 和 work 专用的模块 `mbox`; + +| | fifo | queue | pipe | mbox | +| :------------: | :-------: | :-------: | :------: | :------: | +| 对象 | k_fifo_t | k_queue_t | k_pipe_t | k_work_t | +| 数据缓存 | 堆 | 独立 | 独立 | 堆 | +| 数据吞吐 | 中 | 慢 | 快 | 中 | +| 数据交换 | 指针 | 副本 | 副本 | 指针 | +| 单位长度 | 不限 | 固定 | 字节 | 不限 | +| 总长度 | 不限 | 固定 | 固定 | 不限 | +| 在中断申请缓存 | 禁止 | 可用 | 不需要 | 禁止 | +| work | 完整可用 | 完整可用 | 完整可用 | 完整可用 | +| timer | 仅收发 | 仅收发 | 仅收发 | 仅发送 | +| isr | 仅收发 | 仅收发 | 仅收发 | 仅发送 | + +## 调度机制 + + > 每个 work 对象都对应一个勾子函数为任务,由入口 k_work_q_handler() 管理(软定时器同理),通过三个关键链表的操作,调用对应的勾子函数。 + > 主要由接口 k_work_submit(), k_work_resume(), k_work_suspend(), k_work_later() 等触发有关链表的操作。 + + - dlist_t event_list; + > dlist_t event_list 是一个双向链表,由上述的接口插入,在队列管理接口中取出; + + - slist_t ready_list[8]; + > dlist_t event_list 共8个成员的单向链表,每个成员表示一个优先级,由队列管理接口使用; + + - slist_t delayed_list; + > slist_t delayed_list 是一个单向链表,由队列管理接口使用; + + - 触发调试的过程: + 1. 在接口 k_work_submit() 中把任务对象添加超时信息并插入到 event_list 中; + 2. k_work_q_handler() 检测 delayed_list 如果有超时的任务,插入到 ready_list 中; + 3. k_work_q_handler() 总是在 event_list 取出任务对象,如果: + - 任务已超时:插入到 ready_list; + - 任务未超时:按时间顺序,插入到 delayed_list; + 4. 在 ready_list 中查找,如果: + - 存在任务:从链中删除并执行任务; + - 没有任务:无动作; + 5. 返回下个超时的任务的剩余时间; + +## END + diff --git a/components/system/source/k_kit/k_kit.c b/components/system/source/k_kit/k_kit.c new file mode 100755 index 0000000..e6c7c4a --- /dev/null +++ b/components/system/source/k_kit/k_kit.c @@ -0,0 +1,4437 @@ +/** + * @file k_kit.c + * @author LokLiang (lokliang@163.com) + * @brief 裸机版微内核 + * @version 1.1.0 + * + * @date 2023-01-01 添加 k_timer + * @date 2022-12-09 + * @date 2021-01-13 + * + * @copyright Copyright (c) 2022 + * + * 可被任意实时内核继承 + * 可管理任意数量的任务 + * 8个任务优先级 + * 允许任务 yield 和 sleep + * 极少的移植操作 + * 所有接口访问线程安全 + * + * 效率:在72MHz主频下 + * k_work_submit -- 3uS + * work scheduler -- 4uS + * k_fifo_put/k_fifo_take -- 2.5uS + * k_queue_alloc/k_queue_put -- 2.5uS + * k_pipe_poll_write/k_pipe_poll_read -- 1uS + * + * 任务和时间管理 + * -- 独立的任务队列 + * -- 所有任务对象可自由创建和删除 + * -- 拥有运行、就绪、延时、挂起四个状态 + * -- 栈来自所在的工作队列 + * -- 所有任务可挂到任意工作队列中 + * -- 可设置8档优先级(0..7) + * -- 在运行过程中允许让出CPU使用权 + * + * 软件定时器 + * -- 独立的任务队列 + * -- 与任务数据结构一致 + * -- 所有定时器对象可自由创建和删除 + * -- 拥有运行、就绪、延时、挂起四个状态 + * -- 栈来自所在的软定时器队列 + * -- 可设置8档优先级(0..7) + * + * 邮箱和邮件 + * -- 邮箱和邮件的数据空间由堆管理,邮件数量不限 + * -- 每个任务可创建和删除一个专属邮箱 + * -- 收到邮件的任务被置到就绪状态 + * -- 已提取的邮件在任务返回后被自动清理 + * -- 依赖临界操作和堆管理,效率较低 + * + * FIFO + * -- 队列和数据块的空间由堆管理,数量不限 + * -- 专用的数据块 + * -- 每个数据块长度不限 + * -- 依赖临界操作和堆管理,效率较低 + * -- 独立的对象,可脱离内核单独使用 + * + * 队列 + * -- 队列空间由堆管理,数量不限 + * -- 数据块由队列管理,数据结构化,数量和长度在创建队列时指定,无内存碎片 + * -- 临界时间短,效率较高 + * -- 独立的对象,可脱离内核单独使用 + * + * 管道 + * -- 管道空间由堆管理,数量不限 + * -- 允许两个线程同时读写数据流 + * -- 代码简短,适合在内存中执行 + * -- 不依赖临界操作和堆管理,通讯效率高 + * -- 独立的对象,可脱离内核单独使用 + * + * 堆 + * -- 管理对象为任意的内存空间 + * -- 申请的内存块管理数据长度为 sizeof(size_t) * 4 + * -- 效率和速度较高 + * -- 独立的对象,可脱离内核单独使用 + */ + +#include "k_kit.h" + +#include +#include + +/** + * @defgroup debug + * @{ + */ + +#ifndef CONFIG_K_KIT_LOG_ON +#define CONFIG_K_KIT_LOG_ON 1 /* 0: k_log_sched() 无效; 1: k_log_sched() 生效 */ +#endif + +#ifndef CONFIG_K_KIT_DBG_ON +#define CONFIG_K_KIT_DBG_ON 1 /* 允许 k_kit 打印错误日志 */ +#endif + +#ifndef CONFIG_K_KIT_DBG_COLOR_ON +#define CONFIG_K_KIT_DBG_COLOR_ON 1 /* 在打印的日志中添加颜色控制 */ +#endif + +#ifndef CONFIG_K_KIT_PRINT +#define CONFIG_K_KIT_PRINT printf /* 设置打印函数 */ +#endif + +#define _CONS_PRINT(FMT, ARG...) \ + do \ + { \ + if (CONFIG_K_KIT_DBG_ON) \ + { \ + CONFIG_K_KIT_PRINT(FMT, ##ARG); \ + } \ + } while (0) + +#if (CONFIG_K_KIT_DBG_COLOR_ON == 1) +#define _COLOR_R "\033[31m" /* 红 RED */ +#define _COLOR_G "\033[32m" /* 绿 GREEN */ +#define _COLOR_Y "\033[33m" /* 黄 YELLOW */ +#define _COLOR_B "\033[34m" /* 蓝 BLUE */ +#define _COLOR_P "\033[35m" /* 紫 PURPLE */ +#define _COLOR_C "\033[36m" /* 青 CYAN */ +#define _COLOR_RY "\033[41;33m" /* 红底黄字 */ +#define _COLOR_END "\033[0m" /* 结束 */ +#else +#define _COLOR_R "" /* 红 */ +#define _COLOR_G "" /* 绿 */ +#define _COLOR_Y "" /* 黄 */ +#define _COLOR_B "" /* 蓝 */ +#define _COLOR_P "" /* 紫 */ +#define _COLOR_C "" /* 青 */ +#define _COLOR_RY "" /* 红底黄字 */ +#define _COLOR_END "" /* 结束 */ +#endif + +#define _DO_SYS_LOG(FLAG, FMT, ARG...) \ + do \ + { \ + if (FLAG) \ + { \ + _CONS_PRINT(FMT, ##ARG); \ + } \ + } while (0) + +#define _FILENAME(FILE) (strrchr(FILE, '/') ? (strrchr(FILE, '/') + 1) : (strrchr(FILE, '\\') ? (strrchr(FILE, '\\') + 1) : FILE)) + +#define _GEN_DOMAIN "[KIT] " + +#define _SYS_LOG_COLOR(FLAG, INFO, COLOR, FMT, ...) _DO_SYS_LOG(FLAG, INFO "%s:%d \t%s -> " COLOR FMT _COLOR_END "\r\n", \ + _FILENAME(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__) + +#define _DBG_WRN(FMT, ...) _SYS_LOG_COLOR(CONFIG_K_KIT_DBG_ON, "[WRN] * " _GEN_DOMAIN, _COLOR_Y, FMT, ##__VA_ARGS__) + +#define _ASSERT_FALSE(EXP, FMT, ...) \ + do \ + { \ + if (EXP) \ + { \ + _CONS_PRINT("[ASS] # " _GEN_DOMAIN "%s:%d \t%s -> %s; ## " _COLOR_RY FMT _COLOR_END "\r\n", \ + _FILENAME(__FILE__), __LINE__, __FUNCTION__, #EXP, ##__VA_ARGS__); \ + do \ + { \ + volatile int FLAG = 0; \ + do \ + { \ + } while (FLAG == 0); \ + } while (0); \ + } \ + } while (0) + +/** + * @} debug + */ + +#define _SIGNED_MAX(N) ((int)((1u << (sizeof(N) * 8 - 1)) - 1)) + +#define _WORK ((k_work_handle_t *)work_handle->hdl) +#define _WORK_Q ((k_work_q_handle_t *)_WORK->work_q_handle->hdl) +#define _K_PORT (&s_kit_init_struct) +#define _K_CONTAINER_OF(PTR, TYPE, MEMBER) ((TYPE *)&((uint8_t *)PTR)[-(int)&((TYPE *)0)->MEMBER]) + +#define _TRUE true +#define _FALSE false + +#define _K_DIS_INT() _k_interrupt_save() +#define _K_DIS_SCHED() _k_scheduler_disable() + +#define _K_EN_INT() _k_interrupt_restore() +#define _K_EN_SCHED() _k_scheduler_enable() + +#define _K_FIFO_FREE 0x83459321 +#define _K_QUEUE_FREE 0x32478065 + +#define _SLIST_SET(Value1, Value2) (Value1 = (__list_node_t)Value2) +#define _SLIST_GET(Value) ((__slist_node_t *)(Value)) + +#define _DLIST_SET(Value1, Value2) (Value1 = (__list_node_t)Value2) +#define _DLIST_GET(Value) ((__dlist_node_t *)(Value)) + +typedef void *__list_node_t; + +typedef struct +{ + __list_node_t next; +} __slist_node_t; +typedef struct +{ + __list_node_t head; + __list_node_t tail; +} __slist_t; + +typedef struct +{ + __list_node_t next; + __list_node_t prev; +} __dlist_node_t; +typedef struct +{ + __list_node_t head; +} __dlist_t; + +typedef struct +{ + __slist_t mbox_valid; + __slist_t mbox_idle; +} k_work_mb_list_t; + +typedef struct +{ + __slist_node_t state_node; + __slist_t *state_list; + __dlist_node_t event_node; + k_tick_t timeout; + k_work_fn work_route; + void *arg; + k_work_q_t *work_q_handle; + k_work_t *work_handle; + uint8_t priority; + uint8_t exec_flag; + uint8_t exec_repeat; + uint8_t delete_flag : 1; /* 表示任务正在运行时被关闭,当运行完毕后资源被回收 */ + uint8_t obj_flag_type : 1; /* 0: 表示任务类型为 work; 1: 表示任务类型为 timer */ + uint8_t obj_flag_periodic : 1; /* 任务类型为 timer 时有效,表示使用自动重装 */ + uint8_t obj_flag_auto_del : 1; /* 任务类型为 timer 时有效,表示执行完成后自动删除对象 */ + union + { + k_work_mb_list_t *mbox_list; + k_tick_t period; + } obj_data; + +#if (CONFIG_K_KIT_LOG_ON) + const char *name; +#endif +} k_work_handle_t; + +typedef struct +{ + __dlist_t event_list; + __slist_t ready_list[8]; + __slist_t delayed_list; + unsigned priority_tab; + k_work_t *curr_work; + k_work_handle_t *yield_work; + k_work_q_fn work_q_resume; + void *work_q_resume_arg; + k_work_fn hook_entry; + k_tick_t timeout_early; + volatile unsigned resume_flag; +} k_work_q_handle_t; + +typedef struct +{ + union + { + __slist_node_t state_node; + k_work_t *work_handle; + } ctrl; +} k_work_mb_handle_t; + +typedef struct +{ + __slist_t list; + k_work_t *resume_work; + k_tick_t resume_delay; +} k_fifo_q_handle_t; + +typedef struct +{ + union + { + __slist_node_t state_node; + unsigned free_flag; + } ctrl; +} k_fifo_handle_t; + +typedef struct +{ + __slist_t list_valid; + __slist_t list_idle; + size_t item_size; + k_work_t *resume_work; + k_tick_t resume_delay; +} k_queue_handle_t; + +typedef struct +{ + union + { + __slist_node_t state_node; + unsigned free_flag; + } ctrl; + k_queue_t *queue_handle; +} k_queue_data_t; + +typedef volatile size_t pipe_id_t; + +typedef struct +{ + size_t size; // 缓存总大小 + pipe_id_t wid; // 已写进的下标 + pipe_id_t rid; // 已读出的下标 + k_work_t *resume_work; + k_tick_t resume_delay; +} k_pipe_handle_t; + +static struct +{ + unsigned interrupt_call; + unsigned interrupt_nest; + + k_work_t *curr_hook; + k_work_t *curr_log; + +#if (CONFIG_K_KIT_LOG_ON) + k_work_t *work_log[8]; + uint8_t log_index; + uint8_t log_max; +#endif +} s_cm_kit; + +static k_work_q_t work_q_hdl_main; +static k_timer_q_t timer_q_hdl_main; +k_work_q_t *default_work_q_hdl = &work_q_hdl_main; +k_timer_q_t *default_timer_q_hdl = &timer_q_hdl_main; + +static k_init_t s_kit_init_struct; + +static const uint8_t _tab_clz_8[] = { + 7, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // 0x00 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0x10 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x20 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xF0 +}; + +static void _slist_init_list(__slist_t *list); +static void _slist_init_node(__slist_node_t *node); +static int _slist_insert_font(__slist_t *list, __slist_node_t *node); +static int _slist_insert_tail(__slist_t *list, __slist_node_t *node); +static int _slist_remove(__slist_t *list, __slist_node_t *node); +static int _slist_remove_next(__slist_t *list, __slist_node_t *prev_node, __slist_node_t *remove_node); +static int _slist_insert_next(__slist_t *list, __slist_node_t *tar_node, __slist_node_t *new_node); +static __slist_node_t *_slist_take_head(__slist_t *list); +static __slist_node_t *_slist_peek_head(__slist_t *list); +static __slist_node_t *_slist_peek_tail(__slist_t *list); +static __slist_node_t *_slist_peek_next(__slist_node_t *node); +static __slist_node_t *_slist_peek_prev(__slist_t *list, __slist_node_t *node); +static int _slist_is_pending(__slist_node_t *node); + +static void _dlist_init_list(__dlist_t *list); +static void _dlist_init_node(__dlist_node_t *node); +static int _dlist_insert_tail(__dlist_t *list, __dlist_node_t *node); +static int _dlist_remove(__dlist_t *list, __dlist_node_t *node); +static __dlist_node_t *_dlist_take_head(__dlist_t *list); +static __dlist_node_t *_dlist_peek_head(__dlist_t *list); +static __dlist_node_t *_dlist_peek_tail(__dlist_t *list); +static __dlist_node_t *_dlist_peek_next(__dlist_t *list, __dlist_node_t *node); +static int _dlist_is_pending(__dlist_node_t *node); + +static void _slist_init_list(__slist_t *list) +{ + _SLIST_SET(list->head, 0); + _SLIST_SET(list->tail, 0); +} + +static void _slist_init_node(__slist_node_t *node) +{ + _SLIST_SET(node->next, 0); +} + +static int _slist_insert_font(__slist_t *list, __slist_node_t *node) +{ + __slist_node_t *head_node; + + if (_SLIST_GET(node->next) != NULL) + { + return -1; + } + + head_node = _SLIST_GET(list->head); + if (head_node) + { + _SLIST_SET(node->next, head_node); + } + else + { + _SLIST_SET(node->next, node); + _SLIST_SET(list->tail, node); + } + _SLIST_SET(list->head, node); + + return 0; +} + +static int _slist_insert_tail(__slist_t *list, __slist_node_t *node) +{ + if (_SLIST_GET(node->next) != NULL) + { + return -1; + } + + _SLIST_SET(node->next, node); + if (_SLIST_GET(list->head) == NULL) + { + _SLIST_SET(list->head, node); + } + else + { + __slist_node_t *tail_node = (__slist_node_t *)_SLIST_GET(list->tail); + + _SLIST_SET(tail_node->next, node); + } + _SLIST_SET(list->tail, node); + + return 0; +} + +static int _slist_remove(__slist_t *list, __slist_node_t *node) +{ + __slist_node_t *list_head; + __slist_node_t *node_next; + + if (node == NULL || list == NULL) + { + return -1; + } + + list_head = _SLIST_GET(list->head); + node_next = _SLIST_GET(node->next); + if (node_next == NULL || list_head == NULL) + { + return -1; + } + + if (node == list_head) // node 是首个节点 + { + if (node == _SLIST_GET(list->tail)) // node 是最后一个节点 + { + _SLIST_SET(list->head, 0); + _SLIST_SET(list->tail, 0); + } + else + { + _SLIST_SET(list->head, node_next); + } + } + else // node 不是首个节点 + { + __slist_node_t *prev_node = _slist_peek_prev(list, node); + if (prev_node == NULL) + { + return -1; + } + + if (node == _SLIST_GET(list->tail)) // node 是最后一个节点 + { + _SLIST_SET(list->tail, prev_node); + _SLIST_SET(prev_node->next, prev_node); + } + else + { + _SLIST_SET(prev_node->next, node_next); + } + } + + _SLIST_SET(node->next, 0); + + return 0; +} + +static int _slist_remove_next(__slist_t *list, __slist_node_t *prev_node, __slist_node_t *remove_node) +{ + __slist_node_t *list_head; + __slist_node_t *node_next; + + if (remove_node == NULL || list == NULL) + { + return -1; + } + + list_head = _SLIST_GET(list->head); + node_next = _SLIST_GET(remove_node->next); + if (node_next == NULL || list_head == NULL) + { + return -1; + } + + if (remove_node == list_head) // remove_node 是首个节点 + { + if (remove_node == _SLIST_GET(list->tail)) // remove_node 是最后一个节点 + { + _SLIST_SET(list->head, 0); + _SLIST_SET(list->tail, 0); + } + else + { + _SLIST_SET(list->head, node_next); + } + } + else // remove_node 不是首个节点 + { + if (prev_node == NULL) + { + prev_node = _slist_peek_prev(list, remove_node); + if (prev_node == NULL) + { + return -1; + } + } + + if (remove_node == _SLIST_GET(list->tail)) // remove_node 是最后一个节点 + { + _SLIST_SET(list->tail, prev_node); + _SLIST_SET(prev_node->next, prev_node); + } + else + { + _SLIST_SET(prev_node->next, node_next); + } + } + + _SLIST_SET(remove_node->next, 0); + + return 0; +} + +static int _slist_insert_next(__slist_t *list, __slist_node_t *tar_node, __slist_node_t *new_node) +{ + __slist_node_t *tar_node_next = _SLIST_GET(tar_node->next); + + if (_SLIST_GET(list->head) == NULL || tar_node_next == NULL || _SLIST_GET(new_node->next) != NULL) + { + return -1; + } + + if (tar_node == _SLIST_GET(list->tail)) // tar_node 是最后一个节点 + { + _SLIST_SET(new_node->next, new_node); + _SLIST_SET(tar_node->next, new_node); + _SLIST_SET(list->tail, new_node); + } + else + { + _SLIST_SET(new_node->next, tar_node_next); + _SLIST_SET(tar_node->next, new_node); + } + + return 0; +} + +static __slist_node_t *_slist_take_head(__slist_t *list) +{ + __slist_node_t *ret = _SLIST_GET(list->head); + _slist_remove(list, ret); + return ret; +} + +static __slist_node_t *_slist_peek_head(__slist_t *list) +{ + return _SLIST_GET(list->head); +} + +static __slist_node_t *_slist_peek_tail(__slist_t *list) +{ + return _SLIST_GET(list->tail); +} + +static __slist_node_t *_slist_peek_next(__slist_node_t *node) +{ + __slist_node_t *next_node = _SLIST_GET(node->next); + if (next_node == node) + { + return NULL; + } + else + { + return next_node; + } +} + +static __slist_node_t *_slist_peek_prev(__slist_t *list, __slist_node_t *node) +{ + __slist_node_t *prev_node = NULL; + __slist_node_t *test_node = _SLIST_GET(list->head); + while (test_node && test_node != node && prev_node != test_node) + { + prev_node = test_node; + test_node = _SLIST_GET(test_node->next); + } + if (test_node == node) + { + return prev_node; + } + + return NULL; +} + +static int _slist_is_pending(__slist_node_t *node) +{ + return (_SLIST_GET(node->next) != NULL); +} + +static void _dlist_init_list(__dlist_t *list) +{ + _DLIST_SET(list->head, 0); +} + +static void _dlist_init_node(__dlist_node_t *node) +{ + _DLIST_SET(node->next, 0); + _DLIST_SET(node->prev, 0); +} + +static int _dlist_insert_tail(__dlist_t *list, __dlist_node_t *node) +{ + __dlist_node_t *first_node; + + if (_DLIST_GET(node->next) != NULL || _DLIST_GET(node->prev) != NULL) + { + return -1; + } + + first_node = _DLIST_GET(list->head); + if (first_node == NULL) + { + /* 直接设置链头 */ + _DLIST_SET(node->next, node); + _DLIST_SET(node->prev, node); + _DLIST_SET(list->head, node); + } + else + { + __dlist_node_t *first_node_prev = _DLIST_GET(first_node->prev); + + _DLIST_SET(node->next, first_node); + _DLIST_SET(node->prev, first_node_prev); + _DLIST_SET(first_node_prev->next, node); + _DLIST_SET(first_node->prev, node); + } + + return 0; +} + +static int _dlist_remove(__dlist_t *list, __dlist_node_t *node) +{ + __dlist_node_t *first_node; + __dlist_node_t *node_next; + + if (node == NULL || list == NULL) + { + return -1; + } + first_node = _DLIST_GET(list->head); + node_next = _DLIST_GET(node->next); + if (node_next == NULL || first_node == NULL) + { + return -1; + } + + if (node_next == node) // 是最后一节点 + { + _DLIST_SET(list->head, 0); + } + else // 不是最后一节点 + { + __dlist_node_t *node_prev = _DLIST_GET(node->prev); + if (first_node == node) + { + _DLIST_SET(list->head, node_next); // 链头指向下一节点 + } + _DLIST_SET(node_prev->next, node_next); + _DLIST_SET(node_next->prev, node_prev); + } + + _DLIST_SET(node->next, 0); + _DLIST_SET(node->prev, 0); + + return 0; +} + +static __dlist_node_t *_dlist_take_head(__dlist_t *list) +{ + __dlist_node_t *ret = _DLIST_GET(list->head); + _dlist_remove(list, ret); + return ret; +} + +static __dlist_node_t *_dlist_peek_head(__dlist_t *list) +{ + return _DLIST_GET(list->head); +} + +static __dlist_node_t *_dlist_peek_tail(__dlist_t *list) +{ + __dlist_node_t *list_head = _DLIST_GET(list->head); + if (list_head == NULL) + { + return NULL; + } + else + { + return _DLIST_GET(list_head->prev); + } +} + +static __dlist_node_t *_dlist_peek_next(__dlist_t *list, __dlist_node_t *node) +{ + __dlist_node_t *ret = _DLIST_GET(node->next); + if (_dlist_peek_head(list) == ret) + { + ret = NULL; + } + return ret; +} + +static int _dlist_is_pending(__dlist_node_t *node) +{ + return (_DLIST_GET(node->next) != NULL); +} + +static void _k_interrupt_save(void) +{ + if (s_cm_kit.interrupt_call == 0) + { + s_cm_kit.interrupt_nest = _K_PORT->interrupt_save(); + } + s_cm_kit.interrupt_call++; +} + +static void _k_interrupt_restore(void) +{ + s_cm_kit.interrupt_call--; + _ASSERT_FALSE((int)s_cm_kit.interrupt_call < 0, "error"); + if (s_cm_kit.interrupt_call == 0) + { + _K_PORT->interrupt_restore(s_cm_kit.interrupt_nest); + } +} + +static void _k_scheduler_disable(void) +{ + _K_PORT->scheduler_disable(); +} + +static void _k_scheduler_enable(void) +{ + _K_PORT->scheduler_enable(); +} + +static void _k_nop(void) +{ +} + +static k_work_q_t *_k_nop_get_work_q_hdl(void) +{ + return default_work_q_hdl; +} + +static void _k_read_curr_handle(k_work_q_t **work_q_handle, k_work_t **work_handle) +{ + *work_q_handle = _K_PORT->get_work_q_hdl(); + *work_handle = ((k_work_q_handle_t *)(*work_q_handle)->hdl)->curr_work; +} + +/** + * @brief 删除指定邮箱队列中的邮件 + * + * @param list 邮箱队列,mbox_idle 或 mbox_valid + */ +static void _k_work_mbox_clr_list(__slist_t *list) +{ + for (;;) + { + __slist_node_t *node = _slist_take_head(list); + if (node == NULL) + { + break; + } + else + { + k_work_mb_handle_t *mbox_data = _K_CONTAINER_OF(node, k_work_mb_handle_t, ctrl.state_node); + _K_PORT->free(mbox_data); + } + } +} + +/** + * @brief 执行删除任务 + * + * @param work_handle 由 k_work_create() 初始化 + */ +static void _k_work_delete(k_work_t *work_handle) +{ + if (work_handle == NULL) + { + k_work_q_t *work_q_handle; + _k_read_curr_handle(&work_q_handle, &work_handle); + } + + if (work_handle->hdl != NULL) + { + k_work_handle_t *work = work_handle->hdl; + + if (work->obj_flag_type == 0 && work->obj_data.mbox_list != NULL) + { + k_work_mb_list_t *mbox_list = work->obj_data.mbox_list; + if (mbox_list) + { + _k_work_mbox_clr_list(&mbox_list->mbox_idle); + _k_work_mbox_clr_list(&mbox_list->mbox_valid); + _K_PORT->free(mbox_list); + work->obj_data.mbox_list = NULL; + } + } + _slist_remove(work->state_list, &work->state_node); + if (_dlist_is_pending(&work->event_node)) + { + k_work_q_handle_t *work_q = work->work_q_handle->hdl; + _dlist_remove(&work_q->event_list, &work->event_node); + } + _K_PORT->free(work_handle->hdl); + +#if (CONFIG_K_KIT_LOG_ON) + { + unsigned max_index = sizeof(s_cm_kit.work_log) / sizeof(s_cm_kit.work_log[0]); + unsigned index; + + if (max_index > s_cm_kit.log_index) + { + max_index = s_cm_kit.log_index; + } + + for (index = 0; index < max_index; index++) + { + if (s_cm_kit.work_log[index] == work_handle) + { + unsigned i; + for (i = index; i + 1 < max_index; i++) + { + s_cm_kit.work_log[i] = s_cm_kit.work_log[i + 1]; + } + s_cm_kit.work_log[i] = NULL; + s_cm_kit.log_index--; + s_cm_kit.log_max--; + break; + } + } + } +#endif + + work_handle->hdl = NULL; + } +} + +/** + * @brief 从信号链 (work_q->event_list) 提取任务,并根据时间设置插入到延时链或直接插入到就绪链或中 + * + * @param event_list 来源链 + * @param sys_time 当前系统时间 + */ +static void _k_work_q_handler_take_event_list(__dlist_t *event_list, k_tick_t sys_time) +{ + if (!_dlist_peek_head(event_list)) + { + return; + } + + for (;;) + { + __dlist_node_t *event_node; + + _K_DIS_INT(); + event_node = _dlist_take_head(event_list); + if (event_node == NULL) + { + _K_EN_INT(); + break; + } + else + { + k_work_handle_t *opt_work = _K_CONTAINER_OF(event_node, k_work_handle_t, event_node); + _K_EN_INT(); + + /* 根据时间设置,把任务插入到延时链或直接插入到就绪链或中 */ + _K_DIS_SCHED(); + if (opt_work->timeout - sys_time > 0) + { + /* 按时间排序,插入到 work_q->delayed_list */ + k_work_q_handle_t *work_q = opt_work->work_q_handle->hdl; + __slist_t *new_list = &work_q->delayed_list; + __slist_node_t *work_node; + __slist_node_t *test_node; + + _slist_remove(opt_work->state_list, &opt_work->state_node); + opt_work->state_list = new_list; + work_node = &opt_work->state_node; + test_node = _slist_peek_head(new_list); + + if (test_node == NULL) + { + work_q->timeout_early = opt_work->timeout; + _slist_insert_font(new_list, work_node); + } + else + { + __slist_node_t *end_node = _slist_peek_tail(new_list); + __slist_node_t *prev_node = NULL; + k_work_handle_t *test_work = _K_CONTAINER_OF(test_node, k_work_handle_t, state_node); + + work_q->timeout_early = test_work->timeout; + + do + { + if ((int)(opt_work->timeout - test_work->timeout) < 0) + { + break; + } + + prev_node = test_node; + test_node = _slist_peek_next(test_node); + + test_work = _K_CONTAINER_OF(test_node, k_work_handle_t, state_node); + + } while (prev_node != end_node); + + if (prev_node == NULL) + { + work_q->timeout_early = opt_work->timeout; + _slist_insert_font(new_list, work_node); + } + else + { + _slist_insert_next(new_list, prev_node, work_node); + } + } + } + else + { + /* 直接插入到 work_q->ready_list[] */ + k_work_q_handle_t *work_q = opt_work->work_q_handle->hdl; + __slist_t *new_list = &work_q->ready_list[opt_work->priority]; + if (opt_work->state_list != new_list) + { + _slist_remove(opt_work->state_list, &opt_work->state_node); + _slist_insert_tail(new_list, &opt_work->state_node); + opt_work->state_list = new_list; + work_q->priority_tab |= 1 << opt_work->priority; + } + } + _K_EN_SCHED(); + } + } +} + +/** + * @brief 从延时链 (work_q->delayed_list) 中把到所有到达时间的任务插入到就绪链中 + * + * @param src_list 来源链 + * @return k_tick_t 距离下个延时任务的时间 + */ +static k_tick_t _k_work_q_handler_take_delay_list(k_work_q_handle_t *work_q, k_tick_t sys_time) +{ + int ret; + + _k_work_q_handler_take_event_list(&work_q->event_list, sys_time); + + if ((int)(sys_time - *(volatile int *)&work_q->timeout_early) >= 0) + { + for (;;) + { + __slist_node_t *delayed_node; + + _K_DIS_SCHED(); + delayed_node = _slist_peek_head(&work_q->delayed_list); + + if (delayed_node != NULL) + { + k_work_handle_t *opt_work = _K_CONTAINER_OF(delayed_node, k_work_handle_t, state_node); + + if (opt_work->work_q_handle->hdl == NULL) + { + _k_work_delete(opt_work->work_handle); + } + else + { + if ((int)(sys_time - opt_work->timeout) >= 0) + { + _slist_remove(opt_work->state_list, &opt_work->state_node); + opt_work->state_list = &work_q->ready_list[7 - _tab_clz_8[1 << opt_work->priority]]; + _slist_insert_tail(opt_work->state_list, &opt_work->state_node); + work_q->priority_tab |= 1 << opt_work->priority; + } + else + { + work_q->timeout_early = opt_work->timeout; + _K_EN_SCHED(); + break; + } + } + } + else + { + work_q->timeout_early = sys_time + _SIGNED_MAX(sys_time); + _K_EN_SCHED(); + break; + } + _K_EN_SCHED(); + } + } + + ret = work_q->timeout_early - sys_time; + if (ret < 0) + { + ret = 0; + } + return ret; +} + +/** + * @brief 获取下个可执行的任务对象 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @return k_work_handle_t* 下个可执行的任务对象 + */ +static k_work_handle_t *_k_work_q_handler_take_ready_work(k_work_q_t *work_q_handle) +{ + for (;;) + { + k_work_q_handle_t *work_q; + + _K_DIS_SCHED(); + work_q = work_q_handle->hdl; + if (work_q == NULL || work_q->priority_tab == 0) + { + _K_EN_SCHED(); + return NULL; + } + else + { + int prio; + __slist_t *state_list; + __slist_node_t *state_node; + k_work_handle_t *exec_work; + + prio = 7 - _tab_clz_8[work_q->priority_tab]; + if (work_q->yield_work != NULL && prio < work_q->yield_work->priority) + { + _K_EN_SCHED(); + return NULL; + } + state_list = &work_q->ready_list[prio]; + state_node = _slist_take_head(state_list); + exec_work = NULL; + if (state_node == NULL) + { + work_q->priority_tab &= ~(1 << prio); + } + else + { + exec_work = _K_CONTAINER_OF(state_node, k_work_handle_t, state_node); + if (_slist_peek_head(state_list) == NULL) + { + work_q->priority_tab &= ~(1 << prio); + } + if (exec_work->exec_flag == 0) + { + work_q->curr_work = exec_work->work_handle; + } + exec_work->state_list = NULL; + } + _K_EN_SCHED(); + + if (exec_work != NULL) + { + if (exec_work->exec_flag == 0) + { + return exec_work; + } + else + { + exec_work->exec_repeat = 1; + } + } + } + } +} + +/** + * @brief 用于记录和打印调度日志 + * + * @param work_handle 由 k_work_create() 初始化 + */ +static void _k_work_log(void *arg) +{ +#if (CONFIG_K_KIT_LOG_ON) + + k_work_q_t *work_q_handle; + k_work_t *work_handle; + unsigned max_index; + + _K_DIS_SCHED(); + + _k_read_curr_handle(&work_q_handle, &work_handle); + max_index = sizeof(s_cm_kit.work_log) / sizeof(s_cm_kit.work_log[0]); + if (max_index > s_cm_kit.log_index) + { + max_index = s_cm_kit.log_index; + } + while (max_index) + { + if (s_cm_kit.work_log[--max_index] == work_handle) + { + s_cm_kit.log_index = max_index + 1; + break; + } + } + + if (s_cm_kit.work_log[max_index] != work_handle) + { + if (s_cm_kit.log_index < sizeof(s_cm_kit.work_log) / sizeof(s_cm_kit.work_log[0])) + { + s_cm_kit.work_log[s_cm_kit.log_index] = work_handle; + } + + if (s_cm_kit.log_index <= sizeof(s_cm_kit.work_log) / sizeof(s_cm_kit.work_log[0])) + { + s_cm_kit.log_index++; + s_cm_kit.log_max = s_cm_kit.log_index; + } + } + + _K_EN_SCHED(); + +#endif +} + +/** + * @brief 把任务插入到 work_q->event_list 中 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @param work_handle 由 k_work_create() 初始化 + * @param timeout 指定的唤醒时间点 + * @param wait_forever 是否永远等待 + */ +static void _k_work_do_submit(k_work_q_t *work_q_handle, k_work_t *work_handle, k_tick_t timeout, bool wait_forever) +{ + k_work_handle_t *work = work_handle->hdl; + k_work_q_handle_t *work_q = work_q_handle->hdl; + int resume_flag = 1; + + if (work_handle == NULL || work == NULL) + { + _DBG_WRN("work_handle invalid"); + return; + } + if (work_q_handle == NULL || work_q == NULL) + { + _DBG_WRN("work_q_handle invalid"); + return; + } + + if (wait_forever) + { + if (work->work_q_handle != work_q_handle || + _dlist_is_pending(&work->event_node)) + { + _K_DIS_INT(); + + if (work_handle->hdl != NULL) + { + if (_dlist_is_pending(&work->event_node)) + { + k_work_q_handle_t *work_q_old = work->work_q_handle->hdl; + _dlist_remove(&work_q_old->event_list, &work->event_node); + } + work->work_q_handle = work_q_handle; + work->work_handle = work_handle; + } + + _K_EN_INT(); + } + } + else + { + if (work->timeout != timeout || + work->work_q_handle != work_q_handle || + !_dlist_is_pending(&work->event_node)) + { + _K_DIS_INT(); + + if (work_handle->hdl != NULL) + { + work->timeout = timeout; + if (work->work_q_handle != work_q_handle) + { + if (_dlist_is_pending(&work->event_node)) + { + k_work_q_handle_t *work_q_old = work->work_q_handle->hdl; + _dlist_remove(&work_q_old->event_list, &work->event_node); + } + } + work->work_q_handle = work_q_handle; + if (!_dlist_is_pending(&work->event_node)) + { + _dlist_insert_tail(&work_q->event_list, &work->event_node); + work->work_handle = work_handle; + resume_flag = work_q->resume_flag; + } + } + + _K_EN_INT(); + } + + if (resume_flag == 0) + { + work_q->resume_flag = 1; + if (work_q->work_q_resume != NULL) + { + work_q->work_q_resume(work_q->work_q_resume_arg); + } + } + } +} + +/** + * @brief 由内部通讯模块所用,执行唤醒已注册的任务。 + * + * @param resume_work 已注册的任务 + * @param resume_delay 唤醒延时 + * @param resume_mode 预留的。 resume_delay 为非 0 时有效: + * 0 - 计时从首次收到信号后开始; + * 1 - 计时从最后一次收到信号后开始; + */ +static void _k_ipc_resume(k_work_t *resume_work, k_tick_t resume_delay, int resume_mode) +{ + if (resume_work != NULL) + { + k_work_handle_t *work = resume_work->hdl; + if (work != NULL && work->work_q_handle != NULL) + { + if (resume_delay == 0) + { + if (!k_work_is_pending(resume_work)) + { + _k_work_do_submit(work->work_q_handle, resume_work, _K_PORT->get_sys_ticks(), _FALSE); + } + } + else + { + if (resume_mode != 0 || !k_work_is_pending(resume_work)) + { + k_work_submit(work->work_q_handle, resume_work, resume_delay); + } + } + } + } +} + +/** + * @brief 当被执行完的任务为软件定时器时,按时间重新提交一次到队列中 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @param exec_work 被执行的任务对象 + */ +static void _k_timer_end(k_work_q_t *work_q_handle, k_work_handle_t *exec_work) +{ + if (exec_work->obj_flag_auto_del) + { + k_work_t work_handle; + + work_handle.hdl = exec_work; + exec_work->work_handle = &work_handle; + k_work_delete(&work_handle); + } + else if (exec_work->obj_flag_periodic) + { + k_tick_t timeout = exec_work->timeout + + exec_work->obj_data.period + + (_K_PORT->get_sys_ticks() - exec_work->timeout) / exec_work->obj_data.period * exec_work->obj_data.period; + _k_work_do_submit(work_q_handle, exec_work->work_handle, timeout, _FALSE); + } +} + +/** + * @brief 初始化微内核 + * 建议初始化后再配置系统的定时器,每1毫秒产生一次中断 + * + * @param init_struct 初始化参数 + */ +void k_init(const k_init_t *init_struct) +{ + if (s_kit_init_struct.malloc != NULL) + { + _DBG_WRN("Duplicate initialization"); + } + _ASSERT_FALSE(init_struct->malloc == NULL || + init_struct->free == 0 || + init_struct->interrupt_save == NULL || + init_struct->interrupt_restore == NULL, + "Parameter error"); + + memcpy(&s_kit_init_struct, init_struct, sizeof(s_kit_init_struct)); + + if (init_struct->scheduler_disable == NULL || init_struct->scheduler_enable == NULL) + { + s_kit_init_struct.scheduler_disable = _k_nop; + s_kit_init_struct.scheduler_enable = _k_nop; + } + if (init_struct->get_work_q_hdl == NULL) + { + s_kit_init_struct.get_work_q_hdl = _k_nop_get_work_q_hdl; + } + + memset(&s_cm_kit, 0, sizeof(s_cm_kit)); +} + +/** + * @brief 取消初始化微内核 + * + */ +void k_deinit(void) +{ + memset(&s_kit_init_struct, 0, sizeof(s_kit_init_struct)); +} + +/** + * @brief 工作队列执行入口 + * 所有在队列中就绪的任务都将被按顺序逐个执行。 + * 执行顺序为: + * 1. 按任务的优先级 + * 2. 按就绪的顺序 + * 栈用量 32 bytes + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @return k_tick_t 距离下个延时任务的时间 + */ +k_tick_t k_work_q_handler(k_work_q_t *work_q_handle) +{ + k_work_q_handle_t *work_q; + k_work_t *last_work; + k_tick_t ret; + + if (s_kit_init_struct.malloc == NULL) + { + return -1u; + } + + work_q = work_q_handle->hdl; + + if (work_q == NULL) + { + return -1u; + } + + last_work = work_q->curr_work; + + if (last_work != NULL) + { + _DBG_WRN("Not recommended executing this function in work_handler"); + } + + do + { + k_work_handle_t *exec_work; + + if (work_q->priority_tab != 0) + { + exec_work = _k_work_q_handler_take_ready_work(work_q_handle); + if (exec_work != NULL) + { + exec_work->exec_flag = 1; + + if (exec_work->obj_flag_type) + { + exec_work->work_route(exec_work->arg); + _k_timer_end(work_q_handle, exec_work); + } + else + { + if (s_cm_kit.curr_hook != exec_work->work_handle) + { + k_work_fn hook_entry; + + s_cm_kit.curr_hook = exec_work->work_handle; + _k_work_log(exec_work->work_handle); + + hook_entry = work_q->hook_entry; + if (hook_entry) + { + hook_entry(exec_work->work_handle); + } + } + + exec_work->work_route(exec_work->arg); + } + work_q->curr_work = last_work; + + if (exec_work->delete_flag == 0) + { + _ASSERT_FALSE(!k_work_is_valid(exec_work->work_handle), "exec_work->work_handle maybe be deleted"); + + exec_work->exec_flag = 0; + if (exec_work->obj_flag_type == 0 && exec_work->obj_data.mbox_list != NULL) + { + if (_slist_peek_head(&exec_work->obj_data.mbox_list->mbox_idle) != NULL) + { + _K_DIS_SCHED(); + _k_work_mbox_clr_list(&exec_work->obj_data.mbox_list->mbox_idle); + _K_EN_SCHED(); + } + } + } + else + { + k_work_delete(exec_work->work_handle); + } + } + } + else + { + exec_work = NULL; + } + + ret = _k_work_q_handler_take_delay_list(work_q, _K_PORT->get_sys_ticks()); + + if (exec_work != NULL) + { + break; + } + + } while (*(volatile uint8_t *)&work_q->priority_tab != 0); + + if (work_q->priority_tab != 0) + { + ret = 0; + } + + if (last_work != NULL) + { + if (((k_work_handle_t *)last_work->hdl)->exec_repeat != 0) + { + ((k_work_handle_t *)last_work->hdl)->exec_repeat = 0; + k_work_submit(((k_work_handle_t *)last_work->hdl)->work_q_handle, + ((k_work_handle_t *)last_work->hdl)->work_handle, + 0); + } + } + + if (work_q->resume_flag) + { + work_q->resume_flag = 0; + ret = 0; + } + + if (work_q->curr_work) + { + if (s_cm_kit.curr_hook != work_q->curr_work) + { + k_work_fn hook_entry; + + s_cm_kit.curr_hook = work_q->curr_work; + _k_work_log(work_q->curr_work); + + hook_entry = work_q->hook_entry; + if (hook_entry) + { + hook_entry(work_q->curr_work); + } + } + } + + return ret; +} + +/** + * @brief 创建一个工作队列的对象 + * 对象数据由堆自动分配 + * 对象内存: 112 bytes + * + * @param work_q_handle[out] 初始化队列对象指针 + * @return k_err_t 0 -- 成功, -1 -- 堆内存不足 + */ +k_err_t k_work_q_create(k_work_q_t *work_q_handle) +{ + k_work_q_handle_t *work_q; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + _K_DIS_SCHED(); + work_q = _K_PORT->malloc(sizeof(k_work_q_handle_t)); + if (work_q != NULL) + { + unsigned i; + + memset(work_q, 0, sizeof(*work_q)); + work_q_handle->hdl = work_q; + work_q->work_q_resume = NULL; + work_q->work_q_resume_arg = NULL; + for (i = 0; i < sizeof(work_q->ready_list) / sizeof(work_q->ready_list[0]); i++) + { + _slist_init_list(&work_q->ready_list[i]); + } + _slist_init_list(&work_q->delayed_list); + _dlist_init_list(&work_q->event_list); + } + _K_EN_SCHED(); + + if (work_q == NULL) + { + _DBG_WRN("Insufficient memory"); + return -1; + } + else + { + return 0; + } +} + +/** + * @brief 删除一个工作队列的对象 + * 1. 在该队列中所有已就绪的任务被立即删除 + * 2. 延时中的任务在到达时间后被立即删除 + * 3. 工作队列被删除 + * 4. 工作队列及相关任务的堆内存被自动回收 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + */ +void k_work_q_delete(k_work_q_t *work_q_handle) +{ + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + _K_DIS_SCHED(); + if (work_q_handle->hdl != NULL) + { + k_work_q_handle_t *work_q = work_q_handle->hdl; + + while (*(volatile uint8_t *)&work_q->priority_tab != 0) + { + __slist_node_t *state_node = _slist_take_head(&work_q->ready_list[7 - _tab_clz_8[work_q->priority_tab]]); + if (state_node == NULL) + { + break; + } + else + { + k_work_handle_t *work = _K_CONTAINER_OF(state_node, k_work_handle_t, state_node); + _k_work_delete(work->work_handle); + } + } + + for (;;) + { + __dlist_node_t *node = _dlist_take_head(&work_q->event_list); + if (node == NULL) + { + break; + } + else + { + k_work_handle_t *work = _K_CONTAINER_OF(node, k_work_handle_t, event_node); + _k_work_delete(work->work_handle); + } + } + + for (;;) + { + __slist_node_t *state_node = _slist_take_head(&work_q->delayed_list); + if (state_node == NULL) + { + break; + } + else + { + k_work_handle_t *work = _K_CONTAINER_OF(state_node, k_work_handle_t, state_node); + _k_work_delete(work->work_handle); + } + } + + if (work_q->curr_work != NULL) + { + _k_work_delete(work_q->curr_work); + } + + _K_PORT->free(work_q_handle->hdl); + work_q_handle->hdl = NULL; + } + _K_EN_SCHED(); +} + +/** + * @brief 注册一个函数:当有任务被提交时,此回调函数将被自动执行 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @param work_q_resume 当有任务被提交时,被执行的回调函数将 + * @param arg 回调函数将附带的参数 + */ +void k_work_q_resume_regist(k_work_q_t *work_q_handle, k_work_q_fn work_q_resume, void *arg) +{ + if (k_work_q_is_valid(work_q_handle)) + { + k_work_q_handle_t *work_q = work_q_handle->hdl; + work_q->work_q_resume_arg = arg; + work_q->work_q_resume = work_q_resume; + } +} + +/** + * @brief 获取工作队列是否有效 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @retval true 对象有效 + * @retval false 对象无效 + */ +bool k_work_q_is_valid(k_work_q_t *work_q_handle) +{ + if (s_kit_init_struct.malloc == NULL || work_q_handle == NULL || work_q_handle->hdl == NULL) + { + return _FALSE; + } + else + { + return _TRUE; + } +} + +/** + * @brief 设置切换任务时,进入和退出任务时回调函数 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @param entry 在执行一个新任务时的回调函数,为 NULL 时不回调,注意同一个任务不会被连续回调 + */ +void k_work_hook(k_work_q_t *work_q_handle, k_work_fn hook) +{ + k_work_q_handle_t *work_q = work_q_handle->hdl; + work_q->hook_entry = hook; +} + +/** + * @brief 获取是否有正在延时状态的任务 + * + * @retval true 有延时状态的任务 + * @retval false 队列已空 + */ +bool k_work_q_delayed_state(k_work_q_t *work_q_handle) +{ + k_work_q_handle_t *work_q = work_q_handle->hdl; + if (_dlist_peek_head(&work_q->event_list) != NULL) + { + return _TRUE; + } + if (_slist_peek_head(&work_q->delayed_list) != NULL) + { + return _TRUE; + } + return _FALSE; +} + +/** + * @brief 获取工作队列中是否有就绪的任务 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @retval true 有就绪的任务 + * @retval false 队列已空 + */ +bool k_work_q_ready_state(k_work_q_t *work_q_handle) +{ + k_work_q_handle_t *work_q = work_q_handle->hdl; + if (work_q == NULL || work_q->priority_tab == 0) + { + return _FALSE; + } + else + { + return _FALSE; + } +} + +/** + * @brief 创建由工作队列管理的任务 + * 任务对象数据由堆内存自动分配 + * 任务必须返回 + * 任务堆栈来自 k_work_q_handler() + * 对象内存: 48 bytes + * + * @param work_handle[out] 初始化任务对象数据 + * @param name 任务名 + * @param work_route 任务的入口地址 + * @param arg 附带的一个参数指针 + * @param priority 任务优先级,0 .. 7,0 为最低 + * @return k_err_t 0 -- 成功,-1 -- 失败:对象非空或堆内存不足 + */ +k_err_t k_work_create(k_work_t *work_handle, + const char *name, + k_work_fn work_route, + void *arg, + uint8_t priority) +{ + k_work_handle_t *work; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + if (work_route == NULL) + { + _DBG_WRN("work_route invalid"); + return -1; + } + + if (priority >= (8 - _tab_clz_8[sizeof(_tab_clz_8) - 1])) + { + _DBG_WRN("Param over range: priority = %d, New value is %d", + priority, + priority % (8 - _tab_clz_8[sizeof(_tab_clz_8) - 1]) % + (sizeof(((k_work_q_handle_t *)0)->ready_list) / sizeof(((k_work_q_handle_t *)0)->ready_list[0]))); + } + + _K_DIS_SCHED(); + work = _K_PORT->malloc(sizeof(k_work_handle_t)); + _K_EN_SCHED(); + + if (work) + { + memset(work, 0, sizeof(*work)); + work->work_handle = work_handle; + work->work_route = work_route; + work->arg = arg; + work->priority = priority % (8 - _tab_clz_8[sizeof(_tab_clz_8) - 1]) % + (sizeof(((k_work_q_handle_t *)0)->ready_list) / sizeof(((k_work_q_handle_t *)0)->ready_list[0])); + _slist_init_node(&work->state_node); + _dlist_init_node(&work->event_node); +#if (CONFIG_K_KIT_LOG_ON) + work->name = name; +#endif + work_handle->hdl = work; + return 0; + } + else + { + _DBG_WRN("Insufficient memory"); + return -1; + } +} + +/** + * @brief 删除由工作队列管理的任务 + * 任务立即失效 + * 任务对象数据被立即回收 + * + * @param work_handle 由 k_work_create() 初始化 + */ +void k_work_delete(k_work_t *work_handle) +{ + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + if (work_handle == NULL) + { + k_work_q_t *work_q_handle; + _k_read_curr_handle(&work_q_handle, &work_handle); + } + + _K_DIS_SCHED(); + k_work_handle_t *work = work_handle->hdl; + if (work->exec_flag == 0) + { + _k_work_delete(work_handle); + } + else + { + work->delete_flag = 1; + } + _K_EN_SCHED(); +} + +/** + * @brief 获取任务对象是否有效 + * + * @param work_handle 由 k_work_create() 初始化 + * @retval true 任务对象有效 + * @retval false 任务对象无效 + */ +bool k_work_is_valid(k_work_t *work_handle) +{ + if (work_handle == NULL || work_handle->hdl == NULL) + { + return _FALSE; + } + else + { + return _TRUE; + } +} + +/** + * @brief 获取任务是否在就绪或延时的状态 + * + * @param work_handle 由 k_work_create() 初始化 + * @retval true 任务就绪或延时 + * @retval false 任务被挂起 + */ +bool k_work_is_pending(k_work_t *work_handle) +{ + k_work_handle_t *work = work_handle->hdl; + + if (work != NULL) + { + if (work->exec_flag != 0) + { + return _TRUE; + } + if (_dlist_is_pending(&work->event_node)) + { + return _TRUE; + } + if (_slist_is_pending(&work->state_node)) + { + return _TRUE; + } + } + + return _FALSE; +} + +/** + * @brief 获取任务距离下个执行的剩余时间 + * + * @param work_handle + * @return k_tick_t + */ +k_tick_t k_work_time_remain(k_work_t *work_handle) +{ + k_work_handle_t *work = work_handle->hdl; + k_tick_t ret = 0; + + if (!_dlist_is_pending(&work->event_node) && + !_slist_is_pending(&work->state_node)) + { + return -1; + } + + ret = work->timeout - _K_PORT->get_sys_ticks(); + if (ret < 0) + { + ret = 0; + } + return ret; +} + +/** + * @brief 把任务插入到 work_q->event_list 中 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @param work_handle 由 k_work_create() 初始化 + * @param delay_ticks 指定的唤醒时间,为0时立即就绪,-1为永不就绪 + */ +void k_work_submit(k_work_q_t *work_q_handle, k_work_t *work_handle, k_tick_t delay_ticks) +{ + if (delay_ticks == -1) + { + _k_work_do_submit(work_q_handle, work_handle, 0, _TRUE); + } + else + { + if ((unsigned)delay_ticks > _SIGNED_MAX(delay_ticks)) + { + delay_ticks = _SIGNED_MAX(delay_ticks); + } + _k_work_do_submit(work_q_handle, work_handle, _K_PORT->get_sys_ticks() + delay_ticks, _FALSE); + } +} + +/** + * @brief 唤醒任务。注意需要先使用 k_work_submit 绑定一个 work_q_handle + * + * @param work_handle 由 k_work_create() 初始化 + * @param delay_ticks 让出CPU的时间 + */ +void k_work_resume(k_work_t *work_handle, k_tick_t delay_ticks) +{ + k_work_handle_t *work = work_handle->hdl; + if (work->work_q_handle == NULL) + { + _DBG_WRN("you must using k_work_submit() to define one work_queue first"); + } + else + { + if ((unsigned)delay_ticks > _SIGNED_MAX(delay_ticks)) + { + delay_ticks = _SIGNED_MAX(delay_ticks); + } + k_work_submit(work->work_q_handle, work_handle, delay_ticks); + } +} + +/** + * @brief 延时多少个系统节拍后再执行本任务 + * + * @param delay_ticks 指定的唤醒时间,为0时立即就绪 + */ +void k_work_later(k_tick_t delay_ticks) +{ + k_work_q_t *work_q_handle; + k_work_t *work_handle; + _k_read_curr_handle(&work_q_handle, &work_handle); + if ((unsigned)delay_ticks > _SIGNED_MAX(delay_ticks)) + { + delay_ticks = _SIGNED_MAX(delay_ticks); + } + k_work_submit(work_q_handle, work_handle, delay_ticks); +} + +/** + * @brief 挂起任务,任务不会被删除 + * + * @param work_handle 由 k_work_create() 初始化 + */ +void k_work_suspend(k_work_t *work_handle) +{ + k_work_handle_t *work; + + _K_DIS_INT(); + work = work_handle->hdl; + if (work != NULL) + { + if (_dlist_is_pending(&work->event_node)) + { + k_work_q_handle_t *work_q = work->work_q_handle->hdl; + _dlist_remove(&work_q->event_list, &work->event_node); + } + } + _K_EN_INT(); + + _K_DIS_SCHED(); + work = work_handle->hdl; + if (work != NULL) + { + _slist_remove(work->state_list, &work->state_node); + work->state_list = NULL; + work->exec_flag = 0; + work->obj_flag_periodic = 0; + } + _K_EN_SCHED(); +} + +/** + * @brief 从最后一次唤醒的时间算起,延时多少个系统节拍后再执行本任务(固定周期的延时) + * + * @param delay_ticks 指定的唤醒时间,为0时立即就绪 + */ +void k_work_later_until(k_tick_t delay_ticks) +{ + k_work_q_t *work_q_handle; + k_work_t *work_handle; + k_work_handle_t *work; + k_tick_t timeout; + + _k_read_curr_handle(&work_q_handle, &work_handle); + work = work_handle->hdl; + + if ((unsigned)delay_ticks > _SIGNED_MAX(delay_ticks)) + { + delay_ticks = _SIGNED_MAX(delay_ticks); + } + timeout = work->timeout + delay_ticks + (_K_PORT->get_sys_ticks() - work->timeout) / delay_ticks * delay_ticks; + + _k_work_do_submit(work_q_handle, work_handle, timeout, _FALSE); +} + +/** + * @brief 释放一次CPU的使用权,不调度低于当前任务优先级的任务 + * 返回机制: + * 给予的队列中有高于当前任务优先级的任务:直到所有更高优先给的任务被执行完后; + * 给予的队列中首个任务的优先级等于当前任务的优先级:执行完首个任务后返回。 + * 注意:不允许低优先级的任务堵塞高优先级的任务 + * + * @param delay_ticks 让出CPU的时间 + */ +void k_work_yield(k_tick_t delay_ticks) +{ + k_work_q_t *work_q_handle; + k_work_t *work_handle; + k_work_handle_t *yield_work; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + _k_read_curr_handle(&work_q_handle, &work_handle); + yield_work = _WORK_Q->yield_work; + _WORK_Q->yield_work = _WORK; + + k_work_sleep(delay_ticks); + + _WORK_Q->yield_work = yield_work; +} + +/** + * @brief 释放一次CPU的使用权,可调度低于当前任务优先级的任务 + * 返回机制: + * 给予的队列中有高于当前任务优先级的任务:直到所有更高优先给的任务被执行完后; + * 给予的队列中首个任务的优先级等于当前任务的优先级:执行完首个任务后返回。 + * 栈用量 24 bytes + * + * @param delay_ticks 让出CPU的时间 + */ +void k_work_sleep(k_tick_t delay_ticks) +{ + k_work_q_t *work_q_handle; + k_work_t *work_handle; + k_tick_t sys_time; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + if ((unsigned)delay_ticks > _SIGNED_MAX(delay_ticks)) + { + delay_ticks = _SIGNED_MAX(delay_ticks); + } + + _k_read_curr_handle(&work_q_handle, &work_handle); + sys_time = _K_PORT->get_sys_ticks(); + _WORK->timeout = sys_time + delay_ticks; + + _k_work_q_handler_take_event_list(&_WORK_Q->event_list, sys_time); + + do + { + if (default_timer_q_hdl->hdl != NULL && /* 已创建软定时器队列对象 */ + s_kit_init_struct.scheduler_disable == _k_nop && /* 非多线程 */ + k_timer_q_handler(default_timer_q_hdl) == 0) /* 有就绪的定时任务 */ + { + sys_time = _K_PORT->get_sys_ticks(); + continue; + } + + if (_WORK_Q->priority_tab != 0) + { + k_work_handle_t *exec_work = _k_work_q_handler_take_ready_work(_WORK->work_q_handle); + if (exec_work != NULL) + { + exec_work->exec_flag = 1; + + if (exec_work->obj_flag_type) + { + exec_work->work_route(exec_work->arg); + _k_timer_end(work_q_handle, exec_work); + } + else + { + if (s_cm_kit.curr_hook != exec_work->work_handle) + { + k_work_fn hook_entry; + + s_cm_kit.curr_hook = exec_work->work_handle; + _k_work_log(exec_work->work_handle); + + hook_entry = _WORK_Q->hook_entry; + if (hook_entry) + { + hook_entry(exec_work->work_handle); + } + } + + exec_work->work_route(exec_work->arg); + } + _WORK_Q->curr_work = work_handle; + + if (exec_work->delete_flag == 0) + { + _ASSERT_FALSE(!k_work_is_valid(exec_work->work_handle), "exec_work->work_handle maybe be deleted"); + + exec_work->exec_flag = 0; + if (exec_work->obj_flag_type == 0 && exec_work->obj_data.mbox_list != NULL) + { + if (_slist_peek_head(&exec_work->obj_data.mbox_list->mbox_idle) != NULL) + { + _K_DIS_SCHED(); + _k_work_mbox_clr_list(&exec_work->obj_data.mbox_list->mbox_idle); + _K_EN_SCHED(); + } + } + } + else + { + k_work_delete(exec_work->work_handle); + } + } + } + + if (work_handle->hdl == NULL) + { + break; + } + + sys_time = _K_PORT->get_sys_ticks(); + if (_k_work_q_handler_take_delay_list(_WORK_Q, sys_time) > 0 && + _K_PORT->thread_sleep && + *(volatile int *)&_WORK->timeout - sys_time > 0) + { + _K_PORT->thread_sleep(1); + sys_time = _K_PORT->get_sys_ticks(); + _k_work_q_handler_take_delay_list(_WORK_Q, sys_time); + } + + } while (*(volatile int *)&_WORK->timeout - sys_time > 0); + + if (_WORK->exec_repeat != 0) + { + _WORK->exec_repeat = 0; + k_work_submit(_WORK->work_q_handle, _WORK->work_handle, 0); + } + + if (_WORK_Q->curr_work) + { + if (s_cm_kit.curr_hook != _WORK_Q->curr_work) + { + k_work_fn hook_entry; + + s_cm_kit.curr_hook = _WORK_Q->curr_work; + _k_work_log(_WORK_Q->curr_work); + + hook_entry = _WORK_Q->hook_entry; + if (hook_entry) + { + hook_entry(_WORK_Q->curr_work); + } + } + } +} + +/** + * @brief 查询最近一次执行的任务 + * + * @param work_q_handle 由 k_work_q_create() 初始化 + * @return k_work_t* 指向任务对象 + */ +k_work_t *k_get_curr_work_handle(k_work_q_t *work_q_handle) +{ + k_work_q_handle_t *work_q = work_q_handle->hdl; + return work_q->curr_work; +} + +/** + * @brief 创建任务的邮箱 + * 对象内存: 20 bytes + * + * k_mbox 与 k_fifo 的主要区别: + * 1. 邮箱是任务的扩展,与任务关联; + * 2. 邮件被发送的同时,对应的任务将被自动唤醒; + * 3. 邮件收取后,任务结束时被自动删除,也可使用 k_work_mbox_cancel() 来显式删除 + * + * @param work_handle 由 k_work_create() 初始化 + * @return k_err_t 0 -- 成功,-1 -- 任务对象无效或内存不足 + */ +k_err_t k_work_mbox_create(k_work_t *work_handle) +{ + k_work_handle_t *work; + k_work_mb_list_t *mbox_list; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + work = work_handle->hdl; + if (work == NULL || work->obj_flag_type != 0) + { + _DBG_WRN("work_handle invalid"); + return -1; + } + + if (work->obj_data.mbox_list != NULL) + { + _DBG_WRN("work mbox was be created"); + return -1; + } + + if (work->work_q_handle == NULL) + { + _DBG_WRN("work_handle = %p was not submit to any work_q_handle handle", work); + } + + _K_DIS_SCHED(); + + mbox_list = _K_PORT->malloc(sizeof(*mbox_list)); + if (mbox_list != NULL) + { + _slist_init_list(&mbox_list->mbox_idle); + _slist_init_list(&mbox_list->mbox_valid); + work->obj_data.mbox_list = mbox_list; + } + + _K_EN_SCHED(); + + if (mbox_list == NULL) + { + _DBG_WRN("Insufficient memory"); + return -1; + } + else + { + return 0; + } +} + +/** + * @brief 删除任务的邮箱 + * + * @param work_handle 由 k_work_create() 初始化 + */ +void k_work_mbox_delete(k_work_t *work_handle) +{ + k_work_handle_t *work; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + work = work_handle->hdl; + if (work == NULL) + { + _DBG_WRN("work_handle invalid"); + return; + } + + if (work->obj_flag_type != 0) + { + _DBG_WRN("work_handle invalid"); + return; + } + + if (work->obj_data.mbox_list == NULL) + { + _DBG_WRN("work mbox never be created"); + return; + } + + _K_DIS_SCHED(); + + if (work->obj_data.mbox_list != NULL) + { + k_work_mb_list_t *mbox_list = work->obj_data.mbox_list; + _k_work_mbox_clr_list(&mbox_list->mbox_idle); + _k_work_mbox_clr_list(&mbox_list->mbox_valid); + _K_PORT->free(mbox_list); + work->obj_data.mbox_list = NULL; + } + + _K_EN_SCHED(); +} + +/** + * @brief 申请一个邮件 + * 邮件数据没有限制 + * 不能申请给自己的邮件 + * 管理块内存 8 bytes + * + * @param work_handle 由 k_work_create() 初始化 + * @param size 数据大小 + * @return void* 内存地址 + */ +void *k_work_mbox_alloc(k_work_t *work_handle, size_t size) +{ + k_work_handle_t *work; + k_work_mb_handle_t *mbox_data; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + work = work_handle->hdl; + if (work == NULL) + { + _DBG_WRN("work_handle invalid"); + return NULL; + } + + if (work->work_q_handle == NULL) + { + _DBG_WRN("work was not pending or to be canceled"); + return NULL; + } + + if (work->obj_flag_type != 0) + { + _DBG_WRN("work_handle invalid"); + return NULL; + } + + if (work->obj_data.mbox_list == NULL) + { + _DBG_WRN("never create mbox with function 'k_work_mbox_create()'"); + return NULL; + } + + _K_DIS_SCHED(); + mbox_data = _K_PORT->malloc(sizeof(*mbox_data) + size); + if (mbox_data != NULL) + { + mbox_data->ctrl.work_handle = work_handle; + } + _K_EN_SCHED(); + + if (mbox_data == NULL) + { + _DBG_WRN("Insufficient memory"); + return NULL; + } + else + { + return (void *)&mbox_data[1]; + } +} + +/** + * @brief 取消已申请的邮件 + * + * @param mbox 通过 k_work_mbox_alloc() 申请的数据 + * @return k_err_t 0 -- 成功,-1 -- 工作队列对象或任务对象无效 + */ +k_err_t k_work_mbox_cancel(void *mbox) +{ + k_work_mb_handle_t *mbox_data = &((k_work_mb_handle_t *)mbox)[-1]; + k_err_t ret = 0; + + k_work_handle_t *work = mbox_data->ctrl.work_handle->hdl; + if (work == NULL) + { + _DBG_WRN("work_handle invalid"); + ret = -1; + } + + if (work->work_q_handle == NULL) + { + _DBG_WRN("work was not pending or to be canceled"); + ret = -1; + } + + if (work->obj_data.mbox_list == NULL) + { + _DBG_WRN("never create mbox with function 'k_work_mbox_create()'"); + ret = -1; + } + + if (ret == 0) + { + _K_DIS_SCHED(); + _K_PORT->free(mbox_data); + _K_EN_SCHED(); + } + + return ret; +} + +/** + * @brief 发送邮件 + * 如果任务在延时的状态则立即被切换到就绪状态 + * + * @param mbox 通过 k_work_mbox_alloc() 申请的数据 + * @return k_err_t 0 -- 成功,-1 -- 工作队列对象或任务对象无效 + */ +k_err_t k_work_mbox_submit(void *mbox) +{ + k_work_mb_handle_t *mbox_data = &((k_work_mb_handle_t *)mbox)[-1]; + k_work_handle_t *work = mbox_data->ctrl.work_handle->hdl; + k_err_t ret = 0; + + if (work == NULL) + { + _DBG_WRN("work_handle invalid"); + ret = -1; + } + else if (work->work_q_handle == NULL) + { + _DBG_WRN("work was not pending or to be canceled"); + ret = -1; + } + else if (work->obj_flag_type != 0) + { + _DBG_WRN("work_handle invalid"); + ret = -1; + } + else if (work->obj_data.mbox_list == NULL) + { + _DBG_WRN("never create mbox with function 'k_work_mbox_create()'"); + ret = -1; + } + + if (ret != 0) + { + _K_DIS_SCHED(); + _K_PORT->free(mbox_data); + _K_EN_SCHED(); + return ret; + } + + _slist_init_node(&mbox_data->ctrl.state_node); + _K_DIS_SCHED(); + _slist_insert_tail(&work->obj_data.mbox_list->mbox_valid, &mbox_data->ctrl.state_node); + _K_EN_SCHED(); + + k_work_submit(work->work_q_handle, work->work_handle, 0); + + return ret; +} + +/** + * @brief 提取邮件 + * 当任务返回后自动被清除 + * + * @return void* 数据地址,NULL -- 邮箱队列为空 + */ +void *k_work_mbox_take(void) +{ + k_work_q_t *work_q_handle; + k_work_t *work_handle; + k_work_handle_t *work; + __slist_node_t *node; + + _k_read_curr_handle(&work_q_handle, &work_handle); + work = work_handle->hdl; + + if (work == NULL) + { + _DBG_WRN("work_handle invalid"); + return NULL; + } + + if (work->obj_flag_type != 0) + { + _DBG_WRN("work_handle invalid"); + return NULL; + } + + if (work->obj_data.mbox_list == NULL) + { + _DBG_WRN("never create mbox with function 'k_work_mbox_create()'"); + return NULL; + } + + _K_DIS_SCHED(); + + node = _slist_take_head(&work->obj_data.mbox_list->mbox_valid); + if (node == NULL) + { + _K_EN_SCHED(); + return NULL; + } + else + { + k_work_mb_handle_t *mbox_data = _K_CONTAINER_OF(node, k_work_mb_handle_t, ctrl.state_node); + _slist_insert_tail(&work->obj_data.mbox_list->mbox_idle, &mbox_data->ctrl.state_node); + _K_EN_SCHED(); + return &mbox_data[1]; + } +} + +/** + * @brief 查询邮件 + * + * @return void* 数据地址,NULL -- 邮箱队列为空 + */ +void *k_work_mbox_peek(void) +{ + k_work_q_t *work_q_handle; + k_work_t *work_handle; + k_work_handle_t *work; + __slist_node_t *node; + + _k_read_curr_handle(&work_q_handle, &work_handle); + work = work_handle->hdl; + + if (work == NULL) + { + _DBG_WRN("work_handle invalid"); + return NULL; + } + + if (work->obj_flag_type != 0) + { + _DBG_WRN("work_handle invalid"); + return NULL; + } + + if (work->obj_data.mbox_list == NULL) + { + _DBG_WRN("never create mbox with function 'k_work_mbox_create()'"); + return NULL; + } + + node = _slist_peek_head(&work->obj_data.mbox_list->mbox_valid); + if (node == NULL) + { + return NULL; + } + else + { + k_work_mb_handle_t *mbox_data = _K_CONTAINER_OF(node, k_work_mb_handle_t, ctrl.state_node); + return (void *)&mbox_data[1]; + } +} + +/** + * @brief 清空任务的所有邮件 + */ +void k_work_mbox_clr(void) +{ + k_work_q_t *work_q_handle; + k_work_t *work_handle; + k_work_handle_t *work; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + _k_read_curr_handle(&work_q_handle, &work_handle); + work = work_handle->hdl; + + if (work == NULL) + { + _DBG_WRN("work_handle invalid"); + return; + } + + if (work->obj_flag_type != 0) + { + _DBG_WRN("work_handle invalid"); + return; + } + + if (work->obj_data.mbox_list == NULL) + { + _DBG_WRN("work mbox never be created"); + return; + } + + _K_DIS_SCHED(); + + if (work->obj_data.mbox_list != NULL) + { + _k_work_mbox_clr_list(&work->obj_data.mbox_list->mbox_idle); + _k_work_mbox_clr_list(&work->obj_data.mbox_list->mbox_valid); + } + + _K_EN_SCHED(); +} + +/** + * @brief 软定时器队列执行入口 + * 所有在队列中就绪的定时器都将被按顺序逐个执行。 + * 执行顺序为: + * 1. 按定时器的优先级 + * 2. 按就绪的顺序 + * 栈用量 32 bytes + * + * @param timer_q_handle 由 k_timer_q_create() 初始化 + * @return k_tick_t 距离下个延时定时器的时间 + */ +k_tick_t k_timer_q_handler(k_timer_q_t *timer_q_handle) +{ + return k_work_q_handler((k_work_q_t *)timer_q_handle); +} + +/** + * @brief 创建一个软定时器队列的对象 + * 对象数据由堆自动分配 + * 对象内存: 104 bytes + * + * @param timer_q_handle[out] 初始化队列对象指针 + * @return k_err_t 0 -- 成功, -1 -- 堆内存不足 + */ +k_err_t k_timer_q_create(k_timer_q_t *timer_q_handle) +{ + return k_work_q_create((k_work_q_t *)timer_q_handle); +} + +/** + * @brief 删除一个软定时器队列的对象 + * 1. 在该队列中所有已就绪的定时器被立即删除 + * 2. 延时中的定时器在到达时间后被立即删除 + * 3. 软定时器队列被删除 + * 4. 软定时器队列及相关定时器的堆内存被自动回收 + * + * @param timer_q_handle 由 k_timer_q_create() 初始化 + */ +void k_timer_q_delete(k_timer_q_t *timer_q_handle) +{ + k_work_q_delete((k_work_q_t *)timer_q_handle); +} + +/** + * @brief 注册一个函数:当有任务被提交时,此回调函数将被自动执行 + * + * @param timer_q_handle 由 k_timer_q_create() 初始化 + * @param timer_q_resume 当有任务被提交时,被执行的回调函数将 + * @param arg 回调函数将附带的参数 + */ +void k_timer_q_resume_regist(k_timer_q_t *timer_q_handle, k_timer_q_fn timer_q_resume, void *arg) +{ + k_work_q_resume_regist((k_work_q_t *)timer_q_handle, (k_work_q_fn)timer_q_resume, arg); +} + +/** + * @brief 获取软定时器队列是否有效 + * + * @param timer_q_handle 由 k_timer_q_create() 初始化 + * @retval true 对象有效 + * @retval false 对象无效 + */ +bool k_timer_q_is_valid(k_timer_q_t *timer_q_handle) +{ + return k_work_q_is_valid((k_work_q_t *)timer_q_handle); +} + +/** + * @brief 获取是否有正在延时状态的定时器 + * + * @retval true 有延时状态的定时器 + * @retval false 队列已空 + */ +bool k_timer_q_delayed_state(k_timer_q_t *timer_q_handle) +{ + return k_work_q_delayed_state((k_work_q_t *)timer_q_handle); +} + +/** + * @brief 获取软定时器队列中是否有就绪的定时器 + * + * @param timer_q_handle 由 k_timer_q_create() 初始化 + * @retval true 有就绪的定时器 + * @retval false 队列已空 + */ +bool k_timer_q_ready_state(k_timer_q_t *timer_q_handle) +{ + return k_work_q_ready_state((k_work_q_t *)timer_q_handle); +} + +/** + * @brief 创建由软定时器队列管理的定时器 + * 定时器对象数据由堆内存自动分配 + * 定时器必须返回 + * 定时器堆栈来自 k_timer_q_handler() + * 对象内存: 48 bytes + * + * @param timer_handle[out] 初始化定时器对象数据 + * @param timer_q_handle 由 k_timer_q_create() 初始化 + * @param timer_route 定时器的入口地址 + * @param arg 附带的一个参数指针 + * @param priority 定时器优先级,0 .. 7,0 为最低 + * @return k_err_t 0 -- 成功,-1 -- 失败:对象非空或堆内存不足 + */ +k_err_t k_timer_create(k_timer_t *timer_handle, + k_timer_q_t *timer_q_handle, + k_timer_fn timer_route, + void *arg, + uint8_t priority) +{ + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + if (timer_q_handle == NULL || timer_q_handle->hdl == NULL) + { + _DBG_WRN("timer_q_handle invalid"); + return -1; + } + + if (k_work_create((k_work_t *)timer_handle, "", (k_work_fn)timer_route, arg, priority) == 0) + { + k_work_handle_t *work = timer_handle->hdl; + work->work_q_handle = (k_work_q_t *)timer_q_handle; + work->obj_flag_type = 1; + work->obj_data.period = _SIGNED_MAX(work->obj_data.period); + return 0; + } + else + { + return -1; + } +} + +/** + * @brief 删除由软定时器队列管理的定时器 + * 定时器立即失效 + * 定时器对象数据被立即回收 + * + * @param timer_handle 由 k_timer_create() 初始化 + */ +void k_timer_delete(k_timer_t *timer_handle) +{ + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + if (timer_handle == NULL) + { + k_work_q_t *work_q_handle; + _k_read_curr_handle(&work_q_handle, (k_work_t **)&timer_handle); + } + + _K_DIS_SCHED(); + k_work_handle_t *work = timer_handle->hdl; + if (work->exec_flag == 0) + { + _k_work_delete((k_work_t *)timer_handle); + } + else + { + work->delete_flag = 1; + } + _K_EN_SCHED(); +} + +/** + * @brief 设置定时器的自动重装值 + * + * @param timer_handle 由 k_timer_create() 初始化 + * @param periodic 是否自动重装 + * @param period 重装值 + */ +void k_timer_set_period(k_timer_t *timer_handle, bool periodic, k_tick_t period) +{ + k_work_handle_t *work = timer_handle->hdl; + if (work) + { + if ((unsigned)period > _SIGNED_MAX(period)) + { + period = _SIGNED_MAX(period); + } + _K_DIS_SCHED(); + work->obj_flag_periodic = periodic; + work->obj_data.period = period; + _K_EN_SCHED(); + } + else + { + _DBG_WRN("you must using k_timer_create() to create one timer_handle first"); + } +} + +/** + * @brief 启动定时器 + * + * @param timer_handle 由 k_timer_create() 初始化 + * @param periodic 是否自动重装 + * @param period 重装值 + * @param delay_ticks 延时启动 + */ +void k_timer_start(k_timer_t *timer_handle, k_tick_t delay_ticks) +{ + k_work_handle_t *work = timer_handle->hdl; + if (work) + { + _k_work_do_submit(work->work_q_handle, (k_work_t *)timer_handle, _K_PORT->get_sys_ticks() + delay_ticks, _FALSE); + } +} + +/** + * @brief 挂起定时器,定时器不会被删除 + * + * @param timer_handle 由 k_timer_create() 初始化 + */ +void k_timer_stop(k_timer_t *timer_handle) +{ + k_work_suspend((k_work_t *)timer_handle); +} + +/** + * @brief 生成一个临时定时器,执行完自动删除 + * + * @param timer_q_handle 由 k_timer_q_create() 初始化 + * @param timer_route + * @param arg + * @param delay_ticks + * @return k_err_t + */ +k_err_t k_timer_newsubmit(k_timer_q_t *timer_q_handle, k_timer_fn timer_route, void *arg, k_tick_t delay_ticks) +{ + k_timer_t timer_handle; + + memset(&timer_handle, 0, sizeof(timer_handle)); + + if (k_timer_create( + &timer_handle, + timer_q_handle, + timer_route, + arg, + 0) == 0) + { + k_work_handle_t *work = timer_handle.hdl; + work->obj_flag_auto_del = 1; + k_timer_set_period(&timer_handle, _FALSE, delay_ticks); + k_timer_start(&timer_handle, delay_ticks); + return 0; + } + else + { + return -1; + } +} + +/** + * @brief 删除并重新生成一个新的临时定时器,执行完自动删除 + * + * @param timer_q_handle 由 k_timer_q_create() 初始化 + * @param timer_route + * @param arg + * @param delay_ticks + * @return k_err_t + */ +k_err_t k_timer_resubmit(k_timer_q_t *timer_q_handle, k_timer_fn timer_route, void *arg, k_tick_t delay_ticks) +{ + k_timer_cancel(timer_q_handle, timer_route); + return k_timer_newsubmit(timer_q_handle, timer_route, arg, delay_ticks); +} + +/** + * @brief 立即删除临时定时器 + * + * @param timer_q_handle 由 k_timer_q_create() 初始化 + * @param timer_route + */ +void k_timer_cancel(k_timer_q_t *timer_q_handle, k_timer_fn timer_route) +{ + k_work_q_handle_t *work_q; + + _K_DIS_SCHED(); + + work_q = timer_q_handle->hdl; + if (work_q != NULL) + { + do + { + __dlist_t *test_list = &work_q->event_list; + __dlist_node_t *test_node = _dlist_peek_head(test_list); + __dlist_node_t *end_node = _dlist_peek_tail(test_list); + __dlist_node_t *prev_node = NULL; + + do + { + k_work_handle_t *test_work; + + if (test_node == NULL) + { + break; + } + test_work = _K_CONTAINER_OF(test_node, k_work_handle_t, event_node); + + if ((void *)test_work->work_route == (void *)timer_route) + { + _dlist_remove(test_list, test_node); + if (prev_node == NULL) + { + test_node = _dlist_peek_head(test_list); + } + else + { + test_node = _dlist_peek_next(test_list, prev_node); + } + _K_PORT->free(test_work); + } + else + { + prev_node = test_node; + test_node = _dlist_peek_next(test_list, prev_node); + } + + } while (prev_node != end_node); + + } while (0); + + do + { + __slist_t *test_list = &work_q->delayed_list; + __slist_node_t *test_node = _slist_peek_head(test_list); + __slist_node_t *end_node = _slist_peek_tail(test_list); + __slist_node_t *prev_node = NULL; + + do + { + k_work_handle_t *test_work; + + if (test_node == NULL) + { + break; + } + test_work = _K_CONTAINER_OF(test_node, k_work_handle_t, state_node); + + if ((void *)test_work->work_route == (void *)timer_route) + { + _slist_remove_next(test_list, prev_node, test_node); + if (prev_node == NULL) + { + test_node = _slist_peek_head(test_list); + } + else + { + test_node = _slist_peek_next(prev_node); + } + _K_PORT->free(test_work); + } + else + { + prev_node = test_node; + test_node = _slist_peek_next(prev_node); + } + + } while (prev_node != end_node); + + } while (0); + + do + { + unsigned i; + for (i = 0; i < sizeof(work_q->ready_list) / sizeof(work_q->ready_list[0]); i++) + { + __slist_t *test_list = &work_q->ready_list[i]; + __slist_node_t *test_node = _slist_peek_head(test_list); + __slist_node_t *end_node = _slist_peek_tail(test_list); + __slist_node_t *prev_node = NULL; + + do + { + k_work_handle_t *test_work; + + if (test_node == NULL) + { + break; + } + test_work = _K_CONTAINER_OF(test_node, k_work_handle_t, state_node); + + if ((void *)test_work->work_route == (void *)timer_route) + { + _slist_remove_next(test_list, prev_node, test_node); + if (prev_node == NULL) + { + test_node = _slist_peek_head(test_list); + } + else + { + test_node = _slist_peek_next(prev_node); + } + _K_PORT->free(test_work); + } + else + { + prev_node = test_node; + test_node = _slist_peek_next(prev_node); + } + + } while (prev_node != end_node); + } + + } while (0); + } + + _K_EN_SCHED(); +} + +/** + * @brief 获取定时器对象是否有效 + * + * @param timer_handle 由 k_timer_create() 初始化 + * @retval true 定时器对象有效 + * @retval false 定时器对象无效 + */ +bool k_timer_is_valid(k_timer_t *timer_handle) +{ + return k_work_is_valid((k_work_t *)timer_handle); +} + +/** + * @brief 获取定时器是否在就绪或延时的状态 + * + * @param timer_handle 由 k_timer_create() 初始化 + * @retval true 定时器就绪或延时 + * @retval false 定时器被挂起 + */ +bool k_timer_is_pending(k_timer_t *timer_handle) +{ + return k_work_is_pending((k_work_t *)timer_handle); +} + +/** + * @brief 查询当前是否自动重装 + * + * @param timer_handle 由 k_timer_create() 初始化 + * @retval true 当前状态为自动重装 + * @retval false 当前状态为单次模式 + */ +bool k_timer_is_periodic(k_timer_t *timer_handle) +{ + k_work_handle_t *work = timer_handle->hdl; + return (bool)!!work->obj_flag_periodic; +} + +/** + * @brief 查询当前的自动重装值 + * + * @param timer_handle + * @return k_tick_t + */ +k_tick_t k_timer_get_period(k_timer_t *timer_handle) +{ + k_work_handle_t *work = timer_handle->hdl; + return work->obj_data.period; +} + +/** + * @brief 获取定时器距离下个执行的剩余时间 + * + * @param timer_handle + * @return k_tick_t + */ +k_tick_t k_timer_time_remain(k_timer_t *timer_handle) +{ + return k_work_time_remain((k_work_t *)timer_handle); +} + +/** + * @brief 创建一个FIFO对象 + * 对象数据由堆自动分配 + * 对象内存: 20 bytes + * + * @param fifo_handle[out] 初始化FIFO对象指针 + * @return k_err_t 0 -- 成功, -1 -- 堆内存不足 + */ +k_err_t k_fifo_q_create(k_fifo_t *fifo_handle) +{ + k_fifo_q_handle_t *fifo_q; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + _K_DIS_SCHED(); + fifo_q = _K_PORT->malloc(sizeof(k_fifo_q_handle_t)); + if (fifo_q != NULL) + { + fifo_handle->hdl = fifo_q; + fifo_q->resume_work = NULL; + fifo_q->resume_delay = 0; + _slist_init_list(&fifo_q->list); + } + _K_EN_SCHED(); + + if (fifo_q == NULL) + { + _DBG_WRN("Insufficient memory"); + return -1; + } + else + { + return 0; + } +} + +/** + * @brief 删除一个FIFO的对象 + * 1. 在该队列中所有数据被立即删除 + * 2. FIFO对象被删除 + * 3. FIFO对象及数据的堆内存被自动回收 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + */ +void k_fifo_q_delete(k_fifo_t *fifo_handle) +{ + k_fifo_q_handle_t *fifo_q; + + _K_DIS_SCHED(); + fifo_q = fifo_handle->hdl; + if (fifo_q != NULL) + { + for (;;) + { + __slist_node_t *node = _slist_take_head(&fifo_q->list); + if (node == NULL) + { + break; + } + else + { + k_fifo_handle_t *fifo_data = _K_CONTAINER_OF(node, k_fifo_handle_t, ctrl.state_node); + _K_PORT->free(fifo_data); + } + } + _K_PORT->free(fifo_q); + fifo_handle->hdl = NULL; + } + _K_EN_SCHED(); +} + +/** + * @brief 清除FIFO内的所有数据 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + */ +void k_fifo_q_clr(k_fifo_t *fifo_handle) +{ + k_fifo_q_handle_t *fifo_q; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + _K_DIS_SCHED(); + fifo_q = fifo_handle->hdl; + if (fifo_q != NULL) + { + for (;;) + { + __slist_node_t *node = _slist_take_head(&fifo_q->list); + if (node == NULL) + { + break; + } + else + { + k_fifo_handle_t *fifo_data = _K_CONTAINER_OF(node, k_fifo_handle_t, ctrl.state_node); + _K_PORT->free(fifo_data); + } + } + } + _K_EN_SCHED(); +} + +/** + * @brief 获取 FIFO 对象是否有效 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + * @retval true + * @retval false + */ +bool k_fifo_q_is_valid(k_fifo_t *fifo_handle) +{ + if (s_kit_init_struct.malloc == NULL || fifo_handle == NULL || fifo_handle->hdl == NULL) + { + return _FALSE; + } + else + { + return _TRUE; + } +} + +/** + * @brief 注册任务,当队列非空时,任务被唤醒。 + * 注意同时只能注册一个任务 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + * @param work_handle 由 k_work_create() 初始化 + * @param delay_ticks 延迟唤醒时间 + */ +void k_fifo_q_regist(k_fifo_t *fifo_handle, k_work_t *work_handle, k_tick_t delay_ticks) +{ + k_fifo_q_handle_t *fifo_q = fifo_handle->hdl; + if (fifo_q != NULL) + { + fifo_q->resume_work = work_handle; + fifo_q->resume_delay = delay_ticks; + } +} + +/** + * @brief 取消注册任务 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + */ +void k_fifo_q_unregist(k_fifo_t *fifo_handle) +{ + k_fifo_q_handle_t *fifo_q = fifo_handle->hdl; + fifo_q->resume_work = NULL; +} + +/** + * @brief 申请可用于FIFO的数据结构 + * 管理块内存 8 bytes + * + * @param size 数据结构大小,单位为字节 + * @return void* 数据结构地址,为 NULL 时表示内存不足 + */ +void *k_fifo_alloc(size_t size) +{ + k_fifo_handle_t *fifo_data; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + _K_DIS_SCHED(); + fifo_data = _K_PORT->malloc(sizeof(*fifo_data) + size); + _K_EN_SCHED(); + + if (fifo_data == NULL) + { + _DBG_WRN("Insufficient memory"); + return NULL; + } + else + { + fifo_data->ctrl.free_flag = _K_FIFO_FREE; + return (void *)&fifo_data[1]; + } +} + +/** + * @brief 释放由 k_fifo_alloc() 申请的数据结构 + * 注意数据不允许在FIFO中时被释放 + * + * @param data 数据结构地址 + * @return k_err_t 0 -- 成功,-1 -- 数据未从FIFO中提取或数据无效 + */ +k_err_t k_fifo_free(void *data) +{ + k_fifo_handle_t *fifo_data; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + fifo_data = &((k_fifo_handle_t *)data)[-1]; + + _K_DIS_SCHED(); + if (fifo_data->ctrl.free_flag == _K_FIFO_FREE) + { + fifo_data->ctrl.free_flag = 0; + _K_PORT->free(fifo_data); + fifo_data = NULL; + } + _K_EN_SCHED(); + + if (fifo_data != NULL) + { + _DBG_WRN("%p was not in fifo queue, can not to free", data); + return -1; + } + else + { + return 0; + } +} + +/** + * @brief 把数据结构压入到FIFO中 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + * @param data 数据结构 + * @return k_err_t 0 -- 成功,-1 -- FIFO对象无效或数据结不是由 k_fifo_alloc() 所创建 + */ +k_err_t k_fifo_put(k_fifo_t *fifo_handle, void *data) +{ + k_fifo_q_handle_t *fifo_q = fifo_handle->hdl; + k_fifo_handle_t *fifo_data = &((k_fifo_handle_t *)data)[-1]; + bool resume_flag = false; + + if (fifo_q == NULL) + { + _DBG_WRN("fifo_handle invalid"); + return -1; + } + + if (data == NULL || fifo_data->ctrl.free_flag != _K_FIFO_FREE) + { + _DBG_WRN("fifo_data invalid"); + return -1; + } + + _slist_init_node(&fifo_data->ctrl.state_node); + _K_DIS_INT(); + if (_slist_peek_head(&fifo_q->list) == NULL) + { + resume_flag = true; + } + _slist_insert_tail(&fifo_q->list, &fifo_data->ctrl.state_node); + _K_EN_INT(); + + if (resume_flag) + { + _k_ipc_resume(fifo_q->resume_work, fifo_q->resume_delay, 0); + } + + return 0; +} + +/** + * @brief 从FIFO中弹出最先压入的数据 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + * @return void* 数据地址,NULL -- FIFO队列为空 + */ +void *k_fifo_take(k_fifo_t *fifo_handle) +{ + k_fifo_q_handle_t *fifo_q = fifo_handle->hdl; + __slist_node_t *node; + + if (fifo_q == NULL) + { + _DBG_WRN("fifo_handle invalid"); + return NULL; + } + + _K_DIS_INT(); + node = _slist_take_head(&fifo_q->list); + _K_EN_INT(); + + if (node == NULL) + { + return NULL; + } + else + { + k_fifo_handle_t *fifo_data = _K_CONTAINER_OF(node, k_fifo_handle_t, ctrl.state_node); + fifo_data->ctrl.free_flag = _K_FIFO_FREE; + return (void *)&fifo_data[1]; + } +} + +/** + * @brief 查询FIFO中头部的数据地址 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + * @return void* 数据地址,NULL -- FIFO队列为空 + */ +void *k_fifo_peek_head(k_fifo_t *fifo_handle) +{ + k_fifo_q_handle_t *fifo_q; + __slist_node_t *node; + + if (fifo_handle->hdl == NULL) + { + return NULL; + } + + fifo_q = fifo_handle->hdl; + node = _slist_peek_head(&fifo_q->list); + if (node == NULL) + { + return NULL; + } + else + { + k_fifo_handle_t *fifo_data = _K_CONTAINER_OF(node, k_fifo_handle_t, ctrl.state_node); + return (void *)&fifo_data[1]; + } +} + +/** + * @brief 查询FIFO中尾部的数据地址 + * + * @param fifo_handle 由 k_fifo_q_create() 初始化 + * @return void* 数据地址,NULL -- FIFO队列为空 + */ +void *k_fifo_peek_tail(k_fifo_t *fifo_handle) +{ + k_fifo_q_handle_t *fifo_q; + __slist_node_t *node; + + if (fifo_handle->hdl == NULL) + { + return NULL; + } + + fifo_q = fifo_handle->hdl; + node = _slist_peek_tail(&fifo_q->list); + if (node == NULL) + { + return NULL; + } + else + { + k_fifo_handle_t *fifo_data = _K_CONTAINER_OF(node, k_fifo_handle_t, ctrl.state_node); + return (void *)&fifo_data[1]; + } +} + +/** + * @brief 创建一个QUEUE对象 + * 对象数据由堆自动分配 + * 对象内存: 32 bytes + * + * @param queue_handle[out] 初始化QUEUE对象指针 + * @param queue_length 指定队列有多少顶 + * @param item_size 指定每项的长度(字节) + * @return k_err_t 0 -- 成功, -1 -- 堆内存不足 + */ +k_err_t k_queue_create(k_queue_t *queue_handle, size_t queue_length, size_t item_size) +{ + size_t real_item_size; + k_queue_handle_t *queue; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + real_item_size = (size_t) & ((k_queue_data_t *)0)[1] + (item_size + sizeof(size_t) - 1) / sizeof(size_t) * sizeof(size_t); + + _K_DIS_SCHED(); + queue = _K_PORT->malloc(sizeof(k_queue_handle_t) + real_item_size * queue_length); + if (queue != NULL) + { + size_t i; + queue_handle->hdl = queue; + queue->item_size = item_size; + queue->resume_work = NULL; + queue->resume_delay = 0; + _slist_init_list(&queue->list_valid); + _slist_init_list(&queue->list_idle); + for (i = 0; i < queue_length; i++) + { + k_queue_data_t *queue_data = (k_queue_data_t *)&((uint8_t *)&queue[1])[real_item_size * i]; + queue_data->queue_handle = queue_handle; + _slist_init_node(&queue_data->ctrl.state_node); + _slist_insert_tail(&queue->list_idle, &queue_data->ctrl.state_node); + } + } + _K_EN_SCHED(); + + if (queue == NULL) + { + _DBG_WRN("Insufficient memory"); + return -1; + } + else + { + return 0; + } +} + +/** + * @brief 删除一个QUEUE的对象 + * 1. 在该队列中所有数据被立即删除 + * 2. QUEUE对象被删除 + * 3. QUEUE对象及数据的堆内存被自动回收 + * + * @param queue_handle 由 k_queue_create() 初始化 + */ +void k_queue_delete(k_queue_t *queue_handle) +{ + k_queue_handle_t *queue; + + _K_DIS_SCHED(); + queue = queue_handle->hdl; + if (queue != NULL) + { + _K_PORT->free(queue); + queue_handle->hdl = NULL; + } + _K_EN_SCHED(); +} + +/** + * @brief 清除QUEUE内的所有数据 + * + * @param queue_handle 由 k_queue_create() 初始化 + */ +void k_queue_clr(k_queue_t *queue_handle) +{ + k_queue_handle_t *queue; + __slist_node_t *node; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + queue = queue_handle->hdl; + if (queue != NULL) + { + for (;;) + { + _K_DIS_INT(); + node = _slist_take_head(&queue->list_valid); + if (node == NULL) + { + _K_EN_INT(); + break; + } + else + { + _slist_insert_font(&queue->list_idle, node); + _K_EN_INT(); + } + } + } +} + +/** + * @brief 获取 QUEUE 对象是否有效 + * + * @param queue_handle 由 k_queue_create() 初始化 + * @retval true + * @retval false + */ +bool k_queue_is_valid(k_queue_t *queue_handle) +{ + if (s_kit_init_struct.malloc == NULL || queue_handle == NULL || queue_handle->hdl == NULL) + { + return _FALSE; + } + else + { + return _TRUE; + } +} + +/** + * @brief 注册任务,当队列非空时,任务被唤醒。 + * 注意同时只能注册一个任务 + * + * @param queue_handle 由 k_queue_create() 初始化 + * @param work_handle 由 k_work_create() 初始化 + * @param delay_ticks 延迟唤醒时间 + */ +void k_queue_regist(k_queue_t *queue_handle, k_work_t *work_handle, k_tick_t delay_ticks) +{ + k_queue_handle_t *queue = queue_handle->hdl; + if (queue != NULL) + { + queue->resume_work = work_handle; + queue->resume_delay = delay_ticks; + } +} + +/** + * @brief 取消注册任务 + * + * @param queue_handle 由 k_queue_create() 初始化 + */ +void k_queue_unregist(k_queue_t *queue_handle) +{ + k_queue_handle_t *queue = queue_handle->hdl; + queue->resume_work = NULL; +} + +/** + * @brief 接收并复制数据 + * 等价于 k_queue_take() + memcpy() + k_queue_free() + * + * @param queue_handle 由 k_queue_create() 初始化 + * @param dst 保存接收到的数据 + * @return k_err_t 0 接收成功,-1 队列中没有数据 + */ +k_err_t k_queue_recv(k_queue_t *queue_handle, void *dst) +{ + k_queue_handle_t *queue; + void *data = k_queue_take(queue_handle); + if (data == NULL) + { + return -1; + } + + queue = queue_handle->hdl; + memcpy(dst, data, queue->item_size); + + k_queue_free(data); + + return 0; +} + +/** + * @brief 复制数据并发送 + * 等价于 k_queue_alloc() + memcpy() + k_queue_put() + * + * @param queue_handle 由 k_queue_create() 初始化 + * @param src 发送的数据 + * @return k_err_t 0 发送成功,-1 队列已满 + */ +k_err_t k_queue_send(k_queue_t *queue_handle, const void *src) +{ + k_queue_handle_t *queue; + void *data = k_queue_alloc(queue_handle); + if (data == NULL) + { + return -1; + } + + queue = queue_handle->hdl; + memcpy(data, src, queue->item_size); + + k_queue_put(data); + + return 0; +} + +/** + * @brief 申请可用于QUEUE的数据结构 + * 管理块内存 4 bytes + * + * @param queue_handle 由 k_queue_create() 初始化 + * @return void* 数据结构地址,为0时表示内存不足 + */ +void *k_queue_alloc(k_queue_t *queue_handle) +{ + k_queue_handle_t *queue = queue_handle->hdl; + __slist_node_t *node; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + if (queue == NULL) + { + _DBG_WRN("queue_handle invalid"); + return NULL; + } + + _K_DIS_INT(); + node = _slist_take_head(&queue->list_idle); + _K_EN_INT(); + + if (node == NULL) + { + return NULL; + } + else + { + k_queue_data_t *queue_data = _K_CONTAINER_OF(node, k_queue_data_t, ctrl.state_node); + queue_data->ctrl.free_flag = _K_QUEUE_FREE; + return (void *)&queue_data[1]; + } +} + +/** + * @brief 释放由 k_queue_alloc() 申请的数据结构 + * 注意数据不允许在QUEUE中时被释放 + * + * @param data 由 k_queue_alloc() 或 k_queue_take() 获取 + * @return k_err_t 0 -- 成功,-1 -- 数据未从QUEUE中弹出或数据无效 + */ +k_err_t k_queue_free(void *data) +{ + k_queue_data_t *queue_data; + k_queue_handle_t *queue; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + if (data == NULL) + { + _DBG_WRN("data invalid"); + return -1; + } + + queue_data = &((k_queue_data_t *)data)[-1]; + queue = (k_queue_handle_t *)queue_data->queue_handle->hdl; + + if (queue == NULL) + { + _DBG_WRN("queue_handle invalid"); + return -1; + } + if (queue_data->ctrl.free_flag != _K_QUEUE_FREE) + { + _DBG_WRN("%p was freed", data); + return -1; + } + + _slist_init_node(&queue_data->ctrl.state_node); + _K_DIS_INT(); + _slist_insert_font(&queue->list_idle, &queue_data->ctrl.state_node); + _K_EN_INT(); + + return 0; +} + +/** + * @brief 把数据结构压入到QUEUE中 + * + * @param data 由 k_queue_alloc() 或 k_queue_take() 获取 + * @return k_err_t 0 -- 成功,-1 -- QUEUE对象无效或数据结不是由 k_queue_alloc() 所创建 + */ +k_err_t k_queue_put(void *data) +{ + k_queue_data_t *queue_data; + k_queue_handle_t *queue; + bool resume_flag = false; + + if (data == NULL) + { + _DBG_WRN("data invalid"); + return -1; + } + + queue_data = &((k_queue_data_t *)data)[-1]; + queue = (k_queue_handle_t *)queue_data->queue_handle->hdl; + + if (queue == NULL) + { + _DBG_WRN("queue_handle invalid"); + return -1; + } + if (queue_data->ctrl.free_flag != _K_QUEUE_FREE) + { + _DBG_WRN("%p was freed", data); + return -1; + } + + _slist_init_node(&queue_data->ctrl.state_node); + _K_DIS_INT(); + if (_slist_peek_head(&queue->list_valid) == NULL) + { + resume_flag = true; + } + _slist_insert_tail(&queue->list_valid, &queue_data->ctrl.state_node); + _K_EN_INT(); + + if (resume_flag) + { + _k_ipc_resume(queue->resume_work, queue->resume_delay, 0); + } + + return 0; +} + +/** + * @brief 从QUEUE中弹出最先压入的数据 + * + * @param queue_handle 由 k_queue_create() 初始化 + * @return void* 数据地址,NULL -- QUEUE队列为空 + */ +void *k_queue_take(k_queue_t *queue_handle) +{ + k_queue_handle_t *queue = queue_handle->hdl; + __slist_node_t *node; + + if (queue == NULL) + { + _DBG_WRN("queue_handle invalid"); + return NULL; + } + + _K_DIS_INT(); + node = _slist_take_head(&queue->list_valid); + _K_EN_INT(); + + if (node == NULL) + { + return NULL; + } + else + { + k_queue_data_t *queue_data = _K_CONTAINER_OF(node, k_queue_data_t, ctrl.state_node); + queue_data->ctrl.free_flag = _K_QUEUE_FREE; + return (void *)&queue_data[1]; + } +} + +/** + * @brief 查询QUEUE中头部的数据地址 + * + * @param queue_handle 由 k_queue_create() 初始化 + * @return void* 数据地址,NULL -- QUEUE队列为空 + */ +void *k_queue_peek_head(k_queue_t *queue_handle) +{ + k_queue_handle_t *queue; + __slist_node_t *node; + + if (queue_handle == NULL || queue_handle->hdl == NULL) + { + return NULL; + } + + queue = queue_handle->hdl; + node = _slist_peek_head(&queue->list_valid); + if (node == NULL) + { + return NULL; + } + else + { + k_queue_data_t *queue_data = _K_CONTAINER_OF(node, k_queue_data_t, ctrl.state_node); + return (void *)&queue_data[1]; + } +} + +/** + * @brief 查询QUEUE中尾部的数据地址 + * + * @param queue_handle 由 k_queue_create() 初始化 + * @return void* 数据地址,NULL -- QUEUE队列为空 + */ +void *k_queue_peek_tail(k_queue_t *queue_handle) +{ + k_queue_handle_t *queue; + __slist_node_t *node; + + if (queue_handle == NULL || queue_handle->hdl == NULL) + { + return NULL; + } + + queue = queue_handle->hdl; + node = _slist_peek_tail(&queue->list_valid); + if (node == NULL) + { + return NULL; + } + else + { + k_queue_data_t *queue_data = _K_CONTAINER_OF(node, k_queue_data_t, ctrl.state_node); + return (void *)&queue_data[1]; + } +} + +/** + * @brief 查询已申请数据的所属队列句柄 + * + * @param data 已申请数据的数据地址 + * @return k_queue_t 所属队列句柄 + */ +k_queue_t *k_queue_read_handle(void *data) +{ + k_queue_data_t *queue_data; + + if (data == NULL) + { + _DBG_WRN("data invalid"); + return NULL; + } + + queue_data = &((k_queue_data_t *)data)[-1]; + return queue_data->queue_handle; +} + +/** + * @brief 读回 k_queue_create() 中设置的 item_size 的值 + * + * @param queue_handle 由 k_queue_create() 初始化 + * @return size_t k_queue_create() 中设置的 item_size 的值 + */ +size_t k_queue_get_item_size(k_queue_t *queue_handle) +{ + k_queue_handle_t *queue; + + if (queue_handle == NULL || queue_handle->hdl == NULL) + { + return 0; + } + + queue = queue_handle->hdl; + return queue->item_size; +} + +/** + * @brief 创建一个管道对象 + * 对象内存: 24 bytes + * + * @param pipe_handle[out] 初始化管道对象 + * @param pipe_size 可保存的最大长度 + * @return k_err_t 0 -- 成功, -1 -- 堆内存不足 + */ +k_err_t k_pipe_create(k_pipe_t *pipe_handle, size_t pipe_size) +{ + k_pipe_handle_t *pipe; + + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + pipe_size++; + + _K_DIS_SCHED(); + pipe = _K_PORT->malloc(sizeof(k_pipe_handle_t) + pipe_size); + if (pipe != NULL) + { + pipe_handle->hdl = pipe; + pipe->size = pipe_size; + pipe->resume_work = NULL; + pipe->resume_delay = 0; + } + _K_EN_SCHED(); + + if (pipe == NULL) + { + _DBG_WRN("Insufficient memory"); + return -1; + } + else + { + k_pipe_clr(pipe_handle); + return 0; + } +} + +/** + * @brief 删除一个管道对象 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + */ +void k_pipe_delete(k_pipe_t *pipe_handle) +{ + _ASSERT_FALSE(s_kit_init_struct.malloc == NULL, "Never use 'k_init()' to initialize"); + + _K_DIS_SCHED(); + if (pipe_handle->hdl != NULL) + { + _K_PORT->free(pipe_handle->hdl); + pipe_handle->hdl = NULL; + } + _K_EN_SCHED(); +} + +/** + * @brief 清空管道的数据 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + */ +void k_pipe_clr(k_pipe_t *pipe_handle) +{ + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + + if (pipe == NULL) + { + _DBG_WRN("fifo_handle invalid"); + return; + } + + _K_DIS_SCHED(); + pipe->wid = 0; + pipe->rid = 0; + _K_EN_SCHED(); +} + +/** + * @brief 获取 PIPE 对象是否有效 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @retval true + * @retval false + */ +bool k_pipe_is_valid(k_pipe_t *pipe_handle) +{ + if (s_kit_init_struct.malloc == NULL || pipe_handle == NULL || pipe_handle->hdl == NULL) + { + return _FALSE; + } + else + { + return _TRUE; + } +} + +/** + * @brief 注册任务,当队列非空时,任务被唤醒。 + * 注意同时只能注册一个任务 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @param work_handle 由 k_work_create() 初始化 + * @param delay_ticks 延迟唤醒时间 + */ +void k_pipe_regist(k_pipe_t *pipe_handle, k_work_t *work_handle, k_tick_t delay_ticks) +{ + k_pipe_handle_t *pipe = pipe_handle->hdl; + if (pipe != NULL) + { + pipe->resume_work = work_handle; + pipe->resume_delay = delay_ticks; + } +} + +/** + * @brief 取消注册任务 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + */ +void k_pipe_unregist(k_pipe_t *pipe_handle) +{ + k_pipe_handle_t *pipe = pipe_handle->hdl; + pipe->resume_work = NULL; +} + +/** + * @brief 写一个字节到缓存中(写入缓存) + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @param data 值(字节) + * @return size_t 返回实际复制成功的字节数 + */ +size_t k_pipe_poll_write(k_pipe_t *pipe_handle, uint8_t data) +{ + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + bool resume_flag; + + if (pipe) + { + pipe_id_t wid = pipe->wid; + + if (++wid >= pipe->size) + { + wid = 0; + } + + if (wid != pipe->rid) + { + ((uint8_t *)&pipe[1])[pipe->wid] = data; + + _K_DIS_INT(); + resume_flag = (pipe->wid == pipe->rid); + pipe->wid = wid; + _K_EN_INT(); + + if (resume_flag) + { + _k_ipc_resume(pipe->resume_work, pipe->resume_delay, 0); + } + + return 1; + } + } + + return 0; +} + +/** + * @brief 把内存数据复制到缓存中 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @param data 来源数据。如果值为 NULL 时,仅执行填充 + * @param size 来源数据大小(字节) + * @return size_t 返回实际复制成功的字节数 + */ +size_t k_pipe_fifo_fill(k_pipe_t *pipe_handle, const void *data, size_t size) +{ + size_t ret = 0; + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + bool resume_flag; + + if (pipe) + { + pipe_id_t wid = pipe->wid; + + resume_flag = (wid == pipe->rid); + + while (size) + { + pipe_id_t rid = *(volatile pipe_id_t *)&pipe->rid; + pipe_id_t min; + pipe_id_t remain; + + if (wid >= rid) + { + remain = pipe->size - wid - (rid == 0); + } + else + { + remain = rid - wid - 1; + } + if (remain == 0) + { + break; + } + min = size < remain ? size : remain; + + if (data) + { + memcpy(&((uint8_t *)&pipe[1])[wid], &((uint8_t *)data)[ret], min); + } + + if ((wid += min) >= pipe->size) + { + wid = 0; + } + + size -= min; + ret += min; + } + _K_DIS_INT(); + resume_flag = (pipe->wid == pipe->rid); + pipe->wid = wid; + _K_EN_INT(); + + if (resume_flag && ret) + { + _k_ipc_resume(pipe->resume_work, pipe->resume_delay, 0); + } + } + + return ret; +} + +/** + * @brief 从缓存复制一个字节到指定地址中 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @param data 目标内存地址 + * @return size_t 返回 0 表示缓存空 + */ +size_t k_pipe_poll_read(k_pipe_t *pipe_handle, uint8_t *data) +{ + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + + if (pipe) + { + pipe_id_t rid = pipe->rid; + if (rid != pipe->wid) + { + data[0] = ((uint8_t *)&pipe[1])[rid++]; + if (rid < pipe->size) + { + pipe->rid = rid; + } + else + { + pipe->rid = 0; + } + return 1; + } + } + + return 0; +} + +/** + * @brief 从管道中复制数据到内存(从缓存读取)。注:参数 data 值可以为 NULL,此时不复制数据,只释放相应的数据量 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @param data 目标内存地址。如果值为 NULL 时,仅执行释放 + * @param size 目标内存大小(字节) + * @return size_t 返回实际复制成功的字节数 + */ +size_t k_pipe_fifo_read(k_pipe_t *pipe_handle, void *data, size_t size) +{ + size_t ret = 0; + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + + if (pipe) + { + pipe_id_t rid = pipe->rid; + while (size) + { + pipe_id_t wid = *(volatile pipe_id_t *)&pipe->wid; + pipe_id_t min; + pipe_id_t remain; + + if (rid > wid) + { + remain = pipe->size - rid; + } + else + { + remain = wid - rid; + } + if (remain == 0) + { + break; + } + min = size < remain ? size : remain; + + if (data) + { + memcpy(&((uint8_t *)data)[ret], &((uint8_t *)&pipe[1])[rid], min); + } + + if ((rid += min) >= pipe->size) + { + rid = 0; + } + + size -= min; + ret += min; + } + pipe->rid = rid; + } + + return ret; +} + +/** + * @brief 获取管道非空 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @retval true 有数据 + * @retval false 表示空 + */ +bool k_pipe_is_ne(k_pipe_t *pipe_handle) +{ + if (pipe_handle->hdl == NULL) + { + return _FALSE; + } + else + { + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + if (pipe->rid == pipe->wid) + { + return _FALSE; + } + else + { + return _TRUE; + } + } +} + +/** + * @brief 获取管道的数据大小(字节数) + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @return size_t 数据大小(字节数) + */ +size_t k_pipe_get_valid_size(k_pipe_t *pipe_handle) +{ + if (pipe_handle->hdl == NULL) + { + return 0; + } + else + { + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + if (pipe->wid < pipe->rid) + { + return pipe->size + pipe->wid - pipe->rid; + } + else + { + return pipe->wid - pipe->rid; + } + } +} + +/** + * @brief 获取管道的剩余空间(字节数) + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @return size_t 总剩余大小(字节数) + */ +size_t k_pipe_get_empty_size(k_pipe_t *pipe_handle) +{ + if (pipe_handle->hdl == NULL) + { + return 0; + } + else + { + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + return pipe->size - (k_pipe_get_valid_size(pipe_handle) + 1); + } +} + +/** + * @brief 获取当前已写入的连续的内存信息。 + * pipe 实际是一环形缓存结构,获取的 dst_size 是指在缓存中的连续的长度,因此并不一定等于总长度。 + * 当需要读取已缓存数据时,取得当前已缓存的数据的连续内存信息后,可对缓存的直接访问,以节省多次复制内存的资源开销, + * 配合使用 k_pipe_fifo_read(pipe_handle, NULL, size) 直接释放对应的长度。 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @param dst_base[out] 已缓存数据的内存地址 + * @param dst_size[out] 已缓存数据的连续长度(字节) + */ +void k_pipe_peek_valid(k_pipe_t *pipe_handle, void **dst_base, size_t *dst_size) +{ + if (pipe_handle->hdl) + { + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + if (dst_base) + { + *dst_base = (&((uint8_t *)&pipe[1])[pipe->rid]); + } + if (dst_size) + { + pipe_id_t rid = pipe->rid; + pipe_id_t wid = pipe->wid; + if (rid > wid) + { + *dst_size = pipe->size - rid; + } + else + { + *dst_size = wid - rid; + } + } + } +} + +/** + * @brief 获取当前空闲的连续的内存信息。 + * pipe 实际是一环形缓存结构,获取的 dst_size 是指在缓存中的连续的长度,因此并不一定等于总长度。 + * 当需要写入缓存数据时,此函数用于取得当前空闲的连续内存信息后,可对缓存的直接访问,以节省多次复制内存的资源开销, + * 配合使用 k_pipe_fifo_fill(pipe_handle, NULL, size) 直接填充对应的长度。 + * + * @param pipe_handle 由 k_pipe_create() 初始化 + * @param dst_base[out] 当前空闲的内存地址 + * @param dst_size[out] 当前空闲的连续长度(字节) + */ +void k_pipe_peek_empty(k_pipe_t *pipe_handle, void **dst_base, size_t *dst_size) +{ + if (pipe_handle->hdl) + { + k_pipe_handle_t *pipe = (k_pipe_handle_t *)pipe_handle->hdl; + if (dst_base) + { + *dst_base = (&((uint8_t *)&pipe[1])[pipe->wid]); + } + if (dst_size) + { + pipe_id_t rid = pipe->rid; + pipe_id_t wid = pipe->wid; + if (wid >= rid) + { + *dst_size = pipe->size - wid - (rid == 0); + } + else + { + *dst_size = rid - wid - 1; + } + } + } +} + +/** + * @brief 获取当前系统时间 + * + * @return k_tick_t 当前系统时间 + */ +k_tick_t k_get_sys_ticks(void) +{ + return _K_PORT->get_sys_ticks(); +} + +/** + * @brief 禁止中断 + * + */ +void k_disable_interrupt(void) +{ + _K_DIS_INT(); +} + +/** + * @brief 恢复中断 + * + */ +void k_enable_interrupt(void) +{ + _K_EN_INT(); +} + +/** + * @brief 打印当前调度日志。 + * 在调度的过程中会实时记录调度和嵌套顺序, + * 通过本函数打印出具日志。 + * 需要配置 CONFIG_K_KIT_LOG_ON 为非 0 时生效 + * 注意若当前执行的任务未发生变化时不会打印 + */ +void k_log_sched(void) +{ +#if (CONFIG_K_KIT_LOG_ON) +#define _WORK_NAME(WORK) ((k_work_handle_t *)WORK->hdl)->name + k_work_t *work = s_cm_kit.curr_hook; + unsigned flag; + + if (s_cm_kit.curr_log == work) + { + return; + } + else + { + s_cm_kit.curr_log = work; + } + + _K_DIS_SCHED(); + + CONFIG_K_KIT_PRINT("\r\n"); + + do + { + unsigned index; + const char *arg; + unsigned max_index; + + flag = 0; + + max_index = sizeof(s_cm_kit.work_log) / sizeof(s_cm_kit.work_log[0]); + if (max_index > s_cm_kit.log_index) + { + max_index = s_cm_kit.log_index; + } + + CONFIG_K_KIT_PRINT("[work_q]"); + + for (index = 0; index < max_index; index++) + { + if (s_cm_kit.work_log[index] == work) + { + index++; + break; + } + arg = _WORK_NAME(s_cm_kit.work_log[index]); + if (arg == NULL) + arg = "Unknow"; + CONFIG_K_KIT_PRINT(" ==> '" _COLOR_C "%s" _COLOR_END "'", arg); + } + + arg = _WORK_NAME(work); + if (arg == NULL) + arg = "Unknow"; + if (index < s_cm_kit.log_max) + { + if (s_cm_kit.log_index > sizeof(s_cm_kit.work_log) / sizeof(s_cm_kit.work_log[0])) + { + CONFIG_K_KIT_PRINT(" ==> ... ==> '" _COLOR_C "%s" _COLOR_END "' >>>\r\n", arg); + } + else + { + unsigned i; + + CONFIG_K_KIT_PRINT(" ==> '" _COLOR_C "%s" _COLOR_END "'", arg); + + max_index = sizeof(s_cm_kit.work_log) / sizeof(s_cm_kit.work_log[0]); + if (max_index > s_cm_kit.log_max) + { + max_index = s_cm_kit.log_max; + } + for (i = index; i < max_index; i++) + { + arg = _WORK_NAME(s_cm_kit.work_log[i]); + if (arg == NULL) + arg = "Unknow"; + CONFIG_K_KIT_PRINT(" <<< '" _COLOR_C "%s" _COLOR_END "'", arg); + s_cm_kit.work_log[i] = NULL; + } + + if (s_cm_kit.log_max > sizeof(s_cm_kit.work_log) / sizeof(s_cm_kit.work_log[0])) + { + CONFIG_K_KIT_PRINT(" <<< ..."); + } + + CONFIG_K_KIT_PRINT("\r\n"); + + s_cm_kit.log_max = index; + flag = 1; + } + } + else + { + CONFIG_K_KIT_PRINT(" ==> '" _COLOR_C "%s" _COLOR_END "' >>>\r\n", arg); + } + + } while (flag); + + _K_EN_SCHED(); +#undef _WORK_NAME +#endif +} diff --git a/components/system/source/k_kit/k_kit.h b/components/system/source/k_kit/k_kit.h new file mode 100755 index 0000000..14a590f --- /dev/null +++ b/components/system/source/k_kit/k_kit.h @@ -0,0 +1,311 @@ +/** + * @file k_kit.h + * @author LokLiang (lokliang@163.com) + * @brief 裸机版微内核 + * @version 1.1.0 + * + * @copyright Copyright (c) 2022 + * + */ + +#ifndef __K_KIT_H__ +#define __K_KIT_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef int k_err_t; +typedef int k_tick_t; + +typedef void* k_hdl_t; + +typedef struct +{ + k_hdl_t hdl; +} k_work_q_t; + +typedef struct +{ + void *(*malloc)(size_t); // 执行申请内存(内部已通过 scheduler_disable()/scheduler_enable() 进行临界保护) + void (*free)(void *); // 执行释放内存(内部已通过 scheduler_disable()/scheduler_enable() 进行临界保护) + unsigned (*get_sys_ticks)(void); // 获取当前系统时间 + unsigned (*interrupt_save)(void); // 禁止中断 + void (*interrupt_restore)(unsigned); // 恢复中断 + + void (*scheduler_disable)(void); // 禁止线程的调度(实时内核使用,非实时内核使用可设置为NULL) + void (*scheduler_enable)(void); // 恢复线程的调度(实时内核使用,非实时内核使用可设置为NULL) + k_work_q_t *(*get_work_q_hdl)(void); // 获取当前执行 k_work_q_handler() 的线程的 k_work_q_t 对象(实时内核使用,非实时内核使用可设置为NULL) + void (*thread_sleep)(k_tick_t sleep_ticks); // 使线程休眠(实时内核使用,非实时内核使用可设置为NULL) +} k_init_t; + +void k_entry(const k_init_t *init_struct); // 初始化并进入微内核模式 + +void k_init(const k_init_t *init_struct); // 初始化微内核 +void k_deinit(void); // 取消初始化微内核 + +/** @defgroup work queue + * @{ + */ + +typedef void (*k_work_q_fn)(void *arg); + +extern k_work_q_t *default_work_q_hdl; // 预留的一个工默认的作队列对象内存,初始值为内部默认的对象 + +k_tick_t k_work_q_handler(k_work_q_t *work_q_handle); // 工作队列执行入口 + +k_err_t k_work_q_create(k_work_q_t *work_q_handle); // 创建一个工作队列的对象 +void k_work_q_delete(k_work_q_t *work_q_handle); // 删除一个工作队列的对象 + +void k_work_q_resume_regist(k_work_q_t *work_q_handle, k_work_q_fn work_q_resume, void *arg); // 注册一个函数:当有任务被提交时,此回调函数将被自动执行 + +bool k_work_q_is_valid(k_work_q_t *work_q_handle); // 获取工作队列是否有效 + +/** + * @} + */ + +/** @defgroup work + * @{ + */ + +typedef struct +{ + k_hdl_t hdl; +} k_work_t; + +typedef void (*k_work_fn)(void *arg); + +void k_work_hook(k_work_q_t *work_q_handle, k_work_fn hook); // 设置切换任务时,进入和退出任务时回调函数 + +bool k_work_q_delayed_state(k_work_q_t *work_q_handle); // 获取是否有正在延时状态的任务 +bool k_work_q_ready_state(k_work_q_t *work_q_handle); // 获取工作队列中是否有就绪的任务 + +k_err_t k_work_create(k_work_t *work_handle, + const char *name, + k_work_fn work_route, + void *arg, + uint8_t priority); + +void k_work_delete(k_work_t *work_handle); // 删除由工作队列管理的任务 + +bool k_work_is_valid(k_work_t *work_handle); // 获取任务对象是否有效 +bool k_work_is_pending(k_work_t *work_handle); // 获取任务是否在就绪或延时的状态 +k_tick_t k_work_time_remain(k_work_t *work_handle); // 获取任务距离下个执行的剩余时间 + +void k_work_submit(k_work_q_t *work_q_handle, k_work_t *work_handle, k_tick_t delay_ticks); // 使任务在指定工作队列中在指定时间后就绪 +void k_work_resume(k_work_t *work_handle, k_tick_t delay_ticks); // 唤醒任务。注意需要先使用 k_work_submit 绑定一个 work_q_handle +void k_work_suspend(k_work_t *work_handle); // 挂起任务,任务不会被删除 + +void k_work_later(k_tick_t delay_ticks); // 延时多少个系统节拍后再执行本任务 +void k_work_later_until(k_tick_t delay_ticks); // 从最后一次唤醒的时间算起,延时多少个系统节拍后再执行本任务(固定周期的延时) +void k_work_yield(k_tick_t delay_ticks); // 释放一次CPU的使用权,不调度低于当前任务优先级的任务 +void k_work_sleep(k_tick_t delay_ticks); // 释放一次CPU的使用权,可调度低于当前任务优先级的任务 + +k_work_t *k_get_curr_work_handle(k_work_q_t *work_q_handle); // 查询最近一次执行的任务 + +k_err_t k_work_mbox_create(k_work_t *work_handle); // 创建任务的邮箱 +void k_work_mbox_delete(k_work_t *work_handle); // 删除任务的邮箱 + +void *k_work_mbox_alloc(k_work_t *work_handle, size_t size); // 申请一个邮件 +k_err_t k_work_mbox_cancel(void *mbox); // 取消已申请的邮件 +k_err_t k_work_mbox_submit(void *mbox); // 发送邮件 +void *k_work_mbox_take(void); // 提取邮件 +void *k_work_mbox_peek(void); // 查询邮件 +void k_work_mbox_clr(void); // 清空任务的所有邮件 + +/** + * @} + */ + +/** @defgroup timer queue + * @{ + */ + +typedef struct +{ + k_hdl_t hdl; +} k_timer_q_t; + +typedef void (*k_timer_q_fn)(void *arg); + +extern k_timer_q_t *default_timer_q_hdl; // 预留的一个工默认的软定时器队列对象内存,初始值为内部默认的对象 + +k_tick_t k_timer_q_handler(k_timer_q_t *timer_q_handle); // 软定时器队列执行入口 + +k_err_t k_timer_q_create(k_timer_q_t *timer_q_handle); // 创建一个软定时器队列的对象 +void k_timer_q_delete(k_timer_q_t *timer_q_handle); // 删除一个软定时器队列的对象 + +void k_timer_q_resume_regist(k_timer_q_t *timer_q_handle, k_timer_q_fn timer_q_resume, void *arg); // 注册一个函数:当有任务被提交时,此回调函数将被自动执行 + +bool k_timer_q_is_valid(k_timer_q_t *timer_q_handle); // 获取软定时器队列是否有效 + +/** + * @} + */ + +/** @defgroup timer + * @{ + */ + +typedef struct +{ + k_hdl_t hdl; +} k_timer_t; + +typedef void (*k_timer_fn)(void *arg); + +bool k_timer_q_delayed_state(k_timer_q_t *timer_q_handle); // 获取是否有正在延时状态的定时器 +bool k_timer_q_ready_state(k_timer_q_t *timer_q_handle); // 获取软定时器队列中是否有就绪的定时器 + +k_err_t k_timer_create(k_timer_t *timer_handle, + k_timer_q_t *timer_q_handle, + k_timer_fn timer_route, + void *arg, + uint8_t priority); + +void k_timer_delete(k_timer_t *timer_handle); + +void k_timer_set_period(k_timer_t *timer_handle, bool periodic, k_tick_t period); // 设置定时器的自动重装值 +void k_timer_start(k_timer_t *timer_handle, k_tick_t delay_ticks); // 使定时器在指定软定时器队列中在指定时间后就绪 +void k_timer_stop(k_timer_t *timer_handle); // 挂起定时器,定时器不会被删除 + +k_err_t k_timer_newsubmit(k_timer_q_t *timer_q_handle, k_timer_fn timer_route, void *arg, k_tick_t delay_ticks); // 生成一个临时定时器,执行完自动删除 +k_err_t k_timer_resubmit(k_timer_q_t *timer_q_handle, k_timer_fn timer_route, void *arg, k_tick_t delay_ticks); // 删除并重新生成一个新的临时定时器,执行完自动删除 +void k_timer_cancel(k_timer_q_t *timer_q_handle, k_timer_fn timer_route); // 立即删除临时定时器 + +bool k_timer_is_valid(k_timer_t *timer_handle); // 获取定时器对象是否有效 +bool k_timer_is_pending(k_timer_t *timer_handle); // 获取定时器是否在就绪或延时的状态 +bool k_timer_is_periodic(k_timer_t *timer_handle); // 查询当前是否自动重装 +k_tick_t k_timer_get_period(k_timer_t *timer_handle); // 查询当前的自动重装值 +k_tick_t k_timer_time_remain(k_timer_t *timer_handle); // 获取定时器距离下个执行的剩余时间 + +/** + * @} + */ + +/** @defgroup fifo + * @{ + */ + +typedef struct +{ + k_hdl_t hdl; +} k_fifo_t; + +k_err_t k_fifo_q_create(k_fifo_t *fifo_handle); // 创建一个 FIFO 对象 +void k_fifo_q_delete(k_fifo_t *fifo_handle); // 删除一个 FIFO 的对象 +void k_fifo_q_clr(k_fifo_t *fifo_handle); // 清除 FIFO 内的所有数据 + +bool k_fifo_q_is_valid(k_fifo_t *fifo_handle); // 获取 FIFO 对象是否有效 + +void k_fifo_q_regist(k_fifo_t *fifo_handle, k_work_t *work_handle, k_tick_t delay_ticks); // 注册当队列非空时被唤醒的任务 +void k_fifo_q_unregist(k_fifo_t *fifo_handle); // 取消注册任务 + +void *k_fifo_alloc(size_t size); // 申请可用于 FIFO 的数据结构 +k_err_t k_fifo_free(void *data); // 释放由 k_fifo_alloc() 申请的数据结构 + +k_err_t k_fifo_put(k_fifo_t *fifo_handle, void *data); // 把数据结构压入到 FIFO 中 +void *k_fifo_take(k_fifo_t *fifo_handle); // 从 FIFO 中弹出最先压入的数据 +void *k_fifo_peek_head(k_fifo_t *fifo_handle); // 查询 FIFO 中头部的数据地址 +void *k_fifo_peek_tail(k_fifo_t *fifo_handle); // 查询 FIFO 中尾部的数据地址 + +/** + * @} + */ + +/** @defgroup queue + * @{ + */ + +typedef struct +{ + k_hdl_t hdl; +} k_queue_t; + +k_err_t k_queue_create(k_queue_t *queue_handle, size_t queue_length, size_t item_size); // 创建一个 QUEUE 对象 +void k_queue_delete(k_queue_t *queue_handle); // 删除一个 QUEUE 的对象 +void k_queue_clr(k_queue_t *queue_handle); // 清除 QUEUE 内的所有数据 + +bool k_queue_is_valid(k_queue_t *queue_handle); // 获取 QUEUE 对象是否有效 + +void k_queue_regist(k_queue_t *queue_handle, k_work_t *work_handle, k_tick_t delay_ticks); // 注册当队列非空时被唤醒的任务 +void k_queue_unregist(k_queue_t *queue_handle); // 取消注册任务 + +k_err_t k_queue_recv(k_queue_t *queue_handle, void *dst); // 接收并复制数据 +k_err_t k_queue_send(k_queue_t *queue_handle, const void *src); // 复制数据并发送 + +void *k_queue_alloc(k_queue_t *queue_handle); // 申请可用于 QUEUE 的数据结构 +k_err_t k_queue_free(void *data); // 释放由 k_queue_alloc() 申请的数据结构 +k_err_t k_queue_put(void *data); // 把数据压入到 QUEUE 中 +void *k_queue_take(k_queue_t *queue_handle); // 从 QUEUE 中弹出最先压入的数据 + +void *k_queue_peek_head(k_queue_t *queue_handle); // 查询 QUEUE 中头部的数据地址 +void *k_queue_peek_tail(k_queue_t *queue_handle); // 查询 QUEUE 中尾部的数据地址 + +size_t k_queue_get_item_size(k_queue_t *queue_handle); // 读回 k_queue_create() 中设置的 item_size 的值 + +k_queue_t *k_queue_read_handle(void *data); // 查询已申请数据的所属队列句柄 + +/** + * @} + */ + +/** @defgroup pipe + * @{ + */ + +typedef struct +{ + k_hdl_t hdl; +} k_pipe_t; + +k_err_t k_pipe_create(k_pipe_t *pipe_handle, size_t pipe_size); // 创一个管道对象 +void k_pipe_delete(k_pipe_t *pipe_handle); // 删除一个管道对象 +void k_pipe_clr(k_pipe_t *pipe_handle); // 清空管道的数据 + +bool k_pipe_is_valid(k_pipe_t *pipe_handle); // 获取 PIPE 对象是否有效 + +void k_pipe_regist(k_pipe_t *pipe_handle, k_work_t *work_handle, k_tick_t delay_ticks); // 注册当队列非空时被唤醒的任务 +void k_pipe_unregist(k_pipe_t *pipe_handle); // 取消注册任务 + +size_t k_pipe_poll_write(k_pipe_t *pipe_handle, uint8_t data); // 写一个字节到缓存中(写入缓存),返回实际复制成功的字节数 +size_t k_pipe_fifo_fill(k_pipe_t *pipe_handle, const void *data, size_t size); // 把内存数据复制到缓存中(写入缓存),返回实际复制成功的字节数 +size_t k_pipe_poll_read(k_pipe_t *pipe_handle, uint8_t *data); // 从缓存复制一个字节到指定地址中,返回 0 表示缓存空 +size_t k_pipe_fifo_read(k_pipe_t *pipe_handle, void *data, size_t size); // 从管道中复制数据到内存(从缓存读取),返回实际复制成功的字节数。注:参数 data 值可以为 NULL,此时不复制数据,只释放相应的数据量 + +bool k_pipe_is_ne(k_pipe_t *pipe_handle); // 获取管道非空, true 有数据 +size_t k_pipe_get_valid_size(k_pipe_t *pipe_handle); // 获取管道的数据大小(字节数) +size_t k_pipe_get_empty_size(k_pipe_t *pipe_handle); // 获取管道的剩余空间(字节数) + +void k_pipe_peek_valid(k_pipe_t *pipe_handle, void **dst_base, size_t *dst_size); // 获取当前已写入的连续的内存信息 +void k_pipe_peek_empty(k_pipe_t *pipe_handle, void **dst_base, size_t *dst_size); // 获取当前空闲的连续的内存信息 + +/** + * @} + */ + +/** @defgroup miscellaneous + * @{ + */ + +k_tick_t k_get_sys_ticks(void); // 获取当前系统时间 +void k_disable_interrupt(void); // 禁止中断(屏蔽中断并自动记录嵌套数) +void k_enable_interrupt(void); // 恢复中断(根据嵌套数自动恢复中断) + +void k_log_sched(void); // 打印当前调度日志 + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/source/k_kit/kk.h b/components/system/source/k_kit/kk.h new file mode 100755 index 0000000..c533e9b --- /dev/null +++ b/components/system/source/k_kit/kk.h @@ -0,0 +1,153 @@ +/** + * @file kk.h + * @author lokliang + * @brief k_kit.h 衍生的易用接口,简化部分接口中的参数,去除不常用的接口 + * @version 1.0 + * @date 2022-12-09 + * @date 2021-01-13 + * + * @copyright Copyright (c) 2021 + * + */ + +#ifndef __KK_H__ +#define __KK_H__ + +#include "k_kit.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef k_err_t kk_err_t; +typedef k_tick_t kk_time_t; +typedef k_work_q_t kk_work_q_t; +typedef k_work_t kk_work_t; +typedef k_timer_t kk_timer_t; +typedef k_fifo_t kk_fifo_t; +typedef k_queue_t kk_queue_t; +typedef k_pipe_t kk_pipe_t; + +#define WORK_Q_HDL default_work_q_hdl +#define TIMER_Q_HDL default_timer_q_hdl + +#define kk_handler() k_work_q_handler(WORK_Q_HDL) // 工作队列执行入口 +#define kk_timer_handler() k_timer_q_handler(TIMER_Q_HDL) // 软定时器队列执行入口 + +#define kk_work_q_delayed_state() k_work_q_delayed_state(WORK_Q_HDL) // 获取是否有正在延时状态的任务 +#define kk_work_q_ready_state() k_work_q_ready_state(WORK_Q_HDL) // 获取工作队列中是否有就绪的任务 +#define kk_work_q_delete() k_work_q_delete(WORK_Q_HDL) // 删除一个工作队列的对象 +#define kk_work_q_is_valid() k_work_q_is_valid(WORK_Q_HDL) // 获取工作队列是否有效 + +#define kk_work_create(work_handle, work_route, arg, priority) k_work_create(work_handle, #work_route, work_route, arg, priority) // 创建由工作队列管理的任务 +#define kk_work_delete(work_handle) k_work_delete(work_handle) // 删除由工作队列管理的任务 +#define kk_work_submit(work_q_handle, work_handle, delay) k_work_submit(work_q_handle, work_handle, delay) // 多少时间后唤醒任务 + +#define kk_work_is_valid(work_handle) k_work_is_valid(work_handle) // 获取任务对象是否有效 +#define kk_work_is_pending(work_handle) k_work_is_pending(work_handle) // 获取任务是否在就绪或延时的状态 +#define kk_work_time_remain(work_handle) k_work_time_remain(work_handle) // 获取任务距离下个执行的剩余时间 + +#define kk_timer_create(timer_handle, timer_route, arg) k_timer_create(timer_handle, TIMER_Q_HDL, timer_route, arg, 0) // 创建由软定时器队列管理的定时器任务 +#define kk_timer_delete(timer_handle) k_timer_delete(timer_handle) // 删除由软定时器队列管理的定时器任务 + +#define kk_timer_start(timer_handle, periodic, period) do { k_timer_set_period(timer_handle, periodic, period); k_timer_start(timer_handle, period); } while (0) // 启动定时器 +#define kk_timer_stop(timer_handle) k_timer_stop(timer_handle) // 挂起定时器,定时器不会被删除 + +#define kk_timer_is_valid(timer_handle) k_timer_is_valid(timer_handle) // 获取定时器对象是否有效 +#define kk_timer_is_pending(timer_handle) k_timer_is_pending(timer_handle) // 获取定时器是否在就绪或延时的状态 +#define kk_timer_time_remain(timer_handle) k_timer_time_remain(timer_handle) // 获取定时器距离下个执行的剩余时间 + +#define kk_work_mbox_create(work_handle) k_work_mbox_create(work_handle) // 创建任务的邮箱 +#define kk_work_mbox_delete(work_handle) k_work_mbox_delete(work_handle) // 删除任务的邮箱 + +#define kk_fifo_q_create(fifo_handle) k_fifo_q_create(fifo_handle) // 创建一个FIFO对象 +#define kk_fifo_q_delete(fifo_handle) k_fifo_q_delete(fifo_handle) // 删除一个FIFO的对象 +#define kk_fifo_q_clr(fifo_handle) k_fifo_q_clr(fifo_handle) // 清除FIFO内的所有数据 +#define kk_fifo_q_is_valid(fifo_handle) k_fifo_q_is_valid(fifo_handle) // 获取 FIFO 对象是否有效 +#define kk_fifo_q_regist(fifo_handle, work_handle, ticks) k_fifo_q_regist(fifo_handle, work_handle, ticks) // 注册当队列非空时被唤醒的任务 + +#define kk_queue_create(queue_handle, queue_length, item_size) k_queue_create(queue_handle, queue_length, item_size) // 创建一个QUEUE对象 +#define kk_queue_delete(queue_handle) k_queue_delete(queue_handle) // 删除一个QUEUE的对象 +#define kk_queue_clr(queue_handle) k_queue_clr(queue_handle) // 清除QUEUE内的所有数据 +#define kk_queue_is_valid(queue_handle) k_queue_is_valid(queue_handle) // 获取 QUEUE 对象是否有效 +#define kk_queue_regist(queue_handle, work_handle, ticks) k_queue_regist(queue_handle, work_handle, ticks) // 注册当队列非空时被唤醒的任务 + +#define kk_pipe_create(pipe_handle, pipe_size) k_pipe_create(pipe_handle, pipe_size) // 创一个管道对象 +#define kk_pipe_delete(pipe_handle) k_pipe_delete(pipe_handle) // 删除一个管道对象 +#define kk_pipe_clr(pipe_handle) k_pipe_clr(pipe_handle) // 清空管道的数据 +#define kk_pipe_is_valid(pipe_handle) k_pipe_is_valid(pipe_handle) // 获取 PIPE 对象是否有效 +#define kk_pipe_regist(pipe_handle, work_handle, ticks) k_pipe_regist(pipe_handle, work_handle, ticks) // 注册当队列非空时被唤醒的任务 + +#define kk_work_resume(work_handle, delay) k_work_resume(work_handle, delay) // 唤醒任务。注意需要先使用 k_work_submit 绑定一个 work_q_handle +#define kk_work_suspend(work_handle) k_work_suspend(work_handle) // 挂起任务,任务不会被删除 +#define kk_work_later(delay) k_work_later(delay) // 设置下个执行延时 +#define kk_work_later_until(delay) k_work_later_until(delay) // 从最后一次唤醒的时间算起,延时多少个系统节拍后再执行本任务(固定周期的延时) +#define kk_work_yield(delay) k_work_yield(delay) // 释放一次CPU的使用权,不调度低于当前任务优先级的任务 +#define kk_work_sleep(delay) k_work_sleep(delay) // 释放一次CPU的使用权,可调度低于当前任务优先级的任务 + +#define kk_work_mbox_alloc(work_handle, size) k_work_mbox_alloc(work_handle, size) // 申请一个邮件 +#define kk_work_mbox_cancel(mbox) k_work_mbox_cancel(mbox) // 取消已申请的邮件 +#define kk_work_mbox_submit(mbox) k_work_mbox_submit(mbox) // 发送邮件 +#define kk_work_mbox_take() k_work_mbox_take() // 提取邮件 +#define kk_work_mbox_peek() k_work_mbox_peek() // 查询邮件 +#define kk_work_mbox_clr() k_work_mbox_clr() // 清空任务的所有邮件 + +#define kk_fifo_alloc(size) k_fifo_alloc(size) // 申请可用于FIFO的数据结构 +#define kk_fifo_free(data) k_fifo_free(data) // 释放由 kk_fifo_alloc() 申请的数据结构 +#define kk_fifo_put(fifo_handle, data) k_fifo_put(fifo_handle, data) // 把数据结构压入到FIFO中 +#define kk_fifo_take(fifo_handle) k_fifo_take(fifo_handle) // 从FIFO中弹出最先压入的数据 +#define kk_fifo_peek_head(fifo_handle) k_fifo_peek_head(fifo_handle) // 查询FIFO中头部的数据地址 +#define kk_fifo_peek_tail(fifo_handle) k_fifo_peek_tail(fifo_handle) // 查询FIFO中尾部的数据地址 + +#define kk_queue_recv(queue_handle, dst) k_queue_recv(queue_handle, dst) // 接收并复制数据 +#define kk_queue_send(queue_handle, src) k_queue_send(queue_handle, src) // 复制数据并发送 +#define kk_queue_alloc(queue_handle) k_queue_alloc(queue_handle) // 申请可用于QUEUE的数据结构 +#define kk_queue_free(data) k_queue_free(data) // 释放由 kk_queue_alloc() 申请的数据结构 +#define kk_queue_put(data) k_queue_put(data) // 把数据压入到QUEUE中 +#define kk_queue_take(queue_handle) k_queue_take(queue_handle) // 从QUEUE中弹出最先压入的数据 +#define kk_queue_peek_head(queue_handle) k_queue_peek_head(queue_handle) // 查询QUEUE中头部的数据地址 +#define kk_queue_peek_tail(queue_handle) k_queue_peek_tail(queue_handle) // 查询QUEUE中尾部的数据地址 +#define kk_queue_get_item_size(queue_handle) k_queue_get_item_size(queue_handle); // 读回 k_queue_create() 中设置的 item_size 的值 + +#define kk_pipe_fifo_fill(pipe_handle, data, size) k_pipe_fifo_fill(pipe_handle, data, size) // 把内存数据复制到缓存中(写入缓存),返回实际复制成功的字节数 +#define kk_pipe_poll_write(pipe_handle, data) k_pipe_poll_write(pipe_handle, data) // 写一个字节到缓存中(写入缓存),返回实际复制成功的字节数 +#define kk_pipe_fifo_read(pipe_handle, data, size) k_pipe_fifo_read(pipe_handle, data, size) // 从管道中复制数据到内存(从缓存读取),返回实际复制成功的字节数。注:参数 data 值可以为 NULL,此时不复制数据,只释放相应的数据量 +#define kk_pipe_poll_read(pipe_handle, data) k_pipe_poll_read(pipe_handle, data) // 从缓存复制一个字节到指定地址中,返回 0 表示缓存空 + +#define kk_pipe_is_ne(pipe_handle) k_pipe_is_ne(pipe_handle) // 获取管道非空, true 有数据 +#define kk_pipe_get_valid_size(pipe_handle) k_pipe_get_valid_size(pipe_handle) // 获取管道的数据大小(字节数) +#define kk_pipe_get_empty_size(pipe_handle) k_pipe_get_empty_size(pipe_handle) // 获取管道的剩余空间(字节数) + +#define kk_pipe_peek_valid(pipe_handle, dst_data, dst_size) k_pipe_peek_valid(pipe_handle, dst_data, dst_size) // 获取当前已写入的连续的内存信息 +#define kk_pipe_peek_empty(pipe_handle, dst_data, dst_size) k_pipe_peek_empty(pipe_handle, dst_data, dst_size) // 获取当前空闲的连续的内存信息 + +#define kk_get_sys_ticks() k_get_sys_ticks() // 获取当前系统时间 +#define kk_get_heap_mem() k_get_heap_mem() // 获取初始化时指定的堆内存 + +#define kk_disable_interrupt() k_disable_interrupt() // 禁止中断 +#define kk_enable_interrupt() k_enable_interrupt() // 恢复中断 + +#define kk_log_sched() k_log_sched() // 打印当前调度日志 + +#if defined(MIX_COMMON) +#include "heap.h" +#endif + +#define kk_malloc(size) heap_malloc(NULL, size) // 申请内存 +#define kk_calloc(size) heap_calloc(NULL, size) // 申请内存并置0 +#define kk_realloc(ptr, size) heap_realloc(NULL, ptr, size) // 重定义已申请的内存大小 +#define kk_free(ptr) heap_free(NULL, ptr) // 释放内存 + +#define kk_heap_is_valid(ptr) heap_is_valid(NULL, ptr) // 获取内存指针是否有效 +#define kk_heap_block_size(ptr) heap_block_size(ptr) // 已申请内存块的实际占用空间大小(不含所有控制信息)(字节) +#define kk_heap_space_size(ptr) heap_space_size(ptr) // 已申请内存块的实际占用空间大小(含所有控制信息)(字节) +#define kk_heap_used_size() heap_used_size(NULL) // 获取总已使用空间 +#define kk_heap_free_size() heap_free_size(NULL) // 获取总空闲空间(包含所有碎片) +#define kk_heap_block_max() heap_block_max(NULL) // 获取当前最大的连续空间 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/system/source/shell/README.md b/components/system/source/shell/README.md new file mode 100755 index 0000000..29e3827 --- /dev/null +++ b/components/system/source/shell/README.md @@ -0,0 +1,104 @@ +[简要](../../../README.md) + +# shell + + > 是一款适合单片机用的简单的 shell 模块,使单片也能像 Linux 一样,具体基本的命令行功能,适用于示例、调试、应用等场合。 + +## 快捷键支持 + - Tap: 补全命令或参数 + - Up: 查看上一个命令 + - Down: 查看下一个命令 + - Home: 光标移到开头位置 + - End: 光标移到结尾位置 + - Ctrl+C: 结束程序 + - Ctrl+D: 删除光标后一个字符或结束程序 + - Ctrl+L: 清空终端 + - Ctrl+A: 同 Home + - Ctrl+E: 同 End + - Ctrl+U: 擦除光标前到行首的全部内容 + - Ctrl+K: 擦除光标后到行尾的全部内容 + - Ctrl+W: 擦除光标前的单词 + - Ctrl+Y: 还原擦除的内容 + - Ctrl+Right: 光标移动到单词首位 + - Ctrl+Left: 光标移动到单词结尾 + +## 如何使用 + +### 移植 + > 调用 sh_init_vt100() 进行初始化时指定并实现两个对应的接口即可,实际上在 [sh_vt100.c](./sh_vt100.c) 已做好一系列的工作,一般情况下无需再另外实现,除非有多数据接口的需求。 + +### 初始化 + > 在 [sh_t100.c](./sh_vt100.c) 中已通过 _install_shell_vt100() 被初始化,对象 g_uart_handle_vt100 直接可用。 + +### 注册命令列表 + > 所有命令都必须先注册才可生效。 + + - 使用宏 SH_REGISTER_CMD 注册 + > 使用自动初始化的宏注册一个父命令列表。以下摘自 [sc.h](./sh.c) 文件的命令定义部分。 +```c +SH_DEF_SUB_CMD( + _cmd_echo_sublist, + SH_SETUP_CMD("on", "Enable to feedback the command line", _cmd_echo_on, NULL), // + SH_SETUP_CMD("off", "Disable to feedback the command line", _cmd_echo_off, NULL), // +); + +SH_DEF_SUB_CMD( + _cmd_sh_sublist, + SH_SETUP_CMD("echo", "Turn on/off echo through sh_echo()", NULL, _cmd_echo_sublist), // + SH_SETUP_CMD("history", "Show history control", _cmd_history, _cmd_history_param), // + SH_SETUP_CMD("list-init", "List all auto initialize function\r\n\t* Usage: list-init [filter]", _cmd_print_init_fn, NULL), // + SH_SETUP_CMD("list-module", "List all module structure\r\n\t* Usage: list-module [filter]", _cmd_print_module_struct, NULL), // + SH_SETUP_CMD("version", "Print the shell version", _cmd_version, NULL), // + SH_SETUP_CMD("parse-value", "Parse the value of a string demo", _cmd_parse_test, NULL), // +); + +SH_REGISTER_CMD( + register_internal_command, + SH_SETUP_CMD("sh", "Internal command", NULL, _cmd_sh_sublist), // + SH_SETUP_CMD("help", "Print the complete root command", _cmd_print_help, _cmd_print_help_param), // + SH_SETUP_CMD("select", "Select parent command", _cmd_select, _cmd_select_param), // + SH_SETUP_CMD("exit", "Exit parent command or disconnect", _cmd_exit, NULL), // +); +``` + + - 使用函数接口动态注册 + - sh_register_cmd(const sh_cmd_reg_t *sh_reg); + - sh_register_cmd_hide(const sh_cmd_reg_t *sh_reg); + - sh_unregister_cmd(const sh_cmd_reg_t *sh_reg); + +### 设置子命令、命令勾子和补全勾子 + > 命令勾子和执行补全功能的勾子函数都在命令列表中确定,它们分别位于 SH_SETUP_CMD 的第 3、4 个参数。 + + - 命令勾子 + > 如实例中显示,位于 SH_SETUP_CMD 的第 3 个参数,设置命令勾子。 + > 如果命令勾子取值为函数地址,则表示这个命令为完整命令,此时第 4 个参数必须是 cp_fn ,并且可设置对补全勾子或者为 NULL; + > 如果命令勾子取值为NULL,则表示这个命令为父命令,此时第 4 个参数必须是 sub_cmd ,并且指向另一个由 SH_DEF_SUB_CMD 定义的命令列表; + +### 命令勾子的可用接口 + - sh_set_prompt() + - sh_reset_line() + - sh_echo() + - sh_parse_value() + - sh_merge_param() + +### 补全勾子的可用接口 + - sh_putstr_quiet() + - sh_cmd_info() + - sh_completion_cmd() + - sh_completion_param() + - sh_completion_resource() + - sh_get_cp_result() + +### 数据流向 + - 输入 + > 任何需要解析的数据由 sh_putc() 或 sh_putstr() 开始, 在内部最终完成解析和回调执行的整个过程; + + - 输出 + > 命令勾子或补全勾子可使用如下函数回显: + - sh_echo() + - sh_set_prompt() + - sh_reset_line() + +## 参考实例 + - [heap_shell.c](../../../architecture/common/heap/heap_shell.c) + - [fatfs_shell.c](../../../source/components/fs/fatfs_shell.c) diff --git a/components/system/source/shell/sh.c b/components/system/source/shell/sh.c new file mode 100755 index 0000000..6d579d5 --- /dev/null +++ b/components/system/source/shell/sh.c @@ -0,0 +1,3051 @@ +/** + * @file sh.c + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-03-21 + * + * @copyright Copyright (c) 2023 + * + */ + +#include "sh.h" + +#include +#include +#include +#include + +#define SYS_LOG_DOMAIN "sh" +#include "sys_log.h" + +#ifndef CONFIG_SH_MAX_PARAM +#define CONFIG_SH_MAX_PARAM (CONFIG_SH_MAX_LINE_LEN / 5) /* 以空格为分隔符的最多命令段数 */ +#endif + +#define _SH_ARRAY_COUNT(NAME) (sizeof(NAME) / sizeof(NAME[0])) +#define _IS_ALPHA(c) ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) +#define _IS_DIGIT(c) (c >= '0' && c <= '9') + +#define _YEAR ((((__DATE__[7] - '0') * 10 + (__DATE__[8] - '0')) * 10 + (__DATE__[9] - '0')) * 10 + (__DATE__[10] - '0')) + +#define _MONTH ((__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n') ? 1 \ + : (__DATE__[0] == 'F' && __DATE__[1] == 'e' && __DATE__[2] == 'b') ? 2 \ + : (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r') ? 3 \ + : (__DATE__[0] == 'A' && __DATE__[1] == 'p' && __DATE__[2] == 'r') ? 4 \ + : (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y') ? 5 \ + : (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n') ? 6 \ + : (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l') ? 7 \ + : (__DATE__[0] == 'A' && __DATE__[1] == 'u' && __DATE__[2] == 'g') ? 8 \ + : (__DATE__[0] == 'S' && __DATE__[1] == 'e' && __DATE__[2] == 'p') ? 9 \ + : (__DATE__[0] == 'O' && __DATE__[1] == 'c' && __DATE__[2] == 't') ? 10 \ + : (__DATE__[0] == 'N' && __DATE__[1] == 'o' && __DATE__[2] == 'v') ? 11 \ + : 12) + +#define _DAY ((__DATE__[4] == ' ' ? 0 : __DATE__[4] - '0') * 10 + (__DATE__[5] - '0')) + +static sys_pslist_t s_cmd_list; // 已注册的根命令链 +static sys_pslist_t s_cmd_list_hide; // 已注册的根命令链(不会出现在补全中) + +static const sh_key_t *_find_key(sh_t *sh_hdl, const char *code); // 在热键链中查找完全匹配的节点 +static const sh_cmd_t *_find_cmd(sh_t *sh_hdl, const sh_cmd_reg_t **dest_reg_struct, const sh_cmd_t *sub_cmd, const char *cmd); // 在所有命令结构中中查找完全匹配的节点 +static const sh_cmd_t *_find_root_cmd(sh_t *sh_hdl, const sh_cmd_reg_t **dest_reg_struct, const char *cmd); // 在根命令链中查找完全匹配的节点 +static int _register_cmd(sh_t *sh_hdl, sys_pslist_t *list, const sh_cmd_reg_t *sh_reg); // 注册命令 +static int _register_cmd_check_fn(char *err_cmd, const sh_cmd_t *cmd); // 检查 sh_cmd_t::fn 的类型是否符合规则 +static void _find_and_run_key(sh_t *sh_hdl, char c); // 一系列动作:保存一个字符到热键缓存,并尝试查找对应的热键功能函数。缓存会被自动清除。 +static void _insert_str(sh_t *sh_hdl, const char *str); // 在当前的光标前插入一个字符并显示 +static void _read_param(int *argc, const char *argv[CONFIG_SH_MAX_PARAM], char *dest, const char *src); // 从 src 中取得结果,输出到 int *argc, char **argv[], char *dest +static void _store_history(sh_t *sh_hdl, const char *src); // 保存历史记录 +static void _apply_history(sh_t *sh_hdl); // 当在回翻历史记录时,一旦对记录修改则立即将当前记录复制到缓存中 +static unsigned _get_end_pos(sh_t *sh_hdl); // 根据当前显示内容,获取最大光标位置 + +static void _store_cmd_bak(sh_t *sh_hdl, const char *line, int pos, int n); // 保存擦除的数据 +static void _clear_argv(int argc, const char *argv[]); // 使剩余的字符串指针指向空字符串 + +static const char *sh_ctrl_get_prompt(sh_t *sh_hdl); // 获取当前提示符 +static const char *sh_ctrl_get_line(sh_t *sh_hdl); // 获取当前已输入(或回显的历史)的字符串 + +static sh_cp_info_t _completion_init(const char *cmd); // 初始化待自动补全数据 +static void _completion_update( + sh_t *sh_hdl, + sh_cp_info_t *info, + const sh_cmd_t *sub_cmd, + bool print_match, + int max_lines, + int flag_parent); +static sh_cp_op_t _sh_completion_cmd(sh_t *sh_hdl, int argc, const char *argv[], int flag_parent); +static bool _do_completion_cmd(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cmd_info_t *cmd_info, bool print_match, int flag_parent); // 执行一次自动补全命令的功能 +static bool _do_completion_param(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cp_param_t *sh_param, bool print_match); // 执行一次自动补全参数的功能 +static bool _do_completion_insert(sh_t *sh_hdl, sh_cp_info_t *info); // 执行自动补全 +static void _do_restore_line(sh_t *sh_hdl); // 在展示选项后恢复命令行的显示 +static int _calc_list_algin(sh_cp_info_t *info); // 确定每个命令的对齐的长度 +static bool _arg_is_end(sh_t *sh_hdl); // 当前输入的最后一个参数是否完整(以空格分开) + +static void _update_cursor(sh_t *sh_hdl); // 同步热键功能:更新光标位置记录 + +#if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1) +static const char *_skipwhite(const char *q); +static double _strtod(const char *str, char **end); // https://gitee.com/mirrors_mattn/strtod/blob/master/strtod.c +#endif + +/** + * @brief 查找由 sh_register_key() 注册的完全匹配的节点 + * + * @param code 热键编码,@ref @c sh_vt100.h + * @return const sh_key_t* + */ +static const sh_key_t *_find_key(sh_t *sh_hdl, const char *code) +{ + const sh_key_reg_t *test_reg_key; + int match_count = 0; + SYS_PSLIST_FOR_EACH_CONTAINER(&sh_hdl->key_list, test_reg_key, node) + { + const sh_key_t *test_key = test_reg_key->key; + for (int i = 0; *(int *)&test_key[i] != 0; i++) + { + if (strcmp(test_key[i].code, code) == 0) + { + return &test_key[i]; + } + if (strncmp(test_key[i].code, code, sh_hdl->key_stored) == 0) + { + match_count++; + } + } + } + + if (match_count == 0) + { + sh_hdl->key_stored = 0; + sh_hdl->key_str[sh_hdl->key_stored] = '\0'; + } + return NULL; +} + +/** + * @brief 查找由 sh_register_cmd() sh_register_cmd_hide() sh_register_key_cmd() 注册的完全匹配的单元命令节点。 + * 如果当前为模块模式,由优先在模块中查找。 + * + * @param dest_reg_struct[out] 保存根命令结构的定义地址 + * @param sub_cmd NULL -- 从根命令中历遍查找; ! NULL 从已知的父命令节点中查找 + * @param cmd 单元命令字符串 + * @return const sh_cmd_t* NULL -- 没有记录这个命令; ! NULL 找到的命令节点 + */ +static const sh_cmd_t *_find_cmd(sh_t *sh_hdl, const sh_cmd_reg_t **dest_reg_struct, const sh_cmd_t *sub_cmd, const char *cmd) +{ + const sh_cmd_t *test_cmd; + if (sh_hdl->select_cmd && sub_cmd == NULL) + { + test_cmd = sh_hdl->select_cmd->sub_fn.sub_cmd; + for (int i = 0; test_cmd[i].cmd != NULL; i++) + { + if ((test_cmd->cmd_func != NULL || test_cmd->sub_fn.sub_cmd != NULL) && + strcmp(test_cmd[i].cmd, cmd) == 0) + { + if (dest_reg_struct) + { + *dest_reg_struct = sh_hdl->select_reg_struct; + } + return &test_cmd[i]; + } + } + } + + if (sub_cmd == NULL) + { + if (dest_reg_struct) + { + *dest_reg_struct = NULL; + } + return _find_root_cmd(sh_hdl, dest_reg_struct, cmd); + } + else + { + test_cmd = sub_cmd; + for (int i = 0; test_cmd[i].cmd != NULL; i++) + { + if ((test_cmd->cmd_func != NULL || test_cmd->sub_fn.sub_cmd != NULL) && + strcmp(test_cmd[i].cmd, cmd) == 0) + { + return &test_cmd[i]; + } + } + return NULL; + } +} + +/** + * @brief 仅在根链表中查找由 sh_register_cmd() sh_register_cmd_hide() sh_register_key_cmd() 注册的完全匹配的单元命令节点。 + * + * @param dest_reg_struct[out] 保存根命令结构的定义地址 + * @param cmd 单元命令字符串 + * @return const sh_cmd_t* NULL -- 没有记录这个命令; ! NULL 找到的命令节点 + */ +static const sh_cmd_t *_find_root_cmd(sh_t *sh_hdl, const sh_cmd_reg_t **dest_reg_struct, const char *cmd) +{ + const sh_cmd_reg_t *sh_reg; + sys_pslist_t *cmd_list[] = { + &s_cmd_list_hide, + &s_cmd_list, + NULL, + }; + if (sh_hdl) + { + cmd_list[sizeof(cmd_list) / sizeof(cmd_list[0]) - 1] = &sh_hdl->cmd_list; + } + for (int i = 0; i < sizeof(cmd_list) / sizeof(cmd_list[0]); i++) + { + sys_pslist_t *test_list = cmd_list[i]; + if (test_list != NULL) + { + SYS_PSLIST_FOR_EACH_CONTAINER(test_list, sh_reg, node) + { + const sh_cmd_t *reg_cmd = sh_reg->cmd; + for (int i = 0; *(int *)®_cmd[i] != 0; i++) + { + const sh_cmd_t *test_cmd = ®_cmd[i]; + if ((test_cmd->cmd_func != NULL || test_cmd->sub_fn.sub_cmd != NULL) && + strcmp(test_cmd->cmd, cmd) == 0) + { + if (dest_reg_struct) + { + *dest_reg_struct = sh_reg; + } + return test_cmd; + } + } + } + } + } + return NULL; +} + +/** + * @brief 执行注册根命令 + * + * @param list 指定插入的链 @ref @c sh_register_cmd(),sh_register_cmd_hide(),sh_register_key_cmd() + * @param sh_reg 根命令数据 + * @return int 0 -- 成功; ! 0 -- 命令中包含空格或命令已存在 + */ +static int _register_cmd(sh_t *sh_hdl, sys_pslist_t *list, const sh_cmd_reg_t *sh_reg) +{ + const sh_cmd_t *reg_cmd = sh_reg->cmd; + for (int i = 0; *(int *)®_cmd[i] != 0; i++) + { + const sh_cmd_t *test_cmd = ®_cmd[i]; + char err_cmd[0x100]; + + if (_register_cmd_check_fn(err_cmd, test_cmd)) + { + SYS_LOG_WRN("sub_fn: pointer type mismatch in conditional expression!\r\n" + "Defined in file: %s:%d\r\n" + "Command:%s", + sh_reg->file, + sh_reg->line, + err_cmd); + return -1; + } + + if (strchr(test_cmd->cmd, ' ')) + { + if (sh_hdl && sh_hdl->disable_echo == 0) + SYS_LOG_WRN("Command cannot contain spaces: '%s'\r\n", test_cmd->cmd); + return -1; + } + + if (_find_root_cmd(sh_hdl, NULL, test_cmd->cmd)) + { + if (sh_hdl && sh_hdl->disable_echo == 0) + SYS_LOG_WRN("Repeat command: '%s'\r\n", test_cmd->cmd); + return -1; + } + } + + sys_pslist_append(list, &sh_reg->node); + + return 0; +} + +/** + * @brief _register_cmd() 专用,递归遍历一个根命令及所有子命令,确认 sh_cmd_t::fn 的类型是否符合规则 + * + * @param err_cmd[out] 输出记录错误的命令路径 + * @param cmd 命令 + * @retval 0 所有命令的 sh_cmd_t::fn 类型符合规则 + * @retval -1 有错误的 sh_cmd_t::fn 类型 + */ +static int _register_cmd_check_fn(char *err_cmd, const sh_cmd_t *cmd) +{ + for (int i = 0; *(int *)&cmd[i] != 0; i++) + { + const sh_cmd_t *test_cmd = &cmd[i]; + strcpy(err_cmd, " "); + strcpy(&err_cmd[1], test_cmd->cmd); + + if (test_cmd->cmd_func == NULL) // 需要子命令 + { + if (test_cmd->fn_type == _SH_SUB_TYPE_SUB) + { + if (_register_cmd_check_fn(&err_cmd[strlen(err_cmd)], test_cmd->sub_fn.sub_cmd) != 0) // 递归查检子命令 + { + return -1; + } + } + else + { + if (test_cmd->fn_type != _SH_SUB_TYPE_VOID || test_cmd->sub_fn.nul != NULL) + { + return -1; + } + } + } + else // 是最终命令 + { + if (test_cmd->fn_type != _SH_SUB_TYPE_CPFN) + { + if (test_cmd->fn_type != _SH_SUB_TYPE_VOID || test_cmd->sub_fn.nul != NULL) + { + return -1; + } + } + } + } + + err_cmd[0] = '\0'; + return 0; +} + +/** + * @brief 保存一个字符到热键缓存,并尝试查找对应的热键功能函数。如果热代码错误缓存会被自动清除。 + * + * @param c 字符流 + */ +static void _find_and_run_key(sh_t *sh_hdl, char c) +{ + if (sh_hdl->key_stored < sizeof(sh_hdl->key_str) - 1) + { + sh_hdl->key_str[sh_hdl->key_stored++] = c; + sh_hdl->key_str[sh_hdl->key_stored] = '\0'; + + const sh_key_t *match_key = _find_key(sh_hdl, sh_hdl->key_str); + if (match_key) + { + sh_hdl->key_stored = 0; + sh_hdl->key_str[sh_hdl->key_stored] = '\0'; + match_key->key_func(sh_hdl); + return; + } + } + + if (sh_hdl->key_stored >= sizeof(sh_hdl->key_str)) + { + sh_hdl->key_stored = 0; + sh_hdl->key_str[sh_hdl->key_stored] = '\0'; + } +} + +/** + * @brief 在当前的光标(指针)前插入并记录一个字符串。这个动作将同步到终端的命令行显示 + * + * @param str 字符串流 + */ +static void _insert_str(sh_t *sh_hdl, const char *str) +{ + int len = strlen(str); + if (len) + { + int remain = sizeof(sh_hdl->cmd_line) - (sh_hdl->cmd_buf - sh_hdl->cmd_line) - sh_hdl->cmd_stored - 1; + if (len > remain) + { + len = remain; + } + _apply_history(sh_hdl); + if (sh_hdl->cmd_stored < remain) + { + if (sh_hdl->port.insert_str) /* 设置终端插入一个字符 */ + { + sh_hdl->port.insert_str(sh_hdl, str); + } + + for (int i = sh_hdl->cmd_stored - 1 + len; i > sh_hdl->cmd_input; i--) /* 使光标之后的数据往后移动 len 个字节 */ + sh_hdl->cmd_buf[i] = sh_hdl->cmd_buf[i - len]; + + for (int i = 0; i < len; i++) + sh_hdl->cmd_buf[sh_hdl->cmd_input + i] = str[i]; + + sh_hdl->cmd_input += len; + sh_hdl->cmd_stored += len; + sh_hdl->sync_cursor += len; + + sh_hdl->cmd_buf[sh_hdl->cmd_stored] = '\0'; + + /* 启用刷新命令行模式 */ + if (sh_hdl->port.clear_line) + { + sh_refresh_line(sh_hdl); + } + } + } +} + +/** + * @brief 以空格为分隔符整理出参数。 + * 从 src 中取得结果,输出到 int *argc, char **argv[], char *dest + * + * @param argc[out] 保存参数个数 + * @param argv[out] 保存每个参数的地址(指向dest) + * @param dest[out] 分离出来的参数字符串的副本,注意参数的使用期间需要保留。 + * @param src 已记录的命令行数据 + */ +static void _read_param(int *argc, const char *argv[CONFIG_SH_MAX_PARAM], char *dest, const char *src) +{ + *argc = 0; + while (*argc < CONFIG_SH_MAX_PARAM) + { + int len = 0; + + /* 以空格为分隔符,分段提取命令到 dest */ + while (*src == ' ') + { + ++src; + } + + if (*src == '"') /* 使用双引号分隔的字符串 */ + { + ++src; + while (1) + { + dest[len] = src[len]; + if (dest[len] == '"') + { + ++src; + dest[len] = '\0'; + } + if (dest[len] == '\0') + { + break; + } + + if (dest[len] == '\\') + { + ++src; + switch (src[len]) + { + case '\0': + --src; + break; + case 'b': + dest[len] = '\b'; + break; + case 'f': + dest[len] = '\f'; + break; + case 'n': + dest[len] = '\n'; + break; + case 'r': + dest[len] = '\r'; + break; + case 't': + dest[len] = '\t'; + break; + case '\"': + case '\\': + case '/': + dest[len] = src[len]; + break; + + default: + dest[len] = src[len]; + break; + } + } + + ++len; + } + } + else + { + char split_char; + if (*src == '\'') /* 使用单引号分隔的字符串 */ + { + split_char = '\''; + ++src; + } + else /* 使用空格分隔的字符串 */ + { + split_char = ' '; + } + + while (1) + { + dest[len] = src[len]; + if (dest[len] == split_char) + { + ++src; + dest[len] = '\0'; + } + if (dest[len] == '\0') + { + break; + } + ++len; + } + } + + if (len) // dest 数据有效 + { + argv[*argc] = dest; + ++(*argc); + + dest = &dest[len + 1]; + src = &src[len]; + } + else + { + break; + } + } +} + +/** + * @brief 尝试保存到历史记录中 + * + * @param src 被保存的字符串(注意恢复空格) + */ +static void _store_history(sh_t *sh_hdl, const char *src) +{ + if (sh_hdl->disable_history == 0) + { + int len = strlen(src) + 1; + int history_valid_num; + + if (*src == '\0') + { + return; + } + if (len > sh_hdl->history_size) + { + return; + } + + if (strcmp(src, sh_hdl->cmd_history) != 0) + { + /* 更新历史信息 */ + for (int i = sh_hdl->history_index_num - 1; i > 0; i--) + sh_hdl->history_index[i] = sh_hdl->history_index[i - 1] + len; + + /* 分析并刷新有效记录数 */ + for (history_valid_num = 0; history_valid_num < sh_hdl->history_index_num - 1; history_valid_num++) + { + int test_index = sh_hdl->history_index[history_valid_num]; + if (test_index > sh_hdl->history_size) + { + --history_valid_num; + break; + } + if (sh_hdl->history_index[history_valid_num + 1] <= test_index) + { + break; + } + } + sh_hdl->history_valid_num = history_valid_num; + + /* 使历史数据往后移动 len 字节 */ + for (int i = sh_hdl->history_size - 1; i >= len; i--) + sh_hdl->cmd_history[i] = sh_hdl->cmd_history[i - len]; + + /* 复制新数据 */ + strcpy(sh_hdl->cmd_history, src); + } + } +} + +/** + * @brief 当在回翻历史记录时,一旦对记录修改则立即将当前记录复制到命令行的缓存中 + * + * @param sh_hdl + */ +static void _apply_history(sh_t *sh_hdl) +{ + if (sh_hdl->history_show) + { + char *str = &sh_hdl->cmd_history[sh_hdl->history_index[sh_hdl->history_show - 1]]; + strcpy(sh_hdl->cmd_buf, str); + sh_hdl->cmd_stored = strlen(str); + sh_hdl->history_show = 0; + } + + sh_hdl->tab_press_count = 0; + sh_hdl->cp_resource_flag = 0; +} + +/** + * @brief 根据当前显示内容(包含回翻的历史记录),获取最大光标位置(不含提示符和模块提示) + * + * @return unsigned 结尾位置 0..n + */ +static unsigned _get_end_pos(sh_t *sh_hdl) +{ + if (sh_hdl->history_show) + return strlen(&sh_hdl->cmd_history[sh_hdl->history_index[sh_hdl->history_show - 1]]); + else + return sh_hdl->cmd_stored; +} + +/** + * @brief 保存擦除的数据。 + * 保存最后一次因 KEY_CTR_U KEY_CTR_K 或 KEY_CTR_W 操作而被擦除的数据, + * 用于 KEY_CTR_Y 还原的数据。如果长度不足则原来已保存的数据被清空 + * + * @param line 当前完整的命令行数据 + * @param pos 被保存的位置 0..n + * @param n 保存的长度 + */ +static void _store_cmd_bak(sh_t *sh_hdl, const char *line, int pos, int n) +{ + if (sh_hdl->cmd_bank && sh_hdl->bank_size) + { + if (n > 0) + { + if (n < sh_hdl->bank_size - 1) + { + for (int i = 0; i < n; i++) + { + sh_hdl->cmd_bank[i] = line[pos + i]; + } + sh_hdl->cmd_bank[n] = '\0'; + } + else + { + sh_hdl->cmd_bank[0] = '\0'; + } + } + } +} + +/** + * @brief 使剩余的字符串指针指向空字符串 + * + * @param argc 参数数量 + * @param argv 参数字符串指针 + */ +static void _clear_argv(int argc, const char *argv[]) +{ + for (int i = argc; i < CONFIG_SH_MAX_PARAM; i++) + { + argv[i] = NULL; + } +} + +/** + * @brief 获取当前提示符(不含模块提示符) + * + * @param sh_hdl + * @return const char* + */ +static const char *sh_ctrl_get_prompt(sh_t *sh_hdl) +{ + if (sh_hdl->prompt) + return sh_hdl->prompt; + else + return ">>> "; +} + +/** + * @brief 获取当前已输入(或回显的历史)的命令行数据 + * + * @return const char* 指向历史记录或命令行缓存 + */ +static const char *sh_ctrl_get_line(sh_t *sh_hdl) +{ + if (sh_hdl->history_show) + { + return &sh_hdl->cmd_history[sh_hdl->history_index[sh_hdl->history_show - 1]]; + } + else + { + return sh_hdl->cmd_buf; + } +} + +/** + * @brief 注册该对象的硬件抽象接口 + * + * @param port 硬件接口的结构体(不要求静态类型) + * @return int + */ +int sh_register_port(sh_t *sh_hdl, const sh_port_t *port) +{ + sh_hdl->port = *port; + return -!port; +} + +/** + * @brief 注册根命令 + * @ref @c SH_REGISTER_CMD + * + * @param sh_reg 根命令数据 + * @return int 0 -- 成功; ! 0 -- 命令中包含空格或命令已存在 + */ +int sh_register_cmd(const sh_cmd_reg_t *sh_reg) +{ + return _register_cmd(NULL, &s_cmd_list, sh_reg); +} + +/** + * @brief 注册根命令。这些根命令不会在补全和帮助中出现,但可被执行 + * @ref @c SH_REGISTER_CMD + * + * @param sh_reg 根命令数据 + * @return int 0 -- 成功; ! 0 -- 命令中包含空格或命令已存在 + */ +int sh_register_cmd_hide(const sh_cmd_reg_t *sh_reg) +{ + return _register_cmd(NULL, &s_cmd_list_hide, sh_reg); +} + +/** + * @brief 取消注册一个根命令 + * + * @param sh_reg 根命令数据 + * @return int 0 -- 成功; ! 0 -- 命令中包含空格或命令已存在 + */ +int sh_unregister_cmd(const sh_cmd_reg_t *sh_reg) +{ + bool ret = (sh_reg == NULL); + ret = (ret != false ? ret : sys_pslist_find_and_remove(&s_cmd_list, &sh_reg->node)); + ret = (ret != false ? ret : sys_pslist_find_and_remove(&s_cmd_list_hide, &sh_reg->node)); + return -!ret; +} + +/** + * @brief 注册一组热键 + * 适用于动态加载/卸载 + * + * @param sh_key 热键数据 + * @return int 0 -- 成功; ! 0 -- 热键中包含空格或热键已存在 + */ +int sh_register_key(sh_t *sh_hdl, const sh_key_reg_t *sh_key) +{ + if (_find_key(sh_hdl, sh_key->key->code)) + { + if (sh_hdl->disable_echo == 0) + SYS_LOG_WRN("Repeat key code: '%s'\r\n", sh_key->key->code); + return -1; + } + + sys_pslist_append(&sh_hdl->key_list, &sh_key->node); + + return 0; +} + +/** + * @brief 注册仅属于对应的终端可见的根命令。 + * @ref @c SH_REGISTER_CMD + * 适用于动态加载/卸载 + * + * @param sh_key 根命令数据 + * @return int 0 -- 成功; ! 0 -- 命令中包含空格或命令已存在 + */ +int sh_register_key_cmd(sh_t *sh_hdl, const sh_cmd_reg_t *sh_reg) +{ + return _register_cmd(sh_hdl, &sh_hdl->cmd_list, sh_reg); +} + +/** + * @brief 取消注册一组热键 + * 适用于动态加载/卸载 + * + * @param sh_key 热键数据 + * @return int 0 -- 成功; ! 0 -- 数据不存在 + */ +int sh_unregister_key(sh_t *sh_hdl, const sh_key_reg_t *sh_key) +{ + return -!sys_pslist_find_and_remove(&sh_hdl->key_list, &sh_key->node); +} + +/** + * @brief 取消注册仅属于对应的终端可见的根命令 + * 适用于动态加载/卸载 + * + * @param sh_key 根命令数据 + * @return int 0 -- 成功; ! 0 -- 数据不存在 + */ +int sh_unregister_key_cmd(sh_t *sh_hdl, const sh_cmd_reg_t *sh_reg) +{ + return -!sys_pslist_find_and_remove(&sh_hdl->cmd_list, &sh_reg->node); +} + +/** + * @brief 配置用于记录历史的内存。sh_init_vt100() 默认不会配置此项。 + * @ref @c sh_vt100.c@_install_shell_vt100() + * + * @param mem 内存地址 + * @param size 内存大小(字节) + */ +void sh_config_history_mem(sh_t *sh_hdl, void *mem, int size) +{ + if (size > 10) + { + int rem = (sizeof(*sh_hdl->history_index) - (unsigned)mem % sizeof(*sh_hdl->history_index)) % sizeof(*sh_hdl->history_index); + + size -= rem; + sh_hdl->history_index = (void *)((unsigned)mem + rem); + + int history_index_num = size / 10 + 1; + if (history_index_num > (1 << sizeof(*sh_hdl->history_index) * 8) - 1) + { + history_index_num = (1 << sizeof(*sh_hdl->history_index) * 8) - 1; + } + sh_hdl->history_index_num = history_index_num; + + sh_hdl->cmd_history = (char *)&sh_hdl->history_index[sh_hdl->history_index_num]; + + sh_hdl->history_size = size - sizeof(*sh_hdl->history_index) * sh_hdl->history_index_num; + + sh_hdl->history_valid_num = 0; + sh_hdl->history_show = 0; + } +} + +/** + * @brief 配置用于保存 sh_ctrl_del_word(), sh_ctrl_del_left(), sh_ctrl_del_right() 删除的内容。sh_init_vt100() 默认不会配置此项。 + * @ref @c sh_vt100.c@_install_shell_vt100() + * + * @param mem 内存地址 + * @param size 内存大小(字节) + */ +void sh_config_backup_mem(sh_t *sh_hdl, void *mem, int size) +{ + sh_hdl->cmd_bank = mem; + sh_hdl->bank_size = size; +} + +/** + * @brief 输入字符流到内部缓存中并解析和执行。 + * 是整个数据解析流程的起点。 + * + * @param sh_hdl 由 sh_init_vt100() 初始化的对象 + * @param c 字符流 + */ +void sh_putc(sh_t *sh_hdl, char c) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + if (sh_hdl->exec_flag == 0) + { + if (c < ' ' || c >= 0x7F || sh_hdl->key_stored > 0) + { + _find_and_run_key(sh_hdl, c); + return; + } + else + { + char str[2] = {c, '\0'}; + _insert_str(sh_hdl, str); + } + } +} + +/** + * @brief 同 sh_putc() + * + * @param str 字符串流 + */ +void sh_putstr(sh_t *sh_hdl, const char *str) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + char buf[sizeof(sh_hdl->cmd_line)]; + const char *from = str; + char *to = buf; + for (int i = 0; i < sizeof(buf);) + { + char c = from[i]; + if (c < ' ' || c >= 0x7F || sh_hdl->key_stored > 0 || i >= sizeof(buf) - 1) + { + to[i++] = '\0'; + _insert_str(sh_hdl, to); + + if (c == '\0') + { + break; + } + else + { + sh_putc(sh_hdl, c); + from = &from[i]; + to = &to[i]; + i = 0; + } + } + else + { + to[i++] = c; + } + } +} + +/** + * @brief 同 sh_putstr() 但不会回显 + * + * @param str 字符串流 + */ +void sh_putstr_quiet(sh_t *sh_hdl, const char *str) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + bool disable_echo = sh_hdl->disable_echo; + bool disable_history = sh_hdl->disable_history; + sh_port_t port_bak = sh_hdl->port; + + sh_hdl->disable_echo = true; + sh_hdl->disable_history = true; + sh_hdl->port.insert_str = NULL; + sh_hdl->port.set_cursor_hoffset = NULL; + sh_hdl->port.clear_line = NULL; + + sh_putstr(sh_hdl, str); + + sh_hdl->port = port_bak; + sh_hdl->disable_history = disable_history; + sh_hdl->disable_echo = disable_echo; +} + +/** + * @brief 设置提示符。 + * + * @param prompt 提示符。要求为静态数据。 + */ +void sh_set_prompt(sh_t *sh_hdl, const char *prompt) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + sh_hdl->prompt = prompt; +} + +/** + * @brief 清除命令接收缓存(终端显示不会被更新) + * + */ +void sh_reset_line(sh_t *sh_hdl) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + sh_hdl->cmd_input = 0; + sh_hdl->sync_cursor = 0; + sh_hdl->cmd_stored = 0; + sh_hdl->cmd_buf[sh_hdl->cmd_stored] = '\0'; + + sh_hdl->key_stored = 0; + sh_hdl->key_str[sh_hdl->key_stored] = '\0'; + + sh_hdl->tab_press_count = 0; + sh_hdl->cp_resource_flag = 0; +} + +/** + * @brief 回显到终端 + * + */ +int sh_echo(sh_t *sh_hdl, const char *fmt, ...) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + if (sh_hdl != NULL && sh_hdl->disable_echo == 0) + { + if (sh_hdl->port.vprint) + { + va_list arg; + va_start(arg, fmt); + int ret = sh_hdl->port.vprint(fmt, arg); + va_end(arg); + return ret; + } + } + + return 0; +} + +#if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1) +/** + * @brief _strtod() 用,跳过空格 + * + * @param q 来源字符串 + * @return const char* 字符串首个非空格的地址 + */ +static const char *_skipwhite(const char *q) +{ + const char *p = q; + while (*p == ' ') + { + ++p; + } + return p; +} +/** + * @brief 用于替换 中的 strtod() + */ +static double _strtod(const char *str, char **end) +{ + double d = 0.0; + int sign; + int n = 0; + const char *p, *a; + + a = p = str; + p = _skipwhite(p); + + /* decimal part */ + sign = 1; + if (*p == '-') + { + sign = -1; + ++p; + } + else if (*p == '+') + { + ++p; + } + if (_IS_DIGIT(*p)) + { + d = (double)(*p++ - '0'); + while (*p && _IS_DIGIT(*p)) + { + d = d * 10.0 + (double)(*p - '0'); + ++p; + ++n; + } + a = p; + } + else if (*p != '.') + { + goto done; + } + d *= sign; + + /* fraction part */ + if (*p == '.') + { + double f = 0.0; + double base = 0.1; + ++p; + + if (_IS_DIGIT(*p)) + { + while (*p && _IS_DIGIT(*p)) + { + f += base * (*p - '0'); + base /= 10.0; + ++p; + ++n; + } + } + d += f * sign; + a = p; + } + + /* exponential part */ + if ((*p == 'E') || (*p == 'e')) + { + int e = 0; + ++p; + + sign = 1; + if (*p == '-') + { + sign = -1; + ++p; + } + else if (*p == '+') + { + ++p; + } + + if (_IS_DIGIT(*p)) + { + while (*p == '0') + { + ++p; + } + + if (*p == '\0') + { + --p; + } + e = (int)(*p++ - '0'); + while (*p && _IS_DIGIT(*p)) + { + e = e * 10 + (int)(*p - '0'); + ++p; + } + e *= sign; + } + else if (!_IS_DIGIT(*(a - 1))) + { + a = str; + goto done; + } + else if (*p == 0) + { + goto done; + } + + if (d == 2.2250738585072011 && e == -308) + { + d = 0.0; + a = p; + errno = ERANGE; + goto done; + } + if (d == 2.2250738585072012 && e <= -308) + { + d *= 1.0e-308; + a = p; + goto done; + } + d *= pow(10.0, (double)e); + a = p; + } + else if (p > str && !_IS_DIGIT(*(p - 1))) + { + a = str; + goto done; + } + +done: + if (end) + { + *end = (char *)a; + } + return d; +} +#endif /* #if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1) */ + +/** + * @brief 参数工具:解析字符串对表示的值,包括 整数/浮点/字符串 + * + * @param str 目标解析字符串 + * @return sh_parse_t 输出解析结果 + */ +sh_parse_t sh_parse_value(const char *str) +{ +#define _ATTR_OCT (1 << 0) +#define _ATTR_DEC (1 << 1) +#define _ATTR_HEX (1 << 2) +#define _ATTR_FLOAT (1 << 3) + + unsigned attr = 0; + int dot = 0; // '.' 出现次数 + sh_parse_t ret = { + .type = _PARSE_TYPE_STRING, + .value.val_string = str, + .endptr = (char *)str, + }; + + if (str == NULL || *str == '\0') + { + return ret; + } + + ret.endptr = &((char *)str)[strlen(str)]; + + /* 根据首个字符,初步确定属性值为 attr */ + do + { + if (*str == '+' || *str == '-') + { + str = &str[1]; + } + + if (*str == '0') + { + if (str[1] == 'x' || str[1] == 'X') + { + if ((str[2] >= '0' && str[2] <= '9') || (str[2] >= 'a' && str[2] <= 'f') || (str[2] >= 'A' && str[2] <= 'F')) + { + attr = _ATTR_HEX; + str = &str[3]; + } + else + { + return ret; + } + } + else + { + attr = _ATTR_OCT | _ATTR_FLOAT; + } + } + else if (*str == '.') + { + attr = _ATTR_FLOAT; + str = &str[1]; + dot++; + if (*str == '\0') + { + return ret; + } + } + else if (*str >= '1' && *str <= '9') + { + attr = _ATTR_DEC | _ATTR_FLOAT; + } + + } while (0); + + /* 按照 attr 给出的可能性,按具体特性单独进行格式检测,若格式正确则进行数值解析并返回 */ + do + { + if (attr & _ATTR_OCT) /* 八进制 */ + { + while (*str >= '0' && *str <= '7') + { + str = &str[1]; + } + + if (*str == '\0') + { + if (*ret.value.val_string == '-') + { + ret.type = _PARSE_TYPE_INTEGER; + ret.value.val_integer = strtol(ret.value.val_string, &ret.endptr, 8); + } + else + { + ret.type = _PARSE_TYPE_UNSIGNED; + ret.value.val_unsigned = strtoul(ret.value.val_string, &ret.endptr, 8); + } + return ret; + } + } + + if (attr & _ATTR_DEC) /* 十进制 */ + { + while (*str >= '0' && *str <= '9') + { + str = &str[1]; + } + + if (*str == '\0') + { + if (*ret.value.val_string == '-') + { + ret.type = _PARSE_TYPE_INTEGER; + ret.value.val_integer = strtol(ret.value.val_string, &ret.endptr, 10); + } + else + { + ret.type = _PARSE_TYPE_UNSIGNED; + ret.value.val_unsigned = strtoul(ret.value.val_string, &ret.endptr, 10); + } + return ret; + } + } + + if (attr & _ATTR_HEX) /* 十六进制 */ + { + while ((*str >= '0' && *str <= '9') || (*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F')) + { + str = &str[1]; + } + + if (*str == '\0') + { + if (*ret.value.val_string == '-') + { + ret.type = _PARSE_TYPE_INTEGER; + ret.value.val_integer = strtol(ret.value.val_string, &ret.endptr, 16); + } + else + { + ret.type = _PARSE_TYPE_UNSIGNED; + ret.value.val_unsigned = strtoul(ret.value.val_string, &ret.endptr, 16); + } + return ret; + } + } + +#if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1) + if (attr & _ATTR_FLOAT) /* 浮点数 */ + { + int e = 0; // 'e' 出现次数 + + while (dot < 2 && e < 2) + { + while (*str >= '0' && *str <= '9') + { + str = &str[1]; + } + + if (*str == '\0') + { + if (attr & _ATTR_OCT) + { + if (dot + e == 0) + { + return ret; /* 如 08、09 */ + } + } + ret.type = _PARSE_TYPE_FLOAT; + ret.value.val_float = _strtod(ret.value.val_string, &ret.endptr); + return ret; + } + + switch (*str) + { + case '.': + { + dot++; + str = &str[1]; + break; + } + case 'e': + { + e++; + str = &str[1]; + if (*str == '\0') + { + return ret; + } + else if (*str == '+' || *str == '-') + { + str = &str[1]; + if (*str == '\0') + { + return ret; + } + } + break; + } + default: + return ret; + } + } + } +#endif /* #if defined(CONFIG_SH_USE_STRTOD) && (CONFIG_SH_USE_STRTOD == 1) */ + + } while (0); + + return ret; +} + +/** + * @brief 参数工具:以空格为分隔符,合并多个参数 + * + * @param dst 用于保存合并结果的内存地址 + * @param len 用于保存合并结果的内存长度 + * @param argc 参数数量 + * @param argv 参数字符串指针 + * @return int 合并后的长度(不包含结束字符) + */ +int sh_merge_param(char *dst, int len, int argc, const char *argv[]) +{ + int ret = 0; + if (len) + { + char *p = dst; + for (int i = 0; i < argc; i++) + { + size_t n = strlen(argv[i]) + 1; + + if (n > len) + n = len; + + strncpy(p, argv[i], n); + + if (i + 1 < argc) + p[n - 1] = ' '; + else + p[n - 1] = '\0'; + + p = &p[n]; + len -= n; + ret += n; + if (len == 0) + { + break; + } + } + --ret; + } + return ret; +} + +/** + * @brief 获取上一次命令的执行结果 + * + * @param sh_hdl 由 sh_init_vt100() 初始化的对象 + * @retval 1 command not found + * @retval 2 command not complete + * @retval 其他值:由被执行的命令的返回值 + */ +int sh_get_cmd_result(sh_t *sh_hdl) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + return sh_hdl->cmd_return; +} + +/** + * @brief 执行刷一次当前命令行显示 + * 注意仅在 sh_port_t::clear_line 被设置时有效 + */ +void sh_refresh_line(sh_t *sh_hdl) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + if (sh_hdl->port.clear_line) + { + uint16_t input_pos = sh_hdl->cmd_input; + sh_hdl->port.clear_line(sh_hdl); + sh_ctrl_print_cmd_line(sh_hdl); + sh_ctrl_set_input_pos(sh_hdl, input_pos); + } +} + +/** + * @brief 自动补全功能用:初始化补全命令信息。 + * + * @param cmd 待解析的实例命令字符串 + * @return sh_cp_info_t 输出初始的基本补全信息 + */ +static sh_cp_info_t _completion_init(const char *cmd) +{ + sh_cp_info_t info = { + .arg_str = cmd, // 待分析的参数 + .arg_len = strlen(cmd), // 待解析命令长度 + .match_num = 0, // 可打印的列表的条目数 + .list_algin = 0, // 确定格式中 %-*s 的 * 的值 + }; + info.match_str[0] = '\0'; // 保存相同的字符部分 + return info; +} + +/** + * @brief 自动补全功能用:每被执行一次都被更新一次补全信息或直接打印可选项。 + * + * @param info[out] 由 _completion_init() 初始化的补全信息指针,并被实时记录刷新。 + * @param sub_cmd 包含所有可选项的父节点。 + * @param print_match false -- 只刷新 info; true -- 只打印匹配的备选项。 + * @param max_lines print_match 为 true 时,可打印的选项数超过这个设定时,仅列出匹配的命令(不打印帮助信息)。 + * @param flag_parent 仅在内部的命令 select 中置 1 使用,表示只包含父命令的可选项。 + */ +static void _completion_update(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cmd_t *sub_cmd, bool print_match, int max_lines, int flag_parent) +{ + int list_algin = 100 / _calc_list_algin(info); + + if ((sub_cmd->cmd_func != NULL || sub_cmd->sub_fn.sub_cmd != NULL) && + (info->arg_len == 0 || strncmp(sub_cmd->cmd, info->arg_str, info->arg_len) == 0)) + { + if (flag_parent && sub_cmd->cmd_func) // 只按父命令更新 + { + return; + } + + if (print_match == false) // 更新补全信息 + { + int test_len = strlen(sub_cmd->cmd); + if (info->list_algin < test_len) + info->list_algin = test_len; + + if (info->match_num) + { + for (int i = 0; info->match_str[i] != '\0'; i++) + { + if (info->match_str[i] != sub_cmd->cmd[i]) + { + info->match_str[i] = '\0'; + break; + } + } + } + else + { + strcpy(info->match_str, sub_cmd->cmd); + } + + info->match_num++; + } + else // 打印匹配的命令 + { + info->print_count++; + + const char *color_begin = ""; + const char *color_end = ""; + if (sub_cmd->cmd_func) + { + if (sub_cmd->sub_fn.cp_fn) // 有参数 + { + color_begin = _COLOR_C; + color_end = _COLOR_END; + } + } + else // 有子命令 + { + color_begin = _COLOR_P; + color_end = _COLOR_END; + } + sh_echo(sh_hdl, "%s%-*s%s", color_begin, _calc_list_algin(info), sub_cmd->cmd, color_end); + + if (info->match_num > max_lines) // 超过 max_lines 个匹配项时,不再显示帮助信息 + { + if (info->print_count >= info->match_num || info->print_count % list_algin == 0) + { + sh_echo(sh_hdl, "\r\n"); + } + } + else + { + if (sub_cmd->help && *sub_cmd->help != '\0') + { + sh_echo(sh_hdl, "-- %s\r\n", sub_cmd->help); + } + else + { + sh_echo(sh_hdl, "\r\n"); + } + } + } + } +} + +/** + * @brief 自动补全功能用:执行一次自动补全命令或打印备选命令的功能 + * + * @param info[out] 由 _completion_init() 初始化的补全信息指针,并被实时记录刷新。 + * @param cmd_info 包含所有备选项的父节点的原始数据结构。 + * @param print_match false -- 仅尝试自动补全命令; true -- 只打印匹配的备选项。 + * @param flag_parent 仅在内部的命令 select 中置 1 使用,表示只包含父命令的备选命令。 + * @return true 当 print_match 为 false (仅尝试自动补全命令)时,成功执行了一次补全命令的动作 + * @return false 当 print_match 为 true (只打印匹配的备选项)或无法补全命令 + */ +static bool _do_completion_cmd(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cmd_info_t *cmd_info, bool print_match, int flag_parent) +{ + char new_cmd[sizeof(sh_hdl->cmd_line)]; + const sh_cmd_t *sub_cmd; + int err_status; + + if (print_match) + { + if (info->match_num == 0) + { + return false; + } + + if (sh_hdl->cp_operate != SH_CP_OP_PEND) + { + sh_hdl->cp_operate = SH_CP_OP_PEND; + sh_echo(sh_hdl, "\r\n"); + } + info->print_count = 0; + } + else + { + info->match_num = 0; + } + + if (cmd_info) + { + sub_cmd = cmd_info->match_cmd; + err_status = cmd_info->err_status; + } + else + { + sub_cmd = sh_hdl->select_cmd; + err_status = !_SH_CMD_STATUS_Incomplete; + } + if (sub_cmd == NULL) + { + sub_cmd = sh_hdl->select_cmd; + } + + while (1) + { + if (sub_cmd == NULL) // 历遍根命令链 + { + const sh_cmd_reg_t *sh_reg; + sys_pslist_t *cmd_list[] = { + &s_cmd_list, + &sh_hdl->cmd_list, + }; + for (int i = 0; i < sizeof(cmd_list) / sizeof(cmd_list[0]); i++) + { + sys_pslist_t *test_list = cmd_list[i]; + SYS_PSLIST_FOR_EACH_CONTAINER(test_list, sh_reg, node) + { + const sh_cmd_t *reg_cmd = sh_reg->cmd; + for (int i = 0; *(int *)®_cmd[i] != 0; i++) + { + sh_cmd_t new_data = reg_cmd[i]; + new_data.cmd = new_cmd; + strcpy(new_cmd, reg_cmd[i].cmd); + strcat(new_cmd, " "); + _completion_update(sh_hdl, info, &new_data, print_match, 30, flag_parent); + } + } + } + } + else // 历遍子命令数据 + { + sub_cmd = sub_cmd->sub_fn.sub_cmd; + for (int i = 0; sub_cmd[i].cmd != NULL; i++) + { + sh_cmd_t new_data = sub_cmd[i]; + new_data.cmd = new_cmd; + strcpy(new_cmd, sub_cmd[i].cmd); + strcat(new_cmd, " "); + _completion_update(sh_hdl, info, &new_data, print_match, 30, flag_parent); + } + + if (sh_hdl->select_cmd != NULL && // 当前状态为模块模式 + info->print_count == 0 && // 没有打印任何可选项 + err_status != _SH_CMD_STATUS_Incomplete && // 如果正在输出当前模块下的子命令,则只继续补全子命令 + print_match == true && // 只打印匹配的备选项 + flag_parent == 0 // 目前仅在 _cmd_select_param() 被置起,此时不再历遍根命令链 + ) + { + sub_cmd = NULL; // 使从根命令链重新查找 + continue; + } + } + + if (sh_hdl->select_cmd != NULL && // 当前状态为模块模式 + info->match_num == 0 && // 没有找到可匹配的命令 + err_status != _SH_CMD_STATUS_Incomplete && // 如果正在输出当前模块下的子命令,则只继续补全子命令 + sub_cmd != NULL // 未历遍过根命令链 + ) + { + sub_cmd = NULL; // 使从根命令链重新查找 + } + else + { + break; + } + } + + if (print_match) + { + return false; + } + + return _do_completion_insert(sh_hdl, info); +} + +/** + * @brief 自动补全功能用:执行一次自动补全参数或打印备选参数的功能 + * + * @param info[out] 由 _completion_init() 初始化的补全信息指针,并被实时记录刷新。 + * @param sh_param 包含所有备选项的参数的结构。注意结构体必须有值为 NULL 的成员为结束。 + * @param print_match false -- 仅尝试自动补全参数; true -- 只打印匹配的备选项。 + * @return true 当 print_match 为 false (仅尝试自动补全参数)时,成功执行了一次补全参数的动作 + * @return false 当 print_match 为 true (只打印匹配的备选项)或无法补全参数 + */ +static bool _do_completion_param(sh_t *sh_hdl, sh_cp_info_t *info, const sh_cp_param_t *sh_param, bool print_match) +{ + if (print_match) + { + if (info->match_num == 0) + { + return false; + } + + if (sh_hdl->cp_operate != SH_CP_OP_PEND) + { + sh_echo(sh_hdl, "\r\n"); + sh_hdl->cp_operate = SH_CP_OP_PEND; + } + info->print_count = 0; + } + else + { + info->match_num = 0; + } + + for (int i = 0; sh_param[i].cmd != NULL; i++) + { + char new_cmd[sizeof(sh_hdl->cmd_line)]; + sh_cmd_t sub_cmd = { + .cmd = new_cmd, + .help = sh_param[i].help, + .cmd_func = (__typeof(sub_cmd.cmd_func))1, + .sub_fn = {.sub_cmd = NULL}, + }; + strcpy(new_cmd, sh_param[i].cmd); + strcat(new_cmd, " "); + _completion_update(sh_hdl, info, &sub_cmd, print_match, 30, 0); + } + + if (print_match) + { + return false; + } + + return _do_completion_insert(sh_hdl, info); +} + +/** + * @brief 自动补全功能用:执行补全 + * + * @param info[out] 由 _completion_init() 初始化的补全信息指针 + * @return true 成功执行了一次补全的动作 + * @return false 未执行补全的动作 + */ +static bool _do_completion_insert(sh_t *sh_hdl, sh_cp_info_t *info) +{ + /* 执行自动补全,返回:显示是否被自动更新 */ + if (info->match_str[info->arg_len] == '\0') + { + return false; + } + else + { + _insert_str(sh_hdl, &info->match_str[info->arg_len]); + sh_hdl->cp_operate = SH_CP_OP_CP; + return true; + } +} + +/** + * @brief 在展示备选项后恢复命令行的显示 + */ +static void _do_restore_line(sh_t *sh_hdl) +{ + if (sh_hdl->cp_operate == SH_CP_OP_PEND) + { + uint16_t cmd_input = sh_hdl->cmd_input; + sh_ctrl_print_cmd_line(sh_hdl); + sh_ctrl_set_input_pos(sh_hdl, cmd_input); + sh_hdl->cp_operate = SH_CP_OP_LIST; + } +} + +/** + * @brief 确定每个命令或参数的对齐的长度 + * + * @param info[out] 由 _completion_update() 更新的补全信息指针 + * @return int 每个命令或参数应占用的打印空间 + */ +static int _calc_list_algin(sh_cp_info_t *info) +{ + return (info->list_algin > 8 ? info->list_algin : 8) + 2; +} + +/** + * @brief 完整解析命令并获取命令信息 + * + * @param argc 命令数量 + * @param argv 命令字符串指针 + * @return sh_cmd_info_t 命令信息 + */ +sh_cmd_info_t sh_cmd_info(sh_t *sh_hdl, int argc, const char *argv[]) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + unsigned cmd_sections = 0; + sh_cmd_info_t info; + + info.err_status = _SH_CMD_STATUS_Bad; + info.match_count = 0; + info.reg_struct = NULL; + info.match_cmd = NULL; + + for (cmd_sections = 0; cmd_sections < argc; cmd_sections++) // 依次查找全部子命令 + { + info.match_cmd = _find_cmd(sh_hdl, &info.reg_struct, info.match_cmd, argv[cmd_sections]); + + if (info.match_cmd == NULL) + { + info.err_status = _SH_CMD_STATUS_Bad; + info.match_count = cmd_sections; + return info; + } + + if (info.match_cmd->cmd_func) // 找到子命令 + { + info.err_status = _SH_CMD_STATUS_Success; + info.match_count = cmd_sections + 1; + return info; + } + + if (cmd_sections + 1 < argc) + { + info.match_cmd = info.match_cmd->sub_fn.sub_cmd; + } + } + + if (info.match_cmd && info.match_cmd->sub_fn.sub_cmd && cmd_sections == argc) + { + info.err_status = _SH_CMD_STATUS_Incomplete; + info.match_count = cmd_sections; + return info; + } + + return info; +} + +/** + * @brief 根据输入的参数,结合当前命令行的状态,在已注册命令中查找并自执行一次自动补全命令/打印备选命令的全过程 + * + * @param argc 命令数量 + * @param argv 命令字符串指针 + * @param flag_parent 仅在内部的命令 select 中置 1 使用,表示只包含父命令的可选项。 + * @return sh_cp_op_t 自动补全命令/打印备选命令的执行结果 + */ +static sh_cp_op_t _sh_completion_cmd(sh_t *sh_hdl, int argc, const char *argv[], int flag_parent) +{ + sh_cmd_info_t cmd_info; + sh_cp_info_t cp_info; + + if (sh_hdl->exec_flag == 0) + { + sh_hdl->cp_operate = SH_CP_OP_NA; + } + + if (argc) + { + /* 初始化 cmd_info 和 cp_info */ + if (_arg_is_end(sh_hdl)) + { + cmd_info = sh_cmd_info(sh_hdl, argc, argv); + cp_info = _completion_init(""); + } + else + { + cmd_info = sh_cmd_info(sh_hdl, --argc, argv); + if (cmd_info.err_status == _SH_CMD_STATUS_Success) + { + cp_info = _completion_init(argv[argc++]); + } + else + { + cp_info = _completion_init(argv[cmd_info.match_count]); + } + } + + if (cmd_info.err_status == _SH_CMD_STATUS_Success) + { + if (cmd_info.match_cmd->sub_fn.cp_fn != NULL) + { + do + { + if (sh_hdl->cp_resource_flag) // 仅作用于使用了 sh_completion_resource() 的情况 + { + if (sh_hdl->tab_press_count) + { + cp_info.match_num = sh_hdl->cp_match_num; + cp_info.list_algin = sh_hdl->cp_list_algin; + if (cp_info.match_num < 2) + { + break; + } + + if (sh_hdl->cp_operate != SH_CP_OP_PEND) + { + sh_hdl->cp_operate = SH_CP_OP_PEND; + sh_echo(sh_hdl, "\r\n"); + } + } + } + + ++sh_hdl->exec_flag; + sh_hdl->cp_info = &cp_info; + + _clear_argv(argc, argv); + cmd_info.match_cmd->sub_fn.cp_fn(sh_hdl, argc - cmd_info.match_count, &argv[cmd_info.match_count], _arg_is_end(sh_hdl)); + + sh_hdl->cp_info = NULL; + --sh_hdl->exec_flag; + + if (sh_hdl->cp_resource_flag) // 仅作用于使用了 sh_completion_resource() 的情况 + { + int cp_resource_flag = sh_hdl->cp_resource_flag; + if (sh_hdl->tab_press_count == 0) + { + if (cp_info.match_num) + { + sh_hdl->tab_press_count = 1; + sh_hdl->cp_match_num = cp_info.match_num; + sh_hdl->cp_list_algin = cp_info.list_algin; + _do_completion_insert(sh_hdl, &cp_info); + } + } + else + { + /* 恢复当前命令行显示 */ + _do_restore_line(sh_hdl); + } + sh_hdl->cp_resource_flag = cp_resource_flag; + } + + } while (0); + } + + return sh_hdl->cp_operate; // 包含完整的命令 + } + + if (cmd_info.match_count != argc) + { + return sh_hdl->cp_operate; // 中间的命令错误 + } + } + else + { + cmd_info = sh_cmd_info(sh_hdl, argc, argv); + cp_info = _completion_init(""); // 没有任何命令时直接打印所有根命令 + } + + do + { + if (_do_completion_cmd(sh_hdl, &cp_info, &cmd_info, false, flag_parent)) + { + break; // 因为命令行被自动更新,不打印可用命令 + } + if (cp_info.match_num == 0) + { + break; // 没有匹配的命令 + } + if (sh_hdl->tab_press_count == 0) + { + sh_hdl->tab_press_count = 1; + break; // 暂停一次操作 + } + + /* 打印命令 */ + _do_completion_cmd(sh_hdl, &cp_info, &cmd_info, true, flag_parent); + + /* 恢复当前命令行显示 */ + _do_restore_line(sh_hdl); + + } while (0); + + return sh_hdl->cp_operate; +} + +/** + * @brief 根据输入的参数,结合当前命令行的状态,在已注册命令中查找并自执行一次自动补全命令/打印备选命令的全过程 + * + * @param argc 命令数量 + * @param argv 命令字符串指针 + * @return sh_cp_op_t 自动补全命令/打印备选命令的执行结果 + */ +sh_cp_op_t sh_completion_cmd(sh_t *sh_hdl, int argc, const char *argv[]) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + return _sh_completion_cmd(sh_hdl, argc, argv, 0); +} + +/** + * @brief 仅在 cp_fn() 中可用,根据当前命令行内容,在给出的参数结构中查找并自动补全参数/打印备选参数的全过程 + * + * @param param 允许为 NULL。包含所有备选项的参数的结构。注意结构体必须有值为 NULL 的成员为结束。 + * @return sh_cp_op_t 自动补全参数/打印备选参数的执行结果 + */ +sh_cp_op_t sh_completion_param(sh_t *sh_hdl, const sh_cp_param_t *param) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + if (sh_hdl->exec_flag == 0) + { + sh_hdl->cp_operate = SH_CP_OP_NA; + } + + do + { + sh_hdl->cp_resource_flag = 0; + + if (sh_hdl->cp_info == NULL) + { + if (sh_hdl->disable_echo == 0) + SYS_LOG_WRN("This function is only used for functions with automatic parameter completion. @ref SH_SETUP_CMD\r\n"); + break; + } + + if (param == 0) + { + break; + } + + if (_do_completion_param(sh_hdl, sh_hdl->cp_info, param, false)) + { + break; // 因为命令行被自动更新,不打印可用命令 + } + if (sh_hdl->cp_info->match_num == 0) + { + break; // 没有匹配的命令 + } + if (sh_hdl->tab_press_count == 0) + { + sh_hdl->tab_press_count = 1; + break; // 暂停一次操作 + } + + /* 打印参数 */ + _do_completion_param(sh_hdl, sh_hdl->cp_info, param, true); + + /* 恢复当前命令行显示 */ + _do_restore_line(sh_hdl); + + } while (0); + + return sh_hdl->cp_operate; +} + +/** + * @brief 仅在 cp_fn() 中可用,根据当前命令行内容,逐个给出可供查找的提示符。为内部提供 自动补全/打印备选项 的具体内容。 + * 自动补全/打印备行项 将在 cp_fn() 退出后自动执行。 + * @ref @c fatfs_shell.c@_scan_files()/@_cmd_df_cp() + * + * @param arg_str 指定已输入的待自动补全的字符串,当为NULL时表示按当前命令行自动填充 + * @param res_str 候选字符串 + * @param help 帮助描述信息。如果值为 NULL 则不会被打印 + */ +void sh_completion_resource(sh_t *sh_hdl, const char *arg_str, const char *res_str, const char *help) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + if (sh_hdl->cp_info == NULL) + { + if (sh_hdl->cp_resource_flag == 0) + { + sh_hdl->cp_resource_flag = 1; + if (sh_hdl->disable_echo == 0) + SYS_LOG_WRN("This function is only used for functions with automatic parameter completion. @ref SH_SETUP_CMD\r\n"); + } + return; + } + + sh_hdl->cp_resource_flag = 1; // 标记执行了本函数 + + if (res_str == NULL || *res_str == '\0') + { + return; + } + + if (arg_str) + { + sh_hdl->cp_info->arg_str = arg_str; + sh_hdl->cp_info->arg_len = strlen(arg_str); + } + + sh_cmd_t sub_cmd = { + .cmd = res_str, + .help = help, + .cmd_func = (__typeof(sub_cmd.cmd_func))1, + .sub_fn = {.sub_cmd = NULL}, + }; + int max_lines = 0; + if (help != NULL && *help != '\0') + { + max_lines = ~(1u << (sizeof(max_lines) * 8 - 1)); + } + _completion_update(sh_hdl, sh_hdl->cp_info, &sub_cmd, (bool)!!sh_hdl->tab_press_count, max_lines, 0); +} + +/** + * @brief 获取当前输入的最后一个参数是否完整(以空格分开) + * + * @return true 已完整输入一个参数(结尾有空格) + * @return false 正在输入参数 + */ +static bool _arg_is_end(sh_t *sh_hdl) +{ + return (bool)(sh_ctrl_get_line(sh_hdl)[sh_hdl->cmd_input - 1] == ' '); +} + +/** + * @brief 同步热键功能:根据当前命令行缓存的指针位置,更新光标位置记录 + * + */ +static void _update_cursor(sh_t *sh_hdl) +{ + sh_hdl->tab_press_count = 0; + sh_hdl->cp_resource_flag = 0; + if (sh_hdl->port.set_cursor_hoffset) + { + if (sh_hdl->cmd_input != sh_hdl->sync_cursor) + { + int offset = strlen(sh_hdl->prompt) + (sh_hdl->cmd_buf - sh_hdl->cmd_line); // 输入命令的起始绝对位置 + int last_pos = sh_hdl->sync_cursor + offset; + int new_pos = sh_hdl->cmd_input + offset; + sh_hdl->port.set_cursor_hoffset(sh_hdl, last_pos, new_pos); + } + } + sh_hdl->sync_cursor = sh_hdl->cmd_input; +} + +/** + * @brief 配合 sh_completion_resource() 使用,在下次进入 cp_fn() 时获取前一次自动补全的执行效果 + * + * @return sh_cp_op_t 最后一次自动补全的执行效果 + */ +sh_cp_op_t sh_get_cp_result(sh_t *sh_hdl) +{ + SYS_ASSERT(sh_hdl != NULL, ""); + + return sh_hdl->cp_operate; +} + +/** + * @brief 同步热键功能:自动实例/打印命令 + * + */ +void sh_ctrl_tab(sh_t *sh_hdl) +{ + int argc; + const char *argv[CONFIG_SH_MAX_PARAM]; + char cmd_param[sizeof(sh_hdl->cmd_line)]; + + /* 复制当前显示的命令到 cmd_param ,并在光标处截断 */ + strcpy(cmd_param, sh_ctrl_get_line(sh_hdl)); + cmd_param[sh_hdl->cmd_input] = '\0'; + + /* 解析参数并输出 argc, argv, cmd_param */ + _read_param(&argc, argv, cmd_param, cmd_param); + + /* 自动补全命令 */ + sh_completion_cmd(sh_hdl, argc, argv); +} + +/** + * @brief 同步热键功能:执行已缓存到的命令 + * + * @return true 命令行中的命令被找到并且执行完毕 + * @return false 没有执行任何命令 + */ +bool sh_ctrl_enter(sh_t *sh_hdl) +{ + int argc; + const char *argv[CONFIG_SH_MAX_PARAM]; + char cmd_param[sizeof(sh_hdl->cmd_line)]; + bool ret = false; + const char *cmd_line = sh_ctrl_get_line(sh_hdl); + + ++sh_hdl->exec_flag; + + /* 解析参数并输出 argc, argv, cmd_param */ + _read_param(&argc, argv, cmd_param, cmd_line); + if (argc) + { + /* 将当前回翻的记录复制到缓存中 */ + _apply_history(sh_hdl); + + /* 根据 argc, argv,解析出 info */ + sh_cmd_info_t info = sh_cmd_info(sh_hdl, argc, argv); + if (info.err_status == _SH_CMD_STATUS_Success) + { + _clear_argv(argc, argv); + sh_hdl->cmd_return = info.match_cmd->cmd_func(sh_hdl, argc - info.match_count, &argv[info.match_count]); + ret = true; + } + else + { + for (int i = 0; i < argc; i++) + { + sh_echo(sh_hdl, "%s%s", argv[i], i + 1 < argc ? " " : ""); + } + if (info.err_status == _SH_CMD_STATUS_Incomplete) + { + sh_echo(sh_hdl, ": command not complete\r\n"); + sh_hdl->cmd_return = 2; + } + else + { + sh_echo(sh_hdl, ": command not found\r\n"); + sh_hdl->cmd_return = 1; + } + ret = false; + } + + /* 去除重复的空格,但保持引号内的空格 */ + int index_src = 0; + int index_dst = 0; + char split_char = ' '; + while (cmd_line[index_src] != '\0' && cmd_line[index_src] == ' ') + { + index_src++; + } + while (cmd_line[index_src] != '\0') + { + char c = cmd_line[index_src++]; + if (split_char == ' ') + { + if (c == '\'' || c == '"') + { + split_char = c; + } + } + else if (c == split_char) + { + split_char = ' '; + } + + if (split_char == ' ' && c == ' ') + { + while (cmd_line[index_src] != '\0' && cmd_line[index_src] == ' ') + { + index_src++; + } + + if (cmd_line[index_src] == '\0') + { + break; + } + } + cmd_param[index_dst++] = c; + } + cmd_param[index_dst] = '\0'; + + /* 尝试保存到历史记录中 */ + _store_history(sh_hdl, cmd_param); + } + else + { + sh_hdl->tab_press_count = 0; + sh_hdl->cp_resource_flag = 0; + } + + sh_reset_line(sh_hdl); + + --sh_hdl->exec_flag; + + return ret; +} + +/** + * @brief 同步热键功能:删除光标的后 n 个字符 + * + * @param n 字符数 + * @return true 执行了动作 + * @return false 未执行动作 + */ +bool sh_ctrl_delete(sh_t *sh_hdl, int n) +{ + if (n == 0) + { + return true; + } + + _apply_history(sh_hdl); + + if (sh_hdl->cmd_input + n <= sh_hdl->cmd_stored) + { + for (int i = sh_hdl->cmd_input; i + n < sh_hdl->cmd_stored; i++) /* 使光标之后的数据往前移动一个字节 */ + sh_hdl->cmd_buf[i] = sh_hdl->cmd_buf[i + n]; + + sh_hdl->cmd_stored -= n; + sh_hdl->cmd_buf[sh_hdl->cmd_stored] = '\0'; + return true; + } + else + { + return false; + } +} + +/** + * @brief 同步热键功能:删除光标的前 n 个字符 + * + * @param n 字符数 + * @return true 执行了动作 + * @return false 未执行动作 + */ +bool sh_ctrl_backspace(sh_t *sh_hdl, int n) +{ + if (n == 0) + { + return true; + } + + _apply_history(sh_hdl); + + if (sh_hdl->cmd_input >= n) + { + sh_hdl->cmd_input -= n; + sh_hdl->sync_cursor -= n; + return sh_ctrl_delete(sh_hdl, n); + } + else + { + return false; + } +} + +/** + * @brief 同步热键功能:查看上一个命令,并更新光标位置记录 + * + * @return true 执行了动作 + * @return false 未执行动作 + */ +bool sh_ctrl_up(sh_t *sh_hdl) +{ + if (sh_hdl->history_show < sh_hdl->history_valid_num) + { + ++sh_hdl->history_show; + sh_ctrl_end(sh_hdl); + return true; + } + else + { + return false; + } +} + +/** + * @brief 同步热键功能:查看下一个命令,并更新光标位置记录 + * + * @return true 执行了动作 + * @return false 未执行动作 + */ +bool sh_ctrl_down(sh_t *sh_hdl) +{ + if (sh_hdl->history_show > 0) + { + --sh_hdl->history_show; + sh_ctrl_end(sh_hdl); + return true; + } + else + { + return false; + } +} + +/** + * @brief 同步热键功能:光标左移,并更新光标位置记录 + * + */ +void sh_ctrl_left(sh_t *sh_hdl) +{ + if (sh_hdl->cmd_input > 0) + --sh_hdl->cmd_input; + + _update_cursor(sh_hdl); +} + +/** + * @brief 同步热键功能:光标右移,并更新光标位置记录 + * + */ +void sh_ctrl_right(sh_t *sh_hdl) +{ + if (sh_hdl->cmd_input < _get_end_pos(sh_hdl)) + ++sh_hdl->cmd_input; + + _update_cursor(sh_hdl); +} + +/** + * @brief 同步热键功能:光标移到开头位置,并更新光标位置记录 + * + */ +void sh_ctrl_home(sh_t *sh_hdl) +{ + sh_hdl->cmd_input = 0; + + _update_cursor(sh_hdl); +} + +/** + * @brief 同步热键功能:光标移到结尾位置,并更新光标位置记录 + * + */ +void sh_ctrl_end(sh_t *sh_hdl) +{ + sh_hdl->cmd_input = _get_end_pos(sh_hdl); + + _update_cursor(sh_hdl); +} + +/** + * @brief 同步热键功能:光标移到指定位置(不包括提示符,起始位置为0),并更新光标位置记录 + * + * @param pos 0.._get_end_pos(sh_hdl) + */ +void sh_ctrl_set_cursor(sh_t *sh_hdl, int pos) +{ + if (pos < 0) + { + sh_hdl->cmd_input = 0; + } + else if (pos > _get_end_pos(sh_hdl)) + { + sh_hdl->cmd_input = _get_end_pos(sh_hdl); + } + else + { + sh_hdl->cmd_input = pos; + } + _update_cursor(sh_hdl); +} + +/** + * @brief 同步热键功能:光标移到当前单词的开头,并更新光标位置记录 + * + */ +void sh_ctrl_word_head(sh_t *sh_hdl) +{ + const char *line = sh_ctrl_get_line(sh_hdl); + int pos = sh_hdl->cmd_input; + while (pos) + { + if (_IS_ALPHA(line[pos - 1]) || _IS_DIGIT(line[pos - 1])) + { + break; + } + --pos; + } + + while (pos) + { + if (!_IS_ALPHA(line[pos - 1]) && !_IS_DIGIT(line[pos - 1])) + { + break; + } + --pos; + } + sh_ctrl_set_cursor(sh_hdl, pos); +} + +/** + * @brief 同步热键功能:光标移到当前单词的结尾,并更新光标位置记录 + * + */ +void sh_ctrl_word_tail(sh_t *sh_hdl) +{ + const char *line = sh_ctrl_get_line(sh_hdl); + int pos = sh_hdl->cmd_input; + while (line[pos] != '\0') + { + if (_IS_ALPHA(line[pos]) || _IS_DIGIT(line[pos])) + { + break; + } + ++pos; + } + + while (line[pos] != '\0') + { + if (!_IS_ALPHA(line[pos]) && !_IS_DIGIT(line[pos])) + { + break; + } + ++pos; + } + sh_ctrl_set_cursor(sh_hdl, pos); +} + +/** + * @brief 同步热键功能:打印一次命令行内容 + * + */ +void sh_ctrl_print_cmd_line(sh_t *sh_hdl) +{ + sh_echo(sh_hdl, _COLOR_G); + sh_echo(sh_hdl, sh_ctrl_get_prompt(sh_hdl)); + if (sh_hdl->select_cmd) + { + sh_echo(sh_hdl, _COLOR_P); + sh_echo(sh_hdl, "%s: ", sh_hdl->cmd_line); + } + sh_echo(sh_hdl, _COLOR_END); + const char *line = sh_ctrl_get_line(sh_hdl); + sh_echo(sh_hdl, line); + + sh_hdl->cmd_input = strlen(line); + sh_hdl->sync_cursor = sh_hdl->cmd_input; + _update_cursor(sh_hdl); +} + +/** + * @brief 同步热键功能:擦除光标前的单词内容 + * + * @return unsigned 成功擦除的字符数 + */ +unsigned sh_ctrl_del_word(sh_t *sh_hdl) +{ + const char *line = sh_ctrl_get_line(sh_hdl); + int pos = sh_hdl->cmd_input; + int n = 0; + while (pos) + { + if (_IS_ALPHA(line[pos - 1]) || _IS_DIGIT(line[pos - 1])) + { + break; + } + --pos; + ++n; + } + + while (pos) + { + if (!_IS_ALPHA(line[pos - 1]) && !_IS_DIGIT(line[pos - 1])) + { + break; + } + --pos; + ++n; + } + + _store_cmd_bak(sh_hdl, line, pos, n); + + sh_ctrl_backspace(sh_hdl, n); + + return n; +} + +/** + * @brief 同步热键功能:擦除光标前到行首的全部内容 + * + * @return unsigned 成功擦除的字符数 + */ +unsigned sh_ctrl_del_left(sh_t *sh_hdl) +{ + const char *line = sh_ctrl_get_line(sh_hdl); + int pos = sh_hdl->cmd_input; + + _store_cmd_bak(sh_hdl, line, 0, pos); + + sh_ctrl_backspace(sh_hdl, pos); + return pos; +} + +/** + * @brief 同步热键功能:擦除光标后到行尾的全部内容 + * + * @return unsigned 成功擦除的字符数 + */ +unsigned sh_ctrl_del_right(sh_t *sh_hdl) +{ + const char *line = sh_ctrl_get_line(sh_hdl); + int pos = sh_hdl->cmd_input; + int n = strlen(line) - pos; + + _store_cmd_bak(sh_hdl, line, pos, n); + + sh_ctrl_delete(sh_hdl, n); + + return n; +} + +/** + * @brief 同步热键功能:插入 sh_ctrl_del_word(), sh_ctrl_del_left(), sh_ctrl_del_right() 擦除的内容 + * + * @return true 执行了动作 + * @return false 未执行动作 + */ +bool sh_ctrl_undelete(sh_t *sh_hdl) +{ + if (sh_hdl->cmd_bank && sh_hdl->bank_size) + { + if (sh_hdl->cmd_bank[0]) + { + _insert_str(sh_hdl, sh_hdl->cmd_bank); + return true; + } + } + return false; +} + +/** + * @brief 同步热键功能:查询当前是否使用了 select 命令进入的模块模式(快捷命令模式) + * + * @return true 当前在模块模式 + * @return false 当前未在模块模式 + */ +bool sh_ctrl_is_module(sh_t *sh_hdl) +{ + return (bool)(!!sh_hdl->select_cmd); +} + +/** + * @brief 设置当前指针(光标)位置(不包括提示符和模块提示) + * + * @param input_pos 0.._get_end_pos(sh_hdl) + */ +void sh_ctrl_set_input_pos(sh_t *sh_hdl, uint16_t input_pos) +{ + if (input_pos > _get_end_pos(sh_hdl)) + input_pos = _get_end_pos(sh_hdl); + + sh_hdl->cmd_input = input_pos; + + _update_cursor(sh_hdl); +} + +/** + * @brief 获取当前指针(光标)位置(不包括提示符和模块提示),起始位置为0 + * + * @param input_pos 0.._get_end_pos(sh_hdl) + */ +uint16_t sh_ctrl_get_input_pos(sh_t *sh_hdl) +{ + return sh_hdl->cmd_input; +} + +/** + * @brief 根据当前显示内容(包含回翻的历史记录),获取当前光标位置(提示符+模块提示+命令) + * + * @return uint16_t 获取当前光标位置(提示符+模块提示+命令) + */ +uint16_t sh_ctrl_get_line_pos(sh_t *sh_hdl) +{ + return strlen(sh_hdl->prompt) + + (sh_hdl->cmd_buf - sh_hdl->cmd_line) + + sh_hdl->cmd_input; +} + +/** + * @brief 根据当前显示内容(包含回翻的历史记录),获取最大光标位置(包含提示符和模块提示) + * 用于模拟清除当前行 + * + * @return uint16_t 当前命令行显示的总字符数 + */ +uint16_t sh_ctrl_get_line_len(sh_t *sh_hdl) +{ + return strlen(sh_hdl->prompt) + + (sh_hdl->cmd_buf - sh_hdl->cmd_line) + + _get_end_pos(sh_hdl); +} + +/** + * @brief 获取命令行部分的字符(提示符+模块提示+命令) + * + * @param pos 命令行位置 0.._get_end_pos(sh_hdl) + * @return char 字符 + */ +char sh_ctrl_get_line_char(sh_t *sh_hdl, uint16_t pos) +{ + int test_len; + + test_len = strlen(sh_hdl->prompt); + if (pos < test_len) + { + return sh_hdl->prompt[pos]; + } + pos -= test_len; + + test_len = sh_hdl->cmd_buf - sh_hdl->cmd_line; + if (pos < test_len) + { + return sh_hdl->cmd_line[pos]; + } + pos -= test_len; + + test_len = _get_end_pos(sh_hdl); + if (pos < test_len) + { + if (sh_hdl->history_show) + return sh_hdl->cmd_history[sh_hdl->history_index[sh_hdl->history_show - 1] + pos]; + else + return sh_hdl->cmd_buf[pos]; + } + + return '\0'; +} + +/* 内部默认命令 -------------------------------------------------------------------------------------------- */ + +#if defined(MIX_SHELL) + +SH_CMD_FN(_cmd_print_init_fn) +{ + extern sys_init_t __sys_init_leader_0; + extern sys_init_t __sys_init_leader_e; + sys_init_t *start = &__sys_init_leader_0; + sys_init_t *end = &__sys_init_leader_e; + int match_count = 0; + sh_echo(sh_hdl, "Automatic initialization list >>>\r\n"); + while (start < end) + { + if (start->level + start->prior != 0 && + start->level * 100 + start->prior != 100 && + start->level * 100 + start->prior != 999) + { + const char *lev2char[] = { + "BOARD", + "PREV", + "DEVICE", + "COMPONENT", + "ENV", + "APP", + }; + char buf[0x200]; + SYS_SNPRINT(buf, sizeof(buf), "%-10s %d-%-2d %s:%d @%s", + start->level < sizeof(lev2char) / sizeof(lev2char[0]) ? lev2char[start->level] : "MISC", + start->level, + start->prior, + _FILENAME(start->file), + start->line, + start->fn_name); + + int result = !argc; + for (int i = 0; i < argc; i++) + { + if (strstr(buf, argv[i])) + { + result = 1; + break; + } + } + + if (result) + { + sh_echo(sh_hdl, "%s\r\n", buf); + match_count++; + } + } + start = &start[1]; + } + sh_echo(sh_hdl, "Total: %d\r\n", match_count); + return 0; +} + +SH_CMD_FN(_cmd_print_module_struct) +{ + extern const module_t __sys_module_leader_0; + extern const module_t __sys_module_leader_9; + const module_t *start = &__sys_module_leader_0; + const module_t *end = &__sys_module_leader_9; + int match_count = 0; + sh_echo(sh_hdl, "Internal module cmd list >>>\r\n"); + + const struct + { + module_type_t type; + const char *name; + } type2char[] = { + {MODULE_TYPE_DEVICE, "DEVICE"}, + }; + + for (int n = 0; n < sizeof(type2char) / sizeof(type2char[0]); n++) + { + while (start < end) + { + if (start->type == type2char[n].type) + { + char buf[0x200]; + SYS_SNPRINT(buf, sizeof(buf), "%p %-10s %-10s %s:%d", + start->obj, + type2char[n].name, + start->name, + _FILENAME(start->file), + start->line); + + int result = !argc; + for (int i = 0; i < argc; i++) + { + if (strstr(buf, argv[i])) + { + result = 1; + break; + } + } + + if (result) + { + sh_echo(sh_hdl, "%s\r\n", buf); + match_count++; + } + } + start = &start[1]; + } + } + + sh_echo(sh_hdl, "Total: %d\r\n", match_count); + return 0; +} + +#endif /* #if defined(MIX_SHELL) */ + +SH_CMD_FN(_cmd_history) +{ + if (argc == 0) + { + for (int i = sh_hdl->history_valid_num; i--;) + { + sh_echo(sh_hdl, "%s\r\n", &sh_hdl->cmd_history[sh_hdl->history_index[i]]); + } + } + else + { + if (strcmp(argv[0], "enable") == 0) + { + sh_hdl->disable_history = false; + } + else if (strcmp(argv[0], "disable") == 0) + { + sh_hdl->disable_history = true; + } + else + { + sh_echo(sh_hdl, "%s: parameter error\r\n", argv[0]); + } + } + return 0; +} + +SH_CMD_FN(_cmd_select) +{ + if (argc) + { + sh_cmd_info_t cmd_info = sh_cmd_info(sh_hdl, argc, argv); + if (cmd_info.err_status == _SH_CMD_STATUS_Incomplete) + { + sh_hdl->select_reg_struct = cmd_info.reg_struct; + sh_hdl->select_cmd = cmd_info.match_cmd; + + /* 把 cmd_param 中分散的参数以空格分隔,合并成一字符串并保存 */ + char *p = sh_hdl->cmd_line; + for (int i = 0; i < argc; i++) + { + for (int n = 0; argv[i][n] != '\0'; n++) + { + *p++ = argv[i][n]; + } + if (i + 1 < argc) + { + *p++ = ' '; + } + else + { + *p++ = '\0'; + } + } + sh_hdl->cmd_buf = p; + return 0; + } + else + { + sh_echo(sh_hdl, "%s: not a parent command\r\n", argv[0]); + } + } + else + { + if (sh_hdl->select_cmd) + { + sh_hdl->select_cmd = NULL; + sh_hdl->cmd_buf = sh_hdl->cmd_line; + return 0; + } + } + + return -1; +} + +SH_CMD_CP_FN(_cmd_select_param) +{ + _sh_completion_cmd(sh_hdl, argc, argv, 1); +} + +SH_CMD_CP_FN(_cmd_history_param) +{ + sh_completion_resource(sh_hdl, NULL, "enable", NULL); + sh_completion_resource(sh_hdl, NULL, "disable", NULL); +} + +SH_CMD_FN(_cmd_version) +{ + sh_echo(sh_hdl, "sh v" SH_VERSION " build %04d-%02d-%02d\r\n", _YEAR, _MONTH, _DAY); + return 0; +} + +SH_CMD_FN(_cmd_exit) +{ + if (sh_hdl->select_cmd) + { + sh_hdl->select_cmd = NULL; + sh_hdl->cmd_buf = sh_hdl->cmd_line; + return 0; + } + else if (sh_hdl->port.disconnect) + { + sh_hdl->port.disconnect(); + return 0; + } + else + { + extern void drv_hal_sys_exit(void); + drv_hal_sys_exit(); + return -1; + } +} + +SH_CMD_FN(_cmd_print_help) +{ + sh_cmd_info_t info = sh_cmd_info(sh_hdl, argc, argv); + + if (argc == 0) + { + sh_cp_info_t cp_info = _completion_init(""); + sh_echo(sh_hdl, "Command list >>>"); + cp_info.match_num = 1; + _do_completion_cmd(sh_hdl, &cp_info, NULL, false, 0); + _do_completion_cmd(sh_hdl, &cp_info, NULL, true, 0); + return 0; + } + + if (info.err_status == _SH_CMD_STATUS_Bad) + { + sh_echo(sh_hdl, "sh: help: not found Command '"); + for (int i = 0; i < argc; i++) + { + sh_echo(sh_hdl, "%s%s", argv[i], i + 1 < argc ? " " : "'\r\n"); + } + return 0; + } + + int total_len = 0; + for (int i = 0; i < info.match_count; i++) + { + total_len += strlen(argv[i]) + 1; + } + total_len += 3; + for (int i = 1; i <= info.match_count; i++) + { + sh_cmd_info_t tmp = sh_cmd_info(sh_hdl, i, argv); + + if (i == 1) + { + sh_echo(sh_hdl, "build in %s:%d\r\n", _FILENAME(info.reg_struct->file), info.reg_struct->line); + } + + int print_len = 0; + for (int j = 0; j < i; j++) + { + print_len += sh_echo(sh_hdl, "%s%s", argv[j], j + 1 < i ? " " : ""); + } + for (int j = 0; j < total_len - print_len; j++) + { + sh_echo(sh_hdl, " "); + } + sh_echo(sh_hdl, "-- %s\r\n", tmp.match_cmd->help); + + if (i == info.match_count) + { + if (info.err_status == _SH_CMD_STATUS_Success && info.match_cmd->sub_fn.cp_fn == NULL) + { + break; + } + for (int j = 0; j < i; j++) + { + sh_echo(sh_hdl, "%s%s", argv[j], j + 1 < i ? " " : ""); + } + if (info.err_status == _SH_CMD_STATUS_Incomplete) + { + sh_echo(sh_hdl, " <...>\r\n"); // 命令未完整 + } + else if (info.match_cmd->sub_fn.cp_fn) + { + sh_echo(sh_hdl, " [...]\r\n"); // 带参数自动补全的命令 + } + } + } + + return 0; +} + +SH_CMD_CP_FN(_cmd_print_help_param) +{ + sh_completion_cmd(sh_hdl, argc, argv); +} + +SH_CMD_FN(_cmd_echo_on) +{ + sh_hdl->disable_echo = 0; + return 0; +} + +SH_CMD_FN(_cmd_echo_off) +{ + sh_hdl->disable_echo = 1; + return 0; +} + +SH_CMD_FN(_cmd_parse_test) +{ + for (int i = 0; i < argc; i++) + { + sh_parse_t pv = sh_parse_value(argv[i]); + switch (pv.type) + { + case _PARSE_TYPE_STRING: // 字符串 + sh_echo(sh_hdl, "param%d: type = string \"%s\"\r\n", + i + 1, + pv.value.val_string); + break; + case _PARSE_TYPE_INTEGER: // 带符号整型 + sh_echo(sh_hdl, "param%d: type = inteter, value = %u(dec) 0x%x(hex) 0%o(oct)\r\n", + i + 1, + pv.value.val_unsigned, pv.value.val_unsigned, pv.value.val_unsigned); + break; + case _PARSE_TYPE_UNSIGNED: // 无符号整型 + sh_echo(sh_hdl, "param%d: type = inteter, value = %d(dec) 0x%x(hex) 0%o(oct)\r\n", + i + 1, + pv.value.val_integer, pv.value.val_integer, pv.value.val_integer); + break; + case _PARSE_TYPE_FLOAT: // 浮点 + sh_echo(sh_hdl, "param%d: type = float, value = %f\r\n", + i + 1, + pv.value.val_float); + break; + + default: + break; + } + } + return 0; +} + +SH_DEF_SUB_CMD( + _cmd_echo_sublist, + SH_SETUP_CMD("on", "Enable to feedback the command line", _cmd_echo_on, NULL), // + SH_SETUP_CMD("off", "Disable to feedback the command line", _cmd_echo_off, NULL), // +); + +SH_DEF_SUB_CMD( + _cmd_sh_sublist, + SH_SETUP_CMD("echo", "Turn on/off echo through sh_echo()", NULL, _cmd_echo_sublist), // + SH_SETUP_CMD("history", "Show history control", _cmd_history, _cmd_history_param), // +#if defined(MIX_SHELL) + SH_SETUP_CMD("list-init", "List all auto initialize function\r\n\t* Usage: list-init [filter]", _cmd_print_init_fn, NULL), // + SH_SETUP_CMD("list-module", "List all module structure\r\n\t* Usage: list-module [filter]", _cmd_print_module_struct, NULL), // +#endif + SH_SETUP_CMD("version", "Print the shell version", _cmd_version, NULL), // + SH_SETUP_CMD("parse-value", "Parse the value of a string demo", _cmd_parse_test, NULL), // +); + +SH_REGISTER_CMD( + register_internal_command, + SH_SETUP_CMD("sh", "Internal command", NULL, _cmd_sh_sublist), // + SH_SETUP_CMD("help", "Print the complete root command", _cmd_print_help, _cmd_print_help_param), // + SH_SETUP_CMD("select", "Select parent command", _cmd_select, _cmd_select_param), // + SH_SETUP_CMD("exit", "Exit parent command or disconnect", _cmd_exit, NULL), // +); + +/** + * @brief 用于显式初始化内部函数。 + * 当自动初始化函数不适用时,使用此函数可强制初始化内部的一些功能。包含: + * 1. 执行 register_internal_command() 注册内部的基本命令 + * 2. 执行 sh_init_vt100() 控制相关的初始化 + * + * @param out 定义执行 vprintf 的函数函数 + */ +void sh_register_external(sh_vprint_fn out) +{ + sh_register_cmd(®ister_internal_command); + + if (out == NULL) + { + out = SYS_VPRINT; + } + if (sh_init_vt100(&g_uart_handle_vt100, out, NULL) == 0) + { + static uint8_t history_mem[255]; + static uint8_t bank_mem[CONFIG_SH_MAX_LINE_LEN]; + sh_config_history_mem(&g_uart_handle_vt100, history_mem, sizeof(history_mem)); + sh_config_backup_mem(&g_uart_handle_vt100, bank_mem, sizeof(bank_mem)); + } +} diff --git a/components/system/source/shell/sh.h b/components/system/source/shell/sh.h new file mode 100755 index 0000000..44b8b28 --- /dev/null +++ b/components/system/source/shell/sh.h @@ -0,0 +1,400 @@ +/** + * @file sh.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-03-21 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __SH_H__ +#define __SH_H__ + +#include "sys_init.h" +#include "list/pslist.h" + +#define SH_VERSION "0.1" + +#ifndef CONFIG_SH_MAX_LINE_LEN +#define CONFIG_SH_MAX_LINE_LEN 512 /* 最大命令长度 */ +#endif + +#ifndef CONFIG_SH_USE_STRTOD +#define CONFIG_SH_USE_STRTOD 1 /* 允许参数解析工具 sh_parse_value() 解析浮点数 */ +#endif + +typedef struct sh_obj_def sh_t; + +typedef int (*sh_vprint_fn)(const char *format, va_list va); // 实现发送字符串到终端 +typedef void (*sh_disconnect_fn)(void); // 实现断开连接 + +typedef struct // 兼容接口 +{ + void (*set_cursor_hoffset)(sh_t *sh_hdl, int last_pos, int new_pos); // 设置光标水平位置 + void (*insert_str)(sh_t *sh_hdl, const char *str); // 在光标前插入一个字符串 + sh_vprint_fn vprint; // 实现发送字符串到终端 + sh_disconnect_fn disconnect; // 实现断开连接 + + void (*clear_line)(sh_t *sh_hdl); // 执行清除一行。值为 NULL 时不启用刷命令行 +} sh_port_t; + +typedef struct +{ + const char *arg_str; // 待分析的参数 + int arg_len; // 待解析的参数长度 + char match_str[CONFIG_SH_MAX_LINE_LEN / 4 * 4]; // 保存相同的字符部分 + int list_algin; // 配合 list_fmt ,确定格式中 %-ns 的 n 的值 + int match_num; // 可打印的列表的条目数 + int print_count; // 已打印计算 +} sh_cp_info_t; + +typedef enum __packed +{ + SH_CP_OP_NA, // 未发生任何动作 + SH_CP_OP_CP, // 执行了自动补全 + SH_CP_OP_PEND, // 正在列举选项 + SH_CP_OP_LIST, // 完成了列举选项 +} sh_cp_op_t; + +typedef struct sh_cmd sh_cmd_t; + +typedef void (*sh_cp_fn)(sh_t *sh_hdl, int argc, const char *argv[], bool flag); // flag: 表示当前输入的最后一个参数是否完整(以空格分开) + +typedef enum +{ + _SH_SUB_TYPE_VOID, + _SH_SUB_TYPE_CPFN, + _SH_SUB_TYPE_SUB, +} sh_fn_type_t; + +typedef struct sh_param +{ + const char *cmd; + const char *help; +} sh_cp_param_t; + +typedef struct sh_cmd +{ + const char *cmd; + const char *help; + int (*cmd_func)(sh_t *sh_hdl, int argc, const char *argv[]); + union + { + const void *nul; + const sh_cmd_t *sub_cmd; + sh_cp_fn cp_fn; + } sub_fn; + sh_fn_type_t fn_type; +} sh_cmd_t; + +typedef struct +{ + sys_psnode_t *node; + const sh_cmd_t *cmd; + const char *file; + int line; +} sh_cmd_reg_t; + +typedef struct +{ + const char *code; + void (*key_func)(sh_t *sh_hdl); +} sh_key_t; + +typedef struct +{ + sys_psnode_t *node; + const sh_key_t *key; +} sh_key_reg_t; + +typedef struct +{ + enum + { + _SH_CMD_STATUS_Bad, // 未找到命令 + _SH_CMD_STATUS_Success, // 成功找到命令函数 + _SH_CMD_STATUS_Incomplete, // 命令不完整 + } err_status; + + int match_count; // 表示有多少段为成功匹配的命令段 + + const sh_cmd_reg_t *reg_struct; // 根命令结构的定义地址 + + const sh_cmd_t *match_cmd; // 最后一个匹配的命令信息 + +} sh_cmd_info_t; + +typedef struct // 字符串值解释结果 +{ + enum + { + _PARSE_TYPE_STRING, // 字符串 + _PARSE_TYPE_INTEGER, // 带符号整型 + _PARSE_TYPE_UNSIGNED, // 无符号整型 + _PARSE_TYPE_FLOAT, // 浮点 + } type; + + union + { + const char *val_string; // 指向字符串地址 + int val_integer; // 带符号整型值 + unsigned val_unsigned; // 无符号整型值 + float val_float; // 浮点值 + } value; + + char *endptr; // 指向结束字符 + +} sh_parse_t; + +typedef struct sh_obj_def // 对象内存结构 +{ + sh_port_t port; + + sys_psnode_t obj_key_node; + sh_key_reg_t obj_key_data; + + sys_psnode_t obj_cmd_node; + sh_cmd_reg_t obj_cmd_data; + + const char *prompt; // 提示符 + + sys_pslist_t key_list; // 已注册的热键链 + char key_str[8]; // 已缓存的热键代码 + uint16_t key_stored; // 已缓存的热键数 + + sys_pslist_t cmd_list; // 已注册的仅属于对应的终端可见的根命令链 + char cmd_line[CONFIG_SH_MAX_LINE_LEN / 4 * 4]; // 当前命令缓存(包括 模块提示、命令行,不包括 提示符) + char *cmd_buf; // 当前命令行在 cmd_line[] 中的指针 + uint16_t cmd_stored; // 当前已缓存字符数(不包括 提示符 和 模块提示) + uint16_t cmd_input; // 当前光标位置(0.._MAX_CMD_LEN) + uint16_t sync_cursor; // 与当前光标同步的位置 + int cmd_return; // 指令执行的结果 + + const sh_cmd_reg_t *select_reg_struct; // 内部 select 命令 + const sh_cmd_t *select_cmd; // 内部 select 命令 + + char *cmd_history; // 命令的历史记录 + int history_size; // cmd_history 的长度 + uint16_t *history_index; // 历史记录信息 + uint16_t history_index_num; // history_index 的成员数 + uint16_t history_valid_num; // 已记录的历史命令数 + uint16_t history_show; // 当前正在显示历史命令。如果在显示历史命令时输入任意有效字符,则 cmd_line[] 将立即被更新 + + char *cmd_bank; // 用于保存 sh_ctrl_del_word(), sh_ctrl_del_left(), sh_ctrl_del_right() 删除的内容 + int bank_size; // cmd_bank 的大小(建议值为 CONFIG_SH_MAX_LINE_LEN ) + + sh_cp_info_t *cp_info; // 在允许传递 TAB 键到函数并预执行时暂存信息 + int cp_match_num; // 记录 sh_completion_resource() 需要保持的信息 + int cp_list_algin; // 记录 sh_completion_resource() 需要保持的信息 + uint8_t cp_resource_flag; // 标记执行了 sh_completion_resource() + uint8_t tab_press_count; // cp_fn() 执行次数计数 + uint8_t exec_flag; // 禁止重入关键函数 + volatile sh_cp_op_t cp_operate; // 保存最后一次自动补全的执行效果 + + bool disable_echo; // 关闭回显 + bool disable_history; // 关闭记录历史(以节约CPU开销) +} sh_t; + +#define _SH_DO_CONCAT(X, Y) X##Y +#define _SH_CONCAT(X, Y) _SH_DO_CONCAT(X, Y) +#define _SH_NAME(NAME) _SH_CONCAT(_SH_CONCAT(_SH_CONCAT(__, NAME), __), __LINE__) +#define _SH_GENERIC_SUB(SUB) (__builtin_types_compatible_p(__typeof(SUB), void *) ? _SH_SUB_TYPE_VOID \ + : __builtin_types_compatible_p(__typeof(SUB), __typeof(_sh_generic_cp_fn)) ? _SH_SUB_TYPE_CPFN \ + : __builtin_types_compatible_p(__typeof(SUB), sh_cmd_t const[]) ? _SH_SUB_TYPE_SUB \ + : ~0u) + +/* 定义命令 ------------------------------------------------------------------------- */ + +/** + * @brief 定义执行命令的函数 + * + * @param sh_hdl 由 sh_init_vt100() 初始化,在 sh_putc() 传入入的对象 + * @param argc 已输入参数的数量 + * @param argv 已输入参数的指针(字符串) + * + * @verbatim + * @c sh.c + * @endverbatim + */ +#define SH_CMD_FN(NAME) __used static int NAME(sh_t *sh_hdl, int argc, const char *argv[]) + +/** + * @brief 定义执行自动补全的函数,当收到按键为 TAB 时被调用。 + * + * @param sh_hdl 由 sh_init_vt100() 初始化,在 sh_putc() 传入入的对象 + * @param argc 已输入参数的数量 + * @param argv 已输入参数的指针(字符串) + * @param flag false 表示最后一个参数正在输入,true 表示最后一个参数已完整输入(光标前一个字符是空格) + * + * @verbatim + * @c sh.c + * @endverbatim + */ +#define SH_CMD_CP_FN(NAME) __used static void NAME(sh_t *sh_hdl, int argc, const char *argv[], bool flag) + +/** + * @brief 定义命令列表 SH_REGISTER_CMD 或 SH_DEF_SUB_CMD 中的成员 + * + * @param CMD 命令(字符串,不要有空格) + * @param HELP 命令的帮助信息(字符串) + * @param FUNC 执行命令对的函数( static int _cmd_func(sh_t *sh_hdl, int argc, const char *argv[]) )。如果即值为 NULL 时,表示有子命令。 + * @param SUB_NAME + * 1. 如果 FUNC 取值为 NULL 时, SUB_NAME 必须指向 SH_DEF_SUB_CMD 定义的子命令列表名; + * 2. 如果 FUNC 取值为函数时: + * 2.1 SUB_NAME 可为 NULL; + * 2.1 SUB_NAME 指向 void (*cp_fn)(sh_t *sh_hdl, int argc, const char *argv[]) 表示按 TAB 键时被执行的函数,用于参数补全。 + * + * @verbatim + * @c sh.c + * @endverbatim + */ +#define SH_SETUP_CMD(CMD, HELP, FUNC, SUB) \ + { \ + .cmd = CMD, \ + .help = HELP, \ + .cmd_func = FUNC, \ + .sub_fn = {SUB}, \ + .fn_type = _SH_GENERIC_SUB(SUB), \ + } + +/** + * @brief 定义子命令列表(由 SH_SETUP_CMD 中的参数 SUB_NAME 所指向的列表)。 + * + * @param SUB_NAME 子命令列表名。需要与 SH_SETUP_CMD 中的参数 SUB_NAME 的值保持一致 + * + * @verbatim + * @c sh.c + * @endverbatim + */ +#define SH_DEF_SUB_CMD(SUB_NAME, ...) \ + static sh_cmd_t const SUB_NAME[] = { \ + __VA_ARGS__{0}} + +/** + * @brief 定义根命令列表。 + * + * @param NAME 描述自动初始化的函数名。 + * + * @param ... 使用 SH_SETUP_CMD 添加任意数量的根命令的信息。 + */ +#define SH_DEF_CMD(NAME, ...) \ + static sys_psnode_t _SH_NAME(cmd_node_); \ + static sh_cmd_t const _SH_NAME(cmd_data_)[] = { \ + __VA_ARGS__{0}}; \ + static sh_cmd_reg_t const NAME = { \ + .node = &_SH_NAME(cmd_node_), \ + .cmd = _SH_NAME(cmd_data_), \ + .file = __FILE__, \ + .line = __LINE__, \ + } + +/** + * @brief 注册根命令列表。将在初始化阶段被自动执行。 + * + * @param NAME 描述自动初始化的函数名。 + * + * @param ... 使用 SH_SETUP_CMD 添加任意数量的根命令的信息。 + * + * @verbatim + * @c sh.c + * @endverbatim + */ +#define SH_REGISTER_CMD(NAME, ...) \ + SH_DEF_CMD(NAME, __VA_ARGS__); \ + static int _init_##NAME(void) \ + { \ + return sh_register_cmd(&NAME); \ + } \ + INIT_EXPORT_COMPONENT(_init_##NAME) + +SH_CMD_CP_FN(_sh_generic_cp_fn) {} + +/* 注册命令 -------------------------------------------------------------------------- */ + +int sh_register_cmd(const sh_cmd_reg_t *sh_reg); // 可用自动初始化宏执行 @ref SH_REGISTER_CMD +int sh_register_cmd_hide(const sh_cmd_reg_t *sh_reg); // 可用自动初始化宏执行 @ref SH_REGISTER_CMD +int sh_unregister_cmd(const sh_cmd_reg_t *sh_reg); // 取消注册一个根命令 + +/* 现成终端协议初始化 ----------------------------------------------------------------- */ + +extern sh_t g_uart_handle_vt100; // 内部默认已初始化的句柄 + +int sh_init_vt100(sh_t *sh_hdl, sh_vprint_fn vprint, sh_disconnect_fn disconnect); // 初始化一个新的 VT100 的终端对象 + +void sh_config_history_mem(sh_t *sh_hdl, void *mem, int size); // 选用:配置用于记录历史的缓存 +void sh_config_backup_mem(sh_t *sh_hdl, void *mem, int size); // 选用:配置用于保存 sh_ctrl_del_word(), sh_ctrl_del_left(), sh_ctrl_del_right() 删除的内容 + +/* 执行过程 --------------------------------------------------------------------------- */ + +void sh_putc(sh_t *sh_hdl, char c); // 记录一个字符到内部缓存中并解析和执行 +void sh_putstr(sh_t *sh_hdl, const char *str); // 同 sh_putc() + +void sh_putstr_quiet(sh_t *sh_hdl, const char *str); // 同 sh_putstr() 但不会回显 + +void sh_set_prompt(sh_t *sh_hdl, const char *prompt); // 设置提示符 + +void sh_reset_line(sh_t *sh_hdl); // 清除命令接收缓存 + +int sh_echo(sh_t *sh_hdl, const char *fmt, ...); // 回显到终端 + +sh_parse_t sh_parse_value(const char *str); // 参数工具:解析字符串表示的数值 + +int sh_merge_param(char *dst, int len, int argc, const char *argv[]); // 参数工具:以空格为分隔符,合并多个参数 + +int sh_get_cmd_result(sh_t *sh_hdl); // 获取上一条有效命令的返回值 + +void sh_refresh_line(sh_t *sh_hdl); // 执行刷一次当前命令行显示 + +/* cp_fn 应用 ---------------------------------------------------------------------- */ + +sh_cmd_info_t sh_cmd_info(sh_t *sh_hdl, int argc, const char *argv[]); // 根据参数,获取完全匹配的命令信息 + +sh_cp_op_t sh_completion_cmd(sh_t *sh_hdl, int argc, const char *argv[]); // 根据参数,在已注册命令中查找并自动补全命令 + +sh_cp_op_t sh_completion_param(sh_t *sh_hdl, const sh_cp_param_t *param); // 仅在 cp_fn() 中可用,根据当前命令行内容,在给出的参数结构中查找并自动补全参数。参数允许为 NULL + +void sh_completion_resource(sh_t *sh_hdl, const char *arg_str, const char *res_str, const char *help); // 仅在 cp_fn() 中可用,根据当前命令行内容,逐个给出可供查找的提示符。内部将根据所提供的数据自动补全 +sh_cp_op_t sh_get_cp_result(sh_t *sh_hdl); // 配合 sh_completion_resource() 使用,在下次进入 cp_fn() 时获取前一次自动补全的执行效果 + +/* 热键功能同步 ----------------------------------------------------------------------- */ + +int sh_register_port(sh_t *sh_hdl, const sh_port_t *port); // 配置终端的控制接口 +int sh_register_key(sh_t *sh_hdl, const sh_key_reg_t *sh_key); // 注册一组热键 +int sh_unregister_key(sh_t *sh_hdl, const sh_key_reg_t *sh_key); // 取消注册一组热键 +int sh_register_key_cmd(sh_t *sh_hdl, const sh_cmd_reg_t *sh_reg); // 注册仅属于对应的终端可见的根命令 @ref SH_REGISTER_CMD +int sh_unregister_key_cmd(sh_t *sh_hdl, const sh_cmd_reg_t *sh_reg); // 取消注册仅属于对应的终端可见的根命令 + +void sh_ctrl_tab(sh_t *sh_hdl); // 同步热键功能:自动实例/打印命令 +bool sh_ctrl_enter(sh_t *sh_hdl); // 同步热键功能:执行已缓存到的命令 +bool sh_ctrl_delete(sh_t *sh_hdl, int n); // 同步热键功能:删除光标的后 n 个字符。返回是否成功 +bool sh_ctrl_backspace(sh_t *sh_hdl, int n); // 同步热键功能:删除光标的前 n 个字符。返回是否成功 +bool sh_ctrl_up(sh_t *sh_hdl); // 同步热键功能:查看上一个命令,并更新光标位置记录。返回是否成功 +bool sh_ctrl_down(sh_t *sh_hdl); // 同步热键功能:查看下一个命令,并更新光标位置记录。返回是否成功 +void sh_ctrl_left(sh_t *sh_hdl); // 同步热键功能:光标左移,并更新光标位置记录 +void sh_ctrl_right(sh_t *sh_hdl); // 同步热键功能:光标右移,并更新光标位置记录 +void sh_ctrl_home(sh_t *sh_hdl); // 同步热键功能:光标移到开头位置,并更新光标位置记录 +void sh_ctrl_end(sh_t *sh_hdl); // 同步热键功能:光标移到结尾位置,并更新光标位置记录 +void sh_ctrl_set_cursor(sh_t *sh_hdl, int pos); // 同步热键功能:光标移到指定位置(不包括提示符,起始位置为0),并更新光标位置记录 +void sh_ctrl_word_head(sh_t *sh_hdl); // 同步热键功能:光标移到当前单词的开头,并更新光标位置记录 +void sh_ctrl_word_tail(sh_t *sh_hdl); // 同步热键功能:光标移到当前单词的结尾,并更新光标位置记录 +void sh_ctrl_print_cmd_line(sh_t *sh_hdl); // 同步热键功能:打印一次命令行内容 +unsigned sh_ctrl_del_word(sh_t *sh_hdl); // 同步热键功能:擦除光标前的单词内容。返回:成功擦除的字符数 +unsigned sh_ctrl_del_left(sh_t *sh_hdl); // 同步热键功能:擦除光标前到行首的全部内容。返回:成功擦除的字符数 +unsigned sh_ctrl_del_right(sh_t *sh_hdl); // 同步热键功能:擦除光标后到行尾的全部内容。返回:成功擦除的字符数 +bool sh_ctrl_undelete(sh_t *sh_hdl); // 同步热键功能:插入 sh_ctrl_del_word(), sh_ctrl_del_left(), sh_ctrl_del_right() 擦除的内容。返回是否成功 +bool sh_ctrl_is_module(sh_t *sh_hdl); // 同步热键功能:查询当前是否使用了 select 命令进入的模块模式(快捷命令模式) + +void sh_ctrl_set_input_pos(sh_t *sh_hdl, uint16_t input_pos); // 设置当前指针(光标)位置(不包括提示符和模块提示),起始位置为0 +uint16_t sh_ctrl_get_input_pos(sh_t *sh_hdl); // 获取当前指针(光标)位置(不包括提示符和模块提示),起始位置为0 + +uint16_t sh_ctrl_get_line_pos(sh_t *sh_hdl); // 根据当前显示内容(包含回翻的历史记录),获取当前光标位置(提示符+模块提示+命令) +uint16_t sh_ctrl_get_line_len(sh_t *sh_hdl); // 根据当前显示内容(包含回翻的历史记录),获取最大光标位置(提示符+模块提示+命令) +char sh_ctrl_get_line_char(sh_t *sh_hdl, uint16_t pos); // 获取命令行部分的字符(提示符+模块提示+命令) + +/* 其他 ------------------------------------------------------------------------------- */ + +void sh_register_external(sh_vprint_fn out); // 用于显式初始化内部函数 + +#endif diff --git a/components/system/source/shell/sh_vset.c b/components/system/source/shell/sh_vset.c new file mode 100755 index 0000000..1e59d1c --- /dev/null +++ b/components/system/source/shell/sh_vset.c @@ -0,0 +1,1521 @@ +/** + * @file sh_vset.h.c + * @author LokLiang + * @brief sh_vset.h 模块源码 + * @version 0.1 + * @date 2023-09-22 + * + * @copyright Copyright (c) 2023 + * + */ + +#include "sh_vset.h" +#include + +#undef CONFIG_SYS_LOG_LEVEL +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF +#define SYS_LOG_DOMAIN "VSET" +#define CONS_ABORT() +#include "sys_log.h" + +#define __ARR_SIZE(ARR) (sizeof(ARR) / sizeof((ARR)[0])) + +#define _TYPE_FLAG_UNSIGNED (1 << 0) +#define _TYPE_FLAG_INTEGER (1 << 1) +#define _TYPE_FLAG_FLOAT (1 << 2) +#define _TYPE_FLAG_STRING (1 << 3) + +#define _PARAM_CB() \ + do \ + { \ + if (param->cb) \ + param->cb(s_sh_hdl, &value); \ + } while (0) + +static sh_t *s_sh_hdl; +static vset_global_cb s_global_cb; + +static bool _is_in_option(const char *input, const sh_vset_param_t *p, unsigned size); +static const sh_vset_param_t *_find_option(const char *input, const sh_vset_param_t *p, unsigned size); +static bool _do_completing_option(sh_t *sh_hdl, int argc, const char *argv[], const sh_vset_param_t *p, unsigned size); +static bool _do_completing_enum(const char *option, const sh_vset_param_t *p, unsigned size); + +static int _set_dest_unsigned(const __vset_param_t *param, unsigned int value, unsigned int low, unsigned int high); +static int _set_dest_integer(const __vset_param_t *param, signed int value, signed int low, signed int high); +static int _set_dest_float(const __vset_param_t *param, float value, float low, float high); +static int _set_dest_string(const __vset_param_t *param, const char *str, unsigned bufsize); +static int _set_dest_enum(const __vset_param_t *param, const char *value, int type_flag); + +static unsigned int _get_unsigned(const __vset_param_t *param); +static signed int _get_integer(const __vset_param_t *param); +static float _get_float(const __vset_param_t *param); +static char *_get_string(const __vset_param_t *param); +static int _enum_format(char *enum_buf, int buf_len, const char *enum_str); +static int _get_enum_by_key(const char *key, char **match_value, char *enum_buf); +static int _get_enum_by_param(const __vset_param_t *param, char **match_key, char **match_value, char *enum_buf, bool print_list, bool print_cp); + +/** + * @brief s_attr_to_type_tbl + * 由变量的具体类型转换到可用于参数解析的范围 + */ +static int const s_attr_to_type_tbl[] = { + [__TYPE_ATTR_CHR] = _PARSE_TYPE_INTEGER, + [__TYPE_ATTR_BOOL] = _PARSE_TYPE_INTEGER, + [__TYPE_ATTR_U8] = _PARSE_TYPE_UNSIGNED, + [__TYPE_ATTR_S8] = _PARSE_TYPE_INTEGER, + [__TYPE_ATTR_U16] = _PARSE_TYPE_UNSIGNED, + [__TYPE_ATTR_S16] = _PARSE_TYPE_INTEGER, + [__TYPE_ATTR_U32] = _PARSE_TYPE_UNSIGNED, + [__TYPE_ATTR_S32] = _PARSE_TYPE_INTEGER, + [__TYPE_ATTR_U64] = _PARSE_TYPE_UNSIGNED, + [__TYPE_ATTR_S64] = _PARSE_TYPE_INTEGER, + [__TYPE_ATTR_FLOAT] = _PARSE_TYPE_FLOAT, + [__TYPE_ATTR_DOUBLE] = _PARSE_TYPE_FLOAT, + [__TYPE_ATTR_STR] = _PARSE_TYPE_STRING, +}; + +/** + * @brief 初始化设置 + * + * @param sh_hdl 设置 shell 的句柄 + * @param cb 当任意一个值被修改时回调此函数,可设为 NULL + */ +void vset_init(sh_t *sh_hdl, vset_global_cb cb) +{ + s_sh_hdl = sh_hdl; + s_global_cb = cb; +} + +/** + * @brief 直接执行一次 vset_init() 所定义的回调函数 + * + */ +void vset_force_cb(void) +{ + vset_global_cb cb = s_global_cb; + if (cb) + { + cb(s_sh_hdl); + } +} + +/** + * @brief 一次性根据选项结构 p 解析 argv, 自动选择对应的设置函数进行对变量的设置。 + * 这将导致输入的参数 argc 和 argv 被更新。 + * + * @details + * 这将一将性解析全部符合 p 内部符合的参数,并执行对应的处理函数,成功设置的 argc 和 argv 会被删除。 + * 注意如果有任何处理失败的函数不会立即返回,已成功设置的值也不会被恢复,直到全部 argv 被解析过一次后返回。 + * + * @param sh_hdl 原参数 + * @param argc[i/o] 指向原参数,可能被更新 + * @param argv[i/o] 原参数指针值,可能被更新 + * @param p 选项描述结构体 + * @param size 选项描述结构体的长度 + * @retval 0 所有存在于 sh_vset_param_t::option 的选项被正确设置 + * @retval != 0 首个设置错误的 sh_vset_param_t::option + */ +int vset_option_set(sh_t *sh_hdl, int *argc, const char *argv[], const sh_vset_param_t *p, unsigned size) +{ + int argc_old = *argc; + const char *err_option = NULL; + + /* 遍历输入参数 argv 在 p 中找到匹配的 p[i].option 对应的 p[i].set_func 并执行 */ + for (int i = 0; i < argc_old; i++) + { + for (int j = 0; j < size / sizeof(*p); j++) + { + const sh_vset_param_t *pset = &p[j]; + if (strcmp(argv[i], pset->option) == 0) // 匹配到选项 + { + if (pset->set_func) // 该选有对应的设置函数 + { + bool flag = false; + argv[i] = NULL; + + const char *str[] = { + "", + "", + }; + if (i + 1 < argc_old) + { + ++i; + str[0] = argv[i]; + argv[i] = NULL; + + if (strcmp("?", str[0]) == 0) + { + flag = true; + sh_echo(s_sh_hdl, "选项: %s\r\n", pset->option); + } + } + + int ret = pset->set_func(str); + if (flag) + { + sh_echo(s_sh_hdl, "========\r\n"); + } + if (ret != 0) + { + if (err_option == NULL) + { + err_option = pset->option; + } + sh_echo(s_sh_hdl, "( 选项 %s )\r\n", pset->option); + sh_echo(s_sh_hdl, "========\r\n"); + } + } + + break; + } + } + } + + /* 更新已被处理的输入参数 argc 和 argv */ + int argc_new = 0; + for (int i = 0; i < argc_old; i++) + { + if (argv[i] && *argv[i] != '\0') + { + argv[argc_new++] = argv[i]; // 更新 argv + } + } + *argc = argc_new; // 更新 argc + while (argc_new < argc_old) + { + argv[argc_new++] = NULL; + } + + return (int)err_option; +} + +/** + * @brief 为 cp_fn 执行与 set_func 对应的参数补全动作 + * + * @param sh_hdl 原参数 + * @param argc 原参数 + * @param argv 原参数指针值,可能被更新 + * @param flag 原参数 + * @param p 选项描述结构体 + * @param size 选项描述结构体的长度 + * @retval true 有补全动作, cp_fn 函数不需要进行任何动作 + * @retval false 未补全动作, cp_fn 函数可自行执行补全 + */ +bool vset_option_cp(sh_t *sh_hdl, int argc, const char *argv[], bool flag, const sh_vset_param_t *p, unsigned size) +{ + if (flag == false) // 一个参数正在输入 + { + if (argc > 0 && _is_in_option(argv[argc - 1], p, size)) // 前正在输入的参数有对应存在的候选选项 + { + _do_completing_option(sh_hdl, argc, argv, p, size); // 执行对选项的补全 + return true; + } + else if (argc > 1) + { + return _do_completing_enum(argv[argc - 2], p, size); // 执行对 SET_ENUM 中的 ENUM_STR 的补全 + } + else + { + return false; + } + } + else // 当前参数未有任何输入 + { + const sh_vset_param_t *pset; + if (argc > 0 && (pset = _find_option(argv[argc - 1], p, size)) != NULL) // 找到上一个输入的参数的对应选项 + { + vset_cp_enum(1, false, pset->enum_str); + return true; + } + else + { + _do_completing_option(sh_hdl, argc, argv, p, size); // 执行对选项的补全 + return true; + } + } +} + +int vset_unsigned(const __vset_param_t *param, const char *argv[], unsigned int low, unsigned int high) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[param->attr] == _PARSE_TYPE_UNSIGNED, "待设置的参数类型不是正整数"); + + if (s_sh_hdl == NULL) + { + return -1; + } + if (param->attr >= __ARR_SIZE(s_attr_to_type_tbl)) + { + sh_echo(s_sh_hdl, "param->attr 的值不在 __type_attr_t 所定义的范围\r\n"); + return -1; + } + if (s_attr_to_type_tbl[param->attr] != _PARSE_TYPE_UNSIGNED) + { + sh_echo(s_sh_hdl, "待设置的参数类型不是正整数\r\n"); + return -1; + } + + if (argv[0] == NULL || *argv[0] == '\0') + { + sh_echo(s_sh_hdl, "缺少参数\r\n"); + return -1; + } + + if (strcmp(argv[0], "?") == 0) + { + if (high < low) + { + low ^= high; + high ^= low; + low ^= high; + } + sh_echo(s_sh_hdl, "有效范围: %d .. %d\r\n", low, high); + sh_echo(s_sh_hdl, "当前值: %u\r\n", _get_unsigned(param)); + return 0; + } + + sh_parse_t pv = sh_parse_value(argv[0]); + switch (pv.type) + { + case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型 + return _set_dest_unsigned(param, pv.value.val_unsigned, low, high); + + default: + sh_echo(s_sh_hdl, "参数 '%s' 格式错误, 请输入正整数\r\n", argv[0]); + return -1; + } +} + +int vset_integer(const __vset_param_t *param, const char *argv[], signed int low, signed int high) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[param->attr] == _PARSE_TYPE_INTEGER, "待设置的参数类型不是带符号整数"); + + if (s_sh_hdl == NULL) + { + return -1; + } + if (param->attr >= __ARR_SIZE(s_attr_to_type_tbl)) + { + sh_echo(s_sh_hdl, "param->attr 的值不在 __type_attr_t 所定义的范围\r\n"); + return -1; + } + if (s_attr_to_type_tbl[param->attr] != _PARSE_TYPE_INTEGER) + { + sh_echo(s_sh_hdl, "待设置的参数类型不是带符号整数\r\n"); + return -1; + } + + if (argv[0] == NULL || *argv[0] == '\0') + { + sh_echo(s_sh_hdl, "缺少参数\r\n"); + return -1; + } + + if (strcmp(argv[0], "?") == 0) + { + if (high < low) + { + low ^= high; + high ^= low; + low ^= high; + } + sh_echo(s_sh_hdl, "有效范围: %d .. %d\r\n", low, high); + sh_echo(s_sh_hdl, "当前值: %d\r\n", _get_integer(param)); + return 0; + } + + sh_parse_t pv = sh_parse_value(argv[0]); + switch (pv.type) + { + case _PARSE_TYPE_INTEGER: // 解析出的参数格式是带符号整型 + return _set_dest_integer(param, pv.value.val_integer, low, high); + + case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型 + if (pv.value.val_unsigned > (1u << (sizeof(pv.value.val_unsigned) * 8 - 1)) - 1) + { + sh_echo(s_sh_hdl, "参数 %u 溢出\r\n", pv.value.val_unsigned); + return -1; + } + return _set_dest_integer(param, pv.value.val_unsigned, low, high); + + default: + sh_echo(s_sh_hdl, "参数 '%s' 格式错误, 请输入整数\r\n", argv[0]); + return -1; + } +} + +int vset_float(const __vset_param_t *param, const char *argv[], float low, float high) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[param->attr] == _PARSE_TYPE_FLOAT, "待设置的参数类型不是浮点数"); + + if (s_sh_hdl == NULL) + { + return -1; + } + if (param->attr >= __ARR_SIZE(s_attr_to_type_tbl)) + { + sh_echo(s_sh_hdl, "param->attr 的值不在 __type_attr_t 所定义的范围\r\n"); + return -1; + } + if (s_attr_to_type_tbl[param->attr] != _PARSE_TYPE_FLOAT) + { + sh_echo(s_sh_hdl, "待设置的参数类型不是浮点数\r\n"); + return -1; + } + + if (argv[0] == NULL || *argv[0] == '\0') + { + sh_echo(s_sh_hdl, "缺少参数\r\n"); + return -1; + } + + if (strcmp(argv[0], "?") == 0) + { + if (high < low) + { + float tmp = low; + low = high; + high = tmp; + } + sh_echo(s_sh_hdl, "有效范围: %f .. %f\r\n", low, high); + sh_echo(s_sh_hdl, "当前值: %f\r\n", _get_float(param)); + return 0; + } + + sh_parse_t pv = sh_parse_value(argv[0]); + switch (pv.type) + { + case _PARSE_TYPE_INTEGER: // 解析出的参数格式是带符号整型 + return _set_dest_float(param, pv.value.val_integer, low, high); + + case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型 + return _set_dest_float(param, pv.value.val_unsigned, low, high); + + case _PARSE_TYPE_FLOAT: // 解析出的参数格式是浮点数 + return _set_dest_float(param, pv.value.val_float, low, high); + + default: + sh_echo(s_sh_hdl, "参数 '%s' 格式错误, 请输入数值\r\n", argv[0]); + return -1; + } +} + +int vset_str(const __vset_param_t *param, const char *argv[], unsigned bufsize) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[param->attr] == _PARSE_TYPE_STRING, "待设置的参数类型不是字符串"); + + if (s_sh_hdl == NULL) + { + return -1; + } + if (param->attr >= __ARR_SIZE(s_attr_to_type_tbl)) + { + sh_echo(s_sh_hdl, "param->attr 的值不在 __type_attr_t 所定义的范围\r\n"); + return -1; + } + if (s_attr_to_type_tbl[param->attr] != _PARSE_TYPE_STRING) + { + sh_echo(s_sh_hdl, "待设置的参数类型不是字符串\r\n"); + return -1; + } + + if (argv[0] == NULL || *argv[0] == '\0') + { + sh_echo(s_sh_hdl, "缺少参数\r\n"); + return -1; + } + + if (strcmp(argv[0], "?") == 0) + { + sh_echo(s_sh_hdl, "最大长度: %d\r\n", bufsize - 1); + sh_echo(s_sh_hdl, "当前值: %s\r\n", _get_string(param)); + return 0; + } + + if (argv[1] && *argv[1] != '\0') + { + sh_echo(s_sh_hdl, "参数中有空格。如果目标设备名中包含空格,请使用引号表示\r\n"); + return -1; + } + + return _set_dest_string(param, argv[0], bufsize); +} + +int vset_enum(const __vset_param_t *param, const char *argv[], const char *enum_str) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + + if (s_sh_hdl == NULL) + { + return -1; + } + if (param->attr >= __ARR_SIZE(s_attr_to_type_tbl)) + { + sh_echo(s_sh_hdl, "param->attr 的值不在 __type_attr_t 所定义的范围\r\n"); + return -1; + } + + if (argv[0] == NULL || *argv[0] == '\0') + { + sh_echo(s_sh_hdl, "缺少参数\r\n"); + return -1; + } + + /* 格式化 enum_str 到 enum_buf */ + char enum_buf[CONFIG_SH_MAX_LINE_LEN]; + int enum_len = _enum_format(enum_buf, __ARR_SIZE(enum_buf), enum_str); + if (enum_len < 0) + { + return -1; + } + + char *match_key = NULL; + char *match_value = NULL; + int type_flag = 0; + + if (strcmp(argv[0], "?") == 0) + { + sh_echo(s_sh_hdl, "可选值:\r\n"); + _get_enum_by_param(param, &match_key, &match_value, enum_buf, true, false); + if (match_value) + { + sh_echo(s_sh_hdl, "当前值:\t%s\r\n", match_key); + } + return 0; + } + + type_flag = _get_enum_by_key(argv[0], &match_value, enum_buf); + if (type_flag == 0) + { + sh_echo(s_sh_hdl, "选项为空\r\n"); + return -1; + } + if (match_value == NULL) + { + sh_echo(s_sh_hdl, "'%s': 非法的取值\r\n", argv[0]); + return -1; + } + + return _set_dest_enum(param, match_value, type_flag); +} + +void vset_cp_enum(int argc, bool flag, const char *enum_str) +{ + if (argc + flag == 1) + { + if (enum_str != NULL) + { + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + + if (s_sh_hdl == NULL) + { + return; + } + + /* 格式化 enum_str 到 enum_buf */ + char enum_buf[CONFIG_SH_MAX_LINE_LEN]; + int enum_len = _enum_format(enum_buf, __ARR_SIZE(enum_buf), enum_str); + if (enum_len < 0) + { + return; + } + + __vset_param_t param = { + .dest = enum_buf, + .attr = __GENERIC_ATTR(enum_buf), + }; + + /* 分割字符串 str_buf 并穷举匹配到的值,输出 match_key, match_value, type_flag */ + char *match_key = NULL; + char *match_value = NULL; + _get_enum_by_param(¶m, &match_key, &match_value, enum_buf, false, true); + } + else + { + sh_completion_resource(s_sh_hdl, NULL, "? ", NULL); + } + } +} + +/** + * @brief 内部函数,由 vset_option_cp() 专用,测试前正在输入的参数有对应存在的候选选项 + * + * @param input 当前正在输入的参数 + * @param p 选项描述结构体 + * @param size 选项描述结构体的长度 + * @retval true 当前正在输入的参数存在于候选选项中 + * @retval false 当前正在输入的参数不在候选选项中 + */ +static bool _is_in_option(const char *input, const sh_vset_param_t *p, unsigned size) +{ + int len = strlen(input); + for (int i = 0; i < size / sizeof(*p); i++) + { + if (strncmp(input, p[i].option, len) == 0) + { + return true; + } + } + return false; +} + +/** + * @brief 内部函数,由 vset_option_cp() 专用,查找输入的参数的对应选项 + * @brief + * + * @param input + * @param p + * @param size + * @return const sh_vset_param_t* + */ +static const sh_vset_param_t *_find_option(const char *input, const sh_vset_param_t *p, unsigned size) +{ + for (int i = 0; i < size / sizeof(*p); i++) + { + if (strcmp(input, p[i].option) == 0) + { + return &p[i]; + } + } + return NULL; +} + +/** + * @brief 内部函数,由 vset_option_cp() 专用,执行对选项的补全 + * + * @param sh_hdl 原参数 + * @param argc 原参数 + * @param argv 原参数指针值,可能被更新 + * @param p 选项描述结构体 + * @param size 选项描述结构体的长度 + * @retval true + */ +static bool _do_completing_option(sh_t *sh_hdl, int argc, const char *argv[], const sh_vset_param_t *p, unsigned size) +{ + bool ret = false; + + for (int i = 0; i < size / sizeof(*p); i++) // 遍历 p[i].option 分析 + { + /* 遍历已输入的参数确认当前的 p[i].option 未曾出现过 */ + bool repeat_flag = false; + for (int j = 0; j < argc - 1; j++) + { + if (strcmp(p[i].option, argv[j]) == 0) + { + repeat_flag = true; + break; + } + } + + if (repeat_flag == false) + { + /* 执行 sh_completion_resource() */ + char option_cpy[0x100]; + int len = 0; + for (len = 0; len < sizeof(option_cpy) - 2; len++) + { + option_cpy[len] = p[i].option[len]; + if (option_cpy[len] == '\0') + { + break; + } + } + option_cpy[len++] = ' '; + option_cpy[len] = '\0'; + sh_completion_resource(sh_hdl, NULL, option_cpy, p[i].help); + ret = true; + } + } + + return ret; +} + +/** + * @brief 内部函数,由 vset_option_cp() 专用,执行对 SET_ENUM 中的 ENUM_STR 的补全 + * + * @param option 已知的选项 + * @param p 选项描述结构体 + * @param size 选项描述结构体的长度 + */ +static bool _do_completing_enum(const char *option, const sh_vset_param_t *p, unsigned size) +{ + /* 根据上一个参数,遍历 p 找到对应的成员 */ + if (option) + { + for (int i = 0; i < size / sizeof(*p); i++) + { + if (strcmp(option, p[i].option) == 0) + { + if (p[i].set_func) + { + vset_cp_enum(1, false, p[i].enum_str); + } + + return true; + } + } + } + + return false; +} + +static int _set_dest_unsigned(const __vset_param_t *param, unsigned int value, unsigned int low, unsigned int high) +{ + void *dest = param->dest; + __type_attr_t attr = param->attr; + vset_global_cb cb = NULL; + int ret = -1; + + SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_UNSIGNED, "待设置的参数类型不是正整数"); + + if (value < low) + { + sh_echo(s_sh_hdl, "设置失败: %d < %d(min)\r\n", value, low); + return -1; + } + + if (value > high) + { + sh_echo(s_sh_hdl, "设置失败: %d > %d(max)\r\n", value, high); + return -1; + } + + switch (attr) + { + case __TYPE_ATTR_U8: + { + SYS_ASSERT((low & (~0u << 8)) == 0 && (high & (~0u << 8)) == 0, "范围值溢出"); + if ((low & (~0u << 8)) || (high & (~0u << 8))) + { + sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n"); + return -1; + } + if (*((uint8_t *)dest) != value) + { + _PARAM_CB(); + *((uint8_t *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + case __TYPE_ATTR_U16: + { + SYS_ASSERT((low & (~0u << 16)) == 0 && (high & (~0u << 16)) == 0, "范围值溢出"); + if ((low & (~0u << 16)) || (high & (~0u << 16))) + { + sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n"); + return -1; + } + if (*((uint16_t *)dest) != value) + { + _PARAM_CB(); + *((uint16_t *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + case __TYPE_ATTR_U32: + { + if (*((uint32_t *)dest) != value) + { + _PARAM_CB(); + *((uint32_t *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + case __TYPE_ATTR_U64: + { + if (*((uint64_t *)dest) != value) + { + _PARAM_CB(); + *((uint64_t *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + default: + return -1; + } + + if (cb) + { + cb(s_sh_hdl); + } + return ret; +} + +static int _set_dest_integer(const __vset_param_t *param, signed int value, signed int low, signed int high) +{ +#define _MAX_S8 127 +#define _MIN_S8 -128 +#define _MAX_S16 32767 +#define _MIN_S16 -32768 + + void *dest = param->dest; + __type_attr_t attr = param->attr; + vset_global_cb cb = NULL; + int ret = -1; + + SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_INTEGER, "待设置的参数类型不是带符号整数"); + + if (value < low) + { + sh_echo(s_sh_hdl, "设置失败: %d < %d(min)\r\n", value, low); + return -1; + } + + if (value > high) + { + sh_echo(s_sh_hdl, "设置失败: %d > %d(max)\r\n", value, high); + return -1; + } + + switch (attr) + { + case __TYPE_ATTR_CHR: + { + char data = value; + if (data != value) + { + sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n"); + return -1; + } + if (*((char *)dest) != data) + { + _PARAM_CB(); + *((char *)dest) = data; + cb = s_global_cb; + } + ret = 0; + break; + } + + case __TYPE_ATTR_BOOL: + { + bool data = value; + if (data != value) + { + sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n"); + return -1; + } + if (*((bool *)dest) != data) + { + _PARAM_CB(); + *((bool *)dest) = data; + cb = s_global_cb; + } + ret = 0; + break; + } + + case __TYPE_ATTR_S8: + { + SYS_ASSERT(low >= _MIN_S8 && low <= _MAX_S8, "范围值溢出"); + SYS_ASSERT(high >= _MIN_S8 && high <= _MAX_S8, "范围值溢出"); + if (low < _MIN_S8 || low > _MAX_S8 || + high < _MIN_S8 || high > _MAX_S8) + { + sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n"); + return -1; + } + if (*((int8_t *)dest) != value) + { + _PARAM_CB(); + *((int8_t *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + case __TYPE_ATTR_S16: + { + SYS_ASSERT(low >= _MIN_S16 && low <= _MAX_S16, "范围值溢出"); + SYS_ASSERT(high >= _MIN_S16 && high <= _MAX_S16, "范围值溢出"); + if (low < _MIN_S16 || low > _MAX_S16 || + high < _MIN_S16 || high > _MAX_S16) + { + sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n"); + return -1; + } + if (*((int16_t *)dest) != value) + { + _PARAM_CB(); + *((int16_t *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + case __TYPE_ATTR_S32: + { + if (*((int32_t *)dest) != value) + { + _PARAM_CB(); + *((int32_t *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + case __TYPE_ATTR_S64: + { + if (*((int64_t *)dest) != value) + { + _PARAM_CB(); + *((int64_t *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + default: + return -1; + } + + if (cb) + { + cb(s_sh_hdl); + } + return ret; +} + +static int _set_dest_float(const __vset_param_t *param, float value, float low, float high) +{ + void *dest = param->dest; + __type_attr_t attr = param->attr; + vset_global_cb cb = NULL; + int ret = -1; + + SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_FLOAT, "待设置的参数类型不是浮点数"); + + if (value < low) + { + sh_echo(s_sh_hdl, "设置失败: %f < %f(min)\r\n", value, low); + return -1; + } + + if (value > high) + { + sh_echo(s_sh_hdl, "设置失败: %f > %f(max)\r\n", value, high); + return -1; + } + + switch (attr) + { + case __TYPE_ATTR_FLOAT: + { + if (*((float *)dest) != value) + { + _PARAM_CB(); + *((float *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + case __TYPE_ATTR_DOUBLE: + { + if (*((double *)dest) != value) + { + _PARAM_CB(); + *((double *)dest) = value; + cb = s_global_cb; + } + ret = 0; + break; + } + + default: + return -1; + } + + if (cb) + { + cb(s_sh_hdl); + } + return ret; +} + +static int _set_dest_string(const __vset_param_t *param, const char *str, unsigned bufsize) +{ + void *dest = param->dest; + int str_size = strlen(str) + 1; + __type_attr_t attr = param->attr; + vset_global_cb cb = NULL; + int ret = -1; + + SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_STRING, "待设置的参数类型不是字符串"); + + if (str_size > bufsize) + { + sh_echo(s_sh_hdl, "注意: '%s' 大于最大长度 %d\r\n", str, bufsize - 1); + } + + switch (attr) + { + case __TYPE_ATTR_STR: + memset(dest, 0, bufsize); + if (memcmp(dest, str, str_size)) + { + if (param->cb) + { + char value[0x100]; + memcpy(dest, str, str_size); + _PARAM_CB(); + } + memcpy(dest, str, str_size); + cb = s_global_cb; + } + ret = 0; + break; + + default: + return -1; + } + + if (cb) + { + cb(s_sh_hdl); + } + return ret; +} + +static int _set_dest_enum(const __vset_param_t *param, const char *value, int type_flag) +{ + sh_parse_t pv = sh_parse_value(value); + switch (s_attr_to_type_tbl[param->attr]) + { + case _PARSE_TYPE_UNSIGNED: // 待设置的参数格式是无符号整型 + { + if (type_flag & (_TYPE_FLAG_INTEGER | _TYPE_FLAG_FLOAT | _TYPE_FLAG_STRING)) + { + sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n"); + return -1; + } + switch (pv.type) + { + case _PARSE_TYPE_UNSIGNED: // 当前匹配的参数格式是无符号整型 + return _set_dest_unsigned(param, pv.value.val_unsigned, pv.value.val_unsigned, pv.value.val_unsigned); + + default: + return -1; + } + } + break; + + case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型 + { + if (type_flag & (_TYPE_FLAG_FLOAT | _TYPE_FLAG_STRING)) + { + sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n"); + return -1; + } + switch (pv.type) + { + case _PARSE_TYPE_INTEGER: // 当前匹配的参数格式是带符号整型 + return _set_dest_integer(param, pv.value.val_integer, pv.value.val_integer, pv.value.val_integer); + + case _PARSE_TYPE_UNSIGNED: // 当前匹配的参数格式是无符号整型 + return _set_dest_integer(param, pv.value.val_unsigned, pv.value.val_unsigned, pv.value.val_unsigned); + + default: + return -1; + } + } + break; + + case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数 + { + if (type_flag & (_TYPE_FLAG_STRING)) + { + sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n"); + return -1; + } + switch (pv.type) + { + case _PARSE_TYPE_INTEGER: // 当前匹配的参数格式是带符号整型 + return _set_dest_float(param, pv.value.val_integer, pv.value.val_integer, pv.value.val_integer); + + case _PARSE_TYPE_UNSIGNED: // 当前匹配的参数格式是无符号整型 + return _set_dest_float(param, pv.value.val_unsigned, pv.value.val_unsigned, pv.value.val_unsigned); + + case _PARSE_TYPE_FLOAT: // 当前匹配的参数格式是浮点数 + return _set_dest_float(param, pv.value.val_float, pv.value.val_float, pv.value.val_float); + + default: + return -1; + } + } + break; + + case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串 + { + return _set_dest_string(param, value, strlen(value) + 1); + } + break; + + default: + sh_echo(s_sh_hdl, "param->attr 的值不在 __type_attr_t 所定义的范围\r\n"); + return -1; + } + + return -1; +} + +static signed int _get_integer(const __vset_param_t *param) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[param->attr] == _PARSE_TYPE_INTEGER, "待设置的参数类型不是带符号整数"); + + switch (param->attr) + { + case __TYPE_ATTR_CHR: + return *((char *)param->dest); + + case __TYPE_ATTR_BOOL: + return *((bool *)param->dest); + + case __TYPE_ATTR_S8: + return *((int8_t *)param->dest); + + case __TYPE_ATTR_S16: + return *((int16_t *)param->dest); + + case __TYPE_ATTR_S32: + return *((int32_t *)param->dest); + + case __TYPE_ATTR_S64: + return *((int64_t *)param->dest); + + default: + return -1; + } +} + +static float _get_float(const __vset_param_t *param) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[param->attr] == _PARSE_TYPE_FLOAT, "待设置的参数类型不是浮点数"); + + switch (param->attr) + { + case __TYPE_ATTR_FLOAT: + return *((float *)param->dest); + + case __TYPE_ATTR_DOUBLE: + return *((double *)param->dest); + + default: + return -1; + } +} + +static char *_get_string(const __vset_param_t *param) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[param->attr] == _PARSE_TYPE_STRING, "待设置的参数类型不是字符串"); + + return param->dest; +} + +static unsigned int _get_unsigned(const __vset_param_t *param) +{ + SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化"); + SYS_ASSERT(param->attr < __ARR_SIZE(s_attr_to_type_tbl), "param->attr 的值不在 __type_attr_t 所定义的范围"); + SYS_ASSERT(s_attr_to_type_tbl[param->attr] == _PARSE_TYPE_UNSIGNED, "待设置的参数类型不是正整数"); + + switch (param->attr) + { + case __TYPE_ATTR_U8: + return *((uint8_t *)param->dest); + + case __TYPE_ATTR_U16: + return *((uint16_t *)param->dest); + + case __TYPE_ATTR_U32: + return *((uint32_t *)param->dest); + + case __TYPE_ATTR_U64: + return *((uint64_t *)param->dest); + + default: + return -1; + } +} + +/** + * @brief 格式化原始的 enum_str 并输出到 enum_buf + * + * @param enum_buf[out] 复制 enum_str 并格式化的需要的缓存 + * @param buf_len enum_buf 的长度 + * @param enum_str 原始的选项格式 + * @retval < 0 表示 enum_str 的格式错误(键值或数值不能包含空格) + * @retval >= 0 表示输出的 enum_buf 中的字符数(不包含 '\0') + */ +static int _enum_format(char *enum_buf, int buf_len, const char *enum_str) +{ + /* 复制 enum_str 到 str_buf 并移除空格 */ + if (strlen(enum_str) >= buf_len) + { + sh_echo(s_sh_hdl, "注意: str_buff 长度不足\r\n"); + } + strncpy(enum_buf, enum_str, buf_len - 1); + enum_buf[buf_len - 1] = '\0'; + + /* 去除可能的多余空格 */ + buf_len = strlen(enum_buf); + int cid = 0; + int flag1 = 0; + int flag2 = 1; + for (int i = 0; i < buf_len; i++) + { + if (enum_buf[i] > ' ') + { + enum_buf[cid] = enum_str[i]; + cid++; + + if (enum_buf[i] != ',' && enum_buf[i] != '=') + { + flag1 += flag2; + flag1 += !flag1; + if (flag1 > 1) + { + break; + } + } + else + { + flag1 = 0; + } + flag2 = 0; + } + else + { + flag2 = 1; + } + } + if (flag1 > 1) + { + sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 键值或数值不能包含空格\r\n"); + return -1; + } + + enum_buf[cid] = '\0'; + return cid; // 返回字符串长度(不包含结束符) +} + +/** + * @brief 根据给出的 key 在 enum_buf 查找对应的 value + * + * @param key 给出的被搜索的 key + * @param match_value[out] 与 key 中的值完全匹配的 value 位置,该位置在 enum_buf 中,如果结果为 NULL 表示在 enum_buf 中不存在与 param 的值完全匹配的项 + * @param enum_buf 通过 _enum_format() 格式化的缓存 + * @retval int 按位表示在 enum_buf 中出现过的 value 的数据类型。 + * _TYPE_FLAG_UNSIGNED 掩码表示在 enum_buf 中包含无符号整型 + * _TYPE_FLAG_INTEGER 掩码表示在 enum_buf 中包含有符号整型 + * _TYPE_FLAG_FLOAT 掩码表示在 enum_buf 中包含浮点数 + * _TYPE_FLAG_STRING 掩码表示在 enum_buf 中包含字符串 + */ +static int _get_enum_by_key(const char *key, char **match_value, char *enum_buf) +{ + int type_flag = 0; // 在 match_value 中出现过的数据类型 + + /* 分割字符串 enum_buf 并穷举匹配到的值 */ + *match_value = NULL; + char *save_token; + char *token = strtok_r(enum_buf, ",", &save_token); + while (token) + { + char *enum_value; + char *enum_key = strtok_r(token, "=", &enum_value); + + if (enum_key == NULL || *enum_key == '\0' || *enum_value == '\0') + { + sh_echo(s_sh_hdl, "'%s' 的格式错误, 请使用如下格式表示: 'key1=value,key2=value,...'\r\n", token); + return 0; + } + + sh_parse_t pv = sh_parse_value(enum_value); + switch (pv.type) + { + case _PARSE_TYPE_INTEGER: // 解析出的参数格式是带符号整型 + type_flag |= _TYPE_FLAG_INTEGER; + break; + + case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型 + type_flag |= _TYPE_FLAG_UNSIGNED; + break; + + case _PARSE_TYPE_FLOAT: // 解析出的参数格式是浮点数 + type_flag |= _TYPE_FLAG_FLOAT; + break; + + case _PARSE_TYPE_STRING: // 解析出的参数格式是字符串 + type_flag |= _TYPE_FLAG_STRING; + break; + + default: + break; + } + + if (*match_value == NULL) + { + if (strcmp(enum_key, key) == 0) + { + *match_value = enum_value; + } + } + + token = strtok_r(NULL, ",", &save_token); + } + + return type_flag; +} + +/** + * @brief 根据给出的 param 在 enum_buf 查找对应的 key 和 value + * 1. 分析 enum_buf 中存在的所有 value 项的数据类型并作为返回值 + * 2. 根据 param 的值,在格式化数据中搜索与该值完全匹配的 key 和 value 并输出到 *match_key 和 *match_value + * 2. 可选打印 enum_buf 中的 key 和 value + * + * @param param 指向待设置的变量属性的结构体 + * @param match_key[out] 与 param 中的值完全匹配的 key 位置,该位置在 enum_buf 中,如果结果为 NULL 表示在 enum_buf 中不存在与 param 的值完全匹配的项 + * @param match_value[out] 与 param 中的值完全匹配的 value 位置,该位置在 enum_buf 中,如果结果为 NULL 表示在 enum_buf 中不存在与 param 的值完全匹配的项 + * @param enum_buf 通过 _enum_format() 格式化的缓存 + * @param print_list 是否打印所有 enum_buf 中的项目 + * @param print_cp 是否执行自动补全 + * @retval int 按位表示在 enum_buf 中出现过的 value 的数据类型。 + * _TYPE_FLAG_UNSIGNED 掩码表示在 enum_buf 中包含无符号整型 + * _TYPE_FLAG_INTEGER 掩码表示在 enum_buf 中包含有符号整型 + * _TYPE_FLAG_FLOAT 掩码表示在 enum_buf 中包含浮点数 + * _TYPE_FLAG_STRING 掩码表示在 enum_buf 中包含字符串 + */ +static int _get_enum_by_param(const __vset_param_t *param, char **match_key, char **match_value, char *enum_buf, bool print_list, bool print_cp) +{ + char param_value_str[0x100]; + int type_flag = 0; // 在 match_value 中出现过的数据类型 + + if (param->attr >= __ARR_SIZE(s_attr_to_type_tbl)) + { + sh_echo(s_sh_hdl, "param->attr 的值不在 __type_attr_t 所定义的范围\r\n"); + return -1; + } + + switch (s_attr_to_type_tbl[param->attr]) + { + case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型 + SYS_SNPRINT(param_value_str, sizeof(param_value_str), "%d", _get_integer(param)); + break; + + case _PARSE_TYPE_UNSIGNED: // 待设置的参数格式是无符号整型 + SYS_SNPRINT(param_value_str, sizeof(param_value_str), "%u", _get_unsigned(param)); + break; + + case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数 + SYS_SNPRINT(param_value_str, sizeof(param_value_str), "%f", _get_float(param)); + break; + + case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串 + SYS_SNPRINT(param_value_str, sizeof(param_value_str), "%s", _get_string(param)); + break; + + default: + sh_echo(s_sh_hdl, "param->attr 的值不在 __type_attr_t 所定义的范围\r\n"); + return -1; + } + param_value_str[__ARR_SIZE(param_value_str) - 1] = '\0'; + + /* 分割字符串 enum_buf 并穷举匹配到的值 */ + *match_key = NULL; + *match_value = NULL; + char *save_token; + char *token = strtok_r(enum_buf, ",", &save_token); + while (token) + { + char *enum_value; + char *enum_key = strtok_r(token, "=", &enum_value); + char key_value_str[__ARR_SIZE(param_value_str)]; + + if (*enum_key == '\0' || *enum_value == '\0') + { + sh_echo(s_sh_hdl, "'%s' 的格式错误, 请使用如下格式表示: 'key1=value,key2=value,...'\r\n", enum_key); + return 0; + } + + key_value_str[0] = '\0'; + sh_parse_t pv = sh_parse_value(enum_value); + switch (pv.type) + { + case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型 + type_flag |= _TYPE_FLAG_UNSIGNED; + if (*match_key == NULL) + { + switch (s_attr_to_type_tbl[param->attr]) + { + case _PARSE_TYPE_UNSIGNED: // 待设置的参数格式是无符号整型 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%u", pv.value.val_unsigned); + break; + case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%d", pv.value.val_unsigned); + break; + case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%f", (float)pv.value.val_unsigned); + break; + case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%s", enum_value); + break; + default: + break; + } + } + break; + + case _PARSE_TYPE_INTEGER: // 解析出的参数格式是带符号整型 + type_flag |= _TYPE_FLAG_INTEGER; + if (*match_key == NULL) + { + switch (s_attr_to_type_tbl[param->attr]) + { + case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%d", pv.value.val_integer); + break; + case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%f", (float)pv.value.val_integer); + break; + case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%s", enum_value); + break; + default: + break; + } + } + break; + + case _PARSE_TYPE_FLOAT: // 解析出的参数格式是浮点数 + type_flag |= _TYPE_FLAG_FLOAT; + if (*match_key == NULL) + { + switch (s_attr_to_type_tbl[param->attr]) + { + case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%f", (float)pv.value.val_float); + break; + case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串 + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%s", enum_value); + break; + default: + break; + } + } + break; + + case _PARSE_TYPE_STRING: // 解析出的参数格式是字符串 + type_flag |= _TYPE_FLAG_STRING; + if (*match_key == NULL) + SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%s", enum_value); + break; + + default: + break; + } + + if (*match_key == NULL) + { + if (strcmp(key_value_str, param_value_str) == 0) + { + *match_key = enum_key; + *match_value = enum_value; + } + } + + if (print_list) + { + sh_echo(s_sh_hdl, "\t%s (%s)\r\n", enum_key, enum_value); + } + + if (print_cp) + { + char key_cpy[0x100]; + int len = 0; + for (len = 0; len < sizeof(key_cpy) - 2; len++) + { + key_cpy[len] = enum_key[len]; + if (key_cpy[len] == '\0') + { + break; + } + } + key_cpy[len++] = ' '; + key_cpy[len] = '\0'; + sh_completion_resource(s_sh_hdl, NULL, key_cpy, NULL); + } + + token = strtok_r(NULL, ",", &save_token); + } + + switch (s_attr_to_type_tbl[param->attr]) + { + case _PARSE_TYPE_UNSIGNED: // 待设置的参数格式是无符号整型 + { + if (type_flag & (_TYPE_FLAG_INTEGER | _TYPE_FLAG_FLOAT | _TYPE_FLAG_STRING)) + { + sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n"); + return -1; + } + } + break; + + case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型 + { + if (type_flag & (_TYPE_FLAG_FLOAT | _TYPE_FLAG_STRING)) + { + sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n"); + return -1; + } + } + break; + + case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数 + { + if (type_flag & (_TYPE_FLAG_STRING)) + { + sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n"); + return -1; + } + } + break; + + default: + break; + } + + return type_flag; +} diff --git a/components/system/source/shell/sh_vset.h b/components/system/source/shell/sh_vset.h new file mode 100755 index 0000000..cff3158 --- /dev/null +++ b/components/system/source/shell/sh_vset.h @@ -0,0 +1,223 @@ +/** + * @file sh_vset.h + * @author LokLiang + * @brief shell 模块专用,可用于通过命令行中的参数设置变量的基本接口 + * @version 0.1 + * @date 2023-09-22 + * + * @copyright Copyright (c) 2023 + * + * 本模块实现解析字符,并根据解析的结果对常见类型的的变量赋值的功能。 + * + * 特性: + * - 自动分析设置变量的具体类型 + * - 对取值范围限制 + * - 可通过命令行的形式进行设置 + * - 每增加一项写入接口最低消耗 38 个字节的代码空间 + * + * 使用: + * 1. 使用 vset_init() 指定内部的 sh 模块对象 + * 2. 使用对应的宏作为设置函数,其中: + * - @b SET_VAR 用于设置无符号整形、带符号整型、单精度的浮点数和字符串的变量 + * - @b SET_ENUM 用于设置枚举型的变量,支持 SET_VAR 所支持的全部类型 + * - @b SET_CP_ENUM 对应 SET_ENUM 所设置的变量的可用自动补全函数 + * 3. 如果输入的参数为 ? 可用于打印当前的设置范围。 + * 4. 通过选项设置参数,其中: + * - @b PSET_FN 指定一个已定义的 const sh_vset_param_t* 的结构作为选项 + * - @b PSET_CP 根据一个已定义的 const sh_vset_param_t* 执行自动补选项 + * + * 实例1: 以下这些函数可被 SH_SETUP_CMD 中定义的 FUNC 执行,或者作为 sh_vset_param_t::set_func 的成员 + * static int _value_set_u8(const char *argv[]) { SET_VAR(&var_u8, 1, 200); } + * static int _value_set_s16(const char *argv[]) { SET_VAR(&var_s16, -1000, 1000); } + * static int _value_set_float(const char *argv[]) { SET_VAR(&var_float, -1000, var_u8); } + * static int _value_set_str(const char *argv[]) { SET_VAR(&var_str, 0, 0); } + * static int _value_set_enum(const char *argv[]) { SET_ENUM(&var_s32, "enable=1,disable=0"); } + * + * 实例2: 实现选项+参数的格式 + * static sh_vset_param_t const s_param_template[] = { + * {"--u8", "该选项的帮助信息", value_set_u8, NULL}, + * {"--s16", "该选项的帮助信息", value_set_s16, NULL}, + * {"--float", "该选项的帮助信息", value_set_float, NULL}, + * {"--str", "该选项的帮助信息", value_set_str, NULL}, + * {"--enum", "该选项的帮助信息", value_set_enum, "enable=1,disable=0"}, + * static int _value_set(sh_t *sh_hdl, int argc, const char *argv[]) { PSET_FN(s_param_template); } // 这个函数可作为 SH_SETUP_CMD 中定义的 FUNC + * static void _value_cp(sh_t *sh_hdl, int argc, const char *argv[], bool flag) { PSET_CP(s_param_template); } // 这个函数可作为 SH_SETUP_CMD 中定义的 SUB + * + */ + +#ifndef __SH_VSET_H__ +#define __SH_VSET_H__ + +#include "sys_types.h" +#include "sh.h" + +/* 描述支待的待设置变量的具体类型 */ +typedef enum +{ + __TYPE_ATTR_CHR, + __TYPE_ATTR_BOOL, + __TYPE_ATTR_U8, + __TYPE_ATTR_S8, + __TYPE_ATTR_U16, + __TYPE_ATTR_S16, + __TYPE_ATTR_U32, + __TYPE_ATTR_S32, + __TYPE_ATTR_U64, + __TYPE_ATTR_S64, + __TYPE_ATTR_FLOAT, + __TYPE_ATTR_DOUBLE, + __TYPE_ATTR_STR, + __TYPE_ATTR_OTHER, +} __type_attr_t; + +/** + * @brief vset_cb + * 当一个参数的设置值合法,即将被执行设置前回调的函数。当回调退出后才会更新到目标变量中。 + * @param new_value 指向 sh_vset 内部的新值的栈内存地址。提示:可配合 __typeof() 强制转换为确定的类型。 + */ +typedef void (*vset_cb)(sh_t *sh_hdl, void *new_value); + +/* 待设置变量数据结构 */ +typedef struct +{ + void *dest; + __type_attr_t attr; + vset_cb cb; +} __vset_param_t; + +#define __GENERIC_ATTR(VAR) (__builtin_types_compatible_p(__typeof(VAR), char) ? __TYPE_ATTR_CHR \ + : __builtin_types_compatible_p(__typeof(VAR), volatile char) ? __TYPE_ATTR_CHR \ + : __builtin_types_compatible_p(__typeof(VAR), bool) ? __TYPE_ATTR_BOOL \ + : __builtin_types_compatible_p(__typeof(VAR), volatile bool) ? __TYPE_ATTR_BOOL \ + : __builtin_types_compatible_p(__typeof(VAR), uint8_t) ? __TYPE_ATTR_U8 \ + : __builtin_types_compatible_p(__typeof(VAR), volatile uint8_t) ? __TYPE_ATTR_U8 \ + : __builtin_types_compatible_p(__typeof(VAR), int8_t) ? __TYPE_ATTR_S8 \ + : __builtin_types_compatible_p(__typeof(VAR), volatile int8_t) ? __TYPE_ATTR_S8 \ + : __builtin_types_compatible_p(__typeof(VAR), uint16_t) ? __TYPE_ATTR_U16 \ + : __builtin_types_compatible_p(__typeof(VAR), volatile uint16_t) ? __TYPE_ATTR_U16 \ + : __builtin_types_compatible_p(__typeof(VAR), int16_t) ? __TYPE_ATTR_S16 \ + : __builtin_types_compatible_p(__typeof(VAR), volatile int16_t) ? __TYPE_ATTR_S16 \ + : __builtin_types_compatible_p(__typeof(VAR), uint32_t) ? __TYPE_ATTR_U32 \ + : __builtin_types_compatible_p(__typeof(VAR), volatile uint32_t) ? __TYPE_ATTR_U32 \ + : __builtin_types_compatible_p(__typeof(VAR), int32_t) ? __TYPE_ATTR_S32 \ + : __builtin_types_compatible_p(__typeof(VAR), volatile int32_t) ? __TYPE_ATTR_S32 \ + : __builtin_types_compatible_p(__typeof(VAR), uint64_t) ? __TYPE_ATTR_U64 \ + : __builtin_types_compatible_p(__typeof(VAR), volatile uint64_t) ? __TYPE_ATTR_U64 \ + : __builtin_types_compatible_p(__typeof(VAR), int64_t) ? __TYPE_ATTR_S64 \ + : __builtin_types_compatible_p(__typeof(VAR), volatile int64_t) ? __TYPE_ATTR_S64 \ + : __builtin_types_compatible_p(__typeof(VAR), float) ? __TYPE_ATTR_FLOAT \ + : __builtin_types_compatible_p(__typeof(VAR), volatile float) ? __TYPE_ATTR_FLOAT \ + : __builtin_types_compatible_p(__typeof(VAR), double) ? __TYPE_ATTR_DOUBLE \ + : __builtin_types_compatible_p(__typeof(VAR), volatile double) ? __TYPE_ATTR_DOUBLE \ + : __builtin_types_compatible_p(__typeof(VAR), char[]) ? __TYPE_ATTR_STR \ + : __TYPE_ATTR_OTHER) + +typedef int (*vset_var_fn)(const char *argv[]); + +typedef struct // 用于长选项设置的描述结构 +{ + const char *option; // 选项,如 "--value" + const char *help; // 对该选项的描述 + vset_var_fn set_func; // 与 option 对应的,使用宏 SET_VAR() 或 SET_ENUM() 设置变量的函数。如果值为 NULL 表示该选项无参数,同时对应的输入参数被保留 + const char *enum_str; // 仅在类型为 vset_enum_fn 时有效,为对应的选项提供可选的补全参数选项,值为 NULL 或 "" 时默认候选参数为 '?' +} sh_vset_param_t; + +int vset_unsigned(const __vset_param_t *param, const char *argv[], unsigned int low, unsigned int high); +int vset_integer(const __vset_param_t *param, const char *argv[], signed int low, signed int high); +int vset_float(const __vset_param_t *param, const char *argv[], float low, float high); +int vset_str(const __vset_param_t *param, const char *argv[], unsigned bufsize); +int vset_enum(const __vset_param_t *param, const char *argv[], const char *enum_str); +void vset_cp_enum(int argc, bool flag, const char *enum_str); + +int vset_option_set(sh_t *sh_hdl, int *argc, const char *argv[], const sh_vset_param_t *p, unsigned size); +bool vset_option_cp(sh_t *sh_hdl, int argc, const char *argv[], bool flag, const sh_vset_param_t *p, unsigned size); + +/* 自动分析并设置变量的值,带设置回调 */ +#define SET_VAR_CB(NAME, LOW, HIGH, CB) \ + do \ + { \ + static __vset_param_t const param = { \ + .dest = NAME, \ + .attr = __GENERIC_ATTR(*(NAME)), \ + .cb = CB, \ + }; \ + if ( \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_U8 || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_U16 || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_U32 || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_U64) \ + { \ + return vset_unsigned(¶m, argv, LOW, HIGH); \ + } \ + else if ( \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_CHR || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_BOOL || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_S8 || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_S16 || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_S32 || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_S64) \ + { \ + return vset_integer(¶m, argv, LOW, HIGH); \ + } \ + else if ( \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_FLOAT || \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_DOUBLE) \ + { \ + return vset_float(¶m, argv, LOW, HIGH); \ + } \ + else if ( \ + __GENERIC_ATTR(*(NAME)) == __TYPE_ATTR_STR) \ + { \ + return vset_str(¶m, argv, sizeof(*(NAME))); \ + } \ + else \ + { \ + return -1; \ + } \ + } while (0) + +/* 设置数据类型为 枚举型 的变量,带设置回调 */ +#define SET_ENUM_CB(NAME, ENUM_STR, CB) \ + do \ + { \ + static __vset_param_t const param = { \ + .dest = NAME, \ + .attr = __GENERIC_ATTR(*(NAME)), \ + .cb = CB, \ + }; \ + return vset_enum(¶m, argv, ENUM_STR); \ + } while (0) + +/* 自动分析并设置变量的值 */ +#define SET_VAR(NAME, LOW, HIGH) SET_VAR_CB(NAME, LOW, HIGH, NULL) + +/* 设置数据类型为 枚举型 的变量 */ +#define SET_ENUM(NAME, ENUM_STR) SET_ENUM_CB(NAME, ENUM_STR, NULL) + +/* 对应 SET_VAR 所设置的变量的可用自动补全函数 */ +#define SET_CP_VAR() \ + do \ + { \ + if (argc + flag == 1) \ + { \ + sh_completion_resource(sh_hdl, NULL, "? ", NULL); \ + } \ + } while (0) + +/* 对应 SET_ENUM 所设置的变量的可用自动补全函数 */ +#define SET_CP_ENUM(ENUM_STR) \ + do \ + { \ + vset_cp_enum(argc, flag, ENUM_STR); \ + } while (0) + +#define PSET_FN(OPT) vset_option_set(sh_hdl, &argc, argv, OPT, sizeof(OPT)) /* 作为 SH_CMD_FN() 的实际执行函数 */ +#define PSET_CP(OPT) vset_option_cp(sh_hdl, argc, argv, flag, OPT, sizeof(OPT)) /* 作为 SH_CMD_CP_FN() 的实际执行函数 */ + +typedef void (*vset_global_cb)(sh_t *sh_hdl); + +void vset_init(sh_t *sh_hdl, vset_global_cb cb); + +void vset_force_cb(void); + +#endif diff --git a/components/system/source/shell/sh_vt100.c b/components/system/source/shell/sh_vt100.c new file mode 100755 index 0000000..3c50750 --- /dev/null +++ b/components/system/source/shell/sh_vt100.c @@ -0,0 +1,455 @@ +/** + * @file sh_vt100.c + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-03-21 + * + * @copyright Copyright (c) 2023 + * + */ + +#include "sh.h" +#include "sh_vt100.h" +#include "sys_log.h" + +#ifndef CONFIG_SH_MAX_HISTORY_LEN +#define CONFIG_SH_MAX_HISTORY_LEN 255 /* 用于记录命令的缓存长度 */ +#endif + +/** + * @brief 定义热键列表 SH_DEF_KEY 中的成员 + * + * @param KEY 热键代码(字符串) + * @param FUNC 热键对应的函数( static void _key_func(void) ) + * + * @verbatim + * @c sh_vt100.c + * @endverbatim + */ +#define SH_SETUP_KEY(KEY, FUNC) \ + { \ + .code = KEY, \ + .key_func = FUNC, \ + } + +#define SH_DEF_KEY(NAME, ...) \ + static sys_psnode_t _SH_NAME(key_node_); \ + static sh_key_t const _SH_NAME(key_data_)[] = { \ + __VA_ARGS__{0}}; \ + static sh_key_reg_t const NAME = { \ + .node = &_SH_NAME(key_node_), \ + .key = _SH_NAME(key_data_), \ + }; + +sh_t g_uart_handle_vt100; + +static char last_key = '\0'; + +/* 基本键键 */ +static void _exec_key_TAB(sh_t *sh_hdl); // 补全命令 +static void _exec_key_ENTER(sh_t *sh_hdl); // 执行命令 +static void _exec_key_LF(sh_t *sh_hdl); // 执行命令 +static void _exec_key_BACKSPACE(sh_t *sh_hdl); // 删除光标前一个字符 +static void _exec_key_CTR_H(sh_t *sh_hdl); // 删除光标前一个字符 +static void _exec_key_DELETE(sh_t *sh_hdl); // 删除光标后一个字符 +static void _exec_key_UP(sh_t *sh_hdl); // 查看上一个命令 +static void _exec_key_DW(sh_t *sh_hdl); // 查看下一个命令 +static void _exec_key_RIGHT(sh_t *sh_hdl); // 光标右移 +static void _exec_key_LEFT(sh_t *sh_hdl); // 光标左移 +static void _exec_key_HOME(sh_t *sh_hdl); // 光标移到行首(提示符除外) +static void _exec_key_END(sh_t *sh_hdl); // 光标移到行尾 + +/* 其他键键 */ +static void _exec_key_CTR_C(sh_t *sh_hdl); // 结束程序 +static void _exec_key_CTR_D(sh_t *sh_hdl); // 删除光标后一个字符或结束程序 +static void _exec_key_CTR_L(sh_t *sh_hdl); // 清空终端 +static void _exec_key_CTR_A(sh_t *sh_hdl); // 同 HOME +static void _exec_key_CTR_E(sh_t *sh_hdl); // 同 END +static void _exec_key_CTR_U(sh_t *sh_hdl); // 擦除光标前到行首的全部内容 +static void _exec_key_CTR_K(sh_t *sh_hdl); // 擦除光标后到行尾的全部内容 +static void _exec_key_CTR_W(sh_t *sh_hdl); // 擦除光标前的单词 +static void _exec_key_CTR_Y(sh_t *sh_hdl); // 还原擦除的内容 +static void _exec_key_CTR_RIGHT(sh_t *sh_hdl); // 光标移动到单词首位 +static void _exec_key_CTR_LEFT(sh_t *sh_hdl); // 光标移动到单词结尾 + +/* 终端操作 */ +static void _clear(sh_t *sh_hdl); // 清屏 +static void _del_line(sh_t *sh_hdl, unsigned len); // 删除当前行 +static void _set_cursor_hoffset(sh_t *sh_hdl, int last_pos, int new_pos); +static void _insert_str(sh_t *sh_hdl, const char *str); + +SH_DEF_KEY( + _register_vt100_keys, + + /* 基本 */ + SH_SETUP_KEY(KEY_TAB, _exec_key_TAB), // 补全命令 + SH_SETUP_KEY(KEY_CTR_M, _exec_key_ENTER), // 执行命令 + SH_SETUP_KEY(KEY_CTR_J, _exec_key_LF), // 执行命令 + SH_SETUP_KEY(KEY_BACKSPACE, _exec_key_BACKSPACE), // 删除光标前一个字符 + SH_SETUP_KEY(KEY_CTR_H, _exec_key_CTR_H), // 删除光标前一个字符 + SH_SETUP_KEY(KEY_DELETE, _exec_key_DELETE), // 删除光标后一个字符 + SH_SETUP_KEY(KEY_UP, _exec_key_UP), // 查看上一个命令 + SH_SETUP_KEY(KEY_DW, _exec_key_DW), // 查看下一个命令 + SH_SETUP_KEY(KEY_RIGHT, _exec_key_RIGHT), // 光标右移 + SH_SETUP_KEY(KEY_LEFT, _exec_key_LEFT), // 光标左移 + SH_SETUP_KEY(KEY_HOME, _exec_key_HOME), // 光标移到行首(提示符除外) + SH_SETUP_KEY(KEY_END, _exec_key_END), // 光标移到行尾 + + /* 其他 */ + SH_SETUP_KEY(KEY_CTR_C, _exec_key_CTR_C), // 结束程序 + SH_SETUP_KEY(KEY_CTR_D, _exec_key_CTR_D), // 删除光标后一个字符或结束程序 + SH_SETUP_KEY(KEY_CTR_L, _exec_key_CTR_L), // 清空终端 + SH_SETUP_KEY(KEY_CTR_A, _exec_key_CTR_A), // 同 HOME + SH_SETUP_KEY(KEY_CTR_E, _exec_key_CTR_E), // 同 END + SH_SETUP_KEY(KEY_CTR_U, _exec_key_CTR_U), // 擦除光标前到行首的全部内容 + SH_SETUP_KEY(KEY_CTR_K, _exec_key_CTR_K), // 擦除光标后到行尾的全部内容 + SH_SETUP_KEY(KEY_CTR_W, _exec_key_CTR_W), // 擦除光标前的单词 + SH_SETUP_KEY(KEY_CTR_Y, _exec_key_CTR_Y), // 还原擦除的内容 + SH_SETUP_KEY(KEY_CTR_RIGHT, _exec_key_CTR_RIGHT), // 光标移动到单词首位 + SH_SETUP_KEY(KEY_CTR_LEFT, _exec_key_CTR_LEFT), // 光标移动到单词结尾 +); + +static void _exec_key_TAB(sh_t *sh_hdl) // "\x09" +{ + sh_ctrl_tab(sh_hdl); // 自动实例/打印命令 +} + +static void _exec_key_ENTER(sh_t *sh_hdl) // "\x0D" +{ + sh_echo(sh_hdl, "\r\n"); + sh_ctrl_enter(sh_hdl); // 执行已缓存到的命令 + sh_ctrl_print_cmd_line(sh_hdl); + last_key = '\r'; +} + +static void _exec_key_LF(sh_t *sh_hdl) // "\x0A" +{ + if (last_key != '\r') + { + _exec_key_ENTER(sh_hdl); + } + last_key = '\n'; +} + +static void _exec_key_DELETE(sh_t *sh_hdl) // "\e[3~" +{ + if (sh_ctrl_delete(sh_hdl, 1)) + { + /* 删除光标右边一个字符 */ + sh_echo(sh_hdl, DCH(1)); + } +} + +static void _exec_key_BACKSPACE(sh_t *sh_hdl) // "\x7F" +{ + if (sh_ctrl_backspace(sh_hdl, 1)) + { + /* 删除光标左边一个字符,同时使光标自动左移一位 */ + sh_echo(sh_hdl, CUB(1)); + sh_echo(sh_hdl, DCH(1)); + } +} + +static void _exec_key_CTR_H(sh_t *sh_hdl) // "\x8" +{ + if (sh_ctrl_backspace(sh_hdl, 1)) + { + /* 删除光标左边一个字符,同时使光标自动左移一位 */ + uint16_t line_pos = sh_ctrl_get_line_pos(sh_hdl); + uint16_t line_len = sh_ctrl_get_line_len(sh_hdl); + + sh_echo(sh_hdl, "\b"); + for (int i = 0; i < line_len - line_pos; i++) + { + sh_echo(sh_hdl, "%c", sh_ctrl_get_line_char(sh_hdl, line_pos + i)); + } + sh_echo(sh_hdl, " "); + for (int i = 0; i < line_len - line_pos + 1; i++) + { + sh_echo(sh_hdl, "\b"); + } + } +} + +static void _exec_key_RIGHT(sh_t *sh_hdl) // "\e[C" +{ + sh_ctrl_right(sh_hdl); // 光标右移 +} + +static void _exec_key_LEFT(sh_t *sh_hdl) // "\e[D" +{ + sh_ctrl_left(sh_hdl); // 光标左移 +} + +static void _exec_key_HOME(sh_t *sh_hdl) // "\e[H" +{ + sh_ctrl_home(sh_hdl); // 光标移到开头位置 +} + +static void _exec_key_END(sh_t *sh_hdl) // "\e[F" +{ + sh_ctrl_end(sh_hdl); // 光标移到结尾位置 +} + +static void _exec_key_UP(sh_t *sh_hdl) // "\e[A" +{ + int len = sh_ctrl_get_line_len(sh_hdl); + if (sh_ctrl_up(sh_hdl)) // 上一个命令 + { + _del_line(sh_hdl, len); + sh_ctrl_print_cmd_line(sh_hdl); + } +} + +static void _exec_key_DW(sh_t *sh_hdl) // "\e[B" +{ + int len = sh_ctrl_get_line_len(sh_hdl); + if (sh_ctrl_down(sh_hdl)) // 下一个命令 + { + _del_line(sh_hdl, len); + sh_ctrl_print_cmd_line(sh_hdl); + } +} + +__weak void drv_hal_sys_exit(void) +{ + SYS_LOG_WRN("never define exit function"); +} +static void _exec_key_CTR_C(sh_t *sh_hdl) // "\x03" +{ + sh_echo(sh_hdl, "\r\n"); + drv_hal_sys_exit(); +} + +static void _exec_key_CTR_D(sh_t *sh_hdl) // "\x04" +{ + if (sh_ctrl_delete(sh_hdl, 1)) + { + /* 删除光标右边一个字符 */ + sh_echo(sh_hdl, DCH(1)); + } + else + { + uint16_t input_pos = sh_ctrl_get_input_pos(sh_hdl); + if (input_pos == 0) + { + if (sh_ctrl_is_module(sh_hdl)) + { + sh_putstr(sh_hdl, "exit\r"); + } + else + { + drv_hal_sys_exit(); + } + } + } +} + +static void _exec_key_CTR_L(sh_t *sh_hdl) // "\x0C" +{ + uint16_t input_pos = sh_ctrl_get_input_pos(sh_hdl); + + _clear(sh_hdl); + _del_line(sh_hdl, sh_ctrl_get_line_len(sh_hdl)); + sh_ctrl_print_cmd_line(sh_hdl); + + sh_ctrl_set_input_pos(sh_hdl, input_pos); +} + +static void _exec_key_CTR_A(sh_t *sh_hdl) // "\x01" +{ + sh_ctrl_home(sh_hdl); // 光标移到开头位置 +} + +static void _exec_key_CTR_E(sh_t *sh_hdl) // "\x05" +{ + sh_ctrl_end(sh_hdl); // 光标移到结尾位置 +} + +static void _exec_key_CTR_U(sh_t *sh_hdl) // "\x15" +{ + /* 删除光标左边的所有字符 */ + int n = sh_ctrl_del_left(sh_hdl); + if (n) + { + sh_echo(sh_hdl, CUB(n)); + sh_echo(sh_hdl, DCH(n)); + } +} + +static void _exec_key_CTR_K(sh_t *sh_hdl) // "\x0B" +{ + /* 删除光标右边的所有字符 */ + int n = sh_ctrl_del_right(sh_hdl); + if (n) + { + sh_echo(sh_hdl, DCH(n)); + } +} + +static void _exec_key_CTR_W(sh_t *sh_hdl) // "\x17" +{ + int n = sh_ctrl_del_word(sh_hdl); + if (n) + { + /* 删除光标左边 n 个字符,同时使光标自动左移 n 位 */ + sh_echo(sh_hdl, CUB(n)); + sh_echo(sh_hdl, DCH(n)); + } +} + +static void _exec_key_CTR_Y(sh_t *sh_hdl) // "\x19" +{ + sh_ctrl_undelete(sh_hdl); +} + +static void _exec_key_CTR_RIGHT(sh_t *sh_hdl) // "\e[1;5C" +{ + sh_ctrl_word_tail(sh_hdl); +} + +static void _exec_key_CTR_LEFT(sh_t *sh_hdl) // "\e[1;5D" +{ + sh_ctrl_word_head(sh_hdl); +} + +static int _cmd_clear(sh_t *sh_hdl, int argc, const char *argv[]) +{ + _clear(sh_hdl); + return 0; +} +SH_DEF_CMD( + _register_cmd_clear, + SH_SETUP_CMD("clear", "Clear the terminal screen", _cmd_clear, NULL), // 清屏 +); + +#if defined(__linux__) + +static void _clear(sh_t *sh_hdl) +{ + sh_echo(sh_hdl, "\ec"); +} + +static void _del_line(sh_t *sh_hdl, unsigned len) +{ + sh_echo(sh_hdl, DL(0)); +} + +static void _set_cursor_hoffset(sh_t *sh_hdl, int last_pos, int new_pos) +{ + /* 设置光标水平位置 */ + int hoffset = new_pos - last_pos; + if (hoffset > 0) // 右移 + { + sh_echo(sh_hdl, CUF(hoffset)); + } + else if (hoffset < 0) // 左移 + { + sh_echo(sh_hdl, CUB(-hoffset)); + } +} + +#else + +static void _clear(sh_t *sh_hdl) +{ + sh_echo(sh_hdl, "\ec"); +} + +static void _del_line(sh_t *sh_hdl, unsigned len) +{ + sh_echo(sh_hdl, "\r"); + for (int i = 0; i <= len; i++) + { + sh_echo(sh_hdl, " "); + } + sh_echo(sh_hdl, "\r"); +} + +static void _set_cursor_hoffset(sh_t *sh_hdl, int last_pos, int new_pos) +{ + /* 设置光标水平位置 */ + if (new_pos > last_pos) // 右移 + { + for (; new_pos > last_pos; last_pos++) + { + char c = sh_ctrl_get_line_char(sh_hdl, last_pos); + sh_echo(sh_hdl, "%c", c); + } + } + else if (new_pos < last_pos) // 左移 + { + for (; new_pos < last_pos; last_pos--) + { + sh_echo(sh_hdl, "\b"); + } + } +} + +#endif + +static void _insert_str(sh_t *sh_hdl, const char *str) +{ + sh_echo(sh_hdl, ICH(strlen(str))); + sh_echo(sh_hdl, "%s", str); +} + +__used static void _clear_line(sh_t *sh_hdl) +{ + _del_line(sh_hdl, sh_ctrl_get_line_len(sh_hdl)); +} + +/** + * @brief 初始化一个新的 VT100 的终端对象。 + * + * @param sh_hdl 被初始化由 sh_putc() 或 sh_putstr() 的对象 + * @param vprint 注册实现函数 sh_hdl ==> 终端和显示 + * @param disconnect 当执行 exit 命令时被回调的,用于断开连接的函数。如不需要可设为 NULL + */ +int sh_init_vt100(sh_t *sh_hdl, sh_vprint_fn vprint, sh_disconnect_fn disconnect) +{ + int ret = 0; + for (int i = 0; i < sizeof(*sh_hdl); i++) + { + ((uint8_t *)sh_hdl)[i] = 0; + } + + sh_port_t port = { + .set_cursor_hoffset = _set_cursor_hoffset, + .insert_str = _insert_str, + .vprint = vprint, + .disconnect = disconnect, + // .clear_line = _clear_line, // 执行清除一行。值为 NULL 时不启用刷命令行 + }; + sh_register_port(sh_hdl, &port); + + sh_hdl->cmd_buf = sh_hdl->cmd_line; + + sh_hdl->obj_key_data = _register_vt100_keys; + sh_hdl->obj_key_data.node = &sh_hdl->obj_key_node; + ret |= sh_register_key(sh_hdl, &sh_hdl->obj_key_data); + + sh_hdl->obj_cmd_data = _register_cmd_clear; + sh_hdl->obj_cmd_data.node = &sh_hdl->obj_cmd_node; + ret |= sh_register_key_cmd(sh_hdl, &sh_hdl->obj_cmd_data); + + return -!!ret; +} + +static int _install_shell_vt100(void) +{ + if (sh_init_vt100(&g_uart_handle_vt100, SYS_VPRINT, NULL) == 0) + { + static uint8_t history_mem[CONFIG_SH_MAX_HISTORY_LEN]; + static uint8_t bank_mem[CONFIG_SH_MAX_LINE_LEN]; + sh_config_history_mem(&g_uart_handle_vt100, history_mem, sizeof(history_mem)); + sh_config_backup_mem(&g_uart_handle_vt100, bank_mem, sizeof(bank_mem)); + return 0; + } + else + { + return -1; + } +} +INIT_EXPORT_COMPONENT(_install_shell_vt100); diff --git a/components/system/source/shell/sh_vt100.h b/components/system/source/shell/sh_vt100.h new file mode 100755 index 0000000..3017eb6 --- /dev/null +++ b/components/system/source/shell/sh_vt100.h @@ -0,0 +1,117 @@ +/** + * @file sh_vt100.h + * @author LokLiang (lokliang@163.com) + * @brief + * @version 0.1 + * @date 2023-03-21 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __SH_VT100_h__ +#define __SH_VT100_h__ + +/* 热键 */ +#define KEY_CTR_A "\x01" +#define KEY_CTR_B "\x02" +#define KEY_CTR_C "\x03" +#define KEY_CTR_D "\x04" +#define KEY_CTR_E "\x05" +#define KEY_CTR_F "\x06" +#define KEY_CTR_G "\x07" +#define KEY_CTR_H "\x08" +#define KEY_CTR_I "\x09" +#define KEY_TAB "\x09" +#define KEY_CTR_J "\x0A" +#define KEY_CTR_K "\x0B" +#define KEY_CTR_L "\x0C" +#define KEY_CTR_M "\x0D" +#define KEY_CTR_N "\x0E" +#define KEY_CTR_O "\x0F" +#define KEY_CTR_P "\x10" +#define KEY_CTR_Q "\x11" +#define KEY_CTR_R "\x12" +#define KEY_CTR_S "\x13" +#define KEY_CTR_T "\x14" +#define KEY_CTR_U "\x15" +#define KEY_CTR_V "\x16" +#define KEY_CTR_W "\x17" +#define KEY_CTR_X "\x18" +#define KEY_CTR_Y "\x19" +#define KEY_CTR_Z "\x1A" +#define KEY_PAUSE "\x1A" +#define KEY_ESC "\x1b" +#define KEY_BACKSPACE "\x7F" +#define KEY_UP "\x1b[A" +#define KEY_DW "\x1b[B" +#define KEY_RIGHT "\x1b[C" +#define KEY_LEFT "\x1b[D" +#define KEY_HOME "\x1b[H" +#define KEY_END "\x1b[F" +#define KEY_CTR_UP "\x1b[1;5A" +#define KEY_CTR_DW "\x1b[1;5B" +#define KEY_CTR_RIGHT "\x1b[1;5C" +#define KEY_CTR_LEFT "\x1b[1;5D" +#define KEY_INSERT "\x1b[2~" +#define KEY_DELETE "\x1b[3~" +#define KEY_PAGE_UP "\x1b[5~" +#define KEY_PAGE_DOWN "\x1b[6~" +#define KEY_F1 "\x1bOP" +#define KEY_F2 "\x1bOQ" +#define KEY_F3 "\x1bOR" +#define KEY_F4 "\x1bOS" +#define KEY_F5 "\x1b[15~" +#define KEY_F6 "\x1b[17~" +#define KEY_F7 "\x1b[18~" +#define KEY_F8 "\x1b[19~" +#define KEY_F9 "\x1b[20~" +#define KEY_F10 "\x1b[21~" +#define KEY_F11 "\x1b[23~" +#define KEY_F12 "\x1b[24~" + + +/*光标操作符*/ +#define CUU(n) "\x1b[%dA",n /* 光标向上 光标向上 行 */ +#define CUD(n) "\x1b[%dB",n /* 光标向下 光标向下 行 */ +#define CUF(n) "\x1b[%dC",n /* 光标向前 光标向前(右) 行 */ +#define CUB(n) "\x1b[%dD",n /* 光标向后 光标向后(左) 行 */ +#define CNL(n) "\x1b[%dE",n /* 光标下一行 光标从当前位置向下 行 */ +#define CPL(n) "\x1b[%dF",n /* 光标当前行 光标从当前位置向上 行 */ +#define CHA(n) "\x1b[%dG",n /* 绝对光标水平 光标在当前行中水平移动到第 个位置 */ +#define VPA(n) "\x1b[%dd",n /* 绝对垂直行位置 光标在当前列中垂直移动到第 个位置 */ +#define CUP(y,x) "\x1b[%d;%dH",y,x /* 光标位置 *光标移动到视区中的 ; 坐标,其中 行的列 */ +#define HVP(y,x) "\x1b[%d;%df",y,x /* 水平垂直位置 *光标移动到视区中的 ; 坐标,其中 行的列 */ + +/*光标可见性*/ +#define CU_START_BL "\x1b[?12h" /* ATT160 文本光标启用闪烁 开始光标闪烁 */ +#define CU_STOP_BL "\x1b[?12l" /* ATT160 文本光标禁用闪烁 停止闪烁光标 */ +#define CU_SHOW "\x1b[?25h" /* DECTCEM 文本光标启用模式显示 显示光标 */ +#define CU_HIDE "\x1b[?25l" /* DECTCEM 文本光标启用模式隐藏 隐藏光标 */ + +/* 字符操作 */ +#define ICH(n) "\x1b[%d@",n /* 插入字符 在当前光标位置插入 个空格,这会将所有现有文本移到右侧。 向右溢出屏幕的文本会被删除。*/ +#define DCH(n) "\x1b[%dP",n /* 删除字符 删除当前光标位置的 个字符,这会从屏幕右边缘以空格字符移动。*/ +#define ECH(n) "\x1b[%dX",n /* 擦除字符 擦除当前光标位置的 个字符,方法是使用空格字符覆盖它们。*/ +#define IL(n) "\x1b[%dL",n /* 插入行 将 行插入光标位置的缓冲区。 光标所在的行及其下方的行将向下移动。*/ +#define DL(n) "\x1b[%dM\r",n /* 删除行 从缓冲区中删除 行,从光标所在的行开始。*/ + +/* 打印字体颜色设置 */ +#define TX_DEF "\x1b[0m" +#define TX_BLACK "\x1b[30m" +#define TX_RED "\x1b[31m" +#define TX_GREEN "\x1b[32m" +#define TX_YELLOW "\x1b[33m" +#define TX_BLUE "\x1b[34m" +#define TX_WHITE "\x1b[37m" + +/* 打印背景颜色设置 */ +#define BK_DEF "\x1b[0m" +#define BK_BLACK "\x1b[40m" +#define BK_RED "\x1b[41m" +#define BK_GREEN "\x1b[42m" +#define BK_YELLOW "\x1b[43m" +#define BK_BLUE "\x1b[44m" +#define BK_WHITE "\x1b[47m" + +#endif diff --git a/components/system/test/main.c b/components/system/test/main.c new file mode 100755 index 0000000..54d84ca --- /dev/null +++ b/components/system/test/main.c @@ -0,0 +1,124 @@ +#include "os_test.h" +#include "sys_init.h" +#include "os/os.h" + +#undef SYS_LOG_DOMAIN +#define SYS_LOG_DOMAIN "OS" +#include "os_util.h" + +#ifndef CONFIG_OS_THREAD_MAIN_STACK_SIZE +#define CONFIG_OS_THREAD_MAIN_STACK_SIZE 0x2000 +#endif + +#ifndef CONFIG_OS_THREAD_MAIN_PRIORITY +#define CONFIG_OS_THREAD_MAIN_PRIORITY OS_PRIORITY_NORMAL +#endif + +static unsigned s_int_nest; + +static unsigned _port_interrupt_save(void) +{ + if (s_int_nest == 0) + { + os_interrupt_disable(); + } + return s_int_nest++; +} + +static void _port_interrupt_restore(unsigned nest) +{ + s_int_nest = nest; + if (nest == 0) + { + os_interrupt_enable(); + } +} + +static k_work_q_t *_port_get_work_q_hdl(void) +{ + struct os_thread_handle *thread_handle = os_thread_get_self(); + os_work_q_list_t *work_q_list = thread_handle->work_q_list; + return &work_q_list->work_q_handle; +} + +static void _port_thread_sleep(k_tick_t sleep_ticks) +{ + vTaskDelay(sleep_ticks); +} + +static void _work_app_main(void *arg) +{ +} + +static void _os_init(void) +{ + static k_init_t const init_struct = { + .malloc = os_malloc, + .free = os_free, + .get_sys_ticks = os_get_sys_ticks, + .interrupt_save = _port_interrupt_save, + .interrupt_restore = _port_interrupt_restore, + .scheduler_disable = os_scheduler_suspend, + .scheduler_enable = os_scheduler_resume, + .get_work_q_hdl = _port_get_work_q_hdl, + .thread_sleep = _port_thread_sleep, + }; + k_init(&init_struct); + + os_work_q_create(default_os_work_q_hdl, + "app-work_q", + CONFIG_OS_THREAD_MAIN_STACK_SIZE, + CONFIG_OS_THREAD_MAIN_PRIORITY); + + static os_work_t _work_hdl_init; + os_work_create(&_work_hdl_init, "work-main", _work_app_main, NULL, 3); + os_work_submit(default_os_work_q_hdl, &_work_hdl_init, 0); +} + +#include "sh.h" + +static void _work_sh(void *arg) +{ + char c; + // while (drv_uart_poll_read(board_uart_cons.id, &c) == 0) + // { + // sh_putc(&g_uart_handle_vt100, c); + // } + os_work_later(10); +} + +static void _start_shell(void) +{ + static os_work_t work_handler_shell; + os_work_create(&work_handler_shell, "work-shell", _work_sh, NULL, 0); + os_work_submit(default_os_work_q_hdl, &work_handler_shell, 0); + + sh_putstr_quiet(&g_uart_handle_vt100, "sh version\r"); + sh_set_prompt(&g_uart_handle_vt100, "sh:/> "); + sh_putc(&g_uart_handle_vt100, '\r'); +} + +void _app_main_handler(void *pArg) +{ + extern int os_test_main(void); + os_test_main(); +} + +int app_main(void) +{ + _os_init(); + + _start_shell(); + + static os_thread_t s_main_thread; + static char *const FirstTaskName = "test_main"; // 首个线程的线程名 + + os_thread_create(&s_main_thread, + FirstTaskName, + _app_main_handler, + (void *)rand(), + 0x1000, + 1); + + return 0; +} diff --git a/components/system/test/main.h b/components/system/test/main.h new file mode 100755 index 0000000..6503371 --- /dev/null +++ b/components/system/test/main.h @@ -0,0 +1,11 @@ +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include "kk.h" +#include "os/os.h" + +void main_k_init(void); +void main_timer_reset(void); +void main_timer_hook(void (*fn)(void)); + +#endif diff --git a/components/system/test/os_test.c b/components/system/test/os_test.c new file mode 100755 index 0000000..81933fc --- /dev/null +++ b/components/system/test/os_test.c @@ -0,0 +1,82 @@ +#include "os_test.h" +#include "sys_init.h" + +Type_OS_TestMem_Def rm_Test; // 用于测试的内存 +os_work_t test_work_handle[10]; +os_fifo_t fifo_handle[10]; +os_queue_t queue_handle[10]; +os_pipe_t pipe_handle[10]; + +os_thread_t *os_test_create_thread(os_thread_t *thread_handle, void (*TaskRoute)(void *), char *name, s16_t Priority) +{ + os_thread_create(thread_handle, + name, + TaskRoute, + 0, + 0x1000, + Priority); + + return thread_handle; +} +// + +/* 具体测试 */ +extern void os_test_fifo(void); +extern void os_test_queue(void); +extern void os_test_pipe(void); +extern void os_test_yield(uint TestTime); +extern void os_test_sem(uint TestTime); +extern void os_test_sleep(uint TestTime); + +typedef void (*test_single_t)(void); +typedef void (*test_con_t)(uint TestTime); + +static test_con_t const test_tab_rt[] = { + os_test_yield, + os_test_sem, + os_test_sleep, +}; + +static test_single_t const test_tab_ipc[] = { + os_test_fifo, + os_test_queue, + os_test_pipe, +}; + +INIT_EXPORT_APP(99) +{ + SYS_LOG_INF("success"); + os_thread_sleep(200); +} + +void os_test_main(void) +{ + rand(); // rand() 用到 malloc() + CONS_PRINT("\r\n"); // printf() 用到 _malloc_r() + + SYS_LOG_INF("测试准备中 ... \r\n"); + + CONS_PRINT("开始测试...\r\n\r\n"); + + for (uint i = 0; i < sizeof(test_tab_rt) / sizeof(test_tab_rt[0]); i++) + { + test_tab_rt[i](1); + CONS_PRINT("\r\n"); + } + + for (uint i = 0; i < sizeof(test_tab_ipc) / sizeof(test_tab_ipc[0]); i++) + { + test_tab_ipc[i](); + } + + for (uint n = 0; n < sizeof(test_work_handle) / sizeof(test_work_handle[0]); n++) + { + if (os_work_is_valid(&test_work_handle[n])) + { + os_work_delete(&test_work_handle[n]); + } + } + + CONS_PRINT("所有测试结束\r\n"); + CONS_PRINT("============================\r\n"); +} diff --git a/components/system/test/os_test.h b/components/system/test/os_test.h new file mode 100755 index 0000000..d801ced --- /dev/null +++ b/components/system/test/os_test.h @@ -0,0 +1,68 @@ +#ifndef __OS_TEST_H__ +#define __OS_TEST_H__ + +#define CONFIG_SYS_LOG_COLOR_ON 1 +#undef CONFIG_SYS_LOG_LEVEL +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_DBG +#include "sys_log.h" + +#include "os/os.h" +#include +#include + +#define OSTest_Assert 1 + +/* 测试使用到的内存结构 */ +typedef struct +{ + u8_t UseRate; //记录 CPU 使用率 + uint CyclePreMS; //计算平均每毫秒运行了任务的循环次数 + uint StickCount; //累计系统嘀嗒时钟的中断次数 + uint Error_Line; //调度暂停位置 + void (*SysTickRoute)(void); //每当产生系统节拍时钟中断时运行的测试函数 + + os_mutex_t Mem_Mutex1; + os_mutex_t Mem_Mutex2; + os_mutex_t Mem_Mutex3; + os_mutex_t Mem_Mutex4; + + uint Mutex_Value; + + struct + { + os_thread_t thread_handle; //任务句柄 + os_timer_t timer_handle; //任务句柄 + struct Type_OS_TaskMemDef *AllocMem; //申请到的内存地址 + uint RunTimes; //任务循环次数 + uint EventTimes; // + uint Stack_Used; //任务最大使用堆栈 + uint Stack_Empty; //任务最大剩余堆栈 + vuint Count; + double Result_dFPU; + volatile float Result_fFPU; + } Task[8]; + + struct + { + uint TotalWrite; + uint TotalRead; + uint WriteLog; + uint ReadComp; + uint DebugTime1; + uint DebugTime2; + } BufTest; + +} Type_OS_TestMem_Def; + +extern Type_OS_TestMem_Def rm_Test; + +extern os_work_t test_work_handle[10]; +extern os_fifo_t fifo_handle[10]; +extern os_queue_t queue_handle[10]; +extern os_pipe_t pipe_handle[10]; + +/* 用于测试的简便工具 */ +os_thread_t *os_test_create_thread(os_thread_t *thread_handle, void (*TaskRoute)(void *), char *name, s16_t Priority); //简单创建任务 + +#endif +// diff --git a/components/system/test/os_test_ipc_fifo.c b/components/system/test/os_test_ipc_fifo.c new file mode 100755 index 0000000..ba23d4d --- /dev/null +++ b/components/system/test/os_test_ipc_fifo.c @@ -0,0 +1,81 @@ +#include "os_test.h" + +static vuint test_flag = 0; + +static void _os_test_fifo_handler_0(void *arg) +{ + SYS_LOG_INF("测试创建和清空, 应出现1个警告"); + void *first_handle; + SYS_ASSERT_FALSE(os_fifo_q_create(&fifo_handle[0]) != OS_OK, ""); + first_handle = fifo_handle[0].handle; + os_fifo_q_clr(&fifo_handle[0]); + os_fifo_q_delete(&fifo_handle[0]); + + SYS_LOG_INF("测试警告, 应出现3个警告"); + SYS_ASSERT_FALSE(os_fifo_q_create(&fifo_handle[0]) != OS_OK, ""); + uint32_t *p32 = os_fifo_alloc(sizeof(*p32)); + SYS_ASSERT_FALSE(p32 == 0, ""); + *p32 = 0x1234abcd; + SYS_ASSERT_FALSE(os_fifo_put(&fifo_handle[0], p32) != OS_OK, ""); + SYS_ASSERT_FALSE(os_fifo_put(&fifo_handle[0], p32) == OS_OK, ""); // os_fifo_put(): fifo_data not valid + SYS_ASSERT_FALSE(os_fifo_take(&fifo_handle[0], 0) != p32, ""); + SYS_ASSERT_FALSE(*p32 != 0x1234abcd, ""); + SYS_ASSERT_FALSE(os_fifo_take(&fifo_handle[0], 0) != 0, ""); + SYS_ASSERT_FALSE(os_fifo_free(p32) != OS_OK, ""); + SYS_ASSERT_FALSE(os_fifo_free(p32) == OS_OK, ""); // os_fifo_free(): 0x.. was not in fifo queue, can not to free + SYS_ASSERT_FALSE(os_fifo_put(&fifo_handle[0], p32) == OS_OK, ""); // os_fifo_put(): fifo_data not valid + + SYS_LOG_INF("测试清除"); + SYS_ASSERT_FALSE(os_fifo_alloc(sizeof(*p32)) != p32, ""); + SYS_ASSERT_FALSE(os_fifo_put(&fifo_handle[0], p32) != OS_OK, ""); + os_fifo_q_clr(&fifo_handle[0]); + SYS_ASSERT_FALSE(os_fifo_take(&fifo_handle[0], 0) != 0, ""); + SYS_ASSERT_FALSE(os_fifo_alloc(sizeof(*p32)) != p32, ""); + SYS_ASSERT_FALSE(os_fifo_free(p32) != OS_OK, ""); + + SYS_LOG_INF("测试自动释放已在队列内的数据"); + SYS_ASSERT_FALSE(os_fifo_alloc(sizeof(*p32)) != p32, ""); + SYS_ASSERT_FALSE(os_fifo_put(&fifo_handle[0], p32) != 0, ""); + os_fifo_q_delete(&fifo_handle[0]); + SYS_ASSERT_FALSE(os_fifo_q_create(&fifo_handle[0]) != OS_OK, ""); + SYS_ASSERT_FALSE(first_handle != fifo_handle[0].handle, ""); + SYS_ASSERT_FALSE(os_fifo_alloc(sizeof(*p32)) != p32, ""); + SYS_ASSERT_FALSE(os_fifo_put(&fifo_handle[0], p32) != OS_OK, ""); + os_fifo_q_delete(&fifo_handle[0]); + test_flag = 1; +} + +static void _os_test_fifo_t1(void *arg) +{ + test_flag = test_flag << 4 | 1; + void *mem = os_fifo_take(&fifo_handle[0], -1u); + test_flag = test_flag << 4 | 2; + os_fifo_free(mem); +} + +void os_test_fifo(void) +{ + SYS_LOG_INF("-------------------------------"); + test_flag = 0; + os_work_create(&test_work_handle[0], "", _os_test_fifo_handler_0, NULL, 1); + os_work_submit(default_os_work_q_hdl, &test_work_handle[0], 0); + os_work_delete(&test_work_handle[0]); + SYS_ASSERT_FALSE(test_flag == 0, ""); + + SYS_LOG_INF("-------------------------------"); + size_t max_block_size; + os_heap_info(NULL, NULL, &max_block_size); + static os_thread_t t1; + void *mem; + + SYS_LOG_INF("测试提交 FIFO 唤醒"); + test_flag = 0; + os_fifo_q_create(&fifo_handle[0]); + os_test_create_thread(&t1, _os_test_fifo_t1, "t1", OS_PRIORITY_HIGHEST); + SYS_ASSERT_FALSE(test_flag != 0x1, ""); + mem = os_fifo_alloc(0x10); + SYS_ASSERT_FALSE(test_flag != 0x1, ""); + os_fifo_put(&fifo_handle[0], mem); + SYS_ASSERT_FALSE(test_flag != 0x12, ""); + os_fifo_q_delete(&fifo_handle[0]); +} diff --git a/components/system/test/os_test_ipc_pipe.c b/components/system/test/os_test_ipc_pipe.c new file mode 100755 index 0000000..e9ba1e9 --- /dev/null +++ b/components/system/test/os_test_ipc_pipe.c @@ -0,0 +1,57 @@ +#include "os_test.h" + +static vuint test_flag = 0; + +static void _os_test_pipe_handler_0(void *arg) +{ + uint32_t data = 0; + uint8_t *p8 = (uint8_t *)&data; + SYS_LOG_INF("测试创建和清空, 应出现1个警告"); + void *first_handle; + SYS_ASSERT_FALSE(os_pipe_create(&pipe_handle[0], 4) != OS_OK, ""); + first_handle = pipe_handle[0].handle; + os_pipe_clr(&pipe_handle[0]); + os_pipe_delete(&pipe_handle[0]); + + SYS_LOG_INF("测试状态"); + SYS_ASSERT_FALSE(os_pipe_create(&pipe_handle[0], 3) != OS_OK, ""); + SYS_ASSERT_FALSE(first_handle != pipe_handle[0].handle, ""); + SYS_ASSERT_FALSE(os_pipe_is_ne(&pipe_handle[0]) != false, ""); + SYS_ASSERT_FALSE(os_pipe_get_valid_size(&pipe_handle[0]) != 0, ""); + SYS_ASSERT_FALSE(os_pipe_get_empty_size(&pipe_handle[0]) != 3, ""); + + SYS_LOG_INF("测试写"); + SYS_ASSERT_FALSE(os_pipe_poll_write(&pipe_handle[0], 1) != 1, ""); + SYS_ASSERT_FALSE(os_pipe_is_ne(&pipe_handle[0]) != true, ""); + SYS_ASSERT_FALSE(os_pipe_get_valid_size(&pipe_handle[0]) != 1, ""); + SYS_ASSERT_FALSE(os_pipe_get_empty_size(&pipe_handle[0]) != 2, ""); + p8[0] = 2, p8[1] = 3; + SYS_ASSERT_FALSE(os_pipe_fifo_fill(&pipe_handle[0], &data, sizeof(data)) != 2, ""); + SYS_ASSERT_FALSE(os_pipe_get_valid_size(&pipe_handle[0]) != 3, ""); + SYS_ASSERT_FALSE(os_pipe_get_empty_size(&pipe_handle[0]) != 0, ""); + + SYS_LOG_INF("测试读"); + data = -1u; + SYS_ASSERT_FALSE(os_pipe_poll_read(&pipe_handle[0], &p8[0]) != 1, ""); + SYS_ASSERT_FALSE(os_pipe_is_ne(&pipe_handle[0]) != true, ""); + SYS_ASSERT_FALSE(os_pipe_get_valid_size(&pipe_handle[0]) != 2, ""); + SYS_ASSERT_FALSE(os_pipe_get_empty_size(&pipe_handle[0]) != 1, ""); + SYS_ASSERT_FALSE(os_pipe_fifo_read(&pipe_handle[0], &p8[1], 3) != 2, ""); + SYS_ASSERT_FALSE(os_pipe_is_ne(&pipe_handle[0]) != false, ""); + SYS_ASSERT_FALSE(os_pipe_get_valid_size(&pipe_handle[0]) != 0, ""); + SYS_ASSERT_FALSE(os_pipe_get_empty_size(&pipe_handle[0]) != 3, ""); + SYS_ASSERT_FALSE(data != 0xff030201, ""); + + os_pipe_delete(&pipe_handle[0]); + test_flag = 1; +} + +void os_test_pipe(void) +{ + SYS_LOG_INF("-------------------------------"); + test_flag = 0; + os_work_create(&test_work_handle[0], "", _os_test_pipe_handler_0, NULL, 1); + os_work_submit(default_os_work_q_hdl, &test_work_handle[0], 0); + os_work_delete(&test_work_handle[0]); + SYS_ASSERT_FALSE(test_flag == 0, ""); +} diff --git a/components/system/test/os_test_ipc_queue.c b/components/system/test/os_test_ipc_queue.c new file mode 100755 index 0000000..992de9c --- /dev/null +++ b/components/system/test/os_test_ipc_queue.c @@ -0,0 +1,47 @@ +#include "os_test.h" + +static vuint test_flag = 0; + +static void _os_test_queue_t3(void *arg) +{ + uint64_t dst_64 = 0; + test_flag = test_flag << 4 | 1; + SYS_ASSERT_FALSE(os_queue_recv(&queue_handle[0], &dst_64, OS_WAIT_FOREVER) != OS_OK, ""); + SYS_ASSERT_FALSE(dst_64 != 0x1234567890abcdef, ""); + test_flag = test_flag << 4 | 2; +} + +void os_test_queue(void) +{ + SYS_LOG_INF("-------------------------------"); + static os_thread_t t2; + uint64_t src_64 = 0x1234567890abcdef; + uint64_t dst_64 = 0; + int systime_start; + int systime_end; + os_queue_create(&queue_handle[0], 1, sizeof(src_64)); + + SYS_LOG_INF("测试发送和接收"); + test_flag = 0; + os_test_create_thread(&t2, _os_test_queue_t3, "t3", OS_PRIORITY_HIGHEST); + SYS_ASSERT_FALSE(test_flag != 0x1, "test_flag = 0x%x", test_flag); + SYS_ASSERT_FALSE(os_queue_send(&queue_handle[0], &src_64, 100) != OS_OK, ""); + SYS_ASSERT_FALSE(test_flag != 0x12, "test_flag = 0x%x", test_flag); + + SYS_LOG_INF("测试发送和发送超时"); + systime_start = os_get_sys_ticks(); + SYS_ASSERT_FALSE(os_queue_send(&queue_handle[0], &src_64, 100) != OS_OK, ""); + SYS_ASSERT_FALSE(os_queue_send(&queue_handle[0], &src_64, 100) == OS_OK, ""); + systime_end = os_get_sys_ticks(); + SYS_ASSERT_FALSE(systime_end - systime_start < 100 || systime_end - systime_start > 120, "systime_end - systime_start = %d", systime_end - systime_start); + + SYS_LOG_INF("测试接收和接收超时"); + systime_start = os_get_sys_ticks(); + SYS_ASSERT_FALSE(os_queue_recv(&queue_handle[0], &dst_64, 100) != OS_OK, ""); + SYS_ASSERT_FALSE(dst_64 != src_64, ""); + SYS_ASSERT_FALSE(os_queue_recv(&queue_handle[0], &dst_64, 100) == OS_OK, ""); + systime_end = os_get_sys_ticks(); + SYS_ASSERT_FALSE(systime_end - systime_start < 100 || systime_end - systime_start > 120, "systime_end - systime_start = %d", systime_end - systime_start); + + os_queue_delete(&queue_handle[0]); +} diff --git a/components/system/test/os_test_sem.c b/components/system/test/os_test_sem.c new file mode 100755 index 0000000..7524f53 --- /dev/null +++ b/components/system/test/os_test_sem.c @@ -0,0 +1,46 @@ +#include "os_test.h" + +static os_thread_t thread_1; +static os_thread_t thread_2; +static os_thread_t thread_3; +static os_sem_t test_sem_fifo; +static vuint test_flag = 0; +static void _test_os_t1(void *arg) +{ + os_sem_take(&test_sem_fifo, OS_WAIT_FOREVER); + test_flag = test_flag << 4 | 1; + os_sem_take(&test_sem_fifo, OS_WAIT_FOREVER); + test_flag = test_flag << 4 | 1; +} +static void _test_os_t2(void *arg) +{ + os_sem_take(&test_sem_fifo, OS_WAIT_FOREVER); + test_flag = test_flag << 4 | 2; +} +static void _test_os_t3(void *arg) +{ + os_sem_take(&test_sem_fifo, OS_WAIT_FOREVER); + test_flag = test_flag << 4 | 3; +} + +void os_test_sem(uint TestTime) +{ + SYS_LOG_INF(""); + + SYS_LOG_DBG("测试 os_sem_create()"); + SYS_ASSERT_FALSE(os_sem_create(&test_sem_fifo, 1, 3) != OS_OK, ""); + + SYS_LOG_DBG("测试 OS_SEM_TYPE_FIFO"); + test_flag = 0; + os_test_create_thread(&thread_1, _test_os_t1, "task01-2", 2); + os_test_create_thread(&thread_2, _test_os_t2, "task02-3", 3); + os_test_create_thread(&thread_3, _test_os_t3, "task03-4", 4); + SYS_ASSERT(test_flag == 0x1, ""); + + os_sem_release(&test_sem_fifo); + os_sem_release(&test_sem_fifo); + os_sem_release(&test_sem_fifo); + SYS_ASSERT(test_flag == 0x1321, "test_flag = %#x", test_flag); + + os_sem_delete(&test_sem_fifo); +} diff --git a/components/system/test/os_test_thread_sleep.c b/components/system/test/os_test_thread_sleep.c new file mode 100755 index 0000000..dde5a09 --- /dev/null +++ b/components/system/test/os_test_thread_sleep.c @@ -0,0 +1,27 @@ +#include "os_test.h" + +void os_test_sleep(uint TestTime) +{ + vuint start_tick; + vuint test_tick; + int offset; + + SYS_LOG_INF("测试延时"); + + SYS_LOG_DBG("测试 os_thread_sleep()"); + os_thread_sleep(1); + start_tick = os_get_sys_ticks(); + os_thread_sleep(10); + test_tick = os_get_sys_ticks(); + offset = test_tick - start_tick; + SYS_ASSERT_FALSE(offset != 10 && offset != 11, "offset = %d", offset); + + SYS_LOG_DBG("测试 os_thread_sleep_until()"); + os_thread_sleep(1); + start_tick = os_get_sys_ticks(); + + os_thread_sleep(30); + test_tick = os_get_sys_ticks(); + offset = test_tick - start_tick; + SYS_ASSERT_FALSE(offset < 30 || offset > 31, "offset = %d", offset); //使用软件测试时允许有1毫秒的误差 +} diff --git a/components/system/test/os_test_thread_yield.c b/components/system/test/os_test_thread_yield.c new file mode 100755 index 0000000..2c591eb --- /dev/null +++ b/components/system/test/os_test_thread_yield.c @@ -0,0 +1,110 @@ +#include "os_test.h" + +static os_thread_t thread_1; +static os_thread_t thread_2; +static os_thread_t thread_3; +static os_thread_t thread_4; +static vuint count1 = 0; +static vuint count2 = 0; +static vuint count3 = 0; +static vuint count4 = 0; + +#define _TEST_TIMES 2000 + +static void _test_os_t1(void *arg) +{ + count1 = 0; + while (1) + { + if (count1++ % _TEST_TIMES == 0) + { + SYS_LOG_DBG("%d", count1 / _TEST_TIMES); + if (count1 >= (_TEST_TIMES * 10)) + { + return; + } + } + os_thread_yield(); + } +} +static void _test_os_t2(void *arg) +{ + count2 = 0; + while (1) + { + if (count2++ % _TEST_TIMES == 0) + { + SYS_LOG_DBG("%d", count2 / _TEST_TIMES); + if (count2 >= (_TEST_TIMES * 10)) + { + return; + } + } + os_thread_yield(); + } +} + +static void _test_os_t3(void *arg) +{ + count3 = 0; + while (1) + { + if (count3++ % _TEST_TIMES == 0) + { + SYS_LOG_DBG("%d", count3 / _TEST_TIMES); + if (count3 >= (_TEST_TIMES * 10)) + { + return; + } + } + os_thread_suspend(NULL); + SYS_ASSERT_FALSE(count3 != count4, "count3 = %d, count4 = %d, suspend fail", count3, count4); + } +} +static void _test_os_t4(void *arg) +{ + count4 = 0; + while (1) + { + if (count4++ % _TEST_TIMES == 0) + { + SYS_LOG_DBG("%d", count4 / _TEST_TIMES); + if (count4 >= (_TEST_TIMES * 10)) + { + return; + } + } + os_thread_resume(&thread_3); + SYS_ASSERT_FALSE(count4 + 1 != count3, "count3 = %d, count4 = %d, thread 3 dead!", count3, count4); + } +} + +void os_test_yield(uint TestTime) +{ + uint start_tick, test_tick, time_nS; + SYS_LOG_INF(""); + + os_scheduler_suspend(); + + os_test_create_thread(&thread_1, _test_os_t1, "task01-4", 4); + os_test_create_thread(&thread_2, _test_os_t2, "task02-4", 4); + + start_tick = os_get_sys_ticks(); + os_scheduler_resume(); + test_tick = os_get_sys_ticks(); + time_nS = (test_tick - start_tick) * 1000000 / (count1 + count2); + SYS_LOG_DBG("平均时间: %d.%03d uS", time_nS / 1000, time_nS % 1000); + + os_scheduler_suspend(); + + os_test_create_thread(&thread_3, _test_os_t3, "task03-3", 3); + os_test_create_thread(&thread_4, _test_os_t4, "task04-2", 2); + + start_tick = os_get_sys_ticks(); + os_scheduler_resume(); + test_tick = os_get_sys_ticks(); + time_nS = (test_tick - start_tick) * 1000000 / (count3 + count4); + SYS_LOG_DBG("平均时间: %d.%03d uS", time_nS / 1000, time_nS % 1000); + + os_sys_print_info(); +} diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..421eeb0 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,47 @@ + +set(srcs-app +) + +# 基础程序 +set(srcs-common + "ota.c" + "../app/app_main.c" + "../app/console.c" + "../app/drivers/data_port/sb_data_port.c" + "../app/drivers/data_port/uart/uart_port.c" + "../app/button/button_event.c" +) + +# 自定义框架抽象层 +set(srcs-components + "../components/system/source/k_kit/k_kit.c" + "../components/system/source/shell/sh_vt100.c" + "../components/system/source/shell/sh_vset.c" + "../components/system/source/shell/sh.c" + "../sal/esp32s3/kernel/os_heap.c" + "../sal/esp32s3/kernel/os_mutex.c" + "../sal/esp32s3/kernel/os_hook.c" + "../sal/esp32s3/kernel/os_timer.c" + "../sal/esp32s3/kernel/os_semaphore.c" + "../sal/esp32s3/kernel/os_thread.c" + "../sal/esp32s3/kernel/os_kit.c" + "../sal/esp32s3/kernel/os_service.c" + "../sal/esp32s3/chip/uart_esp32.c" + "../sal/esp32s3/soc_shell.c" + "../app/config/board_config.c" +) +set(incs + "../app" + "../components/system/include" + "../components/system/source" + "../components/system/source/k_kit" + "../components/system/source/shell" + "../sal/esp32s3" + "../sal/esp32s3/kernel" + "${IDF_PATH}/components/freertos/FreeRTOS-Kernel/include/freertos" +) + +idf_component_register(SRCS "main.c" + ${srcs-components} + ${srcs-common} + INCLUDE_DIRS "." ${incs}) diff --git a/main/main.c b/main/main.c new file mode 100755 index 0000000..b04faee --- /dev/null +++ b/main/main.c @@ -0,0 +1,116 @@ +/** + * @file main.c + * @author LokLiang + * @brief 根据实际平台启动并初始化,用户 APP 入口为 void work_app_main(void *arg) + * @version 0.1 + * @date 2023-11-24 + * + * @copyright Copyright (c) 2023 + * + */ + +#include "os/os.h" +#include "app_main.h" + +#include "ota.h" +#include "nvs_flash.h" +#include "esp_err.h" + +#undef SYS_LOG_DOMAIN +#define SYS_LOG_DOMAIN "OS" +#include "os_util.h" + +#ifndef CONFIG_OS_THREAD_MAIN_STACK_SIZE +#define CONFIG_OS_THREAD_MAIN_STACK_SIZE 0x1C00 +#endif + +#ifndef CONFIG_OS_THREAD_MAIN_PRIORITY +#define CONFIG_OS_THREAD_MAIN_PRIORITY OS_PRIORITY_NORMAL +#endif + +static unsigned s_int_nest; + +static unsigned _port_interrupt_save(void) +{ + if (s_int_nest == 0) + { + os_interrupt_disable(); + } + return s_int_nest++; +} + +static void _port_interrupt_restore(unsigned nest) +{ + s_int_nest = nest; + if (nest == 0) + { + os_interrupt_enable(); + } +} + +static k_work_q_t *_port_get_work_q_hdl(void) +{ + struct os_thread_handle *thread_handle = os_thread_get_self(); + os_work_q_list_t *work_q_list = thread_handle->work_q_list; + return &work_q_list->work_q_handle; +} + +static void _port_thread_sleep(k_tick_t sleep_ticks) +{ + vTaskDelay(sleep_ticks); +} + +static void _os_init(void) +{ + static k_init_t const init_struct = { + .malloc = os_malloc, + .free = os_free, + .get_sys_ticks = os_get_sys_ticks, + .interrupt_save = _port_interrupt_save, + .interrupt_restore = _port_interrupt_restore, + .scheduler_disable = os_scheduler_suspend, + .scheduler_enable = os_scheduler_resume, + .get_work_q_hdl = _port_get_work_q_hdl, + .thread_sleep = _port_thread_sleep, + }; + k_init(&init_struct); + + os_work_q_create(default_os_work_q_hdl, + "app-work_q", + CONFIG_OS_THREAD_MAIN_STACK_SIZE, + CONFIG_OS_THREAD_MAIN_PRIORITY); + + extern void work_app_main(void *arg); + static os_work_t _work_hdl_init; + os_work_create(&_work_hdl_init, "work-main", work_app_main, NULL, 3); + os_work_submit(default_os_work_q_hdl, &_work_hdl_init, 0); +} + +static void _init_prev_nvs(void) +{ + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + // OTA app partition table has a smaller NVS partition size than the + // non-OTA partition table. This size mismatch may cause NVS + // initialization to fail. If this happens, we erase NVS partition and + // initialize NVS again. + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK(err); +} + +int app_main(void) +{ + /* ota */ + ota_partition_check(); + + /* 初始化平台相关 */ + _init_prev_nvs(); + + /* 初始化 os 抽象层 */ + _os_init(); + + return 0; +} diff --git a/main/ota.c b/main/ota.c new file mode 100644 index 0000000..304607d --- /dev/null +++ b/main/ota.c @@ -0,0 +1,75 @@ +#include "ota.h" + +#include "esp_partition.h" +#include "esp_ota_ops.h" +#include "esp_flash_partitions.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN +#define SYS_LOG_DOMAIN "OTA" +#define CONS_ABORT() +#include "sys_log.h" + +#define HASH_LEN 32 /* SHA-256 digest length */ +static void _print_sha256(const uint8_t *image_hash, const char *label) +{ + char hash_print[HASH_LEN * 2 + 1]; + hash_print[HASH_LEN * 2] = 0; + for (int i = 0; i < HASH_LEN; ++i) + { + sprintf(&hash_print[i * 2], "%02x", image_hash[i]); + } + SYS_LOG_INF("%s %s", label, hash_print); +} + +void ota_partition_check() +{ + uint8_t sha_256[HASH_LEN] = {0}; + esp_partition_t partition; + + // get sha256 digest for the partition table + partition.address = ESP_PARTITION_TABLE_OFFSET; + partition.size = ESP_PARTITION_TABLE_MAX_LEN; + partition.type = ESP_PARTITION_TYPE_DATA; + esp_partition_get_sha256(&partition, sha_256); + _print_sha256(sha_256, "SHA-256 for the partition table:"); + + // get sha256 digest for bootloader + partition.address = ESP_BOOTLOADER_OFFSET; + partition.size = ESP_PARTITION_TABLE_OFFSET; + partition.type = ESP_PARTITION_TYPE_APP; + esp_partition_get_sha256(&partition, sha_256); + _print_sha256(sha_256, "SHA-256 for bootloader: "); + + // get sha256 digest for running partition + esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256); + _print_sha256(sha_256, "SHA-256 for current firmware: "); + + const esp_partition_t *running = esp_ota_get_running_partition(); + esp_ota_img_states_t ota_state; + if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) + { + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) + { + // run diagnostic function ... + bool diagnostic_is_ok = true; // diagnostic(); + if (diagnostic_is_ok) + { + SYS_LOG_INF("Diagnostics completed successfully! Continuing execution ..."); + esp_ota_mark_app_valid_cancel_rollback(); + } + else + { + SYS_LOG_ERR("Diagnostics failed! Start rollback to the previous version ..."); + esp_ota_mark_app_invalid_rollback_and_reboot(); + } + } + else + { + SYS_LOG_INF(""); + } + } + else + { + SYS_LOG_INF(""); + } +} diff --git a/main/ota.h b/main/ota.h new file mode 100644 index 0000000..a59bd8e --- /dev/null +++ b/main/ota.h @@ -0,0 +1,3 @@ +#pragma once + +void ota_partition_check(void); diff --git a/partitions.csv b/partitions.csv new file mode 100755 index 0000000..3f3e1ea --- /dev/null +++ b/partitions.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap,,,, +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, , 0x5C000, +otadata, data, ota, , 0x2000, +phy_init, data, phy, , 0x1000, +bcfg, 0x40, 0x00, 0x6F000, 0x1000, +ota_0, app, ota_0, , 0x1B0000, +ota_1, app, ota_1, , 0x1B0000, diff --git a/sal/esp32s3/chip/uart_esp32.c b/sal/esp32s3/chip/uart_esp32.c new file mode 100755 index 0000000..dd0ad39 --- /dev/null +++ b/sal/esp32s3/chip/uart_esp32.c @@ -0,0 +1,131 @@ +#include "drivers/chip/uart.h" + +#include "driver/uart.h" + +#define SYS_LOG_DOMAIN "uart" +#include "sys_log.h" + +void drv_uart_pin_configure_txd(hal_id_t id, uint8_t pin) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_pin_configure_rxd(hal_id_t id, uint8_t pin) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_enable(hal_id_t id) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_disable(hal_id_t id) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_init(hal_id_t id, const uart_param_t *param) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_deinit(hal_id_t id) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_set_br(hal_id_t id, unsigned clk, unsigned baudrate) +{ + SYS_LOG_ERR("func not set"); +} + +unsigned drv_uart_get_br(hal_id_t id, unsigned clk) +{ + SYS_LOG_ERR("func not set"); + return -1; +} + +int drv_uart_poll_read(hal_id_t id, void *data) +{ + return uart_read_bytes(id, data, 1, 0); +} + +int drv_uart_poll_write(hal_id_t id, uint8_t data) +{ + SYS_LOG_ERR("func not set"); + return 0; +} + +int drv_uart_fifo_read(hal_id_t id, void *data, int size) +{ + int ret = 0; + for (int i = 0; i < size; i++) + { + if (drv_uart_poll_read(id, &((char *)data)[i]) <= 0) + { + break; + } + ++ret; + } + return 0; +} + +int drv_uart_fifo_fill(hal_id_t id, const void *data, int size) +{ + for (unsigned i = 0; i < size; i++) + { + if (drv_uart_poll_write(id, ((char *)data)[i]) < 0) + { + return -1; + } + } + return size; +} + +int drv_uart_irq_callback_enable(hal_id_t id, uart_isr_cb_fn cb) +{ + SYS_LOG_ERR("func not set"); + return -1; +} + +int drv_uart_irq_callback_disable(hal_id_t id) +{ + SYS_LOG_ERR("func not set"); + return -1; +} + +void drv_uart_irq_tx_enable(hal_id_t id, int priority) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_irq_tx_disable(hal_id_t id) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_irq_rx_enable(hal_id_t id, int priority) +{ + SYS_LOG_ERR("func not set"); +} + +void drv_uart_irq_rx_disable(hal_id_t id) +{ + SYS_LOG_ERR("func not set"); +} + +bool drv_uart_wait_tx_busy(hal_id_t id) +{ + return false; +} + +bool drv_uart_is_tx_ready(hal_id_t id) +{ + return true; +} + +bool drv_uart_is_rx_ready(hal_id_t id) +{ + return true; +} diff --git a/sal/esp32s3/kernel/os_debug.h b/sal/esp32s3/kernel/os_debug.h new file mode 100755 index 0000000..d0dc4ce --- /dev/null +++ b/sal/esp32s3/kernel/os_debug.h @@ -0,0 +1,27 @@ +#ifndef _OS_DEBUG_H_ +#define _OS_DEBUG_H_ + +#undef CONFIG_SYS_LOG_LEVEL +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF +#define SYS_LOG_DOMAIN "OS" +#include "sys_log.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define OS_LOG(FLAG, FMT, ...) _DO_SYS_LOG(FLAG, FMT, ##__VA_ARGS__) +#define OS_DBG(FMT, ...) SYS_LOG_DBG(FMT, ##__VA_ARGS__) +#define OS_INF(FMT, ...) SYS_LOG_INF(FMT, ##__VA_ARGS__) +#define OS_WRN(FMT, ...) SYS_LOG_WRN(FMT, ##__VA_ARGS__) +#define OS_ERR(FMT, ...) SYS_LOG_ERR(FMT, ##__VA_ARGS__) + +#define OS_ASS_ISR() SYS_ASSERT(!os_is_isr_context(), "function '%s' exec in ISR contex", __FUNCTION__) +#define OS_ASS_HDL(EXP, HANDLE) SYS_ASSERT(EXP, "handle %p", HANDLE) + +#ifdef __cplusplus +} +#endif + +#endif /* _OS_DEBUG_H_ */ diff --git a/sal/esp32s3/kernel/os_heap.c b/sal/esp32s3/kernel/os_heap.c new file mode 100755 index 0000000..1bc088f --- /dev/null +++ b/sal/esp32s3/kernel/os_heap.c @@ -0,0 +1,46 @@ +#include "os/os.h" +#include "os_util.h" +#include "portable.h" +#include + +void *os_malloc(size_t size) +{ + return pvPortMalloc(size); +} + +void *os_calloc(size_t size) +{ + void *ret = os_malloc(size); + if (ret) + { + memset(ret, 0, size); + } + return ret; +} + +void *os_realloc(void *p, size_t size) +{ +#if (configSTACK_ALLOCATION_FROM_SEPARATE_HEAP == 1) + return realloc(p, size); +#else + OS_DBG("%s() fail @ %d function not support!\r\n", __func__, __LINE__); + return NULL; +#endif +} + +void os_free(void *p) +{ + vPortFree(p); +} + +void os_heap_info(size_t *used_size, size_t *free_size, size_t *max_block_size) +{ + if (max_block_size) + { + *max_block_size = 0; + } + if (used_size) + *used_size = 0; + if (free_size) + *free_size = xPortGetFreeHeapSize(); +} diff --git a/sal/esp32s3/kernel/os_hook.c b/sal/esp32s3/kernel/os_hook.c new file mode 100755 index 0000000..414b614 --- /dev/null +++ b/sal/esp32s3/kernel/os_hook.c @@ -0,0 +1,124 @@ +#define SYS_LOG_DOMAIN "OS" +#include "os/os.h" +#include "sys_log.h" +#include "FreeRTOS.h" +#include "task.h" + +StackType_t uxTimerTaskStack[configTIMER_TASK_STACK_DEPTH]; + +void vAssertCalled(void); +void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName); +void vApplicationMallocFailedHook(void); +void vApplicationIdleHook(void); +void vApplicationTickHook(void); +void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, + StackType_t **ppxTimerTaskStackBuffer, + uint32_t *pulTimerTaskStackSize); +void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, + StackType_t **ppxIdleTaskStackBuffer, + uint32_t *pulIdleTaskStackSize); + +void vAssertCalled(void) +{ + volatile unsigned long looping = 0; + + os_interrupt_disable(); + { + /* Use the debugger to set ul to a non-zero value in order to step out + * of this function to determine why it was called. */ + while (looping == 0LU) + { + } + } + os_interrupt_enable(); +} + +void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) +{ + SYS_LOG_ERR("task %p(%s) stack over flow\n", pxTask, pcTaskName); + /* Run time stack overflow checking is performed if + * configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook + * function is called if a stack overflow is detected. */ + taskDISABLE_INTERRUPTS(); + + for (;;) + { + } +} + +void vApplicationMallocFailedHook(void) +{ + /* Called if a call to pvPortMalloc() fails because there is insufficient + * free memory available in the FreeRTOS heap. pvPortMalloc() is called + * internally by FreeRTOS API functions that create tasks, queues, software + * timers, and semaphores. The size of the FreeRTOS heap is set by the + * configTOTAL_HEAP_SIZE configuration constant in FreeRTOSConfig.h. */ + taskDISABLE_INTERRUPTS(); + + for (;;) + { + } +} + +void vApplicationIdleHook(void) +{ + /* This is just a trivial example of an idle hook. It is called on each + * cycle of the idle task. It must *NOT* attempt to block. In this case the + * idle task just queries the amount of FreeRTOS heap that remains. See the + * memory management section on the https://www.FreeRTOS.org web site for memory + * management options. If there is a lot of heap memory free then the + * configTOTAL_HEAP_SIZE value in FreeRTOSConfig.h can be reduced to free up + * RAM. */ +} + +void vApplicationTickHook(void) +{ +} + +void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, + StackType_t **ppxTimerTaskStackBuffer, + uint32_t *pulTimerTaskStackSize) +{ + /* If the buffers to be provided to the Timer task are declared inside this + * function then they must be declared static - otherwise they will be allocated on + * the stack and so not exists after this function exits. */ + static StaticTask_t xTimerTaskTCB; + + /* Pass out a pointer to the StaticTask_t structure in which the Timer + * task's state will be stored. */ + *ppxTimerTaskTCBBuffer = &xTimerTaskTCB; + + /* Pass out the array that will be used as the Timer task's stack. */ + *ppxTimerTaskStackBuffer = uxTimerTaskStack; + + /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. + * Note that, as the array is necessarily of type StackType_t, + * configMINIMAL_STACK_SIZE is specified in words, not bytes. */ + *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; +} + +/* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide an + * implementation of vApplicationGetIdleTaskMemory() to provide the memory that is + * used by the Idle task. */ +void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, + StackType_t **ppxIdleTaskStackBuffer, + uint32_t *pulIdleTaskStackSize) +{ + /* If the buffers to be provided to the Idle task are declared inside this + * function then they must be declared static - otherwise they will be allocated on + * the stack and so not exists after this function exits. */ + static StaticTask_t xIdleTaskTCB; + static StackType_t uxIdleTaskStack[configMINIMAL_STACK_SIZE]; + + /* Pass out a pointer to the StaticTask_t structure in which the Idle task's + * state will be stored. */ + *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; + + /* Pass out the array that will be used as the Idle task's stack. */ + *ppxIdleTaskStackBuffer = uxIdleTaskStack; + + /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer. + * Note that, as the array is necessarily of type StackType_t, + * configMINIMAL_STACK_SIZE is specified in words, not bytes. */ + *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; +} diff --git a/sal/esp32s3/kernel/os_kit.c b/sal/esp32s3/kernel/os_kit.c new file mode 100755 index 0000000..700f94c --- /dev/null +++ b/sal/esp32s3/kernel/os_kit.c @@ -0,0 +1,886 @@ +#include "os/os.h" +#include "os_util.h" +#include "k_kit.h" + +static os_work_q_t s_default_work_q_handle; +os_work_q_t *default_os_work_q_hdl = &s_default_work_q_handle; + +static void _os_work_q_thread(void *arg); +static void _os_work_q_resume(void *arg); + +static void _os_work_q_thread(void *arg) +{ + os_work_q_list_t *list = arg; + for (;;) + { + k_tick_t tick = k_work_q_handler(&list->work_q_handle); + os_sem_take(&list->sem_handle, OS_TicksToMSecs(tick)); + } +} + +static void _os_work_q_resume(void *arg) +{ + os_sem_release(arg); +} + +os_state os_work_q_create(os_work_q_t *work_q_handle, // 队列句柄 + const char *name, // 队列名 + size_t stack_size, // 栈大小(字节) + os_priority priority // 优先级(0 为最低,CONFIG_OS_MAX_PRIORITY 为最高。若输入为负值则表示优先级为 (CONFIG_OS_MAX_PRIORITY + 1 + priority)) +) +{ + os_work_q_list_t *list = NULL; + os_state ret; + + do + { + if (os_work_q_is_valid(work_q_handle) != false) + { + OS_WRN("work_q is initialized"); + ret = OS_FAIL; + break; + } + + list = os_malloc(sizeof(*list)); + if (list == NULL) + { + ret = OS_E_NOMEM; + break; + } + else + { + memset(list, 0, sizeof(*list)); + } + + if (k_work_q_create(&list->work_q_handle) != 0) + { + ret = OS_E_NOMEM; + break; + } + + ret = os_sem_create(&list->sem_handle, 0, 1); + if (ret != OS_OK) + { + break; + } + + ret = os_thread_create(&list->thread, + name, + _os_work_q_thread, + list, + stack_size, + priority); + if (ret != OS_OK) + { + break; + } + + if (work_q_handle) + { + work_q_handle->handle = list; + } + + struct os_thread_handle *thread_handle = list->thread.handle; + thread_handle->work_q_list = list; + + k_work_q_resume_regist(&list->work_q_handle, _os_work_q_resume, &list->sem_handle); + + ret = OS_OK; + + } while (0); + + if (ret != OS_OK) + { + if (list) + { + if (list->thread.handle) + os_thread_delete(&list->thread); + if (list->sem_handle.handle) + os_sem_delete(&list->sem_handle); + if (list->work_q_handle.hdl) + k_work_q_delete(&list->work_q_handle); + os_free(list); + } + } + + return ret; +} + +os_state os_work_q_delete(os_work_q_t *work_q_handle) +{ + OS_ASS_ISR(); + + os_state ret; + + do + { + if (os_work_q_is_valid(work_q_handle) == false) + { + OS_WRN("work_q is invalid"); + ret = OS_FAIL; + break; + } + + os_scheduler_suspend(); + + os_work_q_list_t *list = work_q_handle->handle; + if (list) + { + os_thread_delete(&list->thread); + os_sem_delete(&list->sem_handle); + k_work_q_delete(&list->work_q_handle); + os_free(list); + work_q_handle->handle = NULL; + ret = OS_OK; + } + else + { + OS_ERR("err %p", work_q_handle); + ret = OS_E_PARAM; + } + + os_scheduler_resume(); + + } while (0); + + return ret; +} + +bool os_work_q_is_valid(os_work_q_t *work_q_handle) +{ + bool ret; + + if (work_q_handle == NULL || work_q_handle->handle == NULL) + { + ret = false; + } + else + { + ret = k_work_q_is_valid(work_q_handle->handle); + } + + return ret; +} + +bool os_work_q_delayed_state(os_work_q_t *work_q_handle) +{ + bool ret; + + if (os_work_q_is_valid(work_q_handle) == false) + { + OS_WRN("work_q is invalid"); + ret = false; + } + else + { + ret = k_work_q_delayed_state(work_q_handle->handle); + } + + return ret; +} + +bool os_work_q_ready_state(os_work_q_t *work_q_handle) +{ + bool ret; + + if (os_work_q_is_valid(work_q_handle) == false) + { + OS_WRN("work_q is invalid"); + ret = false; + } + else + { + ret = k_work_q_ready_state(work_q_handle->handle); + } + + return ret; +} + +os_state os_work_create(os_work_t *work_handle, const char *name, os_work_fn work_route, void *arg, uint8_t sub_prior) +{ + OS_ASS_ISR(); + k_err_t ret = k_work_create((k_work_t *)work_handle, name, (k_work_fn)work_route, arg, sub_prior); + + if (ret == 0) + { + return OS_OK; + } + else + { + return OS_E_NOMEM; + } +} + +void os_work_delete(os_work_t *work_handle) +{ + OS_ASS_ISR(); + k_work_delete((k_work_t *)work_handle); +} + +bool os_work_is_valid(os_work_t *work_handle) +{ + bool ret = k_work_is_valid((k_work_t *)work_handle); + return ret; +} + +bool os_work_is_pending(os_work_t *work_handle) +{ + bool ret = k_work_is_pending((k_work_t *)work_handle); + return ret; +} + +os_time_t os_work_time_remain(os_work_t *work_handle) +{ + k_tick_t ret = k_work_time_remain((k_work_t *)work_handle); + return os_calc_ticks_to_msec(ret); +} + +void os_work_submit(os_work_q_t *work_q_handle, os_work_t *work_handle, os_time_t delay_ms) +{ + if (os_work_q_is_valid(work_q_handle) == false) + { + OS_WRN("work_q is invalid"); + } + else + { + k_work_submit(work_q_handle->handle, (k_work_t *)work_handle, os_calc_msec_to_ticks(delay_ms)); + } +} + +void os_work_resume(os_work_t *work_handle, os_time_t delay_ms) +{ + k_work_resume((k_work_t *)work_handle, os_calc_msec_to_ticks(delay_ms)); +} + +void os_work_suspend(os_work_t *work_handle) +{ + k_work_suspend((k_work_t *)work_handle); +} + +void os_work_yield(os_time_t ms) +{ + k_work_yield(os_calc_msec_to_ticks(ms)); +} + +void os_work_sleep(os_time_t ms) +{ + k_work_sleep(os_calc_msec_to_ticks(ms)); +} + +void os_work_later(os_time_t ms) +{ + k_work_later(os_calc_msec_to_ticks(ms)); +} + +void os_work_later_until(os_time_t ms) +{ + k_work_later_until(os_calc_msec_to_ticks(ms)); +} + +#include "os/os.h" +#include "os_util.h" + +#include "k_kit.h" +#include "os/os_semaphore.h" +#include "queue.h" + +#include + +static void *_os_wait_memory(void *(*fn)(void *func_handle, va_list arp), void *func_handle, os_sem_t *sem, os_time_t wait_ms, ...); +static void *_os_ipc_fifo_take(void *fifo_handle, va_list arp); +static void *_os_ipc_queue_recv(void *queue_handle, va_list arp); +static void *_os_ipc_queue_send(void *queue_handle, va_list arp); +static void *_os_ipc_queue_alloc(void *queue_handle, va_list arp); +static void *_os_ipc_queue_take(void *queue_handle, va_list arp); + +struct os_fifo_q_handle +{ + k_fifo_t handle; + os_sem_t sem_take; +}; + +os_state os_fifo_q_create(os_fifo_t *fifo_handle) +{ + os_state ret; + + do + { + struct os_fifo_q_handle *fifo = os_malloc(sizeof(*fifo)); + if (fifo == NULL) + { + fifo_handle->handle = NULL; + ret = OS_E_NOMEM; + break; + } + + memset(fifo, 0, sizeof(*fifo)); + + if (k_fifo_q_create(&fifo->handle) != 0) + { + os_free(fifo); + ret = OS_E_NOMEM; + break; + } + + os_sem_create(&fifo->sem_take, 0, 1); + fifo_handle->handle = fifo; + + ret = OS_OK; + + } while (0); + + return ret; +} + +os_state os_fifo_q_delete(os_fifo_t *fifo_handle) +{ + OS_ASS_HDL(os_fifo_q_is_valid(fifo_handle), fifo_handle->handle); + + struct os_fifo_q_handle *fifo = fifo_handle->handle; + + os_scheduler_suspend(); + os_sem_delete(&fifo->sem_take); + k_fifo_q_delete(&fifo->handle); + os_free(fifo); + fifo_handle->handle = NULL; + os_scheduler_resume(); + + return OS_OK; +} + +os_state os_fifo_q_clr(os_fifo_t *fifo_handle) +{ + OS_ASS_HDL(os_fifo_q_is_valid(fifo_handle), fifo_handle->handle); + + struct os_fifo_q_handle *fifo = fifo_handle->handle; + k_fifo_q_clr(&fifo->handle); + + return OS_OK; +} + +static void *_os_wait_memory(void *(*fn)(void *func_handle, va_list arp), void *func_handle, os_sem_t *sem, os_time_t wait_ms, ...) +{ + va_list arp; + va_start(arp, wait_ms); + void *ret = fn(func_handle, arp); + va_end(arp); + + if (ret == NULL && os_scheduler_is_running() != false && !os_is_isr_context()) + { + TickType_t wait_ticks = os_calc_msec_to_ticks(wait_ms); + int wakeup_tick = os_get_sys_ticks() + wait_ticks; + + if (wait_ms) + { + while (wait_ticks == portMAX_DELAY || (int)(wakeup_tick - os_get_sys_ticks()) > 0) + { + os_sem_take(sem, wait_ms); + + va_start(arp, wait_ms); + ret = fn(func_handle, arp); + va_end(arp); + + if (ret) + { + os_sem_release(sem); + break; + } + + wait_ms = wakeup_tick - os_get_sys_ticks(); + } + } + + if (ret == NULL) + { + OS_DBG("waiting semaphore %p timeout", sem); + } + } + + return ret; +} + +void os_fifo_q_regist(os_fifo_t *fifo_handle, os_work_t *work_handle, int delay_ticks) +{ + struct os_fifo_q_handle *fifo = fifo_handle->handle; + k_fifo_q_regist(&fifo->handle, (k_work_t *)work_handle, delay_ticks); +} + +void os_fifo_q_unregist(os_fifo_t *fifo_handle) +{ + struct os_fifo_q_handle *fifo = fifo_handle->handle; + k_fifo_q_unregist(&fifo->handle); +} + +void *os_fifo_alloc(size_t size) +{ + OS_ASS_ISR(); + return k_fifo_alloc(size); +} + +os_state os_fifo_free(void *fifo_data) +{ + os_state ret; + if (k_fifo_free(fifo_data) == 0) + { + ret = OS_OK; + } + else + { + ret = OS_E_PARAM; + } + + return ret; +} + +os_state os_fifo_put(os_fifo_t *fifo_handle, void *fifo_data) +{ + OS_ASS_HDL(os_fifo_q_is_valid(fifo_handle), fifo_handle->handle); + + struct os_fifo_q_handle *fifo = fifo_handle->handle; + os_state ret; + + if (k_fifo_put(&fifo->handle, fifo_data) == 0) + { + os_sem_release(&fifo->sem_take); + ret = OS_OK; + } + else + { + ret = OS_E_PARAM; + } + + return ret; +} + +static void *_os_ipc_fifo_take(void *fifo_handle, va_list arp) +{ + return k_fifo_take(fifo_handle); +} + +void *os_fifo_take(os_fifo_t *fifo_handle, os_time_t wait_ms) +{ + OS_ASS_HDL(os_fifo_q_is_valid(fifo_handle), fifo_handle->handle); + + struct os_fifo_q_handle *fifo = fifo_handle->handle; + void *ret = _os_wait_memory(_os_ipc_fifo_take, &fifo->handle, &fifo->sem_take, wait_ms); + return ret; +} + +void *os_fifo_peek_head(os_fifo_t *fifo_handle) +{ + OS_ASS_HDL(os_fifo_q_is_valid(fifo_handle), fifo_handle->handle); + struct os_fifo_q_handle *fifo = fifo_handle->handle; + void *ret = k_fifo_peek_head(&fifo->handle); + return ret; +} + +void *os_fifo_peek_tail(os_fifo_t *fifo_handle) +{ + OS_ASS_HDL(os_fifo_q_is_valid(fifo_handle), fifo_handle->handle); + struct os_fifo_q_handle *fifo = fifo_handle->handle; + void *ret = k_fifo_peek_tail(&fifo->handle); + return ret; +} + +bool os_fifo_q_is_valid(os_fifo_t *fifo_handle) +{ + if (fifo_handle && fifo_handle->handle) + { + return true; + } + else + { + return false; + } +} + +struct os_queue_handle +{ + k_queue_t handle; + os_sem_t sem_send; + os_sem_t sem_recv; +}; + +os_state os_queue_create(os_queue_t *queue_handle, size_t queue_length, size_t item_size) +{ + os_state ret; + + do + { + struct os_queue_handle *queue = os_malloc(sizeof(*queue)); + if (queue == NULL) + { + queue_handle->handle = NULL; + ret = OS_E_NOMEM; + break; + } + + memset(queue, 0, sizeof(*queue)); + + if (k_queue_create(&queue->handle, queue_length, item_size) != 0) + { + os_free(queue); + ret = OS_E_NOMEM; + break; + } + + os_sem_create(&queue->sem_recv, 0, 1); + os_sem_create(&queue->sem_send, 0, 1); + queue_handle->handle = queue; + + ret = OS_OK; + + } while (0); + + return ret; +} + +os_state os_queue_delete(os_queue_t *queue_handle) +{ + OS_ASS_HDL(os_queue_is_valid(queue_handle), queue_handle->handle); + + struct os_queue_handle *queue = queue_handle->handle; + + os_scheduler_suspend(); + os_sem_delete(&queue->sem_recv); + os_sem_delete(&queue->sem_send); + k_queue_delete(&queue->handle); + os_free(queue); + queue_handle->handle = NULL; + os_scheduler_resume(); + + return OS_OK; +} + +os_state os_queue_clr(os_queue_t *queue_handle) +{ + OS_ASS_HDL(os_queue_is_valid(queue_handle), queue_handle->handle); + + struct os_queue_handle *queue = queue_handle->handle; + k_queue_clr(&queue->handle); + + return OS_OK; +} + +void os_queue_regist(os_queue_t *queue_handle, os_work_t *work_handle, int delay_ticks) +{ + struct os_queue_handle *queue = queue_handle->handle; + k_queue_regist(&queue->handle, (k_work_t *)work_handle, delay_ticks); +} + +void os_queue_unregist(os_queue_t *queue_handle) +{ + struct os_queue_handle *queue = queue_handle->handle; + k_queue_unregist(&queue->handle); +} + +static void *_os_ipc_queue_recv(void *queue_handle, va_list arp) +{ + void *dst = va_arg(arp, void *); + if (k_queue_recv(queue_handle, dst) == 0) + { + return (void *)!NULL; + } + else + { + return NULL; + } +} + +os_state os_queue_recv(os_queue_t *queue_handle, void *dst, os_time_t wait_ms) +{ + OS_ASS_HDL(os_queue_is_valid(queue_handle), queue_handle->handle); + + struct os_queue_handle *queue = queue_handle->handle; + os_state ret; + if (_os_wait_memory(_os_ipc_queue_recv, &queue->handle, &queue->sem_recv, wait_ms, dst) == NULL) + { + ret = OS_E_TIMEOUT; + } + else + { + os_sem_release(&queue->sem_send); + ret = OS_OK; + } + + return ret; +} + +static void *_os_ipc_queue_send(void *queue_handle, va_list arp) +{ + const void *src = va_arg(arp, void *); + if (k_queue_send(queue_handle, src) == 0) + { + return (void *)!NULL; + } + else + { + return NULL; + } +} + +os_state os_queue_send(os_queue_t *queue_handle, const void *src, os_time_t wait_ms) +{ + OS_ASS_HDL(os_queue_is_valid(queue_handle), queue_handle->handle); + + struct os_queue_handle *queue = queue_handle->handle; + os_state ret; + + if (_os_wait_memory(_os_ipc_queue_send, &queue->handle, &queue->sem_send, wait_ms, src) == NULL) + { + ret = OS_E_TIMEOUT; + } + else + { + os_sem_release(&queue->sem_recv); + ret = OS_OK; + } + + return ret; +} + +static void *_os_ipc_queue_alloc(void *queue_handle, va_list arp) +{ + return k_queue_alloc(queue_handle); +} + +void *os_queue_alloc(os_queue_t *queue_handle, os_time_t wait_ms) +{ + OS_ASS_HDL(os_queue_is_valid(queue_handle), queue_handle->handle); + + struct os_queue_handle *queue = queue_handle->handle; + void *ret = _os_wait_memory(_os_ipc_queue_alloc, &queue->handle, &queue->sem_send, wait_ms); + + return ret; +} + +os_state os_queue_free(void *queue_data) +{ + os_state ret; + + if (k_queue_free(queue_data) == 0) + { + k_queue_t *handle = k_queue_read_handle(queue_data); + struct os_queue_handle *queue = OS_CONTAINER_OF(handle, struct os_queue_handle, handle); + os_sem_release(&queue->sem_send); + ret = OS_OK; + } + else + { + ret = OS_E_PARAM; + } + + return ret; +} + +os_state os_queue_put(void *queue_data) +{ + os_state ret; + + if (k_queue_put(queue_data) == 0) + { + k_queue_t *handle = k_queue_read_handle(queue_data); + struct os_queue_handle *queue = OS_CONTAINER_OF(handle, struct os_queue_handle, handle); + os_sem_release(&queue->sem_recv); + ret = OS_OK; + } + else + { + ret = OS_E_PARAM; + } + + return ret; +} + +static void *_os_ipc_queue_take(void *queue_handle, va_list arp) +{ + return k_queue_take(queue_handle); +} + +void *os_queue_take(os_queue_t *queue_handle, os_time_t wait_ms) +{ + OS_ASS_HDL(os_queue_is_valid(queue_handle), queue_handle->handle); + + struct os_queue_handle *queue = queue_handle->handle; + void *ret = _os_wait_memory(_os_ipc_queue_take, &queue->handle, &queue->sem_recv, wait_ms); + + return ret; +} + +void *os_queue_peek_head(os_queue_t *queue_handle) +{ + OS_ASS_HDL(os_queue_is_valid(queue_handle), queue_handle->handle); + struct os_queue_handle *queue = queue_handle->handle; + void *ret = k_queue_peek_head(&queue->handle); + return ret; +} + +void *os_queue_peek_tail(os_queue_t *queue_handle) +{ + OS_ASS_HDL(os_queue_is_valid(queue_handle), queue_handle->handle); + struct os_queue_handle *queue = queue_handle->handle; + void *ret = k_queue_peek_tail(&queue->handle); + return ret; +} + +size_t os_queue_get_item_size(os_queue_t *queue_handle) +{ + struct os_queue_handle *queue = queue_handle->handle; + size_t ret = k_queue_get_item_size(&queue->handle); + return ret; +} + +bool os_queue_is_valid(os_queue_t *queue_handle) +{ + if (queue_handle && queue_handle->handle) + { + return true; + } + else + { + return false; + } +} + +struct os_pipe_handle +{ + k_pipe_t handle; +}; + +os_state os_pipe_create(os_pipe_t *pipe_handle, size_t pipe_size) +{ + os_state ret; + + do + { + struct os_pipe_handle *pipe = os_malloc(sizeof(*pipe)); + if (pipe == NULL) + { + pipe_handle->handle = NULL; + ret = OS_E_NOMEM; + } + + memset(pipe, 0, sizeof(*pipe)); + + if (k_pipe_create(&pipe->handle, pipe_size) != 0) + { + os_free(pipe); + ret = OS_E_NOMEM; + } + + pipe_handle->handle = pipe; + + ret = OS_OK; + + } while (0); + + return ret; +} + +os_state os_pipe_delete(os_pipe_t *pipe_handle) +{ + OS_ASS_HDL(os_pipe_is_valid(pipe_handle), pipe_handle->handle); + + struct os_pipe_handle *pipe = pipe_handle->handle; + + k_pipe_delete(&pipe->handle); + os_free(pipe); + pipe_handle->handle = NULL; + + return OS_OK; +} + +os_state os_pipe_clr(os_pipe_t *pipe_handle) +{ + OS_ASS_HDL(os_pipe_is_valid(pipe_handle), pipe_handle->handle); + + struct os_pipe_handle *pipe = pipe_handle->handle; + k_pipe_clr(&pipe->handle); + + return OS_OK; +} + +void os_pipe_regist(os_pipe_t *pipe_handle, os_work_t *work_handle, int delay_ticks) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + k_pipe_regist(&pipe->handle, (k_work_t *)work_handle, delay_ticks); +} + +void os_pipe_unregist(os_pipe_t *pipe_handle) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + k_pipe_unregist(&pipe->handle); +} + +size_t os_pipe_poll_write(os_pipe_t *pipe_handle, uint8_t data) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + size_t ret = k_pipe_poll_write(&pipe->handle, data); + return ret; +} + +size_t os_pipe_fifo_fill(os_pipe_t *pipe_handle, const void *data, size_t size) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + size_t ret = k_pipe_fifo_fill(&pipe->handle, data, size); + return ret; +} + +size_t os_pipe_poll_read(os_pipe_t *pipe_handle, uint8_t *data) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + size_t ret = k_pipe_poll_read(&pipe->handle, data); + return ret; +} + +size_t os_pipe_fifo_read(os_pipe_t *pipe_handle, void *data, size_t size) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + size_t ret = k_pipe_fifo_read(&pipe->handle, data, size); + return ret; +} + +bool os_pipe_is_ne(os_pipe_t *pipe_handle) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + bool ret = k_pipe_is_ne(&pipe->handle); + return ret; +} + +size_t os_pipe_get_valid_size(os_pipe_t *pipe_handle) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + size_t ret = k_pipe_get_valid_size(&pipe->handle); + return ret; +} + +size_t os_pipe_get_empty_size(os_pipe_t *pipe_handle) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + size_t ret = k_pipe_get_empty_size(&pipe->handle); + return ret; +} + +void os_pipe_peek_valid(os_pipe_t *pipe_handle, void **dst_base, size_t *dst_size) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + k_pipe_peek_valid(&pipe->handle, dst_base, dst_size); +} + +void os_pipe_peek_empty(os_pipe_t *pipe_handle, void **dst_base, size_t *dst_size) +{ + struct os_pipe_handle *pipe = pipe_handle->handle; + k_pipe_peek_empty(&pipe->handle, dst_base, dst_size); +} + +bool os_pipe_is_valid(os_pipe_t *pipe_handle) +{ + if (pipe_handle && pipe_handle->handle) + { + return true; + } + else + { + return false; + } +} diff --git a/sal/esp32s3/kernel/os_mutex.c b/sal/esp32s3/kernel/os_mutex.c new file mode 100755 index 0000000..7a59b1d --- /dev/null +++ b/sal/esp32s3/kernel/os_mutex.c @@ -0,0 +1,80 @@ +#include "os/os.h" +#include "os_util.h" +#include "semphr.h" + +os_state os_mutex_create(os_mutex_t *mutex) +{ + OS_ASS_HDL(!os_mutex_is_valid(mutex), mutex->handle); + + mutex->handle = xSemaphoreCreateMutex(); + if (mutex->handle == NULL) + { + OS_ERR("err %p\r\n", mutex->handle); + return OS_FAIL; + } + + return OS_OK; +} + +os_state os_mutex_delete(os_mutex_t *mutex) +{ + OS_ASS_HDL(os_mutex_is_valid(mutex), mutex->handle); + + vSemaphoreDelete(mutex->handle); + mutex->handle = NULL; + return OS_OK; +} + +os_state os_mutex_lock(os_mutex_t *mutex, os_time_t wait_ms) +{ + BaseType_t ret; + + OS_ASS_HDL(os_mutex_is_valid(mutex), mutex->handle); + + ret = xSemaphoreTake(mutex->handle, os_calc_msec_to_ticks(wait_ms)); + if (ret != pdPASS) + { + OS_DBG("%s() fail @ %d, %u ms\n", __func__, __LINE__, wait_ms); + return OS_FAIL; + } + + return OS_OK; +} + +os_state os_mutex_unlock(os_mutex_t *mutex) +{ + BaseType_t ret; + + OS_ASS_HDL(os_mutex_is_valid(mutex), mutex->handle); + + ret = xSemaphoreGive(mutex->handle); + if (ret != pdPASS) + { + OS_DBG("%s() fail @ %d\n", __func__, __LINE__); + return OS_FAIL; + } + + return OS_OK; +} + +os_thread_handle_t os_mutex_get_holder(os_mutex_t *mutex) +{ + if (!os_mutex_is_valid(mutex)) + { + return OS_INVALID_HANDLE; + } + + return (os_thread_handle_t)xSemaphoreGetMutexHolder(mutex->handle); +} + +bool os_mutex_is_valid(os_mutex_t *mutex) +{ + if (mutex && mutex->handle) + { + return true; + } + else + { + return false; + } +} diff --git a/sal/esp32s3/kernel/os_semaphore.c b/sal/esp32s3/kernel/os_semaphore.c new file mode 100755 index 0000000..61f7835 --- /dev/null +++ b/sal/esp32s3/kernel/os_semaphore.c @@ -0,0 +1,105 @@ +#include "os/os.h" +#include "os_util.h" +#include "semphr.h" + +os_state os_sem_create(os_sem_t *sem, size_t init_value, size_t max_value) +{ + OS_ASS_HDL(!os_sem_is_valid(sem), sem->handle); + + sem->handle = xSemaphoreCreateCounting(max_value, init_value); + if (sem->handle == NULL) + { + OS_ERR("err %p\r\n", sem->handle); + return OS_FAIL; + } + + return OS_OK; +} + +os_state os_sem_delete(os_sem_t *sem) +{ + OS_ASS_HDL(os_sem_is_valid(sem), sem->handle); + + vSemaphoreDelete(sem->handle); + sem->handle = NULL; + return OS_OK; +} + +os_state os_sem_take(os_sem_t *sem, os_time_t wait_ms) +{ + BaseType_t ret; + BaseType_t taskWoken; + + OS_ASS_HDL(os_sem_is_valid(sem), sem->handle); + + if (os_is_isr_context()) + { + if (wait_ms != 0) + { + OS_ERR("%s() in ISR, wait %u ms\n", __func__, wait_ms); + return OS_E_ISR; + } + taskWoken = pdFALSE; + ret = xSemaphoreTakeFromISR(sem->handle, &taskWoken); + if (ret != pdPASS) + { + OS_DBG("%s() fail @ %d\n", __func__, __LINE__); + return OS_E_TIMEOUT; + } + portYIELD_FROM_ISR(taskWoken); + } + else + { + ret = xSemaphoreTake(sem->handle, os_calc_msec_to_ticks(wait_ms)); + if (ret != pdPASS) + { + OS_DBG("%s() fail @ %d, %u ms\n", __func__, __LINE__, wait_ms); + return OS_E_TIMEOUT; + } + } + + return OS_OK; +} + +os_state os_sem_release(os_sem_t *sem) +{ + BaseType_t ret; + BaseType_t taskWoken; + + OS_ASS_HDL(os_sem_is_valid(sem), sem->handle); + + if (os_is_isr_context()) + { + taskWoken = pdFALSE; + ret = xSemaphoreGiveFromISR(sem->handle, &taskWoken); + if (ret != pdPASS) + { + OS_DBG("%s() fail @ %d\n", __func__, __LINE__); + return OS_FAIL; + } + portYIELD_FROM_ISR(taskWoken); + } + else + { + ret = xSemaphoreGive(sem->handle); + if (ret != pdPASS) + { + OS_DBG("%s() fail @ %d\n", __func__, __LINE__); + return OS_FAIL; + } + } + + return OS_OK; +} + +bool os_sem_is_valid(os_sem_t *sem) +{ + if (sem && sem->handle) + { + return true; + } + else + { + return false; + } +} diff --git a/sal/esp32s3/kernel/os_service.c b/sal/esp32s3/kernel/os_service.c new file mode 100755 index 0000000..aa92058 --- /dev/null +++ b/sal/esp32s3/kernel/os_service.c @@ -0,0 +1,184 @@ +#include "os/os.h" +#include "os_util.h" +#include "task.h" + +static size_t int_flag; +static portMUX_TYPE s_os_service = portMUX_INITIALIZER_UNLOCKED; + +int os_start(void *heap_mem, size_t heap_size) +{ + extern int os_entry(void *heap, size_t size); + return os_entry(heap_mem, heap_size); +} + +void os_int_entry(void) +{ + os_interrupt_disable(); + ++int_flag; + os_interrupt_enable(); +} + +void os_int_exit(void) +{ + os_interrupt_disable(); + int_flag -= !!int_flag; + os_interrupt_enable(); +} + +bool os_is_isr_context(void) +{ + if (int_flag) + { + return true; + } + else + { + return false; + } +} + +void os_interrupt_disable(void) +{ + portENTER_CRITICAL(&s_os_service); +} + +void os_interrupt_enable(void) +{ + portEXIT_CRITICAL(&s_os_service); +} + +void os_scheduler_suspend(void) +{ + vTaskSuspendAll(); +} + +void os_scheduler_resume(void) +{ + xTaskResumeAll(); +} + +bool os_scheduler_is_running(void) +{ + return (bool)(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); +} + +#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS +void os_sys_print_info(void) +{ + const size_t bytes_per_task = 40; /* see vTaskList description */ + char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task); + if (task_list_buffer == NULL) + { + OS_LOG(1, "failed to allocate buffer for vTaskList output\r\n"); + return; + } + fputs("Task Name\tStatus\tPrio\tHWM\tTask#", stdout); +#ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID + fputs("\tAffinity", stdout); +#endif + fputs("\n", stdout); + vTaskList(task_list_buffer); + fputs(task_list_buffer, stdout); + free(task_list_buffer); +} + +#else + +void os_sys_print_info(void) +{ + OS_LOG(1, "os_sys_print_info() not supported, please set configUSE_TRACE_FACILITY to 1\n"); +} + +#endif + +os_time_t os_get_sys_time(void) +{ + os_time_t ticks_count = xTaskGetTickCount(); + return OS_TicksToMSecs(ticks_count); +} + +size_t os_get_sys_ticks(void) +{ + return (size_t)xTaskGetTickCount(); +} + +os_time_t os_calc_ticks_to_msec(size_t ticks) +{ + os_time_t msec; + + if (ticks == OS_WAIT_FOREVER) + { + msec = portMAX_DELAY; + } + else if (ticks == 0) + { + msec = 0; + } + else + { + msec = OS_TicksToMSecs(ticks); + if (msec == 0) + { + msec = 1; + } + } + return msec; +} + +size_t os_calc_msec_to_ticks(os_time_t msec) +{ + size_t tick; + + if (msec == OS_WAIT_FOREVER) + { + tick = portMAX_DELAY; + } + else if (msec == 0) + { + tick = 0; + } + else + { + tick = OS_MSecsToTicks(msec); + if (tick == 0) + { + tick = 1; + } + } + return tick; +} + +size_t os_cpu_usage(void) +{ + return 100; +} + +#if (configUSE_POSIX_ERRNO == 1) + +extern int FreeRTOS_errno; + +int os_get_err(void) +{ + return FreeRTOS_errno; +} + +void os_set_err(int err) +{ + FreeRTOS_errno = err; +} + +#elif (configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0) + +#define OS_ERRNO_LOCATION_IDX 0 + +int os_get_err(void) +{ + return (int)pvTaskGetThreadLocalStoragePointer(NULL, OS_ERRNO_LOCATION_IDX); +} + +void os_set_err(int err) +{ + vTaskSetThreadLocalStoragePointer(NULL, OS_ERRNO_LOCATION_IDX, (void *)err); +} + +#endif diff --git a/sal/esp32s3/kernel/os_thread.c b/sal/esp32s3/kernel/os_thread.c new file mode 100755 index 0000000..1f78185 --- /dev/null +++ b/sal/esp32s3/kernel/os_thread.c @@ -0,0 +1,277 @@ +#include "os/os.h" +#include "os_util.h" +#include "task.h" +#include "list/slist.h" + +/* Macro used to convert os_priority to the kernel's real priority */ +#define OS_KERNEL_PRIO(prio) (prio) + +static slist_t s_thread_list; + +static void _os_delete(struct os_thread_handle *thread_handle) +{ + os_scheduler_suspend(); + + slist_remove(&s_thread_list, &thread_handle->node); + thread_handle->thread->handle = NULL; + if (thread_handle->flag_free) + { + os_free(thread_handle->thread); + } + os_free(thread_handle); + + os_scheduler_resume(); +} + +static void _os_thread_start(void *arg) +{ + struct os_thread_handle *thread_handle = arg; + thread_handle->entry(thread_handle->arg); + _os_delete(thread_handle); + vTaskDelete(NULL); +} + +os_state os_thread_init(os_thread_t *thread, + const char *name, + os_thread_entry_t entry, + void *arg, + void *stack_base, + size_t stack_size, + os_priority priority) +{ +#if (configSUPPORT_STATIC_ALLOCATION == 1) + + OS_ASS_HDL(!os_thread_is_valid(thread), thread->handle); + + if (stack_size <= sizeof(struct os_thread_handle) + sizeof(StaticTask_t)) + { + OS_ERR("err size: %d <= %d\r\n", stack_size, sizeof(struct os_thread_handle) + sizeof(StaticTask_t)); + return OS_FAIL; + } + + struct os_thread_handle *thread_handle = stack_base; + StaticTask_t *pxTaskBuffer = (StaticTask_t *)&thread_handle[1]; + StackType_t *puxStackBuffer = (StackType_t *)&pxTaskBuffer[1]; + + thread_handle->thread = thread; + thread_handle->entry = entry; + thread_handle->arg = arg; + thread_handle->flag_free = 0; + thread_handle->pxCreatedTask = xTaskCreateStatic(_os_thread_start, + name, + stack_size / sizeof(StackType_t), + thread_handle, + OS_KERNEL_PRIO(priority), + puxStackBuffer, + pxTaskBuffer); + thread->handle = thread_handle; + return OS_OK; + +#else + + OS_ERR("err configSUPPORT_STATIC_ALLOCATION != 1\r\n"); + return OS_FAIL; + +#endif +} + +os_state os_thread_create(os_thread_t *thread, + const char *name, + os_thread_entry_t entry, + void *arg, + size_t stack_size, + os_priority priority) +{ + int ret; + + if (thread) + { + OS_ASS_HDL(!os_thread_is_valid(thread), thread->handle); + } + + struct os_thread_handle *thread_handle = os_malloc(sizeof(struct os_thread_handle)); + if (thread_handle == NULL) + { + return OS_E_NOMEM; + } + + if (thread == NULL) + { + thread = os_malloc(sizeof(os_thread_t)); + if (thread == NULL) + { + os_free(thread_handle); + return OS_E_NOMEM; + } + memset(thread, 0, sizeof(os_thread_t)); + thread_handle->flag_free = 1; + } + else + { + thread_handle->flag_free = 0; + } + thread->handle = thread_handle; + thread_handle->work_q_list = NULL; + thread_handle->thread = thread; + thread_handle->entry = entry; + thread_handle->arg = arg; + slist_init_node(&thread_handle->node); + + os_scheduler_suspend(); + slist_insert_tail(&s_thread_list, &thread_handle->node); + os_scheduler_resume(); + + ret = xTaskCreatePinnedToCore(_os_thread_start, + name, + stack_size / sizeof(StackType_t), + thread_handle, + OS_KERNEL_PRIO(priority), + &thread_handle->pxCreatedTask, + 1); + if (ret != pdPASS) + { + OS_ERR("err %d\r\n", ret); + + os_scheduler_suspend(); + slist_remove(&s_thread_list, &thread_handle->node); + os_scheduler_resume(); + + if (thread_handle->flag_free == 0) + { + thread->handle = NULL; + } + if (thread_handle->thread) + { + os_free(thread_handle->thread); + } + os_free(thread_handle); + return OS_FAIL; + } + return OS_OK; +} + +os_state os_thread_delete(os_thread_t *thread) +{ + if (thread == NULL) /* delete self */ + { + struct os_thread_handle *thread_handle = os_thread_get_self(); + _os_delete(thread_handle); + vTaskDelete(NULL); + return OS_OK; + } + else + { + OS_ASS_HDL(os_thread_is_valid(thread), thread->handle); + struct os_thread_handle *thread_handle = thread->handle; + vTaskDelete(thread_handle->pxCreatedTask); + _os_delete(thread_handle); + return OS_OK; + } +} + +void os_thread_sleep(os_time_t msec) +{ + vTaskDelay((TickType_t)os_calc_msec_to_ticks(msec)); +} + +void os_thread_yield(void) +{ + taskYIELD(); +} + +void os_thread_suspend(os_thread_t *thread) +{ + if (thread) + { + OS_ASS_HDL(os_thread_is_valid(thread), thread->handle); + struct os_thread_handle *thread_handle = thread->handle; + vTaskSuspend(thread_handle->pxCreatedTask); + } + else + { + vTaskSuspend(NULL); + } +} + +void os_thread_resume(os_thread_t *thread) +{ + OS_ASS_HDL(os_thread_is_valid(thread), thread->handle); + struct os_thread_handle *thread_handle = thread->handle; + vTaskResume(thread_handle->pxCreatedTask); +} + +os_thread_handle_t os_thread_get_self(void) +{ + os_thread_handle_t ret = NULL; + + os_scheduler_suspend(); + TaskHandle_t xTask = xTaskGetCurrentTaskHandle(); + struct os_thread_handle *thread_handle; + struct os_thread_handle *prev_handle = NULL; + int cnt = 0; + SLIST_FOR_EACH_CONTAINER(&s_thread_list, thread_handle, node) + { + if (thread_handle->pxCreatedTask == xTask) + { + ret = thread_handle->thread->handle; + break; + } + prev_handle = thread_handle; + cnt++; + } + if (cnt > 5) + { + slist_remove_next(&s_thread_list, &prev_handle->node, &thread_handle->node); + slist_insert_font(&s_thread_list, &thread_handle->node); + } + os_scheduler_resume(); + SYS_ASSERT(ret, "xTask: %p", xTask); + return ret; +} + +const char *os_thread_get_name(os_thread_t *thread) +{ + struct os_thread_handle *thread_handle = thread->handle; + return pcTaskGetName(thread_handle->pxCreatedTask); +} + +size_t os_thread_stack_min(os_thread_t *thread) +{ +#if INCLUDE_uxTaskGetStackHighWaterMark + TaskHandle_t xTask; + + if (thread != NULL) + { + if (os_thread_is_valid(thread)) + { + struct os_thread_handle *thread_handle = thread->handle; + xTask = thread_handle->pxCreatedTask; + } + else + { + return 0; + } + } + else + { + xTask = NULL; + } + + extern UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask); + return (uxTaskGetStackHighWaterMark(xTask) * sizeof(StackType_t)); +#else + return 0; +#endif +} + +bool os_thread_is_valid(os_thread_t *thread) +{ + if (thread && thread->handle) + { + return true; + } + else + { + return false; + } +} diff --git a/sal/esp32s3/kernel/os_timer.c b/sal/esp32s3/kernel/os_timer.c new file mode 100755 index 0000000..d3ebe3b --- /dev/null +++ b/sal/esp32s3/kernel/os_timer.c @@ -0,0 +1,219 @@ +#include "os/os.h" +#include "os_util.h" +#include "timers.h" + +/* TODO: what block time should be used ? */ +#define OS_TIMER_WAIT_FOREVER portMAX_DELAY +#define OS_TIMER_WAIT_NONE 0 + +/* Timer private data definition */ +typedef struct OS_TimerPriv +{ + TimerHandle_t handle; /* Timer handle */ + os_timer_cb_fn callback; /* Timer expire callback function */ + void *argument; /* Argument of timer expire callback function */ +} OS_TimerPriv_t; + +static void _os_timer_cb(TimerHandle_t xTimer) +{ + OS_TimerPriv_t *priv; + + priv = pvTimerGetTimerID(xTimer); + if (priv && priv->callback) + { + priv->callback(priv->argument); + } + else + { + OS_WRN("Invalid timer callback\n"); + } +} + +os_state os_timer_create(os_timer_t *timer, os_timer_cb_fn cb, void *arg) +{ + OS_TimerPriv_t *priv; + + OS_ASS_HDL(!os_timer_is_valid(timer), timer->handle); + + priv = os_malloc(sizeof(OS_TimerPriv_t)); + if (priv == NULL) + { + return OS_E_NOMEM; + } + + priv->callback = cb; + priv->argument = arg; + priv->handle = xTimerCreate("", + os_calc_msec_to_ticks(OS_WAIT_FOREVER), + pdFALSE, + priv, + _os_timer_cb); + if (priv->handle == NULL) + { + OS_ERR("err %p\r\n", priv->handle); + os_free(priv); + return OS_FAIL; + } + timer->handle = priv; + return OS_OK; +} + +static TimerHandle_t _os_timer_get_handle(os_timer_t *timer) +{ + OS_TimerPriv_t *priv = timer->handle; + return priv->handle; +} + +os_state os_timer_delete(os_timer_t *timer) +{ + TimerHandle_t handle; + int ret; + + OS_ASS_HDL(os_timer_is_valid(timer), timer->handle); + + handle = _os_timer_get_handle(timer); + ret = xTimerDelete(handle, OS_TIMER_WAIT_FOREVER); + if (ret != pdPASS) + { + OS_ERR("err %d\r\n", ret); + return OS_FAIL; + } + + OS_TimerPriv_t *priv = timer->handle; + timer->handle = NULL; + + os_free(priv); + + return OS_OK; +} + +os_state os_timer_start(os_timer_t *timer) +{ + TimerHandle_t handle; + int ret; + BaseType_t taskWoken; + + OS_ASS_HDL(os_timer_is_valid(timer), timer->handle); + + handle = _os_timer_get_handle(timer); + + if (os_is_isr_context()) + { + taskWoken = pdFALSE; + ret = xTimerStartFromISR(handle, &taskWoken); + if (ret != pdPASS) + { + OS_ERR("err %d\r\n", ret); + return OS_FAIL; + } + portYIELD_FROM_ISR(taskWoken); + } + else + { + ret = xTimerStart(handle, OS_TIMER_WAIT_NONE); + if (ret != pdPASS) + { + OS_ERR("err %d\r\n", ret); + return OS_FAIL; + } + } + + return OS_OK; +} + +os_state os_timer_set_period(os_timer_t *timer, os_timer_type_t type, os_time_t period_ms) +{ + TimerHandle_t handle; + TickType_t ticks; + int ret; + + OS_ASS_HDL(os_timer_is_valid(timer), timer->handle); + + handle = _os_timer_get_handle(timer); + ticks = os_calc_msec_to_ticks(period_ms); + + if (os_is_isr_context()) + { + BaseType_t taskWoken = pdFALSE; + ret = xTimerChangePeriodFromISR(handle, ticks, &taskWoken); + if (ret != pdPASS) + { + OS_ERR("err %d\r\n", ret); + return OS_FAIL; + } + portYIELD_FROM_ISR(taskWoken); + } + else + { + ret = xTimerChangePeriod(handle, ticks, OS_TIMER_WAIT_NONE); + if (ret != pdPASS) + { + OS_ERR("err %d\r\n", ret); + return OS_FAIL; + } + } + + vTimerSetReloadMode(handle, type == OS_TIMER_PERIODIC ? pdTRUE : pdFALSE); + + return OS_OK; +} + +os_state os_timer_stop(os_timer_t *timer) +{ + TimerHandle_t handle; + int ret; + BaseType_t taskWoken; + + OS_ASS_HDL(os_timer_is_valid(timer), timer->handle); + + handle = _os_timer_get_handle(timer); + + if (os_is_isr_context()) + { + taskWoken = pdFALSE; + ret = xTimerStopFromISR(handle, &taskWoken); + if (ret != pdPASS) + { + OS_ERR("err %d\r\n", ret); + return OS_FAIL; + } + portYIELD_FROM_ISR(taskWoken); + } + else + { + ret = xTimerStop(handle, OS_TIMER_WAIT_FOREVER); + if (ret != pdPASS) + { + OS_ERR("err %d\r\n", ret); + return OS_FAIL; + } + } + + return OS_OK; +} + +bool os_timer_is_pending(os_timer_t *timer) +{ + TimerHandle_t handle; + + if (!os_timer_is_valid(timer)) + { + return 0; + } + + handle = _os_timer_get_handle(timer); + + return (xTimerIsTimerActive(handle) != pdFALSE); +} + +bool os_timer_is_valid(os_timer_t *timer) +{ + if (timer && timer->handle) + { + return true; + } + else + { + return false; + } +} diff --git a/sal/esp32s3/kernel/os_util.h b/sal/esp32s3/kernel/os_util.h new file mode 100755 index 0000000..b35c1cc --- /dev/null +++ b/sal/esp32s3/kernel/os_util.h @@ -0,0 +1,33 @@ +#ifndef __OS_UTIL_H__ +#define __OS_UTIL_H__ + +#include "list/slist.h" +#include "os/os_common.h" +#include "os_debug.h" + +#include "k_kit.h" + +#include "FreeRTOS.h" +#include "task.h" + +#define OS_TICK_RATE configTICK_RATE_HZ + +typedef struct os_work_q_list +{ + k_work_q_t work_q_handle; + os_sem_t sem_handle; + os_thread_t thread; +} os_work_q_list_t; + +struct os_thread_handle +{ + slist_node_t node; + TaskHandle_t pxCreatedTask; + os_thread_t *thread; + os_work_q_list_t *work_q_list; + os_thread_entry_t entry; + void *arg; + uint8_t flag_free; +}; + +#endif /* __OS_UTIL_H__ */ diff --git a/sal/esp32s3/soc_shell.c b/sal/esp32s3/soc_shell.c new file mode 100644 index 0000000..5b2efca --- /dev/null +++ b/sal/esp32s3/soc_shell.c @@ -0,0 +1,27 @@ +#include "shell/sh.h" + +#include "esp_system.h" +#include "freertos/FreeRTOS.h" + +#define CONFIG_SYS_LOG_DUMP_ON 1 +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF +#define SYS_LOG_DOMAIN "SOC" +#include "sys_log.h" + +#include "soc_shell_base.c" +#include "soc_shell_nvs.c" + +SH_DEF_SUB_CMD( + sub_soc, + SH_SETUP_CMD("base", "Basic information of the system", NULL, sub_soc_base), // + SH_SETUP_CMD("nvs", "Partition for operating nvs", NULL, sub_soc_nvs), // +); + +SH_DEF_CMD( + _register_cmd_soc, + SH_SETUP_CMD("soc", "System on chip", NULL, sub_soc), ); + +void soc_shell_register(void) +{ + sh_register_cmd(&_register_cmd_soc); +} diff --git a/sal/esp32s3/soc_shell_base.c b/sal/esp32s3/soc_shell_base.c new file mode 100644 index 0000000..634357c --- /dev/null +++ b/sal/esp32s3/soc_shell_base.c @@ -0,0 +1,141 @@ +/** + * @file soc_shell_base.c + * @author LokLiang + * @brief 仅被 soc_shell.c 所包含,完成子命令列表及功能 + * @version 0.1 + * @date 2023-09-15 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __SOC_SHELL_BASE_C__ +#define __SOC_SHELL_BASE_C__ + +#include "shell/sh.h" +#include "esp_heap_caps.h" +#include "spi_flash_mmap.h" +#include "esp_flash.h" +#include "esp_chip_info.h" +#include "task.h" + +/* Unsigned integers. */ +#define PRIu8 "u" +#define PRIu16 "u" +#define PRIu32 "u" +#define PRIu64 __PRI64_PREFIX "u" + +SH_CMD_FN(_base_free); +SH_CMD_FN(_base_heap); +SH_CMD_FN(_base_version); +SH_CMD_FN(_base_tasks); +SH_CMD_FN(_base_restart); + +SH_DEF_SUB_CMD( + sub_soc_base, +#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS + SH_SETUP_CMD("tasks", "Get information about running tasks", _base_tasks, NULL), // +#endif + SH_SETUP_CMD("free", "Get the current size of free heap memory", _base_free, NULL), // + SH_SETUP_CMD("heap", "Get minimum size of free heap memory that was available during program execution", _base_heap, NULL), // + SH_SETUP_CMD("version", "Get version of chip and SDK", _base_version, NULL), // + SH_SETUP_CMD("restart", "Software reset of the chip", _base_restart, NULL), // +); + +SH_CMD_FN(_base_free) +{ + sh_echo(sh_hdl, "当前堆内存空闲大小: %d bytes\r\n", esp_get_free_heap_size()); + return 0; +} + +SH_CMD_FN(_base_heap) +{ + sh_echo(sh_hdl, "当前堆内存最少剩余: %d bytes\r\n", heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT)); + return 0; +} + +SH_CMD_FN(_base_version) +{ + const char *model; + esp_chip_info_t info; + uint32_t flash_size; + esp_chip_info(&info); + + switch (info.model) + { + case CHIP_ESP32: + model = "ESP32"; + break; + case CHIP_ESP32S2: + model = "ESP32-S2"; + break; + case CHIP_ESP32S3: + model = "ESP32-S3"; + break; + case CHIP_ESP32C3: + model = "ESP32-C3"; + break; + case CHIP_ESP32H2: + model = "ESP32-H2"; + break; + case CHIP_ESP32C2: + model = "ESP32-C2"; + break; + default: + model = "Unknown"; + break; + } + + if (esp_flash_get_size(NULL, &flash_size) != ESP_OK) + { + sh_echo(sh_hdl, "Get flash size failed"); + return 1; + } + sh_echo(sh_hdl, "IDF Version:%s\r\n", esp_get_idf_version()); + sh_echo(sh_hdl, "Chip info:\r\n"); + sh_echo(sh_hdl, "\tmodel:%s\r\n", model); + sh_echo(sh_hdl, "\tcores:%d\r\n", info.cores); + sh_echo(sh_hdl, "\tfeature:%s%s%s%s%" PRIu32 "%s\r\n", + info.features & CHIP_FEATURE_WIFI_BGN ? "/802.11bgn" : "", + info.features & CHIP_FEATURE_BLE ? "/BLE" : "", + info.features & CHIP_FEATURE_BT ? "/BT" : "", + info.features & CHIP_FEATURE_EMB_FLASH ? "/Embedded-Flash:" : "/External-Flash:", + flash_size / (1024 * 1024), " MB"); + sh_echo(sh_hdl, "\trevision number:%d\r\n", info.revision); + return 0; +} + +SH_CMD_FN(_base_tasks) +{ +#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS + const size_t bytes_per_task = 40; /* see vTaskList description */ + char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task); + if (task_list_buffer == NULL) + { + sh_echo(sh_hdl, "failed to allocate buffer for vTaskList output\r\n"); + return -1; + } + sh_echo(sh_hdl, "Task Name\tStatus\tPrio\tHWM\tTask#", stdout); +#ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID + sh_echo(sh_hdl, "\tAffinity", stdout); +#endif + sh_echo(sh_hdl, "\r\n", stdout); + vTaskList(task_list_buffer); + sh_echo(sh_hdl, task_list_buffer, stdout); + + sh_echo(sh_hdl, "Task_Name\tRun_Cnt\t\tUsage_Rate\r\n", stdout); + vTaskGetRunTimeStats(task_list_buffer); + sh_echo(sh_hdl, task_list_buffer, stdout); + + free(task_list_buffer); +#endif + return 0; +} + +SH_CMD_FN(_base_restart) +{ + esp_restart(); + return 0; +} + +#endif diff --git a/sal/esp32s3/soc_shell_nvs.c b/sal/esp32s3/soc_shell_nvs.c new file mode 100644 index 0000000..201f8bd --- /dev/null +++ b/sal/esp32s3/soc_shell_nvs.c @@ -0,0 +1,117 @@ +/** + * @file soc_shell_nvs.c + * @author LokLiang + * @brief 仅被 soc_shell.c 所包含,完成子命令列表及功能 + * @version 0.1 + * @date 2023-09-15 + * + * @copyright Copyright (c) 2023 + * + */ + +#ifndef __SOC_SHELL_NVS_C__ +#define __SOC_SHELL_NVS_C__ + +#include "shell/sh.h" +#include "shell/sh_vset.h" +#include "nvs_flash.h" +#include "os/os.h" + +static char s_nvs_str_buf[0x40]; + +static int _value_set_str(const char *argv[]) { SET_VAR(&s_nvs_str_buf, 0, 0); } + +SH_CMD_FN(_nvs_dump); +SH_CMD_FN(_nvs_erase); + +SH_DEF_SUB_CMD( + sub_soc_nvs, + SH_SETUP_CMD("dump", "Dump blob value for given key ", _nvs_dump, NULL), // + SH_SETUP_CMD("erase", "Erase the default NVS partition", _nvs_erase, NULL), // +); + +SH_CMD_FN(_nvs_dump) +{ + const char *argv_r[2] = {"", ""}; + size_t required_size; + char namespace_buf[sizeof(s_nvs_str_buf)]; + char key_buf[sizeof(s_nvs_str_buf)]; + int err = 0; + + argv_r[0] = argv[0]; + err = err ? err : _value_set_str(argv_r); + if (err == 0) + strcpy(namespace_buf, s_nvs_str_buf); + + argv_r[0] = argv[1]; + err = err ? err : _value_set_str(argv_r); + if (err == 0) + strcpy(key_buf, s_nvs_str_buf); + + if (err == 0) + { + nvs_handle nvs_hdl; + if (nvs_open(namespace_buf, NVS_READONLY, &nvs_hdl) == ESP_OK) + { + err = nvs_get_blob(nvs_hdl, key_buf, NULL, &required_size); + if (err == 0) + { + if (required_size) + { + void *data = os_malloc(required_size); + if (data) + { + err = nvs_get_blob(nvs_hdl, key_buf, data, &required_size); + if (err == 0) + { + SYS_LOG_DUMP(data, required_size, 0, 0); + } + else + { + sh_echo(sh_hdl, "Can not get blob key: '%s'\r\n", key_buf); + } + os_free(data); + } + else + { + sh_echo(sh_hdl, "os_malloc() fail\r\n"); + } + } + else + { + sh_echo(sh_hdl, "Blob data empty\r\n"); + } + } + else + { + sh_echo(sh_hdl, "Can not get blob key: '%s'\r\n", key_buf); + } + nvs_close(nvs_hdl); + } + else + { + sh_echo(sh_hdl, "Can not open name space: '%s'\r\n", namespace_buf); + } + } + + return err; +} + +SH_CMD_FN(_nvs_erase) +{ + sh_echo(sh_hdl, "正在擦除 NVS 分区 ...\r\n"); + esp_err_t err = nvs_flash_erase(); + if (err == ESP_OK) + { + sh_echo(sh_hdl, "操作成功,重启系统 ...\r\n"); + esp_restart(); + return 0; + } + else + { + sh_echo(sh_hdl, "操作异常: err = %d\r\n", err); + return -1; + } +} + +#endif diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 0000000..6ea7a4f --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1,24 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c3" +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PRODUCT_TYPE_LEDSTRIP=y +CONFIG_CAP_LED_STRIP=y +CONFIG_LED_STRIP_SKIP_PWRUP=y +CONFIG_BT_ENABLED=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y +CONFIG_ESP_SLEEP_POWER_DOWN_FLASH=y +CONFIG_ESP_IPC_TASK_STACK_SIZE=1536 +CONFIG_ESP_TIMER_TASK_STACK_SIZE=2048 +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=768 +CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=25 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=1536 +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_LOG_DEFAULT_LEVEL_NONE=y