修复CAN中断优先级导致函数重入访问static变量的问题,提升了bsp和部分module的性能。
This commit is contained in:
parent
eefc0883ed
commit
41d033e3f8
|
@ -48,4 +48,22 @@
|
|||
|
||||
### 紧急程度
|
||||
|
||||
⭐⭐⭐⭐⭐
|
||||
⭐⭐⭐⭐⭐
|
||||
|
||||
## 总线挂载多个电机后,pitch和yaw的GM6020电机出现编码器反馈值跳动
|
||||
|
||||
> 已修复,详细信息见“如何定位bug.md”
|
||||
|
||||
CAN1总线挂载5个电机,4\*3508+1\*6020,控制报文发送频率为500Hz,电机的反馈频率皆为1kHz.云台在控制时会出现突然跳动.添加到Ozone graph查看发现ECD(编码器)值在静止状态下也会出现突然抖动,并且幅度超过4000.但不会出现超过编码器反馈值范围的值.
|
||||
|
||||
### 尝试解决的方案
|
||||
|
||||
若使用单个6020电机,不会出现此问题. 曾认为是指针越界导致`motor_measure->ecd`值被修改, 需要进一步观察其他反馈值是否出现问题. 且反馈值始终在编码器范围之内.
|
||||
|
||||
### 如何复现问题
|
||||
|
||||
同时启用CAN1和CAN2,并在单条CAN总线上挂载超过5个电机.
|
||||
|
||||
### 紧急程度
|
||||
|
||||
⭐⭐⭐
|
|
@ -122,7 +122,7 @@ void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
|
|||
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
||||
|
||||
/* CAN1 interrupt Init */
|
||||
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 6, 0);
|
||||
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
|
||||
HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
|
||||
|
@ -155,7 +155,7 @@ void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
|
|||
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
|
||||
|
||||
/* CAN2 interrupt Init */
|
||||
HAL_NVIC_SetPriority(CAN2_RX0_IRQn, 6, 0);
|
||||
HAL_NVIC_SetPriority(CAN2_RX0_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(CAN2_RX0_IRQn);
|
||||
HAL_NVIC_SetPriority(CAN2_RX1_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(CAN2_RX1_IRQn);
|
||||
|
|
|
@ -659,7 +659,7 @@ Project.SetOSPlugin(“plugin_name”)
|
|||
|
||||
在Terminal窗口查看,还可以通过命令直接控制单片机的运行(不过不常用)。
|
||||
|
||||
未打开窗口则在view-> terminal中打开。
|
||||
未打开窗口则在view-> terminal中打开。使用bsp_log打印的日志会输出到该窗口中.
|
||||
|
||||
- **外设查看**
|
||||
|
||||
|
|
|
@ -276,9 +276,9 @@ Mcu.UserName=STM32F407IGHx
|
|||
MxCube.Version=6.7.0
|
||||
MxDb.Version=DB.6.0.70
|
||||
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
|
||||
NVIC.CAN1_RX0_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN1_RX0_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN1_RX1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN2_RX0_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN2_RX0_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true\:true
|
||||
NVIC.CAN2_RX1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true
|
||||
NVIC.DMA1_Stream1_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
|
||||
NVIC.DMA1_Stream2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true
|
||||
|
@ -432,7 +432,7 @@ PF0.Mode=I2C
|
|||
PF0.Signal=I2C2_SDA
|
||||
PF1.Mode=I2C
|
||||
PF1.Signal=I2C2_SCL
|
||||
PF6.GPIOParameters=GPIO_Label,GPIO_Speed,GPIO_PuPd
|
||||
PF6.GPIOParameters=GPIO_Speed,GPIO_PuPd,GPIO_Label
|
||||
PF6.GPIO_Label=IMU_TEMP
|
||||
PF6.GPIO_PuPd=GPIO_PULLUP
|
||||
PF6.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
|
||||
|
|
|
@ -131,9 +131,9 @@ void CANSetDLC(CANInstance *_instance, uint8_t length)
|
|||
*/
|
||||
static void CANFIFOxCallback(CAN_HandleTypeDef *_hcan, uint32_t fifox)
|
||||
{
|
||||
static uint8_t can_rx_buff[8]; // 用于保存接收到的数据,static是为了减少栈空间占用,避免重复分配
|
||||
static CAN_RxHeaderTypeDef rxconf; // 同上
|
||||
|
||||
uint8_t can_rx_buff[8];
|
||||
|
||||
HAL_CAN_GetRxMessage(_hcan, fifox, &rxconf, can_rx_buff); // 从FIFO中获取数据
|
||||
for (size_t i = 0; i < idx; ++i)
|
||||
{ // 两者相等说明这是要找的实例
|
||||
|
|
|
@ -15,7 +15,7 @@ static GPIOInstance *gpio_instance[GPIO_MX_DEVICE_NUM] = {NULL};
|
|||
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
|
||||
{
|
||||
// 如有必要,可以根据pinstate和HAL_GPIO_ReadPin来判断是上升沿还是下降沿/rise&fall等
|
||||
static GPIOInstance *gpio;
|
||||
GPIOInstance *gpio;
|
||||
for (size_t i = 0; i < idx; i++)
|
||||
{
|
||||
gpio = gpio_instance[i];
|
||||
|
|
|
@ -23,8 +23,7 @@ static void CANCommResetRx(CANCommInstance *ins)
|
|||
*/
|
||||
static void CANCommRxCallback(CANInstance *_instance)
|
||||
{
|
||||
static CANCommInstance *comm;
|
||||
comm = (CANCommInstance *)_instance->id; // 注意写法,将can instance的id强制转换为CANCommInstance*类型
|
||||
CANCommInstance *comm = (CANCommInstance *)_instance->id; // 注意写法,将can instance的id强制转换为CANCommInstance*类型
|
||||
|
||||
/* 接收状态判断 */
|
||||
if (_instance->rx_buff[0] == CAN_COMM_HEADER && comm->recv_state == 0) // 之前尚未开始接收且此次包里第一个位置是帧头
|
||||
|
|
|
@ -33,7 +33,7 @@ uint8_t DaemonIsOnline(DaemonInstance *instance)
|
|||
|
||||
void DaemonTask()
|
||||
{
|
||||
static DaemonInstance *dins; // 提高可读性同时降低访存开销
|
||||
DaemonInstance *dins; // 提高可读性同时降低访存开销
|
||||
for (size_t i = 0; i < idx; ++i)
|
||||
{
|
||||
dins = daemon_instances[i];
|
||||
|
|
|
@ -29,9 +29,8 @@ static uint8_t ist8310_write_reg_data_error[IST8310_WRITE_REG_NUM][3] = {
|
|||
*/
|
||||
static void IST8310Decode(IICInstance *iic)
|
||||
{
|
||||
static int16_t temp[3]; // 用于存储解码后的数据
|
||||
static IST8310Instance *ist; // 用于存储IST8310实例的指针
|
||||
ist = (IST8310Instance *)(iic->id); // iic的id保存了IST8310实例的指针(父指针)
|
||||
int16_t temp[3]; // 用于存储解码后的数据
|
||||
IST8310Instance *ist= (IST8310Instance *)(iic->id); // iic的id保存了IST8310实例的指针(父指针)
|
||||
|
||||
memcpy(temp, ist->iic_buffer, 6 * sizeof(uint8_t)); // 不要强制转换,直接cpy
|
||||
for (uint8_t i = 0; i < 3; i++)
|
||||
|
|
|
@ -29,7 +29,7 @@ static DaemonInstance *vision_daemon_instance;
|
|||
*/
|
||||
static void DecodeVision()
|
||||
{
|
||||
static uint16_t flag_register;
|
||||
uint16_t flag_register;
|
||||
DaemonReload(vision_daemon_instance); // 喂狗
|
||||
get_protocol_info(vision_usart_instance->recv_buff, &flag_register, (uint8_t *)&recv_data.pitch);
|
||||
// TODO: code to resolve flag_register;
|
||||
|
@ -80,9 +80,9 @@ Vision_Recv_s *VisionInit(UART_HandleTypeDef *_handle)
|
|||
*/
|
||||
void VisionSend(Vision_Send_s *send)
|
||||
{
|
||||
static uint16_t flag_register;
|
||||
static uint8_t send_buff[VISION_SEND_SIZE];
|
||||
static uint16_t tx_len;
|
||||
uint16_t flag_register;
|
||||
uint8_t send_buff[VISION_SEND_SIZE];
|
||||
uint16_t tx_len;
|
||||
// TODO: code to set flag_register
|
||||
|
||||
// 将数据转化为seasky协议的数据包
|
||||
|
|
|
@ -72,7 +72,8 @@ static void MotorSenderGrouping(DJIMotorInstance *motor, CAN_Init_Config_s *conf
|
|||
if (dji_motor_instance[i]->motor_can_instance->can_handle == config->can_handle && dji_motor_instance[i]->motor_can_instance->rx_id == config->rx_id)
|
||||
{
|
||||
LOGERROR("[dji_motor] ID crash. Check in debug mode, add dji_motor_instance to watch to get more information."); // 后续可以把id和CAN打印出来
|
||||
while (1); // 6020的id 1-4和2006/3508的id 5-8会发生冲突(若有注册,即1!5,2!6,3!7,4!8) (1!5!,LTC! (((不是)
|
||||
while (1)
|
||||
; // 6020的id 1-4和2006/3508的id 5-8会发生冲突(若有注册,即1!5,2!6,3!7,4!8) (1!5!,LTC! (((不是)
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -99,7 +100,8 @@ static void MotorSenderGrouping(DJIMotorInstance *motor, CAN_Init_Config_s *conf
|
|||
if (dji_motor_instance[i]->motor_can_instance->can_handle == config->can_handle && dji_motor_instance[i]->motor_can_instance->rx_id == config->rx_id)
|
||||
{
|
||||
LOGERROR("[dji_motor] ID crash. Check in debug mode, add dji_motor_instance to watch to get more information.");
|
||||
while (1); // 6020的id 1-4和2006/3508的id 5-8会发生冲突(若有注册,即1!5,2!6,3!7,4!8)
|
||||
while (1)
|
||||
; // 6020的id 1-4和2006/3508的id 5-8会发生冲突(若有注册,即1!5,2!6,3!7,4!8)
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -118,13 +120,10 @@ static void MotorSenderGrouping(DJIMotorInstance *motor, CAN_Init_Config_s *conf
|
|||
*/
|
||||
static void DecodeDJIMotor(CANInstance *_instance)
|
||||
{
|
||||
// 由于需要多次变址访存,直接将buff和measure地址保存在寄存器里避免多次存取
|
||||
static uint8_t *rxbuff;
|
||||
static DJI_Motor_Measure_s *measure;
|
||||
rxbuff = _instance->rx_buff;
|
||||
// 这里对can instance的id进行了强制转换,从而获得电机的instance实例地址
|
||||
// _instance指针指向的id是对应电机instance的地址,通过强制转换为电机instance的指针,再通过->运算符访问电机的成员motor_measure,最后取地址获得指针
|
||||
measure = &(((DJIMotorInstance *)_instance->id)->motor_measure); // measure要多次使用,保存指针减小访存开销
|
||||
uint8_t *rxbuff = _instance->rx_buff;
|
||||
DJI_Motor_Measure_s *measure = &(((DJIMotorInstance *)_instance->id)->motor_measure); // measure要多次使用,保存指针减小访存开销
|
||||
|
||||
// 解析数据并对电流和速度进行滤波,电机的反馈报文具体格式见电机说明手册
|
||||
measure->last_ecd = measure->ecd;
|
||||
|
@ -188,7 +187,7 @@ void DJIMotorChangeFeed(DJIMotorInstance *motor, Closeloop_Type_e loop, Feedback
|
|||
}
|
||||
else
|
||||
{
|
||||
LOGERROR("[dji_motor] loop type error, check memory access and func param");// 检查是否传入了正确的LOOP类型,或发生了指针越界
|
||||
LOGERROR("[dji_motor] loop type error, check memory access and func param"); // 检查是否传入了正确的LOOP类型,或发生了指针越界
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,15 +216,14 @@ void DJIMotorSetRef(DJIMotorInstance *motor, float ref)
|
|||
// 为所有电机实例计算三环PID,发送控制报文
|
||||
void DJIMotorControl()
|
||||
{
|
||||
// 预先通过静态变量定义避免反复释放分配栈空间,直接保存一次指针引用从而减小访存的开销
|
||||
// 同样可以提高可读性
|
||||
static uint8_t group, num; // 电机组号和组内编号
|
||||
static int16_t set; // 电机控制CAN发送设定值
|
||||
static DJIMotorInstance *motor;
|
||||
static Motor_Control_Setting_s *motor_setting; // 电机控制参数
|
||||
static Motor_Controller_s *motor_controller; // 电机控制器
|
||||
static DJI_Motor_Measure_s *motor_measure; // 电机测量值
|
||||
static float pid_measure, pid_ref; // 电机PID测量值和设定值
|
||||
// 直接保存一次指针引用从而减小访存的开销,同样可以提高可读性
|
||||
uint8_t group, num; // 电机组号和组内编号
|
||||
int16_t set; // 电机控制CAN发送设定值
|
||||
DJIMotorInstance *motor;
|
||||
Motor_Control_Setting_s *motor_setting; // 电机控制参数
|
||||
Motor_Controller_s *motor_controller; // 电机控制器
|
||||
DJI_Motor_Measure_s *motor_measure; // 电机测量值
|
||||
float pid_measure, pid_ref; // 电机PID测量值和设定值
|
||||
|
||||
// 遍历所有电机实例,进行串级PID的计算并设置发送报文的值
|
||||
for (size_t i = 0; i < idx; ++i)
|
||||
|
|
|
@ -40,12 +40,9 @@ static float uint_to_float(int x_int, float x_min, float x_max, int bits)
|
|||
*/
|
||||
static void HTMotorDecode(CANInstance *motor_can)
|
||||
{
|
||||
static uint16_t tmp; // 用于暂存解析值,稍后转换成float数据,避免多次创建临时变量
|
||||
static HTMotor_Measure_t *measure;
|
||||
static uint8_t *rxbuff;
|
||||
|
||||
rxbuff = motor_can->rx_buff;
|
||||
measure = &((HTMotorInstance *)motor_can->id)->motor_measure; // 将can实例中保存的id转换成电机实例的指针
|
||||
uint16_t tmp; // 用于暂存解析值,稍后转换成float数据,避免多次创建临时变量
|
||||
uint8_t *rxbuff = motor_can->rx_buff;
|
||||
HTMotor_Measure_t *measure = &((HTMotorInstance *)motor_can->id)->motor_measure; // 将can实例中保存的id转换成电机实例的指针
|
||||
|
||||
measure->last_angle = measure->total_angle;
|
||||
|
||||
|
@ -89,12 +86,12 @@ void HTMotorSetRef(HTMotorInstance *motor, float ref)
|
|||
|
||||
void HTMotorControl()
|
||||
{
|
||||
static float set, pid_measure, pid_ref;
|
||||
static uint16_t tmp;
|
||||
static HTMotorInstance *motor;
|
||||
static HTMotor_Measure_t *measure;
|
||||
static Motor_Control_Setting_s *setting;
|
||||
static CANInstance *motor_can;
|
||||
float set, pid_measure, pid_ref;
|
||||
uint16_t tmp;
|
||||
HTMotorInstance *motor;
|
||||
HTMotor_Measure_t *measure;
|
||||
Motor_Control_Setting_s *setting;
|
||||
CANInstance *motor_can;
|
||||
|
||||
// 遍历所有电机实例,计算PID
|
||||
for (size_t i = 0; i < idx; i++)
|
||||
|
|
|
@ -13,8 +13,8 @@ static CANInstance *sender_instance; // 多电机发送时使用的caninstance(
|
|||
*/
|
||||
static void LKMotorDecode(CANInstance *_instance)
|
||||
{
|
||||
static LKMotor_Measure_t *measure;
|
||||
static uint8_t *rx_buff;
|
||||
LKMotor_Measure_t *measure;
|
||||
uint8_t *rx_buff;
|
||||
rx_buff = _instance->rx_buff;
|
||||
measure = &(((LKMotorInstance *)_instance->id)->measure); // 通过caninstance保存的id获取对应的motorinstance
|
||||
|
||||
|
@ -72,11 +72,11 @@ LKMotorInstance *LKMotorInit(Motor_Init_Config_s *config)
|
|||
/* 第一个电机的can instance用于发送数据,向其tx_buff填充数据 */
|
||||
void LKMotorControl()
|
||||
{
|
||||
static float pid_measure, pid_ref;
|
||||
static int16_t set;
|
||||
static LKMotorInstance *motor;
|
||||
static LKMotor_Measure_t *measure;
|
||||
static Motor_Control_Setting_s *setting;
|
||||
float pid_measure, pid_ref;
|
||||
int16_t set;
|
||||
LKMotorInstance *motor;
|
||||
LKMotor_Measure_t *measure;
|
||||
Motor_Control_Setting_s *setting;
|
||||
|
||||
for (size_t i = 0; i < idx; ++i)
|
||||
{
|
||||
|
|
|
@ -82,7 +82,7 @@ void Servo_Motor_Type_Select(ServoInstance *Servo_Motor, int16_t mode)
|
|||
*/
|
||||
void ServeoMotorControl()
|
||||
{
|
||||
static ServoInstance *Servo_Motor;
|
||||
ServoInstance *Servo_Motor;
|
||||
|
||||
for (size_t i = 0; i < servo_idx; i++)
|
||||
{
|
||||
|
|
|
@ -92,12 +92,13 @@ static void RemoteControlRxCallback()
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @brief 遥控器离线的回调函数,注册到守护进程中,串口掉线时调用
|
||||
*
|
||||
*/
|
||||
static void RCLostCallback()
|
||||
static void RCLostCallback(void *id)
|
||||
{
|
||||
// @todo 遥控器丢失的处理
|
||||
USARTServiceInit(rc_usart_instance); // 尝试重新启动接收
|
||||
}
|
||||
|
||||
RC_ctrl_t *RemoteControlInit(UART_HandleTypeDef *rc_usart_handle)
|
||||
|
@ -111,9 +112,9 @@ RC_ctrl_t *RemoteControlInit(UART_HandleTypeDef *rc_usart_handle)
|
|||
// 进行守护进程的注册,用于定时检查遥控器是否正常工作
|
||||
// @todo 当前守护进程直接在这里注册,后续考虑将其封装到遥控器的初始化函数中,即可以让用户决定reload_count的值(是否有必要?)
|
||||
Daemon_Init_Config_s daemon_conf = {
|
||||
.reload_count = 100, // 100ms,遥控器的接收频率实际上是1000/14Hz(大约70)
|
||||
.callback = NULL, // 后续考虑重新启动遥控器对应串口的传输
|
||||
.owner_id = NULL, // 只有1个遥控器,不需要owner_id
|
||||
.reload_count = 10, // 100ms未收到数据视为离线,遥控器的接收频率实际上是1000/14Hz(大约70Hz)
|
||||
.callback = RCLostCallback,
|
||||
.owner_id = NULL, // 只有1个遥控器,不需要owner_id
|
||||
};
|
||||
rc_daemon_instance = DaemonRegister(&daemon_conf);
|
||||
|
||||
|
@ -126,5 +127,4 @@ uint8_t RemotecontrolIsOnline()
|
|||
if (rc_init_flag)
|
||||
return DaemonIsOnline(rc_daemon_instance);
|
||||
return 0;
|
||||
|
||||
}
|
|
@ -13,8 +13,8 @@ static SuperCapInstance *super_cap_instance = NULL; // 可以由app保存此指
|
|||
|
||||
static void SuperCapRxCallback(CANInstance *_instance)
|
||||
{
|
||||
static uint8_t *rxbuff;
|
||||
static SuperCap_Msg_s *Msg;
|
||||
uint8_t *rxbuff;
|
||||
SuperCap_Msg_s *Msg;
|
||||
rxbuff = _instance->rx_buff;
|
||||
Msg = &super_cap_instance->cap_msg;
|
||||
Msg->vol = (uint16_t)(rxbuff[0] << 8 | rxbuff[1]);
|
||||
|
|
Loading…
Reference in New Issue