×
嵌入式 > 技术百科 > 详情

NEC V850 之 定时器TMM0

发布时间:2020-06-20 发布时间:
|
时钟的事情搞定了,下面就是和时间有关系的定时器了。看了下有3个定时器分别是AA,AB,M。其中M是最简单的一个,只有一个功能就是计数。感觉和STM32的systick有异曲同工之妙。就是为操作系统留的心跳时钟啊。所以先搞他吧。

先说说这个TMM的时钟特性吧,有一个16位的比较寄存器(TM0CMP0),最大计数范围就是0~65535了。另外定时器TMM0不支持自动重装初值,所以在中断服务函数里除了要清标志位还要重新装入初值。然后这个定时器机器简单只有一个可屏蔽中断就是溢出中断。

下面说下他的计数时间的计算方法。关于系统时钟和内部设备时钟在上一篇博文里说过了,系统时钟如果是32MHz,那么内部设备时钟也是32MHz,如果想分频只有在具体的外设分频器处分频。

  系统时钟是32MHz;

  TMM0分频系数为不分频,则TMM0的时钟是32MHz;

  一个时钟定时器记一次数,则记一次数的时间是1 / 32MHz = 0.03125 us;

 假如我现在需要定时1ms就是1000us,需要的计数次数就是 1000us / 0.03125us = 32000 次。

所以应该给比较寄存器(TM0CMP0)赋值(32000 - 1)次,因为在定时器计时开始时需要消耗3~4个时钟周期,所以我们不减去1,直接给比较寄存器赋值32000次,做为误差补偿,但是随着计时时长的增加,误差会在一定程度上积累。大家要根据具体需要来进行设置校准。

现在我们知道在系统时钟为32MHz时,TMM0不分频的最大计时为65536 * 0.03125 = 2.048 ms,如果我们需要定时的时长比较长,就需要对TMM0的时钟进行分频,如果我们需要的定时时长为1s,

  系统时钟是32MHz;

  TMM0分频系数设置为512分频,则TMM0的时钟是 32000000Hz  / 512 = 62500Hz;

  定时器记一次数的时间就是  1 / 62500Hz = 0.000016s = 16 us;

  我们需要的定时时长是1s,需要的计数次数为 1s = 1000000us / 16us = 62500 次。

定时器的定时范围是有最大值的,同时要注意的是,他也是有最小值的,这就是他的I/O口翻转速率,经过测试,70f3630这款MCU在定时器TMM0下,最小计数单元为5us,如果再低就意味着I/O口的翻转速率更高,则会得到比较大的误差值,下面有一个对照表来列举一组测试说明,并会附图。

 

 计算定时时长  比较寄存器(TM0CMP0)值  实际测量时长  备注
 1us  32      (TMM0 = fxx)  2.0625us  附图1
 5us  160    (TMM0 = fxx)  5.063us  附图2
 10us  320    (TMM0 = fxx)  10.031us  
 30us  960    (TMM0 = fxx)  30.001us  
 50us  1600  (TMM0 = fxx)  50.06us  
 1ms  32000(TMM0 = fxx)  1.000ms  附图3
 1s  62500(TMM0 = fxx/512)  999.9ms  
 上面这个表说明了,在定时时长很小的时候误差很大,在定时时长很大的时候,虽然相对误差小,但是也有比较大的绝对误差,所以这其中的数值补偿要根据实际情况具体问题具体分析。下面来看下附图:

 

 
 说完了理论,来说说代码部分。操作TMM0需要用到3个寄存器分别是
  •  可屏蔽中断控制寄存器(定时器M )       TM0EQIC0
  •  TMM0 比较寄存器0                                   TM0CMP0
  •  TMM0 控制寄存器0                                   TM0CTL0

 程序操作上有3部分,分别是 开始定时器 , 关闭定时器 , 定时器初始化。

  • 开始定时器(TMM0_Start)
    1. 清TMM0中断标志位,设置寄存器 TM0EQIC0 的第7位 TM0EQIF0 为 0 ;
    2. 允许响应TMM0中断,设置寄存器 TM0EQIC0 的第6位 TM0EQMK0 为 0;
    3. 开定时器使能端,设置寄存器TM0CTL0 的第7位 TM0CE 为 1 。        
  • 关闭定时器(TMM0_Stop)
    1. 关定时器使能端,设置寄存器TM0CTL0 的第7位 TM0CE 为 0;
    2. 禁止响应TMM0中断,设置寄存器 TM0EQIC0 的第6位 TM0EQMK0 为 1;
    3. 清TMM0中断标志位,设置寄存器 TM0EQIC0 的第7位 TM0EQIF0 为 0 。
  • 定时器初始化(TMM0_Init)
    1. 关闭定时器,执行TMM0_Stop;
    2. 设置中断优先级,设置寄存器 TM0EQIC0 的后3位 ,确定TMM0的中断优先级;
    3. 设置定时器分频系数,设置寄存器 TM0CTL0 的后3位,确定TMM0的运行时钟;
    4. 设置比较寄存器初值,设置寄存器 TM0CMP0的值,确定触发溢出中断的计数次数。

