目录

前言

1.报文消息格式介绍

1.1 术语定义

1.2 帧类型

编辑

2.代码格式化处理待发送的消息

2.1 单帧

2.2 首帧

2.3 连续帧


前言

        can总线通讯时,需要按照can报文格式进行消息的发送.发送的消息长度不同,格式会不一样.

下面用代码完成消息转换成报文.

1.报文消息格式介绍

1.1 术语定义

术语简写

术语描述

SI 

 服务标识符

PCI 

 协议控制信息

DID 

 数据标识符

SF 

 单帧

FF 

 首帧

FC 

 流控制

CF 

 连续帧

FF_DL 

 首帧字节长度

SF_DL 

 单帧字节长度

BS

 块大小

STmin

 时间间隙

SN 

 连续帧编号

NRC 

 消极应答码

DTC 

 故障码

PH 

 物理层

PDU

 协议数据单元

1.2 帧类型

2.代码格式化处理待发送的消息

2.1 单帧

发送的消息不超过7个字节.

# 单帧-SingleFrame
def func_sf_format(data):
    """
    发送数据前,添加一个字节的帧类型+数据长度
    data_new = 0{数据长度}{data_raw}
    :param data: str    '500300' -> 0{3}{500300}     500300为三个字节
    :return:
    """
    # 首字节 高4bit值 0000 是SingleFrame标志 低4bit值为DataLength
    _data_length = int(len(data) / 2)
    _data = f"0{hex(_data_length).replace('0x', '')}{data}"
    return _data

2.2 首帧

发送的消息不超过6个字节.

def get_ff_format(data):
    """

    :param data: str
    :return:
    """
    _data_length = int(len(data) / 2)
    # 数据长度 已包含 (传送数据服务请求id)
    _ff_data = f"10{hex(_data_length).replace('0x', '').zfill(2)}{data}"
    return _ff_data

2.3 连续帧

(['10', '82', '36', '01', '18', '08', '00', '20'], 'FF', '30')
 [['21', '9D', '41', '00', '00', '8D', '50', '00'], 'SN', None]
 [['22', '00', '79', '50', '00', '00', '00', '00'], 'SN', None]
 [['23', '00', '00', '00', '00', '00', '00', '00'], 'SN', None]

当发送的消息超过6个字节时,需要用连续帧的报文格式进行发送.

注意:

2.3.1 发送多帧数据时, 报文消息体包含两类数据帧: 首帧 + 连续帧

首帧构成 数据帧类型 + 数据长度 + 服务ID + 数据块大编号(01-FF) + 消息体

连续帧构成 数据帧类型  + 数据块小编号(0-F) + 消息体

def func_mf_format(data, id):
      """
        
        :param data: str 
        :return:
        """
    data_list = re.findall(".{2}", data)
    data_group = []
    group_num = 0
    temp = []

    def get_ff_data(_data_list, _group_sn):
        if id == "36":
            # 数据长度 + 2 (传送数据服务请求id 占一个字节 + 数据块编号 占一个字节)
            _ff_data = ["10", f"{hex(len(_data_list) + 2).replace('0x', '').zfill(2)}", id, _group_sn]
        else:
            # 数据长度 已包含 (传送数据服务请求id)
            _ff_data = ["10", f"{hex(len(_data_list)).replace('0x', '').zfill(2)}"]
        # 首帧的信息由 数据帧类型+数据长度+id + (循环的次数) 剩余位置 用有效数据补
        rest_length = 8 - len(_ff_data)
        _ff_data.extend(_data_list[:rest_length])
        return (_ff_data, "FF", "30"), _data_list[rest_length:]

    def get_sn_data(_data_list, _group_sn, is_check=True):
        """
        在首帧 占用了序号0 之后,连续帧就从 1开始到 F
        :param _data_list: list 有效数据 ['00', '01', '02']
        :return:
        """
        sn_num = 1
        _sn_data_list = []
        step = 7
        for i in range(0, len(_data_list), step):
            sn = f"2{hex(sn_num).replace('0x', '')}"
            _cf_data = _data_list[i:i + step]
            _cf_data.insert(0, sn)
            # sn 最大为 f,
            _sn_data_list.append([_cf_data, "SN", None])
            if sn_num >= 15:
                sn_num = 0
            else:
                sn_num += 1
        if is_check:
            _sn_data_list[-1][-1] = "76"
        return _sn_data_list

    def _set_command(data_list, _group_sn):
        loop_data_list = []
        # 构造首帧
        ff_data, rest_data = get_ff_data(data_list, _group_sn)
        loop_data_list.append(ff_data)
        # 构造连续帧  (剩余有效数据,构造成 连续帧
        sn_data_list = get_sn_data(rest_data, _group_sn)
        loop_data_list.extend(sn_data_list)
        return loop_data_list

    command_big_list = []
    # 每128个字节(有效数据)为一组 一个小循环
    # 一个小循环分 首帧 6个 其余 122个,7个为一组, 分成9组, 不足一组用 55 补齐
    group_step = 128
    _group_num = 1
    for j in range(0, len(data_list), group_step):
        _group_sn = f"{hex(_group_num).replace('0x', '').zfill(2)}"
        temp = data_list[j:j + group_step]
        if len(temp) >= 7:
            command_list = _set_command(temp, _group_sn)
        else:
            # 少于7个 发单帧   长度增加2   服务id + 编号
            base_temp = [f"0{hex(len(temp) + 2).replace('0x', '')}", "36", _group_sn]
            base_temp.extend(temp)
            command_list = [(base_temp, "SF", "76")]
        command_big_list.extend(command_list)
        _group_num += 1
        # 分组累加到 256(FF) 时,从0开始
        if _group_num == 255:
            _group_num = 0
    return command_big_list