LDC1000循迹小车
以LDC1000电感/数字转换器为核心,设计并制作一个可自动根据铁丝形状循迹小车。
系统的主要功能模块包括:主控模块、金属信号探测模块、速度检测模块,电机驱动模块及电源模块。
系统主控芯片STC89C52根据检测到的金属距离信号,判断小车是否偏离轨道,从而做出方向调整,使小车能够在规定的平面跑道自动按一定方向自动循迹。
循轨广泛应用很于工业自动化及系统,而循迹的方式有很多种,每种都有其缺点。
机械链条式轨道存在磨损占用地方;光电慢反射检测式需要轨道为黑色、受环境光影响比较大;摄像头模式识别式需要很好的识别算法,受环境光影响也比较大。
基于此,本文采用LDC1000对铁丝进行检测判断,实现自动循轨应用。
LDC1000简介
LDC1000是美国德州仪器公司TI推出的一块全新的电感数字转换器(LDC),具有低功耗,小封装,低成本的优良性质。
它的SPI界面可以很方便的连接MCU。
LDC1000只需要外接一个PCB线圈或者自制线圈就可以实现非接触式电感检测。
LDC1000的电感检测并不是指像Q表那样测试线圈的电感量,而是可以测试外部金属物体和LDC1000相连的测试线圈的空间位置关系。
利用LDC1000这个特性配以外部设计的金属物体即可很方便实现:
水平或垂直距离检测;
角度检测;
位移监测;
运动检测;
振动检测;
金属成分检测;
可以广泛应用在汽车、消费电子、计算机、工业、通信和医疗领域。
stc89c52 与LDC1000接口
mcu与LDC1000接口图
使用stc89c52的io口模拟spi接口,和从机(LDC1000)进行通讯的时候,
应该遵循以下几个步骤:
片选信号置零;
stc89c52通过模拟SPI线向LDC1000写入访问寄存器地址,其中最高位0表示写入,1表示读出,其余7位为寄存器的地址;
占据八个时钟周期,这段时间内SDO线处于高阻状态;
如果命令在读写状态时,传输的数据最高位为1,SDO在线发送来自其他地址寄存器的8位字节;
如果命令在读写状态时,SDI线接收来自stc89c52的8位字节数据写入相应的寄存器中;
片选信号置高,释放对该从机的控制。
LDC1000 SPI 读写时序图
stc89c52 驱动程序设计
根据LDC1000的spi接口时序设计如下驱动程序:
// 读1B数据
unsigned char Ldc_SPIread(void);
//写1B数据
void Ldc_SPIwrite(unsigned char ch);
//写寄存器
void LDC1000_WriteReg(unsigned char addr, unsigned char dat);
//读寄存器
unsigned char LDC1000_ReadReg(unsigned char addr);
//芯片初始化
void LDC1000_Init(void);
//读取距离参数
unsigned int ReadValue(void);
控制逻辑
小车车头布置感应天线,当小车沿铁丝向前行车时,由于特殊原因(如路面不平,小车的车轮不对称等原因)导致车身偏离轨道,会出现A,B,C三种情况(如下图)。
A,B,C 三种情况的天线下,利用函数ReadValue()得到的数值为Fa,Fb,Fc。
三者间的关系为Fa<=Fb, Fc<=Fb。
为此,当检测到数值为Fb时小车直行,否则小车转弯。
左转与右转根据转弯的幅度做调整。
程序流程如下:
根据以上原理制作了系统轨道,实验小车,并利用串口检测系统监测的铁丝数据。
铁丝的直径0.9mm以上,循轨小车包括控制主板、显示、光电测速、电机、蓝牙收发模块等。
根据车身是否偏离轨道监测到的采样数据。
总结
利用PCB线圈或自制线圈可以实现非接触式电感线圈检测,通过分析待测金属物体对电感线圈磁场的影响可以很方便地实现金属检测。
经实测,通过合理设置模块寄存器值,可以有效检测3cm距离的金属。
外界光照条件对本系统没有太大的影响,而且还必须能够在一定的距离内快速检测到金属并能够辨别金属类型。
附:LDC1000驱动程序
#include “reg52.h”
// LDC COMMANDS
#define LDC1000_CMD_REVID 0x00
#define LDC1000_CMD_RPMAX 0x01
#define LDC1000_CMD_RPMIN 0x02
#define LDC1000_CMD_SENSORFREQ 0x03
#define LDC1000_CMD_LDCCONFIG 0x04
#define LDC1000_CMD_CLKCONFIG 0x05
#define LDC1000_CMD_THRESHILSB 0x06
#define LDC1000_CMD_THRESHIMSB 0x07
#define LDC1000_CMD_THRESLOLSB 0x08
#define LDC1000_CMD_THRESLOMSB 0x09
#define LDC1000_CMD_INTCONFIG 0x0A
#define LDC1000_CMD_PWRCONFIG 0x0B
#define LDC1000_CMD_STATUS 0x20
#define LDC1000_CMD_PROXLSB 0x21
#define LDC1000_CMD_PROXMSB 0x22
#define LDC1000_CMD_FREQCTRLSB 0x23
#define LDC1000_CMD_FREQCTRMID 0x24
#define LDC1000_CMD_FREQCTRMSB 0x25
sbit PIN_LDC_CS=P1^0;
sbit PIN_LDC_SO=P1^1;
sbit PIN_LDC_SCK=P1^2;
sbit PIN_LDC_SI=P1^3;
#define SET_PIN_LDC_SI(bValue) PIN_LDC_SI=bValue
#define GET_PIN_LDC_SO() PIN_LDC_SO
#define SET_PIN_LDC_SCK(bValue) PIN_LDC_SCK=bValue
#define SET_PIN_LDC_CS(bValue) PIN_LDC_CS=bValue
/*功能: 读1B数据 输入: 无 输出: 1B数据*/
unsigned char Ldc_SPIread(void)
{
unsigned char i = 0;
ch = 0;
for(i = 0; i <= 8; i ++)
{
SET_PIN_LDC_SCK(0);
ch <<= 1;
SET_PIN_LDC_SCK(1);
if(GET_PIN_LDC_SO())ch = ch | 0x01;
}
SET_PIN_LDC_SCK(0);
return (ch);
}
/*功能: 写1B数据 输入: ch--1B数据 输出: 无 */
void Ldc_SPIwrite(unsigned char ch)
{
unsigned char i = 0;
for(i = 0;i <= 8;i ++)
{
SET_PIN_LDC_SCK(0);
if(ch & 0x80) SET_PIN_LDC_SI(1);
else SET_PIN_LDC_SI(0);
SET_PIN_LDC_SCK(1) ch <<= 1;
}
SET_PIN_LDC_SCK(0)
}
//写寄存器
void LDC1000_WriteReg(unsigned char addr, unsigned char dat)
{
addr = addr & 0x7F;
SET_PIN_LDC_CS (0);
Ldc_SPIwrite( addr);
Ldc_SPIwrite( dat);
SET_PIN_LDC_CS (1);
}
//读寄存器
unsigned char LDC1000_ReadReg(unsigned char addr)
{
unsigned char temp;
addr = addr | 0x80;
SET_PIN_LDC_CS (0);
Ldc_SPIwrite( addr);
temp = (UINT8)Ldc_SPIread();
SET_PIN_LDC_CS (1);
return temp;
}
//芯片初始化
void LDC1000_Init(void)
{
LDC1000_WriteReg(LDC1000_CMD_RPMAX, 0x13);
LDC1000_WriteReg(LDC1000_CMD_RPMIN, 0x3A);
LDC1000_WriteReg(LDC1000_CMD_SENSORFREQ, 0x94);
LDC1000_WriteReg(LDC1000_CMD_LDCCONFIG, 0x17);
LDC1000_WriteReg(LDC1000_CMD_CLKCONFIG, 0x02);
LDC1000_WriteReg(LDC1000_CMD_INTCONFIG, 0x02);
LDC1000_WriteReg(LDC1000_CMD_THRESHILSB, 0x50);
LDC1000_WriteReg(LDC1000_CMD_THRESHIMSB, 0x14);
LDC1000_WriteReg(LDC1000_CMD_THRESLOLSB, 0xC0);
LDC1000_WriteReg(LDC1000_CMD_THRESLOMSB, 0x12);
LDC1000_WriteReg(LDC1000_CMD_PWRCONFIG, 0x01);
}
//读取距离参数
unsigned int ReadValue(void)
{
unsigned int iTemp;
iTemp= LDC1000_ReadReg(LDC1000_CMD_PROXMSB);
iTemp<<=8;
iTemp+= LDC1000_ReadReg(LDC1000_CMD_PROXLSB);
return iTemp;
}