参考代码
This commit is contained in:
109
components/system/source/k_kit/README.md
Executable file
109
components/system/source/k_kit/README.md
Executable file
@@ -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
|
||||
|
||||
4437
components/system/source/k_kit/k_kit.c
Executable file
4437
components/system/source/k_kit/k_kit.c
Executable file
File diff suppressed because it is too large
Load Diff
311
components/system/source/k_kit/k_kit.h
Executable file
311
components/system/source/k_kit/k_kit.h
Executable file
@@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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
|
||||
153
components/system/source/k_kit/kk.h
Executable file
153
components/system/source/k_kit/kk.h
Executable file
@@ -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
|
||||
104
components/system/source/shell/README.md
Executable file
104
components/system/source/shell/README.md
Executable file
@@ -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)
|
||||
3051
components/system/source/shell/sh.c
Executable file
3051
components/system/source/shell/sh.c
Executable file
File diff suppressed because it is too large
Load Diff
400
components/system/source/shell/sh.h
Executable file
400
components/system/source/shell/sh.h
Executable file
@@ -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
|
||||
1521
components/system/source/shell/sh_vset.c
Executable file
1521
components/system/source/shell/sh_vset.c
Executable file
File diff suppressed because it is too large
Load Diff
223
components/system/source/shell/sh_vset.h
Executable file
223
components/system/source/shell/sh_vset.h
Executable file
@@ -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
|
||||
455
components/system/source/shell/sh_vt100.c
Executable file
455
components/system/source/shell/sh_vt100.c
Executable file
@@ -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);
|
||||
117
components/system/source/shell/sh_vt100.h
Executable file
117
components/system/source/shell/sh_vt100.h
Executable file
@@ -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 /* 光标向上 光标向上 <n> 行 */
|
||||
#define CUD(n) "\x1b[%dB",n /* 光标向下 光标向下 <n> 行 */
|
||||
#define CUF(n) "\x1b[%dC",n /* 光标向前 光标向前(右)<n> 行 */
|
||||
#define CUB(n) "\x1b[%dD",n /* 光标向后 光标向后(左)<n> 行 */
|
||||
#define CNL(n) "\x1b[%dE",n /* 光标下一行 光标从当前位置向下 <n> 行 */
|
||||
#define CPL(n) "\x1b[%dF",n /* 光标当前行 光标从当前位置向上 <n> 行 */
|
||||
#define CHA(n) "\x1b[%dG",n /* 绝对光标水平 光标在当前行中水平移动到第 <n> 个位置 */
|
||||
#define VPA(n) "\x1b[%dd",n /* 绝对垂直行位置 光标在当前列中垂直移动到第 <n> 个位置 */
|
||||
#define CUP(y,x) "\x1b[%d;%dH",y,x /* 光标位置 *光标移动到视区中的 <x>; <y> 坐标,其中 <x> 是 <y> 行的列 */
|
||||
#define HVP(y,x) "\x1b[%d;%df",y,x /* 水平垂直位置 *光标移动到视区中的 <x>; <y> 坐标,其中 <x> 是 <y> 行的列 */
|
||||
|
||||
/*光标可见性*/
|
||||
#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 /* 插入字符 在当前光标位置插入 <n> 个空格,这会将所有现有文本移到右侧。 向右溢出屏幕的文本会被删除。*/
|
||||
#define DCH(n) "\x1b[%dP",n /* 删除字符 删除当前光标位置的 <n> 个字符,这会从屏幕右边缘以空格字符移动。*/
|
||||
#define ECH(n) "\x1b[%dX",n /* 擦除字符 擦除当前光标位置的 <n> 个字符,方法是使用空格字符覆盖它们。*/
|
||||
#define IL(n) "\x1b[%dL",n /* 插入行 将 <n> 行插入光标位置的缓冲区。 光标所在的行及其下方的行将向下移动。*/
|
||||
#define DL(n) "\x1b[%dM\r",n /* 删除行 从缓冲区中删除 <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
|
||||
Reference in New Issue
Block a user