STM32按键控制LED灯(中断实现)

文章目录

    • 1- 轮询模式和中断模式
    • 2- 配置管脚为中断模式
    • 3- 添加代码
    • 4- 具体实现原理
    • 5- main()——第一个执行程序?

在一般情况下,我们希望实现按键控制灯的亮和灭,这次学习的是中断开发按键实现LED量灭。

1- 轮询模式和中断模式

轮询(Polling)模式:
每个外围设备提供一个或多个状态信息,CPU逐次读入并测试各个外围设备的状态信息,若该外围设备请求服务(请求交换信息),则为之服务,然后清除该状态信息。否则,跳过,查询下一个外围设备的状态。各外围设备查询完一遍后,再返回从头查询起,直到发出停止命令为止。但是在查询式I/O方式下,CPU要不断地读取状态字和检测状态字,不管那个外围设备是否有服务请求,都必须一一查询,许多次的重复查询,可能都是无用的,而又占去了CPU的时间,效率较低。

中断(Interrupt)模式:
为了提高CPU的效率和使系统具有良好的实时性,可以采用中断控制I/O方式。采用中断方式CPU就不必花费大量时间去查询各外围设备的状态了。而是当外围设备需要请求服务时,向CPU发出中断请求(ARQ),CPU响应外围设备中断,停止执行当前程序,转去执行一个外围设备服务的程序,此服务程序称为中断服务处理程序,或称中断服务子程序。中断处理完毕,CPU又返回来执行原来的程序。

2- 配置管脚为中断模式

这是扩展的按键的电路,在单片机上也可以看见key1和key2两个按键。我们希望通过这两个按键分别控制LED其中两个灯的量灭。
STM32按键控制LED灯(中断实现)_第1张图片

看到原理图的话,按下按钮PC15变为低电平,所以我们需要给PA15一个高电平,即内部上拉。对应图中的GPIO Pull_up-Pull_down。
一般我们采用的都是下降沿触发,所以对应的就是GPIO mode。
最后一个User Label 相当于重命名。
当然,PC13也是一样的,LED灯的管脚配置就不过多说啦,然后Ctrl+s保存生成代码。

至于内不上拉和下拉以及沿触发、水平触发不懂的可以去了解一下,这里不过多阐述了。

STM32按键控制LED灯(中断实现)_第2张图片
并且要将中断管脚【10-15】的优先级设置为稍低一点。越大说明优先级越低。
STM32按键控制LED灯(中断实现)_第3张图片

3- 添加代码

我们需要在生成的代码中添加代码才能实现功能,就等于是按键的中断的配置配好了,我们需要利用起来。
在gpio.c中的/* USER BEGIN END 2 / 和/ USER CODE END 2 */之间添加如下代码:

/* USER CODE BEGIN 2 */
typedef struct gpio_s
{
	  GPIO_TypeDef   *group;
	  uint16_t       pin;
} gpio_t;

/*这里定义了一个结构体数组,group指向第一个,pin指向第二个*/
gpio_t      leds[LedMax] =
{
		{RedLed_GPIO_Port,  SysLed_Pin},
		{RedLed_GPIO_Port,  BlueLed_Pin},
		{RedLed_GPIO_Port,  RedLed_Pin},
		{RedLed_GPIO_Port,  GreenLed_Pin},
};
/*which代表选择哪一个灯,int类型的,每个灯都有一个数字代替,去enum去看;ststus控制灯的亮灭,对应参数是OFF 和 ON*/
void turn_led(int which, int status)
{
	GPIO_PinState       level;
	if( which<0 || which>=LedMax)
	{
		return ;
	}
	level = status == OFF ? GPIO_PIN_SET : GPIO_PIN_RESET;

	HAL_GPIO_WritePin(leds[which].group, leds[which].pin, level);
}

/*int whitch对应的是控制哪一个灯,interval传时间,单位毫秒*/
void blink_led(int which, uint32_t interval)
{
	if(which>=LedMax || interval<=0)
	{
		return ;
	}

	turn_led(which, ON);
	HAL_Delay(interval);

	turn_led(which, OFF);
	HAL_Delay(interval);
}

/*每次复位都会闪三下,表示启动*/
void sysled_hearbeat(void)
 {
 	blink_led(SysLed, 100);
 	blink_led(SysLed, 100);
 	blink_led(SysLed, 800);
 }

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if (Key1_Pin == GPIO_Pin)
	{
		blink_led(BlueLed, 500);
	}

	else if(Key2_Pin == GPIO_Pin)
	{
		blink_led(RedLed, 500);
	}
}
/* USER CODE END 2 */

在gpio.h中添加声明:

/* USER CODE BEGIN Prototypes */
/*枚举,SysLed默认为0,后面依次增长*/
enum
{
	SysLed,
	BlueLed,
	RedLed,
	GreenLed,
	LedMax,
};

#define OFF 0
#define ON 1

extern void turn_led(int which, int status);
extern void blink_led(int which, uint32_t interval);
void sysled_hearbeat(void);
/* USER CODE END Prototypes */

我们如果现在烧录测试的话就会发现我们要改变LED灯就必须按复位键,并且灯只会亮一个,而且一直亮着。死在了那里。
全部完成之后,我们需要注意的是,HAL_Delay()是由Systick滴答定时器中断实现的,所以我们需要Systick中断优先级大于我们前面设置的优先级(数字越大优先级越低)。如下设置:在红色的地方将优先设置为最高0即可。

STM32按键控制LED灯(中断实现)_第4张图片

4- 具体实现原理

程序怎么接受中断请求的呢?接收到中断请求又是怎么处理的呢?
本来CPU是在自己的main函数中的while(1)中执行的,突然有按键的中断请求过来了,然后就去找是哪一个中断服务处理程序,并且去执行。

STM32按键控制LED灯(中断实现)_第5张图片

然后找到是EXTI15_10_IRQHandler的中断服务处理程序。EXTI15_10_IRQHandler这个中断服务处理程序是在stm32L4xx_it.h这个文件中声明的。
STM32按键控制LED灯(中断实现)_第6张图片

在stm32L4xx_it.c中找到了这个函数,系统知道我们按的是哪一个管脚,然后调用相关函数。
STM32按键控制LED灯(中断实现)_第7张图片

找到HAL_GPIO_EXTI_Callback(GPIO_Pin)然后执行我们写好的功能。我们想让这个按钮按下之后执行什么事情就是在这个HAL_GPIO_EXTI_Callback(GPIO_Pin)函数中写的。函数大小写等需要完全一样,传过来的参数就是哪一个管脚。
STM32按键控制LED灯(中断实现)_第8张图片

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if (Key1_Pin == GPIO_Pin)
	{
		blink_led(BlueLed, 500);
	}

	else if(Key2_Pin == GPIO_Pin)
	{
		blink_led(RedLed, 500);
	}
}

执行完毕之后会返回while(1)中继续执行没执行完毕的环节。

5- main()——第一个执行程序?

在startup_stm32l433cbtx.s中我们也鞥能够看见,其实STM32上电的时候,main函数并不是第一个执行的程序,他的前面还要执行一系列的操作。
还需要做:

  • 调用时钟系统初始化函数
    /* Call the clock system initialization function.*/

  • 将数据段初始化器从flash复制到SRAM
    /* Copy the data segment initializers from flash to SRAM */

  • 零填充bss段
    /* Zero fill the bss segment. */

  • 调用静态构造函数
    /* Call static constructors */

  • 调用应用程序的入口点
    /* Call the application's entry point.*/

笔记,如有错误还请指出~

你可能感兴趣的:(STM32,stm32,单片机,c语言,嵌入式硬件,物联网)