鼠标宏系列之二-光电鼠标设计案例

我们会从一个真实的鼠标设计案例开始,来看看一个真实的光电鼠标是什么样子的,在文章的最后我会将鼠标项目打包上传到资源部分。

这个方案主要使用了adns2620芯片,这个芯片是光电传感器的集成芯片,方案中也有PCB图以及对应的程序代码,PCB结构之类的和本系列无关,就不去一一赘述了,我们主要看代码部分。

fireware代码如下:

/* 自动捕获鼠标坐标 */
static void getMouseMovement(void)
{
	reportBuffer.dx = -read(DELTA_X_REG);
	reportBuffer.dy = read(DELTA_Y_REG);
}

/* ------------------------------------------------------------------------- */

usbMsgLen_t usbFunctionSetup(uchar data[8])
{
    usbRequest_t    *rq = (void *)data;

    // 以下请求从未使用过。但由于它们是规范,我们在这个例子中实现了它们。

    if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS)
    {    
        // 类请求类型
        DBG1(0x50, &rq->bRequest, 1);   
        // 调试输出: 打印请求 
        if(rq->bRequest == USBRQ_HID_GET_REPORT)
        {   
            // wValue:ReportType(高字节),ReportID(低字节)
            usbMsgPtr = (void *)&reportBuffer;
            return sizeof(reportBuffer);
        }
        else if(rq->bRequest == USBRQ_HID_GET_IDLE)
        {
            usbMsgPtr = &idleRate;
            return 1;
        }    
        else if(rq->bRequest == USBRQ_HID_SET_IDLE)
        {
            idleRate = rq->wValue.bytes[1];
        }
    }
    else
    {
        // 未实现特定于供应商的请求 未实现请求,就不向主机返回任何数据
    }
    return 0;   
}

/* ------------------------------------------------------------------------- */

/* ------------------- ADNS2610 Functions ---------------------------------- */
//Function Description: 从ADNS2610读取指定地址的字节    
//Inputs:  ADNS2610要写入的寄存器地址
//Outputs: None
//Return:  从ADNS2610读取的字节
//usage: value = read(CONFIGURATION_REG);
char read(char address)
{
    char value=0;
	sbi(ADNS_DIR, ADNS_SDIO);	// 确保SDIO引脚设置为输出。
    sbi(ADNS_PORT, ADNS_SCL);   // 确保时钟引脚拉高。
    address &= 0x7F;            // 请确保地址字节的最高位为“0”以指示读取。
 
	int address_bit=0;
    // 发送地址到 ADNS2610
    for(address_bit=7; address_bit >=0; address_bit--)
    {
        cbi(ADNS_PORT, ADNS_SCL);  // 拉低时钟引脚
        sbi(ADNS_DIR, ADNS_SDIO);  // 确保SDIO引脚设置为输出
        
        // 如果当前位为1,则设置SDIO引脚。否则,清除SDIO引脚
        if(address & (1<<address_bit))
        {
            sbi(ADNS_PORT, ADNS_SDIO);
        }
        else
        {
            cbi(ADNS_PORT, ADNS_SDIO);
        }
        _delay_us(1);
        sbi(ADNS_PORT, ADNS_SCL);
        _delay_us(1);
    }
    
    // 允许ADNS2610有额外的时间转换SDIO引脚(根据数据表)
    _delay_us(120);   

    // 将SDIO作为微控制器上的输入
    cbi(ADNS_DIR, ADNS_SDIO);	// 确保将SDIO引脚设置为输入。
	sbi(ADNS_PORT, ADNS_SDIO);	// SDIO引脚的上拉。
        
	int value_bit=0;
    // 将值按字节发送到ADNS2610
    for(value_bit=7; value_bit >= 0; value_bit--)
    {
        cbi(ADNS_PORT, ADNS_SCL);     // 拉低 clock 引脚
        _delay_us(1);                 // 允许ADNS2610配置SDIO引脚
        sbi(ADNS_PORT, ADNS_SCL);     // 上拉 clock 引脚
        _delay_us(1);
        // 如果SDIO引脚为高电平,则在value变量中设置当前位。如果为低,则保留默认值位(0)。
        //if(sdio.read())value|=(1<<value_bit);     
		if((ADNS_PIN & (1<<ADNS_SDIO)) == (1<<ADNS_SDIO))value|=(1<<value_bit);
    }
    
    return value;
}

