基于CAN总线的汽车诊断协议UDS (网络层 ISO 15765)_15765协议处于can协议哪一层

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

上个月一个同事Z跳槽去了德赛西威,Z之前是完全不懂诊断的MCU工程师,去德赛后做诊断开发,让我感觉到,汽车嵌入式行业,CAN和诊断工程师还是比较稀缺的。之前我和Z共同负责一个项目,我负责CAN网络和诊断部分,经过4个多月的奋战,我一个人把汽车诊断UDS的系统搭建出来,自认为,完成度很高,代码质量也极好。他跳槽去德赛做诊断开发,我想多少有点受益于我开发的诊断代码,另外我也悉心指导他,讲解相关的知识,他确实也学到不少,即便是现在,他有问题也会打电话向我求助。

前两年的工作和学习,我了解到汽车CAN网络和诊断还是比较难以学习的,网上资料参差不齐,我花了很大功夫才把这部分掌握,所以考虑写几篇相关的文章,以帮助后来者。

网络层的国际标准是ISO 15756-2,该标准详细规定了协议的具体细节。CAN总线是一帧8个字节,该协议可以使CAN总线高效的传输大约8个字节(up to 4095 bytes)的命令和数据。基于该标准文档,我开发出了一个独立性良好的协议栈,工作在上层诊断协议之下和下层CAN驱动之上,下面详解开发协议栈时需要实现的部分(基于 ISO 15765-2:2004(E))

4 Network layer overview

4.2 Services provided by network layer to higher layers

4.2小节是描述网络层协议提供给上层的服务

(a) Communication services  (通信服务)

有四个,其中第1个是发送消息的服务,我实现为一个外部函数,提供给上层调用,第2,3,4是上层获取协议栈发送和接收状态的服务,我按照回调函数的方式实现,于是变成了上层提供给网络层的接口。如果转成C++代码,可以用虚函数来实现。

1) N_USData.request

是网络层提供给上层的发送消息的服务,5.2.1小节对其有详细的描述,我只实现了两个参数,msg_buf和msg_dlc,发送时根据消息长度判断是单帧发送还是多帧发送,

extern void network_send_udsmsg (uint8_t msg_buf[],uint16_t msg_dlc)  
{

  if (msg_dlc==0|| msg_dlc> UDS_FF_DL_MAX)return;

  if (msg_dlc<= UDS_SF_DL_MAX)
  {
    send_singleframe (msg_buf, msg_dlc);
  }
  else
  {
    nwl_st = NWL_XMIT;
    send_multipleframe (msg_buf, msg_dlc);
  }
}

2)N_USData_FF.indication

该服务用来通知上层,网络层收到了首帧,5.2.3小节对其有详细的描述,我实现了一个参数msg_dlc,该函数通过回调实现,具体细节在上层代码中,按下不表。

函数原型声明如下

typedef void (*ffindication_func) (uint16_t msg_dlc);

网络层接收到首帧后调用该服务。

3)N_USData.indication

该服务把接收到的完整消息传递给上层,5.2.4小节对其有详细的描述,我实现了3个参数,msg_buf,msg_dlc和n_result,该函数通过回调实现,具体细节在上层代码中,按下不表。

函数原型声明如下

typedef void (*indication_func) (uint8_t msg_buf[], uint16_t msg_dlc, n_result_t n_result);

该函数调用较多:

1.接收到单帧,with N_OK

2.接收连续帧,如果sn错误,with N_WRONG_SN

3.接收连续帧,如果长度正确,with N_OK

4.网络层主循环中,如果CR定时器超时,with N_TIMEOUT_Cr

5.接收到首帧和单帧,如果网络层状态异常,with N_UNEXP_PDU

4)N_USData.confirm

该服务用来通知上层,消息发送已经完成,并返回成功与否,5.2.2小节对其有详细的描述。我实现了1个参数n_result,该函数通过回调实现。具体细节在上层代码中,按下不表。

函数原型声明如下

typedef void(*confirm_func)(n_result_t n_result);

该函数调用如下:

1.接受到流控帧,如果流状态>= FS_RESERVED, with N_INVALID_FS

2.接收到流控帧,如果流状态== FS_OVERFLOW, with N_BUFFER_OVFLW

3.网络层主循环中,如果BS定时器超时,with N_TIMEOUT_Bs

b) Protocol parameter setting services (协议参数控制服务)

协议参数控制服务有两个,我没有实现,具体用处我还不明白,但是不影响实现协议栈功能。

6 Network layer protocol

第6节描述网络层协议内容

6.1-6.4小节简要说明

当消息长度小于等于6(扩展地址和混合地址)或者7(普通地址)个字节时,是通过一个N_PDU(数据单元)发送完成,叫做SF(单帧)。

当消息长度较大时,是通过多个N_PDUs(数据单元)发送完成,这种数据单元叫做FF(首帧,第一个N_PDU)和CF(连续帧,后续的N_PDUs)。

FF(首帧)包括前面5个(扩展地址和混合地址)或者6个(普通地址)字节的内容,1个或者多个CF(连续帧),每个CF包括后续的6个(扩展地址和混合地址)或者7个(普通地址)字节的内容,当然也可以少于6个或者7个字节。消息长度信息在FF(首帧)中发送,所有的CF(连续帧)在发送端被编号,以帮助接收者按顺序重组

消息。(最后一句话没什么卵用)

接收者通过Flow control(流控帧)的机制,告知发送者自己有多大的接收能力。(其实就是每两个FC之间允许连续发送多少个CF,每两个CF之间的时间不能过快)

Flow control 包含三个字段:

Flow status(FS),流状态,用来控制发送方接下来的行为,总共有三个定义,分别是FC.CTS(继续发送),FC.WAIT(继续等待),FC_OVFLW(缓存溢出,此时应该终止发送)。

Block Size (BS),每次收到流控帧之后,发送者最大可发送的连续帧的个数。

SeparationTimeMin (STmin),两个连续帧之间的最小间隔。

综上所述,网络层共有4中数据单元类型:SF N_PDU,FF N_PDU, CF N_PDU, FC N_PDU。详细说明在6.4节,不再赘述。

Tale 2 是N_PDU format (数据单元格式),每个N_PDU由三个域组成。

在使用普通地址时,地址域仅由CAN ID组成,CAN消息数据的第一个字节(或前两字节)为N_PCI Bytes。N_PCI(Protocol control information)标识了一条消息的类型和附加信息。

6.5 Protocol control information specification

Table 3描述各种类型的N_PDU 的N_PCI bytes的定义。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

N_PCI byte的第一个字节的高4位为N_PCItype,标识该N_PDU(数据单元)的类型。

0,SF(单帧)

1,FF(首帧)

2,CF(连续帧)

3,FC(流控帧)

4-F,保留定义

我在程序中接收到一条诊断报文后,通过一条宏定义获取N_PCItype

#define NT_GET_PCI_TYPE(n_pci) (n_pci>>4)

pci_type = NT_GET_PCI_TYPE (frame_buf[0]);

然后根据pci_type进行不同的处理。

(1)单帧的情况下,N_PCI byte第一个字节的低4位为SF_DL(消息长度),范围在1-6(扩展地址和混合地址)或者1-7(普通地址)之间,如果SF_DL错误,网络层应该忽略这条N_PDU

(2)首帧的情况下,N_PCI bytes 第一个字节的低4位和第二个字节共同组成FF_DL(消息长度),范围在8-FFF(扩展地址和混合地址)或者7-FFF(普通地址)之间,如果FF_DL大于接收者的接收缓存,网络层应该丢弃这条消息,并且发送FC with  FlowStatus = Overflow

(3)连续帧情况下,N_PCI byte第一个字节的低4位为SN(SequenceNumber),

在每开始发送一段数据的时候SN必须从零开始,FF(首帧)没有SN字段,但应该被认为是SN = 0,

FF之后的第一个CF的SN应该为1,

每发送一个新的CF,SN都应该增加1,

CF的值不应该受FC的影响,

当SN的值达到15的时候,下次发送的CF,SN应被重置为0,

如果SN出错,网络层应该丢弃已接收到的消息,并且调用N_USData.indication服务,with N_WRONG_SN

(4)流控帧情况下,

N_PCI bytes第一个字节的低4位为FS(Flow status),FS有4个定义,

0, CTS     ,代表发送者可以正常发送

1, WT       ,代表发送者应该再等待下一个FC,并且重启N_BS timer

2, OVFLW,代表接收方缓存溢出,发送方收到此FS后,应该终止发送,调用N_USData.confirm 服务,with N_BUFFER_OVFLW

3-F, Reserved

如果发送者收到的FS出错,网络层应该停止消息发送,并且调用 N_USData.confirm 服务,with  N_INVALID_FS

N_PCI bytes的第2个字节为BS(BlockSize),BS代表发送方在收到下一个FC之前,应发送的CF的数量,只有最后一块数据,其CF的数量可以少于BS,BS的值分两个情况,

0,      代表没有BS限制,发送方不必等待FC,把所有的FC一次发送。

1-FF,代表发送方发送BS数量的CF后,需等待FC,

N_PCI bytes的第3个字节为STmin,发送方收到FC后,应该把STmin保存下来,该值表明两个CF之间的最小间隔,STmin的值定义如下图

如果发送方收到一个FC,其STmin的值是Reserved,则发送方应默认STmin为7F(127ms)

STmin参数体现在程序中就是一个定时器,发送完一帧CF后,应该立即启动STmin timer

timer超时之后才能发送下一个CF,我的实现方式如下,nt_timer_run(TIMER_STmin) < 0 代表STmin timer超时。

    if (nt_timer_run (TIMER_STmin) < 0)
    {
        g_xcf_sn++;
        if (g_xcf_sn > 0x0f)
            g_xcf_sn = 0;
        OSMutexPend(UdsMutex,0,&err);
        send_len = send_consecutiveframe (&remain_buf[remain_pos], remain_len, g_xcf_sn);
        remain_pos += send_len;
        remain_len -= send_len;

        if (remain_len > 0)
        {
            if (g_rfc_bs > 0)
            {
                g_xcf_bc++;
                if (g_xcf_bc < g_rfc_bs)
                {
                    nt_timer_start (TIMER_STmin);
                }
                else
                {
                    /**
                     * start N_Bs and wait for a fc.
                     */
                    g_wait_fc = TRUE;
                    nt_timer_start (TIMER_N_BS);
                }
            }
            else
            {
                nt_timer_start (TIMER_STmin);
            }
        }
        else
        {
            clear_network ();
        }
        OSMutexPost(UdsMutex);
    }
6.6 Maximum number of FC.Wait frame transmissions (N_WFTmax)

6.6节,最大FC.Wait次数,是本地(local)的参数,不包含在FC中,

指明接收方最大能连续发送多少个FC.Wait,

这个上限参数应该在系统规划的时候由用户定义,

该参数只在接收消息的时候使用,

该参数如果为0,则接收方应该禁用FC.Wait,即不发送FS = WT的流控帧。

我实现的时候,默认了该参数为0,实际是根本没定义该参数,也不使用FC = WT的流控帧,

6.7 Network layer timing
6.7.1 Timing parameters

Table 16 定义了网络层的时间参数值,以及各个时间参数的开始和结束点,这些体现通信性能的值,通信双方都应该满足,每个程序都可以定义具体的值,但是要在Table 16的范围内。(实际上,车厂会给一个文档,叫做诊断规范,会规定这些参数的值)

通常,将超时值定义为高于性能要求的值,以确保系统能在特殊情况下工作。指定的超时值应被视为任何给定实现的下限。真正的超时值应不晚于指定的超时值 + 50%。(这是一堆废话,按照车厂的诊断规范确定超时值)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

13904678?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjgwODY2Mzc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)

[外链图片转存中…(img-PDUFgXoO-1715714093168)]
[外链图片转存中…(img-z5CyrRTx-1715714093170)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值