This commit is contained in:
liubiren 2025-11-24 08:56:19 +08:00
parent 134384051f
commit a028edf842
2 changed files with 138 additions and 35 deletions

View File

@ -7,8 +7,8 @@ led = Pin(0, Pin.OUT)
toggle_switch = MTS102(1) toggle_switch = MTS102(1)
while True: while True:
if toggle_switch.switched: if toggle_switch.switched:
led.on() led.on()
else: else:
led.off() led.off()
utime.sleep_ms(200) utime.sleep_ms(200)

View File

@ -1,7 +1,3 @@
import utime
from machine import Pin
class MTS102: class MTS102:
"""MTS102三脚二档钮子开关""" """MTS102三脚二档钮子开关"""
@ -15,6 +11,9 @@ class MTS102:
self.pull_down = pull_down self.pull_down = pull_down
import utime
from machine import Pin
# 尝试初始化引脚 # 尝试初始化引脚
try: try:
self.pin = Pin( self.pin = Pin(
@ -63,10 +62,12 @@ class WS2812:
raise TypeError("led_beads数据类型应为整数") raise TypeError("led_beads数据类型应为整数")
if not led_beads >= 1: if not led_beads >= 1:
raise ValueError("led_beads数值应大于等于1") raise ValueError("led_beads应大于等于1")
self.led_beads = led_beads self.led_beads = led_beads
import utime
from machine import Pin
from neopixel import NeoPixel from neopixel import NeoPixel
# 尝试初始化LED # 尝试初始化LED
@ -98,7 +99,7 @@ class WS2812:
raise TypeError("通道数据数据类型应为整数") raise TypeError("通道数据数据类型应为整数")
if not 0 <= channel_value <= 255: if not 0 <= channel_value <= 255:
raise ValueError("通道数据数值应在0255范围内") raise ValueError("通道数据应在0255范围内")
for bead_idx, bead_color in enumerate(colors): for bead_idx, bead_color in enumerate(colors):
# 将RGB转为GRB并设置灯珠颜色 # 将RGB转为GRB并设置灯珠颜色
@ -115,51 +116,153 @@ class WS2812:
raise TypeError("hue_offset数据类型应为整数") raise TypeError("hue_offset数据类型应为整数")
if not 0 <= hue_offset <= 60: if not 0 <= hue_offset <= 60:
raise ValueError("hue_offset数值应在060范围内") raise ValueError("hue_offset应在060范围内")
# 初始化HSV颜色空间参数其中hue为色相saturation为饱和度value为亮度 # 初始化HSV颜色空间参数其中hue为色相saturation为饱和度value为亮度
hue, saturation, value = 0, 255, 128 hue, saturation, value = 0, 255, 128
while True: while True:
# 初始化颜色列表
colors = [] colors = []
for bead_idx in range(self.led_beads): for bead_idx in range(self.led_beads):
# 色相 # 色相
hue = (hue + bead_idx * hue_offset) % 360 hue = (hue + bead_idx * hue_offset) % 360
# 色域 # 色域
region = hue // 60 region = hue // 60
# 色域内相对位置 # 色域内相对位置
remainder = (hue - region * 60) * 255 // 60 remainder = (hue % 60) * 255 // 60
# RGB颜色通道中的最小值分量
primary = (value * (255 - saturation)) // 255
# RGB颜色通道中的过渡分量
tertiary = (value * remainder) // 255
# 最小分量 # 根据色域将HSV转为RGB
P = (value * (255 - saturation)) // 255 if region == 0:
# 过渡值 color = (value, tertiary, primary) # 由红色渐变为黄色
transition = (value * remainder) // 255 elif region == 1:
color = (value - tertiary, value, primary) # 由黄色渐变为绿色
# 根据色域将HSV转为RGB并新增至颜色列表 elif region == 2:
colors.append( color = (primary, value, tertiary) # 由绿色渐变为青色
{ elif region == 3:
0: (value, transition, P), # 由红色渐变为黄色 color = (primary, value - tertiary, value) # 由青色渐变为蓝色
1: (value - transition, value, P), # 由黄色渐变为绿色 elif region == 4:
2: (P, value, transition), # 由绿色渐变为青色 color = (tertiary, primary, value) # 由蓝色渐变为洋红色
3: (P, value - transition, value), # 由青色渐变为蓝色 else:
4: (transition, P, value), # 由蓝色渐变为洋红色 color = (value, primary, value - tertiary) # 由洋红色渐变为红色
5: (value, P, value - transition), # 由洋红色渐变为红色
}[region] colors.append(color)
)
self.set_colors(colors) self.set_colors(colors)
# 根据色域动态调整色相步长 # 根据色域动态调整色相步长
hue = hue + [1, 1, 1, 1, 1, 1][region] hue = hue + [1, 3, 3, 3, 3, 1][region]
utime.sleep_ms(100) utime.sleep_ms(20)
class Servo: class Servo:
"""舵机(基类)""" """舵机(基类)"""
# 运动轨迹
_TRAJECTORIES = { class Easing:
"linear": lambda x: (x, 1), # 表示当前进度和速度 """缓动曲线对象"""
def __init__(self, function, density):
"""
:param function: 缓动曲线函数X轴为进度0至1Y轴为单位速率
:param density: 步数密度
"""
self.function = function
self.density = density
self.speed = self._calculate_speed()
def _calculate_speed(self, intervals=100):
"""
基于复核中点矩形法计算平均速率
:param intervals: 积分间隔数
"""
return sum(self.function((2 * i + 1) / (2 * intervals))[1] for i in range(intervals)) / intervals
# 缓动曲线
_EASINGS = {
"linear": Easing(lambda x: (x, 1), 1.0),
"ease_in": Easing(lambda x: (x ** 2, 2 * x), 1.5),
"ease_out": Easing(lambda x: (1 - (1 - x) ** 2, 2 * (1 - x)), 1.5),
} }
def __init__(self, pin, frequency=50, min_angle=0, max_angle=180, min_pulsewidth=500, max_pulsewidth=2500, dead_zone=8, duration_per_angle=0.13):
"""
:param pin: 引脚编号
:param frequency: 频率单位为赫兹
:param min_angle: 最小转动角度单位为度
:param max_angle: 最大转动角度单位为度
:param min_pulsewidth: 最小转动角度对应的脉宽单位为微秒
:param max_pulsewidth: 最大转动角度对应的脉宽单位为微秒
:param dead_zone: 死区单位为微秒
:param duration_per_angle: 转动单位角度对应的时长单位为秒/六十度
"""
import utime
from machine import Pin, PWM
# 尝试初始化PWM
try:
self.pwm = PWM(Pin(pin, Pin.OUT))
# 设置频率
self.pwm.freq(frequency)
except Exception as exception:
raise RuntimeError(f"初始化PWM发生异常{str(exception)}") from exception
self.min_angle, self.max_angle = min_angle, max_angle
# 脉宽单位由微秒转为纳秒(默认脉宽单位为纳秒)
self.min_pulsewidth, self.max_pulsewidth = min_pulsewidth * 1000, max_pulsewidth * 1000
# 死区单位由微秒转为纳秒
self.dead_zone = dead_zone * 1000
# 转动单位角度对应的脉宽,单位为纳秒/度
self.pulsewidth_per_angle = (self.max_pulsewidth - self.min_pulsewidth) / (self.max_angle - self.min_angle)
# 转动单位角度对应的时长单位由秒/六十度转为纳秒/度
self.duration_per_angle = duration_per_angle * 1000 * 1000 * 1000 / 60
# 是否旋转中
self.rotating = False
# 当前角度
self.current_angle = self.min_angle
def set_angle(self, target_angle, easing="line"):
"""
设置旋转角度
:param target_angle: 目标转动角度单位为度
:param easing: 缓动函数名称
"""
if not isinstance(target_angle, (int, float)):
raise TypeError("target_angle数据类型应为整数或浮点")
# 限制目标转动角度在最小转动角度至最大转动角度范围之内
target_angle = max(self.min_angle, min(self.max_angle, target_angle))
if not isinstance(easing, str):
raise TypeError("easing数据类型应为字符")
# 缓动函数
self.easing = self._EASINGS.get(easing.low(), self._EASINGS["linear"])
# 若为旋转中则返回设置旋转角度失败
if self.rotating:
return False
# 若目标转动角度对应的脉宽和当前转动角度对应的脉宽差小于死区则返回设置旋转角度成功
if abs(self._angle_to_pulsewidth(target_angle) - self._angle_to_pulsewidth(self.current_angle)) < self.dead_zone:
return True
steps, duration = self._calculate_steps(abs(target_angle - self.current_angle))
def _angle_to_pulsewidth(self, angle):
"""将转动角度转为脉宽"""
pulsewidth = self.min_pulsewidth + (angle - self.min_angle) * self.pulsewidth_per_angle
return int(round(pulsewidth))
def _calculate_steps(self, angle_difference):
"""计算转动步数"""