2024年寒假练 - 基于带屏12指神探的传感器扩展板实现的颜色识别功能
该项目使用了带屏12指神探的传感器扩展板,实现了颜色识别的设计,它的主要功能为:实现至少5种颜色的识别。
标签
颜色识别
2024年寒假练
带屏12指神探的传感器扩展板
冷月烟
更新2024-03-29
185

项目介绍

项目功能介绍

本项目实现了通过扩展板板上集成的颜色传感器,实现颜色识别功能,具有以下特点

  • 可识别至少五种颜色
  • 可在LCD上显示基本信息如当前识别颜色
  • 可在LCD上用色块显示识别的颜色并大致接近


设计思路

该项目主要有两部分组成:

  • LCD驱动部分:用于驱动LCD显示
  • RGB颜色采集部分:用于驱动LTR-381RGB颜色传感器


由于颜色传感器内部各个传感器对光线感知不同,同样的强度不同颜色的光线可能读取的数据不一样,所以颜色传感器使用是一般使用环境白色为基准进行校正,然后对之后的数据进行归一化处理,不过有句话叫力大砖飞,只要你光线够强,数据还是能超过某一个阈值的。

校正的第二个原因是因为每次传感器处在的环境不一样,白天肯定亮度高,数值大,夜晚亮度就低,数据小,为了省下这个归一化,就在一个较为昏暗的环境进行测试,这样就省下校正部分了。


开发环境

RP2040支持多种环境进行开发,它作为一个M0的双核芯片,第一时间想到的就是用ARM的亲儿子keil开发设计,可是树莓派官方并没有提供这一种方式,而是把类似linux的一套搬到RP2040上面了,GCC固然强大,可是使用起来还是比较折腾的,MicroPython固然简单,可是与主流的嵌入式开发相差不小,封装起来的代码使用起来很畅快,但是自己要写一个驱动就麻烦的很。不过早就有大佬实现了这个想法,在keil上搭建了RP2040的开发环境。

这里我是使用了傻孩子大佬的一个开源项目:https://github.com/GorgonMeducer/Pico_Template 。使用这个项目就能轻松的使用keil进行RP2040的开发了。部署非常简单,keil只要额外安装GorgonMeducer.perf_counter.1.9.4.pack这个包(这个包在项目里面也有提供,是一个测量运行时间的组件),就能直接打开工程使用了。

上面的项目再搭配上另一个开源项目:https://github.com/majbthrd/pico-debug/releases (将RP2040的一个核实现为DAP功能),打开后下载 pico-debug-gimmecache.uf2文件,将其烧录到RP2040里,就能利用上RP2040的一个核作为DAP去调试另一个核,让RP2040的开发方式更加接近于传统的单片机的开发方式,而且实现只需要一根USB线就完成下载与调试的功能,再也不用折腾来折腾去了。


硬件框图


软件流程图


硬件介绍

带屏版的12指神探

在原板基础上,配备了一块240*240分辨率的LCD彩屏以及两个可程控按键和一个拨轮,丰富了人机交互功能,方便信息观察、界面切换等使用方式。此外还配备了白色外壳,精心设计的包装不仅使板卡日常使用时更加美观也便于板卡的站立以及使用安全。



这个模块是通过Type C的USB接口提供供电、下载以及通信的功能,板上有5V转3.3V,最高支持800mA的电压变换器,在12根引脚上也将5V和3.3V引出,方便对其它外设板供电。


主控芯片:采用树莓派Pico核心芯片RP2040

  • 双Arm Cortex M0+内核,可以运行到133MHz
  • 264KBSRAM,板卡上外扩2MBFlash
  • 性能强大、高度灵活的可编程IO(PIO)可用于高速数字接口
  • 拥有2个UART、2个SPI、2个I2C、16个PWM通道以及4路12位精度ADC
  • 支持MicroPython、C、C++编程
  • 拖拽UF2固件至U盘的方式烧录固件,方便快捷


12指神探的传感器扩展板

接口完全适配,正确方向插入后即可使用。扩展板搭载了几款常见传感器和功能模块,包括为初学者准备的麦克风、蜂鸣器、、红外收发、霍尔效应开关、加热电阻,为进阶操作准备的温湿度传感器、六轴传感器、接近/环境光/IR传感器、颜色传感器。其中温湿度传感器、六轴传感器、接近传感器、颜色传感器可拆卸为单个模块,通过杜邦线等连接线延伸其使用的空间范围。出厂默认传感器正面朝上使用。若需背面朝上使用,则自行焊接排母后按指示方向插入。

功能:

  • 通过各个GPIO引脚直接获取各个传感器的数字或模拟量
  • IO:红外收发模块、霍尔效应模块
  • ADC:麦克风模块
  • 通过编写协议使主控芯片与各个传感器从机进行数据交流,学习协议使用规则的之后即可随心所欲操控
  • IIC协议:温湿度传感器、六轴传感器、接近传感器、颜色传感器
  • 蜂鸣器:可通过IO控制发出声音,丰富人机交互
  • 加热电阻:通过IO控制可升高电阻温度,通过配合温湿度传感器并辅以PID等自控算法可实现恒温恒湿效果


实现的功能及图片展示

数据显示


识别红色


识别绿色


识别蓝色


识别黄色


识别紫色


识别青色


识别白色


主要代码片段及说明

LCD初始化

void LCD_GPIO_Init(void)
{
gpio_init(0);
gpio_set_dir(0, GPIO_OUT);
gpio_init(1);
gpio_set_dir(1, GPIO_OUT);
gpio_init(2);
gpio_set_dir(2, GPIO_OUT);
gpio_init(3);
gpio_set_dir(3, GPIO_OUT);
gpio_init(4);
gpio_set_dir(4, GPIO_OUT);

gpio_put(0, 1);
gpio_put(1, 1);
gpio_put(2, 1);
gpio_put(3, 1);
gpio_put(4, 1);
}
/******************************************************************************
函数说明:LCD串行数据写入函数
入口数据:dat 要写入的串行数据
返回值: 无
******************************************************************************/
void LCD_Writ_Bus(uint8_t dat)
{
// spi_write_blocking(spi0, &dat, 1);
uint8_t i;
LCD_CS_Clr();
for(i=0;i<8;i++)
{
LCD_SCLK_Clr();
if(dat&0x80)
{
LCD_MOSI_Set();
}
else
{
LCD_MOSI_Clr();
}
LCD_SCLK_Set();
dat<<=1;
}
LCD_CS_Set();
}

/******************************************************************************
函数说明:LCD写入数据
入口数据:dat 写入的数据
返回值: 无
******************************************************************************/
void LCD_WR_DATA8(uint8_t dat)
{
LCD_Writ_Bus(dat);
}

/******************************************************************************
函数说明:LCD写入数据
入口数据:dat 写入的数据
返回值: 无
******************************************************************************/
void LCD_WR_DATA(uint16_t dat)
{
LCD_Writ_Bus(dat>>8);
LCD_Writ_Bus(dat);
}

/******************************************************************************
函数说明:LCD写入命令
入口数据:dat 写入的命令
返回值: 无
******************************************************************************/
void LCD_WR_REG(uint8_t dat)
{
LCD_DC_Clr();//写命令
LCD_Writ_Bus(dat);
LCD_DC_Set();//写数据
}

/******************************************************************************
函数说明:设置起始和结束地址
入口数据:x1,x2 设置列的起始和结束地址
y1,y2 设置行的起始和结束地址
返回值: 无
******************************************************************************/
void LCD_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
if(USE_HORIZONTAL==0)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1);
LCD_WR_DATA(x2);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1);
LCD_WR_DATA(y2);
LCD_WR_REG(0x2c);//储存器写
}
else if(USE_HORIZONTAL==1)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1);
LCD_WR_DATA(x2);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1+80);
LCD_WR_DATA(y2+80);
LCD_WR_REG(0x2c);//储存器写
}
else if(USE_HORIZONTAL==2)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1);
LCD_WR_DATA(x2);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1);
LCD_WR_DATA(y2);
LCD_WR_REG(0x2c);//储存器写
}
else
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1+80);
LCD_WR_DATA(x2+80);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1);
LCD_WR_DATA(y2);
LCD_WR_REG(0x2c);//储存器写
}
}

void LCD_Init(void)
{
LCD_GPIO_Init();//初始化GPIO

LCD_RES_Clr();//复位
sleep_ms(100);
LCD_RES_Set();
sleep_ms(100);

LCD_BLK_Set();//打开背光
sleep_ms(100);

//************* Start Initial Sequence **********//
LCD_WR_REG(0x11); //Sleep out
sleep_ms(120); //Delay 120ms
//************* Start Initial Sequence **********//
LCD_WR_REG(0x36);
if(USE_HORIZONTAL==0)LCD_WR_DATA8(0x00);
else if(USE_HORIZONTAL==1)LCD_WR_DATA8(0xC0);
else if(USE_HORIZONTAL==2)LCD_WR_DATA8(0x70);
else LCD_WR_DATA8(0xA0);

LCD_WR_REG(0x3A);
LCD_WR_DATA8(0x05);

LCD_WR_REG(0xB2);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x33);
LCD_WR_DATA8(0x33);

LCD_WR_REG(0xB7);
LCD_WR_DATA8(0x35);

LCD_WR_REG(0xBB);
LCD_WR_DATA8(0x32); //Vcom=1.35V

LCD_WR_REG(0xC2);
LCD_WR_DATA8(0x01);

LCD_WR_REG(0xC3);
LCD_WR_DATA8(0x15); //GVDD=4.8V 颜色深度

LCD_WR_REG(0xC4);
LCD_WR_DATA8(0x20); //VDV, 0x20:0v

LCD_WR_REG(0xC6);
LCD_WR_DATA8(0x0F); //0x0F:60Hz

LCD_WR_REG(0xD0);
LCD_WR_DATA8(0xA4);
LCD_WR_DATA8(0xA1);

LCD_WR_REG(0xE0);
LCD_WR_DATA8(0xD0);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x0E);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x05);
LCD_WR_DATA8(0x31);
LCD_WR_DATA8(0x33);
LCD_WR_DATA8(0x48);
LCD_WR_DATA8(0x17);
LCD_WR_DATA8(0x14);
LCD_WR_DATA8(0x15);
LCD_WR_DATA8(0x31);
LCD_WR_DATA8(0x34);

LCD_WR_REG(0xE1);
LCD_WR_DATA8(0xD0);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x0E);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x15);
LCD_WR_DATA8(0x31);
LCD_WR_DATA8(0x33);
LCD_WR_DATA8(0x48);
LCD_WR_DATA8(0x17);
LCD_WR_DATA8(0x14);
LCD_WR_DATA8(0x15);
LCD_WR_DATA8(0x31);
LCD_WR_DATA8(0x34);
LCD_WR_REG(0x21);

LCD_WR_REG(0x29);
}


颜色传感器驱动

#include "LTR_381RGB.h"
//#include "delay.h"
#include "lcd.h"
/*
**********************************************************
*
* IIC总线模拟程序
*
**********************************************************
*/

/*
*=========================================================
* 函数功能:MY_I2C端口状态配置
* 参数:
*
* 函数返回值:无
*=========================================================
*/
void MY_I2C_Init(void)
{
gpio_init(20);
gpio_set_dir(20, GPIO_OUT);
gpio_init(21);
gpio_set_dir(21, GPIO_OUT);

gpio_pull_up(20);
gpio_pull_up(21);
gpio_put(20, 1);
gpio_put(21, 1);
}


/*
*=========================================================
* 函数功能:MY_I2C的SDA数据线配置,这里作为输出用
* 参数:
*
* 函数返回值:无
*=========================================================
*/
void MY_I2C_SDA_IOOUT(void)
{
gpio_set_dir(20, GPIO_OUT);
}


/*
*=========================================================
* 函数功能:MY_I2C的SDA数据线配置,这里作为输入用
* 参数:
*
* 函数返回值:无
*=========================================================
*/
void MY_I2C_SDA_IOIN(void)
{
// gpio_pull_up(20);
gpio_set_dir(20, GPIO_IN);
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线启动信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
MY_I2C_SDA_H;
MY_I2C_SCL_H;
sleep_us(4);;
MY_I2C_SDA_L;
sleep_us(4);;
MY_I2C_SCL_L;
sleep_us(4);;
}

/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线停止信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
MY_I2C_SDA_L;
MY_I2C_SCL_H;
sleep_us(4);;
MY_I2C_SDA_H;
}

/*
*********************************************************************************************************
* 函 数 名: i2c_SendByte
* 功能说明: CPU向I2C总线设备发送8bit数据
* 形 参:_ucByte : 等待发送的字节
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;

/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
MY_I2C_SDA_H;
}
else
{
MY_I2C_SDA_L;
}
sleep_us(4);;
MY_I2C_SCL_H;
sleep_us(4);;
MY_I2C_SCL_L;
if (i == 7)
{
MY_I2C_SDA_H; // 释放总线
}
_ucByte <<= 1; /* 左移一个bit */
sleep_us(4);;
}
}

/*
*********************************************************************************************************
* 函 数 名: i2c_ReadByte
* 功能说明: CPU从I2C总线设备读取8bit数据
* 形 参:无
* 返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;

/* 读到第1个bit为数据的bit7 */
value = 0;
MY_I2C_SDA_IOIN();
for (i = 0; i < 8; i++)
{
value <<= 1;
MY_I2C_SCL_H;
sleep_us(4);;
if (MY_I2C_SDA_D)
{
value++;
}

MY_I2C_SCL_L;
sleep_us(4);;
}
MY_I2C_SDA_IOOUT();
return value;
}

/*
*********************************************************************************************************
* 函 数 名: i2c_WaitAck
* 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
* 形 参:无
* 返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
uint8_t re;

MY_I2C_SDA_H; /* CPU释放SDA总线 */
MY_I2C_SDA_IOIN();
sleep_us(4);;
MY_I2C_SCL_H; /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
sleep_us(4);;
if (MY_I2C_SDA_D) /* CPU读取SDA口线状态 */
{
re = 1;
}
else
{
re = 0;
}
MY_I2C_SDA_IOOUT();
MY_I2C_SCL_L;
sleep_us(4);;
return re;
}

/*
*********************************************************************************************************
* 函 数 名: i2c_Ack
* 功能说明: CPU产生一个ACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(void)
{
MY_I2C_SDA_L; /* CPU驱动SDA = 0 */
sleep_us(4);;
MY_I2C_SCL_H; /* CPU产生1个时钟 */
sleep_us(4);;
MY_I2C_SCL_L;
sleep_us(4);;
MY_I2C_SDA_H; /* CPU释放SDA总线 */
}

/*
*********************************************************************************************************
* 函 数 名: i2c_NAck
* 功能说明: CPU产生1个NACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{
MY_I2C_SDA_H; /* CPU驱动SDA = 1 */
sleep_us(4);;
MY_I2C_SCL_H; /* CPU产生1个时钟 */
sleep_us(4);;
MY_I2C_SCL_L;
sleep_us(4);;
}

/*
*********************************************************************************************************
* 函 数 名: i2c_CheckDevice
* 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
* 形 参:_Address:设备的I2C总线地址
* 返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;

// i2c_CfgGpio(); /* 配置GPIO */


i2c_Start(); /* 发送启动信号 */

/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
i2c_SendByte(_Address);
ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */

i2c_Stop(); /* 发送停止信号 */

return ucAck;
}

uint8_t LTR_381RGB_ReadReg(uint8_t Regs_Addr)
{
uint8_t ret;

i2c_Start();
i2c_SendByte(LTR_381RGB_DEV_WRITE); /* 此处是写指令 */
if (i2c_WaitAck() != 0)
{
}
i2c_SendByte((uint8_t)Regs_Addr);
if (i2c_WaitAck() != 0)
{
}

i2c_Start();
i2c_SendByte(LTR_381RGB_DEV_READ); /* 此处是读指令 */
if (i2c_WaitAck() != 0)
{
}
ret = i2c_ReadByte(); /* 读1个字节 */
i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
i2c_Stop();

return ret;
}

