精华内容
下载资源
问答
  • 1、故障现象 在给单位的一台Server2008R2 X64服务器上安装扫描枪的时候发现...(代码 28)”,就是安装驱动程序有错,但是具体是什么错误,我们从这里是无法得到的。如果以错误提示或者错误代码为关键字上网搜索...

    1、故障现象

           在给单位的一台Server2008R2 X64服务器上安装扫描枪的时候发现无法安装扫描枪驱动,打开设备管理器可以看到扫描枪硬件有黄色叹号标记。错误信息如下:

            Windows给出的错误提示很简单,“该设备的驱动程序未被安装。(代码 28)”,就是安装驱动程序有错,但是具体是什么错误,我们从这里是无法得到的。如果以错误提示或者错误代码为关键字上网搜索的话,相信我你一定得不到太多有用的信息。因为之前另外一台win7x64上安装这个扫描枪驱动是没问题的,所以我知道问题不在驱动本身或者硬件设备上。一开始以为是操作系统版本问题,但08r2和win7本来就是差不多的,绕了一些弯路后,还是决定从错误的根源上找原因。

     2、问题分析

          上网搜索后,知道驱动安装会生成系统日志保持在C:\Windows\inf\setupapi.dev.log\setupapi.dev.log中。打开这个日志找到相关日志信息再来分析问题就非常简单了。这是一个非常值得学习的小技巧,log日志远比图形界面提供的错误信息完整并且更有指导意义。如下是setupapi.dev.log中驱动安装时的相关错误信息节选:

         inf:           Opened INF: 'c:\windows\temp\dmiwu\{de4ae465-6949-463b-9822-287a65fb2b68}\nls_vcp_driver.inf' ([strings])
    !    inf:           Could not find include INF file "layout.inf". Error = 0x00000002
    !    inf:           Unable to load INF: 'C:\Windows\System32\DriverStore\FileRepository\mdmcpq.inf_amd64_neutral_b53453733bd795bc\mdmcpq.inf'(00000003)
    !    inf:           Error 3: The system cannot find the path specified.
    !    inf:           Could not find include INF file "mdmcpq.inf". Error = 0x00000003

           相比设备管理器提供的错误信息,这里的日志足够清楚了,安装驱动的时候因为打不开mdmcpq.inf文件所以驱动安装无法继续报错了。上面一行的layout.inf文件经过分析上下文是不影响安装进行的,重要的还是缺少mdmcpq这个东西。找到驱动的安装目录,在这个扫描枪驱动的inf文件中有如下行:

    [VCP_DriverInstall.NT]
    Include=mdmcpq.inf
    CopyFiles=FakeModemCopyFileSection
    AddReg=VCP_DriverInstall.NT.AddReg

    扫描枪的驱动在安装过程中会用到mdmcpq.inf文件,但是安装时候找不到这个文件,所以出错了。

    3、解决方法

           原因找到了缺少依赖驱动,修复就行了,上网直接搜吧,很容易找到一堆关于mdmcpq缺失的问题。同时也会搜到一些无效的方法,比如复制原版的mdmcpq.inf 和 usbser.sys到system32的inf和drivers目录。

           但是从日志很明显知道驱动查找的是C:\Windows\System32\DriverStore\FileRepository\目录。所以最好的方法还是从正常的系统中直接复制C:\Windows\System32\DriverStore\FileRepository\mdmcpq.inf_amd64_neutral_b53453733bd795bc目录到故障系统的同位置目录下,如果复制时提示文件夹没有访问权限,给FileRepository目录增加用户权限就行了。

    我这里出现故障的系统版本是非原版系统,作者也是小有名气的。但在修改系统过程中难免出现一些隐藏的问题,这种隐藏比较深的故障,对于普通用户来说确实是很难解决的问题。这里非常值得注意的是驱动安装日志用于分析问题的方法,图形化界面虽然友好,但在分析故障原因上有时候的提示太过简化反而变得毫无意义。

     

     

     

    展开全文
  • 步进电机28BYJ-48的驱动(arduino平台),最全的驱动详细原理,驱动电路分析,驱动代码解释 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录 前言 一、pandas...

    步进电机28BYJ-48的驱动(arduino平台,STM32),最全的驱动详细原理,驱动电路分析,驱动代码解释

     


    目录

    步进电机28BYJ-48的驱动(arduino平台,STM32),最全的驱动详细原理,驱动电路分析,驱动代码解释

    前言

    一、步进电机28BYJ-48结构和工作原理

    1.基本知识:

    2.步进电机28BYJ-48结构:

    3.步进电机的控制的基本概念

    4.步进电机28BYJ-48工作原理

    5.步进电机的驱动拍数设定模式

    6.步进电机的驱动参数解读

    二、驱动电路分析和使用

    1.ULN2003驱动电路

    2.L298N小功率驱动电路

    三.驱动代码分析

    1.ULN2003驱动代码分析

    (1)驱动库:

    (2)应用案例:

     2.L298N驱动代码分析

    (1)驱动库

    (2)应用案例

    四、Arduino STM32驱动参考代码下载

    1.ULN200驱动库下载链接:

    2.L298N驱动库下载链接:

    3.STM32驱动库链接:

    总结


     

    前言

    在本网站查找了很多资料,收益匪浅,今来研究了步进电机28BYJ-48的驱动,查了很多资料,发现介绍不够详细,没有抓到关键点,特别是原理,只有原理清楚才能实现精确控制。因此写了本文,借用一些作者的资源,表示感谢,以飨读者,回报大家!


    提示:以下是本篇文章正文内容,下面案例可供参考

    一、步进电机28BYJ-48结构和工作原理

    1.基本知识:

    步进电机:是数字控制电机,它将脉冲信号转变成角位移,即给一个脉冲信号,步进电机就转动一个角度,因此非常适合于单片机控制。步进电机可分为反应式步进电机(简称VR)、永磁式步进电机(简称PM)和混合式步进电机(简称HB),后两种常用。

    步进电机控制特点:1、它是通过输入脉冲信号来进行控制的。2、电机的总转动角度由输入脉冲数决定。3、电机的转速由脉冲信号频率决定。

    2.步进电机28BYJ-48结构

    3.步进电机的控制的基本概念

    4.步进电机28BYJ-48工作原理

    5.步进电机的驱动拍数设定模式

    6.步进电机的驱动参数解读

    二、驱动电路分析和使用

    1.ULN2003驱动电路

     

     

    2.L298N小功率驱动电路

    三.驱动代码分析

    arduino自带的steper驱动库,针对两相电机,而我们驱动的28BYJ48步进电机时四相的,直接使用不可以,笔者试用发现电机嗯嗯响不转动,如果要用自带库,可以对电机接线进行调换改造,或者修改驱动里的拍子表,太麻烦一些。

    我们采用自建的驱动库,知道工作原理,驱动库可以自建。

    1.ULN2003驱动代码分析

    (1)驱动库:

    /*
     * 用法:
    1、把Stepper28BYJ48.zip解压在libraries目录下
    2、电机通过ULN2003驱动板和ULN2803驱动板测试通过
    3、驱动的输入依次接arduino的8~9,步进电机依次接蓝,粉,黄,橙。
     */
    
    // ensure this library description is only included once
    #ifndef Stepper28BYJ48_h
    #define Stepper28BYJ48_h
    
    // library interface description
    class Stepper {
      public:
        // constructors:
        Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2,
                                     int motor_pin_3, int motor_pin_4);
    
        // speed setter method:
        void setSpeed(long whatSpeed);
    
        // mover method:
        void step(int number_of_steps);
    
      private:
        void stepMotor(int this_step);
    
        int direction;            // Direction of rotation
        int speed;                // Speed in RPMs
        unsigned long step_delay; // delay between steps, in ms, based on speed
        int number_of_steps;      // total number of steps this motor can take
        int pin_count;            // how many pins are in use.
        int step_number;          // which step the motor is on
    
        // motor pin numbers:
        int motor_pin_1;
        int motor_pin_2;
        int motor_pin_3;
        int motor_pin_4;
    
        unsigned long last_step_time; // time stamp in us of when the last step was taken
    };
    
    #endif
    
    
    

    (2)应用案例:

    /*
    	控制28BYJ48电机,正转一圈,反转一圈
     */
    
    #include <Stepper28BYJ48.h>
    
    const int stepsPerRevolution = 4096;  //28BYJ48电机旋转一周需要的步数
    
    
    // 电机接在引脚8 ~ 11: 电机线依次为蓝,粉,黄,橙
    Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);
    
    int stepCount = 0;         // number of steps the motor has taken
    
    void setup() {
      // initialize the serial port:
      Serial.begin(9600);
      
      //设置电机转速r/min
      myStepper.setSpeed(10);
    }
    
    void loop() {
      // 正转一圈
      myStepper.step(stepsPerRevolution);
      Serial.print("steps:" );
      Serial.println(stepsPerRevolution);
      delay(5000);
      
      // 反转一圈
      myStepper.step(-stepsPerRevolution);
      Serial.print("steps:" );
      Serial.println(-stepsPerRevolution);
      delay(5000);
      
      // 正转64步
      myStepper.step(64);
      Serial.print("steps:" );
      Serial.println(64);
      delay(5000);
      
      // 正转64步
      myStepper.step(64);
      Serial.print("steps:" );
      Serial.println(64);
      delay(5000);
      
    }

     2.L298N驱动代码分析


    (1)驱动库

    参考网上驱动库,经过我修改完善,运行很好。

    // library interface description
    //二相步进电机可以用arduino IDE 自带的实例里的程序和库驱动,。但是 Steper_28BYJ48四相的驱动不了
    class Steper_28BYJ48 {
      public:
        // constructors:
        Steper_28BYJ48(unsigned int speed_ratio,unsigned int beat_mode, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4);
    
        // mover method:
        void step(int degree_to_move);
    	//set speed
        void setSpeed(unsigned int step_delay);
        void lowPower_idel();
      private:
        unsigned int beat_mode;    //1:EightBeat��2:FourBeat��3:DoubleFourBeat
        unsigned int step_delay;    // delay between steps, in ms, based on speed
        unsigned int speed_ratio ;
    
        // motor pin numbers:
        int motor_pin_1;
        int motor_pin_2;
        int motor_pin_3;
        int motor_pin_4;
    };
    

    (2)应用案例

    应用这个库编写了案例,实现功能,通过串口1进行交互,输入转动角度,实现控制电机转动角度,修改代码可以体验驱动步进电机拍数3种模式,1:EightBeat,2:FourBeat,3:DoubleFourBeat

    代码使用说明在程序头部。

    /*接线:务必不要错了,不然查错非常头大!
      L298输入1-4 对应 arduino数字端口 8,9,10,11
      L298电路板输出有两种接口,一种是有步进电机的卡扣端子ABCD+vcc,一种是输出端子:out1--A+白,OUT2--A-蓝,out3--B+黄,out4--B-绿,电源端接红线5V
      或者 红线接电源5V,橙色电线接out4口,黄色电线接out3口,粉色电线接out2口,蓝色接out1口根据提示进行接线。
      //思路是通过减速比来控制单步执行的脉冲次数,电机是减速比16,电机每一个脉冲前进5.625度,而主轴前进5.625/16=0.3515625度,但经过我测试,这种玩具级的电机主轴低于5.625度步进的精度是无法保障的,因此我简化了代码,每步执行16次脉冲。
    //接线顺序是红->﹢5V,橙黄粉蓝分别接L298N 的OUT1、OUT2、OUT3、OUT4, arduino的8,9,10,11分别接L298N的IN1-IN4
    */
    /*28BYJ-48步进电机的齿轮减速比常见有两种,1:16,1:64,
     * 步距角:θ=360度/(转子齿数J*运行拍数),齿数为8,八拍运行,电机主轴的步距角为θ=360度/(8*8)=5.625度(俗称半步),在除以减速比64,所以输出轴的步距角:5.625/64。
    电机参数表中的减速比这个参数吧——1:64,转子转64圈,最终输出轴才会转一圈,也就是需要64*64=4096个节拍输出轴才转过一圈,2 ms*4096=8192 ms,8秒多才转一圈4096个节拍转动一圈,那么一个节拍转动的角度——步进角度就是360/4096,看一下表中的步进角度参数5.625/64。
    要输出某一角度degree,求输出步数N=(degree*减速比)*(齿数*运行拍数)/360
    启动频率是≥550,单位是 P.P.S,即每秒脉冲数,这里的意思就是说:电机保证在你每秒给出550个步进脉冲的情况下,可以正常启动。那么换算成单节拍持续时间就是 1 s/550=1.8 ms,为了让电机能够启动,我们控制节拍刷新时间大于 1.8 ms 就可以了。
    减速比为1:64情况下
      4步控制信号序列:11.25度/步,主电机32步旋转一周。
      8步控制信号序列:5.625度/步,主电机64步旋转一周,减速轴旋转一周将用:64(步/周)X64(齿轮比) = 4096 步
      测试电机减速比为1:16,8拍模式下旋转一周将用:64(步/周)X16(齿轮比) = 1024 步。*/
    #include "Steper_28BYJ48.h" //自带驱动,也可以加入驱动库
    
    Steper_28BYJ48 steper(16, 1, 8, 9, 10, 11);
    //6个参数为:第1个是步进电机减速比,第2个是步进电机拍数模式设定,1:EightBeat,2:FourBeat,3:DoubleFourBeat,另外4个驱动引脚
    int degree;                  //转动的角度,负值为逆时针方向。
    String inString = "";    // 用于转换数字的字符串缓冲区
    String read_String = "";
    unsigned char dir = 3; //1顺时针方向。clockwise,2逆时针。3,空闲等待。
    
    void setup() {
      Serial.begin(9600);  //初始化串口,作为监视。
      steper.setSpeed(3);//节拍刷新时间,一般要大于2ms// delay between steps, in ms, based on speed,if delay<2ms,lost step.so delay must be more than 3ms
      while (!Serial) {
        ; // wait for serial port to connect. Needed for Leonardo only
      }
      // 发送信息
      Serial.println("Please input MOTOR diretion and turn degree(eg,-24,mean,counterclockwise,24 du)");
    }
    
    void loop() {
      int inChar;
      // 读取串口发送的信息:
      while (Serial.available() > 0)
      {
        inChar = Serial.read();
        read_String += (char)inChar;
        delay(2);
      }
      if (read_String.length() != 0) {
         Serial.println("**********************************************************************");
        Serial.print("serial port read_string:");
        Serial.println(read_String);
        if (read_String.charAt(0) == '-')
        {
          dir = 0;
          Serial.print("turn direction:");
          Serial.println("CounterClockWise");
          degree = (read_String.substring(1)).toInt();
        }
        else
        {
          Serial.print("turn direction:");
          Serial.println("ClockWise");
            dir = 1;
            degree = read_String.toInt();
       
        }
        read_String = "";
      }
      if (dir == 1)//CW
      {
        steper.step(degree);
        steper.lowPower_idel();
            Serial.print("Motor finished degree: ");
            Serial.println(degree);
           Serial.println("**********************************************************************");
    
         dir = 3;   
      }
       if (dir == 0)//CCW
      {
        degree=0-degree;
        steper.step(degree);
        steper.lowPower_idel();
        dir = 3;
            Serial.print("Motor finished degree: ");
            Serial.println(degree);
            Serial.println("**********************************************************************");
    
        }
        degree = 0;
        delay(5000);
     }

    四、Arduino STM32驱动参考代码下载

    1.ULN200驱动库下载链接:

    https://download.csdn.net/download/anchoretor/15176116

    2.L298N驱动库下载链接:

    https://download.csdn.net/download/anchoretor/15176829

    3.STM32驱动库链接:

    https://download.csdn.net/download/anchoretor/15270614


    总结

    1首先需要熟悉步进电机28BYJ-48的机构,工作原理。驱动拍的工作特点。关键:五线四相结构,驱动的步进角公式会计算。

    2..驱动库编写需要针对不同驱动电路的设计,驱动电路的使用工作原理。关键:ULn2003驱动电路特点,输入和输出是反相,输入高电平,驱动输出端拉低,驱动绕组励磁工作。L298N驱动电路特点,输入和输出是同相,输入低电平,驱动输出端拉低,驱动绕组励磁工作。

    3.应用代码分析,通过应用大家熟悉如何使用自建库,同时完成实践应用,知行合一多多练习。

    4.看过有用,鼓励一下我的劳动成果,收藏点个赞呗,能力有限,有错误地方欢迎指正。

    展开全文
  • imx28 leds 平台总线驱动

    千次阅读 2012-11-07 15:32:56
    什么是LED驱动呢,柿子总是软的好吃!其实也不全了,led代码比较少,用它来入门平台总线驱动的写法还是不错的选择滴。IMX28的led驱动是使用PWM调节亮度的,驱动是挂接在linux 标准led platform总线模型上的,...


                 
    imx28  leds 平台总线驱动

    LED驱动从实质上来说很简单,只是在linux平台总线下,要理清这层层的关系还是要费点功夫了。

    为什么是LED驱动呢,柿子总是软的好吃!其实也不全是了,led代码比较少,用它来入门平台总线驱动的写法还是不错的选择滴。IMX28led驱动是使用PWM调节亮度的,驱动是挂接在linux 标准led platform总线模型上的,即leds 子系统。进入正题:

    leds的代码结构:

    # LED Core

    obj-$(CONFIG_NEW_LEDS)+= led-core.o

    obj-$(CONFIG_LEDS_CLASS)+= led-class.o

    obj-$(CONFIG_LEDS_TRIGGERS)+= led-triggers.o

    obj-$(CONFIG_LEDS_GPIO)+= leds-gpio.o 

    obj-$(CONFIG_LEDS_MXS)+= leds-mxs-pwm.o

    主要的文件就这么几个了。先来看看led-core.c 吧,这个文件无比的具有个性和让人兴奋,个性是只有变量的声明,兴奋是代码极其的少。

    DECLARE_RWSEM(leds_list_lock);

    EXPORT_SYMBOL_GPL(leds_list_lock);

    LIST_HEAD(leds_list); 

    EXPORT_SYMBOL_GPL(leds_list);

    所有代码就怎么几行,而且就是声明了2个变量。貌似注定打酱油来得,不过还真不是打酱油哦。leds_list_lock: 防止竞态,并发的锁,leds_list: 链接所有注册的led的全局链表 .2个全局数据无比的重要,后面你就会发现了。


    在开始分析led-class.c之前,先来介绍下一些重要的数据结构:

    /include/linux/leds.h 

    亮度描述:

    1. enum led_brightness {
    2.     LED_OFF    = 0, //熄灭
    3.     LED_HALF    = 127,
    4.     LED_FULL    = 255, 
    5. };

    led的实例 

    1. struct led_classdev {
    2.     const char     *name; 
    3.     int     brightness; //当前亮度,imx28 通过控制pwm控制led端的电压
    4.     int     max_brightness; //最大亮度 LED_FULL
    5.     int     flags; //标志,目前只支持 LED_SUSPENDED 
    6.     /*设置led的亮度,不可以睡眠*/ 
    7.     void     (*brightness_set)(struct led_classdev *led_cdev,
    8.     enum led_brightness brightness);
    9.     /* 获取亮度 */ 
    10.     enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); 
    11.     /* 激活硬件加速闪烁 */ 
    12.     int     (*blink_set)(struct led_classdev *led_cdev,
    13.      unsigned long *delay_on,
    14.      unsigned long *delay_off);

    15.     struct device     *dev;
    16.     struct list_head     node;     /* 串联起所有已经注册的led_classdev */
    17.     const char     *default_trigger;    /* 默认触发器 */
    18.     #ifdef CONFIG_LEDS_TRIGGERS
    19.     /* 这个读写信号量保护触发器数据 */ 
    20.     struct rw_semaphore     trigger_lock;
    21.     struct led_trigger    *trigger; //触发器指针 
    22.      /*触发器使用的链表节点,用来连接同一触发器上的所有led_classdev */ 
    23.     struct list_head     trig_list;
    24.     void     *trigger_data; //触发器使用的私有数据 
    25.     #endif
    26. };


    触发器的结构体 

    1.   Struct led_trigger {
    2.         const char     *name; //触发器名字 
    3.        /*led_classdev和触发器建立连接时会调用这个方法。*/
    4.         void        (*activate)(struct led_classdev *led_cdev);
    5.       /*led_classdev和触发器取消连接时会调用这个方法 */
    6.         void        (*deactivate)(struct led_classdev *led_cdev);

    7.         rwlock_t     leddev_list_lock; //保护链表的锁 
    8.         struct list_head led_cdevs;

    9.        /* 连接下一个已注册触发器的链表节点,所有已注册的触发器都会被加入一个全局链表*/ 
    10.         struct list_head next_trig;
    11.   };

    平台设备相关的led数据结构 

    1. struct led_info {
    2.      const char *name;     //led 的名字
    3.      char *default_trigger; //默认触发器
    4.      int flags;
    5. };

    6. struct led_platform_data {
    7.      int num_leds; //led 个数 imx28 有2个led
    8.      struct led_info *leds; //每个led 信息
    9. };

    在来介绍下和imx28平台相关的数据结构

    arch/arm/plat-mxs/include/mach/device.h

    1. struct mxs_pwm_led {
    2.     struct led_classdev dev; //每个led对应一个led_classdev
    3.     const char *name; //名字
    4.     unsigned int pwm; //pwm通道,最大8个通道
    5. };
    6. struct mxs_pwm_leds_plat_data {
    7.     unsigned int num;
    8.     struct mxs_pwm_led *leds;
    9. };
    10. struct mxs_pwm_leds {
    11.     struct clk *pwm_clk; //pwm 模块的时钟
    12.     unsigned int base; //寄存器基地址
    13.     unsigned int led_num; //led 个数,这里开发板只有2个led
    14.     struct mxs_pwm_led *leds; //指向2个led
    15. };


    继续分析 driver/leds/led-class.c ,初始化函数在sys/class 目录下产生leds子目录,按照leds模型注册的设备都会产生这个目录。


    1. static int __init leds_init(void)
    2. {
    3.     leds_class = class_create(THIS_MODULE, "leds"); 
    4.     if (IS_ERR(leds_class))
    5.         return PTR_ERR(leds_class);
    6.     leds_class->suspend = led_suspend;
    7.     leds_class->resume = led_resume;
    8.     return 0;
    9. }

    10. static void __exit leds_exit(void)
    11. {
    12.     class_destroy(leds_class);
    13. }

    14. static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
    15. static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
    16. #ifdef CONFIG_LEDS_TRIGGERS
    17. static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
    18. #endif

    属性文件,在用户空间可以访问。后面会有提及到.


    1. int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
    2. {
    3.     int rc;
    4.     /*创建device 在leds_class生成的目录下*/
    5.     led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
    6.       "%s", led_cdev->name);
    7.     if (IS_ERR(led_cdev->dev))
    8.         return PTR_ERR(led_cdev->dev);
    9.     /* register the attributes */
    10.     rc = device_create_file(led_cdev->dev, &dev_attr_brightness); 
    11.     //在sys/class/leds/led-pwmx 下创建一个属性文件。 
    12.     if (rc)
    13.         goto err_out;
    14.     #ifdef CONFIG_LEDS_TRIGGERS
    15.     init_rwsem(&led_cdev->trigger_lock);
    16.     #endif
    17.     /* add to the list of leds */
    18.     down_write(&leds_list_lock);
    19.     list_add_tail(&led_cdev->node, &leds_list);//将新的led加入全局链表 
    20.     up_write(&leds_list_lock);
    21.     if (!led_cdev->max_brightness)
    22.         led_cdev->max_brightness = LED_FULL;
    23.     rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
    24.     if (rc)
    25.         goto err_out_attr_max;
    26.     led_update_brightness(led_cdev);//获取led的当前亮度 更新led_cdev->brightness 
    27.     #ifdef CONFIG_LEDS_TRIGGERS
    28.     rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
    29.     if (rc)
    30.         goto err_out_led_list;
    31.     led_trigger_set_default(led_cdev);//为led_cdev设置默认的触发器 
    32.     #endif
    33.     printk(KERN_INFO "Registered led device: %s\n",
    34.     led_cdev->name);
    35.         return 0;
    36.     #ifdef CONFIG_LEDS_TRIGGERS
    37.     err_out_led_list:
    38.     device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
    39.     #endif
    40.     err_out_attr_max:
    41.     device_remove_file(led_cdev->dev, &dev_attr_brightness);
    42.     list_del(&led_cdev->node);
    43.     err_out:
    44.         device_unregister(led_cdev->dev);
    45.     return rc;
    46. }

    led_classdev_register注册的struct led_classdev会被加入leds_list链表.(led-core中定义了这个list)


    注销led_classdev:

    void led_classdev_unregister(struct led_classdev *led_cdev)


    led_brightness_show 和 led_brightness_store 分别显示和设置亮度 

    led_trigger_show用于读取当前触发器的名字,led_trigger_store用于指定触发器的名字, 它会寻找所有已注册的触发器,找到同名的并设置为当前led的触发器。 

    1. static ssize_t led_brightness_show(struct device *dev, 
    2.        struct device_attribute *attr, char *buf)
    3. {
    4.     struct led_classdev *led_cdev = dev_get_drvdata(dev);
    5.     led_update_brightness(led_cdev); 
    6.     return sprintf(buf, "%u\n", led_cdev->brightness); //显示当前亮度
    7. }

    8. static ssize_t led_brightness_store(struct device *dev,
    9.        struct device_attribute *attr, const char *buf, size_t size)
    10. {
    11.     /*取得led_classdev ,放在device的driver_data里。*/
    12.     struct led_classdev *led_cdev = dev_get_drvdata(dev); 
    13.     ssize_t ret = -EINVAL;
    14.     char *after;
    15.     unsigned long state = simple_strtoul(buf, &after, 10); //字符串形式的亮度值转换成数字。
    16.     size_t count = after - buf;
    17.     if (*after && isspace(*after))
    18.     count++;
    19.     if (count == size) {
    20.     ret = count;
    21.     if (state == LED_OFF)
    22.     led_trigger_remove(led_cdev);
    23.     //如果亮度值为0,移除trigger led_cdev->trigger = NULL;
    24.     led_set_brightness(led_cdev, state); //设置亮度值
    25.     }
    26.     return ret;
    27. }


    led_trigger分析 /driver/leds/led-triggers.c 

    注册触发器

    int led_trigger_register(struct led_trigger *trigger)

    注册的trigger会加入到全局链表trigger_list中。

    首选确定触发器的名字不存在,然后遍历所有注册的触发器,如果发现那个led_classdev 的 default_trigger和这个触发器名字相同,就将这个触发器设置成led_classdev 的默认触发器。

    设置触发器上所有的led为某个亮度

    void led_trigger_event(struct led_trigger *trigger, enum led_brightness brightness)

    触发器和led的连接

    void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)//建立连接

    //建立连接的时候会调用触发器的activate方法

    void led_trigger_remove(struct led_classdev *led_cdev)//取消连接。

    取消连接的时候会调用触发器的deactivate方法

    void led_trigger_set_default(struct led_classdev *led_cdev)

    //在所有已注册的触发器中寻找led_cdev的默认触发器并调用 led_trigger_set建立连接

     LED trigger --->>> LED设备模型 ---->>>LED硬件

    trigger好比是控制LED类设备的算法,这个算法决定着LED什么时候亮什么时候暗。LED trigger类设备可以是现实的硬件设备,比如IDE硬盘,也可以是系统心跳等事件。 


    Imx28 led 驱动:

    /driver/leds/leds-mxs-pwm.c


    1. static struct platform_driver mxs_pwm_led_driver = {
    2.     .probe = mxs_pwm_led_probe, 
    3.     .remove = __devexit_p(mxs_pwm_led_remove), 
    4.     .driver = {
    5.     .name = "mxs-leds", 
    6.     },
    7. };
    8. static int __init mxs_pwm_led_init(void)
    9. {
    10.     return platform_driver_register(&mxs_pwm_led_driver);
    11. }
    12. static void __exit mxs_pwm_led_exit(void)
    13. {
    14.     platform_driver_unregister(&mxs_pwm_led_driver);
    15. }

    mxs_pwm_led_init 里面会调用platform_driver_register,最终会调到mxs_pwm_led_driverprobe函数。即mxs_pwm_led_probe。进去一探究竟!

    1. static int __devinit mxs_pwm_led_probe(struct platform_device *pdev)
    2. {
    3.     struct mxs_pwm_leds_plat_data *plat_data;
    4.     struct resource *res;
    5.     struct led_classdev *led;
    6.     unsigned int pwmn;
    7.     int leds_in_use = 0, rc = 0;
    8.     int i;
    9.     plat_data = (struct mxs_pwm_leds_plat_data *)pdev->dev.platform_data;
    10.     //pdev->dev.platform_data 在mx28evk_init_leds()设置了
    11.     if (plat_data == NULL)
    12.         return -ENODEV;
    13.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    14.     //得到资源,即pwm寄存器的物理地址
    15.     if (res == NULL)
    16.         return -ENODEV;
    17.     leds.base = (unsigned int)IO_ADDRESS(res->start); 
    18.      //物理地址转化为虚拟地址(静态映射方式)
    19.     mxs_reset_block((void __iomem *)leds.base, 1);
    20.     leds.led_num = plat_data->num;
    21.     if (leds.led_num <= 0 || leds.led_num > CONFIG_MXS_PWM_CHANNELS)
    22.         return -EFAULT;
    23.     leds.leds = plat_data->leds;
    24.     if (leds.leds == NULL)
    25.         return -EFAULT;
    26.     leds.pwm_clk = clk_get(&pdev->dev, "pwm"); //得到pwm控制器的时钟
    27.     if (IS_ERR(leds.pwm_clk)) {
    28.         rc = PTR_ERR(leds.pwm_clk);
    29.         return rc;
    30.     }
    31.     clk_enable(leds.pwm_clk); //开启时钟
    32.     for (i = 0; i < leds.led_num; i++) {
    33.         pwmn = leds.leds[i].pwm; 
    34.         if (pwmn >= CONFIG_MXS_PWM_CHANNELS) {//检测pwm的通道
    35.         dev_err(&pdev->dev,
    36.         "[led-pwm%d]:PWM %d doesn't exist\n",
    37.         i, pwmn);
    38.         continue;
    39.     }
    40.     led = &(leds.leds[i].dev);
    41.     led->name = leds.leds[i].name;
    42.     led->brightness = LED_HALF;
    43.     led->flags = 0;
    44.     led->brightness_set = mxs_pwm_led_brightness_set;
    45.     led->default_trigger = 0;
    46.     //led_trigger_register 中会设置default_trigger
    47.     rc = led_classdev_register(&pdev->dev, led);
    48.     if (rc < 0) {
    49.         dev_err(&pdev->dev,
    50.         "Unable to register LED device %d (err=%d)\n",
    51.         i, rc);
    52.         continue;
    53.     }
    54.     leds_in_use++;
    55.     /* Set default brightness */
    56.     mxs_pwm_led_brightness_set(led, LED_HALF);
    57.     }
    58.     if (leds_in_use == 0) {
    59.         dev_info(&pdev->dev, "No PWM LEDs available\n");
    60.         clk_disable(leds.pwm_clk);
    61.         clk_put(leds.pwm_clk);
    62.         return -ENODEV;
    63.     }
    64.     return 0;
    65. }

    66. static struct mxs_pwm_led mx28evk_led_pwm[2] = {
    67.     [0] = {
    68.     .name = "led-pwm0",
    69.     .pwm = 0,//通道0
    70.     },
    71.     [1] = {
    72.     .name = "led-pwm1",
    73.     .pwm = 1,//通道1
    74.     },
    75. };

    76. struct mxs_pwm_leds_plat_data mx28evk_led_data = {
    77.     .num = ARRAY_SIZE(mx28evk_led_pwm), 
    78.     .leds = mx28evk_led_pwm,
    79. };
    80. static struct resource mx28evk_led_res = {
    81.     .flags = IORESOURCE_MEM,
    82.     .start = PWM_PHYS_ADDR,
    83.     .end = PWM_PHYS_ADDR + 0x3FFF, //pwm寄存器地址范围
    84. };

    85. static void __init mx28evk_init_leds(void) //系统启动时候调用
    86. {
    87.     struct platform_device *pdev;
    88.     pdev = mxs_get_device("mxs-leds", 0);
    89.     if (pdev == NULL || IS_ERR(pdev))
    90.     return;
    91.     pdev->resource = &mx28evk_led_res; 
    92.     pdev->num_resources = 1;
    93.     pdev->dev.platform_data = &mx28evk_led_data;
    94.     mxs_add_device(pdev, 3);
    95. }
    1. #define BM_PWM_CTRL_PWM_ENABLE    ((1<<(CONFIG_MXS_PWM_CHANNELS)) - 1)
    2. #define BF_PWM_CTRL_PWM_ENABLE(n) ((1<<(n)) & BM_PWM_CTRL_PWM_ENABLE)
    3. #define BF_PWM_PERIODn_SETTINGS     \
    4. (BF_PWM_PERIODn_CDIV(5) | /* divide by 64 */     \
    5.  BF_PWM_PERIODn_INACTIVE_STATE(3) | /* low */
    6.  BF_PWM_PERIODn_ACTIVE_STATE(2) | /* high */     \ 
    7.  BF_PWM_PERIODn_PERIOD(LED_FULL)) /* 255 cycles */ 
    8.  //24MHZ 进行64分频
    9.  //这里代码注释好像有问题。通过查看数据手册,INACTIVE_STATE(3)时 pwm output 
    10.  //定义为1,ACTIVE_STATE(2) 时,为0
    11.  //一个PWM为256个clk

    12. static void mxs_pwm_led_brightness_set(struct led_classdev *pled,
    13.     enum led_brightness value)
    14. {
    15.     struct mxs_pwm_led *pwm_led;
    16.     pwm_led = container_of(pled, struct mxs_pwm_led, dev);
    17.     if (pwm_led->pwm < CONFIG_MXS_PWM_CHANNELS) {
    18.         //先关闭PWM
    19.          __raw_writel(BF_PWM_CTRL_PWM_ENABLE(pwm_led->pwm),
    20.                leds.base + HW_PWM_CTRL_CLR); 
    21.        //设置无效时间和无效时间 
    22.          __raw_writel(BF_PWM_ACTIVEn_INACTIVE(LED_FULL) |
    23.                       BF_PWM_ACTIVEn_ACTIVE(value),
    24.                leds.base + HW_PWM_ACTIVEn(pwm_led->pwm)); 
    25.        //设置周期
    26.        __raw_writel(BF_PWM_PERIODn_SETTINGS,
    27.              leds.base + HW_PWM_PERIODn(pwm_led->pwm));
    28.       //打开PWM
    29.        __raw_writel(BF_PWM_CTRL_PWM_ENABLE(pwm_led->pwm),
    30.              leds.base + HW_PWM_CTRL_SET); 
    31.     }
    32. }


    看看几个主要的寄存器吧:

    1PWM Control and Status Register (HW_PWM_CTRL)PWM0-PWM7的控制,在驱动的probe时候设置,主要是设置PWM通道的可以状态。

    2PWM Channel 0 Active Register

       31:16     INACTIVE     RW         无效时间。 
       15: 0      ACTIVE       RW         有效时间。 

        

    有效和无效只是相对的,因为在PWM Channel n Period Register中有效时间和无效时间都会有对应的状态,在设置寄存器时只要把有效时间及有效时间的状态、无效时间和无效时间的状态对应的设置成我们想要的效果就可以了。 

    3PWM Channel Period Register 

       31:27      RSRVD2       保留位。 
       26        HSADC_OUT   PWM output to high speed ADC

       25        HSADC_CLK_SEL  HSADC clock select for PWM output   

      24      MATT_SEL         多芯片附件模式 

      23      MATT多芯片附件模式 

      22-20   CDIV 时钟分频比率      

      

         0x0 DIV_1 — Divide by 1. 

         0x01 DIV_2 — Divide by 2. 

         0x02DIV_4 — Divide by 4. 

         0x03DIV_8 — Divide by 8. 

         0x04DIV_16 — Divide by 16. 

         0x05DIV_64 — Divide by 64. 

         0x06DIV_256 — Divide by 256. 

         0x07DIV_1024 — Divide by 1024.

       19–18INACTIVE_STATE  有效值的状态

            0x0 表示HI_Z(高阻)。 
            0x2 表示低电平。 
            0x3 表示高电平。 

       17:16   ACTIVE_STATE     无效时间的状态,设置值含义同上。    

       15:0    PERIOD      波形周期,一个波形周期等于此值减一的时钟周期。 

    官方文档给出了个PWM应用的例子:


              

        到此,leds 也分析完毕,现在在用户空间测试:

        先写个简单的sh 

        #vi led.sh

        #!bin/sh

           path=/sys/class/leds/led-pwm1/brightness

           LED_FULL=255

           LED_OFF=0

           LED_HALF=127

           while true

           do

               echo $LED_FULL > $path

               sleep 1

               echo $LED_OFF > $path

               sleep 1

               echo $LED_HALF > $path

               sleep 1

           done


          #chmod u+x led.sh 

          # . ./led.sh  (或者 source led.sh

          开发板的led13种状态之间闪烁.

     

    原始链接

    http://blog.chinaunix.net/uid-27229906-id-3332492.html

    展开全文
  • 一,表驱动是什么? 利用查表的方式,取代一些复杂的判断逻辑 例如: if(month == 1) { days = 31; } else if (month == 2) { days = 28; } else if (month == 3) { days = 31; } ... ... ... else if...

    一,表驱动法是什么?

    利用查表的方式,取代一些复杂的判断逻辑

    例如:

    if(month == 1) {
        days = 31;
    }
    else if (month == 2) {
        days = 28;
    }
    else if (month == 3) {
        days = 31;
    }
    ...
    ...
    ...
    else if(month == 12) {
        days = 31;
    }

    类似于这样的代码可以简化成查表

    int monthTable[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
    days = monthTable[month - 1]; 

    二,表驱动法的优点?

    用表取代复杂逻辑,可读性更好,容易修改

    三,本章内容有什么用,怎么用?

    用表驱动法需要思考两个问题:

    1、数据怎么存?

    2、数据怎么取?

    表的查询方法:直接访问、索引访问、阶梯访问。

    展开全文
  • 是什么原因呢?有没有人知道啊?非常感谢! 另外说一下,其实我是要向受保护的进程中注入DLL,我想用KeAttackStackProcess附加到目标进程之后,调用LoadLibrary加载自己想要加载的DLL,不知道这样可不可行,有没有...
  • 这电机为什么只震动,不转向,我也有些诧异,拿过来去网上找了一个代码自己试一下果然如此,为此我就和这个电机耗上了,翻看了好多论坛资料,都有这个问题,但回答却众说纷纭,也没有搞明白到底哪个才解决方案,...
  • 但是运行时,jdbc的驱动一直无法加载成功,代码没有被运行 后来我在网上查了查,当修改pom.xml时又会报错 ![图片说明](https://img-ask.csdn.net/upload/201806/28/1530167308_912958.png) 想问问还有没有什么办法能...
  • WIN XP蓝屏代码大全

    2013-08-08 12:29:21
    ◇解决方案:既然微软都帮不上忙, 就得靠自己了, 请仔细回想这个错误是什么时候出现的; 第一次发生时你对系统做了哪些操作; 发生时正在进行什么操作. 从这些信息中找出可能的原因, 从而选择相应解决方案尝试排除. 3...
  • Code Compl 代码大全

    2011-05-26 12:09:22
     1.1 什么是软件构建  1.2 软件构建为何如此重要  1.3 如何阅读本书  关键点  第2章 用隐喻来更充分地理解软件开发  2.1 隐喻的重要性  2.2 如何使用软件隐喻  2.3 常见的软件隐喻  软件中的书法:写作代码...
  • javaSE代码实例

    2016-06-21 22:30:18
    11.3.1 什么是异常的再抛出 206 11.3.2 显性再抛出 207 11.3.3 隐性再抛出 209 11.3.4 方法重写对抛出异常声明的约束 210 11.4 定义自己的异常 212 11.4.1 创建自己的异常类 212 11.4.2 使用自定义的...
  • 一、什么是代码? 以下引用业内资深人士的话:低代码是基于可视化和模型驱动理念,结合云原生与多端体验技术,它能 够在多数业务场景下实现大幅度的提效降本,为专业开发者提供了一种全新的高生产力开发范式。另...
  • Java局域网通信——飞鸽传书源代码 28个目标文件 内容索引:JAVA源码,媒体网络,飞鸽传书 Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java...
  • 是什么 我计划在其中为Makersbase TFT28 v4.0开发板开发基本固件的便条纸。 该模块已经关闭,对许多爱好者来说是一个主要的痛点。 随意借用或学习这里的任何作品。 希望它会变成惊人的东西。 免责声明! 截至10月...
  • 2020-05-28

    2020-05-28 13:23:49
    什么eclipse就是连接不上MySQL?有没有哪位大佬帮助一下 这个我到的驱动包 这个运行的代码,用户名,密码都没错 这个显示的异常
  • 3.4 本章的中心思想是什么? 3.5 结论 3.6致谢 第4章 查找 4.1. 耗时 4.2. 问题:博客数据 4.3. 问题:时间,人物,以及对象? 4.4. 大规模尺度的搜索 4.5. 结论 第5章 正确、优美、迅速(按重要性排序):从设计XML...
  • 打开一个文件,判断是否存在,判断编码类型,写入方式追加还是覆盖,接着创建流,写入流,关闭流等等没有十几,二十行代码根本搞不定,还不一定见得对的. 5. 模板的模板 首先,要强调一点,只要可以用文本编辑器编辑的...
  • 该资料《C语言入门经典(第4版)》的源代码及课后练习答案 对应的书籍资料见: C语言入门经典(第4版) 基本信息 原书名: Beginning C: From Novice to Professional, Fourth Edition 原出版社: Apress 作者: ...
  • 该资料《Oracle SQL高级编程》的源代码 对应的书籍资料见: Oracle SQL高级编程(资深Oracle专家力作,OakTable团队推荐) 基本信息 原书名: Pro Oracle SQL 原出版社: Apress 作者: (美)Karen Morton Kerry ...
  • C++程序设计语言(特别版)--源代码

    热门讨论 2012-04-23 07:33:51
    2.1 为什么是c++ 19 2.2 程序设计范型 19 2.3 过程式程序设计 20 2.3.1 变量和算术 21 2.3.2 检测和循环 22 2.3.3 指针和数组 23 2.4 模块程序设计 23 2.4.1 分别编译 24 2.4.2 异常处理 25 2.5 数据抽象 ...
  • 2.1 为什么是c++ 19 2.2 程序设计范型 19 2.3 过程式程序设计 20 2.3.1 变量和算术 21 2.3.2 检测和循环 22 2.3.3 指针和数组 23 2.4 模块程序设计 23 2.4.1 分别编译 24 2.4.2 异常处理 25 2.5 数据抽象 ...
  • 10 在python2中的继承顺序是什么 11 在子类中调用父类方法 12 super调用父类的方法 13 选择系统作业讲解 第26章 01 学生自主复习 02 分享列表 03 多态 04 封装 05 面向对象概念总结 06 反射 07 反射及动态导入模块...
  • 任务: ...初步判断串口初始化函数的问题,仔细检查了代码,和网上的做对比,修改了一些细节,应该没有什么问题; 代码如下: void uart_init(u32 bound) { GPIO_InitTypeDef GPIO_InitStructure;
  • 1.1 什么是Ajax 3 1.1.1 Ajax构成要素 3 1.1.2 异步Web编程 5 1.1.3 XMLHttpRequest对象 7 1.1.4 Ajax开发问题 10 1.2 ASP.NET AJAX架构 11 1.2.1 客户端框架 12 1.2.2 服务器框架 13 1.2.3 客户端中心开发...
  • 该资料《OCPOCA认证考试指南全册:Oracle Database 11g(1Z0-051,1Z0-052,1Z0-053)》的随书源代码 对应的书籍资料见: OCPOCA认证考试指南全册:Oracle Database 11g(1Z0-051,1Z0-052,1Z0-053) 基本信息 ...
  • 在这种访问中值的我们注意的虽然JAVA"Write Once ,Run Anywhere",但是如果通过这种访问的话,需要客户端必须设置ODBC和有相应的数据库客户机的驱动,当你看了下面的另外一个流程的时候或许你会想:明明下一种更...
  • 根绝官方的说法根本没有,必须去看你的网卡到底是什么驱动,到底支持哪些操作 这部分的调试工作,主要集中在驱动部分的代码,示例程序给出的错误提示一点用都没有 很多网卡并不能支持drop操作,也就是第一个比如必须...
  • 作业2:总结实现HC-SR04驱动,都有什么步骤。 作业3(选做):有ENC28J60或ESP8266或RW007模块的,可以尝试将小车传感器数据发送到onenet云平台。 【注:没有小车的,可以使用其他传感器,按照文档中心...

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 279
精华内容 111
关键字:

代码28是什么驱动