当前位置: 首页 > article >正文

STM32-通用定时器

目录

通用定时器

功能框图

输入捕获

测量频率        

测量脉宽

PWM输入模式

输出比较

PWM边沿对齐模式

PWM中心对齐模式

初始化结构体

TIM_TimeBaseInitTypeDef:时基结构体

TIM_OC_InitTypeDef:输出比较结构体

TIM_IC_InitTypeDef:输入捕获结构体

通用定时器实验:输入捕获

常规配置

TIM5配置

测试环节

实验现象


通用定时器

通用定时器由一个可编程预分频器驱动的16位自动重新加载计数器组成。应用测量输入的脉冲长度信号(输入捕获)产生输出波形(输出比较和PWM)

脉冲长度和波形周期可以从几微秒调制到几毫秒,使用定时器预分频器和RCC时钟控制器预分频器。

输入捕获应用场景:脉冲跳变沿时间测量PWM输入测量

输出比较应用场景:PWM8种模式(常用PWM边沿对齐模式PWM中心对齐模式

功能框图

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

16位可编程预分频器,1~65536。

多达4个独立通道,用于:

        输入捕获

        输出比较

        PWM产生(边沿对齐模式和中心对齐模式)

        单脉冲模式输出

同步电路控制定时器和外部信号,并互连多个定时器。

中断/DMA生成以下事件:

        更新:计数器溢出/下溢,计数器初始化(由软件或内部/外部触发)

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

        输入捕获

        输出比较

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

触发输入外部时钟或逐周期电流管理。

时基单元包括:

计数器寄存器(TIMx_CNT)

预分频寄存器(TIMx_PSC)

自动重装载寄存器(TIMx_ARR)

计数模式:

向上计数

向下计数

中心对齐:计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件。然后继续从0开始重新计数,以此循环。

TIx为输入通道,需要被测量的信号从定时器的外部引脚TIMx_CH1/2/3/4进入。

当输入的信号存在高频干扰时,需要对输入信号进行滤波,根据采样定律(采样频率必须大于或等于两倍的输入信号),比如输入信号为1M,存在高频信号干扰时就要进行滤波,可以设置采样频率为2M,这样可以在保证采样到有效信号的基础上把高于2M的高频干扰信号过滤掉。

输入滤波器的配置由TIMx_CR1:CKD[1:0]和TIMx_CCMR1/2:ICxF[3:0]控制。根据ICxF位的描述,采样频率Fsample可以由Fck_int(内部时钟)和Fdts(Fck_int经过分频后的频率,分频因子由CKD[1:0]决定,1/2/4分频)分频后的时钟提供。

边沿检测器用来设置信号在捕获时什么边沿有效(上升沿、下降沿、双边沿),具体由TIMx_CCER:CCxP、CCxNP决定。

捕获通道:IC1/2/3/4。每个捕获通道都有对应的捕获寄存器CCR1/2/3/4,当发生捕获时,计数器CNT的值就会被锁存到捕获寄存器中。

输入通道TIx是用来输入信号的,捕获通道ICx是用来捕获输入信号的通道。一个输入通道的信号可以同时输入给两个捕获通道。比如TI1的信号经过滤波和边沿检测器后TI1FP1和TI1FP2可以进入到捕获通道IC1和IC2。输入通道和捕获通道的映射关系具体由TIMx_CCMR:CCxS[1:0]配置。

ICx的输出型会经过预分频器,用于决定发生多少个事件时进行一次捕获。具体由TIMx_CCMR:ICxPSC配置。如果希望捕获信号的每一个边沿,则不分频。

经过预分频器的信号ICxPS是最终被捕获的信号,当发生捕获时(第一次),计数器CNT的值会被锁存到捕获寄存器TIMx_CCR中,还会产生CCxI中断,相应的中断位CCxIF(在SR寄存器中)会被置位,通过软件或读取CCR的值可以将CCxIF清0。如果发生第二次捕获(即重复捕获,CCR寄存器中已捕获到计数器值且CCxIF标志已置1),则捕获溢出标志位CCxOF(在SR寄存器中)会被置位,CCx_OF只能通过软件清零。

输入捕获

测量频率        

当捕获通道TIx上出现上升沿时,发生第一次捕获,计数器CNT的值会被锁存到捕获寄存器TIM_CCRx中,而且还会进入捕获中断,在中断服务程序中记录一次捕获(可以用一个标志变量来记录),并把捕获寄存器的值读取到value1中。

当出现第二次上升沿时,发生第二次捕获,计数器CNT的值会再次被锁存到捕获寄存器TIM_CCRx中,并再次进入捕获中断,在中断服务程序中记录一次捕获,并把捕获寄存器的值读取到value2中,清除捕获记录标志。利用value2-value1的差值就可以算出信号的周期(频率)。

测量脉宽

当捕获通道TIx上出现上升沿时,发生第一次捕获,计数器CNT的值会被锁存到捕获寄存器TIM_CCRx中,而且还会进入捕获中断,在中断服务程序中记录一次捕获(可以用一个标志变量来记录),并把捕获寄存器的值读取到value1中。然后把捕获边沿改变为下降沿捕获,目的是捕获后面的下降沿。

当下降沿到来时,发生第二次捕获,计数器CNT的值会再次被锁存到捕获寄存器TIM_CCRx中,而且还会进入捕获中断,在中断服务程序中记录一次捕获,并把捕获寄存器的值读取到value2中,清除捕获记录标志。然后把捕获边沿设置为上升沿捕获。

在测量脉宽过程中需要来回切换捕获边沿的极性,如果测量的脉宽时间比较长,定时器就会发生溢出,溢出时会产生更新中断,我们可以在中断里对溢出进行记录处理。

PWM输入模式

测量脉宽和频率还有一个更简便的方法:PWM输入模式,该模式是输入捕获的特例,只能使用通道1和2,通道3和4不能使用。测量脉宽和频率的方法只使用一个捕获寄存器,PWM输入模式需要占用两个捕获寄存器

当使用PWM输入模式时,因为一个输入通道(TIx)会占用两个捕获通道(ICx),所以一个定时器在使用PWM输入模式时最多只能使用两个输入通道(TIx)。

以输入通道TI1工作在PWM输入模式为例:

PWM信号由输入通道TI1进入,因为是PWM输入模式,所以信号被分为两路(TI1FP1、TI1FP2)。其中一路是周期,另一路是占空比,具体哪一路信号对应周期还是占空比,得从程序上设置哪一路信号作为触发输入(作为触发输入的那一路信号对应的是周期,另一路信号对应的是占空比)。作为触发输入的那一路信号还需要设置极性(上升沿捕获还是下降沿捕获),一旦设置好触发输入的极性,另外一路硬件就会自动配置为相反的极性捕获,无需软件配置。

概况:选定输入通道,确定触发信号,然后设置触发性的极性(因为是PWM输入模式,所以另一路由硬件配置,无需软件配置)

当使用PWM输入模式时必须将从模式控制器配置为复位模式(配置寄存器TIMx_SMCR:SMS[2:0]实现),即当我们启动触发信号开始进行捕获时,同时把计数器CNT复位清零。

上图为例,PWM信号由输入通道TI1进入,配置TI1FP1为触发信号,上升沿捕获。

当上升沿到来时IC1和IC2同时捕获,计数器CNT清零;

到了下降沿时IC2捕获,计数器CNT的值被锁存在捕获寄存器TIMx_CCR2中;

到了下一个上升沿到来时IC1捕获,计数器CNT的值被锁存在捕获寄存器TIMx_CCR1中。

其中TIMx_CCR2 + 1测量的是脉宽,TIMx_CCR1 + 1测量的是周期。(注意:TIMx_CCR1和TIMx_CCR2都必须加1,因为计数器是从0开始计数的)

从软件上看,PWM输入模式测量脉宽和周期更容易,缺点是需要占用两个捕获寄存器。

输出比较

输出比较模式共8种,具体由TIMx_CCMR1/2:OCxM[2:0]配置。

PWM输出就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装载寄存器ARR的值决定,占空比由比较寄存器CCR的值决定。

PWM模式分两种:PWM1、PWM2。

以PWM1模式为例。以计数器CNT计数方向不同还分为边沿对齐模式和中心对齐模式。PWM信号主要都是用来控制电机,一般电机控制用的都是边沿对齐模式,FOC电机一般用的是中心对齐模式。

PWM边沿对齐模式

在递增计数模式下,计数器从0计数到自动重装载值(TIMx_ARR寄存器的内容),然后重新从0开始计数并生成计数器上溢事件。

在边沿对齐模式下,计数器CNT只工作在一种模式:递增或递减模式。

以CNT工作在递增模式为例,ARR=8,CCR=4,CNT从0开始计数。

当CNT < CCR的值时,OCXREF为有效的高电平,此时比较中断寄存器CCxIF置位。

当CCR <= CNT <= ARR时,OCXREF为无效的低电平。

然后CNT又从0开始计数并生成计数器上溢事件,以此循环往复。

PWM中心对齐模式

中心对齐模式下,计数器CNT是工作在递增/递减模式下。

开始时,计数器CNT从0开始计数到自动重装载值减1(ARR-1),生成计数器上溢事件;

然后从自动重装载值开始向下计数到1,生成计数器下溢事件。然后从0开始重新计数,以此往复。

以PWM1模式的中心对齐模式为例,ARR=8,CCR=4。

第一阶段计数器CNT工作在递增模式下从0开始计数。

        当CNT < CCR的值时,OCXREF为有效的高电平。

        当CCR <= CNT <= ARR时,OCXREF为无效的低电平。

第二阶段计数器CNT工作在递减模式下从ARR的值开始计数。

        当CNT > CCR的值时,OCXREF为无效的低电平。

        当1 <= CNT <= CRR时,OCXREF为有效的高电平。

在波形图中可以把波形分为两个阶段,

第一个阶段是计数器CNT工作在递增模式的波形,这个阶段又分为1和2两个阶段。

第一个阶段是计数器CNT工作在递减模式的波形,这个阶段又分为3和4两个阶段。

中心对齐模式下的波形特征是1和3的时间相等,2和4的时间相等。

中心对齐模式又分为中心对齐1/2/3三种,具体由寄存器TIMx_CR1:CMS[1:0]配置。具体的区别是比较中断标志位CCxIF在何时置1。

中心模式1在CNT递减计数时置1,中心模式2在CNT递增计数时置1,中心模式3在CNT递增和递减计数时都置1

初始化结构体

TIM_TimeBaseInitTypeDef:时基结构体

typedef struct
{
    uint16_t TIM_Prescaler;           // 预分频器,基本定时器配置需要
    uint16_t TIM_CounterMode;         // 计数模式,通用定时器配置需要
    uint16_t TIM_Period;              // 定时周期,基本定时器配置需要
    uint16_t TIM_ClockDivision;       // 时钟分频,通用定时器配置需要
    uint8_t TIM_RepetitionCounter;    // 重复计数器
} TIM_TimeBaseInitTypeDef;   

对于TIM_CounterMode,有。

#define TIM_CounterMode_Up                 ((uint16_t)0x0000)    //边沿对齐模式,向上计数
#define TIM_CounterMode_Down               ((uint16_t)0x0010)    //边沿对齐模式,向下计数
 
#define TIM_CounterMode_CenterAligned1     ((uint16_t)0x0020)    //中央对齐模式1,计数器交替向上向下计数。
//产生下溢中断,配置为输出的通道(TIMx_CCMRx寄存器中CCxS=00)的输出比较中断标志位,只在计数器向下计数时被设置。
 
#define TIM_CounterMode_CenterAligned2     ((uint16_t)0x0040)    //中央对齐模式2,计数器交替向上向下计数。 
//产生上溢中断,配置为输出的通道(TIMx_CCMRx寄存器中CCxS=00)的输出比较中断标志位,只在计数器向上计数时被设置。
 
#define TIM_CounterMode_CenterAligned3     ((uint16_t)0x0060)    //中央对齐模式3,计数器交替向上向下计数。 
//产生下溢和上溢中断,配置为输出的通道(TIMx_CCMRx寄存器中CCxS=00)的输出比较中断标志位,在计数器向上和向下计数时均被设置。

对于TIM_ClockDivision,为时钟分频,设置定时器时钟CK_INT频率与死区发生器以及数字滤波器采样时钟频率分频比,可选择1/2/4分频。

TIM_OC_InitTypeDef:输出比较结构体

与TIM_OCxInit函数配合使用完成指定定时器输出通道初始化配置。

typedef struct {
     uint32_t OCMode;        // 比较输出模式,8种,常用PWM1或PWM2
     uint32_t Pulse;         // 比较输出脉冲宽度。0~65535
     uint32_t OCPolarity;    // 比较输出极性。决定着定时器通道有效电平
     uint32_t OCNPolarity;   // 互补输出极性
     uint32_t OCFastMode;    // 比较输出模式快速使能
     uint32_t OCIdleState;   // 空闲状态下比较输出状态
     uint32_t OCNIdleState;  // 空闲状态下比较互补输出状态
 } TIM_OCInitTypeDef;

TIM_IC_InitTypeDef:输入捕获结构体

与HAL_TIM_IC_ConfigChannel函数配合使用完成指定定时器输入通道初始化配置。

如果使用PWM输入模式需要与HAL_TIM_PWM_ConfigChannel函数配合使用完成指定定时器输入通道初始化配置。

typedef struct {
     uint32_t ICPolarity;   // 输入捕获通道的极性选择,上升沿/下降沿/边沿跳变触发捕获事件
     uint32_t ICSelection;  // 输入捕获通道的输入源,捕获通道ICx的信号可来自三个输入通道,分别为TIM_ICSELECTION_DIRECTTI、TIM_ICSELECTION_INDIRECTTI或TIM_ICSELECTION_TRC
     uint32_t ICPrescaler;  // 输入捕获预分频器,1/2/4/8,如果需要捕获输入信号的每个有效边沿,则设置1分频即可
     uint32_t ICFilter;     // 输入捕获滤波器,0x0~0xf。一般不使用滤波器,即设置为0
} TIM_IC_InitTypeDef;

ICSelection为输入捕获通道的输入源。

        当为TIM_ICSELECTION_DIRECTTI时,TI1、TI2、TI3、TI4对应IC1、IC2、IC3、IC4。

        当为TIM_ICSELECTION_DIRECTTI时,TI1、TI2、TI3、TI4对应IC2、IC1、IC4、IC3。

        当为TIM_ICSELECTION_TRC时,TI1、TI2、TI3、TI4对应TRC。

通用定时器实验:输入捕获

常规配置

USART:115200-8-N-1,支持重定向输出printf函数,勾选使用C库。

TIM5配置

硬件原理图得知,PA0是KEY1的按键IO,也是TIM5_CH1的IO。

TIM_HandleTypeDef htim5;

void MX_TIM5_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    TIM_IC_InitTypeDef sConfigIC = {0};

    htim5.Instance 					= TIM5;
    htim5.Init.Prescaler 			= 71;
    htim5.Init.CounterMode 			= TIM_COUNTERMODE_UP;
    htim5.Init.Period 				= 65535;
    htim5.Init.ClockDivision 		= TIM_CLOCKDIVISION_DIV1;
    htim5.Init.AutoReloadPreload 	= TIM_AUTORELOAD_PRELOAD_ENABLE;
    if (HAL_TIM_Base_Init(&htim5) != HAL_OK)
    {
        Error_Handler();
    }

    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK)
    {
        Error_Handler();
    }

    if (HAL_TIM_IC_Init(&htim5) != HAL_OK)
    {
        Error_Handler();
    }

    sMasterConfig.MasterOutputTrigger 	= TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode 		= TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)
    {
        Error_Handler();
    }

    sConfigIC.ICPolarity 	= TIM_INPUTCHANNELPOLARITY_RISING;
    sConfigIC.ICSelection 	= TIM_ICSELECTION_DIRECTTI;
    sConfigIC.ICPrescaler 	= TIM_ICPSC_DIV1;
    sConfigIC.ICFilter 		= 0;
    if (HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
    {
        Error_Handler();
    }
}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *tim_baseHandle)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    if (tim_baseHandle->Instance == TIM5)
    {
        __HAL_RCC_TIM5_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
		
        /**TIM5 GPIO Configuration
        PA0-WKUP     ------> TIM5_CH1
        */
        GPIO_InitStruct.Pin 	= GPIO_PIN_0;
        GPIO_InitStruct.Mode 	= GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull 	= GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        HAL_NVIC_SetPriority(TIM5_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(TIM5_IRQn);
    }
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *tim_baseHandle)
{
    if (tim_baseHandle->Instance == TIM5)
    {
        __HAL_RCC_TIM5_CLK_DISABLE();

        /**TIM5 GPIO Configuration
        PA0-WKUP     ------> TIM5_CH1
        */
        HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0);

        /* TIM5 interrupt Deinit */
        HAL_NVIC_DisableIRQ(TIM5_IRQn);
    }
}

