作者:良知犹存
转载授权以及围观:欢迎添加微信:becom_me
总述
接上篇文章Kwp2000协议的应用(硬件原理使用篇),本篇针对kwp2000协议标准的服务ID详细介绍,以及针对程序实现应答机制,进行介绍。
三、通讯实现过程详解
KWP2000有两种启动方式,5波特率启动和高速启动方式,5波特率基本不使用了,并且我使用过程中是高速初始化的方式,所以下面就按照高速初始化的方式介绍:
在向ECU发送C1 33 F1 81 66 的启动数据之前,还需要进行KWP通信的握手,这时候就需要用到快速初始化,用来告诉ECU有设备准备接入。
Tidle 我在程序实现设置为400ms,>= 300ms
TiniL 我在程序实现设置为25ms, 24~26ms范围内
Twup 我在程序实现设置为50ms,49ms~51ms
注意:由于高速初始化需要对TX引脚进行置高置低,所以IO配置成推挽输出比较好,所以发送数据的时候使用模拟串口配置TX引脚进行数据的发送。
下面代码展示是堵塞执行,其实已经用状态机实现了,类似的延时状态机之前文章已经描述过,因为篇幅的原因,此处就不多做赘述,需要的人可以看我之前的文章。程序堵塞的优化方法(一)解决程序堵塞的优化方法(二)
u8 KlineFastInit(void) { KLin_RX_CLOSE();/*关闭接收引脚*/ K_OUT_HIGH; /*TX引脚输出置高*/ Delay_ms(400); /*保持拉高400ms*/ K_OUT_LOW; /*TX引脚输出拉低*/ Delay_ms(25);/*保持拉低25ms*/ K_OUT_HIGH; /*TX引脚输出置高*/ Delay_ms(25);/*保持拉高25ms*/ KLin_RX_OPEN();/*打开接收引脚*/ Start_CommunicationKLin();/*向K-line发送 C1 33 F1 81 66 进行启动 */ }
Tres 进行发送与接收之间的判断,通过这个时间可以判断ECU的回应情况,而不至于MCU出现长等待情况,如果出现该时间内数据没有回应,MCU端可以进行ECU重新建立通信的初始化过程。
正常情况下一些ECU回复信息 hex:83 F1 11 C1 E9 8F BE
注意了:KB1 KB2 就是我们需要提取的关键词,用来判断ECU是否回复积极响应,不过日本产的三国ECU的关键词是0xE98F,而德尔福和博世ECU的回复关键词是0xEF8F。
BOSH的回复信息 hex:C3 F1 11 C1 EF 8F 04
不过都一样,整条信息最后一位符合累加校验,我们把数据确认是可以用的之后,直接进行关键词判断,对于德尔福、BOSH的接收的信息的不一致,就可以在软件层面忽略,解析判断代码如下:
u8 ReceiveECUStartCommunication(u8 *p, u8 len) { p++;//移动八位 显示第二位数据的地址 if ((BigtoLittle16(*(u16*)(p)) == 0xEF8F)|| (BigtoLittle16(*(u16*)(p)) == 0xE98F))//关键字判断 通过大小端转化函数 { printf("Kline-StartCommunication!\r\n"); } return 0; }
通讯一旦建立之后我们就可以进行对ECU读取想要的数据。比如读或者删除整车故障码,车辆编号,水温,车速,转速,油压等各种车身上提供的传感器数据。.
这是对应的服务ID,通过ID这个关键词我们可以进行读取所需的不同种类的车身信息
例如我要读取车身的故障码,由上图可知故障码属于ID:0x03的服务ID类中
这时候我就向ECU发送数据 hex:C1 33 F1 03 E8
__packed typedef struct{ u8 fmt; u8 tgt; u8 src; u8 sid; }KlineSend; u8 ReadDTC(void) { KlineSend *p = (KlineSend*)malloc(20); p->fmt = 0xC1; p->tgt = 0x33; p->src = 0xF1; p->sid = 0x03; //cheksum = 0xE8 ((u8*)p)[sizeof(KlineSend)] = CheckSum((u8*)p, sizeof(KlineSend)); SendBuf_KLin((u8*)p, sizeof(KlineSend)+1); free(p); return 0; }
假设回来数据为 hex:87 F1 11 43 0562 0000 0000 33
(BOSH ECU 回传数据大致为 hex>:C7 F1 11 43 0118 0107 0000 2D )故障码信息是随意写的
u8 ReceiveECUReadDTCByStatus(u8 *p, u8 len) { u8 lenth = 0; ++p; --len;//刚好的故障码长度 for (u8 i = 0; i<3; i++) { if (*(u16*)&(p[i * 2]) != 0x0000) { lenth += 2;//两个字节为一组 } } for (u8 i = 0; i<lenth / 2; i++) { printf(".DT_%d,0x%04X\r\n",i,BigtoLittle16(*(u16*)&((p[i * 2])))); } return 0; }
解析逻辑执行如下:仅供参考
__packed typedef struct{ u8 fmt; u8 tgt; u8 src; u8 sid; }KlineSecondFORMAT; __packed typedef struct{ u8 sid; u8(*f)(u8*, u8); }KlineAnalyzTypeDef; uint8_t CheckSum(uint8_t* data, uint8_t len)// V { uint8_t i; uint8_t sum; sum = 0; for(i = 0; i < len; i++) { sum += data[i]; } return sum; } KlineAnalyzTypeDef KlineAnalyzTab[] = { { 0xC1, ReceiveECUStartCommunication}, //启动通讯 { 0x43, ReceiveECUReadDTCByStatus }, }; u8 KlineAnalyz(u8 *p,u8 lenth) { KlineSecondFORMAT*p1; p1 = (KlineSecondFORMAT*)p; while(lenth) { u8 len = p1->fmt & 0x3F; if (len != 0) { if (p[len + sizeof(KlineSecondFORMAT)-1] != CheckSum(p, len + sizeof(KlineSecondFORMAT)-1)) return 1;//返回错误 for (int i = 0; i < sizeof(KlineAnalyzTab) / sizeof(KlineAnalyzTypeDef); i++) { if (p1->sid == KlineAnalyzTab[i].sid) { KlineAnalyzTab[i].f(&(p1->sid), len); break; } } lenth = lenth -(len+ sizeof(KlineSecondFORMAT)); p1=(KlineSecondFORMAT*)((u8*)p1+(len+ sizeof(KlineSecondFORMAT))); } return 0; }
这就是我分享的kwp2000解析,里面代码是实践过的,还有很多细节因为篇幅与时间的原因就不多写了,如果大家有什么更好的思路,欢迎分享交流哈