实现自己的printf函数

本文介绍了一种用于嵌入式系统的自定义打印函数实现方法,该函数类似于标准的printf功能,支持多种数据类型的输出,包括字符、整数、浮点数、二进制数、十六进制数及字符串。

在嵌入式开发中,常常会通过串口打印一些信息到PC终端,这就需要实现自己的printf函数,下面介绍打印函数print的实现。
print.h

#ifndef     __PRINT_H_
#define    __PRINT_H_

void    print(char* fmt, ...);
void    printch(char ch);
void    printdec(int dec);
void    printflt(double flt);
void    printbin(int bin);
void    printhex(int hex);
void    printstr(char* str);

#define console_print(ch)    putchar(ch)

#endif    /*#ifndef __PRINT_H_*/

上面print函数为全功能的打印函数,可以实现类似printf的功能,printch实现单个字符的打印、printdec实现十进制格式数字的打印,printflt实现浮点数的打印,printbin实现二进制格式数字的打印,printhex实现十六进制格式数字的打印,printstr实现字符串的打印,console_print函数是串口单字符打印函数的宏定义,这里暂时用PC终端单字符打印函数putchar代替。在实际嵌入式环境下,替换成串口单字符打印函数即可。
print.c

#include <stdio.h>
#include <stdarg.h>
#include "print.h"

int main(void)
{
    print("print: %c\n", 'c');
    print("print %d\n", 1234567);
    print("print: %f\n", 1234567.1234567);
    print("print: %s\n", "string test");
    print("print: %b\n", 0x12345ff);
    print("print: %x\n", 0xabcdef);
    print("print: %%\n");
    return 0;
}

void    print(char* fmt, ...)
{
    double vargflt = 0;
    int  vargint = 0;
    char* vargpch = NULL;
    char vargch = 0;
    char* pfmt = NULL;
    va_list vp;

    va_start(vp, fmt);
    pfmt = fmt;

    while(*pfmt)
    {
        if(*pfmt == '%')
        {
            switch(*(++pfmt))
            {
                
                case 'c':
                    vargch = va_arg(vp, int); 
                    /*    va_arg(ap, type), if type is narrow type (char, short, float) an error is given in strict ANSI
                        mode, or a warning otherwise.In non-strict ANSI mode, 'type' is allowed to be any expression. */
                    printch(vargch);
                    break;
                case 'd':
                case 'i':
                    vargint = va_arg(vp, int);
                    printdec(vargint);
                    break;
                case 'f':
                    vargflt = va_arg(vp, double);
                    /*    va_arg(ap, type), if type is narrow type (char, short, float) an error is given in strict ANSI
                        mode, or a warning otherwise.In non-strict ANSI mode, 'type' is allowed to be any expression. */
                    printflt(vargflt);
                    break;
                case 's':
                    vargpch = va_arg(vp, char*);
                    printstr(vargpch);
                    break;
                case 'b':
                case 'B':
                    vargint = va_arg(vp, int);
                    printbin(vargint);
                    break;
                case 'x':
                case 'X':
                    vargint = va_arg(vp, int);
                    printhex(vargint);
                    break;
                case '%':
                    printch('%');
                    break;
                default:
                    break;
            }
            pfmt++;
        }
        else
        {
            printch(*pfmt++);
        }
    }
    va_end(vp);
}

void    printch(char ch)
{
    console_print(ch);
}

void    printdec(int dec)
{
    if(dec==0)
    {
        return;
    }
    printdec(dec/10);
    printch( (char)(dec%10 + '0'));
}

void    printflt(double flt)
{
    int icnt = 0;
    int tmpint = 0;
    
    tmpint = (int)flt;
    printdec(tmpint);
    printch('.');
    flt = flt - tmpint;
    tmpint = (int)(flt * 1000000);
    printdec(tmpint);
}

void    printstr(char* str)
{
    while(*str)
    {
        printch(*str++);
    }
}

void    printbin(int bin)
{
    if(bin == 0)
    {
        printstr("0b");
        return;
    }
    printbin(bin/2);
    printch( (char)(bin%2 + '0'));
}

void    printhex(int hex)
{
    if(hex==0)
    {
        printstr("0x");
        return;
    }
    printhex(hex/16);
    if(hex < 10)
    {
        printch((char)(hex%16 + '0'));
    }
    else
    {
        printch((char)(hex%16 - 10 + 'a' ));
    }
}

编译执行结果如下:

print: c
print: 1234567
print: 1234567.123456
print: string test
print: 0b1001000110100010111111111
print: 0xabcdef
print: %
如上所示,print函数实现了类似printf的功能。





                
在Keil环境中自行实现printf函数,通常采用重定向`fputc`函数的方法。因为`printf`函数在输出字符时会调用`fputc`函数,通过重写`fputc`函数可以将输出重定向到目标设备,如串口。 以下是具体实现步骤及示例代码: 1. **包含必要的头文件**:需要包含`<stdio.h>`头文件,因为标准的`printf`函数相关的定义在这个头文件中。 ```c #include <stdio.h> ``` 2. **实现`fputc`函数**:将输出重定向到目标设备。以下是一个简单的串口输出重定向示例: ```c #include <stdio.h> // 假设这里是串口发送数据的函数 void usart_data_transmit(int uartNo, uint8_t ch); // 假设这里是获取串口发送缓冲区空标志的函数 int usart_flag_get(int uartNo, int flag); // 全局的串口硬件信息结构体 typedef struct { int uartNo; } UartHwInfo; UartHwInfo g_uartHwInfo; // 重定向fputc函数 int fputc(int ch, FILE *f) { usart_data_transmit(g_uartHwInfo.uartNo, (uint8_t)ch); while(0 == usart_flag_get(g_uartHwInfo.uartNo, 0x20)); // 等待发送缓冲区为空 return ch; } ``` 3. **使用自定义的`printf`函数**:在完成上述步骤后,就可以像使用标准的`printf`函数一样使用它了。 ```c int main() { g_uartHwInfo.uartNo = 0; // 假设使用串口0 printf("Hello, World!\n"); return 0; } ``` ### 代码解释 - `#include <stdio.h>`:包含标准输入输出头文件,提供了`printf`等函数的声明。 - `fputc`函数:该函数接收一个字符`ch`和一个文件指针`f`,将字符`ch`通过串口发送出去,并等待发送缓冲区为空,最后返回该字符。 - `main`函数:初始化串口信息,然后使用`printf`函数输出字符串。 ### 注意事项 - 示例中的`usart_data_transmit`和`usart_flag_get`函数需要根据具体的硬件平台和串口驱动进行实现。 - 不同的硬件平台和串口配置可能会有所不同,需要根据实际情况调整代码。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值