在直接將CANopenSTM32的示例工程直接移植到Keil環(huán)境下。
如果移植工程未實(shí)現(xiàn)printf函數(shù)重定向,則要注釋掉log_printf下面的printf函數(shù),使日志打印失效
/* Printf function of CanOpen app */
#define log_printf(macropar_message, ...) //printf(macropar_message, ##__VA_ARGS__)
在未在選項(xiàng)中勾選使用微庫(kù)的時(shí)候,程序會(huì)卡死。調(diào)試會(huì)發(fā)現(xiàn)是卡死在了BKAP 0xAB處,網(wǎng)上搜索會(huì)有很多說(shuō)明,是因?yàn)槭褂昧藀rintf函數(shù)而為實(shí)現(xiàn)重定向?qū)е碌?。解決辦法1:勾選上使用微庫(kù)。辦法2:禁用半主機(jī)模式。
選擇辦法1后進(jìn)行編譯,程序可以正常運(yùn)行了。
問(wèn)題描述
因?yàn)槲以谄渌こ躺鲜褂胊rm clang編譯器,因而不能選擇勾選微庫(kù)的方式。因而我嘗試辦法2,但這時(shí)就出現(xiàn)了比較奇怪的問(wèn)題(兩個(gè)編譯器都會(huì)出現(xiàn)這個(gè)奇怪的問(wèn)題,這里是在armcc編譯器下的測(cè)試)
按照我之前文章中https://blog.csdn.net/xiaoyuanwuhui/article/details/110538555描述的如下方式便可以重定向。
/* ------------------通過(guò)重定向?qū)rintf函數(shù)映射到串口1上-------------------*/
#if !defined(__MICROLIB)
#pragma import(__use_no_semihosting)
void _sys_exit(int x) //避免使用半主機(jī)模式
{
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
{
/* 實(shí)現(xiàn)串口發(fā)送一個(gè)字節(jié)數(shù)據(jù)的函數(shù) */
//serial_write(&serial1, (uint8_t)ch); //發(fā)送一個(gè)自己的數(shù)據(jù)到串口
//HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return ch;
}
但是在添加了上述內(nèi)容后,進(jìn)行編譯提示了如下報(bào)錯(cuò):請(qǐng)求禁用半主機(jī)模式,但是_sys_open
函數(shù)未定義
仿照之前定義_sys_exit()
函數(shù)的方式定義_sys_open()
函數(shù),如下:
void _sys_open(int x)
{
x = x;
}
然后進(jìn)行編譯程序又出現(xiàn)了如下報(bào)錯(cuò):_sys_open
函數(shù)重復(fù)定義
至此,一臉懵逼,到底什么情況:不定義提示缺少定義,定義了又提示重復(fù)定義,只能是無(wú)語(yǔ)了。期待有大佬可以詳細(xì)描述下這是怎么回事 ,不勝感激!
問(wèn)題結(jié)決
https://developer.arm.com/documentation/ka002219/latest文章中提供了一種半主機(jī)問(wèn)題的解決方案:使用RTE(Run-Time-Environment)中的Compiler
組件重新定位標(biāo)準(zhǔn)C運(yùn)行時(shí)庫(kù)的I/O函數(shù)。
這里我只針對(duì)于解決上面的問(wèn)題,如上圖所示,在STDOUT處選中,并將后面的值改為User。
將程序中之前重定向的代碼全部移除掉,然后進(jìn)行編譯,這時(shí)仍會(huì)有一個(gè)報(bào)錯(cuò)提示,如下:
這是因?yàn)槲覀冞x擇了重定向輸出,還需要實(shí)現(xiàn)對(duì)應(yīng)的stdout_putchar
函數(shù)(用于打印一個(gè)字符到輸出設(shè)備),一般而言這需要通過(guò)串口實(shí)現(xiàn)發(fā)送一個(gè)字符的功能,這里暫時(shí)先定義一個(gè)空函數(shù):
int stdout_putchar (int ch) {
}
然后再進(jìn)行編譯,結(jié)果如下:
此時(shí)已經(jīng)沒(méi)有報(bào)錯(cuò)了,程序下載到單片機(jī)上也可以正常運(yùn)行了。
思考:
為什么這種方式可以解決問(wèn)題,之前的方式就不可以呢?
這里我們將RTE下的retarget_io.c
文件中的內(nèi)容復(fù)制一份到retarget.c
文件,并添加到工程中,然后將之前勾選的RTE中的STDOUT的取消,在retarget.c文件的前面添加上如下宏定義并注釋掉RTE組件頭文件
//#include "RTE_Components.h"
#define RTE_Compiler_IO_STDOUT_User
#define RTE_Compiler_IO_STDOUT
直接進(jìn)行編譯,也沒(méi)有出現(xiàn)報(bào)錯(cuò),下載到單片機(jī)中也可以正常運(yùn)行。
__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個(gè)函數(shù)注釋掉哪一個(gè)都會(huì)出現(xiàn)如下報(bào)錯(cuò):
好像是要將所有的這幾個(gè)函數(shù)都實(shí)現(xiàn)了才不會(huì)調(diào)用C庫(kù)中的函數(shù)。
進(jìn)一步實(shí)驗(yàn):
將上面的6個(gè)函數(shù)的空語(yǔ)句添加到main.c的空白位置,然后進(jìn)行編譯,發(fā)現(xiàn)不會(huì)報(bào)錯(cuò),但程序下載到單片機(jī)內(nèi)依然會(huì)卡死。
再在6個(gè)函數(shù)的前面添加上__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) {};
然后再進(jìn)行編譯也不會(huì)報(bào)錯(cuò),下載到單片機(jī)內(nèi)也可以正常運(yùn)行。
初步結(jié)論:只有6個(gè)函數(shù)和3個(gè)變量都定義了才能完成完整的重定向功能。
實(shí)際應(yīng)用時(shí)還是建議直接使用RTE的組件,這里拆分出來(lái)是為了進(jìn)一步分析。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-534783.html
相關(guān)文章
std::mt19937 with ARM Compiler 6 uses sys_open and breaks retarget.c
I/O Retargeting文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-534783.html
到了這里,關(guān)于Keil環(huán)境下CANopenNode移植到STM32問(wèn)題記錄(一)---printf重定向問(wèn)題的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!