ESP32在休眠模式下保持PWM输出

前言

IDF手册上说如果PWM的clk_cfg配置为RTC8M_CLK支持Light-Sleep模式,但是官方的Repo里面也没有如何使用这个特性的例子.

实操

经过一圈摸索.需要配置menuconfig才行

1
idf.py menuconfig

找到Component config->Hardware Settings -> Sleep Config, 关闭light sleep GPIO reset workaround

在esp-idf内置的LEDC (LED Controller) basic example的基础上修改,得到以下代码

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

#include <stdio.h>

#include "driver/ledc.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "rtc.h"

#define PIN_LED GPIO_NUM_5

static void example_ledc_init(void) {
// Prepare and then apply the LEDC PWM timer configuration
ledc_timer_config_t ledc_timer = {
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 1000,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
.clk_cfg = LEDC_USE_RTC8M_CLK,
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

// Prepare and then apply the LEDC PWM channel configuration
ledc_channel_config_t ledc_channel = {.channel = LEDC_CHANNEL_0,
.duty = 128,
.gpio_num = PIN_LED,
.speed_mode = LEDC_LOW_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}
void app_main(void) {
// Set the LEDC peripheral configuration
example_ledc_init();
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC8M, ESP_PD_OPTION_ON);
while (1) {
for (int i = 1; i < (1 << 13); i += 128) {
ESP_LOGE("Duty", "Current Duty is %d", i);
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, i));
// Update duty to apply the new value
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
esp_sleep_enable_timer_wakeup(1000 * 1000 * 1);
esp_light_sleep_start();
}
}
}

ESP32-C3 PlatformIO 'embed_txtfiles' 修复

前言

9.9元入手了一块ESP32-C3板子, 用来取代手上引脚不足的ESP8266.
但是编译时候总是失败,从Log上看是这一步出了错误.

1
2
3
4
5
prepare_file([".pio\build\esp32c3\lite.ttf.txt.o"], ["src\lite.ttf"])
Converting .pio\build\esp32c3\lite.ttf.txt.o
'xtensa-esp32-elf-objcopy' \xb2\xbb\xca\xc7\xc4ڲ\xbf\xbb\xf2\xcdⲿ\xc3\xfc\xc1Ҳ\xb2\xbb\xcaǿ\xc9\xd4\xcb\xd0еij\xcc\xd0\xf2
\xbb\xf2\xc5\xfa\xb4\xa6\xc0\xed\xceļ\xfe\xa1\xa3
*** [.pio\build\esp32c3\lite.ttf.txt.o] Error 1

此外并没有任何提示了.

错误分析

lite.ttf是我的一个放在src目录下的文件,

为其在platformio.ini配置”board_build.embed_txtfiles = src/lite.ttf”,就可以让这个文件嵌入固件中.
从日志里看到是转化的时候出了问题, xtensa-esp32-elf-objcopy

xtensa是ESP32,ESP32-S2,ESP32-S3的处理器结构,而ESP32-C3是RISC-V的,所以这里出错应该是结构不匹配.
去ESP-IDF的文件夹搜索”elf-objcopy”后发现的确有不同很多类似关键字.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for dir in esp32 esp32s2 esp32c3 esp32s3; do
if [ $dir = esp32 ]; then
TOOLCHAIN="xtensa-esp32-elf"
elif [ $dir = esp32s2 ]; then
TOOLCHAIN="xtensa-esp32s2-elf"
elif [ $dir = esp32c3 ]; then
TOOLCHAIN="riscv32-esp-elf"
elif [ $dir = esp32s3 ]; then
TOOLCHAIN="xtensa-esp32s3-elf"
else
echo "$dir does not exist"
fi
if [ -d "$dir" ]; then
cd $dir
git status libphy.a | grep "modified" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo $dir/libphy.a fixed
$TOOLCHAIN-objcopy --redefine-sym ets_printf=phy_printf libphy.a
fi

所以应该是PlatformIO在转化的时候调用了错误的程序导致的.

解决问题

去PlatformIO的工作目录(.platformio)下搜索xtensa-esp32-elf-objcopy

1
2
3
4
5
6
7
8
9
10
11
12
13
[
"xtensa-esp32-elf-objcopy",
"--input-target",
"binary",
"--output-target",
"elf32-xtensa-le",
"--binary-architecture",
"xtensa",
"--rename-section",
".data=.rodata.embedded",
"$SOURCE",
"$TARGET",
]

确实搜索到了多个包含这个关键字的文件_embed_files.py
(platforms\espressif32\builder\frameworks_embed_files.py)
其中platforms下有好几个文件夹

