Anfängerproblem, Schleifendurchlauf Fehler

daywalker0086

Neues Mitglied
04. Mai 2009
15
0
0
Sprachen
Hallo Leute bin neu hier weil ich als alter Anfänger hier noch verzweifel. Folgendes Problem:
Ich habe folgenden Code (in C) bei dem ich Zeichen(Hex) vom PC mit HTerm über die Serielle an den AVR schicke, dieser gibt mir dann eine Prüfsumme(alle Zeichen aufaddiert) zurück über die Serielle(und noch was auf dem Display ist aber nebensächlich).
Code:
int main(void)
{
    UCSRB |= (1<<TXEN);                // UART TX einschalten
    UCSRB |= (1<<RXEN);
    UCSRC |= (1<<URSEL)|(3<<UCSZ0);    // Asynchron 8N1

    UBRRH = UBRR_VAL >> 8;
    UBRRL = UBRR_VAL & 0xFF;


    char daten[41];
    int ende = 0;
    int wert = 0;

    int checksumme(char Nextchar,int wert)
    {

    wert = wert + Nextchar;

    return wert;
    }

    void USART_Transmit( unsigned char data )
    {
    /* Wait for empty transmit buffer */
    while ( !( UCSRA & (1<<UDRE)) )
    ;
    /* Put data into buffer, sends the data */
    UDR = data;
    }

    uint8_t usart_getc(void)
    {
        while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
            ;
        return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben
    }


    void usart_gets( char* Buffer, uint8_t MaxLen )
    {
      uint8_t NextChar;
      uint8_t StringLen = 0;

      NextChar = usart_getc();         // Warte auf und empfange das nächste Zeichen

                                      // Sammle solange Zeichen, bis:
                                      // * entweder das String Ende Zeichen kam
                                      // * oder das aufnehmende Array voll ist
      wert = checksumme(NextChar,wert);
      *Buffer++ = NextChar;
      ende = wert;
      while( (UCSRA & (1<<RXC)) && StringLen < MaxLen - 1 )
      {
    	NextChar = usart_getc();
        *Buffer++ = NextChar;

        wert = checksumme(NextChar,wert);
        ende = wert;
        StringLen++;

      };



    }

    // LCD Initialisieren
        uint8_t loop;
        lcd_init();
        lcd_clear();
        lcd_set_cursor(0,LINE0);	// set cursor position
        	lcd_putc(small_font,'1');	// draw some singel chars
        	lcd_putc(small_font,'2');
        	lcd_putc(small_font,'3');



anweisungen:
	usart_gets( daten, sizeof( daten ) );

	lcd_clear();
    lcd_set_cursor(0,LINE2);
    int i;
    for(i=0;i<=ende;i++)
    {
    	lcd_putc(small_font,daten[i]);
    }


    USART_Transmit(ende);
goto anweisungen;
}

wenn ich in hterm 20(hex) schicke kommt 00 zurück obwohl ja 20 kommen
müsste.
wenn ich 20 20 20(also alles eingeben und auf einmal schicken) eingebe
dann kommt 00 60 raus. also das 60 stimmt ja(lasse alle empfangenen
Zeichen aufaddieren) aber warum kommt vorher 00 was wird hier zu oft
durchlaufen oder was ist hier schief???
Ich verzweifle an so einer einfachen Sache
 
Hi,

vielleicht helfen Dir diese Anmerkungen weiter:

- bevor die Funktion usart_gets aufgerufen wird, sollte die Variable "wert" zurückgesetzt werden (wert=0).

- Die Variable "wert" wird als Variable in "main" und auch als Parameter in der Funktion "checksumme" verwendet, ist nicht verboten aber man kommt schnell durcheinander welche Variable jetzt wo gültig ist.

- Die while Loop würde ich in eine do-while umbauen, da ja auf jedenfall 1 Zeichen empfangen werden soll

do
{
// Zeichen holen
NextChar = usart_getc();
.....
}while(NextChar != '\0' && DataCnt < MaxLen - 1);

- Den Variablen einen Aussagekräftigen Namen verpassen
wert -> Checksumme

- Verwendung von "ende" ist unklar. Der Variable "ende" wird der Wert der Checksumme zugewiesen und in der for Schleife verwendet! Ist das so gewollt? Ich vermute, da ist eigentlich eine Variable für die Anzahl der empfangenen Daten gemeint.

