2008-03-19 22:52:00 weixin_34246551 阅读数 18
  • 全面认识海思SDK及嵌入式层开发-第1/11季视频课程

    本课程目标是学习海思SDK的结构,并且学会配置、编译整个SDK并部署到专用开发板上,启动Linux系统并运行MPP中的Sample。课程隶属朱有鹏物联网大讲堂“项目驱动创新学习训练营”之《A0201-海思HI3518E方案视频编解码传输深度学习》项目,整个项目共11季,由浅入深开展,此为开篇第1季。

    4255 人正在学习 去看看 朱有鹏
作者:宋宝华 email:author@linuxdriver.cn
    在过去这些年,Linux已经成功应用于服务器和桌面系统,而近年来,随着嵌入式系统应用的持续升温,Linux也开始广泛应用于嵌入式领域,逐步成为通信、工业控制、消费电子等领域的主流操作系统。Linux正以其独特的优势极大地吸引电子设计工程师,很多工程师从自己编写的或专用的RTOS转移到Linux,Linux在嵌入式系统中的占有率与日俱增。全世界有无数的嵌入式产品正使用Linux作为其操作系统,在这些采用Linux作为操作系统的设备中,无一例外都包含着多个Linux设备驱动,没有这些设备驱动,用户便无法享受Linux上诸多精彩纷呈的应用。
1.Linux设备驱动开发的基础
    Linux设备驱动的开发需要牢固的硬件基础,并需要对驱动中所涉及的Linux内核知识有良好的掌握,具体表现在:
    (1)驱动直接与硬件打交道,在编写某类硬件设备的驱动时,我们必须对该驱动涉及到的硬件的工作原理和接口有清楚的掌握,因为许多时候,我们需要直接操作寄存器、控制中断和DMA。
    (2)编写Linux设备驱动涉及到许多Linux内核的API,会大量使用自旋锁、信号量、等待队列、tasklet、内存与I/O访问,如果对内核中的相关API了解不够充分,很难写出高质量的驱动。
    在Linux设备驱动开发中,自旋锁和信号量是两种最常用的用于并发控制的手段,几乎所有的设备驱动中都使用了自旋锁或信号量。自旋锁和信号量控制临界区的方法相似:
spin_lock (&lock) ; //获取自旋锁,保护临界区
critical section //临界区
spin_unlock (&lock) ; //释放自旋锁

down(&mount_sem);//获取信号量,保护临界区
critical section //临界区
up(&mount_sem);//释放信号量
    自旋锁或信号量的区别在于:信号量是进程级的,用于多个进程之间对资源的互斥,虽然也是在内核中,但是该内核执行路径是以进程的身份,代表进程来争夺资源的。如果竞争失败,会发生进程上下文切换(当前进程进入睡眠状态,CPU运行其它进程)。当所要保护的临界区访问时间比较短时,用自旋锁是非常方便的,因为它节省上下文切换的时间。自旋锁锁定期间不允许阻塞,因此要求锁定的临界区小。
    阻塞和非阻塞I/O是设备访问的两种不同模式,阻塞操作意味着在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作,被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。应用程序多以阻塞方式访问设备,在Linux驱动程序中,经常使用等待队列(wait queue)来实现进程的阻塞与唤醒控制,一个典型的流程如下所示:
1  static ssize_t xxx_write(struct file *file, const char *buffer, size_t count,
2    loff_t *ppos)
3  {
4    ...
5    DECLARE_WAITQUEUE(wait, current); //定义等待队列
6    add_wait_queue(&xxx_wait, &wait);  //添加等待队列

8    ret = count;
9    /* 等待设备缓冲区可写 */
10   do
11   {
12     avail = device_writable(...);
13     if (avail < 0)
14       __set_current_state(TASK_INTERRUPTIBLE);//改变进程状态
15
16     if (avail < 0)
17     {
18       if (file->f_flags &O_NONBLOCK)  //非阻塞
19       {
20         if (!ret)
21           ret =  - EAGAIN;
22         goto out;
23       }
24       schedule();   //调度其他进程执行
25       if (signal_pending(current))//如果是因为信号唤醒
26       {
27         if (!ret)
28           ret =  - ERESTARTSYS;
29         goto out;
30       }
31     }
32   }while (avail < 0);
33
34   /* 写设备缓冲区 */
35   device_write(...)
36   out:
37   remove_wait_queue(&xxx_wait, &wait);//将等待队列移出等待队列头
38   set_current_state(TASK_RUNNING);//设置进程状态为TASK_RUNNING
39   return ret;
40 }
    上述流程中,当设备暂时不可写时,驱动主动通过schedule()调度其他进程执行本身进入睡眠状态,由于进程进入被加入了等待队列,它可以被中断或其他执行路径唤醒。
    大多数外设都包含一个以上的中断,Linux将中断分成了2个半部,即顶半部和底半部,顶半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态并清除中断标志后就进行“登记中断”的工作。“登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。这样,顶半部执行的速度就会很快,可以服务更多的中断请求,而中断处理工作的重心就落在了底半部的头上,它来完成中断事件的绝大多数任务。底半部可以被新的中断打断,这也是底半部和顶半部的最大不同。tasklet、work-queue是Linux内核中常用的用于调度底半部执行的机制,调度底半部的典型方法如下:
1  /*定义tasklet和底半部函数并关联*/
2  void xxx_do_tasklet(unsigned long);
3  DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);

5  /*中断处理底半部*/
6  void xxx_do_tasklet(unsigned long)
7  {
8      ...
9  }
10
11 /*中断处理顶半部*/
12 irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
13 {
14     ...
15     tasklet_schedule(&xxx_tasklet); //调度底半部执行
16     ...
17 }
    高性能处理器一般会提供一个内存管理单元(MMU),该单元辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和CACHE缓存控制等硬件支持。Linux的每个进程可以访问4GB的内存,0~3GB位于用户空间,对所有进程单独控制,3GB~4GB位于内核空间,被所有进程共享。在Linux内核空间申请内存涉及到的函数主要包括kmalloc()、__get_free_pages()和vmalloc()等。kmalloc()和__get_free_pages()申请的内存在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系。而vmalloc() 在虚拟内存空间给出一块连续的内存区,实质上,这片连续的虚拟内存在物理内存中并不一定连续,而vmalloc()申请的虚拟内存和物理内存之间也没有简单的换算关系。
    在驱动中,对于外设的寄存器,不能直接访问物理地址,需访问经过映射后的虚拟地址。外设的寄存器可以用2种方式被映射到虚拟地址,一是静态映射,二是通过ioremap()动态映射。静态映射的方法是在将Linux移植到特定平台时建立一个 map_desc数组,通过 MACHINE_START和MACHINE_END宏之间的.map_io成员函数建立页面。       
2.Linux设备驱动的架构
    近年来内核在驱动方面更偏向于提供设备驱动的架构(Framework)而非单个设备驱动,考虑到框架更强的兼容性,字符设备、块设备、网络设备、MTD设备、TTY设备、I2C设备、LCD设备、音频设备、摄像头、USB设备、PCI设备等驱动的体系结构都变得愈发复杂。
    Linux设备驱动首先作为一个内核模块而存在,模块可以直接编译进内核或编译为.ko文件通过insmod、modprobe动态加载。
    Linux字符设备驱动的核心是file_operations结构体,驱动的主体是实现其中的read()、write()、ioctl()等成员函数,如:
1 struct file_operations xxx_fops =
2 {
3   .owner = THIS_MODULE,
4   .read = xxx_read,
5   .write = xxx_write,
6   .ioctl = xxx_ioctl,
7   ...
8 };
    Linux块设备驱动并不直接实现file_operations成员函数,其主体变成处理实现block_device_operations成员函数以及处理上层下达的I/O请求,处理I/O请求典型流程如下:
1  static void xxx_request(request_queue_t *q)
2  {
3    struct request *req;
4    while ((req = elv_next_request(q)) != NULL)
5    {
6      struct xxx_dev *dev = req->rq_disk->private_data;
7      if (!blk_fs_request(req)) //不是文件系统请求
8      {
9        printk(KERN_NOTICE "Skip non-fs request\n");
10       end_request(req, 0);//通知请求处理失败
11       continue;
12     }
13     xxx_transfer(dev, req->sector, req->current_nr_sectors, req->buffer,
14       rq_data_dir(req)); //处理这个请求
15     end_request(req, 1); //通知成功完成这个请求
16   }

    Linux网络设备驱动的结构如上图所示,其中设备驱动功能层各函数是网络设备接口层net_device 数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接收操作。sk_buff 结构体用于表示描述网络包,它定义了对应于传输层TCP/UDP(及ICMP 和IGMP)、网络层 和和链路层协议的协议头。
    Linux下编写网络设备驱动的主体工作是完成net_device结构体的填充以及成员函数的实现,底层最核心的工作是:发送数据包和接收数据包,接收数据包是由中断触发的。发送数据包函数的典型结构如下:
1 int xxx_tx(struct sk_buff *skb, struct net_device *dev)
2 {
3     int len;
4     char *data, shortpkt[ETH_ZLEN];
5     /* 获得有效数据指针和长度 */
6     data = skb->data;
7     len = skb->len;
8     if (len < ETH_ZLEN)
9     {
10          /* 如果帧长小于以太帧最小长度,补0 */
11          memset(shortpkt, 0, ETH_ZLEN);
12          memcpy(shortpkt, skb->data, skb->len);
13          len = ETH_ZLEN;
14          data = shortpkt;
15     }
16
17     dev->trans_start = jiffies; /* 记录发送时间戳 */
18
19     /* 设置硬件寄存器让硬件把数据包发送出去 */
20     xxx_hw_tx(data, len, dev);
21     ...
22 }
    接收数据包的典型结构是:
1 static void xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
2 {
3     ...
4     switch (status &ISQ_EVENT_MASK)
5     {
6          case ISQ_RECEIVER_EVENT:
7          /* 获取数据包 */
8          xxx_rx(dev);
9          break;
10     /* 其他类型的中断 */
11     }
12 }
13 static void xxx_rx(struct xxx_device *dev)
14 {
15     ...
16     length = get_rev_len (...);
17     /* 分配新的套接字缓冲区 */
18     skb = dev_alloc_skb(length + 2);
19
20     skb_reserve(skb, 2); /* 对齐 */
21     skb->dev = dev;
22
23     /* 读取硬件上接收到的数据 */
24     insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length), length >> 1);
25     if (length &1)
26     skb->data[length - 1] = inw(ioaddr + RX_FRAME_PORT);
27
28     /* 获取上层协议类型 */
29     skb->protocol = eth_type_trans(skb, dev);
30
31     /*把数据包交给上层 */
32     netif_rx(skb);
33
34     /* 记录接收时间戳 */
35     dev->last_rx = jiffies;
36     ...
37 }
    对于其他如MTD设备、TTY设备、I2C设备、LCD设备、音频设备、摄像头、USB设备、PCI设备等,Linux都定义了类似于网络设备驱动的复杂的层次结构,如TTY设备驱动的层次如下:

    这些复杂结构的定义,加大了Linux驱动的开发门槛,同时也使得开发Linux驱动甚至具有了类似于使用VC++开发MFC程序的特点。
