当前位置: 首页 > news >正文

零知开源——基于STM32F407VET6的TCS230颜色识别器设计与实现

 ✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!

✔访问零知实验室,获取更多实战项目和教程资源吧!

www.lingzhilab.com

目录

一、硬件系统部分

二、软件架构设计

三、操作过程及数据展示

四、TCS230技术原理

五、常见问题指引

六、结论


(1)项目概述

        本项目基于STM32F407VET6主控芯片的零知增强板,结合TCS230高精度颜色传感器和ST7789显示屏,开发了一套专业的颜色识别系统。系统通过创新的独立通道校准算法,实现了对RGB颜色的高精度识别,并能将检测结果以直观的UI界面展示,同时提供标准的HEX颜色代码输出。项目优化了颜色识别过程中的通道干扰问题校准流程

(2)项目亮点

        >采用TCS230传感器,支持1600万色识别
        >RGB三通道独立校准,解决通道干扰问题
        >分区显示传感器数据和检测结果
        >带错误检测的三步校准流程
        >多次采样平均算法,抗干扰能力强

(3)项目难点及解决方案

问题描述:在识别纯色时,其他通道值偏高(如识别绿色时红蓝值偏高)

解决方案

        >独立通道校准算法
        >多次采样平均值滤波
        >通道干扰补偿机制

一、硬件系统部分

1.1 硬件清单

组件型号数量
主控板零知增强板(STM32F407VET6)1
颜色传感器TCS2301
显示屏ST7789 (240x320)1
按钮轻触开关1
杜邦线20cm若干

1.2 接线方案

TCS230传感器ST7789显示屏(SPI)零知增强板
VCCVCC5V / 3.3V
GNDGNDGND
/SCL52 (SPI1 SCL)
/SDA51 (SPI1 MOSI)
/RES47
/DC49
/CS53
S2/10
S3/11
OUT/12
按钮/13

ps:TCS230传感器S1、S0接5V电源,OE接GND

1.3 硬件连接图

1.4 实物连接图

二、软件架构设计

2.1 系统初始化

void setup() {Serial.begin(9600);tft.init(240, 320);          // 初始化240x320显示屏tft.setRotation(3);           // 横向显示tft.fillScreen(BACKGROUND);   // 设置背景色drawUI();                     // 绘制UI界面// 初始化传感器引脚pinMode(s2, OUTPUT);pinMode(s3, OUTPUT);pinMode(out, INPUT);pinMode(button, INPUT_PULLUP);showMessage("Please Calibrate!", WARNING_COLOR);
}

2.2 主循环逻辑

void loop() {// 检查串口校准命令if (Serial.available()) {char c = Serial.read();if (c == 'c' || c == 'C') {calibrate(); // 执行校准}}// 检查按钮是否按下if (digitalRead(button) == LOW) {detectColor(); // 执行颜色检测delay(300);    // 简单消抖}
}

2.3 颜色检测算法

void detectColor() {// 多次采样取平均值int r_sum = 0, g_sum = 0, b_sum = 0;const int samples = 3;for (int i = 0; i < samples; i++) {color();r_sum += red;g_sum += green;b_sum += blue;delay(50); // 采样间隔}red = r_sum / samples;green = g_sum / samples;blue = b_sum / samples;// 映射到0-255范围(使用独立通道校准)red = constrain(map(red, cal_min_r, cal_max_r, 255, 0), 0, 255);green = constrain(map(green, cal_min_g, cal_max_g, 255, 0), 0, 255);blue = constrain(map(blue, cal_min_b, cal_max_b, 255, 0), 0, 255);// 更新显示updateColorDisplay();// 串口输出Serial.print("R: "); Serial.print(red);Serial.print(" G: "); Serial.print(green);Serial.print(" B: "); Serial.println(blue);
}

2.4 系统校准

void calibrate() {// 重置校准值cal_min_r = 10000; cal_min_g = 10000; cal_min_b = 10000;cal_max_r = 0; cal_max_g = 0; cal_max_b = 0;// 第一步:黑色校准(取最大值)showMessage("BLACK surface", HIGHLIGHT);waitForButton(); // 等待按钮按下for (int i = 0; i < 5; i++) {color();cal_max_r = max(cal_max_r, red);// ... 其他通道类似Serial.print("Black Sample R="); Serial.println(red);}// 第二步:白色校准(取最小值)showMessage("WHITE surface", HIGHLIGHT);waitForButton(); // 等待按钮按下for (int i = 0; i < 5; i++) {color();cal_min_r = min(cal_min_r, red);// ... 其他通道类似Serial.print("White Sample R="); Serial.println(red);}// 校准值验证if (cal_min_r >= cal_max_r) {showMessage("Invalid Calibration!", WARNING_COLOR);} else {showMessage("Cal Success!", SUCCESS_COLOR);}
}

2.5 颜色数据采集

void color() {// 读取红色分量digitalWrite(s2, LOW);digitalWrite(s3, LOW);red = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);// 读取蓝色分量digitalWrite(s3, HIGH);blue = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);// 读取绿色分量digitalWrite(s2, HIGH);green = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);
}

2.6 完整代码