测试环节

typedef struct              
{   
	uint8_t   ucFinishFlag;   // 捕获结束标志位
	uint8_t   ucStartFlag;    // 捕获开始标志位
	uint16_t  usCtr;          // 捕获寄存器的值
	uint16_t  usPeriod;       // 自动重装载寄存器更新标志 
}STRUCT_CAPTURE; 			  // 定时器输入捕获用户自定义变量结构体声明
STRUCT_CAPTURE TIM_ICUserValueStructure = {0, 0, 0, 0};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    TIM_ICUserValueStructure.usPeriod++;
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    TIM_IC_InitTypeDef IC_Config;

    if (TIM_ICUserValueStructure.ucStartFlag == 0)	// 上升沿捕获,开始捕获
    {
        __HAL_TIM_SET_COUNTER(htim, 0); 			// 清零定时器计数
        TIM_ICUserValueStructure.usPeriod 	= 0;
        TIM_ICUserValueStructure.usCtr	 	= 0;

        // 配置输入捕获参数,主要是修改触发电平
        IC_Config.ICPolarity 	= TIM_INPUTCHANNELPOLARITY_FALLING;
        IC_Config.ICSelection 	= TIM_ICSELECTION_DIRECTTI;
        IC_Config.ICPrescaler 	= TIM_ICPSC_DIV1;
        IC_Config.ICFilter 		= 0;
        HAL_TIM_IC_ConfigChannel(&htim5, &IC_Config, TIM_CHANNEL_1);
		
        // 清除TIM捕获/比较1中断标志位
        __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
		
        // 启动输入捕获并开启中断
        HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_1);
		
        TIM_ICUserValueStructure.ucStartFlag = 1;
    }
    else											// 下降沿捕获,结束捕获
    {
        // 获取定时器计数值
        TIM_ICUserValueStructure.usCtr = HAL_TIM_ReadCapturedValue(&htim5, TIM_CHANNEL_1);
		
        // 配置输入捕获参数,主要是修改触发电平
        IC_Config.ICPolarity 	= TIM_INPUTCHANNELPOLARITY_RISING;
        IC_Config.ICSelection 	= TIM_ICSELECTION_DIRECTTI;
        IC_Config.ICPrescaler 	= TIM_ICPSC_DIV1;
        IC_Config.ICFilter 		= 0;
        HAL_TIM_IC_ConfigChannel(&htim5, &IC_Config, TIM_CHANNEL_1);

        // 清除中断标志位
        __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
		
        // 启动输入捕获并开启中断
        HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_1);
		
        TIM_ICUserValueStructure.ucStartFlag 	= 0;
        TIM_ICUserValueStructure.ucFinishFlag 	= 1;
    }
}