Schöne Grüße
rangar
 
Ich danke dir erstmal das du dich mir erbarmt hast, hab deine Ratschläge befolgt und hab jetzt diesen Code:
Code:
int main(void)
{
    UCSRB |= (1<<TXEN);                // UART TX einschalten
    UCSRB |= (1<<RXEN);
    UCSRC |= (1<<URSEL)|(3<<UCSZ0);    // Asynchron 8N1

    UBRRH = UBRR_VAL >> 8;
    UBRRL = UBRR_VAL & 0xFF;


    char daten[41];
    int ende = 0;
    int checksumme = 0;

    int checksummeberechnen(char Nextchar,int summe)
    {
    	summe = summe + Nextchar;
    	return summe;
    }

    void USART_Transmit( unsigned char data )
    {
    /* Wait for empty transmit buffer */
    while ( !( UCSRA & (1<<UDRE)) )
    ;
    /* Put data into buffer, sends the data */
    UDR = data;
    }

    uint8_t usart_getc(void)
    {
        while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
            ;
        return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben
    }


    void usart_gets( char* Buffer, uint8_t MaxLen )
    {
      uint8_t NextChar;
      uint8_t StringLen = 0;
      do
      {
    	NextChar = usart_getc();        					 // Warte auf und empfange das nächste Zeichen
        *Buffer++ = NextChar;

        checksumme = checksummeberechnen(NextChar,checksumme);
        ende++;
        StringLen++;

      }while( (UCSRA & (1<<RXC)) && StringLen < MaxLen - 1 ); // Sammle solange Zeichen, bis:
															  // * entweder das String Ende Zeichen kam
															  // * oder das aufnehmende Array voll ist
    }

    // LCD Initialisieren
        uint8_t loop;
        lcd_init();
        lcd_clear();
        lcd_set_cursor(0,LINE0);	// set cursor position
        	lcd_putc(small_font,'1');	// draw some singel chars
        	lcd_putc(small_font,'2');
        	lcd_putc(small_font,'3');



anweisungen:
	checksumme=0;
	usart_gets( daten, sizeof( daten ) );

	lcd_clear();
    lcd_set_cursor(0,LINE2);
    int i;
    for(i=0;i<=ende;i++)
    {
    	lcd_putc(small_font,daten[i]);
    }


    USART_Transmit(checksumme);
goto anweisungen;
}

Also ein Problem, nämlich das ich wenn ich 20 schicke 00 zurückbekomme ist damit behoben.
Ein weiteres hab ich noch: wenn ich 2mal 20, also 20 20 eingebe und dann abschicke bekomme ich auch 2 mal zwanzig zurück, obwohl es ja 40 sein müsste, und nur einmal...
Wenn ich dreimal 20 eingebe bekomme ich 1x 20 und 1x 40, es sollten aber 60 sein...
Was kann das jetzt noch sein?
Oder hab ich was an der Befehlsabarbeitung nicht richtig verstanden?
Er sollte doch in der while Schleife bleiben solange Zeichen anstehen und erst dann die Schleife verlassen und zu USART_Transmit(checksumme) gehen???


P.S: wie kann man denn C Code posten?
 
Hallo daywalker,

1. C-Code posten ...
- entweder als zip anhängen (die Büroklaammer)
- oder als Code einfügen (# - hast du ja gemacht)
- oder mit (highlight=C) .... (/highlight) Die Klammern sind natürlich die [

2. Ich kenn mich ja nicht so ganz mit C aus aber es sieht danach aus als ob
du deine Funktionen und Subroutinen IN dein Hauptprogramm in den Ablauf
reingebaut hast. Also werden die Subroutinen/Funktionen evtl schon vor deiner
Hauptschleife durchlaufen. Muß das so sein ?

Ich bau das eigentlich immer folgendermaßen auf ...
- Initialisierung (logischerweise am Anfang so wie bei dir)
- weitere Voreinstellungen (Variablen mit Werten besetzen ...)
- Hauptroutine (Hauptschleife oder was anderes)
- wenn die Hauptroutine keine Endlosschleife ist dann hier einen Blockierung
einbauen
- Subroutinen/Funktionen

Bei dir liegen die Teile USART_Transmit , checksummenberechnen ,
usart_getc , ... vor der eigentlichen Hauptschleife. Müssen die vorher
im Programmlauf definiert sein damit der Compiler sie vorher hat oder läufst
du damit Gefahr das sie an der Stelle auch schon vorher abgearbeitet werden
obwohl es eigentlich nachher in der Hauptschleife passieren soll ??

Nur mal so als Verständnisproblem eines Assembler-Programmierers ...
evtl ist ja das schon dein Fehler ??

Gruß
Dino
 
Hey,

sieht schon besser aus, der Rest wird schon noch :)

Die Abbruchbedingung könnte das Problem sein, so wie ich das sehe.

Ich denke mal, der Empfang soll mit einem Stringende (laut Kommentar) beendet werden, oder? Dann müsstest Du auch auf '\0' abfragen.

also

Code:
}while(NextChar != '\0' && DataCnt < MaxLen - 1);

Wenn Du die Testdaten mit einem Terminalprogramm sendest, dann könnte es auch sinnvoller sein die "Carriage Return" Taste abzufragen, d.h.
Coding 0x0D (anstatt die 0 für Stringende)

also

Code:
}while(NextChar != 0x0D && DataCnt < MaxLen - 1);

Das wäre ein Versuch Wert!
 
Danke für die Antworten...
@Rangar:
Also die Abbruchbedingungen bereiten mir auch noch Kopfzerbrechen, es ist nämlich so das:
Dieses Programm(wenn es dann mal fertig sein sollte) mit einer Steuerung kommunizieren soll die Ihr Daten Paketweise liefert, nach jedem Paket verlangt sie eine Quittierung. Leider haben die Pakete KEIN Endzeichen wie /n oder /cr, welches man in die Abfrage einbauen könnte, hört einfach mit dem letzten "Nutzzeichen" auf und wartet auf die Antwort.

Deswegen auch der Versuch mit dem (UCSRA & (1<<RXC)) && ... in der while Schleife.
Wie würdet ihr das in dem Fall machen???
Ein Timeout wäre auch was, wenn nix mehr kommt dann is das Paket fertig und kann in die Variable geschrieben werden/ausgegeben werden..
 
Hi,

... Leider haben die Pakete KEIN Endzeichen

Das ist natürlich ätzend :eek: Auch keine feste Paketlänge oder Ableitung der Paketlänge aus dem Paketaufbau?

