基础实验

MicroPython 更强调的是针对应用的学习,强大的底层库函数让我们可以直接关心功能的实现,也就是说我们只要理解和熟练相关的函数用法,就可以很好

的玩转 MicroPython。它让我们可以做到不关心硬件和底层原理(当然有兴趣和能力的小伙伴可以深入研究)而直接跑起硬件。

点亮第一个 LED

实验讲解:

pyWiFi-ESP32-S2 上有 1 个 LED(蓝色),控制 LED 使用 machine 中的 Pin 对

象,其构造函数和使用方法如下:

构造函数
led=machine.Pin(id,mode,pull)
构建 led 对象。id:引脚编号;mode:输入输出方式;pull:上下拉电阻配置。
使用方法
led.value([x])
引脚电平值。输出状态:x=0 表示低电平,x=1 表示高电平;输入状态:无须
参数,返回当前引脚值。
led.on()
使引脚输出高电平“1”。
led.off()
使引脚输出低电平“0”。
更详细内容,请查看 micropython 库文档:https://docs.01studio.cc/

上表对 MicroPython 的 machine 中 Pin 对象做了详细的说明,machine 是大

模块,Pin 是 machine 下面的其中一个小模块,在 python 编程里有两种方式引用

相关模块:

方式 1 :import machine,然后通过 machine.Pin 来操作;

方式 2 :from machine import Pin,意思是直接从 machine 中引入 Pin 模块,

然后直接通过构建 led 对象来操作。显然方式 2 会显得更直观和方便,本实验也

是使用方式 2 来编程。代码编写流程如下:

从原理图可以看到 LED 跟模块引脚 2 相连,通过输出高电平方式点亮

image-20230701223726651

1
2
3
4
5
6
7
'''
实验名称:点亮 LED 蓝灯
版本:v1.0
'''
from machine import Pin #导入 Pin 模块
led=Pin(2,Pin.OUT) #构建 led 对象,GPIO2,输出
led.value(1) #点亮 LED,也可以使用 led.on()

总结:

从第一个实验我们可以看到,使用 MicroPython 来开发关键是要学会构造函

数和其使用方法,便可完成对相关对象的操作,在强大的模块函数支持下,实验

只用了简单的两行代码便实现了点亮 LED 灯。

 

 

按键

前言:

按键是最简单也最常见的输入设备,很多产品都离不开按键,包括早期的iPhone。有了按键输入功能,我们就可以做很多好玩的东西了。

实验目的:

使用按键功能,通过检测按键被按下后,改变 LED(蓝灯)的亮灭状态。

实验讲解:

pyWiFi-ESP32-S2 开发板上有 2 个按键,RST 和 KEY,RST 顾名思义是复位用的,所以真正自带可以用的就只有 1 个按键 KEY。

让我们先来搞清楚 MicroPython 里面 Pin 模块实现按键的构造函数和使用方法。

 

构造函数
KEY=machine.Pin(id,mode,pull)
构建按键对象。id:引脚编号;mode:输入输出方式;pull:上下拉电阻配置。
使用方法
KEY.value()
引脚电平值。输入状态:无须参数,返回当前引脚值 0 或者 1。

可以看到跟上一节 LED 一样,只是输入/输出状态的一个改变。从下面原理图可以看到,我们只需要在开发板上电后判断 KEY 引脚的电平,当被按下时候引

脚为低电平“0

按键被按下时候可能会发生抖动,抖动如下图,有可能造成误判,因此我们

需要使用延时函数来进行消抖:

image-20230701224121294

常用的方法就是当检测按键值为 0 时,延时一段时间,大约 10ms,再判断按键引脚值仍然是 0,是的话说明按键被按下。延时使用 time 模块,使用方法如

下:

1
2
3
4
5
6
7
import time
time.sleep(1) # 睡眠 1 秒
time.sleep_ms(500) # 睡眠 500 毫秒
time.sleep_us(10) # 睡眠 10 微妙
start = time.ticks_ms() # 获取毫秒计时器开始值
delta = time.ticks_diff(time.ticks_ms(), start) # 计算从上电开始到当前时间
的差值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
'''
实验名称:按键
说明:通过按键改变 LED 的亮灭状态
'''
from machine import Pin
import time
LED=Pin(2,Pin.OUT) #构建 LED 对象,开始熄灭
KEY=Pin(0,Pin.IN,Pin.PULL_UP) #构建 KEY 对象
state=0 #LED 引脚状态
while True:
if KEY.value()==0: #按键被按下
time.sleep_ms(10) #消除抖动
if KEY.value()==0: #确认按键被按下
state=not state #使用 not 语句而非~语句
LED.value(state) #LED 状态翻转
print('KEY')
while not KEY.value(): #检测按键是否松开
pass

从上面代码可以看到,初始化各个对象后,进入循环,当检测到 KEY 的值为0(按键被按下)时候,先做了 10ms 的延时,再次判断;

state 为 LED 状态的值,每次按键按下后通过使用 not 来改变。这里注意的是在 python 里使用‘not’而不是‘~’的方式。not 返回的是 True 和 False,即

0,1。而~ 是取反操作,会导致出错。

 

外部中断

前言:

前面我们在做普通的 GPIO 时候,虽然能实现 IO 口输入输出功能,但代码是一直在检测 IO 输入口的变化,因此效率不高,特别是在一些特定的场合,比如

某个按键,可能 1 天才按下一次去执行相关功能,这样我们就浪费大量时间来实时检测按键的情况。

为了解决这样的问题,我们引入外部中断概念,顾名思义,就是当按键被按下(产生中断)时,我们才去执行相关功能。这大大节省了 CPU 的资源,因此中断

的在实际项目的应用非常普遍.

实验目的:

利用中断方式来检查按键 KEY 状态,被按键被按下(产生外部中断)后使 LED

的亮灭状态翻转

实验讲解:

外部中断也是通过 machine 模块的 Pin 子模块来配置,我们先来看看其配构

造函数和使用方法

构造函数
KEY=machine.Pin(id,mode,pull)
构建按键对象。id:引脚编号;mode:输入输出方式;pull:上下拉电阻配置。
使用方法
KEY.irq(handler,trigger)
配置中断模式。
handler:中断执行的回调函数;
trigger: 触发中断的方式,共 4 种,分别是 Pin.IRQ_FALLING(下降沿触发)、
Pin.IRQ_RISING(上升沿触发)、Pin.IRQ_LOW_LEVEL(低电平触发)、
Pin.IRQ_HIGH_LEVEL(高电平触发)

上升沿和下降沿触发统称边沿触发。从上一节按键可以看到,按键被按下时一个引脚值从 1 到 0 变化的过程,边沿触发就是指这个过程。

image-20230701224921935

由此可见,我们可以选择下降沿方式触发外部中断,也就是当按键被按下的时候立即产生中断。

编程思路中断跟按键章节类似,在初始化中断后,当系统检测到外部终端时候,执行 LED 亮灭状态反转的代码即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'''
实验名称:外部中断
说明:通过按键改变LED的亮灭状态(外部中断方式)
'''

from machine import Pin
import time

LED=Pin(2,Pin.OUT) #构建LED对象,开始熄灭
KEY=Pin(0,Pin.IN,Pin.PULL_UP) #构建KEY对象
state=0 #LED引脚状态

#LED状态翻转函数
def fun(KEY):
global state
time.sleep_ms(10) #消除抖动
if KEY.value()==0: #确认按键被按下
state = not state
LED.value(state)

KEY.irq(fun,Pin.IRQ_FALLING) #定义中断,下降沿触发

以上代码中需要注意的地方:

1、state 是全局变量,因此在 fun 函数里面用该变量必须添加 globalstate 代码,否则会在函数里面新建一个样的变量造成冲突。

2、在定义回调函数 fun 的时候,需要将 Pin 对象 KEY 传递进去。

总结:

从参考代码来看,只是用了几行代码就实现了实验功能,而且相对于使用while True 实时检测函数来看,代码的效率大大增强。外部中断的应用非常广,

出来普通的按键输入和电平检测外,很大一部分输入设备,比如传感器也是通过外部中断方式来实时检测.

 

定时器

前言:

定时器,顾名思义就是用来计时的,我们常常会设定计时或闹钟,然后时间到了就告诉我们要做什么了。单片机也是这样,通过定时器可以完成各种预设好

的任务。

实验目的:

通过定时器让 LED 周期性每秒闪烁 1 次。

实验讲解:

ESP32-S2 内置 RTOS(实时操作系统)定时器,在 machine 的 Timer 模块中。通过 MicroPython 可以轻松编程使用。我们也是只需要了解其构造对象函数和使

用方法即可!

构造函数
tim=machine.Timer(id)
构建定时器对象。
【id】ESP32-S2 有 2 路硬件定时器,id=0~1,也可以定义成-1,即RTOS 虚拟定时器
使用方法
tim.init(period,mode,callback)
定时器初始化。
period:单位为 ms;
mode:2 种工作模式,Timer.ONE_SHOT(执行一次)、Timer.PERIODIC(周期性);callback:定时器中断后的回调函数。

定时器到了预设指定时间后,也会产生中断,因此跟外部中断的编程方式类似,代码编程流程图如下:

image-20230701225642158

参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'''
实验名称:定时器
说明:通过定时器让LED周期性每秒闪烁1次
'''
from machine import Pin,Timer

led=Pin(2,Pin.OUT)
Counter = 0
Fun_Num = 0

