OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

正点原子全套STM32/FPGA开发资料,上千讲STM32视频教程,RT1052教程免费下载啦...
查看: 3749|回复: 64

CANOPEN协议栈的移植以及使用

[复制链接]

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
发表于 2017-12-31 16:27:32 | 显示全部楼层 |阅读模式
这是一篇入门帖,高手请指点一下,主要是记录我对CANOPEN协议的理解以及使用。图片还没研究好怎么上传,等后面研究好了补上。第一步;新建一个工程,配置好各种时钟之类的。当然你用的是原子的板子, 你也可以拿一个LED实验或者蜂鸣器实验的工程  直接来改,你把其他的注释掉就好了。
第二步;到网上下载CanFestival源码CanFestival-3-10,解压出来,并将文件夹名字改为CanFestival-3-10。移植需要用到的源文件在CanFestival-3-10\src目录下,头文件在CanFestival-3-10\include目录下。
第三部;开始移植。





回复

使用道具 举报

0

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2018-10-10
在线时间
5 小时
发表于 2018-10-17 19:56:27 | 显示全部楼层
本帖最后由 雨天Al2 于 2018-10-17 19:59 编辑
whj467467274672 发表于 2018-10-17 08:24
找到答案了可以给我科普一下吗?

今天又找了一天,其实问题就是出在getElapsedTime()函数这个地方,
last_time_set=TimeCNT;不应该放在这里。应该把这句话放在函数TimeDispatch();的前面
回复 支持 1 反对 0

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2017-12-31 16:55:00 | 显示全部楼层
最后一步就是main函数怎么启动了。
unsigned char nodeID=0x07;                 这个是你给你移植好的设备设置的站号
extern CO_Data TestSlave_Data;            这个是后面讲到对象字典了再告诉讲
extern UNS8 TEST[8];                          这个是后面讲到对象字典了再告诉讲

int main(void)
{

        delay_init(168);       
            
        CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,CAN_Mode_Normal);

        setNodeId(&TestSlave_Data, nodeID);
        setState(&TestSlave_Data, Initialisation);
        setState(&TestSlave_Data, Operational);          

        TIM3_Init();
        while(1)
        {
           }
                       }

setState(&TestSlave_Data, Operational);         看过CANOPEN协议的朋友应该知道这是什么意思?而我为什么直接程序让他启动了呢,是因为第一次做希望上电就能收到报文,所以写了一个这样,不需要的可以直接注释掉。
回复 支持 1 反对 0

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2017-12-31 16:36:21 | 显示全部楼层
本帖最后由 whj467467274672 于 2017-12-31 16:37 编辑

移植步骤:步骤一;在新建好的工程目录下新建文件夹CanFestival,再在CanFestival下新建文件夹driver、inc和src,再在inc文件夹下面新建stm32文件夹。
步骤二;将CanFestival-3-10\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目录下;将CanFestival-3-10\include目录下的所有.h文件共19个文件全部拷贝到CanFestival\inc目录下,再把CanFestival-3-10\examples\AVR\Slave目录下              的ObjDict.h文件拷贝过来,一共20个;将CanFestival-3-10\include\AVR目录下的applicfg.h、canfestival.h、config.h、timerscfg.h共4个头文件拷贝到c                                         anfestival\inc\stm32 目录下;将CanFestival-3-10\examples\TestMasterSlave目录下的TestSlave.c、TestSlave.h、TestMaster.h、TestMaster.c拷贝到canfestival\driver目               录下,并在该目录下新建 stm32_canfestival.c文件。
步骤三;将CanFestival\src目录下的所有.c文件添加到工程;将canfestival\driver目录下的stm32_canfestival.c文件添加到工程;如果实现的是从设备,再将                                             canfestival\driver目录下的TestSlave.c文件添加到工程,如果实现的是主设备,则将TestMaster.c文件添加到工程。
步骤四;将文件目录canfestival\inc、canfestival\inc\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>
            如果还有其他错误,可能就是不同版本的源码导致的,也有可能是不同的DEMO引起的,不能解决这些问题的话,那就是GAMEOVER,如果能解决,那么我们继续。

回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2017-12-31 16:45:38 | 显示全部楼层
通过以上步骤处理掉所有错误,对于源码的操作已经结束了,下面就是我们的底层的操作了。
接下来实现刚才定义的3个空函数,函数void setTimer(TIMEVAL value)主要被源码用来定时的,时间到了就需要调用一下函数TimeDispatch(),函数TIMEVAL getElapsedTime(void)主要被源码用来查询距离下一个定时触发还有多少时间。
我们在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函数实现如下:
void setTimer(TIMEVAL value)
{
        NextTime=(TimeCNT+value)%TIMER_MAX_COUNT;
}

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();
        }
}

下面我贴上定时器的初始化代码
void TIM3_Init(void)
{
        TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
       
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
       
        TIM_TimeBaseInitStructure.TIM_Period = 1000-1;
        TIM_TimeBaseInitStructure.TIM_Prescaler= 84-1;  
        TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
        TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
       
        TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
       
        TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
        TIM_Cmd(TIM3,ENABLE);
       
        NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03;
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_Init(&NVIC_InitStructure);
       
}

定时器3中断服务函数
void TIM3_IRQHandler(void)
{
        if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //òç3öÖD¶Ï
        {
                TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
       
//        TimerForCan();
              TimeCNT++;
        if (TimeCNT>=TIMER_MAX_COUNT)
        {
                TimeCNT=0;
        }
        if (TimeCNT==NextTime)

                TimeDispatch();
      }
}



回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2017-12-31 16:49:53 | 显示全部楼层
上面讲过了定时器,下面就是CAN的发送与接收函数。
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_GPIOA, ENABLE);                                                                                                          
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);       
       

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
       
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
       
       
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1);
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1);
          
         
           CAN_InitStructure.CAN_TTCM=DISABLE;       
          CAN_InitStructure.CAN_ABOM=DISABLE;       
          CAN_InitStructure.CAN_AWUM=DISABLE;
          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;       
          CAN_InitStructure.CAN_BS1=tbs1;
          CAN_InitStructure.CAN_BS2=tbs2;
          CAN_InitStructure.CAN_Prescaler=brp;  
          CAN_Init(CAN1, &CAN_InitStructure);   

               
        CAN_FilterInitStructure.CAN_FilterNumber=0;          
          CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
          CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
          CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
          CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
          CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
          CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
           CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;
          CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;
          CAN_FilterInit(&CAN_FilterInitStructure);
               

       
          CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);                    

          NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     
          NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         
          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
          NVIC_Init(&NVIC_InitStructure);

          return 0;
}   






                    
void CAN1_RX0_IRQHandler(void)
{
         u32 i;
   Message m;
   CanRxMsg RxMessage;
   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] = RxMessage.Data[i];
   canDispatch(&TestSlave_Data, &m);
}

unsigned char canSend(CAN_PORT notused, Message *m)
{
    uint32_t  i;
          CanTxMsg  TxMessage;
    CanTxMsg *ptx_msg=&TxMessage;
    ptx_msg->StdId = m->cob_id;

    if(m->rtr)
    ptx_msg->RTR = CAN_RTR_REMOTE;
    else
    ptx_msg->RTR = CAN_RTR_DATA;

    ptx_msg->IDE = CAN_ID_STD;
    ptx_msg->DLC = m->len;
    for(i = 0; i < m->len; i++)
    ptx_msg->Data[i] = m->data[i];
    if( CAN_Transmit( CAN1, ptx_msg )==CAN_NO_MB)
    {
        return 0xff;
    }
    else
    {
        return 0x00;
        }
}


回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2017-12-31 16:56:42 | 显示全部楼层
本帖最后由 whj467467274672 于 2017-12-31 17:02 编辑

后面我来继续讲怎么使用对象字典。但是看后面的内容,还是建议一定熟读CANOPEN协议了之后再尝试,不然可能会看不懂。还有就是我要研究一下怎么发图片,不然一会可能说不清楚。还得安装3个软件,Beremiz-1.1_rc5   python-2.7.10(必须是2.7版本)  wxpython2.8  其中python2.7安装好了还有一个设置环境变量,这里就不说的太细了,不然你们什么都不查,自己就不懂脑经了。
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2017-12-31 19:27:33 | 显示全部楼层
安装好上面几个程序之后,打开C:\Program Files (x86)\Beremiz\CanFestival-3\objdictgen\objdicttedit.py   打开方式选择python。如下图1.你可以新建,如果你其他的EDS文件,你也可以直接打开。打开之后的界面如图2所示。可以看到,我选择了0x1000-0x1029,下面选择的是 1017,那么右边有一个写值0x64的话,就是100ms发送一次心跳。其他的PDO就自己慢慢研究吧。

图1

图1

图2

图2
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2017-12-31 19:30:11 | 显示全部楼层
在移植完毕使用之后发现几个问题。第一个就是定时器是一个动态值,第二个就是事件发送不能发送,不知道是什么问题。忘高手路过指点一下。
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2017-12-31 19:31:09 | 显示全部楼层
另外工程暂时不发在论坛了,我觉得上面已经说得非常详细了,如果还有其他问题可以论坛交流。
回复 支持 反对

使用道具 举报

43

主题

215

帖子

0

精华

高级会员

Rank: 4

积分
903
金钱
903
注册时间
2012-1-10
在线时间
193 小时
发表于 2018-1-2 16:00:53 | 显示全部楼层
收藏!
回复 支持 反对

使用道具 举报

13

主题

49

帖子

0

精华

初级会员

Rank: 2

积分
162
金钱
162
注册时间
2017-12-4
在线时间
53 小时
发表于 2018-2-3 18:55:00 | 显示全部楼层
不知道能否给我一份canopen源代码,谢谢,邮箱516182660@qq.com
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2018-2-4 10:44:54 | 显示全部楼层
516182660 发表于 2018-2-3 18:55
不知道能否给我一份canopen源代码,谢谢,邮箱

看附件

CanFestival-3-10.zip

4.71 MB, 下载次数: 1628

回复 支持 反对

使用道具 举报

0

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2018-10-10
在线时间
5 小时
发表于 2018-10-11 08:17:46 | 显示全部楼层
你好!
        我有个问题想请教你。
        1. 首先是TimeDispatch()这个 函数的工作原理,你能给我简单描述一下吗?
        2. 下面这两个定义的作用,为什么要这样定义?能否举个简单的例子,谢谢
        // The timer is incrementing every 1 ms.
        #define MS_TO_TIMEVAL(ms) ((ms))                    //change @bruce
        #define US_TO_TIMEVAL(us) ((us)/1000)     //change @bruce
      
回复 支持 反对

使用道具 举报

0

主题

92

帖子

0

精华

初级会员

Rank: 2

积分
178
金钱
178
注册时间
2018-9-27
在线时间
15 小时
发表于 2018-10-12 09:54:00 | 显示全部楼层
收藏了,谢谢楼主
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2018-10-14 10:47:31 | 显示全部楼层
本帖最后由 whj467467274672 于 2018-10-15 09:28 编辑
雨天Al2 发表于 2018-10-11 08:17
你好!
        我有个问题想请教你。
        1. 首先是TimeDispatch()这个 函数的工作原理,你能给我 ...

TimeDispatch我的理解是使用定时器来处理各个节点的状态,功能是遍历timers,如果有超时的,就调用相应的call, 这些timers是由SetAlarm()函数来设定的


#define TIMEVAL_MAX  0XFFFFFF(定时器的最大计数值, 24位为0xFFFFFF)
#define MS_TO_TIMEVAL(ms)     (1ms对应的计数值, 例如计数频率为1us,则此值为1000)
#define US_TO_TIMEVAL(us)      (1us对应的计数值,  例如计数频率为1us,则此值为1)

回复 支持 反对

使用道具 举报

0

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2018-10-10
在线时间
5 小时
发表于 2018-10-16 08:18:21 | 显示全部楼层
whj467467274672 发表于 2018-10-14 10:47
TimeDispatch我的理解是使用定时器来处理各个节点的状态,功能是遍历timers,如果有超时的,就 ...

谢谢你的回答。        我在移植的时候遇到一个问题,上电后,板子按照我设置的心跳时间给我发送状态,但是我改变状态后,其心跳时间就变了,
         假如我设置的心跳是1S,那么在1S内就会发送两次心跳,这明显是不对的,请问这个可能造成这种现象的原因都有哪些?

TIM图片20181010201410.png
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2018-10-16 20:50:56 | 显示全部楼层
雨天Al2 发表于 2018-10-16 08:18
谢谢你的回答。        我在移植的时候遇到一个问题,上电后,板子按照我设置的心跳时间给我发送状态,但 ...

