无人问津也好,技不如人也罢,都应静下心来,去做该做的事。
最近在学STM32,所以也开贴记录一下主要内容,省的过目即忘。视频教程为江科大(改名江协科技),网站jiangxiekeji.com
现在开始上难度,STM32功能最强大、结构最复杂的外设——定时器,分四期介绍。
本期介绍最基础的定时功能,也就是定一个时间,然后让定时器每隔这个时间产生一个中断。
下一期介绍定时器输出比较的功能,常用产生PWM波驱动电机。
再下一期介绍定时器输入捕获功能,常用测量方波频率。
最后介绍定时器的编码器接口,更方便读取正交编码器的输出波形,常用编码电机测速。
TIM定时器简介
计数器:用来执行计数定时的一个寄存器,每来一个时钟,计数器加1。
预分频器:可以对计数器的时钟进行分频,让这个计数更加灵活。
自动重装寄存器:计数的目标值,就是我想要计多少个时钟申请中断。
以上三个都是16位的,也就是最大定时59.65秒,定时器还支持级联,59.65秒×65536(2的16次方)
主要介绍通用定时器
无论是高级定时器,还是通用定时器,还是基本定时器,它们的内部基准时钟都是72MHz
定时中断基本结构
定时器类型
高级定时器:重复计数器、死区生成、互补输出、刹车输入等这些功能,主要是为了三相无刷电机的驱动设计的,比如四轴飞行器、电动车的后轮、电钻等。本系列不会涉及到。
主要介绍通用定时器,
无论是高级定时器,还是通用定时器,还是基本定时器,它们的内部基准时钟都是72MHz
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4,没有基本定时器。
基本定时器
定时中断功能
基本定时器只能选择内部时钟、只有向上计数模式
如果PSC预分频器写0,这时候输出频率=输入频率=72MHz
如果PSC预分频器写1,即二分频,这时候输出频率=输入频率/2=36MHz
如果PSC预分频器写2,即三分频,这时候输出频率=输入频率/3=24MHz
以此类推,即实际分频系数=预分频器的值+1,这个预分频器是16位的,所以最大值可以写65535,也就是65536分频
计数时钟每来一个上升沿,CNT计数器的值就加1,可以从0加到65535,然后回到0重新开始。
当计数值等手自动重装值时,也就是计时时间到了,会清零计数器。这时也需要和外部中断一样选择触发中断还是事件,在定时器里叫更新中断和更新事件。
主模式触发DAC功能
它能让内部的硬件在不受程序的控制下实现自动运行,特定场景下可极大减轻CPU负担。
这个用途就是在我们使用DAC的时候,可能会用DAC输出一段波形,那就需要每隔一段时间来触发一次DAC,让它输出下一个电压点,如果用正常的思路来实现,就是先设置一个定时器产生中断,每隔一段时间在中断程序中调用代码手动触发一次DAC转换,然后DAC输出。这样也没问题,但会使主程序频繁被中断。
基于以上这种情况,定时器设计了一个主模式,使用这个主模式可以把定时器的更新事件映射到这个触发输出TRGO(Trigger Out)的位置,然后TRGO直接接到DAC的触发转换引脚上。
通用定时器
计数模式
通用定时器和高级定时器还支持向下计数模式和中央对齐模式
向上计数模式:从0开始,往上自增,加到自动重装值,然后回到0,申请中断。
向下计数模式:从重装值开始,向下自减,减到0之后,回到重装值同时申请中断。
中央对齐模式:从0开始,先向上自增,计到重装值,申请中断,然后再向下自减,减到0,再申请中断,然后开始下一轮。
时钟源
通用定时器的时钟源不仅可以选择内部的72MHz时钟 ,还可选择外部时钟,
如果要使用外部时钟,首选ETR引脚外部时钟模式2的输入,最简单、直接
第一个外部时钟就是来自TIMx_ETR引脚上的外部时钟,称为外部时钟模式2
第二个外部时钟是TRGl(Trigger In),它主要是用作触发输入来使用的。称为外部时钟模式1
定时器的级联就是使用该模式
总结一下就是,外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚的边沿、
CH1引脚和CH2引脚
高级定时器
时序图
这一部分建议直接看视频讲解
预分频器时序
预分频寄存器实际上是有两个,预分频器为了防止计数中途更改数值造成错误,设计了缓冲寄存器或影子寄存器
一个是预分频控制寄存器,供我们读写用的,它并不直接决定分频系数;
另外还有一个预分频缓冲器或者说是影子寄存器,这个是真正起作用的寄存器
在一个计数周期内,前半部分和后半部分的频率不一样,这里计数到一半,计数频率突然就被改变,因此设计这个缓冲寄存器。这样。当我在计数计到一半的时候改变了分频值,这个变化并不会立刻生效,而是会等到本次计数周期结末时,产生更新事件,预分频寄存器的值才会被传递到缓冲寄存器里面去,才会生效。
计数器时序图
计数器的这个ARR自动重装寄存器,也是有一个缓冲寄存器的,并且这个缓冲寄存器是用还是不用,是可以自己设置的。
计数器在这个时钟每个上升沿自增,当增到0036的时候,发生溢出,再来一个上升沿,计数器清零,计数器溢出,产生一个更新事件脉冲,另外还会置一个更新中断标志位UIF,这个标志位只要置1,就会去申请中断。然后中断响应后,需要在中断程序中手动清零。
用72MHz/(PSC+1)/(ARR+1)就能得到溢出频率,再取倒数就能知道溢出时间。
计数器无预装时序
通过设置这个ARPE位,就河以选择是否使用预装功能
计数器有预装时序
ARPE=1时,就使用预装功能
RCC时钟树
ST公司已经帮我们写好了配置个时钟树的SystemInit函数,首先它会启动内部时钟,选择内部8MHz为系统时钟,暂时以内部8MHz的时钟运行,然后再启动外部时钟,配置外部时钟进入PLL锁相进行倍频,8MHz倍频9倍,得到72MHz。等到锁相环输出稳定后,选择锁相环输出为系统时钟,这样就把系统时钟由8MHz切换为了72MHz。
如果你的外接8MHz晶振出了问题,系统时钟就无法切换到72MHz,那它就会以内部的8MHz运行,8MHz相比较72MHz,大概就慢了10倍,比如你的delay函数慢了10倍。
下图的左边为时钟产生电路,右边为时钟分配电路
时钟产生电路有四个震荡源:内部的8MHz高速RC振荡器、
外部的4-16MHz高速石英晶体振荡器(一般都是外接8MHz晶振,比内部的8MHz稳定)、
外部的32.768KHz低速晶振(一般是给RTC提供时钟的)、
内部的40KHz低速RC振荡器(这个可以给看门狗提供时钟)
上面两个高速时钟是给系统提供的,我们的AHB、APB2、APB1的时钟都是来源于这两个高速晶振
本期主要两个实验
定时器定时中断
可以看到OLED上显示了一个数字Num,并且每秒自动加1。这个就是用了定时中断的功能,定时器使用内部时钟定了1秒的时间,每隔1秒申请一下中断,然后在中断函数里执行Num++ ,最后在OLED上显示Num++。
定时器外部时钟
使用外部时钟来驱动定时器,我们可以在定时器指定的外部引脚上,输入一个方波信号,来提供定时器计算的时钟,现在这里暂时用这个对射式红外传感器来手动模拟一个外部时钟,用挡光片,依次遮挡、移开、遮挡、移开,提供一个方波。可以看到这个OLED上下面这个CNT就是定时器中计数器的值,每遮挡、移开一次,计数器加1,然后计数器记到9后,自动清0。同时申请中断,执行Num++。