def fun(tim):

global Counter
Counter = Counter + 1
print(Counter)
led.value(Counter%2)

#开启RTOS定时器,编号为-1
tim = Timer(1)
tim.init(period=1000, mode=Timer.PERIODIC,callback=fun) #周期为1000ms

 

I2C 总线(OLED 显示屏)

前面学习了按键输入设备后,这一节我们来学习输出设备 OLED 显示屏,其实之前的 LED 灯也算是输出设备,因为它们确切地告诉了我们硬件的状态。只是

相对于只有亮灭的 LED 而言,显示屏可以显示更多的信息,体验更好。

实验讲解:

什么是 I2C**?**

I2C 是用于设备之间通信的双线协议,在物理层面,它由 2 条线组成:SCL 和SDA,分别是时钟线和数据线。也就是说不通设备间通过这两根线就可以进行通

信。

什么是 OLED 显示屏?

OLED 的特性是自己发光,不像 TFT LCD 需要背光,因此可视度和亮度均高,其次是电压需求低且省电效率高,加上反应快、重量轻、厚度薄,构造简单,成

本低等特点。简单来说跟传统液晶的区别就是里面像素的材料是由一个个发光二极管组成,因为密度不高导致像素分辨率低,所以早期一般用作户外 LED 广告

牌。随着技术的成熟,使得集成度越来越高。小屏也可以制作出较高的分辨率。

image-20230701230249879

在了解完 I2C 和 OLED 显示屏后,我们先来看看 pyBase 开发板的原理图,也就是上面的 OLED 接口是如何连线的。

image-20230701230306218

我们从 pyWiFi-ESP32-S2 和 pyBase 相结合的原理图可以看到 GPIO38—Y6—SCL, GPIO40—Y8—SDA 的连接关系:

image-20230701230321167

本实验将使用 MicroPython 的 Machine 模块来定义 Pin 口和 I2C 初始化。具体如下:

构造函数
i2c = machine.I2C(scl,sda)i2c = machine.I2C(scl,sda)
构建 I2C 对象。scl:时钟引脚;sda:数据引脚。构建 I2C 对象。scl:时钟引脚;sda:数据引脚。
使用方法**使用方法**
i2c.scan()i2c.scan()
扫描 I2C 总线的设备。返回地址,如:0x3c;扫描 I2C 总线的设备。返回地址,如:0x3c;
i2c.readfrom(addr,nbytes)i2c.readfrom(addr,nbytes)
从指定地址读数据。addr:指定设备地址;nbytes:读取字节数;从指定地址读数据。addr:指定设备地址;nbytes:读取字节数;
i2c.write(buf)
写数据。buf:数据内容;

定义好 I2C 后,还需要驱动一下 OLED。这里我们已经写好了 OLED 的库函数,在 ssd1306.py 文件里面。开发者只需要拷贝到 pyBoard 文件系统里面,然后在 main.py 里面调用函数即可。人生苦短,我们学会调用函数即可,也就是注重顶层的应用,想深入的小伙伴也可以自行研究 ssd1306.py 文件代码。OLED 显示屏对象介绍如下:

构造函数
oled = SSD1306_I2C(width, height, i2c, addr)
构 OLED 显示屏对象。width:屏幕宽像素;height: 屏幕高像素;i2c:定义好的
I2C 对象; addr:显示屏设备地址。
使用方法
oled.text(string,x,y)
将 string 字符写在指定为位置。string:字符;x:横坐标;y:纵坐标。
oled.show()
执行显示。
oled.fill(RGB)
清屏。RGB:0 表示黑色,1 表示白色。

学习了 I2C、OLED 对象用法后我们通过编程流程图来理顺一下思路:

image-20230701231539182

mian函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'''
实验名称:I2C总线(OLED显示屏)
'''

from machine import SoftI2C,Pin #从machine模块导入I2C、Pin子模块
from ssd1306 import SSD1306_I2C #从ssd1306模块中导入SSD1306_I2C子模块

i2c = SoftI2C(sda=Pin(40), scl=Pin(38)) #I2C初始化:sda-->40, scl -->38
oled = SSD1306_I2C(128, 64, i2c, addr=0x3c) #OLED显示屏初始化:128*64分辨率,OLED的I2C地址是0x3c

oled.text("Hello World!", 0, 0) #写入第1行内容
oled.text("MicroPython", 0, 20) #写入第2行内容
oled.text("By 01Studio", 0, 50) #写入第3行内容

oled.show() #OLED执行显示

上述代码中 OLED 的 I2C 地址是 0x3C,不同厂家的产品地址可能预设不一样,具体参考厂家的说明书。或者也可以通过 I2C.scan()来获取设备地址。另外记得将我们提供的示例代码中的 ssd1306.py 驱动文件拷贝到 pyWiFiESP32-S2 的文件系统下,跟 main.py 保持同一个路径。

image-20230701231632416

总结:

这一节我们学会了驱动 OLED 显示屏,换着以往如果从使用单片机从 0 开发的话你需要了解 I2C 总线原理,了解 OLED 显示屏的使用手册,编程 I2C 代码,有经验的嵌入式工程师搞不好也要弄个几天。现在基本半个小时解决问题。当然前提是别人已经给你搭好桥了,有了强大的底层驱动代码支持,我们只做好应用就好。

 

 

RTC 实时时钟

前言:

时钟可以说我们日常最常用的东西了,手表、电脑、手机等等无时无刻不显示当前的时间。可以说每一个电子爱好者心中都希望拥有属于自己制作的一个电子时钟,接下来我们就用 MicroPython 开发板来制作一个属于自己的电子时钟。

实验讲解:

实验的原理是读取 RTC 数据,然后通过 OLED 显示。毫无疑问,强大的MicroPython 已经集成了内置时钟函数模块。位于 machine 的 RTC 模块中,具体介绍如下:

构造函数
rtc=machine.RTC()
构建 RTC 对象。
使用方法
rtc.datetime((2019, 4, 1, 0, 0, 0, 0, 0))
设置日期和时间。按顺序分别是:(年,月,日,星期,时,分,秒,微秒),
其中星期使用 0-6 表示周一至周日。
rtc.datetime()
获取当前日期和时间

从上表可以看到 RTC()的使用方法,我们需要做的就是先设定时间,然后再获取当前芯片里的时间,通过 OLED 显示屏显示,如此循环。在循环里,如果一直获取日期时间数据会造成资源浪费,所以可以每隔第一段时间获取一次数据,又由于肉眼需要看到至少每秒刷新一次即可,这里每隔 300ms 获取一次数据,使用前面学习过的 RTOS 定时器来计时,具体编程流程如下:

image-20230701232806658

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
'''
实验名称:RTC实时时钟
说明:使用Thonny连接开发板会自动更新RTC时间
'''

# 导入相关模块
from machine import Pin, SoftI2C, RTC,Timer
from ssd1306 import SSD1306_I2C

# 定义星期和时间(时分秒)显示字符列表
week = ['Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun']
time_list = ['', '', '']

# 初始化所有相关对象
i2c = SoftI2C(sda=Pin(40), scl=Pin(38)) #I2C初始化:sda-->40, scl-->38
oled = SSD1306_I2C(128, 64, i2c, addr=0x3c)
rtc = RTC()

# 首次上电配置时间,按顺序分别是:年,月,日,星期,时,分,秒,次秒级;这里做了
# 一个简单的判断,检查到当前年份不对就修改当前时间,开发者可以根据自己实际情况来
# 修改。
if rtc.datetime()[0] != 2023:
rtc.datetime((2021, 7, 1, 0, 0, 0, 0, 0))

def RTC_Run(tim):

datetime = rtc.datetime() # 获取当前时间

oled.fill(0) # 清屏显示黑色背景
oled.text('01Studio', 0, 0) # 首行显示01Studio
oled.text('RTC Clock', 0, 15) # 次行显示实验名称

# 显示日期,字符串可以直接用“+”来连接
oled.text(str(datetime[0]) + '-' + str(datetime[1]) + '-' + str(datetime[2]) + ' ' + week[datetime[3]], 0, 40)

# 显示时间需要判断时、分、秒的值否小于10,如果小于10,则在显示前面补“0”以达
# 到较佳的显示效果
for i in range(4, 7):
if datetime[i] < 10:
time_list[i - 4] = "0"
else:
time_list[i - 4] = ""

# 显示时间
oled.text(time_list[0] + str(datetime[4]) + ':' + time_list[1] + str(datetime[5]) + ':' + time_list[2] + str(datetime[6]), 0, 55)
oled.show()

#开启RTOS定时器
tim = Timer(0)
tim.init(period=300, mode=Timer.PERIODIC, callback=RTC_Run) #周期300ms


由于实验要用到 OLED 显示屏,所以同样别忘了将示例代码该实验文件夹下的 ssd1306.py 文件复制到 pyWiFi-ESP32-S2 的文件系统里面。

由于 ESP32-S2 没有后备电池引脚,所以不支持掉电保存。因此 pybase 上面的纽扣电池是不起作用的。

总结:

细心的用户或许已经发现运行程序后 RTC 时间自动更新,那是因为 thonny每次连接 MicroPython 开发板会自动更新开发板的 RTC 时间。

RTC 实时时钟的可玩性很强,我们还可以根据自己的风格来设定数字显示位置,以及加上一些属于自己的字符标识。打造自己的电子时钟。

 

ADC(电位器)

前言:

ADC(analog to digital conversion) 模拟数字转换。意思就是将模拟信号转化成数字信号,由于单片机只能识别二级制数字,所以外界模拟信号常常会通过 ADC转换成其可以识别的数字信息。常见的应用就是将变化的电压转成数字信号。

实验目的:

通过编程调用 MicroPython 的内置 ADC 函数,实现测量输入电压,并显示到屏幕上。

实验讲解:

pyBase 开发底板的 X7 引脚连接到了电位器,通过电位器的调节可以使得 X7引脚上的电压变化范围实现从 0-3.3V。

image-20230701233122746

image-20230701233132565

从上图可以看到,电位器引脚对应 pyBase 的 X7,实际是跟 pyWiFi-ESP32-S2

的‘

6’引脚 ADC 输入引脚相连。ESP32-S2 的 ADC 默认只能测量 0-1V 的量程,

但 ESP32-S2 内部集成了衰减器,最大支持 11dB 衰减,通过配置衰减器最多能测

量 3V 左右的电压。我们来看看 ADC 模块的构造函数和使用方法。

构造函数

adc=machine.ADC(Pin(id))

构建 ADC 对象。

【id】目前仅支持 ESP32-S2 的 ADC1,共 10 个通道:

GPIO1: ADC1_0

GPIO2: ADC1_1

GPIO3: ADC1_2

GPIO4: ADC1_3

GPIO5: ADC1_4

GPIO6: ADC1_5

GPIO7: ADC1_6

GPIO8: ADC1_7

GPIO9: ADC1_8

GPIO10: ADC1_9

使用方法

adc.read()

获取 ADC 值。测量精度是 13 位,返回 0- 8191(表示 0-1V)。

adc.atten(attenuation)

配置衰减器。配置衰减器能增加电压测量范围,但是以精度为代价的。

attenuation:衰减设置

ADC.ATTN_0DB: 0dB 衰减, 最大输入电压为 1.00v - 这是默认配置;

ADC.ATTN_2_5DB: 2.5dB 衰减, 最大输入电压约为 1.34v;

ADC.ATTN_6DB:6dB 衰减, 最大输入电压约为 2.00v;

ADC.ATTN_11DB:11dB 衰减, 最大输入电压约为 3.3v。

你没看错,就这么简单,两句函数就可以获得 ADC 数值。我们将在本实验中以默认的量程 0-1V 来测试。让我们来理顺一下编程逻辑。先导入相关模块,然后初始化模块。在循环中不断读取 ADC 的值,转化成电压值后在 OLED 上面显示,每隔 300 毫秒读取一次,具体如下:

image-20230701233235973

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
'''
实验名称:ADC-电压测量
说明:通过对ADC数据采集,转化成电压在显示屏上显示。ADC精度13位(0~8191),默认电压0-1V。
'''

#导入相关模块
from machine import Pin,SoftI2C,ADC,Timer
from ssd1306 import SSD1306_I2C

#初始化相关模块
i2c = SoftI2C(sda=Pin(40), scl=Pin(38)) #I2C初始化:sda-->40, scl -->38
oled = SSD1306_I2C(128, 64, i2c, addr=0x3c)
adc = ADC(Pin(6)) #6引脚跟pyBase的电位器相连接
adc.atten(ADC.ATTN_11DB) #开启衰减,测量量程增大到3.3V

def ADC_Test(tim):

oled.fill(0) # 清屏显示黑色背景
oled.text('01Studio', 0, 0) # 首行显示01Studio
oled.text('ADC', 0, 15) # 次行显示实验名称

#获取ADC数值
oled.text(str(adc.read()),0,40)
oled.text('(8191)',60,40)

#计算电压值,获得的数据0-4095相当于0-1V,('%.2f'%)表示保留2位小数
oled.text(str('%.2f'%(adc.read()/8191*3.3)),0,55)
oled.text('V',40,55)

oled.show()

#开启定时器
tim = Timer(1)
tim.init(period=300, mode=Timer.PERIODIC, callback=ADC_Test) #周期300ms

总结:

这一节我们学习了 ADC 的应用,主要用于电压的检测。有兴趣的用户可以尝试使用其衰减器测试,可以扩充电压量程,但精度会有所下降。

 

 

PWM(无源蜂鸣器)

前言:

上一节的 ADC 是信号输入,这节的 PWM 就是一个信号输出。PWM(脉冲宽度调制),主要用于输出不同频率、占空比(一个周期内高电平出现时间占总时间比例)的方波。以实现固定频率或平均电压输出。

实验目的:

通过不同频率的 PWM 信号输出,驱动无源蜂鸣器发出不同频率的声音。

实验讲解:

蜂鸣器分有源蜂鸣器和无源蜂鸣器,有源蜂鸣器的使用方式非常简单,只需要接上电源,蜂鸣器就发声,断开电源就停止发声。而本实验用到的无源蜂鸣器,是需要给定指定的频率,才能发声的,而且可以通过改变频率来改变蜂鸣器的发声音色,以此来判定 pyWiFi-ESP32-S2 的 PWM 输出频率是在变化的。pyBase 开发底板上的无源蜂鸣器连接到 pyBase 引脚 X5。如下图所示:

image-20230702110722346

从 pyWiFi-ESP32-S2 原理图可以看到由底板蜂鸣器 X5 连接到 ESP32-S2 的引脚 4。

image-20230702110739134

PWM 可以通过 ESP32-S2 所有 GPIO 引脚输出. 所有通道都有 1 个特定的频率,从 0 到 40M 之间(单位是 Hz)。占空比的值为 0 至 1023 之间。在本实验中我们用到引脚 4。

先看看 PWM 模块对象:

构造函数

pwm=machine.PWM(machine.Pin(id),freq,duty)

构建 PWM 对象。id:引脚编号;freq:频率值;duty:占空比;配置完后 PWM 自

动生效。

使用方法

pwm.freq(freq)

设置频率。freq:频率值在 1-1000 之间,freq 为空时表示获取当前频率值。

pwm.duty(duty)

设置占空比。duty:占空比在 0-1023 之间,duty 为空时表示获取当前占空比值。

pwm.deinit()

关闭 PWM。

无源蜂鸣器我们可以用特定频率的方波来驱动,方波的原理很简单,就是一定频率的高低电平转换,可以简单理解成占空比为 50%的 PWM 输出。

结合上述讲解,总结出代码编写流程图如下:

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
'''
实验名称:PWM
版本:v1.0
日期:2021.8
作者:01Studio
说明:通过不同频率的PWM信号输出,驱动无源蜂鸣器发出不同频率的声音。
'''

from machine import Pin, PWM
import time

Beep = PWM(Pin(4), freq=0, duty=512) # 在同一语句下创建和配置PWM,占空比50%

#蜂鸣器发出频率200Hz响声
Beep.freq(200)
time.sleep(1)

#蜂鸣器发出频率400Hz响声
Beep.freq(400)
time.sleep(1)

#蜂鸣器发出频率600Hz响声
Beep.freq(600)
time.sleep(1)

#蜂鸣器发出频率800Hz响声
Beep.freq(800)
time.sleep(1)

#蜂鸣器发出频率1000Hz响声
Beep.freq(1000)
time.sleep(1)

#停止
Beep.deinit()

 

UART(串口通信)

前言:

串口是非常常用的通信接口,有很多工控产品、无线透传模块都是使用串口来收发指令和传输数据,这样用户就可以在无须考虑底层实现原理的前提下将各类串口功能模块灵活应用起来。

实验讲解:

pyWiFi-ESP32-S2 开发板一共有 2 个串口,编号是 0-1,如下表:

UART(0) TX 43
RT 44
UART(1) TX 任意映射IO
RX 任意映射IO

由于 UART0 用于下载和 REPL 调试,因此我们使用 UART1 来进行本节实验。

我们先来了解一下串口对象的构造函数和使用方法

 

构造函数

uart=machine.UART(id,baudrate,tx=None,rx=None bits=8, parity=None, stop=1,…)

创建 UART 对象。

【id】0-1

【baudrate】波特率,常用 115200、9600

【tx】自定义 IO

【rx】自定义 IO

【bits】数据位

【parity】校验;默认 None, 0(偶校验),1(奇校验)

【stop】停止位,默认 1

特别说明: ESP32-S2 的 UART 引脚映射到其它 IO 来使用,用户可以通过构造

函数时候指定如 tx=8,rx=9 的方式来改变串口引脚,实现更灵活的应用。

使用方法

uart.deinit()

关闭串口

uart.any()

返回等待读取的字节数据,0 表示没有

uart.read([nbytes])

【nbytes】读取字节数

UART.readline()

读行

UART.write(buf)

【buf】串口 TX 写数据

 

我们可以用一个USB转TTL工具,配合电脑上位机串口助手来跟MicroPython

开发板模拟通信。

image-20230702111650095

注意要使用 3.3V 电平的 USB 转串口 TTL 工具,本实验我们使用串口 2,也就是 8(TX)和 9(RX),接线示意图如下:

在本实验中我们可以先初始化串口,然后给串口发去一条信息,这样 PC 机的串口助手就会在接收区显示出来,然后进入循环,当检测到有数据可以接收时候就将数据接收并打印,并通过 REPL 打印显示。代码编写流程图如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'''
实验名称:串口通信
说明:通过编程实现串口通信,跟电脑串口助手实现数据收发。
平台:pyWiFi-ESP32
'''

#导入串口模块
from machine import UART

