www.mikrocontroller.net

Forum: Compiler & IDEs timer, uart interupt und drehgeber


Autor: heike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Tag,

ich habe hier gerade ein kleines Problem, bei dem mich hoffentlich
jemand in die richtige Richtung schubsen kann:

Das Programm (siehe unten) soll später mal dazu dienen, an einem AVR
Mega8 einen Wert per Drehgeber einstellen zu können.

Dafür soll der Benutzer am PC-terminal eintippen:
"Start" --> Drehgeber auslesen und das unendlich am PC-terminal ausgeben
"Stop" --> Ausgabe stop
"Weiter" --> Vom Letzer gespeicherte Wert wieder anfangen zu Zählen
"Reset" --> Vom null wieder anfangen zu zählen

- Wert auf PC-terminal ausgeben klappt Dazu habe ich den Code von Peter 
F.
benutzt (Danke!).
- Drehgeber auslesen: Klappt. Dazu habe ich den Code von Peter D.
benutzt (Danke!).
soweit funktioniert alles korrekt.

Jetzt das verbliebene Problem:
tippe ich "Start" ein, wird unendlich der ausgelesener Wert eingegeben. 
(Klar möchte ich es). Leider ist mir nicht mehr möglich etwas 
einzutippen.

Wie kann ich den Code schreiben um der Benutzer die möglichkeit geben, 
etwas anderen Befehlen einzutippen. ("Stop", "Weiter", "Reset")

Danke im Voraus!
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <string.h>

#include "Drehgeber_uart.h"

#define PHASE_A (PINC & 0x01)  // PINC.0
#define PHASE_B (PINC & 0x02)  // PINC.1
#define LENGTH 7


static volatile long int  enc_delta;    // -2147483647 ... 2147483647
static char s[20];
char received[LENGTH+1];
unsigned char enter;
static volatile char enc_last;


SIGNAL (SIG_OVERFLOW0)
{
  //static char enc_last = 0x01;
  char i = 0;

  if( PHASE_A )
  i = 1;

  if( PHASE_B )
  i ^= 3;            // convert gray to binary

  i -= enc_last;       // difference new - last

  if( i & 1 )    // bit 0 = value (1)
  {    
    enc_last += i;      // store new as next last
    enc_delta += (i & 2) - 1;   // bit 1 = direction (+/-)
  }
}


void Timer_init( void )
{
  TCCR0 = 1<<CS00;            //No prescaling
  TIMSK = 1<<TOIE0;           //enable timer interrupt
}

//call this Function for initialisate the Port Output and Input
void port_init(void)
{
  
  DDRD = 0x02;    // Input-Port, except TxD
    PORTD = 0xFF;    // actiate Pull-Ups

  DDRC = 0x00;    // define as Input for the Rotary encoder
  PORTC = 0x03;      //enables Pull-Up on PC0 and PC1
  
  DDRB = 0xFF;    // outputs for LEDs
  PORTB = 0xFF;     // pullups
  
}

void encode_read( void )  // read single step encoders
{
  
    for(;;)              
  {  
    uart_gets(received, LENGTH);       // Receive data
    
    if(strcmp(received, "start") == 0)    
    { 
      do       // -2147483647 ... 2147483647
      {
        ltoa( enc_delta, s, 10 );   
        uart_puts("\r\n");  
        uart_puts( s );
        uart_puts("/");        
        PORTB = 0xFE;
      }while(strcmp(received, "start") == 0);
    }
    else if(strcmp(received, "stop") == 0)
    {
      cli();
                        PORTB = 0xFB;
    }
    else if(strcmp(received, "weiter") == 0)
    {  
      ltoa( enc_delta, s, 10 ); 
      uart_puts("\r\n");  
      uart_puts( s );
      uart_puts("/");
      PORTB = 0xFA;
    }
    else if(strcmp(received, "reset") == 0)
    {  
      enc_delta = 0x00;
      enc_last  = 0x00;
      ltoa( enc_delta, s, 10 ); 
      uart_puts("\r\n");
      uart_puts( s );
      uart_puts("/");
      PORTB = 0xF8;
    }
  }
}

