数学中的函数一词泛指发生在集合之间的一种对应关系和变化过程。在程序设计领域,函数实际上就是一段具有特定功能、完成特定任务的程序,以减少重复编写程序的工作量。使用 PWM 模拟信号除了能够控制灯光亮度外,还能控制许多其他的电子设备,蜂鸣器就是其中一种。在本节课程中,我们将了解关于蜂鸣器的基本知识,和它的使用方法,并学习如何使用函数来简化程序,完成一段简单的旋律。
知识锦囊
蜂鸣器
蜂鸣器是一种常见的发声装置,它常被作为发声器用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中。我们常见的蜂鸣器又分为无源蜂鸣器与有源蜂鸣器。
这其中的“源”并非指电源,而是指震荡源。
震荡源即为震荡电路,它能将输入的直流电流信号转换为震荡电流信号输出,从而驱动蜂鸣器发声。
有源蜂鸣器就内置有一个震荡源,在使用时为其提供恒定直流电压即可工作,其发声频率由内部震荡电路决定,音调一般无法改变。
而无源蜂鸣器则没有震荡源,只能靠外界输入 PWM (方波)信号来实现磁场交变,所以我们可以使用 PWM 信号驱动振动装置从而使蜂鸣器发声。而通过调整 PWM 信号的频率和占空比,我们就能控制蜂鸣器的音调高低和音量大小。
在本节课程中,我们将要使用蜂鸣器来演奏一段简单的旋律,但在开始前,我们还需要学习如何通过定义函数来降低工作量。
定义函数
在数学定义中,函数是指发生在集合之间的一种对应关系和变化过程。而在程序设计领域,函数实际上就是一段具有特定功能、能够完成特定任务的程序。当我们编写好这段程序后,就可以在之后的程序里重复的调用这段程序,来处理相同任务,以提高代码的重复利用率。
在之前的学习中,你已经使用了许多 Python 的内置函数,例如 print() , Pin() 等。当然,我们也可以自己自定义函数来进行使用。
在 Python 中我们使用 def 来定义函数,其定义函数的语法如下:
def 函数名(参数列表):
函数体
在定义函数时,函数代码块以 def 关键词开头,后接函数名称和小括号” () “,小括号后的冒号” : “表示函数体的开始。函数体,即为定义一个函数功能的所有代码组成的整体,当你完成了函数的定以后,每一次调用该函数时系统都会执行一次函数体中的代码。
直接来看示例程序:
def HelloWorld():
print("Hello World")
HelloWorld()
该程序的执行结果为:
Hello World
在该程序中,我们定义了一个名为 HelloWorld 的函数,在每次使用 “HelloWorld()” 调用该函数时,都会执行一次函数体中的 “print(“Hello World”) “ 语句。
在函数名后的括号中,我们还可以写入需要被引入该函数的参数,例如:
a = 4
b = 5
def max(a, b):
if a > b:
return a
else:
return b
print(max(a, b))
在该程序中,我们创建了一个名为 max() 的函数,该函数会在参数列表中引入两个参数。在函数体中,程序会比较两个数的大小,并返回较大的数。
其中 return [表达式] 代表函数的结束,它会返回一个值给调用它的函数。例如在该程序中,我们会通过 if 条件判断来判断出 a,b 中较大的那一个数,并返回它的值。所以该程序的执行结果为5,即 a,b 中较大的 b 的值。
该程序的执行结果为:
5
实践操作
项目一:演奏基本音节
首先,我们先来控制蜂鸣器演奏 do、re、mi、fa、sol、la、si 七个基本音节。
硬件连接
在该项目中,我们所需要使用的硬件有:
- 树莓派 Pico
- 树莓派 Pico 拓展板
- Grove - 蜂鸣器
插接好 Pico 和拓展板,使用 Grove 数据线将蜂鸣器连接到 A1 接口。
编写程序
我们要想使用蜂鸣器来演奏乐曲,就需要学会控制蜂鸣器发出的声音的两个维度:音量和音调。
对于音量而言,我们同样可以像控制 LED 灯亮度一样,通过改变 PWM 信号的占空比来实现。而对于音调来说,声音的音调高低是由声音的频率决定的,所以我们只需要修改 PWM 信号的频率,即可实现对音调的控制。
我们先来编写一个程序,让蜂鸣器发出 do、re、mi、fa、sol、la、si 七个音节。
在该程序中。我们需要定义引脚,并使用 sleep() 函数控制每个音节的持续时间,所以我们需要在程序的开头声明用到的函数库:
from machine import Pin, PWM
from time import sleep
接着定义引脚
buzzer = PWM(Pin(27))
接下来我们需要使用 freq() 函数和 duty_u16() 函数来定义音调与音量:
buzzer.freq(1046) #DO
buzzer.duty_u16(1000)
buzzer.freq(1175) #RE
buzzer.duty_u16(1000)
buzzer.freq(1318) #MI
buzzer.duty_u16(1000)
buzzer.freq(1397) #FA
buzzer.duty_u16(1000)
buzzer.freq(1568) #SO
buzzer.duty_u16(1000)
buzzer.freq(1760) #LA
buzzer.duty_u16(1000)
buzzer.freq(1967) #SI
buzzer.duty_u16(1000)
最后,使用 sleep() 函数规定每一个音节的发声时间长短,程序就完成了,完整的程序如下:
from machine import Pin, PWM
from time import sleep
buzzer = PWM(Pin(27))
while True:
buzzer.freq(1046) #DO
buzzer.duty_u16(1000)
sleep(1)
buzzer.freq(1175) #RE
buzzer.duty_u16(1000)
sleep(1)
buzzer.freq(1318) #MI
buzzer.duty_u16(1000)
sleep(1)
buzzer.freq(1397) #FA
buzzer.duty_u16(1000)
sleep(1)
buzzer.freq(1568) #SO
buzzer.duty_u16(1000)
sleep(1)
buzzer.freq(1760) #LA
buzzer.duty_u16(1000)
sleep(1)
buzzer.freq(1967) #SI
buzzer.duty_u16(1000)
sleep(1)
使用 USB 数据线将 Pico 连接到电脑,点击运行按钮将程序保存到任意位置,你就能听到蜂鸣器演奏七个基本音节了。
演奏基本音节.mp4
项目二:两只老虎
接下来,我们通过定义函数简化程序,控制蜂鸣器演奏两只老虎完整的旋律。乐谱如下:
编写程序
当我们编写一段简单的乐曲时,我们可以使用逐行编写的方法来完成程序,但用这种方法所编写的程序过于冗长和重复,如果我们所想要复现一整首歌曲,不论是从提高代码重复利用率的角度,还是从程序可读性的角度出发,我们都需要使用自定义函数来简化程序。在任务一的基础上,我们可以将每一个音节都定义为一个函数,当我们想要演奏该音节时,只需要调用一次该函数即可。
以音节 DO 为例,定义函数的程序如下:
def DO():
buzzer.freq(1046) #1
buzzer.duty_u16(1000)
将七个音节与停止发声的执行程序都按这样的方式定义为函数:
def DO():
buzzer.freq(1046) #1
buzzer.duty_u16(1000)
def RE():
buzzer.freq(1175) #2
buzzer.duty_u16(1000)
def MI():
buzzer.freq(1318) #3
buzzer.duty_u16(1000)
def FA():
buzzer.freq(1397) #4
buzzer.duty_u16(1000)
def SO():
buzzer.freq(1568) #5
buzzer.duty_u16(1000)
def LA():
buzzer.freq(1760) #6
buzzer.duty_u16(1000)
def SI():
buzzer.freq(1967) #7
buzzer.duty_u16(1000)
def N():
buzzer.duty_u16(0) #close
在之后的程序中,我们只需要对这些函数进行调用,即可完成乐曲,演奏两只老虎的程序如下:
from machine import Pin, I2C, ADC, PWM
from time import sleep
buzzer = PWM(Pin(27))
vol = 1000
def DO():
buzzer.freq(1046) #1
buzzer.duty_u16(vol)
def RE():
buzzer.freq(1175) #2
buzzer.duty_u16(vol)
def MI():
buzzer.freq(1318) #3
buzzer.duty_u16(vol)
def FA():
buzzer.freq(1397) #4
buzzer.duty_u16(vol)
def SO():
buzzer.freq(1568) #5
buzzer.duty_u16(vol)
def LA():
buzzer.freq(1760) #6
buzzer.duty_u16(vol)
def SI():
buzzer.freq(1967) #7
buzzer.duty_u16(vol)
def N():
buzzer.duty_u16(0) #close
while True:
DO()
sleep(0.25)
RE()
sleep(0.25)
MI()
sleep(0.25)
DO()
sleep(0.25)
N()
sleep(0.01)
DO()
sleep(0.25)
RE()
sleep(0.25)
MI()
sleep(0.25)
DO()
sleep(0.25)
MI()
sleep(0.25)
FA()
sleep(0.25)
SO()
sleep(0.5)
MI()
sleep(0.25)
FA()
sleep(0.25)
SO()
sleep(0.5)
N()
sleep(0.01)
SO()
sleep(0.125)
LA()
sleep(0.125)
SO()
sleep(0.125)
FA()
sleep(0.125)
MI()
sleep(0.25)
DO()
sleep(0.25)
SO()
sleep(0.125)
LA()
sleep(0.125)
SO()
sleep(0.125)
FA()
sleep(0.125)
MI()
sleep(0.25)
DO()
sleep(0.25)
RE()
sleep(0.25)
SO()
sleep(0.25)
DO()
sleep(0.5)
N()
sleep(0.01)
RE()
sleep(0.25)
SO()
sleep(0.25)
DO()
sleep(0.5)
在程序的第五行,我们添加了一个名为 vol 的变量,并在之后的程序中用它来表示 PWM 信号中的占空比数值,现在,每当我们想要修改蜂鸣器的音量时,只需要修改变量 vol 的值即可。
vol = 1000
仔细看看我们的程序,比起逐行直接编写程序已经简化了不少。现在在该程序中,总是重复的部分只剩下了 sleep() 函数,由于要构成一首完整的乐曲,许多音节发声的时间都不同,所以我们不能简单的将 sleep() 函数直接添加进函数体中—-我们需要创建一个带有参数的自定义函数,同样拿音节 DO 来举例,定义音节 DO 的程序如下:
def DO(time):
buzzer.freq(1046) #1
buzzer.duty_u16(vol)
sleep(time)
当我们使用该函数时,需要在括号内填上传入函数体的参数,该参数会被函数体中的 sleep() 函数调用,使我们可以自由控制音节的发音时间。例如当需要音节 DO 发音0.5秒时,我们可以这样编写程序:
DO(0.5)
怎么样,是不是又简洁了许多。通过这样定义函数,我们可以让程序的编写更加简洁,下面是该程序最终的样子:
from machine import Pin, I2C, ADC, PWM
from time import sleep
buzzer = PWM(Pin(27))
vol = 1000
def DO(time):
buzzer.freq(1046) #1
buzzer.duty_u16(vol)
sleep(time)
def RE(time):
buzzer.freq(1175) #2
buzzer.duty_u16(vol)
sleep(time)
def MI(time):
buzzer.freq(1318) #3
buzzer.duty_u16(vol)
sleep(time)
def FA(time):
buzzer.freq(1397) #4
buzzer.duty_u16(vol)
sleep(time)
def SO(time):
buzzer.freq(1568) #5
buzzer.duty_u16(vol)
sleep(time)
def LA(time):
buzzer.freq(1760) #6
buzzer.duty_u16(vol)
sleep(time)
def SI(time):
buzzer.freq(1967) #7
buzzer.duty_u16(vol)
sleep(time)
def N(time):
buzzer.duty_u16(0) #close
sleep(time)
while True:
DO(0.25)
RE(0.25)
MI(0.25)
DO(0.25)
N(0.01)
DO(0.25)
RE(0.25)
MI(0.25)
DO(0.25)
MI(0.25)
FA(0.25)
SO(0.5)
MI(0.25)
FA(0.25)
SO(0.5)
N(0.01)
SO(0.125)
LA(0.125)
SO(0.125)
FA(0.125)
MI(0.25)
DO(0.25)
SO(0.125)
LA(0.125)
SO(0.125)
FA(0.125)
MI(0.25)
DO(0.25)
RE(0.25)
SO(0.25)
DO(0.5)
N(0.01)
RE(0.25)
SO(0.25)
DO(0.5)
使用 USB 数据线将 Pico 连接到电脑,点击运行按钮将程序保存到任意位置,就能听见蜂鸣器演奏两只老虎了。
两只老虎.mp4