当前位置: 首页 > article >正文

C++编程:嵌入式Linux-ARM与外设中断交互的程序设计

文章目录

    • 0. 引言
    • 1. 设备与处理器中断交互机制
      • 1.1 交互时序图
      • 1.2 时序图说明
      • 1.3 用户空间中断处理方法
    • 2. 中断模块设计要点
    • 3. 代码说明
      • 3.1 `Interrupts` 类
      • 3.2 中断处理
      • 3.3 `start` 方法

0. 引言

本文介绍在 Linux-ARM 系统中利用中断与外设(如 DSP、DAC、扫描仪等)交互的模块,实现低延迟的中断响应服务。外设通过 UIO 驱动暴露 /dev/uio 设备节点,用户空间程序可以通过这些节点来处理中断。

本方案将使用到select,select的高效使用请:Linux编程:使用 select高效的 UART 通信

1. 设备与处理器中断交互机制

  • 设备侧(裸机程序/外设)

    • 当设备完成任务或发生事件时,触发硬件中断信号。
    • 中断信号通过硬件线路发送至中断控制器。
  • 处理器侧(运行操作系统)

    • 中断控制器接收信号,判断中断类型和优先级。
    • 操作系统内核处理并调度相应的中断服务程序(ISR)。
    • ISR 可能位于内核空间,也可能通过 /dev/uio 让用户空间程序处理。

1.1 交互时序图

设备 (裸机程序/外设) 中断控制器 处理器 (操作系统) 中断服务程序 设备运行裸机程序/外设 传递中断信号 发送中断请求 (IRQ) 中断处理入口 调用中断服务程序 (ISR) 处理中断事件 中断处理完成 通知中断处理完成 中断响应完成 设备继续执行 设备 (裸机程序/外设) 中断控制器 处理器 (操作系统) 中断服务程序

1.2 时序图说明

  • 设备触发中断:设备触发硬件中断信号,中断信号通过中断线传递给控制器
  • 中断控制器处理:控制器生成中断请求并发送给处理器。
  • 处理器接收中断:操作系统接收到中断请求并暂停当前任务。
  • 调用 ISR:操作系统调用对应的中断服务程序(ISR)。
  • 处理中断:ISR 执行预定义的处理逻辑。
  • 中断处理完成:ISR 处理完毕后,返回内核。
  • 通知控制器:处理器通知中断控制器处理完成。
  • 设备继续执行:设备得到响应后继续运行。

1.3 用户空间中断处理方法

在用户空间处理中断时,通常使用 UIO(Userspace I/O)机制。设备驱动将中断映射到 /dev/uioX 设备文件,用户空间程序通过 select 等系统调用来等待并处理中断。

// 用户空间中断处理示例
int fd = open("/dev/uio0", O_RDWR);
struct pollfd fds;
fds.fd = fd;
fds.events = POLLIN;

while (true) {
    int ret = poll(&fds, 1, -1);
    if (ret > 0) {
        uint32_t info;
        read(fd, &info, sizeof(info));  // 读取中断信息
        // 处理中断事件
        // ...

        // 重新使能中断
        write(fd, &info, sizeof(info));
    }
}

2. 中断模块设计要点

中断管理模块的设计包括以下要点:

  1. 线程优先级设置:使用 sched_setscheduler 设置线程的优先级和调度策略。
  2. 实时线程:通过 SCHED_FIFOSCHED_RR 确保线程能够及时获得 CPU 时间片。
  3. 中断信号处理:确保实时线程能够正确处理中断信号,避免任务被不必要地打断。

以下展示了如何通过 /dev/uio 设备文件接收中断,并利用 select 等待中断。

#include <fcntl.h>
#include <sys/select.h>
#include <unistd.h>
#include <functional>
#include <unordered_map>
#include <string>
#include <iostream>
#include <thread>
#include <sched.h>
#include <sys/types.h>
#include <pthread.h>

class Interrupts {
 public:
  // 构造函数,初始化成员变量
  Interrupts();

  // 中断初始化
  int init();

  // 启动中断处理线程
  void start();

  // 注册中断并连接到回调函数
  int registerInterrupt(const std::string& interrupt_name, 
                        std::function<void(void)> interrupt_handler);

  // 等待中断事件
  int waitForInterrupt();

  // 处理中断事件
  int processInterrupts();

 private:
  // 保存文件描述符与回调函数的映射
  std::unordered_map<int, std::function<void(void)>> interrupt_handlers_;

  // 最大文件描述符
  int max_interrupt_fd_;

  // 文件描述符集合
  fd_set master_set_;
  fd_set backup_set_;

  // 实时线程优先级
  static constexpr int kInterruptThreadPriority = 50;  // 设置线程优先级,假设为50(越高越优先)
};

// 构造函数,初始化成员变量
Interrupts::Interrupts() : max_interrupt_fd_(-1) {
  FD_ZERO(&master_set_);
  FD_ZERO(&backup_set_);
}

// 初始化中断源
int Interrupts::init() {
  std::function<void(void)> dma_handler = []() {
    fprintf(stdout, "DMA interrupt triggered!\n");
  };

  if (registerInterrupt("dma_irq", dma_handler) < 0) {
    fprintf(stderr, "Failed to register DMA interrupt!\n");
    return -1;
  }

  return 0;
}

