做网站需要会什么 知乎,wordpress搜索框选择分类,淄博免费建站,wordpress商业破解初识STM32单片机-TIM定时器 一、定时器概述二、定时器类型2.1 基本定时器(TIM6和TIM7)2.2 通用定时器(TIM2、TIM3、TIM4和TIM5)2.3 高级定时器(TIM1和TIM8) 三、定时中断基本结构和时基单元工作时序3.1 定时器基本结构3.2 预分频器时序3.3 计数器时序3.3.1 计数器有无预装时序(… 初识STM32单片机-TIM定时器 一、定时器概述二、定时器类型2.1 基本定时器(TIM6和TIM7)2.2 通用定时器(TIM2、TIM3、TIM4和TIM5)2.3 高级定时器(TIM1和TIM8) 三、定时中断基本结构和时基单元工作时序3.1 定时器基本结构3.2 预分频器时序3.3 计数器时序3.3.1 计数器有无预装时序(有无缓冲寄存器) 四、TIM输出比较OC[4.1 PWM波形](https://blog.csdn.net/m0_51319492/article/details/138548314)[4.2 舵机](https://blog.csdn.net/m0_51319492/article/details/138548314)4.3 直流电机 五、TIM输入捕获IC5.1 频率测量5.2 输入捕获/PWMI基本结构5.2.1 输入捕获基本结构5.2.2 PWMI基本结构 六、编码器6.1 正交编码器方向6.2 编码器电路和基本结构 七、代码编写7.1 定时器库函数介绍7.2 定时器定时中断代码7.3 定时器外部时钟申请7.4 定时器输出比较代码7.4.1 PWM驱动LED呼吸灯7.4.2 PWM驱动舵机7.4.3 PWM驱动直流电机 7.5 定时器输入捕获代码7.5.1 输入捕获模式测频率7.5.2 PWMI模式测频率和占空比 7.6 编码器接口测速 一、定时器概述
TIM(Timer)定时器定时器可以对输入的时钟进行计数并在计数值达到设定值时触发中断16位计数器、预分频器、自动重装寄存器的时基单元在72MHz计数时钟下可以实现最大59.65s的定时 频率1MHz对应周期1/1MHz 1us, 1KHz对应1ms, 1Hz对应1s 在STM32中一个基准时钟(时钟周期)是1s/72MHz计72个数字就是过了1s/72MHz×72 1us如果计72000个数那就是1ms
不仅具备基本的定时中断功能而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
二、定时器类型 高级到低级向下兼容
STM32F103C8T6定时器资源TIM1、TIM2、TIM3、TIM4
2.1 基本定时器(TIM6和TIM7) 基本定时器TIM6和TIM7各包含一个16位自动装载计数器由各自的可编程预分频器驱动 它们可以作为通用定时器提供时间基准特别地可以为数模转换器(DAC)提供时钟。实际上它们在芯片内部直接连接到DAC并通过触发输出直接驱动DAC 主要功能
16位自动重装载累加计数器16位可编程(可实施修改)预分频器用于对输入的时钟按系数为1~65535之间的任意数值分频触发DAC的同步电路在更新事件(计数器溢出)时产生中断/DMA中断 RCC的TIMxCLK是内部时钟输入频率值是系统的主频72MHz所以通向时基单元的基准频率就是72MHz。 PSC预分频器、CNT计数器、自动重装载寄存器共同组成时基单元PSC对72MHz的基准时钟进行预分频(寄存器写0就是1分频写1就是2分频输出频率输入频率/272MHz/2)。CNT(16位)对预分频后的时钟进行计数(每来一个上升沿计数时钟加1)。自动重装载寄存器就是存的写入的计数目标运行过程中计数值不断自增自动重装值是固定的目标当计数值等于自动重装值即计数达到产生中断信号并清零计数器开始下一次的计数 其中UI为更新中断通往NVICU代表更新事件不会触发中断触发其他事件的工作 主从模式触发DAC更新事件U映射到TRGOTRGO就可以触发DAC不需要利用中断去执行 C51系列单片机是设定计数初值STM32系列是设定计数终值
2.2 通用定时器(TIM2、TIM3、TIM4和TIM5) 通用定时器是一个通过可编程分频器驱动的16位自动装载计数器构成 它适合于多种场合包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM) 使用定时器预分频器和RCC时钟控制器预分频器脉冲长度和波形周期可以在几个微秒和几个毫秒间调整 主要功能
16位向上(计数器从0开始向上自增计到重装值清零同时申请中断)、向下(计数器从重装值开始向下自减减到0以后回到重装值同时申请中断)、向上/向下(计数器从0开始先向上自增计到重装值申请中断然后再向下自减减到0申请中断)自动装载计数器16位可编程(可以实现修改)预分频器计数器时钟频率的分频系数为1~65535之间的任意数值4个独立通道输入捕获、输出比较、PWM生成和单脉冲模式输出使用外部信号控制定时器和定时器互连的同步电路如下事件发生时产生中断/DMA
更新计数器向上溢出/向下溢出计数器初始化(软件或者内部/外部触发)触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)输入捕获输出比较
支持针对定位的增量(正交)编码器和霍尔传感器电路触发输入作为外部时钟或者按周期的电流管理 时基单元的基本结构预分频器、计数器和自动重装载寄存器同基本定时器工作流程也是一样但对于通用计数器而言计数器的模式就不止向上计数这一种了。 下面介绍通用定时器与基本定时器不同的地方 如下图所示是内外时钟源选择和主从触发模式的结构 基本定时器只能选择内部时钟也就是系统频率72MHz。通用定时器里时钟源不仅可以选择72MHz时钟也可以选择外部时钟相当于定时器指定的外部引脚上输入一个方波信号来提供定时器的时钟。 选择内部还是外部来提供时钟的本质区别就是计数频率是72MHz还是外部方波信号 TIMx_ETR引脚上的外部时钟通过ETRP、输入滤波、ETRF进入时基单元这一路叫做外部时钟模式2。TRGI(触发输入)也可以提供时钟由ETR、ITR(其他定时器)、CH1引脚边沿、CH1、CH2提供可以触发定时器的从模式叫做外部时钟模式1 如下图是输出比较电路 总共有4个通道分别对应CH1-CH4的引脚用于输出PWM的波形 下面是输入捕获电路 也是4个通道对应的是CH1-CH4的引脚用于测量输入方波的频率等 下面是捕获/比较寄存器是输入捕获和输出比较电路公用的,输入捕获和输出比较不可以同时使用 2.3 高级定时器(TIM1和TIM8) 高级定时器由一个16位的自动装载计数器组成由一个可编程的预分频器驱动 它适合多种用途包含测量输入信号的脉冲宽度(输入捕获)或者产生输出波形(输出比较、PWM、嵌入死区时间的互补PWM等) 使用定时器预分频器和RCC时钟控制预分频器可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节 主要功能
16位向上(计数器从0开始向上自增计到重装值清零同时申请中断)、向下(计数器从重装值开始向下自减减到0以后回到重装值同时申请中断)、向上/向下(计数器从0开始先向上自增计到重装值申请中断然后再向下自减减到0申请中断)自动装载计数器16位可编程(可以实现修改)预分频器计数器时钟频率的分频系数为1~65535之间的任意数值4个独立通道输入捕获、输出比较、PWM生成和单脉冲模式输出死区时间可编程的互补输出使用外部信号控制定时器和定时器互连的同步电路允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态如下事件发生时产生中断/DMA
更新计数器向上溢出/向下溢出计数器初始化(软件或者内部/外部触发)触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)输入捕获输出比较刹车信号输入
支持针对定位的增量(正交)编码器和霍尔传感器电路触发输入作为外部时钟或者按周期的电流管理 下面给出高级定时器的框图 从上图中可以看出高级定时器与通用定时器不同的部分仅两块内容 第一块是申请中断的部分加了一个重复次数计数器可以实现每隔几个计数周期产生一次更新事件和更新中断通用定时器是一个计数周期一个中断高级定时器相当于对输出的更新信号又进行了一次分频。 第二块是对输出比较器的升级DTG是死区生成电路右边前三路的输出引脚由原来的一个变为了两个互补的输出可以输出一组互补的PWM波为了驱动三相无刷电机。下面的是刹车输入功能为了给电机驱动提供安全保障的如果外部引脚BKIN产生了刹车信号或者内部时钟失效那么控制电路会自动切断电机的输出防止意外发生
三、定时中断基本结构和时基单元工作时序
3.1 定时器基本结构 下面给出定时中断基本结构即定时器的工作配置流程 其中最重要的就是由PSC预分频器、CNT计数器和ARR自动重装器组成的时基单元。运行控制是控制寄存器的一些位比如启动停止、向上或者向下计数等等就可以控制时基单元的运行了。 左半部分是为时基单元提供时钟的部分可以选择RCC提供内部时钟或者ETR引脚提供的外部时钟模式2也可以选择触发输入(ETR、ITRx其他定时器、TIx捕获通道)当作外部时钟即外部时钟模式1或者是由编码器提供时钟。 最右边是计时时间到达时产生更新中断后的信号去向其中如果是高级定时器还会再输出信号部分多个重复计数器中断信号会先在状态寄存器里置一个中断标志位经过中断输出控制(判断哪个中断允许)进入NVIC最后进入中断函数
3.2 预分频器时序 当预分频器的参数从0变到1(1分频(不分频)和2分频)时计数器的时序图如下所示 CK_PSC是内部时钟CNT_EN是计数器使能高电平计数器正常运行低电平关闭CK_CNT是定时器时钟它既是预分频器的时钟输出也是计数器的时钟输入。前半段预分频器系数为0(1分频)计数器的时钟等于预分频器的时钟后半段预分频器系数为1(2分频)计数器的时钟变为预分频器时钟的一半在计数器时钟的驱动下下面的计数器寄存器也跟随时钟的上升沿不断自增(到达FC后变0ARR自动重装值就是FC) 下面三行描述的是预分频器的一种缓冲机制,当计数计到一半的时候改变了分频值这个变化不会立刻生效而是会等到本次计数周期结束产生更新事件预分频器寄存器的值才会被传递到预分频缓冲器中才会生效 计数频率CK_CNT CK_PSC/(PSC1) 3.3 计数器时序 计数器时序图内部时钟分频因子为2(2分频)即分频系数为1 CK_INT内部时钟72MHzCNT_EN时钟使能CK_CNT为定时器时钟周期为内部时钟的一半(2分频)计数器上升沿自增自增到0036溢出产生更新事件脉冲(UEV)置一个更新中断标志位(UIF置1申请中断,中断响应后手动清零标志位) 计数器溢出频率 CK_CNT_OV CK_CNT/(ARR1) CK_PSC/(PSC1)/(ARR1) 其中CK_PSC是内部时钟72MHz或者外部方波信号、PSC是给定分频器的分频、ARR为自动重装值(计数目标值) 计数器溢出时间取溢出频率的倒数即可 其中也是带缓冲机制的影子寄存器可以自己设置时序如下所示 3.3.1 计数器有无预装时序(有无缓冲寄存器) 当ARPE 0时的更新事件(TIMx_ARR没有预装入) 计数目标突然从FF变成了36所以直接计到36就产生更新中断开始下一轮计数 当ARPE 1时的更新事件(预装入了TIMx_ARR) 计数目标从F5变成了36但是并没有立即计到36停止而是先计到F5产生更新中断以后下一次计数周期的计数目标是36 影子寄存器才是真正起作用的影子寄存器的功能就是让值的变化和更新事件同步发生(比如计数已经到31你突然改成重装值30)防止在运行途中更改造成错误
四、TIM输出比较OC
输出比较可以通过比较CNT(计数器)和CCR(捕获/比较寄存器)值的关系来对输出电平进行置1、置0和电平翻转的操作用于输出一定频率和占空比的PWM波形 如下图所示就是为CNT计数器和CCR这是输入捕获和输出比较公用的寄存器。当使用输入捕获时它就是捕获寄存器当使用输出比较时它就是比较寄存器。 本节介绍输出比较所以这块电路会比较CNT和CCR的值CNT计数自增CCR是我们给定的值 每个高级定时器和通用定时器都拥有4个输出比较通道(CH1-CH4)并且公用一个CNT计数器高级定时器的前3个通道(CH1-CH3)额外拥有死区生成和互补输出的功能用于驱动三相无刷电机
4.1 PWM波形 具体PWM介绍见跳转连接 下面给出高级定时器的前3个输出比较部分 最左边是CNT和CCR比较的结果OC1和OC1N就是两个互补端口分别控制上管和下管的导通和关闭 下面给出通用定时器的输出比较部分(高级定时器的第4个输出) 左边是通过输出模式控制器(TIMx_CCMR1寄存器控制)得到的CNT和CCR的比较结果输出OC1REF的高低电平随之REF可以映射到主模式的TRGO输出也可以通过极性选择(TIMx_CCER)到达输出使能电路最后输出OC1(CH1)引脚 下面给出输出模式控制器的执行逻辑—8种 有效电平—高电平、无效电平—低电平 PWM模式1和PWM模式2是相反逻辑 下面给出PWM的基本执行结构 左边是定时器配置的部分只需要比较CNT和CCR的值即可不需要配置中断。下图中ARR99CCR30 PWM的参数计算如下所示 PWM频率(计数器更新频率) CK_PSC/(PSC1)/(ARR1) PWM占空比 CCR/(ARR1) PWM分辨率 1/(ARR1)
4.2 舵机
4.3 直流电机
电机正接电机正转电机反接电机反转直流电机属于大功率器件GPIO口无法直接驱动需要配合电机驱动电路来操作(51单片机用达林顿管驱动电机)电机驱动模块有TB6612、L298N等下面介绍TB6612TB6612是一款双路H桥型的直流电机驱动芯片可以驱动两个直流电机并控制其转速和方向(不需要反接也可以反转) 下面就是H桥电路基本结构由两路推挽电路组成 左上和右下导通电流从左到右右上和左下导通电流从右到左可以实现电机正反转 下面给出TB6612的硬件电路具体引脚定义见下图 注意电机的驱动电源和逻辑电源驱动电源需要接大电源。STBY接VCC则电机工作接GND电机不工作。PWMA、AIN2、AIN1、AO1和AO2控制1路电机PWMB、BIN2、BIN1、BO1和BO2控制2路电机 如上图所示STBY接低电平时电机不转。IN1和IN2接相同电平电机不转。O1和O2接相同电平电机不转 IN1接低电平、IN2给高电平PWM给高电平(占空比不为0)电机反转。IN1接高电平、IN2给低电平PWM给高电平(占空比不为0)电机正转
五、TIM输入捕获IC
输入捕获模式下当通道输入引脚出现指定电平跳变时当前CNT的值将被锁存到CCR中(当前CNT的值读出来写入到CCR中)用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数每个高级定时器和通用定时器都拥有4个输入捕获通道可配置为PWMI模式(PWM输入模式)同时测量频率和占空比可配合主从模式触发实现硬件全自动测量 如下图所示为通道输入部分 输入引脚部分有个三输入的异或门输入接在了通道CH123端口当三个输入引脚的任何一个有电平翻转时输出引脚就产生一次电平翻转输出通过数据选择器到达输入捕获通道1。如果选择上面一个就是三个输入的异或值。如果选择下面一个异或门无效各用各的引脚。 一旦有边沿(自己配置)出现输入滤波和边沿检测器就会检测到边沿其中输出有两个通道选择TI1FP1和TI2FP2分别可以输入给两个通道。可以灵活切换后续捕获电路的输入也可以把一个引脚的输同时映射到两个捕获单元。 来到预分频器对前面的信号进行分预分频分频之后的触发信号就可以触发捕获电路进行工作了每来一个信号CNT的值就锁存到CCR中CNT的数值就可以记录两个上升沿的时间间隔 下面给出输入捕获/比较通道(细化) TI1(CH1)的引脚作为输入,fDTS是滤波器的采样时钟来源CCMR寄存器里的ICF控制滤波器参数输出的TI1F就是滤波后的信号经过边沿检测CC1P位选择极性最终得到TI1FP1触发信号通过数据选择器进入通道1后续的捕获电路CC1S配置数据选择器ICPS配置分频器CC1E位控制使能最终CNT的值就到了CCR里并且每捕获一次CNT的值就需要清零CNT以便于下一次捕获 TIFP1和TI1的边沿信号都可以通向从模式控制器触发从模式从模式里面可以自动完成CNT的清零 下图是主从触发模式的框图 它可以完成硬件的自动化操作。主模式可以将定时器内部的信号映射到TRGO引脚用于触发别的外设。从模式就是接收其他外设或者自身外设的一些信号用于控制自身定时器的运行也就是被别的信号控制。触发源选择就是选择从模式的触发信号源选择指定的一个信号得到TRGITRGI去触发从模式从模式在列表里选择一项操作去自动执行 (例让TI1FP1信号自动触发CNT清零触发源选择就可以选中TI1FP1从模式执行Reset的操作自动清零CNT实现硬件全自动测量) 5.1 频率测量 首先给出一个频率逐渐降低的方波信号如下图所示 测频法在闸门时间T内对上升沿进行计次得到N则频率为N/T。适合高频信号 闸门时间通常设置为1s在1s时间内对信号的上升沿计次从0开始每来一个上升沿(1个周期信号)计次1。在1s时间内来了多少个周期频率就是多少Hz。
测周法在两个上升沿内以标准频率fc计次得到N则频率为fc/N。适合低频信号 捕获两个上升沿之间持续的时间就是一个周期。用一个已知的标准频率fc来计次驱动计数器从一个上升沿开始计计数器从0开始一直计到下一个上升沿停止。计一个数的时间是1/fc计N个数时间就是N/fc所以频率就是fc/N
中界频率(频率多高算高多低算低选用哪种方法)测频法和测周法误差相等的频率点为根号fc/T 当待测信号频率小于中界频率时测周法误差小反之测频法误差小
5.2 输入捕获/PWMI基本结构
5.2.1 输入捕获基本结构 下图是输入捕获基本结构只使用了一个通道只能测量频率 时基单元配置好启动定时器CNT在预分频之后的时钟驱动下不断自增CNT就是测周法用来计时的标准频率72MHz/预分频系数。输入捕获通道1的GPIO口输入方波信号经过滤波器和边缘检测选择TI1FP1为上升沿触发当出现上升沿后CNT的当前计数值进入CCR1里同时触发源选择选中TI1FP1为触发信号从模式选中复位操作CNT自动清零(CNT的值先进CCR1里再清零)输入直连分频器不分频。 5.2.2 PWMI基本结构 下图是PWMI基本结构使用两个通道同时捕获一个引脚可以同时测量频率和占空比 TI1FP1同上面一样正常测量频率TI1FP2设置为下降沿触发通过交叉通道触发通道2的捕获单元。CCR2不触发CNT清零CCR1触发CNT清零所以CCR2就是高电平计数值CCR1是整个周期计数值所以CCR2/CCR1就是占空比 六、编码器
编码器接口可以接收增量(正交)编码器的信号根据编码器旋转产生的正交信号脉冲自动控制CNT自增或者自减从而指示编码器的位置、旋转方向和旋转速度每个高级定时器和通用定时器都拥有一个编码器接口两个输入引脚借用了输入捕获的通道1和通道2
6.1 正交编码器方向 下面给出正交编码器的正反转判断图编码器接口涉及逻辑就是首先把A相和B相的所有边沿作为计数器的计数时钟出现边沿信号时计数自增或自减是由另一相的状态来决定。就是当出现某一个边沿时我们判断另一相的高低电平
正转 反转 下面给出计数方向和编码器信号的关系(均不反相) 从表中看出当TI1计数时B相(TI2FP2)的边沿不计数只在A相(TI1FP1)的边沿计数正转向上计数反转向下计数 下面给出编码器TI1和TI2上计数的操作实例。毛刺部分是当一个信号电平不变的情况下另一个信号会使得计数器加减加减保持计数值不变过滤噪声 6.2 编码器电路和基本结构 如下图所示是编码器接口电路图编码器接口的两个输入端TI1FP1和TI2FP2分别接到编码器的A相和B相编码器的输出部分相当于从控制器去控制CNT的计数时钟和计数方向由上述表控制CNT自增还是自减 其中TI1FP1和TI2FP2对应在CH1和CH2通道输入滤波和边沿检测也需要使用后面的交叉、预分频、CCR寄存器与编码器无关 下面给出编码器的基本结构图 输入捕获的CH1和CH2通过GPIO接到编码器的AB相然后通过滤波器和极性选择产生TI1FP1和TI2FP2通向编码器接口编码器接口通过预分频器控制CNT计数器的时钟同时还根据编码器的旋转方向控制CNT的计数方向编码器正转时CNT自增编码器反转时CNT自减ARR设置成最大量程 七、代码编写
7.1 定时器库函数介绍 介绍一下定时器的部分库函数打开stm32f10x_tim.h
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//时基单元初始化函数
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//时基单元结构体变量赋一个默认值void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);//使能计数器
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//使能中断输出信号void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择ITRx其他定时器的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);//选择TIx捕获通道的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式1输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式2输入的时钟
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);//单独配置ETR引脚的预分频器、极性、滤波器参数void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//单独写预分频值
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//改变计数器的计数模式
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);//自动重装器预装功能配置
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);//给计数器写入一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);//给自动重装器写入值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);//获取当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);//获取当前预分频器值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);//获取定时器的中断标志位
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);//清除定时器的中断标志位
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);//获取挂起中断标志位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);//清除挂起中断标志位
//输出比较
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);//初始化输出比较单元
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);//高级定时器输出PWM时使能主输出
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);//给输出比较结构体默认值
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);//单独设置输出比较的极性
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);//高级定时器互补通道配置
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);//单独修改输出使能
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);//单独更改输出比较模式
//输入捕获
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);//输入捕获初始化四个通道共用一个函数
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);//输入捕获初始化快速配置两个通道
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);//输入捕获结构体赋初值
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择输入触发源TRGI-从模式触发源
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);//选择输出触发源TRGO-主模式
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);//选择从模式
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);//单独配置通道1的分频器
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);//读CCR的值
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);//单独写CCR寄存器值
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,int16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);//定时器编码器接口配置7.2 定时器定时中断代码 使用定时器中断首先需要初始化定时器 步骤如下RCC开启时钟(定时器基准时钟和整个外设时钟都会打开) — 选择时基单元的时钟源(内部时钟和外部时钟) — 配置时基单元(PSC、CNT、ARR) — 配置输出中断控制(使能中断输出允许更新中断输出到NVIC) — 配置NVIC(打开定时器中断的通道并分配优先级) — 运行控制(使能计数器) — 定时器中断函数 下面给出Timer.c每隔1s进入中断
#include stm32f10x.h // Device header/*
函数功能TIM2定时器中断(T 1s)
*/
void Timer_Init()
{//开启RCC时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//选择时基单元时钟-内部时钟一般默认就是内部时钟TIM_InternalClockConfig(TIM2);//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; //1分频TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //向上计数//时基单元关键寄存器参数对72MHz进行7200分频得到10k的计数频率计10000个数字就是1s - 1Hz 1sTIM_TimeBaseInitStructure.TIM_Period 10000-1; //ARR自动重装器的值TIM_TimeBaseInitStructure.TIM_Prescaler 7200-1; //PSC预分频器的值 72MHz/(PSC1)/(ARR1)TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; //重复计数器的值高级定时器专用TIM_TimeBaseInit(TIM2,TIM_TimeBaseInitStructure);//使能更新中断TIM_ClearFlag(TIM2,TIM_FLAG_Update); //避免刚初始化完就进入中断TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能更新中断函数//NVIC配置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority 1;NVIC_Init(NVIC_InitStructure);//启动定时器TIM_Cmd(TIM2,ENABLE); //使能计数器
}/*
函数功能定时器TIM2中断函数
*/
//void TIM2_IRQHandler()
//{
// if(TIM_GetITStatus(TIM2,TIM_IT_Update) SET)
// {
//
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
// }
//}
7.3 定时器外部时钟申请 使用定时器中断首先需要初始化定时器 步骤如下RCC开启时钟(定时器基准时钟和整个外设时钟都会打开) — 选择时基单元的时钟源(内部时钟和外部时钟) — 配置时基单元(PSC、CNT、ARR) — 配置输出中断控制(使能中断输出允许更新中断输出到NVIC) — 配置NVIC(打开定时器中断的通道并分配优先级) — 运行控制(使能计数器) — 定时器中断函数 下面给出Timer.c对射红外传感器挡一次计数器加1计到第10次进入中断
#include stm32f10x.h // Device header/*
函数功能外部时钟(对射红外传感器)定时器中断
*/
void Timer_Init()
{//开启RCC时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);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);//选择时基单元时钟-ETR引脚的外部时钟模式2配置TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0F); //滤波器0x00-0x0F给最大比较准确//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; //1分频TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //向上计数//时基单元关键寄存器参数TIM_TimeBaseInitStructure.TIM_Period 10-1; //ARR自动重装器的值 计0-9 10个数字TIM_TimeBaseInitStructure.TIM_Prescaler 1-1; //外部时钟提供不是内部的72MHz如果分频更大则需要触发多次计数器才会加1TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; //重复计数器的值TIM_TimeBaseInit(TIM2,TIM_TimeBaseInitStructure);//使能更新中断TIM_ClearFlag(TIM2,TIM_FLAG_Update); //避免刚初始化完就进入中断TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//NVIC配置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority 1;NVIC_Init(NVIC_InitStructure);//启动定时器TIM_Cmd(TIM2,ENABLE);
}uint16_t Timer_GetCounter()
{return TIM_GetCounter(TIM2);
}//void TIM2_IRQHandler()
//{
// if(TIM_GetITStatus(TIM2,TIM_IT_Update) SET)
// {
//
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
// }
//} 对于外部时钟源来控制定时器需要了解本质上是将外部引脚给的方波信号作为时钟来完成功能以对射红外为例预分频数值越大方波信号被分频导致频率小周期变大所以挡一次计数器不会加1需要多挡几次达到周期时间计数器才会加1 外部中断和使用外部时钟源的定时器中断也有不同。前者是直接在GPIO口获取到中断电平要求执行操作后者是达到定时器重装值计数器溢出触发中断执行操作
7.4 定时器输出比较代码 定时器输出PWM基本步骤如下RCC开启时钟(TIM和GPIO) — 时钟源选择加配置时基单元 — 配置输出比较单元(CCR的值、输出比较模式、极性选择、输出使能参数 — 配置GPIO(复用推挽输出) — 运行控制(启动计数器) 同一个定时器可以使用多个通道输出多个PWM多个通道公用一个计数器所以它们的频率是相同的占空比由各自的CCR决定计数器更新所有PWM同时跳变相位同步
7.4.1 PWM驱动LED呼吸灯 LED正极接在PA0(CH1)口负极接GND占空比越大LED越亮 选取GPIO模式的时候需要选取复用推挽输出才可以将引脚的控制权交给片上外设PWM才可以通过引脚输出 下面给出PWM.c
#include stm32f10x.h // Device headervoid PWM_Init()
{//开启RCC时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOA初始化(CH1)GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; //复用推挽输出模式(在此模式下引脚的控制权才可以交给片上外设PWM才可以通过引脚输出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; //1分频TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //向上计数//时基单元关键寄存器参数---频率1000HzTIM_TimeBaseInitStructure.TIM_Period 100-1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler 720-1; //PSC TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; //重复计数器的值高级定时器专用TIM_TimeBaseInit(TIM2,TIM_TimeBaseInitStructure);//配置输出比较单元(PA0是OC1输出口,使用OC1初始化函数)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(TIM_OCInitStructure); //给结构体赋初始值TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; //设置输出比较模式 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; //设置输出比较极性TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; //设置输出使能TIM_OCInitStructure.TIM_Pulse 0; //CCR 占空比为0%TIM_OC1Init(TIM2,TIM_OCInitStructure);//启动定时器TIM_Cmd(TIM2,ENABLE); //使能计数器PWM波形通过PA0输出
}/*
函数功能单独设置CCR的值调整占空比
*/
void PWM_SetCompare(uint16_t Compare)
{TIM_SetCompare1(TIM2, Compare);
}主函数中调用可调CCR函数调整占空比
while(1){for(i 0;i100;i){PWM_SetCompare(i); //逐渐变亮Delay_ms(10);}for(i 0;i100;i){PWM_SetCompare(100-i); //逐渐变暗Delay_ms(10);}}7.4.2 PWM驱动舵机 舵机信号输出线接在PA1(CH2)口 下面给出PWM.h我们需要设置周期为20ms高电平时间对应从0.5ms-2.5ms。ARR和PSC决定周期CCR和ARR决定占空比
#include stm32f10x.h // Device headervoid PWM_Init()
{//开启RCC时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOA初始化(CH1)GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; //复用推挽输出模式(在此模式下引脚的控制权才可以交给片上外设PWM才可以通过引脚输出GPIO_InitStructure.GPIO_Pin GPIO_Pin_1;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; //1分频TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //向上计数//时基单元关键寄存器参数---频率50Hz 72000000/72/20000 50HZ 周期为20msTIM_TimeBaseInitStructure.TIM_Period 20000-1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler 72-1; //PSC TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; //重复计数器的值高级定时器专用TIM_TimeBaseInit(TIM2,TIM_TimeBaseInitStructure);//配置输出比较单元(PA0是OC1输出口,使用OC1初始化函数)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(TIM_OCInitStructure); //给结构体赋初始值TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; //设置输出比较模式 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; //设置输出比较极性TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; //设置输出使能TIM_OCInitStructure.TIM_Pulse 0; //CCR 占空比为0%TIM_OC2Init(TIM2,TIM_OCInitStructure);//启动定时器TIM_Cmd(TIM2,ENABLE); //使能计数器PWM波形通过PA0输出}/*
函数功能单独设置CCR的值调整占空比 20000对应20msCompare 500--0.5ms 2500--2.5ms
*/
void PWM_SetCompare(uint16_t Compare)
{TIM_SetCompare2(TIM2, Compare);
} 将舵机封装成函数并在主函数调用实现按键每按一下舵机转动45°并在OLED显示屏上显示出度数
#include stm32f10x.h // Device header
#include PWM.hvoid Servo_Init()
{PWM_Init();
}/*
0°x -- 500y
180° -- 2500
y kx bb 500
180k500 2500
y x/180*2000 500函数功能输入角度舵机旋转固定角度
形式参数转动的角度
*/
void Servo_SetAngle(float Angle)
{PWM_SetCompare(Angle/180*2000 500);
}uint8_t KeyNum;
float Angle;int main(void)
{OLED_Init();Servo_Init();Key_Init(); //这里不初始化舵机会乱转Servo_SetAngle(0);OLED_ShowString(1, 1, Angle:);while(1){KeyNum Key_GetNum();if(KeyNum 1){Angle 45;if(Angle 180)Angle 0;Servo_SetAngle(Angle);}OLED_ShowNum(1, 7, Angle,3);}
}7.4.3 PWM驱动直流电机 驱动直流电机需要通过电机驱动电路这里使用的TB6612模块 接线如下注意的是PWM输出口接的是PA2口对应的是TIM2的通道3需要更改PWM.h里的输出通道 下面给出Motor.c
#include stm32f10x.h // Device header
#include PWM.hvoid Motor_Init()
{PWM_Init();//电机控制方向的GPIO口 PA4 PA5RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,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);
}/*
函数功能设置电机转动方向和速度
*/
void Motor_SetSpeed(int8_t Speed)
{if(Speed 0) //正转{GPIO_SetBits(GPIOA,GPIO_Pin_4); //IN1-PA4高GPIO_ResetBits(GPIOA,GPIO_Pin_5); //IN2-PA5低PWM_SetCompare(Speed);}else //反转{GPIO_ResetBits(GPIOA,GPIO_Pin_4); //IN1-PA4低GPIO_SetBits(GPIOA,GPIO_Pin_5); //IN2-PA5高PWM_SetCompare(-Speed);}
}主函数调用使用按键去控制电机的转速将速度显示在OLED屏幕上
int main(void)
{OLED_Init();Key_Init(); //必须初始化不初始化的情况下会乱转Motor_Init();OLED_ShowString(1, 1, Speed:);while(1){KeyNum Key_GetNum();if(KeyNum 1){Speed 20;if(Speed 100)Speed 0;Motor_SetSpeed(Speed);}OLED_ShowNum(1, 7, Speed,3);}
}
7.5 定时器输入捕获代码 信号输入是由自己生成所以直接连接单片机的GPIO口将PA6和PA0接起来PA0输出方波信号PA6检测输入捕获
7.5.1 输入捕获模式测频率 首先使用定时器配置好PA0(TIM2_CH1)口输出一个方波信号这里是产生一个1000Hz占空比为50%的方波信号 然后再配置PA6(TIM3_CH1)口实现输入捕获具体步骤如下配置RCC时钟(GPIO和TIM) — GPIO初始化(输入模式) — 配置时基单元 — 配置输入捕获单元(滤波器极性、直连还是交叉、分频器等) — 从模式触发源并执行Reset操作 — 开启定时器 当要读取最新一个周期频率时读取CCR按照fc/N得到 首先给出PWM.c此函数内主要是通过PA0口输出一个模拟的方波信号就是利用定时器去输出一个波形
#include stm32f10x.h // Device headervoid PWM_Init()
{//开启RCC时钟PA0口输出方波信号RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOA初始化(CH1)GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; //复用推挽输出模式(在此模式下引脚的控制权才可以交给片上外设PWM才可以通过引脚输出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; //1分频TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //向上计数//时基单元关键寄存器参数---调节PSC来调节频率TIM_TimeBaseInitStructure.TIM_Period 100-1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler 720-1; //PSC TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; //重复计数器的值高级定时器专用TIM_TimeBaseInit(TIM2,TIM_TimeBaseInitStructure);//配置输出比较单元(PA0是OC1输出口,使用OC1初始化函数)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(TIM_OCInitStructure); //给结构体赋初始值TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; //设置输出比较模式 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; //设置输出比较极性TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; //设置输出使能TIM_OCInitStructure.TIM_Pulse 0; //CCR占空比为0%TIM_OC1Init(TIM2,TIM_OCInitStructure);//启动定时器TIM_Cmd(TIM2,ENABLE); //使能计数器PWM波形通过PA0输出}/*
函数功能单独设置CCR的值调整占空比
*/
void PWM_SetCompare(uint16_t Compare)
{TIM_SetCompare1(TIM2, Compare);
}
/*
函数功能单独设置PSC的值调整频率
*/
void PWM_SetPrescaler(uint16_t Prescaler)
{TIM_PrescalerConfig(TIM2, Prescaler,TIM_PSCReloadMode_Immediate);
} 然后给出IC.c此函数内主要是通过PA6(TIM3_CH1)口输入捕获到PA0口的方波信号
#include stm32f10x.h // Device headervoid IC_Init()
{//开启RCC时钟P6口输入方波TIM2输出PWM波,TIM3输入捕获RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOA初始化TIM3的CH1为PA6GPIO_InitTypeDef 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(TIM3);//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; //1分频TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //向上计数//时基单元关键寄存器参数TIM_TimeBaseInitStructure.TIM_Period 65536-1; //防止计数溢出给最大TIM_TimeBaseInitStructure.TIM_Prescaler 72-1; //标准频率1MHzTIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; //重复计数器的值高级定时器专用TIM_TimeBaseInit(TIM3,TIM_TimeBaseInitStructure);//初始化输入捕获单元TIM_ICInitTypeDef TIM_ICInitStructure;TIM_ICInitStructure.TIM_Channel TIM_Channel_1; //TIM3的CH1TIM_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);//主从触发源选择TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1); //TIM3 ,TI1FP1TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset); //从模式 Reset//启动定时器TIM_Cmd(TIM3,ENABLE);
}
/*
函数功能读取最新一个周期的频率f fc/N 72M/PSC1/N(NCCR)
*/
uint32_t IC_GetFreq()
{return 1000000 / (TIM_GetCapture1(TIM3)1); //面向结果编程1
}在主函数中首先设置好PA0口的输出波形再捕获PA6的输入频率
//PA0输出频率1000Hz占空比50%的方波信号PWM_SetPrescaler(720 - 1); //f 72M / PSC1 / ARR1 1000Hz PWM_SetCompare(50); //D CCR / ARR1 50%while(1){OLED_ShowNum(1,6,IC_GetFreq(),5);}7.5.2 PWMI模式测频率和占空比 同时测频率和占空比时与上述不同的部分是要配置好两个通道之后用CCR2/CCR1得到占空比大小PWM.c没有变化 下面给出IC.c文件
#include stm32f10x.h // Device headervoid IC_Init()
{//开启RCC时钟P6口输入方波TIM2输出PWM波RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOA初始化TIM3的CH1为PA6GPIO_InitTypeDef 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(TIM3);//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; //1分频TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //向上计数//时基单元关键寄存器参数TIM_TimeBaseInitStructure.TIM_Period 65536-1; //防止计数溢出给最大TIM_TimeBaseInitStructure.TIM_Prescaler 72-1; //标准频率1MHzTIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; //重复计数器的值高级定时器专用TIM_TimeBaseInit(TIM3,TIM_TimeBaseInitStructure);//初始化输入捕获单元TIM_ICInitTypeDef TIM_ICInitStructure;TIM_ICInitStructure.TIM_Channel TIM_Channel_1; //TIM3的CH1TIM_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_PWMIConfig(TIM3,TIM_ICInitStructure); //直接配置第二通道(初始化一个通道即可另一个通道会直接初始化成相反的配置)下降沿触发交叉//主从触发源选择TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1); //TIM3 ,TI1FP1TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset); //从模式 Reset//启动定时器TIM_Cmd(TIM3,ENABLE);
}
/*
函数功能读取最新一个周期的频率f fc/N 72M/PSC1/N(NCCR1)
*/
uint32_t IC_GetFreq()
{return 1000000 / (TIM_GetCapture1(TIM3)1); //面向结果编程1
}
/*
函数功能获取PWMCCR2/CCR1
*/
uint32_t IC_GetDuty()
{return (TIM_GetCapture2(TIM3)1)*100/(TIM_GetCapture1(TIM3)1);
}同样在主函数中调用如下所示
//PA0输出频率1000Hz占空比50%的方波信号PWM_SetPrescaler(720 - 1); //f 72M / PSC1 / ARR1 1000Hz PWM_SetCompare(50); //D CCR / ARR1 50%while(1){OLED_ShowNum(1,6,IC_GetFreq(),5);OLED_ShowNum(2,6,IC_GetDuty(),3);}7.6 编码器接口测速 本节使用旋转编码器AB相选择接到PA6(TIM3_CH1)和PA7(TIM3_CH2)输入引脚 代码步骤如下开启RCC时钟(GPIO和TIM时钟) — 配置GPIO(PA6和PA7输入模式) — 配置时基单元 — 配置输入捕获单元(滤波器和极性) — 配置编码器接口模式 — 启动定时器 代码主要功能是测旋转编码器旋转的速度利用TIM2去每隔1s读取速度显示在OLED上
#include stm32f10x.h // Device headervoid Encoder_Init()
{//开启RCC时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOA初始化TIM3的CH1\CH2为PA6\PA7GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; //输入模式上拉输入GPIO_InitStructure.GPIO_Pin GPIO_Pin_6|GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);//配置时基单元--编码器时钟驱动计数器TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; //1分频TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //向上计数//时基单元关键寄存器参数TIM_TimeBaseInitStructure.TIM_Period 65536-1; //最大量程TIM_TimeBaseInitStructure.TIM_Prescaler 1-1; //不分频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; //TIM3的CH1-PA6TIM_ICInitStructure.TIM_ICFilter 0xF; //滤波//TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; //高低电平极性不反转(不反相)TIM_ICInit(TIM3,TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel TIM_Channel_2; //TIM3的CH2-PA7TIM_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_Cmd(TIM3,ENABLE);
}int16_t Encoder_Get() //有符号
{int16_t Temp;Temp TIM_GetCounter(TIM3);TIM_SetCounter(TIM3,0); //CNT清零return Temp;
} 在主函数利用中断1s检测一次
int16_t Speed;int main(void)
{OLED_Init();Timer_Init();Encoder_Init();OLED_ShowString(1,1,Speed:);while(1){OLED_ShowSignedNum(1,7,Speed,5);}
}/*
函数功能定时器TIM2中断函数 1s
*/
void TIM2_IRQHandler()
{if(TIM_GetITStatus(TIM2,TIM_IT_Update) SET){Speed Encoder_Get();TIM_ClearITPendingBit(TIM2,TIM_IT_Update);}
}