Forum: Mikrocontroller und Digitale Elektronik ATxMega256A3BU & RTC32


von Jan H. (janiiix3)


Lesenswert?

Guten Morgen,

Bin schon seit Tagen dabei, eine RTC mit der internen Hardware vom xMega 
zu realisieren.
Benutzen tue ich ein XPLAINED Board mit einem LCD.
Der xMega besitzt auch eine externe Batterie. Nun Soll es damit wohl 
möglich sein, die Uhrzeit zwischen zu speichern. Wie genau funktioniert 
das. Wie zähle ich die Zeit weiter, wenn VCC nicht mehr vorhanden ist?

von Arduinoquäler (Gast)


Lesenswert?

Jan H. schrieb:
> Nun Soll es damit wohl
> möglich sein, die Uhrzeit zwischen zu speichern.

Was soll es helfen die Zeit zu speichern? Und wo? Im EEPROM?

Jan H. schrieb:
> Wie zähle ich die Zeit weiter

Du zählst nicht die Zeit weiter sondern die Uhr deines XMega.

von Basti (Gast)


Lesenswert?

Nimm dir doch das Beispielprojekt dazu heran... da ist doch alles 
realisiert. Jedenfalls bei dem Xplained ohne LCD ist es verwendet. Wenn 
die extra mit Batterie ausliefern, dann wird es bei deinem Board wohl im 
Beispielprojekt ebenfalls verwendet werden.

von Jan H. (janiiix3)


Lesenswert?

Ich steige durch das Datenblatt bsw. durch den Abschnitt RTC32 nicht 
durch.
Das heißt im Endeffekt, die RTC32 muss ein Register haben, was während 
VCC aus ist, weiter zählt? Ist es eins von den "Cnt" Registern?

von Holm T. (Gast)


Lesenswert?

Google mal nach AVR Appnote 1321 da ist ein Bisschen was beschrieben.
Ich habe vorige Woche mich genau durch diesen Kram gefräst..

Du brauchst die VBAT Initialisierungsgeschicht aus der Appnote:
1
* Depending on the VBAT system check appropriate actions need to 
2
        * be taken.
3
        * In this example we re-initialize the VBAT system in all
4
        * error cases.
5
        */
6
        switch (vbat_status)
7
        {
8
        case VBAT_STATUS_OK:
9
                // Interrupts must be re-enabled
10
                //RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_LO_gc);
11
                RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_OFF_gc);
12
                break;
13
        case VBAT_STATUS_NO_POWER: // fall through
14
        case VBAT_STATUS_BBPOR: // fall through
15
        case VBAT_STATUS_BBBOD: // fall through
16
        case VBAT_STATUS_XOSCFAIL: // fall through
17
        default:
18
                vbat_init();
19
                break;
20
        }

..und ja, ich habe auch an mir gezweifelt, die Manuals sind nicht 
übermäßig didaktisch geschrieben.. aber bei mir funzt das indessen
(auf anderer Hardware).


Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Danke.
Wie funktioniert das?
Ist das der Zählstand der in den Register von RTC32.CNT0 - 3 ist?
Oder muss ich in der ISR Variablen deklarieren und die werden dann 
weiter gezählt?

von Bastian W. (jackfrost)


Lesenswert?

Schau dir das Datablatt vom 256a3bu an, auf Seite 27 ist ein Diagramm 
wie das mit dem Battery Backup geht.

Gruß JackFrost

von Holm T. (Gast)


Lesenswert?

Die RTC32 ist ein 32Bit counter der mit Batteriepower weiterlaufen kann,
bei Sekundentakt läuft der nach 168 Jahren (?) über, bis dahin mußt Du 
Dir um die Zeit keine Sorgen machen.

Umrechnen der Sekunden nach Zeit mittels mktime() und gmtime() (oder 
localtime etc..). Interrupts sind nicht unbedingt notwendig.

Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Hallo Holm,

Das macht einiges Verständlicher. Ich bin mir immer noch nicht ganz 
sicher, wo ich die Sekunden nun auslesen kann. Ist ziemlich doof 
beschrieben.
Sind es wirklich die jeweils 8 x 4 Bit breiten CNT Register?