void test(void)
{
	uint32_t time, TIM_PscCLK;
	
	初始化
	
	// TIM 计数器的驱动时钟
    TIM_PscCLK = HAL_RCC_GetHCLKFreq() / 71;

    /* 启动定时器 */
    HAL_TIM_Base_Start_IT(&htim5);
    /* 启动定时器通道输入捕获并开启中断 */
    HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_1);
	
	while (1)
    {
        /* 完成测量高电平脉宽 */
        if (TIM_ICUserValueStructure.ucFinishFlag == 1)
        {
            /* 计算高电平计数值 */
            time = TIM_ICUserValueStructure.usPeriod * 65535 + TIM_ICUserValueStructure.usCtr;
            /* 打印高电平脉宽时间 */
            printf("测得高电平脉宽时间:%d.%d s\n", time / TIM_PscCLK, time % TIM_PscCLK);
            TIM_ICUserValueStructure.ucFinishFlag = 0;
        }
    }
}

实验现象


http://www.kler.cn/a/109155.html

相关文章:

  • Flink API 的层次结构
  • C 语言标准库 - <errno.h>
  • Golang常见编码
  • Pandas | 数据分析时将特定列转换为数字类型 float64 或 int64的方法
  • 【人工智能】Transformers之Pipeline(二十三):文档视觉问答(document-question-answering)
  • Android HandlerThread 基础
  • SpringBoot 整合 Nacos 实现统一配置中心
  • Azure云工作站上做Machine Learning模型开发 - 全流程演示
  • DVWA-SQL Injection SQL注入
  • 当『后设学习』碰上『工程学思维』
  • iOS iGameGuardian修改器检测方案
  • Python requests之Cookie
  • 大数据Flink(一百零五):SQL性能调优
  • 常见的配置文件格式:yaml,json,xml,ini,csv等
  • 一、灵动mm32单片机_开发环境的搭建(Keil)
  • jvm对象内存划分
  • C++设计模式_17_Mediator 中介者
  • 2023.10.28 关于 synchronized 原理
  • SDL事件处理以及线程使用(2)
  • 模型对象CSS2DObject始终在画布的左上角(问题解决)
  • LeetCode 541 反转字符串 II 简单
  • Python——PyQt5以及Pycharm相关配置
  • MyBatis的使用(XML映射文件)
  • review-java-basis
  • Centos7 Linux系统下生成https的crt和key证书
  • 【已解决】VSCode运行C#控制台乱码显示