`

Linux设备驱动的Hello World—LED驱动

 
阅读更多

要看懂驱动源码,肯定是要从最基本的看起,C语言中,如printf("hello world\n");而对于驱动,肯定是LED,呵呵,恰好年轻时写过一个,还保留着,而且是流水灯式的,下面以ARM270(共有8个LED灯)为例。

一、无操作系统时的LED驱动

在嵌入式系统的设计中,LED一般直接由CPU的GPIO(通用可编程 I/O 口)控制。GPIO一般由两组寄存器控制,即一组控制寄存器和一组数据寄存器。控制寄存器可设置GPIO 口的工作方式为输入或输出。当引脚被设置为输出时,向数据寄存器的对应位写入1和0会分别在引脚上产生高电平和低电平;当引脚设置为输入时,读取数据寄存器的对应位可获得引脚上相应的电平信号。则在无操作系统的情况下,设备驱动代码如下所示。

//片选B-CS4基地址为0x10000000,数码管的偏移地址为0x500000.

#defineSHOW_LED (*((volatile unsigned int *)0x10500000))

#defineCTRL _LED (*((volatile unsigned int *)0x40E00068)) //GPIO80设置为转换功能2

//初始化LED ,一般不需要初始化,因为boot已经对其进行初始化了。

voidLightInit(void)

{

CTRL_LED = 0x1400; /*设置GPIO为输出*/

}

//点亮第n个LED

voidLightOn(void)

{

SHOW_LED &= ~(1 << n);/*在GPIO上输出低电平*/

}

//熄灭第n个LED

voidLightOff(void)

{

SHOW_LED |= (1 << n); /*在GPIO上输出高电平*/

}

上述程序中的LightInit()、LightOn()、LightOff()等函数都将作为 LED驱动提供给应用程序的外部接口函数。 程序中ToVirtual()等函数的作用是当系统启动了硬件MMU之后,根据物理地址和虚拟地址的映射关系,将寄存器的物理地址转化为虚拟地址。

二、Linux系统下的LED驱动

在Linux 操作系统下编写LED 设备的驱动时,操作硬件的LightInit()、LightOn()、LightOff()这些函数仍然需要,但是,需要遵循Linux编程的命名习惯,重新将其命名为light_init()、light_on()、light_off()。这些函数将被LED 驱动中独立于设备的针对内核的接口进行调用。

led.c:

//head
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>

#define BASE_NUM 0
#define COUNT 1
#define DEV_NAME "led"

static char k_buf[128] = {0};
//static struct cdev *led_cdev = NULL;
static struct cdev led_cdev;
static dev_t dev = 0;
//#define VADDR __REG(0x10500000)
static volatile unsigned int pa = 0x10500000;
static volatile unsigned int va;

//description
MODULE_AUTHOR("huangminqiang2007@163.com");
MODULE_DESCRIPTION("THIS IS FOR TESTING");
MODULE_LICENSE("GPL");

//open close read write
//open
static int led_open(struct inode *i_node, struct file *filep)
{
	return 0;
}

//close
static int led_close(struct inode *i_node, struct file *filep)
{
	return 0;
}

//read
static int led_read(struct file *filep,char *buf,
			size_t size,loff_t *offset)
{
	int cnt = -1;
	
	if(NULL == filep)
	{
		goto _out;
	}	
	
	cnt = copy_to_user(buf,k_buf,sizeof(k_buf));
	
	return (sizeof(k_buf)-cnt);
_out:
	return -1;
}

//write
static int led_write(struct file *filep,const char *buf,
			size_t size,loff_t *offset)
{
	if(NULL == filep)
	{
		goto _out;
	}	

	copy_from_user(k_buf,buf,size);
	*(char *)va = k_buf[0];	
	//writew(k_buf[0],va);
	//*(char *)VADDR = k_buf[0];
	return size;
_out:
	return -1;
}

//file operations
static struct file_operations led_ops = {
	.owner=	 	THIS_MODULE,
	.open=		led_open,
	.release=	led_close,	
	.read= 		led_read,
	.write= 	led_write,
};

//init
static int __init led_init(void)
{
	int cnt = -1;
	
	//paddr to vaddr
	(int *)va = ioremap(pa,4);

	//alloc cdev region
	cnt = alloc_chrdev_region(&dev,BASE_NUM,COUNT,DEV_NAME);
	if(0 > cnt)
	{
		goto _out;
	}
		
	//alloc cdev,如果直接定义变量则不用申请内存。
	//led_cdev = cdev_alloc();
	//init cdev
	cdev_init(&led_cdev,&led_ops);
	//add cdev
	cnt = cdev_add(&led_cdev,dev,COUNT);
	//printk major_num
	printk("<0>" "major number : %d\n",MAJOR(dev));


	if(0 > cnt)
	{
		goto _out;
	}

	printk("<0>" "init ok!\n");

	return 0;
_out:
	return -1;
}

//exit
static void __exit led_exit(void)
{
	//unregister cdev region
	unregister_chrdev_region(dev,COUNT);
	//del cdev
	cdev_del(&led_cdev);

	iounmap((char *)va);

	printk("<0>" "exit ok!\n");
}

//add to kernel
module_init(led_init);
module_exit(led_exit);
Makefile:
EXTRA_CFLAGS += $(DEBFLAGS) -Wall

ifneq ($(KERNELRELEASE),)
	obj-m := led.o
else
	KDIR ?= /opt/270-s-2.6.9/linux-2.6.9_270-s  #之前的内核位置
	PWD := $(shell pwd)
all:
	make $(EXTRA_CFLAGS) -C $(KDIR) M=$(PWD) modules
endif
clean:
	rm *.o *.ko led.mod.c 
test.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define DEV_NAME "/dev/test"
#define FLAGS (O_RDWR | O_CREAT)

int main(void)
{
	int fd = -1;
	int i = -1;
	int cnt = -1;
	unsigned char led = 0xff;
	
	//open
	fd = open(DEV_NAME, FLAGS);
	if(0 > fd)
	{
		perror("open err:");
		goto _out;
	}	
	
	//流水灯
	while(1)
	{
		for(i = 0; i < 8; i++)
		{
			led &= ~(1 << i);
			cnt = write(fd, &led, 1);
			if(0 > cnt)
			{
				perror("write err:");
				goto _out;
			}
			led = 0xff;
			sleep(1);
		}
	}

	//close
	close(fd);

	return 0;
_out:
	return -1;
}

由于代码比较多,2个目录,一个驱动driver目录,一个应用程序目录test,上面程序的下载代码地址:http://download.csdn.net/detail/huangminqiang201209/4904596

大致过程如下:

1.生成led.ko

A.编写led.c

1)驱动描述:MODULE_DESCRIPTION,MODULE_LICENSE等。

2)系统操作函数:open、read、write、close等。

3)file_operation结构体的封装,2.6内核的。

4)注册设备:__init中register_chrdev。

5) 注销:__exit中unregister。

6) 模块的导入导出内核:module_init(led_init); module_exit(led_exit);

B.编写Makefile(这个Makefile还是有一点点复杂的哦),与led.c同一目录

C.make生成led.ko

还有一种生成*.ko的方法是编译内核模块,Makeconfigure:Y/N/M选择(呵呵,在此之前还要对Makefile、Kconfig等进行配置)M是将该功能编译成可以在需要时动态插入到内核中的模块,而Y是直接编译进内核,可以省略后面的几步,直接运行app。

2.生成应用程序APP

1)编写main.c,应用程序

2)/usr/local/hybus-arm-linux-R1.1/bin/arm-linux-gcc main.c -o app

3.led.koapp通过NFS挂在到开发板上。

4.创建led结点

mknod /dev/led c 2500

5.装载led模块并查看led.ko

insmod led.ko lsmod led.ko

6.运行应用程序./app

7.卸载led模块

rmmod led.ko

分享到:
评论

相关推荐

    嵌入式linux开发

    系列文章选取了几个典型的linux设备驱动,如LED、gpio按键、u盘、sd卡、网卡、nand flash、lcd等等,介绍了linux设备驱动框架的模式。针对嵌入式开发,介绍了nand flash文件系统的移植,nfs的搭建(调试开发根文件...

    树莓派驱动开发实战,基于Raspberry Pi 3B+平台学习Linux驱动开发的记录与分享

    基于Raspberry Pi 3B+平台学习Linux驱动开发的记录与分享,旨于对Linux内核模块机制的熟悉、常见接口的Linux驱动...目前实现hello world, gpio led, gpio key, device io,pwm, 红外, PDD与设备树相关驱动,还有一些文档

    Linux学习备忘册--II.驱动学习_V1.3.pdf

    4.加载和卸载helloworld模块。5.移植编写led驱动模块。6.移植编写lcd驱动模块。上面的学习过程没有阶跃,应该不存在从某一步到某一步无法跨越的问题,再深入的,去理解驱动模块,去改写移植其他类型的驱动模块应该也...

    linux 多种应用程序

    压缩包里带了linux 多种驱动的应用层测试程序 485 adc-test backlight bserv button_test cam_test gprs gps hello_world led_test mplayer pic ttytest wlan

    FS4412内核各类模块实验代码

    FS4412内核hello world模块实验代码、ubuntu18.04_helloworld实验代码、FS412 LED 字符设备驱动程序 传统模型、FS4412 LED字符设备、FS4412 LED字符设备驱动程序 平台总线(platformbus)模型、FS4412 串口模块实验...

    ARM Cortex-A8和Android 4.x联动报警系统

    第46节:Android 4.x设备驱动开发HelloWorld演示.zip 第47节:Android 4.x字符设备驱动程序.zip 第48节:Android 4.x重要内核数据结构.zip 第49节:Android 4.x字符设备驱动程序示例.zip 第50节:另一种简单的字符...

    linux基础实验篇

    实验一 HelloWorld................................................................................................................64 实验二编译Bootloader...................................................

    Magic ARM 2410

    5.1 HelloWorld 程序实验...........................175 5.2 Linux 定时器实验 ................................178 5.3 多进程实验...........................................180 5.4 多线程实验..............

    uboott移植实验手册及技术文档

    4、交叉编译器 arm-softfloat-linux-gnu-gcc-3.4.5 【实验步骤】 一、建立自己的平台类型 (1)解压文件 #tar jxvf u-boot-1.3.1.tar.bz2 (2)进入 U-Boot源码目录 #cd u-boot-1.3.1 (3)创建自己的开发板...

    mini2440用户手册

    1.1.3 Linux系统特性...........................................................................................................................- 18 -  1.1.4 WindowsCE 5.0 系统特性........................

    GEC2410B实验箱教学平台-基础实验教程

    BOOT在GEC2410上的移植..................................................LINUX的LED驱动程序设计..........................................401 5.7 嵌入式GUI开发..................................................

Global site tag (gtag.js) - Google Analytics