ARM 嵌入式系统 第七章 嵌入式Linux
嵌入式Linux 嵌入式Linux概述 ARM Linux在PXA270实验系统上的移植 ARM Linux的设备驱动
嵌入式Linux概述 Linux介绍 嵌入式Linux系统交叉开发环境 开发工具GNU的介绍
Linux介绍 Linux是类UNIX操作系统。最初是由Linus Torvalds于1991年在基于Intel80386处理器的IBM兼容机上开发的操作系统。出现在二十世纪九十年代,在短短的十几年的时间里发展成为功能强大,设计完善的操作系统。源程序可以在http://www.kernel.org/下载。 Linux有着异常丰富的驱动程序资源,支持各种主流的硬件设备与技术。嵌入式Linux能够固化在容量只有几KB或者几MB的存储芯片或者微控制器中,Linux包含了现代的UNIX操作系统的所有功能特性,这些功能包括多任务、虚拟内存、虚拟文件系统、SVR4进程间通信、对称多处理器(SMP)、多用户支持等功能。
Linux介绍 Linux具有以下特性: 单一内核 支持多处理器 良好的开放性 设备独立性 支持多线程 抢占式内核功能 文件系统
嵌入式Linux操作系统的应用领域: 手机、PDA 数字相机、数字电视机、数码相机 VCD/DVD音响设备、可视电话 家庭网络设备 洗衣机、电冰箱 智能玩具
Linux介绍 广泛应用的原因: 公开源代码 没有专利费 外部设备驱动丰富 网络协议及中间件非常丰富 非常稳定
嵌入式Linux 嵌入式Linux(Embedded Linux)是指对标准Linux经过小型化裁剪处理之后,能够固化在容量只有几KB或者几MB字节的存储器芯片或者单片机中,是适合于特定嵌入式应用场合的专用Linux操作系统。 在目前已经开发成功的嵌入式系统中,大约有一半使用的是Linux。这与它自身的优良特性是分不开的。嵌入式Linux同Linux一样,具有低成本、多种硬件平台支持、优异的性能和良好的网络支持等优点。
嵌入式Linux 嵌入式Linux还在Linux基础上做了部分改进,主要的改动有: 改善的内核结构 提高的系统实时性
嵌入式Linux系统交叉开发环境 交叉开发模型主要思想是,首先在宿主机(Host)上安装开发工具,编辑、编译目标板(Target)的Linux引导程序、内核和文件系统,然后下载到目标板上运行。通常这种在宿主机环境下开发,在目标机上运行的开发模式叫做交叉开发。交叉开发模型见图所示。
开发工具GNU的介绍 GNU(GNU’s Not Unix)项目是自由软件基金会(Free Software Foundation)的董事长Richard M. Stallman于1984年发起,意在软件开发团体中发起支持开发自由软件的运动。 GNU软件包括: C编译器gcc C++编译器g++ GNU的汇编器as GNU的链接器ld 二进制转换工具(objcopy、objdump) 调试工具(gdb、gdbserver、kgdb)和基于不同硬件平台的开发库。
开发工具GNU的介绍 1.GNU Binutils工具 工具集GNU Binutils主要是一套用来构造和使用二进制代码所需的工具集。建立嵌入式交叉编译环境,GNU Binutils工具包是不可缺少的,没有Binutils,GNU的C编译器gcc将无法正常工作。 Binutils的官方下载地址是: ftp://ftp.gnu.org/gnu/binutils/, 在这可以下载到不同版本的Binutils工具包。目前比较新的版本是Binutils-2.17。GNU Binutils工具集主要有以下一系列的部件。
开发工具GNU的介绍 ld GNU的链接器 汇编器产生的目标代码生成可执行文件 as GNU的汇编器 汇编语言编写的源程序换成二进制形式的目标代码 add2line 将地址转换成文件名或行号对 C++filt 过滤掉C++符号,防止重载冲突 gprof 显示程序调用段的各种数据 nlmconv 将目标代码转换成NL nm 从目标代码中列出所有变量 objdump 显示目标文件信息 strings 从文件中列出可打印的字符串 ranlib 对归档文件生成索引 windres Windows源程序的编译器
开发工具GNU的介绍 2.编译器gcc gcc是GNU推出的功能强大,性能优越的多平台编译器,是Linux中最重要的软件开发工具。是GNU的代表作品之一。编译器被成功地移植到不同的处理平台上,标准的台式Linux上的gcc是针对Intel CPU的,而ARM系列开发软件使用的是针对ARM系列处理器的gcc编译器arm-elf-gcc、arm-elf-as及相应的GNU Binutils工具集。 使用gcc编译器编译C语言程序时,通常会经过四个处理阶段,即预处理阶段、编译阶段、汇编阶段和链接阶段。 gcc是通过文件的后缀来区别文件的类别,下面的表中给出gcc的部分约定规则。在使用gcc编译器时,需要给出一系列调用参数和文件名,当没有给出时,gcc将使用缺省参数,gcc基本的用法是: gcc [options][filename]
开发工具GNU的介绍 其中,options就是参数选项,filename是相关的文件名称。常用的选项有: –c :只编译生成目标文件,不链接成可执行文件。 –DMACRO=DEFN: 以字符串“DEFN”定义MACRO宏。 –E: 只对程序进行预处理。 –g: 生成调试信息,GNU调试器可利用该信息。 –l library: 用来指定所使用的库文件。 –l directory: 为include文件的搜索指定目录。 –o filename: 生成指定的文件名的可执行文件。
开发工具GNU的介绍
开发工具GNU的介绍 3.调试器gdb gdb是Gnu DeBugger的缩写,是GNU C的用来调试C和C++程序的调试工具。开发者在使用它时,可以了解程序在运行时的详细情况,如程序的内部结构和内存等信息。gdb能够通过完成以下几个任务来帮助你查找程序中的错误。 启动程序,设置影响程序运行的调试条件。 能使程序在特定条件下停止。 在程序停止时,检查程序的运行情况。 调整程序,改正错误后继续调试。
gdb调试命令的使用方法 以下程序是汉诺塔的源程序: #include "stdio.h" main() { void hanoi(int,char,char,char); int m; printf("input the number of disks:"); scanf("%d",&m); printf("The step to moving %d disks:\n",m); hanoi(m,'A','B','C'); } void hanoi(int n,char a,char b,char c) { void move(char,char); if(n==1) move(a,c); else { hanoi(n-1,a,c,b); move(a,c); hanoi(n-1,b,a,c); } } void move(char x,char y) { printf("%c-->%c\n",x,y); }
gdb调试命令的使用方法 为了方便调试可执行程序,可以用下面的语句来编译程序。 开始调试: 1.查看源代码,使用list命令 #gcc –g –o test test.c 开始调试: #gdb –Qtest (gdb) 1.查看源代码,使用list命令 (gdb) list 1 #include<stdio.h> 2 main() 3 { 4 void Hanoi(int, char, char, char); 5 int m; 6 scanf("%d",&m); 7 printf("The step to move %d disks:\n",m); 8 hanoi(m,'A','B','C'); 9 } 10 void hanoi(int n,char a,char b,char c)
gdb调试命令的使用方法 2.运行源程序,使用run命令。 (gdb)run Starting program /home/ding/test.exe Input the number of disks;3 The step to move 3 disks: A-->C A-->B C-->B B-->A B-->C Program exited normally (gdb) 如上所述,使用run命令会执行一个可执行程序。
gdb调试命令的使用方法 设置断点 使用break N命令来设置断点,N表示在源代码的第N行处设置断点,如果想看程序中断点数量与位置,可以使用info break命令来查看。 清除断点 gdb用clear指令来清除断点。它的使用格式如下; (gdb) clear n 查看变量的值 gdb用print指令查看变量的值。执行时键入print或p。如果想看变量的类型时,用whatis命令。 单步执行 gdb用step指令进行单步执行方式。此指令可以跟踪到函数内部,执行时键入step或s。另外一个指令是next指令,只用于单步执行,不进入到函数内部。 退出程序调试 如果程序执行完了,则直接退出。如果程序在执行中,则提示程序在执行中,是否要退出的提示,确认后即可结束调试。
ARM Linux在PXA270实验系统上的移植
ARM Linux的开发环境的建立 1. Toolchain简介 ARM交叉编译环境不同于X86系列桌面的编译环境。PXA270芯片同样是基于ARM体系结构的,所以在基于PXA270的嵌入式的开发过程中必须使用ARM的交叉编译环境。 Toolchain具体包括如下: GNU gcc compilers for C, C++ GNU binutil GNU C Library GNU C header
ARM Linux的开发环境的建立 2.Toolchain的安装与配置 binutils-2.15.tar.gz gcc-3.3.2.tar.gz glibc-2.3.2.tar.gz glibc-linuxthreads-2.3.2.tar.gz
ARM Linux的开发环境的建立 搭建开发环境所需的全部资源文件已经全部在光盘中提供,在开发主机上以root用户登录,放入光盘之后使用mount命令将其挂载,这样主机就可以将光盘当作文件来读取。 当光盘放入光驱之后,linux系统可以自动的挂载,可以跳过这个步骤: [root@localhost root]# mount /dev/cdrom /mnt/cdrom [root@localhost root]# cd /mnt/cdrom
ARM Linux的开发环境的建立 检查CD-ROM正确挂载后,需要在开发主机上创建目录,并将光盘内容拷贝到目录中。在根目录创建了一个名为PXA270_Linux的目录,将光盘中的内容拷贝到了该目录中。 [root@localhost root]# mkdir /PXA270_Linux [root@localhost root]# cd /PXA270_Linux [root@localhost PXA270_Linux]# cp /mnt/cdrom/ PXA270_Linux/* -a ./
ARM Linux的开发环境的建立 移动到bin目录下使用ls命令查看,可以看到这些编译工具。
ARM Linux的开发环境的建立 为了之后在任何目录下面都能够使用Toolchain,必须要对路径进行设置。 打开/root/.bash_profile文件来设置路径,使用vi编译器来改.bash_profile文件。 [root@localhost root]# vi ~/.bash_profile 用vi编辑器打开/root/.bash_profile文件并添加下述的路径: PATH=$PATH:/opt/xscalev1/bin
ARM Linux的开发环境的建立 现在在任何的目录下都能打开/opt/xscalev1/bin。 保存并退出该文件,使用source命令来使路径生效。此时,使用arm-linux-命令时,Toolchain 中的工具会被使用。
ARM Linux的交叉编译 #include "stdio.h" main() { 使用vi编辑器创建一个hello.c文件。编写一个简单的程序来打印出一行简单的信息:Hello World。程序如下所示: #include "stdio.h" main() { printf("Hello World\n "); return 0; } 保存并退出该文件。使用如下的命令来编译该文件。 [root@localhost root]# gcc –o hello hello.c [root@localhost root]# arm-linux-gcc –o hello-arm hello.c [root@localhost root]# file hello [root@localhost root]# file hello-arm
ARM Linux的交叉编译 下载该二进制(hello-arm)文件到EELIOD并执行将会输出“Hello World.”。若要下载到目标板,将会使用到minicom 中的串口下载功能。首先需要配置minicom。配置完成之后,可以通过minicom 来对目标板进行操作。 下面几幅图minicom的配置过程以及程序下载到目标板的方法:
Minicom的使用
Minicom的使用
Minicom的使用
Minicom的使用
Minicom的使用
ARM Linux内核的配置与编译 arch 包括所有与体系结构相关的内核代码 include 包括编译内核所需要的头文件 init 包含内核的初始化代码,但不是系统的引导代码,其中所包含main.c和version.c文件是研究Linux 内核的起点 mm 该目录包含所有独立于CPU体系结构的内存管理代码,如页式存储管理内存的分配和释放等。与ARM体系结构相关的代码在arch/arm/mm中 kernel 包括主要的内核代码,此目录下的文件实现大多数Linux的内核函数,其中最重要的文件是sched.c。
ARM Linux内核的配置与编译 2.内核配置系统的基本结构 Makefile 分布在Linux内核源码中的Makefile,定义Linux 内核的编译规则;顶层Makefile是整个内核配置、编译的总体控制文件。 config.in 给用户提供配置选择的功能;.config:内核配置文件,包括由用户选择的配置选项,用来存放内核配置后的结果。 配置工具 包括对配置脚本中使用的配置命令进行解释的配置命令解释器和配置用户界面(基于字符界面:make config;基于Ncurses图形界面:make menuconfig;基于XWindows图形界面:make xconfig)。 rules.make 规则文件,被所有的Makefile 使用。
ARM Linux内核的配置与编译 3.编译内核的常用命令 Make config:内核配置。 make dep:寻找依存关系。。 make clean:清除以前所产生的所有的目标文件,模块文件,核心以及一些临时文件等,不产生任何文件。 make rmproper:删除所有以前在生成内核过程中所产生的所有文件,及除了做make clean外,还要删除.config,.depend等文件,把核心源码恢复到最原始的状态。 make zImage:在make的基础上产生压缩的核心映像文件./arch/$(ARCH)/boot/zImage以及./arch/$(ARCH)/boot/compressed目录下产生一些临时文件。 make bzImage:在make的基础上产生压缩比例更大的的核心映像文件./arch/$(ARCH)/boot/bzImage及./arch/$(ARCH)/boot/compressed目录下产生一些临时文件。
ARM Linux内核的配置与编译 4.内核编译过程 make mrproper 删除所有以前在生成内核过程中所产生的所有文件 make menuconfig 内核配置 make dep 寻找依存关系 make zImage 产生压缩的核心映像文件内核编译完毕之后,生成zImage内核映象文件保存在源代码的arch/arm/boot/目录下。
ARM Linux内核的配置与编译 5.内核配置项介绍 首先将压缩的linux内核源代码文件linux-2.4.21- PXA270.tar.gz解压,进入linux内核源代码所在的目录, 并在终端输入make menuconfig, 系统弹出基于Ncurses内核配置图形界面,便可进行内核选项的配置。 [root@localhost root]# tar xvfz linux-2.4.21-PXA270.tar.gz [root@localhost root]# cd PXA270_Linux/kernel/ [root@localhost kernel]# make xsbase270_config [root@localhost kernel]# make oldconfig [root@localhost kernel]# make menuconfig
ARM Linux内核的配置与编译 6.下载Linux内核镜像 如果bootp和tftp命令能够正常工作,可以使用如下命令来下载内核,具体步骤如下: 设置需要下载的镜像名:在菜单模式下,选择“a”,按提示输入内核镜像和文件系统镜像名,用户必须保证在/tftpboot目录下存在同名的镜像文件。 下载内核镜像:在菜单模式下,选择“3”,此时,内核镜像通过以太网下载到开发板的sdram上;若传输超时或失败,请重新执行。 烧写到flash:在菜单模式,选择“4”,将刚下载的内核镜像烧到flash上。 以上步骤正常结束后,内核镜像已烧入flash中。
嵌入式Linux文件系统的建立与设置 1 Linux文件系统的类型 EXT文件系统 NFS文件系统 JFFS2文件系统 2 文件系统的制作 Busybox介绍 Busybox的编译 配制文件系统
ARM Linux的设备驱动 Linux的设备管理 设备驱动程序结构 GPIO驱动程序设计 基于轮循的UART驱动程序设计
Linux的设备管理 嵌入式Linux设备驱动程序的设计大致包括以下步骤: 向系统申请也可以动态获得主、次设备号。 实现设备初始化和卸载模块。 设计对设备文件操作。如定义file_operations结构。 设计对设备文件操作调用。如read、write等操作。 实现中断服务函数,用request_irq向内核注册。 将驱动程序编译到内核或编译成模块,用ismod命令加载。 生成设备节点文件。
Linux的设备管理 驱动程序的加载方法 在设计完主要数据结构和函数接口后就要把设备驱动加入到内核中。内核模块是Linux 内核的重要组成要素,内核模块能在Linux 系统启动之后能够动态进行装载和卸载, 因此不需对内核进行重新编译或重启系统就可将内核的一部分替换掉。 GPIO驱动程序的设计主要包括以下四个函数的设计: 加载本驱动时执行init_module函数 卸载驱动时执行cleanup_module函数 打开驱动程序文件时执行gpio_open函数 关闭驱动程序文件时执行gpio_release函数
基于轮循的UART驱动程序设计 基于轮循的UART驱动程序,主要函数的功能如下: init_module() 初始化串口模块,加载串口设备驱动; uart_open() 打开一个串口设备节点,当一个设备节点被打开时,计数器MOD_INC_USE_COUNT加一; uart_release() 关闭一个设备节点,当一个设备节点被关闭时,MOD_DEC_USE_COUNT减一; uart_write() 调用copy_from_user()函数将用户空间的数据拷贝到内核空间,然后通过BTuart发送出去,在该函数中调用了SerialOutputByte()函数; uart_read() 首先读BTRBR寄存器,将接收到的数据读到内核空间,然后通过copy_to_user()函数将接收数据拷贝到用户空间; cleanup_module() 将BTuart设备驱动卸载;
基于中断UART驱动程序设计 基于中断UART驱动程序设计,主要函数的功能如下: init_module() 初始化串口模块,加载串口设备驱动; uart_open() 打开一个串口设备节点,当一个设备节点被打开时,计数器MOD_INC_USE_COUNT加一; uart_release() 关闭一个设备节点,当一个设备节点被关闭时,MOD_DEC_USE_COUNT减一; uart_write() 首先调用copy_from_user()函数将用户空间的数据拷贝到内核空间,然后通过BTuart发送出去,在该函数中调用了SerialOutputByte()函数; uart_read() interruptible_sleep_on()使接收进程进入睡眠状态,等待中断唤醒睡眠状态; uart_init(void) 配置串口的波特率、数据位、停止位、奇偶校验和开启串口接收中断; SerialOutputByte(const char c) 串口发送,将字符C通过串口发送出去;
ARM Linux下应用程序设计 UART应用程序设计 基于SOCKET的网络应用程序设计 USB摄像头接口应用程序设计 Framebuffer图片显示应用程序设计
UART应用程序设计 在 Linux操作系统中对底层终端的处理是一个非常复杂的过程,需要处理许多不同类型的设备。Linux系统处理终端的方法是通过串行接口连接的控制台与系统通信并运行程序。 1.终端控制函数介绍 在对底层终端操作中有一个用于查询和操作终端的标准接口结构体 termios,该结构体对终端的输入、输出、硬件特性、控制协议等方面进行了定义,具体定义如下: c_iflag :用来控制输入处理选项; c_oflag : 控制输出数据的处理; c_cflag :设置决定终端硬件特性的控制标志; c_lflag :存放本地模式标志,用来操纵终端特性; c_line :表示控制协议; c_cc :包含特殊字符序列的值以及它们所代表的操作。
UART应用程序设计 1) 终端属性控制函数 对终端的操作主要通过属性设置函数tcsetattr()和属性获取函数tcgetattr()来实现。tcsetattr()和 tcgetattr()的调用形式如下: int tcsetattr(int fd,int action ,struct termios *tp) 函数tcsetattr()使用由tp引用的termios数据结构来设置与文件描述符fd相关联的终端参数 int tcgetattr(int fd , struct termios *tp) 查询和文件描述符相关联的终端参数,并将参数存储到由tp所引用的termios数据结构体中。
UART应用程序设计 2) 终端速度控制函数 终端速度控制函数用来设置终端设备的输入、输出速度,速度以波特率来定义。这些函数都是成对出现,其中的两个用来获取和设置输入的速度,另两个用来获取和设置输出线路的速度,它们定义形式如下: int cfgetispeed (struct termios *tp) int cfsetispeed (struct termios *tp , speed_t speed) int cfgetospeed (struct termios *tp) int cfsetospeed (struct termios *tp , speed_t speed)
UART应用程序设计 2 串口操作函数步骤 1)打开串口函数,返回操作标志 2) 关闭串口子程序,返回操作标志 3) 向串口写数据,返回写入串口的总长度 4) 设置串口参数,主要设置数据位、停止位、奇偶校验位、速度、超时设置等参数,返回操作标志。 5) 读取串口数据
UART应用程序设计 3 串口通信源代码的编译 1) PC平台的串口通信程序编译 3 串口通信源代码的编译 1) PC平台的串口通信程序编译 将光盘提供的serial.c的源代码复制到硬盘中(假设将源码复制在/root/PAX270_Linux目录下)对源码进行编译。 [root@localhost PAX270_Linux]#cd serial [root@localhost serial]#gcc –o serial serial.c 在PC机上运行serial程序; [root@localhost serial]#./serial 1
UART应用程序设计 2)ARM平台的串口通信程序的编译 将光盘提供的serialarm的源代码复制到硬盘中(假设将源码复制在/root/PAX270_Linux目录下)设置交叉编译工具参数(arm-linux-gcc)。 对源代码进行编译。 [root@localhost PAX270_Linux]#cd serialarm [root@localhost serialarm]#arm-linux-gcc –o serialarm serialarm.c 将编译好的程序serial下载到开发板的/usr/qpe/bin目录下。 在目标板上利用chmod命令修改serial的属性 # chmod 755 serial
基于SOCKET的网络应用程序设计 1. Linux网络知识介绍 1)客户端程序和服务端程序 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的:客户端和服务器端。网络程序是先有服务器程序启动,等待客户端的程序运行并建立连接。一般的来说是服务端的程序 在一个端口上监听,直到有一个客户端的程序发来了请求。 2)TCP/UDP介绍 TCP(Transfer Control Protocol)传输控制协议是一种面向连接的协议,当我们的网络程序使用这个协议的时候,网络可以保证我们的客户端和服务端的连接是可靠的、安全的。 UDP(User Datagram Protocol)用户数据报协议是一种非面向连接的协议,这种协议并不能保证我们的网络程序的连接是可靠的,所以我们现在编写的程序一般是采用TCP协议的。
基于SOCKET的网络应用程序设计 2. 初等网络函数介绍(TCP) socket 为网络通讯做基本的准备。成功时返回文件描述符,使用系统用 bind 一旦有了一个套接口以后,下一步工作就是把套接口绑定到本地计算机的某一端口上,但如果只想使用connect()则无此必要。 listen 在服务器端,如果希望等待一个进入的连接请求,然后再处理这个连接请求,可以通过首先调用listen(),然后再调用accept()来实现。 accept 当在远端的客户机试图使用connect()连接服务器使用listen()正在监听的端口时,此连接将会在队列中等待,直到服务器使用accept()处理它。 connect 该系统调用由客户端调用。
USB摄像头接口应用程序设计 USB 摄像头以其良好的性能和低廉的价格得到广泛应用。同时因其灵活、方便的特性,易于集成到嵌入式系统中,现有的符合Video for Linux标准的驱动程序配合通用应用程序,可以实现USB 摄像头视频数据的采集及应用开发。 1.摄像头驱动配置 如果需要在Linux操作系统中使用USB摄像头进行视频数据采集,则必须在进行内核配置时,应检查Linux内核中是否已经添加了对USB摄像头驱动模块的支持。
USB摄像头接口应用程序设计 2. USB摄像头图像采集程序 Linux下摄像头驱动是以81为主设备号的字符型设备驱动,应用程序可以通过打开一个具有该主设备号的设备文件来建立与设备驱动程序的通信,如果所使用的Linux没有该文件,你必须首先手动创建该设备文件,可使用如下指令: [root@localhost root]#mknod /dev/video0 c 81 0 在内核/include/linux/videodev.h中定义了以下几个重要的结构体,打开摄像设备在应用程序中,可通过打开该设备文件,来获取与设备驱动程序的通信的设备描述符。