3.总结
    简言之,可以得出如下等式:Linux设备驱动开发=硬件控制+Linux内核API(用于并发/同步控制、阻塞/唤醒、中断底半部调度、内存和I/O访问等)+驱动框架。等式右边的3个要素缺一不可,开发高质量的Linux驱动也势必要求工程师对这些知识有良好的掌握,拙著《Linux设备驱动开发详解》一书对这些知识都进行了深入讲解。
2016-11-18 13:02:05 NOStandby 阅读数 590
  • 全面认识海思SDK及嵌入式层开发-第1/11季视频课程

    本课程目标是学习海思SDK的结构,并且学会配置、编译整个SDK并部署到专用开发板上,启动Linux系统并运行MPP中的Sample。课程隶属朱有鹏物联网大讲堂“项目驱动创新学习训练营”之《A0201-海思HI3518E方案视频编解码传输深度学习》项目,整个项目共11季,由浅入深开展,此为开篇第1季。

    4255 人正在学习 去看看 朱有鹏

S3C2440完全开发流程   
Linux系统命令及其使用详解
Linux主要shell命令详解   
深入理解Linux内核(第三版 英文版)   
深入分析Linux内核源代码教程   
linux从入门到精通教程   
Linux菜鸟专用资料   
Linux网络编程教程   
嵌入式Linux应用开发完全手册
Linux设备驱动程序学习    
Linux实用培训教程合并版    
精通linux shell编程教程    
Qt编程及应用经典教程
Linux嵌入式软件开发教程    
Linux程序员指南教程
ARM9+嵌入式Linux教程
LINUX编程白皮书
ARM应用系统开发详解
Linux C语言编程一站式学习
linux命令大全--分类总结、实例和用法   
ARM应用系统开发详解——基于S3C4510B的系统设计(第二版)   
ARM嵌入式入门级教程
ARM嵌入式系统基础教程+周立功
U-boot源代码分析
Ubuntu从入门到精通

2012-03-20 20:45:14 jmq_0000 阅读数 98342
  • 全面认识海思SDK及嵌入式层开发-第1/11季视频课程

    本课程目标是学习海思SDK的结构,并且学会配置、编译整个SDK并部署到专用开发板上,启动Linux系统并运行MPP中的Sample。课程隶属朱有鹏物联网大讲堂“项目驱动创新学习训练营”之《A0201-海思HI3518E方案视频编解码传输深度学习》项目,整个项目共11季,由浅入深开展,此为开篇第1季。

    4255 人正在学习 去看看 朱有鹏

