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.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:设备路径,例如注册一个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;
}
Nuttx驱动实例请看下一篇文章
### Nuttx操作系统驱动开发入门 Nuttx种实时嵌入式操作系统,广泛应用于资源受限的设备上。对于 Nuttx驱动开发,通常涉及硬件抽象层 (HAL)设备驱动程序的设计与实现。 #### 1. 设备驱动的基础结构 在 Nuttx 中,设备驱动程序通常是通过 `struct file_operations` 来定义的。该结构体包含了各种文件操作回调函数指针,例如打开 (`open`)、关闭 (`close`)、读取 (`read`) 和写入 (`write`) 等功能[^5]。开发者需要根据具体的硬件特性实现这些接口。 以下是典型的驱动注册代码示例: ```c #include <nuttx/fs/ioctl.h> #include <nuttx/fs/fs.h> static int my_open(struct file *filep, const char *path, int oflags, mode_t mode); static int my_close(struct file *filep); static ssize_t my_read(struct file *filep, char *buffer, size_t buflen); static ssize_t my_write(struct file *filep, const char *buffer, size_t buflen); static const struct file_operations my_fops = { .open = my_open, .close = my_close, .read = my_read, .write = my_write, }; int register_my_driver(void) { return register_driver("/dev/mydevice", &my_fops, 0666, NULL); } ``` 上述代码展示了如何定义个简单的字符型设备驱动,并将其挂载到 `/dev/mydevice` 路径下[^5]。 --- #### 2. GPIO 驱动开发 GPIO(通用输入输出)是许多嵌入式应用中的基础组件之,在 Nuttx 中可以通过特定的 HAL 接口访问 GPIO 引脚状态。以下是个基本的 GPIO 控制示例: ```c #include <nuttx/gpio.h> void configure_gpio(int pin_number, bool output_mode) { if (output_mode) gpio_config(pin_number, OUTPUT_PIN); // 设置为输出模式 else gpio_config(pin_number, INPUT_PIN); // 设置为输入模式 } bool read_gpio_state(int pin_number) { return gpio_read(pin_number); } void set_gpio_state(int pin_number, bool state) { gpio_write(pin_number, state ? HIGH : LOW); } ``` 此代码片段演示了如何配置 GPIO 引脚以及对其进行读写操作[^6]。 --- #### 3. I2C/SPI 驱动开发 除了 GPIO 外,I2C 和 SPI 协议也是常见的外设通信方式。下面展示了个基于 Nuttx 的 I2C 总线传输示例: ```c #include <nuttx/i2c/i2c_master.h> int i2c_transfer_example(FAR struct i2c_master_s *i2c_dev, uint8_t address, FAR uint8_t *data_out, size_t out_len, FAR uint8_t *data_in, size_t in_len) { struct i2c_msg_s msg[2]; memset(msg, 0, sizeof(msg)); /* 发送数据 */ msg[0].addr = address; msg[0].flags = 0; // 表明这是发送方向 msg[0].buffer = data_out; msg[0].length = out_len; /* 接收数据 */ msg[1].addr = address; msg[1].flags = I2C_M_READ; // 表明这是接收方向 msg[1].buffer = data_in; msg[1].length = in_len; return I2C_TRANSFER(i2c_dev, msg, 2); } ``` 这段代码实现了向指定地址的 I2C 设备发送命令并读回响应的功能[^7]。 --- #### 4. 定时器驱动开发 定时器驱动可以用于周期性触发事件或者延迟执行某些任务。Nuttx 提供了套标准 API 来管理定时器中断和服务请求。以下是段创建软定时器的例子: ```c #include <nuttx/timers/hrtimer.h> static void timer_callback(hrt_timer_handle_t handle, hrt_abstime t) { printf("Timer expired at %llu\n", (unsigned long long)t); } hrt_timer_handle_t create_hardware_timer(unsigned int period_us) { hrt_init(); // 初始化高分辨率计时器子系统 hrt_timer_handle_t handle = hrt_allocate(); if (!handle) return NULL; hrt_callout callout = {timer_callback}; hrt_start(handle, HRT_USEC(period_us), HRT_PERIODIC, &callout); return handle; } ``` 这里介绍了如何利用 Nuttx 的高精度计时器库启动个具有固定间隔时间的任务调度循环[^8]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值