在STM32中使用printf()的方法(可直接复制粘贴)

1. 使用printf的方法

1.1 重定向

在使用printf之前添加重定向代码:

#ifdef __GNUC__
  /* 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)
#endif /* __GNUC__ */
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
 
  return ch;
}

Clion可以使用上面这个方法。如果是keil的话,要去配置Use microLIB。

不使用 mircroLIB 的话,直接对 fputs 重定向:

添加头文件 `#include “stdio.h”

然后添加以下代码:

struct __FILE
{
        int handle;
};
FILE __stdout;
int fputc(int ch, FILE *f)
{
 HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);//更具实际情况更改驱动
         return (ch);
}
1.2 重写_write
#if defined(__GNUC__)
int _write(int fd, char * ptr, int len)
{
  HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);
  return len;
}
#endif

VScode中可以使用上面这个方法。

将上面两种方法都写到一起,就可以兼容。如果使用的是keil,则直接复制任意一段代码添加到文件首部即可

#ifdef __GNUC__
/* With GCC, 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)
#endif /* __GNUC__ */

PUTCHAR_PROTOTYPE {
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART1 and Loop until the end of transmission */
    // 注意下面第一个参数是&huart1,因为cubemx配置了串口1自动生成
    HAL_UART_Transmit(&huart1, (uint8_t *) &ch, 1, 0xFFFF);
    return ch;
}

int _write(int file, char *ptr, int len) {
    int DataIdx;
    for (DataIdx = 0; DataIdx < len; DataIdx++) { __io_putchar(*ptr++); }
    return len;
}

我是直接在usart.c的文件头直接加上上面的预编译。用的是Clion开发stm32。

image-20211126165658588
我用RTOS验证,在2个任务里面打印字符串。使用效果如下:

image-20211126185706688
image-20211126185631347

1.3 Shawn Hymel的方法(稚辉君在Clion上使用)

新建一个retarget.h文件内容如下:

#ifndef _RETARGET_H__
#define _RETARGET_H__

#include "stm32f1xx_hal.h"
#include <sys/stat.h>
#include <stdio.h>

void RetargetInit(UART_HandleTypeDef *huart);

int _isatty(int fd);

int _write(int fd, char *ptr, int len);

int _close(int fd);

int _lseek(int fd, int ptr, int dir);

int _read(int fd, char *ptr, int len);

int _fstat(int fd, struct stat *st);

#endif //#ifndef _RETARGET_H__

再新建一个retarget.c文件内容如下:

#include <_ansi.h>
#include <_syslist.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/times.h>
#include <retarget.h>
#include <stdint.h>

#if !defined(OS_USE_SEMIHOSTING)

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

UART_HandleTypeDef *gHuart;

void RetargetInit(UART_HandleTypeDef *huart)
{
    gHuart = huart;

    /* Disable I/O buffering for STDOUT stream, so that
     * chars are sent out as soon as they are printed. */
    setvbuf(stdout, NULL, _IONBF, 0);
}

int _isatty(int fd)
{
    if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
        return 1;

    errno = EBADF;
    return 0;
}

int _write(int fd, char *ptr, int len)
{
    HAL_StatusTypeDef hstatus;

    if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
    {
        hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);
        if (hstatus == HAL_OK)
            return len;
        else
            return EIO;
    }
    errno = EBADF;
    return -1;
}

int _close(int fd)
{
    if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
        return 0;

    errno = EBADF;
    return -1;
}

int _lseek(int fd, int ptr, int dir)
{
    (void) fd;
    (void) ptr;
    (void) dir;

    errno = EBADF;
    return -1;
}

int _read(int fd, char *ptr, int len)
{
    HAL_StatusTypeDef hstatus;

    if (fd == STDIN_FILENO)
    {
        hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);
        if (hstatus == HAL_OK)
            return 1;
        else
            return EIO;
    }
    errno = EBADF;
    return -1;
}

int _fstat(int fd, struct stat *st)
{
    if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
    {
        st->st_mode = S_IFCHR;
        return 0;
    }

    errno = EBADF;
    return 0;
}

#endif //#if !defined(OS_USE_SEMIHOSTING)

添加这两个文件到工程,更新CMake,编译之后会发现,有几个系统函数重复定义了,被重复定义的函数位于Src目录的syscalls.c文件中,我们把里面重复的几个函数删掉即可。

在main函数的初始化代码中添加对头文件的引用并注册重定向的串口号:

#include "retarget.h"

RetargetInit(&huart1);

然后就可以愉快地使用printfscanf啦:

char buf[100];

printf("\r\nYour name: ");
scanf("%s", buf);
printf("\r\nHello, %s!\r\n", buf);
1.4 稚辉君的方法

直接修改CMakeList.txt,加入下述编译选项

set(COMMON_FLAGS "-specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float")

我没尝试过

2. Clion的动态模板

动态模板的作用:实现自动补全。

添加动态模板:

打开clion的设置setting,找到编辑器选项editor, 选择实时模板live template

选择C/C++栏目,点击右边的"+",选择1.动态模板

image-20211126172303593

然后输入缩写,添加描述,再将补全的文本输进去

image-20211126172538682

点击应用后确认。

这里以重定向代码为例:

image-20211126172616809

在编辑器输入$printf会弹出动态模板,直接点击Tab补全模板。

image-20211126172814101

按下tab后

image-20211126172852191

  • 6
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
你可以在STM32CubeMX使用printf函数来进行调试输出。首先,在"Project"选项卡,选择"Settings",然后在"Code Generator"部分的"Toolchain/IDE"下拉菜单选择你使用的编译器。确保选择了正确的编译器,这样CubeMX会自动生成相应的初始化代码。 接下来,在"Configuration"选项卡的"Middleware"部分,找到"USARTx"(x为你选择的串口号)并启用它。选择合适的波特率和其他参数。 然后,在生成代码后的工程文件找到main.c文件,并打开它。你需要在头文件部分添加以下代码: ```c #include <stdio.h> #include "usart.h" ``` 然后,在main函数,你需要初始化USART,并使用freopen函数将标准输出重定向到USART。下面是一个示例代码: ```c int main(void) { /* 初始化外设 */ /* 初始化USART */ MX_USART1_UART_Init(); /* 将标准输出重定向到USART */ freopen("USART1:", "w", stdout); /* 开始你的代码 */ while (1) { printf("Hello, World!\r\n"); HAL_Delay(1000); } } ``` 在上面的示例代码,我们调用了printf函数并打印了"Hello, World!"字符串。由于我们将标准输出重定向到了USART1,所以这个字符串将会通过USART1发送出去。 注意,使用printf函数输出的数据会通过串口发送,因此你需要连接串口线将STM32开发板与PC连接起来,并使用串口调试工具(如Tera Term、Putty等)来查看输出结果。 希望这个回答对你有帮助!如有任何进一步的问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值