实例介绍
【实例简介】
基于OsekNM 2.5.3协议实现的OsekNM,跨平台的结构,目前只实现了STM32F407ZGT6平台的驱动。
1. 目录结构
1.1 App目录是一个keil5工程,App/keil/Objects/OsekNM.hex是一个可以直接在STM32F407ZGT6平台运 行的hex文件。
1.2 Driver目录包含了stm32f407子目录和Win7子目录,stm32f407目录下就是实现的STM32F407ZGT6平台的底层驱动,包括定时器和CAN模块的驱动,以及Stm32标准库的东西;Win7子目录是在windows7平台下执行的一些文件;Driver_Common.c是一些公用的驱动,由OsekNM_core统一调用。
1.3 OsekNM_core实现了OsekNM 2.5.3协议的核心逻辑,OsekNM.c实现了对各个节点各状态的处理,OsekNMServer.c实现了OsekNM 2.5.3协议提供给应用程序的API。
2. 移植
2.1 在Driver目录新建文件夹,保存新平台的驱动程序,新的平台下需要实现CAN模块和定时器及中断的驱动程序。
2.2 修改Driver_Common.c文件,修改TX_CAN_Transmit()函数,调用新平台发送CAN报文的CAN模块驱动程序,修改InitPlatform()函数,调用新平台初始化CAN模块和定时器的函数;Recv_EveryMessage()被CAN接受报文中断服务函数调用。
2.3 修改Driver_Common.h文件,#define 新的平台,并包含驱动相关的头文件,#define NMID 新的网络管理报文ID,#define ADDR_SELF 新的节点源地址。
2.4 可以参考stm32f407的例子来实现自己平台的移植。
3. 附注
整个代码移植到我的开发板,并且在CANoe平台验证过。
专门在Vspy平台实现了虚拟的网络管理节点
【实例截图】
【核心代码】
#include "Driver_Common.h"
//与平台相关的公用的驱动代码在这里实现,包括NM报文缓冲区、定时器
//定时器设置
static char SetAlarm_TTYP = 0;
static char SetAlarm_TMAX = 0;
static char SetAlarm_TERROR = 0;
static char SetAlarm_TWBS = 0;
//定时器计数器
static int TTYP_Count = 0;
static int TMAX_Count = 0;
static int TERROR_Count = 0;
static int TWBS_Count = 0;
//报文缓冲区定义
static RecvFIFO_t RecvFIFO;
//定时器超时标志
static TimerOutFlag_t TimerOutFlag_TTYP = 0;
static TimerOutFlag_t TimerOutFlag_TMAX = 0;
static TimerOutFlag_t TimerOutFlag_TERROR = 0;
static TimerOutFlag_t TimerOutFlag_TWBS = 0;
//节点当前状态
extern NMStateType_t NMCurrentState;
//节点当前子状态
extern NMStateType_t NMCurrentSubState;
//节点上一个状态
extern NMStateType_t NMPreState;
extern NMNodeCfg_t NodeCfg;
//DEBUG
//#define DRVCOM_DEBUG
#ifdef DRVCOM_DEBUG
#define DRVCOM_PRINT(...) printf(__VA_ARGS__)
#else
#define DRVCOM_PRINT(...)
#endif
/*函数名:TX_CAN_Transmit
*参数:NMPDU
*返回值:成功 1
*说明:调用平台相关的报文发送函数
*/
NMTypeU8_t TX_CAN_Transmit(NMPDU_t* NMPDU)
{
/*发送报文到总线*/
#ifdef STM32F407
return STM32_TX_CAN_Transmit(NMPDU);
#endif
}
//NMPDU初始化,保留位置1
void InitNMPDU(NMPDU_t* NMPDU)
{
int i = 0;
NMPDU->MsgCtl = 0xc8;
//NMPDU->MsgID = ((NMID << 8) | ADDR_SELF);
//NMPDU->MsgDA = ADDR_SELF;
for (; i < 6; i )
{
NMPDU->MsgData[i] = 0xff;
}
}
//返回定时器是否超时,-1 失败
TimerOutFlag_t GetTimerIsOut(NMTimerType_t TimerType)
{
switch (TimerType)
{
case NM_TIMER_TTYP:
return TimerOutFlag_TTYP;
case NM_TIMER_TMAX:
return TimerOutFlag_TMAX;
case NM_TIMER_TERROR:
return TimerOutFlag_TERROR;
case NM_TIMER_TWBS:
return TimerOutFlag_TWBS;
}
return -1;
}
//清除定时器超时标志
void ClcTimerOutFlag(NMTimerType_t TimerType)
{
switch (TimerType)
{
case NM_TIMER_TTYP:
TimerOutFlag_TTYP = 0;
break;
case NM_TIMER_TMAX:
TimerOutFlag_TMAX = 0;
break;
case NM_TIMER_TERROR:
TimerOutFlag_TERROR = 0;
break;
case NM_TIMER_TWBS:
TimerOutFlag_TWBS = 0;
break;
}
}
//FIFO相关的函数
/*说明:SetFIFO,将收到的报文放入FIFO,并调整FIFO
* 参数:GenericMessage* msg,报文指针
* 返回值:1:成功放入报文到FIFO,0:放入失败
*/
char SetToFIFO(NMPDU_t* msg)
{
if (RecvFIFO.FullFlag == 1)//先判断缓冲区满否
return 0;
/*放入报文到缓冲区*/
RecvFIFO.MSGs[RecvFIFO.Tail% FIFOMAX] = *msg;
RecvFIFO.Tail = (RecvFIFO.Tail 1) % FIFOMAX;
/*清除空标识*/
RecvFIFO.EmptyFlag = 0;
if ((RecvFIFO.Tail 1) == RecvFIFO.Head)//缓冲区满
RecvFIFO.FullFlag = 1;
return 1;
}
/*说明:GetFIFO,从FIFO取出报文,并调整FIFO
* 参数:GenericMessage* msg,报文指针
* 返回值:1:成功取出报文,0:取出失败
*/
char GetFromFIFO(NMPDU_t* msg)
{
int i = 2;
if (RecvFIFO.EmptyFlag == 1)//先判断缓冲区空否
return 0;
/*从缓冲区取出报文*/
msg->MsgDA = RecvFIFO.MSGs[RecvFIFO.Head% FIFOMAX].MsgDA;
msg->MsgCtl = RecvFIFO.MSGs[RecvFIFO.Head% FIFOMAX].MsgCtl;
msg->MsgID = RecvFIFO.MSGs[RecvFIFO.Head% FIFOMAX].MsgID;
//数据域直接复制
for (; i < OSEKNM_DLC; i )
{
msg->MsgData[i] = RecvFIFO.MSGs[RecvFIFO.Head% FIFOMAX].MsgData[i];
}
RecvFIFO.Head = (RecvFIFO.Head 1) % FIFOMAX;
/*清除满标识*/
RecvFIFO.FullFlag = 0;
if ((RecvFIFO.Tail) == RecvFIFO.Head)//缓冲区空
RecvFIFO.EmptyFlag = 1;
return 1;
}
/*说明:ClearFIFO,清空整个FIFO
* 参数:void
* 返回值:void
*/
void ClearFIFO(void)
{
RecvFIFO.Total = 0;
RecvFIFO.Head = 0;
RecvFIFO.Tail = 0;
RecvFIFO.FullFlag = 0;
RecvFIFO.EmptyFlag = 1;
}
//定时器超时函数
/*LimpHome定时器*/
static void TimerOutTERROR()
{
TimerOutFlag_TERROR = 1;
#ifdef PRINT_LOG
char buf[100];
sprintf(buf, "TError out State:%d\n", NMCurrentState);
LogOutPut(buf);
#endif
}
/*TMax超时定时器*/
static void TimerOutTMAX()
{
TimerOutFlag_TMAX = 1;
#ifdef PRINT_LOG
char buf[100];
sprintf(buf, "TMAX out State:%d\n", NMCurrentState);
LogOutPut(buf);
#endif
}
/*TTYP定时器*/
static void TimerOutTTYP()
{
TimerOutFlag_TTYP = 1;
#ifdef PRINT_LOG
char buf[100];
sprintf(buf, "TTYP out State:%d\n", NMCurrentState);
LogOutPut(buf);
#endif
}
/*WaitBusSleep定时器*/
static void TimerOutTWBS()
{
TimerOutFlag_TWBS = 1;
#ifdef PRINT_LOG
char buf[100];
sprintf(buf, "TWBS out State:%d\n", NMCurrentState);
LogOutPut(buf);
#endif
}
//10ms定时器
void Timer10()
{
/*调用SetAlarm(xx),定时器开始递增*/
if (SetAlarm_TTYP)
{
TTYP_Count ;
if (TTYP_Count >= 10)//TTYP=100ms
{
TTYP_Count = 0;//重新计数
TimerOutTTYP();
SetAlarm_TTYP = 0;//每次用完定时器都将其关闭,从而简化定时器管理
}
}
else {
TTYP_Count = 0;
}
if (SetAlarm_TMAX)
{
TMAX_Count ;
if (TMAX_Count >= 26)//TMAX=260ms
{
TMAX_Count = 0;//重新计数
TimerOutTMAX();
SetAlarm_TMAX = 0;//每次用完定时器都将其关闭
}
}
else {
TMAX_Count = 0;
}
if (SetAlarm_TERROR)
{
TERROR_Count ;
if (TERROR_Count >= 100)//TError=1000ms
{
TERROR_Count = 0;//重新计数
TimerOutTERROR();
SetAlarm_TERROR = 0;//每次用完定时器都将其关闭
}
}
else {
TERROR_Count = 0;
}
if (SetAlarm_TWBS)
{
TWBS_Count ;
if (TWBS_Count >= 500)//TError=5000ms
{
TWBS_Count = 0;//重新计数
TimerOutTWBS();
SetAlarm_TWBS = 0;//每次用完定时器都将其关闭
}
}
else {
TWBS_Count = 0;
}
}
//平台相关的初始化
void InitPlatform()
{
/*缓冲区初始化*/
RecvFIFO.GetMsg = GetFromFIFO;
RecvFIFO.SetMsg = SetToFIFO;
RecvFIFO.ClearBuff = ClearFIFO;
RecvFIFO.ClearBuff();
/*1.STM32相关的初始化*/
#ifdef STM32F407
/*定时器初始化*/
Stm32Timer3Init();//10ms中断一次
/*CAN模块初始化*/
STM32_CAN1_Init();
#endif
}
/*
*自定义定时器函数:SetAlarm
参数:定时器类型
说明:每次调用都使定时器重新从0开始
*返回值:定时器ID,用定时器类型ID代替定时器ID
*/
int SetAlarm(NMTimerType_t timer)
{
int Tid = 0;
switch (timer)
{
case NM_TIMER_TTYP:
SetAlarm_TTYP = 1;
TTYP_Count = 0;
TimerOutFlag_TTYP = 0;//每次设置定时器前先清除标志位
Tid = NM_TIMER_TTYP;
break;
case NM_TIMER_TMAX:
TMAX_Count = 0;
SetAlarm_TMAX = 1;
TimerOutFlag_TMAX = 0;//每次设置定时器前先清除标志位
Tid = NM_TIMER_TMAX;
break;
case NM_TIMER_TERROR:
TERROR_Count = 0;
SetAlarm_TERROR = 1;
TimerOutFlag_TERROR = 0;//每次设置定时器前先清除标志位
Tid = NM_TIMER_TERROR;
break;
case NM_TIMER_TWBS:
TWBS_Count = 0;
SetAlarm_TWBS = 1;
TimerOutFlag_TWBS = 0;//每次设置定时器前先清除标志位
Tid = NM_TIMER_TWBS;
break;
}
return Tid;
}
/*
*自定义定时器函数:CancelAlarm
参数:定时器类型
说明:定时器清0,不再计数
*/
void CancelAlarm(NMTimerType_t timer)
{
switch (timer)
{
case NM_TIMER_TTYP:
TTYP_Count = 0;
SetAlarm_TTYP = 0;
TimerOutFlag_TTYP = 0;//每次关闭定时器先清除标志位
break;
case NM_TIMER_TMAX:
TMAX_Count = 0;
SetAlarm_TMAX = 0;
TimerOutFlag_TMAX = 0;//每次关闭定时器先清除标志位
break;
case NM_TIMER_TERROR:
TERROR_Count = 0;
SetAlarm_TERROR = 0;
TimerOutFlag_TERROR = 0;//每次关闭定时器先清除标志位
break;
case NM_TIMER_TWBS:
TWBS_Count = 0;
SetAlarm_TWBS = 0;
TimerOutFlag_TWBS = 0;//每次关闭定时器先清除标志位
break;
}
}
/*CAN中断收到的报文*/
void Recv_EveryMessage(NMPDU_t* p_Msg)
{
/*将所有收到的NM报文放入缓冲区FIFO*/
if (((p_Msg->MsgID) != NMID) && ((p_Msg->MsgID>>8) == (NMID>>8)))//过滤网络报文,不接收自己发出去的
{
RecvFIFO.SetMsg(p_Msg);//暂时不处理返回值
}
}
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明


网友评论
我要评论