# 6. 平台相关接口

# 6.1 文件系统移植

文件系统属于移植 AWTK 时的非必须功能,如果要使用文件模块的功能,比如想把 AWTK 应用的资源文件放到文件系统中管理,则需要对文件系统进行移植。在 AWTK 中,需要开启宏 WITH_FS_RES 来支持文件系统功能,更多关于通用文件模块描述请参考本文附录二。

移植文件系统需要实现 awtk/src/tkc/fs.h 中的相关接口,其中包括:文件操作接口(fs_file_t)、文件夹操作接口(fs_dir_t)以及文件系统操作接口(fs_t),因为接口太多就不在这里一一列举,具体详见本文附录一,完成以上适配工作后,只需实现获取文件系统对象的 os_fs 函数即可,示例代码详见下文。

1、实现 fs.h 中的相关接口

先实现文件读写操作接口 fs_file_vtable_t,以及文件夹遍历接口 fs_dir_vtable_t,代码片段如下:

static int32_t fs_os_file_read(fs_file_t* file, void* buffer, uint32_t size) {
  FILE* fp = (FILE*)(file->data);
  return (int32_t)fread(buffer, 1, size, fp);
}

static int32_t fs_os_file_write(fs_file_t* file, const void* buffer, uint32_t size) {
  FILE* fp = (FILE*)(file->data);
  return fwrite(buffer, 1, size, fp);
}

/* 构建 fs_file_t 对象的虚函数表 */
static const fs_file_vtable_t s_file_vtable = {
  .read = fs_os_file_read,
  .write = fs_os_file_write,
  ...
}

/* 构建 fs_dir_t 对象的虚函数表 */
static const fs_dir_vtable_t s_dir_vtable = {
  .read = fs_os_dir_read,
  ...
};

2、实现 os_fs 函数

接着实现文件系统对象 fs_t,文件对象打开成功后要与上述 fs_file_vtable_t 对象挂接一起。最后实现 os_fs 函数返回全局 fs_t 对象即可,代码片段如下:

/* 打开文件 */
static fs_file_t* fs_os_open_file(fs_t* fs, const char* name, const char* mode) {
  /* 此处使用 fopen 接口实现为例,具体请根据相关平台实现 */
  fs_file_t* f = TKMEM_ZALLOC(fs_file_t);
  f->vt = &s_file_vtable;  /* 挂接fs_file_vtable_t对象 */
  f->data = (void*)fopen(name, mode);
  return f;
}

/* 打开目录 */
static fs_dir_t* fs_os_open_dir(fs_t* fs, const char* name) {
  fs_dir_t* d = TKMEM_ZALLOC(fs_dir_t);
  d->vt = &s_dir_vtable;  /* 挂接fs_dir_vtable_t对象 */
  d->data = opendir(name);
  return d;
}

/* 构建 fs_t 对象:将实现的文件系统操作函数赋值给虚表中对应的函数指针 */
static const fs_t s_os_fs = {.open_file = fs_os_open_file,
                             .open_dir = fs_os_open_dir,
                             ...};
/* 获取 fs_t 对象 */
fs_t* os_fs(void) {
  return (fs_t*)&s_os_fs;
}

此外,在嵌入式平台中,有时没有 posix 兼容的文件系统 API,需要把一些文件系统实现包装成 awtk/src/tkc/fs.h 中的接口,此时可以直接使用 AWTK 提供的文件系统适配器 awtk-fs-adapter (opens new window),目前支持的文件系统如下:

  • FATFS 主要用于访问 TF card。
  • SPIFFS 主要用于访问 Nor Flash。

# 6.2 多线程相关资源

AWTK 本身只有一个 GUI 线程,无需用到多线程相关资源,因此线程相关功能可根据项目实际需求决定是否进行移植。AWTK 中的多线程相关功能的对应的接口文件详见下表:

函数库 接口文件 说明
thread src/tkc/thread.h 线程
mutex src/tkc/mutex.h 互斥锁
cond src/tkc/cond.h 条件变量
semaphore src/tkc/semaphore.h 信号量

需要注意的是,在应用程序中,如果需要在非 GUI 线程向主循环(GUI 线程)发送事件则必须参考本文 5.5.2 章节的内容移植互斥锁,如果在非 GUI 线程中调用 AWTK 内存管理器(awtk/src/tkc/mem.h)中的相关函数,除了需移植互斥锁之外,还需要参考本文 5.2.3 章节内容移植信号量

下文中的示例代码均以 pthread 为例,具体代码实现请参考:awtk/platforms/pc/thread_with_pthread.c。

AWTK 提供了常见 RTOS 的多线程资源实现,具体详见 awtk/src/platforms 目录下对应平台的实现代码,用户可直接加入工程使用。

# 6.2.1 线程

移植 AWTK 的线程需要实现 awtk/src/tkc/thread.h 中的 _tk_thread_t 结构体以及相关接口,结构体代码如下:

struct _tk_thread_t {
  /* 平台无关成员(移植时必须定义) */
  void*             args;       /* 线程回调函数上下文 */
  tk_thread_entry_t entry;      /* 线程回调函数 */
  bool_t            running;    /* 线程是否正在执行 */
  const char*       name;       /* 线程的名称 */
  uint32_t          stack_size; /* 线程的栈大小 */
  uint32_t          priority;   /* 线程的优先级 */

  /* 平台相关成员(移植到其他平台时,请根据实际需求定义) */
  pthread_t thread;  /* 此处以 pthread 为例 */
};

线程相关接口说明详见下表:

接口 说明 备注
tk_thread_create 创建thread对象 保存上下文 ctx 和回调函数 entry
tk_thread_set_name 设置线程名称
tk_thread_set_stack_size 设置线程栈大小
tk_thread_set_priority 设置线程优先级 某些平台可能不支持
tk_thread_get_priority_from_platform 获取平台相关的优先级 某些平台可能不支持
tk_thread_start 启动线程 将 running 属性设置为 TRUE
tk_thread_join 等待线程退出 将 running 属性设置为 FALSE
tk_thread_get_args 获取线程的参数
tk_thread_destroy 销毁thread对象
tk_thread_self 获取当前线程的原生句柄

# 6.2.2 互斥锁

移植 AWTK 的互斥锁需要实现 awtk/src/tkc/mutex.h 中的 _tk_mutex_t 结构体以及相关接口,结构体代码如下:

struct _tk_mutex_t {
  /* 定义一个互斥锁,请根据实际平台实现 */
  pthread_mutex_t mutex;  /* 此处以 pthread 为例 */
};

互斥锁相关接口详见下表:

接口 说明
tk_mutex_create 创建互斥锁对象
tk_mutex_lock 加锁
tk_mutex_try_lock 尝试加锁
tk_mutex_unlock 解锁
tk_mutex_destroy 销毁互斥锁对象

# 6.2.3 信号量

移植 AWTK 的信号量需要实现 awtk/src/tkc/semaphore.h 中的 _tk_semaphore_t 结构体以及相关接口,结构体代码如下:

struct _tk_semaphore_t {
  /* 定义一个信号量,请根据实际平台实现 */
  sem_t* sem;  /* 此处以 sem_t 为例 */
};

信号量相关接口详见下表:

接口 说明
tk_semaphore_create 创建信号量对象
tk_semaphore_wait 获取资源
tk_semaphore_post 释放资源
tk_semaphore_destroy 销毁信号量对象

# 6.2.4 条件变量

移植 AWTK 的条件变量需要实现 awtk/src/tkc/cond.h 中的 _tk_cond_t 结构体以及相关接口,结构体代码如下:

条件变量通常与互斥锁结合使用,一般来说,移植条件变量前需要先移植互斥锁。

struct _tk_cond_t {
  /* 定义一个条件变量,请根据实际平台实现 */
  pthread_cond_t cond;  /* 此处以 pthread 为例 */
};

条件变量相关接口详见下表:

接口 说明
tk_cond_create 创建条件变量对象
tk_cond_wait 等待
tk_cond_wait_timeout 等待指定时间
tk_cond_signal 唤醒
tk_cond_destroy 销毁条件变量对象