uart=UART(1,115200,rx=9,tx=8) #设置串口号1和波特率

uart.write('Hello 01Studio!')#发送一条数据

while True:

#判断有无收到信息
if uart.any():

text=uart.read(128) #接收128个字符
print(text) #通过REPL打印串口3接收的数据


实验结果:

我们按照上述方式将 USB 转 TTL 的 TX 接到开发板的 RX(

9),USB 转 TTL 的

RX 接到开发板的 TX(

8)。GND 接一起,3.3V 可以选择接或不接。

根据上图设备管理器里面的信息,将串口工具配置成 COM14,REPL 串口配置成 COM27(根据自己的串口号调整)。波特率 115200。运行程序,可以看到一开始串口助手收到开发板上电发来的信息“Hello 01Studio!”。我们在串口助手的发送端输入“http://www.01studio.org”, 点击发送,可以看到 pyWiFi-ESP32-S2 在接收到该信息后在 REPL 里面打印了出来。如下图所示:

image-20230702111909798

 

 

LCD 显示屏

前言:

前面用到的 OLED 显示屏虽然能显示信息,但是颜色只有黑白,而且分辨率也比较低 128x64,本节我们来学习 3.2 寸 TFT_LCD 彩色显示屏的使用方法。

实验目的:

通过 MicorPython 编程方式实现 LCD 的各种显示功能,包括画点、线、矩形、圆形、显示英文、显示图片等

 

实验用的 LCD 是 3.2 寸,驱动是的 ILI9341,使用 SPI 方式跟 ESP32-S2 通信,按以往嵌入式 C 语言开发,我们需要对 ILI9341 进行编程实现驱动,然后再建立各种描点、划线、以及显示图片函数。

使用 MicroPython 其实也需要做以上工作,但由于可读性和移植性强的特点我们只需要搞清各个对象函数使如何使用即可。总的来说和前面实验一样,有构造函数和功能函数。构造函数解决的是初始化问题,告诉开发板该外设是怎么接线,初始化参数如何,而功能函数解决的则是使用问题,我们基于自己的需求直接调用相关功能函数,实现自己的功能即可!

我们管这些函数的集合叫驱动,驱动可以是预先在固件里面,也可以通过.py文件存放在开发板文件系统。也就是说工程师已经将复杂的底层代码封装好,我们顶层直接使用 python 开发即可,人生苦短。我们来看看 pyWiFi-ESP32-S2P 开发板 3.2 寸 LCD 的构造函数和使用方法

 

构造函数

tftlcd.LCD32(portrait=1)

构建 3.2 寸 LCD 对象。

【portrait】 设置屏幕方向:

⚫ 1 - 竖屏,240*320 ,默认

⚫ 2 - 横屏,320*240 ,1 基础上顺时针旋转 90°

⚫ 3 - 竖屏,240*320 ,1 基础上顺时针旋转 180°

⚫ 4 - 横屏,320*240 ,1 基础上顺时针旋转 270°

使用方法

LCD32.fill(color)

【color】RGB 颜色数据;如(255,0,0)表示红色。

LCD32.drawPixel(x,y,color)

画点。

【x】:横坐标,

【y】:纵坐标,

【color】:颜色。

LCD32.drawLine(x0,y0,x1,y1,color)

画线段。

【x0,y0】:起始坐标,

【x1,y1】:终点坐标,

【color】:颜色

LCD32.drawRect(x,y,width,height,color,border=1,fillcolor=None)

画矩形。

x,y】:起始坐标,

【width】:宽度,

【height】:高度,

【color】:颜色,

【border】:边宽,

【fillcolor】:填充颜色,默认 None 为不填充

LCD32.drawCircle(x,y,radius,color,border=1,fillcolor=None)

画圆。

【x, y】:圆心,

【radius】:半径,

【color】:颜色,

【border】:边宽,

【fillcolor】:填充颜色,默认 None 为不填充

LCD32.printStr(text,x,y,color,backcolor=None,size=2)

写字符。

【text】:字符,

【x,y】:起始坐标,

【color】:字体颜色;

【backcolor】:字体背景颜色;

【size】:字体尺寸(1-小号,2-标准,3-中号,4-大号)

LCD32.Picture(x,y,filename)

显示图片。支持图片格式类型:jpg、bmp

【x,y】:起始坐标。

【filename】: 图片路径+名称,如:”/cat.jpg”

(‘/’表示开发板的板载 flash 的根目录。)

有了上面的对象构造函数和使用说明,编程可以说是信手拈来了,我们在使用中将以上功能都跑一遍先看看编程流程图:

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
'''
实验名称:3.2寸LCD液晶显示屏
说明:通过编程实现LCD的各种显示功能,包括填充、画点、线、矩形、圆形、显示英文、显示图片等。
'''

