定时器

01、模块介绍

高级控制定时器(TIMER1)由一个 16 位的自动装载计数器组成,它由一个可编程的预分频器驱动。它适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较、PWM、嵌入死区时间的互补 PWM 等)。

使用定时器预分频器和 CMU 时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。

高级控制定时器(TIMER1)和通用定时器(TIMERx)是完全独立的,它们不共享任何资源。

02、功能特点

• 16 位向上、向下、向上/下自动装载计数器

• 16 位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为 1~65535 之间的任意数值

• 多达 4 个独立通道:

– 输入捕获

– 输出比较

– PWM 生成(边缘或中间对齐模式)

– 单脉冲模式输出

• 死区时间可编程的互补输出

• 使用外部信号控制定时器和定时器互联的同步电路

• 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器

• 刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态

• 如下事件发生时产生中断/DMA:

– 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)

– 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)

– 输入捕获

– 输出比较

– 刹车信号输入

• 支持针对定位的增量(正交)编码器和霍尔传感器电路

• 触发输入作为外部时钟或者按周期的电流管理

03、功能说明

1 时基单元

可编程高级控制定时器的主要部分是一个 16 位计数器和与其相关的自动装载寄存器。这个计数器可以向上计数、向下计数或者向上向下双向计数。此计数器时钟由预分频器分频得到。

计数器、自动装载寄存器和预分频器寄存器可以由软件读写,即使计数器还在运行读写仍然有效。

时基单元包含:

• 计数器寄存器(TIMERx_CNT)

• 预分频器寄存器 (TIMERx_PSC)

• 自动装载寄存器 (TIMERx_ARR)

• 重复次数寄存器 (TIMERx_RCR)

自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器。根据在TIMERx_CR1 寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被立即或在每次的更新事件 UEV 时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当 TIMERx_CR1寄存器中的 UDIS 位等于 0 时,产生更新事件。更新事件也可以由软件产生。随后会详细描述每一种配置下更新事件的产生。

计数器由预分频器的时钟输出 CK_CNT 驱动,仅当设置了计数器 TIMERx_CR1 寄存器中的计数器使能位(CEN)时,CK_CNT 才有效。(更多有关使能计数器的细节,请参见控制器的从模式描述)。

注意,在设置了 TIMERx_CR 寄存器的 CEN 位的一个时钟周期后,计数器开始计数。

预分频器描述:

预分频器可以将计数器的时钟频率按 1 到 65536 之间的任意值分频。它是基于一个(在 TIMERx_PSC寄存器中的)16 位寄存器控制的 16 位计数器。因为这个控制寄存器带有缓冲器,它能够在运行时被改变,新的预分频器的参数在下一次更新事件到来时被采用。

以下两图给出了在预分频器运行时,更改计数器参数的例子。

当预分频器的参数从 1 变到 2 时,计数器的时序图:

1.png

2 计数器模式

向上计数模式:

在向上计数模式中,计数器从 0 计数到自动加载值(TIMERx_ARR 计数器的内容),然后重新从 0 开始计数并且产生一个计数器溢出事件。

如果使用了重复计数器功能,在向上计数达到设置的重复计数次数(TIMERx_RCR)时,产生更新事件 (UEV);否则每次计数器溢出时才产生更新事件。

在 TIMERx_EGR 寄存器中(通过软件方式或者使用从模式控制器)设置 UG 位也同样可以产生一个更新事件。

设置 TIMERx_CR1 寄存器中的 UDIS 位,可以禁止更新事件;这样可以避免在向预装载寄存器中写入新值时更新影子寄存器。在 UDIS 位被清‘0’之前,将不产生更新事件。但是在应该产生更新事件时,计数器仍会被清‘0’,同时预分频器的计数也被清 0(但预分频器的数值不变)。此外,如果设置了TIMERx_CR1 寄存器中的 URS 位(选择更新请求),设置 UG 位将产生一个更新事件 UEV,但硬件不设置UIF 标志(即不产生中断或 DMA 请求)。这是为了避免在捕获模式下清除计数器时,同时产生更新和捕获中断。

当发生一个更新事件时,所有的寄存器都被更新,硬件同时(依据 URS 位)设置更新标志位(TIMERx_SR寄存器中的 UIF 位)。

• 重复计数器被重新加载为 TIMERx_RCR 寄存器的内容。

• 自动装载影子寄存器被重新置入预装载寄存器的值(TIMERx_ARR)。

• 预分频器的缓冲区被置入预装载寄存器的值(TIMERx_PSC 寄存器的内容)。

下图给出一些例子,当 TIMERx_ARR=0x36 时计数器在不同时钟频率下的动作。

计数器时序图,预分频参数为 1

2.png

计数器时序图,预分频参数为 2

3.png

计数器时序图,当 ARPE=0 时的更新事件(TIMERx_ARR 没有预装入)

4.png

计数器时序图,当 ARPE=1 时的更新事件(预装入了 TIMERx_ARR)

5.png

向下计数模式:

在向下模式中,计数器从自动装入的值(TIMERx_ARR 计数器的值)开始向下计数到 0,然后从自动装入的值重新开始并且产生一个计数器向下溢出事件。

如果使用了重复计数器,当向下计数重复了重复计数寄存器(TIMERx_RCR)中设定的次数后,将产生更新事件(UEV),否则每次计数器下溢时才产生更新事件。

在 TIMERx_EGR 寄存器中(通过软件方式或者使用从模式控制器)设置 UG 位,也同样可以产生一个更新事件。

设置 TIMERx_CR1 寄存器的 UDIS 位可以禁止 UEV 事件。这样可以避免向预装载寄存器中写入新值时更新影子寄存器。因此 UDIS 位被清为 0 之前不会产生更新事件。然而,计数器仍会从当前自动加载值 重新开始计数,并且预分频器的计数器重新从 0 开始(但预分频系数不变)。

此外,如果设置了 TIMERx_CR1 寄存器中的 URS 位(选择更新请求) ,设置 UG 位将产生一个更新事件 UEV 但不设置 UIF 标志(因此不产生中断和 DMA 请求),这是为了避免在发生捕获事件并清除计数器时,同时产生更新和捕获中断。

当发生更新事件时,所有的寄存器都被更新,并且(根据 URS 位的设置)更新标志位(TIMERx_SR 寄存

器中的 UIF 位)也被设置。

• 重复计数器被重新加载为 TIMERx_RCR 寄存器的内容。

• 预分频器的缓冲区被置入预装载寄存器的值(TIMERx_PSC 寄存器的内容)。

• 当前的自动加载寄存器被更新为预装载值(TIMERx_ARR 寄存器中的内容)。注:自动装载在计数器重载入之前被更新,因此下一个周期将是预期的值。

以下是一些当 TIMERx_ARR=0x36 时,计数器在不同时钟频率下的操作例子。

计数器时序图,预分频参数为 1

6.png

计数器时序图,预分频参数为 2

7.png

中央对齐模式(向上/向下计数):

在中央对齐模式,计数器从 0 开始计数到自动加载的值(TIMERx_ARR 寄存器)−1,产生一个计数器溢出事件,然后向下计数到 1 并且产生一个计数器下溢事件;然后再从 0 开始重新计数。

在此模式下,不能写入 TIMERx_CR1 中的 DIR 方向位。它由硬件更新并指示当前的计数方向。

可以在每次计数上溢和每次计数下溢时产生更新事件;也可以通过(软件或者使用从模式控制器)设置TIMERx_EGR 寄存器中的 UG 位产生更新事件。然后,计数器重新从 0 开始计数,预分频器也重新从 0 开始计数。

设置 TIMERx_CR1 寄存器中的 UDIS 位可以禁止 UEV 事件。这样可以避免在向预装载寄存器中写入新值时更新影子寄存器。因此 UDIS 位被清为 0 之前不会产生更新事件。然而,计数器仍会根据当前自动重加载的值,继续向上或向下计数。

此外,如果设置了 TIMERx_CR1 寄存器中的 URS 位(选择更新请求) ,设置 UG 位将产生一个更新事件 UEV 但不设置 UIF 标志(因此不产生中断和 DMA 请求),这是为了避免在发生捕获事件并清除计数器时, 同时产生更新和捕获中断。

当发生更新事件时,所有的寄存器都被更新,并且(根据 URS 位的设置)更新标志位(TIMERx_SR 寄存器中的 UIF 位)也被设置。

• 重复计数器被重新加载为 TIMERx_RCR 寄存器的内容。

• 预分频器的缓冲区被置入预装载寄存器的值(TIMERx_PSC 寄存器的内容)。

• 当前的自动加载寄存器被更新为预装载值(TIMERx_ARR 寄存器中的内容)。注:如果因为计数器溢出而产生更新,自动重装载将在计数器重载入之前被更新,因此下一个周期将是预期的值(计数器被装载为新的值)。

以下是一些计数器在不同时钟频率下的操作的例子:

计数器时序图,内部时钟分频因子为 1,TIMERx_ARR=0x6

8.png

计数器时序图,ARPE=0 时的更新事件(计数器下溢)

9.png

计数器时序图,ARPE=1 时的更新事件(计数器溢出)

10.png

3 重复计数器

“时基单元”解释了计数器上溢/下溢时更新事件(UEV)是如何产生的,然而事实上它只能在重复计数达到 0 的时候产生。这个特性对产生 PWM 信号非常有用。

这意味着在每 N 次计数上溢或下溢时,数据从预装载寄存器传输到影子寄存器(TIMERx_ARR 自动重载入寄存器,TIMERx_PSC 预装载寄存器,还有在比较模式下的捕获/比较寄存器 TIMERx_CCRx),N是TIMERx_RCR 重复计数寄存器中的值。

重复计数器在下述任一条件成立时递减:

• 向上计数模式下每次计数器溢出时

• 向下计数模式下每次计数器下溢时

• 中央对齐模式下每次上溢和每次下溢时。虽然这样限制了 PWM 的最大循环周期为 128,但它能够在

每个 PWM 周期 2 次更新占空比。在中央对齐模式下,因为波形是对称的,如果每个 PWM 周期中仅刷新一次比较寄存器,则最大的分辨率为 2xTck 。

重复计数器是自动加载的,重复速率是由 TIMERx_RCR 寄存器的值定义(参考下图 15-21)。当更新事件由软件产生(通过设置 TIMERx_EGR 中的 UG 位)或者通过硬件的从模式控制器产生,则无论重复计数器的值是多少,立即发生更新事件,并且 TIMERx_RCR 寄存器中的内容被重载入到重复计数器。

不同模式下更新速率的例子,及 TIMERx_RCR 的寄存器设置

11.png

4 时钟选择

计数器时钟可由下列时钟源提供:

• 内部时钟(CK_INT)

• 外部时钟模式 1:外部输入引脚

• 外部时钟模式 2:外部触发输入 ETR

• 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器。如可以配置一个定时器 Timer1而作为另一个定时器 Timer2 的预分频器。详见下一章。

内部时钟源 内部时钟源(CK_INT)

如果禁止了从模式控制器(SMS=000),则 CEN、DIR(TIMERx_CR1 寄存器)和 UG 位(TIMERx_EGR寄存器)是事实上的控制位,并且只能被软件修改(UG 位仍被自动清除)。只要 CEN 位被写成“1”,预分频器的时钟就由内部时钟 CK_INT 提供。

下图显示控制电路和向上计数器在一般模式下,不带预分频器时的操作。

一般模式下的控制电路,预分频参数为 1

12.png

外部时钟源模式 1

当 TIMERx_SMCR 寄存器的 SMS=111 时,此模式被选中。计数器可以在选定输入端的每个上升沿或下降沿计数。

TI2 外部时钟连接例子

13.png

例如,要配置向上计数器在 T12 输入端的上升沿计数,使用下列步骤:

配置 TIMERx_CCMR1 寄存器 CC2S=01,配置通道 2 检测 TI2 输入的上升沿

配置 TIMERx_CCMR1 寄存器的 IC2F[3:0],选择输入滤波器带宽(如果不需要滤波器,保持 IC2F=0000)

配置 TIMERx_CCER 寄存器的 CC2P=0,选定上升沿极性

配置 TIMERx_SMCR 寄存器的 SMS=111,选择定时器外部时钟模式 1

配置 TIMERx_SMCR 寄存器中的 TS=110,选定 TI2 作为触发输入源

设置 TIMERx_CR1 寄存器的 CEN=1,启动计数器

注:捕获预分频器不用作触发,所以不需要对它进行配置

当上升沿出现在 TI2,计数器计数一次,且 TIF 标志被设置。

在 TI2 的上升沿和计数器实际时钟之间的延时,取决于在 TI2 输入端的重新同步电路。

外部时钟模式 1 下的控制电路

14.png

外部时钟源模式 2:

选定此模式的方法为:令 TIMERx_SMCR 寄存器中的 ECE=1,计数器能够在外部触发 ETR 的每一个上升沿或下降沿计数。

下图是外部触发输入的框图

15.png

例如,要配置在 ETR 下每 2 个上升沿计数一次的向上计数器,使用下列步骤:

本例中不需要滤波器,置 TIMERx_SMCR 寄存器中的 ETF[3:0]=0000

设置预分频器,置 TIMERx_SMCR 寄存器中的 ETPS[1:0]=01

选择 ETR 的上升沿检测,置 TIMERx_SMCR 寄存器中的 ETP=0

开启外部时钟模式 2,写 TIMERx_SMCR 寄存器中的 ECE=1

启动计数器,写 TIMERx_CR1 寄存器中的 CEN=1

计数器在每 2 个 ETR 上升沿计数一次。

在 ETR 的上升沿和计数器实际时钟之间的延时取决于在 ETRP 信号端的重新同步电路。

16.png

5 捕获/比较通道

每一个捕获/比较通道都是围绕着一个捕获/比较寄存器(包含影子寄存器),包括捕获的输入部分(数字滤波、多路复用和预分频器),和输出部分(比较器和输出控制)。

以下三图是一个捕获/比较通道概览。

输入部分对相应的 TIx 输入信号采样,并产生一个滤波后的信号 TIxF。然后,一个带极性选择的边缘监测器产生一个信号(TIxFPx),它可以作为从模式控制器的输入触发或者作为捕获控制。该信号通过预分频进入捕获寄存器(ICxPS)。

17.png

输出部分产生一个中间波形 OCxRef(高有效)作为基准,链的末端决定最终输出信号的极性。

18.png

19.png

20.png

捕获/比较模块由一个预装载寄存器和一个影子寄存器组成。读写过程仅操作预装载寄存器。

在捕获模式下,捕获发生在影子寄存器上,然后再复制到预装载寄存器中。

在比较模式下,预装载寄存器的内容被复制到影子寄存器中,然后影子寄存器的内容和计数器进行比较。

6 输入捕获模式:

在输入捕获模式下,当检测到 ICx 信号上相应的边沿后,计数器的当前值被锁存到捕获/比较寄存器(TIMERx_CCRx)中。当发生捕获事件时,相应的 CCxIF 标志(TIMERx_SR 寄存器)被置 1,如果开放了中断或者 DMA 操作,则将产生中断或者 DMA 请求。如果发生捕获事件时 CCxIF 标志已经为高,那么重复捕获标志 CCxOF(TIMERx_SR 寄存器)被置 1。写 CCxIF=0 可清除 CCxIF,或读取存储在 TIMERx_CCRx 寄存器中的捕获数据也可清除 CCxIF。写 CCxOF=0 可清除 CCxOF。

以下例子说明如何在 TI1 输入的上升沿时捕获计数器的值到 TIMERx_CCR1 寄存器中,步骤如下:

• 选择有效输入端:TIMERx_CCR1 必须连接到 TI1 输入,所以写入 TIMERx_CCMR1 寄存器中的CC1S=01,只要 CC1S 不为“00”,通道被配置为输入,并且 TIMERx_CCR1 寄存器变为只读。

• 根据输入信号的特点,配置输入滤波器为所需的带宽(即输入为 TIx 时,输入滤波器控制位是TIMERx_CCMRx 寄存器中的 ICxF 位)。假设输入信号在最多 5 个内部时钟周期的时间内抖动,我们须配置滤波器的带宽长于 5 个时钟周期;因此我们可以(以 f DTS 频率)连续采样 8 次,以确认在 TI1上一次真实的边沿变换,即在 TIMERx_CCMR1 寄存器中写入 IC1F=0011。

• 选择 TI1 通道的有效转换边沿,在 TIMERx_CCER 寄存器中写入 CC1P=0(上升沿)。

• 配置输入预分频器。在本例中,我们希望捕获发生在每一个有效的电平转换时刻,因此预分频器被禁止(写 TIMERx_CCMR1 寄存器的 IC1PS=00)。

• 设置 TIMERx_CCER 寄存器的 CC1E=1,允许捕获计数器的值到捕获寄存器中。

• 如果需要,通过设置 TIMERx_DIER 寄存器中的 CC1IE 位允许相关中断请求,通过设置 TIMERx_DIER寄存器中的 CC1DE 位允许 DMA 请求。

• 当发生一个输入捕获时:

• 产生有效的电平转换时,计数器的值被传送到 TIMERx_CCR1 寄存器。

• CC1IF 标志被设置(中断标志)。当发生至少 2 个连续的捕获时,而 CC1IF 未曾被清除,CC1OF 也被置1。

• 如设置了 CC1IE 位,则会产生一个中断。

• 如设置了 CC1DE 位,则还会产生一个 DMA 请求。

• 为了处理捕获溢出,建议在读出捕获溢出标志之前读取数据,这是为了避免丢失在读出捕获溢出标志之后和读取数据之前可能产生的捕获溢出信息。

注:设置 TIMERx_EGR 寄存器中相应的 CCxG 位,可以通过软件产生输入捕获中断和/或 DMA 请求。

7 PWM 输入模式

该模式是输入捕获模式的一个特例,除下列区别外,操作与输入捕获模式相同:

• 两个 ICx 信号被映射至同一个 TIx 输入。

• 这 2 个 ICx 信号为边沿有效,但是极性相反。

• 其中一个 TIxFP 信号被作为触发输入信号,而从模式控制器被配置成复位模式。

例如,你需要测量输入到 TI1 上的 PWM 信号的长度(TIMERx_CCR1 寄存器)和占空比(TIMERx_CCR2寄存器),具体步骤如下(取决于 CK_INT 的频率和预分频器的值)

