查看: 5219|回复: 21

[其他] 【经验】在单片机程序中使用printf

[复制链接]
  • TA的每日心情
    奋斗
    2017-1-17 10:45
  • 签到天数: 3 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    38

    主题

    395

    帖子

    3

    高级会员

    Rank: 4

    积分
    780
    最后登录
    2023-11-17
    发表于 2015-5-6 00:40:24 | 显示全部楼层 |阅读模式
    在单片机程序中使用printf
    2015-05

    【准备目标平台的串口驱动】
    【在KEIL中配置使用stdio】
    【测试程序】

    在PC机上编写C语言程序的时候,最常用的一个函数就是printf了,printf可以进行格式化输出,控制输出格式,使用起来非常方便,那么在单片机程序开发的时候,能不能也使用这个printf函数呢?
    Of course,当然可以,但如果要自己从零开始实现,那必定是一个超级麻烦的事情。靠,你不会真的熬夜给写出来了吧,好吧,其实我想问问,如果用这些时间写点有意思的小程序是不是更好。对于大多数喜欢偷懒的童鞋们,比如说我,会用更简单的方式实现在单片机中使用printf的功能。
    在常用的集成开发环境中,一般都自带了针对交叉编译环境下目标平台的标准输入输出的算法库,这些库帮忙实现的与硬件无关的部分,我们只要在具体的平台上调用底层驱动同库进行对接,就能通过库函数控制底层设备了。幸运的是,stdio就包含在这些预先准备好的库中。想想stdio在PC机编程中提供的服务我就激动不已,一旦对接了stdio并且在应用程序中引用了stdio.h文件,那就意味着我们可以在程序中调用N多方便的库函数,它们是:printf, scanf, putchar, getchar。你没有看错,比printf更麻烦的scanf在这里也能搞定。那么我们赶快看看如何快速地对接stdio吧。
    这里我使用FRDM-KL25作为具体的目标硬件平台,在常用的KEIL环境下进行说明,IAR也可类似配置,此处暂略。
    【准备目标平台的串口驱动】
    无论stdio能够玩出多NB的算法,最终都要“接地气”,我们需要将stdio的输入输出通道映射到串口上。在FRDM-KL25板子上,OpenSDA对接的串口是UART0,那么我就用这个UART0作为stdio的信道。为此,我准备了最最简单的UART0轮询驱动程序(用中断驱动或是DMA驱动也可以,不过实现起来会麻烦得多),配置好波特率及传输格式后,只要实现轮询发送一个字节和接收一个字节的驱动函数即可。
    我为UART0实现的基本驱动函数如下:
    typedef struct
    {
        uint32_t BusClkHz;
        uint32_t Baudrate;
    } UART_Config_T;
    /* UART0. */
    bool UART0_ConfigTransfer(const UART_Config_T *configPtr)
    {
        uint16_t sbr_val;
        /* Disable the Rx and Tx. */
        UART0->C2 &= ~(UART0_C2_TE_MASK | UART0_C2_RE_MASK);
       
        /* configure uart1 for 8-bit mode , no parity */
        UART0->C1 = 0U;
        /* calculate the sbr value. */
        sbr_val = (configPtr->BusClkHz >> 4)/configPtr->Baudrate;
        UART0->BDH = (uint8_t)(((0x1F00 & sbr_val) >> 8)&UART0_BDH_SBR_MASK);
        UART0->BDL = (uint8_t)(sbr_val & UART0_BDL_SBR_MASK);
       
        UART0->C3 = 0U;
        UART0->S1 = 0x1FU;
        UART0->S2 = 0U;
       
        /* enable the tx and rx  */
        UART0->C2 |= (UART0_C2_TE_MASK | UART0_C2_RE_MASK);
       
        return true;
    }
    void UART0_PutTxDataBlocking(uint8_t txData)
    {
        while (0U == (UART0->S1 & UART0_S1_TDRE_MASK) ) {}
        UART0->D = txData;
    }
    uint8_t UART0_GetRxDataBlocking(void)
    {
        while ( 0U != (UART0->S1 & UART0_S1_RDRF_MASK)  ) {}
        return UART0->D;
    }
    然后,我在stdio_adapter.c文件中实现了fputc和fgetc,完成了stdio同底层驱动的对接。
    #include <app_inc.h>
    #include <stdio.h>
    int fputc(int c, FILE *f)
    {
        UART0_PutTxDataBlocking((uint8_t)c);
        return c;
    }
    int fgetc(FILE *f)
    {
        return (UART0_GetRxDataBlocking());
    }
    在程序中使用stdio的服务前一定要初始化好UART0,对UART0模块严格遵循先初始化再使用的原则,否则有你好看的。
    到此为止,代码部分的准备工作就已经结束了。后面还需要在集成开发环境的工程中进行配置,在工程中包含库。
    【在KEIL中配置使用stdio
    打开KEIL工程后,戳开“Option for Target <工程名>”对话框,在“Target”标签页下勾选“Use MicroLIB”。

    keil.png

    【测试程序】
    编写测试工程的main.c代码如下:
    #include "app_inc.h"
    #include <stdint.h>
    #include <stdbool.h>
    #include <stdio.h>
    int main(void)
    {
        char ch;
       
        init_board();
       
        printf("\r\nHello, Board.\r\n");
        printf("Compiled at %s on %s\r\n", __TIME__, __DATE__);
       
        printf("Press any key to display charactor ...\r\n");
       
        while (1)
        {
            ch = getchar();
            putchar(ch);
        }
    }
    /* EOF. */
    编译工程将程序下载到FRDM-KL25板子中,打开串口终端,运行,Bingo!

    terminal.png

    后面的“www.eefocus.com”是我在终端窗口中输入后回显出来的。
    在测试程序中没有验证scanf函数,小伙伴们赶快自己试试吧。
    - End
    附上PDF版本的文档及工程代码:

    AN_SY20150506.pdf (290.13 KB, 下载次数: 22)
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2017-4-27 10:34
  • 签到天数: 3 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    36

    主题

    507

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    1450
    最后登录
    2020-8-3
    发表于 2015-5-6 08:39:37 | 显示全部楼层
    很不错的经验分享!
    哎...今天够累的,签到来了1...
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    2

    主题

    52

    帖子

    0

    注册会员

    Rank: 2

    积分
    157
    最后登录
    2015-8-30
    发表于 2015-5-6 08:58:28 | 显示全部楼层
    收下了,谢谢
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    2

    主题

    52

    帖子

    0

    注册会员

    Rank: 2

    积分
    157
    最后登录
    2015-8-30
    发表于 2015-5-6 09:00:10 | 显示全部楼层
    /* calculate the sbr value. */

        sbr_val = (configPtr->BusClkHz >> 4)/configPtr->Baudrate;

        UART0->BDH = (uint8_t)(((0x1F00 & sbr_val) >> 8)&UART0_BDH_SBR_MASK);

        UART0->BDL = (uint8_t)(sbr_val & UART0_BDL_SBR_MASK);

       

        UART0->C3 = 0U;

        UART0->S1 = 0x1FU;

        UART0->S2 = 0U;

       这段什么意思
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2018-7-2 06:04
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    59

    主题

    2888

    帖子

    10

    金牌会员

    Rank: 6Rank: 6

    积分
    6030
    最后登录
    2025-8-21
    发表于 2015-5-6 10:37:10 | 显示全部楼层
    非常感谢你关于Kinetis的经验分享 !
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2017-1-17 10:45
  • 签到天数: 3 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    38

    主题

    395

    帖子

    3

    高级会员

    Rank: 4

    积分
    780
    最后登录
    2023-11-17
     楼主| 发表于 2015-5-6 10:37:22 | 显示全部楼层
    ifafkajflkef 发表于 2015-5-6 09:00
    /* calculate the sbr value. */

        sbr_val = (configPtr->BusClkHz >> 4)/configPtr->Baudrate;

    初始化串口模块的配置。
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    0

    主题

    15

    帖子

    0

    新手上路

    Rank: 1

    积分
    48
    最后登录
    2018-7-24
    发表于 2015-5-6 11:10:51 | 显示全部楼层
    好帖,用printf看起来高级多了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情

    2021-7-13 18:31
  • 签到天数: 127 天

    连续签到: 1 天

    [LV.7]常住居民III

    19

    主题

    1229

    帖子

    1

    金牌会员

    Rank: 6Rank: 6

    积分
    6124
    最后登录
    2024-12-19
    发表于 2015-5-6 11:38:26 | 显示全部楼层
    说实话,总觉得用printf很别扭,还是直接用UART比较舒服
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    13

    主题

    143

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    491
    最后登录
    1970-1-1
    发表于 2015-5-6 11:43:13 | 显示全部楼层
    wweeww 发表于 2015-5-6 11:38
    说实话,总觉得用printf很别扭,还是直接用UART比较舒服

    printf函数封装的很好,任何字符都可直接输出,不用做任何处理。使用起来很方便
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    0

    主题

    50

    帖子

    0

    注册会员

    Rank: 2

    积分
    126
    最后登录
    1970-1-1
    发表于 2015-5-6 13:37:24 | 显示全部楼层
    收下了,谢谢
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-9-4 03:28 , Processed in 0.103763 second(s), 31 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表