【工业树莓派 CM0 Dev Board】扩展板设计
本文介绍了树莓派 CM0 Dev Board 单板计算机扩展板 PCB 设计,包括原理图、资源参数、环境搭建、工程测试等。

项目介绍
- 扩展板设计:PCB、资源分布、参数特点、原理图等;
- 环境搭建:安装所需 python 库;
- 工程测试:驱动 DHT11、ADC、AHT10、LM75、数码管、步进电机等。
扩展板
树莓派 Easy 扩展板

资源
- DHT11 温湿度
- 4 路独立按键
- 4 颗单色 LED
- 6 颗 WS2812 彩色 LED
- HX1838 红外接收
- GL5528 光敏电阻
- 旋转电位器
- 无源蜂鸣器
接口
- 硬件 I2C 接口
- 0.96 寸 I2C OLED 接口
- 超声波模块接口
- CH340E 芯片 USB 转 TTL 串口
- 预留数字拓展引脚
- 模拟拓展通道
原理图

环境搭建
创建并激活虚拟环境
bash
python -m venv .venv
source .venv/bin/activate
终端执行如下指令,安装所需 python 库
bash
pip install pillow # image processing library
pip install adafruit-circuitpython-ssd1306 # oled library
pip install adafruit-circuitpython-dht # dht11 driver library
pip install adafruit-circuitpython-pcf8591 # pcf8591 library
pip install adafruit-circuitpython-ahtx0 # aht10 library
pip install adafruit-circuitpython-pct2075 # lm75 library
DHT11
使用 adafruit_dht 库函数和单总线协议读取 DHT11 温湿度数据,并通过 adafruit_ssd1306 或 luma.oled 库函数实现 OLED 显示。
代码
python
import time
import board
import busio
import adafruit_dht
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont
# 初始化DHT11 物理引脚7
dht = adafruit_dht.DHT11(board.D4, use_pulseio=False)
# 初始化I2C OLED 物理3/5
i2c = busio.I2C(board.SCL, board.SDA)
oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
w, h = oled.width, oled.height
img = Image.new("1", (w, h))
draw = ImageDraw.Draw(img)
# 加载字体
try:
# 系统矢量字体,支持缩放
big_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
except:
# 默认字体
big_font = ImageFont.load_default()
def screen_clear():
draw.rectangle((0,0,w,h), fill=0)
try:
while True:
screen_clear()
try:
temp = dht.temperature
hum = dht.humidity
draw.text((0, 0), f"Temp: {temp:.1f} C", font=big_font, fill=255)
draw.text((0, 28), f"Humi: {hum} %RH", font=big_font, fill=255)
except RuntimeError as e:
draw.text((0, 0), "Read Error", font=big_font, fill=255)
oled.image(img)
oled.show()
time.sleep(2)
except KeyboardInterrupt:
pass
finally:
dht.exit()
oled.fill(0)
oled.show()
保存代码。
效果
终端执行指令 python dht11_oled.py 运行程序;
板载 OLED 显示 DHT11 采集的环境温湿度数据;

ADC
使用 adafruit_pcf8591 库函数和 IIC 总线协议读取 A1 引脚(旋转电位器)输入的模拟电压,并通过串口打印。
代码
python
import time
import board
import adafruit_pcf8591.pcf8591 as PCF
from adafruit_pcf8591.analog_in import AnalogIn
# 初始化I2C总线
i2c = board.I2C()
# 创建PCF8591实例,默认地址0x48
pcf = PCF.PCF8591(i2c)
# 绑定Ax输入通道
adc = AnalogIn(pcf, PCF.A1)
# 循环打印电压
while True:
# 原始归一化数值(0~65535)
raw = adc.value
# 换算实际电压
volt = (raw / 65535) * adc.reference_voltage
print(f"A1通道电压:{volt:.2f} V,原始值:{raw}")
time.sleep(1)
保存代码。
效果
终端执行指令 python pcf8591_print.py 运行程序;
旋转电位器旋钮,调整 ADC 输入电压;

终端打印 A1 采样电压值和原始值;

呼吸灯
结合 PCF8591 芯片控制 AOUT 模拟输出,实现 LED 渐变效果。
代码
python
import time
import board
import adafruit_pcf8591.pcf8591 as PCF
from adafruit_pcf8591.analog_out import AnalogOut
# I2C初始化
i2c = board.I2C()
pcf = PCF.PCF8591(i2c)
dac_out = AnalogOut(pcf, PCF.OUT)
# 阈值参数
VOLT_THRESHOLD = 2.3 # LED点亮电压阈值
V_REF = 3.3 # PCF8591基准电压
DAC_THRESHOLD = round(VOLT_THRESHOLD / V_REF * 65535) # 45676
DAC_MAX = 65535
print(f"LED点亮DAC阈值:{DAC_THRESHOLD},对应电压{VOLT_THRESHOLD}V")
try:
while True:
# 渐亮:从阈值升到最大
for dac_val in range(DAC_THRESHOLD, DAC_MAX + 1, 256):
dac_out.value = dac_val
volt = (dac_val / 65535) * V_REF
#print(f"DAC:{dac_val:5d} 电压:{volt:.2f}V LED亮")
time.sleep(0.02)
# 渐暗:从最大降到阈值,低于阈值直接归零,LED熄灭
for dac_val in range(DAC_MAX, DAC_THRESHOLD - 1, -256):
dac_out.value = dac_val
volt = (dac_val / 65535) * V_REF
#print(f"DAC:{dac_val:5d} 电压:{volt:.2f}V LED亮")
time.sleep(0.02)
# 完全熄灭阶段(保持0V,停顿0.5秒)
dac_out.value = 0
#print("DAC:0 电压:0.00V LED熄灭")
time.sleep(0.5)
except KeyboardInterrupt:
# Ctrl+C退出,关闭LED
dac_out.value = 0
print("\n程序结束,DAC清零")
保存代码。
效果
终端执行指令 python aout_breath.py 运行程序;
板载 LED 呈现呼吸灯效果;

