前言
1.在学习STM32单片机时,我时常会因为急于实现功能,而省略掉原理部分,往往直接看代码,这样的学习使我每日既焦虑,又发现自己学到的东西总容易忘掉,变得更加焦虑,而每当问学长时,学长并不会耗费自己的精力去给你一步一步细讲,而正因如此,当我们遇到问题时,往往会停滞不前,反而更容易陷入焦虑的循环中。
2.学习单片机,嵌入式是一个长时间的事情,让我们放下心中的焦躁,静下心来,才能有所收获。
一、TIM简介
1.定时器是存在于STM32单片机中的一个外设。STM32中一共有8个定时器,分别是2个高级定时器(TIM1、TIM8),4个通用定时器(TIM2、TIM3、TIM4、TIM5)和2个基本定时器(TIM5、TIM6)。
2.定时器是16位计数器,2的16次方为65536,所以在72MHz的时钟下,最大的定时时间为59.65s。
二、定时器硬件结构分析
1.基本定时器
时基单元分析(计数计时电路)
1.由三个重要寄存器构成,预分频器,自动重装载寄存器,cnt计数器。
2.基本定时器只能选择内部时钟,所以,可看作由内部时钟输入,主频为72MHz,之后经过预分频器进行分频(分得的频率=主频/(PSC+1)),之后进入计数器,计数器从0开始,计数器计算每一个上升沿,计数器+1(0~65535),之后计数器的值会与自动重装值对比,自动重装值是一个固定的值,当计数器=自动重装值,计数器清0,执行中断。(这里计数器的模式只能向上计数)
3.这种中断称为更新中断,后通往NVIC,之后要配置NVIC的定时器通道。
2.通用定时器
电路分析
1.其核心部分和基本定时器一样,同样都是时基单元,不同的是,计数器的计数模式不止向上计数一种模式,还有向下计数模式,中央对齐计数模式。
2.向下计数模式,从自动重装值开始,减到0触发更新中断。
3.中央对齐模式,从0开始,向上自增,到达重装值后触发更新中断,然后向下自减,到达0后再次触发更新中断。
4。选择时钟源的不同,通用定时器可以选择
三、基本功能定时
1.概述
定时,简言之就是用来确定一段时间,然后每相隔这一段时间,产生一个中断,之后执行一个功能(一段程序)。
2.原理
当计数器的值与自动重装值相等时,触发更新中断,之后经过NVIC通道,达到每隔一段时间,执行中断,
3.代码框架
定时中断所走路线
//第一步,配置RCC,开启时钟
//第二步,配置GPIO,输入模式
//第三步,配置AFIO,
//第四步,配置EXTI,选择边沿触发方式
//第五步,配置NVIC,选择优先级
//第六步,运行控制
//第七步,使能计数器
4.常用函数
void TIM_DeInit
//恢复默认设置
void TIM_TimeBaseInit
//时基单元初始化
void TIM_TimeBaseStructInit
//用结构体给时基单元赋初值
void TIM_Cmd
//使能计数器
void TIM_ITConfig
//使能中断输出函数
时钟选择(分别对应图里的6个通道)
void TIM_InternalClockConfig
//内部时钟
void TIM_ITRxExternalClockConfig
//选择ITRx其他定时器通道
void TIM_TIxExternalClockConfig
//捕获通道时钟
void TIM_ETRClockMode1Config
//选择ETR外部时钟模式1通道
void TIM_ETRClockMode2Config
//选择ETR外部时钟模式2通道
配置ETR参数
TIM_ETRConfig
5.Timer.c与Timer.h文件
1.Timer.c
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启通用定时器TIM2的时钟
TIM_InternalClockConfig(TIM2);//选择时基单元的时钟(默认使用内部时钟)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,1表示不分频
//滤波器作用:滤掉信号的抖动干扰
//滤波器原理:在一个固定的时钟频率f下采样,若连续N个采样点电平一致,表示输入信号稳定,则输出
//若N个采样点电平不相同,表示信号有抖动,则直接输出低电平或输出上一次的电平
//采样频率由内部时钟直接来或由内部时钟加时钟分频来
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数器模式(向上/向下/中间3个)
TIM_TimeBaseInitStructure.TIM_Period=10000-1;//周期(10K的频率下计1w个数即为1s)
TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;//预分频器(72M对7200分频,得到10K的计数频率)
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);
//中断分组,2位抢占2位响应(分组只进行一次)
NVIC_InitTypeDef NVIC_InitStructure;//NVIC结构体
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//选择中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//指定通道是使能还是失能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//指定抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//指定响应优先级
NVIC_Init(&NVIC_InitStructure);//NVIC初始化函数
//配置NVIC
TIM_Cmd(TIM2,ENABLE);//启动定时器
}
2.Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
四、定时器输出比较
1.概述
主要是使用输出比较用STM32来s输出一个PWM波形,来驱动舵机和直流电机等。
2.原理
1.输出比较OC(Output Compare)
如图
流程:在进行CNT与CCR比较后,其结果在经过输出比较电路后,通过TIM_CH1输出到GPIO引脚上。
原理:通过进行对CNT计数器与CCR捕获比较寄存器的比较,来输出一个电平不断跳变的PWM方波。
每当CNT=RCC时,就会进行一次电平翻转,CNT>CCR且小于ARR(即在一个周期内)时输出高电平。
2.输出比较电路
原理:当CNT大于或等于CCR时,传输信号至控制器,后改变其输出的电平。
3.PWM.c和PWM.h文件
1.PWM.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
//1.RCC开启时钟(GPIO和TIM)
//2.选择时基单元的时钟源(定时中断选择内部时钟源)
//3.配置时基单元(预分频器、自动重装器、计数模式)
//4.配置CCR的值、输出比较模式、极性选择、输出使能的等参数
//5.配置GPIO(PWM对应的GPIO使用复用推挽输出模式)
void PWM_Init(void)
{
double Num;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//GPIO初始化
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启通用定时器TIM2的时钟
TIM_InternalClockConfig(TIM2);//选择时基单元的时钟(默认使用内部时钟)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,1表示不分频
//滤波器作用:滤掉信号的抖动干扰
//滤波器原理:在一个固定的时钟频率f下采样,若连续N个采样点电平一致,表示输入信号稳定,则输出
//若N个采样点电平不相同,表示信号有抖动,则直接输出低电平或输出上一次的电平
//采样频率由内部时钟直接来或由内部时钟加时钟分频来
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数器模式(向上/向下/中间3个)
TIM_TimeBaseInitStructure.TIM_Period=100-1;//周期(10K的频率下计1w个数即为1s)(ARR)
TIM_TimeBaseInitStructure.TIM_Prescaler=6-1;//预分频器(72M对7200分频,得到10K的计数频率)(PSC)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器(高级计数器才有)
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化结构体
TIM_OCInitTypeDef TIM_OCInit_InitStructure;
TIM_OCStructInit(&TIM_OCInit_InitStructure);//给结构体赋初值,以免对高级定时器等造成干扰
TIM_OCInit_InitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较的模式
//TIM_OCMode_Timing 冻结模式
//TIM_OCMode_Active 相等时置有效电平
//TIM_OCMode_Inactive 相等时置无效电平
//TIM_OCMode_Toggle 相等时电平翻转
//TIM_OCMode_PWM1 PW1模式
//TIM_OCMode_PWM2 PW2模式
TIM_OCInit_InitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//设置输出比较的极性
//TIM_OCPolarity_High 极性不翻转,REF有效是高电平,REF有效时输出高电平
//TIM_OCPolarity_Low REF电平取反,有效电平为低电平
TIM_OCInit_InitStructure.TIM_OutputState=ENABLE;//设置输出使能
TIM_OCInit_InitStructure.TIM_Pulse=50;//设置CCR
TIM_OC1Init(TIM2,&TIM_OCInit_InitStructure);
//初始化输出比较单元
TIM_Cmd(TIM2,ENABLE);//启动定时器
}
2.PWM,h
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
#endif
4.PWM简介(Pulse width modulation)
1.定义:PWM,就是脉冲宽度调制,也就是占空比可变的脉冲波形。
2.适用条件:PWM所应用的场景必须为惯性系统,像led,当立即变为低电平时,led并不会瞬间熄灭,而是具有惯性,在短时间内为逐渐熄灭。
3.应用:智能车,机器人等项目。呼吸灯、驱动电机等。
2、频率
频率为Ts的倒数,频率越大,则等效模拟信号越平稳。(即输出的方波整体上较为密集)
3、占空比(Duty Ratio)
1.定义:占空比指在一个脉冲循环内,通电时间相对于总时间所占的比例。(或者说在一个周期中,高电平的时间占整个周期的时间)
2.计算:D=Ton/Ts(Ton为一个周期内高电平的时间、Ts为高低电平变换周期的时间)
3.作用:频率一定时,可以通过改变占空比来调节电压。
例如高电平为5V,低电平为0V时,占空比为80%时,可以看作为4/5处的电压,即4V。
4、分辨率
1.定义:是指PWM最小能设定到的高电平时间所占周期的比例,即最小占空比。
2.在一个时钟下,若降低频率,则周期变大,分辨率增大。
五、定时器输入捕获的功能
1.概述
六、定时器编码器接口
1.概述
即接收正交信号,自动执行CNT的自增和自减,从而达到
3.Encoder.c和Encoder.h文件
1.Encoder.c
#include "stm32f10x.h" // Device header
//1.RCC开启时钟,开启GPIO和定时器时钟
//2.配置GPIO,把其选中的引脚配置成输入模式
//3.配置时基单元,一般选择不分频,自动重装给最大65535,cnt执行计数
//4.配置输入捕获单元,仅对滤波器和极性选择这两个参数有用
//5.配置编码器接口模式(调用函数)
//6.启动定时器
//若选择编码器模式择这个引脚被占用,基本无法执行其他功能
void Encoder_Init(void)
{
//1.开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//2.GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//3.编码器接口就是一个带方向控制的外部时钟,直接驱动计时器
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR(满量程计数)
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC(不分频)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
//4.输入捕获单元配置,
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);
//结构体不完整,用函数初始化赋一个初值
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;//滤波器oxF
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
//配置编码器接口
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
//参数1TIMX,参数2编码器模式(TI1,TI2,TI12)//TI1计数TI2,计数TI1,TI2都计数
//参数3,4(IC1与IC2的极性)(Falling通道反向,Rising通道不反向)
TIM_Cmd(TIM3, ENABLE);
}
int16_t Encoder_Get(void)
{
return TIM_GetCounter(TIM3);
// int16_t Temp;
// Temp = TIM_GetCounter(TIM3);
// TIM_SetCounter(TIM3, 0);
// return Temp;
}
2.Encoder.h
#include "stm32f10x.h"
#ifndef __ENCODER_H
#define __ENCODER_H
void Encoder_Init(void);
int16_t Encoder_Get(void);
#endif /* __PWM1_H */