NUC140之定时器

来源:互联网 发布:mac jdk 默认安装路径 编辑:程序博客网 时间:2024/05/17 03:46

相信用过芯塘NUC1xx系列单片机的朋友们对NUC1xx 固件库函数一定不陌生.

本人用的固件库版本是V1.05.001,在用定时器的库函数的时候发现了一个问题:

本人用定时器监控一个信号,这个信号平时是高电平,当它变为低电平的时候,启动定时器,开始计时,假设计时时间为120S,

如果这个信号保持低电平120S,则会触发中断函数,从而使这个信号不再起作用,

如果在这120S内,这个信号恢复高电平,则重置计数器,在下次这个信号变为低电平的时候,从新计数。

以下是本人设置的函数:

/**************************************************
 *函数名: initialize_timer       *
 *功能: 初始化timer必须调用的函数             *
 *出口参数:无           *
 *入口参数:无           *
 *************************************************/

void initialize_timer (void)
{
 DrvTIMER_Init (); //初始化timer必须调用的函数
}

 

/**************************************************
 *函数名: initialize_timer0       *
 *功能: 初始化timer0                          *
 *出口参数:无           *
 *入口参数:无           *
 *************************************************/

void initialize_timer0 (void)
{
 DrvSYS_UnlockProtectedReg ();                         //解锁受保护的系统寄存器
 DrvSYS_SelectIPClockSource (E_SYS_TMR0_CLKSRC, 0x00); //设置定时器0的时钟源为外部12M晶振
 DrvSYS_LockProtectedReg ();                           //对系统寄存器上锁
 delay (50);                      //延迟使时钟稳定
 DrvTIMER_Open (E_TMR0,           //使能定时器0
       1,                        //每秒计数1次
                   E_PERIODIC_MODE);             //单周期模式
}

 

/**************************************************
 *函数名: setup_ptt_limit_time      *
 *功能: 调整发定时时间                        *
 *出口参数:无           *
 *入口参数:无           *
 *************************************************/
void setup_ptt_limit_time (int32_t PTT_limit_time_)    //参数最后加"_"是为了区别全局变量
{
 DrvTIMER_SetTimerEvent (E_TMR0,        //定时器0
          PTT_limit_time_,            //发定时时间,单位为秒
       PTT_callback,                 //发定时回调函数,回调函数类型必须为typedef void (*TIMER_CALLBACK)(uint32_t data); /* function pointer */,否则应像例子那样用强制转换类型符
          0);         //传给回调函数的参数,暂无用
}

 

/**************************************************
 *函数名: PTT_callback             *
 *功能: PTT定时回调函数,用于停发             *
 *出口参数:无           *
 *入口参数:有,暂时无用        *
 *************************************************/
void PTT_callback (uint32_t callback_data)
{

 DrvTIMER_DisableInt(E_TMR0);       //关定时器0中断,发定时一直起作用的时候无需多次进入该中断中
 DrvTIMER_ClearIntFlag(E_TMR0);                          //清空定时器0中断标志位
 PTT_time_off = 1;                                       //发定时到
}

简单的讲:就是先设定1秒中断几次,假如说想要200ms的中断,就设置1秒计数5次,计数1次就进入中断,

如果想要的是400ms,就设置1秒计数5次,计数两次进入中断

 

本人发现用定时器的one-shot模式,根本进不到中断函数中,只好用单周期模式,不知何解。

严归正传以上的函数是用以初始化定时器,并设置用户的中断服务函数,其实我们之后可以看到

用户设置的中断函数,并不是真的中断部分函数,而是在厂家的固件库函数里,通过函数指针来调用用户的中断函数。

笔者用以上的几个函数来初始化定时器中断

 

之后是笔者用来启动定时器和清除定时计数次数的函数

 if ((enable_PTT_limit == 1) && (active_PTT_limit == 0)) //如果使能发定时并且没有使能定时器0中断
 {
  DrvTIMER_EnableInt (E_TMR0);  //使能定时器0中断
  DrvTIMER_Start (E_TMR0);         //定时器0开始计时
  active_PTT_limit = 1;           //发定时已激活
 }

  else if ((PTT_mic == 0) && (PTT_remote == 0))
  {
   DrvTIMER_Stop(E_TMR0);           //暂停定时0并清空当前计时
   PTT_time_off = 0;                //清除发定时到时标志
   PTT_start = 0;                   //PTT未发起
   active_PTT_limit = 0;           //清除发定时已使能标志,使得定时器可以在PTT_on中再次被激活
  }

其中DrvTIMER_Stop(E_TMR0);          这个函数是笔者自己写的

这样就能完成笔者所要的目的了,其中使能中断和定时器开始计数,只不过是对TCSR这个寄存器的几个控制位进行操作,在此就不贴出了

 

之后我们来看固件库函数,笔者一共用到的固件库函数如下:大家只用注意看有中文注释的部分即可

另外大家要对TIMER的寄存器有所了解 具体可参看NUC130-140数据手册

还有注意看红体字的部分那是很重要的

由于笔者只用定时器0,而且其他定时器的函数都是重复的,建议大家只看定时器0即可

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvTIMER_Open                                                                          */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  ch - [in]                                                                              */
/*                      E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3                           */  
/*                  uTicksPerSecond - [in]                                                                 */
/*                      This value means how many timer interrupt ticks in one second                      */
/*                  op_mode - [in]                                                                         */
/*                      E_TIMER_OPMODE, E_ONESHOT_MODE/E_PERIODIC_MODE/E_TOGGLE_MODE/E_CONTINUOUS_MODE     */
/* Returns:                                                                                                */
/*                  E_SUCCESS                               Operation successful                           */
/*                  E_DRVTIMER_CHANNEL                      Invalid Timer channel                          */
/*                  E_DRVTIMER_CLOCK_RATE                   Calculate initial value fail                   */
/* Description:                                                                                            */
/*                  Open the specified timer channel with specified operation mode.                        */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvTIMER_Open(E_TIMER_CHANNEL ch, uint32_t uTicksPerSecond, E_TIMER_OPMODE op_mode)
{
    uint32_t i;
    uint32_t uRegTcmpr, uRegTcr = 0x0;

    switch (ch)
    {
        case E_TMR0:
        {
            if ((bIsTimer0Initial != TRUE) || (bIsTimer0Used != FALSE))
                return E_DRVTIMER_EIO;
           
            bIsTimer0Used = TRUE;   //定时器0正在使用标志
          
            SYSCLK->APBCLK.TMR0_EN = 1;  //打开定时器0的APB时钟
                                                             
            outpw((uint32_t)&TIMER0->TCSR, 0);                  /* disable timer,这个很巧妙,是将TCSR整个32位全部赋0,注意看强制转换uint32_t并且按位与 */
          
            TIMER0->TISR.TIF = 1;                               /* write 1 to clear for safty ,清定时器0中断标志位*/
                              
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime0Event[i].active = FALSE;      //设置定时器0事件未激活,给下一个事件设置函数做准备
            }

            uTimer0Tick = 0;                   //定时器0 Tick设置为0
            _sys_uTimer0TickPerSecond = uTicksPerSecond;   //将用户设置的1秒内Tick数传递给_sys_uTimer0TickPerSecond
                   
            uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR0), uTicksPerSecond);    //进行计算
            if (uRegTcmpr == (uint32_t)-1)     
            {
                return E_DRVTIMER_CLOCK_RATE;          
            }

            TIMER0->TCMPR = (uRegTcmpr << 8) >> 8;            //对24位定时器比较寄存器赋值,注意移位运算
            outpw((uint32_t)&TIMER0->TCSR,  (uRegTcr|(uRegTcmpr>>24))|(op_mode<<27));  //初始化TCSR           
            break;
        }

        case E_TMR1:
        {
            if ((bIsTimer1Initial != TRUE) || (bIsTimer1Used != FALSE))
                return E_DRVTIMER_EIO;

            bIsTimer1Used = TRUE;

            SYSCLK->APBCLK.TMR1_EN = 1;
           
            outpw((uint32_t)&TIMER1->TCSR, 0);                  /* disable timer */
           
            TIMER1->TISR.TIF = 1;                               /* write 1 to clear for safty */

            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime1Event[i].active = FALSE;
            }

            uTimer1Tick = 0;
            _sys_uTimer1TickPerSecond = uTicksPerSecond;

            uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR1), uTicksPerSecond);
            if(uRegTcmpr == (uint32_t)-1)      
            {
                return E_DRVTIMER_CLOCK_RATE;          
            }

            TIMER1->TCMPR = (uRegTcmpr << 8) >> 8;
            outpw((uint32_t)&TIMER1->TCSR,  (uRegTcr|(uRegTcmpr>>24))|(op_mode<<27));  
            break;
        }

        case E_TMR2:
        {
            if ((bIsTimer2Initial != TRUE) || (bIsTimer2Used != FALSE))
                return E_DRVTIMER_EIO;   

            bIsTimer2Used = TRUE;

            SYSCLK->APBCLK.TMR2_EN =1;
           
            outpw((uint32_t)&TIMER2->TCSR, 0);                  /* disable timer */

            TIMER2->TISR.TIF = 1;                               /* write 1 to clear for safty */

            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime2Event[i].active = FALSE;
            }

            uTimer2Tick = 0;
            _sys_uTimer2TickPerSecond = uTicksPerSecond;

            uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR2), uTicksPerSecond);
            if(uRegTcmpr == (uint32_t)-1)      
            {
                return E_DRVTIMER_CLOCK_RATE;          
            }

            TIMER2->TCMPR = (uRegTcmpr << 8) >> 8; 
            outpw((uint32_t)&TIMER2->TCSR,  (uRegTcr|(uRegTcmpr>>24))|(op_mode<<27));  
            break;
        }

        case E_TMR3:
        {
            if ((bIsTimer3Initial != TRUE) || (bIsTimer3Used != FALSE))
                return E_DRVTIMER_EIO;

            bIsTimer3Used = TRUE;

            SYSCLK->APBCLK.TMR3_EN = 1;

            outpw((uint32_t)&TIMER3->TCSR, 0);                  /* disable timer */

            TIMER3->TISR.TIF = 1;                               /* write 1 to clear for safty */

            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                tTime3Event[i].active = FALSE;
            }

            uTimer3Tick = 0;
            _sys_uTimer3TickPerSecond = uTicksPerSecond;
           
            uRegTcmpr = CalTimerInitValue(GetTimerClock(E_TMR3), uTicksPerSecond);
            if(uRegTcmpr == (uint32_t)-1)      
            {
                return E_DRVTIMER_CLOCK_RATE;          
            }

            TIMER3->TCMPR = (uRegTcmpr << 8) >> 8;
            outpw((uint32_t)&TIMER3->TCSR,  (uRegTcr|(uRegTcmpr>>24))|(op_mode<<27));   
            break;
        }

        default:
        {
            return E_DRVTIMER_CHANNEL ;
        }
    }

    if (op_mode == E_TOGGLE_MODE)
    {
        switch (ch)
        {
            case E_TMR0:
            case E_TMR1:
            case E_TMR2:
      case E_TMR3:
            {
       DrvGPIO_InitFunction((E_DRVGPIO_FUNC)((uint32_t)E_FUNC_TMR0 + (uint32_t)ch)); 
            }
   
            default:
            {
                return E_DRVTIMER_CHANNEL ;
            }
        }
    }

    return E_SUCCESS;
}

 

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvTIMER_SetTimerEvent                                                                 */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  ch - [in]                                                                              */
/*                      E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3                           */  
/*                  uInterruptTicks - [in]                                                                 */
/*                      Number of timer interrupt occurred                                                 */
/*                  pTimerCallback  - [in]                                                                 */
/*                      The function pointer of the interrupt callback function                            */
/*                  parameter - [in]                                                                       */
/*                      A parameter of the callback function                                               */
/*                                                                                                         */
/* Returns:                                                                                                */       
/*                  uTimerEventNo                           The timer event number                         */
/*                  E_DRVTIMER_EVENT_FULL                   The timer event is full                        */
/* Description:                                                                                            */
/*                  Install the interrupt callback function of the specified timer channel.                */
/*                  And trigger timer callback functuion when interrupt occur specified times.             */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvTIMER_SetTimerEvent(E_TIMER_CHANNEL ch, uint32_t uInterruptTicks, TIMER_CALLBACK pTimerCallback, uint32_t parameter)
{
    volatile int32_t i;
    int32_t uTimerEventNo = 0;

    switch (ch)
    {
        case E_TMR0:
        {
            if (uTime0EventCount >= TIMER_EVENT_COUNT)    //如果已经设置事件了
                return E_DRVTIMER_EVENT_FULL;      //返回事件已满

            bIsSetTime0Event = TRUE;        //有事件
            uTime0EventCount++;          //设置事件计数器加1
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                if (tTime0Event[i].active  == FALSE)    //如果定时器0事件激活状态为0
                {
                    tTime0Event[i].active       = TRUE;    //设置激活状态为1
                    tTime0Event[i].initTick     = uInterruptTicks;  //将传入的多少次Tick进入用户中断赋值给初始化Tick及当前Tick两个变量
                    tTime0Event[i].curTick      = uInterruptTicks;  //当前Tick数
                    tTime0Event[i].funPtr       = (TIMER_CALLBACK)pTimerCallback; //用户自定义的回调函数
                    tTime0Event[i].transParam   = parameter;   //用户自定义的回调函数的参数
                    uTimerEventNo = i;
                    break;
                }
            }
            break;
        }

        case E_TMR1:
        {
            if (uTime1EventCount >= TIMER_EVENT_COUNT)
                return E_DRVTIMER_EVENT_FULL;

            bIsSetTime1Event = TRUE;
            uTime1EventCount++;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                if (tTime1Event[i].active   == FALSE)
                {
                    tTime1Event[i].active       = TRUE;
                    tTime1Event[i].initTick     = uInterruptTicks;
                    tTime1Event[i].curTick      = uInterruptTicks;
                    tTime1Event[i].funPtr       = (TIMER_CALLBACK)pTimerCallback;
                    tTime1Event[i].transParam   = parameter;
                    uTimerEventNo = i;
                    break;
                }
            }
            break;
        }

        case E_TMR2:
        {
            if (uTime2EventCount >= TIMER_EVENT_COUNT)
                return E_DRVTIMER_EVENT_FULL;

            bIsSetTime2Event = TRUE;
            uTime2EventCount++;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                if (tTime2Event[i].active   == FALSE)
                {
                    tTime2Event[i].active       = TRUE;
                    tTime2Event[i].initTick     = uInterruptTicks;
                    tTime2Event[i].curTick      = uInterruptTicks;
                    tTime2Event[i].funPtr       = (TIMER_CALLBACK)pTimerCallback;
                    tTime2Event[i].transParam   = parameter;
                    uTimerEventNo = i;
                    break;
                }
            }
            break;
        }

        case E_TMR3:
        {
            if (uTime3EventCount >= TIMER_EVENT_COUNT)
                return E_DRVTIMER_EVENT_FULL;

            bIsSetTime3Event = TRUE;
            uTime3EventCount++;
            for (i=0; i<TIMER_EVENT_COUNT; i++)
            {
                if (tTime3Event[i].active   == FALSE)
                {
                    tTime3Event[i].active       = TRUE;
                    tTime3Event[i].initTick     = uInterruptTicks;
                    tTime3Event[i].curTick      = uInterruptTicks;
                    tTime3Event[i].funPtr       = (TIMER_CALLBACK)pTimerCallback;
                    tTime3Event[i].transParam   = parameter;
                    uTimerEventNo = i;
                    break;
                }
            }
            break;
        }

        default:
        {
            break;
        }
    }

    return uTimerEventNo;
}

 

