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

该实验通过STM32单片机使用NVIC和定时器中断控制LED灯以500ms的周期闪烁,并在OLED屏上显示闪烁次数。主要涉及系统初始化、中断服务程序、NVIC配置和定时器设置。实验代码包括Timer.c和main.c,通过KEIL5进行编译调试,加深了对嵌入式中断系统和硬件交互的理解。
摘要由CSDN通过智能技术生成

一、实验目的与任务

实验目的:

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

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面为您提供相关代码: ```c #include "stm32f10x.h" void Delay_ms(uint16_t nms); void LED_Init(void); void TIM2_Init(void); void NVIC_Config(void); int main(void) { LED_Init(); TIM2_Init(); NVIC_Config(); while (1) { // 主程序中不需要做任何操作,LED闪烁中断服务程序控制 } } void Delay_ms(uint16_t nms) { uint16_t i; while (nms--) { for (i = 0; i < 12000; i++); } } void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } void TIM2_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 定时器自动重载值 TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 定时器预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_Cmd(TIM2, ENABLE); } void NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); } void TIM2_IRQHandler(void) { static uint8_t LED_Status = 0; if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if (LED_Status == 0) { GPIO_SetBits(GPIOB, GPIO_Pin_12); LED_Status = 1; } else { GPIO_ResetBits(GPIOB, GPIO_Pin_12); LED_Status = 0; } } } ``` 在这段代码中,我们首先定义了`Delay_ms()`函数,用于实现毫秒级的延时,然后定义了`LED_Init()`函数和`TIM2_Init()`函数,分别用于初始化LED和定时器。在`NVIC_Config()`函数中,我们配置了NVIC中断控制器,使定时器溢出时触发中断。最后,在`TIM2_IRQHandler()`函数中,我们实现了LED闪烁功能。 在主程序中,我们只需要初始化LED和定时器,然后让程序进入死循环即可,LED闪烁中断服务程序控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

追上

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值