新建项目

This commit is contained in:
gitea committed 2023-04-29 09:09:12 +08:00
1 parent 5e2e890cfc
commit 633d9b872b
1351 files changed
+730564

No files matched your search

@@ -0,0 +1,309 @@
/**
* @file log.c
* @author Letter (nevermindzzt@gmail.com)
* @brief log
* @version 1.0.0
* @date 2020-07-30
*
* @copyright (c) 2020 Letter
*
*/
#include "log.h"
#include "stdio.h"
#include "stdarg.h"
#include "shell.h"
#if LOG_USING_COLOR == 1
#define memPrintHead CSI(31) \
" Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" \
CSI(39) \
"\r\n"
#define memPrintAddr CSI(31)"0x%08x: "CSI(39)
#else
#define memPrintHead " Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n"
#define memPrintAddr "0x%08x: "
#endif
Log *logList[LOG_MAX_NUMBER] = {0};
static char logBuffer[LOG_BUFFER_SIZE];
#if LOG_USING_LOCK == 1
/**
* @brief 上锁log对象
* @param log log对象
*/
static void logLock(Log *log)
{
if (log == LOG_ALL_OBJ)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] && logList[i]->active)
{
if (logList[i]->lock)
{
LOG_LOCK(logList[i]);
}
}
}
}
else if (log->lock)
{
LOG_LOCK(log);
}
}
/**
* @brief 解锁log对象
* @param log log对象
*/
static void logUnlock(Log *log)
{
if (log == LOG_ALL_OBJ)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] && logList[i]->active)
{
if (logList[i]->unlock)
{
LOG_UNLOCK(logList[i]);
}
}
}
}
else if (log->unlock)
{
LOG_UNLOCK(log);
}
}
#endif /* LOG_USING_LOCK == 1 */
/**
* @brief 注册log对象
*
* @param log log对象
*/
void logRegister(Log *log, Shell *shell)
{
if (shell)
{
log->shell = shell;
#if SHELL_USING_COMPANION == 1
shellCompanionAdd(shell, SHELL_COMPANION_ID_LOG, log);
#endif
}
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] == 0)
{
logList[i] = log;
return;
}
}
}
/**
* @brief 注销log对象
*
* @param log log对象
*/
void logUnRegister(Log *log)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i] == log)
{
logList[i] = 0;
return;
}
}
}
/**
* @brief 设置log日志级别
*
* @param log log对象
* @param level 日志级别
*/
void logSetLevel(Log *log, LogLevel level)
{
logAssert(log, return);
log->level = level;
}
#if SHELL_USING_COMPANION == 1
SHELL_EXPORT_CMD_AGENCY(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
logSetLevel, logSetLevel, set log level\r\n logSetLevel [level],
(void *)shellCompanionGet(shellGetCurrent(), SHELL_COMPANION_ID_LOG), p1);
#else
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
logSetLevel, logSetLevel, set log level\r\n logSetLevel [log] [level]);
#endif /** SHELL_USING_COMPANION == 1 */
/**
* @brief log写buffer
*
* @param log log对象
* @param level 日志级别
* @param buffer buffer
* @param len buffer长度
*/
static void logWriteBuffer(Log *log, LogLevel level, char *buffer, short len)
{
#if LOG_USING_LOCK == 1
logLock(log);
#endif /* LOG_USING_LOCK == 1 */
if (log == LOG_ALL_OBJ)
{
for (short i = 0; i < LOG_MAX_NUMBER; i++)
{
if (logList[i]
&& logList[i]->active
&& logList[i]->level >= level)
{
logList[i]->write(logBuffer, len);
}
}
}
else if (log && log->active && log->level >= level)
{
log->write(logBuffer, len);
}
#if LOG_USING_LOCK == 1
logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}
/**
* @brief log格式化写入数据
*
* @param log log对象
* @param level log级别
* @param fmt 格式
* @param ... 参数
*/
void logWrite(Log *log, LogLevel level, const char *fmt, ...)
{
va_list vargs;
short len;
#if LOG_USING_LOCK == 1
logLock(log);
#endif /* LOG_USING_LOCK == 1 */
va_start(vargs, fmt);
len = vsnprintf(logBuffer, LOG_BUFFER_SIZE - 1, fmt, vargs);
va_end(vargs);
logWriteBuffer(log, level, logBuffer, len);
#if LOG_USING_LOCK == 1
logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}
/**
* @brief 16进制输出
*
* @param log log对象
* @param level 日志级别
* @param base 内存基址
* @param length 长度
*/
void logHexDump(Log *log, LogLevel level, void *base, unsigned int length)
{
unsigned char *address;
unsigned int len;
unsigned int printLen = 0;
if (length == 0 || (log != LOG_ALL_OBJ && log->level < level))
{
return;
}
#if LOG_USING_LOCK == 1
logLock(log);
#endif /* LOG_USING_LOCK == 1 */
len = snprintf(logBuffer, LOG_BUFFER_SIZE - 1, "memory of 0x%08x, size: %d:\r\n%s",
(unsigned int)base, length, memPrintHead);
logWriteBuffer(log, level, logBuffer, len);
len = length;
address = (unsigned char *)((unsigned int)base & (~0x0000000F));
length += (unsigned int)base - (unsigned int)address;
length = (length + 15) & (~0x0000000F);
while (length)
{
printLen += sprintf(logBuffer + printLen, memPrintAddr, (unsigned int)address);
for (int i = 0; i < 16; i++)
{
if ((unsigned int)(address + i) < (unsigned int)base
|| (unsigned int)(address + i) >= (unsigned int)base + len)
{
logBuffer[printLen ++] = ' ';
logBuffer[printLen ++] = ' ';
logBuffer[printLen ++] = ' ';
}
else
{
printLen += sprintf(logBuffer + printLen, "%02x ", *(address + i));
}
}
logBuffer[printLen ++] = '|';
logBuffer[printLen ++] = ' ';
for (int i = 0; i < 16; i++)
{
if ((unsigned int)(address + i) < (unsigned int)base
|| (unsigned int)(address + i) >= (unsigned int)base + len)
{
logBuffer[printLen ++] = ' ';
}
else
{
if (*(address + i) >= 32 && *(address + i) <= 126)
{
printLen += sprintf(logBuffer + printLen, "%c", *(address + i));
}
else
{
logBuffer[printLen ++] = '.';
}
}
}
logBuffer[printLen ++] = ' ';
logBuffer[printLen ++] = '|';
logBuffer[printLen ++] = '\r';
logBuffer[printLen ++] = '\n';
logWriteBuffer(log, level, logBuffer, printLen);
address += 16;
length -= 16;
printLen = 0;
}
#if LOG_USING_LOCK == 1
logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}
#if SHELL_USING_COMPANION == 1
SHELL_EXPORT_CMD_AGENCY(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
hexdump, logHexDump, hex dump\r\n hexdump [base] [len],
(void *)shellCompanionGet(shellGetCurrent(), SHELL_COMPANION_ID_LOG), LOG_NONE, (void *)p1, (unsigned int)p2);
#else
SHELL_EXPORT_CMD(
SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,
hexdump, logHexDump, hex dump\r\n hexdump [log] [level] [base] [len]);
#endif /** SHELL_USING_COMPANION == 1 */
#if SHELL_USING_COMPANION == 1
void logSwitchLevel(Shell *shell)
{
Log *log = shellCompanionGet(shell, SHELL_COMPANION_ID_LOG);
SHELL_ASSERT(log, return);
log->level = (LogLevel)(log->level >= LOG_ALL ? LOG_NONE : (log->level + 1));
logPrintln("set log level: %d", log->level);
}
SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x04000000, logSwitchLevel, switch log level);
#endif /** SHELL_USING_COMPANION == 1 */
@@ -0,0 +1,224 @@
/**
* @file log.h
* @author Letter (nevermindzzt@gmail.com)
* @brief log
* @version 1.0.0
* @date 2020-07-30
*
* @copyright (c) 2020 Letter
*
*/
#ifndef __LOG_H__
#define __LOG_H__
#ifdef __cplusplus
extern "C" {
#endif /**< defined __cplusplus */
#include "shell.h"
#define LOG_VERSION "1.0.1"
#define SHELL_COMPANION_ID_LOG -2
#define LOG_USING_LOCK 0
#define LOG_BUFFER_SIZE 256 /**< log输出缓冲大小 */
#define LOG_USING_COLOR 1 /**< 是否使用颜色 */
#define LOG_MAX_NUMBER 5 /**< 允许注册的最大log对象数量 */
#define LOG_AUTO_TAG 1 /**< 是否自动添加TAG */
#define LOG_END "\r\n" /**< log信息结尾 */
#define LOG_TIME_STAMP 0 /**< 设置获取系统时间戳 */
#ifndef LOG_TAG
#define LOG_TAG __FUNCTION__ /**< 自定添加的TAG */
#endif
#ifndef LOG_ENABLE
#define LOG_ENABLE 1 /**< 使能log */
#endif
#if LOG_USING_LOCK == 1
#define LOG_LOCK(log) log->lock(log)
#define LOG_UNLOCK(log) log->unlock(log)
#else
#define LOG_LOCK(log)
#define LOG_UNLOCK(log)
#endif /* LOG_USING_LOCK == 1 */
#define LOG_ALL_OBJ ((Log *)-1) /**< 所有已注册的log对象 */
/**
* 终端字体颜色代码
*/
#define CSI_BLACK 30 /**< 黑色 */
#define CSI_RED 31 /**< 红色 */
#define CSI_GREEN 32 /**< 绿色 */
#define CSI_YELLOW 33 /**< 黄色 */
#define CSI_BLUE 34 /**< 蓝色 */
#define CSI_FUCHSIN 35 /**< 品红 */
#define CSI_CYAN 36 /**< 青色 */
#define CSI_WHITE 37 /**< 白色 */
#define CSI_BLACK_L 90 /**< 亮黑 */
#define CSI_RED_L 91 /**< 亮红 */
#define CSI_GREEN_L 92 /**< 亮绿 */
#define CSI_YELLOW_L 93 /**< 亮黄 */
#define CSI_BLUE_L 94 /**< 亮蓝 */
#define CSI_FUCHSIN_L 95 /**< 亮品红 */
#define CSI_CYAN_L 96 /**< 亮青 */
#define CSI_WHITE_L 97 /**< 亮白 */
#define CSI_DEFAULT 39 /**< 默认 */
#define CSI(code) "\033[" #code "m" /**< ANSI CSI指令 */
/**
* log级别字符(包含颜色)
*/
#if LOG_USING_COLOR == 1
#define ERROR_TEXT CSI(31) "E(%d) %s:" CSI(39) /**< 错误标签 */
#define WARNING_TEXT CSI(33) "W(%d) %s:" CSI(39) /**< 警告标签 */
#define INFO_TEXT CSI(32) "I(%d) %s:" CSI(39) /**< 信息标签 */
#define DEBUG_TEXT CSI(34) "D(%d) %s:" CSI(39) /**< 调试标签 */
#define VERBOSE_TEXT CSI(36) "V(%d) %s:" CSI(39) /**< 冗余信息标签 */
#else
#define ERROR_TEXT "E(%d) %s:"
#define WARNING_TEXT "W(%d) %s:"
#define INFO_TEXT "I(%d) %s:"
#define DEBUG_TEXT "D(%d) %s:"
#define VERBOSE_TEXT "V(%d) %s:"
#endif
/**
* @brief 日志级别定义
*
*/
typedef enum
{
LOG_NONE = 0, /**< 无级别 */
LOG_ERROR = 1, /**< 错误 */
LOG_WRANING = 2, /**< 警告 */
LOG_INFO = 3, /**< 消息 */
LOG_DEBUG = 4, /**< 调试 */
LOG_VERBOSE = 5, /**< 冗余 */
LOG_ALL = 6, /**< 所有日志 */
} LogLevel;
/**
* @brief log对象定义
*
*/
typedef struct log_def
{
void (*write)(char *, short); /**< 写buffer */
char active; /**< 是否激活 */
LogLevel level; /**< 日志级别 */
#if LOG_USING_LOCK == 1
int (*lock)(struct log_def *); /**< log 加锁 */
int (*unlock)(struct log_def *); /**< log 解锁 */
#endif /** LOG_USING_LOCK == 1 */
Shell *shell; /**< 关联shell对象 */
} Log;
/**
* @brief log打印(自动换行)
*
* @param fmt 格式
* @param ... 参数
*/
#define logPrintln(format, ...) \
logWrite(LOG_ALL_OBJ, LOG_NONE, format "\r\n", ##__VA_ARGS__)
/**
* @brief 日志格式化输出
*
* @param text 消息文本
* @param level 日志级别
* @param fmt 格式
* @param ... 参数
*/
#define logFormat(text, level, fmt, ...) \
if (LOG_ENABLE) {\
logWrite(LOG_ALL_OBJ, level, text " " fmt "" LOG_END, \
LOG_TIME_STAMP, LOG_TAG, ##__VA_ARGS__); }
/**
* @brief 错误log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logError(fmt, ...) \
logFormat(ERROR_TEXT, LOG_ERROR, fmt, ##__VA_ARGS__)
/**
* @brief 警告log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logWarning(fmt, ...) \
logFormat(WARNING_TEXT, LOG_WRANING, fmt, ##__VA_ARGS__)
/**
* @brief 信息log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logInfo(fmt, ...) \
logFormat(INFO_TEXT, LOG_INFO, fmt, ##__VA_ARGS__)
/**
* @brief 调试log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logDebug(fmt, ...) \
logFormat(DEBUG_TEXT, LOG_DEBUG, fmt, ##__VA_ARGS__)
/**
* @brief 冗余log输出
*
* @param fmt 格式
* @param ... 参数
*/
#define logVerbose(fmt, ...) \
logFormat(VERBOSE_TEXT, LOG_VERBOSE, fmt, ##__VA_ARGS__)
/**
* @brief 断言
*
* @param expr 表达式
* @param action 断言失败操作
*/
#define logAssert(expr, action) \
if (!(expr)) { \
logError("\"" #expr "\" assert failed at file: %s, line: %d", __FILE__, __LINE__); \
action; \
}
/**
* @brief 16进制输出到所有终端
*
* @param base 内存基址
* @param length 长度
*/
#define logHexDumpAll(base, length) \
logHexDump(LOG_ALL_OBJ, LOG_ALL, base, length)
void logRegister(Log *log, Shell *shell);
void logUnRegister(Log *log);
void logSetLevel(Log *log, LogLevel level);
void logWrite(Log *log, LogLevel level, const char *fmt, ...);
void logHexDump(Log *log, LogLevel level, void *base, unsigned int length);
#ifdef __cplusplus
}
#endif /**< defined __cplusplus */
#endif
@@ -0,0 +1,215 @@
# log
![version](https://img.shields.io/badge/version-1.0.1-brightgreen.svg)
![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg)
![build](https://img.shields.io/badge/build-2020.08.02-brightgreen.svg)
![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)
嵌入式日志打印工具
![preview](../../doc/img/log_preview.png)
- [log](#log)
- [简介](#简介)
- [使用](#使用)
- [配置](#配置)
- [API](#api)
- [logPrintln](#logprintln)
- [logError](#logerror)
- [logWarning](#logwarning)
- [logInfo](#loginfo)
- [logDebug](#logdebug)
- [logVerbose](#logverbose)
- [logAssert](#logassert)
- [logRegister](#logregister)
- [logSetLevel](#logsetlevel)
- [logHexDump](#loghexdump)
- [结合letter shell尾行模式](#结合letter-shell尾行模式)
- [其他用法](#其他用法)
- [单独控制某个文件日志](#单独控制某个文件日志)
## 简介
log是一个用于嵌入式系统的日志打印工具,可以为日志定义不同的级别,然后设置日志工具的打印级别,可以进行日志打印的控制
此外,log通过letter shell的伴生对象功能,可以和letter shell结合,实现log和shell的绑定等功能
## 使用
1. 实现log写buffer函数
```C
void uartLogWrite(char *buffer, short len)
{
serialTransmit(&debugSerial, (uint8_t *)buffer, len, 0x100);
}
```
2. 定义log对象
```C
Log uartLog = {
.write = uartLogWrite,
.active = true,
.level = LOG_DEBUG
};
```
3. 注册log对象
```C
logRegister(&uartLog, NULL);
```
## 配置
通过修改log.h文件中的宏,可以对log工具进行配置
| 宏 | 意义 |
| --------------- | ------------------------- |
| LOG_BUFFER_SIZE | log输出缓冲大小 |
| LOG_USING_COLOR | 是否使用颜色 |
| LOG_MAX_NUMBER | 允许注册的最大log对象数量 |
| LOG_AUTO_TAG | 是否自动添加TAG |
| LOG_END | log信息结尾 |
| LOG_TAG | 自定添加的TAG |
| LOG_TIME_STAMP | 设置获取系统时间戳 |
## API
以下是log工具部分API的说明
### logPrintln
宏声明,用于一般的打印输出
```C
#define logPrintln(format, ...)
```
- 参数
- `format` 输出格式
- `...` 可变参数
### logError
宏声明,错误日志级别输出
```C
#define logError(fmt, ...)
```
- 参数
- `fmt` 输出格式
- `...` 可变参数
### logWarning
宏声明,警告日志级别输出,函数原型及参数说明参考`logError`
### logInfo
宏声明,信息日志级别输出,函数原型及参数说明参考`logError`
### logDebug
宏声明,调试日志级别输出,函数原型及参数说明参考`logError`
### logVerbose
宏声明,冗余日志级别输出,函数原型及参数说明参考`logError`
### logAssert
宏声明,断言
```C
#define logAssert(expr, action)
```
- 参数
- `expr` 表达式
- `action` 断言失败执行操作
### logRegister
注册log对象
```C
void logRegister(Log *log, Shell *shell)
```
- 参数
- `log` log对象
- `shell` 关联的shell对象
### logSetLevel
设置日志级别
```C
void logSetLevel(Log *log, LogLevel level)
```
- 参数
- `log` log对象
- `level` 日志级别
### logHexDump
数据16进制打印
```C
void logHexDump(Log *log, LogLevel level, void *base, unsigned int length)
```
- 参数
- `log` log对象
- `level` 日志级别
- `base` 数据基址
- `length` 数据长度
## 结合letter shell尾行模式
log工具可以结合letter shell的尾行模式,实现log和shell共用一个终端,但不影响shell交互体验
1. 使用`shellWriteEndLine`事项log写buffer函数
```C
void uartLogWrite(char *buffer, short len)
{
if (uartLog.shell)
{
shellWriteEndLine(uartLog.shell, buffer, len);
}
}
```
2. 定义log对象
```C
Log uartLog = {
.write = uartLogWrite,
.active = true,
.level = LOG_DEBUG
};
```
3. 注册log对象
```C
logRegister(&uartLog, &shell);
```
## 其他用法
### 单独控制某个文件日志
log工具可以单独对某个文件的日志进行打印控制,使用时,在对应的.c文件中加入
```C
#undef LOG_ENABLE
#define LOG_ENABLE 1
```
即可单独控制某个文件的日志开关