一、SysTick—系统定时器
1.1 SysTick简介
SysTick—系统定时器是属于CM3内核中的一个外设,内嵌在NVIC中。系统定时器是一个24bit的向下递减的计数器,计数器每计数一次的时间为1/SYSCLK,一般我们设置系统时钟SYSCLK等于72M。
1.2 SysTick寄存器介绍
SysTick—系统定时器有4个寄存器,在使用SysTick产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。
| 寄存器名称 | 寄存器描述 |
|---|---|
| CTRL | SysTick控制及状态寄存器 |
| LOAD | SysTick重装载数值寄存器(从某值开始倒数) |
| VAL | SysTick当前数值寄存器(当前计数值) |
| CALIB | SysTick校准数值寄存器 |
| 位段 | 名称 | 类型 | 复位值 | 描述 |
|---|---|---|---|---|
| 16 | COUNTFLAG | R/W | 0 | 如果在上次读取本寄存器后, SysTick 已经计到 了 0,则该位为 1。 |
| 2 | CLKSOURCE | R/W | 0 | 时钟源选择位,0=AHB/8,1=处理器时钟AHB |
| 1 | TICKINT | R/W | 0 | 1=SysTick倒数计数到 0时产生 SysTick异常请 求,0=数到 0 时无动作。也可以通过读取COUNTFLAG标志位来确定计数器是否递减到0 |
| 0 | ENABLE | R/W | 0 | SysTick 定时器的使能位 |
1.3 Delay函数实现
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值,72代表的是系统时钟72M
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器,即位0和位2置1
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器,SysTick定时器使能位置0
}
二、TIM—基本定时器
2.1 定时器分类
定时器分为基本定时器,通用定时 器和高级定时器。


不同的芯片拥有的定时器数量不同,具体看对应的数据手册。
2.2 基本定时器

时钟源:定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1 预分频器后分频提供,如果 APB1 预分频系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M。
自动重装载寄存器 :自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数 值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
定时时间:计数一次的时间为:1/CK_CNT,总的计数时间为(1/CK_CNT) * (ARR+1)。
2.3 通用定时器

2.4 高级定时器

2.4.1 时钟源
高级控制定时器有四个时钟源可选:
- 内部时钟源CK_INT
- 外部时钟模式1:外部输入引脚TIx(即TIMx_CHx)(x=1,2,3,4)
- 外部时钟模式2:外部触发输入ETR
- 内部触发输入(ITRx)
2.4.1.1 外部时钟模式1

2.4.1.2 外部时钟模式2

2.4.2 控制器
高级控制定时器控制器部分包括触发控制器、从模式控制器以及编码器接口。
2.4.3 时基单元
该部分即为基本控制器部分
2.4.4 输入捕获
输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,常用的有测量输
入信号的脉宽和测量PWM输入信号的频率和占空比这两种。
输入捕获的大概的原理就是,当捕获到信号的跳变沿的时候,把计数器CNT的值锁存到捕获寄存器 CCR中,把前后两次捕获到的 CCR寄存器中的值相减,就可以算出脉宽或者频率。如果捕获的脉宽的时间长度超过你的捕获定时器的周期,就会发生溢出,这个我们需要做额外的处理。
2.4.5 输出比较
输出比较就是通过定时器的外部引脚对外输出控制信号,有冻结、将通道 X(x=1,2,3,4)设置为匹配时输出有效电平、将通道 X 设置为匹配时输出无效电平、翻转、强制变为无效电平、强制变为有效电平、PWM1和PWM2这八种模式,具体使用哪种模式由寄存器 CCMRx的位 OCxM[2:0]配置。
2.4.6 断路功能
断路功能就是电机控制的刹车功能,使能断路功能时,根据相关控制位状态修改输出信号电平。在任何情况下,OCx和 OCxN输出都不能同时为有效电平,这关系到电机控制常用的H桥电路结构原因。
断路源可以是时钟故障事件,由内部复位时钟控制器中的时钟安全系统(CSS)生成,也可以是外部断路输入IO,两者是或运算关系。
系统复位启动都默认关闭断路功能,将断路和死区寄存器(TIMx_BDTR)的BKE为置1,
使能断路功能。可通过TIMx_BDTR 寄存器的BKP位设置设置断路输入引脚的有效电平,设置为1时输入BRK为高电平有效,否则低电平有效。
2.5 定时中断基本结构

