TCS3200是TAOS公司推出的可编程彩色光到频率的转换器,它把可配置的硅光电二极管与电流频率转换器集成在一个单一的CMOS电路上,同时集成了三种颜色(RGB)的滤光器;TCS3200能检测物体反射的光强,并生成不同频率的方波信号(50%占空比)
TCS3200有一个8*8的光电二极管矩阵,其中分别有三组16个光电二极管有RGB三种滤波器,一组16个光点二极管无滤波器,共64个光电二极管
具有不同滤波器的光电二极管分别检测RGB的值
S2和S3用于配置光电二极管矩阵(选择上面四组光电二极管中的一组),S0和S1用于配置电流频率转换器(可以控制输出信号的频率范围)
S2、S3
用于选择滤波器 对应有 RGB
和 无滤波器
S0、S1
用于选择输出波形的频率范围(可以理解为限制输出信号频率的最大值)
下面对TCS3200的输出信号频率作一个简单讲解
根据网上资料: 该传感器的典型输出频率范围从2Hz-500KHz
(但官方资料好像典型值应该到600KHz, 不重要)
当S0=H, S1=H
对应输出状态为100%
输出典型值为600KHz
当S0=H, S1=L
对应输出状态为20%
输出典型值为600*0.2 = 120KHz
当S0=L, S1=H
对应输出状态为2%
输出典型值为 600*0.02=12KHz
这是TCS3200运行特征表的一部分,可以看到在100%输出的模式下
, 不同颜色光照对应的输出信号频率范围
白平衡就是告诉TCS3200什么是白色的,以此为参照才能计算其他颜色的RGB。理论上,白色是由等量的RGB混合而成(255、255、255)但实际中,由于光照条件和人的感知能力有限等原因,我们看到的白色很有可能不是由均匀的RGB组成,并且TCS3200对三种颜色的敏感性也不相同(参考TCS3200运行特征表),因此进行白平衡给TCS3200一个标准(这个标准有时候不一定准确,会导致识别的结果出现一定的误差)
白平衡的方法: 在装置启动前,先将一个白色物体(不是白色就校验不准)置于传感器处,等待传感器检测,MCU计算出比例因子
题外话: 校验不准容易导致后续的检测中出现异常值,RGB的值超出255,这样在现有的RGB体系下我们无法找到对应的颜色;校验的越准,越不容易出现异常值
参考TCS230,大致有两种思路
这里采用第二种思路
设置一个定时器每隔1s进入一次中断服务,期间对TCS3200输出的信号进行采集和处理;设置另外一个定时器,捕获TCS3200的输出信号**(外部时钟源模式2,上升沿使计数器加一);第一次的检测用于白平衡,后续循环检测RGB**,每完成一组将RGB的值显示在OLED屏幕上
回顾一下这个图,要想用外部时钟源模式2,信号的流向是这样子的。
可简单分为三个部分:
外部时钟源模式2
参数配置 (极性选择,边沿检测,预分频器,滤波器)接下来根据信号的流向,讲解整个流程的配置
首先从参考手册的描述中可知,设定该模式主要操作的是SMCR寄存器
也就是所谓的从模式寄存器
第二句话说明它的功能,即我们所需的功能
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0);
从模式控制寄存器(TIMx_SMCR)
可以看到其实有八种模式可以配置,然后可以发现外部时钟模式1
,刚好对应了我们框图中的信号流向,因此我们需要将从模式配置为外部时钟模式1
,从模式的外部时钟模式1
和时钟选择的外部时钟源模式1
不同
下面介绍需要使用和了解的库函数
TIM_ITRxExternalClockConfig
/**
* @brief Configures the TIMx Internal Trigger as External Clock
* @param TIMx: where x can be 1, 2, 3, 4, 5, 9, 12 or 15 to select the TIM peripheral.
* @param TIM_ITRSource: Trigger source.
* This parameter can be one of the following values:
* @param TIM_TS_ITR0: Internal Trigger 0
* @param TIM_TS_ITR1: Internal Trigger 1
* @param TIM_TS_ITR2: Internal Trigger 2
* @param TIM_TS_ITR3: Internal Trigger 3
* @retval None
*/
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)
{
/* Check the parameters */
assert_param(IS_TIM_LIST6_PERIPH(TIMx));
assert_param(IS_TIM_INTERNAL_TRIGGER_SELECTION(TIM_InputTriggerSource));
/* Select the Internal Trigger */
TIM_SelectInputTrigger(TIMx, TIM_InputTriggerSource); // 选择输入触发的通道
/* Select the External clock mode1 */
TIMx->SMCR |= TIM_SlaveMode_External1; // 内部使用了从模式的外部模式1
}
可以看到该函数无论是在库函数编程手册,还是在ST库的注释中,都只有4个参数可选择(但实际不是, 接着往下看)
可以看到该函数内部调用了函数TIM_SelectInputTrigger
函数TIM_SelectInputTrigger
/**
* @brief Selects the Input Trigger source
* @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9, 12 or 15 to select the TIM peripheral.
* @param TIM_InputTriggerSource: The Input Trigger source.
* This parameter can be one of the following values:
* @arg TIM_TS_ITR0: Internal Trigger 0
* @arg TIM_TS_ITR1: Internal Trigger 1
* @arg TIM_TS_ITR2: Internal Trigger 2
* @arg TIM_TS_ITR3: Internal Trigger 3
* @arg TIM_TS_TI1F_ED: TI1 Edge Detector
* @arg TIM_TS_TI1FP1: Filtered Timer Input 1
* @arg TIM_TS_TI2FP2: Filtered Timer Input 2
* @arg TIM_TS_ETRF: External Trigger input // 我们要选的参数
* @retval None
*/
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)
{
uint16_t tmpsmcr = 0;
/* Check the parameters */
assert_param(IS_TIM_LIST6_PERIPH(TIMx));
assert_param(IS_TIM_TRIGGER_SELECTION(TIM_InputTriggerSource));
/* Get the TIMx SMCR register value */
tmpsmcr = TIMx->SMCR;
/* Reset the TS Bits */
tmpsmcr &= (uint16_t)(~((uint16_t)TIM_SMCR_TS));
/* Set the Input Trigger source */
tmpsmcr |= TIM_InputTriggerSource;
/* Write to TIMx SMCR */
TIMx->SMCR = tmpsmcr;
}
实际可调用参数有8个
配置代码
TIM_ITRxExternalClockConfig(TIM2, TIM_TS_ETRF);
至此,框图信号流动路线全部配置完毕,外部时钟源模式2即可使用
#ifndef __TCS_H
#define __TCS_H
#include "stm32f10x.h"
/**
* @breif TCS3200端口定义
* S0: PA4
* S1: PA5
* S2: PA6
* S3: PA7
* LED: PB0
*/
#define S0_L GPIO_ResetBits(GPIOA, GPIO_Pin_4);
#define S0_H GPIO_SetBits(GPIOA, GPIO_Pin_4);
#define S1_L GPIO_ResetBits(GPIOA, GPIO_Pin_5);
#define S1_H GPIO_SetBits(GPIOA, GPIO_Pin_5);
#define S2_L GPIO_ResetBits(GPIOA, GPIO_Pin_6);
#define S2_H GPIO_SetBits(GPIOA, GPIO_Pin_6);
#define S3_L GPIO_ResetBits(GPIOA, GPIO_Pin_7);
#define S3_H GPIO_SetBits(GPIOA, GPIO_Pin_7);
#define LED_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_0);
#define LED_ON GPIO_SetBits(GPIOB, GPIO_Pin_0);
extern float RGB_Scale[3]; // R、G、B 比例因子
extern uint16_t count; // 脉冲计数
extern uint16_t cnt[3]; // RGB 三种颜色的脉冲数
// 频率选择
typedef enum
{
TCS_2Percentage = 0x01,
TCS_20Percentage = 0x02,
TCS_100Percentage = 0x03,
TCS_Frequent_OFF = 0x04
}TCS_FrequentTypeDef;
// 滤波器
typedef enum
{
TCS_Filter_Red = 0x01,
TCS_Filter_Blue = 0x02,
TCS_Filter_Green = 0x03,
TCS_Filter_None = 0x04
} TCS_FilterTypeDef;
void TCS_Init(void);
void TCS_FrequentKeyer(TCS_FrequentTypeDef TCS_Frequent);
#endif
#include "stm32f10x.h"
#include "MyNVIC.h"
#include "TCS.h"
float RGB_Scale[3]; // R、G、B 比例因子
uint16_t count; // 脉冲计数
uint16_t cnt[3]; // RGB 三种颜色的脉冲数
static TCS_FilterTypeDef flag = TCS_Filter_None; // 滤波器选择模式标志
/**
* @brief 颜色识别端口初始化 PA 4 5 6 7; PB 0
*/
void TCS_PortInit(void)
{
// RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
// GPIO S0-S3(PA4-7) 用来控制TCS3200工作模式 配置为通用推挽输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// LED(PB0) 用来控制TCS3200的LED的亮灭 配置为通用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief TIM1 初始化 用于计时 定时时间为1s
*/
void TCS_TIM1_Init(void)
{
// RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
// TimeBase
TIM_TimeBaseInitTypeDef TimeBase_InitStructure;
TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TimeBase_InitStructure.TIM_Period = 10000-1; // ARR
TimeBase_InitStructure.TIM_Prescaler = 7200-1; // PSC
TimeBase_InitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TimeBase_InitStructure);
// NVIC
MyNVIC_TIM1_Config();
// 开启中断
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
/**
* @brief TIM2 初始化 外部时钟模式 用于捕获TCS3200 OUT口的信号 PA0信号输入口 TIM_ETR
*/
void TCS_TIM2_Init(void)
{
// RCC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 拉倒低电平
// TimeBase
TIM_TimeBaseInitTypeDef TimeBase_InitStructure;
TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TimeBase_InitStructure.TIM_Period = 0xFFFF; // ARR 65535
TimeBase_InitStructure.TIM_Prescaler = 0; // PSC
TimeBase_InitStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2, &TimeBase_InitStructure);
// Select the clock source 选择时钟源
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0);
TIM_ITRxExternalClockConfig(TIM2, TIM_TS_ETRF);
TIM_SetCounter(TIM2, 0); // 将CNT置零
TIM_Cmd(TIM2, ENABLE); // 开启定时器
}
/**
* @brief 频率调制
*
* @param TCS_FrequentTypeDef: TCS_2Percentage TCS_20Percentage TCS_100Percentage TCS_Frequent_OFF
*/
void TCS_FrequentKeyer(TCS_FrequentTypeDef TCS_Frequent)
{
switch(TCS_Frequent)
{
case TCS_2Percentage:
S0_L;
S1_H;
break;
case TCS_20Percentage:
S0_L;
S1_H;
break;
case TCS_100Percentage:
S0_H;
S1_H;
break;
case TCS_Frequent_OFF:
S0_L;
S1_L;
break;
default:
break;
}
}
/**
* @brief 选择滤波器
* @param TCS_Filter: TCS_Filter_Red TCS_Filter_Blue TCS_Filter_Green TCS_Filter_None
*/
void TCS_SelectFilter(TCS_FilterTypeDef TCS_Filter)
{
flag = TCS_Filter; // 更新标志位
switch(TCS_Filter)
{
case TCS_Filter_Red:
S2_L;
S3_L;
break;
case TCS_Filter_Blue:
S2_L;
S3_H;
break;
case TCS_Filter_Green:
S2_H;
S3_H;
break;
case TCS_Filter_None:
S2_H;
S3_L;
break;
default:
break;
}
}
/**
* @brief TCS3200初始化 初始化端口 和两个用到的定时器
*/
void TCS_Init(void)
{
TCS_PortInit(); // 控制端口
TCS_TIM1_Init(); // 定时计时器
TCS_TIM2_Init(); // 捕获脉冲数 包括输入端口初始化
TCS_FrequentKeyer(TCS_Frequent_OFF); // 频率调制器断电
TCS_SelectFilter(TCS_Filter_None); // 关闭TCS滤波器
LED_OFF; // 关闭LED
}
/**
* @brief TIM1更新中断
*/
void TIM1_UP_IRQHandler(void)
{
if(TIM_GetITStatus(TIM1, TIM_IT_Update))
{
count = TIM_GetCounter(TIM2); // 读取脉冲数
// 依次读取 RGB三种颜色的值
switch(flag)
{
case TCS_Filter_None:
count = 0;
TCS_SelectFilter(TCS_Filter_Red); // 选择红色滤波器
break;
case TCS_Filter_Red:
cnt[0] = count;
TCS_SelectFilter(TCS_Filter_Green); // 选择绿色滤波器
break;
case TCS_Filter_Green:
cnt[1] = count;
TCS_SelectFilter(TCS_Filter_Blue); // 选择蓝色滤波器
break;
case TCS_Filter_Blue:
cnt[2] = count;
TCS_SelectFilter(TCS_Filter_None); // 关闭滤波器
break;
default:
break;
}
// 将TIM2的计数器清零
TIM_SetCounter(TIM2, 0);
// 清除标志位
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
}
}
#ifndef __MyNVIC_H
#define __MyNVIC_H
void MyNVIC_TIM1_Config(void);
#endif
#include "stm32f10x.h"
/**
* @brief TIM1 中断向量嵌套配置
*/
void MyNVIC_TIM1_Config(void)
{
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
}
#include "stm32f10x.h"
#include "delay.h" // 江科大对应的驱动
#include "OLED.h" // 江科大对应的驱动
#include "TCS.h"
int main(void)
{
OLED_Init();
TCS_Init(); // 初始化TCS3200
TCS_FrequentKeyer(TCS_2Percentage); // 选择输出频率
// 这里选2% 因为计数器最多到65535
// 若选20%最高输出120kHz 120000 当检测时长为1s时会超出计数器的范围
// 但输出频率越高,结果越精确
// 感兴趣的可以尝试两个定时器级联 扩大计数范围
LED_ON; // LED 开
// 第一次获得的参数 进行白平衡
Delay_ms(8000); // 延时8s,两轮检测确保白平衡的进行
RGB_Scale[0] = 255.0/cnt[0]; // Get Red Scale
RGB_Scale[1] = 255.0/cnt[1]; // Get Green Scale
RGB_Scale[2] = 255.0/cnt[2]; // Get Blue Scale
while(1)
{
Delay_ms(4000);
OLED_ShowString(1, 1, "R:");
OLED_ShowNum(1, 3, cnt[0]*RGB_Scale[0], 3);
OLED_ShowString(2, 1, "G:");
OLED_ShowNum(2, 3, cnt[1]*RGB_Scale[1], 3);
OLED_ShowString(3, 1, "B:");
OLED_ShowNum(3, 3, cnt[2]*RGB_Scale[2], 3);
}
}
【TCS3200颜色识别模块】
基于STM32F103的TCS3200颜色传感器的使用
肝了一下午,学会TCS3200颜色识别——基于STM32,分享一波
高分辨颜色传感器TCS230的原理与应用
TCS3200.pdf (DataSheet)
STM32F10xxx参考手册(中文).pdf