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

Linux驱动15 --- buildroot杂项驱动开发方法

目录

一、中断

1.1 介绍

1.2 Linux 下的中断

        须知

        1、SGI 软件中断

        2、PPI 私有外设中断

        3、SPI 共享外设中断

1.3 Linux 下的中断开发方式

        1.设备树下的中断 --- 重点

        获取中断号

        使能中断 --- 目前不用

        注册中断服务函数

        注销中断服务函数

        失能中断 --- 目前不用

        2.一般中断开发方式 ---了解

        获取中断号

        注册中断服务函数

        释放终端服务函数

二、等待队列

2.1 介绍

        等待队列的使用

        等待队列的申请

        等待队列的阻塞

        等待队列的释放

三、定时器

3.1 介绍

        内核定时器的使用

        定时器的初始化

        定时器的添加

        定时器的删除

        定时器的调用


一、中断

1.1 介绍

        中断是一个信号,是一个异常,打断正在运行的过程,转而执行中断服务函数

        中断会有分类:

                在 MCU 中:外部中断(EXIT)、定时器中断(TIM)、串口中断(USART)、看门狗、ADC 中断、DMA 中断等等

        其中应用最多的就是串口中断定时器中断

        中断的唯一标识:中断向量

                中断向量表 --- 启动文件

        中断服务函数 --- 启动文件

                无参无返回值

        中断控制器:NVIC

                中断向量,使能,抢占(占先)优先级,响应(次级)优先级

                数字越小,优先级越高

1.2 Linux 下的中断

        须知

        Linux 是一个整个操作系统,我们可以用中断,但是接触不到控制中断

        和 FreeRTOS 类似 --- 当操作系统接手板子的管理,那么操作系统会帮忙管理中断

        在 Linux 中断 --- 共享中断(SPI),私有中断(PPI),软件中断(SGI)

        三类中断中我们接触最多的就是共享中断 --- 其中包含了 GPIO

                不同的中断号可以使用相同的中断服务函数

                但是需要保证中断的触发方式是相同的

        在 Linux 的中断唯一标识 --- 中断号

                中断号是需要获取的

        在 Linux 的中断控制器 --- GIC

                在目前的编程过程中接触不到

        1、SGI 软件中断

                16 个,中断号:0—15。通过向 ICDSGIR 寄存器写入 SGI 中断号、CPU ID,来产生一个软件中断;通过读 ICCIAR 寄存器或者向 ICDICPR 寄存器相应的比特位写 1,可以清除中断。所有的 SGI 为边沿触发。

        2、PPI 私有外设中断

                每个 CPU(CPU0、CPU1)连接 5 个私有中断,中断号:27—31。ICDICFR1 为 PPIs 的优先级及触发条件控制寄存器,是只读的,因而 PPIs 的触发条件也不可更改。需要注意到的是,PL 部分的快速响应中断 FIQ(fast interrupt)信号与普通中断 IRQ(interrupt)需要被送往中断控制器中,所以即便 ICDICFR1 规定 IRQ 与 FIQ 的响应等级为 low level,但是他们的在 PS 与 PL 接口的响应等级仍是 high。

        3、SPI 共享外设中断

                中断号:32-95。由 PS 和 PL 上的各种 I/O 控制器和存储器控制器产生,如 GPIO、 DMA、定时器等模块的中断信号,这些中断信号会被路由到 CPU。PS 的外设产生的SPI 中断也会路由到 PL 上。