有了上面的准备,下面我们在main函数里先初始化定时器然后再调用开始定时器,一定不要忘记开总中断;然后在中断服务程序里写入用户代码加上重新赋初值和清标志位的操作就可以了,这样定时器就能够正常工作了,下面是部分代码,请参阅:


代码出处:timer.c

/*******************************************************************************
* Function Name : TMM0_Start
* Description : 开始TMMO计数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TMM0_Start( void )
{
TM0EQIF0 = 0; // 设置TM0EQIC0 的第7位中断标志位位 0 ,清除定时器M0的中断响应标志
TM0EQMK0 = 0; // 设置TM0EQIC0 的第6位中断屏蔽位为 0 ,允许产生定时器M0中断
TM0CE = 1; // 设置TM0CTL0 的最高位TM0CE为 1 ,定时器计数启动
}

/*******************************************************************************
* Function Name : TMM0_Stop
* Description : 停止TMM0计数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TMM0_Stop( void )
{
TM0CE = 0; // 设置TM0CTL0 的最高位TM0CE为 0 ,禁止定时器时钟启动
TM0EQMK0 = 1; // 设置TM0EQIC0 的第6位中断屏蔽位为 1 ,禁止产生定时器M0中断
TM0EQIF0 = 0; // 设置TM0EQIC0 的第7位中断标志位位 0 ,清除定时器M0的中断响应标志
}

/*******************************************************************************
* Function Name : TMM0_Init
* Description : TMM0初始化
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TMM0_Init( void )
{
TMM0_Stop(); // 首先禁止定时器

TM0EQIC0 |= 0x07; // 设置TM0EQIC0 的低3位 中断优先级值 全为1,为最低中断优先级
TM0CTL0 = TMM_INTERNAL_CLOCK0 ; // 设置TMOCTL0 的低3位TMM0预分频器值 全为0 ,不分频
TM0CMP0 = 32000; // 设置TM0CMP0 定时器计数比较寄存器 的值为32000,初值定时为1ms
}

/*******************************************************************************
* Function Name : TMM0_ChangeTimerCondition
* Description : 修改定时器计数变量值
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TMM0_ChangeTimerCondition( unsigned short regvalue )
{
TM0CMP0 = regvalue; // 设置TM0CMP0 定时器计数比较寄存器
}

/*******************************************************************************
* Function Name : MD_INTTM0EQ0
* Description : 定时器TMM0溢出中断服务函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
#pragma vector = INTTM0EQ0_vector
__interrupt void MD_INTTM0EQ0(void)
{
P9L_bit.no4 = ~P9L_bit.no4 ; // 端口P94翻转,LED闪烁

TM0CMP0 = 32000; // 定时器TMM0不支持自动重装初值,一定要在执行完用户程序后重装初值
TM0EQIF0 = 0; // 清除定时器中断标志位
}

在高亮处更改TMM0分频系数。

代码出处:main.c

void main( void )
{
SystemClkInit(); // 初始化系统时钟为32MHz
/* PCL setting */
PCLM = CG_PCL_DISABLE; // 关闭可编程时钟输出

__DI(); // 关闭总中断
PMC9L_bit.no4 = 0 ; // 设置P94为输入输出口模式
PM9L_bit.no4 = 0 ; // 设置P94端口方向为输出
TMM0_Init(); // 定时器TMM0初始化
__EI(); // 打开总中断

TMM0_Start(); // 定时器TMM0开始计数

while (1);
}

最后把相关寄存器的datasheet截图上传下:

 

 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
联发科高端芯片系列出新品Helio P10 中文名[曦力"