驱动之路-硬件访问及混杂设备LED驱动

 一、重要知识点

1.I/O端口和I/O内存

    寄存器   和常规内存的区别:寄存器和     RAM   主要不同在于寄存器有边际效果,读取某个地址时可能导致该地址的内容发生变化,比如说很多设备的中断状态寄存器只要一读取,便自动清0。所以硬件寄存器不能直接访问,而要通过I/O端口和I/O内存两种方式访问。

在硬件层,I/O内存区域和I/O端口区域没有概念上的区别:它们都是通过向地址总线和控制总线发生电平信号进行访问,再通过数据总线读写数据。

a.I/O端口:

一些     CPU       制造   厂在它们的芯片中使用单一的地址空间,而一些则为外设保留独立的地址空间,以便和内存区间分开来,这段独立与内存地址空间的地址空间就叫I/O端口。在/proc/ioport中可以看到。嵌入式处理器大部分不支持I/O端口。

访问I/O端口有两步:1.申请I/O端口2.读写I/O端口

申请I/O端口:

structresource *request_region(unsigned long fi     rs   t, unsigned long n, const char *name)

申请从first开始的n个端口。参数name为设备名称。如果分配成功则返回非NULL值。

释放I/O端口:

voidrelease_region(unsigned long start, unsigned long n)

读写I/O端口:

读写一个字节

unsignedinb(unsigned port)

voidoutb(usigned char by     te   , unsigned port)

读写二个字节

unsignedinb(unsigned port)

voidoutb(usigned short byte, unsigned port)

读写四个字节

unsignedinb(unsigned port)

voidoutb(usigned long byte, unsigned port)

b.I/O内存

通过将外设寄存器映射到内存空间来进行访问叫做I/O内存,嵌入式大多只支持这种操作。

访问I/O内存有三步:1.申请I/O内存区域2.映射I/O内存区域3.读写I/O内存

申请I/O内存区域

structresource *request_mem_region(unsigned long start, unsigned long len, char *name)

申请访问从start(I/O物理地址)开始的len长度的I/O内存区域,如成功则返回非NULL值。在/proc/iomem中可可以查看到已经被申请的I/O内存区域。在后面的我写的驱动程序中并没用使用申请这一步,是因为我使用的G     PI   O内存区域已经被申请,如果在申请会导致失败。但是这样做法是不安全的做法,因为同一I/O内存区域域被多个模块使用。

释放I/O内存区域

voidrelease_mem_region(unsigned long start, unsigned long len)

映射I/O内存区域

void*ioremap(unsigned long phy_addr, unsigned long size)

映射从物理地址phy_addr开始的size长度的的地址空间。返回可以访问I/O内存地址。由ioreamp返回的地址不应该直接引用,必须通过下面一些列的读写函数完成。

读写操作I/O内存

读1、2、4个字节:

unsignedint read8(void *addr);

unsignedint read16(void *addr)

unsignedint read32(void *addr)

写读1、2、4个字节:

voidiowrite8(u8 value, void *addr)

voidiowrite16(u8 value, void *addr)

voidiowrite32(u8 value, void *addr)

2.混杂设备驱动

    Linux   系统中,存在一类     字符   设备,他们共享一个主设备号(10),但此设备号不同,我们称这类设备为混杂设备(     mi   scdeivce),查看/proc/device中可以看到一个名为misc的主设备号为10。所有的混杂设备形成一个链表,对设备访问时内存根据次设备号找到对应的miscdevice设备。

Linux内核使用structmiscdeivce来描述一个混杂设备

structmiscdevice{
int minor;

conststruct file_oprea     ti   ons *fops;

structlist_head list;

structdevice *parent;

structdevice *this_device;

}

使用时只需填写minor次设备号,*name设备名,*fops文件操作函数集即可。

Linux内核使用misc_register函数注册一个混杂设备。注册成功后,linux内核为自动为该设备创建设备文件。

intmisc_register(struct miscdevice *misc)

二、驱动代码

1.驱动代码一

这段LED驱动代码采用手动I/O内存映射的方式访问。没有使用申请内存区域函数,这样使不安全的。直接访问I/O内存地址而不是通过读写函数访问也是不安全。同时驱动代码包含硬件相关代码也是移植性不好的。写这段代码是为了帮助理解I/O内存映射的过程。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

vola  TI le unsigned int long *gpb_con = NULL;

vola  TI le unsigned int long *gpb_data = NULL;

sta  TI c int leds_ioctl(struct inode *inode, struct file *file,

unsigned int cmd, unsigned long arg)

{

if((cmd>1) |(arg>3))

return-EINVAL;

switch(cmd)

{

case 0:

*gpb_data&= ~(1<<arg+5);  < p="">

break;

case 1:

*gpb_data|= (1<<arg+5);  < p="">

break;

default:

return-EINVAL;

}

return 0;

}

sta  TI c const struct file_operations leds_fops = {

.owner = THIS_MODULE,

.ioctl = leds_ioctl,

};

static struct miscdevice misc = {

.minor =MISC_DYNAMIC_MINOR,

.name ="my_leds",

.fops =&leds_fops,

};

static int __init leds_init(void)

