1硬件分析
在天嵌科技提供的开发板中4 个LED 灯(TQ2440)分别使用了S3C2440芯片的:GPB5、GPB6、GPB7 和GPB8下面列出来对应的原理图
根据上图可以知道,当CPU 的GPB5 到8 是低电平时,LED 灯亮;当为高电平时LED 灯灭。
2编写驱动程序
然后再在内核源码的drivers/char目录下新建一个名为EmbedSky_leds.c的文件:
#i nclude <linux/module.h>
#i nclude <linux/kernel.h>
#i nclude <linux/fs.h>
#i nclude <linux/init.h>
#i nclude <linux/delay.h>
#i nclude <asm/irq.h>
#i nclude<mach/regs-gpio.h>//官方此处为#i nclude <asm/arch/regs-gpio.h>
#i nclude<mach/hardware.h>//官方为#i nclude <asm/hardware.h>
#i nclude <linux/device.h>
#define DEVICE_NAME "EmbedSky-leds" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称*/
#define LED_MAJOR 231 /* 主设备号*/
#define IOCTL_LED_ON 1
#define IOCTL_LED_OFF 0
static unsigned long led_table [] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
static unsigned int led_cfg_table [] =
{
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
static int EmbedSky_leds_open(struct inode *inode, struct file *file)
{
// int i;
// for (i = 0; i < 4; i++)
// {
// 设置GPIO 引脚的功能:本驱动中LED 所涉及的GPIO 引脚设为输出功能
// s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
// }
return 0;
}
static int EmbedSky_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
if (arg > 4)
{
return -EINVAL;
}
switch(cmd)
{
case IOCTL_LED_ON:
s3c2410_gpio_setpin(led_table[arg], 0);
return 0;
case IOCTL_LED_OFF:
s3c2410_gpio_setpin(led_table[arg], 1);
return 0;
default:
return -EINVAL;
}
}
/* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的open、read、write 等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations EmbedSky_leds_fops =
{
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module 变量*/
.open = EmbedSky_leds_open,
.ioctl = EmbedSky_leds_ioctl,
};
static char __initdata banner[] = "TQ2440LEDS";
static struct class *led_class;
/*
* 执行“insmod EmbedSky_leds.ko”命令时就会调用这个函数
*/
static int __init EmbedSky_leds_init(void)
{
int ret;
printk(banner);
/* 注册字符设备驱动程序
* 参数为主设备号、设备名字、file_operations 结构;
* 这样,主设备号就和具体的file_operations 结构联系起来了,
* 操作主设备为LED_MAJOR 的设备文件时,就会调用EmbedSky_leds_fops 中的相关成员函数
* LED_MAJOR 可以设为0,表示由内核自动分配主设备号
*/
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &EmbedSky_leds_fops);
if (ret < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
//注册一个类,使mdev 可以在"/dev/"目录下面建立设备节点
led_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(led_class))
{printk("Err: failed in EmbedSky-leds class. \n");
return -1;
}
//创建一个设备节点,节点名为DEVICE_NAME
device_create(led_class, NULL, MKDEV(LED_MAJOR, 0), NULL, DEVICE_NAME);
//官方文档为class_device_create(led_class, NULL, MKDEV(LED_MAJOR, 0), NULL, DEVICE_NAME);
printk(DEVICE_NAME " initialized\n");
return 0;
}
/*
* 执行”rmmod EmbedSky_leds.ko”命令时就会调用这个函数
*/
static void __exit EmbedSky_leds_exit(void)
{
/* 卸载驱动程序*/
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
device_destroy(led_class, MKDEV(LED_MAJOR, 0));
//删掉设备节点官方文档为class_device_destroy(led_class, MKDEV(LED_MAJOR, 0));
class_destroy(led_class); //注销类
}
/* 这两行指定驱动程序的初始化函数和卸载函数*/
module_init(EmbedSky_leds_init);
module_exit(EmbedSky_leds_exit);
/* 描述驱动程序的一些信息,不是必须的*/
MODULE_AUTHOR("http://www.embedsky.net"); // 驱动程序的作者
MODULE_DEION("TQ2440/SKY2440 LED Driver"); // 一些描述信息
MODULE_LICENSE("GPL"); // 遵循的协议
注意:以上红色部分被屏蔽了,因为在“arch/arm/plat-s3c24xx/common-smdk.c”文件中已经完成了这个初始化了。
3在内核源码中添加对驱动的支持
然后修改同目录下的“Kconfig”文件即linux-2.6.30.4/drivers/char/Kconfig
config EmbedSky_LEDS
tristate "TQ2440/SKY2440 LEDs Driver"
depends on ARCH_S3C2440
help
EmbedSky TQ2440/SKY2440 User leds.
然后修改同目录的“Makefile”文件
obj-$(CONFIG_EmbedSky_LEDS) += EmbedSky_leds.o
添加完以上内容之后,输入:#make menuconfig,然后配置如下:
Device Drivers --->
Character devices --->
<*> TQ2440/SKY2440 LEDs Driver
将其选择为“M”(模块),然后保存配置,编译出内核镜像烧写到开发板中。
然后再使用命令#make SUBDIR=drivers/char/ modules,然后编译出驱动模块,在内核目录下面的也可以将其配置为“*”(添加到内核中),然后编译出镜像,烧写到开发板中,LED 灯就可以进行控制了。下面是加载模块的情况,在/dev/目录下面有EmbedSky-leds的设备名:“drivers/char/”目录下面,名为:EmbedSky_led.ko,将其复制到开发板中。
4编写LED灯控制程序
下面列出LED 灯的控制程序,这里的控制程序直接使用的SKY2440 或TQ2440 光盘提供的LED 灯的
控制程序代码
#i nclude <stdio.h>
#i nclude <stdlib.h>
#i nclude <unistd.h>
#i nclude <sys/ioctl.h>
int main(int argc, char **argv)
{
int on;
int led_no;
int fd;
if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 ||on < 0 || on > 1 || led_no < 1 || led_no > 4)
{
fprintf(stderr, "Usage: leds led_no 0|1\n");
exit(1);
}
fd = open("/dev/EmbedSky-leds", 0);
if (fd < 0)
{
perror("open device leds");
exit(1);
}
ioctl(fd, on, (led_no-1));
close(fd);
return 0;
}
使用#arm-linux-gcc –g leds.c –o leds来生成led程序下载到开发板中
一定注意是arm-linux-gcc交叉编译工具来编译源文件这样生成的文件才能在嵌入式设备上使用如果是gcc编译源文件出来的程序只能在PC上使用安装驱动程序Insmod EmbedSky_leds.ko
然后./leds 1 1意思是第一个灯亮
./leds 1 0第一个灯灭至此终于完成了在linux下面点亮LED的任务~