实验(七):NVIC中断应用:精准延时的LED闪烁实验

一、实验目的与任务

实验目的:

1. 学习对NVIC的使用;

2. 掌握KEIL5的仿真与调试。

任务:

1.根据要求编写程序,并写出原理性注释;

2. 将检查程序运行的结果,分析一下是否正确;

3. 完成所建工程的验证调试。

二、实验要求

使目标板上红色LED按固定时间一直闪烁,其中点亮时间为500ms,熄灭时间为500ms。同时,在主程序中定义一个32位无符号变量CountOfToggle统计红色LED闪烁的次数,并且每当红色LED完成一次闪烁时,便在调试窗口中输出该变量的值,要求使用中断实现。

三、实验内容及步骤

1. 设计电路图

图1 RGBLED灯与STM32单片机接口电路

2. 软件设计

  • 后台(主程序):

后台流程有系统初始化加上一个无限循环构成,如图2所示。

图2 后台程序流程图

1)系统初始化

本实例中的STM32F103微控制器上电复位后,首先通过一系列初始化函数完成系统初始化工作,包括初始化引脚PA1(连接红色LED的复用引脚)、初始化NVIC和初始化TIM2等。

  • 初始化引脚PA1.

引脚PA1连接LED1。

  • 初始化NVIC。

初始化NVIC(设置TIM2中断)。

  • 初始化TIM2。

如需使用STM32F103微控制器的定时器中断,除了常规配置定时器)外,还需使能定时器的相关中断源。具体来说,本实验中初始化TIM2的软件流程如图3所示。

图3 初始化TIM2

本实例初始化TIM2过程中的每一步都可以由STM32F103x标准外设库中对应的库函数编程实现。

2)无限循环

无限循环是后台程序的主体部分。当系统初始化完成后,即始终陷于该无限循环中运行。在本例的后台循环中,不断查询TIM2更新中断标志变量flag(初始值0)并做相应的处理:

如果flag的值为0(即TIM2的更新还未发生或者已处理完毕),那么此时微控制器什么也不做,回到无限循环的起始处继续查询。

如果flag的值为1(即TIM2的更新中断发生且未被处理),那么此时微控制器将翻转连接红色LED的引脚PA1的输出,记录红色LED闪烁的次数,并将flag清零后回到无限循环的起始处重新开始查询。

  • 前台(TIM2中断服务程序)

本实例的前台软件由TIM2中断服务程序构成,该中断服务程序(因TIM2更新中断)每隔500ms被执行一次。

实验中对TIM2更新中断的处理(如翻转红色LED、统计其闪烁次数等)都由主程序实现,并根据TIM2更新中断标志变量的取值而执行。因此,

因此,TIM2中断服务程序非常简短,其主要工作就是将标志变量置1,具体流程如图4所示。

图4 TIM2中断服务程序流程图

除了将TIM2更新中断标志变量flag置1这一步外,前台流程的其他各步都可使用相应的库函数编程实现。

3. 实验步骤

(1)运行Keil uVision5开发环境,建立一个项目工程。

(2)在工程中添加main.c文件,因其本实验用到LED灯和OLED显示屏,所以将LED和OLED相关文件移植到工程中,如图5所示。

图5 文件移植

(3)在工程中添加定时器相关文件,因其本实验用定时器中断,故再建立一个Timer.c和Timer.h的两个文件,开启定时器中断(通用定时器TIM2),首先编写Timer.c的源代码,如图6所示。

图6 编写Timer.c代码

(4)编写Timer.h程序,方便以后工程文件以移植,使项目工程工具有移植性,如图7所示。

图7 编写Timer.h程序

运行并调试成功并无错误和警告。

(5)编写main.c程序,调用定时器2中断函数(计时中断),在主程序中进行初始化设置、标志位判断、LED闪烁,如图8所示。

图8 编写main.c程序

4. 调试验证及结果

(1)将开发板连接到电脑上,使用STLINK将程序烧录到STM32单片机中,如图9所示:

图9 烧录程序

(2)程序烧录后,观察到OLED 上显示LED闪烁次数如图10所示:

图10 OLED 上显示LED闪烁次数

四、实验代码分析

(1)Timer.c程序:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void){
	//开启时钟,TIM2是APB1的时钟外设
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    	
	//选择时基单元的时钟,可以不选,默认上电后选择内部时钟
	TIM_InternalClockConfig(TIM2);
	
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数器模式
	TIM_TimeBaseInitStructure.TIM_Period = 5000 - 1;//ARR自动重装器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//PSC预分频器的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,高级定时器使用
	
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
    //手动清除中断标志位,避免刚初始化完就进入中断
	TIM_ClearFlag(TIM2,TIM_IT_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);
    
}

(2)main.c程序:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "OLED.h"
#include "Timer.h"

uint32_t CountOfToggle;//计数变量
uint8_t flag = 0;//标志位

int main(void)
{
	OLED_Init();
	Timer_Init();
	LED_Init();
	
	OLED_ShowString(1, 1, "Num:");
	
	while (1)
	{
		OLED_ShowNum(1, 5, CountOfToggle / 2, 5);
		if(flag == 1)
		{
			LED1_Turn();
			flag = 0;
			CountOfToggle++;
		}
	}
}

void TIM2_IRQHandler(void)
{
	//判断中断
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		flag = 1;
		//清除中断标志位
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

五、实验总结

本次实验是学习嵌入式系统中NVIC的使用,掌握如何使用中断实现目标板上红色LED的闪烁,并且在每次闪烁完成后在调OLED显示屏上显示LED闪烁次数的实验。在实验中,我们需要了解NVIC的概念和使用方法,熟悉如何在STM32F1系列的单片机中使用NVIC,以及如何使用中断处理程序来控制外设的操作。

首先,我们需要在主程序中定义一个32位无符号变量CountOfToggle来记录LED闪烁的次数,同时使用定时器中断来控制LED的闪烁时间,并使用GPIO控制LED的开关。在程序的启动阶段,我们需要初始化定时器和GPIO,以及NVIC的相关设置。在定时器中断中,我们需要控制LED的开关和CountOfToggle的值,并将CountOfToggle的值通过I2C协议传输到调OLED显示屏上。

在实验中,我们需要注意以下几点:

  • 在编写程序时,需要根据芯片型号选择正确的头文件和库文件,并且在KEIL中配置正确的编译和下载选项,确保程序能够正确编译和下载到单片机中。
  • 在使用NVIC时,需要注意优先级的设置,通常中断优先级需要比任务优先级高,以确保中断能够得到及时处理。
  • 在使用定时器中断时,需要根据定时器的时钟源和分频系数来设置定时器的计数周期,以及计数器溢出的时间间隔,以达到所需的闪烁时间。
  • 在使用GPIO时,需要注意GPIO口的初始化和配置,以及控制GPIO口状态的方法。

通过本次实验,我更深入地了解了NVIC的概念和使用方法,学会了使用中断来控制外设的操作,并且通过使用I2C协议将数据传输到调OLED显示屏上,掌握了更多与硬件交互的方法。同时,在编写程序的过程中,我注意到了代码风格和注释的重要性,这有助于提高代码的可读性和可维护性。在调试程序时,我也学会了如何使用KEIL的仿真和调试工具,能够更快速地定位问题并进行修复。

总的来说,本次实验让我收获颇丰,对于嵌入式系统的开发和调试都有了更深入的了解,也为我今后的学习和工作奠定了良好的基础。

源码:实验7

猜你喜欢

转载自blog.csdn.net/qq_61228493/article/details/131211075