【Linux驱动】TQ2440 LED驱动程序
★整体介绍
LED驱动程序主要实现了TQ2440开发板上的4个LED灯的硬件驱动,实现了对引脚GPIOB5、GPIOB6、GPIOB7、GPIOB8的高低电平设置(common-smdk.c中已经实现了对引脚的配置),利用測试程序调用该驱动程序,通过命令控制LED灯的亮灭。
★具体介绍
1、驱动程序代码:My_led.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/device.h> #define DEVICE_NAME "My_led" /**载入模块后运行cat/proc/devices中看到的设备名称**/
#define Led_MAJOR 103 /**主设备号**/
#define LED_ON 1
#define LED_OFF 0 /**Led的控制引脚**/
static unsigned long led_table[ ] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
}; static int My_led_open(struct inode *inode,struct file *file)
{
printk("My_led open\n");
return 0;
} static int My_led_ioctl(struct inode * inode, struct file * file,unsigned int cmd,unsigned long arg)
{
if(arg > 4)
{
return -1;
}
switch(cmd)
{
case LED_ON:
s3c2410_gpio_setpin(led_table[arg], 0);//设置指定引脚为输出电平为0
return 0;
case LED_OFF:
s3c2410_gpio_setpin(led_table[arg], 1);//设置指定引脚为输出电平为1
return 0;
default:
return -1;
}
} static struct file_operations My_led_fops =
{
.owner = THIS_MODULE,
.open = My_led_open,
.ioctl = My_led_ioctl,
}; static struct class *led_class; static int __init My_led_init(void)
{
int ret;
printk("My_led start\n"); /**注冊字符设备驱动程序**/
/**參数为主设备号、设备名字、file_operations结构**/
/**这样主设备号就与file_operations联系起来**/
ret = register_chrdev(Led_MAJOR, DEVICE_NAME, &My_led_fops);
if(ret < 0)
{
printk("can't register major number\n");
return ret;
} //注冊一个类。使mdev能够在"/dev/文件夹下建立设备节点"
led_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(led_class))
{
printk("failed in My_led class.\n");
return -1;
}
device_create(led_class, NULL, MKDEV(Led_MAJOR,0), NULL, DEVICE_NAME);
printk(DEVICE_NAME "initialized\n");
return 0; } static void __exit My_led_exit(void)
{
unregister_chrdev(Led_MAJOR, DEVICE_NAME);
device_destroy(led_class, MKDEV(Led_MAJOR,0));//注销设备节点
class_destroy(led_class);//注销类
} module_init(My_led_init);
module_exit(My_led_exit); MODULE_LICENSE("GPL");
2、宏定义
#define DEVICE_NAME "My_led" //载入模块后运行cat/proc/devices中看到的设备名称
#define Led_MAJOR 103 //主设备号
#define LED_ON 1
#define LED_OFF 0 My_led_ioctl函数中要输入的參数命令,LED_ON会运行打开灯的命令、LED_OFF运行关闭灯的命令
3、Led灯的引脚控制
定义一个led_table[ ] 数组。在后面调用时要方便些。
具体说明一下S3C2410_GPB5是什么意思:
如上图所看到的。是关于S3C2410_GPB5相关的全部代码。
通过上面的代码能够得出S3C2410_GPB5为37。实际上S3C2410_GPB5就是port的编号。bank是分组的基号码,offset是组内的偏移量。
这样算出来的S3c2410_GPB5 的值为37,他的作用是代表S3c2440中B组port的第五个引脚。
4、My_led_ioctl()函数
此函数主要负责响应应用程序的相关命令。然后依据命令运行相关的代码。cmd命令包含宏定义的LED_ON、LED_OFF。
s3c2410_gpio_setpin()函数
自觉得这是本驱动程序最重要的一个函数,它的作用是:将指定的引脚设置为低电平或者高电平。
本驱动程序中仅仅是用了内核提供的这一个接口来实现对引脚的控制。如今来分析一下这个函数。
◇函数的作用
这个函数是设置s3c2440中引脚的高低电平的。
它的第一个參数是linux系统中关于引脚定义的一些宏。比如s3c2410_GPB5、s3c2410_GPB6等代表芯片的port引脚;还有一个參数就是一个值to。这个值代表0或者1。
◇此函数是怎样设置高低电平的
首先贴出此函数的定义在内核版本号Linux2.6.30.4/arch/arm/plat-s3c24xx/gpio.c中
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C24XX_GPIO_BASE(pin);
unsigned long offs = S3C2410_GPIO_OFFSET(pin);
unsigned long flags;
unsigned long dat; local_irq_save(flags); dat = __raw_readl(base + 0x04);
dat &= ~(1 << offs);
dat |= to << offs;
__raw_writel(dat, base + 0x04); local_irq_restore(flags);
}
先来分析第一个函数S3C24XX_GPIO_BASE(pin)
5、注冊一个类
假设不注冊这个类,那么当在开发板上挂载驱动之后,还要做的一件事是手动“生成设备节点”。就是利用mknod命令。可是驱动程序都是随操作系统的执行而開始工作的,因此生成设备节点要让它自己主动生成。而并不是手动。所以才会注冊这样一个类,在挂载驱动之后,自己主动将设备节点生成。注冊类的核心代码:
led_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(led_class))
{
printk("failed in My_led class.\n");
return -1;
}
device_create(led_class, NULL, MKDEV(Led_MAJOR,0), NULL, DEVICE_NAME);
6、測试程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int main()
{
int fd,i,cmd;
fd=open("/dev/My_led",0);
if (fd<0)
{
printf("open led_driver error");
exit(1);
}
while(1)
{
scanf("%d",&cmd);
switch(cmd)
{
case 0:
printf("All off\n");
for(i = 0;i < 4;i ++)
ioctl(fd,0,i);
break;
case 1:
printf("light first led\n");
ioctl(fd,1,0);
break;
case 2:
printf("light second led\n");
ioctl(fd,0,0);
ioctl(fd,1,1);
break;
case 3:
printf("light third led\n");
ioctl(fd,0,1);
ioctl(fd,1,2);
break;
case 4:
printf("light fourth led\n");
ioctl(fd,0,2);
ioctl(fd,1,3);
break;
case 5:
printf("All light \n");
for(i = 0;i < 4;i ++)
ioctl(fd,1,i);
break;
default:
i = 10;
break; }
if (i == 10)
break;
} return 0;
}
★遇到的错误
1、找不到文件
这个错误的主要原因非常可能是内核的版本号不同。不同内核的头文件存放的路径不同样。因此,当编译驱动程序报错“找不到文件”时,检查头文件的路径是否正确
2、Makefile文件指定交叉编译器
以下是驱动程序的Makefile文件
#LED_makefile KERNELDIR := /home/xg/linux_arm/linux-2.6.30.4/
PWD :=$(shell pwd) all:
make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=/opt/opt/EmbedSky/4.3.3/bin/arm-linux-
test:
/opt/opt/EmbedSky/4.3.3/bin/arm-linux-gcc -o test test.c
clean:
rm -rf *.o *ko obj-m :=My_led.o
当时出的错误就是编译器的路径没有搞清楚。
【Linux驱动】TQ2440 LED驱动程序的更多相关文章
- linux驱动之LED驱动
通过之前的学习,了解到linux驱动编写的流程是:先通过注册函数注册我们编写的入口函数,然后在入口函数中获取设备号->注册字符设备->自动创建设备节点->获取设备树信息,最后通过销毁 ...
- 1.7见识一下什么叫Linux驱动:LED
1.任何的Linux驱动都有一个装载函数(装载驱动时调用)和一个卸载函数(卸载驱动时调用): 2.装载函数和卸载函数分别通过module_init和module_exit宏指定.
- Linux驱动之LED驱动编写
从上到下,一个软件系统可以分为:应用程序.操作系统(内核).驱动程序.结构图如下:我们需要做的就是写出open.read.write等驱动层的函数.一个LED驱动的步骤如下: 1.查看原理图,确定需要 ...
- 【Linux 驱动】设备驱动程序再理解
学习设备驱动编程也有一段时间了,也写过了几个驱动程序,因此有对设备驱动程序有了一些新的理解和认识,总结一下.学习设备驱动编程也有一段时间了,也写过了几个驱动程序.因此有对设备驱动程序有了一些新的理解和 ...
- Linux下的led驱动程序,ok6410
本程序採用动态映射的方法控制led.硬件平台为飞凌的ok6410 led.h:定义控制命令 #ifndef _LED_H #define _LED_H #define LED_MAGIC 'M' #d ...
- linux驱动之USB驱动程序
1. USB是主从结构的 所有的USB传输,都是从USB主机这方发起:USB设备没有"主动"通知USB主机的能力. 例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来 ...
- linux驱动之触摸屏驱动程序
触摸屏归纳为输入子系统,这里主要是针对电阻屏,其使用过程如下 :当用触摸笔按下时,产生中断.在中断处理函数处理函数中启动ADC转换x,y坐标.ADC结束,产生ADC中断,在ADC中断处理函数里上报(i ...
- linux驱动之LED驱动_1
步骤: 1.框架 2.完好硬件的操作: a.看原理图.引脚 b.看2440手冊 c.写代码: IO口须要用ioremap映射 我的板子电路例如以下所看到的 1.配置GPBCON 寄存器,配置输出 ...
- Linux驱动之按键驱动编写(中断方式)
在Linux驱动之按键驱动编写(查询方式)已经写了一个查询方式的按键驱动,但是查询方式太占用CPU,接下来利用中断方式编写一个驱动程序,使得CPU占有率降低,在按键空闲时调用read系统调用的进程可以 ...
随机推荐
- POJ 2442 Squence (STL heap)
题意: 给你n*m的矩阵,然后每行取一个元素,组成一个包含n个元素的序列,一共有n^m种序列, 让你求出序列和最小的前n个序列的序列和. 解题思路: 1.将第一序列读入seq1向量中,并按升序排序. ...
- chfn,chsh,last,login,mail ,mesg ,talk,wall,write,nice ,pstree ,renice,skill ,expr ,reset,tset,compress ,lpd ,lpq ,lpr ,lprm,fdformat ,mformat ,mkdosf
名称:chfn 使用权限:所有使用者 用法:shell>> chfn 说明:提供使用者更改个人资讯,用于finger and mail username 范例: shell>> ...
- Android应用开发学习笔记之播放视频
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 在Android中,我们可以通过使用VideoView类或者MediaPlayer类来播放视频. 一.使用Video ...
- 8086 CPU 寻址方式
8086 CPU 寻址方式灵活.有以下几种 idata 表示常量 1. [ idata ] 用一个常量来表示地址,可用于直接定位内存单元,但是在 MASM中要显实在的说明 ds 段寄存器, 比如 ...
- c#调用语音功能
转自 http://www.cnblogs.com/Hans2Rose/p/WeatherSpeaker.html .Net里面自带了一个语音类库:System.Speech,调用系统的语音功能,就能 ...
- 14.5.1 Resizing the InnoDB System Tablespace
14.5.1 Resizing the InnoDB System Tablespace 本节描述如何增加或者减少InnoDB 系统表空间的大小 增加InnoDB 系统表空间的大小 最简单的方式增加I ...
- [置顶] Cocos2d-x 实例源码分析之二 小实例的主框架
这篇文章是分析第一个小实例ActionTest的源码.其实所有实例程序的结构都是一样的,只有特定方法里的代码不同,大的框架都是一样的.也就是说看完这篇文章你就可以自己开始分析其他源码了. 废话不多说, ...
- linux 软连接 硬连接
1.Linux链接概念 Linux链接分两种,一种被称为硬链接(Hard Link),另一种被称为符号链接(Symbolic Link).默认情况下,ln命令产生硬链接. [硬连接] 硬连接指通过索引 ...
- PAT 1055
题目链接:https://www.patest.cn/contests/pat-b-practise/1055 分析:思路很巧妙,感觉很有意义的字符串题目 #include<bits/stdc+ ...
- LAN路由
一.实验的目的: 实现不同子网之前的信息交流 二.如果 1.虚拟子网 VMnet8:192.168.233.0/24 VMnet1:172.16.1.0/24 2.虚拟机vm1 ip:1 ...