ARM是什么:

ARM是Advanced RISC Machines的简写
RISC:精简指令集计算机(Reduced Instruction Set Computer)

嵌入式硬件可以分为三部分:

1、微处理器
	2、外围电路
	3、外部设备

嵌入式的软件系统可分为四个层次:

1、板级支持包(BSP,   Board Support Packet)
	2、实时操作系统(RTOS,   Real Time Operstion System)
	3、应用编程接口(API,   Application Programmable Interface)
	4、应用程序

根据结构和功能特点不同,嵌入式处理器可分为:

1、嵌入式微处理器(MPU,  MicroProcessor Unit)
	2、嵌入式微控制器(MCU,  MicroController  Unit)
	3、嵌入式数字信号处理器(DSP,  Digital Single Processor)
	//4、片上系统SOC(System on Chip)

简述MCU和DSP的区别:

MCU是微控制器,DSP是数字信号处理器
	MCU相当于小型电脑,集成度高,偏重于控制
	DSP是专用的信息处理器,处理速度快,偏重于信号处理

ARM体系结构有哪几种工作状态?又有哪几种运行模式?其中哪些为特权模式?哪些为异常模式?并指出处理器在什么情况下进入相应模式?

工作状态:
		第一种:ARM状态。处理器执行32位的字对齐的ARM指令
		第二种:Thumb状态。处理器执行16位的半字对齐的Thumb指令
	运行模式:
		用户模式、管理模式、系统模式、快速中断模式、外部中断模式、数据访问终止模式、未定义指令终止模式。
		在这7种运行模式中,除了用户模式外,其余6种为特权模式。
		在这6种特权模式中,除了系统模式外,其它5种特权模式为异常模式。
		程序正常执行的时候是在用户模式下,如果有异常发生,处理器会自动切换工作模式。

ARM体系可以用两种方法存储字数据:

大端格式(Big Endian):
		数据的低字节存储在高地址中。
	小端格式(Little Endian):
		数据的低字节存储在低地址中。

ARM处理器寄存器组织:

共有37个寄存器,其中31个通用寄存器,6个状态寄存器

嵌入式Linux系统移植:

嵌入式Linux系统移植主要由四大部分组成:
	一、搭建交叉开发环境
	二、BootLoader的选择和移植
	三、kernel的配置、编译、和移植
	四、根文件系统的制作

Bootloader启动步骤:

stage1(汇编语言实现)
	1、硬件设备初始化
	2、为加载Bootloader的stage2准备RAM空间
	3、复制Bootloader的stage2到RAM空间中
	4、设置好堆栈,堆栈指针的设置是为执行C语言代码做好准备
	5、跳转到stage2的C语言入口点
stage2(C语言实现)
	1、初始化本阶段要使用到的硬件设备
	2、检测系统内存映射
	3、kernel映像和根文件映像从Flash上读到RAM空间中
	4、为内核设置启动参数
	5、调用内核

ARM常用的Bootloader程序有哪些:

U-boot、Blob、ARMBoot、RedBoot、vivi。

大多数Bootloader程序都包括两种不同的操作模式:

1、启动加载模式(Bootloading)
	2、下载模式(Downloading)

MakeFile文件的编写(使用了预定义变量):

书本上:
		CC=gcc
		TARGET=All   
		OBJIECTS=m.o visit.o listen.o watch.o study.o play.o
		$(TARGET):$(OBJIECTS)
			$(CC) $^ -o m  ($^规则中所以依赖的列表,以空格为分隔符)
		*.o:*.c
			$(CC) -c $< -o $@    ($<规则中的第一个依赖文件名   $@规则中的目标所对应的文件名)
		clean:
			rm *.o

	简单的hello.c的Makefile文件:
		all:hello.o
			gcc helllo.o -o hello
		hello.o:hello.c
			gcc -c hello.c -o hello.o
		clean:
			rm *.o

make和Makefile之间的关系:

make是一种命令,是根据Makefile文件的规则决定如何编译和连接程序或其他的动作。
	makefile文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。
	简单的说 makefile 就像批处理的脚本文件,里边写好了一些命令的集合,当运行 make 命令时 ,便会按着 makefile 提供的命令及顺序来完成编译。

	#make
	#make -f  makefile文件名
	-f 选项就是指定将哪个文件作为makefile文件。如果没有使用-f选项,标准版本make命令将首先在当前目录下找名字为makefile的文件,找不到会继续查找Makefile的文件。

BusyBox工具的功能:

BusyBox工具用来精简基本用户命令和程序,它将数以百计的常用Unix/Linux命令集成到一个可执行文件中。

Sqlite数据库运行环境:

1、库文件安装的目录:/usr/local/lib
	2、可执行文件安装的目录:/usr/local/bin

	3、头文件安装的目录:/usr/local/include
	说明:
		系统自动在哪个目录搜索库是由/etc/ld.so.conf文件决定的。
		因此需修改/etc/ld.so.conf文件,在文件中添加上一行/usr/local/lib。
		然后让文件内容生效,则要运行命令#/sbin/ldconfig。

Sqlite数据库常用操作:

//创建数据库文件stu.db
	#sqlite3 stu.db
	//创建数据表student
	sqlite>create table student(ID integer primary key, Name varchar(20),Age integer,Sex varchar(10))
	//添加记录
	sqlite>insert into student values(1001,'zhangsan',20,'female')
	//查询操作
	sqlite>select * from student
	//查看数据库stu.db已经拥有的数据表
	sqlite>.tables
	//退出
	sqlite>.quit

Sqlite数据库编程:

#include<stdio.h>
	#include<sqlite3.h>

	int main()
	{
		sqlite3 *db=NULL;
		int rc;
		char *Errormsg,*sql;
		int nrow;
		int ncol;
		char **Result;
		int i=0;
		
		rc=sqlite3_open("stu.db",&db);
		if(rc){
			fprintf(stderr,"can't open database: %s\n", sqlite3_errmsg(db));
			sqlite3_close(db);
			return 1;
		}else
			printf("open database successly!!!\n");

		//创建学生student表
		sql="create table student(ID integer primary key,Name varchar(20),Age integer,Sex varchar(10))";
		sqlite3_exec(db,sql,0,0,&Errormsg);

		//插入两条记录
		sql="insert into student values(10001,'zhangsan',20,'female')";
		sqlite3_exec(db,sql,0,0,&Errormsg);
		sql="insert into student values(10002,'lisi',19,'male')";
		sqlite3_exec(db,sql,0,0,&Errormsg);

		//获得数据表
		sql="select * from student";
		sqlite3_get_table(db,sql,&Result,&nrow,&ncol,&Errormsg);

		//输出行数与列数
		printf("row=%d column=%d\n",nrow,ncol);
		//输出结果,打印显示
		printf("the result is:\n");
		printf("-----------------------------------------\n");
		for(i=0;i<(nrow+1)*ncol;i++){
			printf("%20s",Result[i]);
			if((i+1)%4 == 0) printf("\n");
		}
				  
		sqlite3_free(Errormsg);
		sqlite3_free_table(Result);
		sqlite3_close(db);
		return 0;
	}

驱动程序连接到内核的两种方法:

静态连接:将驱动程序源码保存在内核源码指定的位置,作为内核源码的一部分,然后重新编译内核,这时驱动程序被编译到内核映像文件之中。
	动态连接:将驱动程序作为一个模块(module)单独编译,在需要它的时候再动态加载到内核,如果不需要它,又可以将它从内核中删除。

设备驱动程序的功能:

1、对设备初始化和释放
	2、数据传送
	3、检测和处理设备出现的错误

设备驱动程序的组成

1、自动配置和初始化子程序
	2、服务于I/O请求的子程序,又称为驱动程序的上半部分
	3、中断服务子程序,又称为驱动程序的下半部分

设备文件:

设备文件有时也称为设备节点,一般存在/dev目录下。正常情况下,/dev目录下的每一个设备文件对应一个设备(包括虚拟设备),设备文件的命名一般为“设备名+数字或字母”。

设备文件创建方式:

自动创建:指驱动程序加载时,驱动程序内部调用了自动创建设备文件的函数,从而由内核完成设备文件的创建工作。
	手动创建:指用户通过mknod命令来创建设备文件。

简述设备文件、驱动程序、主设备号和次设备号之间的关系:

驱动程序加载到内核后会有一个主设备号。在linux内核中,主设备号标识设备对应的驱动程序,告诉linux内核使用哪一个驱动程序为该设备(也就是/dev下的设备文件)服务,而次设备号则用来标识具体且唯一的某个设备。

简述字符设备驱动程序提供的常用入口点及各自的功能:

open入口点:对将要进行的I/O操作做好必要的准备工作,如清除缓冲区。
	read入口点:当从设备上读取数据时,需要调用read子程序。
	write入口点:当要向设备上读数据时,需要调用write子程序。
	close入口点:当设备操作结束时,需要调用close子程序关闭设备。
	ioctl入口点:主要用于对设备进行读之外的其它操作,比如配置设备,进入或退出某种操作模式。

设备驱动模块化编程三个过程:

加载过程:当执行insmod命令加载驱动程序时,首先调用驱动程序中的入口函数module_init,该函数完成设备驱动的初始化工作。
		向内核注册该设备,字符设备调用register_chrdev函数完成注册,块设备调用register_blkdev函数完成注册。
		注册成功后,该设备将获得系统分配的主设备号、自定义的次设备号,并建立起与文件系统的关联。
	系统调用过程:当驱动程序加载后,就一直等待应用进程来调用。应用程序可以利用设备文件对其进行操作,如调用open、read、write、ioctl和close等函数。
	卸载过程:当执行rmmod命令卸载驱动程序时,则会调用驱动程序中的module_exit函数,该函数完成后回收相应的资源。
		字符设备调用unregister_chrdev函数完成注销,块设备调用unregister_blkdev函数完成注销。

设备驱动程序操作命令的使用方法:

1、lsmod命令  用于显示当前已加载的模块
		#lnsmod
	2、insmod命令 用于将驱动模块加载到操作系统内核
		#insmod file_name
		//如将数码管驱动程序(tube.o)加载到内核
		#insmod tube.o
	3、驱动程序加载时,如果系统支持设备文件系统,则系统会自动创建设备文件,否则需要手动创建。
		#mknod  dev/设备文件名 flag 主设备号 次设备号
		//如手动创建设备文件
		#mknod dev/tube c 240 0
		说明:flag标志。b 标志表示这个特殊文件是面向块的设备(磁盘、软盘或磁带),c 标志表示这个特殊文件是面向字符的设备(其他设备)。
	
	4、rmmod命令 用于将驱动模块从内核中删除
		#rmmod module_name
		//如将rube模块删除
		#rmmod tube

常用Linux命令的使用:

在实验箱上挂载SD卡:
		#mkdir sdcard
		#mount  /dev/mmcblkp1 sdcard
	将U盘挂接到Linux系统/mnt/usb目录上,可使用如下命令
		#mount   /dev/sda1  /mnt/usb
	将当前目录下的ab.png更名为xyz.png
		#mv ab.png xyz.png
	将文本文件33.txt和44.txt两文件的内容合并到aa.txt文件中
		#cat 33.txt  44.txt  > aa.txt
	在/root目录,查找第一个字符为h的文件。
		#find  /root -name h*.* 
	从普通用户切换到root用户,及从root用户切换回普通用户
		$su root
		#exit

常用汇编指令:

ARM指令代码格式:
	<opcode>{<cond>}{S}<Rd>,<Rn>{<OP2>}
	格式中<>的内容必不可少,{}中的内容可省略
	<opcode>是操作码,如ADD表示算数加法
	{<cond>}表示指令执行的条件域,如EQ、NE等。
	{S}决定指令的执行结果是否影响CPSR的值,使用该后缀则指令执行的结果影响CPSR的值,否则不影响。
	<Rd>表示目的寄存器
	<Rn>表示第一个操作数,为寄存器
	<op2>表示第二个操作数,可以是立即数、寄存器或寄存器移位操作数。

数据处理指令:
	1、ADD	加法
	指令格式:	ADD{cond}{S} Rd,Rn,op2 (op2可以是寄存器、立即数或被移位的寄存器)
	功能:	Rd=Rn +op2   		将Rn的值与操作数op2相加,结果存放到目的寄存器Rd中。
	示例:	ADD R0,R1,R2		R0=R1+R2
		ADD R0,R1,#256		R0=R1+256
		ADD R0,R2,R3,LSL#1	R0=R2+(R3<<1)

	2、SUB 	减法
	指令格式:SUB{cond}{S} Rd,Rn,op2 (op2同上)
	功能:	Rd=Rn -op2 		将Rn的值与操作数op2相减,结果存放到目的寄存器Rd中。
	示例:	SUB R0,R1,R2		R0=R1-R2
		SUB R0,R1,#256		R0=R1-256
		SUB R0,R2,R3,LSL#1		R0=R2-(R3<<1)	

	3、BIC 	位清除
	指令格式:	BIC{cond}{S} Rd,Rn,op2 (op2同上)
	功能:	Rd=Rn AND (!op2)		将Rn 的值与操作数op2 的反码按位逻辑”与”,结果存放到目的寄存器Rd 中。
	示例: 	BIC R0,R0,#0x0F  		将R0最低4位清零,其余位不变。
		BIC R9,R8,#0xFF00		将R8中8~15位清零, 其余位不变。
	4、EOR	逻辑异或
	指令格式:EOR{cond}{S} Rd,Rn,op2 (op2同上)
	功能:	Rd=Rn EOR op2		将Rn的值与操作数op2进行异或,结果存放到目的寄存器Rd 中。
	
	5、MOV	移动
	指令格式:MOV{cond}{S} Rn,op1     (op1可以是寄存器、立即数或被移位的寄存器)
	功能:	Rd=op1			数据传送
	示例:	MOV  R2,R0		R0的值送给R2
		MOV  R0, R0, LSL#3		R0 = (R0<<3)


	6、CMP	比较
	指令格式:	CMP{cond} Rn,op1	(op1为寄存器或立即数)
	功能:	Rn-op1			将Rn的值与op1比较
	示例:	CMP  R1, R2		比较R1和R2的值
		CMP  R1,#1000		R1的值和1000比较

	
加载/储存指令:
	1、LDR	加载字数据指令
	指令格式:LDR{cond}Rd,addr
	功能:	Rd<--[addr]
	示例:	LDR R0,[R1]		将存储器地址为R1的字节数据读入寄存器R0中
		LDR  R1,[R0,R2]	   	将R0+R2地址的数据读出,保存到R1中(R0的值不变)

		LDR  R1,[R0,R2,LSL #2] 	将R0+R2×4地址处的数据读出,保存到R1中(R0、R2的值不变)


	2、STR	存储字数据指令
	指令格式:	STR{cond}Rd,addr
	功能:	[addr]<--Rd
	示例:	STR R0,[R1]		将寄存器R0中的字节写入地址为R1的存储器中

	
	3、LDM	多寄存器加载指令
	指令格式:	LDM{cond}{mode}Rn(!),reglist
	功能:	reglist<--[Rn……]
	4、STM	多寄存器存储指令
	指令格式:	STMM{cond}{mode}Rn(!),reglist
	功能:	[Rn……]<--reglist

	说明:
	LDM/STM 的主要用途有现场保护、数据复制和参数传递等。
	其模式有 8 种,其中前面 4 种用于数据块的传输,后面4种是堆栈操作,如下所示。 
		(1)IA:每次传送后地址加 4。 (2)IB:每次传送前地址加 4。 (3)DA:每次传送后地址减 4。 (4)DB:每次传送前地址减 4。 
		(5)FD:满递减堆栈。 (6)ED:空递增堆栈。 (7)FA:满递增堆栈。 (8)EA:空递增堆栈


协处理器指令:
	coproc:协处理器名,标准名为Pn,n为1~15
	opcode1:协处理器的特定操作码
	CRd:作为目标寄存的协处理器寄存器
	CRn:  存放第1个操作数的协处理器寄存器
	CRm: 存放第2个操作数的协处理器寄存器
	opcode2:可选的协处理器的特定操作码

	1、ARM寄存器到协处理器寄存器的数据传送(MCR)
	格式:MCR{cond} coproc,opcode1,CRd,CRn,CRm(,opcode2)
	举例:MCR P5,2,R7,C1,C2
	2、协处理器寄存器到ARM寄存器的数据传送(MRC)
	格式:MRC{cond} coproc,opcode1,CRd,CRn,CRm(,opcode2)
	举例:MRC P5,2,R7,C1,C2

课本上ADC驱动程序的框架:

S3c2410_adc_open()  {…..}
	S3c2410_adc_read()  {…..}
	S3c2410_adc_write()  {…..}
	S3c2410_adc_release()  {…..}

	static struct file_operations s3c2410_fops = {	
	open:	s3c2410_adc_open,
	read:	s3c2410_adc_read,
	write:    	s3c2410_adc_write,
	release:	 s3c2410_adc_release,
	};


	S3c2410_adc_init()  {…..}
	module_init(S3c2410_adc_init);

	S3c2410_adc_exit()  {…..}

	module_exit(S3c2410_adc_exit);

	
	解读:
		安装驱动程序时,即执行insmod adc.o时,会运行该函数S3c2410_adc_init()  {…..}
		可以用lsmod来查看执行insmod adc.o后,生成一个名为adc的模块驱动程序
		卸载驱动程序时,即执行rmmod adc时,会运行该函数S3c2410_adc_exit()  {…..}

LINUX设备驱动模型之基于设备数的platform(平台)总线:

Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线
	platform总线管理:
		两个结构体platform_device和platform_driver
	1、把硬件设备添加到设备数
		xxx_platfrom@xxxxx{
    				compatible = "company_name, device_name";
    				reg = <0x11000c40 1>,<0x11000c20 1>;
		};
	2、更改驱动程序中的加载入口
		不需要再使用字符设备的3大模块, 改为:
			module_platform_driver(xxx_platform_driver);   //入口声明
			MODULE_LICENSE("Dual BSD/GPL");  //模块许可声明

			从入口声明的xxx_platform_driver结构体找到驱动的加载/卸载
			struct     platform_driver   xxx_platform_driver{
					.driver = {
  						.name = "driver_name",
    						.owner = THIS_MODULE,
   						 .of_match_table = of_match_ptr(xxx_dt_of_matches),
					},
				 .probe = xxx_init,
					.remove = xxx_exit,
			};

		说明:
			(1)结构体提供的.probe, .remove对应的函数,就是模块加载/卸载的入口;
			(2).of_match_table就是内核在设备驱动加载时查找的设备驱动表(这里表名字xxx_dt_of_matches)

			
				static const struct of_device_id xxx_dt_of_matches[] = {
						{ .compatible = "company_name,device_name"},     
				};
	3、驱动程序的修改