1. 从“看见”到“识别”:TCS3200颜色传感器入门

今天想和大家聊聊一个挺有意思的小项目——用STM32驱动TCS3200颜色传感器。这玩意儿听起来挺高大上,什么“颜色识别”,其实原理并不复杂,但真要把数据读准、读稳,确实需要花点心思。我昨天下午就折腾了好几个小时,从接线、配置寄存器到最后的滤波算法,踩了几个不大不小的坑,总算是把红绿蓝三原色的值稳定地读出来了。整个过程就像给单片机装上了一双能“分辨颜色”的眼睛,虽然比不上人眼,但对于很多自动化场景,比如流水线分拣、智能玩具或者简单的环境光检测,已经足够用了。

TCS3200是一颗基于光电原理的传感器,核心是一个8x8的光电二极管阵列,上面覆盖了红、绿、蓝和透明四种滤光片。它不直接输出颜色值,而是输出一个频率信号,这个频率与照射到传感器上的光强成正比。我们的任务,就是用STM32的定时器去捕获这个频率,再通过一定的比例换算,还原出RGB分量。听起来简单,但实际做起来,你会发现光照条件、传感器距离、甚至电源噪声都会对结果产生巨大影响。这篇文章,我就把自己从零搭建、调试到最终稳定运行的完整过程,以及中间遇到的那些“坑”和解决方案,详细地捋一遍。无论你是刚接触STM32的新手,还是想给项目增加颜色识别功能的老鸟,希望这些经验都能帮你省下一些摸索的时间。

2. TCS3200传感器的工作原理与硬件连接要点

TCS3200的颜色识别,本质上是一个“光电转换+频率调制”的过程。理解这个过程,是后续一切编程和调试的基础。

2.1 核心:光电二极管阵列与滤光片

拆开看TCS3200的内部,它有一个8x8共64个光电二极管的阵列。这64个二极管并不是一视同仁的,它们被分成了4组,每组16个,分别覆盖了红色、绿色、蓝色和全透明(无滤光片)的滤光片。当光照射到传感器表面时,只有特定波长的光能通过对应的滤光片,激发其下方的光电二极管产生电流。这个设计非常巧妙,它相当于用硬件完成了初步的光谱分离。

传感器内部集有一个电流-频率(I-F)转换器。每组光电二极管产生的光电流,会被汇总并送到这个转换器里。转换器输出的就是一个方波信号(OUT引脚),这个方波的频率与输入的光电流大小呈线性关系。光越强,电流越大,输出的频率就越高。所以,我们最终从传感器读到的,不是一个电压值,也不是一个数字量,而是一个频率值。这是TCS3200与那些直接输出数字RGB值的传感器(如TCS34725)最大的不同,也决定了我们后续必须使用STM32的输入捕获功能来读取数据。

2.2 引脚功能与STM32连接方案

TCS3200的引脚不多,但每个都有明确用途:

  • S0, S1 : 输出频率分频选择引脚。这是第一个容易让人困惑的点。传感器输出的原始频率可能很高(可达600kHz),直接测量对MCU要求高。这两个引脚可以设置分频系数,降低输出频率,方便测量。常见设置:S0=S1=HIGH(输出频率100%), S0=H, S1=L(输出频率20%), S0=L, S1=H(输出频率2%)。对于STM32,如果定时器时钟频率较高(如72MHz),测量几百kHz的信号问题不大,初期调试我建议先设置为100%输出,数据变化更明显。
  • S2, S3 : 滤光片选择引脚。这是核心控制引脚。通过设置S2和S3的高低电平,我们可以选择让哪一组光电二极管(即哪种颜色的滤光片)工作。其真值表为:S2=L, S3=L(选择红色滤光片), S2=L, S3=H(选择蓝色滤光片), S2=H, S3=L(选择无滤光片,即“清除”通道), S2=H, S3=H(选择绿色滤光片)。 注意 :很多资料里蓝色和绿色的顺序容易记混,务必以数据手册或实测为准。
  • OUT : 频率信号输出引脚。这就是我们要测量的信号线,需要连接到STM32的定时器通道引脚,并配置为输入捕获模式。
  • OE (Output Enable): 输出使能引脚,低电平有效。通常直接接地,让传感器始终输出。
  • VCC, GND : 电源和地。TCS3200工作电压范围是2.7V到5.5V。如果STM32是3.3V系统,传感器也接3.3V即可,但要注意输出频率的幅度是否满足STM32的输入高电平阈值(通常VIL<0.99V, VIH>2.31V for 3.3V)。如果担心,可以接5V,并在OUT引脚串联一个1kΩ左右的电阻到STM32的IO口,起限流作用,一般STM32的IO口耐压5V。

我的连接方案(以STM32F103C8T6为例):

  • S0, S1: 连接到两个普通的GPIO输出口(如PA1, PA2),用于软件控制分频。
  • S2, S3: 连接到两个普通的GPIO输出口(如PA3, PA4),用于软件选择滤光片。
  • OUT: 连接到定时器2的通道1输入引脚(PA0,因为TIM2_CH1对应PA0)。
  • OE: 直接接地。
  • VCC: 接3.3V。
  • GND: 共地。

注意:电源稳定性非常关键。我在调试时发现,如果使用开发板上通过LDO转换的3.3V,且板上其他模块(如电机、屏幕)同时工作时,电源纹波会导致OUT信号频率有微小抖动,严重影响测量稳定性。建议给TCS3200单独供电或加一个简单的LC滤波(例如一个10μF电解电容并联一个0.1μF陶瓷电容紧贴传感器VCC和GND引脚)。

3. STM32定时器输入捕获配置与频率测量逻辑

既然传感器输出的是频率信号,那么STM32的定时器输入捕获功能就是我们的核心武器。这里我以STM32F1的通用定时器TIM2为例,讲解如何配置。

3.1 定时器基础配置与输入捕获设置

我们的目标是测量OUT引脚上方波的周期,然后换算成频率。输入捕获的原理是:在捕获引脚(PA0)的上升沿(或下降沿)到来时,硬件会自动将当前定时器计数器(CNT)的值锁存到捕获/比较寄存器(CCR)中。通过连续捕获两个上升沿的CNT值,它们的差值就是这段时间内定时器计数的个数,结合定时器的计数频率,就能算出周期。

首先,需要初始化定时器的基础时基单元:

  1. 时钟源 :使用内部时钟(APB1总线,在F103上通常为72MHz,但定时器有倍频,实际CK_INT可达72MHz)。
  2. 预分频器(PSC) :这个值决定了定时器计数器的实际计数频率。 计数频率 = 定时器时钟 / (PSC + 1) 。为了能测量较宽范围的频率,我们需要权衡。如果PSC设为0,计数频率是72MHz,每个计数周期约13.9ns,精度高,但计数器(ARR为16位最大值65535)很快会溢出,适合测高频。如果PSC设置过大,精度会下降。对于TCS3200,其输出频率在分频后通常在几kHz到几百kHz。我经过测试,将PSC设置为71,这样 计数频率 = 72MHz / (71+1) = 1MHz ,即每个计数代表1微秒。这样,测量1kHz的信号周期为1000微秒,计数器值为1000,非常直观,且16位计数器能测量的最大周期为65535微秒(约65ms),对应最低频率约15Hz,完全够用。
  3. 自动重装载值(ARR) :设置为最大值65535。因为我们是用两次捕获值的差来算周期,ARR只要大于可能的最大周期计数值即可。

然后,配置通道1为输入捕获模式:

  1. 输入捕获模式 :选择直接映射到TI1(即PA0)。
  2. 边沿检测 :选择上升沿捕获。这样每次OUT引脚出现上升沿,就会触发捕获事件。
  3. 捕获预分频器 :设置为不分频(每次捕获都有效)。
  4. 使能捕获/比较中断 :这是关键。我们需要在捕获事件发生时,进入中断服务程序,读取CCR1的值。

3.2 中断服务程序中的频率计算逻辑

配置好硬件,逻辑就集中在中断服务函数里了。思路是:用静态变量记录上一次捕获的值( lastCaptureValue )和当前的捕获值( thisCaptureValue )。每次进入中断,读取CCR1到 thisCaptureValue

核心计算

  • periodInTicks = thisCaptureValue - lastCaptureValue 。这里有个 大坑 :定时器计数器是16位的,会从0到65535循环。如果 thisCaptureValue 小于 lastCaptureValue ,说明发生了溢出,实际的周期应该是 (65535 - lastCaptureValue) + thisCaptureValue + 1 。很多例程忽略了这一点,在测量低频时会导致周期计算错误。
  • 周期(微秒): periodInUs = periodInTicks * (1 / 计数频率) = periodInTicks * 1us (因为之前我们配置了1MHz计数频率)。
  • 频率(Hz): frequency = 1000000 / periodInUs

但是,直接这样算频率会有浮点数运算,在MCU中效率不高。更常见的做法是,我们其实不关心绝对频率,而是关心 相对光强 。因为最终RGB值是通过比较红、绿、蓝三个通道的读数比例得到的。所以,我们可以直接用捕获到的 periodInTicks (即周期计数值)的倒数来代表光强。 周期值越小,频率越高,光强越强 。因此,我们通常记录的是周期值,并在后续处理中将其转换为一个与光强成正比的数值。

我的中断服务函数(IRQHandler)伪代码逻辑如下:

uint32_t lastCapture = 0;
uint32_t thisCapture = 0;
uint32_t periodTicks = 0;
volatile uint8_t captureFlag = 0; // 用于主程序查询的新数据标志

void TIM2_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) {
        // 清除中断标志
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);

        // 读取本次捕获值
        thisCapture = TIM_GetCapture1(TIM2);

        // 计算周期(处理计数器溢出)
        if (thisCapture >= lastCapture) {
            periodTicks = thisCapture - lastCapture;
        } else {
            periodTicks = (0xFFFF - lastCapture) + thisCapture + 1;
        }

        // 更新上一次捕获值
        lastCapture = thisCapture;

        // 设置标志,通知主程序有新数据
        if (periodTicks > 0 && periodTicks < 60000) { // 简单滤波,去除异常值
            captureFlag = 1;
        }
    }
}

注意:中断函数里不要做复杂的计算或打印,只做最必要的操作(计算周期、设置标志)。将数据传递到主循环或通过DMA传输是更好的方式,但初期用标志位查询法最简单直观。

4. 软件流程设计:如何有序获取RGB原始数据

有了测量单个频率的能力,下一步就是设计一个状态机或者流程,来循环获取红、绿、蓝三个通道的原始数据。这里不能同时获取,因为传感器同一时间只能让一种滤光片工作。

4.1 主循环中的状态控制

我采用一个简单的状态机,在主循环中不断轮询。基本状态有: IDLE , MEASURE_RED , MEASURE_GREEN , MEASURE_BLUE , MEASURE_CLEAR (可选)。每个测量状态下的操作是类似的:

  1. 设置S2、S3引脚,选择对应的滤光片。
  2. 延迟一小段时间(例如10-50ms),让传感器输出稳定。 这个延迟非常必要 ,TCS3200切换滤光片后,输出需要几个周期才能稳定。
  3. 清除 captureFlag ,并等待一定时间或等待多次捕获完成。我采用的方法是:等待 captureFlag 被置位,然后读取 periodTicks ,存入对应的数组( redRaw , greenRaw , blueRaw )。为了提高可靠性,可以连续采样N次(比如10次),然后取中值或平均值。
  4. 进入下一个测量状态。

一个简化的主循环片段:

typedef enum {
    STATE_IDLE,
    STATE_MEASURE_RED,
    STATE_MEASURE_GREEN,
    STATE_MEASURE_BLUE,
    STATE_PROCESS
} MeasureState_t;

MeasureState_t state = STATE_MEASURE_RED;
uint32_t redRaw = 0, greenRaw = 0, blueRaw = 0;
uint8_t sampleCount = 0;
uint32_t sampleSum = 0;

while(1) {
    switch(state) {
        case STATE_MEASURE_RED:
            TCS3200_SetFilter(RED_FILTER); // 设置S2,S3为选择红色滤光片
            HAL_Delay(20); // 稳定时间
            sampleSum = 0;
            for(int i=0; i<10; i++) {
                captureFlag = 0;
                while(captureFlag == 0); // 等待一次捕获完成
                sampleSum += periodTicks;
            }
            redRaw = sampleSum / 10; // 取10次平均值
            state = STATE_MEASURE_GREEN;
            break;

        case STATE_MEASURE_GREEN:
            // ... 类似操作,选择绿色滤光片
            state = STATE_MEASURE_BLUE;
            break;

        case STATE_MEASURE_BLUE:
            // ... 类似操作,选择蓝色滤光片
            state = STATE_PROCESS;
            break;

        case STATE_PROCESS:
            // 在这里对 redRaw, greenRaw, blueRaw 进行后续处理(白平衡、映射等)
            processRGB(redRaw, greenRaw, blueRaw);
            state = STATE_MEASURE_RED; // 开始新一轮测量
            // 可以在这里加入延时,控制采样率,比如每秒采样1-5组
            HAL_Delay(200);
            break;
    }
}

4.2 关于“清除”(Clear)通道的使用

你可能注意到数据手册和很多例程里提到了第四个通道——无滤光片的“清除”通道。这个通道接收所有波长的光,其读数理论上反映了环境光的总体强度。它主要有两个用途:

  1. 自动增益控制(AGC)的参考 :如果环境光变化剧烈,可以用Clear通道的值来动态调整S0、S1的分频比,或者作为RGB值归一化的基准,使得RGB读数在不同光照下具有可比性。例如,将R、G、B的原始值分别除以Clear通道的值,再进行后续处理。
  2. 检测是否有光 :作为一个简单的是否有物体靠近或环境光是否存在的判断。

在基础的颜色识别中,你可以先不使用Clear通道,专注于把R、G、B三个通道的数据读稳。当基本功能实现后,再引入Clear通道来做白平衡优化,这是一个进阶的步骤。

5. 数据处理、白平衡与颜色识别实战

拿到R、G、B三个原始周期值( redRaw , greenRaw , blueRaw )后,你会发现它们离我们想要的“颜色”还有很大距离。直接使用这些值,你会发现白色物体可能偏蓝,黑色物体读数也不为零。这就需要一系列的数据处理。

5.1 原始数据的特性与问题

首先明确一点:我们得到的原始值(周期计数值)与 光强成反比 。光越强,传感器输出频率越高,周期值越小。所以:

  • 对着一个明亮的红色物体, redRaw 值会很小(可能几百),而 greenRaw blueRaw 会很大(可能几千)。
  • 在黑暗环境下,所有值都会很大(接近计数器溢出值)。
  • 传感器本身、滤光片、LED光源(如果你用了的话)都有一定的光谱响应偏差,导致即使是在纯白光照下,R、G、B三个通道的读数也不相等。

5.2 关键步骤一:数据反转与线性化

为了使数值与光强成正比,更符合我们的直觉,我们需要进行“反转”。一种简单的方法是设定一个参考最大值(比如定时器能稳定捕获的最大周期值,我设为60000),然后用这个值减去原始值: R_inv = REF_MAX - redRaw G_inv = REF_MAX - greenRaw B_inv = REF_MAX - blueRaw 现在, R_inv , G_inv , B_inv 这三个值就与各自通道的光强成正比了。数值越大,表示该颜色分量越强。

5.3 关键步骤二:白平衡校准

这是颜色识别准确与否的灵魂。白平衡的目的是让传感器认为“白色”就是白色。具体操作是:找一个你认为是白色的标准参照物(比如一张纯白的A4纸,或者专业的白平衡卡),在最终使用的光照条件下,用传感器去测量它。

假设测量白色参照物得到的三通道反转值为 (Rw_inv, Gw_inv, Bw_inv) 。理论上,在理想白色下,这三个值应该相等。但实际上它们不相等。我们计算三个比例系数: Kr = (Gw_inv + Bw_inv) / (2.0 * Rw_inv) // 红色通道的校正系数 Kg = 1.0 // 绿色通道通常作为参考,系数为1 Kb = (Rw_inv + Gw_inv) / (2.0 * Bw_inv) // 蓝色通道的校正系数

注意:这里的分母是目标通道的值。系数的计算方式可以有多种,核心思想是让白点的三通道值归一化。这里我采用了一种常见方法:让红色和蓝色通道向绿色通道看齐。你也可以让三通道向它们的最大值或平均值看齐。

然后,对于后续测量的任何颜色,其校正后的值 (R_bal, G_bal, B_bal) 为: R_bal = R_inv * Kr G_bal = G_inv * Kg (实际上就是 G_inv ) B_bal = B_inv * Kb

经过白平衡后,你再用传感器去测那张白纸,理论上 R_bal , G_bal , B_bal 三个值就会非常接近了。

5.4 关键步骤三:归一化与颜色判断

白平衡后的数据范围可能很大也可能很小,为了方便显示和判断,通常进行归一化到0-255的范围(即标准的RGB888格式)。

首先找到 R_bal, G_bal, B_bal 中的最大值 maxVal 。 然后计算归一化系数 scale = 255.0 / maxVal 。 最后: R_norm = (uint8_t)(R_bal * scale) G_norm = (uint8_t)(G_bal * scale) B_norm = (uint8_t)(B_bal * scale)

现在,你得到了标准的RGB值。颜色识别就变得简单了:

  • 单色识别 :判断哪个通道的值最大。例如,如果 R_norm 远大于 G_norm B_norm ,则可以认为是红色。
  • 阈值判断 :设定一些经验阈值。比如识别红、绿、蓝三原色:
    • 红色:(R > 200) && (G < 100) && (B < 100)
    • 绿色:(G > 200) && (R < 100) && (B < 100)
    • 蓝色:(B > 200) && (R < 100) && (G < 100)
  • 色差计算 :对于更复杂的颜色,可以计算当前RGB值与预设颜色模板的欧氏距离,距离最小的即为识别结果。这在MCU上计算量稍大,但更精确。

6. 调试过程中的“坑”与稳定性优化经验

理论很美好,调试很骨感。下面是我在一下午的折腾中遇到的几个典型问题及解决办法。

6.1 信号噪声与软件滤波

最初,我直接读取单次捕获的周期值,发现数值跳变非常厉害,同一个颜色,连续读数可能相差几十甚至上百个计数。这主要是由电源噪声、环境光微小变化以及传感器本身噪声引起的。

解决方案

  1. 硬件滤波 :在传感器OUT引脚和STM32输入引脚之间,增加一个简单的RC低通滤波电路(例如一个1kΩ电阻串联,然后对地接一个100pF电容)。这可以滤除一些高频毛刺。
  2. 软件多次采样 :这是最有效的方法。不要相信单次读数。我采用的方法是 中值滤波 。连续采样奇数次(比如7次或15次),然后将这些值排序,取中间的那个值作为本次测量结果。中值滤波对于去除偶发的尖峰脉冲干扰(比如电机的火花干扰)特别有效。
  3. 滑动平均滤波 :在多次采样的基础上,还可以对连续几组的最终结果(比如归一化后的RGB值)进行滑动平均,使输出更平滑。但要注意这会引入延迟。

我的改进版采样函数:

uint32_t getFilteredPeriod(void) {
    uint32_t samples[15];
    for(int i=0; i<15; i++) {
        captureFlag = 0;
        while(captureFlag == 0); // 等待一次捕获
        samples[i] = periodTicks;
        // 可以加一个很小的延时,确保捕获到的是不同的周期
        delay_us(10);
    }
    // 简单的冒泡排序找中值(对于15个数据,可以优化)
    bubbleSort(samples, 15);
    return samples[7]; // 返回第8个元素(0索引,即中位数)
}

6.2 环境光的影响与应对

TCS3200对环境光极其敏感。在室内,开灯和关灯,或者白天和晚上,读出的原始值能差好几倍。这会导致两个问题:一是白平衡系数失效,二是颜色判断阈值失效。

解决方案

  1. 固定光源 :这是最根本的解决方案。为你的检测环境增加一个稳定的、覆盖传感器工作区域的光源(比如一个白色的LED,加上乳白色灯罩使其光线均匀)。这样就能创造一个稳定的“光照实验室”,彻底排除环境光干扰。很多工业颜色分选机都是这么做的。
  2. 使用Clear通道动态补偿 :如果无法避免环境光变化,可以引入Clear通道。在每次测量颜色前(或定期),测量一次Clear通道的值 C_inv 。然后将R、G、B的原始反转值分别除以 C_inv ,得到一个与环境光强度相对无关的比值。然后再对这个比值进行白平衡和归一化。公式类似于: R_adj = (R_inv / C_inv) * Kr 。这种方法在环境光缓慢变化时有一定效果。
  3. 自适应阈值 :如果不方便加光源,又不想用Clear通道,可以尝试让颜色判断的阈值动态化。例如,不判断绝对RGB值,而是判断R、G、B三者之间的比例关系。比如识别红色,条件可以改为 (R_norm > G_norm * 2) && (R_norm > B_norm * 2) 。这种方法对光照强度变化有一定鲁棒性,但对光照色温变化(比如日光灯和白炽灯)依然敏感。

