使用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其中两个灯的量灭。

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

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


    并且要将中断管脚【10-15】的优先级设置为稍低一点。越大说明优先级越低。

    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即可。

    4- 具体实现原理

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

    然后找到是EXTI15_10_IRQHandler的中断服务处理程序。EXTI15_10_IRQHandler这个中断服务处理程序是在stm32L4xx_it.h这个文件中声明的。

    在stm32L4xx_it.c中找到了这个函数,系统知道我们按的是哪一个管脚,然后调用相关函数。

    找到HAL_GPIO_EXTI_Callback(GPIO_Pin)然后执行我们写好的功能。我们想让这个按钮按下之后执行什么事情就是在这个HAL_GPIO_EXTI_Callback(GPIO_Pin)函数中写的。函数大小写等需要完全一样,传过来的参数就是哪一个管脚。

    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.*/

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

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32中断实现按键控制LED灯

    发表评论