STM32——TIM定时器、PWM

定时器分类
类型使用编号总线描述
基本定时器TIM6、TIM7APB1
1,主要部分是一个带有自动重装载的 16 位累加计数器。
2,时基单元包含:
计数器寄存器TIMx_CNT, Count Up Timer】
预分频寄存器TIMx_PSC, Prescaler】
自动重装载寄存器TIMx_ARR, Auto-Reload Register】
3,计数器时钟由内部时钟【CK_INT, Clock Internal 提供。
通用定时器

TIM2、TIM3

TIM4、TIM5

APB1
高级定时器TIM1、TIM8APB2

 STM32F103C8T6定时器资源:只有一个高级定时器 TIM1 和三个通用定时器 TIM2、TIM3、TIM4。没有基本定时器。

1、基本定时器

基本定时器框架图

时钟:单片机时钟电路可以分为‌内部时钟外部时钟两种类型。内部时钟:单片机内部集成了一个时钟发生器,能够产生稳定的时钟信号。外部时钟:单片机需要外部连接一个时钟源,例如‌晶体振荡器或者外部时钟信号发生器。

基本定时器的时基单元有三部分组成:

计数器寄存器TIMx_CNT, Count Up Timer】
预分频寄存器TIMx_PSC, Prescaler】
自动重装载寄存器TIMx_ARR, Auto-Reload Register】

首先,基本定时器时钟(基准计数时钟频率)来源于内部时钟,频率值为系统主频:72MHz,即图中CK_PSC=72MHz。

预分频器对基准计数时钟(72MHz)进行预分频。如果预分频寄存器写0,分频系数为0+1=1,该分频器为不分频或者1分频,则分频器输出频率CK_CNT=72MHz。如果预分频寄存器写1,分频系数为1+1=2,该分频器为2分频,则分频器输出频率CK_CNT=72MHz/2=36MHz。

实际分频系数=预分频器的值+1;CK_CNT= CK_PSC/(PSC+1);

如上图下半部分:预分频寄存器值从0变到1,定时器不会立即就响应,会有一个缓冲器来同步修改定时器时钟,定时器时钟根据写入的值对频率进行减半,此时CK_CNT=36MHz。计数器会根据这个时钟频率来进行计数。

计数器的工作为:从0到往上计数,直到与自动重装载寄存器的值相等,产生中断或者事件。

设预分频寄存器值PSC=7200-1 ,自动重装寄存器ARR=10000-1,分频器时钟频率=CK_PSC=72MHz。

则计数器的时钟频率CK_CNT = CK_PSC / (PSC+1) = 72MHz/7200 = 10000Hz ,即一秒钟可以计数10000次,又因为自动重装寄存器ARR值为 10000-1 ,于是当计数器从0开始计数到10000,花费1秒。此时产生中断。  

计数器溢出频率 CK_CNT_OV= CK_CNT / (ARR+1)  =  CK_PSC / (PSC+1) / (ARR+1) 。

2、通用定时器

通用定时器的时钟信号不仅仅可以通过内部时钟获得,还可以通过外部时钟获得。而不同的外部时钟源模式下有多种不同的外部时钟输入源。通用定时器的时钟信号输入可分为内部时钟源、外部时钟源模式1和外部时钟源模式2。

通用定时器框架图

外部时钟源模式2:一个时钟源,通过ETR信号——分频器ETRP——滤波去毛刺ETRF——触发控制器——时基单元时钟

外部时钟源模式2

外部时钟源模式1下的时钟源有:4个时钟源,ETR引脚的信号、ITR信号、CH1引脚的边沿、CH1引脚和CH2引脚 。

模式1ETR引脚的时钟信号与模式2不同的是,它进入了TRGI(触发输入通道),再进入触发控制器。

外部时钟源模式1 :ETR时钟信号

ITRx(0-3)信号是来自其他定时器的,是其他定时器的TRGO输出。主要应用于主从设备级联。

外部时钟源模式1 :其他定时器时钟信号

例子:比如我可以先初始化TIM3,然后使用主模式把它的更新事件映射到TRGO上,接着再初始化TIM2,这里选择ITR2,对应的就是TIM3的TRGO(与ITR2连接),然后后面再选择时钟为外部时钟模式1,这样TM3的更新事件就可以驱动TM2的时基单元,也就实现了定时器的级联。

外部时钟模式1的CH1引脚的边沿、CH1引脚和CH2引脚 的时钟源以及高级定时器后面再讲。

3,实验学习

3.1 初始化定时器:

第1步:RCC开启时钟。同时开启定时器的基准时钟外设的工作时钟

第2步:选择时基单元的时钟源。这里选择内部时钟源

第3步:配置时基单元。包括PSC预分频器CNT计数器模式、ARR自动重装器。

第4步:配置输出中断控制。允许更新中断输出到NVIC

第5步:配置NVIC。在NVIC中打开定时器中断的通道,分配一个优先级

第6步:运行控制,使能计数器

#include "stm32f10x.h"                  // Device header


void Timer_Init(void)                                                // 定时器初始化函数
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);                // 第1步:RCC开启时钟
  
  TIM_InternalClockConfig(TIM2);                                     // 选择内部时钟源2
  
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;                // 时基单元参数结构体
  TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
  TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
  TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);                 // 配置时基单元

  TIM_ClearFlag(TIM2,TIM_FLAG_Update);                               // 清除中断标志位
  TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);                           // 使能中断
  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                    // NVIC优先级分组
  
  
  NVIC_InitTypeDef NVIC_InitStructure;                               
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;                    // NVIC通道
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_Init(&NVIC_InitStructure);                                    // NVIC通道初始化
  
  TIM_Cmd(TIM2,ENABLE);                                              // 启动定时器
}



/*
void TIM2_IRQHandler(void)                                           // 中断函数
{
  if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update) == SET)               
  {
    
    TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
  }

}
*/

3.2 PWM输出比较 OC(Output Compare):

通过 捕获/比较寄存器 CCR(Capture Compare Register)的值 与计数器的值做大小比较,对输出电平进行0或1的翻转,输出一定频率和占空比的PWM波形。

如上图,CNT计数累加,累加到ARR值停止,又继续从0开始。 在累加过程中,CNT值比CCR值小的时段对应输出高电平,比CCR值大的部分对应输出低电平。

PWM参数

(1) PWM频率      Freq =  CK_PSC / ( PSC + 1 ) / ( ARR + 1 )

(2) PWM占空比  Duty =  t1 / Ts  =  CCR / ( ARR + 1 )

(3) PWM分辨率  Reso = 1 / ( ARR + 1 )   

PWM分辨率为占空比的变化维度大小,如占空比可由1%的大小变化。

一般情况下,是通过想要输出的PWM波形呈现什么特性,来决定ARR、PSC和CCR值的设置。

如想输出PWM波形 频率为1KHz占空比为50%分辨率为1% ,则先由(3)可知需要先将ARR值设置为100-1,再由(2)可得CCR的值为50 ,最后由时钟频率(72MHz)和(1)式将PSC的值设为 720-1。

如何使用PWM来设置输出具有一定占空比的信号波形。首先需要先进行PWM初始化,再封装改变占空比和分辨率的函数,最后在主程序调用函数功能。

PWM基本结构

3.3 PWM初始化

第1步:RCC开启时钟,选择时钟源。开启定时器TIM的基准时钟GPIO外设的工作时钟,选择内部时钟源

第2步:并配置时基单元。配置PSC预分频器CNT计数器模式ARR自动重装器

第3步:配置输出比较单元。包括设置CCR值、输出比较模式、极性选择、输出使能。

第4步:配置GPIO。把PWM对应的GPIO口初始化为复用推挽输出模式。

第5步:运行控制使能启动计数器

PWM驱动呼吸灯
void PWM_Init()
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);      // 启动定时器 TIM2 时钟
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);     // 启动 GPIOA 时钟
  
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;           // 设置GPIO口为复用推挽模式
  GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
  GPIO_Init(GPIOA,&GPIO_InitStructure);                    // GPIO初始化
  
  TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
  
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;
  TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;                   // ARR
  TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;                // PSC
  TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);                // 初始化时基单元
  
  TIM_OCInitTypeDef TIM_OCInitStructure;                            
  TIM_OCStructInit(&TIM_OCInitStructure);                        // 初始化结构体 成员为默认值
  TIM_OCInitStructure.TIM_OCMode= TIM_OCMode_PWM1;               // 输出比较模式 PWM1  
  TIM_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_High;       // 输出比较极性
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  // 输出使能
  TIM_OCInitStructure.TIM_Pulse= 50 ;                              // CRR
  TIM_OC1Init(TIM2,&TIM_OCInitStructure);                          // 初始化输出比较单元
  
  TIM_Cmd(TIM2,ENABLE);                                            // 定时器使能


}

void PWM_SetCompare1(uint16_t Compare)                     // 设置CRR的值来改变输出引脚的PWM波形
{
  TIM_SetCompare1(TIM2,Compare);

}

