树莓派-PICO-入门笔记-全-

树莓派 PICO 入门笔记(全)

树莓派Pico初学者入门:p01:从这里开始学习微控制器

在本节课中,我们将要学习什么是微控制器,以及如何利用树莓派Pico开启你的创造之旅。树莓派Pico是一个微控制器,它是构建各种智能设备和项目的核心。

微控制器就像一个微型大脑,能让日常物品变得更智能。你可以用它来创造自主机器人、自动植物浇水系统、各种小工具,甚至是自己的智能家居设备。这些小小的芯片是现代世界大多数设备背后的秘密源泉。本教程旨在带领完全的新手入门,让你掌握足够的知识,能够走出去开始制作一些非常酷的东西。

本课程内容全面,但我们力求用最少的时间教授最多的知识。在学习过程中,我们会确保使用的示例是实用的,能够解决你在现实世界中会遇到的问题。最重要的是,本课程完全免费,所有内容都不会隐藏在付费墙后。我们已尽量减少你需要准备的物品,所需材料清单、所有代码及其他信息都可以在课程页面找到。

接下来,我们将初步涉足编程基础,学习如何控制LED灯、传感器和电机等。之后,我们会为项目添加更智能的思考能力。最后,我们将提升难度,进入更高级的主题,例如通过WiFi用这块板子托管网站,以及使用GPS和OLED屏幕等复杂硬件。

如果你想学习一项可能改变你人生轨迹的技能,请跟随我们一起开始。


本节课中我们一起学习了微控制器的基本概念、树莓派Pico的潜力以及本课程的大致路线图。从下一节开始,我们将动手实践,点亮第一个LED。

002:课程概述与第一章简介

在本节课中,我们将要学习整个课程的框架,并预览第一章“入门准备”的核心内容。第一章旨在帮助你理解微控制器的基础知识,并完成树莓派Pico的初始设置。

课程开篇

让我们开始这门课程。每一章的第一个视频都将概述该章内容,并介绍一些与我们即将学习的主题相关的关键概念。

第一章内容简洁明了,它可能是所有章节中最短的一章。

第一章内容预览

以下是第一章我们将要涵盖的主要内容:

  • 微控制器介绍:我们将首先讲解什么是微控制器,以及它们擅长做什么。
  • 认识Pico:我们将了解树莓派Pico及其一些不同的型号变体。
  • 安全指南:我们会学习一些基本的安全操作准则。
  • 环境设置:我们将完成所有设置,为编程Pico做好准备。
  • 学习建议:最后,我们会分享一些技巧和建议,帮助你从本课程中获得最大收益。

本章除了设置Pico并准备编程环境外,没有太多需要动手操作的内容。因此,你可以放松心情,让我们正式开始。


本节课中我们一起学习了课程的总体结构以及第一章“入门准备”的具体学习目标。接下来,我们将深入探讨什么是微控制器,并开始动手设置你的树莓派Pico。

003:什么是微控制器

在本节课中,我们将要学习微控制器的核心概念,了解它与普通计算机的区别,并探索它在现实世界中的广泛应用。

你可能通过出色的单板计算机系列了解树莓派。但这里的树莓派Pico不是一台计算机,它是一块微控制器板,这完全是另一回事。

微控制器仍然拥有与普通计算机相似的硬件类型,比如处理器RAM内存,它都具备。那么两者的区别是什么?

你的笔记本电脑或台式机等计算机,设计用于执行广泛的任务,例如浏览互联网、玩游戏或撰写报告。而像我们这里的Pico这样的微控制器板,是一个我们可以编程来完成非常特定任务的微型处理器。

计算机运行像Windows或MacOS这样功能齐全的操作系统。微控制器通常没有你可以用键盘或鼠标交互的用户界面。相反,它们允许你轻松地将硬件接入其中,例如温度传感器、光传感器、电机等。然后你编写一些代码,将其放入微控制器,这些代码会告诉微控制器如何处理你刚刚接入的硬件。

🛠️ 微控制器应用实例

以下是几个具体的应用示例,展示了微控制器如何执行特定任务。

  • 智能植物浇水系统:在这个项目中,我们将一个土壤湿度传感器和一个水泵接入Pico。我们为Pico编写了一些代码,告诉它使用传感器检查土壤的湿度。在代码中,我们还告诉Pico:如果土壤湿润,什么也不做;如果干燥,则打开水泵给植物浇水。Pico会一遍又一遍地循环执行这个相同的任务。
  • 智能车库门:这是一个自动车库门,接入了一个Pico,使Pico能够发送信号来开关车库门。随后编写了一些代码,利用Pico的Wi-Fi功能,允许通过手机无线控制车库门,取代了那个老旧的遥控器。
  • 野生动物保护项目:一个非常酷的例子是肯尼亚的Mara大象项目。他们将一个微控制器(比如这个Pico)与GPS追踪器和运动传感器连接,然后安装到大象身上。接着编写代码,使其能无线报告这些大象的位置。这使得研究人员能够收集数据并研究大象,也让当地护林员能够追踪它们的位置,以帮助保护它们免受偷猎者的伤害。

🔌 无处不在的微控制器

微控制器也存在于一切事物中。你会在洗衣机、电脑鼠标、微波炉、相机、无人机、电动牙刷中找到它们。你会发现它们存在于如此多的电器中,这毫不夸张。它们在这些设备内部就像一个微小的指挥家,控制着设备,但它们只能演奏它们被编程好的“曲目”。

对于所有这些应用场景,微控制器都比普通计算机更理想,原因如下:

  • 功耗更低
  • 发热更少
  • 尺寸更小
  • 价格便宜得多
  • 因为这些应用需要特定且简单的任务,意味着需要一遍又一遍地重复执行,所以微控制器是完美的选择。

我认为最重要的一点是,理解如何使用像这个Pico这样的微控制器,能让你有能力为特定问题构建自己的定制解决方案。范围从拯救一个物种,到简化和自动化家中的任务,再到设计和构建你自己的产品。而这正是我们想要做的——我们想为你配备一套技能工具箱,让你能够创造很棒的东西。但如何使用它,完全取决于你。


本节课中我们一起学习了微控制器的定义、它与通用计算机的关键区别,并通过几个生动的实例看到了微控制器如何通过编程与硬件结合,完成自动化、数据收集等特定任务。理解微控制器是开启硬件编程和物联网项目创作的第一步。

树莓派Pico初学者入门:1.3:Pico型号详解 🧩

在本节中,我们将梳理树莓派Pico的各种型号。型号名称看似繁多,但其命名规则清晰明了。了解这些差异将帮助你选择最适合自己需求的Pico。


基础型号:Pico与Pico 2

首先,存在Pico和Pico 2两种基础型号。Pico是原始型号,Pico 2是第二代型号。Pico 2更新、更快(速度提升约2-3倍),并在硬件上有多处改进。

本课程的大部分内容,使用原始Pico或Pico 2均可完成。尽管Pico 2速度更快,但我们很少运行计算密集型任务。因此,对于本课程的项目,你几乎感觉不到两者的性能差异,它们基本可以互换。

  • 选择建议:若希望为未来项目预留更多性能,选择Pico 2。若想节省一些预算,选择原始Pico。

无线功能型号:名称中的“W”

上一节我们介绍了基础型号,本节中我们来看看功能的扩展。基础型号Pico不具备无线连接能力。

部分Pico型号会配备一个大型银色无线芯片,这赋予了它们Wi-Fi和蓝牙功能。这些型号会在名称中包含字母 “W”

例如,Pico 2W和Pico W就是在基础型号上增加了此无线芯片的版本。除了这颗额外的芯片,它们的功能与常规型号完全相同。在后续章节中,我们将使用Wi-Fi完成一些有趣的项目,因此推荐你购买带无线功能的型号


预焊排针型号:名称中的“H”

你的Pico需要焊接排针(引脚),这是我们连接外部元件的方式。如果购买基础型号,你需要自行购买并焊接排针。

你可以省去这个麻烦,直接购买已预焊好排针的型号。如果型号名称中包含字母 “H”,则表示它出厂时已焊好排针。

以下是具体示例:

  • Pico:无排针,需自行焊接。
  • Pico H:已预焊排针,无需焊接。
    此规则同样适用于无线型号:
  • Pico W:无排针,需自行焊接。
  • Pico WH:已预焊排针且具备无线功能。

总结与选购指南

本节课中我们一起学习了树莓派Pico的型号命名规则。我们来总结一下:

  1. 代际:选择原始Pico以节省开支,或选择性能更强的Pico 2
  2. 无线功能:名称中含 W 的型号具备Wi-Fi和蓝牙。
  3. 排针:名称中含 H 的型号已预焊排针,到手即用。

如果不知如何选择,直接购买一款 WH 型号(如Pico WH或Pico 2WH)是最省心的方案。这样,你既能完成无线章节的项目,也无需自己动手焊接。

008:成功小贴士 🎯

在本章节的最后一节中,我们将探讨一些有助于你从本课程中获得最大收益的实用建议。

概述

本节将提供一系列关于如何高效学习编程、处理代码以及应对学习过程中可能遇到的困难的具体建议。我们的目标是帮助你建立正确的学习心态和方法。

核心学习理念

上一节我们介绍了具体的项目实践,本节中我们来看看如何优化你的学习过程。

首先,本课程将涵盖大量执行不同功能的代码片段。重要的是,你不需要死记硬背所有内容。

我的意思是,你无需记住我们学习的每一段代码的确切语法或用法。更重要的是理解代码的工作原理以及每一部分的作用。

你可以随时回顾我们提供的任何示例,并将代码复制到你正在进行的项目或任何实际应用中。我完成了机电工程学的整个学位,我所做的每一个项目都包含从旧项目中复制并适配到新项目的代码。但要做到这一点,你首先需要理解它是如何工作的。理解比记忆更重要

编程风格与效率

其次,编写代码时,通常有几种不同的方法可以实现相同的结果。通常,只要代码能运行,就没有绝对“正确”或“错误”的方式。

以下是关于编程风格的建议:

  • 会有更高效和更低效的方法,但请随意使用你最习惯的方式。
  • 在99%的情况下,这都无关紧要。但请确保,如果你以某种奇特或不寻常的方式做事,请保持一致性。
  • 比试图理解某人用奇怪方式编写的代码更糟糕的是,如果他们每两秒钟就改变他们使用这种奇怪方式的系统,那会让人非常难以跟进。

个性化学习路径

另一件事是按照你想要的方式学习。如果你想快速学完整个课程并开始自己的项目,请随意这样做。

如果你学到一半,意识到已经学到了完成目标项目所需的一切,那就去完成那个项目。本课程只是一个庞大的资源库,供你学习想学的东西。

这引出了互动学习的一个重要观点:通过跟随并复现一个演示,你会比仅仅坐着观看学到更多

如果你想更进一步,可以尝试以下方法:

  • 摆弄它,调整它,修改它,混合它。
  • 以不同的方式应用它,或许应用到你在做的项目上,将其推到极限。

你正在以最有效的方式之一进行学习。你不必这样做,但至少在你能力范围内尽量跟随操作。

遇到困难时怎么办

好的,最后一件事:如果遇到困难该怎么办?你很可能会遇到障碍,或者出现一些你无法解决的问题。

😡 首先,重新做一遍。回到它还能正常工作的时候,或者从头开始,从那里重新跟随操作。很可能你遗漏了某些东西。

一个更好的选择是寻求专家帮助。如果你能找到这样的人,那太棒了,无论是朋友还是老师。但并非所有人都有这样的资源,所以我们可以使用次优选择。

ChatGPT 在2023年某种程度上永远改变了编程,因为我们突然可以访问像ChatGPT、Gemini、Mial这样的大型语言模型,它们通常比我们更理解代码的工作原理。我强烈建议你尽可能获取最好的模型。更强大的模型将能提供更多帮助,但这可能需要付费订阅。在撰写本文时,ChatGPT是主导的大型语言模型,其免费的GPT模型应该足够强大,足以帮助你完成本课程。请注意,你需要创建一个账户。

例如,我可以问:“为什么我的MicroPython代码不工作?”我可以直接将代码粘贴到ChatGPT中,也许给它一些关于我想做什么以及什么不工作的上下文,它应该能够帮助排查问题所在。你可以像和真人一样与它交谈:“我试了这些方法,但没用,还有其他想法吗?”它会给你更多需要检查的事项。

它也可以为你从头生成代码。例如,我可以要求它:“为我生成MicroPython代码,当按下连接到P16的按钮时,让连接到我的Pico P15的LED闪烁10次。同时告诉我如何连接线路。”它就会照做。

不过,不要过度依赖它来为你编写所有代码。它是一个很好的辅助工具。在使用它生成的代码之前,确保你至少理解它写了什么,这是一个好习惯。

假设你无法获得专家帮助或无法访问ChatGPT,或者ChatGPT没有帮助。欢迎在我们的社区论坛上发帖。我们那里有很多关注者,你很可能会得到帮助。这是一个非常社区化的空间,有许多不同技能水平的人在创客相关活动上合作。

总结

还有最后一点:享受乐趣。做你需要做的事情来享受这门课程。如果你玩得开心,你更有可能完成它。所以,无论你需要做什么,就去做吧。

本节课中我们一起学习了如何高效学习编程的核心理念、处理代码的灵活方法、制定个性化学习路径的重要性,以及在遇到困难时可以采取的具体步骤。记住,理解胜过记忆,实践促进学习,而保持乐趣是坚持到底的关键。

现在,让我们开始实践吧!😊

树莓派Pico入门教程:2.1:基础输入输出简介

在本章中,我们将学习如何使用基础输入输出(基础IO)让树莓派Pico开始观察和与周围世界互动。

当我们提到基础IO时,指的是通过Pico的GPIO引脚输入和输出数字与模拟信号。

首先,数字信号只有两种可能的状态:开或关。当我们使用Pico的引脚输出数字信号时,它类似于一个普通的电灯开关,只能打开或关闭电源,没有中间状态。

另一方面,模拟信号则是一个可以在开与关之间,或者说最大值与最小值之间取任意值的信号。它与数字输出不同,因为它可以取两者之间的任何值。当我们使用Pico的引脚输出模拟信号时,它类似于一个可调光开关,你可以在完全打开和完全关闭之间设置任何亮度级别。

我们现在只是介绍这些概念,一旦我们通过一些示例深入探讨,所有这些都会变得更加清晰。

除了编写代码来使用Pico的引脚进行基础IO操作,我们还将穿插一些电路搭建、关于电源的理论知识以及Pico的通用知识。因此,在本章结束时,你应该具备连接一些基本硬件并让你的Pico开始与周围世界互动的技能。


上一节我们介绍了基础IO的概念,本节中我们来看看数字信号与模拟信号的具体区别。

