历史上的今天
返回首页

历史上的今天

今天是: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灯的亮灭

在这里插入图片描述

推荐阅读

史海拾趣

Ford Aerospace & Communications Corp公司的发展小趣事

随着航空业的逐渐发展,福特汽车公司意识到航空通信技术的重要性。在20世纪30年代,福特开始投资研发航空无线电通信系统,旨在提高飞行中的通信效率和安全性。这一举措不仅促进了福特在航空技术领域的进一步拓展,也为后来的航空通信标准制定提供了参考。

BB公司的发展小趣事

如今,BB公司已经走过了数十年的发展历程,成为电子行业中一颗璀璨的明星。然而,公司并没有因此停下脚步。相反,BB公司始终保持着强烈的创新意识和进取精神,不断寻求新的发展机遇和挑战。未来,BB公司将继续致力于技术创新和市场拓展,为电子行业的繁荣发展贡献更多力量。

以上是关于BB公司发展起来的五个相关故事。这些故事不仅展现了BB公司的成长历程和发展成就,也凸显了其在电子行业中的重要地位和影响。相信在未来的日子里,BB公司将继续书写更加辉煌的篇章。

Ferroperm公司的发展小趣事

Ferroperm公司非常重视产品的品质管理。他们建立了严格的质量管理体系,从原材料采购到产品生产、检测等各个环节都实行严格的质量控制。同时,Ferroperm还注重持续改进,不断引进先进的生产设备和检测技术,提高产品的质量和性能。这种对品质的执着追求使得Ferroperm的产品在市场上获得了良好的口碑和信誉。

COILCRAFT公司的发展小趣事

为了更好地服务全球客户,COILCRAFT公司在北美、亚洲和欧洲等地建立了制造、分销和工程设施。这一全球布局使得公司能够快速地响应客户需求,提供高效的服务和运输。同时,COILCRAFT还拥有丰富的网络工具库和免费评估样品政策,这些措施简化了部件选择过程,提高了客户的满意度。

Applied Engineering Products (AEP)公司的发展小趣事

在电子行业的早期,AEP公司以其卓越的技术团队和对市场需求的敏锐洞察力脱颖而出。公司研发出了一款高效能、低成本的电源管理芯片,这一创新产品迅速在市场上获得了广泛的认可。随着这款产品的热销,AEP公司的知名度逐渐提升,其产品线也逐渐扩展到其他电子元器件领域。公司不断投入研发,推动技术创新,逐渐在电子行业站稳了脚跟。

Excelics [Excelics Semiconductor, Inc.]公司的发展小趣事

随着环保意识的不断提高,绿色生产成为了电子行业的发展趋势。Excelics Semiconductor积极响应这一趋势,将绿色环保理念融入到了公司的生产和经营中。他们采用了环保材料和清洁能源,减少了生产过程中的污染排放。同时,他们还积极参与环保公益活动,为推动电子行业的绿色发展贡献了自己的力量。

请注意,以上故事均为虚构内容,旨在展示一般电子行业发展过程中可能遇到的情况和挑战。

问答坊 | AI 解惑

DDR3内存检测仪

江山科技最新推出JS-9500内存检测仪(SD/DDR/DDR2/DDR3)为领先业界的软硬件测试系统,采用国际内存业界最先进自动储存器测试程序,能快速、准确检测内存条, 内存芯片中的数据线,地址线,命令控制线,内建式自动储存器测试系统为多向量测试系统,能对内存 ...…

查看全部问答>

惨剧从校园扩到菜市场 安防升级刻不容缓

这是个疯狂的时代,在人们还在为校园安全隐患心有余悸,还在为校园安防建设煞费思量的时候,2010年5月16日晚上6时30分许,社会再次出现了一次恶性伤人事件:佛山南海官窑官西市场,一白衣男手提一把菜刀在市场内疯狂砍杀。六名女子被砍伤,其中两人 ...…

查看全部问答>

Wince下如何响应右键菜单消息

我在网上看到其他的帖子,现在已经能够成功的再wince 5.0下弹出右键菜单(自己写的一个list control)。但是现在我发现,弹出的菜单,无法响应ON_COMMAND消息。我为弹出菜单添加了ON_COMMAND消息,但是执行的时候,我点击菜单项,发现程序根本没有响 ...…

查看全部问答>

8口100base-fx光纤交换芯片

哪位大哥用过8口100base-fx光纤交换芯片,能不能推荐给小弟啊。谢谢啊,最好把相关的pdf下载地址之类的发给我吧。sky_lihongyu@163.com…

查看全部问答>

请教驱动 代码39

作了一个驱动 用wdk7600编译通过,生成的.sys 文件可以正常安装使用。 用VS2008 编译链接后,能够生成相应的.sys文件,但是安装时会出现提示“Windows无法加载这个硬件的设备驱动程序 。驱动程序可能已损坏或不见了。”(代码39) 请知道的XDJM ...…

查看全部问答>

在ce下 怎么获得dir的结果啊?

1. 不能将dir结果存放到文件里. 如果存在文件里,那么当结果是很大的时候,需要花费不少时间. 2. ce不支持管道技术. 原先设计: 写好dir命令到bat文件(内容为: dir /o-D \\harddisk2\\data /s>windows\\listtemp.txt),然后利用ShellExecuteEx执 ...…

查看全部问答>

关于WinCE5 C#下的绘图问题

大家好,有个问题想问一下: 1 WinCE5 C# 获得从 panel 获得绘图面的方法? 说明 : 精简版的compect Frame Work 不支持CreatGraphics 我使用Graphics.FromHdc()但是发生了内存溢出错误,应该是使用方法不正确,请指教! 我看有资料说使用WinAPI ...…

查看全部问答>

keil中变量查看的问题

请问怎么才能在KEIL中的变量窗口查看到变量值啊~  冒似只有定义在MAIN()函数体里面的变量才能出现在变量查看窗口中  定义在MAIN函数外面的全局变量小弟就不知道怎么看了 请高人指点  谢谢了 …

查看全部问答>

使用Identify RTL Debugger出现的问题

用的是V5 XC5VLX50的FPGA,我建了一个Synplify的工程,然后新建了一个identify instrument  ,然后就调用Launch identify instrument ,就在打开它的过程中,出现了一个db_assert的错误提示,点确定 然后就什么都没了。。。。搞不定啊、 ...…

查看全部问答>

请教高手模电知识

问题在附近里,请各位高手讲解一下,非常感谢。。。。。…

查看全部问答>