From 6f7bf8e9d7936ffdce8ca139a5621fcdabeba2cb Mon Sep 17 00:00:00 2001 From: NeoZng Date: Sat, 18 Mar 2023 20:36:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E7=AD=89=E7=BA=A7?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=92=8CRTT=20viewer=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/launch.json | 1 + .vscode/tasks.json | 7 +++++ VSCode+Ozone使用方法.md | 7 ++++- bsp/log/bsp_log.c | 5 ++-- bsp/log/bsp_log.h | 27 ++++++++++++++++---- bsp/usart/bsp_usart.c | 17 +++++++++++-- bsp/usart/bsp_usart.h | 25 +++++++++++++++--- modules/master_machine/master_process.c | 34 ++++++++++++++++++++++--- modules/motor/DJImotor/dji_motor.md | 6 ++++- 9 files changed, 110 insertions(+), 19 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 440a688..989a8f1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -35,6 +35,7 @@ "servertype": "jlink", "interface": "swd", "svdFile": "STM32F407.svd", + "rtos": "FreeRTOS", // "preLaunchTask": "build task",//先运行Build任务,取消注释即可使用 }, ], diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7ef9fdf..46dbedf 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -30,5 +30,12 @@ "isDefault": false, } }, + { + "label": "log", + "type": "shell", + "command":"JlinkRTTClient", + "args": [], + "problemMatcher": [], + } ] } \ No newline at end of file diff --git a/VSCode+Ozone使用方法.md b/VSCode+Ozone使用方法.md index a9f241c..17a8c1d 100644 --- a/VSCode+Ozone使用方法.md +++ b/VSCode+Ozone使用方法.md @@ -8,7 +8,6 @@ > > 1. 添加一键编译+启用ozone调试脚本,使得整个进一步流程自动化 > 2. 增加更多的背景知识介绍 -> 3. 增加VSCode下RTT viewer的支持 @@ -481,6 +480,12 @@ VSCode `ctrl+,`进入设置,通过`搜索`找到cortex-debug插件的设置。 +### RTT Viewer日志功能 + +本框架添加了vscode下Segger RTT client的支持。在`.vscode/task.json`中已经添加了启动rtt viewer client的任务。你也可以将此任务作为附加启动任务和调试一起启动,方便查看日志。要使用日志,请包含`bsp_log.h`。注意,需要将jlink的安装目录添加到环境变量中。 + + + ### 更好的编辑体验 建议安装以下插件: diff --git a/bsp/log/bsp_log.c b/bsp/log/bsp_log.c index 815a59b..630733e 100644 --- a/bsp/log/bsp_log.c +++ b/bsp/log/bsp_log.c @@ -15,7 +15,7 @@ int PrintLog(const char *fmt, ...) { va_list args; va_start(args, fmt); - int n = SEGGER_RTT_vprintf(BUFFER_INDEX, fmt, &args); + int n = SEGGER_RTT_vprintf(BUFFER_INDEX, fmt, &args); // 一次可以开启多个buffer(多个终端),我们只用一个 va_end(args); return n; } @@ -31,4 +31,5 @@ void Float2Str(char *str, float va) sprintf(str, "-%d.%d", head, point); else sprintf(str, "%d.%d", head, point); -} \ No newline at end of file +} + diff --git a/bsp/log/bsp_log.h b/bsp/log/bsp_log.h index b3d6e7a..d4fa43c 100644 --- a/bsp/log/bsp_log.h +++ b/bsp/log/bsp_log.h @@ -2,17 +2,34 @@ #define _BSP_LOG_H /** - * @brief 初始化日志功能,在操作系统启动之前调用 + * @brief 日志功能原型,供下面的LOGI,LOGW,LOGE等使用 * */ -void BSPLogInit(); +#define LOG_PROTO(type,color,format,...) \ + SEGGER_RTT_printf(BUFFER_INDEX," %s%s"format"\r\n%s", \ + color, \ + type, \ + ##__VA_ARGS__, \ + RTT_CTRL_RESET) + +/* 清屏 */ +#define LOG_CLEAR() SEGGER_RTT_WriteString(0, " "RTT_CTRL_CLEAR) + +/* 无颜色日志输出 */ +#define LOG(format,...) LOG_PROTO("","",format,##__VA_ARGS__) + +/* 有颜色格式日志输出 */ +#define LOGINFO(format,...) LOG_PROTO("I: ", RTT_CTRL_TEXT_BRIGHT_GREEN , format, ##__VA_ARGS__) +#define LOGWARNING(format,...) LOG_PROTO("W: ", RTT_CTRL_TEXT_BRIGHT_YELLOW, format, ##__VA_ARGS__) +#define LOGERROR(format,...) LOG_PROTO("E: ", RTT_CTRL_TEXT_BRIGHT_RED , format, ##__VA_ARGS__) + /** * @brief 通过segger RTT打印日志,支持格式化输出,格式化输出的实现参考printf * - * @param fmt - * @param ... - * @return int + * @param fmt 格式字符串 + * @param ... 参数列表 + * @return int 打印的log字符数 */ int PrintLog(const char *fmt, ...); diff --git a/bsp/usart/bsp_usart.c b/bsp/usart/bsp_usart.c index ba019d2..f1c0a97 100644 --- a/bsp/usart/bsp_usart.c +++ b/bsp/usart/bsp_usart.c @@ -25,7 +25,7 @@ static USARTInstance *usart_instance[DEVICE_USART_CNT] = {NULL}; * * @param _instance instance owned by module,模块拥有的串口实例 */ -static void USARTServiceInit(USARTInstance *_instance) +void USARTServiceInit(USARTInstance *_instance) { HAL_UARTEx_ReceiveToIdle_DMA(_instance->usart_handle, _instance->recv_buff, _instance->recv_buff_size); // 关闭dma half transfer中断防止两次进入HAL_UARTEx_RxEventCallback() @@ -67,11 +67,24 @@ void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size, break; default: while (1) - ; // illegal mode! check your code context! + ; // illegal mode! check your code context! 检查定义instance的代码上下文,可能出现指针越界 break; } } +/* 串口发送时,gstate会被设为BUSY_TX */ +uint8_t USARTIsReady(USARTInstance *_instance) +{ + if(_instance->usart_handle->gState | HAL_UART_STATE_BUSY_TX) + { + return 0; + } + else + { + return 1; + } +} + /** * @brief 每次dma/idle中断发生时,都会调用此函数.对于每个uart实例会调用对应的回调进行进一步的处理 * 例如:视觉协议解析/遥控器解析/裁判系统解析 diff --git a/bsp/usart/bsp_usart.h b/bsp/usart/bsp_usart.h index e7e9dcf..e6d5e4d 100644 --- a/bsp/usart/bsp_usart.h +++ b/bsp/usart/bsp_usart.h @@ -10,6 +10,7 @@ // 模块回调函数,用于解析协议 typedef void (*usart_module_callback)(); +/* 发送模式枚举 */ typedef enum { USART_TRANSFER_NONE=0, @@ -37,17 +38,25 @@ typedef struct } USART_Init_Config_s; /** - * @brief 注册一个串口实例. + * @brief 注册一个串口实例,返回一个串口实例指针 * * @param init_config 传入串口初始化结构体 */ USARTInstance *USARTRegister(USART_Init_Config_s *init_config); +/** + * @brief 启动串口服务,需要传入一个usart实例.一般用于lost callback的情况(使用串口的模块daemon) + * + * @param _instance + */ +void USARTServiceInit(USARTInstance *_instance); + + /** * @brief 通过调用该函数可以发送一帧数据,需要传入一个usart实例,发送buff以及这一帧的长度 - * 当前默认为DMA发送,后续会增加中断发送和阻塞发送模式的选择 - * @todo 目前只支持DMA发送,后续会增加中断发送和阻塞发送模式的选择 - * 在短时间内连续调用此接口会导致上一次的发送未完成而新的发送取消,后续会增加一个发送状态的判断以及消息队列以解决这个问题 + * @note 在短时间内连续调用此接口,若采用IT/DMA会导致上一次的发送未完成而新的发送取消. + * @note 若希望连续使用DMA/IT进行发送,请配合USARTIsReady()使用,或自行为你的module实现一个发送队列和任务. + * @todo 是否考虑为USARTInstance增加发送队列以进行连续发送? * * @param _instance 串口实例 * @param send_buf 待发送数据的buffer @@ -55,4 +64,12 @@ USARTInstance *USARTRegister(USART_Init_Config_s *init_config); */ void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size,USART_TRANSFER_MODE mode); +/** + * @brief 判断串口是否准备好,用于连续或异步的IT/DMA发送 + * + * @param _instance 要判断的串口实例 + * @return uint8_t ready 1, busy 0 + */ +uint8_t USARTIsReady(USARTInstance *_instance); + #endif diff --git a/modules/master_machine/master_process.c b/modules/master_machine/master_process.c index 3bed315..11044dd 100644 --- a/modules/master_machine/master_process.c +++ b/modules/master_machine/master_process.c @@ -12,12 +12,15 @@ #include "bsp_usart.h" #include "usart.h" #include "seasky_protocol.h" +#include "daemon.h" +#include "bsp_log.h" static Vision_Recv_s recv_data; // @todo:由于后续需要进行IMU-Cam的硬件触发采集控制,因此可能需要将发送设置为定时任务,或由IMU采集完成产生的中断唤醒的任务, // 后者显然更nice,使得时间戳对齐. 因此,在send_data中设定其他的标志位数据,让ins_task填充姿态值. // static Vision_Send_s send_data; static USARTInstance *vision_usart_instance; +static DaemonInstance *vision_daemon_instance; /** * @brief 接收解包回调函数,将在bsp_usart.c中被usart rx callback调用 @@ -27,8 +30,23 @@ static USARTInstance *vision_usart_instance; static void DecodeVision() { static uint16_t flag_register; + DaemonReload(vision_daemon_instance); // 喂狗 get_protocol_info(vision_usart_instance->recv_buff, &flag_register, (uint8_t *)&recv_data.pitch); // TODO: code to resolve flag_register; + PrintLog("decode vision"); + +} + +/** + * @brief 离线回调函数,将在daemon.c中被daemon task调用 + * @attention 由于HAL库的设计问题,串口开启DMA接收之后同时发送有概率出现__HAL_LOCK()导致的死锁,使得无法 + * 进入接收中断.通过daemon判断数据更新,重新调用服务启动函数以解决此问题. + * + * @param id vision_usart_instance的地址,此处没用. + */ +static void VisionOfflineCallback(void *id) +{ + USARTServiceInit(vision_usart_instance); } /* 视觉通信初始化 */ @@ -38,8 +56,16 @@ Vision_Recv_s *VisionInit(UART_HandleTypeDef *_handle) conf.module_callback = DecodeVision; conf.recv_buff_size = VISION_RECV_SIZE; conf.usart_handle = _handle; - vision_usart_instance = USARTRegister(&conf); + + // 为master process注册daemon,用于判断视觉通信是否离线 + Daemon_Init_Config_s daemon_conf = { + .callback = VisionOfflineCallback, // 离线时调用的回调函数,会重启串口接收 + .owner_id = vision_usart_instance, + .reload_count = 10, + }; + vision_daemon_instance = DaemonRegister(&daemon_conf); + return &recv_data; } @@ -58,11 +84,11 @@ void VisionSend(Vision_Send_s *send) static uint8_t send_buff[VISION_SEND_SIZE]; static uint16_t tx_len; // TODO: code to set flag_register - + // 将数据转化为seasky协议的数据包 get_protocol_send_data(0x02, flag_register, &send->yaw, 3, send_buff, &tx_len); - USARTSend(vision_usart_instance, send_buff, tx_len,USART_TRANSFER_IT); // 和视觉通信使用IT,防止和接收使用的DMA冲突 + USARTSend(vision_usart_instance, send_buff, tx_len, USART_TRANSFER_DMA); // 和视觉通信使用IT,防止和接收使用的DMA冲突 // 此处为HAL设计的缺陷,DMASTOP会停止发送和接收,导致再也无法进入接收中断. // 也可在发送完成中断中重新启动DMA接收,但较为复杂.因此,此处使用IT发送. - + // 若使用了daemon,则也可以使用DMA发送. } \ No newline at end of file diff --git a/modules/motor/DJImotor/dji_motor.md b/modules/motor/DJImotor/dji_motor.md index bed1d82..bec0ba5 100644 --- a/modules/motor/DJImotor/dji_motor.md +++ b/modules/motor/DJImotor/dji_motor.md @@ -7,6 +7,10 @@ > 1. 给不同的电机设置不同的低通滤波器惯性系数而不是统一使用宏 > 2. 为M2006和M3508增加开环的零位校准函数 +--- + +> 建议将电机的反馈频率通过RoboMaster Assistant统一设置为500Hz。当前默认的`MotorTask()`执行频率为500Hz,若不修改电机反馈频率可能导致单条总线挂载的电机数量有限,且容易出现帧错误和仲裁失败的情况。 + ## 总览和封装说明 > 如果你不需要理解该模块的工作原理,你只需要查看这一小节。 @@ -19,7 +23,7 @@ dji_motor模块对DJI智能电机,包括M2006,M3508以及GM6020进行了详 2. ==速度环为角速度,单位为**度/每秒**(deg/sec)== -3. ==电流环为mA== +3. ==电流环为A== 4. ==GM6020的输入设定为**力矩**,待测量(-30000~30000)==