以下是两种信号的核心特征:

  • 数字信号

    • 状态:只有两种,通常表示为 高电平(1/ON)低电平(0/OFF)
    • 类比:普通电灯开关。
    • 在代码中,通常用布尔值 True/False 或数字 1/0 表示。
  • 模拟信号

    • 状态:在最小值和最大值之间连续变化,可以取无限个值。
    • 类比:可调光开关或音量旋钮。
    • 在微控制器中,通常通过脉宽调制(PWM) 来模拟,或用模数转换器(ADC) 读取。

通过本章的学习,我们一起了解了树莓派Pico基础输入输出的核心概念,区分了数字信号与模拟信号的根本不同,并明确了本章的学习目标是为后续的实际硬件交互打下基础。

012:读取数字输入

在本节课中,我们将学习如何读取数字输入。树莓派Pico的26个GPIO引脚都可以用作数字输入。我们将通过一个按钮的例子来理解其工作原理,并学习如何编写代码来读取引脚的状态。

数字输入原理

上一节我们介绍了如何设置数字输出,本节中我们来看看如何读取数字输入。

数字输入与数字输出的逻辑相反。当我们设置数字输出时,使用 pin.value(1) 将引脚设置为3.3伏,使用 pin.value(0) 将其设置为0伏。对于数字输入,当引脚接收到约3.3伏电压时,代码会读取到值 1;当接收到约0伏电压时,会读取到值 0。本质上,它是在测量引脚上的电压是接近3.3伏还是0伏,并相应地返回1或0。

实践:读取按钮状态

学习的最佳方式是通过实例。让我们直接看一个使用按钮的例子,按钮是数字输入设备的完美示例。

如果你还没有连接上一视频中的电路,请确保至少按以下方式连接好电路。

创建一个新文件并粘贴演示代码。以下是我们的代码,它非常简洁,以便我们专注于数字读取。

from machine import Pin
import time

button = Pin(15, Pin.IN, Pin.PULL_DOWN)

while True:
    print(button.value())
    time.sleep(0.1)

以下是代码的详细解释:

  1. 导入库和模块:通过导入 Pin,我们可以使用 .value() 和引脚设置函数。导入 time 允许我们使用 sleep 函数。
  2. 初始化输入引脚:我们将引脚15初始化为输入,并将其存储在名为 button 的变量中。第三个设置 Pin.PULL_DOWN 是可选的,用于启用下拉电阻,我们稍后会讨论。
  3. 注意大小写:代码对大小写敏感。例如,将 Pin 写成 pin 会导致语法错误。
  4. 读取引脚值:使用 .value() 命令读取我们设置为输入的引脚状态,并将其打印到Shell中。
  5. 无限循环与延时:代码位于 while True 循环中,会不断重复。time.sleep(0.1) 将读取速度减慢到每秒10次,这足以满足我们的需求,并避免一些问题。

电路的连接方式是:按下按钮时,将Pico的引脚15连接到我们设置的电源轨的3.3伏;不按下时,则不提供任何电压。这完全符合数字输入读取的3.3伏逻辑电平。

确保你的Pico已连接,运行代码。如果一切连接正确,你将看到Pico打印按钮的当前状态:0 表示未按下,1 表示按下并提供3.3伏。

你还可以点击“视图”然后选择“绘图仪”,以图表形式查看状态变化。按下按钮,图表线应上升到1。

电压容差与下拉电阻

有时会提出一个问题:如果向引脚提供2伏或1伏电压,而不是精确的3.3伏,它会读取到什么?

实际上,数字输入不需要精确的3.3伏或0伏,两端都有一定的容差空间。但是,这仅适用于数字输入。对于上一节学习的数字输出,引脚输出会非常接近3.3伏或0伏。

现在,让我们看看禁用下拉电阻会发生什么。在引脚设置中删除 Pin.PULL_DOWN 参数。

button = Pin(15, Pin.IN)  # 移除了下拉电阻

重新运行代码后,你会发现即使没有按下按钮,引脚电压也会漂浮不定,读数混乱。这是因为当按钮未按下时,引脚没有连接到任何明确的电压源(0伏或3.3伏),处于“悬空”状态,容易受到环境噪声干扰。

下拉电阻的作用就是微弱地将该电压拉低至0伏。当没有3.3伏电压供应时,它能确保引脚稳定在0伏,而不是漂浮不定。Pico内部集成了微小的下拉电阻,可以通过代码启用,这非常方便。

综合应用:用按钮控制LED

最后一个例子,我们将刚学的数字输入与之前学的数字输出结合起来。你可以用已连接的电路跟着操作。

复制第二段演示代码并粘贴到新文件中。

from machine import Pin
import time

button = Pin(15, Pin.IN, Pin.PULL_DOWN)
led = Pin(16, Pin.OUT)

while True:
    if button.value() == 1:
        led.value(1)  # 打开LED
    else:
        led.value(0)  # 关闭LED

在这段代码中,我们像之前一样设置输入,同时将引脚16设置为输出并连接到LED。

while True 循环中,我们引入了一个 if-else 语句(我们将在下一章深入学习此类逻辑)。它的工作原理很简单:检查按钮引脚的值,如果等于 1,则运行其内部的代码(打开LED);否则(即不等于1),运行 else 部分的代码(关闭LED)。注意,ifelse 下面的代码需要缩进(4个空格或1个制表符)。

运行这段代码,当你按下按钮时,LED应该会亮起。这段代码完美展示了微控制器最基本的应用:读取某种数据(本例中为按钮状态),然后通过if语句进行决策或计算,最后输出信号来控制某些设备(本例中的LED)。

数字输入的应用场景

那么,数字输入还能做什么呢?

  • 安全开关:这个按钮可以是机器人或任何项目上的大型红色紧急停止开关。如果按下按钮,则关闭一切以确保安全。
  • 键盘:你可以将许多按钮串联起来制作一个键盘。有一个完整的社区专门制作非常精美的定制键盘,他们使用像Pico这样的微控制器来实现。
  • 传感器:如果你不想用按钮,还有很多传感器可用:
    • 声音传感器:如果环境安静则返回0伏,如果声音大则返回3.3伏。你可以制作一套能用掌声开关的灯。
    • 光线传感器:返回0或3.3伏。你可以将其指向地面,用于制作循线机器人,这是一个非常常见的入门项目。
    • 超声波距离传感器:它 technically 使用数字输出和输入。它的工作原理是发出一束我们听不到的声波,当声波反弹回来时返回3.3伏。通过一些数学计算,你可以算出声音传播的时间,从而用声音计算距离。

总结

本节课中我们一起学习了:

  1. 我们需要导入模块和库(如 machine.Pintime)来使用有用的函数。
  2. 26个GPIO引脚中的任何一个都可以读取数字输入,0伏返回0,3.3伏返回1
  3. 使用按钮等组件时,需要使用下拉电阻来防止电压漂浮不定。

013:变量

概述

在本节课中,我们将要学习编程中的一个核心概念——变量。变量是存储数据和信息的方式,理解它们对于编写任何程序都至关重要。我们将通过简单的比喻和实际的代码示例来学习如何创建和使用变量,并了解其基本规则和类型。

什么是变量?📦

变量是编程的基石。我们可以将变量想象成一个盒子,用于存储数字、文字或任何类型的信息。例如,如果我们想记录一只狗的年龄,可以创建一个名为 age 的“盒子”。今年狗3岁,我们就把数字 3 存入这个盒子。每年生日,我们只需将盒子里的数字加1,而无需记住具体的数字。任何时候,我们都可以通过查看盒子里的数字来知道狗的年龄,无需进行额外计算或记忆。

虽然这个例子很简单,但在微控制器编程中,你可能需要同时跟踪几十个变量、数字、状态和倒计时,而微控制器每秒能执行数百万次计算。此时,变量就成为一种极其重要且简便的数据管理和使用方式。

变量的实际应用 💡

我们已经在前面的课程中不知不觉地使用了变量。例如,当我们设置引脚15为输入时,我们将这个引脚实例存储在一个名为 button 的“盒子”里。之后,当我们想读取这个引脚的值时,实际上就是读取了装有引脚实例的 button 盒子。

学习变量的最佳方式是通过实例。接下来,我们将通过两个代码示例来深入理解。

示例一:按钮计时器 ⏱️

以下是第一个示例。我们将计算按钮被按下的秒数。

首先,将示例代码复制并粘贴到一个新文件中并保存。示例代码可在课程网站页面找到,YouTube视频描述中也附有链接。

这段代码与之前提到的“狗的年龄”例子非常相似。我们导入必要的库,在引脚15上设置一个按钮,然后创建一个名为 timer 的变量。创建变量非常简单:只需输入变量名,加上等号,然后赋予它数据即可。

import machine
import utime

button = machine.Pin(15, machine.Pin.IN, machine.Pin.PULL_DOWN)
timer = 0

while True:
    utime.sleep(1)
    if button.value() == 1:
        timer = timer + 1
    print(timer)

while True 循环中,我们先延迟一秒。之后每秒检查一次按钮状态。如果按钮被按下(值为1),就将变量 timer 的值增加1。最后,打印出 timer 变量中存储的当前数值。

运行代码,Pico每秒会打印一次 timer 的当前值。如果不按按钮,数值不会增加。如果按住按钮,它将计算按钮被按下的秒数。松开按钮后,计数停止。

示例二:优化按钮与LED代码 💡

现在,让我们修改上一个视频中的按钮和LED代码,来展示使用变量的优势。假设我们除了控制LED,还想在Shell中打印按钮的状态。

初始代码可能如下所示:

import machine
import utime

button = machine.Pin(15, machine.Pin.IN, machine.Pin.PULL_DOWN)
led = machine.Pin(25, machine.Pin.OUT)

while True:
    utime.sleep(0.1)
    if button.value() == 1:
        led.value(1)
    else:
        led.value(0)
    print(button.value())

这段代码可以正常工作,但效率不高。为了解释原因,想象一下Pico不是读取按钮,而是你在检查天空是否有云。第一行指令是:“如果天上有云,就打开LED。” 你需要起身,到外面查看,然后回来。紧接着,print 函数又命令你:“起身,到外面查看是否有云,然后打印结果。” 但你刚刚才查看过,已经知道结果了。这种方式效率低下。

对于运行速度极快的Pico来说,if 语句和 print 语句中两次读取按钮状态的时间间隔极短(可能只有百万分之一秒),按钮状态在这段时间内几乎不可能改变。因此,反复读取是不必要的。

以下是使用变量优化的更好方式:

import machine
import utime

button = machine.Pin(15, machine.Pin.IN, machine.Pin.PULL_DOWN)
led = machine.Pin(25, machine.Pin.OUT)

while True:
    utime.sleep(0.1)
    b_state = button.value()  # 读取一次并存入变量
    if b_state == 1:
        led.value(1)
    else:
        led.value(0)
    print(b_state)  # 使用存储的变量值

现在,循环开始时只读取一次按钮状态,并将其存储在变量 b_state 中。随后的 if 语句和 print 语句都使用这个变量值。这就像你只出门查看一次天气,然后在心里记住(存储)这个结果,并基于这个记忆(变量)进行后续操作。

在简单代码中,两种方式效果相同。但在更复杂、冗长的代码中,每次都让Pico检查引脚状态会显著降低效率。因此,使用变量的方式是更优的编程实践,也使代码更易读。

变量的重要特性:临时性 ⚠️

需要特别注意的一点是:变量是临时的。如果你关闭Pico的电源,所有存储在变量中的数据都会完全丢失。重新启动Pico后,一切将从零开始。

变量的类型 🔢

MicroPython 让使用变量变得非常容易,以至于你有时可以忽略变量类型。但理解类型仍然很重要。变量有不同的类型,最常见的三种是:整数浮点数字符串

  • 字符串:可以将其视为只存储单词、句子或任何键盘可输入内容的盒子。创建时需用引号括起来。
    x = "这是一个字符串"
    print(x)
    
  • 整数:存储整数,如 1, 3, 5。
  • 浮点数:存储带小数点的数字,如 2.33, 4.1。

对于初学者,选择正确的变量类型可能令人困惑。但幸运的是,在 MicroPython 中创建变量时,它会根据我们想要存储的数据自动选择正确的变量类型

例如:

x = 2.5  # MicroPython 会自动将其分配为浮点数

我们也可以手动覆盖并设置变量类型:

x = int(2.5)  # 尝试将2.5作为整数存储
print(x)      # 输出将是 2,因为小数部分被截断了

所以,字符串存储文本,整数存储整数,浮点数存储小数。MicroPython 的自动类型管理是其易于使用的原因之一。

变量的命名规则 📝

之前我说过可以随意命名变量,但这并不完全准确。命名变量有一些规则:

  1. 变量名不能包含空格
  2. 只能使用字母、数字和下划线。例如,我们使用了 b_state,其中的下划线就是为了替代空格。
  3. 变量名不能以数字开头。必须以字母或下划线开头。
  4. 变量名是大小写敏感的。b_stateB_state 是两个不同的变量。
  5. 不能使用 Python 的关键字作为变量名。例如,不能将变量命名为 import,因为 MicroPython 无法区分你是指一个叫 import 的变量,还是想导入一个库。

变量与数学运算 ➕➖✖️➗

在第一个计时器示例中,我们使用了 + 运算符来增加 timer 变量的值。MicroPython 提供了多种数学运算符供你使用。

你可以回到那个例子,尝试将 + 改为 -*(乘)、/(除)等。如果导入 math 库,你还可以使用三角函数(如 sin, cos, tan)等,这在处理角度相关的项目时非常有用。

你不需要现在就掌握所有数学函数,只需知道:当需要在 Pico 上处理数据并进行数学计算时,一定有相应的数学函数可以完成。变量和数学运算常常携手并进。

总结 🎯

本节课中我们一起学习了变量的核心知识:

  1. 变量是代码中存储和跟踪数据的便捷方式。它们就像贴有标签的盒子,让数据管理变得清晰有序。
  2. 变量类型很重要,但 MicroPython 能很好地自动管理它们。我们了解了字符串、整数和浮点数这三种基本类型。
  3. 变量命名有特定规则:只能包含字母、数字和下划线;不能以数字开头;大小写敏感;不能使用Python关键字。

掌握变量是迈向有效编程的关键一步。在接下来的项目中,你会越来越频繁地使用它们来构建更复杂、更强大的程序。

树莓派Pico初学者入门:2.6:读取模拟输入

在本节课中,我们将要学习如何让树莓派Pico读取模拟信号。这是进入更酷项目的关键一步,因为许多现实世界中的传感器都输出模拟信号。


概述:模拟信号与数字信号

