Forum: Mikrocontroller und Digitale Elektronik PIN-Register mittels USART in festem Zeitintervall ausgeben


von Lukas Bommes (Gast)


Lesenswert?

Hallo Zusammen,

ich bastele mir gerade einen einfachen Logik-Analyser auf Basis eines 
AtMega2560.
Ziel ist es, das PINC-Register in einem konstanten Intervall von 1 
Mikrosekunde mittels des USARTs an den PC zu schicken, wo die Daten 
geloggt werden. Nun stellt sich die Frage, wie ich sinnvollerweise 
hierbei vorgehen könnte.
Meine Idee zur Umsetzung ist im untenstehenden Code zu erkennen. Mittels 
Timer 1, der im CTC-Modus bei 1 MHz betrieben wird, soll der 
TIMER1_COMPA-Interrupt ausgelöst werden, innerhalb dessen ISR das 
PINC-Register über den USART an den PC gesendet wird.

Verwende ich die auskommentierte Variante, d.h. werden die Daten so 
schnell wie möglich versendet (immer wenn das Sende-Register leer ist), 
funktioniert alles problemlos. Allerdings habe ich Probleme mit dem 
unten stehenden Code, d.h. der Kombination aus Timer und USART. Es 
kommen hierbei einfach keine Daten am PC (Putty) an.
Sieht zufällig jemand meinen Fehler? Oder habt ihr grundlegend andere 
Vorschläge, wie man Daten in einem festen Zeitintervall von 1 us über 
den USART zu verschicken könnte?

Vielen Dank schonmal!

Beste Grüße
Lukas
1
const float sample_rate = 1000000; // [Samples per sec.]
2
3
void setup() {
4
  cli();
5
  TIMSK0 &= ~(1<<TOIE0); // disable timer0 interrupt (Arduino specific)
6
  sei();
7
  
8
  // Todo: setup timer to send data in specific intervals
9
  // check, if bit UDRE0 in UCSR0A is set (data has been sent)
10
  // desired sample time 1 MSpS
11
  
12
  // Todo2: start measurement on external interrupt and let it
13
  // run for desired delta time, e.g. 1 second
14
  
15
  cli();
16
  // configure input pins
17
  DDRC = 0x00; // set port C as input
18
  PORTC = 0xff; // enable internal pullups
19
  
20
  // setup timer for sampling
21
  TCCR1A = 0;
22
  TCCR1B = 0;
23
  TCNT1  = 0; // initialise counter with 0
24
  OCR1A = (16000000.0/(sample_rate*2))-1.0; // (needs to be < 65536) // set compare match register
25
  TCCR1B |= (1 << WGM12); // ctc mode
26
  TCCR1B |= (1 << CS10); // set no prescaling
27
  TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
28
  
29
  // setup usart
30
  int ubrr = 1; // gives a baud rate of 1 Mbit/s, s. datasheet page 226
31
  UBRR0H = (unsigned char)(ubrr>>8); // baud rate
32
  UBRR0L = (unsigned char)ubrr;
33
  UCSR0A |= (1<<U2X0); // double transmission speed
34
  UCSR0B = (1<<TXEN0);// | (1<<UDRIE0); // Enable transmitter, enbale interrupt
35
  UCSR0C |= (1<<USBS0) | (1<<UCSZ00) | (1<<UCSZ01); // Frame format: 8 data bits, 2 stop bits
36
  sei();
37
}
38
39
// usart interrupt
40
/*
41
ISR(USART0_UDRE_vect)
42
{
43
  UDR0 = PINC;
44
}*/
45
46
// timer interrupt
47
ISR(TIMER1_COMPA_vec)
48
{
49
  //if(UCSR0A & (1<<UDRE0)) UDR0 = PINC; // send only, when data register is empty
50
  UDR0 = PINC;
51
}
52
53
void loop() {
54
}

von Thomas E. (picalic)


Lesenswert?

Bei der Datenrate von 1 MBit und zwei Stopbits dauert die Übertragung 
eines Bytes 11µs, da wirst Du wohl kaum jede µs ein neues Datenbyte 
verschicken können!

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Lukas Bommes schrieb:
> ich bastele mir gerade einen einfachen Logik-Analyser auf Basis eines
> AtMega2560.

Du weißt, dass es von Cypress den CY7C68013a gibt und der einen 
wunderbaren, einfachen 8-Bit Logik-Analyser mit 24MHz Abtastrate abgibt?
Bei ebay gibt es den Chip fertig im Gehäuse für unter 7€ (z.B. 
331803014389).
Außer aus sportlichem Ehrgeiz gibt es da eigentlich keinen Grund, einen 
ATmega zu quälen, um mal ein paar Bits live auf die Finger zu schauen.

von Lukas Bommes (Gast)


Lesenswert?

Hallo ihr beiden,

danken für eure Antworten!

@Thomas: Ich hatte nicht bedacht, dass pro Sendevorgang 11 bit 
verschickt werden müssen. Somit hast du natürlich vollkommen Recht. 
Allerdings erreicht der Mega 2560 damit bei 16 MHz Systemtakt nur knapp 
90 KHz Sampling-Rate, was ja nicht so viel ist.

