文章目录
- 问题描述
- 问题结决
- 思考:
- 相关文章
在直接将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