栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

求职笔记之嵌入式知识点06

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

求职笔记之嵌入式知识点06

Linux驱动开发-补充

1.函数修饰符
(1) __init:本质上是个宏定义,这个宏定义的作用是将被它修饰的函数放入.init.text段中去(默认为.text)。内核启动后会统一加载.init.text 段中的这些模块安装函数。.init.text 段的函数加载完了之后就会把这个段给释放掉以节省内存。
(2) __exit

2.驱动模块中的头文件
应用编程中的头文件是编译器带来的 C 库函数(如 gcc 的头文件路径在/user/include 下),而驱动源代码中的头文件是内核源代码 include 目录下的头文件。

3.驱动编译的Makefile分析

KERNELDIR := /home/linux-imxrel_imx_4.1.15_2.1.0_ga_alientek(linux根目录)
CURRENT_PATH := $(shell pwd)
obj-m := chrdevbase.o

build: kernel_modules

kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

.PHONY:clean
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

4.裸机和Linux驱动中如何操控硬件
(1)寄存器地址不同: 裸机中直接使用物理地址;Linux驱动开发需要用该物理地址在内核中映射的虚拟地址, 其中物理地址是 CPU 设计时决定的,可从 datasheet 获取。
(2)编程方法不同:裸机中习惯直接用函数指针操作寄存器地址;而Linux驱动开发习惯用封装好的 io 读写函数来操作寄存器, 以实现最大程度的可移植性。

5.驱动设备文件的创建

  • 方法一 手动创建
    • (1) 设备文件指的是设备驱动抽象而来的文件,应用程序通过打开设备文件进而调用设备驱动。设备文件通过主次设备号与相应的file_operations 绑定
    • (2) 使用 mknod 创建设备文件: mknod /dev/xxx c 主设备号 次设备号。
  • 方法二 自动创建设备文件’
    • 解决方案: udev(嵌入式中用的是 mdev)
      • (1) udev 是应用层的一个应用程序,可以创建设备文件。
      • (2)内核驱动和应用层 udev 之间有一套信息传输机制(netlink 协议)。
      • (3) 应用层启用 udev,内核驱动中使用相应接口。
      • (4)驱动注册和注销的信息都会被传输给 udev,由 udev 中应用层进行设备文件的创建和删除。
    • 步骤
      • (1) class_create
      • (2) device_create
      • (3)device_destory
      • (4)class_destory

6.设备号
(1)包括主设备号和次设备号。
(2) dev_t 类型: unsigned int 类型, 32 位,用于在驱动程序中定义设备编号。高 12 位为主设备号,低 20 位为次设备号
(3) MKDEV、 MAJOR、 MINOR 三个宏: MKDEV 用于由主设备号、次设备号获得设备号,MAJOR 用于由设备号获取主设备号,MINOR 用于由设备号获取次设备号。

7.使用 cdev_alloc
从内存角度体会 cdev_alloc 用与不用的差别:
不用的 cdev_alloc 时在程序开始时直接创建一个 cdev 结构体, 在加载程序时就占用了 cdev 结构体大小的内存,直到程序运行结束时释放;用 cdev_alloc 时程序开始时创建一个 cdev 结构体指针,在加载程序时只占用了指针大小(在 32 位系统中占 4 个字节)的内存,直到运行时调用 cdev_alloc才申请 cdev 结构体大小的内存,最后在调用 cdev_del 时释放。

8.内核的虚拟地址映射方法
内核中有 2 套虚拟地址映射方法: 动态和静态
(1)静态映射
①内核移植时以代码的形式硬编码,如果要更改必须源代码后重新编译内核。
②在内核启动时建立静态映射表,到关机时销毁,中间一直有效。
(2)动态映射
①驱动程序根据需要随时动态地建立映射、使用映射、销毁映射。
②映射是短期的、临时的。

具体操作

  • 静态操作
    定义 GPIO 寄存器的相关寄存器虚拟地址,并强制类型转换为 unsigned int 型指针,并声明为 volatile。

    #define GPJ0CON S5PV210_GPJ0CON
    #define GPJ0DAT S5PV210_GPJ0DAT
    #define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
    #define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)
    rGPJ0CON = 0x1111 1111;
    rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮
    rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); //灭
    
  • 动态操作(1)

    #define GPJ0CON_PA 0xe0200240 //物理地址
    #define GPJ0DAT_PA 0xe0200244
    unsigned int *pGPJ0CON; //虚拟地址
    unsigned int *pGPJ0DAT;
    pGPJ0CON = ioremap(GPJ0CON_PA, 4);
    pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
    *pGPJ0CON = 0x11111111;
    *pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮
    *pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); //灭
    iounmap(pGPJ0CON);
    iounmap(pGPJ0DAT);
    

    两个寄存器一起映射:由于两个寄存器地址是挨着的,只需映射 GPJ0CON_PA(size 改为 8)得到*pGPJ0CON, *(pGPJ0CON + 1)就是*pGPJ0DAT。

  • 动态操作(2)
    仿效真实驱动中,用结构体封装的方式来进行单次多寄存器的地址映射,来替代多次
    映射。

    结构体及指针:
    typedef struct GPJ0REG
    {
        volatile unsigned int gpj0con;
        volatile unsigned int gpj0dat;
    }gpj0_reg_t;
    gpj0_reg_t *pGPJ0REG;
    宏定义:
    #define GPJ0_REGbase 0xe0200240
    模块安装函数:
    // 2 步完成映射
    pGPJ0REG = ioremap(GPJ0_REGbase, sizeof(gpj0_reg_t));
    // 映射之后用指向结构体的指针来进行操作
    pGPJ0REG -> gpj0con = 0x11111111;
    pGPJ0REG -> gpj0dat = ((0<<3) | (0<<4) | (0<<5)); // 亮
    模块卸载函数:
    pGPJ0REG->gpj0dat = ((1<<3) | (1<<4) | (1<<5)); // 灭
    // 解除映射
    iounmap(pGPJ0REG);
    

8.内核提供的寄存器读写接口
writel 和 readl:写和读一个 32 位的寄存器;
使用动态映射虚拟地址和静态映射虚拟地址均可以使用。

9.内核中 LED 驱动框架的基本情况

  • 1.相关文件
    (1) drivers/leds 目录:驱动框架规定 LED 驱动应该放置的目录。
    (2) led-class.c 和 led-core.c:这两个文件加起来属于 LED 驱动框架的第一部分,由内核开发者提供,描述所有厂家不同 LED 的相同部分逻辑。
    (3) leds-xxx.c:是 LED 驱动框架的第二部分,由不同厂商的驱动工程师编写添加,他们结合自己的 LED 的设备情况,使用第一部分的接口来实现最终驱动功能;
    以leds-s3c24xx.c 为例:通过调用 led_classdev_register(在 drivers/leds/led-class.c 中定义)。
    来完成 LED 驱动的注册。
  • 2.典型驱动开发行业现状
    (1)内核开发者对驱动框架进行开发和维护、升级,对应 led-class.c 和 led-core.c。
    (2) SoC 厂商的驱动工程师对设备驱动源码进行编写、调试,提供参考版本,对应leds-s3c24xx.c。
    (3)做产品的厂商的驱动工程师以 SoC 厂商提供的驱动源码为基础,做移植和测试。

10.gpiolib 使用方法
(1)使用 gpio_request 申请使用一个 IO 口。
(2)使用 gpio_direction_input/gpio_direction_output 设置输入/输出模式。
(3)使用 gpio_set_value 设置输出值,使用 gpio_get_value 获取 IO 口的值。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/277355.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号