• 选择 TIMERx_CCR1 的有效输入:置 TIMERx_CCMR1 寄存器的 CC1S=01(选中 TI1)。

• 选择 TI1FP1 的有效极性(用来捕获数据到 TIMERx_CCR1 中和清除计数器):置 CC1P=0(上升沿有效)。

• 选择 TIMERx_CCR2 的有效输入:置 TIMERx_CCMR1 寄存器的 CC2S=10(选中 TI1)。

• 选择 TI1FP2 的有效极性(捕获数据到 TIMERx_CCR2):置 CC2P=1(下降沿有效)。

• 选择有效的触发输入信号:置 TIMERx_SMCR 寄存器中的 TS=101(选择 TI1FP1)。

• 配置从模式控制器为复位模式:置 TIMERx_SMCR 中的 SMS=100。

• 使能捕获:置 TIMERx_CCER 寄存器中 CC1E=1 且 CC2E=1。

21.png

因为只有 TI1FP1 和 TI2FP2 连到了从模式控制器,所以 PWM 输入模式只能使用TIMERx_CH1/TIMERx_CH2 信号。

8 强制输出模式:

在输出模式(TIMERx_CCMRx 寄存器中 CCxS=00)下,输出比较信号(OCxREF 和相应的 OCx/OCxN)能够直接由软件强置为有效或无效状态,而不依赖于输出比较寄存器和计数器间的比较结果。

置 TIMERx_CCMRx 寄存器中相应的 OCxM=101,即可强置输出比较信号(OCxREF/OCx)为有效状态。这样 OCxREF 被强置为高电平(OCxREF 始终为高电平有效),同时 OCx 得到 CCxP 极性相反的信号。

例如:CCxP=0(OCx 高电平有效),则 OCx 被强置为高电平。

置 TIMERx_CCMRx 寄存器中的 OCxM=100,可强置 OCxREF 信号为低。

该模式下,在 TIMERx_CCRx 影子寄存器和计数器之间的比较仍然在进行,相应的标志也会被修改。因此仍然会产生相应的中断和 DMA 请求。这将会在下面的输出比较模式一节中介绍。

9 输出比较模式

此项功能是用来控制一个输出波形,或者指示一段给定的的时间已经到时。

当计数器与捕获/比较寄存器的内容相同时,输出比较功能做如下操作:

• 将输出比较模式(TIMERx_CCMRx 寄存器中的 OCxM 位)和输出极性(TIMERx_CCER 寄存器中的 CCxP 位)定义的值输出到对应的引脚上。在比较匹配时,输出引脚可以保持它的电平(OCxM=000)、被设置成有效电平(OCxM=001)、被设置成无效电平(OCxM=010)或进行翻转(OCxM=011)。

• 设置中断状态寄存器中的标志位(TIMERx_SR 寄存器中的 CCxIF 位)。

• 若设置了相应的中断屏蔽(TIMERx_DIER 寄存器中的 CCxIE 位),则产生一个中断。

• 若设置了相应的使能位(TIMERx_DIER 寄存器中的 CCxDE 位,TIMERx_CR2 寄存器中的 CCDS 位选择 DMA 请求功能),则产生一个 DMA 请求。

TIMERx_CCMRx 中的 OCxPE 位选择 TIMERx_CCRx 寄存器是否需要使用预装载寄存器。

在输出比较模式下,更新事件 UEV 对 OCxREF 和 OCx 输出没有影响。

同步的精度可以达到计数器的一个计数周期。输出比较模式(在单脉冲模式下)也能用来输出一个单脉冲。

输出比较模式的配置步骤:

1. 选择计数器时钟(内部,外部,预分频器)。

2. 将相应的数据写入 TIMERx_ARR 和 TIMERx_CCRx 寄存器中。

3. 如果要产生一个中断请求,设置 CCxIE 位。

4. 选择输出模式,例如:

– 要求计数器与 CCRx 匹配时翻转 OCx 的输出引脚,设置 OCxM=011

– 置 OCxPE = 0 禁用预装载寄存器

– 置 CCxP = 0 选择极性为高电平有效

– 置 CCxE = 1 使能输出

5. 设置 TIMERx_CR1 寄存器的 CEN 位启动计数器

TIMERx_CCRx 寄存器能够在任何时候通过软件进行更新以控制输出波形,条件是未使用预装载寄存器(OCxPE=‘0’,否则 TIMERx_CCRx 的影子寄存器只能在发生下一次更新事件时被更新)。下图给出了一个例子。

输出比较模式,翻转 OC1

22.png

04、代码配置

(1)定时器计数模式:

23.png

(2)定时器捕获功能:

24.png25.png

来源:珠海巨晟科技股份有限公司

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 16

本课将为大家讲解CKS32F4xx系列产品的SysTick定时器原理及使用方法。SysTick定时器也叫SysTick滴答定时器,属于Cortex-M4内核外设。SysTick定时器可以用于查询延时、中断延时以及测量函数运行时间;在实时操作系统RTOS中作为滴答定时器,用于上下文切换。采用Cortex-M内核的微处理都有SysTick定时器,方便不同处理器之间的软件移植。SysTick定时器时钟源可直接选取系统时钟,还可以通过系统时钟8分频后取得。

SysTick定时器内部是一个递减的计时器,当减到0时,将从LOAD寄存器中自动重装定时器初始值,重新向下递减计数,如此循环往复。如果开启SysTick中断,当计数器减到0时,SysTick可以生产异常,异常编号为15。

SysTick定时器寄存器    

ysTick定时器内部是一个24位向下递减的计时器,包含4个寄存器,如图。

1.png

图1 SysTick定时器框图

1)STK_CTRL寄存器

STK_CTRL是SysTick定时器的控制及状态寄存器,相应功能如下:

2.jpg

2) STK_LOAD寄存器

STK_LOAD寄存器是SysTick定时器的重装载数值寄存器,相应功能如下:

3.jpg

3)STK_VAL寄存器

STK_VAL寄存器是SysTick定时器的当前数值寄存器,相应功能如下:

4.jpg

4)STK_CALIB寄存器

STK_CALIB寄存器是SysTick定时器的校准数值定时器,用于利用片上硬件为软件提供校准信息,但使用情况较少。在CMSIS Core中,不需要使用SysTick校准寄存器,因为CMSIS Core提供了一个名为“SystemCoreClock”的软件变量。此变量在系统初始化函数“SystemInit()”中设置,每次更改系统时钟配置时也会更新。这种方法比使用SysTick CalibrationRegister的硬件方法灵活。校准寄存器描述如下表:

5.jpg

查询延时使用步骤    

1)配置SysTick定时器时钟源

2)加载延时计数值

3)清零计数器,启动定时器开始递减计数

4)等待计数结束

5)清零计数,关闭定时器,延时结束

相关函数如下:

void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{  
    if (SysTick_CLKSource == SysTick_CLKSource_HCLK)  
    {    
        SysTick->CTRL |= SysTick_CLKSource_HCLK;  
    }  
    else  
    {    
        SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;  
    }
}
void TickDelayInit(u8 SYSCLK)
{
    //选择时钟源为AHB/8    
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
    fac_us=SYSCLK>>3;
    fac_ms=((u32)SYSCLK*1000)>>3;
}
void TickDelayUs(u16 nus)
{
    uint32_t temp;SysTick->LOAD=nus*fac_us-1;
    SysTick->VAL=0x00;
    SysTick->CTRL=0x01;
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));
    SysTick->CTRL=0x00;        
    SysTick->VAL =0X00;       
}
void TickDelayMs(u16 nms)
{        
    u32 temp;    
    SysTick->LOAD=nms*fac_ms -1;
    SysTick->VAL =0x00;SysTick->CTRL=0x01 ;
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));
    SysTick->CTRL=0x00;
    SysTick->VAL =0X00;  
}

中断方式延时使用步骤     

1)配置SysTick定时器时钟源

2)调用系统函数SysTick_Config(),开启中断,配置中断间隔

3)延时函数赋值延时变量,并等待延时变量递减到0,达到精确延时效果

4)中断函数中延时变量递减到0

相关函数如下:

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{   
    if ((ticks - 1) > 0xFFFFFF)//24位寄存器,大于该值返回错误   
    {      
        return (1);     
    }  
    SysTick->LOAD  = (uint32_t)(ticks - 1);//计数到0,ticks值应减1     
    NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1);  
    SysTick->VAL   = 0UL;      
    SysTick->CTRL  = (1<<2) |(1<<1) | 1;//配置时钟源,使能定时器,开启中断  
    return (0UL);  
}
void TickInterruptDelay(__IO u32 nTime)
{  
    TimingDelay = nTime;  
    while(TimingDelay != 0);
}
void TickInterruptHandleTimingDelay_Decrement(void)
{  
    if (TimingDelay != 0)   
    {     
        TimingDelay--;   
    }
}

测量短时函数的执行时间

SysTick计时器可用于计时测量。例如,可以使用以下代码测量短函数的持续时间:

SysTick->CTRL = 0; // 禁用 SysTick
SysTick->LOAD = 0xFFFFFFFF; // 设置重装寄存器到最大值
SysTick->VAL = 0; // 清零VAL
SysTick->CTRL = 0x5; // 使能SysTick, 使用处理器时钟
while(SysTick->VAL != 0); // 等待重装完毕
start_time = SysTick->VAL; // 较大的起始点
TestDelayFunc(); // 待测函数执行时间
stop_time = SysTick->VAL; // 获取执行结束时间
cycle_count = start_time e stop_time;//计算函数执行时间

由于SysTick是一个递减计数器,因此start_time的值大于stop_time。如果待测函数执行时间较长,这种情况必须启用SysTick异常,并使用SysTick处理程序来计算SysTick计数器下溢的次数。

总结及注意事项

SysTick定时器是微处理器系统内部定时器,提供精确的时间延时和计时功能。采用中断方式延时,需要考虑SysTick中断优先级较低,容易被打断影响延时;在嵌入式系统中,系统将使用SysTick计时器,应用程序中则不可在使用SysTick;在系统在线调试停止时,SysTick计时器将停止计时。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 14

本章以CW32通用定时器为例介绍单片机定时器的用法。

定时器是单片机中一个非常传统且重要的外设,定时器的本质其实就是一个计数器,只不过被计数的对象是定时器的时钟源。定时器要正常工作,需要这几个要点步骤:有时钟源输入、计数器工作、有一个可以比较的值(自动重装载值)。其工作流程是这样的:计数器会随着时钟源对时钟源提供的脉冲进行计数,计数值不断上涨(或下降),如果计数值和自动重装载值一样,那么计数器的值就会被硬件清零重新计数,这个清零重新计数被称为定时器计数溢出,这个事情会触发一个中断,被叫做定时器溢出中断,也就是说,定时器依靠对稳定的时钟源定次数计数来实现定时,并且每一个定时周期完成都会产生溢出中断。

上面讲述的就是任何一个定时器都会具备的功能,只要是个定时器就会有,程序上为了方便,关于上述功能的配置项都被以”base”命名,比如这样: 

1.png

2.png

3.png

看图中的结构体,这个结构体的成员同样对应了定时器相关的寄存器,由于定时器本质是一个计数器,所以根据时钟源的选择,定时器会有不同的工作模式,如外部计数模式、编码器模式等。这里选择定时器模式(就是开头介绍的那种工作过程),之后定时器会自动选择单片机自己工作使用的时钟作为时钟源,连续计数模式下,定时器会自动重复执行上述溢出中断的过程,预分频系数根据用户需要进行配置,它和下面的重装载值共同决定定时器的溢出周期。

以图中的配置为例,该定时器的时钟源是48MHz,现在需要一个50Hz(也就是周期为20ms)的定时器,该定时器会每20ms触发一次定时器中断。在不进行干涉的情况下,定时器每秒会计数48M次,预分频系数设置成32之后,定时器每秒计数150万次,将重装载值设置为30000,定时器在每计数30000次之后触发一次中断,1秒触发50次中断,正好是需要的50Hz频率。但是填入的时候不能直接填入30000,因为30000是人类从1开始计算第一个数字得出的结果,计算机的第一个数字是0,因此需要在最后减1。图中的代码直接列出了上述文字表达的公式,其中的50就是频率。

随后设置好中断,完成必要的初始化,定时器的基本功能就可以使用了,中断服务函数可以在函数列表中找到。

好的,你已经掌握了所有单片机定时器的基本用法,不过细心的小伙伴肯定想过:为什么CW32的定时器叫ATIM、GTIM和BTIM呢?TIM就是timer,也就是定时器,A是advanced的缩写,ATIM就是高级定时器,GTIM是通用定时器,BTIM自然是基本定时器。这是根据功能对定时器资源进行划分的,这么划分的好处是不需要查手册就能通过代码直接看出来某个定时器具备什么功能,基本定时器只具备上述基本功能,通用定时器额外拥有捕获/比较功能,高级定时器包含通用定时器所有的功能,而且还有更多其他功能。理论上来说这些附带的功能都可以通过代码来实现,但由于很多工业场景需要用到,所以做到硬件层面会更加稳定,也更方便。

本章使用的是通用定时器,下面介绍高级定时器的捕获/比较功能,因为这个功能很常用。

首先需要着重声明的一点是,捕获比较功能大概率拥有多个通道,但是定时器,也就是上述的基础功能只有一个,所以即使使用很多个捕获比较通道,其所属定时器的定时周期也是相同的。

下面就来看看通用定时器的结构框图,初看这个图可能会不知所措,我们可以先进行简单的划分,框图上半部分的右侧有一个16位计数器,计数器可以从左侧选择输入的时钟源,可以对输入进行分频。框图下半部分展示了定时器的4个捕获比较通道channel1~channel4,通道可以用来输出也可以用来输入,但同一时间只能使用输入|输出中的一个功能。笔者刚学习单片机的时候,不知道通道是什么,总是稀里糊涂的,通道就是让信号走的路,放到这里就是说,这个定时器拥有4个可以用来输出|输入的电信号道路。那这个通道输出的是什么东西呢?

4.png

我们都知道,对电平进行周期反转就可以制造方波,而定时器基本功能就可以实现这个效果,只需要在中断中反转IO电平即可。但是这样很不方便,比如我想要在不调整周期的情况下去控制方波的占空比,这种原始的办法就会略显麻烦,需要在中断内修改定时器的设置来实现。为了避免这种麻烦,出现了一种带输出比较功能的定时器。理念也很简单,定时器自己有一个在有限区间内周期性增长归零的计数器,那我直接设置一个新的门限值:当这个自增的计数值小于门限时,输出高电平;计数值大于门限时,输出低电平。这就是定时器的输出比较功能,对应上图下半部分右侧的输出功能。这种方式可以便捷快速地输出一个可轻松修改占空比的方波,而这种对信号的处理方式,也叫做脉宽调制(Pulse-width modulation),简称PWM,用这种方式输出的方波也叫做PWM波。

现在来看使用PWM功能需要进行哪些操作。先思考,除去基本的定时器配置之外,PWM需要用到捕获比较通道,那必然会有对比较捕获功能相关寄存器的配置,它需要输出一个波,那必定会有引脚相关的初始化。

下面看代码:首先当然是对IO的初始化,相信经过对前几章的阅读,读者必定是能轻松配置GPIO了,这里着重介绍对PWM输出功能的配置。第一步当然是找到输出比较功能的函数,输出比较的英文是output compare,简写是OC,所以直接找到函数“通用定时器_输出比较初始化”。这个函数有3个参数,按顺序分别表示要初始化的定时器是哪个、要初始化的通道是哪个、以及这个通道的输出模式。输出模式就是设定:当计数值大于|小于门限值的时候,是该输出高电平还是低电平。这里设定的是计数值小于门限时输出高电平。第二步就是设置这个关键的门限值,我们可以直接找到“通用定时器_设置比较1”来设置门限值,这里我把门限值设定为重装载值的一半,最后的效果就是输出一个占空比50%的方波。

5.png

6.png

对占空比的修改不一定需要用到这个设置占空比的函数,我们可以直接修改寄存器来实现。单片机中,存储这个门限值的是一个叫做CCR的寄存器,所以为什么叫CCR?没错,他原名叫Capture Comparison Register,所以就简写为CCR。定时器的每一个通道都有一个自己的捕获比较寄存器,所以CCR一共有四个,故而上图那个设置门限值的函数也有4个,但是由于整个寄存器都只用来装这一个值,所以我们修改的时候可以直接操作寄存器修改,就像这样CW_GTIM1->CCR1=0,我们也可以直接对这个赋值号左侧的部分进行自增操作或是别的什么操作都可以,但是写入操作仅限于作为输出模式时使用。

下面就是紧张刺激的验证环节了,笔者手上没有可以接的用来发光的灯泡,所以直接用万用表测量输出引脚的电压来验证PWM功能,万用表在测量方波时,会显示该方波的平均值,所以如果PWM正常,万用表的直流档会显示1.65V左右的电压,交流档会显示3.3V的电压,这里我为了使现象更明显,在中断中对PWM波的占空比进行周期性修改。

7.png

经过测量,占空比50%时,PA6输出电压为1.62V,算上误差这个在预期结果内。而加入中断的代码后,万用表示数会周期性跳变,符合预期结果,可以认定该配置下,PWM功能正常工作。

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 7

01、引言

某客户设计需要启动定时器在 3ms 后产生中断,其后定时器不再运行,直至下一次软件要求再次启动定时器产生中断,实测代码后发现定时器启动后立即产生了超时中断。

02、调研

客户通过 STM32CubeMX 配置 TIM7 并生成工程,在主循环中添加定时器启动代码;在定时器中断处理函数处理 update event 回调时停止定时器,并翻转一个 I/O 脚指示定时器启停,代码如下:

1.jpg

图1.启动定时器

2.jpg

图2.定时器中断回调

客户期待的结果是启动定时器时 I/O 拉高并保持 3ms,当 3ms 到达后产生中断拉低I/O,但实测时发现,定时器启动后立即产生了超时中断(高电平持续时间约 2us):

3.jpg

图3.实测结果

03、分析

应用代码在初始化定时器时会调用 HAL_TIM_Base_Init( )接口,此接口会调用TIM_Base_SetConfig( ) 配置定时器,并产生更新事件(TIMx->EGR = TIM_EGR_UG)加载寄存器,此事件标志 UIF 会被置位,在调用 HAL_TIM_Base_Start_IT( ) 启动定时器,在此使能定时器中断时,由于 UIF 已经置位,所以会立即触发并进入中断处理函数,中断回调函数会停止定时器计数,并禁止定时器中断;但当从中断处理函数返回继续执行HAL_TIM_Base_Start_IT( )时,此接口会使能定时器开始计数,进而在下一次调用HAL_TIM_Base_Start_IT( )时又会立即产生中断,循环往复,详细时序和具体代码如下:

4.jpg

图4.问题产生时序描述

5.jpg

图5.定时器启动代码

04、处理

修改代码,在启动定时器前强制停止定时器计数、清除中断位、清除 NVIC 挂起的中断后,再启动定时器,详见下图红框内代码;

6.jpg

图6.问题修正代码

7.jpg

图7.修正 BUG 后的运行结果

05、小结

在碰到这类定时器异常问题时,可以利用 I/O 口指示运行状态,结合代码分析找到原因并加以解决。

来源:STM32单片机

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 27

相关文章:

【CW32学习笔记】单片机启动&库函数构成

【CW32学习笔记】看手册配置时钟树

我们在前两节讲解了单片机启动到时钟树配置的内容,到此为止,单片机已经能开发别的功能并使用了,但这里我插入一个笔者个人觉得很重要的章节来介绍一个内核外设——滴答定时器(System Tick)。

根据cortex-M0+内核手册的介绍,这是一个24位倒计时定时器,它拥有4个可以访问的寄存器,

1.png

这个定时器和一般的定时器用法没什么区别,他没有高级定时器的功能,但是由于是内核白送的定时器,所以经常用于单片机的心跳时钟。

值得注意的是,滴答定时器是一个不可修改计数方式的定时器,他只能向下计数,查阅寄存器定义之后,进行如下代码的初始化操作,即可实现1ms定时并开启定时器中断。

2.png

开启定时器并打开中断之后,定时器就能正常工作,并且会正常进入其对应的中断服务函数,中断服务函数的名字可以直接在启动文件中找到(还记得第一节讲的中断向量表吗?)。

3.png

一般情况下,芯片厂商不会主动提供滴答定时器的中断服务函数接口,也就是xxxxx_it.c文件中不会出现此中断服务函数的名字(CW32的库会提供一个参考stm32 hal库的固件库,但是我觉得写的不符合我的风格,索性自己写了一个滴答定时器的文件),任何时候,只需要有这么个同名函数存在于工程文件中,该中断服务函数就能被正常调用并执行,例如这样:

4.png

实际上,任何中断服务函数都可以用这种办法来重写一个自己的,只是大部分时候官方都已经提供了良好的固件库,也就不需要我们手动重写了。

这里编写了一个测试函数来测试滴答定时器是否正常工作,实际上是使用延时函数来验证的:

5.png

那么结果当然是正常工作的!

在这里给一些建议,上手一款新的芯片的时候,完全可以直接用滴答定时器点灯来测试和上手。

总结:

1、滴答定时器是内核外设,芯片手册不会记录它的用法,内核手册才会有它的用法。

2、滴答定时器可以作为整个程序的心跳时钟,非常常用。

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 16

1. 问题提出

客户使用STM32G474的高精度定时器,基于CubeMX进行外设设置和代码生成,将某个子定时器的计数方式设置为retriggerable single shot方式,发现该子定时器无PWM输出,在调试模式下发现该子定时器的计数器一直为0,即计数器一直没有启动,但如果将计数方式修改为continuous模式,其他保持不变,定时器工作正常。

2. 问题描述

检查客户提供的CubeMX配置文件,客户使能了Master Timer与Timer B,Master Timer的比较器事件2触发Timer B复位与运行,并配置了Timer B的PWM输出,使用该工程文件直接生成代码,并添加高精度定时器计数使能与输出使能函数,如下:

1.png

进入调试模式观察,发现Master Timer正常计数,但是Timer B的计数器一直保持0,按照客户的描述,将Timer B的计数方式修改为“continous”后,Timer B恢复正常计数。对比Timer B在“retriggerable single shot”与“continous”两种工作模式下的相关寄存器的值进行对比,发现除了计数模式不同之外,其他所有状态都相同。

莫非真是“retriggerable single shot”存在问题?这明显不可能,此前多次使用过该模式并没有发现问题,于是将以前设计的能成功工作的例程拿来与该“问题”工程进行对比。经过比对分析发现,工程设置中使用的寄存器更新方式存在不同,如下图所示:

2.png

图1.正常工作工程中的寄存器更新配置

3.png

图2.“问题”工作工程中的寄存器更新配置

“问题”工程配置中使用Master Timer的更新作为触发寄存器更新触发源,并且更新要等到本定时器的下一次的Reset/Roll-over事件出现时才生效。通过将“Update taken into account on the following Reset/Roll-over event”修改为“Update taken into account immediately”,定时器也可以正常运行了,问题的原因就是由于该配置引起的。

查看该配置对应的寄存器说明:

4.png

当配置“Update taken into account on the following Reset/Roll-over event”对应于该位置“1”,即更新事件,无论是来自相邻定时器的还是软件产生的,都需要等到下一个Reset/Roll-over event才生效。

在直接使用CubeMX产生的HAL底层配置代码且配置中使能了预加载的情况下,该寄存器更新配置方式会导致Timer B的初始化配置无法生效。

如图3/4/5所显示的,因为在CubeMX直接生成的代码中,Timer B参数的配置通过调用函数HAL_HRTIM_WaveformTimerConfig()写入寄存器,然后在该函数中调用软件更新函数HRTIM_ForceRegistersUpdate()的方式让配置生效,那么在目前的配置下,软件触发更新也必须等待Reset/Roll-over event的出现。而在retriggerable single shot计数模式下,定时器不能自动启动计数,必须等待来自Master Timer的Reset事件(即前文提到的比较器事件2)

5.png

图3.“问题”工程中的寄存器更新与定时器Reset配置

6.png

图4.“问题”工程产生的Timer B初始化代码

7.png

图5.Timer B初始化代码中调用软件触发更新

综上,虽然正确地配置了Reset事件,Master Timer也正常计数且产生了比较事件2,但问题在于在运行了初始化代码后,该配置仅仅是写到了preload寄存器中,而没有写入active寄存器中,即Timer B的复位源没有生效,带来的后果就是Timer B的计数器不运行,一直保持0,且所有带有预加载特性的寄存器的值也没有生效。

作为对比,当配置为“Update taken into account immediately”时,调用软件触发更新函数HRTIM_ForceRegistersUpdate()函数,将使所有配置立即生效,定时器可正常工作。

3. 解决方法

基于以上分析,造成该问题的主要原因时CubeMX基于HAL库自动生成的初始化代码中没有考虑不同的寄存器更新配置方式,只是单一的采用软件更新的方式来触发寄存器更新。要解决以上问题,需要修改代码中的定时器初始化的时序,在配置寄存器更新方式为“Update taken into account on the following Reset/Roll-over event”前,让其他的配置先生效,基本逻辑如下,该逻辑在客户自己编写初始化代码时也需要遵循。

8.png

4.小结

解决客户提出的高精度定时器中的子定时器在retriggerable single shot计数模式下无法工作的问题,原因在于该计数模式下,当更新配置方式为“Update taken into account on the following Reset/Roll-over event”时,使用CubeMX生成的原始初始化代码出现了定时器配置无法生效,从而导致问题的出现。通过对初始化代码的逻辑进行简单修改,问题得以解决。当然,STM32CubeMX的未来版本应会就这个地方做针对性地完善。

来源:STM32单片机

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 21

上一章我们介绍了CKS32F4的通用定时器定时操作的使用方法,这一章我们将向大家介绍通用定时器作为定时器脉冲计数的使用。在本章中,我们将用TIM5的通道1(PA0)来做输入捕获,捕获PA0上的脉冲。

输入捕获简介

输入捕获模式可以用来测量脉冲宽度或者脉冲计数,我们简单说明脉冲计数的原理,测量方法如下:首先设置定时器通道x为上升沿捕获,在通道有脉冲触发时,定时器进入捕获中断,我们可以在中断中完成一次计数的累加,当一个计数周期结束后,得到的累加值就是脉冲计数值。

CKS32F4的定时器,除了TIM6和TIM7,其他定时器都有输入捕获功能。CKS32F4的输入捕获,简单的说就是通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA等。

本章我们用到TIM5_CH1来实现脉冲计数。

输入捕获操作

接下来,我们介绍我们本章需要用到的一些寄存器配置,需要用到的寄存器有:TIMx_ARR、TIMx_PSC、TIMx_CCMR1、TIMx_CCER、TIMx_DIER、TIMx_CR1、TIMx_CCR1接下来我们介绍这几个寄存器的配置。

首先TIMx_ARR和TIMx_PSC,这两个寄存器用来设自动重装载值和TIMx的时钟分频,对于TIMx_AR,如图1所示:一定要注意当自动装载的值为空时,计数器不工作。

1.png

图1

对于TIMx_PSC,如图2所示,利用这个寄存器和RCC的预分频寄存器配合,我们可以得到几微妙到几毫秒的计数周期。

2.png

图2 

再来看看捕获/比较模式寄存器1:TIMx_CCMR1,这个寄存器在输入捕获的时候,非常有用,该寄存器的各位描述如图3所示:

3.png

图3 TIMx_CCMR1寄存器各位描

当在输入捕获模式下使用的时候,对应图3的第二行描述,从图中可以看出,TIMx_CCMR1明显是针对2个通道的配置,低八位[7:0]用于捕获/比较通道1的控制,而高八位[15:8]则用于捕获/比较通道2的控制,因为TIMx还有CCMR2这个寄存器,所以可以知道CCMR2是用来控制通道3和通道4,这里我们用到的是TIM5的捕获/比较通道1,我们重点介绍TIMx_CCMR1的[7:0]位(其高8位配置类似),TIMx_CCMR1的[7:0]位详细描述见图4所示:

4.png

图4 TIMx_CCMR1[7:0]位详细描述

其中CC1S[1:0],这两个位用于CCR1的通道配置,这里我们设置IC1S[1:0]=01,也就是配置IC1映射在TI1上,即CC1对应TIMx_CH1。输入捕获1预分频器IC1PSC[1:0],这个比较好理解。我们是1次边沿就触发1次捕获,所以选择00就是了。输入捕获1滤波器IC1F[3:0],这个用来设置输入采样频率和数字滤波器长度。其中,fCK_INT是定时器的输入频率(TIMxCLK),一般为84Mhz/168Mhz,而fDTS则是根据TIMx_CR1的CKD[1:0]的设置来确定的,如果CKD[1:0]设置为00,那么fDTS=fCK_INT,N值就是滤波长度,举个简单的例子:假设IC1F[3:0]=0011,并设置IC1映射到通道1上,且为上升沿触发,那么在捕获到上升沿的时候,再以fCK_INT的频率,连续采样到8次通道1的电平,如果都是高电平,则说明确是一个有效的触发,就会触发输入捕获中断(如果开启了的话)。

这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤波的效果。这里,我们不做滤波处理,所以设置IC1F[3:0]=0000,只要采集到上升沿,就触发捕获。再来看看捕获/比较使能寄存器:TIMx_CCER,本章我们要用到这个寄存器的最低2位,CC1E和CC1P位。这两个位的描述如图5所示:

5.png

图5 TIMx_CCER最低2位描述

所以,要使能输入捕获,必须设置CC1E=1,而CC1P则根据自己的需要来配置。接下来我们再看看中断使能寄存器:TIMx_DIER,该寄存器的各位描述见图6:

6.png

图6 TIMx_DIER寄存器各位描述

本章,我们需要用到中断来处理捕获数据,所以必须开启通道1的捕获比较中断,即CC1IE设置为1。控制寄存器:TIMx_CR1,我们只用到了它的最低位,也就是用来使能定时器的,这里前面两章都有介绍,请大家参考前面的章节。最后再来看看捕获/比较寄存器TIMx_CCR1,该寄存器用来存储捕获发生时,TIMx_CNT的值,我们从TIMx_CCR1就可以读出通道1捕获发生时刻的TIMx_CNT值,至此,我们把本章要用的几个相关寄存器都介绍完了,本章要实现通过输入捕获,来计量TIM5_CH1(PA0)上面的脉冲数量,下面我们介绍库函数配置上述功能输入捕获的步骤:

1)开启TIM5时钟,配置PA0为复用功能(AF2),并开启下拉电阻

要使用TIM5,我们必须先开启TIM5的时钟。同时我们要捕获TIM5_CH1上面的高电平脉宽,所以先配置PA0为带下拉的复用功能,同时,为了让PA0的复用功能选择连接到TIM5,所以设置PA0的复用功能为AF2,即连接到TIM5上面。开启TIM5时钟的方法为:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//>>TIM5时钟使能

当然,这里我们也要开启PA0对应的GPIO的时钟。配置PA0为复用功能,所以我们首先要设置PA0引脚映射AF2,方法为:

br

最后,我们还要初始化GPIO的模式为复用功能,同时这里我们还要设置为开启下拉。方法为:

typedefstructGPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//GPIOA0
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度100MHz
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽复用输出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;//下拉
GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化PA0

跟上一讲PWM输出类似,这里我们使用的是定时器5的通道1,所以我们从CKS32F4对应的数据手册可以查看到对应的IO口为PA0:

2)初始化TIM5,设置TIM5的ARR和PSC

在开启了TIM5的时钟之后,我们要设置ARR和PSC两个寄存器的值来设置输入捕获的自动重装载值和计数频率。这在库函数中是通过TIM_TimeBaseInit函数实现的,在上面章节已经讲解过,这里不重复讲解。

TIM_TimeBaseStructure.TIM_Prescaler=psc;//定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr;//自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);//初始化TIM5

3)设置TIM5的输入捕获参数,开启输入捕获

TIM5_CCMR1寄存器控制着输入捕获1和2的模式,包括映射关系,滤波和分频等。这里我们需要设置通道1为输入模式,且IC1映射到TI1(通道1)上面,并且不使用滤波(提高响应速度)器。库函数是通过TIM_ICInit函数来初始化输入比较参数的:

voidTIM_ICInit(TIM_TypeDef*TIMx,TIM_ICInitTypeDef*TIM_ICInitStruct)

同样,我们来看看参数设置结构体TIM_ICInitTypeDef的定义:

typedef struct
{
    uint16_t TIM_Channel; //>>通道
    uint16_t TIM_ICPolarity; //>>捕获极性
    uint16_t TIM_ICSelection;//>>映射
    uint16_t TIM_ICPrescaler;//>>分频系数
    uint16_t TIM_ICFilter; //>>滤波器长度
}TIM_ICInitTypeDef;

参数TIM_Channel很好理解,用来设置通道。我们设置为通道1,为TIM_Channel_1。参数TIM_ICPolarit是用来设置输入信号的有效捕获极性,这里我们设置为TIM_ICPolarity_Rising,上升沿捕获。同时库函数还提供了单独设置通道1捕获极性的函数为:

TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);

这表示通道1为上升沿捕获,我们后面会用到,同时对于其他三个通道也有一个类似的函数,使用的时候一定要分清楚使用的是哪个通道该调用哪个函数,格式为TIM_OCxPolarityConfig()。参数TIM_ICSelection是用来设置映射关系,我们配置IC1直接映射在TI1上,选TIM_ICSelection_DirectTI。参数TIM_ICPrescaler用来设置输入捕获分频系数,我们这里不分频,所以选中TIM_ICPSC_DIV1,还有2,4,8分频可选。参数TIM_ICFilter设置滤波器长度,这里我们不使用滤波器,所以设置为0。这些参数的意义,在我们讲解寄存器的时候举例说明过,这里不做详细解释。我们的配置代码是:

TIM5_ICInitStructure.TIM_Channel=TIM_Channel_1;//>>选择输入端IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//>>上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//>>映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//>>配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter=0x00;//>>IC1F=0000配置输入滤波器不滤波
TIM_ICInit(TIM5,&TIM5_ICInitStructure);

4)使能捕获和更新中断(设置TIM5的DIER寄存器)

因为我们要捕获的是高电平信号,所以,第一次捕获是上升沿,这两件事,我们都在中断里面做,所以必须开启捕获中断和更新中断。这里我们使用定时器的开中断函数TIM_ITConfig即可使能捕获和更新中断:

TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//>>允许更新中断和捕获中断

5)设置中断优先级,编写中断服务函数

因为我们要使用到中断,所以我们在系统初始化之后,需要先设置中断优先级分组,这里方法跟我们前面讲解一致,调用NVIC_PriorityGroupConfig()函数即可,我们系统默认设置都是分组2。设置中断优先级的方法前面多次提到这里我们不做讲解,主要是通过函数NVIC_Init()来完成。设置优先级完成后,我们还需要在中断函数里面完成数据处理和捕获设置等关键操作,从而实现高电平计数统计。在中断服务函数里面,跟以前的外部中断和定时器中断实验中一样,我们在中断开始的时候要进行中断类型判断,在中断结束的时候要清除中断标志位。使用到的函数在上面的实验已经讲解过,分别为TIM_GetITStatus()函数和TIM_ClearITPendingBit()函数。

if(TIM_GetITStatus(TIM5,TIM_IT_Update)!=RESET){}//>>判断是否为更新中断
if(TIM_GetITStatus(TIM5,TIM_IT_CC1)!=RESET){}//>>判断是否发生捕获事件
TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_Update);//>>清除中断和捕获标志位

6)使能定时器(设置TIM5的CR1寄存器)

最后,必须打开定时器的计数器开关,启动TIM5的计数器,开始输入捕获。

TIM_Cmd(TIM5,ENABLE);//>>使能定时器5

通过以上6步设置,定时器5的通道1就可以开始输入捕获了。

代码示例

这里我们主要是添加了输入捕获初始化函数TIM5_CH1_Cap_Init以及中断服务函数TIM5_IRQHandler。对于输入捕获,我们也是使用的定时器相关的操作,接下来我们来看看两个函数的内容:

TIM_ICInitTypeDef TIM5_ICInitStructure;

//>>定时器5通道1输入捕获配置

//>>arr:自动重装值(TIM2,TIM5是32位的!!)psc:时钟预分频数

void TIM5_CH1_Cap_Init(u32 arr,u16 psc) 
{ 
    GPIO_InitTypeDef GPIO_InitStructure; 
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 
    NVIC_InitTypeDef NVIC_InitStructure; 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE); //>>TIM5 时钟使能 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //>>使能 PORTA 时钟 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //>>GPIOA0 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//>>复用功能 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; 
    //>>速度 100MHz 
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //>>推挽复用输出 
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //>>下拉 
    GPIO_Init(GPIOA,&GPIO_InitStructure); //>>初始化 PA0 
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5); //>>PA0 复用位定时器 5 
    TIM_TimeBaseStructure.TIM_Prescaler=psc; //>>定时器分频 
    TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //>>向上计数模式 
    TIM_TimeBaseStructure.TIM_Period=arr; //>>自动重装载值 
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
    TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure); 
    TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //>>选择输入端 IC1 映射到 TI1 上 
    TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; 
    //>>上升沿捕获 
    TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //>>映射到 TI1 上 
    TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //>>配置输入分频,不分频 
    TIM5_ICInitStructure.TIM_ICFilter = 0x00;//>>IC1F=0000 配置输入滤波器 不滤波 
    TIM_ICInit(TIM5, &TIM5_ICInitStructure); //>>初始化 TIM5 输入捕获参数 
    TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//>>允许更新和捕获中断 
    TIM_Cmd(TIM5,ENABLE ); 
    //>>使能定时器 5 
    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//>>抢占优先级 2 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;//>>响应优先级 0 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //>>IRQ 通道使能 
    NVIC_Init(&NVIC_InitStructure); //>>根据指定的参数初始化 VIC 寄存器、 
} 
//>>捕获状态 
//>>[7]:0,没有成功的捕获;1,成功捕获到一次. 
//>>[6]:0,还没捕获到低电平;1,已经捕获到低电平了. 
//>>[5:0]:捕获低电平后溢出的次数(对于 32 位定时器来说,1us 计数器加 1,溢出时间:4294 秒) 
u8 TIM5CH1_CAPTURE_STA=0; //>>输入捕获状态
u32 TIM5CH1_CAPTURE_VAL;//>>输入捕获值(TIM2/TIM5 是 32 位) 
//>>定时器 5 中断服务程序 
void TIM5_IRQHandler(void) 
{        
    if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//>>捕获 1 发生捕获事件         
    {                 
        TIM5CH1_CAPTURE_VAL  ++;     }        
    }    
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //>>清除中断标志位 
}

此部分代码包含两个函数,其中TIM5_CH1_Cap_Init函数用于TIM5通道1的输入捕获设置,其设置和我们上面讲的步骤是一样的,这里就不多说,特别注意:TIM5是32位定时器,所以arr是u32类型的。接下来,重点来看看第二个函数。TIM5_IRQHandler是TIM5的中断服务函数,变量TIM5CH1_CAPTURE_VAL,则用来记录捕获到上升沿的时候,对脉冲进行计数,timer.h头文件内容比较简单,主要是函数申明,这里我们不做过多讲解。接下来,我们看看main函数内容:

extern u8 TIM5CH1_CAPTURE_STA; 
//>>输入捕获状态
extern u32 
TIM5CH1_CAPTURE_VAL;//输入捕获值 
int main(void) 
{ 
    long long temp = 0; 
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//>>设置系统中断优先级分组 2 
    delay_init(168); //初始化延时函数 
    uart_init(115200);//初始化串口波特率为 115200
    TIM14_PWM_Init(500-1,84-1); 
    //>>84M/84=1Mhz 的计数频率计数到 500,频率为 1M/500=2Khz 
    TIM5_CH1_Cap_Init(0XFFFFFFFF,84-1);//>>以 84M/84=1Mhz 的频率计数 
    while(1) 
    { 
        delay_ms(100);
        //>>得到脉冲计数 
        printf("PWM CNT:%d \r\n", TIM5CH1_CAPTURE_VAL);//>>打印脉冲计数
    } 
}

该main函数是在PWM实验的基础上修改来的,我们保留了PWM输出,同时通过设置TIM5_Cap_Init(0XFFFFFFFF,84-1),将TIM5_CH1的捕获计数器设计为1us计数一次,并设置重装载值为最大以达到不让定时器溢出的作用(溢出时间为2^32-1us),所以我们的捕获时间精度为1us。每隔100ms打印一次脉冲计数值。至此,我们的软件设计就完成了。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 33

以CW32L083为例,其定时器分为6种:

基本定时器:CW32L083 内部集成 3 个基本定时器 (BTIM),每个 BTIM 完全独立且功能完全相同,各包含一个 16bit 自动重装 载计数器并由一个可编程预分频器驱动。BTIM 支持定时器模式、计数器模式、触发启动模式和门控模式 4 种工作模式,支持溢出事件触发中断请求和 DMA 请求。得益于对触发信号的精细处理设计,使得 BTIM 可以由硬件自 动执行触发信号的滤波操作,还能令触发事件产生中断和 DMA 请求。

低功耗定时器:CW32L083 内部集成 1 个 16 位低功耗定时器(LPTIM),可以以很低的功耗实现定时或对外部脉冲计数的功能。通过选择合适的时钟源和触发信号,可以实现系统低功耗休眠时将MCU唤醒的功能。LPTIM 内部具有一个比较寄存器,可实现比较输出和 PWM 输出,并可以控制输出波形的极性。此外,LPTIM 还可以与正交编码器连接,自动 实现递增计数和递减计数。

通用定时器:CW32L083 内部集成 4 个通用定时器(GTIM),每个 GTIM 完全独立且功能完全相同,各包含一个16bit 自动重 装载计数器并由一个可编程预分频器驱动。GTIM 支持定时器模式、计数器模式、触发启动模式和门控模式 4 种基本工作模式,每组带4 路独立的捕获 / 比较通道,可以测量输入信号的脉冲宽度(输入捕获)或者产生输出波形(输出比较和 PWM)。

高级定时器:高级定时器 (ATIM) 由一个 16 位的自动重载计数器和 7 个比较单元组成,并由一个可编程的预分频器驱动。ATIM 支持 6 个独立的捕获 / 比较通道,可实现 6 路独立 PWM 输出或 3 对互补 PWM 输出或对 6 路输入进行捕获。可 用于基本的定时 / 计数、测量输入信号的脉冲宽度和周期、产生输出波形(PWM、单脉冲、插入死区时间的互补 PWM 等)。

独立看门狗定时器 (IWDT):CW32L083 内部集成独立看门狗定时器 (IWDT),使用专门的内部 RC 时钟源 RC10K,可避免运行时受到外部因素 影响。一旦启动 IWDT,用户需要在规定时间间隔内对 IWDT 的计数器进行重载,否则计数器溢出会触发复位或 产生中断信号。IWDT 启动后,可停止计数。可选择在深度休眠模式下 IWDT 保持运行或暂停计数。专门设置的键值寄存器,可以锁定 IWDT 的关键寄存器,防止寄存器被意外修改。

窗口看门狗定时器 (WWDT):CW32L083 内部集成窗口看门狗定时器 (WWDT),用户需要在设定的时间窗口内进行刷新,否则将触发系统复位。WWDT 通常被用来监测有严格时间要求的程序执行流程,防止由外部干扰或未知条件造成应用程序的执行异常, 导致发生系统故障。

01、CW32定时器中断

定时器中断是由CW32中的定时器引起的中断,所谓中断就是程序正常顺序执行的时候,出现了突发事件,CPU停止当前的程序的执行,转去处理突发事件,处理完毕后又返回原程序被中断的位置继续执行。

02、CW32定时器中断源

定时器主要中断源包括如下:

基本定时器(BTIM): 计数器(ARR/TOP)溢出中断、计数器触发中断。

低功耗定时器(LPTIM): 计数方向反向中断、ARR更新完成中断、比较寄存器更新完成中断、计数器触发中断、ARR自动重载匹配中断、比较匹配中断。

通用定时器(GTIM): 编码器计数方向变化中断、CHx捕获比较中断、计数器下溢中断、计数器触发中断、计数器ARR溢出中断。

高级定时器(ATIM): CHxA/B捕获比较中断、CHxA/B捕获数据丢失中断、计数器上溢中断、计数器下溢中断、刹车中断、CH4比较匹配中断、事件更新中断。

更新事件, 触发事件。

独立看门狗定时器(IWDT): 计数器溢出中断。

窗口看门狗定时器(WWDT):计数器溢出中断。

各中断源的含义详细描述参见对应产品的用户手册内容。

03、实际操作

以CW32L083为例,控制基本定时器BTIM1以固定的时间间隔产生中断,并在ARR溢出中断中控制口线电平翻转。

1. RCC时钟初始化

void RCC_Configuration(void)
{
    RCC_HSI_Enable(RCC_HSIOSC_DIV6);

    //系统时钟设置为HSI时钟6分频,8MHz, PCLK、HCLK不分频,PCLK=HCLK=SysClk=8MHz

    __RCC_BTIM_CLK_ENABLE();

    __RCC_GPIOB_CLK_ENABLE();

}

2.初始化GPIO口

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.IT = GPIO_IT_NONE;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pins = GPIO_PIN_8;
    GPIO_Init(CW_GPIOB, &GPIO_InitStruct);
}

