STM32基础篇(五)------TIM定时器比较输出

发布于:2025-03-24 ⋅ 阅读:(37) ⋅ 点赞:(0)

简介

在这里插入图片描述

定时器的类型

在《STM32F10xxx参考手册(中文).pdf》中可以看到下面三个章节
在这里插入图片描述

因此可以得到
在这里插入图片描述

高级定时器含有通用定时器的所有功能,通用定时器含有基本定时器的所有功能!!!!!!

在下图中可以看到这款芯片确实只有TIM1高级定时器和TIM2~TIM4通用定时器这四个定时器(无基本定时器)

在这里插入图片描述

定时中断基本结构

在这里插入图片描述

定时器计数方式

类型 计数方式
基本定时器 向上计数
通用定时器 向上/向下计数、中央计数
高级定时器 向上/向下计数、中央计数

基本定时器结构

在这里插入图片描述

● 计数器寄存器(TIMx_CNT)
● 预分频寄存器(TIMx_PSC)
● 自动重装载寄存器(TIMx_ARR)

基本定时器是TIM定时器中最简单的,以此为例解释其工作原理。通过上图易知基本定时器的工作流程就是:计数器寄存器(TIMx_CNT)一直在自增,当增加到自动重装载寄存器(TIMx_ARR)规定的数值的时候,则触发中断或者事件。

设 TIMxCLK = 72MHz即 每秒中有72,000,000(7200万)个时钟脉冲。1s=1000ms,1ms = 1000us,则1s = 1000000。则1us会有72个时钟脉冲。则1us会有72个时钟脉冲。现在我们假设上图中没有PSC预分频器,那么我想延时1s的话,TIMx_ARR的数值是不是需要设置为7200万。我们看一下TIMx_ARR的寄存器
在这里插入图片描述
只有16位欸,最大不过是0xFFFF=65535,这可完犊子了。。。。所以引入了预分频寄存器(TIMx_PSC)。这个东西通俗点解释可以这么理解。TIMxCLK 给过来的是72MHz的信号,相当于1s有72,000,000(7200万)个抖动,我嫌弃这抖的太快了,我想要一秒钟只抖动72,000次,那我就给TIMx_PSC设置为1000分频,这不每秒就只抖动72,000次就行了。OK了家人们,我们就给TIMx_PSC设置为1000,然后我想实现延迟1S,那TIMx_ARR是不是得设置为72,000,我i嘞个去啊这不又完犊子了,TIMx_ARR最大才65535啊。没关系家人们,我们看一下TIMx_PSC这逼崽子。
在这里插入图片描述
芜湖!!!!起飞。也是16位,那不就是说TIMx_PSC支持0~65535嘛,那我给它设置为10000,这下TIMx_ARR给个7200不就OK了嘛。

TIMx_PSC = 65535,TIMx_ARR = 65535
TIMx_PSC  *  TIMx_ARR  = 4,294,836,225
4,294,836,225 / 72,000,000 = 59.650503125

那么也就是说单个定时器最多可以延时 59.650503125秒。

在这里插入图片描述

看没错吧,手册上面给的也是59.6.

● 预分频寄存器(TIMx_PSC)
● 自动重装载寄存器(TIMx_ARR)
注意这俩寄存器中的这两句话:

1(TIMx_PSC) 计数器的时钟频率CK_CNT等于fCK_PSC/(PSC[15:0]+1)。
也就是说我们在设置TIMx_PSC的时候要手动-1,如果TIMx_PSC想要设置为10000,则实际需要设置为10000 - 1

2、TIMx_ARR这个和TIMx_PSC一样也是实际使用中需要-1,具体原因我没想明白也没找到。但是教程里面都是这样使用的。有知道的可以评论区交流。

因为stm32f103中没有基本定时器,我们使用通用定时器来实现一个 1s的延时。

#include "Tim.h"

uint32_t TIM2_Count = 0;

void TIM2_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    //1、选择时钟源为内部时钟
    TIM_InternalClockConfig(TIM2);

    //2、初始化时基单元
    TIM_TimeBaseInitTypeDef TIM_Prama;
    TIM_Prama.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    TIM_Prama.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
    TIM_Prama.TIM_Period = 7200 -1; //ARR
    TIM_Prama.TIM_Prescaler = 10000 - 1; //PCS
    TIM_Prama.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_Prama);

    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//TIM_TimeBaseInit 初始化时候触发了一次更新事件

    //3、选择中断触发方式
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//更新方式触发中断

    //4、配置中断优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    //5、配置NVIC
    NVIC_InitTypeDef NVIC_Prama;
    NVIC_Prama.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_Prama.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Prama.NVIC_IRQChannelSubPriority = 0;
    NVIC_Prama.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_Init(&NVIC_Prama);

    //使能定时器
    TIM_Cmd(TIM2,ENABLE);
}

uint32_t GetTIM2Count(void)
{
    return TIM2_Count;
}

void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        TIM2_Count++;
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

通用定时器

在这里插入图片描述

通过此图可以发现上面我们使用的系统内部时钟实现了定时中断,下面我们使用外部时钟模式2,即外部每触发10次,TIM2进入一次中断

#include "Tim_ETR.h"

uint16_t TIM2ETR_CNT = 0;
uint32_t TIM2ETR_Count = 0;

