diff --git a/demo/easy_transplant/call_demo/user_main.c b/demo/easy_transplant/call_demo/user_main.c new file mode 100644 index 0000000..16a998d --- /dev/null +++ b/demo/easy_transplant/call_demo/user_main.c @@ -0,0 +1,41 @@ +/** + * @file user_main.c + * @author fool_dog (2696652257@qq.com) + * @brief //> 此文件仅用于展示如何调用shell的逻辑,不要添加此文件进行编译 + * @version 1.0 + * @date 2024-08-09 + * + * @copyright Copyright (c) 2024 + * + */ + +#include "../shell_all.h" + +int user_main(void) +{ + letter_shell_init(); // 初始化shell + + for (;;) + { + letter_shell_task(); // 处理shell任务 + + // 建议隔一定时间调用这两个函数,可以在中断中调用,也可以在主循环中调用 + { + port_tx_trigger(); + port_rx_trigger(); + } + } +} + +//+********************************* 通常下面是接收和发送中断 **********************************/ +void rx_end_callback() +{ + // 在接收函数回调中或者实际接收到数据后调用 + port_rx_end(-1); // 如果知道实际接收了多少个字节就传入实际的字节数,不然传入负数让其自动根据上次调用是传入的大小进行处理 +} + +void tx_end_callback() +{ + // 在发送函数回调中,或者实际发送数据后调用 + port_tx_end(-1); // 如果知道实际发送了多少个字节就传入实际的字节数,不然传入负数让其自动根据上次调用是传入的大小进行处理 +} \ No newline at end of file diff --git a/demo/easy_transplant/image/readme/1723214231937.png b/demo/easy_transplant/image/readme/1723214231937.png new file mode 100644 index 0000000..9b3f31c Binary files /dev/null and b/demo/easy_transplant/image/readme/1723214231937.png differ diff --git a/demo/easy_transplant/image/readme/1723216984202.png b/demo/easy_transplant/image/readme/1723216984202.png new file mode 100644 index 0000000..36db16f Binary files /dev/null and b/demo/easy_transplant/image/readme/1723216984202.png differ diff --git a/demo/easy_transplant/image/readme/1723217000891.png b/demo/easy_transplant/image/readme/1723217000891.png new file mode 100644 index 0000000..076cd6d Binary files /dev/null and b/demo/easy_transplant/image/readme/1723217000891.png differ diff --git a/demo/easy_transplant/image/readme/1723217009356.png b/demo/easy_transplant/image/readme/1723217009356.png new file mode 100644 index 0000000..1533f6a Binary files /dev/null and b/demo/easy_transplant/image/readme/1723217009356.png differ diff --git a/demo/easy_transplant/readme.md b/demo/easy_transplant/readme.md new file mode 100644 index 0000000..1ab3de0 --- /dev/null +++ b/demo/easy_transplant/readme.md @@ -0,0 +1,38 @@ +# easy_transplant demo + +此目录文件为通用 letter-shell 移植的实例,目前仅提供了STM32 HAL库的适配文件 + +## 使用(以STM32 HAL库串口1 DMA适配为例)--AC6编译--AC5编译器已完善支持 + +在cubemx中开启串口,并且开启串口中断,添加串口的DMA发送和接收,DMA模式选择常规模式------其他时钟,调试接口之类的配置请自行配置 + +![1723214231937](image/readme/1723214231937.png) + +生成工程后打开 + +此处以keil为例 + +~~ 将keil编译器切换为AC6,AC5太老了编译又慢,一定要选用AC5导致的编译报错请自行解决 ~~ 已完善AC5编译器支持 + +1. 将此文件夹下shell_all.c和stm32_HAL_adapt文件夹下stm32_HAL_adapt.c添加编译 +2. 将shell根目录下src文件夹和extensions/shell_enhance和shell_all.h所在文件夹添加头文件包含 + +![1723216984202](image/readme/1723216984202.png) + +在main.c中包含shell_all.h头文件,调用一次 `letter_shell_init`,轮询调用 `letter_shell_task`,轮询或定期调用 `port_tx_trigger`和 `port_rx_trigger` + +![1723217000891](image/readme/1723217000891.png) + +OK,编译,烧录完事 + +![1723217009356](image/readme/1723217009356.png) + +# 其他移植可以参考stm32_HAL_adapt文件夹下实现 + +按照上诉方法进行移植也请看一下该文件夹下的实现,该实现下重写了STM32 HAL库的两个串口中断回调 + +# 其他说明 + +此例程移植力求不更改letter shell原仓库文件分布情况 + +shell对接最重要的在于read与write,通常是直接对接到外设上(例如串口),但是外设的速度是远低于内核的运行速度的,于是此例的移植对shell的输入输出都采用ringbuffer缓冲,这样方便解决在中断中调用shell读写(比如log组件)导致中断的的长时间运行,以及stm32_HAL_adapt中实现了支持FreeRTOS的情况下加锁之类的操作 diff --git a/demo/easy_transplant/ringbuffer/ringbuffer.h b/demo/easy_transplant/ringbuffer/ringbuffer.h new file mode 100644 index 0000000..88ceef0 --- /dev/null +++ b/demo/easy_transplant/ringbuffer/ringbuffer.h @@ -0,0 +1,350 @@ +/** + * @file ringbuffer.h + * @author 独霸一方 (2696652257@qq.com) + * @brief //> 部分参考cherryRB实现的环形缓冲区,完整实现参考:https://github.com/cherry-embedded/CherryRB + * 这里的API调用不进行加锁,请自行保证线程安全 + * @version 1.0 + * @date 2024-08-05 + * + * @copyright Copyright (c) 2024 + * + */ + +// > 单次包含宏定义 +#ifndef __RINGBUFFER_H_ +#define __RINGBUFFER_H_ + +#include +#include +#include + +#include + +#undef RINGBUFFER_ASSERT +#define RINGBUFFER_ASSERT(exp) (void)0 // 开启此宏关闭此文件用户自定义断言 + +// clang-format off +#ifndef RINGBUFFER_ASSERT + #include + #define RINGBUFFER_ASSERT(exp) \ + if (!(exp)) \ + { \ + printf("\"" #exp "\" assert failed at file: %s, line: %d", __FILE__, __LINE__); \ + for (;;) \ + ; \ + } +#endif // ! RINGBUFFER_ASSERT +// clang-format on + +// > C/C++兼容性宏定义 +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef uint16_t ring_size_t; + + typedef struct __ringbuffer_t ringbuffer_t; + struct __ringbuffer_t + { + bool busy; // 用于指示'慢速'外设是否正在操作缓冲区,比如DMA传输 + ring_size_t head; + ring_size_t tail; + ring_size_t data_count; // 当前队列中的数据量 + ring_size_t last_series_count; // 调用连续读或写时候实际设置的值 + ring_size_t buffer_size; // 缓冲区大小 + uint8_t *buffer; + }; + + static inline void ringbuffer_init(ringbuffer_t *rb, uint8_t *buffer, ring_size_t size); + static inline void ringbuffer_reset(ringbuffer_t *rb); + + static inline ring_size_t ringbuffer_get_free(ringbuffer_t *rb); // 获取剩余空间 + static inline ring_size_t ringbuffer_get_used(ringbuffer_t *rb); // 获取已使用空间 + static inline ring_size_t ringbuffer_get_size(ringbuffer_t *rb); // 获取缓冲区大小 + static inline ring_size_t ringbuffer_get_last_series_size(ringbuffer_t *rb); // 获取上次调用线性读取或者写入时候的设置的大小 + + static inline ring_size_t ringbuffer_write(ringbuffer_t *rb, const uint8_t *data, ring_size_t size); // 返回实际写入的数据量 + static inline ring_size_t ringbuffer_read(ringbuffer_t *rb, uint8_t *data, ring_size_t size); // 返回实际读取的数据量 + + static inline bool ringbuffer_write_byte(ringbuffer_t *rb, uint8_t data); // 返回是否写入成功 + static inline bool ringbuffer_read_byte(ringbuffer_t *rb, uint8_t *data); // 返回是否读取成功 + + // for linear read/write operation(such as DMA) + static inline uint8_t *ringbuffer_linear_write_setup(ringbuffer_t *rb, ring_size_t *size); // 返回写入的地址并设置实际能写入的数据量 + static inline uint8_t *ringbuffer_linear_read_setup(ringbuffer_t *rb, ring_size_t *size); // 返回读取的地址并设置实际能读取的数据量 + static inline ring_size_t ringbuffer_linear_write_done(ringbuffer_t *rb, ring_size_t size); // 返回实际写入的数据量 + static inline ring_size_t ringbuffer_linear_read_done(ringbuffer_t *rb, ring_size_t size); // 返回实际读取的数据量 + + static inline void ringbuffer_mark_busy(ringbuffer_t *rb); // 标记慢速外设正在操作缓冲区 + static inline void ringbuffer_mark_idle(ringbuffer_t *rb); // 标记慢速外设操作缓冲区结束 + static inline bool ringbuffer_is_busy(ringbuffer_t *rb); // 返回慢速外设是否正在操作缓冲区 + + //+********************************* 函数实现 **********************************/ + + static inline void ringbuffer_init(ringbuffer_t *rb, uint8_t *buffer, ring_size_t size) + { + RINGBUFFER_ASSERT(rb != NULL); + + rb->buffer = buffer; + rb->buffer_size = size; + rb->busy = false; + + ringbuffer_reset(rb); + } + + static inline void ringbuffer_reset(ringbuffer_t *rb) + { + RINGBUFFER_ASSERT(rb != NULL); + rb->head = 0; + rb->tail = 0; + rb->data_count = 0; + } + + static inline ring_size_t ringbuffer_get_free(ringbuffer_t *rb) + { + RINGBUFFER_ASSERT(rb != NULL); + return rb->buffer_size - rb->data_count; + } + + static inline ring_size_t ringbuffer_get_used(ringbuffer_t *rb) + { + RINGBUFFER_ASSERT(rb != NULL); + return rb->data_count; + } + + static inline ring_size_t ringbuffer_get_size(ringbuffer_t *rb) + { + RINGBUFFER_ASSERT(rb != NULL); + return rb->buffer_size; + } + + static inline ring_size_t ringbuffer_get_last_series_size(ringbuffer_t *rb) + { + RINGBUFFER_ASSERT(rb != NULL); + return rb->last_series_count; + } + + static inline ring_size_t ringbuffer_write(ringbuffer_t *rb, const uint8_t *data, ring_size_t size) + { + RINGBUFFER_ASSERT(rb != NULL); + + ring_size_t free_size = ringbuffer_get_free(rb); + + if (size > free_size) + { + size = free_size; // 多余的数据不写入(丢掉) + } + + // 判断超过尾部没有 + if (rb->tail + size > rb->buffer_size) + { + ring_size_t first_size = rb->buffer_size - rb->tail; + memcpy((uint8_t *)(rb->buffer) + rb->tail, data, first_size); + memcpy(rb->buffer, data + first_size, size - first_size); + } + else + { + memcpy((uint8_t *)(rb->buffer) + rb->tail, data, size); + } + + // 限制尾部位置 + if (rb->tail + size >= rb->buffer_size) + { + rb->tail = size - (rb->buffer_size - rb->tail); + } + else + { + rb->tail += size; + } + + rb->data_count += size; + + return size; + } + + static inline ring_size_t ringbuffer_read(ringbuffer_t *rb, uint8_t *data, ring_size_t size) + { + RINGBUFFER_ASSERT(rb != NULL); + + ring_size_t used_size = ringbuffer_get_used(rb); + + if (size > used_size) + { + size = used_size; // 读取的数据超过了已有数据量 + } + + // 判断超过尾部没有 + if (rb->head + size > rb->buffer_size) + { + ring_size_t first_size = rb->buffer_size - rb->head; + memcpy(data, (uint8_t *)(rb->buffer) + rb->head, first_size); + memcpy(data + first_size, rb->buffer, size - first_size); + } + else + { + memcpy(data, (uint8_t *)(rb->buffer) + rb->head, size); + } + + // 限制头部位置 + if (rb->head + size >= rb->buffer_size) + { + rb->head = size - (rb->buffer_size - rb->head); + } + else + { + rb->head += size; + } + + rb->data_count -= size; + + return size; + } + + static inline bool ringbuffer_write_byte(ringbuffer_t *rb, uint8_t data) + { + RINGBUFFER_ASSERT(rb != NULL); + + if (ringbuffer_get_free(rb) == 0) + { + return false; + } + + *((uint8_t *)(rb->buffer) + rb->tail) = data; + + // 限制尾部位置 + if (rb->tail + 1 >= rb->buffer_size) + { + rb->tail = 0; + } + else + { + rb->tail++; + } + + rb->data_count++; + + return true; + } + + static inline bool ringbuffer_read_byte(ringbuffer_t *rb, uint8_t *data) + { + RINGBUFFER_ASSERT(rb != NULL); + + if (ringbuffer_get_used(rb) == 0) + { + return false; + } + + *data = *((uint8_t *)(rb->buffer) + rb->head); + + // 限制头部位置 + if (rb->head + 1 >= rb->buffer_size) + { + rb->head = 0; + } + else + { + rb->head++; + } + + rb->data_count--; + + return true; + } + + static inline uint8_t *ringbuffer_linear_write_setup(ringbuffer_t *rb, ring_size_t *size) + { + RINGBUFFER_ASSERT(rb != NULL); + RINGBUFFER_ASSERT(size != NULL); + ring_size_t max_size = ringbuffer_get_free(rb); + + if (max_size + rb->tail > rb->buffer_size) + { + max_size = rb->buffer_size - rb->tail; + } + + *size = max_size; + rb->last_series_count = max_size; + + return (uint8_t *)(rb->buffer) + rb->tail; + } + + static inline uint8_t *ringbuffer_linear_read_setup(ringbuffer_t *rb, ring_size_t *size) + { + RINGBUFFER_ASSERT(rb != NULL); + RINGBUFFER_ASSERT(size != NULL); + ring_size_t max_size = ringbuffer_get_used(rb); + + if (max_size + rb->head > rb->buffer_size) + { + max_size = rb->buffer_size - rb->head; + } + + *size = max_size; + rb->last_series_count = max_size; + + return (uint8_t *)(rb->buffer) + rb->head; + } + + static inline ring_size_t ringbuffer_linear_write_done(ringbuffer_t *rb, ring_size_t size) + { + RINGBUFFER_ASSERT(rb != NULL); + + // 限制尾部位置 + if (rb->tail + size >= rb->buffer_size) + { + rb->tail = size - (rb->buffer_size - rb->tail); + } + else + { + rb->tail += size; + } + + rb->data_count += size; + + return size; + } + + static inline ring_size_t ringbuffer_linear_read_done(ringbuffer_t *rb, ring_size_t size) + { + RINGBUFFER_ASSERT(rb != NULL); + + // 限制头部位置 + if (rb->head + size >= rb->buffer_size) + { + rb->head = size - (rb->buffer_size - rb->head); + } + else + { + rb->head += size; + } + + rb->data_count -= size; + + return size; + } + + static inline void ringbuffer_mark_busy(ringbuffer_t *rb) + { + RINGBUFFER_ASSERT(rb != NULL); + rb->busy = true; + } + + static inline void ringbuffer_mark_idle(ringbuffer_t *rb) + { + RINGBUFFER_ASSERT(rb != NULL); + rb->busy = false; + } + + static inline bool ringbuffer_is_busy(ringbuffer_t *rb) + { + RINGBUFFER_ASSERT(rb != NULL); + return rb->busy; + } + +#ifdef __cplusplus +} +#endif //\ __cplusplus + +#endif //\ __RINGBUFFER_H_ diff --git a/demo/easy_transplant/shell_all.c b/demo/easy_transplant/shell_all.c new file mode 100644 index 0000000..a87ca83 --- /dev/null +++ b/demo/easy_transplant/shell_all.c @@ -0,0 +1,395 @@ +#include "shell_all.h" + +#include +#include + +// 将所有的源码文件加入到这一个文件中,方便Keil工程的添加 + +#if 1 + +// shell核心实现包含 +#include "../../src/shell.c" +#include "../../src/shell_ext.c" +#include "../../src/shell_cmd_list.c" +#include "../../src/shell_companion.c" + +// shell拓展组件 + +// shell增强组件 +#include "../../extensions/shell_enhance/shell_cmd_group.c" // 命令组 +#include "../../extensions/shell_enhance/shell_passthrough.c" // 透传 +#include "../../extensions/shell_enhance/shell_secure_user.c" // 安全用户 + +// shell log组件 +#include "../../extensions/log/log.c" // 日志 + +//// shell fs组件 +// #include "../../extensions/fs_support/shell_fs.c" // 文件命令 + +//// shell telnet组件 +// #include "../../extensions/telnet/telnetd.c" // telnet,需要命令组拓展(注意头文件路径包含) + +#endif + +//// shell game组件需要单独添加.c到工程中,不然会有一些同名函数的问题 +// #include "../../extensions/game/game.c" // 游戏,需要命令组拓展(注意头文件路径包含) +// #include "../../extensions/game/2048/2048.c" // 2048 +// #include "../../extensions/game/pushbox/pushbox.c" // 贪吃蛇 + +//+********************************* 环形队列中转 **********************************/ +#if SHELL_SHOW_INFO == 1 +#if SHELL_TX_BUFFER_SIZE < 400 +#warning "建议将输出环形队列的大小设置为大于400,否则打印初始化信息的时候可能会出现部分打印数据丢失" +#warning "shell tx buffer size is too small, please increase the buffer size" +#endif +#endif + +// 原子操作进入和退出 +#ifndef SHELL_ATOMIC_ENTER +#define SHELL_ATOMIC_ENTER() (void)0 +#endif // !SHELL_ATOMIC_ENTER + +#ifndef SHELL_ATOMIC_EXIT +#define SHELL_ATOMIC_EXIT() (void)0 +#endif // !SHELL_ATOMIC_EXIT + +// 写入是否可以阻塞,默认不可以阻塞,一般判断是否在中断中就可以判断是否可以阻塞 +#ifndef SHELL_WRITE_CAN_BLOCK +#define SHELL_WRITE_CAN_BLOCK() (false) +#endif // !SHELL_WRITE_CAN_BLOCK + +// 等待一段时间 +#ifndef WAIT_A_MOMENT +#define WAIT_A_MOMENT() (void)0 +#endif // !WAIT_A_MOMENT + +#undef USER_ASSERT +#define USER_ASSERT(exp) (void)0 // 开启此宏关闭此文件用户自定义断言 + +// clang-format off +#ifndef USER_ASSERT + #include + #define USER_ASSERT(exp) \ + if (!(exp)) \ + { \ + printf("\"" #exp "\" assert failed at file: %s, line: %d", __FILE__, __LINE__); \ + for (;;) \ + ; \ + } +#endif // ! USER_ASSERT +// clang-format on + +// 定义shell和log对象 +static Shell user_shell; +static Log user_log; + +// 发送和接收的中转环形缓冲队列 +static ringbuffer_t rb_rx; +static ringbuffer_t rb_tx; + +//+******************************** 对外调用接口 ***************************************/ +void port_tx_trigger(void) +{ + if (ringbuffer_is_busy(&rb_tx) || ringbuffer_get_used(&rb_tx) <= 0) + { + return; // 没有数据或者外设还在响应上一次的发送 + } + + // 这两个栈变量可能会在极端情况下破坏原子操作性,改成static然后提到函数开头效果可能会更好 + uint16_t size; + uint8_t *p; + + SHELL_ATOMIC_ENTER(); + // 获取ringbuffer当前的读指针和最大线性可读数据大小 + ringbuffer_mark_busy(&rb_tx); // 标记为忙,调用port_tx_end函数后再标记为闲 + p = ringbuffer_linear_read_setup(&rb_tx, &size); + SHELL_ATOMIC_EXIT(); + + // 发送数据,调用平台相关的发送函数 + PLATFORM_TX_WRAP(p, size); +} + +void port_rx_trigger(void) +{ + if (ringbuffer_is_busy(&rb_rx) || ringbuffer_get_free(&rb_rx) <= 0) // 性能优化,对于接收,外设忙的情况更多先判断是否在忙 + { + return; // 没有空间接收或者正在接收数据 + } + + // 这两个栈变量可能会在极端情况下破坏原子操作性,改成static然后提到函数开头效果可能会更好 + uint16_t size; + uint8_t *p; + + SHELL_ATOMIC_ENTER(); + ringbuffer_mark_busy(&rb_rx); // 标记为忙,调用port_rx_end函数后再标记为闲 + // 获取ringbuffer当前的写指针和最大线性可写数据大小 + p = ringbuffer_linear_write_setup(&rb_rx, &size); + SHELL_ATOMIC_EXIT(); + + // 接收数据,调用平台相关的接收函数 + PLATFORM_RX_WRAP(p, size); +} + +// 通常用于中断中调用 +void port_tx_end(short truely_tx_len) +{ + SHELL_ATOMIC_ENTER(); + if (truely_tx_len < 0) + { + truely_tx_len = ringbuffer_get_last_series_size(&rb_tx); + } + ringbuffer_linear_read_done(&rb_tx, truely_tx_len); + ringbuffer_mark_idle(&rb_tx); + SHELL_ATOMIC_EXIT(); + +#if SHELL_TX_CONTINUOUSLY + port_tx_trigger(); // 继续发送 +#endif +} + +void port_rx_end(short truely_rx_len) +{ + SHELL_ATOMIC_ENTER(); + if (truely_rx_len < 0) + { + truely_rx_len = ringbuffer_get_last_series_size(&rb_rx); + } + ringbuffer_linear_write_done(&rb_rx, truely_rx_len); + ringbuffer_mark_idle(&rb_rx); + SHELL_ATOMIC_EXIT(); + +#if SHELL_RX_CONTINUOUSLY + //! 继续接收,如果这里由于队列满了导致没有触发接收,那么直到再次触发接收之前,都不会再接收数据了 + port_rx_trigger(); +#endif +} + +int port_tx_available(void) +{ + return ringbuffer_get_used(&rb_tx); +} + +int port_rx_available(void) +{ + return ringbuffer_get_used(&rb_rx); +} + +//+********************************* shell读写对接 **********************************/ + +static short write_to_ringbuffer(char *data, unsigned short len) +{ + short total_written = 0; + short ret = 0; + + // 不可被阻塞,缓冲区还不能完全写入就直接丢掉,避免数据写入不完全 + if (!SHELL_WRITE_CAN_BLOCK() && ringbuffer_get_free(&rb_tx) < len) + { + WRITE_OVER_FLOW_HOOK((char *)data, len); + return 0; // 直接丢弃数据,不再写入 + } + + do + { + SHELL_ATOMIC_ENTER(); + + ret = ringbuffer_write(&rb_tx, (uint8_t *)data, len); + + SHELL_ATOMIC_EXIT(); + + if (ret >= len) // 只可能小于等于 + { + return total_written; // 全部数据写入成功 + } + else + { + total_written += ret; + data += ret; + len -= ret; + port_tx_trigger(); // 主动尝试触发发送 + + WAIT_A_MOMENT(); // 等待一段时间,避免频繁触发发送 + } + } while (len <= 0); + + return total_written; +} + +static short read_from_ringbuffer(char *data, unsigned short len) +{ + short ret = 0; + SHELL_ATOMIC_ENTER(); + + ret = ringbuffer_read(&rb_rx, (uint8_t *)data, len); + + SHELL_ATOMIC_EXIT(); + return ret; +} + +//+******************************** log组件对接 ***************************************/ + +static void UserLogWrite(char *buffer, short len) +{ +#if SHELL_SUPPORT_END_LINE == 1 + USER_ASSERT(user_log.shell != NULL); + shellWriteEndLine(user_log.shell, buffer, len); +#else + USER_ASSERT(user_log.shell != NULL); + USER_ASSERT(user_log.shell->write != NULL); + user_log.shell->write(buffer, len); +#endif +} + +//+******************************** shell和log组件初始化,shell任务 ***************************************/ +// 锁函数需要在对应的适配文件中实现 +// shell 锁实现函数 +extern int shell_lock(struct shell_def *shell); +extern int shell_unlock(struct shell_def *shell); + +// log锁实现函数 +extern int log_lock(struct log_def *log); +extern int log_unlock(struct log_def *log); + +extern void user_init_before_shell(void); + +void letter_shell_init(void) +{ + user_init_before_shell(); + + // 初始化输入输出环形队列 + static char rb_rx_buffer[SHELL_RX_BUFFER_SIZE]; + static char rb_tx_buffer[SHELL_TX_BUFFER_SIZE]; + ringbuffer_init(&rb_rx, (uint8_t *)rb_rx_buffer, sizeof(rb_rx_buffer)); + ringbuffer_init(&rb_tx, (uint8_t *)rb_tx_buffer, sizeof(rb_tx_buffer)); + + user_shell.write = write_to_ringbuffer; + user_shell.read = read_from_ringbuffer; +#if SHELL_USING_LOCK == 1 + user_shell.lock = shell_lock; + user_shell.unlock = shell_unlock; +#endif + + // 用于存放命令之类的buffer + static char shell_buffer[512]; + shellInit(&user_shell, shell_buffer, sizeof(shell_buffer)); // 初始化shell + + user_log.active = 1; // 激活log,默认输出所有等级的log,绑定log输出 + user_log.level = LOG_ALL; + user_log.write = UserLogWrite, +#if LOG_USING_LOCK == 1 + user_log.lock = log_lock; + user_log.unlock = log_unlock; +#endif + + logRegister(&user_log, &user_shell); // 注册log +} + +void letter_shell_task(void) +{ +#if SHELL_TASK_WHILE == 1 + for (;;) + { +#endif + //> 请自行定期调用两个trigger函数 + // port_tx_trigger(); + // port_rx_trigger(); + + // 读取数据进行处理,这里可以多读几个字节,提高处理效率 + static char data; + // while (user_shell.read(&data, 1)) + if (user_shell.read(&data, 1)) + { + shellHandler(&user_shell, data); + } +#if SHELL_TASK_WHILE == 1 + } +#endif +} + +int shell_write(char *data, int len) +{ + return (int)(write_to_ringbuffer(data, len)); +} + +#if USE_SHELL_PRINTF +#include "./xprintf/src/xprintf.c" + +static volatile int _byte_count = 0; + +static inline void output_byte_buffer(int ch) +{ + if (ringbuffer_write_byte(&rb_tx, (uint8_t)ch)) + { + _byte_count++; + } +} + +int shell_printf(const char *format, ...) +{ + int len = 0; + + Shell *shell = shellGetCurrent(); + + SHELL_LOCK(shell); + + _byte_count = 0; + + va_list args; + va_start(args, format); + xvfprintf(output_byte_buffer, format, args); + va_end(args); + + len = _byte_count; + + SHELL_UNLOCK(shell); + + return len; +} +#endif + +//+********************************* 导出一些可能用到的命令或快捷键 **********************************/ +static void logChangeLevel() +{ + Log *log = &user_log; + SHELL_ASSERT(log, return); + log->level = (LogLevel)(log->level >= LOG_ALL ? LOG_NONE : (log->level + 1)); + + char logLevel[40] = {0}; + switch (log->level) + { + case LOG_NONE: + sprintf(logLevel, "set log level : LOG_NONE\t(%d)", log->level); + break; + + case LOG_ERROR: + sprintf(logLevel, "set log level : LOG_ERROR\t(%d)", log->level); + break; + + case LOG_WRANING: + sprintf(logLevel, "set log level : LOG_WRANING\t(%d)", log->level); + break; + + case LOG_INFO: + sprintf(logLevel, "set log level : LOG_INFO\t(%d)", log->level); + break; + + case LOG_DEBUG: + sprintf(logLevel, "set log level : LOG_DEBUG\t(%d)", log->level); + break; + + case LOG_VERBOSE: + sprintf(logLevel, "set log level : LOG_VERBOSE\t(%d)", log->level); + break; + + case LOG_ALL: + sprintf(logLevel, "set log level : LOG_ALL\t\t(%d)", log->level); + break; + + default: + break; + } + + logPrintln("%s", logLevel); +} +//> 0x04000000对应的应该是鼠标中键,但是在Xshell中,按下鼠标中间会以字符串的形式发送04000000,导致无法触发,现在修改为 ~ 的按键值 +// SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x04000000, logChangeLevel, switch log level); +SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x60000000, logChangeLevel, '~' switch log level); diff --git a/demo/easy_transplant/shell_all.h b/demo/easy_transplant/shell_all.h new file mode 100644 index 0000000..e93b7b9 --- /dev/null +++ b/demo/easy_transplant/shell_all.h @@ -0,0 +1,108 @@ +/** + * @file shell_all.h + * @author 独霸一方 (2696652257@qq.com) + * @brief //> 这个文件将包含letter shell的所有头文件实现(相对路径),方便在Keil工程中使用 + * @version 1.0 + * @date 2024-08-05 + * + * @copyright Copyright (c) 2024 + * + */ + +// 此文件使用相对路径,保证在任何地方都可以使用 + +// > 单次包含宏定义 +#ifndef __SHELL_ALL_H_ +#define __SHELL_ALL_H_ + +//> 平台适配的头文件必须放在最前面,确保宏定义的预处理定义正确 +//> 如果有自己的实现请替换为对应自己的头文件 +#include "./stm32_HAL_adapt/stm32_HAL_adapt.h" + +#include "shell_cfg_user.h" // 使用本文件目录下的自定义配置文件 + +//> 环形队列用来等待接收和发送的数据,这样就可以在中断中调用shell的读写函数 +#define SHELL_RX_BUFFER_SIZE 512 +#define SHELL_TX_BUFFER_SIZE 512 + +// 连续发送与接收宏定义,当使用阻塞式接收发送时必须关闭指定的宏定义 +#define SHELL_TX_CONTINUOUSLY 1 // 使能连续发送(end中继续检测是否还有数据需要发送) +#define SHELL_RX_CONTINUOUSLY 1 // 使能连续接收(end中继续检测是否还有空间可以接收) + +// shell 写入溢出的时候可以调用钩子函数进行一些处理 +#ifndef WRITE_OVER_FLOW_HOOK +#define WRITE_OVER_FLOW_HOOK(data, len) (void)0 +#endif + +#if !defined(PLATFORM_TX_WRAP) || !defined(PLATFORM_RX_WRAP) +#error "请实现自己的接收与发送函数对接" +#error "please implement your own receive and send function docking" +#endif + +// > C/C++兼容性宏定义 +#ifdef __cplusplus +extern "C" +{ +#endif + + //+******************************** C scope ***************************************/ + +#include "./ringbuffer/ringbuffer.h" // 环形队列 + +#include "../../src/shell.h" + +// shell核心头文件 +#include "../../src/shell_cfg.h" +#include "../../src/shell.h" +#include "../../src/shell_ext.h" + +// shell增强组件 +#include "../../extensions/shell_enhance/shell_cmd_group.h" // 命令组 +#include "../../extensions/shell_enhance/shell_passthrough.h" // 透传 +#include "../../extensions/shell_enhance/shell_secure_user.h" // 安全用户 + + // #undef LOG_ENABLE + // #define LOG_ENABLE 0 //>在对应的C文件中使用此定义即可关闭当前文件的日志输出,需要加在文件最前面,直接打开这里的注释是关闭所有文件的日志输出 + +// shell log组件 +#include "../../extensions/log/log.h" // 日志 + + //// shell fs组件 + // #include "../../extensions/fs_support/shell_fs.h" // 文件命令 + + //// shell telnet组件 + // #include "../../extensions/telnet/telnetd.h" // telnet + + void port_tx_trigger(void); // 触发发送,定期调用 + void port_tx_end(short truely_tx_len); // 实际发送完后一定要调用,发送中断中调用(如果有的话),end函数如果传入负数表示使用上次调用时候记录的值 + int port_tx_available(void); // 获取发送缓冲区已有的数据量 + + void port_rx_trigger(void); // 触发接收,定期调用 + void port_rx_end(short truely_rx_len); // 实际接收完后一定要调用,接收中断中调用(如果有的话),end函数如果传入负数表示使用上次调用时候记录的值 + int port_rx_available(void); // 获取接收缓冲区已有的数据量,可以根据此函数判断是否需要调用shell任务,对于RTOS可以在无数据时释放CPU + + // 初始化shell和log + void letter_shell_init(void); + + // shell任务 + void letter_shell_task(void); + + // shell写入函数,用于向shell写入数据,返回实际写入的字节数 + int shell_write(char *data, int len); + +#define USE_SHELL_PRINTF 1 +#if USE_SHELL_PRINTF + // 代替标准库的printf,shell_printf使用xprintf的格式化输出,以源码形式添加编译 + int shell_printf(const char *format, ...); +#define SHELL_PRINTF +#endif + +#ifdef __cplusplus +} +//+******************************** c++ scope ***************************************/ +// shell C++支持组件 +#include "../../extensions/cpp_support/shell_cpp.h" // C++命令 + +#endif //\ __cplusplus + +#endif //\ __SHELL_ALL_H_ diff --git a/demo/easy_transplant/shell_cfg_user.h b/demo/easy_transplant/shell_cfg_user.h new file mode 100644 index 0000000..bc9344a --- /dev/null +++ b/demo/easy_transplant/shell_cfg_user.h @@ -0,0 +1,117 @@ +/** + * @file shell_cfg_user.h + * @author Letter (nevermindzzt@gmail.com) + * @brief shell config + * @version 3.0.0 + * @date 2019-12-31 + * + * @copyright (c) 2019 Letter + * + */ + +#ifndef __SHELL_CFG_USER_H__ +#define __SHELL_CFG_USER_H__ + +#include + +#ifdef SHELL_CFG_USER +#include SHELL_CFG_USER +#endif + +// clang-format off + +/** + * @brief shell默认用户 + */ +#define SHELL_DEFAULT_USER "letter" + +// /** +// * @brief 是否使用shell伴生对象,开启后需要支持malloc和free接口 +// * 一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象 +// */ +// #define SHELL_USING_COMPANION 0 + +// /** +// * @brief shell内存分配 +// * shell本身不需要此接口,若使用shell伴生对象,需要进行定义 +// */ +// extern void *pvPortMalloc(size_t xSize); +// #define SHELL_MALLOC(size) pvPortMalloc(size) + +// /** +// * @brief shell内存释放 +// * shell本身不需要此接口,若使用shell伴生对象,需要进行定义 +// */ +// extern void vPortFree(void *pv); +// #define SHELL_FREE(obj) vPortFree(obj) + +/** + * @brief 使用锁 + * @note 使用shell锁时,需要对加锁和解锁进行实现 + */ +#define SHELL_USING_LOCK 1 + +#ifndef SHELL_TASK_WHILE +/** + * @brief 是否使用默认shell任务while循环 + * 使能此宏,则`shell_task()`函数会一直循环读取输入,一般使用操作系统建立shell + * 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shell_task()` + */ +#define SHELL_TASK_WHILE 0 +#endif /** SHELL_TASK_WHILE */ + +/** + * @brief 是否显示shell信息 + */ +#define SHELL_SHOW_INFO 1 + +/** + * @brief 支持shell尾行模式 + */ +#define SHELL_SUPPORT_END_LINE 1 + + +/** + * @brief 使用LF作为命令行回车触发 + * 可以和SHELL_ENTER_CR同时开启 + */ +#define SHELL_ENTER_LF 0 + +/** + * @brief 使用CR作为命令行回车触发 + * 可以和SHELL_ENTER_LF同时开启 + */ +#define SHELL_ENTER_CR 0 + +/** + * @brief 使用CRLF作为命令行回车触发 + * 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启 + */ +#define SHELL_ENTER_CRLF 1 + +#ifndef SHELL_PRINT_BUFFER +/** + * @brief shell格式化输出的缓冲大小 + * 为0时不使用shell格式化输出 + */ +#define SHELL_PRINT_BUFFER 256 +#endif /** SHELL_PRINT_BUFFER */ + +/** + * @brief shell格式化输入的缓冲大小 + * 为0时不使用shell格式化输入 + * @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用 + */ +#define SHELL_SCAN_BUFFER 256 + +/** + * @brief 使用函数签名 + * 使能后,可以在声明命令时,指定函数的签名,shell 会根据函数签名进行参数转换, + * 而不是自动判断参数的类型,如果参数和函数签名不匹配,会停止执行命令 + */ +#define SHELL_USING_FUNC_SIGNATURE 0 + +//+******************************** log组件配置 ***************************************/ +//>log组件的配置在log.h中配置 + +#endif diff --git a/demo/easy_transplant/stm32_HAL_adapt/stm32_HAL_adapt.c b/demo/easy_transplant/stm32_HAL_adapt/stm32_HAL_adapt.c new file mode 100644 index 0000000..25717c3 --- /dev/null +++ b/demo/easy_transplant/stm32_HAL_adapt/stm32_HAL_adapt.c @@ -0,0 +1,213 @@ +/** + * @file stm32_HAL_adapt.c + * @author 独霸一方 (2696652257@qq.com) + * @brief //> stm32 HAL库适配letter shell + * @version 1.0 + * @date 2024-08-07 + * + * @copyright Copyright (c) 2024 + * + */ + +// clang-format off +//> 必须对接实现的API,哪怕是空函数也要定义一份(可以通过配置宏来预处理关掉,但是还是建议实现,哪怕是空函数) + +/* + +// shell锁实现函数 +int shell_lock(struct shell_def *shell) {}; +int shell_unlock(struct shell_def *shell) {}; + +// log锁实现函数 +int log_lock(struct log_def *log) {}; +int log_unlock(struct log_def *log) {}; + +// shell初始化之前的初始化函数主要是为了初始化硬件接口等 +void user_init_before_shell(void) {}; + +*/ +// clang-format on + +#include "stm32_HAL_adapt.h" + +#include "../shell_all.h" + +#if (RTOS_MODE == RTOS_FREERTOS) +#include "FreeRTOS.h" +#include "semphr.h" +static SemaphoreHandle_t _shell_mutex; // 互斥锁,防止输出混乱 +static SemaphoreHandle_t _log_mutex; // 互斥锁,防止输出混乱 +#endif + +// 写入是否可以阻塞,默认不可以阻塞,一般判断是否在中断中来决定是否可以阻塞 +#ifndef SHELL_WRITE_CAN_BLOCK +#define SHELL_WRITE_CAN_BLOCK() (false) +#endif // !SHELL_WRITE_CAN_BLOCK + +// 如果有操作系统,可以使用操作系统提供的递归互斥锁 +int shell_lock(struct shell_def *shell) +{ + if (SHELL_WRITE_CAN_BLOCK()) + { // 操作系统的加锁 +#if (RTOS_MODE == RTOS_FREERTOS) + xSemaphoreTakeRecursive(_shell_mutex, portMAX_DELAY); +#endif + } + else + { + // 处于不能阻塞的地方,一般认为是中断,直接不管或者必须使用支持嵌套的原子加锁,下面的几个API类似 + } + + return 0; +} + +int shell_unlock(struct shell_def *shell) +{ + if (SHELL_WRITE_CAN_BLOCK()) + { // 操作系统的解锁 +#if (RTOS_MODE == RTOS_FREERTOS) + xSemaphoreGiveRecursive(_shell_mutex); +#endif + } + else + { + } + + return 0; +} + +int log_lock(struct log_def *log) +{ + if (SHELL_WRITE_CAN_BLOCK()) + { // 操作系统的加锁 +#if (RTOS_MODE == RTOS_FREERTOS) + xSemaphoreTakeRecursive(_log_mutex, portMAX_DELAY); +#endif + } + else + { + } + + return 0; +} + +int log_unlock(struct log_def *log) +{ + if (SHELL_WRITE_CAN_BLOCK()) + { // 操作系统的解锁 +#if (RTOS_MODE == RTOS_FREERTOS) + xSemaphoreGiveRecursive(_log_mutex); +#endif + } + else + { + } + + return 0; +} + +// shell使用的硬件初始化,使用cubemx开启串口,然后使能对应的串口中断,DMA发送与接收中断 +void user_init_before_shell(void) +{ + // 由于初始化已经由CUBEMX处理好了,这里什么都不需要做 + +#if (RTOS_MODE == RTOS_FREERTOS) + _shell_mutex = xSemaphoreCreateMutex(); + _log_mutex = xSemaphoreCreateMutex(); +#endif +} + +//+******************************** 平台的中断回调 ***************************************/ +//! 如果有自己其他串口的中断回调,请自行按照这个格式添加 + +#if 1 + +#if (PLATFORM_MODE == PLATFORM_MODE_DMA) + +void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) // 串口接收中断 +{ + UNUSED(huart); + UNUSED(Size); + + if (huart->Instance == SHELL_UART_ADDR->Instance) + { + port_rx_end(Size); + } +} + +void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) // 串口发送中断 +{ + UNUSED(huart); + + if (huart->Instance == SHELL_UART_ADDR->Instance) + { + port_tx_end(-1); // 传输默认是传输完了上一次的,传入负数让其自行处理 + } +} + +#elif (PLATFORM_MODE == PLATFORM_MODE_IT) + +void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) +{ + if (huart->Instance == SHELL_UART_ADDR->Instance) + { + port_rx_end(1); // 中断接收的时候是单字节接收,因此实际接收到1个字节就会进入此中断 + } +} + +void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) // 串口发送中断 +{ + UNUSED(huart); + + if (huart->Instance == SHELL_UART_ADDR->Instance) + { + port_tx_end(-1); // 传输默认是传输完了上一次的,传入负数让其自行处理 + } +} + +#endif + +#endif + +//+******************************** 导出一些可能常用的命令 ***************************************/ +#if 1 + +int Reboot() +{ + HAL_NVIC_SystemReset(); // 单片机重启 + return 0; +} +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), Reboot, Reboot, reboot the mcu); + +#include +int date() +{ + Shell *shell = shellGetCurrent(); + + if (!shell) + return 0; + + int64_t ticks = SHELL_GET_TICK(); + +#if SHELL_USING_LOCK == 1 + shell->lock(shell); +#endif + + char str[150]; + snprintf(str, sizeof(str), "current time: %lld\r\n", ticks); + shellWriteString(shell, str); + + // 换算为天`时`分`秒`毫秒 + // snprintf(str, sizeof(str), "current time: %lld 天 %02lld 时 %02lld 分 %02lld 秒 %03lld 毫秒\r\n", ticks / (1000 * 60 * 60 * 24), (ticks / (1000 * 60 * 60)) % 24, (ticks / (1000 * 60)) % 60, (ticks / 1000) % 60, ticks % 1000); + snprintf(str, sizeof(str), "current time: %lld days %02lld hours %02lld minutes %02lld seconds %03lld milliseconds\r\n", ticks / (1000 * 60 * 60 * 24), (ticks / (1000 * 60 * 60)) % 24, (ticks / (1000 * 60)) % 60, (ticks / 1000) % 60, ticks % 1000); + shellWriteString(shell, str); + +#if SHELL_USING_LOCK == 1 + shell->unlock(shell); +#endif + + return 0; +} +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), date, date, current time); + +#endif diff --git a/demo/easy_transplant/stm32_HAL_adapt/stm32_HAL_adapt.h b/demo/easy_transplant/stm32_HAL_adapt/stm32_HAL_adapt.h new file mode 100644 index 0000000..ca5b07d --- /dev/null +++ b/demo/easy_transplant/stm32_HAL_adapt/stm32_HAL_adapt.h @@ -0,0 +1,107 @@ +/** + * @file stm32_HAL_adapt.h + * @author fool_dog (2696652257@qq.com) + * @brief // STM32HAL库 裸机与FreeRTOS的对接方式 + * @version 1.0 + * @date 2024-08-08 + * + * @copyright Copyright (c) 2024 + * + */ + +// > 单次包含宏定义 +#ifndef _STM32_HAL_ADAPT_H_ +#define _STM32_HAL_ADAPT_H_ + +// clang-format off +/** + * 此文件主要是必须对接的宏定义,包括: + * #define PLATFORM_TX_WRAP(buffer, len) ... //> 发送包装宏 + * #define PLATFORM_RX_WRAP(buffer, len) ... //> 接收包装宏 + * #define SHELL_GET_TICK() ... //> 获取系统时间(ms) + * 可选宏: + * #define SHELL_WRITE_CAN_BLOCK() ... //> 判定写入是否可以阻塞 + * #define WAIT_A_MOMENT() ... //> 等待一段时间(可选,默认等待1ms) + * #define SHELL_ATOMIC_ENTER() ... //> 进入原子操作 + * #define SHELL_ATOMIC_EXIT() ... //> 退出原子操作 + * #define SHELL_UART_ADDR ... //> 取决于接收和发送宏的实现(可以不用串口) + */ + +// clang-format on + +#include "cmsis_compiler.h" +#include "usart.h" + +// 接收发送方式,不使用阻塞式发送接收,有RTOS可以考虑阻塞发送(可以但没必要),发送和接收的方式是可以组合使用的需自行更改 +#define PLATFORM_MODE_DMA 0 // DMA发送与接收 +#define PLATFORM_MODE_IT 1 // 单字节中断发送与接收 +#define PLATFORM_MODE_POLL 2 // 轮询发送与接收 + +// 使用DMA发送与接收 +#define PLATFORM_MODE PLATFORM_MODE_DMA + +#define RTOS_NONE 0 // 无操作系统 +#define RTOS_FREERTOS 1 // FreeRTOS +// ... 其他操作系统 + +// 定义使用的操作系统 +#define RTOS_MODE RTOS_NONE + +//+********************************* 必须对接的宏 **********************************/ +// 写入是否可以阻塞的判断函数,通常不在中断中就可以阻塞 +#define SHELL_WRITE_CAN_BLOCK() (__get_IPSR() == 0) + +#if (RTOS_MODE == RTOS_NONE) +#define WAIT_A_MOMENT() HAL_Delay(1) // 等待一段时间,外设跟不上内核填充的速度 +#elif (RTOS_MODE == RTOS_FREERTOS) +#include "FreeRTOS.h" +#include "task.h" +#define WAIT_A_MOMENT() vTaskDelay(1) +#else +#warning "请实现自己的等待函数" +#warning "please implement your own wait function" +#endif + +// 原子操作保证数据的完整性,支持嵌套更好 +#define SHELL_ATOMIC_ENTER() __disable_irq() +#define SHELL_ATOMIC_EXIT() __enable_irq() + +//> 默认使用串口1 +#define SHELL_UART_ADDR (&huart1) +// clang-format off + +#if (PLATFORM_MODE == PLATFORM_MODE_DMA) +#include "dma.h" + extern HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size); + extern HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); + #define PLATFORM_TX_WRAP(buffer, len) HAL_UART_Transmit_DMA(SHELL_UART_ADDR, (uint8_t *)buffer, len) + #define PLATFORM_RX_WRAP(buffer, len) HAL_UARTEx_ReceiveToIdle_DMA(SHELL_UART_ADDR, (uint8_t *)buffer, len) +#elif (PLATFORM_MODE == PLATFORM_MODE_IT) + extern HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size); + extern HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); + #define PLATFORM_TX_WRAP(buffer, len) HAL_UART_Transmit_IT(SHELL_UART_ADDR, (uint8_t *)buffer, len) + // 中断接收改成单个字节接收,接收回调调用port_rx_end(1);表示接收到一个字节 + #define PLATFORM_RX_WRAP(buffer, len) HAL_UART_Receive_IT(SHELL_UART_ADDR, (uint8_t *)buffer, 1) +#elif (PLATFORM_MODE == PLATFORM_MODE_POLL) +#warning "强烈不建议使用阻塞式API发送与接收,建议使用DMA或者中断方式" +#warning "It is not recommended to use blocking API for sending and receiving. It is recommended to use DMA or interrupt mode" + extern HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout); + extern HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); + #define PLATFORM_TX_WRAP(buffer, len) HAL_UART_Transmit(SHELL_UART_ADDR, (uint8_t *)buffer, len, 1000) + #define PLATFORM_RX_WRAP(buffer, len) HAL_UART_Receive(SHELL_UART_ADDR, (uint8_t *)buffer, len, 1000) +#else +#error "请实现自己的接收与发送函数对接" +#error "please implement your own receive and send function docking" +#endif + +// clang-format on + +/** + * @brief 获取系统时间(ms) + * 定义此宏为获取系统Tick,如`HAL_GetTick()` + * @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定 + */ +extern uint32_t HAL_GetTick(void); +#define SHELL_GET_TICK() HAL_GetTick() + +#endif //\ _STM32_HAL_ADAPT_H_ diff --git a/demo/easy_transplant/xprintf/doc/css_e.css b/demo/easy_transplant/xprintf/doc/css_e.css new file mode 100644 index 0000000..64664b1 --- /dev/null +++ b/demo/easy_transplant/xprintf/doc/css_e.css @@ -0,0 +1,59 @@ +* {margin: 0; padding: 0; border-width: 0;} +body {margin: 8px; background-color: #e0ffff; font-color: black; font-family: serif; line-height: 133%; max-width: 1024px;} +a:link {color: blue;} +a:visited {color: darkmagenta;} +a:hover {background-color: #a0ffff;} +a:active {color: darkmagenta; overflow: hidden; outline:none; position: relative; top: 1px; left: 1px;} +abbr {border-width: 1px;} + +p {margin: 0 0 0.3em 1em;} +em {font-style: normal; font-weight: bold; margin: 0 0.1em;} +strong {} +pre {border: 1px dashed gray; margin: 0.5em 1em; padding: 0.5em; line-height: 1.2em; font-size: 85%; font-family: "Consolas", "Courier New", monospace; background-color: white;} +pre span.c {color: green;}, +pre span.k {color: blue;} +tt {margin: 0 0.2em; font-size: 85%; font-family: "Consolas", "Courier New", monospace; } +ol {margin: 0 2.5em;} +ul {margin: 0 2em;} +dl {margin: 0 1em;} +dt {font-size: 85%; font-family: "Consolas", "Courier New", monospace;} +dl.par dt {margin: 0.5em 0 0 0 ; font-style: italic; } +dl.ret dt {margin: 0.5em 0 0 0 ; } +dd {margin: 0 2em;} +hr {border-width: 1px; margin: 1em;} +div.abst {font-family: sans-serif;} +div.para {clear: both; font-family: serif;} +div.ret a {font-size: 85%; font-family: "Consolas", "Courier New", monospace; } +.equ {text-indent: 0; margin: 1em 2em 1em;} +.indent {margin-left: 2em;} +.rset {float: right; margin: 0 0 0.5em 0.5em;} +.lset {float: left; margin: 0 0.5em 0.5em 0.5em;} +ul.flat li {list-style-type: none; margin: 0;} +a.imglnk img {border: 1px solid;} +.iequ {white-space: nowrap; font-weight: bold;} +.clr {clear: both;} +.arg {font-style: italic;} +.it {font-style: italic;} +.mfd {font-size: 0.7em; padding: 0 1px; border: 1px solid; white-space : nowrap} + +h1 {line-height: 1em; font-size: 2em; font-family: sans-serif; padding: 0.3em 0 0.3em;} +p.hdd {float: right; text-align: right; margin-top: 0.5em;} +hr.hds {clear: both; margin-bottom: 1em;} + +h2 {font-size: 2em; font-family: sans-serif; background-color: #d8d8FF; padding: 0.5em 0.5em; margin: 0 0 0.5em;} +h3 {font-size: 1.5em; font-family: sans-serif; margin: 1.5em 0 0.5em;} +h4 {font-size: 1.2em; font-family: sans-serif; margin: 1em 0 0.2em;} +h5 {font-size: 1em; font-family: sans-serif; margin: 0.5em 0 0em;} +small {font-size: 80%;} +.indent {margin-left: 2em;} + +/* Tables */ +table {margin: 0.5em 1em; border-collapse: collapse; border: 2px solid black; } +th {background-color: white; border-style: solid; border-width: 1px 1px 2px; border-color: black; padding: 0 3px; vertical-align: top; white-space: nowrap;} +td {background-color: white; border: 1px solid black; padding: 0 3px; vertical-align: top; line-height: 1.3em;} +table.lst td:first-child {font-size: 85%; font-family: "Consolas", "Courier New", monospace;} +table.lst2 td {font-size: 85%; font-family: "Consolas", "Courier New", monospace;} +table caption {font-family: sans-serif; font-weight: bold;} +tr.lst3 td { border-width: 2px 1px 1px; } + +p.foot {clear: both; text-indent: 0; margin: 1em 0.5em 1em;} diff --git a/demo/easy_transplant/xprintf/doc/css_j.css b/demo/easy_transplant/xprintf/doc/css_j.css new file mode 100644 index 0000000..ec3f9db --- /dev/null +++ b/demo/easy_transplant/xprintf/doc/css_j.css @@ -0,0 +1,62 @@ +@charset "Shift_JIS"; +/* Common style sheet for Tech Notes */ + +* {margin: 0; padding: 0; border-width: 0;} +body {margin: 8px; background-color: #e0ffff; font-color: black; font-family:"lr o", serif; line-height: 150%; letter-spacing: 1px; max-width: 1024px;} +a:link {color: blue;} +a:visited {color: darkmagenta;} +a:hover {background-color: #a0ffff;} +a:active {color: darkmagenta; overflow: hidden; outline:none; position: relative; top: 1px; left: 1px;} +abbr {border-width: 1px;} + +p {text-indent: 1em; margin: 0 0 0.3em 0.5em;} +em {font-style: normal; font-weight: bold; margin: 0 0.1em;} +strong {} +pre {border: 1px dashed gray; margin: 0.5em 1em; padding: 0.5em; line-height: 1.2em; letter-spacing: 0; font-size: 85%; font-family: "Consolas", "Courier New", "lr SVbN", monospace; background-color: white;} +pre span.c {color: green;} +pre span.k {color: blue;} +tt {margin: 0 0.2em; letter-spacing: 0; font-size: 85%; font-family: "Consolas", "Courier New", "lr SVbN", monospace;} +ol {margin: 0 2.5em;} +ul {margin: 0 2em;} +dl {margin: 0 1em;} +dt {font-size: 85%; font-family: "Consolas", "Courier New", "lr SVbN", monospace;} +dl.par dt {margin: 0.5em 0 0 0 ; font-style: italic; letter-spacing: 0;} +dl.ret dt {margin: 0.5em 0 0 0 ; font-size: 85%; font-family: "Consolas", "Courier New", "lr SVbN", monospace; letter-spacing: 0; } +dd {margin: 0 2em;} +hr {border-width: 1px; margin: 1em;} +div.abst {font-family: "lr oSVbN",sans-serif;} +div.para {clear: both; font-family: "lr o",serif;} +div.ret a {font-size: 85%; font-family: "Consolas", "Courier New", "lr SVbN", monospace; } +.equ {text-indent: 0; margin: 1em 2em 1em;} +.indent {margin-left: 2em;} +.rset {float: right; margin: 0 0 0.5em 0.5em;} +.lset {float: left; margin: 0 0.5em 0.5em 0.5em;} +ul.flat li {list-style-type: none; margin: 0;} +a.imglnk img {border: 1px solid;} +.iequ {white-space: nowrap; font-weight: bold;} +.clr {clear: both;} +.arg {font-style: italic;} +.it {font-style: italic;} +.mfd {font-size: 0.7em; padding: 0 1px; border: 1px solid; white-space : nowrap} + +h1 {line-height: 1em; font-size: 2em; font-family: sans-serif; padding: 0.3em 0 0.3em;} +p.hdd {float: right; text-align: right; margin-top: 0.5em;} +hr.hds {clear: both; margin-bottom: 1em;} + +h2 {font-size: 2em; font-family: "lr oSVbN",sans-serif; background-color: #d8d8FF; padding: 0.5em 0.5em; margin: 0 0 0.5em;} +h3 {font-size: 1.5em; font-family: "lr oSVbN",sans-serif; margin: 1.5em 0 0.5em;} +h4 {font-size: 1.2em; font-family: "lr oSVbN",sans-serif; margin: 1em 0 0.2em;} +h5 {font-size: 1em; font-family: "lr oSVbN",sans-serif; margin: 0.5em 0 0em;} +small {font-size: 80%;} +.indent {margin-left: 2em;} + +/* Tables */ +table {margin: 0.5em 1em; border-collapse: collapse; border: 2px solid black; letter-spacing: 0;} +th {background-color: white; border-style: solid; border-width: 1px 1px 2px; border-color: black; padding: 0 3px; vertical-align: top;} +td {background-color: white; border: 1px solid black; padding: 0 3px; vertical-align: top; line-height: 1.3em;} +table.lst td:first-child {font-size: 85%; font-family: "Consolas", "Courier New", monospace; white-space: nowrap;} +table.lst2 td {font-size: 85%; font-family: "Consolas", "Courier New", monospace; white-space: nowrap;} +table caption {font-family: sans-serif; font-weight: bold;} +tr.lst3 td {border-width: 2px 1px 1px; } + +p.foot {clear: both; text-indent: 0; margin: 1em 0.5em 1em;} diff --git a/demo/easy_transplant/xprintf/doc/xprintf.html b/demo/easy_transplant/xprintf/doc/xprintf.html new file mode 100644 index 0000000..2144571 --- /dev/null +++ b/demo/easy_transplant/xprintf/doc/xprintf.html @@ -0,0 +1,214 @@ + + + + + + + +ELM - Embedded String Functions + + + +

