基于STM32F103的TCS3200颜色传感器的使用

基于STM32F103的TCS3200颜色传感器的使用

        团队布置的任务内容是2019年中国机器人大赛的题目,小车运行过程中需要对不同物料进行颜色上的识别。因此我最近就学了TCS3200颜色传感器的基本原理和代码的具体实现。网上对该模块的使用基本是基于Arduino的,对于基于STM32的介绍较少。鉴于此,补一篇代码可以跑STM32的笔记是有必要的(实际上为了凑一篇笔记赶紧交作业

一、TCS3200芯片介绍

        TCS3200是TAOS公司推出的可编程彩色光到频率的转换器,它把可配置的硅光电二极管与电流频率转换器集成在一个单一的CMOS电路上,同时集成了三种颜色(RGB)的滤光器,是业界第一个具有数字兼容接口的RGB彩色传感器。

        工作时,通过两个可编程的引脚来动态选择所需要的滤光器,该传感器的典型输出频率范围从2Hz~500kHz,用户还可以通过两个可编程引脚来选择100%、20%、2%的输出比例因子,或是电源关断模式。选择不同的输出比例因子,可以对输出频率范围进行调整,适应不同的测量范围。

        TCS3200适合于色度计测量应用领域。比如彩色打印、医疗诊断、计算机彩色监视器校准以及油漆、纺织品、化妆品和印刷材料的过程控制。
下图是其电路原理图(不看也行)

在这里插入图片描述

二、颜色识别原理

(一)三原色原理

         通常所看到的物体颜色,实际上是物体表面吸收了照射到它上面的白光中的一部分有色成分,而反射出的另一部分有色光在人眼中的反应。白色是由各种频率的可见光混合在一起构成的,也就是说白光中包含着各种颜色的色光(如红R、黄Y、绿G、青V、蓝B、紫P)。根据德国物理学家赫姆霍兹的三原色理论可知,各种颜色是由不同比例的三原色(红、绿、蓝)混合而成的。每一种颜色都一个标准值。由上面的三原色感应原理可知,特定颜色的值就是红绿蓝三种颜色标准值的线性组合。

(二)TCS3200的识别原理

        对于TCS3200来说,当选定一个颜色滤波器时,它只允许某种特定的原色通过,阻止其它原色的通过。例如:当选择红色滤波器时,入射光中只有红色可以通过,蓝色和绿色都被阻止,这样就可以得到红色光的光强;同理,选择其它的滤波器,就可以得到蓝色光和绿色光的光强。通过这三个光强值,就可以分析出反射到TCS3200D传感器上的光的颜色。TCS3200传感器有红绿蓝和清除4种滤光器,可以通过其引脚 S2S3 的高低电平来选择滤波器模式,如下图。

L表示低电平,H表示高电平

        当被测物体反射光的红、绿、蓝三色光线分别透过相应滤波器到达TAOS TCS3200RGB感应芯片时,其内置的振荡器会输出方波,方波频率与所感应的光强成比例关系,光线越强,内置的振荡器方波频率越高。TCS3200传感器有一个OUT引脚,它输出信号的频率与内置振荡器的频率也成比例关系,它们的比率因子可以靠其引脚S0和S1的高低电平来选择,如下图。

在这里插入图片描述

        现在我们对光强值做了处理,令其转换成了波,又在它的基础上乘上了一个因子来转换成OUT引脚输出给主控板的信号频率。现在就差要得到对应颜色的RGB标准值,这需要我们就需要做 白平衡校正

(三)白平衡校准

        白平衡就是告诉传感器什么是“白色”的。以白色为参照颜色,才能计算其他颜色的RGB标准值。从理论上讲,白色是由等量的红色、蓝色、绿色混合而成的。

在这里插入图片描述

Window画图功能中编辑颜色界面

而然实际上白色中的RGB并不是完全相等,并且TCS3200光传感器对三种颜色的敏感性并不相同,导致RGB输出并不相同。因此必须要做白平衡校正,使得TCS3200检测的`“白色”``输出的RGB值一样。

        白平衡的方法:把一个白色物体放置在TCS3200颜色传感器之下,两者相距10mm左右,点亮传感器上的4个白光LED灯,然后选通三原色的滤波器,让被测物体反射光中红、绿、蓝三色光分别通过滤波器,得到OUT引脚输出信号的脉冲数。在用白色的RGB标准值分别处以对应颜色就得到了三种颜色的比例因子。有了白平衡校正得到的RGB比例因子,就可以讲测得其他颜色的信号脉冲换算成RGB标准值了。

S = 255 N 0 (1) S={255\over N_0}\tag{1} S=N0255(1)
E = S ∗ N 0 (1) E=S*N_0\tag{1} E=SN0(1)

N0是白平衡是测得的脉冲数,S是RGB比例因子,E是当前检测颜色的RGB值。

三、颜色识别的思路、方法以及代码实现

(一)方法及思路

        根据上文的介绍,对颜色信号的采集基本上是以下步骤

在这里插入图片描述

我们的检测思路是:开一个定时器,间隔1s进入一次中断服务,在中断服务函数中采集信号并进行计算处理。1s检测RGB中的一种颜色。另外再开一个定时器,用来统计OUT引脚输出的脉冲数。第一次的检测做白平衡检测,用255除以脉冲数得到RGB比例因子,就可以用于之后的识别。所以白平衡进行一次,之后的检测都写在while函数里即可。开启串口通信,用printf函数把RGB值每隔4s打印一次,检查TCS3200传感器的识别结果。

(一)代码实现

我使用STM32CubeMax进行STM32开发,开发方式使用HAL库。用的板子型号是正点原子MINI板,芯片型号为STM32F103RCT6。

巨量的初始化函数自动生成,函数直接集成不同功能,谁用谁爽~~

这是我使用的GPIO口:

GPIO输出模式

PA4 —————— S0

PA5 —————— S1

PA6 —————— S2

PA7 —————— S3

PC4 —————— LED

定时器2外部时钟模式
PA0 —————— OUT

然后开了串口1 定时器1

在这里插入图片描述

定时器1采用内部时钟模式,经过倍频后APB2总线上的频率为72kHz,设置预分频系数和计数值分别为(7200-1)和(10000-1),这样定时间隔即为1s

在这里插入图片描述

定时器2采用外部时钟模式,统计OUT引脚输出的脉冲个数,计数方式为向上计数,65535溢出
在这里插入图片描述

打开串口1,采用异步传输方式,波特率为115200,8位字节的数据长度。没有奇偶校验位,数据发送停止位有1位。

在这里插入图片描述

下图是对时钟树的配置,在这里不多说

在这里插入图片描述

下面上代码:

我把有用的代码都写在了主函数文件里面,那些初始化函数啥的就不放出来,直接看有用的部分吧。另外声明,这些代码参考了http://www.eefocus.com/zhang700309/blog/13-08/296390_6c438.html,我改成了能在STM32板子上跑的代码。

  

#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"    //使用printf函数需要调用这个库

#define S0_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);  

#define S0_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);

#define S1_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);  

#define S1_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

#define S2_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);  

#define S2_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);

#define S3_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);  

#define S3_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);

#define LED_OFF HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);  

#define LED_ON HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET);

float RGB_Scale[3];          //数组存储3个RGB比例因子
int count=0;               //统计脉冲数
int cnt[3];              //数组存储RGB三种颜色的脉冲值
int flag=0;               //滤波器选择模式标志

void SystemClock_Config(void);

void filter(int s2,int s3)  //滤波器模式选择函数,根据S2和S3的电位来选择红、绿、蓝、三种颜色的滤波器
{
    if(s2==0&&s3==0){
			S2_L;S3_L;
		}
    if(s2==0&&s3==1){
		  S2_L;S3_H;
		}
    if(s2==1&&s3==0){
			S2_H;S3_L;
		}
    if(s2==1&&s3==1){
		  S2_H;S3_H;
		}			
}



void TSC_WB(int s2, int s3)  //结束上一种颜色识别,开始下一种颜色的识别 
{
  count = 0;   //统计脉冲值清零
  flag ++;     //输出信号计数标志+1,进行下一个颜色的脉冲统计
  filter(s2, s3); //选择滤波器模式

}

int fputc(int c, FILE *stream)    //重定义printf函数,在川口打印数据
{

    HAL_UART_Transmit(&huart1, (unsigned char *)&c, 1, 1000);   
    return 1;
}


int main(void)
{
  HAL_Init();        //初始化函数
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_USART1_UART_Init();  
  MX_TIM2_Init();
   
  HAL_TIM_Base_Start_IT(&htim1); //使能TIM1
  HAL_TIM_Base_Start_IT(&htim2);  //使能TIM2

  S0_L;
  S1_H;    //选择2%的输出比例因子
  LED_ON;   //打开白光LED进行白平衡
  HAL_Delay(4000);  //延时四秒等待识别
  //通过白平衡测试,计算得到白色物体RGB值255与1s内三色光脉冲数的RGB比例因子
  RGB_Scale[0] = 255.0/ cnt[0];     //红色光比例因子
  RGB_Scale[1] = 255.0/ cnt[1] ;    //绿色光比例因子
  RGB_Scale[2] = 255.0/ cnt[2] ;    //蓝色光比例因子
  //打印白平衡后的红、绿、蓝三色的RGB比例因子
  printf("%5lf\r\n",RGB_Scale[0]);
  printf("%5lf\r\n",RGB_Scale[1]);
  printf("%5lf\r\n",RGB_Scale[2]);
  //红、绿、蓝三色光分别对应的1s内TCS3200输出脉冲数乘以相应的比例因子就是RGB标准值
  //打印被测物体的RGB值
  for(int i=0; i<3; i++)
    printf("%d\r\n",(int)(cnt[i]*RGB_Scale[i])); 
  printf("白平衡调试结束\r\n");
   //白平衡结束后,就可以检测其他的颜色了,为了让它不断检测,我们让他循环起来
  while (1)
  {
    flag=0;  //标志位清0 开始下一轮的识别
	count=0;   //脉冲数置为0,开始统计下一个脉冲数
	HAL_Delay(4000);  //延时4s 等待识别结果
	for(int i=0; i<3; i++)
      printf("%d\r\n",(int)(cnt[i] * RGB_Scale[i])); 

}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

//定时器1的中断服务函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
	if(htim->Instance==htim1.Instance){
		 count=__HAL_TIM_GET_COUNTER(&htim2);//将TIM2统计的脉冲数存入变量count
		 switch(flag){
         case 0:
            printf("->WB Start\r\n");
            TSC_WB(0, 0);   //选择让红色光线通过滤波器的模式
            break;
         case 1:
            printf("->Frequency R=");
            printf("%d\r\n",count);   //打印1s内的红光通过滤波器时,TCS3200输出的脉冲个数
            cnt[0] = count;    //存储1s内的红光通过滤波器时,TCS3200输出的脉冲个数
            TSC_WB(1, 1);  //选择让绿色光线通过滤波器的模式
            break;
         case 2:
            printf("->Frequency G=");
            printf("%d\r\n",count);   //打印1s内的绿光通过滤波器时,TCS3200输出的脉冲个数
            cnt[1] = count;    //存储1s内的绿光通过滤波器时,TCS3200输出的脉冲个数
            TSC_WB(0, 1);  //选择让蓝色光线通过滤波器的模式
            break;
 
         case 3:
            printf("->Frequency B=");
            printf("%d\r\n",count);   //打印1s内的蓝光通过滤波器时,TCS3200输出的脉冲个数
            printf("->WB End\r\n");
            cnt[2] = count;     //存储1s内的蓝光通过滤波器时,TCS3200输出的脉冲个数
            TSC_WB(1, 0);   //选择无滤波器的模式  
            break;
         default:
            count = 0;     //计数值清零
             break;
      }
   }
}

void Error_Handler(void)
{

}

void assert_failed(uint8_t *file, uint32_t line)
{
  
}
#endif 

下面是实物接线,emmm,不太会拍,具体怎么接线参考其他文章吧。

在这里插入图片描述

现在我们来测试一下,打开串口助手。

在这里插入图片描述

可以看到我们接收到了来自传感器的信号,然后根据传来的数值做进一步处理。比如在这个比赛里,可以利用传来数据控制舵机呀,电机这些的下一步动作。不过这个测试还是有一些问题的,白平衡阶段和识别颜色阶段获得的脉冲数有异常的变动。我认为可能是硬件方面的问题,需要做进一步排查。欢迎大家积极讨论,发现问题。





分割线一下

发文两天后,经过一个大佬的指点,发现了我代码中的错误。为什么做完白平衡之后,我统计的脉冲数会暴增呢?原因就在于上面代码块的第144行中的_HAL_TIM_GET_COUNTER();我进了他函数内部看了一下,发现这根本不是一个函数,而是一个宏定义。

在这里插入图片描述
        所以这个语句他只会读取当前TIM2统计的脉冲数,而并不会每次读完之后都清除掉统计脉冲值,所以TIM2上的脉冲数一直是在累加的,这就是为什么我之前做的时候脉冲数异常暴增。解决方法很简单,就是在每次读完脉冲数之后手动清除CNT寄存器中的值TIM2->CNT=0;这样就可以啦。另外就是白平衡时和检测颜色时要保证外部的光照条件基本一样,所以全程要点亮白光LED,消除外界光源影响。
        我们再来做一遍测试。我测的红色。看一下传感器返回给上位机的结果。
在这里插入图片描述
在这里插入图片描述
返回的RGB标准值分别是159 66 56,我们在Windows画图里检验一下结果的准确性。
在这里插入图片描述
哈哈哈哈哈哈,我只能说识别结果还行。
        那么到这里,对这款传感器的讲解就到这里啦。

  • 114
    点赞
  • 514
    收藏
    觉得还不错? 一键收藏
  • 102
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 102
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值