0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用系统IO和标准IO的基本原理

CHANBAEK 来源:非典型威廉希尔官方网站 宅 作者:非典型威廉希尔官方网站 宅 2023-11-24 09:48 次阅读

系统 IO 和标准 IO

系统 IO 一般指的是 Linux/Unix 系统调用中关于 I/O 操作的统称,其中包括 open、read、write、close 等操作。

与系统 IO 对应还有标准 IO,标准 IO 是 ISO 标准中 C 语言标准定义的 IO 访问接口,例如 fprintf/fgets 等 C 语言标准中定义的文件访问接口。

在 Linux 系统中 open/read/write 等函数的底层实现是通过系统调用访问的,在 STM32 的裸机中没有操作系统,更没有这些系统调用。

但是我们可以用一种其他的方式去实现这些系统 IO,而不需要操作系统。

半主机模式重写文件访问接口

这个方法其实就是利用半主机模式,去重写系统库中关于半主机接口中关于文件访问接口的底层 "弱定义"

这个听上去好像挺陌生的,其实很多人都使用过,就是最简单的 printf 重定向。

在 GCC 重定向 printf 到串口使用了如下代码:

int _write(int fd, char * ptr, int len)
{
  HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);
  return len;
}

这个就是在半主机模式下重写了 write 函数的底层接口,当系统调用 printf 函数时最终会调到 _write 函数向串口写入数据。

ARM 关于主机模式的文档 中,Direct semihosting C library function dependencies 一节提供了可重写的系统 IO 的底层函数。

图片

通过重写上述列表中的函数,即可通过调用 C 库 系统 IO 访问。

构建文件系统

在上面介绍使用系统 IO 的基本原理:通过重写 _open/_write/_read 等接口,即可通过 open/write/read 接口访问。

但是以上只提供了一系列系统接口,并将其与标准 IO 绑定,可以使用 open/fopen 等函数进行访问,但是具体访问的数据依旧需要自己进行实现。

在这次测试中我选用了 LittleFS 作为文件系统,使用 RAM 中预分配的全局变量作为存储介质,构建了一个基于内存的文件系统。(开发板没有 Flash 先用 RAM 代替了。。。)

其 _open 函数如下:

// 文件描述符列表,不包括标准输入输出, 最大 fd 为 FS_FILE_MAX + 3
lfs_file_t *g_file_list[FS_FILE_MAX] = {0};

int _open(const char *name, int flags)
{
  int i;
  int i_flags = 0;

  if ((flags & O_CREAT)  == O_CREAT)  i_flags |= LFS_O_CREAT;
  if ((flags & O_RDONLY) == O_RDONLY) i_flags |= LFS_O_RDONLY;
  if ((flags & O_WRONLY) == O_WRONLY) i_flags |= LFS_O_WRONLY;
  if ((flags & O_RDWR)   == O_RDWR)   i_flags |= LFS_O_RDWR;

  for (i = 0; i < FS_FILE_MAX; i++)
  {
    if (g_file_list[i] == NULL)
    {
      g_file_list[i] = malloc(sizeof(lfs_file_t));
      lfs_file_open(&g_lfs, g_file_list[i], name, i_flags);
      return i + 3;
    }
  }
  
  return -1;
}

其基本逻辑是将 open 传入的参数转换为 lfs_file_open 使用的参数,传入 lfs_file_oen, 然后分配一个空闲的文件描述符作为返回值。

在 _read 和 _write 接口中对文件描述符进行判断,当文件描述符为 0/1/2 时将数据重定向到串口,否则从文件中读写数据。代码如下:

int _write(int fd, char *pBuffer, int size)
{
  int res = 0;

  if (fd == 1 || fd ==2)
  {
    HAL_UART_Transmit(&huart3, (uint8_t *)pBuffer, size, size);
  }
  else
  {
    res = lfs_file_write(&g_lfs, g_file_list[fd], pBuffer, size);
  }

  return res;
}

完成以上步骤后,便可以在程序中使用 open/read/write 等接口访问文件系统了,测试程序如下:

fs_init();

  write(STDOUT_FILENO, "system init ...n", 17);
  
  mkdir("/data", 0755);
  fd = open("/data/ascii.txt", O_CREAT|O_WRONLY);
  
  for (ch = 32; ch < 126; ch++)
  {
    write(fd, &ch, 1);
  }
  close(fd);

  fd = open("/data/ascii.txt", O_RDONLY);
  while (1)
  {
    char buff[16];
    int res = read(fd, buff, 16);
    if (res < 0)
    {
      close(fd);
      break; 
    }

    printf("system tick: %"PRIu32"n", HAL_GetTick());
    printf("read file data:%.*sn", 16, buff);
    HAL_Delay(500);
  }

程序下载烧录后,使用串口工具查看到以下数据:

图片

移植的用途

关于在 STM32 中使用系统 IO 的尝试,主要是为了在 STM32 上移植一些 Linux 下的第三方库。

他们很多都不可避免的使用了文件 IO 和 Posix 线程接口,对于 Posix 线程的接口在 FreeRTOS 中有提供,但是系统 IO 却没有找到什么合适的方案,于是有了这样的一种尝试。

现在好像已经有了更好的方案而不用去移植,不过使用这种方式的好处是以较少的代码可以将系统 IO 和标准 IO 进行关联。

关于半主机模式

最后提一下半主机模式:这个实质上是提供了一个在调试时访问主机数据的方法:

通过触发 SVC 指令,在 R0 寄存器中传入需要的系统调用 ID, 在 R1 寄存器中传入参数结构体的指针。

通过调试器,可以在主机接受到对应的系统调用,并进行相应的处理。

该测试程序整理好后,上传到文末 阅读原文 的 github 链接,或者发送 “测试代码” 到公众号后台获取源码。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    8580

    浏览量

    151029
  • Linux
    +关注

    关注

    87

    文章

    11296

    浏览量

    209348
  • 操作系统
    +关注

    关注

    37

    文章

    6808

    浏览量

    123289
  • STM32
    +关注

    关注

    2270

    文章

    10896

    浏览量

    355767
  • IO口
    +关注

    关注

    3

    文章

    170

    浏览量

    24024
收藏 人收藏

    评论

    相关推荐

    标准IO的介绍

    标准IO由ANSI C标准定义在ANSI C标准中还定义了c库,用于提供一系列的函数标准IO就是
    发表于 04-26 09:53

    GPIO基本原理与寄存器配置基础信息

    GPIO基本原理与寄存器配置基础信息STM32F103ZET6,一共有7组IO口,每组IO口有16个IOUSART:(Universal Synchronous/Asynchronous
    发表于 08-16 07:56

    文件IO标准IO有何区别

    嵌入式Linux开发系统开发之《一节课搞懂文件IO标准IO
    发表于 11-04 06:42

    搞懂文件IO标准IO

    嵌入式Linux开发系统开发之《一节课搞懂文件IO标准IO
    发表于 12-16 08:18

    电源滤波器的基本原理和常用标准

    电源滤波器是电力电子威廉希尔官方网站 中常用的一种装置,用于去除电源中的噪声和高频干扰,保证电子设备的正常运行。下面小编带大家来了解一下电源滤波器的基本原理和常用标准。一、电源滤波器的基本原理电源滤波器是一种电路
    发表于 04-14 15:21

    AVR的IO结构分析与操作

    AVR的IO是真正双向IO结构,由于大部分网友都是从标准51转过来的,受标准51的准双向IO和布尔操作概念影响,没能掌握AVR的
    发表于 11-01 01:21 23次下载

    变跨导乘法器的基本原理

    变跨导乘法器的基本原理 图5.4-25为变跨导乘法器原理图。它利用V1、V2管的跨导GM正比于恒流源电流IO,而IO又受另一个输入电压控制,而实
    发表于 05-18 14:48 3246次阅读
    变跨导乘法器的<b class='flag-5'>基本原理</b>

    网络监控的基本原理标准介绍

    网络监控的基本原理标准介绍
    发表于 11-08 17:46 88次下载

    如何使用io.Reader和io.Writer接口在程序中实现流式IO

    Go 语言标准io 包内有一些常用接口和方法,本文配合图片和实际代码,详细介绍了 io 包。 前言 在 Go 中,输入和输出操作是使用原语实现的,这些原语将数据模拟成可读的或可写的字节流。 为此
    的头像 发表于 07-29 16:46 3295次阅读

    嵌入式Linux开发系统开发之《一节课搞懂文件IO标准IO

    嵌入式Linux开发系统开发之《一节课搞懂文件IO标准IO》http://www.makeru.com.cn/live/5413_2293.html?s=47236
    发表于 11-01 17:37 10次下载
    嵌入式Linux开发<b class='flag-5'>系统</b>开发之《一节课搞懂文件<b class='flag-5'>IO</b>与<b class='flag-5'>标准</b><b class='flag-5'>IO</b>》

    怎样将IO设备分配给IO控制器?

    PROFINET IO 系统由一个 PROFINET IO 控制器和其分配的 PROFINET IO 设备组成。当添加了 IO 控制器和
    的头像 发表于 12-21 09:57 3585次阅读

    使用STM32F10xxx SWJ引脚作为标准IO

    本文介绍了如何使用GPIO固件库来释放SWJ-DP引脚(串行JTAG调试端口),并且重用为标准IO。SWJ-DP IO能够用作标准IOs,它的性能可以达到
    的头像 发表于 12-23 11:47 2849次阅读

    多路IO复用模型和异步IO模型介绍

    多路 IO 复用模型 多路 IO 复用,有时也称为事件驱动 IO。它的基本原理就是有个函数会不断地轮询所负责的所有 socket ,当某个 socket有数据到达了,就通知用户进程。
    的头像 发表于 10-08 17:21 779次阅读
    多路<b class='flag-5'>IO</b>复用模型和异步<b class='flag-5'>IO</b>模型介绍

    信号驱动IO与异步IO的区别

    一. 谈信号驱动IO (对比异步IO来看) 信号驱动IO 对比 异步 IO进行理解 信号驱动IO: 内核将数据准备好的时候, 使用SIGIO
    的头像 发表于 11-08 15:32 1056次阅读
    信号驱动<b class='flag-5'>IO</b>与异步<b class='flag-5'>IO</b>的区别

    电流倒灌揭秘:IO口损坏与系统故障的真相

    导读本期文章将继续深入了解电流倒灌,分析嵌入式系统IO口损坏和系统稳定性问题的根本原因。在上期的工程笔记中,我们了解了电流倒灌并探讨了电流倒灌可能导致的一系列问题,包括
    的头像 发表于 12-11 11:38 258次阅读
    电流倒灌揭秘:<b class='flag-5'>IO</b>口损坏与<b class='flag-5'>系统</b>故障的真相