华大HC32L13系列国产32单片机开启串口打印(汇总多种方法)
官方demo存在的问题
使用keil5 MDK进行开发时,无法使用printf()
函数进行串口调试,按照网上关于ARM单片机的相关教程,在使用微库(即打开keil软件中的Use MicroLIB
选项)时测试发现依然无用。查看代码:
发现Debug_Output()
函数被注释掉了!
即使去掉注释也无法使用printf()
函数。
解决方法一
参考下面内容修改库文件ddl.c
下的Debug_Output()
函数(大概在173行),然后再配置UART0的RXD和TXD引脚即可使用UART0进行串口打印。如果要替换成其他串口,则需要将Debug_Output()
函数中的UART0
替换成其他的串口即可。
void Debug_Output(uint8_t u8Data)
{
//M0P_UART0->SCON_f.REN = 0;
//M0P_UART0->SBUF = u8Data;
//
//while (TRUE != M0P_UART0->ISR_f.TI)
//{
// ;
//}
//M0P_UART0->ICR_f.TICLR = 0;
M0P_UART0->SCON_f.REN = 0;
M0P_UART0->SBUF = u8Data;
while (TRUE != M0P_UART0->ISR_f.TC)
{
;
}
M0P_UART0->ICR_f.TCCF = 0;
}
亲测,当打开keil MDK中的有关微库(即Use MicroLIB
选项)时候,仅仅更改Debug_Output()
函数即可。
但如果没有开启微库(即Use MicroLIB
选项)时候,则需要在库文件ddl.c
中取消ARM的半主机工作模式(大概在208行):
#if defined (__CC_ARM) //KEIL
#pragma import(__use_no_semihosting) //取消ARM的半主机工作模式
void _sys_exit(int x) //定义_sys_exit()以避免使用半主机模式
{
x = x;
}
struct __FILE //标准库需要的支持函数
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
/* FILE is typedef?d in stdio.h. */
FILE __stdout;
#endif
解决方法二
在库文件ddl.h
中添加引用:#include "uart.h"
,然后参考下面内容修改库文件ddl.c
下的fputc()
函数(大概在231行),再配置UART0的RXD和TXD引脚即可使用UART0进行串口打印。如果要替换成其他串口,则需要将该函数中的M0P_UART0
替换成其他的串口(如M0P_UART1
)即可。
#ifdef __DEBUG
/**
******************************************************************************
** \brief Re-target putchar function
******************************************************************************/
int fputc(int ch, FILE *f)
{
// if (((uint8_t)ch) == '\n')
// {
// Debug_Output('\r');
// }
// Debug_Output(ch);
Uart_SendDataPoll(M0P_UART0, ch); //串口未启用发送中断模式,即查询模式发送数据
// Uart_SendDataIt(M0P_UART0, ch); //串口开启了发送中断模式
return ch;
}
#endif
亲测,当打开keil MDK中的有关微库(即Use MicroLIB
选项)时候,仅仅更改fputc()
函数即可,若关闭微库,则需要按照方法一中的内容打开ddl.c
中208~224行的代码。此外,Uart_SendDataPoll()
和Uart_SendDataIt()
函数只能打开其中之一,根据实际情况选择!
解决方法三
在库文件ddl.h
中添加引用:#include "uart.h"
,然后参考下面内容修改库文件ddl.c
下的fputc()
函数(大概在231行),再配置UART0的RXD和TXD引脚即可使用UART0进行串口打印。如果要替换成其他串口,则需要将该函数中的M0P_UART0
替换成其他的串口(如M0P_UART1
)即可。
#ifdef __DEBUG
/**
******************************************************************************
** \brief Re-target putchar function
******************************************************************************/
int fputc(int ch, FILE *f)
{
// if (((uint8_t)ch) == '\n')
// {
// Debug_Output('\r');
// }
// Debug_Output(ch);
while ( 0 == (M0P_UART0->ISR & 0x08))
{
;
}
M0P_UART0->SBUF_f.DATA = (unsigned char)ch;
return ch;
}
#endif
亲测,当打开keil MDK中的有关微库(即Use MicroLIB
选项)时候,仅仅更改fputc()
函数即可,若关闭微库,则需要按照方法一中的内容打开ddl.c
中208~224行的代码。
ddl.c
原始位置:hc32l13x_ddl_Rev1.9.2 Lite\driver\src\ddl.c
ddl.h
原始位置:hc32l13x_ddl_Rev1.9.2 Lite\driver\inc\ddl.h
扩展知识
1,ARM内核单片机的半主机模式
① 一种用于ARM处理器或者模拟器与PC主机之间进行输入输出(键盘输入和PC屏幕显示输出 )通信(通过串口、网口、通用总线、USB等的通信)的机制,即可将来自应用程序代码的输入/输出请求传送至运行调试器的主机。 例如,使用此机制可以启用 C 库中的函数,如
printf()
和scanf()
,来使用主机的屏幕和键盘,而不是在目标系统上配备屏幕和键盘。
② ARM处理器的模拟器使用的是半主机模式,使得我们无需添加某些头文件或者指令即可使用半主机模式中的一些接口或者命令,比如HLT、SVC、BKPT等。但是那是适用于模拟器的,并不适用于一个实际的嵌入式硬件开发板,因为大多情况下我们的开发板是不会接入一个键盘来获取数据的, 比如我们要实现的调试串口的驱动,我们希望调用
printf()
函数来让UART输出数据, 我们需要重写fputc()
这个函数才行。
③ 非半主机模式下运用C库,只需要在程序中任意地方添加以下代码即可:
#pragma import(__use_no_semihosting)
,即取消ARM的半主机工作模式。关于如何重定向printf()
(即重写fputc()
),在C库手册中有示例:
2,MicroLib
microlib是C库中的一种,它是默认C库的替代库文件(缺省c库的备选库),它提供了一个有限的stdio子系统,仅支持未缓冲的stdin、stdout和stderr。
特点:
① 主要用于那些基于极少内存映射的嵌入式应用程序,与嵌入式应用程序配合使用,且这些应用程序不在操作系统中运行;
② 高度优化以使代码变得很小,功能比缺省c库少,不具备某些ISO C特性,部分库函数的运行速度也比较慢,如内存拷贝函数
memcpy()
。与缺省c库之间的主要差异:
(1)MicroLib 不符合 ISO C 库标准。 不支持某些 ISO 特性,并且其他特性具有的功能也较少。
(2)MicroLib 不符合 IEEE 754 二进制浮点算法标准。
(3)MicroLib 进行了高度优化以使代码变得很小。
(4)无法对区域设置进行配置。 缺省 C 区域设置是唯一可用的区域设置。
(5)不能将main()
声明为使用参数,并且不能返回内容。
(6)不支持 stdio,但未缓冲的 stdin、stdout 和 stderr 除外。
(7)MicroLib对 C99 函数提供有限的支持。
(8)MicroLib不支持操作系统函数。
(9)MicroLib不支持与位置无关的代码。
(10)MicroLib不提供互斥锁来防止非线程安全的代码。
(11)MicroLib不支持宽字符或多字节字符串。
(12)与stdlib不同,MicroLib不支持可选择的单或双区内存模型。MicroLib只提供双区内存模型,即单独的堆栈和堆区。
③ 使用MicroLib的话,嵌入式设备便不会使用半主机模式;
④ Microlib对于IO功能仅定义了_stdout和_stdin,用户重定向
fputc()
/fgetc()
的时候无需再重新定义_stdout/_stdin。
但是,keil MDK中勾选了Use MicroLib
选项后,还是不能直接使用printf()
函数,其打印的字符串最终不知道打印到何处。我们要做的是将调试信息打印到UART中,所以需要对printf()
函数所依赖的打印输出函数fputc()
重定向(MicroLib中的printf()
函数打印操作依赖fputc()
)。
所以,ARM内核的嵌入式设备,若要使用 printf() 函数,归根到底只有2种方法:
方法一:开启MicroLib,且重定义fputc()函数;
方法二:关闭MicroLib,关闭半主机模式,且重定义fputc()函数。推荐使用方法二。