[方案相关] 华大单片机、STM32单片机如何做printf串口打印格式化输出

[复制链接]
1626|16
 楼主| wang6623 发表于 2023-10-29 02:12 | 显示全部楼层 |阅读模式
第一种方法:使用标准C库,但使用标准C库你必须关闭半主机模式(1)添加下面代码就是关闭半主机模式

  1. /* 告知连接器不从C库链接使用半主机的函数 */
  2. #pragma import(__use_no_semihosting)

  3. /* 定义 _sys_exit() 以避免使用半主机模式 */
  4. void _sys_exit(int x)
  5. {
  6.     x = x;
  7. }

  8. /* 标准库需要的支持类型 */
  9. struct __FILE
  10. {
  11.     int handle;
  12. };

  13. FILE __stdout;


 楼主| wang6623 发表于 2023-10-29 02:13 | 显示全部楼层
在独立应用程序中,不可能支持半主机操作。 因此,必须确保应用程序中没有链接 C 库半主机函数。

为确保没有从 C 库链接使用半主机的函数, 必须导入符号 __use_no_semihosting 。

可在工程的任何 C 或汇编语言源文件中执行此操作,如下所示:

在 C 模块中,使用 #pragma 指令:

#pragma import(__use_no_semihosting)

在汇编语言模块中,使用 IMPORT 指令:

IMPORT __use_no_semihosting
 楼主| wang6623 发表于 2023-10-29 02:13 | 显示全部楼层
串口重定向
将你要输出信息的串口添加到这句函数里面
 楼主| wang6623 发表于 2023-10-29 02:13 | 显示全部楼层
///< 串口发送重定向
int fputc(int ch, FILE * file)
{
    Uart_SendDataPoll(M0P_UART0,ch);         //调用库函数,通过UART0发送一个字母。

    return ch;
}
 楼主| wang6623 发表于 2023-10-29 02:13 | 显示全部楼层
如果是不同型号的MCU,或者使用那个串口更改对应的串口号即可
 楼主| wang6623 发表于 2023-10-29 02:13 | 显示全部楼层
 楼主| wang6623 发表于 2023-10-29 02:13 | 显示全部楼层
接下来你就自己配置好对应的串口初始化就OK了
这里需要注意下:本文代码我采用华大HC32L系列的,它这个库函数Uart_SendDataPoll(M0P_UART0,ch)里面是有等待数据发送完毕的
 楼主| wang6623 发表于 2023-10-29 02:13 | 显示全部楼层
 楼主| wang6623 发表于 2023-10-29 02:14 | 显示全部楼层
如果你用的是STM32单片机,重定向函数应该这么写
  1. //< 串口发送重定向
  2. int fputc(int ch, FILE * file)
  3. {
  4.     USART_SendData(USART1,ch);      
  5.         while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
  6.     return ch;
  7. }
 楼主| wang6623 发表于 2023-10-29 02:14 | 显示全部楼层
需要自己在添加一行代码来等待发送完毕,因为ST发送库函数里面没有等待语句
8559653d4f786ae2f.png
 楼主| wang6623 发表于 2023-10-29 02:14 | 显示全部楼层
(3)下面是我自己完整的配置(我是使用串口0)

#include "BSP_GPIO.h"
#include "BSP_Uart.h"

//#include "UFD.h"
uint8_t u8Rx0Data;

/* 告知连接器不从C库链接使用半主机的函数 */
#pragma import(__use_no_semihosting)

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

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

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

    return ch;
}
static void Uart0_PortInit(void)
{
    stc_gpio_cfg_t stcGpioCfg;

    ///< 打开GPIO外设时钟门控
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);

    DDL_ZERO_STRUCT(stcGpioCfg);

    ///< 端口方向配置 UART0_TXD
    stcGpioCfg.enDir = GpioDirOut;
    Gpio_Init(PORT_DEBUG_TXD,PIN_DEBUG_TXD,&stcGpioCfg);
    Gpio_SetAfMode(PORT_DEBUG_TXD,PIN_DEBUG_TXD,GpioAf2);//UART0_TXD

    ///< 端口方向配置 UART0_RXD
    stcGpioCfg.enDir = GpioDirIn;
    ///< 端口上下拉配置->上拉
    stcGpioCfg.enPu = GpioPuEnable;
    Gpio_Init(PORT_DEBUG_RXD,PIN_DEBUG_RXD,&stcGpioCfg);
    Gpio_SetAfMode(PORT_DEBUG_RXD,PIN_DEBUG_RXD,GpioAf2);//UART0_RXD

    Sysctrl_SetFunc(SysctrlSWDUseIOEn, TRUE);    /*Set SWD port to GPIO mode*/
}
static void Uart0_Init(void)
{
    stc_uart_cfg_t  stcCfg;
    stc_uart_baud_t stcBaud;

    DDL_ZERO_STRUCT(stcCfg);
    DDL_ZERO_STRUCT(stcBaud);

    ///< 打开UART0外设时钟门控
    Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);

    ///<UART Init
    stcCfg.enRunMode        = UartMskMode1;                 ///<模式1
    stcCfg.enStopBit        = UartMsk1bit;                  ///<1bit停止位
    stcCfg.enMmdorCk        = UartMskDataOrAddr;            ///<多机模式时
    stcCfg.stcBaud.u32Baud  = 1000000;                      ///<波特率1000000
    stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;              ///<通道采样分频配置
    stcCfg.stcBaud.u32Pclk  = Sysctrl_GetPClkFreq();        ///</<获得外设时钟(PCLK)频率值
    Uart_Init(M0P_UART0, &stcCfg);                          ///<串口初始化
   
    ///<UART中断使能
    Uart_ClrStatus(M0P_UART0,UartRC);                       ///<清接收请求
    Uart_ClrStatus(M0P_UART0,UartTC);                       ///<清接收请求
   
        Uart_EnableIrq(M0P_UART0,UartRxIrq);                    ///<使能串口接收中断
    EnableNvic(UART0_2_IRQn, IrqLevel3, TRUE);              ///<系统中断使能
}

