Skip to content

isr.c isr.h isr_config.h

学习笔记:TC264 中断配置与处理 (基于逐飞开源库)

阶段一:最基础的概念回顾 (与代码无关,但至关重要)

  1. 计算机为什么需要中断?

    • 想象一下你在专心做作业,但同时需要等快递。
      • 轮询方式: 你每隔几分钟就跑到窗边看看快递员来了没有。这很浪费你的时间和精力,做作业的效率很低。
      • 中断方式: 你告诉门卫,快递到了就按门铃通知你。你继续专心做作业,直到门铃响了(中断发生),你才暂停作业去拿快递(处理中断),拿完回来继续做作业(返回原程序)。
    • 微控制器 (MCU) 也是如此。它需要处理很多任务,有些任务是突发的、异步的(比如按键按下、串口收到数据)。如果MCU一直去“轮询”检查这些事件,就会浪费大量CPU时间。中断机制允许MCU在正常执行主程序时,被这些突发事件“打断”,高效地处理它们。
  2. 中断的基本流程:

    • 事件发生: 外部设备(如按键)或内部模块(如定时器)产生一个中断请求信号。
    • CPU响应: 如果CPU当前允许中断,并且该中断的优先级足够高,CPU会:
      1. 完成当前指令。
      2. 保存当前程序的执行位置(通常是程序计数器 PC)和一些重要的寄存器状态(上下文)。
      3. 跳转到该中断对应的**中断服务程序 (ISR)**。
    • 执行ISR: CPU执行ISR中的代码,处理引发中断的事件。
    • 返回: ISR执行完毕后,CPU恢复之前保存的上下文,并从被打断的地方继续执行主程序。
  3. 关键要素(再次强调,因为它们会在代码中体现):

    • 中断源: 是什么触发了中断?(定时器、GPIO、UART...)
    • 中断向量/入口: ISR代码放在哪里?MCU怎么找到它?
    • 中断优先级: 多个中断同时来,先理谁?
    • 中断使能/禁止: 我想不想理这个中断?或者暂时不想理所有中断?
    • 中断标志位: 我怎么知道中断真的发生了?处理完后要告诉MCU“我知道了,下次再来”。

阶段二:进入代码世界 - 从最简单的 isr.h 开始

  1. isr.h:一个“路标”和“公告板”
    • 目的: 这个文件的核心作用是“声明”。它告诉编译器:“嘿,我们项目里可能会用到一些和中断相关的东西,它们长这个样子,叫这个名字。”
    • #ifndef _isr_h / #define _isr_h / #endif
      • 这叫做“头文件保护符”或“Include Guard”。
      • 作用: 防止这个文件被意外地包含多次。如果一个C文件不小心 include 了两次同一个头文件,没有这个保护,头文件里的内容就会被复制两遍,可能导致重定义错误。
      • 类比: 像是在公告板上贴公告前,先看看是不是已经贴过同一张了。
    • #include "zf_common_headfile.h"
      • 这行代码告诉编译器:“在看我这个 isr.h 之前,你先去看看 zf_common_headfile.h 里面有什么。”
      • zf_common_headfile.h 通常是一个项目级别的“总头文件”或者“公共头文件”,它里面可能包含了:
        • 其他重要模块的头文件 (比如 GPIO驱动、定时器驱动等)。
        • 常用的数据类型定义 (比如 uint8, uint32_t 等,虽然标准库 stdint.h 也提供,但项目中可能自定义)。
        • 重要的宏定义 (比如 IFX_INTERRUPT 这个定义ISR的宏可能就在这里,或者它包含的更底层的英飞凌SDK头文件里)。
        • 一些全局函数或变量的 extern 声明。
      • 类比: 公告板上说:“详细内容请参考《项目规章总则》(zf_common_headfile.h)”。
    • 为什么这个 isr.h 看起来这么“空”?
      • 在这个具体的逐飞库例子中,isr.h 可能本身不需要声明太多东西,因为它依赖 zf_common_headfile.h 来提供大部分必要的声明。
      • 它的存在更多是为了**代码组织结构**:表明这里是与“isr”模块相关的接口。如果将来 isr.c 中有一些辅助函数需要被其他模块调用,那么这些函数的声明就会被添加到 isr.h 中。

阶段三:配置中断 - isr_config.h 的世界

  1. isr_config.h:中断的“属性配置单”
    • 目的: 集中管理所有中断源的配置参数,主要是**优先级 (Priority)** 和 服务提供者 (Service Provider/TOS)。它使用宏定义来实现,方便用户修改和查看。
    • 核心规则(非常重要!):
      • “中断优先级不能设置为相同值,所有中断优先级都必须设置为不一样的值。”
        • 这是这个库或者TC264中断控制器的一个硬性规定。如果两个中断优先级一样,当中断仲裁时可能会出问题。
        • 类比: 两个同样紧急的任务,老板不知道先派给谁。
    • 参数解读:
      • ISR_PRIORITY (中断优先级):
        • 数值范围:1-255。
        • 关键:0 表示不开启这个中断。 这是禁用某个中断的常用方法。
        • 关键:255 是最高优先级。 (与STM32等MCU相反,要记住!)
        • 类比: 任务的紧急程度,255最急,1最不急,0表示“这个任务不用做了”。
      • INT_SERVICE (中断服务者):
        • IfxSrc_Tos_cpu0: 这个中断由CPU0来处理。
        • IfxSrc_Tos_cpu1: 这个中断由CPU1来处理 (TC264是双核MCU)。
        • IfxSrc_Tos_dma: 这个中断由DMA控制器来处理 (CPU不直接参与,DMA处理完可能会再触发一个DMA完成中断给CPU)。
        • 特殊: 如果设为 IfxSrc_Tos_dma,那么对应的 ISR_PRIORITY 只能是 0-47。
        • 类比: 任务派给谁?A员工(CPU0),B员工(CPU1),还是机器人(DMA)?
    • 如何阅读和修改这个文件?
      • 找到你需要的中断源: 文件按照外设类型(PIT、GPIO、DMA、UART)对中断进行了分组。
      • 确定服务者: 根据你的需求,决定这个中断应该由CPU0、CPU1还是DMA处理。通常,大多数应用层中断由CPU0处理。一些高性能数据传输可能由DMA处理。
      • 分配优先级:
        • 最重要的原则:不能重复!
        • 根据任务的实时性要求分配。实时性要求高的,优先级设高一些 (更接近255)。
        • 例如:控制电机PWM的定时器中断可能比串口接收一个调试字符的中断优先级要高。
        • 按键扫描的定时器中断,如果不需要非常快速的响应,可以设一个较低的优先级。
      • 示例:按键扫描的定时器中断 (假设用 CCU6_0_CH0)
        #define CCU6_0_CH0_INT_SERVICE  IfxSrc_Tos_cpu0     // 按键扫描让CPU0处理
        #define CCU6_0_CH0_ISR_PRIORITY 30              // 给它一个较低的优先级30
        
      • 示例:摄像头PCLK触发DMA (假设用 EXTI_CH2_CH6)
        #define EXTI_CH2_CH6_INT_SERVICE IfxSrc_Tos_dma     // PCLK信号直接控制DMA
        #define EXTI_CH2_CH6_INT_PRIO   5                   // DMA中断优先级设为5 (0-47之间)
        
    • 学习点: 这个文件是用户与中断系统交互的主要入口之一。理解每个宏的意义和约束条件是正确配置中断的前提。

阶段四:中断的执行者 - isr.c 的核心

  1. isr.c:中断服务程序的“大本营”
    • 目的: 包含所有中断服务程序 (ISR) 的具体实现代码。当一个中断发生,CPU最终会跳转到这里执行对应的函数。
    • #include "isr_config.h"#include "isr.h"
      • 引入配置参数 (优先级等) 和可能的声明。
    • 中断嵌套的关键:interrupt_global_enable(0);
      • TC264特性: 进入ISR后,硬件会自动关闭全局中断,防止其他中断打扰。
      • interrupt_global_enable(0); 的作用: 重新开启全局中断。
      • 为什么需要? 如果不开启,那么即使一个更高优先级的中断发生了,也无法抢占当前正在执行的低优先级ISR。这可能导致高优先级任务响应延迟。
      • 何时使用? 如果你的ISR执行时间很短,并且不希望被其他中断打断,可以不开启。如果ISR执行时间较长,或者有更高优先级的实时任务,就应该在ISR开头开启中断嵌套。
      • 参数 0 通常指CPU核心0。
      • 类比: 你正在处理一个普通任务,这时电话响了(更高优先级中断)。如果允许嵌套,你会接电话;如果不允许,你会等当前任务做完再说。
    • ISR的定义方式:IFX_INTERRUPT(isr_name, core_id, priority_macro)
      • 这是英飞凌提供的标准宏,用于定义一个ISR。
      • isr_name: 你给这个ISR起的名字,例如 cc60_pit_ch0_isr
      • core_id: 通常是 0,表示这个ISR在CPU0上执行。
      • priority_macro: isr_config.h 中读取对应的优先级宏,例如 CCU6_0_CH0_ISR_PRIORITY。这就是配置与实现关联的地方!
      • 编译器魔法: 这个宏会做一些底层工作,将 isr_name 这个函数注册到中断向量表中,并与指定的优先级和核心关联起来。
    • ISR 的标准三步曲:
      1. (可选)开启中断嵌套: interrupt_global_enable(0);
      2. 清除中断标志位: 极其重要! 例如 pit_clear_flag(CCU60_CH0);exti_flag_clear(ERU_CH0_REQ0_P15_4);
        • 为什么? 当一个中断事件发生时,硬件会设置一个“标志位”,表示“嘿,我这儿有事!”。CPU响应中断后,这个标志位通常不会自动清除。如果你在ISR里不手动清除它,那么ISR执行完毕返回后,CPU会发现标志位还在,以为又来了一个新的中断,于是又跳进同一个ISR,形成死循环!
        • 类比: 快递员按了门铃(设标志),你开门拿了快递(处理中断),但你没告诉门卫你已经拿了(没清标志),门卫以为你没听到,又按了一遍门铃...
      3. 执行中断处理任务: 调用具体的业务逻辑函数,例如 key_IRQHandler();camera_vsync_handler(); 等。
    • 共享中断的处理 (以 exti_ch0_ch4_isr 为例):
      • 有些中断源在硬件上共享同一个中断请求线和同一个ISR入口。
      • 如何在ISR中区分? 通过读取更具体的子状态/子标志位。
        IFX_INTERRUPT(exti_ch0_ch4_isr, 0, EXTI_CH0_CH4_INT_PRIO)
        {
            interrupt_global_enable(0);
            if(exti_flag_get(ERU_CH0_REQ0_P15_4))  // 是不是通道0触发的?
            {
                exti_flag_clear(ERU_CH0_REQ0_P15_4); // 清除通道0的标志
                // ... 处理通道0的事件 ...
            }
            if(exti_flag_get(ERU_CH4_REQ13_P15_5))  // 是不是通道4触发的?
            {
                exti_flag_clear(ERU_CH4_REQ13_P15_5); // 清除通道4的标志
                // ... 处理通道4的事件 ...
            }
        }
        
      • 类比: 一个办公室只有一个电话分机(共享ISR入口),但可以区分是A部门打来的还是B部门打来的(检查具体标志)。
    • 回调函数的使用:
      • ISR中调用了大量的 _handler()_callback() 函数。
      • 好处: 保持ISR本身逻辑清晰简单,主要负责中断框架(开嵌套、清标志)。具体的业务逻辑封装在专门的函数中,方便维护和复用。
      • 类比: 你是项目经理(ISR),接到客户电话(中断)。你记录下来(清标志),然后把具体任务派给相应的工程师(回调函数)去处理。

阶段五:串联起来看 - 整个流程

  1. 编译阶段:

    • 编译器读取 isr_config.h 中的宏定义。
    • 当编译 isr.c 时,IFX_INTERRUPT 宏会使用 isr_config.h 中定义的优先级。
    • 链接器会将ISR的地址放入中断向量表。
  2. 初始化阶段 (通常在 main.c 或专门的初始化模块中,这部分代码未提供,但可以推测):

    • 使能对应外设的时钟。
    • 配置外设工作模式 (例如,配置定时器周期,配置GPIO为输入模式等)。
    • 关键:配置中断控制器 (Interrupt Controller / VIC - Vector Interrupt Controller):
      • 根据 isr_config.h 中定义的 INT_SERVICE (服务者),将中断请求路由到指定的CPU核心或DMA。
      • 根据 isr_config.h 中定义的 ISR_PRIORITY,设置该中断在中断控制器中的优先级。
      • 使能该中断源在中断控制器中的响应。 (仅仅在 isr_config.h 中设置优先级不为0还不够,通常还需要一步显式的使能操作)。
    • 使能全局中断 (允许CPU响应任何已配置并使能的中断)。
  3. 运行阶段:

    • 主程序正常运行。
    • 事件发生: 例如,CCU6_0_CH0 定时器溢出。
    • 硬件仲裁: 中断控制器检测到中断请求,检查其优先级,并与当前CPU状态比较。
    • CPU响应:
      • 如果允许,CPU保存上下文,跳转到 isr.ccc60_pit_ch0_isr 函数的地址。
    • 执行 cc60_pit_ch0_isr
      1. interrupt_global_enable(0); (如果写了这句)
      2. pit_clear_flag(CCU60_CH0);
      3. key_IRQHandler();
      4. get_key_msg(&keymsg);
    • ISR返回: CPU恢复上下文,返回主程序继续执行。

系统性掌握的关键点总结:

  • 分离思想: 配置 (isr_config.h) 和实现 (isr.c) 分离。
  • 核心参数: 优先级、服务者。
  • TC264特性: 优先级规则、服务者选项、默认不嵌套。
  • ISR标准流程: (开嵌套) -> 清标志 -> 处理。
  • 宏的作用: IFX_INTERRUPT 定义ISR,配置宏提供参数。
  • 初始化是前提: 中断能工作,离不开正确的初始化配置。

附录:四种中断

前提回顾:

  • 所有这些中断的**优先级**和**服务提供者 (CPU0/CPU1/DMA)** 都在 isr_config.h 中预先定义好了。
  • 所有ISR都使用了 IFX_INTERRUPT 宏来定义,并关联了 isr_config.h 中的优先级。
  • 大部分ISR在开头都调用了 interrupt_global_enable(0); 来允许中断嵌套。
  • **清除中断标志位**是每个ISR中必不可少的操作。

1. PIT 中断 (Periodic Interval Timer / CCU6 模块)

  • 是什么?
    • PIT是一种定时器,可以配置为在固定的时间间隔后产生中断。
    • 在TC264中,CCU6 (Capture Compare Unit 6) 模块通常用来实现PIT功能。一个CCU6模块可能有多个通道,每个通道可以独立配置为一个定时器。
  • 典型应用场景:
    • 周期性任务调度: 如操作系统的心跳时钟、任务切换。
    • 按键扫描: 每隔几十毫秒检查一次按键状态。
    • ADC采样触发: 定时启动ADC转换。
    • LED闪烁控制: 定时改变LED状态。
    • 传感器数据周期性读取。
  • isr.c 中的代码分析 (以 cc60_pit_ch0_isr 为例):
    IFX_INTERRUPT(cc60_pit_ch0_isr, 0, CCU6_0_CH0_ISR_PRIORITY)
    {
        interrupt_global_enable(0);                     // 开启中断嵌套
        pit_clear_flag(CCU60_CH0);                      // 清除CCU6模块0通道0的PIT中断标志
    
        // --- 中断处理逻辑 ---
        key_IRQHandler();                               // 调用按键状态处理函数 (可能是扫描和消抖)
        get_key_msg(&keymsg);                           // 获取处理后的按键消息
    }
    
  • 深入理解:
    • CCU6_0_CH0_ISR_PRIORITY: 这个中断的优先级是在 isr_config.h 中定义的 CCU6_0_CH0_ISR_PRIORITY
    • pit_clear_flag(CCU60_CH0): 这是清除中断标志的关键。CCU60_CH0 很可能是一个宏,代表CCU6模块0的通道0。函数 pit_clear_flag() 是逐飞库提供的用于清除特定PIT通道中断标志的接口。
    • key_IRQHandler()get_key_msg(&keymsg):
      • 这两行代码表明 CCU6_0_CH0 这个定时器中断被用于**按键处理**。
      • key_IRQHandler() 可能负责:
        • 读取按键引脚的当前电平。
        • 进行软件消抖(因为机械按键按下和弹起时会有抖动)。
        • 判断按键状态(按下、弹起、长按)。
      • get_key_msg(&keymsg) 可能负责:
        • key_IRQHandler() 处理得到的按键事件(例如哪个键,什么状态)打包成一个消息结构体 keymsg
        • 这个 keymsg 可能会被放入一个按键消息队列,供主程序或其他任务读取和响应。
    • 其他的PIT ISR (cc60_pit_ch1_isr, cc61_pit_ch0_isr, cc61_pit_ch1_isr):
      • 它们都遵循相同的结构:开嵌套、清标志。
      • 内容是空的,意味着这些定时器通道目前被配置了中断,但没有分配具体的处理任务。开发者可以根据需要在这里添加代码。
      • 思考: 如果这些通道暂时不用,更好的做法是在 isr_config.h 中将它们的 ISR_PRIORITY 设为 0,以完全禁用中断,避免不必要的CPU开销。

2. 外部中断 (EXTI / ERU - Event Request Unit)

  • 是什么?
    • 外部中断是由MCU外部引脚上的电平变化(上升沿、下降沿、双边沿、高电平、低电平)触发的中断。
    • 在TC264中,ERU模块负责处理外部事件请求。
  • 典型应用场景:
    • 按键输入: 按键按下或弹起时触发中断,比定时扫描更实时。
    • 传感器信号检测: 例如红外对管检测到物体、编码器脉冲计数、陀螺仪数据准备好信号。
    • 外部设备通信握手: 例如接收外部模块发来的“准备好”信号。
    • 触发其他模块: 例如,外部信号触发ADC转换或DMA传输。
  • isr.c 中的代码分析 (以 exti_ch1_ch5_isr 为例):
    IFX_INTERRUPT(exti_ch1_ch5_isr, 0, EXTI_CH1_CH5_INT_PRIO)
    {
        interrupt_global_enable(0);                     // 开启中断嵌套
    
        if(exti_flag_get(ERU_CH1_REQ10_P14_3))          // 检查是否是ERU通道1 (连接到P14.3引脚) 触发的中断
        {
            exti_flag_clear(ERU_CH1_REQ10_P14_3);       // 清除通道1的中断标志
    
            tof_module_exti_handler();                  // 调用ToF (Time of Flight) 模块的外部中断处理函数
        }
    
        if(exti_flag_get(ERU_CH5_REQ1_P15_8))           // 检查是否是ERU通道5 (连接到P15.8引脚) 触发的中断
        {
            exti_flag_clear(ERU_CH5_REQ1_P15_8);        // 清除通道5的中断标志
    
            // ... 通道5对应的处理逻辑 (此处为空) ...
        }
    }
    
  • 深入理解:
    • 共享中断: exti_ch1_ch5_isr 这个ISR同时服务于ERU通道1和通道5。这是因为在硬件上,这两个通道可能共享同一个中断请求线到CPU。
    • exti_flag_get(ERU_CHX_REQX_PXX_X): 这个函数用于获取特定ERU通道的中断标志状态。
      • ERU_CH1_REQ10_P14_3: 这个宏可能代表ERU的通道1,它是由某个请求源(REQ10)触发的,并且这个请求源连接到了MCU的P14.3引脚。
    • exti_flag_clear(...): 清除对应通道的外部中断标志。必须在判断之后,针对触发的通道进行清除。
    • tof_module_exti_handler(): 这表明ERU通道1 (P14.3引脚) 可能连接了ToF测距模块的中断输出引脚。当ToF模块完成一次测量或者有特定事件发生时,会通过这个引脚产生一个中断信号,通知MCU来读取数据或进行相应处理。
    • exti_ch0_ch4_isrexti_ch3_ch7_isr:
      • exti_ch0_ch4_isr: 也是一个共享ISR,但其处理逻辑为空。
      • exti_ch3_ch7_isr:
        • 通道3 (ERU_CH3_REQ6_P02_0) 调用 camera_vsync_handler()。这非常典型,摄像头的垂直同步信号 (VSYNC) 常常用作外部中断,标志着一帧图像的开始或结束,MCU可以据此进行图像采集的同步。
        • 通道7的处理逻辑为空。
    • 被注释掉的 exti_ch2_ch6_isr:
      // 由于摄像头pclk引脚默认占用了 2通道,用于触发DMA,因此这里不再定义中断函数
      // IFX_INTERRUPT(exti_ch2_ch6_isr, 0, EXTI_CH2_CH6_INT_PRIO)
      
      这个注释非常重要!它解释了为什么ERU通道2和6的ISR被注释掉了。
      • 摄像头PCLK (Pixel Clock):像素时钟信号,每来一个脉冲通常代表一个像素数据有效。
      • 触发DMA: PCLK信号的变化非常快,如果用CPU中断来处理每个像素,会给CPU带来巨大负担。更高效的做法是让PCLK信号直接触发DMA控制器,DMA会自动将像素数据从摄像头接口搬运到内存,CPU只在整行或整帧数据传输完成后才需要介入。
      • isr_config.h 中的对应配置: EXTI_CH2_CH6_INT_SERVICE 被设置为了 IfxSrc_Tos_dma。这意味着ERU通道2/6的事件请求直接由DMA服务,而不是产生一个CPU中断去执行这个被注释掉的ISR。

3. DMA 中断 (Direct Memory Access)

  • 是什么?
    • DMA是一种允许外设直接与内存之间传输数据,而无需CPU持续干预的技术。
    • 当DMA完成一次预设的数据传输任务(例如,传输了指定数量的字节)后,它可以产生一个DMA中断,通知CPU数据已经准备好或传输已完成。
  • 典型应用场景:
    • 高速数据采集: ADC连续采样、摄像头图像数据采集。
    • 高速数据传输: SPI/I2C 大数据块传输、串口数据块发送/接收。
    • 内存到内存的数据复制。
  • isr.c 中的代码分析 (以 dma_ch5_isr 为例):
    IFX_INTERRUPT(dma_ch5_isr, 0, DMA_INT_PRIO)
    {
        interrupt_global_enable(0);                     // 开启中断嵌套
        // 注意:DMA通道的中断标志清除通常由DMA控制器硬件或其驱动库函数在内部处理,
        // 或者在回调函数 camera_dma_handler() 中处理。
        // 如果需要手动清除,这里应该有类似 dma_clear_transfer_complete_flag(DMA_CHANNEL_5); 的调用。
    
        camera_dma_handler();                           // 摄像头采集完成统一回调函数
    }
    
  • 深入理解:
    • DMA_INT_PRIO: 这个中断的优先级是在 isr_config.h 中定义的 DMA_INT_PRIO
    • dma_ch5_isr: 表明这是DMA控制器某个通道 (这里假设是通道5,具体通道号需要看DMA初始化代码) 完成传输后产生的中断。
    • camera_dma_handler():
      • 这个回调函数被调用,说明这次DMA传输与摄像头数据采集有关。
      • 通常,在摄像头PCLK信号的驱动下,DMA控制器会将摄像头输出的像素数据一行一行或一帧一帧地搬运到内存中的图像缓冲区。当DMA搬运完预设的数据量(比如一行或一帧)后,就会触发这个DMA完成中断。
      • camera_dma_handler() 内部可能做的事情:
        • 标记当前行或帧数据已准备好。
        • 如果配置了双缓冲或多缓冲,则切换DMA的目标缓冲区,并启动下一次DMA传输。
        • 通知图像处理任务开始处理已接收的数据。
        • 清除DMA通道的中断标志(如果驱动没有自动清除)。
    • DMA中断与ERU触发DMA的关系:
      • 回想 exti_ch2_ch6_isr 被注释掉的原因:ERU通道2/6的事件(如摄像头PCLK)**触发**DMA开始工作。
      • 这里的 dma_ch5_isr 是DMA工作**完成**后产生的中断。
      • 这是一个典型的协同工作流程:外部事件 (PCLK) -> 触发DMA传输 -> DMA传输完成 -> 触发DMA完成中断 -> CPU处理。

4. 串口中断 (UART / ASCLIN - Asynchronous/Synchronous Controller Interface)

  • 是什么?
    • 串口是一种常用的串行通信接口。
    • 串口中断通常有以下几种:
      • 接收中断 (RX Interrupt): 当串口接收到一个或多个字节数据并存入接收缓冲器时触发。
      • 发送中断 (TX Interrupt): 当串口发送缓冲器为空(或者达到某个低水位),可以发送更多数据时触发。
      • 错误中断 (Error Interrupt): 当通信过程中发生错误(如帧错误、奇偶校验错误、上溢错误)时触发。
  • 典型应用场景:
    • 与PC或其他设备通信: 发送调试信息、接收控制指令。
    • 连接各种模块: GPS模块、蓝牙模块、WiFi模块、某些传感器。
  • isr.c 中的代码分析:

    • 接收中断 (例:uart0_rx_isr)

      IFX_INTERRUPT(uart0_rx_isr, 0, UART0_RX_INT_PRIO)
      {
          interrupt_global_enable(0);                     // 开启中断嵌套
      
          // 串口接收中断标志的清除通常在具体处理函数内部,
          // 例如读取了接收数据寄存器后,标志位会自动清除或需要手动清除。
      #if DEBUG_UART_USE_INTERRUPT                        // 条件编译:如果开启了debug串口中断
              debug_interrupr_handler();                  // 调用debug串口接收处理函数
      #endif
      }
      

      • debug_interrupr_handler(): 这个函数内部会:
        1. 读取串口0的数据寄存器,获取接收到的字节。
        2. 将字节存入一个环形缓冲区 (ring buffer) 或其他数据结构。
        3. 清除串口0的接收中断标志。
      • 其他RX ISR (uart1_rx_isr, uart2_rx_isr, uart3_rx_isr):
        • camera_uart_handler(): UART1用于摄像头参数配置。
        • wireless_module_uart_handler(): UART2用于无线模块通信。
        • gnss_uart_callback(): UART3用于GPS/GNSS模块数据接收。
        • 它们都遵循相似的逻辑:在回调函数中读取数据并处理。
    • 发送中断 (例:uart0_tx_isr)

      IFX_INTERRUPT(uart0_tx_isr, 0, UART0_TX_INT_PRIO)
      {
          interrupt_global_enable(0);                     // 开启中断嵌套
          // TX ISR 为空
      }
      

      • TX ISR为空的意义:
        • 轮询发送或少量数据发送: 如果每次只发送少量数据,或者对实时性要求不高,程序可能会直接将数据写入发送寄存器并等待发送完成(轮询),而不使用中断。
        • 发送逻辑在别处: 发送大量数据时,通常会有一个发送缓冲区。主程序将数据放入缓冲区,然后启动发送。TX中断在发送数据寄存器为空时触发,ISR从缓冲区取出下一个字节发送,直到缓冲区为空,则关闭TX中断。这部分逻辑可能封装在底层的串口驱动函数中,而不是直接暴露在ISR里。
        • 逐飞库的实现: 逐飞的串口发送函数(如 uart_send_byte, uart_send_string)可能内部处理了发送完成的等待或使用了非中断方式。如果需要中断驱动的连续发送,可能需要调用特定的API来使能TX中断,并在ISR中填充发送逻辑。
    • 错误中断 (例:uart0_er_isr)

      IFX_INTERRUPT(uart0_er_isr, 0, UART0_ER_INT_PRIO)
      {
          interrupt_global_enable(0);                     // 开启中断嵌套
          IfxAsclin_Asc_isrError(&uart0_handle);        // 调用英飞凌SDK提供的标准错误处理函数
      }
      

      • IfxAsclin_Asc_isrError(&uartX_handle): 这是英飞凌官方SDK提供的函数,用于处理ASCLIN模块的各种错误(帧错误、奇偶校验错误、接收缓冲区溢出等)。它会读取错误状态寄存器,并清除错误标志。uartX_handle 是对应串口模块的句柄或状态结构体。

总结与对比:

中断类型 触发源 典型应用 isr.c 中关键点
PIT 定时器周期到达/溢出 周期性任务、按键扫描、定时采样 pit_clear_flag(), 调用周期性任务处理函数
外部中断 外部引脚电平变化 (边沿/电平) 按键、传感器信号、外部设备同步、触发DMA exti_flag_get(), exti_flag_clear(), 共享中断判断,调用事件处理函数
DMA DMA数据块传输完成 高速数据采集/传输 (摄像头, ADC, SPI) 调用DMA完成回调函数 (如camera_dma_handler()),标志位清除可能在回调或驱动内部
串口 RX:数据到达; TX:发送缓冲空; ER:通信错误 与PC/模块通信、调试信息、GPS、蓝牙等 RX:调用数据接收处理函数; TX:通常为空(或有高级发送逻辑); ER:调用SDK错误处理函数

通过这样的分类和对比,你应该对这些不同类型中断的用途和在 isr.c 中的实现方式有了更清晰的认识。记住,核心思想都是响应事件、清除标志、执行任务。具体实现则根据外设特性和应用需求而定。