mirror of
https://gitee.com/dlmu-cone/tronone-h7-scaffold
synced 2026-07-23 19:25:09 +08:00
remote control init
This commit is contained in:
@@ -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, ... */
|
||||
//Todo:INS_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 */
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ void robotSelfCheck(void)
|
||||
void ws2812Task(void *argument)
|
||||
{
|
||||
(void) argument;
|
||||
RobotMode_t RobotMode = REMOTE_NOT_CONNECTED; // 初始状态为遥控器未连接
|
||||
while (1)
|
||||
{
|
||||
switch (RobotMode)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
70
User_Code/module/periph/remote_control/key_define.h
Normal file
70
User_Code/module/periph/remote_control/key_define.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
64
User_Code/module/software/daemon/daemon.c
Normal file
64
User_Code/module/software/daemon/daemon.c
Normal 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等提示
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user