day04

回顾:
1.linux内核字符设备相关内容
  1.1.理念
          一切皆文件
  1.2.字符设备文件
      c/主/次/设备文件名
      mknod手动
      open/fd
  1.3.设备号,主,次
  1.4.字符设备驱动涉及的两个关键数据结构
      struct cdev
      cdev_init/cdev_add/cdev_del
      struct file_operations
              .open
              .release
              .read
                      数据流:硬件到用户
                      尤其注意buf:指向用户缓冲区
                      不能直接操作
                      copy_to_user
              .write
                      数据流:用户到硬件
                      尤其注意buf: 指向用户缓冲区
                      不能直接操作
                      copy_from_user
              .unlocked_ioctl
                      尤其注意arg:指向用户缓冲区
                      不能直接操作
                      还要注意数据类型的转换
 
2.linux内核字符设备相关内容(二)
  2.1.字符设备驱动数据结构之struct inode
  问:open/release接口的第一个形参struct inode *inode
      指针指向谁呢?
  答:struct inode原型
      struct inode {
           dev_t    i_rdev; //保存申请的设备号
           struct cdev        *i_cdev;//就是指向驱动开发者自己定义初始化注册的字符设备对象led_cdev
           ...
      };
      功能:描述一个文件的物理上的信息(字符设备/块设备/普通文件,
      读写权限,用户ID和组ID,日期等,也就是ls -lh test.txt看到的内容)
      生命周期:每当创建一个文件时(touch a.txt/mknod /dev/myled ...)
      内核就会用此数据结构定义初始化一个inode对象来描述此新创建的文件
      属性,每当删除文件时(rm),内核也会删除对应的inode对象
      一个文件仅有唯一的inode对象!
      结论:open/release两个接口的第一个参数inode指针
            就是指向内核创建的inode对象(描述文件的物理信息)
            当然啦,open/release可以通过inode指针来获取到设备号
            inode->i_rdev  
            主设备号=MAJOR(inode->i_rdev);
            次设备号=MINOR(inode->i_rdev);      
   
  2.2.字符设备驱动数据结构之struct file
  问:open/release/read/write/unlocked_ioctl接口的形参struct file *file
      指针指向谁呢?
  答:struct file原型
      struct file {
           //指向驱动开发者自己定义初始化的硬件操作接口对象led_fops
           const struct file_operations    *f_op;
           ...
      };
      功能:描述一个文件被打开(open)以后的状态属性
      生命周期:每当打开(open)一个文件成功时,内核
      会用此数据结构定义初始化一个file对象来描述
      这个文件打开以后的状态属性(O_RDWR),当然每当
      关闭close文件时,内核也会删除对应的file对象
      结论:open/release/read/write/unlocked_ioctl
      的file指针形参就是指向内核创建的file对象
      一个文件可以有多个file对象
       
      问:read/write/unlocked_ioctl的形参没有inode对象指针
          也就是将来没法通过inode指针来获取到设备号
          那file对象和inode对象有没有关系呢?
      答:有关系,可以通过file对象指针获取到inode对象指针
              具体参考fbmem.c(LCD显示屏驱动)
              关系如下:
              struct inode *inode =  
                                    file->f_path.dentry->d_inode;
          int minor = iminor(inode); //获取次设备号
          int major = imajor(inode); //获取主设备号
       
      案例:将四个LED灯作为四个硬件个体,并且他们共享
            一个驱动,驱动通过次设备号来区分
      分析:
      驱动要:1个
      主设备号要:1个
      次设备号要:4个:0/1/2/3
      设备文件要:4个:myled1/myled2/myled3/myled4
      cdev对象要:1个
      硬件操作接口要:1个
      实施步骤:
      上位机执行:
      mkdir /opt/drivers/day04/1.0 -p
      cd /opt/drivers/day04/1.0
      vim led_drv.c
      vim led_test.c
      vim Makefile
      make
      arm...gcc -o led_test led_test.c
      cp led_drv.ko led_test /opt/rootfs/home/drivers
       
      下位机测试:
      cd /home/drivers
      insmod led_drv.ko
      cat /proc/devices
      mknod /dev/myled1 c 主设备号 0
      mknod /dev/myled2 c 主设备号 1
      mknod /dev/myled3 c 主设备号 2
      mknod /dev/myled4 c 主设备号 3
      ./led_test  
 
