基于CanOpen协议的CanFestival移植方法,移植到STM32F07

之前移植了CanFestival现在做个笔记

CanFestival的优势:

1.CanFestival为开发者提供了许多工具,以提高开发的便利性。

2.CanFestival能够运行于多种类型的平台。CanFestival源代码由ANSI-C编写,驱动和例程的编译情况仅取决于具体的编译工具,而且官方提供了对于多种硬件平台的驱动。

3.CanFestival协议功能完整,完全符合CANopen标准。CanFestival完全支持CIA DS-301 V4.02标准,并支持CiA DS302中的简明DFC协议。

上网下载CanFestival-3-8bfe0ac00cdb,地址:https://hg.beremiz.org/CanFestival-3。

下载后解压并将文件名该为CanFestival-3-8。移植需要用到的文件主要在CanFestival-3-8\src,CanFestival-3-8\include,

CanFestival-3-8\examples\AVR\Slave,CanFestival-3-8\include\AVR,CanFestival-3-8\examples\TestMasterSlave;

步骤一:
    在新建好的工程目录下新建文件夹CanFestival,再在CanFestival下新建文件夹driver、inc和src,stm32文件夹
步骤二:
    1、CanFestival-3-8\src目录下的dcf.c、emcy.c、lifegrd.c、lss.c、nmtMaster.c、nmtSlave.c、objacces.c、pdo.c、sdo.c、states.c、sync.c、timer.c共12个文件拷贝到CanFestival\src目录下;
 2、CanFestival-3-8\include目录下的所有.h文件全部拷贝到CanFestival\inc目录下共19个文件;
    CanFestival-3-8\examples\AVR\Slave目录下的ObjDict.h文件拷贝过来,现在CanFestival\inc目录下一共20个;
   CanFestival-3-8\include\AVR目录下的applicfg.h、canfestival.h、config.h、timerscfg.h共4个头文件拷贝到CanFestival\stm32目录下;
        CanFestival-3-8\examples\TestMasterSlave目录下的TestSlave.c、TestSlave.h、TestMaster.c、TestMaster.h、拷贝到canfestival\driver目录下,并在该目录下新建stm32_canfestival.c文件。
    3、部分文件找不到可以将文件编译后生成,像TestSlave.c、TestMaster.c找不到可以下载Linux版本的压缩包,在Linux中编译Makefile后生成,再复制出来
步骤三:
        CanFestival\src目录下的所有.c文件添加到工程;将CanFestival\driver目录下的stm32_canfestival.c文件添加到工程;如果实现的是从设备,再将canfestival\driver目录下的TestSlave.c文件添加到工程,如果实现的是主设备,则将TestMaster.c文件添加到工程;
步骤四:
        将文件目录CanFestival\inc、CanFestival\stm32、CanFestival\driver等路径添加到工程包含路径。
步骤五:
        在stm32_canfestival.c中包含头文件#include "canfestival.h",并定义如下函数:
        void setTimer(TIMEVAL value)
        {
        }
        TIMEVAL getElapsedTime(void)
        {
            return 1;
        }
        unsigned char canSend(CAN_PORT notused, Message *m)
        {
            return 1;
        }
这几个文件主要是CanFestival源码调用的,如果找不到这几个函数编译就会报错。
步骤六:
        通过以上几步,所有的文件都弄齐了,但是编译一定会出现报错,注释或删除掉config.h文件中的如下几行就能编译通过:
        #include <inttypes.h>
        #include <avr\io.h>
        #include <avr\interrupt.h>
        #include <avr/pgmspace.h>
        #include <avr\sleep.h>
        #include <avr\wdt.h>
还有部分内联函数有可能会报错,还有的一般都是有些函数没声明或者某个头文件没有包含或者包含了一些不必要的头文件而该文件不存在或者是一些变量类型不符合需定义之类的。调试成功后移植就算成功了
步骤七:
        解决了所有的编译错误后,接下来实现刚才定义的3个空函数,函数void setTimer(TIMEVAL value)主要被源码用来定时的,时间到了就需要调用一下函数TimeDispatch(),函数TIMEVAL getElapsedTime(void)主要被源码用来查询距离下一个定时触发还有多少时间,unsigned char canSend(CAN_PORT notused, Message *m)函数主要被源码用来发一个CAN包的,需要调用驱动来将一个CAN包发出去。
        stm32_canfestival.c文件里定义几个变量如下:         
        unsigned int TimeCNT=0;                                    //时间计数
        unsigned int NextTime=0;                                    //下一次触发时间计数
        unsigned int TIMER_MAX_COUNT=70000;           //最大时间计数
        static TIMEVAL last_time_set = TIMEVAL_MAX;    //上一次的时间计数
      
         setTimer和getElapsedTime函数实现如下:
        //Set the next alarm //
        void setTimer(TIMEVAL value)
        {
            NextTime=(TimeCNT+value)%TIMER_MAX_COUNT;
        }

 

        // Get the elapsed time since the last occured alarm //
        TIMEVAL getElapsedTime(void)
        {
            int ret=0;
            ret = TimeCNT> last_time_set ? TimeCNT - last_time_set : TimeCNT + TIMER_MAX_COUNT - last_time_set;
            last_time_set = TimeCNT;
            return ret;
        }
        //另外还要开一个1毫秒的定时器,每1毫秒调用一下下面这个函数。
        void timerForCan(void)
        {
            TimeCNT++;
            if (TimeCNT>=TIMER_MAX_COUNT)
            {
                    TimeCNT=0;
            }
            if (TimeCNT==NextTime)
            {
                    TimeDispatch();
            }
    }
can发包函数canSend跟CAN驱动有关,CAN通道可以使用真实的CAN总线,也可以使用虚拟的CAN通道(如文件接口、网络通道等等)。
驱动实现:
//通用定时器5中断初始化
//这里时钟选择为APB1的2倍,而APB1为42M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器5!
static void TIM5_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //时钟使能
    
    //定时器TIM5初始化
    TIM_TimeBaseStructure.TIM_Period = arr;                     //设置在下一个更新事件装入活动的自动重装载寄存器周期的值    
    TIM_TimeBaseStructure.TIM_Prescaler =psc;                     //设置用来作为TIMx时钟频率除数的预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    //TIM向上计数模式
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);             //根据指定的参数初始化TIMx的时间基数单位
 
    TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE );                     //使能指定的TIM5中断,允许更新中断

    //中断优先级NVIC设置
    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;             //TIM5中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;      //先占优先级0级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;          //从优先级3级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure);                              //初始化NVIC寄存器
 
    TIM_Cmd(TIM5, ENABLE);                                      //使能TIMx                    
}
void initTimer(void)
{
    TIM5_Int_Init(1000-1, 84-1);     //1Mhz的计数频率,计数到1000为1ms
}
//定时器5每隔1ms中断1次
void TIM5_IRQHandler(void)
{    
    if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)  //检查TIM5更新中断发生与否
    {
        TIM_ClearITPendingBit(TIM5, TIM_IT_Update  );  //清除TIMx更新中断标志
        TimerForCan();                                 //检查时间相关任务
    }        
}
unsigned char canSend(CAN_PORT notused, Message *m)
{
     return CAN1_Send_Msg((Message *) m);
}
CAN1_Send_Msg实现的相关函数:

u8 CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{

    GPIO_InitTypeDef         GPIO_InitStructure;
    CAN_InitTypeDef           CAN_InitStructure;
    CAN_FilterInitTypeDef      CAN_FilterInitStructure;
    NVIC_InitTypeDef  NVIC_InitStructure;

    
    //使能相关时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);    //使能PORTD时钟                                                                   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);    //使能CAN1时钟    

    //初始化GPIO
    GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_0| GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_AF;            //复用功能
    GPIO_InitStructure.GPIO_OType     = GPIO_OType_PP;        //推挽输出
    GPIO_InitStructure.GPIO_Speed     = GPIO_Speed_100MHz;    //100MHz
    GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_UP;            //上拉
    GPIO_Init(GPIOD, &GPIO_InitStructure);                    //初始化PD1,PD0

    //引脚复用映射配置
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_CAN1); //GPIOD0复用为CAN1
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_CAN1); //GPIOD1复用为CAN1

    //CAN单元设置
    CAN_InitStructure.CAN_TTCM        = DISABLE;    //非时间触发通信模式   
    CAN_InitStructure.CAN_ABOM        = ENABLE;    //软件自动离线管理      
    CAN_InitStructure.CAN_AWUM        = DISABLE;    //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
    CAN_InitStructure.CAN_NART        = ENABLE;    //禁止报文自动传送
    CAN_InitStructure.CAN_RFLM        = DISABLE;    //报文不锁定,新的覆盖旧的  
    CAN_InitStructure.CAN_TXFP        = DISABLE;    //优先级由报文标识符决定
    CAN_InitStructure.CAN_Mode        = mode;         //模式设置
    CAN_InitStructure.CAN_SJW        = tsjw;        //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
    CAN_InitStructure.CAN_BS1        = tbs1;     //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
    CAN_InitStructure.CAN_BS2        = tbs2;        //Tbs2范围CAN_BS2_1tq ~    CAN_BS2_8tq
    CAN_InitStructure.CAN_Prescaler    = brp;      //分频系数(Fdiv)为brp+1    
    CAN_Init(CAN1, &CAN_InitStructure);           //初始化CAN1

    //配置过滤器
    CAN_FilterInitStructure.CAN_FilterNumber            = 0;                              //过滤器0
    CAN_FilterInitStructure.CAN_FilterMode                = CAN_FilterMode_IdMask;         //屏蔽模式
    CAN_FilterInitStructure.CAN_FilterScale                = CAN_FilterScale_32bit;         //32位
    CAN_FilterInitStructure.CAN_FilterIdHigh            = 0x0000;                        //32位ID
    CAN_FilterInitStructure.CAN_FilterIdLow                = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh        = 0x0000;                        //32位MASK
    CAN_FilterInitStructure.CAN_FilterMaskIdLow            = 0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment    = CAN_Filter_FIFO0;                //过滤器0关联到FIFO0
    CAN_FilterInitStructure.CAN_FilterActivation        = ENABLE;                         //激活过滤器0
    CAN_FilterInit(&CAN_FilterInitStructure);                                            //滤波器初始化
        
    CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//FIFO0消息挂号中断允许.            

    NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     // 主优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;            // 次优先级为0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);    
    
    return 0;

u8 CAN1_Send_Msg(Message *msg)
{    
    u8 mbox;
    u16 i=0;
    
    CanTxMsg TxMessage;
    TxMessage.StdId = msg->cob_id;            // 标准标识符
    TxMessage.ExtId = 0;                    // 设置扩展标示符
    TxMessage.IDE = CAN_Id_Standard;         // 标准帧
    TxMessage.RTR = msg->rtr;                // 数据帧
    TxMessage.DLC = msg->len;                // 要发送的数据长度
    for(i=0; i<msg->len; i++)
    {
        TxMessage.Data[i] = msg->data[i];                      
    }
    mbox= CAN_Transmit(CAN1, &TxMessage);   
    i=0;
    while((CAN_TransmitStatus(CAN1, mbox)!=CAN_TxStatus_Ok)&&(i<0XFFF))i++;    //等待发送结束
    if(i>=0XFFF)return 1;
    return 0;    

}

void CAN1_RX0_IRQHandler(void)
{
    u8 i;
    CanRxMsg RxMessage;
    Message m;
    CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
    //接收处理
    m.cob_id=RxMessage.StdId;
    if(RxMessage.RTR == CAN_RTR_REMOTE)
        m.rtr=1;
    else if(RxMessage.RTR == CAN_RTR_DATA)
        m.rtr=0;
    m.len=RxMessage.DLC;
    for(i = 0; i < RxMessage.DLC; i++)
    {
        m.data[i] = (u8)RxMessage.Data[i];
    }

    canDispatch(&TestMaster_Data, &m);    
}


参考:http://bbs.21ic.com/icview-878522-1-1.html

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值