INDICE

 

Prefazione del consiglio di classe

Introduzione

Abstract

Addendum

Specifiche fornite al gruppo elettrotecnici

 

Capitolo 1: Il lavoro nell’insieme

1.0: Finalità del progetto

1.1: Schema a blocchi e logica di funzionamento

1.2: Dettaglio del blocco aritmetico

1.3: Dettaglio del blocco di visualizzazione

1.4: L’elettronica nel sistema

1.5: La scelta del microprocessore

 

Capitolo 2: L’hardware

2.1: La struttura meccanica dell’addizionatore

2.2: La struttura meccanica del visualizzatore

2.3: Le schede con microprocessore

2.4: Il controllo delle elettrocalamite

 

Capitolo 3: Il firmware nel PIC

3.1: Controllo addizionatore: linee guida

3.1.1: Diagramma di flusso

3.1.2: Main program

3.1.3: Temporizzazioni

3.2: Controllo visualizzatore: linee guida

        3.2.1: Diagramma di flusso

        3.2.2: Analisi dei blocchi più significativi

 

Capitolo 4: Il blocco distributore

4.1: Descrizione sintetica

4.2: Schema a blocchi

4.3: La scelta del microprocessore

4.4: La scheda elettronica

4.5: Il software nel PIC

 

Capitolo 5: I blocchi accessori

5.1: L’alimentatore

5.2: Schemi elettrici alimentatore

5.3: La scheda sonora

 

Appendici


Prefazione del Consiglio di Classe

 

 

Sempre più negli ultimi anni si è reso necessario avvicinare la scuola al mondo del lavoro. Per ottenere questo risultato sono percorribili varie strade: una è la partecipazione dei futuri periti a stage aziendali, ma nel caso del corso serale per studenti lavoratori, i candidati partecipano necessariamente alla vita delle aziende dove sono impiegati. Una seconda strada potrebbe essere quella di far partecipare attivamente gli alunni allo sviluppo di un progetto come se fossero realmente a lavorare in un’azienda. Questa seconda metodologia ha dato come risultato questa  dispensa: il Consiglio di Classe ha individuato un progetto di una certa complessità da sviluppare nell’intero arco di due anni scolastici nell’ambito delle ore di laboratorio, principalmente TDP e sistemi. Per lo sviluppo del lavoro gli studenti hanno dovuto seguire il tipico iter aziendale:

 

·                    Analisi delle specifiche

·                    Progettazione

·                    Realizzazione

·                    Documentazione

 

L’impostazione a blocchi dello sviluppo del progetto, ormai pratica comune in qualsiasi attività produttiva, ha permesso di dividere il lavoro tra i vari studenti: ogni singolo “pezzo” dell’intero sistema è stato affidato di volta in volta a uno o più studenti, del tutto responsabili della loro parte: alcuni si sono occupati del software (fortemente presente come componente immateriale in tutto questo lavoro), altri dell’hardware e della documentazione, badando comunque che, alla fine, tutti avessero fatto un po’ di tutto. Questa suddivisione ha permesso ulteriormente di assecondare gli interessi personali degli studenti con il risultato non accessorio di una maggior produttività di ognuno. Il ruolo dell’insegnante è stato solo di coordinatore dell’attività; l’intervento diretto è stato volutamente ridotto al minimo e comunque solo per quelle poche parti che oggettivamente erano fuori della portata degli studenti. Questa scelta ha portato ad ottenere un prodotto che, anche se per alcuni aspetti può essere incompleto o evidentemente perfettibile, nell’insieme è un reale ed apprezzabile prototipo sperimentale funzionante.
INTRODUZIONE

 

 

Il lavoro che presentiamo qui come progetto, è una macchina addizionatrice avente la stessa logica di funzionamento di una calcolatrice elettronica, ma che utilizza, al posto degli elettroni,    delle biglie di plastica.

Per rendere più realistico il lavoro, abbiamo supposto di voler costruire uno strumento didattico destinato alla vendita, e così abbiamo provato a buttare giù un volantino da distribuire  ai potenziali acquirenti per pubblicizzare il prodotto.

In questa ottica abbiamo dato anche un nome al nostro prodotto: Piccola Marta. “Piccola” perché è … tutt’altro che piccola; “MARTA” che sta per

Macchina

Addizionatrice

Rotolante

Teoricamente

Automatica.     

 

Per gli amici, Martina.

 

Il risultato è nella pagina seguente.

 


Volantino
Abstract

 

 

The work here presented is an addition machine having the same logic adopted by an electronic calculator which utilizes plastic marbles instead of electrons.

To make it more realistic we have just assumed to build an educational tool aimed to commercial use and then we have tried to jot down a pamphlet suitable for potential buyers with the intent of advertising the product.

In view of this we have even given a name to the product: Piccola Marta. “Piccola” means ‘little’ which is all but little; “MARTA” is referred to

Machine

Addition

Rolling

Theoretically

Automatic.

 

For the friends just Little Marta.

 

The outcome is on the previous page.

 

 

 


Addendum

 

 

Data la complessità del progetto, avevamo previsto di svilupparlo insieme agli alunni del corso di elettrotecnica; in particolare a loro era stato assegnato il blocco “distributore delle biglie” da realizzare con l’uso di un PLC, che ben si adatta alla loro specializzazione. Problemi vari hanno impedito questa collaborazione e quindi il blocco distributore è stato realizzato quasi interamente da questa classe, con contributi di alunni di altre classi del corso elettronici.

In questo documento abbiamo inserito le specifiche relative al blocco in questione e che avremo dovuto fornire all’altra classe. Abbiamo inoltre inserito un capitolo inizialmente non previsto (capitolo 4) che spiega a grandi linee il progetto e lo sviluppo del blocco distributore. La stesura della documentazione che segue è iniziata in quarta, quindi spesso si fa cenno al “blocco degli elettrotecnici” anche se in realtà è stato sviluppato da questa classe. Ciò è dovuto al fatto che in quel momento ancora non sapevamo della rinuncia.


Nota degli insegnanti di TDP

 

 

Anche a una prima occhiata ci si rende conto che la parte elettromeccanica della nostra “calcolatrice” è decisamente di non semplice realizzazione. I noti “vincoli di bilancio” ci hanno costretti ad adattare materiali riciclati, a inventare soluzioni a costo zero, ad attingere a piene mani al ben noto “Ingegno Italiano”. Per descrivere compiutamente tutte le prove fatte, gli insuccessi, i piccoli passi avanti e quelli indietro ci vorrebbe un volume a parte. In questa sede vogliamo solo segnalare la grande disponibilità, volontà e, non ultima, la pazienza mostrata da tutti gli studenti di questa classe; volentieri li ringraziamo per la passione e l’impegno che hanno dedicato a un lavoro decisamente diverso dai soliti esercizi scolastici.
Specifiche fornite al gruppo elettrotecnici

 

 

Il dispositivo si compone di due parti: il fornitore di biglie e l’addizionatore vero e proprio, più un circuito per la visualizzazione del risultato:

 

 


Il blocco Distributore deve soddisfare le seguenti specifiche:

a somma avverrà tra due numeri a TRE bit (A = A2, A1, A0) e B (B2, B1, B0).

Il risultato sarà rappresentato su 4 bit.

 

 


Sequenza operazioni:

 

 

Utente del sistema

Distributore

Addizionatore

Circuito di visualizzazione

 

 

 

 

Imposta mediante gli encoder i due numeri da sommare

 

 

 

Preme start

Legge gli encoder e in funzione del loro valore, invia le biglie all’addizionatore

Aspetta 5 secondi

 

 

 

Legge i sensori di ingresso

 

 

 

Esegue i calcoli

 

 

 

Invia le biglie ai condotti di uscita

 

 

 

 

Legge i sensori di ingresso

 

 

 

Scrive sul display il risultato

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Capitolo 1: Il lavoro nell’insieme

 

1.0: Finalità del progetto

1.1: Schema a blocchi e logica di funzionamento

1.2: Dettaglio del blocco aritmetico

1.3: Dettaglio del blocco di visualizzazione

1.4: L’elettronica nel sistema

1.5: La scelta del microprocessore

 


1.0: Finalità del progetto

 

 

1) Vogliamo realizzare una macchina addizionatrice avente la stessa logica di funzionamento di una calcolatrice elettronica ma che utilizzi, al posto degli elettroni, delle biglie di vetro.

 

2) Le biglie saranno fornite da un'apparecchiatura progettata e        realizzata dagli studenti del corso di elettrotecnica (nella figura seguente sono rappresentati i blocchi PLC e Contenitore biglie).

 

3) Attraverso  l'uso di ENCODER imposteremo le cifre, cioè le biglie, da addizionare.  

 

4) L’addizionatore calcolerà e visualizzerà il risultato sul display (due display a 7 segmenti).
1.1:      Schema a blocchi

 

 

Lo schema a blocchi del nostro sistema è il seguente:

 

                             

 

Il gruppo degli elettrotecnici si è occupato della progettazione e realizzazione del primo blocco, noi del resto. Nel seguito di questo documento verrà esposta solo la parte di cui ci siamo occupati, lasciando all’altra classe la documentazione del resto.

 

 

L’addizionatore è costituito da due blocchi:

                       

Il primo blocco esegue  fisicamente i calcoli, il secondo decodificala configurazione di biglie e visualizza il risultato.

Il ruolo dell’elettronica nel nostro progetto è esclusivamente di supporto al circuito addizionatore  che è completamente meccanico.


1.2: Dettaglio del blocco aritmetico

 

 

L’addizionatore vero e proprio si basa sullo stesso principio di funzionamento di qualsiasi addizionatrice elettronica, con l’unica differenza che al posto degli elettroni (corrente elettrica) noi utilizziamo biglie di plastica, decisamente più visibili. Il metodo che abbiamo utilizzato è lo stesso che si adopera per fare le addizioni con carta e penna, ad esempio volendo addizionare 47 con 84 si procede come segue:

 

       Riporto 11

                47+

                84

               ¾¾¾

               131

 

Si addizionano le cifre una colonna per volta e si trasporta l’eventuale riporto alla colonna successiva. In base DUE il meccanismo è lo stesso con l’unica differenza che 1+1=2 cioè 1 unità  

 

    Riporto  1

    Cifra A  101+

    Cifra B  001=

             ____

Risultato    110

 

Ovvero      5+1=6           (c.v.d)

Dato quanto sopra, lo schema logico del nostro addizionatore è quello classico realizzato con Full-adder.


 


Per ridurre la complessità del sistema ci siamo limitati a utilizzare numeri di 3 bit. Un Full adder elettronico, per il suo funzionamento, utilizza un flusso di elettroni. Nel nostro addizionatore “rotolante” utilizzeremo delle biglie di vetro. Nell’insieme il sistema può essere