@Wolfgang: Danke für den Tipp! Aber "sportlicher Ehrgeiz" ist das 
Stichwort. ;-) So ein Logik Analyzer ist ja ein ganz nettes Projekt, um 
mal ein bisschen zeitkritischeren Code zu schreiben.

Aber ich habe noch eine prinzipielle Frage, bei der ihr mir ja 
vielleicht weiterhelfen könnt. Und zwar kommt mir meine Lösung über den 
Timer etwas unelegant vor. Kann man stattdessen auf Basis der Kenntnis 
der Baud Rate von 1 Mbit pro Sekunde und der Größe eines Frames von 11 
bit eine exakte Aussage über die Zeit machen, in der in meinem Code oben 
das PINC-Register via UART versendet wird? (Bei Verwendung des 
USART0_UDRE-Interrupts.)
Sprich, darf ich davon ausgehen, dass alle gesendeten Messpunkte 
tatsächlich in einem Abstand von exakt 11 us liegen, oder ist mit 
Abweichungen zu rechnen?

Viele Grüße
Lukas

P.S. In meinem Code oben muss es übrigens TIMER1_COMPA_vect statt 
TIMER1_COMPA_vec heißen. Daher wurde die ISR auch nicht aufgerufen.

von Draco (Gast)


Lesenswert?

Nein, der Sendepuffer muss ja auch noch gelehrt werden.

Mach dir doch ein einfaches Programm, schick deinen Port direkt über UDR 
raus. Prüfe aber vorher ob der Sendepuffer auch leer ist (UDRE Bit im 
UCSRA Register). Und mess das ganze mal mit deinem Oszi nach.

von c-hater (Gast)


Lesenswert?

Lukas Bommes schrieb:

> Aber ich habe noch eine prinzipielle Frage, bei der ihr mir ja
> vielleicht weiterhelfen könnt. Und zwar kommt mir meine Lösung über den
> Timer etwas unelegant vor. Kann man stattdessen auf Basis der Kenntnis
> der Baud Rate von 1 Mbit pro Sekunde und der Größe eines Frames von 11
> bit eine exakte Aussage über die Zeit machen, in der in meinem Code oben
> das PINC-Register via UART versendet wird? (Bei Verwendung des
> USART0_UDRE-Interrupts.)

Natürlich. Da die USART double-buffered ist, kann man einen 
kontinuierlichen Datenstrom mit Vmax darüber verschicken. Jedes Byte 
wird dann exakt in dem Abstand versandt, der durch Bitrate und Bitzahl 
der Frames vorgegeben ist. Das gilt natürlich nur, so lange nix anderes 
(insbesondere konkurrierende längliche ISRs) den Ablauf stört und der µC 
deshalb nicht dazu kommt, schnell genug Daten nachzuliefern...

Übrigens: Wenn die einzige Aufgabe des Gerätes die Sache sein soll, die 
Eingabe eines 8Bit-Port per USART zu versenden, ist ein Mega2560 
natürlich massiv überdimensioniert, dafür reicht dann auch ein Tiny2313 
völlig aus.

Und man braucht dann auch keinen Interrupt, dann ist es wesentlich 
effizienter, die Sache in einer kleinen Endlosschleife in main() 
abzuwickeln. Es entfällt dann nämlich der Interrupt-Overhead, der gerade 
bei einem Mega2560 sogar deutlich grosser als bei den meisten anderen 
AVR8 ist (wegen des 22Bit-PC).

> tatsächlich in einem Abstand von exakt 11 us liegen

Warum eigentlich 11µs? Bei 8N1 wären es 10. Die runde Zahl kommt 
irgendwie viel cooler.

von Draco (Gast)


Lesenswert?

Also mich würde es extrem stören wenn ich bei einem Logikanalyzer keinen 
Trigger hätte ;-) Ansonsten ist das ganze nämlich bloß nen 8 Kanal / 
1Bit Oszilloskop - eben auch ohne Trigger.

von c-hater (Gast)


Lesenswert?

Draco schrieb:

> Also mich würde es extrem stören wenn ich bei einem Logikanalyzer keinen
> Trigger hätte

Mich auch. Aber was hat das jetzt mit dem Thema des Threads zu schaffen?

von Wolfgang (Gast)


Lesenswert?

Draco schrieb:
> Also mich würde es extrem stören wenn ich bei einem Logikanalyzer keinen
> Trigger hätte ;-)

Der Trigger kann doch auf dem empfangenden PC Laufen. Der µC streamt nur 
fortlaufend die Daten.

von Thomas E. (picalic)


Lesenswert?

Wolfgang schrieb:
> Der Trigger kann doch auf dem empfangenden PC Laufen. Der µC streamt nur
> fortlaufend die Daten.

Das hat außerdem den Vorteil, daß man auch sehen kann, was kurz vor dem 
Triggerzeitpunkt passiert ist!

von Pandur S. (jetztnicht)


Lesenswert?

Man sollte mit einem Tracebuffer auf dem Mega arbeiten. Dort waehrend 
dem Trigger erwarten in einen ringbuffer aufzeichen, und erst 
uebertragen wenn der Trigger gekommen ist und der Tracebuffer die 
gewuenschten Daten enthaelt.

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.