硬汉嵌入式论坛

 找回密码
 立即注册
查看: 7377|回复: 28
收起左侧

[STM32H7] 分享一个重定向C库部分函数的方法,包含malloc、free、fopen等函数

  [复制链接]

12

主题

153

回帖

204

积分

高级会员

积分
204
发表于 2021-7-16 13:21:20 | 显示全部楼层 |阅读模式
在我们进行嵌入式开发时,难免要移植一些现有的成熟代码库,这些库大部分要借助C标准库中的输入输出接口、内存分配函数等。这也就产生了库函数重定向的问题。我们最常用重定向库函数为printf函数,重定向的方法为:

AC5
  1. #ifndef __MICROLIB  //不勾选微库的话需要关闭半主从模式
  2. #pragma import(__use_no_semihosting)
  3. #endif
  4. int fputc(int ch, FILE *f){
  5.   //ch为要发送的字符
  6.   return HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100)==HAL_OK?ch:-1;
  7. }
复制代码
AC6
  1. #ifndef __MICROLIB //不勾选微库的话需要关闭半主从模式
  2. __asm(".global __use_no_semihosting\n\t")
  3. #endif
  4. int fputc(int ch, FILE *f){
  5. //ch为要发送的字符
  6. return HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100)==HAL_OK?ch:-1;
  7. }
复制代码
有时我们需要重定向malloc、free等内存分配函数,不使用C库的内存分配,这在使用rtos时很常见,重定向malloc、free的方法为:
AC5
  1. #pragma import(__use_no_heap_region)  //声明不使用C库的堆

  2. void *malloc (size_t size){
  3.         return OS_Malloc(size);
  4. }

  5. void free(void* p){
  6.         OS_Free(p);
  7. }

  8. void *realloc(void* p,size_t want){
  9.         return OS_Realloc(p,want);
  10. }

  11. void *calloc(size_t nmemb, size_t size){
  12.         return OS_Calloc(nmemb,size);
  13. }
复制代码
AC6
  1. __asm(".global __use_no_heap_region\n\t"); //声明不使用C库的堆
  2. void *malloc (size_t size){
  3.         return OS_Malloc(size);
  4. }

  5. void free(void* p){
  6.         OS_Free(p);
  7. }

  8. void *realloc(void* p,size_t want){
  9.         return OS_Realloc(p,want);
  10. }

  11. void *calloc(size_t nmemb, size_t size){
  12.         return OS_Calloc(nmemb,size);
  13. }
复制代码
有的时候我们需要重定向C库的文件IO,如在移植LUA解释器时,源码中会调用C库的fopen、fread等函数,需要对这些IO重定向,这里以FATFS为例实现重定向。
最后给出AC5与AC6均适用的例子,包含重定向printf、重定向malloc、free、重定向io,这里需要注意的是,一定要将前面的fputc注释掉,因为在编译时fputc的优先级要大于_sys_write,这就导致printf、fwirte都会重定向到fputc中。
  1. <blockquote>#include <rt_sys.h>
复制代码



评分

参与人数 1金币 +100 收起 理由
eric2013 + 100 很给力!

查看全部评分

