STM32速成笔记(6)—定时器

发布时间:2024-08-26  

一、什么是定时器

关于什么是定时器,简单来讲,就是是用来定时的。STM32F103ZET6有两个基本定时器TIM6和TIM7,四个通用定时器TIM2~TIM5和两个高级定时器TIM1,TIM8。每一个定时器都是完全独立的,不共享任何资源。


根据中文参考手册介绍,基本定时器最为简单,类似于51单片机的定时器。通用定时器在基本定时器的基础上增加了输入捕获和输出比较功能。高级定时器相比通用定时器,又增加了可编程死区互补输出,重复计数器等功能。



STM32F103ZET6的通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。


这里介绍一下对于定时器的个人理解。定时器的定时原理实际可以理解为按照固定的频率数数。按照固定频率就说明定时器一定要有输入时钟。比如输入为一个1KHz的时钟,那么数一个数的时间就是1ms。另外,数数也不是无限地数,数值有一个上限。可以规定是从0开始数到上限值,还是从上限值数到0。而且每次数到头,需要重新开始。比如,需要控制灯亮200ms。那么只需要在点亮LED之后,等到数到200时熄灭即可。当数到上限值或者数到0时,重新开始数。


二、定时器有什么用

定时器有许多用途,以通用定时器为例。它可以测量输入信号的脉冲宽度,产生PWM波。此外定时器也可以用于触发ADC采集,按键检测等方面。


中文参考手册介绍如下

图片

中文参考手册对于通用定时器功能的描述


三、通用定时器详细介绍

速成选手可以线跳过这一部分,直接看下面,后来再返回来仔细看。


3.1 时钟来源

根据中文参考手册,通用定时器的时钟来源有四个。


• 内部CK_INT

• 外部触发时钟输入TIMx_ETR(外部时钟模式2)

• 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器

• 外部引脚输入(外部时钟模式1)

image.png

通用定时器时钟来源

通过配置TIMx_SMCR寄存器来选择,关于寄存器这里就不再详细介绍了,大家可以去看中文参考手册。

根据中文参考手册关于时钟的介绍,通用定时器挂接在APB1总线。对于APB1总线的时钟如下

241ba0795aefe016da4a778bea434263_wKgaomU3IEWAMxRbAAGlCKJPVGw813.jpg

APB1时钟介绍


如果APB1的预分频系数为1,那么通用定时器的输入时钟频率为36MHz,否则为72MHz。但是通常APB1总线的预分频系数我们不会设置成1,所以通用定时器的时钟频率为72MHz。


3.2 预分频器,计数器,自动重装载寄存器

98b87fc31cb7a23a890cd009ff7a94f3_wKgaomU3IEWAM-a-AAOyNzo4fpc971.jpg

预分频器,计数器和自动重装载寄存器


3.2.1 预分频器

预分频器是对时钟进行分频,范围是1~65536。比如通用定时器输入时钟频率为72MHz,此时,将预分频值设置为72,那么最终计数时的时钟频率为72MHz / 72 = 1MHz。


3.2.2 计数器

计数器就是用来计数的,计数值取值范围是0~65535。有三种计数方式:向上计数,向下计数,中央对齐模式(向上/向下计数)。


在向上计数模式中,计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件。


在向下计数模式中,计数器从自动装入的值(TIMx_ARR计数器的值)开始向下计数到0,然后从自动装入的值重新开始并且产生一个计数器向下溢出事件。


在 中央对齐模式 ,计数器从0开始计数到自动加载的值(TIMx_ARR寄存器)−1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器下溢事件;然后再从0开始重新计数。


3.2.3 自动重装载寄存器

比如,选择向下计数模式,初始值为2000。当计数到0时,会再次从2000开始向下计数。这就叫重装载。但是实际起作用的并不是这里的自动重装载寄存器,而是影子寄存器。关于影子寄存器这里就不再做介绍了,大家可以自行了解。


3.3 触发控制器

从图中的右上角可以注意到,有一个触发控制器。它可以用来触发一些外设,比如触发ADC采集,也可以用来给其他定时器提供时钟。


四、PWM

4.1 什么是PWM

PWM(脉冲宽度调制),它是一种利用微处理器的数字输出来对模拟电路进行控制的技术,也可以理解为是对模拟信号电平进行数字编码的方法。PWM可被应用于电机驱动,调光,通信等方面。


4.2 什么是占空比

一个PWM是有固定频率的,也就意味着周期一定,一个周期内有效电平持续时间占整个周期的比例可以称为占空比。比如一个周期100ms,其中50ms持续为有效电平,那么占空比就是50%。正是通过调节占空比,来调节电机转速,或者用不同占空比代表不同信号,用于通信。


4.3 STM32F1 PWM介绍