AHT10
使用杜邦线连接板载 IIC 排针接口和 AHT10 传感器模块,实现环境温湿度的串口打印。

代码
python
import time
import board
import adafruit_ahtx0
# Create sensor object, communicating over the board's default I2C bus
i2c = board.I2C() # uses board.SCL and board.SDA
sensor = adafruit_ahtx0.AHTx0(i2c)
try:
while True:
print("\nTemperature: %0.1f C" % sensor.temperature)
print("Humidity: %0.1f %%" % sensor.relative_humidity)
time.sleep(2)
except KeyboardInterrupt:
pass
finally:
print("Exiting....")
保存代码。
效果
终端执行指令 python aht_print.py 运行程序;

终端连续打印环境温湿度数据;

LM75
使用杜邦线连接板载 IIC 排针接口和 LM75 传感器模块,实现环境温度的串口打印。

模块 PCB 设计详见:LM75模块 - 立创开源硬件平台 .
由于板载 PCF8591 默认地址 0x48,与 LM75 默认 IIC 地址相同,因此需短接 A1(或 A2) 与 VCC,对应地址为 0x4a (0x4c)。
代码
python
import time
import board
import adafruit_pct2075
# 初始化 I2C 总线
i2c = board.I2C()
sensor = adafruit_pct2075.PCT2075(i2c, address=0x4c)
try:
while True:
temp = sensor.temperature
print(f"温度:{temp:.2f} ℃")
time.sleep(0.5)
except KeyboardInterrupt:
print("程序退出")
保存代码。
效果
终端执行指令 python lm75_print.py 运行程序;

终端打印实时温度数据;

数码管
使用 GPIO 控制数码管显示 CPU 温度。

