ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

嵌入式驱动程序设计

2022-08-02 00:02:34  阅读:184  来源: 互联网

标签:驱动程序 博客 嵌入式 驱动 内核 模块 Linux 设计 设备


1.部分Linux命令

  Linux使用mknod命令创建设备节点

  Linux使用mkdir命令创建目录文件

  Linux使用mkfifo命令创建管道文件

  ☞ linux mknod命令解析_童安格粉丝的博客-CSDN博客_mknod

  ☞ mkfifo函数使用_superywf的博客-CSDN博客_mkfifo函数

 

2. Linux设备驱动

  Linux设备驱动主要分为:字符设备、块设备、网络设备

 ☞ 字符设备_百度百科 (baidu.com)

 ☞ 块设备_百度百科 (baidu.com)

 ☞ 网络设备(网络设备)_百度百科 (baidu.com)

 .  Linux驱动模块的后缀名为: .ko

 ☞ Linux中后缀名为ko、o、a、so、la的文件介绍_LINUX_操作系统

 

3. Linux系统调用

   Linux下常用系统调用名,系统调用实际也是VFS层提供的功能:

    read:读取;open:打开设备;close:关闭设备; lseek:偏移设备

    注意:fopen\fread为标准IO,不是系统调用

  ☞ Linux常用系统调用 - LaplaceDemon - 博客园 (cnblogs.com)

 

4. platform设备驱动

  内核platform总线设备是一种虚拟的设备驱动

  Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动称为platform_driver

  platform_driver和platform_device在总线中匹配成功后,执行probe接口

 ☞ Linux驱动之Platform device/driver - 知乎 (zhihu.com)

 

5. Linux实现物理地址和虚拟地址的映射的方式

   Linux通过ioremap函数实现物理地址和虚拟地址的映射

  ☞ Linux虚拟地址和物理地址的映射 - 流浪的Coder - 博客园 (cnblogs.com)

  ☞ ioremap_百度百科 (baidu.com)

 

6.设备树

  设备树是一种描述硬件结构的数据结构,采用设备树后,许多硬件的细节可以直接通过它传递给linux,而不再需要在内核中进行大量的冗余编码。

  设备树由一系列被命名的节点和属性(成对出现的名称和值)组成,可描述的信息包括:

   a.CPU数量和类别       b.内存基地址和大小

   c.总线和桥          d.外设连接

   e.中断控制器和中断使用情况  f.GPIO控制器和GPIO使用情况

   g.时钟控制器和时钟使用情况

  基本上就是画一颗电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,内核识别出这棵树,并根据它展开出Linux内核中的platform_device、i2C_client等设备,而这些设备用到的内存、IRQ等资源也被传递给了内核,内核会将这些资源给展开的相应的设备。

   设备树被内核识别后,会在/sys目录下为用户提供可视化操作接口

  /sys:sys是system的缩写,该目录也是一个虚拟目录,主要用于收集和存放系统核心设备信息,主要指各类系统核心设备及其驱动信息,例如块设备block、总线bus、内核kernel、电源power等等;

  设备树定义节点时,定义资源的属性名是reg

  reg:给出访问该device node的地址信息

  ☞ 设备树之节点和属性_麻辣小新的博客-CSDN博客_设备树label 

 

7.miscdevice

  miscdevice类型定义的是misc设备驱动

  linux的miscdevice的主设备号是10

  ☞ misc设备驱动_eurphan_y的博客-CSDN博客_misc驱动

  ☞ linux驱动开发 --miscdevice_Sunnie_ge的博客-CSDN博客_miscdevice

 

8.printk内核打印函数

  ☞ 内核打印函数printk_ptonlix的博客-CSDN博客_内核打印函数

  ☞ printk 内核打印 – 人人都懂物联网 (getiot.tech)

  在printk打印内核信息时,以信息级打印应设置KERN_INFO前缀