1
2
3
4
5
6
7
├── .platformio
│ └── platforms
│ └── espressif32
│ └── espressif32@src-ba2d3999402da5eaf2c9d5863ef113c7
│ └── espressif32@src-ebefb4289db63a9f0ca5ee29fa328eef
│ └── espressif8266
│ └── raspberrypi

这应该是不同项目所需的platform,凑巧这次C3项目的platform是专门下载的,所以从文件夹修改日期上来看,我这次C3使用的应该是espressif32@src-ebefb4289db63a9f0ca5ee29fa328eef
打开espressif32@src-ebefb4289db63a9f0ca5ee29fa328eef下的_embed_files.py
里面果然还在使用xtensa-esp32-elf-objcopy
按照ESP-IDF里面的描述,应该使用riscv32-esp-elf-objcopy,并且要把xtensa相关的都修改为riscv.

所以最后修改成了

1
2
3
4
5
6
7
8
9
10
11
12
13
[
"riscv32-esp-elf-objcopy",
"--input-target",
"binary",
"--output-target",
"elf32-littleriscv",
"--binary-architecture",
"riscv",
"--rename-section",
".data=.rodata.embedded",
"$SOURCE",
"$TARGET",
]

重新执行编译,这一次终于通过了.

1
2
3
4
5
6
7
8
9
10
11
12
13
Building in release mode
prepare_file([".pio\build\esp32c3\lite.ttf.txt.o"], ["src\lite.ttf"])
Converting .pio\build\esp32c3\lite.ttf.txt.o
revert_original_file([".pio\build\esp32c3\lite.ttf.txt.o"], ["src\lite.ttf"])
Linking .pio\build\esp32c3\firmware.elf
Retrieving maximum program size .pio\build\esp32c3\firmware.elf
Checking size .pio\build\esp32c3\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM: [ ] 4.7% (used 15480 bytes from 327680 bytes)
Flash: [= ] 14.1% (used 443870 bytes from 3145728 bytes)
Building .pio\build\esp32c3\firmware.bin
esptool.py v3.1
Merged 2 ELF sections

_embed_files.py

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# Copyright 2014-present PlatformIO <[email protected]>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import shutil
from os import SEEK_CUR, SEEK_END
from os.path import basename, isfile, join

from SCons.Script import Builder

Import("env")

board = env.BoardConfig()

#
# Embedded files helpers
#


def extract_files(cppdefines, files_type):
files = []
if "build." + files_type in board:
files.extend(
[
join("$PROJECT_DIR", f)
for f in board.get("build." + files_type, "").split()
if f
]
)
else:
files_define = "COMPONENT_" + files_type.upper()
for define in cppdefines:
if files_define not in define:
continue

value = define[1]
if not isinstance(define, tuple):
print("Warning! %s macro cannot be empty!" % files_define)
return []

if not isinstance(value, str):
print(
"Warning! %s macro must contain "
"a list of files separated by ':'" % files_define
)
return []

for f in value.split(":"):
if not f:
continue
files.append(join("$PROJECT_DIR", f))

for f in files:
if not isfile(env.subst(f)):
print('Warning! Could not find file "%s"' % basename(f))

return files


def remove_config_define(cppdefines, files_type):
for define in cppdefines:
if files_type in define:
env.ProcessUnFlags("-D%s" % "=".join(str(d) for d in define))
return


def prepare_file(source, target, env):
filepath = source[0].get_abspath()
shutil.copy(filepath, filepath + ".piobkp")

with open(filepath, "rb+") as fp:
fp.seek(-1, SEEK_END)
if fp.read(1) != "\0":
fp.seek(0, SEEK_CUR)
fp.write(b"\0")


def revert_original_file(source, target, env):
filepath = source[0].get_abspath()
if isfile(filepath + ".piobkp"):
shutil.move(filepath + ".piobkp", filepath)


def embed_files(files, files_type):
for f in files:
filename = basename(f) + ".txt.o"
file_target = env.TxtToBin(join("$BUILD_DIR", filename), f)
env.Depends("$PIOMAINPROG", file_target)
if files_type == "embed_txtfiles":
env.AddPreAction(file_target, prepare_file)
env.AddPostAction(file_target, revert_original_file)
env.AppendUnique(PIOBUILDFILES=[env.File(join("$BUILD_DIR", filename))])


def transform_to_asm(target, source, env):
files = [join("$BUILD_DIR", s.name + ".S") for s in source]
return files, source


env.Append(
BUILDERS=dict(
TxtToBin=Builder(
action=env.VerboseAction(
" ".join(
[
"riscv32-esp-elf-objcopy",
"--input-target",
"binary",
"--output-target",
"elf32-littleriscv",
"--binary-architecture",
"riscv",
"--rename-section",
".data=.rodata.embedded",
"$SOURCE",
"$TARGET",
]
),
"Converting $TARGET",
),
suffix=".txt.o",
),
TxtToAsm=Builder(
action=env.VerboseAction(
" ".join(
[
join(
env.PioPlatform().get_package_dir("tool-cmake") or "",
"bin",
"cmake",
),
"-DDATA_FILE=$SOURCE",
"-DSOURCE_FILE=$TARGET",
"-DFILE_TYPE=TEXT",
"-P",
join(
env.PioPlatform().get_package_dir("framework-espidf") or "",
"tools",
"cmake",
"scripts",
"data_file_embed_asm.cmake",
),
]
),
"Generating assembly for $TARGET",
),
emitter=transform_to_asm,
single_source=True,
),
)
)


flags = env.get("CPPDEFINES")
for files_type in ("embed_txtfiles", "embed_files"):
if (
"COMPONENT_" + files_type.upper() not in env.Flatten(flags)
and "build." + files_type not in board
):
continue

files = extract_files(flags, files_type)
if "espidf" in env.subst("$PIOFRAMEWORK"):
env.Requires(join("$BUILD_DIR", "${PROGNAME}.elf"), env.TxtToAsm(files))
else:
embed_files(files, files_type)
remove_config_define(flags, files_type)

环境:

1
2
3
4
5
PLATFORM: Espressif 32 (3.3.0+sha.3b5de56) > Espressif ESP32 Dev Module
- framework-arduinoespressif32 0.0.0+sha.68daea4
- tool-esptoolpy 1.30100.210531 (3.1.0)
- toolchain-riscv-esp 1.80400.0 (8.4.0)
- toolchain-riscv32-esp 8.4.0+2021r1

ESP32并口屏幕和串口屏幕下帧率的对比

前言

之前在ESP32上运行LVGL一直使用的是SPI串口驱动LCD, 当时就比较好奇如果换成了并口驱动LCD会对帧率有何影响.于是乎终于在2022年后买入了一块并口屏幕来测试一下.

测试项目

  • 使用LVGL自带的lv_demo_music,开启LV_DEMO_MUSIC_AUTO_PLAY
  • 使用TFT_eSPI自带的Viewport_graphicstest

测试结果

运行LVGL自带的lv_demo_music

对照组序号 屏幕参数 ESP32运行频率 LVGL缓冲参数 SPI速率 TFT_eSPI版本 LVGL版本 帧率
1 240x240 1.54寸 SPI LCD 240MHz 240*120 启用DMA,未使用双缓冲 60MHz 2.3.70 8.1.1-dev 26帧
2 240x240 1.33寸 8位并口 LCD 240MHz 240*120 未使用双缓冲 \ 2.3.70 8.1.1-dev 28帧

感觉帧率差异不是很大的样子.

运行TFT_eSPI自带的Viewport_graphicstest

对照组序号 屏幕参数 ESP32运行频率 SPI速率 TFT_eSPI版本
1 240x240 1.54寸 SPI LCD 240MHz 60MHz 2.3.70
2 240x240 1.33寸 8位并口 LCD 240MHz \ 2.3.70

运行时候明显感觉到并口的绘制速度更快,不过还是得数据说话,于是有如下Log

8位并口结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TFT_eSPI library test!
Benchmark Time (microseconds)
Screen fill 53073
Text 21126
Lines 36424
Horiz/Vert Lines 10469
Rectangles (outline) 5918
Rectangles (filled) 122742
Circles (filled) 28151
Circles (outline) 22372
Triangles (outline) 14111
Triangles (filled) 46193
Rounded rects (outline) 16475
Rounded rects (filled) 127122
Done!

串口结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TFT_eSPI library test!
Benchmark Time (microseconds)
Screen fill 102725
Text 12506
Lines 38321
Horiz/Vert Lines 9293
Rectangles (outline) 6594
Rectangles (filled) 234378
Circles (filled) 30350
Circles (outline) 16884
Triangles (outline) 12893
Triangles (filled) 79688
Rounded rects (outline) 11730
Rounded rects (filled) 240258
Done!

并口驱动情况下绘制时间几乎是串口驱动绘制时间的一半.

果然还是有提升的.

至于为什么在LVGL中拉不开差距,经过群友Principle点拨后意识到,运行LVGL时候CPU性能方面出现了瓶颈,我的ESP32是初代版本,如果换成最新的ESP32 S3则会拉开差距.

© 2025 Do U Find IT? All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero