① ESP8266 开发学习笔记_By_GYC 【更新 ets_printf 函数 使ESP_IDF 能够支持浮点数打印】

① ESP8266 开发学习笔记_By_GYC 【更新 ets_printf 函数 使ESP_IDF 能够支持浮点数打印】

在我们日常的开发过程中,经常使用到的一个功能就是串口打印功能。在ESP8266的IDF框架中,提供了类似控制台的printf操作,可以向串口打印一些信息,但是ESP8266的printf函数被封装经过了简化,不支持浮点数的格式控制符%f。IDF框架中拥有ets_printf函数可以替代封装的printf函数,这个函数在SDK中有源码,可以供我们学习和修改。这篇文章就是总结一下我自己对ESP-IDF工程中的ets_printf.c文件的修改,以实现在ESP8266系统中ets_printf对浮点数的格式控制符的支持。

 

目录

一、启用ets_printf函数

二、修改ets_printf函数

三、直接使用


一、启用ets_printf函数

使用ets_printf函数需要在工程配置里设置一下,在linux终端工程目录下输入

make menuconfig

进入工程配置页面

① ESP8266 开发学习笔记_By_GYC 【更新 ets_printf 函数 使ESP_IDF 能够支持浮点数打印】_第1张图片

 选择Component config选项卡  Enter进入 修改ESP8266工程宏定义配置

① ESP8266 开发学习笔记_By_GYC 【更新 ets_printf 函数 使ESP_IDF 能够支持浮点数打印】_第2张图片

选择ESP8266-specific选项 Enter进入

① ESP8266 开发学习笔记_By_GYC 【更新 ets_printf 函数 使ESP_IDF 能够支持浮点数打印】_第3张图片

 选择 Using new ets_vprintf instead of rom code 选项 按下“空格”勾选。

最后选择  < Save > Enter 确认 ,

然后一直选择< Exit >退出配置页。

至此,ets_printf.c 文件中的内容就生效,替换了原来rom中的ets_printf函数了。

 

二、修改ets_printf函数

官方库里提供的ets_printf函数仍然不支持浮点数的格式控制符,但是源码已经给出了,我们可以在源码基础上修改,使它支持格式控制符“%f”

浮点数的格式控制包括“f”、“0”、“.”、数字几个操作,其中“0”、“.”和数字控制符都已经在格式控制系统内了,我们只需要写“f”对应的函数,并作为分支插入格式控制处理的switch结构里就可以了。

编写的过程参照了%d的格式处理过程:

case 'd':
         attr.value.val32 = va_arg(va, int);
         if (attr.value.val32 < 0) {
            ets_putc('-');
            attr.value.val32 = -attr.value.val32;
         }
         ets_printf_int(&attr, 10);
         break;

 由%d的 处理我发现需要在attr结构体里添加double型的变量,用来缓存变参数列表里的float或者double类型的变量。

 原结构体类型是下面这样的:

typedef union _val_cache {
    uint8_t         val8;
    int32_t         val32;
    uint32_t        val32u;
    const char      *valcp;
} val_cache_t;

 添加double类型的变量valfloat作为浮点数的缓存变量。新结构体类型如下:

typedef union _val_cache {
    uint8_t         val8;
    int32_t         val32;
    uint32_t        val32u;
    const char      *valcp;
    double           valfloat;
} val_cache_t;

接下来,我们就可以在扫描格式控制的函数里,仿照%d的格式控制方式添加%f的格式控制了

涉及到的函数是

int ets_vprintf(const char *fmt, va_list va)

在遍历扫描的switch里添加

case 'f':

在确定了本次所有格式控制操作之后的执行将数值写入打印缓存的switch操作里添加对应的float打印操作

 case 'f':
     attr.value.valfloat = va_arg(va, double);
     if (attr.value.valfloat < 0) {
         ets_putc('-');
         attr.value.valfloat = -attr.value.valfloat;
     }
     ets_printf_float(&attr);
     break;

首先获取可变参列表里的double数据,判断是否为负,并把绝对值传递给专门的float打印函数。

专门的float型数据打印处理函数ets_printf_float(&attr)是我自己根据这套打印结构的特点写的,参照了int型数据的处理过程,内容如下:

#define FLOAT_decimals_MAX_NUM 9
#define VFLOAT_STR_MAX 20

static int ets_printf_float(val_attr_t * const attr)
{
    char buf[VFLOAT_STR_MAX];
    unsigned char offset = VFLOAT_STR_MAX;

    int32_t integer=attr->value.valfloat;
    double decimals = (attr->value.valfloat-integer);  

    if (attr->precision!=0) {
        for (int i =0; iprecision;i++) {
            decimals=decimals*10.0;
        }

        integer=decimals;
        if(decimals-integer>0.5)//末位四舍五入
            integer++;

        for (int i =0; iprecision;i++) {
            unsigned char c = integer % 10;
            buf[--offset] = c + '0';
            integer /= 10;
        }
    }
    else
    {
        int i =0;
        for (i =0; i0.5)
            integer++;
        for (; i>0;i--) {
            unsigned char c = integer % 10;
            buf[--offset] = c + '0';
            integer /= 10;
        }
    }

    buf[--offset] = '.';

    integer=attr->value.valfloat;

    if (integer != 0) {
        for (; integer > 0; integer /= 10) {
            unsigned char c = integer % 10;
                buf[--offset] = c + '0';
        }
    } else
        buf[--offset] = '0';

    if (fill_num(attr)) {
        char fill_data = isfill_0(attr) ? '0' : ' ';
        unsigned char len = fill_num(attr) - (VFLOAT_STR_MAX - offset);
        unsigned char left = fill_num(attr) > (VFLOAT_STR_MAX - offset) ? len : 0;

        if (!isfill_left(attr)) {
            ets_printf_ch_mutlti(fill_data, left);
        }

        ets_printf_buf(&buf[offset], VFLOAT_STR_MAX - offset);

        if (isfill_left(attr)) {
            fill_data = ' ';
            ets_printf_ch_mutlti(fill_data, left);
        }
    } else {
        ets_printf_buf(&buf[offset], VFLOAT_STR_MAX - offset);
    }

    return 0;
}

有了这个函数就可以实现ESP8266的浮点数打印操作了。其他地方完全不用修改的。其中我设置了小数点后最大位数为9位,最大字符长度为20位,如果需要超过这个长度的数据打印,可以修改此处宏定义。(实际上double型的数据可以很长很长……)

有一点需要注意的是,格式控制符中有效数字个数包括小数点。这是和计算机中的printf格式控制保持一致的。

三、直接使用

对于不关注技术实现细节的小伙伴可以直接下载我写好的文件,直接替换掉components/esp8266/source路径下原来的ets_printf.c文件即可实现浮点数的打印。

文件在我的github上(https://github.com/gengyuchao)。

 

 

欢迎关注我的博客和github呀~希望能够和各路大佬一起讨论技术问题~

 

 

 

你可能感兴趣的:(Github,ESP,ESP8266小项目学习笔记,ESP8266,ESP-IDF,printf,浮点数打印)