打印
[RISC-V MCU 应用开发]

第五十九章、CH32V103应用教程——TIM-定时器同步

[复制链接]
710|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 RISCVLAR 于 2021-1-21 16:20 编辑

CH32V103应用教程——TIM-定时器同步

本章教程主要演示4种定时器同步模式。

1、TIM简介及相关函数介绍
定时器能够输出时钟脉冲(TRGO),也能接收其他定时器的输入(ITRx)。不同的定时器的ITRx的来源(别的定时器的TRGO)是不一样的。
所有TIMx定时器在内部相连,用于定时器同步或链接。当一个定时器处于主模式时,它可以对另一个处于从模式的定时器的计数器进行复位、启动、停止或提供时钟等操作。
1、使用一个定时器作为另一个定时器的预分频器
如上图:可以配置定时器1作为定时器2的预分频器。进行下述操作:
● 配置定时器1(TIM1)为主模式,它可以在每一个更新事件UEV时输出一个周期性的触发信号。当控制寄存器2(TIM1_CTLR2)的MMS域的值为010时,即更新事件被选为触发输入(TRGO),每当产生一个更新事件时在TRGO1上输出一个上升沿信号。
● 连接定时器1的TRGO1输出至定时器2,设置定时器2的从模式控制寄存器(TIM2_SMCFGR)的TS域值为000,即选择内部触发(ITR1)用于同步计数器的触发输入源。
● 设置定时器2的从模式控制寄存器(TIM2_SMCFGR)的SMS域值为111,即外部时钟模式 1,选中的触发输入(TRGI)的上升沿驱动计数器。这样定时器2即可由定时器1周期性的上升沿(即定时器1的计数器溢出)信号驱动。
● 最后,设置控制寄存器1(TIMx_CTLR1)的CEN位为1使能启动两个定时器。
注:如果OCx已被选中为定时器1的触发输出(MMS=1xx),它的上升沿用于驱动定时器2的计数器。

2、使用一个定时器使能另一个定时器
当使用一个定时器使能另一个定时器时,定时器2的使能由定时器1的输出比较控制。参考第一种配置定时器1和定时器2的连接方式。只当定时器1的OC1REF(输出参考信号)为高时,定时器2才对分频后的内部时钟计数。两个定时器的时钟频率都是由预分频器对定时器时钟(CK_INT)除以3(fCK_CNT=fCK_INT/3)得到。
● 设置定时器1(TIM1)的控制寄存器2(TIM1_CTLR2)的MMS的值为100,即配置定时器1为主模式,其输出比较参考信号(OC1REF)被用于作为触发输出(TRGO)
● 通过设置比较/捕获控制寄存器1(TIM1_CHCTLR1)的相应位配置定时器1的OC1REF波形
●设置从模式控制寄存器(TIM1_SMCFGR)的TS域的值为000,选择内部触发用于同步计数器的触发输入源,即配置定时器2从定时器1获得输入触发。
● 设置从模式控制寄存器(TIM1_SMCFGR)的SMS域的值为101,即选择选择核心计数器的时钟和触发模式为门控模式,当触发输入(TRGI)为高时,计数器的时钟开启;在触发输入变为低,计数器停止,计数器的启停都是受控的。
● 最后,设置控制寄存器1(TIMx_CTLR1)的CEN位为1使能启动两个定时器(TIM2和TIM1)。
注:定时器2的时钟不与定时器1的时钟同步,这个模式只影响定时器2计数器的使能信号。
在上述配置中,在定时器2启动之前,它们的计数器和预分频器未被初始化,因此它们从当前的数值开始计数。可以在启动定时器1之前复位2个定时器,使它们从给定的数值开始,即在定时器计数器中写入需要的任意数值。通过设置事件产生寄存器(TIM1_SWEVGR) 的UG位为1即可复位定时器。
在下述配置中,需要同步定时器1和定时器2。定时器1是主模式并从0开始,定时器2是从模式并从0xE7开始;2个定时器的预分频器系数相同。设置控制寄存器 1(TIM1_CTLR1) 的CEN位为0,即可禁止定时器1,定时器2随即停止。
●设置定时器1(TIM1)的控制寄存器2(TIM1_CTLR2)的MMS域的值为100,即配置定时器1为主模式,其输出比较参考信号(OC1REF)被用于作为触发输出(TRGO)
● 通过设置比较/捕获控制寄存器1(TIM1_CHCTLR1)的相应位配置定时器1的OC1REF波形
●设置从模式控制寄存器(TIM1_SMCFGR)的TS域的值为000,选择内部触发用于同步计数器的触发输入源,即配置定时器2从定时器1获得输入触发。
● 设置从模式控制寄存器(TIM1_SMCFGR)的SMS域的值为101,即选择选择核心计数器的时钟和触发模式为门控模式,当触发输入(TRGI)为高时,计数器的时钟开启;在触发输入变为低,计数器停止,计数器的启停都是受控的。
●设置事件产生寄存器(TIM1_SWEVGR和TIM2_SWEVGR)的UG位的值为1,复位定时器1和定时器2。
● 写’0xE7’至定时器2计数器(TIM2_CNT),初始化它为0xE7。
● 设置控制寄存器1(TIMx_CTLR1)的CEN位为1使能启动两个定时器(TIM1和TIM2)。
●设置定时器1控制寄存器1(TIM1_CTLR1)的CEN位为0停止定时器1

