Keil环境下STM32移植CANopenNode问题记录(一):printf重定向问题解决方案

文章目录

  • 问题描述
  • 问题结决
  • 思考:
  • 相关文章
  • 在直接将CANopenSTM32的示例工程直接移植到Keil环境下。

    如果移植工程未实现printf函数重定向,则要注释掉log_printf下面的printf函数,使日志打印失效

    /* Printf function of CanOpen app */
    #define log_printf(macropar_message, ...) //printf(macropar_message, ##__VA_ARGS__)
    

    在未在选项中勾选使用微库的时候,程序会卡死。调试会发现是卡死在了BKAP 0xAB处,网上搜索会有很多说明,是因为使用了printf函数而为实现重定向导致的。解决办法1:勾选上使用微库。办法2:禁用半主机模式。
    选择办法1后进行编译,程序可以正常运行了。

    问题描述

    因为我在其它工程上使用arm clang编译器,因而不能选择勾选微库的方式。因而我尝试办法2,但这时就出现了比较奇怪的问题(两个编译器都会出现这个奇怪的问题,这里是在armcc编译器下的测试)

    按照我之前文章中https://blog.csdn.net/xiaoyuanwuhui/article/details/110538555描述的如下方式便可以重定向。

    /* ------------------通过重定向将printf函数映射到串口1上-------------------*/
    #if !defined(__MICROLIB)
    
    #pragma import(__use_no_semihosting)
    void _sys_exit(int x) //避免使用半主机模式
    {
      x = x;
    }
    //__use_no_semihosting was requested, but _ttywrch was 
    void _ttywrch(int ch)
    {
        ch = ch;
    }
    typedef struct __FILE
    {
      int handle;
    }FILE;
    
    #endif
    
    #ifdef __GNUC__
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif 
    PUTCHAR_PROTOTYPE
    {
      /* 实现串口发送一个字节数据的函数 */
      //serial_write(&serial1, (uint8_t)ch); //发送一个自己的数据到串口
    	//HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
      return ch;
    }
    
    

    但是在添加了上述内容后,进行编译提示了如下报错:请求禁用半主机模式,但是_sys_open函数未定义

    仿照之前定义_sys_exit()函数的方式定义_sys_open()函数,如下:

    void _sys_open(int x)
    {
      x = x;
    }
    

    然后进行编译程序又出现了如下报错:_sys_open函数重复定义

    至此,一脸懵逼,到底什么情况:不定义提示缺少定义,定义了又提示重复定义,只能是无语了。期待有大佬可以详细描述下这是怎么回事 ,不胜感激!

    问题结决

    https://developer.arm.com/documentation/ka002219/latest文章中提供了一种半主机问题的解决方案:使用RTE(Run-Time-Environment)中的Compiler组件重新定位标准C运行时库的I/O函数。

    这里我只针对于解决上面的问题,如上图所示,在STDOUT处选中,并将后面的值改为User。
    将程序中之前重定向的代码全部移除掉,然后进行编译,这时仍会有一个报错提示,如下:

    这是因为我们选择了重定向输出,还需要实现对应的stdout_putchar函数(用于打印一个字符到输出设备),一般而言这需要通过串口实现发送一个字符的功能,这里暂时先定义一个空函数:

    int stdout_putchar (int ch) {
    
    }
    

    然后再进行编译,结果如下:

    此时已经没有报错了,程序下载到单片机上也可以正常运行了。

    思考:

    为什么这种方式可以解决问题,之前的方式就不可以呢?
    这里我们将RTE下的retarget_io.c文件中的内容复制一份到retarget.c文件,并添加到工程中,然后将之前勾选的RTE中的STDOUT的取消,在retarget.c文件的前面添加上如下宏定义并注释掉RTE组件头文件

    //#include "RTE_Components.h"
    #define RTE_Compiler_IO_STDOUT_User
    #define RTE_Compiler_IO_STDOUT
    

    直接进行编译,也没有出现报错,下载到单片机中也可以正常运行。

    __attribute__((weak))
    FILEHANDLE _sys_open (const char *name, int openmode) {};
    __attribute__((weak))
    int _sys_close (FILEHANDLE fh) {};
    __attribute__((weak))
    int _sys_write (FILEHANDLE fh, const uint8_t *buf, uint32_t len, int mode) {};
    __attribute__((weak))
    int _sys_istty (FILEHANDLE fh) {};
    __attribute__((weak))
    int _sys_seek (FILEHANDLE fh, long pos) {};
    __attribute__((weak))
    long _sys_flen (FILEHANDLE fh) {};
    

    上面这6个函数注释掉哪一个都会出现如下报错:

    好像是要将所有的这几个函数都实现了才不会调用C库中的函数。
    进一步实验:
    将上面的6个函数的空语句添加到main.c的空白位置,然后进行编译,发现不会报错,但程序下载到单片机内依然会卡死。
    再在6个函数的前面添加上__stdin_name,__stdout_name,__stderr_name的定义

    #include <rt_sys.h>
    ///* Standard IO device name defines. */
    const char __stdin_name[]  = ":STDIN";
    const char __stdout_name[] = ":STDOUT";
    const char __stderr_name[] = ":STDERR";
    __attribute__((weak))
    FILEHANDLE _sys_open (const char *name, int openmode) {};
    __attribute__((weak))
    int _sys_close (FILEHANDLE fh) {};
    __attribute__((weak))
    int _sys_write (FILEHANDLE fh, const uint8_t *buf, uint32_t len, int mode) {};
    __attribute__((weak))
    int _sys_istty (FILEHANDLE fh) {};
    __attribute__((weak))
    int _sys_seek (FILEHANDLE fh, long pos) {};
    __attribute__((weak))
    long _sys_flen (FILEHANDLE fh) {};
    

    然后再进行编译也不会报错,下载到单片机内也可以正常运行。
    初步结论:只有6个函数和3个变量都定义了才能完成完整的重定向功能。
    实际应用时还是建议直接使用RTE的组件,这里拆分出来是为了进一步分析。

    相关文章

    std::mt19937 with ARM Compiler 6 uses sys_open and breaks retarget.c
    I/O Retargeting

    物联沃分享整理
    物联沃-IOTWORD物联网 » Keil环境下STM32移植CANopenNode问题记录(一):printf重定向问题解决方案

    发表评论