【进迭时空 K1 MUSE Pi Pro】扩展板设计

进迭时空 K1 MUSE Pi Pro 的扩展板 PCB 设计,包括原理图、资源参数、环境搭建 、工程测试等。

【进迭时空 K1 MUSE Pi Pro】扩展板设计封面
无垠的广袤2026-07-31 18:41:30OpenAtom OHL 1.0
328
Star1

PCBA

设计文件

KiCad图标Easy扩展板.zip290.62KB(7次下载)

EDA查看器

复制嵌入代码

详细介绍

【进迭时空 K1 MUSE Pi Pro】扩展板设计

本文介绍了进迭时空 K1 MUSE Pi Pro 的扩展板 PCB 设计,包括原理图、资源参数、环境搭建 、工程测试等。

项目介绍

  • 扩展板设计:PCB、资源分布、参数特点、原理图等;

  • 环境搭建:安装所需 python 库;

  • 工程测试:驱动 OLED、RTC 时钟、AHT10温湿度计、ADC 呼吸灯、心率传感器等。

扩展板设计

资源

  • DHT11 温湿度传感器

  • 4 路独立按键

  • 4 颗单色 LED

  • 6 颗 WS2812 彩色 LED

  • HX1838 红外接收

  • GL5528 光敏电阻

  • 旋转电位器

  • 无源蜂鸣器

接口

  • 硬件 I2C 接口

  • 0.96 寸 I2C OLED 接口

  • 超声波模块接口

  • CH340E 芯片 USB 转 TTL 串口

  • 预留数字拓展引脚

  • 模拟拓展通道

原理图

PCB

实物图

硬件连接

  • 使用 HDMI 数据线连接显示屏或 SSH 远程登录;

  • USB 接口连接鼠标键盘;

  • 连接 LAN 有线网或连接 Wi-Fi 无线网;

  • 使用 5V 直流电源 Type-C 供电;

若 CPU 温度过高,需安装散热片、风扇等主动制冷装置;

环境搭建

  • 终端执行如下指令,搭建虚拟环境并激活

 sudo apt update
 sudo apt install python3-venv # 安装虚拟环境软件包
 sudo apt install i2c-tools fonts-dejavu-core libgpiod-dev
 python3 -m venv .venv # 创建虚拟环境
 pip config set global.extra-index-url https://git.spacemit.com/api/v4/projects/33/packages/pypi/simple # 更改pip镜像源
 source .venv/bin/activate # 激活虚拟环境
  • 安装所需软件包

 pip install smbus2 # iic library
 pip install luma.oled # oled library
 pip install periphery # gpio library

MINI 副屏

根据扩展板 OLED 接口,设计迷你副屏,显示系统运行参数状态等信息。

代码

