添加了遥控器的离线检测和daemone的说明文档

This commit is contained in:
NeoZng
2023-01-04 21:26:11 +08:00
parent 48370d4411
commit 3db2a20a0e
14 changed files with 135 additions and 41 deletions

View File

@@ -1,5 +1,36 @@
daemon
# daemon
<p align='right'>neozng1@hnu.edu.cn</p>
用于监测模块和应用运行情况的module(和官方代码中的deteck task作用相同)
用于监测模块和应用运行情况的module(和官方代码中的deteck task作用相同)
## 使用范例
要使用该module则包含`daemon.h`的头文件并在使用daemon的文件中保留一个daemon的指针。
初始化的时候,要传入以下参数:
```c
typedef struct
{
uint16_t reload_count; // 实际上这是app唯一需要设置的值?
offline_callback callback; // 异常处理函数,当模块发生异常时会被调用
void *owner_id; // id取拥有daemon的实例的地址,如DJIMotorInstance*,cast成void*类型
} Daemon_Init_Config_s;
```
`reload_count`是”喂狗“时的重载值,一般根据你希望的离线容许时间和模块接收数据/访问数据的频率确定。
`daemon_task()`会在实时系统中以1kHz的频率运行每次运行该任务都会将所有daemon实例当前的count进行自减操作当count减为零则说明模块已经很久没有上线处于deactivated状态即没有收到数据也没有进行其他读写操作
`offline_callback`是模块离线的回调函数即当包含daemon的模块发生离线情况时该函数会被调用以应对离线情况如重启模块重新初始化等。如果没有则传入`NULL`即可。
`owner_id`即模块取自身地址并通过强制类型转换化为`void*`类型,用于拥有多个实例的模块在`offline_callback`中区分自身。如多个电机都使用一个相同的`offline_callback`,那么在调用回调函数的时候就可以通过该指针来访问某个特定的电机。
> 这种方法也称作“parent pointer”即**保存拥有指向自身的指针对象的地址**。这样就可以在特定的情况下通过自身来访问自己的父对象。
## 具体实现
`DaemonTask()`在操作系统中以1kHz运行。注释详细请参见`daemon.c`

View File

@@ -43,7 +43,8 @@ static void IDcrash_Handler(uint8_t conflict_motor_idx, uint8_t temp_motor_idx)
}
/**
* @brief 根据电调/拨码开关上的ID,计算发送ID和接收ID,并对电机进行分组以便处理多电机控制命令
* @brief 根据电调/拨码开关上的ID,根据说明书的默认id分配方式计算发送ID和接收ID,
* 并对电机进行分组以便处理多电机控制命令
*
* @param config
*/
@@ -69,8 +70,8 @@ static void MotorSenderGrouping(DJIMotorInstance *motor, CAN_Init_Config_s *conf
}
// 计算接收id并设置分组发送id
config->rx_id = 0x200 + motor_id + 1;
sender_enable_flag[motor_grouping] = 1;
config->rx_id = 0x200 + motor_id + 1; // 把ID+1,进行分组设置
sender_enable_flag[motor_grouping] = 1; // 设置发送标志位,防止发送空帧
motor->message_num = motor_send_num;
motor->sender_group = motor_grouping;
@@ -94,8 +95,8 @@ static void MotorSenderGrouping(DJIMotorInstance *motor, CAN_Init_Config_s *conf
motor_grouping = config->can_handle == &hcan1 ? 2 : 5;
}
config->rx_id = 0x204 + motor_id + 1;
sender_enable_flag[motor_grouping] = 1;
config->rx_id = 0x204 + motor_id + 1; // 把ID+1,进行分组设置
sender_enable_flag[motor_grouping] = 1; // 只要有电机注册到这个分组,置为1;在发送函数中会通过此标志判断是否有电机注册
motor->message_num = motor_send_num;
motor->sender_group = motor_grouping;
@@ -127,7 +128,7 @@ static void DecodeDJIMotor(CANInstance *_instance)
// 这里对can instance的id进行了强制转换,从而获得电机的instance实例地址
measure = &((DJIMotorInstance *)_instance->id)->motor_measure; // measure要多次使用,保存指针减小访存开销
// 解析数据并对电流和速度进行滤波
// 解析数据并对电流和速度进行滤波,电机的反馈报文具体格式见电机说明手册
measure->last_ecd = measure->ecd;
measure->ecd = ((uint16_t)rxbuff[0]) << 8 | rxbuff[1];
measure->angle_single_round = ECD_ANGLE_COEF_DJI * (float)measure->ecd;
@@ -137,7 +138,7 @@ static void DecodeDJIMotor(CANInstance *_instance)
CURRENT_SMOOTH_COEF * (float)((int16_t)(rxbuff[4] << 8 | rxbuff[5]));
measure->temperate = rxbuff[6];
// 多圈角度计算,计算的前提是两次采样间电机转过的角度小于180°
// 多圈角度计算,前提是两次采样间电机转过的角度小于180°,高速转动时可能会出现问题,自己画个图就清楚计算过程了
if (measure->ecd - measure->last_ecd > 4096)
measure->total_round--;
else if (measure->ecd - measure->last_ecd < -4096)
@@ -152,8 +153,8 @@ DJIMotorInstance *DJIMotorInit(Motor_Init_Config_s *config)
memset(instance, 0, sizeof(DJIMotorInstance));
// motor basic setting 电机基本设置
instance->motor_type = config->motor_type;
instance->motor_settings = config->controller_setting_init_config;
instance->motor_type = config->motor_type; // 6020 or 2006 or 3508
instance->motor_settings = config->controller_setting_init_config; // 正反转,闭环类型等
// motor controller init 电机控制器初始化
PID_Init(&instance->motor_controller.current_PID, &config->controller_param_init_config.current_PID);
@@ -161,6 +162,7 @@ DJIMotorInstance *DJIMotorInit(Motor_Init_Config_s *config)
PID_Init(&instance->motor_controller.angle_PID, &config->controller_param_init_config.angle_PID);
instance->motor_controller.other_angle_feedback_ptr = config->controller_param_init_config.other_angle_feedback_ptr;
instance->motor_controller.other_speed_feedback_ptr = config->controller_param_init_config.other_speed_feedback_ptr;
// 后续增加电机前馈控制器(速度和电流)
// 电机分组,因为至多4个电机可以共用一帧CAN控制报文
MotorSenderGrouping(instance, &config->can_init_config);
@@ -273,8 +275,8 @@ void DJIMotorControl()
// 分组填入发送数据
group = motor->sender_group;
num = motor->message_num;
sender_assignment[group].tx_buff[2 * num] = (uint8_t)(set >> 8);
sender_assignment[group].tx_buff[2 * num + 1] = (uint8_t)(set & 0x00ff);
sender_assignment[group].tx_buff[2 * num] = (uint8_t)(set >> 8); // 低八位
sender_assignment[group].tx_buff[2 * num + 1] = (uint8_t)(set & 0x00ff); // 高八位
// 电机是否停止运行
if (motor->stop_flag == MOTOR_STOP)
@@ -288,7 +290,7 @@ void DJIMotorControl()
{
if (sender_enable_flag[i])
{
CANTransmit(&sender_assignment[i],1);
CANTransmit(&sender_assignment[i], 1);
}
}
}