驱动概述

        说到 android 驱动是离不开 Linux 驱动的。Android 内核采用的是 Linux2.6 内核 (最近Linux 3.3 已经包含了一些 Android 代码)。但 Android 并没有完全照搬 Linux 系统内核,除了对Linux 进行部分修正,还增加了不少内容。android 驱动 主要分两种类型:Android 专用驱动 和 Android 使用的设备驱动(linux)。

      Android 专有驱动程序:

      1)Android Ashmem 匿名共享内存; 为用户空间程序提供分配内存的机制,为进程间提供大块共享内存,同时为内核提供回收和管理这个内存。

      2)Android Logger    轻量级的LOG(日志) 驱动;

      3)Android Binder     基于 OpenBinder 框架的一个驱动;

      4)Android Power Management  电源管理模块;

      5)Low Memory Killer  低内存管理器;

      6)Android PMEM        物理内存驱动;

      7)USB Gadget             USB 驱动(基于 gaeget 框架);

      8)Ram Console           用于调试写入日志信息的设备;

      9)Time Device             定时控制设备;  

     10)Android Alarm         硬件时钟


     Android 上的设备驱动:

      1)Framebuff 显示驱动;

      2)Event 输入设备驱动;

      3)ALSA 音频驱动;

      4)OSS 音频驱动;

      5)v412摄像头:视频驱动;

      6)MTD 驱动;

      7)蓝牙驱动;

      8)WLAN 设备驱动;


 Android 专有驱动程序

      1.Android Ashmem 

               为用户空间程序提供分配内存的机制,为进程间提供大块共享内存,同时为内核提供回收和管理这个内存。

               设备节点:/dev/ashmen .主设备号 10.

               源码位置: include/linux/ashmen.h    Kernel /mm/ashmen.c

                     相比于 malloc 和 anonymous/named mmap 等传统的内存分配机制,其优势是通过内核驱动提供了辅助内核的内存回收算法机制(pin/unoin)

      2.Android Logger  

                    无论是底层的源代码还上层的应用,我们都可以使用 logger 这个日志设备看、来进行调试。

                     设备节点:  /dev/log/main      /dev/log/event   /dev/log/radio

                     源码位置:include/linux/logger.h         include/linux/logger.c

      3.Android Binder     

                IPC Binder 一种进程间通信机制。他的进程能够为其它进程提供服务 ----- 通过标准的 Linux 系统调用 API。

                设备节点 :/dev/binder

                源码位置:Kernel/include/linux/binder.h    Kernel/drivers/misc/binder.c

      4.Android Power Management  

               一个基于标准 linux 电源管理的轻量级 Android 电源管理系统,在 drivers/android/power.c      kernel/power/

      5.Low Memory Killer 

                它在用户空间中指定了一组内存临界值,当其中某个值与进程描述中的 oom_adj 值在同一范围时,该进程将被Kill掉(在parameters/adj中指定oome_adj 的最小值)。它与标准的Linux OOM机制类似,只是实现方法不同

                源码位置:drivers/misc/lowmemorykiller.c       

      6.Android PMEM       

                PMEM 主要作用就是向用户空间提供连续的物理内存区域。

                      1.让 GPU 或 VPU 缓冲区共享 CPU 核心。

                      2.用于 Android service 堆。

               源码位置:include/linux/android_pmem.h drivers/android/pmem.c                        

      7.USB Gadget            

                基于标准 Linux USB gaeget 驱动框架的设备驱动。

                源码位置:drivers/usb/gadet/ 

      8.Ram Console         

                为了提供调试功能,android 允许将调试日志信息写入这个设备,它是基于 RAM 的 buffer.

                源码位置: drivers/staging/android/ram_console.c

      9.Time Device            

               定时控制,提供了对设备进行定时控制的功能。

               源码位置:drivers/staging/android/timed_output.c(timed_gpio.c)

    10.Android Alarm       

                提供一个定时器,用于把设备从睡眠状态唤醒,同时它还提供了一个即使在设备睡眠时也会运行的时钟基准。

                 设备节点:/dev/alarm

                 源码位置:drivers/trc/alarm.c


Android 设备驱动

    1. Framebuffer 帧缓存设备

         Framebuffer 驱动在 Linux 中是标准的显示设备的驱动。对于 PC 系统,它是显卡的驱动 ; 对于嵌入式 SOC 处理器系统,它是 LCD 控制器或者其他显示控制器的驱动。它是一个字符设备,在文件系统中设备节点通常是 /dev/fbx 。 每个系统可以有多个显示设备 , 依次用 /dev/fbO 、 /dev/fb l
