嵌入式Linux驱动复习(5)驱动设计的思想:面向对象/分层/分离

2023-05-16

在上节中我们已经写出了具体单板的驱动程序:

字符设备驱动程序抽象出一个file_operations结构体;

我们写的程序针对硬件部分抽象出led_operations结构体。

我们采用的是上下分层的驱动写法

上下分层,比如我们前面写的LED驱动程序就分为2层:

① 上层实现硬件无关的操作,比如注册字符设备驱动:leddrv.c

② 下层实现硬件相关的操作,比如board_A.c实现单板A的LED操作

 

这一节我们要对这个驱动进行改进:

还能不能改进?分离

在board_A.c中,实现了一个led_operations,为LED引脚实现了初始化函数、控制函数:

如果硬件上更换一个引脚来控制LED怎么办?你要去修改上面结构体中的init、ctl函数。

实际情况是,每一款芯片它的GPIO操作都是类似的。比如:GPIO1_3、GPIO5_4这2个引脚接到LED:

① GPIO1_3属于第1组,即GPIO1。

有方向寄存器DIR、数据寄存器DR等,基础地址是addr_base_addr_gpio1。

设置为output引脚:修改GPIO1的DIR寄存器的bit3。

设置输出电平:修改GPIO1的DR寄存器的bit3。

② GPIO5_4属于第5组,即GPIO5。

有方向寄存器DIR、数据寄存器DR等,基础地址是addr_base_addr_gpio5。

设置为output引脚:修改GPIO5的DIR寄存器的bit4。

设置输出电平:修改GPIO5的DR寄存器的bit4。

既然引脚操作那么有规律,并且这是跟主芯片相关的,那可以针对该芯片写出比较通用的硬件操作代码。

比如board_A.c使用芯片chipY,那就可以写出:chipY_gpio.c,它实现芯片Y的GPIO操作,适用于芯片Y的所有GPIO引脚。

使用时,我们只需要在board_A_led.c中指定使用哪一个引脚即可。

程序结构如下:

 

以面向对象的思想,在board_A_led.c中实现led_resouce结构体,它定义“资源”──要用哪一个引脚。

在chipY_gpio.c中仍是实现led_operations结构体,它要写得更完善,支持所有GPIO。

代码如下:

lcd_resource.h

#ifndef _LED_RESOURCE_H
#define _LED_RESOURCE_H

/* GPIO3_0 */
/* bit[31:16] = group */
/* bit[15:0]  = which pin */
#define GROUP(x) (x>>16)
#define PIN(x)   (x&0xFFFF)
#define GROUP_PIN(g,p) ((g<<16) | (p))

struct led_resource {
	int pin;   //使用哪个GPIO
};

struct led_resource *get_led_resouce(void);   //声明Board_A_led.c中的函数,这个函数主要是向上层提供

#endif

board_A_led.c


#include "led_resource.h"

static struct led_resource board_A_led = {
	.pin = GROUP_PIN(3,1),
};

struct led_resource *get_led_resouce(void)
{
	return &board_A_led;    //反回一个led_resource,别的文件通过调用得到该对哪个GPIO进行初始化
}


led_opr.h

#ifndef _LED_OPR_H
#define _LED_OPR_H

struct led_operations {
	int (*init) (int which); /* 初始化LED, which-哪个LED */       
	int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void);  //声明函数,向上层提供


#endif

chip_demo_gpio.c

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include "led_opr.h"
#include "led_resource.h"

static struct led_resource *led_rsc;
static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */	   
{	
	//printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
	if (!led_rsc)
	{
		led_rsc = get_led_resouce();  //得到要初始化哪个GPIO
	}
	
	printk("init gpio: group %d, pin %d\n", GROUP(led_rsc->pin), PIN(led_rsc->pin));
	switch(GROUP(led_rsc->pin))
	{
		case 0:
		{
			printk("init pin of group 0 ...\n");
			break;
		}
		case 1:
		{
			printk("init pin of group 1 ...\n");
			break;
		}
		case 2:
		{
			printk("init pin of group 2 ...\n");
			break;
		}
		case 3:
		{
			printk("init pin of group 3 ...\n");
			break;
		}
	}
	
	return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
	//printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
	printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(led_rsc->pin), PIN(led_rsc->pin));

	switch(GROUP(led_rsc->pin))
	{
		case 0:
		{
			printk("set pin of group 0 ...\n");
			break;
		}
		case 1:
		{
			printk("set pin of group 1 ...\n");
			break;
		}
		case 2:
		{
			printk("set pin of group 2 ...\n");
			break;
		}
		case 3:
		{
			printk("set pin of group 3 ...\n");
			break;
		}
	}

	return 0;
}