STM32F1系列单片机,除了基本定时器TIM6和TIM7外,都可以产生PWM输出。其中高级定时器TIM1和TIM8可以同时产生高达7路PWM输出。PWM输出其实就是对外输出占空比可调的方波,信号频率由自动重装载寄存器ARR的值决定,占空比由比较寄存器CCR的值决定。假设高电平为有效电平,见下图。ARR决定了周期(频率),CCR调节占空比。

6777141bfc8a735dc3d5aa7b7303a9aa_wKgaomU3IESACN10AABtxP1939Y631.jpg

PWM示意图


根据中文参考手册介绍,STM32F1的PWM比较输出模式共有8种。脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。在TIMx_CCMRx寄存器中的OCxM位写入’110’(PWM模式1)或’111’(PWM模式2),能够独立地设置每个OCx输出通道产生一路PWM。有关寄存器的内容,这里就不不再做详细介绍。


这里介绍一下8种输出模式中比较常用的两种PWM输出模式,PWM1和PWM2。其实这两种输出模式差别不大,只不过输出电平的极性不同。

03aefd57df0f0857af8edcf8efdd9bd5_wKgaomU3IISAA319AADzL-uJ0PI969.jpg

4.4 PWM频率计算

频率 = (主时钟频率(72MHz) / (分频系数 + 1)) / 自动重装载值(单位为Hz)


五、通用定时器输出引脚

9882caf329538a715d0cff4bac8fa604_wKgZomU3IJiAM9xvAAEy7qFMNxc807.jpg

其他几个定时器如下

b3fb97e536e62a509f1c515f620e6739_wKgZomU3IK-Aaxi5AADStlPb848268.jpg

TIM2的PWM引脚

342c1d5a2ea08d92fff0029820a78ead_wKgaomU3IK-AZVIEAACDLptTsEI277.jpg

TIM4的PWM引脚

deb6b585620379987a3ff8dfb44bb5d9_wKgaomU3IK-AIv7pAADcOlPcz2w919.jpg

TIM5的PWM引脚


六、实战项目

这里以一个经典项目——呼吸灯,来一起熟悉一下定时器的配置和使用,要求灭—>亮—>灭,时间为2.5s。


6.1 呼吸灯

呼吸灯是指灯能够像人的呼吸一样,实现由暗到亮或由亮到暗的变化,通常用于消息提示功能,或者作为系统正在运行的提示。之前一篇博文介绍过三种呼吸灯的实现方式,这里针对普中核心板,来介绍一下如果实现呼吸灯。


6.2 实现思路

这里用两种方法来实现一下呼吸灯。分别是定时器的溢出中断和PWM。其实第一种和PWM类似,我非就是控制LED点亮时间。


• 定时器中断实现 配置好预分频系数和重装载值,使每0.25ms进入一次定时器中断,记录进入中断次数(count)。当进入次数满100次之后(2.5ms),控制LED点亮的变量(t)值加1。主函数的while(1)轮询中,如果t小于等于count的时候,LED点亮,否则LED熄灭。t的值累计100加次后(2.5s),开始递减,LED由亮到灭。控制t是递增还是递减的是一个标志位(flag),初始值为0,具体可以看程序设计。


• PWM 利用PWM实现呼吸灯就更加简单了,只需要不断调节占空比即可。


66.3 定时器配置

配置通用定时器,有以下步骤


• 使能定时器时钟


• 初始化定时器参数,包括自动重装载值,分频系数,计数方式等


• 设置中断类型,并使能


• 设置中断优先级,使能定时器中断通道


• 开启定时器


• 编写定时器中断服务函数


需要注意的是,配置预分频系数时,比如设置为6,实际是6 + 1。


定时时间T = 自动重装载值 * ((预分频系数 + 1) / 主时钟频率)。主时钟频率为72MHz。(为了避免误导,这里写的主时钟频率为72MHz是APB1总线分频系数不是1的前提下。)


6.4 定时器中断实现呼吸灯

定时器配置程序如下,使用定时器2,控制LED1实现呼吸灯,由灭—>亮—>灭,时间为5秒。


/*

 *==============================================================================

 *函数名称:TIM2_Iint

 *函数功能:初始化定时器2

 *输入参数:per:自动重装载值;psc:预分频系数

 *返回值:无

 *备  注:无

 *==============================================================================

 */

void TIM2_Iint (u16 per,u16 psc)

{

    // 结构体定义

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);   // 使能TIM2时钟

    

    TIM_TimeBaseInitStructure.TIM_Period = per;   // 自动装载值

    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;   // 分频系数

    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;   // 不分频

    TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;   // 设置向上计数模式

    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

    

    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);   // 开启定时器中断

    TIM_ClearITPendingBit(TIM2,TIM_IT_Update);   // 使能更新中断

    

    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;   // 定时器中断通道

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;   // 抢占优先级

    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;   // 子优先级

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   // IRQ通道使能

    NVIC_Init(&NVIC_InitStructure); 

    

    TIM_Cmd(TIM2,ENABLE);   // 使能定时器 

}

初始化时定时器的程序如下


TIM2_Iint(250,71);   // TIM2初始化

预分频系数为71 + 1 = 72,计数到250进入一次中断,也就是0.25ms进入一次中断。累计进入100次(25ms)中断开始调节一点LED的亮度。由灭到亮,累计调节100次(2.5s)。主函数和中断服务函数如下


u8 gTimIrqCunt = 0;   // 进入中断次数计数变量

u8 gLedLightCtrl = 0;   // LED亮度控制变量

u8 gLedFlag = 0;   // LED亮灭控制标志位,0:灭— >亮;1:亮— >灭


int main(void)

{

    Med_Mcu_Iint();   // 系统初始化

    

    while(1)

  {

        if (gLedLightCtrl <= gTimIrqCunt)

        {

            Med_Led_StateCtrl (LED1,LED_OFF);   // 熄灭LED1

        }

        if (gLedLightCtrl > gTimIrqCunt)

        {

            Med_Led_StateCtrl (LED1,LED_ON);   // 熄灭LED1

        }

    }

}


// TIM2中断服务函数

void TIM2_IRQHandler(void)   // TIM2中断

{

    // 产生更新中断

    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)

    {

        gTimIrqCunt = gTimIrqCunt + 1;   // 进入中断次数加1

        

        // 累计进入100次中断,且是由灭到亮

        if (gTimIrqCunt >= 100 && gLedFlag < 100)

        {

            gTimIrqCunt = 0;   // 清零进入中断计数变量

            gLedLightCtrl = gLedLightCtrl + 1;   // LED亮度控制变量加1

            gLedFlag = gLedFlag + 1;   // LED亮灭控制标志位加1

        }

        

        // 累计进入100次中断,且是由亮到灭

        if (gTimIrqCunt >= 100 && gLedFlag >= 100)

        {

            gTimIrqCunt = 0;   // 清零进入中断计数变量

            gLedLightCtrl = gLedLightCtrl - 1;   // LED亮度控制变量加1

            gLedFlag = gLedFlag + 1;   // LED亮灭控制标志位加1

        }

        

        // 一个亮灭周期结束

        if (gLedFlag >= 200)

        {

            gLedFlag = 0;   // 清零LED亮灭控制标志位

        }

    }

    

    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);   // 清除TIM2更新中断标志

}

6.5 使用PWM实现呼吸灯

PWM配置步骤


• 使能定时器以及GPIO时钟,设置引脚复用映射

• 初始化定时器参数,包括自动重装载值,分频系数,计数方式等

• 初始化PWM输出参数,包括PWM模式,输出极性,使能等

• 开启定时器

• 修改CCRx的值来修改占空比

• 使能TIMx在CCRx上的预装载寄存器

• 使能TIMx在ARR上的预装载寄存器允许位

TIM3的通道1配置程序如下这里对引脚进行了重映射。


/*

 *==============================================================================

 *函数名称:TIM3_CH1_PWM_Init

 *函数功能:初始化定时器3的PWM通道1

 *输入参数:per:自动重装载值;psc:预分频系数

 *返回值:无

 *备  注:无

 *==============================================================================

 */

void TIM3_CH1_PWM_Init (u16 per,u16 psc)

{

    // 结构体定义

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    TIM_OCInitTypeDef TIM_OCInitStructure;

    GPIO_InitTypeDef GPIO_InitStructure;

    

    // 开启时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

    

    // 初始化GPIO

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   // 复用推挽输出

    GPIO_Init(GPIOC,&GPIO_InitStructure);

    

    GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);   // 改变指定管脚的映射 

    

    // 初始化定时器参数

    TIM_TimeBaseInitStructure.TIM_Period = per;   // 自动装载值

    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;   // 分频系数

    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;   // 设置向上计数模式

    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); 

    

    // 初始化PWM参数

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   // 比较输出模式

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;   // 输出极性

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   // 输出使能

    TIM_OC1Init(TIM3,&TIM_OCInitStructure);   // 输出比较通道1初始化

    

    TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);   // 使能TIMx在 CCR1 上的预装载寄存器

    TIM_ARRPreloadConfig(TIM3,ENABLE);   // 使能预装载寄存器

    

    TIM_Cmd(TIM3,ENABLE);   // 使能定时器

        

}

实现呼吸灯时,只需要在main函数中不断调整占空比即可,调整占空比的函数为


void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)

这里main函数就不在列出来了。需要注意的是,设置的CCR的值,不能超过自动重装载值 - 1。

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>