简介
这个拾色播放器的项目,用到了 TCS3200 颜色传感器模块(SEN0101)。刚拿到模块的时候,说实话有点懵——它不像常见的传感器那样直接给你一个模拟值或者 I²C 数据,而是输出一个“频率”。
为了搞清楚它到底是怎么工作的,我重新翻了一下下单时提供的数据手册,也顺便让 AI 帮我一起梳理了一下思路。这里把理解到的内容整理成一篇简单的记录,给同样第一次接触 TCS3200 的朋友做个参考。
SEN0101 工作原理
SEN0101 是一款集成度比较高的颜色检测模块,核心芯片是 TAOS 的 TCS3200。模块本身已经把外围电路和光源都准备好了,包括:
一颗 TCS3200 颜色传感器芯片
模块化 PCB
4 颗白色 LED,用来照亮被测物体
TCS3200 的一个特点是:
它并不输出模拟电压,而是输出一个与光强成正比的方波频率信号。
也正因为这样,单片机可以直接通过 GPIO 来读取信号,不需要额外的 ADC,这一点在一些资源受限的系统里还挺方便的。
这个模块常见的用途包括颜色识别、色带或条码检测、环境光检测,以及一些颜色匹配或教学实验场景
从整体流程来看,SEN0101 的工作方式其实并不复杂,大致可以理解为下面几个步骤:
首先,模块上的白色 LED 会先把被测物体照亮,保证每次测量的光照条件尽量一致。物体表面反射回来的光进入 TCS3200 芯片内部。
芯片内部有四组不同的光电二极管阵列,分别对应 红色、绿色、蓝色 以及 无滤光(Clear) 通道。通过控制模块的 S2、S3 引脚,可以在这几种滤波通道之间切换,每次只测量其中一种颜色成分。
当某一颜色通道被选中后,对应的光强会先被转换成电流信号,然后由芯片内部的电流-频率转换电路,生成一个数字方波信号从 OUT 引脚输出。光越强,输出频率就越高。
单片机只需要在同一光照条件下,分别读取红、绿、蓝三个通道的输出频率,再进行简单的比例换算和校准,就可以大致判断物体的颜色,或者计算出对应的 RGB 数值。
代码如下
def __init__(self, s0_pin, s1_pin, s2_pin, s3_pin, out_pin):
# 初始化控制引脚
self.s0 = digitalio.DigitalInOut(s0_pin)
self.s1 = digitalio.DigitalInOut(s1_pin)
self.s2 = digitalio.DigitalInOut(s2_pin)
self.s3 = digitalio.DigitalInOut(s3_pin)
self.out = digitalio.DigitalInOut(out_pin)
# 配置引脚方向
self.s0.direction = digitalio.Direction.OUTPUT
self.s1.direction = digitalio.Direction.OUTPUT
self.s2.direction = digitalio.Direction.OUTPUT
self.s3.direction = digitalio.Direction.OUTPUT
self.out.direction = digitalio.Direction.INPUT
# 设置频率缩放比为100% (最高精度)
self.set_frequency_scaling(100)
# 白平衡校准系数 (初始值为1,需要实际校准)
self.r_scal = 1.0
self.g_scal = 1.0
self.b_scal = 1.0
print("TCS3200传感器初始化完成")首先根据传感器的引脚来初始化对应的PIN
def set_frequency_scaling(self, scaling):
"""
设置传感器的频率缩放比例
# s0 s1
# L L 关闭
# L H 2%
# H L 20%
# H H 100%
"""
if scaling == 2: # 2%
self.s0.value = False
self.s1.value = True
# print("TCS3200传感器设置频率缩放比为:%2 -> s0,s1[0,1]")
elif scaling == 20: # 20%
self.s0.value = True
self.s1.value = False
# print("TCS3200传感器设置频率缩放比为:%20 -> s0,s1[1,0]")
elif scaling == 100: # 100%
self.s0.value = True
self.s1.value = True
# print("TCS3200传感器设置频率缩放比为:%100 -> s0,s1[1,1]")
else: # 关闭
self.s0.value = False
self.s1.value = False
# print("TCS3200传感器设置频率缩放比为:%0 -> s0,s1[0,0]")
time.sleep(0.01) # 短暂延时稳定频率缩放比例然后来控制传感器的输出频率,过高的频率可能会不准确。
def set_color_filter(self, filter_type):
"""
设置传感器的颜色滤波器
# s2 s3
# L L Red
# H H Green
# L H Blue
# H L Clear(no filter)
"""
if filter_type == "Red":
self.s2.value = False
self.s3.value = False
# print("TCS3200传感器设置颜色滤波器为:Red -> s2,s3[0,0]")
elif filter_type == "Green":
self.s2.value = True
self.s3.value = True
# print("TCS3200传感器设置颜色滤波器为:Green -> s2,s3[1,1]")
elif filter_type == "Blue":
self.s2.value = False
self.s3.value = True
# print("TCS3200传感器设置颜色滤波器为:Blue -> s2,s3[0,1]")
else: # "Clear"
self.s2.value = True
self.s3.value = False
# print("TCS3200传感器设置颜色滤波器为:Clear -> s2,s3[1,0]")
time.sleep(0.01) # 短暂延时稳定滤波器使用光照滤波器控制IO仅仅接收对应通道的数据。
def measure_frequency(self):
# 测量频率,并转换单位为Hz
timestamps = []
last_state = self.out.value
while len(timestamps) < self.NUM_CYCLES:
current_state = self.out.value
if current_state != last_state: # 发生边缘变化
timestamps.append(time.monotonic_ns())
last_state = current_state
# 计数周期
periods = []
for i in range(2, len(timestamps), 2):
period_ns = timestamps[i] - timestamps[i - 2] # 一个完整周期(两个边缘)
periods.append(period_ns)
avg_period_ns = sum(periods) / len(periods)
frequency = 1000000000 / avg_period_ns # 转换为 Hz
# print(f"原始频率值 -> Frequency:{frequency:.3f}")
return frequency对输出的频率进行检测。
def read_rgb_freq(self):
# 读取RGB三个通道的频率值
red_freq = 0
green_freq = 0
blue_freq = 0
# 读取红色分量 (S2=0, S3=0)
self.set_color_filter("Red")
red_freq = self.measure_frequency()
# 读取绿色分量 (S2=1, S3=1)
self.set_color_filter("Green")
green_freq = self.measure_frequency()
# 读取蓝色分量 (S2=0, S3=1)
self.set_color_filter("Blue")
blue_freq = self.measure_frequency()
# 清除绿色分量 (S2=1, S3=0)
# self.set_color_filter("Clear")
# print(f"原始频率值 -> R:{red_freq:.3f}, G:{green_freq:.3f}, B:{blue_freq:.3f}")
return red_freq, green_freq, blue_freq然后再上述的方法中,依次的打开和关闭对应的RGB通道来读取对应频率从而来计算出颜色的信息。真的不得不感叹设计这个模块的人真的是天才,竟然能通过这种方法从而来读取到颜色的数据。不过如果是数字量的输出的话其实也就是类似在这个输出上加了一层,可以直接输出RGB。
def read_rgb(self): # 读取RGB三个通道的频率值,应用白平衡,并转换为RGB r = 0 g = 0 b = 0 # 读取RGB三个通道的频率值 red_freq, green_freq, blue_freq = self.read_rgb_freq() # 应用白平衡校准 (18000/255 = 70) r = int(red_freq / 70 * self.r_scal) g = int(green_freq / 70 * self.g_scal) b = int(blue_freq / 70 * self.b_scal) # 限制在0-255范围 r = max(0, min(255, r)) g = max(0, min(255, g)) b = max(0, min(255, b)) return r, g, b
最后的方法就是白平衡的校准、使其传感器更加精确。完成代码如下所示
import board
import digitalio
import time
import displayio
import terminalio
from adafruit_display_text import label
# 定义按键变量
Button0_Value = 0
Button1_Value = 0
Button2_Value = 0
class TCS3200:
NUM_CYCLES = 10 # 测试多少个周期
def __init__(self, s0_pin, s1_pin, s2_pin, s3_pin, out_pin):
# 初始化控制引脚
self.s0 = digitalio.DigitalInOut(s0_pin)
self.s1 = digitalio.DigitalInOut(s1_pin)
self.s2 = digitalio.DigitalInOut(s2_pin)
self.s3 = digitalio.DigitalInOut(s3_pin)
self.out = digitalio.DigitalInOut(out_pin)
# 配置引脚方向
self.s0.direction = digitalio.Direction.OUTPUT
self.s1.direction = digitalio.Direction.OUTPUT
self.s2.direction = digitalio.Direction.OUTPUT
self.s3.direction = digitalio.Direction.OUTPUT
self.out.direction = digitalio.Direction.INPUT
# 设置频率缩放比为100% (最高精度)
self.set_frequency_scaling(100)
# 白平衡校准系数 (初始值为1,需要实际校准)
self.r_scal = 1.0
self.g_scal = 1.0
self.b_scal = 1.0
print("TCS3200传感器初始化完成")
def set_frequency_scaling(self, scaling):
"""
设置传感器的频率缩放比例
# s0 s1
# L L 关闭
# L H 2%
# H L 20%
# H H 100%
"""
if scaling == 2: # 2%
self.s0.value = False
self.s1.value = True
# print("TCS3200传感器设置频率缩放比为:%2 -> s0,s1[0,1]")
elif scaling == 20: # 20%
self.s0.value = True
self.s1.value = False
# print("TCS3200传感器设置频率缩放比为:%20 -> s0,s1[1,0]")
elif scaling == 100: # 100%
self.s0.value = True
self.s1.value = True
# print("TCS3200传感器设置频率缩放比为:%100 -> s0,s1[1,1]")
else: # 关闭
self.s0.value = False
self.s1.value = False
# print("TCS3200传感器设置频率缩放比为:%0 -> s0,s1[0,0]")
time.sleep(0.01) # 短暂延时稳定频率缩放比例
def set_color_filter(self, filter_type):
"""
设置传感器的颜色滤波器
# s2 s3
# L L Red
# H H Green
# L H Blue
# H L Clear(no filter)
"""
if filter_type == "Red":
self.s2.value = False
self.s3.value = False
# print("TCS3200传感器设置颜色滤波器为:Red -> s2,s3[0,0]")
elif filter_type == "Green":
self.s2.value = True
self.s3.value = True
# print("TCS3200传感器设置颜色滤波器为:Green -> s2,s3[1,1]")
elif filter_type == "Blue":
self.s2.value = False
self.s3.value = True
# print("TCS3200传感器设置颜色滤波器为:Blue -> s2,s3[0,1]")
else: # "Clear"
self.s2.value = True
self.s3.value = False
# print("TCS3200传感器设置颜色滤波器为:Clear -> s2,s3[1,0]")
time.sleep(0.01) # 短暂延时稳定滤波器
def measure_frequency(self):
# 测量频率,并转换单位为Hz
timestamps = []
last_state = self.out.value
while len(timestamps) < self.NUM_CYCLES:
current_state = self.out.value
if current_state != last_state: # 发生边缘变化
timestamps.append(time.monotonic_ns())
last_state = current_state
# 计数周期
periods = []
for i in range(2, len(timestamps), 2):
period_ns = timestamps[i] - timestamps[i - 2] # 一个完整周期(两个边缘)
periods.append(period_ns)
avg_period_ns = sum(periods) / len(periods)
frequency = 1000000000 / avg_period_ns # 转换为 Hz
# print(f"原始频率值 -> Frequency:{frequency:.3f}")
return frequency
def read_rgb_freq(self):
# 读取RGB三个通道的频率值
red_freq = 0
green_freq = 0
blue_freq = 0
# 读取红色分量 (S2=0, S3=0)
self.set_color_filter("Red")
red_freq = self.measure_frequency()
# 读取绿色分量 (S2=1, S3=1)
self.set_color_filter("Green")
green_freq = self.measure_frequency()
# 读取蓝色分量 (S2=0, S3=1)
self.set_color_filter("Blue")
blue_freq = self.measure_frequency()
# 清除绿色分量 (S2=1, S3=0)
# self.set_color_filter("Clear")
# print(f"原始频率值 -> R:{red_freq:.3f}, G:{green_freq:.3f}, B:{blue_freq:.3f}")
return red_freq, green_freq, blue_freq
def calibrate_white_balance(self):
# 白平衡校准 - 将传感器对准白色参考物后调用此方法
print("正在进行白平衡校准...")
print("请将传感器对准白色参考物")
# 读取白色参考物的原始频率
red, green, blue = self.read_rgb_freq()
# 换算为RGB值 (18000/255 = 70)
red = red / 70 if red > 0 else 1.0
green = green / 70 if green > 0 else 1.0
blue = blue / 70 if blue > 0 else 1.0
# 计算校准系数 (假设我们希望白色时RGB值接近255)
self.r_scal = 255.0 / red if red > 0 else 1.0
self.g_scal = 255.0 / green if green > 0 else 1.0
self.b_scal = 255.0 / blue if blue > 0 else 1.0
print(f"校准完成 -> R:{self.r_scal:.3f}, G:{self.g_scal:.3f}, B:{self.b_scal:.3f}")
def read_rgb(self):
# 读取RGB三个通道的频率值,应用白平衡,并转换为RGB
r = 0
g = 0
b = 0
# 读取RGB三个通道的频率值
red_freq, green_freq, blue_freq = self.read_rgb_freq()
# 应用白平衡校准 (18000/255 = 70)
r = int(red_freq / 70 * self.r_scal)
g = int(green_freq / 70 * self.g_scal)
b = int(blue_freq / 70 * self.b_scal)
# 限制在0-255范围
r = max(0, min(255, r))
g = max(0, min(255, g))
b = max(0, min(255, b))
return r, g, b
# 初始化板载按键D0/BOOT0,按下接地
button0 = digitalio.DigitalInOut(board.BUTTON)
button0.switch_to_input(pull=digitalio.Pull.UP)
# 初始化板载按键D1,按下接VCC
button1 = digitalio.DigitalInOut(board.D1)
button1.switch_to_input(pull=digitalio.Pull.DOWN)
# 初始化板载按键D2,按下接VCC
button2 = digitalio.DigitalInOut(board.D2)
button2.switch_to_input(pull=digitalio.Pull.DOWN)
# 初始化板载LED
led0 = digitalio.DigitalInOut(board.LED)
# led0 = digitalio.DigitalInOut(board.D13)
led0.direction = digitalio.Direction.OUTPUT
led0.value = True
# 初始化颜色传感器LED补光灯
led1 = digitalio.DigitalInOut(board.D5)
led1.direction = digitalio.Direction.OUTPUT
led1.value = True
# 初始化显示屏
display = board.DISPLAY
# 创建显示组
splash = displayio.Group()
display.root_group = splash
# 创建DIY活动文本标签
textdo = "Let's do EEPW DIY"
# text_area_do = label.Label(terminalio.FONT, text=textdo, color=0xFFFF00, x=60, y=20)
text_area_do = label.Label(terminalio.FONT, text=textdo, color=0xFFFF00, x=20, y=20, scale=2)
# 创建文本标签用于显示RGB值
text_area_r = label.Label(terminalio.FONT, text="R: ---", color=0xFF0000, x=10, y=50)
text_area_g = label.Label(terminalio.FONT, text="G: ---", color=0x00FF00, x=10, y=70)
text_area_b = label.Label(terminalio.FONT, text="B: ---", color=0x0000FF, x=10, y=90)
text_area_h = label.Label(terminalio.FONT, text="#------", color=0xFFFFFF, x=10, y=120)
# 显示输出
for text_area in (text_area_do, text_area_r, text_area_g, text_area_b, text_area_h):
splash.append(text_area)
# 创建TCS3200颜色传感器实体
try:
# 创建TCS3200对象
color_sensor = TCS3200(
s0_pin=board.D12,
s1_pin=board.D11,
s2_pin=board.D10,
s3_pin=board.D9,
out_pin=board.D6
)
print("TCS3200实例创建成功")
except Exception as e:
print(f"初始化TCS3200失败: {e}")
# 创建虚拟传感器用于测试
color_sensor = None
# 主循环
while True:
led0.value = True
time.sleep(0.05)
# 读取按键0
if not button0.value:
print("button0按下")
Button0_Value = 1
Button1_Value = 0
Button2_Value = 0
# 读取按键1
if button1.value:
print("button1按下")
Button0_Value = 0
Button1_Value = 1
Button2_Value = 0
# 读取按键2
if button2.value:
print("button2按下")
Button0_Value = 0
Button1_Value = 0
Button2_Value = 1
if Button0_Value:
Button0_Value = 0
# 调用白平衡校准
color_sensor.calibrate_white_balance()
# 更新显示
text_area_r.text = "R: {:.3f}".format(color_sensor.r_scal)
text_area_g.text = "G: {:.3f}".format(color_sensor.g_scal)
text_area_b.text = "B: {:.3f}".format(color_sensor.b_scal)
text_area_h.text = "#------"
if Button1_Value:
# 读取并显示TCS3200颜色传感器的频率值
if color_sensor is not None:
try:
# 调用read_rgb_freq方法读取频率值
R_Val, G_Val, B_Val = color_sensor.read_rgb_freq()
# 更新显示
text_area_r.text = "R: {:.3f}".format(R_Val)
text_area_g.text = "G: {:.3f}".format(G_Val)
text_area_b.text = "B: {:.3f}".format(B_Val)
text_area_h.text = "#: {:.3f},{:.3f},{:.3f}".format(R_Val, G_Val, B_Val)
print("RGB: {:.3f},{:.3f},{:.3f}".format(R_Val, G_Val, B_Val))
except Exception as e:
print(f"读取颜色传感器错误: {e}")
R_Val = 128
G_Val = 128
B_Val = 128
else:
# 更新显示屏
text_area_r.text = "R: {:.3f}".format(R_Val)
text_area_g.text = "G: {:.3f}".format(G_Val)
text_area_b.text = "B: {:.3f}".format(B_Val)
text_area_h.text = "#: {:.3f},{:.3f},{:.3f}".format(R_Val, G_Val, B_Val)
if Button2_Value:
# 读取并显示TCS3200颜色传感器的RGB值
if color_sensor is not None:
try:
# 调用read_rgb方法读取颜色值
R_Val, G_Val, B_Val = color_sensor.read_rgb()
# 更新显示
text_area_r.text = "R: {:3d}".format(R_Val)
text_area_g.text = "G: {:3d}".format(G_Val)
text_area_b.text = "B: {:3d}".format(B_Val)
text_area_h.text = "#: {:02X}{:02X}{:02X}".format(R_Val, G_Val, B_Val)
print("RGB: #{:02X}{:02X}{:02X}".format(R_Val, G_Val, B_Val))
except Exception as e:
print(f"读取颜色传感器错误: {e}")
R_Val = 128
G_Val = 128
B_Val = 128
else:
# 更新显示屏
text_area_r.text = "R: {:3d}".format(R_Val)
text_area_g.text = "G: {:3d}".format(G_Val)
text_area_b.text = "B: {:3d}".format(B_Val)
text_area_h.text = "#: {:02X}{:02X}{:02X}".format(R_Val, G_Val, B_Val)
led0.value = False
time.sleep(0.05)实验效果


我要赚赏金