等来表示。在 Android 系统中主设备号为 29 ,次设备号递增生成。

         Android 对 Framebuffer 驱动的使用方式是标准的 , 在 / dev / graphie / 中的 Framebuffer 设备节点由 init 进程自动创建 , 被 libui 库调用 。 Android 的 GUI 系统中 , 通过调用 Framebuffer 驱动的标准接口,实现显示设备的抽象。

         

     Framebuff的结构框架和实现 : 

          linux LCD驱动(二)--FrameBuffer  

              Linux LCD驱动(四)--驱动的实现                                    

    2.Event输入设备驱动

         Input 驱动程序是 Linux 输入设备的驱动程序 , 分为游戏杆 (joystick) 、 鼠标 (mouse 和 mice)和事件设备 (Event queue)3 种驱动程序。其中事件驱动程序是目前通用的程序,可支持键盘 、 鼠标、触摸屏等多种输入设备。 Input 驱动程序的主设备号是 l3 ,每一种 Input 设备从设备号占 用5 位 , 3 种从设备号分配是 : 游戏杆 0 ~ 61 ; Mouse 鼠标 33 ~ 62 ; Mice 鼠标 63 ; 事件设备 64 ~ 95 ,各个具体的设备在 misc 、 touchscreen 、 keyboard 等目录中。
        Event 设备在用户空问使用 read 、 ioctl 、 poll 等文件系统的接口操作, read 用于读取输入信息, ioctl 用于获取和设置信息, poll 用于用户空间的阻塞,当内核有按键等中断时,通过在中断中唤醒内核的 poll 实现。 

        Event 输入驱动的架构和实现:

                          Linux设备驱动之——input子系统

 

     3.ALSA音频驱动

         高级 Linux 声音体系 ALSA(Advanced Linux Sound Architecture ) 是为音频系统提供驱动 的Linux 内核组件,以替代原先的开发声音系统 OSS 。它是一个完全开放源代码的音频驱动程序集 ,除了像 OSS 那样提供一组内核驱动程序模块之外 , ALSA 还专门为简化应用程序的编写提供相应的函数库,与 OSS 提供的基于 ioctl 等原始编程接口相比, ALSA 函数库使用起来要更加方便一些 

        利用该函数库,开发人员可以方便、快捷地开发出自己的应用程序,细节则留给函数库进行内部处理 。 所以虽然 ALSA 也提供了类似于 OSS 的系统接口 , 但建议应用程序开发者使用音频函数库,而不是直接调用驱动函数。

                     ALSA 驱动的主设备号为 116 ,次设备号由各个设备单独定义,主要的设备节点如下:
                             / dev / snd / contmlCX —— 主控制 ;
                             / dev / snd / pcmXXXc —— PCM 数据通道 ;
                             / dev / snd / seq —— 顺序器;
                             / dev / snd / timer —— 定义器。
        在用户空问中 , ALSA 驱动通常配合 ALsA 库使用 , 库通过 ioctl 等接口调用 ALSA 驱动程序的设备节点。对于 AIJSA 驱动的调用,调用的是用户空间的 ALsA 库的接口,而不是直接调用  ALSA 驱动程序。 ALSA 音频驱动的架构如下图所示:

                                     

        ALSA 驱动程序的主要头文件是 include / sound ./ sound . h ,驱动核心数据结构和具体驱动的注册函数是 include / sound / core . h ,驱动程序 的核心实现是 Sound / core / sound . c 文件。                     

       ALSA 驱动程序使用下面的函数注册控制和设备:

                int snd _ pcm _ new (struct snd _ card * card , char * id , int device , int playback _ count , int capture _ count , struct snd _ pcm ** rpcm) ;

                 int snd ctl _ add(struct snd _ card * card , struct snd _ kcontrol * kcontro1) ;

         ALSA 音频驱动在内核进行 menuconfig 配置时 , 配置选项为 “ Device Drivers ” > “ Sound c ard support ” 一 > “ Advanced Linux Sound Architecture ” 。子选项包含了 Generic sound devices( 通用声音设备 ) 、 ARM 体系结构支持,以及兼容 OSS 的几个选项。 ALsA 音频驱动配置对应的文件是sound / core / Kconfig 。

      Android 没有直接使用 ALSA 驱动,可以基于 A-LSA 驱动和 ALSA 库实现 Android Audio 的硬件抽象层; ALSA 库调用内核的 ALSA 驱动, Audio 的硬件抽象层调用 ALSA 库。      


      4.OSS音频驱动

         OSS(Open Sound System开放声音系统)是 linux 上最早出现的声卡驱动。OSS 由一套完整的内核驱动程序模块组成,可以为绝大多数声卡提供统一的编程接口。

         OSS 是字符设备,主设备号14,主要包括下面几种设备文件:

          1) /dev/sndstat

                 它是声卡驱动程序提供的简单接口,它通常是一个只读文件,作用也只限于汇报声卡的当前状态。(用于检测声卡)

          2)/dev/dsp

                 用于数字采样和数字录音的设备文件。对于音频编程很重要。实现模拟信号和数字信号的转换。

          3)/dev/audio

                 类似于/dev/dsp,使用的是 mu-law 编码方式。

          4)/dev/mixer

                 用于多个信号组合或者叠加在一起,对于不同的声卡来说,其混音器的作用可能各不相同。

          5)/dev/sequencer

                   这个设备用来对声卡内建的波表合成器进行操作,或者对 MIDI 总线上的乐器进行控制。

           OSS 驱动所涉及的文件主要包括:

                kernel/include/linux/soundcard.h

                kernel/include/linux/sound.h   定义 OSS 驱动的次设备号和注册函数

                kernel/sound_core.c    OSS核心实现部分

           OSS驱动架构图:

       

     5.V4l2视频驱动

   V4L2是V4L的升级版本,为linux下视频设备程序提供了一套接口规范。包括一套数据结构和底层V4L2驱动接口。V4L2提供了很多访问接口,你可以根据具体需要选择操作方法。需要注意的是,很少有驱动完全实现了所有的接口功能。所以在使用时需要参考驱动源码,或仔细阅读驱动提供者的使用说明。

      V4L2的主设备号是81,次设备号:0~255,这些次设备号里也有好几种设备(视频设备、Radio设备、Teletext、VBI)。

        V4L2的设备节点: /dev/videoX, /dev/vbiX and /dev/radioX

      V4L2框架图:

        


