tag 标签: 中断

相关帖子
相关博文
  • 热度 3
    2025-6-12 10:11
    381 次阅读|
    1 个评论
    ESP32开发之GPIO中断
    电路图 GPIO的中断类型 相关API函数 应用举例 总结 电路图 在ESP32中内部有完整的控制电路,比如上下拉以及滤波器等,所以我们这里可以直接用一个微动开关连接到地。 GPIO的中断类型 GPIO_INTR_DISABLE 不使能中断 GPIO_INTR_POSEDGE 上升沿触发 GPIO_INTR_NEGEDGE 下降沿触发 GPIO_INTR_ANYEDGE 上升沿和下降沿都触发 GPIO_INTR_LOW_LEVEL 低电平触发 GPIO_INTR_HIGH_LEVEL 高电平触发 在GPIO中断应用中,一般使用上升沿或者下降沿触发 相关API函数 GPIO配置结构体 typedef struct { uint64_t pin_bit_mask; /*gpio引脚的位掩码 ,设置为(1ULLGPIO_NUM)*/ gpio_mode_t mode; /*GPIO模式,中断设置为输入模式 */ gpio_pullup_t pull_up_en; /*GPIO上拉使能 */ gpio_pulldown_t pull_down_en; /*GPIO下拉使能 */ gpio_int_type_t intr_type; /*GPIO中断类型 */ # if SOC_GPIO_SUPPORT_PIN_HYS_FILTER gpio_hys_ctrl_mode_t hys_ctrl_mode; /*GPIO迟滞过滤控制模式 */ # endif } gpio_config_t ; GPIO配置函数 esp_err_t gpio_config ( const gpio_config_t *pGPIOConfig) //参数为填充过的gpio_config_t结构体 //返回值:成功返回ESP_OK // 失败返回ESP_ERR_INVALID_ARG,表示参数错误 注册中断 //步骤一:安装中断服务,匹配一个gpio中断 esp_err_t gpio_install_isr_service ( int intr_alloc_flags //用于分配中断的标志,一般为ESP_INTR_FLAG_*,在 esp_intr_alloc.h定义 ) /*返回值: ESP_OK :成功 ESP_ERR_NO_MEM :没有内存安装这个服务 ESP_ERR_INVALID_STATE :中断服务已安装 ESP_ERR_NOT_FOUND :没有找到指定标志的空闲中断 ESP_ERR_INVALID_ARG :gpio错误 */ //步骤二:给gpio中断添加服务函数 esp_err_t gpio_isr_handler_add ( gpio_num_t gpio_num, //gpio编号 gpio_isr_t isr_handler, //中断服务函数 void *args //传递给中断服务函数的参数 ) 应用举例 /** * Copyright (C) 2024-2034 HalfMoon2. * All rights reserved. * * @file Filename without the absolute path * @brief Brief description * @author HalfMoon2 * @date 2025-06-10 * @version v0.1 * * @revision history: * 2025-06-10 - Initial version. */ # include stdio.h # include driver/gpio.h # include freertos/FreeRTOS.h # include freertos/task.h # include freertos/event_groups.h # include esp_log.h # define KEY_GPIO GPIO_NUM_7 # define LED_GPIO GPIO_NUM_4 # define EVENT_BIT0 (10) static EventGroupHandle_t gpio_event= NULL ; static void IRAM_ATTR gpio_isr_handler ( void *pvparam) { BaseType_t pxHigherPriorityTaskWoken= pdFALSE; //先储存下来,在此例程中不做处理 xEventGroupSetBitsFromISR (gpio_event,EVENT_BIT0,pxHigherPriorityTaskWoken); } void event_task ( void *pvparam) { while ( 1 ){ if ( xEventGroupWaitBits (gpio_event,EVENT_BIT0,pdTRUE,pdFALSE,portMAX_DELAY)== 0x01 ){ gpio_set_level (LED_GPIO, 1 ); vTaskDelay ( pdMS_TO_TICKS ( 100 )); gpio_set_level (LED_GPIO, 0 ); ESP_LOGI ( "key" , "Event Wait Success!!!" ); } } } /** * @brief GPIO初始化 */ void gpio_init ( void ) { /*配置LED所连接的GPIO*/ gpio_config_t led_config={ .mode=GPIO_MODE_OUTPUT, .pin_bit_mask=( 1ULL LED_GPIO), .pull_up_en=GPIO_PULLUP_DISABLE, .pull_down_en=GPIO_PULLDOWN_DISABLE, .intr_type=GPIO_INTR_DISABLE }; gpio_config (led_config); /*配置key所连接的GPIO*/ gpio_config_t key_config={ .mode=GPIO_MODE_INPUT, .pin_bit_mask=( 1ULL KEY_GPIO), .pull_up_en=GPIO_PULLUP_ENABLE, .pull_down_en=GPIO_PULLDOWN_DISABLE, .intr_type=GPIO_INTR_NEGEDGE }; gpio_config (key_config); /*创建事件组,用于发出中断事件*/ gpio_event= xEventGroupCreate (); /*任务创建 */ xTaskCreatePinnedToCore (event_task, "event_task" , 2048 , NULL , 10 , NULL , 1 ); /*安装中断服务,此函数进行中断寄存器初始化,并注册中断*/ gpio_install_isr_service ( 0 ); /*给特定GPIO添加中断服务函数 */ gpio_isr_handler_add (KEY_GPIO,gpio_isr_handler, NULL ); } void app_main ( void ) { gpio_init (); } 当每次按键按下,将触发中断服务函数,中断服务函数设置事件组,任务函数获取并处理事件。将得到如下情况: 总结 在中断服务函数中不要使用ESP_LOGI或者printf等打印函数,将报错 不能直接使用gpio_isr_register()函数,会报段错误,因为在注册之前需要清除中断寄存器等操作。可以通过分析gpio_install_isr_service()函数原型了解其中缘由。 GPIO中断服务函数前一定要添加IRAM_ATTR关键字,表示将此函数放在内部RAM中,未添加则将放在flash中,中断触发后会反应很慢。
  • 热度 17
    2022-5-17 08:53
    2723 次阅读|
    0 个评论
    如何触发STM32F103C8T6的外部中断?
    中断是MCU的特征之一,STM32F103C8T6也不例外。以下通过STM32F103C8T6 Blue Pill外接一个按钮,向MCU触发外部中断。 STM32F103C8T6中断特征 中断有硬件中断和软件中断两种,当一个中断发生时,相应的中断服务程序(ISR)和中断处理程序就开始运行了。其中,ISR的指令必须尽可能小,ISR中也不能出现delay ()之类的阻塞函数。 STM32 Blue Pill或者STM32F103C8T6 MCU指定NVIC来管理所有外部中断和外设中断,其应用采用了Arduino IDE。因此,我们可使用相同的句法来配置外部中断,这可通过以下称作 “attachInterrupt ()” 的句法给出: attachInterrupt(digitalPinToInterrupt(pin), ISR, mode); 该句法的第一个参数 digitalPinToInterrupt(pin) 代表预期外部中断的MCU引脚,例如,如果按钮连接于PA0,该参数就变成“digitalPinToInterrupt(PA0)”。 第二个参数是ISR函数,该函数必须具有 void 返回类型,不能代任何参数。 第三个函数是是触发中断的模式,例如,信号突变到哪个点位时,必须触发一个中断。Arduino环境下,该参数通常为5个用户预定义的常数,但STM32须从如下3个选项中选取: CHANGE: 在引脚数值改变时触发中断。 RISING: 当引脚数值从LOW 上升到 HIGH 时触发中断。 FALLING: 当引脚数值从 HIGH 回落到 LOW 时触发中断。 用按钮为STM32 MCU触发一个中断 本项目所需元件包括:STM32F103C8T6蓝丸版、按钮、USB-UART转换器、USB电缆,杜邦线。 首先,将按钮连接到 PA0 引脚,并通过一个4.7KΩ电阻器上拉到HIGH ,另一引脚接 GND。 以下代码用以激励 PA0 引脚的中断,button_ISR用来切换LED。 int ledPin = PC13; int buttonPin = PA0; int ledToggle; int previousState = HIGH; unsigned int previousPress = 0; volatile int buttonFlag; int buttonDebounce = 20; void setup() { pinMode(ledPin, OUTPUT); pinMode(buttonPin, INPUT); attachInterrupt(digitalPinToInterrupt(buttonPin), button_ISR, CHANGE); } void loop() { buttonDebounce && buttonFlag) { previousPress = millis(); if(digitalRead(buttonPin) == LOW && previousState == HIGH) { ledToggle =! ledToggle; digitalWrite(ledPin, ledToggle); previousState = LOW; } else if(digitalRead(buttonPin) == HIGH && previousState == LOW) { previousState = HIGH; } buttonFlag = 0; } } void button_ISR() { buttonFlag = 1; } 本项目演示了STM32F103C8T6 Blue Pill外部中断的操作,每按一次按钮就触发一次中断,STM32 MCU就执行一次简单ISR,返回来切换LED状态。
  • 热度 3
    2021-5-9 23:12
    1915 次阅读|
    0 个评论
    1.编写片上外设的正常工作的程序 将片上外设的功能配置OK(在查询方式下能够工作) 2和3只需要设置一次,所以在片上外设的初始化函数中编写 2.开启NVIC(在core_cm3.h或 core_cm4.h中找NVIC开头的函数 ) step1:设置优先级分组 step2:合成优先级并使用一个变量接收函数的返回值 step3:设置优先级到这个片上外设中 step4:使能这个片上外设的NVIC控制器 3.开启这个片上外设的模块级中断使能(在stm32提供的F1系列或F4系列的手册中的对应片上外设的寄存器中找) step1: 根据需要这个片上外设的各个中断使能位 4.编写中断服务函数 step1:清要实现的功能的必要的标志位 step2:把要在中断中执行的操作写在函数体中 以USART为例 1.正常初始化设备 2.之后将中断的步骤添加进去 NVIC_SetPriorityGrouping(7-2); NVIC_SetPriority(USART1_IRQn,NVIC_EncodePriority(7-2,2,1)); NVIC_EnableIRQ(USART1_IRQn); CR1 |= (1<<5); 3.编写中断服务函数 void USART1_IRQHandler(void) { SR &= (1<<5)!=0) { DR; } }
  • 热度 24
    2020-7-23 16:20
    2476 次阅读|
    0 个评论
    转载自 https://www.cnblogs.com/qiyuexin/p/8921718.html 目录 FAULTMASK PRIMASK BASEPRI BASEPRI_MAX 正文 1、总开关 每个CPU有一个中断总开关。通过CPU中断控制寄存器实现。Cortex-M的中断控制寄存器包括:FAULTMASK、PRIMASK、BASEPRI、BASEPRI_MAX。总开关的本质是变更当前执行优先级,根据Cortex-M的架构设计,只有优先级高于当前执行优先级的中断或异常才能抢占CPU。 回到顶部 FAULTMASK 设置为1后关闭所有中断和异常,包括HardFault异常,只有NMI和Reset可以得到响应。 回到顶部 PRIMASK 设置为1后关闭所有中断和除了HardFault异常外的所有其他异常,只有NMI、Reset和HardFault可以得到响应。 回到顶部 BASEPRI 设置为n后,屏蔽所有优先级数值大于等于n的中断和异常。Cortex-M的优先级数值越大其优先级越低。 回到顶部 BASEPRI_MAX 和BASEPRI类似,但有个限制,即后写入的优先级数值要比当前的BASEPRI值小才会起作用,否则不起作用。影响范围最广,影响CPU内的所有中断源。 事实上BASEPRI_MAX和BASSEPRI是操作同一个寄存器,不过BASEPRI_MAX是一个条件写指令,可以通过下列等效功能代码理解: // atomic related functions for unittest. extern uint8_t atomic_BASEPRI; // 用来模拟 BASEPRI 的值 // BASEPRI 设置 static inline uint8_t __set_BASEPRI(uint8_t prio) { atomic_BASEPRI = prio; } // BASEPRI_MAX 设置 static inline uint8_t __set_BASEPRI_MAX(uint8_t prio) { prio)) { atomic_BASEPRI = prio; } } 疑问:线程模式下,当前优先级和中断优先级的抢占优先级相同,但中断的子优先级比当前子优先级高,会怎么样?当前优先级也分抢占优先级和子优先级吗? 2、分开关 Cortex-M包括一个嵌套向量中断控制器NVIC,每个外设在NVIC中都有一个对应的位,用来控制该外设的中断。 ISER寄存器中外设对应位为1打开中断。 ICER寄存器中外设对应位为1关闭中断。 分开关只影响特定外设的中断。 3、源开关 外设通常有多个中断源,如接收到数据、发送完成、接收超时等等。外设通常提供中断使能寄存器控制哪些中断源产生时要向上汇报(向分开关汇报)。源开关与特定的外设相关,不同的外设会有不同的中断,有不同的中断控制寄存器。有些外设本身有总开关,该总开关控制该外设的所有中断。 源开关影响范围最窄,仅影响外设的某一种中断源。
  • 热度 19
    2020-7-23 11:27
    2658 次阅读|
    0 个评论
    熟悉ucos,或者读过Jean.J.Labrosse写过的ucos书籍的人,一定会知道ucos中著名的临界去管理宏:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。 同样是通过关中断来保护临界区,OS_ENTER_CRITICAL/OS_EXIT_CRITICAL一共实现了三种实现方式,如下所示: #if OS_CRITICAL_METHOD == 1 #define OS_ENTER_CRITICAL() __asm__("cli") #define OS_EXIT_CRITICAL() __asm__("sti") #endif #if OS_CRITICAL_METHOD == 2 #define OS_ENTER_CRITICAL() __asm__("pushf \n\t cli") #define OS_EXIT_CRITICAL() __asm__("popf") #endif #if OS_CRITICAL_METHOD == 3 #define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR()) #define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr)) #endif 第一种方式,OS_ENTER_CRITICAL()简单地关中断,OS_EXIT_CRITICAL()简单地开中断。这种方式虽然简单高效,但无法满足嵌套的情况。如果有两层临界区保护,在退出内层临界区时就会开中断,使外层的临界区也失去保护。虽然ucos的内核写的足够好,没有明显嵌套临界区的情况,但谁也无法保证一定没有,无法保证今后没有,无法保证在附加的驱动或什么位置没有,所以基本上第一种方法是没有人用的。 第二种方式,OS_ENTER_CRITICAL()会在关中断前保存之前的标志寄存器内容到堆栈中,OS_EXIT_CRITICAL()从堆栈中恢复之前保存的状态。这样就允许了临界区嵌套的情况。但现在看来,这种方法还存在很大的问题,甚至会出现致命的漏洞。 在OS_CRITICAL_METHOD=2的情况下,假设有如下代码: function_a() { int a=(1<<31); OS_ENTER_CRITICAL(); function_b(a); OS_EXIT_CRITICAL(); } 会出现什么情况?在我的实验中,OS_EXIT_CRITICAL()之后,会出现处理器异常。为什么会出现处理起异常,让我来模拟一下它的汇编代码。之所以是模拟,并非是我虚构数据,而是因为我实际碰到问题的函数复杂一些,理解起来就需要更多的代码。而这个问题是有普遍意义的,所以请允许我来浅显地揭示这个隐藏的bug。 function_a: push ebp mov ebp, esp sub esp, 8 mov 4(esp), 0x80000000 pushfd cli mov edi, 4(esp) mov (esp), edi call function_b popfd mov esp, ebp ret 这是参照了gcc编译结果的汇编模拟,无论是否加优化选项这一问题都存在。这个问题的起因很简单,gcc想聪明一点,一次把堆栈降个够,然后它就可以在栈上随意放参数去调用其他函数。尤其是在调用函数较多的时候,这种做法就更有意义。而且,gcc这种聪明与优化选项O好像没有太大关系,好像没有什么能禁止它这么做。但问题是,gcc不知道我们的OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()是操作了堆栈的,我尝试过使用__asm__ __volatile__("pushfd \n\tcli":::"memory")来通知gcc内存数据改变了,但显然gcc不认为堆栈也改变了。于是,OS_ENTER_CRITICAL()保存在栈上的状态就被冲掉了,比如被这里调用参数a的值。在恢复时,是否会引发异常,会引发什么异常,这个就要靠运气了。但我相信一个人的运气不会总是那么好的,所以最后别使用OS_CRITICAL_METHOD=2。 第三种,在关中断前,使用局部变量保存中断状态。这也是几乎所有实时操作系统共有的选择。但ucos是一朵奇葩,为了兼容前两种方式,OS_ENTER_CRITICAL()/ OS_EXIT_CRITICAL()宏定义并没有提供传递状态参数的功能。所以它的临界去必须这么用: function_a() { #if OS_CRITICAL_METHOD == 3 int cpu_sr; #endif int a = 1<<31; OS_ENTER_CRITICAL(); function_b(a); OS_EXIT_CRITICAL(); } 这种代码怎么看怎么别扭,可能是因为在函数体内加了宏定义吧。然后,第三种方法对同一个函数体内的嵌套临界区无法支持,这在一些很长大的函数中使用时或许会造成一定困扰。 好吧,如果有了问题,就要有解决方案,毕竟我不是为了让大家对ucos失去信心的。我们可以参考下一般的实时操作系统是如何实现关中断临界区的,就是以显式的方式用局部变量保存中断状态。 int int_lock() { int cpu_sr; __asm__ __volatile__("pushfd \n\t pop %0\n\t cli":"=r"(cpu_sr)); return cpu_sr; } void int_unlock(int cpu_sr) { __asm__ __volatile__("push %0\n\t popfd"::"r"(cpu_sr)); } function_a() { int a, cpu_sr; a=1<<31; cpu_sr = int_lock(); function_b(a); int_unlock(cpu_sr); } int_lock()和int_unlock()的可以用汇编更高效地实现,也可以选择只恢复中断标志的状态。这种方法让我们显示地管理状态保存的情况,我觉得至少要比宏定义清楚多了。 ———————————————— 版权声明:本文为CSDN博主「qb_2008」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qb_2008/java/article/details/7201340
相关资源