Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

Problema scrittura codice AVR Assembly (Atmega 8535)

Raccolta di codici sorgenti

Moderatore: Foto UtentePaolino

0
voti

[1] Problema scrittura codice AVR Assembly (Atmega 8535)

Messaggioda Foto UtenteRabeluk » 12 lug 2013, 10:48

salve a tutti, ho un piccolo problema e non riesco proprio a venirne a capo... ho scritto un codice che riguarda la realizzazione di una specie di sveglia digitale. Funziona alla grande ma non riesco a capire come mai il conteggio dei secondi è troppo sballato... ho usato il TimerCounter 0 per tale scopo in modalità CTC con prescaler di 1024
ed ho usato per il micro una frequenza(cristallo esterno) esterna di 4915200 Hz
in poche parole col prescaler di 1024 la frequenza di conteggio del timer counter dovrebbe essere 4800 Hz
ho impostato il TCNT0 in modo che contasse fino a 240 e poi si attiva un interrupt dove incremento un contatore che in quando arriva a 20 mi va a incrementare i secondi fino a 59 (poi inizia ad incrementare i minuti e cosi via)...
non riesco proprio a capire dove sbaglio, gia ad occhio si vede che il conteggio è sbagliato,spero che possiate aiutarmi
questa è la parte di codice che riguarda l'inizializzazione del timer counter

Codice: Seleziona tutto
;TCNT0
ldi temp,0b00001101         ;uso il TCNT0 con prescaler di 1024 in modalità CTC
out TCCR0,temp
clr temp               ;faccio partire TCNT0 da 0
out TCNT0,temp
ldi temp,240   ;imposto il conteggio fino a 240
out OCR0,temp
ldi temp,0b00000010         ;attivo l'interrupt compare match di TCNT0         
out TIMSK,temp


questa è la parte che riguarda l'interrupt (questa parte continua ma poi sotto confronta soltanto i registri della sveglia con quelli dell'ora attuale per far suonare la sveglia)
Codice: Seleziona tutto
OVTCNT0:   
      inc TIMEH         ;incremento TIMEH
      cpi TIMEH,20      ;confronto TIMEH con 20
      breq PC+2         ;se TIMEH diverso da 20
      rjmp sveglia      ;vado alla routine controllo sveglia
      clr TIMEH         ;altrimenti resetto TIMEH
      cpi secondi,59      ;se secondi diverso da 59
      brne Fine_Inc      ;vado salto a Fine_Inc
      clr secondi         ;altrimenti resetto secondi
      cpi minuti,59      ;ripeto la stessa cosa per i minuti
      brne Fine_Inc2      ;
      clr minuti
      cpi ore,23         ;e per le ore impostando limite
      brne Fine_Inc3      ;max a 23
      clr ore
      rcall CGM         ;chiamo CGM (controllo giorno max)
      cp giorno,VALMAX   ;se giorno attuale diverso dal giorno max raggiungibile
      brne Fine_Inc4      ;saltiamo sotto
      ldi giorno,1      ;altrimenti resetto il contatore dei giorni a 1
      cpi mese,12         ; se mese diverso da 12
      brne Fine_Inc5      ;salto sotto
      ldi mese,1         ;altrimenti resetto mese a 1
      inc anno         ;ed incremento l'anno
      rjmp Fine         ;saltando alla fine
Fine_Inc:      
       inc secondi         ;incrementa i secondi
      rjmp Fine         ;e salta alla fine
Fine_Inc2:     
       inc minuti         ;incrementa i minuti
       rjmp Fine         ;salta alla fine
Fine_Inc3:      
      inc ore            ;incrementa le ore
      rjmp Fine         ;e salta alla fine
Fine_Inc4:      
      inc giorno         ;incrementa giorno
      rcall  fine         ;e salta alla fine
Fine_Inc5:      
      inc mese         ;incrementa i mesi
Fine:         
      sbrs help,4         ;se il bit 4 di help è 1 vuol dire che sto impostando la sveglia quindi non aggiorno il display
      rcall lcd_out


mentre questa è la routine che aggiorna il display

Codice: Seleziona tutto
lcd_out   :
ldi dataReg,0b10000000  ;sposto il cursore del display alla posizione 00
rcall LCD_Command      ;da dove visualizzero l'ora della sveglia
nop
nop
nop
nop
nop


mov dataReg,ore         ;mi salvo il contenuto di ore in dataReg
rcall converti         ;converto il contenuto in decine e unità
rcall LCD_w_Data      ;invio le decine salvate in dataReg al display
nop
nop
nop
nop
nop

mov dataReg,unita      ;dopodiche lo faccio con le
rcall LCD_w_Data      ;unita
nop
nop
nop
nop
nop


mov dataReg,duepunti   ;invio al display
rcall LCD_w_Data      ;il simbolo : tra ore e minuti
nop
nop
nop
nop
nop

mov dataReg,minuti      ;mi salvo il contenuto di minuti in dataReg
rcall converti         ;converto il contenuto in decine e unità
rcall LCD_w_Data      ;invio le decine salvate in dataReg al display
nop
nop
nop
nop
nop
mov dataReg,unita      ;dopodiche lo faccio con le
rcall LCD_w_Data      ;unita
nop
nop
nop
nop
nop

mov dataReg,punto      ;invio al display
rcall LCD_w_Data      ;il simbolo punto tra minuti e secondi
nop
nop
nop
nop
nop
mov dataReg,secondi      ;mi salvo il contenuto di secondi in dataReg
rcall converti         ;converto il contenuto in decine e unità
rcall LCD_w_Data      ;invio le decine salvate in dataReg al display
nop
nop
nop
nop
nop
mov dataReg,unita      ;dopodiche lo faccio con le
rcall LCD_w_Data      ;unita
nop
nop
nop
nop
nop               
                  
ldi dataReg,0b10001011  ;sposto il cursore del display alla posizione 11
rcall LCD_Command      ;da dove visualizzero l'ora della sveglia
nop
nop
nop
nop
nop

mov dataReg,hh         ;invio dati relativi alle ore della sveglia
rcall converti
rcall LCD_w_Data
nop
nop
nop
nop
nop
mov dataReg,unita
rcall LCD_w_Data
nop
nop
nop
nop
nop

mov dataReg,duepunti   ;inserisco duepunti
rcall LCD_w_Data
nop
nop
nop
nop
nop
mov dataReg,mm         ;invio dati relativi ai minuti della sveglia
rcall converti
rcall LCD_w_Data
nop
nop
nop
nop
nop
mov dataReg,unita
rcall LCD_w_Data
nop
nop
nop
nop
nop
ldi dataReg,0b11000100   ;vado sulla riga successiva dove
rcall LCD_Command      ;visualizzero il contenuto della data
nop
nop
nop
nop
nop
mov dataReg,giorno      ;giorno
rcall converti
rcall LCD_w_Data
nop
nop
nop
nop
nop
mov dataReg,unita
rcall LCD_w_Data
nop
nop
nop
nop
nop

mov dataReg,slash      ;slash
rcall LCD_w_Data
nop
nop
nop
nop
nop

mov dataReg,mese      ;mese
rcall converti
rcall LCD_w_Data
nop
nop
nop
nop
nop
mov dataReg,unita
rcall LCD_w_Data
nop
nop
nop
nop
nop

mov dataReg,slash      ;slash
rcall LCD_w_Data
nop
nop
nop
nop
nop
ldi dataReg,0b00110010
rcall LCD_w_Data
nop
nop
nop
nop
nop

mov dataReg,anno         ;anno
subi dataReg,200
brcs cont_cent
ldi dataReg,50
rjmp final
cont_cent:
   subi dataReg,-200
   subi dataReg,100
   brcs zero
   ldi dataReg,49
   rjmp final
zero:
   ldi dataReg,48
final:
   rcall LCD_w_Data
nop
nop
nop
nop
nop
mov dataReg,anno      
rcall converti
rcall LCD_w_Data
nop
nop
nop
nop
nop
mov dataReg,unita
rcall LCD_w_Data
nop
nop
nop
nop
nop
ret
converti:
   clr decine
   clr unita
con_dec:
   subi dataReg,10
   brcs con_unit
   inc decine
   rjmp con_dec
con_unit:
   subi dataReg,-58
   mov unita,dataReg
   mov dataReg,decine
   subi dataReg,-48
   ret
Avatar utente
Foto UtenteRabeluk
116 1 4 9
Sostenitore
Sostenitore
 
Messaggi: 765
Iscritto il: 30 gen 2011, 22:26

0
voti

[2] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto Utentesimo85 » 12 lug 2013, 13:49

Rabeluk ha scritto:ed ho usato per il micro una frequenza(cristallo esterno) esterna di 4915200 Hz


Forse è meglio se usi un quarzo da 32768 Hz. In più la ATmel mette a disposizione una bella AN guarda caso riguardo l'implementazione di un RTC (AVR134).

Qui il codice C dell'esempio.
Codice: Seleziona tutto
/**** A V R  A P P L I C A T I O N  NOTE 1 3 4 **************************
*
* Title:           Real Time Clock
* Version:         1.01
* Last Updated:    12.10.98
* Target:          ATmega103 (All AVR Devices with secondary external oscillator)
*
* Support E-mail:  avr@atmel.com
*
* Description     
* This application note shows how to implement a Real Time Clock utilizing a secondary
* external oscilator. Included a test program that performs this function, which keeps
* track of time, date, month, and year with auto leap-year configuration. 8 LEDs are used
* to display the RTC. The 1st LED flashes every second, the next six represents the
* minute, and the 8th LED represents the hour.
*
******************************************************************************************/

#include <iom103.h>
#include <ina90.h>         

char not_leap(void);

typedef struct{
unsigned char second;   //enter the current time, date, month, and year
unsigned char minute;
unsigned char hour;                                     
unsigned char date;       
unsigned char month;
unsigned int year;     
            }time;

time t;     
                                                       
void C_task main(void)   // C_task means "main" is never called from another function
{                               
//    init_rtc();
    int temp0,temp1;   
     
    for(temp0=0;temp0<0x0040;temp0++)   // Wait for external clock crystal to stabilize
    {
        for(temp1=0;temp1<0xFFFF;temp1++);
    }
    DDRB=0xFF;           
    TIMSK &=~((1<<TOIE0)|(1<<OCIE0));     //Disable TC0 interrupt
    ASSR |= (1<<AS0);           //set Timer/Counter0 to be asynchronous from the CPU clock
                                //with a second external clock(32,768kHz)driving it. 
    TCNT0 = 0x00;
    TCCR0 = 0x05;                 //prescale the timer to be clock source / 128 to make it
                                //exactly 1 second for every overflow to occur
    while(ASSR&0x07);           //Wait until TC0 is updated
    TIMSK |= (1<<TOIE0);        //set 8-bit Timer/Counter0 Overflow Interrupt Enable                             
    _SEI();                     //set the Global Interrupt Enable Bit 
                             
    while(1)                     
    {
        MCUCR = 0x38;           //entering sleeping mode: power save mode
        _SLEEP();              //will wake up from time overflow interrupt 
        _NOP();
        TCCR0=0x05;           // Write dummy value to Control register
        while(ASSR&0x07);     //Wait until TC0 is updated
    }           
}

interrupt [TIMER0_OVF_vect] void counter(void) //overflow interrupt vector
{
   
    if (++t.second==60)        //keep track of time, date, month, and year
    {
        t.second=0;
        if (++t.minute==60)
        {
            t.minute=0;
            if (++t.hour==24)
            {
                t.hour=0;
                if (++t.date==32)
                {
                    t.month++;
                    t.date=1;
                }
                else if (t.date==31)
                {                   
                    if ((t.month==4) || (t.month==6) || (t.month==9) || (t.month==11))
                    {
                        t.month++;
                        t.date=1;
                    }
                }
                else if (t.date==30)
                {
                    if(t.month==2)
                    {
                       t.month++;
                       t.date=1;
                    }
                }             
                else if (t.date==29)
                {
                    if((t.month==2) && (not_leap()))
                    {
                        t.month++;
                        t.date=1;
                    }               
                }                         
                if (t.month==13)
                {
                    t.month=1;
                    t.year++;
                }
            }
        }
    } 
    PORTB=~(((t.second&0x01)|t.minute<<1)|t.hour<<7);




char not_leap(void)      //check for leap year
{
    if (!(t.year%100))
        return (char)(t.year%400);
    else
        return (char)(t.year%4);
}         


Spero ti sia utile e che tu possa appunto prendere spunto da questo.
Avatar utente
Foto Utentesimo85
30,8k 7 12 13
Disattivato su sua richiesta
 
Messaggi: 9927
Iscritto il: 30 ago 2010, 4:59

0
voti

[3] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto UtenteRabeluk » 12 lug 2013, 14:14

potresti spiegarti meglio? sinceramente non conosco C...... ho studiato solo assembly per i micro avr....
mi stai consigliando di usare un clock esterno per il timer counter invece di quello interno e il prescaler?
Avatar utente
Foto UtenteRabeluk
116 1 4 9
Sostenitore
Sostenitore
 
Messaggi: 765
Iscritto il: 30 gen 2011, 22:26

0
voti

[4] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto Utentesimo85 » 12 lug 2013, 14:18

Rabeluk ha scritto:mi stai consigliando di usare un clock esterno per il timer counter invece di quello interno e il prescaler?

Si, proprio così. :D

Se non ti è chiaro qualcosa del codice C e relative istruzioni puoi chiedere e vediamo di chiarire eventuali dubbi.

O_/
Avatar utente
Foto Utentesimo85
30,8k 7 12 13
Disattivato su sua richiesta
 
Messaggi: 9927
Iscritto il: 30 ago 2010, 4:59

0
voti

[5] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto UtenteRabeluk » 12 lug 2013, 14:26

ok, mi sai dire se devo modificare solo il funzionamento del timer counter o altro? nel senso basta dire al timercounter di usare un clock esterno ed è fatta?
Avatar utente
Foto UtenteRabeluk
116 1 4 9
Sostenitore
Sostenitore
 
Messaggi: 765
Iscritto il: 30 gen 2011, 22:26

0
voti

[6] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto Utentesimo85 » 12 lug 2013, 14:37

Una alternativa al tuo problema potrebbe essere quella di usare una RTC esterna, come appunto il DS1307.

Tra l'altro, con gcc (avr-gcc in questo caso), con il comando:

Codice: Seleziona tutto
avr-gcc -mmcu=atmega103 -S -O2 source.c


Il compilatore genera codice Assembly a partire dal codice C (in questo caso -mmcu=atmega103 è per la AN linkata e "source.c" è il nome generico del file sorgente). Sicuramente ti è utile. ;-)

Rabeluk ha scritto:mi sai dire se (..) nel senso basta dire (..)

Ovviamente devi configurare come clock di riferimento per il TMR0 l'oscillatore esterno.
Avatar utente
Foto Utentesimo85
30,8k 7 12 13
Disattivato su sua richiesta
 
Messaggi: 9927
Iscritto il: 30 ago 2010, 4:59

0
voti

[7] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto Utenterusty » 12 lug 2013, 14:40

Secondo me interno od esterno cambia poco.
Devi considerare che anche il codice in se' ha bisogno di tempo (cicli di istruzione) per essere eseguito, questo, essendo pure variabile in funzione del conteggio che avanza, ti "rallenta" inevitabilmente anche il tempo reale che stai considerando.
Potresti tarare al meglio il conteggio considerando di togliere il numero di cicli per ogni istruzione che viene eseguita tra un "tik" del timer e l'altro sottraendo il tempo per ciclo (dato dal quarzo o dalla frequenza di sistema) per ogni istruzione, considerando quanti cicli occupano (variano da 1 a 2 a 4 cicli in merito alla specifica istruzione, verificabile dal manuale assembly).

Per ovviare a tutto questo potresti usare l'RTC, nato apposta per questo tipo di "conti" :ok:
Avatar utente
Foto Utenterusty
4.077 2 9 11
Utente disattivato per decisione dell'amministrazione proprietaria del sito
 
Messaggi: 1578
Iscritto il: 25 gen 2009, 13:10

0
voti

[8] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto UtenteRabeluk » 12 lug 2013, 14:44

rusty ha scritto:Secondo me interno od esterno cambia poco.
Devi considerare che anche il codice in se' ha bisogno di tempo (cicli di istruzione) per essere eseguito, questo, essendo pure variabile in funzione del conteggio che avanza, ti "rallenta" inevitabilmente anche il tempo reale che stai considerando.
Potresti tarare al meglio il conteggio considerando di togliere il numero di cicli per ogni istruzione che viene eseguita tra un "tik" del timer e l'altro sottraendo il tempo per ciclo (dato dal quarzo o dalla frequenza di sistema) per ogni istruzione, considerando quanti cicli occupano (variano da 1 a 2 a 4 cicli in merito alla specifica istruzione, verificabile dal manuale assembly).

Per ovviare a tutto questo potresti usare l'RTC, nato apposta per questo tipo di "conti" :ok:


anche io ero arrivato a questa conclusione ... cioè che magari i registri si aggiornano "quasi" nel tempo giusto però poi prima di aggiornare scorrono un po' di istruzioni che mi fanno sembrare un secondo leggermente + lungo... ma alla lunga è un disastro :(

in che modo posso usare l'RTC per ovviare al problema
Avatar utente
Foto UtenteRabeluk
116 1 4 9
Sostenitore
Sostenitore
 
Messaggi: 765
Iscritto il: 30 gen 2011, 22:26

0
voti

[9] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto Utentesimo85 » 12 lug 2013, 14:48

Se usi l'RTC esterna semplifichi di molto il codice. Si minimizza di molto anche il problema dei tempi di esecuzione delle istruzioni, usando appunto l'interfaccia seriale hardware I2C.
Avatar utente
Foto Utentesimo85
30,8k 7 12 13
Disattivato su sua richiesta
 
Messaggi: 9927
Iscritto il: 30 ago 2010, 4:59

0
voti

[10] Re: problema scrittura codice avr-assembly(atmega 8535)

Messaggioda Foto UtenteRabeluk » 12 lug 2013, 14:57

simo85 ha scritto:Se usi l'RTC esterna semplifichi di molto il codice. Si minimizza di molto anche il problema dei tempi di esecuzione delle istruzioni, usando appunto l'interfaccia seriale hardware I2C.


non ti seguo più... io sono rimasto al cristallo esterno per modificare la frequenza del timer counter
Avatar utente
Foto UtenteRabeluk
116 1 4 9
Sostenitore
Sostenitore
 
Messaggi: 765
Iscritto il: 30 gen 2011, 22:26

Prossimo

Torna a Firmware e programmazione

Chi c’è in linea

Visitano il forum: Nessuno e 17 ospiti