野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 42|回复: 1

CAN多机总线通讯

[复制链接]
发表于 昨天 21:54 | 显示全部楼层 |阅读模式
请问一下有人遇到过CAN多设备通讯出现一对一通讯是正常的,一堆多后设备就会导致主机获取不到从机数据,但是从机是可以接收到主机数据的,而且从机给主机响应发送的数据我使用CAN工具是捕获到数据的,就是主机没有方法触发中断也没有办法接收数据。设备工作的逻辑是F407作为主机,每3秒查询一套设备的数据,从机设备每隔10ms发送响应数据,总共回复20条数据。下面是从机的软件,定位到的原因就是从机导致的问题,主机解析其它厂家的标准模块都是正常的。这个坑查了一周都没有找到哪里的问题。

#include "can.h"
#include "can_protocol.h"
#include "delay.h"
#include "timer.h"
#include <string.h>

static volatile bool can_bus_off_flag = false;
static volatile uint8_t can_tx_err = 0;
static volatile uint8_t can_rx_err = 0;
static volatile uint32_t can_bus_off_tick = 0; // 总线离线恢复计时

/**
  * @brief  进入CAN初始化模式
  * @param  无
  * @retval 成功返回true,超时返回false
  */
static bool CAN_Enter_Init_Mode(void)
{
    uint32_t timeout = 1000;
    CAN1->MCR |= CAN_MCR_INRQ;
    while ((CAN1->MSR & CAN_MSR_INAK) == 0)
    {
        if (--timeout == 0) return false;
        Delay_Us(1);
    }
    return true;
}

/**
  * @brief  退出CAN初始化模式
  * @param  无
  * @retval 成功返回true,超时返回false
  */
static bool CAN_Exit_Init_Mode(void)
{
    uint32_t timeout = 1000;
    CAN1->MCR &= ~CAN_MCR_INRQ;
    while ((CAN1->MSR & CAN_MSR_INAK) != 0)
    {
        if (--timeout == 0) return false;
        Delay_Us(1);
    }
    return true;
}

/**
  * @brief  CAN硬件初始化
  * @param  无
  * @retval 无
  */
void CAN_Hardware_Init(void)
{
    CAN_InitTypeDef CAN_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    CAN_FilterInitTypeDef CAN_FilterInitStructure;
    // 1. 时钟使能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
    // 2. GPIO配置 (PB8=CAN_RX 上拉输入, PB9=CAN_TX 复用推挽)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    // 3. CAN1引脚重映射
    GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);
    // 4. CAN控制器复位
    CAN_DeInit(CAN1);
    // 5. CAN参数配置 (125K波特率)
    CAN_StructInit(&CAN_InitStructure);
    CAN_InitStructure.CAN_TTCM = DISABLE;        // 禁用时间触发通信模式
    CAN_InitStructure.CAN_ABOM = ENABLE;         // 自动总线离线管理
    CAN_InitStructure.CAN_AWUM = DISABLE;        // 禁用自动唤醒
    CAN_InitStructure.CAN_NART = DISABLE;        // 使能自动重传
    CAN_InitStructure.CAN_RFLM = DISABLE;        // 禁用接收FIFO锁定
    CAN_InitStructure.CAN_TXFP = DISABLE;        // 禁用发送FIFO优先级
    CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;// 正常模式(从机)
    // 波特率计算: APB1=36MHz / ((1+6+1)*36) = 125K
    CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;     // 同步跳转宽度: 1TQ
    CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;     // 时间段1: 6TQ
    CAN_InitStructure.CAN_BS2 = CAN_BS2_1tq;     // 时间段2: 1TQ
    CAN_InitStructure.CAN_Prescaler = 36;        // 预分频器
    CAN_Init(CAN1, &CAN_InitStructure);

    // 6. 滤波器配置 (接收所有扩展帧)
    CAN_FilterInitStructure.CAN_FilterNumber = 0;
    CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
    CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
    CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
    CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;
    CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
    CAN_FilterInit(&CAN_FilterInitStructure);
    // 7. 中断配置
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 统一中断分组
    // 接收中断 (FIFO0消息挂起)
    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    // 错误中断 (总线错误/离线/警告)
    NVIC_InitStructure.NVIC_IRQChannel = CAN1_SCE_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    // 8. 使能中断
    CAN_ITConfig(CAN1, CAN_IT_FMP0 | CAN_IT_ERR, ENABLE);
    // 9. 初始化状态标志
    can_bus_off_flag = false;
    can_tx_err = 0;
    can_rx_err = 0;
    can_bus_off_tick = 0;
}

/**
  * @brief  发送CAN扩展帧
  * @param  id: 32位扩展帧ID
  * @param  data: 数据指针(空指针时发送空数据)
  * @param  len: 数据长度(0~8)
  * @retval 发送成功返回true,失败返回false
  */
