mirror of
https://gitee.com/dlmu-cone/half-steering-basic-framework
synced 2026-10-24 01:52:20 +08:00
Compare commits
2 Commits
63d4b7ae7d
...
a9fe5069f4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9fe5069f4 | ||
|
|
c40525234f |
56
.Doc/CAN发送非阻塞改造隐患.md
Normal file
56
.Doc/CAN发送非阻塞改造隐患.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# CAN 非阻塞发送改造 · 隐患记录
|
||||
|
||||
> 记录时间:2026-08-29
|
||||
> 背景:修复“拔掉 CAN1 后,CAN2 上的 pitch / 摩擦轮 / 拨盘电机明显抖动”的问题。
|
||||
|
||||
## 一、本次改动摘要
|
||||
|
||||
| 文件 | 改动 | 目的 |
|
||||
| --- | --- | --- |
|
||||
| bsp/can/bsp_can.c | `CANTransmit()` 由忙等改为非阻塞 | 避免某一路 CAN 离线时拖慢电机控制任务 |
|
||||
| bsp/can/bsp_can.c | `CANAddFilter()` 对 `can_filter_conf` 先 `memset` 清零 | 避免未初始化字段成为垃圾匹配 ID |
|
||||
| Src/can.c | CAN1/CAN2 的 `AutoBusOff` 改为 `ENABLE` | 断开后重新连接能自动从 bus-off 恢复 |
|
||||
|
||||
## 二、隐患与风险
|
||||
|
||||
### 1. 双板模式下的 `CANCommSend` 多帧发送可能丢包(当前为休眠隐患)
|
||||
|
||||
- 相关位置:`modules/can_comm/can_comm.c` 的 `CANCommSend()`(约 106、116、121 行)。
|
||||
- `CANCommSend()` 按每 8 字节一帧连续发送。当待发送数据超过 24 字节(即超过 3 帧)时,第 4 帧起会因为 3 个发送邮箱都还在忙碌而被直接丢弃。
|
||||
- 原来的忙等版本会等待邮箱空闲后继续发完,非阻塞版本不会。
|
||||
- 当前工程为 `ONE_BOARD`,该路径未参与编译,暂无影响;一旦切换到 `CHASSIS_BOARD` / `GIMBAL_BOARD`,此问题会暴露。
|
||||
|
||||
### 2. 丢帧变为静默,失去诊断信息
|
||||
|
||||
- 相关位置:`bsp/can/bsp_can.c` 的 `CANTransmit()`(约 107 行)。
|
||||
- 原实现邮箱满时会打印 `LOGWARNING`;现在直接 `return 0`,不重试、不记录。
|
||||
- 未来若总线设备增多或发送频率升高出现真实拥塞,丢帧将不可见,排查难度增加。
|
||||
- 对 DJI 电机本身影响较小:500 Hz 控制帧偶尔丢一两帧,电机内部看门狗时间远大于 2 ms,不会触发保护。
|
||||
|
||||
### 3. `timeout` 参数实际失效
|
||||
|
||||
- `CANTransmit()` 签名仍保留 `float timeout`,但函数内部用 `(void)timeout` 忽略了它。
|
||||
- 调用方传入的 `1`、`0.2`、`0.5` 等值已不再生效,存在可读性与预期一致性隐患。
|
||||
|
||||
### 4. `AutoBusOff` 行为变化 + CubeMX 覆盖风险
|
||||
|
||||
- 相关位置:`Src/can.c`(约 48、80 行)。
|
||||
- 改为 `ENABLE` 后,持续总线故障(如短路)会导致控制器在 bus-off ↔ 恢复之间反复循环;当前未开启 CAN 错误中断,不会产生中断风暴,但行为不同于 `DISABLE` 时的锁死状态。
|
||||
- `Src/can.c` 是 CubeMX 生成文件,从 `basic_framework.ioc` 重新生成工程时会被覆盖回 `DISABLE`,需要同步修改 `.ioc` 中的 Automatic Bus-Off Management 选项。
|
||||
|
||||
### 5. 过滤器配置 `memset`(基本无风险)
|
||||
|
||||
- 相关位置:`bsp/can/bsp_can.c` 的 `CANAddFilter()`(约 32 行)。
|
||||
- 属于纯修正,无副作用。
|
||||
|
||||
## 三、建议后续动作
|
||||
|
||||
- [ ] 若将来启用双板,给 `CANCommSend()` 的多帧发送增加“短且有界”的等待/重试,避免丢中间帧。
|
||||
- [ ] 评估是否为 `CANTransmit()` 增加可选的丢帧统计或低频告警,在“不阻塞任务”与“可诊断”之间取得平衡。
|
||||
- [ ] 在 `basic_framework.ioc` 中同步开启两个 CAN 的 Automatic Bus-Off Management,避免重新生成代码时被覆盖。
|
||||
|
||||
## 四、相关文件
|
||||
|
||||
- `bsp/can/bsp_can.c`
|
||||
- `modules/can_comm/can_comm.c`
|
||||
- `Src/can.c`
|
||||
@@ -45,7 +45,7 @@ void MX_CAN1_Init(void)
|
||||
hcan1.Init.TimeSeg1 = CAN_BS1_10TQ;
|
||||
hcan1.Init.TimeSeg2 = CAN_BS2_3TQ;
|
||||
hcan1.Init.TimeTriggeredMode = DISABLE;
|
||||
hcan1.Init.AutoBusOff = DISABLE;
|
||||
hcan1.Init.AutoBusOff = ENABLE;
|
||||
hcan1.Init.AutoWakeUp = DISABLE;
|
||||
hcan1.Init.AutoRetransmission = ENABLE;
|
||||
hcan1.Init.ReceiveFifoLocked = DISABLE;
|
||||
@@ -77,7 +77,7 @@ void MX_CAN2_Init(void)
|
||||
hcan2.Init.TimeSeg1 = CAN_BS1_10TQ;
|
||||
hcan2.Init.TimeSeg2 = CAN_BS2_3TQ;
|
||||
hcan2.Init.TimeTriggeredMode = DISABLE;
|
||||
hcan2.Init.AutoBusOff = DISABLE;
|
||||
hcan2.Init.AutoBusOff = ENABLE;
|
||||
hcan2.Init.AutoWakeUp = DISABLE;
|
||||
hcan2.Init.AutoRetransmission = ENABLE;
|
||||
hcan2.Init.ReceiveFifoLocked = DISABLE;
|
||||
|
||||
@@ -175,7 +175,7 @@ void ChassisInit()
|
||||
};
|
||||
|
||||
chassis_motor_steering_config.can_init_config.tx_id = 2;
|
||||
motor_steering_rf = DJIMotorInit(&chassis_motor_steering_config);
|
||||
motor_steering_rf = DJIMotorInit(&chassis_motor_steering_config);///
|
||||
|
||||
chassis_motor_steering_config.can_init_config.tx_id = 1;
|
||||
motor_steering_lb = DJIMotorInit(&chassis_motor_steering_config);
|
||||
@@ -192,11 +192,11 @@ void ChassisInit()
|
||||
.MaxOut = 50,
|
||||
// .Derivative_LPF_RC = 0.01, // 0.01
|
||||
};
|
||||
PIDInit(&chassis_follow_pid, &chassis_follow_pid_conf);
|
||||
PIDInit(&chassis_follow_pid, &chassis_follow_pid_conf);////
|
||||
|
||||
// 裁判系统初始化
|
||||
|
||||
referee_data = UITaskInit(&huart6,&ui_data); // 裁判系统初始化,会同时初始化UI
|
||||
referee_data = UITaskInit(&huart6,&ui_data); // 裁判系统初始化,会同时初始化UI//
|
||||
|
||||
SuperCap_Init_Config_s cap_conf = {
|
||||
.can_config = {
|
||||
@@ -556,7 +556,6 @@ void ChassisTask() {
|
||||
DJIMotorEnable(motor_steering_rf);
|
||||
DJIMotorEnable(motor_steering_lb);
|
||||
}
|
||||
|
||||
// 根据控制模式设定旋转速度
|
||||
switch (chassis_cmd_recv.chassis_mode)
|
||||
{
|
||||
@@ -568,7 +567,7 @@ void ChassisTask() {
|
||||
float single_round_offset = chassis_cmd_recv.offset_angle;
|
||||
if(single_round_offset > 180.0f)
|
||||
{
|
||||
single_round_offset -= 360.0f;
|
||||
single_round_offset -= 360.0f;///
|
||||
}
|
||||
else if(single_round_offset < -180.0f)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ static uint8_t idx; // 全局CAN实例索引,每次有新的模块注册会自
|
||||
static void CANAddFilter(CANInstance *_instance)
|
||||
{
|
||||
CAN_FilterTypeDef can_filter_conf;
|
||||
memset(&can_filter_conf, 0, sizeof(can_filter_conf)); // 防止未初始化字段(如FilterIdHigh等)成为垃圾匹配ID
|
||||
static uint8_t can1_filter_idx = 0, can2_filter_idx = 14; // 0-13给can1用,14-27给can2用
|
||||
|
||||
can_filter_conf.FilterMode = CAN_FILTERMODE_IDLIST; // 使用id list模式,即只有将rxid添加到过滤器中才会接收到,其他报文会被过滤
|
||||
@@ -105,24 +106,18 @@ CANInstance *CANRegister(CAN_Init_Config_s *config)
|
||||
/* 如果让CANinstance保存txbuff,会增加一次复制的开销 */
|
||||
uint8_t CANTransmit(CANInstance *_instance, float timeout)
|
||||
{
|
||||
static uint32_t busy_count;
|
||||
static volatile float wait_time __attribute__((unused)); // for cancel warning
|
||||
float dwt_start = DWT_GetTimeline_ms();
|
||||
while (HAL_CAN_GetTxMailboxesFreeLevel(_instance->can_handle) == 0) // 等待邮箱空闲
|
||||
(void)timeout; // 保留接口兼容性;改为非阻塞发送,避免阻塞调用它的任务
|
||||
|
||||
// 邮箱全满说明上一帧还没发出去,或总线上没有节点回应ACK(例如断开某一路CAN)。
|
||||
// 此时直接放弃本帧,保证电机控制等任务的周期稳定,不因一路CAN离线而拖慢另一路CAN。
|
||||
if (HAL_CAN_GetTxMailboxesFreeLevel(_instance->can_handle) == 0)
|
||||
{
|
||||
if (DWT_GetTimeline_ms() - dwt_start > timeout) // 超时
|
||||
{
|
||||
LOGWARNING("[bsp_can] CAN MAILbox full! failed to add msg to mailbox. Cnt [%d]", busy_count);
|
||||
busy_count++;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
wait_time = DWT_GetTimeline_ms() - dwt_start;
|
||||
|
||||
// tx_mailbox会保存实际填入了这一帧消息的邮箱,但是知道是哪个邮箱发的似乎也没啥用
|
||||
if (HAL_CAN_AddTxMessage(_instance->can_handle, &_instance->txconf, _instance->tx_buff, &_instance->tx_mailbox))
|
||||
if (HAL_CAN_AddTxMessage(_instance->can_handle, &_instance->txconf, _instance->tx_buff, &_instance->tx_mailbox) != HAL_OK)
|
||||
{
|
||||
LOGWARNING("[bsp_can] CAN bus BUS! cnt:%d", busy_count);
|
||||
busy_count++;
|
||||
return 0;
|
||||
}
|
||||
return 1; // 发送成功
|
||||
|
||||
@@ -40,7 +40,7 @@ static void sbus_to_rc(const uint8_t *sbus_buf)
|
||||
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;
|
||||
rc_ctrl[TEMP].rc.rocker_l_= rc_ctrl[TEMP].rc.rocker_l_+5.5;
|
||||
rc_ctrl[TEMP].rc.rocker_l_= rc_ctrl[TEMP].rc.rocker_l_+0.5;
|
||||
// 左侧拨轮
|
||||
RectifyRCjoystick();
|
||||
// 开关,0左1右
|
||||
|
||||
Reference in New Issue
Block a user