历史上的今天
今天是:2025年07月22日(星期二)
2021年07月22日 | 嵌入式驱动学习之GPIO驱动
2021-07-22 来源:eefocus
开发环境
主机开发环境:ubuntu12.04
BootLoader:u-boot-1.1.6
kernel:linux-2.6.30.4
CPU:s3c2440
开发板:TQ2440
开发步骤
1、硬件分析
在天嵌科技提供的开发板中 4 个 LED 灯(TQ2440)分别使用了 S3C2440芯片的:GPB5、GPB6、GPB7 和 GPB8,下面列出来对应的原理图:

根据上图可以知道,当 CPU 的 GPB5 到 8 是低电平时,LED 灯亮;当为高电平时 LED 灯灭。只需要在驱动中实现对 GPB 口电平的控制就可以实现对灯进行开关操作。
2、编写LED驱动
以下为驱动源码:
#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 DEVICE_NAME "le2440-leds" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */ #define LED_MAJOR 231 /* 主设备号 */ /* 应用程序执行 ioctl(fd, cmd, arg)时的第 2 个参数 */ #define IOCTL_LED_ON 1 #define IOCTL_LED_OFF 0 /* 用来指定 LED 所用的 GPIO 引脚 */ static unsigned long led_table[] = { S3C2410_GPB5, S3C2410_GPB6, S3C2410_GPB7, S3C2410_GPB8, }; /* 用来指定 GPIO 引脚的功能:输出 */ static unsigned int led_cfg_table[] = { S3C2410_GPB5_OUTP, S3C2410_GPB6_OUTP, S3C2410_GPB7_OUTP, S3C2410_GPB8_OUTP, }; /* 应用程序对设备文件/dev/le2440-leds 执行 open(...)时, * 就会调用 le2440_leds_open 函数 */ static int le2440_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; } /* 应用程序对设备文件/dev/le2440-leds 执行 ioclt(...)时, * 就会调用 le2440_leds_ioctl 函数 */ static int le2440_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: // 设置指定引脚的输出电平为 0 s3c2410_gpio_setpin(led_table[arg], 0); return 0; case IOCTL_LED_OFF: // 设置指定引脚的输出电平为 1 s3c2410_gpio_setpin(led_table[arg], 1); return 0; default: return -EINVAL; } } /* 这个结构是字符设备驱动程序的核心 * 当应用程序操作设备文件时所调用的 open、read、write 等函数, * 最终会调用这个结构中指定的对应函数 */ static struct file_operations le2440_leds_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module 变量 */ .open = le2440_leds_open, .ioctl = le2440_leds_ioctl, }; static char __initdata banner[] ="TQ2440 le2240-ledsn"; static struct class *led_class; /* * 执行“insmod le2440_leds.ko”命令时就会调用这个函数 */ static int __init le2440_leds_init(void) { int ret; printk(banner); /* 注册字符设备驱动程序 * 参数为主设备号、设备名字、file_operations 结构; * 这样,主设备号就和具体的 file_operations 结构联系起来了, * 操作主设备为 LED_MAJOR 的设备文件时,就会调用 le2440_leds_fops 中的相关成员函数 * LED_MAJOR 可以设为 0,表示由内核自动分配主设备号 */ ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &le2440_leds_fops); if (ret < 0) { printk(DEVICE_NAME " can't register major numbern"); return ret; } //注册一个类,使 mdev 可以在"/dev/"目录下面建立设备节点 led_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(led_class)) { printk("Err: failed in le2440-leds class. n"); return -1; } //创建一个设备节点,节点名为 DEVICE_NAME device_create(led_class, NULL, MKDEV(LED_MAJOR, 0), NULL, DEVICE_NAME); printk(DEVICE_NAME " initializedn"); return 0; } /* * 执行”rmmod le2440_leds.ko”命令时就会调用这个函数 */ static void __exit le2440_leds_exit(void) { /* 卸载驱动程序 */ unregister_chrdev(LED_MAJOR, DEVICE_NAME); device_destroy(led_class, MKDEV(LED_MAJOR, 0)); //删掉设备节点 class_destroy(led_class); //注销类 } /* 这两行指定驱动程序的初始化函数和卸载函数 */ module_init(le2440_leds_init); module_exit(le2440_leds_exit); /* 描述驱动程序的一些信息,不是必须的 */ MODULE_DESCRIPTION("TQ2440 LED Driver"); // 一些描述信息 MODULE_LICENSE("GPL"); // 遵循的协议 Makefile如下: #Makefile ifeq ($(KERNELRELEASE),) KERNELDIR ?= /home/linux/sky/ker/linux-2.6.30.4/ #KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules* .PHONY: modules modules_install clean else obj-m := leds.o endif 将其编译成.ko文件拷贝到NFS文件系统即可。 3、编写app控制LED灯 #include #include #include #include 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 which-led 0|1n"); exit(1); } fd = open("/dev/le2440-leds", 0); if (fd < 0) { perror("open device leds"); exit(1); } ioctl(fd, on, (led_no-1)); close(fd); return 0; } Makefile如下: CROSS=arm-linux- all: leds leds:leds.c $(CROSS)gcc -o leds leds.c $(CROSS)strip leds clean: @rm -vf leds *.o *~ 编译成可执行文件拷贝到NFS文件系统即可 4、实验现象 加载.ko文件,执行app,便可以在开发板上控制任意一个LED灯的亮灭
编译过程中遇到一些问题,经过查找资料,解决方法如下:
上一篇:嵌入式驱动学习之按键驱动
史海拾趣
|
这是个疯狂的时代,在人们还在为校园安全隐患心有余悸,还在为校园安防建设煞费思量的时候,2010年5月16日晚上6时30分许,社会再次出现了一次恶性伤人事件:佛山南海官窑官西市场,一白衣男手提一把菜刀在市场内疯狂砍杀。六名女子被砍伤,其中两人 ...… 查看全部问答> |
|
我在网上看到其他的帖子,现在已经能够成功的再wince 5.0下弹出右键菜单(自己写的一个list control)。但是现在我发现,弹出的菜单,无法响应ON_COMMAND消息。我为弹出菜单添加了ON_COMMAND消息,但是执行的时候,我点击菜单项,发现程序根本没有响 ...… 查看全部问答> |
|
哪位大哥用过8口100base-fx光纤交换芯片,能不能推荐给小弟啊。谢谢啊,最好把相关的pdf下载地址之类的发给我吧。sky_lihongyu@163.com… 查看全部问答> |
|
1. 不能将dir结果存放到文件里. 如果存在文件里,那么当结果是很大的时候,需要花费不少时间. 2. ce不支持管道技术. 原先设计: 写好dir命令到bat文件(内容为: dir /o-D \\harddisk2\\data /s>windows\\listtemp.txt),然后利用ShellExecuteEx执 ...… 查看全部问答> |
|
大家好,有个问题想问一下: 1 WinCE5 C# 获得从 panel 获得绘图面的方法? 说明 : 精简版的compect Frame Work 不支持CreatGraphics 我使用Graphics.FromHdc()但是发生了内存溢出错误,应该是使用方法不正确,请指教! 我看有资料说使用WinAPI ...… 查看全部问答> |
|
请问怎么才能在KEIL中的变量窗口查看到变量值啊~ 冒似只有定义在MAIN()函数体里面的变量才能出现在变量查看窗口中 定义在MAIN函数外面的全局变量小弟就不知道怎么看了 请高人指点 谢谢了 … 查看全部问答> |
|
用的是V5 XC5VLX50的FPGA,我建了一个Synplify的工程,然后新建了一个identify instrument ,然后就调用Launch identify instrument ,就在打开它的过程中,出现了一个db_assert的错误提示,点确定 然后就什么都没了。。。。搞不定啊、 ...… 查看全部问答> |