int main( void )
{

  DDRD = 0x02;      // Input-Port, except TxD
  PORTD = 0xFF;      // actiate Pull-Ups

  Timer_init();

  port_init();

  // Initialize UART library, pass baudrate and AVR cpu clock
  uart_init();
   
  // Now enable interrupt, since UART library is interrupt controlled   
  sei(); 

  uart_puts("\r\n");
  uart_puts("Starting the measurement:");
  uart_puts("\r\n\n");
  _delay_ms(10);
  PORTB = 0xF0;
  
  for(;;)              // main loop
    encode_read();
 return 0;  
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Problem besteht darin, dass hier
      do       // -2147483647 ... 2147483647
      {
        ltoa( enc_delta, s, 10 );   
        uart_puts("\r\n");  
        uart_puts( s );
        uart_puts("/");        
        PORTB = 0xFE;
      }while(strcmp(received, "start") == 0);

innerhalb der Schleife es für received keine Möglichkeit mehr gibt, 
einen anderen Wert zu erhalten.

Du musst deine komplette Systematik umstellen. So etwas wie eine 
Schleife die auf etwas wartet, darf es nicht mehr geben. Damit darfst du 
auch zb deine uart_gets Funktion nicht mehr weiter benutzen, solange die 
darauf wartet, dass eine Zeile komplett empfangen wurde.

Du musst die Logik in die Hauptschleife bringen:
Sieh dir nacheinander Dinge an, die man tun könnte, ob die 
Voraussetzungen dafür gegeben sind und mache sie, wenn die 
Voraussetzungen dafür gegeben sind. Bei einigen derartigen Aktionen wird 
es dann so sein, dass sie die Voraussetzungen für andere Aktionen 
schaffen oder vernichten

    while( 1 )

      if( Zeichen wurde empfangen ) {
        if( Zeichen ist ein '\n' ) {
          // Zeile ist komplett, merk dir das
          Zeile_komplett = TRUE;
        }
      }

      if( Zeile_komplett ) {
        Zeile_komplett = FALSE;
        if( Zeile ist "Start" )
          Merken dass ausgegeben werden soll
        else if( Zeile ist "Stop" )
          Den Merker für die Ausgabe zurücksetzen
        else if( Zeile ist "Weiter" )
          ...
      }

      if( Merker für Ausagbe ist gesetzt )
        mache 1 Ausgabe

    }

Ich hoffe man kann erkennen, wie das funktioniert. Du darfst nirgends 
warten. Stattdessen dreht die Haupt-while-Schleife ständig ihre Runden, 
sieht sich die einzelnen Sachen an und entscheidet anhand von Merkern 
(einfache Variablen die entweder 0 oder 1 sein können) ob eine Aktion 
gemacht werden soll oder nicht.

Du musst weg von einem sequentiellen Programmierstil (zuerst mach das, 
dann mach das, dann warte auf dieses und zum Schluss machst du noch 
jenes) hin zu einem ereignisorientierten Programmierstil: Ein Ereignis 
tritt auf, bearbeite es; in der Hauptschleife werden ständig alle 
möglichen Ereignisse überprüft ob sie vorliegen.

Autor: heike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
viele dank für deine Tipps karl Heinz. Leider funnktioniert immer noch 
nicht, Bitte um weitere Tipps

so sieht der teil meiner Routine aus:
for(;;)                  // main loop
  {  
    enter = uart_getc();      
      if( enter != 0 )
    {
      do
        {
          enter = uart_getc();
            if(enter !='\r')  // = ENTER-Taste
        {
          *s=enter;
          s++;
            j++;
        }    
      }
        while( j!=LENGTH && enter!='\r');  
        *s = '\0';
      Zeile_komplett = TRUE;
    }    
    
    if( Zeile_komplett == TRUE ) 
    {
      if(strcmp(received, "start") == 0)
      {
        ltoa( enc_delta, s, 10 ); 
        Merker =1;
      }
      else if(strcmp(received, "stop") == 0)
      {
        Merker =0;
      }
      else if(strcmp(received, "weiter") == 0)
      {  
        Merker =1;
      }
      else if(strcmp(received, "reset") == 0)
      {  
        enc_delta = 0x00;
        enc_last  = 0x00;
        ltoa( enc_delta, s, 10 ); 
        Merker =1;
      }
    }
    if( Merker ==1 )
    {
          uart_puts("\r\n");
      uart_puts( s );
      uart_puts("/");
    }

    Zeile_komplett = FALSE;
  }  

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
heike schrieb:

>     enter = uart_getc();

Was macht uart_getc()?
Wartet die auf ein Zeichen, oder kommt die gleich zurück wenn kein 
Zeichen vorhanden ist?

Wenn ich mich recht erinnere benutzt du die UART Lib vom Peter Fleury. 
Sieh dir in seiner Demo an, wie man den Returnwert von uart_getc richtig 
auswertet.