6.3 定时器溢出与测量范围限制

在配置定时器时,我们设置了1MHz的计数频率。对于周期测量,能测量的最大周期受限于16位计数器的最大值65535,即65.535ms,对应最低频率约15Hz。TCS3200在很暗的环境下,输出频率可能会低于这个值,导致测量出错(周期值恒定为65535)。

解决方案

  1. 调整预分频器PSC :如果主要关心弱光环境,可以增大PSC,降低计数频率,从而扩大可测量的最大周期。例如,PSC设为719,则计数频率为100kHz,每个计数代表10微秒,最大可测周期为655.35ms,对应最低频率约1.5Hz。但代价是测量精度下降。
  2. 软件处理溢出 :在中断中,除了处理计数器循环溢出的情况,还可以设置一个“超时”机制。如果连续很长时间(比如超过预估最大周期的1.5倍)没有捕获到新的上升沿,则认为信号频率过低或丢失,返回一个特定的错误值或默认值。
  3. 动态分频 :结合传感器的S0、S1引脚。在测量前,先快速判断一下信号频率范围。如果发现周期值接近65535,则通过S0、S1提高传感器的输出分频比(比如从100%降到20%),这样传感器输出频率会降低5倍,但仍在可测范围内。不过这会增加软件复杂度。

7. 项目集成与进阶应用思路

当你能稳定地获取并识别几种基本颜色后,就可以把这个功能集成到更大的项目中了。

7.1 与上位机或显示设备通信

最简单的集成方式是通过串口将识别出的RGB值或颜色名称发送到电脑,用串口助手查看。也可以驱动一个OLED屏幕,实时显示当前检测到的颜色块和RGB数值,非常直观。对于STM32,使用HAL库或标准库的串口发送函数,在主循环的 STATE_PROCESS 状态后,将 R_norm, G_norm, B_norm 打包成一段字符串发送出去即可。

7.2 构建简单的颜色分拣装置

这是一个经典的DIY项目。你需要一个舵机(或步进电机)、一个容纳不同颜色物体的托盘(比如乐高积木)。工作流程可以是:

  1. 传感器检测托盘当前位置物体的颜色。
  2. MCU根据识别结果(红/绿/蓝),控制舵机旋转到对应的出口位置。
  3. 通过另一个机构(如推杆)将物体推出。 这个项目综合了传感器技术、电机控制和简单的逻辑判断,非常适合作为课程设计或兴趣项目。

7.3 进阶挑战:色卡识别与颜色匹配

如果你不满足于识别三原色,可以尝试识别更复杂的颜色,比如潘通色卡上的颜色。这需要更精细的数据处理:

  1. 建立颜色数据库 :事先用你的传感器,在固定光源下,测量一系列目标色卡的RGB值,并记录下来,作为模板。
  2. 色差计算 :当检测一个未知颜色时,计算其RGB值与数据库中每个模板颜色的“距离”。最常用的距离是欧氏距离: Distance = sqrt((R_test - R_ref)^2 + (G_test - G_ref)^2 + (B_test - B_ref)^2)
  3. 寻找最近邻 :找出距离最小的那个模板颜色,即为识别结果。 由于涉及浮点运算和开方,对STM32F103这类没有FPU的MCU会有一定压力。可以做一些优化,比如使用平方距离代替开方,或者将RGB颜色空间转换到更符合人眼感知的Lab空间后再计算距离(这更复杂,但效果更好)。

折腾一下午,从连上线没反应,到数据乱跳,再到最后能稳定区分出红绿蓝甚至浅粉、天蓝,这个过程虽然有点烧脑,但成就感十足。TCS3200作为一个入门级的颜色传感器,其价值在于让你亲手打通从模拟信号(光)到频率信号,再到数字处理(MCU)的完整链条。它暴露出来的所有问题——噪声、环境光干扰、数据稳定性——都是嵌入式传感系统中最真实、最经典的挑战。解决这些问题的过程,比单纯调用一个现成的I2C颜色传感器库,学到的东西要多得多。最后给想尝试的朋友一个建议:务必先搞定电源和硬件滤波,这是后续所有软件算法稳定的基石。然后耐心地做白平衡,别指望一次成功,多调几次系数。当你看到屏幕上稳定地显示出正确的颜色名字时,那一刻的快乐,就是对一下午“肝”代码最好的回报。

更多推荐