1.3 Linux 下的中断开发方式

        1.设备树下的中断 --- 重点

        中断的关键字:irq、interrupt

        注意:当前在设备树中的触发方式选择为 IRQ_TYPE_EDGE_FALLING 下降沿

                                                                           IRQ_TYPE_EDGE_RISING  上升沿

        在编程的时候也要选择为  IRQ_TYPE_EDGE_FALLING

        单独编译内核,烧录内核

        GPIO 的中断(EXIT)在 MCU 是做什么的 --- 按键检测

        使用中断检测的优势 --- 更加准确的检测按键的状态(消抖) 

        获取中断号

        头文件:#incluide <linux/of_irq.h>

                        #include <linux/irq.h>

                        #include <linux/interrupt.h>

        函数原型

                int platform_get_irq(struct platform_device *dev, unsigned int num)

        函数参数

                dev:直接使用(*probe)的形参

                num:下标 ,从 0 开始

        函数返回值

                成功返回中断号,失败返回负数

        理论上中断需要使能

        但是在这个板子下,使能中断会报警告

        使能中断 --- 目前不用

        函数原型

                void enable_irq(unsigned int irq)

        注册中断服务函数

        函数原型

                int __must_check devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)

        函数参数

                dev:在(*probe)函数的参数 struct platform_device{struct device dev;}

                irq:中断号

                handler:中断服务函数的原型

                        irqreturn_t  __irqfunc(自己名字) (*irq_handler_t)(int, void *)

                        函数参数

                                        int:表示进入中断服务函数的中断号

                                                换句话说,是产生中断的中断号

                                                void *:是中断服务函数注册函数传入的参数 ,一般不用

                irqflags:中断触发标志

                        IRQ_TYPE_EDGE_RISING 上升沿

                        IRQ_TYPE_EDGE_FALLING 下降沿

                devname:不重复,无所谓,尽可能有意义

                dev_id:传入中断服务函数的参数 ,一般不用,填 NULL

        函数返回值

                必须承接,否则报警告

        注销中断服务函数

        函数原型

                void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id)

        函数参数

                dev:同上

                irq:中断号

                dev_id:传递给中断服务函数的参数

        当前发现,按键已经比较灵敏了

        但是,会发现本该阻塞的 read,现在没有阻塞

        失能中断 --- 目前不用

        函数原型

                void disable_irq(unsigned int irq)

        函数参数

                irq:中断号

        2.一般中断开发方式 ---了解

        获取中断号

        函数原型

                int gpio_to_irq(unsigned int gpio)

        函数参数

                gpio:GPIO 号

        函数返回值

                成功返回中断号,失败返回负数

        注册中断服务函数

        函数原型

                int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)

        函数参数

                irq:中断号

                handler:中断服务函数

                flags:中断触发方式

                name:无所谓,尽可能由意义

                dev:传递给中断服务函数的参数

        函数返回值

                需要承接,否则报警告

        释放终端服务函数

        函数原型

                void *free_irq(unsigned int irq, void *dev_id)

        函数参数

                irq:中断号

                dev_id:传递给中断服务函数的参数

二、等待队列

2.1 介绍

        不用往队列的方向去考虑,用起来很简单

        等待队列是内核阻塞最常用的实现方式

        我们使用等待队列主要是为了让 read 函数阻塞

        等待队列,是指 linux 系统中进程所组成的队列,就是需要其他事件的发生才会自己本身被唤醒的进程,也就是说这些进程本身是在等待其他某些进程为他们提供进程发生的条件。他们是属于消费者的,但是他们要消耗的东西还没有产生,这些就是处于等待状态的进程,组成了等待队列。等待队列很容易使用, 尽管它的设计很是微妙, 但不需要知道它的内部细节。

        等待队列的使用

        关键字:wait_queue

        头文件:#include <linux/wait.h>

                      #include <linux/sched.h>

        等待队列的申请

        原型                

                DECLARE_WAIT_QUEUE_HEAD(name)

        函数参数

                name:就是自己申请等待队列的名字

        等待队列的阻塞

        函数原型

                wait_event_interruptible(wq_head,condition)

        函数参数

                wq_head:就是等待队列的名字

                condition:条件 ,要求传入一个 0 值就是阻塞

        等待队列的释放

        函数原型

                wake_up_interruptible(x)

        函数参数

                x:需要给等待队列取地址传入

                并且需要将等待队列的条件赋值为真

三、定时器