Ich les mir den Code noch weiter durch

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
      if( enter != 0 )
    {
      do
        {
          enter = uart_getc();
            if(enter !='\r')  // = ENTER-Taste
        {
          *s=enter;
          s++;
            j++;
        }    
      }
        while( j!=LENGTH && enter!='\r');  
        *s = '\0';
      Zeile_komplett = TRUE;
    }    

NEIN!

Ich sagte doch: keine Warteschleifen.

Du holst ein Zeichen wenn eines da ist und hängst es an dein received 
Buffer an, das wars. Das nächste Zeichen wird bearbeitet, wenn die 
globale for(;;) Schleife das nächste mal zur Abfrage kommt "Ist ein 
Zeichen da?"
    if( enter != 0 )         // von der UART kommt wieder was
    {
      if( enter == '\r' )    // bei einem Newline ist die Zeile komplett
      {
        *s = '\0';
        Zeile_komplett = TRUE;
        s = received;        // s gleich wieder an den Anfang von received
                             // stellen, damit die nächsten Tastendrücke
                             // den nächsten String von vorne aufbauen
      } 
      else                   // ansonsten hänge das Zeichen hinten drann
      {
        *s = enter;
        s++;
      }
    }


Du denkst noch sequentiell:
Ich will eine Zeile komplett einlesen, also warte ich bis ich ein \r 
erhalte.
Der springende Punkt ist das Wörtchen 'warte'. Das musst du los werden.

Deine Denkweise muss sein:
Ich habe ein und nur ein Zeichen erhalten, was mache ich damit?
Hänge ich es hinten an das bisher empfangene drann oder ist mit diesem 
einen Zeichen eine Zeile vollständig und kann weiterbearbeitet werden.

(Das Ereignis ist: Ein Zeichen wurde empfangen
und der Rest ist die Reaktion auf dieses Ereignis)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
   if( Merker ==1 )
    {
          uart_puts("\r\n");
      uart_puts( s );
      uart_puts("/");
    }


Wieso s?

Woher weißt du wo s gerade hinzeigt?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
heike schrieb:

>     if( Zeile_komplett == TRUE )
>     {
>       if(strcmp(received, "start") == 0)
>       {
>         ltoa( enc_delta, s, 10 );
>         Merker =1;
>       }
>       else if(strcmp(received, "stop") == 0)
>       {
>         Merker =0;
>       }
>       else if(strcmp(received, "weiter") == 0)
>       {
>         Merker =1;
>       }
>       else if(strcmp(received, "reset") == 0)
>       {
>         enc_delta = 0x00;
>         enc_last  = 0x00;
>         ltoa( enc_delta, s, 10 );
>         Merker =1;
>       }
>     }
>     if( Merker ==1 )
>     {
>           uart_puts("\r\n");
>       uart_puts( s );
>       uart_puts("/");
>     }
>
>     Zeile_komplett = FALSE;
>   }


Nope. Das Rücksetzen von Zeile_komplett hat hier nichts verloren.
Das gehört logischerweise in den Zweig, in dem du das Ereignis 
Zeile_komplett == TRUE behandelst.
      if( Zeile_komplett == TRUE )
      {
        Zeile_komplett = FALSE;
        if(strcmp(received, "start") == 0)
        {
          ltoa( enc_delta, s, 10 );

          ...

      }

Hier gibt es auch noch ein anderes Problem. Wenn die Auswertung länger 
dauert und du schnell tippen kannst, dann kannst du klarerweise Zeichen 
in received 'einschmuggeln' ehe sie ausgewertet wurden.
Aber mach erst mal den Rest.

Autor: heike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke schön
ich kümmere mich weiter morgen
vielen dank nochmals

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Siehe Multitasking

Autor: heike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lösung:
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <string.h>

#define FALSE   0
#define TRUE    1


#ifndef F_CPU
/* In the new version of the WinAVR/Mfile Makefile guideline One can defined F_CPU in the Makefile, a repeated 
   definition here would lead to a compiler warning . therefore "prevention" through    #ifndef/#endif

   This "Prevention" can lead to Debugger, if AVRStudio use a another, not the hardware fitting Clock rate: 
   Then the    following definition doesn't use, but instead the default value (1 MHz?) of AVRStudio - hence 
   the Output of a warning if F_CPU yet does not define:*/ 
#warning "F_CPU was not defined yet, now make up with 3686400"
#define F_CPU 3686400L   // Systemtakt in Hz - define as  long>> Without errors in the computation 
#endif

#include <util/delay.h>


#define BAUD 9600L //38400L
#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)     //clever round
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))         //real baud rate
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)     //Error per thousand