#导入相关模块
from tftlcd import LCD32
import time

#定义常用颜色
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
WHITE = (255,255,255)

########################
# 构建3.2寸LCD对象并初始化
########################
d = LCD32(portrait=1) #默认方向竖屏

#填充白色
d.fill(WHITE)

#画点
d.drawPixel(5, 5, RED)

#画线段
d.drawLine(5, 10, 200, 10, RED)

#画矩形
d.drawRect(5, 30, 200, 40, RED, border=5)

#画圆
d.drawCircle(100, 120, 30, RED, border=5)

#写字符,4种尺寸
d.printStr('Hello 01Studio', 10, 200, RED, size=1)
d.printStr('Hello 01Studio', 10, 230, GREEN, size=2)
d.printStr('Hello 01Studio', 10, 270, BLUE, size=3)

time.sleep(5) #等待5秒

#显示图片
d.Picture(0,0,"/picture/1.jpg")
time.sleep(3)
d.Picture(0,0,"/picture/2.jpg")
time.sleep(3)
d.Picture(0,0,"/picture/01studio.jpg")

实验结果:

将示例程序的素材文件上传到 pyWiFi-ESP32-S2P 开发板。(也可以只上传单张图片,注意修改代码中文件的路径即可。)

 

电阻触摸屏

前言:

上一节我们学习了 LCD 实验,但 LCD 只能显示相关内容,跟人是缺乏交互的。好比我们的智能手机,如果只有显示不能触碰,那么就没有可玩性了。因此本节学习一下 3.2 寸 LCD 的电阻触摸屏使用方法。

实验讲解:

01Studio 配套的 3.2 寸 LCD 上带电阻触摸屏,驱动芯片为 XPT2046。当手指按下时候,通过简单的编程即可返回一个坐标,我们来看看其 micropython 构造函数和使用方法:

构造函数

touch.XPT2046(portrait=1)

构建触摸屏对象。XPT2046 表示驱动芯片型号。

【portrait】 设置屏幕方向:

⚫ 1 - 竖屏,240*320 ,默认

⚫ 2 - 横屏,320*240 ,1 基础上顺时针旋转 90°

⚫ 3 - 竖屏,240*320 ,1 基础上顺时针旋转 180°

⚫ 4 - 横屏,320*240 ,1 基础上顺时针旋转 270°

使用方法

XPT2046.tick_inc()

手动刷新触摸。

XPT2046.read()

读取触摸屏数据,返回(states,x,y)

【states】-当前触摸状态:0:按下;1:移动;2:松开。

【x】:触摸横坐标

【y】:触摸纵坐标

 

学会了触摸对象用法后,我们可以编程实现触摸后屏幕打点表示,然后左上角显示当前触摸的坐标。另外再加入一个按键,按下清空屏幕。编程流程图如下:

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
'''
实验名称:电阻触摸屏
说明:电阻触摸屏采集触摸信息
'''

from touch import XPT2046
from tftlcd import LCD32
from machine import Pin
import time

#定义颜色
BLACK = (0,0,0)
WHITE = (255,255,255)
RED=(255,0,0)

#LCD初始化
d = LCD32(portrait=1) #默认竖屏
d.fill(WHITE) #填充白色

#电阻触摸屏初始化,方向和LCD一致
t = XPT2046(portrait=1)

def fun(KEY):

d.fill(WHITE) #清屏

#USR按键初始化
KEY=Pin(0,Pin.IN,Pin.PULL_UP) #构建KEY对象
KEY.irq(fun,Pin.IRQ_FALLING) #定义中断,下降沿触发

while True:

data = t.read() #获取触摸屏坐标
print(data) #REPL打印

#当产生触摸时
if data[0]!=2: #0:按下; 1:移动; 2:松开

#触摸坐标画圆
d.drawCircle(data[1], data[2], 5, BLACK, fillcolor=BLACK)
d.printStr('(X:'+str('%03d'%data[1])+' Y:'+str('%03d'%data[2])+')',10,10,RED,size=1)

time.sleep_ms(20) #触摸响应间隔


实验结果:

运行程序,首次运行会自动提示进行触摸校准(电阻屏需要校准),按提示分别点击四个角落进行校准,如校准失败会自动重复。校准成功会自动保存一个“touch.cail”文件到开发板 flash,下次无须再校准。

成功后出现空白画面,用手指触摸屏幕或者在屏幕上滑动,可以看到描点并在 LCD 左上角显示当前坐标。

重启开发板,可以看到文件系统多了一个“touch.cail”,在运行电阻屏初始化时候会检测这个文件,如果存在则不进行校准,若想重新校准的用户可以把这个文件删除即可!

总结:

没有触摸屏的 LCD 就失去了灵魂,有了触摸屏,跟开发板的交互就变得有意思了。