2005-02-05 09:27:00 zcatlinux 阅读数 1935
  • 全面认识海思SDK及嵌入式层开发-第1/11季视频课程

    本课程目标是学习海思SDK的结构,并且学会配置、编译整个SDK并部署到专用开发板上,启动Linux系统并运行MPP中的Sample。课程隶属朱有鹏物联网大讲堂“项目驱动创新学习训练营”之《A0201-海思HI3518E方案视频编解码传输深度学习》项目,整个项目共11季,由浅入深开展,此为开篇第1季。

    4255 人正在学习 去看看 朱有鹏
概述

嵌入式Linux具有稳定、可扩充性及开放原始程序代码等特点,可兼容多种处理器和主机,广泛适用于各种产品和应用。但是,交叉编译、设备驱动程序开发/除错,以及更小尺寸等要求对嵌入式Linux开发者来说都是严峻的挑战。为应对这些挑战,针对嵌入式Linux开发的专用工具应运而生,而且发展十分迅速。

但是,许多这类开发工具都不兼容非X86平台,而且也没有很好地实现归档备案或整合。在其它开发环境下,组件间的高度整合并没有完全兑现。因此,要想完全从这些免费的软件组件开始制作一个完整的跨平台开发环境,开发者应意识到这将需要大量的调查、实施、训练和维护方面的工作。

Linux是少数既可以在嵌入式设备上执行也可作为开发环境的操作系统之一。这一特性可让开发者在转向此开发系统之前于常用硬件(比如X86桌面系统)之上开发、除错和测试应用程序和库,因此可减少对标准参考平台和指令集仿真器的依赖。这一技术仅适用于应用程序和库,但不适用于设备驱动程序,因为后者的开发依赖于Linux架构。

开放原始程序代码团体及一些软件供货商可提供设备驱动程序开发工具。由于设备驱动程序比标准应用程序距离硬件更近,因此它们的开发比较困难。所幸的是,Linux桌面系统可以利用一些Windows及其它操作系统所没有的工具。有足够经验开发设备驱动程序的开发人员可能已经习惯用Linux开发系统了。

Linux的快速发展及其桌面方案的不断涌现突显了一个重要问题:所选择的工具方案应如何在不同的Linux分布式系统上执行?它们依赖于主机平台的软件配置吗?

有些Linux工具提供独立于主机平台的开发环境,包括一系列可支持开发工具的应用软件、库和实用程序。这一方法几乎将开发环境与主机配置完全隔离开来,因此主机可以是任何Linux分布式系统,而且任何更新和修改都不会影响开发环境的功能。

这种方法的主要缺点是对储存空间的要求有所增加─约200MB,因为它自己实际上相当于一个微型Linux分布式系统。

可用的工具

一个嵌入式Linux产品的开发需要几个阶段,包括为目标板配置和建构基本Linux OS;除错应用程序、库、核心及设备驱动程序/核心模块;出货前最终方案的最佳化、测试和验证。

有数百种开放原始程序代码开发工具可供选择。只要开发者原意花时间和精力去调查、实施和维护一系列各不相同的工具,总能找出一个完整的解决方案,完成几乎任何开发任务。


图1

开发者必须精确地考虑到这些工具的松散集合能提供什么样的功能,以及需要付出多大的努力才能形成完整的解决方案。"

在Linux应用程序和库的除错方面,GNU Debugger (GDB)作为一种标准已有几年的历史。它是一种命令行程序,由多个不同的图形用户接口前端予以支持,每个前端都能以多种方式提供除错控制功能。尽管GDB不是一个完美的方案,但它足够应对各种除错任务,而且已经得到开放原始程序代码团体的广泛支持。

Linux核心或设备驱动程序的除错要比应用程序的除错繁琐得多。

在做调查时,以下方面应特别注意:

什么除错方法支持要开发产品的硬件?
需要什么核心支持程序?
还需要其它什么支持程序?
除错接口怎么样,如何使用?
该工具需要除错核心模块及处理虚拟地址转换吗?
还需要其它什么工具才能提供完整的方案?

经过进一步的调查,开发者往往发现工具A和工具B并没有提供完全一致的功能,因为它们是在彼此独立的情况下开发的。结果,开发者必须精确地考虑到这些工具的松散集合能提供什么样的功能,还需要付出多大的努力才能形成完整的解决方案。

如果不同处理器类型间的整合、可用性、互通作业性和移植性很关键的话,开发者应考虑购买商用开发工具。这主要是因为将开发一个'免费'方案所付出的努力考虑进去,商用开发工具并不算贵。

Linux BSP

Linux系统有两大主要部份:带设备驱动程序的Linux核心;以及根文件系统,包括系统所需的全部支持应用程序、服务和库。

除了驻留在目标板上的OS组件外,还需要制作一个由GNU Compiler Collection构成的交叉编译环境,为库和二进制程式(binutils)提供支持。

虽然几乎每一个组件都可在网上找到,但在硬件或设备驱动程序支持、整合测试信息、交叉编译指南或软件兼容性方面却很难收集到太多信息。尽管开发者可从网上免费下载各种组件以配置嵌入式Linux操作系统,但每个组件在版本、支持、稳定性和测试等方面的状态则需要开发者自己决定。然后,开发者还要完成最后的OS整合和测试,以及为所开发产品提供终身Linux OS维护。

另一方面,嵌入式Linux供货商所提供的商用Linux板支持工具套件一般都是经过预先安装和测试的,而且提供支持和维护。其它须考虑的因素包括Linux桌面主机将会添加不同的库和核心功能,以及由于组织内的开发者变动而引起的长期维护问题。

品质保证部门一般会执行一系列严格的验证和性能测试,其中包括内存泄漏检测/纠正、程序代码最佳化和任务追踪等。那些想利用开放原始程序代码工具开发针对非X86平台的嵌入式Linux产品开发者将会发现这一任务甚至要比选择开放原始程序代码除错方案难得多。Linux Trace Toolkit、Valgrind工具及其它内存分析程序可完成部份测试和验证任务。但总的来说,它们缺乏关键特性、整合功能及广泛的硬件支持。这些开放原始程序代码分析工具的评估过程与评估除错方案的过程基本相同。

最后的分析就是,一个设计得恰到好处的开发环境应能够供货商用和开放原始程序代码两个世界所具有的最好特性:

* 完整的开发能力;
* 易于使用和整合;
* 大型工程组织的协调控制;
* 品质和支持保证;
* 持续性;
* 按照自己的判断力使用开放原始程序代码的能力。

作者:David Beal 嵌入式Linux总监 Email:dbeal@metrowerks.com Metrowerks公司

2010-05-08 14:32:00 ramon_gu 阅读数 502
  • 全面认识海思SDK及嵌入式层开发-第1/11季视频课程

    本课程目标是学习海思SDK的结构,并且学会配置、编译整个SDK并部署到专用开发板上,启动Linux系统并运行MPP中的Sample。课程隶属朱有鹏物联网大讲堂“项目驱动创新学习训练营”之《A0201-海思HI3518E方案视频编解码传输深度学习》项目,整个项目共11季,由浅入深开展,此为开篇第1季。

    4255 人正在学习 去看看 朱有鹏

4   设备驱动程序 
   嵌入式系统通常有许多设备用于与用户交互,象触摸屏、小键盘、滚动轮、传感器、RS232接口、LCD等等。除了这些设备外,还有许多其它专用设备,包括闪存、USBGSM等。内核通过所有这些设备各自的设备驱动程序来控制它们,包括GUI用户应用程序也通过访问这些驱动程序来访问设备。
    开发者编写驱动程序是应该特别注意下面所提到的概念:编写访问硬件的内核代码是不要给用户强加任何策略。因为不同的用户有不同的需求,驱动程序应该处理如何使硬件可用的问题,而将怎样使用硬件的问题留给上层应用。因此,当驱动程序只提供了访问硬件的功能而没有附加任何限制时,这个驱动程序就比较灵活。然而,有时候我们也需要在驱动程序中实现一些策略。例如,某个数字I/O驱动程序只提供了以字节为单位访问硬件的方法,这样就省去了编写额外代码以处理单个数据位的麻烦。
    如果从另一个角度来看驱动程序,那么它可以被看作是应用和实际设备之间的一个软件层。这种定位使开发者可以选择如何展现设备特性:即使对于相同的设备,不同的驱动程序也可以提供不同的功能。实际的驱动程序设计应该在许多考虑因素之间取得平衡。例如,某个驱动程序可能同时被多个进程使用,我们就应该考虑如何处理并发的问题:可以在设备上实现独立于硬件功能的内存映射;也可以提供一个函数库,以帮助应用程序开发者在已有原语的基础上实现新的策略。总之,驱动程序的设计主要还是综合考虑下面三方面的因素:提供给用户尽量多的选项、编写驱动程序占用较少的时间以及尽量保持程序简单而不至于错误丛生。
    本节着重讨论通常几乎在每个嵌入式环境中都会使用的一些重要设备的设备驱动程序。
 
