第13章 定时器Timer
13-1 相关例程演示
定时器Timer
64-Bits Timer Plus
1、TIMER
64位通用定时器控制核心板LED循环点亮,周期1秒
2、TIMER_Dual_32-bit_Chained
双32位关联定时器,定时器12为通用定时器,定时器34作为预定标器,周期5秒
3、TIMER_Dual_32-bit_UnChained
双32位独立定时器,定时器12周期1秒,定时器34周期2秒
4、TIMER_Dual_32-bit_UnChained_4-bit_Prescaler
双32位独立定时器,同时定时器34带有4位预定标定时器12周期1秒,定时器34,周期2秒,输入时钟2分频
5、WatchDog
看门狗定时器,程序异常时复位(上电复位Power On Reset)
配置串口软件secureCRT演示
如果没有这秒内没有字符输入,程序复位。
6、BUZZER
蜂鸣器
7、DCMOTOR
直流电机
8、STEPPERMOTOR
步进电机
13-2 特性及时钟

在这一期视频教程中为大家讲解TMS320C6748当中的定时器模块,几乎所有的嵌入式CPU当中都有定时器这一个外设。定时器的主要功能是帮助我们周期性的执行一些特定的程序功能或者来进行一些计数的操作。比如说我们要闪烁一个LED灯,需要每一秒闪烁一次,所以就需要定时器来为我们实现精确的定时,当然如果对时间要求不是很敏感,我们也可以使用简单的指令延时的方式进行延时,而使用定时器的好处是我们可以获得一个比较精确的时间操作。定时器还有一个功能就是计数功能,它可以对我们外部输入的脉冲进行一些计数,比如说常见的有一些传感器,它的输入就是以一些脉冲波的形式输出的,这时候我们就需要有捕获的模块来进行捕获,当然我们也可以使用专门的捕获模块,比如说在C6748中有eCAP模块(增强捕获模块)来捕获脉冲,如果脉冲波不是很复杂,我们也可以使用定时器来完成操作。
在这一期视频教程中主要讲解以下3个方面内容:

1、特性
首先是特性,也就是我们TMS320C6748当中的定时器模块有哪些特点。
2、时钟
第二部分讲解定时器模块的输入时钟,既然我们要将定时器来作为定时功能使用的时候,我们必须要知道定时器的时钟是多少,这样的话我们才能够计算出一个比较精确的定时时间。
3、模式
第三部分内容讲解C6748中的定时器模块有哪几种工作模式,在这里,定时器跟我们前面所讲过的GPIO通用输入输出口是一样的,它也可以产生中断事件,也可以产生EDMA3事件,当然对于C6000系列DSP或者对于C6748来说,中断和EDMA的配置其实也没什么太大的不同,所以在这里就不详细介绍了,在后面讲解代码时也主要讲解定时器配置部分的内容。

首先是定时器的结构,在C6748的文档中对于定时器的命名是64-Bit Timer Plus,在Timer后面还有一个Plus这个词,说明这个定时器的功能比我们之前所遇到的或者与其他某些做控制用的CPU来说,它的功能更为强大一些。而且定时器应该算是C6748所有的外设中第二个比较简单的外设,从这个框图中就可以明显的看出来。第一个简单的外设是上一集讲到的GPIO通用输入输出口。

可以看到在这张图中,定时器的结构就是几部分内容:首先就是时钟,对于定时器的时钟,它既可以来源于内部时钟,也就是C6748的内部锁相环来提供时钟,也可以由外部输入时钟,注意这个外部输入的时钟既可以是外部的时钟,也可以是外部的事件,外部事件输入以后就触发相应的中断或者是EDMA3的操作。如果输入的是外部时钟,我们就可以把这个定时器当做一个计数器来使用,比如说我们在使用的某些传感器,它的输出可能是以类似于PWM波形输出的模拟信号,这个时候我们就需要有方法来捕获这个脉冲,通过计算占空比来计算这个传感器所要传回来的一些参数。我们在捕获的时候既可以使用eCAP模块,因为C6748中海油3个eCAP模块(增强型捕获模块),如果它的输出比较简单我们也可以使用定时器来完成这个操作。在C6748定时器支持对脉冲的上下沿进行捕获。

在时钟后就是定时器模式的选择,就是选择我们定时器所要工作的模式。

