LiteOS中STM32中断的应用及实例程序Demo
STM32L431的提供了丰富的中断资源,例如大部分GPIO引脚都具有中断功能。但是其中断功能的理解和使用略显复杂。这里首先简述了TM32L431的中断优先级分组功能,并分析了GPIO中断功能及配置方法步骤,其次简述了LiteOS系统中断应用方法,最后在IoT Link Studio中,以小熊派开发板为硬件基础,在LiteOS系统中实现了外部按键中断检测及LCD显示功能的程序实例。
1.STM32L431的中断系统
STM32L431采用了Arm Cortex-M4内核,具有丰富的中断资源,大部分的GPIO引脚都具有中断功能。对于这些丰富的中断的管理,STM32也有相对复杂的中断管理功能。在程序中使用中断,首先要进行中断优先级的分组设置,那我们也就先从中断优先级分组开始。
1.1 中断优先级分组管理
所有的中断的优先级进行分组管理。分成0-4组,分别确定了每组抢占优先级和响应优先级数值所占据的位数,在stm32l4xx_hal_cortex.h中有如下宏定义和说明。
/** @defgroup CORTEX_Preemption_Priority_Group CORTEX Preemption Priority Group * @{ */ #define NVIC_PRIORITYGROUP_0 ((uint32_t)0x00000007) /*!< 0 bit for pre-emption priority, 4 bits for subpriority */ #define NVIC_PRIORITYGROUP_1 ((uint32_t)0x00000006) /*!< 1 bit for pre-emption priority, 3 bits for subpriority */ #define NVIC_PRIORITYGROUP_2 ((uint32_t)0x00000005) /*!< 2 bits for pre-emption priority, 2 bits for subpriority */ #define NVIC_PRIORITYGROUP_3 ((uint32_t)0x00000004) /*!< 3 bits for pre-emption priority, 1 bit for subpriority */ #define NVIC_PRIORITYGROUP_4 ((uint32_t)0x00000003) /*!< 4 bits for pre-emption priority, 0 bit for subpriority */
每个中断都可以设置抢占优先级的等级数值和响应优先级的等级数值。这里需要理解清楚,抢占优先级和响应优先级的功能作用。抢占优先级高的中断可以打断抢占优先级低的中断,抢占优先级相同的中断是不能相互打断的。当抢占优先级相同的多个中断同时发生时,响应优先级高的中断会首先被响应。
中断优先级需要在硬件初始化阶段配置完成,一旦配置好后,就不能在修改。在HAL库中,调用如下函数进行配置
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
例如,如下代码,设置优先级管理组为4,
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
1.2 具体中断的配置
对于GPIO中断配置,可以使用HAL库中GPIO的初始化函数HAL_GPIO_Init,如下所示。
/** * @brief Initialize the GPIOx peripheral according to the specified parameters in the GPIO_Init. * @param GPIOx: where x can be (A..H) to select the GPIO peripheral for STM32L4 family * @param GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains * the configuration information for the specified GPIO peripheral. * @retval None */ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
HAL_GPIO_Init函数中的第二个参数GPIO_Init对应的结构体GPIO_InitTypeDef,如下所示。
typedef struct { uint32_t Pin; /*!< Specifies the GPIO pins to be configured. This parameter can be any value of @ref GPIO_pins */ uint32_t Mode; /*!< Specifies the operating mode for the selected pins. This parameter can be a value of @ref GPIO_mode */ uint32_t Pull; /*!< Specifies the Pull-up or Pull-Down activation for the selected pins. This parameter can be a value of @ref GPIO_pull */ uint32_t Speed; /*!< Specifies the speed for the selected pins. This parameter can be a value of @ref GPIO_speed */ uint32_t Alternate; /*!< Peripheral to be connected to the selected pins This parameter can be a value of @ref GPIOEx_Alternate_function_selection */ }GPIO_InitTypeDef;
在这个结构体中的成员变量Mode就是用来这是GPIO的模式的。这里的GPIO模式包括了如下13种模式,其中的后面6种模式是用来设置中断相关的模式的。
/** @defgroup GPIO_mode GPIO mode * @brief GPIO Configuration Mode * Elements values convention: 0xX0yz00YZ * - X : GPIO mode or EXTI Mode * - y : External IT or Event trigger detection * - z : IO configuration on External IT or Event * - Y : Output type (Push Pull or Open Drain) * - Z : IO Direction mode (Input, Output, Alternate or Analog) * @{ */ #define GPIO_MODE_INPUT (0x00000000u) /*!< Input Floating Mode */ #define GPIO_MODE_OUTPUT_PP (0x00000001u) /*!< Output Push Pull Mode */ #define GPIO_MODE_OUTPUT_OD (0x00000011u) /*!< Output Open Drain Mode */ #define GPIO_MODE_AF_PP (0x00000002u) /*!< Alternate Function Push Pull Mode */ #define GPIO_MODE_AF_OD (0x00000012u) /*!< Alternate Function Open Drain Mode */ #define GPIO_MODE_ANALOG (0x00000003u) /*!< Analog Mode */ #define GPIO_MODE_ANALOG_ADC_CONTROL (0x0000000Bu) /*!< Analog Mode for ADC conversion */ #define GPIO_MODE_IT_RISING (0x10110000u) /*!< External Interrupt Mode with Rising edge trigger detection */ #define GPIO_MODE_IT_FALLING (0x10210000u) /*!< External Interrupt Mode with Falling edge trigger detection */ #define GPIO_MODE_IT_RISING_FALLING (0x10310000u) /*!< External Interrupt Mode with Rising/Falling edge trigger detection */ #define GPIO_MODE_EVT_RISING (0x10120000u) /*!< External Event Mode with Rising edge trigger detection */ #define GPIO_MODE_EVT_FALLING (0x10220000u) /*!< External Event Mode with Falling edge trigger detection */ #define GPIO_MODE_EVT_RISING_FALLING (0x10320000u) /*!< External Event Mode with Rising/Falling edge trigger detection */ /** * @} */
例如将PA0设置为中断模式,且上升沿触发,对应的代码如下
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING; //设置模式为中断模式,且上升沿触发
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO的中断模式配置好之后,就可以被响应了。可以根据需要编写对应的中断响应函数。
1.3 中断响应对应关系
GPIO中断的响应的对应关系,如下图所示。
有16条中断线跟GPIO按照上图的分组关系相关联。例如PA1~PE1引脚上的中断会被在EXTI1这条中断线上被响应。而对应的中断号及中断响应函数又有一些特别的分组对应关系。EXTI0~EXTI4,这5条中断线都有各自独立的中断号及中断响应函数,而EXTI5-EXTI9,这5个中断线共用一个中断号及中断响应函数,EXTI10-EXTI15这5个中断线共用一个中断号及中断响应函数。
具体对应的中断号,可以在stm32l431xx.h中的IRQ_Type这个枚举定义中看到。这个枚举定义中,定义了所有中断的中断号,供编程使用。
举简单的例子说明一下。如果PA0引脚上产生中断了,你需要在EXTI0对应的那个中断函数中做处理。如果PA3引脚上产生中断了,你需要在EXTI3对应的那个中断函数中做处理。如果PA5,PA6,PC5,PC6上产生了中断,你需要在EXTI5-9对应的那一个中断函数中进行处理。
2. LiteOS系统应用GPIO中断
2.1 硬件中断管理功能的配置
要使用硬件中断,移植LiteOS的时候,需要在OS_CONFIG文件夹中的targetconfig.h文件中的如下代码段修改好配置。
/*============================================================================= Hardware interrupt module configuration =============================================================================*/ /** * @ingroup los_config * Configuration item for hardware interrupt tailoring */ #define LOSCFG_PLATFORM_HWI YES /** * @ingroup los_config * Maximum number of used hardware interrupts, including Tick timer interrupts. */ #define LOSCFG_PLATFORM_HWI_LIMIT 96
上面代码中的
#define LOSCFG_PLATFORM_HWI 后面的 YES,是表示要使用硬件中断。如果不用是NO。
#define LOSCFG_PLATFORM_HWI_LIMIT 后面数字的96, 目标硬件平台可有的最多硬件中断源数量,包括了时钟中断源。这个数值根据具体的目标硬件平台的中断源数量来确定。
2.2 GPIO中断模式的配置
在移植成功的LiteOS中,要使用GPIO的中断,就要在硬件初始化阶段对相关的引脚进行中断配置。配置的方法跟1.2具体中断配置中的方法相同。
2.3 LiteOS中的中断函数编写
在LiteOS中,可以自己定义个没有返回值、带参数或者不带参数的函数,作为某个中断响应的中断函数。如下IoT Link 模板工程中的os_interrupt_demo.c中的示例代码,如下
tatic Key1_interrupt_entry() { printf("KEY1 Interrupt entry OK!,sum1:%d\r\n",sum1++); __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_2); }
在自己定义的中断函数中,可以编写自己需要的处理程序代码。在编写自己的代码时候,时常要用到STM32相关的库函数。
2.4 中断的注册
调用LiteOS的osal_int_connect函数,将该中断及中断函数一起注册到LiteOS系统中。osal_int_connect函数在osal.c文件里面,代码如下
int osal_int_connect(int intnum, int prio, int mode, fn_interrupt_handle callback, void *arg) { int ret = -1; if((NULL != s_os_cb) &&(NULL != s_os_cb->ops) &&(NULL != s_os_cb->ops->int_connect)) { ret = s_os_cb->ops->int_connect(intnum, prio, mode, callback, arg); } return ret; }
其中,参数intnum 是要注册的中断号,这个中断号是STM32中每个中断元定义的中断号,在stm32l431xx.h中的IRQ_Type枚举类型中有具体的定义。例如在IRQ_Type枚举类型中,可以看到
EXTI0中断线对应的中断号是6,用EXTI0_IRQn来表示,如下代码示例。
EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
参数prio是这个中断的优先级。
参数mode,在这函数中暂时还没有使用。可以设置为0。
callback就是中断响应需要执行的中断函数的名称。
在IoT Link 模板工程中,有如下的参考代码。
#define KEY1_EXTI_IRQn EXTI2_IRQn osal_int_connect(KEY1_EXTI_IRQn, 2,0,Key1_IRQHandler,NULL);
这句话意思是将中断函数Key1_IRQHandler这个中断函数与EXTI2_IRQn这个中断号关联起来。这样中断线EXTI2上有中断信号产生时,就会自动调用Key1_IRQHandler这个中断函数。
3. GPIO中断检测及LCD显示的程序实例
以小熊派开发板为硬件平台,使用PA11和PB12引脚来做中断检测实验。这两个引脚在小熊派开发板的P5端子排的10和9号,预留出来给用户自己使用的。通常用户可以在此基础上扩展自己的应用功能。
使用IoT Link 中的使用参考模板os_interrupt_demo的模板工程建立实例工程。修改程序代码。程序的实现的主要功能是,记住中断检测的次数,并将次数显示在LCD屏幕上。
修改GPIO初始化函数MX_GPIO_INIT,增加对PA11和PB12引脚的初始化配置。如下
//配置PA11引脚为中断模式 GPIO_InitStruct.Pin = GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //配置PB12引脚为中断模式 GPIO_InitStruct.Pin = GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
修改os_interrupt_demo.c文件中的代码如下
uint32_t sum3; uint32_t UserKey1Number=0; uint32_t UserKey2Number=0; static int app_hello_world_entry() { while (1) { printf("Hello World! This is Bearpi!\r\n"); POINT_COLOR = RED; LCD_ShowNum(20, 90, UserKey1Number, 8, 24); LCD_ShowNum(20, 170, UserKey2Number, 8, 24); osal_task_sleep(50); } } //@brief 这函数处理EXTI10-15 的中断。 //PA11的中断连接到EXTI11 //PA12的中断连接到EXTI12,但是EXTI10~EXTI15 共用一个中断号,都在一个中断函数中处理 static UserKey1_Key2_entry() { int i=300000; //这三行代码目的是想做延时消抖用,暂时还没找到更好的在中断函数中用的延时函数。 while(i>0) i--; if(__HAL_GPIO_EXTI_GET_FLAG(GPIO_PIN_11) != 0x00u) // PA11产生了中断信号 { UserKey1Number++; __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_11); } if(__HAL_GPIO_EXTI_GET_FLAG(GPIO_PIN_12) != 0x00u) // PB12产生了中断信号 { UserKey2Number++; __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_12); } if(UserKey1Number >100 || UserKey2Number > 100 ) { UserKey1Number=0; UserKey2Number=0; sum3=0; } printf("UserKEY1_2 Interrupt entry OK!,sum3:%d,i:%d\r\n",sum3++,i); } int standard_app_demo_main() { LCD_Clear(WHITE); POINT_COLOR = BLUE; LCD_ShowString(20, 50, 240, 24, 24, "UserKey1Number is"); LCD_ShowString(20, 130, 240, 16, 24, "UserKey2Number is"); osal_task_create("helloworld",app_hello_world_entry,NULL,0x400,NULL,2); osal_int_connect( EXTI2_IRQn, 3, NULL, Key1_interrupt_entry, NULL); osal_int_connect( EXTI3_IRQn, 4, NULL, Key2_interrupt_entry, NULL); osal_int_connect( EXTI15_10_IRQn, 5, NULL, UserKey1_Key2_entry, NULL); return 0; }
编译下载运行,运行效果图如下所示
在IoT Serial 的调试串口中可以看到如下信息
4. 总结
STM32的中断有些复杂,理解清楚中断相关的概念及应用方法,会帮助在LiteOS系统顺利的使用它的中断。LiteOS系统接管硬件时,要先在target_config.h中简单配置一下,就可以使用硬件中断了。GPIO中断在使用时,要先在GPIO初始化中配置上它的中断模式,然后编写相关的中断函数,最后在LiteOS中调用os_int_connect函数,配置中断优先级,关联中断的中断号与中断函数,这样系统就可以自动调用中断函数。这种应用,比在STM32裸机上应用中断要简单容易一些。
中断的复杂性,导致应用和理解容易出现差错,如果内容如有问题欢迎大家提出讨论,分享,共同学习,共同进步。
- 点赞
- 收藏
- 关注作者
评论(0)