这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 成果贴

共1条 1/1 1 跳转至

成果贴

菜鸟
2025-12-14 15:48:50     打赏
import time  
import digitalio  
import board  
import displayio  
import terminalio  
from adafruit_display_text import label  
import neopixel  
import pwmio  
# ==============================  
# 初始化硬件  
# ==============================  
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)  
pixel.brightness = 0.3  
pwm= pwmio.PWMOu(board.D13, duty_cycle=0, frequency=440, variable_frequency=True)  
note_naes = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']  
frequencies = [261, 293, 329, 349, 392, 440, 493, 523]  
DEBOUNCE_TIME = 0.2  
NOTE_DURATION = 0.5  
# ==============================  
# TCS3200 类  
# ==============================  
class TCS3200:  
    def __init__(self, s0, s1, s2, s3, out_pin):  
        self.s0 = s0; self.s1 = s1; self.s2 = s2; self.s3 = s3; self.out = out_pin  
        for pin in (s0, s1, s2, s3):  
   
            pin.direction = digitalio.Direction.OUTPUT  
        out_pin.direction = digitalio.Direction.INPUT  
    def set_frequency(self, mode):  
        self.s0.value = (mode >> 1) & 1  
        self.s1.value = mode & 1  
    def set_color_channel(self, color):  
        if color == 'R':  
            self.s2.value, self.s3.value = False, False  
        elif color == 'G':  
            self.s2.value, self.s3.value = True, True  
        elif color == 'B':  
            self.s2.value, self.s3.value = False, True  
    def measure_frequency(self, duration=0.08):  
        start = time.monotonic()  
        count = 0  
        last = self.out.value  
        while (time.monotonic() - start) < duration:  
            cur = self.out.value  
            if cur != last:  
                count += 1  
                last = cur  
        return (count / 2) / duration if duration > 0 else 0  
