ATmega328p Timer

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

ATmega328p Timer

Beitrag von Mathias »

Dieser Arduino-Sketch funktioniert:

Code: Alles auswählen

#include <Arduino.h>
#include <avr/io.h>
#include <avr/interrupt.h>
 
void setup() {
   pinMode(13, OUTPUT);  // Pin 13 Output
 
   TCCR2A = 0;           // set timer2 normal mode
   TCCR2B |= _BV(CS20);  // timer2 clock = system clock
   TIMSK2 |= _BV(TOIE2); // enable timer2 overflow interrupt
 
   sei();               // enable interrupts
}
 
void loop() {
   delay(1000);          // wait for a second
}
 
ISR(TIMER2_OVF_vect) {   // === Wie unter Pascal ?
   static int z = 0;
   z += 1;
   if (z == 500) {
      digitalWrite(13, HIGH);  // turn the LED on (HIGH is the voltage level)
   }
   if (z >= 1000) {
      digitalWrite(13, LOW);   // turn the LED on (HIGH is the voltage level)
      z = 0;
   }
}


Jetzt wollte ich dies in Pascal umsetzen, nur weis ich nicht, wie ich das mit ISR(TIMER2_OVF_vect) lösen soll.
Jemand eine Idee ?

Code: Alles auswählen

program Project1;
const
  BP5 = 5; // Pin 13 des Arduino
  sl = 250000;
 
  WGM01 = 1;
  CS20 = 0;
var
  zaehler: integer;
 
  procedure mysleep(t: int32);
  var
    i: Int32;
  begin
    for i := 0 to t do begin
      asm
               Nop;
      end;
    end;
  end;
 
  procedure sei; assembler;
  asm
           Sei;
  end;
 
  procedure ISR(TIMER2_OVF_vect);       // Wie geht dies ?
  begin
    Inc(zaehler);
    if zaehler = 500 then begin
      PORTB := PORTB or (1 shl BP5);
    end;
    if zaehler = 1000 then begin
      PORTB := PORTB and not (1 shl BP5);
      zaehler := 0;
    end;
  end;
 
begin
  DDRB := DDRB or (1 shl BP5);       // Pin 13 Output
 
  TCCR2A := 0;                       // set timer2 normal mode
  TCCR2B := TCCR2B or (1 shl CS20)// timer2 clock = system clock
  TIMSK2 := TIMSK2 or (1 shl TOIE2); // enable timer2 overflow interrupt
  sei();
 
  repeat
    mysleep(sl);
  until 1 = 2;
end.


In der Unit Atmega328p habe ich folgende Zeile gefunden:

Code: Alles auswählen

procedure TIMER2_OVF_ISR; external name 'TIMER2_OVF_ISR'; // Interrupt 9 Timer/Counter2 Overflow

Diese befinde sich im implementation-Teil und somit nicht öffentlich.
Ob die weiter hilft ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: ATmega328p Timer

Beitrag von kupferstecher »

In dem Beispielprojekt das ich im andern Thread hochgeladen habe (ich nehm an, das du das gerade verwendest), gibt es eine Datei uInterrupts.pas. In dieser Datei sind alle für diesen Controller verfügbaren Interruptroutinen aufgelistet. Einfach die Auskommentierung entfernen und schon ist die entsprechende ISR aktiv. Der Name, den du in der Unit "Atmega328p" gefunden hast wird genau darin verwendet.

Wie man Interrupts benutzt, auch im Allgemeinen ohne die vorgefertigte Unit uInterrupts (Achtung, controllerspezifisch) siehe Wiki:
http://wiki.freepascal.org/AVR_Programming#Interrupts

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATmega328p Timer

Beitrag von Timm Thaler »

Code: Alles auswählen

// Timer 2 Oberflow Interrupt
 
procedure t2over_interrupt; alias: 'TIMER2_OVF_ISR'; interrupt; public;
begin
    Inc(zaehler);
    if zaehler = 500 then begin
      PORTB := PORTB or (1 shl BP5);
    end;
    if zaehler = 1000 then begin
      PORTB := PORTB and not (1 shl BP5);
      zaehler := 0;
    end;
end;


Wenn Du zaehler im Programm noch irgendwo verwenden willst, dann musst Du vorher Interrupts ausschalten und danach wieder einschalten, sonst kann das falsche Werte geben.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATmega328p Timer