schematizzato come nella dalla figura seguente:

 

 

 

 

 

 

 


 


La presenza delle biglie nei vari settori sarà letta da sensori; il flusso attraverso i vari condotti sarà regolato da sportellini collegati ad elettrocalamite.

A controllare e gestire il tutto sarà un microprocessore.


1.3: Dettaglio del blocco di visualizzazione

 

 

Il blocco addizionatore della Piccola MARTA non genera numeri, ma biglie opportunamente instradate e pilotate dalle elettrocalamite. Una calcolatrice deve però avere un display per il risultato, operazione compiuta dal blocco di visualizzazione.

Lo schema a blocchi seguente da un idea del  sistema:

 

SCHEMA N° 1

Le biglie scorrono in tubi. Ogni tubo è dotato di un sensore per individuare l’eventuale presenza della pallina. I sensori sono collegati alla scheda con un microprocessore come indicato nella figura seguente:

 

SCHEMA N° 2

 

 

 

Come possiamo vedere dallo schema n° 2, ci sono quattro tubi in cui in ognuno scorre una biglia. I sensori successivamente provvedono a trasformare i dati ricevuti in valori binari (ovverosia A, B, C e D), ognuno dei quali è collegato “idealmente” al tubo corrispondente: Tubo N° 1 = A; Tubo n° 2 = B; Tubo n° 3 = C e Tubo n° 4 = D.

In pratica, quando in un tubo scorre una biglia il sensore segnala il passaggio della biglia assegnando il valore 1 all’entrata binaria o, viceversa, assegna il valore 0. Queste quattro biglie, divenuti valori binari grazie ai sensori, vengono gestite dal microprocessore.

Nello schema seguente, lo schema n° 3, possiamo vedere nello specifico il collegamento del Microprocessore con i driver per i display a 7 segmenti.


SCHEMA N° 3

Il microprocessore provvede quindi, come detto in precedenza, a convertire il risultato,  fornito dai full-adder sotto forma di biglie, in un numero decimale leggibile su display. Avremmo potuto pilotare     i display con il PIC, ma sarebbero serviti almeno 14 piedini di I/O     (7 per ogni display). Abbiamo ridotto però il numero di pin di I/O necessari utilizzando dei driver TTL, i 7447 che necessitano di soli     4 bit per ognuno il loro pilotaggio. Un ulteriore vantaggio di questa scelta è la riduzione della complessità del codice nel micro:              la conversione da BCD a 7 segmenti è implementata direttamente negli integrati.


1.4: L’elettronica nel sistema

 

 

Schema a blocchi del sistema:

 


 

 


Come già accennato in precedenza, l’elettronica nel nostro elaborato ha il solo compito di pilotare la parte meccanica. Il sistema è completamente modulare: modulo distributore, modulo addizionatore e modulo visualizzatore. Dal punto di vista meccanico il più complesso è l’addizionatore, anch’esso composto a sua volta da tre moduli full-adder identici tra loro.



Ogni modulo ha la seguente struttura:

 

 


In realtà è necessario interporre tra il μp e gli attuatori dei circuiti di potenza in grado di erogare tutta la corrente richiesta dalle elettrocalamite, come indicato nel seguente schema a blocchi:

 



1.5: La scelta del microprocessore

 

 


Il sistema di controllo della Piccola MARTA può essere gestito da uno o più microprocessori con il compito di leggere i sensori e pilotare le elettrocalamite per l’instradamento delle biglie.

Nel caso di un solo microprocessore per il pilotaggio dei tre addizionatori, come  nello schema sotto riportato (Schema n°1), il numero dei sensori (e quindi degli ingressi) è abbastanza grande. Allo stesso modo sono numerose anche le uscite che collegano il microprocessore ad ogni elettromagnete, necessario per istradare la biglia all’uscita dell’addizionatore.

 

SCHEMA N° 1

 

 

Schema  elettronico di un addizionatore a microprocessore unico, con nove ingressi (sensori: S1, S2, ecc…), e 18 uscite, a gruppi di tre, formati ognuno da 5 elettromagneti

Questo approccio ha il vantaggio di utilizzare un solo microprocessore, anche se con molti pin di I/O, ma ha bisogno di un firmware complesso. Come alternativa, per ridurre la complessità del codice, possiamo pilotare ogni singolo full-adder con un microprocessore. Ognuno dei tre microprocessori riceve il segnale da tre sensori in ingresso, e successivamente lo trasmette a 5 elettromagneti, diminuendo il numero di I/O su un microprocessore unico. Nella “Piccola Marta” abbiamo scelto questo secondo approccio, sicuramente più semplice ottenendo un utile vantaggio accessorio: volendo aumentare il numero di bit della calcolatrice basta aggiungere un modulo identico ai precedenti. Segue lo schema a blocchi della soluzione adottata:

Schema elettronico a blocchi di un  singolo full- adder , formato da  sensori meccanici (interruttori), un microprocessore, ed elettromagneti.


 

 

 

 

 

 

 

 

 

 

 

 

Capitolo 2: L’hardware

 

2.1: La struttura meccanica dell’addizionatore

2.2: La struttura meccanica del visualizzatore

2.3: Le schede con microprocessore

2.4: Il controllo delle elettrocalamite
2.1: LA STRUTTURA MECCANICA DELL’ADDIZIONATORE

 

 

Per chiarire la funzione della struttura della “Piccola Marta” è opportuno partire dal metodo che si utilizza per fare l’addizione binaria (si veda il paragrafo 1.2): è necessario utilizzare un particolare circuito chiamato FULL-ADDER che ha la seguente   tabella di verità.

 

 

TABELLA DI VERITÀ:

 

Carry in

A

B

Risu (unità)

Carry out

 

 

 

 

 

0

0

0

0

0

0

0

1

1

0

0

1

0

1

0

0

1

1

0

1

1

0

0

1

0

1

0

1

0

1

1

1

0

0

1

1

1

1

1

1

 

 

Nella nostra apparecchiatura gli “UNI” sono rappresentati da “presenza di una biglia”, gli “ZERI” dalla sua assenza. Possiamo leggere la tabella di verità nel modo “classico”, cioè: una certa combinazione di ingresso ne fornisce una di uscita, oppure nel  “modo a palle”.

Per chiarire questo nuovo modo si consideri ad esempio la 2° riga: 00110 che può essere letta come: se in ingresso ho una biglia nel solo canale B, questa biglia deve andare nel canale di uscita S. Dato quanto sopra il nostro dispositivo meccanico dovrà avere come l’equivalente elettrico, 3 ingressi          e 2 uscite:

Gli ingressi sono le cifre dei 2 addendi e il riporto dal blocco precedente, le uscite saranno la cifra delle unità (S) e il riporto da trasportare al blocco successivo.

Per indirizzare le biglie nei giusti percorsi avremmo potuto progettare e realizzare un dispositivo completamente privo di componenti elettronici ma avrebbe comportato una progettazione meccanica non richiesta dal nostro curriculum e, per altro, non avrebbe permesso di applicare le conoscenze elettroniche tipiche del nostro corso di studi.       Abbiamo quindi adottato la seguente strategia: con dei sensori elettromeccanici si verifica la presenza delle biglie nei condotti all’ingresso e, in funzione alla tabella di verità un microprocessore    le convoglierà nei condotti di uscita opportuni.

 

 

 

Lo schema della figura precedente è però solo di principio: la struttura meccanica deve permettere il corretto percorso delle biglie, l’elettronica deve poter agire sugli attuatori meccanici in modo da instradare correttamente il nostro “macro elettrone”.


Nella figura seguente si nota la biglia che dal canale d’ingresso B viene indirizzata sull’uscita Risu.

 

 

I sensori sono semplici interruttori di fine-corsa. Per sincronizzare il movimento delle biglie si utilizzano delle elettrocalamite che dividono il condotto di ingresso in modo da permettere che la corretta identificazione dei condotti liberi e di quelli occupati. Si nota anche   la presenza di un uscita supplementare: SCARTO. Ciò risulta necessario in quanto la configurazione di ingresso 111 (ovvero         3 biglie d’ingresso), prevede un uscita con due soli 1, cioè 2 biglie, questo implica che è necessario scartarne una.
2.2: La struttura meccanica del visualizzatore

 

Discende immediatamente dallo schema logico:

I tubi di ingresso accolgono le biglie risultanti dal calcolo eseguito dal full-adder a monte; i sensori sono montati direttamente su tubi e, come per gli stadi precedenti, non sono altro che micro-interruttori fine corsa. Il microprocessore legge gli ingressi, calcola la configurazione da inviare al display e, infine, scarica le biglie e si predispone per un nuovo calcolo.
2.3: Le schede con microprocessore

 

 

La Piccola Marta fa largo uso di processori:

·        Uno per il distributore di biglie

·        Tre per i full-adder

·        Uno per il visualizzatore del risultato

 

Tutti i processori sono della famiglia PIC: il distributore usa dei PIC16F876, gli altri blocchi dei PIC16F84. La differenza sostanziale tra i due modelli e il numero di piedini di I/O: 13 per l’F84, 22 per l’876. Seguendo gli schemi elettrici delle 3 schede riportati nel seguito di questo paragrafo, si nota che il nucleo centrale con il processore è praticamente identico, come identici sono i driver per le elettrocalamite (i BJT BDX53). Ovviamente le 3 schede hanno poi caratteristiche proprie. Il blocco distributore deve poter leggere gli encoder di ingresso e il blocco visualizzatore deve gestire il display.


2.3.1: Schema elettrico distributore

 

 

 

 

Gli encoder sono collegati ai connettori  a destra nello schema;

Dal punto di vista logico forniscono una cifra BCD, dal punto di vista elettrico generano una tensione alta in corrispondenza dei bit=1.      Il loro schema equivalente è riportato nella figura seguente:

 

 

 

 

 

 

 


 

 


Per evitare di lasciare flottanti gli ingressi aperti sulla scheda sono state inserite delle resistenze verso massa.

Il pilotaggio delle elettrocalamite avviene con dei BJT tipo Darlington di potenza BDX53. Per i dettagli si veda il paragrafo 2.4.

 

 

 


2.3.2: Schema elettrico full-adder

 

Come accennato precedentemente, il full-adder deve leggere i sensori e azionare le bobine.

Nello schema i sensori sono rappresentati dai pulsanti a sinistra: se la biglia è presente il pulsante è chiuso e al microprocessore arriva un 1 logico, altrimenti arriva uno 0.

Il pilotaggio delle elettrocalamite è identico a quello dello stadio precedente.

