代码就是硬件的 时序逻辑 和 命令逻辑。
驱动工程师 完成了 硬件对 应用工程师的透明。 他们是面向 应用程序员编程 提供 机制,提供文件按操作函数指针。
驱动:就是驱使硬件设备动作 硬件操作函数指针。
终极操作:就是 读写 相关 寄存器 , 时序 和 命令 完成对 硬件的操作 形成 函数指针。
操作系统下: 应用工程师 就 通过 标准接口(系统调用)去调用驱动工程师完成的机制。
无操作系统:驱动工程师 自行定义接口。 有操作系统 必须按照相应的框架 自行定义 驱动工程师 必须按照相应的标准 设计 面向应用工程师编程。
无操作系统的 设备驱动 单任务架构 可以完成比较好的 需求 。
一个无限循环 夹杂着 对 设备中断 的检测 或 对 设备的轮询 是对这种 系统中软件的典型架构。
// 应用开发工程师
int main()
{
if(seriaInt == 1)
{
// 中断处理函数
// 清空中断标志位
}
int status = Check();
switch( )
{
}
} 必定包含硬件驱动 。
裸板驱动 中 每一个模块 都会定义成一个 .c 和 .h 文件
每个模块都会是下面这种格式:
serialinit //初始化串口
serialSend //串口发送
serialRecv //串口接受
serialIsr // 串口中断处理函数
驱动程序的接口 被直接暴露给 应用工程师进行使用。
应用软件 -> 设备驱动 -> 硬件。
在存在操作系统的内核中,驱动变成了 内核 连接硬件的桥梁。
没有操作系统 完成多任务并发 是很困难的。
操作系统每个进程都享有独立的4G 地址空间。 操作系统管理了内存。
操作系统通过给驱动制造麻烦 给应用层访问提供了 便利 应用开发者不需要知道 硬件。通过系统调用就能访问硬件 来操作硬件的目的。
硬件主要由 CPU 外设 内存组成 。
CPU 内部就集成了 外设控制器 SPI IIC USB 音视频编解码。
字符 块 网络。
字符设备就是必须按照串行必须访问的设备。
linux 设备驱动工程师 有很高的要求 必须懂硬件
1、懂 SRAM SDRAM Flash 磁盘的读写方式。URART IIC USB 轮询中断 DMA的原理
2、有非常好的C语言功底。
3、有一定的内核基础。 明白 内核 和 驱动的 交互的接口。 块设备 网络设备 flash 设备 串口设备等复杂网络设备。
4、多任务并发 和 复杂控制的基础 驱动中大量使用自旋锁 信号量 等待队列 等并发 同步机制。
经验值的获取并非一朝一夕的事。足够的恒心 和 毅力。
动手进行验证和测试永远是学习任何软件开发的最好的方法。
LED 一般由 GPIO 通用可编程IO 进程控制
GPIO 一般由2组 寄存器 进程控制 数据寄存器 和 控制寄存器。
GPIO 通过控制 状态 寄存器 设置 输入 输出 两种方式。 输入 读数据数据寄存器 可知 高低 电平
输出 读数据寄存器 可知 高低电平 输出。
硬件学习
通用处理器:
soc 芯片设计方法 硬件描述语言 soc 内电路实现的
各种组件 类似堆积木的方法 组合在一起 。
存储器:
地址总线 和 数据总线。 DSP 数字信号处理。
ROM flash RAM 光/磁介质存储器
EPROM 可擦除rom
NOR 或非 SRAM 接口 不需要增加额外的控制电路 芯片内执行 NOR上可以跑代码
NAND 与非 CPU接口必须控制电路装欢 以块的方式访问 不支持 芯片内执行。
uart 通用串行总线。 // 总线通信协议
RS232 RS485 RS422
IIC 2线制通行接口。 SDA SCL // // 总线通信协议
SPI 四线制 同步通信 // 总线通信协议
USB // 总线通信协议
芯片手册的阅读方法 是 定位相关信息 精准阅读 忽略其他信息。
linux 驱动编程 本质就是 linux 内核编程
主控 SPI 作为主机 其他作为从机。
linux 内核 剖析
1、arch
包含对每种不同硬件平台的代码支持 内存管理 进程调度 中断支持等。
2、driver 硬件支持 重点
3、ipc 进程间通讯的代码
4、kernel 定时器 进程调度相关
内核做到 driver 和 arch 架构 分离 driver
不包含板级信息 kernel 的通用部分 定时器 fs 文件系统 ipc net 等 与 驱动 和 arch 分离。
linux 内核 总纲:SCHED MM VFS NET IPC 5个通用子系统 arm 板级硬件支持 和 driver
SCHED: 进程调度 task 结构体 管理 6种状态 优先级 算法
MM: 内存管理 页管理 完成 虚拟地址 到 物理地址的 的映射 通过操作 虚拟地址 来操作物理地址。
虚拟文件系统:linux 虚拟文件系统 隐藏了硬件实现的各种细节。为 所有设备提供了 接口。 对各种文件提供
提供了一种抽象。
kernal_pthread_
网络接口:提供对 网络标准的存取 和 各种网络硬件的支持
IPC: 进程间通信的 各种机制 Android 添加了 binder 机制
进程间 调度(SCHED) 第一步就是 把 代码 装载到 内存当中 (MM)
虚拟文件系统 通过网络接口 支持 NFS 网络文件系统
通过内存管理 来 支持 RAMDISK 设备。
arm 处理器的工作模式:
1、usr
2、fiq 高速数据传输 或 通道处理
3、irq 通用中断处理
4、svc 系统保护模式
5、数据中止访问模式 abt
6、系统模式 sys
linux驱动的开发者 必须牢牢的 掌握 linux 内核的 编译方法 和 构建 linux 内核 镜像。
#make config 传统的配置界面
#make menuconfig 基于文本菜单的 配置界面
运行 make menuconfig 等时 配置工具 首先分析 与 体系结构对应的
/arch/xxx/Kconfig 文件, 文件种包含一些和 体系结构相关的配置项 和 配置菜单以外
source 语句 引入 一系列Kconifg 文件 而 这些 Kconfig 可能会引入 各种 kconfig文件.
1、在linux 内核种 增加程序 需要完成 以下3项 工作
将编写的源代码 复制到 linux 内核源代码的相应目录中。
在目录的Kconfig 文件中增加关于新源代码 对应的项目编译选项
在目录的makefile文件中 增加对新源代码的编译条目。
Y N M 描述了 3态 1、可编入内核 2、 可编程 模块 3、不编入内核
1、在 driver 子目录 添加 .c 文件 和 config 目录 添加 Kconfig 配置项 和 Makefile 中的编译选项。
Linux 内核的引导过程:
SOC 内嵌了 bootrom bootloader 最著名的 bootloader 就是 Uboot
http://git.denx.de/u-boot.git/
init busybox init SysVinit systmd 等 他们职责类似
把系统启动 看成 一颗进程树 。
Linux 缩进 采用 tab 缩进。
linux 内核编程 是linux driver驱动编程的 先决条件。
一个linux 内核模块 主要由 如下几个部分的组成
模块加载
模块卸载
模块许可声明
模块可选参数 可选
模块导出符号 可选
模块作者 可选
EXPORT_SYMBOL( 符号名);
EXPORT_SYMBOL_GPL(符号名);
Linux 下 一切皆文件 意思就是 硬件就是文件。
filp <-> inode 两个重要的结构体 对 驱动文件进行管理 。
filp 打开文件时创建 并传递 给在文件上操作的 任何函数 文件关闭后 释放这个结构体 。
f_path fpath.dentry f_inode file_operations void* private_data
大多只想 被设备自定义 用于描述设备的结构体。
VFS 中 inode 包含文件访问的权限 属主 组 大小 生成文件 访问时间 最后修改时间
linux 管理文件系统的最小基本 单位 文件系统连接到 任务子目录 文件的 桥梁
struct inode {
i_mode uid gid rdev i_size ic_dev / ib_cdev / i_pipe;.
// rdev 高 12位 rdev 低20位
}
devfs
register_chrdev() 传递0可以获取可用的主设备号
sys fs 把连接在系统的设备 和 总线 组织成 一个分级的文件。
block 所有块设备
bus 所有总线类型
dev 所有
只需按照 填鸭式 地 填充 xxx. driver 的各种回调函数
bus_type device_driver device 3个结构体。
设备 和 驱动的注册时 分开的。
设备和驱动各种 涌向 内核 ,它会去寻找自己的另外一半。
match 成员函数 设备和驱动就是 红尘中 漂浮的男女
bus_type match 就是牵线的月老。 识别 什么驱动 和 什么设备配对。
一旦配对 成功 driver probe 就被执行 总线 驱动 设备 最终落实为 sysfs 中的1个目录。
struct bus_ type{
int match (struct dev, strucet drv );
}
struct device_driver {
struc bus_type;
int (probe)(dev);
int remove(dev);
}
struct device {
bus_type;
devier_drv;
}