Sommario:

Tutorial Assembler AVR 7: 12 passaggi
Tutorial Assembler AVR 7: 12 passaggi

Video: Tutorial Assembler AVR 7: 12 passaggi

Video: Tutorial Assembler AVR 7: 12 passaggi
Video: AVR Ассемблер. Урок 2. Порты. Мигалка. AVR Assembler. Lesson 2. Ports. Flasher. 2024, Dicembre
Anonim
Tutorial assemblatore AVR 7
Tutorial assemblatore AVR 7

Benvenuto nell'esercitazione 7!

Oggi mostreremo prima come eseguire lo scavenging di una tastiera, quindi come utilizzare le porte di ingresso analogico per comunicare con la tastiera. Lo faremo utilizzando gli interrupt e un singolo filo come input. Collegheremo la tastiera in modo che ogni pressione di un tasto invii una tensione univoca all'ingresso analogico che ci consentirà di distinguere in base alla tensione quale tasto è stato premuto. Quindi forniremo il numero premuto al nostro analizzatore di registro per mostrare che tutto sta accadendo come dovrebbe. Ci sono una serie di insidie in cui puoi incorrere quando usi il convertitore analogico-digitale (ADC) nell'ATmega328p e quindi lo faremo prendi le cose in poche fasi sulla strada per cercare di capire come evitarle. Vedremo anche perché l'utilizzo del convertitore da analogico a digitale non è il modo migliore per controllare una tastiera anche se utilizza meno porte sul microcontrollore. In questo tutorial avrai bisogno di:

  1. una tastiera. Puoi comprarne uno o puoi fare quello che ho fatto io e recuperarne uno.
  2. 2 intestazioni femminili per la tastiera (se ne stai cercando uno)
  3. cavi di collegamento
  4. una breadboard
  5. 4 resistenze da 1 Kohm
  6. 1 resistenza da 15 Kohm
  7. 1 resistenza da 3,3 Kohm
  8. 1 resistenza da 180 ohm
  9. 1 resistenza da 680 ohm
  10. un multimetro digitale
  11. il tuo analizzatore dal Tutorial 5

Potresti voler saltare i primi passaggi se hai già una tastiera e non hai bisogno di recuperarne una.

Ecco un link alla raccolta completa dei miei tutorial sull'assemblatore AVR:

Passaggio 1: pulire una tastiera 1

Cerca una tastiera 1
Cerca una tastiera 1
Cerca una tastiera 1
Cerca una tastiera 1
Cerca una tastiera 1
Cerca una tastiera 1
Cerca una tastiera 1
Cerca una tastiera 1

Molto tempo fa, quando anche i tuoi nonni erano semplici bambini, le persone usavano questi strani dispositivi, che avevano lunghi cavi collegati al muro, per comunicare tra loro. Si chiamavano "telefoni" e di solito erano oggetti di plastica a buon mercato che emettevano un suono fastidioso quando qualcuno ti chiamava (non che le suonerie di "Justin Bieber" di oggi non siano ugualmente fastidiose). In ogni caso, questi dispositivi avevano tastiere che erano cablate in modo molto semplice e quindi sono facili da recuperare e hanno 2 tasti extra ("ricomposizione" e "flash") dalle tastiere che puoi acquistare che potresti voler riutilizzare come "tasti freccia", "tasti menu" o qualcos'altro. Quindi inizieremo recuperando una tastiera da un vecchio telefono. Per prima cosa prendi il telefono (io ne uso uno GE come mostrato nelle immagini) e fai leva per rivelare il cablaggio. Quindi prendi uno scalpello e stacca le piccole manopole di plastica che tengono la tastiera e rimuovi la tastiera.

Passaggio 2: pulire una tastiera 2

Cerca una tastiera 2
Cerca una tastiera 2
Cerca una tastiera 2
Cerca una tastiera 2
Cerca una tastiera 2
Cerca una tastiera 2

Ora prendi una sega per PVC e taglia la plastica intorno ai fori della serratura e poi taglia intorno al bordo per ottenere la profondità giusta lasciando una tastiera sottile.

Quindi rimetti la tastiera usando i piccoli pioli che rimangono dopo averne ritagliato la parte superiore nell'ultimo passaggio e usa un saldatore per infilare semplicemente il ferro caldo in ciascun foro del piolo che fonderà la plastica e la spargerà sul parte inferiore della tastiera formando nuove "manopole" che manterranno la tastiera in posizione come prima.

