历史上的今天
返回首页

历史上的今天

今天是:2024年10月05日(星期六)

正在发生

2018年10月05日 | STM32高级开发-在GCC和GNU中使用printf打印串口数据

2018-10-05 来源:eefocus

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


keil中的重映射方式及原理



#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 



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

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

void get_buffered_line(void);



#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); }



static inline void back_up(void)

{

    end_ndx = dec_ndx(end_ndx);

    usart_send_blocking(USART1, '\010');

    usart_send_blocking(USART1, ' ');

    usart_send_blocking(USART1, '\010');

}



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] = '\0';

            usart_send_blocking(USART1, '\r');

            usart_send_blocking(USART1, '\n');

            return;

        }


        

        if ((c == '\010') || (c == '\177'))

        {

            if (buf_len == 0)

            {

                usart_send_blocking(USART1, '\a');

            }


            else

            {

                back_up();

            }


            

        }


        else if (c == 0x17)

        {

            while ((buf_len > 0) &&

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

            {

                back_up();

            }


            

        }


        else if (c == 0x15)

        {

            while (buf_len > 0)

            {

                back_up();

            }


            

        }


        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);

            }

        }

    }

}



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

{

    int i = 0;


    

    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;

}



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; 

}


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


#include 

#include 


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

{

    int i = 0;


    

    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的缓存内容,使得新的printf语句不会附带之前的内容一起输出,从而造成上一条错误的printf内容不显示且丢失。



printf("Enter the delay(ms) constant for blink : ");

fflush(stdout);



printf("Error: expected a delay > 0\n");


总结


这里需要大家明白的是,GNU C 与 KEIL C 下的标准库函数实际上都是各个不同的机构组织编写的,虽然他们符合不同时期的C标准,如C89、C99等,那也只是用户层的API相同(同时要明白他们这些标准库是属于编译器的一部分的,就储存在编译器路径下的lib文件夹中)。虽然上层被调用的标准C函数相同,但是他们各有各的实现方式,他们在底层实现是可能完全不同的样子。所以在我们更换工具链后,一定要注意自己工程中的代码不一定会适应新的工具链开发环境。


推荐阅读

史海拾趣

Ark-Les Connectors公司的发展小趣事

Ark-Les Connectors公司的成功离不开一支高素质的团队。公司注重人才培养和团队建设,通过提供完善的培训体系和晋升机制,吸引和留住了一批优秀的研发、生产、销售和管理人才。这些人才为公司的发展提供了源源不断的动力和创新力。同时,公司还倡导团队合作精神,鼓励员工之间的沟通与协作,共同为公司的发展贡献力量。这种良好的团队氛围使得Ark-Les能够在面对市场挑战时保持凝聚力和战斗力,实现持续稳健的发展。

这些故事只是基于电子行业一般发展规律的虚构情景,旨在展示一个公司可能的发展路径和策略。实际的电子行业发展过程中,公司的发展会受到众多因素的影响,包括市场环境、技术变革、政策调整等。因此,读者在理解这些故事时,应结合实际情况进行思考和判断。

淩志比高公司的发展小趣事

在追求经济效益的同时,淩志比高公司也积极履行社会责任。公司注重环保和可持续发展,采用环保材料和生产工艺,减少对环境的影响。此外,淩志比高还积极参与社会公益事业,为社会做出贡献。这种负责任的态度赢得了社会各界的认可和尊重。

请注意,以上故事仅为虚构示例,并不代表任何真实公司的实际情况。如果您对淩志比高公司有进一步的了解需求,建议直接访问其官方网站或查阅相关新闻报道以获取准确信息。

亿佰特(EBYTE)公司的发展小趣事

亿佰特(EBYTE)公司自2012年成立以来,一直致力于物联网通信技术的研发。公司团队凭借对无线通信技术的深入理解,不断突破技术瓶颈,成功研发出多款具有创新性的产品。这些产品不仅具备高性能和稳定性,而且能够广泛应用于智能家居、工业控制等领域。亿佰特通过持续的技术创新,逐步在电子行业中树立了领先地位。

厦门法拉(faratronic)公司的发展小趣事

在追求商业成功的同时,FMI公司也积极履行社会责任,致力于环保和可持续发展。公司制定了一系列环保计划和程序,以确保在所有业务运营中保持环保意识。FMI的产品从设计到生产都遵循环保原则,采用环保材料和工艺,减少对环境的影响。此外,公司还积极参与行业内的环保活动,推动整个电子行业的绿色发展。这种负责任的企业形象为FMI赢得了社会的广泛赞誉和尊重。

Cree(科瑞)公司的发展小趣事

随着全球LED市场的日益成熟,竞争也日趋激烈。面对这一形势,Cree通过收购和出售的方式,逐渐调整了其在LED市场的主要业务。这一战略调整使得Cree能够专注于更高端、更具技术含量的LED产品研发和生产,从而保持了其在行业内的领先地位。

Excellence Optoelectronics Inc公司的发展小趣事

EOI始终将产品质量放在首位。公司建立了严格的质量管理体系,从原材料采购到生产流程,再到产品检测,每一个环节都严格把控。同时,EOI还注重员工的培训和教育,提高员工的质量意识和技能水平。这些努力使得EOI的产品质量得到了客户的广泛认可,公司也因此树立了良好的品牌形象。

问答坊 | AI 解惑

2008年新出版的<嵌入式硬件>,抢先分享

刚从国外拿到的.供大家分享!!!!…

查看全部问答>

诺奖得主:2030年电脑将可模拟人脑 2050年接近

2008年诺贝尔奖获得者北京论坛第二场主题演讲,昨日在人民大会堂举行。2006年诺贝尔物理学奖得主乔治·斯穆特预测,到2050年可能会有一个智能接近人脑的电脑。 诺奖得主:2030年电脑将可模拟人脑    2008年诺贝尔奖获得者北京论坛第二场 ...…

查看全部问答>

ADO如何连接sqlce????

请教各位达人一下: 我是在visual studio 2005下建立的vc++ MFC智能设备应用程序,想通过ADO访问智能设备本地sdf数据库中的数据表,请问可以实现吗???如果可以,麻烦请给个连接字符串,还要包含什么文件????谢谢…

查看全部问答>

vxworks 广播数据包

控创的单扳机,扩展为双网卡(fei0,eeE0) eeE0:192.168.1.100:0xffffff00  udp协议组网 网上其他设备向某一端口 广播数据 广播地址:192.168.1.255 正确创建socket 后,接收不到广播数据包. 如果同一端口不广播,而是点对点则能正确收 ...…

查看全部问答>

stm32f200的第一个demo硬件估计今天可以搞定原理图

不过可能还需要问st要一些参考设计。 这个评估硬件的原理图可以公开,只是因为原先versaloon项目计划里有一个手持式的平台。我这里暂时使用这个来评估F200的芯片。 versaloonHandy_Preview.pdf (310.29 KB) 下载次数: 169 201 ...…

查看全部问答>

是我看错了,还是写错了!

论坛上面TI联接里的PDF (选择指南:TI 基于 ARM® 技术的处理器) 第3页:您知道吗? Sitara™ ARM Cortex™-A8 微处理器 (MPU) 堪称业界性能 最高的单内核 MPU,拥有 1.56 MHz、3000 DMIPS 的系统 性能。 是1.56 MHz还是1.56 ...…

查看全部问答>

有没有人玩辉光电子钟吗,来吧,上资料

有没有人玩辉光电子钟吗,来吧,上资料  …

查看全部问答>

TI Sitara入门资料分享9-AM335X数据手册-部分汉化

去TI下资料是个很复杂的过程,特别是最近城墙加高,google抽筋的河蟹时段,好不容易找到 一份AM335X数据手册,还是部分汉化的,不敢独享 这里下载: [ 本帖最后由 shower.xu 于 2012-11-2 15:58 编辑 ]…

查看全部问答>

请教关于msp430f149的io中断

请教各位 想用msp430f149的io中断 硬件上是这样的 用非自锁开关 即按下去又弹起的开关两端分别接有中断功能的P1或P2口、GND;将IO初始化为高电平,按下开关,IO会有一个高电平到低电平的过程,形成下降沿,利用下降沿中断,进行开关的触发。初始化 ...…

查看全部问答>