Hmm, dann würde ich auch eine Timeoutüberwachung einbauen, was anderes fällt mir da nicht ein.
Mit der while( (UCSRA & (1<<RXC)) Abfrage wird das auf jedenfall nicht wie gewünscht funktionieren :(

dino03 hat auch nicht ganz unrecht mit dem Programmaufbau, üblicherweise schreibt man die C-Funktionen selbstständig für sich, außerhalb der main-Loop.

Du hast mehr den Objektorientierten Ansatz gewählt :D und innerhalb der Klasse "main" einige Methoden definiert. Da nutzt Du aber alle Feinheiten aus, was der GCC so bietet :cool:

Allerdings würde ich die Sprungmarke "anweisungen" mit einer while Loop ersetzen
Code:
while(1)
{
  checksumme=0; 
  usart_gets( daten, sizeof( daten ) ); 
   ...
   ...
}

und die LCD Initialisierung weiter nach oben schieben!
 
Hallo daywalker,
Wie würdet ihr das in dem Fall machen???
Ein Timeout wäre auch was, wenn nix mehr kommt dann is das Paket fertig und kann in die Variable geschrieben werden/ausgegeben werden..

wenn die Paketlänge nicht bekannt ist, kannst du eigentlich nur auf das Ende des Paketes reagieren, wenn ein Timeout der empfangenen Daten auftritt. Am besten wäre es natürlich, du könntest irgendwie auf Grund des Aufbaus der Daten die Paketlänge ermitteln.

Elegant ist es, wenn du in der while(!(UCSR1A & (1<<RXC1))) Schleife auf ein Timeout-Flag oder Timeout-Timer reagierst (also zusätzlich als Abbruchbedingung nutzt). Das Flag und/oder den Timer initialisiertst du vor der while-Schleife. In einer Timerinterruptroutine wird der Timer dann inkrementiert oder dekrementiert, zum Beispiel bei Timeout-Timer = 0 wird das Flag gesetzt. Die while-Schleife wird abgebrochen. Das ist nurmal ein Denkanstoß, es gibt bestimmt noch andere einfachere Lösungen.

Grüße,
Dirk
 
Also habe meinen Code wie folgt abgeändert und die Schleifen nach vorn verlagert: leider funktioniert es trotzdem nicht wie gewünscht und rechnet auch nichtmehr:(
Code:
#include <avr/io.h>
#include <stdint.h>
#include <stdlib.h>
#include "mylcd.h"
#include "small_font.h"

#define BAUD 9600UL          // Baudrate
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden




void USART_Transmit( char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) )
;
/* Put data into buffer, sends the data */
UDR = data;
}

uint8_t usart_getc(void)
{
    while (!(UCSRA & (1<<RXC)))   // warten bis Zeichen verfuegbar
        ;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben
}


void usart_gets( char* Buffer, uint8_t MaxLen,int* ende,int* checksumme)
{
  uint8_t NextChar;
  uint8_t StringLen = 0;
  do
  {
	NextChar = usart_getc();        					 // Warte auf und empfange das nächste Zeichen
    *Buffer++ = NextChar;

    checksumme = checksumme + NextChar;
    ende++;
    StringLen++;

  }while( (UCSRA & (1<<RXC)) && StringLen < MaxLen - 1 ); // Sammle solange Zeichen, bis:
														  // * entweder das String Ende Zeichen kam
														  // * oder das aufnehmende Array voll ist
}
int main(void)
{
    UCSRB |= (1<<TXEN);                // UART TX einschalten
    UCSRB |= (1<<RXEN);
    UCSRC |= (1<<URSEL)|(3<<UCSZ0);    // Asynchron 8N1

    UBRRH = UBRR_VAL >> 8;
    UBRRL = UBRR_VAL & 0xFF;

    // LCD Initialisieren
            uint8_t loop;
            lcd_init();
            lcd_clear();

    char daten[41];
    int ende = 0;
    int checksumme = 0;




        lcd_set_cursor(0,LINE0);	// set cursor position
        	lcd_putc(small_font,'1');	// draw some singel chars
        	lcd_putc(small_font,'2');
        	lcd_putc(small_font,'3');



while(1)
{

	usart_gets( daten, sizeof( daten ),&ende,&checksumme );

	lcd_clear();
    lcd_set_cursor(0,LINE2);
    int i;
    for(i=0;i<=ende;i++)
    {
    	lcd_putc(small_font,daten[i]);
    }

    USART_Transmit(checksumme);
}
}
 
Hi,

zumindest ist die usart_gets Funktion verfuscht, da die neuen Pointer nicht richtig behandelt werden.

Versuch es mal so rum:

Code:
void usart_gets( char* Buffer, uint8_t MaxLen, uint16_t * pDataCnt, uint16_t * pChecksumme)
{
  uint8_t  NextChar;
  uint16_t DataCntTmp = 0;	
  uint16_t ChkSumTmp  = 0;
	
  do
  {
  	NextChar = usart_getc();        					 // Warte auf und empfange das nächste Zeichen
    *Buffer++ = NextChar;
    ChkSumTmp = ChkSumTmp + NextChar;		// Checksumme berechnen
    DataCntTmp++;											  // Zeichen zählen

  }while( (UCSRA & (1<<RXC)) && DataCntTmp < MaxLen - 1 ); // Sammle solange Zeichen, bis:
														  // * entweder das String Ende Zeichen kam
														  // * oder das aufnehmende Array voll ist
															
  *pDataCnt    = DataCntTmp;		// Anzahl Zeichen zurückliefern
  *pChecksumme = ChkSumTmp;     // Checksumme zurückliefern
}

Die Idee mit den Pointer ist aber gut, wobei aber ich die Anzahl der empfangen Daten von der Funktion selber als return Wert zurückliefern würde.

Die while( (UCSRA & (1<<RXC)) würde ich allerdings auch ganz schnell gegen die Timeoutabfrgae ersetzen (s. Porsting von Dirk). Ich wundere mich, daß das überhaupt mal halbwegs funktioniert hat.:eek:
 
Jaaaa...
er rechnet wieder:)
Es kommen zwar immernoch zu viele Ausgaben aber ich denke das liegt an der noch nicht veränderten Abbruchbedingung der While Schleife.
Um auf dem Timer zurück zu kommen...
Kann mir jemand ein Beispiel geben wie ein Timer initialisiert wird?
Soll ich den 8 bit oder 16bit nehmen?
Ich hab zwar das Datenblatt des Atmega32 vor mir aber werd da nicht so richtig schlau draus...
 
Hallo,

hier ein Denkanstoss zum Thema Timer Initialisierung mit Interrupt.




CodeBox C


#include <avr/io.h>
#include <avr/interrupt.h>

unsigned long TimerTick = 0;

void Start10msTimer(void);

int main(void)
{
Start10msTimer();
sei();
while(1);
}

void Start10msTimer(void)
{
// 10ms Timer starten, @F_CPU=14,xxxMHz
OCR2 = 143; // Compare Register wird mit TCNT2 verglichen
TIMSK |= _BV(OCIE2); // IRQ Freigeben Overflow
TCCR2 = 0x0F; // Clk2Ts / 1024 from Prescaler, Clear On Timer Compare
}


SIGNAL(SIG_OUTPUT_COMPARE2)
{
TimerTick++;
}
 
Leute!
Die Steuerung hat doch eine Prüsumme:)
also kann ich die abfragen lassen und brauch keinen Timeout:D

Aber da ha ich noch folgendes Problem:
Die Datenpakete sind folgendermaßen aufgebaut:
2 Byte Kommando, 1 Byte Länge der folgenden Daten, Daten, 2 Byte Prüfsumme.

Die Prüfsumme sind einfachalle Bytes (Kommando, Länge, Daten) aufsummiert, highbyte zuerst.

Aber ich hab jetzt immernoch das Problem wie man die Prüfsumme errechnet kann mir das mal jemand erklären bei 2 Byte? Wenn ich z.Bsp. folgendes Kommando hab:
4D410F49000000034B657373656C74656D700506
 
Die Prüfsumme sind einfachalle Bytes (Kommando, Länge, Daten) aufsummiert, highbyte zuerst.
Du meinst "Bits" aufsummieren oder?
Bist du dir sicher dass es nicht etwas komplizierter ist, z.b. ein CRC-Check oder so?

Wenn es tatsächlich nur um Bits geht:
Überleg dir mal wie du eine Funktion schreibst die die Bits eines Bytes zählt.
Danach überlege dir etwas womit du alle Bytes deines Datenpaketes nach der Reihe aufrufen kannst.
Nun brauchst du nur noch beides Kombinieren.
 
Ich meinte eigentlich schon bytes.
So wie wenn ich den String
5261010100B5 habe, dann ist B5 die Prüfsumme aller Bytes aufsummiert. Das Wäre ja dann eine 1 byte Prüfsumme oder?
 
Hallo Daywalker,

ja in deinen beiden Beispielen werden einfach nur die übertragenen Daten byte-weise summiert.

4D41 0F 49000000034B657373656C74656D70 0506 ; 2 Byte Summe

So wie es aussieht, setzt sich das Paket folgendermassen zusammen:

2 Byte Kommando
1 Byte Länge n der Daten (einschließlich Byte für Länge)
n-1 Byte Daten
2 Byte Summe

Also einfach alles summieren und dann mit den letzten beiden empfangenen Bytes vergleichen.

Grüße,
Dirk
 
Servus Leute, hab jetzt mal wiede rZeit für meinen kleinen Chip.
Also noch ein kleines Problem bei der Berechnung, wie teile ich meine Summe auf zwei byte auf? da ja bei 255 schluss ist und dann sozusagen das erste byte überläuft und dann das zweite um eins hochgezählt werden muss.
Oder hab ich da einen Denkfehler und muss nur die variable vergrößern und er wandelt dann z.B. die Zahl 1024 automatishc in zwei Byte um???
 
Hallo,

du definierst einfach deine Variable zum Beispiel als 16Bit Zahl (unsigned 0...65535d)

uint16_t MeineVariable;


Gruß,
Dirk
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)