Mi piace recuperare i tre altoparlanti e forse le altre cose come gli interruttori e quant'altro che sono sulla scheda. Tuttavia, questa volta non andrò a cercare gli interruttori e cose del genere perché abbiamo altri obiettivi al momento. Inoltre, c'è un IC lineare TA31002 che è una suoneria del telefono. La scheda tecnica è facilmente reperibile e scaricabile online fornendo la piedinatura e le caratteristiche. Quindi lo lascerò saldato alla scheda per ora e poi ci giocherò più tardi. Vorrei collegarlo a un oscilloscopio e vedere quali segnali interessanti posso ottenere da esso. Forse anche farne un campanello. Chi lo sa.

Ad ogni modo, una volta che avrai finito di distruggere il telefono e di recuperare le parti, finiremo di realizzare la nostra tastiera.

Passaggio 3: pulire una tastiera 3

Cerca una tastiera 3
Cerca una tastiera 3
Cerca una tastiera 3
Cerca una tastiera 3
Cerca una tastiera 3
Cerca una tastiera 3

Usa uno stoppino dissaldante e rimuovi i cavi a nastro dalla parte inferiore della tastiera assicurandoti che i fori nella scheda del circuito siano liberi e quindi collega due intestazioni femmina sulla scheda dove si trovano i fori. Probabilmente dovrai ritagliare le tue intestazioni in modo che siano intestazioni a 4 pin.

Ora che le intestazioni sono collegate, puoi collegarlo a una breadboard, prendere un multimetro e testare i tasti attaccando il multimetro su pin casuali e misurando la resistenza. Questo ti permetterà di mappare le chiavi. È difficile vedere come sono collegati i tasti alle uscite guardando il circuito, ma se usi un multimetro puoi collegarlo a due pin qualsiasi e quindi premere i pulsanti finché non vedi un numero sullo schermo invece di un circuito aperto. Questo sarà il pinout per quella chiave.

Mappa tutte le chiavi sui pin di output in questo modo.

Passaggio 4: cablare la tastiera

Cablare la tastiera
Cablare la tastiera
Cablare la tastiera
Cablare la tastiera

Ora segui lo schema elettrico e collega la tastiera alla breadboard.

Come funzionerà, metteremo 5V sul lato sinistro e il lato destro andrà su GND. Il primo pin a destra nel diagramma va nel primo dei nostri pin analogici sul microcontrollore Atmega328p. Quando non ci sono pulsanti premuti, il segnale sarà 0V e quando si preme ciascuno dei diversi pulsanti l'ingresso alla porta analogica sarà compreso tra 0V e 5V con una quantità diversa a seconda del tasto premuto. Abbiamo scelto i valori dei resistori in modo che ogni percorso contenesse una resistenza diversa dal resto. La porta analogica del microcontrollore prende un segnale analogico e lo divide in 1024 diversi canali tra 0V e 5V. Ciò significa che ogni canale ha una larghezza 5V/1024 = 0,005 V/canale = 5 mV/canale. Quindi la porta analogica può distinguere le tensioni di ingresso purché differiscano di oltre 5 mV. Nel nostro caso abbiamo scelto i valori del resistore in modo che due qualsiasi pressione di un tasto invii un segnale di tensione che differisce di più di questo, quindi il microcontrollore dovrebbe essere in grado di decidere facilmente quale tasto è stato premuto. Il grosso problema è che l'intero sistema è molto rumoroso, quindi dovremo scegliere una gamma di voltaggi da mappare a ogni pressione di un pulsante, ma ne parleremo un po' più tardi.

Si noti che siamo in grado di controllare una tastiera a 14 pulsanti utilizzando solo una singola linea di ingresso al controller. Questo è uno degli aspetti utili degli ingressi analogici.

Ora il nostro primo tentativo di controllare la tastiera sarà di fare in modo che la pressione di un tasto causi un'interruzione, la subroutine di interruzione leggerà la porta di ingresso analogico e deciderà quale tasto è stato premuto, quindi emetterà quel numero alla nostra subroutine dell'analizzatore di registro che visualizzerà il valore chiave in binario sui nostri 8 LED che abbiamo impostato nel Tutorial 5.

Passaggio 5: collegare la tastiera all'analizzatore

Collega la tastiera al tuo analizzatore
Collega la tastiera al tuo analizzatore
Collega la tastiera al tuo analizzatore
Collega la tastiera al tuo analizzatore

Le immagini mostrano come vogliamo collegare la tastiera al microcontrollore in modo da poter vedere l'output sul display del nostro analizzatore. In sostanza, colleghiamo semplicemente l'uscita dalla tastiera al pin 0 di PortC, chiamato anche ADC0 sull'ATmega328P.

