赞
踩
该部分主要进行介绍一些与定时器相关的基本知识和原理,以便对定时器有更好的理解。
想要很好的利用定时器来进行程序的设计,我们必须要明确一个问题——什么是定时器,简单理解,定时器就是一个计数器,对输入的时钟信号进行计数,当达到设定值的时候会产生一个中断,然后将计数器的初始值重置,继续进行下一轮计数。
在stm32中,定时器分为三类:基本定时器、通用定时器和高级定时器。这三种定时器有各自的应用场景,而且复杂度也有很大不同,其中基本定时器最为简单,高级定时器最为复杂,如下将对三种定时器进行简单介绍。
上图为基本定时器的原理图(出自芯片手册)
基本计时器可以实现16位自动重装累加计数器、可产生中断,并且他还有一个特殊的功能:主模式触发DAC的同步电路。
预分频器(16位寄存器):该寄存器实现了对输入的时钟信号的分频,输入的时钟信号频率一般都会很大,如果对该信号直接计数,由于输入的时钟信号周期很小,就会导致计数的频率过快,无法达到想要的时间,预分频器的作用就是将时钟信号进行分频,加长一个周期的时间。
当预分频系数设置为0时,相当于不分频(也可理解为1分频),这样以此类推预分频系数设置为1时,为2分频。因此有:
分频 = 预分频系数 + 1
计数器(16位寄存器):该寄存器可以对预分频后的时钟信号进行计数,当时钟信号产生一个上升沿的时候,计数器就会加一(减一)。
自动重装载寄存器(16位寄存器):该寄存器用来存储目标值。
在定时器中,预分频器、计数器和自动重装寄存器构成了最基本的计数计时电路,因此这一部分被称为时基单元。
基本计数器尽可以选择内部时钟(CK_INT)作为时钟源信号,
在程序运行过程中,内部时钟(CK_INT)作为时钟源信号,然后由预分频器对该信号进行分频,计数器不断增加,当计数器的值增加到与自动重装载寄存器相等时,计数器的值便会清零,同时产生更新中断和更新事件。
(至于更加高级的其他用法,这里不做介绍)
上图为通用定时器的原理图(出自芯片手册)
拥有基本定时器的全部功能(主模式触发DAC的同步电路除外),并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
时钟源触发及主从触发模式结构:该部分用来选择计数的输入时钟源信号,以及主从触发模式来控制定时器级联等功能。
时基单元:该部分与基本定时器基本形同。
输出比较电路:可以用于输出PWM波形。
输入捕获电路:可以用来测量输入方波的一些基本信息。
通用定时器的时钟源可以是系统时钟、也可以是上一级定时器的TRGO输入到ITR等等,更高级的用法这里先不进行介绍
通用定时器有三种计数模式:
1、先上计数模式:这种计数方法就是基本定时器所采用,计数器从零开始不断增加,当其值与自动重装载值相同时,便将计时器置零,产生中断,开始下一轮计数。
2、向下计数模式:计数器从自动重装载值开始向下计数到零,当计数器为零时,将计时器重置位初始值,产生中断,开始下一轮计数。
3、中央对齐模式(向上/向下计数):计数器先从零开始向上自增,当达到重装载值的时候申请中断,然后从重装载值开始向下自减,当计时器达到零的时候申请中断,然后开始下一轮计数。
该定时器具备通用定时器的所有功能,可以把他当作通用定时器来使用,由于高级定时器的特有功能是针对于更专业的工业电机控制而设计的,增加了可编程死区互补输出、重复计数器、带刹车功能。(笔者也不会┭┮﹏┭┮)
在stm32f103c8t6中,一共有四个定时器资源——一个高级定时器(TIM1)和三个通用定时器(TIM2)
由上述原理可知,定时器溢出的计算公式:
Tout(溢出时间) = (分频系数+1)*自动重装载值/时钟频率
打开CubeMX。新建工程,然后选择stm32f103c8t6芯片,在System Core中选择RCC,将HSE和LSE均选择Crystal/Ceramic Resonator作为时钟源。
选择SYS,将其中的Debug改为Serial Wire,这样可以多次进行程序下载,方便调试和下载程序。
配置时钟源(Clock Source)为内部时钟源(Internal Clock)
根据溢出时间的计算公式,选择合适的预分频值和自动重装载值,我这里选择预分频值为7200-1,重装载值为10000,即1s溢出一次。
对于auto-reload preload(自动重装载)在这个实验中开启与否都一样,因为当定时器溢出后会进行一次自动重装载。
选择NVIC Setting,使能更新中断(update interrupt)。
若使用的是其他定时器(通用定时器),则需要使能global interrupt
设置HCLK为72MHz,因为TIM1是连接在APB1Timer clock上的,因此TIM1时钟源信号为72MHz。
输入工程名称选择IDE为MDK-ARM
选择Code Generator勾选Generate perioheral initialization as a pair of ‘.c/.h’ files per peripheral。生成文件即可。
HAL_TIM_Base_Start_IT(&htim1);//使能定时器中断
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == htim1.Instance)//用于判断是哪一个定时器产生的中断
{
//函数体
}
}
用单片机制作一个简单的定时器,并在OLED上显示。
if(flag == 0)
{
flag = 1;
int t;
int n = num;
for(t = 0; n > 0; t++)
{
n/=10;
}
OLED_ShowNum(10, 10, num,t,8, 1);
OLED_Refresh();
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == htim1.Instance)//用于判断是哪一个定时器产生的中断
{
//函数体
flag = 0;
num++;
}
}
由于人工误差(无法使手机计时器与单片机同时开启),导致有1s的误差值。
按键时最简单的输入设备,按下时导通,松手后断开。
注:该图片参考江大自化协的视频(STM32入门教程)
该图中第一种方法,当按键未被按下时,引脚PA0处于悬空状态,会出现引脚电压不稳定的状态,因此需要把PA0设置为上拉输入的状态,这也是最简单的按键电路。
该图第二种方法就是使用外部硬件进行电压的上拉,这种情况下就可以将PA0的输入方式配置为浮空输入或者是上拉输入,此时如果将PA0配置为上拉输入的话,高电平就会更加稳定一些。
第三、四种方法,分别对应于一、二种方法,不过此时需要将PA0配置成下拉输入。
这里我选用的是图一的连接方法,将输入模式选择为Pull-up(上拉输入),
然而真实的按键不会像理想中的一样,按下立刻为高电平,没按下的时候恒为低电平,在按键按下和松开的过程中,难免会有抖动。
//当检测到按键按下之后,进入函数
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10) == GPIO_PIN_RESET){
HAL_Delay(20);//延迟20ms,消去由于抖动引起的误触,当20ms后依然为低电平,可认为按键是被按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10) == GPIO_PIN_RESET){
//用while函数消去按键持续按下的时间
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10) == GPIO_PIN_RESET);
//函数体
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。