一、定时器简介
定时器总共分为4部分,8小结。
第一部分(定时中断、内外时钟源选择):定时器基本定时计数功能,定一个时间,让定时器每隔一段时间定时中断一次 。
第二部分(输出比较):定时器输出比较功能,常见于产生PWM波形,用于控制电机等设备。
第三部分(输入捕获、主从触发模式):输入捕获,PWMI可以测量方波的频率。
第四部分(编码器接口):定时器的编码器接口,可以更方便的读出正交编码器的波形。在编码电机测速中应用广泛。


本次主要使用通用定时器的功能,高级定时器主要用来驱动三相无刷电机使用。
1、基本定时器(向上计数)
1)、时基单元介绍

PSC、CNT和自动重装载寄存器构成了最基本的计时电路(时基单元),基本定时器只能选择内部时钟,所以时基单元直接连接到了内部时钟输入CK_INT。RCC的TIMxCLK一般都是系统的主频72MHz。
时基单元可以对系统的72MHz输入时钟进行分频,0为不分频,/1。值为0-65525,即1-65536。
CNT(16bit)对输入的时钟进行计数,没来一个上升沿,计数+1,CNT可以从0加到65535,正常会一直加到目标值,如果到了目标值,会产生中断,中断结束后会从继续0开始。
自动重装载寄存器(16bit)用于指定CNT计数的目标值,指定使CNT自动重装恢复初始值的目标值。

2)、主模式触发DAC介绍
主模式触发DAC,即如果需要定时输出DAC的电压点,一般思路是定时器定时中断,每隔一段时间使用定时中断内手动触发一次DAC。然后DAC输出。这样频繁的中断会影响主程序的运行和其他中断的响应。
STM32设定了主模式触发,可以使用主模式触发DAC,把定时器的定时事件动作U映射到触发输出DRGO(Trigger Out),然后TRGO直接接到DAC的出发引脚上。这样定时DAC就不需要通过中断来手动触发DAC的转换了。只需要把定时器主模式把更新事件通过主模式映射到TRGO,通过TRGO接到DAC定时进行转换即可。直接用硬件自动化,不需要软件的参与。

2、通用定时器(不止向上计数)
计数方式:
- 向上计数(从0开始到重装值)
- 向下计数(从重装值开始减到0,回到重装值申请中断)
- 中央对齐计数(0-重装值(申请中断)-0(申请中断)-重装值(申请中断))
圈出的是内外时钟源选择和主从触发模式结构。
1)、通用定时器除了可以选择内部时钟源还可以选择外部引脚输入的时钟。(在STM32中称为:外部时钟模式2)
即来自TIMx_ETR引脚的外部时钟,作为外部时钟源ETR。



2)、TRGI触发输入,把TRGI当作外部时钟输入。(TRGI还可以触发定时器的从模式,作为从模式触发输入)(STM32称为:外部时钟模式1)

(1).外部时钟模式1-方式1:TIMx_ETR外部引脚输入后,直接输入给TRGI,然后作为外部时钟输入时钟源。
(2).外部时钟模式1-方式2:第一个定时器 时基单元作为时钟源,通过TRGO触发到其他定时器,连接到其他定时器的ITRx上,然后在给第二个定时器 作为时钟输入时钟源。

外部时钟源模式1的定时器级联:

如表含义为:TIM1的TRGO接在了TIM2的ITR0上。 通过次触发模式可以实现定时器级联的功能,把TIM1接在TIM2上。
比如使用TIM4,使用它的主模式将更新事件映射到他的TRGO上,然后使用TIM2,把TIM2的ITR3作为外部时钟模式1的输入。
(3).外部时钟模式1-输入3:还可以选择TI1F_ED输入作为时基单元输入,即从TIMx_CH1输入,可以作为PWMI。TI1F_ED表示边沿的意思,上升沿和下降沿均有效。
(4).外部时钟模式1-输入4:通过输入捕获的TI1FP1和TI2FP2,即TIMxCH1和TIMxCH2。

3)、定时器主模式触发:将定时器更新事件的触发事件映射到TRGO上,用于触发其他定时器、DAC/ADC

4)、定时器输出比较电路(不能和输入比较电路同时使用)-用于输出PWM波形,驱动电机


CC(Compare/Capture),一般指输入捕获/输出比较单元。

通过对比CCR和CNT的值,不断输出电平跳变的PWM波形。
PWM:脉冲单周期中高电平时间和总脉冲时间的比值。

高级定时器输出比较原理图:

1、一般载OC1和OC2接入两个大功率的开关管MOS管(低电平上下断开,高电平导通)
上管导通下管断开输出的是高电平,下官导通上管断开输出低电平。
上下都导通是不允许的,所以有死区电路(或者互补电路)。上下都断开就是断路高阻态。
两个推挽电路构成H桥可以驱动直流电机。三个推挽电路可以驱动三相无刷电机了。
所以途中OC1和OC1N为互补PWM输出。通过死区生成电路,生成死区间隔,例如上管关断后通过死区电路延迟一段时间载导通下管。
通用定时器输出比较原理图:


- 当CNT>CCR1或者CNT =CCR1时,会给输出模式控制器传一个信号,然后输出模式控制器就会改变输出的oc1ref(参考信号reference)的高低电平。
- ETRF是定时器的小功能,一般不用
- ref可以映射到主模式控制器TRGO,一般不使用,直接到极性选择处,当CC1P寄存器=0,信号就会往上走,直接使用该信号,CC1P=1,则会取反。
- CC1E是输出使能电路,可以选择要不要输出
- 最终输出到OC1--TIMx_CHx引脚

(有效电平即,高电平,可以驱动的电平,无效电平是低电平不能驱动电机的电平)
一般使用向上计数,且PWM1和2模式只是改变了极性,CCR配置也可以改变极性。此处画图举例子。


如上图:黄线是重装载值,实际计算需要+1,填充为实际值-1(从0开始)。红线是CCR。

5)、定时器输入捕获(input-capture)电路-用于测量输入方波PWM和频率


通用定时器有4个输入捕获通道,高级定时器也有4个输入捕获通道。基本定时器没有输入捕获功能。

STM32只能测量方波,若需要测量3.3V正弦波,那么需要使用转换电路(例如运放搭建比较器)使正弦波转换为方波, 若测量的电压非常高,还需要考虑隔离的问题(隔离放大器、电压互感器)。
测频法适合测量高频信号,若频率太低,闸门时间内没有计次,则失败,或误差大。测频法闸门时间内更新一次,更新慢。但是等于自带均值滤波,较稳定。
测周法适合测量低频信号,若频率太高,那么对标准频率要求太高,容易失败,或误差增大。结果更新快,但是受噪声影响大。
对于测频方法的选择,需要根据中介频率计算,为N相等时的两个公式计算出的。
异或逻辑:输入的两个值相同为0,不同为1。三个值则从左向右运算。
在异或电路中,若输入有一个反转了电平,那么输出则反转电平。定时器异或门用于三相无刷电机使用,有三个霍尔传感器检测转子的位置。该定时器输入作为无刷电机的接口定时器,驱动三相电机的换相。


交叉使用的意义:
1、灵活切换。2、可以使用IC1上升沿捕获PWM,使用IC2下降沿捕获频率和周期,一个定时器即可一起捕获。
另外的TRC作为IC输入,目的也是为了驱动无刷电机。

每有一个触发信号CC2I就能发生一个捕获事件。事件会在状态寄存器产生标志位,同时也可以产生中断。

因此可以设置边沿捕获,没有边沿进行CNT捕获到CCR一次。CNT的计数可以通过内部时钟频率计算两个上升沿的时间间隔。为周期,再取倒数,为频率。每次取出CCR需要使CNT清零,这样每次取出的CCR才是两次上升沿的时间间隔。总结为一次捕获过后,使CNT自动清零的动作,可以使用主从触发模式自动完成。

CC1S可以对输入进行选择,ICPS可以对分频进行选择CC1E使能开关。TI1F_ED和TI1FP1使用从模式触发,用于每次获取CCR后将CNT计数清0。
主从模式触发:

主模式:用于触发别的片上外设,到TRGO。
从模式:接收其他或者自身外设的一些信号,用于控制自身定时器的运行。被别的信号控制。所以叫从模式。到TRGI。
触发源选择:选择从模式触发源信号选择。
从模式触发CNT自动清零,使用TI1FP1触发Reset即可。

TI1FP1捕获频率
TI1FP2捕获占空比,CCR2/CCR1为占空比。
6)、TIM编码器接口

常用于电机测速,当电机转速比较高的时候,一般使用霍尔传感器或者光栅进行测速。


对于正交编码器,使用任意一相可以用来测频率。两个一起可以用来测方向。
正交信号对比单独信号的优势:
1、抗噪声,可以设计外围抗噪声电路,当一相没变化,另一相跳变时,不进行编码器计数。
2、精度更高,AB相都可以计次,相当于计次频率提高了一倍。

TI1FP1和TI2FP2的前序电路,也直通编码器接口,可以使用,后续的交叉和预分频电路不能使用。然后通过编码器接口控制CNT增减。其中CK_PSC和时基单元配置的计数方向编码器接口不能控制和使用(这些在配置时基单元时才能使用,此处相当于编码器作为TIM3的时钟,PSC可以使用进行分频)。CNT根据编码器的正反转进行±计数。

CNT正转自增,反转会变为65535然后递减,可以通过将16bit无符号转换为16bit有符号数,可以直接转换为负数。
对工作模式分析:
1、TI1FP1和TI2FP2对应A、B相信号,对应上表,A上升沿,B相低电平时,CNT向上计数。
2、可选择有效边沿,CNT的增减根据有效边沿配置和A、B相边沿和电平状态控制。

如图,通过AB相边沿和电平计数,可以对信号毛刺进行过滤。
极性选择


在输入捕获模式下,此处极性选择是选择上升还是下降沿进行CNT计数,即边沿选择。
在编码器接口模式下,此处为高低电平极性选择,选择是否反转高低电平(边沿也会反转),相当于选择是否添加一个非门。配置上升沿选择,为不反转不反相。


