domenica 20 marzo 2011

Interfaccia mouse ottico e PIC


se volete linkare, copiare, usare ecc ecc quello che c'è scritto in questo articolo potete farlo, ma citando l'autore.
non mi assumo responsabilità per danni a mouse, robot o alle vostre persone XD
e SI, sono io che ho pubblicato questo articolo anche su grix, quindi non l'ho copiato, è roba mia!

OK, COMINCIAMO! :)
Per un robot che sto costruendo avevo bisogno di un sensore di movimento preciso ed economico... cosa meglio di un mouse ottico? solo che il pic che stavo usando non ha controller usb...


Per cominciare, un po' di teoria del funzionamento di un mouse ottico.
Per trovare un mouse adatto ho passato le ultime settimane smontando mouse di amici e parenti, il risultato è che mi sono fatto un'idea di come funzionano le cose: ultimamente i mouse economici e "cinesi" utilizzano un integrato che si "appoggia" sul pc per funzionare, questi mouse NON vanno bene (ovviamente). Quello che uso nel seguito l'ho pagato 3€ su una bancarella dell'usato.
I mouse che ho provato e che mi hanno dato buoni risultati sono basati su integrati ADNS2051 e ADNS2610.
                          

Immagino che altri chip usino lo stesso protocollo (o molto simile), ma io questi ho provato e questi vi riporto. Nel seguito dell'articolo, se non diversamente specificato, farò riferimento al ADNS2610 per le spiegazioni.
Un piccolo inciso: mi preme far notare che questi chip sono estremamente sensibili all'inversione di polarità, purtroppo ho bruciato un mouse collegandolo male, quindi bisogna fare estremo affidamento ai datasheet e controllare più volte i fili.

Tornando al discorso: gli integrati possono essere alimentati fino a 5v utilizzando i piedini VDD e GND, saldando direttamente dei fili sui piedini e permettendo una facile riconversione a mouse "normale" (il resto del mouse non viene toccato o modificato in nessun modo).
Il protocollo utilizzato per la comunicazione è di tipo seriale, bi-direzionale, half-duplex e basato su un clock condiviso tra mouse e PIC (generato da quest'ultimo). Quindi per il collegamento al PIC bastano due fili, da collegare ai pin SCK (il clock) e SDIO (la linea dati), per un totale di 4 fili da saldare sull'integrato (2 di alimentazione e 2 di comunicazione).
Il mouse che ho utilizzato dopo la modifica si presenta come in foto:



Il funzionamento - in teoria
Finora ho descritto i collegamenti, passiamo adesso alla vera e propria teoria.
Un mouse ottico è basato su quella che viene definita "tecnologia di navigazione ottica", ossia rileva lo spostamento attraverso l'acquisizione sequenziale di immagini (attraverso un modulo chiamato IAS Image Acquisition System) e il confronto matematico di questi (attuato da un DSP, Digital Signal Processor).
Nella prossima immagine è schematizzato il funzionamento dell'integrato, dove DSP e IAS sono integrati in quello che viene chiamato "IMAGE PROCESSOR"
Nell'esploso seguente si nota il chip/sensore con il led di illuminazione e la lente di collimazione sottostante.

Lo spostamento è memorizzato (in modo automatico, quindi) nei registri interni del chip, l'unica difficoltà è recuperare questo dato.
Il protocollo di comunicazione
Inizialmente utilizzavo un mouse basato su ADNS2051, leggermente più difficile da utilizzare perché richiede una prima fase di inizializzazione e un controllo di un registro di stato prima di ogni lettura dei dati di spostamento. Visto che non posso fare più esperimenti con questo mouse (causa rottura), parlerò solo del (MOLTO) più semplice ADNS2610, a chi è interessato posso comunque fornire il codice necessario al corretto funzionamento dei sensori come il ADNS2051 (comunque una volta capito il funzionamento del protocollo è facilissimo implementare altre routine).
La comunicazione seriale è utilizzata per leggere e scrivere parametri sui registri del mouse e per la lettura delle informazioni di spostamento (quello che interessa a noi).
A iniziare la comunicazione è sempre il microcontrollore:

La lettura dello spostamento consiste in 2 byte di dati scambiati: nel primo (il tempo nella figura va da sinistra a destra) il PIC invia 1 byte di informazioni, con 0 nel MSB per indicare la direzione dei dati, seguìto dall'indirizzo del registro che vuole leggere. Il chip sul mouse risponde con 1 byte di dati in complemento a 2, quindi il primo bit codifica il segno e i restanti lo spostamento rispetto all'ultima lettura.
NB: se 1bit va per il segno ci sono 7bit rimanenti, il massimo spostamento tra una lettura e l'altra è quindi 2^7=128 (in modulo ovviamente). Come detto in precedenza la comunicazione è sincronizzata con il clock, nello specifico si commuta la linea dati sul fronte discendente e si legge sul fronte ascendente (ma questo sarà molto più chiaro guardando il codice). Come specificato sul datasheet del ADNS2610, bisogna attendere almeno 100uS tra l'invio del primo byte e la ricezione delle informazioni, per permettere la preparazione dei dati (nell'immagine precedente è indicata con una "spaccatura" tra il ciclo 8 e 9).
A titolo di esempio, questi sono i registri presenti su ADNS2610:
Particolarmente interessante è il contenuto del registro 0x08, che permette di leggere i pixel "visti" dal sensore, attraverso 324 letture consecutive (che tornano l'intensità del pixel) con il seguente schema:
conto di approfondire l'argomento in futuro, magari in un prossimo articolo, se interessa.
Un po' di pratica - il circuito
Lo schema elettrico è molto semplice, è un semplice PIC 16F877 clockato a 4Mhz. Ho usato la porta E per il collegamento al mouse e la porta B per un LCD 16x2. Il tutto ovviamente a 5v.
Image Hosted by ImageShack.us

realizzazione:
Un po' di pratica - il codice

Il codice che presento è sempre scritto in MikroC e lo trovate allegato, so che molti sono affezionati all'ASM ma questo rende la lettura molto più semplice e immediata. Nel seguito presento e commento un codice semplificato basato su quello che allego.
Questo che espongo è un po' l'"hello world" del mio progetto, ossia come ottenere le coordinate lette dal mouse. Da qui poi è facile modificare il codice.

Cominciamo con il main() che semplicemente legge i valori nei registri e li scrive su LCD ciclicamente (dopo aver inizializzato le porte e il display LCD). Ricordo che il registro contiene un valore in complemento a 2, quindi basta sommare sempre il valore di ritorno (positivo o negativo non importa, è solo uno spostamento dall'ultima lettura).

void main(){
        init_LCD_PIC();

        for(;;) {
          x += readRegister(DX);
          y += readRegister(DY);
          
          LCDWriteResult(x,y);
        }
}

Come avviene la lettura dei registri è presto detto (anzi scritto):

signed short readRegister(const unsigned short int address){
        signed short output = 0;
   
        DIRDATA = OUT;

        //scrivi l'indirizzo del registro da leggere
        for(i=7;i!=-1;i--){
            MOUSE_CLK = 0;
            MOUSE_DATA = 1 && ((address & (1<<i))>0);
            MOUSE_CLK = 1;
        }

        //preparati a leggere i dati inviati
        DIRDATA=IN;

        //aspetta che siano pronti
        Delay_us(100);

        for(i=8;i!=0;i--)  {
           MOUSE_CLK = 0;
           MOUSE_CLK = 1;
           output |= MOUSE_DATA << i-1;
        }
        
        return output;
}


Spiegazione: DIRDATA codifica lo stato di ricezione o invio di dati sulla seriale. Il primo for invia il primo byte di dati al mouse, utilizzando il clock come sincronia. Qui l'unica cosa da capire è il booleano risultante da:
1 && ((address & (1<<i))>0)
Per semplicità divido l'espressione:
address & (1<<i) fa un semplice OR bitwise tra una maschera a singolo 1 e tutti 0 e la variabile "address".
Il problema sorge quando l'indirizzo del registro ha più di un bit a 1: è questo il caso, ad esempio, del registro 0x03 (00000011 in binario) torna "2" con una maschera come 00000010. Se si prova a scrivere "2" su MOUSE_DATA (che corrisponde ad UN SINGOLO bit sulla PORT del PIC) si ottiene... niente! e quindi l'AND binario tra 1 e il risultato dell'espressione precedente serve a tornare "1" quando l'or bitwise della maschera con l'indirizzo del registro è maggiore o uguale a 1. Facile no? XD
Il secondo for "compone" bit per bit il numero risultato, leggendo (sul fronte di salita del clock) i dati dal mouse.
Foto del risultato:


Conclusioni












Ho mostrato come sia possibile leggere le coordinate di spostamento del mouse, ma questa è solo una possibile applicazione... 
è consentito qualsiasi uso non a scopo di lucro del codice, ma per favore citate l'autore (e possibilmente linkate questo sito)
Spero di non aver dimenticato niente... per qualsiasi commento o critica (possibilmente entrambi costruttivi...) sono a vostra disposizione. 

FILE SORGENTE

6 commenti:

  1. Ciao! Stavo seguendo il tuo progetto adattandolo al pic di Arduino. Avrei alcune domande da porti. Hai un canale privato o preferisci qui di seguito?
    grazie

    RispondiElimina
  2. chiedi pure... ma non conosco troppo bene arduino

    RispondiElimina
  3. Ciao complimenti per il progetto molto carino.
    Io ho smontato il mio mouse e ho un IC ADNS-2030
    che ha la stessa piedinatura del 2051.

    E' molto complicato estendere le routines per il 2051?

    Condivideresti con me quello che hai già fatto?
    ...intanto mi studio il codice che hai postato per l'A2610
    e cerco di imparare qualcosa di nuovo :-)

    Ciao e buona giornata,
    Virgus

    RispondiElimina
  4. tu sei un piccolo genio cazzo ... mi piace =) complimenti

    RispondiElimina
  5. Fantastico! sarebbe possibile anche ricavare la velocità di spostamento?

    RispondiElimina
    Risposte
    1. si certo... basta tenere traccia della posizione in funzione del tempo, avevo anche cominciato a muovermi in questo senso ma poi mi sono dedicato ad altro (purtroppo i mouse con i chip "giusti" cominiciano a scarseggiare).

      Elimina