STM32中断和基于HAL库的LED流水灯

前言

随着科技的不断发展,LED技术在日常生活和工业生产中的应用越来越广泛。其中,LED流水灯作为一种重要的LED显示技术,被广泛应用于各种场景。本实验旨在探究LED流水灯的效果,并通过实践了解相关的控制技术。

LED流水灯是一种由多个LED灯珠组成的阵列,通过控制每个灯珠的亮灭时间,实现一种流动的光效。这种显示方式不仅美观大方,而且具有较高的实用价值。例如,在商场、酒店、写字楼等场所,LED流水灯可以用来展示广告、标语或通知等信息;在城市道路的路灯、警示牌等场所,LED流水灯也可以发挥重要的作用。

本实验的主要目的是通过实际操作,探究LED流水灯的各种效果,并验证利用HAL库(硬件抽象层库)进行LED亮度控制以及利用定时器实现流水灯的相关理论。同时,本实验将通过程序代码实现对LED流水灯的多种控制效果,并对定时器的精度进行测量和分析。

在实验过程中,我们将首先搭建一个简单的LED流水灯实验平台,然后利用HAL库和定时器对LED灯珠进行控制。通过改变定时器的参数,我们可以实现不同的LED流水灯效果,并对其精度进行测量。

STM32中断原理

中断是STM32微控制器中一种常用的事件驱动机制,用于处理紧急和实时的外部或内部事件。它允许微控制器在执行主程序的同时响应和处理中断事件,提高了系统的实时性和效率。

中断的基本原理

  1. 中断源:STM32微控制器具有多个硬件模块,如定时器、外部中断引脚、串口等,这些模块可以产生中断请求,被称为中断源。
  2. 中断控制器:STM32微控制器内部集成了一个中断控制器,用于管理和控制不同中断源的优先级、屏蔽和响应方式。
  3. 中断服务程序(ISR):当中断事件发生时,中断控制器根据优先级确定中断源,并将控制权转移到相应的中断服务程序。中断服务程序是用户自定义的函数,用于处理中断事件。
  4. 中断优先级:不同的中断源具有不同的优先级,中断控制器根据优先级来确定应该响应的中断。高优先级的中断会打断正在执行的低优先级中断。
  5. 中断嵌套:STM32微控制器支持中断嵌套,即在一个中断服务程序中可以接收和处理其他中断请求。

通过中断优先级的设置,可以灵活控制中断的嵌套。

中断的使用步骤

  1. 初始化中断控制器:在程序开始时,需要初始化中断控制器,包括设置中断向量表、配置中断优先级和屏蔽中断等。
  2. 注册中断服务程序:将用户自定义的中断服务程序注册到中断向量表中,以便中断控制器在中断事件发生时能够正确地调用相应的中断服务程序。
  3. 配置中断源:根据需要,配置相应的中断源的触发条件和工作模式。
  4. 启用中断:在需要时,使能相应的中断源,使其能够产生中断请求。
  5. 中断处理:当中断事件发生时,中断控制器会自动跳转到相应的中断服务程序,执行用户定义的中断处理代码。在中断服务程序中,可以读取和处理中断源的状态、清除中断标志位、执行相应的操作等。
  6. 退出中断:在中断服务程序的末尾,使用特定的指令退出中断,将控制权交还给主程序或其他中断。

对比

相比于轮询方式,中断机制具有以下优点:

  • 实时性:中断可以立即响应外部或内部事件,提高了系统的实时性和响应速度。
  • 节省处理器时间:中断机制允许处理器在执行主程序的同时处理中断事件,节省了处理器的时间和资源。
  • 灵活性:中断机制可以根据中断优先级和中断嵌套的设置,灵活控制中断的响应顺序和处理方式。
  • 可靠性:中断机制可以避免因为长时间的轮询而导致的事件延迟或丢失。
    然而,中断机制也存在一些注意事项:
  • 中断处理时间要尽量短,以避免影响其他中断或主程序的执行。
  • 中断嵌套的设置要谨慎,避免死锁或优先级反转等问题。
  • 中断服务程序的编写和调试需要

使用STM32CubeMX和HAL库函数进行开发方法

下面是使用STM32CubeMX和HAL库函数进行开发的基本步骤和流程:

