《嵌入式Linux下LED报警灯驱动设计及编程》
实验报告
学生姓名:
学号:
专业班级:
指导教师:
完成时间:
实验5 嵌入式Linux下LED报警灯驱动设计及编程
一.实验目的
理解驱动本质,掌握嵌入式Linux系统下驱动开发相关知识,包括端口寄存器访问、接口函数编写、和文件系统挂接、注册及相关应用编程等知识点。
二.实验内容
实验5.1 嵌入式Linux下LED报警灯驱动设计及跑马灯应用编程
实验5.2 添加看门狗功能的跑马灯应用编程
三.预备知识
Linux使用、驱动相关知识等
四.实验设备及工具(包括软件调试工具)
硬件:ARM 嵌入式开发平台、PC 机Pentium100 以上、串口线。
软件:WinXP或UBUNTU开发环境。
五.实验5.1步骤
5.1 前期准备
(1)看懂相关硬件电路图【见S3C6410实验箱电路图-底板.pdf】,以LED报警灯为例进行设计
打开PDF硬件电路图,明确LED灯用到的多个GPIO及其控制器
本实验电路LED1-------GPM0
LED2-------GPM1
LED3-------GPM2
LED4-------GPM3
LED5-------GPM4
LED6-------GPM5
LED7-------GPQ0
LED8-------GPQ1
得出结论:8个LED灯使用到的硬件控制器分别为GPM和GPQ两个硬件控制器
(2)在芯片手册中找到相应的硬件控制器部分,重心是看懂端口寄存器
本实验要求完成LED流水灯设计,所以需要设置控制器中端口寄存器:
GPMCON----设置相应位为输出口
GPMDAT-----控制相应位输出高电平-----点亮LED灯
输出低电平-----熄灭LED灯
(3) linux内核中相关寄存器读写函数
读寄存器函数
readl(寄存器虚地址);
写寄存器函数
writel(值(无符号整型), 寄存器虚地址);
具体端口寄存器地址宏定义在/opt/FriendlyARM/linux-2.6.38/arch/arm/mach-s3c64xx/include/mach文件夹下的文件中,如端口M寄存器在gpio-bank-m.h文件中有定义:
#define S3C64XX_GPMCON (S3C64XX_GPM_BASE + 0x00)
#define S3C64XX_GPMDAT (S3C64XX_GPM_BASE + 0x04)
5.2 LED报警灯驱动设计s3c6410_leddrv.c
(1)头文件包含和相关宏定义
#include
#include
#include
//#include #include
#include
#include
#include
#include
#include
#include
#include
#include #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ON 1
#define OFF 0
(2)编写驱动接口函数
/*
功能:配置GPM0~5/GPQ0~1为输出口
参数:无
返回值:无
*/
void LedConfig(void)
{
//读出端口M控制寄存器(S3C64XX_GPMCON)值,修改并写回相关端口寄存器//add your code
unsigned int tmp;
tmp =readl(S3C64XX_GPMCON);
tmp &= ~((0XF<<0X0)|(0XF<<0X4)|(0XF<<0X8)|(0XF<<0XC)|(0XF<<0X10)|(0XF<<0X14));
tmp |= (0X1<<0X0)|(0X1<<0X4)|(0X1<<0X8)|(0X1<<0XC)|(0X1<<0X10)|(0X1<<0X14);
writel(tmp,S3C64XX_GPMCON);
}
/*
功能:点亮第i个LED灯
参数:无符号整型变量iLed,表示第i个LED灯
返回值:无
*/
void iLedOn(unsigned int iLed)
{
//读出端口M数据寄存器(S3C64XX_GPKDAT)值,修改并写回相关端口寄存器
//add your code here
unsigned int tmp;
tmp =readl(S3C64XX_GPMDAT);
tmp &= ~((0X1<<0X0)|(0X1<<0X1)|(0X1<<0X2)|(0X1<<0X3)|(0X1<<0X4)|(0X1<<0X5));
writel(tmp,S3C64XX_GPMDAT);
}
/*
功能:熄灭第i个LED灯
参数:无符号整型变量iLed,表示第i个LED灯
返回值:无
*/
void iLedOff (unsigned int iLed)
{
//读出端口M数据寄存器(S3C64XX_GPKDAT)值,修改并写回相关端口寄存器
//add your code here
unsigned int tmp;
tmp =readl(S3C64XX_GPMDAT);
tmp &= ~((0X1<<0X0)|(0X1<<0X1)|(0X1<<0X2)|(0X1<<0X3)|(0X1<<0X4)|(0X1<<0X5));
tmp |= (0X1<<0X0)|(0X1<<0X1)|(0X1<<0X2)|(0X1<<0X3)|(0X1<<0X4)|(0X1<<0X5);
writel(tmp,S3C64XX_GPMDAT);
}
(2)和文件系统接口对接
static int s3c6410_led_open(struct inode *inode, struct file *filp)
{
//把之前的端口K控制寄存器值读出来保存起来
//调用LedConfig函数,把GPIO口配置成输出口
//add your code
old_gpmcon_val=readl(S3C64XX_GPMCON);
LedConfig();
renturn 0;
}
static int s3c6410_led _release(struct inode *inode, struct file *filp)
{
//恢复之前的端口K控制寄存器初始值
//add your code
writel(old_gpmcon_val,S3C64XX_GPMCON);
renturn 0;
}
static long s3c6410_led _ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case ON:
//点亮所有LED灯
//add your code
i LedOn();
break;
case OFF:
//熄灭所有LED灯
//add your code
break;
}
}
struct file_operations led_fops=
{
.release=___s3c6410_led_release______,
.unlocked_ioctl=___s3c6410_led_ioctl____, };
(3)添加模块标记代码
static int __init led_dev_init(void)
{
int ret;
注册设备printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit led_dev_exit(void)
{
//注销设备
//add your code
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lic@njupt.");
5.2 编写Makefile并加载到内核
(1)编写Makefile如下:
all:
make –C pwd) modules
嵌入式Linux应用软件开发流程
从软件工程的角度来说,嵌入式应用软件也有一定的生命周期,如要进行需求分析、系统设计、代码编写、调试和维护等工作,软件工程的许多理论对它也是适用的。 但和其他通用软件相比,它的开发有许多独特之处: ·在需求分析时,必须考虑硬件性能的影响,具体功能必须考虑由何种硬件实现。 ·在系统设计阶段,重点考虑的是任务的划分及其接口,而不是模块的划分。模块划分则放在了任务的设计阶段。 ·在调试时采用交叉调试方式。 ·软件调试完毕固化到嵌入式系统中后,它的后期维护工作较少。 下面主要介绍分析和设计阶段的步骤与原则: 1、需求分析 对需求加以分析产生需求说明,需求说明过程给出系统功能需求,它包括:·系统所有实现的功能 ·系统的输入、输出 ·系统的外部接口需求(如用户界面) ·它的性能以及诸如文件/数据库安全等其他要求 在实时系统中,常用状态变迁图来描述系统。在设计状态图时,应对系统运行过程进行详细考虑,尽量在状态图中列出所有系统状态,包括许多用户无需知道的内部状态,对许多异常也应有相应处理。 此外,应清楚地说明人机接口,即操作员与系统间地相互作用。对于比较复杂地系统,形成一本操作手册是必要的,为用户提供使用该系统的操作步骤。为使系统说明更清楚,可以将状态变迁图与操作手册脚本结合起来。
在对需求进行分析,了解系统所要实现的功能的基础上,系统开发选用何种硬件、软件平台就可以确定了。 对于硬件平台,要考虑的是微处理器的处理速度、内存空间的大小、外部扩展设备是否满足功能要求等。如微处理器对外部事件的响应速度是否满足系统的实时性要求,它的稳定性如何,内存空间是否满足操作系统及应用软件的运行要求,对于要求网络功能的系统,是否扩展有以太网接口等。 对于软件平台而言,操作系统是否支持实时性及支持的程度、对多任务的管理能力是否支持前面选中的微处理器、网络功能是否满足系统要求以及开发环境是否完善等都是必须考虑的。 当然,不管选用何种软硬件平台,成本因素都是要考虑的,嵌入式Linux 正是在这方面具有突出的优势。 2、任务和模块划分 在进行需求分析和明确系统功能后,就可以对系统进行任务划分。任务是代码运行的一个映象,是无限循环的一段代码。从系统的角度来看,任务是嵌入式系统中竞争系统资源的最小运行单元,任务可以使用或等待CPU、I/O设备和内存空间等系统资源。 在设计一个较为复杂的多任务应用系统时,进行合理的任务划分对系统的运行效率、实时性和吞吐量影响都极大。任务分解过细会不断地在各任务之间切换,而任务之间的通信量也会很大,这样将会大大地增加系统的开销,影响系统的效率。而任务分解过粗、不够彻底又会造成原本可以并行的操作只能按顺序串行执行,从而影响系统的吞吐量。为了达到系统效率和吞吐量之间的平衡折中,在划分任务时应在数据流图的基础上,遵循下列步骤和原则:
linux驱动原理-LED驱动分析
第五章:Linux驱动介绍 5.1 驱动原理: LINUX提供标准接口函数给底层,底层驱动按照LINUX编程规则进行驱动编写。操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。设备驱动程序是内核的一部分,硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有60%以上。因此,熟悉驱动的编写是很重要的. Linux内核中采用可加载的模块化设计(LKMs,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其他的代码可以编译到内核中,或者编译为内核的模块文件(在需要时动态加载)。 5.2 内核模块的主要相关命令 ◆lsmod:列出当前系统中加载的模块,其中左边第一列是模块名,第二列是该模块大小,第三列 则是使用该模块的对象数目。 ◆rmmod:是用于将当前模块卸载。 ◆insmod和modprobe是用于加载当前模块,但insmod不会自动解决依存关系,即如果要加 载的模块引用了当前内核符号表中不存在的符号,则无法加载,也不会去查在其他尚未加载的模块中是否定义了该符号;modprobe可以根据模块间依存关系以及/etc/modules.conf文件中的内容自动加载其他有依赖关系的模块。 5.3 设备分类 Linux系统的设备分为三类:字符设备--(包含一个混杂设备)、块设备和网络设备。 字符设备通常指像普通文件或字节流一样,以字节为单位顺序读写的设备,如并口设备、虚拟控制台等。字符设备可以通过设备文件节点访问,它与普通文件之间的区别在于普通文件可以被随机访问(可以前后移动访问指针),而大多数字符设备只能提供顺序访问,因为对它们的访问不会被系统所缓存。但也有例外,例如帧缓存(framebuffer)是一个可以被随机访问的字符设备。 块设备通常指一些需要以块为单位随机读写的设备,如IDE硬盘、SCSI硬盘、光驱等。块设备也是通过文件节点来访问,它不仅可以提供随机访问,而且可以容纳文件系统(例如硬盘、闪存等)。Linux可以使用户态程序像访问字符设备一样每次进行任意字节的操作,只是在内核态内部中的管理方式和内核提供的驱动接口上不同。 $ ls –l /dev crw-rw---- 1 root uucp 4, 64 08-30 22:58 ttyS0 /*串口设备, c表示字符设备*/ brw-r----- 1 root disk 8, 0 08-11 23:03 sda /*硬盘设备,b表示块设备*/ 5.4 设备驱动程序工作原理 模块在调用insmod命令时被加载,此时的入口点是init_module()函数,通常在该函数中完成设备的注册。同样,模块在调用rmmod命令时被卸载,此时的入口点是cleanup_module()函数,在该函数中完成设备的卸载。在设备完成注册加载之后,用户的应用程序就可以对该设备进行一定的操作,
嵌入式点亮一个LED灯的程序
飞凌OK6410开发板(裸板)第一个点亮LED灯程序,主要的C程序,完整程序请下载附件。 #define rGPMCON (*(volatile unsigned *)(0x7F008820)) #define rGPMDAT (*(volatile unsigned *)(0x7F008824)) #define rGPMPUD (*(volatile unsigned *)(0x7F008828)) void msDelay(int time) { volatile unsigned int i,j; for(i = 0; i < 2000000; i++) for(j=0; j