// PWM.h 就不多赘述了



// main.c :CRR从0到100 改变PWM占空比 延时输出 再反过来

#include "stm32f10x.h"
#include "Delay.h"                  // Device header
#include "PWM.h"

uint8_t i;


int main(void)
{

    PWM_Init();
	  while(1)
	  {
      for(i=0;i<100;i++)
      {
        PWM_SetCompare1(i);
        Delay_ms(10);
      }
      for(i=0;i<100;i++)
      {
        PWM_SetCompare1(100 - i);
        Delay_ms(10);
      }
	  }

}

3.4 舵机驱动实验

要求按一下键,舵机旋转30度。

舵机要求周期为 20ms,则PWM输出频率为 1/20ms = 50Hz。

舵机要求高电平时间为 0.5ms—2.5ms,需要求出计数器范围,再通过占空比求出CCR范围

设PSC=72,则计数器的时钟频率CK_CNT = CK_PSC / (PSC+1) = 72MHz/72 = 1MHz , 即一秒钟可以计数1M。又需要PWM输出频率 = CK_CNT / (ARR +1) = 50Hz,得ARR=20K。

计数器1s计数1M,计数20K需要0.02s=20ms。舵机要求高电平时间范围为 0.5ms—2.5ms,对应计数时间0—20ms,相当于计数0—20K范围中0.5K—2.5K (500—2500)。

因此在PWM_Init(); 中将ARR值设为20000 - 1;PSC值为72 - 1;CCR默认为0。

TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;                   // ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;                // PSC

TIM_OCInitStructure.TIM_Pulse= 0 ;                // CCR

PWM驱动舵机
// Servo.c
#include "stm32f10x.h"
#include "PWM.h"


void Servo_Init(void)
{
    PWM_Init();
}

void Servo_SetAngle(float Angle)
{
    PWM_SetCompare2( Angle / 180 * 2000 + 500);
}


// main.c
#include "stm32f10x.h"
#include "Delay.h"   
#include "PWM.h"
#include "Servo.h"   
#include "Key.h"


uint8_t KeyNum;
float Angle=0;

int main(void)
{
    OLED_Init();
    Key_Init();
    Servo_Init();
    
    OLED_ShowString(1, 1, "Angle:");    

	  while(1)
	  {
        KeyNum = Key_GetNum();
        if(KeyNum == 1)
        {
          Angle += 30;  
          if(Angle > 180)
            {
               Angle = 0; 
            }
        }
        Servo_SetAngle(Angle);
        OLED_ShowString(1, 7, Angle, 3);
	  }

}

3.5 直流电机驱动实验

PWM驱动直流电机
// Motor.h
void Motor_Init(void)
{
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Pin= GPIO_Pin_4 | GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
  GPIO_Init(GPIOA,&GPIO_InitStructure);
  
  PWM_Init;


}

void Motor_SetSpeed(uint8_t Speed)
{
  if(Speed >= 0)
  {
        GPIO_SetBits(GPIOA,GPIO_Pin_4);
        GPIO_ResetBits(GPIOA,GPIO_Pin_5);
        PWM_SetCompare1(Speed);                          // 使用PWM初始化函数设置0-100占空比
  }
  else
  {
        GPIO_ResetBits(GPIOA,GPIO_Pin_4);
        GPIO_SetBits(GPIOA,GPIO_Pin_5);
        PWM_SetCompare1(-Speed);
  }
}

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用STM32 Tim8定时器进行PWM输出,你需要进行以下步骤: 1. 配置GPIO口为复用推挽输出模式,并选择对应的定时器功能。 2. 配置定时器的时钟源和分频器。 3. 配置定时器的计数模式和自动重载值。 4. 配置PWM输出模式和占空比。 以下是一个简单的代码示例,可以实现Tim8定时器PWM输出: ```c // 选择GPIO口为复用推挽输出模式 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); // 选择GPIO口对应的定时器功能 GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_TIM8); // 配置定时器的时钟源和分频器 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = 999; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); // 配置PWM输出模式和占空比 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 500; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM8, &TIM_OCInitStructure); // 启动定时器 TIM_Cmd(TIM8, ENABLE); ``` 在上述示例中,我们将GPIOC的第9个引脚配置为复用推挽输出模式,并选择对应的定时器功能。然后我们配置了定时器的时钟源和分频器,以及PWM输出模式和占空比。最后启动定时器即可实现PWM输出。 需要注意的是,上述示例中的占空比为50%,即TIM_OCInitStructure.TIM_Pulse = 500。如果需要改变占空比,只需要修改这个值即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值