ESP8266开发学习笔记_浮点数打印支持

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函数需要在工程配置里设置一下,在linux终端工程目录下输入

make menuconfig

进入工程配置页面

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

选择ESP8266-specific选项 Enter进入

 选择 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的格式处理过程:

1
2
3
4
5
6
7
8
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类型的变量。

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

1
2
3
4
5
6
typedef union _val_cache {
uint8_t val8;
int32_t val32;
uint32_t val32u;
const char *valcp;
} val_cache_t;

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

1
2
3
4
5
6
7
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里添加

1
case 'f':

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

1
2
3
4
5
6
7
8
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型数据的处理过程,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#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; i<attr->precision;i++) {
decimals=decimals*10.0;
}

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

for (int i =0; i<attr->precision;i++) {
unsigned char c = integer % 10;
buf[--offset] = c + '0';
integer /= 10;
}
}
else
{
int i =0;
for (i =0; i<FLOAT_decimals_MAX_NUM;i++) {
decimals=decimals*10.0;
int digit=decimals;
if((decimals-(digit))==0.0)
{
i++;
break;
}
}
integer=decimals;
if(decimals-integer>0.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呀~ 希望能够和各路大佬一起讨论技术问题~

Comments

:D 一言句子获取中...

Loading...Wait a Minute!