ADNS5050驱动程序(Arduino)

        这个是一个用来做位移传感用的,精确度差不多能到0.02mm,但1mm都够用了,所以各种性能溢出,主要还是驱动吧。说起驱动,其实没什么的,就是按照DataSheet去写而已。只要你看DataSheet就能写出来,上面各个引脚的作用,电路图,每个寄存器的作用都写得明明白白(不过确实有些不很好理解的,例如图像的读取部分写得就比较模糊,但是这个功能就是拿来玩吧,光流传感器都已经把位移处理的非常好了,何必自己找那个麻烦,又滤波又融合的,结果还没人家自己算的好,呵呵了)。

一、读DataSheet

        这部分非常重要,包括SPI通讯的各种设置以及电路连接(A5050是MSIO,IO是同一个口,需要你分成两根连Arduino的MSI和MSO,有一说用4K7和1K电阻的,我用了,但DataSheet上的电路图上并没有用。中间掉进去的最大坑是LED,用了杂牌垃圾LED,导致度数不断的上窜下跳,反复检查驱动之后发现没问题,于是换了某品牌(确实金线很清晰)的符合DataSheet上说的650nmLED问题解决。LED的限流电阻还是要的,算了一下发现没有合适的,用了个100欧的将就着,开了好久也没烧就当没问题了。有条件最好还是按DataSheet的电路图做。

二、电路连接

        不想上图了,发注释:

在NRESET和 + 5V之间焊接10K 欧姆电阻
+5V            —— Arduino +5V                电源正
GND            —— Arduino GND                电源负
NCS        I    —— Arduino digital pin 7        片选(低有效)
SDIO    I/O    —— Arduino digital pin 6        串行数据输入输出
SCLK    I    —— Arduino digital pin 5        串行时钟输入

XY_LED    O    ——                            LED控制
NRESET    I    —— 10K欧    Arduino +5V            重启(低有效)

照明LED要求HLMP-ED80(639nm)

凡是标了连接的都必须连接,需要注意的是XY_LED这个端口接的是LED的负极(抄DataSheet)。

三、写驱动

        DataSheet上有好多寄存器,用到的其实不多,因为固定CPI可以简化代码,所以没写那么多函数和常量。要达成的目的就这么几个:

0、控制CPI(默认500,设置为1000)

1、读Dx,Dy

2、读图像(玩的,根本没用到)

3、为了图像的灰度稳定性设置LED_DC_MODE寄存器,可以让LED常亮而不是自动亮度

这些的基础都是寄存器读写,至少你要能理解这种通讯模式下读写的方式才行,不理解的问问度娘吧。

a、读寄存器、DataSheet里写的很清楚MSB为0时是读

uint8_t ADNS5050Drive::ReadRegister(uint8_t addr)
{
	digitalWrite(PIN_NCS, LOW);		//开始SPI通讯,拉低
	delayMicroseconds(1);
	//读取寄存器地址时,MSB为0
	SPI.transfer(addr & 0x7f);
	delayMicroseconds(5);
	//读取返回值
	uint8_t data = SPI.transfer(0);
	delayMicroseconds(30);
	digitalWrite(PIN_NCS, HIGH);	//结束通讯
	delayMicroseconds(30);
	return data;
}

b、写寄存器

void ADNS5050Drive::WriteRegister(uint8_t addr, uint8_t data)
{
	digitalWrite(PIN_NCS, LOW);
	delayMicroseconds(1);
	//写入寄存器时,MSB为1
	SPI.transfer(addr | 0x80);
	delayMicroseconds(5);
	//发送数据
	SPI.transfer(data);
	delayMicroseconds(30);
	digitalWrite(PIN_NCS, HIGH);
	delayMicroseconds(30);
}

很懒,延时什么的写的差不多一样,延时多少也是读DataSheet,注意它里面的单位us,ms啥啥都有,自己算一下,代码里写到够用就行。

有了这两个就差不多了,加个初始化:

c、

#define PROD_ID                          0x00	//R
#define REV_ID                           0x01	//R
#define MOTION_ST                        0x02	//R		自上次读取是否发生了移动0为未移动,8位二进制补码
#define DELTA_X                          0x03	//R		
#define DELTA_Y                          0x04	//R
#define SQUAL                            0x05	//R		表面质量0为无,最大128
#define SHUT_HI                          0x06	//R		当前帧的最大像素值
#define SHUT_LO                          0x07	//R
#define PIX_MAX                          0x08	//R		
#define PIX_SUM							 0x09	//R
#define PIX_MIN                          0x0a	//R
#define PIX_GRAB                         0x0b	//R/W	连续读取361次,任何写入都将重置
#define MOUSE_CTRL                       0x0d	//R/W	最低位为0时CPI为500(默认)为1时1000;第一位为0(默认)时开启电源,1时关闭
#define MOUSE_CTRL2                      0x019	//R/W	第4位为1时,0-3位表示CPI
#define LED_DC_MODE                      0x022	//R/W	写入0x80将强制直流模式,00为自动亮度
#define CHIP_RESET						 0x3A	//W
#define PROD_ID2						 0x26	//R
#define INV_REV_ID						 0x3F	//R
#define MOTION_BURST                     0x63	//R
/*

MOUSE_CTRL2
Bit		7			6		5		4	3	2	1	0
Field Reserved Reserved Reserved RES_EN RES RES RES RES

RES_EN	= 0 Disable RES[3:0] setting.
		= 1 Enable RES[3:0] setting.
RES [3:0]	= 0b0001: 125 CPI
			= 0b0010: 250 CPI
			= 0b0011: 375 CPI
			= 0b0100: 500 CPI
			= 0b0101: 625 CPI
			= 0b0110: 750 CPI
			= 0b0111: 875 CPI
			= 0b1000: 1000 CPI
			= 0b1001: 1125 CPI
			= 0b1010: 1250 CPI
			= 0b1011: 1375 CPI
先把第4位设置为1,然后设置低3位。

*/	
    //初始化SPI
	pinMode(PIN_SDI, INPUT);
	pinMode(PIN_SDO, OUTPUT);
	pinMode(PIN_NCS, OUTPUT);
	SPI.begin();
	//设置SPI通讯
	SPI.setBitOrder(MSBFIRST); //位顺序(高位在前)
	SPI.setDataMode(SPI_MODE3); //空闲时高电平、在SCLK第二个边沿(上升沿)取样
	SPI.setClockDivider(SPI_CLOCK_DIV16); // 16MHz/16 = 1MHz
	delay(10);
	//打开A5050并进行配置
	digitalWrite(PIN_NCS, HIGH);		//片选
	delay(20);
	digitalWrite(PIN_NCS, LOW);			//片选完成
	delay(20);
	digitalWrite(PIN_NCS, HIGH);		//通讯前准备
	delay(20);
	WriteRegister(CHIP_RESET, 0x5a);	//使用寄存器将其重启
	delay(65);							//等待重启完成(55ms)
	WriteRegister(MOUSE_CTRL, 0x01);	//配置1000CPI:25.4mm分成1000份
	delay(60);
	WriteRegister(LED_DC_MODE, 0x80);	//配置LED_DC_MOD为直流模式
	delay(60);

这些都是DataSheet要求的,上面的图示说的很清楚,按照它设置就可以了,频率嘛应该可以支持的高一点,但高有什么用呢,够用就行。

然后就是Dx,Dy的偏移了,这个东西只要你读的快,它就不会溢出。另外关于类型的转化问题,寄存器里读回来的都是uint_8,即无符号8位,然后DataSheet上有个图,跟你说的很清楚几代表的是几,所以不要巴拉巴拉的一大堆又补码又啥的,直接把uint_8赋值给int_8就是正确的值了(不要怀疑,同长度直接把内容拿过来,值自然就符合DataSheet上说的)。

bool ADNS5050Drive::Loop()
{
	if (ReadRegister(MOTION_ST) == 0) {
		XOffset = 0;
		YOffset = 0;
		return false;
	}
	else {
		XOffset = ReadRegister(DELTA_X);
		YOffset = ReadRegister(DELTA_Y);
		XOffsetFull += XOffset;
		YOffsetFull += YOffset;
		return true;
	}
}

d、最后,就是玩的了,可能玩的更吸引人?

        DataSheet上有一个大篇幅的光流传感器读图像的描述。但是我真的理解了好几次才理解上去,他并不是说你每次读19*19=361,而是说你读361个MSB为1的是一幅图像。所以边读边舍弃MSB为0的直到读满361次就是一个图像,而DataSheet上说任何写入都咋的咋的,我的理解就是它进行了一个重新初始化的过程,并且当你下次读回来的为0时说明初始化完成可以读取了。

/*
PIX_GRAB 的读取结果顺序:
18	37	...	322	341	360
17	36	...			359
16	35				358
.
.
.
2	21
1	20
0	19				342
其0-6位为数据,最高位为有效性标志
*/
void ADNS5050Drive::ReadImage()
{
	uint16_t p = 0;
	uint8_t val = 0;
	WriteRegister(PIX_GRAB, 0);
	while (p!=361) {
		val = ReadRegister(PIX_GRAB);
		if (val&0x80) {
			Image[p / 19][p % 19] = val;
			p++;
		}
	}
}

所以每次读图像之前我都写了个0进去,可能这不是必要操作。然后?读就好了啊。最后,可能需要用到SQUAL(表面质量)寄存器,这个应该是意味着这个值比较好的时候数据才是可信的。所以才有人给这玩意套了个镜头改善近视之后就当不近视了吧,具有好的增益和滤波电路的还好,不然就自然照度下能识别成什么德行还真不好说。

        本来还买了ADNS3050的(这货在某宝上比5050还贵不少),但是丢件大家都理解,所以就没写这货的驱动。能驱动光流传感器、有了Arduino可以自己做鼠标了,Arduino有鼠标键盘库,可以发送移动、点击,虽然EEPROM小点,但存若干个简单的自动化脚本一点问题也没有,自己写个WINDOWS程序和串口通讯一下,就能实现可编程鼠标了。给Arduino连根线,点击切换功能,让某个功能时左键按下则额外发送鼠标移动就自动压枪了,某个功能时开始录制脚本(鼠标按下时统计总dx,dy偏移),写入EEPROM,播放时插入随机移动,规定模拟时和录制点击点的偏移在一个范围内,做成一个不需要WINDOWS程序的自动录制宏鼠标有那么难吗?兴趣上来了?那就去试试!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清晨曦月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值