作者:良知猶存
轉載授權以及圍觀:歡迎添加微信: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解析,裏面代碼是實踐過的,還有很多細節因爲篇幅與時間的原因就不多寫了,如果大家有什麼更好的思路,歡迎分享交流哈。
更多分享,掃碼關注我