3、高级定时器(不止向上计数)

和通用定时器不同:
1、增加重复次数计数器,相当于对输出的信号做了分频,可以计次计数更新触发一次更新事件
2、增加DTG死区生成电路,右边的输出引脚比通用定时器变味了2个互补输出,可以输出一段互补的PWM波形,是为了驱动三相无刷电机的。三相无刷电机驱动需要三个桥臂,每个桥臂用两个大功率开关管来控制,共6个。所以前三路PWM输出变为了互补输出。为了防止互补输出的PWM驱动桥臂时,由于器件的不理想造成短暂的直通现象,所以加上了死区生成电路。在开关切换的瞬间,生成一定时常的死区。使得桥臂的上下管都关断,防止直通。
3、最下面的刹车输入功能,是为了给电机驱动提供安全保障的。如果外部BKIN产生了刹车信号,或者内部时钟失效产生了故障,那么内部电机自动会切断电机的输出。
4、定时器使用流程和框图

运行控制:控制寄存器对的位,控制计数方式、启停等。
程序1:定时器定时中断。使用RCC内部时钟
程序2:外部方波作为时钟输入。使用ETR外部引脚输入作为时钟。
5、其他
1.预分频器时序

PSC为预分频系数,CK_PSC为系统时钟频率。
可以看到:
1、当预分频控制寄存器写入新值,此处为1,实际的预分频缓冲器并未获取到新值,分频系数未生效。
2、0为不分频,1为2分频,分频系数在程序运行期间写入不会直接生效,在产生更新事件的上升沿会生效,在预分频计数器会根据分频值计脉冲数。
3、此处FC为重装寄存器值,到达重装计数器后的上升沿会进行新计数和分频的开始。
4、定时器时钟脉冲是分频的PSC系统时钟后的频率。每次预分频计数到达分频值后,下一个PSC脉冲会生成一个PSC脉冲的高电平作为定时器时钟脉冲。

2.计数器时序

公式中ARR为自动重装载值,也要加1,自动重装载值也是从0开始。
获得精确的溢出事件,那么频率倒数即可。T=1/f。
电路原理图中带有黑色阴影的都是有影子寄存器的缓冲。

可以选择是否使用缓冲寄存器,下图可以看到时序,在计数时直接写入ARR寄存器可直接生效。 使用缓冲寄存器,写入的值不会立即生效。
这是没有使用影子寄存器的情况。
3.RCC时钟树
程序运行main函数之前,会运行SystemInit函数,即用来配置时钟树的。
AHB左边都是用来产生时钟的电路,右边都是时钟分配电路。SYSCLK就是系统时钟72MHz。
时钟产生电路有四个震荡源:
- 8MHz高速RC振荡器,8MHz HSI RC 。
- 外部的4-16Hz高速石英晶体振荡器,晶振,一般都接8MHz HSE OSC。
- 外部低速晶振,一般给RTC提供时钟,LSE OSC 32.768kHz。
- 内部40kHz低速RC振荡器,LSI RC 40kHz,给IWDG提供时钟
前两个高速晶振用来提供系统时钟,AHB、APB1、APB2的时钟都来源于这两个高速晶振。两个振荡器都可以使用,不过外部的石英振荡器比外部的内部的RC振荡器更稳定。
STSystemInit时钟配置顺序:
- 启动内部8MHz的RC振荡器作为系统时钟,先以8MHz系统时钟运行。
- 然后启动外部8MHz的石英振荡器,配置外部时钟,进入PLM锁相环进行倍频,8MHz倍频9倍,输出72MHz,锁相环输出稳定后选择锁相环输出为系统时钟。若外部晶振除了问题,那么程序的时钟就会慢了9倍。CSS用于切换时钟,一旦外部时钟异常,就会切换为内部RC振荡器。防止程序卡死造成事故。同高级定时器的刹车输入CSS。
- 时钟选择后进入AHB预分频器,系统默认为1,不分频。
- 然后进入APB1预分频器,默认分频为2,使用36MHz时钟频率。
- APB1预分频器的时钟,根据APB1分频进行了×1/×2直接供给TIM2-TIM7,所以这里默认使用72MHz
- AHB预分频器后进入APB2预分频,默认为1,所以APB2外设默认使用72MHz时钟频率。
- APB2预分频器的时钟,根据APB2分频进行了×1/×2直接供给TIM1和TIM8,所以这里默认使用72MHz。所有的定时器时钟若不更改默认72MHz。
- 所有的APB外设时钟在输出时都有时钟使能,所以我们每次使用时都要先使能总线时钟。
- 其他的时ADC时钟分频,使用ADC时自己配置。


二、定时器第一部分,功能示例程序,定时中断和外部时钟ETR
1、定时器定时中断,定时器内部每秒定时中断1次