Tuttavia, ci sono un paio di cose aggiuntive. Inoltre, collegheremo un pulsante a PD2. Cioè. prendi un filo dalla tua guida 5V a un pulsante e dall'altro lato del pulsante a PD2, e infine, vogliamo scollegare il pin AREF dalla nostra guida 5V e invece lasciarlo scollegato. Se volessimo, potremmo inserire un condensatore di disaccoppiamento da 0,1 microfarad. Questo è un condensatore ceramico con scritto 104 sopra. Le prime due cifre sono il numero e l'ultima cifra è la potenza di 10 la moltiplichiamo per ottenere una risposta in picofarad (pico significa 10^-12), quindi 104 significa 10 x 10^4 picofarad, che è lo stesso di 100 nanofarad (nano significa 10^-9), che equivale a 0,1 microfarad (micro significa 10^-6). Ad ogni modo, tutto ciò che fa è stabilizzare il pin AREF quando possiamo usarlo come pin di riferimento.

Vogliamo anche un resistore da 1 Mohm tra PD2 e massa. Imposteremo PD2 come pin di uscita a 0V e attiveremo su un fronte positivo su quel pin. Vogliamo che il bordo scompaia immediatamente quando rilasciamo il pulsante, quindi inseriremo questo resistore "pull down".

Il motivo per cui vogliamo il pulsante è perché vogliamo attivare il nostro convertitore analogico-digitale dal pin INT0 sul chip, che è anche PD2. Alla fine vorremmo che la pressione del tasto attivasse l'ADC e fornisse anche l'input da convertire senza avere un pulsante separato, ma a causa del modo in cui funziona la temporizzazione inizieremo con un pulsante separato per attivare l'ADC e una volta stirato tutto i bug e siamo fiduciosi che tutto funzioni correttamente, quindi affronteremo i problemi di rumore e temporizzazione che derivano dall'attivazione dalla stessa pressione del pulsante che vogliamo leggere.

Quindi, per ora, il modo in cui funziona è tenere premuto un tasto, quindi premere il pulsante per attivare l'ADC, quindi rilasciare e si spera che il valore binario del pulsante che abbiamo premuto venga visualizzato sull'analizzatore.

Quindi scriviamo del codice che lo realizzi.

Passaggio 6: quali interruttori a levetta dovremmo impostare?

Quali interruttori a levetta dovremmo impostare?
Quali interruttori a levetta dovremmo impostare?