Beitrag von Mathias »

Das werde ich morgen mal probieren.
Ich hatte vorhin Gedanken an Set und GetIntVec von Turbo-Pascal gehabt, da konnte man auch auf Interrupts reagieren.

Wen das klappt, steht einem Multiplex für eine 7-Segment-Anzeige nicht mehr im Wege. :wink:

Wenn Du zaehler im Programm noch irgendwo verwenden willst,

Dies ist nur für eine Kontroll-LED gedacht, die zeigt, das der Timer arbeitet.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATmega328p Timer

Beitrag von Mathias »

Der Timer funktioniert jetzt, aber ich aber immer noch ein Problem, wieso blinkt die LED beim Pascal-Code ca. um den Faktor 100 schneller ?
Woran könnte dies liegen ?
Initialisiert evtl. Arduino gewisse Register im Voraus ?

Code: Alles auswählen

#include <avr/io.h>
#include <avr/interrupt.h>
 
#define BP5 5
#define CS20 0
 
ISR(TIMER2_OVF_vect) {
  static int z = 0;
  z += 1;
  if (z == 1000) {
     PORTB |= 1 << BP5;
  }
  if (z >= 2000) {
    PORTB &=  ~(1 << BP5);
    z = 0;
  }
}
 
void setup() {
   DDRB |= (1 << BP5);     // Pin 13 Output
 
   TCCR2A = 0;             // set timer2 normal mode
   TCCR2B |= (1 << CS20);  // timer2 clock = system clock
   TIMSK2 |= (1 << TOIE2); // enable timer2 overflow interrupt
   sei();                  // enable interrupts
}
 
void loop() {
}


Code: Alles auswählen

program Project1;
const
  BP5 = 5; // Pin 13 des Arduino
  CS20 = 0;
 
  procedure sei; assembler;
  asm
           Sei;
  end;
 
  procedure t2over_interrupt; alias: 'TIMER2_OVF_ISR'; interrupt; public;
  const
    zaehler: integer = 0;
  begin
    Inc(zaehler);
    if zaehler = 1000 then begin
      PORTB := PORTB or (1 shl BP5);
    end;
    if zaehler >= 2000 then begin
      PORTB := PORTB and not (1 shl BP5);
      zaehler := 0;
    end;
  end;
 
begin
  DDRB := DDRB or (1 shl BP5);       // Pin 13 Output
 
  TCCR2A := 0;                       // set timer2 normal mode
  TCCR2B := TCCR2B or (1 shl CS20)// timer2 clock = system clock
  TIMSK2 := TIMSK2 or (1 shl TOIE2); // enable timer2 overflow interrupt
  sei();                             // enable interrupts
 
  repeat
  until 1 = 2;
end.


PS:
Initialisiert evtl. Arduino gewisse Register im Voraus ?

Es ist so, habe den Code von |= auf = geändert.

Code: Alles auswählen

//   TCCR2B |= (1 << CS20);  // timer2 clock = system clock
//   TIMSK2 |= (1 << TOIE2); // enable timer2 overflow interrupt
   TCCR2B = (1 << CS20);  // timer2 clock = system clock
   TIMSK2 = (1 << TOIE2); // enable timer2 overflow interrupt

Jetzt blinken beide gleich schnell. :roll:

Du hast Recht, Arduino ist nicht alles, das passieren Sachen, man weis nicht warum. :roll:
Aus irgend einem Grund hat Arduino sehr viel Overhead.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATmega328p Timer

Beitrag von Mathias »

Wie kriege ich es hin, das der Timer 1 Sekunden.Takt aufgerufen wird ?
Ich denke, dies hat mit OCR2 etwas zu tun, oder täusche ich mich da ?

Code: Alles auswählen

program Project1;
const
  BP5 = 5; // Pin 13 des Arduino
  CS20 = 0;
 
  procedure sei; assembler;
  asm
           Sei;
  end;
 
  procedure t2over_interrupt; alias: 'TIMER2_OVF_ISR'; interrupt; public;
  const
    zaehler: integer = 0;
  begin
    Inc(zaehler);
    if zaehler = 10 then begin
      PORTB := PORTB or (1 shl BP5);
    end;
    if zaehler >= 20 then begin
      PORTB := PORTB and not (1 shl BP5);
      zaehler := 0;
    end;
  end;
 
