35
Funzioni per un Elettrocardiografo Digitale Progettazione di dispositivi biomedici programmabili Docente Marco Knaflitz Zito Antonella matr.188795 - Gruppo di lavoro 09 a.a. 2012/2013

Design of programmable medical devices_Teamwork

Embed Size (px)

Citation preview

Funzioni per un Elettrocardiografo Digitale Progettazione di dispositivi biomedici programmabili Docente Marco Knaflitz

Zito Antonella matr.188795 - Gruppo di lavoro 09 a.a. 2012/2013

1

Sommario

Introduzione ...................................................................................................................................................... 2

1 Configurazione del circuito elettrico .............................................................................................................. 2

2 Specifiche di progetto ..................................................................................................................................... 5

3 Caratteristiche del segnale in ingresso ........................................................................................................... 5

4 Software ......................................................................................................................................................... 5

4.1 Inizializzazioni e impostazione del convertitore ...................................................................................... 7

4.2 Subroutine di risposta ad interrupt ....................................................................................................... 10

4.3 Funzione per la verifica della tensione di batteria ................................................................................ 12

4.4 Controllo dell’indirizzo di memoria ....................................................................................................... 17

4.5 Funzione per il campionamento di un segnale analogico ..................................................................... 19

4.6 Funzione per la lettura e la ricostruzione del segnale ........................................................................... 22

5 Appendice ..................................................................................................................................................... 27

Listato completo del software ..................................................................................................................... 27

2

Introduzione

Il progetto descritto in queste pagine ha come obiettivo quello di realizzare un circuito in grado di

svolgere alcune funzioni di un elettrocardiografo digitale. L’elemento circuitale principale è il

microcontrollore Atmel ATMEGA 8 opportunamente programmato in linguaggio Assembly. Per la

realizzazione delle varie funzioni, il microcontrollore è stato interfacciato con altri componenti

elettronici meglio specificati in seguito. La descrizione di ogni funzione è supportata dal flow-chart

e dal codice implementativo corrispondenti.

1 Configurazione del circuito elettrico

I componenti rilevanti del circuito sono:

Il microcontrollore a 8 bit Atmel ATMEGA 8

Il convertitore Digitale-Analogico DAC0808

L’amplificatore Operazionale TL081

Il microcontrollore è alimentato con una tensione costante (Vcc) di 5 V fornita attraverso il pin 7. Il

convertitore Analogico-Digitale interno al microcontrollore, che riceve in ingresso il segnale simil-

ECG e la tensione di batteria, è alimentato con una tensione costante di 5 V sul pin 20. Il

convertitore lavora con una tensione di riferimento interna misurata pari a 2,71 V. Il segnale simil-

ECG è portato in ingresso al convertitore tramite il pin 25, mentre la tensione di batteria arriva sul

pin 24 attraverso un partitore di tensione atto a dimezzare la tensione Vcc. L’alimentatore da

banco utilizzato è un Agilent E3631A.

I pin 15,16,17 settati come uscite comandano ciascuno l’accensione di un led.

A ciascuno dei pin 27 e 28, settati come ingressi, è collegato un interruttore per la selezione delle

modalità di funzionamento.

Il segnale convertito in digitale viene trasferito in parallelo al DAC0808 tramite i pin

2,3,4,5,6,11,12,13. Il DAC0808 è alimentato tra tensioni costanti di +5 V (sul pin 13) e -5 V (sul pin

3). La tensione di riferimento con cui lavora il DAC è la stessa utilizzata dal convertitore A/D e

viene direttamente prelevata dal pin 21 (AREF) del microcontrollore per essere condotta fino al

pin 14 del DAC.

Successivamente il segnale in uscita dal DAC0808 viene condotto all’amplificatore operazionale

TL081, il quale è alimentato tra tensioni costanti di +5 V (sul pin 7) e -5 V (sul pin 4). Al fine di

ridurre il rumore sul segnale in uscita dall’amplificatore sono stati utilizzati due condensatori (C8 e

C9) con capacità pari a 120 nF, collegati tra le rispettive tensioni d’alimentazione e la massa.

3

Figura 1. Configurazione del circuito elettrico

4

Figura 2. Foto del circuito

5

2 Specifiche di progetto

Le specifiche di progetto prevedono che il circuito sia in grado di svolgere le seguenti funzioni:

1. Verifica della tensione di batteria e generazione di un allarme visivo in presenza di tensione

troppo bassa.

2. Campionamento di un segnale analogico con frequenza pari a 500 Hz e memorizzazione dei campioni in SRAM.

3. Lettura dei campioni di segnale ECG in SRAM e trasferimento ad un convertitore D/A parallelo per ricostruire il segnale.

4. Variazione della velocità con la quale i campioni memorizzati sono portati al DAC.

3 Caratteristiche del segnale in ingresso

Il segnale in ingresso è un segnale simil-ECG ottenuto mediante un generatore di funzioni Agilent

33220A. La frequenza del segnale è stata impostata ad 1 Hz, mentre l’ampiezza picco-picco ad 1 V.

E’ stato introdotto un offset di 0.3 V in modo tale da evitare di inviare tensioni negative al

microcontrollore. Al fine di visualizzare correttamente la forma d’onda del segnale

sull’oscilloscopio è stata impostata la modalità con alta impedenza in uscita dal generatore di

funzioni.

4 Software

In questo capitolo verranno descritte singolarmente le varie parti che compongono il programma

caricato sul microcontrollore. Il listato del codice nella sua interezza è riportato in appendice.

L’implementazione è stata effettuata utilizzando il software AVR Studio 4 mentre la

programmazione del microcontrollore è avvenuta tramite l’utilizzo della scheda AVR Dragon.

Nella scrittura del programma, la prima operazione necessaria è quella di includere il file

“atmega8.inc” che consente di utilizzare delle stringhe piuttosto che esplicitare i corrispondenti

indirizzi di memoria specifici del microcontrollore. Questo si effettua tramite le direttive:

.NOLIST ; La direttiva .NOLIST fa si che ciò che segue non venga ;incluso nel listato.

.INCLUDE "atmega8.inc" ; La direttiva .INCLUDE inserisce il file "atmega8.inc"

.LIST ; La direttiva .LIST svolge l’operazione duale a quella ;svolta da .NOLIST.

6

Le successive operazioni che precedono il ciclo principale del software prevedono la definizione

dei registri utilizzati, l’assegnazione di stringhe ai valori numerici utilizzati e l’allocazione della

memoria SRAM necessaria alla memorizzazione di una variabile a 16 bit e dei campioni di segnale.

In particolare, si è scelto di allocare uno spazio pari a 500 bytes che ad una frequenza di

