Can通讯

本文介绍了如何使用C++实现CAN通讯,通过CANManager类封装了CAN卡的初始化、关闭、发送和接收线程。在实际项目中,为了保证性能,发送和接收操作都在单独的线程中处理。文章提供了详细的代码示例,包括CANManager和ControlManager类的实现,以及如何使用ZLG提供的接口进行CAN通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好久没上来了,不久前有一个兄弟问我can通讯实现,这东西用了很久了,正好整理下资料,分享下。其实这个根据开发资料去做个简单demo还是很easy的,不过真实项目中使用,就应该注意处理通信中的性能,发送数据与接受数据都应该在线程中去处理。废话不多说,上干货。

准备:

  1. Can卡硬件有pci接口和usb接口的。pci性能上相对要更强,价格也贵很多,usb的性能上一般也能满足大多数通信需要,而且比较便宜,本例以usb接口为例;
  2. can通信相关资料,就以当前市面上最常见的产品为例,  hZLG致远电子-广州致远电子有限公司这里面有各种型号的硬件以及驱动下载;还有各种实现示例;
  3. 计算机连接can硬件,并安装好驱动;
  4. 所用的dll库为所提供资料中主要是调用ControlCAN.dll中的接口,将ControlCAN.dll和kerneldlls文件夹一起放入到自己工程中;

使用c++实现:

为了提高性能,一定要将can的发送和接受在线程中来实现;

封装can管理类CANManager

CANManager.h

class CANManager

{

private:

    CANManager(void);

public:

    ~CANManager(void);

    bool InitCan();

    bool CloseCan();

    void StartAllThread();

    void CloseAllThread();

    void SetCmdToQueue(VCI_CAN_OBJ* OBJ);

    void ReceiveMsgCan1();//接收CAN1消息

    bool SendCommandToCan1();//发送消息

    static CANManager * getInstance();

    static unsigned __stdcall SendTheardCAN1(LPVOID);

    static unsigned __stdcall RecvTheardCAN1(LPVOID);

private:

    bool m_bCanOpenSuc;

    bool m_bStartThread;

    CRITICAL_SECTION  lockQ1;

    HANDLE m_Rec1Thread;

    HANDLE m_Send1Thread;

    HANDLE m_hEventExitSend1;

    HANDLE m_hEventExitRecv1;

    std::queue<VCI_CAN_OBJ*> m_QSendCAN1; //CAN1发送队列

    ZLG *m_CAN1;

};

CANManager.cpp

CANManager::CANManager(void)

{

    m_bCanOpenSuc = false;

    m_bStartThread = false;

    InitializeCriticalSection(&lockQ1);

}

CANManager::~CANManager(void)

{

}

CANManager * CANManager::getInstance()

{

    static CANManager g_CANManager;

    return &g_CANManager;

}

//初始化CAN

bool CANManager::InitCan()

{

    CAN_PROPERTY_PARAM Init_PAram;//这个结构为什么这么赋值,请看资料,主要是配置can类型、索引、超时、模式、can通道以及波特率;

    Init_PAram.enUSBorPCI = USBCAN;

    Init_PAram.dwCANCardIndex = 0;

    Init_PAram.dwCANCardType = 21;

    Init_PAram.sendTimeout = 100;

    Init_PAram.mode = 0;

    Init_PAram.Baud.nBaudForPCICAN = 0x1C;

Init_PAram.Baud.nBaudForUSBCAN = 0x1C;

    //打开CAN1

    Init_PAram.dwCANChannelIndex = 0;

    m_CAN1 = new ZLG();

    m_CAN1->setCanProperty(Init_PAram);

    if (!m_CAN1->openDevice())

    {

        return false;

    }

    m_bCanOpenSuc = true;

    return true;

}

//关闭CAN

bool CANManager::CloseCan()

{

    if (!m_bCanOpenSuc){

        //already closed

        return true;

    }

    m_bCanOpenSuc = false;

    //关闭CAN

    if (!m_CAN1->closeDevice())

    {

        return false;

    }

    delete m_CAN1;

    m_CAN1 = NULL;

    return true;

}

void CANManager::StartAllThread()

{

    if (m_bCanOpenSuc)

    {

        m_bStartThread = true;

        m_hEventExitSend1 = CreateEvent(NULL, TRUE, TRUE, NULL);

        ResetEvent(m_hEventExitSend1);

        m_Send1Thread = (HANDLE)_beginthreadex(NULL, 0, CANManager::SendTheardCAN1, this, 0, 0);

        m_hEventExitRecv1 = CreateEvent(NULL, TRUE, TRUE, NULL);

        ResetEvent(m_hEventExitRecv1);

        m_Rec1Thread = (HANDLE)_beginthreadex(NULL, 0, CANManager::RecvTheardCAN1, this, 0, 0);

    }

}

void CANManager::CloseAllThread()

{

    if ((!m_bCanOpenSuc) || (!m_bStartThread)) {

        //already closed

        return;

    }

    m_bStartThread = false;

    if (WAIT_OBJECT_0 == WaitForSingleObject(m_hEventExitSend1, 500))

    {

        CloseHandle(m_Send1Thread);

        m_Send1Thread = INVALID_HANDLE_VALUE;

    }

    if (WAIT_OBJECT_0 == WaitForSingleObject(m_hEventExitRecv1, 500))

    {

        CloseHandle(m_Rec1Thread);

        m_Rec1Thread = INVALID_HANDLE_VALUE;

    }

    EnterCriticalSection(&lockQ1);

    while (!m_QSendCAN1.empty()) {

        delete m_QSendCAN1.front();

        m_QSendCAN1.pop();

    }

    LeaveCriticalSection(&lockQ1);

}

void CANManager::SetCmdToQueue(VCI_CAN_OBJ * OBJ)

{

    EnterCriticalSection(&lockQ1);

    m_QSendCAN1.push(OBJ);

    LeaveCriticalSection(&lockQ1);

}

//CAN1===============================================

unsigned __stdcall CANManager::SendTheardCAN1(LPVOID Owner)

{

    ((CANManager*)Owner)->SendCommandToCan1();

    return 0;

}

bool CANManager::SendCommandToCan1()

{

    while (m_bStartThread)

    {

        if (m_QSendCAN1.size() == 0)

        {

            Sleep(1);

        }

        else

        {

            //Sleep(1);

            EnterCriticalSection(&lockQ1);

            m_CAN1->transmit(m_QSendCAN1.front()->ID, m_QSendCAN1.front()->Data);

            delete m_QSendCAN1.front();

### CAN通信协议概述 Controller Area Network (CAN) 是一种广泛应用于汽车和工业控制系统中的串行数据通信协议[^1]。该协议由博世公司开发,旨在支持分布式实时控制应用。CAN 协议的特点在于其高可靠性和抗干扰能力,在恶劣环境下仍能保持稳定的数据传输。 #### 数据帧结构与功能特性 CAN 总线的通信接口集成了 CAN 协议的物理层和数据链路层的功能,能够完成对通信数据的成帧处理,包括但不限于位填充、数据块编码、循环冗余校验(CRC),以及优先级判断等功能[^2]。这些机制共同作用以确保消息传递过程中的准确性与时效性。 ### 车载网络的应用场景 在现代车辆中,多个电子控制单元(ECU)通过 CAN 总线连接在一起形成复杂的车载网络系统。例如,动力传动系管理、车身舒适度调节、安全辅助驾驶等子系统均依赖于这一高效可靠的通信架构来交换信息并协同工作。此外,随着电动汽车的发展,电池管理系统(BMS)也越来越多地采用 CAN 技术实现内部组件间的交互操作。 ### 工业控制领域内的实践案例 除了汽车行业外,CAN 还被广泛应用到各种自动化设备当中,比如机器人手臂运动协调、工厂流水线上不同工位之间的同步运作等等。具体来说,某些情况下会利用 FreeScale 公司生产的 MPC5646C 嵌入式处理器作为网关节点构建起整个 CAN 网络平台,并借助第三方软件工具(如Vector公司的CANoe)来进行仿真测试及性能评估等活动[^3]。 ### 故障诊断方法论 当遇到 CAN 通讯异常时,可以从以下几个角度入手进行排查: - **硬件层面**:检查线路连接是否牢固;确认终端电阻设置正确无误;测量信号电平范围是否符合标准规定。 - **配置参数一致性核查**:对比各 ECU 的波特率设定值是否存在差异;核实 ID 编码规则的一致性。 - **逻辑分析仪抓包检测**:运用专用仪器捕捉实际运行期间产生的报文序列,进而深入剖析潜在问题所在之处。 ```python import can def check_can_bus(): bus = can.interface.Bus(bustype='socketcan', channel='vcan0') message = bus.recv(timeout=0.5) if not isinstance(message, type(None)): print(f"Received Message: {message}") else: print("No messages received.") check_can_bus() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值