Hallo Liebe Gemeinde,
Ich benötige mal eure Hilfe in Sachen Kommunikation zwischen AVR's.
Zur Hardware:
Der Sender ist ein ATmega162PU. Dieser sendet über ein Bluetooth-Modul
(BTM222) folgende Informationen:
1
A,0
2
B,0
3
C,0
4
D,0
5
E,48
6
F,56
7
G,0
8
H,0
9
I,0
10
J,0
11
K,0
12
L,0
13
M,0
14
N,0
15
O,0
16
P,0
17
Q,0
18
R,0
19
S,0
20
T,0
21
U,0
22
V,0
23
W,0
24
X,0
25
Y,0
Das funktioniert auch ganz Gut. Die Zahlen hinter den Buchstaben sind
Werte eines DMX-Pultes.
Auf Empfängerseite ist ebenfalls ein BTM222 mit einem ATMega644.
Das Bluetooth-Modul empfängt auch alles wunderbar. Nur dem AVR konnte
ich das zuhören noch nicht richtig beibringen.
Um Die Einstellung Baudrate mache ich mir keine Sorgen. Aber der
Vollständigkeit halber: Der AVR läuft mit einem Externen 16MHz Quarz.
Die Baudrate beträgt 19200 und der Code um die ganze geschichte zu
Initialisieren sieht so aus:
1
UBRR1H=(unsignedchar)(F_CPU/(BAUD*16L)-1)>>8;
2
UBRR1L=(unsignedchar)(F_CPU/(BAUD*16L)-1);
3
4
UCSR1A=(1<<RXC1);
5
UCSR1B=(1<<RXEN1)|(1<<TXEN1)|(1<<RXCIE1);
6
UCSR1C=(1<<UCSZ11)|(1<<UCSZ10);
7
sei();
Die Einzige Debugging-möglichkeit die ich habe ist über dem UART0 die
Daten Auszugeben und mir am PC im Terminal anzusehen.
Hier ist mein Code um den Empfang zu realisieren:
Auszug aus der UART.h:
1
charuart_getchar()
2
{
3
// Ist schon ein Zeichen im Buffer?
4
while(!(UCSR1A&(1<<RXC1)));
5
returnUDR1;
6
}
7
8
voiduart_readline(char*str)
9
{
10
charc;
11
unsignedcharindex;
12
13
index=0;
14
15
while(c!=13)
16
{
17
c=uart_getchar();
18
if(c!=-1)
19
{
20
if(c==13)/*ASCII: NewLine */
21
{
22
str[index]='\0';/* Ende der Zeile erreicht,also String abschließen */
23
/* Funktion beenden */
24
return;
25
}
26
else/*Ansonsten normales Zeichen, anhängen an die Zeichenkette */
27
{
28
str[index]=c;
29
index++;
30
}
31
}
32
}
33
}
34
[c]
35
36
37
38
UnddieMain.c
39
40
[c]
41
ISR(USART1_RX_vect)
42
{
43
44
chareingabe[255];
45
charchan[1];
46
charval;
47
uart_readline(eingabe);
48
uart_puts0("\r\n");
49
uart_puts0("Eingabe ist: ");
50
uart_puts0(eingabe);
51
char*ptr;
52
ptr=strtok(eingabe,",");
53
strcpy(chan,ptr);
54
uart_puts0("\nChan:");
55
uart_puts0(chan);
56
ptr=strtok(NULL,",");
57
val=atoi(ptr);
58
uart_puts0("\nVal:");
59
uart_putc0(val);
60
uart_puts0("\n");
61
}
62
63
64
65
intmain(void)
66
{
67
uart_init();
68
DDRB=0b00000001;// Feedback LED
69
PORTB|=(1<<0);// Feedback LED AN
70
_delay_ms(1000);
71
PORTB&=~(1<<0);// Feedback LED AUS
72
73
while(1)
74
{
75
76
PORTB|=(1<<0);
77
_delay_ms(50);
78
PORTB&=~(1<<0);
79
_delay_ms(100);
80
}
81
82
83
}
Hier ist ein Ausschnitt von dem, was mir mein kleiner Krabbler so
ausspuckt:
1
Eingabe ist: A,0
2
Chan:A
3
Val:[00]
4
5
Eingabe ist: B,–¸?,4hxÄÇ€·ŽZ>rd~ÿ
6
Chan:B
7
Val:[00]
8
9
Eingabe ist: E,50
10
Chan:E
11
Val:2
12
13
Eingabe ist: F,,104
14
Chan:F
15
Val:h
16
17
Eingabe ist: M,8
18
Chan:M
19
Val:[08]
20
21
Eingabe ist: Q,100
22
Chan:Q
23
Val:d
24
25
Eingabe ist: W,
26
Chan:W
27
Val:[00]
Manchmal funktioniert alles wie geplant, manchmal setzt er ein Komma da
wo keins sein soll. Manchmal Datensalat. Hin und wieder versagt strtok.
Alles in allem zu ineffizient. Ich tippe mal Stark darauf, dass die Art,
wie ich den Interrupt abarbeite zu lange dauert und der AVR deswegen
info's verliert.
Mir ist auch klar, dass ich durch diese ganzen uart_puts0("whatever");
die ISR unfassbar verlängere.
Leider habe ich wie gesagt keine andere Möglichkeit in den AVR zu
gucken. Des weiteren bin ich auch am Ende meiner Programmiererischen
Fähigkeiten und hoffe, dass ihr mir den Entscheidenden Tipp gebt.
Bin für jeden konstruktiven Rat dankbar.
Christian S. schrieb:> Bin für jeden konstruktiven Rat dankbar.
Lass deiner ISR Routine ein bisschen Luft, mach die USART Ausgabe
in main.
In der ISR Stringende abwarten, Flag setzen, Rest in main()
Da sieht man mal wieder, wie mal sich selbst in's Abseits stellt.
Programmiere einen RX- und TX- Fifo für jeweils einen Uart, dann kann
man ganz bequem in der Main-Loop den Datenstrom per DEA analysieren und
entsprechend deiner Vorgaben reagieren.
Christian S. schrieb:> Die Einzige Debugging-möglichkeit die ich habe ist über dem UART0 die> Daten Auszugeben und mir am PC im Terminal anzusehen.
Dann solltest du vielleicht mal 8€ in einen kleinen Logikanalysator
investieren, damit du dir solche Dinge zeitlich genau ansehen kannst,
z.B. indem du einen IO-Pin setzt, solange der µC in der ISR am werkeln
ist und sehen kannst, wie das in Bezug zur Datenübertragung steht.
Die ISR wird normalerweise für jedes empfangene Zeichen ausgelöst, du
hast also, wenn du ganze Strings in der ISR abfragst (abgesehen von dem
zeitraubenden extra Krams) auf jeden Fall nach dem ersten empfangenen
Zeichen beim zweiten Zeichen einen 'pending' Interrupt, der dazu führt,
das die ISR beim Verlassen auf jeden Fall sofort wieder getriggert wird,
egal, ob da noch ein Zeichen ist oder nicht.
Die ISR sollte also, wie oben schon angedeutet, nur ein Zeichen
empfangen und kann meinetwegen Flags oder was auch immer setzen, um CR
oder Komma zu signalisieren. Bei DMX bietet es sich an, die Daten ohne
Rücksicht in einen Buffer zu schreiben und während des langen Mark/Space
zu bearbeiten.
Danke für eure Antworten
Wolfgang schrieb:> Christian S. schrieb:>> Die Einzige Debugging-möglichkeit die ich habe ist über dem UART0 die>> Daten Auszugeben und mir am PC im Terminal anzusehen.>> Dann solltest du vielleicht mal 8€ in einen kleinen Logikanalysator> investieren, damit du dir solche Dinge zeitlich genau ansehen kannst,> z.B. indem du einen IO-Pin setzt, solange der µC in der ISR am werkeln> ist und sehen kannst, wie das in Bezug zur Datenübertragung steht.
Logic-Analyser ist vorhanden, allerdings habe ich in diesem Zusammenhang
keine Verwendung gesehen. Dass die Abarbeitung der ISR zu lange dauert
habe ich auch ohne Analyzer herausgefunden indem ich einfach meine
Blinkende LED aus der main beobachtet habe. Ist zwischendurch immer
wieder stehen geblieben. Aber der Tipp ist gut. Ohne meine Feedback-LED
oder einem Analyzer hätte ich mich wohl tot gesucht.
Die Idee mit dem FIFO bin ich mal angegangen, allerdings mit mäßigem
Erfolg. Ich habe mehrere fertig gestrickte versionen eines Ringpuffers
ausprobiert und bin dann letztendlich bei diesem hier stehen geblieben:
https://www.mikrocontroller.net/articles/FIFO#Code-Beispiel
Und hier lässt mich wieder mein Hirn im Stich. Diese Pointerarithmetik
ist mir noch'ne nummer zu hoch. Ich habe den FIFO-Buffer aus dem
Tutorial ohne änderungen in mein Projekt intigriert und meinen Code wie
folgt angepast:
main.c
1
ISR(USART1_RX_vect)
2
{
3
BufferIn(uart_getchar());
4
}
5
6
intmain(void)
7
{
8
uart_init();
9
while(1)
10
{
11
12
uint8_teingabe;
13
BufferOut(&eingabe);
14
15
uart_puts0("\r\n");
16
uart_puts0("Eingabe ist: ");
17
uart_putc0(eingabe);
18
19
PORTB|=(1<<0);
20
_delay_ms(50);
21
PORTB&=~(1<<0);
22
_delay_ms(100);
23
}
24
25
26
}
UART.h
1
charuart_getchar()
2
{
3
while(!(UCSR1A&(1<<RXC1)));
4
returnUDR1;
5
}
6
7
voiduart_readline(char*str)
8
{
9
charc;
10
unsignedcharindex;
11
12
index=0;
13
14
while(c!=13)
15
{
16
c=BufferOut(&str);
17
if(c!=-1)
18
{
19
if(c==13)
20
{
21
str[index]='\0';
22
return;
23
}
24
else
25
{
26
str[index]=c;
27
index++;
28
}
29
}
30
}
31
}
Auf dem ersten blick in meinen Terminal habe ich mich gefreut. Nach dem
Controller-Start sieht es so aus:
1
Eingabe ist: A
2
Eingabe ist: ,
3
Eingabe ist: 0
4
Eingabe ist:
5
6
Eingabe ist: B
7
Eingabe ist: ,
8
Eingabe ist: 0
9
Eingabe ist:
10
11
Eingabe ist: C
12
Eingabe ist: ,
13
Eingabe ist: 0
14
Eingabe ist:
15
16
Eingabe ist: D
17
Eingabe ist: ,
18
Eingabe ist: 0
Danach kommt allerdings nurnoch Block A und B in Dauerschleife. Selbes
spiel, wenn ich den Puffer erhöhe. Dann schafft die erste runde es
lediglich ein paar Buchstaben weiter im Alphabet.
ICh muss aber dazu sagen, dass mein Compiler einige Warnugen und
Hinweise ausspuckt:
1
expected 'uint8_t *' but argument is of type 'char **'
dabei bezieht er sich auf die stelle stelle im FIFO-Code
1
uint8_tBufferOut(uint8_t*pByte)
und
1
Warning 1 passing argument 1 of 'BufferOut' from incompatible pointer type [enabled by default]
Christian S. schrieb:> Das funktioniert auch ganz Gut.Christian S. schrieb:> while (!(UCSR1A & (1<< RXC1)));
Nein, das funktioniert eben nicht ganz gut, sondern ganz schlecht.
Du machst den immer wiederholten Anfängerfehler, mit dem Kopf durch die
Wand programmieren zu wollen ohne auch nur einmal links oder rechts zu
schauen.
Also:
1. schreib dir einen ordentlichen UART-Treiber! Das ist so einer, der
beide Zeichenströme puffert (in und out), der die Zeichen-Ein- und
Ausgabe per Interrupt erledigt und eine hardwareunabhängige
Schnittstelle zu den höheren Programmschichten bietet. Ich hab hier
schon so oft was Entsprechendes gepostet.
2. mache kein derartiges Gedöns wie in deiner geposteten
Interruptroutine! In eine ISR kommt sowas nicht rein, merk dir das.
Grund: du blockierst damit nämlich auf sinnlosesete Weise den ganzen µC,
weil er in der ISR auf irgend ein langsames Flag warten muß, ohne
derweil was Sinnvolles tun zu können. Selbst ein anderer Interrupt geht
bei sowas regelmäßig flöten.
3. zur Auswertung:
Lade dir am besten die Lernbetty herunter, die ist zwar für einen
ARM7TDMI, aber bis auf die Innereien des UART-Treibers ist z.B. die
gesamte Kommandoauswertung hardwareunabhängig. Das könnte dann so
aussehen:
if (match("A,",&Cpt)) { Awert = Long_In(&Cpt); return;}
if (match("B,",&Cpt)) { Bwert = Long_In(&Cpt); return;}
usw.
Also denke zuerst nach und hau erst dann in die Tasten.
W.S.