# color_identify **Repository Path**: zhang_en/color_identify ## Basic Information - **Project Name**: color_identify - **Description**: 基于CH32V307天然色素提取工艺改进 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 2 - **Created**: 2023-02-28 - **Last Updated**: 2025-04-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # color_identify #### 介绍 基于CH32V307天然色素提取工艺改进 1、天然色素及提取 天然色素(NaturalPigments)的开发与应用己成为各行业科技工作者普遍关注的课题。人们试图从各种动植物资源中获取天然色素,同时探索其生理活性,来缓解并解决由合成色素所带来的各种问题。但是由于天然色素色泽不稳定,在其使用过程中容易受各种因素(如光照、温度、氧化、pH值、介质极性、金属离子、添加剂等)的影响而发生褪色、变色等方面的变化,而影响其着色效果,严重制约了天然色素代替人工合成色素的进程。现代天然植物色素的提取方法可分为溶剂提取法、熬煮法、酶反应法、超临界萃取法、压榨法、粉碎等方法。植物色素提取设备现场如图1所示。 ![输入图片说明](https://foruda.gitee.com/images/1677552069121081451/38fd8a6e_5476365.png "1.png") 图1植物色素提取设备现场 2、天然色素提取工艺特点及关键技术 天然色素提取,现场采用的方法是萃取提取,在各储料罐顶部需要进料、进水、进酒精,底部需要出料,包括最终的色素、水,其中酒精可能需要重复使用。因此储料罐链接的各个管道及阀门需要合理的控制,在保证质量的前提下,节约用料也是重点考量的一个要素。 目前现场工艺的主要特点包括:1、各种加料工程全部是人工完成,有经验的工人来完成调节阀门,期间不定时通过观察窗查看现场各步骤结果;2、出料品质由人工观察提取,并送研究院化验,取样和化验周期长,很难获取符合标准质量的色素;3、部分色素需人工品尝后来决定其品质,主观因素影响较大。 关键技术:通过开发一种识别色素特征的终端设备,将特征参数识别出来后发送到后台,后台系统通过标定,并与研究院化验数据进行对比,PLC控制系统根据终端设备的数据,控制管道阀门的开关,同时控制储料罐温度和气压。 3、原理实现 现场设备主要提取6种天然植物色素,现场每种色素的颜色差别较大,并且同一种色素在每个工艺过程中,颜色差别也较大。因此可以通过识别不同工艺下的色素颜色RGB分量,通过标定在不同RGB分量值下,对应研究院化验的结果,可得到各色素质量较好且经济的控制。 4、硬件设计及传感器 样机硬件采用CH32V307VC单片机,小批量量产采用CH32V303CB单片机和ARM单片机GD32F303CC,这里主要以CH32V307VC开发板效果介绍。 其中主要的颜色识别传感器为欧姆龙的B5WC系列,价格1200RMB左右,采用IIC协议进行通信。B5WC型设备内置专用颜色传感器由装载了PD(光电二极管)来检测以具有可见光宽波长区域的白色LED为光源、以红:Red、绿:Green、蓝:Blue共三种光原色为受光元件的各波长区域光的RGB光电IC、光学透镜、装载了MCU的内部电路等专用元件构成。此外,即使检测距离发生变化,RGB比率也几乎保持不变状态,这也是颜色传感器的另一大特色。以下表示检测距离发生变化时的颜色传感器的受光输出、以及受光输出比率。颜色传感器也是一种反射型传感器,所以RGB受光输出值也会根据检测距离的变化而发生变化,但RGB受光输出比率几乎不变。这是因为除了检测物体的反射光量以外,还会检测反射光中所含R/G/B各波长成分所致。因此,即使与检测物体的间距各异并发生变化,也可稳定辨别检测物体的色差。传感器实物图及与单片机连接分别如图4和图5所示。 ![B5WC传感器实物图](https://foruda.gitee.com/images/1677552580361810610/6241db46_5476365.png "2.png") ![B5WC传感器与单片机连接图](https://foruda.gitee.com/images/1677552611453568364/1fef661a_5476365.png "3.png") 5、关键代码 5.1 B5WC传感器驱动 ![编程流程图](https://foruda.gitee.com/images/1677552687994873843/bed122db_5476365.png "4.png") ``` /* * b5wc.c * * Created on: Jan 30, 2023 * Author: zeshou */ #include "b5wc.h" #define Address_Lenth Address_8bit void b5wc_io_set(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; I2C_InitTypeDef I2C_InitTSturcture = {0}; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); I2C_InitTSturcture.I2C_ClockSpeed = 100000; I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C; I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitTSturcture.I2C_OwnAddress1 = 0x80; I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable; I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init(I2C2, &I2C_InitTSturcture); I2C_Cmd(I2C2, ENABLE); I2C_AcknowledgeConfig(I2C2, ENABLE); } void b5wc_write_byte(u16 addr,u8 data) { while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY) != RESET); I2C_GenerateSTART(I2C2, ENABLE); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C2, 0X80, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); #if(Address_Lenth == Address_8bit) I2C_SendData(I2C2, (u8)(addr & 0x00FF)); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); #elif(Address_Lenth == Address_16bit) I2C_SendData(I2C2, (u8)(WriteAddr >> 8)); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, (u8)(WriteAddr & 0x00FF)); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); #endif if(I2C_GetFlagStatus(I2C2, I2C_FLAG_TXE) != RESET) { I2C_SendData(I2C2, data); } while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C2, ENABLE); } //void b5wc_wait_standby_state(void) //{ // vu32 val = 0; // // while(1) // { // while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY)); // i2c_start_on_bus(I2C0); // while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)); // i2c_master_addressing(I2C0, 0x80, I2C_TRANSMITTER); // // do // { // val = I2C_STAT0(I2C0); // // }while(0 == (val & (I2C_STAT0_ADDSEND | I2C_STAT0_AERR))); // // if(val & I2C_STAT0_ADDSEND) // { // i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND); // i2c_stop_on_bus(I2C0); // return ; // // } // else // { // i2c_flag_clear(I2C0,I2C_FLAG_AERR); // } // i2c_stop_on_bus(I2C0); // while(I2C_CTL0(I2C0)&0x0200); // } //} u8 b5wc_read_byte(u16 addr) { u8 temp = 0; while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY) != RESET); I2C_GenerateSTART(I2C2, ENABLE); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C2, 0x80, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); #if(Address_Lenth == Address_8bit) I2C_SendData(I2C2, (u8)(addr & 0x00FF)); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); #elif(Address_Lenth == Address_16bit) I2C_SendData(I2C2, (u8)(ReadAddr >> 8)); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, (u8)(ReadAddr & 0x00FF)); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); #endif I2C_GenerateSTART(I2C2, ENABLE); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C2, 0x80, I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); while(I2C_GetFlagStatus(I2C2, I2C_FLAG_RXNE) == RESET) I2C_AcknowledgeConfig(I2C2, DISABLE); temp = I2C_ReceiveData(I2C2); I2C_GenerateSTOP(I2C2, ENABLE); return temp; } void b5cw_init(void) { b5wc_io_set(); Delay_Ms(100); b5wc_write_byte(0x00,0x5a); b5wc_write_byte(0x01,0x0a); b5wc_write_byte(0x00,0x5b); if(0x0a==b5wc_read_byte(0x01)) { printf("参数设置成功...\r\n"); } else { printf("参数设置失败...\r\n"); } } void b5cw_RGB_value_get(u16 *rgb) { u8 temp1=0,temp2=0,temp3=0,temp4=0,temp5=0,temp6=0; temp1=b5wc_read_byte(0x02); temp2=b5wc_read_byte(0x03); temp3=b5wc_read_byte(0x04); temp4=b5wc_read_byte(0x05); temp5=b5wc_read_byte(0x06); temp6=b5wc_read_byte(0x07); rgb[0]=(temp2<<8)+temp1; rgb[1]=(temp4<<8)+temp3; rgb[2]=(temp6<<8)+temp5; } ``` 5.2 Modbus-RTU协议实现 ``` #include "tim3.h" #include "Sys_Config.h" #if MD_USD_SALVE //#include "MDS_RTU_Serial.h" #include "MDS_RTU_Serial_1.h" #else #include "MDM_RTU_Serial.h" #endif vu32 sys_tick_100us=0; //General purpose timer 3 interrupt initialization //The clock here is 2 times that of APB1, and APB1 is 36M //arr: automatic reload value. //psc: clock prescaler number //Timer 3 is used here! void TIM3_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_Prescaler =psc; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM3, ENABLE); } void TIM3_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); sys_tick_100us++; #if !MD_RTU_USED_OS #if MD_USD_SALVE // MDSTimeHandler100US(sys_tick_100us); MDSTimeHandler100US_1(sys_tick_100us); #else MDMTimeHandler100US(sys_tick_100us); #endif #endif } } #include "usart3.h" #include "Sys_Config.h" #if MD_USD_SALVE #include "MDS_RTU_Serial_1.h" #else #include "MDM_RTU_Serial.h" #include "MD_RTU_SysInterface.h" #include "MDM_RTU_Fun.h" #endif void RS485RWConvInit(void) { // GPIO_InitTypeDef GPIO_InitStructure; // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC| RCC_APB2Periph_AFIO, ENABLE); // BKP_TamperPinCmd(DISABLE); // BKP_ITConfig(DISABLE); // GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; // GPIO_Init(GPIOC, &GPIO_InitStructure); // GPIO_ResetBits(GPIOC,GPIO_Pin_13); } void init_usart3(u32 baudRate) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStruct; /* Enable GPIO clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 |\ RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = baudRate; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* Configure USARTz */ USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE); NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStruct.NVIC_IRQChannelSubPriority=0; NVIC_Init(&NVIC_InitStruct); } void usart3_send_byte(u8 byte) { while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET); USART_SendData(USART1,byte); while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET); } void usart3_send_bytes(u8 *bytes,int len) { int i; for(i=0;i