begin
  DDRB := DDRB or (1 shl BP5);       // Pin 13 Output
 
  OCR2A := $FF;
  TCCR2A := %10;          // OCR2
  TCCR2B := %111;         // Clock / 1024
  TIMSK2 := (1 shl TOIE2); // enable timer2 overflow interrupt
  sei();                             // enable interrupts
 
  repeat
  until 1 = 2;
end

Auf dieser Seite habe ich etwas gefunden, der letzt Timer am Ende der Seite.
https://sites.google.com/site/qeewiki/b ... -atmega328
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATmega328p Timer

Beitrag von Timm Thaler »

Also erstens: Du hast zwei Varianten um einen Timer zu realisieren.

1. Mit Overflow:

Der Interrupt löst aus, wenn der Timer überläuft (TCNT2 geht von $FF auf $00). Die Zeitspanne beträgt immer 256 x Timertakt. Für kürzere Zeitspannen muss man TCNT jedesmal wenn ein Overflow erfolgte auf einen Wert zwischen 0 und 255 setzen, entsprechend weniger oft muss er bis zum Überlauf zählen und entsprechend kürzer wird die Zeitspanne.

Das war bei älteren AVRs die einzige Möglichkeit Timer0 als Taktgeber zu verwenden.

2. Mit Output Compare:

Ursprünglich war Output Compare dazu gedacht, Pulse oder PWM auszugeben, deswegen das "Output". Man kann das aber auch für einen internen Timer verwenden.

Der Interrupt löst dann aus, wenn TCNT gleich dem eingestellten Wert von OCRxA oder B wird (sind auch verschiedene Interrupts). Idealerweise hat man CTC (Clear Timer on Compare) gesetzt, dann wird TCNT automatisch wieder auf Null gesetzt. Ursprünglich konnte man damit verschiedene Frequenzen erzeugen, indem man die Zeit bis zum Überlauf einstellte.

Output Compare stand bei älteren AVRs nur Timer1 zu Verfügung. Inzwischen können die neuen Typen Output Compare auf allen 3 Timern.

Welches Verfahren auf welchem Timer Du nimmst hängt davon ab, für was Du die Timer sonst noch so brauchst (PWM, RTC mit Uhrenquarz an Timer2, Ereignisse Zählen - Timer als Counter, Frequenzmessung - Timer als Counter mit Tor...).

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATmega328p Timer

Beitrag von Timm Thaler »

Jetzt hast Du Dich für den Timer2 und Overflow entschieden.

Da spielt OCR2A = Output Compare keine Rolle. Das setzt zwar bei Erreichen von $FF das Output Compare Flag, aber das fragst Du nicht ab.

Für den Overflow Interrupt musst Du TCNT auf einen Wert vorsetzen. Rechnen wir mal:

Systemtakt ist 8MHz. Prescaler ist 1024. Timertakt ist dann um die 8kHz. Damit sieht man sofort, dass mit einer 256er Zählvariable minimal eine Zeitspanne von 33msec erreicht werden kann. 1 Sekunde ist mit Timer2 an 8MHz nicht möglich.

Du hast jetzt zwei Möglichkeiten.

1. Du nimmst Timer1, der kann 16bit und mithin bis 8 Sekunden bei 8MHz. Wäre aber schade um den schönen Timer, nur für eine schnöde Sekunde.

2. Du erzeugst mit Timer2 alle paar Millisekunden einen Interrupt und zählst darin eine Variable hoch. Ist die wieder Null, ist eine Sekunde vorbei.

1sec durch 256 = 3.91msec. Der Interrupt muss also alle 3.91msec anspringen. Das wären 30.517 Timertakte. Da nur 30 oder 31 möglich sind, wäre das ein Fehler von fast 2%.

Wenn wir aber den Vorteiler auf 256 statt auf 1024 setzen, haben wir 122.07 Timertakte bis zum Überlauf. Das sieht schon deutlich besser aus.

Initialisierung:
zaehler = 0, als Byte definiert
Timer Prescaler auf 256
TCNT2 auf 256 - 122 = 134 setzen, der Timer zählt dann von 134 bis 256 hoch und löst den Interrupt aus

Im Interrupt:
TCNT wieder auf 134 setzen
zaehler inc

In der Hauptschleife:
ist zaehler = 0, eine Sekunde vorüber

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATmega328p Timer

