目录
LIN的API
本章介绍 LIN
的
API
的概念、功能和一般用法,并以例子的形式介绍了调用
API
的一般流程。本章内容对应 LIN
规范的以下部分:
● LIN Application Program Interface Specification
1、什么是API
API(Application Program Interface) 是一组“
规约
”
,用来定义软件模块的使用方法。
API
既可以是数据结构,也可以是若干个函数,还可以是它们的混合。
软件开发者可以把 API
看作是与软件模块的会话方式。应用程序和程序员既可以使用该模块的功能,又无需访问其源代码,或者理解其内部工作机制的细节。
API 对软件开发意义重大。软件规模日益庞大,常常需要把复杂系统划分成小的组成部分,或者重复使用代码,这时都会涉及到 API
。
2、LIN的API
LIN 规范用
C
语言定义了
LIN
的
API
,但未定义
API
的内部实现。
LIN 协会规定:对于采用 LIN 规范 2.x 版的 LIN 节点,如果用 C 语言开发应用程序,那么就必须使用 API,对采用 LIN 规范 1.x 版的 LIN 节点,可以不使用标准规定的 API。
按照用途,可以把 LIN
的
API
分为
3
类
——
核心 API、传输层 API 和配置与识别 API
。三类
API
相对独立,彼此关联,如图 6.1
所示。
3、核心API
核心 API
是
API
的基础,除了完成协议层的帧收发,
LIN
应用层各项功能都要用到核心
API
。
核心 API
包含多个函数,其中,
l_sch_tick()(
时基节拍管理
)
和
l_sch_set()(
进度表管理
)
是与进度表相关的两个函数。其他的函数负责控制各种硬件协调工作,完成初始化、中断响应、比特流收发、字节缓冲、休眠、唤醒以及物理层的差错报告等功能。
LIN 规范把核心
API
分成
6
个组,如下表
所示。
4、传输层API
从
LIN
规范
2.0
版开始,增加了传输层
API
。
传输层 API
是为配置、识别和诊断这三项服务设置的,是应用层与协议层的接口。
传输层 API 的功能包括:建立并管理 PDU 队列、收发 PDU 以及检查 PDU 的通信状态。
传输层
API
接收应用层消息,调用核心
API
发送主机请求帧;收到从机应答帧时,传输层剥离协议层的帧头信息获得 PDU
,送往应用层处理。
对于识别或配置服务,因为用于识别和配置的诊断帧已经预先安排在进度表内,所以传输层 API 只是在有关帧时隙到来时才工作,不影响核心 API
的进度表调度,不影响
LIN
的确定性。对于诊断服务,因为诊断请求通常来自诊断仪表或者上级网络(
例如
CAN)
,发生时机和频次不可预测,所以传输层
API
要能动态地产生诊断帧,并将诊断帧插入到当前的进度表里,这会影响 LIN
的确定性。
LIN 规范定义了两种传输层 API——Raw API 和 Cooked API
,二者功能一致,区别在于对应用层消息的处理方式。两种 API
不建议混用,用户宜根据需要选用一种。
如果节点需要监视通信细节,那么应该用 Raw API,它允许节点以 PDU 为单位处理信息。如果节点只需要转发消息而不需要关心消息内容,那么适合使用 Cooked API,它允许节点以消息为单位处理信息。
传输层
API
如表
6.2
所示。

5、配置与识别API
从LIN
规范
2.0
版开始,增加了配置与识别
API
。用于支持应用层的配置功能和识别功能。
配置与识别
API
如表
6.3
所示。

注:1. 仅适用于主机节点。
2. 仅适用于从机节点。
配置 API
除了实现具体的服务项目,还可以向应用程序报告服务的执行情况。
识别 API
包含两个函数:
ld_read_by_id
和
ld_read_by_id_callout
。
ld_read_by_id 仅供主机节点使用,主机节点只能通过它获得从机节点的硬件信息,例如产品代号等。
ld_read_by_id_callout 仅供从机节点使用,这是一 个从 API
向节点应用程序的逆向调用,它并不是必须的,专门用于实现用户自定义的服务。
6、注意事项
6.1、兼容性
API 的兼容性体现在两个方面,一是不同版本
API
之间的兼容性,二是
API
对帧收发硬件的兼容性。
API
版本之间的兼容性如表
6.4
所示。

