***************************************************************************** * prova15.asc * * Stefano Rosa - Sergio Sigala * * VERSIONE: 1.0 - 04 JUL 2001 * * DESCRIZIONE: programma da installare sul 68HC11 per controllare in velocit… * i motori del MARMOT via canale seriale. Vedere la documentazione per * ulteriori informazioni. * * ATTENZIONE: ricordarsi di predisporre opportunamente il jumper J5 sulla * scheda, in modo da selezionare le modalit… special bootstrap o single * chip, come necessario. * * ATTENZIONE: le routine sono state scritte per la massima velocit… * operativa e non per minimizzare l'occupazione di memoria. ***************************************************************************** ***************************************************************************** * settaggi di alto livello ***************************************************************************** PERMIN equ 300 * periodo minimo per i motori (2.5KHz) PERMAX equ 16000 * periodo massimo per i motori (62.5Hz) STOP equ 16001 * periodo indicante motori fermi (PERMAX + 1) ID_SETSPEED equ 0 * id pacchetto settaggio velocit… ID_GETSPEED equ 1 * id pacchetto lettura velocit… ID_GETADC equ 2 * id pacchetto lettura convertitori A/D ID_BADPKT equ 3 * id pacchetto per mancato riconoscimento ID_BADCHKSUM equ 4 * id pacchetto notifica errore di checksum ID_TIMEOUT equ 5 * id pacchetto notifica errore di timeout ID_SETMOTORS equ 6 * id pacchetto settaggio stato motori ID_GETMOTORS equ 7 * id pacchetto richiesta stato motori ID_DEADCONN equ 8 * id pacchetto notifica connessione morta ID_BADPARMS equ 9 * id pacchetto notifica dati inacettabili ************************************************************* * Tempo trascorso il quale la connessione con il PC viene * dichiarata caduta dal 68HC11; come risultato di questa * situazione i motori vengono fermati. Questa costante * viene espressa in multipli di 5ms. ************************************************************* DEADTIME equ 1000 * 1000 * 5ms = 5s ***************************************************************************** * indirizzi dei registri ***************************************************************************** PORTC equ $03 PORTB equ $04 DDRC equ $07 * data direction register port c PORTD equ $08 DDRD equ $09 * data direction register port d TCNT equ $0e TOC1 equ $16 TOC2 equ $18 TOC3 equ $1A TOC4 equ $1C TOC5 equ $1E TCTL1 equ $20 TCTL2 equ $21 TMSK1 equ $22 TFLG1 equ $23 TMSK2 equ $24 TFLG2 equ $25 SPCR equ $28 SPSR equ $29 SPDR equ $2a BAUD equ $2b SCCR1 equ $2c SCCR2 equ $2d SCSR equ $2e SCDR equ $2f ADCTL equ $30 ADR1 equ $31 ADR2 equ $32 ADR3 equ $33 ADR4 equ $34 OPTION equ $39 ***************************************************************************** * indirizzi in memoria RAM ***************************************************************************** rambase equ $00 * indirizzo prima cella di RAM disponibile stacktop equ $ff * indirizzo top of stack rxsize equ 32 * dimensione del buffer di ricezione txsize equ 32 * dimensione del buffer di trasmissione ***************************************************************************** * allocazioni in memoria RAM ***************************************************************************** ************************************************************* * Variabili utilizzate per la generazione dei tre segnali ad * onda quadra per il pilotaggio dei motori. * * ATTENZIONE: non toccare l'ordine di inserimento delle * variabili in questo blocco. Analizzare il funzionamento * delle routine checkoc5 e setspddir per ulteriori * informazioni. ************************************************************* org rambase per_oc2 rmb 2 * periodo per OC2 - word newp_oc2 rmb 2 * nuovo periodo desiderato per OC2 - word per_oc3 rmb 2 * periodo per OC3 - word newp_oc3 rmb 2 * nuovo periodo desiderato per OC3 - word per_oc4 rmb 2 * periodo per OC4 - word newp_oc4 rmb 2 * nuovo periodo desiderato per OC4 - word dir rmb 1 * direzioni correnti dei motori - byte newdir rmb 1 * nuove direzioni dei motori - byte ************************************************************* * Due variabili locali utilizzate dalla routine * aggiornarampa. ************************************************************* mask rmb 1 * campo di bit per selezionare il motore salto rmb 2 * incremento (o decremento) per la rampa ************************************************************* * Variabile locale utilizzata dalla routine setmotors. ************************************************************* temp rmb 1 * byte ************************************************************* * Varabili utilizzate dal buffer di ricezione del canale * seriale. ************************************************************* rxin rmb 2 * indice in ingresso - word rxout rmb 2 * indice in uscita - word rxlen rmb 2 * quantit… di caratteri in coda - word rxbuf rmb rxsize * buffer di ricezione - blocco oldrxlen rmb 1 * dimensione precedente iterazione - byte rxpktsize rmb 1 * dimensioni pacchetto in ricezione - byte rxtimer rmb 1 * timer per rilevare timeout - byte ************************************************************* * Variabili utilizzate dal buffer di trasmissione del canale * seriale. ************************************************************* txin rmb 2 * indice in ingresso - word txout rmb 2 * indice in uscita - word txlen rmb 2 * quantit… di caratteri in coda - word txbuf rmb txsize * buffer di trasmissione - blocco txchksum rmb 1 * running checksum per la trasmissione ************************************************************* * Contiene l'intervallo di tempo, espresso in multipli di * 5ms, trascorso il quale senza ricevere dati dalla seriale, * viene dichiarato caduto il collegamento col PC e quindi * fermati i tre motori. ************************************************************* deadtime rmb 2 * word ***************************************************************************** * altre locazioni in memoria non RAM ***************************************************************************** regbas equ $1000 * indirizzo base del register block iniprog equ $f800 * indirizzo prima cella EEPROM ($f800-$ffff) ***************************************************************************** * vettori di interrupt (mappati in EEPROM) ***************************************************************************** org $ffd6 * vettore di interrupt della seriale fdb #isr_sci org $ffe2 * vettori di interrupt dei timer fdb #isr_oc4 fdb #isr_oc3 fdb #isr_oc2 org $fffe * reset vector fdb #iniprog ***************************************************************************** * reset entry point ***************************************************************************** ************************************************************* * Inizializzazione del processore. * * ATTENZIONE: per motivi di efficienza operativa da qui in * poi il registro X Š riservato per contenere l'indirizzo * base del blocco di registri di I/O. ************************************************************* org iniprog lds #stacktop * predispone lo stack ldx #regbas * carica indirizzo register block jsr initimer * inizializza i timer jsr initsci * inizializza il canale seriale jsr initports * inizializza le porte rimanenti jsr initadc * inizializza l'ADC cli * enable interrupts ************************************************************* * Main program. * * E' un semplicissimo ciclo infinito, eseguito alla massima * velocit… consentita e senza introdurre alcun ritardo. ************************************************************* main jsr checkpkt * guarda se ricevuto nuovo pacchetto jsr checkoc5 * esegue il polling del timer OC5 bra main ***************************************************************************** * ROUTINE CHE POTREBBERO ESSERE MIGLIORATE ***************************************************************************** ***************************************************************************** * Questa routine aggiorna il periodo del contatore relativo al motore * indicato dalla posizione dell'UNICO bit a 1 nel registro A. Il registro Y * deve contenere l'offset, espresso in byte, dei dati in memoria relativi a * tale motore, tenendo come base l'indirizzo per_oc2. * * Input: A = maschera per selezionare il motore di interesse * Y = offset dei dati del motore, a partire da per_oc2 * Output: nessuno * Registri alterati: ignoti * * ATTENZIONE: la routine usa le variabili locali mask e salto. ***************************************************************************** * Ecco i significati dei bit contenuti nella variabile dir: * * dir = 00000xxx * |||||||| * |||||||+- direzione (CW/CCW#) motore M1 collegato a OC2 * ||||||+-- direzione (CW/CCW#) motore M2 collegato a OC3 * |||||+--- direzione (CW/CCW#) motore M3 collegato a OC4 * ||||+---- libero * |||+----- libero * ||+------ libero * |+------- libero * +-------- libero ***************************************************************************** aggiornarampa staa mask ldaa dir eora newdir * queste istruzioni fanno saltare anda mask * se il motore indicato da mask NON beq ar_nochdir * deve cambiare direzione ************************************************************* * Se l'esecuzione arriva a questo punto il motore in esame * deve cambiare direzione: prima lo rallentiamo fino a * raggiungere il periodo massimo consentito, attraverso * l'aumento istantaneo o progressivo del periodo * dell'oscillatore a cui Š collegato. * * Calcola la differenza tra i periodi; se Š piccola salta * direttamente al nuovo periodo, altrimenti vi arriva * progressivamente. ************************************************************* ldd #PERMAX subd per_oc2,y * D = PERMAX - per_oc2,y cpd #32 blt ar_salta * salta se (PERMAX - per_oc2,y) < 32 ************************************************************* * L'incremento per il periodo si trova caricando e dividendo * per 32 il periodo attuale. ************************************************************* ldd per_oc2,y lsrd * : 2 lsrd * : 4 lsrd * : 8 lsrd * : 16 lsrd * : 32 ************************************************************* * Somma al periodo attuale e controlla il risultato, * confrontandolo col periodo massimo consentito. ************************************************************* addd per_oc2,y cpd #PERMAX * confronta con massimo consentito blt ar_update * salta se D < PERMAX ************************************************************* * Il motore ha oltrepassato il periodo massimo, dunque Š * necessario invertire il suo senso di marcia, sempre * mantenendolo al massimo periodo (ovvero la minima * velocit…). * * Prima di terminare controlla se alla fine il motore deve * essere fermato, perch‚ l'arresto pu• essere effettuato * immediatamente qui. ************************************************************* ar_salta ldaa dir * inverte la direzione del motore eora mask staa dir ldd newp_oc2,y cpd #PERMAX * salta se newp_oc2 <= PERMAX, ovvero bls ar_prosegui * se motore non deve essere fermato ************************************************************* * Effettivamente si richiede di fermare il motore; niente di * pi— facile, basta portare il suo periodo a PERMAX + 1. ************************************************************* ldd #STOP * fissa il periodo a PERMAX + 1 bra ar_update ************************************************************* * Se invece l'esecuzione arriva qui significa che la somma * precedente ha dato come risultato un periodo maggiore del * massimo consentito ma, nonostante ci•, il motore deve * continuare a girare. Dunque limita il valore al periodo * massimo. ************************************************************* ar_prosegui ldd #PERMAX * fissa il periodo a PERMAX ar_update std per_oc2,y * aggiorna il periodo in memoria rts * <=== IMPORTANTISSIMO ************************************************************* * Una volta appurato che il motore NON DEVE cambiare * direzione, controlla se deve almeno cambiare periodo; in * caso negativo esce immediatamente. ************************************************************* ar_nochdir ldd newp_oc2,y subd per_oc2,y beq ar_nochspeed * salta se i periodi sono uguali ************************************************************ * Il periodo deve essere cambiato. Per farlo esistono due * modi alternativi: (i) alterarlo immediatamente secondo il * nuovo valore desiderato; (ii) raggiungere in modo graduale * il nuovo valore. * * La scelta del modo avviene considerando il modulo della * differenza tra i due periodi; se Š piccolo si sceglie il * metodo (i), altrimenti il (ii). Ricordiamo che nel * registro D ora vi Š il valore (newp_oc2 - per_oc2). ************************************************************ bpl ar_positivo * salta se la differenza Š positiva coma comb * esegue complemento a due per addd #1 * cambiare il segno alla differenza ************************************************************* * A questo punto D = |newp_oc2 - per_oc2| e si pu• scegliere * molto facilmente il metodo da usare per l'aggiornamento * del periodo. ************************************************************* ar_positivo cpd #32 * salta se differenza >= 32, cio‚ se bhs ar_graduale * valore desiderato Š troppo distante ************************************************************* * Se l'esecuzione arriva qui i periodi sono quasi uguali, * cio‚ |newp_oc2 - per_oc2| < 32, dunque Š accettabile * passare direttamente al nuovo periodo, invece di arrivarci * gradualmente. ************************************************************* ldd newp_oc2,y std per_oc2,y rts * <=== IMPORTANTE ************************************************************* * L'esecuzione salta qui se il nuovo periodo Š troppo * distante e deve quindi essere raggiunto progressivamente. ************************************************************* ar_graduale ldd per_oc2,y cpd newp_oc2,y * salta se per_oc2 < newp_oc2, cio‚ blo ar_aumenta * se si deve aumentare il periodo ************************************************************* * Il decremento per il periodo si trova caricando la * differenza tra i periodi e dividendo per 32. ************************************************************* ldd per_oc2,y subd newp_oc2,y * calcola la differenza tra i periodi lsrd * : 2 lsrd * : 4 lsrd * : 8 lsrd * : 16 lsrd * : 32 std salto ************************************************************* * Sottrae dal periodo attuale e controlla il risultato, * confrontandolo col periodo minimo consentito * (corrispondente alla massima velocit…). ************************************************************* ldd per_oc2,y subd salto cpd #PERMIN bhs ar_aggiorna * salta se D >= PERMIN ldd #PERMIN * fissa a PERMIN bra ar_aggiorna ************************************************************* * Se l'esecuzione arriva qui Š necessario incrementare il * periodo attuale. L'incremento si trova caricando e * dividendo per 32 il periodo attuale. ************************************************************* ar_aumenta ldd per_oc2,y * carica periodo attuale e divide lsrd * : 2 lsrd * : 4 lsrd * : 8 lsrd * : 16 lsrd * : 32 ************************************************************* * Somma al periodo attuale e controlla il risultato, * confrontandolo col periodo massimo consentito, * corrispondente alla minima velocit…. ************************************************************* addd per_oc2,y cpd #PERMAX bls ar_aggiorna * salta se D <= PERMAX ************************************************************* * Visto che il periodo attuale ha superato il massimo * permesso, controlla se il motore deve essere fermato. In * caso positivo viene arrestato qui immediatamente. ************************************************************* ldd newp_oc2,y cpd #PERMAX bls ar_continua * salta se newp_oc2 <= PERMAX ldd #STOP * fissa il periodo a PERMAX + 1 bra ar_aggiorna ar_continua ldd #PERMAX * fissa il periodo a PERMAX ar_aggiorna std per_oc2,y * aggiorna il periodo in memoria ar_nochspeed rts ***************************************************************************** * NON TOCCARE NIENTE QUI SOTTO: VA TUTTO BENE ***************************************************************************** ***************************************************************************** * Routine che controlla se Š scattato il timer OC5. Ricordiamo che tale * timer viene gestito ESCLUSIVAMENTE in modalit… polling, pertanto Š * necessario controllare periodicamente il suo stato, con una frequenza * maggiore di quella che il timer stesso genera. Questa routine viene * chiamata CONTINUAMENTE dal main program, dunque la condizione precedente * risulta ampiamente soddisfatta. * * ATTENZIONE: Il valore per incrementare il contatore associato ad OC5 Š * stato scelto in modo che la ruotine entri NEL SUO NUCLEO ogni 5ms. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** coc5_end rts checkoc5 brclr TFLG1,x,#$08,coc5_end * esce se non Š scattato ************************************************************* * Nucleo della routine: se l'esecuzione arriva qui significa * che il timer associato a OC5 Š scattato. Dunque, per prima * cosa, aggiorna il contatore di OC5 ed azzera il flag di * segnalazione relativo. ************************************************************* ldd TOC5,x addd #10000 * 10000 * 0.5us = 5ms std TOC5,x * carica nuovo valore ldaa #$08 staa TFLG1,x * azzera il flag relativo a OC5 ************************************************************* * Le attivit… che Š necessario svolgere periodicamente sono * tre: (i) decrementare il contatore di timeout per il * canale seriale, (ii) controllare a che punto siamo * all'interno della rampa di velocit… di ciascuno dei tre * motori, aggiornando la situazione se necessario, e (iii) * verificare se il collegamento seriale Š vitale. ************************************************************* dec rxtimer ************************************************************* ldaa #%00000001 * A = maschera individuazione CW/CCW# ldy #0 * Y = offset dei dati jsr aggiornarampa * aggiorna rampa per OC2 ldaa #%00000010 * A = maschera individuazione CW/CCW# ldy #4 * Y = offset dei dati jsr aggiornarampa * aggiorna rampa per OC3 ldaa #%00000100 * A = maschera individuazione CW/CCW# ldy #8 * Y = offset dei dati jsr aggiornarampa * aggiorna rampa per OC4 ************************************************************* jsr checkconn * controlla stato della connessione ************************************************************* jmp checkperiodi * aggiorna registri I/O del micro ***************************************************************************** * Questa routine controlla lo stato del collegamento seriale. Se non vi Š * attivit… da pi— di DEADTIME multipli di 5ms, la connessione viene * dichiarata caduta dal 68HC11 e perci• i motori vengono fermati. E' anche * possibile mandare un pacchetto di tipo DEADCONN al PC per segnalare la * situazione. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** checkconn ldy deadtime beq cc_end * salta se il contatore Š gi… nullo dey * decrementa sty deadtime * aggiorna bne cc_end * salta se non Š ancora zero ************************************************************* * E' da troppo tempo che non si ricevono pacchetti dal PC. * Dunque Š saggio fermare i tre motori, mantenendoli per• * sempre alimentati. ************************************************************* ldd #STOP std newp_oc2 * ferma il motore M1 std newp_oc3 * ferma il motore M2 std newp_oc4 * ferma il motore M3 ************************************************************* * Commentare le linee seguenti se non si vuole un pacchetto * di segnalazione dal 68HC11. ************************************************************* *ldaa #3 *jsr sciputlen * trasmette dimensione pacchetto * *ldaa #ID_DEADCONN *jsr sciput * trasmette conferma ricezione * *jmp sciputchksum * trasmette checksum ************************************************************* cc_end rts ***************************************************************************** * Controlla se Š stato ricevuto almeno un byte; in caso positivo predispone * un timer software per controllare la ricezione dei byte rimanenti, * necessari per formare un pacchetto completo. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** cp_end rts checkpkt ldaa rxlen+1 beq cp_end * esce subito se nessun byte ricevuto ************************************************************* * Se arriva qui ha ricevuto almeno un byte. ************************************************************* ldaa oldrxlen * salta se non Š la prima volta che bne cp_notfirst * viene chiamata PER QUESTO PACCHETTO ************************************************************* * Codice eseguito solo alla ricezione del primo byte del * pacchetto. ************************************************************* jsr scipeek * legge la lunghezza del pacchetto, * SENZA AGGIORNARE il buffer staa rxpktsize * e la memorizza inc oldrxlen * aggiorna vecchia dimensione ************************************************************* * Predispone un timer software per controllare l'intervallo * di tempo che passa tra la ricezione di un carattere ed il * successivo. Poich‚ viene decrementato di una unit… ogni * 5ms, il timer viene azzerato dopo un intervallo di tempo * di 15ms. Se questa situazione capita mentre la ricezione * del pacchetto Š ancora in corso, viene prodotto un errore * di timeout, con conseguente trasmissione al PC di un * pacchetto informativo per segnalare la situazione. ************************************************************* cp_loadtimers ldaa #3 * 3 * 5ms = 15ms staa rxtimer * carica il contatore di timeout ************************************************************* * Aggiorna il timer software che controlla l'attivit… del * canale seriale. Viene decrementato ogni 5ms; se raggiunge * lo zero la connessione viene dichiarata caduta ed i motori * vengono fermati. ************************************************************* ldd #DEADTIME std deadtime rts * <=== IMPORTANTE ************************************************************* * Eseguita soltanto per tutti i caratteri successivi. * * Il timer software era gi… attivo perch‚ almeno un carattere * del pacchetto Š gi… stato ricevuto. Dunque verifica il * timer, controllando se ha raggiunto lo zero o un qualsiasi * valore negativo. In caso affermativo Š passato troppo * tempo dalla ricezione del carattere precedente e dunque * trasmette al PC un messaggio di errore di timeout. Il * timeout Š fissato in 15ms circa. ************************************************************* cp_notfirst ldaa rxtimer * carica contatore di timeout ble cp_timeout * salta se Š passato troppo tempo ************************************************************* * Se l'esecuzione arriva qui non Š passato troppo tempo, * quindi pu• procedere nell'acquisizione del pacchetto. * Prima controlla se c'Š qualcosa di nuovo, altrimenti esce * subito. ************************************************************* ldaa rxlen+1 * esce subito se non ha ricevuto cmpa oldrxlen * altro nell'intervallo beq cp_end staa oldrxlen * aggiorna vecchia dimensione ************************************************************* * Rinfresca i timer che controllano lo stato della * connessione, preservando il contenuto del registro A. ************************************************************* psha jsr cp_loadtimers pula ************************************************************* * Controlla se ha ricevuto tutti i byte necessari a formare * questo pacchetto. Esce subito se non sono abbastanza. ************************************************************* cmpa rxpktsize blt cp_end * salta se ricevuti pochi byte ************************************************************* * Se l'esecuzione raggiunge questo punto allora sono stati * ricevuti tutti i byte necessari a formare il pacchetto. * Perci• svolge un controllo di checksum su di essi, * mandando un messaggio di checksum errato al PC in caso di * fallimento. ************************************************************* ldab rxpktsize jsr dochecksum * calcola checksum bne cp_checksum * salta se errore di checksum ************************************************************* * A questo punto anche il checksum Š verificato; rimane solo * da chiamare la routine di gestione giusta. Prima azzera il * contatore che memorizza il numero di caratteri ricevuti, * a segnalare che il pacchetto Š stato gestito. ************************************************************* clr oldrxlen * segnala pacchetto gestito ************************************************************* jsr sciget * elimina lunghezza del pacchetto jsr sciget * A = id del pacchetto cmpa #ID_SETSPEED bne nosetspddir * salta se id != ID_SETSPEED jmp setspddir nosetspddir cmpa #ID_GETSPEED bne nogetspddir * salta se id != ID_GETSPEED jmp getspddir nogetspddir cmpa #ID_GETADC beq getadc * salta se id = ID_GETADC cmpa #ID_GETMOTORS beq getmotors * salta se id = ID_GETMOTORS cmpa #ID_SETMOTORS beq setmotors * salta se id = ID_SETMOTORS ************************************************************* * Se l'esecuzione arriva qui significa che il pacchetto Š * accettabile ma il suo identificatore non Š stato * riconosciuto. Manda un messaggio di errore al PC. ************************************************************* cp_badpkt ldaa #3 * lunghezza = 3 byte jsr sciputlen ldaa #ID_BADPKT * segnala cattivo pacchetto bra cp_senderror ************************************************************* * Se l'esecuzione arriva qui il pacchetto ha un problema di * checksum. Manda un messaggio di errore al PC. ************************************************************* cp_checksum ldaa #3 * lunghezza = 3 byte jsr sciputlen ldaa #ID_BADCHKSUM * segnala errore di checksum bra cp_senderror ************************************************************* * Se l'esecuzione arriva qui il pacchetto ha un problema di * timeout. Manda un messaggio di errore al PC. ************************************************************* cp_timeout ldaa #3 * lunghezza = 3 byte jsr sciputlen ldaa #ID_TIMEOUT * segnala errore di timeout ************************************************************* cp_senderror clr oldrxlen * segnala pacchetto gestito jsr sciput * manda il codice di errore sei clr rxin+1 * inizializza il buffer di ricezione clr rxout+1 clr rxlen+1 cli jmp sciputchksum * manda il checksum ***************************************************************************** * Fa partire la conversione sui quattro canali analogici ed al termine * spedisce i risultati al PC. * * ATTENZIONE: I primi due byte, lunghezza del pacchetto e codice di * identificazione, sono gi… stati letti, mentre il checksum Š stato * verificato ma deve ancora essere letto. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** getadc bclr ADCTL,x,#%10000000 * fa partire una conversione ************************************************************* jsr sciget * niente dati, butta il checksum ************************************************************* ldaa #7 jsr sciputlen * spedisce lunghezza pacchetto ldaa #ID_GETADC jsr sciput * spedisce id pacchetto ga_wait brclr ADCTL,x,#%10000000,ga_wait * attende conclusione ldaa ADR1,x jsr sciput * spedisce lettura ADR1 ldaa ADR2,x jsr sciput * spedisce lettura ADR2 ldaa ADR3,x jsr sciput * spedisce lettura ADR3 ldaa ADR4,x jsr sciput * spedisce lettura ADR4 jmp sciputchksum * spedisce checksum del pacchetto ***************************************************************************** * Legge lo stato delle linee di ENABLE# dei tre motori. Il pacchetto * trasmesso al PC contiene un solo byte di dati, i cui tre bit meno * significativi sono lo stato delle tre linee. Se un certo bit Š nullo il * rispettivo motore Š alimentato; viceversa, il valore Š 1 se il motore Š * spento. * * ATTENZIONE: I primi due byte, lunghezza del pacchetto e codice di * identificazione, sono gi… stati letti, mentre il checksum Š stato * verificato ma deve ancora essere letto. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** getmotors jsr sciget * niente dati, butta il checksum ************************************************************* ldaa #4 jsr sciputlen * spedisce lunghezza pacchetto ldaa #ID_GETMOTORS jsr sciput * spedisce id pacchetto ldaa PORTC,x * legge lo stato dei motori anda #%00000111 * |||||||| * |||||||+- ENABLE# M1 * ||||||+-- ENABLE# M2 * |||||+--- ENABLE# M3 * ||||+---- CK2 M1 * |||+----- CK2 M2 * ||+------ CK2 M3 * |+------- REFIN M1, M2 e M3 * +-------- libero jsr sciput * spedisce stato dei motori jmp sciputchksum * spedisce checksum del pacchetto ***************************************************************************** * Alimenta o spegne i motori, in funzione del valore dell'unico byte di dati * immesso nel pacchetto. Ciascuno dei suoi tre bit meno significativi * identifica uno dei tre motori. Se un certo bit Š nullo il rispettivo * motore verr… alimentato; viceversa, il valore Š 1 se il motore dovr… essere * spento. * * ATTENZIONE: I primi due byte, lunghezza del pacchetto e codice di * identificazione, sono gi… stati letti, mentre il checksum Š stato * verificato ma deve ancora essere letto. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** setmotors jsr sciget * legge il byte di dati anda #%00000111 * tiene solo i bit bassi staa temp * salva per dopo ldaa PORTC,x * legge lo stato dei motori anda #%11111000 * tiene solo i bit alti * |||||||| * |||||||+- ENABLE# M1 * ||||||+-- ENABLE# M2 * |||||+--- ENABLE# M3 * ||||+---- CK2 M1 * |||+----- CK2 M2 * ||+------ CK2 M3 * |+------- REFIN M1, M2 e M3 * +-------- libero oraa temp * fonde con nuove impostazioni staa PORTC,x * scrive nuovo stato dei motori ************************************************************* jsr sciget * butta il checksum ************************************************************* ldaa #3 jsr sciputlen * trasmette dimensione pacchetto ldaa #ID_SETMOTORS jsr sciput * trasmette conferma ricezione jmp sciputchksum * trasmette checksum ***************************************************************************** * Spedisce al PC i periodi correnti dei motori e le loro direzioni. * * ATTENZIONE: I primi due byte, lunghezza del pacchetto e codice di * identificazione, sono gi… stati letti, mentre il checksum Š stato * verificato ma deve ancora essere letto. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** getspddir jsr sciget * niente dati, butta il checksum ************************************************************* ldaa #10 jsr sciputlen * trasmette lunghezza pacchetto ldaa #ID_GETSPD jsr sciput * trasmette id pacchetto ************************************************************* ldaa per_oc2 * high periodo OC2 jsr sciput ldaa per_oc2+1 * low periodo OC2 jsr sciput ************************************************************* ldaa per_oc3 * high periodo OC3 jsr sciput ldaa per_oc3+1 * low periodo OC3 jsr sciput ************************************************************* ldaa per_oc4 * high periodo OC4 jsr sciput ldaa per_oc4+1 * low periodo OC4 jsr sciput ************************************************************* ldaa dir * direzione corrente jsr sciput jmp sciputchksum * spedisce checksum del pacchetto ***************************************************************************** * Forza la locazione di memoria newp_oc2,y ad assumere un valore compreso * tra PERMIN e STOP (ovvero PERMAX + 1). * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** safetycheck ldd newp_oc2,y cpd #PERMIN bhs sc_leftok * salta se >= PERMIN ldd #PERMIN * fissa a PERMIN bra sc_rightok sc_leftok cpd #STOP bls sc_rightok * salta se <= STOP ldd #STOP * fissa a STOP sc_rightok std newp_oc2,y rts ***************************************************************************** * Questa routine processa un pacchetto di tipo impostazione velocit… e * direzione, che Š gi… stato verificato per quanto riguarda errori di * timeout o di checksum e dunque pu• essere considerato valido. * * ATTENZIONE: I primi due byte, lunghezza del pacchetto e codice di * identificazione, sono gi… stati letti, mentre il checksum Š stato * verificato ma deve ancora essere letto. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** setspddir jsr sciget * high periodo OC2 staa newp_oc2 * salva parte alta jsr sciget * low periodo OC2 staa newp_oc2+1 * salva parte bassa ************************************************************* jsr sciget * high periodo OC3 staa newp_oc3 * salva parte alta jsr sciget * low periodo OC3 staa newp_oc3+1 * salva parte bassa ************************************************************* jsr sciget * high periodo OC4 staa newp_oc4 * salva parte alta jsr sciget * low periodo OC4 staa newp_oc4+1 * salva parte bassa ************************************************************* jsr sciget * direzione per OC2, OC3 e OC4 staa newdir * salva nuova direzione ************************************************************* jsr sciget * butta il checksum ************************************************************* ldy #0 jsr safetycheck * controlla newp_oc2 ldy #4 jsr safetycheck * controlla newp_oc3 ldy #8 jsr safetycheck * controlla newp_oc4 ************************************************************* * Commentare le linee seguenti se non si vuole un pacchetto * di risposta dal 68HC11. Ricordarsi di aggiungere una * istruzione RTS per ritornare il controllo al chiamante. ************************************************************* ldaa #3 jsr sciputlen * trasmette dimensione pacchetto ldaa #ID_SETSPEED jsr sciput * trasmette conferma ricezione jmp sciputchksum * trasmette checksum ***************************************************************************** * Calcola il checksum a 8 bit e lo controlla. Per come Š stato definito il * formato del pacchetto, basta verificare che la somma di tutti i suoi byte * sia nulla. La routine NON ALTERA il contenuto del buffer di ricezione. * * Input: B - numero di byte di cui Š composto il pacchetto * Output: nessuno * Registri alterati: B - Š sovrascritto * Z - Š attivo se checksum corretto, altrimenti spento * sono ignoti gli effetti sugli altri registri ***************************************************************************** dochecksum orab #0 beq dcs_end * salta se pacchetto vuoto ************************************************************* clra * azzera accumulatore ldy rxout * carica l'indice del primo elemento ************************************************************* * ciclo che viene eseguito per ciascun byte ************************************************************* dcs_loop adda rxbuf,y * legge un byte dal buffer e lo somma iny * incrementa indice cpy #rxsize * confronta con dimensione buffer blt dcs_nowrap * salta se non Š a fine buffer ldy #0 * riparte dalla prima cella dcs_nowrap decb bne dcs_loop * elabora tutti i byte del pacchetto ************************************************************* oraa #0 * imposta correttamente il flag Z dcs_end rts ***************************************************************************** * Interrupt service routine per il canale seriale. * * Input: nessuno * Output: nessuno * Registri alterati: nessuno ***************************************************************************** isr_sci brclr SCSR,x,#$80,no_tdre ************************************************************* * L'istruzione precedente salta se il flag TDRE (transmit * data register empty) non Š attivo nello status register. ************************************************************* ldaa txlen+1 * legge parte bassa della dimensione beq is_stop * salta se buffer trasmissione vuoto ldy txout * carica indice ldaa txbuf,y * estrae il byte dal buffer staa SCDR,x * trasmette il byte, azzerando * contemporaneamente il flag iny * incrementa indice cpy #txsize blt is_nowrap * salta se non Š a fine buffer ldy #0 * riparte dalla prima cella is_nowrap sty txout * aggiorna indice dec txlen+1 * aggiorna parte bassa dimensione bne no_tdre * salta se ci sono altri byte is_stop ldaa #%00101100 * disattiva interrupt di trasmissione staa SCCR2,x * nel control register 2 ************************************************************* no_tdre brclr SCSR,x,#$20,no_rdrf ************************************************************* * L'istruzione precedente salta se il flag RDRF (receive * data register full) non Š attivo nello status register. ************************************************************* ldaa SCDR,x * legge il byte appena ricevuto ************************************************************* ldab SCSR,x * carica SCI status register andb #%00001110 bne no_rdrf * errore di ricezione, scarta il byte ************************************************************* ldy rxlen * legge dimensione del buffer cpy #rxsize * confronta con dimensione massima bge no_rdrf * buffer pieno, scarta il byte ************************************************************* ldy rxin * carica indice staa rxbuf,y * memorizza il byte nel buffer iny * incrementa indice cpy #rxsize blt is1_nowrap * salta se non Š a fine buffer ldy #0 * riparte dalla prima cella ************************************************************* is1_nowrap sty rxin * aggiorna indice inc rxlen+1 * aggiorna parte bassa dimensione ************************************************************* no_rdrf rti ***************************************************************************** * Ritorna il primo byte presente nel buffer di ricezione, ma SENZA ESTRARLO. * * Input: nessuno * Output: A - valore del byte * Registri alterati: ignoti ***************************************************************************** scipeek ldy rxout * carica indice ldaa rxbuf,y * estrae il byte dal buffer rts ***************************************************************************** * Questa routine estrae un byte dal buffer circolare di ricezione del canale * seriale e lo mette nell'accumulatore. Se non vi Š alcun byte da ricevere, * l'accumulatore conterr… spazzatura. * * Input: nessuno * Output: A - byte ricevuto * Registri alterati: ignoti ***************************************************************************** sciget ldy rxlen * legge dimensione del buffer beq sg_end * buffer vuoto, torna subito ************************************************************* ldy rxout * carica indice ldaa rxbuf,y * estrae il byte dal buffer iny * incrementa indice cpy #rxsize blt sg_nowrap * salta se non Š a fine buffer ldy #0 * riparte dalla prima cella ************************************************************* sg_nowrap sty rxout * aggiorna indice dec rxlen+1 * aggiorna parte bassa dimensione ************************************************************* sg_end rts ***************************************************************************** * Questa routine trasmette un byte sul canale seriale, inserendolo nel buffer * circolare di trasmissione. Mette il byte passato nel buffer ed attiva il * trasmettitore della porta seriale; mantiene aggiornato il calcolo del * checksum. Se il buffer Š pieno il byte viene definitivamente perso. * * Input: A - byte da trasmettere * Output: nessuno * Registri alterati: ignoti ***************************************************************************** sciput ldy txlen * legge dimensione del buffer cpy #txsize * confronta con dimensione massima bge sp_end * buffer pieno, scarta il byte ************************************************************* tab addb txchksum * aggiorna il running checksum stab txchksum ************************************************************* ldy txin * carica indice staa txbuf,y * memorizza il byte nel buffer iny * incrementa indice cpy #txsize blt sp_nowrap * salta se non Š a fine buffer ldy #0 * riparte dalla prima cella ************************************************************* sp_nowrap sty txin * aggiorna indice inc txlen+1 * aggiorna parte bassa dimensione ldaa #%10101100 * attiva interrupt di trasmissione staa SCCR2,x * control register 2 ************************************************************* sp_end rts ***************************************************************************** * Trasmette il primo byte di un pacchetto (lunghezza). Inizializza il * checksum. * * Input: A - lunghezza del pacchetto * Output: nessuno * Registri alterati: ignoti ***************************************************************************** sciputlen clr txchksum * inizializza running checksum jmp sciput ***************************************************************************** * Trasmette l'ultimo byte di un pacchetto (checksum complementato). * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** sciputchksum ldaa txchksum * legge il running checksum nega * complemento a due jmp sciput * trasmette ***************************************************************************** * Inizializza il canale seriale; da chiamare una sola volta alla partenza * del programma. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** initsci ldaa #$ff staa DDRD,x * setta la portd in uscita ldaa #$00 staa PORTD,x * zero ************************************************************* ldaa #%00000000 staa SPCR,x * portd in push-pull ldaa #%00110000 staa BAUD,x * 9600bps, 1 stop bit, no parity ldaa #%00000000 staa SCCR1,x * control register 1 ldaa #%00101100 staa SCCR2,x * control register 2 ************************************************************* clr oldrxlen clr rxin * inizializza buffer di ricezione clr rxin+1 * parte bassa clr rxout * parte alta clr rxout+1 * parte bassa clr rxlen * parte alta clr rxlen+1 * parte bassa ************************************************************* clr txin * inizializza buffer di trasmissione clr txin+1 * parte bassa clr txout * parte alta clr txout+1 * parte bassa clr txlen * parte alta clr txlen+1 * parte bassa rts ***************************************************************************** * Inizializza le porte che non hanno a che fare con gli output compare o con * il canale seriale; da chiamare una sola volta alla partenza del programma. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** initports ldaa #%00000111 * |||||||| * |||||||+- CW/CCW# M1 * ||||||+-- CW/CCW# M2 * |||||+--- CW/CCW# M3 * ||||+---- libero * |||+----- libero * ||+------ libero * |+------- libero * +-------- libero staa PORTB,x staa dir * sincronizza la copia in memoria ************************************************************* ldaa #$ff staa DDRC,x * setta la portc in uscita ldaa #%00111000 * |||||||| * |||||||+- ENABLE# M1 * ||||||+-- ENABLE# M2 * |||||+--- ENABLE# M3 * ||||+---- CK2 M1 * |||+----- CK2 M2 * ||+------ CK2 M3 * |+------- REFIN M1, M2 e M3 * +-------- libero staa PORTC,x rts ***************************************************************************** * Interrupt service routine per l'uscita OC2. Per minimizzare la durata * della routine, eventuali aggiunte NON vanno fatte qui. * * Input: nessuno * Output: nessuno * Registri alterati: nessuno ***************************************************************************** isr_oc2 ldd per_oc2 addd TOC2,x * aggiorna il contatore std TOC2,x ldaa #$40 * azzera il flag relativo a OC2 staa TFLG1,x rti ***************************************************************************** * Interrupt service routine per l'uscita OC3. Per minimizzare la durata * della routine, eventuali aggiunte NON vanno fatte qui. * * Input: nessuno * Output: nessuno * Registri alterati: nessuno ***************************************************************************** isr_oc3 ldd per_oc3 * aggiorna il contatore addd TOC3,x std TOC3,x ldaa #$20 * azzera il flag relativo a OC3 staa TFLG1,x rti ***************************************************************************** * Interrupt service routine per l'uscita OC4. Per minimizzare la durata * della routine, eventuali aggiunte NON vanno fatte qui. * * Input: nessuno * Output: nessuno * Registri alterati: nessuno ***************************************************************************** isr_oc4 ldd per_oc4 * aggiorna il contatore addd TOC4,x std TOC4,x ldaa #$10 * azzera il flag relativo a OC4 staa TFLG1,x rti ***************************************************************************** * Inizializza i timer hardware per OC2, OC3 e OC4; da chiamare una sola * volta al reset del programma. Inizializza anche il timer OC5, utilizzato * per• esclusivamente in polling mode. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** initimer ldd #STOP * setta i timer al massimo periodo std per_oc2 * possibile + 1 per indicare motori std per_oc3 * fermi std per_oc4 std newp_oc2 * aggiorna anche le tre copie std newp_oc3 std newp_oc4 ************************************************************* ldaa #%00000001 * OM2:OL2 = 0:0 => spento * OM3:OL3 = 0:0 => spento * OM4:OL4 = 0:0 => spento * OM5:OL5 = 0:1 => toggle output staa TCTL1,x * init OC2, OC3, OC4 and OC5 ldaa #%00000000 staa TMSK1,x * disable OC2, OC3, OC4 and OC5 ints staa TFLG1,x * clear any pending OC2, OC3, OC4 and * OC5 interrupt ************************************************************* ldd TOC5,x addd #10000 std TOC5,x * carica 5ms su OC5 ************************************************************* ldd #DEADTIME std deadtime rts ***************************************************************************** * Inizializza ed accende il convertitore analogico-digitale in modo MULT. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** initadc bset OPTION,x,#%10000000 * alimenta l'ADC ldaa #%00010000 * seleziona modo MULT sui 4 canali staa ADCTL,x rts ***************************************************************************** * Questa routine va chiamata ogni volta che i periodi dei segnali di clock * dei motori oppure le loro direzioni sono stati appena cambiati. Verifica * se Š necessario attivare o disattivare i timer che pilotano i segnali CK1 * dei motori. La routine controlla anche i segnali CW/CCW# e REFIN. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** checkperiodi ldy #0 * conterr… il numero di motori in funzione ************************************************************* ldd per_oc2 cpd #PERMAX bhi no_oc2 * ferma OC2 ? iny bset TCTL1,x,#%01000000 * fa partire OC2 bset TMSK1,x,#%01000000 bra end_oc2 no_oc2 bclr TCTL1,x,#%11000000 * ferma OC2 bclr TMSK1,x,#%01000000 ************************************************************* end_oc2 ldd per_oc3 cpd #PERMAX bhi no_oc3 * ferma OC3 ? iny bset TCTL1,x,#%00010000 * fa partire OC3 bset TMSK1,x,#%00100000 bra end_oc3 no_oc3 bclr TCTL1,x,#%00110000 * ferma OC3 bclr TMSK1,x,#%00100000 ************************************************************* end_oc3 ldd per_oc4 cpd #PERMAX bhi no_oc4 * ferma OC4 ? iny bset TCTL1,x,#%00000100 * fa partire OC4 bset TMSK1,x,#%00010000 bra end_oc4 no_oc4 bclr TCTL1,x,#%00001100 bclr TMSK1,x,#%00010000 ************************************************************* end_oc4 ldaa dir * aggiorna i segnali staa PORTB,x * CW/CCW# dei motori ************************************************************* * Se i periodi sono tutti nulli allora i motori sono tutti * fermi, perci• abbassa la linea REFIN per selezionare bassa * corrente. Viceversa, se almeno uno dei motori Š in * funzione, alza la linea REFIN per alimentare TUTTI i tre * motori con la massima corrente. Alimentiamo con la * massima corrente anche i motori fermi per evitare rotazioni * indesiderate dovute al trascinamento da parte dei motori * in funzione. ************************************************************* cpy #0 beq off_refin * salta se motori spenti bset PORTC,x,#%01000000 * predispone corrente alta rts off_refin bclr PORTC,x,#%01000000 * predispone corrente bassa rts ***************************************************************************** * SEGUONO VECCHIE ROUTINE, PERFETTAMENTE FUNZIONANTI MA NON PIU' UTILIZZATE ***************************************************************************** *cp_end rts * *checkpkt ldaa rxlen+1 * carica byte basso * beq cp_end * salta se non ha ricevuto niente * * ************************************************************* * * attende l'arrivo di tutti i byte del pacchetto * ************************************************************* * * ldab #10 * attende 10 byte * jsr waitpkt * bhi cp_timeout * salta se manca qualcosa * * ************************************************************* * * controlla il pacchetto * ************************************************************* * * jsr pktcheck * calcola checksum * beq procpkt * pacchetto corretto, lo processa * * ************************************************************* * * gestisce errore di checksum * ************************************************************* * *cp_checksum ldaa #'C * segnala cattivo checksum * bra cp_errore * * ************************************************************* * * gestisce errore di timeout * ************************************************************* * *cp_timeout ldaa #'T * segnala pacchetto in timeout * * ************************************************************* * *cp_errore sei * svuota il buffer di ricezione * clr rxin+1 * clr rxout+1 * clr rxlen+1 * cli * jmp sciput * manda codice di errore ***************************************************************************** * Attende un pacchetto composto da B bytes, ciascuno dei quali entro un * certo timeout. * * Input: B - numero di byte da attendere * Output: nessuno * Registri alterati: CCR - secondo il risultato del confronto * sono ignoti gli effetti sugli altri registri ***************************************************************************** *waitpkt cmpb rxlen+1 * confronta con il byte basso * bls wp_end * salta se ricevuti abbastanza byte * * ************************************************************* * * ldaa rxlen+1 * legge numero di byte ricevuti * ldy #2000 * setta contatore di timeout * * ************************************************************* * * NOTA: il loop seguente impiega 13 cicli per essere * * eseguito, equivalenti a 13 * 0.5ns = 6.5us con un clock di * * sistema a 8MHz. Siccome pu• essere eseguito al pi— 2000 * * volte, il tempo massimo impiegato Š esattamente di * * 2000 * 6.5us = 13ms, corrispondente al ritardo massimo * * ammissibile prima di dichiarare una situazione di timeout. * ************************************************************* * *wp_loop cmpa rxlen+1 * 3 * bne waitpkt * 3 - salta se ha ricevuto altro * dey * 4 * bne wp_loop * 3 - salta se non Š in timeout * * ************************************************************* * *wp_end cmpb rxlen+1 * confronta con il byte basso * rts ***************************************************************************** * Genera alcuni segnali di prova per mostrare al mondo esterno (cio‚ noi) * che il micro sta lavorando correttamente. * * Input: nessuno * Output: nessuno * Registri alterati: ignoti ***************************************************************************** *flip_portc ldaa PORTC,x * eora #$ff * staa PORTC,x * ldaa #$55 * manda un pattern di bit alternati * staa PORTC,x * nop * ldaa #$aa * inverte * staa PORTC,x * rts ***************************************************************************** end