Beitrag von Timm Thaler »

Wie man die Timer einrichtet hängt sehr stark von den Anforderungen ab - und braucht Erfahrung.

Zum Beispiel kann man den Timer auch so einstellen, dass er alle 10msec den Interrupt auslöst. Und dann läßt man den zaehler nicht bei 256 überlaufen, sondern setzt ihn bei 100 zurück. Damit erreicht man auch 1 Sekunde, aber mit 10msec Taktung. Die 10msec kann man dann noch nutzen, um eine Entprellroutine für angeschlossene Tasten aufzurufen. Oder man setzt den zaehler bei 200 zurück, und hat 2 Sekunden: Unter 100 LED ein, darüber LED aus, blinkt mit 2 Sekunden.

Initialisierung:
zaehler = 0, als Byte definiert
Timer Prescaler auf 1024 (10msec passen nicht mehr in 256 x 256 x 8MHz)
TCNT2 auf 256 - 78 = 178 setzen

Im Interrupt:
TCNT wieder auf 178 setzen
zaehler inc
if zaehler = 100 then zaehler = 0 (hier auf keinen Fall mit mod arbeiten, das ruft eine Ganzzahl-Division in Software auf und dauert zu lange)

In der Hauptschleife:
ist zaehler = 0, eine Sekunde vorüber

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATmega328p Timer

Beitrag von Mathias »

1 Sekunde ist mit Timer2 an 8MHz nicht möglich.

Da hätte ich noch lange mit Registernwerten suchen können. :wink:

Du erzeugst mit Timer2 alle paar Millisekunden einen Interrupt und zählst darin eine Variable hoch. Ist die wieder Null, ist eine Sekunde vorbei.

Was bringt der Interrupt für einen Vorteil gegenüber dem was ich mit dem "zaehler" gemacht habe ?

Systemtakt ist 8MHz. Prescaler ist 1024. Timertakt ist dann um die 8kHz.

Verstehe ich das richtig, das mit diesem Prescaler der Timer 8'000/Sek. aufgerufen wird ?

16'000'000 / 1024 / 16'000 = ~1

Wen dies so wäre, dann müsste cl in meinem Fall 16'000 sein, da mein Nano mit 16MHz getaktet ist.
Aber wieso wechselt die LED nur mit cl = 32 ca. alle Sek. auf High ? :roll:
Irgendetwas verstehe ich falsch. :oops:

Code: Alles auswählen

  procedure t2over_interrupt; alias: 'TIMER2_OVF_ISR'; interrupt; public;
  const
    zaehler: integer = 0;
    cl = 32;
  begin
    Inc(zaehler);
    if zaehler = cl then begin
      PORTB := PORTB or (1 shl BP5);
    end;
    if zaehler >= cl * 2 then begin
      PORTB := PORTB and not (1 shl BP5);
      zaehler := 0;
    end;
  end;
 
begin
  DDRB := DDRB or (1 shl BP5);       // Pin 13 Output
 
  TCCR2A := %00;          // Normal
  TCCR2B := %111;         // Clock / 1024
  TIMSK2 := (1 shl TOIE2); // enable timer2 overflow interrupt
  sei();                             // enable interrupts
 
  repeat
  until 1 = 2;
end
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATmega328p Timer

Beitrag von Timm Thaler »

Mathias hat geschrieben:Was bringt der Interrupt für einen Vorteil gegenüber dem was ich mit dem "zaehler" gemacht habe ?


Es ist fast das, was Du in der Interruptroutine machst. Außer dass TCNT in der ISR immer neu gesetzt werden muss, wenn Du genaues Timing willst. Und dass Du den zaehler außerhalb der Routine in der Mainloop auswertest.

Merke: Interrupts immer so kurz wie möglich halten.

Mathias hat geschrieben:Wen dies so wäre, dann müsste cl in meinem Fall 16'000 sein, da mein Nano mit 16MHz getaktet ist.
Aber wieso wechselt die LED nur mit cl = 32 ca. alle Sek. auf High ? :roll:


Nö, passt schon:

Der 16MHz Quarz wird durch 1024 geteilt. Das ist der Vorteiler für den Timer: 15,625kHz = 0,064msec
Mit diesem Vorteiler wird jetzt TCNT hochgezählt, also ein Schritt alle 0,064msec.
Da TCNT von 0 bis 255 zählt, ergibt das 256 Schritte = 16,384msec. Jetzt wird ein Timer Overflow ausgelöst und wieder bei 0 angefangen.
Im Interrupt wird jetzt alle 16msec zaehler + 1 hochgezählt. 32 Schritte x 16msec = 0,5 sec, 64 Schritte = 1 sec.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATmega328p Timer

Beitrag von Timm Thaler »

Tip: Wenn man etwas Ahnung von Digitalelektronik hat, also Gatter, Zähler kennt, dann sind gerade bei den Timern die Schaltbilder in den Datenblättern sehr hilfreich. Da wird schnell klar, welcher Takt woher kommt, wie geteilt wird und wie gezählt wird.

Letztlich darf man nicht vergessen: In den AVRs sind das Logikschaltungen, wie man sie auch diskret aufbauen kann. Der Prescaler wäre sowas wie der CMOS 4060, und mit CS2..0 entscheide ich, an welchem Pin ich den Takt abnehme. Der Takt geht auf einen 8-bit-Zähler aus zwei CMOS 4516, der von 0 bis 255 durchzählt. Diesen Zähler kann ich über die P-Eingänge mit einem Wert voreinstellen. Bei einem Übertrag an COUT wird der Overflow Interrupt ausgelöst. Durch einen Vergleich des Zählerstandes mit zwei CMOS 4063 Vergleichern kann ein Output Compare Interrupt ausgelöst werden.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATmega328p Timer

Beitrag von Mathias »

Jetzt habe ich die Sekunde recht genau.

Code: Alles auswählen

cl = 16000000 div 1024 div 256 div 2;

Ich habe das TCNT2 nicht berücksichtigt.

Sowas hat in der Timer-Procedure einen Einfluss:

Code: Alles auswählen

if TCNT2 < 128 then TCNT2 := 200;      

Dies muss ich mir mal genauer angucken.

Dank dir werde ich was AVR anbelangt immer schlauer. :wink:
Da kann man sich krank googlen und man findet nichts schlaues. :evil:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: ATmega328p Timer

Beitrag von Mathias »

Tip: Wenn man etwas Ahnung von Digitalelektronik hat, also Gatter, Zähler kennt, dann sind gerade bei den Timern die Schaltbilder in den Datenblättern sehr hilfreich. Da wird schnell klar, welcher Takt woher kommt, wie geteilt wird und wie gezählt wird.
Etwas verstehe ich schon von Digital-Elektronik, aber auch dort kann man noch viel lernen. :wink:
Mit den Datenblättern habe ich recht Mühe, Elektronik habe ich nie in einer Schule, Kurse gelernt, ich habe fast alles aus Büchern und heutzutage mit Google gelernt.
Aber immerhin habe ich es geschafft einen 8Bit Voll Addierer /Subtrahiereer aus lauter Transistoren zu bauen. Ich gebe es zu, die visuelle Ausgabe übernimmt AVR mit einer 4-stelligen 7-Segment-Anzeige. Wen du willst, kann ich mal ein Bild hochladen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: ATmega328p Timer

Beitrag von Timm Thaler »

Mathias hat geschrieben:

Code: Alles auswählen

if TCNT2 < 128 then TCNT2 := 200;      


Damit zählt TCNT immer nur von 200 bis 255, macht dann Overflow auf 0, der Interrupt wird aufgerufen und da TCNT 0 ist wird es auf 200 gesetzt und zählt wieder bis 255.

Allerdings kann man sich das IF auch sparen, denn wenn der Interrupt aufgerufen wird ist TCNT immer 0 oder etwas darüber.

Warum etwas darüber? Nun der Timer läuft ja weiter, und wenn der Controller inzwischen einen anderen Interrupt abarbeitet oder an einer Programmstelle ist wo Interrupts disabled sind, wird der Timer Interrupt erst ausgeführt, wenn der andere Interrupt verlassen oder Interrupts wieder enabled sind. Und bei schnellen Timern (kleiner Prescaler) oder langen Interruptroutinen kann TCNT inzwischen schon weitergezählt haben.

Das ist ja das Schöne an diesen Hardware-Timern, Hardware-Uart, Hardware-SPI - die arbeiten auch weiter wenn der Controller anderweitig beschäftigt ist. Und sogar wenn der Controller schläft.

Antworten