diff --git a/Core/Src/freertos.c b/Core/Src/freertos.c index 9539c9d..fab278a 100644 --- a/Core/Src/freertos.c +++ b/Core/Src/freertos.c @@ -35,6 +35,7 @@ // #include "bsp_log.h" #include "dev_cmd.h" #include "daemon.h" +#include "bsp_usart.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -449,6 +450,8 @@ __attribute__((noreturn)) void StartDaemonTask(void *argument) /* Infinite loop */ for (;;) { + USARTServiceTask(); + // 1. 执行核心的数据刷新逻辑 Daemon_Update(); diff --git a/Core/Src/usart.c b/Core/Src/usart.c index 1011755..1bdac9e 100644 --- a/Core/Src/usart.c +++ b/Core/Src/usart.c @@ -361,7 +361,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) hdma_uart5_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_uart5_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_uart5_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; - hdma_uart5_rx.Init.Mode = DMA_CIRCULAR; + hdma_uart5_rx.Init.Mode = DMA_NORMAL; hdma_uart5_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH; hdma_uart5_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_uart5_rx) != HAL_OK) diff --git a/STM32H723XG_FLASH.ld b/STM32H723XG_FLASH.ld index b3edcfa..856e5a0 100644 --- a/STM32H723XG_FLASH.ld +++ b/STM32H723XG_FLASH.ld @@ -227,6 +227,17 @@ SECTIONS PROVIDE( __bss_start = _sbss ); PROVIDE( __bss_size = __bss_end - __bss_start ); + /* DMA1/DMA2 cannot access DTCM. The MPU config makes RAM_D1 non-cacheable. */ + .dma_buffer (NOLOAD) : + { + . = ALIGN(32); + __dma_buffer_start__ = .; + KEEP(*(.dma_buffer)) + KEEP(*(.dma_buffer.*)) + . = ALIGN(32); + __dma_buffer_end__ = .; + } >RAM_D1 + /* 用户堆栈段,用于检查剩余RAM是否足够 */ ._user_heap_stack (NOLOAD) : { diff --git a/TronOneH7_Scaffold.ioc b/TronOneH7_Scaffold.ioc index 6de4824..6420c7a 100644 --- a/TronOneH7_Scaffold.ioc +++ b/TronOneH7_Scaffold.ioc @@ -156,7 +156,7 @@ Dma.UART5_RX.13.FIFOMode=DMA_FIFOMODE_DISABLE Dma.UART5_RX.13.Instance=DMA1_Stream5 Dma.UART5_RX.13.MemDataAlignment=DMA_MDATAALIGN_BYTE Dma.UART5_RX.13.MemInc=DMA_MINC_ENABLE -Dma.UART5_RX.13.Mode=DMA_CIRCULAR +Dma.UART5_RX.13.Mode=DMA_NORMAL Dma.UART5_RX.13.PeriphDataAlignment=DMA_PDATAALIGN_BYTE Dma.UART5_RX.13.PeriphInc=DMA_PINC_DISABLE Dma.UART5_RX.13.Polarity=HAL_DMAMUX_REQ_GEN_RISING diff --git a/User_Code/application/indicator_app/ws2812status.c b/User_Code/application/indicator_app/ws2812status.c index bc94ee2..8bff7b0 100644 --- a/User_Code/application/indicator_app/ws2812status.c +++ b/User_Code/application/indicator_app/ws2812status.c @@ -24,6 +24,7 @@ #include "delayticks.h" #include "robot_def.h" +#include "rc.h" /*---------------------VARIABLES---------------------*/ uint8_t r = 0; @@ -53,10 +54,13 @@ void robotSelfCheck(void) void ws2812Task(void *argument) { (void) argument; - RobotMode_t RobotMode = REMOTE_NOT_CONNECTED; // 初始状态为遥控器未连接 while (1) { - switch (RobotMode) + RobotMode_t display_mode = RobotMode; + if (display_mode != SYS_ERROR_OCCURRED && !RemoteControlIsOnline()) + display_mode = REMOTE_NOT_CONNECTED; + + switch (display_mode) { case NORMAL_MODE: BlinkGreen(); diff --git a/User_Code/application/robot.c b/User_Code/application/robot.c index 38e4425..05ab6f4 100644 --- a/User_Code/application/robot.c +++ b/User_Code/application/robot.c @@ -16,6 +16,8 @@ #include "bsp_init.h" #include "robot.h" +#include "rc.h" +#include "robot_def.h" #include "cmsis_gcc.h" // #include "robot_def.h" @@ -27,6 +29,8 @@ // #pragma message "check if you have configured the parameters in robot_def.h, IF NOT, please refer to the comments AND DO IT, otherwise the robot will have FATAL ERRORS!!!" // #endif // !ROBOT_DEF_PARAM_WARNING +volatile RobotMode_t RobotMode = REMOTE_NOT_CONNECTED; + // #if defined(ONE_BOARD) || defined(CHASSIS_BOARD) // #include "chassis.h" // #endif @@ -59,6 +63,9 @@ void RobotInit() BSPInit(); + if (RemoteControlInit(&huart5) == NULL) + RobotMode = SYS_ERROR_OCCURRED; + #if defined(ONE_BOARD) || defined(GIMBAL_BOARD) RobotCMDInit(); // GimbalInit(); diff --git a/User_Code/bsp/usart/bsp_usart.c b/User_Code/bsp/usart/bsp_usart.c index 5c02096..4a934fa 100644 --- a/User_Code/bsp/usart/bsp_usart.c +++ b/User_Code/bsp/usart/bsp_usart.c @@ -11,43 +11,116 @@ #include "bsp_usart.h" #include "bsp_log.h" -#include "stdlib.h" #include "memory.h" /* usart service instance, modules' info would be recoreded here using USARTRegister() */ /* usart服务实例,所有注册了usart的模块信息会被保存在这里 */ static uint8_t idx; static USART_Instance *usart_instance[DEVICE_USART_CNT] = {NULL}; +static USART_Instance usart_instance_pool[DEVICE_USART_CNT] + __attribute__((section(".dma_buffer"), aligned(32))); + +static USART_Instance *USARTFindInstance(UART_HandleTypeDef *huart) +{ + for (uint8_t i = 0; i < idx; ++i) + { + if (usart_instance[i]->usart_handle == huart) + return usart_instance[i]; + } + return NULL; +} /** - * @brief 启动串口服务,会在每个实例注册之后自动启用接收,当前实现为DMA接收,后续可能添加IT和BLOCKING接收 + * @brief 启动串口DMA接收服务,模块完成实例和回调初始化后显式调用 * - * @todo 串口服务会在每个实例注册之后自动启用接收,当前实现为DMA接收,后续可能添加IT和BLOCKING接收 - * 可能还要将此函数修改为extern,使得module可以控制串口的启停 + * @note 配合DMA_NORMAL和ReceiveToIdle使用,每次接收事件后由BSP重新启动 * * @param _instance instance owned by module,模块拥有的串口实例 */ -void USARTServiceInit(USART_Instance *_instance) +HAL_StatusTypeDef USARTServiceInit(USART_Instance *_instance) { - HAL_UARTEx_ReceiveToIdle_DMA(_instance->usart_handle, _instance->recv_buff, _instance->recv_buff_size); + if (_instance == NULL || _instance->usart_handle == NULL || _instance->usart_handle->hdmarx == NULL) + return HAL_ERROR; + + HAL_StatusTypeDef status = HAL_UARTEx_ReceiveToIdle_DMA( + _instance->usart_handle, _instance->recv_buff, _instance->recv_buff_size); + // 关闭dma half transfer中断防止两次进入HAL_UARTEx_RxEventCallback() // 这是HAL库的一个设计失误,发生DMA传输完成/半完成以及串口IDLE中断都会触发HAL_UARTEx_RxEventCallback() // 我们只希望处理第一种和第三种情况,因此直接关闭DMA半传输中断 - __HAL_DMA_DISABLE_IT(_instance->usart_handle->hdmarx, DMA_IT_HT); + if (status == HAL_OK) + { + __HAL_DMA_DISABLE_IT(_instance->usart_handle->hdmarx, DMA_IT_HT); + _instance->rx_restart_pending = 0; + } + else + { + _instance->rx_restart_pending = 1; + } + + return status; +} + +static HAL_StatusTypeDef USARTRecoverRx(USART_Instance *instance) +{ + UART_HandleTypeDef *huart = instance->usart_handle; + DMA_HandleTypeDef *hdma = huart->hdmarx; + HAL_StatusTypeDef abort_status = HAL_UART_AbortReceive(huart); + + if (abort_status != HAL_OK || HAL_DMA_GetState(hdma) != HAL_DMA_STATE_READY) + { + if (HAL_DMA_DeInit(hdma) != HAL_OK || HAL_DMA_Init(hdma) != HAL_OK) + return HAL_ERROR; + + /* An abort timeout returns before HAL restores the UART Rx state. */ + if (HAL_UART_AbortReceive(huart) != HAL_OK) + return HAL_ERROR; + } + + return USARTServiceInit(instance); +} + +void USARTServiceTask(void) +{ + for (uint8_t i = 0; i < idx; ++i) + { + USART_Instance *instance = usart_instance[i]; + if (!instance->rx_restart_pending) + continue; + + if (USARTRecoverRx(instance) != HAL_OK) + instance->rx_restart_error_count++; + } } USART_Instance *USARTRegister(USART_Init_Config_s *init_config) { + if (init_config == NULL || init_config->usart_handle == NULL || init_config->usart_handle->hdmarx == NULL || + init_config->recv_buff_size == 0 || init_config->recv_buff_size > USART_RXBUFF_LIMIT) + { + LOGERROR("[bsp_usart] invalid USART register config"); + return NULL; + } + if (init_config->usart_handle->hdmarx->Init.Mode != DMA_NORMAL) + { + LOGERROR("[bsp_usart] ReceiveToIdle service requires DMA_NORMAL"); + return NULL; + } + if (idx >= DEVICE_USART_CNT) // 超过最大实例数 - while (1) - LOGERROR("[bsp_usart] USART exceed max instance count!"); + { + LOGERROR("[bsp_usart] USART exceed max instance count!"); + return NULL; + } for (uint8_t i = 0; i < idx; i++) // 检查是否已经注册过 if (usart_instance[i]->usart_handle == init_config->usart_handle) - while (1) - LOGERROR("[bsp_usart] USART instance already registered!"); + { + LOGERROR("[bsp_usart] USART instance already registered!"); + return NULL; + } - USART_Instance *instance = (USART_Instance *) malloc(sizeof(USART_Instance)); + USART_Instance *instance = &usart_instance_pool[idx]; memset(instance, 0, sizeof(USART_Instance)); instance->usart_handle = init_config->usart_handle; @@ -55,7 +128,6 @@ USART_Instance *USARTRegister(USART_Init_Config_s *init_config) instance->module_callback = init_config->module_callback; usart_instance[idx++] = instance; - USARTServiceInit(instance); return instance; } @@ -82,10 +154,8 @@ void USARTSend(USART_Instance *_instance, uint8_t *send_buf, uint16_t send_size, /* 串口发送时,gstate会被设为BUSY_TX */ uint8_t USARTIsReady(USART_Instance *_instance) { - if (_instance->usart_handle->gState | HAL_UART_STATE_BUSY_TX) - return 0; - else - return 1; + return _instance != NULL && _instance->usart_handle != NULL && + _instance->usart_handle->gState == HAL_UART_STATE_READY; } /** @@ -97,27 +167,22 @@ uint8_t USARTIsReady(USART_Instance *_instance) * 我们只希望处理,因此直接关闭DMA半传输中断第一种和第三种情况 * * @param huart 发生中断的串口 - * @param Size 此次接收到的总数居量,暂时没用 + * @param Size 此次接收到的数据量 */ void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { - for (uint8_t i = 0; i < idx; ++i) - { - // find the instance which is being handled - if (huart == usart_instance[i]->usart_handle) - { - // call the callback function if it is not NULL - if (usart_instance[i]->module_callback != NULL) - { - usart_instance[i]->module_callback(); - memset(usart_instance[i]->recv_buff, 0, Size); // 接收结束后清空buffer,对于变长数据是必要的 - } - HAL_UARTEx_ReceiveToIdle_DMA(usart_instance[i]->usart_handle, usart_instance[i]->recv_buff, - usart_instance[i]->recv_buff_size); - __HAL_DMA_DISABLE_IT(usart_instance[i]->usart_handle->hdmarx, DMA_IT_HT); - return; // break the loop - } - } + USART_Instance *instance = USARTFindInstance(huart); + if (instance == NULL) + return; + + instance->rx_event_count++; + instance->last_rx_size = Size; + + if (Size > 0 && Size <= instance->recv_buff_size && instance->module_callback != NULL) + instance->module_callback(instance, instance->recv_buff, Size); + + if (USARTServiceInit(instance) != HAL_OK) + instance->rx_restart_error_count++; } /** @@ -129,17 +194,15 @@ void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) */ void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { - for (uint8_t i = 0; i < idx; ++i) - { - if (huart == usart_instance[i]->usart_handle) - { - HAL_UARTEx_ReceiveToIdle_DMA(usart_instance[i]->usart_handle, usart_instance[i]->recv_buff, - usart_instance[i]->recv_buff_size); - __HAL_DMA_DISABLE_IT(usart_instance[i]->usart_handle->hdmarx, DMA_IT_HT); - LOGWARNING("[bsp_usart] USART error callback triggered, instance idx [%d]", i); - return; - } - } + USART_Instance *instance = USARTFindInstance(huart); + if (instance == NULL) + return; + + instance->uart_error_count++; + instance->last_uart_error = HAL_UART_GetError(huart); + + if (USARTServiceInit(instance) != HAL_OK) + instance->rx_restart_error_count++; } diff --git a/User_Code/bsp/usart/bsp_usart.h b/User_Code/bsp/usart/bsp_usart.h index a192809..f4a520a 100644 --- a/User_Code/bsp/usart/bsp_usart.h +++ b/User_Code/bsp/usart/bsp_usart.h @@ -7,8 +7,10 @@ #define DEVICE_USART_CNT 5 // 喵板至多分配5个串口 #define USART_RXBUFF_LIMIT 256 // 如果协议需要更大的buff,请修改这里 +typedef struct usart_instance USART_Instance; + // 模块回调函数,用于解析协议 -typedef void (*usart_module_callback)(); +typedef void (*usart_module_callback)(USART_Instance *instance, const uint8_t *recv_data, uint16_t recv_size); /* 发送模式枚举 */ typedef enum @@ -21,18 +23,24 @@ typedef enum // 串口实例结构体,每个module都要包含一个实例. // 由于串口是独占的点对点通信,所以不需要考虑多个module同时使用一个串口的情况,因此不用加入id;当然也可以选择加入,这样在bsp层可以访问到module的其他信息 -typedef struct +struct usart_instance { - uint8_t recv_buff[USART_RXBUFF_LIMIT]; // 预先定义的最大buff大小,如果太小请修改USART_RXBUFF_LIMIT - uint8_t recv_buff_size; // 模块接收一包数据的大小 + uint8_t recv_buff[USART_RXBUFF_LIMIT] __attribute__((aligned(32))); // DMA接收buffer + uint16_t recv_buff_size; // 模块接收一包数据的大小 UART_HandleTypeDef *usart_handle; // 实例对应的usart_handle usart_module_callback module_callback; // 解析收到的数据的回调函数 -} USART_Instance; + volatile uint32_t rx_event_count; + volatile uint32_t uart_error_count; + volatile uint32_t rx_restart_error_count; + volatile uint32_t last_uart_error; + volatile uint16_t last_rx_size; + volatile uint8_t rx_restart_pending; +}; /* usart 初始化配置结构体 */ typedef struct { - uint8_t recv_buff_size; // 模块接收一包数据的大小 + uint16_t recv_buff_size; // 模块接收一包数据的大小 UART_HandleTypeDef *usart_handle; // 实例对应的usart_handle usart_module_callback module_callback; // 解析收到的数据的回调函数 } USART_Init_Config_s; @@ -45,11 +53,16 @@ typedef struct USART_Instance *USARTRegister(USART_Init_Config_s *init_config); /** - * @brief 启动串口服务,需要传入一个usart实例.一般用于lost callback的情况(使用串口的模块daemon) + * @brief 启动串口DMA接收服务,需要传入一个已注册的usart实例 * * @param _instance */ -void USARTServiceInit(USART_Instance *_instance); +HAL_StatusTypeDef USARTServiceInit(USART_Instance *_instance); + +/** + * @brief 在任务上下文中恢复中断回调里启动失败的串口接收 + */ +void USARTServiceTask(void); /** diff --git a/User_Code/module/paramdef/robot_def.h b/User_Code/module/paramdef/robot_def.h index 5373470..6b391b1 100644 --- a/User_Code/module/paramdef/robot_def.h +++ b/User_Code/module/paramdef/robot_def.h @@ -13,7 +13,7 @@ typedef enum // 添加其他模式 } RobotMode_t; //机器人控制模式 -extern RobotMode_t RobotMode; +extern volatile RobotMode_t RobotMode; // #pragma pack() // 开启字节对齐,结束前面的#pragma pack(1) diff --git a/User_Code/module/periph/buzzer/buzzer.cpp b/User_Code/module/periph/buzzer/buzzer.cpp index de0c3ae..fd1f3bb 100644 --- a/User_Code/module/periph/buzzer/buzzer.cpp +++ b/User_Code/module/periph/buzzer/buzzer.cpp @@ -313,9 +313,9 @@ void systemstart_song(void) // buzzer_note(120, 0.5); //高音sol // HAL_Delay(550); - // SongSpring(); //为什么要演奏春日影!!! + //SongSpring(); //为什么要演奏春日影!!! - // SongLaoda(); + //SongLaoda(); buzzer_off(); } diff --git a/User_Code/module/periph/remote_control/rc.c b/User_Code/module/periph/remote_control/rc.c index 3a9ec05..cabad3b 100644 --- a/User_Code/module/periph/remote_control/rc.c +++ b/User_Code/module/periph/remote_control/rc.c @@ -77,23 +77,31 @@ #define REMOTE_CONTROL_FRAME_SIZE 25u // 遥控器接收的buffer大小 -//@todo:测试define -#define REMOTE_FS_I6X - // 定义SBUS协议的起始标志 #define SBUS_HEAD 0X0F // 定义SBUS协议的结束标志 #define SBUS_END 0X00 +#define SBUS_FLAG_FRAME_LOST (1u << 2) +#define SBUS_FLAG_FAILSAFE (1u << 3) // 遥控器数据 static RC_ctrl_t rc_ctrl[2]; //[0]:当前数据TEMP,[1]:上一次的数据LAST.用于按键持续按下和切换的判断 static uint8_t rc_init_flag = 0; // 遥控器初始化标志位 +static volatile uint8_t rc_data_valid = 0; +static volatile uint32_t rc_valid_frame_count = 0; +static volatile uint32_t rc_invalid_frame_count = 0; +static volatile uint32_t rc_failsafe_count = 0; // 遥控器拥有的串口实例,因为遥控器是单例,所以这里只有一个,就不封装了 static USART_Instance *rc_usart_instance; static Daemon_Instance *rc_daemon_instance; +static uint8_t SBusEndByteIsValid(uint8_t value) +{ + return value == SBUS_END || value == 0x04u || value == 0x14u || value == 0x24u || value == 0x34u; +} + #ifdef REMOTE_DJI_DT7 /** @@ -112,8 +120,11 @@ static void RectifyRCjoystick() * * @param sbus_buf 接收buffer */ -static void sbus_to_rc(const uint8_t *sbus_buf) +static uint8_t sbus_to_rc(const uint8_t *sbus_buf, uint16_t size) { + if (size != 18u) + return 0; + // 摇杆,直接解算时减去偏置 rc_ctrl[TEMP].rc.rocker_r_ = ((sbus_buf[0] | (sbus_buf[1] << 8)) & 0x07ff) - RC_CH_VALUE_OFFSET; //!< Channel 0 rc_ctrl[TEMP].rc.rocker_r1 = (((sbus_buf[1] >> 3) | (sbus_buf[2] << 5)) & 0x07ff) - RC_CH_VALUE_OFFSET; @@ -169,6 +180,7 @@ static void sbus_to_rc(const uint8_t *sbus_buf) } memcpy(&rc_ctrl[LAST], &rc_ctrl[TEMP], sizeof(RC_ctrl_t)); // 保存上一次的数据,用于按键持续按下和切换的判断 + return 1; } #endif @@ -178,7 +190,7 @@ static void sbus_to_rc(const uint8_t *sbus_buf) * * @param sbus_buf 接收buffer */ -static void sbus_to_rc(const uint8_t *sbus_buf) +static uint8_t sbus_to_rc(const uint8_t *sbus_buf, uint16_t size) { // // 摇杆,直接解算时减去偏置 // rc_ctrl[TEMP].rc.rocker_r_ = ((sbus_buf[0] | (sbus_buf[1] << 8)) & 0x07ff) - RC_CH_VALUE_OFFSET; //!< Channel 0 @@ -230,8 +242,11 @@ static void sbus_to_rc(const uint8_t *sbus_buf) // rc_ctrl[TEMP].key_count[KEY_PRESS_WITH_SHIFT][i]++; // } - if ((sbus_buf[0] != SBUS_HEAD) || (sbus_buf[24] != SBUS_END)) - return; + if (size != REMOTE_CONTROL_FRAME_SIZE || sbus_buf[0] != SBUS_HEAD || !SBusEndByteIsValid(sbus_buf[24])) + return 0; + + if ((sbus_buf[23] & (SBUS_FLAG_FRAME_LOST | SBUS_FLAG_FAILSAFE)) != 0u) + return 0; // if (sbus_buf[23] == 0x0C) // rc_ctrl->online = 0; @@ -261,20 +276,18 @@ static void sbus_to_rc(const uint8_t *sbus_buf) // rc_ctrl[TEMP].rc.ch[9] = ((sbus_buf[12] >> 3 | sbus_buf[13] << 5) & 0x07FF); // 通道10 (VrB旋钮) rc_ctrl[TEMP].sw_a = (rc_ctrl[TEMP].rc.ch[4] == 0x00F0) ? RC_SW_UP : RC_SW_DOWN; - rc_ctrl[TEMP].sw_b = rc_ctrl[TEMP].rc.ch[5]; //3档 - rc_ctrl[TEMP].sw_c = rc_ctrl[TEMP].rc.ch[6]; //3档 rc_ctrl[TEMP].sw_d = (rc_ctrl[TEMP].rc.ch[7] == 0x00F0) ? RC_SW_UP : RC_SW_DOWN; // 解析SWB状态 - if (rc_ctrl[TEMP].rc.ch[6] == 0x00F0) + if (rc_ctrl[TEMP].rc.ch[5] == 0x00F0) { rc_ctrl[TEMP].sw_b = RC_SW_UP; } - else if (rc_ctrl[TEMP].rc.ch[6] == 0x0400) + else if (rc_ctrl[TEMP].rc.ch[5] == 0x0400) { rc_ctrl[TEMP].sw_b = RC_SW_MID; } - else if (rc_ctrl[TEMP].rc.ch[6] == 0x070F) + else if (rc_ctrl[TEMP].rc.ch[5] == 0x070F) { rc_ctrl[TEMP].sw_b = RC_SW_DOWN; } @@ -283,27 +296,14 @@ static void sbus_to_rc(const uint8_t *sbus_buf) rc_ctrl[TEMP].sw_b = 0; } - if (rc_ctrl[TEMP].sw_b != rc_ctrl[TEMP].sw_b_last) - { - if (rc_ctrl[TEMP].sw_b == RC_SW_UP) - { - rc_ctrl[TEMP].sw_b_midtoup_flag = 1; - rc_ctrl[TEMP].sw_b_uptomid_flag = 0; - rc_ctrl[TEMP].sw_b_midtodown_flag = 0; - } - else if (rc_ctrl[TEMP].sw_b == RC_SW_MID) - { - rc_ctrl[TEMP].sw_b_midtoup_flag = 0; - rc_ctrl[TEMP].sw_b_uptomid_flag = 1; - rc_ctrl[TEMP].sw_b_midtodown_flag = 0; - } - else if (rc_ctrl[TEMP].sw_b == RC_SW_DOWN) - { - rc_ctrl[TEMP].sw_b_midtoup_flag = 0; - rc_ctrl[TEMP].sw_b_uptomid_flag = 0; - rc_ctrl[TEMP].sw_b_midtodown_flag = 1; - } - } + rc_ctrl[TEMP].sw_b_midtoup_flag = + rc_ctrl[TEMP].sw_b_last == RC_SW_MID && rc_ctrl[TEMP].sw_b == RC_SW_UP; + rc_ctrl[TEMP].sw_b_uptomid_flag = + rc_ctrl[TEMP].sw_b_last == RC_SW_UP && rc_ctrl[TEMP].sw_b == RC_SW_MID; + rc_ctrl[TEMP].sw_b_midtodown_flag = + rc_ctrl[TEMP].sw_b_last == RC_SW_MID && rc_ctrl[TEMP].sw_b == RC_SW_DOWN; + rc_ctrl[TEMP].sw_b_downtomid_flag = + rc_ctrl[TEMP].sw_b_last == RC_SW_DOWN && rc_ctrl[TEMP].sw_b == RC_SW_MID; rc_ctrl[TEMP].sw_b_last = rc_ctrl[TEMP].sw_b; // 解析SWC状态 @@ -324,46 +324,22 @@ static void sbus_to_rc(const uint8_t *sbus_buf) rc_ctrl[TEMP].sw_c = 0; } - if (rc_ctrl[TEMP].sw_c != rc_ctrl[TEMP].sw_c_last) - { - if (rc_ctrl[TEMP].sw_c == RC_SW_UP) - { - rc_ctrl[TEMP].sw_c_midtoup_flag = 1; - rc_ctrl[TEMP].sw_c_uptomid_flag = 0; - rc_ctrl[TEMP].sw_c_midtodown_flag = 0; - } - else if (rc_ctrl[TEMP].sw_c == RC_SW_MID) - { - rc_ctrl[TEMP].sw_c_midtoup_flag = 0; - rc_ctrl[TEMP].sw_c_uptomid_flag = 1; - rc_ctrl[TEMP].sw_c_midtodown_flag = 0; - } - else if (rc_ctrl[TEMP].sw_c == RC_SW_DOWN) - { - rc_ctrl[TEMP].sw_c_midtoup_flag = 0; - rc_ctrl[TEMP].sw_c_uptomid_flag = 0; - rc_ctrl[TEMP].sw_c_midtodown_flag = 1; - } - } + rc_ctrl[TEMP].sw_c_midtoup_flag = + rc_ctrl[TEMP].sw_c_last == RC_SW_MID && rc_ctrl[TEMP].sw_c == RC_SW_UP; + rc_ctrl[TEMP].sw_c_uptomid_flag = + rc_ctrl[TEMP].sw_c_last == RC_SW_UP && rc_ctrl[TEMP].sw_c == RC_SW_MID; + rc_ctrl[TEMP].sw_c_midtodown_flag = + rc_ctrl[TEMP].sw_c_last == RC_SW_MID && rc_ctrl[TEMP].sw_c == RC_SW_DOWN; + rc_ctrl[TEMP].sw_c_downtomid_flag = + rc_ctrl[TEMP].sw_c_last == RC_SW_DOWN && rc_ctrl[TEMP].sw_c == RC_SW_MID; rc_ctrl[TEMP].sw_c_last = rc_ctrl[TEMP].sw_c; - // SWA和SWB的状态变化 - if (rc_ctrl[TEMP].sw_a != rc_ctrl[TEMP].sw_a_last) - { - rc_ctrl[TEMP].sw_a_up_to_down_flag = (rc_ctrl[TEMP].sw_a == RC_SW_UP) ? 1 : 0; - } + // SWA和SWD的下降沿 + rc_ctrl[TEMP].sw_a_up_to_down_flag = + rc_ctrl[TEMP].sw_a_last == RC_SW_UP && rc_ctrl[TEMP].sw_a == RC_SW_DOWN; rc_ctrl[TEMP].sw_a_last = rc_ctrl[TEMP].sw_a; - - // if (rc_ctrl[TEMP].sw_b != rc_ctrl[TEMP].sw_b_last) { - // rc_ctrl[TEMP].sw_b_up_to_down_flag = (rc_ctrl[TEMP].sw_b == RC_SW_UP) ? 1 : 0; - // } - // rc_ctrl[TEMP].sw_b_last = rc_ctrl[TEMP].sw_b; - - // SWD的状态变化 - if (rc_ctrl[TEMP].sw_d != rc_ctrl[TEMP].sw_d_last) - { - rc_ctrl[TEMP].sw_d_up_to_down_flag = (rc_ctrl[TEMP].sw_d == RC_SW_UP) ? 1 : 0; - } + rc_ctrl[TEMP].sw_d_up_to_down_flag = + rc_ctrl[TEMP].sw_d_last == RC_SW_UP && rc_ctrl[TEMP].sw_d == RC_SW_DOWN; rc_ctrl[TEMP].sw_d_last = rc_ctrl[TEMP].sw_d; // 修正 ch1~ch4 @@ -374,17 +350,54 @@ static void sbus_to_rc(const uint8_t *sbus_buf) // ...existing code... memcpy(&rc_ctrl[LAST], &rc_ctrl[TEMP], sizeof(RC_ctrl_t)); // 保存上一次的数据,用于按键持续按下和切换的判断 + return 1; } #endif +static void RemoteControlPublishOffline(uint8_t force) +{ + uint32_t primask = __get_PRIMASK(); + __disable_irq(); + + uint8_t daemon_expired = rc_daemon_instance != NULL && rc_daemon_instance->temp_count == 0; + if (force || daemon_expired) + { + rc_data_valid = 0; + memset(rc_ctrl, 0, sizeof(rc_ctrl)); + if (RobotMode != SYS_ERROR_OCCURRED) + RobotMode = REMOTE_NOT_CONNECTED; + } + + __set_PRIMASK(primask); +} + /** * @brief 对sbus_to_rc的简单封装,用于注册到bsp_usart的回调函数中 * */ -static void RemoteControlRxCallback() +static void RemoteControlRxCallback(USART_Instance *instance, const uint8_t *recv_data, uint16_t recv_size) { - sbus_to_rc(rc_usart_instance->recv_buff); // 进行协议解析 - DaemonReload(rc_daemon_instance); // 先喂狗 + (void) instance; + + if (recv_size == REMOTE_CONTROL_FRAME_SIZE && recv_data[0] == SBUS_HEAD && + SBusEndByteIsValid(recv_data[24]) && (recv_data[23] & SBUS_FLAG_FAILSAFE) != 0u) + { + rc_failsafe_count++; + RemoteControlPublishOffline(1); + return; + } + + if (!sbus_to_rc(recv_data, recv_size)) + { + rc_invalid_frame_count++; + return; + } + + rc_valid_frame_count++; + rc_data_valid = 1; + DaemonReload(rc_daemon_instance); + if (RobotMode == REMOTE_NOT_CONNECTED) + RobotMode = REMOTE_CONNECTED; } /** @@ -393,38 +406,74 @@ static void RemoteControlRxCallback() */ static void RCLostCallback(void *id) { - memset(rc_ctrl, 0, sizeof(rc_ctrl)); // 清空遥控器数据 - USARTServiceInit(rc_usart_instance); // 尝试重新启动接收 - - RobotMode = REMOTE_NOT_CONNECTED; + (void) id; + RemoteControlPublishOffline(0); // LEDErrLog(0, LED_COLOR_R); // 红灯常亮 表示遥控器离线 } RC_ctrl_t *RemoteControlInit(UART_HandleTypeDef *rc_usart_handle) { - USART_Init_Config_s conf; - conf.module_callback = RemoteControlRxCallback; - conf.usart_handle = rc_usart_handle; - conf.recv_buff_size = REMOTE_CONTROL_FRAME_SIZE; + if (rc_init_flag) + return &rc_ctrl[TEMP]; + if (rc_usart_handle == NULL || rc_usart_handle->hdmarx == NULL) + return NULL; - rc_usart_instance = USARTRegister(&conf); + memset(rc_ctrl, 0, sizeof(rc_ctrl)); + rc_data_valid = 0; - // 进行守护进程的注册,用于定时检查遥控器是否正常工作 - Daemon_Init_Config_s daemon_conf = { - .reload_count = 10, // 100ms未收到数据视为离线,遥控器的接收频率实际上是1000/14Hz(大约70Hz) - .callback = RCLostCallback, - .owner_id = NULL, // 只有1个遥控器,不需要owner_id - }; - rc_daemon_instance = DaemonRegister(&daemon_conf); + if (rc_usart_instance == NULL) + { + USART_Init_Config_s conf = { + .module_callback = RemoteControlRxCallback, + .usart_handle = rc_usart_handle, + .recv_buff_size = REMOTE_CONTROL_FRAME_SIZE, + }; + rc_usart_instance = USARTRegister(&conf); + if (rc_usart_instance == NULL) + return NULL; + } + + if (rc_daemon_instance == NULL) + { + // 100ms未收到有效数据视为离线,SBUS接收频率约70Hz. + Daemon_Init_Config_s daemon_conf = { + .reload_count = 10, + .callback = RCLostCallback, + .owner_id = NULL, + }; + rc_daemon_instance = DaemonRegister(&daemon_conf); + } rc_init_flag = 1; - return rc_ctrl; + if (USARTServiceInit(rc_usart_instance) != HAL_OK) + rc_usart_instance->rx_restart_error_count++; + return &rc_ctrl[TEMP]; } -uint8_t RemoteControlIsOnline() +uint8_t RemoteControlReadSnapshot(RC_ctrl_t *out) { - if (rc_init_flag) - return DaemonIsOnline(rc_daemon_instance); - return 0; + if (out == NULL) + return 0; + + uint32_t primask = __get_PRIMASK(); + __disable_irq(); + uint8_t valid = rc_init_flag && rc_data_valid && rc_daemon_instance != NULL && + rc_daemon_instance->temp_count > 0; + if (valid) + memcpy(out, &rc_ctrl[TEMP], sizeof(*out)); + __set_PRIMASK(primask); + + return valid; +} + +uint8_t RemoteControlIsOnline(void) +{ + uint32_t primask = __get_PRIMASK(); + __disable_irq(); + uint8_t online = rc_init_flag && rc_data_valid && rc_daemon_instance != NULL && + rc_daemon_instance->temp_count > 0; + __set_PRIMASK(primask); + + return online; } diff --git a/User_Code/module/periph/remote_control/rc.h b/User_Code/module/periph/remote_control/rc.h index 1d111d2..689ba8d 100644 --- a/User_Code/module/periph/remote_control/rc.h +++ b/User_Code/module/periph/remote_control/rc.h @@ -125,21 +125,30 @@ typedef struct uint8_t key_count[3][16]; } RC_ctrl_t; #endif // FSI6X + /* ------------------------- Internal Data ----------------------------------- */ /** * @brief 初始化遥控器,该函数会将遥控器注册到串口 * - * @attention 注意分配正确的串口硬件,遥控器在C板上使用USART3 + * @attention 当前板级配置使用UART5/PD2接收SBUS * */ RC_ctrl_t *RemoteControlInit(UART_HandleTypeDef *rc_usart_handle); +/** + * @brief 原子复制当前遥控器数据 + * + * @param out 接收遥控器数据快照 + * @return uint8_t 1:快照有效 0:未初始化、离线或参数无效 + */ +uint8_t RemoteControlReadSnapshot(RC_ctrl_t *out); + /** * @brief 检查遥控器是否在线,若尚未初始化也视为离线 * * @return uint8_t 1:在线 0:离线 */ -uint8_t RemoteControlIsOnline(); +uint8_t RemoteControlIsOnline(void); #endif // REMOTE_H diff --git a/User_Code/module/software/daemon/daemon.c b/User_Code/module/software/daemon/daemon.c index 6300368..3bd8b3d 100644 --- a/User_Code/module/software/daemon/daemon.c +++ b/User_Code/module/software/daemon/daemon.c @@ -1,6 +1,7 @@ #include "daemon.h" #include "bsp_dwt.h" #include "cmsis_os2.h" +#include "main.h" #include "memory.h" #include "stdlib.h" @@ -16,8 +17,7 @@ Daemon_Instance *DaemonRegister(Daemon_Init_Config_s *config) daemon_instance->id = config->owner_id; daemon_instance->reload_count = config->reload_count == 0 ? 100 : config->reload_count; // 默认重载值为100 daemon_instance->callback = config->callback; - daemon_instance->temp_count = config->init_count == 0 ? 100 : config->init_count; // 默认上线等待时间为100 - daemon_instance->temp_count = config->reload_count; + daemon_instance->temp_count = config->init_count == 0 ? daemon_instance->reload_count : config->init_count; daemon_instances[idx++] = daemon_instance; @@ -31,7 +31,13 @@ Daemon_Instance *DaemonRegister(Daemon_Init_Config_s *config) */ void DaemonReload(Daemon_Instance *daemon) { + if (daemon == NULL) + return; + + uint32_t primask = __get_PRIMASK(); + __disable_irq(); daemon->temp_count = daemon->reload_count; + __set_PRIMASK(primask); } /** @@ -42,7 +48,14 @@ void DaemonReload(Daemon_Instance *daemon) */ uint8_t DaemonIsOnline(Daemon_Instance *daemon) { - return daemon->temp_count > 0; + if (daemon == NULL) + return 0; + + uint32_t primask = __get_PRIMASK(); + __disable_irq(); + uint8_t online = daemon->temp_count > 0; + __set_PRIMASK(primask); + return online; } /** @@ -61,9 +74,17 @@ void Daemon_Update(void) // 名字改掉,不要叫 Task,避免和 RTOS 线 for (uint8_t i = 0; i < idx; i++) { daemon = daemon_instances[i]; + uint8_t expired = 0; + + uint32_t primask = __get_PRIMASK(); + __disable_irq(); if (daemon->temp_count > 0) daemon->temp_count--; - else if (daemon->callback != NULL) + else + expired = 1; + __set_PRIMASK(primask); + + if (expired && daemon->callback != NULL) daemon->callback(daemon->id); } } diff --git a/User_Code/module/software/daemon/daemon.h b/User_Code/module/software/daemon/daemon.h index 3de5ee8..0f94293 100644 --- a/User_Code/module/software/daemon/daemon.h +++ b/User_Code/module/software/daemon/daemon.h @@ -25,7 +25,7 @@ typedef struct daemon_ins uint16_t reload_count; // 重载值 offline_callback callback; // 离线处理函数,当模块离线时调用 - uint16_t temp_count; // 当前值,减为零说明模块离线或异常 + volatile uint16_t temp_count; // 当前值,减为零说明模块离线或异常 void *id; // 模块id,用于标识模块,初始化时传入 } Daemon_Instance; diff --git a/ozonedeb/windebnewestux.jdebug b/ozonedeb/windebnewestux.jdebug index f1f9552..96fa65e 100644 --- a/ozonedeb/windebnewestux.jdebug +++ b/ozonedeb/windebnewestux.jdebug @@ -23,7 +23,7 @@ void OnProjectLoad (void) { // Dialog-generated settings // Project.SetDevice ("STM32H723VG"); - Project.SetHostIF ("USB", "602712948"); + Project.SetHostIF ("USB", "63728936"); Project.SetTargetIF ("SWD"); Project.SetTIFSpeed ("4 MHz"); Project.AddPathSubstitute ("D:/RM/Elec Control/TronOneH7_Scaffold/ozonedeb", "$(ProjectDir)"); diff --git a/ozonedeb/windebnewestux.jdebug.user b/ozonedeb/windebnewestux.jdebug.user index af67ca4..2504603 100644 --- a/ozonedeb/windebnewestux.jdebug.user +++ b/ozonedeb/windebnewestux.jdebug.user @@ -4,15 +4,15 @@ Breakpoint=D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/module/periph/power_m GraphedExpression="(QEKF_INS).Roll", Color=#a00909 GraphedExpression="(BMI088).Temperature", Color=#09a01b GraphedExpression="(QEKF_INS).Pitch", Color=#09a087 -OpenDocument="bsp_dwt.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/bsp/dwt/bsp_dwt.c", Line=59 -OpenDocument="stm32h7xx_hal_spi.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi.c", Line=1553 -OpenDocument="tasks.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Middlewares/Third_Party/FreeRTOS/Source/tasks.c", Line=2295 +OpenDocument="main.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Core/Src/main.c", Line=72 +OpenDocument="stm32h7xx_hal_spi.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi.c", Line=958 +OpenDocument="tasks.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Middlewares/Third_Party/FreeRTOS/Source/tasks.c", Line=2306 OpenDocument="delayticks.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/bsp/delayticks/delayticks.c", Line=4 -OpenDocument="dev_cmd.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/user_task/dev_tasks/dev_cmd.c", Line=23 -OpenDocument="main.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Core/Src/main.c", Line=71 +OpenDocument="dev_cmd.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/user_task/dev_tasks/dev_cmd.c", Line=0 +OpenDocument="stm32h7xx_it.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Core/Src/stm32h7xx_it.c", Line=306 OpenDocument="xidipwmeter.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/module/periph/power_meters/xiditech/xidipwmeter.c", Line=37 OpenDocument="stm32h7xx_hal.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal.c", Line=331 -OpenDocument="stm32h7xx_it.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/Core/Src/stm32h7xx_it.c", Line=306 +OpenDocument="bsp_dwt.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/bsp/dwt/bsp_dwt.c", Line=109 OpenDocument="QuaternionEKF.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/module/algorithm/ekf/QuaternionEKF.c", Line=436 OpenDocument="buzzer.cpp", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/module/periph/buzzer/buzzer.cpp", Line=319 OpenDocument="rc.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/module/periph/remote_control/rc.c", Line=0 @@ -30,35 +30,35 @@ OpenDocument="ws2812.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Co OpenDocument="ins_task.c", FilePath="D:/RM/Elec Control/TronOneH7_Scaffold/User_Code/module/periph/imu/ins_task.c", Line=0 OpenToolbar="Debug", Floating=0, x=0, y=0 OpenToolbar="Breakpoints", Floating=0, x=1, y=0 -OpenWindow="Call Stack", DockArea=RIGHT, x=0, y=0, w=681, h=324, TabPos=0, TopOfStack=1, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 -OpenWindow="Registers 1", DockArea=BOTTOM, x=1, y=0, w=361, h=181, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, FilteredItems=[], RefreshRate=1 -OpenWindow="Source Files", DockArea=LEFT, x=0, y=0, w=663, h=273, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 -OpenWindow="Disassembly", DockArea=BOTTOM, x=2, y=0, w=629, h=181, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 -OpenWindow="Break & Tracepoints", DockArea=LEFT, x=0, y=1, w=663, h=240, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, VectorCatchIndexMask=254 -OpenWindow="Memory 1", DockArea=BOTTOM, x=3, y=0, w=465, h=181, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, EditorAddress=0x2000B98C -OpenWindow="Global Data", DockArea=RIGHT, x=0, y=2, w=681, h=377, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 -OpenWindow="Watched Data 1", DockArea=LEFT, x=0, y=2, w=663, h=313, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 -OpenWindow="Functions", DockArea=LEFT, x=0, y=3, w=663, h=246, TabPos=1, TopOfStack=0, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 -OpenWindow="Call Graph", DockArea=LEFT, x=0, y=3, w=663, h=246, TabPos=2, TopOfStack=1, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 -OpenWindow="Data Sampling", DockArea=BOTTOM, x=0, y=0, w=1102, h=181, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, VisibleTab=0, UniformSampleSpacing=0 -OpenWindow="Timeline", DockArea=RIGHT, x=0, y=1, w=681, h=372, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=1, DataPaneShown=1, PowerPaneShown=0, CodePaneShown=0, PinCursor="Cursor Movable", TimePerDiv="1 s / Div", TimeStampFormat="Time", DataGraphDrawAsPoints=0, DataGraphLegendShown=1, DataGraphUniformSampleSpacing=0, DataGraphLegendPosition="0;0", PowerGraphDrawAsPoints=0, PowerGraphLegendShown=0, PowerGraphAvgFilterTime=Off, PowerGraphAvgFilterLen=Off, PowerGraphUniformSampleSpacing=0, PowerGraphLegendPosition="459;-65", CodeGraphLegendShown=0, CodeGraphLegendPosition="475;0" -OpenWindow="Console", DockArea=LEFT, x=0, y=3, w=663, h=246, TabPos=0, TopOfStack=0, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 -OpenWindow="FreeRTOS", DockArea=RIGHT, x=0, y=0, w=681, h=324, TabPos=1, TopOfStack=0, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, Showing="Task List" +OpenWindow="Call Stack", DockArea=RIGHT, x=0, y=0, w=681, h=225, TabPos=0, TopOfStack=1, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 +OpenWindow="Registers 1", DockArea=BOTTOM, x=1, y=0, w=271, h=181, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, FilteredItems=[], RefreshRate=1 +OpenWindow="Source Files", DockArea=LEFT, x=0, y=0, w=663, h=189, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 +OpenWindow="Disassembly", DockArea=BOTTOM, x=2, y=0, w=472, h=181, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 +OpenWindow="Break & Tracepoints", DockArea=LEFT, x=0, y=1, w=663, h=176, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, VectorCatchIndexMask=254 +OpenWindow="Memory 1", DockArea=BOTTOM, x=3, y=0, w=348, h=181, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, EditorAddress=0x2000B98C +OpenWindow="Global Data", DockArea=RIGHT, x=0, y=2, w=681, h=260, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 +OpenWindow="Watched Data 1", DockArea=LEFT, x=0, y=2, w=663, h=206, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 +OpenWindow="Functions", DockArea=LEFT, x=0, y=3, w=663, h=168, TabPos=1, TopOfStack=0, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 +OpenWindow="Call Graph", DockArea=LEFT, x=0, y=3, w=663, h=168, TabPos=2, TopOfStack=1, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 +OpenWindow="Data Sampling", DockArea=BOTTOM, x=0, y=0, w=826, h=181, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, VisibleTab=0, UniformSampleSpacing=0 +OpenWindow="Timeline", DockArea=RIGHT, x=0, y=1, w=681, h=255, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=1, DataPaneShown=1, PowerPaneShown=0, CodePaneShown=0, PinCursor="Cursor Movable", TimePerDiv="1 s / Div", TimeStampFormat="Time", DataGraphDrawAsPoints=0, DataGraphLegendShown=1, DataGraphUniformSampleSpacing=0, DataGraphLegendPosition="0;0", PowerGraphDrawAsPoints=0, PowerGraphLegendShown=0, PowerGraphAvgFilterTime=Off, PowerGraphAvgFilterLen=Off, PowerGraphUniformSampleSpacing=0, PowerGraphLegendPosition="484;-16", CodeGraphLegendShown=0, CodeGraphLegendPosition="498;0" +OpenWindow="Console", DockArea=LEFT, x=0, y=3, w=663, h=168, TabPos=0, TopOfStack=0, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0 +OpenWindow="FreeRTOS", DockArea=RIGHT, x=0, y=0, w=681, h=225, TabPos=1, TopOfStack=0, FilterBarShown=0, TotalValueBarShown=0, ToolBarShown=0, Showing="Task List" +TableHeader="Call Graph", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Stack Total";"Stack Local";"Code Total";"Code Local";"Depth";"Called From"], ColWidths=[384;100;100;100;100;100;102] TableHeader="Task List", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Run Count";"Priority";"Status";"Timeout";"Stack Info (Free / Size)";"ID";"Mutex Count";"Notified Value";"Notify State"], ColWidths=[110;110;110;110;110;110;110;110;110;110] TableHeader="Functions", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Address";"Size";"#Insts";"Source"], ColWidths=[1164;100;100;100;100] -TableHeader="Global Data", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Value";"Location";"Size";"Type";"Scope"], ColWidths=[222;130;100;54;95;486] +TableHeader="Global Data", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Value";"Location";"Size";"Type";"Scope"], ColWidths=[222;130;100;54;95;100] TableHeader="Vector Catches", SortCol="None", SortOrder="ASCENDING", VisibleCols=["";"Vector Catch";"Description"], ColWidths=[50;300;500] TableHeader="Break & Tracepoints", SortCol="None", SortOrder="ASCENDING", VisibleCols=["";"Type";"Location";"Extras"], ColWidths=[100;100;142;321] -TableHeader="Source Files", SortCol="File", SortOrder="ASCENDING", VisibleCols=["File";"Status";"Size";"#Insts";"Path"], ColWidths=[229;100;100;100;1046] -TableHeader="Data Sampling Table", SortCol="Index", SortOrder="ASCENDING", VisibleCols=["Index";"Time";" (QEKF_INS).Roll";" (BMI088).Temperature";" (QEKF_INS).Pitch"], ColWidths=[100;100;100;100;100] -TableHeader="Data Sampling Setup", SortCol="Expression", SortOrder="ASCENDING", VisibleCols=["Expression";"Type";"Value";"Min";"Max";"Average";"# Changes";"Min. Change";"Max. Change"], ColWidths=[320;100;110;102;100;110;102;118;118] +TableHeader="Source Files", SortCol="File", SortOrder="ASCENDING", VisibleCols=["File";"Status";"Size";"#Insts";"Path"], ColWidths=[229;100;100;100;917] +TableHeader="Data Sampling Table", SortCol="None", SortOrder="ASCENDING", VisibleCols=["Index";"Time";" (QEKF_INS).Roll";" (BMI088).Temperature";" (QEKF_INS).Pitch"], ColWidths=[100;100;100;100;100] +TableHeader="Data Sampling Setup", SortCol="Expression", SortOrder="ASCENDING", VisibleCols=["Expression";"Type";"Value";"Min";"Max";"Average";"# Changes";"Min. Change";"Max. Change"], ColWidths=[320;100;100;100;100;100;100;102;102] TableHeader="Power Sampling", SortCol="Index", SortOrder="ASCENDING", VisibleCols=["Index";"Time";"Ch 0"], ColWidths=[100;100;100] -TableHeader="Registers 1", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Value";"Description"], ColWidths=[100;105;294] -TableHeader="Watched Data 1", SortCol="Expression", SortOrder="ASCENDING", VisibleCols=["Expression";"Value";"Type";"Location";"Refresh"], ColWidths=[171;101;162;112;117] +TableHeader="Registers 1", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Value";"Description"], ColWidths=[100;105;259] +TableHeader="Watched Data 1", SortCol="Expression", SortOrder="ASCENDING", VisibleCols=["Expression";"Value";"Type";"Location";"Refresh"], ColWidths=[171;101;162;112;100] TableHeader="RegisterSelectionDialog", SortCol="None", SortOrder="ASCENDING", VisibleCols=[], ColWidths=[] -TableHeader="Call Graph", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Stack Total";"Stack Local";"Code Total";"Code Local";"Depth";"Called From"], ColWidths=[384;100;100;100;100;100;118] TableHeader="Call Stack", SortCol="Function", SortOrder="ASCENDING", VisibleCols=["Function";"Stack Frame";"Source";"PC";"Return Address";"Stack Used"], ColWidths=[110;126;190;100;190;100] -TableHeader="TargetExceptionDialog", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Value";"Address";"Description"], ColWidths=[27;27;27;38] +TableHeader="TargetExceptionDialog", SortCol="Name", SortOrder="ASCENDING", VisibleCols=["Name";"Value";"Address";"Description"], ColWidths=[27;27;27;32] WatchedExpression="QEKF_INS", RefreshRate=2, Window=Watched Data 1 WatchedExpression="BMI088", RefreshRate=2, Window=Watched Data 1 WatchedExpression="RobotMode_t", Window=Watched Data 1