我也不是特别懂协议栈底层。我忘记在哪里看到的,是说协议栈本身的时间机制,不是一个特别精准,而是一个动态调节的时间机制。
回复 支持 反对

使用道具 举报

0

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2018-10-10
在线时间
5 小时
发表于 2018-10-16 21:28:15 | 显示全部楼层
whj467467274672 发表于 2018-10-16 20:50
我也不是特别懂协议栈底层。我忘记在哪里看到的,是说协议栈本身的时间机制,不是一个特别精准,而是一个 ...

谢谢你的回复,我在继续找找问题
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2018-10-17 08:24:41 | 显示全部楼层
雨天Al2 发表于 2018-10-16 21:28
谢谢你的回复,我在继续找找问题

找到答案了可以给我科普一下吗?
回复 支持 反对

使用道具 举报

0

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2018-10-10
在线时间
5 小时
发表于 2018-10-17 20:04:06 | 显示全部楼层
whj467467274672 发表于 2018-10-17 08:24
找到答案了可以给我科普一下吗?

我看网上也有问这个问题的
https://www.a_m_o_bbs.com/thread-5688488-1-1.html?_dsign=e79ff9a5
链接上的代码试了,有效。不过方式不是太一样,效果是一样的
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2018-10-18 09:40:48 | 显示全部楼层
本帖最后由 whj467467274672 于 2018-10-18 09:43 编辑

原子论坛把阿mo论坛的链接屏蔽了吗?上面的地址要把a_m_o_bbs中间的横线去掉才是正确的链接地址
阿mo论坛的资料无法下载是硬伤。
回复 支持 反对

使用道具 举报

0

主题

92

帖子

0

精华

初级会员

Rank: 2

积分
178
金钱
178
注册时间
2018-9-27
在线时间
15 小时
发表于 2018-10-18 10:12:06 | 显示全部楼层
谢谢楼主分享
回复 支持 反对

使用道具 举报

0

主题

8

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2018-4-9
在线时间
25 小时
发表于 2018-11-24 17:05:51 | 显示全部楼层
/* 功能:设置定时器在value微妙之后触发 */
void setTimer(TIMEVAL value)
{
        uint16_t capture = 0;

        capture = htim1.Instance->CNT;                                        /* 计数器值 */
        htim1.Instance->ARR = capture + value;        /* 自动重载寄存器 */
}

/* 获取定时器当前计数值 */
TIMEVAL getElapsedTime(void)
{
        uint16_t timer = htim1.Instance->CNT;        /* 计数器值 */
        timer = timer - last_time_set;

        return timer;
}

