printf 函数重定向到UART串口输出

printf 函数重定向到UART串口输出

快捷注释所有 printf() 调用语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#if 1
#define PRINTF printf
#else
#define PRINTF(...)
#endif

/* 或者分模块注释printf,方便调试,例如ADC和TIM1模块内
* 进行调试信息打印时,可分别调用各自的printf宏定义别名,
* 这么做可以在程序调试期间方便对某一模块的调试信息功能启用和关闭
*/
#if 1
#define ADC_PRINTF printf
#else
#define ADC_PRINTF(...)
#endif

#if 1
#define TIM1_PRINTF printf
#else
#define TIM1_PRINTF(...)
#endif

printf 动态打印已知长度字符串

1
2
3
4
5
/* 动态打印已知长度的字符串 */
printf("%.*s\r\n", length, str);

/* 动态打印已知长度的字符串,totalLen指最终打印字符串的长度,strLen指打印str的多少个字符 */
printf("%*.*s\r\n", totalLen, strLen, str);

Keil MDK, GCC ( True Studio / NXP S32 Design Studio / … )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#ifdef __GNUC__
/* 若需打印浮点数 则需要在
* 工程属性->C/C++ Build->Settings->C Linker->Miscellaneous->Other options中添加 -u _printf_float
* 以使能浮点打印功能,此外,使能浮点打印功能,编译后会明显增加RAM和FLASH占用
*/
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)

#ifndef __MICROLIB
/* 加入以下代码,支持printf函数,而不需要选择use MicroLIB */
#pragma import(__use_no_semihosting)

/* 标准库需要的支持函数 */
struct __FILE {
int handle;
};

/* FILE 定义在 stdio.h */
FILE __stdout;

/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
#endif
#endif /* __GNUC__ */

//#define USE_REG_FOR_PRINTF

PUTCHAR_PROTOTYPE {
#if defined(USE_REG_FOR_PRINTF)
/* 寄存器方式 */

/* MM32 */
// while ((UART2->CSR & UART_FLAG_TXEMPTY) == RESET);
// UART2->TDR = (ch & (uint16_t)0x00FF);

/* STM32 不同型号的寄存器及相关FLAG定义有所不同,按需选择 */
#if 0
while ((USART2->SR & USART_FLAG_TXE) == RESET);
USART2->DR = (uint8_t)ch;
while ((USART2->SR & USART_FLAG_TC) == RESET);
#else
while ((USART2->ISR & USART_ISR_TXE) == RESET);
USART2->TDR = (uint8_t)ch;
while ((USART2->ISR & USART_ISR_TC) == RESET);
#endif

#else
#if defined(USE_STDPERIPH_DRIVER)
/* SPL标准库方式 */
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, (uint8_t)ch);
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
#endif

#if defined(USE_FULL_LL_DRIVER)
/* LL库方式 */
while (LL_USART_IsActiveFlag_TXE(USART2) == RESET);
LL_USART_TransmitData8(USART2, ch);
while (LL_USART_IsActiveFlag_TC(USART2) == RESET);
#endif /* USE_FULL_LL_DRIVER */

#if defined(USE_HAL_DRIVER)
/* HAL库方式 */
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
#endif /* USE_HAL_DRIVER */

#endif /* USE_REG_FOR_PRINTF */
return ch;
}

自定义一个可传不定量参数的printf函数

1
2
3
4
5
6
7
8
9
10
11
12
void SCI_printf(const char *format, ...)
{
char TX_Buffer[256];
va_list args;

va_start(args, format);
// va_starttop(args, format);
vsprintf(TX_Buffer, format, args);
va_end(args);

LPUART_DRV_SendDataBlocking(INST_LPUART1, (const uint8_t *)TX_Buffer, strlen((const char *)TX_Buffer), 50U);
}
1
2
3
4
5
6
7
8
9
/* SEGGER J-Link RTT Viewer PC客户端快捷方式参数设置 */
/* 长参数: */
-device STM32F031K6 -connection usb -interface swd -speed 4000 -autoconnect
/* 短参数: */
-d STM32F031K6 -ct usb -if swd -s 4000 -a