4.1 帧缓冲区驱动程序(FrameBuffer)
    这是最重要的驱动程序之一,因为通过这个驱动程序才能使系统屏幕显示内容。帧缓冲区驱动程序通常有三层。最底层是基本控制台驱动程序
drivers/char/console.c,它提供了文本控制台常规接口的一部分。通过使用控制台驱动程序函数,我们能将文本打印到屏幕上 — 但图形或动画还不能(这样做需要使用视频模式功能,通常出现在中间层,也就是drivers/video/fbcon.c 中)。这个第二层驱动程序提供了视频模式中绘图的常规接口。
    帧缓冲区是显卡上的内存,需要将它内存映射到用户空间以便可以将图形和文本能写到这个内存段上:然后这个信息将反映到屏幕上。帧缓冲区支持提高了绘图的速度和整体性能。这也是顶层驱动程序引人注意之处:顶层是非常特定于硬件的驱动程序,它需要支持显卡不同的硬件方面 — 象启用/禁用显卡控制器、深度和模式的支持以及调色板等。所有这三层都相互依赖以实现正确的视频功能。与帧缓冲区相对应的设备是/dev/fb0(主设备号29,次设备号0)。可以用下面的命令来创建该设备:
#mknod /dev/fb0 c 29 0
    有一个简单的比喻来理解帧缓冲驱动。假设当前需要的显示分辨率是640x480x16bpp(16bpp代表16位色,一般的RGB排列为565,),那么要存储整个屏幕的像素信息所需要的内存大小为640x480x16/8 = 600K bytes(一个像素的16位色需要两个字节来表示)。帧缓冲驱动即在内存中分配了上述大小的一块区域,同时与实际屏幕上的像素一一对应。
    如果我们要改变屏幕上某个像素的颜色显示,首先计算该颜色的16位的RGB排列,然后计算出该像素在内存中的偏移量,最后操作fb0设备,重画该像素。下面的代码将屏幕上的第二排的第二个像素设置成白色:
{
int fd, pixel_addr, pixel_color, ;
 
pixel_addr = (640+1)x16/8;      /* 得到像素偏移地址 */
pixel_color = 0xffff;           /* 设置像素点颜色 */
 
fd = open(/dev/fb0”, O_RDWR); /* 打开framebuffer设备 */
if (fd >= 0)
{
    lseek(fd, pixel_addr, SEEK_SET);    /* 找到该像素 */
    write(fd, pixel_color, 2);          /* 重画该像素 */
}
    现在一般的linux内核已经包含了对帧缓冲驱动的支持,我们要作的只是在内核配置选项中加入它:
#make menuconfig(命令行下),或xconfig(XWindow下)。
    与 frame buffer device有关的选项有(用空格键来进行选中或去除,其余编译选项请参考其它资料):
Code maturity level opetions
   [*] Prompt for development and/or incomplete codes/drivers
   Console drivers
   [*] Video mode selection support
   ...
   [*] Support for frame buffer devices
   ...
   [*] VESA VGA graphics console
   ...
   [*] Advance low level driver options
   ...
[Exit]
[Exit]
Do you wish to save your new kernel configuration?
[Yes]
编译安装内核:
#make dep 
#make bzImage

4.2 输入设备驱动程序
    可触摸板是用于嵌入式设备的最基本的用户交互设备之一 — 小键盘、传感器和滚动轮也包含在许多不同设备中以用于不同的用途。
    触摸板设备的主要功能是随时报告用户的触摸,并标识触摸的坐标。这通常在每次发生触摸时,通过生成一个中断来实现。然后,这个设备驱动程序的角色是每当出现中断时就查询触摸屏控制器,并请求控制器发送触摸的坐标。一旦驱动程序接收到坐标,它就将有关触摸和任何可用数据的信号发送给用户应用程序,并将数据发送给应用程序(如果可能的话)。然后用户应用程序根据它的需要处理数据。
    几乎所有输入设备 — 包括小键盘 — 都以类似原理工作。
    输入设备驱动程序通常需要与上层的GUI应用接口。比如触摸板需要和GUI的鼠标显示结合起来,随着用户触摸位置的不同,屏幕上鼠标的图标移动到相应的位置。一般在GUI中有专门处理外部输入的文件,修改这些文件,可以支持自己的输入设备。

4.3 闪存MTD驱动程序
    MTD
设备是象闪存芯片、小型闪存卡、记忆棒等之类的设备,它们在嵌入式设备中的使用正在不断增长。
    MTD驱动程序是在linux下专门为嵌入式环境开发的新的一类驱动程序。
    相对于常规块设备驱动程序,使用MTD驱动程序的主要优点在于MTD驱动程序是专门为基于闪存的设备所设计的,所以它们通常有更好的支持、更好的管理和基于扇区的擦除和读写操作的更好的接口。

没有更多推荐了,返回首页