打印
[RISC-V MCU 创新应用比赛]

基于CH32V307天然色素提取工艺改进

[复制链接]
465|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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,传输到后台数据库,采集原始样本并形成规模数据后与研究院沟通。



云南享有动植物王国,天然植物色素丰富,集团内部下属公司多是生物医药,相同的工艺环节甚多,推广前景很好。另外化工、喷漆等行业也是潜在的应用行业。


567606401e7e649a8e.png (261.7 KB )

567606401e7e649a8e.png

118306401e830adafb.png (27.9 KB )

118306401e830adafb.png

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

67

主题

1957

帖子

14

粉丝