• 第八章 让开发板发出声音:蜂鸣器驱动


    一、Linux代码的重用

      重用=静态重用(将要重用的代码放到其他的文件的头文件中声明)+动态重用(使用另外一个Linux驱动中的资源,例如函数、变量、宏等)

      1、编译是由多个文件组成的Linux驱动,即静态重用

      对于复杂的Linux驱动,需要使用多个源代码文件存放不同的功能代码,这样做有利于代码分类和管理,那么就不得不编译多个源代码文件,最终生成.ko文件或编译进Linux内核

      下面,就举例介绍将3个.c文件分别编译为3个.o文件,并将这3个.o文件链接(link)成一个.ko文件——静态重用

      假设C语言源代码文件有main.c、fun.c、product.c、product.h,其中main.c是Linux驱动的主程序,在fun.c和product.c、product.h中定义和实现了在main.c中使用的函数,在main.c中通过extern关键字使用fun.c中的函数,通过包含product.h文件使用product.c文件中的函数

      最重要的一步就是编写Makefile文件

        #Makefile

        obj-m := multi_file_driver(文件所在目录).o

        multi_file_driver-y := main.o fun.o product.o

      总之,c或c++ 语言中编译多个源代码文件时,如果a.c使用了b.c文件中的函数,需要在a.c文件中使用extern预先定义b.c中的函数,extern的作用是告诉编译器该函数的函数名、参数个数、参数类型、返回值类型,等到将a.o和b.o链接成可执行文件或程序库时,编译器再到b.o中寻找函数的具体实现。除此之外,还可以使用b.h文件定义b.c中的函数,然后在a.c中包含b.h文件。

      2、Linux驱动模块的依赖,即动态重用

      在一个驱动模块里使用另一个驱动模块里的被导出的符号(常量、变量、函数等)

      下面,示例由两个Linux驱动组成(symbol_producer和symbol_consumer),其中symbol_producer(symbol_producer.c文件)驱动的两个函数(add和sub)和symbo_const常量以及    result变量被导出,而在symbol_consumer(symbol_concumer.c文件)驱动中则使用了这4个被导出的符号。

      symbol_producer.c 文件部分代码如下://导出add函数

                          EXPORT_SYMBOL(add);

                         //导出result变量

                          EXPRORT_SYMBOL(result);

                         //导出sub函数,使用EXPROT_SYMBOL_GPL导出的符号

                          EXPORT_SYMBOL_GPL(sub);

                         //导出symbol_const常量

                          EXPORT_SYMBOL_GPL(symbol_const);

      symbol_consumer.c文件中部分代码如下:extern const char* symbol_const;//定义被导出的常量

                          extern int result;//定义被导出的变量

                          extern int add(int x1,int x2);//定义被导出的add函数

                          extern int sub(int x1,int x2);//定义被导出的sub函数

      由于有两个Linux驱动,因此需要在Makefile文件中指定两个Linux模块,代码如下:

        #Makefile

        obj-m := symbol_consumer.o

        obj-m +=symbol_producer.o

      注意:在安装symbol_consumer之前,需要先安装symbol_producer;卸载时顺序正好相反。

    (二)强行卸载Linux驱动

      情况1:初始化函数崩溃

      由于Linux驱动模块的初始化函数进行了某些操作而崩溃,从而导致初始化函数无法正常返回,这种情况变现是当前Linux驱动模块没用被任何其他的Linux驱动模块使用,但却显示已经被应用了一次

      这种情况关键是引用计数器的值和引用者不一致。只需要将当前的Linux驱动模块的引用计数器清零即可,修改计数器可以使用下面两个函数

        //是module指向的Linux驱动模块的引用计数器加1,成功返回1,失败返回0

        static inline int try_module_get(struct module *module);

        //是module指向的Linux驱动模块的引用计数器减1

        extern void module_put(struct module *module);

      情况2:卸载函数被阻塞

      在使用rmmod命令卸载Linux驱动时,系统会调用卸载函数,只有卸载函数成功返回时,Linux驱动才会被卸载,如果卸载函数被阻塞,rmmod命令也会被阻塞,也就是说永远不会执行到卸载Linux驱动模块的代码,这种情况的表现是一执行rmmod命令就会停在那不动了,永远也不会返回到系统的操作提示符

      这种情况的问题根源就是卸载函数,只要将原来的卸载函数替换成一个空的卸载函数即可

      总之,两者情况都要解决一个不可回避的问题,就是要获取表示要卸载的Linux驱动模块的module结构体指针。

    (三)让蜂鸣器发声
      蜂鸣器(PWM)驱动--蜂鸣器是开发板上自带的一个硬件。通过相应的寄存器控制蜂鸣器的打开和关闭,并且在实现蜂鸣器的驱动时,将打开和关闭蜂鸣器的函数放到了另外的C源代码中,通过多个*.o文件产生*.ko驱动文件。
      蜂鸣器驱动的原理:蜂鸣器也称为PWM-脉冲宽度调制,基本原理是通过脉冲来控制蜂鸣器的打开和停止。PWM使用端口F的GPFCON寄存器进行控制。该寄存器在Linux内核中对应的宏是S3C64XX_GPFCON,表示GPFCON寄存器的虚拟地址,只用了高两位来控制PWM。当高两位为10时,打开PWM,为00时停止PWM,只需通过iowrite32()为GPFCON寄存器设置不同的值即可。
      实现蜂鸣器驱动的方式为通过I/O命令可以打开和停止PWM。PWM驱动有3个文件,驱动主程序、头文件和包含打开和停止PWM的两个函数的文件。还有编译驱动的Makefile文件。
      测试蜂鸣器驱动的步骤为,先进入/root/drivers/ioctl目录,执行build.sh脚本文件编译和上传ioctl文件。在测试PWM驱动前,先要编译和安装PWM驱动,然后进入开发板终端的/data/local目录,输入命令“# ./ioctl /dev/pwm_dev 1 0”打开PWM。输入命令“# ./ioctl /dev/pwm_dev 0 0”停止PWM。ioctl命令必须要求输入I/O命令的参数,但PWM驱动未使用I/O命令参数,所以ioctl最后一个命令行参数可任意输入。

    http://home.cnblogs.com/u/ligang1307/

  • 相关阅读:
    day7 反射
    day7 面向对象进阶
    day7 面向对象class()学习
    day6 subprocess模块、logging模块
    day6 hashlib模块
    day6 ConfigParser模块 yaml模块
    day6 xml文件格式的处理
    day6 shelve模块
    day6 SYS模块
    Servlet的学习之Response响应对象(1)
  • 原文地址:https://www.cnblogs.com/ligang1307/p/5652494.html
Copyright © 2020-2023  润新知