固件库里给了两个函数,看名字貌似是获得当前计数次数和清除当前计数次数我们来看函数:

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvTIMER_GetIntTicks                                                                   */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  ch - [in]                                                                              */
/*                      E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3                           */  
/* Returns:                                                                                                */
/*                  uTimerTick                              Return the interrupt ticks                     */
/*                  E_DRVTIMER_CHANNEL                      Invalid Timer channel                          */
/* Description:                                                                                            */
/*                  This function is used to get the number of interrupt occurred                          */
/*                  after the timer interrupt function is enabled.                          .              */
/*                  Thus DrvTIMER_EnableInt(ch) must been called in advance.                               */
/*---------------------------------------------------------------------------------------------------------*/
uint32_t DrvTIMER_GetIntTicks(E_TIMER_CHANNEL ch)
{
    switch (ch)
    {
        case E_TMR0:
        {
            return uTimer0Tick; 
        }

        case E_TMR1:
        {
            return uTimer1Tick;
        }  

        case E_TMR2:
        {
            return uTimer2Tick;
        }

        case E_TMR3:
        {
            return uTimer3Tick;
        }

        default:
        {
            return E_DRVTIMER_CHANNEL;
        }
    }
}

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvTIMER_ResetIntTicks                                                                 */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  ch - [in]                                                                              */
/*                      E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3                           */  
/* Returns:                                                                                                */
/*                  E_SUCCESS                               Operation successful                           */
/*                  E_DRVTIMER_CHANNEL                      Invalid Timer channel                          */
/* Description:                                                                                            */
/*                  This function is used to clear interrupt ticks to 0.                                   */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvTIMER_ResetIntTicks(E_TIMER_CHANNEL ch)
{
    switch (ch)
    {
        case E_TMR0:
        {
            uTimer0Tick = 0;
            break;
        }

        case E_TMR1:
        {
            uTimer1Tick = 0;
            break;
        }

        case E_TMR2:
        {
            uTimer2Tick = 0;
            break;
        }

        case E_TMR3:
        {
            uTimer3Tick = 0;
            break;
        }

        default:
        {
            return E_DRVTIMER_CHANNEL;
        }
    }

    return E_SUCCESS;
}

 