xprintf - Embedded String Functions

+
+ +
+

xprintf is a compact string I/O library. It is ideal for tiny microcontrollers that has insufficient program memory for regular printf function. The recommended use is: writing formatted strings into LCD or UART and for debug/maintenance console.

+

xprintf can be configured with configuration options to reduce the module size. Following table shows the example of code size in Cortex-M3 (gcc -Os). long long and float want C99 or later.

+ + + + + + +
Feature.text
Basic output789
long long integer+91
Floating point+1027
Input+238
+
+ +
+

Application Interface

+

The Embedded String Functions provides following functions.

+

Output

+
+/*----------------------------------------------/
+/  xputc - Put a character
+/----------------------------------------------*/
+
+void xputc (
+    int chr           /* A character to be output (0-255) */
+);
+
+void xfputc (
+    void(*func)(int), /* Pointer to the output function */
+    int chr           /* Character to be output (0-255) */
+);
+
+
+/*----------------------------------------------/
+/  xputs - Put a null-terminated string
+/----------------------------------------------*/
+
+void xputs (
+    const char* str   /* Pointer to the null-terminated string to be output */
+);
+
+void xfputs (
+    void(*func)(int), /* Pointer to the output function */
+    const char* str   /* Pointer to the null-terminated string to be output */
+);
+
+
+/*----------------------------------------------/
+/  xprintf - Formatted string output
+/----------------------------------------------*/
+
+void xprintf (        /* Put a formatted string to the default device */
+    const char* fmt,  /* Pointer to the null-terminated format string */
+    ...               /* Optional arguments... */
+);
+
+void xfprintf (       /* Put a formatted string to the specified device */
+    void(*func)(int), /* Pointer to the output function */
+    const char* fmt,  /* Pointer to the null-terminated format string */
+    ...               /* Optional arguments... */
+);
+
+void xsprintf (       /* Put a formatted string to the memory */
+    char* buff,       /* Pointer to the buffer to store output string */
+    const char* fmt,  /* Pointer to the null-terminated format string */
+    ...               /* Optional arguments... */
+);
+
+
+/*----------------------------------------------/
+/  put_dump - Put a line of binary dump
+/----------------------------------------------*/
+
+void put_dump (
+    const void* buff,   /* Pointer to the data to be displayed */
+    unsigned long adr,  /* Heading address */
+    int cnt,            /* Number of items to be displayed */
+    int width           /* Size of item (1, 2 or 4) */
+);
+
+