{

int ret;

//注册混杂设备

ret =misc_register(&misc);

//映射I/O内存

gpb_con = (volatileunsigned long *)ioremap(0x56000010, 16); //0x56000010为GPIOB控制寄存器的物理地址

gpb_data = gpb_con+1;

//配置LED对应的GPIOB 5、6、7、8口为输出并初始化为1,LED灭

*gpb_con |=(1<<5*2)|(1<<6*2)|(1<<7*2)|(1<<8*2);

*gpb_data |=(1<<5) | (1<<6) | (1<<7) | (1<<8);

printk("le     dsi   nit.\n");

return ret;

}

static void leds_exit(void)

{

misc_deregister(&misc);

printk("leds_exit\n");

}

module_init(leds_init);

module_exit(leds_exit);

MODULE_AUTHOR("Y-Kee");

MODULE_LICENSE("GPL");

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include volatile unsigned int long *gpb_con = NULL;volatile unsigned int long *gpb_data = NULL;static int leds_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg){if((cmd>1) |(arg>3))return-EINVAL;switch(cmd){case 0:*gpb_data&= ~(1<

2.驱动代码二:

采用内核定义好的GPIO接口(S3C2410_GPB5和S3C2410_GPB5_OUTP)和GPIO操作函数(s3c2410_gpio_setpin和s3c2410_gpio_cfgpin)。可移植性好,也是正确的做法。内核的GPIO操作函数也是通过一些的运算将GPIO接口换算成虚拟内存地址然后进行访问的。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

static unsigned long led_table[] =   {

S3C2410_GPB5,

S3C2410_GPB6,

S3C2410_GPB7,

S3C2410_GPB8,

};

static unsigned long led_cfg_table[] =    {

S3C2410_GPB5_OUTP,

S3C2410_GPB6_OUTP,

S3C2410_GPB7_OUTP,

S3C2410_GPB8_OUTP,

};

static int leds_ioctl(struct inode *inode, struct file *file,

unsigned int cmd, unsigned long arg)

{

if((cmd>1) |(arg>3))

return-EINVAL;

switch(cmd)

{

case 0:

s3c2410_gpio_setpin(led_table[arg],0);

break;

case 1:

s3c2410_gpio_setpin(led_table[arg],1);

break;

default:

return-EINVAL;

}

return 0;

}

static const struct file_operations leds_fops = {

.owner = THIS_MODULE,

.ioctl = leds_ioctl,

};

static struct miscdevice misc = {

.minor =MISC_DYNAMIC_MINOR,

.name ="my_leds",

.fops =&leds_fops,

};

static int __init leds_init(void)

{

int ret, i;

//注册混杂设备

ret =misc_register(&misc);

//配置LED对应的GPIOB 5、6、7、8口为输出并初始化为1,LED灭

for(i=0; i<4; i++)

{

s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);

s3c2410_gpio_setpin(led_table[i],1);

}

printk("ledsinit.\n");

return ret;

}

static void leds_exit(void)

{

misc_deregister(&misc);

printk("leds_exit\n");

}

module_init(leds_init);

module_exit(leds_exit);

MODULE_AUTHOR("Y-Kee");

MODULE_LICENSE("GPL");

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static unsigned long led_table[] = {S3C2410_GPB5,S3C2410_GPB6,S3C2410_GPB7,S3C2410_GPB8,};static unsigned long led_cfg_table[] = {S3C2410_GPB5_OUTP,S3C2410_GPB6_OUTP,S3C2410_GPB7_OUTP,S3C2410_GPB8_OUTP,};static int leds_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg){if((cmd>1) |(arg>3))return-EINVAL;switch(cmd){case 0:s3c2410_gpio_setpin(led_table[arg],0);break;case 1:s3c2410_gpio_setpin(led_table[arg],1);break;default:return-EINVAL;}return 0;}static const struct file_operations leds_fops = {.owner = THIS_MODULE,.ioctl = leds_ioctl,};static struct miscdevice misc = {.minor =MISC_DYNAMIC_MINOR,.name ="my_leds",.fops =&leds_fops,};static int __init leds_init(void){int ret, i;//注册混杂设备ret =misc_register(&misc);//配置LED对应的GPIOB 5、6、7、8口为输出并初始化为1,LED灭for(i=0; i<4; i++){s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);s3c2410_gpio_setpin(led_table[i],1);}printk("ledsinit.\n");return ret;}static void leds_exit(void){misc_deregister(&misc);printk("leds_exit\n");}module_init(leds_init);module_exit(leds_exit);MODULE_AUTHOR("Y-Kee");MODULE_LICENSE("GPL");



驱动之路_硬件访问及混杂设备LED驱动_设计制作_可编程逻辑
12
156
0
93

相关资讯

  1. 1、互联网从“骑士时代”走进“智者时代”125
  2. 2、筷搜真的来了,智能硬件空间有多大?192
  3. 3、B2B增长引擎在哪里?4988
  4. 4、看家庭O2O的逐鹿:齐家而后治国平天下1327
  5. 5、移动互联网的下半场,是场景、流量和产业的重构4373
  6. 6、资深投资人全力反击:VC增值平台从来就不是一坨狗屎978
  7. 7、张小龙微信公开课PRO演讲:信息互联的7个思考2866
  8. 8、聊聊我们在产品路上踩过的“坑”2611
  9. 9、版权之争告一段落,但在线音乐仍是一场持久战606
  10. 10、十大“羊”成语之羊年新解,道出互联网新趋势1956
全部评论(0)
我也有话说
0
收藏
点赞
顶部