ARM体系结构及编程 实验三 定时器中断实验

一、实验目的

      1. 深入了解STM32定时器结构和工作原理;

      2. 掌握定时器的配置方法;

      3. 掌握定时器常用的固件函数和使用方法;

      4. 掌握定时应用程序及定时器中断服务程序的编写方法。

二、实验内容

      使用库函数开发模版编写定时器应用程序,使用通用定时器TIM3实现LED灯D1的定时闪烁(让D1每隔1s状态反转一次)。

三、实验设备仪器及材料

      硬件:STM32开发板, PC 机;

      软件:MDK5集成开发环境,Windows 操作系统;

四、实验原理及接线

1. 定时器相关概念

      STM32F4的通用定时器TIMx (TIM2-TIM5 和TIM9-TIM14)具有如下功能:

  (1)16位/32位(仅TIM2和TIM5)向上、向下、向上/向下自动装载计数器(TIMx_CNT),注意:TIM9-TIM14只支持向上(递增)计数方式。

   (2)16位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为1~65535之间的任意数值。

   (3)4个独立通道(TIMx_CH1-4,TIM9-TIM14最多⒉个通道),这些通道可以用来作为:A.输入捕获 B.输出比较 C.PWM生成(边缘或中间对齐模式),注意:TIM9-TIM14不支持中间对齐模式 D.单脉冲模式输出

   (4)可使用外部信号(TIMx_ETR)控制定时器,且可实现多个定时器互连(可以用1个定时器控制另外一个定时器)的同步电路。

   (5)发生如下事件时产生中断/DMA请求(TIM9-TIM14不支持 DMA):A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) C.输入捕获 D.输出比较

   (6)支持针对定位的增量(正交)编码器和霍尔传感器电路(TIM9-TIM14不支持)

   (7)触发输入作为外部时钟或者按周期的电流管理(TIM9-TIM14不支持)

2. 向上计数

      在向上(递增)计数模式下,计数器从0计数到自动重载值(TIMx_ARR寄存器的内容),然后重新从0开始计数并生成计数器上溢事件。每次发生计数器上溢时会生成更新事件(UEV),或将TIMx_EGR寄存器中的UG位置1(通过软件或使用从模式控制器)也可以生成更新事件。通过软件将TIMx_CR1寄存器中的UDIS位置1可禁止 UEV事件。这可避免向预装载寄存器写入新值时更新影子寄存器。在 UDIS位写入0之前不会产生任何更新事件。不过,计数器和预分频器计数器都会重新从0开始计数(而预分频比保持不变)。此外,如果设置TIMx_CR1寄存器中相应的中断位置1,也会产生中断事件。

五、实验操作步骤

1. 编写led.c文件

#include "led.h"
void LED_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量,该语句的作用是将GPIO_InitTypeDef结构体命名为GPIO_InitStructure
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); //使能端口F时钟,
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;//设置输出模式
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;//管脚设置为F9和F10
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//IO口速度为100M
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉
	GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化结构体,初始化PF口
	GPIO_SetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10);//输出高电平
}

