STM32F103C8T6 - 定时器

一、定时器简介

定时器总共分为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。

时钟产生电路有四个震荡源:

  1. 8MHz高速RC振荡器,8MHz HSI RC 。
  2. 外部的4-16Hz高速石英晶体振荡器,晶振,一般都接8MHz HSE OSC。
  3. 外部低速晶振,一般给RTC提供时钟,LSE OSC 32.768kHz。
  4. 内部40kHz低速RC振荡器,LSI RC 40kHz,给IWDG提供时钟

前两个高速晶振用来提供系统时钟,AHB、APB1、APB2的时钟都来源于这两个高速晶振。两个振荡器都可以使用,不过外部的石英振荡器比外部的内部的RC振荡器更稳定。

STSystemInit时钟配置顺序:

  1. 启动内部8MHz的RC振荡器作为系统时钟,先以8MHz系统时钟运行。
  2. 然后启动外部8MHz的石英振荡器,配置外部时钟,进入PLM锁相环进行倍频,8MHz倍频9倍,输出72MHz,锁相环输出稳定后选择锁相环输出为系统时钟。若外部晶振除了问题,那么程序的时钟就会慢了9倍。CSS用于切换时钟,一旦外部时钟异常,就会切换为内部RC振荡器。防止程序卡死造成事故。同高级定时器的刹车输入CSS。
  3. 时钟选择后进入AHB预分频器,系统默认为1,不分频。
  4. 然后进入APB1预分频器,默认分频为2,使用36MHz时钟频率。
  5. APB1预分频器的时钟,根据APB1分频进行了×1/×2直接供给TIM2-TIM7,所以这里默认使用72MHz
  6. AHB预分频器后进入APB2预分频,默认为1,所以APB2外设默认使用72MHz时钟频率。
  7. APB2预分频器的时钟,根据APB2分频进行了×1/×2直接供给TIM1和TIM8,所以这里默认使用72MHz。所有的定时器时钟若不更改默认72MHz。
  8. 所有的APB外设时钟在输出时都有时钟使能,所以我们每次使用时都要先使能总线时钟。
  9. 其他的时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=1HzTIM_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=1HzTIM_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.2MHzTIM_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);//设置CCRif(LED_PWM<1000){//此处CCR值10000每次增加到1000才会变为0 ,关乎到呼吸灯的呼吸频率LED_PWM++;}else{LED_PWM=0;}}return 0;
}

PWM.c

#include "stm32f10x.h"                  // Device headervoid 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;//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter  = 0 ;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化CCR通道引脚-此处使用TIM2-CH1TIM_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;//设置CCRTIM_OC1Init(TIM2, &TIM_OCInitStructure);//打开和配置中断NVIC的功能不需要,直接开启定时器TIM_Cmd(TIM2,ENABLE);/*例如:ARR自动装载值 = 10000PSC分频 = 72PWM频率 = 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 headervoid 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;//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter  = 0 ;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化CCR通道引脚-此处使用TIM2-CH1TIM_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;//设置CCRTIM_OC1Init(TIM2, &TIM_OCInitStructure);//打开和配置中断NVIC的功能不需要,直接开启定时器TIM_Cmd(TIM2,ENABLE);/*例如:ARR自动装载值 = 10000PSC分频 = 72PWM频率 = 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;//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter  = 0 ;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化CCR通道 -此处使用TIM2-CH1TIM_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;//设置CCRTIM_OC1Init(TIM2, &TIM_OCInitStructure);//打开和配置中断NVIC的功能不需要,直接开启定时器TIM_Cmd(TIM2,ENABLE);/*例如:ARR自动装载值 = 10000PSC分频 = 72PWM频率 = 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 headervoid 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;//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter  = 0 ;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化CCR通道引脚-此处使用TIM2-CH1TIM_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;//设置CCRTIM_OC1Init(TIM2, &TIM_OCInitStructure);//打开和配置中断NVIC的功能不需要,直接开启定时器TIM_Cmd(TIM2,ENABLE);/*例如:ARR自动装载值 = 10000PSC分频 = 72PWM频率 = 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;//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter  = 0 ;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化CCR通道 -此处使用TIM2-CH1TIM_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;//设置CCRTIM_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;//PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter  = 0 ;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化CCR通道 -此处使用TIM2-CH1TIM_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;//设置CCRTIM_OC1Init(TIM3, &TIM_OCInitStructure);//打开和配置中断NVIC的功能不需要,直接开启定时器TIM_Cmd(TIM3,ENABLE);/*例如:ARR自动装载值 = 10000PSC分频 = 72PWM频率 = 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 headervoid 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 headervoid 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引脚输入捕获采集高电平频率为50HzTIM_TimeBaseInitStructure.TIM_Period = 1000-1;//72000000/1440 = 50000/1000 = 50Hz的PWMTIM_TimeBaseInitStructure.TIM_Prescaler  = 1440-1;//PSCTIM_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的输出比较通道1TIM_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;//设置CCRTIM_OC1Init(TIM2, &TIM_OCInitStructure);//初始化TIM3-CH1的输入捕获通道1TIM_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的TI1FP1TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);//选择TIGI的从模式,TI1FP1触发从模式TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//从模式设置为重置(重置CNT计数,其中CCR1和CCR2在高低电平是分别会获取CNT计数值,以以至于用两个参数计算频率和占空比)//打开和配置中断NVIC的功能不需要不用配置,直接开启定时器2TIM_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=1HzTIM_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;//中断通道选择定时器2NVIC_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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1558168.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

