CAN通信入门

上位机实现CAN通信的方法

上位机实现CAN通信通常需要使用CAN控制器或CAN总线适配器等硬件设备,以及相应的CAN通信软件。下面分别介绍上位机实现CAN通信的硬件和软件方法。

 

1. 硬件方法

上位机实现CAN通信需要使用CAN控制器或CAN总线适配器等硬件设备。CAN控制器是一种专门用于实现CAN通信的芯片,具有CAN总线接口和处理器接口等功能。CAN总线适配器是一种将CAN总线信号转换为标准串行接口的设备,可以通过串口通信实现CAN通信。上位机可以通过PCI、USB等接口与CAN控制器或CAN总线适配器连接,实现CAN通信。

2. 软件方法

上位机实现CAN通信需要使用相应的CAN通信软件。CAN通信软件包括驱动程序、通信协议和应用程序等组成部分。驱动程序用于控制CAN控制器或CAN总线适配器的工作,实现CAN通信的底层控制。通信协议用于规定CAN通信的数据格式和传输方式,实现CAN通信的标准化。应用程序用于实现具体的CAN通信功能,如数据采集、控制指令发送等。上位机可以通过安装相应的CAN通信软件,实现CAN通信的功能

 

window:USB2CAN转换器(安装驱动,调用dll,ControlCAN.dll-周立功),以太网转can

C#:CAN通讯上位机的简单示例Ⅰ_c# can通讯-CSDN博客

C#:CAN通讯上位机的简单示例Ⅱ_c# can通信-CSDN博客

以太网转CAN模块-广州致远电子股份有限公司 (zlg.cn)

CAN详解--各家CAN分析仪与软件的比较_pcan和周立功usbcan区别-CSDN博客

linux:使用socketcan(包含arm、树莓派、esp32-micropython 烧写指定固件) netcore支持socketcan

如何使用钡铼技术arm工控机BL302 CAN口发送和接收数据? - 知乎 (zhihu.com) 

linux 中在命令行中使用 SocketCAN(以 PCAN 为例) - 简书 (jianshu.com)

 

can总线发展经历了can 2.0、can fd、can xl,分别于1991年发布can2.0标准(有iso11898和iso11519标准,数据段最多8字节),2015年发布can FD标准(数据段最多64字节),2020年发布can XL标准

特点
can总线采用多主通讯模式,每个can节点都能自主收发数据
有id仲裁机制,id小的优先级高,保证优先级高的数据实时传输

 

 

import can

bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=500000)

bus2 = bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000)

 

https://github.com/derek-will/SocketCANSharp

 

模拟测试

Kvaser CANKing:模拟仿真CAN总线通讯

 

 模拟器配置及发送

 

 虚拟通道号为1

用Python实现can通信

方式1:pip install canlib

Welcome to canlib - a Python wrapper for Kvaser CANlib

文档地址:file:///D:/Program%20Files/Kvaser/Canlib/python/pycanlib/docs/index.html

kvaserapi.py

from canlib import canlib, Frame

def print_frame(frame):
    """Prints a message to screen"""
    if (frame.flags & canlib.canMSG_ERROR_FRAME != 0):
        print("***ERROR FRAME RECEIVED***")
    else:
        print("{id:0>8X}  {dlc}  {data}  {timestamp}".format(
            id = frame.id,
            dlc = frame.dlc,
            data= ' '.join('%02x' % i for i in frame.data),
            timestamp = frame.timestamp
        ))

if __name__ == '__main__':
    channel_number = 1

    chdata = canlib.ChannelData(channel_number)
    print("%d. %s (%s / %s)" % (channel_number, chdata.channel_name,
                                chdata.card_upc_no,
                                chdata.card_serial_no))

    chd = canlib.openChannel(channel_number, canlib.canOPEN_ACCEPT_VIRTUAL)

    print("Setting bitrate to 500 kb/s")
    chd.setBusParams(canlib.canBITRATE_500K)
    chd.busOn()

    print("Sending a message")
    frame = Frame(id_ = 123,
                  data =[1, 2, 3, 4, 5, 6, 7, 8],
                  dlc = 8,
                  flags = 0)
    chd.write(frame)

    finished = False
    print("   ID    DLC DATA                     Timestamp")
    while not finished:
        try:
            frame = chd.read(timeout=1)
            if frame is not None:
                print_frame(frame)
        except(canlib.canNoMsg) as ex:
            pass
        except (canlib.canError) as ex:
            print(ex)
            finished = True

    # Channel teardown
    chd.busOff()
    chd.close()

