×
单片机 > 单片机程序设计 > 详情

STM32高级开发(12)-在GCC中使用printf打印串口数据

发布时间:2020-12-28 发布时间:
|

在大家使用keil或是iar开发stm32等arm芯片的时候,想来最不陌生的就是使用print通过串口输出一些数据,用来调试或是其他作用。但是要明确的是由于keil iar gcc 他们使用的标准C语言库虽然都遵循一个标准,但他们底层的函数实现方式都是不同的,那么在GCC中我们能否像在keil中一样重映射print的输出流到串口上呢?答案是肯定的。


keil中的重映射方式及原理

/* 

 * libc_printf.c 

 * 

 *  Created on: Dec 26, 2015 

 *      Author: Yang 

 * 

 *      使用标准C库时,重映射printf等输出函数的文件 

 *    添加在工程内即可生效(切勿选择semihost功能) 

 */  

 

#include   

//include "stm32f10x.h"    

 

 

#pragma import(__use_no_semihosting)               

//标准库需要的支持函数                   

struct __FILE  

{  

    int handle;  

 

};  

FILE __stdout;  

 

//定义_sys_exit()以避免使用半主机模式      

_sys_exit(int x)  

{  

    x = x;  

}  

 

//重映射fputc函数,此函数为多个输出函数的基础函数  

int fputc(int ch, FILE *f)  

{  

    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);  

    USART_SendData(USART1, (uint8_t) ch);  

    return ch;  

}  


在keil中的C库中,printf、scanf等输入输出流函数是通过fputc、fgetc来实现最底层操作的,所以我们只需要在我们的工程中重定义这两个函数的功能就可以实现printf、scanf等流函数的重映射。


GNU下的C流函数重映射方式

我们来看看前几篇中提供的样例工程中的usart_stdio例程中的代码片段:


#include

#include

#include

 

/*

 * To implement the STDIO functions you need to create

 * the _read and _write functions and hook them to the

 * USART you are using. This example also has a buffered

 * read function for basic line editing.

 */

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

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

void get_buffered_line(void);

 

/*

 * This is a pretty classic ring buffer for characters

 */

#define BUFLEN 127

 

static uint16_t start_ndx;

static uint16_t end_ndx;

static char buf[BUFLEN + 1];

#define buf_len ((end_ndx - start_ndx) % BUFLEN)

static inline int inc_ndx(int n) { return ((n + 1) % BUFLEN); }

static inline int dec_ndx(int n) { return (((n + BUFLEN) - 1) % BUFLEN); }

 

/* back up the cursor one space */

static inline void back_up(void)

{

    end_ndx = dec_ndx(end_ndx);

    usart_send_blocking(USART1, '10');

    usart_send_blocking(USART1, ' ');

    usart_send_blocking(USART1, '10');

}

 

/*

 * A buffered line editing function.

 */

void get_buffered_line(void)

{

    char c;

 

    if (start_ndx != end_ndx)

    {

        return;

    }

 

    while (1)

    {

        c = usart_recv_blocking(USART1);

 

        if (c == 'r')

        {

            buf[end_ndx] = 'n';

            end_ndx = inc_ndx(end_ndx);

            buf[end_ndx] = '';

            usart_send_blocking(USART1, 'r');

            usart_send_blocking(USART1, 'n');

            return;

        }

 

        /* or DEL erase a character */

        if ((c == '10') || (c == '177'))

        {

            if (buf_len == 0)

            {

                usart_send_blocking(USART1, 'a');

            }

 

            else

            {

                back_up();

            }

 

            /* erases a word */

        }

 

        else if (c == 0x17)

        {

            while ((buf_len > 0) &&

                    (!(isspace((int) buf[end_ndx]))))

            {

                back_up();

            }

 

            /* erases the line */

        }

 

        else if (c == 0x15)

        {

            while (buf_len > 0)

            {

                back_up();

            }

 

            /* Non-editing character so insert it */

        }

 

        else

        {

            if (buf_len == (BUFLEN - 1))

            {

                usart_send_blocking(USART1, 'a');

            }

 

            else

            {

                buf[end_ndx] = c;

                end_ndx = inc_ndx(end_ndx);

                usart_send_blocking(USART1, c);

            }

        }

    }

}

 

/*

 * Called by libc stdio fwrite functions

 */

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

{

    int i = 0;

 

    /*

     * write "len" of char from "ptr" to file id "fd"

     * Return number of char written.

     *

    * Only work for STDOUT, STDIN, and STDERR

     */

    if (fd > 2)

    {

        return -1;

    }

 

    while (*ptr && (i < len))

    {

        usart_send_blocking(USART1, *ptr);

 

        if (*ptr == 'n')

        {

            usart_send_blocking(USART1, 'r');

        }

 

        i++;

        ptr++;

    }

 

    return i;

}

 

/*

 * Called by the libc stdio fread fucntions

 *

 * Implements a buffered read with line editing.

 */

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

{

    int my_len;

 

    if (fd > 2)

    {

        return -1;

    }

 

    get_buffered_line();

    my_len = 0;

 

    while ((buf_len > 0) && (len > 0))

    {

        *ptr++ = buf[start_ndx];

        start_ndx = inc_ndx(start_ndx);

        my_len++;

        len--;

    }

 

    return my_len; /* return the length we got */

}


这个文件因为实现了scanf的功能同时还带有在串口上终端回显并支持backspace键所以显得有些长,我们来将其中的实现printf重映射的片段取出:


#include

#include

 

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

{

    int i = 0;

 

    /*

     * write "len" of char from "ptr" to file id "fd"

     * Return number of char written.

     *

    * Only work for STDOUT, STDIN, and STDERR

     */

    if (fd > 2)

    {

        return -1;

    }

 

    while (*ptr && (i < len))

    {

        usart_send_blocking(USART1, *ptr);

 

        if (*ptr == 'n')

        {

            usart_send_blocking(USART1, 'r');

        }

 

        i++;

        ptr++;

    }

 

    return i;

}


与keil C库类似GNU C库下的流函数底层是通过_read、_write函数实现的,我们只要在工程中将他们重新定义就可以实现重映射的功能了。


补充

差点忘了最重要的。我们在使用GNU的printf时,一定要记住在发送的内容后添加 n或者在printf后使用fflush(stdout),来立即刷新输出流。否则printf不会输出任何数据,而且会被后来的正确发送的printf数据覆盖。这是由于printf的数据流在扫描到 n以前会被保存在缓存中,直到 n出现或是fflush(stdout)强制刷新才会输出数据,如果我们在printf数据的末尾不加入n或fflush(stdout),这个printf数据就不会被发送出去,而且在新的printf语句也会重写printf的缓存内容。


关键字:STM32  高级开发  GCC  printf打印  串口数据

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
MSP430F5529 上手小例程2