步骤描述
1打开STM32CubeMX软件,并创建一个新工程。选择目标STM32微控制器系列和型号。
2在图形化界面中进行配置,包括时钟配置、引脚配置、外设配置等。可以选择需要的外设和功能,并设置相应的参数。
3生成代码:在STM32CubeMX中点击"生成代码"按钮,软件将根据配置自动生成相应的初始化代码,包括主函数、启动文件、驱动库等。
4打开生成的工程代码,可以在主函数中添加自定义的应用程序代码。
5使用HAL库函数进行外设初始化和控制。HAL库提供了一套统一的API接口,可以方便地进行外设的初始化、配置和控制。
6编译和调试:使用合适的编译器和调试器对工程进行编译和调试,检查代码的正确性和功能的正常运行。
7在开发过程中,可以根据需要添加中断处理、定时器配置、串口通信等其他功能模块。
8最后,将生成的可执行文件烧录到目标STM32微控制器中,进行测试和运行。

使用STM32CubeMX和HAL库函数进行开发的优势在于,它提供了一种图形化和自动生成代码的方式,使开发者能够更快速地进行STM32微控制器的开发和调试。而HAL库函数提供了一套统一的API接口,方便开发者进行外设的初始化和控制,简化了底层寄存器操作的复杂性。
注意,使用STM32CubeMX和HAL库函数进行开发时,开发者仍然需要理解STM32微控制器的基本原理和寄存器操作,以便更好地进行调试和优化。

STM32CubeMX

STM32CubeMX是STMicroelectronics推出的一款强大的图形化配置工具,用于快速开发STM32微控制器的嵌入式应用程序。它提供了一种简单而直观的方式来配置和初始化STM32微控制器,减少了开发过程中的工作量和时间。

主要特点

  1. 图形化界面:STM32CubeMX提供了直观的图形化界面,用户可以通过简单的拖拽和点击来进行配置和初始化操作,无需编写繁琐的代码。
  2. 自动代码生成:根据用户在图形界面中的配置,STM32CubeMX能够自动生成相应的初始化代码,包括时钟配置、引脚配置、外设初始化等。这大大简化了开发者的工作,减少了出错的可能性。
  3. 支持多种STM32系列:STM32CubeMX支持多个STM32系列微控制器,包括STM32F0、STM32F1、STM32F2、STM32F3、STM32F4、STM32F7和STM32L4等。用户可以根据自己的需求选择不同系列的微控制器进行开发。
  4. 集成外设库:STM32CubeMX集成了STMicroelectronics提供的STM32Cube HAL库,这是一套丰富而强大的外设驱动库。用户可以在图形界面中选择需要的外设,并自动配置和初始化相应的库函数。
  5. 连接外部中间件和库:STM32CubeMX还支持连接外部中间件和库,如RTOS(实时操作系统)、USB库、文件系统等。这样可以方便地集成第三方软件组件,加快开发进度。

与Keil的对比

相比于Keil,STM32CubeMX具有以下优点:

  • 可视化配置:STM32CubeMX提供了图形化界面,使得配置和初始化操作更直观和易于理解,无需深入了解底层的寄存器操作。
  • 自动代码生成:STM32CubeMX能够根据用户的配置自动生成初始化代码,减少了手动编写和调试代码的工作量。
  • 外设库集成:STM32CubeMX集成了STM32Cube HAL库,提供了一套统一的API接口,简化了外设驱动的开发和使用。
  • 多系列支持:STM32CubeMX支持多个STM32系列微控制器,使得用户可以根据项目需求选择合适的微控制器进行开发。

然而,Keil也有其自身的优势:

  • 成熟稳定:Keil作为一款经典的嵌入式开发工具,已经被广泛应用于STM32的开发,具有成熟和稳定的特点。
  • 更多工具支持:Keil提供了更多的编译器、调试器和仿真器支持,适用于不同的开发需求。
    综上所述,STM32CubeMX是一款功能强大且易用的配置工具,可以大大简化STM32微控制器的开发过程。它与Keil相互补充,用户可以根据自己的需求选择适合的工具进行开发。

软件安装和库安装

安装前期的准备

首先,STM32CubeMX使用java编写的,所以,电脑上首先需要安装java环境,安装链接在此
链接: https://www.java.com/zh-CN/download/
尽量安装64为的java
安装完成后,在命令提示符(cmd)中输入

java -version

即可查看是否安装成功:
在这里插入图片描述

安装STM32CubeMX

软件的下载地址:
链接: https://www.st.com/en/development-tools/stm32cubemx.html
在这里插入图片描述
选择自己需要的版本,填写相干内容,注意,姓名可以不真实,但邮箱一定要真实,否则你就无法收到软件了
在这里插入图片描述
下载好软件后直接安装即可。

库安装

打开stm32CubeMAX,点上面的Help -> Manage embedded software packages(如果第一打开有提示界面,选最后一个 NO thank 就可以了)
在这里插入图片描述
然后选择你的芯片型号,安装既可以了( 点击“Install Now” )
在这里插入图片描述
在这里插入图片描述

使用STM32CubeMX创建一个工程