2.6 定时器初始化结构体
时基结构体 TIM_TimeBaseInitTypeDef(用于定时器基础参数设置)
typedef struct {
uint16_t TIM_Prescaler; //预分频器
uint16_t TIM_CounterMode; //计数模式,可设置为向上计数、向下计数以及中心对齐
uint32_t TIM_Period; //定时器周期,实际就是设定自动重载寄存器ARR的值
uint16_t TIM_ClockDivision; //时钟分频,设置定时器时钟CK_INT频率与死区发生器以及数字滤波器采样时钟频率分频比
uint8_t TIM_RepetitionCounter; //重复计数器,只存在于高级定时器
} TIM_TimeBaseInitTypeDef;
输出比较结构体TIM_OCInitTypeDef(用于输出比较模式)
typedef struct{
uint16_t TIM_OCMode; //比较输出模式,比较输出模式选择,总共有八种,常用的为PWM1/PWM2
uint16_t TIM_OutputState; //比较输出使能,决定最终的输出比较信号OCx是否通过外部引脚输出
uint16_t TIM_OutputNState; //比较互补输出使能,决定OCx的互补信号OCxN是否通过外部引脚输出
uint16_t TIM_Pulse; //脉冲宽度
uint16_t TIM_OCPolarity; //输出极性
uint16_t TIM_OCNPolarity; //互补输出极性
uint16_t TIM_OCIdleState; //空闲状态下比较输出状态
uint16_t TIM_OCNIdleState; //空闲状态下比较互补输出状态
} TIM_OCInitTypeDef;
输入捕获结构体 TIM_ICInitTypeDef (用于输入捕获模式)
typedef struct{
uint16_t TIM_Channel; // 输入通道选择,可选TIM_Channel_1、TIM_Channel_2、TIM_Channel_3或 TIM_Channel_4四个通道
uint16_t TIM_ICPolarity; // 输入捕获触发选择,输入捕获边沿触发选择,可选上升沿触发、下降沿触发或边沿跳变触发
uint16_t TIM_ICSelection; // 输入捕获选择,捕获通道ICx的信号可来自三个输入通道,分别为TIM_ICSelection_DirectTI、TIM_ICSelection_IndirectTI 或 TIM_ICSelection_TRC。如果是普通的输入捕获,4个通道都可以使用,如果是PWM输入则只能使用通道1和通道2。
uint16_t TIM_ICPrescaler; // 输入捕获预分频器,可设置1、2、4、8分频
uint16_t TIM_ICFilter; // 输入捕获滤波器,一般我们不使用滤波器,即设置为0
} TIM_ICInitTypeDef;
断路和死区结构体TIM_BDTRInitTypeDef(用于断路和死区参数的设置,属于高级定时器专用,用于配置断路时通道输出状态,以及死区时间)
typedef struct{
uint16_t TIM_OSSRState; // 运行模式下的关闭状态选择
uint16_t TIM_OSSIState; // 空闲模式下的关闭状态选择
uint16_t TIM_LOCKLevel; // 锁定配置
uint16_t TIM_DeadTime; // 死区时间
uint16_t TIM_Break; // 断路输入使能控制
uint16_t TIM_BreakPolarity; // 断路输入极性
uint16_t TIM_AutomaticOutput; // 自动输出使能
} TIM_BDTRInitTypeDef;
三、定时中断库函数实现
根据定时中断基本结构,首先确定时钟源,之后配置时钟模式,再配置时基单元,最后配置中断输出控制与NVIC。
以通用定时器为例。
3.1 确定时钟源,配置时钟模式
以RCC内部时钟和外部时钟模式2为例
RCC:
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
/*配置时钟源*/
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
外部时钟模式2:
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为上拉输入
/*外部时钟配置*/
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted,0x0F);
//选择外部时钟模式2,时钟从TIM_ETR引脚输入
//注意TIM2的ETR引脚固定为PA0,无法随意更改
//最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动
3.2 配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
//假设选择的是RCC内部时钟72MHz,分频之后时钟频率为10_000Hz,记一次的时间为100us,计数周期10000,总时间为1s
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
3.3 配置中断输出控制
/*中断输出配置*/
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断
3.4 配置NVIC
/*NVIC中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
/*NVIC配置*/
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
四、PWM基本结构

五、PWM库函数实现
5.1 PWM初始化
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
/*GPIO重映射(非必要步骤)*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,重映射必须先开启AFIO的时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //GPIO输出模式设置为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//受外设控制的引脚,均需要配置为复用模式
/*配置时钟源*/
TIM_InternalClockConfig(TIM3); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
/*时基单元初始化*/
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(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
/*输出比较初始化*/
TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量
TIM_OCStructInit(&TIM_OCInitStructure);
//结构体初始化,若结构体没有完整赋值,则最好执行此函数,给结构体所有成员都赋一个默认值
//避免结构体初值不确定的问题
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性,选择为低,若选择极性为低,则输出高低电平取反
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
//引脚重映射时需加此命令
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
/*TIM使能*/
TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行
占空比为CCR/(ARP+1)
5.2 修改CCR值
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM3, Compare); //设置CCR2的值
}
六、输入捕获
6.1 测量频率

6.2 PWMI(测量频率和占空比)

七、输入捕获库函数实现
7.1 测量频率
7.1.1 输入捕获初始化
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入
/*配置时钟源*/
TIM_InternalClockConfig(TIM3); //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
/*时基单元初始化*/
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 = 72 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
/*输入捕获初始化*/
TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择配置定时器通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,选择为上升沿触发捕获
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //输入信号交叉,选择直通,不交叉
TIM_ICInit(TIM3, &TIM_ICInitStructure); //将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
/*选择触发源及从模式*/
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
/*TIM使能*/
TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行
7.1.2 测量频率函数
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可
}
7.2 测量频率和占空比
7.2.1 PWMI初始化
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入
/*配置时钟源*/
TIM_InternalClockConfig(TIM3); //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
/*时基单元初始化*/
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 = 72 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
/*PWMI模式初始化*/
TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择配置定时器通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,选择为上升沿触发捕获
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //输入信号交叉,选择直通,不交叉
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure); //将结构体变量交给TIM_PWMIConfig,配置TIM3的输入捕获通道
//此函数同时会把另一个通道配置为相反的配置,实现PWMI模式
/*选择触发源及从模式*/
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
/*TIM使能*/
TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行
7.2.2 测量频率和占空比函数
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可
}
uint32_t IC_GetDuty(void)
{
return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1); //占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可
}
1万+

被折叠的 条评论
为什么被折叠?



