Files
QDAC-firmware/libraries/zf_common/zf_common_debug.c
2023-12-11 21:45:06 +08:00

472 lines
18 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*********************************************************************************************************************
* CH32V307VCT6 Opensourec Library 即CH32V307VCT6 开源库)是一个基于官方 SDK 接口的第三方开源库
* Copyright (c) 2022 SEEKFREE 逐飞科技
*
* 本文件是CH32V307VCT6 开源库的一部分
*
* CH32V307VCT6 开源库 是免费软件
* 您可以根据自由软件基金会发布的 GPLGNU General Public License即 GNU通用公共许可证的条款
* 即 GPL 的第3版即 GPL3.0)或(您选择的)任何后来的版本,重新发布和/或修改它
*
* 本开源库的发布是希望它能发挥作用,但并未对其作任何的保证
* 甚至没有隐含的适销性或适合特定用途的保证
* 更多细节请参见 GPL
*
* 您应该在收到本开源库的同时收到一份 GPL 的副本
* 如果没有,请参阅<https://www.gnu.org/licenses/>
*
* 额外注明:
* 本开源库使用 GPL3.0 开源许可证协议 以上许可申明为译文版本
* 许可申明英文版在 libraries/doc 文件夹下的 GPL3_permission_statement.txt 文件中
* 许可证副本在 libraries 文件夹下 即该文件夹下的 LICENSE 文件
* 欢迎各位使用并传播本程序 但修改内容时必须保留逐飞科技的版权声明(即本声明)
*
* 文件名称 zf_common_debug
* 公司名称 成都逐飞科技有限公司
* 版本信息 查看 libraries/doc 文件夹内 version 文件 版本说明
* 开发环境 MounRiver Studio V1.8.1
* 适用平台 CH32V307VCT6
* 店铺链接 https://seekfree.taobao.com/
*
* 修改记录
* 日期 作者 备注
* 2022-09-15 大W first version
********************************************************************************************************************/
#include "zf_driver_uart.h"
#include "zf_common_interrupt.h"
#include "zf_common_fifo.h"
#include "zf_common_debug.h"
#if DEBUG_UART_USE_INTERRUPT // 如果启用 debug uart 接收中断
uint8 debug_uart_buffer[DEBUG_RING_BUFFER_LEN]; // 数据存放数组
uint8 debug_uart_data;
#endif
fifo_struct debug_uart_fifo;
static debug_output_struct debug_output_info;
static volatile uint8 zf_debug_init_flag = 0;
static volatile uint8 zf_debug_assert_enable = 1;
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 软延时函数 在 120MHz 下是一秒多的时间 各单片机需要根据各自时钟试验
// 参数说明 pass 判断是否触发断言
// 参数说明 *file 文件名
// 参数说明 line 目标行数
// 返回参数 void
//-------------------------------------------------------------------------------------------------------------------
static void debug_delay (void)
{
vuint32 loop_1 = 0, loop_2 = 0;
for(loop_1 = 0; loop_1 <= 0xFF; loop_1 ++)
for(loop_2 = 0; loop_2 <= 0xFFFF; loop_2 ++)
__NOP();
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 保护处理 主要是防止断言后出现信号维持而导致硬件失控
// 参数说明 void
// 返回参数 void
// 使用示例 debug_protective_handler();
// 备注信息 本函数在文件内部调用 用户不用关注 也不可修改
//-------------------------------------------------------------------------------------------------------------------
static void debug_protective_handler (void)
{
// 暂未更新
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 串口输出接口 此部分不允许用户更改
// 参数说明 *str 需要输出的字符串
// 返回参数 void
// 使用示例 debug_uart_str_output("Log message");
// 备注信息 本函数在文件内部调用 用户不用关注 也不可修改
//-------------------------------------------------------------------------------------------------------------------
static void debug_uart_str_output (const char *str)
{
uart_write_string(DEBUG_UART_INDEX, str);
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 输出接口
// 参数说明 *type log 类型
// 参数说明 *file 文件名
// 参数说明 line 目标行数
// 参数说明 *str 信息
// 返回参数 void
// 使用示例 debug_output("Log message", file, line, str);
// 备注信息 本函数在文件内部调用 用户不用关注 也不可修改
//-------------------------------------------------------------------------------------------------------------------
static void debug_output (char *type, char *file, int line, char *str)
{
char *file_str;
vuint16 i = 0, j = 0;
vint16 len_origin = 0;
vint16 show_len = 0;
vint16 show_line_index = 0;
len_origin = strlen(file);
char output_buffer[256];
char file_path_buffer[64];
if(debug_output_info.type_index)
{
debug_output_info.output_screen_clear();
}
if(zf_debug_init_flag)
{
if(debug_output_info.type_index)
{
// 需要分行将文件的路径和行数输出
// <不输出完整路径 只输出一级目录 例如 src/main.c>
// 输出 line : xxxx
debug_output_info.output_screen(0, show_line_index ++, type);
file_str = file;
len_origin = strlen(file);
show_len = (debug_output_info.display_x_max / debug_output_info.font_x_size);
while(*file_str++ != '\0');
// 只取一级目录 如果文件放在盘符根目录 或者 MDK 的工程根目录 就会直接输出当前目录
for(j = 0; (j < 2) && (len_origin >= 0); len_origin --) // 查找两个 '/'
{
file_str --;
if((*file_str == '/') || (*file_str == 0x5C))
{
j ++;
}
}
// 文件路径保存到数组中
if(len_origin >= 0)
{
file_str ++;
sprintf(output_buffer, "file: %s", file_str);
}
else
{
if(0 == j)
{
sprintf(output_buffer, "file: mdk/%s", file_str);
}
else
{
sprintf(output_buffer, "file: %s", file_str);
}
}
// 屏幕显示路径
for(i = 0; i < ((strlen(output_buffer) / show_len) + 1); i ++)
{
for(j = 0; j < show_len; j ++)
{
if(strlen(output_buffer) < (j + i * show_len))
{
break;
}
file_path_buffer[j] = output_buffer[j + i * show_len];
}
file_path_buffer[j] = '\0'; // 末尾添加\0
debug_output_info.output_screen(0, debug_output_info.font_y_size * show_line_index ++, file_path_buffer);
}
// 屏幕显示行号
sprintf(output_buffer, "line: %d", line);
debug_output_info.output_screen(0, debug_output_info.font_y_size * show_line_index ++, output_buffer);
// 屏幕显示 Log 如果有的话
if(NULL != str)
{
for(i = 0; i < ((strlen(str) / show_len) + 1); i ++)
{
for(j = 0; j < show_len; j ++)
{
if(strlen(str) < (j + i * show_len))
{
break;
}
file_path_buffer[j] = str[j + i * show_len];
}
file_path_buffer[j] = '\0'; // 末尾添加\0
debug_output_info.output_screen(0, debug_output_info.font_y_size * show_line_index ++, file_path_buffer);
}
}
}
else
{
char output_buffer[256];
memset(output_buffer, 0, 256);
debug_output_info.output_uart(type);
if(NULL != str)
{
sprintf(output_buffer, "\r\nfile %s line %d: %s.\r\n", file, line, str);
}
else
{
sprintf(output_buffer, "\r\nfile %s line %d.\r\n", file, line);
}
debug_output_info.output_uart(output_buffer);
}
}
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 调试串口发送缓冲区
// 参数说明 *buff 读出数据存放的数组指针
// 参数说明 len 需要发送的长度
// 返回参数 uint32 剩余未发送的长度
// 使用示例
// 备注信息 本函数需要开启 DEBUG_UART_USE_INTERRUPT 宏定义才可使用
//-------------------------------------------------------------------------------------------------------------------
uint32 debug_send_buffer(const uint8 *buff, uint32 len)
{
uart_write_buffer(DEBUG_UART_INDEX, buff, len);
return 0;
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 读取 debug 环形缓冲区数据
// 参数说明 *buff 读出数据存放的数组指针
// 参数说明 len 需要读取的长度
// 返回参数 uint32 读出数据的实际长度
// 使用示例
// 备注信息 本函数需要开启 DEBUG_UART_USE_INTERRUPT 宏定义才可使用
//-------------------------------------------------------------------------------------------------------------------
uint32 debug_read_ring_buffer (uint8 *buff, uint32 len)
{
fifo_read_buffer(&debug_uart_fifo, buff, &len, FIFO_READ_AND_CLEAN);
return len;
}
#if DEBUG_UART_USE_INTERRUPT // 条件编译 只有在启用串口中断才编译
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 串口中断处理函数 isr.c 中对应串口中断服务函数调用
// 参数说明 void
// 返回参数 void
// 使用示例 debug_interrupr_handler();
// 备注信息 本函数需要开启 DEBUG_UART_USE_INTERRUPT 宏定义才可使用
// 并且本函数默认放置在 UART1 的串口接收中断处理处
//-------------------------------------------------------------------------------------------------------------------
void debug_interrupr_handler (void)
{
if(zf_debug_init_flag)
{
uart_query_byte(DEBUG_UART_INDEX, &debug_uart_data); // 读取串口数据
fifo_write_buffer(&debug_uart_fifo, &debug_uart_data, 1); // 存入 FIFO
}
}
#endif
//------------------------------------------------------------------------- // printf 重定向 此部分不允许用户更改
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 printf重定向
// 参数说明 void
// 返回参数 void
// @since v1.0
// 备注信息 重定向printf到DEBUG串口上
//-------------------------------------------------------------------------------------------------------------------
#if (1 == PRINTF_ENABLE)
int _write(int fd, char *buf, int size)
{
int i;
for(i=0; i<size; i++)
{
while (USART_GetFlagStatus((USART_TypeDef*)uart_index[DEBUG_UART_INDEX], USART_FLAG_TC) == RESET);
USART_SendData((USART_TypeDef*)uart_index[DEBUG_UART_INDEX], *buf++);
}
return size;
}
#endif
//------------------------------------------------------------------------- // printf 重定向 此部分不允许用户更改
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 启用断言
// 参数说明 void
// 返回参数 void
// 使用示例 debug_assert_enable();
// 备注信息 断言默认开启 建议开启断言
//-------------------------------------------------------------------------------------------------------------------
void debug_assert_enable (void)
{
zf_debug_assert_enable = 1;
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 禁用断言
// 参数说明 void
// 返回参数 void
// 使用示例 debug_assert_disable();
// 备注信息 断言默认开启 不建议禁用断言
//-------------------------------------------------------------------------------------------------------------------
void debug_assert_disable (void)
{
zf_debug_assert_enable = 0;
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 断言处理函数 此部分不允许用户更改
// 参数说明 pass 判断是否触发断言
// 参数说明 *file 文件名
// 参数说明 line 目标行数
// 返回参数 void
// 使用示例 zf_assert(0);
// 备注信息 这个函数不是直接调用的 此部分不允许用户更改
// 使用 zf_commmon_debug.h 中的 zf_assert(x) 接口
//-------------------------------------------------------------------------------------------------------------------
void debug_assert_handler (uint8 pass, char *file, int line)
{
do
{
if(pass || !zf_debug_assert_enable)
{
break;
}
static uint8 assert_nest_index = 0;
if(0 != assert_nest_index)
{
while(1);
}
assert_nest_index ++;
interrupt_global_disable();
debug_protective_handler();
while(1)
{
// 如果代码跳转到这里停住了
// 一般你的函数参数传递出错了
// 或者你自己调用的 zf_assert(x) 接口处报错了
// 如果调用了 debug_init 初始化了 log 输出
// 就在对应串口输出去查看是哪个文件的哪一行报错
// 如果没有初始化 debug
// 那就看看这个 file 的字符串值和 line 的行数
// 那代表报错的文件路径名称和对应报错行数
// 再去调试看看是为什么参数出错
debug_output("Assert error", file, line, NULL);
debug_delay();
}
}while(0);
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 调试信息处理函数 此部分不允许用户更改
// 参数说明 bool 判断是否触发断言
// 参数说明 *str 要输出的调试信息
// 参数说明 *file 文件名
// 参数说明 line 目标行数
// 返回参数 void
// 使用示例 zf_log(0, "Log Message");
// 备注信息 这个函数不是直接调用的 此部分不允许用户更改
// 使用 zf_commmon_debug.h 中的 zf_log(x, str) 接口
//-------------------------------------------------------------------------------------------------------------------
void debug_log_handler (uint8 pass, char *str, char *file, int line)
{
do
{
if(pass)
{
break;
}
if(zf_debug_init_flag)
{
debug_output("Log message", file, line, str);
// printf("Log message from %s line %d :\"%s\".\r\n", file, line, str);
}
}while(0);
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 输出绑定信息初始化 此部分不允许用户更改
// 参数说明 *info debug 输出的信息结构体
// 返回参数 void
// Sample usage: debug_output_struct_init(info);
//-------------------------------------------------------------------------------------------------------------------
void debug_output_struct_init (debug_output_struct *info)
{
info->type_index = 0;
info->display_x_max = 0xFFFF;
info->display_y_max = 0xFFFF;
info->font_x_size = 0xFF;
info->font_y_size = 0xFF;
info->output_uart = NULL;
info->output_screen = NULL;
info->output_screen_clear = NULL;
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 输出绑定初始化 此部分不允许用户更改
// 参数说明 *info debug 输出的信息结构体
// 返回参数 void
// 使用示例 debug_output_init(info);
// 备注信息 这个函数一般不由用户调用
//-------------------------------------------------------------------------------------------------------------------
void debug_output_init (debug_output_struct *info)
{
debug_output_info.type_index = info->type_index;
debug_output_info.display_x_max = info->display_x_max;
debug_output_info.display_y_max = info->display_y_max;
debug_output_info.font_x_size = info->font_x_size;
debug_output_info.font_y_size = info->font_y_size;
debug_output_info.output_uart = info->output_uart;
debug_output_info.output_screen = info->output_screen;
debug_output_info.output_screen_clear = info->output_screen_clear;
zf_debug_init_flag = 1;
}
//-------------------------------------------------------------------------------------------------------------------
// 函数简介 debug 串口初始化 此部分不允许用户更改
// 参数说明 void
// 返回参数 void
// 使用示例 debug_init();
// 备注信息 开源库示例默认调用 但默认禁用中断接收
//-------------------------------------------------------------------------------------------------------------------
void debug_init (void)
{
debug_output_struct info;
debug_output_struct_init(&info);
info.output_uart = debug_uart_str_output;
debug_output_init(&info);
uart_init(
DEBUG_UART_INDEX, // 在 zf_common_debug.h 中查看对应值
DEBUG_UART_BAUDRATE, // 在 zf_common_debug.h 中查看对应值
DEBUG_UART_TX_PIN, // 在 zf_common_debug.h 中查看对应值
DEBUG_UART_RX_PIN); // 在 zf_common_debug.h 中查看对应值
#if DEBUG_UART_USE_INTERRUPT // 条件编译 只有在启用串口中断才编译
fifo_init(&debug_uart_fifo, FIFO_DATA_8BIT, debug_uart_buffer, DEBUG_RING_BUFFER_LEN);
uart_rx_interrupt(DEBUG_UART_INDEX, 1); // 使能对应串口接收中断
#endif
}