+The format control directive is a sub-set of standard library shown as follows: +

+
+    %[flag][width][precision][size]type
+
+
+
flag
Padding options. A - specifies left-aligned. A 0 specifies zero padded. The default setting is in right-aligned and space padded.
+
width
Minimum width of the field, 1-99 or *. If the width of generated string is less than the specified value, rest field is padded with spaces or zeros. An * specifies the value comes from an argument in int type.
+
precision
Specifies number of fractional digits or maximum width of string, .0-.99 or .*. If number is omitted, it will be same as .0. Default setting is 6 for number and no limit for string.
+
size
Specifies size of integer argument, l(long) and ll(long long). If sizeof (long) == sizeof (int) is true (this is typical of 32-bit systems), prefix l can be omitted for long integer argument. The default size is int for integer arrument and floating point argument is always assumed double.
+
type
Specifies type of the output format and the argument as shown below. The length of generated string is in assumtion of int is 32-bit. + + + + + + + + + + + +
TypeFormatArgumentLength
dSigned decimalint,
long,
long long
1 to 11 (20 for ll) characters.
uUnsigned decimal1 to 10 (20 for ll) characters.
oUnsigned octal1 to 11 (22 for ll) characters.
x XUnsigned hexdecimal1 to 8 (16 for ll) characters.
bUnsigned binary1 to 32 characters. Limited to lower 32 digits when ll is specified.
cCharacterint1 character.
sStringchar*As input string. If the maximum length of input string is unkown, precision should be specified to avoid output buffer overflow. Null pointer generates a null string.
fFloating point
(decimal)
double1 to 31 characters. If the number of characters exceeds 31, it generates "±OV". Not a number and infinite generate "NaN" and "±INF".
e EFloating point
(e notation)
4 to 31 characters. If the number of characters exceeds 31 or exponent exceeds +99, it generates "±OV".
+
+
+
+Examples:
+    xprintf("%d", 1234);             /* "1234" */
+    xprintf("%6d,%3d%%", -200, 5);   /* "  -200,  5%" */
+    xprintf("%-6u", 100);            /* "100   " */
+    xprintf("%ld", 12345678);        /* "12345678" */
+    xprintf("%llu", 0x100000000);    /* "4294967296"   <XF_USE_LLI> */
+    xprintf("%lld", -1LL);           /* "-1"           <XF_USE_LLI> */
+    xprintf("%04x", 0xA3);           /* "00a3" */
+    xprintf("%08lX", 0x123ABC);      /* "00123ABC" */
+    xprintf("%016b", 0x550F);        /* "0101010100001111" */
+    xprintf("%*d", 6, 100);          /* "   100" */
+    xprintf("%s", "abcdefg");        /* "abcdefg" */
+    xprintf("%5s", "abc");           /* "  abc" */
+    xprintf("%-5s", "abc");          /* "abc  " */
+    xprintf("%.5s", "abcdefg");      /* "abcde" */
+    xprintf("%-5.2s", "abcdefg");    /* "ab   " */
+    xprintf("%c", 'a');              /* "a" */
+    xprintf("%12f", 10.0);           /* "   10.000000" <XF_USE_FP> */
+    xprintf("%.4E", 123.45678);      /* "1.2346E+02"   <XF_USE_FP> */
+
+ + + +

