from machine import Pin, I2C, RTC, Timer, PWM, ADC
import machine
import ssd1306
import utime
import time
import pic
import uasyncio as asyncio
i2c = I2C(0, scl=Pin(22), sda=Pin(21))
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
button_A = machine.Pin(5, machine.Pin.IN, machine.Pin.PULL_UP)
button_B = machine.Pin(35, machine.Pin.IN, machine.Pin.PULL_UP)
# Define the speaker pin and PWM object
speaker_pin = Pin(14)
speaker_pwm = PWM(speaker_pin)
speaker_pwm.duty(0)
standard_to_freq = {
'0G': 196 ,
'C' : 262 ,
'C#': 277 ,
'D' : 294 ,
'D#': 311 ,
'E' : 330 ,
'F' : 349 ,
'F#': 370 ,
'G' : 392 ,
'G#': 415 ,
'A' : 440 ,
'A#': 466 ,
'B' : 494 ,
'C1': 523 ,
'0' : 1,
}
button_stateA = 1
button_stateB = 1
button_press_timeA = 0
button_press_timeB = 0
long_press_threshold = 1000
current_timeA = time.ticks_ms()
current_timeB = time.ticks_ms()
#0=press 1=unpress
def button_callback1(pin):
global button_stateA, button_press_timeA, current_timeA
current_timeA = time.ticks_ms()
button_stateA = pin.value()
if button_stateA == 0:
button_press_timeA = current_timeA
def button_callback2(pin):
global button_stateB, button_press_timeB, current_timeB
current_timeB = time.ticks_ms()
button_stateB = pin.value()
if button_stateB == 0:
button_press_timeB = current_timeB
button_A.irq(trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING, handler=button_callback1)
button_B.irq(trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING, handler=button_callback2)
def draw_oled(oled, data, x, y):
for row in range(64):
for byte_index in range(16):
byte_val = data[row * 16 + byte_index]
x_0 = x + byte_index * 8
y_0 = y + row
for bit in range(8):
if byte_val & (1 << (7 - bit)):
oled.pixel(x_0 + bit, y_0, 1)
def draw_16(oled, data, x, y):
for row in range(16):
for byte_index in range(2):
byte_val = data[row * 2 + byte_index]
x_0 = x + byte_index * 8
y_0 = y + row
for bit in range(8):
if byte_val & (1 << (7 - bit)):
oled.pixel(x_0 + bit, y_0, 1)
def draw_24(oled, data, x, y):
for row in range(24):
for byte_index in range(3):
byte_val = data[row * 3 + byte_index]
x_0 = x + byte_index * 8
y_0 = y + row
for bit in range(8):
if byte_val & (1 << (7 - bit)):
oled.pixel(x_0 + bit, y_0, 1)
def clear_region(oled, x, y, width, height):
for row in range(height):
for col in range(width):
oled.pixel(x + col, y + row, 0)
class Note:
def __init__(self, x, y, speed):
self.x = x
self.y = y
self.speed = speed
class NoteAnimation:
def __init__(self, music_frames, point_frames):
self.frames = music_frames
self.point_frames = point_frames
self.frame_index = 0
self.point_frame_index = 0
self.note = Note(57, -8, 2)
def play_frame(self):
oled.fill(0)
draw_16(oled, self.frames[self.frame_index], self.note.x, self.note.y)
self.frame_index = (self.frame_index + 1) % len(self.frames)
def bottom_animation(self):
draw_24(oled, self.point_frames[self.frame_index], 53, 40)
self.point_frame_index = (self.point_frame_index + 1) % len(self.point_frames)
def update_note_position(self):
if self.note.y <= 30: # 如果接近 bottomAnimation 的位置
self.note.speed = 2
if self.note.y > 40: # 如果接近 bottomAnimation 的位置
self.note.speed = 1
self.note.y += self.note.speed
if self.note.y > 64:
self.note.y = -8
# 初始化音符动画
animation = NoteAnimation([pic.music, pic.music2, pic.music3, pic.music4], [pic.point, pic.point2, pic.point3, pic.point4])
def check_button(timer):
if button_stateA == 0 and press_durationA <= long_press_threshold:
# 检测按钮按下时的碰撞
if 55 <= animation.note.x <= 59 and 40 <= animation.note.y <= 48:
draw_24(oled, pic.perfectsign, 55, 42)
draw_24(oled, pic.perfect, 53, 25)
oled.show()
utime.sleep_ms(200)
elif 50 <= animation.note.x <= 64 and 35 <= animation.note.y <= 53:
draw_24(oled, pic.good, 52, 22)
oled.show()
utime.sleep_ms(200)
elif 0 <= animation.note.x <= 128 and 0 <= animation.note.y <= 64:
oled.fill(0)
draw_24(oled, pic.badsign, 55, 42)
draw_24(oled, pic.bad, 52, 23)
oled.show()
utime.sleep_ms(200)
else:
pass
# 重置音符位置
animation.note.y = -8
# 定时器,每隔一定时间调用 check_button
button_timer = Timer(1)
button_timer.init(period=50, mode=Timer.PERIODIC, callback=lambda t: check_button(t))
melody_bee = [
(392, 'quarter'), # G4 四分音符
(440, 'quarter'), # A4 四分音符
(494, 'quarter'), # B4 四分音符
(392, 'quarter'), # G4 四分音符
(392, 'quarter'), # G4 四分音符
(440, 'quarter'), # A4 四分音符
(494, 'quarter'), # B4 四分音符
(392, 'quarter'), # G4 四分音符
(349, 'quarter'), # F4 四分音符
(392, 'quarter'), # G4 四分音符
(440, 'quarter'), # A4 四分音符
(349, 'quarter'), # F4 四分音符
(349, 'quarter'), # F4 四分音符
(392, 'quarter'), # G4 四分音符
(440, 'quarter'), # A4 四分音符
(392, 'quarter') # G4 四分音符
]
current_note = None
async def play_note(note, duration):
global current_note
if current_note == note:
speaker_pwm.deinit()
await asyncio.sleep(0.065) # 使用秒作为单位,65毫秒转为秒
speaker_pwm.init()
duration -= 65
speaker_pwm.freq(note)
current_note = note
print("PWM Frequency: {}".format(note)) # 添加调试输出
await asyncio.sleep(duration / 1000) # 将毫秒转为秒
await asyncio.sleep(0.1) # 使用秒作为单位,100毫秒转为秒
async def play_melody(melody, repeat=1):
global speaker_pwm
try:
for _ in range(repeat):
for note, duration_type in melody:
if duration_type == 'quarter':
duration = 500
elif duration_type == 'eighth':
duration = 250
elif duration_type == 'half':
duration = 1000
else:
duration = 500
await play_note(note, duration)
await asyncio.sleep(0.02) # 使用秒作为单位,20毫秒转为秒
finally:
speaker_pwm.deinit() # 在退出函数时关闭 PWM
async def button_task():
global current_timeB
while True:
press_durationB = current_timeB - button_press_timeB
current_timeB = time.ticks_ms()
if button_stateB == 0 and press_durationB <= long_press_threshold:
asyncio.create_task(main())
else:
pass
async def main():
print("Entering main")
await play_melody(melody_bee, repeat=1)
print("Exiting main")
speaker_pwm.deinit()
# 主循环
while True:
press_durationA = current_timeA - button_press_timeA
press_durationB = current_timeB - button_press_timeB
current_timeA = time.ticks_ms()
current_timeB = time.ticks_ms()
animation.update_note_position()
animation.play_frame()
animation.bottom_animation()
oled.show()
# 在主循环中运行按钮任务
asyncio.create_task(button_task())
print(button_stateB)
utime.sleep_ms(20)