之后就是关于定时器在运行过程中的一些操作,首先就是计数寄存器,一旦我们对定时器初始化完毕并且使能以后,这个定时器当中的计数寄存器每当一个时钟脉冲来临的时候,它的计数值就加1,当这个计数值达到设定的周期值的时候,通过比较模块就产生一个脉冲,而这个脉冲同时可以生成以下4个事件:
1、Timer interrupt to CPU interrupt controller,产生一个CPU中断到CPU中断控制器。
2、Timer event to DMA controller,也可以同时产生一个DMA事件到DMA控制器。
还有两个就是定时器比较特殊的地方
3、Output event to device reset,复位我们的CPU,这里需要注意的是,这个复位执行的是POR复位(Power On Reset),这个跟我们前面讲CCS的Debug操作的system reset的功能是一样的,它是将我们整个CPU当中的所有的设备包括CPU核心,外设,锁相环等等一切全部复位,然后将整个CPU内部全部模块置于一个我们全部所已知的状态下。这里需要注意的是,这个事件只能是看门狗定时器来产生的,而且在C6748中我们只能将定时器1来作为看门狗定时器。当我们把定时器1定义为看门狗定时器的时候,它就来进行一个计数,以一定的时钟周期,当计数值达到我们所设定的周期时候,就向CPU发送一个复位信号,这样的话就把我们的CPU进行复位了。你如果接触到其他的微控制器呢,可能对看门狗定时器比较了解,因为我们的微控制器在通常情况下都是应用于工业控制这样的场合,在这样的场合下我们对我们系统可靠性要求和稳定性要求都是非常高的,我们都必须让我们的程序在运行过程中的任何状态我们都必须是可知的,但是在我们的程序编写过程中可能会遇到各种各样的问题,比如说当时所处的硬件环境比较恶劣,或者我们的程序本身书写有问题,导致我们的程序在运行过程中进行了一次意外,也就是说我们常说的程序跑飞,而程序跑飞以后就并没有按我们之前所预设定的流程来进行,这个时候看门狗定时器的作用就发挥出来了,它将我们整个CPU复位,从而将我们的程序带到一个完全已知的状态再重新运行,在C6748当中的看门狗定时器也是这个功能。
4、Output event to TM64P_OUT12,还有一个输出就是输出事件到相应的输出管脚,在C6748中每一个定时器都有这样一个输出管脚,在我们定时器设置相应的模式下,达到某种触发条件就会向外输出一个事件,也就是脉冲信号。这里还需要讲一下,在这个周期寄存器旁边还有一个周期重载寄存器,它的作用是什么呢?它的作用就是改变我们定时器每次计数达到最大值的时候所要重载的那个值。
定时器工作的流程一般是这样的,在我们设定好周期后,每当脉冲来临时,它的计数值就加1,一直加到周期的时候就会产生脉冲信号,然后就会将这个计数的变量清零再重新计数,而这个时候就会将周期重载寄存器的值复制到周期寄存器当中,那么这个功能由什么用呢,比如说如果我们想用定时器的输出管脚来输出PWM波形来控制LED或者电机的时候,这个就比较有用,通过改变重载的值我们就可以输出更为丰富的PWM波形。
这里还有两个比较特殊的寄存器是控制定时器34,在C6748定时器因为它是64位定时器,所以使用当中我们既可以把它作为64位寄存器来使用也可以当作两个32位寄存器来使用。一般情况下我们把低32位寄存器叫做定时器12,把高32位定时器叫做定时器34,这两个寄存器就是给定时器34使用,它可以为定时器34指定一个分频系数。,

在C6748中还有几个比较寄存器,当我们的定时器的计数值达到比较寄存器的值的时候也会产生相应的事件。
下面看一下C6748当中定时器的一些特性。

首先在C6748中一共有4个64位的通用定时器,也就是说这4个定时器都可以作为通用的64位定时器来使用,同时每个定时器均可配置为两个32位定时器,当它们配置为两个32位定时器时就有以下3中关系:
1、两个32位独立定时器,这两个定时器完全没有关系,而且功能是对称的,可以独立来使用,只不过计数器的最大计数值或者说定时器的周期值从64位变为32位,
2、两个32位独立定时器,定时器34带有4位的预定标,也就是带有4位的分频功能,简单来说对于定时器34来说,它的输入时钟会被我们所设定4位的分频系数进行分频以后再输入到定时器当中。
3、同时这两个32位定时器也可以配置为关联模式,在关联模式下,定时器34作为定时器12的预定标器,也就是说定时器34就相当于定时器12的预分频。
定时器1还可以作为看门狗定时器,当我们程序异常的时候复位整个系统,而且是执行Power On Reset完全复位,将CPU内部的所有模块全部进行复位。
定时器的输入输出支持内部/外部的时钟输入方式。
定时器工作模式支持单次运行:也就是定时器在我们初始化启动之后只运行一次,当我们达到某个特定事件,比如说计数值达到我们所预设的周期值。
连续运行:当我们计数值达到我们所预设的周期值就在产生相应事件后将计数值清0,重新开始计数。
连续运行+周期重载:不仅要重新开始计数,而且每次要将周期值重载,也就是将reload寄存器的值复制到period寄存器当中。
对于事件触发,支持中断触发,EDMA3事件、还支持事件的输出。
当我们的定时器用于捕获的时候它有8个比较寄存器,当我们的计数值达到8个比较寄存器跟任意值相等的时候就产生相应的事件,而且在捕获的时候支持上升沿捕获、下降沿捕获和双边沿捕获脉冲。所以C6748定时器的功能还是比较强大的,这也就是为什么在数据手册对它的描述不仅仅标志为定时器Timer,还加了一个plus。
下面讲解一下时钟。

首先时钟有内部输入和外部输入,如果将定时器作为定时功能来使用的时候,使用最多的就是内部输入了,而外部输入是从定时器相应的管脚输入脉冲信号,这种情况常用的是事件触发和脉冲捕获的时候会用到。
在这里详细讲解使用内部时钟的情况。
首先看这张图,是系统锁相环控制器输出时钟。

这里我们需要关注我们定时器所使用的时钟,Timer64 P0/P1也就是C6748中的定时器0和定时器1,使用的是PLL0_AUXCLK,也就是PLL bypass clock(PLL旁路时钟)。在时钟那一部分我们讲过,PLL旁路时钟就相当于把晶体加CPU内部振荡器之后的时钟直接输入到模块中,在我们的核心板中使用的24MHz的晶体,所以PLL旁路时钟晶振的值是24MHz,也就是说我们定时器Timer64 P0/P1这两个定时器如果使用内部时钟就是24MHz的时钟,这点必须要注意,在我们计算周期值的时候时钟是很关键的。然后就是定时器Timer64 P2/P3,这两个时钟虽然是写在PLL1_SYSCLK2这里,但是需要注意的是:所有这些外设默认使用PLL0_SYSCLK2(all the modules use PLL0_SYSCLK2 by default)这个时钟,当然也可以通过修改寄存器的值来让它使用PLL1的时钟。
在创龙提供的程序当中,所有的外设使用的都是PLL0_SYSCLK2的时钟,所以定时器Timer64 P2/P3也是使用PLL0_SYSCLK2的时钟。这里需要注意的是这个时钟默认的频率是CPU的2分频,而且这个分频系数是固定的不可以更改,当我们的CPU运行在456MHz主频的时候,我们的定时器2和3它的输入时钟就是228MHz。
这里顺带提一下为什么这些外设的时钟是可选的:我们都知道C6748是一款低功耗的CPU,也就是说我们在CPU负载低的时候是可以通过降低CPU频率、降低CPU电压来降低功耗的。但是我们有一些外设比如说串口、定时器这样的外设,如果CPU频率在不停的变化,在运行的时候就会出现一些问题。比如说串口,如果CPU频率发生变化,那么我们在计算波特率的时候就要随着CPU频率的变化而变化,这样在实际运行中就会很麻烦。还有定时器也是一样的,如果频率在随意改变,那么这个定时器的值变得不可靠,就不知道定时是多少。这样就通过修改一个寄存器的值来将它的这些外设的值从跟CPU相关的锁相环转移到PLL1这个锁相环跟CPU的无关的,这样的话不管我们的CPU频率如何变化,这些外设都使用一个固定的频率在运行,这样的话当CPU频率发生变化的时候就不会影响比如说串口的输出、定时器的功能。
在创龙提供的程序中,程序都是跑在CPU默认的主频456MHz下,而所有的外设都是使用的PLL0_SYSCLK2,也就是说这些外设的输入时钟都是CPU的2分频也就是228MHz。对于我们的定时器来说,定时器0、1所使用的时候是旁路时钟也就是24MHz的时钟,而定时器2、3使用的是CPU2分频228MHz的输入时钟。
下面将一下时钟的输入输出管脚,这几张图节选自C6748详细的计数参考手持管脚复用那一节,SYSTEM CONFIGURATION。

