1、驱动的基本知识
一、驱动的作用
驱动实现的是,对硬件进行驱动,充当了硬件和应用软件的桥梁,使得上层的应用层可以通过统一的接口实现硬件的访问和操作。对硬件驱动,在有无操作系统的时候,硬件的操作是类似的,但是在接口的定义,却是存在差异。
1.1、没有操作系统的驱动
一般来说,并不是所有的嵌入式设备都是需要系统的,操作系统并不是必须的。在实现功能单一,简单的、不需要多任务调度的任务的系统的时候,就不需要上操作系统,比如刷卡机、微波炉或者小灵通。因为它们实现的功能简单,只需要在一个 while 循环体内,一直做无限的循环检测或者轮询,一般误操作系统都采用了这种架构。
一般的驱动,都是以模块的进行区分,也就是软件模块内,一般有 .c 文件和 .h 文件。.h 文件,实现了头文件的包含,数据结构的定义以及外部函数的声明。 .c 文件则是对被声明的函数,完成具体的实现。在当调用这些模块的时候,只需要包含模块的头文件就可以使用定义的函数。
在没有操作系统的时候,一般是将软件的架构设置为:
也就是说,在没有操作系统的时候,一般是将驱动程序直接交给软件的应用工程师,那么这些接口是对工程师直接看到,且直接使用驱动实现的接口进行调用。但是,一般上,是不能将驱动设置为以下的两种类型:
将驱动与应用放在同一个层级,这显然是非常的不合理,因为不符合高内聚、低耦合。
内聚: 要求模块的内部,紧密结合,实现的功能专一
低耦合:要求模块之间耦合度低(模块与模块之间联系少,使之模块与模块之间独立性好),当修改模块的时候,不会因耦合度高,而动一发而牵全身,在低耦合的情况下,低耦合的模块,只需要修改本模块的代码就可以。
当在应用层里面直接进行驱动硬件的时候,那么这种情况是更为糟糕的,因为没有通过单独设计驱动模块,也就是说,这些驱动的代码没有被重用(每一个需要驱动硬件的时候,就需要重新编写代码)
1.2、有操作系统时候的驱动
当有操作系统时候,驱动首先,对于实现硬件操作的部分是必不可少的;同时,因为有了操作系统(内核),这个时候需要面向操作系统进行驱动架构的设计(其实,内核就像是提供了框架的接口,比如插座;而驱动就必须针对这种框架的接口,编写代码,实现对用的接口,比如插头),而操作系统提供的驱动框架的接口,是针对某一类设备而言,结构一致,这些接口不是针对某个具体的设备。
驱动的分类:
字符设备:指的那些必须以串行顺序(一个字符、一个字符)依次进行访问的设备,一般是触摸屏、LED、鼠标、
块驱动设备:可以按任意的顺序访问设备,但是以块为单位进行操作,比如硬盘、EMMC
网络设备:针对网卡驱动和协议栈。
当存在操作系统的时候,驱动编程了连接硬件和内核的桥梁:
可见,这个时候的驱动:设备的接口 + 硬件的操作,驱动编程了连接硬件与操作系统(内核)的桥梁了。驱动的代码部分,很显然会多了一些设备接口操作,很显然会带来比没有操作系统时候更多的代码,之所以这样做,是因为有操作系统的时候,上层的应用层就可以有统一的接口进行访问,而底层驱动实现比较麻烦,但是好在上层应用访问比较容易,估计这就是俗话说的,把悲伤留给自己。
二、硬件的基本知识
连接嵌入式系统中,较为常用硬件基本知识。
2.1、处理器
处理器(CPU),既处理器,实现任务的计算功能。早起的 CPU 就是CPU ,而到后期集成电路的发展,在 CPU 封装的时候,而外将外设的控制器(比如串口)和外设(比如显卡)直接封装了进去,这个时候的 CPU 不只是基本的 CPU ,我们可以称之为 SOC,也就是 system on chip,片上系统。
2.1.1、哈佛结构与冯诺依曼结构
一般的处理器是可分为两种结构,一种是哈佛结构、一种是冯诺依曼结构。
哈佛结构: 比如现在的 ARM9、ARM11、Cortex 系列。
是将程序指令与数据进行分开的存储,也就是说,指令和数据可以有不同的数据宽度;同时,人们还采用了独立的总线,既程序总线和数据总线,分别为程序指令和数据专用通道,加快了执行的效率。
哈佛结构的原理图:
冯诺依曼结构: 也被橙汁为普林斯顿结构,比如之前的 ARM7、MIPS 系列的处理器。
是程序指令存储器与数据存储器合并在一起的存储器结构,程序指令存储地址以及数据存储地址指向了同一个存储器的不同物理地址,此时要求程序指令和数据的宽度是相同的。程序指令和数据走的是同一条总线。
哈佛结构与冯诺依曼的结构差异,比较的明显,其实从是否有专有的总线就可以进行区分。
2.1.2、精简指令集与复杂指令集
不同的 CPU 厂家,会提供自己的指令集(想当时不同区域的人,有自己当地的方言),这些指令集可以分为精简指令集和复杂指令集。
复杂指令集: 以上楼梯为例子,每走一步、每一步的步伐大小、速率都不一样,但是这些不同的走法,都有对应的、确定的一条指令,楼梯20步,所以就有20条指令,因此目标的代码就会是比较的少,但是需要程序猿记住 N 多的指令,复杂的指令,指令的周期长。复杂指令集,较为代表性的是 intel 的 X86。
精简指令集: 是相对于复杂的指令集而言的,之所以称之为精简,是因为只提供了,基本的、常用的指令,还是以上楼梯为例,就只是提供了较为常用的上楼梯指令,具体楼梯的步伐长度、速率,需要程序猿自己去设置,因为生成的代码量就会是比较的长,但是降低程序猿记住复杂多样的指令。精简指令集,较为代表性的是 MIPS、ARM、Power 架构的 CPU。
2.2、存储器
一般存储器,分为只读存储器(ROM)、闪存(flash)、随机存取存储器(RAM),这些可以掉电时候,数据是否丢失进行区分
对于 ROM 来说,现在一般都是 EEPROM(也就是 点可擦除编程 ROM),走的是 IIC 协议,后面就会学习到。
对于 flash 而言,intel 在 88 年开发的 nor flash,彻底更改了 EEPROM 一桶添加的局面,NOR Flash 的接口书类似于 SRAM 的接口,所以可以通过CPU 的数据总线以及地址总线访问设备,不在需要而外的控制电路,因为接口是 类SRAM的,所以就支持 XIP(execute in place),也就是程序可以直接在nor 上面被 CPU 加载运行。
Nand Flash 的接口则不是,NAND Flash拥有属于自己的接口,这些接口虽然是可以通过 GPIO 进行模拟,但是因为有Nand Flash 的控制器,所以一般是通过 Nand Flash 的控制器进行控制。 Nand Flash 比 NOR flash 而言,存储量比较的大,起码是一个数量级,而且写入速度也是快了很多。
Flash 的编程原理是稚嫩通过将 1 写为 0,不能将 0 写为 1,也就是 falsh 编程之前,必须进行块的擦除,其实就是将整个数据块全部设置为1(0XFF),后续写入的数据,为1 的时候,就不需要更改,写入为 0 的时候,就 将1 变为 0。
RAM 可以分为: 静态的RAM,也就是 SRAM,和动态 RAM,既DRAM。SRAM 是数据被写上之后,就保持较为稳定的状态,因此不需要定期去刷新数据,一般 CPU 内部的高速缓存(cache)就是 SRAM。而DRAM,是动态的,一般是以电荷的形式进行存储,也就是数据被存储在电容器,电容器因为漏电会导致电荷的流失,所以需要在定期地进行数据的刷新,我们常说的 SDRAM、DDR SDRAM 都是属于 DRAM 的范畴,一般将其理解为内存比较好理解。