/*TCS230 Color Recognizer with ST7789 DisplayDesigned for 零知IDE 零知增强板Display Rotation: 3 (Landscape)Filename: ColorSensor_TCS230_ST7789_Optimized.ino
*/#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <Fonts/FreeSans18pt7b.h>  // 大字体用于标题
#include <Fonts/FreeSans12pt7b.h>  // 中字体用于按钮
#include <Fonts/FreeSans9pt7b.h>   // 小字体用于数值// ST7789 显示屏引脚定义
#define TFT_CS   53
#define TFT_DC   49
#define TFT_RST  47 Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);// 颜色定义
#define BACKGROUND    0x2104  // 深灰色背景
#define PANEL_COLOR   0x39C7  // 面板蓝灰色
#define TEXT_COLOR    0xFFFF  // 白色文字
#define HIGHLIGHT     0x07FF  // 青色高亮
#define WARNING_COLOR 0xF800  // 红色警告
#define SUCCESS_COLOR 0x07E0  // 绿色成功提示// UI 尺寸定义
#define PANEL_WIDTH 140
#define COLOR_BOX_SIZE 100
#define MARGIN 10// 颜色传感器引脚
const int s2 = 10;
const int s3 = 11;
const int out = 12;
const int button = 13;// 颜色变量
int red = 0;
int green = 0;
int blue = 0;// 独立通道校准值
int cal_min_r = 10000; // 初始化为较大值
int cal_min_g = 10000;
int cal_min_b = 10000;
int cal_max_r = 0;     // 初始化为较小值
int cal_max_g = 0;
int cal_max_b = 0;// 校准状态
unsigned long lastCalibration = 0;
bool calibrated = false;void setup() {Serial.begin(9600);// 初始化显示屏tft.init(240, 320);          // 初始化240x320显示屏tft.setRotation(3);           // 横向显示tft.invertDisplay(false);tft.fillScreen(BACKGROUND);   // 设置背景色// 绘制UI界面drawUI();// 初始化颜色传感器引脚pinMode(s2, OUTPUT);pinMode(s3, OUTPUT);pinMode(out, INPUT);pinMode(button, INPUT_PULLUP);// 初始校准提示showMessage("Please Calibrate!", WARNING_COLOR);
}void loop() {// 检查串口校准命令if (Serial.available()) {char c = Serial.read();if (c == 'c' || c == 'C') {calibrate();}}// 检查按钮是否按下if (digitalRead(button) == LOW) {detectColor();delay(300); // 简单消抖}
}void drawUI() {// 清屏tft.fillScreen(BACKGROUND);// 绘制标题tft.setFont(&FreeSans18pt7b);tft.setTextColor(HIGHLIGHT);tft.setCursor(20, 40);tft.print("COLOR SENSOR");// 左侧信息面板tft.fillRoundRect(MARGIN, 60, PANEL_WIDTH, 240, 10, PANEL_COLOR);// 传感器标签tft.setFont(&FreeSans9pt7b);tft.setTextColor(TEXT_COLOR);// 调整间距防止重叠tft.setCursor(MARGIN + 10, 90);tft.print("RED:");tft.setCursor(MARGIN + 10, 130);tft.print("GREEN:");tft.setCursor(MARGIN + 10, 170);tft.print("BLUE:");tft.setCursor(MARGIN + 10, 210);tft.print("STATUS:");// 校准按钮tft.fillRoundRect(MARGIN + 10, 250, PANEL_WIDTH - 20, 40, 5, HIGHLIGHT);tft.setFont(&FreeSans12pt7b);tft.setCursor(MARGIN + 25, 280);tft.print("CALIBRATE");// 右侧颜色显示区域tft.fillRoundRect(PANEL_WIDTH + MARGIN*2, 60, 320 - PANEL_WIDTH - MARGIN*3, 240, 10, PANEL_COLOR);uint16_t detectedColor = tft.color565(204, 0, 255);// 颜色显示框tft.fillRoundRect(PANEL_WIDTH + MARGIN*3 + 15, 75, COLOR_BOX_SIZE, COLOR_BOX_SIZE, 10, detectedColor);// 颜色框标签tft.setFont(&FreeSans9pt7b);tft.setCursor(PANEL_WIDTH + MARGIN*2 - 5, 200);tft.print("DETECTED COLOR");// RGB值显示区域tft.setCursor(PANEL_WIDTH + MARGIN*2 - 5, 235);tft.print("HEX: #");
}void detectColor() {// 多次采样取平均值int r_sum = 0, g_sum = 0, b_sum = 0;const int samples = 3;for (int i = 0; i < samples; i++) {color();r_sum += red;g_sum += green;b_sum += blue;delay(50); // 采样间隔}red = r_sum / samples;green = g_sum / samples;blue = b_sum / samples;// 映射到0-255范围(使用独立通道校准)red = constrain(map(red, cal_min_r, cal_max_r, 255, 0), 0, 255);green = constrain(map(green, cal_min_g, cal_max_g, 255, 0), 0, 255);blue = constrain(map(blue, cal_min_b, cal_max_b, 255, 0), 0, 255);// 更新显示updateColorDisplay();// 串口输出Serial.print("R: "); Serial.print(red);Serial.print(" G: "); Serial.print(green);Serial.print(" B: "); Serial.println(blue);
}void updateColorDisplay() {// 转换为16位颜色值uint16_t detectedColor = tft.color565(red, green, blue);// 更新颜色框tft.fillRoundRect(PANEL_WIDTH + MARGIN*3 + 15, 75, COLOR_BOX_SIZE, COLOR_BOX_SIZE, 10, detectedColor);// 转换为十六进制格式char hexColor[7];sprintf(hexColor, "%02X%02X%02X", red, green, blue);// 更新HEX值显示tft.setFont(&FreeSans9pt7b);tft.fillRect(PANEL_WIDTH + MARGIN*3 + 40, 220, 80, 30, PANEL_COLOR); // 清除旧值tft.setCursor(PANEL_WIDTH + MARGIN*3 + 45, 235);tft.setTextColor(TEXT_COLOR);tft.print(hexColor);// 更新传感器数值显示tft.fillRect(MARGIN + 70, 70, 60, 30, PANEL_COLOR);   // 清除旧红色值tft.fillRect(MARGIN + 91, 110, 30, 30, PANEL_COLOR);  // 清除旧绿色值tft.fillRect(MARGIN + 90, 150, 30, 30, PANEL_COLOR);  // 清除旧蓝色值tft.setCursor(MARGIN + 90, 90);tft.print(red);tft.setCursor(MARGIN + 91, 130);tft.print(green);tft.setCursor(MARGIN + 90, 170);tft.print(blue);// 更新校准状态if (calibrated) {tft.fillRect(MARGIN , 190, 130, 30, PANEL_COLOR);tft.setCursor(MARGIN + 25, 210);tft.setTextColor(SUCCESS_COLOR);tft.print("Calibrated!");}
}void calibrate() {// 重置校准值cal_min_r = 10000;  cal_min_g = 10000;  cal_min_b = 10000;cal_max_r = 0;  cal_max_g = 0;  cal_max_b = 0;// 第一步:黑色校准showMessage("BLACK surface", HIGHLIGHT);Serial.println("Calibration Step 1: Place BLACK surface and press button");// 等待按钮按下while (digitalRead(button)) {if (Serial.available() && Serial.read() == 'x') {showMessage("Cal Canceled", WARNING_COLOR);return;}}// 读取黑色值(多次采样取最大)for (int i = 0; i < 5; i++) {color();cal_max_r = max(cal_max_r, red);cal_max_g = max(cal_max_g, green);cal_max_b = max(cal_max_b, blue);delay(100);Serial.print("Black Sample ");Serial.print(i+1);Serial.print(": R="); Serial.print(red);Serial.print(" G="); Serial.print(green);Serial.print(" B="); Serial.println(blue);}// 第二步:白色校准showMessage("WHITE surface", HIGHLIGHT);Serial.println("Calibration Step 2: Place WHITE surface and press button");// 等待按钮按下while (digitalRead(button)) {if (Serial.available() && Serial.read() == 'x') {showMessage("Cal Canceled", WARNING_COLOR);return;}}// 读取白色值(多次采样取最小)for (int i = 0; i < 5; i++) {color();cal_min_r = min(cal_min_r, red);cal_min_g = min(cal_min_g, green);cal_min_b = min(cal_min_b, blue);delay(100);Serial.print("White Sample ");Serial.print(i+1);Serial.print(": R="); Serial.print(red);Serial.print(" G="); Serial.print(green);Serial.print(" B="); Serial.println(blue);}calibrated = true;lastCalibration = millis();// 显示成功信息showMessage("Cal Success!", SUCCESS_COLOR);Serial.println("Calibration complete!");// 输出详细校准信息Serial.println("Calibration Values:");Serial.print("R min/max: "); Serial.print(cal_min_r); Serial.print("/"); Serial.println(cal_max_r);Serial.print("G min/max: "); Serial.print(cal_min_g); Serial.print("/"); Serial.println(cal_max_g);Serial.print("B min/max: "); Serial.print(cal_min_b); Serial.print("/"); Serial.println(cal_max_b);// 验证校准值if (cal_min_r >= cal_max_r || cal_min_g >= cal_max_g || cal_min_b >= cal_max_b) {showMessage("Invalid Calibration!", WARNING_COLOR);Serial.println("ERROR: Invalid calibration values! White min should be LESS than black max.");delay(3000);}// 3秒后恢复状态显示delay(3000);updateColorDisplay();
}void color() {// 读取红色分量digitalWrite(s2, LOW);digitalWrite(s3, LOW);red = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);// 读取蓝色分量digitalWrite(s3, HIGH);blue = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);// 读取绿色分量digitalWrite(s2, HIGH);green = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);
}void showMessage(const char* msg, uint16_t color) {tft.fillRect(MARGIN, 190, PANEL_WIDTH, 40, PANEL_COLOR);tft.setFont(&FreeSans9pt7b);tft.setTextColor(color);tft.setCursor(MARGIN + 2, 210);tft.print(msg);
}

