5.1 LED指示灯的控制¶
LED指示灯是最简单的一种输出设备,常用于指示计算机系统的内部状态。在BlueFi单板机上有两个可编程控制的LED指示灯, 一个是红色的 (靠近电源指示灯),另一个是白色的(靠近集成光学传感器)。所谓可编程控制的LED,就是我们可以用程序控制其亮或灭。
5.1.1 LED灯的亮灭¶
下例程序将控制BlueFi上的红、白LED灯交替闪烁:
1 2 3 4 5 6 7 8 9 10 | import time
from hiibot_bluefi.basedio import LED
led = LED()
while True:
led.red = 0
led.white = 1
time.sleep(0.5)
led.red = 1
led.white = 0
time.sleep(0.5)
|
将这段代码通过MU编辑器保存到BlueFi的CIRCUITPY磁盘上,文件名为code.py。一旦将code.py文件保存到BlueFi的CIRCUITPY磁盘上, BlueFi立即开始执行这个脚本程序。
这个示例程序的执行效果是:红色LED灯和白色LED灯交替闪烁。闪烁的LED指示灯常用于指示计算机系统正在工作中,如果程序一旦停止, 指示灯也停止闪烁。如果你打开MU编辑器的串口控制台,按下“Ctrl+C”键让BlueFi终止执行code.py程序,进入REPL模式, 你会发现红色LED指示灯不再闪烁。
下面我们逐行来分析每行脚本程序的效果,就像REPL一样地执行程序。
- 第1行,导入一个Python内建的模块“time”。
- 第2行,从“/CIRCUITPY/lib/hiibot_bluefi/basedio.py”模块中导入一个名叫“LED”的类。
- 第3行,将导入的“LED”类实例化为一个实体对象,名叫“led”。
- 第4行,判断条件永远为真的无限循环。
- 第5行(无穷循环程序块的第1行),led对象的red属性(led.red)设置为0。
- 第6行(无穷循环程序块的第2行),led对象的white属性(led.white)设置为1。
- 第7行(无穷循环程序块的第3行),执行time的sleep方法,参数为0.5秒。
- 第8行(无穷循环程序块的第4行),led对象的red属性(led.red)设置为1。
- 第9行(无穷循环程序块的第5行),led对象的white属性(led.white)设置为0。
- 第10行(无穷循环程序块的第6行),执行time的sleep方法,参数为0.5秒。
在本例中,用到了两个模块,分别是time模块和basedio模块。对于time模块,使用的是模块中的sleep方法,该方法的入口参数为延时的时间长度,单位为秒; 而basedio模块的话,使用的是该模块中的“LED”类,在调用类之前,需要将类实例化,即led=LED()。
观察两个LED灯的闪烁情况,我们只能知道LED灯的亮灭是由等号后面的1和0(如led.red = 0)决定的,但到底是哪个让LED灯亮呢? 我们可以借助于REPL模式分别单步执行其中一个语句,并观察LED灯的状态来确定。在MU编辑器的串口控制台窗口,按下“Ctrl+C” 键, 让BlueFi进入REPL模式,然后在“>>>”提示符后依次输入以下语句并按“Enter”键执行每一个语句:
1 2 3 4 | >>> import time
>>> from hiibot_bluefi.basedio import LED
>>> led = LED()
>>> led.red = 1
|
观察红色LED灯的状态,再输入:
1 | >>> led.red = 0
|
可以看到,等号右边为1时,LED灯亮;等号右边为0时,LED灯灭。
除了red和white之外,在“LED”类中还有多少个变量或函数?这个问题的答案仍可以求助于REPL。 在REPL的提示符“>>”依次执行以下语句,最后一句是输入“led.”并按Tab键,REPL将把led对象的全部属性和方法都列举出来。
1 2 3 4 5 | >>> import time
>>> from hiibot_bluefi.basedio import LED
>>> led = LED()
>>> led.
red white redToggle whiteToggle
|
这些信息并不能确定led对象所支持的4个接口是什么,如果我们使用“help(led)”将会得到更详细的信息。
1 2 3 4 5 6 7 8 9 | >>> help(led)
object <LED object at 20016680> is of type LED
__init__ -- <function __init__ at 0x200381f0>
whiteToggle -- <function whiteToggle at 0x20038190>
white -- <property>
__qualname__ -- LED
redToggle -- <function redToggle at 0x200380a0>
red -- <property>
__module__ -- hiibot_bluefi.basedio
|
其中,red和white分别是led对象的两个属性,redToggle和whiteToggle分别是led对象的两个函数,修改示例程序,观察这两个函数的功能:
1 2 3 4 5 6 7 | import time
from hiibot_bluefi.basedio import LED
led = LED()
while True:
led.whiteToggle()
led.redToggle()
time.sleep(0.5)
|
这个程序中的无穷循环程序块仅有3个语句,其中两个是调用led对象的函数——whiteToggle()和redToggle(),最后一个仍是延时0.5秒。 从程序执行效果看,红色LED灯和白色LED灯同时亮灭。说明led对象的whiteToggle()函数和redToggle()函数是在切换LED的亮和灭。
5.1.2 呼吸的LED灯¶
在上一小节中我们已经掌握从hiibot_bluefi.basedio中导入LED类,并实例化为led,然后对red和white两个属性赋不同的值, 分别实现红色LED和白色LED的亮/灭控制,或者调用led对象的两个函数:redToggle()或whiteToggle()分别控制红色LED和白色LED的状态切换, 也能达到指示灯闪烁的目的。
如果我们能够改变LED指示灯的亮度,可以让LED指示灯输出更多种计算机系统状态,譬如工作状态、空闲状态、故障状态等。 这一小节我们来学习控制LED亮度的方法。
我们首先以BlueFi的白色LED为例,实现其亮度控制。BlueFi的白色LED主要作用是为集成光学传感器提供辅助光,根据不同的光照环境, 能够调节辅助光的亮度是基本的需求。
1. 白色LED灯的亮度控制¶
我们已经知道REPL可以帮助我们了解一个实体对象的具体属性和函数等信息,我们在执行下面示例程序前,可以先在REPL中依次输入以下语句:
1 2 3 4 | >>> from hiibot_bluefi.basedio import PWMLED
>>> led=PWMLED()
>>> led.
red white
|
显然,PWMLED类的实体对象led仅有两个属性,再无其他接口!然后,仍在REPL模式输入以下语句,并按“Enter”键后观察白色LED的亮度:
1 2 3 4 | >>> led.white = 100
>>> led.white = 1000
>>> led.white=65535
>>> led.white=0
|
显然,我们能够用程序来控制白色LED灯的亮度,只要赋予led对象的white属性不同的值,亮度随着值的大小变化。 但是这种控制LED亮度的原理是什么?PWM信号。
2. PWM¶
PWM, 脉冲宽度调制(Pulse-Width Modulation)的英文缩写。这是一种计算机系统内常用的特殊数字信号,这种信号的形式是方波, 频率是固定不变的,但是高电平的宽度与周期的比值是可调节的,俗称“脉冲(高电平)宽度(可)调变”信号。根据前一节的所知道的, led.white=1时白色LED灯亮,为0时灭。PWM方法调节LED亮度的原理是,以固定的周期,如1000ns,其中500ns让LED亮,500ns让LED灭, LED的实际亮度为最大亮度的1/2;其中250ns亮,750ns灭,LED的实际亮度为最大亮度的1/4;如果0ns亮,1000ns灭,实际上LED是灭掉的; 如果1000ns亮,0ns灭,实际上LED处于最亮状态;..。
由此,我们可以推断出PWM控制LED亮度的数学计算如下:
亮度 = (高电平的宽度)/(PWM周期) * 最大亮度
当高电平的宽度刚好等于PWM周期时,LED最亮;当高电平的宽度为0时,LED灭;当高电平宽度为PWM周期的1/4时,LED亮度就是最大亮度的1/4。
在hiibot_bluefi.basedio模块的PWMLED类中,PWM周期固定为1ms(正好1000ns),每一个周期被分割为2^16(即65536)份, 也就是所说PWM信号的高电平宽度定义为“v*(1000ns/65536)”,当v=65535时,高电平宽度几乎等于PWM信号的一个周期(即LED最亮)。 我们习惯上把这里的“v”称作占空比,其有效的取值范围为:0~65535。
至此,你能与REPL模式下执行”led.white=v”时随着v的大小亮度随之变化的规律对上了吗?
3. 呼吸灯¶
在MU编辑器中输入下面的程序,并保存到/CIRCUITPY/code.py文件,让BlueFi执行这个示例的程序, 观察白色LED亮度的变化规律:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import time
from hiibot_bluefi.basedio import PWMLED
led = PWMLED()
b=0
d=1
while True:
led.white = b
b += 655 if d==1 else -655
if b>65535:
b=65535
d=0
if b<0:
b=0
d=1
time.sleep(0.01)
|
仔细观察本示例程序的执行效果,感觉到BlueFi在呼吸了吗?白色LED灯的亮度逐渐变到最亮,然后又逐渐熄灭,如此往复。 为什么有这种效果?下面我们逐行来分析每行脚本程序的效果。
- 第1行,导入一个Python内建的模块“time”。
- 第2行,从“/CIRCUITPY/lib/hiibot_bluefi/basedio.py”模块中导入一个名叫“PWMLED”的类。
- 第3行,将导入的“PWMLED”类实例化为一个实体对象,名叫“led”。
- 第4行,创建变量b,存放亮度大小。
- 第5行,创建变量d,标志位,为1时代表亮度逐渐变量的过程;为0时代表亮度逐渐变暗的过程。
- 第6行,开始一个无穷循环的程序块。
- 第7行(无穷循环程序块的第1行),led对象的white属性(led.white)设置为变量b的值.
- 第8行(无穷循环程序块的第2行),判断变量d的值是否为1,若为1,b的值增加655;若为0,b的值减少655。
- 第9行(无穷循环程序块的第3行),判断变量b的值是否大于65535。
- 第10行(无穷循环程序块的第4行),如果b>65535,则b=65535。
- 第11行(无穷循环程序块的第5行),如果b>65535,则d=0。
- 第12行(无穷循环程序块的第6行),判断变量b的值是否小于0。
- 第13行(无穷循环程序块的第7行),如果b<0,则b=0。
- 第14行(无穷循环程序块的第8行),如果b<0,则d=1。
- 第15行(无穷循环程序块的第8行),执行time的sleep方法,参数为0.01秒(即10ms)。
这段程序能够让白色LED亮度以呼吸节奏改变亮度,其中的关键之处是变量b的变化规律。第8~14行程序都是在增加或减少b变量的值。
你能用一句既贴切又合适的话来概括变量b的变化规律吗?