最近在学习ISO15765关于OTA升级部分,为了能了解的详细一点,决定动手写一个OTA升级的上位机程序。当然是用C#了,容易上手:)
开始要拿到CAN 盒的驱动程序和开发库(.dll),参考例子程序。例子程序里当然没有OTA升级部分的代码。然后就没有然后了,就是15765的协议了。要是能用到自家产品上,还是要有公司的私有协议,大部分相同。
代码基本流程如下:
//解析XCD文件
XElement root = XElement.Load(@"Data\XXXXXXX.xcd");
SwCode = root.Elements("Configure").Elements("XXXSoftwareCode").FirstOrDefault().Value;
var queryXml = (from typeItem in root.Elements("Data").Elements("XXXBlock") select typeItem.Value).FirstOrDefault();
var xcd_checksum = (from typeItem in root.Elements("Data").Elements("XXXBlock") select typeItem).Attributes("checkSum").FirstOrDefault().Value;
var xcd_test = root.Elements("Configure").Elements("XXXVersion");
var xcd_test_attrib = root.Elements("Configure").Elements("XXXVersion").Attributes("Used");
if (xcd_test_attrib.FirstOrDefault().Value.ToLower() == "true")
{
CanOtaData += "\r\n";
CanOtaData += $"XXX VER: {xcd_test.FirstOrDefault().Value} \r\n";
}
byte[] CanDataFromXCD = strToHexByte(queryXml);
//进入模式会话模式
var SendData = new byte[] { 0x02, 0x10, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
//读软件编码
//读软件编码的首帧
SendData = new byte[] { 0x03, 0x22, 0xf1, 0x94, 0x55, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rSwCodeFirstByte) == false)
{
MessageBox.Show("读软件编码首字节失败!!");
}
//发送流控,读软件编码的第二帧
SendData = new byte[] { 0x30, 0x00, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rSwCodeSencondByte) == false)
{
MessageBox.Show("读软件编码第二个字节失败!!");
}
//启动更新
//进入扩展会话模式
SendData = new byte[] { 0x02, 0x10, 0x83, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(0x7df, SendData, 100);
//禁止DTC设置
SendData = new byte[] { 0x02, 0x85, 0x82, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(0x7df, SendData, 100);
//禁止发送通讯报文
SendData = new byte[] { 0x03, 0x28, 0x81, 0x01, 0x55, 0x55, 0x55, 0x55 };
SendCanData(0x7df, SendData, 100);
//请求种子
SendData = new byte[] { 0x02, 0x27, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR6701) == false)
{
MessageBox.Show("APP--请求种子失败!");
return;
}
//发送秘钥
//SendData = new byte[] { 0x04, 0x27, 0x02, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };//in bootloader
SendData = new byte[] { 0x04, 0x27, 0x02, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };//in application
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR6702) == false)
{
MessageBox.Show("APP--发送秘钥失败!");
return;
}
//进入编程模式
SendData = new byte[] { 0x02, 0x10, 0x02, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR5002) == false)
{
MessageBox.Show("进入编程模式失败!");
return;
}
Thread.Sleep(2000);
//请求种子
SendData = new byte[] { 0x02, 0x27, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR6701) == false)
{
MessageBox.Show("bootloader--请求种子失败!");
return;
}
//发送秘钥
SendData = new byte[] { 0x04, 0x27, 0x02, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };//in bootloader
//SendData = new byte[] { 0x04, 0x27, 0x02, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };//in application
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR6702) == false)
{
MessageBox.Show("bootloader--发送秘钥失败!");
return;
}
//开始擦除
SendData = new byte[] { 0x10, 0x0d, 0x31, 0x01, 0xdf, 0xff, 0x44, 0xXX };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rFlowControl) == false)
{
MessageBox.Show("数据擦除流控帧 失败!");
return;
}
SendData = new byte[] { 0x21, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX };
SendCanData(RequestDeviceID, SendData, 100);
if (RespErase() == false)
{
MessageBox.Show("擦除失败!");
return;
}
//数据下载
SendData = new byte[] { 0x10, 0x0b, 0x34, 0x00, 0x44, 0xXX, 0xXX, 0xXX };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rFlowControl) == false)
{
MessageBox.Show("数据下载 1 流控帧失败!");
return;
}
SendData = new byte[] { 0x21, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR74) == false)
{
MessageBox.Show("数据下载 R74 失败!");
return;
}
int oddlength = CanDataFromXCD.Length % 0xf0;
int index = 0;
for (int i = 0; i < CanDataFromXCD.Length / 0xf0; i++)
{
//取4字节放在首帧
SendData = new byte[] { 0x10, 0xf2, 0x36, 0x01, 0x77, 0x77, 0x77, 0x77 };
SendData[3] = (byte)(i + 1);
Array.Copy(CanDataFromXCD, i * (7 * 34 - 2 + 4), SendData, 4, 4);
SendCanData(RequestDeviceID, SendData, 3);
if (RespFun(respons.rFlowControl) == false)
{
MessageBox.Show($"数据下载首帧 SN:{i * 0xf2} 流控帧失败!");
return;
}
for (int j = 0; j < 34; j++)
{
SendData[0] = (byte)(0x20 + ((j + 1) & 0xf));
if (j == 33)
{
Array.Copy(CanDataFromXCD, 4 + i * (7 * 34 - 2 + 4) + j * 7, SendData, 1, 5);
SendData[6] = 0x55;
SendData[7] = 0x55;
}
else
{
Array.Copy(CanDataFromXCD, 4 + i * (7 * 34 - 2 + 4) + j * 7, SendData, 1, 7);
}
SendCanData(RequestDeviceID, SendData, 3);
}
if (RespR7fR76() == false)
{
break;
}
index = (i + 1) * 0xf0;
Console.WriteLine($"Block {i} finished!");
this.Count = i * 100 / 469;
}
if (oddlength == 0)
{
//数据下载完
goto download_fininished;
}
else
{
//取4字节放在首帧
SendData = new byte[] { 0x10, (byte)(oddlength + 2), 0x36, 0xXX, 0x77, 0x77, 0x77, 0x77 };
Array.Copy(CanDataFromXCD, index, SendData, 4, 4);
index += 4;
SendCanData(RequestDeviceID, SendData, 3);
if (RespFun(respons.rFlowControl) == false)
{
MessageBox.Show($"数据下载首帧 SN 流控帧失败!");
return;
}
if (oddlength <= 4)
{
//数据下载完
goto download_fininished;
}
else
{
int i = 0;
for (i = 0; i < (oddlength - 4) / 7; i++)
{
SendData[0] = (byte)(0x20 + ((i + 1) & 0xf));
Array.Copy(CanDataFromXCD, index, SendData, 1, 7);
SendCanData(RequestDeviceID, SendData, 3);
index += 7;
}
SendData[0] = (byte)(0x20 + ((i + 1) & 0xf));
Array.Copy(CanDataFromXCD, index, SendData, 1, (oddlength - 4) % 7);
SendCanData(RequestDeviceID, SendData, 3);
}
}
download_fininished:
//数据下载完成
SendData = new byte[] { 0x01, 0x37, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR77) == false)
{
MessageBox.Show("数据下载R77失败!");
return;
}
//内存校验
SendData = new byte[] { 0x10, 0x08, 0x31, 0x01, 0xdf, 0xfe, 0xXX, 0xXX };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rFlowControl) == false)
{
MessageBox.Show("内存校验 失败!");
return;
}
SendData = new byte[] { 0x21, 0xXX, 0xXX, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR7101) == false)
{
MessageBox.Show("内存校验失败!");
return;
}
//ECU复位
SendData = new byte[] { 0x02, 0x11, 0x01, 0xdf, 0xfe, 0x55, 0x55, 0x55 };
SendCanData(RequestDeviceID, SendData, 100);
if (RespFun(respons.rR5101) == false)
{
MessageBox.Show("复位 失败!");
return;
}
//恢复发送通讯报文
SendData = new byte[] { 0x03, 0x28, 0x80, 0x01, 0x55, 0x55, 0x55, 0x55 };
SendCanData(0x7df, SendData, 100);
//恢复DTC设置
SendData = new byte[] { 0x02, 0x85, 0x81, 0x55, 0x55, 0x55, 0x55, 0x55 };
SendCanData(0x7df, SendData, 100);
this.Count = 100;