OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

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

CANOPEN协议栈的移植以及使用

[复制链接]

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

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





回复

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2017-5-30
在线时间
241 小时
 楼主| 发表于 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

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2017-5-30
在线时间
241 小时
 楼主| 发表于 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,如果能解决,那么我们继续。

回复 支持 反对

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2017-5-30
在线时间
241 小时
 楼主| 发表于 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();
      }
}



回复 支持 反对

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2017-5-30
在线时间
241 小时
 楼主| 发表于 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;
        }
}


回复 支持 反对

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2017-5-30
在线时间
241 小时
 楼主| 发表于 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安装好了还有一个设置环境变量,这里就不说的太细了,不然你们什么都不查,自己就不懂脑经了。
回复 支持 反对

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2017-5-30
在线时间
241 小时
 楼主| 发表于 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
回复 支持 反对

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

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

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

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

使用道具 举报

  离线 

43

主题

214

帖子

0

精华

高级会员

Rank: 4

积分
892
金钱
892
注册时间
2012-1-10
在线时间
190 小时
发表于 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
回复 支持 反对

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

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

看附件

CanFestival-3-10.zip

4.71 MB, 下载次数: 322

回复 支持 反对

使用道具 举报

  离线 

0

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
51
金钱
51
注册时间
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 | 显示全部楼层
收藏了,谢谢楼主
回复 支持 反对

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2017-5-30
在线时间
241 小时
 楼主| 发表于 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

主题

7

帖子

0

精华

初级会员

Rank: 2

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

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

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

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

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

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

使用道具 举报

  离线 

0

主题

7

帖子

0

精华

初级会员

Rank: 2

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

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

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

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

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

使用道具 举报

  离线 

0

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
51
金钱
51
注册时间
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();的前面
回复 支持 反对

使用道具 举报

  离线 

0

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
51
金钱
51
注册时间
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
链接上的代码试了,有效。不过方式不是太一样,效果是一样的
回复 支持 反对

使用道具 举报

  离线 

19

主题

248

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1356
金钱
1356
注册时间
2017-5-30
在线时间
241 小时
 楼主| 发表于 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;
        }
}
回复 支持 反对

使用道具 举报

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

本版积分规则




关闭

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

正点原子公众号

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

GMT+8, 2018-12-19 15:15

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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