#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10))
#error Systematic error in the baud rate more than 1% and thus too high!
#endif

#define PHASE_A (PINC & 0x01)  // PINC.0
#define PHASE_B (PINC & 0x02)  // PINC.1

void UART_Init() 
{
  
  UBRRH = (unsigned char)(UBRR_VAL>>8);  // Set baud rate
  UBRRL = (unsigned char)UBRR_VAL;
  
  UCSRB |= (1<<RXEN)|(1<<TXEN);// Enable receiver and transmitter
  
  UCSRC = (1<<URSEL) | (1<<UCSZ0)|(1<<UCSZ1);  // Set frame format: 8data, 1stop bit        
}//end uart_Init()


void UART_Putc(unsigned char c)
{
    while (!(UCSRA & (1<<UDRE)));// Do nothing until data have been transmited 
    
  UDR = c;                  // Put data into Buffer, sends the data
  _delay_ms(10);   
}//end uart_Putc()


void UART_Puts (char *string)
{
    while( *string != '\0' )          //as long as *string != '\0' so unlike the "stringer end-character"
    {  
        UART_Putc(*string);
        string++;
    }
}//end uart_Puts()
//////////////////////////////////////////////////////////////////////////

SIGNAL (SIG_OVERFLOW0)
{
  static char enc_last = 0x01;
  char i = 0;

  if( PHASE_A )
    i += 1;

  if( PHASE_B)
    i ^= 3;                // convert gray to binary

  i -= enc_last;             // difference new - last

  if( i & 1 )            // bit 0 = value (1)
  {    
    enc_last += i;          // store new as next last
    enc_delta += (i & 2) - 1;   // bit 1 = direction (+/-)
  }
}


void Timer_init( void )
{
  TCCR0 = 1<<CS00;            //No prescaling
  TIMSK = 1<<TOIE0;           //enable timer interrupt
}



//call this Function for initialisate the Port Output and Input
void port_init(void)
{
  
  DDRD = 0x02;      // Input-Port, except TxD
    PORTD = 0xFF;      // activate Pull-Ups

  DDRC = 0x00;  // define as Input for the Rotary encoder
  PORTC = 0x03;   //enables Pull-Up on PC0 and PC1
}

void encode_read0( void )// read single step encoders
{
  // Now enable the Timer interrupt
  sei();
  enc_delta = 0x00;
  //enc_last  = 0x00;
  
  ltoa( enc_delta, s, 10 );   //convert Ascii in  DEZ-Zahlen 
  UART_Puts( s );
  UART_Puts("/");
  UART_Puts("\r\n");
}

void encode_read( void )  // read single step encoders
{
  sei();
  
    ltoa( enc_delta, s, 10 ); //convert Ascii in  DEZ-Zahlen 
    UART_Puts( s );
    UART_Puts("/");  //So that it isn't leaves the cypher behind
    UART_Puts("\r\n");
}
///////////////////////////////////////////////////////////////////////////

unsigned char uart_lesen(void)
{
    uint8_t tmp;
    if((UCSRA & (1<<RXC)))
  {                // empfangenes Zeichen abholbereit im UART ?
        tmp = UDR;
        while (!(UCSRA & (1<<UDRE))); // Warte auf freien Sendepuffer vom UART
        UDR = tmp;
    }
  //return UDR;
}

int befehl_lesen(void)
{
  unsigned char received;

    received = uart_lesen();           // Receive data
  
  //Start --> 0
  //Stop --> 1
  //Weiter --> 2
  //Reset --> 3
  if(received == 0x30)
    return 0;

  else if(received == 0x31)
    return 1;

  else if(received == 0x32)
    return 2;

  else if(received == 0x33)
    return 3;
}

void Messwert_Ausgeben(int Befehl)
{
  if(Befehl == 0)
    encode_read();

  else if(Befehl == 1)
    cli();

  else if(Befehl == 2)    
    encode_read();

  else if(Befehl == 3)
    encode_read0();
}

int main( void )
{
  int befehl;

  DDRD = 0x02;      // Input-Port, except TxD
  PORTD = 0xFF;      // activate Pull-Ups
  PORTB = 0xF0;

  Timer_init();

  port_init();

  //  Initialize UART library, pass baudrate and AVR cpu clock
  UART_Init();

    while (1)                  // main loop
  {
    befehl = befehl_lesen();
    Messwert_Ausgeben(befehl);
  }
    return 0;
}

viele grüsse

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.