MicroPython是为了在嵌入式系统中运行Python 3编程语言而设计的轻量级版本解释器。与常规Python相比,MicroPython解释器体积小(仅100KB左右),通过编译成二进制Executable文件运行,执行效率较高。它使用了轻量级的垃圾回收机制并移除了大部分Python标准库,以适应资源限制的微控制器。
MicroPython主要特点包括:
1、语法和功能与标准Python兼容,易学易用。支持Python大多数核心语法。
2、对硬件直接访问和控制,像Arduino一样控制GPIO、I2C、SPI等。
3、强大的模块系统,提供文件系统、网络、图形界面等功能。
4、支持交叉编译生成高效的原生代码,速度比解释器快10-100倍。
5、代码量少,内存占用小,适合运行在MCU和内存小的开发板上。
6、开源许可,免费使用。Shell交互环境为开发测试提供便利。
7、内置I/O驱动支持大量微控制器平台,如ESP8266、ESP32、STM32、micro:bit、掌控板和PyBoard等。有活跃的社区。
MicroPython的应用场景包括:
1、为嵌入式产品快速构建原型和用户交互。
2、制作一些小型的可 programmable 硬件项目。
3、作为教育工具,帮助初学者学习Python和物联网编程。
4、构建智能设备固件,实现高级控制和云连接。
5、各种微控制器应用如物联网、嵌入式智能、机器人等。
使用MicroPython需要注意:
1、内存和Flash空间有限。
2、解释执行效率不如C语言。
3、部分库函数与标准版有差异。
4、针对平台优化语法,订正与标准Python的差异。
5、合理使用内存资源,避免频繁分配大内存块。
6、利用原生代码提升速度关键部位的性能。
7、适当使用抽象来封装底层硬件操作。
总体来说,MicroPython让Python进入了微控制器领域,是一项重要的创新,既降低了编程门槛,又提供了良好的硬件控制能力。非常适合各类物联网和智能硬件的开发。
RP2(Pico)是树莓派基金会推出的一款微控制器开发板,基于自研的 RP2040 芯片,售价仅为 4 美元。它可以用 C/C++ 或 Python 语言编程,适合用于物联网、机器人、音乐等各种应用场景。技术参数:RP2(Pico)的技术参数如下:
1、搭载双核 ARM Cortex M0+ 处理器,运行频率 133 MHz
2、内置 264 KB 的片上 RAM,板载 2 MB 闪存
3、可通过专用 QSPI 总线支持最高 16 MB 的片外闪存
4、DMA 控制器
5、30 个 GPIO 引脚,其中 4 个可用作模拟输入
6、2 个 UART、2 个 SPI 控制器和 2 个 I2C 控制器
7、16 个 PWM 通道
8、USB 1.1 主机和设备支持
9、8 个树莓派可编程 I/O(PIO)状态机,用于自定义外围设备支持
10、支持 UF2 的 USB 大容量存储启动模式,用于拖放式编程
MicroPython的RP2(Pico)计时器是一种用于定时和计时任务的功能模块,它在嵌入式系统和物联网应用中具有广泛的应用。
主要特点:
精确计时能力:RP2(Pico)计时器提供了精确的计时功能,可以以微秒级或毫秒级的精度测量时间。这种高精度的计时能力使得它非常适合需要准确计时的应用场景,如数据采集、测量、调度等。
多种计时模式:RP2(Pico)计时器支持多种计时模式,包括单次触发模式、周期性触发模式和输入捕获模式。单次触发模式用于执行一次性的任务或延迟执行任务,周期性触发模式用于定时执行任务,而输入捕获模式用于捕获外部事件的时间戳。
多个计时器实例:RP2(Pico)计时器提供多个独立的计时器实例,可以同时处理多个计时任务。每个计时器实例都有自己的配置和回调函数,可以灵活地根据具体需求进行设置和管理。
应用场景:
嵌入式系统:RP2(Pico)计时器在嵌入式系统中具有广泛的应用。它可以用于精确的任务调度、周期性数据采集、定时触发传感器读取等场景,以满足实时性要求。
物联网应用:在物联网应用中,RP2(Pico)计时器可以用于设备之间的时序同步、事件触发和定时任务执行。例如,可以利用计时器实现多个传感器数据的同步采集,或者按照预定时间间隔发送数据等。
控制系统:RP2(Pico)计时器在控制系统中起到关键作用。它可以用于周期性控制任务的调度,例如定时控制电机转动、周期性检测传感器状态等。通过精确的计时能力,可以确保控制系统按照预期的时间间隔执行任务,提高系统的稳定性和可靠性。
需要注意的事项:
计时器资源限制:RP2(Pico)上的计时器资源是有限的,因此在使用计时器时需要注意资源的分配和管理。如果需要多个计时器实例,确保合理分配计时器资源,并避免资源冲突。
中断处理:RP2(Pico)计时器通常通过中断来触发计时任务。在编写计时器的回调函数时,应注意避免阻塞或长时间执行的操作,以免影响系统的响应性能。
精度误差:尽管RP2(Pico)计时器提供了高精度的计时能力,但在实际应用中,由于硬件和软件的限制,可能存在一定的计时误差。在对计时精度要求较高的应用中,需要进行适当的校准和补偿,以确保精确的计时结果。
总之,MicroPython的RP2(Pico)计时器是一种强大的计时和定时功能模块,具有精确计时能力、多种计时模式和多个计时器实例等特点。它在嵌入式系统和物联网应用中有广泛的应用。在使用RP2(Pico)计时器时,需要注意合理分配计时器资源、避免阻塞操作、处理计时精度误差等事项。这样才能充分利用计时器的功能,满足实时性要求,并提高系统的稳定性和可靠性。
参考代码案例:
案例1:控制 LED 闪烁:这是一个简单的程序,用于控制 Pico 开发板上的内置 LED(GPIO 25)闪烁。代码如下:
# 导入 machine 模块,用于控制 GPIO 和 Timer
import machine
# 创建一个 Pin 对象,表示 GPIO 25 引脚
led = machine.Pin(25, machine.Pin.OUT)
# 创建一个 Timer 对象,设置为周期性模式,周期为 1 秒
tim = machine.Timer(period=1000, mode=machine.Timer.PERIODIC)
# 定义一个回调函数,用于切换 led 引脚的电平
def toggle(t):
led.toggle()
# 将回调函数绑定到 Timer 对象上
tim.callback(toggle)
测量排序算法时间:这是一个稍微复杂的程序,用于测量不同排序算法(冒泡排序和选择排序)对同一组数据进行排序所需的时间。代码如下:
# 导入 time 模块,用于计时
import time
# 导入 random 模块,用于生成随机数据
import random
# 定义一个冒泡排序算法的函数
def bubble_sort(data):
# 获取数据的长度
n = len(data)
# 遍历数据的每个元素
for i in range(n):
# 遍历数据的剩余元素
for j in range(n - i - 1):
# 如果前一个元素大于后一个元素,则交换它们的位置
if data[j] > data[j + 1]:
data[j], data[j + 1] = data[j + 1], data[j]
# 定义一个选择排序算法的函数
def selection_sort(data):
# 获取数据的长度
n = len(data)
# 遍历数据的每个元素
for i in range(n):
# 假设当前元素是最小的元素
min_index = i
# 遍历数据的剩余元素
for j in range(i + 1, n):
# 如果找到比当前元素更小的元素,则更新最小元素的索引
if data[j] < data[min_index]:
min_index = j
# 如果最小元素不是当前元素,则交换它们的位置
if min_index != i:
data[i], data[min_index] = data[min_index], data[i]
# 创建一个空列表,用于存储随机数据
data = []
# 生成 1000 个随机整数,并添加到列表中
for i in range(1000):
data.append(random.randint(0, 10000))
# 复制一份原始数据,用于冒泡排序
data1 = data.copy()
# 获取当前的微秒计数器值,作为冒泡排序的开始时间
start1 = time.ticks_us()
# 调用冒泡排序算法的函数,对数据进行排序
bubble_sort(data1)
# 获取当前的微秒计数器值,作为冒泡排序的结束时间
end1 = time.ticks_us()
# 计算冒泡排序所需的时间,单位为微秒
time1 = time.ticks_diff(end1, start1)
# 复制一份原始数据,用于选择排序
data2 = data.copy()
# 获取当前的微秒计数器值,作为选择排序的开始时间
start2 = time.ticks_us()
# 调用选择排序算法的函数,对数据进行排序
selection_sort(data2)
# 获取当前的微秒计数器值,作为选择排序的结束时间
end2 = time.ticks_us()
# 计算选择排序所需的时间,单位为微秒
time2 = time.ticks_diff(end2, start2)
# 在 REPL 上打印两种排序算法的时间比较结果
print("Bubble sort time: {} us".format(time1))
print("Selection sort time: {} us".format(time2))
案例3:生成 PWM 信号:这是一个较为高级的程序,用于生成 PWM(脉宽调制)信号来控制电机或 LED。代码如下:
# 导入 machine 模块,用于控制 GPIO 和 Timer
import machine
# 创建一个 Pin 对象,表示 GPIO 0 引脚(连接电机或 LED)
pin = machine.Pin(0, machine.Pin.OUT)
# 创建一个 Timer 对象,设置为 PWM 模式,频率为 50 Hz
tim = machine.Timer(freq=50, mode=machine.Timer.PWM)
# 定义一个回调函数,用于改变 PWM 的占空比
def change_duty(t):
# 获取当前的占空比
duty = t.duty_u16()
# 如果占空比达到最大值,则减小占空比
if duty == 65535:
t.duty_u16(duty - 1000)
# 如果占空比达到最小值,则增大占空比
elif duty == 0:
t.duty_u16(duty + 1000)
# 否则,根据当前的方向来改变占空比
else:
direction = t.direction()
if direction == 1:
t.duty_u16(duty + 1000)
else:
t.duty_u16(duty - 1000)
# 将回调函数绑定到 Timer 对象上,并设置周期为 0.1 秒
tim.callback(change_duty, period=0.1)
# 将 Timer 对象连接到 Pin 对象上,开始输出 PWM 信号
tim.channel(pin)
案例4:使用计时器实现周期性的任务调度:
import machine
import utime
# 定义任务1的回调函数
def task1_callback(timer):
print("执行任务1")
# 定义任务2的回调函数
def task2_callback(timer):
print("执行任务2")
# 初始化计时器1
timer1 = machine.Timer()
# 设置计时器1参数
timer1.init(mode=machine.Timer.PERIODIC, period=1000, callback=task1_callback)
# 初始化计时器2
timer2 = machine.Timer()
# 设置计时器2参数
timer2.init(mode=machine.Timer.PERIODIC, period=500, callback=task2_callback)
# 主循环
while True:
# 执行其他任务
# ...
utime.sleep_ms(100) # 给其他任务留出时间片
这个程序使用计时器实现周期性的任务调度。
定义了两个任务的回调函数 task1_callback 和 task2_callback,分别在计时器触发时执行相应的任务。
使用 machine.Timer() 初始化两个计时器对象 timer1 和 timer2。
分别使用 timer1.init() 和 timer2.init() 方法设置计时器的参数,参数 mode 设置定时器模式为周期性,period 设置计时器的周期,单位是毫秒,callback 设置计时器触发时调用的回调函数。
在主循环中可以执行其他任务,通过调用 utime.sleep_ms() 方法留出时间片给其他任务。
案例5:使用计时器实现一次性延迟任务:
import machine
import utime
# 定义延迟任务的回调函数
def delay_task_callback(timer):
print("延迟任务执行")
# 初始化计时器
timer = machine.Timer()
# 设置延迟任务的参数
timer.init(mode=machine.Timer.ONE_SHOT, period=5000, callback=delay_task_callback)
# 主循环
while True:
# 执行其他任务
# ...
utime.sleep_ms(100) # 给其他任务留出时间片
这个程序使用计时器实现一次性的延迟任务。
定义了延迟任务的回调函数 delay_task_callback,在计时器触发时执行延迟任务。
使用 machine.Timer() 初始化计时器对象 timer。
使用 timer.init() 方法设置延迟任务的参数,参数 mode 设置定时器模式为一次性,period 设置延迟任务的延迟时间,单位是毫秒,callback 设置计时器触发时调用的回调函数。
在主循环中可以执行其他任务,通过调用 utime.sleep_ms() 方法留出时间片给其他任务。
案例6:使用计时器实现精确的时间测量:
import machine
import utime
# 定义开始时间和结束时间
start_time = 0
end_time = 0
# 定义计时器回调函数
def timer_callback(timer):
global end_time
end_time = utime.ticks_us()
# 初始化计时器
timer = machine.Timer()
# 设置计时器参数
timer.init(mode=machine.Timer.ONE_SHOT, period=5000, callback=timer_callback)
# 执行需要计时的代码
# ...
# 获取经过的时间
elapsed_time = utime.ticks_diff(end_time, start_time)
print("执行时间:", elapsed_time, "微秒")
这个程序使用计时器实现精确的时间测量。定义了开始时间 start_time 和结束时间 end_time,用于记录时间戳。定义了计时器回调函数 timer_callback,在计时器触发时记录结束时间。使用 machine.Timer() 初始化计时器对象 timer。使用 timer.init() 方法设置计时器的参数,参数 mode 设置定时器模式为一次性,period 设置计时器的延迟时间,单位是毫秒,callback 设置计时器触发时调用的回调函数。执行需要计时的代码段之前,使用 utime.ticks_us() 方法获取开始时间,返回的是微秒级的时间戳。在计时器触发的回调函数中,使用 utime.ticks_us() 方法获取结束时间,并将其赋值给全局变量 end_time。在需要计时的代码段之后,使用 utime.ticks_diff() 方法计算经过的时间,参数是结束时间和开始时间,返回的是微秒级的时间差。通过打印语句输出执行时间。这样可以实现对代码段执行时间的精确测量。
案例7:使用RP2(Pico)计时器测量时间间隔
import time
from machine import Pin, Timer
# 初始化引脚和计时器
pin = Pin(13, Pin.OUT)
timer = Timer(-1)
# 设置计时器回调函数
def callback(timer):
print('Timer expired!')
# 启动计时器,每隔1秒触发一次回调函数
timer.init(period=1000, mode=Timer.PERIODIC, callback=callback)
# 主循环
while True:
pin.value(1) # 设置引脚为高电平
time.sleep_ms(500) # 延时500毫秒
pin.value(0) # 设置引脚为低电平
time.sleep_ms(500) # 延时500毫秒
案例8:使用RP2(Pico)计时器实现倒计时功能
import time
from machine import Pin, Timer
# 初始化引脚和计时器
pin = Pin(13, Pin.OUT)
timer = Timer(-1)
# 设置计时器回调函数
def callback(timer):
print('Timer expired!')
if timer.time >= 10: # 倒计时10秒
timer.cancel() # 取消计时器
pin.value(0) # 设置引脚为低电平
else:
timer.init(period=1000, mode=Timer.PERIODIC, callback=callback)
# 启动计时器,每隔1秒触发一次回调函数
timer.init(period=1000, mode=Timer.PERIODIC, callback=callback)
# 主循环
while True:
pass
案例9:使用RP2(Pico)计时器实现闹钟功能
import time
from machine import Pin, Timer
# 初始化引脚和计时器
pin = Pin(13, Pin.OUT)
timer = Timer(-1)
# 设置计时器回调函数
def callback(timer):
print('Alarm!')
timer.cancel() # 取消计时器
pin.value(0) # 设置引脚为低电平
# 设置闹钟时间(单位:毫秒)
alarm_time = 5000 # 闹钟响铃时间为5秒后
# 启动计时器,每隔1秒触发一次回调函数
timer.init(period=1000, mode=Timer.PERIODIC, callback=callback)
# 等待闹钟时间到达
while timer.time < alarm_time:
pass
# 取消计时器
timer.cancel()
案例10:简单的计时器程序
import machine
import utime
# 配置GPIO引脚为输出模式
led = machine.Pin(25, machine.Pin.OUT)
def timer_callback():
print("计时器回调函数被触发!")
# 创建计时器对象,设置回调函数和超时时间
timer = utime.Timer(1.0, timer_callback)
while True:
led.toggle() # 翻转LED状态
utime.sleep(0.5) # 等待0.5秒
# 重启计时器
timer.init(1.0, timer_callback)
案例11:倒计时程序
import machine
import utime
# 配置GPIO引脚为输出模式
led = machine.Pin(25, machine.Pin.OUT)
def timer_callback():
print("计时器回调函数被触发!")
# 创建计时器对象,设置回调函数和超时时间
timer = utime.Timer(1.0, timer_callback)
# 设置倒计时时间(单位:秒)
countdown_time = 10
while True:
led.toggle() # 翻转LED状态
utime.sleep(0.5) # 等待0.5秒
# 更新倒计时时间,并显示剩余时间
countdown_time -= 0.5
print("剩余时间:", countdown_time, "秒")
# 如果倒计时时间到达0,则停止计时器并退出循环
if countdown_time <= 0:
timer.deinit() # 停止计时器
break
案例12:周期性触发程序
import machine
import utime
# 配置GPIO引脚为输出模式
led = machine.Pin(25, machine.Pin.OUT)
def timer_callback():
print("计时器回调函数被触发!")
led.toggle() # 翻转LED状态
# 创建计时器对象,设置回调函数和超时时间,并设置周期性触发标志位
timer = utime.Timer(1.0, timer_callback, periodic=True)
while True:
utime.sleep(0.5) # 等待0.5秒
请注意,以上案例只是为了拓展思路,可能存在错误或不适用的情况。不同的硬件平台、使用场景和MicroPython版本可能会导致不同的使用方法。在实际编程中,您需要根据您的硬件配置和具体需求进行调整,并进行多次实际测试。