Input

+
+/*----------------------------------------------/
+/  xgets - Get a line from the input device
+/----------------------------------------------*/
+
+int xgets (     /* 0:End of stream, 1:A line arrived */
+    char* buff, /* Pointer to the buffer to input */
+    int len     /* Buffer length */
+);
+
+
+/*----------------------------------------------/
+/  xatoi - Get a value of integer string
+/----------------------------------------------*/
+/* "123 -5    0x3ff 0b1111 0377 1.5 "
+       ^                            1st call returns 123 and next ptr
+          ^                         2nd call returns -5 and next ptr
+                   ^                3rd call returns 1023 and next ptr
+                          ^         4th call returns 15 and next ptr
+                               ^    5th call returns 255 and next ptr
+                                 ^  6th call fails and returns 0
+*/
+
+int xatoi (      /* 0:Failed, 1:Succeeded */
+    char** str,  /* Pointer to pointer to the string */
+    long* res    /* Pointer to the valiable to store the value */
+);
+
+
+/*----------------------------------------------/
+/  xatof - Get a value of floating point string
+/----------------------------------------------*/
+/* "123 -5.75 .6   +8.88E+5 1e-6  .  "
+       ^                              1st call returns 1.23e2 and next ptr
+             ^                        2nd call returns -5.75e0 and next ptr
+                ^                     3rd call returns 6e-1 and next ptr
+                           ^          4th call returns 8.88e5 and next ptr
+                                ^     5th call returns 1e-6 and next ptr
+                                   ^  6th call fails and returns 0
+*/
+
+int xatof (      /* 0:Failed, 1:Succeded */
+    char** str,  /* Pointer to pointer to the string */
+    double* res  /* Pointer to the valiable to store the value */
+);
+
+ +
+ + +
+

Device I/O functions

+

The output function is a user provided call-back function to write a byte to the output device. Its address should be set to the function pointer xfunc_output in the module, default output device. Typically, this function puts the byte to UART, LCD or some output device. The output function is called-back from xputc(). There is a macro to set it easy. For example, when attach void uart1_putc (uint8_t chr); to the module, xdev_out(uart1_putc); will do. If the output function has multiple arguments or simple output function is not available, a glue function will be needed. xfputc(), xfputs(), xfprintf() and xsprintf() override the default output device with its argument.

+

The input function is a user provided call-back function to read a byte from the input device. Its address should be set to the function pointer xfunc_input, default input device. There is a macro xdev_in() to set it easy. e.g. xdev_in(uart1_getc); The xfgets() function override the default input device with its argument. The input function is called-back from the xgets() function. Typically, input function reads a byte from input device or file. When the input device reported end of stream, the input function should return -1. The xgets() function aborts with zero and the application will able to detect it.

+
+/* Write a byte to the output dechvice */
+
+void output_func (
+    int chr  /* A byte to be written */
+);
+
+
+/* Read a byte from the input device */
+
+int input_func (void);  /* Returns 0 to 255 as a read character, -1 as EOF */
+
+
+ + + + diff --git a/demo/easy_transplant/xprintf/doc/xprintf_j.html b/demo/easy_transplant/xprintf/doc/xprintf_j.html new file mode 100644 index 0000000..57afdd5 --- /dev/null +++ b/demo/easy_transplant/xprintf/doc/xprintf_j.html @@ -0,0 +1,216 @@ + + + + + + + +ELM - 組み込み用printfモジュール + + + +

xprintf - 組み込み用printfモジュール

+
+ +
+

xprintfは組み込み用に特化したコンパクトなprintfとサポート関数群です。標準入出力関数のサポートされない組み込みシステムにおいて、既存の入出力デバイス(UARTやLCD)に結合することにより、それらに対してprintfで簡単に整形文字列を出力することができます。このため、LCDやUARTなどに手軽に整形出力したいときや、デバッグ・メンテナンス・コンソールなどに有効です。

+

xprintfは構成オプション(xprintf.h内に定義)で必要な機能のみ組み込むことでモジュールサイズを削減することができます。例としてCortex-M3でのコンパイル結果を次の表に示します。(gcc -Os) なお、long longと浮動小数点は、C99を必要とします。

+ + + + + + +
機能.text
基本出力789
long long+91
浮動小数点+1027
入力+238
+
+ + +
+

モジュールAPI

+

組み込み用printfモジュールは、次に示す関数を提供します。

+ +

出力

+
+/*----------------------------------------------/
+/  xputc - 1文字出力
+/----------------------------------------------*/
+
+void xputc (
+    int chr           /* 出力文字(0-255) */
+);
+
+void xfputc (
+    void(*func)(int), /* 出力デバイスの出力関数へのポインタ */
+    int chr           /* 出力する文字(0-255) */
+);
+
+
+/*----------------------------------------------/
+/  xputs - 文字列の出力
+/----------------------------------------------*/
+
+void xputs (
+    const char* str   /* 出力するASCIZ文字列へのポインタ(終端の'\0'は出力されません) */
+);
+
+void xfputs (
+    void(*func)(int), /* 出力デバイスの出力関数へのポインタ */
+    const char* str   /* 出力するASCIZ文字列へのポインタ(終端の'\0'は出力されません) */
+);
+
+
+/*----------------------------------------------/
+/  xprintf - 書式付き文字列出力
+/----------------------------------------------*/
+
+void xprintf (        /* デフォルト出力デバイスへの書式付き文字列出力 */
+    const char* fmt,  /* 書式文字列へのポインタ */
+    ...               /* オプションの引数... */
+);
+
+void xfprintf (       /* 指定出力デバイスへの書式付き文字列出力 */
+    void(*func)(int), /* 出力デバイスの出力関数へのポインタ */
+    const char* fmt,  /* 書式文字列へのポインタ */
+    ...               /* オプションの引数... */
+);
+
+void xsprintf (       /* メモリ上に書式付き文字列(ASCIZ)を出力 */
+    char* buff,       /* 文字列出力バッファへのポインタ */
+    const char* fmt,  /* 書式文字列へのポインタ */
+    ...               /* オプションの引数... */
+);
+
+ +
+/*----------------------------------------------/
+/  put_dump - 1行のバイナリ・ダンプを出力
+/----------------------------------------------*/
+
+void put_dump (
+    const void* buff,   /* 出力する配列へのポインタ */
+    unsigned long adr,  /* 行頭に表示するアドレス値 */
+    int cnt,            /* 出力する要素数 */
+    int width           /* 要素のサイズ (1, 2, 4) */
+);
+
+ +

+書式制御命令は次に示すように標準ライブラリのサブセットとなっています。 +

+
+    %[フラグ][最小幅][精度][サイズ]タイプ
+
+
+
フラグ
生成文字列の幅が最小幅に満たないときのパディング。0はゼロ'0'埋めを指定します。-は左詰めを指定します。デフォルトは、右詰めでスペースでパディングです。
+
最小幅
最小幅の指定。1-99または**は引数(int型)から値を取得します。デフォルト値は0です。
+
精度
浮動小数点数の小数部の桁数または文字列の最大幅を指定します。.0~.99または.*。数値を省略した場合は.0と同じです。デフォルトは、浮動小数点では6桁、文字列では無制限です。
+
サイズ
整数引数のサイズ。l(long)またはll(long long)。デフォルトはintで、sizeof (long) == sizeof (int)の場合(32ビット系の多く)はlを省略できます。浮動小数点引数は常にdoubleとみなされます。
+
タイプ
次の表に示すように引数の型および出力形式を指定します。(生成文字列長はintが32ビットの場合) + + + + + + + + + + + +
タイプフォーマット引数生成文字列長(パディング含まず)
d符号付き10進数int,
long,
long long
1~11(ll指定時は20)文字。
u符号なし10進数1~10(ll指定時は20)文字。
o符号なし8進数1~11(ll指定時は22)文字。
x X符号なし16進数1~8(ll指定時は16)文字。
b符号なし2進数1~32(ll指定時も下位32桁まで)文字。
cキャラクタint1文字。
s文字列char*xsprintfで入力文字列長が不明のときは適切な最大幅を指定すること。
ヌルポインタは文字列長0と見なされる。
f浮動小数点
(固定桁)
double1~31文字、文字数が31を越える場合は"±OV"、非数は"NaN", 無限大は"±INF"
e E浮動小数点
(指数)
4~31文字、文字数が31を越える場合や指数が+99を越える場合は"±OV"
+
+
+
+使用例:
+    xprintf("%d", 1234);             /* "1234" */
+    xprintf("%6d,%3d%%", -200, 5);   /* "  -200,  5%" */
+    xprintf("%-6u", 100);            /* "100   " */
+    xprintf("%ld", 12345678);        /* "12345678" */
+    xprintf("%llu", 0x100000000);    /* "4294967296"   <XF_USE_LLI> */
+    xprintf("%lld", -1LL);           /* "-1"           <XF_USE_LLI> */
+    xprintf("%04x", 0xA3);           /* "00a3" */
+    xprintf("%08lX", 0x123ABC);      /* "00123ABC" */
+    xprintf("%016b", 0x550F);        /* "0101010100001111" */
+    xprintf("%*d", 6, 100);          /* "   100" */
+    xprintf("%s", "abcdefg");        /* "abcdefg" */
+    xprintf("%5s", "abc");           /* "  abc" */
+    xprintf("%-5s", "abc");          /* "abc  " */
+    xprintf("%.5s", "abcdefg");      /* "abcde" */
+    xprintf("%-5.2s", "abcdefg");    /* "ab   " */
+    xprintf("%c", 'a');              /* "a" */
+    xprintf("%12f", 10.0);           /* "   10.000000" <XF_USE_FP> */
+    xprintf("%.4E", 123.45678);      /* "1.2346E+02"   <XF_USE_FP> */
+
+ +

入力

+
+/*----------------------------------------------/
+/  xgets - 入力デバイスから1行入力
+/----------------------------------------------*/
+
+int xgets (     /* 結果: 0:EOF, 1:1行確定 */
+    char* buff, /* 入力バッファへのポインタ */
+    int len     /* 入力バッファのサイズ(文字数) */
+);
+
+
+/*----------------------------------------------/
+/  xatoi - 整数文字列の数値を取得
+/----------------------------------------------*/
+/* "123 -5   0x3ff 0b1111 0377 1.5 "
+       ^                           1st call returns 123 and next ptr
+          ^                        2nd call returns -5 and next ptr
+                  ^                3rd call returns 1023 and next ptr
+                         ^         4th call returns 15 and next ptr
+                              ^    5th call returns 255 and next ptr
+                                ^  6th call fails and returns 0
+*/
+
+int xatoi (      /* 0:失敗, 1:成功 */
+    char** str,  /* 対象の文字列を指すポインタへのポインタ */
+    long* res    /* 結果をストアする変数へのポインタ */
+);
+
+
+/*----------------------------------------------/
+/  xatof - 実数文字列の数値を取得
+/----------------------------------------------*/
+/* "123 -5.75 .6   +8.88E+5 1e-6 .  "
+       ^                             1st call returns 1.23e2 and next ptr
+             ^                       2nd call returns -5.75e0 and next ptr
+                ^                    3rd call returns 6e-1 and next ptr
+                           ^         4th call returns 8.88e5 and next ptr
+                                ^    5th call returns 1e-6 and next ptr
+                                  ^  6th call fails and returns 0
+*/
+
+int xatof (      /* 0:失敗, 1:成功 */
+    char** str,  /* 対象の文字列を指すポインタへのポインタ */
+    double* res  /* 結果をストアする変数へのポインタ */
+);
+
+ +
+ + +
+

入出力デバイスとの結合

+

UARTやLCDなどの出力デバイスに結合するには、デフォルト出力デバイスとしてモジュールのグローバル変数xfunc_outputに、そのデバイスの1文字出力関数へのポインタを代入するだけでOKです。これにより、xputc/xputs/xprintf/put_dump関数の出力は指定された関数に渡されます。xfputc/xfputs/xfprintf/xsprintf関数の出力先は、それぞれの引数で直接指定されます。この設定のためマクロも用意されているので、例えば、void uart1_putc (uint8_t chr);に結合する場合、xdev_out(uart1_putc);とすればOKです。出力関数が複数の引数をとる場合や単純な1文字出力が無い場合は、グルー関数を間にかませる必要があるかも知れません。

+

UARTなどの入力デバイスに結合するには、デフォルト入力デバイスとしてモジュールのグローバル変数xfunc_inputに、そのデバイスの1文字読み出し関数へのポインタを代入するだけでOKです。(設定マクロを使用した例:xdev_in(uart1_getc);) xgets関数はデフォルト入力デバイスからライン入力を行います。xfgets関数は入力関数を引数で直接指定します。読みだされた文字は、順にバッファにストアされます。'\r'、'\b'以外の制御文字は無視されます。'\r'が読み出されると読み出しを終了してxgets関数は1を返します。'\r'はバッファにはストアされず、文字列は'\0'で終端されます。通常、入力があるまで読み出し関数は制御を返しませんが、入力デバイスはコンソールに限られるわけではない(ファイルに結合した例もあります)ので、入力ストリームの終端が明確なときは-1を返すべきです。これにより、xgets関数は中断して0を返すので、アプリケーションがストリーム終端を知ることが可能になります。