View File

@@ -98,10 +98,10 @@ static void JudgeReadData(uint8_t *ReadFromUsart)
}
}
}
// 首地址加帧长度,指向CRC16下一字节,用来判断是否为0xA5,用来判断一个数据包是否有多帧数据
// 首地址加帧长度,指向CRC16下一字节,用来判断是否为0xA5,从而判断一个数据包是否有多帧数据
if (*(ReadFromUsart + sizeof(xFrameHeader) + LEN_CMDID + referee_info.FrameHeader.DataLength + LEN_TAIL) == 0xA5)
{
// 如果一个数据包出现了多帧数据,则再次读取
// 如果一个数据包出现了多帧数据,则再次调用解析函数,直到所有数据包解析完毕
JudgeReadData(ReadFromUsart + sizeof(xFrameHeader) + LEN_CMDID + referee_info.FrameHeader.DataLength + LEN_TAIL);
}
}

View File

@@ -3,13 +3,14 @@
#include "bsp_usart.h"
#include "memory.h"
#include "stdlib.h"
#include "daemon.h"
#define REMOTE_CONTROL_FRAME_SIZE 18u // 遥控器接收的buffer大小
// 遥控器数据
static RC_ctrl_t rc_ctrl[2]; //[0]:当前数据TEMP,[1]:上一次的数据LAST.用于按键持续按下和切换的判断
// 遥控器拥有的串口实例,因为遥控器是单例,所以这里只有一个,就不封装了
static USARTInstance *rc_usart_instance;
static DaemonInstance *rc_daemon_instance;
/**
* @brief 矫正遥控器摇杆的值,超过660或者小于-660的值都认为是无效值,置0
*
@@ -79,13 +80,22 @@ static void sbus_to_rc(const uint8_t *sbus_buf)
}
/**
* @brief protocol resolve callback
* this func would be called when usart3 idle interrupt happens
* 对sbus_to_rc的简单封装,用于注册到bsp_usart的回调函数中
* @brief 对sbus_to_rc的简单封装,用于注册到bsp_usart的回调函数中
*
*/
static void RemoteControlRxCallback()
{
sbus_to_rc(rc_usart_instance->recv_buff);
DaemonReload(rc_daemon_instance); // 先喂狗
sbus_to_rc(rc_usart_instance->recv_buff); // 进行协议解析
}
/**
* @brief
*
*/
static void RCLostCallback()
{
// @todo 遥控器丢失的处理
}
RC_ctrl_t *RemoteControlInit(UART_HandleTypeDef *rc_usart_handle)
@@ -95,5 +105,15 @@ RC_ctrl_t *RemoteControlInit(UART_HandleTypeDef *rc_usart_handle)
conf.usart_handle = rc_usart_handle;
conf.recv_buff_size = REMOTE_CONTROL_FRAME_SIZE;
rc_usart_instance = USARTRegister(&conf);
// 进行守护进程的注册,用于定时检查遥控器是否正常工作
// @todo 当前守护进程直接在这里注册,后续考虑将其封装到遥控器的初始化函数中,即可以让用户决定reload_count的值(是否有必要?)
Daemon_Init_Config_s daemon_conf = {
.reload_count = 100, // 100ms,遥控器的接收频率实际上是1000/14Hz(大约70)
.callback = NULL, // 后续考虑重新启动遥控器对应串口的传输
.owner_id = NULL, // 只有1个遥控器,不需要owner_id
};
rc_daemon_instance = DaemonRegister(&daemon_conf);
return (RC_ctrl_t *)&rc_ctrl;
}