三、操作过程及数据展示

3.1 操作步骤

        (1)确保Adafruit_GFX、Adafruit_ST7789库安装,将程序上传到零知IDE,驱动屏幕初始化,屏幕提示"Please Calibrate!"说明传感器需要进行校准操作。

        (2)打开串口监视器设置为9600波特率,发送 'c' 开始校准,按照提示放置黑色参考物和白色参考物并按下按钮校准,屏幕提示"Calibrated"的时候说明校准成功:

        (3)按下按钮识别当前颜色,查看显示屏上的颜色和HEX值,当前识别到的绿色物体HEX为"#005D1D":

3.2 校准过程演示

        按照串口打印的提示内容观察校准过程和校准数据:

3.3 识别效果视频展示

TCS230颜色识别器进行校准和颜色数据读取

分别放置黑色、白色参考物进行校准,接着进行颜色识别

四、TCS230技术原理

4.1 工作原理

        TCS230是可编程颜色光频率转换器,由光电二极管阵列和电流-频率转换器组成:

        >64个光电二极管(16个红,16个绿,16个蓝,16个白)
        >可配置的输出频率比例(S0、S1引脚)
        >可选择的滤波器(S2、S3引脚)

4.2 工作模式配置

        每16个光电二极管并联连接,因此使用两个控制引脚S2和S3,我们可以选择读取哪个。如果我们想要检测红色,我们可以通过根据表格将两个引脚设置为低逻辑电平来使用16个红色滤波光电二极管,根据以下配置可以选择对应的滤波器:

S2S3选择的滤波器
00红色
01蓝色
10无滤波器
11绿色

五、常见问题指引

Q1: 为什么校准后颜色识别不准确?

A: 可能原因:

        参考物不标准(使用专业黑/白参考卡)
        环境光变化(保持稳定光照)
        传感器距离不当(保持2-5cm距离)

Q2: 如何判断校准是否成功?

A: 检查串口输出的校准值:

        白色值(min)应小于黑色值(max)
        各通道值比例应接近
        观察黑色校准值是否合理

Q3: HEX值显示不正确怎么办?

A: 检查步骤:

        确认校准成功
        检查颜色映射范围(0-255)
        验证HEX转换函数
        确保显示屏初始化正确

Q4: 如何提高识别精度?

A: 优化建议:

        增加采样次数(修改samples值)
        使用遮光罩减少环境光影响
        固定传感器与物体的距离

六、结论

        项目成功实现了基于STM32F407VET6的高精度颜色识别系统,通过以下创新点解决了颜色识别中的关键问题:

        >独立通道校准算法:显著提高了颜色识别准确性
        >三步校准流程:简化操作并确保校准质量
        >实时数据显示:提供详细的校准和识别反馈
        >直观UI设计:展示颜色信息和传感器数据

项目资源:

        TCS230用户手册:TCS230数据手册

        显示屏库文件:ST7789驱动库

本项目已在零知增强板上全面测试通过,欢迎在评论区分享您的实现经验和改进建议!点击了解更多零知开发教程:
https://www.lingzhilab.com/freesources.html

http://www.dtcms.com/a/334062.html

相关文章:

  • 开源数据发现平台:Amundsen Frontend Service 推荐实践
  • Camx-Tuning参数加载流程分析
  • 【时时三省】(C语言基础)共用体类型数据的特点
  • 她的热情为何突然冷却?—— 解析 Kafka 吞吐量下降之谜
  • 智能合约:区块链时代的“数字契约革命”
  • 外出业务员手机自动添加报价单​——仙盟创梦IDE
  • 多商户商城系统源码选型指南:开源框架 vs 定制开发的优劣对比
  • Android RxJava 组合操作符实战:优雅处理多数据源
  • 12分区南排烟机,多线模块没电
  • Linux上管理Java的JDK版本
  • LeetCode 刷题【43. 字符串相乘】
  • 34 HTB Cat 机器 - 中等难度
  • 完整设计 之 智能合约系统:主题约定、代理协议和智能合约 (临时命名)----PromptPilot (助手)答问之2
  • Three.js三大组件:场景(Scene)、相机(Camera)、渲染器(Renderer)
  • 线性代数之两个宇宙文明关于距离的对话
  • 图像相似度算法汇总及Python实现
  • 01数据结构-关键路径
  • Unity 游戏提升 Android TargetVersion 相关记录
  • Docker小游戏 | 使用Docker部署人生重开模拟器
  • MySQL的三大范式:
  • 机器学习--决策树
  • Rust 语法基础教程
  • sqli-labs通关笔记-第52关 GET数值型order by堆叠注入(手工注入+脚本注入两种方法)
  • Ubuntu 25.04 安装并使用 MySQL 8.4.5 的步骤
  • 使用 npm-run-all2 简化你的 npm 脚本工作流
  • Linux中的restore
  • PHP域名授权系统网站源码/授权管理工单系统/精美UI/附教程
  • 集成电路学习:什么是Haar Cascade Classifier Haar级联分类器
  • Vue 3.5+ Teleport defer 属性详解:解决组件渲染顺序问题的终极方案
  • JavaScript 实用工具方法小全