campionamento di 500 Hz consente la memorizzazione di un secondo di segnale. La porzione di

codice che svolge questo compito è la seguente:

.DEF mp = R16 ; registro di lavoro (generico)

.DEF mp1 = R17 ; registro di lavoro secondario (generico)

.DEF mp2 = R24 ; primo registro per il decremento della variabile a 16 bit

.DEF mp3 = R25 ; secondo registro per il decremento della variabile a 16 bit

.DEF count_500 = R18 ; primo registro per la temporizzazione a 500 Hz

.DEF count_1000 = R19 ; secondo registro per la temporizzazione a 1000 Hz

.EQU tsh_bat = 227 ; fissa la soglia di tensione a 2,4V nominali

.EQU alta30 = 0x75 ;valore alto di 30000 per temporizzare il check batteria

.EQU bassa30 = 0x30 ; valore basso di 30000

; Allocazione della memoria SRAM

.DSEG

ind_tempo_batt: .BYTE 2

; riserva 2 bytes in cui salvo la variabile a 16 bit

; (30000) per il controllo della tensione di batteria

.org 0x93

; dopo le 96 celle dedicate ai registri, per mantenere 50 celle libere

; della SRAM, scrive i campioni a partire dalla 147esima cella

indirizzo: .BYTE 500

; riserva 500 bytes per scrivere i campioni del segnale ECG

.CSEG

Nella seguente porzione di codice si specifica la gestione degli interrupt. rjmp RESET ; Reset Handler

reti ;rjmp EXT_INT0 ; IRQ0 Handler

reti ;rjmp EXT_INT1 ; IRQ1 Handler

reti ;rjmp TIM2_COMP ; Timer2 Compare Handler

reti ;rjmp TIM2_OVF ; Timer2 Overflow Handler

reti ;rjmp TIM1_CAPT ; Timer1 Capture Handler

reti ;rjmp TIM1_CAPT ; Timer1 Capture Handler

reti ;rjmp TIM1_COMPA ; Timer1 CompareA Handler

reti ;rjmp TIM1_COMPB ; Timer1 CompareB Handler

reti ;rjmp TIM1_OVF ; Timer1 Overflow Handler

rjmp TIM0_OVF ; Timer0 Overflow Handler

reti ;rjmp SPI_STC ; SPI Transfer Complete Handler

reti ;rjmp USART_RXC ; USART RX Complete Handler

reti ;rjmp USART_UDRE ; UDR Empty Handler

reti ;rjmp USART_TXC ; USART TX Complete Handler

reti ;rjmp ADC_conv ; ADC Conversion Complete Handler

reti ;rjmp EE_RDY ; EEPROM Ready Handler

reti ;rjmp ANA_COMP ; Analog Comparator Handler

reti ;rjmp TWSI ; Two-wire Serial Interface Handler

reti ;rjmp SPM_RDY ; Store Program Memory Ready Handler

7

La parte successiva del codice consiste nella routine principale del programma il cui funzionamento è schematizzato nel seguente flow-chart generale.

Figura 3. Flow-chart generale della routine principale del software

4.1 Inizializzazioni e impostazione del convertitore

All’interno della routine principale del programma, la prima operazione da compiere è

l’inizializzazione dello Stack Pointer. Ciò è d’obbligo in quanto si utilizzerà una subroutine di

risposta ad interrupt. Per l’inizializzazione si ricorre ai registri SPH ed SPL in cui vengono scritte

rispettivamente le parti alta e bassa dell’ultimo indirizzo della SRAM.

Per svolgere le funzioni richieste vengono utilizzate le porte B,C e D del microcontrollore. Per

scegliere la direzione dei dati sui pin delle varie porte si utilizzano i registri DDR (Data Direction

Register), mentre per impostare il valore iniziale presente sulla porta si fa utilizzo dei registri PORT.

La scelta progettuale prevede l’utilizzo di un solo Timer Counter a 8 bit, nello specifico il Timer

Counter 0. Per la sua gestione vengono utilizzati i registri TCCR0, TCNT0 e TIMSK che permettono

di impostare rispettivamente il passo di prescaler, il valore iniziale del timer e la generazione del

segnale di interrupt in corrispondenza di un overflow. Il valore iniziale assegnato a TCNT0 è pari a

255, in tal modo l’overflow del contatore si verifica circa ogni millisecondo poiché il passo di

prescaler prescelto è di 1024 e la frequenza del clock del sistema risulta pari a 1MHz.

Stabilito che il controllo della tensione di batteria venga effettuato ogni 30 secondi, per ottenere

l’intervallo di tempo corrispondente, si è scelto il valore 30000 essendo questo decrementato ogni

millisecondo. Tale valore a 16 bit viene memorizzato nella SRAM all’indirizzo precedentemente

assegnato (ind_tempo_batt).

RESET

INIZIALIZZAZIONI

CHECK BATTERIA

CONTROLLO INDIRIZZO

SCRITTURA

LETTURA

FINE

8

Per stabilire l’intervallo di campionamento e quello di trasferimento dei campioni al convertitore

D/A, vengono inizializzate le variabili count_500 e count_1000.

Inoltre, l’ultima operazione di questa sezione di codice prevede l’inizializzazione della SRAM. Nelle

celle riservate alla memorizzazione dei campioni viene scritto il valore zero per avere certezza di

ciò che viene letto nel caso di avvio del programma nella modalità di lettura, quando cioè nella

SRAM non sono stati ancora memorizzati i campioni di segnale.

Quanto detto è riportato in dettaglio nel listato seguente:

RESET:

ldi mp,high(RAMEND) ;(0x04)

out SPH,mp

ldi mp,low(RAMEND) ;(0x5f)

out SPL,mp

; inizializzazione dello stack pointer all'ultimo indirizzo della SRAM

ldi mp,0b00000000

out DDRC,mp

; setta i pin della PORTA C tutti in ingresso

ldi mp,0x00

out PORTC,mp

; scrive zero nel registro della PORTA C

ldi mp,0b00001110

out DDRB,mp

; imposta in uscita i pin 15, 16 e 17 della PORTA B

; per i led di controllo scrittura, batteria, lettura

ldi mp,0x00

out PORTB,mp

; scrive zero nel registro della PORTA B

ldi mp,0b11111111

out DDRD,mp

; setta tutti i pin della PORTA D come uscite

ldi mp,0x00

out PORTD,mp

; scrive zero nel registro della PORTA D

ldi mp,0b00000101

out TCCR0,mp

; carica in TCCR0 i bit di mp, solo i primi 3 bit sono configurabili R/W,

; gli altri restano a 0. Il registro TCCR0 è usato per gestire la frequenza