模拟信号和数字信号是完全不同的。我们可以用天空中的云来举例。

  • 数字信号就像问:“天上有云吗?”这个问题只有一组二进制答案。换句话说,答案要么是“是”,要么是“否”,你可以用 10 来表示。
  • 模拟信号则像问:“天空被云覆盖的百分比是多少?”这个问题的答案远不止一个,范围在 0%100% 之间。答案可以是 42.371%,也可以是 99.2%,可以是 0100% 之间的任何数字。

对于树莓派Pico来说,这意味着当我们把一个引脚设置为模拟输入时,我们可以读取该引脚上 0V3.3V 之间的精确电压值。

如果你还记得之前视频中的例子,我手动调整引脚上的电压,橙色线是读取该电压数字输入的结果,只能返回 10;而蓝色线实际上就是该电压的模拟读数,我们得到了从 03.3V 的连续值。它只是读取我提供给引脚的任意电压。


硬件连接与代码准备

要开始实践,你需要搭建本章节对应的电路。至少,你需要一个连接到Pico的电位器。

示例代码可以在我们的课程页面找到。我们从导入必要的模块开始。

from machine import ADC, Pin
import time

这次我们导入了 ADC,它代表模数转换器,这将允许我们设置模拟输入。


设置模拟输入引脚

接下来,我们将Pico的引脚26设置为模拟输入,并连接到面包板上的电位器。

pot = ADC(Pin(26))

这里有一个非常重要的点:只有引脚 26、27 和 28 可以用作模拟输入。Pico只有这三个引脚可以做到这一点。这是因为将模拟信号转换为数字信号需要一块特殊的硬件,称为模数转换器,而只有这三个引脚可以访问Pico上的这块硬件。如果你尝试在其他引脚上使用,它将无法工作。

我们还定义了一个变量,暂时先忽略它,我们稍后会详细解释。

conversion_factor = 3.3 / 65535

读取并处理模拟值

在我们的主循环中,我们读取电位器的值。

while True:
    reading = pot.read_u16()

read_u16() 是实际读取模拟输入的地方。它会返回一个值。

现在,我们进行一点数学运算,将读数乘以转换因子,然后存储在一个变量中。

    pot_voltage = reading * conversion_factor
    print(pot_voltage)
    time.sleep(0.2)

插入你的Pico,运行代码。当你旋转电位器时,Shell中显示的电压读数应该会变化,报告该引脚上当前设置的电压。你也可以启用绘图器,这非常酷,当你旋转电位器时,可以看到一条线在你的电脑上移动。


理解原始数据与转换

我们的电位器工作原理是:它的一端接地,另一端接3.3V。当我们转动中间的旋钮时,它实际上是在混合这两种电压。

但这里我们“取巧”了。Pico上的模拟输入并不能完美地读取0到3.3V。我们使用的 conversion_factor 在打印之前将其修改到了这个理想的电压范围。这也展示了使用变量的另一种好方法:将它们用作永远不会改变的常量

现在,让我们移除乘以转换因子的步骤,看看原始数据输入是什么。

while True:
    reading = pot.read_u16()
    print(reading)
    time.sleep(0.2)

你会发现它的行为类似,但打印的范围是 0 到 65535,而不是 0 到 3.3。这是因为ADC和MicroPython的工作方式。


模数转换器的工作原理

Pico是一个数字设备,而我们周围的世界(例如电压)是非常模拟的。为了让像Pico这样的数字设备读取其引脚上的模拟信号(如电压),它需要使用一个ADC

ADC做的事情正如其名:它将Pico引脚上的模拟电压转换成数字Pico可以使用的东西。但它的实现方式是:将那个电压转换成一个漂亮的整数。这有点奇怪,但这就是数字设备的工作方式。

在我们的Pico上:

  • 0V 的读数将返回数字 0
  • 3.3V 的读数将返回数字 65535
  • 0V 和 3.3V 中间的电压将返回 0 和 65535 中间的数字。
  • 四分之一处的电压将给出四分之一处的数字。

它在两个范围之间完美映射。通过一点数学计算,你可以发现,如果乘以我们存储在 conversion_factor 变量中的数字,它会变回 0 到 3.3 的范围。你并不总是需要这样做,但我们的目标是读取引脚上的电压,这样做使其更易于人类阅读。你应该根据项目需求或自己的习惯来选择是否转换。


模拟传感器的应用世界

现在我们可以读取引脚上的电压了(只要它在0到3.3V之间),我们能用它做什么呢?我们可以读取传感器

我们周围的世界是非常模拟的,这意味着你遇到的许多传感器都会为你提供模拟信号。它们通常比数字传感器更有趣,提供的信息也更多。

以下是几个例子:

  • 麦克风传感器模块:它有两个输出。数字输出在安静时返回0,响亮时返回1。但模拟输出返回一个与房间响度相对应的电压,因此你可以获得比“安静或响亮”更多的数据,你可以知道房间有多响。
  • 温度传感器:它也有一个模拟输出。在 -50°C 时,它输出 0V;在 125°C 时,输出 1.7V。你可以将其插入Pico,进行模拟读数,然后通过一点数学计算将读取的电压转换为实际温度。
  • 土壤湿度传感器:它根据检测到的土壤中的水分输出一个电压。你可以将其放入干燥的土壤中查看读数,然后放入刚浇过水的土壤中,看到电压读数升高。然后,你可以使用Pico监测传感器输出的电压水平,当土壤变得太干时打开水泵。我们在一个很酷的自动浇水植物套件中正是这样做的。
  • 压敏电阻:我们可以将其接线,使其在无压力时读取0V,当我们施加一些力时,它会输出更高的电压。然后我们可以将其放在垫子下,使用Pico测量人们踩上去时的电压峰值。你甚至可能能够检测到是宠物还是人:猫或狗施加的压力较小,导致的电压变化较小,而一个重的人会产生更大的信号。

我想说的是,一旦我们可以使用模拟传感器,一个不可思议的世界就打开了。


总结

本节课中我们一起学习了如何让树莓派Pico读取模拟输入。让我们回顾三个关键要点:

  1. 引脚限制:只有Pico的引脚 26、27 和 28 可以用作模拟输入。
  2. 读取步骤:要读取模拟信号,你需要导入 ADC,然后使用 ADC(Pin(pin_number)) 设置引脚,并使用 .read_u16() 方法读取它。
  3. 读数范围:模拟读数将输入一个从 0 到 65535 的数字,该数字代表引脚上从 0V 到 3.3V 的电压。你可以通过数学计算将其转换回电压值。

树莓派Pico初学者入门:2.7:PWM输出

在本节课中,我们将要学习如何使用脉冲宽度调制(PWM)来模拟模拟信号输出。我们将了解PWM的工作原理,如何在树莓派Pico上配置PWM,并通过控制LED亮度的实例来掌握其应用。


上一节我们介绍了模拟输入,本节中我们来看看模拟输出。从模拟信号转换为数字信号并不困难,但从数字信号转换为真正的模拟信号则要复杂得多,以至于大多数微控制器无法原生输出一个在0到3.3伏特之间连续变化的电压。

然而,我们可以使用一种称为脉冲宽度调制(PWM)的技巧来创建模拟信号的复制品或模仿品。Pico的引脚只能输出3.3伏特或0伏特,没有中间值。但我们可以让一个引脚以极快的速度(例如每秒数千次)反复开关,从而在0到3.3伏特之间产生一个平均电压。

例如,如果一个引脚开启1毫秒,然后关闭1毫秒,并不断重复这个模式,由于引脚有50%的时间处于开启状态,我们就能产生一个1.65伏特的平均电压(即3.3伏特的50%)。在这个开关周期中,引脚开启时间的百分比被称为占空比

  • 100%的占空比意味着引脚一直开启,输出3.3伏特。
  • 10%的占空比意味着引脚有10%的时间开启,输出0.33伏特。
  • 20%的占空比输出0.66伏特,以此类推。

通过计算任何百分比占空比对应的电压(介于0到3.3伏特之间),我们就可以创建出模拟输出的效果。虽然这不是完美的替代方案,但通常足够好,许多数字设备无法区分PWM信号和真正的模拟信号。


让我们通过示例代码来实践一下。代码可以在我们的课程页面找到(YouTube观众请查看描述中的链接)。

以下是设置Pico在引脚16(我们连接了一个LED和电阻)上输出PWM信号的基础代码。

要使用PWM,我们需要从machine模块导入它,然后初始化我们希望设置为输出的引脚。其语法与设置ADC引脚非常相似。但有一点不同:Pico的26个GPIO引脚中的任何一个都可以输出PWM信号,这对于微控制器来说相当多。

from machine import Pin, PWM

pwm_pin = PWM(Pin(16))

在这行代码中,我们设置PWM信号的频率。这不是占空比,而是引脚每秒开关的次数。设置频率是可选的,你可以移除这行代码,PWM仍将正常工作,只是会使用默认频率。在某些情况下你需要手动设置频率,所以我们包含了它。但请再次注意,这不是占空比。你可以尝试调整它,频率最低可至8赫兹,但通常应保持在1000赫兹左右。

pwm_pin.freq(1000)

然后,我们使用duty_u16方法在引脚上设置占空比。这里我们遇到了与模拟输入相同的问题:我们不能直接输入0到100的占空比值,而必须输入一个介于0到65535之间的数字,它线性地代表0%到100%的占空比。这就是为什么我这里有一个名为max的变量,它对应100%占空比(或3.3伏特)的最大值。这样设置可以方便地设定占空比。

max_duty = 65535
desired_duty_cycle = 0.5  # 代表50%
pwm_pin.duty_u16(int(max_duty * desired_duty_cycle))

在这个例子中,我设置了50%的占空比。运行这段代码,LED应该会亮起,但亮度低于全亮。用万用表测量,可以看到读数约为1.65伏特。如果将PWM占空比改为20%,则可以在万用表上测量到大约0.66伏特。


最后,我们来看第二段示例代码。这段代码将读取电位器的电压值,并将其输出到Pico的16号引脚。这是一个简洁的交互式示例,很好地演示了模拟输入和PWM输出的结合。

在这里,我们只是读取ADC的原始模拟输入值(一个介于0到65535之间的数字),将其存储在名为pot_value的变量中,然后将这个精确的值输出到我们的PWM引脚。我们同时将值打印到Shell,以便观察。

from machine import Pin, PWM, ADC
from time import sleep

adc_pin = ADC(Pin(26))
pwm_pin = PWM(Pin(16))
pwm_pin.freq(1000)

while True:
    pot_value = adc_pin.read_u16()  # 读取0-65535之间的值
    pwm_pin.duty_u16(pot_value)     # 将该值直接设置为PWM占空比
    print(pot_value)
    sleep(0.1)

运行代码,你现在应该可以看到LED的亮度正通过电位器进行控制。这是我们通过结合模拟输入和PWM输出解锁的一项非常酷的能力。


现在我们知道如何使用PWM在Pico的引脚上设置0到3.3伏特之间的平均电压了。那么我们可以用它来做什么呢?我们可以做大多数数字输出能做的事情,但现在有了更多的控制和精细度。

以下是PWM的一些常见应用:

  • 控制LED亮度:这比简单的开关提供了更多的可能性。
  • 驱动RGB LED:RGB LED非常有趣,它们可以使用3个PWM输出来控制内部红、绿、蓝LED的亮度,从而混合出任何你想要的颜色。
  • 控制MOSFET和晶体管:PWM也可用于控制MOSFET和晶体管。例如,我们可以用Pico的PWM来控制汽车前灯的亮度。
  • 控制电机:我认为PWM最强大的功能之一是能够控制电机。例如遥控车、飞机和无人机,它们需要通过PWM精确控制电机速度。风扇内部也有电机,现在你可以使用模拟温度传感器并编写代码,让风扇转速随着温度升高而增加。
  • 驱动各类电机执行器:例如用于推拉物体的线性执行器、用于精确旋转的步进电机,以及伺服电机——这种神奇的设备可以让你以非常精确的角度控制物体。所有这些都可以通过来自Pico的PWM信号进行控制。

本节课中我们一起学习了以下三个关键要点:

  1. 脉冲宽度调制(PWM) 可用于通过快速开关数字引脚来模拟模拟输出。
  2. 我们使用术语 占空比 来描述信号开启时间的百分比,这是我们控制引脚平均电压的方式。
  3. Pico的26个GPIO引脚中的任何一个都可以设置为使用PWM,它使用一个介于 0到65535 之间的值来代表0%到100%的占空比。

通过掌握PWM,你为你的树莓派Pico项目打开了控制亮度、速度和位置的大门。

016:导入库

在本节课中,我们将学习如何使用库来简化编程工作。库是预先编写好的代码集合,可以帮助我们完成复杂任务,而无需自己从头编写所有代码。

什么是库?

库是一种极其强大的工具,可以极大地简化编程工作。它们本质上是预先编写好的代码片段,能够完成大量繁重的工作,从而减轻我们的负担。实际上,我们已经使用了一些库,主要是用于延时和引脚设置。当我们使用 time.sleep() 时,我们不需要了解其背后的实现细节,但为了让Pico进入休眠状态,硬件层面发生了大量复杂且涉及广泛的操作。幸运的是,你只需导入时间库,一行代码 time.sleep() 就能为你完成所有工作。库让我们能够使用预先编写的代码,使生活更轻松。

库的类型

上一节我们介绍了库的基本概念,本节中我们来看看库的两种主要类型。

标准库

MicroPython 拥有所谓的标准库。当你将 MicroPython 安装到 Pico 上时,这些库也随之内置。要使用它们,你只需要 import 后跟库的名称即可。

以下是几个标准库的例子:

  • machine 库:允许我们使用 ADC、PWM 和 Pin 功能,用于设置 Pico 的引脚。
  • time 库:我们一直在使用它的 sleep 函数。
  • math 库:包含 costanlog 等数学函数。
  • bluetooth 库:允许你在 Pico 上使用蓝牙功能。

标准库有很多,一次快速的谷歌搜索就能让你知道可以使用哪些。

外部库

除了标准库,还存在一个庞大的外部库世界,其数量之多令人难以置信,通常用于非常具体和特定的应用场景。很多时候,当你购买一个模块时(比如这个非常酷的 OLED 显示屏),你会找到指向外部库的链接来帮助你使用它。或者,如果它是一个常见的模块,通过快速谷歌搜索也能找到。当你开始使用更复杂的模块时,比如那个交互起来相当困难的 OLED 模块,你将会大量使用库,你的很多工作都将围绕库展开。

但这些外部库需要下载并安装到你的 Pico 上,它们并不随 MicroPython 标准安装。

安装和使用外部库:以舵机库为例

现在,让我们通过一个你可能常用的库——舵机库,来演示一个简单的例子。我们将完整地走一遍寻找库、将其整合到代码中并学会使用的实际过程。

