MicroPython基础
2024-01-03 07:56:53

MicroPython基础入门

MicroPython官网:https://micropython.org/

MicroPython文档:https://docs.micropython.org/en/latest/

MicroPython固件下载:https://micropython.org/download/?port=esp8266

Thonny官网:https://thonny.org/

01 MicroPython简介

MicroPython是Python 3编程语言的一个完整软件实现,采用C语言编写,可以在微控制器上运行。
因此,在学习MicroPython,我们需要准备一个单片机,本课程用到的是ESP32单片机。
MicroPython是一个完全的Python编译器和运行时系统,专为微控制器硬件而设计。它提供了一个交互式提示符(REPL),可立即执行支持的命令。除了核心Python库,MicroPython还包括用于访问低层硬件的模块。

MicroPython使用C99编写,并以非常自由的MIT许可证发布,可供一般用户使用。大多数库和扩展模块(包括第三方提供的一些)也可以在MIT或类似许可证下使用。因此,您可以自由地在个人用途、教育和商业产品中使用和修改MicroPython。

一句话说就是:MicroPython是跑在微控制器上的Python,内置了Python解析器。我们只需要给微控制器发送运行py文件或py命令,就能微控制器运行相应的功能了。

MicroPython与C语言在单片机有什么不一样?
我们分别用两种语言来完成同一件事情来进行对比:

C语言:
使用C语言,需要经历几个过程:
1、编写源代码

1
2
3
4
5
6
#include <stdio.h>

int main(int argc, void* argv)
{
printf("Hello world!")
}

2、编译链接代码
3、下载到开发板
4、运行调试

MicroPython:
我们只需要将MicroPython的固件烧录到单片机上面,然后用USB数据线连接单片机。
由于MicroPython提供了交互式提示符(REPL):
然后在终端提示符窗口输入:

1
print("Hello world!")

即可完成相同的事情。

02 驱动安装

CH9102是一个USB总线的转接芯片,实现USB转高速异步串口。

小知识:CP2102和CH340是什么?CP2102和CH340有什么不同?
CP2102和CH340都是USB转串口芯片,通常用于计算机与外设之间的数据传输。
CP2102:3.3V供电,集成度高,使用方便,但字符发送是严格连续的,易导致误码。
CH340:5V供电,成本低,但数据传输速度慢,容易出现误码。

1688980830872

03 开发环境搭建

Thonny IDE 是一款面向初学者的 Python 集成开发环境。
Thonny 是由爱沙尼亚的塔尔图大学开发,它是基于 Python 内置的图形库 tkinter 开发出来的可视化工具,能帮助初学者搞懂每一行代码的运行细节,它帮小白解决了一些繁杂的环境问题。

1686109935109

04 烧录MicroPython到单片机

本节目标:将MicroPython烧录到ESP32单片机
选择配置解析器:

1688989205451

选择 安装或更新MicroPython(esptool)

1688989190380

按下方进行选择后点击 install,即可开始烧录。

1688989065842

容易出现的bug:

解决No module named ‘serial’,模块不存在的问题

1688982580306

解决方法:

1
python -m pip install pyserial

05 开发板简介

ESP32是一款由乐鑫公司设计的独立系统MCU,可通过SPI/SDIO或I2C/UART接口提供Wi-Fi和蓝牙功能。

它具备低功耗性能、强大的处理能力和Wi-Fi & 蓝牙功能。

ESP32具备高度集成的自校准电路,可消除外部电路的缺陷并适应外部条件的变化。此外,ESP32还具备极其稳定的性能,工作温度范围达到-40°C到+125°C

电路板实物图

引脚介绍:

在上图中,我们可以看到单片机上的引脚处都标记了D1、D2、D3这样的名称。但还有一种叫法叫GPIO(General-purpose input/output)引脚。中文翻译过来就是:通用输入/输出。

我们可以通过程序来控制这些引脚的电位,让其设置成 高电位 或者 低电位

那什么是高电位?什么是低电位?

简易的理解为通电(大于2.5伏特)、断电(小于0.5伏特)即可。

GPIO(General Purpose I/O Ports)通用输入/输出端口,是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态。

PWM(Pulse Width Modulation)脉冲宽度调制,是一种模拟控制方式,根据相应载荷的变化来调制晶体管基极或MOS管栅极的偏置,来实现晶体管或MOS管导通时间的改变,从而实现开关稳压电源输出的改变。

ADC(Analog-to-Digital Converter)模拟数字转换器,或简称ADC,通常是指一个将模拟信号转变为数字信号的电子元件。

晶振与时钟频率的关系是:一个时钟周期等于两个振荡周期,所以晶振频率等于2倍时钟频率。

晶振是一种控制频率元件,在电路模块中提供频率脉冲信号源,给电路提供一定频率的稳定的震荡(脉冲)信号。时钟频率就像发动机转速,以一定频率在转,频率越高,对信号质量要求越高,能耗越大。

1686575584649

1686575717072

接下来,我们将通过实验来逐步为大家解说关于单片机和MicroPython的知识点:

做实验前需要准备的条件:

条件:
1、一个ESP32的单片机
2、一台电脑,并且确保您已经安装好MicroPython环境并成功连接到ESP32开发板。
3、一颗好奇心

您可以使用MicroPython的REPL(Read-Evaluate-Print Loop)或者通过一个代码编辑器来编写和上传代码。

实验一:点灯仪式 点亮LED

这是一个极具意义的特别实验,也就是”单片机点灯仪式”!这个实验对我们来说很重要,因为它代表了我们踏入单片机世界的第一步,了解它的意义。

在这个实验里,我们只需要用到一个单片机开发板来点亮内置的一颗LED灯。虽然看起来简单,但这两个小东西将带领我们进入一个全新的领域,让我们亲身体验硬件和软件的结合,掌握嵌入式系统的基本原理。

这个点灯仪式的目标是验证我们对硬件连接和程序编写的理解是否正确。我们要通过控制单片机的输出引脚来点亮这个LED灯。虽然简单,但它代表了我们作为工程师的初心和责任:把抽象的概念变成真实的技术,创造出能改变世界的创新解决方案。

这个仪式不仅仅是个普通的实验,更是一次内心的火焰和探险。在我们的努力和思考中,我们将探索电子元件之间的关系,理解信号的传递,学习如何用编程语言来控制硬件。在这个过程中,我们会遇到挑战和困惑,但同时也会感受到解决问题的乐趣和成就感。

通过这个点灯仪式,我们将进入一个广阔的世界,那里有无尽的可能性等待我们去探索。我们可以创造智能家居系统,设计自动化机器人,甚至开发医疗设备来拯救生命。所有这些都离不开对单片机技术的理解和应用。

最后,我希望每个参加点灯仪式的小伙伴都全情投入,珍惜这个学习的机会。让我们勇敢地迈出第一步,点亮这颗LED灯,开启我们作为未来工程师的旅程。

此过程需要用到Thonny IDE,所以需要确保第四步已经安装完成:

打开Thonny IDE后,在右下方视图中,可以看到MicroPython(ESP32)-COM10, 此处的COM10与设备管理器中的一致。

1688984010586

1688983871400

您可以按照以下步骤来点亮LED:

  1. 新建文件

1688984036344

  1. 编写代码:

    • 在MicroPython中,可以通过引用”machine”模块来访问GPIO引脚的控制功能。请确保在代码的开头添加以下行来导入模块:

      1
      from machine import Pin
    • 接下来,您需要初始化GPIO引脚作为输出引脚。在此示例中,我们将使用GPIO Pin 2,您可以根据实际连接的引脚进行调整。添加以下行来初始化引脚:

      1
      led_pin = Pin(33, Pin.OUT)
    • 最后,您可以使用以下代码来点亮LED:

      1
      led_pin.value(0)  # 将引脚输出设置为高电平(3.3V),LED点亮

      如果要将LED熄灭,您可以使用以下代码:

      1
      led_pin.value(1)  # 将引脚输出设置为低电平(0V),LED熄灭
  2. 上传和运行代码:

    • 将编写好的代码上传到ESP32开发板上。您可以使用MicroPython的REPL,通过串口或无线连接来上传代码。
    • 运行代码后,您应该能够看到LED点亮。

完整代码:

1
2
3
4
from machine import Pin

led = Pin(33, Pin.OUT)
led.value(0)

实验二:闪烁LED

本实验跟上方的实验是基本类似,不同点是这次增加了一个定时器。

使用ESP32单片机和MicroPython编写代码时,您可以按照以下步骤来点亮LED:

  1. 编写代码:

    • 在MicroPython中,可以通过导入”machine”模块来访问GPIO引脚的控制功能,导入time来控制睡眠时间。请确保在代码的开头添加以下行来导入模块:

      1
      2
      from machine import Pin
      import time
    • 接下来,您还需要初始化GPIO引脚作为输出引脚。在此示例中,我们将使用GPIO Pin 2,您可以根据实际连接的引脚进行调整。添加以下行来初始化引脚:

      1
      led_pin = Pin(33, Pin.OUT)
    • 可以看到下方的循环语句,循环了10遍

      1
      2
      3
      4
      5
      for i in range(10):
      led.value(1)
      time.sleep(0.5)
      led.value(0)
      time.sleep(0.5)
    • 上传和运行代码:

  • 将编写好的代码上传到ESP32开发板上。您可以使用MicroPython的REPL,通过串口或无线连接来上传代码。
    • 运行代码后,您应该能够看到LED交替闪烁。

最终代码如下:

1
2
3
4
5
6
7
8
9
10
from machine import Pin
import time

led = Pin(33, Pin.OUT)

for i in range(10):
led.value(1)
time.sleep(0.5)
led.value(0)
time.sleep(0.5)

实验三:按钮控制LED

经过上面两个实验,相信已经对单片机的控制有一定了解。

接下来,我们将通过使用单片机上面的按钮去控制LED的亮灭。

  1. 编写代码:

    • 在MicroPython中,可以通过导入”machine”模块来访问GPIO引脚的控制功能,导入time来控制睡眠时间。请确保在代码的开头添加以下行来导入模块:

      1
      2
      from machine import Pin
      import time
    • 接下来,您还需要初始化GPIO引脚作为输出引脚。在此示例中,我们将使用GPIO Pin 35设置为输入,GPIO Pin 35设置为输出,您可以根据实际连接的引脚进行调整。添加以下行来初始化引脚:

      1
      2
      button = Pin(35, Pin.IN)
      led = Pin(33, Pin.OUT)
    • 可以看到下方的循环语句,进行了无限循环。

      • 当检查到按钮被按下后led(也就是33引脚)的引脚将设置为低电位
      • 而按钮释放的时候,led的引脚将被设置为高电位
      1
      2
      3
      4
      5
      6
      7
      8
      while True:
      time.sleep(0.1)
      if button.value() == 0:
      print("按钮被按下了")
      led.value(0)
      else:
      print("按钮被释放了")
      led.value(1)
    • 上传和运行代码:

  • 将编写好的代码上传到ESP32开发板上。您可以使用MicroPython的REPL,通过串口或无线连接来上传代码。
    • 运行代码后,您应该能够看到LED会随着按钮的按下释放,而亮起和熄灭。

以下是完整的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from machine import Pin
import time

button = Pin(35, Pin.IN)
led = Pin(33, Pin.OUT)


while True:
time.sleep(0.1)
if button.value() == 0:
print("按钮被按下了")
led.value(0)
else:
print("按钮被释放了")
led.value(1)


实验四:连接WIFI

使用ESP32连接到Wi-Fi网络时,它可以利用无线通信功能与其他设备进行交流和数据传输。您可以使用ESP32与智能手机、电脑或服务器等设备进行无线连接,方便地共享信息和控制远程设备。

另外,Wi-Fi连接还使ESP32能够访问互联网。您可以通过ESP32与在线资源进行交互,例如下载实时天气数据、获取新闻信息,甚至上传数据到云端服务器进行处理和分析。

通过Wi-Fi连接,您可以远程控制ESP32及其连接的设备。借助智能手机应用或网页界面,您可以方便地监控和操控智能家居设备、机器人等,从任何地方实现远程控制。

所以,ESP32连接到Wi-Fi网络的意义是不仅可以实现无线通信和上网功能,还可以方便地进行远程控制。这使得它成为物联网应用和远程控制系统中不可或缺的关键组件。

ESP32的network.WLAN模块具有以下几种模式:

模式 说明
Station模式(STA) 在此模式下,ESP32作为一个客户端连接到一个现有的Wi-Fi网络,以获取互联网访问和与其他设备通信。
Access Point模式(AP) 在此模式下,ESP32可以作为一个独立的Wi-Fi接入点,允许其他设备连接到它,并通过ESP32提供的网络访问互联网。
Station + Access Point模式(STA+AP) 在此模式下,ESP32同时充当客户端和接入点。它可以连接到一个Wi-Fi网络并提供另一个独立的Wi-Fi网络,允许其他设备连接到它并与其通信。
Promiscuous模式 在此模式下,ESP32可以监控Wi-Fi网络中的所有数据包,而不仅仅是与自己相关的数据包。这使得它可以用于网络分析、数据包捕获和监视等应用。

除了这些常见的模式外,ESP32还支持其他高级模式和配置选项,如Mesh网络模式、混合模式等,以满足更复杂的网络需求。但常用的模式是Station(STA)和Access Point(AP),而Station + Access Point(STA+AP)模式也经常被使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import network
import time

wlan1 = network.WLAN(network.STA_IF) # 设置WIFI模式
wlan1.active(True) # 激活
print("scan result:", wlan1.scan()) # 扫描接入点

print("isconnected:", wlan1.isconnected()) # 检查站点是否连接到 AP

# connect的第一个参数是WIFI名称,第二个参数是WIFI密码
wlan1.connect('hello', '12345678') # 连接到 AP
time.sleep(3)
print("MAC:", wlan1.config('mac')) # 获取接口的MAC地址
print("IP config information:", wlan1.ifconfig()) # 获取接口的 IP/网络掩码/gw/DNS 地址

启动热点

1
2
3
4
5
6
import network

ap = network.WLAN(network.AP_IF) # 创建接入点接口
ap.config(ssid='ESP-AP') # 设置接入点的 SSID
ap.config(max_clients=10) # 设置可以连接到网络的客户端数量
ap.active(True) # 激活

实验五:连接WIFI并且通过WEB点亮LED

在MicroPython中,boot.py是一份启动文件,它会随着单片机的启动而启动。

1
2
3
4
5
import common.Wifi as Wifi            # 导入自己封装的Wifi模块
import common.WebServer as WebServer # 导入自己封装的网页服务模块

Wifi.connection('goodboy', '123456789')
WebServer.start()

下方的代码跟实验四的代码进一步封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
# /common/Wifi.py
import network

def connection(ssid, password):
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password)

while station.isconnected() == False:
pass

print('Connection successful')
print(station.ifconfig())

下方的代码封装Web服务

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
# /common/WebServer.py

try:
import usocket as socket
except:
import socket

from machine import Pin

gpio27_state = "OFF"
led = Pin(33, Pin.OUT)

def web_page():
global gpio27_state
if led.value() == 1:
gpio27_state = "OFF"
else:
gpio27_state = "ON"

html = """<html><head> <title>ESP Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #4CAF50; border: none;
border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
.button2{background-color: #555555;}</style></head><body> <h1>ESP32-ESP8266 WebServer</h1>
<p>GPIO27 state: <strong>""" + gpio27_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
<p><a href="/?led=off"><button class="button button2">OFF</button></a></p>
</br> </body></html>"""
return html

def start():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)

while True:
conn, addr = s.accept()
print('Got a connection from %s' % str(addr))
request = conn.recv(1024)
request = str(request)
print('Content = %s' % request)
led_on = request.find('/?led=on')
led_off = request.find('/?led=off')

if led_on == 6:
print(led_on)
print('LED ON')
led.value(0)
if led_off == 6:
print('LED OFF')
led.value(1)

response = web_page()
conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.sendall(response)
conn.close()

什么是PWM? 脉冲宽度调制(PWM)

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。

PWM的频率:
频率指的是在1秒钟内,PWM信号从高电平到低电平再回到高电平的次数。它用赫兹(Hz)作为单位表示。常见的PWM频率有50Hz和100Hz。例如,如果频率为50Hz,则信号每秒钟会经历50次从高到低再到高的过程。

PWM的周期:
周期是指一个完整的PWM信号波形所需要的时间。它可以通过频率计算得出,即周期等于1除以频率。
即:T=1/f (周期=1/频率)。
以50Hz为例,一个周期的时间将是20毫秒(ms)。这意味着在每个20毫秒的周期内,PWM信号会完成一次从高电平到低电平再回到高电平的过程。

占空比:
占空比是指在一个PWM周期内,高电平所占的时间与整个周期时间的比例。它以百分比(%)表示。

例如,如果占空比为20%,则表示高电平的时间占整个周期时间的20%。占空比范围从0%(完全低电平)到100%(完全高电平)。占空比决定了PWM信号的平均功率输出。

在这里插入图片描述

周期: 一个脉冲信号的时间 (1s内测周期次数等于频率)
脉宽时间: 高电平时间

上图中 脉宽时间占总周期时间的比例,就是占空比

比方说周期的时间是10ms,脉宽时间是8ms 那么低电平时间就是2ms 总的占空比 8/(8+2)= 80%

这就是占空比为80%的脉冲信号

而我们知道PWM就是脉冲宽度调制 通过调节占空比,就可以调节脉冲宽度(脉宽时间) 。
而频率 就是单位时间内脉冲信号的次数,频率越大。
以20Hz占空比为80% 举例 就是1秒钟之内输出了20次脉冲信号 每次的高电平时间为40ms

在这里插入图片描述

上图中,周期为T,T1为高电平时间,T2 为低电平时间

假设周期T为1秒,即频率为1Hz,高电平时间为0.5秒,低电平时间也为0.5秒。这样总的占空比就是0.5/1,即50%。

PWM的原理是,在单片机等设备中,输出的是数字信号,只能表示高电平和低电平。为了输出不同的模拟电压信号,可以使用PWM技术,通过改变输出方波的占空比来模拟模拟电压。

假设高电平为5V,低电平为0V,通过PWM调节占空比,可以获得模拟的任意电压值(范围在0~5V之间),连接表示直流供电输出,断开表示直流供电断开。通过控制连接和断开的时间,可以理论上输出任意不大于最大电压值的模拟电压。

例如,占空比为50%,即高电平和低电平时间相等,在一定的频率下,可以得到模拟的2.5V输出电压。而75%的占空比则对应3.75V的输出电压。

PWM的调节作用来自于对”占周期”宽度的控制。当”占周期”变宽时,输出能量提高,平均电压值也会增加。而当”占周期”变窄时,输出电压信号的平均电压值降低。

因此,在一定的频率下,通过调节不同的占空比,可以得到不同的输出模拟电压。

PWM利用这种原理实现了数字到模拟(D/A)转换。

总结起来,PWM通过在适当的信号频率下,通过改变周期内的占空比,从而改变输出的有效电压。

实验六:蜂鸣器播放音乐

蜂鸣器是一种能够产生声音的电子元件。它通常由一个压电陶瓷或电磁线圈和一个振动膜组成。蜂鸣器可以被用作警报器、指示器或音频输出设备。

蜂鸣器的工作原理基于压电效应或电磁感应。在压电蜂鸣器中,当施加电压时,压电陶瓷会发生形变,导致振动,从而产生声音。电磁蜂鸣器则利用电磁线圈在电流通过时产生磁场,使得振动膜发生运动,进而产生声音。

蜂鸣器通常通过连接到电子设备或电路的输出引脚来控制。通过在一定的频率下激活蜂鸣器,可以产生特定的音调或声音信号。蜂鸣器的声音可以是单一的持续音,也可以是一系列短促的脉冲音。

蜂鸣器在各种应用中广泛使用,例如作为电子设备的警报器、时钟的报时器、电子游戏的音效装置等。它们还常用于嵌入式系统中,用于提供音频反馈或警示信号。

有源蜂鸣器和无源蜂鸣器的区别:

  1. 有源蜂鸣器(Active Buzzer): 有源蜂鸣器是一种内部集成了振荡器的蜂鸣器。它通常由一个振荡器和一个电路驱动器组成。当给有源蜂鸣器供电时,内部振荡器会产生特定频率的振荡信号,并由电路驱动器放大和驱动,使得蜂鸣器发出声音。有源蜂鸣器可以直接连接到电源和控制信号引脚上,无需外部驱动电路。
  2. 无源蜂鸣器(Passive Buzzer): 无源蜂鸣器是一种没有内部振荡器的蜂鸣器。它是一个简单的声音发生器,通常由一个振动膜和一个共振腔组成。当在无源蜂鸣器的振动膜上施加电流时,振动膜开始振动,产生声音。由于无源蜂鸣器没有内部振荡器,因此需要外部的电路或信号源来提供振荡信号。
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
from machine import Pin, PWM
import time

# 定义常量
BUZZER = 25

# 创建音乐旋律列表,超级玛丽
melody = [330, 330, 330, 262, 330, 392, 196, 262, 196, 165, 220, 247, 233, 220, 196, 330, 392, 440, 349, 392, 330, 262, 294, 247, 262, 196, 165, 220, 247, 233, 220, 196, 330, 392, 440, 349, 392, 330, 262, 294, 247, 392, 370, 330, 311, 330, 208, 220, 262, 220, 262, 294, 392, 370, 330, 311, 330, 523, 523, 523, 392, 370, 330, 311, 330, 208, 220, 262, 220, 262, 294, 311, 294, 262, 262, 262, 262, 262, 294, 330, 262, 220, 196, 262, 262, 262, 262, 294, 330, 262, 262, 262, 262, 294, 330, 262, 220, 196]

# 创建音调持续时间列表
noteDurations = [8, 4, 4, 8, 4, 2, 2, 3, 3, 3, 4, 4, 8, 4, 8, 8, 8, 4, 8, 4, 3, 8, 8, 3, 3, 3, 3, 4, 4, 8, 4, 8, 8, 8, 4, 8, 4, 3, 8, 8, 2, 8, 8, 8, 4, 4, 8, 8, 4, 8, 8, 3, 8, 8, 8, 4, 4, 4, 8, 2, 8, 8, 8, 4, 4, 8, 8, 4, 8, 8, 3, 3, 3, 1, 8, 4, 4, 8, 4, 8, 4, 8, 2, 8, 4, 4, 8, 4, 1, 8, 4, 4, 8, 4, 8, 4, 8, 2]

# 初始化引脚
buzzer = PWM(Pin(BUZZER))

# 主循环
while True:
for i in range(len(noteDurations)):
# 设置PWM频率
buzzer.freq(melody[i])

# 设置PWM占空比
buzzer.duty(512)

# 节奏间隙
time.sleep((1000 / noteDurations[i]) * 1.0 / 1000)

# 停止播放
buzzer.duty(0)

# 曲子放完,则停止2秒
time.sleep(2)

实验七:驱动舵机

舵机是一种位置伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。其工作原理是:控制信号由接收机的通道进入信号调制芯片,获得直流偏置电压。它内部有一个基准电路,产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。最后,电压差的正负输出到电机驱动芯片决定电机的正反转。

1691636051395

img

img

脉冲宽度与旋转角度对照表

时间 旋转角度
0.5ms 0度
1.0ms 45度
1.5ms 90度
2.0ms 135度
2.5ms 180度

为让理解舵机的原理,我们先用以下这段代码是使用MicroPython编写的控制舵机(servo)的程序。

首先,导入了Pintime模块,它们是MicroPython中用于控制IO引脚和进行时间延迟的模块。

1
2
from machine import Pin
import time

servopin = Pin(13, Pin.OUT)创建了一个输出引脚对象,用于连接舵机。在这里,引脚号13(可以根据具体硬件调整)被指定为舵机的控制引脚。

1
servopin = Pin(13, Pin.OUT)

servopulse(angle)是一个自定义的函数,用于控制舵机的脉冲信号。它接受一个参数angle,表示舵机的角度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def servopulse(angle):
global servopin
# 在函数内部,根据舵机的角度计算了脉冲宽度`pulsewidth`。具体计算方式是将角度乘以11,并加上500,以得到脉冲宽度的微秒数。
# (2480 - 500) / 180 = 11, 2480指的是舵机接受的最大脉冲宽度,500是舵机接受的最小脉冲宽度,除以180
pulsewidth = (angle * 11) + 500

# 将舵机的控制引脚设置为高电平,启动脉冲信号。
# 使用`time.sleep_us(pulsewidth)`进行微秒级的延迟,延迟时间等于脉冲宽度。
servopin.value(1)
time.sleep_us(pulsewidth)

# 接下来,`servopin.value(0)`将舵机的控制引脚设置为低电平,结束脉冲信号。
# 再次使用`time.sleep_us(20000 - pulsewidth)`进行延迟,延迟时间等于周期时间(20毫秒)减去脉冲宽度。
servopin.value(0)
time.sleep_us(20000 - pulsewidth)

最后,通过一个循环调用servopulse(90)来控制舵机转动到90度的位置。循环运行了100次,即让舵机重复执行转到90度的动作。

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from machine import Pin
import time

servopin = Pin(13, Pin.OUT)

def servopulse(angle):
global servopin
pulsewidth = (angle * 11) + 500
servopin.value(1)
time.sleep_us(pulsewidth)
servopin.value(0)
time.sleep_us(20000 - pulsewidth)

for i in range(100):
servopulse(90)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from machine import Pin, PWM
import time

# 绑定一个引脚,并启用PWM,频率为50Hz
pwm0 = PWM(Pin(13, Pin.OUT), freq=50)

def turn(angle):
value = int(500 + (angle / 180) * (2500 - 500))
pwm0.duty_ns(value * 1000)

for i in range(1000):
turn(0)
time.sleep(1.5)
turn(180)
time.sleep(1.5)

实验八:光耦传感器

光耦传感器(Optocoupler)是一种电子器件,用于隔离和传输电信号。它通常由一个发光二极管(LED)和一个光敏元件(如光敏三极管或光敏二极管)组成。光耦传感器在输入端和输出端之间使用光信号进行隔离,从而实现电气隔离。

工作原理如下:当输入端的电流通过LED时,LED会发出光。这些光会被传输到光敏元件,根据光的强弱,光敏元件的电阻或电压会发生相应的变化。这个变化可以被连接在输出端的电路检测到,并作为输出信号进行处理。

光耦传感器的隔离性能使得它在电气隔离、信号转换和传输方面有广泛的应用。它可以用于电路隔离、噪声滤波、电位器调节、电压测量、逻辑电平转换等应用场景。此外,光耦传感器还具有高速响应、低功耗和可靠性高等特点。

光耦传感器的具体型号和规格有很多种类,常见的有光电耦合器(Photocoupler)和光隔离器(Optoisolator)。不同型号的光耦传感器在电气参数、封装形式和特性等方面可能有所差异,因此在使用光耦传感器时需要参考其具体的规格和技术资料。

1
2
3
4
5
6
7
8
9
10
11
12
13
from machine import Pin

# 定义连接光耦输出的引脚号
optical_coupler_pin = 23 # 替换为正确的引脚号

# 创建一个Pin对象,使用指定的引脚号,并将其设置为输入模式
optical_coupler = Pin(optical_coupler_pin, Pin.IN)

# 读取光耦的状态
coupler_state = optical_coupler.value()

# 打印光耦的状态
print("光耦状态:", coupler_state)

当有东西阻挡的时候输出1,当无东西阻挡的时候输出0

实验九:无刷电机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 电机
class Motor:
def __init__(self, forward_pin, backward_pin, freq):
# 配置 PWM 引脚,并设置频率
self.forward_pin = PWM(Pin(forward_pin, Pin.OUT), freq = freq)
# 配置 PWM 引脚,并设置频率
self.backward_pin = PWM(Pin(backward_pin, Pin.OUT), freq = freq)

# 速度 [0 - 1023],但太少带不动
def forward(self, speed):
self.forward_pin.duty(speed)
self.backward_pin.duty(0)

def backward(self, speed):
self.forward_pin.duty(0)
self.backward_pin.duty(speed)

def stop(self):
self.forward_pin.duty(0)
self.backward_pin.duty(0)
1
2
3
4
motor = Motor(26, 27, 1000)
motor.forward() # 正转
motor.backward() # 反转
motor.stop() # 停止