simon
Differenze
Queste sono le differenze tra la revisione selezionata e la versione attuale della pagina.
Entrambe le parti precedenti la revisioneRevisione precedenteProssima revisione | Revisione precedente | ||
simon [2020/10/02 14:33] – [Software] admin | simon [2023/10/27 18:02] (versione attuale) – [Software] admin | ||
---|---|---|---|
Linea 18: | Linea 18: | ||
* cinque bottoni (uno per avviare il gioco) | * cinque bottoni (uno per avviare il gioco) | ||
* un microcontrollore che esegue il software che gestisce il gioco | * un microcontrollore che esegue il software che gestisce il gioco | ||
+ | |||
+ | Un video che descrive il progetto: | ||
+ | |||
+ | {{vimeo> | ||
===== Specifiche hardware e scelta componenti ===== | ===== Specifiche hardware e scelta componenti ===== | ||
Linea 68: | Linea 72: | ||
Coi pulsanti - che a tutti gli effetti sono dei contatti normalmente aperti - si può generare un segnale alto o basso utilizzando i 3 Volt di alimentazione e una resistenza di [[wpi> | Coi pulsanti - che a tutti gli effetti sono dei contatti normalmente aperti - si può generare un segnale alto o basso utilizzando i 3 Volt di alimentazione e una resistenza di [[wpi> | ||
- | I LED si collegano ai pin dei pulsanti corrispondenti. Quando i pin, usati come uscite, saranno al livello basso permetteranno alla corrente di scorrere dall' | + | I LED si collegano ai pin dei pulsanti corrispondenti. Quando i pin, usati come uscite, saranno al livello basso permetteranno alla corrente di scorrere dall' |
`R = (V_(\C\C)-V_F)/ | `R = (V_(\C\C)-V_F)/ | ||
Linea 85: | Linea 89: | ||
* il circuito non è simulabile (non si simula un microcontrollore!) | * il circuito non è simulabile (non si simula un microcontrollore!) | ||
* il componente ATtiny13A non è disponibile in Multisim e va creato | * il componente ATtiny13A non è disponibile in Multisim e va creato | ||
- | * tutti i componenti neri non hanno un footprint e non vengono considerati | + | * tutti i componenti neri non hanno un footprint e non vengono considerati |
* il colore dei LED non è importante perché in fase di montaggio si possono montare in qualunque ordine (i resistori sono tutti uguali) | * il colore dei LED non è importante perché in fase di montaggio si possono montare in qualunque ordine (i resistori sono tutti uguali) | ||
Linea 93: | Linea 97: | ||
* __assegnare i footprint corretti__ agli altri componenti, se possibile (LED e buzzer non lo consentono e bisogna farlo in Ultiboard) | * __assegnare i footprint corretti__ agli altri componenti, se possibile (LED e buzzer non lo consentono e bisogna farlo in Ultiboard) | ||
- | **Prima di creare | + | Per risparmiare tempo è possibile utilizzare |
- | * resistori: | + | * in Mutisim cliccando col tasto destro e scegliendo |
- | * LED: '' | + | * in Ultiboard selezionando il componente e scegliendo |
- | * ATtiny13A: | + | |
- | * pulsanti: footprint custom | + | |
- | * portabatteria: | + | |
- | * buzzer: footprint custom | + | |
+ | In alternativa si procede come descritto nel paragrafo seguente. | ||
- | Fatte queste considerazioni | + | Dopo aver creato footprint (portabatteria, |
- | {{:simon_4b.png|}} | + | {{:: |
+ | ==== Creare un componente in Multisim ==== | ||
+ | **NB non è necessario se si importano i simboli del file Multisim disponibile tra le risorse** | ||
+ | |||
+ | **Prima di creare i componenti conviene [[simon# | ||
+ | * buzzer: footprint custom '' | ||
+ | * ATtiny13A: '' | ||
+ | * pulsanti: footprint custom '' | ||
+ | * portabatteria: | ||
- | ==== Creare un componente in Multisim ==== | ||
Si usa una procedura guidata: | Si usa una procedura guidata: | ||
Linea 133: | Linea 141: | ||
Indicazioni per la creazione del pulsante TACT-65R-F: | Indicazioni per la creazione del pulsante TACT-65R-F: | ||
* step 1: TACT-65R-F, NINIGI, Tactile Switch SPST-NO, layout only | * step 1: TACT-65R-F, NINIGI, Tactile Switch SPST-NO, layout only | ||
- | * step 2: footprint custom TACT-SPST da creare in Ultiboard copiandolo da SKHH1_1 | + | * step 2: footprint custom TACT-SPST da creare in Ultiboard copiandolo da SKHH1 (in //Through Hole Technoly Parts|Buttons and Switches|Tact// |
* step 3: copiare da DPST-2NO-DB (//Electro mechanical|Supplementary switches//) e modificare aggiustando il nome dei pin del simbolo | * step 3: copiare da DPST-2NO-DB (//Electro mechanical|Supplementary switches//) e modificare aggiustando il nome dei pin del simbolo | ||
* step 4: nominare i pin come indicato nel datasheet | * step 4: nominare i pin come indicato nel datasheet | ||
Linea 139: | Linea 147: | ||
* step 6: creare la famiglia //SWITCH// nel gruppo //Basic// del database User e cliccare su //Finish// | * step 6: creare la famiglia //SWITCH// nel gruppo //Basic// del database User e cliccare su //Finish// | ||
- | Indicazioni per la creazione del portabatteria DS1092-05: | + | Indicazioni per la creazione del portabatteria DS1092-04: |
- | * step 1: <del>DS1092-05</ | + | * step 1: DS1092-04, Connfly, CR2032 battery holder, layout only |
- | * step 2: footprint custom | + | * step 2: footprint custom DS1092-04 da creare in Ultiboard secondo usando le informazioni contenute nel datasheet |
* step 3: copiare da 1028 (in // | * step 3: copiare da 1028 (in // | ||
* step 4: accettare la piedinatura | * step 4: accettare la piedinatura | ||
Linea 148: | Linea 156: | ||
===== Layout del PCB ===== | ===== Layout del PCB ===== | ||
+ | |||
+ | In questa fase di procede alla definizione delle dimensioni della scheda, poi al layout (posizionamento) dei componenti e infine allo sbroglio, cioè al disegno delle piste in rame che collegano i componenti **che non devono mai toccarsi o incrociarsi**. | ||
Requisiti per la realizzazione nella sala acidi della scuola: | Requisiti per la realizzazione nella sala acidi della scuola: | ||
* single layer | * single layer | ||
* piste da 1 mm | * piste da 1 mm | ||
- | * testo specchiato col nome dello studente | + | |
+ | | ||
* massimo 2 piste sotto l' | * massimo 2 piste sotto l' | ||
Altri requisiti: | Altri requisiti: | ||
* dimensioni massime 6 x 5 cm | * dimensioni massime 6 x 5 cm | ||
- | * pulsanti facilmente raggiungibili | + | * pulsanti facilmente raggiungibili |
* minimizzare ingombri | * minimizzare ingombri | ||
Indicazioni varie: | Indicazioni varie: | ||
* imparare a usare i filtri di selezione! | * imparare a usare i filtri di selezione! | ||
+ | * imparare a misurare le dimensioni usando le indicazioni delle coordinate in basso a destra; un click del mouse imposta il punto di partenza della misura poi dx, dy e L indicano lo spostamento orizzontale, | ||
* ridimensionare il board outline prima di piazzare i componenti (selezionare il layer //Board outline// e attivare il filtro //Enable selecting other objects// per cambiare le dimensioni del rettangolo giallo che delimita il PCB) | * ridimensionare il board outline prima di piazzare i componenti (selezionare il layer //Board outline// e attivare il filtro //Enable selecting other objects// per cambiare le dimensioni del rettangolo giallo che delimita il PCB) | ||
* posizionare e ruotare i componenti per facilitare lo sbroglio | * posizionare e ruotare i componenti per facilitare lo sbroglio | ||
- | * disabilitare l' | + | * disabilitare l' |
- | * impostare l' | + | * impostare l' |
- | * dove è possibile usare piazzole circolari con foro da 0,6mm e diametro 2,6mm | + | * dove è possibile usare piazzole circolari con foro da 0,6 mm e diametro 2,6 mm |
- | * per i componenti con i pin troppo vicini tra loro usare [[simon# | + | * per i componenti con i pin troppo vicini tra loro usare piazzole ovali (o [[simon# |
* imparare a creare footprint custom | * imparare a creare footprint custom | ||
Cominciamo così: | Cominciamo così: | ||
* la prima volta che si passa da Multisim a Ultiboard si usa // | * la prima volta che si passa da Multisim a Ultiboard si usa // | ||
- | * se dopo aver creato | + | * se dopo aver iniziato |
- | * la prima cosa da fare è guardare | + | * la prima cosa da fare è osservare se il footprint dei vari componenti |
+ | |||
+ | Andranno cambiati i footprint di: | ||
+ | * resistori scegliendo '' | ||
+ | * LED: '' | ||
+ | |||
+ | Prima di procedere allo sbroglio è bene anche aggiustare se necessario le dimensioni delle piazzole: | ||
+ | * dove possibile foro da 0,6mm e diametro 2,6mm | ||
+ | * per microcontrollore e LED piazzole ovali, scegliendo //Rounded Rectangle// con foro da 0,6mm e Pad diameter 3mm, Length 1,8mm e Corner radius 0,9mm | ||
Dopo aver ridimensionato il PCB si dovrebbe ottenere qualcosa di simile: | Dopo aver ridimensionato il PCB si dovrebbe ottenere qualcosa di simile: | ||
Linea 181: | Linea 201: | ||
Da qui si procede con lo sbroglio, piazzando i componenti e tracciando le piste. Qualche indicazione: | Da qui si procede con lo sbroglio, piazzando i componenti e tracciando le piste. Qualche indicazione: | ||
* bisogna fare più tentativi, spostando i componenti e ridisegnando le piste, per trovare la soluzione migliore | * bisogna fare più tentativi, spostando i componenti e ridisegnando le piste, per trovare la soluzione migliore | ||
- | * seguire le // | + | * seguire le // |
* fare attenzione a non sovrapporre piste/ | * fare attenzione a non sovrapporre piste/ | ||
* verificare che la // | * verificare che la // | ||
- | * verificare che il PCB abbia l' | + | * verificare che il PCB abbia l' |
* a sbroglio completato verificare che il progetto non contenga errori osservando il risultato nella scheda //Results// della // | * a sbroglio completato verificare che il progetto non contenga errori osservando il risultato nella scheda //Results// della // | ||
* // | * // | ||
Linea 190: | Linea 210: | ||
Un possibile sbroglio è mostrato in figura: | Un possibile sbroglio è mostrato in figura: | ||
+ | |||
+ | |||
+ | |||
+ | |||
{{: | {{: | ||
- | Il render 3D ha questo aspetto: | + | |
+ | Il render | ||
+ | |||
+ | |||
+ | |||
{{:: | {{:: | ||
+ | |||
+ | |||
==== Creare un footprint ==== | ==== Creare un footprint ==== | ||
- | Ultiboard organizza i footprint in più database; quello predefinito si chiama //Ultiboard Master// e contiene i footprint di migliaia di componenti. Quando un progetto include un componente il cui footprint | + | **NB non è necessario se si importano i footprint del file Ultiboard disponibile tra le risorse** |
+ | |||
+ | Ultiboard organizza i footprint in più database; quello predefinito si chiama //Ultiboard Master// e contiene i footprint di migliaia di componenti. Quando un progetto include un componente il cui footprint non è disponibile in questo database bisogna crearne uno custom e salvarlo nel database //User//. Si può: | ||
* creare il footprint da zero e salvarlo nel database //User// | * creare il footprint da zero e salvarlo nel database //User// | ||
* copiare un footprint dal database master per poi modificarlo e salvarlo nel database //User// | * copiare un footprint dal database master per poi modificarlo e salvarlo nel database //User// | ||
- | * importare un footprint da un file Ultiboard dal //Database Manager// | + | * importare un footprint da un file Ultiboard dal //Database Manager// |
Il footprint di buzzer e portabatteria va creato da zero, quello dei pulsanti si può fare modificandone uno di Ultiboard. | Il footprint di buzzer e portabatteria va creato da zero, quello dei pulsanti si può fare modificandone uno di Ultiboard. | ||
Linea 209: | Linea 242: | ||
* nella vista ad albero a sinistra selezionare //User database// (in questa fase, volendo si può creare un gruppo/ | * nella vista ad albero a sinistra selezionare //User database// (in questa fase, volendo si può creare un gruppo/ | ||
* cliccare l' | * cliccare l' | ||
- | * l' | + | * l' |
* piazzare i pin con // | * piazzare i pin con // | ||
- | * utilizzando il layer // | + | * utilizzando il layer // |
* dopo aver **controllato attentamente le dimensioni del layout((le misure si possono fare cliccando in un punto e osservando in basso a destra il valore delle coordinate e delle distanze dX dY e L)), la distanza tra i piedini e il posizionamento dei piedini nel layout del componente** salvare il footprint nel database User (eventualmente in un gruppo adatto) dandogli un nome significativo | * dopo aver **controllato attentamente le dimensioni del layout((le misure si possono fare cliccando in un punto e osservando in basso a destra il valore delle coordinate e delle distanze dX dY e L)), la distanza tra i piedini e il posizionamento dei piedini nel layout del componente** salvare il footprint nel database User (eventualmente in un gruppo adatto) dandogli un nome significativo | ||
==== Creare una piazzola custom ==== | ==== Creare una piazzola custom ==== | ||
- | I LED e l' | + | **NB non è necessario da Multism 14.2 o superiore** |
+ | |||
+ | I LED e l' | ||
Creare la piazzola custom: | Creare la piazzola custom: | ||
Linea 241: | Linea 276: | ||
===== Software ===== | ===== Software ===== | ||
- | Il programma che caricheremo nel microcontrollore non usa nessuna delle funzioni di Arduino ma opera direttamente sui registri dell' | + | Il programma(({{ : |
- | I commenti | + | Il programma non è spiegato nel dettaglio ma dai commenti |
- | * come funzionano gli [[wpi> | + | |
- | * interpretare i datasheet di un microcontrollore, | + | |
- | Ad esempio | + | Prerequisiti: |
+ | * sapere come funzionano gli [[wpi> | ||
+ | * essere in grado di trovare le informazioni | ||
+ | * saper programmare in C | ||
+ | |||
+ | Il codice | ||
while (ADCSRA & (1 << ADSC)); | while (ADCSRA & (1 << ADSC)); | ||
che si interpreta così: | che si interpreta così: | ||
- | * ADSC, definito in una macro((vedi [[https:// | + | * ADSC è un bit del registro ADCSRA e corrisponde al numero 6, cioè il secondo bit più significativo((è |
* 1 << ADSC corrisponde a 01000000 | * 1 << ADSC corrisponde a 01000000 | ||
- | * ADCSRA (ADC Control and Status Register A) è uno dei registro a 8 bit che gestisce il convertitore analogico-digitale | + | * ADCSRA (ADC Control and Status Register A) è uno dei registro a 8 bit che gestisce il convertitore analogico-digitale; quando la conversione analogico digitale termina il bit ADSC del registro ADCSRA passa a 0 |
- | * quando la conversione analogico digitale termina il bit ADSC del registro ADCSRA passa a 0 | + | * allora l' |
- | * allora l' | + | |
+ | Insomma per capire cosa succede in questa istruzione, che serve ad attendere il termine | ||
<code C> | <code C> | ||
Linea 285: | Linea 324: | ||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
OTHER DEALINGS IN THE SOFTWARE. | OTHER DEALINGS IN THE SOFTWARE. | ||
+ | */ | ||
- | + | /* | |
- | **** COMMENTI E SPIEGAZIONI INTRODUTTIVE **** | + | **** SPIEGAZIONI INTRODUTTIVE **** |
SCOPO DEL PROGRAMMA | SCOPO DEL PROGRAMMA | ||
Il codice implementa il classico gioco Simon con 4 LED, 4 pulsanti | Il codice implementa il classico gioco Simon con 4 LED, 4 pulsanti | ||
- | e una sequenza da ricordare; la sequenza casuale è generata quando | + | e una sequenza |
- | | + | La sequenza casuale è generata quando il pulsante start/reset avvia |
- | | + | |
- | punteggio massimo viene salvto su EEPROM (si può cancellare l' | + | Il gioco si interrompe quando si commette un errore. |
- | score premendo | + | |
+ | score tenere premuto | ||
+ | Il watchdog timer viene usato per il debouncing, lo sleep-mode e all' | ||
+ | avvio per il random generator. | ||
+ | Il timer viene usato per generare il segnale PWM per il buzzer. | ||
PREREQUISITI | PREREQUISITI | ||
- | generalità sui microcontrollori e programmazione in C, | + | |
- | saper leggere un datasheet lungo e complesso, bitwise operation e | + | |
- | | + | - bitwise operation e bit masking per la gestione dei registri |
CONFIGURAZIONE | CONFIGURAZIONE | ||
- | | + | |
- | i " | + | corretta come tempi. |
- | ha due possibili frequenze di funzionamento e un prescaler per | + | |
- | ridurre la frequenza). Settando una frequenza diversa | + | |
- | | + | |
REGISTRI PIU' IMPORTANTI UTILIZZATI | REGISTRI PIU' IMPORTANTI UTILIZZATI | ||
I numeri di pagina nei commenti fanno riferimento al datasheet | I numeri di pagina nei commenti fanno riferimento al datasheet | ||
- | dell' | + | dell' |
- | | + | |
- | DDRB = 0b00000000 -> tutti ingressi | + | |
- | PORTB = | + | DDRB = 0b00000000 -> tutti ingressi |
+ | PORTB | ||
ADCSRA = 0b00000000 -> ADC spento | ADCSRA = 0b00000000 -> ADC spento | ||
- | ADMUX = | + | ADMUX |
+ | |||
+ | GESTIONE INGRESSI E USCITE | ||
+ | DDRB imposta come uscite i pin che hanno il bit corrispondente a 1 | ||
+ | (pagina 50). I pin sono sempre usati come ingressi tranne quando | ||
+ | viene chiamata la funzione play(). | ||
+ | Il pin 1 (PB5) è usato sia per il pulsante start (è un RESET attivo | ||
+ | basso che come ingresso analogico (ADC0) scollegato, per generare | ||
+ | il seed del random generator. | ||
+ | Il pin 6 (PB1/OC0B) è usato per pilotare il buzzer con un' | ||
+ | quadra generata col timer/ | ||
- | Ingressi e uscite. | ||
- | DDRB imposta come uscite i bit a 1 (pagina 50). I pin sono ingressi | ||
- | tranne quando viene chiamata play(). | ||
- | Il pin 5 (PB5) viene usato come: | ||
- | * RESET attivo basso (start) | ||
- | * ADC0 scollegato | ||
- | Il pin 6 (PB1/OC0B) è usato per generare l'onda quadra per il | ||
- | buzzer usando il timer/ | ||
- | |||
Per la gestione del timer/ | Per la gestione del timer/ | ||
- TCNT0 che contiene il valore del conteggio | - TCNT0 che contiene il valore del conteggio | ||
Linea 333: | Linea 376: | ||
frequenza, e quello in corrispondenza del quale c'è la commutazione | frequenza, e quello in corrispondenza del quale c'è la commutazione | ||
tra livello alto e basso. Il waveform generator usa il risultato | tra livello alto e basso. Il waveform generator usa il risultato | ||
- | della comparazione per produrre un segnale PWM nel pin OC0B del buzzer. | + | della comparazione per produrre un segnale PWM nel pin OC0B (buzzer). |
- | + | ||
- | Il timer/clock, che si ferma se non si seleziona una sorgente di clock, | + | Il timer/counter |
- | | + | |
- | - normal: conta in su fino a 0xFF poi interrupt e ricomincia | + | - normal: conta in su fino a 0xFF poi genera un interrupt e ricomincia |
- CTC: conta fino a OCR0A | - CTC: conta fino a OCR0A | ||
- | - fast PWM: va basso su match con OCR0A alto su 0xFF (fig. 11-6) | + | - fast PWM: va basso sul match con OCR0A e alto su 0xFF (fig. 11-6) |
- phase correct PWM: conta in su e in giù | - phase correct PWM: conta in su e in giù | ||
+ | Nel programma si usa la modalità phase correct PWM per il buzzer. | ||
*/ | */ | ||
Linea 346: | Linea 390: | ||
// LIBRERIE UTILIZZATE (da https:// | // LIBRERIE UTILIZZATE (da https:// | ||
- | // sleep mode per ridurre il consumo. | + | // sleep mode |
- | // il MCU va in POWER-DOWN per non consumare e si sveglia | + | // Al termine di ogni partita, se scade il watchdog timer il MCU va in |
+ | // POWER-DOWN per ridurre il consumo; | ||
#include < | #include < | ||
- | // funzione simile a delay() di Arduino (cicli di CPU buttati) | + | // delay |
- | // bene per piccole temporizzazioni, | + | // funzione simile a delay() di Arduino (cicli di CPU buttati). Va |
- | // il codice usa _delay_loop_2(t) dove t è un int a 16 bit | + | // bene per piccole temporizzazioni, |
- | // il ritardo | + | // Il codice usa _delay_loop_2(t) dove t è un int a 16 bit; il ritardo |
- | // t per il tempo di quattro cicli di clock. | + | // dipende dal clock del MCU e si calcola moltiplicando t per il tempo |
- | // Esempio: | + | // di quattro cicli di clock. |
+ | // l'attesa | ||
#include < | #include < | ||
+ | // eeprom | ||
// per salvare l' | // per salvare l' | ||
#include < | #include < | ||
Linea 364: | Linea 411: | ||
// NB le variabili dichiarate e non inizializzate valgono zero, vedi | // NB le variabili dichiarate e non inizializzate valgono zero, vedi | ||
- | // www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_varinit.html | + | // https://www.nongnu.org/avr-libc/user-manual/FAQ.html# |
- | // i quattro elementi impostano come uscite PB1 (il buzzer) e uno tra | + | // nella fase di gioco in cui viene mostrata la sequenza con la funzione |
- | // PB3, PB2, PB0 e PB4 per accendere rispettivamente | + | // play() |
- | // nella fase di gioco in cui viene mostrata la sequenza | + | // impostano come uscite PB1 (il buzzer) e uno tra PB3, PB2, PB0 e PB4 |
+ | // (i LED 1, 4, 3 e 2) | ||
const uint8_t buttons[4] = { | const uint8_t buttons[4] = { | ||
0b00001010, 0b00000110, 0b00000011, 0b00010010 | 0b00001010, 0b00000110, 0b00000011, 0b00010010 | ||
Linea 374: | Linea 422: | ||
// array con quattro valori usati dal waveform generator per produrre | // array con quattro valori usati dal waveform generator per produrre | ||
- | // le note: re# sol# do re# (2.5, 3.3, 4.2 e 5 kHz) | + | // le note: re fa# la re |
// vedere la funzione play() per interpretare i valori | // vedere la funzione play() per interpretare i valori | ||
const uint8_t tones[4] = { | const uint8_t tones[4] = { | ||
Linea 380: | Linea 428: | ||
}; | }; | ||
- | uint8_t lastKey; // ultimo tasto premuto per il debouncing | + | uint8_t lastKey; // ultimo tasto premuto |
uint8_t lvl = 0; // livello (parte da 0 -> sequenza con un solo LED) | uint8_t lvl = 0; // livello (parte da 0 -> sequenza con un solo LED) | ||
- | uint8_t maxLvl; | + | uint8_t maxLvl; |
- | // variabili per pseudo-random generator | + | // variabili per pseudo-random generator |
- | uint16_t seed; // generato con l'ADC su un pin scollegato (e mischiato) | + | uint16_t seed; // generato con l'ADC su un pin scollegato (poi mischiato) |
uint16_t ctx; // valori successivi generati a partire dal seed | uint16_t ctx; // valori successivi generati a partire dal seed | ||
// variabili volatile modificate dalla ISR del watchdog | // variabili volatile modificate dalla ISR del watchdog | ||
+ | // vedi http:// | ||
+ | // le motivazioni circa il qualificatore " | ||
volatile uint8_t nrot = 8; // quante volte si mischia il seed nella ISR | volatile uint8_t nrot = 8; // quante volte si mischia il seed nella ISR | ||
- | // usando il valore del timer | ||
volatile uint16_t time; // conteggio incrementato dall' | volatile uint16_t time; // conteggio incrementato dall' | ||
// usato per il debouncing dei pulsanti e per | // usato per il debouncing dei pulsanti e per | ||
Linea 398: | Linea 447: | ||
void sleepNow() { | void sleepNow() { | ||
- | // power-down riduce al massimo il consumo; il risveglio avviene col | + | // power-down riduce al massimo il consumo |
- | // pulsant start/reset | + | // il risveglio avviene col pulsant start/reset |
// di default tutti i pin sono ingressi; in power-down gli ingressi | // di default tutti i pin sono ingressi; in power-down gli ingressi | ||
- | // flottanti è meglio che siano scollegati (pag. 53) | + | // flottanti è meglio che siano scollegati |
- | PORTB = 0b00000000; // tri-state (disabilita la resitenza | + | PORTB = 0b00000000; // tri-state (disabilita la resistenza |
cli(); // disabilita gli interrupt | cli(); // disabilita gli interrupt | ||
WDTCR = 0; // spegne il Watchdog timer | WDTCR = 0; // spegne il Watchdog timer | ||
Linea 412: | Linea 461: | ||
void play(uint8_t i, uint16_t t = 45000) { | void play(uint8_t i, uint16_t t = 45000) { | ||
// accende un LED e suona la nota corrisponente col buzzer | // accende un LED e suona la nota corrisponente col buzzer | ||
- | // i è l' | + | // i è l' |
- | // per ritardare di (4/1.2M)*t sec (45000 -> il delay è di 150ms) | + | // t è usato nella funzione delay per ritardare di (4/1.2M)*t sec |
+ | // il valore | ||
- | // con i pin impostati come ingressi | + | // quando non viene eseguita la funzione play() |
- | // 0 -> se è un input disabilita la resistenza di pull-up | + | // come ingressi |
- | // 0 -> se è un output imposta il livello basso di tensione | + | // prima di passare da ingresso a uscita bisogna impostarli come |
- | // questa operazione serve nel passaggio da ingresso con pull-up a | + | //tri-state (pagina 51) quindi si modifica |
- | // uscita ed è indicata a pagina 51 | + | // 0 -> come input disabilita la resistenza di pull-up |
+ | // 0 -> come output imposta il livello basso di tensione | ||
PORTB = 0b00000000; | PORTB = 0b00000000; | ||
// imposta come uscite il buzzer e uno dei pin collegati ai LED | // imposta come uscite il buzzer e uno dei pin collegati ai LED | ||
- | // con i bit di PORTB a 0 (livello basso) il LED si accende | + | // con i bit di PORTB a 0 (livello basso) il LED si accende |
- | // buzzer viene generato il segnale PWM | + | |
DDRB = buttons[i]; | DDRB = buttons[i]; | ||
+ | // generazione del segnale PWM per il buzzer con il waveform generator | ||
+ | // modo PWM phase-correct (con TTCR0A e TTCR0B) | ||
+ | // - OCR0A e OCR0B impostano la frequenza della nota e il duty-cicle | ||
+ | // - bit di TTCR0B impostano il modo PWM e il prescaler | ||
+ | // la frequenza vale (pag. 68): f=(1.2M/ | ||
+ | // con i valori in tones (239, 179, 143, 119) -> 314 419 524 e 630 Hz | ||
+ | // che per qualche strana ragione che andrebbe investigata diventano | ||
+ | // re, fa#, la, re (294 370 440 587 Hz) | ||
+ | | ||
// assegno a OCR0A il valore massimo del conteggio del timer/ | // assegno a OCR0A il valore massimo del conteggio del timer/ | ||
- | // in modo da modificare la frequenza; da pag. 68 si capisce che la | + | // per modificare la frequenza (nota) |
- | // frequenza minima si otterrebbe senza usare OCR0A (quindi contando | + | |
- | // sempre fino a 0xFF - 255) f = 1.2M/ | + | |
- | // con i valori in tones si ottiene ad es. f=1.2M/ | + | |
- | // gli altri valori sono 3.3, 4.2 e 5 kHz (re# sol# do re#) | + | |
OCR0A = tones[i]; | OCR0A = tones[i]; | ||
- | // e a OCR0B la sua metà | + | // assegno |
OCR0B = tones[i] >> 1; //shiftR di 1: 01110111 (239 diviso 2: 119) | OCR0B = tones[i] >> 1; //shiftR di 1: 01110111 (239 diviso 2: 119) | ||
- | // TCCR0A/B sono impostati | + | // TCCR0A |
- | // che il waveform generator funzioni in modalità PWM phase correct | + | // TTCR0B in modo che il waveform generator funzioni in modalità PWM |
- | // WGM02 imposta il conteggio fino a OCR0A, CS01 il prescaler | + | // phase correct e conteggio fino a ORC0A con prescaler 8 |
TCCR0B = (1 << WGM02) | (1 << CS01); | TCCR0B = (1 << WGM02) | (1 << CS01); | ||
+ | // TTCR0A = 00100001 e TTCR0B = 00001010 quindi: | ||
+ | // WGM0[2:0] = 101 -> modo phase correct fino a OCR0A | ||
+ | // CS0[2:1] = 010 -> prescaler 8 | ||
// suona la nota e tiene il LED acceso per 150ms | // suona la nota e tiene il LED acceso per 150ms | ||
_delay_loop_2(t); | _delay_loop_2(t); | ||
- | // spegne il timer/ | + | // spegne il timer/ |
TCCR0B = 0b00000000; | TCCR0B = 0b00000000; | ||
// spegne il LED e disabilita il buzzer (tutti i pin come ingressi) | // spegne il LED e disabilita il buzzer (tutti i pin come ingressi) | ||
DDRB = 0b00000000; | DDRB = 0b00000000; | ||
- | // abilita le resistenze di pul-up per i pulsanti | + | // abilita le resistenze di pull-up per i pulsanti |
PORTB = 0b00011101; | PORTB = 0b00011101; | ||
} | } | ||
Linea 461: | Linea 519: | ||
// se si batte l'high score salva il livello raggiunto su eeprom | // se si batte l'high score salva il livello raggiunto su eeprom | ||
if (lvl > maxLvl) { | if (lvl > maxLvl) { | ||
- | eeprom_write_byte((uint8_t*) 0, ~lvl); // write best score | + | eeprom_write_byte((uint8_t*) 0, ~lvl); // high score complementato |
// | // | ||
for (uint8_t i = 0; i < 3; i++) { | for (uint8_t i = 0; i < 3; i++) { | ||
Linea 478: | Linea 536: | ||
uint8_t simple_random4() { | uint8_t simple_random4() { | ||
- | // linear-feedback shift register (Galois) | + | // LFSR linear-feedback shift register (Galois) |
- | // per una spiegazione | + | // per una spiegazione |
- | // o la pagina wikipedia (e l'AN della Maxim linkata): | + | // o la pagina wikipedia (e l'Application Note della Maxim linkata): |
// it.wikipedia.org/ | // it.wikipedia.org/ | ||
- | // modifica | + | // Modifica |
+ | // uno dei quattro | ||
// la sequenza pseudo-casuale di valori di ctx si ripete sempre | // la sequenza pseudo-casuale di valori di ctx si ripete sempre | ||
- | // uguale | + | // allo stesso modo se non cambia il seed; riportando ctx al valore |
- | // si può ripetere | + | // del seed si può ripercorrere |
- | // pulsanti | + | // pressione dei pulsanti |
+ | |||
// servono due bit per un nuovo indice da 0 a 3 (00 -> 11) | // servono due bit per un nuovo indice da 0 a 3 (00 -> 11) | ||
for (uint8_t i = 0; i < 2; i++) { | for (uint8_t i = 0; i < 2; i++) { | ||
Linea 494: | Linea 553: | ||
if (lsb || !ctx) { // se LSB (uscita) è 1 o se ctx contiene tutti 0 | if (lsb || !ctx) { // se LSB (uscita) è 1 o se ctx contiene tutti 0 | ||
// (con tutti zeri la sequenza non cambia mai) | // (con tutti zeri la sequenza non cambia mai) | ||
- | ctx ^= 0xB400; // inverte i bit secondo la maschera (XOR con 1) | + | ctx ^= 0xB400; |
+ | // 0xB400 -> 1011010000000000 quindi XOR con i | ||
+ | // bit 16, 14, 13 e 11, vedi su wikipedia en | ||
+ | // Linear-feedback_shift_register# | ||
} | } | ||
} | } | ||
Linea 502: | Linea 564: | ||
} | } | ||
- | ISR(WDT_vect) { // Watchdog Timeout Interrupt da avr-libc | + | ISR(WDT_vect) { |
- | // eseguita ogni volta che scade il timer (16ms) | + | |
- | // ogni 16ms viene incrementato il valore della variabile usata per | + | // eseguita ogni volta che scade il timer |
- | // gestire il poweroff | + | // ogni 16ms viene incrementato il valore della variabile |
+ | // per gestire il poweroff | ||
time++; // increase each 16 ms | time++; // increase each 16 ms | ||
- | // dopo ogni reset viene mischiato | + | // il watch timer viene usato anche all' |
- | // cabia più fino al reset successivo | + | // seed con l'ADC; il seed non cambia |
if (nrot) { | if (nrot) { | ||
nrot--; | nrot--; | ||
- | // mischia il seed facendo uno shift a sinistra e XOR con valore | + | // mischia il seed facendo uno shift a sinistra e XOR con il valore |
- | // del timer/ | + | // del timer/ |
+ | // a 8bit senza prescaler, quindi | ||
seed = (seed << 1) ^ TCNT0; | seed = (seed << 1) ^ TCNT0; | ||
} | } | ||
Linea 523: | Linea 587: | ||
int main(void) { | int main(void) { | ||
- | // pull-up sui 4 pulsanti dei LED (tutti ingressi di default) | + | // pull-up sui 4 pulsanti dei LED (sono tutti ingressi di default) |
PORTB = 0b00011101; | PORTB = 0b00011101; | ||
// GENERAZIONE DEL SEED | // GENERAZIONE DEL SEED | ||
- | // ADC | + | |
// Il pin PB5/ADC0 (start/ | // Il pin PB5/ADC0 (start/ | ||
- | // attivando l' | + | // attivando l' |
- | // il valore viene poi mischiato otto volte nella ISR usando il timer | + | // Il valore viene poi mischiato |
+ | // allo scadere del watchdog | ||
+ | |||
+ | // ADC | ||
+ | |||
ADCSRA |= (1 << ADEN); // abilita l'ADC (pagina 82 e 92) | ADCSRA |= (1 << ADEN); // abilita l'ADC (pagina 82 e 92) | ||
ADCSRA |= (1 << ADSC); // parte la conversione sul pin ADC0 scollegato | ADCSRA |= (1 << ADSC); // parte la conversione sul pin ADC0 scollegato | ||
Linea 541: | Linea 609: | ||
seed = ADCL; | seed = ADCL; | ||
ADCSRA = 0b00000000; // spegne l'ADC | ADCSRA = 0b00000000; // spegne l'ADC | ||
+ | |||
// WATCHDOG TIMER | // WATCHDOG TIMER | ||
+ | |||
// abilita l' | // abilita l' | ||
- | // il WDT usa un oscillatore separato a 128kHz (il timer va col clock) | + | // il WDT usa un oscillatore separato |
+ | // va col clock) | ||
// senza prescaling c'è un timeout ogni 16ms (2048 cicli, pag 43) e | // senza prescaling c'è un timeout ogni 16ms (2048 cicli, pag 43) e | ||
// viene eseguita la ISR | // viene eseguita la ISR | ||
WDTCR = (1 << WDTIE); // parte il watchdog timer con prescaler da 16ms | WDTCR = (1 << WDTIE); // parte il watchdog timer con prescaler da 16ms | ||
sei(); // global interrupt enable (mette a 1 il bit I nello SREG) | sei(); // global interrupt enable (mette a 1 il bit I nello SREG) | ||
- | | + | |
+ | | ||
+ | |||
// imposta il timer/ | // imposta il timer/ | ||
- | // prescaler (CS00 a uno): conta fino a 0xFF poi ricomincia e segnala | + | // prescaler (CS00 a uno): conta fino a 0xFF, segnala |
- | // con TOV0 (pag. 64) | + | // ricomincia il conteggio |
// il timer comincia a contare quando si imposta una sorgente per il | // il timer comincia a contare quando si imposta una sorgente per il | ||
// clock (pag. 60) | // clock (pag. 60) | ||
TCCR0B = (1 << CS00); | TCCR0B = (1 << CS00); | ||
+ | // TTCR0A = 00000000 e TTCR0B = 00000001 quindi: | ||
+ | // - WGM0[2:0] = 000 -> normal mode (pag 73) | ||
+ | // - CS0[2:0] = 001 -> no prescaling (clock a 1,2MHz) | ||
// il watchdog timer scade otto volte e la ISR mischia il seed | // il watchdog timer scade otto volte e la ISR mischia il seed | ||
while (nrot); | while (nrot); | ||
- | // IMPOSTAZIONE | + | // TIMER/ |
- | // cambia | + | // la modalità di funzionamento del timer cambia |
- | // a PWM phase correct (WGM0[2: | + | // phase correct (WGM0[2: |
- | // a 0xFF (nella funzione play() conterà fino al valore contenuto nel | + | // a 0xFF (tabella a pagina 73) |
- | // registro OCR0A per modificare la frequenza e produrre più note)) | + | // nella funzione play() |
- | // Si imposta il compare output mode del waveform generator | + | // note diverse, |
- | // che il pin del buzzer PB1/ | + | // Si imposta il compare output mode del waveform generator |
- | // match col valore del registro OCR0B mentre conta in su e alto | + | // (COM0B[1: |
- | // mentre conta in giù (OCR0B è impostato nella funzione play() come | + | // sul match col valore del registro OCR0B mentre |
- | // OCR0A) generando un onda quadra con la frequenza desiderata | + | // alto mentre |
+ | // quadra con la frequenza desiderata | ||
// NB il segnale PWM è presente solo se il pin è impostato come uscita | // NB il segnale PWM è presente solo se il pin è impostato come uscita | ||
- | // (pag 68) quindi il buzzer non suona finche non si chiama play() | + | // (pag 68) e se è impostata una sorgente per il clock (pag 60) quindi |
+ | // il buzzer non suona finche non si chiama play() | ||
TCCR0A = (1 << COM0B1) | (0 << COM0B0) | (0 << WGM01) | TCCR0A = (1 << COM0B1) | (0 << COM0B0) | (0 << WGM01) | ||
+ | // TTCR0A = 00100001 e TTCR0B = 00000001 quindi: | ||
+ | // - WGM0[2:0] = 001 -> phase correct mode (pag 73) | ||
+ | // - COM0B[1:0] = 10 -> reset up-counting, | ||
- | // LETTURA | + | // LETTURA DELL' |
- | // maxLvl viene scritto e letto negato | + | // maxLvl viene complementato |
- | // valore di default | + | // o letto perché il valore di default |
+ | // e con la EEPROM " | ||
// (uint8_t*) 0 è un puntatore a un byte che parte dall' | // (uint8_t*) 0 è un puntatore a un byte che parte dall' | ||
maxLvl = ~eeprom_read_byte((uint8_t*) 0); | maxLvl = ~eeprom_read_byte((uint8_t*) 0); | ||
+ | |||
+ | // EVENTUALE RESET DELL' | ||
// legge lo stato dei pin della port B e lo confronta con la maschera | // legge lo stato dei pin della port B e lo confronta con la maschera | ||
+ | // (dove 0 significa premuto) | ||
switch (PINB & 0b00011101) { | switch (PINB & 0b00011101) { | ||
- | // AND tra gli ingressi e la maschera sopra; 0 significa premuto, | + | // NB lo switch non è necessario; nel codice originale permetteva di |
- | // PB1 è il buzzer | + | // attivare altre funzioni tenedo premuti altri pulsanti all' |
- | // questo punto del programma sarà già alto); i pin dei pulsanti | + | // per ridurre le dimensioni dell' |
- | // non premuti sono al livello alto (pull-up) | + | // sono state eliminate |
- | // se PB3 (LED rosso) è premuto | + | // PB1 e PB5 non contano: uno è il buzzer e l' |
+ | // basso (che a questo punto del programma sarà già alto); i pin dei | ||
+ | // pulsanti | ||
+ | // all' | ||
case 0b00010101: // PB3 premuto dopo il reset -> azzera high-score | case 0b00010101: // PB3 premuto dopo il reset -> azzera high-score | ||
eeprom_write_byte((uint8_t*) 0, 255); // scrive 255 -> livello 0 | eeprom_write_byte((uint8_t*) 0, 255); // scrive 255 -> livello 0 | ||
Linea 596: | Linea 684: | ||
while (1) { | while (1) { | ||
// MOSTRA LA SEQUENZA | // MOSTRA LA SEQUENZA | ||
+ | |||
// reimposta ctx al valore del seed; la sequenza di valori di ctx | // reimposta ctx al valore del seed; la sequenza di valori di ctx | ||
// sarà sempre la stessa sequenza finché non si resetta | // sarà sempre la stessa sequenza finché non si resetta | ||
Linea 601: | Linea 690: | ||
// accende in sequenza un numero di LED pari al livello più uno | // accende in sequenza un numero di LED pari al livello più uno | ||
for (uint8_t cnt = 0; cnt <= lvl; cnt++) { | for (uint8_t cnt = 0; cnt <= lvl; cnt++) { | ||
- | // attesa tra un LED e il successivo; valore massimo | + | // attesa tra un LED e il successivo |
- | // -> 218ms poi scende se aumenta lvl | + | // il valore |
+ | // poi diminuisce all' | ||
_delay_loop_2(4400 + 489088 / (8 + lvl)); | _delay_loop_2(4400 + 489088 / (8 + lvl)); | ||
- | // accende il LED con indice | + | // accende il LED con l'indice restituito da simple_random4(), un |
- | // generato come resto della divisione per 4 di ctx, il cui valore | + | // numero tra 0 e 3 che è il resto della divisione per 4 di ctx |
- | // viene aggiornato ad ogni chiamata | + | // il valore di ctx viene aggiornato ad ogni chiamata |
play(simple_random4()); | play(simple_random4()); | ||
} | } | ||
Linea 612: | Linea 702: | ||
// INSERIMENTO SEQUENZA DA PARTE DEL GIOCATORE | // INSERIMENTO SEQUENZA DA PARTE DEL GIOCATORE | ||
- | // azzera il conteggio che porta al poweroff | + | // azzera il conteggio che porta al power-off |
time = 0; | time = 0; | ||
- | // imposta lastKey a un valore che non corrisponde a nessun pulsante | + | // per il debouncing |
+ | // a nessun pulsante | ||
lastKey = 5; | lastKey = 5; | ||
// riporta ctx al valore iniziale uguale al seed | // riporta ctx al valore iniziale uguale al seed | ||
Linea 620: | Linea 711: | ||
// tante volte quanti sono i LED da indovinare | // tante volte quanti sono i LED da indovinare | ||
for (uint8_t cnt = 0; cnt <= lvl; cnt++) { | for (uint8_t cnt = 0; cnt <= lvl; cnt++) { | ||
+ | // azzero pressed | ||
bool pressed = false; | bool pressed = false; | ||
// finché non si preme un bottone | // finché non si preme un bottone | ||
Linea 626: | Linea 718: | ||
for (uint8_t i = 0; i < 4; i++) { | for (uint8_t i = 0; i < 4; i++) { | ||
// controlla se il pulsante i è premuto | // controlla se il pulsante i è premuto | ||
- | // buttons & maschera -> bit a 1 solo per il pin/ | + | // buttons & maschera -> bit a 1 solo per il pin/ |
// PINB ha tutti i bit a 1 (resistenze di pull-up attivate | // PINB ha tutti i bit a 1 (resistenze di pull-up attivate | ||
// nella funzione play) tranne quello del pulsante premuto | // nella funzione play) tranne quello del pulsante premuto | ||
Linea 633: | Linea 725: | ||
if (!(PINB & buttons[i] & 0b00011101)) { | if (!(PINB & buttons[i] & 0b00011101)) { | ||
// DEBOUNCE | // DEBOUNCE | ||
- | // se sono passati 16ms (tempo max per eventuali rimbalzi o | + | // se sono passati 16ms (tempo max per eventuali rimbalzi) o |
// se è stato premuto un pulsante diverso (non è rimbalzo) | // se è stato premuto un pulsante diverso (non è rimbalzo) | ||
if (time > 1 || i != lastKey) { | if (time > 1 || i != lastKey) { | ||
Linea 644: | Linea 736: | ||
if (i != correct) { | if (i != correct) { | ||
// accendi il LED giusto tante volte pari al livello raggiunto | // accendi il LED giusto tante volte pari al livello raggiunto | ||
- | for (uint8_t j = 0; j < lvl; j++) { | + | for (uint8_t j = 0; j <= lvl; j++) { |
_delay_loop_2(65536); | _delay_loop_2(65536); | ||
play(correct, | play(correct, | ||
Linea 662: | Linea 754: | ||
} | } | ||
} | } | ||
- | // se passano 64s (time = 4000) e non succede niente poweroff | + | // se passano 64s (time = 4000) e non viene premuto nessun |
+ | // pulsante si attiva il power-off | ||
if (time > 4000) { | if (time > 4000) { | ||
sleepNow(); | sleepNow(); | ||
Linea 677: | Linea 770: | ||
} | } | ||
} | } | ||
+ | |||
</ | </ | ||
Linea 687: | Linea 781: | ||
===== Programmazione del microcontrollore ===== | ===== Programmazione del microcontrollore ===== | ||
- | Per programmare il micro ATtiny13A useremo una scheda Arduino Uno come programmatore come descritto in [[https:// | + | Per programmare il micro ATtiny13A useremo una scheda Arduino Uno((funziona anche con una Arduino Duemilanove ma bisogna collegare un condensatore da 10uF tra reset e ground altrimenti l' |
La soluzione più semplice è quella di usare una breadboard e sei cavi rigidi collegati come nella figura seguente((LED e resistori non servono ma permettono di testare velocemente se la programmazione funziona caricando sul micro uno sketch che fa lampeggiare il led)): | La soluzione più semplice è quella di usare una breadboard e sei cavi rigidi collegati come nella figura seguente((LED e resistori non servono ma permettono di testare velocemente se la programmazione funziona caricando sul micro uno sketch che fa lampeggiare il led)): | ||
Linea 722: | Linea 816: | ||
* spazzolare la scheda per togliere il photoresist e facilitare la saldatura | * spazzolare la scheda per togliere il photoresist e facilitare la saldatura | ||
- | * forare | + | * forare |
* come saldare (vedi immagine) e la pagina sull' | * come saldare (vedi immagine) e la pagina sull' | ||
* montare i LED rispettando la polarità | * montare i LED rispettando la polarità | ||
Linea 741: | Linea 835: | ||
* {{ : | * {{ : | ||
* [[https:// | * [[https:// | ||
- | * <del>{{ :: | + | * {{ ::ds1092-04-b6p.pdf |datasheet portabatteria}} |
* [[https:// | * [[https:// | ||
+ | * [[https:// | ||
* Tutorial National Instruments | * Tutorial National Instruments | ||
* [[http:// | * [[http:// | ||
Linea 748: | Linea 843: | ||
* altro | * altro | ||
* [[https:// | * [[https:// | ||
+ | * [[https:// | ||
* [[https:// | * [[https:// | ||
* [[http:// | * [[http:// | ||
+ | * [[https:// | ||
* {{ : | * {{ : | ||
- | * [[https://www.tme.eu/it/parking/ | + | |
+ | | ||
+ | * {{ : | ||
+ | * {{ :: | ||
+ | |||
simon.txt · Ultima modifica: 2023/10/27 18:02 da admin