回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-7-16 13:23:12 | 显示全部楼层
  1. #include <rt_sys.h>
  2. #include <stdio.h>

  3. #define SUPPORT_FILE_SYSTEM                0
  4. #define SUPPORT_MALLOC                        1

  5. #include "stm32h7xx_hal.h"
  6. extern UART_HandleTypeDef huart1;

  7. //约定返回值为1函数执行成功,0函数执行失败
  8. #define MESSAGE_PRINT(BUF,LEN)  HAL_UART_Transmit(&huart1, (uint8_t*)BUF,LEN, 100)==HAL_OK
  9. #define MESSAGE_SCANF(BUF,LEN) 0
  10. #define OS_Malloc(SIZE)   NULL
  11. #define OS_Free(P)
  12. #define OS_Calloc(N,SIZE) NULL
  13. #define OS_Realloc(P,SIZE) NULL

  14. #if SUPPORT_FILE_SYSTEM
  15.         #include "ff.h"
  16. #endif

  17. #if defined(__clang__)
  18.         __asm(".global __use_no_semihosting\n\t");
  19.         #if SUPPORT_MALLOC
  20.                 __asm(".global __use_no_heap_region\n\t");
  21.         #endif
  22. #elif defined(__CC_ARM)
  23.         #pragma import(__use_no_semihosting)
  24.         #if SUPPORT_MALLOC
  25.                 #pragma import(__use_no_heap_region)
  26.         #endif
  27. #endif

  28. #if defined(__MICROLIB)
  29.         #if defined(__clang__)
  30.                 __asm(".global __use_full_stdio\n\t");
  31.         #elif defined(__CC_ARM)
  32.                 #pragma import(__use_full_stdio)
  33.         #endif
  34. #endif

  35. #define STDIN        0
  36. #define STDOUT        1
  37. #define STDERR        2
  38. #define IS_STD(fn)        ((fn)>=0&&(fn)<=2)
  39. /*
  40. * These names are used during library initialization as the
  41. * file names opened for stdin, stdout, and stderr.
  42. * As we define _sys_open() to always return the same file handle,
  43. * these can be left as their default values.
  44. */
  45. const char __stdin_name[] = ":tt";
  46. const char __stdout_name[] = ":tt";
  47. const char __stderr_name[] = ":tt";


  48. FILEHANDLE _sys_open(const char *pcFile, int openmode)
  49. {
  50. #if SUPPORT_FILE_SYSTEM
  51.         BYTE mode;
  52.         FIL *fp;
  53.         FRESULT fr;
  54. #endif
  55.         if(pcFile==__stdin_name)
  56.                 return STDIN;
  57.         else if(pcFile==__stdout_name)
  58.                 return STDOUT;
  59.         else if(pcFile==__stderr_name)
  60.                 return STDERR;
  61. #if SUPPORT_FILE_SYSTEM
  62.         fp=malloc(sizeof(FIL));
  63.         if(fp==NULL){
  64.                 printf("malloc fail!\r\n");
  65.                 return -1;
  66.         }
  67.         /* http://elm-chan.org/fsw/ff/doc/open.html */
  68.         if(openmode & OPEN_W){
  69.                 mode = FA_CREATE_ALWAYS | FA_WRITE;
  70.                 if (openmode & OPEN_PLUS)
  71.                         mode |= FA_READ;
  72.         }
  73.         else if(openmode & OPEN_A){
  74.                 mode = FA_OPEN_APPEND | FA_WRITE;
  75.                 if (openmode & OPEN_PLUS)
  76.                         mode |= FA_READ;
  77.         }
  78.         else{
  79.                 mode = FA_READ;
  80.                 if (openmode & OPEN_PLUS)
  81.                         mode |= FA_WRITE;
  82.         }

  83.         fr = f_open(fp, pcFile, mode);
  84.         if (fr == FR_OK)
  85.                 return (FILEHANDLE)fp;

  86.         free(fp);
  87. #endif
  88.         return -1;
  89. }

  90. int _sys_close(FILEHANDLE fh)
  91. {
  92. #if SUPPORT_FILE_SYSTEM
  93.         FRESULT fr;
  94. #endif
  95.         if(IS_STD(fh))
  96.                 return 0;
  97. #if SUPPORT_FILE_SYSTEM
  98.         fr = f_close((FIL *)fh);
  99.         if (fr == FR_OK){
  100.                 free((void *)fh);
  101.                 return 0;
  102.         }
  103. #endif
  104.         return -1;
  105. }

  106. int _sys_write(FILEHANDLE fh, const unsigned char *buf,\
  107.         unsigned len, int mode)
  108. {       
  109. #if SUPPORT_FILE_SYSTEM
  110.         FRESULT fr;
  111.         UINT bw;
  112. #endif
  113.         if(fh==STDIN)
  114.                 return -1;
  115.        
  116.         if(fh==STDOUT||fh==STDERR){
  117.                 return MESSAGE_PRINT(buf,len)?0:-1;
  118.         }
  119. #if SUPPORT_FILE_SYSTEM       
  120.         fr = f_write((FIL *)fh, buf, len, &bw);
  121.         if (fr == FR_OK)
  122.                 return len - bw;
  123. #endif       
  124.         return -1;
  125. }

  126. int _sys_read(FILEHANDLE fh, unsigned char *buf,\
  127.         unsigned len, int mode)
  128. {
  129. #if SUPPORT_FILE_SYSTEM
  130.         FRESULT fr;
  131.         UINT br;
  132. #endif
  133.         if(fh==STDOUT||fh==STDERR){
  134.                 return -1;
  135.         }
  136.         if(fh==STDIN){
  137.                 return MESSAGE_SCANF(buf,len)?0:-1;
  138.         }
  139. #if SUPPORT_FILE_SYSTEM               
  140.         fr = f_read((FIL *)fh, buf, len, &br);
  141.         if (fr == FR_OK)
  142.                 return len - br;
  143. #endif
  144.         return -1;
  145. }

  146. void _ttywrch(int ch)
  147. {
  148.         char c = ch;
  149.         MESSAGE_PRINT(&c,1);
  150. }

  151. int _sys_istty(FILEHANDLE fh)
  152. {
  153.         return IS_STD(fh); /* buffered output */
  154. }

  155. int _sys_seek(FILEHANDLE fh, long pos)
  156. {
  157. #if SUPPORT_FILE_SYSTEM
  158.         FRESULT fr;
  159.         if(!IS_STD(fh)){
  160.                 fr = f_lseek((FIL *)fh, pos);
  161.                 if (fr == FR_OK)
  162.                         return 0;
  163.         }
  164. #endif
  165.         return -1;
  166. }

  167. long _sys_flen(FILEHANDLE fh)
  168. {
  169. #if SUPPORT_FILE_SYSTEM
  170.         if (!IS_STD(fh))
  171.                 return f_size((FIL *)fh);
  172. #endif
  173.         return -1;
  174. }
  175. int _sys_ensure(FILEHANDLE fh){
  176. #if SUPPORT_FILE_SYSTEM
  177.         if (!IS_STD(fh)){
  178.                 if(f_sync((FIL*)fh)==FR_OK)
  179.                         return 0;
  180.         }
  181. #endif
  182.         return -1;
  183. }
  184. void _sys_exit(int code){
  185.         printf("This Program unexpected exit! err:%d\r\n",code);
  186.         while(1);
  187. }

  188. int remove(const char *pcPath){
  189. #if SUPPORT_FILE_SYSTEM
  190.         if(pcPath==__stdin_name||pcPath==__stdout_name||pcPath==__stderr_name)
  191.                 return -1;
  192.         if(f_unlink(pcPath)==FR_OK)
  193.                 return 0;
  194. #endif
  195.         return -1;
  196. }

  197. //不清楚如何实现
  198. int _sys_tmpnam(char * name, int sig, unsigned maxlen){
  199.         return 0;
  200. }

  201. //重定向内存分配的相关库函数
  202. #if SUPPORT_MALLOC
  203. void *malloc (size_t size){
  204.         return OS_Malloc(size);
  205. }

  206. void free(void* p){
  207.         OS_Free(p);
  208. }

  209. void *realloc(void* p,size_t want){
  210.         return OS_Realloc(p,want);
  211. }

  212. void *calloc(size_t nmemb, size_t size){
  213.         return OS_Calloc(nmemb,size);
  214. }

  215. #endif
复制代码
回复

使用道具 举报

13

主题

192

回帖

231

积分

高级会员

积分
231
发表于 2021-7-16 15:29:10 | 显示全部楼层
牛,受教了,多谢
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106721
QQ
发表于 2021-7-16 16:16:43 | 显示全部楼层
必须置cool,非常好
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-7-16 16:29:43 | 显示全部楼层
eric2013 发表于 2021-7-16 16:16
必须置cool,非常好

回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-7-16 16:30:29 | 显示全部楼层

回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-7-16 16:34:22 | 显示全部楼层
另外补充资料来源,主要的参考资料为《Arm C and C++ Libraries and Floating-Point Support User Guide Version 6.16》,在MDK的安装目录下就有,以及网友们的一些经验。实测很稳定,在仅修改基础的头文件就可以完成LUA的基础移植
回复

使用道具 举报

4

主题

160

回帖

172

积分

初级会员

积分
172
发表于 2021-7-17 16:11:36 | 显示全部楼层
#ifndef __MICROLIB //不勾选微库的话需要关闭半主从模式
__asm(".global __use_no_semihosting\n\t")
#endif
int fputc(int ch, FILE *f){
//ch为要发送的字符
return HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100)==HAL_OK?ch:-1;
}




#if defined(__MICROLIB)
        #if defined(__clang__)
                __asm(".global __use_full_stdio\n\t");
        #elif defined(__CC_ARM)
                #pragma import(__use_full_stdio)
        #endif
