三、高级定时器——PWM互补输出
【实现功能】通过使用高级定时器 TIM1 的输出通道 CH1,输出一对互补信号 PWM。要求占空比为 50%。
【基本思路】查看原理图,在 STM32F103ZE 中,高级定时器 TIM1 的输出通道 1 对应的引脚是 PA8,互补输出通道 1 对应的引脚是 PB13,刹车通道对应的引脚是 PB12。只需初始化这三个引脚即可。
还是跟以前一样,我们设置分频因子为 72,则得到了一次计数周期为 1us,再设置重装载值为 1000,则计数器数到 1000 会重新清零,此时过去了 1000 * 1us = 1000us = 1ms。于是我们得到了 1ms 为周期的方波信号,不过此时还未设置占空比。
现在我们已经设置重装载值为 1000(对应的寄存器是ARR),为了使占空比为 50%,则我们要将脉冲宽度设置为 500(对应的寄存器是CCR1),因为 500/1000 = 50%。如果我们需要将占空比调整至 30%,则寄存器 CCR1 的值应该设置为 300。
在配置互补输出信号的时候,将两者电平极性设置为高电平,这样我们之前设置的占空比才是对应高电平的;如果设置的是低电平的话,那么占空比对应的就是低电平了。
解释一下空闲电平:如果中途关闭了输出使能,那么此时输出通道输出的就是空闲电平。空闲电平极性为高,则一直输出高电平;空闲电平极性为低,则一直输出低电平。
最后提一句,用通用定时器 一样也能实现PWM互补输出。
【编程要点】
输出通道、互补输出通道、刹车通道 GPIO 引脚初始化;
TIM1 时基单元初始化配置;
TIM1 输出比较通道、互补输出比较通道、刹车通道初始化配置;
main 函数调用 TIM 的初始化函数;
通过 keil5 仿真逻辑仪查看实验结果。
完整代码如下:
1. advance_tim.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #ifndef __ADVANCE_TIM_H #define __ADVANCE_TIM_H #include "stm32f10x.h" #define ADVANCE_TIM TIM1 #define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1 #define ADVANCE_TIM_PERIOD (1000 - 1) #define ADVANCE_TIM_PSC (72 - 1) #define ADVANCE_TIM_CCR1 500 #define ADVANCE_TIM_RECNT 0 #define ADVANCE_TIM_CH1_PORT_CLK RCC_APB2Periph_GPIOA #define ADVANCE_TIM_CH1_PORT GPIOA #define ADVANCE_TIM_CH1_PIN GPIO_Pin_8 #define ADVANCE_TIM_CH1N_PORT_CLK RCC_APB2Periph_GPIOB #define ADVANCE_TIM_CH1N_PORT GPIOB #define ADVANCE_TIM_CH1N_PIN GPIO_Pin_13 #define ADVANCE_TIM_BKIN_PORT_CLK RCC_APB2Periph_GPIOB #define ADVANCE_TIM_BKIN_PORT GPIOB #define ADVANCE_TIM_BKIN_PIN GPIO_Pin_12 #define ADVANCE_TIM_DEAD_TIME 11 void ADVANCE_TIM_Init (void ) ;#endif
2. advance_tim.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include "advance_tim.h" static void ADVANCE_TIM_GPIO_Config (void ) { GPIO_InitTypeDef GPIO_InitStrutcure; RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_PORT_CLK, ENABLE); GPIO_InitStrutcure.GPIO_Pin = ADVANCE_TIM_CH1_PIN; GPIO_InitStrutcure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStrutcure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(ADVANCE_TIM_CH1_PORT ,&GPIO_InitStrutcure); RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1N_PORT_CLK, ENABLE); GPIO_InitStrutcure.GPIO_Pin = ADVANCE_TIM_CH1N_PIN; GPIO_InitStrutcure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStrutcure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(ADVANCE_TIM_CH1N_PORT ,&GPIO_InitStrutcure); RCC_APB2PeriphClockCmd(ADVANCE_TIM_BKIN_PORT_CLK, ENABLE); GPIO_InitStrutcure.GPIO_Pin = ADVANCE_TIM_BKIN_PIN; GPIO_InitStrutcure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStrutcure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(ADVANCE_TIM_BKIN_PORT ,&GPIO_InitStrutcure); GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT, ADVANCE_TIM_BKIN_PIN); } static void ADVANCE_TIM_Mode_Config (void ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; RCC_APB2PeriphClockCmd(ADVANCE_TIM_CLK, ENABLE); TIM_TimeBaseStructure.TIM_Prescaler = ADVANCE_TIM_PSC; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = ADVANCE_TIM_PERIOD; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_RepetitionCounter = ADVANCE_TIM_RECNT; TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = ADVANCE_TIM_CCR1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC1PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable); TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1; TIM_BDTRInitStructure.TIM_DeadTime = ADVANCE_TIM_DEAD_TIME; TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable; TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; TIM_BDTRConfig(ADVANCE_TIM, &TIM_BDTRInitStructure); TIM_Cmd(ADVANCE_TIM, ENABLE); TIM_CtrlPWMOutputs(ADVANCE_TIM, ENABLE); } void ADVANCE_TIM_Init (void ) { ADVANCE_TIM_GPIO_Config(); ADVANCE_TIM_Mode_Config(); }
3. main.c
1 2 3 4 5 6 7 8 9 10 11 12 #include "stm32f10x.h" #include "advance_tim.h" int main (void ) { ADVANCE_TIM_Init(); while (1 ) { } }
4. Keil5 逻辑分析仪仿真
因为手头没有示波器,因此用软件自带逻辑分析仪进行仿真。这里只是大概说一下怎么设置,详细步骤请查看其它相关博文。
首先在魔法棒(options for target)的 debug 选项卡按照下图设置好(DARMSTM.DLL)(-pSTM32F103ZE):
如下图所示,菜单栏点击 Debug/Start Debug Session,然后点击图中1处的示波器,选择 Logic Analyzer。菜单栏点击 View/Symbols Window,然后右边会出现窗口,点击其中的 Virtual Registers,便可以查看哪些寄存器可以加入观察对象。
点击上图2处的setup,加入 PORTA 和 PORTB,并设置好显示类型、掩码和右移位数。比如我要查看 PA13,那么 Display Type 设置为 Bit,掩码为 0x100(二进制 0000 0001 0000 0000 = 十六进制 0x100),右移8位。如图所示:
点击 Run,等一会时间再点 Stop,即可查看仿真结果(基本符合预期):
注意,仿真结果并不代表实际结果也是这样。
附:呼吸灯的实现
现在我们来讲讲呼吸灯是如何实现的。如果不用定时器,用延时函数写呼吸灯的部分程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include "stm32f10x.h" #include "systick.h" #include "led.h" int main (void ) { uint16_t i = 0 ; uint16_t num = 1500 ; LED_Init(); while (1 ) { for (i = 0 ; i < num; i++) { LED0_ON; Systick_Delay_us(i); LED0_OFF; Systick_Delay_us(num - i); } for (i = num; i > 0 ; i--) { LED0_ON; Systick_Delay_us(i); LED0_OFF; Systick_Delay_us(num - i); } } }
由以上程序可知呼吸灯的实现原理。其实现原理非常简单,就是利用人眼的延缓视觉效果,逐渐加快 LED 亮灭的频率,然后逐渐减慢 LED 亮灭的频率,其实质是一个高电平占空比不断变少、而后不断变多的方波信号,即可调制信号 PWM ,这样就实现呼吸灯的效果了,如下图所示(借用了他人图片,若有侵权请联系本人进行删除):
类似的,如果使用定时器去实现,那么只需每次循环都修改占空比即可,这样就达到了不断修改占空比的功能,进而实现呼吸灯的目的。修改后的 main 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include "stm32f10x.h" #include "systick.h" #include "advance_tim.h" int main (void ) { uint16_t i; ADVANCE_TIM_Init(); while (1 ) { for (i = 0 ; i < 1000 ; i++) { TIM_SetCompare1(TIM1, i); Systick_Delay_us(1500 ); } for (i = 999 ; i > 0 ; i--) { TIM_SetCompare1(TIM1, i); Systick_Delay_us(1500 ); } Systick_Delay_ms(500 ); } }
需要注意的是,STM32的高级定时器 TIM1 的输出引脚与 LED 引脚并不连在一起,那我们怎么去实现呼吸灯呢?这里提供两个办法:
方法1:使用杜邦线和面包板外接 LED 灯
需要自行准备LED灯(注意区分正负极),自己连接电路,记得连上拉电阻,大概 100 - 200 欧姆即可。如果忘记连电阻,LED 会烧掉,几分钱也没了,不仅如此,还会伴随一股焦味,如果手没及时拿开还可能会肉疼一会儿。
这里只实现了一个呼吸灯。本来打算写个四路输出PWM(实现起来很简单,就是输出通道初始化4次即可,下面第四部分将展示4路输出是如何编写的),实现4个流水呼吸灯的小玩意,但是我的面包板似乎总是间歇性接触不良,导致进一步的调试极为麻烦,而且板载 LED 只有两个,因此不得不放弃。
方法2:使用杜邦线将输出引脚和 LED 引脚连起来,即飞线
这种办法比较便捷:一般的系统版上都会有排针,可以用两端都是母头的杜邦线将输出引脚(PA8)和 LED0(PB5)连起来,将互补输出引脚(PB13)和 LED1(PE5)连起来,你会看到两盏呼吸灯相互亮灭,这与仿真结果符合。
四、高级定时器——PWM输入捕获
这部分内容挺重要的,因为是最后压轴出场!!!
【实现功能】使用通用定时器输出4路不同占空比的PWM信号,然后使用高级定时器的输入捕获功能测量PWM输出信号的周期和占空比,并将结果通过USART输出到上位机上。
【基本思路】通用定时器的4路输出就不用讲了,思路与呼吸灯是一样的。下面我们来详细分析输入捕获是如何工作的,如图所示:
我们使用高级定时器 TIM1 的输入捕获功能来测量信号的周期和占空比,使用的是 TI1 通道,通过输入滤波器和边沿检测器 后产生2路滤波信号 TI1FP1 和 TI1FP2,其中 TI1FP1 映射到了捕获通道 IC1,捕获到的值送入寄存器 CCR1;TI1FP2 映射到了捕获通道 IC2,捕获到的值送入到寄存器 CCR2。因此实际上 TIM1 使用了两路通道 CH1 和 CH2 。
我们需要理解信号 TI1FP1 和 TI1FP2 的实质:二者均来自于同一个通道 TI1,然后映射到不同的捕获通道,其实还是同一路信号,但是由于经过了边沿检测器,这两路信号的极性相反。当我们将 TI1FP1 配置为上升沿触发的时候,TI1FP2 就是下降沿触发;反之亦然。
既然有两路信号 TI1FP1 和 TI1FP2,那么用哪个通道判断 PWM 上升沿的到来?STM32提供的办法是:可以将其中一路信号设置为触发信号,告诉定时器,如果这路信号检测到上升沿,那么就可以开始捕获工作了。我们选择 TI1FP1 作为触发信号。
那么另外一路信号 TI1FP2 怎么办呢?STM32提供的办法是:将另一路信号 TI1FP2 设置为从机模式 ,这样另一路信号就会在主信号 TI1FP1 触发时也会被触发。这时的 TI1FP1 称之为从模式触发源。 从模式在 STM32 一共有四种:复位模式、门控模式、触发模式、外部时钟模式2 + 其他三种触发模式(说白了就是可以与其他模式搭配使用)。我们将 TI1FP2 配置为从模式的复位模式。现在我们只需要记住复位模式 的功能:在发生一个触发输入事件时,计数器和它的预分频器能够重新被初始化。
读到这里的同学可能不明白上面这一通操作是干嘛的,我们举个例子就明白了:PWM 上升沿到来,TI1FP1 检测到上升沿(被触发了),由于 TI1FP2 是从机,因此也会被触发;由于是复位模式,计数器的值清零,此时不会产生捕获中断。下降沿到来时,TI1FP2 是下降沿触发,因此会 发生捕获事件,将此时计数器的值存到寄存器 CCR2。新一轮上升沿到来时,TI1FP1 是上升沿触发,因此发生捕获事件,将此时计数器的值存到寄存器 CCR1,此时产生捕获中断,同时计数器清零。因此我们测得的周期是 72MHz / CCR1,测得的占空比是 CCR2 / CCR1。但实际上,计数器是从0开始计数的,所以正确的结果是:周期 = 72MHz / (CCR1+1),占空比 = (CCR2+1) / (CCR1+1)。 整个过程如下图所示:
看到这里大家也许明白为什么要弄个从机模式了。因为测量占空比和测量周期的工作必须要同时开始进行,所以需要一个主通道和一个从通道,主通道说开始要做事了,从通道也会跟着同时做事!
到这里其实已经讲解的很清楚了,不过需要注意三点:
(1)因为只有 TI1FP1 和 TI2FP2 连到了从模式控制器,所以PWM输入模式只能使用 TIMx_CH1 和 TIMx_CH2 信号 ,用 CH3 和 CH4 是不行的哦,可以对照功能框图进行查看。
(2)注意直连(TIM_ICSelection_DirectTI)和非直连(TIM_ICSelection_IndirectTI)的区别:直连指的是输入通道 IC1 连接 CH1,输入通道 IC2 连接到 CH2;非直连指的是输入通道 IC1 连接 CH2,输入通道 IC2 连接到 CH1。对于我们这个实验来说,选择直连,IC1 测得就是周期,IC2 测的是占空比;选择非直连,IC1 测得就是占空比,IC2 测的是周期。
(3)不用配置 IC2(或者说TI1FP2) ,因为你给一个通道配置了时钟和极性后,另一个通道会通过硬件给你配置好。如果你多配置了 IC2,反而测量结果会不正确。
最后我们附上STM32中文参考手册 里的 PWM 输入模式的配置步骤:
两个ICx信号被映射至同一个TIx输入。
这2个ICx信号为边沿有效,但是极性相反。
其中一个TIxFP信号被作为触发输入信号,而从模式控制器被配置成复位模式。
例如,你需要测量输入到TI1上的PWM信号的长度(TIMx_CCR1寄存器)和占空比(TIMx_CCR2
寄存器),具体步骤如下(取决于CK_INT的频率和预分频器的值):
选择TIMx_CCR1的有效输入:置TIMx_CCMR1寄存器的CC1S=01(选中TI1)。
选择TI1FP1的有效极性(用来捕获数据到TIMx_CCR1中和清除计数器):置CC1P=0(上升沿
有效)。
选择TIMx_CCR2的有效输入:置TIMx_CCMR1寄存器的CC2S=10(选中TI1)。
选择TI1FP2的有效极性(捕获数据到TIMx_CCR2):置CC2P=1(下降沿有效)。
选择有效的触发输入信号:置TIMx_SMCR寄存器中的TS=101(选择TI1FP1)。
配置从模式控制器为复位模式:置TIMx_SMCR中的SMS=100。
使能捕获:置TIMx_CCER寄存器中CC1E=1且CC2E=1。
【编程要点】
通用定时器时基单元初始化;
通用定时器4个GPIO引脚初始化;
通用定时器4路PWM输出CH1-CH4初始化;
高级定时器时基单元初始化;
高级定时器GPIO引脚初始化;
高级定时器输入捕获IC1初始化(不用初始化IC2);
开启高级定时器捕获中断,并配置NVIC;
编写高级定时器中断服务函数,并计算测量结果,然后输出。
完整代码如下:
1. general_tim.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #ifndef __GENERAL_TIM_H #define __GENERAL_TIM_H #include "stm32f10x.h" #define GENERAL_TIM TIM3 #define GENERAL_TIM_CLK RCC_APB1Periph_TIM3 #define GENERAL_TIM_PERIOD (100 - 1) #define GENERAL_TIM_PSC (72 - 1) #define GENERAL_TIM_RECNT 0 #define GENERAL_TIM_CH1_PORT_CLK RCC_APB2Periph_GPIOA #define GENERAL_TIM_CH1_PORT GPIOA #define GENERAL_TIM_CH1_PIN GPIO_Pin_6 #define GENERAL_TIM_CCR1 10 #define GENERAL_TIM_CH2_PORT_CLK RCC_APB2Periph_GPIOA #define GENERAL_TIM_CH2_PORT GPIOA #define GENERAL_TIM_CH2_PIN GPIO_Pin_7 #define GENERAL_TIM_CCR2 25 #define GENERAL_TIM_CH3_PORT_CLK RCC_APB2Periph_GPIOB #define GENERAL_TIM_CH3_PORT GPIOB #define GENERAL_TIM_CH3_PIN GPIO_Pin_0 #define GENERAL_TIM_CCR3 50 #define GENERAL_TIM_CH4_PORT_CLK RCC_APB2Periph_GPIOB #define GENERAL_TIM_CH4_PORT GPIOB #define GENERAL_TIM_CH4_PIN GPIO_Pin_1 #define GENERAL_TIM_CCR4 75 void GENERAL_TIM_Init (void ) ;#endif
2. general_tim.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 #include "general_tim.h" static void GENERAL_TIM_GPIO_Config (void ) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_PORT_CLK, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN; GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_PORT_CLK, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH2_PIN; GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(GENERAL_TIM_CH3_PORT_CLK, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH3_PIN; GPIO_Init(GENERAL_TIM_CH3_PORT, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(GENERAL_TIM_CH4_PORT_CLK, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH4_PIN; GPIO_Init(GENERAL_TIM_CH4_PORT, &GPIO_InitStructure); } static void GENERAL_TIM_MODE_Config (void ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(GENERAL_TIM_CLK, ENABLE); TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = GENERAL_TIM_PERIOD; TIM_TimeBaseStructure.TIM_Prescaler = GENERAL_TIM_PSC; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0 ; TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR1; TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure); TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR2; TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure); TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR3; TIM_OC3Init(GENERAL_TIM, &TIM_OCInitStructure); TIM_OC3PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_CCR4; TIM_OC4Init(GENERAL_TIM, &TIM_OCInitStructure); TIM_OC4PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); TIM_Cmd(GENERAL_TIM, ENABLE); } void GENERAL_TIM_Init (void ) { GENERAL_TIM_GPIO_Config(); GENERAL_TIM_MODE_Config(); }
3. advance_tim.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #ifndef __ADVANCE_TIM_H #define __ADVANCE_TIM_H #include "stm32f10x.h" #define ADVANCE_TIM TIM1 #define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1 #define ADVANCE_TIM_PERIOD (1000 - 1) #define ADVANCE_TIM_PSC (72 - 1) #define ADVANCE_TIM_RECNT 0 #define ADVANCE_TIM_CH1_PORT_CLK RCC_APB2Periph_GPIOA #define ADVANCE_TIM_CH1_PORT GPIOA #define ADVANCE_TIM_CH1_PIN GPIO_Pin_8 #define ADVANCE_TIM_CH1N_PORT_CLK RCC_APB2Periph_GPIOB #define ADVANCE_TIM_CH1N_PORT GPIOB #define ADVANCE_TIM_CH1N_PIN GPIO_Pin_13 #define ADVANCE_TIM_BKIN_PORT_CLK RCC_APB2Periph_GPIOB #define ADVANCE_TIM_BKIN_PORT GPIOB #define ADVANCE_TIM_BKIN_PIN GPIO_Pin_12 #define ADVANCE_TIM_DEAD_TIME 11 #define ADVANCE_TIM_IRQ TIM1_CC_IRQn #define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler void ADVANCE_TIM_Init (void ) ;#endif
4. advance_tim.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include "advance_tim.h" static void ADVANCE_TIM_GPIO_Config (void ) { GPIO_InitTypeDef GPIO_InitStrutcure; RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_PORT_CLK, ENABLE); GPIO_InitStrutcure.GPIO_Pin = ADVANCE_TIM_CH1_PIN; GPIO_InitStrutcure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStrutcure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(ADVANCE_TIM_CH1_PORT ,&GPIO_InitStrutcure); } static void ADVANCE_TIM_NVIC_Config (void ) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = ADVANCE_TIM_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0 ; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3 ; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } static void ADVANCE_TIM_Mode_Config (void ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; RCC_APB2PeriphClockCmd(ADVANCE_TIM_CLK, ENABLE); TIM_TimeBaseStructure.TIM_Prescaler = ADVANCE_TIM_PSC; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = ADVANCE_TIM_PERIOD; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_RepetitionCounter = ADVANCE_TIM_RECNT; TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure); TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0 ; TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure); TIM_SelectInputTrigger(ADVANCE_TIM, TIM_TS_TI1FP1); TIM_SelectMasterSlaveMode(ADVANCE_TIM, TIM_MasterSlaveMode_Enable); TIM_SelectSlaveMode(ADVANCE_TIM, TIM_SlaveMode_Reset); TIM_ITConfig(ADVANCE_TIM, TIM_IT_CC1, ENABLE); TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1); TIM_Cmd(ADVANCE_TIM, ENABLE); } void ADVANCE_TIM_Init (void ) { ADVANCE_TIM_GPIO_Config(); ADVANCE_TIM_NVIC_Config(); ADVANCE_TIM_Mode_Config(); }
5. stm32f10x_it.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include "stm32f10x_it.h" #include "advance_tim.h" #include "usart.h" void ADVANCE_TIM_IRQHandler (void ) { volatile uint16_t IC1Value = 0 ; volatile uint16_t IC2Value = 0 ; volatile float DutyCycle = 0 ; volatile float Frequency = 0 ; TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1); IC1Value = TIM_GetCapture1(ADVANCE_TIM); IC2Value = TIM_GetCapture2(ADVANCE_TIM); if (IC1Value != 0 ) { DutyCycle = ( (float )(IC2Value+1 ) ) / (IC1Value+1 ); Frequency = (float )( (72000000 /(ADVANCE_TIM_PSC+1 )) / (IC1Value+1 ) ); printf ("占空比:%0.2f%% 周期:%0.2fHz\r\n" , DutyCycle*100 , Frequency); } }
6. main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include "stm32f10x.h" #include "advance_tim.h" #include "general_tim.h" #include "usart.h" int main (void ) { USART_Config(); printf ("\r\n开始测试!\r\n" ); GENERAL_TIM_Init(); ADVANCE_TIM_Init(); while (1 ) { } }
使用杜邦线可将输出通道和输入通道连接起来,就能在串口调试助手中看到正确结果。