/* 定时器回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
        switch((uint32_t)htim->Instance)
        {
                case (uint32_t)TIM1:
                        last_time_set = htim1.Instance->CNT;
                        TimeDispatch();
                        break;
               
                default:
                        break;
        }
}
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-2 13:15:17 | 显示全部楼层
whj467467274672 发表于 2017-12-31 16:36
移植步骤:步骤一;在新建好的工程目录下新建文件夹CanFestival,再在CanFestival下新建文件夹driver、inc ...

请问下,你发出来的canfestival移植源文件中为何找不到TestSlave.c和TestMaster.c文件,网上下的版本也是找不到这两个文件。或者有这两个文件的话前面的文件又会缺少,望指点下!
2019-01-02_131456.jpg
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-3 08:54:14 | 显示全部楼层
本帖最后由 whj467467274672 于 2019-1-3 08:55 编辑
NewGuard 发表于 2019-1-2 13:15
请问下,你发出来的canfestival移植源文件中为何找不到TestSlave.c和TestMaster.c文件,网上下的版本也是 ...

这个就是对象字典文件了,要自己来实现。你需要一个canopen对象字典编辑器
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-3 09:22:29 | 显示全部楼层
whj467467274672 发表于 2019-1-3 08:54
这个就是对象字典文件了,要自己来实现。你需要一个canopen对象字典编辑器

谢谢我昨天偶然间搞定了,objdictedit.py把TestMaster.od生成词典TestMaster.c就可以了,我这边移植完后感觉没怎么用上canfestival的感觉,只用TimeDispatch()就行了吗?我是有点迷茫,可否指点下?
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-3 14:31:03 | 显示全部楼层
NewGuard 发表于 2019-1-3 09:22
谢谢我昨天偶然间搞定了,objdictedit.py把TestMaster.od生成词典TestMaster.c就可以了,我这边移植完后 ...

canDispatch()和TimeDispatch()都要用上
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-3 16:24:13 | 显示全部楼层
whj467467274672 发表于 2019-1-3 14:31
canDispatch()和TimeDispatch()都要用上

1.TimeDispatch()在ms定时器里调用就需要管理了吧
2.canDispatch()怎么用,怎么才能和固件CAN的发送和接收联系到一起
3.我只有一块F1板子,回环模式能测试到效果吗?
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-3 16:40:31 | 显示全部楼层
NewGuard 发表于 2019-1-3 16:24
1.TimeDispatch()在ms定时器里调用就需要管理了吧
2.canDispatch()怎么用,怎么才能和固件CAN的发送 ...

canDispatch()需要放在CAN中断接收里面,通过这个函数把接收的数据发送给协议栈处理。
只有一个F1的板子肯定是不行的,你遵守了CANOPEN协议,那么你就应该把数据发送出来,通过USB_CAN来观察报文。
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-3 17:58:14 | 显示全部楼层
whj467467274672 发表于 2019-1-3 16:40
canDispatch()需要放在CAN中断接收里面,通过这个函数把接收的数据发送给协议栈处理。
只有一个F1的板 ...

//中断接收函数
//USB_LP_CAN1_RX0_IRQHandler->HAL_CAN_IRQHandler->CAN_Receive_IT->HAL_CAN_RxCpltCallback
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)
{
        __HAL_CAN_CLEAR_FLAG(hcan,CAN_IT_FMP0); //清除标志位
        HAL_CAN_Receive_IT(hcan, CAN_FIFO0); //重新开启接收中断
        __HAL_CAN_ENABLE_IT(hcan,CAN_IT_FMP0); //使能中断       
       
        can_msg.cob_id = hcan->pRxMsg->StdId;
        can_msg.rtr = hcan->pRxMsg->RTR;
        can_msg.len = hcan->pRxMsg->DLC;
        for(uint8_t i =0; i < 8; i++)
                can_msg.data = hcan->pRxMsg->Data;
        canDispatch(&TestMaster_Data, &can_msg);    //处理消息
       
        //printf("HAL_CAN_RxCpltCallbackata[0]= 0x%X,Data[1]=0x%X \r\n",hcan->pRxMsg->Data[0],hcan->pRxMsg->Data[1]);
        UART_Mixed_TxTrigger(&muart1,(char *)hcan->pRxMsg->Data,hcan->pRxMsg->DLC);       
}

我大概就这么调用的,回头买个USBCAN调试工具再看看情况吧
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-4 09:02:18 | 显示全部楼层
NewGuard 发表于 2019-1-3 17:58
//中断接收函数
//USB_LP_CAN1_RX0_IRQHandler->HAL_CAN_IRQHandler->CAN_Receive_IT->HAL_CAN_RxCpltCa ...

你这个接收回调函数吧,大概看了下没问什么问题, 你可以先测试一下。你这里是不是应该做一下清除定时器的中断呢?
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-4 09:26:48 | 显示全部楼层
whj467467274672 发表于 2019-1-4 09:02
你这个接收回调函数吧,大概看了下没问什么问题, 你可以先测试一下。你这里是不是应该做一下清除定时器 ...

不太明白,清除定时器什么意思?TimeDispatch()吗?
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-4 09:52:39 | 显示全部楼层
TimeDispatch()是需要在硬件定时器里面定时调用的。
回复 支持 反对

使用道具 举报

0

主题

128

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
205
金钱
205
注册时间
2018-12-31
在线时间
10 小时
发表于 2019-1-4 09:58:46 | 显示全部楼层
可以的,好好学一下!!!
回复 支持 反对

使用道具 举报

1

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2018-10-15
在线时间
17 小时
发表于 2019-1-6 23:11:54 | 显示全部楼层
你好,请教一下,我用Objdictedit来生成字典,为什么我的PDO的映射参数表里面无法映射?只有毫无和Error Register两个选项
q1.png
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-8 08:39:23 | 显示全部楼层
tonghan2009 发表于 2019-1-6 23:11
你好,请教一下,我用Objdictedit来生成字典,为什么我的PDO的映射参数表里面无法映射?只有毫无和Error Re ...

因为可以映射的地址你没有配置
回复 支持 反对

使用道具 举报

1

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2018-10-15
在线时间
17 小时
发表于 2019-1-9 13:47:39 | 显示全部楼层
嗯嗯,确实是,谢谢
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-9 17:07:59 | 显示全部楼层
whj467467274672 发表于 2019-1-4 09:02
你这个接收回调函数吧,大概看了下没问什么问题, 你可以先测试一下。你这里是不是应该做一下清除定时器 ...

1.首先谢谢楼主的无私帮助
2.定时器中断我用的是HAL库的延时函数中断,直接在里面timerForCan();
3.买的USBCAN调试工具到了,我测试了下会返回数据(附有图片),但是我设置的节点是0x08
为什么发给我的确实0x388和0x708不太明白?
4.然后心跳包是只有主机发送吗?我用的从机为啥没有数据,0x1017也配置了具体值
5.这样canopen算跑起来了吗?具体的接收数据处理是不是还需要自己另外操作,还是只要字典里面修改就可以了,我到现在还是迷茫到要死。

再次感谢楼主!



2019-01-09_165535.jpg
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-9 21:20:18 | 显示全部楼层
NewGuard 发表于 2019-1-9 17:07
1.首先谢谢楼主的无私帮助
2.定时器中断我用的是HAL库的延时函数中断,直接在里面timerForCan();
3.买 ...

这应该算是把协议栈跑起来了,但是距离正在的使用,我觉得还有待学习。
至于为什么这么说,是因为你还不知道COB_id是什么,我简单的说一下,你自己再去查查资料。
0X708就是COB_ID+NODE_ID,你现在应该收到的数据是0x708 05或者0x708 7F。0x708是COB_ID+NODE_ID    05表示节点状态。
canopen没有严格的主从机之分,常常大家说的主机是指具备网络管理的那个节点,你可以把这个功能赋予到任何节点,但是一般只赋予一个,那么他就认为是主机其他的节点都是一样的了。
具体的接收数据你自己再研究一下吧。
有问题欢迎再看过资料之后再问,这样更高沟通。
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-10 09:54:39 | 显示全部楼层
whj467467274672 发表于 2019-1-9 21:20
这应该算是把协议栈跑起来了,但是距离正在的使用,我觉得还有待学习。
至于为什么这么说,是因为你还不 ...

谢谢!
的确自己应该主动去多看下资料,对自己提高也有益处!
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-10 13:16:02 | 显示全部楼层
NewGuard 发表于 2019-1-10 09:54
谢谢!
的确自己应该主动去多看下资料,对自己提高也有益处!

可以通过USB_CAN能控制LED的亮灭基本上就算会用了
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-10 16:39:26 | 显示全部楼层
本帖最后由 NewGuard 于 2019-1-10 16:41 编辑
whj467467274672 发表于 2019-1-10 13:16
可以通过USB_CAN能控制LED的亮灭基本上就算会用了

之前接收的内容一个是类似节点上线通知和心跳报文状态,但现在遇到一个奇怪的问题,心跳报文的时间间隔和设置的时间不一样(设置0x64),大概会是什么问题?
2019-01-10_161456.jpg
2019-01-10_164008.jpg
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-10 17:33:54 | 显示全部楼层
本帖最后由 NewGuard 于 2019-1-10 17:39 编辑
whj467467274672 发表于 2019-1-10 13:16
可以通过USB_CAN能控制LED的亮灭基本上就算会用了

另外对接收函数做了处理,不知道是不是这样?如果是正确的话,只需要把相应printf的内容更改为对数据处理就可以了,PC上位机发送的CANID:0x208(PDOrx)、0x188(PDOtx)
其中对PDO的映射不太明白有什么用,还望解惑,谢谢!

1.jpg
2.jpg
3.jpg
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-11 09:09:56 | 显示全部楼层
NewGuard 发表于 2019-1-10 16:39
之前接收的内容一个是类似节点上线通知和心跳报文状态,但现在遇到一个奇怪的问题,心跳报文的时间间隔和 ...

节点上线通知的专业术语叫BOOT UP。 心跳不准的话,你要看下定时器的事。
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-11 09:15:20 | 显示全部楼层
NewGuard 发表于 2019-1-10 17:33
另外对接收函数做了处理,不知道是不是这样?如果是正确的话,只需要把相应printf的内容更改为对数据处理 ...

打印函数都是错误把,你好好看看,你不要乱改。关于PDO映射的东西,百度一大堆,你先看一下,具体哪一个地方不懂你再问,你这么问,需要说的东西太多了。
回复 支持 反对

使用道具 举报

6

主题

110

帖子

0

精华

高级会员

Rank: 4

积分
515
金钱
515
注册时间
2017-3-1
在线时间
95 小时
发表于 2019-1-14 09:38:06 | 显示全部楼层
本帖最后由 NewGuard 于 2019-1-14 17:57 编辑
whj467467274672 发表于 2019-1-11 09:15
打印函数都是错误把,你好好看看,你不要乱改。关于PDO映射的东西,百度一大堆,你先看一下,具体哪一个 ...

感谢回复!
1.心跳包的问题,我用示波器测试了定时器是1ms轮循一次timerForCan()函数,我把函数文件放出来
2.打印函数错误不要乱改,是指void canDispatch(CO_Data* d, Message *m)函数不能修改,但是如何对接收数据处理呢?应该有需要更改的地方吧,比如点灯之类的操作3.TPDO和RPDO都有参数设置和一一映射的关系,对于映射到其它相应地址、子索引和空间占用大小,但是如上我的疑问一样,我还是不太明白具体的处理函数不是我自己
写吗?我只有一个板子和USBCAN上位机,怎么才能做到在协议中去控制LED的开关(底层的处理函数),能指点下思路,我总是想不通这点,谢谢!



uint32_t TimeCNT=0;//时间计数
uint32_t NextTime=0;//下一次触发时间计数
uint32_t TIMER_MAX_COUNT = 70000;//最大时间计数
static TIMEVAL last_time_set = TIMEVAL_MAX;//上一次的时间计数


//源码用来定时的,时间到了就需要调用一下函数TimeDispatch()
void setTimer(TIMEVAL value)
{
  NextTime = (TimeCNT+value)%TIMER_MAX_COUNT;                                                                                                                                                        
}

//源码用来查询距离下一个定时触发还有多少时间
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;                                                                                                                                
}

//没1ms要调用一次下面的函数
void timerForCan(void)
{
        TimeCNT++;
        if(TimeCNT >= TIMER_MAX_COUNT)
        {
                TimeCNT=0;
        }
        if(TimeCNT == NextTime)
        {
                TimeDispatch();
        }
}

//canSend 会被canfestival调用
UNS8 canSend(CAN_PORT notused, Message *m)
{
        return CAN1_Send_Msg((Message *) m);   //发送CAN消息
}

UNS8 canChangeBaudRate(CAN_PORT port, char* baud)
{
    return 0;
}


clipboard.png
回复 支持 反对

使用道具 举报

21

主题

299

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1677
金钱
1677
注册时间
2017-5-30
在线时间
278 小时
 楼主| 发表于 2019-1-15 08:50:30 | 显示全部楼层
NewGuard 发表于 2019-1-14 09:38
感谢回复!
1.心跳包的问题,我用示波器测试了定时器是1ms轮循一次timerForCan()函数,我把函数文件放出 ...

第一个心跳的问题,你看下我前面给别人的回答,有个地方需要修改定时器的最大计数器,你看看你用的是不是16位的,这里你改一下看看。
第二个问题,协议栈不需要修改,对接收到的数据进行处理最后都指向了对象字典,你需要做的就是对象字典的操作。
第三个问题,如何控制LED,假如你在对象字典里有一个变量,初始值是00,那么你可以通过发送SDO的方式来修改这个变量的值,你在程序里判断变量不为0的时候LED就点亮。
回复 支持 反对

使用道具 举报

527

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
137214
金钱
137214
注册时间
2010-12-1
在线时间
1410 小时
发表于 2019-1-22 01:50:24 | 显示全部楼层
谢谢分享
回复 支持 反对

使用道具 举报

527

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
137214
金钱
137214
注册时间
2010-12-1
在线时间
1410 小时
发表于 2019-1-22 01:51:22 | 显示全部楼层
cool
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则




关闭

正点原子STM32/FPGA资料免费下载上一条 /1 下一条

正点原子公众号

QQ|联系我们|手机版|官方淘宝店|微信公众平台|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2019-2-20 08:51

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表
/* */