STM32串口--printf重定向问题

652 阅读1分钟

printf重定向问题

在Keil里面为了使用printf函数我们需要重定向fputc函数:

int fputc (int ch, FILE *f){
​
    (void)HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
    
    return ch;
​
}

其中的FILE定义在stdio.h头文件中,所以需要在项目中包含这个头文件,但是经过测试发现,Keil里面包含的是MDK\ARM\ARMCC\include这个目录下的stdio.h,而在Clion中是不会链接到这个文件的。因此如果在Clion中也按之前的方法进行重定向,会发现printf没有任何输出。

在Clion中链接的是GNU-Tools-ARM-Embedded\arm-none-eabi\include里面的stdio.h,如果仍然想使用printf函数功能,则需要进行如下操作:

新建一个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);

然后就可以愉快地使用printf和scanf啦:

char buf[100];
​
​
​
printf("\r\nYour name: ");
​
scanf("%s", buf);
​
printf("\r\nHello, %s!\r\n", buf);