代码
python
import RPi.GPIO as GPIO
import time
import threading
# ==================== 74HC595 引脚定义 ====================
# 使用 BCM 编号,可根据实际接线修改
LATCH = 9 # RCLK (STCP)
CLOCK = 10 # SRCLK (SHCP)
DATA = 11 # DS (SER)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(LATCH, GPIO.OUT)
GPIO.setup(CLOCK, GPIO.OUT)
GPIO.setup(DATA, GPIO.OUT)
# ==================== 共阳数码管段码 ====================
# 段码:dp g f e d c b a
SMG_DUAN = [
0b11000000, # 0
0b11111001, # 1
0b10100100, # 2
0b10110000, # 3
0b10011001, # 4
0b10010010, # 5
0b10000010, # 6
0b11111000, # 7
0b10000000, # 8
0b10010000, # 9
]
# ==================== 595 移位发送 ====================
def shift_out(byte):
for i in range(8):
bit = (byte >> (7 - i)) & 1
GPIO.output(DATA, bit)
GPIO.output(CLOCK, GPIO.HIGH)
GPIO.output(CLOCK, GPIO.LOW)
# ==================== 显示函数 ====================
def show(left, right, dot=False):
seg_l = SMG_DUAN[left % 10]
seg_r = SMG_DUAN[right % 10]
if dot:
seg_r &= 0b01111111 # 共阳:点亮小数点(dp 拉低)
GPIO.output(LATCH, GPIO.LOW)
shift_out(seg_r) # 先送右边(级联时后到高位)
shift_out(seg_l)
GPIO.output(LATCH, GPIO.HIGH)
# ==================== 读取 CPU 温度 ====================
def get_cpu_temp():
try:
with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
temp_str = f.read().strip()
return int(temp_str) / 1000.0 # 转为摄氏度
except Exception as e:
print("读取温度失败:", e)
return 0.0
# ==================== 全局变量与锁 ====================
current_left = 0
current_right = 0
current_dot = False
lock = threading.Lock()
# ==================== 刷新线程(防止闪烁)====================
def display_loop():
"""独立线程持续刷新数码管,避免主循环 sleep 导致闪烁"""
while running:
with lock:
l, r, d = current_left, current_right, current_dot
show(l, r, d)
time.sleep(0.005) # 5ms 刷新一次,视觉无闪烁
# ==================== 主程序 ====================
running = True
try:
# 启动显示刷新线程
t = threading.Thread(target=display_loop, daemon=True)
t.start()
while True:
temp = get_cpu_temp()
print("CPU 温度: {:.1f} °C".format(temp))
with lock:
if temp >= 10:
# 两位数:显示整数,如 42
t_round = round(temp, 0)
current_left = int(t_round // 10)
current_right = int(t_round % 10)
current_dot = False
else:
# 个位数:显示一位小数,如 8.5
t_round = round(temp * 10) / 10
current_left = int(t_round)
current_right = int((t_round * 10) % 10)
current_dot = True
time.sleep(1) # 每秒更新一次温度数值
except KeyboardInterrupt:
print("\n程序已停止")
finally:
running = False
t.join(timeout=0.5)
GPIO.output(LATCH, GPIO.LOW)
GPIO.cleanup()
保存代码。
效果
终端执行指令 python segment_cpu.py 运行程序;

数码管显示 CPU 温度值;

步进电机
树莓派 CM0 控制 GPIO 输出电平,结合 A4988 模块实现 42 步进电机驱动,并显示旋转状态,包括转速、角度和方向。

模块 PCB 设计详见:42步进电机A4988扩展板 - 立创开源硬件平台 .
代码
终端执行指令 touch stepmotor_oled.py 新建文件,并添加如下代码
python
import board
import busio
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont
import os
import time
import datetime
import threading
import RPi.GPIO as GPIO
# ---------------------- OLED 初始化 ----------------------
# I2C 总线初始化,树莓派默认SDA=GPIO2(SDA), SCL=GPIO3(SCL)
i2c = busio.I2C(board.SCL, board.SDA)
# SSD1306 128*64 0x3C地址
WIDTH = 128
HEIGHT = 64
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C)
# 清空屏幕缓存
oled.fill(0)
oled.show()
# 创建绘图缓冲区
image = Image.new("1", (WIDTH, HEIGHT))
draw = ImageDraw.Draw(image)
# 字体
font_main = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14)
font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12)
# ---------------------- 运行状态变量 ----------------------
current_angle = 180
speed_ms = 0.2
direct_text = "CW"
running = True
# 独立屏幕刷新线程
def screen_task():
while running:
now = datetime.datetime.now()
date_str = now.strftime("%Y-%m-%d")
time_str = now.strftime("%H:%M:%S")
# 每次刷新前清空画布
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=0)
# 左侧时间信息
draw.text((0, 0), date_str, font=font_small, fill=1)
draw.text((0, 11), time_str, font=font_main, fill=1)
# 左侧电机运行状态
info_dir = f"Dir: {direct_text}"
info_speed = f"V: {speed_ms}ms"
info_angle = f"Ang: {abs(current_angle)}deg"
draw.text((2, 24), info_dir, font=font_small, fill=1)
draw.text((2, 36), info_speed, font=font_small, fill=1)
draw.text((2, 48), info_angle, font=font_small, fill=1)
# 将PIL图像写入OLED并刷新
oled.image(image)
oled.show()
time.sleep(0.1)
# ---------------------- 电机驱动函数 ----------------------
STEP = 9
DIR = 10
EN = 11
PULSE_PER_360DEG = 1600 # 8 细分
PULSE_PER_DEG = PULSE_PER_360DEG / 360
GPIO.setmode(GPIO.BCM)
GPIO.setup(STEP, GPIO.OUT)
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(EN, GPIO.OUT)
GPIO.output(EN, GPIO.LOW)
def run_angle(degree, speed):
global direct_text
pulses = int(abs(degree) * PULSE_PER_DEG)
if degree > 0:
GPIO.output(DIR, GPIO.HIGH)
direct_text = "CW"
else:
GPIO.output(DIR, GPIO.LOW)
direct_text = "CCW"
delay_s = speed / 1000.0
# 脉冲输出
for _ in range(pulses):
GPIO.output(STEP, GPIO.HIGH)
time.sleep(delay_s)
GPIO.output(STEP, GPIO.LOW)
time.sleep(delay_s)
if __name__ == "__main__":
# 启动后台屏幕线程
t = threading.Thread(target=screen_task, daemon=True)
t.start()
try:
while True:
current_angle = 180
run_angle(current_angle, speed_ms)
time.sleep(1)
current_angle = -180
run_angle(current_angle, speed_ms)
time.sleep(1)
except KeyboardInterrupt:
running = False
time.sleep(0.2)
# 清屏
oled.fill(0)
oled.show()
# 电机释放
GPIO.output(EN, GPIO.HIGH)
GPIO.cleanup()
print("程序退出,屏幕与电机已释放")
保存代码。
效果
-
终端执行指令
python a4988_run.py运行程序; -
OLED 显示日期、时间、步进电机状态,包括方向、速度、角度等;
-
步进电机正反交替旋转;

总结
本文介绍了树莓派 CM0 Dev Board 单板计算机扩展板 PCB 设计,包括原理图、资源参数、环境搭建、工程测试等,为相关产品的快速开发和应用设计提供了参考。