// Function Description: 将值写入ADNS2619上的指定地址
// Inputs: 字符地址-ADNS2610要写入的寄存器地址
// Outputs: None
// Return:  None
// usage: write(CONFIGURATION_REGISTER, config_setting);
 void write(char address, char value)
 {
	sbi(ADNS_DIR, ADNS_SDIO);	// 确保 SDIO 引脚设置为输出。
    sbi(ADNS_PORT, ADNS_SCL);   // 确认 clock 引脚为高.
    address |= 0x80;            // 请确保地址字节的最高位为“1”以指示写入.

	int address_bit=0;
    //Send the Address to the ADNS2610
    for(address_bit=7; address_bit >=0; address_bit--)
    {
        cbi(ADNS_PORT, ADNS_SCL);  // 拉低 clock 引脚
        
        // 提供一个小延迟(仅用于第一次迭代,以确保ADNS2610放弃
        
        _delay_us(1); 

        // 如果我们在“读取”命令之后执行此写入,则控制SDIO。
        
        // 如果当前位为1,则设置SDIO引脚。如果没有,清除SDIO引脚
        if(address & (1<<address_bit))sbi(ADNS_PORT, ADNS_SDIO);
        else cbi(ADNS_PORT, ADNS_SDIO);
        _delay_us(1);
        sbi(ADNS_PORT, ADNS_SCL);
        _delay_us(1);
    }
    
	int value_bit=0;
    //Send the Value byte to the ADNS2610
    for(value_bit=7; value_bit >= 0; value_bit--)
    {
        cbi(ADNS_PORT, ADNS_SCL);  // 拉低 clock 引脚
        // 如果当前位为1,则设置SDIO引脚。否则,清除SDIO引脚
        if(value & (1<<value_bit))
            sbi(ADNS_PORT, ADNS_SDIO);
        else 
            cbi(ADNS_PORT, ADNS_SDIO);
        _delay_us(1);
        sbi(ADNS_PORT, ADNS_SCL);
        _delay_us(1);
    }
}

void sync(void)
{
    sbi(ADNS_PORT, ADNS_SCL);
    _delay_ms(1);
    cbi(ADNS_PORT, ADNS_SCL);
    _delay_ms(1);
    sbi(ADNS_PORT, ADNS_SCL);
    _delay_ms(100);
}

//Function Description: 初始化ADNS2610的I/O。假设已经定义了sck和sdio的实例.
//Inputs: None
//Outputs: None
//Return:  None. 
//usage: setup();
void setupAdns(void)
{
	sbi(ADNS_DIR, ADNS_SCL);	// 将时钟设置为输出
	sbi(ADNS_DIR, ADNS_SDIO); 	// 将SDIO设置为输出

    sbi(ADNS_PORT, ADNS_SCL);  // 设置 Clock 引脚为高
    sbi(ADNS_PORT, ADNS_SDIO); // 设置 SDIO 引脚为高
 }
 
 /* ------------------------------------------------------------------------------------------- */

int __attribute__((noreturn)) main(void)
{
    uchar   i;

    wdt_enable(WDTO_1S);
    // 即使不用看门狗,也要把它关掉。在较新的设备上,
    // 看门狗的状态(开/关,周期)!


    // RESET状态:所有端口位均为无上拉输入。
    // 这就是我们需要D+和D-的方式。所以我们不需要
    // 额外的硬件初始化。

    odDebugInit();
    DBG1(0x00, 0, 0);       /* debug output: main starts */
    setupAdns();
	_delay_ms(100);
	sync();
	usbInit();
    usbDeviceDisconnect();  /* 强制重新枚举,在禁用中断时执行此操作! */
    i = 0;
    while(--i)
    {             
        /* USB断开连接 > 250 ms */
        wdt_reset();
        _delay_ms(1);
    }
    usbDeviceConnect();
    sei();
    DBG1(0x01, 0, 0);       /* debug output: main loop starts */
    for(;;){                /* 主循环 */
        DBG1(0x02, 0, 0);   /* debug output: main loop iterates */
        wdt_reset();
        usbPoll();
        if(usbInterruptIsReady())
        {
            // 在每次轮询中断端点后调用
            //advanceCircleByFixedAngle();
			getMouseMovement();
            DBG1(0x03, 0, 0);   /* debug output: interrupt report prepared */
            usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));
        }
    }
}

/* ------------------------------------------------------------------------- */

这部分主要是对一些芯片上的引脚的操作。

可以看一下Auduino库:

// 从ADNS2620传感器读取寄存器。将结果返回给调用函数。
// Example: value = mouse.read(CONFIGURATION_REG);
char ADNS2620::read(char address)
{
    char value=0;
	pinMode(_sda, OUTPUT);     // 确保SDIO引脚设置为输出。
    digitalWrite(_scl, HIGH);  // 确保时钟引脚为高。
    address &= 0x7F;           // 请确保地址字节的最高位为“0”以指示读取。
 
    //Send the Address to the ADNS2620
    for(int address_bit=7; address_bit >=0; address_bit--)
    {
        digitalWrite(_scl, LOW); 
        pinMode(_sda, OUTPUT); 
        
        if(address & (1<<address_bit))
        {
            digitalWrite(_sda, HIGH);
        }
        else
        {
            digitalWrite(_sda, LOW);
        }
        delayMicroseconds(10);
        digitalWrite(_scl, HIGH);
        delayMicroseconds(10);
    }
    
    delayMicroseconds(120); 
    pinMode(_sda, INPUT);	
	digitalWrite(_sda, HIGH); 
        
    //Send the Value byte to the ADNS2620
    for(int value_bit=7; value_bit >= 0; value_bit--)
    {
        digitalWrite(_scl, LOW);   
        delayMicroseconds(10);     
        digitalWrite(_scl, HIGH);  
        delayMicroseconds(10);

		if(digitalRead(_sda))value |= (1<<value_bit);

    }
    
    return value;
}	

我们对比FW程序和Auduino库,会发现几乎一致,实际上就是这么修改的,在读写的时候,会根据引脚的状态来设置value。

不过我估计很多人看了这个还是一头雾水,所以下一个文档会从最上层的开发方式来再做一个鼠标键盘输入器,读者可以体会两者之间的差距。

 

 

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ADNS-7050介绍: Avago公司的ADNS-7050(ADNS-7050数据手册)光学传感器是基于新的LaserStream技术,包括图像采集系统(IAS),数字信号处理器(DSP)和一个四线串口。ADNS-7050传感器的工作电压为2.7V-3.6V,并可自我调节,还具备延长电池使用寿命的节电模式。它可以实现每秒高达20英寸的移动探测能力、高达8G的加速度及可选的每英寸400和800cpi的分辨率。可广泛应用在激光鼠标,光学跟踪球,综合输入设备和以电池为能源的输入设备。 ADNS主要特性: Low power architecture New LaserStream technology Self-adjusting power-saving modes for longest battery life Speed motion detection up to 20 ips and 8G Enhanced SmartSpeed self-adjusting frame rate for optimum performance Motion detect pin output Internal oscillator – no clock input needed Selectable 400 and 800 cpi resolution Wide operating voltage: 2.7 V-3.6 V nominal Four wire serial port Minimal number of passive components Laser fault detect circuitry on-chip for Eye Safety Compliance ADNS-7050 2D装配图 ADNS-7050 光学鼠标框图 ANDS-7050应用: Laser mice Optical trackballs Integrated input devices Battery-powered input devices The ADNS-7050 sensor along with the ADNS-6120 or ADNS-6130-001 lens, ADNS-6230-001 clip and ADNV-6340 VCSEL form a complete and compact laser mouse tracking system. There is no moving part, which means high reliability and less maintenance for the end user. In addition, precision optical alignment is not required, facilitating high volume assembly. 附件内容包括: ADNS-7050 激光2.4G无线LED鼠标原理图; ADNS-7050 激光2.4G有线LED鼠标原理图; ADNS-7050数据手册; 激光2.4G无线鼠标设计说明书(英文); 日本NEC的2.4G无线光电鼠标参考设计(包括AVR和ARM两种程序及相关原理图);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值