导图社区 ARM体系结构裸板开发、Linux内核驱动开发学习框架笔记
ARM体系结构裸板开发、Linux内核驱动开发学习框架笔记,包括:1. ARM体系结构及裸板开发、2.系统移植初步、3.Linux内核及驱动开发、Socket常用函数介绍。
编辑于2022-11-11 09:27:05 广东ARM体系结构裸板开发、Linux内核驱动开发学习框架笔记
1.ARM体系结构及裸板开发
Lesson1_ARM学习准备工作
课程目标: 熟悉Tiny210 开发板结构 ARM 9 - 2410 , ARM11 - 6410 , CortexA8-Tiny210 , Cortex A15 建立板子和PC COM连接 学会脱机刷系统 重点难点: 考核目标: 课后练习: 把课程中的演练都做一遍
Lesson2_ARM世界概要
课程目标: 对ARM世界芯片做一个了解,以及对ARM体系厂商做一个了解 重点难点: 考核目标: 课后练习:
Lesson3_ARM Cortex-A系列体系结构
Lessson4_ARM硬件基础
课程目标: 了解我们常用硬件接口,并且编程控制 重点难点: 对各个接口实现原理的掌握 考核目标: 各个接口的作用,实现原理及特性 课后练习: 完成每个接口课堂上的demo
Lesson5_ARM硬件接口
课程目标: 了解ARM,特别是CortexA8 启动过程 开发各个接口的裸板程序 ,熟悉各常用总线,协议原理 重点难点: 对片上系统启动的理解 对各个总线原理的掌握 考核目标: 各个接口的作用,实现原理及特性 课后练习: 完成每个接口课堂上的demo
ARM启动顺序
  iROM(也叫BL0)的作用: 初始化系统时钟,设置看门狗,初始化栈和堆 加载BL1 BL1的作用: 初始化RAM , 关闭Cache , 设置栈 加载BL2 BL2的作用: 初始化其它外设 加载OS内核 按照三星《S5PV210_UM_REV1.1》手册上说明的启动流程为:S5PV210上电将从IROM(interal ROM)处执行固化的启动代码,它对时钟等初始化、对启动设备进行判断,并从启动设备中复制BL1(最大16KB)到IRAM(0xd002_0000处,其中0xd002_0010之前的16个字节储存的BL1的校验信息和BL1尺寸)中,并对BL1进行校验,校验OK转入BL1进行执行; 首先解释一下我认为的BL0、BL1、BL2: (1)BL0:是指S5PV210的IROM中固化的启动代码; (2)BL1:是指在IRAM自动从外扩存储器(nand /sd/usb)中拷贝的uboot.bin二进制文件的头最大16K代码; (3)BL2:是指在代码重定向后在内存中执行的的UBOOT的完整代码; (4)三者之间关系是:(Interal ROM固话代码)BL0将BL1(bootloader的前16kB)加载到iRAM;BL1然后在iRAM中运行将BL2(其实整个bootloader)加载到SDRAM(DDR);BL2加载内核;BL就是bootloader的简写;
说点前面的话
关于开发板的线
关于程序链接地址和程序地址
做一个GPIO的例子
要研究裸板开发一定要知道启动顺序,要知道启动顺序一定要了解存储机制
 PV210 Block Diagram  address map
Reset状态

启动过程详细示例
  iROM中执行: Disable看门狗 初始化Cache控制器 初始化栈和堆区域 检查安全key 设置clock 检查OM pin码,加载BL1到iRam 如果是安全模式,进行完整校验 如果校验通过,跳到BL1的iRAM地址(0xD0020010) SRAM启动过程如下所示 从启动设备加载BL2 到 iRAM 如果安全启动,执行完整性校验 如果校验成功 ,跳到iRAM中的 BL2 如果校验失败,停止BL1 加载 OS kernel到DRAM 跳转到DRAM(0x20000000 or 0x40000000) DRAM启动顺序 如果是从SLEEP , DEEP_STOP, DEEP_IDLE唤醒,则恢复上一个状态 跳转到OS Kernel
请你阐述清楚iROM,iRAM(SRAM),DRAM,Nand关系
说说如何用三星芯片手册
GPIO
什么是GPIO?
我们来看看芯片手册
FAQ:GPIO寄存器在哪儿?
从零开始GPIO实验
实验一:从零开始写裸板程序,点亮LED灯
实验要求: 用汇编从零开始写一个led点亮程序 , 并且从零写makefile文件 ,烧写来实验
顺便再次熟悉一下我们的arm汇编以及gcc命令
实验二:用C语言来完成实验一的内容
实验三:通过按键Key来控制LED等的点亮
实验四:优化程序,优化Makefile,控制看门狗和Cache缓存
接口编程的准备工作
ARM接口编程的前言
学习方式
学习目的
学习安排
确定外设资源
了解时钟分布
UART
课程目标: 掌握通信的基本模型和分类 了解UART通信的基本协议 掌握S5PV210下UART收发数据程序的编写过程 重点难点: UART协议的了解 UART控制器的编程 如何读芯片手册进行外围接口的开发(顺序执行) 考核目标: UART接口的作用,实现原理及编程方式 课后练习: 完成每个接口课堂上的demo
通讯的基本模型
UART协议介绍
实验一、
中断控制器
I2C&SPI
PWM/WatchDog/RTC
DMA
Flash ROM
LCD
ADC
Project1_
Subtopic
2.系统移植初步
Lesson1_系统移植概述
Lesson2_Bootloader
S5PV210启动过程

iROM固化的BL0上电初始化
硬件初始化
1、关Watchdog 2、Cache初始化 3、栈初始化 4、设置时钟
根据片选设置引导BL1
外存上的bootloader
BL1第一阶段代码
BL2第二阶段代码
BootLoader概述
BootLoader是在操作系统内核运行之前运行的一段小程序。负责初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为加载调用操作系统内核准备好正确的环境。 嵌入式平台下,BootLoader是严重地依赖于硬件而实现的。
主流的Bootloader
Bootloader 描述 x86 ARM PowerPC LILO Linux磁盘引导程序 是 否 否 GRUB GNU的IO替代程序 是 否 否 U-boot 通用引导程序 是 是 是 RedBoot 基于Cs的引导程序 是 是 是 vivi 专为arm设计 否 是 否
u-boot简介
U-Boot,全称 Universal Boot Loader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。 U-boot的特点: 1、免费开源 2、跨平台移植性好 A、支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS B、支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale 3、可靠性和稳定性较高 4、功能强大 定制灵活、功能全面,适合U-Boot调试、操作系统不同引导要求、产品发布等 5、易于移植 A、丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等 B、较为丰富的开发调试文档与强大的网络技术支持
u-boot引导过程分析
硬件初始化
CPU自身初始化
工作模式设置(SVC)
关MMU
Cache初始化
关中断
时钟初始化
重定位(自搬移)
设置堆栈,初始化堆栈指针
BSS清零
跳转到第二阶段(C入口)
第二阶段代码
U-boot私有数据空间分配及初始化
回调一组初始化函数
外存初始化
nandflash
norflash
OneNand
SD卡
U盘
LCD初始化
VFD初始化
串口初始化(用作控制台的串口除外)
环境变量重定位(外存搬到ram)
从环境变量中读IP及MAC
控制台初始化
打开中断
板子后期初始化
网络初始化(以太网)
进入主循环
引导加载内核
传递内核的参数写入内存偏移0x100的位置
屏蔽所有具有DMA功能的设备,以便内存不会被伪造的网络包或磁盘数据误导
CPU寄存器设置
r1是必须要设置的,r0一般都是0,r2可以不用(目前内核一般也不用)
r0:0
r1:机器码
r2:标记列表在系统内存中的物理地址
MMU必须关闭、指令cache可关闭或打开、数据cache必须关闭
跳转到内核起始地址
U-boot目录结构介绍
老版本
u-boot-2010.03及以前版本 ├── api 存放uboot提供的接口函数 ├── board 根据不同开发板定制的代码,代码也不少 ├── common 通用的代码,涵盖各个方面,已命令行处理为主 ├── cpu 与体系结构相关的代码,uboot的重头戏 ├── disk 磁盘分区相关代码 ├── doc 文档,一堆README开头的文件 ├── drivers 驱动,很丰富,每种类型的设备驱动占用一个子目录 ├── examples 示例程序 ├── fs 文件系统,支持嵌入式开发板常见的文件系统 ├── include 头文件,已通用的头文件为主 ├── lib_【arch】 与体系结构相关的通用库文件(10个文件,支持10种处理器架构) ├── nand_spl NAND存储器相关代码 ├── net 网络相关代码,小型的协议栈 ├── onenand_ipl ├── post 加电自检程序 └── tools 辅助程序,用于编译和检查uboot目标文件
新版本
u-boot-2010.06及以后版本 ├── api 存放uboot提供的接口函数 ├── arch 与体系结构相关的代码,uboot的重头戏 ├── board 根据不同开发板定制的代码,代码也不少 ├── common 通用的代码,涵盖各个方面,已命令行处理为主 ├── disk 磁盘分区相关代码 ├── doc 文档,一堆README开头的文件 ├── drivers 驱动,很丰富,每种类型的设备驱动占用一个子目录 ├── examples 示例程序 ├── fs 文件系统,支持嵌入式开发板常见的文件系统 ├── include 头文件,已通用的头文件为主 ├── lib 通用库文件 ├── nand_spl NAND存储器相关代码 ├── net 网络相关代码,小型的协议栈 ├── onenand_ipl ├── post 加电自检程序 └── tools 辅助程序,用于编译和检查uboot目标文件 ====================================== |--arch\ |--arm\ |--cpu\ 子目录对应一种处理器的不同产品型号或者系列; |--arm720t\ |--arm920t\ |--a320\ |--at91\ |... |--s3c24x0\ |--interrupts.c |--speed.c |--timer.c |--usb.c |--usb_ohci.c |--usb_ohci.h |--Makefile |--cpu.c |--interrupts.c |--start.S |--u-boot.lds |--Makefile |--config.mk |--include\ 子目录是处理器用到的头文件; |--asm\ |--arch-a320\ |... |--arch_s3c24x0\ |--memory.h |--s3c2400.h |--s3c2410.h |--s3c24x0_cpu.h |--s3c24x0.h |... |--proc-armv\(公用) |--atomic.h |--bitops.h |--byteorder.h |--cache.h |--... (等共24个.h公用头文件) |--lib\ 目录对应用到处理器公用的代码; |--board.c |--config.mk |--... (等共16个文件) |--board\ |--ppmc7xx\ |... |--samsung\ |--goni\ |--smdk2400\ |--smdk2410\ |--smdk2410.c |--flash.c |--lowlevel_init.S |--config.mk |--Makefile |--nand_read.c |--nand_read_save.c |... drivers/i2c/s3c24x0_i2c.c /mtd/nand/nand_base.c /mtd/nand/nand_util.c /mtd/nand/s3c2410_nand.c /net/dm9000x.c /video/cfb_console_2.c /video/cfb_console.c /video/Makefile /video/s3c2410_fb.c /video/videomodes.c /video/videomodes.h /configs/fl2440.h /linux/mtd/mtd.h /serial.h common/cmd_nand.c /serial.c board.cfg Makefile
U-boot控制台命令及环境变量
1、查看环境变量 # printenv (可以简写为pri) bootcmd:自启动变量 bootargs:传递给内核的启动参数 ipaddr:本地IP serverip:server IP netmask:子网掩码 bootdelay:启动延时 2、设置环境变量 # setenv bootcmd tftp 20008000 zImage\;go 20008000 3、保存环境变量 # saveenv 4、外存操作(以Nand为例) A、查看帮助信息 # help nand B、擦除 nand erase 偏移地址 长度 # nand erase 100000 300000 C、读写 nand read/write 内存地址 外存偏移地址 长度 #nand read 20008000 100000 300000 #nand write 20008000 100000 300000 5、tftp tftp 内存偏移地址 文件名称 #tftp 20008000 zImage 6、运行内核 go 内存偏移地址 # go 20008000 7、内核启动参数 A、示例1 noinitrd root=/dev/mtdblock2 rw init=/linuxrc console=ttySAC0,115200 rootfstype=yaffs2 noinitrd 不使用ramdisk root指定根文件系统在哪个设备 console指定控制台 rootfstype指定文件系统类型 B、示例2 noinitrd console=ttyS0 115200 root=/dev/nfs nfsroot=192.168.1.3:/home/rootfs ip=192.168.1.2:192.168.1.3:192.168.1.1:255.255.255.0:::eth0:off nfsroot如果root指定的是nfs的话,那么需要指定nfs server共享的目录,本地网络设置 ip=本地IP:serverIP:子网掩码:主机名:域名(可以不写):
Lesson3_BootLoader移植
u-boot移植步骤
选板子
修改Makefile
随着版本的变化修改方式不一样,但是修改的内容都一致 1、指定交叉编工具链 2、指定平台 ========================================= 先约定如下: Target(目标):tiny210 ARCH(架构): arm CPU(芯片):armv7 Board name(板子名称):tiny210 Vendo(生产厂家):samsung SoC(CPU类型): s5pc1xx Options(可选项): ========================================= 以u-boot-xxx为例说明具体步骤: 1、修改顶层目录下的boards.cfg文件 添加如下内容: tiny210 arm armv7 tiny210 samsungs5pc1xx 2、指定交叉编译工具链 ifeq (arm,$(ARCH)) CROSS_COMPILE ?= arm-linux- endif
配置
在顶层目录下执行 # make tiny210_config Makeile $(BFIN_BOARDS:%=%_config) : unconfig @$(MKCONFIG) $(@:_config=) blackfin blackfin $(@:_config=) ---> mkconfig 生成config.mk、config.h
定制或修改板文件
按照boards.cfg指定的内容 准备board/samsung/tiny210 一般是复制参考板目录修改一下文件名及内容 board/samsung/smdkv100 复制一份,改目录名为board/samsung/tiny210 目录下的相应文件一般也最好改成对应的文件名,Makefile对应修改 smdkv100.c--->tiny210.c 修改Makefile COBJS-y := smdkv100.o --->COBJS-y := tiny210.o 添加修改第一阶段代码 一般添加外存读写代码(譬如nandflash添加为nandflash.c) 修改Makefile COBJS-y := nandflash.o 修改初始化代码lowlevel_init.S 修改链接脚本arch\arm\cpu\armv7\u-boot.lds .text : { arch/arm/cpu/armv7/start.o (.text) *(.text) } 修改为: .text : { arch/arm/cpu/armv7/start.o (.text) board/samsung/fsc100/lowlevel_init.o board/samsung/fsc100/nandflash.o *(.text) } 同理板文件目录下添加了什么,板文件目录下的Makefile得改,链接脚本也得添加。
选配或是添加模块
复制include\configs\smdkc100.h ---> include\configs\tiny100.h 这里面除了参数(CPU时钟、内存基地址、环境变量等),还有模块开关。 譬如要nand命令: 添加宏定义 #define CONFIG_CMD_NAND 则相应驱动代码及逻辑代码得有。 譬如网卡,逻辑代码就是选择(宏开关),但是驱动不一定有或是要修改,一定通过板文件或是C入口初始化代码来选。 那么这些宏开关从哪里来,就是相应目录下的Makefile中找,如果是自己添加的驱动或是逻辑代码,也应该在对应Makefile中设置这样的宏开关。
Lesson4_linux内核简述
Linux是一种自由和开放源码的类Unix操作系统,使用C编写,符合POSIX标准,采用GNU通用许可证授权。
linux内核特点
免费开源
获得源码的官网:http://www.kernel.org 目前有两种授权GPL、Dual BSD/GPL
宏内核架构

抢占式调度
2.6以后版本的内核开始支持内核抢占,这样提高了系统的实时性
多任务多用户
主流操作都会提供虚拟机制,虚拟处理器就允许系统同时存在多个任务。linux还支持多用户。
网络功能强大
Linux支持目前主流的网络通讯协议
安全稳定可靠
Linux提供了许多安全机制,对读、写进行权限控制、审计跟踪、核心授权等。Linux也广泛应用于服务器,这对稳定性也有比较高的要求。
支持多平台,易于移植
Linux支持多种硬件平台:arm、x86、PowerPC等处理器。
内核功能划分

进程管理
进程创建、销毁、输入/输出、进程间通讯、调度
内存管理
为每一个进程在可用的有限物理内存上建立一个虚拟地址空间
文件系统
linux继承了unix很重要的一个概念:一切皆是文件。 系统有7类文件(广义的概念): 普通文件、目录、符号文件、管道、字符设备、块设备、套接字
设备控制
操作系统最核心的任务就是有效的管理设备,几乎所有的系统操作都会映射到一个实际设备上。有三类基本框架:字符设备驱动、块设备驱动、网络设备驱动。
网络功能
linux内核提供了很好的网络管理功能
内核源码
开发特点
不需要第三方库支持
内核所有功能均内核代码来实现
使用GNU C
内核开发依赖GNU工具,需要GNU C扩展特性
没有内存保护机制
内核代码的内存访问权限是无法控制的
杜绝浮点数
内核对浮点运算除了性能的问题外,浮点运算支持不够好
栈区固定
内核栈是固定的,随着版本变化会有所变化。
必须关注并发及同步
内核是进程共用的代码,竞态是很容易产生的
注意可移植性
Linux是可移植的系统,大小端、边界对齐、不假定字长、不假定页面长度等都需要注意
目录结构
arch
体系平台相关的代码
block
块设备IO调度相关代码
crypto
常用加密和散列算法代码
Documentation
内核文档
drivers
设备驱动代码
firmware
第三方固件代码
fs
文件系统支持代码
include
通用头文件,和体系统平台相关的头文件在相应arch目录下的相关架构目录下 如arm arch/arm/include arch/arm/xxx/include
init
系统初始化代码,系统引导代码在arch目录相应体系平台目录下 如arm arch/arm/boot
ipc
进程间通讯代码
kernel
系统核心代码(如进程调度),系统核心库代码 体系平台相关的代码如arm arch/arm/kernel
lib
系统核心库代码 体系平台相关的代码如arm arch/arm/lib/目录下。
mm
内存管理代码 体系平台相关的代码如arm arch/arm/mm目录下。
net
网络相关的代码,如协议
samples
内核编程的一些规范示例
scripts
配置编译内核的一些脚本及工具代码
security
主要包含SELinux模块 SELinux(Security-Enhanced Linux) 是美国国家安全局(NSA)对于强制访问控制的实现,是 Linux上最杰出的新安全子系统。NSA是在Linux社区的帮助下开发了一种访问控制体系,在这种访问控制体系的限制下,进程只能访问那些在他的任务中所需要文件。
sound
ALSA,OSS音频设备的驱动核心代码和常用设备驱动。
tools
一些工具代码
usr
实现了用于打包和压缩的cpio等
virt
内核虚拟机
Lesson5_linux初始化简述
各个体系平台的初始化过程是有差异的 这里以为arm为例来说明
启动条件
1、CPU工作于SVC模式 2、屏蔽IRQ和FIQ中断 3、关闭MMU 4、关闭数据cache(指令cache(Instruction cache)可打开,也可关闭的) 5、r0必须是0 6、r1必须是机器码
第一阶段
内核非压缩和非压缩两种,压缩的会先自解压,后面的动作就一样了,一般有下面一些工作要做(随着版本变化,具体实现可能会有差异) 1、确保CPU工作于SVC模式并且IRQ和FIQ中断是关闭的 2、验证processor type 3、验证machine type 4、创建页表、开启MMU 5、跳转到C入口的第二阶段代码
第二阶段
1、执行各种模块的初始化(核心及设备等) 2、挂接根文件系统 3、启动第一个init进程
Lesson6_linux内核移植步骤
选平台
修改Makefile
修改顶层目录下的Makefile ARCH ?= arm CROSS_COMPILE ?= arm-linux- 指定arch/arm目录下的指定板子目录需要交叉编译编译,其他体系平台下的目录则不会。
修改定制板文件
选配模块
文件系统制作
3.Linux内核及驱动开发
Socket常用函数介绍
socket - create an endpoint for communication #include #include int socket(int domain ,int type, int protocol) ; bind - bind a name to a socket #include #include int bind(int sockfd,const struct sockaddr *addr, socklen_t addrlen) ; listen - listen for connections on a socket #include #include int listen(int sockfd,int backlog) ; accept - accept a connection on a socket #include #include int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen) ; connect #include #include int connect(int sockfd,const struct sockaddr *addr, socklen_t addrlen) ; read , write , close send,sendto , sendmsg - send a message to a socket #include #include ssize_t send(int sockfd,const void *buf,size_t len,int flags) ; ssize_t sendto(int sockfd,const void *buf,size_t len,int flags, const struct sockaddr *dest_addr,socklen_t addrlen) ; ssize_t sendmsg(int sockfd,const struct msghdr *msg,int flags) ; recv, recvfrom , recvmsg - receive a message from a socket #include #include ssize_t recv(int sockfd, void *buf, size_t len, int flags) ; ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen) ; ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) ;