方式2:pip install python-can

CAN(Controller Area Network)是一种用于汽车和其他设备之间通信的协议。由于CAN协议在不同厂商和应用中有多种实现方式,因此存在多种不同的CAN接口和硬件类型

驱动地址:https://blog.csdn.net/qq_21649903/article/details/115940799

 

pythoncanapi.py


import can

def print_recv(frame):
print("{id:0>8X} {dlc} {data} {timestamp}".format(
id=frame.arbitration_id,
dlc=frame.dlc,
data=' '.join('%02x' % i for i in frame.data),
timestamp=frame.timestamp
))

if __name__ == '__main__':
channel_number = 1 # 通道号为1 CAN 1
#bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=500000)
bus = can.interface.Bus(bustype='kvaser', channel=channel_number, bitrate=500000)

print("Sending a message")
SendID = 1
can_data_list = [1, 2, 3, 0, 0, 0, 0, 0]
# can.message(timestamp=0.0, # 时间戳
# arbitration_id = 0, # 报文id
# is_extended_id = True, # 是否为扩展帧
# is_remote_frame = False, # 是否为远程帧
# is_error_frame = False, # 是否为错误帧
# channel = None, # 通道
# dlc = None, # dlc大小
# data = None, # 数据
# is_fd = False, # 是否为canfd
# is_rx = True, # 是否为接收帧
# bitrate_switch = False, # 比特率开关
# error_state_indicator = False,
# check = False)
msg = can.Message(arbitration_id=SendID, data=can_data_list, is_extended_id=False)
bus.send(msg)

print(bus.state) # ⑦ BusState.ACTIVE

#bus.flush_tx_buffer()
#bus.reset()

finished = False
while not finished:
try:
# ========================
# 只接收自己的信息
recv_id = 1
can_filter = [{"can_id": recv_id, "can_mask": 0xFFFFFFFF}]
bus.set_filters(can_filter)
# ========================
rx_msg = bus.recv(timeout=1)
if rx_msg is not None:
print_recv(rx_msg)
except can.CanError as ex:
print(ex)
finished = True

bus.shutdown()
 

 

用C#实现

using Kvaser.CanLib;
using System.Text;

namespace CanDemo
{
    // https://blog.csdn.net/supposing962464/article/details/124387781
    // D:\Program Files\Kvaser\Canlib\dotnet\x64\netstandard2.0\Kvaser.CanLib.dll
    class KvaserApi
    {
        int hnd = 0;//Kvaser通道句柄
        bool CanState = false;//CAN状态
        Thread readCANThread;   //创建数据监听控制线程
                                //
        public class CanMsg//定义CanMsg包
        {
            public int ID;
            public byte[] Data;
            public string DataType;
            public long TimeStamp;
        }

        public void InitKvaser(int BaudRate)
        {
            int freq = 0;
            if (BaudRate == 50)//波特率50K对应freq为-7
                freq = -7;
            if (BaudRate == 100)//波特率100K对应freq为-5
                freq = -5;
            if (BaudRate == 125)//波特率125K对应freq为-4
                freq = -4;
            if (BaudRate == 250)//波特率250K对应freq为-3
                freq = -3;
            if (BaudRate == 500)//波特率500K对应freq为-2
                freq = -2;
            if (BaudRate == 1000)//波特率1000K对应freq为-1
                freq = -1;
            //创建Kvaser状态句柄
            Canlib.canStatus stat = new Canlib.canStatus();
            //Kvaser软件库初始化
            Canlib.canInitializeLibrary();
            //打开CAN通道;其中第一个参数为通道号,从0开始,如有多CAN卡或多通道,则依次为2、3...;第二个参数为canOPEN_xxx标志,一般默认为0;返回值为通道句柄
            hnd = Canlib.canOpenChannel(0, Canlib.canOPEN_ACCEPT_VIRTUAL);// Canlib.canOPEN_EXCLUSIVE
            //设置CAN参数;第一个参数为CAN通道句柄,第二个参数为波特率对应参数,其他参数默认为0即可;返回值为0则代表设置成功,否则设置失败
            stat = Canlib.canSetBusParams(hnd, freq, 0, 0, 0, 0, 0);
            if (stat == Canlib.canStatus.canOK)//如果打开成功,则CAN状态更新为true
                CanState = true;
            //启动CAN BUS总线
            Canlib.canBusOn(hnd);
            //重置CAN BUS总线
            Canlib.canResetBus(hnd);
            //清空缓存区
            Canlib.canFlushReceiveQueue(hnd);
            if (CanState == false)//如果CAN卡打开失败,则弹出错误提示信息
            {
                Console.WriteLine("CAN启动失败!请连接CAN卡或重新插拔CAN卡!");
                return;
            }

            canWrite(hnd, 111, new byte[] { 1, 1, 1, 1, 1 }, "标准帧");
            //canWrite(hnd, 222, new byte[] { 1, 1, 1, 1, 1 }, "扩展帧");

            //如打开成功,启动CAN信号读取线程
            readCANThread = new Thread(new ThreadStart(DataReadCAN));
            readCANThread.IsBackground = true;
            readCANThread.Start();//启动CAN接收
        }