tcs = TCS3200(  
    s0=digitalio.DigitalInOut(board.D11),  
    s1=digitalio.DigitalInOut(board.D10),  
    s2=digitalio.DigitalInOut(board.D9),  
    s3=digitalio.DigitalInOut(board.D6),  
    out_pin=digitalio.DigitalInOut(board.D5)  
)  
tcs.set_frequency(1)  
# TCS3200 板载 LED (D12) —— 上电默认关闭  
led = digitalio.DigitalInOut(board.D12)  
led.direction = digitalio.Direction.OUTPUT  
led.value = False  # <<< 上电即关闭  
# ==============================  
# 显示初始化  
# ==============================  
display = board.DISPLAY  
main_group = displayio.Group()  
display.root_group = main_group  
bg_bitmap = displayio.Bitmap(display.width, display.height, 1)  
bg_palette = displayio.Palette(1)  
bg_palette[0] = 0x000000  
bg_sprite = displayio.TileGrid(bg_bitmap, pixel_shader=bg_palette)  
main_group.append(bg_sprite)  
text_label = label.Label(  
    terminalio.FONT,  
    text="Ready",  
    color=0xFFFFFF,  
    scale=2  
)  
text_label.anchor_point = (0.5, 0.5)  
text_label.anchored_position = (display.width // 2, display.height // 2)  
main_group.append(text_label)  
reed_switch = digitalio.DigitalInOut(board.D14)  
reed_switch.switch_to_input(pull=digitalio.Pull.UP)  
d0_button = digitalio.DigitalInOut(board.D0)  
d0_button.switch_to_input(pull=digitalio.Pull.UP)  
d2_button = digitalio.DigitalInOut(board.D2)  
d2_button.switch_to_input(pull=digitalio.Pull.DOWN)  
# ==============================  
# 白卡参考值  
# ==============================  
white_r = 700.0  
white_g = 600.0  
white_b = 580.0  
def measure_avg(channel, n=3):  
    vals = []  
    for _ in range(n):  
        tcs.set_color_channel(channel)  
        f = tcs.measure_frequency(duration=0.08)  
        if f > 5:  
            vals.append(f)  
        time.sleep(0.02)  
    return sum(vals) / len(vals) if vals else 0  
# ==============================  
# 颜色转换  
# ==============================  
def freq_to_rgb(fr, fg, fb):  
    r_wb = fr / white_r if white_r > 10 else 0  
    g_wb = fg / white_g if white_g > 10 else 0  
    b_wb = fb / white_b if white_b > 10 else 0  
    max_val = max(r_wb, g_wb, b_wb, 0.001)  
    r = min(255, int(255 * r_wb / max_val))  
    g = min(255, int(255 * g_wb / max_val))  
    b = min(255, int(255 * b_wb / max_val))  
    return r, g, b  
# ==============================  
# 白卡校准  
# ==============================  
def calibrate_white():  
    global white_r, white_g, white_b  
    print("Place WHITE card over sensor. Calibrating in 2s...")  
    text_label.text = "Put WHITE card\nCalibrating..."  
    pixel.fill((255, 255, 255))  
    time.sleep(2)  
    fr = measure_avg('R')  
    fg = measure_avg('G')  
    fb = measure_avg('B')  
    white_r = fr if fr > 10 else white_r  
    white_g = fg if fg > 10 else white_g  
    white_b = fb if fb > 10 else white_b  
    print(f" White calibrated:\n   R={white_r:.1f}, G={white_g:.1f}, B={white_b:.1f}")  
    text_label.text = f"White OK!\nR{int(white_r)} G{int(white_g)} B{int(white_b)}"  
    pixel.fill((255, 255, 0))  
    time.sleep(2)  
# ==============================  
# 音符控制  
# ==============================  
def play_note(note_index):  
    if 0 <= note_index < len(note_names):  
        freq = frequencies[note_index]  
        pwm.frequency = int(freq)  
        pwm.duty_cycle = 2 ** 15  
def stop_playing():  
    pwm.duty_cycle = 0  
# ==============================  
# 电源管理  
# ==============================  
def enter_sleep_mode():  
    global is_awake  
    if is_awake:  
        display.brightness = 0.0  
        bg_palette[0] = 0x000000  
        pixel.fill((0, 0, 0))  
        text_label.text = ""  
        is_awake = False  
        # 注意:不操作 led.value,保持用户设置  
def enter_wake_mode():  
    global is_awake  
    if not is_awake:  
        display.brightness = 1.0  
        bg_palette[0] = 0x000000  
        pixel.fill((0, 0, 0))  
        text_label.text = "D0: Scan\nLong(3s): Calib\nDouble: LED"  
        is_awake = True  
# ==============================  
# 主循环  
# ==============================  
is_awake = True  
note_index = 0  
last_d2_state = False  
note_start_time = time.monotonic()  
last_d0_press = 0  
pending_d0_click = False  
d0_press_start = None  
D0_DOUBLE_DELAY = 0.3  
D0_LONG_PRESS_TIME = 3.0  
print("System started.")  
print(f"Initial white ref: R={white_r}, G={white_g}, B={white_b}")  
# 初始进入唤醒状态(但 LED 保持关闭)  
enter_wake_mode()  
while True:  
    reed_open = reed_switch.value  
    if reed_open:  
        enter_wake_mode()  
    else:  
        enter_sleep_mode()  
    if is_awake:  
        d0_current = d0_button.value  
        if not d0_current:  # 按下 D0  
            d0_press_start = time.monotonic()  
            while not d0_button.value:  
                if time.monotonic() - d0_press_start > D0_LONG_PRESS_TIME:  
                    break  
            press_duration = time.monotonic() - d0_press_start  
            d0_press_start = None  
            if press_duration > D0_LONG_PRESS_TIME:  
                calibrate_white()  
                text_label.text = "D0: Scan\nLong(3s): Calib\nDouble: LED"  
            else:  
                now = time.monotonic()  
                if pending_d0_click:  
                    pending_d0_click = False  
                    led.value = not led.value  # 切换 TCS3200 LED  
                    status = "ON" if led.value else "OFF"  
                    text_label.text = f"TCS LED {status}"  
                    #  No NeoPixel feedback for LED toggle  
                    time.sleep(0.3)  
                else:  
                    last_d0_press = now  
                    pending_d0_click = True  
        if pending_d0_click and (time.monotonic() - last_d0_press > D0_DOUBLE_DELAY):  
            pending_d0_click = False  
            text_label.text = "Scanning..."  
            fr = measure_avg('R')  
            fg = measure_avg('G')  
            fb = measure_avg('B')  
            if fr < 10 and fg < 10 and fb < 10:  
                r, g, b = 0, 0, 0  
                text_label.text = "Too dark!"  
                bg_palette[0] = 0x000000  
                pixel.fill((0, 0, 0))  
                print("Color: Too dark! (R=0, G=0, B=0)")  
            else:  
                r, g, b = freq_to_rgb(fr, fg, fb)  
                text_label.text = f"R:{r}\nG:{g}\nB:{b}"  
                bg_palette[0] = (r << 16) | (g << 8) | b  
                pixel.fill((r, g, b))  
                print(f"Color: R={r}, G={g}, B={b}")  
        # --- D2 音阶测试 ---  
        d2_current = d2_button.value  
        if d2_current and not last_d2_state:  
            stop_playing()  
            play_note(note_index)  
            note_index = (note_index + 1) % len(note_names)  
            note_start_time = time.monotonic()  
            time.sleep(DEBOUNCE_TIME)  
        if time.monotonic() - note_start_time > NOTE_DURATION:  
            stop_playing()  
            note_start_time = time.monotonic()  
        last_d2_state = d2_current  
    else:  
        time.sleep(0.1)  
    time.sleep(0.01)

首先:先将各个任务完成情况进行展示,后面在进行描述

任务一:通过传感器,将识别到的颜色进行显示,由于当时并未考虑检测这么多颜色,所以临时在现有的环境下进行测试。fd6fba4a7b5e9dc3a9b34d4da4b5bde.jpg9ea6a976785aeda6d02ba09fc6109f2.jpg84cf64dc6b8c303c609dcf797703e10.jpg

07297ec778547a8ce45cfae13a6852a.jpg

任务二:将识别的颜色通过后面的RGB灯珠显示对应颜色,当时拍的时候,我的摄像头有点脏,可能不太能完整的显示对应的颜色

8efde4f98404464cfc98be5ebc46e1c.jpg1b46a0f06a02ccb821655300e55d799.jpg

任务三:播放8个音阶,当磁铁靠近时关闭

这个视频贴在附件中,不知道能不能上传完成。


三原色原理与TCS3200传感器的白平衡机制

我们所见的世界之所以五彩斑斓,从物理光学的角度来看,源于物体对光线的选择性反射与吸收。具体而言,物体所呈现的颜色,并非其自身发光产生,而是物体表面材质选择性地吸收了入射白光中的部分频率成分,同时将未被吸收的特定频率光反射出去。这些反射光进入人眼,刺激视觉细胞,最终在大脑中形成颜色感知。因此,颜色本质上是一种主观的视觉体验,其根基在于客观的光谱分布。

为了量化与重现丰富的颜色,人们建立了三原色理论。该理论指出,绝大多数肉眼可辨的颜色,都可以通过红、绿、蓝这三种基本颜色以不同比例混合而成。例如,等强度的红光与绿光混合产生黄光,红光与蓝光混合产生品红光。而白色,在理想状态下,被定义为由所有可见光谱成分均匀混合而成,或者等价地,由等比例、等强度的红、绿、蓝三原色光叠加而成。这是现代彩色显示与颜色传感技术的理论基础。

TCS3200颜色传感器正是这一理论的具体应用。其核心是一个覆盖有红、绿、蓝及透明滤波器的光电二极管阵列。传感器工作时,通过编程选择激活不同的滤波器组。当选定某一颜色滤波器时,传感器实质上就变身为一个“单色通道”,只允许该种原色(如红色)的光波大量通过,并有效阻挡其他原色光。随后,内部的光电转换电路将透过的光强转化为相应频率的电信号输出。通过循环切换红、绿、蓝三个滤镜并读取输出,我们便能依次获取物体反射光中红色分量、绿色分量和蓝色分量的相对强度值,从而以数字化的方式“看见”并测量颜色。

然而,从理论到实践存在一个关键步骤:白平衡校准。在理想世界中,一张纯白的纸反射的光中,红、绿、蓝三原色的分量应是相等的。但现实中存在多重偏差:首先,环境光源(如日光、白炽灯、LED灯)的光谱本身并非绝对均匀,其中各颜色成分的比例已有差异;其次,TCS3200传感器内部的硅光电二极管对不同波长光的灵敏度天然不同,通常对绿光最为敏感,对红光和蓝光则稍弱。这两者叠加,导致即使测量同一个“白色”物体,传感器直接读出的原始R、G、B三个数值也往往不相等。

若不对这种系统性偏差进行校正,所有后续的颜色测量都将建立在失准的基础上,导致识别的颜色严重失真。因此,白平衡调整是TCS3200使用前不可或缺的初始化步骤。其标准操作流程是:将传感器对准用于参考的“标准白色”物体(或在其设定的光照环境下),分别读取并记录此时输出的原始R、G、B值。这些值代表了该系统下“白色”的实际读数。随后,通过软件算法(通常是计算比例因子),将这三个原始值归一化到相同的水平。经过此番校准后,传感器便拥有了一个准确的“颜色坐标系”,在此坐标系下,当它再次测量该白色物体时,输出的R、G、B值将被调整至相等,从而确保了此后测量其他任何颜色时,所获数据都能真实反映该颜色与标准白之间的光谱比例关系,实现精确、稳定的颜色识别。

总结来说,TCS3200的工作是模拟人眼的三原色感知机制,而白平衡则是赋予这台“电子眼”正确理解“什么是白色”的基准认知,是连接颜色理论、物理现实与准确测量之间的关键桥梁。


系统连接图,我是借鉴https://forum.eepw.com.cn/thread/398148/1这位大神的帖子,这样就不用画了。

image.png








共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]