2022TI杯10月联赛D题:盲盒识别装置详解

盲盒识别装置-基于MSP430F5529和LDC1314

  • 前言
  • 总体方案描述
  • LDC1314模块原理图
  • 硬件大致连接情况
  • LDC1314介绍
  • 用MSP430F5529驱动LDC1314(代码)
  • 驱动代码
  • 代码使用说明
  • 提升方面
  • 完整工程文件链接
  • 前言

       为备战2023国赛,在2022TI杯的10月联赛赛题中选择了D题盲盒识别装置作为训练题目。期间查询了很多金属探测的方式,基本是通过电磁感应来检测金属,通过比较不同金属对线圈电感的不同影响程度,加入振荡电路来测出振荡频率,进而比较不同的硬币情况。
       TI公司有给出专门用于电感测量的数字转换器。本文章选择了LDC1314作为检测芯片,通过不同硬币对LC振荡电路的不同影响,由单片机读取到不同的数据,来进行硬币的识别。
       在题目的完成过程中,查询了诸多LDC1314的驱动代码,但是基本上都不是使用裸芯片和MSP430F5529单片机进行控制的。进行多次修改后,完成了对LDC1314的数据读取。本文章侧重MSP430F5529对芯片的软件读取代码。文末给出了CCS工程文件链接。
       本代码显示使用了LCD12864液晶屏进行显示,板子为最小开发板。屏幕代码在文章中不再给出,在CCS工程文件中有相关说明。

    总体方案描述

       本系统以MSP430F5529作为控制核心,设计并制作了盲盒识别装置,实现了检测盲盒的有无,进而通过电磁检测技术判断不同种类盲盒内硬币的种类以及摆放方式。系统通过光敏传感器LED灯的亮灭来判断是否有盲盒放置,当检测到有盲盒时,LC振荡器谐振频率发生变化,系统通过LDC1314传感器来测量LC振荡器的谐振频率,不同的谐振频率对应不同种类的盲盒,进而判断盲盒内硬币的种类和摆放方式。系统通过扩展板OLED屏显示传感区域盲盒“有”“无”,以及工作状态,识别完成后能够显示识别完成和硬币种类和硬币组合。
       全部硬件的实物图片如下图所示:

    LDC1314模块原理图

       LDC1314模块的原理图大致如下图所示:
    LDC1314模块
       由于模块中在通道连接出已经有100pF电容,后续连接LC传感器的时候需要注意电容的值。
       该模块在淘宝中有现成的,建议可以直接买该模块:LDC1314转接板
       不太建议自己去焊接这个模块(自己血泪的教训)。其一、该芯片封装是WQFN(RGH)|16,芯片引脚全在下面,手焊很难;其二、该PCB资料还需要自己去画,进行打板,周期很长。

    硬件大致连接情况

       硬件主要是一个LDC1314模块和光电传感器模块。光电传感器模块淘宝上应该几块钱就能买到,主要是以一个VDD电源接口、一个GND接口和一个DO数字输出接口,模拟接口AO不需要接。用5529读取数字输出接口,判断有无盲盒遮挡就行;LDC1314需要用到I2C通信的引脚SCL和SDA,I2C地址选择端ADDR,以及工作状态引脚SD。
      LDC1314CLKIN采用了外部40M时钟。

    LDC1314介绍

       LDC1314读取LC振荡器的振荡频率,转化成相应的12位数字量,通过I2C通信来进行数据的读取。
       这篇文章介绍了LDC1314的各个引脚的功能以及连接情况,链接在这里:LDC1314中文引脚说明
       这篇文章介绍了LDC1314的使用方法,各个流程。以及一些寄存器的意义。LDC1314的使用
       两篇文章都是对TI官网上LDC1314数据手册的中文翻译,更具体的使用方法可以直接在数据手册上进行查找。

    用MSP430F5529驱动LDC1314(代码)

    驱动代码

       具体的I2C通信代码写法在此不再介绍,如有需要可以自行搜索I2C通信进行相关了解。直接给出LDC1314驱动全部代码。

    ldc1314.h文件如下:

    #ifndef SRC_LDC1314_H_
    #define SRC_LDC1314_H_
    
    typedef unsigned char uint8_t;
    typedef unsigned int uint16_t;
    
    extern unsigned int SENSOR_CH;              //数组声明 存储四个通道的电感线圈采样值
    
    //PORT_SET
    #define SCL_DIR  P6DIR |=BIT1
    #define SDA_DIR  P6DIR |=BIT2
    #define ADDR_DIR P6DIR|=BIT3
    #define SD_DIR   P6DIR|=BIT5
    
    #define ADDR_0 P6OUT&=~BIT3
    #define SD_0   P6OUT&=~BIT5
    #define SCL1 P6OUT |=BIT1
    #define SCL0 P6OUT &=~BIT1
    #define SDA1 P6OUT |=BIT2           //IIC数据引脚
    #define SDA0 P6OUT &=~BIT2
    #define SDAIN P6DIR &=~BIT2
    #define SDAOUT P6DIR |=BIT2
    #define SDADATA (P6IN & BIT2)
    
    
    // I2C
    void I2C_INIT(void);
    void IIC_Start(void);
    void IIC_Stop(void);
    void IIC_Ack(void);
    void IIC_NAck(void);
    unsigned char IIC_Wait_Ack(void);
    unsigned char IIC_Read_Byte(unsigned char ack);
    void IIC_Send_Byte(unsigned char txd);
    unsigned int Single_Write(unsigned char SlaveAddress,unsigned char REG_Address,unsigned int REG_data);
    unsigned char Single_Read(unsigned char SlaveAddress,unsigned char REG_Address,unsigned int *Read);
    
    // LDC1314 COMMANDS
    #define LDC13xx16xx_CMD_DATA_MSB_CH0          0x00
    #define LDC13xx16xx_CMD_DATA_LSB_CH0          0x01
    #define LDC13xx16xx_CMD_DATA_MSB_CH1          0x02
    #define LDC13xx16xx_CMD_DATA_LSB_CH1          0x03
    #define LDC13xx16xx_CMD_DATA_MSB_CH2          0x04
    #define LDC13xx16xx_CMD_DATA_LSB_CH2          0x05
    #define LDC13xx16xx_CMD_DATA_MSB_CH3          0x06
    #define LDC13xx16xx_CMD_DATA_LSB_CH3          0x07
    #define LDC13xx16xx_CMD_REF_COUNT_CH0         0x08     //参考计数设置
    #define LDC13xx16xx_CMD_REF_COUNT_CH1         0x09
    #define LDC13xx16xx_CMD_REF_COUNT_CH2         0x0A
    #define LDC13xx16xx_CMD_REF_COUNT_CH3         0x0B
    #define LDC13xx16xx_CMD_OFFSET_CH0          0x0C     //补偿值
    #define LDC13xx16xx_CMD_OFFSET_CH1          0x0D
    #define LDC13xx16xx_CMD_OFFSET_CH2          0x0E
    #define LDC13xx16xx_CMD_OFFSET_CH3          0x0F
    #define LDC13xx16xx_CMD_SETTLE_COUNT_CH0      0x10     //解决参考计数
    #define LDC13xx16xx_CMD_SETTLE_COUNT_CH1    0x11
    #define LDC13xx16xx_CMD_SETTLE_COUNT_CH2      0x12
    #define LDC13xx16xx_CMD_SETTLE_COUNT_CH3      0x13
    #define LDC13xx16xx_CMD_CLOCK_DIVIDERS_CH0  0x14     //参考和传感器分频设置
    #define LDC13xx16xx_CMD_CLOCK_DIVIDERS_CH1  0x15
    #define LDC13xx16xx_CMD_CLOCK_DIVIDERS_CH2  0x16
    #define LDC13xx16xx_CMD_CLOCK_DIVIDERS_CH3  0x17
    #define LDC13xx16xx_CMD_STATUS              0x18
    #define LDC13xx16xx_CMD_ERROR_CONFIG        0x19     //错误报告配置
    #define LDC13xx16xx_CMD_CONFIG              0x1A     //转换配置
    #define LDC13xx16xx_CMD_MUX_CONFIG          0x1B     //通道复用
    #define LDC13xx16xx_CMD_RESET_DEVICE        0x1C     //复位设备
    #define LDC13xx16xx_CMD_SYSTEM_CLOCK_CONFIG 0x1D     //
    #define LDC13xx16xx_CMD_DRIVE_CURRENT_CH0     0x1E     //当前驱动设置
    #define LDC13xx16xx_CMD_DRIVE_CURRENT_CH1   0x1F     //
    #define LDC13xx16xx_CMD_DRIVE_CURRENT_CH2   0x20     //
    #define LDC13xx16xx_CMD_DRIVE_CURRENT_CH3     0x21     //
    #define LDC13xx16xx_CMD_MANUFACTID          0x7E     //制造商ID
    #define LDC13xx16xx_CMD_DEVID                 0x7F     //设备ID
    
    #define EVM_MIN_I2CADDR     0x2A
    #define EVM_MAX_I2CADDR     0x2B
    #define EVM_DEFAULT_I2CADDR  EVM_MIN_I2CADDR                        //EVM_MIN_I2CADDR
    #define EVM_DEFAULTS_SIZE 24 // 13 registers, 0x08 - 0x14
    
    
    
    void LDC1314_INIT(void);
    void LDC1314_Read(void);
    
    
    #endif /* SRC_LDC1314_H_ */
    

    ldc1314.c文件如下:

    #include <msp430.h>
    #include "ldc1314.h"
    typedef unsigned char u8;
    unsigned int SENSOR_CH;                //存储四个通道的电感线圈采样值
    static uint8_t default_addr;
    
    #define CPU_F ((double)1000000)
    #define DelayMS(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))    //宏定义延时函数
    #define IIC_Delay(x) { unsigned char y; y = x; while(--y);}
    
    //I2C start/
    unsigned char I2C_SDA_READ()
    {
        unsigned char s;
        SDAIN;
        s=SDADATA;
        SDAOUT;
        return s;
    }
    
    void I2C_INIT(void)
    {
    
    }
    
    
    /*******************************************************************************
    * Function Name  : IIC_Start
    * Description    : Master Start Simulation IIC Communication
    * Input          : None
    * Output         : None
    * Return         : Wheather  Start
    ****************************************************************************** */
    void IIC_Start(void)
    {
        SDA1;
        SCL1;
        IIC_Delay(7);
        IIC_Delay(7);
        SDA0; ;
        IIC_Delay(7);
        SCL0;
    }
    /*******************************************************************************
    * Function Name  : IIC_Stop
    * Description    : Master Stop Simulation IIC Communication
    * Input          : None
    * Output         : None
    * Return         : None
    ****************************************************************************** */
    void IIC_Stop(void)
    {
        SCL0;
        SDA0;//STOP:when CLK is high DATA change form low to high
        IIC_Delay(7);
        SCL1;
        SDA1;//??I2C??????
        IIC_Delay(7);
    }
    
    /*******************************************************************************
    * Function Name  : IIC_Ack
    * Description    : Master Send Acknowledge Single
    * Input          : None
    * Output         : None
    * Return         : None
    ****************************************************************************** */
    void IIC_Ack(void)
    {
        SDA0;
        IIC_Delay(7);
        SCL1;
        IIC_Delay(7);
        SCL0;
        SDA1;
        IIC_Delay(1);
        SDA0;
        SCL0;
    }
    /*******************************************************************************
    * Function Name  : IIC_NAck
    * Description    : Master Send No Acknowledge Single
    * Input          : None
    * Output         : None
    * Return         : None
    ****************************************************************************** */
    void IIC_NAck(void)
    {
        SCL0;
        SDA1;
        IIC_Delay(7);
        SCL1;
        IIC_Delay(7);
        SCL0;
        SDA0;
    }
    
    /*******************************************************************************
    * Function Name  : I2C_Wait_Ack
    * Description    : Master Reserive Slave Acknowledge Single
    * Input          : None
    * Output         : None
    * Return         : Wheather  Reserive Slave Acknowledge Single
    ****************************************************************************** */
    unsigned  char IIC_Wait_Ack(void)
    {
        unsigned int ucErrTime = 0;
        SDA1;
        IIC_Delay(7);
        SCL1;
        IIC_Delay(7);
    
        while(I2C_SDA_READ())
        {
            ucErrTime++;
            if(ucErrTime>250)
            {
                IIC_Stop();
                return 1;
            }
        }
        SCL0;
        SDA0;
        IIC_Delay(7);
        return 0;
    }
    
    /*******************************************************************************
    * Function Name  : IIC_Send_Byte
    * Description    : Master Send a Byte to Slave
    * Input          : Will Send Date
    * Output         : None
    * Return         : None
    ****************************************************************************** */
    void IIC_Send_Byte(unsigned  char Dat)
    {
        unsigned  char t;
    
        for(t=0;t<8;t++)
        {
                    if(Dat & 0x80)
                    {
                        SDA1;
                    }
                    else
                    {
                        SDA0;
                    }
            Dat<<=1;
                    IIC_Delay(7);
                    SCL1;
                    IIC_Delay(7);
                    SCL0;
        }
    }
    /*******************************************************************************
    * Function Name  : I2C_Read_Byte
    * Description    : Master Reserive a Byte From Slave
    * Input          : None
    * Output         : None
    * Return         : Date From Slave
    ****************************************************************************** */
    unsigned  char IIC_Read_Byte(unsigned char ack)
    {
            unsigned char i,receive=0;
    
            SDA1;
            for(i=0;i<8;i++ )
            {
                SCL0;
                IIC_Delay(7);
                SCL1;
                IIC_Delay(3);
          receive<<=1;
          if(I2C_SDA_READ())receive++;
                IIC_Delay(3);
                SCL0;
        }
        if (!ack)
            IIC_NAck();
        else
            IIC_Ack();
        return receive;
    }
    //ZRX
    //单字节写入*******************************************
    
    uint16_t Single_Write(unsigned  char SlaveAddress, unsigned  char REG_Address,uint16_t REG_data)             //void
    {
            static unsigned  char buffer[2];
    
            buffer[0] = (REG_data >> 8);
            buffer[1] = (unsigned  char)(REG_data & 0x00ff);
    
        IIC_Start();
    
        IIC_Send_Byte(SlaveAddress << 1);
    
        if (IIC_Wait_Ack() == 1)
            {
                return 0;
            }
    
        IIC_Send_Byte(REG_Address);
            if (IIC_Wait_Ack() == 1)
            {
                return 0;
            }
    
            IIC_Send_Byte(buffer[0]);
            if (IIC_Wait_Ack() == 1)
            {
                return 0;
            }
    
            IIC_Send_Byte(buffer[1]);
            if (IIC_Wait_Ack() == 1)
            {
                return 0;
            }
    
            IIC_Stop();
    
            return 1;
    }
    
    
    //单字节读取*****************************************
    unsigned char Single_Read(unsigned  char SlaveAddress, unsigned  char reg_add,unsigned int *Read)
    {
        unsigned  char Dat_L = 0;
        unsigned  char Dat_H = 0;
    
        /* 器件地址 */
        IIC_Start();
        IIC_Send_Byte(SlaveAddress << 1);
        if (IIC_Wait_Ack() == 1)
        {
            return 0;
        }
    
        /* 寄存器地址 */
        IIC_Send_Byte(reg_add);
        if (IIC_Wait_Ack() == 1)
        {
            return 0;
        }
    
        /* 器件地址(读)*/
        IIC_Start();
    
        IIC_Send_Byte((SlaveAddress << 1) + 1);
        if (IIC_Wait_Ack() == 1)
        {
            return 0;
        }
    
    
        Dat_H = IIC_Read_Byte(1);   //ack
        Dat_L = IIC_Read_Byte(0);   //Nack
    
        IIC_Stop();
    
        *Read = ((Dat_H << 8) | Dat_L);
    
        return 1;
    }
    //I2C/
    
    
    
    
    /***************************************
     * 函数名:InitLDC1314
     * 描述  :初始化LDC1314
     * 输入  :无
     * 输出  :无
     ***************************************/
    void LDC1314_INIT(void)
    {
        uint8_t retVal=1;     //执行错误标志
    
        //IO_SET
        SCL_DIR  ;
        SDA_DIR  ;
        ADDR_DIR ;
        SD_DIR   ;
    
        //init begin
        ADDR_0;
        default_addr = EVM_DEFAULT_I2CADDR;
        SD_0;
        Single_Write(default_addr,LDC13xx16xx_CMD_RESET_DEVICE,0x8000);
        DelayMS(10);                  //此处延时10MS
        do
        {
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_REF_COUNT_CH0,0x04D6);     // 4 clock periods
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_REF_COUNT_CH1,0xFFFF);
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_REF_COUNT_CH2,0xFFFF);
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_REF_COUNT_CH3,0xFFFF);
    
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_OFFSET_CH0,0x0000);        //补偿值
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_OFFSET_CH1,0x0000);
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_OFFSET_CH2,0x0000);
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_OFFSET_CH3,0x0000);
    
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_SETTLE_COUNT_CH0,0x000A);  // 1 clock period
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_SETTLE_COUNT_CH1,0x0400);
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_SETTLE_COUNT_CH2,0x04FF);
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_SETTLE_COUNT_CH3,0x0400);
    
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_CLOCK_DIVIDERS_CH0,0x1001); // 1000
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_CLOCK_DIVIDERS_CH1,0x0000);
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_CLOCK_DIVIDERS_CH2,0x0000);
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_CLOCK_DIVIDERS_CH3,0x0000);
    
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_STATUS,0x0000); // report only DRDYs to INT
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_CONFIG,0x1601); // CLKIN pin1281
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_MUX_CONFIG,0x020D); // ch0, ch1,ch2,ch3-> Wipro for 4 ch
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_SYSTEM_CLOCK_CONFIG,0x0200); // default, divide by 2
    
            retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_DRIVE_CURRENT_CH0,0xF000); //
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_DRIVE_CURRENT_CH1,0x0000); //
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_DRIVE_CURRENT_CH2,0x0000); //
    //        retVal &= Single_Write(default_addr,LDC13xx16xx_CMD_DRIVE_CURRENT_CH3,0x0000); //
        }while(retVal==0);         //若执行出错,则继续循环初始化
    
    }
    /***************************************
     1. 函数名:LDC1314_Read
     2. 描述  :读取寄存器数据
     3. 输入  :无
     4. 输出  :无
     **************************************/
    void LDC1314_Read()
    {
        uint8_t retVal;
        do
        {
          retVal = 1;
          retVal &= Single_Read(default_addr,LDC13xx16xx_CMD_DATA_MSB_CH0,&SENSOR_CH);
    //      retVal &= Single_Read(default_addr,LDC13xx16xx_CMD_DATA_MSB_CH1,&SENSOR_CH[1]);
    //      retVal &= Single_Read(default_addr,LDC13xx16xx_CMD_DATA_MSB_CH2,&SENSOR_CH[2]);
    //      retVal &= Single_Read(default_addr,LDC13xx16xx_CMD_DATA_MSB_CH3,&SENSOR_CH[3]);
        }while( retVal == 0);
        //消除四个通道之间本身存在的采样误差值,归一化处理
    //    SENSOR_CH[0]+=2;
    //    SENSOR_CH[1]+=3;
    //    SENSOR_CH[2]+=3;
    //    SENSOR_CH[3]+=3;
    }
    

    示例main函数如下:

    #include <msp430.h>
    #include "ldc1314.h"
    #define SlaveAddress 0x2A   //ADDR需要接地
    unsigned int i=0;
    unsigned int temp=0;  //传感器结果
    
    int main(void)
     {
        WDTCTL = WDTPW | WDTHOLD;     // Stop watchdog timer
        SCL_DIR  ;   //6.1
        SDA_DIR  ;   //6.2
        ADDR_DIR ;   //6.3
        SD_DIR   ;   //6.5
        LDC1314_INIT();                //LDC1314初始化后需要延时
    
          _delay_cycles(1000000);      //1s  ldc线圈工作安全时间
      	LDC1314_Read();
        while(1)
      {
    
                    for(i=0;i<20;i++)
                    {
                        LDC1314_Read();
                        temp=temp+SENSOR_CH;
                        if(i==19)
                        {
                            temp=temp/20;
                        }
                    }
                    temp=0;
      }
      return 0;
     }
    
    

    代码运行后应该可以在temp中读取到传感器输出结果。

    代码使用说明

    1、代码首先需要使用LDC1314_INIT()函数进行初始化,本代码中初始化是针对CH0单通道检测进行的,里面对LDC1314的控制可以根据前面给出的LDC1314使用链接或者数据手册进行配置和修改。
    2、LDC1314_Read()函数是对检测数据进行读取,读取结果在SENSOR_CH()中。

    提升方面

       在完成过程中只实现了盲盒A和盲盒B的识别,并且盲盒B的识别效果并不理想。可以提升的方面有以下几点:
    1、LC传感器线圈的设计,推荐在用Ti给的适配LC传感器生成时将线圈设计的大一点,至少半径应该要大于所识别硬币的最大半径。
    2、选择使用LDC1614芯片作为电感检测芯片,该芯片使用方式和LDC1314完全相同,但是测量精度更高,可能能够更好的识别不同硬币间的差异。

    完整工程文件链接

    百度网盘链接:https://pan.baidu.com/s/14ISwsRxe3vFO6HXqM75A4Q?pwd=o7nv
    提取码:o7nv
       显示采用LCD12864,自己可以根据自己的屏幕对相关的显示代码进行更改。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 2022TI杯10月联赛D题:盲盒识别装置详解

    发表评论