我们可以看到,所谓的获取计数次数和重置计数次数不过是操作了  uTimer2Tick这个全局变量而已。

接下来我们来看最重要的部分:

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        TMR0_IRQHandler                                                                        */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  None                                                                                   */
/* Returns:                                                                                                */
/*                  None                                                                                   */
/* Description:                                                                                            */
/*                  The TIMER0 default IRQ, declared in startup_NUC1xx.s                                   */
/*---------------------------------------------------------------------------------------------------------*/
void TMR0_IRQHandler(void)
{
    int32_t i;
  
    if ((TIMER0->TCSR.IE == 1) && (TIMER0->TISR.TIF == 1))     //如果使能了中断且中断标志为1
        TIMER0->TISR.TIF = 1;            //写1清中断标志

    if ((TIMER0->TEXCON.TEXEN == 1) && (TIMER0->TEXCON.TEXIEN == 1) && (TIMER0->TEXISR.TEXIF == 1))
        TIMER0->TEXISR.TEXIF = 1;

    uTimer0Tick++;        //定时器0 Tick++,这个Tick数只不过是给Get_Tick和Reset_Tick这两个函数用的而已,清除它根本不能使定时器从新计时
                                             //要想让定时器从新计时,必须清除tTime0Event[i].curTick这个,由于他是递减运算,所以采用这句
            //tTime0Event[i].curTick = tTime0Event[i].initTick;   //重新装载用户设置的多少次Tick进入用户中断
    if (bIsSetTime0Event)                       /* Timer Event Handle ,如果已经设置了事件*/
    {
        for (i=0; i<TIMER_EVENT_COUNT; i++)
        {
            if (tTime0Event[i].active)
            {
                tTime0Event[i].curTick--;     //定时器0当前Tick计数器,找到了关键所在
                if (tTime0Event[i].curTick == 0)   //如果定时器当前计数器Tick值为0
                {
                    (*tTime0Event[i].funPtr)(tTime0Event[i].transParam);  //调用用户中断函数,在此用了函数指针间接调用了用户函数
                    tTime0Event[i].curTick = tTime0Event[i].initTick;   //重新装载用户设置的多少次Tick进入用户中断
                }
            }
        }
    }
}

这才是真正的问题所在,笔者曾经试验过很多种方法,就像前面所说的,当控制信号为高电平的时候,笔者希望定时器从新计数,

想让定时器不计数很简单,我们可以设置寄存器TCSRDE 的CEN位为0让它暂停计数,

也可以禁止中断,但是禁止中断后,定时器仍然会继续计数,当它溢出后,又会从新开始,

但是由于我们用的是固件库函数,我们并非直接操作的是寄存器,所以笔者在此推荐,

哪里出现的问题,从哪里找办法解决,固件库很强大,但是也不可能是面面俱到,我们可以修改库函数来满足自己的要求。

笔者其实就是想找一个函数能够清除当前计数次数。

如果用DrvTIMER_ResetIntTicks  函数会出现什么情况呢?

假如我设置的是1秒计数1次,120次计数进入中断,那么,无论我使控制信号为高为低多少次,

定时器一定会忠诚地,每120秒打断我一次,控制信号为高并不能重置计数器的次数

我相信我的注释已经非常详细了,不需要再解释什么了吧?

下面给出我自己写的暂停计数器和重置计数器计数次数函数

/*---------------------------------------------------------------------------------------------------------*/
/* Function:        DrvTIMER_Stop                                                                         */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*                  ch - [in]                                                                              */
/*                      E_TIMER_CHANNEL, it could be E_TMR0/E_TMR1/E_TMR2/E_TMR3                           */  
/* Returns:                                                                                                */
/*                  E_SUCCESS                               Operation successful                           */
/*                  E_DRVTIMER_CHANNEL                      Invalid Timer channel                          */
/* Description:                                                                                            */
/*                  暂停对应的计时器通道,并重置当前计数次数Tick                                           */
/*---------------------------------------------------------------------------------------------------------*/
int32_t DrvTIMER_Stop (E_TIMER_CHANNEL ch)
{
 TIMER_T * tTMR;

    switch (ch)
    {
        case E_TMR0:
        case E_TMR1:
        case E_TMR2:
  case E_TMR3:
        {
         tTMR = (TIMER_T *)((uint32_t)TIMER0 + CH_OFFSET[ch]);       
            tTMR->TCSR.CEN = 0;           //暂停计时器
   tTime0Event[ch].curTick = tTime0Event[ch].initTick;   //重新装载用户设置的多少次Tick进入用户中断
            return E_SUCCESS ;
        }

        default:
        {
            return E_DRVTIMER_CHANNEL ;
        }
    }
}

其实我只是修改了DrvTIMER_Start函数红字的两行,这样,由于基本上还是用的是库函数,BUG少,用着也放心。

到此就完成了这次库函数TIMER篇的一个小问题的讲解。

 

 

原创粉丝点击