bool CAN_Send_Frame(uint32_t id, const uint8_t *data, uint8_t len)
{
    CanTxMsg TxMessage;
    uint8_t mailbox;
    uint32_t timeout = CAN_TX_TIMEOUT_CNT;
    uint8_t tx_status;
    // 1. 总线离线时直接返回失败
    if (can_bus_off_flag || (CAN1->ESR & CAN_ESR_BOFF))
        return false;
    // 2. 限制数据长度(0~8)
    len = (len > 8) ? 8 : len;
    // 3. 初始化发送结构体
    memset(&TxMessage, 0, sizeof(CanTxMsg));
    TxMessage.StdId = 0;                // 无标准ID
    TxMessage.ExtId = id;               // 扩展ID
    TxMessage.IDE = CAN_Id_Extended;    // 扩展帧模式
    TxMessage.RTR = CAN_RTR_Data;       // 数据帧
    TxMessage.DLC = len;                // 数据长度
    if (data != NULL && len > 0)
        memcpy(TxMessage.Data, data, len);
    // 4. 申请发送邮箱
    mailbox = CAN_Transmit(CAN1, &TxMessage);
    if (mailbox == CAN_TxStatus_NoMailBox)
        return false;
    // 5. 等待发送完成
    while (timeout--)
    {
        tx_status = CAN_TransmitStatus(CAN1, mailbox);
        if (tx_status == CAN_TxStatus_Ok)      // 发送成功
            return true;
        else if (tx_status == CAN_TxStatus_Failed) // 发送失败
        {
            CAN_CancelTransmit(CAN1, mailbox); // 取消发送
            return false;
        }
        Delay_Us(10); // 10µs轮询一次
    }

    // 6. 发送超时: 取消发送并返回失败
    CAN_CancelTransmit(CAN1, mailbox);
    return false;
}

/**
  * @brief  CAN接收中断服务函数 (USB_LP_CAN1_RX0_IRQn)
  * @param  无
  * @retval 无
  */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
    CanRxMsg RxMessage;
    uint32_t err_flag = CAN_GetFlagStatus(CAN1, CAN_FLAG_FOV0);

    // 1. 处理FIFO溢出错误
    if (err_flag != RESET)
    {
        CAN_ClearFlag(CAN1, CAN_FLAG_FOV0);
    }

    // 2. 处理接收FIFO中的所有消息
    while (CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)
    {
        CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
        // 仅处理扩展帧
        if (RxMessage.IDE == CAN_Id_Extended)
        {
                                        //内部软件过滤,不是设备地址直接退出
            CAN_Process_ReceivedMessage(RxMessage.ExtId, RxMessage.Data, RxMessage.DLC);
        }
    }

    // 3. 清除中断挂起位
    CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
}

/**
  * @brief  CAN错误中断服务函数 (CAN1_SCE_IRQn)
  * @param  无
  * @retval 无
  */
void CAN1_SCE_IRQHandler(void)
{
    // 1. 检查错误中断标志
    if (CAN_GetITStatus(CAN1, CAN_IT_ERR) != RESET)
    {
        // 2. 更新错误计数
        can_tx_err = (CAN1->ESR >> 16) & 0xFF; // 发送错误计数(0~255)
        can_rx_err = (CAN1->ESR >> 24) & 0x7F; // 接收错误计数(0~127)

        // 3. 检测总线离线
        if ((CAN1->ESR & CAN_ESR_BOFF) != 0)
        {
            can_bus_off_flag = true;
            can_bus_off_tick = Get_SysTick(); // 记录离线时刻(需实现SysTick获取函数)   
        }

        // 4. 清除错误中断挂起位
        CAN_ClearITPendingBit(CAN1, CAN_IT_ERR);
    }
}

/**
  * @brief  获取CAN总线状态
  * @param  无
  * @retval CAN_Bus_Status
  */
CAN_Bus_Status can_get_state(void)
{
    // 1. 总线离线
    if (CAN1->ESR & CAN_ESR_BOFF)
        return CAN_Bus_Off;

    // 2. 错误被动状态 (TX/RX错误计数≥128)
    if (can_tx_err >= 128 || can_rx_err >= 128)
        return CAN_Error_Passive;

    // 3. 错误警告状态 (TX/RX错误计数≥96)
    if (can_tx_err >= 96 || can_rx_err >= 96)
        return CAN_Error_Warning;

    // 4. 正常状态
    return CAN_Normal;
}

/**
  * @brief  获取最后一次错误码
  * @param  无
  * @retval 错误码 (0~7)
  */
uint8_t can_get_error_status(void)
{
    return CAN_GetLastErrorCode(CAN1);
}

/**
  * @brief  获取接收错误计数
  * @param  无
  * @retval 接收错误计数值(0~127)
  */
uint8_t can_get_rx_err_cnt(void)
{
    return can_rx_err;
}

/**
  * @brief  获取发送错误计数
  * @param  无
  * @retval 发送错误计数值(0~255)
  */
uint8_t can_get_tx_err_cnt(void)
{
    return can_tx_err;
}

/**
  * @brief  CAN总线状态处理与自动恢复
  * @param  无
  * @retval 无
  */
void can_process(void)
{
    // 1. 总线离线且达到恢复延时
    if (can_bus_off_flag && (Get_SysTick() - can_bus_off_tick >= CAN_BUS_RECOVERY_MS))
    {
        // 2. 进入初始化模式
        if (CAN_Enter_Init_Mode())
        {
            // 3. 复位CAN控制器
            CAN_DeInit(CAN1);
            Delay_Ms(10);
            // 4. 重新初始化CAN
            CAN_Hardware_Init();
            // 5. 退出初始化模式
            CAN_Exit_Init_Mode();
            // 6. 清除离线标志
            can_bus_off_flag = false;
        }
        can_bus_off_tick = Get_SysTick(); // 更新恢复计时
    }
}

回复

使用道具 举报

发表于 7 小时前 | 显示全部楼层
软件上没有什么突破口还是只能用示波器看从机到底怎么回的了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2026-4-21 18:23 , Processed in 0.109962 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表