/* 建议使用短参数,长参数可能不生效。 */
/* 示例,如下设置 "目标" 项: */
"C:\Program Files (x86)\SEGGER\JLink_V644g\JLinkRTTViewer.exe" -d STM32F031K6 -ct usb -if swd -s 4000 -a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
RTT软件包一般都在Segger J-Link驱动的安装目录下:
C:\Program Files (x86)\SEGGER\JLink_V644g\Samples\RTT

将压缩包内最关键的RTT目录内的几个源文件添加到工程中,并在
SEGGER_RTT_Conf.h 内配置相关Buffer的大小即可开始使用,
若需重定向 printf() 函数,则需要根据 IDE 所使用的工具链,
添加 Syscalls 文件夹内的对应源文件,进行重定向。
*/
#if 1
#define PRINTF SEGGER_RTT_printf
#else
#define PRINTF(...)
#endif

SEGGER_RTT_Init();

SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);


/* TODO: J-Link RTT 使用示例 */
PRINTF(0, "%sCounter: %s%s%u%s ", RTT_CTRL_RESET, RTT_CTRL_BG_BRIGHT_RED, RTT_CTRL_TEXT_BRIGHT_WHITE, cnt++, RTT_CTRL_RESET);
SEGGER_RTT_WriteString(0, "SEGGER Real-Time-Terminal Sample\r\n");
SEGGER_RTT_WriteString(0, "###### Testing SEGGER_printf() ######\r\n");

PRINTF(0, "printf Test: %%s, \"RTT\" : %s.\r\n", "RTT");
PRINTF(0, "%sprintf Test: %s%%u, 12345 : %u.%s\r\n", RTT_CTRL_TEXT_WHITE, RTT_CTRL_TEXT_BRIGHT_RED, 12345, RTT_CTRL_RESET);
PRINTF(0, "%sprintf Test: %s%%+u, 12345 : %+u.%s\r\n", RTT_CTRL_TEXT_BRIGHT_WHITE, RTT_CTRL_TEXT_BRIGHT_GREEN, 12345, RTT_CTRL_RESET);
PRINTF(0, "%sprintf Test: %s%%.3u, 12345 : %.3u.%s\r\n", RTT_CTRL_TEXT_WHITE, RTT_CTRL_TEXT_BRIGHT_YELLOW, 12345, RTT_CTRL_RESET);
PRINTF(0, "%sprintf Test: %s%%.6u, 12345 : %.6u.%s\r\n", RTT_CTRL_TEXT_BRIGHT_WHITE, RTT_CTRL_TEXT_BRIGHT_BLUE, 12345, RTT_CTRL_RESET);
PRINTF(0, "%sprintf Test: %s%%6.3u, 12345 : %6.3u.%s\r\n", RTT_CTRL_TEXT_WHITE, RTT_CTRL_TEXT_BRIGHT_MAGENTA, 12345, RTT_CTRL_RESET);
PRINTF(0, "%sprintf Test: %s%%8.6u, 12345 : %8.6u.%s\r\n", RTT_CTRL_TEXT_BRIGHT_WHITE, RTT_CTRL_TEXT_BRIGHT_CYAN, 12345, RTT_CTRL_RESET);
PRINTF(0, "%sprintf Test: %s%%08u, 12345 : %08u.%s\r\n", RTT_CTRL_TEXT_WHITE, RTT_CTRL_TEXT_BRIGHT_WHITE, 12345, RTT_CTRL_RESET);
PRINTF(0, "printf Test: %%p, &_Cnt : %p.\r\n", &_Cnt);

SEGGER_RTT_WriteString(0,
RTT_CTRL_RESET"Red: " \
RTT_CTRL_TEXT_BRIGHT_RED"This text is red. " \
RTT_CTRL_TEXT_BLACK"" \
RTT_CTRL_BG_BRIGHT_RED"This background is red. " \
RTT_CTRL_RESET"Normal text again.\r\n"
);

SEGGER_RTT_WriteString(0, "###### SEGGER_printf() Tests done. ######\r\n\r\n");

/* 在Terminal 1输出信息 */
SEGGER_RTT_TerminalOut(1, RTT_CTRL_TEXT_BRIGHT_RED"Counter overflow!\r\n"RTT_CTRL_RESET);

/* 清空 J-Link RTT Viewer 的输出显示 */
//SEGGER_RTT_WriteString(0, RTT_CTRL_CLEAR);

评论