#endif


一个未定义,一个定义。两个都用?就用一个?
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-7-18 00:31:13 来自手机 | 显示全部楼层
如果是简单使用printf的话,就用第一种方式,如果是需要重定向文件io的话就使用第三种方式
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-7-18 00:34:36 来自手机 | 显示全部楼层
fyyxxm 发表于 2021-7-17 16:11
#ifndef __MICROLIB //不勾选微库的话需要关闭半主从模式
__asm(".global __use_no_semihosting\n\t")
#e ...

其实在使能微库的情况下,仅使用printf的话关不关半主从模式都一样,把那个ifndef去掉一样可以正常工作。因此这两种定义方式不矛盾。最后推荐使用我在一楼发的完整版代码。
回复

使用道具 举报

31

主题

76

回帖

169

积分

初级会员

积分
169
发表于 2021-7-19 09:26:28 | 显示全部楼层
mark
回复

使用道具 举报

7

主题

190

回帖

216

积分

高级会员

积分
216
发表于 2021-12-20 11:09:31 | 显示全部楼层
楼主  这种malloc的方式稳定吗
回复

使用道具 举报

3

主题

1222

回帖

1231

积分

至尊会员

积分
1231
发表于 2021-12-20 13:15:29 | 显示全部楼层
WZH 发表于 2021-7-18 00:34
其实在使能微库的情况下,仅使用printf的话关不关半主从模式都一样,把那个ifndef去掉一样可以正常工作。 ...

这种情况下,用不用MicroLib,有什么区别吗? 谢谢!
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-12-20 23:58:08 | 显示全部楼层
morning_enr6U 发表于 2021-12-20 13:15
这种情况下,用不用MicroLib,有什么区别吗? 谢谢!

有区别,使用微库很多文件IO相关函数都需要更改,但会节省一些空间
回复

使用道具 举报

3

主题

1222

回帖

1231

积分

至尊会员

积分
1231
发表于 2021-12-21 08:39:34 | 显示全部楼层
WZH 发表于 2021-12-20 23:58
有区别,使用微库很多文件IO相关函数都需要更改,但会节省一些空间

感谢回复

那资源充足的情况下,不使用MicroLib,在功能上没有影响,对吧?
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-12-21 09:24:50 来自手机 | 显示全部楼层
morning_enr6U 发表于 2021-12-21 08:39
感谢回复

那资源充足的情况下,不使用MicroLib,在功能上没有影响,对吧?

完全没影响,如果不用Microlib,一定要记住关闭半主从模式,那样才能重定向printf等函数,推荐使用我在帖子回复的方案
回复

使用道具 举报

58

主题

267

回帖

446

积分

高级会员

积分
446
发表于 2021-12-21 17:42:49 | 显示全部楼层
以前在 AC6 下实现过这个,现在 Cube MX + Free RTOS,只能用 AC5,结果冒出来这个错误。 QQ图片20211221174104.png

问题是我怎么也找不到 sys_io.o 在哪里,我的工程也没有对应的 C 文件等。。。
真的是搞死人。
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-12-21 23:22:33 | 显示全部楼层
ihavedone 发表于 2021-12-21 17:42
以前在 AC6 下实现过这个,现在 Cube MX + Free RTOS,只能用 AC5,结果冒出来这个错误。

问题是我怎么 ...

把半主从模式关闭了么?
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-12-21 23:24:27 | 显示全部楼层
在源码添加这个命令试试
#pragma import(__use_no_semihosting)
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2021-12-21 23:27:12 | 显示全部楼层
abcde1224 发表于 2021-12-20 11:09
楼主  这种malloc的方式稳定吗

非常稳定,如果不放心,可以先在源码中添加这个代码:
__asm(".global __use_no_heap_region\n\t"); //声明不使用C库的堆
然后调用malloc函数,一般就会提示编译错误。
回复

使用道具 举报

21

主题

61

回帖

124

积分

初级会员

积分
124
发表于 2022-4-2 15:01:58 | 显示全部楼层
版主,像fmount,fformat,等这样的函数是怎样重定向的?
回复

使用道具 举报

75

主题

684

回帖

909

积分

金牌会员

积分
909
发表于 2022-4-2 21:28:40 | 显示全部楼层
很实用的知识,学到了。
回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
发表于 2022-4-6 09:24:03 | 显示全部楼层
这样是一个字节一个字节地发吗?我想使用串口DMA一次性printf出去该如何重定向呢
回复

使用道具 举报

5

主题

51

回帖

66

积分

初级会员

积分
66
QQ
发表于 2022-4-6 10:04:24 | 显示全部楼层
cool
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2022-4-6 10:49:55 | 显示全部楼层
zhumx 发表于 2022-4-2 15:01
版主,像fmount,fformat,等这样的函数是怎样重定向的?

这个不是标准C库的内容,没有相关的重定向函数
回复

使用道具 举报

12

主题

153

回帖

204

积分

高级会员

积分
204
 楼主| 发表于 2022-4-6 11:37:27 | 显示全部楼层
mumuge 发表于 2022-4-6 09:24
这样是一个字节一个字节地发吗?我想使用串口DMA一次性printf出去该如何重定向呢

如果重定向的fputc,那么printf是一个字节一个字节发的,如果重定向的_sys_write,那么这个发送是缓冲发送的,内部有个64字节的缓冲,在缓冲满的时候或者输出\n字符时刷新缓冲,可以主动调用fflush函数刷新输出。
如果使用DMA重定向的话,建议重定向_sys_write,并在函数内部填充DMA发送缓冲区等待DMA发送完毕,结合系统使用消息邮箱+后台任务可以将其设计成非阻塞的发送函数
回复

使用道具 举报

1

主题

30

回帖

33

积分

新手上路

积分
33
发表于 2022-4-18 22:47:30 | 显示全部楼层
mark

      
回复

使用道具 举报

19

主题

62

回帖

119

积分

初级会员

积分
119
发表于 2022-6-2 17:22:55 | 显示全部楼层
请问有人测试过内存分配相关函数的重定向吗?我这边测试字节就挂掉,都起不来了
回复

使用道具 举报

22

主题

47

回帖

113

积分

初级会员

积分
113
发表于 2022-7-1 11:02:30 | 显示全部楼层
mask
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-1 02:12 , Processed in 0.428311 second(s), 37 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表