汽车OBD初级开发入门
我所认识的OBD
直观的从名称上来说是英文On-Board Diagnostics的缩写,中文翻译为“车载诊断系统”。书面上的解释就是处理汽车相关故障的诊断系统。对于硬件接口如下图
针对不同车型,汽车上的OBD接口位置会有所不同
而目前对于OBD接口的商用一般是作为GPS tracker即gps定位器,在汽车OBD上主要引出的接口包含了12V的电源、CAN总线、K线,L线等等如下图所示
对于实现与汽车内部诊断系统通信,就是通过这几条总线进行通信。我所理解的OBD从嵌入式的角度来说的话就是:外部器件与汽车内部系统通信的接口。
从何开始学习OBD
当我们认识了OBD相关概念之后,可是对于毫无经验的开发者来说,到底该如何下手,该先学习什么内容。我们要了解汽车内部的数据系统,那必须要学习的就是怎么和汽车进行通信,这时候就很明确了,那就是通过OBD接口的通信总线来打通外部与汽车内部的数据通信,而OBD包含的通信接口包含有CAN总线、K线以及L线,我这里主要是讲解CAN相关应用。
stm32的CAN总线
如果学习过单片机的朋友,谈到CAN总线相比OBD应该会更熟悉一些,对于CAN总线现在都是集成到芯片内部的,比较常用的就是stm32,对于使用者来说,不用太关心CAN总线的电路实现,我们只要能通过配置CAN总线进行通信就行了。在连接CAN总线通信的时候需要注意的是,在通信回路中需要并一个120欧的电阻,至于为什么需要电阻可以参考这篇博文:CAN总线为什么要有两个120Ω的终端电阻。关于CAN总线的理论基础,在网上都有很好的总结大家可以自行查找。我这里直接讨论在STM32中该如何配置CAN总线。首先调试CAN总线,可以先配置成回环模式进行调试,通过是否通信来判断can总线的相关初始化是否正确。对于can的初始化的流程,
1、配置CAN的IO口,在stm32中一般CAN的IO口是复用的,所以这里在配置的时候需要注意一下。
2、打开CAN总线的时钟,同时开始i配置CAN相关的寄存器数据,在这里很重要的一点就是要配置通信模式和波特率,这会关系到CAN的通信。
3、在初始化完之后,还需要配置CAN的滤波器,配置滤波器的目的是因为在汽车通信中,由于部分汽车是没有做网关的,当你通过CAN总线去通信的时候,会返回很多无用的消息,这个时候通过配置滤波器,直接过滤掉无用的,只接收特定的想要的数据。在滤波器中包含16位的滤波器和32位的滤波器配置,这个可以根据需要配置相应的滤波器。
网上有很多相关can初始化的配置,因为我这边使用的是stm32f042的芯片,可供大家参考一下
/****************************************************************************************
**函数信息(information):void CAN_GPIOConfig(void)
**功能描述(description):CAN所用IO引脚配置
**输入参数(Arguments) :None
**输出参数(Returns) :None
**调用提示(reference) :
*****************************************************************************************/
void CAN_GPIOConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE); //使能SYSCFG,
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11 ,GPIO_AF_4);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_4); //复用CAN功能
SYSCFG->CFGR1 |= PA11_PA12_RMP; //配置打开PA11_PA12功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/****************************************************************************************
**函数信息(information):void CAN1_Config32BitFilter(u32 id1, u32 id2, u32 mid1, u32 mid2)
**功能描述(description):CAN扩展帧滤波器设置
**输入参数(Arguments) :id1,id2 校验码 mid1,mid2 屏蔽码
**输出参数(Returns) :None
**调用提示(reference) :
*****************************************************************************************/
void CAN1_Config32BitFilter(u32 id1, u32 id2, u32 mid1, u32 mid2)
{
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = id1>>13;
CAN_FilterInitStructure.CAN_FilterIdLow = (id1<<3)|4;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = mid1>>13;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = (mid1<<3)|4;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
CAN_FilterInitStructure.CAN_FilterNumber = 1;
CAN_FilterInitStructure.CAN_FilterIdHigh = id2>>13;
CAN_FilterInitStructure.CAN_FilterIdLow = (id2<<3)|4;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = mid2>>13;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = (mid2<<3)|4;
CAN_FilterInit(&CAN_FilterInitStructure);
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = CEC_CAN_IRQn; //CAN中断
NVIC_InitStructure.NVIC_IRQChannelPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN, CAN_IT_FMP0, ENABLE);
}
/****************************************************************************************
**函数信息(information):void CAN1_Config16BitFilter(u16 id1, u16 id2, u16 mid1, u16 mid2)
**功能描述(description):CAN标准帧滤波器设置
**输入参数(Arguments) :id1,id2 校验码 mid1,mid2 屏蔽码
**输出参数(Returns) :None
**调用提示(reference) :
*****************************************************************************************/
void CAN1_Config16BitFilter(u16 id1, u16 id2, u16 mid1, u16 mid2)
{
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
CAN_FilterInitStructure.CAN_FilterNumber=1;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_16bit;
CAN_FilterInitStructure.CAN_FilterIdHigh=id1<<5;
CAN_FilterInitStructure.CAN_FilterIdLow=id2<<5;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=mid1<<5;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=mid2<<5;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = CEC_CAN_IRQn; //CAN中断
NVIC_InitStructure.NVIC_IRQChannelPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN, CAN_IT_FMP0, ENABLE);
}
/****************************************************************************************
**函数信息(information):u8* Send_CANFrame(CanTxMsg* TxMessage,u8 CANStype,ErrorStatus* err)
**功能描述(description):发送一帧CAN数据
**输入参数(Arguments) :
**输出参数(Returns) :None
**调用提示(reference) :
*****************************************************************************************/
__IO ErrorStatus RxFlay;
void Send_CANFrame(CanTxMsg* TxMessage,u8 CANStype,ErrorStatus* err)
{
u32 i;
RxFlay = ERROR;
TxMessage->IDE = CANStype;
TxMessage->DLC = 8;
// USARTx_SendMData(&(TxMessage->Data[0]),9);
CAN_Transmit(CAN, TxMessage);
for (i = 0;i < 1500;i++)
{
delay_ms(1);
if (RxFlay == SUCCESS)
break;
}
*err = RxFlay;
}
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 CAN1_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出
CAN_Receive(CAN, CAN_FIFO0, &RxMessage);//读取数据
for(i=0;i<RxMessage.DLC;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;
}
/****************************************************************************************
**函数信息(information):void CEC_CAN_IRQHandler(void)
**功能描述(description):CAN中断接收函数
**输入参数(Arguments) :
**输出参数(Returns) :None
**调用提示(reference) :
*****************************************************************************************/
void CEC_CAN_IRQHandler(void)
{
u8 data[8],i,j;
u32 tmp;
CanRxMsg RbufMege; //CAN receive message
memset(&RbufMege,0,sizeof(CanRxMsg)); //先清空CAN数据接收缓存区
CAN_Receive(CAN, CAN_FIFO0, &RbufMege);
}
需要注意的是如果CAN数据要中断接收的话,需要打开CAN的中断。
OBD的标准协议
要进行OBD数据的通信,那么必须要知道通信的通信协议,简而言之就是要获取数据的数据报文是什么,我发一条报文下去,它会返回相应的数据,我们对数据进行解析就可以获得我们最终的数据。而到底发送什么数据报文通信才会返回相应的数据,这就需要了解OBD协议的,而在OBD的通信协议中包含了不同总线的通信协议,也是世界通用的标准协议。
1、故障码标准ISO-15031。
2、通讯协议标准ISO-941,ISO-14230,ISO-15765,SAEJ1850
我这边主要讨论CAN总线在OBD中的协议ISO-15765。
首先我们必须要熟悉OBD通信的应用层,我们只需要看ISO-15031-5就行了,里面包含了OBD通信中的9种模式,每种模式需要获取的数据不一样如下:
Mode 1: 请求动力系当前数据
Mode 2: 请求冻结祯数据
Mode 3: 请求排放相关的动力系诊断故障码
Mode 4: 清除/复位排放相关的诊断信息
Mode 5: 请求氧传感器监测测试结果
Mode 6: 请求非连续监测系统OBD测试结果
Mode 7: 请求连续监测系统OBD测试结果
Mode 8: 请求控制车载系统,测试或者部件
Mode 9: 读车辆和标定识别号
其次,我们还需要ISO-15765-3里面包含了标准帧和扩展帧的使用方式和具体的数据流。通过这两个的组合我们就可以知道我们要发什么样的数据流去获取数据。
下面具体的数据流是什么,我推荐可以看一下这篇博文:ISO 15765 学习笔记
1.請求支持PID01-20
7DF 02 01 00 00 00 00 00 00------Tools To ECU 01 00:01表示SIDRQ,請求當前動力診斷信息;00表示PID((是否支持PID01-20))
7E8 07 41 00 FF FF FF FF FF------ECU To Tools (ECU #1(ECM)回覆的,41表示SIDPR,如果是表示肯定回答,00表示支持PID01到20;FF爲全支持)
2.請求支持PID21-40
7DF 02 01 20 00 00 00 00 00------Tools To ECU 01 20:01表示SIDRQ,請求當前動力診斷信息;20表示PID((是否支持PID21-40))
7E8 07 41 20 FF FF FF FF FF------ECU To Tools (ECU #1(ECM)回覆的,41表示SIDPR,如果是表示肯定回答,20表示PID((是否支持PID21-40));FF爲全支持)
3.請求支持PID41-60
7DF 02 01 40 00 00 00 00 00------Tools To ECU 01 40:01表示SIDRQ,請求當前動力診斷信息;40表示PID((是否支持PID41-60))
7E8 07 41 40 FF FF FF FF FF------ECU To Tools (ECU #1(ECM)回覆的,41表示SIDPR,如果是表示肯定回答,40表示PID((是否支持PID40-60));FF爲全支持)
4.外部測試設備向汽車請求後續的PID
7DF 02 01 01 00 00 00 00 00------Tools To ECU 01 01:第一個01表示SIDRQ,請求當前動力診斷信息;第二個01表示PID,向外發送DTC和MIL狀態數目。
7E8 07 41 01 33 44 55 66 77------ECU To Tools (// ECU #1(ECM)回覆的。41表示SIDPR;01表示PID;33表示不能發動,燃料系統,全面監控;…)
7DF 02 09 02 00 00 00 00 00------Tools To ECU 09 02:09表示SIDRQ;02表示信息類型:VIN碼
7E8 10 14 49 02 01 31 32 33------ECU To Tools(10:目前不知什麼意思, 14:表示迴應爲20字節 49:表示肯定響應 02:表示信息類型 VIN碼 01:表示第一個響應消息 31 32 33:VIN碼)
7E0 30 00 05 00 00 00 00 00------Tools To ECU (目前不知什麼意思,懷疑是連續包之類的)
7E8 21 34 35 36 37 38 39 41------ECU To Tools (21:目前不知什麼意思, 34~41:VIN碼)
7E8 22 42 43 44 45 46 47 48------ECU To Tools (22:目前不知什麼意思, 42~48: VIN碼;VIN碼全部加起來爲17字節)
知道了具体的数据流后可以根据相应9种模式到车上或者模拟器去实测一下,在国内对于CAN总线的实施是在2009年以后才开始的,所以有些老车不支持CAN总线,这个要去实测一下才知道。