使用正则表达式删除文本的奇数行或者偶数行

用智谱清言和kimi搜出来的结果都没法在notepad生效&#xff0c;后面在overflow上找到的答案比较靠谱。 查找&#xff1a;^[^\n]*\n([^\n]*) 替换&#xff1a;\1 删除偶数行 查找&#xff1a;^([^\n]*)\n[^\n]* 替换&#xff1a;\1 代码解释 ^&#xff1a;这个符号代表字符…

Java面试题——第九篇(JVM)

1. Java中的强引用、软引用、弱引用和虚引用分别是什么 强引用 最常见的引用类型&#xff0c;在Java中&#xff0c;默认情况下&#xff0c;任何普通的对象引用都是强引用只要一个对象有强引用指向他&#xff0c;垃圾回收器永远不会回收该对象&#xff0c;即使系统内存紧张。 …

Linux的图形系统概述 (TODO)

&#xff08;TODO&#xff09; Linux graphics stack 现代 Linux 图形栈由多个子系统和层次组成&#xff0c;从应用程序到硬件之间的各个层面协同工作来处理图形显示和硬件加速。随着时间的推移&#xff0c;Linux 从传统的 **X Window System** 逐步过渡到 **Wayland**&#x…

三 星 SCX-4521F 硒 鼓 清 零 及 一 般 故 障 维 修 浅 谈

基本参数 耗材容量:SCX-4521D3/XIL(3000页) 功 率:平均功率350W、休眠模式10W 一般故障讲解 一、三星SCX-4521F打印机更换硒鼓(或加粉)后仍显示墨粉用尽 (加粉清零、关闭碳粉通知) 按菜单------#1934(快速按完)------屏幕会有TECH字母显示------菜单------向…

LeetCode-2608. 图中的最短环【广度优先搜索 图,腾讯面试真题】

LeetCode-2608. 图中的最短环【广度优先搜索 图&#xff0c;腾讯面试真题】 题目描述&#xff1a;解题思路一&#xff1a;【一图秒懂】枚举起点跑 BFS解题思路二&#xff1a;背诵版解题思路三&#xff1a; 题目描述&#xff1a; 现有一个含 n 个顶点的 双向 图&#xff0c;每个…

二分图的判定-染色法

二分图 如果一张无向图的N个节点可以分成A.B两个不相交的非空集合&#xff0c;并且同一集合内的点之间没有边相连&#xff0c;那么称该无向图为二分图(BipartiteGraph)。 定理&#xff1a;二分图不存在奇环(长度为奇数的环)。 因为每一条边都是从一个集合走到另一个集合&#…

构建宠物咖啡馆:SpringBoot框架的实现策略

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于Spring Boot的宠物咖啡馆平台的设计与…

malloc(0)

malloc(0) 在操作系统底层的实现涉及内存分配管理的多个方面。下面是对 malloc(0) 的实现原理的详细解释&#xff1a; 1. 内存分配管理 操作系统通过内存管理子系统来处理内存分配请求&#xff0c;包括 malloc 函数。内存分配通常使用以下几种策略&#xff1a; 堆管理&#…

卫星测绘AI技术-立哥尖端科研

分布式微波干涉测绘卫星是以多颗满足一定编队构形的卫星为平台&#xff0c;以合成孔径雷达 和高精度星间相对状态测量设备等为有效载荷&#xff0c;具备全天时、全天候获取雷达干涉影像数 据&#xff0c;快速测制全球数字表面模型、数字雷达正射影像等测绘产品能力的卫星系统…

论文解析三: D2-Net 用于联合描述和检测局部特征的可训练CNN

目录 1.D2-Net摘要2.D2-Net关键点介绍3. Joint Detection and Description (联合检测和描述)3.1 Feature Extraction3.2 Feature Detection3.2.1 Hard feature detection &#xff08;硬特征检测&#xff09;3.2.1 Soft Feature Detection&#xff08;软特征检测&#xff09; 3…

BUU刷题-Pwn-jarvisoj_typo(ARM符号表恢复技术,Rizzo,FLIRT)

解题所涉知识点&#xff1a; 泄露或修改内存数据&#xff1a; 堆地址&#xff1a;栈地址&#xff1a;libc地址&#xff1a;BSS段地址&#xff1a; 劫持程序执行流程&#xff1a;ARM_ROP 获得shell或flag&#xff1a;调用程序中的system 题目类型&#xff1a; ARM_Pwn arm32 …

Spring Boot 学习之路 -- Thymeleaf 模板引擎

前言 最近因为业务需要&#xff0c;被拉去研究后端的项目&#xff0c;代码框架基于 Spring Boot&#xff0c;后端对我来说完全小白&#xff0c;需要重新学习研究…出于个人习惯&#xff0c;会以 Blog 文章的方式做一些记录&#xff0c;文章内容基本来源于「 Spring Boot 从入门…

Docsify基础配置

一、激活加载动画 轻松修改index.html文件&#xff1a;<div id"app">内容加载中&#xff0c;请稍候...</div>二、设定文档标题与Github链接 <script>window.$docsify {name: 王涵的博客文档,repo: http://baidu.com,} </script>效果展示&…

需求7———通过一个简单的小需求来理清修改后端的思路

我今天下午刚刚完成了睿哥早上说的几个小问题&#xff0c;现在距离下班时间还有两个小时&#xff0c;已经没啥可干的了&#xff0c;然后我发现我之前做的很多需求还没有写文章来总结&#xff0c;所以现在趁着有空&#xff0c;我先写一下总结。这么多需求中&#xff0c;我挑了一…

【leetcode】238.除自身以外数组的乘积

由于该题不能使用除法&#xff0c;所以参考题解写一个左右乘积列表的方法 创建两个新的数组pef,suf 一个用于记录从左到右的乘积&#xff08;类似于动态规划的思想&#xff09;pef 另一个记录从右到左的乘积 bsuf&#xff08;注意suf是从右到左进行累乘&#xff09; 而pef的最左…

【3dgs】3DGS**(3D Geometry Sensing)与 **NeRF**(Neural Radiance Fields)对比

以下是 3DGS&#xff08;3D Geometry Sensing&#xff09;与 NeRF&#xff08;Neural Radiance Fields&#xff09;对比表格&#xff1a; 更加详细的资料&#xff0c;轻参考&#xff1a; NERF/3DGS 对比维度3DGS (3D Geometry Sensing)NeRF (Neural Radiance Fields)基本原理…

Linux之shell详解(Linux Shell Detailed Explanation)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

使用 Helsinki-NLP 中英文翻译本地部署 - python 实现

通过 Helsinki-NLP 本地部署中英文翻译功能。该开源模型性价比相对高&#xff0c;资源占用少&#xff0c;对于翻译要求不高的应用场景可以使用&#xff0c;比如单词&#xff0c;简单句式的中英文翻译。 该示例使用的模型下载地址&#xff1a;【免费】Helsinki-NLP中英文翻译本…

浙江大学机试试题合集(2)

🍰🍰🍰hello宝子们,今天我们继续来练习浙江大学的机试题目。加油!fighting!( •̀ ω •́ )✧ 21🍩1696 Ambulance Dispatch 给定一张城市地图,上面有所有的救护车调度中心(救护车派遣中心) 并标记所有拾取点。你应该写一个程序来处理紧急呼叫。假设来电者正在某个…

得物App荣获“科技创新服务示范案例”,推动品质消费新升级

备受瞩目的2024年中国国际服务贸易交易会在北京盛大开幕&#xff0c;这一由商务部和北京市政府联合举办、并获得世贸组织、联合国等国际组织支持的国家级、国际性、综合型服务贸易盛会&#xff0c;再次吸引了全球的目光。作为上海科技企业的优秀代表&#xff0c;得物App亮相此次…