Pensiamo prima a come lo codificheremo in modo che il controller possa leggere l'input dalla tastiera e trasformarlo in un valore numerico corrispondente al pulsante che è stato premuto. Utilizzeremo il convertitore analogico-digitale (ADC) integrato nell'Atmega328p. Useremo AREF come tensione di riferimento e la nostra uscita della tastiera sarà collegata a PortC0 o PC0. Nota che questo pin è anche chiamato ADC0 per il convertitore analogico-digitale 0. Potrebbe essere una buona idea leggere la Sezione 12.4 sugli interrupt per l'ATmega328P e anche il capitolo 24 sul convertitore analogico-digitale prima di arrivare avviato o almeno avere quelle sezioni pronte per il riferimento. Per impostare il microcontrollore in modo che sappia cosa fare con un segnale di ingresso analogico e come interagire con il nostro programma, dobbiamo prima impostare alcuni dei vari ADC relativi bit di registro. Questi sono essenzialmente equivalenti ai vecchi interruttori a levetta sui primi computer. O accendi o spegni un interruttore, o anche più indietro colleghi i cavi tra una presa e l'altra in modo che gli elettroni che raggiungono quel bivio trovino un cancello chiuso e un altro aperto costringendolo a percorrere un percorso diverso nel labirinto di circuiti e quindi svolgere un compito logico diverso. Quando si codifica in linguaggio assembly, abbiamo un accesso ravvicinato a queste funzioni del microcontrollore che è una delle cose interessanti del farlo in primo luogo. È più "hands on" e molto meno accade "dietro le quinte" per così dire. Quindi non pensare di impostare questi registri come un compito noioso. Questo è ciò che rende interessante il linguaggio assembly! Stiamo acquisendo una relazione molto personale con il funzionamento interno e la logica del chip e stiamo facendo in modo che faccia esattamente ciò che vogliamo: né più né meno. Nessun ciclo di clock sprecato. Quindi ecco un elenco degli interruttori che dobbiamo impostare:

  1. Disattivare il bit Power Reduction ADC, PRADC, che è il bit 0 del registro PRR, poiché se questo bit è attivo spegnerà l'ADC. Il registro di riduzione della potenza è essenzialmente un modo per spegnere varie cose che usano energia quando non ne hai bisogno. Poiché stiamo utilizzando l'ADC, vogliamo assicurarci che non sia disabilitato in questo modo. (Vedi PRADC a pagina 46)
  2. Selezionare il canale di ingresso analogico come ADC0 disattivando MUX3…0 nel registro ADC Multiplexer Selection (ADMUX) (vedere tabella 24-4 pagina 249) questi sono già disattivati per impostazione predefinita, quindi non è necessario farlo. Tuttavia, lo sto includendo poiché se mai usi una porta diversa da ADC0 dovrai attivare questi interruttori di conseguenza. Varie combinazioni di MUX3, MUX2, MUX1, MUX0 ti consentono di utilizzare una qualsiasi delle porte analogiche come input e puoi anche cambiarle al volo se vuoi guardare un mucchio di diversi segnali analogici contemporaneamente.
  3. Disattiva i bit REFS0 e REFS1 nel registro ADMUX in modo da utilizzare AREF come tensione di riferimento anziché come riferimento interno (vedi pagina 248).
  4. Attiva il bit ADLAR in ADMUX in modo che il risultato sia "regolato a sinistra" discuteremo di questa scelta nel passaggio successivo.
  5. Impostare il bit ADC0D nel registro di disabilitazione dell'ingresso digitale (DIDR0) per disattivare l'ingresso digitale su PC0. Stiamo usando quella porta per l'ingresso analogico, quindi potremmo anche disabilitare l'ingresso digitale per questo.
  6. Impostare ISC0 e ISC1 nell'External Interrupt Control Register A (EICRA) per indicare che si desidera eseguire il trigger sul fronte di salita di un segnale di tensione al pin INT0 (PD2), vedere pagina 71.
  7. Cancellare i bit INT0 e INT1 nell'External Interrupt Mask Register (EIMSK) per indicare che non stiamo usando gli interrupt su questo pin. Se dovessimo abilitare gli interrupt su questo pin avremmo bisogno di un gestore di interrupt all'indirizzo 0x0002 ma invece lo stiamo impostando in modo che un segnale su questo pin attivi la conversione ADC, il cui completamento è gestito dall'interruzione completa conversione ADC a indirizzo 0x002A. Vedi pagina 72.
  8. Impostare il bit di abilitazione ADC (ADEN) (bit 7) nel controllo ADC e nel registro di stato A (ADCSRA) per abilitare l'ADC. Vedi pagina 249.
  9. Potremmo avviare una singola conversione impostando il bit di inizio conversione dell'ADC (ADSC) ogni volta che volessimo leggere il segnale analogico, tuttavia, per ora preferiremmo farlo leggere automaticamente ogni volta che qualcuno preme il pulsante, quindi abiliteremo l'ADC Bit di abilitazione dell'attivazione automatica (ADATE) nel registro ADCSRA in modo che l'attivazione venga eseguita automaticamente.
  10. Abbiamo anche impostato i bit ADPS2..0 (i bit AD Prescalar) su 111 in modo che l'orologio ADC sia l'orologio della CPU diviso per un fattore di 128.
  11. Selezioneremo la sorgente dell'attivazione dell'ADC come PD2 che è anche chiamata INT0 (External Interrupt Request 0). Lo facciamo alternando i vari bit nel registro ADCSRB (vedi Tabella 24-6 a pagina 251). Dalla tabella vediamo che vogliamo ADTS0 disattivato, ADTS1 attivato e ADTS2 disattivato in modo che l'ADC si attivi su quel pin. Nota se volessimo campionare continuamente la porta analogica come se stessimo leggendo un segnale analogico continuo (come il campionamento del suono o qualcosa del genere) lo imposteremmo in modalità di esecuzione libera. Il metodo che stiamo utilizzando per impostare il trigger su PD2 attiva una lettura ADC della porta analogica PC0 senza causare un interrupt. L'interrupt arriverà quando la conversione è completa.
  12. Abilita il bit ADC Interrupt Enable (ADIE) nel registro ADCSRA in modo che quando la conversione da analogico a digitale sarà completa, genererà un interrupt per il quale possiamo scrivere un gestore di interrupt e metterlo su.org 0x002A.
  13. Imposta il bit I in SREG per abilitare gli interrupt.

Esercizio 1: Assicurati di leggere le sezioni pertinenti nel foglio dati per ciascuna delle impostazioni di cui sopra in modo da capire cosa sta succedendo e cosa accadrebbe se le cambiassimo in impostazioni alternative.

Passaggio 7: scrivere il gestore di interrupt

Nell'ultimo passaggio abbiamo visto che l'abbiamo impostato in modo che un fronte di salita rilevato su PD2 attivi una conversione da analogico a digitale su PC0 e quando questa conversione è completa lancerà un interrupt ADC Conversion Complete. Ora vogliamo fare qualcosa con questo interrupt. Se esamini la Tabella 12-6 a pagina 65 vedrai un elenco dei possibili interrupt. Abbiamo già visto l'interrupt RESET all'indirizzo 0x0000 e l'interrupt Timer/Counter0 Overflow all'indirizzo 0x0020 nei Tutorial precedenti. Ora vogliamo guardare l'interrupt ADC che vediamo dalla tabella è all'indirizzo 0x002A. Quindi all'inizio del nostro codice in linguaggio assembly avremo bisogno di una riga che dice:

.org 0x002Arjmp ADC_int

che salterà al nostro gestore di interrupt etichettato ADC_int ogni volta che l'ADC ha completato una conversione. Quindi, come dovremmo scrivere il nostro gestore di interrupt? Il modo in cui funziona l'ADC consiste nell'eseguire il seguente calcolo:

ADC = Vin x 1024 / Vref

Quindi vediamo cosa succede se premo il pulsante "ricomponi" sulla tastiera. In tal caso la tensione su PC0 cambierà in qualche valore, diciamo 1,52V, e poiché Vref è a 5V avremo:

ADC = (1,52 V) x 1024 / 5 V = 311.296

e quindi verrebbe visualizzato come 311. Se volessimo riconvertirlo in una tensione, invertiremmo semplicemente il calcolo. Tuttavia, non sarà necessario farlo poiché non ci interessano le tensioni effettive solo per distinguerle. Al termine della conversione, il risultato viene memorizzato in un numero a 10 bit posto nei registri ADCH e ADCL e abbiamo fatto sì che venga "regolato a sinistra", il che significa che i 10 bit iniziano al bit 7 di ADCH e scendono a bit 6 di ADCL (ci sono 16 bit in totale in questi due registri e ne usiamo solo 10, cioè 1024 canali). Potremmo avere il risultato "regolato a destra" se lo volessimo cancellando il bit ADLAR nel registro ADMUX. Il motivo per cui scegliamo regolato a sinistra è perché i nostri segnali sono abbastanza distanti che le ultime due cifre del numero di canale non sono rilevanti e sono probabilmente solo rumore, quindi distingueremo i tasti premuti usando solo le 8 cifre superiori, in altre parole, dovremo solo guardare ADCH per capire quale pulsante è stato premuto. Quindi il nostro gestore di interruzioni dovrebbe semplicemente leggere il numero da ADCH registro, convertire quel numero in un valore della tastiera e quindi inviare quel valore ai LED del nostro analizzatore di registro in modo che possiamo verificare che premendo un "9", diciamo, si accendono i LED corrispondenti a "00001001". tuttavia dobbiamo prima vedere cosa appare in ADCH quando premiamo i vari pulsanti. Quindi scriviamo un semplice gestore di interrupt che invii semplicemente il contenuto di ADCH al display dell'analizzatore. Quindi ecco cosa ci serve:

ADC_int:lds analizzatore, ADCH;carica il valore di ADCH nei nostri analizzatoribi EIFR, 0; cancellare il flag di interrupt esterno in modo che sia pronto per ripartire

A questo punto, dovresti essere in grado di copiare semplicemente il codice dal nostro analizzatore nel tutorial 5 e aggiungere questo interrupt e le impostazioni di commutazione ed eseguirlo. Esercizio 2: Scrivi il codice ed eseguilo. Vedere che si ottiene la visualizzazione di ADCH sul display dell'analizzatore. Prova a premere lo stesso tasto più volte. Ottieni sempre lo stesso valore in ADCH?

Passaggio 8: mappare i valori della pressione dei tasti

Mappare i valori della pressione dei tasti
Mappare i valori della pressione dei tasti

Quello che dobbiamo fare ora è convertire i valori in ADCH in numeri corrispondenti al tasto che è stato premuto. Lo facciamo scrivendo il contenuto di ADCH per ogni pressione di un tasto e poi convertendolo in un numero decimale come ho fatto nell'immagine. Nella nostra routine di gestione degli interrupt considereremo un intero intervallo di valori come corrispondente a ogni pressione di un tasto, in modo che l'ADC associ qualsiasi cosa in quell'intervallo a una determinata pressione di un tasto.

Esercizio 3: esegui questo ma-p.webp

Ecco cosa ho preso per il mio (molto probabilmente il tuo sarà diverso). Nota che l'ho impostato con un intervallo di valori per ogni pressione di un tasto.

ADC_int:; Analizzatore di interrupt esterno clr; preparare per nuovi numeri ld buttonH, ADCH; ADC si aggiorna quando viene letto ADCH clccpi buttonH, 240brlo PC+3; se ADCH è più grande allora è un analizzatore 1ldi, 1; quindi caricare l'analizzatore con un ritorno 1rjmp; e ritorno clccpi buttonH, 230; se ADCH è più grande di un analizzatore 2brlo PC+3ldi, 2rjmp ritorno clccpi buttonH, 217brlo PC+3ldi analizzatore, 3rjmp return clccpi buttonH, 203brlo PC+3ldi analizzatore, 4rjmp ritorno clccpi buttonH, 187brlo PC+3ldi analizzatore, 5rpijmp buttonH, cl analizzatore 155brlo PC+3ldi, 6rjmp return clccpi buttonH, analizzatore 127brlo PC+3ldi, 255; imposteremo flash come tutti onrjmp return clccpi buttonH, 115brlo PC+3ldi analizzatore, 7rjmp return clccpi buttonH, 94brlo PC+3ldi analizzatore, 8rjmp return clccpi buttonH, 62brlo PC+3ldi analizzatore, 9rjmp return clccpi buttonH, 37brlo PC+3ldi analizzatore 0b11110000; l'asterisco è la metà superiore sul pulsante clccpi di ritorno jmpH, analizzatore 28brlo PC+3ldi, 0rjmp return clccpi buttonH, analizzatore 17brlo PC+3ldi, 0b00001111; il segno hash è la metà inferiore onrjmp return clccpi buttonH, 5brlo PC+3ldi Analyzer, 0b11000011; la ricomposizione è l'analizzatore ldi di ritorno 2rjmp superiore 2 inferiore, 0b11011011; altrimenti si è verificato un errore return:reti

Passaggio 9: codice e video per la versione 1

Ho allegato il mio codice per questa prima versione del driver della tastiera. In questo è necessario premere la chiave e quindi premere il pulsante per fare in modo che l'ADC legga l'input dalla tastiera. Quello che preferiremmo non è un pulsante, ma invece il segnale per eseguire la conversione proviene dalla pressione dei tasti stessa. Esercizio 3: Assemblare e caricare questo codice e provarlo. Potrebbe essere necessario modificare le varie soglie di conversione in modo che corrispondano alle tensioni dei tasti premuti poiché probabilmente differiscono dalle mie. Cosa succede se si tenta di utilizzare un input da tastiera sia per l'ADC0 che per il pin di interrupt esterno invece che tramite un pulsante? Allegherò anche un video del funzionamento di questa prima versione del nostro keypress driver. Noterete che nel mio codice c'è una sezione che inizializza lo Stack Pointer. Ci sono vari registri che potremmo voler spingere ed estrarre dallo stack quando stiamo manipolando variabili e quant'altro e ci sono anche registri che potremmo voler salvare e ripristinare in seguito. Ad esempio, SREG è un registro che non viene conservato tra gli interrupt, quindi i vari flag impostati e cancellati come risultato delle operazioni possono essere modificati se si verifica un'interruzione nel mezzo di qualcosa. Quindi è meglio se metti SREG nello stack all'inizio di un gestore di interrupt e poi lo spegni di nuovo alla fine del gestore di interrupt. L'ho inserito nel codice per mostrare come è inizializzato e per anticipare come ne avremo bisogno in seguito, ma poiché non ci interessa cosa succede a SREG durante gli interrupt nel nostro codice, non ho usato lo stack per questo. Nota anche che ho usato l'operazione di spostamento per impostare vari bit nei registri durante l'inizializzazione. Ad esempio nella riga:

ldi temperatura, (1<> sts EICRA, temp

Il comando "<<" nella prima riga di codice sopra è un'operazione di spostamento. Prende essenzialmente il numero binario 1, che è 0b00000001 e lo sposta a sinistra della quantità del numero ISC01. Questa è la posizione del bit denominato ISC01 nel registro EICRA. Poiché ISC01 è il bit 1, il numero 1 viene spostato alla posizione 1 a sinistra per diventare 0b0000001. Allo stesso modo il secondo, ISC00, è il bit 0 di EICRA e quindi lo spostamento del numero 1 è zero posizioni a sinistra. Se dai un'altra occhiata al file m328Pdef.inc che hai scaricato nel primo tutorial e da allora hai usato evrr, vedrai che è solo un lungo elenco di istruzioni ".equ". Scoprirai che ISC01 è uguale a 1. L'assemblatore sostituisce ogni sua istanza con 1 prima ancora di iniziare ad assemblare qualsiasi cosa. Sono solo nomi per bit di registro che aiutano noi umani a leggere e scrivere codice. Ora, la linea verticale tra le due operazioni di spostamento sopra è un'operazione logica "o". Ecco l'equazione:

0b0000001 | 0b0000001 = 0b00000011

e questo è ciò che stiamo caricando (usando "ldi") in temp. Il motivo per cui le persone usano questo metodo per caricare i valori in un registro è che consente di utilizzare il nome del bit invece di un semplice numero e questo rende il codice molto più facile da leggere. Ci sono anche altre due tecniche che abbiamo usato. Usiamo le istruzioni "ori" e "andi". Questi ci permettono di SET e CLEAR bit rispettivamente senza cambiare nessuno degli altri bit in un registro. Ad esempio, quando ho usato

ori temperatura, (1

questo "o" è temp con 0b00000001 che mette un 1 nel bit zero e lascia tutto il resto invariato. Anche quando abbiamo scritto

andi temp, 0b11111110

questo cambia il bit zero di temp in 0 e lascia tutto il resto invariato.

Esercizio 4: Dovresti esaminare il codice e assicurarti di aver compreso ogni riga. Potresti trovare interessante trovare metodi migliori per fare le cose e scrivere un programma migliore. Ci sono cento modi per codificare le cose e sono abbastanza fiducioso che tu possa trovare un modo molto migliore del mio. Potresti anche trovare (il cielo non voglia!) errori e omissioni. In tal caso mi piacerebbe sicuramente sentirne parlare in modo che possano essere risolti.

Ok, ora vediamo se riusciamo a sbarazzarci di quel bottone superfluo…

Passaggio 10: codice per la versione 2

Il modo più semplice per sbarazzarsi del pulsante è semplicemente rimuoverlo del tutto, dimenticare l'input su PB2 e passare semplicemente l'ADC in "Modalità di esecuzione libera".

In altre parole, cambia semplicemente il registro ADCSRB in modo che ADTS2, ADTS1 e ADTS0 siano tutti zero.

Quindi imposta il bit ADSC in ADCSRA su 1 che avvierà la prima conversione.

Ora caricalo sul tuo microcontrollore e scoprirai che il numero corretto appare sul display mentre premi il pulsante e solo mentre premi il pulsante. Questo perché l'ADC campiona continuamente la porta ADC0 e visualizza il valore. Quando togli il dito dal pulsante, il "rimbalzo del pulsante" farà sì che alcuni valori casuali si verifichino molto rapidamente e quindi si ristabilirà sull'ingresso 0V. Nel nostro codice abbiamo questo 0V che appare come 0b11011011 (perché la pressione del tasto "0" sta già utilizzando il valore di visualizzazione 0b00000000)

Questa non è la soluzione che vogliamo per due motivi. Innanzitutto non vogliamo dover tenere premuto il pulsante. Vogliamo premerlo una volta e avere il numero visualizzato (o utilizzato in un nuovo codice in un tutorial successivo). In secondo luogo, non vogliamo campionare continuamente l'ADC0. Vogliamo che esegua una singola lettura, la converta e poi si fermi fino a quando una nuova pressione di un tasto non attiva una nuova conversione. La modalità di esecuzione libera è la migliore se l'unica cosa che vuoi che il microcontrollore faccia è leggere continuamente un input analogico, come se volessi visualizzare le temperature in tempo reale o qualcosa del genere.

Quindi troviamo un'altra soluzione…

Passaggio 11: come sbarazzarsi del pulsante? Versione 3

Come ci liberiamo del bottone? Versione 3
Come ci liberiamo del bottone? Versione 3

Ci sono molti modi in cui potremmo procedere. Per prima cosa potremmo aggiungere hardware per sbarazzarci del pulsante. Ad esempio, potremmo provare a mettere un transistor nel circuito sulla linea di uscita della pressione del tasto in modo che prenda un piccolo flusso di corrente dall'uscita e invii un impulso di 5V al pin di interruzione PD2.

Tuttavia, ciò sarebbe probabilmente almeno troppo rumoroso e, nel peggiore dei casi, non consentirebbe tempo sufficiente per una lettura accurata della pressione dei tasti poiché l'uscita di tensione della tastiera non avrebbe il tempo di stabilizzarsi prima che la lettura dell'ADC venga acquisita.

Quindi preferiremmo trovare una soluzione software. Quello che vorremmo fare è aggiungere un interrupt sul pin PD2 e scrivere un gestore di interrupt per esso che chiama una singola lettura del pin della tastiera. In altre parole, eliminiamo l'interruzione dell'autotrigger dall'ADC e aggiungiamo un'interruzione esterna che chiama l'ADC al suo interno. In questo modo il segnale per leggere l'ADC arriva dopo che il segnale PD2 si è già verificato e questo potrebbe dare alle cose abbastanza tempo per stabilizzarsi su una tensione accurata prima che il pin PC0 venga letto e convertito. Avremmo ancora un interrupt di completamento dell'ADC che invia il risultato al display dell'analizzatore alla fine.

Ha senso? Bene facciamolo…

Dai un'occhiata al nuovo codice allegato.

Vengono visualizzate le seguenti modifiche:

  1. Abbiamo aggiunto un rjmp all'indirizzo.org 0x0002 per gestire l'interrupt esterno INT0
  2. Abbiamo cambiato il registro EIMSK per indicare che vogliamo interrompere sul pin INT0
  3. Abbiamo cambiato il pin ADATE nel registro ADCSRA per disabilitare l'autotriggering
  4. Ci siamo sbarazzati delle impostazioni ADCSRB poiché sono irrilevanti quando ADATE è disattivato
  5. Non dobbiamo più reimpostare il flag di trigger esterno poiché la routine di interrupt INT0 lo fa automaticamente quando viene completata - in precedenza non avevamo una routine di interrupt, abbiamo semplicemente attivato l'ADC da un segnale a quel pin, quindi abbiamo dovuto cancella quella bandiera a mano.

Ora nel gestore di interrupt chiamiamo semplicemente una singola conversione dall'ADC.

Esercizio 5: esegui questa versione e guarda cosa succede.

Passaggio 12: codice e video per la versione funzionante

Come abbiamo visto nell'ultima versione, l'interruzione del pulsante non funziona molto bene perché l'interruzione viene attivata su un fronte di salita al pin PD2 e quindi il gestore dell'interruzione chiama la conversione ADC. Tuttavia, l'ADC ottiene quindi la lettura della tensione prima che si sia stabilizzata e quindi legge un'assurdità.

Ciò di cui abbiamo bisogno è introdurre un ritardo tra l'interrupt su PD2 e la lettura dell'ADC su PC0. Lo faremo aggiungendo un timer/contatore, un'interruzione di overflow del contatore e una routine di ritardo. Fortunatamente sappiamo già come farlo dal Tutorial 3! Quindi copieremo e incolleremo semplicemente il codice pertinente da lì.

Ho fornito il codice risultante e un video che lo mostra in funzione.

Noterai che le letture non sono così accurate come si spera. Ciò è probabilmente dovuto a una serie di fonti:

  1. stiamo intercettando dall'uscita di tensione della tastiera per innescare su PD2 che influenza la lettura in PC0.
  2. non sappiamo davvero quanto tempo aspettare dopo il trigger per ottenere la migliore lettura.
  3. ci vogliono alcuni cicli per completare la conversione dell'ADC, il che significa che non possiamo sparare rapidamente sulla tastiera.
  4. probabilmente c'è rumore nella tastiera stessa.
  5. eccetera…

Quindi, anche se siamo riusciti a far funzionare la tastiera e ora potremmo usarla nelle applicazioni utilizzando i valori della pressione dei tasti in qualche altro modo invece di emetterli semplicemente sul display dell'analizzatore, non è molto preciso ed è molto fastidioso. Ecco perché penso che il modo migliore per cablare le tastiere sia semplicemente quello di attaccare ogni uscita dalla tastiera a una porta diversa e decidere quale tasto viene premuto da quali porte vedono una tensione. Questo è facile, molto veloce e molto preciso.

In effetti, ci sono solo due ragioni per cui si vorrebbe pilotare una tastiera come abbiamo fatto qui:

  1. Utilizza solo 2 dei pin sul nostro microcontrollore invece di 8.
  2. È un ottimo progetto mostrare diversi aspetti dell'ADC sul microcontrollore che è diverso dalle cose standard che puoi trovare lì come letture di temperatura, potenziometri rotanti, ecc. Volevo un esempio di letture singole innescate e attivazione automatica del pin esterno piuttosto che solo la modalità di consumo della CPU a esecuzione libera.

Ad ogni modo, ecco un paio di esercizi finali per te:

Esercizio 6: Riscrivi il gestore dell'interruzione completa della conversione ADC per utilizzare una tabella di ricerca. Cioè. In modo che verifichi il valore analogico con il primo elemento nella tabella e se è più grande ritorni dall'interrupt, in caso contrario incrementa Z all'elemento successivo nella tabella e torna nuovamente al test. Questo ridurrà il codice e ripulirà la routine di interruzione e la farà sembrare più bella. (Darò una possibile soluzione come passaggio successivo) Esercizio 7: Collega la tastiera a 8 pin sul microcontrollore e scrivi il semplice driver per esso e sperimenta quanto sia più bello. Riesci a pensare ad alcuni modi per far funzionare meglio il nostro metodo?

Questo è tutto per questo tutorial. Ho allegato la versione finale con i puntatori. Man mano che ci avviciniamo al nostro obiettivo finale, utilizzeremo ancora una volta la tastiera nel Tutorial 9 per mostrare come controllare i display a sette segmenti con essa (e costruire qualcosa di interessante che utilizzi i tasti extra sulla tastiera del telefono) e poi passa invece al controllo delle cose con la pressione dei pulsanti (dal momento che quel metodo si adatta meglio al prodotto finale che stiamo costruendo con questi tutorial) e accantoneremo semplicemente la tastiera.

Arrivederci alla prossima!

Consigliato: