linux驱动框架和驱动代码

一.linux文件系统:

        1.什么是文件系统
              文件系统是操作系统用于明确存储设备组织文件的方法。 以上说的方法:就是文件管理系统(程序),简称文件系统    

          文件系统(文件管理系统的方法)的种类有哪些?
                FAT  VFAT  NTFS  EXT1/2/3/4  HFS ....
             
  树莓派查看文件系统的命令: df -T
                vfat  :  boot(bootloader, kernel)
                ext4  :  根目录
                tmpfs : 内存文件系统

    2. 什么是分区?
                windows:
随意(面向普通用户PC),目录即分区(比如c

      盘d盘)  C(装系统的位置)也可以随意在C盘存放文件.    D盘(用户随意发挥)

                 Linux: 按照功能来分区,每个分区严格存放文件(开发者)   
                    嵌入式系统可以分为4个区,分别是
                    bootloader:  启动代码
                    para:        启动代码向内核传递参数的位置
                    kerne:         内核分区
                   根分区等      文件系统结构

    3. 什么是文件系统目录结构?
        
    常规认知:从根目录开始的一个树状结构按功能划分,和windows不同,不是分区,和windows不同

    4. 什么虚拟文件系统Virtual File System ?

             Linux中一切东西都存放在唯一的虚拟文件系统(VSF)中这个虚拟文件系统是树状结构从一个根目录(boot)开始,每次系统启动都会在内存中自动生成一个虚拟文件系统。 vfs就是对各种文件系统的一个抽象,它为各种文件系统提供了一个通用的接口。
    
    5. 虚拟文件系统有什么作用?
               
简化应用程序员的开发不管是什么文件类型,不管文件是磁盘还是设备,都只用open read write统一操作。程序员在编程时要打开不同文件系统分区的不同文件时通过VSF用统一的方式。

二.linux内核结构:

三.Linux应用程序控制硬件的过程:

        应用程序通过函数库提供的的接口(open,read等)通过一个软中断从用户态进入内核态,并触发系统调用接口(sys_call),系统调用接口通过虚拟文件系统(VSF)用文件名,设备号在驱动链表中找到相应的驱动设备,通过驱动设备控制硬件。

      问:c库中的open,read等接口如何找到内核中的驱动:

          open\rightarrow软中断触发系统调用(sys_call)\rightarrowVFS找到sys_open\rightarrow通过设备名设备号在内核驱动链表中找到相应驱动并调用其中的open。

四.微机总线地址物理地址和虚拟地址:

        总线地址:cpu能访问的内存范围(如32位系统就可以访问2的32次方大小的内存;64位系统可以访问2的64次方大小的内存)

        物理地址:硬件硬盘上的实际地址,绝对地址。

        虚拟地址:逻辑(基于算法.软件层面的)地址,为假地址,物理地址中的程序是在虚拟地址中运行的,虚拟地址可以比物理地址大。

五.树莓派寄存器相关:

        树莓派中有6个功能寄存器分别为GPFSEL(0~5), 六个寄存器每个控制9个IO口分别为pin(0~9),每个寄存器有4个字节32位,每3位控制一个IO口,当三位为000时,IO为输入当三位为001时IO为输出

        树莓派有两个置1寄存器为GPSET0控制pin0~pin31这32个IO口每一个bit控制一个IO口;GPSET1控制32~53的IO口,每一个bit控制一个IO口。

        树莓派有两个清0寄存器GPCLR0GPCLR1和置一寄存器一样。

六.寄存器按位操作:

        按位与(&):有0就为0,全1才是1;当要给相应位置0时就与上0。

        按位或(|):有1就为1,全0才是0;当要给相应位置1时就或上1。

   列:要设置树莓派的pin4口为输出口,需要将寄存器GPFSEL0bit12~14位配置成001:

              *GPFSEL0 &= ~(0x6 << 12);  //将14 13 位配置成0;0x6的二进制数是110,先取反为001,然后左移12位再进行与操作就使bit14,bit13变为0但是bit12不确定。
              *GPFSEL0 |= (0x1 << 12);  // 将12 位配置成1;0x1的二进制数是001,左移12位或操作后使bit12置1。

           让pin4口输出高电平,需要将寄存器GPSET0bit4位配置成1:

                *GPSET0 | = 0x1 << 4;001左移4位后或操作。

           让pin4口输出低电平,需要将寄存器GPSET0bit4位配置成0:

                *GPCLR0 | = 0x1 << 4;

七.树莓派驱动编译步骤:

         1.编写驱动代码

         2.放入Linux源码下的dirver/char(字符设备)文件夹下

         3.修改该文件夹下的makefile文件(对照其中的文件修改)

         4.回到源码下输入ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules命令编译驱动代码生成xxxx.ko文件就是驱动程序

         5.用交叉编译工具arm-linux-gnueabihf-gcc pin4test.c -o pin4test编译上层代码,并将驱动程序和编译好的上层代码传入树莓派。

         6.insmod pin4drive.ko命令加载驱动;lsmod查看驱动;sudo chmod 666 /dev/pin4命令给生成的驱动文件访问权限;dmesg命令查看内核打印情况。

八.pin4IO口驱动代码:

#include <linux/fs.h>		 //file_operations声明
#include <linux/module.h>    //module_init  module_exit声明
#include <linux/init.h>      //__init  __exit 宏定义声明
#include <linux/device.h>	 //class  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件

static struct class *pin4_class; //设备类 
static struct device *pin4_class_dev;//设备

static dev_t devno;                //设备号
static int major =231;  		   //主设备号
static int minor =0;			   //次设备号
static char *module_name="pin4";   //模块名

volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0  = NULL;
volatile unsigned int* GPCLR0  = NULL;

//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
    printk("pin4_open\n");  //内核的打印函数和printf类似
   //配置pin4口为输出口;将寄存器的bit 12~14位配置成001;
     
	 *GPFSEL0 &= ~(0x6 << 12);  //将14 13 位配置成0;
	 *GPFSEL0 |= (0x1 << 12);  // 将12 位配置成1;
    
    return 0;
}

//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
	int usercmd;
	//获取上层write的函数值;
	copy_from_user(&usercmd,buf,count);//buf为用户空间的缓存
	//根据值来操作IO口输出高电平或低电平;
	if(usercmd == 1){
		printk("set 1\n");
		*GPSET0 | = 0x1 << 4;
	}else if(usercmd == 0){
		printk("set 0\n");
		*GPCLR0 | = 0x1 << 4;
	}else{
		printk("undo\n");
	}
    return 0;
}

static struct file_operations pin4_fops = {

    .owner = THIS_MODULE,
    .open  = pin4_open,
    .write = pin4_write
};

int __init pin4_drv_init(void)   
{
    int ret;
	printk("insmod driver pin4 success\n");
	
    devno = MKDEV(major,minor);  //创建设备号
    ret   = register_chrdev(major, module_name,&pin4_fops);  //注册驱动  告诉内核,把这个驱动pin4_fops加入到内核驱动的链表中

    pin4_class=class_create(THIS_MODULE,"myfirstdemo");//让代码在~/dev/路径下自动生成设备类
    pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);  //创建设备文件

//void * =ioremap(物理地址,寄存器大小);//一个寄存器四个字节32位;

    GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);//物理地址转换成虚拟地址,io口寄存器映射成普通内存单元进行访问;
	GPSET0  = (volatile unsigned int *)ioremap(0x3f20001c,4);
	GPCLR0 = (volatile unsigned int *)ioremap(0x3f200028,4);
	
    return 0;
}

void __exit pin4_drv_exit(void)
{
    iounmap(GPFSEL0);//解除物理地址的映射
	iounmap(GPSET0);
	iounmap(GPCLR0);
	
	
    device_destroy(pin4_class,devno);
    class_destroy(pin4_class);
    unregister_chrdev(major, module_name);  //卸载驱动

}

module_init(pin4_drv_init);  //入口,内核加载驱动时,这个宏会被调用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

         

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux系统中,i2c设备驱动程序通常会使用i2c核心框架提供的API,以下是一般的i2c驱动代码框架: ```c #include <linux/module.h> #include <linux/i2c.h> static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { // 初始化i2c设备 // 创建字符设备节点 // 设置私有数据 return 0; } static int my_i2c_remove(struct i2c_client *client) { // 删除字符设备节点 // 释放私有数据 return 0; } static const struct i2c_device_id my_i2c_id[] = { { "my_i2c_device", 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, my_i2c_id); static struct i2c_driver my_i2c_driver = { .driver = { .name = "my_i2c_device", .owner = THIS_MODULE, }, .probe = my_i2c_probe, .remove = my_i2c_remove, .id_table = my_i2c_id, }; static int __init my_i2c_init(void) { return i2c_add_driver(&my_i2c_driver); } static void __exit my_i2c_exit(void) { i2c_del_driver(&my_i2c_driver); } module_init(my_i2c_init); module_exit(my_i2c_exit); MODULE_AUTHOR("author"); MODULE_DESCRIPTION("i2c driver for my i2c device"); MODULE_LICENSE("GPL"); ``` 以上代码定义了一个名为`my_i2c_device`的i2c设备驱动程序,其中包含了设备的`probe`和`remove`函数,以及设备的标识信息和驱动程序的初始化和卸载函数。在`probe`函数中,需要完成i2c设备的初始化工作,包括创建字符设备节点和设置私有数据。在`remove`函数中,需要释放相关资源,包括删除字符设备节点和释放私有数据。最后,通过`i2c_add_driver`和`i2c_del_driver`函数将驱动程序注册到i2c核心框架中。 需要注意的是,以上代码仅为框架示例,具体的驱动程序实现会因不同的i2c设备而异。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值