void TIM2ETR_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    //1、外部时钟2模式作为TIM2的时钟输入
    //TIM_ExtTRGPSC_OFF 不适使用分频
    //TIM_ExtTRGPolarity_NonInverted 高电平有效
    //以一个f采样频率采样N个点,如果N个点都一样才会有效输出
    TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    //GPIO初始化 上拉输入
    GPIO_InitTypeDef GPIO_InitPram;
    GPIO_InitPram.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitPram.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOA,&GPIO_InitPram);



    //2、初始化时基单元
    TIM_TimeBaseInitTypeDef TIM_Prama;
    TIM_Prama.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    TIM_Prama.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
    TIM_Prama.TIM_Period = 10 -1; //ARR
    TIM_Prama.TIM_Prescaler = 1 - 1; //PCS
    TIM_Prama.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_Prama);

    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//TIM_TimeBaseInit 初始化时候触发了一次更新事件

    //3、选择中断触发方式
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//更新方式触发中断

    //4、配置中断优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    //5、配置NVIC
    NVIC_InitTypeDef NVIC_Prama;
    NVIC_Prama.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_Prama.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Prama.NVIC_IRQChannelSubPriority = 3;
    NVIC_Prama.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_Init(&NVIC_Prama);

    //使能定时器
    TIM_Cmd(TIM2,ENABLE);
}

uint32_t GetTIM2ETRCount(void)
{
    return TIM2ETR_Count;
}

uint16_t GetTIM2ETRCNT(void)
{
    return TIM_GetCounter(TIM2);
}

void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        TIM2ETR_Count++;
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

高级定时器

在这里插入图片描述

通过上图可以发现高级定时器左上部分就是通用定时器,其余部分才是高级定时器独有的功能。

定时器的输出比较

基于上面的结构图可以发现,只有通用/高级定时器才有输出比较功能,基础定时器无此功能。

在这里插入图片描述

PWM

在这里插入图片描述
通过上图可以得到、

占空比越大,等效的模拟电压就越趋近于高电平
占空比越小,等效的模拟电压就越趋近于低电平

在这里插入图片描述
输出模式控制器见下表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

PWM频率公式推导过程

CK_PSC 输入时钟
PSC 分频系数
ARR 重新转载值

T = ((PSC + 1)*(ARR + 1)) / CK_PSC   T是定时周期
f = 1/T
=> f = CK_PSC / ((PSC + 1)*(ARR + 1)) 
=> f = CK_PSC / (PSC + 1) / (ARR + 1)

下面是个使用PWM驱动舵机示例(舵机型号:G90),使用旋转编码器控制舵机旋转角度

#include "Servo.h"

//通用定时器TIM2 CH3通道 ----PA2
//PA2输出PWM波
//舵机需要的PWM时钟周期为20ms  舵机转角范围0~180度
//0.5ms ------ 0度
//1.0ms ------ 45度
//1.5ms ------ 90度
//2.0ms ------ 135度
//2.5ms ------ 180度

void Servo_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

    TIM_InternalClockConfig(TIM2);

    
    //GPIO初始化 上拉输入
    GPIO_InitTypeDef GPIO_InitPram;
    GPIO_InitPram.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitPram.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitPram.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitPram);

    //2、初始化时基单元  20ms的周期
    TIM_TimeBaseInitTypeDef TIM_Prama;
    TIM_Prama.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    TIM_Prama.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
    TIM_Prama.TIM_Period = 20000 -1; //ARR 20ms
    TIM_Prama.TIM_Prescaler = 72 - 1; //PCS
    TIM_Prama.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_Prama);


    TIM_OCInitTypeDef TIM_OCParam;
    TIM_OCStructInit(&TIM_OCParam);//TIM_OCInitTypeDef 结构体中部分是高级定时器才使用的功能,因此需要一个默认值,所以使用TIM_OCStructInit初始化一个默认值
                                   //假设TIM_OCInitTypeDef中的A_Pram变量取值范围是(1,2)。如果不使用TIM_OCStructInit初始化可能A_Pram = 0.可能会影响系统。

    TIM_OCParam.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式选择模式1 向上计数:CNT<CCR时,REF置有效电平(+),CNT≥CCR时,REF置无效电平(-)
    TIM_OCParam.TIM_OutputState = TIM_OutputState_Enable;//输出使能
    TIM_OCParam.TIM_Pulse = 0; //CRR初始值
    TIM_OCParam.TIM_OCPolarity = TIM_OCPolarity_High;//指定输出级性
    TIM_OC3Init(TIM2, &TIM_OCParam);

    //使能定时器
    TIM_Cmd(TIM2,ENABLE);
}

/*

TIM_Prama.TIM_Period = 20000 -1; //ARR 20ms
TIM_Prama.TIM_Prescaler = 72 - 1; //PCS

((PCS + 1) * (ARR + 1)) / 定时器输入时钟 = 定时周期

0.5ms ------ 0度
1.0ms ------ 45度
1.5ms ------ 90度
2.0ms ------ 135度
2.5ms ------ 180度

将周期转换成计数的数值也就是

500  ------ 0度
1000 ------ 45度
1500 ------ 90度
2000 ------ 135度
2500 ------ 180度

可得到方程 计数值CRR = k * 2000 + 500;其中K是 n/180 (n是设置的度数)

*/



void SetCRRValue(uint16_t val)
{
    uint16_t Kval = (val *1.0f / 180.0f) * 2000 + 500;
    TIM_SetCompare3(TIM2, Kval);
}


网站公告

今日签到

点亮在社区的每一天
去签到