确定自己用的定时器在APBx。
TIM_ClockDivision采样滤波器的时钟分频,在外部时钟输入时内部对滤波器的分频。
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
int main(){
Timer_Init();
OLED_Init();
OLED_ShowString(1,1,"TIM2Con:");
while(1){
//
OLED_ShowNum(1,9,Get_TIM2Count(),4);
}
return 0;
}
Timer.c
#include "stm32f10x.h" // Device header
uint16_t TIM2_Count;
void Timer_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);//开启APB1的TIM2时钟
TIM_InternalClockConfig(TIM2);//选择时基单元的时钟 ,默认也是内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//采样滤波器的时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式
TIM_TimeBaseInitStructure.TIM_Period = 10000-1;//ARR自动重装器值,从0开始
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1;//预分频器的值,从0开始。 72000000/10000 = 7200/7200=1Hz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值 高级定时器才有,不是计数器
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//写入预分频器和ARR等会立即生成更新事件生效,产生一次中断
TIM_ClearFlag(TIM2,TIM_IT_Update);//清除这次中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能TIMER - NVIC 中断使能
//中断使能以及优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//配置结束后使能定时器
TIM_Cmd(TIM2,ENABLE);
}
uint16_t Get_TIM2Count(void){
return TIM2_Count;
}
//中断函数名
void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){
TIM2_Count++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
Timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h" // Device header
void Timer_Init(void);
uint16_t Get_TIM2Count(void);
#endif
2、定时器外部时钟模式2,定时器使用外部输入的时钟(方波信号,可以使用外部方波信号,也可以接外部时钟信号(例如晶振和时钟芯片)),作为中断和计数源



采样滤波参数:

Timer.c
#include "stm32f10x.h" // Device header
uint16_t TIM2_Count;//TIM2中断次数计数变量
/**
* @brief TIM2定时器内部时钟源定时中断初始化函数,1s更新中断周期
* @param None
* @arg None
* @param None
* @arg None
* @retval None
*/
void Timer_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);//开启APB1的TIM2时钟
TIM_InternalClockConfig(TIM2);//选择时基单元的时钟 ,默认也是内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//采样滤波器的时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式
TIM_TimeBaseInitStructure.TIM_Period = 10000-1;//ARR自动重装器值,从0开始
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1;//预分频器的值,从0开始。 72000000/10000 = 7200/7200=1Hz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值 高级定时器才有,不是计数器
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//写入预分频器和ARR等会立即生成更新事件生效,产生一次中断
TIM_ClearFlag(TIM2,TIM_IT_Update);//清除这次中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能TIMER - NVIC 中断使能
//中断使能以及优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//配置结束后使能定时器
TIM_Cmd(TIM2,ENABLE);
}
/**
* @brief TIM2外部定时器模式2 ,ETR引脚A0作为外部时钟源,每10次上升沿,更新中断一次
* @param
* @arg
* @param
* @arg
* @retval None
*/
void TimerETR_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能TIMER2_ETR引脚GPIOA0的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);//开启APB1的TIM2时钟
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);
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,1);//TIM_ExtTRGPolarity_NonInverted和TIM_ExtTRGPolarity_Inverted触发相反
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//采样滤波器的时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式
TIM_TimeBaseInitStructure.TIM_Period = 10-1;//ARR自动重装器值,从0开始 10次触发一次
TIM_TimeBaseInitStructure.TIM_Prescaler = 1-1;//预分频器的值,从0开始。 72000000/10 = 7200000/1=7.2MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值 高级定时器才有,不是计数器
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//写入预分频器和ARR等会立即生成更新事件生效,产生一次中断
TIM_ClearFlag(TIM2,TIM_IT_Update);//清除这次中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能TIMER - NVIC 中断使能
//中断使能以及优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//配置结束后使能定时器
TIM_Cmd(TIM2,ENABLE);
}
/**
* @brief TIM2中断次数获取
* @param
* @arg
* @param
* @arg
* @retval TIM2中断次数
*/
uint16_t Get_TIM2Count(void){
return TIM2_Count;
}
/**
* @brief 获取CNT当前计数值
* @param
* @arg
* @param
* @arg
* @retval 返回CNT当前值
*/
uint16_t GetCount(void){
return TIM_GetCounter(TIM2);//
}
/**
* @brief TIM2中断函数,TIM2_Count对次数计数
* @param
* @arg
* @param
* @arg
* @retval None
*/
void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){
TIM2_Count++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
Timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h" // Device header
void Timer_Init(void);
void TimerETR_Init(void);
uint16_t Get_TIM2Count(void);
uint16_t GetCount(void);
#endif
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
int main(){
TimerETR_Init();
OLED_Init();
OLED_ShowString(1,1,"Con:");
OLED_ShowString(2,1,"TIM2Con:");
while(1){
//
OLED_ShowNum(1,9,GetCount(),4);
OLED_ShowNum(2,9,Get_TIM2Count(),4);
}
return 0;
}
三、定时器第二部分,功能示例程序,呼吸灯和舵机控制
1、PWM输出控制呼吸灯


main .c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint16_t LED_PWM = 0;
int main(){
OLED_Init();
Timer2_PWM_Init();
OLED_ShowString(1,1,"PWM:");
while(1){
//
OLED_ShowNum(1,5,LED_PWM,4);
PWM_SetCompare1(LED_PWM);//设置CCR
if(LED_PWM<1000){//此处CCR值10000每次增加到1000才会变为0 ,关乎到呼吸灯的呼吸频率
LED_PWM++;
}else{
LED_PWM=0;
}
}
return 0;
}
PWM.c
#include "stm32f10x.h" // Device header
void Timer2_PWM_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);
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);
TIM_InternalClockConfig(TIM2);//使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000-1;//ARR 72000000/72=1000000/1000=1000Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//初始化CCR通道引脚-此处使用TIM2-CH1
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性,REF输出有效时为高电平,表示极性不反转
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//打开和配置中断NVIC的功能不需要,直接开启定时器
TIM_Cmd(TIM2,ENABLE);
/*
例如:ARR自动装载值 = 10000
PSC分频 = 72
PWM频率 = 72000000/72/10000=100Hz 表示PWM一次高电平+低电平周期为10ms,每秒100次PWM输出
CCR = 0~10000 占空比 = 0%~100%
*/
}
/**
* @brief 对应Timer2_PWM_Init(void);函数控制呼吸灯的PWM输出,用于改变TIME2CH1的CCR
* @param
* @arg
* @param
* @arg
* @retval None
*/
void PWM_SetCompare1(uint16_t CCR_Value){
TIM_SetCompare1(TIM2,CCR_Value);
}
PWM.h
#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h" // Device header
void Timer2_PWM_Init(void);
void PWM_SetCompare1(uint16_t CCR_Value);
#endif
2、PWM输出控制舵机SG90,OLED显示角度

