嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):78630559
本次赞助数额为: 2 元微信扫码支付:2 元
请留下您的邮箱,我们将在2小时内将文件发到您的邮箱
基于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);//暂时不处理返回值
}
}