Keil环境下CANopenNode移植到STM32问题记录(一)---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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值