Hallo!
Ich bin Neuling in der AVR-Programmierung (und in C auch, komme
eigentlich aus der Clipper- und VB-Welt) und habe folgendes Problem:
Ich habe ein Progrämmchen gebastelt, dass Eingaben über ein Terminal
(bei mir HTerm) entgegennimmt, diese auf Tastendruck in einem Array und
schließlich im EEPROM speichert, und (ebenfalls per Taster) wieder auf
dem Terminal ausgibt. Dabei kann man auch mehrere Eingaben
hintereinander tätigen und mit Enter bestätigen. erst beim Drücken des
'SAVE'-Tasters wird der komplette String ins EEPROM geschrieben.
Nun funktioniert das soweit ganz gut, jedoch gibt es manchmal das
Phänomen, dass nicht alle Zeichen gespeichert werden. Z.B. sende ich 10
Zeichen "1234567890", was mehrmals hintereinander klappt. Dann jedoch
kommen nur 2 oder 3 der 10 Zeichen an. Alles im selben
Programmdurchlauf. Danach kann wieder alles funktionieren. Sehr seltsam.
Die Taktung müsste stimmen (F_CPU=4000000, BAUD_RATE=9600), ich bekomme
mit dem Funktionsaufruf long_delay_ms(1000) auch eine Verzögerung von
genau 1 Sekunde. Das ist doch das Indiz schlechthin, dass in der
Hinsicht alles stimmt, oder?
Jedenfalls arbeite ich schon seit Tagen an dem Programm und bekomme es
nicht 100%-ig hin. Ich habe mehrere Test-Ausgaben eingebaut. Bbei Druck
auf SW0-Taster wird 'SAVED' gesendet, bei SW2 wird der Wert von
iUSART_inp ausgegeben (Anzahl der eingelesenen Zeichen). Dieser Wert ist
dann beim Auftreten des Fehlers entsprechend klein (z.B. 2 statt 10
Zeichen).
Ich verwende den Tiny2313 mit dem STK500 und das AVR-Studio 4.13 (Build
528).
Ich poste hier einfach mal den ganzen Code. Angehängt habe ich einen
ScreenShot von HTerm, wo sehr gut zu sehen ist, was ich meine. Die
Reihenfolge bei HTerm war folgendermaßen: Eingabe, SW2 (iUSART_inp
ausgeben), SW0 (im EEPROM speichern), SW1 (EEPROM auslesen und ans
Terminal schicken).
1
#ifndef F_CPU
2
#define F_CPU 4000000
3
#endif
4
#ifndef UART_BAUD_RATE
5
#define UART_BAUD_RATE 9600
6
#endif
7
//
8
#include<stdlib.h>
9
#include<avr/io.h>
10
#include<avr/eeprom.h>
11
#include<stdio.h>
12
#include<util/delay.h>
13
/*
14
#include <avr/interrupt.h>
15
#include <stdint.h>
16
#include <stdbool.h>
17
*/
18
//
19
//PROTOTYPEN
20
voidmyWait(unsignedintiX);
21
unsignedintmyPot2(unsignedintiExp);
22
voidUSART_init(unsignedintbaud);
23
voidUSART_transmit(unsignedcharcData);
24
voidUSART_transmit_str(char*cStr);
25
voidlong_delay_ms(unsignedlongms);
26
27
28
/***************/
29
intmain(void){
30
/***************/
31
unsignedshortinti=0;
32
char*cUSART_inp=NULL;
33
unsignedshortintiUSART_inp=0;
34
unsignedshortiTest=1;
35
charcZahl[5]={0};
36
unsignedcharcBoolSaved=0;
37
//
38
DDRB=0xff;//Port B Pins als Ausgänge definieren
39
PORTB=0xff;
40
//DDRD = 0x00; //Port D Pins als Eingänge definieren. Wohl nicht notwendig, da die Bits standardmäßig sowieso 0 sind
41
//
42
USART_init(UART_BAUD_RATE);
43
//
44
for(;;){
45
//
46
/*
47
if( ++iTest % 500 == 0 ) {
48
USART_transmit('.');
49
iTest = 0;
50
}
51
*/
52
//
53
if(UCSRA&(1<<RXC)){//Zeichen werden empfangen und im char-array gespeichert
UCSRB=(1<<RXEN)|(1<<TXEN);//Receiver und Transmitter aktivieren
137
}
138
139
/***************/
140
voidUSART_transmit(unsignedcharcData){
141
/***************/
142
while(!(UCSRA&(1<<UDRE))){
143
;//warten, bis der transmit buffer leer ist, so dass wieder übertragen werden kann
144
}
145
UDR=cData;
146
}
147
148
/***************/
149
voidUSART_transmit_str(char*cStr){
150
/***************/
151
while(*cStr){
152
USART_transmit(*cStr++);
153
}
154
}
Ich hoffe, jemand findet hier den Fehler oder kann mir sonst irgendeinen
Tipp geben. Ich verliere langsam den Mut... ach, wär ich doch beim
Clipper geblieben...
Was mir nicht gefällt sind die ganzen Wartereien im Programm.
Mit dem realloc bin ich auch nicht wirklich glücklich.
zum Problem:
Ich würde mal versuchen, direkt im Empfangsabschnitt das
empfangene Zeichen zu echoen um zu sehen ob da im
Fehlerfall überhaupt was ankommt, bzw. ob da irgnedwelche
Zeitverzögerungen erkennbar sind.
Hi Karl Heinz,
erstmal HERZLICHEN DANK dafür, dass du dir diese Mühe machst!
Zum Thema Delays und realloc():
Ist ja nur ein Testprogramm, dass ich ständig erweitere. realloc() habe
ich eingebaut, nachdem das Lesen und Schreiben im EEPROM auf dem
direkten Wege nicht gut funktioniert hat (Zeichen haben gefehlt etc.).
Daher habe ich diese Zwischenlösung gebaut, die, denke ich, auch nicht
das Problem ist. Natürlich wäre es im Hinblick auf Codegröße und
Sicherheit sinnvoller, ein statisches Array zu nehmen, da sowieso nur
128 Bytes (tiny2313) im EEPROM zur Verfügung stehen. Sobald ich dafür
Zeit habe...
Die Delays sind meiner Meinung nach notwendig, da sonst die Schleife
mehrmals durchlaufen wird, während man noch den Taster drückt.
Vielleicht ist das keine elegante Lösung, aber ich kenne keine bessere.
Für Vorschläge bin ich offen.
Echo habe ich eingebaut:
1
if(UCSRA&(1<<RXC)){//Zeichen werden empfangen und im char-array gespeichert
es kommen alle Zeichen an und werden unverfälscht wieder ans Terminal
gesendet. Die eigentliche Funktionalität des Programms wird durch diesen
Test aber zerstört. Die Strings werden nur noch zerstückelt gespeichert
(s. Anhang).
Liegt das daran, dass das Datenregister UDR nur dazu gedacht ist, einmal
verwendet zu werden? Würde beispielsweise Folgendes gehen?
1
sTmp=UDR;
2
sTmp2=UDR;
3
sTmp3=UDR;
Oder muss man daraus dieses machen, damit es funktioniert?
1
sTmp=UDR;
2
sTmp2=sTmp;
3
sTmp3=sTmp;
So, ich sehe also nun, dass die Zeichen korrekt gesendet und wieder
empfangen werden können. Da die bisherige Funktionalität durch den Test
ausgehebelt wird (oder mache ich da etwas falsch?!), lässt sich leider
nicht sagen, ob im Fehlerfall auch alle Zeichen ankommen oder nicht. Wo,
denkst du, könnte die Fehlerquelle liegen?
Schließlich muss man ja bedenken, dass der Fehler, wie gesagt, ja nur
manchmal auftritt, mit demselben String, der 5 Sekunden vorher gepasst
hat. Ist das etwas, was ich per Code in den Griff bekomme?
Gruß Matze
Hier noch ein ScreenShot von HTerm. Man sieht, dass die ersten drei
Versuche problemlos klappen. Der String "GAAANZ LAAANGER VERSUCH" hat
das Ganze zum Absturz gebracht.
Der 2313 reagiert nicht mehr. Die LEDs 2,3 und 4 (an PORTB) leuchten
plötzlich (hab ich bislang nicht gesehen)! Irgendeine Idee, was das zu
bedeuten hat?
Gruß Matze
So, hier ein ScreenShot, mit dem du vielleicht was anfangen kannst. Es
wurden zwei Strings gesendet:
"langerTextDerOhneLeerzeichenIst"
und
"zweiterLangerTextOhneBlanks"
Direkt nach Eingabe (vor irgendeiner Verarbeitung) wurden jede Menge
binäre Nullen an den Rechner gesendet und das Programm blieb hängen.
Vielleicht kann ja jemand was mit diesem Phänomen anfangen...
Gruß Matze
Sorry, ich wollte sagen, direkt nach Eingabe des 2. Strings wurde die
Nullen gesendet und das Programm blieb hängen. Der erste String wurde
noch (scheinbar) korrekt verarbeitet.
Sieht für mich nach einem Pointer/Speicherfehler aus...
Mach das ganze Programm doch erst mal "sauber". Also mit festem Array
zum abspeichern.
Weiteres Problem könnten die delays sein, wenn während des delays Daten
eingehen können diese nicht verarbeitet werden. Das könntest Du in einem
der USART Register abfragen (komm ich grad nicht drauf), oder noch
besser, Empfang mit einer ISR realisieren.
Gruß
Philipp
Das Schreiben ins EEPROM könnte da ebenfalls noch ein
Problem aufwerfen. Der Vorgang ist relativ langsam.
Ich kann jetzt aber nicht sagen, wie das in Relation
zur Baudrate steht.
Ev. wirst du über ein Handshake auf der UART nachdenken
müssen.
So, es hat sich einiges getan!
Ich habe realloc() rausgeschmissen und durch ein statisches Array
ersetzt (Code um die Hälfte geschrumpft!).
Desweiteren gibt es nun eine Funktion, die (ausgelöst durch einen
Taster)
den restlichen verfügbaren Speicher (SRAM) ans Terminal sendet. Daran
sieht man auch die Problematik mit realloc(). Denn ich habe für das
Array sowieso nur 30 oder 40 Bytes zur Verfügung, der Rest der 128 Bytes
(tiny2313) ist belegt. Deshalb haben kleine Strings immer funktioniert,
große aber nicht.
Das Programm hätte also funktioniert, wenn ich mehr Speicher gehabt
hätte...
Die größte Neuerung sind zwei Funktionen, die ich für das Speichern im
EEPROM geschrieben habe:
eeprom_get_next_byte()
eeprom_get_first_written_byte()
So ist es mir möglich, mich quasi im Kreis zu bewegen und das EEPROM
gleichmäßig zu beschreiben. Ich bin sicher, da gibt es haufenweise
(wahrscheinlich bessere) Lösungen im Internet, aber diese kleine
Herausforderung wollte ich mir nicht nehmen lassen. Man kann schließlich
auch ohne Copy&Paste programmieren (ja, das geht wirklich)!
Einziger Nachteil meiner Lösung: Beim ersten Durchlauf (wirklich nur
nach Programmstart) wird das Byte 0 im EEPROM ausgelassen. Bei jedem
weiteren Durchlauf wird es aber beschrieben. Da mir keine bessere Lösung
einfiel, als mit einer globalen Variable zu arbeiten (kostet ja wieder
Speicher), habe ich es erstmal so gelassen. Dann lebt das erste Byte
eben minimal länger als die anderen...
So, nochmal vielen Dank an alle, die mir geholfen haben!
Da ich denke, dass es viele AVR-Anfänger wie mich gibt, poste ich hier
nochmal den kompletten, aktuellen Code. Ich habe ihn hinreichend
kommentiert und bin sicher, dass dem einen oder anderen damit vielleicht
ein Licht aufgeht.
Kritik/Anregungen/Vorschläge sind ausdrücklich erwünscht!
Gruß Matze
1
/* T E S T P R O G R A M M (USART+EEPROM)
2
* ---------------------------------------
3
*
4
* Dieses Testprogramm (geschrieben für ATTiny2313) empfängt und sendet Zeichen über die
5
* RS232-Schnittstelle (UART/USART) und ermöglicht die folgenden Funktionen:
6
*
7
* SW0 (Taster 0, bei mir an PIND2): empfangene Zeichen im EEPROM speichern
8
*
9
* SW1 (Taster 1, bei mir an PIND3): gespeicherte Zeichen wieder auslesen und ans Terminal senden
10
*
11
* SW2 (Taster 2, bei mir an PIND4): Anzahl aktuell eingelesener Zeichen ans Terminal senden
12
*
13
* SW3 (Taster 3, bei mir an PIND5): freien Speicher (SRAM) ans Terminal senden
14
*
15
* Zum Testen dieses Programm benötigt man ein Terminal-Programm (ich nutze HTerm), mit dem man
16
* Daten zum AVR schicken und von ihm empfangen kann.
17
*
18
* Wenn das Terminal-Programm verbunden ist, sieht man, dass kontinuierlich ein Punkt ('.') gesendet
19
* wird. Dies ist als Lebenszeichen des AVR zu verstehen. Bricht der Datenstrom plötzlich ab, ist
20
* das Programm wahrscheinlich hängengeblieben (z.B. wegen Speicherüberlauf o.ä.).
21
*
22
* Da das SRAM in seiner Größe sehr beschränkt ist (beim ATTiny2313 nur 128 Bytes), habe ich eine
23
* maximale Größe für den empfangenen String festgelegt. Sie kann über IUSART_INP_MAX geändert werden.
24
*
25
* Das Beschreiben des EEPROM ist in der Hinsicht kritisch, als dass der Speicher nur
26
* ca. 100000 mal beschreibbar ist. Deshalb ist es nicht sinnvoll, jedesmal wieder ab
27
* Byte 0 zu schreiben. Deshalb habe ich die Funktionen eeprom_get_last_byte() und
28
* eeprom_get_first_written_byte() geschrieben. Mithilfe dieser Funktionen kann man so
29
* navigieren, dass das EEPROM kontinuierlich beschrieben wird, so dass alle Bytes
30
* gleichmäßig beansprucht werden und die Lebensdauer deutlich erhöht wird. Erreicht man
31
* das Ende des EEPROM (beim ATTiny2313 128 Bytes, änderbar über EEPROM_MAX_BYTE), so
32
* beginnt man wieder bei Byte 0, wobei ein String auch überlappen kann (z.B. ein
33
* 5-Zeichen-String in den Bytes 126, 127, 0, 1 und 2).
34
*
35
* Bei meinem Testboard (STK500) habe ich die Taster nicht nach Standardkonfiguration angeschlossen,
36
* wie oben an der Funktionsübersicht zu sehen ist (SW0 auf PIND2 statt PIND0 usw.). Wer ganz normal
37
* SW0 an PIND0 hat, muss lediglich diese wenigen Stellen im Code abändern.
So, hier nochmal der aktuelle Code. Die Delay-Funktionen habe ich
entfernt und durch eine eigene Funktion zum Tastenentprellen ersetzt.
Außerdem gibt es noch weitere kleine Verbesserungen.
Perfekt ist es noch nicht, aber ich muss mich jetzt meiner produktiven
Aufgabe widmen und kann leider nicht weiter am Testprogramm werkeln.
Die funktion long_delay_ms() wird nicht mehr benutzt. Ich habe sie aber
drinnengelassen, da ich finde, dass eine solche Funktion für Anfänger
zum Testen doch sehr nützlich ist. Bitte nicht schlagen! Ich weiß ja,
dass delays keinen guten Ruf haben, aber wir reden hier von den ersten
Gehversuchen in der AVR-Welt...
1
/* T E S T P R O G R A M M (USART+EEPROM)
2
* ---------------------------------------
3
*
4
* Dieses Testprogramm (geschrieben für ATTiny2313) empfängt und sendet Zeichen über die
5
* RS232-Schnittstelle (UART/USART) und ermöglicht die folgenden Funktionen:
6
*
7
* SW0 (Taster 0, bei mir an PIND2): empfangene Zeichen im EEPROM speichern
8
*
9
* SW1 (Taster 1, bei mir an PIND3): gespeicherte Zeichen wieder auslesen und ans Terminal senden
10
*
11
* SW2 (Taster 2, bei mir an PIND4): Anzahl aktuell eingelesener Zeichen ans Terminal senden
12
*
13
* SW3 (Taster 3, bei mir an PIND5): freien Speicher (SRAM) ans Terminal senden
14
*
15
* Zum Testen dieses Programm benötigt man ein Terminal-Programm (ich nutze HTerm), mit dem man
16
* Daten zum AVR schicken und von ihm empfangen kann.
17
*
18
* Wenn das Terminal-Programm verbunden ist, sieht man, dass kontinuierlich ein Punkt ('.') gesendet
19
* wird. Dies ist als Lebenszeichen des AVR zu verstehen. Bricht der Datenstrom plötzlich ab, ist
20
* das Programm wahrscheinlich hängengeblieben (z.B. wegen Speicherüberlauf o.ä.).
21
*
22
* Da das SRAM in seiner Größe sehr beschränkt ist (beim ATTiny2313 nur 128 Bytes), habe ich eine
23
* maximale Größe für den empfangenen String festgelegt. Sie kann über IUSART_INP_MAX geändert werden.
24
*
25
* Das Beschreiben des EEPROM ist in der Hinsicht kritisch, als dass der Speicher nur
26
* ca. 100000 mal beschreibbar ist. Deshalb ist es nicht sinnvoll, jedesmal wieder ab
27
* Byte 0 zu schreiben. Deshalb habe ich die Funktionen eeprom_get_last_byte() und
28
* eeprom_get_first_written_byte() geschrieben. Mithilfe dieser Funktionen kann man so
29
* navigieren, dass das EEPROM kontinuierlich beschrieben wird, so dass alle Bytes
30
* gleichmäßig beansprucht werden und die Lebensdauer deutlich erhöht wird. Erreicht man
31
* das Ende des EEPROM (beim ATTiny2313 128 Bytes, änderbar über EEPROM_MAX_BYTE), so
32
* beginnt man wieder bei Byte 0, wobei ein String auch überlappen kann (z.B. ein
33
* 5-Zeichen-String in den Bytes 126, 127, 0, 1 und 2).
34
*
35
* Bei meinem Testboard (STK500) habe ich die Taster nicht nach Standardkonfiguration angeschlossen,
36
* wie oben an der Funktionsübersicht zu sehen ist (SW0 auf PIND2 statt PIND0 usw.). Wer ganz normal
37
* SW0 an PIND0 hat, muss lediglich diese wenigen Stellen im Code abändern.
_matze wrote:
> Nachschlag zum Thema delays: Im AVR-GCC-Tutorial werden in der Funktion> zum Tastenentprellen übrigens auch delays verwendet. Nur so am Rande...
Und gleich unter dem Code steht auch, wo der Pferdefuss dabei liegt.
So what?
Ja, da hast du Recht. Ich habe, nachdem ich meine Funktion geschrieben
hatte, mal im Internet nach alternativen Lösungen gesucht und bin (mal
wieder) auf das AVR-GCC-Tutorial gestoßen. Ich hab mir aber nur den Code
angesehen und nicht den Text darunter gelesen.
Ich wollte ja auch nur darauf hinweisen, dass delays trotz ihres
offensichtlich schlechten Rufes eine gewisse Daseinsberechtigung haben,
und dies wollte ich damit untermauern, dass sogar im genannten Tutorial
delays vorkommen.
Gruß Matze