von Holm T. (Gast)


Lesenswert?

hole Dir doch mal die Appnote und das dazugehörige Zip Archiv, die 
Funktionen sind darin dokumentiert und das Ganze darfst Du ja auch 
benutzen.

Suche nach AVR1321 z.B. bei Atmel auf der Webseite unter den Dokuementen 
zum AtXmega.

Das was Du suchst steht in rtc32_driver.c
1
/*! \brief This function returns the current RTC count value.
2
 *
3
 *  This function synchronizes the RTC32 module's CNT value to the system
4
 *  clock domain, then returns its value.
5
 *
6
 *  \return The current RTC count value.
7
 */
8
uint32_t RTC32_GetCount( void )
9
{
10
        /* Synchronize the RTC module's CNT value to the system clock domain.  */
11
        RTC32_SyncCnt();
12
        do { } while ( RTC32_SyncCntBusy() );
13
        
14
        return RTC32.CNT;
15
}

Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Ich habe den Zähler jetzt Initalisiert, laufen tut er aber viel zu 
schnell.
Normalerweise sollte das CNT Register ja jede Sekunde um +1 erhöht 
werden oder sehe ich das falsch?
Es wird aber viel zu schnell gezählt.

Hier meine Init ->
1
void RTC32_Init(void)
2
{
3
  /* Apply a reset of the VBAT */
4
  VBAT.CTRL |= VBAT_ACCEN_bm;
5
6
  /* Set access enable bit */
7
  VBAT.CTRL = VBAT_RESET_bm;
8
9
  /* Enable 32.768kHz crystal oscillator */
10
  VBAT.CTRL |= VBAT_XOSCEN_bm | VBAT_XOSCSEL_bm;
11
12
  /* Wait until oscillator is stable */
13
  while(!(VBAT.STATUS & VBAT_XOSCRDY_bm));
14
15
  /* Enable low power mode for external oscillator */
16
  OSC.XOSCCTRL = OSC_X32KLPM_bm;
17
18
  /* Reset RTC32 module */
19
  RTC32.CTRL = 0;
20
  
21
  /* Wait until sync done */
22
  while(RTC32_SyncBusy());
23
24
  /* Set PER to 1024. CLK source of the RTC32 is 1024 ticks / second = 1 Hz */
25
  RTC32.PER = 1023;
26
27
  /* Reset CNT register */
28
  RTC32.CNT = 0;
29
30
  /* Wait until sync done */
31
  while(RTC32_SyncBusy());
32
33
  /* Enable overflow interrupts */
34
  RTC32.INTCTRL = RTC32_OVFINTLVL_MED_gc;
35
36
  /* Enable real time module */
37
  RTC32.CTRL = RTC32_ENABLE_bm;
38
39
  /* Wait until sync done */
40
  while(RTC32_SyncBusy());
41
42
  /* Enable medium priority interrupts */
43
  PMIC.CTRL = PMIC_MEDLVLEN_bm;
44
45
  /* Enable global interrupts */
46
  sei();
47
}

von Holm T. (Gast)


Lesenswert?

1
void vbat_enable_xosc(bool use1khz)
2
{
3
        // Enable the failure detection.
4
        VBAT.CTRL |= VBAT_XOSCFDEN_bm;
5
6
        /* A delay is needed to give the voltage in the backup system some time
7
         * to stabilise.
8
         */
9
        delay_us(200);
10
11
        // Enable oscillator, with 1 kHz or 1 Hz output.
12
        if (use1khz)
13
                VBAT.CTRL |= VBAT_XOSCEN_bm | VBAT_XOSCSEL_bm;
14
        else
15
                VBAT.CTRL |= VBAT_XOSCEN_bm;
16
}

Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Danke für deine Unterstützung.
Leider funktioniert das alles nicht so wirklich.
Habe jetzt die Init von der Atmel Note genommen...
Und von deinem letzten Post die VBAT Init. Diese führe ich direkt nach 
der Init von der RTC32 aus.

Wenn alles Konfiguriert ist, lese ich das CNT Register aus. Leider steht 
dort jedes mal "0".
1
int main (void)
2
{  
3
  system_clock_init();
4
  
5
  LCD_init();
6
    
7
  pwm_timer_init();
8
  
9
  PORTR.DIR |= (YELLOW_LED0_BIT  | YELLOW_LED1_BIT);
10
  PORTD.DIR |= (RED_STATE_LED_PIN | GREEN_POWER_LED_PIN);
11
  
12
  PORTE.DIRCLR = SWITCH_ONE_PIN;
13
  PORTF.DIRCLR = SWITCH_TWO_PIN;
14
  PORTF.DIRCLR = SWITCH_THREE_PIN;
15
16
  /* Reset the battery backup module */
17
  RTC32_Reset();
18
19
  /* Configure and enable TOSC, then set up and enable the RTC32 module */
20
  RTC32_ToscEnable(1);
21
  RTC32_Initialize(1023,0,0);
22
23
  /* Enable RTC compare interrupts */
24
  RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_LO_gc);
25
26
  PMIC.CTRL |= PMIC_LOLVLEN_bm;
27
28
  sprintf(Buffer,"%"PRIu32,RTC32_GetCount());
29
  LCD_SendString(Buffer,0,0);
30
31
  sei();

von Holm T. (Gast)


Lesenswert?

Jan H. schrieb:
> Danke für deine Unterstützung.
> Leider funktioniert das alles nicht so wirklich.
> Habe jetzt die Init von der Atmel Note genommen...

Was denn nun, vorher hattest Du den Zustand "viel zu schnell" was daran 
lag, das Du bei der Intialisierung nicht den 1 Sekunden Takt sondern den 
1Khz Takt ausgewählt hast.
1
VBAT.CTRL |= VBAT_XOSCEN_bm | VBAT_XOSCSEL_bm;

für 1Hz mußt Du aber
1
VBAT.CTRL |= VBAT_XOSCEN_bm;

benutzen.

..und nun zählt das Ding gar nicht mehr?

Du mußt Dir klar darüber werden wann Du die Uhr initialisieren willst, 
das sollte tunlichst nicht  bei jedem Systemstart sondern nur bei einem 
Reset passieren ansonsten haut es Dir die Zeit jedes Mal durcheinander.

> Und von deinem letzten Post die VBAT Init. Diese führe ich direkt nach
> der Init von der RTC32 aus.

Im Code der Application Note ist das so, das vbat_init() vor 
RTC32_Initialize() ausgeführt wird wenn ein Reset vorliegt, der 32Khz 
Oszillator mit Teiler muß erst einmal eingeschaltet werden.
Die Kontrolle über den Oszillator hat der vbat Modul.
Da ich keinen PowerOn Reset bei meiner Hardware hin bekomme ohne 3 
Stecker abzuziehen habe ich im Code einmalig vbat_init() beim startup 
gerufen und das so programmiert. Damit lief der Zähler dann los. Danach 
habe ichs wieder auskommentiert und wieder neu programmiert.

Gruß,
Holm

von Jan H. (janiiix3)


Lesenswert?

Sobald ich die Versorgungsspannung weg nehme, Resetet er den Zähler 
(CNT) und beim Starten fängt er wieder von "0" an. Ob ich es 
auskommentiere oder nicht.

Kannst du mir mal deine Main zeigen?

von Holm T. (Gast)


Lesenswert?

Jan H. schrieb:
> Sobald ich die Versorgungsspannung weg nehme, Resetet er den Zähler
> (CNT) und beim Starten fängt er wieder von "0" an. Ob ich es
> auskommentiere oder nicht.
>
> Kannst du mir mal deine Main zeigen?

...eigentlich nicht, ich werde dafür bezahlt das für Jemanden zu 
entwickeln, aber der Ausschnitt tuts vielleicht..das ist sowieso nur
der Appnote entsprechend:
1
int main(void)
2
{
3
4
uint8_t                                 vbat_status;
5
static  uint16_t                count;                                                  // Zaehler - zaehlt alle 10ms )
6
static  uint16_t                count_s;                                                // Zaehler - zaehlt jede sec )
7
8
        clock_init();
9
//      vbat_init();
10
        vbat_status = vbat_system_check(true);
11
        
12
        /* 
13
        * Depending on the VBAT system check appropriate actions need to 
14
        * be taken.
15
        * In this example we re-initialize the VBAT system in all
16
        * error cases.
17
        */
18
        switch (vbat_status)
19
        {
20
        case VBAT_STATUS_OK:
21
                // Interrupts must be re-enabled
22
                //RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_LO_gc);
23
                RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_OFF_gc);
24
                break;
25
        case VBAT_STATUS_NO_POWER: // fall through
26
        case VBAT_STATUS_BBPOR: // fall through
27
        case VBAT_STATUS_BBBOD: // fall through
28
        case VBAT_STATUS_XOSCFAIL: // fall through
29
        default:
30
                vbat_init();
31
                break;
32
        }
33
        port_init();
34
        adc_init();
35
        dac_init();
36
        usart_init();
37
//      spi_init();
38
#ifndef NO_RTC

90% davon hast Du schon mal gesehen..

Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Und der RTC32 wird gar nicht Konfiguriert?

von Holm T. (Gast)


Lesenswert?

grr...
1
//******************************************************************************
2
****************************************
3
void vbat_init(void)
4
{
5
        vbat_reset();
6
        vbat_enable_xosc(0);
7
        RTC32_Initialize(0xffffffff, 0, 0 );
8
        //RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_LO_gc);
9
        RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_OFF_gc);
10
}
11
//******************************************************************************

Wenn Du ihn jedesmal neu initialisierst brauchst Du Dich nicht wundern 
warum der immer von vorne anfängt zu zählen.

Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Villeicht bin ich einfach nur zu Blöd dafür.
Er zeigt mir jedes mal nach nem Neustart "0" an!
1
int main (void)
2
{  
3
  sprintf(Buffer,"%"PRIu32,RTC32_GetCount());
4
  
5
  //vbat_init();
6
    switch (VBAT.STATUS)
7
    {
8
    case VBAT_STATUS_OK: RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_OFF_gc);break;
9
    case VBAT_STATUS_NO_POWER: // fall through
10
    case VBAT_STATUS_BBPOR: // fall through
11
    case VBAT_STATUS_BBBOD: // fall through
12
    case VBAT_STATUS_XOSCFAIL: // fall through
13
    default: vbat_init();break;
14
  }
15
  PMIC.CTRL |= PMIC_LOLVLEN_bm;
16
    
17
  sei();
18
  
19
  /* configure core clock */
20
  system_clock_init();
21
  
22
  /* configure lcd */
23
  LCD_init();
24
    
25
  /* configure pwm */
26
  pwm_timer_init();
27
  
28
  /* pins as output */
29
  PORTR.DIR |= (YELLOW_LED0_BIT  | YELLOW_LED1_BIT);
30
  PORTD.DIR |= (RED_STATE_LED_PIN | GREEN_POWER_LED_PIN);
31
  
32
  /* switch as input */
33
  PORTE.DIRCLR = SWITCH_ONE_PIN;
34
  PORTF.DIRCLR = SWITCH_TWO_PIN;
35
  PORTF.DIRCLR = SWITCH_THREE_PIN;
36
37
  /* interrupts enable */
38
  sei();
39
  
40
  sprintf(Buffer,"%"PRIu32,RTC32_GetCount());
41
  LCD_SendString(Buffer,0,0);
42
  _delay_ms(1500);
43
  
44
  while(1)
45
  {
46
    Board_TestMode();  
47
    sprintf(Buffer,"%"PRIu32,RTC32_GetCount());
48
    LCD_SendString(Buffer,0,0);
49
    _delay_ms(1500);    
50
  }
51
52
}

: Bearbeitet durch User
von Holm T. (Gast)


Lesenswert?

Offensichtlich ist bei dir der Status nicht VBAT_STATUS_OK.

Warum nimmst Du nicht mal den originalen und änderst ihn erst ab wenn er 
funktioniert hat? Hast Du die Brownout Detecor enable fuse gesetzt?

Was ist mit dieser Funktion?
1
uint8_t vbat_system_check(bool first_time_startup)
2
{
3
        uint8_t vbat_status;
4
        /*
5
         * Check if sufficient power was detected on the VBAT input. The brown-
6
         * out detector (BBBOD) will be sampled once when the device starts up
7
         * and the result is visible as the BBPWR flag.
8
         */
9
        if (VBAT.STATUS & VBAT_BBPWR_bm)
10
                vbat_status = VBAT_STATUS_NO_POWER;
11
        else {
12
                /*
13
                 * We hav sufficient power, now we check if a power-on-reset
14
                 * (BBPOR) was detected on VBAT. This is visible from the BBPORF
15
                 * flag which is also only updated once when the device starts.
16
                 */
17
                if (VBAT.STATUS & VBAT_BBPORF_bm) {
18
                        if (first_time_startup)
19
                                vbat_status = VBAT_STATUS_INIT;
20
                        else
21
                                vbat_status = VBAT_STATUS_BBPOR;
22
                }
23
                else if (VBAT.STATUS & VBAT_BBBORF_bm)
24
                        vbat_status = VBAT_STATUS_BBBOD;
25
                else {
26
                        VBAT.CTRL = VBAT_ACCEN_bm;
27
                        if (VBAT.STATUS & VBAT_XOSCFAIL_bm)
28
                                vbat_status = VBAT_STATUS_XOSCFAIL;
29
                        else
30
                                vbat_status = VBAT_STATUS_OK;
31
                }
32
        }
33
        return vbat_status;
34
}


Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Holm,

besten dank! Ich muss jetzt noch mal genau nach schauen an was es 
gelegen hat. Die Funktion vbat_system_check() hat aufjedenfall den 
Erfolg gebracht.
Besten dank!

von Holm T. (Gast)


Lesenswert?

Na also, geht doch.

Viel weiter bin ich auch noch nicht, aber die Zeit lät sich stellen mit 
mktime() und zum auslesen nehme ich gmtime()...2038 läuft allerdings 
dann der 23 Bit Timer über..
Ich bin dabei eine Software zu portieren die ich mal für den 
AtXmega128A3 geschrieben hatte und der hatte eine extern (PCFxxxx) Uhr 
on Board die nun wegrationalisiert wurde.
Ich habe noch genug an der Baustelle zu tun..die ADUs machen auch nicht 
ganz das was ich will..

Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Hallo Holm,

Eine Frage noch, kann es sein, dass so wie der Counter jetzt 
konfiguriert ist, es nicht möglich ist den Interrupt weiter laufen zu 
lassen?

von Holm T. (Gast)


Lesenswert?

..ist doch ausgeschaltet in vbat_init() :

         ....
        //RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_LO_gc);
        RTC32_SetCompareIntLevel(RTC32_COMPINTLVL_OFF_gc);
        ...
Ich brauchte keinen Interrupt, wollte nur zwecks Datenspeicherung auf SD 
Karte den aktuellen Timestamp abrufen können.
Den müßtest Du dort also wieder aktivieren, logischerweise reagiert die 
CPU auf den Interrupt aber nur im Sleepmode oder wenn sie wach ist, 
nicht wenn sie keinen Saft hat.

Gruß,

Holm

von Jan H. (janiiix3)


Lesenswert?

Ja ist klar, habe ich schon gesehen.
Habe Versucht den Overflow Interrupt zu nehmen, bzw. einfach wie von dir 
gepostet (nicht CompareMatch) sondern eben Overflow.
Leider passiert da nichts.

Wie machst du das mit den Sekunden? Rufst du ständig das CNT Register ab 
und stellst den Wert da oder rufst du ihn einmalig beim App Start ab und 
lässt es dann via. Interrupt weiter zählen? Bzw. Wie würdest du das 
machen?

von Holm T. (Gast)


Lesenswert?

..der Overflow Interrupt löst bei Sekundentakt doch erst alle 168 Jahre 
aus (oder so..) Du mußt also noch ein Bisschen warten.

..es sei denn Du betriebst die Uhr wieder "zu schnell".


Ich brauche keine Echtzeit im Programm, es gibt da aber einen 
durchlaufenden 100ms Interrupt nach dem ich mich richte.
Beim Abspeichern von Datensätzen wird simpel der aktuelle Zeitstempel 
aus der RTC gelesen und dann verwendet.

Gruß,

Holm

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.