        public void CloseKvaser()
        {
            Canlib.canBusOff(hnd);//关闭CAN总线
            Canlib.canClose(hnd);//关闭CAN通道
            Canlib.canUnloadLibrary();//卸载软件库
            CanState = false; //CAN状态更新为false
            if (readCANThread != null)
                readCANThread.Abort();//退出监听线程
        }

        //写入CAN数据
        public bool canWrite(int hnd, int ID, byte[] data, string dataType)
        {
            bool writeResult = false;//发送是否成功标识
            Canlib.canStatus stat = Canlib.canStatus.canERR_NOMSG;//Kvaser状态句柄
                                                                  //canWrite输入5个参数。第一个为通道句柄,为canOpenChannel的返回值;第二个参数为CAN报文的ID;第三个参数为报文数据;
                                                                  //第四个参数为报文长度;第五个参数为报文类型(扩展帧或标准帧)
            if (dataType == "标准帧")
                stat = Canlib.canWrite(hnd, ID, data, data.Length, Canlib.canMSG_STD);
            if (dataType == "扩展帧")
                stat = Canlib.canWrite(hnd, ID, data, data.Length, Canlib.canMSG_EXT);
            if (stat == Canlib.canStatus.canOK)//如果写入返回值为canOK,说明写入成功
                writeResult = true;
            return writeResult;
        }

        //读取CAN数据
        public void canRead(int hnd, int filterID)
        { 
            int dlc, flags;//定义数据长度及数据标识
            byte[] msg = new byte[8];//定义数据数组
            int IDReceive = filterID;//定义过滤ID
            long time;//定义时间戳
            Canlib.canStatus stat;//Kvaser状态句柄
                                  //canRead第一个参数为通道句柄,为canOpenChannel的返回值;第二个参数为过滤的ID,如果为-1,则全部接受;否则为待接收的ID;
                                  //第三个参数为报文数据;第四个参数为报文长度;第五个参数为报文类型(扩展帧或标准帧);第六个参数为报文时间戳
            if (filterID == -1)
                stat = Canlib.canRead(hnd, out IDReceive, msg, out dlc, out flags, out time);//如果全部接收,则使用canRead函数
            else
                stat = Canlib.canReadSpecific(hnd, filterID, msg, out dlc, out flags, out time);//如果定义了过滤ID,则使用canReadSpecific函数

            if (stat == Canlib.canStatus.canOK)
            {
                CanMsg canmsg = new CanMsg() { TimeStamp = time };
                canmsg.ID = IDReceive;
                canmsg.Data = msg;

                if (flags == 2)
                    canmsg.DataType = "标准帧";
                if (flags == 4)
                    canmsg.DataType = "扩展帧"; 

                string hex = ToHexString(canmsg.Data, canmsg.Data.Length, true);
                Console.WriteLine($"recv,TimeStamp={canmsg.TimeStamp},DataType={canmsg.DataType},ID={canmsg.ID},Data={hex}");
            }
        }

        private void DataReadCAN()
        {
            while (true)
            {
                //读取CAN报文
                canRead(hnd, -1); 
            }
        }

        public static string ToHexString(byte[] bytes, int length, bool space)
        {
            string strFill = space ? " " : "";
            string hexString = string.Empty;
            if (bytes != null)
            {
                StringBuilder strB = new StringBuilder();

                for (int i = 0; i < length; i++)
                {
                    strB.Append(bytes[i].ToString("X2") + strFill);
                }
                hexString = strB.ToString();
            }

            hexString = hexString.Trim();

            return hexString;
        }
    }
}

 

用qt实现

 

posted @ 2024-02-20 08:55  CHHC  阅读(45)  评论(0编辑  收藏  举报