2. 编写time.c文件

      由于本次实验使用TIM3,所以先对TIM3进行初始化。

      操作步骤和思路:

   (1)使能定时器时钟

      TIM3是挂接在APB1总线上的,所以可以使用APB1总线时钟使能函数来使能TIM3,调用函数RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

   (2)初始化定时器参数,包含自动重装值,分频系数,计数方式等

      其库函数如下:TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);

      要使用定时器功能,需要对定时器内相关参数初始化,第一个参数是确定定时器类型为TIM3;第二个参数是第一个结构体指针变量,结构体类型是TIM_TimeBaseInitTypeDef,其中包含了定时器初始化的成员变量,包括TIM_Period、TIM_Prescaler、TIM_ClockDivision、TIM_CounterMode。

      TIM_Period用于设置定时器自动重载计数周期值,也就是自动装载值。

      TIM_Prescaler是定时器的预分频器系数,时钟源经过该预分频器后输出的才是定时器时钟。

      TIM_ClockDivision是时钟分频因子,用于设置定时器时钟CK_INT频率与数字滤波器采样时钟频率分频比。

      TIM_CounterMode是定时器计数方式,本次实验采用向上计数模式。

   (3)设置定时器中断类型并使能

      其库函数如下:TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE)

      第一个参数是选择定时器TIM3,第二个参数用于设置定时器中断类型,本次实验设置为更新中断TIM_IT_Update,第三个参数用于失能定时器中断类型,所以是ENABLE。

   (4)设置定时器中断优先级,使能定时器中断通道

    调用中断函数NVIC,并对其初始化,即NVIC_Init(),其中包括定时器中断通道设置为TIM3_IRQn,抢占优先级为2,子优先级为3,IRQ通道进行使能,完成初始化。

   (5)开启定时器

      调用库函数TIM_Cmd(TIM3,ENABLE)用于开启定时器。

   (6)编写定时器中断服务函数

      函数TIM3_IRQHandler用于进行定时器中断服务。此函数用于判断TIM3的更新中断是否产生,所以调用if(TIM_GetITStatus(TIM3,TIM_IT_Update))。如果产生更新中断,那么调用TIM_GetITStatus函数后返回值为1,这样就会进入到if函数内执行中断控制程序,即控制led1的亮灭;否则不会进入到中断处理程序。

      TIM_ClearITPendingBit(TIM3,TIM_IT_Update)用于清楚TIM3的更新中断标志位。

      详细代码:

#include "time.h"
#include "led.h"

void TIM3_Init(u16 per,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3时钟	
	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);
	
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //开启定时器中断
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;//定时器中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	
	
	TIM_Cmd(TIM3,ENABLE); //使能定时器	
}
void TIM3_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM3,TIM_IT_Update))
	{
		led1=!led1;
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);	
}

3. 编写main.c文件

      操作步骤和思路:

   (1)主函数实现的功能首先是初始化对应的硬件端口时钟和I0口,然后调用TIM4的初始化函数,这里我们设定定时器自动重装载值为5000-1,预分频系数为8400-1,这里减1是因为定时器预分频器内部会自动加1,所以如果要进行8400分频的话,就传递8399,而重装载值是从0开始计数的,所以累积5000的话最终传递的参数是5000-1。最后进入while循环语句,不断让led1指示间隔200ms闪烁。

   (2)初始化后,定时器开始工作,计数器CNT从0开始计数,每来一个定时器时钟,计数器就会累加一次,当计数到5000-1也就是刚好计数5000次,这个时候定时器就会发生溢出并产生更新中断,没产生一次更新中断的时间为500ms。计算过程如下:

   (3)由于TIM3是挂载在APB1总线上的,而APB1的时钟为42MHz,如果APB1时钟分频系数为1,TIM2-7以及TIM12-14的时钟为APB1总线的时钟,否则就是APB1总线时钟的2倍,即84MHz。再根据自动重装载值和预分频系数的计算公式Tout= ((per+1)*(psc+1))/Tclk就可以计算出定时时间,

   (4)Tclk是定时器的时钟频率值,84M,per和 psc是需要参数值,Tout是定时器产生中断的时间,单位是us。将数据代入即可得到产生定时更新中断时间是500ms。

      详细代码:

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "time.h"
int main()
{
	u8 i=0;
	SysTick_Init(168);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组
	LED_Init();
	TIM3_Init(10000-1,8400-1);  //产生1s,预分频器8400
	while(1)
	{
		i++;
		if(i%20==0)
		{
			led2=!led2;
		}
		delay_ms(10);
	}
}

六、实验结果

Led1保持1秒间隔亮灭,led2保持闪烁,如下图所示:

总结:

      本次实验采用定时器定时功能,使得led1每隔500ms状态取反一次,实现1秒钟闪烁一次。定时器就是经过PSC预分频器之后,即CK_CNT,用来驱动计数器计数,而当计数器CNT的值与比较寄存器CCR的值相等的时候,会产生比较中断,其相应的标志位会置位,以实现重复计数。而中断时间的产生式因为定时器发生溢出产生更新中断,这个时间可以由自动重装载值和预分频系数求出,其计算公式为Tout= ((per+1)*(psc+1))/Tclk。

  • 19
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布零酱

阿里嘎多!谢谢打赏嗷喵~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值