zoukankan      html  css  js  c++  java
  • 8.驱动与硬件通信

              驱动与硬件通信

      

      驱动程序控制设备,主要是通过访问设备内的寄存器来达到控制目的,因此我们讨论如何访问硬件,就成了如何访问这些寄存器了.

       

     一、地址映射

      在Linux系统中,无论是内核程序还是应用程序,都只能使用虚拟地址,而芯片手册中给出的硬件寄存器地址或者RAM地址则是物理地址,无法直接使用,因此,我们读写寄存器的第1步就是将将它的物理地址映射为虚拟地址。

     1.1动态映射

      所谓动态映射,是指在驱动程序中采用ioremap函数将物理地址映射为虚拟地址。

    原型:void * ioremap(physaddr, size)
    参数:
      Physaddr:待映射的物理地址
      Size: 映射的区域长度
      返回值:映射后的虚拟地址

    1.2静态映射 

      所谓静态映射,是指Linux系统根据用户事先指定的映射关系,在内核启动时,自动地将物理地址映射为虚拟地址。

    1.2.1映射关系 

      在静态映射中,用户是通过map_desc结构来指明物理地址与虚拟地址的映射关系。

    struct map_desc{
      unsigned long virtual; /* 映射后的虚拟地址 */
      unsigned long pfn; /* 物理地址所在的页帧号 */
      unsigned long length; /* 映射长度 */
      unsigned int type; /* 映射的设备类型 */
    };
      pfn: 利用__phys_to_pfn(物理地址)可以计算出物理地址所在的物理页帧号

       该结构有四个成员:第一个参数是我们的虚拟地址,第二个参数才是我们实际的物理地址,只不过我们现在使用页桢号来表示。假如物理地址是50008000,一页的大小为4K,用物理地址除以4K,就得到页桢号了。

     1.2.2如何映射

      在source insight里面找到自己平台对应的Cpu.c文件进入,可以找到如下结构:

      

      可以看出这是个数组,这个数组的元素是结构struct map_desc。一个这样的结构对应的是内存的一片映射区域。  

      看一下GPIO的这一片区域映射关系:

      

    其中主要参数的来源:

      

    查看S3c6410芯片手册memory map章节:

      

    0x7f008000刚好是GPIO这片区域的起始物理地址。然后这片区域会被映射到下面的虚拟地址:

    S3C64XX_VA_GPIO:

      

    通过查找找到S3C_ADRR_CPU:

      

    可以看到同一个文件里面的上面个有对S3C_ADDR的定义:

      

    同时也有对S3C_ADDR_BASE的定义

    找到的是上面的S3C_ADDR_BASE   0XF4000000加上偏移量x,就是S3C_ADDR的地址。有了这样的地址,当我们的内核要去使用这表的的时候:

      

      当我们的6410启动的时候,会去找这样的一张表来完成映射。这就是内核启动的时候完成的静态映射。

    12.3静态映射应用

    在工程里面搜索S3C64XX_GPNCON:

      

      这就是我们拿到的IO的虚拟地址,下面我们分析一下到底是如何来的:

    1.搜索:S3C64XX_GPN_BASE

      

    我们可以同时看到S3C64XX_GPN_BASE以及S3C64XX_GPIOREG的来源

    2.我们接着搜索:S3C64XX_VA_GPIO

       

    可以看到,我们又回到了前面静态映射的物理地址所以S3C64XX_GPNCON的地址就是我们的虚拟地址,我们可以直接使用。

      理论上呢拿到S3C64XX_GPNCON这个值就可以使用了但是我在led程序中使用的时候居然卡死在程序里面出不来了,而我学习内存静态转换的过程也就卡到这儿了希望日后可以解决吧

     1 #include <mach/map.h>
     2 #include <mach/regs-gpio.h>
     3 #include <mach/regs-clock.h>
     4 #include <mach/gpio-bank-n.h>
     5 #include <plat/gpio-cfg.h>
     6 
     7 
     8 
     9 int led_open (struct inode *node, struct file *filp)
    10 {
    11     writel(0x1111,S3C64XX_GPNCON); //为虚拟地址写入值
    12     return 0;
    13 }
    14 
    15 //响应系统调用函数的驱动函数
    16 long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    17 {
    18     switch (cmd)   //通过不同命令执行控制命令
    19     {
    20         case LED_ON:
    21             writel(0x00,S3C64XX_GPNDAT);
    22             return 0;
    23         
    24         case LED_OFF:
    25             writel(0xff,S3C64XX_GPNDAT);
    26             return 0;
    27         
    28         default:
    29             return -EINVAL;
    30     }
    31 }

     二、寄存器读写

      在完成地址映射后,就可以读写寄存器了,Linux内核提供了一系列函数,来读写寄存器。

     1   unsigned ioread8(void *addr)
     2   unsigned ioread16(void *addr)
     3   unsigned ioread32(void *addr)
     4   unsigned readb(address)
     5   unsigned readw(address)
     6   unsigned readl(address)
     7 
     8 
     9   void iowrite8(u8 value, void *addr)
    10   void iowrite16(u16 value, void *addr)
    11   void iowrite32(u32 value, void *addr)
    12   void writeb(unsigned value, address)
    13   void writew(unsigned value, address)
    14   void writel(unsigned value, address)
  • 相关阅读:
    个人学习代码保存:例8.在存储过程中使用简单的事务处理
    个人学习代码保存:例6.多文件上传
    泛型 .net学习日记
    .net 点击刷新验证码问题
    个人学习代码保存:例11.读取Excel文件中的数据
    个人学习代码保存:例12.读取GridView文件中的数据到Excel文件
    个人学习代码保存:例5.利用标准FileUpload单文件上传
    Android视频采集
    Android视频采集+H264编码
    实时传输协议(RTP)和实时控制协议(RTCP)
  • 原文地址:https://www.cnblogs.com/wmx-learn/p/5359450.html
Copyright © 2011-2022 走看看