不同的硬件需要使用不同的 API。LIN API 的实现通常都是与帧收发硬件密切相关的,不能简单挪用。
6.2、开发工具
目前已经有一些商品化的 LIN
开发工具。要开发
LIN
的应用,商品化的开发工具并不是必须的,不过,此类工具确实能提高开发效率,尤其是处理那些同时容纳不同 LIN
规范版本的节点的网络。
图 6.2
显示了此类开发工具的工作原理。
API
的实现可以是独立的若干个库文件,也可能包含在某种开发工具之中。API
通常不能直接被调用,需要配合若干外部附属模块
(
例如驱动函数
)
以及映射文件
(
例如用宏定义实现节点端口与 API
库文件之间的衔接
)
。库文件与附属模块、映射文件一起,在编译阶段添加到用户代码中
。
7、API使用示例
参考资料[11]
给出了一个
LIN 2.0
版的
API
的示例,从中可以看出
API
的调用次序。该示例包括两部分:在从机节点初始化阶段需要执行的 API,以及在从机节点应用程序中调用
API
的方法。
7.1从机节点初始化
extern unsigned char lin_SomeCotrol_init( void );
void PowerON_Reset(void)
{
HardwareSetup();/* 系统初始化 */
if( l_sys_init() )
{
/* LIN API 初始化失败 */
sleep();
}
else
{
if( lin_SomeCotrol_init() ) {
/* LIN 相关的模块初始化失败,例如传感器、执行器 */
sleep();
}
}
/* 其他系统要求的功能 */
main();
return;
}
/* 帧收发硬件的驱动程序入口 */
const T_Lib_Slave_Handle Slave_handle = {
Lin_Drv_Init,
Lin_Drv_HeaderIn,
Lin_Drv_Pid_RecvReq,
Lin_Drv_SendData,
Lin_Drv_RecvData,
Lin_Drv_SendRecvFinish,
Lin_Drv_LinBus_Enable,
Lin_Drv_LinBus_Disable,
Lin_Drv_WakeUp
};
/* LIN 网络初始化 */
unsigned char lin_SomeCotrol_init( void )
{
unsigned char rtn;
rtn = 0;
if( l_ifc_ioctl( 0, LIN_ENTRY_SLAVE_DRV, &Slave_handle ) )
{
/* 帧收发硬件的驱动程序初始化失败 */
rtn = 1u;
}
else
{
l_ifc_init(0); /* LIN 端口初始化 */
if( l_ifc_connect(0) )
{
/* LIN 端口初始化失败 */
rtn = 1u;
}
else
{
/* 其他必要的操作 */
}
}
return rtn;
}
7.2、从机节点主程序
#include "sfr_r825.h"
#include "Lin_DrvR8C.h"
#include "lin20.h"
void lin_application( void );
/***************************/
/* Main Function */
/***************************/
void main(void)
{
while( 1 )
{
/* ......Something to do */
lin_application();
/* ......Something to do */
}
}
/*******************************/
/* LIN Application Function */
/*******************************/
extern l_flg Lin_Frm_FrameMst0_flg;
extern l_flg Lin_Frm_FrameU1_flg;
extern l_flg Lin_Frm_FrameU2_flg;
extern l_flg Lin_Frm_FrameU3_flg;
extern l_flg Lin_Frm_FrameEve0_flg;
extern l_flg Lin_Frm_FrameSlv0_flg;
extern l_flg Lin_Sig_Command_flg;
extern T_Signal Lin_Sig_Status_Slv0;
extern T_Signal Lin_Sig_Status_Slv1;
extern T_Signal Lin_Sig_Command;
void lin_application( void )
{
l_u8 data[8];
l_u16 status;
/* 判断:是否收到了新的帧? */
if( 0 != l_flg_tst(&Lin_Frm_FrameU1_flg) )
{
l_flg_clr( &Lin_Frm_FrameU1_flg );
/* 根据收到的帧执行相应的操作 */
}
else if( 0 != l_flg_tst(&Lin_Frm_FrameMst0_flg) )
{
l_flg_clr( &Lin_Frm_FrameMst0_flg );
/* 根据收到的帧执行相应的操作 */
}
/* 判断:帧是否已经发出? */
if( 0 != l_flg_tst(&Lin_Frm_FrameU2_flg) )
{
l_flg_clr( &Lin_Frm_FrameU2_flg );
/* 执行发送结束之后的操作 */
}
else if( 0 != l_flg_tst(&Lin_Frm_FrameU3_flg) )
{
l_flg_clr( &Lin_Frm_FrameU3_flg );
/* 执行发送结束之后的操作 */
else if( 0 != l_flg_tst(&Lin_Frm_FrameEve0_flg) )
{
l_flg_clr( &Lin_Frm_FrameEve0_flg );
/* 执行发送结束之后的操作 */
}
else if( 0 != l_flg_tst(&Lin_Frm_FrameSlv0_flg) )
{
l_flg_clr( &Lin_Frm_FrameSlv0_flg );
/* 执行发送结束之后的操作 */
}
status = l_ifc_read_status( 0 );
/* 处理可能出现的应答错误 */
if( status & 0x0001u )
{
/* 出现应答错误,执行相关操作 */
}
if( LD_DATA_AVAILABLE == ld_raw_rx_status(0) )
{
ld_get_raw( 0, data );
}
/* 判断:来自主节点的信号被更新了吗? */
if( 0 != l_flg_tst(&Lin_Sig_Command_flg) )
{
l_flg_clr( &Lin_Sig_Command_flg );
if( 0x1234u == l_u16_rd(&Lin_Sig_Command) )
{
l_u16_wr( &Lin_Sig_Status_Slv0, 0x0101u );
l_u16_wr( &Lin_Sig_Status_Slv1, 0x0201u );
/* 其他相关操作 */
}
else if( 0x5678u == l_u16_rd(&Lin_Sig_Command) )
{
l_u16_wr( &Lin_Sig_Status_Slv0, 0x0100u );
l_u16_wr( &Lin_Sig_Status_Slv1, 0x0200u );
/* 其他相关操作 */
}
}
/* 监测休眠命令 */
if( status & 0x0008u )
{
/* LIN 端口休眠的相关操作 */
}
return;
}
}