点击创建新工程:
在这里插入图片描述
选择芯片STM32F103C8T6:
在这里插入图片描述
点击START PROJECT,进入以下界面:
在这里插入图片描述
sys的选择:
在这里插入图片描述
rcc的选择:
在这里插入图片描述
选择GPIO_OUTPUT(此处选择PA3,PA4,PA5口):
在这里插入图片描述
OUT_PUT改为hight
在这里插入图片描述
配置时钟:
在这里插入图片描述
输出配置:
在这里插入图片描述
在这里插入图片描述
创建完成后,打开工程:
在这里插入图片描述
在Keil中:
在这里插入图片描述
在while(1)循环中添加以下代码:

	  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); 
	  HAL_Delay(1000);
	  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
	  
	  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
	  HAL_Delay(1000); // ??1s
	  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	  
	  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
	  HAL_Delay(1000);
	  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

在这里插入图片描述
编译程序,0错误,0警告
在这里插入图片描述

程序烧录结果

实现LED流水灯,即用GPIO端口完成3只LED红绿灯的周期闪烁。

程序见上,实物的链接图如图所示:
在这里插入图片描述

烧录的效果图见下:
在这里插入图片描述

采用中断模式编程,当开关接高电平时,LED流水灯工作

实现方法:
用stm32F103核心板的GPIOA端某一管脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED流水灯工作;接低电平时,LED流水灯停止工作。
选择 LED 灯引脚 PA5,设置引脚为输出模式 GPIO_Output;
选择作为外部中断的引脚 PB15,设置为与中断线 GPIO_EXTI15 连接。
在这里插入图片描述
使能引脚对应的外部中断线 EXTI line[15:10]
在这里插入图片描述
配置中断优先级,修改外部中断线的抢先优先级为15

在这里插入图片描述
生成并打开代码:
在这里插入图片描述
将下面的代码写入main.c处:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) 
{ 
	if( GPIO_Pin == B1_EXTI_Pin )
		{
			HAL_GPIO_TogglePin(LD5_GPIO_Port, LD5_Pin); 
		}
}

注意,这里的LD5是和配置时自己的User Lable对应的
在这里插入图片描述

本任务只设置一个外部中断引脚,因此不需要判断多个引脚。如果系统中设置了多个外部中断,建议使用 switch-case 进行多分支判断。
编译
在这里插入图片描述
没有错误和警告
解析生成代码
在这里插入图片描述
再main中,函数 static void MX_GPIO_Init(void) 是 CubeMX 自动生成的引脚初始化函数,不需要做修改。
其中,设置了外部中断触发方式为:下降沿触发。

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : LD1_Pin */
  GPIO_InitStruct.Pin = LD1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : B1_EXTI_Pin */
  GPIO_InitStruct.Pin = B1_EXTI_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_EXTI_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 15, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

这段代码是使用HAL库函数进行GPIO引脚初始化的函数MX_GPIO_Init。下面是对代码的解释:

  1. 首先,定义了一个GPIO_InitTypeDef类型的结构体变量GPIO_InitStruct,用于配置GPIO引脚的参数。
  2. __HAL_RCC_GPIOD_CLK_ENABLE()__HAL_RCC_GPIOA_CLK_ENABLE()__HAL_RCC_GPIOB_CLK_ENABLE()是使能GPIO引脚所在的GPIO端口的时钟。
  3. HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_RESET)将LD1_Pin引脚的状态设置为低电平(GPIO_PIN_RESET),即将LED1引脚设置为熄灭状态。
  4. GPIO_InitStruct结构体的成员Pin设置为LD1_Pin,表示当前配置的GPIO引脚是LD1_Pin。
  5. GPIO_InitStruct结构体的成员Mode设置为GPIO_MODE_OUTPUT_PP,表示将GPIO引脚配置为推挽输出模式。
  6. GPIO_InitStruct结构体的成员Pull设置为GPIO_NOPULL,表示禁用引脚的上下拉电阻。
  7. GPIO_InitStruct结构体的成员Speed设置为GPIO_SPEED_FREQ_LOW,表示引脚的输出速度为低速。
  8. HAL_GPIO_Init(LD1_GPIO_Port, &GPIO_InitStruct)根据上述配置,进行LD1引脚的初始化。
  9. GPIO_InitStruct结构体的成员Pin设置为B1_EXTI_Pin,表示当前配置的GPIO引脚是B1_EXTI_Pin。
  10. GPIO_InitStruct结构体的成员Mode设置为GPIO_MODE_IT_FALLING,表示将GPIO引脚配置为下降沿触发的外部中断模式。
  11. GPIO_InitStruct结构体的成员Pull设置为GPIO_NOPULL,表示禁用引脚的上下拉电阻。
  12. HAL_GPIO_Init(B1_EXTI_GPIO_Port, &GPIO_InitStruct)根据上述配置,进行B1_EXTI引脚的初始化。
  13. HAL_NVIC_SetPriority(EXTI15_10_IRQn, 15, 0)设置外部中断EXTI15_10_IRQn的优先级为15。
  14. HAL_NVIC_EnableIRQ(EXTI15_10_IRQn)使能外部中断EXTI15_10_IRQn。

这段代码主要完成了GPIO引脚的初始化和配置,包括设置引脚的输入/输出模式、上下拉电阻、输出速度等参数。同时,也设置了LD1引脚和B1_EXTI引脚的初始状态和中断相关的配置。

烧录
使用STLink烧录,
烧录前将板子上的 BOOT0 置1,BOOT1 置0;烧录后,将板子断电,将 BOOT0 置0,BOOT1 置0,再插电即可出现效果。
在这里插入图片描述
在这里插入图片描述
从实际效果可以看到:GPIO_PB15 接高电平时,LED 亮;接低电平时,LED 灭。

使用Keil的软件仿真逻辑分析仪功能观察管脚的时序波形

可以参考此篇博客
链接: https://blog.csdn.net/weixin_63019977/article/details/133749827
在这里插入图片描述

在这里插入图片描述
分析可知,引脚的电平正确

总结

使用STM32CubeMX和HAL库函数进行基于GPIO的LED流水灯实验,使得开发过程更加简单和高效。通过图形化界面的配置和自动生成代码的功能,减少了手动编写和调试的工作量。同时,HAL库函数提供了一套统一的API接口,方便开发者进行GPIO的初始化和控制。

在实验中,我们也学习到了使用STM32CubeMX的基本流程,包括配置和生成代码。通过HAL库函数的使用,我们可以更方便地进行外设的初始化和控制,提高了开发效率。

总的来说,这个实验帮助我们熟悉了基于HAL库的GPIO控制和STM32CubeMX的使用方法,为后续的STM32开发提供了基础。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是基于STM32CubeIDE和HAL库的定时器中断实现LED流水灯的代码: ```c #include "main.h" #include "stm32f4xx_hal.h" /* 定义GPIO引脚 */ #define LED1_Pin GPIO_PIN_0 #define LED1_GPIO_Port GPIOA #define LED2_Pin GPIO_PIN_1 #define LED2_GPIO_Port GPIOA #define LED3_Pin GPIO_PIN_2 #define LED3_GPIO_Port GPIOA #define LED4_Pin GPIO_PIN_3 #define LED4_GPIO_Port GPIOA /* 定义全局变量和函数 */ TIM_HandleTypeDef htim2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM2_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); HAL_TIM_Base_Start_IT(&htim2); /* 启动定时器并开启中断 */ while (1) { /* 主函数不做任何事情 */ } } /* 定时器中断处理函数 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint8_t led_status = 0x01; /* 初始状态下第一个LED亮 */ /* 根据led_status位控制LED的亮灭 */ if (led_status & 0x01) HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); else HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); if (led_status & 0x02) HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); else HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); if (led_status & 0x04) HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); else HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); if (led_status & 0x08) HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET); else HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_RESET); /* 更新led_status的值 */ if (led_status == 0x08) led_status = 0x01; else led_status <<= 1; } /* System Clock Configuration */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /* GPIO初始化 */ void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* 使能GPIOA时钟 */ __HAL_RCC_GPIOA_CLK_ENABLE(); /* 配置GPIO引脚为输出模式 */ GPIO_InitStruct.Pin = LED1_Pin | LED2_Pin | LED3_Pin | LED4_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } /* 定时器初始化 */ void MX_TIM2_Init(void) { /* 使能TIM2时钟 */ __HAL_RCC_TIM2_CLK_ENABLE(); /* 初始化htim2的各项参数 */ htim2.Instance = TIM2; htim2.Init.Prescaler = 8399; /* 预分频值 */ htim2.Init.CounterMode = TIM_COUNTERMODE_UP; /* 向上计数模式 */ htim2.Init.Period = 999; /* 自动重装值 */ htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; /* 时钟分频 */ if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } } ``` 注释已经非常详细了,简单来说就是通过定时器中断不断地改变LED的状态,从而实现LED流水灯的效果。每当定时器计数器达到自动重装值时,就会产生一次定时器中断,然后在中断处理函数中改变LED的状态。 需要注意的是,这里使用的定时器是TIM2,预分频值为8399,自动重装值为999,因此定时器的计数频率为84MHz / (8399 + 1) = 10kHz,即每隔100ms产生一次定时器中断。如果需要改变LED流水灯的速度,可以调整预分频值和自动重装值。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值