尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

5.0 TIM

5.0 TIM TIM简介1. 什么是定时器TIM字面意思TIM是Timer的缩写就是用来计时的工具。核心原理它就像一个精密的“计数器”不停地对输入的时钟脉冲进行累加计数。当数字累加到程序员预先设定的数值时它会立刻触发一个中断打个比方就像闹钟走到设定的时间就会响铃通知CPU去执行相应的任务。2. 核心硬件结构如何实现长定时PPT里提到了三个关键硬件16位计数器、预分频器、自动重装寄存器。预分频器Prescaler可以把输入的时钟频率“降速”。比如输入是72MHz我们可以把它除以7200变成10kHz。预分频器的作用是让定时器能计更久的时间。16位计数器它能计数的最大值是 216−165535216−165535。自动重装寄存器Auto-reload当计数器数到我们设定的值后自动清0并重新开始下一轮计数。最大59.65秒是怎么来的在72MHz的时钟下最大预分频系数是 65536。最大计数值是 65536。最大定时时间 65536×65536÷72,000,000≈59.6565536×65536÷72,000,000≈59.65 秒。这意味着仅靠这一个硬件即使不借助软件扩展也能实现将近1分钟的精准定时。3. 多功能性PPT特别强调了定时器的多功能性它不止能干“定时报警”的活还具备输入捕获Input Capture可以测量外部信号的脉冲宽度比如测量遥控器的信号。输出比较Output Compare可以产生特定波形比如控制舵机、电机调速的PWM波。编码器接口可以直接连接旋转编码器用来读取电机转动的角度和速度。主从触发模式可以让一个定时器作为“指挥官”去触发另一个定时器或者外设比如ADC采样实现同步操作。定时器的分类根据用途分级STM32的定时器并不是统一的根据复杂程度分为三类基本定时器Basic功能最简单只能做基础定时用来做简单的延时或触发DAC。通用定时器General功能中等应用最广具备输入捕获、输出比较PWM等功能常用于电机控制、信号测量。高级定时器Advanced功能最强大除了具备通用定时器的所有功能外还增加了死区控制用于控制大功率MOSFET管防短路、互补输出等专门用于高级电机如三相电机的驱动控制。基本定时器框图解析STM32 基本定时器TIM6/TIM7内部是怎么“数时间”的。RCC 时钟 → CK_PSC → 预分频器 PSC → CK_CNT → CNT计数器 → 自动重装载寄存器 ARR → 产生更新事件先把很快的时钟“分频变慢”然后让计数器一个一个数数到设定值以后产生事件。1. 最左边RCC 给定时器一个时钟来自RCC的TIMxCLK这个就是定时器的输入时钟。比如你的 STM32F103C8T6假设某个定时器最终拿到TIMxCLK 72 MHz那么意味着定时器每秒钟接收到 72000000 个时钟脉冲。但是这么快肯定没办法直接拿来做每1秒触发一次所以要先经过下面这个东西。2. PSC预分频器图中CK_PSC → PSC预分频器 → CK_CNTPSC 就是Prescaler预分频器它的作用非常简单把定时器时钟降下来TIMxCLK 72 MHzPSC 7199实际上 STM32 这里的计算是CKCNT​PSC1TIMxCLK​所以CKCNT​7199172MHz​ 10kHz也就是说原来72000000 次/秒经过 PSC10000 次/秒于是计数器就舒服多了。3. CNT真正“数数”的地方图中这个/- CNT计数器就是定时器最核心的东西。它本质上就是一个寄存器TIMx-CNT它会随着 CK_CNT 的节拍0123456...不断变化。基本定时器这里主要就是向上计数。所以你可以把它想成一个人在数1、2、3、4、5、6……每来一个 CK_CNT它就数一次。4. ARR自动重装载寄存器图中上面的自动重装载寄存器也就是ARRAuto Reload Register它的作用就是规定 CNT 数到多少。例如ARR 9999那么 CNT 就会012...99989999继续往下数就会产生一次更新事件。然后 CNT 重新开始012...这就是“自动重装载”。所以ARR决定“数多少个”。而PSC决定“数得多快”。这两个你一定要分清。5. 为什么叫“更新事件”这个图里虽然没有特别突出写出来但是这就是定时器最重要的一个动作。比如CK_CNT 10 kHzARR 9999也就是CNT每秒数10000次。那么从0 → 9999一共需要10000个计数周期。所以T1000010000​1s于是每1秒产生一次更新事件。这就是定时器。你可以把它理解成时钟↓分频↓慢下来的时钟↓CNT开始数↓数到ARR↓产生更新事件↓重新开始数6. 图左上角那个“控制器”是什么图中还有触发控制器下面还有控制器这个部分你现在可以先把它理解成管理定时器什么时候运行、什么时候停止、什么时候复位、什么时候计数。复位、使能、计数所以你可以把它理解成定时器的“总管”。比如TIM_Cmd(TIM2, ENABLE);本质上就是告诉这个定时器开始工作。7. TRGO 是干什么的图右上方有TRGO → 至 DAC这个先不要被吓到。TRGOTrigger Output触发输出也就是说定时器产生某些内部事件之后可以把这个事件作为一个触发信号送给其他外设。例如这里画到了 DAC。定时器可以按照固定时间间隔去触发 DAC 工作。所以定时器不只是“让我延时”还可以“每隔固定时间让另一个外设干一件事。”这个思想以后非常重要。8. 图里面两个“U”是什么意思你图里 ARR 和 CNT 附近有很多U这个通常表示Update更新也就是更新事件相关的信号。比如 CNT 数满CNT ARR发生更新。这个更新事件可以进一步更新寄存器产生中断触发其他外设让一些内部状态发生改变所以以后看到UI、U、Update你就知道“定时器计数到头了发生更新了。”9. 这个图最重要的地方RCC提供时钟 → PSC把时钟分频 → CNT按照分频后的时钟开始计数 → CNT数到ARR → 产生更新事件 → CNT重新开始计数。画成你脑子里的简图RCC│▼TIMxCLK│▼┌──────────┐│ PSC │ ← 决定“数得多快”│ 预分频器 │└────┬─────┘│▼CK_CNT│▼┌──────────┐│ CNT │ ← 真正负责数数│ 计数器 │└────┬─────┘││ 数到 ARR▼┌──────────┐│ ARR │ ← 决定“数多少”│自动重装载│└────┬─────┘│▼更新事件│├──→ 中断├──→ TRGO└──→ CNT重新开始最后给你一个特别重要的公式定时器最终的溢出/更新周期例如TIMxCLK 72MHzPSC 7199ARR 9999那么T720000007200×10000​1s所以这组参数就是每 1 秒产生一次更新事件。更新事件 → 触发中断 → CPU进入中断函数。通用定时器框图解析那为什么通用定时器突然这么复杂因为它不满足于“我每隔一段时间数一次。”① 可以接外部信号图左边TIMx_CH1TIMx_CH2TIMx_CH3TIMx_CH4TIMx_ETR这些就是定时器的外部输入引脚。所以通用定时器可以接收外部脉冲然后数这些脉冲。例如外部传感器 → 定时器 → CNT传感器来一个脉冲定时器就数一次。这对你之前学习的旋转编码器就特别重要。② 它不光能“数时间”还能测外部信号图下面这一大片输入滤波器和边沿检测器↓输入捕获↓捕获/比较寄存器这玩意儿可以干测一个脉冲什么时候来。例如定时器可以通过这些东西测脉冲宽度周期频率相邻两个边沿之间的时间这就是以后你会学的输入捕获③ 它还能自己输出波形图右边捕获/比较寄存器↓输出控制↓TIMx_CH1这意味着定时器不仅能接收信号还能自己生成信号最典型的就是PWM比如这就是 PWM 波。所以通用定时器可以拿来控制舵机调节 LED 亮度控制电机输出特定频率的方波你现在真正需要理解的其实只有三层别想着把整张图一次啃掉。第一层最基础你现在先学定时器怎么“计时”时钟↓预分频器↓CNT↓ARR↓更新事件这就是你刚才那个基本定时器。第二层通用定时器增加了“输入”通用定时器在原来的基础上增加外部引脚↓输入滤波↓输入捕获↓捕获/比较寄存器于是它可以测外部信号。第三层通用定时器增加了“输出”又增加CNT↓捕获/比较寄存器↓输出控制↓CH1/CH2/CH3/CH4于是它可以产生 PWM 等输出信号。你前面学的基本定时器实际上就是一个功能比较少的定时器。现在这个通用定时器在“会计时”的基础上又加了输入捕获、输出比较、PWM、外部触发等一大堆功能。高级定时器框图解析通用定时器 一大堆专门给电机/PWM控制准备的功能。你现在看这张图先看它最核心的骨架跟前面完全一样时钟↓预分频器↓CNT计数器↓自动重装载寄存器↓更新事件所以基本定时器计时通用定时器计时 输入捕获 输出比较/PWM高级控制定时器通用定时器重复计数器互补输出死区控制刹车保护更强的PWM控制右边多出来一堆① 重复次数计数器图里这个重复次数计数器以及REP寄存器这个是高级定时器比较有代表性的东西。普通定时器可能数满一次↓更新事件高级定时器可以数满一次↓先不更新再数满一次↓还是不更新……重复若干次↓才产生一次真正的更新所以它多了一个“要重复几次才算真正更新”的功能。② 最重要的互补输出你看到右边TIMx_CH1TIMx_CH1N还有TIMx_CH2TIMx_CH2N这个N很关键。例如CH1CH1N就是一对互补输出。可以理解成一个高的时候另一个往往低。这个东西特别适合控制电机、逆变桥、MOS管所以高级定时器为什么叫“高级”不是它计时更高级。而是它能玩功率控制。③ 死区时间这张图里有一个很关键的DTG寄存器对应的就是死区发生器。这个东西第一次看到会懵但实际上非常简单。假设有上下两个 MOS 管上管│▼电机│下管理论上上管开下管关然后换状态上管关下管开但是现实中的 MOS 管不是说关就立刻完全关。如果上管刚关下管立刻就开可能出现上管还没完全关↓下管已经开了↓两个管子同时导通↓短路这就很危险。所以高级定时器故意插一个很短的“都关着的空白时间”这就是死区时间这个功能就是高级定时器特别重要的地方。④ 刹车输入图最下面TIMx_BKIN这个也很重要。它相当于一个紧急刹车/保护输入例如电机发生异常过流过压故障外部保护电路给 BKIN 一个信号。高级定时器可以马上关闭 PWM 输出。所以你可以把它理解成“出事了先把功率管关了”这就是高级控制定时器的安全功能。所以你现在看到三种定时器可以这样理解基本定时器我只负责计时核心时钟 → 预分频 → CNT → ARR → 更新通用定时器我除了计时还能测信号、输出PWM增加输入捕获输出比较PWM4个通道高级控制定时器我主要是拿来控制电机、逆变器这些东西的在通用定时器基础上增加重复计数互补输出死区时间刹车保护你现在千万别试图把这张图全看懂真的没必要。你现阶段直接把这张图砍成下面这一部分PWM → 通用定时器 → 高级定时器再一个一个把捕获/比较、互补输出、死区、刹车、重复计数你会发现它根本不是“又来了一套新知识”而是在原来的定时器上不断加功能。所以你现在看到基本 → 通用 → 高级会计时 → 会计时测信号发PWM → 会计时PWM专门搞电机的保护和控制定时中断基本结构流程“谁给定时器提供时钟” → 决定计数规则 → CNT开始数 → 数到ARR → 产生更新事件 → 允许的话产生中断 → NVIC通知CPU。第一步先给定时器一个“时钟”图最左边有很多可能的时钟来源RCC内部时钟GPIO → ETR外部时钟其他定时器 → ITRxGPIO → TIx捕获通道你可以把它理解成定时器有很多个“水龙头”但一次只能选择自己要用哪一个。① RCC内部时钟最上面RCC内部时钟↓内部时钟模式这是我们最常用、也最容易理解的。比如RCC给TIM2一个72MHz的时钟意思就是定时器自己按照系统提供的时钟节拍工作。你平时写普通的定时中断大多数就是走这条路。还可以用外部时钟图里面还有GPIO↓ETR外部时钟↓外部时钟模式2就是不让定时器按照内部时钟计数而是让外面的引脚给它脉冲。比如外面来了↑ ↓ ↑ ↓ ↑ ↓来了一个脉冲定时器就可以数一次。所以内部时钟 芯片自己打节拍外部时钟 外面的设备来打节拍其他定时器也能给它提供时钟图中ITRx其他定时器↓外部时钟模式1这个看着很高级其实思想非常简单定时器之间可以串起来。比如定时器1↓定时器2↓定时器3定时器1产生一个事件可以拿去驱动定时器2。这以后做复杂定时、同步控制的时候特别有用。你现在先知道一个定时器可以拿另一个定时器的信号当自己的输入。捕获通道也可以参与图左下GPIO↓TIx捕获通道↓编码器模式这个是以后你学输入捕获编码器会用到的。例如你之前玩的旋转编码器A、B信号就可以进入定时器的捕获通道。所以这张图其实已经把定时器 旋转编码器这条路也画出来了。最关键的一块内部时钟模式选择你看到中间这个黄色大框内部时钟模式外部时钟模式2外部时钟模式1编码器模式这实际上就是在做一件事选择“定时器到底采用哪一种计数方式”。你可以把它理解成一个大总开关/模式选择器。比如你现在只想做普通定时选择内部时钟模式如果要让外部脉冲驱动选择外部时钟模式如果要接旋转编码器选择编码器模式本质就是选择谁来驱动CNT。选完之后进入“时基单元”图中粉色的大框时基单元这就是定时器真正负责“计时”的核心部分。里面有三个你现在最应该记住的东西预分频器计数器自动重装载器也就是PSCCNTARR预分频器把时钟变慢假设前面来的时钟是72MHz经过预分频器72MHz → 10kHz比如CKCNT​PSC1TIMxCLK​所以预分频器决定CNT数得有多快。你可以记成PSC 调速度CNT真正开始数经过预分频之后时钟↓预分频器↓CNT计数器CNT开始012345……所以CNT就是“数数的人”。ARR决定“数到多少”旁边这个ARR 自动重装器它相当于告诉CNT你数到多少算一轮。比如ARR 9999那么0123...9999到头以后产生一次更新。所以ARR 决定数多少现在就到最关键的一步更新事件当CNT数到ARR规定的值以后CNT数满↓更新事件图上这个“U”和“UI”本质都和更新有关。这个更新事件可以理解为“这一轮计时结束了”例如72MHz↓预分频↓10kHz↓CNT数10000次↓更新事件如果刚好是一秒那就每秒产生一次更新事件。这就是定时器定时的本质。但是“更新事件”不等于“中断”这个地方非常重要。很多初学者容易把这两个东西混在一起事件 ≠ 中断正确的关系是CNT数满↓产生更新事件↓如果开启了更新中断↓产生中断请求↓进入中断系统↓NVIC↓CPU进入中断函数所以你前面学的TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);干的事情其实就是允许“更新事件”去申请中断。运行控制是干什么的图下方运行控制这个你可以理解成定时器的“开关和控制面板”。例如TIM_Cmd(TIM2, ENABLE);就是启动定时器。如果关闭TIM_Cmd(TIM2, DISABLE);就是停止定时器。所以运行控制↓允许时基单元运行↓CNT开始数最后就是“中断输出控制”图右边中断输出控制↓NVIC这里非常关键。整个过程可以完整串起来RCC内部时钟↓选择内部时钟模式↓预分频器↓CNT计数器↓ARR判断↓CNT数满↓产生更新事件↓开启更新中断↓中断输出控制↓NVIC↓CPU↓执行中断函数这就是你现在学习的定时器中断完整流程拿一个实际例子你就彻底明白了假设我们要让STM32每1秒进入一次定时器中断。假设定时器时钟 72MHzPSC 7199ARR 9999先经过预分频72MHz÷(71991)10kHz所以CNT每秒数10000次。ARR数到9999。所以012...9999↓更新事件↓更新中断↓NVIC↓进入TIM中断函数然后重新012...9999↓又一次中断所以最后就是每1秒执行一次中断函数。你现在可以把整张图压缩成这张“脑内地图”谁来提供时钟↓┌──────────┬──────────┐↓ ↓ ↓RCC 外部引脚 其他定时器↓ ↓ ↓模式选择↓时基单元↓预分频器 PSC↓计数器 CNT↓自动重装载 ARR↓数到头了↓更新事件↓开启了更新中断↙ ↘否 是↓ ↓结束 NVIC↓CPU↓中断函数第一时钟从哪里来。第二预分频器负责把时钟变慢。第三CNT负责数。第四ARR负责规定数到多少。第五数满产生更新事件更新中断打开后才会一路进 NVIC、进中断函数。而你图上那些ETR、ITRx、TIx、编码器模式、外部时钟模式先不要全部背。它们主要是在回答一个问题“定时器的时钟/计数信号还可以从哪里来”时钟 → 预分频 → CNT计数 → ARR判断 → 更新事件 → 中断 → NVIC → CPU预分频时序计数器时序计数器无预装时序计数器有预装时序定时器不停地拿时钟来数数PSC决定它数得有多慢CNT负责数ARR决定数到多少当CNT数满以后产生更新事件而PSC、ARR这些“新参数”到底什么时候真正换上去就是这几张时序图想告诉你的。比如你原来PSC1现在想改成PSC2。你虽然已经把新值写进寄存器了但定时器不会毫无秩序地在当前计数过程中突然换节奏它会先把这个新值放在那里等到一个合适的更新时间再真正启用。所以第一张图其实就是在解释“我改了预分频值什么时候真正影响CNT”然后 CNT 一直数假设 ARR 是 36它就从 00、01、02……一路数到 36。数到头以后回到 00这就是一次溢出同时产生更新事件。第二张图就是把这个过程画成波形让你看到时钟来了 → CNT加一 → CNT数满 → 溢出 → 更新事件 → 更新中断标志。接下来 ARR 又有两种情况。如果不开预装载你改 ARR新的 ARR 就直接拿来用了所以这一轮计数可能马上受到影响。如果开预装载你改的 ARR 不会立刻碰真正正在工作的那个值而是先存起来。等这一轮计数结束产生更新事件的时候再把新 ARR 换进去。这样就相当于“这一轮先跑完下一轮再换新参数。”这就是后面那两张图真正想表达的东西。时钟来了 → 预分频 → CNT开始数 → ARR规定终点 → CNT数满 → 更新事件 → 新参数在这个时刻完成交接。这样看四张图其实是在描述同一个定时器只不过把同一个过程的不同细节放大给你看。RCC时钟树RCC 时钟树可以理解成STM32内部有很多种时钟源RCC负责从里面选一个、加工一下再把不同频率的时钟分配给 CPU、总线、定时器、USB、ADC 等外设。产生时钟 → 选择和倍频/分频 → 分发给不同模块。定时器计数时钟配num//TIME.c文件 #include stm32f10x.h // Device header void Timer_Init() { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//激活TIM3时钟 TIM_InternalClockConfig(TIM3);//设置TIM时钟来源于内部 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//创造一个表 TIM_TimeBaseInitStructure.TIM_ClockDivisionTIM_CKD_DIV1;//时钟分频 TIM_TimeBaseInitStructure.TIM_CounterModeTIM_CounterMode_Up;//计数模式为向上计数 TIM_TimeBaseInitStructure.TIM_Period9999;//ARR TIM_TimeBaseInitStructure.TIM_Prescaler7199;//psc TIM_TimeBaseInitStructure.TIM_RepetitionCounter0;//高级功能重复次数寄存器 TIM_TimeBaseInit(TIM3,TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM3,TIM_FLAG_Update);//清除标志防止刚刚触发就进入中断从1开始计数. TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//配置中断 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 配置NVIC优先级分组 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannelTIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmdENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority0;//抢占 NVIC_InitStructure.NVIC_IRQChannelSubPriority0;//事件 NVIC_Init(NVIC_InitStructure); TIM_Cmd(TIM3, ENABLE); } volatile uint8_t num0; void TIM3_IRQHandler() { if(TIM_GetITStatus(TIM3,TIM_IT_Update)SET) { num; TIM_ClearITPendingBit(TIM3,TIM_IT_Update); } } Time.h文件 #ifndef __Timer_H #define __Timer_H void Timer_Init(void); extern volatile uint8_t num; #endif //main.c文件 #include stm32f10x.h #include Delay.h #include OLED.h #include Timer.h int main(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_ModeGPIO_Mode_IPU; GPIO_InitStructure.GPIO_PinGPIO_Pin_4; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); OLED_Init(); Timer_Init(); while (1) { OLED_ShowNum(1,1,num,8); if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)0) { num0; } } }定时器外部时钟毛刺暂未克服//主函数 #include stm32f10x.h #include Delay.h #include OLED.h #include ExternTimerInfraredSensor.h int main(void) { OLED_Init(); OLED_ShowString(1,1,counts:); OLED_ShowString(3,1,NUM:); SensorInit(); EX_Timer(); while (1) { OLED_ShowNum(2,1,TIM_GetCounter(TIM2),8); OLED_ShowNum(4,1,num,8); } } //.c #include stm32f10x.h // Device header volatile uint8_t num0; //对射式红外传感器GPIO配置 void SensorInit()//配置传感器GPIO口 { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_ModeGPIO_Mode_IPU; GPIO_InitStructure.GPIO_PinGPIO_Pin_0; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); } void EX_Timer() { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//时钟 TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x02);//外部时钟配置 TIM_TimeBaseInitTypeDef Time_BaseInitStructure;//时基单元配置 Time_BaseInitStructure.TIM_ClockDivisionTIM_CKD_DIV1; Time_BaseInitStructure.TIM_CounterModeTIM_CounterMode_Up; Time_BaseInitStructure.TIM_Period10-1; Time_BaseInitStructure.TIM_Prescaler0; Time_BaseInitStructure.TIM_RepetitionCounter0; TIM_TimeBaseInit(TIM2,Time_BaseInitStructure); TIM_ClearFlag(TIM2,TIM_FLAG_Update);//防止上电就中断 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//中断配置 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//NVIC分组 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannelTIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmdENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority0; NVIC_InitStructure.NVIC_IRQChannelSubPriority0; NVIC_Init(NVIC_InitStructure); TIM_Cmd(TIM2,ENABLE);//打开时钟 } volatile uint8_t lock 0; void TIM2_IRQHandler() { if(TIM_GetITStatus(TIM2,TIM_IT_Update)SET) { num; TIM_ClearITPendingBit(TIM2,TIM_IT_Update); } } //.h #ifndef __ExternTimerInfraredSensor_H #define __ExternTimerInfraredSensor_H void SensorInit(); void EX_Timer(); extern volatile uint8_t num; #endif
返回列表