PWM控制舵机角度,电位计获取当前角度,如果大于目标角度电机反转,小于目标角度电机正转。PWM需要的周期为20ms,为50Hz。高电平宽度为0.5ms~2.5ms,为占空比2.5%~12.5%(2.5、5、7.5、10、12.5)。

main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint16_t LED_PWM;
int main(){
OLED_Init();
Timer2_PWMServo_Init();
OLED_ShowString(1,1,"PWM:");
LED_PWM=25;
while(1){
//
OLED_ShowNum(1,5,LED_PWM,4);
TIM_SetCompare1(TIM2,LED_PWM);
if(LED_PWM<125){
LED_PWM+=5;
}else{
LED_PWM=25;
}
Delay_ms(200);
}
return 0;
}
PWM.c
#include "stm32f10x.h" // Device header
/**
* @brief TIM2的比较输出PWM在GPIOA0输出;目前ARR=1000;CCR=0 占空比0;
* @param
* @arg
* @param
* @arg
* @retval None
*/
void Timer2_PWM_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);
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);
TIM_InternalClockConfig(TIM2);//使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000-1;//ARR 72000000/72=1000000/1000=1000Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//初始化CCR通道引脚-此处使用TIM2-CH1
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性,REF输出有效时为高电平,表示极性不反转
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//打开和配置中断NVIC的功能不需要,直接开启定时器
TIM_Cmd(TIM2,ENABLE);
/*
例如:ARR自动装载值 = 10000
PSC分频 = 72
PWM频率 = 72000000/72/10000=100Hz 表示PWM一次高电平+低电平周期为10ms,每秒100次PWM输出
CCR = 0~10000 占空比 = 0%~100%
*/
}
/**
* @brief TIM2的GPIOA0比较输出PWM模式1,控制舵机(PWM周期20ms,0.5ms-2.5ms(-90°~90°))
* @param
* @arg
* @param
* @arg
* @retval None
*/
void Timer2_PWMServo_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
//初始化TIM2_CH1引脚
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);
TIM_InternalClockConfig(TIM2);//使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000-1;//ARR 72000000/1440=50000/1000=50Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 1440-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//初始化CCR通道 -此处使用TIM2-CH1
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性,REF输出有效时为高电平,表示极性不反转
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//打开和配置中断NVIC的功能不需要,直接开启定时器
TIM_Cmd(TIM2,ENABLE);
/*
例如:ARR自动装载值 = 10000
PSC分频 = 72
PWM频率 = 72000000/72/10000=100Hz 表示PWM一次高电平+低电平周期为10ms,每秒100次PWM输出
CCR = 0~10000 占空比 = 0%~100%
*/
}
PWM.h
#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h" // Device header
void Timer2_PWM_Init(void);
void Timer2_PWMServo_Init(void);
#endif
3、PWM控制直流电机速度


01、02是输出,下管导通上管断开,接在PGND输出,下管导通上管断开,接在VM输出。


VM是驱动电压输出4.5-10V,根据电机供电接。
VCC是控制芯片逻辑电平,例如STM32,3.3V。
AO1、AO2和BO1、BO2为控制电机的输出端,可以从VM汲取电流。
PWMA、PWMB是两路电机控制的PWM信号输入。
STBY是待机控制引脚,如果接GND就不工作。需要的话可以直接接GPIO控制。
控制信号IN1、IN2逻辑,和PWM高低电平时对应的情况:

接线图:

使用TIM3_CH1,A6-AFIO引脚重映射到 B4引脚。

如上图,当使用TIM3的部分重映射1,此时TIM3的CH1和CH2都被重新映射引脚改变了
使用AFIO的引脚重映射功能。并且需要取消默认的JTAG功能

如果要使用TIM3_CH1重映射到PB4,需要取消默认的额JTAG功能。
SWJ就是SWG和JTAG。
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);//解除JTRST引脚的默认功能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDDisable, ENABLE);//解除JTAG的JTDO和JTDI功能,PA15和PA14仍为SWD功能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);//解除JTAG和SWD引脚的全部功能,及PA14、PA15、PB3、PB4、PA13都恢复默认引脚功能
程序:
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "TB6612IN.h"
#include "PWM.h"
int16_t motor_CCR;
int main(){
OLED_Init();
TB6612IN_Init();
Timer3_PWMMotor_Init();
OLED_ShowString(1,1,"PWM:");
SetMotor_Z();
while(1){
//
if(GetButton_motor()){
if(motor_CCR<1000){
motor_CCR+=100;
}else{
motor_CCR=-1000;
}
}
Control_Motor(motor_CCR);
OLED_ShowSignedNum(1,5,motor_CCR,5);
}
return 0;
}
PWM.c
#include "stm32f10x.h" // Device header
/**
* @brief TIM2的CH1,GPIOA0比较输出PWM在GPIOA0输出;目前ARR=1000;CCR=0 占空比0;
* @param
* @arg
* @param
* @arg
* @retval None
*/
void Timer2_PWM_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);
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);
TIM_InternalClockConfig(TIM2);//使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000-1;//ARR 72000000/72=1000000/1000=1000Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//初始化CCR通道引脚-此处使用TIM2-CH1
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性,REF输出有效时为高电平,表示极性不反转
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//打开和配置中断NVIC的功能不需要,直接开启定时器
TIM_Cmd(TIM2,ENABLE);
/*
例如:ARR自动装载值 = 10000
PSC分频 = 72
PWM频率 = 72000000/72/10000=100Hz 表示PWM一次高电平+低电平周期为10ms,每秒100次PWM输出
CCR = 0~10000 占空比 = 0%~100%
*/
}
/**
* @brief TIM2的CH1,GPIOA0比较输出PWM模式1,控制舵机(PWM周期20ms,0.5ms-2.5ms(-90°~90°))
* @param
* @arg
* @param
* @arg
* @retval None
*/
void Timer2_PWMServo_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
//初始化TIM2_CH1引脚
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);
TIM_InternalClockConfig(TIM2);//使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000-1;//ARR 72000000/1440=50000/1000=50Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 1440-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//初始化CCR通道 -此处使用TIM2-CH1
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性,REF输出有效时为高电平,表示极性不反转
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//打开和配置中断NVIC的功能不需要,直接开启定时器
TIM_Cmd(TIM2,ENABLE);
}
/**
* @brief 对应Timer2_PWM_Init(void);函数控制呼吸灯的PWM输出,用于改变TIME2CH1的CCR
* @param
* @arg
* @param
* @arg
* @retval None
*/
void PWM_SetCompare1(uint16_t CCR_Value){
TIM_SetCompare1(TIM2,CCR_Value);
}
/**
* @brief TIM3的CH1-PA6通过AFIO复用映射到PB4,通过PB4输出PWM,需要取消默认的PB4的JTAG功能
* @arg使用TB6612FNG 输出PWM信号和AIN1、AIN2控制信号,芯片输出AOUT1,AOUT2控制直流电机
* @param
* @arg
* @param
* @arg
* @retval None
*/
void Timer3_PWMMotor_Init(void){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);//TIM3的部分映射,TIM3的CH1和CH2引脚改变
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);
//初始化TIM2_CH1引脚
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
TIM_InternalClockConfig(TIM3);//使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000-1;//ARR 72000000/1440=50000/1000=50Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 1440-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
//初始化CCR通道 -此处使用TIM2-CH1
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性,REF输出有效时为高电平,表示极性不反转
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
//打开和配置中断NVIC的功能不需要,直接开启定时器
TIM_Cmd(TIM3,ENABLE);
/*
例如:ARR自动装载值 = 10000
PSC分频 = 72
PWM频率 = 72000000/72/10000=100Hz 表示PWM一次高电平+低电平周期为10ms,每秒100次PWM输出
CCR = 0~10000 占空比 = 0%~100%
*/
}
PWM.h
#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h" // Device header
void Timer2_PWM_Init(void);
void Timer2_PWMServo_Init(void);
void Timer2_PWMMotor_Init(void);
void Timer3_PWMMotor_Init(void);
void PWM_SetCompare1(uint16_t CCR_Value);
#endif
TB6612IN.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
extern int16_t motor_CCR;
void TB6612IN_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,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);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
void SetMotor_Z(void){
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
}
void SetMotor_F(void){
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
}
uint8_t GetButton_motor(void){
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == RESET){
Delay_ms(30);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == RESET);
Delay_ms(30);
return 1;
}else{
return 0;
}
}
void Control_Motor(int16_t PWM){
if(PWM<0){
SetMotor_Z();
if(PWM>=-400){
TIM_SetCompare1(TIM3,-400);
}else{
TIM_SetCompare1(TIM3,-motor_CCR);
}
}else if(PWM>0){
SetMotor_F();
if(PWM<=400){
TIM_SetCompare1(TIM3,400);
}else{
TIM_SetCompare1(TIM3,motor_CCR);
}
}else{
TIM_SetCompare1(TIM3,motor_CCR);
}
}
TB6612.h
#ifndef __TB6612IN_H
#define __TB6612IN_H
#include "stm32f10x.h" // Device header
void TB6612IN_Init(void);
void SetMotor_Z(void);
void SetMotor_F(void);
void Control_Motor(int16_t PWM);
uint8_t GetButton_motor(void);
#endif
四、定时器第三部分,功能示例程序,使用交叉通道(若使用直连通道则不使用交叉通道配置即可,删除TIM_PWMIConfig),PWMI测量PWM和频率(测周法)


使用TIM2CH1输出PWM指定频率,使用TIM3CH1测量PWM和频率。


程序为交叉通道TIM3CH1测量频率、TIM3CH2测量占空比。
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "IC.h"
uint8_t SetFreq = 0;//指定TIM2输出的PWM频率
uint8_t SetPWM = 0;//指定TIM2输出的PWM频率
int main(void){
OLED_Init();
Timer3_PWMI_Init();
OLED_ShowString(1,1,"SetFreq:000Hz");
OLED_ShowString(3,1,"SetPWM:000%");
OLED_ShowString(2,1,"GetFreq:000Hz");
OLED_ShowString(4,1,"GetPWM:000%");
SetFreq = 50;
SetPWM = 50;
while(1){
SetTIM3PWMI_InputPSC(SetFreq);
SetTIM3PWMI_InputPWM(SetPWM);
OLED_ShowNum(1,9,SetFreq,3);
OLED_ShowNum(2,9,TIM3_ICGetFreq(),3);
OLED_ShowNum(3,8,SetPWM,3);
OLED_ShowNum(4,8,TIM3_ICGetPWM(),3);
}
return 0;
}
IC.c
#include "stm32f10x.h" // Device header
/**
* @brief 使用TIM2CH1(PA0)输出PWM指定频率,使用TIM3CH1(PA6)的PWMI测量PWM和频率
* @param
* @arg
* @param
* @arg
* @retval None
*/
void Timer3_PWMI_Init(void){
//配置RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 ,ENABLE);//开启TIM3时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);//开启TIM2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);//开启GPIOA时钟
//初始化TIM2_CH1引脚和TIM3_CH1引脚的模式
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_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);
TIM_InternalClockConfig(TIM2);//使用timer2内部时钟
TIM_InternalClockConfig(TIM3);//使用timer3内部时钟
//配置TIM2时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
//TIM2_Prescaler决定测周法测频率的TIM3_Period大小,此处使用1440为50Hz的PWM频率,意义为TIM3_CH1引脚输入捕获采集高电平频率为50Hz
TIM_TimeBaseInitStructure.TIM_Period = 1000-1;//72000000/1440 = 50000/1000 = 50Hz的PWM
TIM_TimeBaseInitStructure.TIM_Prescaler = 1440-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//配置TIM3时基单元
//ARR自动重装载值 CCR捕获比较寄存器值 PSC内部时钟预分频
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536-1;//ARR=65536,目的为了进行最大的计数,
TIM_TimeBaseInitStructure.TIM_Prescaler = 1440-1;//PSC=1440,时钟计数频率为72000000/1440 = 50000。若测周法采集高电平计数应为50000Hz/50Hz = 1000个。
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0 ;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
//初始化TIM2-CH1的输出比较通道1
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较的模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性,REF输出有效时为高电平,表示极性不反转
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse = 600;//设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
//初始化TIM3-CH1的输入捕获通道1
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_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);
// //初始化TIM3-CH1的输入捕获通道2 , 做交叉通道
// TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
// TIM_ICInitStructure.TIM_ICFilter = 0xF;//输入捕获的滤波器
// TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;//下降沿
// TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
// TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//交叉通道
// TIM_ICInit(TIM3,&TIM_ICInitStructure);
//初始化TIM3-CH1的输入捕获通道2 , 做交叉通道,等效于上面的一大段注释程序
TIM_PWMIConfig(TIM3,&TIM_ICInitStructure);//自动配置TIM3CH1交叉通道2,函数内会自动把另一个通道配置成相反的配置,此处为TIM_Channel_2、TIM_ICPolarity_Falling、TIM_ICSelection_DirectTI
//设置TIGI的触发源,选择TIM3的TI1FP1
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//选择TIGI的从模式,TI1FP1触发从模式
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//从模式设置为重置(重置CNT计数,其中CCR1和CCR2在高低电平是分别会获取CNT计数值,以以至于用两个参数计算频率和占空比)
//打开和配置中断NVIC的功能不需要不用配置,直接开启定时器2
TIM_Cmd(TIM2,ENABLE);
TIM_Cmd(TIM3,ENABLE);
}
/**
* @brief 配合Timer3_PWMI_Init,修改TIM2CH1输出的占空比,其中ARR固定为1000,我们只需要修改CRR即可
* @param PWM:输入为预期PWM的指定占空比
* @arg 其中默认输入为0%-100%,输入为0-100,若站占空比=0,则频率=0.
* @param
* @arg
* @retval None
*/
void SetTIM3PWMI_InputPWM(uint8_t PWM){
if((PWM>=0)&&(PWM<=100)){
uint16_t Set_PWM = PWM*1000/100+1;
TIM_SetCompare1(TIM2,Set_PWM);
}
}
/**
* @brief 配合Timer3_PWMI_Init,修改TIM2CH1输出的占空比频率,其中ARR固定为1000,我们只需要修改PSC即可
* @param PSC:为输入的想要TIM2_CH1输出的PWM想要的频率和周期,单位为Hz
* @arg 默认输入为2Hz-100Hz
* @param
* @arg
* @retval None
*/
/*
分析,上面TIM2的PWM输出根据需要频率[2,100],TIM2的PSC设置范围为[36000,720],对应TIM3的采集计数为50000/需要频率=[25000,500],均符合uint16_t取值范围内
默认TIM2的PSC=1440,ARR=1000,PWM频率 = 72000000/1440/1000 = 50Hz
*/
void SetTIM3PWMI_InputPSC(uint16_t PSC){
if((PSC>=2)&&(PSC<=100)){
uint16_t Set_PSC = 72000000/1000/PSC-1;
TIM_PrescalerConfig(TIM2,Set_PSC,TIM_PSCReloadMode_Update);
}
}
/**
* @brief 配合Timer3_PWMI_Init函数,获取TIM3采集的PWM频率
* @param
* @arg
* @retval 返回采集到的PWM频率,计算方法为=TIM3计数频率(72000000/1440)/PWMI输入捕获计数
* @arg 此处受输入控制,默认为2Hz-100Hz
*/
uint8_t TIM3_ICGetFreq(void){
return (50000/TIM_GetCapture1(TIM3));
}
uint8_t TIM3_ICGetPWM(void){
return (TIM_GetCapture2(TIM3)*100/TIM_GetCapture1(TIM3));
}
IC.h
#ifndef __IC_H
#define __IC_H
#include "stm32f10x.h" // Device header
void Timer3_PWMI_Init(void);
void SetTIM3PWMI_InputPWM(uint8_t PWM);
void SetTIM3PWMI_InputPSC(uint16_t PSC);
uint8_t TIM3_ICGetFreq(void);
uint8_t TIM3_ICGetPWM(void);
#endif
五、定时器第四部分,功能示例程序,编码器测速
main.c
#include "stdio.h"
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder_Timer3.h"
extern int16_t Encoder_Speed;
int main(void){
OLED_Init();
Timer2Init();
Encoder_Timer3Init();
OLED_ShowString(1,1,"Speed:+00000rpm");
while(1){
OLED_ShowSignedNum(1,7,Encoder_Speed,5);
}
return 0;
}
Encoder_Timer3.c
#include "stm32f10x.h" // Device header
int16_t Encoder_Speed = 0;
/**
* @brief Timer3CH1和CH2的编码器接口使用
* @param
* @arg
* @param
* @arg
* @retval None
*/
void Encoder_Timer3Init(void){
//定时器和引脚时钟开启
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//引脚初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//基准时钟配置
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);
//输入捕获配置-只使用到滤波器和边沿极性选择器
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);//未用到的结构体变量初始化
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xf;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//电平极性不反转 和下面重复,改变其中一个极性可以反转
TIM_ICInit(TIM3,&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xf;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//电平极性不反转 和下面重复
TIM_ICInit(TIM3,&TIM_ICInitStructure);
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);//TIM_ICPolarity_Rising和上面配置相同
TIM_Cmd(TIM3,ENABLE);
}
/**
* @brief TIM2配合TIM3进行定时编码器计数获取 1Hz
* @param
* @arg
* @param
* @arg
* @retval None
*/
void Timer2Init(void){
//定时器时钟开启
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//内部时钟选择
TIM_InternalClockConfig(TIM2);
//基准时钟配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//被编码器接口托管,无作用
TIM_TimeBaseInitStructure.TIM_Period = 2000-1;//ARR 72000000/2000/36000=1Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 36000-1;//PSC 不分频
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//高级定时器参数,无用
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2,TIM_IT_Update);//清除这次中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能TIMER - NVIC 中断使能
//中断使能以及优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//中断通道选择定时器2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//配置结束后使能定时器
TIM_Cmd(TIM2,ENABLE);
}
/**
* @brief 配合Encoder_Timer3Init获取编码器计数,每次获取后清0
* @param
* @arg
* @param
* @arg
* @retval 返回编码器计数值
*/
int16_t GetEncoder(void){
uint16_t temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3,0);
return temp;
}
/**
* @brief TIM2中断函数,会和别处TIM2中断冲突
* @param
* @arg
* @param
* @arg
* @retval None
*/
void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){
Encoder_Speed = GetEncoder();
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
Encoder_Timer3.h
#ifndef __ENCODER_TIMER3_H
#define __ENCODER_TIMER3_H
#include "stm32f10x.h" // Device header
extern int16_t Encoder_Speed;
int16_t GetEncoder(void);
void Encoder_Timer3Init(void);
void Timer2Init(void);
#endif