概述:
Adaptive AUTOSAR中,标准并不支持XCP的实现,但可以通过改造APP的方式实现XCP,进行对APP内全局变量的在线标定和测量。
能达到的效果:
每个APP都作为一个单独的XCP Slave
可以对拥有静态地址的对象进行测量和标定
XCP可以集成进AP中
AP平台上存储方式和传统嵌入式ECU有很大不同,都是使用文件或Key-Value方式存储标定数据,所以XCP更多的侧重于运行时测量和在线标定
原理:
XCP在传统ECU上使用很多,是通过对地址的读写进行测量和在线标定的协议,但在AP平台中,一是规范没有定义XCP模块和上位机进行XCP通信,二是AP的进程地址空间都是隔离的,APP内变量的地址都是虚拟地址,每次加载进程都不一样,需要在运行时确认测量对象的地址。
因此想要使用XCP,需解决两个问题:
如何使ECU和XCP上位机工具建立XCP通信
如何确认变量运行时的地址
如何使ECU和上位机建立XCP通信
由于XCP天生支持基于以太网(UDP/TCP)的通信方式,所以可以利用原始socket的方式和上位机进行通信,这需要每个想要进行标定和测量的APP都实现XCP协议栈,作为一个XCP Slave,如下图所示
如何确定变量的运行时地址
另一个难点是地址的问题,XCP进行变量的读和写都是基于地址的,但AP中的程序都是进程间隔离的,运行时都在虚拟地址上,每次运行都不一样,需要一些额外的手段使XCP能访问到正确的变量
虽然POSIX环境下,进程加载后,变量的绝对地址是不同的,但偏移量是固定的,所以在AP中使用XCP,只需要根据偏移量来计算当前变量地址即可
在AP中使用XCP,为了解决每次进程加载的地址不一样,需要确定一个基准地址,APP根据基准地址和偏移量计算变量当前的地址,在XCP发送命令读或写时,对该地址进行操作
实现
XCP Slave库的实现
TODO
变量地址确认-基地址法
如原理所述,为了获取变量的地址,需要先确定一个基地址,上位机在XCP命令中设置的地址是变量地址和基地址的offset值
基地址必须小于所有变量地址
最简单的实现:
采用静态库的方式实现XCP Slave,在XCP Slave里获得main函数的地址作为基地址,正常声明变量,在编译时加入编译选项生成map文件,用于将变量和main函数之间的offset算出来
gcc可以采用如下命令生成map文件
g++ -o test main.cpp -Wl,-Map,test.map
gcc生成的map文件如下
假如要标定sfd_a这个变量,要算出sfd_a地址0x201010和main函数地址0x939之间的offset 为0x2006d7,在XCP发送命令时,将0x2006d7传给XCP Slave,XCP Slave再将offset和当前运行时的main函数地址相加,得到对应的当前sfd_a地址
效果演示:每次运行时main函数地址和sfd_a地址都会变,但它们之间的offset不变
变量地址确认-指针法
基本原理是,将想要标定的变量,用特殊的声明方式声明,或运行时添加进某个数组,XCP Slave将变量和序号记录下来,标定时上位机直接传序号,XCP Slave根据序号获取对应的运行时变量地址
编译时声明
#include <iostream>
#define MERGE_BODY(x,y) x ## y
#define MERGE(x,y) MERGE_BODY(x,y)
#define DECLARE_XCP_VAR(TYPE,NAME) \
TYPE NAME; \
void * MERGE(XCP_PTR_,__COUNTER__) = reinterpret_cast<void *>(&NAME);
DECLARE_XCP_VAR(int , test_a);
DECLARE_XCP_VAR(int , test_b);
#define GET_XCP_PTR(index) MERGE(XCP_PTR_,index)
#include <cstdio>
int main() {
std::cout << "Hello, World!" << std::endl;
auto p = GET_XCP_PTR(0);
printf("Xcp pointer 0 addr is %p\n",p);
printf("Test a addr is %p\n",&test_a);
return 0;
}
运行时添加
总结
在AP平台中使用XCP的好处:
- 可重用XCP协议,XCP已在汽车界使用很多年,可以实现APP内部的变量监测