终端执行 touch sys_oled.py 创建文件,添加如下代码

 import time
 import psutil
 from luma.core.interface.serial import i2c
 from luma.core.render import canvas
 from luma.oled.device import ssd1306
 from PIL import ImageFont
 ​
 # 加载字体
 font_big = ImageFont.load_default(size=14)
 ​
 # I2C4 总线,i2cdetect -y 4, OLED地址0x3C
 serial = i2c(port=4, address=0x3C)
 device = ssd1306(serial, width=128, height=64)
 ​
 # 获取CPU温度
 def get_cpu_temp():
     try:
         with open("/sys/class/thermal/thermal_zone0/temp", "r") as f:
             temp = int(f.read()) / 1000
         return f"{temp:.1f}C"
     except:
         return "N/A"
 ​
 # 获取内存占用
 def get_mem():
     mem = psutil.virtual_memory()
     used = mem.used / 1024 / 1024 / 1024
     total = mem.total / 1024 / 1024 / 1024
     percent = mem.percent
     return f"RAM:{used:.0f}/{total:.0f}GB {percent}%"
 ​
 # CPU使用率
 def get_cpu():
     cpu_pct = psutil.cpu_percent(interval=0.2)
     return f"CPU:{cpu_pct}%"
 ​
 # 系统运行时间
 def get_uptime():
     upt = time.time() - psutil.boot_time()
     h = int(upt // 3600)
     m = int((upt % 3600) // 60)
     return f"RUN:{h}h{m:02d}m"
 ​
 if __name__ == "__main__":
     print("Board: SpacemiT K1 MUSE Pi Pro")
     print("System Resource Monitor")
     print("OLED update every 2 second")
     try:
         while True:
             cpu_txt = get_cpu()
             temp_txt = get_cpu_temp()
             mem_txt = get_mem()
             up_txt = get_uptime()
 ​
             with canvas(device) as draw:
                 # 清屏
                 draw.rectangle(device.bounding_box, fill="black")
                 # TEXT Display
                 draw.text((0, 16), f"{cpu_txt}  T:{temp_txt}", font=font_big, fill="white")
                 draw.text((0, 32), mem_txt, font=font_big, fill="white")
                 draw.text((0, 48), up_txt, font=font_big, fill="white")
                 draw.text((0, 0), "MUSE Pi Pro", font=font_big, fill="white")
             time.sleep(2)
     except KeyboardInterrupt:
         print("\n退出OLED显示")

保存代码。

效果

  • 终端执行 python sys_oled.py 运行程序;

  • OLED 显示系统运行信息;

RTC 时钟

结合扩展板 OLED 和开发板 RTC 实现桌面动态时钟设计。

代码

终端执行 touch rtc_oled.py 创建文件,添加如下代码

 from luma.core.interface.serial import i2c
 from luma.core.render import canvas
 from luma.oled.device import ssd1306
 from PIL import Image, ImageFont
 import os
 import time
 import datetime
 ​
 # I2C总线4,OLED地址0x3C
 serial = i2c(port=4, address=0x3C)
 device = ssd1306(serial)
 ​
 # 字体
 font_fg = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 22)
 font_date = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14)
 ​
 # 加载帧
 frame_list = []
 for i in range(1, 21):
     img_path = os.path.join("frame", f"{i}.jpg")
     img = Image.open(img_path).convert("1")  # 转为OLED单色位图
     frame_list.append(img)
 ​
 frame_index = 0
 ​
 week_map = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
 ​
 try:
     while True:
         now = datetime.datetime.now()
         date_str = now.strftime("%Y-%m-%d")
         time_str = now.strftime("%H:%M")
         week_str = week_map[now.weekday()]
 ​
         with canvas(device) as draw:
             # 动画
             draw.bitmap((48, 0), frame_list[frame_index], fill=1)
             # Week
             draw.text((1, 2), week_str, font=font_date, fill=1)
             # 时分
             draw.text((1, 20), time_str, font=font_fg, fill=1)
             # 年月日
             draw.text((1, 48), date_str, font=font_date, fill=1)
 ​
         # 循环切换帧
         frame_index = (frame_index + 1) % 20
         time.sleep(0.1)
 ​
 except KeyboardInterrupt:
     # Ctrl+C 退出清屏
     device.clear()

保存代码。

效果

  • 终端执行 python rtc_oled.py 运行程序;

  • OLED 显示日期、时间、动画等;

动态演示

温湿度计

使用杜邦线连接 AHT10 模块和扩展板板载 IIC 接口。

终端执行指令 sudo i2cdetect -y -r 4 显示 IIC4 总线挂载设备的地址;

其中 0x38 为 AHT10 对应地址,0x3c 是 OLED 的地址,0x48 是 PCF8591 的地址。

代码

终端执行 touch aht_oled.py 创建文件,添加如下代码

 #!/usr/bin/env python3
 # AHT10 温湿度 + SSD1306 OLED 显示 - MUSE PI Pro I2C4
 ​
 import time
 import sys
 from PIL import ImageFont
 from luma.core.interface.serial import i2c
 from luma.core.render import canvas
 from luma.oled.device import ssd1306
 ​
 try:
     import smbus2 as smbus
 except ImportError:
     import smbus
 ​
 # ---------------------- 硬件配置 ----------------------
 I2C_BUS = 4
 AHT10_ADDR = 0x38
 OLED_ADDR = 0x3C
 ​
 # AHT10 命令定义
 CMD_INIT = [0xE1, 0x08, 0x00]       # 初始化/校准
 CMD_MEASURE = [0xAC, 0x33, 0x00]    # 触发测量
 CMD_SOFT_RESET = 0xBA               # 软复位
 CMD_STATUS = 0x71                   # 读取状态
 ​
 # OLED初始化
 serial = i2c(port=I2C_BUS, address=OLED_ADDR)
 device = ssd1306(serial, width=128, height=64)
 # 加载粗体字体
 try:
     big_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14)
 except:
     big_font = ImageFont.load_default()
 ​
 # ---------------------- AHT10 驱动类 ----------------------
 class AHT10:
     def __init__(self, bus_num=I2C_BUS, addr=AHT10_ADDR):
         self.bus = smbus.SMBus(bus_num)
         self.addr = addr
         self._init_sensor()
     
     def _init_sensor(self):
         """初始化 AHT10"""
         # 软复位
         self.bus.write_byte(self.addr, CMD_SOFT_RESET)
         time.sleep(0.02)
         
         # 发送初始化命令
         self.bus.write_i2c_block_data(self.addr, CMD_INIT[0], CMD_INIT[1:])
         time.sleep(0.01)
         
         # 检查状态,确认校准标志位
         status = self.bus.read_byte(self.addr)
         if not (status & 0x08):
             print("警告: AHT10 校准未完成,数据可能不准确")
     
     def read(self):
         """
         读取温湿度
         返回: (temperature_C, humidity_RH)
         """
         # 触发测量
         self.bus.write_i2c_block_data(self.addr, CMD_MEASURE[0], CMD_MEASURE[1:])
         time.sleep(0.08)  # 测量等待时间
         
         # 读取 6 字节数据
         data = self.bus.read_i2c_block_data(self.addr, 0x00, 6)
         
         # 检查状态位 bit71=忙,0=完成
         if data[0] & 0x80:
             time.sleep(0.01)
             data = self.bus.read_i2c_block_data(self.addr, 0x00, 6)
         
         # 解析湿度 (20bit)
         humidity_raw = (data[1] << 12) | (data[2] << 4) | (data[3] >> 4)
         humidity = (humidity_raw / 1048576.0) * 100.0
         
         # 解析温度 (20bit)
         temp_raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]
         temperature = (temp_raw / 1048576.0) * 200.0 - 50.0
         
         # 限幅
         humidity = max(0, min(100, humidity))
         
         return round(temperature, 2), round(humidity, 2)
     
     def close(self):
         self.bus.close()
 ​
 # ---------------------- 主函数:终端打印 + OLED显示 ----------------------
 def main():
     print("=" * 40)
     print("  AHT10 温湿度传感器 + OLED显示")
     print("  I2C Bus: 4, AHT10:0x38 OLED:0x3C")
     print("=" * 40)
     
     try:
         sensor = AHT10()
         
         while True:
             temp, humi = sensor.read()
             # 终端打印温湿度
             print(f"温度: {temp:>6.2f} °C    湿度: {humi:>6.2f} %RH")
             
             # OLED Display
             with canvas(device) as draw:
                 draw.rectangle(device.bounding_box, fill="black")
                 draw.text((5, 8),  f"TEMP: {temp} °C", font=big_font, fill="white")
                 draw.text((5, 32), f"HUMI: {humi} %RH", font=big_font, fill="white")
             
             time.sleep(2)
             
     except KeyboardInterrupt:
         print("\n程序已退出")
     except Exception as e:
         print(f"错误: {e}")
     finally:
         try:
             sensor.close()
         except:
             pass
 ​
 if __name__ == "__main__":
     main()

保存代码。

效果

  • 终端执行 python aht_oled.py 运行程序;

  • OLED 显示 AHT10 传感器采集的温湿度信息;

呼吸灯

通过 PCF8591 芯片的 AOUT 引脚,实现 DAC 控制 LED 亮度周期变化的呼吸灯效果。

代码

终端执行 touch pcf8591_breath_oled.py 创建文件,添加如下代码

 #!/usr/bin/env python3
 # PCF8591 AOUT 呼吸灯 + OLED动态DAC电压波形 (MUSE PI Pro I2C4)
 import time
 import math
 import sys
 from PIL import ImageFont, ImageDraw, Image
 from luma.core.interface.serial import i2c
 from luma.core.render import canvas
 from luma.oled.device import ssd1306
 ​
 try:
     import smbus2 as smbus
 except ImportError:
     import smbus
 ​
 # ===================== 硬件配置 =====================
 I2C_BUS = 4
 PCF8591_ADDR = 0x48
 DAC_ENABLE = 0x40
 OLED_ADDR = 0x3C
 OLED_WIDTH = 128
 OLED_HEIGHT = 64
 ​
 VCC = 3.3
 V_LED = 2.3  # LED 点亮阈值电压
 # DAC有效范围
 DAC_MIN = int((V_LED / VCC) * 255)
 DAC_MAX = 255
 DAC_RANGE = DAC_MAX - DAC_MIN
 ​
 # OLED初始化
 serial = i2c(port=I2C_BUS, address=OLED_ADDR)
 device = ssd1306(serial, width=OLED_WIDTH, height=OLED_HEIGHT)
 # 小字用于标题
 try:
     font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 10)
 except:
     font_small = ImageFont.load_default()
 ​
 # 波形缓冲区:保存历史DAC电压点,长度等于屏幕宽度
 wave_buffer = [0.0] * OLED_WIDTH
 ​
 def dac_to_volt(dac):
     """DAC数值转输出电压 0~3.3V"""
     return round((dac / 255.0) * VCC, 2)
 ​
 def draw_wave(draw, current_volt):
     """绘制动态电压波形,滚动刷新"""
     global wave_buffer
     # 缓冲区左移,丢弃最早数据
     wave_buffer = wave_buffer[1:]
     wave_buffer.append(current_volt)
 ​
     # 波形绘图区域:上下留边,顶部文字占用12像素
     top_margin = 12
     plot_h = OLED_HEIGHT - top_margin
     volt_min = V_LED - 0.1
     volt_max = VCC + 0.1
 ​
     # 绘制标题文字
     draw.rectangle((0, 0, OLED_WIDTH, top_margin), fill="black")
     draw.text((2, 0), f"AOUT Voltage: {current_volt:.2f}V", font=font_small, fill="white")
 ​
     # 绘制坐标轴底线
     draw.line([0, OLED_HEIGHT/2+7, OLED_WIDTH, OLED_HEIGHT/2+7], fill="white")
 ​
     # 逐点绘制波形曲线
     for x in range(OLED_WIDTH):
         v = wave_buffer[x]
         # 电压映射到屏幕Y坐标:电压越高Y越小(向上)
         ratio = (v - volt_min) / (volt_max - volt_min)
         y = OLED_HEIGHT - int(ratio * plot_h)
         # 绘制竖线连接基线到波形点
         draw.point((x, y), fill="white")
         if x > 0:
             prev_v = wave_buffer[x-1]
             prev_ratio = (prev_v - volt_min) / (volt_max - volt_min)
             prev_y = OLED_HEIGHT - int(prev_ratio * plot_h)
             draw.line([x-1, prev_y, x, y], fill="white")
 ​
 def main():
     bus = smbus.SMBus(I2C_BUS)
     print(f"PCF8591 @ I2C-{I2C_BUS}")
     print(f"LED 阈值: {V_LED}V | 有效 DAC: {DAC_MIN}~{DAC_MAX} ({DAC_RANGE} 级)")
     print("OLED实时显示AOUT电压波形,按 Ctrl+C 停止\n")
 ​
     try:
         period = 1.0      # 呼吸周期
         steps = 30
         delay = period / steps
 ​
         while True:
             for i in range(steps):
                 t = i / steps
                 # 正弦波生成
                 sin_val = (math.sin(t * 2 * math.pi - math.pi / 2) + 1) / 2
                 # Gamma校正
                 gamma = 1.0
                 brightness = math.pow(sin_val, gamma)
                 # 映射DAC值
                 dac_val = DAC_MIN + int(brightness * DAC_RANGE)
                 dac_val = min(DAC_MAX, max(DAC_MIN, dac_val))
                 # 输出DAC
                 bus.write_i2c_block_data(PCF8591_ADDR, DAC_ENABLE, [dac_val])
                 # 计算当前输出电压
                 volt = dac_to_volt(dac_val)
 ​
                 # OLED刷新波形
                 with canvas(device) as draw:
                     draw_wave(draw, volt)
 ​
                 time.sleep(delay)
 ​
     except KeyboardInterrupt:
         print("\n停止,熄灭 LED")
         bus.write_i2c_block_data(PCF8591_ADDR, DAC_ENABLE, [0])
         # 清屏
         with canvas(device) as draw:
             draw.rectangle(device.bounding_box, fill="black")
     finally:
         bus.close()
 ​
 if __name__ == "__main__":
     main()

