• Nuttx驱动(一)简介


    第一次写Nuttx系统的驱动,用惯了rt-thread、FreeRTOS等RTOS或裸机的驱动编写。写Nuttx驱动感觉好蹩脚,顺便记录一下(by the way: 先完成,再完善

    Nuttx驱动分类

    Nuttx作为类linux的RTOS,驱动结构、风格与linux很相似

    1. 字符驱动

    例如串口设备、ADC、DAC、CAN、Timer、PWM、编码器、RTC、看门狗、按键等等

    2. 块设备驱动
    3. 特殊设备驱动

    例如网卡、SPI、IIC、LCD、SDIO、USB、MIPI等

    Nuttx驱动简介

    1. 数据结构

    Nuttx通过驱动注册接口,将驱动注册到文件系统中,并实现file_operations操作函数,应用层只需通过标准系统调用,即可调用底层驱动。
    底层驱动有分为上半部分(upper_half)和下半部分(lower_half)
    本质理解:驱动 = 总线 + 功能
    总线:GPIO、SPI、IIC、CAN、USB、串口等;
    功能:读写数据、存储、使能、传输等;

    struct file_operationsfs.h
    struct file_operations
    {
      /* The device driver open method differs from the mountpoint open method */
    
      int     (*open)(FAR struct file *filep);
    
      /* The following methods must be identical in signature and position
       * because the struct file_operations and struct mountp_operations are
       * treated like unions.
       */
    
      int     (*close)(FAR struct file *filep);
      ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
      ssize_t (*write)(FAR struct file *filep, FAR const char *buffer,
                       size_t buflen);
      off_t   (*seek)(FAR struct file *filep, off_t offset, int whence);
      int     (*ioctl)(FAR struct file *filep, int cmd, unsigned long arg);
    
      /* The two structures need not be common after this point */
    
      int     (*poll)(FAR struct file *filep, struct pollfd *fds, bool setup);
    #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
      int     (*unlink)(FAR struct inode *inode);
    #endif
    };
    
    // 与 struct file_operations 相关的数据结构
    struct file
    {
      int               f_oflags;   /* Open mode flags */
      off_t             f_pos;      /* File position */
      FAR struct inode *f_inode;    /* Driver or file system interface */
      FAR void         *f_priv;     /* Per file driver private data */
    };
    
    struct pollfd
    {
      int fd;			/* File descriptor to poll.  */
      short int events;		/* Types of events poller cares about.  */
      short int revents;		/* Types of events that actually occurred.  */
    };
    
    struct inode
    {
      FAR struct inode *i_parent;   /* Link to parent level inode */
      FAR struct inode *i_peer;     /* Link to same level inode */
      FAR struct inode *i_child;    /* Link to lower level inode */
      int16_t           i_crefs;    /* References to inode */
      uint16_t          i_flags;    /* Flags for inode */
      union inode_ops_u u;          /* Inode operations */
    #ifdef CONFIG_PSEUDOFS_ATTRIBUTES
      mode_t            i_mode;     /* Access mode flags */
      uid_t             i_owner;    /* Owner */
      gid_t             i_group;    /* Group */
      struct timespec   i_atime;    /* Time of last access */
      struct timespec   i_mtime;    /* Time of last modification */
      struct timespec   i_ctime;    /* Time of last status change */
    #endif
      FAR void         *i_private;  /* Per inode driver private data */
      char              i_name[1];  /* Name of inode (variable) */
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    1.1. open

    要操作设备,第一步就是要打开相应的设备文件,即使用open()打开设备,其返回一个文件描述符fd。打开设备号之后对该设备的操作可以通过fd来完成。
    应用中open()以设备的节点路径和操作权限为参数,操作进入VFS,调用fs_open.c中的open()函数,通过设备路径找到对应的inode节点,在进程的文件描述符链表中寻找并分配空闲可用的描述符fd和文件file,最后调用设备节点inode中的文件操作file_operation中的函数open()。应用程序调用成功时,返回本次分配的文件描述符fd,发生错误时,返回-1,错误码记录在errno中。

    1.2. close

    关闭设备文件,调用file_operationsclose()函数,释放设备文件、文件描述符fd

    1.3. read

    从设备读取数据。

    • 参数1:文件file指针
    • 参数2:数据buffer
    • 参数3:读取的buffer长度
    1.4. write

    往设备写数据。
    参数同read

    1.5. seek

    查找或调整文件读写位置。

    • 参数1:文件file指针
    • 参数2:文件位置相对偏移
    • 参数3:设置位置起始点
    1.6. ioctl

    用于执行设备特定命令,如设置设备属性、配置设备寄存器等。

    • 参数1:文件file指针
    • 参数2: 控制命令
    • 参数3:命令参数
    1.7. poll

    查询指定的一组文件是否可读或可写。

    首先初始化信号量,用于实现阻塞,直到文件可读或可写(亦可设置超时时间)

    file_operationpoll()函数设计中,如果文件可读、写:

    1. 修改对应的pollfd中的返回事件标志为对应的事件;
    2. 释放信号量。
    • 参数1: poll_fd数组指针
    • 参数2:查询的文件数量
    • 参数3:等待时间
    • 返回正数:可读写的文件数量
    • 返回0:超时
    • 返回-1:错误
    1.8. unlink

    用于已挂载的设备或文件卸载,字符设备一般不涉及;常见于块设备。

    2. 字符设备驱动注册、注销

    Nuttx将驱动设备文件化,即VFS。
    struct file_operations 设备文件操作的方法,通过register_driver接口将驱动设备挂到对应的struct inode节点中,struct inode 描述 了每个设备节点的位置和数据。当系统调用操作设备文件时,根据对应文件的inode就能索引到对应的函数。

    int register_driver(FAR const char *path,
                        FAR const struct file_operations *fops,
                        mode_t mode, FAR void *priv)
    {
      FAR struct inode *node;
      int ret;
    
      /* Insert a dummy node -- we need to hold the inode semaphore because we
       * will have a momentarily bad structure.
       */
    
      ret = inode_semtake();
      if (ret < 0)
        {
          return ret;
        }
    
      ret = inode_reserve(path, mode, &node);
      if (ret >= 0)
        {
          /* We have it, now populate it with driver specific information.
           * NOTE that the initial reference count on the new inode is zero.
           */
    
          INODE_SET_DRIVER(node);
    
          node->u.i_ops   = fops;
          node->i_private = priv;
          ret             = OK;
        }
    
      inode_semgive();
      return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 参数1:设备路径,例如注册一个key驱动到/dev/key
    • 参数2:设备的文件操作指针,指向文件操作实例
    • 参数3:预算的设备访问权限
    • 参数4:为设备驱动传递的私有参数
    • 返回0:注册成功
    • 返回负数:注册失败,错误码
    int unregister_driver(FAR const char *path)
    {
      int ret;
    
      ret = inode_semtake();
      if (ret >= 0)
        {
          ret = inode_remove(path);
          inode_semgive();
        }
    
      return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    Nuttx驱动实例请看下一篇文章
  • 相关阅读:
    Vue + element-ui 【前端项目一】Table 表格并实现分页+搜索 3
    Redis资料整理
    WebDAV之π-Disk派盘 + 言叶
    C#进阶04——委托和事件
    Spring boot(2)
    『CV学习笔记』图像超分辨率等图像处理任务中的评价指标PSNR(峰值信噪比)
    深入Python网络编程:从基础到实践
    HTTP【总结】
    0046【Edabit ★☆☆☆☆☆】【长方形面积】Area of a Rectangle
    SpringBootWeb登录认证
  • 原文地址:https://blog.csdn.net/slimmm/article/details/126519991