回 顾
第四章 嵌入式操作系统开发 4.1 Intel基于PCA架构的嵌入式系统平台 4.2 为什么要用RTOS 4.3 操作系统的内核编译 4.4 什么是BootLoader 4.5 嵌入式操作系统的驱动开发 4.6 嵌入式操作系统的远程调试
4.1 Intel基于PCA架构的嵌入式系统平台
Intel 的PCA Intel 个人互联网用户架构(Personal Internet Client Architecture) 2000年9月推出,面向无线互联网的嵌入式系统架构。 开放式平台架构 应用子系统 通信子系统 内存子系统 各个子系统通过开发式接口相互隔离
Standard Bus Interface Intel PCA 构筑模块 Intel® PCA 软件 计算 / 应用 Intel® XScale™ Microarchitecture and SA-1110 w/ Intel® Integrated Performance Primitives Memory Intel® Flash Software Standard Bus Interface 通信 Intel® baseband chipsets with Intel® Micro Signal Architecture w/Intel® Integrated Performance Primitives Intel® Logic Process Volume Manufacturing Package Technology Wireless Competency Centers Intel® Flash Intel® IPPs XScale Core MSA Intel Value Proposition
Intel PCA 分层软件结构 纵向的, 任务导向的设计 模块化结构 开放接口 可扩展性 . . . . . . Platform Management APIs Mobile Applications Applications, Libraries, e.g., Intel® IPPs System Process 纵向的, 任务导向的设计 模块化结构 开放接口 可扩展性 Telephony Data Resource Authentication SIM Manager DSP Abstraction Other Bluetooth* Power Manager Services Wireless Modem Protocols Operating System(s) & OS APIs Calibration OS Message Driver 1 Services Passing Stack 1 Test Diag Download Driver 2 Stack 2 . . . . . . Memory Stack n Driver n System Manager Hardware Software
PCA应用子系统的开放式软件框架 1. 平台服务: 2. 操作系统: 3. 中间件: 4. 固有运行时程序库: 5. 用户应用: 多媒体服务 安全服务 平台管理 通信服务 3. 中间件: 软件组件提供应用新服务 4. 固有运行时程序库: Intel提供集成性能函数库(IPP) 5. 用户应用: 为用户提供各种最先进的功能。 加图。
Reference/Development Intel PCA 开发工具 Performance Analyzer Host Simulator C Libraries .exe Code (binary) IDE Compile Assembler Disassembler Linker Logic Analyzer Debugger Symbol Tables JTAG Trace Download Binary Results JTAG Tool Reference/Development Board Target 为开发者提供完整同一的开发工具
4.2 为什么要用 RTOS
1. 嵌入式操作系统的特点 嵌入式OS应该 嵌入式OS的分类 编码体积小 面向应用,可裁剪、可移植 实时性强 高可靠性 基于或与Windows兼容 Window CE 嵌入式Linux 工业与通信类 Vxworks 单片机类 μC/OS
2. RTOS需求背景 是指系统能及时(或即时)响应外部事件的请 求,在规定的时间内完成对该事件的处理, 并控制所有实时任务协调一致地运行。 实时系统应用需求 实时控制。 实时信息处理。
3. RTOS的特点 实时为其主要特征。 RTOS系统中必须支持优先级操作。实时系统下的每一个任务都必须有一个优先级 真正的实时系统是指基于优先级的抢占式操作系统。 非抢占调度方式(Non-preemptive Mode) 抢占调度方式(preemptive Mode)
4. 抢占调度方式 适用于紧急任务 要求立即执行。 抢占的原则有: 优先权原则。 短作业(进程)优先原则。 时间片原则。
5. RTOS 抢占型内核的调度 RTOS内核对进程的控制调度原则 抢占过程: 当有更高优先级的任务就绪时总能够得到一个CPU的控制权 当一个运行着的任务使一个比他的优先级高的任务进入到就绪状态时,当前任务的CPU使用权就立即被剥夺,或者叫挂起。而优先级高的进程立刻得到CPU的控制权。 如果是中断服务子程序使一个高优先级的任务进入到就绪态,当中断完成时,被中断的任务会被挂起,而让高优先级进程先运行。
1. linux操作系统的内核编译 2. Window CE的内核定制 4.3 操作系统的内核编译 1. linux操作系统的内核编译 2. Window CE的内核定制
4.3.1 linux操作系统的内核编译
1. Linux OS 的组成 Linux 内核 Linux Shell Linux 文件结构 Linux实用工具 实际上是从抽象资源操作到具体硬件操作细节之间的接口 Linux Shell 系统的用户界面,提供用户与内核进行交互的接口 Shell实际上是一个命令解释器。有如下主要Shell: Bourne Shell ----贝尔实验室开发; BASH-----GNU的OS默认的Shell; Korn Shell----兼容Bourne Shell 但有所发展 C Shell----Sun的Shell的BSD版本 Linux 文件结构 采用多级目录树结构 Linux实用工具
2. Linux操作系统结构图
2. Linux操作系统结构图 进程管理 虚拟文件管理 存储器 管理 网络 协议栈 设备驱动程序
3. Linux的内核编译方法 三条命令来进行配置内核: 文件系统 Jffs2与Extfs Make config (文本界面方式) Make menuconfig (文本界面 菜单驱动) Make xconfig (图形界面方式) 文件系统 Jffs2与Extfs Jffs2文件系统具有更好的崩溃恢复机制
3. Linux的内核编译方法 内核可以编译成三种形式: 映像文件的生成 Vmlinux (无压缩可执行文件) Image (压缩文件) zImage (自解压压缩文件) 映像文件的生成 Mkfs.jffs2工具
4.3.2 Window CE的内核定制
Windows CE 的内核模块组成
Windows CE 内核模块功能 内核 持久存储 图形与多媒体 进程间通信 通信服务 安全服务 用户界面服务 Internet服务 内核模块不可选,其它模块都为可选 内核 持久存储 图形与多媒体 进程间通信 通信服务 安全服务 用户界面服务 Internet服务
Windows CE 内核定制流程 1. 对操作系统进行裁剪,配置上述各个组件; 2. 建立定制WinCE的Image文件; 1. 对操作系统进行裁剪,配置上述各个组件; 2. 建立定制WinCE的Image文件; 3. 如果目标系统本身也是定制的,就需要开发OAL层(OEM适配层)和该系统的WinCE加载程序; 4. 将建立的Image文件下载到目标设备上,进行调试; 5. 重复上述过程直到达到要求。 6. 在定制OS基础上,可发布SDK供软件开发者使用。
4.4 什么是BootLoader
1. 系统的启动与引导 台式机 嵌入式系统 固件中的引导代码和Boot Loader两大部分; 引导装载程序装入主引导记录MBR中; 由BIOS将控制权移交给引导装载程序; 嵌入式系统 没有BIOS固件程序; 加载任务由Boot Loader唯一完成; 系统加电或复位都是从地址0x0开始运行,因此Boot Loader程序都是存放在该地址中。
2. 什么是Boot Loader Boot Loader是在OS内核之前运行的一段小程序。 Boot Loader是硬件专用的 可初始化硬件设备; 建立内存空间映射图; 为调用OS内核准备环境; 下载内核映像与文件系统。 Boot Loader是硬件专用的
3. 关于Boot Loader Boot Loader支持的硬件环境 Boot Loader的安装地址 多阶段启动 启动下载模式与下载模式 串口协议与TFTP协议
4. Linux下Boot Loader的结构 大多数Boot Loader分为阶段1和阶段2 阶段1依赖与CPU体系结构,如设备初始化代码等,一般采用汇编语言来实现; 阶段2 用C语言实现,可以完成一些复杂的功能,并且具有更好的可读性及可移植性。
可以通过修改PC寄存器为合适的地址来实现 5. Boot Loader阶段 1 1. 基本硬件初始化 2. 为加载阶段2准备RAM空间 3. 复制阶段2到RAM空间 4. 设置堆栈 嵌入式系统物理内存布局 5. 跳转到阶段2的C入口点 可以通过修改PC寄存器为合适的地址来实现
6. Boot Loader阶段 2 一个问题: Boot Loader 的C语言代码如何执行? 2. trampoline(蹦床)程序 汇编代码作为阶段2可执行映像的入口点; Trampoline作为main()函数的外部包裹程序;
6. Boot Loader阶段 2 4. 为内核设置启动参数 1. 初始化本阶段要使用的硬件设备 2. 检测系统的内存映射 1. 初始化至少一个串口,初始化计数器等; 2. 初始化之前,可重新点亮LED表明进入main函数运行。 1. 初始化本阶段要使用的硬件设备 2. 检测系统的内存映射 内存描述 内存映射的检测 3. 加载Kernel映像和根文 件系统映像。 规划内存占用布局 从Flash/ROM上复制 4. 为内核设置启动参数 内核用标记列表来传递启动参数 启动参数ATAG_CORE; ATAG_MEM; ATAG_CMDLINE; ATAG_RAMDISK; ATAG_INITRD. 5. 调用内核
7. 关于串口终端 1. 在BootLoader程序设计中,从串口收到正确的打印信息说明BootLoader程序引导系统正常执行 7. 关于串口终端 1. 在BootLoader程序设计中,从串口收到正确的打印信息说明BootLoader程序引导系统正常执行 串口的“booting the kernel …“ 内核启动信息才能说明BootLoader已经成功运行。 2. 串口终端常见问题解决
8. XSBase 的Boot Loader安装与使用 从www.tup.com.cn下载源码: Boot-XSBase255.tar.gz 1. Building xboot255 2. 下载Boot Loader 到目标板 使用与源码同时编译的烧写工具jflash-XSBase255进行烧写 Boot Loader提供了18条命令给用户使用 3. 熟悉使用Boot Loader指令
9. 2款常用的开源boot loader工具 Blob Blob是“Boot loader Object”的缩写,是一款嵌入式linux引导程序。最初是Jan-Derk Bakker和 ErikMouw针对LART目标板而设计的,同其他开源代码一样,很多人未blob的发展做出了自己的贡献。到现在为止,blob已经支持多种cpu,包括sa1100,sa1110,pxa255,pxa270等,用户可以根据目标板的特性进行定制。 它能实现以下功能: 引导嵌入式linux,它可以把linux、kernel等从flash中load到Ram中执行 命令行下在线更新blob、kernel和ramdisk。 命令行下可以直接对物理寻址空间进行查看和修改。 可见blob除了引导系统这个基本功能外,还具备BSP开发的功能,对于嵌入式开发前期,blob是一个非常有效的工具。
9. 2款常用的开源boot loader工具 Blob Blob实现功能: 是“Boot loader Object”的缩写。最初是Jan-Derk Bakker和 ErikMouw针对LART目标板而设计的, 到现在为止,blob已经支持多种cpu,包括sa1100,sa1110,pxa255,pxa270等,用户可以根据目标板的特性进行定制。 Blob实现功能: 引导嵌入式linux,从flash中load到Ram中执行 命令行下在线更新blob、kernel和ramdisk。 命令行下直接对物理寻址空间进行查看和修改。 除了引导系统外,还具备BSP开发的功能,对于嵌入式开发前期,blob是一个非常有效的工具。
9. 2款常用的开源boot loader工具 U-Boot U-Boot实现功能: 由德国工程师Wolfgang Denk从8XXROM代码发展而来 支持多种cpu,包括PowerPC,ARM,MIPS,x86. 源代码可在http://sourceforge.net 下载得到。 U-Boot实现功能: 在线读写Flash、IDE、EEROM、DOC、RTC ; 识别二进制、ELF32、uImage格式的Image; 单任务软件运行环境,可动态加载和运行独立的程序; 支持脚本语言; 支持MTD和文件系统; 支持中断。 支持WatchDog、LCD logo和状态指示功能。
设备驱动程序概述 Linux系统的驱动程序开发 WinCE系统的驱动程序开发 4.5 嵌入式操作系统的驱动开发 设备驱动程序概述 Linux系统的驱动程序开发 WinCE系统的驱动程序开发
4.5.1 设备驱动程序概述
OS中设备驱动程序主要功能 探测设备和初始化设备 从设备接收数据并提交内核 从内核接收数据送到设备 检测和处理设备错误
OS中设备驱动程序分类 Linux操作系统 字符类型设备 块设备 网络设备 WinCE系统设备驱动模型 流接口驱动 本地设备驱动
OS中设备驱动程序的特点 读/写 中断 时钟 每个驱动程序要复制本设备的读/写操作。OS定义好读/写接口,再由驱动程序完成具体的功能。
OS中设备驱动程序的调用 Linux WinCE 采用内核模块机制 驱动模块没有编译到内核模块中 模块的插入与卸载 Insmod rmmod WinCE 驱动程序通常被编译成动态链接库DLL,进行动态加载。 驱动的安装: 连接WinCE设备; 将设备驱动程序的dll文件拷贝到Windows目录下; 在注册表中创建相应的项,填入键值。
4.5.2 Linux系统的驱动程序开发
1. Linux的设备文件 硬件设备看作普通文件,用标准调用接口来完成文件的打开关闭、读写控制。 设备文件的设备号 都用open()打开,read()和write()读写; 驱动主要任务就是实现这些系统调用函数。 设备文件的设备号 主设备号: 次设备号: 已注册的硬件设备的主设备号在/proc/devices查看 标识设备种类,使用的驱动程序 标识同一驱动的不同硬件设备
1. Linux的设备文件 设备驱动源代码分布 应用对硬件设备调用 /drivers/block: /drivers/char: /drivers/cdrom: /drivers/pci: /drivers/scsi: /drivers/net: /drivers/sound: 应用对硬件设备调用 内核根据设备类型和主设备号调用相应的驱动 从用户态进入到内核态; 驱动判断此设备号,完成对相应硬件的操作。
2. Linux设备驱动程序结构 设备驱动的接口 与OS内核的接口 与系统引导的接口 与设备的接口 ./include/linux/fs.h中的file_operations数据结构来完成 与系统引导的接口 利用驱动程序对设备进行初始化; 与设备的接口 描述驱动如何与设备进行交互,与具体设备密切相关。
2. Linux设备驱动程序结构 设备驱动的功能 驱动的注册与注销 设备的打开与释放 设备的读写 设备的控制; 设备的中断和轮询处理
3. Linux设备驱动程序举例 (1) 基本字符设备驱动程序设计 确定主要数据结构 和全局变量 设计主要接口函数 设计模块加载方式 没有具体功能,仅实现驱动程序框架 实现4个基本操作 Xsbase_open() Xsbase_write() Xsbase_read() Xsbase_release() 设计主要接口函数 设计模块加载方式 驱动安装过程 驱动测试 结束
4. 设备驱动设计中涉及的具体问题 用户空间和内核空间 应用程序运行在用户空间,模块运行在内核空间 多用户OS的授权访问机制: Linux的ring0级和ring3级; Linux通过系统调用或者中断完成从用户空间到内核空间的转换。 Copy_to_user()和copy_from_user()函数调用 用户空间与内核空间之间的数据传输; 用来检查用户空间的指针是否有效;
4. 设备驱动设计中涉及的具体问题 Proc文件系统 是另一种内核和内核模块向进程传递信息的方法 Proc文件系统的目录结构举例 以文件系统方式为访问系统内核数据的操作提供接口; /proc下面每个文件都被绑定到一个内核函数,函数动态的生成文件的内容; 用户和应用程序可以通过Proc得到系统信息,并可以改变内核的某些参数。 Proc文件系统的目录结构举例
系统注册的中断信息在:中断号、收到的中断数、驱动器名; 4.1 Proc文件系统的目录结构举例 文件/目录名 描述 Device 系统字符和块设备编号与驱动程序名 Driver 组织了不同的驱动程序; Interrupts 系统注册的中断信息在:中断号、收到的中断数、驱动器名; IRQ 与CPU相关的中断掩码; Modules 系统正在使用的module信息 Net 网络信息目录 Scsi Scsi设备信息 Sys 可更改的内核数据的目录 tty 和终端相关的数据
4.2 /proc/sys下的文件和目录举例 文件/目录名 描述 Kernel/acct 进程帐号控制值 Kernel/domainname 机器域名 Kernel/hostname 主机名 Kernel/osrelease 内核版本号 Kernel/printk 内核消息日志级数 Net/core 通用网络参数 Net/ipv4 IP网络参数 vm/bdflush 磁盘缓冲区刷新参数 Vm/freepages 最小自由页数
4.6 嵌入式应用的远程调试 GNU GDB的远程调试功能
1. PC系统与嵌入式系统调试 调试环境上的区别 嵌入式调试环境带来的问题 调试器与被调试程序如何通信? 被调试程序产生异常如何及时通知调试器? 调试器如何控制、访问被调试程序? 调试器如何识别有关被调试程序的多任务信息并控制某一特定任务? 调试器如何处理目标平台的硬件信息?
2. 嵌入式系统的远程调试环境的创建 用插入Stub程序来解决上述 问题 调试器与被调试程序如何通信? 通过制定通信端口遵循远程调试协议进行通信。 http://rtos.ict.ac.cn/rtos/debugger/ 用插入Stub程序来解决上述 问题 调试器与被调试程序如何通信? 被调试程序产生异常如何及时通 知调试器? 调试器如何控制、访问被调试程 序? 调试器如何识别有关被调试程序 的多任务信息并控制某一特定任 务? 调试器如何处理目标平台的硬件 信息? 目标系统的异常处理专项通信模块通知调试器当前的异常号;调试器将异常告知用户。 控制请求转换成对目标程序地址空间或目标平台寄存器的访问。 由目标操作系统提供相关接口。目标系统根据请求,调用该接口提供信息或进行控制。 调试器能根据异常号识别目标平台产生异常的类型,可有调试器独立完成。
2. 嵌入式系统的远程调试环境的创建 远程调试环境示意图 目标平台桩模块: 支持远程调试协议的 通信模块; 多任务调试接口; 支持远程调试协议的 通信模块; 多任务调试接口; 修改异常处理程序; 断点设置函数。 运行调试器 指定调试通信接口 向目标系统发消息 目标系统初始化 至应用程序入口 主动触发异常 异常处理程序转入 调试端口通信 主机 目标平台 调试器的桩模块
2. 嵌入式系统的远程调试环境的创建 插桩的两种实现方式 插桩方法的使用特点 第一种:ROM Monitor; 第二种:通用操作系统实现。 打开嵌入式Linux OS的kgdb开关,就相当于插桩。 插桩方法的使用特点 实质上是用软件接管了目标系统的全部异常处理和部分中断处理; 一般适用于对应用程序的调试; 通常在调试版的目标系统上使用。
3. GDB的远程调试功能 什么情况下需要使用GDB的远程调试功能? GDB远程调试环境原理 内核 应用程序 Stub程序 远程主机 GDB/XGDB X-Win环境 本地主机 串口或TCP/IP链接
小 结
Boot Loader Stage 1—基本硬件初始化 1. 屏蔽所有中断 Boot Loader 不必响应中断,中断屏蔽可通过写ICMR寄存器来完成 2. 设置CPU的速度和时钟频率 RAM初始化 设置系统内存控制器的功能寄存器 3. 初始化LED 用GPIO来驱动LED,目的是表明系统的状态; 也可以初始化UART,向串口打印Logo字符的形式 4. 关闭CPU内部的指令/数据Cache。
Boot Loader Stage 1—为阶段2准备RAM 1. 为了加快执行速度,通常把阶段2的C代码加载到RAM空间执行 2. 准备RAM空间 地址范围可任意安排,如空间最低或最高端,一般不超过1MB; 要考虑可执行映像+堆栈空间; 空间大小最好是内存页大小(4KB)的整数倍 3. 确保所安排的地址范围是可读写RAM 对所安排地址进行测试; 4. 复制阶段2代码到RAM中 要确定可执行映像在Flash/ROM中的起始/终止地址 RAM空间的起始地址
Blob的RAM地址测试算法 1. 以Memory Page为单位进行测试,测试每个页的头两个字是否可读写。 2. 步骤: 2)向这两个字中写入任意数字,如0x55和0xaa; 3)立即读回,检查是否一致。如果不一致,则说明所分配不是一段有效RAM空间。 4)恢复头两个字的原始内容; 3. 为了得到一段干净的ARM空间,可以将分配的Ram空间进行清零处理。
Boot Loader Stage 1—设置堆栈指针sp 1. 堆栈指针的设置是为执行C语言代码做准备 2. 通常设置sp指针的值为 stage2-4 阶段2的RAM空间的最顶端; 堆栈向下生长。 3. 系统物理内存布局应该如图所示。
Boot Loader Stage 2—Trampoline示例 .text .globl _trampoline _ trampoline: B1 main /*如果main函数返回,就简单的再调用它*/ B _ trampoline
Boot Loader Stage 2—检测系统内存映射 将整个4GB的物理地址空间中那些地址范围分配用来寻址系统的RAM单元。 必须在内核映像读到RAM之前对整个系统内存映射进行检查。 描述RAM地址空间的连续地址范围 内存映射的检测
RAM地址空间连续地址范围描述 内存区域的基地址 内存区域的长度 Used=1:已映射; Used=0:未映射。 Typedef struct memory_area_struct { u32 start; u32 size; int used; } memory_area_t 内存区域的基地址 内存区域的长度 Used=1:已映射; Used=0:未映射。 Memory_area_t memory_map[NUM_MEM_AREAS]={ [0 … (NUM_MEM_AREAS-1)] = { .start = 0, .size = 0, .used = 0}, };
内存映射的检测 For(i=0;i<NUM_MEM_AREAS;i++) memory_map[i].used = 0; 数组初始化 For(i=0;i<NUM_MEM_AREAS;i++) memory_map[i].used = 0; For(addr=MEM_START;addr<MEM_END;addr+=PAGE_SIZE) *(u32 *) addr = 0; For(I = 0,addr=MEM_START;addr<MEM_END;addr+=PAGE_SIZE) { test_mp(); If(current memory page isnot a valid ram page){ if(memory_map[i].used) i++; continue; } If(* (u32 *)addr != 0){ if(memory_map[i].used) i++; 检测从基地址MEM_START+i* PAGE_SIZE开始,大小为PAGE_SIZE的地址空间是否是有效的RAM地址空间 当前页已经是一个被映射到RAM的有效地址范围; 但是还要看看当前页是否只是4GB地址空间中某个地址页的别名。
内存映射的检测 当前页已经是一个被映射到RAM的有效地址范围; 而且它也不是4GB地址空间中某个地址页的别名。 If (memory_map[i].used ==0) { memory_map[i].start = addr; memory_map[i].size = PAGE_SIZE; memory_map[i].used = 1; }else{ memory_map[i].size += PAGE_SIZE; }
规划内存占用布局 要考虑基地址和映像的大小两个方面 内核映像一般将其复制到从MEM_START+0x8000这个基地址开始的约1MB大小的内存范围内(Linux内核不超过1MB) MEM_START~MEM_START+0x8000之间存放全局数据结构,如启动参数和内核页表等信息。 根文件系统一般复制到MEM_START+0x100000开始的地方;
从Flash/ROM上复制 统一编址,Flash寻址与RAM寻址的方法相同 可用一个循环来完成从Flash设备上复制映像的操作 While(count) { *dest++ = *src++; count -= 4; }
标记列表结构 struct tag { struct tag_header hdr; union { struct tag_core core; struct tag_mem32 mem; struct tag_videotext videotext; struct tag_ramdisk ramdisk; struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; struct tag_videolfb videolfb; struct tag_cmdline cmdline; struct tag_acorn acorn; struct tag_memclk memclk; } u; }; struct tag_header { u32 size; u32 tag; };
ATAG_CORE 参数的设置 内核启动参数在内存中的起始基地址 tag_array = (struct tag*) Boot_params; tag_array->hdr.tag=ATAG_CORE; tag_array->hdr.size=tag_size(tag_core); tag_array->u.core.flags=1; tag_array->u.core.pagesize=4096; tag_array->u.core.rootdev=0x00100000; tag_array=tag_next(tag_array); 宏,计算下一个Tag起始地址。 根文件系统设备ID在这设置
每个有效内存段对应一个ATAG_MEM参数标记 For(I = 0;i< NUM_MEM_AREAS; i++){ If(memory_map[i].used){ tag_array->hdr.tag=ATAG_MEM; tag_array->hdr.size=tag_size(tag_mem32); tag_array->u.mem.size=0x04000000; tag_array->u.mem.start=0x20000000; tag_array=tag_next(tag_array); }
ATAG_CMDLINE 参数的设置 Linux内核在启动时可以命令行参数的形式接收信息 For(p=cmdline;*p==‘ ‘;p++){ If(*p==‘\0’ ) return; tag_array->hdr.tag=ATAG_CMDLINE; tag_array->hdr.size=tag_size(tag_cmdline); Strcpy(tag_array->u.cmdline.cmdline,p); tag_array=tag_next(tag_array); } 用命令行字符串: Console=ttyS0,11520n8 来通知内核用ttS0做控制台,串口采用1152bps,无校验位,8位数据位。
告诉Linux内核解压后的Ramdisk有多大KB ATAG_RAMDISK 参数的设置 告诉Linux内核解压后的Ramdisk有多大KB tag_array->hdr.tag=ATAG_RAMDISK; tag_array->hdr.size=tag_size(tag_ramdisk); tag_array->u.ramdisk.start = 0; tag_array->u.ramdisk.size = RAMDISK_SIZE; tag_array->u.ramdisk.flag = 1; tag_array=tag_next(tag_array); 自动调入ramdisk
告诉Linux内核在RAM中的什么地方可以找到initrd映像以及它的大小。 ATAG_INITRD 参数的设置 告诉Linux内核在RAM中的什么地方可以找到initrd映像以及它的大小。 tag_array->hdr.tag=ATAG_INITRD2; tag_array->hdr.size=tag_size(tag_initrd); tag_array->u.ramdisk.start = RAMDISK_RAM_BASE; tag_array->u.ramdisk.size = INITRD_LEN; tag_array=tag_next(tag_array); Initrd映像的长度
Boot Loader Stage 2—调用内核 直接跳转到内核的第一条指令处,即MEM_START+0x8000地址处。 跳转前要检查下列条件是否满足: CPU寄存器设置 CPU模式 Cache和MMU的设置 R0 = 0; R1 = 机器类型ID; R2 = 启动参数标记列表在 RAM中起始基地址 禁止中断IRQs和FIQs; CPU使用SVC模式。 MMU必须关闭; 指令cache可开可关; 数据cache必须关闭;
串口终端常见问题解决 1. 串口终端显示乱码或根本没有显示等问题的解决: Boot Loader对串口的初始化设置不正确; 运行在host端的终端程序对串口的设置不正确,如波特率、奇偶校验、数据位和停止位等设置。 2. Boot Loader运行时可正确输出,但内核启动后却无法看到内核的启动输出信息的问题解决: 首先确认编译内核时是否配置对串口终端的支持;驱动配置正确; Boot Loader与内核对CPU时钟频率配置不一致,也会导致该问题 确认Boot Loader使用的内核基地址与内核映像编译时所用的运行基地址相同。
Boot Loader指令 Help Memcpy Reload Memdump Bootp Hexdump Tftp Memcmp Flash Erase Lock Unlock Boot Memcpy Memdump Hexdump Memcmp Memset Write Read Status reboot
设备的注册与注销 注册 注销 为设备赋予一个主设备号; Fs/devices.c中的register_chrdev()函数,或 Fs/block_dev.c的register_blkdev()函数。 注销 释放主设备号; Fs/devices.c中的unregister_chrdev()函数,或 Fs/block_dev.c的unregister_blkdev()函数。
设备的打开与释放 用打开设备来完成设备的初始化准备 Open()完成的工作: 释放设备 Include/linux/fs.h中file_operation结构的open(); Open()完成的工作: 先检查设备错误,如设备未准备好等错误; 第一次打开要初始化硬件设备; 识别次设备号,更新读写操作当前位置指针f_ops; 分配和填写要放在file->private_data中断数据,计数器增1。 释放设备 file_operation结构的release(); Release()的操作与open() 相反。
block_read() / block_write() 设备的读写操作 字符设备的读写 直接使用函数read()/write(); 块设备的读写: 调用函数block_read() / block_write(); block_read() / block_write() 向设备请求表中增加读写请求,让内核可对请求进行优化; 块读写是对内存缓冲区进行读写; 如果需要真正的从/向设备读/写,要使用结构blk_dev_struct中的request_fn()来完成。
设备的控制操作 对设备除了读写,还需要控制; 设备驱动中的ioctl() 函数完成控制功能: Ioctl() 与设备密切相关,需具体分析
设备的轮询与中断 中断处理与轮询查询 中断是最有效的响应方法 不支持中断的硬件设备读写时要轮询设备状态; 内核定期查询对CPU资源是个浪费; 定时器轮询。 中断是最有效的响应方法 硬件在需要的时候想内核发出中断信号; 内核通过驱动注册过的中断,把硬件中断信息传递给相应的设备驱动; 对中断资源请求在驱动初始化时已完成; 如何将中断发送给CPU取决于体系结构。
驱动注册过的中断信息 中断例程与中断号通过注册已被记录,可在/proc/interrupts查询。
基本字符驱动主要数据结构和全局变量 Fs/device.c中的device_struct结构 Struct device_struct{ 是管理设备的主要数据结构,包含了设备接口fops。 当字符设备注册到内核,设备名字和相关操作被添加到chrdev[]全局数组中; Chrdev[]可组织最多255个device_struct结构。 Struct device_struct{ const char * name; struct file_operations * fops; } Static Struct device_struct chrdevs[MAX_CHRDEV];
基本字符驱动主要接口函数设计 只需实现驱动需要的接口 Static struct file_operations chr_fops = { read: xsbase_read; write: xsbase_write; open: xsbase_open; release: xsbase_release; } Static int xsbase_open (struct inode*inode,struct file *file){ MOD_INC_USE_COUNT; printk(“this chrdev is opened!”); return 0} Static int xsbase_release (struct inode*inode,struct file *file){ MOD_DEC_USE_COUNT; printk(“this chrdev is released!”);
基本字符驱动主要接口函数设计 从内核空间的数据队列中读取数据data Xsbase_write使用: Copy_from_uer() Static ssize_t xsbase_read (struct file *file, char *buf, size_t count,loff_t *f_pos) { Int len; If(count < 0) return –EINVAL; Len = strlen(data); If(len < count) count = len; Copy_to_user(buf,data,count+1); return count } Xsbase_write使用: Copy_from_uer()
基本字符驱动的模块加载 Int init_module(void) { Int res; Res = register_chrdev(0,”xsbase”,&chr_fops) If(res < 0) printk(“can’t get major name!”); return res; } If(xsbase_major == 0) xsbase_major = res; Return 0; Int cleanup_module(void) { unregister_chrdev(xsbase_major,”xsbase”) ;}
1. 新建xsbase.c文件实现主要的数据结构和函数接口; 2. 编译源文件: 3. 生成xsbase.o文件,并下载到开发板上 基本字符驱动的安装 1. 新建xsbase.c文件实现主要的数据结构和函数接口; 2. 编译源文件: $Arm-linux-gcc xsbase.c –c xsbase.c 3. 生成xsbase.o文件,并下载到开发板上 4. 登录minicom; 5. 在minicom下,使用命令加载驱动模块: $Insmod xsbase 6. 创建一个设备文件 $mknod /dev/xsbase c major minor
基本字符驱动的测试 #include<stdio.h> #include<sys/types.h> #include<fcntl.h> Int main( ) { Int fd,length,rlen; Int I; Char *buf = “hello,world”; Char readbuf[12] = {0}; Fd = open(“/dev/xsbase ”,O_RDWR); If(fd <= 0) { printf(“Error opening device for writing!”); exit(1); }
基本字符驱动的测试 length = write(fd, buf, strlen(buf)); If(length < 0) { printf(“Error writing to device!”); exit(1); } rlen = read(fd, readbuf, 12); If(rlen < 0) printf(“Error writing to device!”); printf(“The read result is %s\n”,readbuf”); close(fd); return 0;
固态存储舍不得典型空间分配 0x00000000 Bootloader Boot参数 内核 根文件系统