void BSP_UartInit(void)
{
    Uart0_PortInit();
    Uart0_Init();
}
 楼主| wang6623 发表于 2023-10-29 02:14 | 显示全部楼层
第二种方法:使用微库,因为使用微库的话 ,不会使用半主机模式,咱也就不用在写那几句关闭半主机模式的语句
 楼主| wang6623 发表于 2023-10-29 02:14 | 显示全部楼层
(1)在Keil工程中“中勾选 ”Use MicroLIB 30882653d4f98a343c.png
 楼主| wang6623 发表于 2023-10-29 02:15 | 显示全部楼层
重定向输出(这个跟第一种方法一样)
//< 串口发送重定向
int fputc(int ch, FILE * file)
{
    Uart_SendDataPoll(M0P_UART0,ch);         //调用库函数,通过UART0发送一个字母。

    return ch;
}
 楼主| wang6623 发表于 2023-10-29 02:15 | 显示全部楼层
(3)下面是我自己完整的配置(我是使用串口0)

63839653d4faf60978.png
 楼主| wang6623 发表于 2023-10-29 02:15 | 显示全部楼层
  1. #include "BSP_GPIO.h"
  2. #include "BSP_Uart.h"

  3. //#include "UFD.h"
  4. uint8_t u8Rx0Data;

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

  9.     return ch;
  10. }
  11. static void Uart0_PortInit(void)
  12. {
  13.     stc_gpio_cfg_t stcGpioCfg;

  14.     ///< 打开GPIO外设时钟门控
  15.     Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);

  16.     DDL_ZERO_STRUCT(stcGpioCfg);

  17.     ///< 端口方向配置 UART0_TXD
  18.     stcGpioCfg.enDir = GpioDirOut;
  19.     Gpio_Init(PORT_DEBUG_TXD,PIN_DEBUG_TXD,&stcGpioCfg);
  20.     Gpio_SetAfMode(PORT_DEBUG_TXD,PIN_DEBUG_TXD,GpioAf2);//UART0_TXD

  21.     ///< 端口方向配置 UART0_RXD
  22.     stcGpioCfg.enDir = GpioDirIn;
  23.     ///< 端口上下拉配置->上拉
  24.     stcGpioCfg.enPu = GpioPuEnable;
  25.     Gpio_Init(PORT_DEBUG_RXD,PIN_DEBUG_RXD,&stcGpioCfg);
  26.     Gpio_SetAfMode(PORT_DEBUG_RXD,PIN_DEBUG_RXD,GpioAf2);//UART0_RXD

  27.     Sysctrl_SetFunc(SysctrlSWDUseIOEn, TRUE);    /*Set SWD port to GPIO mode*/
  28. }
  29. static void Uart0_Init(void)
  30. {
  31.     stc_uart_cfg_t  stcCfg;
  32.     stc_uart_baud_t stcBaud;

  33.     DDL_ZERO_STRUCT(stcCfg);
  34.     DDL_ZERO_STRUCT(stcBaud);

  35.     ///< 打开UART0外设时钟门控
  36.     Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);

  37.     ///<UART Init
  38.     stcCfg.enRunMode        = UartMskMode1;                 ///<模式1
  39.     stcCfg.enStopBit        = UartMsk1bit;                  ///<1bit停止位
  40.     stcCfg.enMmdorCk        = UartMskDataOrAddr;            ///<多机模式时
  41.     stcCfg.stcBaud.u32Baud  = 1000000;                      ///<波特率1000000
  42.     stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;              ///<通道采样分频配置
  43.     stcCfg.stcBaud.u32Pclk  = Sysctrl_GetPClkFreq();        ///</<获得外设时钟(PCLK)频率值
  44.     Uart_Init(M0P_UART0, &stcCfg);                          ///<串口初始化
  45.    
  46.     ///<UART中断使能
  47.     Uart_ClrStatus(M0P_UART0,UartRC);                       ///<清接收请求
  48.     Uart_ClrStatus(M0P_UART0,UartTC);                       ///<清接收请求
  49.    
  50.         Uart_EnableIrq(M0P_UART0,UartRxIrq);                    ///<使能串口接收中断
  51.     EnableNvic(UART0_2_IRQn, IrqLevel3, TRUE);              ///<系统中断使能
  52. }

  53. void BSP_UartInit(void)
  54. {
  55.     Uart0_PortInit();
  56.     Uart0_Init();
  57. }
 楼主| wang6623 发表于 2023-10-29 02:15 | 显示全部楼层
总结:
要使用第一种还是第二种,看自己选择,大家好像选择第一种比较多。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

71

主题

617

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部