【MicroPython】开发环境搭建【Thonny】软件安装及【flash_download_tool】固件下载烧录(C 模块)
一、MicroPython简介
MicroPython确实是一种非常适合在资源受限的微控制器上运行的Python解释器版本。它被设计成可以轻松地集成到各种嵌入式系统中,并且能够提供接近于原生代码的性能,这在物联网(IoT)项目和其他需要在小型设备上运行复杂脚本的场景中尤其有用。
以下是MicroPythons的一些关键特性和优势:
-
Python 3 兼容性:
MicroPython支持Python 3的语法和特性,这意味着你可以使用Python 3中的大多数语言结构和标准库函数。例如,你可以使用列表推导式、生成器表达式、字典和集合等高级数据结构。 -
内置模块和库:
MicroPython包含了一些专门为微控制器设计的模块,如utime
用于时间相关的操作,machine
模块提供了对微控制器硬件(如GPIO引脚、ADC、DAC、SPI、I2C等)的直接访问。 -
轻量级和高效:
MicroPython被优化以在有限的RAM和闪存中运行,通常只需要几百K的闪存空间和几十K的RAM就能运行。它的内存管理机制也进行了优化,以减少碎片和提高性能。 -
可扩展性:
开发者可以通过编写C语言扩展模块来增强MicroPython的功能,这些模块可以添加对特定硬件的支持或实现高性能计算密集型任务。 -
跨平台支持:
MicroPython可以在多种微控制器架构上运行,包括ARM Cortex-M系列、ESP8266、ESP32、STM32等,甚至可以在某些具有Linux内核的设备上作为用户空间程序运行。 -
社区和生态系统:
MicroPython有一个活跃的社区,提供了大量的教程、示例代码和第三方库,这使得新用户更容易上手,同时也促进了硬件和软件的创新。
由于其易用性和高效性,MicroPython已经成为教育、原型制作和产品开发领域的一个重要工具,特别是在那些需要快速迭代和简化开发流程的场合。
MicroPython操作微控制器
在MicroPython环境中,开发者可以利用这种精简的Python解释器直接在微控制器上编写和运行代码。下面是各个组件如何协同工作的更详细说明:
-
微控制器硬件:
微控制器是整个系统的物理基础,它包含CPU、RAM、闪存和一系列的外设接口,如GPIO(通用输入/输出)、ADC(模数转换器)、DAC(数模转换器)、SPI(串行外设接口)、I2C(集成电路总线)、PWM(脉冲宽度调制)等。常见的微控制器芯片有ESP32、ESP8266、STM32、Arduino等。 -
MicroPython固件:
这是由MicroPython项目提供的固件,它包含了MicroPython解释器以及一系列针对微控制器优化的标准库和额外模块。固件通常是基于特定的微控制器开发框架(如ESP-IDF、STM32 HAL库等)构建的,这样可以确保MicroPython能够充分利用硬件的特性。开发者通过烧录固件到微控制器上,使微控制器能够理解和执行MicroPython代码。 -
用户程序:
这是开发者使用MicroPython语言编写的代码,它可以包括对微控制器硬件的直接操作,如读取传感器数据、控制LED灯、与无线网络通信等。用户程序可以是简单的脚本文件,也可以是包含多个模块的复杂项目。
开发者通过串口连接或者WiFi等方式将用户程序上传到已经安装了MicroPython固件的微控制器上。然后,微控制器上的MicroPython解释器会逐行解析并执行这些代码,从而实现对硬件的控制和所需功能的实现。
MicroPython为开发者提供了一种简洁而强大的方式来与微控制器交互,同时保持了Python语言的易用性和灵活性。
Thonny IDE 简介
Thonny是一种免费且开源的集成开发环境(IDE),特别设计用于Python编程的教学和学习。它旨在帮助初学者更容易地入门编程,同时也适合有一定经验的程序员使用。Thonny支持多种版本的Python,包括标准Python和MicroPython,这使得它成为学习和开发微控制器项目的好工具。
以下是Thonny的一些主要特点:
-
代码高亮与调试:Thonny提供了语法高亮和基本的调试功能,如设置断点、单步执行和查看变量值,这有助于理解代码的执行流程和调试错误。
-
实时反馈:当代码运行时,Thonny会实时显示控制台输出,便于观察程序的运行状态。
-
简单友好的界面:Thonny有一个直观的用户界面,包括文件管理、代码编辑、运行控制和输出窗口,所有这些都集中在一个易于导航的环境中。
-
MicroPython支持:Thonny可以与各种微控制器配合使用,比如ESP8266、ESP32、Raspberry Pi Pico和其他基于MicroPython的设备。它允许直接上传和运行MicroPython代码,无需离开IDE。
-
教育功能:Thonny包括一些教学功能,如“Turtle”图形库的可视化,以及一个“Learn to program”教程,这些都是为了帮助新手更好地理解编程概念。
-
多平台兼容性:Thonny可以在Windows、macOS和Linux操作系统上运行,这增加了它的可用性和可访问性。
要使用Thonny进行MicroPython开发,你需要做的是:
- 安装Thonny IDE。
- 配置Thonny以识别你的微控制器和相应的串口。
- 编写或上传MicroPython代码到微控制器上。
- 使用Thonny的功能来测试和调试你的代码。
Thonny对于学习编程和进行实际项目开发都是一个很好的选择。
二、Thonny软件安装与使用
安装基本没有什么需要配置选项PC软件正常安装流程
可修改下载路径 和 “create desktop icon
”选项 创建桌面图标界面
这里就选择简体中文和标准(Standard)设置。
三、Thonny 工具的框架解析
1. 菜单板块
在这个板块下包含了文件、编辑、视图、运行、工具和帮助选项。
① 文件选项:
Thonny 工具的文件功能主要是指在程序运行过程中,对文件输入/输出的处理。具体功能包括:新建文件、打开新建、关闭文件等操作,文本界面如下图所示。
② 编辑选项:
Thonny 工具提供了丰富的编辑功能,包括复制/剪切/粘贴、撤销/重做、查找/替换、自动补全、缩进与反缩进选择行、注释代码、折等操作,可以帮助用户更方便地编辑代码文本。编辑界面如下图所示。
③ 视图选项:
Thonny 工具的视图功能主要是指在程序运行过程中,通过勾选不同的选项来显示和关闭不同的视图窗口。有些视图窗口会显示程序运行过程中变量的值,这对于调试程序非常有帮助。
④ 运行选项:
Thonny 工具提供了运行 Python 脚本、单步执行、中断执行、停止/重启后端进程、运行脚本等功能,帮助用户控制和调试 Python 脚本的执行过程。运行界面如下图所示。
⑤ 工具选项:
在“工具”选项中,用户可以管理插件和软件。例如,可以安装和卸载插件,配置解释器和编辑器属性等。工具界面如下图所示。
对于管理插件,用户可以浏览可用的插件列表,选择需要安装的插件,并查看已安装的插件列表。如果需要卸载某个插件,只需选择该插件并点击“卸载”按钮即可。
打开“工具”选项,接着点击管理插件进入插件管理界面,如下图所示。
然后,我们在文本框输入要安装的插件,这里我们安装“thonny-black-format
”插件,接着,点击“在 PyPI 上搜索”,搜索成功后的界面如下所示:
上图中,我们选择“thonny-black-forma
t”插件,然后进入安装界面,如下图所示。
到了这里,我们点击“安装”选项,即可安装这个插件,安装成功后需重启 Thonny 软件,在“工具”选项下会多出“thonny-black-format
”插件。
2. 调试与下载板块
这个板块的功能只不过在菜单板块下截取一些非常实用的功能,如下载调试、新建文件和另存文件等操作。
3. 文本编辑板块
这一个板块是 Python 代码的编辑器,用来编写代码用的。
4. 5. 本地浏览板块和设备存储板块
本地浏览板块可以用来浏览电脑磁盘的内容,而设备存储板块则可以显示 ESP32 开发板的内部文件系统。这些功能使得我们可以在开发过程中,方便地查看和编辑文件。若用户在本地编写好的 py 脚本,可直接保存到 MicroPython 设备(ESP32-S3 内部文件系统)当中。
6. 7. 交互板块和解释器选择板块
交互板块是指 Python 解释器与用户进行交互的界面,它可以用于输出程序结果、错误信息以及其他相关信息,如下图 5.2.2.20 所示。而解释器选择板块则是指选择哪个 Python 解释器来编译和运行 Python 脚本,如下图所示。
在 ESP32 开发板上,通常会选择 MicroPython 解释器来编译和运行 Python 脚本。这是因为MicroPython 是专门为微控制器和嵌入式系统设计的 Python 3 解释器,它具有轻量级、高效和易于使用等特点,非常适合在 ESP32 这样的低功耗设备上运行。
在本地计算机上运行 Python 程序时,则通常会选择本地 Python 3 解释器。这是因为 Python是一种跨平台的语言,可以在不同的操作系统和设备上运行,而本地 Python 解释器则是针对特定平台和操作系统的。
四、下载固件及测试
ESP-32S3 开发 Thonny 的基本配置
使用 Thonny 工具进行 ESP32-S3 应用开发,需要完成以下配置:
打开 Thonny 工具,点击“运行”->“配置解释器”选项。
在配置解释器界面下配置解释器和串口端口,如下图所示。
配置完成后,就可以在 Thonny 工具中开发 ESP32-S3 芯片了。然而,在进行开发之前,需要用户将 MicroPython固件下载到 ESP32-S3芯片中,以便在 Thonny工具中进行开发。固件的下载和烧录将在下一小节中讲解。
固件的下载与烧录
MicroPython 官方提供了多种固件,可直接下载后烧录至开发板运行。
官方的固件大致分为 pyboard、ESP 系列、STM32 系列等几类,下面分别说明:
- pyboard 固件
从 v1.9.1 版本开始,pyboard 的固件分为下面几个不同版本:
① 标准版(standard)
② 双精度浮点版(double FP)
③ 线程版(threading)
④ 双进度浮点+线程版(double FP + threading)
⑤ 网络版(network)
这些版本具有相同的底层功能和包含的模块,但具有不同的专项优化。双精度浮点版本提升了浮点运算的精度,一般版本使用 32 位单精度浮点,而双精度浮点版本则使用 64 位。线程版本支持多线程功能,适合处理并行任务。双精度浮点+线程版本同时具备高精度浮点运算和多线程功能。网络版本通过 SPI接口连接外部网络模块(CC3000等),为 pyboaed增加网络功能。 - ESP 系列固件
ESP 系列包括 ES8266、ESP32、ESP32S3 等多款芯片,每款芯片都有其特定的固件。虽然MicroPython 为这些芯片提供了相应的固件,但有时候这些固件可能不能满足开发需求。因为MicroPython 的固件是针对特定型号的,所以一个芯片的固件资源无法与其他类型的芯片匹配。 - STM32 系列固件
除了前面几种固件外,MicroPython 官方还提供了下面几种芯片的固件,它们分别为:stm32f0、 stm32f4、stm32f7、stm32g0、stm32g4、stm32h5、stm32h7、stm32l0、stm32l1、stm32l4、stm32wb 和 stm32wl。 - MicroPython 中文社区固件
虽然MicroPython官方提供了多种固件,但仍然有许多开发板没有官方支持的固件。对于许多使用者来说,编译源码是一项复杂和具有挑战性的任务。为了解决这个问题,MicroPython 中文社区特别提供了多种常见开发板的预编译固件,以便大家能够轻松地使用和测试这些开发板。
MicroPython 中文社区地址是:https://git.oschina.net/shaoziyang/。
在官方主界面下,点击“DOWNLOAD
”下载选项,然后找到“ESP32
”端口,点击“esp32
”选项进入 ESP32 MicroPython 固件下载网页,接着往下找到“ESP32-S3 Espressif
”选择,如下图所示。
开发板的模组为 ESP32-S3-WROOM-1-N16R8 型号,从《esp32-s3-wroom-1_wroom-1u_datasheet_cn.pdf》模组数据手册
ESP32-S3 MicroPython 固件下载界面,在此界面下找到 Firmware(Support for Octal-SPIRAM
)标签
下载固件成功之后,我们得到了 ESP32_GENERIC_S3-SPIRAM_OCT-20240602-v1.23.0.bin
固件文件,下面我们把这个固件烧录到 ESP32-S3 开发板中,这里会涉及到两种烧录方式。第一种是使用 Thonny
工具烧录,另一种是使用乐鑫官方提供的烧录固件软件 flash_download_tool
。
1. 使用 Thonny 工具烧录:
在 Thonny 工具中,选择“运行”菜单中的“配置解释器”选项,然后点击“安装或者更新microPython(esptool)”如下图 5.3.6 所示,进入固件烧录界面。
在上图中,我们点击红色箭头按键,在它弹出的下拉列表上选择“Select local microPythonimage…”选项,接着选择刚刚下载的 ESP32_GENERIC_S3-SPIRAM_OCT-20240602-v1.23.0.bin
固件文件,此时,esptool
烧录界面变成可编辑界面,如下图所示。
点击“安装”按钮就执行烧录操作了,此时点击“烧录”进度条即可看到烧录进度信息,如下图所示。
等待烧录进度提示“Hard resetting via RTS Pin … Done
”表示烧录完成,接着关闭 esptool
工具,重新选择解释器(MicroPython (ESP32) · USB Serial @ COM46),并按下开发板上的复位按键,即可启动 MicroPython 固件了。最后,在 Shell 交互窗口上看到固件的信息,如下图所示。
从上图可以看到,Shell 交互式窗口提示“MicroPython v1.23.0 on 2024-06-02; Generic ESP32S3 module with Octal-SPIRAM with ESP32S3
”信息,表示已成功烧录 v1.23.0 版本 MicroPython。
from machine import Pin # 导入 Pin 类,用于控制 GPIO 引脚。
from neopixel import NeoPixel # 导入 NeoPixel 类,用于控制 NeoPixel LED。
import time # 导入 time 模块,用于延时。
pin = Pin(48, Pin.OUT) # 创建一个 Pin 对象,设置 GPIO 48 为输出模式。
np = NeoPixel(pin, 1) # 创建一个 NeoPixel 对象,指定使用上面的 Pin 对象和 LED 数量为 1。
np[0] = (10, 10, 10) # 设置 LED 的颜色为白色(红光强度、绿光和蓝光强度为 10)。
np.write() # 将设置的颜色数据写入到 LED。
time.sleep(1) # 延时 1 秒。
r, g, b = np[0] # 读取 LED 当前的颜色值。
while True:
np[0] = (10, 0, 0) # 设置 LED 颜色为红色。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 0, 0) # 设置 LED 颜色为黑色(熄灭)。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (20, 10, 0) # 设置 LED 颜色为橙色。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 0, 0) # 设置 LED 颜色为黑色(熄灭)。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (10, 10, 0) # 设置 LED 颜色为黄色。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 0, 0) # 设置 LED 颜色为黑色(熄灭)。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 10, 0) # 设置 LED 颜色为绿色。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 0, 0) # 设置 LED 颜色为黑色(熄灭)。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 10, 10) # 设置 LED 颜色为青色。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 0, 0) # 设置 LED 颜色为黑色(熄灭)。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 0, 10) # 设置 LED 颜色为蓝色。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 0, 0) # 设置 LED 颜色为黑色(熄灭)。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (10, 0, 10) # 设置 LED 颜色为紫色。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
np[0] = (0, 0, 0) # 设置 LED 颜色为黑色(熄灭)。
np.write() # 更新 LED 的颜色。
time.sleep(1) # 延时 1 秒。
def handle_interrupt(pin): # 定义一个中断处理函数。
np[0] = (10, 0, 0) # 设置 LED 颜色为红色。
np.write() # 更新 LED 的颜色。
time.sleep_ms(150) # 延时 150 毫秒。
np[0] = (0, 0, 0) # 设置 LED 颜色为黑色(熄灭)。
np.write() # 更新 LED 的颜色。
time.sleep_ms(150) # 延时 150 毫秒。
print("test-usr key") # 输出一条消息。
p0 = Pin(0) # 创建一个 Pin 对象,设置 GPIO 0 为默认输入模式。
p0.init(p0.IN, p0.PULL_UP) # 显式设置 GPIO 0 为输入模式,并启用上拉电阻。
p0.irq(trigger=p0.IRQ_FALLING, handler=handle_interrupt) # 设置 GPIO 0 的中断触发条件为下降沿,并关联到 handle_interrupt 函数。
2. flash_download_tool 烧录:
首先打开乐鑫下载工具网页,接着下载 Flash 下载工具,如下图所示。
在上图中,点击下载 Flash 下载工具的链接,当前版本为 3.9.5,下载完成之后双击打开程序文件 flash_download_tool_3.9.5.exe,然后在“DOWNLOAD TOOL MODE
” 界面下选择芯片信息(Flash 下载工具相关介绍,请参考 flash_download_tool_3.9.5\doc 路径下的《Flash_Download_Tool_cn.pdf 使用手册》),如下图所示。
上图中,ChipType 选择 ESP32S3,WorkMode 选择 Develop LoadMode 选择 UART,点击OK,进入下载页面,如下图所示。
上图①表示选择下载的固件;上图②表示该固件将被下载到 ESP32-S3 的 Flash 哪个地址,这里我们选择 0x0000;上图③和④表示 SPI Flash 速率及模式,这里我们选择 80MHz 和 QIO 模式;上图⑤表示串口端口和波特率,这里我们选择 COM4(不同电脑插入,端口号也不同)和 115200;上图⑥表示 Flash 固件下载开始、停止和擦除按键。
首先点击“ERASE”擦除按键,等待 ESP32-S3 擦除完成,然后点击“STOP”按键停止操作,接着点击“START”开始烧录操作,等待烧录完成后,就关闭 flash_download_tool 软件。
需要注意的是,在烧录固件之前,需要确认 ESP32-S3 开发板已经正确连接到电脑,并且端口号正确选择。同时,在烧录过程中要避免断电或断开连接等操作,以免对开发板造成损坏。
五、MicroPython 固件编译
1、搭建编译环境
自编译 MicroPython ESP32-S3 固件需要搭建两个开发环境,一个是 Linux 环境,另一个是ESP-IDF 环境。这两个环境是必需的,因为 MicroPython ESP32 固件依赖于它们进行编译。
1. Linux 环境搭建
全网最全 【Vmware】 下虚拟机 Linux Ubuntu 安装和配置
Linux 环境的搭建有三种方式。
第一种是使用 VMware 虚拟机并安装 Ubuntu 系统来搭建,
第二种是安装多个操作系统(Windows 和 Ubuntu 系统),每次重启电脑时根据需要进行选择进入,
第三种是在 Windows 系统下安装 WSL Ubuntu子系统。
前两种方式属于比较完整的安装方式,都可以有图形用户界面,适合对图形界面有需求的同学。而最后一种 Linux 子系统的方式,安装和运行最快,但是只有命令行,适合 Linux 老手或者对图形界面需求不大的用户。
进入 Windows store 微软商城,输入关键词 ubuntu,即可找到多个版本的 ubuntu 子系统,如下图所示。
这里选的是 Ubuntu22.04.3 LTS 版本。点进去下载安装。接着我们打开 linux 子系统功能,如下步骤所示:
2. WSL 子系统搬迁
Windows Store 微软商城安装的 APP 都默认被安装在 C 盘。我们最好将子系统搬离 C 盘,也就是说,不使用 C 盘来存放文件。将子系统搬到 D 盘存储。WSL 子系统搬迁流程,如下所示:
- 查询 WSL 系统状态。使用管理员身份打开 CMD 命令行,并在命令行输入“
wsl -l -v
”查看 wsl 虚拟机的名称和状态,如下图所示。
- 停止 WSL 子系统。输入
wsl --shutdown
使 Ubuntu-22.04 停止运行,再次使用wsl -l -v
确保其处于stopped
状态,如下图所示。
- 导出/恢复备份。在 D 盘创建一个文件夹用来存放新的 WSL,比如创建了一个
D:\Ubuntu_WSL
文件夹。
①:导出它的备份(比如命名为Ubuntu.tar
)。在 CMD 命令行下输入“wsl --export Ubuntu-22.04 D:\Ubuntu_WSL\Ubuntu.tar
”。
②:确定在D:\Ubuntu_WSL
下是否可看见备份Ubuntu.tar
文件,如下图所示那样,之后注销原有的 WSL。在 CMD 命令行输入“wsl --unregister Ubuntu-22.04
”注销原本的 WSL。
③:将备份文件恢复到 D:\Ubuntu_WSL 中去。在 CMD 命令行下输入“wsl --import Ubuntu-22.04 D:\Ubuntu_WSL D:\Ubuntu_WSL\Ubuntu.tar
”恢复备份文件,如下图所示。
关于将 C 盘的数据移植到 D 盘,这可能是出于以下几个原因:
-
磁盘空间不足:C 盘可能没有足够的空间来容纳更多的数据,或者你想要释放 C 盘的空间以便更好地管理操作系统所在磁盘的存储。
-
性能考虑:如果你的 D 盘是一个 SSD(固态硬盘),而 C 盘是一个传统的 HDD(机械硬盘),那么将数据迁移到 D 盘可能会带来更好的读写性能。
-
组织结构:你可能希望将 WSL 分发版和相关数据放在 D 盘以更好地组织你的文件和项目,使它们与其他开发相关的文件分开。
-
备份和恢复:如果你计划定期备份 D 盘的内容,那么将 WSL 分发版放在 D 盘可以方便进行备份和恢复操作。
-
多用户环境:在某些情况下,你可能需要在一台机器上为不同的用户提供独立的工作环境。将 WSL 分发版放在 D 盘可以更容易地为每个用户分配单独的文件系统。
如何将 C 盘的数据移植到 D 盘
如果你想将 C 盘的数据移植到 D 盘上的 WSL 分发版中,你可以采取以下步骤:
-
导出当前的 WSL 分发版:首先,你需要将现有的 Ubuntu-22.04 分发版导出为一个
.tar
文件。wsl --export Ubuntu-22.04 D:\Ubuntu_WSL\Ubuntu.tar
-
在 D 盘创建新的 WSL 分发版:接下来,使用之前导出的
.tar
文件创建一个新的 WSL 分发版。wsl --import Ubuntu-22.04 D:\Ubuntu_WSL D:\Ubuntu_WSL\Ubuntu.tar
-
验证新分发版:确保新的分发版正确导入了数据,并且所有必要的配置都已迁移。
wsl -d Ubuntu-22.04
-
取消注册旧分发版:如果不再需要原来的分发版,可以取消注册并删除旧的根文件系统。
wsl --unregister Ubuntu-22.04
-
设置新分发版为默认分发版:如果需要的话,你可以将新分发版设置为默认分发版。
wsl --set-default Ubuntu-22.04
完成这些步骤后,你就可以在 D 盘上使用新的 Ubuntu-22.04 分发版了,其中包含了从 C 盘迁移过来的数据。
3. 安装工具链
1,更新工具:
sudo apt-get update
2,安装 GCC:
sudo apt-get install gcc
3,安装 Cmake:
sudo apt-get install cmake
4,安装 python3.10:
sudo apt-get install python3.10
5,安装 python pip:
sudo apt-get install python3-pip
6,安装子系统:
sudo apt-get install source/ linux-source-5.7
7,安装 virtualenv:
sudo apt-get install virtualenv
sudo apt-get install python3.10-venv
8,安装 git:
sudo apt-get install git
4. ESP-IDF 环境搭建
乐鑫芯片在 ESP-IDF 各版本中的支持状态
MicroPython IDF 版本要求: MicroPython 目前支持的 IDF 版本
4.1 克隆 IDF 库
首先打开 ubuntu 22.04.2 LTS 子系统,然后在命令行下输出“git clone -b v5.0.4 --recursive https://github.com/espressif/esp-idf.git
”克隆 Github 仓库中的 ESP-IDF 源码库,如下图所示。
https://github.com/espressif/esp-idf/tree/release/v5.2
git clone -b v5.0.4 --recursive https://github.com/espressif/esp-idf.git
子系统(D:\Ubuntu_WSL\root)根目录下找到一个名为“esp-idf”的文件夹。这个文件夹包含了 ESP-IDF 开发环境的相关文件和工具
4.2 切换 IDF 5.0.4 版本
cd esp-idf
git checkout v5.0.4
4.3 更新 IDF 版本子模块
git submodule update --init --recursive
4.4 运行安装脚本安装所需依赖项和设置环境变量
./install.sh
source export.sh
分别是安装 ESP-IDF 开发所需的依赖项和设置环境变量,设置成功之后系统提示“../export.sh
”信息表示安装及设置环境变量成功
上图提示“idf.py build
”信息表示设置环境变量成功。
请注意,每次进入 Ubuntu 22.04.2 LTS 子系统时,用户必须在 ~/esp-idf#目录下输入“source export.sh
”来操作环境变量,否则在生成固件时可能会报错。
为了解决上述的问题,在~/.bashrc
直接执行 export.sh
文件,来设置 ESP-IDF 环境变量。
~/.bashrc
是一个在 Unix 和 Linux 系统中非常重要的 Shell 初始化文件。这个文件在 bash shell
(Bourne Again SHell)启动时被读取(系统启动时),用户可以在这里定义别名、函数、环境变量、路径等,所有这些在以后启动的 bash shell
中都可用。
这个文件通常位于系统的 home
目录下(~/.bashrc
),用户可以使用文本编辑器(如 vim
、nano
或 emacs
)来编辑它。在此文件最后一行命令下添加以下命令,如下所示:
source ./esp-idf/export.sh
4. 搭建 MicroPython 开发环境
https://github.com/micropython/micropython
# 克隆 MicroPython 库
git clone https://github.com/micropython/micropython.git
cd micropython
# 更新 MicroPython 子模块
git submodule update --init --recursive
# 构建 MicroPython 交叉编译器
make -C mpy-cross
构建 MicroPython 交叉编译器完成之后,就可以构建 MicroPython ESP32 固件了。“cd ports/esp32
”进入 MicroPython ESP32 编译工程,在此目录下输入“make
”构建 ESP32 MicroPython 固件,如下图所示。
cd ports/esp32
make
系统在 esp32\build-ESP32_GENERIC文件夹
下生成了三个 bin文件
,它们分别为 bootloader.bin
、partition-table.bin
和 micropython.bin
。然后,我们使用 flash_download_tool_3.9.5.exe
工具将这三个 bin 文件烧录到 ESP32 芯片中
这些文件通常是在嵌入式系统中使用的固件文件,每个文件有特定的功能和用途。以下是它们的作用和用途的简要说明:
-
bootloader.bin:
- 功能:引导加载程序(Bootloader)的二进制文件。
- 用途:负责在设备上电时初始化硬件设置,加载并启动操作系统或主应用程序。在ESP32系列设备中,bootloader通常位于Flash存储的固定位置,启动时首先执行。
-
partition-table.bin:
- 功能:分区表的二进制文件。
- 用途:定义了Flash存储的布局,包括每个分区的起始地址、大小和用途。例如,可以定义一个分区用于存储应用程序固件(App),另一个分区用于存储文件系统(SPIFFS、FAT等)。ESP32设备使用这个文件来确定每个分区的位置和大小。
-
micropython.bin:
- 功能:MicroPython固件的二进制文件。
- 用途:包含MicroPython解释器和相关的库,用于运行Python代码的固件。这个文件被烧录到设备的特定分区中,使设备能够解释和执行MicroPython代码。
-
firmware.bin:
- 功能:设备的主固件二进制文件。
- 用途:包含设备的主要功能代码和应用程序逻辑。对于基于ESP32的设备,这通常是最终用户要烧录的应用程序固件。这个文件可以包括操作系统、设备驱动程序、用户应用程序等。
烧录顺序和注意事项
烧录这些文件到设备时,通常遵循以下顺序:
- bootloader.bin:通常首先烧录到Flash的起始位置。
- partition-table.bin:接下来烧录分区表,定义Flash存储的布局。
- micropython.bin或firmware.bin:最后烧录主固件,取决于使用的框架或操作系统。
esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect \
0x1000 bootloader.bin \
0x8000 partition-table.bin \
0x10000 micropython.bin
烧录这些文件时,必须确保它们的地址和大小与分区表中的定义一致。此外,确保使用适当的工具(如 esptool.py
或flash_download_tool
)和正确的命令来烧录这些文件,以避免损坏设备或导致无法启动的问题。
根据提示信息,我们需要将生成的 bin 文件烧录到对应的地址中。另外,我们也可以使用 esp32\build-ESP32_GENERIC
文件夹下的 firmware.bin
文件,这个文件是bootloader.bin
、partition-table.bin
和 micropython.bin
三个文件的总 bin 文件:
注意:此固件虽然能满足 ESP32 在 MicroPython 下的运作,但它是有缺陷的。它并没有适配芯片的资源,如,是否挂载 PSRAM、Flash/PSRAM 的大小、时钟是否调节到 240MHz 等等。
如果我们不去配置的话,系统可能选择默认的配置,如时钟只能达到 160MHz、Flash 大小为2MB 等。这种默认的配置无法体现该芯片的价值,所以作者在后续的教程中,会详细讲解如何
5. MicroPython 源文件结构分析
子系统的 Ubuntu 下克隆了 MicroPython 源码包,并且在这个源码包下编译出了 ESP32 MicroPython 固件。
MicroPython 的基础源程序基本由 C 语言编写的,在编译过程中,使用了少量的 Python 脚本,用于在预编译过程中处理字符串。如下表中展示了 MicroPython 项目的源码文件结构。
docs 目录: 可生成 MicroPython 文档,可以在 GitHub 仓库中找到 MicroPython/docs 目录,并安装相关的工具来生成 HTML,如下图所示。
先使用 pip
命令安装 sphinx
和 sphinx_rtd_theme
工具,然后在子系统上使用 cd
命令跳到 micropython/docs
目录下,最后在此目录下输入“make htm
l”命令构建 html
文档。
py 目录:在 MicroPython 的开发过程中,为了确保与 Python 标准库的一致性,我们不会修改 py 目录中的任何源代码。这是因为 py 目录存放着 Python 内核的全部源代码,是实现 Python功能的重要基础。
lib 目录和 extmod 目录:它们都包含了 MicroPython 扩展功能的组件,但它们的分工有所不同。lib 目录中的内容是与 MicroPython 无关的,它们针对特定的微控制器平台,并可以独立地在这些平台上运行,以支持 MicroPython 功能的实现。它们位于 MicroPython 的下层,例如,包括各个微控制器平台的固件库、基本的文件系统协议栈和 USB 协议栈等。而 extmod 目录中实现的是扩展模块,这些模块都是基于 MicroPython 实现的,并在 MicroPython 内部实现扩展功能,例如,machine_spi 模块就是在 MicroPython 的 machine 类中实现的 SPI 类模块。
对于微控制器开发者最有用也是最直接操作的目录是 ports,里面存在了 MicroPython 支持的所有微控制器的移植代码,如下图所示。
在具体微控制器平台的目录下,存放了各自对MicroPython的底层移植源文件以及移植工程的 Makefile 文件。
2、编译 ESP32-S3 固件
Makefile 是用于自动编译和构建软件项目的工具。它使用简单的规则来告诉编译器如何构建软件。Makefile 包含一组命令,这些命令用于编译、链接和构建源代码文件。
在 ESP32-S3 的固件编译过程中,可以在 ports/esp32 目录中找到一个 Makefile 文件。这个Makefile 文件包含了编译 ESP32-S3 固件的规则和指令。通过编辑 Makefile 文件,可以设置要编译的型号,例如 GENERIC_S3,然后保存文件后在终端使用 make命令开始编译适用于对应板型的固件。
2.1 Makefile 文件
micropython\ports\esp32\Makefile
# Makefile for MicroPython on ESP32.
#
# This is a simple, convenience wrapper around idf.py (which uses cmake).
# Select the board to build for:
# (1)选择要构建的板卡:
ifdef BOARD_DIR
# Custom board path - remove trailing slash and get the final component of
# the path as the board name.
BOARD ?= $(notdir $(BOARD_DIR:/=))
else
# (2)如果命令行中没有指定 BOARD_DIR,默认使用 ESP32_GENERIC
BOARD ?= ESP32_GENERIC
# (3)得到对应芯片的编译路径
BOARD_DIR ?= boards/$(BOARD)
endif
ifeq ($(wildcard $(BOARD_DIR)/.),)
ifeq ($(findstring boards/GENERIC,$(BOARD_DIR)),boards/GENERIC)
$(warning The GENERIC* boards have been renamed to ESP32_GENERIC*)
endif
$(error Invalid BOARD specified: $(BOARD_DIR))
endif
# If the build directory is not given, make it reflect the board name (and
# optionally the board variant).
# (4)如果没有给出指定 BUILD 构建目录,使用默认文件名
ifneq ($(BOARD_VARIANT),)
BUILD ?= build-$(BOARD)-$(BOARD_VARIANT)
else
BUILD ?= build-$(BOARD)
endif
# Device serial settings.
# 设备串口波特率设置,默认值为 /dev/ttyUSB0 和 460800 波特率
PORT ?= /dev/ttyUSB0
BAUD ?= 460800
# 指定使用的 Python 解释器,默认是 python3
PYTHON ?= python3
.PHONY: all clean deploy erase submodules FORCE
CMAKE_ARGS =
# (5)使用 C 模块,把自己的 C 驱动编译成 MicroPython 调用的 API
ifdef USER_C_MODULES
CMAKE_ARGS += -DUSER_C_MODULES=${USER_C_MODULES}
endif
IDFPY_FLAGS += -D MICROPY_BOARD=$(BOARD) -D MICROPY_BOARD_DIR="$(abspath $(BOARD_DIR))" $(CMAKE_ARGS)
ifdef FROZEN_MANIFEST
IDFPY_FLAGS += -D MICROPY_FROZEN_MANIFEST=$(FROZEN_MANIFEST)
endif
# (6)设置 PSRAM SPI 模式、D2WD 等
ifdef BOARD_VARIANT
IDFPY_FLAGS += -D MICROPY_BOARD_VARIANT=$(BOARD_VARIANT)
endif
ifdef MICROPY_PREVIEW_VERSION_2
IDFPY_FLAGS += -D MICROPY_PREVIEW_VERSION_2=1
endif
HELP_BUILD_ERROR ?= "See \033[1;31mhttps://github.com/micropython/micropython/wiki/Build-Troubleshooting\033[0m"
define RUN_IDF_PY
idf.py $(IDFPY_FLAGS) -B $(BUILD) -p $(PORT) -b $(BAUD) $(1)
endef
all:
idf.py $(IDFPY_FLAGS) -B $(BUILD) build || (echo -e $(HELP_BUILD_ERROR); false)
@$(PYTHON) makeimg.py \
$(BUILD)/sdkconfig \
$(BUILD)/bootloader/bootloader.bin \
$(BUILD)/partition_table/partition-table.bin \
$(BUILD)/micropython.bin \
$(BUILD)/firmware.bin \
$(BUILD)/micropython.uf2
$(BUILD)/bootloader/bootloader.bin $(BUILD)/partition_table/partition-table.bin $(BUILD)/micropython.bin: FORCE
# 清理构建文件
clean:
$(call RUN_IDF_PY,fullclean)
# 自动烧录
deploy:
$(call RUN_IDF_PY,flash)
# 擦除设备的Flash存储指令
erase:
$(call RUN_IDF_PY,erase-flash)
# 监控设备的串口输出
monitor:
$(call RUN_IDF_PY,monitor)
# 显示固件的大小
size:
$(call RUN_IDF_PY,size)
# 显示固件中各组件的大小
size-components:
$(call RUN_IDF_PY,size-components)
size-files:
$(call RUN_IDF_PY,size-files)
# Running the build with ECHO_SUBMODULES set will trigger py/mkrules.cmake to
# print out the value of the GIT_SUBMODULES variable, prefixed with
# "GIT_SUBMODULES", and then abort. This extracts out that line from the idf.py
# output and passes the list of submodules to py/mkrules.mk which does the
# `git submodule init` on each.
# 初始化和更新 git 子模块
submodules:
@GIT_SUBMODULES=$$(idf.py $(IDFPY_FLAGS) -B $(BUILD)/submodules -D ECHO_SUBMODULES=1 build 2>&1 | \
grep '^GIT_SUBMODULES=' | cut -d= -f2); \
$(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$${GIT_SUBMODULES}" submodules
上述源码中
(1)表示要编译型号(如 make BOARD=GENERIC_S3 就是编译 S3 的MicroPython 固件),如果在命令行中没有给出这个变量,则默认为 ESP32_GENERIC;
(2),即编译 ESP32这款芯片;
(3)表示编译路径,也就是在这个路径下编译哪些文件,如果(1)中没有设置 BOARD 变量,则系统会编译 boards/ESP32_GENERIC 路径下的文件(如下图所示);
(4)表示构建文件夹名称,这个文件夹包含系统编译产生的文件都会保存在这个文件夹中;
(5)表示是否使用 C 模块,也就是说,使用 C 驱动编译成 MicroPython可调用的 API,这个方法我们到第六章时候讲解;
(6)表示编译条件,如挂在 PSRAM 的 SPI 模式为 SPIRAM_OCT。
2.2 mpconfigboard.cmake 文件、sdkconfig文件
MicroPython在ESP32平台上构建过程中的关键配置文件的作用
在MicroPython的ESP32项目中,boards/ESP32_GENERIC
路径下的mpconfigboard.cmake
文件、boards/sdkconfig.base
文件以及boards/sdkconfig.ble
文件都扮演着不同的角色,但它们共同协作以定制和优化MicroPython在ESP32上的构建和运行环境。下面分别解释这三个文件的作用:
-
mpconfigboard.cmake:
- 作用:这个CMake脚本文件负责合并多个
sdkconfig
片段文件,以创建一个完整的sdkconfig
文件用于构建过程。它决定了哪些配置片段应该被包含在内,从而间接控制了哪些ESP-IDF组件和特性会被编译。 - 示例内容:
set(SDKCONFIG_DEFAULTS boards/sdkconfig.base ${SDKCONFIG_IDF_VERSION_SPECIFIC} boards/sdkconfig.ble )
- 作用:这个CMake脚本文件负责合并多个
-
sdkconfig.base:
- 作用:这是一个基础的
sdkconfig
文件,包含了ESP-IDF项目构建的默认配置选项。它定义了所有基本的组件和特性,比如Wi-Fi、以太网、串行通信等。
- 作用:这是一个基础的
-
sdkconfig.ble:
- 作用:这个文件专门用来配置蓝牙低功耗(BLE)相关的选项。如果您的项目需要BLE功能,那么这个文件会添加必要的配置以启用BLE。
这三个文件协同工作,允许开发者选择性地开启或关闭ESP-IDF的不同组件和特性。通过在mpconfigboard.cmake
中合并sdkconfig.base
和sdkconfig.ble
,您可以确保ESP32的MicroPython固件同时具有基础的系统功能和BLE能力,如果需要的话。
在开发过程中,根据项目需求,您可能需要编辑这些文件以启用或禁用特定的特性,从而优化资源使用和性能。
2.3 修改 sdkconfig.board 文件
ports/esp32/borads/ESP32_GENERIC_S3
路径下 sdkconfig.board
文件
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
此文件默认设置 Flash 大小为 8MB,显然不符合 ESP32-S3-WROOM-1-N16R8 模组内置16MB Flash 大小,因此,把CONFIG_ESPTOOLPY_FLASHSIZE_16MB
配置为 y,并且修改分区表名称为“partitions-16MiB.csv
”,修改后如下:
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv"
2.4 修改 partitions-16MiB.csv 分区表
micropython\ports\esp32\partitions-16MiB.csv
乐鑫AI 库存储在 Flash 内需要大量的内存。因此,我们将分区表中的 factory 子分区(存储代码的区域)设置为 6M。
# Notes: the offset of the partition table itself is set in
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x600000, # 6M 内存
vfs, data, fat, 0x700000, 0x900000, # 9M 内存
MicroPython 源代码适配 ESP32-S3-WROOM-1-N16R8 模组完成,接下来,在 ports/esp32/路径下输入“make BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT
”命令构建 ESP32-S3-WROOM-1-N16R8 模组 MicroPython 固件,如下图所示。
make BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT
根据上图所示,系统会在 build-ESP32_GENERIC_S3-SPIRAM_OCT 文件夹下生成四个 bin文件,它们分别是 bootloader.bin、partition-table.bin、micropython.bin 和 firmware.bin
(总 bin)。
接着,我们使用 flash_download_tool.exe 工具将这三个 bin 文件烧录到 ESP32-S3-WROOM-1-N16R8 模组中。需要注意的是,
bootloader.bin
、partition-table.bin
和 micropython.bin
设置的下载地址分别为 0x0000、0x8000 和 0x10000。
如果烧录的是 firmware.bin
(总 bin),则将烧录地址设置为 0x0000。当然,我们也可以借助 Thonny 软件来烧录这个固件,如下图所示。
build-ESP32_GENERIC_S3-SPIRAM_OCT\bootloader\bootloader.bin
build-ESP32_GENERIC_S3-SPIRAM_OCT\partition_table\partition-table.bin
build-ESP32_GENERIC_S3-SPIRAM_OCT\micropython.bin
build-ESP32_GENERIC_S3-SPIRAM_OCT\firmware.bin
import machine # 导入 machine 模块,用于控制底层硬件,如 GPIO、ADC、DAC 等。
import esp # 导入 esp 模块,用于访问与 ESP32-S3 相关的特定功能。
import gc # 导入 gc(垃圾回收)模块,用于管理内存,特别是检查和释放不再使用的内存。
import micropython # 导入 micropython 模块,用于访问 MicroPython 的底层功能和配置。
if __name__ == '__main__':
# 获取并打印系统时钟频率
freq = machine.freq()# 调用 machine.freq() 函数获取当前的 CPU 时钟频率。
# 获取并打印内部 Flash 的大小
flash_size = esp.flash_size()# 调用 esp.flash_size() 函数获取内部 Flash 的大小。
# 获取并打印 当前可用的 PSRAM 内存大小
psram_free = gc.mem_free()# 调用 gc.mem_free() 函数获取当前空闲的内存大小。
# 获取当前已分配的内存大小。
psram_allocated = gc.mem_alloc()
# 计算总的内存大小。
psram_total = psram_free + psram_allocated
print(f'当前系统时钟 {freq / (1000 * 1000)} MHz') # 打印频率,单位转换为 MHz。(240MHz = 240 * 10^6 Hz)
print(f'内部 Flash 大小 {flash_size / (1024 * 1024)} MB') # 打印 Flash 大小,单位转换为 MB。(8MB = 8 * 2^20 byte)
print(f'当前可用 PSRAM 大小 {psram_free / (1024 * 1024)} MB') # 打印当前可用的内存大小,单位转换为 MB。
print(f'已使用 PSRAM 大小: {psram_allocated / (1024 * 1024)} MB')# 打印已使用的内存大小,单位转换为 MB。
print(f'总的 PSRAM 大小: {psram_total / (1024 * 1024)} MB') # 打印总的内存大小,单位转换为 MB。
这个固件的系统时钟被设置为 240MHz,内部 Flash 大小为 16MB,PSRAM经过申请和释放操作,最后打印剩余空间为 约等于 8MB。
六、MicroPython 组件扩展(C模块)
C模块是MicroPython生态系统中的一个重要组成部分,它们是用C语言编写的扩展模块,能够被MicroPython解释器直接加载和执行。C模块之所以重要,主要有以下几个原因:
-
性能优化:
- C模块通常提供比纯Python代码更高的执行效率。这是因为C语言是一种编译型语言,其执行速度通常远快于解释型语言如Python。对于计算密集型任务或实时操作,使用C模块可以显著提高应用程序的性能。
-
环境限制:
- 微控制器和嵌入式设备往往资源有限,如内存和处理能力。C模块可以通过更有效地利用这些资源来支持特定的硬件功能,比如直接访问GPIO、I2C、SPI等接口,或实现复杂的硬件驱动程序,这些都是纯Python代码难以实现的。
-
功能扩展:
- MicroPython本身已经包含了丰富的内置模块,但是C模块可以进一步扩展其功能集,允许用户自定义新的模块来满足特定应用的需求。例如,你可以编写C模块来支持特定的传感器、执行高级数学运算、图像处理等。
要创建和使用C模块,你需要对C语言和MicroPython的内部结构有一定了解。通常,这涉及到在MicroPython源代码树中添加新的C文件,编写C函数来封装所需的逻辑,并使用MicroPython的API将这些函数暴露给Python层。然后,你需要重新编译MicroPython固件,将你的C模块包含进去。一旦完成,你的C模块就可以像任何其他Python模块一样从MicroPython中导入和使用了。
1、组件扩展原理
在MicroPython中调用C语言实现的函数,主要通过MicroPython的“C模块”机制来完成。这种机制允许你在C语言中编写函数,然后在MicroPython的Python环境中调用这些函数。下面是一个基本的步骤指南:
-
编写C代码:
首先,你需要用C语言编写你的函数。这个函数需要遵循一些规则,以便能够被MicroPython正确地识别和调用。 -
注册函数到MicroPython:
在C代码中,你需要使用MicroPython的API(如mp_obj_t
,mp_uint_t
等)来包装你的C函数,使其能够理解Python对象,并返回Python可理解的结果。然后,你需要使用MP_DEFINE_CONST_FUN_OBJ_1
、MP_DEFINE_CONST_FUN_OBJ_2
等宏来定义你的函数,并将其添加到一个模块的const mp_rom_map_elem_t mp_module_name_globals_table[]
数组中。 -
重新编译MicroPython固件:
将你的C代码加入到MicroPython的源代码目录中,通常是加入到ports/<port_name>/modules
目录下,然后重新编译整个MicroPython固件。这样,你的C模块就会被包含在最终的固件中。 -
在MicroPython中导入和使用:
最后,在MicroPython中,你可以像导入和使用任何其他模块一样来导入和使用你的C模块,调用其中定义的函数。
以下是一个简单的示例,展示如何在C中定义一个函数并在MicroPython中调用它:
假设你想定义一个名为my_add
的函数,它接收两个整数并返回它们的和。
C代码示例(my_mod.c
):
#include "py/obj.h"
#include "py/runtime.h"
STATIC mp_obj_t my_add(mp_obj_t x_in, mp_obj_t y_in) {
int x = mp_obj_get_int(x_in);
int y = mp_obj_get_int(y_in);
return mp_obj_new_int(x + y);
}
MP_DEFINE_CONST_FUN_OBJ_2(my_add_obj, my_add);
const mp_obj_type_t mp_type_my_type;
const mp_rom_map_elem_t mp_module_my_mod_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_my_mod) },
{ MP_ROM_QSTR(MP_QSTR_my_add), MP_ROM_PTR(&my_add_obj) },
};
MP_DEFINE_CONST_DICT(mp_module_my_mod_globals, mp_module_my_mod_globals_table);
在MicroPython中调用这个函数:
import my_mod
result = my_mod.my_add(5, 3)
print(result) # 输出应该是 8
请注意,实际的C代码和调用过程可能根据你的MicroPython端口和具体需求有所不同。
MicroPython C模块类型
在MicroPython中使用C扩展进行功能增强的两种常见方式:模块扩展和模块+类扩展。这两种方式各有优势,适用于不同的场景和需求。下面对这两种方式进行详细解释:
1. 模块扩展
特点:
- 简单直接:扩展组件作为一个模块,通常包含一组相关的函数和常量,可以直接被MicroPython导入和使用。
- 易于使用:用户只需要导入模块,就可以访问所有公开的函数和变量,无需额外的实例化操作。
应用场景:
- 当扩展功能相对单一,不需要复杂的状态管理或面向对象的设计时,模块扩展是最直接的选择。
- 例如,一个用于数学运算的模块,或者一个用于硬件访问的模块,如GPIO控制。
2. 模块+类扩展
特点:
- 面向对象:扩展组件不仅提供了模块级别的功能,还通过类的形式提供了封装和数据抽象。
- 灵活性和可扩展性:类可以包含属性和方法,允许用户实例化对象,对每个实例进行独立的状态管理和行为定制。
- 更好的代码组织:类可以组织相关的功能,使得代码结构更加清晰,易于维护和扩展。
应用场景:
- 当扩展组件需要管理复杂的状态,或者提供多样的行为时,模块+类的方式是更好的选择。
- 例如,一个用于网络通信的模块,可能需要管理连接状态、缓冲区、事件回调等,这时使用类可以更好地封装这些细节。
- 又如,一个图形库,可能需要不同的图形对象(如点、线、形状等),每个对象有自己的属性和方法,使用类可以清晰地区分和管理这些对象。
在MicroPython中,无论是模块扩展还是模块+类扩展,都需要通过C语言编写底层实现,并使用MicroPython的API来桥接C和Python的调用。一旦实现,这些扩展就可以像普通的Python模块一样被导入和使用,为MicroPython应用带来高性能或硬件访问的能力。
组件扩展原理解析
MicroPython 提供的三个 C 模块实例,以便用户将来编写自己的 C 模块组件。这些实例位于 micropython\examples\usercmodule
目录下,它们分别是 cexample
、cppexample
和 subpackage C 模块
。
micropython.mk 文件
# USERMOD_DIR 模块目录的路径,如 cexample/
CEXAMPLE_MOD_DIR := $(USERMOD_DIR)
# 将所有 C 文件添加到 SRC_USERMOD
SRC_USERMOD += $(CEXAMPLE_MOD_DIR)/examplemodule.c
# 如果有自定义编译器选项(例如 -I 添加目录以搜索头文件),
# 则应将这些选项添加到 C 代码的 CFLAGS_USERMOD 和 C++代码的 CXXFLAGS_USERMOD
CFLAGS_USERMOD += -I$(CEXAMPLE_MOD_DIR)
链接了 cexample 对象文件,最终生成了一个的共享库。这个共享库可以被 MicroPython 解释器加载和使用。总的来说,micropython.mk 对于 MicroPython C 模块的作用主要是定义构建规则和编译选项,以用于构建和编译 C 模块。
micropython.cmake 文件
micropython.cmake 是一个 CMake 配置文件,用于构建 MicroPython 模块。它包含了构建模块所需的 CMake 配置指令,例如定义库、添加源文件、设置编译选项等。通过使用 CMake,可以方便地构建和管理 MicroPython 模块的构建过程。
# 创建一个 INTERFACE 库
add_library(usermod_cexample INTERFACE)
# 源文件添加到库中
target_sources(usermod_cexample INTERFACE
${CMAKE_CURRENT_LIST_DIR}/examplemodule.c
)
# 将当前目录添加为包含目录
target_include_directories(usermod_cexample INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
# 将我们的 INTERFACE 库链接到 usermod 目标
target_link_libraries(usermod INTERFACE usermod_cexample)
在 CMake 配置文件 micropython.cmake 中,需要定义一个 INTERFACE 库并将源文件关联起来,然后将其链接到 usermod目标。该文件对于 MicroPython C模块的作用是定义 CMake配置,以确保 C 模块能够正确地编译和链接成可执行文件或库,并能够在 MicroPython 环境中正确地运行。
examplemodule.c 文件
模块扩展实现原理
/* 第一部分:添加所需 API 的头文件 */
#include "py/runtime.h"
#include "py/mphal.h"
/* 第二部分:实现功能 */
static mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {
/* 通过 Python 获取的第一个整形参数 arg_1 */
int a = mp_obj_get_int(a_obj);
/* 通过 Python 获取的第二个整形参数 arg_2 */
int b = mp_obj_get_int(b_obj);
/* 处理入参 arg_1 和 arg_2,并向 python 返回整形参数 ret_val */
return mp_obj_new_int(a + b);
}
/* 使用 MP_DEFINE_CONST_FUN_OBJ_2 宏将函数添加到模块中 */
static MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);
...
/* 第三部分:将模块注册到模块列表 */
static const mp_rom_map_elem_t example_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) },
{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },
{ MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&example_type_Timer) },
{ MP_ROM_QSTR(MP_QSTR_AdvancedTimer), MP_ROM_PTR(&example_type_AdvancedTimer) },
};
static MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
/* 第四部分:定义模块对象 */
const mp_obj_module_t example_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&example_module_globals,/* 指向字典对象 */
};
/* 第五部分:注册模块以使其在 Python 中可用 */
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule);
examplemodule.c 文件的部分代码,它主要阐述了简单 MicroPython C 模块的模块扩展架构,首先作者把它这个架构划分为四个部分,如下解析:
第一部分:在.c 文件下添加 microPython 相关头文件,可用来引用相关的 API 函数。
第二部分:加法函数,使用 C 语言的方式实现功能,但需要使用 MicroPython API 函数对入参和出参进行转换,入参转换完成后,才能让 C 语言识别,出参转换成功后才能被 MicroPython调用。最后在 MP_DEFINE_CONST_FUN_OBJ_2 宏的作用下,将函数添加到模块中,以便在MicroPython 环境中调用。
第三部分:将模块注册到模块列表,然后在 MP_DEFINE_CONST_DICT 宏的作用下在 C 语言中创建一个常量字典,并将其添加到 MicroPython 模块中,以便在 Python 环境中直接访问和使用这些字典。这对于需要在 MicroPython 模块中定义和共享常量字典的情况非常有用。大家不妨回顾一下字典的作用,它通过键来访问字典中的值(这个值可用来指向某个函数的地址,这样我们根据这个地址调用函数了)。
第四部分:创建一个模块对象,然后对象的成员变量 globals 指向字典对象,接着在MP_REGISTER_MODULE 宏的作用下将模块注册到 MicroPython 系统中,使其可以在 Python 环境中被导入和使用。
上述组件扩展方式是模块扩展,我们可在 Py 脚本下使用模块扩展的形式调用加法函数,如下示例所示:
# 导入模块
import cexample
"""
* @brief 程序入口
* @param 无
* @retval 无
"""
if __name__ == "__main__":
a = 5
b = 10
c = cexample.add_ints(a,b) # 调用自定义模块的加法函数
print(c)
这种模块扩展类似于自己定义一个 cexample.py 文件,然后,在此文件下实现多个函数,最后,在 main.py 文件下导入 cexample 模块,并以模块名引用函数。
模块+类扩展原理
/* 添加所需 API 的头文件 */
#include "py/runtime.h"
#include "py/mphal.h"
...
/* (2)定义 Timer 对象实例 */
typedef struct _example_Timer_obj_t {
/* 所有对象的基础地址 */
mp_obj_base_t base;
/* 开始时间的变量 */
mp_uint_t start_time;
} example_Timer_obj_t;
/* (3)对象的实现方法(函数) */
static mp_obj_t example_Timer_time(mp_obj_t self_in) {
/* 获取 Timer 句柄 */
example_Timer_obj_t *self = MP_OBJ_TO_PTR(self_in);
/* 获取经过的时间,并将其作为 MicroPython 整数返回 */
mp_uint_t elapsed = mp_hal_ticks_ms() - self->start_time;
return mp_obj_new_int_from_uint(elapsed);
}
/* 使用 MP_DEFINE_CONST_FUN_OBJ_1 宏将函数添加到模块中 */
static MP_DEFINE_CONST_FUN_OBJ_1(example_Timer_time_obj, example_Timer_time);
/* 构造函数 */
static mp_obj_t example_Timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
/* 分配新对象并设置类型 */
example_Timer_obj_t *self = mp_obj_malloc(example_Timer_obj_t, type);
/* 获取开始时间 */
self->start_time = mp_hal_ticks_ms();
/* 返回 Timer 句柄 */
return MP_OBJ_FROM_PTR(self);
}
/* 将模块注册到对象的模块列表 */
static const mp_rom_map_elem_t example_Timer_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&example_Timer_time_obj) },
};
/* 把字典转换成对象 */
static MP_DEFINE_CONST_DICT(example_Timer_locals_dict, example_Timer_locals_dict_table);
...
/* (1)定义了 Timer 类 */
MP_DEFINE_CONST_OBJ_TYPE(
example_type_Timer,
MP_QSTR_Timer,
MP_TYPE_FLAG_NONE,
make_new, example_Timer_make_new,
locals_dict, &example_Timer_locals_dict
);
/* 将模块注册到模块列表 */
static const mp_rom_map_elem_t example_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) },
{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },
{ MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&example_type_Timer) },
{ MP_ROM_QSTR(MP_QSTR_AdvancedTimer), MP_ROM_PTR(&example_type_AdvancedTimer) },
};
/* 把模块注册列表注册到 example_module_globals 字典对象当中 */
static MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
/* 定义模块对象 */
const mp_obj_module_t example_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&example_module_globals,/* 指向字典对象 */
};
/* 注册模块以使其在 Python 中可用 */
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule);
Python 语言是面向对象的语言。正因为如此,在 Python 中创建一个类和对象是很容易的。类是用来描述具有相同的属性(变量)和方法(函数)的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。明白了这点,我们可以去看上述的代码到底怎么实现一个 Python 类的。
(1)它定义了一个名为“Timer”的类,这个类包含了一个对实例对象的操作 example_Timer_make_new,以及一个类的方法(函数)example_Timer_locals_dict;
(2)处定义了 Timer 对象实例,它用来获取开始的运行时间;
(3)处就是类的实现方法 example_Timer_time 即运行的函数。如下是模组+类扩展调用示例,如下代码所示:
from cexample import Timer
"""
* @brief 程序入口
* @param 无
* @retval 无
"""
if __name__ == "__main__":
tr = Timer() # 对象实例(调用 example_Timer_make_new 函数)
timer = tr. time() # 调用对象的方法 example_Timer_time 获取运行时间
print(timer) # 打印运行的时间
C 模块的模块+类扩展实现方式类似于在定义一个 cexample.py 文件,并在该文件中实现一个 Timer 类,然后在该类中实现计算运行时间的方法,最后在 main.py 文件中导入 cexample 模块下的 Timer 类,并使用类名来引用该函数。
2、组件扩展辅助工具
RT-Thread MicroPython C 绑定代码自动生成器
RT-Thread 提供的 MicroPython C 绑定代码自动生成器是一个非常有用的工具,可以帮助开发者快速将 C 语言函数或模块集成到 MicroPython 环境中。通过这个工具,开发者可以轻松添加自己编写的 C 语言函数或者模块到 MicroPython 中,并被 Python 脚本调用。这大大扩展了原版 MicroPython 的能力,并且可以快速实现任何功能。
3、添加C模块驱动扩展组件
移植到子系统 esp-idf 库的 components
目录中
移植到子系统 esp-idf 库的 components
目录中
1. 把扩展组件复制到 MicroPython 源代码
复制 CAMERA、ESP-WHO、IIC、LCD、RGBLCD、SENSOR 文件夹至 D:\Ubuntu_WSL\root\micropython\examples\usercmodule\BSP
目录下
2. 修改 usercmodule 目录下的 micropython.cmake
修改 micropython.cmake 文件,添加编译扩展组件
# This top-level micropython.cmake is responsible for listing
# the individual modules we want to include.
# Paths are absolute, and ${CMAKE_CURRENT_LIST_DIR} can be
# used to prefix subdirectories.
# Add the C example.
include(${CMAKE_CURRENT_LIST_DIR}/cexample/micropython.cmake)
# Add the CPP example.
include(${CMAKE_CURRENT_LIST_DIR}/cppexample/micropython.cmake)
# 添加 IIC 驱动
include(${CMAKE_CURRENT_LIST_DIR}/BSP/IIC/micropython.cmake)
# 添加 CAMERA 驱动
include(${CMAKE_CURRENT_LIST_DIR}/BSP/CAMERA/micropython.cmake)
# 添加 SPI LCD 驱动
include(${CMAKE_CURRENT_LIST_DIR}/BSP/LCD/micropython.cmake)
# 添加 ESP_WHO 乐鑫 AI 驱动
#include(${CMAKE_CURRENT_LIST_DIR}/BSP/ESP-WHO/micropython.cmake)
# 添加 SENSOR 驱动
include(${CMAKE_CURRENT_LIST_DIR}/BSP/SENSOR/micropython.cmake)
# 添加 RGBLCD 驱动
include(${CMAKE_CURRENT_LIST_DIR}/BSP/RGBLCD/micropython.cmake)
有一个重要的点需要关注:ESP_WHO和 LCD 扩展组件不能同时编译。这是因为 ESP_WHO 已经包含了 LCD 驱动,所以这两者不能同时使用。
3. 编译固件
cd micropython/ports/esp32
make USER_C_MODULES=~/micropython/examples/usercmodule/micropython.cmake BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT
七、引脚定义
《esp32-s3-wroom-1_wroom-1u_datasheet_cn.pdf》
《esp32-s3_datasheet_cn.pdf》
ESP32-S3 微控制器上各引脚的详细功能和用途。下面是根据列表的一个概括:
-
电源 §与控制引脚:
- GND (1):接地引脚,用于电路的公共地电位。
- 3V3 (2):提供 3.3V 电源电压,用于芯片和外设供电。
- EN (3):使能引脚,控制芯片的开启与关闭。高电平使能芯片,低电平关闭芯片,不得让其浮空。
-
多功能 I/O 引脚:
- I/O/T:表示引脚可以被配置为输入(I)、输出(O),(T):可设置为高阻。加粗字体为管脚的默认功能。管脚 28 ∼ 30 的
默认功能由 eFuse 位决定。 - TOUCH:触摸感应功能,可以用于实现触摸传感器。
- IO4 至 IO42:通用输入/输出引脚,具有多种复用功能,包括 RTC GPIO、触摸感应、ADC 输入、SPI、I2C、UART 等。
- 其中一些引脚还支持差分信号(如 IO47 和 IO48)或作为 JTAG 调试引脚(如 IO42)。
- I/O/T:表示引脚可以被配置为输入(I)、输出(O),(T):可设置为高阻。加粗字体为管脚的默认功能。管脚 28 ∼ 30 的
-
实时时钟(Real-Time Clock)GPIO:
- RTC_GPIO,即使在 CPU 关闭的情况下,这些引脚也能继续工作,用于低功耗应用。
-
UART 信号引脚:
- RXD0 (G44) 和 TXD0 (G43):用于 UART 串行通信的接收和发送,也可用作通用 I/O 或时钟输出。
- 例如:U0TXD, U0RXD, U0RTS, U0CTS, U1TXD, U1RXD, U1RTS, U1CTS
-
模拟信号引脚:
- ADC1_CH0 至 ADC1_CH8 和 ADC2_CH0 至 ADC2_CH9:模拟数字转换器的输入通道,用于采集模拟信号。
- 这些是模拟数字转换器(Analog-to-Digital Converter)的通道,用于将模拟信号转换为数字信号。
-
全速 SPI (FSPI)和辅助 SPI (SUBSPI)信号引脚:
- 多个引脚可以配置为 全速 SPI 或辅助 SPI(SUBSPI)的 MOSI、MISO、SCLK、SS、HD、DQS 等信号。
- 高速串行通信:FSPIHD, FSPICS0, FSPID, FSPICLK, FSPIQ, FSPIWP, FSPIDQS
- 子系统 SPI 的信号:SUBSPICS0, SUBSPID, SUBSPICLK, SUBSPIQ, SUBSPIWP, SUBSPICLK_P_DIFF, SUBSPICLK_N_DIFF
-
JTAG(Joint Test Action Group)调试引脚 接口的信号,用于调试和编程:
- MTCK (G39)、MTDI (G41)、MTDO (G40) 和 MTMS (G42):用于芯片的 JTAG 调试接口。
-
其他特殊功能引脚:
- CLK_OUT1 、CLK_OUT2、CLK_OUT3:时钟输出信号,可以提供外部电路所需的时钟频率。
- USB_DP 和 USB_DM:用于 USB 通信的差分信号引脚(通过 U1CTS 和 U1RTS 引脚间接提及)。
- USB_D+ (G19):对应 USB_DP(Data Positive),这是 USB 数据线的正向数据线。
- USB_D- (G20):对应 USB_DM(Data Minus),这是 USB 数据线的负向数据线。
-
额外的接地引脚:
- GND (40) 和 EPAD (41):额外的接地引脚,EPAD 还可用于提高芯片的散热性能。
在设计使用 ESP32-S3 的电路时,理解每个引脚的功能和复用能力非常重要,这有助于优化硬件布局和软件配置,以满足项目的需求。
import machine
help(machine)
help(machine.Pin)