Kampis Elektroecke

XMega – RTC32

XMega Blockdiagramm

Einige XMega Modelle (wie z. B. der XMega256A3BU) sind mit einer integrierten Echtzeituhr und einer separaten Spannungsversorgung für die Echtzeituhr über ein Batteriebackup System ausgestattet. In diesem Beispiel möchte ich zeigen, wie der 32-Bit Real-Time Counter und das Battery Backup System eines XMegas genutzt werden kann.

Verwendung des Battery Backup Systems:

Über das Battery Backup System sorgt dafür, dass im Falle eines Spannungsausfalls, der 32-Bit Real-Time Counter über eine externe Batterie weiter mit Spannung versorgt wird.

Damit das Battery Backup System arbeiten kann, muss die Brown Out Detection am Mikrocontroller über die Fusebits eingeschaltet werden. Dazu sind die folgenden Fusebits zu setzen:

  • BODPD auf BOD enabled continously setzen
  • BODACT auf BOD enabled continously setzen

Über das STATUS-Register des Backup Systems kann der aktuelle Status der Backupbbatterie, sowie des externen Quarzes für die RTC32 eingesehen werden. Sobald das Hauptprogramm gestartet wird, wird der Status überprüft:

typedef enum
{
	BATTERY_STATUS_NO_POWER = 0x00,
	BATTERY_STATUS_BBPOR = 0x01,
	BATTERY_STATUS_BBBOD = 0x02,
	BATTERY_STATUS_XOSCFAIL = 0x03,
	BATTERY_STATUS_OK = 0x04,
	BATTERY_STATUS_INIT = 0x05
} BatteryStatus_t;

BatteryStatus_t Battery_CheckStatus(void)
{
	if(VBAT.STATUS & VBAT_BBPWR_bm)
	{
		return BATTERY_STATUS_NO_POWER;
	}
	else if(VBAT.STATUS & VBAT_BBBORF_bm)
	{
		return BATTERY_STATUS_BBBOD;
	}
	else if(VBAT.STATUS & VBAT_BBPORF_bm)
	{
		return BATTERY_STATUS_BBPOR;
	}
	else
	{
		VBAT.CTRL = VBAT_ACCEN_bm;
		if(VBAT.STATUS & VBAT_XOSCFAIL_bm)
		{
			return BATTERY_STATUS_XOSCFAIL;
		}
		else
		{
			return BATTERY_STATUS_OK;
		}
	}
}

BatteryStatus_t Status = Battery_CheckStatus();

Je nach Status wird ein anderer Wert aus der Battery_CheckStatus-Funktion zurückgegeben. Hierbei werden die folgenden Fälle berücksichtigt (von oben nach unten):

  1. Keine Spannung am Batteriepin erkannt
  2. Batteriebackupsystem wird über die Batterie versorgt
  3. Eine Batterie wurde an den Batteriepin angeschlossen, während der Mikrocontroller gestart hat
  4. Oszillatorfehler
  5. Kein Fehler erkannt

Der zurückgegebene Wert wird dann mit einer switch-Anweisung ausgewertet und im Fehlerfall werden das Batteriebackupsystem und die RTC32 neu initialisiert.

switch(Status)
{
	case(BATTERY_STATUS_OK):
	{
		RTC32_EnableInterrupts();
			
		break;
	}
	case(BATTERY_STATUS_NO_POWER):
	case(BATTERY_STATUS_BBPOR):
	case(BATTERY_STATUS_BBBOD):
	case(BATTERY_STATUS_XOSCFAIL):
	default:
	{			
		Battery_Init();
			
		RTC32_Init();
		
		break;
	}
}

Wenn das Batteriebackupsystem neu initialisiert werden soll, muss zuerst das ACCEN-Bit im CTRL-Register gesetzt werden, damit die Software auf die Register des Backupsystems (ausgenommen sind die Bits BBPODF, BBBODF und BBPWR) zugreifen kann.

void Battery_Init(void)
{
	VBAT.CTRL = VBAT_ACCEN_bm;

	ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
	{
		asm volatile(	"movw r30,  %0"	    "\n\t"
				"ldi  r16,  %2"	    "\n\t"
				"out   %3, r16"     "\n\t"
				"st     Z,  %1"     "\n\t"
				::	
				"r" (&VBAT.CTRL),
				"r" (VBAT_RESET_bm),
				"M" (CCP_IOREG_gc),
				"i" (&CCP)
				: "r16", "r30", "r31"
		);
	}

	VBAT.CTRL |= VBAT_XOSCSEL_bm | VBAT_XOSCEN_bm | VBAT_XOSCFDEN_bm;
	while(!(VBAT.STATUS & VBAT_XOSCRDY_bm));
}

Als nächstes wird ein Reset des Backupsystems durchgeführt. Dazu wird das RESET-Bit im CTRL-Register gesetzt. Da dieses Bit über den Configuration Change Mechanismus geschützt wird, muss vorher das IOREG-Bitmuster in das CCP-Register kopiert werden, sodass der Schutz aufgehoben wird.


Hinweis:

Dieser Abschnitt ist zeitkritisch, da nach dem Schreiben des IOREG-Bitmusters nur 4 Taktzyklen Zeit bleiben um ein geschütztes Register zu beschreiben. Daher ist dieser Codeabschnitt in einem atomaren Block untergebracht, der dafür sorgt, dass während dieses Codeabschnitts keine Interrupts auftreten. Zusätzlich ist der Code in Assembler geschrieben, damit der Code auch ohne Optimierung funktioniert.


Sobald der Reset durchgeführt worden ist, wird der externe 32 kHz Oszillator aktiviert und eine Ausgangsfrequenz von 1024 Hz eingestellt. Das Backupsystem ist damit einsatzbereit.

Als nächstes folgt der 32-Bit Real-Time Counter…

Der 32-Bit Real-Time Counter:

Bei dem 32-Bit Real-Time Counter handelt es sich um einen einfach gehaltenen Timer, der mit Hilfe des Backupsystems mit einer Batterie versorgt werden kann.

Bevor der RTC32 konfiguriert werden kann, muss er erst deaktiviert werden:

RTC32.CTRL = ~RTC32_ENABLE_bm;
while(RTC32_Sync());

Da der Timer über einen separaten Oszillator versorgt wird befindet sich der Timer in einer separaten Clock Domain. Dies hat zur Folge, dass nach jedem Schreib- oder Lesezugriff auf das CTRL– oder CNT-Register des Timers eine Synchronisation dieser beiden Clock Domainen erfolgen muss. Während dieser Synchronisation wird der Inhalt der RTC32-Register aus der Clock Domain des Mikrocontrollers in die Clock Domain des RTC32 kopiert.

Diese Synchronisation wird gestart, indem das CTRL-, das SYNCCTRL– oder das MSB des CNT-Registers beschrieben werden. Über das SYNCBUSY-Bit im SYNCCTRL-Register kann der Status der Synchronisation abgefragt werden:

static inline uint8_t RTC32_Sync(void)
{
	return (RTC32.SYNCCTRL & RTC32_SYNCBUSY_bm);
}

Die Synchronisation ist abgeschlossen, wenn das Bit gelöscht worden ist.

Das PER– und das CNT-Register der RTC32 können beschrieben werden, wenn die RTC32 deaktiviert oder die Synchonisation abgeschlossen ist. In meinem Beispiel soll jede Sekunde ein Overflow-und nach 0,5 Sekunden ein Compare-Interrupt ausgelöst werden.

RTC32.PER = 1023;
RTC32.COMP = 511;
RTC32.CNT = 0;

Anschließend wird der Timer wieder aktiviert und das CTRL– und CNT-Register synchronisiert:

RTC32.CTRL = RTC32_ENABLE_bm;
while(RTC32_Sync());

Damit ist auch der RTC32 einsatzbereit. Jetzt müssen noch die Interrupt Service Routinen für den Overflow- und den Compare-Interrupt geschrieben werden. In beiden ISRs soll jeweils eine LED getoggled werden. Diese LEDs befinden sich an den Pins R.0 und R.1.

ISR(RTC32_COMP_vect)
{
	PORTR.OUTTGL = (0x01 << 0x00);
}

ISR(RTC32_OVF_vect)
{
	PORTR.OUTTGL = (0x01 << 0x01);
}

Damit der RTC32 auch Interrupts auslöst, müssen diese aktiviert werden. Da die Einstellungen für die Interrupts nicht durch das Backupsystem geschützt sind, müssen diese bei jedem Start des Programms vorgenommen werden, falls das Backupsystem keinen Fehler meldet.

RTC32_EnableInterrupts();

void RTC32_EnableInterrupts(void)
{
	RTC32.INTCTRL = RTC32_COMPINTLVL_MED_gc | RTC32_OVFINTLVL_LO_gc;
}

Wenn das Programm nun gestartet wird und keine Batterie angeschlossen ist, werden der Real-Time Counter und das Backupsystem erstmalig konfiguriert. Danach kann eine Batterie angeschlossen und das Programm neu gestartet werden. Damit sind nun auch die Interrupts aktiv und es werden abwechseln beide LEDs ein- und ausgeschaltet.

Die Funktionsweise des Backupsystems und des Real-Time Counters bei deaktivierter Spannungsversorgung kann man sehr gut beobachten, wenn in dem BATTERY_STATUS_OK-Zweig ein Breakpoint gesetzt und das CNT-Register des Timers ausgelesen wird.

uint8_t RTC32_GetCount(void)
{
	RTC32.SYNCCTRL |= RTC32_SYNCCNT_bm;
	while(RTC32_Sync());
	
	return RTC32.CNT;
}

uint8_t Count = RTC32_GetCount();

Beim Auslesen des CNT-Registers muss darauf geachtet werden, dass zuerst eine Synchronisation über das SYNCNT-Bit gestartet wird, damit der Inhalt des CNT-Registers in die Clock Domain des Mikrocontrollers kopiert wird. Anschließend kann das Register ausgelesen werden.

case(BATTERY_STATUS_OK):
{
	uint8_t Count = RTC32_GetCount();

	RTC32_EnableInterrupts();

	break; <-- Breakpoint
}

Bei mehrmaligen aus- und wieder einschalten der Spannungsversorgung kann man nun wunderbar erkennen, dass der Zähler weiter zählt.

Das komplette Projekt steht in meinem GitHub-Repository zum Download bereit.

6 Kommentare

  1. In der Funktion void Battery_Init(void) versteht mein Compiler die
    Anweisung: nicht.
    Fehler meldung: each undeclared identifier is reported only once for each function it appears in rtc32.c.
    Was fehlt noch?

  2. In der Funktion void Battery_Init(void) versteht mein Compiler die
    Anweisung: nicht.
    Fehlermeldung: each undeclared identifier is reported only once for each function it appears in rtc32.c.
    Was fehlt noch?

  3. „… Bei dem 32-Bit Real-Time Counter handelt es sich um einen einfach gehaltenen Timer, der mit Hilfe des Backupsystems mit einer Batterie versorgt werden kann“.

    Kann man den RTC32 nutzen (nur als Timer, nicht als Uhrzeiterhalt) wenn am VBAT-Pin nichts angeschlossen ist?

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert