Compare commits

...

8 Commits

Author SHA1 Message Date
Lamdonn
bf21a64ab2 Add the coroutine readme
Add feature to bind CoTask and CoTimer to the specified CoScheduler
Improve functional safety
TODO:
1. Compatibility with coroutine API highly intensive IO, sockets, etc
2. Fix running fail on arm64
2025-11-20 00:58:03 +08:00
Lamdonn
f2b0a31bb3 1. Fix test_floatl.c compile fail issue
2. Update muti-core compile run.sh
2025-11-19 22:31:52 +08:00
Lamdonn
49025692ca Add the initial version coroutine module
TODO:
1. Add feature to bind CoTask and CoTimer to the specified CoScheduler
2. Compatibility with coroutine API highly intensive IO, sockets, etc
3. Improve functional safety
2025-11-11 22:42:25 +08:00
Lamdonn
70d1741bab 1. Fix set and map iterate looping error issue
2. Fix the list pop back macro definition
2025-10-10 20:25:47 +08:00
Lamdonn
a15641c42e Add the initial version yaml parser 2025-05-10 21:28:42 +08:00
Lamdonn
cc3d350ecf Add readme of slup and cant modules 2025-03-18 23:25:00 +08:00
Lamdonn
74119f6c8a Add floatl_to_d() function 2025-03-18 21:53:10 +08:00
Lamdonn
c97a43dd6f Add the source code of the initial version slup and cant 2025-03-18 21:39:48 +08:00
38 changed files with 16575 additions and 43 deletions

View File

@ -2,7 +2,7 @@
![logo](/image/logo.png) ![logo](/image/logo.png)
[![Version](https://img.shields.io/badge/version-0.3.2-blue.svg)](https://gitee.com/Lamdonn/varch) [![Version](https://img.shields.io/badge/version-0.3.5-blue.svg)](https://gitee.com/Lamdonn/varch)
[![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE) [![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE)
[![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com) [![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com)
![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg) ![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg)
@ -11,14 +11,14 @@
[中文版](README.md) [中文版](README.md)
varch (we architecture, meaning our framework library) is a commonly used code module library for embedded C language, including algorithm libraries, data structure (container) libraries, parser libraries, independent C language std libraries, tool libraries, and more. varch (we architecture, meaning our framework library) is a commonly used code module library for embedded C language, including the commonly used embedded algorithm library (search, check, filter, hash, sort, etc.), data structure/container library (general queue, stack, heap, list, dynamic array, string, set, dictionary, map, graph, etc.), parser library (csv, ini, json, xml, etc.), mathematical library (large number operation, mathematical operation), C language coroutine library, independent C language st d library, utility library, etc.
It has the characteristics of **simplicity, universality, and efficiency**, with the aim of **learning** and **using it immediately** in development, improving development efficiency and code reliability and stability. It has the characteristics of **simplicity, universality, and efficiency**, with the aim of **learning** and **using it immediately** in development, improving development efficiency and code reliability and stability.
## Content ## Content
| module | version | usage | path | describe | | module | version | usage | path | describe |
|:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------| |:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------|
| overall | 00.03.02 | [link](README.en.md) | [path](./) | Overall | overall | 00.03.05 | [link](README.en.md) | [path](./) | Overall
| init | 01.00.00 | [link](/doc/init.en.md) | [path](./source/00_application) | Initialize export module | init | 01.00.00 | [link](/doc/init.en.md) | [path](./source/00_application) | Initialize export module
| console | 01.00.01 | [link](/doc/console.en.md) | [path](./source/00_application/console) | Console command input, combined with the 'command' module, parsing commands entered in the console | console | 01.00.01 | [link](/doc/console.en.md) | [path](./source/00_application/console) | Console command input, combined with the 'command' module, parsing commands entered in the console
| arg | 01.00.00 | [link](/doc/arg.en.md) | [path](./source/01_general) | Indefinite parameters, obtain the number of indefinite parameters and specified parameters | arg | 01.00.00 | [link](/doc/arg.en.md) | [path](./source/01_general) | Indefinite parameters, obtain the number of indefinite parameters and specified parameters
@ -34,7 +34,6 @@ It has the characteristics of **simplicity, universality, and efficiency**, with
| tool | 01.01.00 | [link](/doc/tool.en.md) | [path](./source/01_general) | General tools code | tool | 01.01.00 | [link](/doc/tool.en.md) | [path](./source/01_general) | General tools code
| valloc | 01.00.00 | [link](/doc/valloc.en.md) | [path](./source/01_general) | Dynamic memory usage testing tool | valloc | 01.00.00 | [link](/doc/valloc.en.md) | [path](./source/01_general) | Dynamic memory usage testing tool
| vlog | 01.01.00 | [link](/doc/vlog.en.md) | [path](./source/01_general) | Log output module | vlog | 01.01.00 | [link](/doc/vlog.en.md) | [path](./source/01_general) | Log output module
| intl | 01.00.00 | [link](/doc/intl.en.md) | [path](./source/01_general) | Large integer arithmetic module
| date | 01.00.00 | [link](/doc/date.en.md) | [path](./source/01_general) | Date calculation module, calculating date differences, printing calendars, etc | date | 01.00.00 | [link](/doc/date.en.md) | [path](./source/01_general) | Date calculation module, calculating date differences, printing calendars, etc
| vctype | 01.00.00 | [link](/doc/vctype.en.md) | [path](./source/02_vstd) | Similar to the C standard library ctype | vctype | 01.00.00 | [link](/doc/vctype.en.md) | [path](./source/02_vstd) | Similar to the C standard library ctype
| vmath | 01.00.00 | [link](/doc/vmath.en.md) | [path](./source/02_vstd) | Similar to the C standard library math | vmath | 01.00.00 | [link](/doc/vmath.en.md) | [path](./source/02_vstd) | Similar to the C standard library math
@ -46,13 +45,13 @@ It has the characteristics of **simplicity, universality, and efficiency**, with
| queue | 01.00.00 | [link](/doc/queue.en.md) | [path](./source/03_container) | Universal queue container | queue | 01.00.00 | [link](/doc/queue.en.md) | [path](./source/03_container) | Universal queue container
| stack | 01.00.00 | [link](/doc/stack.en.md) | [path](./source/03_container) | Universal stack container | stack | 01.00.00 | [link](/doc/stack.en.md) | [path](./source/03_container) | Universal stack container
| deque | 01.00.00 | [link](/doc/deque.en.md) | [path](./source/03_container) | Universal double-end queue container | deque | 01.00.00 | [link](/doc/deque.en.md) | [path](./source/03_container) | Universal double-end queue container
| list | 01.00.00 | [link](/doc/list.en.md) | [path](./source/03_container) | Universal list container, single-link and internal iteration | list | 01.00.01 | [link](/doc/list.en.md) | [path](./source/03_container) | Universal list container, single-link and internal iteration
| vector | 01.00.00 | [link](/doc/vector.en.md) | [path](./source/03_container) | Universal vector(array) container | vector | 01.00.00 | [link](/doc/vector.en.md) | [path](./source/03_container) | Universal vector(array) container
| str | 01.00.00 | [link](/doc/str.en.md) | [path](./source/03_container) | String class | str | 01.00.00 | [link](/doc/str.en.md) | [path](./source/03_container) | String class
| dict | 01.00.00 | [link](/doc/dict.en.md) | [path](./source/03_container) | Universal dictionarie container, implementation based on hash table | dict | 01.00.00 | [link](/doc/dict.en.md) | [path](./source/03_container) | Universal dictionarie container, implementation based on hash table
| heap | 01.00.00 | [link](/doc/heap.en.md) | [path](./source/03_container) | Universal heap container | heap | 01.00.00 | [link](/doc/heap.en.md) | [path](./source/03_container) | Universal heap container
| set | 01.00.00 | [link](/doc/set.en.md) | [path](./source/03_container) | Universal set container, implementation based on RB-tree | set | 01.00.01 | [link](/doc/set.en.md) | [path](./source/03_container) | Universal set container, implementation based on RB-tree
| map | 01.00.00 | [link](/doc/map.en.md) | [path](./source/03_container) | Universal map container, implementation based on RB-tree | map | 01.00.01 | [link](/doc/map.en.md) | [path](./source/03_container) | Universal map container, implementation based on RB-tree
| tree | 01.00.00 | [link](/doc/tree.en.md) | [path](./source/03_container) | Universal tree container | tree | 01.00.00 | [link](/doc/tree.en.md) | [path](./source/03_container) | Universal tree container
| graph | 01.00.00 | [link](/doc/graph.en.md) | [path](./source/03_container) | Universal graph container | graph | 01.00.00 | [link](/doc/graph.en.md) | [path](./source/03_container) | Universal graph container
| check | 01.00.00 | [link](/doc/check.en.md) | [path](./source/04_algorithm) | Verification algorithm, sum check, parity check, XOR check, LRC check | check | 01.00.00 | [link](/doc/check.en.md) | [path](./source/04_algorithm) | Verification algorithm, sum check, parity check, XOR check, LRC check
@ -68,13 +67,17 @@ It has the characteristics of **simplicity, universality, and efficiency**, with
| json | 01.00.00 | [link](/doc/json.en.md) | [path](./source/05_parser) | JSON file parsing generator | json | 01.00.00 | [link](/doc/json.en.md) | [path](./source/05_parser) | JSON file parsing generator
| txls | 01.01.00 | [link](/doc/txls.en.md) | [path](./source/05_parser) | TXLS file parsing generator | txls | 01.01.00 | [link](/doc/txls.en.md) | [path](./source/05_parser) | TXLS file parsing generator
| xml | 01.00.00 | [link](/doc/xml.en.md) | [path](./source/05_parser) | XML file parsing generator | xml | 01.00.00 | [link](/doc/xml.en.md) | [path](./source/05_parser) | XML file parsing generator
| yaml | 00.01.00 | [link](/doc/yaml.en.md) | [path](./source/05_parser) | YAML file parsing generator
| ramt | 01.00.00 | [link](/doc/ramt.en.md) | [path](./source/06_performance) | RAM test module | ramt | 01.00.00 | [link](/doc/ramt.en.md) | [path](./source/06_performance) | RAM test module
| romt | 01.00.00 | [link](/doc/romt.en.md) | [path](./source/06_performance) | ROM test module | romt | 01.00.00 | [link](/doc/romt.en.md) | [path](./source/06_performance) | ROM test module
| cant | 00.01.00 | [link](/doc/cant.en.md) | [path](./source/06_performance) | CAN test module
| slup | 00.01.00 | [link](/doc/slup.en.md) | [path](./source/06_performance) | serial link universal protocol
| cpul | 01.00.00 | [link](/doc/cpul.en.md) | [path](./source/06_performance) | CPU load test module, including obtaining and increasing load | cpul | 01.00.00 | [link](/doc/cpul.en.md) | [path](./source/06_performance) | CPU load test module, including obtaining and increasing load
| unitt | 01.00.00 | [link](/doc/unitt.en.md) | [path](./source/06_performance) | Simple unit test module | unitt | 01.00.00 | [link](/doc/unitt.en.md) | [path](./source/06_performance) | Simple unit test module
| intl | 01.01.00 | [link](/doc/intl.en.md) | [path](./source/07_math) | Large integer arithmetic module | intl | 01.01.00 | [link](/doc/intl.en.md) | [path](./source/07_math) | Large integer arithmetic module
| floatl | 01.01.00 | [link](/doc/floatl.en.md) | [path](./source/07_math) | Large floating-point arithmetic module | floatl | 01.01.01 | [link](/doc/floatl.en.md) | [path](./source/07_math) | Large floating-point arithmetic module
| flmath | 01.00.00 | [link](/doc/flmath.en.md) | [path](./source/07_math) | Large floating-point arithmetic math module | flmath | 01.00.00 | [link](/doc/flmath.en.md) | [path](./source/07_math) | Large floating-point arithmetic math module
| coroutine | 00.02.00 | [link](/doc/coroutine.en.md) | [path](./source/08_coroutine) | Coroutine module
## Usage ## Usage

View File

@ -2,7 +2,7 @@
![logo](/image/logo.png) ![logo](/image/logo.png)
[![Version](https://img.shields.io/badge/version-0.3.2-blue.svg)](https://gitee.com/Lamdonn/varch) [![Version](https://img.shields.io/badge/version-0.3.5-blue.svg)](https://gitee.com/Lamdonn/varch)
[![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE) [![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE)
[![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com) [![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com)
![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg) ![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg)
@ -11,14 +11,14 @@
[English version](README.en.md) [English version](README.en.md)
varchwe-architecture意为我们的框架库是嵌入式C语言常用代码模块库包含了嵌入式中常用的算法库, 数据结构(容器)库, 解析器库, 独立C语言std库, 工具库等等。 varchwe-architecture意为我们的框架库是嵌入式C语言常用代码模块库包含了嵌入式中常用的算法库(查找、校验、过滤、哈希、排序等)、数据结构/容器库通用队列、栈、堆、列表、动态数组、字符串、集合、字典、映射、图等、解析器库csv、ini、json、xml等、数学库大数运算、数学运算、C语言协程库、独立C语言std库、工具库等等。
具有**简单, 通用, 高效**的特点,目的为了**学习**以及在开发中**拿来就用**,提高开发效率以及代码可靠稳定性。 具有**简单, 通用, 高效**的特点,目的为了**学习**以及在开发中**拿来就用**,提高开发效率以及代码可靠稳定性。
## 内容 ## 内容
| module | version | usage | path | describe | | module | version | usage | path | describe |
|:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------| |:-------------|:---------|:-----------------------------|:------------------------------------------|:--------------------------------------|
| overall | 00.03.02 | [link](README.md) | [path](./) | 整体 | overall | 00.03.05 | [link](README.md) | [path](./) | 整体
| init | 01.00.00 | [link](/doc/init.md) | [path](./source/00_application) | 初始化导出模块 | init | 01.00.00 | [link](/doc/init.md) | [path](./source/00_application) | 初始化导出模块
| console | 01.00.01 | [link](/doc/console.md) | [path](./source/00_application/console) | 控制台命令输入,结合 `command` 模块,解析在控制台中输入的命令 | console | 01.00.01 | [link](/doc/console.md) | [path](./source/00_application/console) | 控制台命令输入,结合 `command` 模块,解析在控制台中输入的命令
| arg | 01.00.00 | [link](/doc/arg.md) | [path](./source/01_general) | 不定参数,获取不定参数和指定参数的个数 | arg | 01.00.00 | [link](/doc/arg.md) | [path](./source/01_general) | 不定参数,获取不定参数和指定参数的个数
@ -34,7 +34,6 @@ varchwe-architecture意为我们的框架库是嵌入式C语言常用
| tool | 01.01.00 | [link](/doc/tool.md) | [path](./source/01_general) | 通用工具代码 | tool | 01.01.00 | [link](/doc/tool.md) | [path](./source/01_general) | 通用工具代码
| valloc | 01.00.00 | [link](/doc/valloc.md) | [path](./source/01_general) | 动态内存使用测试工具 | valloc | 01.00.00 | [link](/doc/valloc.md) | [path](./source/01_general) | 动态内存使用测试工具
| vlog | 01.01.00 | [link](/doc/vlog.md) | [path](./source/01_general) | 日志输出模块 | vlog | 01.01.00 | [link](/doc/vlog.md) | [path](./source/01_general) | 日志输出模块
| intl | 01.00.00 | [link](/doc/intl.md) | [path](./source/01_general) | 大型整数运算模块
| date | 01.00.00 | [link](/doc/date.md) | [path](./source/01_general) | 日期计算模块,计算日期差、打印日历等 | date | 01.00.00 | [link](/doc/date.md) | [path](./source/01_general) | 日期计算模块,计算日期差、打印日历等
| vctype | 01.00.00 | [link](/doc/vctype.md) | [path](./source/02_vstd) | 类似于C标准库ctype | vctype | 01.00.00 | [link](/doc/vctype.md) | [path](./source/02_vstd) | 类似于C标准库ctype
| vmath | 01.00.00 | [link](/doc/vmath.md) | [path](./source/02_vstd) | 类似于C标准库math | vmath | 01.00.00 | [link](/doc/vmath.md) | [path](./source/02_vstd) | 类似于C标准库math
@ -46,13 +45,13 @@ varchwe-architecture意为我们的框架库是嵌入式C语言常用
| queue | 01.00.00 | [link](/doc/queue.md) | [path](./source/03_container) | 通用队列容器 | queue | 01.00.00 | [link](/doc/queue.md) | [path](./source/03_container) | 通用队列容器
| stack | 01.00.00 | [link](/doc/stack.md) | [path](./source/03_container) | 通用栈式容器 | stack | 01.00.00 | [link](/doc/stack.md) | [path](./source/03_container) | 通用栈式容器
| deque | 01.00.00 | [link](/doc/deque.md) | [path](./source/03_container) | 通用双端队列容器 | deque | 01.00.00 | [link](/doc/deque.md) | [path](./source/03_container) | 通用双端队列容器
| list | 01.00.00 | [link](/doc/list.md) | [path](./source/03_container) | 通用列表容器,单链接和支持内部迭代器 | list | 01.00.01 | [link](/doc/list.md) | [path](./source/03_container) | 通用列表容器,单链接和支持内部迭代器
| vector | 01.00.00 | [link](/doc/vector.md) | [path](./source/03_container) | 通用向量(数组)容器 | vector | 01.00.00 | [link](/doc/vector.md) | [path](./source/03_container) | 通用向量(数组)容器
| str | 01.00.00 | [link](/doc/str.md) | [path](./source/03_container) | 字符串类 | str | 01.00.00 | [link](/doc/str.md) | [path](./source/03_container) | 字符串类
| dict | 01.00.00 | [link](/doc/dict.md) | [path](./source/03_container) | 通用字典容器,基于哈希表实现 | dict | 01.00.00 | [link](/doc/dict.md) | [path](./source/03_container) | 通用字典容器,基于哈希表实现
| heap | 01.00.00 | [link](/doc/heap.md) | [path](./source/03_container) | 通用堆容器 | heap | 01.00.00 | [link](/doc/heap.md) | [path](./source/03_container) | 通用堆容器
| set | 01.00.00 | [link](/doc/set.md) | [path](./source/03_container) | 通用集合容器基于RB-tree实现 | set | 01.00.01 | [link](/doc/set.md) | [path](./source/03_container) | 通用集合容器基于RB-tree实现
| map | 01.00.00 | [link](/doc/map.md) | [path](./source/03_container) | 通用映射容器基于RB-tree实现 | map | 01.00.01 | [link](/doc/map.md) | [path](./source/03_container) | 通用映射容器基于RB-tree实现
| tree | 01.00.00 | [link](/doc/tree.md) | [path](./source/03_container) | 通用树容器 | tree | 01.00.00 | [link](/doc/tree.md) | [path](./source/03_container) | 通用树容器
| graph | 01.00.00 | [link](/doc/graph.md) | [path](./source/03_container) | 通用图容器 | graph | 01.00.00 | [link](/doc/graph.md) | [path](./source/03_container) | 通用图容器
| check | 01.00.00 | [link](/doc/check.md) | [path](./source/04_algorithm) | 校验算法求和校验奇偶校验异或校验LRC校验 | check | 01.00.00 | [link](/doc/check.md) | [path](./source/04_algorithm) | 校验算法求和校验奇偶校验异或校验LRC校验
@ -68,13 +67,17 @@ varchwe-architecture意为我们的框架库是嵌入式C语言常用
| json | 01.00.00 | [link](/doc/json.md) | [path](./source/05_parser) | JSON文件解析生成器 | json | 01.00.00 | [link](/doc/json.md) | [path](./source/05_parser) | JSON文件解析生成器
| txls | 01.01.00 | [link](/doc/txls.md) | [path](./source/05_parser) | TXLS文件解析生成器 | txls | 01.01.00 | [link](/doc/txls.md) | [path](./source/05_parser) | TXLS文件解析生成器
| xml | 01.00.00 | [link](/doc/xml.md) | [path](./source/05_parser) | XML文件解析生成器 | xml | 01.00.00 | [link](/doc/xml.md) | [path](./source/05_parser) | XML文件解析生成器
| yaml | 00.01.00 | [link](/doc/yaml.md) | [path](./source/05_parser) | YAML文件解析生成器
| ramt | 01.00.00 | [link](/doc/ramt.md) | [path](./source/06_performance) | RAM测试模块 | ramt | 01.00.00 | [link](/doc/ramt.md) | [path](./source/06_performance) | RAM测试模块
| romt | 01.00.00 | [link](/doc/romt.md) | [path](./source/06_performance) | ROM测试模块 | romt | 01.00.00 | [link](/doc/romt.md) | [path](./source/06_performance) | ROM测试模块
| cant | 00.01.00 | [link](/doc/cant.md) | [path](./source/06_performance) | CAN测试模块
| slup | 00.01.00 | [link](/doc/slup.md) | [path](./source/06_performance) | 串口连接通用协议模块serial link universal protocol
| cpul | 01.00.00 | [link](/doc/cpul.md) | [path](./source/06_performance) | CPU负载测试模块包含获取和增加负载 | cpul | 01.00.00 | [link](/doc/cpul.md) | [path](./source/06_performance) | CPU负载测试模块包含获取和增加负载
| unitt | 01.00.00 | [link](/doc/unitt.md) | [path](./source/06_performance) | 简易的单元测试模块 | unitt | 01.00.00 | [link](/doc/unitt.md) | [path](./source/06_performance) | 简易的单元测试模块
| intl | 01.01.00 | [link](/doc/intl.md) | [path](./source/07_math) | 大型整数运算模块 | intl | 01.01.00 | [link](/doc/intl.md) | [path](./source/07_math) | 大型整数运算模块
| floatl | 01.01.00 | [link](/doc/floatl.md) | [path](./source/07_math) | 大型浮点数运算模块 | floatl | 01.01.01 | [link](/doc/floatl.md) | [path](./source/07_math) | 大型浮点数运算模块
| flmath | 01.00.00 | [link](/doc/flmath.md) | [path](./source/07_math) | 大型浮点数数学运算模块 | flmath | 01.00.00 | [link](/doc/flmath.md) | [path](./source/07_math) | 大型浮点数数学运算模块
| coroutine | 00.02.00 | [link](/doc/coroutine.md) | [path](./source/08_coroutine) | 协程模块
## 使用说明 ## 使用说明

192
doc/cant.en.md Normal file
View File

@ -0,0 +1,192 @@
## Introduction
`cant` is a simple CAN (Controller Area Network) device testing module for the C language. It conducts tests on the CAN bus from the application layer perspective, enabling the monitoring of data error frames and frame losses. Additionally, it can simulate the CAN bus load to ensure stability under high - voltage bus conditions.
## Interfaces
### Type Definitions
#### `cant_transfer_t` Function Pointer Type
```c
typedef int (*cant_transfer_t)(uint32_t canid, uint8_t *data, uint16_t length);
```
**Type Description**: This is a function pointer type related to CAN transmission, which can be used for CAN data sending or receiving functions.
**Parameter Introduction**:
- `canid`: The identifier of the CAN message.
- `data`: A pointer to the data to be sent or received.
- `length`: The length of the data.
**Return Value**: The function returns an `int` value, and its specific meaning is determined by the actual function used.
### Structure Definitions
#### `CANT` Structure
```c
typedef struct
{
struct
{
uint8_t channel;
uint8_t baundrate;
uint16_t period; // `cant_task()` call period
cant_transfer_t transmit; // can transmit the drive function
cant_transfer_t receive; // can receive hook functions
} config;
uint16_t busload;
uint32_t periodbits;
uint32_t timestamp;
struct
{
uint32_t canid;
float compression;
float rate;
uint16_t tarload;
uint32_t gapbase;
uint32_t gapcount;
uint32_t curcount;
uint8_t data[64];
} dummy;
struct
{
uint32_t canid;
uint32_t errcount;
uint32_t curcount;
uint32_t vercount;
uint32_t lstcount;
} verify;
} CANT;
```
**Structure Description**: This structure is used to represent a CAN device, including the configuration information, bus load, timestamp, dummy data, and verification data of the CAN device.
**Sub - structure and Member Introduction**:
- **`config` Sub - structure**: Contains the configuration information of the CAN device.
- `channel`: The channel number of the CAN bus.
- `baundrate`: The baud rate of the CAN bus.
- `period`: The call period of the `cant_task()` function.
- `transmit`: A pointer to the CAN data sending function.
- `receive`: A pointer to the CAN data receiving function.
- **`busload`**: The load value of the CAN bus.
- **`periodbits`**: Bit information related to the period.
- **`timestamp`**: The timestamp.
- **`dummy` Sub - structure**: Contains the dummy data of the CAN device.
- `canid`: The identifier of the CAN message.
- `compression`: The compression factor, of type `float`.
- `rate`: The rate, of type `float`.
- `tarload`: The target load.
- `gapbase`: The gap base value.
- `gapcount`: The gap count.
- `curcount`: The current count.
- `data`: The data array, with a size of 64 bytes.
- **`verify` Sub - structure**: Contains the verification data of the CAN device.
- `canid`: The identifier of the CAN message.
- `errcount`: The error count.
- `curcount`: The current count.
- `vercount`: The verification count.
- `lstcount`: The last count.
### Functions
#### `cant_set_dummy_canid` Function
```c
int cant_set_dummy_canid(CANT *cant, uint32_t canid);
```
**Function Description**: Sets the CAN identifier of the dummy data of the CAN device.
**Parameter Introduction**:
- `cant`: A pointer to the `CANT` structure, representing the CAN device to be operated on.
- `canid`: The CAN identifier to be set.
**Return Value**:
- `CANT_E_OK`: The setting is successful.
- `CANT_E_INVALID`: The passed `cant` pointer is `NULL`.
- `CANT_E_ECANID`: The passed `canid` is 0.
#### `cant_set_verify_canid` Function
```c
int cant_set_verify_canid(CANT *cant, uint32_t canid);
```
**Function Description**: Sets the CAN identifier of the verification data of the CAN device.
**Parameter Introduction**:
- `cant`: A pointer to the `CANT` structure, representing the CAN device to be operated on.
- `canid`: The CAN identifier to be set.
**Return Value**:
- `CANT_E_OK`: The setting is successful.
- `CANT_E_INVALID`: The passed `cant` pointer is `NULL`.
- `CANT_E_ECANID`: The passed `canid` is 0.
#### `cant_set_busload` Function
```c
int cant_set_busload(CANT *cant, uint16_t load);
```
**Function Description**: Sets the bus load of the CAN device.
**Parameter Introduction**:
- `cant`: A pointer to the `CANT` structure, representing the CAN device to be operated on.
- `load`: The bus load value to be set.
**Return Value**:
- `CANT_E_OK`: The setting is successful.
- `CANT_E_INVALID`: The passed `cant` pointer is `NULL`.
#### `cant_get_busload` Function
```c
int cant_get_busload(CANT *cant, uint16_t *load);
```
**Function Description**: Obtains the bus load of the CAN device.
**Parameter Introduction**:
- `cant`: A pointer to the `CANT` structure, representing the CAN device to be operated on.
- `load`: A pointer to the variable used to store the bus load value.
**Return Value**:
- `CANT_E_OK`: The acquisition is successful.
- `CANT_E_INVALID`: The passed `cant` pointer is `NULL`.
- `CANT_E_LOAD`: The passed `load` pointer is `NULL`.
#### `cant_transmit` Function
```c
int cant_transmit(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length);
```
**Function Description**: Sends data through the CAN device.
**Parameter Introduction**:
- `cant`: A pointer to the `CANT` structure, representing the CAN device to be operated on.
- `canid`: The CAN identifier of the data to be sent.
- `data`: A pointer to the data to be sent.
- `length`: The length of the data to be sent.
**Return Value**:
- `CANT_E_OK`: The sending is successful.
- `CANT_E_INVALID`: The passed `cant` pointer is `NULL`.
- `CANT_E_DATA`: The passed `data` pointer is `NULL`.
- `CANT_E_TRANSMIT`: The `transmit` function pointer in the `cant` structure is `NULL`.
- `CANT_E_TRANSFAIL`: The actual sending function returns a non - zero value, indicating a sending failure.
#### `cant_receive` Function
```c
int cant_receive(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length);
```
**Function Description**: Receives data through the CAN device.
**Parameter Introduction**:
- `cant`: A pointer to the `CANT` structure, representing the CAN device to be operated on.
- `canid`: The CAN identifier of the data to be received.
- `data`: A pointer to the buffer used to store the received data.
- `length`: The length of the data to be received.
**Return Value**:
- `CANT_E_OK`: The reception is successful.
- `CANT_E_INVALID`: The passed `cant` pointer is `NULL`.
#### `cant_init` Function
```c
int cant_init(CANT *cant);
```
**Function Description**: Initializes the CAN device.
**Parameter Introduction**:
- `cant`: A pointer to the `CANT` structure, representing the CAN device to be initialized.
**Return Value**:
- `CANT_E_OK`: The initialization is successful.
- `CANT_E_INVALID`: The passed `cant` pointer is `NULL`.
#### `cant_task` Function
```c
int cant_task(CANT *cant);
```
**Function Description**: The main task function of the CAN device, used to handle the periodic tasks of the CAN device.
**Parameter Introduction**:
- `cant`: A pointer to the `CANT` structure, representing the CAN device to execute the task.
**Return Value**:
- `CANT_E_OK`: The task is executed successfully.
- `CANT_E_INVALID`: The passed `cant` pointer is `NULL`.

192
doc/cant.md Normal file
View File

@ -0,0 +1,192 @@
## 介绍
`cant` 是一个用于 C 语言的简单 CANController Area Network设备测试模块。从应用层角度对CAN总线进行测试可以监控数据错误帧、丢帧同时可以模拟CAN总线负载确保在总线高压状态下的稳定性。
## 接口
### 类型定义
#### `cant_transfer_t` 函数指针类型
```c
typedef int (*cant_transfer_t)(uint32_t canid, uint8_t *data, uint16_t length);
```
**类型说明**:这是与 CAN 传输相关的函数指针类型,可用于 CAN 数据的发送或接收函数。
**参数介绍**
- `canid`CAN 消息的标识符。
- `data`:指向要发送或接收的数据的指针。
- `length`:数据的长度。
**返回值**:函数返回一个 `int` 类型的值,具体含义由实际使用的函数决定。
### 结构体定义
#### `CANT` 结构体
```c
typedef struct
{
struct
{
uint8_t channel;
uint8_t baundrate;
uint16_t period; // `cant_task()` call period
cant_transfer_t transmit; // can transmit the drive function
cant_transfer_t receive; // can receive hook functions
} config;
uint16_t busload;
uint32_t periodbits;
uint32_t timestamp;
struct
{
uint32_t canid;
float compression;
float rate;
uint16_t tarload;
uint32_t gapbase;
uint32_t gapcount;
uint32_t curcount;
uint8_t data[64];
} dummy;
struct
{
uint32_t canid;
uint32_t errcount;
uint32_t curcount;
uint32_t vercount;
uint32_t lstcount;
} verify;
} CANT;
```
**结构体说明**:该结构体用于表示 CAN 设备,包含了 CAN 设备的配置信息、总线负载、时间戳、虚拟数据和验证数据等内容。
**子结构体及成员介绍**
- **`config` 子结构体**:包含 CAN 设备的配置信息。
- `channel`CAN 总线的通道号。
- `baundrate`CAN 总线的波特率。
- `period``cant_task()` 函数的调用周期。
- `transmit`:指向 CAN 数据发送函数的指针。
- `receive`:指向 CAN 数据接收函数的指针。
- **`busload`**CAN 总线的负载值。
- **`periodbits`**:与周期相关的位信息。
- **`timestamp`**:时间戳。
- **`dummy` 子结构体**:包含 CAN 设备的虚拟数据。
- `canid`CAN 消息的标识符。
- `compression`:压缩因子,类型为 `float`
- `rate`:速率,类型为 `float`
- `tarload`:目标负载。
- `gapbase`:间隙基值。
- `gapcount`:间隙计数。
- `curcount`:当前计数。
- `data`:数据数组,大小为 64 字节。
- **`verify` 子结构体**:包含 CAN 设备的验证数据。
- `canid`CAN 消息的标识符。
- `errcount`:错误计数。
- `curcount`:当前计数。
- `vercount`:验证计数。
- `lstcount`:上一次计数。
### 函数
#### `cant_set_dummy_canid` 函数
```c
int cant_set_dummy_canid(CANT *cant, uint32_t canid);
```
**函数说明**:设置 CAN 设备虚拟数据的 CAN 标识符。
**参数介绍**
- `cant`:指向 `CANT` 结构体的指针,代表要操作的 CAN 设备。
- `canid`:要设置的 CAN 标识符。
**返回值**
- `CANT_E_OK`:设置成功。
- `CANT_E_INVALID`:传入的 `cant` 指针为 `NULL`
- `CANT_E_ECANID`:传入的 `canid` 为 0。
#### `cant_set_verify_canid` 函数
```c
int cant_set_verify_canid(CANT *cant, uint32_t canid);
```
**函数说明**:设置 CAN 设备验证数据的 CAN 标识符。
**参数介绍**
- `cant`:指向 `CANT` 结构体的指针,代表要操作的 CAN 设备。
- `canid`:要设置的 CAN 标识符。
**返回值**
- `CANT_E_OK`:设置成功。
- `CANT_E_INVALID`:传入的 `cant` 指针为 `NULL`
- `CANT_E_ECANID`:传入的 `canid` 为 0。
#### `cant_set_busload` 函数
```c
int cant_set_busload(CANT *cant, uint16_t load);
```
**函数说明**:设置 CAN 设备的总线负载。
**参数介绍**
- `cant`:指向 `CANT` 结构体的指针,代表要操作的 CAN 设备。
- `load`:要设置的总线负载值。
**返回值**
- `CANT_E_OK`:设置成功。
- `CANT_E_INVALID`:传入的 `cant` 指针为 `NULL`
#### `cant_get_busload` 函数
```c
int cant_get_busload(CANT *cant, uint16_t *load);
```
**函数说明**:获取 CAN 设备的总线负载。
**参数介绍**
- `cant`:指向 `CANT` 结构体的指针,代表要操作的 CAN 设备。
- `load`:指向用于存储总线负载值的变量的指针。
**返回值**
- `CANT_E_OK`:获取成功。
- `CANT_E_INVALID`:传入的 `cant` 指针为 `NULL`
- `CANT_E_LOAD`:传入的 `load` 指针为 `NULL`
#### `cant_transmit` 函数
```c
int cant_transmit(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length);
```
**函数说明**:通过 CAN 设备发送数据。
**参数介绍**
- `cant`:指向 `CANT` 结构体的指针,代表要操作的 CAN 设备。
- `canid`:要发送数据的 CAN 标识符。
- `data`:指向要发送的数据的指针。
- `length`:要发送的数据的长度。
**返回值**
- `CANT_E_OK`:发送成功。
- `CANT_E_INVALID`:传入的 `cant` 指针为 `NULL`
- `CANT_E_DATA`:传入的 `data` 指针为 `NULL`
- `CANT_E_TRANSMIT``cant` 结构体中的 `transmit` 函数指针为 `NULL`
- `CANT_E_TRANSFAIL`:实际的发送函数返回非零值,表示发送失败。
#### `cant_receive` 函数
```c
int cant_receive(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length);
```
**函数说明**:通过 CAN 设备接收数据。
**参数介绍**
- `cant`:指向 `CANT` 结构体的指针,代表要操作的 CAN 设备。
- `canid`:要接收数据的 CAN 标识符。
- `data`:指向用于存储接收数据的缓冲区的指针。
- `length`:要接收的数据的长度。
**返回值**
- `CANT_E_OK`:接收成功。
- `CANT_E_INVALID`:传入的 `cant` 指针为 `NULL`
#### `cant_init` 函数
```c
int cant_init(CANT *cant);
```
**函数说明**:初始化 CAN 设备。
**参数介绍**
- `cant`:指向 `CANT` 结构体的指针,代表要初始化的 CAN 设备。
**返回值**
- `CANT_E_OK`:初始化成功。
- `CANT_E_INVALID`:传入的 `cant` 指针为 `NULL`
#### `cant_task` 函数
```c
int cant_task(CANT *cant);
```
**函数说明**CAN 设备的主任务函数,用于处理 CAN 设备的周期性任务。
**参数介绍**
- `cant`:指向 `CANT` 结构体的指针,代表要执行任务的 CAN 设备。
**返回值**
- `CANT_E_OK`:任务执行成功。
- `CANT_E_INVALID`:传入的 `cant` 指针为 `NULL`

653
doc/coroutine.en.md Normal file
View File

@ -0,0 +1,653 @@
# Table of Contents
- [Overview](#overview)
- [Core Features](#core-features)
- [Architecture Design](#architecture-design)
- [API Interface Details](#api-interface-details)
- [Configuration Options](#configuration-options)
- [Usage Guide](#usage-guide)
- [Advanced Features](#advanced-features)
- [Error Handling](#error-handling)
- [Performance Optimization](#performance-optimization)
- [Platform Support](#platform-support)
- [Application Scenarios](#application-scenarios)
## Overview
The coroutine module is a lightweight user-mode thread management mechanism that plays an important role in program development. It is mainly used in scenarios requiring efficient concurrency, such as resource-constrained embedded devices, network programming, asynchronous I/O, and real-time data processing.
Compared to traditional threads, it has lower resource consumption (no kernel-mode switching required) and faster context switching speed. Through the coroutine module, developers can achieve efficient task switching and concurrent execution, thereby improving program performance and response speed.
**coroutine** is a high-performance, lightweight C language coroutine library that uses an asymmetric stackful coroutine design. This library provides complete coroutine lifecycle management, supports multi-task scheduling, timer management, and event synchronization mechanisms. It offers concise APIs for creation, scheduling, and synchronization, and can be seamlessly integrated into various environments such as embedded systems and server programs. It is also relatively simple to port, requiring only compilation and linking of the module on the target platform (mainstream chip kernel architectures are already supported).
### Design Philosophy
- **Simple and Easy to Use**: Provides intuitive API interfaces
- **Easy to Port**: Requires few resource dependencies for porting
- **High Efficiency**: Low-overhead context switching
- **Stable and Reliable**: Error handling and resource management
## Core Features
### 1. Architecture Features
- **Asymmetric Stackful Coroutines**: Each coroutine has its own independent stack space
- **Multiple Scheduler Support**: Configurable with multiple independent coroutine schedulers
- **Hybrid Memory Management**: Supports both static pre-allocation and dynamic memory management
- **Cross-Platform Compatibility**: Supports various CPU architectures such as x86, x64, ARM, MIPS (not fully tested yet)
### 2. Coroutine State Management
#### Coroutine Scheduler State Definitions
#define COSCHEDULER_STATE_INTI (0) // Initialization state
#define COSCHEDULER_STATE_EXIT (1) // Exit state
#define COSCHEDULER_STATE_START (2) // Start state
#define COSCHEDULER_STATE_RUNNING (3) // Running state
#define COSCHEDULER_STATE_SCHEDULE (4) // Scheduling state
#### Coroutine Task State Definitions
#define COTASK_STATE_INTI (0) // Initialization state
#define COTASK_STATE_READY (1) // Ready state
#define COTASK_STATE_RUNNING (2) // Running state
#define COTASK_STATE_SUSPEND (3) // Suspended state
#define COTASK_STATE_DELETED (4) // Deleted state
### 3. Memory Management Configuration
#### Static Resource Limit Configuration
#define COROUTINE_SCHEDULER_NUMBER 1 // Number of schedulers
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // Maximum number of static tasks
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // Maximum number of static timers
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // Maximum number of static stacks
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // Default stack size
## Architecture Design
### 1. Overall Architecture
+----------------------------------------------------+
| Architecture |
+------------------------------------+---------------+
| | +---------+ |
| +--------+ +--------+ | | CoTimer | |
| | | | | | +---------+ |
| | | CoEvent | | CoEvent +---------+ |
| | CoTask |<=======>| CoTask |<=======>| CoTimer | |
| | | | | | +---------+ |
| | | | | | +---------+ |
| +--------+ +--------+ | | CoTimer | |
+------------------------------------+ +---------+ |
| CoScheduler |
+----------------------------------------------------+
### 2. Coroutine Context Switching Mechanism
- Uses setjmp/longjmp to achieve fast context saving and restoration
- Each coroutine maintains independent stack space and environment
- Supports coroutine suspension, resumption, and deletion operations
## API Interface Details
### 1. Scheduler Management Interface
#### CoScheduler_Init - Initialize Coroutine Scheduler
**Function Prototype:**
#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...)
**Function Description:**
Initializes the specified coroutine scheduler, setting the clock function and clock interval. This function must be executed before calling any other coroutine functions.
It wraps the int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara); function, providing more initialization parameter configuration.
**Parameter Details:**
- CoSchedulerId **[Required]**: Scheduler ID, range 0 to (COROUTINE_SCHEDULER_NUMBER-1)
- tick **[Required]**: Clock function pointer, used to get the current timestamp
- tickInterval **[Required]**: Clock interval, representing the number of nanoseconds corresponding to each tick
- lock **[Optional]**: Lock function pointer, used for resource protection in multi-threaded environments
- unlock **[Optional]**: Unlock function pointer, used for resource release in multi-threaded environments
- malloc **[Optional]**: Memory allocation function pointer
- free **[Optional]**: Memory release function pointer
**Return Value:**
- COROUTINE_E_OK (0): Initialization successful
- COROUTINE_E_INVALID_PARAMETER (-1): Invalid parameter
- COROUTINE_E_INVALID_TICK (-2): Invalid clock function
- COROUTINE_E_INVALID_TICK_INTERVAL (-3): Invalid clock interval
- COROUTINE_E_INVALID_LOCK (-4): Invalid lock function
- COROUTINE_E_INVALID_MHOOK (-5): Invalid memory hook function
**Usage Example:**
// Basic usage: use microsecond clock, 1000us interval
CoScheduler_Init(0, GetTimerUsec, 1000);
// Multi-thread support: with lock functions
CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
// Complete configuration: with memory management functions
CoScheduler_Init(0, GetTimerUsec, 1000,
.lock=thread_lock,
.unlock=thread_unlock,
.malloc=malloc,
.free=free
);
#### CoScheduler_Start - Start Coroutine Scheduler
**Function Prototype:**
int CoScheduler_Start(uint32_t CoSchedulerId);
**Function Description:**
Starts the specified coroutine scheduler and begins coroutine task scheduling.
**Parameters:**
- CoSchedulerId: Scheduler ID
**Return Value:**
- 0: Start successful
- Error code: Start failed
**Usage Process:**
// Complete scheduler startup process
CoScheduler_Init(0, GetTimerUsec, 1000);
testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
CoScheduler_Start(0);
#### CoScheduler_Exit - Exit Coroutine Scheduler
**Function Prototype:**
int CoScheduler_Exit(uint32_t CoSchedulerId);
**Function Description:**
Exits the specified coroutine scheduler, stops all coroutine tasks, and cleans up resources.
**Parameters:**
- CoSchedulerId: Scheduler ID
**Return Value:**
- COROUTINE_E_OK: Exit successful
### 2. Coroutine Task Management Interface
#### CoTask_Create - Create Coroutine Task
**Function Prototype:**
#define CoTask_Create(entry, ...)
**Function Description:**
Creates a new coroutine task, allowing specification of stack space, parameters, and scheduler.
It wraps the CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara); function, providing more initialization parameter configuration.
**Parameter Details:**
- entry **[Required]**: Coroutine entry function pointer, type void (*)(void*)
- pStack **[Optional]**: Stack space address, automatically allocated if NULL
- stackSize **[Optional]**: Stack size, uses default value if not specified
- arg **[Optional]**: Parameter pointer passed to the coroutine
- schedulerId **[Optional]**: Scheduler ID, negative value means automatic allocation
**Return Value:**
- CoTask_t: Successfully created coroutine task handle
- NULL: Creation failed
**Usage Example:**
// Create coroutine with default parameters
CoTask_t testCoroutine = CoTask_Create(test);
// Create coroutine with specified stack space
CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
// Create coroutine with specified stack size
CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
// Create coroutine with parameters
int arg = 4096;
CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
#### CoTask_Delete - Delete Coroutine Task
**Function Prototype:**
int CoTask_Delete(CoTask_t CoTask);
**Function Description:**
Deletes the specified coroutine task and releases related resources.
**Parameters:**
- CoTask: Coroutine task handle to be deleted
**Return Value:**
- 0: Deletion successful
- COROUTINE_E_INVALID_PARAMETER: Invalid parameter
#### CoTask_Self - Get Current Coroutine Task
**Function Prototype:**
CoTask_t CoTask_Self(void);
**Function Description:**
Gets the handle of the currently running coroutine task.
**Return Value:**
- CoTask_t: Current coroutine task handle
- NULL: Not in coroutine context
#### CoTask_SchedulerId - Get Coroutine Scheduler ID
**Function Prototype:**
int CoTask_SchedulerId(void);
**Function Description:**
Gets the scheduler ID to which the current coroutine task belongs.
**Return Value:**
- Scheduler ID: 0 to (COROUTINE_SCHEDULER_NUMBER-1)
### 3. Coroutine Waiting Mechanism Interface
#### CoTask_Wait - Coroutine Wait Function
**Function Prototype:**
#define CoTask_Wait(...)
**Function Description:**
Causes the current coroutine task to wait for a specified event or timeout.
It wraps the uint32_t CoTask_WaitP(CoTaskWaitPara *pPara); function, providing more waiting parameter configuration.
**Parameter Details:**
- pEvent **[Optional]**: Event pointer to wait for
- ms **[Optional]**: Number of milliseconds to wait
- tick **[Optional]**: Number of ticks to wait
**Return Value:**
- uint32_t: Triggered event flag bits
**Usage Example:**
void *test(void *arg) {
while (1) {
// Give up scheduler access right, similar to yield
CoTask_Wait();
// Wait for 1000ms
CoTask_Wait(.ms=1000);
// Wait for event to occur
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
printf("event 0x01 triggered\n");
}
// Combined wait: event + timeout
evs = CoTask_Wait(.pEvent=&g_Event, .ms=5000);
}
}
Wrapping CoTask_Wait can form the following commonly used methods:
#define CoTask_WaitMs(m) CoTask_Wait(.ms=m)
#define CoTask_WaitTick(t) CoTask_Wait(.tick=t)
#define CoTask_WaitEvent(e) CoTask_Wait(.pEvent=e)
#define CoTask_WaitEventMs(e, m) CoTask_Wait(.pEvent=e, .ms=m)
#define CoTask_WaitEventTick(e, t) CoTask_Wait(.pEvent=e, .tick=t)
### 4. Timer Management Interface
#### CoTimer_Create - Create Coroutine Timer
**Function Prototype:**
#define CoTimer_Create(entry, ...)
**Function Description:**
Creates a periodic coroutine timer.
It wraps the CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara); function, providing more initialization parameter configuration.
**Parameter Details:**
- entry **[Required]**: Timer entry function
- ms **[Optional]**: Millisecond interval
- tick **[Optional]**: Tick interval
**Return Value:**
- CoTimer_t: Successfully created timer handle
- NULL: Creation failed
**Usage Example:**
// Create timer with 100ms interval
CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100);
#### CoTimer_Delete - Delete Coroutine Timer
**Function Prototype:**
void CoTimer_Delete(CoTimer_t Timer);
**Function Description:**
Deletes the specified coroutine timer.
**Parameters:**
- Timer: Timer handle to be deleted
#### CoTimer_Self - Get Current Timer
**Function Prototype:**
CoTimer_t CoTimer_Self(void);
**Function Description:**
Gets the handle of the currently running coroutine timer.
**Return Value:**
- CoTimer_t: Current timer handle
- NULL: Not in timer context
### 5. Event Synchronization Interface
#### CoEvent_Init - Initialize Event
**Function Prototype:**
void CoEvent_Init(CoEvent *pEvent);
**Function Description:**
Initializes a coroutine event, preparing it for inter-coroutine synchronization.
**Parameters:**
- pEvent: Event pointer
**Usage Example:**
CoEvent_Init(&g_Event);
This method is called at runtime, while static initialization can directly assign COEVENT_STATIC_VALUE.
CoEvent g_Event = COEVENT_STATIC_VALUE;
#### CoEvent_Notify - Notify Event
**Function Prototype:**
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
**Function Description:**
Notifies the specified event, waking up coroutine tasks waiting for that event.
**Parameter Details:**
- pEvent: Event pointer
- evs: Event flag bits, supports simultaneous notification of multiple events
**Usage Example:**
// Notify single event
CoEvent_Notify(&g_Event, 0x01);
// Notify multiple events simultaneously
CoEvent_Notify(&g_Event, 0x01 | 0x02 | 0x04);
### 6. Monitoring Statistics Interface
#### CoTask_StackMaxUsed - Get Coroutine Stack Maximum Usage
**Function Prototype:**
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
**Function Description:**
Gets the maximum stack usage of the specified coroutine task.
**Parameters:**
- CoTask: Coroutine task handle
**Return Value:**
- size_t: Maximum stack usage (bytes)
#### CoTask_StackCurUsed - Get Coroutine Stack Current Usage
**Function Prototype:**
size_t CoTask_StackCurUsed(CoTask_t CoTask);
**Function Description:**
Gets the current stack usage of the specified coroutine task.
**Parameters:**
- CoTask: Coroutine task handle
**Return Value:**
- size_t: Current stack usage (bytes)
#### CoScheduler_CurLoad - Get Current Load
**Function Prototype:**
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
**Function Description:**
Gets the current load of the specified scheduler.
**Return Value:**
- uint16_t: Current load percentage (0-10000, representing 0.00%-100.00%)
#### CoScheduler_MaxLoad - Get Maximum Historical Load
**Function Prototype:**
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
**Function Description:**
Gets the historical maximum load of the specified scheduler.
**Return Value:**
- uint16_t: Maximum load percentage
### 7. Statistical Counting Interface
#### CoScheduler_TaskCount - Get Coroutine Task Count
**Function Prototype:**
int CoScheduler_TaskCount(uint32_t CoSchedulerId);
**Function Description:**
Gets the number of coroutine tasks in the specified scheduler.
**Parameters:**
- CoSchedulerId: Scheduler ID
**Return Value:**
- int: Number of coroutine tasks
#### CoScheduler_TimerCount - Get Timer Count
**Function Prototype:**
int CoScheduler_TimerCount(uint32_t CoSchedulerId);
**Function Description:**
Gets the number of coroutine timers in the specified scheduler.
**Parameters:**
- CoSchedulerId: Scheduler ID
**Return Value:**
- int: Number of timers
## Configuration Options
### Compile-time Configuration (coroutine_cfg.h)
**Basic Configuration:**
// Scheduler configuration
#define COROUTINE_SCHEDULER_NUMBER 1 // Number of schedulers
// Static resource limits
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // Maximum number of static tasks
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // Maximum number of static timers
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // Maximum number of static stacks
// Stack configuration
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // Default stack size
**Feature Switches:**
// Stack usage calculation
#define COROUTINE_ENABLE_STACK_CALCULATE 1 // Enable stack usage calculation
// Load calculation
#define COROUTINE_ENABLE_LOADING_CALCULATE 1 // Enable load calculation
// Debug support
#define COROUTINE_ENABLE_DEBUG 1 // Enable debug function
## Usage Guide
### 1. Basic Usage Process
#### Initialization Phase
#include "coroutine.h"
// Define clock function
uint64_t GetTimerUsec(void) {
// Return microsecond-level timestamp
return ...;
}
int main(void) {
// Initialize scheduler 0, use microsecond clock, 1000us interval
CoScheduler_Init(0, GetTimerUsec, 1000);
}
#### Coroutine Definition
// Coroutine task function
void *test_task(void *arg) {
int *p_count = (int*)arg;
while (1) {
// Coroutine task logic
(*p_count)++;
// Voluntarily yield CPU
CoTask_Wait();
}
return NULL;
}
#### Creation and Startup
// Create coroutine task
int count = 0;
CoTask_t testCoroutine = CoTask_Create(test_task, .arg=&count);
}
### 2. Advanced Usage Patterns
#### Event-Driven Pattern
void *event_driven_task(void *arg) {
while (1) {
// Wait for event
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
// Handle event 0x01
handle_event_01();
}
if (evs & 0x02) {
// Handle event 0x02
handle_event_02();
}
}
}
#### Timer Pattern
void *timer_task(void *arg) {
while (1) {
// Execute every 100ms
CoTask_Wait(.ms=100);
// Timer logic
periodic_work();
}
}
## Advanced Features
### 1. Stack Usage Calculation
After enabling COROUTINE_ENABLE_STACK_CALCULATE, you can monitor coroutine stack usage:
// Get coroutine stack maximum usage
size_t maxUsed = CoTask_StackMaxUsed(CoTask);
// Get coroutine stack current usage
size_t curUsed = CoTask_StackCurUsed(CoTask);
}
### 2. Load Calculation
After enabling COROUTINE_ENABLE_LOADING_CALCULATE, you can calculate the scheduler's load:
// Get current load
uint16_t curLoad = CoScheduler_CurLoad(CoSchedulerId);
// Get maximum historical load
uint16_t maxLoad = CoScheduler_MaxLoad(CoSchedulerId);
}
### 3. Performance Monitoring
// Monitor coroutine scheduling performance
void monitor_scheduler_performance(void) {
uint16_t curLoad = CoScheduler_CurLoad(0);
uint16_t maxLoad = CoScheduler_MaxLoad(0);
if (curLoad > 8000) { // 80% load warning
printf("Warning: Scheduler load is high: %d.%02d%%\n",
curLoad/100, curLoad%100);
}
}
## Error Handling
### Error Code Definitions
#define COROUTINE_E_OK (0) // Success
#define COROUTINE_E_INVALID_PARAMETER (-1) // Invalid parameter
#define COROUTINE_E_INVALID_TICK (-2) // Invalid clock function
#define COROUTINE_E_INVALID_TICK_INTERVAL (-3) // Invalid clock interval
#define COROUTINE_E_INVALID_LOCK (-4) // Invalid lock function
#define COROUTINE_E_INVALID_MHOOK (-5) // Invalid memory hook function
## Performance Optimization
### 1. Context Switching Optimization
- Uses setjmp/longjmp instead of complete context saving
- Minimizes register save and restore operations
### 2. Memory Management Optimization
- Static pre-allocation reduces dynamic memory allocation
- Stack space reuse mechanism
## Platform Support
This library implements cross-platform stack pointer operations through inline assembly:
- **x86/x64**: Uses esp/rsp registers
- **ARM**: Uses sp register
- **MIPS**: Uses sp register
- **RISC-V**: Uses sp register
- **PowerPC**: Uses r1 register
### Platform-Specific Implementation
// x86 architecture implementation
#if defined(__i386__)
#define COROUTINE_GET_STACK_POINTER(p) \
__asm__ __volatile__("movl %%esp, %0" : "=r"(p))
}
## Application Scenarios
### 1. Embedded Systems
- Real-time task scheduling
- Low-power device management
- Sensor data processing
### 2. Network Programming
- Asynchronous I/O processing
- Connection pool management
- Protocol stack implementation
### 3. Game Development
- Game object updates
- AI behavior scheduling
- Animation system management
### 4. IoT Applications
- Device communication protocols
- Data processing pipelines
- Remote control interfaces
## Summary
The **coroutine** coroutine library is a powerful, high-performance C language coroutine solution. It provides:
1. **Complete coroutine lifecycle management**
2. **Efficient multi-task scheduling mechanism**
3. **Flexible timer and event management**
4. **Comprehensive error handling and monitoring functions**

734
doc/coroutine.md Normal file
View File

@ -0,0 +1,734 @@
## 目录
- [概述](#概述)
- [核心特性](#核心特性)
- [架构设计](#架构设计)
- [API接口详解](#api接口详解)
- [配置选项](#配置选项)
- [使用指南](#使用指南)
- [高级功能](#高级功能)
- [错误处理](#错误处理)
- [性能优化](#性能优化)
- [平台支持](#平台支持)
- [应用场景](#应用场景)
## 概述
协程模块是一种轻量级的用户态线程管理机制在程序开发中扮演着重要角色主要用于资源受限的嵌入式设备、网络编程、异步IO、实时数据处理等需要高效并发的场景。
相比传统线程具有更低的资源消耗(无需内核态切换)和更快的上下文切换速度,通过协程模块,开发者能够实现高效的任务切换和并发执行,从而提升程序的性能和响应速度。
**coroutine** 是一个高性能、轻量级的C语言协程库采用非对称有栈协程设计。该库提供了完整的协程生命周期管理支持多任务调度、定时器管理和事件同步机制提供简洁的创建、调度与同步API接口可无缝集成到嵌入式系统、服务器程序等多种环境中。并且移植起来也比较简单只需要在目标平台上编译并链接该模块即可已支持主流的芯片内核架构
### 设计理念
- **简单易用**提供直观的API接口
- **移植方便**:移植所需资源依赖少
- **高效性能**:低开销的上下文切换
- **稳定可靠**:错误处理和资源管理
## 核心特性
### 1. 架构特性
- **非对称有栈协程**:每个协程拥有独立的栈空间
- **多调度器支持**:可配置多个独立的协程调度器
- **混合内存管理**:支持静态预分配和动态内存管理
- **跨平台兼容**支持x86、x64、ARM、MIPS等多种CPU架构尚未完全测试完
### 2. 协程状态管理
#### 协程调度器状态定义
```c
#define COSCHEDULER_STATE_INTI (0) // 初始化状态
#define COSCHEDULER_STATE_EXIT (1) // 退出状态
#define COSCHEDULER_STATE_START (2) // 启动状态
#define COSCHEDULER_STATE_RUNNING (3) // 运行状态
#define COSCHEDULER_STATE_SCHEDULE (4) // 调度状态
```
#### 协程任务状态定义
```c
#define COTASK_STATE_INTI (0) // 初始化状态
#define COTASK_STATE_READY (1) // 就绪状态
#define COTASK_STATE_RUNNING (2) // 运行状态
#define COTASK_STATE_SUSPEND (3) // 挂起状态
#define COTASK_STATE_DELETED (4) // 删除状态
```
### 3. 内存管理配置
#### 静态资源限制配置
```c
#define COROUTINE_SCHEDULER_NUMBER 1 // 调度器数量
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // 静态任务最大数量
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // 静态定时器最大数量
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // 静态栈最大数量
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // 默认栈大小
```
## 架构设计
### 1. 整体架构
```
+----------------------------------------------------+
| Architecture |
+------------------------------------+---------------+
| | +---------+ |
| +--------+ +--------+ | | CoTimer | |
| | | | | | +---------+ |
| | | CoEvent | | CoEvent +---------+ |
| | CoTask |<=======>| CoTask |<=======>| CoTimer | |
| | | | | | +---------+ |
| | | | | | +---------+ |
| +--------+ +--------+ | | CoTimer | |
+------------------------------------+ +---------+ |
| CoScheduler |
+----------------------------------------------------+
```
### 2. 协程上下文切换机制
- 使用setjmp/longjmp实现快速上下文保存和恢复
- 每个协程维护独立的栈空间和环境
- 支持协程挂起、恢复和删除操作
## API接口详解
### 1. 调度器管理接口
#### CoScheduler_Init - 初始化协程调度器
**函数原型:**
```c
#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...)
```
**函数说明:**
初始化指定的协程调度器,设置时钟函数和时钟间隔。此函数必须在调用任何其他协程函数之前执行。
套用 `int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara);` 函数,提供更多的初始化参数配置。
**参数详解:**
- `CoSchedulerId` **[必需]**调度器ID范围0到(COROUTINE_SCHEDULER_NUMBER-1)
- `tick` **[必需]**:时钟函数指针,用于获取当前时间戳
- `tickInterval` **[必需]**时钟间隔表示每个tick对应的纳秒数
- `lock` **[可选]**:锁函数指针,用于多线程环境下的资源保护
- `unlock` **[可选]**:解锁函数指针,用于多线程环境下的资源释放
- `malloc` **[可选]**:内存分配函数指针
- `free` **[可选]**:内存释放函数指针
**返回值:**
- `COROUTINE_E_OK` (0):初始化成功
- `COROUTINE_E_INVALID_PARAMETER` (-1):无效参数
- `COROUTINE_E_INVALID_TICK` (-2):无效时钟函数
- `COROUTINE_E_INVALID_TICK_INTERVAL` (-3):无效时钟间隔
- `COROUTINE_E_INVALID_LOCK` (-4):无效锁函数
- `COROUTINE_E_INVALID_MHOOK` (-5):无效内存钩子函数
**使用示例:**
```c
// 基本使用使用微秒时钟1000us间隔
CoScheduler_Init(0, GetTimerUsec, 1000);
// 多线程支持:带锁函数
CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
// 完整配置:带内存管理函数
CoScheduler_Init(0, GetTimerUsec, 1000,
.lock=thread_lock,
.unlock=thread_unlock,
.malloc=malloc,
.free=free
);
```
#### CoScheduler_Start - 启动协程调度器
**函数原型:**
```c
int CoScheduler_Start(uint32_t CoSchedulerId);
```
**函数说明:**
启动指定的协程调度器,开始执行协程任务调度。
**参数:**
- `CoSchedulerId`调度器ID
**返回值:**
- 0启动成功
- 错误码:启动失败
**使用流程:**
```c
// 完整的调度器启动流程
CoScheduler_Init(0, GetTimerUsec, 1000);
testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
CoScheduler_Start(0);
```
#### CoScheduler_Exit - 退出协程调度器
**函数原型:**
```c
int CoScheduler_Exit(uint32_t CoSchedulerId);
```
**函数说明:**
退出指定的协程调度器,停止所有协程任务并清理资源。
**参数:**
- `CoSchedulerId`调度器ID
**返回值:**
- `COROUTINE_E_OK`:退出成功
### 2. 协程任务管理接口
#### CoTask_Create - 创建协程任务
**函数原型:**
```c
#define CoTask_Create(entry, ...)
```
**函数说明:**
创建一个新的协程任务,可以指定栈空间、参数和调度器。
套用 `CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara);` 函数,提供更多的初始化参数配置。
**参数详解:**
- `entry` **[必需]**:协程入口函数指针,类型为`void (*)(void*)`
- `pStack` **[可选]**栈空间地址如果为NULL则自动分配
- `stackSize` **[可选]**:栈大小,未指定时使用默认值
- `arg` **[可选]**:传递给协程的参数指针
- `schedulerId` **[可选]**调度器ID负值表示自动分配
**返回值:**
- `CoTask_t`:成功创建的协程任务句柄
- `NULL`:创建失败
**使用示例:**
```c
// 使用默认参数创建协程
CoTask_t testCoroutine = CoTask_Create(test);
// 指定栈空间创建协程
CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
// 指定栈大小创建协程
CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
// 带参数创建协程
int arg = 4096;
CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
```
#### CoTask_Delete - 删除协程任务
**函数原型:**
```c
int CoTask_Delete(CoTask_t CoTask);
```
**函数说明:**
删除指定的协程任务,释放相关资源。
**参数:**
- `CoTask`:要删除的协程任务句柄
**返回值:**
- 0删除成功
- `COROUTINE_E_INVALID_PARAMETER`:无效参数
#### CoTask_Self - 获取当前协程任务
**函数原型:**
```c
CoTask_t CoTask_Self(void);
```
**函数说明:**
获取当前正在运行的协程任务句柄。
**返回值:**
- `CoTask_t`:当前协程任务句柄
- `NULL`:不在协程上下文中
#### CoTask_SchedulerId - 获取协程调度器ID
**函数原型:**
```c
int CoTask_SchedulerId(void);
```
**函数说明:**
获取当前协程任务所属的调度器ID。
**返回值:**
- 调度器ID0到(COROUTINE_SCHEDULER_NUMBER-1)
### 3. 协程等待机制接口
#### CoTask_Wait - 协程等待函数
**函数原型:**
```c
#define CoTask_Wait(...)
```
**函数说明:**
使当前协程任务等待指定的事件或超时。
套用 `uint32_t CoTask_WaitP(CoTaskWaitPara *pPara);` 函数,提供更多的等待参数配置。
**参数详解:**
- `pEvent` **[可选]**:等待的事件指针
- `ms` **[可选]**:等待的毫秒数
- `tick` **[可选]**等待的tick数
**返回值:**
- `uint32_t`:触发的事件标志位
**使用示例:**
```c
void *test(void *arg) {
while (1) {
// 放弃调度器访问权类似yield
CoTask_Wait();
// 等待1000ms
CoTask_Wait(.ms=1000);
// 等待事件发生
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
printf("event 0x01 triggered\n");
}
// 组合等待:事件+超时
evs = CoTask_Wait(.pEvent=&g_Event, .ms=5000);
}
}
```
套用 `CoTask_Wait` 可以形成以下常用方法:
```c
#define CoTask_WaitMs(m) CoTask_Wait(.ms=m)
#define CoTask_WaitTick(t) CoTask_Wait(.tick=t)
#define CoTask_WaitEvent(e) CoTask_Wait(.pEvent=e)
#define CoTask_WaitEventMs(e, m) CoTask_Wait(.pEvent=e, .ms=m)
#define CoTask_WaitEventTick(e, t) CoTask_Wait(.pEvent=e, .tick=t)
```
### 4. 定时器管理接口
#### CoTimer_Create - 创建协程定时器
**函数原型:**
```c
#define CoTimer_Create(entry, ...)
```
**函数说明:**
创建一个周期性的协程定时器。
套用 `CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara);` 函数,提供更多的初始化参数配置。
**参数详解:**
- `entry` **[必需]**:定时器入口函数
- `ms` **[可选]**:毫秒间隔
- `tick` **[可选]**tick间隔
**返回值:**
- `CoTimer_t`:成功创建的定时器句柄
- `NULL`:创建失败
**使用示例:**
```c
// 创建100ms间隔的定时器
CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100);
```
#### CoTimer_Delete - 删除协程定时器
**函数原型:**
```c
void CoTimer_Delete(CoTimer_t Timer);
```
**函数说明:**
删除指定的协程定时器。
**参数:**
- `Timer`:要删除的定时器句柄
#### CoTimer_Self - 获取当前定时器
**函数原型:**
```c
CoTimer_t CoTimer_Self(void);
```
**函数说明:**
获取当前正在运行的协程定时器句柄。
**返回值:**
- `CoTimer_t`:当前定时器句柄
- `NULL`:不在定时器上下文中
### 5. 事件同步接口
#### CoEvent_Init - 初始化事件
**函数原型:**
```c
void CoEvent_Init(CoEvent *pEvent);
```
**函数说明:**
初始化协程事件,准备用于协程间同步。
**参数:**
- `pEvent`:事件指针
**使用示例:**
```c
CoEvent_Init(&g_Event);
```
此方法为运行时调用,静态初始化可以直接赋值为 `COEVENT_STATIC_VALUE`
```c
CoEvent g_Event = COEVENT_STATIC_VALUE;
```
#### CoEvent_Notify - 通知事件
**函数原型:**
```c
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
```
**函数说明:**
通知指定的事件,唤醒等待该事件的协程任务。
**参数详解:**
- `pEvent`:事件指针
- `evs`:事件标志位,支持多事件同时通知
**使用示例:**
```c
// 通知单个事件
CoEvent_Notify(&g_Event, 0x01);
// 同时通知多个事件
CoEvent_Notify(&g_Event, 0x01 | 0x02 | 0x04);
```
### 6. 监控统计接口
#### CoTask_StackMaxUsed - 获取协程栈最大使用量
**函数原型:**
```c
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
```
**函数说明:**
获取指定协程任务栈的最大使用量。
**参数:**
- `CoTask`:协程任务句柄
**返回值:**
- `size_t`:栈最大使用量(字节)
#### CoTask_StackCurUsed - 获取协程栈当前使用量
**函数原型:**
```c
size_t CoTask_StackCurUsed(CoTask_t CoTask);
```
**函数说明:**
获取指定协程任务栈的当前使用量。
**参数:**
- `CoTask`:协程任务句柄
**返回值:**
- `size_t`:栈当前使用量(字节)
#### CoScheduler_CurLoad - 获取当前负载
**函数原型:**
```c
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
```
**函数说明:**
获取指定调度器的当前负载。
**返回值:**
- `uint16_t`当前负载百分比0-10000表示0.00%-100.00%
#### CoScheduler_MaxLoad - 获取最大历史负载
**函数原型:**
```c
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
```
**函数说明:**
获取指定调度器的历史最大负载。
**返回值:**
- `uint16_t`:最大负载百分比
### 7. 统计计数接口
#### CoScheduler_TaskCount - 获取协程任务数量
**函数原型:**
```c
int CoScheduler_TaskCount(uint32_t CoSchedulerId);
```
**函数说明:**
获取指定调度器中的协程任务数量。
**参数:**
- `CoSchedulerId`调度器ID
**返回值:**
- `int`:协程任务数量
#### CoScheduler_TimerCount - 获取定时器数量
**函数原型:**
```c
int CoScheduler_TimerCount(uint32_t CoSchedulerId);
```
**函数说明:**
获取指定调度器中的协程定时器数量。
**参数:**
- `CoSchedulerId`调度器ID
**返回值:**
- `int`:定时器数量
## 配置选项
### 编译时配置 (coroutine_cfg.h)
**基本配置:**
```c
// 调度器配置
#define COROUTINE_SCHEDULER_NUMBER 1 // 调度器数量
// 静态资源限制
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // 静态任务最大数量
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // 静态定时器最大数量
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // 静态栈最大数量
// 栈配置
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // 默认栈大小
```
**功能开关:**
```c
// 栈使用量计算
#define COROUTINE_ENABLE_STACK_CALCULATE 1 // 启用栈使用量计算
// 负载计算
#define COROUTINE_ENABLE_LOADING_CALCULATE 1 // 启用负载计算
// 调试支持
#define COROUTINE_ENABLE_DEBUG 1 // 启用调试功能
```
## 使用指南
### 1. 基本使用流程
#### 初始化阶段
```c
#include "coroutine.h"
// 定义时钟函数
uint64_t GetTimerUsec(void) {
// 返回微秒级时间戳
return ...;
}
int main(void) {
// 初始化调度器0使用微秒时钟1000us间隔
CoScheduler_Init(0, GetTimerUsec, 1000);
```
#### 协程定义
```c
// 协程任务函数
void *test_task(void *arg) {
int *p_count = (int*)arg;
while (1) {
// 协程任务逻辑
(*p_count)++;
// 主动让出CPU
CoTask_Wait();
}
return NULL;
}
```
#### 创建和启动
```c
// 创建协程任务
int count = 0;
CoTask_t testCoroutine = CoTask_Create(test_task, .arg=&count);
```
### 2. 高级使用模式
#### 事件驱动模式
```c
void *event_driven_task(void *arg) {
while (1) {
// 等待事件
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
// 处理事件0x01
handle_event_01();
}
if (evs & 0x02) {
// 处理事件0x02
handle_event_02();
}
}
}
```
#### 定时器模式
```c
void *timer_task(void *arg) {
while (1) {
// 每100ms执行一次
CoTask_Wait(.ms=100);
// 定时器逻辑
periodic_work();
}
}
```
## 高级功能
### 1. 栈使用量计算
启用 `COROUTINE_ENABLE_STACK_CALCULATE` 后,可以监控协程栈的使用情况:
```c
// 获取协程栈最大使用量
size_t maxUsed = CoTask_StackMaxUsed(CoTask);
// 获取协程栈当前使用量
size_t curUsed = CoTask_StackCurUsed(CoTask);
```
### 2. 负载计算
启用 `COROUTINE_ENABLE_LOADING_CALCULATE` 后,可以计算调度器的负载:
```c
// 获取当前负载
uint16_t curLoad = CoScheduler_CurLoad(CoSchedulerId);
// 获取最大历史负载
uint16_t maxLoad = CoScheduler_MaxLoad(CoSchedulerId);
```
### 3. 性能监控
```c
// 监控协程调度性能
void monitor_scheduler_performance(void) {
uint16_t curLoad = CoScheduler_CurLoad(0);
uint16_t maxLoad = CoScheduler_MaxLoad(0);
if (curLoad > 8000) { // 80%负载警告
printf("Warning: Scheduler load is high: %d.%02d%%\n",
curLoad/100, curLoad%100);
}
```
## 错误处理
### 错误码定义
```c
#define COROUTINE_E_OK (0) // 成功
#define COROUTINE_E_INVALID_PARAMETER (-1) // 无效参数
#define COROUTINE_E_INVALID_TICK (-2) // 无效时钟函数
#define COROUTINE_E_INVALID_TICK_INTERVAL (-3) // 无效时钟间隔
#define COROUTINE_E_INVALID_LOCK (-4) // 无效锁函数
#define COROUTINE_E_INVALID_MHOOK (-5) // 无效内存钩子函数
```
## 性能优化
### 1. 上下文切换优化
- 使用setjmp/longjmp替代完整的上下文保存
- 最小化寄存器保存和恢复操作
### 2. 内存管理优化
- 静态预分配减少动态内存分配
- 栈空间复用机制
## 平台支持
该库通过内联汇编实现了跨平台的栈指针操作:
- **x86/x64**使用esp/rsp寄存器
- **ARM**使用sp寄存器
- **MIPS**使用sp寄存器
- **RISC-V**使用sp寄存器
- **PowerPC**使用r1寄存器
### 平台特定实现
```c
// x86架构实现
#if defined(__i386__)
#define COROUTINE_GET_STACK_POINTER(p) \
__asm__ __volatile__("movl %%esp, %0" : "=r"(p))
```
## 应用场景
### 1. 嵌入式系统
- 实时任务调度
- 低功耗设备管理
- 传感器数据处理
### 2. 网络编程
- 异步I/O处理
- 连接池管理
- 协议栈实现
### 3. 游戏开发
- 游戏对象更新
- AI行为调度
- 动画系统管理
### 4. 物联网应用
- 设备通信协议
- 数据处理流水线
- 远程控制接口
## 总结
**coroutine** 协程库是一个功能强大、性能优异的C语言协程解决方案。它提供了
1. **完整的协程生命周期管理**
2. **高效的多任务调度机制**
3. **灵活的定时器和事件管理**
4. **完善的错误处理和监控功能**

255
doc/slup.en.md Normal file
View File

@ -0,0 +1,255 @@
## Introduction
`slup` is a simple serial link general protocol module for the C language. It defines protocol-related configuration structures, queue structures, parser structures, and dummy data structures, etc. At the same time, it provides a series of function interfaces for initializing the protocol module, sending data, obtaining link status and rate information, setting dummy data, and other operations, which facilitates developers to implement communication functions based on serial links in C language projects.
## Interfaces
### Function Pointer Types
1. **`slup_putc_t` Function Pointer Type**:
```c
typedef int (*slup_putc_t)(char c);
```
- **Purpose**: Used to point to a function for writing a character.
- **Parameter**: `c`, the character to be written.
- **Return Value**: Of the `int` type, the specific meaning is determined by the actual function.
2. **`slup_check_t` Function Pointer Type**:
```c
typedef uint32_t (*slup_check_t)(uint8_t *data, uint16_t length);
```
- **Purpose**: Used to point to a function for checking data.
- **Parameters**:
- `data`: A pointer to the data array to be checked.
- `length`: The length of the data.
- **Return Value**: Of the `uint32_t` type, returns the check result.
3. **`slup_receive_t` Function Pointer Type**:
```c
typedef int (*slup_receive_t)(uint8_t *data, uint16_t length);
```
- **Purpose**: Used to point to a function for receiving data.
- **Parameters**:
- `data`: A pointer to the buffer for receiving data.
- `length`: The length of the data to be received.
- **Return Value**: Of the `int` type, the specific meaning is determined by the actual function.
### Structure Definitions
1. **`SLUP_CFG` Structure**:
```c
typedef struct
{
uint8_t head[SLUP_HEAD_MAX]; /**< head mask */
uint8_t tail[SLUP_TAIL_MAX]; /**< tail mask */
uint8_t hsize : 4; /**< head size */
uint8_t tsize : 4; /**< tail size */
uint8_t csize : 4; /**< check code size */
uint8_t ssize : 4; /**< sn size */
} SLUP_CFG;
```
- **Purpose**: Used to store the configuration information of the SLUP protocol, including the head mask, tail mask, head size, tail size, check code size, and sequence number size.
- **Member Introduction**:
- `head`: An array storing the head mask, with a maximum size of `SLUP_HEAD_MAX`.
- `tail`: An array storing the tail mask, with a maximum size of `SLUP_TAIL_MAX`.
- `hsize`: The head size, occupying 4 bits.
- `tsize`: The tail size, occupying 4 bits.
- `csize`: The check code size, occupying 4 bits.
- `ssize`: The sequence number size, occupying 4 bits.
2. **`SLUP_QUEUE` Structure**:
```c
typedef struct
{
char base[SLUP_RXQUE_SIZE]; /* base address of data */
uint32_t size; /* size of queue */
uint32_t head; /* index of queue head */
uint32_t tail; /* index of queue tail */
} SLUP_QUEUE;
```
- **Purpose**: Used to define the SLUP queue for storing received data.
- **Member Introduction**:
- `base`: The base address array of the queue data, with a size of `SLUP_RXQUE_SIZE`.
- `size`: The total size of the queue.
- `head`: The index of the queue head.
- `tail`: The index of the queue tail.
3. **`SLUP_PARSER` Structure**:
```c
typedef struct
{
uint8_t state;
uint8_t hindex : 4;
uint8_t tindex : 4;
uint8_t cindex : 4;
uint8_t sindex : 4;
uint8_t lindex : 2;
uint8_t frame;
uint16_t length;
uint16_t dindex;
uint32_t check;
uint32_t sn;
} SLUP_PARSER;
```
- **Purpose**: A structure used to parse SLUP data, recording various states and information during the parsing process.
- **Member Introduction**:
- `state`: The current state of the parser.
- `hindex`: The head index, occupying 4 bits.
- `tindex`: The tail index, occupying 4 bits.
- `cindex`: The check code index, occupying 4 bits.
- `sindex`: The sequence number index, occupying 4 bits.
- `lindex`: The length index, occupying 2 bits.
- `frame`: The frame status flag.
- `length`: The length of the data being processed.
- `dindex`: The index within the data.
- `check`: The check value of the data.
- `sn`: The sequence number.
4. **`SLUP_DUMMY` Structure**:
```c
typedef struct
{
float compression;
float rate;
uint32_t target;
uint32_t gapbase;
uint32_t gapcount;
uint8_t data[64];
} SLUP_DUMMY;
```
- **Purpose**: A structure used to store SLUP dummy data.
- **Member Introduction**:
- `compression`: The compression factor, of the floating-point type.
- `rate`: The rate value, of the floating-point type.
- `target`: The target value.
- `gapbase`: The gap base value.
- `gapcount`: The gap count.
- `data`: An array for storing data, with a size of 64 bytes.
5. **`SLUP` Structure**:
```c
typedef struct
{
SLUP_CFG cfg;
SLUP_QUEUE queue;
SLUP_PARSER parser;
SLUP_DUMMY dummy;
uint8_t buffer[SLUP_FRAME_MAX];
uint32_t bsize;
uint32_t sn;
uint8_t link;
uint8_t silent;
uint16_t period;
uint32_t timestamp;
uint32_t upbits;
uint32_t downbits;
uint32_t uprate;
uint32_t downrate;
slup_putc_t putc;
slup_check_t check;
slup_receive_t receive;
} SLUP;
```
- **Purpose**: The main structure of the SLUP protocol, integrating multiple parts such as configuration, queue, parser, and dummy data.
- **Member Introduction**:
- `cfg`: The `SLUP_CFG` structure, storing protocol configuration information.
- `queue`: The `SLUP_QUEUE` structure, storing receive queue information.
- `parser`: The `SLUP_PARSER` structure, storing parser information.
- `dummy`: The `SLUP_DUMMY` structure, storing dummy data information.
- `buffer`: The buffer for storing data, with a maximum size of `SLUP_FRAME_MAX`.
- `bsize`: The size of the buffer.
- `sn`: The sequence number.
- `link`: The link status flag.
- `silent`: The silent status flag.
- `period`: The period value.
- `timestamp`: The timestamp.
- `upbits`: The number of bits in the upload direction.
- `downbits`: The number of bits in the download direction.
- `uprate`: The upload rate.
- `downrate`: The download rate.
- `putc`: A function pointer of the `slup_putc_t` type, used for writing characters.
- `check`: A function pointer of the `slup_check_t` type, used for checking data.
- `receive`: A function pointer of the `slup_receive_t` type, used for receiving data.
### Functions
1. **`slup_init` Function**:
```c
int slup_init(SLUP *slup);
```
- **Function**: Initializes the `SLUP` structure.
- **Parameter**:
- `slup`: A pointer to the `SLUP` structure.
- **Return Value**:
- `SLUP_E_OK`: Initialization is successful.
- `SLUP_E_INVALID`: The passed `slup` pointer is `NULL`.
- **Explanation**: This function initializes each field in the `SLUP` structure.
2. **`slup_send` Function**:
```c
int slup_send(SLUP *slup, uint8_t *data, uint16_t length);
```
- **Function**: Sends SLUP protocol data with the frame type `0x01`.
- **Parameters**:
- `slup`: A pointer to the `SLUP` structure.
- `data`: A pointer to the buffer of the data to be sent.
- `length`: The length of the data buffer.
- **Return Value**:
- `SLUP_E_OK`: The data is successfully sent.
- Others: The corresponding error code.
- **Explanation**: This function simply calls the `slup_send_package` function to send the provided data with the frame type `0x01`.
3. **`slup_link_status` Function**:
```c
int slup_link_status(SLUP *slup, uint8_t *link);
```
- **Function**: Obtains the current link status from the `SLUP` structure.
- **Parameters**:
- `slup`: A pointer to the `SLUP` structure.
- `link`: A pointer to the variable used to store the link status.
- **Return Value**:
- `SLUP_E_OK`: The acquisition is successful.
- Others: The corresponding error code.
- **Explanation**: This function checks the validity of the passed pointer.
4. **`slup_upload_rate` Function**:
```c
int slup_upload_rate(SLUP *slup, uint32_t *rate);
```
- **Function**: Obtains the upload rate from the `SLUP` structure.
- **Parameters**:
- `slup`: A pointer to the `SLUP` structure.
- `rate`: A pointer to the variable used to store the upload rate.
- **Return Value**:
- `SLUP_E_OK`: The acquisition is successful.
- Others: The corresponding error code.
- **Explanation**: This function checks the validity of the passed pointer.
5. **`slup_download_rate` Function**:
```c
int slup_download_rate(SLUP *slup, uint32_t *rate);
```
- **Function**: Obtains the download rate from the `SLUP` structure.
- **Parameters**:
- `slup`: A pointer to the `SLUP` structure.
- `rate`: A pointer to the variable used to store the download rate.
- **Return Value**:
- `SLUP_E_OK`: The acquisition is successful.
- Others: The corresponding error code.
- **Explanation**: This function checks the validity of the passed pointer.
6. **`slup_set_dummy` Function**:
```c
int slup_set_dummy(SLUP *slup, uint32_t rate);
```
- **Function**: Sets the target rate of the dummy data in the `SLUP` structure.
- **Parameters**:
- `slup`: A pointer to the `SLUP` structure.
- `rate`: The target rate value to be set.
- **Return Value**:
- `SLUP_E_OK`: The setting is successful.
- `SLUP_E_INVALID`: The passed `slup` pointer is `NULL`.
- **Explanation**: This function checks the validity of the passed pointer.
7. **`slup_getc` Function**:
```c
void slup_getc(SLUP *slup, char c);
```
- **Function**: Handles the reception of a single character in the SLUP system.
- **Parameters**:
- `slup`: A pointer to the `SLUP` structure.
- `c`: The received character.
- **Explanation**: This function updates the link status to the receive state (sets `SLUP_LINK_RX`), clears the silent flag, updates the download statistics, and pushes the received character into the queue.
8. **`slup_task` Function**:
```c
void slup_task(SLUP *slup);
```
- **Function**: The main task function of the SLUP system.
- **Parameter**:
- `slup`: A pointer to the `SLUP` structure.
- **Explanation**: This function updates the timestamp and performs various operations according to the timestamp value, such as sending heartbeat messages, calculating the rate, performing dummy data sending, and updating dummy data parameters, etc. It also calls the `slup_parse_task` function to parse the received data in the queue.

255
doc/slup.md Normal file
View File

@ -0,0 +1,255 @@
## 介绍
`slup` 是一个用于 C 语言的简单串行链路通用协议模块。它定义了协议相关的配置结构体、队列结构体、解析器结构体以及虚拟数据结构体等,同时提供了一系列函数接口,用于初始化协议模块、发送数据、获取链路状态和速率信息、设置虚拟数据等操作,方便开发者在 C 语言项目中实现基于串行链路的通信功能。
## 接口
### 函数指针类型
1. **`slup_putc_t` 函数指针类型**
```c
typedef int (*slup_putc_t)(char c);
```
- **用途**:用于指向一个写字符的函数。
- **参数**`c`,要写入的字符。
- **返回值**`int` 类型,具体含义由实际函数决定。
2. **`slup_check_t` 函数指针类型**
```c
typedef uint32_t (*slup_check_t)(uint8_t *data, uint16_t length);
```
- **用途**:用于指向一个检查数据的函数。
- **参数**
- `data`:指向要检查的数据数组的指针。
- `length`:数据的长度。
- **返回值**`uint32_t` 类型,返回检查结果。
3. **`slup_receive_t` 函数指针类型**
```c
typedef int (*slup_receive_t)(uint8_t *data, uint16_t length);
```
- **用途**:用于指向一个接收数据的函数。
- **参数**
- `data`:指向接收数据缓冲区的指针。
- `length`:接收数据的长度。
- **返回值**`int` 类型,具体含义由实际函数决定。
### 结构体定义
1. **`SLUP_CFG` 结构体**
```c
typedef struct
{
uint8_t head[SLUP_HEAD_MAX]; /**< head mask */
uint8_t tail[SLUP_TAIL_MAX]; /**< tail mask */
uint8_t hsize : 4; /**< head size */
uint8_t tsize : 4; /**< tail size */
uint8_t csize : 4; /**< check code size */
uint8_t ssize : 4; /**< sn size */
} SLUP_CFG;
```
- **用途**:用于存储 SLUP 协议的配置信息,包括头部掩码、尾部掩码、头部大小、尾部大小、校验码大小和序列号大小。
- **成员介绍**
- `head`:存储头部掩码的数组,最大尺寸为 `SLUP_HEAD_MAX`
- `tail`:存储尾部掩码的数组,最大尺寸为 `SLUP_TAIL_MAX`
- `hsize`:头部大小,占用 `4` 位。
- `tsize`:尾部大小,占用 `4` 位。
- `csize`:校验码大小,占用 `4` 位。
- `ssize`:序列号大小,占用 `4` 位。
2. **`SLUP_QUEUE` 结构体**
```c
typedef struct
{
char base[SLUP_RXQUE_SIZE]; /* base address of data */
uint32_t size; /* size of queue */
uint32_t head; /* index of queue head */
uint32_t tail; /* index of queue tail */
} SLUP_QUEUE;
```
- **用途**:用于定义 SLUP 队列,存储接收的数据。
- **成员介绍**
- `base`:队列数据的基地址数组,大小为 `SLUP_RXQUE_SIZE`
- `size`:队列的总大小。
- `head`:队列头部的索引。
- `tail`:队列尾部的索引。
3. **`SLUP_PARSER` 结构体**
```c
typedef struct
{
uint8_t state;
uint8_t hindex : 4;
uint8_t tindex : 4;
uint8_t cindex : 4;
uint8_t sindex : 4;
uint8_t lindex : 2;
uint8_t frame;
uint16_t length;
uint16_t dindex;
uint32_t check;
uint32_t sn;
} SLUP_PARSER;
```
- **用途**:用于解析 SLUP 数据的结构体,记录解析过程中的各种状态和信息。
- **成员介绍**
- `state`:解析器的当前状态。
- `hindex`:头部索引,占用 `4` 位。
- `tindex`:尾部索引,占用 `4` 位。
- `cindex`:校验码索引,占用 `4` 位。
- `sindex`:序列号索引,占用 `4` 位。
- `lindex`:长度索引,占用 `2` 位。
- `frame`:帧状态标志。
- `length`:正在处理的数据长度。
- `dindex`:数据内的索引。
- `check`:数据的校验值。
- `sn`:序列号。
4. **`SLUP_DUMMY` 结构体**
```c
typedef struct
{
float compression;
float rate;
uint32_t target;
uint32_t gapbase;
uint32_t gapcount;
uint8_t data[64];
} SLUP_DUMMY;
```
- **用途**:用于存储 SLUP 虚拟数据的结构体。
- **成员介绍**
- `compression`:压缩因子,浮点数类型。
- `rate`:速率值,浮点数类型。
- `target`:目标值。
- `gapbase`:间隙基值。
- `gapcount`:间隙计数。
- `data`:存储数据的数组,大小为 `64` 字节。
5. **`SLUP` 结构体**
```c
typedef struct
{
SLUP_CFG cfg;
SLUP_QUEUE queue;
SLUP_PARSER parser;
SLUP_DUMMY dummy;
uint8_t buffer[SLUP_FRAME_MAX];
uint32_t bsize;
uint32_t sn;
uint8_t link;
uint8_t silent;
uint16_t period;
uint32_t timestamp;
uint32_t upbits;
uint32_t downbits;
uint32_t uprate;
uint32_t downrate;
slup_putc_t putc;
slup_check_t check;
slup_receive_t receive;
} SLUP;
```
- **用途**SLUP 协议的主要结构体,整合了配置、队列、解析器、虚拟数据等多个部分。
- **成员介绍**
- `cfg``SLUP_CFG` 结构体,存储协议配置信息。
- `queue``SLUP_QUEUE` 结构体,存储接收队列信息。
- `parser``SLUP_PARSER` 结构体,存储解析器信息。
- `dummy``SLUP_DUMMY` 结构体,存储虚拟数据信息。
- `buffer`:存储数据的缓冲区,最大尺寸为 `SLUP_FRAME_MAX`
- `bsize`:缓冲区的大小。
- `sn`:序列号。
- `link`:链路状态标志。
- `silent`:静音状态标志。
- `period`:周期值。
- `timestamp`:时间戳。
- `upbits`:上传方向的位数。
- `downbits`:下载方向的位数。
- `uprate`:上传速率。
- `downrate`:下载速率。
- `putc``slup_putc_t` 类型的函数指针,用于写字符。
- `check``slup_check_t` 类型的函数指针,用于检查数据。
- `receive``slup_receive_t` 类型的函数指针,用于接收数据。
### 函数
1. **`slup_init` 函数**
```c
int slup_init(SLUP *slup);
```
- **功能**:初始化 SLUP 结构体。
- **参数**
- `slup`:指向 `SLUP` 结构体的指针。
- **返回值**
- `SLUP_E_OK`:初始化成功。
- `SLUP_E_INVALID`:传入的 `slup` 指针为 `NULL`
- **说明**:该函数初始化 `SLUP` 结构体中的各个字段。
2. **`slup_send` 函数**
```c
int slup_send(SLUP *slup, uint8_t *data, uint16_t length);
```
- **功能**:以帧类型 `0x01` 发送 SLUP 协议数据。
- **参数**
- `slup`:指向 `SLUP` 结构体的指针。
- `data`:指向要发送的数据缓冲区的指针。
- `length`:数据缓冲区的长度。
- **返回值**
- `SLUP_E_OK`:数据成功发送。
- 其他:相应的错误码。
- **说明**:该函数简单调用 `slup_send_package` 函数,以帧类型 `0x01` 发送提供的数据。
3. **`slup_link_status` 函数**
```c
int slup_link_status(SLUP *slup, uint8_t *link);
```
- **功能**:从 SLUP 结构体中获取当前链路状态。
- **参数**
- `slup`:指向 `SLUP` 结构体的指针。
- `link`:指向用于存储链路状态的变量的指针。
- **返回值**
- `SLUP_E_OK`:获取成功。
- 其他:相应的错误码。
- **说明**:该函数检查传入指针的有效性。
4. **`slup_upload_rate` 函数**
```c
int slup_upload_rate(SLUP *slup, uint32_t *rate);
```
- **功能**:从 SLUP 结构体中获取上传速率。
- **参数**
- `slup`:指向 `SLUP` 结构体的指针。
- `rate`:指向用于存储上传速率的变量的指针。
- **返回值**
- `SLUP_E_OK`:获取成功。
- 其他:相应的错误码。
- **说明**:该函数检查传入指针的有效性。
5. **`slup_download_rate` 函数**
```c
int slup_download_rate(SLUP *slup, uint32_t *rate);
```
- **功能**:从 SLUP 结构体中获取下载速率。
- **参数**
- `slup`:指向 `SLUP` 结构体的指针。
- `rate`:指向用于存储下载速率的变量的指针。
- **返回值**
- `SLUP_E_OK`:获取成功。
- 其他:相应的错误码。
- **说明**:该函数检查传入指针的有效性。
6. **`slup_set_dummy` 函数**
```c
int slup_set_dummy(SLUP *slup, uint32_t rate);
```
- **功能**:设置 SLUP 结构体中虚拟数据的目标速率。
- **参数**
- `slup`:指向 `SLUP` 结构体的指针。
- `rate`:要设置的目标速率值。
- **返回值**
- `SLUP_E_OK`:设置成功。
- `SLUP_E_INVALID`:传入的 `slup` 指针为 `NULL`
- **说明**:该函数检查传入指针的有效性。
7. **`slup_getc` 函数**
```c
void slup_getc(SLUP *slup, char c);
```
- **功能**:处理 SLUP 系统中单个字符的接收。
- **参数**
- `slup`:指向 `SLUP` 结构体的指针。
- `c`:接收到的字符。
- **说明**:该函数更新链路状态为接收状态(设置 `SLUP_LINK_RX`),清除静音标志,更新下载统计信息,并将接收到的字符推入队列。
8. **`slup_task` 函数**
```c
void slup_task(SLUP *slup);
```
- **功能**SLUP 系统的主任务函数。
- **参数**
- `slup`:指向 `SLUP` 结构体的指针。
- **说明**:该函数更新时间戳,并根据时间戳值执行各种操作,如发送心跳消息、计算速率、执行虚拟数据发送和更新虚拟数据参数等。它还调用 `slup_parse_task` 函数来解析队列中接收到的数据。

436
doc/yaml.en.md Normal file
View File

@ -0,0 +1,436 @@
## Introduction
### What is a YAML file?
YAML (YAML Ain't Markup Language) is a **human-readable data serialization format** that focuses on concisely expressing structured data. It is widely used in configuration files, data exchange, and the description of complex data structures (such as the configurations of tools like Kubernetes and Ansible), rather than for storing traditional spreadsheet-like tabular data.
### Characteristics of YAML files
- **High readability**:
It uses indentation and symbols (such as `-` and `:`) to represent hierarchical relationships, with a format similar to natural language, making it easier to read than JSON/XML.
```yaml
user:
name: Alice
age: 30
hobbies:
- reading
- hiking
```
- **Supports complex data structures**:
It can represent scalars (strings, numbers), lists, dictionaries, etc., and supports nesting and multi-line text (using `|` or `>`).
- **Cross-platform compatibility**:
It is a plain text format that can be parsed by all operating systems and programming languages.
- **Seamless integration with programming languages**:
Most languages (Python, Java, Go, etc.) provide native or third-party libraries (such as PyYAML) to support YAML parsing.
### Uses of YAML
1. **Configuration files** (core use)
- Software configuration (such as Docker Compose, GitLab CI/CD)
- Cloud-native tool configuration (Kubernetes manifests)
```yaml
# Kubernetes Deployment example
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
```
2. **Data serialization**
- Replacing JSON/XML to transfer complex data
- Structured data description for API requests/responses
3. **Data exchange**
- Transferring structured information between different systems (such as microservice configuration synchronization)
### How to create and edit YAML files?
1. **Text editors**
- VS Code (it is recommended to install the YAML plugin for syntax highlighting and validation)
- Sublime Text / Vim, etc.
2. **Specialized tools**
- **Online YAML Validator**: Validates the syntax validity.
- **yq**: A command-line tool (similar to jq for processing YAML).
### Precautions
1. **Indentation sensitivity**
- Spaces must be used (usually 2 or 4 spaces, and tabs are prohibited).
- Incorrect indentation will cause parsing to fail.
2. **Key-value pair format**
- A space is required after the colon: `key: value` (not `key:value`).
3. **Special character handling**
- When a string contains symbols such as `:` and `#`, it is recommended to use quotes:
```yaml
message: "Hello:World"
comment: "This is a # symbol"
```
4. **Multi-line text**
- Preserve line breaks: `|`
```yaml
description: |
This is a
multi-line
text.
```
- Fold line breaks: `>`
```yaml
summary: >
This will fold
into a single line.
```
5. **Data type marking**
- Force specifying the type:
```yaml
boolean: !!bool "true"
timestamp: !!timestamp 2023-07-20T15:30:00Z
```
### Common error examples
```yaml
# Error: Mixing spaces and tabs in indentation
user:
name: Bob
age: 25 # Inconsistent indentation
# Error: Missing space in key-value pair
key1:value1 # Should be changed to key1: value1
# Error: Unescaped special characters
message: Line 1
Line 2 # Missing multi-line text identifier
```
### C language version of the YAML library
The YAML library provided by varch is simple and easy to use, capable of performing most basic operations on YAML files, including loading and saving YAML, as well as adding, deleting, modifying, and querying operations.
## Interfaces
### Creating and deleting YAML objects
```c
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
```
Here, **yaml_t** is the YAML structure. The creation method generates an empty YAML object, and the deletion method deletes the specified YAML object.
### Loading YAML objects
```c
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
```
YAML objects can be loaded from string text or from files. If the loading is successful, a YAML object will be returned; otherwise, NULL will be returned.
The flag is some flag for the operation function, defined as follows:
```
#define YAML_F_NONE (0)
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
```
When the YAML object fails to load, you can call the `int yaml_error_info(int* line, int* column);` function to locate the error.
The error types include:
```
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* date error */
```
### Dumping YAML objects
```c
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
```
First, the **yaml_dumps** method dumps the YAML object into a string according to the format. `*len` is the length of the converted string. When `NULL` is passed, the length is not obtained. The return value is the converted string, which is allocated by the function and **needs to be `free`d after use**.
The **yaml_file_dump** method dumps the YAML object into a file based on the **yaml_dumps** method. `filename` is the file name, and the return value is the length of the dump. A negative value indicates a dump failure.
### Adding child objects to a YAML object
```c
#define yaml_seq_add_null(yaml)
#define yaml_seq_add_int(yaml, num)
#define yaml_seq_add_float(yaml, num)
#define yaml_seq_add_string(yaml, string)
#define yaml_seq_add_sequence(yaml, sequence)
#define yaml_seq_add_mapping(yaml, mapping)
#define yaml_map_add_null(yaml, key)
#define yaml_map_add_int(yaml, key, num)
#define yaml_map_add_float(yaml, key, num)
#define yaml_map_add_string(yaml, key, string)
#define yaml_map_add_sequence(yaml, key, sequence)
#define yaml_map_add_mapping(yaml, key, mapping)
```
The above methods add scalars to sequences and mappings respectively. In fact, they are implemented by applying `insert`-type methods:
```c
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
```
These `insert`-type methods are used to insert child objects of different types at specified positions in a `yaml` object. `key` is the key of the inserted object, and `index` is the insertion position. The return value is the `yaml` object after the insertion operation. If the insertion fails, `NULL` is returned. Specifically:
- `yaml_insert_null`: Inserts a null-type child object.
- `yaml_insert_bool`: Inserts a boolean-type child object, where `b` is the boolean value (`YAML_FALSE` or `YAML_TRUE`).
- `yaml_insert_int`: Inserts an integer-type child object, where `num` is the integer value.
- `yaml_insert_float`: Inserts a floating-point-type child object, where `num` is the floating-point value.
- `yaml_insert_string`: Inserts a string-type child object, where `string` is the string value.
- `yaml_insert_sequence`: Inserts a sequence-type child object, where `sequence` is the sequence object.
- `yaml_insert_mapping`: Inserts a mapping-type child object, where `mapping` is the mapping object.
- `yaml_insert_document`: Inserts a document-type child object, where `document` is the document object.
- `yaml_insert_reference`: Inserts a reference-type child object, where `anchor` is the reference anchor, and `doc` is the referenced document object.
### Removing child objects from a YAML object
```c
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
```
Removes the `index`-th child object with the specified key `key` from the `yaml` object. If the removal is successful, `YAML_E_OK` is returned; otherwise, the corresponding error code is returned.
### YAML object attribute operations
```c
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
```
- `yaml_type`: Gets the type of the `yaml` object. The return value is one of the `YAML_TYPE_*` series of macro definitions, used to determine whether the object is of type `NULL`, boolean, integer, floating-point, string, sequence, mapping, document, reference, or complex key.
- `yaml_size`: Gets the size of the `yaml` object. For sequence or mapping-type objects, it returns the number of elements; for other types of objects, the meaning of the return value may vary depending on the specific implementation.
### YAML object comparison and copying
```c
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
```
- `yaml_compare`: Compares two `yaml` objects. `flag` is the comparison flag, used to specify the comparison method. The return value is the comparison result, and its specific meaning is determined by the implementation. Usually, 0 indicates equality, and non-0 indicates inequality.
- `yaml_copy`: Copies a `yaml` object. `flag` is the copy flag, used to specify the copy method. The return value is the copied `yaml` object. If the copy fails, `NULL` is returned.
### YAML object key operations
```c
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
```
- `yaml_set_key`: Sets a simple key for the `yaml` object, where `key` is the string value of the key. The return value is the `yaml` object after setting the key.
- `yaml_set_key_complex`: Sets a complex key for the `yaml` object, where `key` is the `yaml` object of the complex key. The return value is the `yaml` object after setting the key.
- `yaml_key`: Gets the simple key of the `yaml` object. The return value is the string value of the key.
- `yaml_key_complex`: Gets the complex key of the `yaml` object. The return value is the `yaml` object of the complex key.
### YAML object value setting
```c
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
```
These methods are used to set the value of a `yaml` object, and the return value is the `yaml` object after setting the value. Specifically:
- `yaml_set_null`: Sets the value of the `yaml` object to the null type.
- `yaml_set_bool`: Sets the value of the `yaml` object to the boolean type, where `b` is the boolean value (`YAML_FALSE` or `YAML_TRUE`).
- `yaml_set_int`: Sets the value of the `yaml` object to the integer type, where `num` is the integer value.
- `yaml_set_float`: Sets the value of the `yaml` object to the floating-point type, where `num` is the floating-point value.
- `yaml_set_string`: Sets the value of the `yaml` object to the string type, where `string` is the string value.
- `yaml_set_date`: Sets the value of the `yaml` object to the date type, where `year` is the year, `month` is the month, and `day` is the date.
- `yaml_set_time`: Sets the value of the `yaml` object to the time type, where `hour` is the hour, `minute` is the minute, `second` is the second, and `msec` is the millisecond.
- `yaml_set_utc`: Sets the value of the `yaml` object to the UTC time offset type, where `hour` is the hour offset and `minute` is the minute offset.
- `yaml_set_sequence`: Sets the value of the `yaml` object to the sequence type, where `sequence` is the sequence object.
- `yaml_set_mapping`: Sets the value of the `yaml` object to the mapping type, where `mapping` is the mapping object.
- `yaml_set_document`: Sets the value of the `yaml` object to the document type, where `document` is the document object.
### YAML object value retrieval
```c
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
```
These methods are used to retrieve the value of a `yaml` object, and the return value is the retrieved value. Specifically:
- `yaml_value_bool`: Retrieves the boolean value of the `yaml` object.
- `yaml_value_int`: Retrieves the integer value of the `yaml` object.
- `yaml_value_float`: Retrieves the floating-point value of the `yaml` object.
- `yaml_value_string`: Retrieves the string value of the `yaml` object.
- `yaml_value_sequence`: Retrieves the sequence value of the `yaml` object and returns the sequence object.
- `yaml_value_mapping`: Retrieves the mapping value of the `yaml` object and returns the mapping object.
- `yaml_value_document`: Retrieves the document value of the `yaml` object and returns the document object.
### YAML object child element operations
```c
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
```
- `yaml_attach`: Attaches a `yaml` child object `attach` at the specified position `index` in the `yaml` object. The return value is the `yaml` object after the operation. If the operation fails, `NULL` is returned.
- `yaml_dettach`: Detaches a child object from the specified position `index` in the `yaml` object. The return value is the detached child object. If the operation fails, `NULL` is returned.
### YAML object indexing and child object retrieval
```c
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
```
- `yaml_get_index`: Gets the index of the `index`-th child object with the key `key` in the `yaml` object. The return value is the index of the child object. If not found, `YAML_INV_INDEX` is returned.
- `yaml_get_index_complex`: Gets the index of the child object with the complex key `key` in the `yaml` object. The return value is the index of the child object. If not found, `YAML_INV_INDEX` is returned.
- `yaml_get_child`: Gets the `index`-th child object with the key `key` in the `yaml` object. The return value is the `yaml` object of the child object. If not found, `NULL` is returned.
- `yaml_get_child_complex`: Gets the child object with the complex key `key` in the `yaml` object. The return value is the `yaml` object of the child object. If not found, `NULL` is returned.
### YAML object anchor and alias operations
```c
const char* yaml_get_alias(yaml_t yaml);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
unsigned int yaml_anchor_size(yaml_t yaml);
```
- `yaml_get_alias`: Gets the alias of the `yaml` object. The return value is the string value of the alias.
- `yaml_get_anchor`: Gets the anchor object at the specified index `index` in the `yaml` object. The return value is the `yaml` object of the anchor.
- `yaml_set_alias`: Sets an alias for the `yaml` object, where `alias` is the string value of the alias and `doc` is the associated document object. The return value is the `yaml` object after setting the alias.
- `yaml_set_anchor`: Sets an anchor for the `yaml` object, where `anchor` is the string value of the anchor and `doc` is the associated document object. The return value is the `yaml` object after setting the anchor.
- `yaml_anchor_size`: Gets the number of anchors in the `yaml` object. The return value is the number of anchors.
## Reference examples
### Generating a YAML file
```c
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
```
The dumped file **write.yaml**
```yaml
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```
### Loading a YAML file
Similarly, loading a YAML file
```c
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
```
The running result:
```
load success!
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```

432
doc/yaml.md Normal file
View File

@ -0,0 +1,432 @@
## 介绍
### 什么是YAML文件
YAMLYAML Ain't Markup Language是一种**人类可读的数据序列化格式**专注于简洁表达结构化数据。它广泛用于配置文件、数据交换和复杂数据结构的描述如Kubernetes、Ansible等工具的配置而非传统电子表格类的表格数据存储。
### YAML文件的特点
- **可读性高**
使用缩进和符号(如`-`、`:`表示层级关系类似自然语言的格式比JSON/XML更易阅读。
```yaml
user:
name: Alice
age: 30
hobbies:
- reading
- hiking
```
- **支持复杂数据结构**
可表示标量(字符串、数字)、列表、字典等类型,支持嵌套和多行文本(通过`|`或`>`)。
- **跨平台兼容**
纯文本格式,所有操作系统和编程语言均可解析。
- **与编程语言无缝集成**
大多数语言Python、Java、Go等提供原生或第三方库如PyYAML支持YAML解析。
### YAML的用途
1. **配置文件**(核心用途)
- 软件配置如Docker Compose、GitLab CI/CD
- 云原生工具配置Kubernetes manifests
```yaml
# Kubernetes Deployment示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
```
2. **数据序列化**
- 替代JSON/XML传输复杂数据
- API请求/响应的结构化数据描述
3. **数据交换**
- 不同系统间传递结构化信息(如微服务配置同步)
### 如何创建和编辑YAML文件
1. **文本编辑器**
- VS Code推荐安装YAML插件实现语法高亮和校验
- Sublime Text / Vim等
2. **专用工具**
- **Online YAML Validator**:校验语法有效性
- **yq**命令行工具类似jq用于处理YAML
### 注意事项
1. **缩进敏感**
- 必须使用空格通常2或4空格禁止使用Tab
- 缩进错误会导致解析失败
2. **键值对格式**
- 冒号后需有空格:`key: value`(而非`key:value`
3. **特殊字符处理**
- 字符串包含`:`、`#`等符号时建议使用引号:
```yaml
message: "Hello:World"
comment: "This is a # symbol"
```
4. **多行文本**
- 保留换行符:`|`
```yaml
description: |
This is a
multi-line
text.
```
- 折叠换行符:`>`
```yaml
summary: >
This will fold
into a single line.
```
5. **数据类型标记**
- 强制指定类型:
```yaml
boolean: !!bool "true"
timestamp: !!timestamp 2023-07-20T15:30:00Z
```
### 常见错误示例
```yaml
# 错误缩进混用空格和Tab
user:
name: Bob
age: 25 # 缩进不一致
# 错误:键值对缺少空格
key1:value1 # 应改为 key1: value1
# 错误:未转义特殊字符
message: Line 1
Line 2 # 缺少多行文本标识符
```
### C语言版YAML库
varch提供的YAML库简便易用能完成大部分对于YAML文件的基础操作包含对yaml的加载和保存增删改查等操作。
## 接口
### 创建和删除yaml对象
```c
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
```
其中**yaml_t**为yaml的结构体创建方法会生成一个空yaml对象。删除方法则删除指定的yaml对象。
### yaml对象加载
```c
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
```
yaml对象可以从字符串文本中加载也可以从文件中加载。加载成功则会返回一个yaml对象失败则返回NULL。
flag为操作函数的一些flag定义如下
```
#define YAML_F_NONE (0)
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
```
当yaml对象加载失败的时候可以调用`int yaml_error_info(int* line, int* column);`函数进行定位错误。
错误类型包含
```
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* date error */
```
### yaml对象转储
```c
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
```
首先**yaml_dumps**方法将yaml对象按格式转储为字符串。`*len`则是转换出来的字符串长度,传入`NULL`时候就是不获取长度。返回值则是转换出来的字符串,这个字符串是函数分配的,**在结束使用需要`free`掉**。
**yaml_file_dump**方法则是在**yaml_dumps**的基础上将yaml转储到文件当中`filename`传入文件名,返回值为转储的长度,负值表示转储失败。
### yaml添加子对象
```c
#define yaml_seq_add_null(yaml)
#define yaml_seq_add_int(yaml, num)
#define yaml_seq_add_float(yaml, num)
#define yaml_seq_add_string(yaml, string)
#define yaml_seq_add_sequence(yaml, sequence)
#define yaml_seq_add_mapping(yaml, mapping)
#define yaml_map_add_null(yaml, key)
#define yaml_map_add_int(yaml, key, num)
#define yaml_map_add_float(yaml, key, num)
#define yaml_map_add_string(yaml, key, string)
#define yaml_map_add_sequence(yaml, key, sequence)
#define yaml_map_add_mapping(yaml, key, mapping)
```
以上方法分别在序列中和在映射中添加标量,其实就是套用 `insert` 类方法实现的
```c
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
```
这些`insert`类方法用于在`yaml`对象的指定位置插入不同类型的子对象。`key`为插入对象的键,`index`为插入的位置。返回值为插入操作后的`yaml`对象,若插入失败则返回`NULL`。具体如下:
- `yaml_insert_null`:插入一个空类型的子对象。
- `yaml_insert_bool`:插入一个布尔类型的子对象,`b`为布尔值(`YAML_FALSE` 或 `YAML_TRUE`)。
- `yaml_insert_int`:插入一个整数类型的子对象,`num`为整数值。
- `yaml_insert_float`:插入一个浮点数类型的子对象,`num`为浮点数值。
- `yaml_insert_string`:插入一个字符串类型的子对象,`string`为字符串值。
- `yaml_insert_sequence`:插入一个序列类型的子对象,`sequence`为序列对象。
- `yaml_insert_mapping`:插入一个映射类型的子对象,`mapping`为映射对象。
- `yaml_insert_document`:插入一个文档类型的子对象,`document`为文档对象。
- `yaml_insert_reference`:插入一个引用类型的子对象,`anchor`为引用的锚点,`doc`为引用的文档对象。
### yaml移除子对象
```c
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
```
移除`yaml`对象中特定的键为`key`的第`index`个子对象。若移除成功,返回`YAML_E_OK`;若移除失败,返回相应的错误码。
### yaml对象属性操作
```c
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
```
- `yaml_type`:获取`yaml`对象的类型,返回值为`YAML_TYPE_*`系列宏定义的值,用于判断对象是`NULL`、布尔、整数、浮点数、字符串、序列、映射、文档、引用或复杂键等类型。
- `yaml_size`:获取`yaml`对象的大小。对于序列或映射类型的对象,返回其元素的数量;对于其他类型的对象,返回值的含义可能因具体实现而异。
### yaml对象比较和复制
```c
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
```
- `yaml_compare`:比较两个`yaml`对象。`flag`为比较的标志位,用于指定比较的方式。返回值为比较结果,具体含义由实现决定,通常 0 表示相等,非 0 表示不相等。
- `yaml_copy`:复制一个`yaml`对象。`flag`为复制的标志位,用于指定复制的方式。返回值为复制后的`yaml`对象,若复制失败则返回`NULL`。
### yaml对象键操作
```c
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
```
- `yaml_set_key`:为`yaml`对象设置一个简单键,`key`为键的字符串值。返回值为设置键后的`yaml`对象。
- `yaml_set_key_complex`:为`yaml`对象设置一个复杂键,`key`为复杂键的`yaml`对象。返回值为设置键后的`yaml`对象。
- `yaml_key`:获取`yaml`对象的简单键,返回值为键的字符串值。
- `yaml_key_complex`:获取`yaml`对象的复杂键,返回值为复杂键的`yaml`对象。
### yaml对象值设置
```c
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
```
这些方法用于设置`yaml`对象的值,返回值为设置值后的`yaml`对象。具体如下:
- `yaml_set_null`:将`yaml`对象的值设置为空类型。
- `yaml_set_bool`:将`yaml`对象的值设置为布尔类型,`b`为布尔值(`YAML_FALSE` 或 `YAML_TRUE`)。
- `yaml_set_int`:将`yaml`对象的值设置为整数类型,`num`为整数值。
- `yaml_set_float`:将`yaml`对象的值设置为浮点数类型,`num`为浮点数值。
- `yaml_set_string`:将`yaml`对象的值设置为字符串类型,`string`为字符串值。
- `yaml_set_date`:将`yaml`对象的值设置为日期类型,`year`为年份,`month`为月份,`day`为日期。
- `yaml_set_time`:将`yaml`对象的值设置为时间类型,`hour`为小时,`minute`为分钟,`second`为秒,`msec`为毫秒。
- `yaml_set_utc`:将`yaml`对象的值设置为 UTC 时间偏移类型,`hour`为小时偏移,`minute`为分钟偏移。
- `yaml_set_sequence`:将`yaml`对象的值设置为序列类型,`sequence`为序列对象。
- `yaml_set_mapping`:将`yaml`对象的值设置为映射类型,`mapping`为映射对象。
- `yaml_set_document`:将`yaml`对象的值设置为文档类型,`document`为文档对象。
### yaml对象值获取
```c
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
```
这些方法用于获取`yaml`对象的值,返回值为获取到的值。具体如下:
- `yaml_value_bool`:获取`yaml`对象的布尔值。
- `yaml_value_int`:获取`yaml`对象的整数值。
- `yaml_value_float`:获取`yaml`对象的浮点数值。
- `yaml_value_string`:获取`yaml`对象的字符串值。
- `yaml_value_sequence`:获取`yaml`对象的序列值,返回序列对象。
- `yaml_value_mapping`:获取`yaml`对象的映射值,返回映射对象。
- `yaml_value_document`:获取`yaml`对象的文档值,返回文档对象。
### yaml对象子元素操作
```c
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
```
- `yaml_attach`:在`yaml`对象的指定位置`index`附加一个`yaml`子对象`attach`。返回值为操作后的`yaml`对象,若操作失败则返回`NULL`。
- `yaml_dettach`:从`yaml`对象的指定位置`index`分离一个子对象。返回值为分离出的子对象,若操作失败则返回`NULL`。
### yaml对象索引和子对象获取
```c
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
```
- `yaml_get_index`:获取`yaml`对象中键为`key`的第`index`个子对象的索引。返回值为子对象的索引,若未找到则返回`YAML_INV_INDEX`。
- `yaml_get_index_complex`:获取`yaml`对象中复杂键为`key`的子对象的索引。返回值为子对象的索引,若未找到则返回`YAML_INV_INDEX`。
- `yaml_get_child`:获取`yaml`对象中键为`key`的第`index`个子对象。返回值为子对象的`yaml`对象,若未找到则返回`NULL`。
- `yaml_get_child_complex`:获取`yaml`对象中复杂键为`key`的子对象。返回值为子对象的`yaml`对象,若未找到则返回`NULL`。
### yaml对象锚点和别名操作
```c
const char* yaml_get_alias(yaml_t yaml);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
unsigned int yaml_anchor_size(yaml_t yaml);
```
- `yaml_get_alias`:获取`yaml`对象的别名,返回值为别名的字符串值。
- `yaml_get_anchor`:获取`yaml`对象中指定索引`index`的锚点对象,返回值为锚点的`yaml`对象。
- `yaml_set_alias`:为`yaml`对象设置别名,`alias`为别名的字符串值,`doc`为关联的文档对象。返回值为设置别名后的`yaml`对象。
- `yaml_set_anchor`:为`yaml`对象设置锚点,`anchor`为锚点的字符串值,`doc`为关联的文档对象。返回值为设置锚点后的`yaml`对象。
- `yaml_anchor_size`:获取`yaml`对象中锚点的数量。返回值为锚点的数量。
## 参考例子
### 生成yaml文件
```c
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
```
转储的文件 **write.yaml**
```yaml
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```
### 加载yaml文件
同样加载yaml文件
```c
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
```
运行结果:
```
load success!
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```

View File

@ -21,6 +21,7 @@ ALGORITHM_PATH = $(WORKSPACE)/04_algorithm
PARSER_PATH = $(WORKSPACE)/05_parser PARSER_PATH = $(WORKSPACE)/05_parser
PERFORMANCE_PATH = $(WORKSPACE)/06_performance PERFORMANCE_PATH = $(WORKSPACE)/06_performance
MATH_PATH = $(WORKSPACE)/07_math MATH_PATH = $(WORKSPACE)/07_math
COROUTINE_PATH = $(WORKSPACE)/08_coroutine
################################################################################## ##################################################################################
### sources, libaries and head path ### sources, libaries and head path
@ -33,6 +34,7 @@ INCLUDE += $(ALGORITHM_PATH)
INCLUDE += $(PARSER_PATH) INCLUDE += $(PARSER_PATH)
INCLUDE += $(PERFORMANCE_PATH) INCLUDE += $(PERFORMANCE_PATH)
INCLUDE += $(MATH_PATH) INCLUDE += $(MATH_PATH)
INCLUDE += $(COROUTINE_PATH)
LIBSRCS += $(APPLICATION_PATH)/init.c LIBSRCS += $(APPLICATION_PATH)/init.c
LIBSRCS += $(wildcard $(APPLICATION_PATH)/console/*.c) LIBSRCS += $(wildcard $(APPLICATION_PATH)/console/*.c)
@ -43,6 +45,7 @@ LIBSRCS += $(wildcard $(ALGORITHM_PATH)/*.c)
LIBSRCS += $(wildcard $(PARSER_PATH)/*.c) LIBSRCS += $(wildcard $(PARSER_PATH)/*.c)
LIBSRCS += $(wildcard $(PERFORMANCE_PATH)/*.c) LIBSRCS += $(wildcard $(PERFORMANCE_PATH)/*.c)
LIBSRCS += $(wildcard $(MATH_PATH)/*.c) LIBSRCS += $(wildcard $(MATH_PATH)/*.c)
LIBSRCS += $(wildcard $(COROUTINE_PATH)/*.c)
LIBLIST += m LIBLIST += m
LIBLIST += pthread LIBLIST += pthread

View File

@ -1,3 +1,28 @@
version 0.3.5
date 2025.11.20
changes
1. Add the initial version coroutine
---------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------
version 0.3.4
date 2025.05.10
changes
1. Add the initial version yaml parser
---------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------
version 0.3.3
date 2025.03.18
changes
1. Add the initial version slup and cant
2. Add floatl_to_d() function
---------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------
version 0.3.2 version 0.3.2
date 2025.03.13 date 2025.03.13
changes changes

4
run.sh
View File

@ -10,9 +10,9 @@ fi
echo "compilling..." echo "compilling..."
if [ "$1" == "debug" ]; then if [ "$1" == "debug" ]; then
make CFLAG=-g make -j CFLAG=-g
else else
make make -j
fi fi
if [ -f $exe_file ]; then if [ -f $exe_file ]; then

View File

@ -6,7 +6,7 @@
* \unit list * \unit list
* \brief This is a C language singly linked list with built-in iterators, simple, reliable, fast, small space * \brief This is a C language singly linked list with built-in iterators, simple, reliable, fast, small space
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/

View File

@ -6,7 +6,7 @@
* \unit list * \unit list
* \brief This is a C language singly linked list with built-in iterators, simple, reliable, fast, small space * \brief This is a C language singly linked list with built-in iterators, simple, reliable, fast, small space
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -19,7 +19,7 @@
#define LIST_V_MAJOR 1 #define LIST_V_MAJOR 1
#define LIST_V_MINOR 0 #define LIST_V_MINOR 0
#define LIST_V_PATCH 0 #define LIST_V_PATCH 1
/* list type definition, hiding structural members, not for external use */ /* list type definition, hiding structural members, not for external use */
@ -123,7 +123,7 @@ int list_dsize(list_t list);
* \param[out] data: the address of data * \param[out] data: the address of data
* \return 1 success or 0 fail * \return 1 success or 0 fail
*/ */
#define list_pop_back(list) list_erase((list), list_size(list), 1) #define list_pop_back(list) list_erase((list), list_size(list) - 1, 1)
/** /**
* \brief clear list. * \brief clear list.

View File

@ -6,7 +6,7 @@
* \unit map * \unit map
* \brief This is a general-purpose C language map module, with common data structure * \brief This is a general-purpose C language map module, with common data structure
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -694,8 +694,13 @@ static NODE* node_next(map_t map, NODE* node)
} }
else else
{ {
if (node == node->parent->left) node = node->parent; NODE *parent = node->parent;
else node = node->parent->parent; while (parent != map->nil && node == parent->right)
{
node = parent;
parent = parent->parent;
}
return parent;
} }
return node; return node;
} }
@ -709,8 +714,13 @@ static NODE* node_prev(map_t map, NODE* node)
} }
else else
{ {
if (node == node->parent->right) node = node->parent; NODE *parent = node->parent;
else node = node->parent->parent; while (parent != map->nil && node == parent->left)
{
node = parent;
parent = parent->parent;
}
return parent;
} }
return node; return node;
} }

View File

@ -6,7 +6,7 @@
* \unit map * \unit map
* \brief This is a general-purpose C language map module, with common data structure * \brief This is a general-purpose C language map module, with common data structure
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -20,7 +20,7 @@
#define MAP_V_MAJOR 1 #define MAP_V_MAJOR 1
#define MAP_V_MINOR 0 #define MAP_V_MINOR 0
#define MAP_V_PATCH 0 #define MAP_V_PATCH 1
/* map type definition, hiding structural members, not for external use */ /* map type definition, hiding structural members, not for external use */

View File

@ -6,7 +6,7 @@
* \unit set * \unit set
* \brief This is a general-purpose C language set module, with common data structure * \brief This is a general-purpose C language set module, with common data structure
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -592,8 +592,13 @@ static NODE* node_next(set_t set, NODE* node)
} }
else else
{ {
if (node == node->parent->left) node = node->parent; NODE *parent = node->parent;
else node = node->parent->parent; while (parent != set->nil && node == parent->right)
{
node = parent;
parent = parent->parent;
}
return parent;
} }
return node; return node;
} }
@ -607,8 +612,13 @@ static NODE* node_prev(set_t set, NODE* node)
} }
else else
{ {
if (node == node->parent->right) node = node->parent; NODE *parent = node->parent;
else node = node->parent->parent; while (parent != set->nil && node == parent->left)
{
node = parent;
parent = parent->parent;
}
return parent;
} }
return node; return node;
} }

View File

@ -6,7 +6,7 @@
* \unit set * \unit set
* \brief This is a general-purpose C language set module, with common data structure * \brief This is a general-purpose C language set module, with common data structure
* \author Lamdonn * \author Lamdonn
* \version v1.0.0 * \version v1.0.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -19,7 +19,7 @@
#define SET_V_MAJOR 1 #define SET_V_MAJOR 1
#define SET_V_MINOR 0 #define SET_V_MINOR 0
#define SET_V_PATCH 0 #define SET_V_PATCH 1
/* set type definition, hiding structural members, not for external use */ /* set type definition, hiding structural members, not for external use */

7073
source/05_parser/yaml.c Normal file

File diff suppressed because it is too large Load Diff

278
source/05_parser/yaml.h Normal file
View File

@ -0,0 +1,278 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file yaml.h
* \unit yaml
* \brief This is a C language version of yaml streamlined parser
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#ifndef __yaml_H
#define __yaml_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdarg.h>
#include <limits.h>
#include <stdlib.h>
/* version infomation */
#define YAML_V_MAJOR 0
#define YAML_V_MINOR 1
#define YAML_V_PATCH 0
/* yaml type definition, hiding structural members, not for external use */
/**
* \brief Opaque handle to YAML node structure
*
* This typedef provides an opaque pointer to the internal YAML node structure.
* The actual structure definition is hidden from external code to maintain encapsulation.
*/
typedef struct YAML* yaml_t;
/* yaml normal types define */
/**
* \brief Enumeration of YAML data types
*
* These constants represent the different data types supported by the YAML parser.
* They are used to identify the type of value stored in a YAML node.
*/
#define YAML_TYPE_NULL (0) /* null type */
#define YAML_TYPE_BOOL (1) /* bool type */
#define YAML_TYPE_INT (2) /* number int type */
#define YAML_TYPE_FLOAT (3) /* number float type */
#define YAML_TYPE_STRING (4) /* string type */
#define YAML_TYPE_DATE (5) /* date type */
#define YAML_TYPE_SEQUENCE (6) /* sequence type */
#define YAML_TYPE_MAPPING (7) /* mapping type */
#define YAML_TYPE_DOCUMENT (8) /* document type */
#define YAML_TYPE_REFERENCE (9) /* reference type */
#define YAML_TYPE_COMPLEX_KEY (10) /* complex key */
/* bool define */
/**
* \brief Boolean values
*
* These constants represent boolean true and false values in the YAML parser.
*/
#define YAML_FALSE (0) /* bool false */
#define YAML_TRUE (1) /* bool true */
/* error type define */
/**
* \brief Error codes returned by the YAML parser
*
* These constants represent various error conditions that can occur during YAML parsing.
* They are used to indicate the nature of parsing errors to the calling code.
*/
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* target error */
/* dump flags define */
/**
* \brief Flags used to control YAML dumping and loading behavior
*
* These flags can be combined using bitwise OR to modify the behavior
* of functions that dump or load YAML documents.
*/
#define YAML_F_NONE (0) /* none flag */
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
/**
* \brief Represents an invalid index value
*
* This constant is used to indicate an invalid or non-existent index
* in functions that operate on indexed YAML structures.
*/
#define YAML_INV_INDEX ((unsigned int)(-1))
// Functions for creating and deleting YAML nodes.
// These functions are used to initialize and free the memory resources of YAML nodes.
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
// Function for getting YAML parsing error information.
// It is used to obtain error line number and column number during YAML parsing.
int yaml_error_info(int* line, int* column);
// Functions for getting the type and size of a YAML node.
// Used to determine the data type of a YAML node and get the number of child elements of a container node.
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
// Functions for comparing and copying YAML nodes.
// Used to check if two YAML nodes are equal and to create a copy of a YAML node.
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
// Functions for setting the key of a YAML node.
// Used to set a simple key or a complex key for a YAML node.
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
// Functions for getting the key of a YAML node.
// Used to retrieve the simple key or complex key of a YAML node.
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
// Functions for setting the value of a YAML node.
// Used to set a YAML node to different types of values such as null, boolean, integer, float, string, date, time, sequence, mapping, document, etc.
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
// Functions for getting the value of a YAML node.
// Used to retrieve the boolean value, integer value, float value, string value, sequence value, mapping value, document value, etc. of a YAML node.
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
// Functions for attaching and detaching YAML nodes.
// Used to attach a YAML node to another node or detach a child node from a node.
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
// Functions for inserting values into a YAML node.
// Used to insert different types of values such as null, boolean, integer, float, string, sequence, mapping, document, reference, etc. into a YAML node.
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
// Function for removing a value from a YAML node.
// Used to remove a value from a YAML node according to the key and index.
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
// Functions for getting the index of a YAML node.
// Used to obtain the index of a value in a YAML node, supporting the index acquisition of simple keys and complex keys.
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
// Functions for getting child nodes of a YAML node.
// Used to get the child nodes of a YAML node according to the key and index, supporting the retrieval of child nodes of simple keys and complex keys.
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
// Functions for setting and getting the alias of a YAML node.
// Used to set an alias for a YAML node and retrieve the alias of a YAML node.
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
const char* yaml_get_alias(yaml_t yaml);
// Functions for setting and getting the anchor of a YAML node.
// Used to set an anchor for a YAML node, retrieve the anchor, and get the size of the anchor.
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
unsigned int yaml_anchor_size(yaml_t yaml);
// YAML serialization functions.
// Used to serialize a YAML node into a string (yaml_dumps) or write it to a file (yaml_file_dump).
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
// YAML deserialization functions.
// Used to load YAML data from a string (yaml_loads) or a file (yaml_file_dump) and construct a YAML node.
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
// Macros for judging the type of a YAML node.
// Used to determine whether a YAML node is of a specific type (null, bool, int, float, string, sequence, mapping, document).
#define yaml_isnull(yaml) (yaml_type(yaml)==YAML_TYPE_NULL)
#define yaml_isbool(yaml) (yaml_type(yaml)==YAML_TYPE_BOOL)
#define yaml_isint(yaml) (yaml_type(yaml)==YAML_TYPE_INT)
#define yaml_isfloat(yaml) (yaml_type(yaml)==YAML_TYPE_FLOAT)
#define yaml_isstring(yaml) (yaml_type(yaml)==YAML_TYPE_STRING)
#define yaml_issequence(yaml) (yaml_type(yaml)==YAML_TYPE_SEQUENCE)
#define yaml_ismapping(yaml) (yaml_type(yaml)==YAML_TYPE_MAPPING)
#define yaml_isdocument(yaml) (yaml_type(yaml)==YAML_TYPE_DOCUMENT)
// Macros for adding values to a YAML node.
// Used to quickly add different types of values to a YAML sequence or mapping node.
#define yaml_seq_add_null(yaml) yaml_insert_null((yaml), 0, yaml_size((yaml)))
#define yaml_seq_add_bool(yaml, b) yaml_insert_bool((yaml), 0, yaml_size((yaml)), (b))
#define yaml_seq_add_int(yaml, num) yaml_insert_int((yaml), 0, yaml_size((yaml)), (num))
#define yaml_seq_add_float(yaml, num) yaml_insert_float((yaml), 0, yaml_size((yaml)), (num))
#define yaml_seq_add_string(yaml, string) yaml_insert_string((yaml), 0, yaml_size((yaml)), (string))
#define yaml_seq_add_sequence(yaml, sequence) yaml_insert_sequence((yaml), 0, yaml_size((yaml)), (sequence))
#define yaml_seq_add_mapping(yaml, mapping) yaml_insert_mapping((yaml), 0, yaml_size((yaml)), (mapping))
#define yaml_map_add_null(yaml, key) yaml_insert_null((yaml), (key), yaml_size((yaml)))
#define yaml_map_add_bool(yaml, key, b) yaml_insert_bool((yaml), (key), yaml_size((yaml)), (b))
#define yaml_map_add_int(yaml, key, num) yaml_insert_int((yaml), (key), yaml_size((yaml)), (num))
#define yaml_map_add_float(yaml, key, num) yaml_insert_float((yaml), (key), yaml_size((yaml)), (num))
#define yaml_map_add_string(yaml, key, string) yaml_insert_string((yaml), (key), yaml_size((yaml)), (string))
#define yaml_map_add_sequence(yaml, key, sequence) yaml_insert_sequence((yaml), (key), yaml_size((yaml)), (sequence))
#define yaml_map_add_mapping(yaml, key, mapping) yaml_insert_mapping((yaml), (key), yaml_size((yaml)), (mapping))
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,489 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file cant.c
* \unit cant
* \brief This is a can test for C language
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/
#include "cant.h"
/* Frame data base length. */
// Constant representing the base number of bits for a CAN standard frame's data.
// It is set to 47 bits and is used to define the basic length of a CAN standard frame.
static const uint32_t CanStdFrmBaseBits = 47; /**< CAN standard frame data base length. */
// Constant representing the base number of bits for a CAN extended frame's data.
// It is set to 67 bits and is used to define the basic length of a CAN extended frame.
static const uint32_t CanExtFrmBaseBits = 67; /**< CAN extended frame data base length. */
// Constant representing the base number of bits for a CANFD (Controller Area Network Flexible Data Rate) standard frame's data.
// Calculated as the sum of different parts of the frame (17(SOF~BRS) + 12(ACK~IFS) + 5(ESI~DLC) + 18(CRC~DEL) + dlc * 8).
// This defines the basic length of a CANFD standard frame.
static const uint32_t CanFDStdFrmBaseBits = 17 + 12 + 5 + 18; /**< CANFD standard frame data base length, 17(SOF~BRS) + 12(ACK~IFS) + 5(ESI~DLC) + 18(CRC~DEL) + dlc * 8 */
// Constant representing the base number of bits for a CANFD extended frame's data.
// Calculated as the sum of different parts of the frame (36(SOF~BRS) + 12(ACK~IFS) + 5(ESI~DLC) + 18(CRC~DEL) + dlc * 8).
// This defines the basic length of a CANFD extended frame.
static const uint32_t CanFDExtFrmBaseBits = 36 + 12 + 5 + 18; /**< CANFD extended frame data base length, 36(SOF~BRS) + 12(ACK~IFS) + 5(ESI~DLC) + 18(CRC~DEL) + dlc * 8 */
// Constant representing the additional number of bits for CANFD when the Data Length Code (DLC) is greater than 16.
// In this case, the CRC changes from 17 bits to 23 bits, and this value represents that additional length.
static const uint32_t CanFDAppendBits = 4; /**< CANFD append length, when DLC is greater than 16, crc changes from 17bits to 23bits. */
// Array of baud rates available for the CAN bus.
// It contains different baud rate values such as 125000 (125K baud), 250000 (250K baud), etc.
// The size of the array is determined by CANT_BAUDRATE_MAX (although it's not fully clear from this code what that is exactly).
static const uint32_t baudrate_list[CANT_BAUDRATE_MAX] =
{
125000, // CANT_BAUDRATE_125K
250000, // CANT_BAUDRATE_250K
500000, // CANT_BAUDRATE_500K
800000, // CANT_BAUDRATE_800K
1000000, // CANT_BAUDRATE_1000K
// CANT_BAUDRATE_MAX,
};
/**
* \brief Calculates the CRC8 (Cyclic Redundancy Check with 8 bits) value for a given data buffer.
* \param data: Pointer to the data buffer for which the CRC8 is to be calculated.
* \param len: The length of the data buffer.
* \return The calculated CRC8 value as a uint8_t.
*
* This function calculates the CRC8 value for the input data buffer. It iterates through each byte of the data.
* For each byte, it XORs the byte with the current CRC value and then performs a series of shift and XOR operations
* based on the most significant bit of the CRC value to update the CRC. After processing all bytes, it returns the final CRC8 value.
*/
static uint8_t crc8(uint8_t* data, uint32_t len)
{
uint8_t i;
uint8_t crc = 0;
while (len--)
{
crc ^= *data++;
for (i = 0; i < 8; i++)
{
if (crc & 0x80) crc = (crc << 1) ^ 0x07;
else crc <<= 1;
}
}
return crc;
}
/**
* \brief Updates the bus load statistics for a CAN (Controller Area Network) device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param length: The length of the data frame in bytes.
* \return CANT_E_OK on success, indicating that the statistics have been updated.
*
* This function calculates the number of bits transmitted in a CAN FD (Flexible Data Rate) standard frame
* and adds it to the `periodbits` member of the CANT structure. If the data length is greater than 16 bytes,
* it also adds the additional bits due to the change in CRC length from 17 bits to 23 bits.
*/
static int cant_statistics_busload(CANT *cant, uint16_t length)
{
// Check if the configured baud rate is within the valid range
if (cant->config.baundrate < CANT_BAUDRATE_MAX)
{
// Calculate the total number of bits in the frame and add it to the periodbits
cant->periodbits += (CanFDStdFrmBaseBits + length * 8);
/* When DLC is greater than 16, crc changes from 17bits to 23bits. */
if (length > 16)
{
// Add the additional bits due to the CRC change
cant->periodbits += CanFDAppendBits;
}
}
return CANT_E_OK;
}
/**
* \brief Calculates the bus load percentage for a CAN device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \return CANT_E_OK on success, indicating that the bus load has been calculated.
*
* This function calculates the bus load percentage based on the total number of bits transmitted
* during a period and the configured baud rate. It then updates the `busload` member of the CANT structure.
* If the calculated bus load exceeds 100%, it is capped at 100%. Finally, it resets the `periodbits` member.
*/
static int cant_calculate_busload(CANT *cant)
{
// Check if the configured baud rate is within the valid range
if (cant->config.baundrate < CANT_BAUDRATE_MAX)
{
/* Count the amount of data transmitted during this cycle, including sent and received. */
// Calculate the bus load as a percentage and scale it by 10000
cant->busload = (uint16_t)((double)cant->periodbits / baudrate_list[cant->config.baundrate] * 10000);
// Cap the bus load at 100%
if (cant->busload > 10000) cant->busload = 10000;
// Reset the periodbits for the next cycle
cant->periodbits = 0;
}
return CANT_E_OK;
}
/**
* \brief Updates the dummy data parameters based on the bus load in the CAN device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \return CANT_E_OK if the update is successful, CANT_E_DCANID if the CAN ID in the dummy data is 0.
*
* This function first checks if the CAN ID in the dummy data is valid (not 0). If it is valid,
* it compares the current bus load with the target load in the dummy data. If the bus load is less
* than the target load, it increases the 'gapcount' by a fixed resolution value. If the bus load
* is greater than the target load, it decreases the 'gapcount' by the same resolution value.
* Then it calculates the 'gapbase' as the integer part of 'gapcount' divided by 100,
* and the 'compression' as the fractional part of 'gapcount' divided by 100 minus 'gapbase'.
* Finally, it sets the 'rate' to the 'compression' value.
*/
static int cant_dummy_update(CANT *cant)
{
const uint32_t resolution = 5;
if (cant->dummy.canid == 0) return CANT_E_DCANID;
/* Update the deviation value and record it in 'cant->dummy.gapcount' */
if (cant->busload < cant->dummy.tarload)
{
if (cant->dummy.gapcount + resolution > cant->dummy.gapcount)
{
cant->dummy.gapcount += resolution;
}
}
else if (cant->busload > cant->dummy.tarload)
{
if (cant->dummy.gapcount >= resolution)
{
cant->dummy.gapcount -= resolution;
}
}
/* Update the basic sending number and compression rate of the sending point */
cant->dummy.gapbase = (uint32_t)(cant->dummy.gapcount / 100);
cant->dummy.compression = (double)cant->dummy.gapcount / 100.0 - cant->dummy.gapbase;
cant->dummy.rate = cant->dummy.compression;
return CANT_E_OK;
}
/**
* \brief Executes the dummy data sending process in the CAN device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \return CANT_E_OK if the execution is successful.
*
* This function first calculates the number of messages to send based on the 'gapbase'
* and 'compression' values in the dummy data. It adds the 'compression' value to the 'rate'.
* If the 'rate' exceeds 1.0, it subtracts 1.0 from the 'rate' and increments the number of messages to send.
* If there are messages to send, it constructs the dummy data buffer with relevant information such as
* the current count and bus load, calculates the CRC8 value for a part of the data buffer,
* and then calls the 'cant_transmit' function to send the dummy data. After sending each message,
* it increments the current count in the dummy data.
*/
static int cant_dummy_execute(CANT *cant)
{
uint32_t send = 0;
/* Calculate how many messages the current sending point needs to send */
send = cant->dummy.gapbase;
cant->dummy.rate += cant->dummy.compression;
if (cant->dummy.rate > 1.0)
{
cant->dummy.rate -= 1.0;
send++;
}
/* Evenly 'gapCount' the difference to each sending point for sending */
if (send > 0)
{
for (int i = 0; i < send; i++)
{
cant->dummy.data[2] = (cant->dummy.curcount >> 24) & 0xFF;
cant->dummy.data[3] = (cant->dummy.curcount >> 16) & 0xFF;
cant->dummy.data[4] = (cant->dummy.curcount >> 8) & 0xFF;
cant->dummy.data[5] = (cant->dummy.curcount) & 0xFF;
cant->dummy.data[6] = (cant->busload >> 8) & 0xFF;
cant->dummy.data[7] = (cant->busload) & 0xFF;
cant->dummy.data[1] = crc8(&(cant->dummy.data[2]), sizeof(cant->dummy.data) - 2);
cant_transmit(cant, cant->dummy.canid, cant->dummy.data, sizeof(cant->dummy.data));
cant->dummy.curcount++;
}
}
return CANT_E_OK;
}
/**
* \brief Verifies the received dummy data in the CAN device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID of the received data.
* \param data: Pointer to the received data buffer.
* \param length: The length of the received data buffer.
* \return CANT_E_OK if the verification is successful, CANT_E_DCANID if the CAN ID is invalid.
*
* This function first checks if the CAN ID in the verification settings is valid (not 0)
* and if it matches the received CAN ID. If not, it returns an error code. Then it extracts
* the type of the received data. If the type is 0x00, it extracts the expected CRC value,
* the count value, and the loading value from the data buffer. It calculates the CRC value
* for the relevant part of the data buffer and compares it with the expected CRC value.
* If they don't match, it increments the error count. It also checks if the received count
* value is consistent with the current count value in the verification settings. If not,
* it updates the last count value. If the verification is successful (no errors), it increments
* the verification count and updates the current count value.
*/
static int cant_dummy_verify(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length)
{
uint8_t type = 0;
uint8_t calcrc = 0;
uint8_t expcrc = 0;
uint8_t verifyfail = 0;
uint16_t loadding = 0;
uint32_t count = 0;
if (cant->verify.canid == 0 || canid != cant->verify.canid) return CANT_E_DCANID;
type = data[0];
if (type == 0x00)
{
expcrc = data[1];
count = data[2] << 24 | data[3] << 16 | data[4] << 8 | data[5];
loadding = data[6] << 8 | data[7];
calcrc = crc8(&data[2], length - 2);
if (calcrc != expcrc)
{
cant->verify.errcount++;
verifyfail++;
}
if (count != cant->verify.curcount + 1 && cant->verify.vercount != 0)
{
int32_t lstcount = count - cant->verify.curcount - 1;
if (lstcount > 0) cant->verify.lstcount += lstcount;
}
if (verifyfail == 0) cant->verify.vercount++;
cant->verify.curcount = count;
}
return CANT_E_OK;
}
/**
* \brief Sets the target bus load for the CAN device's dummy data.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param load: The target bus load value to be set.
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL.
*
* This function checks if the provided pointer is valid. If so, it ensures that the load
* value is within the valid range (less than or equal to 10000) and then sets the target
* bus load value in the dummy data part of the CANT structure.
*/
int cant_set_busload(CANT *cant, uint16_t load)
{
if (!cant) return CANT_E_INVALID;
if (load >= 10000) load = 10000;
cant->dummy.tarload = load;
return CANT_E_OK;
}
/**
* \brief Retrieves the target bus load for the CAN device's dummy data.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param load: Pointer to a variable where the target bus load will be stored.
* \return CANT_E_OK if the retrieval is successful, CANT_E_INVALID if the pointer is NULL,
* CANT_E_LOAD if the provided load pointer is NULL.
*
* This function checks if the provided pointers are valid. If so, it copies the target
* bus load value from the dummy data part of the CANT structure to the provided variable.
*/
int cant_get_busload(CANT *cant, uint16_t *load)
{
if (!cant) return CANT_E_INVALID;
if (!load) return CANT_E_LOAD;
*load = cant->dummy.tarload;
return CANT_E_OK;
}
/**
* \brief Sets the CAN ID for the CAN device's dummy data.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID value to be set.
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL,
* CANT_E_ECANID if the provided CAN ID is 0.
*
* This function checks if the provided pointer is valid. If so, it checks if the provided
* CAN ID is not 0. If valid, it sets the CAN ID in the dummy data part of the CANT structure.
*/
int cant_set_dummy_canid(CANT *cant, uint32_t canid)
{
if (!cant) return CANT_E_INVALID;
if (canid == 0) return CANT_E_ECANID;
cant->dummy.canid = canid;
return CANT_E_OK;
}
/**
* \brief Sets the CAN ID for the verification settings in the CAN device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID value to be set.
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL,
* CANT_E_ECANID if the provided CAN ID is 0.
*
* This function checks if the provided pointer is valid. If so, it checks if the provided
* CAN ID is not 0. If valid, it sets the CAN ID in the verification part of the CANT structure.
*/
int cant_set_verify_canid(CANT *cant, uint32_t canid)
{
if (!cant) return CANT_E_INVALID;
if (canid == 0) return CANT_E_ECANID;
cant->verify.canid = canid;
return CANT_E_OK;
}
/**
* \brief Transmits data over the CAN bus using the provided CAN device configuration.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID to which the data will be transmitted.
* \param data: Pointer to the data buffer to be transmitted.
* \param length: The length of the data buffer.
* \return CANT_E_OK if the transmission is successful, appropriate error codes otherwise.
* CANT_E_INVALID if the pointer to the CANT structure is NULL.
* CANT_E_DATA if the data pointer is NULL.
* CANT_E_TRANSMIT if the transmission function pointer in the configuration is NULL.
* CANT_E_TRANSFAIL if the actual transmission function returns a non-zero value.
*
* This function first checks the validity of the input parameters. If all parameters are valid,
* it calls the configured transmission function to send the data. If the transmission function
* returns a non-zero value, it indicates a transmission failure. After a successful transmission,
* it updates the bus load statistics for the CAN device.
*/
int cant_transmit(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length)
{
if (!cant) return CANT_E_INVALID;
if (!data) return CANT_E_DATA;
if (!cant->config.transmit) return CANT_E_TRANSMIT;
if (cant->config.transmit(canid, data, length) != 0) return CANT_E_TRANSFAIL;
cant_statistics_busload(cant, length);
return CANT_E_OK;
}
/**
* \brief Receives data over the CAN bus and performs related operations.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID from which the data is received.
* \param data: Pointer to the buffer where the received data will be stored.
* \param length: The length of the received data.
* \return CANT_E_OK if the operation is successful, CANT_E_INVALID if the pointer to the CANT structure is NULL.
*
* This function first checks the validity of the input pointer. If valid, it verifies the received
* dummy data using the 'cant_dummy_verify' function. Then it updates the bus load statistics.
* If the receive function pointer in the configuration is valid, it calls the receive function
* to handle the received data.
*/
int cant_receive(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length)
{
if (!cant) return CANT_E_INVALID;
cant_dummy_verify(cant, canid, data, length);
cant_statistics_busload(cant, length);
if (cant->config.receive) cant->config.receive(canid, data, length);
return CANT_E_OK;
}
/**
* \brief Initializes the CANT structure representing the CAN device.
* \param cant: Pointer to the CANT structure to be initialized.
* \return CANT_E_OK if the initialization is successful, CANT_E_INVALID if the pointer is NULL.
*
* This function initializes various fields in the CANT structure. It sets the bus load, period bits,
* and timestamp to 0. It also initializes the dummy data and verification related fields in the
* CANT structure to their default values. For the dummy data, it sets the CAN ID, compression,
* rate, target load, gap base, gap count, current count, and initializes the data buffer. For
* the verification part, it sets the CAN ID, error count, current count, verification count,
* and last count to 0.
*/
int cant_init(CANT *cant)
{
if (!cant) return CANT_E_INVALID;
cant->busload = 0;
cant->periodbits = 0;
cant->timestamp = 0;
/* Dummy data init */
cant->dummy.canid = 0;
cant->dummy.compression = 0.0;
cant->dummy.rate = 0.0;
cant->dummy.tarload = 0;
cant->dummy.gapbase = 0;
cant->dummy.gapcount = 0;
cant->dummy.curcount = 0;
cant->dummy.data[0] = 0x00;
for (uint8_t i = 8; i < sizeof(cant->dummy.data); i++)
{
cant->dummy.data[i] = i;
}
cant->verify.canid = 0;
cant->verify.errcount = 0;
cant->verify.curcount = 0;
cant->verify.vercount = 0;
cant->verify.lstcount = 0;
return CANT_E_OK;
}
/**
* \brief The main task function for the CAN device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \return CANT_E_OK if the task is executed successfully, CANT_E_INVALID if the pointer is NULL.
*
* This function is the main task handler for the CAN device. It updates the timestamp of the
* CAN device. Based on the timestamp value, it performs different operations at specific intervals.
* Every 1000 units of the timestamp, it calculates the bus load using the 'cant_calculate_busload'
* function. Every 10 units of the timestamp, it executes the dummy data sending operation using
* the 'cant_dummy_execute' function. Every 1000 units of the timestamp, it updates the dummy
* data parameters using the 'cant_dummy_update' function.
*/
int cant_task(CANT *cant)
{
if (!cant) return CANT_E_INVALID;
cant->timestamp += cant->config.period;
if (cant->timestamp >= 252000000) cant->timestamp = 0;
if (cant->timestamp % 1000 == 0)
{
cant_calculate_busload(cant);
}
if (cant->timestamp % 10 == 0)
{
cant_dummy_execute(cant);
}
if (cant->timestamp % 1000 == 0)
{
cant_dummy_update(cant);
}
return CANT_E_OK;
}

View File

@ -0,0 +1,204 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file cant.h
* \unit cant
* \brief This is a can test for C language
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/
#ifndef __cant_H
#define __cant_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/* Version infomation */
#define CANT_V_MAJOR 0
#define CANT_V_MINOR 1
#define CANT_V_PATCH 0
#define CANT_BAUDRATE_125K 0
#define CANT_BAUDRATE_250K 1
#define CANT_BAUDRATE_500K 2
#define CANT_BAUDRATE_800K 3
#define CANT_BAUDRATE_1000K 4
#define CANT_BAUDRATE_MAX 5
#define CANT_E_OK 0 // Success
#define CANT_E_INVALID 1 // Invalid `cant` pointer
#define CANT_E_DATA 2 // Invalid `data` pointer
#define CANT_E_LEN 3 // Invalid legth
#define CANT_E_NTEST 4 // CANT load testing has not started
#define CANT_E_OVER 5 // CANT load exceeds range
#define CANT_E_LINK 6 // Invalid `link` pointer
#define CANT_E_RATE 7 // Invalid `rate` pointer
#define CANT_E_TRANSMIT 8 // Invalid `transmit` pointer
#define CANT_E_TRANSFAIL 9 // Invalid `transmit` pointer
#define CANT_E_LOAD 10 // Invalid `load` pointer
#define CANT_E_DCANID 11 // Dummy canids that do not match
#define CANT_E_ECANID 12 // Error canid
// Function pointer type for functions related to CAN transfer.
// It takes a CAN ID (uint32_t), a pointer to data (uint8_t), and the length of the data (uint16_t) as parameters
// and returns an int. It can be used for functions like CAN transmission or reception.
typedef int (*cant_transfer_t)(uint32_t canid, uint8_t *data, uint16_t length);
// Structure definition for the CANT (presumably related to CAN bus) structure.
typedef struct
{
// Inner structure for configuration related to the CAN functionality.
struct
{
// Channel number of the CAN bus, represented as a uint8_t.
uint8_t channel;
// Baud rate of the CAN bus, represented as a uint8_t.
uint8_t baundrate;
// Call period for the 'cant_task()' function, represented as a uint16_t.
uint16_t period; // `cant_task()` call period
// Function pointer for CAN transmission. It points to a function that can transmit data on the CAN bus.
cant_transfer_t transmit; // can transmit the drive function
// Function pointer for CAN reception. It points to a function that can handle received data on the CAN bus.
cant_transfer_t receive; // can receive hook functions
} config;
// Bus load value of the CAN bus, represented as a uint16_t.
uint16_t busload;
// Value related to period bits (specific meaning depends on the context), represented as a uint32_t.
uint32_t periodbits;
// Timestamp value, represented as a uint32_t.
uint32_t timestamp;
// Inner structure for dummy data related to CAN operations.
struct
{
// CAN ID, represented as a uint32_t.
uint32_t canid;
// Compression factor, represented as a float.
float compression;
// Rate value, represented as a float.
float rate;
// Target load value, represented as a uint16_t.
uint16_t tarload;
// Base value for the gap (specific meaning depends on the context), represented as a uint32_t.
uint32_t gapbase;
// Count value for the gap (specific meaning depends on the context), represented as a uint32_t.
uint32_t gapcount;
// Current count value, represented as a uint32_t.
uint32_t curcount;
// Array to store data, with a size of 64 bytes.
uint8_t data[64];
} dummy;
// Inner structure for verification related data in CAN operations.
struct
{
// CAN ID, represented as a uint32_t.
uint32_t canid;
// Error count value, represented as a uint32_t.
uint32_t errcount;
// Current count value, represented as a uint32_t.
uint32_t curcount;
// Verification count value, represented as a uint32_t.
uint32_t vercount;
// Last count value, represented as a uint32_t.
uint32_t lstcount;
} verify;
} CANT;
/**
* \brief Sets the CAN ID for the CAN device's dummy data.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID value to be set.
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL,
* CANT_E_ECANID if the provided CAN ID is 0.
*
* This function checks if the provided pointer is valid.
*/
int cant_set_dummy_canid(CANT *cant, uint32_t canid);
/**
* \brief Sets the CAN ID for the verification settings in the CAN device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID value to be set.
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL,
* CANT_E_ECANID if the provided CAN ID is 0.
*
* This function checks if the provided pointer is valid.
*/
int cant_set_verify_canid(CANT *cant, uint32_t canid);
/**
* \brief Sets the target bus load for the CAN device's dummy data.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param load: The target bus load value to be set.
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL.
*
* This function checks if the provided pointer is valid.
*/
int cant_set_busload(CANT *cant, uint16_t load);
/**
* \brief Retrieves the target bus load for the CAN device's dummy data.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param load: Pointer to a variable where the target bus load will be stored.
* \return CANT_E_OK if the retrieval is successful, CANT_E_INVALID if the pointer is NULL,
* CANT_E_LOAD if the provided load pointer is NULL.
*
* This function checks if the provided pointers are valid.
*/
int cant_get_busload(CANT *cant, uint16_t *load);
/**
* \brief Transmits data over the CAN bus using the provided CAN device configuration.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID to which the data will be transmitted.
* \param data: Pointer to the data buffer to be transmitted.
* \param length: The length of the data buffer.
* \return CANT_E_OK if the transmission is successful, appropriate error codes otherwise.
* CANT_E_INVALID if the pointer to the CANT structure is NULL.
* CANT_E_DATA if the data pointer is NULL.
* CANT_E_TRANSMIT if the transmission function pointer in the configuration is NULL.
* CANT_E_TRANSFAIL if the actual transmission function returns a non-zero value.
*
* This function first checks the validity of the input parameters.
*/
int cant_transmit(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length);
/**
* \brief Receives data over the CAN bus and performs related operations.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \param canid: The CAN ID from which the data is received.
* \param data: Pointer to the buffer where the received data will be stored.
* \param length: The length of the received data.
* \return CANT_E_OK if the operation is successful, CANT_E_INVALID if the pointer to the CANT structure is NULL.
*
* This function first checks the validity of the input pointer.
*/
int cant_receive(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length);
/**
* \brief Initializes the CANT structure representing the CAN device.
* \param cant: Pointer to the CANT structure to be initialized.
* \return CANT_E_OK if the initialization is successful, CANT_E_INVALID if the pointer is NULL.
*
* This function initializes various fields in the CANT structure.
*/
int cant_init(CANT *cant);
/**
* \brief The main task function for the CAN device.
* \param cant: Pointer to the CANT structure representing the CAN device.
* \return CANT_E_OK if the task is executed successfully, CANT_E_INVALID if the pointer is NULL.
*
* This function is the main task handler for the CAN device. It updates the timestamp of the
* CAN device. Based on the timestamp value, it performs different operations at specific intervals.
*/
int cant_task(CANT *cant);
#endif

View File

@ -0,0 +1,780 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file slup.c
* \unit slup
* \brief This is a simple serial link universal protocol for C language
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/
#include "slup.h"
/*
| HEAD | SN | FrameType | Length | Data | Check | TAIL |
*/
// Macro to send a character and perform some additional operations.
// It calls the putc function pointer in the slup structure to send the character 'c'.
// It also logs the character in hexadecimal format using SLUP_DEBUG and updates the upload statistics.
#define sputc(c) do { (slup->putc)(c); SLUP_DEBUG("%02x ", (c)); slup_statis_upload(slup); } while (0)
// Macro to access the i-th element of the head mask in the SLUP configuration.
#define head(i) (slup->cfg.head[(i)])
// Macro to access the i-th element of the tail mask in the SLUP configuration.
#define tail(i) (slup->cfg.tail[(i)])
// Macro to access the i-th byte of the sequence number (sn) in the slup structure.
#define sn(i) (((uint8_t *)(&(slup->sn)))[(i)])
// Macro to access the i-th byte of the length variable.
#define len(i) (((uint8_t *)(&(length)))[(i)])
// Define states for the SLUP receive state machine.
#define SLUP_RX_STATE_HEAD 0
#define SLUP_RX_STATE_SN 1
#define SLUP_RX_STATE_FRAMETYPE 2
#define SLUP_RX_STATE_LENGTH 3
#define SLUP_RX_STATE_DATA 4
#define SLUP_RX_STATE_CHECK 5
#define SLUP_RX_STATE_TAIL 6
// Debug macro, currently defined as an empty operation.
// Can be redefined to use printf for debugging purposes.
#define SLUP_DEBUG(...)
// #define SLUP_DEBUG printf
/**
* \brief Pushes a character into the SLUP queue.
* \param queue: Pointer to the SLUP queue structure.
* \param data: The character to be pushed into the queue.
* \return 1 if the push operation is successful, 0 otherwise.
*
* This function adds a character to the SLUP queue. If the queue is not full,
* it simply adds the character to the tail of the queue and updates the tail index and size.
* If the queue is full, it overwrites the oldest element and updates both the head and tail indices.
*/
static int slup_queue_push(SLUP_QUEUE *queue, char data)
{
if (!queue) return 0;
if (queue->size < SLUP_RXQUE_SIZE)
{
queue->base[queue->tail] = data;
queue->tail = (queue->tail + 1) % SLUP_RXQUE_SIZE;
queue->size++;
}
else
{
queue->base[queue->tail] = data;
queue->tail = (queue->tail + 1) % SLUP_RXQUE_SIZE;
queue->head = (queue->head + 1) % SLUP_RXQUE_SIZE;
}
return 1;
}
/**
* \brief Pops a specified number of elements from the SLUP queue.
* \param queue: Pointer to the SLUP queue structure.
* \param count: The number of elements to be popped from the queue.
* \return 1 if the pop operation is successful, 0 otherwise.
*
* This function removes a specified number of elements from the SLUP queue.
* If the number of elements to be popped is greater than the current size of the queue,
* it will only pop the existing elements in the queue.
*/
static int slup_queue_pop(SLUP_QUEUE *queue, uint32_t count)
{
if (!queue) return 0;
if (queue->size == 0) return 0;
if (count == 0) return 0;
if (count > queue->size) count = queue->size;
queue->head = (queue->head + count) % SLUP_RXQUE_SIZE;
queue->size -= count;
return 1;
}
/**
* \brief Retrieves an element at a specified index from the SLUP queue.
* \param queue: Pointer to the SLUP queue structure.
* \param index: The index of the element to be retrieved.
* \return The character at the specified index if the queue is valid and not empty, 0 otherwise.
*
* This function returns the element at a given index in the SLUP queue.
* The index is calculated relative to the head of the queue.
*/
static char slup_queue_at(SLUP_QUEUE *queue, uint32_t index)
{
if (!queue) return 0;
if (queue->size == 0) return 0;
return queue->base[((queue->head + index) % SLUP_RXQUE_SIZE)];
}
/**
* \brief Update the upload statistics of the SLUP structure.
* \param slup: Pointer to the SLUP structure.
*
* This function increments the 'upbits' field in the SLUP structure by 8,
* indicating that 8 bits of data have been uploaded. It is called within the 'sputc' macro
* when sending data to keep track of the amount of uploaded data.
*/
static void slup_statis_upload(SLUP *slup)
{
slup->upbits += 8;
}
/**
* \brief Update the download statistics of the SLUP structure.
* \param slup: Pointer to the SLUP structure.
*
* This function increments the 'downbits' field in the SLUP structure by 8,
* indicating that 8 bits of data have been downloaded. It is used to track
* the amount of data received in the SLUP communication process.
*/
static void slup_statis_download(SLUP *slup)
{
slup->downbits += 8;
}
/**
* \brief Receive and process an SLUP package.
* \param slup: Pointer to the SLUP structure.
* \return SLUP_E_OK: Indicates that the package has been successfully received and processed.
*
* This function first checks the 'frame' field in the 'parser' of the SLUP structure.
* If the 'frame' is 0x00, it extracts the link information from the first byte of the 'buffer'.
* Depending on the result of the bitwise AND operation between the link information and SLUP_LINK_RX,
* it updates the 'link' field in the SLUP structure. If the 'frame' is not 0x00 and the 'receive'
* function pointer in the SLUP structure is valid, it calls the 'receive' function to handle
* the received data in the 'buffer' with the specified size 'bsize'.
*/
static int slup_receive_package(SLUP *slup)
{
if (slup->parser.frame == 0x00)
{
uint8_t link = 0;
link = slup->buffer[0];
if (link & SLUP_LINK_RX)
{
slup->link |= SLUP_LINK_TX;
}
else
{
slup->link &= (~SLUP_LINK_TX);
}
}
else
{
if (slup->receive)
{
(slup->receive)(slup->buffer, slup->bsize);
}
}
return SLUP_E_OK;
}
/**
* \brief Send an SLUP package.
* \param slup: Pointer to the SLUP structure.
* \param frame: The frame type of the package.
* \param data: Pointer to the data buffer of the package.
* \param length: The length of the data in the package.
* \return SLUP_E_OK: Indicates that the package has been successfully sent.
* SLUP_E_INVALID: Returned if the 'slup' pointer is NULL.
* SLUP_E_DATA: Returned if the 'data' pointer is NULL.
* SLUP_E_LEN: Returned if the 'length' is 0.
*
* This function constructs and sends an SLUP package. It first validates the input parameters.
* Then it sends each part of the package in order: the head, sequence number (SN), frame type,
* data length, data, checksum, and tail. The sequence number is incremented after being sent.
* The checksum is calculated using the 'check' function pointer in the SLUP structure.
*/
static int slup_send_package(SLUP *slup, uint8_t frame, uint8_t *data, uint16_t length)
{
uint32_t i = 0;
uint32_t check = 0;
if (!slup) return SLUP_E_INVALID;
if (!data) return SLUP_E_DATA;
if (length == 0) return SLUP_E_LEN;
/* Send the HEAD part of the package */
for (i = 0; i < slup->cfg.hsize; i++)
{
sputc(head(i));
}
/* Send the SN (Sequence Number) part of the package */
sputc(sn(0));
sputc(sn(1));
sputc(sn(2));
sputc(sn(3));
slup->sn++;
/* Send the FrameType part of the package */
sputc(frame);
/* Send the Length part of the package */
sputc(len(0));
sputc(len(1));
/* Calculate the checksum of the data */
check = (slup->check)(data, length);
/* Send the Data part of the package */
for (i = 0; i < length; i++)
{
sputc(data[i]);
}
/* Send the Check part of the package */
for (i = 0; i < slup->cfg.csize; i++)
{
sputc((((uint8_t *)(&(check)))[(i)]));
}
/* Send the TAIL part of the package */
for (i = 0; i < slup->cfg.tsize; i++)
{
sputc(tail(i));
}
return SLUP_E_OK;
}
/**
* \brief Parses the received data in the SLUP queue and processes it according to the SLUP protocol.
* \param slup: Pointer to the SLUP structure.
*
* This function is responsible for parsing the data in the SLUP queue. It iterates through all the
* data in the queue when there is data available. For each byte of data, it uses a state machine
* (implemented with a switch statement) to process the data based on the current state of the parser.
* The states represent different parts of the SLUP package such as the head, sequence number (SN),
* frame type, length, data, checksum, and tail. Once a complete package is parsed and verified,
* it calls the slup_receive_package function to handle the received package and resets the buffer
* and parser state for the next package.
*/
static void slup_parse_task(SLUP *slup)
{
uint32_t i = 0;
char data = 0;
if (slup->queue.size > 0)
{
for (i = 0; i < slup->queue.size; i++)
{
data = slup_queue_at(&slup->queue, i);
SLUP_DEBUG("%02x ", data);
switch (slup->parser.state)
{
case SLUP_RX_STATE_HEAD:
{
// If the current index for the head (hindex) is less than the configured head size
if (slup->parser.hindex < slup->cfg.hsize)
{
// If the received data byte matches the expected head byte at the current index
if (data == slup->cfg.head[slup->parser.hindex])
{
// Increment the head index
slup->parser.hindex++;
// If the head index reaches the configured head size, it means the head is fully received
if (slup->parser.hindex == slup->cfg.hsize)
{
slup->parser.hindex = 0;
SLUP_DEBUG(" [SLUP_RX_STATE_HEAD] OK\r\n");
// Move to the next state which is to receive the sequence number (SN)
slup->parser.state = SLUP_RX_STATE_SN;
}
}
else
{
// If the received byte doesn't match, reset the head index
slup->parser.hindex = 0;
}
}
else
{
}
} break;
case SLUP_RX_STATE_SN:
{
// If the current index for the sequence number (sindex) is less than the configured SN size
if (slup->parser.sindex < slup->cfg.ssize)
{
// Store the received data byte into the appropriate position of the parser's SN
((uint8_t *)(&(slup->parser.sn)))[slup->parser.sindex] = data;
// Increment the sequence number index
slup->parser.sindex++;
// If the sequence number index reaches the configured SN size, it means the SN is fully received
if (slup->parser.sindex == slup->cfg.ssize)
{
slup->parser.sindex = 0;
SLUP_DEBUG(" [SLUP_RX_STATE_SN] OK\r\n");
// Move to the next state which is to receive the frame type
slup->parser.state = SLUP_RX_STATE_FRAMETYPE;
}
}
else
{
}
} break;
case SLUP_RX_STATE_FRAMETYPE:
{
// Store the received data byte as the frame type
slup->parser.frame = data;
SLUP_DEBUG(" [SLUP_RX_STATE_FRAMETYPE] OK\r\n");
// Move to the next state which is to receive the length
slup->parser.state = SLUP_RX_STATE_LENGTH;
} break;
case SLUP_RX_STATE_LENGTH:
{
// If the current index for the length (lindex) is less than the size of a uint16_t (since length is uint16_t)
if (slup->parser.lindex < sizeof(uint16_t))
{
// Store the received data byte into the appropriate position of the parser's length
((uint8_t *)(&(slup->parser.length)))[slup->parser.lindex] = data;
// Increment the length index
slup->parser.lindex++;
// If the length index reaches the size of a uint16_t, it means the length is fully received
if (slup->parser.lindex == sizeof(uint16_t))
{
slup->parser.lindex = 0;
SLUP_DEBUG(" [SLUP_RX_STATE_LENGTH] OK\r\n");
// Move to the next state which is to receive the data
slup->parser.state = SLUP_RX_STATE_DATA;
}
}
else
{
}
} break;
case SLUP_RX_STATE_DATA:
{
// Store the received data byte into the buffer and increment the buffer size
slup->buffer[slup->bsize++] = data;
// If the current index for the data (dindex) is less than the received length
if (slup->parser.dindex < slup->parser.length)
{
// Increment the data index
slup->parser.dindex++;
// If the data index reaches the received length, it means the data is fully received
if (slup->parser.dindex == slup->parser.length)
{
slup->parser.dindex = 0;
SLUP_DEBUG(" [SLUP_RX_STATE_DATA] OK\r\n");
// Move to the next state which is to receive the checksum
slup->parser.state = SLUP_RX_STATE_CHECK;
}
}
else
{
}
} break;
case SLUP_RX_STATE_CHECK:
{
// If the current index for the checksum (cindex) is less than the configured checksum size
if (slup->parser.cindex < slup->cfg.csize)
{
// Store the received data byte into the appropriate position of the parser's checksum
((uint8_t *)(&(slup->parser.check)))[slup->parser.cindex] = data;
// Increment the checksum index
slup->parser.cindex++;
// If the checksum index reaches the configured checksum size, it means the checksum is fully received
if (slup->parser.cindex == slup->cfg.csize)
{
slup->parser.cindex = 0;
uint32_t cvalue = 0;
// Calculate the checksum of the received data in the buffer
cvalue = (slup->check)(slup->buffer, slup->bsize);
// Compare the calculated checksum with the received checksum
if (memcmp(&cvalue, &slup->parser.check, slup->cfg.csize) == 0)
{
SLUP_DEBUG(" [SLUP_RX_STATE_CHECK] OK\r\n");
// Move to the next state which is to receive the tail
slup->parser.state = SLUP_RX_STATE_TAIL;
}
else
{
// If the checksums don't match, reset the buffer size for the next reception
slup->bsize = 0;
// Reset the parser state to start receiving a new package from the head
slup->parser.state = SLUP_RX_STATE_HEAD;
}
}
}
else
{
}
} break;
case SLUP_RX_STATE_TAIL:
{
// If the current index for the tail (tindex) is less than the configured tail size
if (slup->parser.tindex < slup->cfg.tsize)
{
// If the received data byte matches the expected tail byte at the current index
if (data == slup->cfg.tail[slup->parser.tindex])
{
// Increment the tail index
slup->parser.tindex++;
// If the tail index reaches the configured tail size, it means the tail is fully received
if (slup->parser.tindex == slup->cfg.tsize)
{
slup->parser.tindex = 0;
SLUP_DEBUG(" [SLUP_RX_STATE_TAIL] OK\r\n");
// Call the function to handle the received package
slup_receive_package(slup);
// Reset the buffer size for the next reception
slup->bsize = 0;
// Reset the parser state to start receiving a new package from the head
slup->parser.state = SLUP_RX_STATE_HEAD;
}
}
else
{
// If the received byte doesn't match, reset the tail index and the parser state to start from the head
slup->parser.tindex = 0;
slup->parser.state = SLUP_RX_STATE_HEAD;
}
}
else
{
}
} break;
default:
break;
}
}
// Pop all the data from the queue after processing
slup_queue_pop(&slup->queue, 0xFFFFF);
}
}
/**
* \brief Calculate the upload and download rates and reset the bit counters.
* \param slup: Pointer to the SLUP structure.
*
* This function is responsible for calculating the upload and download rates.
* It copies the current values of 'upbits' and 'downbits' to 'uprate' and 'downrate' respectively.
* Then it resets 'upbits' and 'downbits' to zero, preparing for the next rate calculation.
*/
static void slup_calrate_task(SLUP *slup)
{
slup->uprate = slup->upbits;
slup->downrate = slup->downbits;
slup->upbits = 0;
slup->downbits = 0;
}
/**
* \brief Update the dummy data parameters based on the upload rate.
* \param slup: Pointer to the SLUP structure.
*
* This function updates the 'gapcount', 'gapbase', 'compression', and 'rate' fields
* in the 'dummy' structure of the SLUP object. It first calculates the deviation
* between the upload rate and the target rate. If the upload rate is less than the target,
* it increases the 'gapcount' by a fixed resolution value. If the upload rate is greater
* than the target, it decreases the 'gapcount' by the same resolution value.
* Then it calculates the 'gapbase' as the integer part of 'gapcount' divided by 100,
* and the 'compression' as the fractional part of 'gapcount' divided by 100 minus 'gapbase'.
* Finally, it sets the 'rate' to the 'compression' value.
*/
static void slup_dummy_update(SLUP *slup)
{
const uint32_t resolution = 5;
/* Update the deviation value and record it in 'slup->dummy.gapcount' */
if (slup->uprate < slup->dummy.target)
{
if (slup->dummy.gapcount + resolution > slup->dummy.gapcount)
{
slup->dummy.gapcount += resolution;
}
}
else if (slup->uprate > slup->dummy.target)
{
if (slup->dummy.gapcount >= resolution)
{
slup->dummy.gapcount -= resolution;
}
}
/* Update the basic sending number and compression rate of the sending point */
slup->dummy.gapbase = (uint32_t)(slup->dummy.gapcount / 100);
slup->dummy.compression = (double)slup->dummy.gapcount / 100.0 - slup->dummy.gapbase;
slup->dummy.rate = slup->dummy.compression;
}
/**
* \brief Execute the dummy data sending process based on the calculated parameters.
* \param slup: Pointer to the SLUP structure.
*
* This function calculates the number of messages to send based on the 'gapbase'
* and 'compression' values in the 'dummy' structure of the SLUP object. It adds the
* 'compression' value to the 'rate'. If the 'rate' exceeds 1.0, it subtracts 1.0 from
* the 'rate' and increments the number of messages to send.
* If there are messages to send, it calls the 'slup_send' function to send the dummy data
* in the 'dummy' structure 'send' times.
*/
static void slup_dummy_execute(SLUP *slup)
{
uint32_t send = 0;
/* Calculate how many messages the current sending point needs to send */
send = slup->dummy.gapbase;
slup->dummy.rate += slup->dummy.compression;
if (slup->dummy.rate > 1.0)
{
slup->dummy.rate -= 1.0;
send++;
}
/* Evenly 'gapCount' the difference to each sending point for sending */
if (send > 0)
{
for (int i = 0; i < send; i++)
{
slup_send(slup, slup->dummy.data, sizeof(slup->dummy.data));
}
}
}
/**
* \brief Sends a heartbeat message in the SLUP protocol.
* \param slup: Pointer to the SLUP structure.
* \return SLUP_E_OK if the message is successfully sent, appropriate error code otherwise.
*
* This function constructs a heartbeat message. It sets the first byte of the data buffer
* to the current link status from the SLUP structure. Then it calls the slup_send_package
* function with a frame type of 0x00 to send the message.
*/
static int slup_send_heart(SLUP *slup)
{
uint8_t data[SLUP_SN_MAX];
uint16_t length = 0;
data[0] = slup->link;
length = 1;
return slup_send_package(slup, 0x00, data, length);
}
/**
* \brief Initializes the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \return SLUP_E_OK if the initialization is successful, SLUP_E_INVALID if the pointer is NULL.
*
* This function initializes various fields in the SLUP structure. It sets the buffer size (bsize),
* sequence number (sn), and timestamp to 0. It also initializes the queue's head, tail, and size to 0,
* and clears the parser structure using memset.
*/
int slup_init(SLUP *slup)
{
if (!slup) return SLUP_E_INVALID;
// memset(slup, 0, sizeof(SLUP));
slup->bsize = 0;
slup->sn = 0;
slup->timestamp = 0;
/* Init queue */
slup->queue.head = 0;
slup->queue.tail = 0;
slup->queue.size = 0;
/* Init parser */
memset(&slup->parser, 0, sizeof(SLUP_PARSER));
return SLUP_E_OK;
}
/**
* \brief Sends data in the SLUP protocol with a frame type of 0x01.
* \param slup: Pointer to the SLUP structure.
* \param data: Pointer to the data buffer to be sent.
* \param length: The length of the data buffer.
* \return SLUP_E_OK if the data is successfully sent, appropriate error code otherwise.
*
* This function simply calls the slup_send_package function with a frame type of 0x01
* to send the provided data.
*/
int slup_send(SLUP *slup, uint8_t *data, uint16_t length)
{
return slup_send_package(slup, 0x01, data, length);
}
/**
* \brief Handles the reception of a single character in the SLUP system.
* \param slup: Pointer to the SLUP structure.
* \param c: The received character.
*
* This function updates the link status to indicate a received character (sets SLUP_LINK_RX),
* clears the silent flag, updates the download statistics, and pushes the received character
* into the queue.
*/
void slup_getc(SLUP *slup, char c)
{
if (!slup) return;
slup->link |= SLUP_LINK_RX;
slup->silent = 0;
slup_statis_download(slup);
slup_queue_push(&slup->queue, c);
}
/**
* \brief Retrieves the current link status from the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \param link: Pointer to a variable where the link status will be stored.
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
*
* This function checks if the provided pointers are valid. If so, it copies the current
* link status from the SLUP structure to the provided variable.
*/
int slup_link_status(SLUP *slup, uint8_t *link)
{
if (!slup) return SLUP_E_INVALID;
if (!link) return SLUP_E_LINK;
*link = slup->link;
return SLUP_E_OK;
}
/**
* \brief Retrieves the upload rate from the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \param rate: Pointer to a variable where the upload rate will be stored.
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
*
* This function checks if the provided pointers are valid. If so, it copies the current
* upload rate from the SLUP structure to the provided variable.
*/
int slup_upload_rate(SLUP *slup, uint32_t *rate)
{
if (!slup) return SLUP_E_INVALID;
if (!rate) return SLUP_E_RATE;
*rate = slup->uprate;
return SLUP_E_OK;
}
/**
* \brief Retrieves the download rate from the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \param rate: Pointer to a variable where the download rate will be stored.
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
*
* This function checks if the provided pointers are valid. If so, it copies the current
* download rate from the SLUP structure to the provided variable.
*/
int slup_download_rate(SLUP *slup, uint32_t *rate)
{
if (!slup) return SLUP_E_INVALID;
if (!rate) return SLUP_E_RATE;
*rate = slup->downrate;
return SLUP_E_OK;
}
/**
* \brief Sets the target rate for the dummy data in the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \param rate: The target rate value to be set.
* \return SLUP_E_OK if the setting is successful, SLUP_E_INVALID if the pointer is NULL.
*
* This function checks if the provided pointer is valid. If so, it sets the target rate
* in the dummy data part of the SLUP structure to the provided value.
*/
int slup_set_dummy(SLUP *slup, uint32_t rate)
{
if (!slup) return SLUP_E_INVALID;
slup->dummy.target = rate;
return SLUP_E_OK;
}
/**
* \brief The main task function for the SLUP system.
* \param slup: Pointer to the SLUP structure.
*
* This function is the main task handler for the SLUP system. It updates the timestamp,
* and based on the timestamp value, performs various operations such as sending heartbeat
* messages, calculating rates, executing dummy data sending, and updating dummy data parameters.
* It also calls the slup_parse_task function to parse the received data in the queue.
*/
void slup_task(SLUP *slup)
{
if (!slup) return;
slup->timestamp += slup->period;
if (slup->timestamp >= 252000000) slup->timestamp = 0;
if (slup->timestamp % 500 == 0)
{
if (slup->silent < 3)
{
slup->silent++;
}
else
{
slup->link &= (~SLUP_LINK_RX);
}
slup_send_heart(slup);
}
if (slup->timestamp % 1000 == 0)
{
slup_calrate_task(slup);
}
if (slup->timestamp % 10 == 0)
{
slup_dummy_execute(slup);
}
if (slup->timestamp % 1000 == 0)
{
slup_dummy_update(slup);
}
slup_parse_task(slup);
}

View File

@ -0,0 +1,279 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file slup.h
* \unit slup
* \brief This is a simple serial link universal protocol for C language
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/
#ifndef __slup_H
#define __slup_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/* Version infomation */
#define SLUP_V_MAJOR 0
#define SLUP_V_MINOR 1
#define SLUP_V_PATCH 0
// Maximum size of the head part in the SLUP structure, set to 4 bytes.
#define SLUP_HEAD_MAX 4
// Maximum size of the tail part in the SLUP structure, set to 4 bytes.
#define SLUP_TAIL_MAX 4
// Maximum size of the check code part in the SLUP structure, set to 4 bytes.
#define SLUP_CHECK_MAX 4
// Maximum size of the sequence number (SN) part in the SLUP structure, set to 4 bytes.
#define SLUP_SN_MAX 4
// Maximum size of the frame in the SLUP structure, set to 4095 bytes.
#define SLUP_FRAME_MAX 4095
// Size of the receive queue in the SLUP structure, set to 1024 bytes.
#define SLUP_RXQUE_SIZE 1024
// Success code, indicating that an operation has been completed successfully.
#define SLUP_E_OK 0
// Error code, indicating that the 'slup' pointer is invalid.
#define SLUP_E_INVALID 1
// Error code, indicating that the 'data' pointer is invalid.
#define SLUP_E_DATA 2
// Error code, indicating that the length value is invalid.
#define SLUP_E_LEN 3
// Error code, indicating that the SLUP load testing has not started.
#define SLUP_E_NTEST 4
// Error code, indicating that the SLUP load exceeds the defined range.
#define SLUP_E_OVER 5
// Error code, indicating that the 'link' pointer is invalid.
#define SLUP_E_LINK 6
// Error code, indicating that the 'rate' pointer is invalid.
#define SLUP_E_RATE 7
// Link status value representing the link is in the down state.
#define SLUP_LINK_DOWN 0x00
// Link status value representing the link is in the transmit (TX) state.
#define SLUP_LINK_TX 0x01
// Link status value representing the link is in the receive (RX) state.
#define SLUP_LINK_RX 0x02
// Link status value representing the link is in the up state.
#define SLUP_LINK_UP 0x03
// Function pointer type for a function that writes a character.
// It takes a character 'c' as an argument and returns an 'int'.
typedef int (*slup_putc_t)(char c);
// Function pointer type for a function that checks data.
// It takes a pointer to an array of 'uint8_t' data and its length (in 'uint16_t') as arguments
// and returns a 'uint32_t'.
typedef uint32_t (*slup_check_t)(uint8_t *data, uint16_t length);
// Function pointer type for a function that receives data.
// It takes a pointer to an array of 'uint8_t' data and its length (in 'uint16_t') as arguments
// and returns an 'int'.
typedef int (*slup_receive_t)(uint8_t *data, uint16_t length);
// Structure definition for SLUP configuration.
typedef struct
{
// Array to store the head mask, with a maximum size defined by SLUP_HEAD_MAX.
uint8_t head[SLUP_HEAD_MAX]; /**< head mask */
// Array to store the tail mask, with a maximum size defined by SLUP_TAIL_MAX.
uint8_t tail[SLUP_TAIL_MAX]; /**< tail mask */
// Size of the head part, stored in 4 bits.
uint8_t hsize : 4; /**< head size */
// Size of the tail part, stored in 4 bits.
uint8_t tsize : 4; /**< tail size */
// Size of the check code part, stored in 4 bits.
uint8_t csize : 4; /**< check code size */
// Size of the sequence number (sn) part, stored in 4 bits.
uint8_t ssize : 4; /**< sn size */
} SLUP_CFG;
/* queue type define */
// Structure definition for an SLUP queue.
typedef struct
{
// Array that serves as the base address for the data in the queue,
// with a size defined by SLUP_RXQUE_SIZE.
char base[SLUP_RXQUE_SIZE]; /* base address of data */
// Total size of the queue.
uint32_t size; /* size of queue */
// Index of the queue head.
uint32_t head; /* index of queue head */
// Index of the queue tail.
uint32_t tail; /* index of queue tail */
} SLUP_QUEUE;
// Structure definition for an SLUP parser.
typedef struct
{
// Current state of the parser.
uint8_t state;
// Index related to the head part, stored in 4 bits.
uint8_t hindex : 4;
// Index related to the tail part, stored in 4 bits.
uint8_t tindex : 4;
// Index related to the check code part, stored in 4 bits.
uint8_t cindex : 4;
// Index related to the sequence number (sn) part, stored in 4 bits.
uint8_t sindex : 4;
// Index related to the length part, stored in 2 bits.
uint8_t lindex : 2;
// Flag indicating the frame status.
uint8_t frame;
// Length of the data being processed.
uint16_t length;
// Index within the data.
uint16_t dindex;
// Check value for the data.
uint32_t check;
// Sequence number.
uint32_t sn;
} SLUP_PARSER;
// Structure definition for an SLUP dummy data structure.
typedef struct
{
// Compression factor, represented as a floating-point number.
float compression;
// Rate value, represented as a floating-point number.
float rate;
// Target value, represented as a 32-bit unsigned integer.
uint32_t target;
// Base value for the gap, represented as a 32-bit unsigned integer.
uint32_t gapbase;
// Count value for the gap, represented as a 32-bit unsigned integer.
uint32_t gapcount;
// Array to store data, with a size of 64 bytes.
uint8_t data[64];
} SLUP_DUMMY;
// Structure definition for the main SLUP structure that combines multiple components.
typedef struct
{
// SLUP configuration structure.
SLUP_CFG cfg;
// SLUP queue structure.
SLUP_QUEUE queue;
// SLUP parser structure.
SLUP_PARSER parser;
// SLUP dummy data structure.
SLUP_DUMMY dummy;
// Buffer to store data, with a maximum size defined by SLUP_FRAME_MAX.
uint8_t buffer[SLUP_FRAME_MAX];
// Size of the buffer.
uint32_t bsize;
// Sequence number.
uint32_t sn;
// Link status flag.
uint8_t link;
// Silent status flag.
uint8_t silent;
// Period value, represented as a 16-bit unsigned integer.
uint16_t period;
// Timestamp value, represented as a 32-bit unsigned integer.
uint32_t timestamp;
// Number of bits for the up direction, represented as a 32-bit unsigned integer.
uint32_t upbits;
// Number of bits for the down direction, represented as a 32-bit unsigned integer.
uint32_t downbits;
// Rate value for the up direction, represented as a 32-bit unsigned integer.
uint32_t uprate;
// Rate value for the down direction, represented as a 32-bit unsigned integer.
uint32_t downrate;
// Function pointer of type 'slup_putc_t' for writing a character.
slup_putc_t putc;
// Function pointer of type 'slup_check_t' for checking data.
slup_check_t check;
// Function pointer of type 'slup_receive_t' for receiving data.
slup_receive_t receive;
} SLUP;
/**
* \brief Initializes the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \return SLUP_E_OK if the initialization is successful, SLUP_E_INVALID if the pointer is NULL.
*
* This function initializes various fields in the SLUP structure.
*/
int slup_init(SLUP *slup);
/**
* \brief Sends data in the SLUP protocol with a frame type of 0x01.
* \param slup: Pointer to the SLUP structure.
* \param data: Pointer to the data buffer to be sent.
* \param length: The length of the data buffer.
* \return SLUP_E_OK if the data is successfully sent, appropriate error code otherwise.
*
* This function simply calls the slup_send_package function with a frame type of 0x01
* to send the provided data.
*/
int slup_send(SLUP *slup, uint8_t *data, uint16_t length);
/**
* \brief Retrieves the current link status from the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \param link: Pointer to a variable where the link status will be stored.
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
*
* This function checks if the provided pointers are valid.
*/
int slup_link_status(SLUP *slup, uint8_t *link);
/**
* \brief Retrieves the upload rate from the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \param rate: Pointer to a variable where the upload rate will be stored.
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
*
* This function checks if the provided pointers are valid.
*/
int slup_upload_rate(SLUP *slup, uint32_t *rate);
/**
* \brief Retrieves the download rate from the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \param rate: Pointer to a variable where the download rate will be stored.
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
*
* This function checks if the provided pointers are valid.
*/
int slup_download_rate(SLUP *slup, uint32_t *rate);
/**
* \brief Sets the target rate for the dummy data in the SLUP structure.
* \param slup: Pointer to the SLUP structure.
* \param rate: The target rate value to be set.
* \return SLUP_E_OK if the setting is successful, SLUP_E_INVALID if the pointer is NULL.
*
* This function checks if the provided pointer is valid.
*/
int slup_set_dummy(SLUP *slup, uint32_t rate);
/**
* \brief Handles the reception of a single character in the SLUP system.
* \param slup: Pointer to the SLUP structure.
* \param c: The received character.
*
* This function updates the link status to indicate a received character (sets SLUP_LINK_RX),
* clears the silent flag, updates the download statistics, and pushes the received character
* into the queue.
*/
void slup_getc(SLUP *slup, char c);
/**
* \brief The main task function for the SLUP system.
* \param slup: Pointer to the SLUP structure.
*
* This function is the main task handler for the SLUP system. It updates the timestamp,
* and based on the timestamp value, performs various operations such as sending heartbeat
* messages, calculating rates, executing dummy data sending, and updating dummy data parameters.
* It also calls the slup_parse_task function to parse the received data in the queue.
*/
void slup_task(SLUP *slup);
#endif

View File

@ -6,7 +6,7 @@
* \unit floatl * \unit floatl
* \brief This is a simple large float number calculate module for C language * \brief This is a simple large float number calculate module for C language
* \author Lamdonn * \author Lamdonn
* \version v1.1.0 * \version v1.1.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -1731,6 +1731,172 @@ floatl floatl_from_d(double value)
return result; return result;
} }
/**
* \brief Converts a custom floating-point type (floatl) to a double precision floating-point number.
* \param a: The input value of the custom floating-point type (floatl) that needs to be converted.
* \return A double precision floating-point number which is the converted result of the input floatl value.
*
* This function is mainly responsible for converting a value of the custom floating-point type 'floatl'
* to a standard double precision floating-point number. It first checks if the 'floatl' type uses 64 bits
* for storage by checking the preprocessor macro 'FLOATL_USE_64BITS'. If it does, it simply copies the memory
* of the 'floatl' variable to a 'double_u' structure and returns the corresponding floating-point value.
* Otherwise, it proceeds with a series of operations for the conversion.
*/
double floatl_to_d(floatl a)
{
double_u du;
// If the 'floatl' type uses 64 bits (same as the size of 'double'), directly copy the memory
// and return the stored value as a 'double'.
#if defined(FLOATL_USE_64BITS)
memcpy(&du, &a, sizeof(double));
return du.float_;
#endif
// Extract the exponent part from the input 'floatl' value. Subtract the middle value of the exponent
// range defined for 'floatl' (__FLOATL_EXP_MID_VALUE__) to get a relative exponent value.
int32_t exp = a.exponent - __FLOATL_EXP_MID_VALUE__;
// Create a new 'floatl' variable'mant' initialized with the input 'a', and then set its sign and exponent
// to 0 to isolate just the mantissa part of the original 'floatl' value.
floatl mant = a; mant.sign = 0; mant.exponent = 0U;
// Set the sign of the result to be the same as the sign of the input 'floatl' value.
bool sign = a.sign;
// Handle special cases such as NaN (Not a Number) and infinity. If the exponent of the input 'floatl'
// is equal to the whole value of the exponent range defined for 'floatl' (__FLOATL_EXP_WHL_VALUE__),
// it indicates a special value.
if (a.exponent == __FLOATL_EXP_WHL_VALUE__)
{
// If the mantissa has a sign (checked by 'floatl_int_sign' function), set the high and low parts
// of the 'int_' structure within 'double_u' to all 1s (representing a special NaN or infinity case).
if (floatl_int_sign(mant))
{
du.int_.high = 0xFFFFFFFF;
du.int_.low = 0xFFFFFFFF;
}
else
{
// Otherwise, set the high and low parts to 0.
du.int_.high = 0;
du.int_.low = 0;
}
// Set the sign and exponent of the 'double_u' structure according to the rules for special values.
du.sign = sign;
du.exponent = 2047;
// Return the resulting 'double' value representing the special value.
return du.float_;
}
// Adjust the extracted exponent to fit within the exponent range of a standard double precision
// floating-point number (-1022 to 1023). Add 1023 to convert it to the biased exponent form
// used by doubles.
exp += 1023;
// Check for overflow. If the adjusted exponent is greater than 2047 (the maximum exponent value
// for a double), it means an overflow has occurred, and we return positive or negative infinity
// depending on the sign.
if (exp > 2047)
{
du.int_.high = 0;
du.int_.low = 0;
du.sign = sign;
du.exponent = 2047;
return du.float_;
}
// Check for underflow. If the adjusted exponent is less than 0, it means an underflow has occurred,
// and we return 0.
else if (exp < 0)
{
du.int_.high = 0;
du.int_.low = 0;
du.sign = sign;
du.exponent = 0;
return du.float_;
}
// Add the hidden bit (which is 1 in the normalized representation of floating-point numbers)
// to the mantissa. Use the 'floatl_int_or' function to perform a bitwise OR operation to set the
// hidden bit.
mant = floatl_int_or(mant, __FLOATL_HIDE_MANT_BIT__);
// Calculate the number of bits that need to be discarded from the mantissa to fit it into the
// 52-bit mantissa of a double precision floating-point number. Subtract 52 from the total number
// of mantissa bits defined for 'floatl' (__FLOATL_MANT_BITS__).
int32_t discard_bits = __FLOATL_MANT_BITS__ - 52;
// Keep only the highest 52 bits of the mantissa. Use the 'floatl_int_shr' function to shift
// the mantissa right by the number of bits to be discarded.
floatl double_mant = floatl_int_shr(mant, discard_bits);
// Create a 'floatl' variable 'int1' representing the integer value 1. Initialize its first 32-bit
// part (u32[0]) to 1.
floatl int1 = __FLOATL_CONST_0__; int1.u32[0] = 1;
// Get the highest bit among the bits to be discarded. Use the 'floatl_int_shl' function to shift
// 'int1' left by the number of bits equal to the number of bits to be discarded minus 1.
// This bit will be used for rounding purposes.
floatl high_bit = floatl_int_shl(int1, discard_bits - 1);
// Get the bits that will be discarded from the mantissa. Use the 'floatl_int_and' function to perform
// a bitwise AND operation between the original mantissa and a value that represents the bits to be
// discarded (formed by first shifting 'int1' left by the number of bits to be discarded and then
// decrementing it using 'floatl_int_dec').
floatl round_bits = floatl_int_and(mant, floatl_int_dec(floatl_int_shl(int1, discard_bits)));
// Determine whether rounding is needed. If the bits to be discarded ('round_bits') are greater than
// the highest bit among them ('high_bit'), or if they are equal and the lowest bit of the remaining
// mantissa ('double_mant') is 1 (checked by 'floatl_int_sign' function), then rounding is required.
if (floatl_int_cmp(round_bits, high_bit) > 0 ||
(floatl_int_cmp(round_bits, high_bit) == 0 && floatl_int_sign(floatl_int_and(double_mant, int1))))
{
// Perform rounding by incrementing the remaining mantissa. Use the 'floatl_int_inc' function.
floatl_int_inc(double_mant);
// Check if the increment of the mantissa causes a carry to the exponent part. If the sign bit
// of the result of a bitwise AND operation between the remaining mantissa and a value representing
// the bit position 2 bits higher than the hidden bit position (shifted 'int1' left by 52 + 2)
// is 1, it means there is a carry to the exponent.
if (floatl_int_sign(floatl_int_and(double_mant, floatl_int_shl(int1, 52 + 2))))
{
// If there is a carry to the exponent, shift the remaining mantissa right by 1 bit
// to make room for the carry in the exponent. Use the 'floatl_int_shr' function.
double_mant = floatl_int_shr(double_mant, 1);
// Increment the exponent by 1.
exp += 1;
// Check for a secondary overflow of the exponent. If the exponent after the increment is
// greater than 2047, it means another overflow has occurred, and we return positive or
// negative infinity depending on the sign.
if (exp > 2047)
{
du.int_.high = 0;
du.int_.low = 0;
du.sign = sign;
du.exponent = 2047;
return du.float_;
}
}
}
// Combine the processed mantissa, sign, and exponent to form the final 'double' value.
// Set the high and low parts of the 'int_' structure within 'double_u' to the corresponding parts
// of the remaining mantissa.
du.int_.high = double_mant.u32[1];
du.int_.low = double_mant.u32[0];
// Set the sign of the 'double_u' structure.
du.sign = sign;
// Set the exponent of the 'double_u' structure.
du.exponent = exp;
// Return the final converted double precision floating-point number.
return du.float_;
}
/** /**
* \brief Get the sign of a 'floatl' number. * \brief Get the sign of a 'floatl' number.
* \param[in] a: The 'floatl' number whose sign is to be determined. * \param[in] a: The 'floatl' number whose sign is to be determined.
@ -3075,7 +3241,7 @@ static char floatlIntLowChar(floatl *num)
// Get the character of the lowest - order digit of the integer part // Get the character of the lowest - order digit of the integer part
c = gChar(*num); c = gChar(*num);
// Divide the 'floatl' number by 10 to move to the next digit // Divide the 'floatl' number by 10 to move to the next digit
*num = floatl_div(*num, floatl_from_f(10.0)); *num = floatl_div(*num, floatl_from_d(10.0));
} }
return c; return c;
@ -3101,7 +3267,7 @@ static char floatlDitHighChar(floatl *num)
if (ST1(temp)) // The decimal part is not 0 if (ST1(temp)) // The decimal part is not 0
{ {
// Multiply the 'floatl' number by 10 to get the next digit // Multiply the 'floatl' number by 10 to get the next digit
temp = floatl_mul(temp, floatl_from_f(10.0)); temp = floatl_mul(temp, floatl_from_d(10.0));
// Get the character of the highest - order digit of the new number // Get the character of the highest - order digit of the new number
c = gChar(temp); c = gChar(temp);
// Update the original 'floatl' number // Update the original 'floatl' number

View File

@ -7,7 +7,7 @@
* \unit floatl * \unit floatl
* \brief This is a simple large float number calculate module for C language * \brief This is a simple large float number calculate module for C language
* \author Lamdonn * \author Lamdonn
* \version v1.1.0 * \version v1.1.1
* \license GPL-2.0 * \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn. * \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/ ********************************************************************************************************/
@ -25,7 +25,7 @@
#define FLOATL_V_MAJOR 1 #define FLOATL_V_MAJOR 1
#define FLOATL_V_MINOR 1 #define FLOATL_V_MINOR 1
#define FLOATL_V_PATCH 0 #define FLOATL_V_PATCH 1
/** /**
* \brief Common constant definitions * \brief Common constant definitions
@ -151,9 +151,11 @@ floatl floatl_round(floatl a);
// Conversion functions // Conversion functions
// floatl_from: Converts a string to a floatl number // floatl_from: Converts a string to a floatl number
// floatl_from_d: Converts a double-precision floating-point number (double) to a floatl number // floatl_from_d: Converts a double-precision floating-point number (double) to a floatl number
// floatl_to_d: Converts a floatl to a double-precision floating-point number (double) number
floatl floatl_from(const char *str); floatl floatl_from(const char *str);
floatl floatl_from_d(double value); floatl floatl_from_d(double value);
double floatl_to_d(floatl a);
// Output function // Output function
// Converts a floatl number to a string according to the specified format and stores it in the buffer // Converts a floatl number to a string according to the specified format and stores it in the buffer

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,532 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file coroutine.h
* \unit coroutine
* \brief This is a C language coroutine library
* \author Lamdonn
* \version v0.2.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#ifndef __coroutine_H
#define __coroutine_H
#ifdef __cplusplus
extern "C" {
#endif
/* Version infomation */
#define COROUTINE_V_MAJOR 0
#define COROUTINE_V_MINOR 2
#define COROUTINE_V_PATCH 0
#include "coroutine_cfg.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include <setjmp.h>
/**
* \brief: Coroutine task
*/
typedef struct CoTask* CoTask_t;
/**
* \brief: Coroutine timer
*/
typedef struct CoTimer* CoTimer_t;
/**
* \brief: Coroutine task entry function
* \param arg: Task argument address
* \return: Task return value address
*/
typedef void *(*CoTaskEntry_t)(void *arg);
/**
* \brief: Coroutine timer entry function
*/
typedef void (*CoTimerEntry_t)(void);
/**
* \brief: Coroutine lock function
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, lock
*/
typedef void (*CoLock_t)(void);
/**
* \brief: Coroutine unlock function
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, unlock
*/
typedef void (*CoUnlock_t)(void);
/**
* \brief: Coroutine malloc function
* \param size: Allocate memory size
* \return: Allocate memory address
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, malloc
*/
typedef void *(*CoMalloc_t)(size_t size);
/**
* \brief: Coroutine free function
* \param block: Free memory address
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, free
*/
typedef void (*CoFree_t)(void *block);
/**
* \brief: Coroutine tick function
* \return: Current tick value
* \note: The time base on which the coroutine schedule depends, such as system tick.
*/
typedef uint64_t (*CoTick_t)(void);
/**
* \brief: Coroutine event
* \note: Used for synchronization between coroutines
*/
typedef struct
{
uint32_t flag; /**< Event flag, 32 bit storage, each bit can individually represent an event */
} CoEvent;
/**
* \brief: Coroutine scheduler initialize parameter
* \note: Coroutine scheduler initialize parameter is used to initialize the coroutine scheduler.
*/
typedef struct CoSchedulerInitPara
{
CoLock_t lock; /**< Coroutine scheduler lock function */
CoUnlock_t unlock; /**< Coroutine scheduler unlock function */
CoMalloc_t malloc; /**< Coroutine scheduler malloc function */
CoFree_t free; /**< Coroutine scheduler free function */
} CoSchedulerInitPara;
/**
* \brief: Coroutine task create parameter
* \note: Coroutine task create parameter is used to create a coroutine task.
*/
typedef struct CoTaskCreatePara
{
void *pStack; /**< Coroutine task stack address */
size_t stackSize; /**< Coroutine task stack size */
void *arg; /**< Coroutine task argument */
int schedulerId; /**< Coroutine task scheduler id */
} CoTaskCreatePara;
/**
* \brief: Coroutine timer create parameter
* \note: Coroutine timer create parameter is used to create a coroutine timer.
*/
typedef struct CoTimerCreatePara
{
uint64_t ms; /**< Coroutine timer ms, indicates the ms interval */
uint64_t tick; /**< Coroutine timer tick, indicates the tick interval */
int schedulerId; /**< Coroutine timer scheduler id */
} CoTimerCreatePara;
/**
* \brief: Coroutine task wait parameter
* \note: Coroutine task wait parameter is used to blocking task waiting for event or timeout.
*/
typedef struct CoTaskWaitPara
{
CoEvent *pEvent; /**< Coroutine task wait event pointer */
uint64_t ms; /**< Coroutine task wait timeout in ms */
uint64_t tick; /**< Coroutine task wait timeout in tick */
} CoTaskWaitPara;
/**
* \brief: Coroutine task create parameter default value
* \note: Coroutine task create parameter default value is used to create a coroutine task.
*/
#define COROUTINE_E_OK (0) /**< Coroutine task create parameter default value, indicates success */
#define COROUTINE_E_INVALID_PARAMETER (-1) /**< Coroutine task create parameter default value, indicates invalid parameter */
#define COROUTINE_E_INVALID_TICK (-2) /**< Coroutine task create parameter default value, indicates invalid tick function */
#define COROUTINE_E_INVALID_TICK_INTERVAL (-3) /**< Coroutine task create parameter default value, indicates invalid tick interval */
#define COROUTINE_E_INVALID_LOCK (-4) /**< Coroutine task create parameter default value, indicates invalid lock function */
#define COROUTINE_E_INVALID_MHOOK (-5) /**< Coroutine task create parameter default value, indicates invalid memory hook function */
#define COROUTINE_E_TCB_MEM_STACK_FAIL (-6) /**< Coroutine task create parameter default value, indicates memory stack fail */
/**
* \brief: Coroutine event default value
* \note: Coroutine event default value is used to initialize the coroutine event.
*/
#define COEVENT_STATIC_VALUE ((CoEvent){.flag=0})
/**
* \brief: Coroutine scheduler initialize function
* \param pScheduler: [Non-default parameter] Coroutine scheduler pointer
* \param tick: [Non-default parameter] Coroutine scheduler tick function, must be specified
* \param tickInterval: [Non-default parameter] Coroutine scheduler tick interval, indicates how many ns each tick is, must be specified
* \param lock: [Default parameter] Coroutine scheduler lock function,
* for use on multiple threads, you must specify as follows .lock=thread_lock
* \param unlock: [Default parameter] Coroutine scheduler unlock function,
* for use on multiple threads, you must specify as follows .unlock=thread_unlock
* \param malloc: [Default parameter] Coroutine scheduler malloc function,
* if you need to allocate memory dynamically (CoTask, CoTimer, stack, etc),
* you must specify as follows .malloc=mallloc
* \param free: [Default parameter] Coroutine scheduler free function,
* if you need to free memory dynamically (CoTask, CoTimer, stack, etc),
* you must specify as follows .free=free
* \return: Coroutine task create parameter default value, indicates success
* \note: Coroutine scheduler initialize function is used to initialize the coroutine scheduler.
* It must be called once before any other coroutine scheduler function.
* Only one initialization is allowed within a single thread
* It will initialize the coroutine scheduler with the given parameter.
* The coroutine scheduler will use the given tick function to measure the load of each task.
* The tick function must be called periodically with the given tick interval.
* The coroutine scheduler will use the given lock function to protect the shared resources.
* The coroutine scheduler will use the given malloc function to allocate memory for the tasks.
* The coroutine scheduler will use the given free function to free memory of the tasks.
* \warning: The coroutine scheduler will not check the validity of the given parameter.
* You must ensure that the given parameter is valid.
* \example:
* CoScheduler_Init(0, GetTimerMs, 1000000);
* \example:
* CoScheduler_Init(0, GetTimerUsec, 1000);
* \example:
* CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
* \example:
* CoScheduler_Init(0, GetTimerUsec, 1000, .malloc=mallloc, .free=free);
*/
#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...) CoScheduler_InitP(CoSchedulerId, tick, tickInterval, (CoSchedulerInitPara[1]){[0]={.lock=NULL,.unlock=NULL,.malloc=NULL,.free=NULL,__VA_ARGS__}})
/**
* \brief: Coroutine scheduler initialize function
* \param pScheduler: Coroutine scheduler pointer
* \param tick: Coroutine scheduler tick function
* \param tickInterval: Coroutine scheduler tick interval, indicates how many ns each tick is
* \param pPara: Coroutine scheduler initialize parameter pointer
* \return: Coroutine task create parameter default value, indicates success
* \note: Coroutine scheduler initialize function is used to initialize the coroutine scheduler.
* It must be called once before any other coroutine scheduler function.
* Only one initialization is allowed within a single thread
* It will initialize the coroutine scheduler with the given parameter.
* The coroutine scheduler will use the given tick function to measure the load of each task.
* The tick function must be called periodically with the given tick interval.
* The coroutine scheduler will use the given lock function to protect the shared resources.
* The coroutine scheduler will use the given malloc function to allocate memory for the tasks.
* The coroutine scheduler will use the given free function to free memory of the tasks.
*/
int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara);
/**
* \brief: Coroutine scheduler start function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine task create parameter default value, indicates success
* \note: Coroutine scheduler start function is used to start the coroutine scheduler.
* It must be called once after the coroutine scheduler is initialized.
* It will start the coroutine scheduler and run the tasks.
* \example:
* CoScheduler_Init(0, GetTimerUsec, 1000);
* testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest), 0);
* CoScheduler_Start(0);
*/
int CoScheduler_Start(uint32_t CoSchedulerId);
/**
* \brief: Coroutine scheduler exit function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine task create parameter default value, indicates success
* \note: Coroutine scheduler exit function is used to exit the coroutine scheduler.
* It must be called once after the coroutine scheduler is started.
* It will exit the coroutine scheduler and stop the tasks.
*/
int CoScheduler_Exit(uint32_t CoSchedulerId);
/**
* \brief: Coroutine scheduler task count function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine scheduler task count, indicates the number of tasks in the coroutine scheduler
* \note: Coroutine scheduler task count function is used to get the number of tasks in the coroutine scheduler.
*/
int CoScheduler_TaskCount(uint32_t CoSchedulerId);
/**
* \brief: Coroutine scheduler timer count function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine scheduler timer count, indicates the number of timers in the coroutine scheduler
* \note: Coroutine scheduler timer count function is used to get the number of timers in the coroutine scheduler.
*/
int CoScheduler_TimerCount(uint32_t CoSchedulerId);
/**
* \brief: Coroutine scheduler current load function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine scheduler current load, indicates the load of the coroutine scheduler
* \note: Coroutine scheduler current load function is used to get the current load of the coroutine scheduler.
* The load is the number of ticks that the coroutine scheduler has run.
* The load is updated periodically with the given tick interval.
*/
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
#endif
/**
* \brief: Coroutine scheduler maximum load function
* \param pScheduler: Coroutine scheduler pointer
* \return: Coroutine scheduler maximum load, indicates the maximum load of the coroutine scheduler
* \note: Coroutine scheduler maximum load function is used to get the maximum load of the coroutine scheduler.
* The load is the number of ticks that the coroutine scheduler has run.
* The load is updated periodically with the given tick interval.
*/
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
#endif
/**
* \brief: Coroutine task create function
* \param entry: [Non-default parameter] Coroutine task entry function, indicates the entry function of the coroutine task, must be specified
* \param stack: [Default parameter] Coroutine task stack, indicates the stack of the coroutine task.
* If not specified, it is allocated via the scheduler's malloc function.
* \param stackSize: [Default parameter] Coroutine task stack size, indicates the size of the coroutine task stack
* If not specified, it is the default stack size `COROUTINE_STACK_DEFAULT_SIZE`.
* \param arg: [Default parameter] Coroutine task argument, indicates the argument of the coroutine task
* \param schedulerId: [Default parameter] Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
* Negative value means the auto distribution.
* \return: Coroutine task handle, indicates the created coroutine task
* \note: Coroutine task create function is used to create a coroutine task.
* It will create a coroutine task with the given parameter.
* The coroutine task will run in the coroutine scheduler.
* \example:
* CoTask_t testCoroutine = CoTask_Create(test);
* \example:
* CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
* \example:
* CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
* \example:
* int arg = 4096;
* CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
*/
#define CoTask_Create(entry, ...) CoTask_CreateP(entry, (CoTaskCreatePara[1]){[0]={.pStack=NULL,.stackSize=0,.arg=NULL,.schedulerId=-1,__VA_ARGS__}})
/**
* \brief: Coroutine task create function, it is recommended to use @ref `CoTask_Create` instead
* \param pPara: Coroutine task create parameter pointer
* \return: Coroutine task handle, indicates the created coroutine task
* \note: Coroutine task create function is used to create a coroutine task.
* It will create a coroutine task with the given parameter.
* The coroutine task will run in the coroutine scheduler.
*/
CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara);
/**
* \brief: Coroutine task delete function
* \param CoTask: Coroutine task handle, indicates the coroutine task to be deleted
* \return: Coroutine task delete parameter default value, indicates success
* \note: Coroutine task delete function is used to delete a coroutine task.
* It will delete the given coroutine task.
* The coroutine task must be in the deleted state.
*/
int CoTask_Delete(CoTask_t CoTask);
/**
* \brief: Coroutine task self function
* \return: Coroutine task handle, indicates the current coroutine task
* \note: Coroutine task self function is used to get the current coroutine task.
* It will return the handle of the current coroutine task.
*/
CoTask_t CoTask_Self(void);
/**
* \brief: Coroutine task scheduler id function
* \return: Coroutine task scheduler id, indicates the scheduler id of the coroutine task
* \note: Coroutine task scheduler id function is used to get the scheduler id of the coroutine task.
*/
int CoTask_SchedulerId(void);
/**
* \brief: Coroutine task stack max used function
* \param CoTask: Coroutine task handle, indicates the coroutine task to get the stack max used
* \return: Coroutine task stack max used, indicates the maximum stack used of the coroutine task
* \note: Coroutine task stack max used function is used to get the maximum stack used of the coroutine task.
* The stack used is the maximum stack used of the coroutine task.
* The stack used is updated periodically with the given tick interval.
*/
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
#endif
/**
* \brief: Coroutine task stack current used function
* \param CoTask: Coroutine task handle, indicates the coroutine task to get the stack current used
* \return: Coroutine task stack current used, indicates the current stack used of the coroutine task
* \note: Coroutine task stack current used function is used to get the current stack used of the coroutine task.
* The stack used is the current stack used of the coroutine task.
* The stack used is updated periodically with the given tick interval.
*/
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t CoTask_StackCurUsed(CoTask_t CoTask);
#endif
/**
* \brief: Coroutine task wait function, blocking task waiting for event or timeout.
* \param pEvent: [Default parameter] Coroutine event pointer, indicates the event to wait
* \param ms: [Default parameter] Coroutine task wait ms, indicates the ms to wait
* \param tick: [Default parameter] Coroutine task wait tick, indicates the tick to wait
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
* \note: Coroutine task wait event function is used to wait for the given event.
* @ref `CoEvent_Init` must first be called to initialize the event.
* @ref `CoEvent_Notify` notifies the occurrence of the event
* It will block the current coroutine task.
* The event value is a bit mask, each bit represents an event.
* If multiple events are triggered, the corresponding bits will be set.
* \note: Coroutine task wait ms function is used to wait for the given ms.
* It will block the current coroutine task.
* \note: Coroutine task wait tick function is used to wait for the given tick.
* It will block the current coroutine task.
* \example:
* void *test(void *arg)
* {
* while (1)
* {
* // Give up access to the scheduler without waiting, similar to yeild
* CoTask_Wait();
*
* // Wait for 1000 ms
* CoTask_Wait(.ms=1000);
*
* // Wait for 1000000 tick
* CoTask_Wait(.tick=1000000);
*
* // Wait for 1000 ms or event, if no any events occur within 1000 ms, and will timeout blocking
* CoTask_Wait(.pEvent=&g_Event, .ms=1000);
*
* // Always wait for the event, and judge every events
* uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
* if (evs & 0x01)
* {
* printf("event 0x01 triggered\n");
* }
* if (evs & 0x02)
* {
* printf("event 0x02 triggered\n");
* }
* }
* }
*/
#define CoTask_Wait(...) CoTask_WaitP((CoTaskWaitPara[1]){[0]={.pEvent=NULL,.ms=0,.tick=0,__VA_ARGS__}})
/**
* \brief: Coroutine task wait function, it is recommended to use @ref `CoTask_Wait` instead
* \param pPara: Coroutine task wait parameter pointer
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
* \note: Coroutine task wait function is used to wait for the given event or timeout.
* It will block the current coroutine task.
* The event value is a bit mask, each bit represents an event.
* If multiple events are triggered, the corresponding bits will be set.
*/
uint32_t CoTask_WaitP(CoTaskWaitPara *pPara);
/**
* \brief: Coroutine task wait functions, the version of the macro definition used is often used
* \param m: Coroutine task wait ms, indicates the ms to wait
* \param t: Coroutine task wait tick, indicates the tick to wait
* \param e: Coroutine event pointer, indicates the event to wait
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
*/
#define CoTask_WaitMs(m) CoTask_Wait(.ms=m)
#define CoTask_WaitTick(t) CoTask_Wait(.tick=t)
#define CoTask_WaitEvent(e) CoTask_Wait(.pEvent=e)
#define CoTask_WaitEventMs(e, m) CoTask_Wait(.pEvent=e, .ms=m)
#define CoTask_WaitEventTick(e, t) CoTask_Wait(.pEvent=e, .tick=t)
/**
* \brief: Coroutine timer create function
* \param entry: [Non-default parameter] Coroutine timer entry, indicates the timer entry function
* \param ms: [Default parameter] Coroutine timer ms, indicates the ms interval
* \param tick: [Default parameter] Coroutine timer tick, indicates the tick interval
* \param schedulerId: [Default parameter] Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
* Negative value means the auto distribution.
* \return: Coroutine timer handle, indicates the created timer
* \note: One of the ms and tick parameters must be set
* \note: Coroutine timer create function is used to create a timer with the given ms or tick interval.
* The timer will call the given entry function periodically with the given ms or tick interval.
* \example:
* CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100); // create a timer with 100ms interval
*/
#define CoTimer_Create(entry, ...) CoTimer_CreateP(entry, (CoTimerCreatePara[1]){[0]={.ms=0,.tick=0,.schedulerId=-1,__VA_ARGS__}})
/**
* \brief: Coroutine timer create function, it is recommended to use @ref `CoTimer_Create` instead
* \param pPara: Coroutine timer create parameter pointer
* \return: Coroutine timer handle, indicates the created timer
* \note: Coroutine timer create function is used to create a timer with the given ms or tick interval.
* The timer will call the given entry function periodically with the given ms or tick interval.
*/
CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara);
/**
* \brief: Coroutine timer create ms function, it is recommended to use @ref `CoTimer_CreateMs` instead
* \param entry: Coroutine timer entry, indicates the timer entry function
* \param m: Coroutine timer ms, indicates the ms interval
* \param t: Coroutine timer tick, indicates the tick interval
* \return: Coroutine timer handle, indicates the created timer
* \note: Coroutine timer create ms function is used to create a timer with the given ms interval.
* The timer will call the given entry function periodically with the given ms interval.
* \example:
* CoTimer_t timer = CoTimer_CreateMs(timer_entry, 100); // create a timer with 100ms interval
*/
#define CoTimer_CreateMs(entry, m) CoTimer_Create(entry, .ms=m)
#define CoTimer_CreateTick(entry, t) CoTimer_Create(entry, .tick=t)
/**
* \brief: Coroutine timer delete function
* \param Timer: Coroutine timer handle, indicates the timer to delete
* \note: Coroutine timer delete function is used to delete the given timer.
* It must be called after @ref `CoTimer_CreateTick` or @ref `CoTimer_CreateMs`.
* \example:
* CoTimer_Delete(timer); // delete the timer
*/
void CoTimer_Delete(CoTimer_t Timer);
/**
* \brief: Coroutine timer self function
* \return: Coroutine timer handle, indicates the current timer
* \note: Coroutine timer self function is used to get the handle of the current timer.
* \example:
* CoTimer_t timer = CoTimer_Self(); // get the current timer
*/
CoTimer_t CoTimer_Self(void);
/**
* \brief: Coroutine timer scheduler id function
* \return: Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
* \note: Coroutine timer scheduler id function is used to get the scheduler id of the coroutine timer.
*/
int CoTimer_SchedulerId(void);
/**
* \brief: Coroutine event initialize function
* \param pEvent: Coroutine event pointer, indicates the event to initialize
* \note: Coroutine event initialize function is used to initialize the given event.
* It must be called before @ref `CoTask_WaitEvent`.
* \example:
* CoEvent_Init(&g_Event);
*/
void CoEvent_Init(CoEvent *pEvent);
/**
* \brief: Coroutine event notify function
* \param pEvent: Coroutine event pointer, indicates the event to notify
* \param evs: Coroutine event value, indicates the event value to notify
* \note: Coroutine event notify function is used to notify the occurrence of the event.
* It will set the corresponding bits in the event value.
* \example:
* CoEvent_Notify(&g_Event, 0x01); // notify event 0x01
* \example:
* CoEvent_Notify(&g_Event, 0x01 | 0x02); // notify event 0x01 and 0x02
*/
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
#ifdef __cplusplus
}
#endif
#endif // !__coroutine_H

View File

@ -0,0 +1,172 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file coroutine_cfg.h
* \unit coroutine
* \brief This is a C language coroutine library
* \author Lamdonn
* \version v0.2.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#ifndef __coroutine_cfg_H
#define __coroutine_cfg_H
/**
* \brief Coroutine scheduler number
* \note This macro defines the number of coroutine schedulers
*/
#define COROUTINE_SCHEDULER_NUMBER 1
/**
* \brief Coroutine static task max number
* \note This macro defines the maximum number of coroutine tasks that can be created statically
*/
#define COROUTINE_STATIC_TASK_MAX_NUMBER 8
/**
* \brief Coroutine static timer max number
* \note This macro defines the maximum number of coroutine timers that can be created statically
*/
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 8
/**
* \brief Coroutine static stack max number
* \note This macro defines the maximum number of coroutine stacks that can be created statically
*/
#define COROUTINE_STATIC_STACK_MAX_NUMBER 8
/**
* \brief Coroutine stack default size
* \note This macro defines the default size of the coroutine stack
* \note If the coroutine stack size is not specified, this value will be used
*/
#define COROUTINE_STACK_DEFAULT_SIZE 10240 // 4096 //
/**
* \brief Coroutine enable stack calculate
* \note This macro defines whether to enable the stack calculate feature
* \note If enabled, the coroutine stack using will be calculated automatically
*/
#define COROUTINE_ENABLE_STACK_CALCULATE 1
/**
* \brief Coroutine enable loading calculate
* \note This macro defines whether to enable the loading calculate feature
* \note If enabled, the coroutine loading will be calculated automatically
*/
#define COROUTINE_ENABLE_LOADING_CALCULATE 1
/**
* \brief Coroutine loading queue size
* \note This macro defines the size of the coroutine loading queue
*/
#define COROUTINE_LOADING_CALCULATE_QSIZE 10
/**
* \brief Coroutine loading calculate period
* \note This macro defines the period of the coroutine loading calculate
* \note The unit is ms
*/
#define COROUTINE_LOADING_CALCULATE_PERIOD 100
/**
* \brief Stack get and set Pointers depending on the platform definition
* \param p Pointer to the stack
* \return None
* \note This macro is used to get and set the stack pointer depending on the platform definition
* \note For x86_32, the stack pointer is esp; for x86_64, the stack pointer is rsp; for arm, the stack pointer is sp
* \note Common platforms have been implemented, not implemented platforms need to be implemented by users
*/
#if defined(__i386__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%esp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%esp" : : "r" (p) : "memory")
#elif defined(__x86_64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%rsp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%rsp" : : "r" (p) : "memory")
#elif defined(__arm__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__aarch64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__mips__)
#define COROUTINE_GET_SP(p) __asm__ volatile(".set noreorder\n\t" "move %0, $sp\n\t" ".set reorder" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile(".set noreorder\n\t" "move $sp, %0\n\t" ".set reorder" : : "r" (p) : "memory")
#elif defined(__riscv__)
#if __riscv_xlen == 32
#define COROUTINE_GET_SP(p) __asm__ volatile("mv %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mv sp, %0" : : "r" (p) : "memory")
#else
#define COROUTINE_GET_SP(p) __asm__ volatile("mv %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mv sp, %0" : : "r" (p) : "memory")
#endif
#elif defined(__powerpc__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__powerpc64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__s390__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
#elif defined(__s390x__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
#elif defined(__sparc__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
#elif defined(__sparcv9__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
#elif defined(__tile__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
#elif defined(__tilegx__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
#elif defined(__hppa__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__hppa64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__ia64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0=sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp=%0" : : "r" (p) : "memory")
#elif defined(__loongarch__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__m68k__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__mips64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__parisc__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__parisc64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
#elif defined(__s390__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
#elif defined(__s390x__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
#elif defined(__sh__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__sh64__)
#define COROUTINE_GET_SP(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
#elif defined(__xtensa__)
#define COROUTINE_GET_SP(p) __asm__ volatile("rsr.a1 %0" : "=r" (p) : : "memory")
#define COROUTINE_SET_SP(p) __asm__ volatile("wsr.a1 %0" : : "r" (p) : "memory")
#else
#error "Unsupported platform, please implement `COROUTINE_GET_SP` and `COROUTINE_SET_SP`"
#endif
#endif // !__coroutine_cfg_H

108
test/file/read.yaml Normal file
View File

@ -0,0 +1,108 @@
mapping:
open: Yes
string null: string null
string null n: string null
n
string sigle: 'string sigle'
string sigle n: 'string
sigle'
string double: "string double"
string double n: "string
double"
string scalar: |
string
scalar
string scalar n: |2
string
scalar
string scalar -: |-
string
scalar
string scalar +: |+
string
scalar
string fold: >
string
fold
string fold n: >2
string
fold
string fold -: >-
string
fold
string fold +: >+
string
fold
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
number dec: 123456
number hex: 0x123456
number float: 012.3456
number e: 1.23e-4
number nan: .nan
number inf: .inf
number inf n: -.inf
Anchors:
people: &people
name: people
age: 18
height: 178
cat: &cat
name: cat
age: 2
string: &string
This is string
sequence: &sequence
- 1
- 2
Zhang San: &default
color: blue
size: medium
property: *people
Inherit:
<<: *people
# shape: circle
square brackets: [1,2,3,4]
brace brackets: {a: 1, b: 2, c: 3}
bin: -0b1010_0111_0100_1010_1110
iso8601: 2025-03-29T23:59:43.89-05:00
date: 2025-03-29
time: 2025-03-29T23:59:43
utc: 2025-03-29T23:59:43+08:00
int_22: !!int 123456
muti:
a: "AABCDEFG"
?
- a
- b
: fruit

11
test/file/write.yaml Normal file
View File

@ -0,0 +1,11 @@
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg

View File

@ -9,6 +9,7 @@ TEST_LIST += txls
TEST_LIST += xml TEST_LIST += xml
TEST_LIST += csv TEST_LIST += csv
TEST_LIST += json TEST_LIST += json
TEST_LIST += yaml
TEST_LIST += vector TEST_LIST += vector
TEST_LIST += list TEST_LIST += list
TEST_LIST += str TEST_LIST += str
@ -41,6 +42,9 @@ TEST_LIST += floatl
TEST_LIST += flmath TEST_LIST += flmath
TEST_LIST += ramt TEST_LIST += ramt
TEST_LIST += romt TEST_LIST += romt
TEST_LIST += cant
TEST_LIST += slup
# TEST_LIST += cpul # TEST_LIST += cpul
TEST_LIST += date TEST_LIST += date
TEST_LIST += unitt TEST_LIST += unitt
TEST_LIST += coroutine

377
test/test_cant.c Normal file
View File

@ -0,0 +1,377 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(TEST_TARGET_cant)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/cant.h>
#include <varch/cQueue.h>
#else
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "cant.h"
#include "cQueue.h"
#endif
/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/
// #define EXIT_TEST
extern uint64_t unitt_clock(void);
static int test_0(void)
{
for (int i = 0; i < 100; i++)
{
if (0)
{
#if defined (EXIT_TEST)
exit(0);
#endif
return UNITT_E_FAIL;
}
}
return UNITT_E_OK;
}
static void unitt_task(void)
{
static UNITT_TCASE rand_tests[] = {
UNITT_TCASE(test_0),
// UNITT_TCASE(test_1),
// UNITT_TCASE(test_2),
};
static UNITT suites[] = {
{ "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
};
UNITT_EXE(suites);
}
/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/
/////////////////////////////////
//// Simulating CAN BUS /////
/////////////////////////////////
typedef int (*canbus_receive_t)(uint32_t canid, uint8_t *data, uint16_t length);
typedef struct
{
uint8_t devid;
canbus_receive_t receive;
} CanDevType;
typedef struct
{
uint8_t devid;
uint16_t length;
uint32_t canid;
uint8_t data[64];
} CanTpType;
typedef struct
{
CanDevType devs[8];
uint8_t devn;
cQueue(CanTpType, 1024) tp;
} CanBusType;
static CanBusType canbus;
static uint8_t canbus_maxdev = (uint8_t)(sizeof(canbus.devs) / sizeof(canbus.devs[0]));
static int canbus_attach_dev(uint8_t devid, canbus_receive_t receive)
{
if (devid >= canbus_maxdev) return -1;
if (!receive) return -2;
canbus.devs[devid].devid = devid;
canbus.devs[devid].receive = receive;
return 0;
}
static int canbus_transer(uint8_t devid, uint32_t canid, uint8_t *data, uint16_t length)
{
CanTpType tp;
int ret = 0;
if (devid >= canbus_maxdev) return -1;
if (canbus.devs[devid].devid == 0xFF) return -3;
if (!data) return -4;
if (length > 64) return -5;
tp.devid = devid;
tp.length = length;
tp.canid = canid;
for (uint16_t i = 0; i < length; i++) tp.data[i] = data[i];
ret = cQueue_push(canbus.tp, tp);
if (!ret) return -6;
return 0;
}
static void canbus_init()
{
memset(&canbus, 0, sizeof(canbus));
for (uint8_t devid = 0; devid < canbus_maxdev; devid++)
{
canbus.devs[devid].devid = 0xFF;
}
cQueue_init(canbus.tp);
}
static void canbus_task()
{
CanTpType tp;
while (cQueue_pop(canbus.tp, tp))
{
for (uint8_t devid = 0; devid < canbus_maxdev; devid++)
{
if (canbus.devs[devid].devid == 0xFF) continue;
if (canbus.devs[devid].devid == tp.devid) continue;
if (canbus.devs[devid].receive)
{
canbus.devs[devid].receive(tp.canid, tp.data, tp.length);
}
}
}
}
/////////////////////////////////
//// Simulating CAN DEV /////
/////////////////////////////////
static CANT cant0;
static CANT cant1;
static int dev0_can_transmit(uint32_t canid, uint8_t *data, uint16_t length)
{
int ret = canbus_transer(0, canid, data, length);
return ret;
}
static int dev1_can_transmit(uint32_t canid, uint8_t *data, uint16_t length)
{
int ret = canbus_transer(1, canid, data, length);
return ret;
}
static int dev0_can_receive(uint32_t canid, uint8_t *data, uint16_t length)
{
#if 0
printf("[DEV0][%08x] ", canid);
for (int i = 0; i < length; i++)
{
printf("%02x ", data[i]);
}
printf("\n");
#endif
cant_receive(&cant0, canid, data, length);
return 0;
}
static int dev1_can_receive(uint32_t canid, uint8_t *data, uint16_t length)
{
#if 0
printf("[DEV1][%08x] ", canid);
for (int i = 0; i < length; i++)
{
printf("%02x ", data[i]);
}
printf("\n");
#endif
cant_receive(&cant1, canid, data, length);
return 0;
}
/////////////////////////////////
///// CANT //////////
/////////////////////////////////
static CANT cant0 = {
.config = {
.channel = 0,
.baundrate = CANT_BAUDRATE_500K,
.period = 5,
.transmit = dev0_can_transmit,
.receive = NULL,
},
};
static CANT cant1 = {
.config = {
.channel = 0,
.baundrate = CANT_BAUDRATE_500K,
.period = 5,
.transmit = dev1_can_transmit,
.receive = NULL,
},
};
static void dev0_cant_task(void)
{
static uint32_t count = 0;
count += 5;
if (count >= 25200000) count = 0;
if (count % 5 == 0)
{
cant_task(&cant0);
}
if (count % 1000 == 0)
{
static uint8_t data[64];
static uint32_t sc = 0;
*(uint32_t *)data = sc++;
cant_transmit(&cant0, 0x75, data, 64);
printf("road = %d\r\n", cant0.busload);
}
}
static void dev1_cant_task(void)
{
cant_task(&cant1);
}
static void test_base(void)
{
printf("cant test!\r\n");
/* can bus init */
canbus_init();
task_create(5, canbus_task);
/* can dev init */
canbus_attach_dev(0, dev0_can_receive);
canbus_attach_dev(1, dev1_can_receive);
/* cant protcol init */
cant_init(&cant0);
cant_init(&cant1);
task_create(5, dev0_cant_task);
task_create(5, dev1_cant_task);
cant_set_dummy_canid(&cant0, 0x777);
cant_set_busload(&cant0, 3000);
}
/************************************************************************************/
/************************************* Command ************************************/
/************************************************************************************/
static void usage(void)
{
printf(
"Usage: cant [opt] [arg] ...\n"
"\n"
"options:\n"
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
" <base> Test base function\n"
" <ut> Unit test\n"
" -h Print help\n"
" -v Print version\n"
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
"\n"
);
}
static int test(int argc, char *argv[])
{
char *execute = NULL;
int ut_period = 1000;
int period = 1000; // ms
/* reset getopt */
command_opt_init();
while (1)
{
int opt = command_getopt(argc, argv, "e:hvu::p:");
if (opt == -1) break;
switch (opt)
{
case 'p' :
period = atoi(command_optarg);
break;
case 'u' :
if (command_optarg) ut_period = atoi(command_optarg);
break;
case 'e' :
execute = command_optarg;
break;
case 'v' :
printf("cant version %d.%d.%d\r\n", CANT_V_MAJOR, CANT_V_MINOR, CANT_V_PATCH);
return 0;
case '?':
printf("Unknown option `%c`\r\n", command_optopt);
return -1;
case 'h' :
default:
usage();
return 0;
}
}
if (execute)
{
if (!strcmp(execute, "base"))
{
test_base();
}
else if (!strcmp(execute, "ut"))
{
#if defined(TEST_TARGET_cant)
while (1)
{
unitt_task();
usleep(1000 * ut_period);
}
#else
printf("create task %d\r\n", task_create(ut_period, unitt_task));
#endif
}
}
else
{
test_base();
}
return 0;
}
/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/
#if defined(TEST_TARGET_cant)
int main(int argc, char *argv[])
{
return test(argc, argv);
}
#else
void test_cant(void)
{
command_export("cant", test);
}
init_export_app(test_cant);
#endif

320
test/test_coroutine.c Normal file
View File

@ -0,0 +1,320 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#if defined(TEST_TARGET_coroutine)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/coroutine.h>
#else
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "coroutine.h"
#endif
#define MUTI_THREAD_NUM 2
#if (MUTI_THREAD_NUM <= 0)
#error "MUTI_THREAD_NUM must be greater than 0"
#endif
#ifdef _WIN32
#include <windows.h>
uint64_t GetTimerUsec(void)
{
LARGE_INTEGER li;
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&li);
return (uint64_t)li.QuadPart * 1000000 / frequency.QuadPart;
}
#else
#include <sys/time.h>
uint64_t GetTimerUsec(void)
{
struct timeval mstime;
uint64_t us = 0;
gettimeofday(&mstime, NULL);
us = mstime.tv_sec * 1000000 + mstime.tv_usec;
return us;
}
#endif
/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/
// #define EXIT_TEST
extern uint64_t unitt_clock(void);
static int test_0(void)
{
for (int i = 0; i < 100; i++)
{
if (0)
{
#if defined (EXIT_TEST)
exit(0);
#endif
return UNITT_E_FAIL;
}
}
return UNITT_E_OK;
}
static void unitt_task(void)
{
static UNITT_TCASE rand_tests[] = {
UNITT_TCASE(test_0),
// UNITT_TCASE(test_1),
// UNITT_TCASE(test_2),
};
static UNITT suites[] = {
{ "coroutine suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
};
UNITT_EXE(suites);
}
/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/
static CoEvent gEvent = COEVENT_STATIC_VALUE;
static CoTask_t gTickCoroutine = NULL;
static CoTask_t gTockCoroutine = NULL;
static uint8_t gStackTick[1024 * 10];
static uint8_t gStackTock[1024 * 10];
static CoTimer_t gTimer0Handle = NULL;
static CoTimer_t gTimer1Handle = NULL;
static void *tick(void *arg)
{
while (1)
{
CoTask_Wait(.ms=1000);
printf("tick-running...\n");
CoEvent_Notify(&gEvent, 0x02);
}
}
static void *tock(void *arg)
{
while (1)
{
uint32_t evs = CoTask_Wait(.pEvent=&gEvent);
if (evs & 0x01)
{
printf("event 0x01 triggered...\n");
}
if (evs & 0x02)
{
printf("event 0x02 triggered...\n");
}
}
}
static void timer0(void)
{
printf("timer0-running...\n");
}
static void timer1(void)
{
printf("timer1-running... %d\n", CoScheduler_CurLoad(CoTimer_SchedulerId()));
// CoEvent_Notify(&gEvent, 0x01 | 0x02);
}
/* Testing begins here */
void *createTask(void *arg)
{
// CoTask_Create(tick, g_StackTick, sizeof(g_StackTick), 0);
// CoTask_Create(tock, g_StackTock, sizeof(g_StackTock), 0);
CoTask_Create(tick);
CoTask_Create(tock);
// timerHandle0 = CoTimer_CreateMs(timer0_entry, 100);
// timerHandle1 = CoTimer_CreateMs(timer1_entry, 1000);
}
#if (MUTI_THREAD_NUM == 1)
void test_singleThread(void)
{
CoScheduler_Init(0, GetTimerUsec, 1000);
createTask(NULL);
CoScheduler_Start(0);
}
#else
static pthread_t threads[MUTI_THREAD_NUM];
static pthread_t testThread;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static void thread_lock(void)
{
pthread_mutex_lock(&mutex);
}
static void thread_unlock(void)
{
pthread_mutex_unlock(&mutex);
}
static void *muti_thread_entry(void *arg)
{
CoScheduler_Start(*(int *)arg);
}
void test_mutiThread(void)
{
int threadId[MUTI_THREAD_NUM];
for (int i = 0; i < MUTI_THREAD_NUM; i++)
{
CoScheduler_Init(i, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
}
createTask(NULL);
for (int i = 0; i < MUTI_THREAD_NUM; i++)
{
threadId[i] = i;
pthread_create(&threads[i], NULL, muti_thread_entry, &threadId[i]);
/* You can bind thread to specific core */
/* So that each CoScheduler can run on different core */
}
for (int i = 0; i < MUTI_THREAD_NUM; i++)
{
pthread_join(threads[i], NULL);
}
}
#endif
static void test_base(void)
{
#if (MUTI_THREAD_NUM == 1)
test_singleThread();
#else
test_mutiThread();
#endif
}
/************************************************************************************/
/************************************* Command ************************************/
/************************************************************************************/
static void usage(void)
{
printf(
"Usage: coroutine [opt] [arg] ...\n"
"\n"
"options:\n"
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
" <base> Test base function\n"
" <ut> Unit test\n"
" ...\n"
" -h Print help\n"
" -v Print version\n"
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
"\n"
);
}
static int test(int argc, char *argv[])
{
char *execute = NULL;
int ut_period = 1000;
/* reset getopt */
command_opt_init();
while (1)
{
int opt = command_getopt(argc, argv, "e:hvu::");
if (opt == -1) break;
switch (opt)
{
// Add others opt here
case 'u' :
if (command_optarg) ut_period = atoi(command_optarg);
break;
case 'e' :
execute = command_optarg;
break;
case 'v' :
printf("coroutine version %d.%d.%d\r\n", COROUTINE_V_MAJOR, COROUTINE_V_MINOR, COROUTINE_V_PATCH);
return 0;
case '?':
printf("Unknown option `%c`\r\n", command_optopt);
return -1;
case 'h' :
default:
usage();
return 0;
}
}
if (execute)
{
if (!strcmp(execute, "base"))
{
test_base();
}
else if (!strcmp(execute, "ut"))
{
#if defined(TEST_TARGET_coroutine)
while (1)
{
unitt_task();
usleep(1000 * ut_period);
}
#else
printf("create task %d\r\n", task_create(ut_period, unitt_task));
#endif
}
}
else
{
test_base();
}
#if 0
if (1 == command_optind) // no opt
{
}
for (int index = command_optind; index < argc; index++)
{
if (!strcmp(argv[index], "base"))
{
test_base();
}
}
if (1 == argc)
{
test_base();
}
#endif
return 0;
}
/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/
#if defined(TEST_TARGET_coroutine)
int main(int argc, char *argv[])
{
return test(argc, argv);
}
#else
void test_coroutine(void)
{
command_export("coroutine", test);
}
init_export_app(test_coroutine);
#endif

View File

@ -4,6 +4,7 @@
#include <time.h> #include <time.h>
#include <math.h> #include <math.h>
#include <float.h> #include <float.h>
#include <stdint.h>
#if defined(TEST_TARGET_floatl) #if defined(TEST_TARGET_floatl)
#include <varch/command.h> #include <varch/command.h>
#include <varch/unitt.h> #include <varch/unitt.h>
@ -243,9 +244,60 @@ static double double_from_uint64(uint64_t value)
return v.double_; return v.double_;
} }
float double_to_float(double d)
{
// 联合体用于直接操作二进制位
union { double d; uint64_t u; } du = { .d = d };
union { float f; uint32_t u; } fu;
// 提取double的符号、指数、尾数
uint64_t sign = (du.u >> 63) & 0x1;
int64_t exp = ((du.u >> 52) & 0x7FF) - 1023; // 原始指数
uint64_t mant = du.u & 0x000FFFFFFFFFFFFF; // 52位尾数
// 特殊值处理NaN/Inf
if (exp == 1024) {
fu.u = (sign << 31) | 0x7F800000 | (mant ? 0x7FFFFF : 0);
return fu.f;
}
// 调整指数到float范围-126~127
exp += 127; // 转换为float偏置指数
if (exp > 255) { // 上溢返回无穷大
fu.u = (sign << 31) | 0x7F800000;
return fu.f;
} else if (exp < 0) { // 下溢返回0
fu.u = sign << 31;
return fu.f;
}
// 尾数处理隐含的1 + 52位尾数
uint64_t extended_mant = mant | 0x0010000000000000; // 恢复隐含的1
uint32_t float_mant = (extended_mant >> 29); // 保留高23位
// 舍入处理检查第29位
uint32_t round_bits = extended_mant & 0x1FFFFFFF;
if (round_bits > 0x10000000 ||
(round_bits == 0x10000000 && (float_mant & 0x1))) {
float_mant += 1;
if (float_mant & 0x00800000) { // 尾数进位导致指数进位
float_mant >>= 1;
exp += 1;
if (exp > 255) { // 指数二次上溢
fu.u = (sign << 31) | 0x7F800000;
return fu.f;
}
}
}
// 组合结果
fu.u = (sign << 31) | (exp << 23) | (float_mant & 0x007FFFFF);
return fu.f;
}
// 将 double 的二进制表示转换为 uint64_t // 将 double 的二进制表示转换为 uint64_t
typedef unsigned long long uint64_t; // typedef unsigned long long uint64_t;
typedef long long int64_t; // typedef long long int64_t;
uint64_t double_to_bits(double x) { uint64_t double_to_bits(double x) {
return *((uint64_t*)&x); return *((uint64_t*)&x);
@ -1562,6 +1614,8 @@ static void test_base(void)
printf("floatl_ceil %s\r\n", floatl_show(floatl_ceil(floatl(v)), buffer, sizeof(buffer), "%f")); printf("floatl_ceil %s\r\n", floatl_show(floatl_ceil(floatl(v)), buffer, sizeof(buffer), "%f"));
printf("floatl_floor %s\r\n", floatl_show(floatl_floor(floatl(v)), buffer, sizeof(buffer), "%f")); printf("floatl_floor %s\r\n", floatl_show(floatl_floor(floatl(v)), buffer, sizeof(buffer), "%f"));
printf("floatl_round %s\r\n", floatl_show(floatl_round(floatl(v)), buffer, sizeof(buffer), "%f")); printf("floatl_round %s\r\n", floatl_show(floatl_round(floatl(v)), buffer, sizeof(buffer), "%f"));
printf("to_d %f\r\n", floatl_to_d(FLOATL_PI));
} }
/************************************************************************************/ /************************************************************************************/

294
test/test_slup.c Normal file
View File

@ -0,0 +1,294 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(TEST_TARGET_slup)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/slup.h>
#include <varch/cQueue.h>
#else
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "slup.h"
#include "cQueue.h"
#endif
/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/
// #define EXIT_TEST
extern uint64_t unitt_clock(void);
static int test_0(void)
{
for (int i = 0; i < 100; i++)
{
if (0)
{
#if defined (EXIT_TEST)
exit(0);
#endif
return UNITT_E_FAIL;
}
}
return UNITT_E_OK;
}
static void unitt_task(void)
{
static UNITT_TCASE rand_tests[] = {
UNITT_TCASE(test_0),
// UNITT_TCASE(test_1),
// UNITT_TCASE(test_2),
};
static UNITT suites[] = {
{ "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
};
UNITT_EXE(suites);
}
/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/
//////////// sim uart dev ////////////
typedef void (*uart_rx_t)(char c);
static cQueue(char, 1024) qdev0;
static cQueue(char, 1024) qdev1;
static uart_rx_t dev0_rx = NULL;
static uart_rx_t dev1_rx = NULL;
static int dev0_uart_send(char c) { cQueue_push(qdev0, c); }
static int dev1_uart_send(char c) { cQueue_push(qdev1, c); }
static void dev0_uart_rxhandle(void) { char c = 0; while (cQueue_pop(qdev1, c)) { if (dev0_rx) dev0_rx(c); } }
static void dev1_uart_rxhandle(void) { char c = 0; while (cQueue_pop(qdev0, c)) { if (dev1_rx) dev1_rx(c); } }
static void dev0_uart_setrx(uart_rx_t rx) { dev0_rx = rx; }
static void dev1_uart_setrx(uart_rx_t rx) { dev1_rx = rx; }
//////////////////////////////////////
static uint32_t crc8(uint8_t* data, uint16_t len);
static int dev0_slup_receive(uint8_t *data, uint16_t length);
static int dev1_slup_receive(uint8_t *data, uint16_t length);
static SLUP slup0 = {
.cfg = {
.head = {'H', 'E', 'A', 'D'},
.tail = {'T', 'A', 'I', 'L'},
.hsize = SLUP_HEAD_MAX,
.tsize = SLUP_TAIL_MAX,
.csize = sizeof(uint8_t),
.ssize = sizeof(uint32_t),
},
.period = 5,
.putc = dev0_uart_send,
.check = crc8,
.receive = dev0_slup_receive,
};
static SLUP slup1 = {
.cfg = {
.head = {'H', 'E', 'A', 'D'},
.tail = {'T', 'A', 'I', 'L'},
.hsize = SLUP_HEAD_MAX,
.tsize = SLUP_TAIL_MAX,
.csize = sizeof(uint8_t),
.ssize = sizeof(uint32_t),
},
.period = 5,
.putc = dev1_uart_send,
.check = crc8,
.receive = dev1_slup_receive,
};
static void dev0_uart_receive(char c)
{
slup_getc(&slup0, c);
}
static void dev1_uart_receive(char c)
{
slup_getc(&slup1, c);
}
static uint32_t crc8(uint8_t* data, uint16_t len)
{
uint8_t i;
uint8_t crc = 0;
while (len--)
{
crc ^= *data++;
for (i = 0; i < 8; i++)
{
if (crc & 0x80) crc = (crc << 1) ^ 0x07;
else crc <<= 1;
}
}
return (uint32_t)crc;
}
static int dev0_slup_receive(uint8_t *data, uint16_t length)
{
printf("length %d\r\n", length);
return 0;
}
static int dev1_slup_receive(uint8_t *data, uint16_t length)
{
printf("length %d\r\n", length);
printf("%s\r\n", data);
return 0;
}
static void dev0_slup_task(void)
{
static uint32_t count = 0;
count += 5;
if (count >= 25200000) count = 0;
if (count % 5 == 0)
{
slup_task(&slup0);
}
if (count % 1000 == 0)
{
uint8_t link = 0;
slup_link_status(&slup0, &link);
printf("dev0[%02x] send...\r\n", link);
slup_send(&slup0, "0123456789", 10);
}
}
static void dev1_slup_task(void)
{
slup_task(&slup1);
}
static void test_base(void)
{
printf("slup test!\r\n");
/* uart dev init */
cQueue_init(qdev0);
cQueue_init(qdev1);
dev0_uart_setrx(dev0_uart_receive);
dev1_uart_setrx(dev1_uart_receive);
task_create(5, dev0_uart_rxhandle);
task_create(5, dev1_uart_rxhandle);
/* slup protcol init */
slup_init(&slup0);
slup_init(&slup1);
task_create(5, dev0_slup_task);
task_create(5, dev1_slup_task);
}
/************************************************************************************/
/************************************* Command ************************************/
/************************************************************************************/
static void usage(void)
{
printf(
"Usage: slup [opt] [arg] ...\n"
"\n"
"options:\n"
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
" <base> Test base function\n"
" <ut> Unit test\n"
" -h Print help\n"
" -v Print version\n"
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
"\n"
);
}
static int test(int argc, char *argv[])
{
char *execute = NULL;
int ut_period = 1000;
int period = 1000; // ms
/* reset getopt */
command_opt_init();
while (1)
{
int opt = command_getopt(argc, argv, "e:hvu::p:");
if (opt == -1) break;
switch (opt)
{
case 'p' :
period = atoi(command_optarg);
break;
case 'u' :
if (command_optarg) ut_period = atoi(command_optarg);
break;
case 'e' :
execute = command_optarg;
break;
case 'v' :
printf("slup version %d.%d.%d\r\n", SLUP_V_MAJOR, SLUP_V_MINOR, SLUP_V_PATCH);
return 0;
case '?':
printf("Unknown option `%c`\r\n", command_optopt);
return -1;
case 'h' :
default:
usage();
return 0;
}
}
if (execute)
{
if (!strcmp(execute, "base"))
{
test_base();
}
else if (!strcmp(execute, "ut"))
{
#if defined(TEST_TARGET_slup)
while (1)
{
unitt_task();
usleep(1000 * ut_period);
}
#else
printf("create task %d\r\n", task_create(ut_period, unitt_task));
#endif
}
}
else
{
test_base();
}
return 0;
}
/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/
#if defined(TEST_TARGET_slup)
int main(int argc, char *argv[])
{
return test(argc, argv);
}
#else
void test_slup(void)
{
command_export("slup", test);
}
init_export_app(test_slup);
#endif

579
test/test_yaml.c Normal file
View File

@ -0,0 +1,579 @@
#include <stdio.h>
#include <string.h>
#if defined(TEST_TARGET_yaml)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/yaml.h>
#else
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "yaml.h"
#endif
/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/
// #define EXIT_TEST
extern uint64_t unitt_clock(void);
static int test_0(void)
{
for (int i = 0; i < 100; i++)
{
if (0)
{
#if defined (EXIT_TEST)
exit(0);
#endif
return UNITT_E_FAIL;
}
}
return UNITT_E_OK;
}
static void unitt_task(void)
{
static UNITT_TCASE rand_tests[] = {
UNITT_TCASE(test_0),
// UNITT_TCASE(test_1),
// UNITT_TCASE(test_2),
};
static UNITT suites[] = {
{ "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
};
UNITT_EXE(suites);
}
/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/
#define READ_FILE "test/file/read.yaml"
#define WRITE_FILE "test/file/write.yaml"
static void yaml_preview(yaml_t yaml)
{
char *s = yaml_dumps(yaml, 0, 0, YAML_F_NONE);
if (s)
{
printf("%s\r\n", s);
free(s);
}
}
static void test_create(void)
{
yaml_t yaml = yaml_create();
if (yaml)
{
printf("yaml_create success!!! %p\r\n", yaml);
}
else
{
printf("yaml_create fail!!!\r\n");
}
yaml_delete(yaml);
}
static void test_add(void)
{
yaml_t yaml = NULL, info = NULL, hobby = NULL, key = NULL, complex = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "Lamdonn", yaml);
info = yaml_map_add_mapping(yaml, "Zhangsan", NULL);
yaml_map_add_int(info, "age", 29);
yaml_map_add_float(info, "height", 175);
yaml_map_add_string(info, "email", "Zhangsan@163.com");
hobby = yaml_map_add_sequence(info, "hobby", NULL);
yaml_seq_add_string(hobby, "Playing basketball");
yaml_seq_add_string(hobby, "Playing football");
yaml_seq_add_string(hobby, "Playing mobile game");
yaml_t kk = yaml_seq_add_mapping(hobby, NULL);
yaml_map_add_int(kk, "year", 29);
yaml_map_add_float(kk, "month", 6);
info = yaml_map_add_mapping(yaml, "Lisi", NULL);
complex = yaml_map_add_int(info, "1", 29);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_insert(void)
{
yaml_t root = NULL, doc = NULL, t = NULL;
root = yaml_create();
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
yaml_preview(root);
yaml_delete(root);
}
static void test_remove(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_remove(info, "email", 0);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_set(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_null(info);
yaml_preview(yaml);
yaml_set_bool(info, YAML_TRUE);
yaml_preview(yaml);
yaml_set_int(info, 123);
yaml_preview(yaml);
yaml_set_float(info, 3.14159);
yaml_preview(yaml);
yaml_set_string(info, "Hello yaml");
yaml_preview(yaml);
yaml_set_date(info, 2025, 01, 01);
yaml_set_time(info, 16, 30, 03, 200);
yaml_set_utc(info, +8, 30);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_get(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_t bool_ = yaml_map_add_bool(info, "open source", YAML_TRUE);
printf("bool_: %d\r\n", yaml_value_bool(bool_));
yaml_t int_ = yaml_map_add_int(info, "age", 26);
printf("int_: %d\r\n", yaml_value_int(int_));
yaml_t float_ = yaml_map_add_float(info, "height", 178.5);
printf("float_: %f\r\n", yaml_value_float(float_));
yaml_t string_ = yaml_map_add_string(info, "email", "Lamdonn@163.com");
printf("string_: %s\r\n", yaml_value_string(string_));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_key(void)
{
yaml_t yaml = NULL, key = NULL;
yaml = yaml_create();
yaml_set_key(yaml, "StringKey");
printf("key: %s\r\n", yaml_key(yaml));
key = yaml_create();
yaml_map_add_int(key, "a", 12);
yaml_map_add_int(key, "b", 13);
yaml_set_key_complex(yaml, key);
printf("complex key: \r\n");
yaml_preview(yaml_key_complex(yaml));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_child(void)
{
yaml_t yaml = NULL, info = NULL, key = NULL, child = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
key = yaml_create();
yaml_map_add_int(key, "a", 12);
yaml_map_add_int(key, "b", 13);
yaml_set_key_complex(yaml_map_add_string(info, "0", "This is a complex key"), key);
child = yaml_get_child(info, "age", 0);
printf("child['age']: %d\r\n", yaml_value_int(child));
child = yaml_get_child(info, NULL, 1);
printf("child[1]: %f\r\n", yaml_value_float(child));
child = yaml_get_child_complex(info, key);
printf("child[?]: %s\r\n", yaml_value_string(child));
printf("child['email'] index = %u\r\n", yaml_get_index(info, "email", 0));
printf("child[?] index = %u\r\n", yaml_get_index_complex(info, key));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_alias(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "AuthorInfo", yaml);
printf("alias: %s\r\n", yaml_get_alias(info));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_anchor(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "AuthorInfo", yaml);
for (int i = 0; i < yaml_anchor_size(yaml); i++)
{
yaml_t t = yaml_get_anchor(yaml, i);
printf("anchor[%d]: %s\r\n", i, yaml_get_alias(t));
}
temp = yaml_map_add_mapping(yaml, "Something", NULL);
yaml_set_anchor(temp, "AuthorInfo", yaml);
temp = yaml_map_add_mapping(yaml, "Extended", NULL);
yaml_insert_reference(temp, NULL, 0, "AuthorInfo", yaml);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_copy(void)
{
yaml_t yaml = NULL, info = NULL, copy = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
printf("SOURCE yaml: \r\n");
yaml_preview(yaml);
printf("COPY yaml: \r\n");
copy = yaml_copy(yaml, YAML_F_RECURSE);
yaml_preview(copy);
yaml_delete(copy);
yaml_delete(copy);
}
static void test_compare(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL, copy = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
copy = yaml_copy(yaml, YAML_F_RECURSE);
printf("yaml_compare %d\r\n", yaml_compare(yaml, copy, YAML_F_RECURSE));
yaml_delete(copy);
yaml_delete(copy);
}
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
static void test_base(void)
{
// test_dump();
test_load();
}
/************************************************************************************/
/************************************* Command ************************************/
/************************************************************************************/
static void usage(void)
{
printf(
"Usage: yaml [opt] [arg] ...\n"
"\n"
"options:\n"
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
" <base> Test base function\n"
" <ut> Unit test\n"
" <create> Test create and delete functions\n"
" <add> Test add category functions\n"
" <insert> Test insert category functions\n"
" <remove> Test remove category functions\n"
" <set> Test set category functions\n"
" <get> Test get category functions\n"
" <key> Test key category functions\n"
" <child> Test child category functions\n"
" <alias> Test alias category functions\n"
" <anchor> Test anchor category functions\n"
" <copy> Test copy function\n"
" <compare> Test compare function\n"
" <dump> Test dump functions\n"
" <load> Test load functions\n"
" -h Print help\n"
" -v Print version\n"
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
"\n"
);
}
static int test(int argc, char *argv[])
{
char *execute = NULL;
int ut_period = 1000;
/* reset getopt */
command_opt_init();
while (1)
{
int opt = command_getopt(argc, argv, "e:hvu::");
if (opt == -1) break;
switch (opt)
{
case 'u' :
if (command_optarg) ut_period = atoi(command_optarg);
break;
case 'e' :
execute = command_optarg;
break;
case 'v' :
printf("yaml version %d.%d.%d\r\n", YAML_V_MAJOR, YAML_V_MINOR, YAML_V_PATCH);
return 0;
case '?':
printf("Unknown option `%c`\r\n", command_optopt);
return -1;
case 'h' :
default:
usage();
return 0;
}
}
if (execute)
{
if (!strcmp(execute, "base"))
{
test_base();
}
else if (!strcmp(execute, "ut"))
{
#if defined(TEST_TARGET_yaml)
while (1)
{
unitt_task();
usleep(1000 * ut_period);
}
#else
printf("create task %d\r\n", task_create(ut_period, unitt_task));
#endif
}
else if (!strcmp(execute, "add"))
{
test_add();
}
else if (!strcmp(execute, "insert"))
{
test_insert();
}
else if (!strcmp(execute, "remove"))
{
test_remove();
}
else if (!strcmp(execute, "set"))
{
test_set();
}
else if (!strcmp(execute, "get"))
{
test_get();
}
else if (!strcmp(execute, "key"))
{
test_key();
}
else if (!strcmp(execute, "child"))
{
test_child();
}
else if (!strcmp(execute, "alias"))
{
test_alias();
}
else if (!strcmp(execute, "anchor"))
{
test_anchor();
}
else if (!strcmp(execute, "copy"))
{
test_copy();
}
else if (!strcmp(execute, "compare"))
{
test_compare();
}
else if (!strcmp(execute, "dump"))
{
test_dump();
}
else if (!strcmp(execute, "load"))
{
test_load();
}
}
else
{
test_base();
}
return 0;
}
/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/
#if defined(TEST_TARGET_yaml)
int main(int argc, char *argv[])
{
return test(argc, argv);
}
#else
void test_yaml(void)
{
command_export("yaml", test);
}
init_export_app(test_yaml);
#endif