In questo schema sono evidenziati anche i diodi di ricircolo per la protezione dei BJT dalle di extratensioni di apertura. I diodi LED permettono di vedere la sequenze delle bobine attivate durante il calcolo.

2.3.3: Schema elettrico visualizzatore

 

 

 

Il visualizzatore, come il full adder, deve leggere i sensori di ingresso (a sinistra dello schema). Il pilotaggio dei display avviene tramite i driver TTL 74LS47. Si nota che nel driver delle decine (in alto nello schema) abbiamo collegato al PIC solo l’ingresso A, mentre gli altri sono collegati a massa, questo perché essendo “14” il risultato massimo che può dare la nostra “calcolatrice”, la cifra delle decine può valere soltanto 1 o 0; questo ci ha permesso di ridurre il numero di pin di I/0 del processore, quindi  di usare il PIC più piccolo (16F84).

Dopo aver visualizzato il risultato bisogna scaricare le biglie.

Anche in questo caso, per risparmiare piedini di I/0 siamo ricorsi a un buffer unico, il 74HC245 in grado di erogare per ogni piedino di uscita 20 ma, più che sufficienti per pilotare i BJT di uscita, i soliti BDX53


2.4: Il controllo delle elettrocalamite

 

La progettazione del driver di potenza per il pilotaggio delle elettrocalamite presenti nella nostra addizionatrice, pur non avendo presentato difficoltà di rilievo, non è stato banale: gli elettromagneti che abbiamo utilizzato, dovendo muovere parti meccaniche di una certa consistenza, hanno bisogno di una potenza di pilotaggio non trascurabile: alimentate a circa 20 Volt assorbono quasi 3 Ampere e il pilotaggio di carichi fortemente induttivi ha i noti problemi di extratensioni di apertura.

Bisogna considerare ancora che il nostro microcontrollore può erogare un massimo di 30 mA per ogni piedino di IO. Questo implica che, utilizzando come elemento di potenza un normale BJT, questo dovrebbe avere un guadagno di corrente di almeno 150, se non 200 per sicurezza, ma con correnti dell’ordine di qualche Ampere dovremmo scegliere componenti molto costosi. La soluzione ottimale appare essere l’uso di MOSFET di potenza: non caricano il circuito pilota e la piccola resistenza di canale, che implica una modesta caduta di tensione in stato di saturazione, ha la utile conseguenza di una limitata potenza dissipata sul componente.

Non avendo in laboratorio componenti di questo genere e avendo già maturato precedente esperienza con i BDX 53, BJT di potenza in configurazione Darlington, considerando che sue le caratteristiche principali

Vce max            = 80 V

Ic max              = 8 A

Pd max             = 60 W

Hfe min            = 750

Vce sat max       = 2 V

 

ben si adattano alle nostre esigenze, abbiamo optato per questo driver.

 

Lo schema di pilotaggio è un classico:

 

 

 

 

Si nota il diodo di ricircolo messo in antiparallelo al carico per smorzare le correnti residue nelle bobine che provocherebbero, in mancanza dei diodi, come accennato sopra, notevoli extratensioni nella fase di apertura del circuito con conseguente distruzione dell’elemento attivo.

Il calcolo della Rb non presenta difficoltà: dall’equazione alla maglia di ingresso:

 

Vin = Rb * Ib + Vbe

 

si ottiene

          

               Vin - Vbe

Rb = -----------------------------

                   Ib

 

Sostituendo i valori:

Vin = 5 V (La tensione di uscita dai piedini di IO del PIC)

Ib = Ic / hfe

 

             5 – 1.4

Rb = ------------------- = 900 Ohm

             3 / 750

 

In pratica è meglio utilizzare un valore inferiore e noi abbiamo scelto 470 Ohm.

Con questo valore la corrente erogata dal Pic è di 7.5 mA, sicuramente compatibile con le possibilità del controllore.

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

Capitolo 3:  Il firmware nel PIC

 

3.1: Controllo addizionatore: linee guida

3.1.1: Diagramma di flusso

3.1.2: Main program

3.1.3: Temporizzazioni

3.2: Controllo visualizzatore: linee guida

        3.2.1: Diagramma di flusso

        3.2.2: Analisi dei blocchi più significativi
3.1:  Controllo addizionatore - linee guida

 

 

Il software del PIC è stato pensato dopo un’attenta analisi delle prestazioni richieste dal dispositivo. Si è quindi proceduto a creare una struttura del programma che consentisse la successione dei passaggi necessari alle diverse operazioni di addizionamento.   Tenuto conto delle caratteristiche dell’apparato e della meccanica    di costruzione dello stesso, si è dovuto prendere in considerazione  sia i tempi che le sequenze così da ottenere materialmente l’addizionamento dei dati immessi in ingresso. Si tratta quindi          di implementare un vero e proprio full-adder meccanico dove gli elettroni vengono simulati dalle biglie mentre le varie porte logiche sono state realizzate tramite sportelli azionati da elettrocalamite comandate, a loro volta, dal software che pilota dei transistor di potenza.

 

Potenzialità del linguaggio scelto

 

Il linguaggio di programmazione C ben si è prestato alle esigenze richieste dal full-adder. Infatti, per chi è avvezzo a questo linguaggio, risulta assai intuitivo intervenire sui vari blocchi software e modificare le diverse funzionalità richieste. Tenuto conto di ciò si è prestata particolare attenzione alla descrizione della funzione svolta da  ciascun singolo blocco, contribuendo così alla facilità d’intervento     in qualunque momento, qualora si rendesse necessario apportare    le modifiche al programma stesso.

3.1.1: Diagramma di flusso

Il segnale “ready” è necessario in quanto le biglie hanno bisogno di tempo perché si portino nella giusta posizione, esse dovranno quindi partire solamente quando lo stadio precedente ha terminato.

Segue l’analisi dei blocchi principali.

Leggo sensori biglie

 

 

In ogni canale, dove scorrono i nostri elettroni giganti, ci sono dei sensori che indicano se è presente o meno una biglia. Si acclude un disegno esplicativo circa la funzione e il posizionamento dei sensori  di finecorsa.

 

 

 

Esegui somma dei bit d’ingresso

 

Il risultato così ottenuto, sarà in funzione della posizione delle biglie presenti. Il full_adder dovrà dare un risultato coerente con la tabella di seguito:


Tabella full adder

 

Carry in

A

B

Risu (unità)

Carry out

 

 

 

 

 

0

0

0

0

0

0

0

1

1

0

0

1

0

1

0

0

1

1

0

1

1

0

0

1

0

1

0

1

0

1

1

1

0

0

1

1

1

1

1

1

 

L’ultima riga della tabella (11111) presenta una caratteristica peculiare che non si verifica con gli elettroni; in ingresso si hanno     3 biglie mentre in uscita solamente 2; questo implica che devo scartarne una e, in fatti, il nostro full_adder ha un’uscita che un full_adder non ha: lo scarto.

 


Attiva sequenza apertura sportelli

 


La sequenza viene attivata dal segnale di start in arrivo dallo stadio precedente. Si dovrà eseguire la lettura della configurazione sullo stato degli ingressi del microprocessore. A questo punto è possibile eseguire la computazione del processo di addizionamento al quale dovrà seguire l’invio dell’impulso di “fine conversione” per lo stadio successivo. La temporizzazione delle aperture e chiusure dei vari sportelli deve tenere conto della loro inerzia e dei tempi di rotolamento delle biglie.

 

Da questo momento in poi, ciò che il programma dovrà eseguire sarà una sequenza di operazioni di apertura e chiusura dei cinque sportelli a disposizione, le quali corrisponderanno alle vere e proprie operazioni di calcolo che simulano il passaggio degli elettroni.          Si avrà dunque una sequenza di aperture, ritardi e chiusure che potrebbe essere come il codice sotto riportato.

Come accennato in precedenza, dobbiamo considerare anche il caso particolare di una biglia da scartare, e questo non pone problemi.

Potrebbero invece presentarsi delle difficoltà nel caso servissero più biglie in uscita per lo stadio successivo rispetto a quante ne si hanno in ingresso. In questo caso avremmo dovuto prevedere una riserva di biglie locali. Fortunatamente questa evenienza non si verifica mai.    Al fine di rendere più chiaro quanto   sin qui esposto, si elenca qui   di seguito un esempio pratico di una porzione del programma sviluppato, con le relative operazioni e temporizzazioni.

Il codice in C è sostanzialmente identico a quanto riportato sopra:

 

   else if((a==1) && (b==0) && (cin==1))

   {

    apro_cout();

    piccolo_ritardo(1 secondo);

    apro_cin();              

    ritardo_lungo(3 secondi);

    chiudo_cout();

    chiudo_cin();

    piccolo_ritardo(1 secondo);

    apro_a();

 

    //Scarico la biglia

    ritardo_lungo(3 secondi);  

    chiudo_a();

    return;

   }

 

Che tradotto nel linguaggio corrente suona come:

·        apro sportello d’uscita

·        attendo che sia aperto

·        apro sportello di ingresso

·        do il tempo alle biglie di portarsi dall’ingresso all’uscita

·        chiudo gli sportelli ecc.

 

Nell’appendice n° 8 sono riportate le sequenze relative a tutte le

possibili condizioni di ingresso.


3.1.2: Main program

 

 

Il linguaggio scelto ci ha permesso di scrivere il codice del loop principale in modo decisamente semplice e chiaro:

 

for(;;)

{

    PORTB=0;

    aspetta_start();

    leggi_ingressi(&a, &b ,&cin);

    esegui_somma(a,b,cin);

    invia_start();

}

 

Per completezza segue la corrispondenza tra i pin di I/O del pic e le funzioni corrispondenti:

 

Interfacciamento

Direzione porte     

TRISA=0xff;     Entrata

TRISB=0x00;         Uscita

    

     Definizione degli ingressi

       A   = RA0

     B   = RA1

     Cin = RA2

     Start dallo stadio precedente: RA3

 

     Definizione delle uscite

     Apre A   = RB0

     Apre B   = RB1

     Apre Cin = RB2

     Apre uscita Risu = RB3

     Apre uscita Carry out = RB4

     End calcolo (start per lo stadio successivo): RB7

 

       RIEPILOGO

 

       RA0: In A

       RA1: In B

       RA2: Carry in

       RA3: Start dallo stadio precedente

 

       RB0: Out sportello ingresso A

       RB1: Out sportello ingresso B

       RB2: Sportello Carry in

       RB3: Sportello Out Risu

       RB4: Sportello Carry out

               RB5: nc

               RB6: nc

       RB7: End calcolo (Start per lo stadio successivo)


3.1.3: Temporizzazioni

 