#define pr_info(fmt,arg...)  \
printk(KERN_INFO fmt,##arg)

 

9.内核空间拷贝到用户空间

  实现内核空间拷贝到用户空间的接口名:copy_to_user

  ☞ linux驱动开发--copy_to_user 、copy_from_user函数实现内核空间数据与用户空间数据的相互访问 (aliyun.com)

 

10.cdev结构体

  内核提供了cdev结构体描述字符设备,cdev_add是向内核添加,cdev_del是从内核中移除

  ☞ Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

 

11.内核模块

  11-1:内核模块源码编译方式

    内核模块源码可以配置为编译进内核和以模块编译的方式

    objs-y的目标被编译进内核镜像中,objs-m 以模块方式编,其余的跟内核没关系。

   

  11-2:内核模块入口

    内核模块的入口需要通过module_init进行注册到内核里

  

  11-3:内核模块加载

    内核模块通过insmod命令实现用户空间向内核空间加载

    ☞ Linux 系统设置 : insmod 命令详解_HarkerYX的博客-CSDN博客_insmod

 

12.linux主设备号

  Linux字符设备和块设备独占主设备号

  Linux设备号中主设备号为12位

 

13.kmalloc和malloc

  使用kmalloc函数在内核空间申请内存,使用malloc函数从用户空间申请内存

  ☞ kmalloc_百度百科 (baidu.com)

 

14.sysfs文件系统

  sysfs是一个虚拟的文件系统,它可以产生一个包括所有系统硬件的层级视图,它的一个目的就是战士设备驱动模型中各组件的层次关系。

  内核通过sysfs文件系统导出内核设备模型

  ☞ sysfs 文件系统 - 简书 (jianshu.com)

  

15.linux内核的虚拟内存

  ☞  linux虚拟内存与物理内存,内核态与用户态_selfsongs的博客-CSDN博客

  在linux系统中,进程的4GB内存空间被分成2个部分,用户空间与内核空间。用户空间的地址一般分布为0~3GB,这样,剩下的3~4G为内核空间。

  linux内核的虚拟内存是3G-4G空间

 

16.wait_event接口

  使用wait_event接口将当前访问进程设置为等待状态

  ☞ linux内核中的wait_event_interruptible_timeout接口解析_weixin_30263277的博客-CSDN博客

 

17.驱动的Makefile框架

BASE_KERNEL ?= 内核的源码目录
obj-m += 驱动源文件名.o
all:
    make -C $(BASE_KERNEL) M=$(PWD) modules
clean:
    make -C $(BASE_KERNEL) M=$(PWD) clean  

 eg.驱动源文件为abc.c和hello.c

# 若是虚拟机自身内核,可以定义变量为: /lib/modules/$(shell uname -r)/build
# 定义内核源码根目录
BASE_KERNEL ?= /lib/modules/$(shell uname -r)/build
# 定义目标名
TARGET := hello

obj-m := abc.o
obj-m += hello.o

all:
    make -C $(BASE_KERNEL) M=$(PWD) modules

clean:
    make -C $(BASE_KERNEL) M=$(PWD) clean

 

18.linux驱动模块组成

  1)模块加载函数,通过insmod命令加载内核,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。
  2)模块卸载函数,通过rmmod命令卸载某模块,被调用执行。
  3)模块许可证声明,使用MODULE_LICENSE配置GPL的声明。
  4)可选模块参数、模块导出符号表、模块作者等信息声明。

  ☞ linux驱动模块组成及其它_天天向上_好好学习的博客-CSDN博客_linux驱动模块的组成

  GPL:GNU 通用公共许可协议

module_init(hello_init);//模块加载
module_exit(hello_exit);//模块卸载
MODULE_LICENSE("GPL");//模块许可证声明
MODULE_AUTHOR("wit@zhaixue.cc");//模块作者等信息声明

   insmod 命令用法:

sudo insmod hello.ko #加载模块到内核中

  rmmod 命令用法:

sudo rmmod hello.ko #卸载模块

 

19.编写global_memory的open函数

  globalmem意味着“全局内存”,在globalmem字符设备驱动中会分配一片大小为GLOBALMEM_SIZE(4KB)的内存空间,并在驱动中提供针对该片内存的读写、控制和定位函数,以供用户空间的进程能通过Linux系统调用获取或设置这片内存的内容。

  对于globalmem驱动而言,私有数据的设置是在global_open()中完成的。

  open在驱动中,主要做面向对象的解包。想办法从基类找子类,把子类绑到所有的文件描述符里面,便于read()、write()、ioctl()、llseek()函数进行访问。思路是先通过inode节点,想办法找到它的子类,映射给虚拟的文件。

static int mem_open(struct inode *node, struct file *fp) {
    struct mem_dev *dev; //定义一把子类的钥匙
    dev = container_of(node->i_cdev, struct mem_dev, cdev);//container_of 通过基类找子类
    fp->private_data = dev; //与虚拟的文件系统进行绑定 (private_data:地址空间的私有成员)
    return 0; //代表成功
}
container_of(ptr,type,member)//其中ptr、type、member分别代表指针、结构体类型、成员

  ☞ container of()函数简介_叨陪鲤的博客-CSDN博客_container_of函数

eg.

struct mem_dev{
     struct cdev abc;
     unsigned char mem[GLOBALMEM_SIZE];
     //...      
}

static int mem_open(struct inode *node, struct file *fp) {
    struct mem_dev *dev;
    dev = container_of(node->i_cdev, struct mem_dev, abc);
    fp->private_data = dev;
    return 0;
}

 

20.最简单的驱动程序“Hello World”编写

#include <linux/init.h>
#include <linux/module.h>

static __init int hello_init(void) {
    printk("Hello world!\n");
    return 0;
}
static __exit void hello_exit(void) {
    printk("Goodbye!\n");
}

module_init(hello_init)
module_exit(hello_exit)
MODULE_LICENSE("GPL");

 

运行结果:

 模块加载之后的结果:

 

 模块卸载之后的结果:

  

 食用方法☞:Linux内核驱动学习---编写最简单Linux内核模块HelloWorld_九阈的博客-CSDN博客_添加最简单的linux内核模块

 

21.从设备树获取资源

  从设备树获取资源一般由 platform_get_resource 函数来实现

  ☞ platform_get_resource_铁头小哥的博客-CSDN博客_platform_get_resource

static int drv_probe(struct platform_device *pdev) {
    const char *value;
    struct resource *res;

    device_property_read_string(&pdev->dev, "xxx_value", &value);
    printk("the value is %s\n", value);

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    printk("the start: %x, the end: %x\n",res->start, res->end);
    return 0;
}

 

platform的OF匹配表,使驱动与.dfs中描述的设备节点进行匹配,从而使驱动的probe()函数执行:

static struct of_device_id matchs[] = {
    {.compatible = "swpu,embed,led", },
    {}
};

static struct platform_driver drv_05025_driver = {
    .probe = drv_probe,
    .remove = drv_remove,
    .driver = {
        .name    = "drv",
        .of_match_table = matchs,
        },
 };

module_platform_driver(drv_05025_driver);
MODULE_LICENSE("GPL");

 

标签:驱动程序,博客,嵌入式,驱动,内核,模块,Linux,设计,设备
来源: https://www.cnblogs.com/lxq-247A3/p/16337398.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有