在准备2023国赛期间,我们从2022TI杯的10月联赛赛题中选择了D题盲盒识别装置作为训练题目。从网上查找了许多资料,期间查询了很多金属探测的方式,大多都是通过电磁感应来检测金属,通过比较不同金属和金属大小对线圈电感的不同影响程度,进而比较不同的硬币情况。
方案描述
本文章选择了WL01涡流传感器作为检测硬币的工具,由DSP读取到不同的数据,来进行硬币的识别。
WL01涡流传感器的工作原理
当接通传感器系统电源时,在传感器模块内部回产生一个高频信号,在线圈周围产生交变磁场。如果有金属导体接近线圈,则交变磁场将在导体的表面产生电涡流场,该电涡流场也会产生一个方向与之相反的交变磁场,由于反作用,就会改变线圈高频电流的幅度和相位,即改变了线圈的有效阻抗。这种变化既与电涡流效应有关又与静磁学效应有关;既与金属导体的电导率、磁导率、几何形状、线圈几何参数,激励电流频率以及线圈金属导体的距离参数有关。
WL01涡流传感器原理图
代码示例
本文章使用的是DSP28335的开发板进行ADC采样,通过检测WL01涡流传感器的信号变化来识别硬币。本代码显示使用了LCD12864液晶屏进行显示,ADC采样代码不在给出,在CCS工程文件中有相关说明。
main.c如下:
#include "DSP28x_Project.h" // Device Headerfile and Examples Include File
#include "LCD12864.h"
#include "Adc.h"
#include "KEY.h"
void main(void)
{
InitSysCtrl();
// InitLedGpio();
// ADC引脚均为专用引脚,不需要初始化
Init_Port();
EALLOW;
adc_sys_clk();
// GpioCtrlRegs.GPBMUX1.bit.GPIO34 = 0; // GPIO pin
// GpioCtrlRegs.GPBDIR.bit.GPIO34 = 1; // Output pin
EDIS;
DINT;
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
// InitCpuTimers();
LCD_init();
InitAdc(); // 使能ADC模块,并上电
// SetAdcParm(); // 设置ADC模块有关参数
while(1)
{
Adc_Convert();
// Scan_Key();
}
}
LCD12864.c如下:
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_Examples.h" // DSP2833x Examples Include File
/***************全局变量定义****************/
#define uchar unsigned char
/****************端口宏定义*****************/
#define RS GpioDataRegs.GPADAT.bit.GPIO0
#define RW GpioDataRegs.GPADAT.bit.GPIO2
#define EN GpioDataRegs.GPADAT.bit.GPIO1
#define DB GpioDataRegs.GPADAT.all
// 使用前,声明本文件中的相关函数
void Init_Port(void); //端口初始化
void LCD_init(void); //初始化函数
void LCD_write_command(unsigned char command); //写指令函数
void LCD_write_data(uchar dat); //写数据函数
void LCD12864SetAddress_f( uchar x, uchar y ); //地址转换
void show(unsigned char x,unsigned char y,unsigned char *pData); //用于显示的子函数
void show1(unsigned char x,unsigned char y,unsigned char j);
void Show_Lcd3(Uint16 i);
void Show_Lcd2(Uint16 i);
void Show_Lcd1(Uint16 i);
void LcdClearTxt( void );
void LcdClearTxt2( void );
//C类盲盒
void zl()
{
show( 3,0,"识别完成");
show( 1,1, "C"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"一元站立");
}
void cw()
{
show( 3,0,"识别完成");
show( 1,1, "C"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"一元倾斜");
}
void tcwyj()
{
show( 3,0,"识别完成");
show( 1,1, "C"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"填充物一元");
}
//B类盲盒
void yyswjx()
{
show( 3,0,"识别完成");
show( 1,1, "B"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"一元在上五角在下");
}
void yyxwjs()
{
show( 3,0,"识别完成");
show( 1,1, "B"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"一元在下五角在上");
}
void lgyj()
{
show( 3,0,"识别完成");
show( 1,1, "B"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"两个一角");
}
void wjsyjx()
{
show( 3,0,"识别完成");
show( 1,1, "B"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"五角在上一角在下");
}
void wjxyjs()
{
show( 3,0,"识别完成");
show( 1,1, "B"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"一角在上五角在下");
}
//A类盲盒
void Show_Lcd3(Uint16 i)
{
show( 3,0,"识别完成"); // LcdClearTxt();
show( 1,1, "A"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"一角");
}
void Show_Lcd2(Uint16 i)
{
// LcdClearTxt();
show( 3,0,"识别完成 ");
show( 1,1, "A"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"五角");
}
void Show_Lcd1(Uint16 i)
{
show( 3,0,"识别完成 ");
show( 1,1, "A"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
show(0,2,"硬币数值:");
show(0,3,"一元");
}
//按键选择
void Al()
{
show( 1,1, "A"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
}
void Bl()
{
show( 1,1, "B"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
}
void Cl()
{
show( 1,1, "C"); //在12864的第1行第1列显示
show(2,1,"类盲盒");
}
void sbiez()
{
// LcdClearTxt();
show( 0,0,"有盲盒");
// show( 3,0,"识别中");
DELAY_US(1000000);//延迟100ms
// LcdClearTxt();
}
void wumh()
{
LcdClearTxt2();
// LCD_init();
show( 0,0,"无盲盒");
}
/*------------------------------------------*/
/*形式参数:void */
/*返回值:void */
/*函数描述:初始化gpio端口 */
/*------------------------------------------*/
void Init_Port(void)
{
EALLOW;
// 用户根据需要可以使能内部上拉或禁止.
// 其他不需要的行注释掉;
// SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;// 开启GPIO时钟
GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0; // 使能GPIO1 引脚内部上拉
GpioCtrlRegs.GPAPUD.bit.GPIO1 = 1; // 禁止GPIO3 引脚内部上拉
/* 通过I/O功能选择寄存器配置通用I/O功能*/
GpioCtrlRegs.GPAMUX1.all = 0x000000; // 配置GPIO0-GPIO15为通用I/O口
/* 设置I/O口为输入引脚*/
GpioCtrlRegs.GPADIR.all = 0x00FFF;// 配置GPIO1-GPIO11为输出引脚
// 每个输入口可以有不同的输入限定
// a) 输入与系统时钟 SYSCLKOUT同步
// b) 输入被指定的采样窗口限定
// c) 输入异步 (仅对外设输入有效)
GpioCtrlRegs.GPAQSEL1.all = 0x0000; // GPIO0-GPIO15与系统时钟SYSCLKOUT 同步
//输出数据LCD_RS置1和LCD_EN清零
GpioDataRegs.GPADAT.bit.GPIO0 = 1;
GpioDataRegs.GPADAT.bit.GPIO1 = 0;
EDIS;
}
/*--------------------------------------
;模块名称:LCD_write_command();
;功 能:LCD12864写指令函数
;参数说明:command为写命令参数
;-------------------------------------*/
void LCD_write_command(uchar command)
{
RS=0; //指令
EN=1; //允许
DB=(command<<2)|0x0002;//赋值给D0-D7对应的GPIO4-GPIO11引脚
DELAY_US(10000);
EN=0;
DELAY_US(100);
}
/*--------------------------------------
;模块名称:LCD_write_data();
;功 能:LCD12864写数据函数
;参数说明:dat为写数据参数
;-------------------------------------*/
void LCD_write_data(uchar dat)
{
RS=1; //指令
EN=1; //允许
DB=(dat<<2)|0x0003;//赋值给D0-D7对应的GPIO4-GPIO11引脚
DELAY_US(10000);
EN=0;
DELAY_US(100);
}
void LcdClearTxt( void )
{
uchar i;
// LCD_write_command(0x30); //8BitMCU,基本指令集合
LCD_write_command(0x80); //AC归起始位
for(i=0;i<16;i++)
LCD_write_data(0x20);
}
void LcdClearTxt1( void )
{
DELAY_US(1000000);
uchar i;
// LCD_write_command(0x30); //8BitMCU,基本指令集合
LCD_write_command(0x80); //AC归起始位
for(i=48;i<64;i++)
LCD_write_data(0x20);
// DELAY_US(1000000);
}
void LcdClearTxt2( void )
{
DELAY_US(1000000);
uchar i;
// LCD_write_command(0x30); //8BitMCU,基本指令集合
LCD_write_command(0x80); //AC归起始位
for(i=0;i<64;i++)
LCD_write_data(0x20);
// DELAY_US(1000000);
}
/*--------------------------------------
;模块名称:LCD_init();
;功 能:初始化LCD12864
;占用资源:--
;参数说明:--
;-------------------------------------*/
void LCD_init(void)
{
DELAY_US(100000);//延迟100ms
LCD_write_command(0x30);//功能设置
DELAY_US(200);//延迟200us
LCD_write_command(0x30);//功能设置
DELAY_US(200);//延迟200us
LCD_write_command(0x0c);//显示开关控制
DELAY_US(200);//延迟200us
LCD_write_command(0x01);//显示清除
DELAY_US(12000);//延迟12ms
LCD_write_command(0x06);//显示清除
DELAY_US(500);//延迟500us
}
void LCD12864SetAddress_f( unsigned char x, unsigned char y ) //地址转换
{
unsigned char byAddress;
switch( y )
{
case 0 :
byAddress = 0x80 + x;
break;
case 1 :
byAddress = 0x90 + x;
break;
case 2 :
byAddress = 0x88 + x;
break;
case 3 :
byAddress = 0x98 + x;
break;
default :
break;
}
LCD_write_command( byAddress ) ;
}
void show(unsigned char x,unsigned char y,unsigned char *pData)
{
LCD12864SetAddress_f( x, y ); //地址转换
while( *pData != '\0' )
{
LCD_write_data( *pData );
pData++;
DELAY_US(10);
}
}
void show1(unsigned char x,unsigned char y,unsigned char j)
{
LCD12864SetAddress_f( x, y ); //地址转换
if(j>0){
LCD_write_data(j/10+0x30);
LCD_write_data(j%10+0x30);
DELAY_US(10);
}
}
检测结果
盲盒识别装置
结果说明
该项目所要实现的功能基本完成,A、B、C类均能检测。但仍有些许不足:在检测时需要固定位置,如果检测位置改变有可能会使结果不准确,这应该是因为改变了硬币跟涡流传感器之间的距离,从而使得ADC采样的结果发生了改变。
如需完整项目,请关注公众号:【电子设计开发】