Dall’esposizione precedente emerge che abbiamo bisogno di valutare brevi intervalli di tempo. Il nostro controllore è dotato di un timer interno per il calcolo dei ritardi e in un prodotto commerciale sarebbe la soluzione da adottare, ma per motivi didattici abbiamo raggiunto l’obbiettivo con un altro approccio che ci ha consentito di meglio raggiungere la finalità didattica del nostro addizionatore a biglie. In virtù di quanto detto, abbiamo utilizzato un ritardo di un millisecondo (1ms) come base dei tempi per tutti gli altri. Viene qui di seguito riportato il codice utilizzato:

 

/*========================================= 

Genera ritardo di un mlli secondo.

   Quarzo da 8 MHz. – Il numero di cicli è stato ricavato sperimentalmente con l’utilizzo

di un oscilloscopio.

=========================================*/

void rit_un_milli(void)

{

   int i;

 

   for(i=0;i<116;i++);

   

}

 

 

 

/*========================================= 

Genero un ritardo di RIT secondi

=========================================*/

void rit_secondi(int rit)

{

   int j,i;

  

   for(i=0;i<rit;i++);   

   {

    //devo aspettare un secondo

    for(j=0;j<1000;j++)

    {

         rit_un_milli();

    }

   }

}


3.2: Controllo Visualizzatore

 

 

Linee Guida

 

La nostra “Piccola MARTA”, per poter funzionare correttamente, ha bisogno di un dispositivo che ci faccia leggere il risultato del calcolo effettuato. Ed è proprio per questo che è stato realizzato il visualizzatore che, per l’appunto, ci permette di leggere il risultato in unità e decine, esattamente come si è abituati con le normalissime calcolatrici. Per far ciò è stato necessario programmare un microprocessore che legge gli ingressi delle biglie e, a sua volta, in base alla configurazione venutasi a creare, ci visualizzasse il risultato attraverso due display montati sullo stesso circuito, uno per le decine e l’altro per le unità. Per quanto riguarda la programmazione del microprocessore, abbiamo usato il linguaggio di programmazione C, molto utile per farci raggiungere il nostro obiettivo con le poche conoscenze di programmazione.


3.2.1: Diagramma di Flusso

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Spiegazione dei blocchi:

 

Aspetto lo START dallo stadio precedente

·        Prima che il programma  si avvii devo portare le biglie nella giusta posizione.

·        Avuto lo start dallo stadio precedente al nostro, partirà il programma.

 

Leggo gli ingressi

·        In base alla presenza delle biglie nei rispettivi canali, essi verranno letti come ingressi e, di seguito, codificati in “A”, “B”, “C” e “D”.

 

 

 

Accendo i display

·        Il nostro risultato, per poter essere visualizzato, ha bisogno di due display a sette segmenti. I nostri display formeranno quindi le cifre, una cifra per le decine e una per le unità.

 

Per pilotare un display a 7 segmenti avremmo dovuto scrivere una tabella di conversione per ogni cifra da visualizzare. L’utilizzo di un “7447”, un integrato che codifica automaticamente per il display a 7 segmenti, ci ha permesso di semplificar il firmware e di ridurre il numero di piedini di IO necessari.

 

 

 

3.2.2: Analisi dei blocchi più significativi

 

Il nostro sistema è un sistema formato da blocchi semplici, come l’attesa dello Start dallo stadio precedente, la lettura degli ingressi,   o l’accensione dei display.

L’unico blocco che richiede spiegazioni più dettagliate è quello relativo al Calcolo delle Uscite, esclusivamente perché è stato necessario scrivere una tavola di verità apposita per decodificare le uscite rispetto alla combinazione degli ingressi.          A questo punto sono stati scritti 16 differenti cicli “if” per far si che ogni combinazione assunta dalle biglie in entrata desse un risultato specifico, ovverosia la possibilità di visualizzare come risultato tutti i numeri, dallo 00 al 15.

Come detto in precedenza, le entrate sono state codificate come ingressi “a”, “b”, “c” e “d”; nel caso in cui la biglia passi, la rispettiva entrata otterrà valore “1”, viceversa otterrà “0”.

Ad esempio, si supponga che siano presenti due biglie nei canali “c” e “d” => Risultato: 12

Devo quindi mostrare sul display delle decine la cifra “1” e su quello delle unità la cifra “2”.

Utilizzando lo stesso ragionamento, è stata quindi scritta la tavola di verità che segue.


 

Entrate

Uscite

Risultato

Decine - Unità

A

B

C

D

Decine

Unità

0

0

0

0

0b0000

0b0000

0 – 0

1

0

0

0

0b0000

0b0001

0 – 1

0

1

0

0

0b0000

0b0010

0 – 2

1

1

0

0

0b0000

0b0011

0 – 3

0

0

1

0

0b0000

0b0100

0 – 4

1

0

1

0

0b0000

0b0101

0 – 5

0

1

1

0

0b0000

0b0110

0 – 6

1

1

1

0

0b0000

0b0111

0 – 7

0

0

0

1

0b0000

0b1000

0 – 8

1

0

0

1

0b0000

0b1001

0 – 9

0

1

0

1

0b00010000

0b0000

1 – 0

1

1

0

1

0b00010000

0b0001

1 – 1

0

0

1

1

0b00010000

0b0010

1 – 2

1

0

1

1

0b00010000

0b0011

1 – 3

0

1

1

1

0b00010000

0b0100

1 – 4

1

1

1

1

0b00010000

0b0101

1 – 5

 

Nella pratica, il nibble delle decine è stato mappato nei quattro bit alti della porta B, quello delle unità del nibble basso.

 

 

 

Esempio dal codice

 

        //a=1, b=2, c=4, d=8

    if( (a==0)&&(b==0)&&(c==1)&&(d==1) )

    {

         // Decine = 1 --- Unità = 2

*out_47H=0b00010000; // nibble alto

         *out_47L=0b0010;     // nibble basso

         return;

    }


 

 

 

 

 

 

 

 

 

 

 

Capitolo 4: Il blocco distributore

 

4.1: Descrizione sintetica

4.2: Schema a blocchi

4.3: La scelta del microprocessore

4.4: La scheda elettronica

4.5: Il software nel PIC
4.1:  Descrizione sintetica

 

 

Come già indicato in precedenza questo blocco avrebbe dovuto essere sviluppato dagli alunni del corso elettrotecnici e ad esso doveva essere dedicata un’intera tesina; è stato invece realizzato da noi e, non essendo stato previsto col necessario anticipo, abbiamo potuto dedicargli solo il tempo indispensabile per ottenere un oggetto presentabile. Anche questo capitolo è decisamente succinto, ma il tempo è stato limitato anche per la stesura della documentazione.

L’obiettivo è stato di realizzare un manufatto funzionante e rispondente alle specifiche, senza poter badare ai particolari minuti. Riteniamo comunque di aver ottenuto un risultato più che accettabile.

 

 

4.2:  Schema a blocchi

 

 

In linea di massima il distributore può essere rappresentato dalla figura seguente:

 



Lo schema della figura precedente può essere espanso come segue:


Per ridurre la complessità meccanica non abbiamo utilizzato un grosso contenitore di biglie, ma dei tubi, uno per ogni bit delle cifre di ingresso, con una piccola riserva di biglie ognuno.

L’elettronica di potenza e gli attuatori (elettrocalamite) sono gli stessi utilizzati per l’addizionatore e illustrati nel paragrafo 2.4.


4.3: La scelta del microprocessore

 

 

Il microprocessore è della stessa famiglia di quella utilizzata nel resto del lavoro, ma dotato di un maggior numero di I/O, il PIC 16F876, infatti per questo caso il nostro sistema ha bisogno dei seguenti pin di IO:

4 +  4 piedini di ingresso per il collegamento dei due encoder,

1 piedino di ingresso per il pulsante start,

6 piedini di uscita,

1 piedino per l’eoc (end of convertion)

------------------------------

totale 16 IO pin

 

 

Abbiamo a disposizione due tipi di processori: il PIC16F84 e il PIC16F876; il primo ha solo 13 bit di IO, il secondo 22. Dobbiamo quindi scegliere necessariamente il secondo.


4.4: La scheda elettronica

 

 

Lo schema a blocchi è piuttosto semplice:


Il microprocessore deve

1.    Leggere gli encoder che abbiamo utilizzato al posto di una tastiera vera e propria, riducendo sensibilmente la complessità del sistema, senza per altro modificare la complessità logica.

2.    Attendere che l’utente dia il via all’operazione di somma con      il pulsante “start”

3.    Pilotare in modo opportuno le elettrocalamite.

 

Non avendo il microprocessore la potenza sufficiente per un pilotaggio diretto, abbiamo interposto un buffer di potenza costituito da transistor darlington  tipo BDX53, in grado di erogare fino a 8 Ampere.

 

 

4.5: Il Software Nel PIC

 

 

Linee Guida

 

La “Piccola MARTA”, come ogni calcolatrice che si rispetti, ha bisogno di un’interfaccia utente per l’input dei dati, in sostanza: la tastiera.

Per questo scopo, quindi, è stato ideato il “Distributore di Biglie”.

In pratica è un dispositivo da collegare alla nostra calcolatrice che, in base alle cifre presenti in ingresso, distribuisce le biglie nei canali opportuni. La “Piccola MARTA”, quindi, deve addizionare due cifre da 3 bit ciascuna.

 

 

 

Nella figura le entrate “a” e “b” rappresentano non solo le entrate delle biglie stesse, ma anche i bit con cui il nostro programma    andrà a lavorare.

 

Per esempio, se volessimo fare una somma di “5 + 3”, il nostro programma dovrà codificare il 5 e il 3 in binario per poi, in base ad esso, far entrare le biglie negli opportuni canali dell’addizionatore vero e proprio.

 

Cifra a = 5 = 101           Cifra b  =  3 = 011

 

 

A questo punto il nostro programma invierà le biglie per poi, successivamente, inviare lo Start allo stadio successivo.
4.6: L’algoritmo di Pilotaggio

 

 

Le operazioni che l’utente della “Piccola Marta” deve eseguire sono ovvie:

 

1)    L’utente imposta con gli encoder i due numeri a sommare;

2)    Preme START per avviare le operazioni di somma.

 

 

Risulta evidente che la complessità logica del programma è oggettivamente piuttosto modesta.

Nel diagramma precedente, l’unica operazione che potrebbe aver bisogno di qualche spiegazione è la decodifica dei valori letti dagli encoder. In realtà, siccome gli encoder che abbiamo scelto hanno l’uscita codificata BCD, l’operazione risulta banale essendo la stessa codifica prevista per gli ingressi della parte addizionatrice, quindi il tutto si riduce a leggere il valore BCD degli encoder e copiarlo sulle porte di uscita che pilotano le elettrocalamite.

 

 

 

 

 

 

 

 

 

 

 

