华大单片机HC32L136如何做printf串口打印格式化输出

关注、星标、置顶公众号

写在前面

通常工程师在做产品开发和程序调试的时候会通过仿真器进入仿真模式来获得芯片运行的结果,从而判断程序编写的是否正确。但是有些特殊的情况仿真器的作用就不大,例如下面这几种情况:

  • 华大MCU芯片进入深度休眠后,仿真器接口将会关闭,工程在调试状态会因为仿真接口的关闭退出调试状态,芯片唤醒后工作的情况将观察不到;
  • 需要实时观察芯片数据的情况,如采集的电池实时电压情况;
  • 不方便接仿真器的情况,如运动小车项目中对车身状况的监控等;
  • 仿真器管脚已经被占用的情况;
  • 程序执行错误的情况等。

遇到仿真器不方便调试的时候我们一般会利用板子上的某个硬件做为输出,通过观察这个输出来判断程序运行的是否正常。如果程序运行到某一状态可以点亮板子上的LED灯,或翻转下某个IO,如果板子上有屏那就更好,直接可以在屏上显示自己想要知道的内容。还是一种方法就是使用UART把数据传输出去。如果UART接了无线模块还可以进行远距离的观测,就像上面提到的运动小车项目。当然使用UART只做普通方式的传输数据的话,数据是不包涵任何格式内容的,我们要解读的时候会费点力气。那有没有什么好办法,让输出的数据直观明了呢?没错,就是用我们学习C语言时候的老朋友printf来做格式化输出。


半主机模式介绍(可以跳过)

因为MCU本身资源的限制,在MCU上运行的C语言代码和在计算机上还是有区别的。这就涉及到一种叫半主机的机制,它使得在ARM目标上跑的代码,如果主机电脑运行了调试器,那么该代码可以使用该主机电脑的输入输出设备。 这点非常重要,因为开发初期,可能开发者根本不知道该 ARM 器件上有什么输入输出设备,而半主基机制使得你不用知道ARM器件的外设,利用主机电脑的外设就可以实现输入输出调试。 所以要利用目标 ARM器件的输入输出设备,首先要关掉半主机机制。然后再将输入输出重定向到 ARM 器件上,如 printf 。需要重写 fputc。
printf之类的函数,使用了半主机模式。使用标准库会导致程序无法运行 ,以下是解决方法 :
方法 1.使用微库 ,因为使用微库的话 ,不会使用半主机模式 .
如果使用的是 MDK,请在工程属性的 “Target “- >”Code Generation “中勾选 ”Use MicroLIB “这样以后就可以
使用 printf ,sprintf 函数了
方法 2.仍然使用标准库 ,在主程序添加下面代码 :
/*为确保没有从 C 库链接使用半主机的函数,因为不使用半主机,标准 C 库 stdio.h 中有些使用半主机的
函数要重新写 ,您必须为这些函数提供自己的实现 */

#pragma import(__use_no_semihosting)
void _sys_exit(int x)
{
    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;

在独立应用程序中,不可能支持半主机操作。 因此,必须确保应用程序中没有链接 C 库半主机函数。
为确保没有从 C 库链接使用半主机的函数, 必须导入符号 __use_no_semihosting 。可在工程的任何 C 或汇编语言源文件中执行此操作,如下所示:
在 C 模块中,使用 #pragma 指令:
#pragma import(__use_no_semihosting)
在汇编语言模块中,使用 IMPORT 指令:
IMPORT __use_no_semihosting
如果仍然链接了使用半主机的函数,则链接器会报告错误。


串口接收初始化

现在以HC32L136芯片来介绍如何实现printf格式化输出。先做UART的初始化,这里用的是UART0,使用PA09做为TX信号,初始化代码如下:

void AppUartInit(void)
{
    stc_uart_cfg_t  stcCfg;
    stc_uart_baud_t stcBaud;	
	stc_gpio_cfg_t stcGpioCfg;
    
    DDL_ZERO_STRUCT(stcGpioCfg);
	DDL_ZERO_STRUCT(stcCfg);
    DDL_ZERO_STRUCT(stcBaud);
    
	Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
    stcGpioCfg.enDir = GpioDirOut;
    Gpio_Init(GpioPortA,GpioPin9,&stcGpioCfg);
    Gpio_SetAfMode(GpioPortA,GpioPin9,GpioAf1); //配置PA09 为UART0 TX
    
    
    Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);//UART0外设模块时钟使能
    
    stcCfg.enRunMode        = UartMskMode3;                     //模式3
    stcCfg.enStopBit        = UartMsk1bit;                      //1位停止位
    stcCfg.enMmdorCk        = UartMskEven;                      //偶校验
    stcCfg.stcBaud.u32Baud  = 9600;                             //波特率9600
    stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;                  //通道采样分频配置
    stcCfg.stcBaud.u32Pclk  = Sysctrl_GetPClkFreq();            //获得外设时钟(PCLK)频率值
    Uart_Init(M0P_UART0, &stcCfg);                              //串口初始化

}

串口发送重定向

知道半主机是怎么回事以后,把ddl.c里的fputc函数给屏蔽掉,因为它没啥用,我们自己来写一个,自己能动手,不用麻烦别人。屏蔽掉ddl.c文件中的fputc()函数,如下图
在这里插入图片描述
自己重写fputc函数,就是下面的代码,看了什么感想?哇塞,怎么这么简单?

///< 串口发送重定向
int fputc(int ch, FILE * file)
{
    Uart_SendDataPoll(M0P_UART0,ch);         //调用库函数,通过UART0发送一个字母。
    
    return ch;
}

主程序

上面工作完成后写个非常简单的主程序我们来验证一下。代码如下:

int32_t main(void)
{   
	uint8_t i = 0;
    AppUartInit();                                    //调用串口初始化函数
    
    printf("Init Sucessful \r\n");	                  //输出Init Sucessful
	delay1ms(1000);
    while (1)
	{
		printf(" i = %d \r\n", i);	                  //格式化输出i的值
		i++;
		delay1ms(1000);
	}

}

连好开发板

在这里插入图片描述


运行效果

在这里插入图片描述


关注微信公众号『芯缘意码』,查看更多内容,回复“加群”加入技术交流群。
淘宝 店铺 搜索 『芯缘意码』,购买开发学习板仿真器。
公众号:芯缘意码https://shop219666036.taobao.com


  • 8
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值