3.1 介绍

        定时器的功能就是计数

        内核中的定时器相较于 MCU 没有那么多功能

        内核定时器的时钟主频只有 --- 300Hz

        这个主频可以自己改:100 ~ 1000

        修改过后需要重新编译内核

        300Hz 意味这 → 每秒钟记 300 次数

        记一次数需要 1/300 s

        对于内核中的定时器来说,它的计数是依靠时间基准实现的

        这个时间基准指的是:当前系统运行时间

                在内核中通过宏定义表示 --- jiffies

        如果使用定时器计时,需要

                当前系统运行时间+计时的时间 ,假如计时时间单位是 s

                jiffies+计时的时间*300

        这个写法在 MCU 中用过

                MCU 在学习系统滴答定时器之后,用滴答定时器写 delay 函数

                原理:

                1、定义一个变量,放在滴答定时器的中断中一直做++

                2、在延时函数中,定义一个变量,获取当前系统运行时间,加上需要延时的时间,作为一个比较值在某一时刻,系统运行时间是固定的

                3、用系统运行时间和第二步得到的比较值做比较

                        比较值是定值,而系统运行时间是变值

                        在后续某一时刻,变值必然会大于定值

        内核定时器的使用

        关键字:timer

        头文件:#include <linux/timer.h>

        定时器的初始化

        函数原型

                __init_timer(_timer, _fn, _flags)

        函数参数

                _timer:定时器核心结构体

                        struct timer_list{

                                unsigned long expires;         //计时时间

                        }

                _fn:void (*function)(struct timer_list *)

                                定时器计时完成执行的函数 ,计时完成一次,执行一次

                _flags:填 0 即可

        定时器的添加

        函数原型

                void add_timer(struct timer_list *timer)

        函数参数

                定时器核心结构体

        定时器的删除

        函数原型

                int del_timer(struct timer_list * timer)

        函数参数

                同上

        函数返回值

                删除活跃定时器返回 1,删除不活跃定时器返回 0

        定时器的调用

        函数原型

                int mod_timer(struct timer_list *timer, unsigned long expires)

        函数参数

                timer:定时器核心结构体

                expires:计时时间

                jiffies+计时时间

        这个 mod_timer 是调用定时器的关键

        注意:为什么调用一次 add_timer 之后会进入定时器的回调函数

        在 add_timer 中,默认调用了 mod_timer

http://www.lryc.cn/news/594972.html

相关文章:

  • windows内核研究(驱动开发-多核同步之临界区和自旋锁)
  • 【Linux内核】Linux驱动开发
  • 智慧场景:定制开发开源AI智能名片S2B2C商城小程序赋能零售新体验
  • 莘默曹工-Cd Automation半导体调功器 RS2300-
  • Mac安装Typescript报错
  • 电脑声音修复?【图文详解】电脑没有声音?声音异常
  • 如何升级到macOS Tahoe:全面指南与实用步骤
  • node.js 为什么要装 express组件
  • Node.js的Transform 流
  • 深度学习-常用环境配置
  • Spring 对数组和集合类的自动注入
  • 机器学习初学者理论初解
  • Oracle 数据库共享池与大池调优指南
  • ElasticSearch:不停机更新索引类型(未验证)
  • Django基础(五)———模板结构
  • 中小型企业如何选择合适的WMS系统?
  • 如何用DispatcherTimer提高运行总时间的精确度
  • AI探索 | 基于 Node.js 开发 MCP 客户端+服务端及优秀项目分享
  • Node.js- node管理工具nvm
  • Spring @RequestBody注解详解与实践
  • Dockerfile 完全指南:从入门到精通
  • 西门子 S7-1500 信号模块硬件配置全解析:从选型到实战
  • (10)机器学习小白入门 YOLOv:YOLOv8-cls 模型评估实操
  • 使用 Tailwind CSS 控制元素在移动端不显示
  • 【LuckiBit】macOS/Linux 常用命令大全
  • Jenkins pipeline触发下游流水线
  • 用Java 代码实现一个简单的负载均衡逻辑
  • 2025最新版PyCharm for Mac统一版安装使用指南
  • springcloud -- 微服务02
  • 【Unity优化】Unity多场景加载优化与资源释放完整指南:解决Additive加载卡顿、预热、卸载与内存释放问题