3.linux内核字符设备文件的自动创建
  3.1.明确:字符设备文件的创建方式两种:
      手动创建:mknod命令
      自动创建
   
  3.2.字符设备文件的自动创建只需"三个保证"+"四个函数"  
      1.保证根文件系统rootfs中必须有mdev可执行程序
        例如:执行命令which is mdev确认
        mdev将来会自动帮你创建设备文件  
       
      2.保证根文件系统rootfs的启动脚本文件rcS中必须
        有以下两句话(/opt/rootfs/etc/init.d/rcS):
        mount -a #将来要解析fstab文件
        #驱动程序将来自动会解析hotplug来确认谁来帮驱动
         创建设备文件,目前需要/sbin/mdev可执行程序
        echo /sbin/mdev > /proc/sys/kernel/hotplug
       
      3.保证根文件系统rootfs的配置文件fstab中必须有以下
        三句话:
        proc   /proc proc  defaults 0 0
        sysfs  /sys  syfs  defaluts 0 0
        tmpfs  /dev  tmpfs defulats 0 0
        结果是:/proc作为procfs虚拟文件系统的入口
                /sys作为sysfs虚拟文件系统的入口
                /dev作为tmpfs虚拟文件系统的入口
                这三种文件系统的内容都是内核创建
                并且创建在内存中,掉电丢失!
        
      4.驱动程序接下来只需调用以下四个函数即可完成
        设备文件的最终创建
        //定义一个设备类指针(长树枝芽)
        struct class *cls;
         
        //创建一个设备类对象,cls指向这个对象(长树枝)
        //并且设备类名为tarena(树枝叫tarena)
        cls = class_create(THIS_MODULE, "tarena");          
         
        //正式创建设备文件,设备文件为/dev/myled
        //类似长苹果
        //dev:设备号
        //myled:设备文件名
        device_create(cls, NULL, dev, NULL, "myled");
         
        //删除设备文件,类似摘苹果
        device_destroy(cls ,dev);
         
        //删除设备类,类似砍树枝
        class_destroy(cls);
       
      案例:给之前的驱动添加自动创建设备文件功能
            实施步骤:
      上位机执行:
      mkdir /opt/drivers/day04/2.0 -p
      cd /opt/drivers/day04/2.0
      vim led_drv.c
      vim led_test.c
      vim Makefile
      make
      arm...gcc -o led_test led_test.c
      cp led_drv.ko led_test /opt/rootfs/home/drivers
       
      下位机测试:
      cd /home/drivers
      insmod led_drv.ko
      ls /dev/myled -lh //查看自动创建的设备文件
      ./led_test  
       
      作业题:将四个LED灯作为四个硬件个体的驱动添加
              设备文件的自动创建功能       
 
3.linux内核混杂设备驱动开发相关内容
  3.1.linux内核混杂设备特点
          混杂设备本质还是字符设备,指示混杂设备的主设备号
          由内核已经定义好为10,将来各个混杂设备个体(驱动)
          通过次设备号进行区分
   
  3.2.linux内核描述混杂设备的数据结构
      struct miscdevice {
          int minor;
                const char *name;
                const struct file_operations *fops;
      };
      成员说明:
      minor:混杂设备对应的次设备号,注意哦:主设备号为10
                  一般指定为宏MISC_DYNAMIC_MINOR,表示让
                  内核来帮你分配一个次设备号
      name:就是将来的设备文件名,并且设备文件是自动创建
            无需调用四个函数,但是三个保证还是需要滴
      fops:给混杂设备添加的硬件操作接口
       
      配套函数:
      //向内核注册一个混杂设备对象
      //内核会帮你自动创建一个名称为name的设备文件
        并且帮你分配一个次设备号
      misc_register(&混杂设备对象)
       
      //从内核卸载一个混杂设备对象
      //内核会帮你删除创建的设备文件
        内核会帮你释放申请的次设备号
      misc_deregister(&混杂设备对象)  
    
   3.3.案例:利用混杂设备驱动编程框架,实现LED驱动
   上位机执行:
      mkdir /opt/drivers/day04/3.0 -p
      cd /opt/drivers/day04/3.0
      vim led_drv.c
      vim led_test.c
      vim Makefile
      make
      arm...gcc -o led_test led_test.c
      cp led_drv.ko led_test /opt/rootfs/home/drivers
       
   下位机测试:
      cd /home/drivers
      insmod led_drv.ko
      ls /dev/myled -lh //查看自动创建的设备文件
                                        //关键看主设备号是否为10
                                        //关键看内核分配的次设备号
      ./led_test       
   
   3.4.案例:编写按键字符设备驱动,实现按键按下打印“按下”
            否则打印"松开",也就打印获取按键的操作状态                      
   上位机执行:
      mkdir /opt/drivers/day04/4.0 -p
      cd /opt/drivers/day04/4.0
      vim btn_drv.c
      vim btn_test.c
      vim Makefile
      make
      arm...gcc -o btn_test btn_test.c
      cp btn_drv.ko btn_test /opt/rootfs/home/drivers
       
   下位机测试:
      cd /home/drivers
      insmod btn_drv.ko
      ls /dev/mybtn -lh  
      ./btn_test //此时操作SW6按键,观察打印信息
      ctrl+c结束测试程序
      ./btn_test & //此时此程序无打印信息,让后台运行
      ps //查看进程
      top //查看进程所占用的CPU资源
      按Q键退出top命令
      kill btn_test进程的PID //杀死后台进程
       
    总结:由于按键操作动作是一个随机的过程,随意的动作
    为了能够让进程及时获取到按键的操作动作,目前的驱动
    采用轮训方式实现保证按键的状态能够捕获到,但是
    轮训的方式会让进程占用大量的CPU资源,虽然驱动能够
    正常工作,但是相当耗费CPU资源
    扩展:外设的处理速度远远慢于CPU,如果按照轮训的方式
    势必大大降低了CPU的利用率,耗费了大量的CPU资源
    如何解决这种问题呢?
    答:采用轮训的死对头中断方式!
 
*************************************************、
4.linux内核中断编程
    面试题:谈谈对中断的理解
    4.1.首先谈谈计算机为什么有中断机制
        中断产生的根本原因就是因为外设的数据处理速度
        远远慢于CPU
        务必举例子说明,以CPU读取UART接收缓冲区的数据为例谈谈
        当CPU读取UART接收缓冲区的数据,发现UART接收缓冲区的数据
        没有准备就绪,首先想到轮训方式,也就是CPU不做其他任何事情
        原地死等直到UART接收缓冲区的数据准备就绪,这样会造成CPU的
        利用率降低,耗费大量的CPU资源,立马想到轮训的死对头
        中断方式,CPU采用中断方式获取UART接收缓冲区的数据流程如下:
        当CPU读取UART接收缓冲区的数据,发现UART接收缓冲区没有准备好
        数据,那么CPU可以干别的事情(处理其他进程,其他进程获取CPU资源)
        一旦将来UART接收缓冲区数据准备就绪,UART控制器最终
        会给CPU发送一个中断电信号(类似,嗨,我这里准备好数据了)
        CPU一旦接收到这个中断电信号,CPU立马停止手头的工作(处理进程)
        转去处理UART接收缓冲区的数据,一旦处理完毕,CPU还会
        回到原先被打断的位置继续执行(继续执行原先处理的那个进程)
        此时此刻,CPU至少两件事,大大提高了CPU的利用率
      
     4.2.中断的硬件触发的流程
         重点要突出中断控制器
         务必举例子说明,以按键为例,谈谈
         首先画出一个按键的简要硬件连接示意图,key.bmp
         然后看图阐述按键中断的触发流程
         1.当按键按下,产生一个下降沿电信号
           此电信号首先跑到中断控制器
         2.中断控制器拿到此下降沿电信号以后,开始进行
           一番的判断
         3.中断控制器首先判断GPIOA28此引脚中断功能
           是否使能,如果禁止,直接丢弃,如果使能
           中断控制器继续判断
         4.接下来中断控制器判断此下降沿电信号是否是有效的
           中断触发电信号(五种),如果无效,直接丢弃
           如果为有效的中断触发信号,继续判断
         5.接下来中断控制器判断当前CPU是否有正在处理的
           高优先级中断,如果有,直接丢弃,如果没有,
           中断控制器继续判断
         6.接下来中断控制器判断此中断信号到底给哪个
           CPU核发送中断信号,是给CPU0啊,还是给全部CPU核发
         7.最后中断控制器还要判断此中断信号到底以哪种
           方式发送给CPU核,以IRQ形式?还是以FIQ形式
           比如这里我们选择IRQ形式发送
           最后CPU核就会接收到按键发送过来的中断电信号
         8.CPU核一旦接收到了按键发送的IRQ中断信号
           CPU核立马触发一个IRQ中断异常,CPU核立马
           要处理IRQ中断异常:
           CPU核硬件上自动完成:
               备份CPSR到SPSR_IRQ
               设置CPSR
                       MODE
                       T
                       I
                     F
               保存返回地址LR_IRQ=PC-4
               设置PC=0x18,让CPU核跑到0x18地址去继续运行
               至此开启了软件进一步处理IRQ中断异常的流程
               
          软件处理IRQ中断异常的流程:
          1.首先要建立一个异常向量表出来
            异常向量表就是在ARM核7种异常的处理入口地址(0x00/0x04/0x08/0x0c/0x10/0x18/0x1c)
            放置自己的代码,每当异常发生,CPU核都会去处理对应的代码
            如果可以,直接写一个异常向量表的代码:
            b   reset   //0x00
            ldr pc _undef_func //0x04
            ldr pc _swi_func //0x08
            ldr pc _pre_instr_func //0x0c
            ldr pc _data_func //0x10
            b .  //0x14 保留
            ldr pc _irq //0x18
            ldr pc _fiq //0x1c
           
          2.CPU核一旦跑到0x18地址运行,软件上做:
            1.保护现场,保护CPU原先处理的任务的现场
              就是做一个压栈处理
            2.根据用户需求完成IRQ中断的处理
              例如打印一句话
            3.恢复现场,恢复到原先被打断的任务的现场
              就是做一个出栈处理
              至此真个IRQ中断处理完毕
             
       4.3.接下来继续谈谈中断的软件编程
               中断的软件编程只需写四部分代码即可:
               1.编写异常向量表的代码
               2.编写保护现场的代码
               3.根据用户需求完成对中断的处理
                 本质就是编写一个中断处理函数
                 中断处理函数中做什么事由用户需求来定
               4.编写恢复现场的代码
               总结:不管是ARM裸板编程还是基于linux系统的中断编程
               程序员只需完成第3步即可,其余124都是由ARM公司
               或者芯片厂家完成!
               此时此刻务必画出一个中断的处理流程图,参见irq.bmp    
               
               中断服务程序=中断服务历程=中断处理函数          
                
          
                  
                  
                 
       
   
         
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值