保存代码。

效果

终端执行 python pcf8591_breath_oled.py 运行程序;

不同阶段 LED 亮度变化

动态演示

心率监测

心率传感器,采用光电容积脉搏波描记法,通过测量血液中血红蛋白随心脏跳动而对氧气吸收的变化量来测量人体心率参数。

  • 具有响应快,性能稳定,适应性强等特点。

  • 模块拥有方波和脉搏波两种信号输出模式,可以通过板载开关去自由切换输出信号。

  • 脉搏波将输出一个连续的心率波形,而方波将根据心率的变化输出对应的方波。

  • 心率传感器体积小巧,可以佩戴于手指、手腕等检测位置。

硬件连接

使用杜邦线将数据引脚与扩展板 PCF8591 的 A2 引脚连接,通过 ADC 实现心率计算、脉搏读取等。

代码

终端执行 touch hr_oled.py 创建文件,添加如下代码

 import time
 import threading
 import os
 from collections import deque
 from periphery import GPIO
 # ========== OLED library ==========
 from luma.core.interface.serial import i2c
 from luma.core.render import canvas
 from luma.oled.device import ssd1306
 from PIL import Image, ImageFont
 ​
 # ========= 硬件配置 =========
 GPIO_CHIP = "/dev/gpiochip0"
 GPIO_LINE = 35 # 心率GPIO
 BEAT_WINDOW = 5
 DEBOUNCE_MS = 300
 TIMEOUT_MS = 3000
 ​
 # OLED配置
 OLED_PORT = 4
 OLED_ADDR = 0x3C
 FRAME_COUNT = 20
 # 字体
 font_fg = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 22)
 font_date = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 16)
 # 星期
 week_map = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
 ​
 # ==================== OLED全局对象 ====================
 serial = i2c(port=OLED_PORT, address=OLED_ADDR)
 device = ssd1306(serial)
 frame_list = []
 frame_index = 0
 # 加载动画帧
 for i in range(1, FRAME_COUNT + 1):
     img_path = os.path.join("frame_hr", f"{i}.png")
     img = Image.open(img_path).convert("1")
     frame_list.append(img)
 ​
 # ==================== 心率监测器 ====================
 class HeartRateMonitor:
     def __init__(self, chip, line):
         self.chip_path = chip
         self.line_num = line
         self.gpio = GPIO(chip, line, "in", edge="rising")
         self.beat_times = deque(maxlen=BEAT_WINDOW)
         self.bpm = 0
         self.running = False
         self._thread = None
         self._lock = threading.Lock()
         
     def start(self):
         self.running = True
         self._thread = threading.Thread(target=self._monitor_loop, daemon=True)
         self._thread.start()
         print(f"[INFO] 心率监测启动: {self.chip_path} line {self.line_num}")
         
     def _monitor_loop(self):
         last_beat_ns = 0
         while self.running:
             if not self.gpio.poll(timeout=TIMEOUT_MS / 1000.0):
                 with self._lock:
                     self.bpm = 0
                 continue
             try:
                 edge, timestamp_ns = self.gpio.read_event()
             except Exception:
                 continue
             if last_beat_ns > 0:
                 interval_ms = (timestamp_ns - last_beat_ns) / 1_000_000
                 if interval_ms < DEBOUNCE_MS:
                     continue
             last_beat_ns = timestamp_ns
             self.beat_times.append(timestamp_ns)
             if len(self.beat_times) >= 2:
                 intervals = []
                 times = list(self.beat_times)
                 for i in range(1, len(times)):
                     intervals.append((times[i] - times[i-1]) / 1_000_000_000.0)
                 intervals.sort()
                 median_interval = intervals[len(intervals) // 2]
                 bpm = int(60.0 / median_interval)
                 bpm = max(30, min(220, bpm))
                 with self._lock:
                     self.bpm = bpm
         
     def get_bpm(self):
         with self._lock:
             return self.bpm
         
     def get_beat_count(self):
         return len(self.beat_times)
         
     def stop(self):
         self.running = False
         if self._thread:
             self._thread.join(timeout=1.0)
         self.gpio.close()
 ​
 # ==================== 主程序(仅增加OLED渲染逻辑) ====================
 def main():
     print("=" * 50)
     print("  心率监测仪  ")
     print("=" * 50)
     print("请将传感器拨到 'D' 档")
     print("按 Ctrl+C 停止\n")
     
     monitor = HeartRateMonitor(GPIO_CHIP, GPIO_LINE)
     monitor.start()
     
     global frame_index
     try:
         while True:
             bpm = monitor.get_bpm()
             beat_cnt = monitor.get_beat_count()
             # 控制台打印
             if bpm > 0:
                 bar = "█" * (bpm // 10) + "░" * ((220 - bpm) // 20)
                 print(f"\r♥ 心率: {bpm:3d} BPM  [{bar}]  脉搏数: {beat_cnt:2d}", end="", flush=True)
             else:
                 print("\r♥ 等待心跳信号...                    ", end="", flush=True)
 ​
             # ========== OLED刷新 ==========
             with canvas(device) as draw:
                 # 帧动画
                 draw.bitmap((0, 0), frame_list[frame_index], fill=1)
                 # 时间日期星期
                 now = time.localtime()
                 date_str = f"{now.tm_year}-{now.tm_mon:02d}-{now.tm_mday:02d}"
                 time_str = f"{now.tm_hour:02d}:{now.tm_min:02d}"
                 week_str = week_map[now.tm_wday]
                 #draw.text((2, 2), date_str, font=font_date, fill=1)
                 #draw.text((2, 18), week_str, font=font_date, fill=1)
                 #draw.text((2, 34), time_str, font=font_fg, fill=1)
                 # 心率BPM
                 draw.text((80, 8), "BPM", font=font_date, fill=1)
                 if bpm > 0:
                     draw.text((75, 32), f"{bpm}", font=font_fg, fill=1)
                 else:
                     draw.text((67, 32), "Waiting Pulse", font=font_date, fill=1)
 ​
             # 动画帧自增循环
             frame_index = (frame_index + 1) % FRAME_COUNT
             time.sleep(0.1)
 ​
     except KeyboardInterrupt:
         print("\n\n[INFO] 停止监测")
         device.clear() # OLED清屏
     finally:
         monitor.stop()
 ​
 if __name__ == "__main__":
     main()

保存代码。

效果

  • 终端执行 python hr_oled.py 运行程序;

  • 终端显示心率 BPM 数据和动态条;

  • OLED 显示心跳动画和 BPM 心率数值;

动态演示

  • 串口打印模拟输出,并绘制心率曲线

总结

本文介绍了进迭时空 K1 MUSE Pi Pro 的扩展板 PCB 设计,包括原理图、资源参数、环境搭建 、工程测试等,为相关产品的快速开发和应用设计提供了参考。