Linux设备驱动之字符设备驱动

学习链接:https://xuesong.blog.csdn.net/article/details/109522945?spm=1001.2014.3001.5502

Linux 字符设备是Linux 三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备, 本文主要记录字符设备驱动相关知识

一、应用程序和驱动的交互原理

1、驱动就是获取外设、或者传感器数据,控制外设。数据会提交给应用程序。Linux驱动编译既要编写一个驱动,还要我们编写一个简单的测试应用程序,APP。单片机下驱动和应用都是放到一个文件里面,也就是杂糅到一起。Linux下驱动和应用是完全分开的。

       用户空间(用户态)和内核空间(内核态):

       Linux操作系统内核和驱动程序运行在内核空间、应用程序运行在用户空间。

       应用程序想要访问内核资源,怎么办,有三种方法:系统调用、异常(中断)和陷入。

       应用程序不会直接调用系统调用,而是通过API函数来间接的调用系统调用,比如POSIX、API和C库等。unix类操作系统中最常用的编程接口就是POSIX。

       应用 程序使用open函数 打开一个设备文件。

       每个系统调用都有一个系统调用号。

       系统调用处于内核空间,应用程序无法直接访问,因此需要“陷入“到内核,方法就是软中断。陷入内核以后还要指定系统调用号。

二、字符设备驱动开发流程

字符设备驱动

应用层:  APP1  APP2  ...

fd = open("led驱动的文件",O_RDWR);

read(fd);

write();

close();

-------------------------------------------------------------------------------------------------------------------------

内核层:  

对灯写一个驱动

 led_driver.c

 driver_open();

 driver_read();

 driver_write();

 driver_close();

struct file_operations {

int (*open) (struct inode *, struct file *);   

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);       

int (*release) (struct inode *, struct file *);(close)

}

cdev:

设备号1        设备号2             设备号n

设备驱动1     设备驱动2    ....    设备驱动n

设备号:32位,无符号数字

高12位   :主设备号 :区分哪一类设备

低20位   :次设备号 :区分同类中哪一个设备

-------------------------------------------------------------------------------------------------------------------------

硬件层:  LED   uart  ADC  PWM

每个驱动里面都有对应的file_operations

open的过程:

open打开文件,这个文件与底层的驱动的设备号有关系,

通过设备号访问设备驱动中的struct file_operations里面的open函数。

read的过程:

open函数会有一个返回值,文件描述符fd,read函数通过fd

找到驱动中的struct file_operations里面的read函数。

字符设备驱动的注册

int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

功能:注册一个字符设备驱动

参数:

@major:主设备号  

  :如果你填写的值大于0,它认为这个就是主设备号

  :如果你填写的值为0,操作系统给你分配一个主设备号

@name :名字 cat /proc/devices 

当注册一个字符设备驱动的时候。

如果成功的话,当你使用cat /proc/devices 命令查看的时候可以看到系统自动分配的主设备号和这个名字

@fops :操作方法结构体

返回值:major>0 ,成功返回0,失败返回错误码(负数) vi -t EIO

major=0,成功主设备号,失败返回错误码(负数)   

手动创建设备文件

sudo mknod hello (路径是任意) c/b  主设备号   次设备号

sudo –rf hello 删除的时候记得加-rf

 

 字符设备驱动应用--点亮LED灯

应用程序将数据传递给驱动

int copy_from_user(void *to, const void __user *from, int n)

功能:从用户空间拷贝数据到内核空间(用户需要写数据的时候)

参数:

@to  :内核中内存的首地址

@from:用户空间的首地址

@n   :拷贝数据的长度(字节)

返回值:成功返回0,失败返回未拷贝的字节的个数

int copy_to_user(void __user *to, const void *from, int n)

功能:从内核空间拷贝数据到用户空间(用户开始读数据)

参数:

@to  :用户空间内存的首地址

@from:内核空间的首地址

@n   :拷贝数据的长度(字节)

返回值:成功返回0,失败返回未拷贝的字节的个数

驱动操作寄存器

rgb_led灯的寄存器是物理地址,在linux内核启动之后,在使用地址的时候,操作的全是虚拟地址。需要将物理地址转化为虚拟地址。在驱动代码中操作的虚拟地址就相当于操作实际的物理地址。

物理地址<------>虚拟地址

void * ioremap(phys_addr_t offset, unsigned long size)

(当__iomen告诉编译器,取的时候是一个字节大小)

功能:将物理地址映射成虚拟地址

参数:

@offset :要映射的物理的首地址

@size   :大小(字节)(映射是以业为单位,一页为4K,就是当你小于4k的时候映射的区域都为4k)

返回值:成功返回虚拟地址,失败返回NULL((void *)0); 

void iounmap(void  *addr)

功能:取消映射

参数:

@addr :虚拟地址

返回值:无

 软件编程控制硬件

只需要向控制寄存器中写值或者读值,就可以让我们处理器完成一定的功能。

RGB_led 

1》GPIOxOUT:控制引脚输出高低电平

RED_LED--->GPIOA28

GPIOAOUT --->  0xC001A000

GPIOA28输出高电平:

GPIOAOUT[28]  <--写--  1

GPIOA28输出低电平:

GPIOAOUT[28]  <--写--  0

2》GPIOxOUTENB:控制引脚的输入输出模式

GPIOAOUTENB  ---> 0xC001A004

设置GPIOA28引脚为输出模式:

GPIOAOUTENB[28] <--写-- 1

3》GPIOxALTFN:控制引脚功能的选择

GPIOAALTFN1  ---> 0xC001A024

设置GPIOA28引脚为GPIO功能:

GPIOAALTFN1[25:24]  <--写-- 0b00

00 = ALT Function0   

01 = ALT Function1 

10 = ALT Function2   

11 = ALT Function3 

GPIO引脚功能的选择:每两位控制一个GPIO引脚

red  :gpioa28

GPIOXOUT   :控制高低电平的   0xC001A000

GPIOxOUTENB:输入输出模式    0xC001A004

GPIOxALTFN1:function寄存器  0xC001A024

(一个寄存器36个字节)

green:gpioe13

0xC001e000

blue :gpiob12

0xC001b000

Linux里面一切皆文件,驱动设备表现就是一个/dev/下的文件,/dev/led。应用程序调用open函数打开设备,比如led。应用程序通过write函数向/dev/led写数据,比如写1表示打开,写0表示关闭。如果要关闭设备那么就是close函数。

编写驱动的 时候也需要编写驱动对应的open、close,write函数。字符设备驱动fileoptions_struct.

驱动最终是被应用调用的,在写驱动的时候要考虑应用开发的便利性。

驱动是分驱动框架的,要按照驱动框架来编写,对于字符设备驱动来说,重点编写应用程序对应的open、close、read、write等函数。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值