static struct led_operations board_demo_led_opr = {
	.init = board_demo_led_init,
	.ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
	return &board_demo_led_opr;
}

lcd_drv.c

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"

#define LED_NUM 2

/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)

/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char status;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&status, buf, 1);

	/* 根据次设备号和status控制LED */
	p_led_opr->ctl(minor, status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node);
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 根据次设备号初始化LED */
	p_led_opr->init(minor);
	
	return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{
	int err;
	int i;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */


	led_class = class_create(THIS_MODULE, "100ask_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "led");
		return -1;
	}

	for (i = 0; i < LED_NUM; i++)
		device_create(led_class, NULL, MKDEV(major, i), NULL, "100ask_led%d", i); /* /dev/100ask_led0,1,... */
	
	p_led_opr = get_board_led_opr();
	
	return 0;
}

/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
	int i;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	for (i = 0; i < LED_NUM; i++)
		device_destroy(led_class, MKDEV(major, i)); /* /dev/100ask_led0,1,... */

	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	unregister_chrdev(major, "100ask_led");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式Linux驱动复习(5)驱动设计的思想:面向对象/分层/分离 的相关文章

  • ucOS系统移植(hal库)

    学习嵌入式实时操作系统 xff08 RTOS xff09 以uc OS III为例 xff0c 将其移植到stm32F103上 xff0c 构建至少3个任务 xff08 task xff09 其中两个task分别以1s和3s周期对LED等进
  • Jmeter性能测试(16)--配置元件之计数器

    先说说利用jmeter生成数据的几种方法 xff1a 1 CSV Data Set Config 这个元件被用来在参数化生成数据时使用 xff0c 简单高效 xff0c 容易生成有序数 xff1b 只需要新建excel xff0c 然后通过
  • Python3 SMTP发送邮件

    SMTP xff08 Simple Mail Transfer Protocol xff09 即简单邮件传输协议 它是一组用于由源地址到目的地址传送邮件的规则 xff0c 由它来控制信件的中转方式 python的smtplib提供了一种很方
  • Tomcat方式启动CAS(附:java环境的切换)

    目录 一 安装java 1 8 注意 二 安装Tomcat 三 安装CAS 一 安装java 1 8 Java JDK在linux系统有两个版本 xff0c 一个开源版本Openjdk xff0c 还有一个oracle官方版本jdk xff
  • STM32—基于ZE08-CH2O模块检测甲醛含量精解

    ZE08 CH2O模块概述 管脚定义 xff1a ZE08 CH2O模块的数据及其使用方法 xff1a 这是我经常用的方法 xff1a 使用串口来接收数据 然后这个模块分为两个模式 xff1a 1 xff0c 主动上传模式 xff0c 2
  • 上位机 PC 和下位机 STM32 串口之间串口通讯的连接图如图 2 所示。USART1_TX 引脚为 PA9,USART1_RX 引脚为PA10 。编程实现如下功能:

    上位机 PC 和下位机 STM32 串口之间串口通讯的连接图如图 2 所示 USART1 TX 引脚为 PA 9 xff0c USART 1 RX 引脚为 PA10 编程实现如下功能 xff1a 当 上位机向下位机发送 1 时 xff0c
  • List集合

    List list集合在java util下使用时需要导包 List集合特点 xff1a 有序 xff1a 存和取的元素顺序一致有索引 xff1a 可以通过索引操作元素可重复 存储的元素可以重复 61 61 List特有 61 61 方法
  • docker服务更新脚本

    一 安装docker docker version systemctl status docker 1 cd home qcpublish qctool dockerInstall 2 yum localinstall y rpm 3 sy
  • 蓝桥杯嵌入式组环境配置(keil mdk+sdk+STM32cubemx)

    1 keil mdk下载 官网也可以下载 地址 MDK ARM Version 5 38a Evaluation Software Request keil com 没梯子速度太慢了 我已经把安装包放在了云盘里链接 xff1a 链接 xff
  • UDP通信代码

    1 客户端代码 创建一个套接字 设置服务器地址 使用sendto向服务器端或接收端发送数据 使用recv接受数据 include lt sys un h gt include lt sys types h gt include lt sys
  • 手把手教CMake编译Qt5工程

    一 新建CMakeLists txt 可以在工程目录下新建一个名为CMakeLists txt文件 xff0c 亦或是 xff0c 在Qtcreator中 xff0c 右键项目 xff0c 添加新文件 xff0c 选择General中的Em
  • Jmeter性能测试(17)--目录结构

    首先得了解一下这些东西 xff0c 以后才能快速的找到某些配置文件进行修改 xff08 举个例子 xff0c 改配置只是其中之一 xff09 一 bin目录 examples 目录中有CSV样例 jmeter bat windows的启动文
  • 冒泡排序和选择排序的区别

    一 区别 1 冒泡排序是比较相邻位置的两个数 xff0c 而选择排序是按顺序比较 xff0c 找最大值或者最小值 xff1b 2 冒泡排序每一轮比较后 xff0c 位置不对都需要换位置 xff0c 选择排序每一轮比较都只需要换一次位置 xf
  • 使用Navicat连接mysql出现的报错:[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and co

    报错如下 Err 1055 Expression 1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 39 information
  • hadoop三大组件

    1 介绍hadoop的组件 hadoop有三个主要的核心组件 xff1a HDFS xff08 分布式文件存储 xff09 MAPREDUCE xff08 分布式的计算 xff09 YARN xff08 资源调度 xff09 xff0c 现
  • phoenix安装及使用(重点二级索引)

    文章目录 一 phoenix搭建1 关闭HBase集群 xff0c 在master中执行2 上传解压配置环境变量3 将phoenix 4 15 0 HBase 1 4 server jar复制到所有节点的hbase lib目录下4 启动hb
  • Hive数仓分层架构

    文章目录 一 为什么要进行数据分层 xff1f 数据分层的好处 xff1f 1 原因2 好处 二 hive数仓分为哪几层 xff1f 1 ODS层 数据运营层 xff08 贴源层 xff09 作用我们需要做的是 xff1f 2 DW层 xf
  • Hive调优方式

    文章目录 一 建表注意事项1 分区 分桶2 一般使用外部表 xff0c 避免数据误删3 选择适当的文件储存格式及压缩格式4 命名要规范5 数据分层 xff0c 表分离 xff0c 但是不要分的太散 二 查询优化1 分区裁剪 where过滤
  • 点到点专线是什么?点到点专线和互联网专线有什么区别?

    在互联网时代 xff0c 宽带是每个家庭不可或缺的东西 xff0c 宽带受到上下速度的限制 xff0c 所以一般宽带不能达到最佳的网速 这时候有些用户为了让自己的网速更快 xff0c 会采用点到点专线 xff0c 那么什么是点到点专线呢 x
  • 数据存储:私有云的好处

    随着技术的进步 xff0c 许多任务 xff08 包括数据管理 xff09 都有可能去物质化 现在 xff0c 越来越多的公司使用云服务器 它是一种解决方案 xff0c 可方便存储 交换和访问公司的计算机数据 当然 xff0c 你已经听说过

随机推荐

  • Switch case 使用及嵌套语法

    目录 switch case 语句讲解 Switch 也可以嵌套 switch case 语句讲解 代码中 虽然没有 限制 if else 能够处理的 分支 数量 xff0c 但当分支过多时 xff0c 用 if else 处理会不太方便
  • git新建分支及切换

    新建分支前 xff0c 先看一下目前是在哪个分支 可以看到下图我是在master分支上 使用以下命令创建新分支 xff0c 并切换到新分支 git checkout b main 创建main分支并切换到main xff0c main可以换
  • Jmeter性能测试(18)--关联之XPath Extractor

    之前的博客 xff0c 有介绍jmeter如何对请求进行关联的一种常见用法 xff0c 即 xff1a 后置处理器中的正则表达式提取器 xff0c 下面介绍另一种关联方法 xff0c XPath Extractor xff01 所谓关联 x
  • docker和LXC简介

    文章目录 1 什么是LXC2 什么是dockerdocker工作方式Docker产生的背景 1 什么是LXC LXC xff08 LinuX Container xff09 简称Linux的容器 xff0c 是世界上第一个容器应用 它将容器
  • 基于I2C/SPI总线的温湿度采集与OLED显示

    目录 一 STM32之基于I2C的温湿度采集 xff08 AHT20 xff09 1 I2C协议2 AHT20温湿度采集程序接线烧录效果视频 二 STM32在oled上显示文字滚动1 了解SPI xff08 串行外设接口 xff09 2 字
  • 调用别人的接口的几种方法

    概述 在实际开发过程中 xff0c 我们经常需要调用对方提供的接口或测试自己写的接口是否合适 很多项目都会封装规定好本身项目的接口规范 xff0c 所以大多数需要去调用对方提供的接口或第三方接口 xff08 短信 天气等 xff09 在Ja
  • 搭建springboot项目报错合集

    报错一 xff1a 服务器端口已被占用 解决办法 xff1a 报错二 xff1a There was a problem with the instance info replicator 该服务尝试将自己作为客服端注册 解决办法 xff1
  • 手写 Lodash (2)

    文章目录 手写 Lodash 2 1 先看这次要达到的效果2 开始手写2 1 这是第一期的代码2 2 接着写思路 xff1a 代码 xff1a 4 测试例子5 总结 手写 Lodash 2 这里参考的是 lodash js 4 17 21
  • [LQR简要快速入门]+[一级倒立摆的LQR控制]

    LQR简要快速入门 43 一级倒立摆的LQR控制 1 什么是LQR2 公式含义3 倒立摆的建模3 1 线性化3 2 状态空间建立 4 LQR算法实现5 MATLAB代码仿真6 优缺点 1 什么是LQR LQR是一种最优控制算法 xff0c
  • 滑模控制理论(SMC)概述

    滑模控制理论 xff08 SMC xff09 概述 一 背景二 数学理论三 高维拓展四 分析 一 背景 滑模控制理论 xff08 Sliding Mode Control SMC xff09 是一种建立在现代控制理论基础上的控制理论 xff
  • 扩张状态观测器简介

    扩张状态观测器简介 1 系统模型2 增广状态空间的建立3 扩张状态观测器的建立4 重构柯西方程组 由于这两天经常用到关于观测器的一些东西 xff0c 于是看到了这个 扩张状态观测器 xff0c Extended State Obsever
  • 基于kubernetes的Prometheus监控mysql

    kubernetes Prometheus监控mysql Prometheus监控Pod中的应用 xff0c 比如mysql xff0c 有2种方法 1 exporter pod方式 xff0c 通过这个pod去监控mysql servic
  • [Ubuntu]Scrcpy+Zeromq实现手机屏幕yuv数据传输,并通过OpenCV实现连续播放——(二)(思路+代码解析)

    Scrcpy在上一篇博客中有所介绍 xff0c 并且使用Scrcpy实现了手机屏幕yuv数据的提取 xff08 Ubuntu Scrcpy获取手机屏幕yuv数据 又是谁在卷的博客 CSDN博客 xff09 本文将介绍一个当下较为好用的消息中
  • Jmeter性能测试(19)--HTTP属性管理器

    jmeter是一个开源灵活的接口和性能测试工具 xff0c 当然也能利用jmeter进行接口自动化测试 在我们利用它进行测试过程中 xff0c 最常用的sampler大概就是Http Request xff0c 使用这个sampler时 x
  • 选择光模块时,应该从哪些方面去选择?

    光模块在通信网络中也占据着不小的位置 xff0c 在选择光模块时 xff0c 我们应该从哪些方面去选择呢 xff1f 1 传输距离 xff1a 不一样的光模块支持的传输距离是不一样的 xff0c 一般而言 xff0c 多模光模块的传输距离会
  • Vue3 + Vite + Ts 开发必备的 VSCode 插件

    Vetur xff1a Vue 语法高亮和语法检查插件 Vue Peek xff1a 快速定位 Vue 组件和模板 Vue 3 Snippets xff1a 快速生成 Vue3 相关代码的代码片段 Vue VSCode Extension
  • 入门STM32--怎么学习STM32

    目录 前言 一 STM32是什么 xff1f 二 一些概念 xff08 不懂可能会被装到 xff09 1 Soc芯片 2 MCU 3 FLASH 三 STM32的学习 1 51 2 学习方法 3 建议 四 学前准备 1 开发板 2 下载器
  • RTOS学习(4)--启动过程分析

    比较常见的启动流程有两种 xff0c 一种是在main函数中完成硬件和RTOS系统的初始化 xff0c 并且创建所需的任务 xff0c 最后只需要开启调度器即可 还有一种是在main函数中将硬件和RTOS初始化 xff0c 然后创建一个启动
  • 【解决问题】RLException: [xx.launch] is neither a launch file in package [x] nor is [x] a launch file name

    看网上很多大神都是进入bashrc修改或者添加代码但是好多没啥用 xff0c 亲测一种方法如下 这种方法不需要每次编写都添加环境变量了 首先打开这个文件 xff1a sudo gedit bashrc 在该文件的最后添加如下代码 ros w
  • 嵌入式Linux驱动复习(5)驱动设计的思想:面向对象/分层/分离

    在上节中我们已经写出了具体单板的驱动程序 xff1a 字符设备驱动程序抽象出一个file operations结构体 xff1b 我们写的程序针对硬件部分抽象出led operations结构体 我们采用的是上下分层的驱动写法 上下分层 x