本帖最后由 zeshoufx 于 2023-3-3 20:45 编辑
基于CH32V307天然色素提取工艺改进 1、天然色素及提取
天然色素(NaturalPigments)的开发与应用己成为各行业科技工作者普遍关注的课题。人们试图从各种动植物资源中获取天然色素,同时探索其生理活性,来缓解并解决由合成色素所带来的各种问题。但是由于天然色素色泽不稳定,在其使用过程中容易受各种因素(如光照、温度、氧化、pH值、介质极性、金属离子、添加剂等)的影响而发生褪色、变色等方面的变化,而影响其着色效果,严重制约了天然色素代替人工合成色素的进程。现代天然植物色素的提取方法可分为溶剂提取法、熬煮法、酶反应法、超临界萃取法、压榨法、粉碎等方法。植物色素提取设备现场如图1所示。
图1植物色素提取设备现场 2、天然色素提取工艺特点及关键技术
天然色素提取,现场采用的方法是萃取提取,在各储料罐顶部需要进料、进水、进酒精,底部需要出料,包括最终的色素、水,其中酒精可能需要重复使用。因此储料罐链接的各个管道及阀门需要合理的控制,在保证质量的前提下,节约用料也是重点考量的一个要素。
目前现场工艺的主要特点包括:1、各种加料工程全部是人工完成,有经验的工人来完成调节阀门,期间不定时通过观察窗查看现场各步骤结果;2、出料品质由人工观察提取,并送研究院化验,取样和化验周期长,很难获取符合标准质量的色素;3、部分色素需人工品尝后来决定其品质,主观因素影响较大。
关键技术:通过开发一种识别色素特征的终端设备,将特征参数识别出来后发送到后台,后台系统通过标定,并与研究院化验数据进行对比,PLC控制系统根据终端设备的数据,控制管道阀门的开关,同时控制储料罐温度和气压。
3、原理实现
现场设备主要提取6种天然植物色素,现场每种色素的颜色差别较大,并且同一种色素在每个工艺过程中,颜色差别也较大。因此可以通过识别不同工艺下的色素颜色RGB分量,通过标定在不同RGB分量值下,对应研究院化验的结果,可得到各色素质量较好且经济的控制。现场的现场胡萝卜素在两个工艺下的颜色如图2所示。
file:///C:/Users/zes/AppData/Local/Temp/ksohtml156/wps3.png
图2 胡萝卜素在两个工艺下的颜色 4、硬件设计及传感器
样机硬件采用CH32V307VC单片机,小批量量产采用CH32V303CB单片机和ARM单片机GD32F303CC,这里主要以CH32V307VC开发板效果介绍。样机硬件和量产硬件如图3所示。
file:///C:/Users/zes/AppData/Local/Temp/ksohtml156/wps4.png
file:///C:/Users/zes/AppData/Local/Temp/ksohtml156/wps5.png
图3 a量产硬件 图3 b样机硬件(基于开发板)
其中主要的颜色识别传感器为欧姆龙的B5WC系列,价格1200RMB左右,采用IIC协议进行通信。B5WC型设备内置专用颜色传感器由装载了PD(光电二极管)来检测以具有可见光宽波长区域的白色LED为光源、以红:Red、绿:Green、蓝:Blue共三种光原色为受光元件的各波长区域光的RGB光电IC、光学透镜、装载了MCU的内部电路等专用元件构成。此外,即使检测距离发生变化,RGB比率也几乎保持不变状态,这也是颜色传感器的另一大特色。以下表示检测距离发生变化时的颜色传感器的受光输出、以及受光输出比率。颜色传感器也是一种反射型传感器,所以RGB受光输出值也会根据检测距离的变化而发生变化,但RGB受光输出比率几乎不变。这是因为除了检测物体的反射光量以外,还会检测反射光中所含R/G/B各波长成分所致。因此,即使与检测物体的间距各异并发生变化,也可稳定辨别检测物体的色差。传感器实物图及与单片机连接分别如图4和图5所示。
file:///C:/Users/zes/AppData/Local/Temp/ksohtml156/wps6.png[size=12.0000pt] [size=12.0000pt]图4 B5WC传感器实物图 file:///C:/Users/zes/AppData/Local/Temp/ksohtml156/wps7.jpg[size=10.5000pt] [size=12.0000pt]图5 B5WC传感器与单片机连接图 5、关键代码
5.1 B5WC传感器驱动
file:///C:/Users/zes/AppData/Local/Temp/ksohtml156/wps8.jpg[size=10.5000pt]
/*
* 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;
}
[size=10.5000pt]
[size=10.5000pt]5.2 Modbus-RTU协议实现
[size=28.0000pt]
#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<len;i++)
{
usart3_send_byte(bytes[i]);
}
}
void usart3_send_string(char *string)
{
while(*string)
{
usart3_send_byte(*string++);
}
}
void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
uint8_t data = USART_ReceiveData(USART1);
#if !MD_RTU_USED_OS
#if MD_USD_SALVE
MDSSerialRecvByte_1(data);
#else
#if MDM_USD_USART3
MDMSerialRecvByte(data);
#endif
#endif
#else
extern Modbus_RTU modbus_RTU;
MD_RTU_MsgPut((PModbusBase)(&modbus_RTU), MD_RTU_MSG_HANDLE_ARG(&modbus_RTU),(void*)(data),0);
#endif
}
}
[size=10.5000pt]
6、识别效果及展示视频
识别效果
file:///C:/Users/zes/AppData/Local/Temp/ksohtml156/wps9.jpg[size=10.5000pt]
[size=10.5000pt]视频链接:
https://www.bilibili.com/video/BV1aG4y1U7fo/?spm_id_from=333.999.list.card_archive.click
天然色———胡萝卜素提取_哔哩哔哩_bilibili
[size=12.0000pt]开源网址:https://gitee.com/zhang_en/color_identify.git
[size=12.0000pt]
7、现场应用效果及前景
目前已装上,通过终端模块连接4G DTU,传输到后台数据库,采集原始样本并形成规模数据后与研究院沟通。
云南享有动植物王国,天然植物色素丰富,集团内部下属公司多是生物医药,相同的工艺环节甚多,推广前景很好。另外化工、喷漆等行业也是潜在的应用行业。
|