remote control init

This commit is contained in:
TuxMonkey
2025-12-15 21:31:36 +08:00
parent 2d905efeb7
commit a78fe6ea11
10 changed files with 893 additions and 109 deletions

View File

@@ -121,14 +121,14 @@ const osThreadAttr_t WS2812Task_attributes = {
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
//Todo: 测试instask
//@Todo:测试instask
const osThreadAttr_t instask_attributes = {
.name = "instask",
.priority = osPriorityAboveNormal, // 较高优先级
.stack_size = 1024 * 4 // 栈大小单位是字节通常是字数的4倍
.name = "instask",
.priority = osPriorityAboveNormal, // 较高优先级
.stack_size = 1024 * 4 // 栈大小单位是字节通常是字数的4倍
};
//Todo:测试使用
//@Todo:测试使用
osThreadId insTaskHandle;
void StartINSTASK(void const *argument);
@@ -136,28 +136,38 @@ void StartINSTASK(void const *argument);
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
void ShootTask(void *argument);
void GimbalTask(void *argument);
void ChassisTask(void *argument);
void StartInitTask(void *argument);
void VisionTask(void *argument);
void CmdTask(void *argument);
void RefereeTask(void *argument);
extern void ws2812Task(void *argument);
extern void MX_USB_DEVICE_Init(void);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/* Hook prototypes */
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName);
void vApplicationMallocFailedHook(void);
/* USER CODE BEGIN 4 */
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName)
{
/* Run time stack overflow checking is performed if
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
called if a stack overflow is detected. */
/* Run time stack overflow checking is performed if
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
called if a stack overflow is detected. */
}
/* USER CODE END 4 */
@@ -165,16 +175,16 @@ void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName)
/* USER CODE BEGIN 5 */
void vApplicationMallocFailedHook(void)
{
/* vApplicationMallocFailedHook() will only be called if
configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook
function that will get called if a call to pvPortMalloc() fails.
pvPortMalloc() is called internally by the kernel whenever a task, queue,
timer or semaphore is created. It is also called by various parts of the
demo application. If heap_1.c or heap_2.c are used, then the size of the
heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
to query the size of free heap space that remains (although it does not
provide information on how the remaining heap might be fragmented). */
/* vApplicationMallocFailedHook() will only be called if
configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook
function that will get called if a call to pvPortMalloc() fails.
pvPortMalloc() is called internally by the kernel whenever a task, queue,
timer or semaphore is created. It is also called by various parts of the
demo application. If heap_1.c or heap_2.c are used, then the size of the
heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
to query the size of free heap space that remains (although it does not
provide information on how the remaining heap might be fragmented). */
}
/* USER CODE END 5 */
@@ -184,25 +194,26 @@ void vApplicationMallocFailedHook(void)
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
void MX_FREERTOS_Init(void)
{
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
@@ -234,16 +245,15 @@ void MX_FREERTOS_Init(void) {
WS2812TaskHandle = osThreadNew(ws2812Task, NULL, &WS2812Task_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
//TodoINS_Task是测试版本
// 创建线程
insTaskHandle = osThreadNew(StartINSTASK, NULL, &instask_attributes);
/* add threads, ... */
//@Todo:INS_Task是测试版本
// 创建线程
insTaskHandle = osThreadNew(StartINSTASK, NULL, &instask_attributes);
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
@@ -258,11 +268,11 @@ __weak void StartDefaultTask(void *argument)
/* init code for USB_DEVICE */
MX_USB_DEVICE_Init();
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* USER CODE END StartDefaultTask */
}
@@ -276,11 +286,11 @@ __weak void StartDefaultTask(void *argument)
__weak void ShootTask(void *argument)
{
/* USER CODE BEGIN ShootTask */
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* USER CODE END ShootTask */
}
@@ -294,11 +304,11 @@ __weak void ShootTask(void *argument)
__weak void GimbalTask(void *argument)
{
/* USER CODE BEGIN GimbalTask */
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* USER CODE END GimbalTask */
}
@@ -312,11 +322,11 @@ __weak void GimbalTask(void *argument)
__weak void ChassisTask(void *argument)
{
/* USER CODE BEGIN ChassisTask */
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* USER CODE END ChassisTask */
}
@@ -330,11 +340,11 @@ __weak void ChassisTask(void *argument)
__weak void StartInitTask(void *argument)
{
/* USER CODE BEGIN StartInitTask */
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* USER CODE END StartInitTask */
}
@@ -348,11 +358,11 @@ __weak void StartInitTask(void *argument)
__weak void VisionTask(void *argument)
{
/* USER CODE BEGIN VisionTask */
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* USER CODE END VisionTask */
}
@@ -366,11 +376,11 @@ __weak void VisionTask(void *argument)
__weak void CmdTask(void *argument)
{
/* USER CODE BEGIN CmdTask */
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* USER CODE END CmdTask */
}
@@ -384,36 +394,38 @@ __weak void CmdTask(void *argument)
__weak void RefereeTask(void *argument)
{
/* USER CODE BEGIN RefereeTask */
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* Infinite loop */
for (;;)
{
osDelay(1);
}
/* USER CODE END RefereeTask */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
//Todo:INS_Task是测试阶段使用。
//@Todo:INS_Task是测试阶段使用。
__attribute__((noreturn)) void StartINSTASK(void const *argument)
{
static float ins_start;
static float ins_dt;
INS_Init(); // 确保BMI088被正确初始化.
// LOGINFO("[freeRTOS] INS Task Start");
for (;;)
{
// 1kHz
// ins_start = DWT_GetTimeline_ms();
INS_Task();
// ins_dt = DWT_GetTimeline_ms() - ins_start;
// if (ins_dt > 1)
// Todo: LOGERROR("[freeRTOS] INS Task is being DELAY! dt = [%f]", &ins_dt);
// // VisionSend(); // 解算完成后发送视觉数据,但是当前的实现不太优雅,后续若添加硬件触发需要重新考虑结构的组织
osDelay(1); // 1ms, 1kHz
}
static float ins_start;
static float ins_dt;
INS_Init(); // 确保BMI088被正确初始化.
// LOGINFO("[freeRTOS] INS Task Start");
for (;;)
{
// 1kHz
// ins_start = DWT_GetTimeline_ms();
INS_Task();
// ins_dt = DWT_GetTimeline_ms() - ins_start;
// if (ins_dt > 1)
// @Todo: LOGERROR("[freeRTOS] INS Task is being DELAY! dt = [%f]", &ins_dt);
// // VisionSend(); // 解算完成后发送视觉数据,但是当前的实现不太优雅,后续若添加硬件触发需要重新考虑结构的组织
osDelay(1); // 1ms, 1kHz
}
}
/* USER CODE END Application */

View File

@@ -53,6 +53,7 @@ void robotSelfCheck(void)
void ws2812Task(void *argument)
{
(void) argument;
RobotMode_t RobotMode = REMOTE_NOT_CONNECTED; // 初始状态为遥控器未连接
while (1)
{
switch (RobotMode)

View File

@@ -17,7 +17,7 @@
/* usart service instance, modules' info would be recoreded here using USARTRegister() */
/* usart服务实例,所有注册了usart的模块信息会被保存在这里 */
static uint8_t idx;
static USARTInstance *usart_instance[DEVICE_USART_CNT] = {NULL};
static USART_Instance *usart_instance[DEVICE_USART_CNT] = {NULL};
/**
* @brief 启动串口服务,会在每个实例注册之后自动启用接收,当前实现为DMA接收,后续可能添加IT和BLOCKING接收
@@ -27,7 +27,7 @@ static USARTInstance *usart_instance[DEVICE_USART_CNT] = {NULL};
*
* @param _instance instance owned by module,模块拥有的串口实例
*/
void USARTServiceInit(USARTInstance *_instance)
void USARTServiceInit(USART_Instance *_instance)
{
HAL_UARTEx_ReceiveToIdle_DMA(_instance->usart_handle, _instance->recv_buff, _instance->recv_buff_size);
// 关闭dma half transfer中断防止两次进入HAL_UARTEx_RxEventCallback()
@@ -36,7 +36,7 @@ void USARTServiceInit(USARTInstance *_instance)
__HAL_DMA_DISABLE_IT(_instance->usart_handle->hdmarx, DMA_IT_HT);
}
USARTInstance *USARTRegister(USART_Init_Config_s *init_config)
USART_Instance *USARTRegister(USART_Init_Config_s *init_config)
{
if (idx >= DEVICE_USART_CNT) // 超过最大实例数
while (1)
@@ -47,8 +47,8 @@ USARTInstance *USARTRegister(USART_Init_Config_s *init_config)
while (1)
LOGERROR("[bsp_usart] USART instance already registered!");
USARTInstance *instance = (USARTInstance *) malloc(sizeof(USARTInstance));
memset(instance, 0, sizeof(USARTInstance));
USART_Instance *instance = (USART_Instance *) malloc(sizeof(USART_Instance));
memset(instance, 0, sizeof(USART_Instance));
instance->usart_handle = init_config->usart_handle;
instance->recv_buff_size = init_config->recv_buff_size;
@@ -60,7 +60,7 @@ USARTInstance *USARTRegister(USART_Init_Config_s *init_config)
}
/* @todo 当前仅进行了形式上的封装,后续要进一步考虑是否将module的行为与bsp完全分离 */
void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size, USART_TRANSFER_MODE mode)
void USARTSend(USART_Instance *_instance, uint8_t *send_buf, uint16_t send_size, USART_TRANSFER_MODE mode)
{
switch (mode)
{
@@ -80,7 +80,7 @@ void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size,
}
/* 串口发送时,gstate会被设为BUSY_TX */
uint8_t USARTIsReady(USARTInstance *_instance)
uint8_t USARTIsReady(USART_Instance *_instance)
{
if (_instance->usart_handle->gState | HAL_UART_STATE_BUSY_TX)
return 0;
@@ -141,3 +141,5 @@ void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
}
}
}

View File

@@ -27,7 +27,7 @@ typedef struct
uint8_t recv_buff_size; // 模块接收一包数据的大小
UART_HandleTypeDef *usart_handle; // 实例对应的usart_handle
usart_module_callback module_callback; // 解析收到的数据的回调函数
} USARTInstance;
} USART_Instance;
/* usart 初始化配置结构体 */
typedef struct
@@ -42,14 +42,14 @@ typedef struct
*
* @param init_config 传入串口初始化结构体
*/
USARTInstance *USARTRegister(USART_Init_Config_s *init_config);
USART_Instance *USARTRegister(USART_Init_Config_s *init_config);
/**
* @brief 启动串口服务,需要传入一个usart实例.一般用于lost callback的情况(使用串口的模块daemon)
*
* @param _instance
*/
void USARTServiceInit(USARTInstance *_instance);
void USARTServiceInit(USART_Instance *_instance);
/**
@@ -62,7 +62,7 @@ void USARTServiceInit(USARTInstance *_instance);
* @param send_buf 待发送数据的buffer
* @param send_size how many bytes to send
*/
void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size, USART_TRANSFER_MODE mode);
void USARTSend(USART_Instance *_instance, uint8_t *send_buf, uint16_t send_size, USART_TRANSFER_MODE mode);
/**
* @brief 判断串口是否准备好,用于连续或异步的IT/DMA发送
@@ -70,6 +70,6 @@ void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size,
* @param _instance 要判断的串口实例
* @return uint8_t ready 1, busy 0
*/
uint8_t USARTIsReady(USARTInstance *_instance);
uint8_t USARTIsReady(USART_Instance *_instance);
#endif

View File

@@ -2,7 +2,8 @@
#ifndef ROBOT_DEF_H
#define ROBOT_DEF_H
typedef enum {
typedef enum
{
NORMAL_MODE = 0, // 正常模式对应数值0
SYS_ERROR_OCCURRED = 1, // 系统错误对应数值1
REMOTE_NOT_CONNECTED = 2, // 遥控器未连接对应数值2
@@ -12,8 +13,7 @@ typedef enum {
// 添加其他模式
} RobotMode_t; //机器人控制模式
// 声明并初始化变量
RobotMode_t RobotMode = REMOTE_NOT_CONNECTED; // 初始状态为遥控器未连接
extern RobotMode_t RobotMode;
// #pragma pack() // 开启字节对齐,结束前面的#pragma pack(1)

View File

@@ -0,0 +1,70 @@
//
// Created by ASUS on 2025/12/14.
//
#ifndef TRONONEH7_SCAFFOLD_KEY_DEFINE_H
#define TRONONEH7_SCAFFOLD_KEY_DEFINE_H
#ifndef KEY_DEFINE_H
#define KEY_DEFINE_H
#include "stdint.h"
// 用于遥控器数据读取,遥控器数据是一个大小为2的数组
#define LAST 1
#define TEMP 0
// 获取按键操作
#define KEY_PRESS 0
#define KEY_STATE 1
#define KEY_PRESS_WITH_CTRL 1
#define KEY_PRESS_WITH_SHIFT 2
/* ----------------------- PC Key Definition-------------------------------- */
// 对应key[x][0~16],获取对应的键;例如通过key[KEY_PRESS][Key_W]获取W键是否按下,后续改为位域后删除
#define Key_W 0
#define Key_S 1
#define Key_A 2
#define Key_D 3
#define Key_Shift 4
#define Key_Ctrl 5
#define Key_Q 6
#define Key_E 7
#define Key_R 8
#define Key_F 9
#define Key_G 10
#define Key_Z 11
#define Key_X 12
#define Key_C 13
#define Key_V 14
#define Key_B 15
/* ----------------------- Data Struct ------------------------------------- */
// 待测试的位域结构体,可以极大提升解析速度
typedef union
{
struct // 用于访问键盘状态
{
uint16_t w: 1;
uint16_t s: 1;
uint16_t a: 1;
uint16_t d: 1;
uint16_t shift: 1;
uint16_t ctrl: 1;
uint16_t q: 1;
uint16_t e: 1;
uint16_t r: 1;
uint16_t f: 1;
uint16_t g: 1;
uint16_t z: 1;
uint16_t x: 1;
uint16_t c: 1;
uint16_t v: 1;
uint16_t b: 1;
};
uint16_t keys; // 用于memcpy而不需要进行强制类型转换
} Key_t;
#endif // !KEY_DEFINE_H
#endif //TRONONEH7_SCAFFOLD_KEY_DEFINE_H

View File

@@ -1,5 +1,430 @@
//
// Created by ASUS on 2025/11/17.
//
/*************************发射机DT7***************************
* *
* ----------------------------------------------------- *
* | (上-1) (上-1) | *
* |SW_L|(中-3) SW_R|(中-3) | *
* | (下-2) (下-2) | *
* | | *
* | | ^ | | ^ | | *
* | | 3 |左摇杆 右摇杆| 1 | | *
* | --- --- --- --- | *
* |< 2> < 0>| *
* | --- --- --- --- | *
* | | | | | | *
* | | | | | | *
* | | *
* ----------------------------------------------------- *
* *
**************************遥控器信息**************************
*域 通道0 通道1 通道2 通道3 S1 S2 *
*偏移 0 11 22 33 44 46 *
*长度(bit) 11 11 11 11 2 2 *
*符号位 无 无 无 无 无 无 *
*范围 ***********最大值1684********* *最大值3* *
* * 中间值1024 * *最小值1* *
* ***********最小值364********** *
*功能 1:上 1:上 *
* 2:下 2:下 *
* 3:中 3:中 *
* *
***************************鼠标信息***************************
*域 鼠标x轴 鼠标y轴 鼠标z轴 鼠标左键 鼠标右键*
*偏移 48 64 80 86 94 *
*长度 16 16 16 8 8 *
*符号位 有 有 有 无 无 *
*范围 ******最大值32767***** ***最大值1*** *
* * 最小值-32768 * ***最小值0*** *
* ******静止值0********* *
*功能 ***鼠标在XYZ轴的移动速度*** *鼠标左右键是否按下*
* * 负值表示往左移动 * * 0:没按下 *
* ***正值表示往右移动******** *****1:按下*********
* *
* *
***************************键盘信息***************************
*域 按键 *
*偏移 102 *
*长度 16 *
*符号位 无 *
*范围 位值标识 *
*功能 每个按键对应一个bit *
* Bit 0:W键 *
* Bit 1:S键 *
* Bit 2:A键 *
* Bit 3:D键 *
* Bit 4:Shift键 *
* Bit 5:Ctrl键 *
* Bit 6:Q键 *
* Bit 7:E键 *
* Bit 8:R键 *
* Bit 9:F键 *
* Bit10:G键 *
* Bit11:Z键 *
* Bit12:X键 *
* Bit13:C键 *
* Bit14:V键 *
* Bit15:B键 *
**************************************************************/
#include "rc.h"
#include "string.h"
#include "bsp_usart.h"
#include "memory.h"
#include "stdlib.h"
#include "daemon.h"
#include "indicator_app/ws2812status.h"
#include "robot_def.h"
#define REMOTE_CONTROL_FRAME_SIZE 25u // 遥控器接收的buffer大小
//@todo测试define
#define REMOTE_FS_I6X
// 定义SBUS协议的起始标志
#define SBUS_HEAD 0X0F
// 定义SBUS协议的结束标志
#define SBUS_END 0X00
// 遥控器数据
static RC_ctrl_t rc_ctrl[2]; //[0]:当前数据TEMP,[1]:上一次的数据LAST.用于按键持续按下和切换的判断
static uint8_t rc_init_flag = 0; // 遥控器初始化标志位
// 遥控器拥有的串口实例,因为遥控器是单例,所以这里只有一个,就不封装了
static USART_Instance *rc_usart_instance;
static Daemon_Instance *rc_daemon_instance;
#ifdef REMOTE_DJI_DT7
/**
* @brief 矫正遥控器摇杆的值,超过660或者小于-660的值都认为是无效值,置0
*
*/
static void RectifyRCjoystick()
{
for (uint8_t i = 0; i < 5; ++i)
if (abs(*(&rc_ctrl[TEMP].rc.rocker_l_ + i)) > 660)
*(&rc_ctrl[TEMP].rc.rocker_l_ + i) = 0;
}
/**
* @brief 遥控器数据解析
*
* @param sbus_buf 接收buffer
*/
static void sbus_to_rc(const uint8_t *sbus_buf)
{
// 摇杆,直接解算时减去偏置
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;
//!< Channel 1
rc_ctrl[TEMP].rc.rocker_l_ = (((sbus_buf[2] >> 6) | (sbus_buf[3] << 2) | (sbus_buf[4] << 10)) & 0x07ff) -
RC_CH_VALUE_OFFSET; //!< Channel 2
rc_ctrl[TEMP].rc.rocker_l1 = (((sbus_buf[4] >> 1) | (sbus_buf[5] << 7)) & 0x07ff) - RC_CH_VALUE_OFFSET;
//!< Channel 3
rc_ctrl[TEMP].rc.dial = ((sbus_buf[16] | (sbus_buf[17] << 8)) & 0x07FF) - RC_CH_VALUE_OFFSET; // 左侧拨轮
RectifyRCjoystick();
// 开关,0左1右
rc_ctrl[TEMP].rc.switch_right = ((sbus_buf[5] >> 4) & 0x0003); //!< Switch right
rc_ctrl[TEMP].rc.switch_left = ((sbus_buf[5] >> 4) & 0x000C) >> 2; //!< Switch left
// 鼠标解析
rc_ctrl[TEMP].mouse.x = (sbus_buf[6] | (sbus_buf[7] << 8)); //!< Mouse X axis
rc_ctrl[TEMP].mouse.y = (sbus_buf[8] | (sbus_buf[9] << 8)); //!< Mouse Y axis
rc_ctrl[TEMP].mouse.z = (sbus_buf[10] | (sbus_buf[11] << 8)); //!< Mouse Z axis // 滚轮
rc_ctrl[TEMP].mouse.press_l = sbus_buf[12]; //!< Mouse Left Is Press ?
rc_ctrl[TEMP].mouse.press_r = sbus_buf[13]; //!< Mouse Right Is Press ?
// 位域的按键值解算,直接memcpy即可,注意小端低字节在前,即lsb在第一位,msb在最后
*(uint16_t *) &rc_ctrl[TEMP].key[KEY_PRESS] = (uint16_t) (sbus_buf[14] | (sbus_buf[15] << 8));
if (rc_ctrl[TEMP].key[KEY_PRESS].ctrl) // ctrl键按下
rc_ctrl[TEMP].key[KEY_PRESS_WITH_CTRL] = rc_ctrl[TEMP].key[KEY_PRESS];
else
memset(&rc_ctrl[TEMP].key[KEY_PRESS_WITH_CTRL], 0, sizeof(Key_t));
if (rc_ctrl[TEMP].key[KEY_PRESS].shift) // shift键按下
rc_ctrl[TEMP].key[KEY_PRESS_WITH_SHIFT] = rc_ctrl[TEMP].key[KEY_PRESS];
else
memset(&rc_ctrl[TEMP].key[KEY_PRESS_WITH_SHIFT], 0, sizeof(Key_t));
uint16_t key_now = rc_ctrl[TEMP].key[KEY_PRESS].keys, // 当前按键是否按下
key_last = rc_ctrl[LAST].key[KEY_PRESS].keys, // 上一次按键是否按下
key_with_ctrl = rc_ctrl[TEMP].key[KEY_PRESS_WITH_CTRL].keys, // 当前ctrl组合键是否按下
key_with_shift = rc_ctrl[TEMP].key[KEY_PRESS_WITH_SHIFT].keys, // 当前shift组合键是否按下
key_last_with_ctrl = rc_ctrl[LAST].key[KEY_PRESS_WITH_CTRL].keys, // 上一次ctrl组合键是否按下
key_last_with_shift = rc_ctrl[LAST].key[KEY_PRESS_WITH_SHIFT].keys; // 上一次shift组合键是否按下
for (uint16_t i = 0, j = 0x1; i < 16; j <<= 1, i++)
{
if (i == 4 || i == 5) // 4,5位为ctrl和shift,直接跳过
continue;
// 如果当前按键按下,上一次按键没有按下,且ctrl和shift组合键没有按下,则按键按下计数加1(检测到上升沿)
if ((key_now & j) && !(key_last & j) && !(key_with_ctrl & j) && !(key_with_shift & j))
rc_ctrl[TEMP].key_count[KEY_PRESS][i]++;
// 当前ctrl组合键按下,上一次ctrl组合键没有按下,则ctrl组合键按下计数加1(检测到上升沿)
if ((key_with_ctrl & j) && !(key_last_with_ctrl & j))
rc_ctrl[TEMP].key_count[KEY_PRESS_WITH_CTRL][i]++;
// 当前shift组合键按下,上一次shift组合键没有按下,则shift组合键按下计数加1(检测到上升沿)
if ((key_with_shift & j) && !(key_last_with_shift & j))
rc_ctrl[TEMP].key_count[KEY_PRESS_WITH_SHIFT][i]++;
}
memcpy(&rc_ctrl[LAST], &rc_ctrl[TEMP], sizeof(RC_ctrl_t)); // 保存上一次的数据,用于按键持续按下和切换的判断
}
#endif
#ifdef REMOTE_FS_I6X
/**
* @brief 遥控器数据解析
*
* @param sbus_buf 接收buffer
*/
static void sbus_to_rc(const uint8_t *sbus_buf)
{
// // 摇杆,直接解算时减去偏置
// 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; //!< Channel 1
// rc_ctrl[TEMP].rc.rocker_l_ = (((sbus_buf[2] >> 6) | (sbus_buf[3] << 2) | (sbus_buf[4] << 10)) & 0x07ff) - RC_CH_VALUE_OFFSET; //!< Channel 2
// rc_ctrl[TEMP].rc.rocker_l1 = (((sbus_buf[4] >> 1) | (sbus_buf[5] << 7)) & 0x07ff) - RC_CH_VALUE_OFFSET; //!< Channel 3
// rc_ctrl[TEMP].rc.dial = ((sbus_buf[16] | (sbus_buf[17] << 8)) & 0x07FF) - RC_CH_VALUE_OFFSET; // 左侧拨轮
// RectifyRCjoystick();
// // 开关,0左1右
// rc_ctrl[TEMP].rc.switch_right = ((sbus_buf[5] >> 4) & 0x0003); //!< Switch right
// rc_ctrl[TEMP].rc.switch_left = ((sbus_buf[5] >> 4) & 0x000C) >> 2; //!< Switch left
// // 鼠标解析
// rc_ctrl[TEMP].mouse.x = (sbus_buf[6] | (sbus_buf[7] << 8)); //!< Mouse X axis
// rc_ctrl[TEMP].mouse.y = (sbus_buf[8] | (sbus_buf[9] << 8)); //!< Mouse Y axis
// rc_ctrl[TEMP].mouse.z = (sbus_buf[10] | (sbus_buf[11] << 8)); //!< Mouse Z axis // 滚轮
// rc_ctrl[TEMP].mouse.press_l = sbus_buf[12]; //!< Mouse Left Is Press ?
// rc_ctrl[TEMP].mouse.press_r = sbus_buf[13]; //!< Mouse Right Is Press ?
// // 位域的按键值解算,直接memcpy即可,注意小端低字节在前,即lsb在第一位,msb在最后
// *(uint16_t *)&rc_ctrl[TEMP].key[KEY_PRESS] = (uint16_t)(sbus_buf[14] | (sbus_buf[15] << 8));
// if (rc_ctrl[TEMP].key[KEY_PRESS].ctrl) // ctrl键按下
// rc_ctrl[TEMP].key[KEY_PRESS_WITH_CTRL] = rc_ctrl[TEMP].key[KEY_PRESS];
// else
// memset(&rc_ctrl[TEMP].key[KEY_PRESS_WITH_CTRL], 0, sizeof(Key_t));
// if (rc_ctrl[TEMP].key[KEY_PRESS].shift) // shift键按下
// rc_ctrl[TEMP].key[KEY_PRESS_WITH_SHIFT] = rc_ctrl[TEMP].key[KEY_PRESS];
// else
// memset(&rc_ctrl[TEMP].key[KEY_PRESS_WITH_SHIFT], 0, sizeof(Key_t));
// uint16_t key_now = rc_ctrl[TEMP].key[KEY_PRESS].keys, // 当前按键是否按下
// key_last = rc_ctrl[LAST].key[KEY_PRESS].keys, // 上一次按键是否按下
// key_with_ctrl = rc_ctrl[TEMP].key[KEY_PRESS_WITH_CTRL].keys, // 当前ctrl组合键是否按下
// key_with_shift = rc_ctrl[TEMP].key[KEY_PRESS_WITH_SHIFT].keys, // 当前shift组合键是否按下
// key_last_with_ctrl = rc_ctrl[LAST].key[KEY_PRESS_WITH_CTRL].keys, // 上一次ctrl组合键是否按下
// key_last_with_shift = rc_ctrl[LAST].key[KEY_PRESS_WITH_SHIFT].keys; // 上一次shift组合键是否按下
// for (uint16_t i = 0, j = 0x1; i < 16; j <<= 1, i++) {
// if (i == 4 || i == 5) // 4,5位为ctrl和shift,直接跳过
// continue;
// // 如果当前按键按下,上一次按键没有按下,且ctrl和shift组合键没有按下,则按键按下计数加1(检测到上升沿)
// if ((key_now & j) && !(key_last & j) && !(key_with_ctrl & j) && !(key_with_shift & j))
// rc_ctrl[TEMP].key_count[KEY_PRESS][i]++;
// // 当前ctrl组合键按下,上一次ctrl组合键没有按下,则ctrl组合键按下计数加1(检测到上升沿)
// if ((key_with_ctrl & j) && !(key_last_with_ctrl & j))
// rc_ctrl[TEMP].key_count[KEY_PRESS_WITH_CTRL][i]++;
// // 当前shift组合键按下,上一次shift组合键没有按下,则shift组合键按下计数加1(检测到上升沿)
// if ((key_with_shift & j) && !(key_last_with_shift & j))
// rc_ctrl[TEMP].key_count[KEY_PRESS_WITH_SHIFT][i]++;
// }
if ((sbus_buf[0] != SBUS_HEAD) || (sbus_buf[24] != SBUS_END))
return;
// if (sbus_buf[23] == 0x0C)
// rc_ctrl->online = 0;
// else
// rc_ctrl->online = 1;
rc_ctrl[TEMP].rc.ch[0] = ((sbus_buf[1] | sbus_buf[2] << 8) & 0x07FF); // 通道1 (云台yaw)
rc_ctrl[TEMP].rc.ch[1] = ((sbus_buf[2] >> 3 | sbus_buf[3] << 5) & 0x07FF); // 通道2 (云台pitch)
rc_ctrl[TEMP].rc.ch[2] = ((sbus_buf[3] >> 6 | sbus_buf[4] << 2 | sbus_buf[5] << 10) & 0x07FF); // 通道3 (前后)
rc_ctrl[TEMP].rc.ch[3] = ((sbus_buf[5] >> 1 | sbus_buf[6] << 7) & 0x07FF); // 通道4 (左右)
rc_ctrl[TEMP].rc.ch[4] = ((sbus_buf[6] >> 4 | sbus_buf[7] << 4) & 0x07FF); // 通道5 (SWA)
rc_ctrl[TEMP].rc.ch[5] = ((sbus_buf[7] >> 7 | sbus_buf[8] << 1 | sbus_buf[9] << 9) & 0x07FF); // 通道6 (SWB)
rc_ctrl[TEMP].rc.ch[6] = ((sbus_buf[9] >> 2 | sbus_buf[10] << 6) & 0x07FF); // 通道7 (SWC)
rc_ctrl[TEMP].rc.ch[7] = ((sbus_buf[10] >> 5 | sbus_buf[11] << 3) & 0x07FF); // 通道8 (SWD)
rc_ctrl[TEMP].rc.ch[8] = ((sbus_buf[12] | sbus_buf[13] << 8) & 0x07FF); // 通道9 (VrA旋钮)
rc_ctrl[TEMP].rc.ch[9] = ((sbus_buf[13] >> 3 | sbus_buf[14] << 5) & 0x07FF); // 通道10 (VrB旋钮)
// rc_ctrl[TEMP].rc.ch[0] = ((sbus_buf[0] | sbus_buf[1] << 8) & 0x07FF); // 通道1 (云台yaw)
// rc_ctrl[TEMP].rc.ch[1] = ((sbus_buf[1] >> 3 | sbus_buf[2] << 5) & 0x07FF); // 通道2 (云台pitch)
// rc_ctrl[TEMP].rc.ch[2] = ((sbus_buf[2] >> 6 | sbus_buf[3] << 2 | sbus_buf[4] << 10) & 0x07FF); // 通道3 (前后)
// rc_ctrl[TEMP].rc.ch[3] = ((sbus_buf[4] >> 1 | sbus_buf[5] << 7) & 0x07FF); // 通道4 (左右)
// rc_ctrl[TEMP].rc.ch[4] = ((sbus_buf[5] >> 4 | sbus_buf[6] << 4) & 0x07FF); // 通道5 (SWA)
// rc_ctrl[TEMP].rc.ch[5] = ((sbus_buf[6] >> 7 | sbus_buf[7] << 1 | sbus_buf[8] << 9) & 0x07FF); // 通道6 (SWB)
// rc_ctrl[TEMP].rc.ch[6] = ((sbus_buf[8] >> 2 | sbus_buf[9] << 6) & 0x07FF); // 通道7 (SWC)
// rc_ctrl[TEMP].rc.ch[7] = ((sbus_buf[9] >> 5 | sbus_buf[10] << 3) & 0x07FF); // 通道8 (SWD)
// rc_ctrl[TEMP].rc.ch[8] = ((sbus_buf[11] | sbus_buf[12] << 8) & 0x07FF); // 通道9 (VrA旋钮)
// 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)
{
rc_ctrl[TEMP].sw_b = RC_SW_UP;
}
else if (rc_ctrl[TEMP].rc.ch[6] == 0x0400)
{
rc_ctrl[TEMP].sw_b = RC_SW_MID;
}
else if (rc_ctrl[TEMP].rc.ch[6] == 0x070F)
{
rc_ctrl[TEMP].sw_b = RC_SW_DOWN;
}
else
{
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_last = rc_ctrl[TEMP].sw_b;
// 解析SWC状态
if (rc_ctrl[TEMP].rc.ch[6] == 0x00F0)
{
rc_ctrl[TEMP].sw_c = RC_SW_UP;
}
else if (rc_ctrl[TEMP].rc.ch[6] == 0x0400)
{
rc_ctrl[TEMP].sw_c = RC_SW_MID;
}
else if (rc_ctrl[TEMP].rc.ch[6] == 0x070F)
{
rc_ctrl[TEMP].sw_c = RC_SW_DOWN;
}
else
{
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_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;
}
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_last = rc_ctrl[TEMP].sw_d;
// 修正 ch1~ch4
rc_ctrl[TEMP].ch1 = rc_ctrl[TEMP].rc.ch[0] - 1024;
rc_ctrl[TEMP].ch2 = rc_ctrl[TEMP].rc.ch[1] - 1024;
rc_ctrl[TEMP].ch3 = rc_ctrl[TEMP].rc.ch[2] - 1024;
rc_ctrl[TEMP].ch4 = rc_ctrl[TEMP].rc.ch[3] - 1024;
// ...existing code...
memcpy(&rc_ctrl[LAST], &rc_ctrl[TEMP], sizeof(RC_ctrl_t)); // 保存上一次的数据,用于按键持续按下和切换的判断
}
#endif
/**
* @brief 对sbus_to_rc的简单封装,用于注册到bsp_usart的回调函数中
*
*/
static void RemoteControlRxCallback()
{
sbus_to_rc(rc_usart_instance->recv_buff); // 进行协议解析
DaemonReload(rc_daemon_instance); // 先喂狗
}
/**
* @brief 遥控器离线的回调函数,注册到守护进程中,串口掉线时调用
*
*/
static void RCLostCallback(void *id)
{
memset(rc_ctrl, 0, sizeof(rc_ctrl)); // 清空遥控器数据
USARTServiceInit(rc_usart_instance); // 尝试重新启动接收
RobotMode = REMOTE_NOT_CONNECTED;
// 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;
rc_usart_instance = USARTRegister(&conf);
// 进行守护进程的注册,用于定时检查遥控器是否正常工作
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);
rc_init_flag = 1;
return rc_ctrl;
}
uint8_t RemoteControlIsOnline()
{
if (rc_init_flag)
return DaemonIsOnline(rc_daemon_instance);
return 0;
}

View File

@@ -6,3 +6,149 @@
#define TRONONEH7_SCAFFOLD_RC_H
#endif //TRONONEH7_SCAFFOLD_RC_H
/**
* @file remote.h
* @author TuxMonkey (nqx_2004@qq.com)
* @brief 遥控器模块
* @version 0.1
* @date 2025-01-06
*
* @copyright Copyright (c) 2025
*
*/
#ifndef REMOTE_H
#define REMOTE_H
//@todo测试define
#define REMOTE_FS_I6X
#include "stdint.h"
#include "main.h"
#include "usart.h"
#include "key_define.h"
#include "robot_def.h"
// 检查接收值是否出错
#define RC_CH_VALUE_MIN ((uint16_t)0)
#define RC_CH_VALUE_OFFSET ((uint16_t)1024)
#define RC_CH_VALUE_MAX ((uint16_t)2048)
/* ----------------------- RC Switch Definition----------------------------- */
#define RC_SW_UP ((uint16_t)1) // 开关向上时的值
#define RC_SW_MID ((uint16_t)3) // 开关中间时的值
#define RC_SW_DOWN ((uint16_t)2) // 开关向下时的值
// 三个判断开关状态的宏
#define switch_is_down(s) (s == RC_SW_DOWN)
#define switch_is_mid(s) (s == RC_SW_MID)
#define switch_is_up(s) (s == RC_SW_UP)
#ifdef REMOTE_DJI_DT7
typedef struct
{
struct
{
int16_t rocker_l_; // 左水平
int16_t rocker_l1; // 左竖直
int16_t rocker_r_; // 右水平
int16_t rocker_r1; // 右竖直
int16_t dial; // 侧边拨轮
uint8_t switch_left; // 左侧开关
uint8_t switch_right; // 右侧开关
} rc;
struct
{
int16_t x;
int16_t y;
int16_t z;
uint8_t press_l;
uint8_t press_r;
} mouse;
Key_t key[3]; // 改为位域后的键盘索引,空间减少8倍,速度增加16~倍
uint8_t key_count[3][16];
} RC_ctrl_t;
#endif // REMOTE_DJI_DT7
// @todo 当前结构体嵌套过深,需要进行优化
#ifdef REMOTE_FS_I6X
typedef struct
{
struct
{
// int16_t rocker_l_; // 左水平
// int16_t rocker_l1; // 左竖直
// int16_t rocker_r_; // 右水平
// int16_t rocker_r1; // 右竖直
// int16_t dial; // 侧边拨轮
// uint8_t switch_left; // 左侧开关
// uint8_t switch_right; // 右侧开关
int16_t ch[10];
} rc;
struct
{
int16_t x;
int16_t y;
int16_t z;
uint8_t press_l;
uint8_t press_r;
} mouse;
uint8_t sw_a; // SWA
uint8_t sw_b; // SWB
uint16_t sw_c; // SWC 3
uint8_t sw_d; // SWD
int16_t ch1; // 通道1 (云台yaw)
int16_t ch2; // 通道2 (云台pitch)
int16_t ch3; // 通道3 (前后)
int16_t ch4; // 通道4 (左右)
uint8_t sw_a_last;
uint8_t sw_b_last;
uint16_t sw_c_last;
uint8_t sw_d_last;
uint8_t sw_a_up_to_down_flag;
uint8_t sw_b_midtoup_flag;
uint8_t sw_b_uptomid_flag;
uint8_t sw_b_midtodown_flag;
uint8_t sw_b_downtomid_flag;
uint8_t sw_c_midtoup_flag;
uint8_t sw_c_uptomid_flag;
uint8_t sw_c_midtodown_flag;
uint8_t sw_c_downtomid_flag;
uint8_t sw_d_up_to_down_flag;
uint16_t online;
Key_t key[3]; // 改为位域后的键盘索引,空间减少8倍,速度增加16~倍
uint8_t key_count[3][16];
} RC_ctrl_t;
#endif // FSI6X
/* ------------------------- Internal Data ----------------------------------- */
/**
* @brief 初始化遥控器,该函数会将遥控器注册到串口
*
* @attention 注意分配正确的串口硬件,遥控器在C板上使用USART3
*
*/
RC_ctrl_t *RemoteControlInit(UART_HandleTypeDef *rc_usart_handle);
/**
* @brief 检查遥控器是否在线,若尚未初始化也视为离线
*
* @return uint8_t 1:在线 0:离线
*/
uint8_t RemoteControlIsOnline();
#endif // REMOTE_H

View File

@@ -0,0 +1,64 @@
#include "daemon.h"
#include "bsp_dwt.h"
#include "stdlib.h"
#include "memory.h"
/* 用于保存所有的daemon instance */
static Daemon_Instance *daemon_instances[DAEMON_MAX_NUM];
static uint8_t idx; // 用于记录当前注册的daemon数量
Daemon_Instance *DaemonRegister(Daemon_Init_Config_s *config)
{
Daemon_Instance *daemon_instance = (Daemon_Instance *) malloc(sizeof(Daemon_Instance));
memset(daemon_instance, 0, sizeof(Daemon_Instance));
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_instances[idx++] = daemon_instance;
return daemon_instance;
}
/**
* @brief 当模块收到新的数据或进行其他动作时,调用该函数重载temp_count,相当于"喂狗"
*
* @param daemon daemon实例指针
*/
void DaemonReload(Daemon_Instance *daemon)
{
daemon->temp_count = daemon->reload_count;
}
/**
* @brief 确认模块是否离线
*
* @param daemon
* @return uint8_t 若在线且工作正常,返回1;否则返回零. 后续根据异常类型和离线状态等进行优化.
*/
uint8_t DaemonIsOnline(Daemon_Instance *daemon)
{
return daemon->temp_count > 0;
}
/**
* @brief 放入rtos中,会给每个daemon实例的temp_count按频率进行递减操作.
* 模块成功接受数据或成功操作则会重载temp_count的值为reload_count.
*
*/
void DaemonTask(void)
{
Daemon_Instance *daemon;
for (uint8_t i = 0; i < idx; i++)
{
daemon = daemon_instances[i];
if (daemon->temp_count > 0) // 如果计数器还有值,说明上一次喂狗后还没有超时,则计数器减一
daemon->temp_count--;
else if (daemon->callback != NULL) // 等于零说明超时了,调用回调函数(如果有的话)
daemon->callback(daemon->id);
// @todo 可以加入蜂鸣器或者led等提示
}
}

View File

@@ -1,8 +1,72 @@
//
// Created by ASUS on 2025/11/17.
//
/**
* @file daemon.h
* @author TuxMonkey (nqx_2004@qq.com)
* @brief 守护进程
* @version 0.1
* @date 2025-01-04
*
* @copyright Copyright (c) 2025
*
*/
#ifndef TRONONEH7_SCAFFOLD_DAEMON_H
#define TRONONEH7_SCAFFOLD_DAEMON_H
#ifndef DAEMON_H
#define DAEMON_H
#endif //TRONONEH7_SCAFFOLD_DAEMON_H
#include "stdint.h"
#include "memory.h"
#define DAEMON_MAX_NUM 32 // 支持的最大守护进程数量
/* 模块离线处理函数指针 */
typedef void (*offline_callback)(void *);
typedef struct daemon_ins
{
uint16_t reload_count; // 重载值
offline_callback callback; // 离线处理函数,当模块离线时调用
uint16_t temp_count; // 当前值,减为零说明模块离线或异常
void *id; // 模块id,用于标识模块,初始化时传入
} Daemon_Instance;
/* daemon初始化配置 */
typedef struct
{
uint16_t reload_count; // 实际上这是app唯一需要设置的值?
uint16_t init_count; // 上线等待时间,有些模块需要收到主控的指令才会反馈报文,或pc等需要开机时间
offline_callback callback; // 异常处理函数,当模块发生异常时会被调用
void *owner_id; // id取拥有daemon的实例的地址,如DJIMotorInstance*,cast成void*类型
} Daemon_Init_Config_s;
/**
* @brief 注册一个daemon实例
*
* @param config 初始化配置
* @return DaemonInstance* 返回实例指针
*/
Daemon_Instance *DaemonRegister(Daemon_Init_Config_s *config);
/**
* @brief 当模块收到新的数据或进行其他动作时,调用该函数重载temp_count,相当于"喂狗"
*
* @param daemon daemon实例指针
*/
void DaemonReload(Daemon_Instance *daemon);
/**
* @brief 确认模块是否离线
*
* @param daemon
* @return uint8_t 若在线且工作正常,返回1;否则返回零. 后续根据异常类型和离线状态等进行优化.
*/
uint8_t DaemonIsOnline(Daemon_Instance *daemon);
/**
* @brief 放入rtos中,会给每个daemon实例的temp_count按频率进行递减操作.
* 模块成功接受数据或成功操作则会重载temp_count的值为reload_count.
*
*/
void DaemonTask(void);
#endif // DAEMON_H