+
+/* デバイス出力関数の型 */
+
+void output_func (
+    int chr  /* 出力文字(0-255) */
+);
+
+
+/* デバイス入力関数の型 */
+
+int input_func (void);  /* 0-255:読み出した文字, -1:入力ストリームの終端 */
+
+
+ + + + diff --git a/demo/easy_transplant/xprintf/src/xprintf.c b/demo/easy_transplant/xprintf/src/xprintf.c new file mode 100644 index 0000000..c319203 --- /dev/null +++ b/demo/easy_transplant/xprintf/src/xprintf.c @@ -0,0 +1,807 @@ +/*------------------------------------------------------------------------/ +/ Universal String Handler for Console Input and Output +/-------------------------------------------------------------------------/ +/ +/ Copyright (C) 2021, ChaN, all right reserved. +/ +/ xprintf module is an open source software. Redistribution and use of +/ xprintf module in source and binary forms, with or without modification, +/ are permitted provided that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/-------------------------------------------------------------------------*/ + +#include "xprintf.h" + +#define SZB_OUTPUT 32 + +#if XF_USE_OUTPUT +#include +void (*xfunc_output)(int); /* Pointer to the default output device */ +static char *strptr; /* Pointer to the output memory (used by xsprintf) */ + +#if XF_USE_FP +/*----------------------------------------------*/ +/* Floating point output */ +/*----------------------------------------------*/ +#include + +static int ilog10(double n) /* Calculate log10(n) in integer output */ +{ + int rv = 0; + + while (n >= 10) + { /* Decimate digit in right shift */ + if (n >= 100000) + { + n /= 100000; + rv += 5; + } + else + { + n /= 10; + rv++; + } + } + while (n < 1) + { /* Decimate digit in left shift */ + if (n < 0.00001) + { + n *= 100000; + rv -= 5; + } + else + { + n *= 10; + rv--; + } + } + return rv; +} + +static double i10x(int n) /* Calculate 10^n */ +{ + double rv = 1; + + while (n > 0) + { /* Left shift */ + if (n >= 5) + { + rv *= 100000; + n -= 5; + } + else + { + rv *= 10; + n--; + } + } + while (n < 0) + { /* Right shift */ + if (n <= -5) + { + rv /= 100000; + n += 5; + } + else + { + rv /= 10; + n++; + } + } + return rv; +} + +static void ftoa( + char *buf, /* Buffer to output the generated string */ + double val, /* Real number to output */ + int prec, /* Number of fractinal digits */ + char fmt /* Notation */ +) +{ + int d; + int e = 0, m = 0; + char sign = 0; + double w; + const char *er = 0; + + if (isnan(val)) + { /* Not a number? */ + er = "NaN"; + } + else + { + if (prec < 0) + prec = 6; /* Default precision (6 fractional digits) */ + if (val < 0) + { /* Nagative value? */ + val = -val; + sign = '-'; + } + else + { + sign = '+'; + } + if (isinf(val)) + { /* Infinite? */ + er = "INF"; + } + else + { + if (fmt == 'f') + { /* Decimal notation? */ + val += i10x(-prec) / 2; /* Round (nearest) */ + m = ilog10(val); + if (m < 0) + m = 0; + if (m + prec + 3 >= SZB_OUTPUT) + er = "OV"; /* Buffer overflow? */ + } + else + { /* E notation */ + if (val != 0) + { /* Not a true zero? */ + val += i10x(ilog10(val) - prec) / 2; /* Round (nearest) */ + e = ilog10(val); + if (e > 99 || prec + 6 >= SZB_OUTPUT) + { /* Buffer overflow or E > +99? */ + er = "OV"; + } + else + { + if (e < -99) + e = -99; + val /= i10x(e); /* Normalize */ + } + } + } + } + if (!er) + { /* Not error condition */ + if (sign == '-') + *buf++ = sign; /* Add a - if negative value */ + do + { /* Put decimal number */ + w = i10x(m); /* Snip the highest digit d */ + d = val / w; + val -= d * w; + if (m == -1) + *buf++ = XF_DPC; /* Insert a decimal separarot if get into fractional part */ + *buf++ = '0' + d; /* Put the digit */ + } while (--m >= -prec); /* Output all digits specified by prec */ + if (fmt != 'f') + { /* Put exponent if needed */ + *buf++ = fmt; + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + *buf++ = '0' + e / 10; + *buf++ = '0' + e % 10; + } + } + } + if (er) + { /* Error condition? */ + if (sign) + *buf++ = sign; /* Add sign if needed */ + do + *buf++ = *er++; + while (*er); /* Put error symbol */ + } + *buf = 0; /* Term */ +} +#endif /* XF_USE_FLOAT */ + +/*----------------------------------------------*/ +/* Put a character */ +/*----------------------------------------------*/ + +void xputc( + int chr /* Character to be output */ +) +{ + xfputc(xfunc_output, chr); /* Output it to the default output device */ +} + +void xfputc( /* Put a character to the specified device */ + void (*func)(int), /* Pointer to the output function (null:strptr) */ + int chr /* Character to be output */ +) +{ + if (XF_CRLF && chr == '\n') + xfputc(func, '\r'); /* CR -> CRLF */ + + if (func) + { + func(chr); /* Write a character to the output device */ + } + else if (strptr) + { + *strptr++ = chr; /* Write a character to the memory */ + } +} + +/*----------------------------------------------*/ +/* Put a null-terminated string */ +/*----------------------------------------------*/ + +void xputs( /* Put a string to the default device */ + const char *str /* Pointer to the string */ +) +{ + xfputs(xfunc_output, str); +} + +void xfputs( /* Put a string to the specified device */ + void (*func)(int), /* Pointer to the output function */ + const char *str /* Pointer to the string */ +) +{ + while (*str) + { /* Put the string */ + xfputc(func, *str++); + } +} + +/*----------------------------------------------*/ +/* Formatted string output */ +/*----------------------------------------------*/ +/* xprintf("%d", 1234); "1234" + xprintf("%6d,%3d%%", -200, 5); " -200, 5%" + xprintf("%-6u", 100); "100 " + xprintf("%ld", 12345678); "12345678" + xprintf("%llu", 0x100000000); "4294967296" + xprintf("%lld", -1LL); "-1" + xprintf("%04x", 0xA3); "00a3" + xprintf("%08lX", 0x123ABC); "00123ABC" + xprintf("%016b", 0x550F); "0101010100001111" + xprintf("%*d", 6, 100); " 100" + xprintf("%s", "String"); "String" + xprintf("%5s", "abc"); " abc" + xprintf("%-5s", "abc"); "abc " + xprintf("%-5s", "abcdefg"); "abcdefg" + xprintf("%-5.5s", "abcdefg"); "abcde" + xprintf("%-.5s", "abcdefg"); "abcde" + xprintf("%-5.5s", "abc"); "abc " + xprintf("%c", 'a'); "a" + xprintf("%12f", 10.0); " 10.000000" + xprintf("%.4E", 123.45678); "1.2346E+02" +*/ + +static void xvfprintf( + void (*func)(int), /* Pointer to the output function */ + const char *fmt, /* Pointer to the format string */ + va_list arp /* Pointer to arguments */ +) +{ + unsigned int r, i, j, w, f; + int n, prec; + char str[SZB_OUTPUT], c, d, *p, pad; +#if XF_USE_LLI + long long v; + unsigned long long uv; +#else + long v; + unsigned long uv; +#endif + + for (;;) + { + c = *fmt++; /* Get a format character */ + if (!c) + break; /* End of format? */ + if (c != '%') + { /* Pass it through if not a % sequense */ + xfputc(func, c); + continue; + } + f = w = 0; /* Clear parms */ + pad = ' '; + prec = -1; + c = *fmt++; /* Get first char of the sequense */ + if (c == '0') + { /* Flag: left '0' padded */ + pad = '0'; + c = *fmt++; + } + else + { + if (c == '-') + { /* Flag: left justified */ + f = 2; + c = *fmt++; + } + } + if (c == '*') + { /* Minimum width from an argument */ + n = va_arg(arp, int); + if (n < 0) + { /* Flag: left justified */ + n = 0 - n; + f = 2; + } + w = n; + c = *fmt++; + } + else + { + while (c >= '0' && c <= '9') + { /* Minimum width */ + w = w * 10 + c - '0'; + c = *fmt++; + } + } + if (c == '.') + { /* Precision */ + c = *fmt++; + if (c == '*') + { /* Precision from an argument */ + prec = va_arg(arp, int); + c = *fmt++; + } + else + { + prec = 0; + while (c >= '0' && c <= '9') + { + prec = prec * 10 + c - '0'; + c = *fmt++; + } + } + } + if (c == 'l') + { /* Prefix: Size is long */ + f |= 4; + c = *fmt++; +#if XF_USE_LLI + if (c == 'l') + { /* Prefix: Size is long long */ + f |= 8; + c = *fmt++; + } +#endif + } + if (!c) + break; /* End of format? */ + switch (c) + { /* Type is... */ + case 'b': /* Unsigned binary */ + r = 2; + break; + case 'o': /* Unsigned octal */ + r = 8; + break; + case 'd': /* Signed decimal */ + case 'u': /* Unsigned decimal */ + r = 10; + break; + case 'x': /* Hexdecimal (lower case) */ + case 'X': /* Hexdecimal (upper case) */ + r = 16; + break; + case 'c': /* A character */ + xfputc(func, (char)va_arg(arp, int)); + continue; + case 's': /* String */ + p = va_arg(arp, char *); /* Get a pointer argument */ + if (!p) + p = ""; /* Null ptr generates a null string */ + j = strlen(p); + if (prec >= 0 && j > (unsigned int)prec) + j = prec; /* Limited length of string body */ + for (; !(f & 2) && j < w; j++) + xfputc(func, pad); /* Left pads */ + while (*p && prec--) + xfputc(func, *p++); /* String body */ + while (j++ < w) + xfputc(func, ' '); /* Right pads */ + continue; +#if XF_USE_FP + case 'f': /* Float (decimal) */ + case 'e': /* Float (e) */ + case 'E': /* Float (E) */ + ftoa(p = str, va_arg(arp, double), prec, c); /* Make fp string */ + for (j = strlen(p); !(f & 2) && j < w; j++) + xfputc(func, pad); /* Left pads */ + while (*p) + xfputc(func, *p++); /* Value */ + while (j++ < w) + xfputc(func, ' '); /* Right pads */ + continue; +#endif + default: /* Unknown type (passthrough) */ + xfputc(func, c); + continue; + } + + /* Get an integer argument and put it in numeral */ +#if XF_USE_LLI + if (f & 8) + { /* long long argument? */ + v = (long long)va_arg(arp, long long); + } + else + { + if (f & 4) + { /* long argument? */ + v = (c == 'd') ? (long long)va_arg(arp, long) : (long long)va_arg(arp, unsigned long); + } + else + { /* int/short/char argument */ + v = (c == 'd') ? (long long)va_arg(arp, int) : (long long)va_arg(arp, unsigned int); + } + } +#else + if (f & 4) + { /* long argument? */ + v = (long)va_arg(arp, long); + } + else + { /* int/short/char argument */ + v = (c == 'd') ? (long)va_arg(arp, int) : (long)va_arg(arp, unsigned int); + } +#endif + if (c == 'd' && v < 0) + { /* Negative value? */ + v = 0 - v; + f |= 1; + } + i = 0; + uv = v; + do + { /* Make an integer number string */ + d = (char)(uv % r); + uv /= r; + if (d > 9) + d += (c == 'x') ? 0x27 : 0x07; + str[i++] = d + '0'; + } while (uv != 0 && i < sizeof str); + if (f & 1) + str[i++] = '-'; /* Sign */ + for (j = i; !(f & 2) && j < w; j++) + xfputc(func, pad); /* Left pads */ + do + xfputc(func, str[--i]); + while (i != 0); /* Value */ + while (j++ < w) + xfputc(func, ' '); /* Right pads */ + } +} + +void xprintf( /* Put a formatted string to the default device */ + const char *fmt, /* Pointer to the format string */ + ... /* Optional arguments */ +) +{ + va_list arp; + + va_start(arp, fmt); + xvfprintf(xfunc_output, fmt, arp); + va_end(arp); +} + +void xfprintf( /* Put a formatted string to the specified device */ + void (*func)(int), /* Pointer to the output function */ + const char *fmt, /* Pointer to the format string */ + ... /* Optional arguments */ +) +{ + va_list arp; + + va_start(arp, fmt); + xvfprintf(func, fmt, arp); + va_end(arp); +} + +void xsprintf( /* Put a formatted string to the memory */ + char *buff, /* Pointer to the output buffer */ + const char *fmt, /* Pointer to the format string */ + ... /* Optional arguments */ +) +{ + va_list arp; + + strptr = buff; /* Enable destination for memory */ + va_start(arp, fmt); + xvfprintf(0, fmt, arp); + va_end(arp); + *strptr = 0; /* Terminate output string */ + strptr = 0; /* Disable destination for memory */ +} + +#if XF_USE_DUMP +/*----------------------------------------------*/ +/* Dump a line of binary dump */ +/*----------------------------------------------*/ + +void put_dump( + const void *buff, /* Pointer to the array to be dumped */ + unsigned long addr, /* Heading address value */ + int len, /* Number of items to be dumped */ + size_t width /* Size of buff[0] (1, 2 or 4) */ +) +{ + int i; + const unsigned char *bp; + const unsigned short *sp; + const unsigned long *lp; + + xprintf("%08lX ", addr); /* address */ + + switch (width) + { + case sizeof(char): + bp = buff; + for (i = 0; i < len; i++) + { /* Hexdecimal dump in (char) */ + xprintf(" %02X", bp[i]); + } + xputs(" "); + for (i = 0; i < len; i++) + { /* ASCII dump */ + xputc((unsigned char)((bp[i] >= ' ' && bp[i] <= '~') ? bp[i] : '.')); + } + break; + case sizeof(short): + sp = buff; + do + { /* Hexdecimal dump in (short) */ + xprintf(" %04X", *sp++); + } while (--len); + break; + case sizeof(long): + lp = buff; + do + { /* Hexdecimal dump in (short) */ + xprintf(" %08lX", *lp++); + } while (--len); + break; + } + + xputc('\n'); +} +#endif /* XF_USE_DUMP */ + +#endif /* XF_USE_OUTPUT */ + +#if XF_USE_INPUT +int (*xfunc_input)(void); /* Pointer to the default input stream */ + +/*----------------------------------------------*/ +/* Get a line from the input */ +/*----------------------------------------------*/ + +int xgets( /* 0:End of stream, 1:A line arrived */ + char *buff, /* Pointer to the buffer */ + int len /* Buffer length */ +) +{ + int c, i; + + if (!xfunc_input) + return 0; /* No input function is specified */ + + i = 0; + for (;;) + { + c = xfunc_input(); /* Get a char from the incoming stream */ + if (c < 0 || c == '\r') + break; /* End of stream or CR? */ + if (c == '\b' && i) + { /* BS? */ + i--; + if (XF_INPUT_ECHO) + xputc(c); + continue; + } + if (c >= ' ' && i < len - 1) + { /* Visible chars? */ + buff[i++] = c; + if (XF_INPUT_ECHO) + xputc(c); + } + } + if (XF_INPUT_ECHO) + { + xputc('\r'); + xputc('\n'); + } + buff[i] = 0; /* Terminate with a \0 */ + return (int)(c == '\r'); +} + +/*----------------------------------------------*/ +/* Get a value of integer string */ +/*----------------------------------------------*/ +/* "123 -5 0x3ff 0b1111 0377 w " + ^ 1st call returns 123 and next ptr + ^ 2nd call returns -5 and next ptr + ^ 3rd call returns 1023 and next ptr + ^ 4th call returns 15 and next ptr + ^ 5th call returns 255 and next ptr + ^ 6th call fails and returns 0 +*/ + +int xatoi( /* 0:Failed, 1:Successful */ + char **str, /* Pointer to pointer to the string */ + long *res /* Pointer to the valiable to store the value */ +) +{ + unsigned long val; + unsigned char c, r, s = 0; + + *res = 0; + + while ((c = **str) == ' ') + (*str)++; /* Skip leading spaces */ + + if (c == '-') + { /* negative? */ + s = 1; + c = *(++(*str)); + } + + if (c == '0') + { + c = *(++(*str)); + switch (c) + { + case 'x': /* hexdecimal */ + r = 16; + c = *(++(*str)); + break; + case 'b': /* binary */ + r = 2; + c = *(++(*str)); + break; + default: + if (c <= ' ') + return 1; /* single zero */ + if (c < '0' || c > '9') + return 0; /* invalid char */ + r = 8; /* octal */ + } + } + else + { + if (c < '0' || c > '9') + return 0; /* EOL or invalid char */ + r = 10; /* decimal */ + } + + val = 0; + while (c > ' ') + { + if (c >= 'a') + c -= 0x20; + c -= '0'; + if (c >= 17) + { + c -= 7; + if (c <= 9) + return 0; /* invalid char */ + } + if (c >= r) + return 0; /* invalid char for current radix */ + val = val * r + c; + c = *(++(*str)); + } + if (s) + val = 0 - val; /* apply sign if needed */ + + *res = val; + return 1; +} + +#if XF_USE_FP +/*----------------------------------------------*/ +/* Get a value of the real number string */ +/*----------------------------------------------*/ +/* Float version of xatoi + */ + +int xatof( /* 0:Failed, 1:Successful */ + char **str, /* Pointer to pointer to the string */ + double *res /* Pointer to the valiable to store the value */ +) +{ + double val; + int s, f, e; + unsigned char c; + + *res = 0; + s = f = 0; + + while ((c = **str) == ' ') + (*str)++; /* Skip leading spaces */ + if (c == '-') + { /* Negative? */ + c = *(++(*str)); + s = 1; + } + else if (c == '+') + { /* Positive? */ + c = *(++(*str)); + } + if (c == XF_DPC) + { /* Leading dp? */ + f = -1; /* Start at fractional part */ + c = *(++(*str)); + } + if (c <= ' ') + return 0; /* Wrong termination? */ + val = 0; + while (c > ' ') + { /* Get a value of decimal */ + if (c == XF_DPC) + { /* Embedded dp? */ + if (f < 0) + return 0; /* Wrong dp? */ + f = -1; /* Enter fractional part */ + } + else + { + if (c < '0' || c > '9') + break; /* End of decimal? */ + c -= '0'; + if (f == 0) + { /* In integer part */ + val = val * 10 + c; + } + else + { /* In fractional part */ + val += i10x(f--) * c; + } + } + c = *(++(*str)); + } + if (c > ' ') + { /* It may be an exponent */ + if (c != 'e' && c != 'E') + return 0; /* Wrong character? */ + c = *(++(*str)); + if (c == '-') + { + c = *(++(*str)); + s |= 2; /* Negative exponent */ + } + else if (c == '+') + { + c = *(++(*str)); /* Positive exponent */ + } + if (c <= ' ') + return 0; /* Wrong termination? */ + e = 0; + while (c > ' ') + { /* Get value of exponent */ + c -= '0'; + if (c > 9) + return 0; /* Not a numeral? */ + e = e * 10 + c; + c = *(++(*str)); + } + val *= i10x((s & 2) ? -e : e); /* Apply exponent */ + } + + if (s & 1) + val = -val; /* Negate sign if needed */ + + *res = val; + return 1; +} +#endif /* XF_USE_FP */ + +#endif /* XF_USE_INPUT */ diff --git a/demo/easy_transplant/xprintf/src/xprintf.h b/demo/easy_transplant/xprintf/src/xprintf.h new file mode 100644 index 0000000..061db6d --- /dev/null +++ b/demo/easy_transplant/xprintf/src/xprintf.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------*/ +/* Universal string handler for user console interface (C)ChaN, 2021 */ +/*------------------------------------------------------------------------*/ + +#ifndef XPRINTF_DEF +#define XPRINTF_DEF +#include + +// clang-format off + +#ifdef __cplusplus +extern "C" { +#endif + +#define XF_USE_OUTPUT 1 /* 1: Enable output functions */ +#define XF_CRLF 1 /* 1: Convert \n ==> \r\n in the output char */ +#define XF_USE_DUMP 1 /* 1: Enable put_dump function */ +#define XF_USE_LLI 1 /* 1: Enable long long integer in size prefix ll */ +#define XF_USE_FP 1 /* 1: Enable support for floating point in type e and f */ +#define XF_DPC '.' /* Decimal separator for floating point */ +#define XF_USE_INPUT 0 /* 1: Enable input functions */ +#define XF_INPUT_ECHO 0 /* 1: Echo back input chars in xgets function */ + + // clang-format on + +#if defined(__GNUC__) && __GNUC__ >= 10 +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + +#if XF_USE_OUTPUT +#define xdev_out(func) xfunc_output = (void (*)(int))(func) + extern void (*xfunc_output)(int); + void xputc(int chr); + void xfputc(void (*func)(int), int chr); + void xputs(const char *str); + void xfputs(void (*func)(int), const char *str); + void xprintf(const char *fmt, ...); + void xsprintf(char *buff, const char *fmt, ...); + void xfprintf(void (*func)(int), const char *fmt, ...); + void put_dump(const void *buff, unsigned long addr, int len, size_t width); +#endif + +#if XF_USE_INPUT +#define xdev_in(func) xfunc_input = (int (*)(void))(func) + extern int (*xfunc_input)(void); + int xgets(char *buff, int len); + int xatoi(char **str, long *res); + int xatof(char **str, double *res); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extensions/log/log.h b/extensions/log/log.h index 4c4075e..33611ab 100644 --- a/extensions/log/log.h +++ b/extensions/log/log.h @@ -4,9 +4,9 @@ * @brief log * @version 1.0.0 * @date 2020-07-30 - * + * * @copyright (c) 2020 Letter - * + * */ #ifndef __LOG_H__ #define __LOG_H__ @@ -21,13 +21,13 @@ extern "C" { #define SHELL_COMPANION_ID_LOG -2 -#define LOG_USING_LOCK 0 +#define LOG_USING_LOCK 1 #define LOG_BUFFER_SIZE 256 /**< log输出缓冲大小 */ #define LOG_USING_COLOR 1 /**< 是否使用颜色 */ #define LOG_MAX_NUMBER 5 /**< 允许注册的最大log对象数量 */ #define LOG_AUTO_TAG 1 /**< 是否自动添加TAG */ #define LOG_END "\r\n" /**< log信息结尾 */ -#define LOG_TIME_STAMP 0 /**< 设置获取系统时间戳 */ +#define LOG_TIME_STAMP SHELL_GET_TICK() /**< 设置获取系统时间戳 */ #ifndef LOG_TAG #define LOG_TAG __FUNCTION__ /**< 自定添加的TAG */ @@ -90,7 +90,7 @@ extern "C" { /** * @brief 日志级别定义 - * + * */ typedef enum { @@ -106,14 +106,14 @@ typedef enum /** * @brief log对象定义 - * + * */ typedef struct log_def { void (*write)(char *, short); /**< 写buffer */ char active; /**< 是否激活 */ LogLevel level; /**< 日志级别 */ -#if LOG_USING_LOCK == 1 +#if LOG_USING_LOCK == 1 int (*lock)(struct log_def *); /**< log 加锁 */ int (*unlock)(struct log_def *); /**< log 解锁 */ #endif /** LOG_USING_LOCK == 1 */ @@ -124,17 +124,25 @@ typedef struct log_def /** * @brief log打印(自动换行) - * + * * @param fmt 格式 * @param ... 参数 */ #define logPrintln(format, ...) \ logWrite(LOG_ALL_OBJ, LOG_NONE, format "\r\n", ##__VA_ARGS__) +/** + * @brief log打印(不换行),用于直接替换printf + * + * @param fmt 格式 + * @param ... 参数 + */ +#define logPrintf(format, ...) \ + logWrite(LOG_ALL_OBJ, LOG_NONE, format, ##__VA_ARGS__) /** * @brief 日志格式化输出 - * + * * @param text 消息文本 * @param level 日志级别 * @param fmt 格式 @@ -147,7 +155,7 @@ typedef struct log_def /** * @brief 错误log输出 - * + * * @param fmt 格式 * @param ... 参数 */ @@ -156,7 +164,7 @@ typedef struct log_def /** * @brief 警告log输出 - * + * * @param fmt 格式 * @param ... 参数 */ @@ -165,7 +173,7 @@ typedef struct log_def /** * @brief 信息log输出 - * + * * @param fmt 格式 * @param ... 参数 */ @@ -174,7 +182,7 @@ typedef struct log_def /** * @brief 调试log输出 - * + * * @param fmt 格式 * @param ... 参数 */ @@ -183,7 +191,7 @@ typedef struct log_def /** * @brief 冗余log输出 - * + * * @param fmt 格式 * @param ... 参数 */ @@ -192,7 +200,7 @@ typedef struct log_def /** * @brief 断言 - * + * * @param expr 表达式 * @param action 断言失败操作 */ @@ -204,7 +212,7 @@ typedef struct log_def /** * @brief 16进制输出到所有终端 - * + * * @param base 内存基址 * @param length 长度 */ diff --git a/extensions/shell_enhance/shell_cmd_group.h b/extensions/shell_enhance/shell_cmd_group.h index 5200681..3a02d9e 100644 --- a/extensions/shell_enhance/shell_cmd_group.h +++ b/extensions/shell_enhance/shell_cmd_group.h @@ -9,6 +9,7 @@ * */ #include "shell.h" +#include /** * @brief shell 命令组函数名 diff --git a/extensions/shell_enhance/shell_passthrough.c b/extensions/shell_enhance/shell_passthrough.c index fec78a5..bea3b80 100644 --- a/extensions/shell_enhance/shell_passthrough.c +++ b/extensions/shell_enhance/shell_passthrough.c @@ -55,7 +55,7 @@ unsigned int shellPassthrough(Shell *shell, const char *prompt, ShellPassthrough shellWriteString(shell, "\r\n"); shell->parser.length = 0; shell->parser.cursor = 0; - return -1; + return (unsigned int)(-1); } else { diff --git a/extensions/shell_enhance/shell_passthrough.h b/extensions/shell_enhance/shell_passthrough.h index f257cf2..1ee606d 100644 --- a/extensions/shell_enhance/shell_passthrough.h +++ b/extensions/shell_enhance/shell_passthrough.h @@ -59,4 +59,4 @@ typedef int (*ShellPassthrough)(char *data, unsigned short len); unsigned int shellPassthrough(Shell *shell, const char *prompt, ShellPassthrough handler, int argc, char *argv[]); -#endif \ No newline at end of file +#endif