Capitolo 5: I blocchi accessori

 

5.1: L’alimentatore

5.2: Schemi elettrici alimentatore

5.3: La scheda sonora


5.1: L’ alimentazione

 

 

In tutte le apparecchiature elettroniche bisogna porre molta cura alla progettazione dell’alimentatore. Nel nostro caso le difficoltà sono accentuate dalla considerazione che nella “Piccola Marta” abbiamo due esigenze opposte; i circuiti elettronici assorbono correnti piuttosto piccole e hanno bisogno di tensioni ben stabilizzate; al contrario le elettrocalamite assorbono grosse correnti senza necessità di stabilizzazione. Per ridurre i disturbi elettrici sarebbe bene utilizzare due alimentatori distinti, ognuno con il suo trasformatore; esigenze di semplicità ci hanno portato, però, ad utilizzare un unico trasformatore di grossa potenza. E’ evidente che abbiamo dovuto dedicare maggior attenzione alla riduzione dei disturbi.


 


Considerando che al massimo abbiamo 4 magneti che conducono contemporaneamente, e il trasformatore da utilizzare deve essere in grado di generare almeno 6  o 7A a 20V. Abbiamo quindi scelto un trasformatore da 24V AC-250VA, cioè una decina di Ampere, più che sufficienti per le nostre esigenze. Per alimentare le bobine non sarebbe necessario raddrizzare la tensione di uscita, ma dalle prove effettuate abbiamo verificato che alla massima potenza le bobine producono un ronzio fastidioso. Abbiamo quindi raddrizzato e livellato con una capacità di 10.000 mF. Questa grossa capacità ci facilita inoltre il compito di ridurre i disturbi che, come accennato sopra, potrebbero dare problemi di funzionamento al resto dell’elettronica.

 

Lo schema di questo primo blocco è un classico:

 

La tensione generata da questo primo blocco verrà poi distribuito agli altri stabilizzatori. Gli stabilizzatori più semplici da utilizzare sarebbero stati i 7805 e 7809, ma purtroppo la loro tensione massima di ingresso (35V) è inferiore alla tensione di picco raddrizzata fornita  dal trasformatore: (24V * √2 = 34V) ai quali è necessario aggiungere un buon 10% di tolleranza.

Siamo ricorsi agli LM317 che non presentano questi problemi:           il circuito è leggermente più complesso, ma in laboratorio avevamo già dei circuiti stampati disegnati apposta e questo ci ha permesso   di semplificare e velocizzare il lavoro.

 

5.2: Schemi elettrici

 

 

 


 

 


Per limitare la corrente assorbita delle elettrocalamite, abbiamo messo a punto un alimentatore da 25V, Imax = 5A. Siccome in questo caso non ci sono problemi di stabilizzazione, abbiamo potuto utilizzare come base ancora LM317 che pilota un BJT di potenza,      il 2N3055.

 


 

 

 


Nello schema della figura seguente si nota il dettaglio del circuito che limita la corrente massima.

 

 

 

I due diodi entrano in conduzione se la Vba supera 1.4V circa .

In pratica se la caduta di tensione ai capi di R3 è inferiore a 0.7V         i due diodi non conducono:

 

ΔV = 0.7A = R3 * Imax

R = 0.7 / Imax = 0.7 / 5 = 0.15 Ώ

 

Naturalmente la potenza dissipata da R non è trascurabile:

P = R * I² = 0.15 * 25 = 3.75W

 

Non avendo a disposizione una resistenza commerciale con queste caratteristiche la abbiamo costruita utilizzando del filo di rame di sezione opportuna, e del quale abbiamo calcolato la lunghezza necessaria. Avendo del filo da 0.5mm di diametro ® R = r (L / S)

                        0.15 * (0.25)2  * 3.14

L = R * S / C = -------------------------------

                                   1.68

 

Essendo r (RO) = 1.68

Abbiamo poi verificato sperimentalmente che il valore della resistenza cosi ottenuta fosse compatibile con quella calcolata.

 

Ø     Nota sulla sezione del filo utilizzato per la realizzazione della resistenza di protezione

La sezione è decisamente modesta (0.5² π = 0.785 mm² ) e se fosse un cavo di un impianto elettrico sarebbe sicuramente sottodimensionato: a causa della sua resistenza elevata ci sarebbero problemi di surriscaldamento, ma in questo caso l’unico parametro critico è la densità di corrente che scorre nel filo e nel nostro caso non è sicuramente elevata: 5/0.785 < 7 A/mmq

 

 

 

 

5.3: La scheda sonora

 

La nostra "Martina" riesce anche a suonare delle note musicali mentre esegue il calcolo!

L’idea è venuta per caso: gli studenti della classe terza stavano sviluppando un programmino didattico in grado di suonare “Fra Martino”. A qualcuno di noi è venuto in mente che si poteva accompagnare il calcolo con una musichina, non certo Fra Martino, ma comunque qualcosa di semplice. Ecco fatto: abbiamo deciso per “Charge Theme”, quindi cercato su Internet la sequenza esatta delle note e le loro frequenze e modificato il programma della terza.

Abbiamo fatto in modo che il pulsante di START, che avvia il processo di calcolo, avvia anche la riproduzione del motivetto MARTINA_THEME; il segnale che indica il termine, generato dall’ultimo full-adder, termina anche il motivetto.

 

Mini teoria musicale

 

Una nota musicale pura è un’onda sinusoidale caratterizzata da una specifica frequenza.

 

 

Con il nostro microcontrollore non è agevole generare un’onda sinusoidale, mentre è piuttosto semplice produrre un’onda quadra. La sua riproduzione sonora non è esattamente uguale a quella di un’onda sinusoidale, ma in questo caso la differenza è trascurabile.

 

Il periodo (P) è il tempo che passa dall’ inizio alla fine dell’onda e si calcola facendo l’inverso della frequenza.

 

 

Una volta deciso quale nota riprodurre, sul piedino di I/O si genera un’onda quadra alla frequenza desiderata.

 

Esempio:

 

Basta ora trovare lo spartito musicale del nostro motivetto. Non è stato facile, ma con l’ausilio di Internet e un po’ di fantasia siamo riusciti nell’intento.

Le frequenze delle note musicali sono facilmente reperibili in Internet, e il gioco è fatto:

 

Sequenza iniziale:

 

LA diesis    466 [Hz]

FA            349

SOL          392

LA            440

 

LA diesis    466

FA            349

SOL          392

LA            440

ecc

 

 

Implementazione software

 

Senza conoscere la musica particolarmente bene e senza scendere nei dettagli, si può definire uno spartito come una tabella dove ogni riga indica frequenza, durata e ampiezza dell’onda sonora da riprodurre. Dal pentagramma si può ricavare frequenza e durata:

 

 

 

L’ampiezza è data dalle notazioni sullo spartito: forte, fortissimo, piano, ecc.

 

Per la traduzione in un programma informatico basta una tabella di conversione tra note e frequenza. Volendo riprodurre fedelmente uno spartito sarà sufficiente fissare la durata base e quindi dividerla per metà, per un quarto ecc; per l’ampiezza basta aumentare o diminuire l’ampiezza in volt della tensione di uscita.

Nel nostro caso si lavora ad ampiezza costante, ma l’effetto è ancora apprezzabile.

 

Per semplificare ulteriormente il codice abbiamo usato, in alternativa alla frequenza, il valore del semiperiodo espresso in decimi di millisecondo e, in vece della durata in battute o in secondi, il numero di cicli della forma d’onda da riprodurre. Alla fine il codice della routine utilizzata è il seguente:

 

/*====================================================

=====================================================*/

void suona(int semiperiodo_decimi, int durata_ncicli)

{

     int i;

    

     for(i=0;i< durata_ncicli;i++)

     {

         RB7 = 1; //IO port utilizzata

         rit_n_decimilli(semiperiodo_decimi);

         RB7 = 0;

         rit_n_decimilli(semiperiodo_decimi); 

     }

}

 

 

Utilizzare il codice precedente è molto semplice: per riprodurre una nota è sufficiente richiamare la funzione suona() con i parametri voluti, ad esempio: suona(35,120) che suona una nota DO bassa per 120 periodi.

 

La prima parte della tabella utilizzata nel nostro programma è la seguente:

 

NOTA

Semiperiodo

in decimi di millisecondo

Durata in numero di cicli

Ampiezza in volt

DO

35

130

5

RE

32

120

5

MI

30

140

5

FA

27

170

5

SOL

26

170

5

LA

23

150

5

SI

20

160

5

DO

19

130

5

 

 

Che tradotto in C diventa:

 

suona(21, 160); //466 hz LA diesis

rit_n_milli(30);

 

suona(29, 140); //349 hz FA

rit_n_milli(30);

suona(26, 170); //392 hz SOL

rit_n_milli(30);

 

suona(23, 170); //440 hz LA

rit_n_milli(30);

 

suona(21, 160); //466 hz LA diesis

rit_n_milli(30);

 

suona(29, 140); //349 hz FA

rit_n_milli(30);

 

suona(26, 170); //392 hz SOL

rit_n_milli(30);

 

suona(23, 170); //440 hz LA

ecc

 

 

I ritardi di 30 millisecondi (rit_n_milli(30)) tra una nota e l’altra servono per migliorare la resa sonora. Il valore di 30 ms è stato trovato sperimentalmente.

 


Appendici

 

1.                Schema Elettrico Full Adder.

2.                Schema Elettrico Distributore.

3.                Schema Elettrico Visualizzatore.

4.                PCB  Full Adder.    

5.                PCB  Distributore.

6.                PCB  Visualizzatore.

7.                Listato Full Adder .

8.                Flusso Full Adder

9.                Listato Distributore.

10.           Listato Visualizzatore.

11.           Listato Martina Theme

12.           Spartito di “Martina theme”
App. 1 - Schema Elettrico Full Adder

 


App. 2 - Schema Elettrico Distributore

 

App. 3 - Schema Elettrico Visualizzatore

   

App. 4 - PCB  Full Adder


App. 5 -  PCB  Distributore

 


 

App. 6 - PCB  Visualizzatore


App. 7 - Listato Full Adder

 

 

/***************************************************

 * Full_Adder.C

 * Autore: Brusco

 ***************************************************/

 

#include <pic.h>

 

void test(void);

 

void aspetta_start(void);

void leggi_ingressi(char *a, char *b, char *cin);

void esegui_somma(char a, char b, char cin);

void invia_start(void);

 

void ritardo_lungo(void);

void apro_risultato(void );

void piccolo_ritardo(void );

void apro_cin(void );

void chiudo_risultato(void );

void chiudo_cin(void );

void apro_cout(void );

void apro_b(void );

void chiudo_b(void );

void apro_a(void );

void chiudo_cout(void);          

void chiudo_a(void);

 

void rit_un_milli(void);

void rit_secondi(int rit);

            

/*========================================= 

Main program

=========================================*/          

void main(void)

{

       char a,b,cin;

      

 

       //Direzione porte  

       TRISA=0xff;  // Entrata

       TRISB=0x00; // Uscita

      

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

         Definizione degli ingressi

        

         A   = RA0

         B   = RA1

         Cin = RA2

         Start dallo stadio precedente: RA3

 

         Definizione delle uscite

 

         Apre A   = RB0

         Apre B   = RB1

         Apre Cin = RB2

         Apre uscita Risu = RB3

         Apre uscita Carry out = RB4

         End calcolo (start per lo stadio successivo): RB7

        

        

         RIEPILOGO

      

         RA0: In A

         RA1: In B

         RA2: Carry in

         RA3: Start dallo stadio precedente

 

         RB0: Out sportello ingresso A

         RB1: Out sportello ingresso B

        RB2: Sportello Carry in

        RB3: Sportello Out Risu

        RB4: Sportello Carry out

              RB5: nc

        RB6: nc

        RB7: End calcolo (Start per lo stadio successivo)     

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

       //test();

                                               

       for(;;)

       {

             PORTB=0;

             aspetta_start();

             leggi_ingressi(&a, &b ,&cin);

             esegui_somma(a,b,cin);

             invia_start();

       }

      

}

 

/*========================================= 

  Attesa tempo necessario per l'avvio

=========================================*/

void aspetta_start(void)

{

       for(;;)

       {

             if(RA3!=0) return;

       }     

}

 

 

 

/*================================================================

  Lettura della configurazione sullo stato degli ingressi

 

  RA0: In A

  RA1: In B

  RA2: Carry in

=================================================================*/

void leggi_ingressi(char *a, char *b, char *cin)

{

       if(RA0!=0)

              *a=1;

       else

             *a=0;

      

       if(RA1!=0)

             *b=1;

       else

             *b=0;

      

       if(RA2!=0)

             *cin=1;

       else

             *cin=0;     

                   

       return;

}

 

/*===================================================

  Computazione del processo di addizionamento

=====================================================*/

void esegui_somma(char a, char b, char cin)

{

       if((a==0) && (b==0) && (cin==0))

       {

             ritardo_lungo();

             return;

       }

       else if((a==0) && (b==0) && (cin==1))

       {

             apro_risultato();

             piccolo_ritardo();

             apro_cin();

             ritardo_lungo();

             chiudo_risultato();

             chiudo_cin();

             return;

       }

       else if((a==0) && (b==1) && (cin==0))

       {

             apro_risultato();

             piccolo_ritardo();

             apro_b();

             ritardo_lungo();

             chiudo_risultato();

             chiudo_b();

             return;

       }

       else if((a==0) && (b==1) && (cin==1))

       {

             apro_cout();

             piccolo_ritardo();

             apro_cin();        

             ritardo_lungo();

             chiudo_cout();

             chiudo_cin();

             piccolo_ritardo();

             apro_b();

             ritardo_lungo();

             chiudo_b();

             return;

       }

       else if((a==1) && (b==0) && (cin==0))

       {

             apro_risultato();

             piccolo_ritardo();

             apro_a();

             ritardo_lungo();

             chiudo_risultato();

             chiudo_a();

             return;

       }

       else if((a==1) && (b==0) && (cin==1))

       {

             apro_cout();

             piccolo_ritardo();

             apro_cin();                      

             ritardo_lungo();

             chiudo_cout();

             chiudo_cin();

             piccolo_ritardo();

             apro_a();

             ritardo_lungo();   //Scarico la biglia//

             chiudo_a();

             return;

       }

       else if((a==1) && (b==1) && (cin==0))

       {

             apro_cout();

             piccolo_ritardo();

             apro_b();

             ritardo_lungo();

             chiudo_cout();

             chiudo_b();

             piccolo_ritardo();

             apro_a();

             ritardo_lungo();   //Scarico la biglia//

             chiudo_a();

             return;

       }

       else if((a==1) && (b==1) && (cin==1))

       {

             apro_risultato();

             piccolo_ritardo();

             apro_cin();

             ritardo_lungo();

             chiudo_risultato();

             chiudo_cin();

             ritardo_lungo();

             apro_cout();

             piccolo_ritardo();

             apro_b();

             ritardo_lungo();

             chiudo_cout();

             chiudo_b();

             ritardo_lungo();

             apro_a();

             piccolo_ritardo();

             chiudo_a();

             // la_pallina_va_nello_scarto

 

             return;

       }

}

 

/*===============================================================

  Invio d'impulso di ready al termine del processo

=================================================================*/

void invia_start(void)

{

       RB7=1;

       rit_secondi(4);

       RB7=0;             

}

 

/*=================================================================  

  Ritardo per permettere alla biglia di rotolare fino alla buca

==================================================================*/ 

void ritardo_lungo(void)

{

       rit_secondi(6);

}

 

/*=================================================== 

  Apro lo sportello d'uscita del risultato

=====================================================*/

void apro_risultato(void )

{

       RB3=1;

}

 

/*=============================================================== 

   Ritardo per garantire la chiusura completa dello sportello

================================================================*/

void piccolo_ritardo(void)

{

       int j;

       for(j=0;j<1000;j++)

       {

             rit_un_milli();

       }

}

 

/*========================================= 

  Apro lo sportello del Carry in

=========================================*/

void apro_cin(void)

{

       RB2=1;

}

 

/*========================================= 

  Chiudo lo sportello del risultato

=========================================*/

void chiudo_risultato(void )

{

       RB3=0;

}

 

/*========================================= 

  Chiudo lo sportello del Carry in

=========================================*/

void chiudo_cin(void )

{

       RB2=0;

}

 

/*========================================= 

  Apro lo sportello Carry out

=========================================*/

void apro_cout(void )

{

       RB4=1;

}

 

/*========================================= 

  Apro lo sportello dell'ingresso B

=========================================*/

void apro_b(void )

{

       RB1=1;

}

 

/*========================================= 

  Chiudo lo sportello dell'ingresso B

=========================================*/

void chiudo_b(void )

{

       RB1=0;

}

 

/*========================================= 

  Apro lo sportello dell'ingresso A

=========================================*/

void apro_a(void )

{

       RB0=1;

}

 

/*========================================= 

  Chiudo lo sportello del Carry out

=========================================*/

void chiudo_cout(void)

{

       RB4 = 0;

}

 

/*========================================= 

  Chiudo lo sportello dell'ingresso A

=========================================*/

void chiudo_a(void)

{

       RB0 = 0;

}

 

/*========================================= 

  Genera ritardo di un mlli secondo

  Quarzo da 8 MHz

=========================================*/

void rit_un_milli(void)

{

       int i;

       for(i=0;i<116;i++);

}

 

/*========================================= 

  Genero un ritardo di RIT secondi

=========================================*/

void rit_secondi(int rit)

{

       int j,i;

       for(i=0;i<rit;i++);

       {

             //devo aspettare un secondo

             for(j=0;j<1000;j++)

             {

                    rit_un_milli();

             }

       }

}

 

 

/*========================================= 

  Programma di Test di tutte le uscite

=========================================*/

void test(void)

{

       for(;;)

       {

             if((RA0==1) && (RA1==0) && (RA2==0)) RB0=1;

             if((RA0==0) && (RA1==1) && (RA2==0)) RB1=1;

             if((RA0==0) && (RA1==0) && (RA2==1)) RB2=1;

             if((RA0==1) && (RA1==1) && (RA2==0)) RB3=1;

             if((RA0==1) && (RA1==1) && (RA2==1)) RB4=1;

             if((RA0==0) && (RA1==0) && (RA2==0)) PORTB=0;

       }

 

}


App. 8 - Fusso full-adder

 

 

Il funzionamento del Full-Adder si sviluppa secondo il seguente schema:

 

·        Aspetto lo “Start” per l’avvio del programma;

·        Leggo la configurazione di sistema;

·        Avvio le sequenza di apertura degli sportelli;

·        Invio “Start” al prossimo stadio.

 

Il Full-Adder, inoltre, utilizza la seguente “Tavola di Verità”:

 

Ingresso

Uscita

a

b

C(in)

C(out)

Risu

0

0

0

0

0

0

0

1

0

1

0

1

0

0

1

0

1

1

1

0

*

1

0

0

0

1

1

0

1

1

0

*

1

1

0

1

0

*

1

1

1

1

1

*

 

 

Note relative alla TDV

 

C(in)

Riporto in ingresso

C(out)

Riporto di uscita

Risu

Risultato

*

Una “pallina” di scarto

 

 

Tabella con “a = 0”

 



a

b

C(in)

 

C(out)

Risu

0

0

0

Þ

0

0

-- Ritardo --

-- Return --

0

0

1

Þ

0

1

Apro “Risu”      -- Piccolo Ritardo --      Apro “C(in)”

-- Ritardo --

Chiudo “Risu”       Chiudo “C(in)”

 

-- Return --

0

1

0

Þ

0

1

Apro “Risu”       -- Piccolo Ritardo --       Apro “b”

-- Ritardo --

Chiudo “Risu”       Chiudo “b”

 

-- Return --

0

1

1

Þ

1

0

Apro “Risu”       -- Piccolo Ritardo --       Apro “C(in)”

-- Ritardo --

Chiudo “Risu”       Chiudo “C(in)”

-- Ritardo --

Apro “C(out)”       -- Piccolo Ritardo --       Apro “b”

-- Ritardo --

Chiudo “C(out)”       Chiudo “b”

 

-- Return --

 

 

 

 

 

 

 

 

 

Tabella con “a = 1”

 



a

b

C(in)

 

C(out)

Risu

1

0

0

Þ

0

1

Apro “Risu”      -- Piccolo Ritardo --      Apro “a”

-- Ritardo --

Chiudo “Risu”      Chiudo “a”

 

-- Return --

1

0

1

Þ

1

0

Apro “Risu”      -- Piccolo Ritardo --      Apro “C(in)”

-- Ritardo --

Chiudo “Risu”       Chiudo “C(in)”

-- Ritardo --

Apro “C(out)”      -- Piccolo Ritardo --      Apro “a”

-- Ritardo --

Chiudo “ C(out)”        Chiudo “a”

 

-- Return --

1

1

0

Þ

1

0

Apro “Risu”      -- Piccolo Ritardo --      Apro “b”

-- Ritardo --

Chiudo “Risu”       Chiudo “b”

-- Ritardo --

Apro “ C(out)”      -- Piccolo Ritardo --      Apro “a”

-- Ritardo --

Chiudo “ C(out)”        Chiudo “a”

 

-- Return --

1

1

1

Þ

1

1

Apro “Risu”      -- Piccolo Ritardo --      Apro “C(in)”

-- Ritardo --

Chiudo “Risu”        Chiudo “C(in)”

-- Ritardo --

Apro “C(out)”      -- Piccolo Ritardo --      Apro “b”

-- Ritardo --

Chiudo “C(out)”         Chiudo “b”

-- Ritardo --

Apro “a”      -- Piccolo Ritardo --      Chiudo “a”

 

-- La palla va nello “Scarto” --

 

-- Return --

 

 


App. 9 - Listato Distributore

 

 

/**************************************************************

 * distributore.c

 * Autore Scarano ecc

 * pc. n 1

 **************************************************************/

 

#include <pic.h>

#include "ritardi.h"

 

void aspetta_start(void);

void leggo_encoder(char *encoder_a, char *encoder_b);

char traduco_encoder(char encoder_a);

void invio_uscite(char cifra_a, char cifra_b);

void invio_ready(void);

 

/*========================================================================      

   Operazione: a + b

   a & b sono due cifre binarie a tre bit: max a = 7, max b = 7

       

       RA0 = In per start

RA1 = Out per ready

 

       RB0 =  out per a0

       RB1 =  out per a1

       RB2 =  out per a2

       RB3 =  out per b0

       RB4 =  out per b1

       RB5 =  out per b2

 

       RC0 =  in per bit 0 dell'encoder relativo ad "a"

       RC1 =  in per bit 1 dell'encoder relativo ad "a"

       RC2 =  in per bit 2 dell'encoder relativo ad "a"

       RC3 =  in per bit 3 dell'encoder relativo ad "a"

 

       RC4 =  in per bit 0 dell'encoder relativo a "b"

       RC5 =  in per bit 1 dell'encoder relativo a "b"

       RC6 =  in per bit 2 dell'encoder relativo a "b"

       RC7 =  in per bit 3 dell'encoder relativo a "b"

 

=======================================================================*/

void main(void)

{

 

       char encoder_a, encoder_b;

       char cifra_a, cifra_b;

      

      

       //--------------------------------------------------------------------------

       // Definisco la direzione delle porte di IO

       //--------------------------------------------------------------------------

 

       //PORTA può essere convertitore AD o IO digitale

       ADCON1=0b00000111;  //Set PORTA come IO digitale

       //RA0 = In per start

       //RA1 = Out per ready

       TRISA = 0b11111101;

 

       //Port B out per elettrocalamite

       TRISB = 0x00;

      

       //Port C in per encoder

       TRISC = 0xff;

 

       PORTB=0x00;

       PORTA=0x00;

       //---------------------------------------------------------------

       // Main loop

       //---------------------------------------------------------------

       for(;;)

       {

             aspetta_start();

 

             leggo_encoder(&encoder_a, &encoder_b);

 

             cifra_a = traduco_encoder(encoder_a);

             cifra_b = traduco_encoder(encoder_b);

 

             invio_uscite(cifra_a, cifra_b);

             invio_ready();

       }

 

}

 

/*============================================================  

   Aspetto che venga premuto il tasto di START

   RA0 = In per start

=============================================================*/

void aspetta_start(void)

{

       for(;;)

       {

             if(RA0!=0)   return;

       }

            

}

 

/*===============================================================  

  Leggo gli encoder e riporto il valore

 

       RC0 =  in per bit 0 dell'encoder relativo ad "a"

       RC1 =  in per bit 1 dell'encoder relativo ad "a"

       RC2 =  in per bit 2 dell'encoder relativo ad "a"

       RC3 =  in per bit 3 dell'encoder relativo ad "a"

 

       RC4 =  in per bit 0 dell'encoder relativo a "b"

       RC5 =  in per bit 1 dell'encoder relativo a "b"

       RC6 =  in per bit 2 dell'encoder relativo a "b"

       RC7 =  in per bit 3 dell'encoder relativo a "b"

==================================================================*/

void leggo_encoder(char *encoder_a, char *encoder_b)

{

       char rc,basso,alto;

 

      

       rc=PORTC;

      

       basso = rc & 0b00001111;

       alto  = rc & 0b11110000;

       alto = alto >> 4;                

 

       *encoder_a = basso;

       *encoder_b = alto;

                   

}

 

 

/*=======================================================================  

   Traduco in binario i valori letti dall'encoder

   Al momento è è una sub inutile, ma in funzione dell'encoder usato,

   potrebbe essere necessaria.

=========================================================================*/

char traduco_encoder(char bit_letti)

{

       return(bit_letti);

}

 

/*=====================================================================  

  Attivo i magneti in funzione delle cifre lette dagli encoder

 

       RB0 =  out per a0

       RB1 =  out per a1

       RB2 =  out per a2

 

       RB3 =  out per b0

       RB4 =  out per b1

       RB5 =  out per b2

==================================================================*/

void invio_uscite(char cifra_a, char cifra_b)

{

       char out,app,i;

      

      

       cifra_a = cifra_a & 0b00000111;

       cifra_b = cifra_b & 0b00000111;

 

       cifra_b = cifra_b << 3;

      

       out = cifra_a | cifra_b;

      

       //PORTB = PORTB & 0b11000000;     //Salvo RB7 e RB6

       //PORTB = PORTB | out;

 

       app=PORTB;

       app = app & 0b11000000;    //Salvo RB7 e RB6

       app = app | out;

      

       PORTB = app & 0b11000001;

       rit_secondi(5);

 

       PORTB = app & 0b11000010;

       rit_secondi(5);

 

       PORTB = app & 0b11000100;

       rit_secondi(5);

 

       PORTB = app & 0b11001000;

       rit_secondi(5);

 

       PORTB = app & 0b11010000;

       rit_secondi(5);

 

       PORTB = app & 0b11100000;

       rit_secondi(5);

      

       PORTB=0;

 

}

 

/*=========================================  

   Attivo l'uscita READY per 5 secondi

   RA1 = Out per ready

=========================================*/

void invio_ready(void)

{

       RA1=1;

       rit_secondi(5);

       RA1=0;

}

 

 

 

/****************************************************************************

 * ritardi.c

 ***************************************************************************/

 

 

/*=============================================== 

  Ritardo da 1/1000 sec (quarzo da 5.000 MHz)

=================================================*/

void rit_un_milli(void)

{                                    

       int i;

       for(i=0;i<72;i++);

}

 

 

/*============================================= 

  Ritardo da 1/1000 sec (quarzo da 10.000 MHz)

===============================================*/

void rit_un_milli(void)

{                                    

       int i;

       for(i=0;i<150;i++);

}

 

/*========================================= 

  Ritardo da d * 1/10 sec

=========================================*/

void rit_decimi(int d)

{

       int i,j;

       for(j=0;j<d;j++)

       {

             for(i=0;i<100;i++)

             {

                    rit_un_milli();

             }

       }

}

 

/*========================================= 

   Ritardo da d * 1/100 sec

=========================================*/

void rit_centesimi(int d)

{

       int i,j;

       for(j=0;j<d;j++)

       {

             for(i=0;i<10;i++)

             {

                    rit_un_milli();

             }

       }

}

 

/*=========================================

  Ritardo da d secondi

=========================================*/

void rit_secondi(int d)

{

       rit_decimi(10);

}


App. 10 - Listato Visualizzatore

 

 

/*=========================================

 * visu_adder.c

 * Autore Ghiotti Daniele

 * pc. n 1

=========================================*/

 

#include <pic.h>

#include "ritardi.h"

 

 

void aspetta_start(void);

 

void leggo_ingressi(char *a, char *b, char *c, char *d);

void calcolo_uscite_decoder(char a, char b, char c, char d, char *out_47H, char *out_47L);

void accendi_display(char out_47H, char out_47L);    

void invio_ready(void);

 

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

   Schema IO port

 

       RA4 -> Input - Start dallo stadio precedente  

  

       RA3    -> Input - Bit 3 risu

       RA2    -> Input - Bit 2 risu

       RA1    -> Input - Bit 1 risu

       RA0    -> Input - Bit 0 risu

      

       RB7 -> Elettrocalamite per liberare le biglie una volta lette

       RB6 -> NC

       RB5 -> NV

       RB4 -> Output - A   digit decine

 

      

       digit unità

       RB3 -> Output - D

       RB2 -> Output - C

       RB1 -> Output - B

       RB0 -> Output - A

 

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

 

/*===============================================================

   Programma principale

=================================================================*/

void main(void)

{

       char a,b,c,d,out_47H,out_47L;

      

 

       TRISA=0xff;  //Port A tutta input

       TRISB=0x00; //Port B tutta output

      

       PORTB = 0;

       for(;;)

       {

             aspetta_start();

      

             //Quando arriva qui, è pronta una nuova configurazione

              //di palline da leggere

      

             leggo_ingressi(&a,&b,&c,&d);

             calcolo_uscite_decoder(a,b,c,d,&out_47H,&out_47L);

             accendi_display(out_47H,out_47L);

             invio_ready();                         

       }

      

}

 

 

/*========================================================

   Aspetto lo start in arrivo dallo stadio precedente.

=========================================================*/

void aspetta_start(void)

{

       for(;;)

       {

             if(RA4!=0) return;

       }     

}

 

/*=========================================

  Significato parametri.

  Ingressi:a,b,c,d --> Tubi di uscita delle palline

 

  Leggo gli ingressi e le palline rilevate dai sensori.

=========================================*/

void leggo_ingressi(char *a,

                    char *b,

                    char *c,

                    char *d)

{

 

       *d=0;

       *c=0;

       *b=0;

       *a=0;

 

       if(RA3!=0) *d=1;          

       if(RA2!=0) *c=1;

       if(RA1!=0) *b=1;

       if(RA0!=0) *a=1;

            

}

 

/*=========================================================================

  Significato parametri.

  Ingressi:a,b,c,d --> Tubi di uscita delle palline

  Uscite: out_47H rappresenta il valore relativo alle decine (0 e 1)

          out_47L rappresenta il valore relativo alle unità (da 0 a 9)

 

  Se la pallina passa, il valore nel rispettivo ingresso sarà = 1

  Se la pallina non passa, il valore nel rispettivo ingresso sarà = 0

  Successivamente avviene la decodifica per i rispettivi

  display: out_47H e out_47L

==========================================================================*/

void calcolo_uscite_decoder(char a,

                            char b,

                            char c,

                            char d,

                            char *out_47H,

                            char *out_47L)

