[STM32 - 野火] - - - 固件库学习笔记 - - -十三.高级定时器
一、高级定时器简介
高级定时器的简介在前面一章已经介绍过,可以点击下面链接了解,在这里进行一些补充。
[STM32 - 野火] - - - 固件库学习笔记 - - -十二.基本定时器
1.1 功能简介
-
1、高级定时器可以向上/向下/两边计数,还独有一个重复计数器RCR。
两边计数:例如计数器从0累加计数到ARR的值,再从ARR的值递减至0,再累加到ARR,循环这个过程。
-
2、有4个GPIO,其中通道1~3还有互补输出GPIO。
-
3、时钟来自PCLK2,为72M,可实现65536分频。
基本定时器、通用定时器的时钟来自PCK1。
1.2 GPIO说明
可在芯片对应的参考数据手册->引脚说明章节进行查询
二、高级定时器功能框图
2.1 时钟源
高级定时器有4个时钟源可选:
-
1、内部时钟CK_INT;
-
2、外部时钟模式1:外部输入引脚TIx;
-
3、外部时钟模式2:外部触发输入ETR;
-
4、内部触发输入:ITRx;
2.1.1 内部时钟CK_INT
-
内部时钟源来自RCC的TIMx_CLK。
-
内部时钟源TIMx_CLK的时钟为72M。
一般情况下,都是使用内部时钟。
2.1.2 外部时钟模式1
-
1、时钟信号输入引脚:当使用外部时钟模式 1 的时候,时钟信号来自于定时器的输入通道,总共有 4 个,分别为TIMx_CH1/2/3/4。
根据TIM_CCMRx寄存器的位 CCxS[1:0] 配置具体使用哪一路信号。
-
2、滤波器:可以使用滤波器对信号进行重新采样,以达到降频或去除高频干扰的目的。
由TIMx_CCMRx寄存器的位 ICxF[3:0]配置。
-
3、边沿检测器:决定是上升沿有效还是下降沿有效。
由 TIMx_CCER寄存器的位 CCxP 和 CCxNP 配置。
-
4、触发选择:可以选择是滤波后的定时器输入1(TI1FP1)当触发源还是滤波后的定时器输入2(TI2FP2)当触发源。
由 TIMxSMCR寄存器的位 TS[2:0] 配置。
-
5、从模式选择:选定了触发源信号后,把信号连接到 TRGI 引脚,让触发信号成为外部时钟模式 1 的输入,最终成为CK_PSC。
由 TIMx_SMCR寄存器 的位 SMS[2:0]配置。
总结一下,外部时钟模式1中时钟信号->CK_PSC的过程:
-
选择时钟信号输入引脚TIMx_CH1/2/3/4;
-
通过滤波器将高频信号降为低频信号,也可以不滤波;
-
经过边沿检测器选择上升有效还是下降有效;最终产生两路触发信号(TI1FP1、TI2FP2);
-
通过寄存器TIMx_SMCR的TS[2:0]选择哪一路连接到TRGI成为触发信号;由TIMx_SMCR的SMS[2:0]位来选择外部时钟模式1;
-
连接到CK_PSC,最终驱动预分频器经过分频后成为计数器的时钟。
2.1.3 外部时钟模式2
-
1、时钟信号输入引脚:当使用外部时钟模式 2 的时候,时钟信号来自于定时器的特定输入通道 TIMx_ETR,只有 1 个。
-
2、外部触发极性:来自 ETR 引脚输入的信号可以选择为上升沿或者下降沿有效。
由 TIMx_SMCR寄存器的位 ETP 配置。
-
3、外部触发预分频器:由于 ETRP 的信号的频率不能超过 TIMx_CLK(72M)的 1/4,当触发信号的频率很高的情况下,就必须使用分频器来降频。
- ETRP:经过外部触发预分频器处理后的信号。
由 TIMx_SMCR寄存器 的位 ETPS[1:0] 配置。
-
4、滤波器:可以使用滤波器对信号进行重新采样(通过另外一个时钟很大的信号对ETRP进行采样),以达到降频或去除高频干扰的目的。
- fDTS时钟频率由TIMx_CR1寄存器的CKD[1:0]控制,与fCK_INT相关。
由 TIMx_SMCR寄存器 的位 ETF[3:0] 配置。
-
5、从模式选择:经过滤波器后的时钟ETRF,连接到 ETRF 引脚,让ETRF信号成为外部时钟模式 2 的输入,最终成为CK_PSC。
由TIMx_SMCR寄存器 的位 ECE 配置。
总结一下,外部时钟模式2中时钟信号->CK_PSC的过程:
-
通过ETR引脚输入时钟信号;
-
选择为上升沿有效还是下降沿有效;
-
通过分频器分频,经过分频器后的信号为ETRP;
-
通过滤波器对信号进行重新采样,经过滤波器后的信号为ETRF;
-
将ETRF信号连接到ETRF引脚,由 TIMx_SMCR寄存器 的位 ETF[3:0] 来选择外部时钟模式2;
-
连接到CK_PSC,最终驱动预分频器经过分频后成为计数器的时钟。
2.1.4 内部触发输入
内部触发输入是使用一个定时器作为另一个定时器的预分频器。
硬件上高级控制定时器和通用定时器在内部连接在一起,可以实现定时器同步或级联。
高级定时器的时钟可以给通用定时器提供时钟。
2.2 控制器
-
控制器用于控制定时器的复位、使能、计数、出发DAC等功能。
-
主要使用CR1、CR2、SMCR、CCER这几个寄存器。
2.3 时基
-
1/、CK_PSC 分频后得 CK_CNT(计数器时钟) 驱动计数器计数,计数最大值存储在ARR(自动重装载寄存器)中;
-
2.1、如果没有使用重复计数器,计数器计数到ARR的值时,会产生一个更新中断,计数值会被清零;
-
2.2、如果使用重复计数器,计数器计数到ARR的值时,计数器清零,重复计数器值+1,当重复计数器的值计数到REP寄存器存放的值时再产生中断。
2.4 输入捕获(IC:Input Compare)
-
原理:当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR寄存器中,把前后两次捕获到的CCR寄存器中的值相减,就可以算出脉宽或者频率。
-
作用:用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数。
边沿信号输入引脚,一旦有边沿,例如有一个上升沿,输入滤波器和边沿检测器就会检测到这个上升沿,让输入捕获电路产生动作(检测电平跳变,执行动作,控制后续电路,让当前CNT的值锁存到CCR寄存器中)。
2.4.1 输入通道
需要被测量的信号从定时器的外部引脚 TIMx_CH1/2/3/4 进入,通常叫TI1/2/3/4。
2.4.2 输入滤波和边沿检测
-
输入滤波器:当输入的信号存在高频干扰的时候,我们需要对输入信号进行滤波,即进行重新采样。
- 采样的频率必须大于等于两倍的输入信号。
-
边沿检测器:设置信号在捕获的时候是什么边沿有效,可以是上升沿,下降沿,或者是双边沿。
2.4.3 捕获通道
捕获通道就是图中的 IC1/2/3/4,每个捕获通道都有相对应的捕获寄存器 CCR1/2/3/4,当发生捕获的时候,计数器CNT的值就会被锁存到捕获寄存器中。
-
输入通道与捕获通道的区别:
-
输入通道:用来输入信号;
-
捕获通道:用来捕获输入信号的通道;
-
一个输入通道的信号可以同时输入给两个捕获通道。
比如输入通道TI1的信号经过滤波器、边沿检测器后的TI1FP1和TI1FP2可以同时进入到捕获通道IC1与IC2。
-
2.4.4 预分频器
ICx 的输出信号会经过一个预分频器,用于决定发生多少个事件时进行一次捕获。
2.4.5 捕获寄存器
经过预分频器的信号 ICxPS 是最终被捕获的信号,当发生捕获时(第一次),计数器 CNT 的值会被锁存到捕获寄存器 CCR 中,还会产生 CCxI 中断相应的中断位 CCxIF(在 SR 寄存器中)会被置位,通过软件或者读取 CCR 中的值可以将 CCxIF 清 0。
注意:如果发生第二次捕获(即CCR寄存器中已捕获计数器值且CCxIF标志位已置1),那么捕获溢出标志位CCxOF会被置位,且CCxOF只能通过软件清零。换句话说:当CCxOF被置位时,说明有没被读取的捕获值。
2.4.6 PWMI模式
-
介绍:PWMI(Pulse Width Modulation Input Mode)模式:也就是PWM的输入模式,是专门为测量PWM频率和占空比设计的,通过硬件自动捕获信号的上升沿和下降沿,极大简化了测量逻辑。
-
原理:利用定时器的两个输入通道(只能为通道1和通道2),对同一个引脚减小捕获,可以测量频率与占空比。
-
频率测量:通过捕获两个上升沿之间的时间差,计算信号周期。
-
占空比测量:通过捕获上升沿到下降沿的时间差(高电平时间),结合周期计算占空比。
-
为什么使用PWMI模式的时候只能使用通道TIMx_CH1、TIMx_CH2?
触发信号连接到控制器里只能是TI1FP1或TI2FP2,这两个触发信号只能由TIMx_CH1、TIMx_CH2产生,因此只能是TIMx_CH1、TIMx_CH2。
-
TIMx_CH1与TIMx_CH2交叉的作用与目的:
-
一个通道灵活切换两个引脚;
-
两个通道同时捕获一个引脚。
-
2.5 输出比较(OC:Output Compare)
-
原理:通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作。
-
作用:用于输出一定频率和占空比的PWM波形。
2.5.1 输出比较寄存器
当计数器 CNT 的值跟比较寄存器 CCR 的值相等的时候,输出参考信号 OCxREF 的信号的极性就会发送改变,并且会产生比较中断 CCxI,相应的标志位 CCxIF(SR 寄存器中)会置位。
OCxREF 再经过一系列的控制之后就成为真正的输出信号 OCx/OCxN。
2.5.2 死区发生器
这是一个半桥驱动电路。
在这个半桥驱动电路中,Q1管导通,Q2管截止。现在要让Q1管截止,Q2管导通。如果在Q1管关闭之后立马打开Q2管,由于MOS管的存在,会在一段时间内相当于Q1管和Q2管都导通了,造成短路。
而死区生成电路能避免上述情况的发生:在上管关闭的时候,延迟一小段时间,再导通下管;下管关闭的时候,延迟一小段时间,再导通上管。
2.5.3 输出控制
在输出比较的输出控制中,参考信号 OCxREF 在经过死区发生器之后会产生两路带死区的互补信号 OCx_DT 和 OCxN_DT,这两路带死区的互补信号就进入输出控制电路。
通道 1~3 才有互补信号,通道 4 没有,其余跟通道 1~3 一样。
- 输出模式控制器的输入是CNT与CCR的大小关系,输出是REF的高低电平。
进入输出控制电路的信号会被分成两路,一路是原始信号OCx,一路是被反向的信号OCxN,再根据寄存器配置选择是否由OCx/OCxN引脚输出到外部引脚CHx/CHxN。
如果加入了断路(刹车)功能,则断路和死区寄存器 BDTR 的 MOE、 OSSI 和 OSSR 这三个位会共同影响输出的信号。
通用定时器的输出比较通道,与高级定时器的相比少了死区发生器部分。
2.5.4 输出引脚
输出比较的输出信号最终是通过定时器的外部 IO 来输出的,分别为 CH1/2/3/4,其中前面三个通道还有互补的输出通道 CH1/2/3N。
2.5.5 输出比较模式
模式 | 描述 |
---|---|
冻结 | CNT=CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CCR时,REF置有效电平 |
匹配时置无效电平 | CNT=CCR时,REF置无效电平 |
匹配时电平翻转 | CNT=CCR时,REF电平翻转 |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
PWM模式1 | 向上计数:CNT<CCR时,REF置有效电平;CNT≥CCR时,REF置无效电平 向下计数:CNT>CCR时,REF置无效电平;CNT≤CCR时,REF置有效电平 |
PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平;CNT≥CCR时,REF置有效电平 向下计数:CNT>CCR时,REF置有效电平;CNT≤CCR时,REF置无效电平 |
-
冻结模式:保持REF不变,维持上一个状态。
例如:你正在输出PWM波,想暂停一会输出,可以切换为冻结模式,输出暂停,高低电平维持在暂停时的状。
-
匹配时置有效电平/匹配时置无效电平:有效电平可以理解为高电平,无效电平是低电平,与关断、刹车这些功能配合表述的。
匹配时置有效电平/匹配时置无效电平是一次性的,不适合输出连续变化的波形。
-
匹配时电平翻转:可以输出占空比为50%的PWM波形。
-
强制为无效电平/有效电平:你想暂停输出波形,并且在暂停期间保持高电平或低电平,可以使用这个模式。
-
PWM模式1/PWM模式2:可用于输出频率和占空比都可调的PWM波形。
PWM模式2实际上就是PWM模式1输出的取反。
REF输出之后还有一个极性配置,使用PWM模式1的正极性与PWM模式2的反极性最终的输出是一样的。
2.6 断路(刹车)功能
断路功能就是电机控制的刹车功能,使能断路功能时,根据相关控制位状态修改输出信号电平。
在任何情况下, OCx 和 OCxN 输出都不能同时为有效电平,这关系到电机控制常用的 H 桥电路结构原因。
断路源可以是时钟故障事件,由内部复位时钟控制器中的时钟安全系统 (CSS) 生成,也可以是外部断路输入 IO,两者是或运算关系。
这个异或门是为三相无刷电机服务的:无刷电机有3个霍尔传感器检测转子的位置,根据转子的位置进行换相。
当三个输入引脚的任何一个有电平翻转时,输出引脚就产生一次电平翻转。
经过异或门的输出信号通过数据选择器,再进入输入捕获通道1。
-
数据选择器如果选择上面,输入捕获通道1的输入就是3个引脚的异或值;
-
数据选择器如果选择下面,那么异或门就没有用,4个通道各用各的引脚。
2.7 输入捕获与输出比较
对于同一个定时器,输入捕获和输出比较只能使用其中一个,不能同时使用(输入捕获与输出比较共用CCR寄存器,通道引脚也共用)。
-
输出比较,引脚是输出端口;根据CNT和CCR的大小关系来执行输出动作。
-
输入捕获,引脚是输入端口;接收到输入信号,执行CCR锁存到CCR动作。
三、PWM
3.1 PWM简介
PWM:脉冲宽度调制。
-
作用:在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获取所需要的模拟参量,常应用于电机控速等领域。
PWM的频率越快,它等效的模拟信号就越平稳,同时性能开销就越大。
-
参数:
- 频率:Freq = 1/Ts
PWM的频率越快,它等效的模拟信号就越平稳,同时性能开销就越大。
- 占空比:Duty = TON/TS
占空比决定了PWM等效出来的模拟电压大小:占空比越大,等效的模拟电压越趋近于高电平;占空比越小,等效的模拟电压越趋近于低电平,是线性的关系。- 分辨率:占空比变化步距
比如占空比只能是1%、2%这样以1%的步距跳变,那么它的分辨率就是1%。
3.2 PWM波形的产生
以通用定时器来说明一下PWM波形是如何产生的。
图自江协科技。
配置完时基单元后,CNT开始不断自增运行。
CCR捕获/比较寄存器中的值是我们自己设置的,不断比较CCR的值与CNT的值通过输出模式控制器输出REF信号。
上图中黄线表示ARR,红线表示CCR,蓝线表示CNT;
下图为产生的PWM波形。
-
当CNT<CCR时REF置有效电平,GPIO输出高电平;
-
当CNT≥CCR时REF置五效电平,GPIO输出低电平;
-
当CNT溢出清零后,又输出高电平;
3.3 PWM波形参数计算
-
PWM频率:Freq = CK_PSC/(PSC+1)/(ARR+1)
-
PWM占空比:Duty = CCR/(ARR+1)
-
PWM分辨率:Reso = 1/(ARR+1)
四、实验
4.1 PWM互补输出实验
4.1.1 硬件设计
使用高级定时器 TIM1 的通道2及其互补通道作为本实验的波形输出通道,对应选择 PC7 和 PB0 引脚。
PB0引脚接到绿灯上,方便查看实验现象。
为增加断路功能,需要用到 TIM8_BKIN 引脚,这里选择 PA6引脚。程序我们设置该引脚为高电平有效,当 BKIN 引脚被置低电平的时候,两路互补的 PWM 输出就被停止,就好像是刹车一样。
4.1.2 软件设计
编程要点:
-
1、定时器用到的 GPIO 初始化
-
2、定时器时基结构体 TIM_TimeBaseInitTypeDef 初始化
-
3、定时器输出比较结构体 TIM_OCInitTypeDef 初始化
-
4、定时器刹车和死区结构体 TIM_BDTRInitTypeDef 初始化
// AdvancedTimer.c文件
#include "AdvancedTimer.h"static void ADVANCED_TIM_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 输出比较通道 GPIO 初始化RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);// 输出比较通道互补通道 GPIO 初始化RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1N_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1N_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(ADVANCE_TIM_CH1N_PORT, &GPIO_InitStructure);// 输出比较通道刹车通道 GPIO 初始化RCC_APB2PeriphClockCmd(ADVANCE_TIM_BKIN_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_BKIN_PIN;//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(ADVANCE_TIM_BKIN_PORT, &GPIO_InitStructure);// BKIN引脚默认先输出低电平GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);
}static void ADVANCED_TIM_Mode_Config(void)
{// 开启定时器时钟,即内部时钟CK_INT=72MADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);/*--------------------时基结构体初始化-------------------------*/TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD; // 驱动CNT计数器的时钟 = Fck_int/(psc+1)TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC; // 时钟分频因子 ,配置死区时间时需要用到TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 计数器计数模式,设置为向上计数TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重复计数器的值,没用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定时器TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);/*--------------------输出比较结构体初始化-------------------*/ TIM_OCInitTypeDef TIM_OCInitStructure;// 配置为PWM模式1TIM_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_PULSE;// 输出通道电平极性配置:配置为高电平有效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_OC2Init(ADVANCE_TIM, &TIM_OCInitStructure);TIM_OC2PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable); // 使能 OC1 预装载功能/*-------------------刹车和死区结构体初始化-------------------*/// 有关刹车和死区结构体的成员具体可参考BDTR寄存器的描述TIM_BDTRInitTypeDef TIM_BDTRInitStructure;TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;// 输出比较信号死区时间配置,具体如何计算可参考 BDTR:UTG[7:0]的描述// 这里配置的死区时间为152nsTIM_BDTRInitStructure.TIM_DeadTime = 11;TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;// 当BKIN引脚检测到高电平的时候,输出比较信号被禁止,就好像是刹车一样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 ADVANCED_TIMER_Init(void)
{ADVANCED_TIM_GPIO_Config();ADVANCED_TIM_Mode_Config();
}
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
当使用短断路(刹车)功能时,两路PWM信号会被强制禁止,静止之后输出上面两个参数设定的状态:Set为高电平,Reset为低电平。
如果不用断路(刹车)功能,可以不配置这两个参数。
- 死区时间计算:参数设置为11(二进制:0000 1011)。
// AdvancedTimer.h文件
#ifndef __ADVANCEDTIMER_H
#define __ADVANCEDTIMER_H#include "stm32f10x.h"/************高级定时器TIM参数定义,只限TIM1和TIM8************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 这里我们使用高级控制定时器TIM8#define ADVANCE_TIM TIM8
#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM8
// PWM 信号的频率 F = TIM_CLK/{(ARR+1)*(PSC+1)}
#define ADVANCE_TIM_PERIOD (8-1) // ARR的值
#define ADVANCE_TIM_PSC (9-1) // 分频因子
#define ADVANCE_TIM_PULSE 4 // 占空比 = ADVANCE_TIM_PULSE/(ADVANCE_TIM_PERIOD+1)#define ADVANCE_TIM_IRQ TIM1_UP_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_UP_IRQHandler// TIM1 输出比较通道
#define ADVANCE_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOC
#define ADVANCE_TIM_CH1_PORT GPIOC
#define ADVANCE_TIM_CH1_PIN GPIO_Pin_7// TIM1 输出比较通道的互补通道
#define ADVANCE_TIM_CH1N_GPIO_CLK RCC_APB2Periph_GPIOB
#define ADVANCE_TIM_CH1N_PORT GPIOB
#define ADVANCE_TIM_CH1N_PIN GPIO_Pin_0// TIM1 输出比较通道的刹车通道
#define ADVANCE_TIM_BKIN_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADVANCE_TIM_BKIN_PORT GPIOB
#define ADVANCE_TIM_BKIN_PIN GPIO_Pin_6/**************************函数声明********************************/void ADVANCED_TIMER_Init(void);#endif /* __ADVANCEDTIMER_H */
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_rccclkconfig.h"
#include "bsp_systick.h"
#include "AdvancedTimer.h"int main(void)
{LED_G_GPIO_Config();LED_B_GPIO_Config();LED_R_GPIO_Config();ADVANCED_TIMER_Init();GPIO_SetBits(LED_PROT, GPIO_Pin_All);while(1){}}
- PWM信号一般的频率为10~25K。
4.2 脉宽测量输入捕获实验
4.2.1 原理:
-
在上升沿捕获,进入中断服务函数中将CNT清零,并将捕获边沿改为下降沿;
-
在下降沿捕获,读取CCR寄存器中的值,并将捕获边沿改为上升沿;
4.2.2 难点
-
1、在中断服务函数中需要不断修改捕获的边沿:上升沿处理完改为下降沿;下降沿处理完改为上升沿。
-
2、在中断服务函数中记录产生了多少次更新自动。(定时器16位,只能记录65535个数,如果记一个数的时间是1us的话只能计65.536ms)
4.2.3 硬件设计
-
在开发板中 PA0 接的是一个按键,默认接 GND,当按键按下的时候 IO 口会被拉高,这个时候我们可以利用定时器的输入捕获功能来测量按键按下的这段高电平的时间。
-
根据上面表33-1可知:PA0对应的是定时器5的通道1。
4.2.4 软件设计
编程要点:
-
1、定时器用到的 GPIO 初始化
-
2、定时器时基结构体 TIM_TimeBaseInitTypeDef 初始化
-
3、定时器输入捕获结构体 TIM_ICInitTypeDef 初始化
-
4、编写中断服务函数,读取捕获值,计算出脉宽的时间
// GeneralTimer.c文件
#include "GeneralTimer.h"// 定时器输入捕获用户自定义变量结构体定义
TIM_ICUserValueTypeDef TIM_ICUserValueStructure = {0,0,0,0};// 中断优先级配置
static void GENERAL_TIM_NVIC_Config(void)
{NVIC_InitTypeDef NVIC_InitStructure; // 设置中断组为0NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 设置中断来源NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ ; // 设置主优先级为 0NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 设置抢占优先级为3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}static void GENERAL_TIM_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 输入捕获通道 GPIO 初始化RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
}static void GENERAL_TIM_Mode_Config(void)
{// 开启定时器时钟,即内部时钟CK_INT=72MGENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);/*--------------------时基结构体初始化-------------------------*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD; // 驱动CNT计数器的时钟 = Fck_int/(psc+1)TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_PSC; // 时钟分频因子 ,配置死区时间时需要用到TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 计数器计数模式,设置为向上计数TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重复计数器的值,没用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定时器TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);/*--------------------输入捕获结构体初始化-------------------*/ TIM_ICInitTypeDef TIM_ICInitStructure;// 配置输入捕获的通道,需要根据具体的GPIO来配置TIM_ICInitStructure.TIM_Channel = GENERAL_TIM_CHANNEL_x;// 输入捕获信号的极性配置TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;// 输入通道和捕获通道的映射关系,有直连和非直连两种TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;// 输入的需要被捕获的信号的分频系数TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;// 输入的需要被捕获的信号的滤波系数TIM_ICInitStructure.TIM_ICFilter = 0;// 定时器输入捕获初始化TIM_ICInit(GENERAL_TIM, &TIM_ICInitStructure);// 清除更新和捕获中断标志位TIM_ClearFlag(GENERAL_TIM, TIM_FLAG_Update|GENERAL_TIM_IT_CCx); // 开启更新和捕获中断 TIM_ITConfig (GENERAL_TIM, TIM_IT_Update | GENERAL_TIM_IT_CCx, ENABLE );// 使能计数器TIM_Cmd(GENERAL_TIM, ENABLE);
}void GENERAL_TIMER_Init(void)
{GENERAL_TIM_GPIO_Config();GENERAL_TIM_Mode_Config();GENERAL_TIM_NVIC_Config();
}
// GeneralTimer.h文件
#ifndef __GENERALTIMER_H
#define __GENERALTIMER_H#include "stm32f10x.h"/************通用定时器TIM参数定义,只限TIM2、3、4、5************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 我们这里默认使用TIM5#define GENERAL_TIM TIM5
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM5
#define GENERAL_TIM_PERIOD 0XFFFF
#define GENERAL_TIM_PSC (72-1)// TIM 输入捕获通道GPIO相关宏定义
#define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_Pin_0
#define GENERAL_TIM_CHANNEL_x TIM_Channel_1// 中断相关宏定义
#define GENERAL_TIM_IT_CCx TIM_IT_CC1
#define GENERAL_TIM_IRQ TIM5_IRQn
#define GENERAL_TIM_INT_FUN TIM5_IRQHandler// 获取捕获寄存器值函数宏定义
#define GENERAL_TIM_GetCapturex_FUN TIM_GetCapture1
// 捕获信号极性函数宏定义
#define GENERAL_TIM_OCxPolarityConfig_FUN TIM_OC1PolarityConfig// 测量的起始边沿
#define GENERAL_TIM_STRAT_ICPolarity TIM_ICPolarity_Rising
// 测量的结束边沿
#define GENERAL_TIM_END_ICPolarity TIM_ICPolarity_Falling// 定时器输入捕获用户自定义变量结构体声明
typedef struct
{ uint8_t Capture_FinishFlag; // 捕获结束标志位uint8_t Capture_StartFlag; // 捕获开始标志位uint16_t Capture_CcrValue; // 捕获寄存器的值uint16_t Capture_Period; // 自动重装载寄存器更新标志
}TIM_ICUserValueTypeDef;extern TIM_ICUserValueTypeDef TIM_ICUserValueStructure;/**************************函数声明********************************/void GENERAL_TIMER_Init(void);#endif /* __GENERALTIMER_H */
// stm32f10x_it.c文件
#include "GeneralTimer.h"void GENERAL_TIM_INT_FUN(void)
{// 当要被捕获的信号的周期大于定时器的最长定时时,定时器就会溢出,产生更新中断// 这个时候我们需要把这个最长的定时周期加到捕获信号的时间里面去if ( TIM_GetITStatus ( GENERAL_TIM, TIM_IT_Update) != RESET ) { TIM_ICUserValueStructure.Capture_Period ++; TIM_ClearITPendingBit ( GENERAL_TIM, TIM_FLAG_Update ); }// 上升沿捕获中断if ( TIM_GetITStatus (GENERAL_TIM, GENERAL_TIM_IT_CCx ) != RESET){// 第一次捕获if ( TIM_ICUserValueStructure.Capture_StartFlag == 0 ){// 计数器清0TIM_SetCounter ( GENERAL_TIM, 0 );// 自动重装载寄存器更新标志清0TIM_ICUserValueStructure.Capture_Period = 0;// 存捕获比较寄存器的值的变量的值清0 TIM_ICUserValueStructure.Capture_CcrValue = 0;// 当第一次捕获到上升沿之后,就把捕获边沿配置为下降沿GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Falling);// 开始捕获标准置1 TIM_ICUserValueStructure.Capture_StartFlag = 1; }// 下降沿捕获中断else // 第二次捕获{// 获取捕获比较寄存器的值,这个值就是捕获到的高电平的时间的值TIM_ICUserValueStructure.Capture_CcrValue = GENERAL_TIM_GetCapturex_FUN (GENERAL_TIM);// 当第二次捕获到下降沿之后,就把捕获边沿配置为上升沿,好开启新的一轮捕获GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Rising);// 开始捕获标志清0 TIM_ICUserValueStructure.Capture_StartFlag = 0;// 捕获完成标志置1 TIM_ICUserValueStructure.Capture_FinishFlag = 1; }TIM_ClearITPendingBit (GENERAL_TIM,GENERAL_TIM_IT_CCx); }
}
定时器所有中断的中断源都是TIMx_IRQn,在中断服务函数中来判断相应标志位决定是哪种中断。
// main.c文件
#include "stm32f10x.h"
#include "bsp_rccclkconfig.h"
#include "bsp_systick.h"
#include "usart.h"
#include "GeneralTimer.h"uint32_t time = 0;int main(void)
{// TIM 计数器的驱动时钟uint32_t TIM_PscCLK = 72000000 / (GENERAL_TIM_PSC+1);GENERAL_TIMER_Init();USART_Config();while(1){if(TIM_ICUserValueStructure.Capture_FinishFlag == 1){// 计算高电平时间的计数器值time = TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM_PERIOD + 1) + (TIM_ICUserValueStructure.Capture_CcrValue + 1);printf("\r\n 高电平脉宽时间: %d.%d \r\n", time/TIM_PscCLK, time%TIM_PscCLK);TIM_ICUserValueStructure.Capture_FinishFlag = 0;}}}
-
TIM_ICUserValueStructure.Capture_Period * (GENERAL_TIM_PERIOD + 1):计算的是高电平脉宽跨越的完整定时器周期的总时间。
-
TIM_ICUserValueStructure.Capture_Period:表示高电平脉宽跨越了多少个完整的定时器周期。
-
GENERAL_TIM_PERIOD + 1:表示一个完整的定时器周期的计数值。因为定时器是从0计数到GENERAL_TIM_PERIOD,所以一个完整的周期是GENERAL_TIM_PERIOD + 1。
-
-
TIM_ICUserValueStructure.Capture_CcrValue + 1:表示在最后一个定时器周期内,高电平脉宽的具体计数值,因为定时器是从0开始计数的,所以实际的计数值需要加1。
-
time/TIM_PscCLK:
-
time是以定时器的时钟周期为单位的计数值;
-
TIM_PscCLK是定时器的时钟频率,定时器的时钟周期(单位是秒)为:1 / TIM_PscCLK;
-
高电平脉宽的实际时间(单位是秒)为:time * (1 / TIM_PscCLK),即 time / TIM_PscCLK。
-
4.3 PWM输入捕获实验
4.3.1 硬件设计
-
使用通用定时器TIM3的通道1(PA6引脚)输出PWM信号;
-
使用高级定时器TIM1的通道1(PA8引脚)捕获PWM信号;
-
通过杜邦线连接PA6引脚与PA8引脚。
4.3.2 软件设计
编程要点:
-
1、通用定时器产生 PWM 配置
-
2、高级定时器 PWM 输入配置
-
3、编写中断服务程序,计算测量的频率和占空比,并打印出来比较
通用定时器产生PWM信号
// GeneralTimer.c文件
#include "GeneralTimer.h"static void GENERAL_TIM_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 输出比较通道1 GPIO 初始化RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
}static void GENERAL_TIM_Mode_Config(void)
{// 开启定时器时钟,即内部时钟CK_INT=72MGENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);/*--------------------时基结构体初始化-------------------------*/TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_PERIOD; // 驱动CNT计数器的时钟 = Fck_int/(psc+1)TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_PSC; // 时钟分频因子 ,配置死区时间时需要用到TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 计数器计数模式,设置为向上计数TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重复计数器的值,没用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定时器TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);/*--------------------输出比较结构体初始化-------------------*/ TIM_OCInitTypeDef TIM_OCInitStructure;// 配置为PWM模式1TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;// 输出使能TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 设置占空比大小TIM_OCInitStructure.TIM_Pulse = GENERAL_TIM_PULSE;// 输出通道电平极性配置:配置为高电平有效TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable); // 使能 OC1 预装载功能// 使能计数器TIM_Cmd(GENERAL_TIM, ENABLE);
}void GENERAL_TIMER_Init(void)
{GENERAL_TIM_GPIO_Config();GENERAL_TIM_Mode_Config();
}
// GeneralTimer.h文件
#ifndef __GENERALTIMER_H
#define __GENERALTIMER_H#include "stm32f10x.h"/************通定时器TIM参数定义************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 这里我们使用通用控制定时器TIM3#define GENERAL_TIM TIM3
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM3
// PWM 信号的频率 F = TIM_CLK/{(ARR+1)*(PSC+1)}
#define GENERAL_TIM_PERIOD (10-1) // ARR的值
#define GENERAL_TIM_PSC (72-1) // 分频因子
#define GENERAL_TIM_PULSE 9 // 占空比 = ADVANCE_TIM_PULSE/(ADVANCE_TIM_PERIOD+1)#define GENERAL_TIM_IRQ TIM3_UP_IRQn
#define GENERAL_TIM_IRQHandler TIM3_UP_IRQHandler// TIM3 输出比较通道1
#define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_Pin_6/**************************函数声明********************************/void GENERAL_TIMER_Init(void);#endif /* __GENERALTIMER_H */
捕获PWM信号
// AdvancedTimer.c文件
#include "AdvancedTimer.h" /*** @brief 高级控制定时器 TIMx,x[1,8]中断优先级配置* @param 无* @retval 无*/
static void ADVANCE_TIM_NVIC_Config(void)
{NVIC_InitTypeDef NVIC_InitStructure; // 设置中断组为0NVIC_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);
}
/*** @brief 高级定时器PWM输入用到的GPIO初始化* @param 无* @retval 无*/
static void ADVANCE_TIM_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);
}/*** @brief 高级定时器PWM输入初始化和用到的GPIO初始化* @param 无* @retval 无*/
static void ADVANCE_TIM_Mode_Config(void)
{// 开启定时器时钟,即内部时钟CK_INT=72MADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);/*--------------------时基结构体初始化-------------------------*/TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD; // 驱动CNT计数器的时钟 = Fck_int/(psc+1)TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC; // 时钟分频因子 ,配置死区时间时需要用到TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 计数器计数模式,设置为向上计数TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重复计数器的值,没用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定时器TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);/*--------------------输入捕获结构体初始化-------------------*/ // 使用PWM输入模式时,需要占用两个捕获寄存器,一个测周期,另外一个测占空比TIM_ICInitTypeDef TIM_ICInitStructure;// 捕获通道IC1配置// 选择捕获通道TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL;// 设置捕获的边沿TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;// 设置捕获通道的信号来自于哪个输入通道,有直连和非直连两种TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;// 1分频,即捕获信号的每个有效边沿都捕获TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;// 不滤波TIM_ICInitStructure.TIM_ICFilter = 0x0;// 初始化PWM输入模式TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);// 当工作做PWM输入模式时,只需要设置触发信号的那一路即可(用于测量周期)// 另外一路(用于测量占空比)会由硬件自带设置,不需要再配置// 捕获通道IC2配置
// TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL;
// TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
// TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
// TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
// TIM_ICInitStructure.TIM_ICFilter = 0x0;
// TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);// 选择输入捕获的触发信号TIM_SelectInputTrigger(ADVANCE_TIM, TIM_TS_TI1FP1); // 选择从模式: 复位模式// PWM输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器CNT会被复位TIM_SelectSlaveMode(ADVANCE_TIM, TIM_SlaveMode_Reset);TIM_SelectMasterSlaveMode(ADVANCE_TIM,TIM_MasterSlaveMode_Enable); // 使能捕获中断,这个中断针对的是主捕获通道(测量周期那个)TIM_ITConfig(ADVANCE_TIM, TIM_IT_CC1, ENABLE); // 清除中断标志位TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);// 使能高级控制定时器,计数器开始计数TIM_Cmd(ADVANCE_TIM, ENABLE);
}
/*** @brief 高级定时器PWM输入初始化和用到的GPIO初始化* @param 无* @retval 无*/
void ADVANCE_TIM_Init(void)
{ADVANCE_TIM_GPIO_Config();ADVANCE_TIM_NVIC_Config();ADVANCE_TIM_Mode_Config();
}
-
这里设置的捕获信号频率为1KHz,那么它能捕获到的信号频率要≥1KHz;(这里产生的PWM信号频率为100KHz)
- 因此,如果捕获信号的分频系数与ARR寄存器的值设定不合理的话会导致捕获不成功。
选择使用TI1FP1直连到捕获/比较寄存器1当作触发信号:
- TI1FP1到捕获/比较寄存器1来捕获周期;
- TI1FP2到捕获/比较寄存器2来捕获占空比(硬件自动配置)
选择使用TI1FP2非直连到捕获/比较寄存器2当作触发信号:
- TI1FP2到捕获/比较寄存器2来捕获周期;
- TI1FP1到捕获/比较寄存器1来捕获占空比(硬件自动配置);
// AdvancedTimer.h文件
#ifndef __ADVANCEDTIMER_H
#define __ADVANCEDTIMER_H#include "stm32f10x.h"/************高级定时器TIM参数定义,只限TIM1和TIM8************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
// 这里我们使用高级控制定时器TIM1#define ADVANCE_TIM TIM1
#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1// 输入捕获能捕获到的最小的频率为 72M/{ (ARR+1)*(PSC+1) }
#define ADVANCE_TIM_PERIOD (1000-1)
#define ADVANCE_TIM_PSC (72-1)// 中断相关宏定义
#define ADVANCE_TIM_IRQ TIM1_CC_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler// TIM1 输入捕获通道1
#define ADVANCE_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADVANCE_TIM_CH1_PORT GPIOA
#define ADVANCE_TIM_CH1_PIN GPIO_Pin_8#define ADVANCE_TIM_IC1PWM_CHANNEL TIM_Channel_1
#define ADVANCE_TIM_IC2PWM_CHANNEL TIM_Channel_2/**************************函数声明********************************/void ADVANCE_TIM_Init(void);#endif /* __ADVANCEDTIMER_H */
// stm32f10x_it.c文件
#include "AdvancedTimer.h"
#include "usart.h"__IO uint16_t IC2Value = 0;
__IO uint16_t IC1Value = 0;
__IO float DutyCycle = 0;
__IO float Frequency = 0;
/** 如果是第一个上升沿中断,计数器会被复位,锁存到CCR1寄存器的值是0,CCR2寄存器的值也是0* 无法计算频率和占空比。当第二次上升沿到来的时候,CCR1和CCR2捕获到的才是有效的值。其中* CCR1对应的是周期,CCR2对应的是占空比。*/
void ADVANCE_TIM_IRQHandler(void)
{/* 清除中断标志位 */TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);/* 获取输入捕获值 */IC1Value = TIM_GetCapture1(ADVANCE_TIM);IC2Value = TIM_GetCapture2(ADVANCE_TIM);// 注意:捕获寄存器CCR1和CCR2的值在计算占空比和频率的时候必须加1if (IC1Value != 0){/* 占空比计算 */DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);/* 频率计算 */Frequency = (72000000/(ADVANCE_TIM_PSC+1))/(float)(IC1Value+1);printf("占空比: %0.2f%% 频率: %0.2fHz\r\n", DutyCycle, Frequency);}else{DutyCycle = 0;Frequency = 0;}
}
// main.c文件
#include "stm32f10x.h"
#include "usart.h"
#include "GeneralTimer.h"
#include "AdvancedTimer.h"int main(void)
{USART_Config();GENERAL_TIMER_Init();ADVANCE_TIM_Init();while(1){}}
-
遇到的问题:原本的思路是将捕获到的值放到 while(1)中 进行打印,但是发现将PA6连接到PA8后,串口就不打印数据了;而断开后就能打印数据。
-
原因:当PA6连接到PA8后,程序会一直跑到定时器1捕获中断服务函数中处理数据。
-
解决:把要打印的数据放到定时器1捕获中断服务函数中。
不建议在中断中添加打印信息,如果打印数据多的话会影响系统的速度。
-
相关文章:

[STM32 - 野火] - - - 固件库学习笔记 - - -十三.高级定时器
一、高级定时器简介 高级定时器的简介在前面一章已经介绍过,可以点击下面链接了解,在这里进行一些补充。 [STM32 - 野火] - - - 固件库学习笔记 - - -十二.基本定时器 1.1 功能简介 1、高级定时器可以向上/向下/两边计数,还独有一个重复计…...

后台管理系统通用页面抽离=>高阶组件+配置文件+hooks
目录结构 配置文件和通用页面组件 content.config.ts const contentConfig {pageName: "role",header: {title: "角色列表",btnText: "新建角色"},propsList: [{ type: "selection", label: "选择", width: "80px&q…...

8.原型模式(Prototype)
动机 在软件系统中,经常面临着某些结构复杂的对象的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。 之前的工厂方法和抽象工厂将抽象基类和具体的实现分开。原型模式也差不多&#…...

Python-基于PyQt5,pdf2docx,pathlib的PDF转Word工具(专业版)
前言:日常生活中,我们常常会跟WPS Office打交道。作表格,写报告,写PPT......可以说,我们的生活已经离不开WPS Office了。与此同时,我们在这个过程中也会遇到各种各样的技术阻碍,例如部分软件的PDF转Word需要收取额外费用等。那么,可不可以自己开发一个小工具来实现PDF转…...

13 尺寸结构模块(size.rs)
一、size.rs源码 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE…...

STM32单片机学习记录(2.2)
一、STM32 13.1 - PWR简介 1. PWR(Power Control)电源控制 (1)PWR负责管理STM32内部的电源供电部分,可以实现可编程电压监测器和低功耗模式的功能; (2)可编程电压监测器(…...

CSS 样式化表格:从基础到高级技巧
CSS 样式化表格:从基础到高级技巧 1. 典型的 HTML 表格结构2. 为表格添加样式2.1 间距和布局2.2 简单的排版2.3 图形和颜色2.4 斑马条纹2.5 样式化标题 3. 完整的示例代码4. 总结 在网页设计中,表格是展示数据的常见方式。然而,默认的表格样式…...

【python】tkinter实现音乐播放器(源码+音频文件)【独一无二】
👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术。 【python】tkinter实现音乐播放器(源码…...

javascript常用函数大全
javascript函数一共可分为五类: •常规函数 •数组函数 •日期函数 •数学函数 •字符串函数 1.常规函数 javascript常规函数包括以下9个函数: (1)alert函数:显示一个警告对话框,包括一个OK按钮。 (2)confirm函数:显…...

C#属性和字段(访问修饰符)
不同点逻辑性/灵活性存储性访问性使用范围安全性属性(Property)源于字段,对字段的扩展,逻辑字段并不占用实际的内存可以被其他类访问对接收的数据范围做限定,外部使用增加了数据的安全性字段(Field)不经过逻辑处理占用内存的空间及位置大部分字段不能直接被访问内存使用不安全 …...

DeepSeek为什么超越了OpenAI?从“存在主义之问”看AI的觉醒
悉尼大学学者Teodor Mitew向DeepSeek提出的问题,在推特上掀起了一场关于AI与人类意识的大讨论。当被问及"你最想问人类什么问题"时,DeepSeek的回答直指人类存在的本质:"如果意识是进化的偶然,宇宙没有内在的意义&a…...

langchain基础(二)
一、输出解析器(Output Parser) 作用:(1)让模型按照指定的格式输出; (2)解析模型输出,提取所需的信息 1、逗号分隔列表 CommaSeparatedListOutputParser:…...

数据库安全管理中的权限控制:保护数据资产的关键措施
title: 数据库安全管理中的权限控制:保护数据资产的关键措施 date: 2025/2/2 updated: 2025/2/2 author: cmdragon excerpt: 在信息化迅速发展的今天,数据库作为关键的数据存储和管理中心,已经成为了企业营运和决策的核心所在。然而,伴随着数据规模的不断扩大和数据价值…...

Leetcode598:区间加法 II
题目描述: 给你一个 m x n 的矩阵 M 和一个操作数组 op 。矩阵初始化时所有的单元格都为 0 。ops[i] [ai, bi] 意味着当所有的 0 < x < ai 和 0 < y < bi 时, M[x][y] 应该加 1。 在 执行完所有操作后 ,计算并返回 矩阵中最大…...

【Proteus】NE555纯硬件实现LED呼吸灯效果,附源文件,效果展示
本文通过NE555定时器芯片和简单的电容充放电电路,设计了一种纯硬件实现的呼吸灯方案,并借助Proteus仿真软件验证其功能。方案无需编程,成本低且易于实现,适合电子爱好者学习PWM(脉宽调制)和定时器电路原理。 一、呼吸灯原理与NE555功能分析 1. 呼吸灯核心原理 呼吸灯的…...

SAP HCM insufficient authorization, no.skipped personnel 总结归纳
导读 权限:HCM模块中有普通权限和结构化权限。普通权限就是PFCG的权限,结构化权限就是按照部门ID授权,颗粒度更细,对分工明细化的单位尤其重要,今天遇到的问题就是结构化权限的问题。 作者:vivi,来源&…...

五. Redis 配置内容(详细配置说明)
五. Redis 配置内容(详细配置说明) 文章目录 五. Redis 配置内容(详细配置说明)1. Units 单位配置2. INCLUDES (包含)配置3. NETWORK (网络)配置3.1 bind(配置访问内容)3.2 protected-mode (保护模式)3.3 port(端口)配置3.4 timeout(客户端超时时间)配置3.5 tcp-keepalive()配置…...

4 [危机13小时追踪一场GitHub投毒事件]
事件概要 自北京时间 2024.12.4 晚间6点起, GitHub 上不断出现“幽灵仓库”,仓库中没有任何代码,只有诱导性的病毒文件。当天,他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒,等待不…...

Shadow DOM举例
这东西具有隔离效果,对于一些插件需要append一些div倒是不错的选择 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"utf-8"> <title>演示例子</title> </head> <body> <style&g…...

力扣动态规划-18【算法学习day.112】
前言 ###我做这类文章一个重要的目的还是记录自己的学习过程,我的解析也不会做的非常详细,只会提供思路和一些关键点,力扣上的大佬们的题解质量是非常非常高滴!!! 习题 1.下降路径最小和 题目链接:931. …...

网络基础
协议 协议就是约定 网络协议是协议中的一种 协议分层 协议本身也是软件,在设计上为了更好的模块化,解耦合,也是设计成为层状结构的 两个视角: 小白:同层协议,直接通信 工程师:同层协议&…...

使用 EXISTS 解决 SQL 中 IN 查询数量过多的问题
在 SQL 查询中,当我们面对需要在 IN 子句中列举大量数据的场景时,查询的性能往往会受到显著影响。这时候,使用 EXISTS 可以成为一种优化的良方。 问题的来源 假设我们有两个表,orders 和 customers,我们需要查询所有…...

使用SpringBoot发送邮件|解决了部署时连接超时的bug|网易163|2025
使用SpringBoot发送邮件 文章目录 使用SpringBoot发送邮件1. 获取网易邮箱服务的授权码2. 初始化项目maven部分web部分 3. 发送邮件填写配置EmailSendService [已解决]部署时连接超时附:Docker脚本Dockerfile创建镜像启动容器 1. 获取网易邮箱服务的授权码 温馨提示…...

Ruby Dir 类和方法详解
Ruby Dir 类和方法详解 引言 在 Ruby 中,Dir 是一个非常有用的类,用于处理文件系统中的目录。它提供了许多方便的方法来列出目录内容、搜索文件、以及处理文件系统的其他相关操作。本文将详细介绍 Ruby 的 Dir 类及其常用方法。 一、Dir 类概述 Dir …...

克隆OpenAI(基于openai API和streamlit)
utils.py: from langchain_openai import ChatOpenAI from langchain.memory import ConversationBufferMemory from langchain.chains import ConversationChain import osdef get_chat_response(api_key,prompt,memory): # memory不能是函数的内部局部变量&…...

位运算算法题
一.判断字符是否唯一 法一: 我们直接借助一个字符数组来模拟哈希表统计字符串即可,并且我们没有必要先将所有字符都放入字符数组中,边插入边判断,当我们要插入某个字符的时候,发现其已经出现了,此时必然重复…...

12 向量结构模块(vector.rs)
一vector.rs源码 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE…...

Android车机DIY开发之学习篇(六)编译讯为3568开发板安卓
Android车机DIY开发之学习篇(六)编译讯为3568开发板安卓 1.SDK解压到家目录下的 rk3588_android_sdk 目录 一. 全部编译 ###安装所需环境 sudo apt-get update sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g…...

Codeforces Round 863 (Div. 3) E. Living Sequence
题目链接 头一回用不是正解的方法做出来,也是比较极限,直接说做法就是二分数位dp 数位 d p dp dp 求 1 − n 1-n 1−n出现多少含 4 4 4的数字个数 这纯纯板子了 \sout{这纯纯板子了} 这纯纯板子了 设 f ( x ) f(x) f(x) 为 1 − x 1-x 1−x 中含有4的…...

一文讲解HashMap线程安全相关问题(上)
HashMap不是线程安全的,主要有以下几个问题: ①、多线程下扩容会死循环。JDK1.7 中的 HashMap 使用的是头插法插入元素,在多线程的环境下,扩容的时候就有可能导致出现环形链表,造成死循环。 JDK 8 时已经修复了这个问…...