您的位置:首页 > 其它

嵌入式内核开发:led灯驱动

2016-08-20 19:51 375 查看
接触单片机或者其他嵌入式芯片的编程中,最简单的就是GPIO的High/Low控制。

比如学校里面最基本的单片机实验:led控制,流水灯,数码管。其实都是最基本的GPIO操作。

那么同样,对应到linux嵌入式开发,GPIO的High/Low控制也是最简单的操作。算的上是c语言的 hello world 例程一样。

linux的设备有分几个类别,这里led一般认作是字符形设备。在linux中访问设备就是访问设备文件,所以linux也作基于文件的操作系统

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>

#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

#define DEVICE_NAME "leds"

static int led_gpios[] = {
S5PV210_GPJ2(0),
S5PV210_GPJ2(1),
S5PV210_GPJ2(2),
S5PV210_GPJ2(3),
};

#define LED_NUM     ARRAY_SIZE(led_gpios)

static long smart210_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > LED_NUM) {
return -EINVAL;
}

gpio_set_value(led_gpios[arg], !cmd);
printk(DEVICE_NAME": %ld %d\n", arg, cmd);
break;

default:
return -EINVAL;
}

return 0;
}

static struct file_operations smart210_led_dev_fops = {
.owner          = THIS_MODULE,
.unlocked_ioctl = smart210_leds_ioctl,
};

static struct miscdevice smart210_led_dev = {
.minor          = MISC_DYNAMIC_MINOR,
.name           = DEVICE_NAME,
.fops           = &smart210_led_dev_fops,
};

static int __init smart210_led_dev_init(void) {
int ret;
int i;

for (i = 0; i < LED_NUM; i++) {
ret = gpio_request(led_gpios[i], "LED");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
led_gpios[i], ret);
return ret;
}

s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i], 1);
}

ret = misc_register(&smart210_led_dev);

printk(DEVICE_NAME"\tinitialized\n");

return ret;
}

static void __exit smart210_led_dev_exit(void) {
int i;

for (i = 0; i < LED_NUM; i++) {
gpio_free(led_gpios[i]);
}

misc_deregister(&smart210_led_dev);
}

module_init(smart210_led_dev_init);
module_exit(smart210_led_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");


将此编译进内核。

2.编写led测试程序。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.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 < 0 || led_no > 3) {
fprintf(stderr, "Usage: leds led_no 0|1\n");
exit(1);
}

fd = open("/dev/leds0", 0);
if (fd < 0) {
fd = open("/dev/leds", 0);
}
if (fd < 0) {
perror("open device leds");
exit(1);
}

ioctl(fd, on, led_no);
close(fd);

return 0;
}


如此,将led驱动直接编译进内核后,然后在busybox构建的文件系统中,调用led测试程序。结果发现可以按照预先的想法顺利控制led的亮暗。



int gpio_request(unsigned gpio, const char *label) ,成功返回0,否则负数

gpio则为你要申请的哪一个管脚,label则是为其取一个名字

自动创建节点的字符杂项设备misc_register

注册

int misc_register(struct miscdevice * misc);

释放

int misc_deregister(struct miscdevice *misc);

杂项设备的

struct miscdevice {

int minor;

const char *name;

const struct file_operations *fops;

struct list_head list;

struct device *parent;

struct device *this_device;

const char *nodename;

umode_t mode;

};

网上关于杂项字符设备与普通字符设备的区别描述如下:

杂项字符设备和一般字符设备的区别:

1.一般字符设备首先申请设备号。 但是杂项字符设备的主设备号为10次设备号通过结构体struct miscdevice中的minor来设置。

2.一般字符设备要创建设备文件。 但是杂项字符设备在注册时会自动创建。

3.一般字符设备要分配一个cdev(字符设备)。 但是杂项字符设备只要创建struct miscdevice结构即可。

4.一般字符设备需要初始化cdev(即给字符设备设置对应的操作函数集struct file_operation). 但是杂项字符设备在结构体truct miscdevice中定义。

5.一般字符设备使用注册函数 int cdev_add struct (cdev *p,devt_t dev, unsigned)(第一个参数为之前初始化的字符设备,第二个参数为设备号,第三个参数为要添加设备的个数) 而杂项字符设备使用int misc_register(struct miscdevice *misc)来注册

驱动调用的实质:

就是通过 设备文件找到与之对应设备号的设备,再通过设备初始化时绑定的操作函数对硬件进行控制的

上面例子中是将led看作一个杂项设备来实现的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  嵌入式开发 内核