; con cui generare un interrupt. Seleziona prescaler passo 1024

ldi mp,255

out TCNT0,mp

; imposta il valore iniziale del contatore a 255

ldi mp,0x01

out TIMSK,mp

; imposta il bit 0 del registro TIMSK ad 1 per abilitare la gestione degli

;interrupt in caso di overflow di TCNT0

9

ldi YL,low(indirizzo)

ldi YH,high(indirizzo)

; scrive in Y l'indirizzo da incrementare per scrivere i campioni di segnale

;nella SRAM

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

; scrive in X l'indirizzo dove andare a salvare il valore 30000 da decrementare

ldi mp,bassa30

ldi mp1,alta30

st X+,mp1

st X,mp

; scrive in SRAM il valore 30000 all'indirizzo contenuto in X

ldi count_500,2

ldi count_1000,1

; inizializza i contatori per temporizzare scrittura e lettura dei campioni

inizializzaRAM:

ldi mp,0

st Y+,mp

ldi mp,low(0x288)

cp YL,mp

brne inizializzaRAM

ldi mp,high(0x288)

cp YH,mp

brne inizializzaRAM

ldi YL,low(indirizzo)

ldi YH,high(indirizzo)

; scrive zero nelle 500 celle di memoria riservate a memorizzare

; i campioni del segnale in modo da essere certi del valore letto inizialmente

; all'avvio del programma

Prima di passare all’implementazione delle funzioni vere e proprie, si procede con la

configurazione del convertitore Analogico-Digitale interno al microcontrollore. Questo si fa

andando a modificare il registro ADCSRA. Il codice seguente svolge quanto detto.

ldi mp,0b10000010

out ADCSRA,mp

; imposta il fattore di prescaling pari a 4.

; ADCSRA è un registro a 8 bit in particolare:

; se il bit7=1 --> abilita ADC

; se il bit6=1 --> start conversione

; se il bit5=1 (ADFR) --> seleziona il Free Running mode

; se il bit4=1(ADIF) --> a fine conversione genero un interrupt portando ad 1 il

;bit 3

; i bit 2:0 selezionano il fattore di prescaling

Infine con l’istruzione sei viene abilitata la sensibilità della CPU agli interrupt, in modo tale che il

programma venga interrotto in corrispondenza di un overflow del Timer Counter 0 per

l’esecuzione della subroutine di risposta ad interrupt.

10

4.2 Subroutine di risposta ad interrupt

Il decrementi dei contatori che serve per temporizzare le operazioni di verifica della tensione di batteria, di campionamento e scrittura o lettura, vengono svolte all’interno di una subroutine di risposta ad interrupt denominata TIM0_OVF.

Figura 4. Flow-chart subroutine di risposta ad interrupt

Preliminarmente si usa l’istruzione di push dei registri utilizzati nella routine principale del

programma per garantire che il loro contenuto non venga modificato durante l’esecuzione di

TIM0_OVF. Inoltre si opera il push del registro di stato, SREG, in modo tale che terminata la

subroutine venga ripristinato lo stato precedente all’interrupt. L’istruzione di push consiste nel

salvare il contenuto dei registri sulla cima dello stack per poi recuperarlo tramite l’istruzione di

pop. Le altre operazioni compiute prevedono la reinizializzazione del registro TCNT0 al valore 255,

il decremento delle variabili a 8 bit, count_500 e count_1000, e il decremento del valore a 16

bit corrispondente al tempo rimanente al prossimo controllo della tensione di batteria.

11

Il codice che realizza quanto detto è il seguente.

TIM0_OVF:

push mp

push mp1

push mp2

push mp3

in mp,SREG

push mp

; push dei registri utilizzati, per garantire che non vengano modificati dalla

;subroutine di risposta ad interrupt

ldi mp,255

out TCNT0,mp

; reinizializza il contatore del timer counter

dec count_500

; decremento del contatore count_500

dec count_1000

; decremento del contatore count_1000

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

ld mp3,X+

ld mp2,X

sbiw mp3:mp2,1

; decremento della variabile a 16 bit per la temporizzazione del controllo di

;tensione di batteria

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

st X+,mp3

st X,mp2

; scrittura nella SRAM del valore a 16 bit decrementato

pop mp

out SREG,mp

pop mp3

pop mp2

pop mp1

pop mp

; ripristino dei registri utilizzati

reti

; ritorna all'esecuzione del programma principale

In particolare si nota come il decremento del valore a 16 bit venga effettuato tramite l’istruzione

sbiw che consente di decrementare in modo diretto parole di 2 bytes. Il valore in questione viene

caricato dalla SRAM tramite l’istruzione ld e successivamente, dopo essere stato decrementato,

viene memorizzato nella SRAM mediante l’istruzione st. L’istruzione finale reti permette di

ritornare all’esecuzione della routine principale del programma dal punto in cui era stata

interrotta.

12

4.3 Funzione per la verifica della tensione di batteria

La funzione di verifica della tensione di batteria ha lo scopo di valutare ogni 30 s la tensione

erogata dalla batteria. Nel caso di tensione al di sotto della soglia stabilita di 2.4 V nominali, il

sistema provvede alla generazione di un allarme visivo che consiste nell’accensione di un led giallo

che resta acceso fino a quando la tensione non viene ripristinata ad un valore accettabile e il

sistema riavviato. La scansione del tempo avviene tramite il decremento della variabile contenuta

all’indirizzo ind_tempo_batt della SRAM, di valore 30000. Questo decremento, come detto

precedentemente, viene effettuato ogni millisecondo, per cui per portare il valore 30000 a zero si

impiegheranno circa 30 s.

Il primo passo da compiere è proprio quello di verificare il valore di tempo corrente. Ciò viene

fatto semplicemente leggendo il valore della variabile scritto nella SRAM e confrontandolo con

zero.

Il valore 30000 viene codificato con una variabile a 16 bit, pertanto viene confrontata con zero

prima la parte bassa, bassa30,e poi la parte alta, alta30. Se la parte bassa è diversa da zero,

vuol dire che certamente non sono trascorsi 30 s, quindi si termina la funzione di verifica della

tensione di batteria e si prosegue con la parte successiva atta al controllo dell’indirizzo

(controllo_indirizzo), altrimenti è necessario verificare che anche la parte alta sia uguale a

zero. Se la parte alta non è uguale a zero vuol dire nuovamente che non sono trascorsi 30 s e si

passa a controllo_indirizzo, altrimenti si procede con le altre operazioni di verifica della

tensione di batteria. Allo scadere dei 30 s è necessario innanzitutto reinizializzare la variabile a 16

bit al valore 30000 in modo da poter contare nuovamente 30 s. Successivamente viene letta la

tensione di batteria, per fare ciò bisogna effettuare una conversione Analogico-Digitale. A questo

scopo viene impostato il pin di ingresso del convertitore interno al microcontrollore tramite il

registro ADMUX. Per dare inizio alla conversione si utilizza il registro ADCSRA.

Si effettua anche una verifica del termine della conversione, check_conv_batteria, tramite un

loop interno che introduce un ritardo che però, essendo di circa 10 µs, può essere tollerato

considerato che l’intervallo di campionamento è pari a 1 ms.

Il valore analogico, una volta convertito, viene scritto nel registro ADCH e può essere utilizzato per

il confronto con il valore di soglia. La tensione di riferimento interna del convertitore, in seguito ad

una misurazione compiuta, si è rivelata essere differente da quella dichiarata dal costruttore (2.56

V) e pari a 2.71 V. Di conseguenza il valore di soglia tsh_batt corrispondente a 2.4 V nominali, è

stato calcolato con una proporzione ed è risultato pari a 227. Se il valore letto nel registro ADCH è

minore della soglia tsh_batt, viene acceso un led giallo sul pin 16, altrimenti si passa a

controllo_indirizzo.

In figura 6 si vede come in corrispondenza di una tensione pari a 2.39 V misurata in ingresso al pin

24 (ADC1) il led giallo si accenda dopo 30 secondi dall’avvio del dispositivo. Per poter effettuare

tale verifica il dispositivo è stato avviato con una tensione di alimentazione pari a 4.8 V.

13

Figura 5. Flow-chart Verifica tensione di batteria.

14

Figura 6. Verifica tensione di batteria

15

Quanto visto precedentemente nel flow-chart, si traduce in linguaggio Assembly come riportato

nel listato seguente.

check_batteria:

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

; carica in X l'indirizzo in cui è salvato il valore 30000 che viene

;decrementato

ld mp1,X+

ld mp,X

; legge dalla SRAM il valore a 16 bit (30000) e lo carica nei registri mp1

;(parte alta) e mp (parte bassa)

cpi mp,0

brne controllo_indirizzo

; se la parte bassa è zero controlla la parte alta del valore altrimenti

; salta al controllo indirizzo

cpi mp1,0

brne controllo_indirizzo

; se anche la parte alta è zero reinizializza il valore a 30000 altrimenti

; salta al controllo indirizzo

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

ldi mp,bassa30

ldi mp1,alta30

st X+,mp1

st X,mp

; riscrive in SRAM il valore iniziale (30000) della variabile da decrementare

; per il controllo della batteria

ldi mp,0b11100001

out ADMUX,mp

; programma l'ADC in modo che lavori con tensione di riferimento interna a 2,56V

; (in realtà è 2,71V), giustifica a sinistra il risultato nel registro ADCH e

; seleziona ADC1 (pin 24) come ingresso del convertitore

in mp,ADCSRA

ldi mp1,0b01000000

or mp,mp1

out ADCSRA,mp

; inizia la conversione del valore di tensione di batteria

check_conv_batteria:

in mp,ADCSRA

ldi mp1,0b01000000

and mp,mp1

brne check_conv_batteria

; aspetta che la conversione sia pronta testando ADSC in ADCSRA

; a conversione terminata ADSC torna a 0

in mp,ADCH

; legge ADCH che contiene il valore di tensione convertito su 8 bit

16

cpi mp,tsh_bat

brsh controllo_indirizzo

; se il valore letto di tensione è maggiore o uguale alla soglia va avanti

;altrimenti accende il led

in mp1,PORTB ;legge quello che ho precedentemente scritto sulla porta B

ldi mp,0b00000100

or mp,mp1

;cambia solo il valore sul pin 16 mantenendo invariati gli altri

out PORTB,mp ;accende il led sul pin 16

In particolare si nota come per attivare la conversione viene portato a 1 il bit 6 del registro

ADCSRA chiamato ADSC, bit che tornerà a zero a conversione terminata. Per fare ciò è stata

eseguita un’operazione di or in modo da andare a modificare soltanto il bit di interesse. Al fine di

verificare che la conversione sia terminata è stato effettuato un and in modo da accertarsi che il

bit 6 sia effettivamente tornato a zero.

17

4.4 Controllo dell’indirizzo di memoria

Per un funzionamento di tipo “buffer circolare” dello spazio riservato ai campioni nella memoria

SRAM, è necessario che quando si è letto o scritto nell’ultima cella riservata si ricominci dalla

prima rileggendo o sovrascrivendo i campioni già presenti.

Figura 7. Flow-chart per il controllo dell'indirizzo corrente.

Gli indirizzi della SRAM sono parole di 2 bytes, pertanto per effettuare il confronto si procede

preliminarmente verificando che la parte bassa di Y sia uguale alla parte bassa di 0x288, indirizzo

corrispondente alla cella successiva all’ultima riservata per contenere i campioni. Se i due valori

non sono uguali evidentemente l’indirizzo corrente non è pari a 0x288 pertanto si passa alla

porzione di codice successiva a partire dal label scrittura. Se invece questi valori sono uguali, si

deve confrontare la parte alta dell’indirizzo corrente con quella alta di 0x288. Se le parti alte non

sono equivalenti si passa a scrittura, altrimenti si assegna all’indirizzo corrente il valore

dell’indirizzo della prima cella riservata (0x93) con la stessa procedura che tiene conto della parte

alta e di quella bassa.

Il codice relativo a quanto esplicitato dal precedente flow-chart, viene riportato di seguito.

controllo_indirizzo:

ldi mp,low(0x288)

cp YL,mp

brne scrittura

; se la parte bassa dell'indirizzo corrente è uguale alla parte bassa

;dell'indirizzo della prima cella dopo l'ultima riservata

;controlla anche la parte alta, altrimenti salta a scrittura

18

ldi mp,high(0x288)

cp YH,mp

brne scrittura

; se anche la parte alta dell'indirizzo corrente è uguale alla parte alta

;dell'indirizzo della prima cella dopo l'ultima riservata

;reinizializza l'indirizzo e prosegui con la scrittura, altrimenti salta a

;scrittura senza reinizializzare l'indirizzo

ldi YH,high(indirizzo)

ldi YL,low(indirizzo)

;reinizializza l'indirizzo della prima cella della SRAM per la scrittura/lettura

19

4.5 Funzione per il campionamento di un segnale analogico

Le operazioni di scrittura e lettura sono tra loro esclusive e identificate dall’accensione di led verdi

corrispondenti. La selezione di una delle due funzioni avviene mediante un interruttore sul pin 28

del microcontrollore. Il campionamento del segnale in ingresso e la scrittura dei campioni in

memoria vengono effettuati alla frequenza di 500 Hz. Per ottenere tale frequenza si fa ricorso alla

variabile count_500, che viene inizializzata al valore 2. Tale valore viene decrementato di 1 ogni

millisecondo, in modo tale che quando count_500 vale zero sia consentito campionare. Il

decremento sfrutta la subroutine di risposta ad interrupt TIM0_OVF.

Figura 8. Flow-chart funzione campionamento e scrittura

20

Per poter effettuare il campionamento del segnale simil-ECG in ingresso sul pin 25, l’interruttore

T1 deve far si che il valore logico in ingresso sul pin 28 sia basso, cioè zero. Altrimenti si passa

all’operazione di lettura. Per avere certezza del valore in uscita sulla PORTA D durante la scrittura,

si scrive zero nel registro PORTD. Nel caso in cui il dispostivo sia stato precedentemente in

modalità di lettura, ci si assicura che il led (pin 17) indentificativo di tale modalità sia spento. Viene

allora acceso il led sul pin 15 che identifica lo stato di scrittura. Dopodiché portando a 1 il bit ADC2

del registro ADMUX viene settato il pin 25 come ingresso del convertitore A/D.

Per verificare se è veramente giunto il momento di campionare si controlla il valore di

count_500, se questa variabile non è uguale a zero vuol dire che non sono passati 2 ms e si passa

avanti al label lettura. Altrimenti si passa innanzitutto alla reinizializzazione di count_500 in

modo da poter attendere che siano trascorsi 2 ms per campionare e scrivere nuovamente.

Successivamente si avvia la conversione e dopo aver atteso che questa sia terminata, come nel

caso precedentemente visto del check_conv_batteria, si legge dal registro ADCH il valore

convertito e lo si scrive nella SRAM in corrispondenza dell’indirizzo contenuto nel registro Y.

Dopodiché l’indirizzo della cella in cui è stato appena scritto il campione corrente viene

incrementato.

La parte di codice relativa a questa funzione è riportata di seguito.

scrittura:

in mp,PINC

ldi mp1,0b00100000

and mp,mp1

cpi mp,0

brne lettura

; controlla il valore dell'interruttore di scrittura/lettura. Se il valore sul

;pin 28 è uguale a zero procedi con la scrittura altrimenti passa alla lettura

ldi mp,0x00

out PORTD,mp

; dato che si sta effettuando la scrittura dei campioni, sulla PORTA D manda

;zero per avere contezza del valore sulla porta

in mp,PORTB

ldi mp1,0b11110111

and mp,mp1

ldi mp1,0b00000010

or mp,mp1

out PORTB,mp

; spegne il led di lettura sul pin 17 e accende il led di scrittura sul pin 15

ldi mp,0b11100010

out ADMUX,mp

; imposta ADC2 (pin 25) come ingresso al convertitore

cpi count_500,0

brne lettura

; verifica del contatore che permette una frequenza di campionamento di 500 Hz,

; se il contatore è uguale a zero è giunto l'istante di campionare

ldi count_500,2

; reinizializza il contatore a 2

21

in mp,ADCSRA

ldi mp1,0b01000000

or mp,mp1

out ADCSRA,mp

; inizia la conversione dei campioni del segnale

check_conv_scrittura:

in mp,ADCSRA

ldi mp1,0b01000000

and mp,mp1

brne check_conv_scrittura

; aspetta che la conversione sia pronta testando ADSC in ADCSRA: a conversione

;terminata ADSC torna a 0

in mp1,ADCH

; legge ADCH che contiene il campione convertito su 8 bit

st Y+,mp1

; scrive il campione in SRAM all'indirizzo contenuto in Y e incrementa

;l'indirizzo contenuto in Y

Il funzionamento in modalità di scrittura è rappresentato in figura 9, in cui è possibile vedere che il

led verde corrispondente alla scrittura è acceso e qual è la posizione dell’interruttore T1 in questa

modalità. Sullo schermo dell’oscilloscopio è rappresentato in alto il segnale proveniente dal

generatore inviato sul canale 1, mentre in basso è visualizzato il canale 2 a cui è collegata l’uscita

dell’amplificatore. Essendo in modalità di scrittura l’uscita è correttamente pari a zero.

Figura 9. Funzionamento in modalità di scrittura

Interruttore T1

22

4.6 Funzione per la lettura e la ricostruzione del segnale

La lettura dei campioni di segnale memorizzati nella SRAM viene attivata con l’interruttore T1.

Questa modalità è identificata dall’accensione di un led di colore verde corrispondente. I campioni

possono essere letti alla stessa frequenza con cui vengono scritti, 500 Hz, oppure a frequenza

doppia, 1000 Hz. La scelta della frequenza è operata dall’interruttore T2 sul pin 29. Per poter

ricostruire la forma d’onda del segnale i campioni letti vengono trasferiti in parallelo sulla PORTA D

del microcontrollore, settata come uscita. Questa è connessa ai pin di ingresso del convertitore

Digitale-Analogico, DAC 0808. Questo convertitore viene alimentato, come detto in precedenza,

con una tensione di alimentazione compresa tra +5 e -5 V. La tensione di riferimento che il DAC

utilizza per effettuare la conversione è la medesima che usa il convertitore Analogico-Digitale. La

configurazione circuitale standard del DAC 0808 prevede anche l’utilizzo di un amplificatore

operazionale, in questo progetto un TL081. Per visualizzare la forma d’onda del segnale ricostruito

è stato utilizzato un oscilloscopio digitale Agilent DSO3102A, prelevando il segnale fornito in uscita

dall’amplificatore. Il procedimento per fare tutto ciò è esplicitato dal flow-chart riportato in figura

7.

Come già detto si verifica immediatamente lo stato dell’interruttore sul pin 28. Se questo fa si che

il pin sia a valore logico basso, vuol dire che si è in modalità di scrittura pertanto si passa al label

fine e si può proseguire tornando alla funzione di verifica della tensione di batteria. Se invece il

valore logico sul pin 28 è alto, si procede con le operazioni di preparazione alla lettura.

Innanzitutto è necessario accendere il led sul pin 17 per identificare lo stato di lettura in corso,

dopo essersi assicurati che il led identificativo della scrittura (pin 15) sia spento. Queste operazioni

sono duali rispetto a quelle viste nel caso della funzione di scrittura.

La scelta della frequenza con cui ogni campione viene letto ed inviato al DAC è affidata ad un

interruttore T2 collegato sul pin 29. Se il valore logico sul pin è basso, la frequenza di lettura

impostata sarà 500 Hz, altrimenti si verificherà nuovamente lo stato dell’interruttore e se questo

sarà al valore logico alto la frequenza di lettura sarà di 1000 Hz. Nel primo caso, label freq_500, si

dovrà per prima cosa verificare che effettivamente sia giunto il momento di leggere, cioè che

count_500, sia uguale a zero ed eventualmente reimpostarne il valore a 2. Successivamente si

legge il campione nella cella della SRAM il cui indirizzo è contenuto nel registro Y, e gli 8 bit

corrispondenti vengono inviati sugli 8 pin della PORTA D. Dopodiché l’indirizzo corrente della cella

appena letta, viene incrementato. Nel caso in cui il valore logico sul pin 29 sia alto, in

corrispondenza di freq_1000, la variabile che viene considerata è count_1000, se questa è

uguale a zero viene reinizializzata al valore 1. Anche in questo caso dopo la reinizializzazione si

scrive sulla PORTA D il valore del campione letto e si incrementa l’indirizzo corrente. Al termine

della lettura si giunge al label fine da cui si torna alla verifica della tensione di batteria.

23

Figura 10. Flow-chart funzione di lettura

24

Il codice che realizza quanto descritto dal flow-chart è il seguente.

lettura:

in mp,PINC

ldi mp1,0b00100000

and mp,mp1

cpi mp,0

breq fine

; controlla il valore dell'interruttore di scrittura/lettura. Se il valore sul

;pin 28 è uguale a zero passa a fine altrimenti procedi

in mp,PORTB

ldi mp1,0b11111101

and mp,mp1

ldi mp1,0b00001000

or mp,mp1

out PORTB,mp

; spegne il led di scrittura sul pin 15 e accende il led di lettura sul pin 17

in mp,PINC

ldi mp1,0b00010000

and mp,mp1

cpi mp,0

brne freq_1000

; controlla il valore dell'interruttore delle frequenze. Se il valore sul pin 27

;è 0 legge a 500 Hz altrimenti a 1000 Hz

freq_500:

cpi count_500,0

brne freq_1000

; controlla il contatore che permette di leggere a 500 Hz. Se è uguale a zero

;vuol dire che la routine di risposta all'interrupt ha azzerato il valore

;iniziale di count_500, quindi può leggere il campione, altrimenti salta a

;freq_1000

ldi count_500,2

; reinizializza il contatore a 2

ld mp,Y+

out PORTD,mp

; scrive il valore letto dalla SRAM sul registro della PORTA D

freq_1000:

in mp,PINC

ldi mp1,0b00010000

and mp,mp1

cpi mp,0

breq fine

; controlla il valore dell'interruttore di scrittura/lettura. Se il valore sul

;pin 27 è uguale a zero passa a fine altrimenti procedi

cpi count_1000,0

brne fine

; controlla il contatore che permette di leggere a 1000 Hz. Se è uguale a zero

;vuol dire che la subroutine di risposta all'interrupt ha azzerato il valore

25

;iniziale di count_1000, quindi può leggere il campione, altrimenti salta a fine

ldi count_1000,1

; reinizializza il contatore a 1

ld mp,Y+

out PORTD,mp

; scrive il valore letto dalla SRAM sul registro della PORTA D

fine:

rjmp check_batteria

; ritorna alla verifica della tensione di batteria

Il funzionamento in modalità di lettura è rappresentato nelle figure 11 e 12, in cui è possibile

vedere che il led verde corrispondente alla lettura è acceso. In particolare, in figura 11 è

osservabile il funzionamento con frequenza di lettura impostata a 500 Hz tramite l’interruttore T2

evidenziato in figura. Sullo schermo dell’oscilloscopio è rappresentato in alto il segnale

proveniente dal generatore inviato sul canale 1, mentre in basso è visualizzato il canale 2 a cui è

collegata l’uscita dell’amplificatore. Sul canale 2 viene rappresentato ciclicamente il segnale

memorizzato nella SRAM di lunghezza pari ad 1 s.

Figura 11. Visualizzazione del segnale a 500 Hz

Interruttore T2

26

In figura 12 è osservabile il funzionamento con frequenza di lettura pari a 1000 Hz, ossia il doppio

della frequenza di campionamento.

Figura 12. Visualizzazione del segnale a 1000 Hz

Interruttore T2

27

5 Appendice

Listato completo del software ;*******************************************************************************

;

; File: elettrocardiografo_digitale.ASM

; Data: 26 giugno 2013

; Version: 1.0

; IDE: AVR-Studio 4.0

;

; Autore: Gruppo 09-Lab. Progettazione Dispositivi Biomedici Programmabili

; Riferimenti: Politecnico di Torino - DET

;

;*******************************************************************************

; Programma che consente di simulare le funzioni di un elettrocardiografo

;digitale in grado di memorizzare il segnale ECG.

;*******************************************************************************

.NOLIST ; La direttiva .NOLIST fa si che ciò che segue non venga ;incluso nel listato.

.INCLUDE "atmega8.inc" ; La direttiva .INCLUDE inserisce il file "atmega8.inc"

.LIST ; La direttiva .LIST svolge l’operazione duale a quella ;svolta da .NOLIST.

; Definizione dei registri utilizzati

.DEF mp = R16 ; registro di lavoro (generico)

.DEF mp1 = R17 ; registro di lavoro secondario (generico)

.DEF mp2 = R24 ; primo registro per il decremento della variabile a 16 bit

.DEF mp3 = R25 ; secondo registro per il decremento della variabile a 16 bit

.DEF count_500 = R18 ; primo registro per la temporizzazione a 500 Hz

.DEF count_1000 = R19 ; secondo registro per la temporizzazione a 1000 Hz

.EQU tsh_bat = 227 ; fissa la soglia di tensione a 2,4V nominali

.EQU alta30 = 0x75 ;valore alto di 30000 per temporizzare il check batteria

.EQU bassa30 = 0x30 ; valore basso di 30000

; Allocazione della memoria SRAM

.DSEG

ind_tempo_batt: .BYTE 2

; riserva 2 bytes in cui salvo la variabile a 16 bit

; (30000) per il controllo della tensione di batteria

.org 0x93

; dopo le 96 celle dedicate ai registri, per mantenere 50 celle libere

; della SRAM, scrive i campioni a partire dalla 147esima cella

indirizzo: .BYTE 500

; riserva 500 bytes per scrivere i campioni del segnale ECG

.CSEG

28

rjmp RESET ; Reset Handler

reti ;rjmp EXT_INT0 ; IRQ0 Handler

reti ;rjmp EXT_INT1 ; IRQ1 Handler

reti ;rjmp TIM2_COMP ; Timer2 Compare Handler

reti ;rjmp TIM2_OVF ; Timer2 Overflow Handler

reti ;rjmp TIM1_CAPT ; Timer1 Capture Handler

reti ;rjmp TIM1_CAPT ; Timer1 Capture Handler

reti ;rjmp TIM1_COMPA ; Timer1 CompareA Handler

reti ;rjmp TIM1_COMPB ; Timer1 CompareB Handler

reti ;rjmp TIM1_OVF ; Timer1 Overflow Handler

rjmp TIM0_OVF ; Timer0 Overflow Handler

reti ;rjmp SPI_STC ; SPI Transfer Complete Handler

reti ;rjmp USART_RXC ; USART RX Complete Handler

reti ;rjmp USART_UDRE ; UDR Empty Handler

reti ;rjmp USART_TXC ; USART TX Complete Handler

reti ;rjmp ADC_conv ; ADC Conversion Complete Handler

reti ;rjmp EE_RDY ; EEPROM Ready Handler

reti ;rjmp ANA_COMP ; Analog Comparator Handler

reti ;rjmp TWSI ; Two-wire Serial Interface Handler

reti ;rjmp SPM_RDY ; Store Program Memory Ready Handler

RESET:

ldi mp,high(RAMEND) ;(0x04)

out SPH,mp

ldi mp,low(RAMEND) ;(0x5f)

out SPL,mp

; inizializzazione dello stack pointer all'ultimo indirizzo della SRAM

ldi mp,0b00000000

out DDRC,mp

; setta i pin della PORTA C tutti in ingresso

ldi mp,0x00

out PORTC,mp

; scrive zero nel registro della PORTA C

ldi mp,0b00001110

out DDRB,mp

; imposta in uscita i pin 15, 16 e 17 della PORTA B

; per i led di controllo scrittura, batteria, lettura

ldi mp,0x00

out PORTB,mp

; scrive zero nel registro della PORTA B

ldi mp,0b11111111

out DDRD,mp

; setta tutti i pin della PORTA D come uscite

ldi mp,0x00

out PORTD,mp

; scrive zero nel registro della PORTA D

ldi mp,0b00000101

out TCCR0,mp

; carica in TCCR0 i bit di mp, solo i primi 3 bit sono configurabili R/W,

; gli altri restano a 0. Il registro TCCR0 è usato per gestire la frequenza

; con cui generare un interrupt. Seleziona prescaler passo 1024

ldi mp,255

out TCNT0,mp

; imposta il valore iniziale del contatore a 255

29

ldi mp,0x01

out TIMSK,mp

; imposta il bit 0 del registro TIMSK ad 1 per abilitare la gestione degli

;interrupt in caso di overflow di TCNT0

ldi YL,low(indirizzo)

ldi YH,high(indirizzo)

; scrive in Y l'indirizzo da incrementare per scrivere i campioni di segnale

;nella SRAM

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

; scrive in X l'indirizzo dove andare a salvare il valore 30000 da decrementare

ldi mp,bassa30

ldi mp1,alta30

st X+,mp1

st X,mp

; scrive in SRAM il valore 30000 all'indirizzo contenuto in X

ldi count_500,2

ldi count_1000,1

; inizializza i contatori per temporizzare scrittura e lettura dei campioni

inizializzaRAM:

ldi mp,0

st Y+,mp

ldi mp,low(0x288)

cp YL,mp

brne inizializzaRAM

ldi mp,high(0x288)

cp YH,mp

brne inizializzaRAM

ldi YL,low(indirizzo)

ldi YH,high(indirizzo)

; scrive zero nelle 500 celle di memoria riservate a memorizzare

; i campioni del segnale in modo da essere certi del valore letto inizialmente

; all'avvio del programma

;-------------------------------------------------------------------------------

; ABILITAZIONE E PROGRAMMAZIONE ADC

;-------------------------------------------------------------------------------

ldi mp,0b10000010

out ADCSRA,mp

; imposta il fattore di prescaling pari a 4.

; ADCSRA è un registro a 8 bit in particolare:

; se il bit7=1 --> abilita ADC

; se il bit6=1 --> start conversione

; se il bit5=1 (ADFR) --> seleziona il Free Running mode

; se il bit4=1(ADIF) --> a fine conversione genero un interrupt portando ad 1 il

;bit 3

; i bit 2:0 selezionano il fattore di prescaling

sei

; abilito la sensibilità della CPU agli interrupt

30

;-------------------------------------------------------------------------------

; VERIFICA TENSIONE DI BATTERIA

; Questa funzione controlla la tensione di batteria ad intervalli di 30 secondi

;generando un allarme visivo (accensione led su pin 16)

;-------------------------------------------------------------------------------

check_batteria:

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

; carica in X l'indirizzo in cui è salvato il valore 30000 che viene

;decrementato

ld mp1,X+

ld mp,X

; legge dalla SRAM il valore a 16 bit (30000) e lo carica nei registri mp1

;(parte alta) e mp (parte bassa)

cpi mp,0

brne controllo_indirizzo

; se la parte bassa è zero controlla la parte alta del valore altrimenti

; salta al controllo indirizzo

cpi mp1,0

brne controllo_indirizzo

; se anche la parte alta è zero reinizializza il valore a 30000 altrimenti

; salta al controllo indirizzo

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

ldi mp,bassa30

ldi mp1,alta30

st X+,mp1

st X,mp

; riscrive in SRAM il valore iniziale (30000) della variabile da decrementare

; per il controllo della batteria

ldi mp,0b11100001

out ADMUX,mp

; programma l'ADC in modo che lavori con tensione di riferimento interna a 2,56V

; (in realtà è 2,71V), giustifica a sinistra il risultato nel registro ADCH e

; seleziona ADC1 (pin 24) come ingresso del convertitore

in mp,ADCSRA

ldi mp1,0b01000000

or mp,mp1

out ADCSRA,mp

; inizia la conversione del valore di tensione di batteria

check_conv_batteria:

in mp,ADCSRA

ldi mp1,0b01000000

and mp,mp1

brne check_conv_batteria

; aspetta che la conversione sia pronta testando ADSC in ADCSRA

; a conversione terminata ADSC torna a 0

in mp,ADCH

; legge ADCH che contiene il valore di tensione convertito su 8 bit

31

cpi mp,tsh_bat

brsh controllo_indirizzo

; se il valore letto di tensione è maggiore o uguale alla soglia va avanti

;altrimenti accende il led

in mp1,PORTB ;legge quello che ho precedentemente scritto sulla porta B

ldi mp,0b00000100

or mp,mp1

;cambia solo il valore sul pin 16 mantenendo invariati gli altri

out PORTB,mp ;accende il led sul pin 16

;-------------------------------------------------------------------------------

; CONTROLLO SULL'INDIRIZZO ATTUALE DELLA SRAM

; Questa funzione controlla che l'indirizzo che punta alla cella di memoria in

;cui memorizzare il campione sia l'ultimo, in tal caso torna a memorizzare a

;partire dalla prima cella del blocco di memoria (500 bytes) riservato ai

;campioni, altrimenti continua a memorizzare nella cella successiva

;-------------------------------------------------------------------------------

controllo_indirizzo:

ldi mp,low(0x288)

cp YL,mp

brne scrittura

; se la parte bassa dell'indirizzo corrente è uguale alla parte bassa

;dell'indirizzo della prima cella dopo l'ultima riservata

;controlla anche la parte alta, altrimenti salta a scrittura

ldi mp,high(0x288)

cp YH,mp

brne scrittura

; se anche la parte alta dell'indirizzo corrente è uguale alla parte alta

;dell'indirizzo della prima cella dopo l'ultima riservata

;reinizializza l'indirizzo e prosegui con la scrittura. altrimenti salta a

;scrittura senza reinizializzare l'indirizzo

ldi YH,high(indirizzo)

ldi YL,low(indirizzo)

;reinizializza l'indirizzo della prima cella della SRAM per la scrittura/lettura

;-------------------------------------------------------------------------------

; CAMPIONAMENTO E SCRITTURA

; Questa funzione campiona il segnale in ingresso, lo converte in digitale e lo

;scrive nella SRAM

;-------------------------------------------------------------------------------

scrittura:

in mp,PINC

ldi mp1,0b00100000

and mp,mp1

cpi mp,0

brne lettura

; controlla il valore dell'interruttore di scrittura/lettura. Se il valore sul

;pin 28 è uguale a zero procedi con la scrittura altrimenti passa alla lettura

ldi mp,0x00

out PORTD,mp

; dato che si sta effettuando la scrittura dei campioni, sulla PORTA D manda

;zero per avere contezza del valore sulla porta

32

in mp,PORTB

ldi mp1,0b11110111

and mp,mp1

ldi mp1,0b00000010

or mp,mp1

out PORTB,mp

; spegne il led di lettura sul pin 17 e accende il led di scrittura sul pin 15

ldi mp,0b11100010

out ADMUX,mp

; imposta ADC2 (pin 25) come ingresso al convertitore

cpi count_500,0

brne lettura

; verifica del contatore che permette una frequenza di campionamento di 500 Hz,

; se il contatore è uguale a zero è giunto l'istante di campionare

ldi count_500,2

; reinizializza il contatore a 2

in mp,ADCSRA

ldi mp1,0b01000000

or mp,mp1

out ADCSRA,mp

; inizia la conversione dei campioni del segnale

check_conv_scrittura:

in mp,ADCSRA

ldi mp1,0b01000000

and mp,mp1

brne check_conv_scrittura

; aspetta che la conversione sia pronta testando ADSC in ADCSRA: a conversione

;terminata ADSC torna a 0

in mp1,ADCH

; legge ADCH che contiene il campione convertito su 8 bit

st Y+,mp1

; scrive il campione in SRAM all'indirizzo contenuto in Y e incrementa

;l'indirizzo contenuto in Y

;-------------------------------------------------------------------------------

; LETTURA E VISUALIZZAZIONE CAMPIONI

; Questa funzione legge i campioni di segnale memorizzati nella SRAM e li invia

;in uscita tramite la PORTA D, con due diverse frequenze, 500 e 1000 Hz.

;-------------------------------------------------------------------------------

lettura:

in mp,PINC

ldi mp1,0b00100000

and mp,mp1

cpi mp,0

breq fine

; controlla il valore dell'interruttore di scrittura/lettura. Se il valore sul

;pin 28 è uguale a zero passa a fine altrimenti procedi

in mp,PORTB

ldi mp1,0b11111101

and mp,mp1

ldi mp1,0b00001000

33

or mp,mp1

out PORTB,mp

; spegne il led di scrittura sul pin 15 e accende il led di lettura sul pin 17

in mp,PINC

ldi mp1,0b00010000

and mp,mp1

cpi mp,0

brne freq_1000

; controlla il valore dell'interruttore delle frequenze. Se il valore sul pin 27

;è 0 legge a 500 Hz altrimenti a 1000 Hz

freq_500:

cpi count_500,0

brne freq_1000

; controlla il contatore che permette di leggere a 500 Hz. Se è uguale a zero

;vuol dire che la routine di risposta all'interrupt ha azzerato il valore

;iniziale di count_500, quindi può leggere il campione, altrimenti salta a

;freq_1000

ldi count_500,2

; reinizializza il contatore a 2

ld mp,Y+

out PORTD,mp

; scrive il valore letto dalla SRAM sul registro della PORTA D

freq_1000:

in mp,PINC

ldi mp1,0b00010000

and mp,mp1

cpi mp,0

breq fine

; controlla il valore dell'interruttore di scrittura/lettura. Se il valore sul

;pin 27 è uguale a zero passa a fine altrimenti procedi

cpi count_1000,0

brne fine

; controlla il contatore che permette di leggere a 1000 Hz. Se è uguale a zero

;vuol dire che la subroutine di risposta all'interrupt ha azzerato il valore

;iniziale di count_1000, quindi può leggere il campione, altrimenti salta a fine

ldi count_1000,1

; reinizializza il contatore a 1

ld mp,Y+

out PORTD,mp

; scrive il valore letto dalla SRAM sul registro della PORTA D

fine:

rjmp check_batteria

; ritorna alla verifica della tensione di batteria

34

;-------------------------------------------------------------------------------

; SUBROUTINE DI RISPOSTA AD INTERRUPT

;-------------------------------------------------------------------------------

TIM0_OVF:

push mp

push mp1

push mp2

push mp3

in mp,SREG

push mp

; push dei registri utilizzati, per garantire che non vengano modificati dalla

;subroutine di risposta ad interrupt

ldi mp,255

out TCNT0,mp

; reinizializza il contatore del timer counter

dec count_500

; decremento del contatore count_500

dec count_1000

; decremento del contatore count_1000

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

ld mp3,X+

ld mp2,X

sbiw mp3:mp2,1

; decremento della variabile a 16 bit per la temporizzazione del controllo di

;tensione di batteria

ldi XL,low(ind_tempo_batt)

ldi XH,high(ind_tempo_batt)

st X+,mp3

st X,mp2

; scrittura nella SRAM del valore a 16 bit decrementato

pop mp

out SREG,mp

pop mp3

pop mp2

pop mp1

pop mp

; ripristino dei registri utilizzati

reti

; ritorna all'esecuzione del programma principale