3、使用一个定时器去启动另一个定时器
当使用一个定时器去启动另一个定时器时,可以使用定时器1的更新事件使能定时器2。参考第一种配置定时器1和定时器2的连接方式。一旦定时器1产生更新事件,定时器2即从它当前的数值(可以是非0)按照分频的内部时钟开始计数。在收到触发信号时,定时器2的控制寄存器1(TIM2_CTLR1)的CEN位被置1,同时计数器开始计数一直到控制寄存器1(TIM2_CTLR1)的CEN位被置0.
两个定时器的时钟频率都是由预分频器对CK_INT除以3(fCK_CNT=fCK_INT/3)。
● 设置定时器1(TIM1)的控制寄存器2(TIM1_CTLR2)的MMS的值为010,即更新事件被选为触发输入(TRGO)。
● 通过设置自动重装值寄存器(TIM1_ATRLR)配置定时器1的周期。
● 通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的TS域的值为000,配置定时器2从定时器1获得输入触发。
● 通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的SMS域的值为110,配置定时器2为触发模式。
● 通过设置TIM1_CTLR1寄存器的CEN位为1启动定时器1。

4、使用一个定时器作为另一个的预分频器
本次配置讲述使用定时器1作为定时器2的预分频器。参考第一种配置定时器1和定时器2的连接方式。配置流程如下:
● 设置定时器1控制寄存器 2(TIM1_CTLR2)的MMS域的值为010,更新事件被选为触发输入(TRGO)。配置定时器1为主模式,送出它的更新事件UEV做为触发输出。然后每次计数器溢出时输出一个周期信号。
● 通过设置定时器1自动重装值寄存器(TIM1_ATRLR)配置定时器1的周期。
● 通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的TS域的值为000,配置定时器2从定时器1获得输入触发。
● 通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的SMS域的值为111,配置定时器2使用外部时钟模式。
● 设置定时器2和定时器1控制寄存器1(TIMx_CTLR1)寄存器的CEN位为1启动定时器2和定时器1。

5、使用一个外部触发同步启动2个定时器
在使用一个外部触发同步启动2个定时器时,当定时器1的TI1输入上升时使能定时器1,使能定时器1的同时使能定时器2,参考第一种配置定时器1和定时器2的连接方式。为保证计数器的对齐,定时器1必须配置为主/从模式(对应TI1为从,对应定时器2为主):
● 通过设置定时器1控制寄存器 2(TIM1_CTLR2)的MMS域的值为001,配置定时器1为主模式,送出它的使能做为触发输出。
● 通过设置定时器1从模式控制寄存器(TIM1_SMCFGR)的TS域的值为100,
配置定时器1为从模式,从TI1获得输入触发。
● 通过设置定时器1从模式控制寄存器(TIM2_SMCFGR)的SMS域的值为110,配置定时器1为触发模式。
● 通过设置定时器1从模式控制寄存器(TIM1_SMCFGR)的MSM位的值为1,配置定时器1为主/从模式。
● 通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的TS域的值为000,配置定时器2从定时器1获得输入触发。
● 通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的SMS域的值为110,配置定时器2为触发模式。
当定时器1的TI1上出现一个上升沿时,两个定时器同步地按照内部时钟开始计数,两个TIF标志也同时被设置。
注: 在上述配置中,在启动之前两个定时器都通过设置事件产生寄存器(TIM1_SWEVGR) 的UG位进行初始化,两个计数器都从0开始,但可以通过写入任意一个计数器寄存器(TIMx_CNT)在定时器间插入一个偏移。
关于CH32V103 TIM具体信息,可参考CH32V103应用手册。TIM标准库函数在第七章节已介绍,在此不再赘述。

2、硬件设计
本章教程主要演示4种定时器同步模式,其中使用一个外部触发同步启动定时器1和定时器2时,需使用PA8引脚进行外部触发。

3软件设计
本章教程主要演示4种定时器同步模式,具体程序如下:
tim.h文件
#ifndef __TIM_H
#define __TIM_H

#include "ch32v10x_conf.h"

void TIM_TimSynchroMode1_Init(void);
void TIM_TimSynchroMode2_Init(void);
void TIM_TimSynchroMode3_Init(void);
void TIM_TimSynchroMode4_Init(void);

#endif
tim.h文件主要进行相关函数声明;
tim.c文件
#include "tim.h"

/*******************************************************************************
* Function Name  : TIM_TimSynchroMode1_Init
* Description    : Using TIM2 as prescaler for TIM1.
* Input          : None
* Return         : None
*******************************************************************************/
//使用定时器2作为定时器1的预分频器
void TIM_TimSynchroMode1_Init(void)
{
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE );
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );

    //将TIM1和TIM2计数模式设置为向上计数模式
    TIM_CounterModeConfig( TIM1, TIM_CounterMode_Up );
    TIM_CounterModeConfig( TIM2, TIM_CounterMode_Up );

    //设置TIM1自动重装值寄存器值
    TIM_SetAutoreload( TIM1, 0x3E8 );

    //设置定时器TIM1预分频器值
    TIM_PrescalerConfig( TIM1, 48000-1, TIM_PSCReloadMode_Immediate );

    //当控制寄存器2(TIM1_CTLR2)的MMS域的值为010时,即更新事件被选为触发输入(TRGO),每当产生一个更新事件时在TRGO1上输出一个上升沿信号。
    TIM_SelectOutputTrigger( TIM1, TIM_TRGOSource_Update );

    //连接定时器1的TRGO1输出至定时器2,设置定时器2的从模式控制寄存器(TIM2_SMCFGR)的TS域值为000,即选择内部触发(ITR1)用于同步计数器的触发输入源。
    TIM_ITRxExternalClockConfig( TIM2, TIM_TS_ITR0 );

    //TIM2作为从机模式且 选中的触发输入(TRGI)的上升沿驱动计数器
    //设置定时器2的从模式控制寄存器(TIM2_SMCFGR)的SMS域值为111,即外部时钟模式 1,选中的触发输入(TRGI)的上升沿驱动计数器。这样定时器2即可由定时器1周期性的上升沿(即定时器1的计数器溢出)信号驱动。
    TIM_SelectSlaveMode( TIM2, TIM_SlaveMode_External1 );

    //使能开启TIM1和TIM2,即设置控制寄存器1(TIMx_CTLR1)的CEN位为1使能启动两个定时器。
    TIM_Cmd( TIM1, ENABLE );
    TIM_Cmd( TIM2, ENABLE );
}

/*******************************************************************************
* Function Name  : TIM_TimSynchroMode2_Init
* Description    : Using TIM2 to use TIM1.
* Input          : None
* Return         : None
*******************************************************************************/
//使用定时器2使能定时器1
void TIM_TimSynchroMode2_Init(void)
{
    TIM_OCInitTypeDef TIM_OCInitStructure;

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE );
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );

    TIM_CounterModeConfig( TIM1, TIM_CounterMode_Up );
    TIM_CounterModeConfig( TIM2, TIM_CounterMode_Up );
    TIM_SetAutoreload( TIM1, 0x3E8 );
    TIM_PrescalerConfig( TIM1, 48000-1, TIM_PSCReloadMode_Immediate );

    //输出比较结构体初始化
    //通过设置比较/捕获控制寄存器1(TIM1_CHCTLR1)的相应位配置定时器1的OC1REF波形
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  //配置为PWM模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  //输出使能
    TIM_OCInitStructure.TIM_Pulse = 0x64;  //设置占空比大小
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //输出通道电平极性配置
    TIM_OC1Init( TIM1, &TIM_OCInitStructure );  //初始化

    //设置定时器1(TIM1)的控制寄存器2(TIM1_CTLR2)的MMS的值为100,即配置定时器1为主模式,其输出比较参考信号(OC1REF)被用于作为触发输出(TRGO)
    TIM_SelectOutputTrigger( TIM1, TIM_TRGOSource_OC1Ref );

    //设置从模式控制寄存器(TIM1_SMCFGR)的TS域的值为000,选择内部触发用于同步计数器的触发输入源,即配置定时器2从定时器1获得输入触发。
    TIM_SelectInputTrigger( TIM2, TIM_TS_ITR0 );

    //设置从模式控制寄存器(TIM1_SMCFGR)的SMS域的值为101,即选择选择核心计数器的时钟和触发模式为门控模式,当触发输入(TRGI)为高时,计数器的时钟开启;在触发输入变为低,计数器停止,计数器的启停都是受控的。
    TIM_SelectSlaveMode( TIM2, TIM_SlaveMode_Gated );

    //最后,设置控制寄存器1(TIMx_CTLR1)的CEN位为1使能启动两个定时器(TIM2和TIM1)。
    TIM_Cmd( TIM2, ENABLE );
    TIM_Cmd( TIM1, ENABLE );
}

/*******************************************************************************
* Function Name  : TIM_TimSynchroMode3_Init
* Description    : Using TIM2 to start TIM1.
* Input          : None
* Return         : None
*******************************************************************************/
//使用定时器2启动定时器1
void TIM_TimSynchroMode3_Init(void)
{
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_TIM1, ENABLE );
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );

    TIM_CounterModeConfig( TIM1, TIM_CounterMode_Up );
    TIM_CounterModeConfig( TIM2, TIM_CounterMode_Up );

    //通过设置自动重装值寄存器(TIM1_ATRLR)配置定时器1的周期。
    TIM_SetAutoreload( TIM1, 0xFFFF );
    TIM_PrescalerConfig( TIM1, 48000-1, TIM_PSCReloadMode_Immediate );

    //设置定时器1(TIM1)的控制寄存器2(TIM1_CTLR2)的MMS的值为010,即更新事件被选为触发输入(TRGO)。
    TIM_SelectOutputTrigger( TIM1, TIM_TRGOSource_Update );

    //通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的TS域的值为000,配置定时器2从定时器1获得输入触发。
    TIM_SelectInputTrigger( TIM2, TIM_TS_ITR0 );

    //通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的SMS域的值为110,配置定时器2为触发模式。
    TIM_SelectSlaveMode( TIM2, TIM_SlaveMode_Trigger );

    //通过设置TIM1_CTLR1寄存器的CEN位为1启动定时器1。
    TIM_Cmd( TIM1, ENABLE );
}

/*******************************************************************************
* Function Name  : TIM_TimSynchroMode4_Init
* Description    : Starting TIM1 and TIM2 synchronously in response to an external trigger.
* Input          : None
* Return         : None
*******************************************************************************/
//使用外部触发同步启动定时器1和定时器2
void TIM_TimSynchroMode4_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE );
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  //输入下拉
    GPIO_Init( GPIOA, &GPIO_InitStructure);
    GPIO_ResetBits( GPIOA, GPIO_Pin_8 );

    TIM_CounterModeConfig( TIM1, TIM_CounterMode_Up );
    TIM_CounterModeConfig( TIM2, TIM_CounterMode_Up );
    TIM_SetAutoreload( TIM1, 0xFFFF );
    TIM_PrescalerConfig( TIM1, 48000-1, TIM_PSCReloadMode_Immediate );

    //输入捕获结构体初始化
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;  //配置输入捕获的通道,需要根据具体的GPIO来配置
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;  //输入的需要被捕获的信号的分频系数
    TIM_ICInitStructure.TIM_ICFilter = 0x00;  //输入的需要被捕获的信号的滤波系数
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;  //输入捕获信号的极性配置
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;  //输入通道和捕获通道的映射关系,有直连和非直连两种
    TIM_ICInit( TIM1, &TIM_ICInitStructure );  //初始化

    //通过设置定时器1从模式控制寄存器(TIM1_SMCFGR)的TS域的值为100,配置定时器1为从模式,从TI1获得输入触发。
    TIM_SelectInputTrigger( TIM1, TIM_TS_TI1FP1 );

    //通过设置定时器1从模式控制寄存器(TIM2_SMCFGR)的SMS域的值为110,配置定时器1为触发模式。
    TIM_SelectSlaveMode( TIM1, TIM_SlaveMode_Trigger );

    //通过设置定时器1控制寄存器 2(TIM1_CTLR2)的MMS域的值为001,配置定时器1为主模式,送出它的使能做为触发输出。
    TIM_SelectMasterSlaveMode( TIM1, TIM_MasterSlaveMode_Enable );
    TIM_SelectOutputTrigger( TIM1, TIM_TRGOSource_Enable );

    //通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的TS域的值为000,配置定时器2从定时器1获得输入触发。
    TIM_SelectInputTrigger( TIM2, TIM_TS_ITR0 );

    //通过设置定时器2从模式控制寄存器(TIM2_SMCFGR)的SMS域的值为110,配置定时器2为触发模式。
    TIM_SelectSlaveMode( TIM2, TIM_SlaveMode_Trigger );
}

tim.c文件主要进行4种定时器同步模式的配置;
main.c文件
int main(void)
{
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n",SystemCoreClock);

/* Timer synchronization Mode Selection */
//  TIM_TimSynchroMode1_Init();
//  TIM_TimSynchroMode2_Init();
//  TIM_TimSynchroMode3_Init();
  TIM_TimSynchroMode4_Init();

    while(1)
    {
        printf("TIM1 cnt:%d\r\n", TIM1->CNT);
        printf("TIM2 cnt:%d\r\n", TIM2->CNT);
    }
}
main.c文件主要进行初始化以及定时器1、2的计数值的打印输出。

4下载验证
将编译好的程序分别在4种不同模式下下载到开发版并复位,串口打印如下:
1、使用定时器2作为定时器1的预分频器;
2、使用定时器2使能定时器1;
3、使用定时器2启动定时器1;
4、使用一个外部触发同步启动定时器1和定时器2;(需使用PA8引脚进行外部触发)

58、TIM-定时器同步.rar

483.83 KB

使用特权

评论回复

相关帖子

沙发
hudi008| | 2021-1-23 20:23 | 只看该作者
能够做编码器的采集吗?

使用特权

评论回复
板凳
xdqfc| | 2021-1-24 11:04 | 只看该作者
hudi008 发表于 2021-1-23 20:23
能够做编码器的采集吗?

完全可以。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

132

主题

293

帖子

34

粉丝