3.配置嵌套矢量中断控制器

void NVIC_Configuration(void)
{
     __disable_irq();
     NVIC_EnableIRQ(BTIM1_IRQn);
    __enable_irq();
}

4.主函数

int32_t main(void)
{
    BTIM_TimeBaseInitTypeDef BTIM_TimeBaseInitStruct = {0};   
    /* System Clocks Configuration */
    RCC_Configuration();

    /* NVIC Configuration */
     NVIC_Configuration();

     /* GPIO Configuration */
    GPIO_Configuration();

    BTIM_TimeBaseInitStruct.BTIM_Mode = BTIM_Mode_TIMER;
    /* 工作模式,00:定时器模式01:计数器模式10:触发计数模式11:门控计数模式  */
    BTIM_TimeBaseInitStruct.BTIM_Period = 49999;
    /* 计数重载周期,取值范围0x0000 到 0xFFFF.  */
    BTIM_TimeBaseInitStruct.BTIM_Prescaler = 7;    // 8分频
    /*8分频, 预分配系数,取值范围2的n次幂,n=0,1,2…15 */
    BTIM_TimeBaseInit(CW_BTIM1, &BTIM_TimeBaseInitStruct);
    //BTIM基本定时器初始化
    BTIM_ITConfig(CW_BTIM1, BTIM_IT_OV, ENABLE);   // 使能BTIMx的溢出中断
    BTIM_Cmd(CW_BTIM1, ENABLE);
    /*BTIM1以1MHz时钟进行计数,设置ARR寄存器为49999,则BTIM1将每50ms溢出一次,并触发中断服务程序。*/
    while (1)
    {
        /* 中断服务程序中 PB8输出翻转 */
    }
}

5.中断函数:PB08输出翻转

void BTIM1_IRQHandler(void)
{
    /* USER CODE BEGIN */
    if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV))
    // 检查BTIM的状态寄存器的各状态位是否置位
    {
        BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV);  
        // 清除BTIM的状态寄存器的各状态位
        PB08_TOG();
        //PB08口信号翻转
    }

    /* USER CODE END */
}

6.实验验证

示波器检测PB08口的信号输出,示波器波形图呈周期性翻转。

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 21

CW32系列MCU的GTIM、ATIM的每个定时器都带有至少4路独立的捕获 / 比较通道,输入捕获功能可以测量输入信号的脉冲宽度或者频率。当捕获比较通道 CHy 上信号发生跳变(上升沿或下降沿)时,硬件自动将当前计数寄存器 GTIMx_CNT 的值存放到对应通道的比较捕获寄存器 GTIMx_CCRy 中,完成一次捕获。通过连续几次捕获即可完成信号脉冲宽度或者频率的测量。

功能框图如下图所示:

1.png

各个通道上触发捕获的条件由比较捕获控制寄存器 GTIMx_CMMR 决定。

2.png

当发生一次捕获时,通道 CHy 比较捕获中断标志 GTIMx_ISR.CCy 被硬件置位,如果允许中断 ( 设置中断使能寄 存器 GTIMx_IER.CCy 为 1),CPU 将响应中断服务程序。退出中断服务程序之前,应设置中断标志清除寄存器 GTIMx_ICR.CCy 为 0 以清除该标志。

输入捕获来源 

GTIM 的输入捕获来源可以是外部 GTIMx_CHy 引脚,也可以是片内其它外设,通过通用定时器输入捕获来源配置寄存器 SYSCTRL_GTIMxCAP 进行配置。当 SYSCTRL_GTIMxCAP.CHy 为 0x00 时,输入捕获信号的外部输入端口由 GPIO 复用功能寄存器 (GPIOx_AFRH 和 GPIOx_AFRL) 进行配置。当 SYSCTRL_GTIMxCAP.CHy 为 0x01 ~ 0x07 时,输入捕获信号来自片内其它外设,如下表所示:

33.png

这种配置下,可以在芯片内部实现外部输入的互联,例如将 UART 的 RXD 信号作为输入捕获来源,可以实现对 UART 波特率的自动检测。

实例演示:利用GTIM的输入捕获功能,测量PWM信号的周期和脉宽

1.初始化系统时钟

void RCC_Configuration(void)

{

__RCC_GTIM1_CLK_ENABLE();

__RCC_GPIOA_CLK_ENABLE();

RCC_HSI_Enable(RCC_HSIOSC_DIV6);

// 系统时钟设置为HSI,6分频,8MHz, PCLK、HCLK不分频, PCLK=HCLK=SysClk=8MHz

}

2.初始化GPIO

void GPIO_Configuration(void)

{

GPIO_InitTypeDef GPIO_InitStruct = {0};

GPIO_InitStruct.IT = GPIO_IT_NONE;

GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

GPIO_InitStruct.Pins = GPIO_PIN_6 | GPIO_PIN_7;

GPIO_Init(CW_GPIOA, &GPIO_InitStruct);

PA06_AFx_GTIM1CH1();//设置PA06复用为GTIM1_CH1, 上升沿触发捕获

PA07_AFx_GTIM1CH2();//设置PA07复用为GTIM1_CH2,下降沿触发捕获。

}

3.配置中断服务程序

void NVIC_Configuration(void)

{

    __disable_irq();

    NVIC_EnableIRQ(GTIM1_IRQn);

    __enable_irq();

}

4.GTIM1中断服务程序:通道1的输入捕获中断获取计数值VALUE1,通道2的输入捕获中断获取计数值VALUE2,通道1的第2次输入捕获中断获取计数值VALUE3。则信号脉宽=VALUE2-VALUE1,信号周期=VALUE3-VALUE1。注意如果待测量信号的脉宽和周期较长,在计算时需要考虑定时器的溢出问题,详见定时器溢出中断处理内容。

void GTIM1_IRQHandler(void)

{

    GTIM1_IRQHandlerCallBack();

}

void GTIM1_IRQHandlerCallBack(void)

{

    static uint8_t stage = 0;

    static uint32_t cnt = 0;

    if (GTIM_GetITStatus(CW_GTIM1, GTIM_IT_OV))

    {

        GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);

        if (stage == 1)

        {

            cnt++;

        }

    }

    if (GTIM_GetITStatus(CW_GTIM1, GTIM_IT_CC1))

    {

        if (stage == 0)

        {

            PWMPeriod = GTIM_GetCapture1(CW_GTIM1);

            stage = 1;

        }

        else if (stage == 1)

        {

            PWMPeriod = GTIM_GetCapture1(CW_GTIM1) + cnt * 65536 - PWMPeriod;

            stage = 0;

            cnt = 0;

        }

        GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_CC1);

    }

    if (GTIM_GetITStatus(CW_GTIM1, GTIM_IT_CC2))

    {

        if (stage == 1)

        {

            PWMWidth = GTIM_GetCapture2(CW_GTIM1) + cnt * 65536 - PWMPeriod;

        }

        GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_CC2);

    }

}

5.主程序:使用GTIM1的CH1和CH2两个通道对PWM输入信号进行测量,在捕获中断服务程序中完成信号的周期和脉宽计算。

static uint32_t PWMPeriod = 0;

static uint32_t PWMWidth = 0;

int32_t main(void)

RCC_Configuration();//System Clocks Configuration 

     GPIO_Configuration();//GPIO Configuration 

     NVIC_Configuration();//NVIC Configuration 

GTIM_InitTypeDef GTIM_InitStruct = {0};

GTIM_ICInitTypeDef GTIM_ICInitStruct = {0};

GTIM_InitStruct.Mode = GTIM_MODE_TIME; /*!< GTIM的模式选择。*/

GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE; 

/*!< GTIM的单次/连续计数模式选择。*/

GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV1; /*!< GTIM的预分频系数。*/

GTIM_InitStruct.ReloadValue = 0xFFFF; /*!< GTIM的重载值。*/

GTIM_InitStruct.ToggleOutState = DISABLE;

GTIM_TimeBaseInit(CW_GTIM1, &GTIM_InitStruct); //GTIM的基础参数初始化


GTIM_ICInitStruct.CHx = GTIM_CHANNEL1;// GTIM 输入捕获的配置参数

GTIM_ICInitStruct.ICFilter = GTIM_CHx_FILTER_NONE;

GTIM_ICInitStruct.ICInvert = GTIM_CHx_INVERT_ON;

GTIM_ICInitStruct.ICPolarity = GTIM_ICPolarity_Rising;

GTIM_ICInit(CW_GTIM1, &GTIM_ICInitStruct);//输入捕获功能初始化


GTIM_ICInitStruct.CHx = GTIM_CHANNEL2;

GTIM_ICInitStruct.ICPolarity = GTIM_ICPolarity_Falling;

GTIM_ICInit(CW_GTIM1, &GTIM_ICInitStruct);


GTIM_ITConfig(CW_GTIM1, GTIM_IT_CC1 | GTIM_IT_CC2 | GTIM_IT_OV, ENABLE);

GTIM_Cmd(CW_GTIM1, ENABLE);

while (1)

    {

    }

}

6.演示说明:

将同一个PWM输入信号引入到PA06和PA07上,运行程序,使用GTIM1的CH1和CH2两个通道对PWM输入信号的脉宽和周期进行测量。 

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 146

页面

订阅 RSS - 定时器