{

 

       //a=1, b=2, c=4, d=8

       if( (a==0)&&(b==0)&&(c==0)&&(d==0) ) // Decine = 0 --- Unità = 0

       {

             *out_47H=0b00000000;

             *out_47L=0b0000;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==1)&&(b==0)&&(c==0)&&(d==0) ) // Decine = 0 --- Unità = 1

       {

             *out_47H=0b00000000;

             *out_47L=0b0001;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==0)&&(b==1)&&(c==0)&&(d==0) ) // Decine = 0 --- Unità = 2

       {

             *out_47H=0b00000000;

             *out_47L=0b0010;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==1)&&(b==1)&&(c==0)&&(d==0) ) // Decine = 0 --- Unità = 3

       {

             *out_47H=0b00000000;

             *out_47L=0b0011;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==0)&&(b==0)&&(c==1)&&(d==0) ) // Decine = 0 --- Unità = 4

       {

             *out_47H=0b00000000;

             *out_47L=0b0100;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==1)&&(b==0)&&(c==1)&&(d==0) ) // Decine = 0 --- Unità = 5

       {

             *out_47H=0b00000000;

             *out_47L=0b0101;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==0)&&(b==1)&&(c==1)&&(d==0) ) // Decine = 0 --- Unità = 6

       {

             *out_47H=0b00000000;

             *out_47L=0b0110;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==1)&&(b==1)&&(c==1)&&(d==0) ) // Decine = 0 --- Unità = 7

       {

             *out_47H=0b00000000;

             *out_47L=0b0111;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==0)&&(b==0)&&(c==0)&&(d==1) ) // Decine = 0 --- Unità = 8

       {

             *out_47H=0b00000000;

             *out_47L=0b1000;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==1)&&(b==0)&&(c==0)&&(d==1) ) // Decine = 0 --- Unità = 9

       {

             *out_47H=0b00000000;

             *out_47L=0b1001;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==0)&&(b==1)&&(c==0)&&(d==1) ) // Decine = 1 --- Unità = 0

       {

             *out_47H=0b00010000;

             *out_47L=0b0000;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==1)&&(b==1)&&(c==0)&&(d==1) ) // Decine = 1 --- Unità = 1

       {

             *out_47H=0b00010000;

             *out_47L=0b0001;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==0)&&(b==0)&&(c==1)&&(d==1) ) // Decine = 1 --- Unità = 2

       {

             *out_47H=0b00010000;

             *out_47L=0b0010;

             return;

       }

 

       if( (a==1)&&(b==0)&&(c==1)&&(d==1) ) // Decine = 1 --- Unità = 3

       {

             *out_47H=0b00010000;

             *out_47L=0b0011;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==0)&&(b==1)&&(c==1)&&(d==1) ) // Decine = 1 --- Unità = 4

       {

             *out_47H=0b00010000;

             *out_47L=0b0100;

             return;

       }

 

       //a=1, b=2, c=4, d=8

       if( (a==1)&&(b==1)&&(c==1)&&(d==1) ) // Decine = 1 --- Unità = 5

       {

             *out_47H=0b00010000;

             *out_47L=0b0101;

             return;

       }

 

       //-----------------------------------------------------------------------

       // Qui non dovrebbe arrivarci mai. Se ci arriva, metto tutto a zero

       *out_47H=0b00000000;

       *out_47L=0b0000;

 

       return;

 

}

      

/*==========================================================================

  Significato parametri.

  Uscite: out_47H rappresenta il valore relativo alle decine (0 e 1)

          out_47L rappresenta il valore relativo alle unità (da 0 a 9)

 

  Accendo i display e in basse ai valori della

  decodifica out_47H e out_47L, visualizzo il risultato.

 

       digit decine

       RB7 -> Output - D

       RB6 -> Output - C

       RB5 -> Output - B

       RB4 -> Output - A

      

       digit unità

       RB3 -> Output - D

       RB2 -> Output - C

       RB1 -> Output - B

       RB0 -> Output - A

===========================================================================*/

void accendi_display(char out_47H,

                                   char out_47L)

{

 

       PORTB = out_47H | out_47L;

 

}

 

 

 

 

 

 

 

 

/*=========================================  

   Attivo l'uscita READY per 5 secondi

   RA1 = Out per ready

=========================================*/

void invio_ready(void)

{

       RB7=1;

       rit_secondi(5);

       RB7=0;

}

 

 


App. 11 - Listato martina_theme

 

/******************************************************

 martina_theme.c

 autore: Rubini

******************************************************/

 

#include <pic.h>

 

void rit_un_milli(void);

void rit_n_milli(int n);

void rit_un_decimilli(void);

void rit_n_decimilli(int n);

void suona(int decimi, int ncicli);

 

/*============================================================

=============================================================*/

void main(void)

{

      

       TRISB = 0x00;

       TRISA = 0xff;

      

       for(;;)

       {

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

               Aspetto START (RA0)

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

             for(;;)

             {

                    if(RA0) break;

             }

                   

                          

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

               Se arriva STOP (RA1) esce dal ciclo

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

             for(;;)

             {

               rit_n_milli(600);

                    suona(21, 160); //466 hz LA diesis

                    if(RA1) break;

                   

                    rit_n_milli(30);

                    suona(29, 140); //349 hz FA

                    if(RA1) break;

                   

                    rit_n_milli(30);

                    suona(26, 170); //392 hz SOL

                    if(RA1) break;                   

 

                    rit_n_milli(30);

                    suona(23, 170); //440 hz LA

                    if(RA1) break;

                   

                   

                    rit_n_milli(30);

                    suona(21, 160); //466 hz LA diesis

                    if(RA1) break;

                   

                    rit_n_milli(30);

                    suona(29, 140); //349 hz FA

                    if(RA1) break;

                   

                    rit_n_milli(30);

                    suona(26, 170); //392 hz SOL

                    if(RA1) break;

                   

                    rit_n_milli(30);

                    suona(23, 170); //440 hz LA

                    if(RA1) break;

                   

                   

                    rit_n_milli(30);

                    suona(20, 160); //494 hz SI

                    if(RA1) break;

                   

                    rit_n_milli(25);

                    suona(27, 135); //370 hz FA diesis

                    if(RA1) break;

                   

                    rit_n_milli(25);

                    suona(24, 150); //415 hz SOL diesis

                    if(RA1) break;

                   

                    rit_n_milli(25);

                    suona(21, 150); //466 hz LA diesis

                    if(RA1) break;

                   

                   

                    rit_n_milli(25);

                    suona(20, 160); //494 hz SI

                    if(RA1) break;

                   

                    rit_n_milli(25);

                    suona(27, 135); //370 hz FA diesis

                    if(RA1) break;

                   

                    rit_n_milli(25);

                    suona(24, 150); //415 hz SOL diesis

                    if(RA1) break;

                   

                    rit_n_milli(25);

                    suona(21, 150); //466 hz LA diesis

                    if(RA1) break;

                   

                   

                    rit_n_milli(25);

                    suona(19, 130); //523 hz DO alta

                    if(RA1) break;

                   

                    rit_n_milli(20);

                    suona(26, 110); //392 hz SOL

                    if(RA1) break;

                   

                    rit_n_milli(20);

                    suona(23, 115); //440 hz LA

                    if(RA1) break;

                   

                    rit_n_milli(20);

                    suona(20, 115); //494 hz SI

                    if(RA1) break;

                   

                   

                    rit_n_milli(20);

                    suona(19, 125); //523 hz DO alta

                    if(RA1) break;

                   

                    rit_n_milli(20);

                    suona(26, 100); //392 hz SOL

                    if(RA1) break;

                   

                    rit_n_milli(20);

                    suona(23, 115); //440 hz LA

                    if(RA1) break;

                   

                    rit_n_milli(20);

                    suona(20, 115); //494 hz SI

                    if(RA1) break;

                   

                    rit_n_milli(20);

                    suona(19, 250); //523 hz DO alta

                    if(RA1) break;

                   

                   

                    rit_n_milli(500);

                    suona(38, 50); //262 hz DO

                    if(RA1) break;

                   

                    suona(30, 50); //330 hz MI

                    if(RA1) break;

                   

                    suona(26, 50); //392 hz SOL (semi-lunga)

                    if(RA1) break;

                   

                    suona(19, 200); //523 hz DO alta

                    if(RA1) break;

                                 

                    rit_n_milli(20);

                    suona(26, 50); //392 hz SOL (semi-lunga)

                    if(RA1) break;

                   

                    suona(19, 300); //523 hz DO alta

                    if(RA1) break;

                   

                    rit_n_milli(5000);

                    if(RA1) break;

      

             }

       }

                   

}

 

/*=====================================================================

   Aspetta 1 millisecondo

======================================================================*/

void rit_un_milli(void)

{

       int i;

      

      

       for(i=0; i<115;i++)

       {

            

       }

 

}

      

/*=====================================================================

   Aspetta 1 decimillisecondo

======================================================================*/

void rit_un_decimilli(void)

{

       int i;

      

       i=i;

       i=i;

       for(i=0; i<10;i++)

       {

            

       }

 

}

 

/*======================================================================

   Aspetta n millisecondi

======================================================================*/

void rit_n_milli(int n)

{  

       int i;

   

      

       for(i=0;i<=n;i++)

       {

             rit_un_milli();

       }

      

}

 

/*======================================================================

   Aspetta n decimillisecondi

======================================================================*/

void rit_n_decimilli(int n)

{  

       int i;

   

      

       for(i=0;i<=n;i++)

       {

             rit_un_decimilli();

       }

      

}

 

 

/*======================================================================

======================================================================*/

void suona(int decimi, int ncicli)

{

      

       int i;

      

      

       for(i=0;i<ncicli;i++)

       {

             RB7 = 1;

             rit_n_decimilli(decimi);

             RB7 = 0;

             rit_n_decimilli(decimi);  

       }

 

}

 

 


Appendice 12: Sequenza completa di “Martina Theme”

 

 

LA diesis                   466[Hz]

FA                              349

SOL                           392

LA                              440

 

LA diesis                   466

FA                              349

SOL                           392

LA                              440

 

SI                                494

FA DIESIS                370

SOL DIESIS              415

LA DIESIS                 466

 

SI                                494

FA DIESIS                370

SOL DIESIS              415

LA DIESIS                 466

 

DO 3a OTTAVA       523

SOL                            392

LA                              440

SI                                494

 

DO 3a OTTAVA       523

SOL                           392

LA                              440

SI                                494

DO 3a OTTAVA       523

 

DO                             262

MI                               330

SOL                           392

SOOOL                     392 (lunga)

MI                               330

SOOOOOOOL         392 (lunga)