// 注册中断
int Interrupts::registerInterrupt(const std::string& interrupt_name, 
                                  std::function<void(void)> interrupt_handler) {
  std::string interrupt_path = "/dev/" + interrupt_name;
  int fd = open(interrupt_path.c_str(), O_RDWR);
  if (fd < 0) {
    fprintf(stderr, "Failed to open interrupt device: %s\n", interrupt_path.c_str());
    return -1;  // 打开文件失败
  }

  uint32_t info = 1;  // 解锁中断
  if (write(fd, &info, sizeof(info)) != sizeof(info)) {
    fprintf(stderr, "Failed to unlock interrupt: %s\n", interrupt_path.c_str());
    close(fd);
    return -1;  // 写入失败
  }

  FD_SET(fd, &master_set_);
  if (fd > max_interrupt_fd_) {
    max_interrupt_fd_ = fd;
  }

  interrupt_handlers_[fd] = interrupt_handler;
  return 0;
}

// 设置线程的调度策略和优先级
void Interrupts::setThreadPriority() {
  struct sched_param param;
  param.sched_priority = kInterruptThreadPriority;

  // 设置调度策略为 FIFO
  if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
    fprintf(stderr, "Failed to set thread priority!\n");
    exit(1);  // 设置失败,退出程序
  }
}

// 等待中断事件
int Interrupts::waitForInterrupt() {
  backup_set_ = master_set_;

  if (select(max_interrupt_fd_ + 1, &backup_set_, nullptr, nullptr, nullptr) < 0) {
    if (errno != EINTR) {
      fprintf(stderr, "Error in select() while waiting for interrupt\n");
      return -1;  // select 出错
    }
    return 0;  // 被信号中断,继续等待
  }

  return 0;
}

// 处理中断事件
int Interrupts::processInterrupts() {
  // 遍历所有已注册的中断处理器
  for (auto& entry : interrupt_handlers_) {
    int fd = entry.first;
    if (FD_ISSET(fd, &backup_set_)) {
      uint32_t info;
      if (read(fd, &info, sizeof(info)) != sizeof(info)) {
        fprintf(stderr, "Failed to read interrupt data from fd: %d\n", fd);
        continue;  // 读取失败,跳过此中断
      }
      entry.second();  // 调用中断处理器
      info = 1;  // 解除中断屏蔽
      if (write(fd, &info, sizeof(info)) != sizeof(info)) {
        fprintf(stderr, "Failed to write interrupt data back to fd: %d\n", fd);
      }
    }
  }

  return 0;
}

// 启动中断处理线程
void Interrupts::start() {
  // 设置当前线程的优先级
  setThreadPriority();

  // 启动一个循环处理
  while (true) {
    if (waitForInterrupt() == 0) {
      if (processInterrupts() != 0) {
        fprintf(stderr, "Error processing interrupts\n");
      }
    }
  }
}

3. 代码说明

3.1 Interrupts

  • 构造函数 (Interrupts):初始化文件描述符集合和最大文件描述符。
  • init:初始化中断源,例如为 DMA 注册中断。
  • registerInterrupt:通过 /dev/uioX 设备文件注册外设中断,解除中断屏蔽并存储回调函数。
  • waitForInterrupt:通过 select 等待中断事件发生。
  • processInterrupts:处理触发的中断事件,调用对应的回调函数。

3.2 中断处理

当 DMA 中断触发时,会调用 dma_handler 来处理。例如,打印 "DMA interrupt triggered!"

3.3 start 方法

start 方法通过 sched_setschedulerSCHED_FIFO 设置线程优先级。它启动一个循环来等待中断并处理。


http://www.kler.cn/a/391127.html

相关文章:

  • python购物计算 2024年6月青少年电子学会等级考试 中小学生python编程等级考试一级真题答案解析
  • Prometheus面试内容整理-Prometheus 的架构和工作原理
  • AtomicInteger 和 AtomicIntegerFieldUpdater的区别
  • 多叉树笔记
  • 算法演练----24点游戏
  • 开源 2 + 1 链动模式、AI 智能名片、S2B2C 商城小程序在用户留存与品牌发展中的应用研究
  • 【疑难杂症】电脑休眠后无法开机,进入 steamVR 时电脑突然黑屏关机
  • React官网生成Recat项目的区别
  • hbase的安装与简单操作
  • C语言实现IIR型零相位带通滤波器
  • 如何使用XSL-FO生成PDF格式的电子发票的技术博文示例
  • 负梯度方法与Newton型方法-数值最优化方法-课程学习笔记-4
  • Spring Boot基础教学:Spring Boot的核心特性
  • sql表的约束练习题
  • git commit 校验
  • 数学建模---利用Matlab快速实现机器学习(上)
  • 技术人,在数字化转型中如何为企业赋能?
  • Vuex 与 Pinia:Vue 状态管理库的选择与对比
  • 基于YOLOv5的人群密度检测系统设计与实现
  • Oracle 数据库创建导入
  • 基于Multisim温度计温度测量温度超限报警电路(含仿真和报告)
  • gitlab 流水线流程简要说明
  • 用于图像识别的判别图正则化技术
  • 《云原生安全攻防》-- K8s安全防护思路
  • Go语言开发基于SQLite数据库实现用户表增删改查项目搭建(一)
  • adb 如何通过wifi连接手机