可以看到我们的定时器都有相应的输入管脚和输出管脚,可以看到左边这张图是定时器23的输入管脚,右边的是定时器23的输出管脚,这里需要注意的是在管脚复用配置中,0是寄存器的默认值,也就是说默认就是作为0这个功能来使用的。当然为了稳妥起见,如果我们要使用定时器的输入,我们最好要手动配置一下定时器的管脚复用,来让它确定的工作在输入模式下,在输入模式下我们就可以通过外部来输入事件或者输入时钟脉冲。而输出管脚也是需要我们配置的,在我们配置完管脚复用以后,相应定时器的两个管脚就可以作为输出来使用就可以作为输出来使用,可以输出事件或者时钟脉冲。
下面这个图就是定时器01输入输出管脚,这里需要注意的是,定时器01来说输入输出管脚是同一个管脚,这样就有一个问题,也就是它的输入输出是不能同时来使用。或者说你可能会想分时复用行不行,理论上来说可以,当然这样用起来不如定时器23方便。这里需要提醒的是如果需要时钟输入输出功能一定要记得配置管脚复用。

13-3 模式

在这一部分给大家讲解一下C6748中定时器工作的几种模式:
1、64位定时器
2、2个32位独立定时器
3、2个32位独立定时器,定时器34带有4位预定标,也就是4位的分频
4、2个32位关联定时器,也就是说定时器34作为定时器12的预定标器,也就是一个预分频
5、看门狗定时器
在这里我只以定时器工作在定时模式来给大家讲解,当然定时器也可以工作在计数模式,配置方法类似,这里就不再赘述了。
在我们的定时器当做64位的通用定时器来说,它的结构比较简单。首先是时钟,可以内部外部,这里我们使用的内部时钟。

在时钟这里多说一句,在所有相关程序都写了注释,提醒大家在使用这些外设的时候要关注这些外设的时钟,对于定时器也不例外。
// 注意:DSP ports, Shared RAM, UART0, EDMA, SPI0, MMC/SDs,
// VPIF, LCDC, SATA, uPP, DDR2/mDDR (bus ports), USB2.0, HPI, PRU
// 这些外设使用的时钟来源为 PLL0_SYSCLK2 默认频率为 CPU 频率的二分之一
// 但是,ECAPs, UART1/2, Timer64P2/3, eHRPWMs,McBSPs, McASP0, SPI1
// 这些外设的时钟来源可以在 PLL0_SYSCLK2 和 PLL1_SYSCLK2 中选择
// 通过修改 System Configuration (SYSCFG) Module
// 寄存器 Chip Configuration 3 Register (CFGCHIP3) 第四位 ASYNC3_CLKSRC
// 配置时钟来源
// (默认值) 0 来源于 PLL0_SYSCLK2
// 1 来源于 PLL1_SYSCLK2
// 如果不是为了降低功耗,不建议修改这个值,它会影响所有相关外设的时钟频率
13-3-1 Timer
在Timer这个例程中使用的定时器2,在默认情况下使用的PLL0_SYSCLK2,也就是CPU的2分频。也就是说定时器2的输入频率是228MHz,写这段话的目的就是着重告诉大家这个地方比较关键,希望大家不要搞错。
定时器时钟完了以后主要就是计数,一个计数寄存器,因为我们是当做64位的定时器来使用的,所以计数位数是64位,最后是周期,周期这里涉及到一个比较模块,当我们的计数值和周期值相等的时候就会产生一个事件,或为中断事件或为EDMA事件,或者输出一个脉冲等等。同时我们可以配置周期重载,在作为64位通用定时器相对比较简单,有一个比较重要的是周期值的计算,可能好奇这个值是怎么得来的。
/****************************************************************************/
/* */
/* 宏定义 */
/* */
/****************************************************************************/
// 软件断点
#define SW_BREAKPOINT asm(" SWBP 0 ");
// 64位 定时器 / 计数器周期
// 定时时间 1 秒
// 低32位
#define TMR_PERIOD_LSB32 (0x0D970100)
// 高32位 0
#define TMR_PERIOD_MSB32 (0)
打开Windows系统自带的计算器功能,切换到程序员模式,将这个值以16进制输入0x0D970100转换成10进制就是228000000。定时时间是1s,1s的时间是怎么算的呢?实际定时器是在做一个周期性的计数,也就是说当这个计数变量的值数够228000000的时候,就产生一此事件,而我们定这个周期就是要设置这样一个值,让它数够这么多次数的时候它所花费的时间正好是1s。大致上可以使用这样一个公式得到计数器的计数周期值,因为我们计数器模块工作的时钟频率是228MHz,也就是说我们定时器内部的计数模块每增加1所需要的时间是 1 228000000 s \frac{1}{228000000}s 2280000001s,那么我们如果想要定时器的定时时间是1s,我们只需要在1s中数够228000000个时钟周期就可以了。


9039

被折叠的 条评论
为什么被折叠?