舵机是一种可以让你通过 PWM 精确控制其臂角度的设备,但它们有点复杂,因为它们不是直接用占空比控制的,而是控制每个脉冲之间的间隔时间。这并不太难,但需要一些计算来确定以什么频率发送什么样的 PWM 信号给舵机。因此,通常使用库来处理会简单得多。

步骤一:下载和安装库

首先,我们需要将库下载并安装到 Pico 上,这在 Thonny 中非常简单。

  1. 确保你的 Pico 已连接,并且选择了正确的 COM 端口。
  2. 转到“工具”菜单,点击“管理包”。
  3. 在弹出的窗口中,我们可以从 Python 包索引(PyPI)搜索和安装库。这是最大的 Python 和 MicroPython 库集合。
  4. 我们将搜索 MicroPython 舵机库。输入“servo”,如果不行,再试试“micropython servo”。最后,我们输入了“micropython-servo”,找到了它。
  5. 点击它,然后点击“安装”。这将为你安装该库。

值得注意的是,与 MicroPython 本身一样,这个库只安装到了这个特定的 Pico 上。如果你使用另一个 Pico,需要重新安装所有库。

从这里我们可以看到它确实安装到了我们的 Pico 上。如果我们返回,进入“文件”,然后进入库文件夹,可以看到它现在就在我们 Pico 的库文件夹里了。

步骤二:了解库的用法

库已经安装好了,但我们怎么知道这个库有哪些函数?怎么使用它?这时,我们需要依赖库的创建者提供的文档。

一个好的检查方法是回到包管理器,点击已安装的库。稍等片刻加载,然后我们就能打开 PyPI 页面,希望那里有一些文档,就像这个库一样。有时你可能有一个特定的网页或指南已经提供了文档。快速谷歌搜索一下这个库名也可能找到。无论有什么可用的,最好都用上。

在这里,我们可以看到这个库的创建者已经提供了一些示例工作代码,这非常有帮助。同时,我们还看到了所谓的 API(应用程序编程接口),这实际上是关于如何使用它的说明。对于初学者来说,阅读 API 有时可能有点技术性和复杂。所以,如果你能找到可以复制、粘贴和改编的工作示例代码,通常会更容易一些。

有些库的文档可能非常糟糕,你可能很难弄清楚如何使用它;有些则可能有非常棒的文档,使用起来轻而易举。我通常会选择文档良好的库,这样会让一切变得更简单、更愉快。

步骤三:应用和修改代码

这里的示例代码看起来非常直接。我将把它复制粘贴到 Thonny 中,作为一个起点,看看会发生什么。

现在,让我们插上我们的 9 克舵机,看看情况如何。舵机末端的三根线有两种常见的颜色方案,我们将介绍这两种。

对于电源线,会有一根红色的,可能还有一根黑色或棕色的线。将地线(GND)插入 GND 引脚。这次我们将使用 VBUS(右上角那个引脚)为其供电,因为这个舵机工作在 5 伏特而不是 3.3 伏特,并且可能消耗高达 600 毫安的电流。我们将在后面的视频中讨论这一点。

然后,我们拿起信号线(橙色或白色),将其插入顶部的 GP0 引脚。这只是我们的示例,任何能产生 PWM 的引脚都可以用于舵机。

现在回到我们的代码。首先,我可以清楚地看到他们是如何设置引脚的。所以我们将它设置为引脚 0(我们插入了那里)。然后导入时间,导入我们刚刚安装的舵机库(from servo import Servo)。如果我们运行那个示例代码,可以看到舵机转动了。

从这里开始,我可以随意修改这段代码。例如,我把它放在一个 while True 循环里,然后把睡眠时间改成 1 秒,接着我可以把角度改成 0 度、90 度和 180 度。如果我运行它,现在可以看到它每次摆动 90 度。

在这里,我找到了最重要的东西:实际设置舵机角度的命令,以及达到那一步所需的实际设置。根据你使用的库,这可能需要进行更多的研究,但这是一个你经常会经历的过程,并且每次体验都可能不同。

另一种导入库的方式

有时,你可能会直接得到一个 .py 文件形式的库。你可能正在跟随一个教程,然后它直接以文件形式把库扔给你。要将其上传到开发板,显然需要先连接开发板,然后在 Thonny 中打开文件浏览器。在你的电脑文件下,需要导航到库文件的位置。然后,在你的 Pico 上,打开库文件夹,简单地右键点击并选择“上传到 /lib”,这样它就会被放到开发板上供你使用。

这不仅适用于库,也适用于任何 MicroPython 代码。如果你写了一些很酷的东西,可以把它从你的开发板上取下来放到网上,然后别人可以从网上拿下来放到他们的开发板上,就像我们在这里做的一样。

导入库的两种方式

这里还有一个值得提及的重要点:我们可能已经看到了两种导入库的方式。一种是 import,就像我们在这里做的;另一种是 from ... import ...。它们都是在导入库,但方式略有不同。

让我们以 time 为例。当我说 import time 时,我是在告诉它导入整个时间库及其包含的所有函数。如果我想使用 sleep 函数,我必须在代码中说 time.sleep(),即使用时间库的 sleep 函数。

另一种方式是,我可以说 from time import sleep。这样做只从时间库中导入 sleep 函数。当我这样做时,必须改变在代码中使用 sleep 的方式,因为不再需要 time. 前缀了,我可以直接使用 sleep()

这也意味着,如果我导入了整个 machine 库(它导入了 Pin、ADC、PWM 以及 machine 下的所有其他东西),当我在这里调用 Pin 时,我必须指定 machine.Pin 并加上前缀。如果我到处复制粘贴,代码会变得非常冗长。

所以,如果你导入整个库,调用函数时需要加上 time.machine. 这样的前缀。如果你只从库中导入特定的函数,则不需要。导入库的方式没有绝对的对错,你应该根据个人习惯和项目需求来选择。

手动上传库文件

有时,你可能会直接得到一个 .py 文件形式的库。你可能正在跟随一个教程,然后它直接以文件形式把库扔给你,就像这样。

要将其上传到开发板,你需要:

  1. 连接你的 Pico。
  2. 在 Thonny 中打开文件浏览器。
  3. 在你的电脑文件部分,导航到库文件所在的位置。
  4. 在你的 Pico 文件部分,打开库文件夹(通常是 /lib)。
  5. 右键点击电脑上的库文件,选择“上传到 /lib”。

这样它就会被放到开发板上供你使用。这不仅适用于库,也适用于任何 MicroPython 代码。如果你写了一些很酷的东西,可以把它从你的开发板上取下来放到网上,然后别人可以从网上拿下来放到他们的开发板上,就像我们在这里做的一样。

总结

本节课中我们一起学习了关于库的知识。三个关键要点是:

  1. 库是预先编写的代码片段,可以使我们的工作轻松很多。
  2. 存在随 MicroPython 附带的标准库,以及你可以去寻找并安装到 Pico 上的外部库。
  3. 使用外部库时,你需要查找文档以弄清楚如何使用它,并且通常需要依赖该库的创建者提供良好的文档。

树莓派Pico入门:2.9:脱离电脑运行Pico 🔌

在本节课中,我们将要学习如何让树莓派Pico脱离电脑独立运行。我们将了解如何将代码存储在Pico上,以及如何通过不同的方式为Pico供电。


在Pico上存储和运行代码

上一节我们介绍了如何通过电脑为Pico编程。本节中我们来看看如何让代码存储在Pico自身并自动运行。

当Pico通过USB连接到电脑时,你可以在Thonny IDE中查看其文件系统。在Thonny中点击“视图”,然后选择“文件”,你将看到两个面板:上方显示电脑的文件,下方显示存储在Pico上的文件。

你的Pico最初可能几乎是空的。下方面板中存储的.py文件就是MicroPython代码文件,你可以像在电脑上一样打开和编辑它们,然后将文件保存回Pico,就像操作一个U盘。

如果你在Pico上保存了一个名为 main.py 的文件,那么Pico在每次上电时都会自动运行这个文件。例如,将Pico插入一个移动电源(而非电脑),如果main.py文件中包含了控制LED的代码,那么LED就会亮起。

这就是脱离电脑运行代码的方法:将所有代码写入文件,将其保存为 main.py 并存储在Pico上。当需要修改代码时,只需将Pico重新连接到电脑,在Thonny中打开main.py文件进行编辑并保存即可。


为Pico供电

现在我们已经知道如何让代码自动运行,接下来需要了解如何为Pico供电。有两种常见的方法。

1. 通过Micro USB供电

这是最简单直接的方法。

  • 将Micro USB线一端插入Pico。
  • 另一端插入任何5V的USB电源,例如手机充电器、插墙上的电源适配器或移动电源。

任何能为手机充电的5V USB电源,通常都能为Pico供电。

注意:Pico功耗极低,而许多移动电源具有省电功能,当检测到输出电流过小时会自动关闭。这可能导致Pico供电中断。并非所有移动电源都如此,如果你的项目(例如点亮一个RGB LED)功耗稍大,可能就能满足移动电源的持续供电要求。最好实际测试一下。

另一种很好的供电方式是使用专用的3.7V锂电池升压模块,它通常带有Micro USB接口,可以稳定输出约5V电压给Pico。

2. 通过VSYS引脚供电

除了USB,我们还可以通过Pico上的 VSYS 引脚直接供电。

你需要一个能提供 1.8V 至 5.5V 的电源,例如:

  • 几节AA电池
  • 太阳能电池板
  • 锂聚合物电池
  • 台式实验室电源

只要电压在1.8V至5.5V之间,将电源的负极(GND)连接到Pico的GND,正极连接到 VSYS 引脚,Pico就能正常启动。

⚠️ 极其重要的警告切勿同时连接USB电源和VSYS电源! 这很可能因为电压不匹配或电流倒灌而损坏Pico或电源设备。

因此,我们建议在使用VSYS供电时,添加一个二极管以提供额外保护。将二极管串联在电源正极和VSYS引脚之间,电流就只能单向流动(从电源流向Pico)。

以下是关键点:

  • 二极管有极性,带标记线的一端为阴极,必须连接到Pico的 VSYS 引脚。
  • 可以使用肖特基二极管或普通整流二极管,只要其正向电流能达到1A即可。
  • 如果不使用二极管,则必须极其小心,确保永远不会同时接入两种电源。例如,在用VSYS供电时想更新代码,务必先断开VSYS电源,再连接USB到电脑。


总结

本节课中我们一起学习了如何让树莓派Pico脱离电脑独立工作:

  1. 可以将代码文件存储在Pico上,命名为 main.py 的文件会在Pico上电时自动运行。
  2. Pico可以通过Micro USB接口或VSYS引脚供电。
  3. 在没有二极管保护的情况下,绝对不能同时使用这两种方式为Pico供电。

通过掌握这些知识,你的Pico项目就可以摆脱电脑的束缚,在任何地方运行了。

018:从Pico获取电源 🔌

在本节课中,我们将学习如何安全地从树莓派Pico获取电源,为连接的外部设备供电。理解电压和电流的概念至关重要,错误的连接方式很容易损坏你的Pico。

上一节我们讨论了如何为Pico本身供电,本节中我们来看看如何为连接到Pico的设备供电,即如何从Pico获取电源。为设备供电时,最重要的两个因素是电压电流。你可以将电压理解为电的“水压”,而电流则是电的“流速”。

首先,我们来介绍Pico上三个主要的电源输出点,然后分析为不同组件供电的一些场景。

Pico的电源输出点

以下是Pico上三个主要的电源来源:

  1. 3V3输出引脚:这是最简单的一个。我们在本章前面将其连接到电源轨,并一直从中获取电源。该引脚输出 3.3伏特 电压,最大可提供 300毫安 的电流。虽然不算强大,但对于大多数3.3伏设备来说已经足够。

  2. VBUS引脚:这是另一个电源来源,但比3V3更复杂。VBUS“劫持”来自USB的电源。只有当Pico通过USB连接时,VBUS引脚才有电。它能提供的电流量取决于USB连接的对象。例如,一个标有5V输出的手机充电器,VBUS最多可提供1安培电流;如果连接到一个移动电源,VBUS最多可提供2安培电流。但请注意,无论连接什么,VBUS都有一个总电流上限,大约为 2安培。VBUS在连接USB时提供 5伏特 电压,电流则取决于USB源,但连续最大电流不超过2安培。

  3. GPIO引脚本身:我们一直在使用它们。这些并非典型的电源,通常不选择用它们来供电。但当我们连接LED和电阻等元件时,技术上就是从GPIO引脚本身获取电源。我们知道可以通过数字和模拟输出提供特定电压,但每个GPIO引脚本身最多只能输出12毫安的电流。1安培等于1000毫安,所以12毫安仅为0.012安培,与VBUS能提供的电流相比微乎其微。因此,GPIO引脚只应用于驱动简单的LED、蜂鸣器等功耗极低的组件。

对于上述所有电源,需要记住的关键点是电压最大电流

供电基本原则

如果我想从这些电源之一为某个设备供电,必须确保(这是一个经验法则):电压匹配,并且设备消耗的电流小于电源能提供的最大电流

信息量很大,让我们逐一分析几个组件,看看应该从Pico的哪个位置为其获取电源。

为不同组件供电的实例分析

以下是几个常见组件及其供电方案:

  • 显示模块:这个显示模块可以插到Pico上。它有两个信号引脚(我们将在后续视频学习使用),还有一个接地(GND)和一个电源(VCC)引脚用于供电。查阅文档可知,它需要 3.3伏特 电压,因此3V3输出引脚是很好的选择,因为电压匹配。同时,文档指出其最大电流消耗为 20毫安。3V3引脚能提供高达300毫安的电流,所以我们有充足的余量,用它为显示器供电是完全安全的。

  • LED点阵:这是一个彩虹色的LED阵列。该设备可以使用 3.3伏到5伏 之间的任何电压供电。你会发现很多组件的供电电压是一个范围,而非固定值。3.3伏意味着我们同样可以使用3V3输出引脚,因为电压匹配。但是,这个LED点阵在全亮度时可能消耗高达 400到500毫安 的电流。而3V3引脚的最大输出电流是 300毫安。如果我们尝试用3V3引脚为其供电,很可能会损坏Pico。更好的选择是使用VBUS,它可以提供高达 5伏特2安培(2000毫安) 的电流。电压范围匹配,且我们未超过VBUS的最大电流。

  • 舵机:这个舵机有三根线,其中两根为舵机供电,另一根接收PWM信号来控制它。信号线不提供电力,所以我们可以将其连接到任意GPIO引脚。该舵机的工作电压范围是 4.8到6伏特。假设Pico通过USB连接,那么来自VBUS的 5伏特 电压就适合为其供电。将舵机的地线(GND)连接到Pico的GND,正极连接到VBUS即可。这个舵机最多消耗 600毫安(0.6安培) 电流。如果Pico连接的是一个1安培的USB电源,那么VBUS能提供1安培电流,我们还有0.4安培的余量。但如果我想连接第二个舵机,电压需求仍是5伏,但电流需求会翻倍,两个舵机共需 1.2安培。由于我们的USB电源只有1安培,VBUS无法提供足够的电力。要解决这个问题,我可以将Pico连接到一个更强大的USB电源上,例如这个2安培的移动电源,或者找一个能提供2安培电流的USB壁式充电器。

  • LED和电阻:我们一直通过GPIO引脚为LED和电阻供电。它们可以在 3.3伏特 下正常工作,并且按照我们的接线方式,最多消耗 5毫安 电流。GPIO引脚可以提供3.3伏特电压,以及最高 12毫安 的电流。所以我们大约有7毫安的余量,一切正常。对于LED或蜂鸣器这类组件,直接使用GPIO引脚供电是完全可以的,因为它们功耗极低。但始终值得再次确认它们消耗的电流是否小于12毫安,你肯定不想损坏Pico的引脚。

  • 电磁阀或电机:GPIO引脚不适合为这类设备供电,即使是图中这个非常小的电机。两者都可以在3.3伏下运行,但它们消耗的电流远大于12毫安。这个电磁阀消耗高达 500毫安,而这个很小的电机也消耗高达 800毫安 的电流。那么如何为它们供电呢?两者都需要5伏电压,可以像这样用VBUS愉快地供电(电机在旋转,请相信我)。然而,它只是持续旋转,我们无法控制它。问题在于,我想控制它的速度,但我们只能通过GPIO引脚用代码控制设备,而我们想控制的设备功耗对这些引脚来说太大了。这时我们该怎么办?

使用驱动板或控制板

我们需要使用驱动板控制板。这些板子允许你使用来自其他电源(如VBUS)的电力,同时也允许你将Pico的一个GPIO引脚连接到板上。这样,你就可以通过那个GPIO引脚编写代码来控制电机,但电机所需的电力则由另一个能提供足够电流的电源供应。

本课程不会展示如何使用这些驱动板,因为市面上有成千上万种选择,每种都有略微不同的设置。我们只是希望确保你了解它们的存在以及为何要使用它们。不过别担心,它们通常非常容易使用,你可以找到许多关于如何使用其中一些流行型号的教程和指南。你可能首先会为了控制电机制作一个小车或循线机器人而使用它们。

连接线与电流承载能力

视频信息量很大,但我们还有最后一点,而且非常简单:你需要确保组件之间的连接线能够支持你通过它们的电流

这是一个更大的舵机,最多可消耗约 2安培 电流。我通过USB电源将其连接到VBUS,一切正常。但是,这些跳线最多只能支持约 1安培 电流(这些线比较细,可以买到能支持2安培的粗线)。如果让这个舵机满负荷运行,持续消耗2安培电流,这些电线可能会开始发热、熔化,如果长时间(比如五分钟甚至更短)消耗这么大电流,甚至可能引发火灾。因此,我需要确保舵机的地线和正极端子使用额定电流达到2安培的导线连接。信号线则没问题,因为它是信号线,不消耗太多电力。

另一件需要注意的事情是面包板本身也有电流限制,大约也是2安培。这有点像链条中最薄弱的一环,从电源到组件,整个链条中的所有环节都必须达到该电流的额定值。

总结

本节课内容非常丰富,而且市面上有数量惊人的组件,各自需要不同的电压和电流。你通常需要查阅数据手册或网站列表来找到这些信息。如果你不确定,最好咨询懂行的人,或者在我们的网站上发帖提出与电源相关的问题,我们很乐意提供帮助。

最后,关于从Pico为组件供电,有四个关键要点:

  1. 必须确保电压匹配,并且组件消耗的电流小于电源的最大输出电流
  2. 3V3输出引脚提供 3.3伏特 电压和最高 300毫安 电流。
  3. 当Pico通过USB连接时,VBUS提供 5伏特 电压,电流取决于USB源,但硬性上限为2安培
  4. Pico的GPIO引脚仅适用于为LED等小型组件供电,因为它们最多只能提供 12毫安 的电流。

025:高级IO简介

在本节课中,我们将要学习如何使用树莓派Pico与更复杂的传感器和模块进行通信。我们将了解通信协议的基本概念,以及它们如何让微控制器轻松获取和处理数据。

我们已经学会了如何读取数字和模拟信号,这些方法非常实用。但是,如果我们想使用更高级的传感器,比如一个GPS模块,该怎么办呢?我们显然无法直接用数字或模拟输入来读取GPS坐标。这正是我们需要使用通信协议的地方。

通信协议使用二进制中的1和0(即比特)在设备之间传递信息。我们的Pico通过设置其引脚上的电压来实现这一点:0伏特代表0,3.3伏特代表1。Pico可以非常快速地切换这两种电压,从而将一串1和0的比特发送给另一个设备。这样,我们就可以将像GPS坐标这样的信息转换成1和0,通过一根导线发送给Pico,然后Pico再将这些1和0转换回有意义的GPS坐标。本质上,这是一个将数据转换为电压,再由接收设备将电压转换回数据的过程。

为了实现这种通信,两个设备必须使用相同的“二进制语言”,并且必须就连接方式和数据传输规则达成一致。这套双方共同遵守的规则系统就叫做通信协议。在本课程中,我们将介绍三种最常见的协议:UART、SPI和I²C。它们各有特点,适用于不同的应用场景,我们稍后会详细讨论。

接下来,我们将学习如何在Pico上使用模块。例如,这是一个带有板载温度传感器的大气传感器模块。你只需要连接3.3伏电源和地线为其供电,然后将另外两根线接回Pico。通过这两根线,它就能将精确的温度读数发送回Pico。

回想一下,如果我们通过Pico的模拟输入来使用一个温度传感器,我们只能得到一个电压值,然后需要查阅数据手册,并通过一些公式来计算传感器测得的电压所对应的温度。在某些情况下这样做没问题,但你也可以直接使用这样的传感器模块,它会为你完成所有工作,直接报告最终的温度值。这就是使用模块的巨大优势:它们接线简单,使用方便,并且大多数时候能为你节省大量时间。基本上,你可以为任何功能找到对应的模块。

以下是一些常见的模块示例:

  • 你或许已经见过或用过的OLED屏幕。
  • 可以连接四个舵机并通过仅两根线控制的舵机控制器。
  • 超声波距离传感器。
  • 颜色传感器。
  • 大气压力传感器。
  • 电子罗盘。

有很多不同的传感器和外设都以模块的形式存在。

最后需要说明的是,UART、SPI和I²C的世界非常深奥,可以很快变得非常复杂。这些通信协议的完整细节远远超出了本课程的范围,并且对初学者极不友好。但幸运的是,借助的帮助,在Pico上使用这些模块实际上极其简单。事实上,你99%的时间可能都会通过库来使用它们。它们的使用难度可能与基础IO一样简单,因为库在幕后处理了所有复杂的魔法,留给我们的是一个相对即插即用的模块使用体验。

本节课中我们一起学习了通信协议的基本概念,了解了它们如何使Pico能够与复杂的传感器模块进行对话。我们还看到了使用模块化传感器的便利性,并认识到库在简化这一过程中的关键作用。在接下来的章节中,我们将具体探索UART、I²C等协议的实际应用。

026:UART通信 🚀

在本节课中,我们将要学习UART通信协议。UART是一种非常常用的设备间通信方式,它能让两个设备相互“对话”,协同工作,完成单个设备无法独立完成的任务。

什么是UART?

UART,全称通用异步收发传输器,是一种最常见的设备间通信协议。它允许两个设备通过简单的连线进行数据交换。

UART的工作原理

上一节我们介绍了UART的基本概念,本节中我们来看看它是如何工作的。

要让两个设备通过UART通信,每个设备都需要两个连接:一根线用于发送数据(TX引脚),另一根线用于接收数据(RX引脚)。UART是一种全双工系统,这意味着两个设备可以同时发送和接收数据。

两个设备必须就通信速度达成一致,这个速度被称为波特率。在设置设备时,必须确保它们使用相同的波特率。常见的标准波特率有9600和115200。

硬件连接:让两个Pico“对话”

以下是连接两个树莓派Pico进行UART通信所需的步骤。

  1. 交叉连接TX和RX引脚:将一个Pico的TX引脚连接到另一个Pico的RX引脚,反之亦然。这是因为一个设备发送(TX)的数据需要被另一个设备接收(RX)。
  2. 连接共地:将一个Pico的GND(接地)引脚连接到另一个Pico的GND引脚。这确保了它们有共同的电压参考点。例如,当一个Pico发出一个3.3伏的信号时,另一个Pico能正确测量到3.3伏。

软件编程:发送端代码

我们将编写代码,让一个Pico(发送端)向另一个Pico(接收端)发送指令,以控制接收端Pico的LED灯。

首先,我们需要导入必要的库并初始化UART。

from machine import Pin, UART
import time

# 初始化UART1,设置波特率为9600,使用GP4作为TX,GP5作为RX
uart = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))

while True:
    uart.write(‘on’)   # 发送字符串“on”
    time.sleep(2)       # 等待2秒
    uart.write(‘off’)  # 发送字符串“off”
    time.sleep(2)       # 等待2秒

软件编程:接收端代码

现在,我们来编写接收端的代码。它将监听UART数据,并根据接收到的字符串控制LED灯的亮灭。

from machine import Pin, UART

# 初始化UART1,波特率必须与发送端一致
uart = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))
# 设置板载LED
led = Pin(‘LED’, Pin.OUT)

while True:
    # 检查缓冲区是否有数据
    if uart.any():
        # 读取数据并解码(UART传输的是字节串,需要解码为字符串)
        message = uart.read().decode()
        print(message)  # 在Shell中打印接收到的消息

        # 根据消息控制LED
        if message == ‘on’:
            led.value(1)
        elif message == ‘off’:
            led.value(0)

运行这两段代码后,你将看到接收端Pico的LED灯按照发送端的指令闪烁,同时在接收端的Shell中会打印出“on”和“off”信息。

数据是如何传输的?

我们刚刚实现了用文字控制另一个设备,这是如何做到的呢?其核心过程是编码与解码

当发送端使用 uart.write() 发送一个字符串(如“on”)时,这个字符串会通过 ASCII编码 规则被转换成由1和0组成的字节。一个字节由8个比特(bit)组成。然后,Pico通过快速改变TX引脚上的电压,将这些比特序列发送出去。

接收端通过RX引脚检测这些电压变化,得到比特序列,然后使用相同的ASCII规则,通过 uart.read().decode() 将字节解码回原始的字符串。

UART通道与引脚复用

树莓派Pico有两个UART硬件通道,称为UART0和UART1。每个通道一次只能连接一个设备,但可以通过多组引脚来访问同一个UART通道。

例如,UART1可以通过GP4/GP5访问,也可以通过GP8/GP9访问。它们连接的是同一个硬件。在初始化UART时,你需要指定使用哪个UART通道以及通过哪组引脚访问它。

# 使用UART0,并通过GP12(TX)和GP13(RX)访问
uart = UART(0, baudrate=9600, tx=Pin(12), rx=Pin(13))

你可以查阅引脚图来了解哪些引脚对应哪个UART通道。

UART的广泛应用

掌握了UART连接后,你可以做很多事情:

  • 连接Pico与其他微控制器(如Arduino Uno)。
  • 连接Pico与更强大的计算机(如树莓派),让计算机处理复杂计算,然后通过UART指挥Pico控制电机等外设。
    • 注意:需注意逻辑电平匹配。例如Arduino使用5V逻辑,而Pico使用3.3V逻辑,这需要一个电平转换器
  • 连接Pico与各种支持UART的模块和传感器(如GPS模块)。模块内部处理数据,Pico只需通过UART读取最终结果。

总结 🎯

本节课中我们一起学习了UART通信。让我们回顾三个关键要点:

  1. UART 是一种允许两个设备同时相互通信的协议。
  2. 它只需要两根线:TXRX,连接时需要交叉(TX接RX,RX接TX),并且必须连接共地
  3. 在MicroPython中,我们可以使用 uart.write() 发送字符串,使用 uart.read().decode() 接收字符串。在读取前,最好先用 uart.any() 检查缓冲区是否有数据。

现在,你已经掌握了让设备彼此交谈的基本技能,可以尝试将你的Pico连接到更广阔的世界了!

027:SPI通信协议

在本节课中,我们将要学习SPI通信协议。SPI比UART更复杂一些,因为它涉及四根线,并且允许多个设备通过这四根线相互通信。这是SPI的主要优势之一,但其最大的优势在于,它是本章介绍的三种协议中速度最快的一种。这使得它非常适合需要高速数据传输的设备,例如本视频中将设置的MicroSD卡读卡器。

上一节我们介绍了UART,本节中我们来看看SPI如何工作。

SPI的工作原理

SPI的多设备同时通信并非完全无序。它通过一种称为控制器-目标的架构进行有序管理。你可能也听说过主从术语,但这些术语正在被逐步淘汰,你可能会听到一些替代术语,如主-从、控制器-工作者、发起者-响应者等,它们描述的都是同一概念。

在我们的例子中,Pico将作为控制器,我们插入的任何模块或其他设备将作为目标。这意味着控制器(我们的Pico)将负责发起和管理所有设备间的通信,就像机场控制塔管理所有飞机一样。

连接SPI设备:以SD卡读卡器为例

让我们深入一个具体的例子:连接并使用这个SD卡读卡器,将Pico的一些数据记录到SD卡上。

UART的优点是我们可以仅使用标准的MicroPython库编写所有必需的代码。但对于像SD卡读卡器这样的SPI设备,你很可能需要使用一个专门的库,并且每个设备可能需要不同的库。如果你有同款SD卡读卡器,可以完全跟随我们的操作。如果你有不同的设备,则需要使用不同的库和完全不同的代码集,但你仍然可以学习本视频的重要部分:如何连接SPI设备以及SPI的工作原理。

硬件连接

与UART类似,Pico有两个可用的SPI外设:SPI0和SPI1。同时,有多个GPIO引脚可以用来访问Pico上的这些硬件。为此,你可能需要查看Pico的引脚图,因为几乎每个GPIO引脚都可以充当SPI外设所需的四根引脚之一。

在本例中,我们将使用连接到SPI1的引脚12至15。

以下是连接步骤:

1. 连接时钟线
SPI是一种同步协议,意味着所有连接在一起的设备需要同步交互。使用SPI时,Pico会产生一个称为时钟信号的数字节奏,所有设备都使用它来同步。我们需要将读卡器的串行时钟引脚连接到Pico的串行时钟引脚,即引脚14。

2. 连接两条数据线
这两条数据线就像是单向街道。一条将信息从Pico传输到目标设备,另一条则将信息从目标设备传输回Pico(控制器)。但这里我们遇到了一个命名不统一的问题。

在我的SD卡读卡器上,这些引脚标记为MOSI和MISO。这是这些引脚最常见的名称,但它们也因使用旧术语而正在被淘汰,并且目前还没有真正标准化的替代名称。你可能会发现设备上这些引脚被标记为DIN和DOUT、SDI和SDO、PICO和POCI等,会遇到很多不同的名称。

关键是要检查你的设备使用什么命名方案,并知道其中一条用于向控制器发送数据,另一条用于从控制器接收数据。不过,有一件事是一致的:我们的Pico将这些引脚称为Tx(发送)和Rx(接收)引脚,就像UART一样。

  • Pico的引脚15是发送引脚,我将把它连接到读卡器的MOSI引脚。
  • 对于使用MISO和MOSI标记的目标设备,你可以看最后一个字母来判断它是输入(I)还是输出(O)。
  • 因此,这根线从Pico的Tx(发送数据)连接到读卡器的MOSI(输入数据)引脚。
  • 然后,从读卡器的MISO引脚(输出数据),我们希望Pico接收这些数据,所以我们将它连接到Pico的引脚12,即Rx引脚。

3. 连接片选引脚
最后一个引脚是片选引脚。控制器用它来选择正在与哪个目标设备通信。因为如果我们有另一个目标设备连接在这里,并且Pico要发送一些信息,由于这两个设备都通过相同的线路连接,它们如何知道哪个设备应该接收该消息?这就是片选引脚的作用。

我们将读卡器的CS引脚连接到Pico的引脚13。当Pico发送或接收数据时,它将使用引脚13上的数字输出来告诉SD卡它应该接收该信息。你不需要担心如何编码这个,库会处理这一切。如果我们有另一个目标设备,我们需要将其CS引脚连接到一个不同的GPIO引脚,任何GPIO引脚都可以用于片选,因为它只是使用一个数字输出。

4. 供电
与任何模块一样,我们需要为其供电。这是一个5伏设备,所以我将把它连接到Pico的VBUS引脚来供电。

至此,我们完成了所有的SPI连接。

编写代码与使用库

从这里开始,每个设备的操作就不同了。我正根据这个SD卡读卡器的兼容文档页面进行操作。如果你有另一个设备,你需要去寻找一个兼容的库并了解如何使用它。

我将在这里寻找要下载的库,保存它。然后,我插入Pico,继续将该库上传到Pico。

在示例代码部分,有一些我们可以使用的示例代码。我将直接复制粘贴过来,并以此作为起点。这段代码看起来是进行模拟读取,然后连同时间戳一起存储到SD卡。我打算快速修改一下。

我们将导入random库,生成一些随机数,然后将其存储在SD卡上。你可以在上面存储任何你想要的数据。

以下是修改后的核心代码示例:

import machine
import sdcard
import uos
import random

# 初始化SPI对象,使用外设1(SPI1),并设置引脚
spi = machine.SPI(1,
                  baudrate=1000000,
                  polarity=0,
                  phase=0,
                  bits=8,
                  firstbit=machine.SPI.MSB,
                  sck=machine.Pin(14),
                  mosi=machine.Pin(15),
                  miso=machine.Pin(12))

# 初始化SD卡对象,指定片选引脚(可以是任何GPIO)
cs = machine.Pin(13, machine.Pin.OUT)
sd = sdcard.SDCard(spi, cs)

# 挂载文件系统
uos.mount(sd, '/sd')

# 打开(或创建)一个文件进行写入
with open('/sd/data.txt', 'w') as f:
    for i in range(100):  # 生成100个随机数
        random_number = random.randint(0, 100)
        f.write(str(random_number) + '\n')  # 写入数字并换行

# 卸载文件系统
uos.umount('/sd')
print("数据已写入SD卡。")

库使用中的重要事项:在上面的代码中,我们初始化了SPI对象,指定了时钟引脚、MOSI引脚和MISO引脚。我们还在这里指定了片选引脚(可以是任何其他GPIO),然后将其传递给我们的库,库会为我们完成所有设置。

我将格式化为FAT32的SD卡插入读卡器,然后运行代码。可以看到我们开始生成一些随机数。运行完成后,我快速拔出SD卡,在电脑上打开它。打开那个文件,可以看到我们随机生成的数字都在里面,并且文件名也是在这里创建的。

SPI的应用场景

现在我们知道如何将SPI设备连接到Pico了。我们可以用它做什么呢?

SPI有点小众。如果我们只想连接两个设备,可能会因为其简单性和与众多设备的兼容性而选择UART。如果我们想像下一个视频中将看到的那样连接许多设备(如多个传感器),I²C通常是更好、更容易的选择。

因此,SPI的优势在于其高速特性。SD卡读卡器希望尽可能快地传输数据,所以它们通常使用SPI。高分辨率显示器也常用SPI,OLED屏幕和TFT显示屏需要更快的速度来更新所有像素。你还会发现SPI模块用于像摄像头模块这样的设备。这个摄像头模块插在另一个SPI外设上,Pico拍摄照片然后将其传输到SD卡。

这并不是说你不能将SPI用于传感器,因为有时你可能别无选择,必须使用SPI传感器。但更常见的是,你会在这些高性能应用中找到它们。

总结

本节课中我们一起学习了SPI通信协议。以下是三个关键要点:

  1. SPI是一种更复杂的协议,可以连接多个设备,并使用控制器-目标架构。
  2. 使用SPI需要四根线:一根时钟线、两根数据传输线和一根片选线
  3. SPI比UART和I²C快得多,常用于高性能应用

028:I²C通信协议

概述

在本节课中,我们将要学习I²C通信协议。这是一种仅使用两根线就能连接大量设备的通信方式,在微控制器项目中非常常用。我们将了解其工作原理,并通过一个实际项目——连接OLED显示屏和大气传感器来构建一个小型气象站——来掌握其使用方法。


🎼 什么是I²C?

I²C,或称集成电路总线,是我们介绍的最后一个但绝非不重要的通信协议。

它只使用两根线:一根是时钟信号线(SCL),用于保持所有设备同步;另一根是数据线(SDA),用于在设备间传输数据。仅凭这两根线,你就可以连接数量惊人的设备。使用SPI时,你可能只会连接两到八个设备,通常是个位数。但I²C仅用两根线就能连接超过100个设备。虽然你可能永远不会连接那么多设备,但如果你需要连接六个左右的设备,I²C会比SPI容易得多。

与SPI类似,I²C也使用控制器和目标设备的术语。你的树莓派Pico很可能是控制器,而你插入的模块将是目标设备。

你可能会在通信协议中听到“总线”这个词,尤其是在I²C中。总线只是一个数据发送所经过的连接或路径。因此我可能会说,在我的I²C总线上,有一个Pico作为控制器连接,一些传感器作为目标设备连接。了解这个术语很有帮助,因为你可能会随机遇到它。

I²C的一个缺点是它是半双工的。这意味着在总线上,一次只能有一个设备“说话”。两个设备不能同时发送和接收数据。这有点像对讲机,一次只能一个人说话。如果两个人同时说话,他们的声音会混在一起,你什么也听不清。这只是意味着设备必须轮流相互通信。这些都由库来处理,你无需担心。而且I²C中的很多数据传输速度非常快,基本上看起来像是它们在同时交谈。


🔌 连接I²C设备:OLED显示屏

让我们通过一个演示来连接一些I²C模块。我们再次遇到了一个问题,即很多内容非常依赖具体设备,而且很多I²C设置都隐藏在库的背后。为了向你展示使用I²C设备的过程,我们将制作一个小项目:连接一些传感器和显示屏,并在屏幕上显示传感器数据。

我们将首先将这个非常通用的OLED显示屏连接到我们的Pico。这些是微小的黑白屏幕,功耗非常低,是任何项目的绝佳补充。

我们的显示屏有四个引脚:左边是电源引脚,然后是串行时钟(SCL)和串行数据(SDA)引脚,这些就是我们的I²C线。你需要将设备的SDA连接到Pico的SDA,SCL连接到Pico的SCL,非常简单。

与SPI类似,Pico有两个我们可以使用的I²C外设:I²C0和I²C1。有很多方法和不同的引脚可以用来访问它们。事实上,Pico上几乎每一个GPIO引脚都可以访问其中一个I²C外设。在这个例子中,我们将使用引脚8和9,因为我们将要使用的库需要这样。如果你的库不允许你选择引脚,你需要检查它默认使用哪些引脚。

我们将把SDA插入Pico的SDA引脚(P8),把SCL插入Pico的SCL引脚(P9)。然后我们还需要将它连接到地(GND)和3.3V输出,因为这是一个3.3V设备。就这样,连接一个I²C设备就是这么简单。

我们使用的这个OLED显示屏是一个非常通用的型号,叫做SSD1306,非常常见。首先,我们需要找到一个驱动它的库和一些示例代码。我已经提前在产品页面上找到了示例代码和GitHub上库的链接。看起来要安装这个,我们需要ssd1306.py文件。我下载了这个文件。

我们需要先插上Pico。然后我将进入我们的库文件夹,把这个文件上传到我们板子上的库中。

作为起点,我将把文档中的示例代码复制到我们的Thonny脚本中,然后运行它。我们可以看到“Hello World”打印在了我们的显示屏上。我快速浏览了一下代码:看起来我们在那里设置了库,然后导入了timePin以及I2C,我们在下面用它们来设置I²C行为。这个演示代码在这里将引脚声明为变量,然后传入,但我不太喜欢这种方式(这只是个人偏好)。我打算把它改成在一行内完成所有设置。我们可以删除它们,如果我把它复制到这里,我基本上只是直接放入数字,而不是先声明变量再传入变量。我就是喜欢这种方式,都在一行里,整洁美观。

这些OLED的工作原理是:首先我们需要用oled.fill(0)完全清空屏幕(用黑色像素填充),然后oled.text会在上面写入文本。看起来我们有一个X和Y坐标来指定在屏幕上的位置。然后当我们调用oled.show时,我们推送这个更改,用我们写的内容更新显示屏。

我们在这里要预先做一件事:我将添加一个while True循环,并把所有内容放进去。如果我运行它,我们可以看到它仍然在打印“Hello World”。


🌡️ 连接更多设备:大气传感器

接下来,我们将连接这个大气传感器模块,它可以让我们读取温度、湿度和气压。我们可以像连接OLED一样焊上一些引脚来连接它,但这个模块为我们提供了一个选项,可以使用一个方便的连接器来插入。

这个连接器为我们提供了电源引脚和I²C引脚来插入。过程完全一样。我们将把SDA插入Pico的SDA,SCL插入Pico的SCL,然后插入GND和3.3V。这样就应该能上电了。看,那里有个小LED状态灯。

现在,假设我们想再连接一个传感器。我们的面包板空间有点不够了。但这个传感器和这个传感器属于同一个Pico开发板生态系统,它们共享相同的连接器。这将允许我们像这样将它们串联在一起。

所以这个传感器连接到同一个I²C总线,这个LED模块、这个传感器和那个传感器,它们都共享同一条总线。这两个传感器都是Pico开发板生态系统的一部分,这只是我们内部制作的一系列I²C设备。如果你有像这样的Pico开发板适配器,你可以更方便一步,像这样把你的Pico放进去,然后只需将它们串联插入,像这样全部串联起来,而无需插任何线。

像Adafruit、Seeed Studio和SparkFun这样的大品牌,都有它们自己的连接器生态系统,可以像这样使用,真正方便地把所有东西组装起来。所以对于I²C设备,你可能有几种不同的接线方式,但只要它们都连接在一起,你使用哪种方式并不重要。


💻 编写传感器代码

现在让我们让这个大气传感器工作起来。基本上过程是一样的:找一个库,找一些示例代码,然后根据你的需要调整示例代码。

我已经提前到了文档页面,看到我需要下载这两个库。然后我将把它们上传到我们Pico上的库文件夹中。接着我将在这里寻找示例代码。看这个,看起来这个导入库的部分非常重要。

然后这里看起来他们从unified库导入了sleep_ms,而不是我们通常使用的time库。我认为我们不需要sleep_ms,所以先不管它。

然后我们用那个初始化了我们的传感器,这看起来非常重要。这里看起来他们为了某个我们不用的功能获取了一个初始海拔读数,所以我们将跳过它。

这里看起来是我们代码的核心部分,即实际读取传感器数据。我们将把它粘贴到我们的while True循环里面。这里看起来他们只是转换了我们的压力值然后打印出来。其余的,我认为我们不需要。回到我们的代码,我们运行它以确保一切正常,看起来没问题。

现在我们将修改它,开始打印那些数据。首先,我将把这个稍微移动一下,我只是想在这里布局一切,让它在屏幕上整齐地排列。

然后在这里我将打印我们的第一个值,即temp_c。我把它在屏幕上移动一点。在这之前,我们只是打印“Temp:”。如果我们运行它,我们可以看到我们需要先把它转换成字符串,因为这不能接受浮点数。运行它。

现在我们可以看到我们正在OLED显示屏上打印出温度。这里我们有了第一个字符串“Temp:”和第二个字符串,它只是打印出那个变量,但先转换成了字符串。其余的看起来很简单。我要做的就是复制粘贴几次。然后这个我会说“Pressure:”,这个我会说“Humidity:”。我将改变这些文本的位置,把这个向下移动几个像素,然后把这个再向下移动几个像素。哦,我差点忘了,我们实际上需要更新我们的变量。所以在这里我将打印压力值,在这里我将打印湿度值。

如果我们运行它,现在我们可以看到我们正在OLED屏幕上打印出我们的数值,我们相当轻松地完成了相当多的工作。想想看,几行复制粘贴的代码,我们就读取了一堆传感器数据并打印在OLED显示屏上。我们在这里构建了一个不错的小型气象站。

如果你想更进一步,你可以画一些线,做一个漂亮的UI设计,或者重复这个过程,添加更多I²C设备。你可以控制一些RGB LED,或者添加一个超声波传感器,或者控制一些舵机,甚至用加速度计添加运动检测,或者我不知道,甚至添加一个指南针。你可以找到大量使用I²C的东西。而且像我们在这里做的一样,将它们串联在一起真的很容易,尤其是当它们来自同一个生态系统时。


🔢 I²C地址的重要性

让我们通过讨论I²C地址来结束这个视频,因为它非常重要。我们连接到I²C总线的每一个目标设备都必须有一个唯一的标识符,称为地址。当你购买一个模块时,它会附带一个已经分配好的地址。地址只是一个十六进制格式的数字。所以你可能会看到0x1B0x250x1A,例如。我们不会深入讲解十六进制,它只是一种不同的计数方式。你只需要知道这些是设备的地址。

所以当我们的Pico在这里发送信息时,它会把信息发送给总线上的每一个设备。所以显示屏和传感器都收到了这条消息。这就是I²C真正聪明的地方:在那条消息中,它也包含了它实际想要发送消息的目标设备的地址。所以如果传感器收到一条消息,但地址是发给显示屏的,它就会忽略它。这就是我们如何能让这么多设备只用两根线进行通信,它们使用这个寻址系统来选择与谁通信。

但这意味着地址不能重复。两个设备不能共享同一个地址,否则这个系统就无法工作。现在,如果我买了另一个这样的显示屏,很可能它会和另一个显示屏有相同的地址,那样就无法工作。问题是这些地址在某种程度上是硬件绑定的,你不能只是进入软件就改变它们的地址。

不过,如果你幸运的话,你的设备背面可能会有这些焊盘。如果你拆下这个电阻并把它移到另一边,你就可以改变这个设备的地址。所以在这个设备上,你可以有两个独立的地址,并在同一个I²C总线上拥有两个设备。

如果你更幸运,你的设备可能像这个伺服驱动器一样有一些开关。这个允许我们根据开关的位置来改变地址,这比拆焊和重焊一个小电阻方便得多。

如果这些选项之后你还是不走运,你可以找一个I²C多路复用器(我们不会在本视频中介绍),它可以让你使用多个具有相同地址的设备,以防你真的陷入困境。

所有这些都引出了一个重要的事情:在你为项目购买模块之前,检查I²C地址,确保你不会有任何地址冲突,或者你至少可以改变一些设备的地址来避免冲突。


📝 总结

本节课中我们一起学习了I²C通信协议。以下是三个关键要点:

  1. I²C是一种通信协议,允许你仅用两根线连接许多设备。
  2. 当使用设备时,你总是将SCL连接到SCL,SDA连接到SDA,并且你可能需要使用一个库来与之交互。
  3. I²C总线上的每个设备都必须有一个唯一的地址,并且可能有办法改变设备的地址。

树莓派Pico初学者入门:5.1:无线连接简介

在本章中,我们将学习如何利用树莓派Pico W的无线功能,实现一些有趣的应用,例如将Pico连接到互联网,或通过Wi-Fi用电脑控制它。

到目前为止,我们所有的数据都只能通过物理有线连接进行发送和接收。但在本章中,我们将探索如何使用Pico W的无线能力来完成一些非常酷的事情。

这个微控制器和无线连接的世界可能与你预期的有所不同。我们无法在Pico上观看YouTube视频,甚至完全不可能,因为硬件本身不支持。相反,我们可以利用无线网络和互联网来发送或接收基于文本的数据。更具体的应用场景包括:用Pico制作一个智能Wi-Fi控制灯、无线邮箱传感器,或者将Pico连接到互联网以获取国际空间站的实时坐标。

这听起来可能有点难度,我们确实从初级技能过渡到了更中级的内容。但得益于库的使用,即使这些非常酷且复杂的任务,在我们的Pico上实现起来也非常简单。再次感叹,用如此精简的代码就能实现这么多功能,实在令人惊叹。

本章内容是一个大主题,我们将其拆分成了多个小视频。每个视频都建立在前一个的基础上,如同踏脚石,最终达成一个目标。我们将从将Pico连接到本地无线网络开始,然后利用互联网连接特定网站并获取有用数据。接着,我们将学习如何在Pico本身上托管一个网页,从而可以通过手机或电脑与之交互以控制Pico。最后,我们将学习如何在Pico本身上设置一个Wi-Fi接入点,用于托管那个页面。

要跟随本章学习,你只需要一块Pico W以及一个可以连接的Wi-Fi网络(可以是家庭网络,甚至是手机的热点)。

让我们开始吧。


在本节课中,我们一起学习了树莓派Pico W无线连接章节的概述,明确了我们将要探索的方向、可实现的应用以及学习路径。从下一节开始,我们将动手将Pico连接到Wi-Fi网络。

树莓派Pico初学者入门:5.2:连接互联网

在本节课中,我们将学习如何将树莓派Pico连接到本地无线网络,并通过互联网查询或连接特定网站。这并非让Pico像普通电脑一样浏览网页,而是使其能够从特定网站发送或接收基于文本的数据。

上一节我们介绍了Pico的基本编程概念,本节中我们来看看如何让它接入网络。

我们不需要额外的硬件,只需一块Pico和连接电脑的USB线。我们将从头开始编写代码,并逐步解释每个部分的作用。完整的代码可在课程页面找到。

首先,导入所需的库。我们需要 networkurequests,它们都是MicroPython标准库的一部分,同时还需要导入 time

import network
import urequests
import time

接下来,创建变量来存储Wi-Fi网络的详细信息。这可以是家庭路由器或手机热点,只要它能提供互联网接入即可。

SSID = "你的Wi-Fi名称"
PASSWORD = "你的Wi-Fi密码"

现在,创建一个用于连接互联网的函数。无线代码容易变得混乱,因此使用函数是一个好习惯。

以下是连接Pico到本地无线网络的核心代码。我们首先设置一个网络接口实例,类似于设置GPIO引脚。然后激活硬件,并使用凭证进行连接。

def connect_to_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(SSID, PASSWORD)

在Pico上使用无线功能时,使用 print 语句进行调试非常重要,因为它可能在某些步骤失败。通过打印信息,我们可以看到问题所在。

我们在连接过程中添加等待和状态提示。

    while not wlan.isconnected():
        print("正在连接,请稍候...")
        time.sleep(1)
    print("连接成功!")

连接成功后,获取Pico在无线网络中的IP地址非常有用。

    ip_address = wlan.ifconfig()[0]
    print("IP地址:", ip_address)
    return ip_address

现在,调用这个函数来测试连接。运行代码后,我们将看到连接过程的状态打印。

处理无线连接时,错误处理是一个非常有用的工具。我们将使用 tryexcept 语句来实现。try 块中的代码将被尝试执行,如果出现错误或Pico崩溃,则会运行 except 块中的代码。

try:
    ip = connect_to_wifi()
except Exception as e:
    print("连接过程中出现错误:", e)

现在,我们已经将Pico连接到本地网络,接下来让它向互联网上的一个网站发送请求。

首先,创建一个变量来存储我们想要查询的网站地址。

site_url = "http://worldtimeapi.org/api/timezone/Etc/UTC"

然后,我们让Pico向该网站发出请求,并将返回的所有信息存储在一个变量中。返回的数据通常是JSON格式,.json() 方法会将其转换为Python可读的字符串。

try:
    print(f"正在查询: {site_url}")
    response = urequests.get(site_url)
    data = response.json()
    print("收到的数据:", data)
    response.close()
except Exception as e:
    print("查询过程中出现错误:", e)
    response.close()

运行代码,我们应该能看到连接成功、查询进行以及返回的信息。我们仅用10美元的微控制器就能从世界另一端获取信息并存储为可用数据,这非常酷。

我们查询的这个网站会返回精确的日期和时间给Pico。我们可以使用字符串操作从中提取实际需要的数据。根据返回信息的结构,这可能简单或复杂。

至此,我们完成了无线章节的第一个基础步骤。仅仅从一个网站查询时间可能看起来有些刻意,但实际上,互联网上有大量网站可以为你的项目提供非常有用的信息。

然而,我们不能随意连接任何网站。它必须是能够返回某种JSON字符串的特定网站。例如,尝试连接Google可能会导致Pico崩溃。

以下是你可以查询信息的一些网站类型示例:

  • 趣味信息:随机笑话、猫猫冷知识、禅意名言。
  • 实用技术信息:国际空间站的实时位置、特定货币的全球汇率、美国地质调查局过去一小时检测到的所有地震。

课程页面将提供这些及更多网站的链接。如果你发现任何对Pico项目有用的酷网站,请告诉我们。

本节课中我们一起学习了:

  1. 使用 network 库将Pico连接到本地无线网络。
  2. 在Pico上进行无线编程时,使用 try/except 进行错误处理、使用 print 语句帮助调试问题以及使用函数是明智的做法。
  3. 只要你的无线网络能访问互联网,Pico就可以查询网站以接收有用信息。

031:在Pico上托管一个HTTP网页

在本节课中,我们将学习如何在树莓派Pico上托管一个简单的Web服务器。我们将通过两种方式实现:一是将Pico连接到现有的Wi-Fi网络,二是让Pico自身创建一个Wi-Fi接入点。这将允许我们通过手机或电脑的浏览器访问Pico托管的网页,并通过该网页与Pico进行交互。

概述与准备工作

上一节我们学习了如何将Pico连接到互联网。本节中,我们将在此基础上,让Pico成为一个能够提供网页服务的微型服务器。

首先,我们需要导入必要的库。

import network
import socket
import time
from machine import Pin

连接到现有Wi-Fi网络

我们将从连接到一个现有的无线网络开始。以下是连接函数,它与我们之前课程中使用的类似。

ssid = ‘你的Wi-Fi名称’
password = ‘你的Wi-Fi密码’

def connect():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)
    while wlan.isconnected() == False:
        print(‘正在连接…’)
        time.sleep(1)
    print(‘连接成功’)
    print(wlan.ifconfig())

在代码主体部分调用此函数,Pico将连接到指定的网络。

connect()

创建并打开Socket

Socket是Pico用来与其他客户端设备交换信息的网络接口。以下是创建和打开Socket的函数。

def open_socket():
    address = (socket.getaddrinfo(‘0.0.0.0’, 80))[0][-1]
    s = socket.socket()
    s.bind(address)
    s.listen(3)
    return s

我们在主代码中调用此函数来打开Socket。

s = open_socket()

处理客户端连接与请求

现在,我们需要处理设备连接到Pico并发送请求的过程。我们将使用一个try-except结构来处理可能出现的错误。

以下是处理连接的核心循环:

state = ‘OFF’
led = Pin(‘LED’, Pin.OUT)

while True:
    try:
        client, addr = s.accept()
        print(‘来自 %s 的连接’ % str(addr))
        request = client.recv(1024)
        request = str(request)
        print(request)
        
        # 响应头,部分浏览器需要
        client.send(‘HTTP/1.1 200 OK\n’)
        client.send(‘Content-Type: text/html\n’)
        client.send(‘Connection: close\n\n’)
        
        # 生成并发送网页
        response = webpage(state)
        client.send(response)
        client.close()
        
    except Exception as e:
        client.close()
        print(e)

生成网页HTML代码

我们需要一个函数来生成包含按钮的简单网页HTML代码。这个网页将显示LED的当前状态,并提供开关按钮。

def webpage(state):
    html = “””
    <!DOCTYPE html>
    <html>
    <head>
    <title>Pico Web Server</title>
    </head>
    <body>
    <h1>LED 控制</h1>
    <p>LED 状态: <strong>“”” + state + “””</strong></p>
    <p><a href=”/?on”><button>开</button></a></p>
    <p><a href=”/?off”><button>关</button></a></p>
    </body>
    </html>
    “””
    return html

解析请求并控制硬件

当用户点击网页按钮时,浏览器会向Pico发送一个包含/?on/?off的请求。我们需要从请求字符串中提取这个信息。

以下是解析请求并控制LED的代码:

        # 在打印request之后,发送响应之前添加
        try:
            request = request.split()[1]
        except:
            request = request
        print(‘解析后的请求:’, request)
        
        if request == ‘/?on’:
            led.on()
            state = ‘ON’
        elif request == ‘/?off’:
            led.off()
            state = ‘OFF’

request.split()方法默认按空格分割字符串,/?on/?off通常位于分割后列表的第二个元素(索引1)。

让Pico自身成为Wi-Fi接入点

除了连接现有网络,我们还可以让Pico自己创建一个Wi-Fi网络。这非常方便,尤其在没有现成网络的环境中。

以下是设置接入点的函数:

def setup_ap():
    ap = network.WLAN(network.AP_IF)
    ap.config(essid=’PicoAP’, password=’password123’) # 设置网络名和密码
    ap.active(True)
    while ap.active() == False:
        print(‘正在启动接入点…’)
        time.sleep(1)
    print(‘接入点已启动’)
    print(ap.ifconfig())

要使用此模式,只需在主代码中将connect()调用替换为setup_ap()即可。连接到此网络的设备(如手机)可以访问Pico的IP地址(通常是192.168.4.1)来打开控制网页。

总结

本节课中我们一起学习了如何在树莓派Pico上托管一个Web服务器。我们掌握了三个核心要点:

  1. 利用Socket通信:我们使用socket库在Pico上创建网络接口,实现了与客户端(如浏览器)的信息收发。
  2. 提供交互式网页:我们编写了HTML代码,让Pico能够向客户端提供包含按钮的网页,并通过解析浏览器发回的请求(如/?on)来控制Pico的硬件(如LED)。
  3. 两种网络模式:我们可以选择将Pico连接到现有Wi-Fi网络,也可以将其配置为独立的Wi-Fi接入点,两种方式都能实现网页控制功能。

这为我们构建基于Web的物联网项目奠定了重要基础。在下一节中,我们将为网页添加更高级的功能,例如滑块控件,以调节PWM信号来控制电机或灯光亮度。

032:增强网页功能 🚀

在本节课中,我们将学习如何为树莓派Pico托管的网页添加更复杂的功能。我们将探讨如何从Pico读取传感器数据并显示在网页上,以及如何通过网页向Pico发送控制指令。同时,我们也会了解如何利用大型语言模型来辅助编写网页代码。


更新网页以显示传感器数据

上一节我们构建了一个网页模板。本节中,我们来看看如何将Pico的传感器数据发送到网页上。

我们将使用Pico内置的温度传感器(ADC4通道)。首先,需要导入必要的库并设置传感器。

from machine import ADC

# 设置温度传感器(ADC4)
temperature_sensor = ADC(4)

接着,我们需要一个函数将ADC读数转换为摄氏度。以下是转换函数:

def read_temperature():
    reading = temperature_sensor.read_u16()
    voltage = reading * 3.3 / 65535
    temperature_c = 27 - (voltage - 0.706) / 0.001721
    return round(temperature_c, 2)

现在,我们需要更新两个部分:网页内容和主循环。

  1. 更新网页内容:在生成网页的web_page函数中,添加一个段落来显示温度值。
  2. 更新主循环:在while True循环中,读取当前温度,并将其作为参数传递给web_page函数。

以下是更新后的web_page函数部分代码示例:

def web_page(temperature, state):
    html = f"""
    <html>
        ...
        <p>Temperature is: {temperature} °C</p>
        ...
    </html>
    """
    return html

在主循环中,调用函数并传入温度值:

while True:
    ...
    temperature = read_temperature()
    response = web_page(temperature, led_state)
    ...

运行代码并连接到Pico的IP地址后,网页上就会显示当前的温度读数。对着Pico吹气,温度显示会下降,这证明了功能的成功。

请注意:目前温度数据只在每次与网页交互(如点击按钮或刷新页面)时才会更新,因为网页是在每次客户端请求时动态生成的。


向网页添加输入控件

了解了如何发送数据到网页后,我们来看看如何从网页接收数据。我们将添加一个滑块控件,用于控制Pico上某个引脚的PWM输出。

首先,我们需要在网页的HTML代码中添加一个滑块元素。以下是该元素的代码:

<input type="range" min="0" max="100" value="50" onchange="location.href='/slider1?'+this.value">

这个滑块的范围是0到100,默认值为50。当滑块值改变时,浏览器会向Pico发送一个形如/slider1?75的请求。

将这段代码添加到web_page函数的HTML字符串中。运行后,网页上会出现滑块。移动滑块,可以在浏览器地址栏或Pico的串口输出中看到请求的变化。


处理网页发送的请求

现在,Pico收到了来自滑块的请求,我们需要在主循环中处理它。直接为每个可能的值(0-100)写if语句是不现实的。

我们需要将请求字符串分解为两部分:

  • 路径:问号?之前的部分(例如 /slider1)。
  • 参数:问号?之后的部分(例如 75)。

以下是处理请求的代码逻辑:

while True:
    ...
    try:
        # 将请求按问号分割成路径和参数
        request_split = request.split('?')
        path = request_split[0]
        # 如果没有参数,则设为空字符串
        params = request_split[1] if len(request_split) > 1 else ''
    except Exception as e:
        path = ''
        params = ''
        print("Error parsing request:", e)

    print("Path:", path, "Params:", params)
    ...

通过这种方式,我们可以轻松地检查path是否为/slider1,然后使用params中的值。


根据滑块值控制PWM

获取到滑块值后,我们就可以用它来控制Pico的PWM输出了。首先设置PWM引脚。

from machine import Pin, PWM

# 设置PWM引脚(例如GP16)
pwm_pin = PWM(Pin(16))
pwm_pin.freq(1000) # 设置频率为1000Hz

然后,在主循环中添加处理/slider1路径的逻辑:

while True:
    ...
    if path == '/slider1':
        # 将0-100的范围映射到0-65535(Pico的PWM范围)
        duty = int((int(params) / 100) * 65535)
        pwm_pin.duty_u16(duty)
        print("Set PWM duty to:", duty)
    ...

现在,将LED连接到GP16引脚。当在网页上移动滑块时,LED的亮度会随之改变,实现了远程调光功能。

为了使代码更统一,我们也可以将之前处理按钮的代码从检查整个request改为检查path(例如path == '/on'path == '/off')。


使用LLM辅助优化网页

我们可能希望网页看起来更美观、更易用,但这需要HTML、CSS和JavaScript知识。作为MicroPython课程,我们可以利用大型语言模型来帮助我们生成这些前端代码。

例如,我们可以向ChatGPT等LLM提供当前的网页代码,并给出提示:

“这是Pico托管网页的代码。请修改HTML,使按钮更大、更适合移动设备;将‘打开’按钮设为绿色,‘关闭’按钮设为红色;放大文本和滑块;将所有内容居中。请使用内联CSS。”

LLM会生成一长段包含样式的新HTML代码。我们只需用其输出替换原有的web_page函数中的HTML字符串即可。运行后,网页的外观和体验会得到显著改善。

我们还可以请LLM帮助解决一些交互问题,例如:“滑块每次改变后都会回到中间位置,能否让它保持住设置的位置?” LLM通常会通过修改HTML元素属性(如移除或固定value属性)来提供解决方案。


项目应用与扩展

掌握了这些技能后,你可以做很多事情:

  • 远程控制小车:添加两个滑块分别控制左右电机,就能制作一个简单的网页遥控车。
  • 无线传感器监控:将Pico连接到各种传感器(如邮箱传感器、温湿度传感器),即可通过网页随时随地查看数据。
  • 智能家居控制:通过网页控制家中的灯光、插座等设备。

此外,你还可以进一步探索:

  • 物联网平台:学习使用MQTT等协议,可以将Pico的数据或控制接口发布到互联网上,实现真正的远程访问。
  • 异步更新:研究如何使用JavaScript实现网页数据的自动刷新,而无需手动点击按钮。

本节总结

本节课中我们一起学习了如何增强树莓派Pico的网页功能。

  1. 核心模板:我们拥有一个可在Pico上托管本地网页的模板,它能连接现有Wi-Fi或创建自己的接入点。
  2. 开发模式:要使用此模板添加功能,需要更新两个关键部分:web_page函数中的网页内容,以及while True循环中的请求处理与数据准备逻辑。
  3. 辅助工具:即使不熟悉前端技术,我们也可以利用大型语言模型来帮助编写和优化网页代码。

通过结合传感器数据读取、PWM输出控制以及网页交互,你已经能够为Pico项目添加强大的无线网络接口,其可能性几乎是无限的。

033:你应该知道的MicroPython工具 🧰

在本章节中,我们将学习一系列有用的MicroPython功能和概念。这些工具可能不会在每个项目中都用到,但它们是你作为创客工具箱中的重要组成部分,能在特定场景下提供巨大帮助。


有用的MicroPython功能

上一节我们介绍了本章的学习目标,本节中我们来看看一些实用的MicroPython功能。我们将通过简单的示例代码进行演示。

使用 time 模块计时

time 模块中的 ticks_ms() 函数可以测量自Pico启动以来经过的毫秒数。它为你提供了一个参考点,用于测量两个事件之间的时间间隔。

以下是使用 ticks_ms() 计时的示例:

import time
import random

start = time.ticks_ms()
time.sleep(random.random())
elapsed = time.ticks_ms() - start
print(f"休眠了 {elapsed} 毫秒")

这段代码测量了 random.random() 产生的随机休眠时间。通过计算 start 时间点和结束时间点的差值,我们就能得到事件A和事件B之间经过的精确时间。


中断:高效响应事件

在之前的课程中,我们使用循环和 sleep 来检查按钮状态,这就像每分钟去门口查看一次包裹是否送达,效率很低。中断则像等待快递员敲门,只在事件发生时才去处理。

中断允许你在事件(如按钮按下)发生时,立即暂停主程序并运行一个特定的处理函数。

以下是如何设置一个按钮中断的示例:

from machine import Pin
import time

led = Pin(25, Pin.OUT)
button = Pin(14, Pin.IN, Pin.PULL_DOWN)

def button_handler(pin):
    led.toggle()

button.irq(trigger=Pin.IRQ_RISING, handler=button_handler)

while True:
    print("Hello")
    time.sleep(2)

在这个例子中,主循环每2秒打印一次“Hello”。同时,我们为引脚14上的按钮设置了一个中断请求。当按钮被按下(引脚电平从低变高)时,它会立即调用 button_handler 函数来切换LED的状态,而无需等待主循环检查。

注意:机械按钮在按下时可能会产生多次触发(抖动)。为了解决这个问题,可以使用去抖技术,ticks_ms() 可以帮助实现这一点。


定时器中断:精确的时间控制

定时器中断是另一种形式的中断,但它不是由GPIO引脚触发,而是由时间触发。它允许你以精确的时间间隔执行代码。

以下是使用定时器中断的示例:

from machine import Pin, Timer
import time

led = Pin(25, Pin.OUT)
timer = Timer()

def toggle_led(timer):
    led.toggle()

# 创建一个周期为1000毫秒(1秒)的定时器
timer.init(period=1000, mode=Timer.PERIODIC, callback=toggle_led)

while True:
    print("Hello")
    time.sleep(2)

这段代码创建了一个周期性定时器,每1000毫秒调用一次 toggle_led 函数来切换LED。同时,主循环每2秒打印一次“Hello”。这样,两段代码可以并行运行。

定时器也可以是“一次性”的:

def five_seconds(timer):
    print("5秒已过")

timer_one_shot = Timer()
timer_one_shot.init(period=5000, mode=Timer.ONE_SHOT, callback=five_seconds)

这个一次性定时器在初始化5秒后触发一次,然后停止。定时器中断非常精确,适用于需要固定时间间隔读取传感器等场景。


Asyncio:同时运行多个循环

asyncio 是MicroPython中一个强大的库,它允许你同时运行多个 while True 循环,实现类似多任务的效果。

以下是使用 asyncio 的示例:

import asyncio
from machine import Pin
import time

led = Pin(25, Pin.OUT)

async def print_hello():
    while True:
        print("Hello")
        await asyncio.sleep(2)

async def blink_led():
    while True:
        led.toggle()
        await asyncio.sleep(1)

async def main():
    task1 = asyncio.create_task(print_hello())
    task2 = asyncio.create_task(blink_led())
    await asyncio.gather(task1, task2)

asyncio.run(main())

在这段代码中,我们定义了两个异步函数,每个都包含一个 while True 循环。asyncio.create_task() 将它们创建为任务,asyncio.gather() 让它们同时运行。关键点是使用 await asyncio.sleep() 而不是 time.sleep(),这样在休眠时不会阻塞其他任务。


PIO(可编程IO):高级硬件控制

PIO是RP2040芯片独有的一个高级功能。它拥有独立的处理器,专门用于处理输入/输出交互。其主要用途是创建自定义的通信协议。

例如,Pico本身只有2个UART外设,但如果你需要4个,就可以用PIO来模拟另外2个。对于现代创客来说,PIO最常见的应用是驱动WS2812B(NeoPixel)LED灯带,因为它们需要非常精确的时序信号。

以下是一个使用PIO以10Hz频率闪烁LED的简化示例,展示了其工作原理:

import rp2
from machine import Pin
import time

@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_10hz():
    wrap_target()
    set(pins, 1)   [31]
    nop()          [31]
    set(pins, 0)   [31]
    nop()          [31]
    wrap()

sm = rp2.StateMachine(0, blink_10hz, freq=2560, set_base=Pin(25))
sm.active(1)
time.sleep(5)
sm.active(0)

这段代码定义了一个PIO程序。set(pins, 1)set(pins, 0) 指令用于控制引脚电平。方括号 [31] 内的数字是延迟周期(不是秒)。整个循环包含256个周期。我们将状态机的频率设置为2560 Hz,因此循环每秒执行10次(2560 / 256 = 10 Hz),从而实现了10Hz的闪烁。

PIO的学习曲线较陡,但它为与特殊硬件接口或生成精确定时信号打开了新世界的大门。


扩展你的知识

MicroPython是一个不断发展的语言,总有新东西可以学习。

官方文档

MicroPython官方文档(micropython.org)是终极参考资料。它包含了本课程所涵盖的所有内容以及更多功能的详细说明、选项和示例。

例如,在 asyncio 的文档中,你可以找到 loop.stop() 这样的方法,用于立即停止异步循环。网站还提供了RP2的快速参考手册,将所有Pico相关的代码片段集中在一处。

其他语言与开发板

MicroPython并非编程Pico的唯一选择。你还可以使用C++。学会MicroPython后,再学其他语言会更容易,主要是语法和一些细节的差异。在某些情况下,比如需要极致的能效或利用特定低级功能时,C++可能是更好的选择。

此外,除了标准的树莓派Pico,还有许多其他基于RP2040芯片的开发板,它们也可以用MicroPython编程。例如,带有以太网口的Pico、集成圆形显示屏的“智能手表”板,或者只有面包屑夹大小的微型板。了解这些选项有助于你为特定项目选择最合适的硬件。


总结

本节课中我们一起学习了多个有用的MicroPython工具和概念:使用 ticks_ms() 进行精确计时、利用中断高效响应外部事件、通过定时器中断执行周期性任务、使用 asyncio 实现多任务并发、以及了解了高级的PIO硬件控制功能。我们还探讨了如何通过官方文档持续学习,并知道了除了MicroPython和标准Pico之外的其他可能性。

你不需要立即掌握所有这些内容,但请将它们视为你创客工具箱中的备用工具,在未来的项目中,它们可能会派上大用场。

树莓派Pico入门教程:6.2:未来之路

在本节课中,我们将对课程进行总结,并探讨在掌握基础技能后,如何继续你的创造者之旅。

我们已经抵达课程的终点。如果你已经完成了本课程的学习,那么恭喜你。互联网上充满了像这样的免费在线课程和教育资源,但真正坚持学习并完成的人并不多。所以,再次恭喜你。

那么,完成课程后,我们该何去何从?我想再次强调,本课程只是为你提供了一系列可以放入“创客工具箱”的工具和技能。如何使用它们,完全取决于你。所以,走出去,动手做点什么。

换句话说,开始一个项目,开始制作一些东西,开始制作有用的东西,或者仅仅是开始动手制作。你现在已经具备了相应的技能。

  • 想制作一个邮箱监控系统吗?你已经掌握了使用按钮或某种传感器,并通过无线方式报告信息的技能。
  • 想制作一个机械臂吗?你知道如何使用伺服电机和逻辑来控制它。
  • 或者制作一个气象站?你可以连接一些I²C或UART设备,将数据显示在OLED屏幕上,或者再次通过无线方式传输数据。

现在,你已经掌握了进行一些真正令人惊叹的创作所需的技能。在整个课程中,你可能已经产生了一些想要制作的东西的想法。走出去,把它们实现出来。

如果你需要一些灵感,我们有一个核心社区项目页面,以及涵盖各种主题和创意的完整指南,旨在帮助你激发创作灵感。课程页面中提供了这些资源的链接。

进行这类项目实践非常重要,因为从此刻起,仅仅观看课程视频会变得越来越难以深入学习。你将通过实践来学习大部分MicroPython和Pico相关的技能。你会学到那些在课堂环境中无法传授的细微之处。

另外需要记住的是,我们只学习了一套特定的创客技能。在你的创客工具箱中,还有其他非常有价值的工具。例如,如果你学习如何使用CAD进行3D建模和3D打印,你就可以为你的项目制作外壳、支架和定制零件。

如果你对此感兴趣,我们也有一个完整的速成课程,专门介绍这些基础的创客技能,以充实你的工具箱。同样,课程页面中提供了链接。

还有一件事,如果你在任何时候遇到困难或需要指导,请访问我们的社区论坛。那里有一个非常棒的社区,成员们正在进行各种各样奇妙而精彩的项目,他们非常乐于帮助他人解决问题、提供指导。无论你接下来打算做什么,我们Core Electronics团队都祝贺你完成了本课程,并祝你在未来的探索中一切顺利。

现在,走出去,创造点什么吧。

posted @ 2026-03-29 09:43  布客飞龙I  阅读(152)  评论(0)    收藏  举报