嵌入式Linux学习笔记之LED驱动

最近在学习嵌入式Linux驱动开发,大致了解了驱动的基本开发流程,本文主要针对字符设备驱动开发做一个简要介绍,也当作是对这几天工作的一个小小总结。

计算机系统是由软硬件相互协调共同完成工作的,作为专用计算机系统的嵌入式系统也不例外,既要有CPU、SDRAM、FLASH、IO等硬件,同时也少不了操作系统和应用软件等软件的支持,而作为应用程序与硬件的桥梁——驱动程序,是整个嵌入式系统开发过程中的关键环节。驱动开发涉及底层,而了解底层作用机制对于整个系统的开发意义重大。

Linux内核中有60%以上是驱动程序,它不仅支持驱动以静态形式编译进内核,而且允许驱动以模块的形式动态加载进内核,大大减小了内核的大小,同时便于调试分析。

Linux将所有的设备当作文件进行处理,Linux系统的设备分为三类:字符设备、块设备和网络设备。字符设备的驱动有一个固定的模板,主要编写file_operations结构体中的成员函数,这些函数最终会在应用程序进行Linux的open()、write()、read()、ioctl()、close()等系统调用时被调用。

驱动开发作用在内核空间,应用程序开发作用在用户空间。下面以GPIO端口驱动LED亮灭为例。

1、查看开发板原理图和芯片数据手册;

   

从上图图中可以看出GPF4~GPF7 分别控制D12~D9,当GPF4~GPF7被配置为输出模式,同时向该引脚输出0时LED亮,输出1时LED灭。

从下图可以看出GPFCON控制寄存器对应的每个引脚由两位决定,01时表示输出,所以GPF7~GPF4为0101时,四个引脚配置为输出,所以GPFCON=0x55FF。GPFDAT的八位分别对应八个引脚,0表示输出0,1表示输出1,譬如要使D9~D12都亮,则GPFDAT=0x0F。GPFUP对应的八位为1时表示上拉失效,为0时对应位上拉。同时可以看出GPFCON、GPFDAT、GPFUP的物理地址分别是0x56000050、0x56000054、0x56000058。

2、在虚拟机的shell终端下,编写和编译驱动程序和测试程序,步骤如下图;

源码如下:

led.c

<span style="font-size:12px;">#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/delay.h>
#include<linux/device.h>
#include<linux/types.h>
#include<linux/ioctl.h>
#include<linux/cdev.h>
#include<linux/errno.h>

#include<asm/io.h>
#include<asm/hardware.h>
#include<asm/arch/regs-gpio.h>
#include<asm/arch-s3c2410/hardware.h>

#define S3C2410_GPFCON S3C2410_GPIOREG(0x50)
#define S3C2410_GPFDAT S3C2410_GPIOREG(0x54)
#define S3C2410_GPFUP S3C2410_GPIOREG(0x58)

#define GPFCON *(volatile unsigned int *)S3C2410_GPFCON
#define GPFDAT *(volatile unsigned int *)S3C2410_GPFDAT
#define GPFUP *(volatile unsigned int *)S3C2410_GPFUP

void delay_1(void)
{
	int i,j;
	for(i=0;i<1000;i++)
		for(j=0;j<10000;j++);
}

#define LED1_ON() (GPFDAT &=~0x8f)
#define LED2_ON() (GPFDAT &=~0x4f)
#define LED3_ON() (GPFDAT &=~0x2f)
#define LED4_ON() (GPFDAT &=~0x1f)
#define LED1_OFF() (GPFDAT |=0x80)
#define LED2_OFF() (GPFDAT |=0x40)
#define LED3_OFF() (GPFDAT |=0x20)
#define LED4_OFF() (GPFDAT |=0x10)

static int LedStatus;
void LedSet(int led)
{
	LedStatus = led;
	if(LedStatus&1)
		LED1_ON();
	else
		LED1_OFF();
	if(LedStatus&2)
		LED2_ON();
	else
		LED2_OFF();
	if(LedStatus&4)
		LED3_ON();
	else
		LED3_OFF();
	if(LedStatus&8)
		LED4_ON();
	else
		LED4_OFF();
}

void LedDisp(void)
{
	LedSet(0x08);
	delay_1();
	LedSet(0x04);
	delay_1();
	LedSet(0x02);
	delay_1();
	LedSet(0x01);
	delay_1();
	LedSet(0x02);
	delay_1();
	LedSet(0x04);
	delay_1();
	LedSet(0x08);
	delay_1();
}

#define DEVICE_NAME "led"
#define LED_MAJOR 220

static int led_open(struct inode *inode,struct file *file)
{
	GPFCON=0x5500;
	GPFUP=0xff;
	printk("LED Driver Open Called!\n");
	return 0;
}

static int led_release(struct inode *inode,struct file *file)
{
	printk("LED Driver Release Called\n");
	return 0;
}

static int led_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg)
{
	int err=0;
	if(cmd==1){
		while(arg--)
		{
			LedDisp();
			printk("....");
		}
		printk("\n");
		return 0;
	}
	return err;
}

static struct file_operations led_fops =
{
	.owner   = THIS_MODULE,
	.open    = led_open,
	.release = led_release,
	.ioctl   = led_ioctl,
};

static int __init led_init(void)
{
	int result=0;
	result=register_chrdev(LED_MAJOR,DEVICE_NAME,&led_fops);
	if(result < 0)
	{
		printk("failed to register!\n");
		return result;
	}
	printk("success to register\n");
	return 0;
}

static void __exit led_exit(void)
{
	printk("Led Driver Module Exit\n");
	unregister_chrdev(LED_MAJOR,DEVICE_NAME);
}

module_init(led_init);
module_exit(led_exit);

MODULE_AUTHOR("njust_sxy");
MODULE_DESCRIPTION("Led Driver");
MODULE_LICENSE("GPL");</span>

Makefile

<span style="font-size:12px;">obj-m :=led.o
KDIR := /home/sxy/linux-2.6.8.1-zzm
PWD :=$(shell pwd)
CC=arm-linux-gcc
default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
	rm -rf .*.cmd *.o *.mod.c *.ko</span>

test.c

<span style="font-size:12px;">#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<linux/delay.h>
#include<sys/ioctl.h>

int main(int argc, char *argv[])
{
	int fd;
	int val=-1;
	fd=open("/dev/led",0);
	if(fd<0)
	{
		perror("can not open device");
		exit(1);
	}
	while(1){
		printf("please select number to run program\n");
		printf("1:led on \n2:quit ");
		scanf("%d",&val);
		if(val==1)
			ioctl(fd,1,10);
		else
			if(val==2){
				close(fd);
			}
		}
	return 0;
}<strong>
</strong></span>

3、在串口终端上,加载驱动模块和测试驱动程序;

mount:挂载虚拟机的/home目录到开发板的/tmp/led目录下;

insmod:加载模块;

mknod:创建设备节点;

rmmod:卸载模块;

led.ko:led设备驱动模块。

实验结果:在终端打印出“...."的同时,LED从D12~D9相继亮灭(流水灯)。

总结:整个LED驱动开发并不算难,如果有了裸机编程的基础,加上一定的Linux基础,上手会更加容易。期间也碰到不少疑问,而最后大部分也都得到了解答。总之,基础还是很重要的,理论+实践才是王道。

ps:曾经尝试调用内核中的s3c2410_gpio_cfgpin()和s3c2410_gpio_setpin()两个函数来实现端口输出配置和向数据寄存器写数据,但会出现下面这个问题:

s3c2410_gpio_cfgpin()和s3c2410_gpio_setpin()这两个函数在/arch/arm/mach-s3c2410/gpio.c文件中定义,在asm-arm/arch-s3c2410/hardware.h中extern声明,最初编译时警告说这两个函数是”undefined“的,后来我在gpio.c文件中添加EXPORT_SYMBOL(s3c2410_gpio_cfgpin);EXPORT_SYMBOL(s3c2410_gpio_setpin);导出符号,却出现“no
CRC”这个警告,这个问题已经折腾我好几天了,求高手解答。

嵌入式Linux学习笔记之LED驱动

时间: 2024-12-11 04:39:15

嵌入式Linux学习笔记之LED驱动的相关文章

嵌入式Linux学习笔记(基于S5PV210 TQ210)

基于S5PV210.TQ210平台. 本文更多的是教会大家如何学习! 包括如下内容: 1.前言 2.开发环境搭建 3.制作交叉编译器 4.裸机编程 4.1.汇编学习 4.2.S5PV210启动流程 4.3.点亮一个LED 4.4.串口 4.5.实现printf 4.6.时钟配置 4.7.重定位 4.8.DDR 4.9.NAND读写 4.11.LCD操作 5.移植u-boot(基于u-boot-2014.4版本) 5.1.概述 5.2.u-boot配置过程分析 5.3.u-boot编译过程分析 5

嵌入式Linux学习笔记(0)基础命令。——Arvin

学习记录: 到今天为止ARM裸机开发学习进程:1.2.1-1.2.14 预科班知识Linux介绍学习进程:0.2.1-0.2.6 学习内容笔记: 学习了Linux的开发方式的优劣介绍 学习了常用文件夹命令 ls(list) ls -a 显示所有文件(包括隐藏文件) ls -l 显示所有详细信息 ls -a -l也可以 man(查询命令) man 1 ls(1表示查询命令,ls表示查询的内容) cd(change directory) cd ..代表上一层 cd ../../代表上上层 cd .是

嵌入式linux学习笔记1—内存管理MMU之虚拟地址到物理地址的转化

一.内存管理基本知识 1.S3C2440最多会用到两级页表:以段的方式进行转换时只用到一级页表,以页的方式进行转换时用到两级页表.页的大小有三种:大页(64KB),小页(4KB),极小页(1KB).条目也称为"描述符",有:段描述符,大页描述符,小页描述符,极小页描述符——他们保存大页,小页,极小页的起始物理地址:粗页表描述符,细页表描述符——他们保存二级页表的物理地址. 2.一级页表描述符的最低两位,可分为以下四种情况: (1).0b00:无效. (2).0b01:粗页表. (3).

新手嵌入式linux学习必备书籍

很多的嵌入式Linux新手,对自己的学习过程十分迷茫,不知道从何学习?学习过程中需要看哪些相关书籍?之前的写的一篇关于嵌入式开发如何入门,如何深入?的文章希望对你有所帮助,接下来就为嵌入式Linux新手们推荐一些关于嵌入式Linux学习的核心书籍. Linux基础 1.<Linux与Unix Shell 编程指南> C语言基础 1.<C Primer Plus,5th Edition>[美]Stephen Prata著 2.<The C Programming Languag

嵌入式开发学习笔记 ( java - c/c++ :从入门到入门 )

发现放到Blog之后排版全乱套了.. 已经把PDF上传到资源页了  http://download.csdn.net/detail/lyy289065406/8934637 那边排版好看一点...看官们随意吧 >...< · 目 录 导 航 1. 引言 1.1. 编写目的 1.2. 阅读范围 1.3. 声明 1.4. 缩写词/名词解释 1.5. 参考资料 2. 嵌入式开发学习笔记 2.1. 开发环境/测试环境 2.2. 开坑:提要 2.3. 入坑:JNI 2.3.1. navicate 接口定

linux学习笔记(一)-文件目录相关的命令&&文件通配符

一.几个命令概述 1.查看目录以及目录底下的文件:ls(-a显示隐藏文件:-d显示目录本身:-l显示详细内容:-R递归显示,即把子目录的文件也显示出来:-h以更加被人类理解的格式显示,比如显示文件大小的时候用M为单位显示:-i显示文件索引ID) 2.查看文件内容:cat,head,tail,less,more,tac cat:将文件连接起来,输出在屏幕上,可接多个文件(-E:显示隐藏的换行符:-n:显示出行号) head:默认查看文件的前10行(-n#:查看文件的前#行.空白行也是一行) tai

Linux学习笔记四:Linux的文件搜索命令

1.文件搜索命令  which 语法:which [命令名称] 范例:$which ls  列出ls命令所在目录 [[email protected] ~]$ which ls alias ls='ls --color=auto' /bin/ls 另外一个命令:whereis [名称名称],也可以列出命令所在目录. [[email protected] ~]$ whereis ls ls: /bin/ls /usr/share/man/man1/ls.1.gz /usr/share/man/ma

Linux学习笔记——例说makefile 增加系统共享库

0.前言 从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力.所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法. 例说makefile大致分为4个部分 1.只有单个C文件 2.含有多个C文件 3.需要包括头文件路径 4.增加宏定义 5.增加系统共享库 6.增加自定义共享库 7.一个实际的例子 [代码仓库]--makefile-example

Linux学习笔记——如何使用echo指令向文件写入内容

0.前言 本文总结如何使用echo命令向文件中写入内容,例如使用echo指令覆盖文件内容,使用echo指令向文件追加内容,使用echo指令往文件中追加制表符. echo向文件中输出内容的基本方法是使用IO重定向指令--">",默认情况下echo输出到标准输出中,使用>指令可重定向输出到文件中. 1.echo指令基本用法 [1]Linux官方用户手册--echo指令 [2]输入指令获得帮助 sudo echo --help 返回内容如下 用法:echo [短选项]... [字