uint8_t LTR_381RGB_WriteReg(uint8_t Regs_Addr,uint8_t Regs_Data)
{
i2c_Start();
i2c_SendByte(LTR_381RGB_DEV_WRITE); /* 此处是写指令 */
if (i2c_WaitAck() == 0)
{
// return 0;
}
i2c_SendByte((uint8_t)Regs_Addr);
if (i2c_WaitAck() != 0)
{
// return 0;
}
i2c_SendByte(Regs_Data);
if (i2c_WaitAck() != 0)
{
// return 0;
}

i2c_Stop();
return 1;

}

void LTR_381RGB_init()
{
MY_I2C_Init();
sleep_ms(40);
char str[10];
uint8_t id = LTR_381RGB_ReadReg(0x06);
// sprintf(str,"ID:%X",id);//i2c_CheckDevice(LTR_381RGB_DEV_WRITE),i2c_CheckDevice(LTR_381RGB_DEV_WRITE+2));
// LCD_ShowString(0,0,str,RED,WHITE,32,0);//显示字符串
//
id = LTR_381RGB_ReadReg(0x07);
// sprintf(str,"a:%X",id);//i2c_CheckDevice(LTR_381RGB_DEV_WRITE),i2c_CheckDevice(LTR_381RGB_DEV_WRITE+2));
// LCD_ShowString(0,64,str,RED,WHITE,32,0);//显示字符串

LTR_381RGB_WriteReg(0x00, 0x06);
LTR_381RGB_WriteReg(0x04, 0x40);
LTR_381RGB_WriteReg(0x05, 0x04);

}


主要代码

int main(void) 
{
uint16_t temp_time = 0;
uint16_t grains_time = 5;
uint8_t reduce_num=0;

system_init();

LCD_Init();//LCD初始化
LCD_Fill(0,0,LCD_W,LCD_H,BLACK);
sleep_ms(1000);
LTR_381RGB_init();

while(1)
{
sleep_ms(100);
LTR_381RGB_read();
}
return 0;
}


颜色处理

void LTR_381RGB_read()
{
char str[40] = {0};
uint8_t data0,data1,data2;
uint32_t data_green = 0,data_red = 0,data_blue = 0;

uint8_t id = LTR_381RGB_ReadReg(0x07);

if(id != 0)
{

data2 = LTR_381RGB_ReadReg(0x0F);
data1 = LTR_381RGB_ReadReg(0x0E);
data0 = LTR_381RGB_ReadReg(0x0D);

data_green = (data2<<16)| (data1 << 8) | data0;

data2 = LTR_381RGB_ReadReg(0x12);
data1 = LTR_381RGB_ReadReg(0x11);
data0 = LTR_381RGB_ReadReg(0x10);
data_red = (data2<<16)| (data1 << 8) | data0;

data2 = LTR_381RGB_ReadReg(0x15);
data1 = LTR_381RGB_ReadReg(0x14);
data0 = LTR_381RGB_ReadReg(0x13);
data_blue = (data2<<16)| (data1 << 8) | data0;

sprintf(str,"R:%d ",data_red);
LCD_ShowString(0,0,str,WHITE,BLACK,32,0);//显示字符串
sprintf(str,"G:%d ",data_green);
LCD_ShowString(0,32,str,WHITE,BLACK,32,0);//显示字符串
sprintf(str,"B:%d ",data_blue);
LCD_ShowString(0,64,str,WHITE,BLACK,32,0);//显示字符串

uint16_t color = 0;
if(data_red >= 1000) color += RED;
if(data_green >= 1000) color += GREEN;
if(data_blue >= 1000) color += BLUE;

LCD_Fill(0, 100, 100, 200, color);
}
}


遇到的主要难题及解决方法

​无


未来的计划或建议

建议优化一下带屏十二指神探的按键,每次下载需要按住isp按键实在太费手了,按得手都累了,可以考虑优化一下壳子,或者按键直接裸露在外,这样按起来不费劲。


附件下载
Pico_Template-1.6.1.zip
测试图片.zip
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号