Forum: Mikrocontroller und Digitale Elektronik String Anfangswert zuweisen


von Andreas H. (Gast)


Lesenswert?

Hallo zusammen,
Habe ein kleines Problem. Möchte eine Zahl über ein Terminalprogramm 
einlesen und diese dann als Integer in einer Variablen speichern. Danach 
soll die Zahl zur Kontrolle wieder ausgegeben werden. Das funktioniert 
auch soweit schon recht gut, allerdings soll die Variable bis zur ersten 
Eingabe schon einen vorbestimmten Wert besitzen und diesen dann erst bei 
der ersten Eingabe ändern. Habe versucht diesen Wert in den String 
einzugeben, allerdings bekomme ich bisher immer nur Null zurück. Würde 
mich freuen wenn mir jemand einen Tip geben könnte.
Gruß Andreas


#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>

#define BAUD 9600UL
#define UBRR_BAUD   ((F_CPU/(8UL*BAUD))-1)
#define UART_MAXSTRLEN 5

volatile uint16_t x;
volatile uint8_t uart_str_complete = 0;     // 1 .. String komplett 
empfangen
volatile uint8_t uart_str_count = 0;
char uart_string[6] = "2200";
char s [5];

void uart_init(void)
{
    // Baudrate einstellen (Normaler Modus)
    UBRR3H = (uint8_t) (UBRR_BAUD>>8);
    UBRR3L = (uint8_t) (UBRR_BAUD);

  UCSR3A= (1<<U2X3);    // U2X auf LO


    // Aktivieren von receiver und transmitter
    UCSR3B = (1<<RXEN3)|(1<<TXEN3)|(1<<RXCIE3);

    // Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit
    UCSR3C = (1<<UCSZ31)|(1<<UCSZ30);
}

int uart_putc(unsigned char c)
{
    while (!(UCSR3A & (1<<UDRE3)))  /* warten bis Senden moeglich */
    {
    }

    UDR3 = c;                      /* sende Zeichen */
    return 0;
}

void uart_puts (char *s)
{
    while (*s)
    {   /* so lange *s != '\0' also ungleich dem 
"String-Endezeichen(Terminator)" */
        uart_putc(*s);
        s++;
  }
}

int main(void)
{
  TCCR3B = (1<<CS32) | (1<<CS30); //Timer 3 initalisieren (Prescaler 
1024)
  TIMSK3 |= (1<<TOIE3); //Timer 3 overflow erlauben

  TCCR4A = (1<<WGM41) | (1<<COM4C1); //PWM Mode 14
  TCCR4B = (1<<WGM42) | (1<<WGM43) | (1<<CS41); //PWM Mode 14 und 
Prescaler 8
  ICR4 = 40000; //PWM Frequenz 50Hz (50Hz=16MHz/8*40000)
  //OCR4C = x; //An-Zeit 1,5ms (16MHz*1,5ms/8)

  DDRH |= (1<<PH5); //OCR4C auf Ausgang

  uart_init(); // USART initialisieren

  sei();

  while (1)
  {
    char tmp[5];

    strcpy ( tmp, uart_string);
    uint16_t x = atoi( tmp );
    OCR4C = x;
  }
}

ISR(USART3_RX_vect)
{
  unsigned char nextChar;
  // Daten aus dem Puffer lesen
  nextChar = UDR3;
if( uart_str_complete == 0 ) {  // wenn uart_string gerade in 
Verwendung, neues Zeichen verwerfen

    // Daten werden erst in uart_string geschrieben, wenn nicht 
String-Ende/max Zeichenlänge erreicht ist/string gerade verarbeitet wird
    if( nextChar != '\n' &&
        nextChar != '\r' &&
        uart_str_count < UART_MAXSTRLEN - 1 ) {
      uart_string[uart_str_count] = nextChar;
      uart_str_count++;
    }
    else {
      uart_string[uart_str_count] = '\0';
      uart_str_count = 0;
      uart_str_complete = 0;
    }
  }
}

ISR (TIMER3_OVF_vect)
{
  itoa (x, s, 10);
  strcat ( s, "  ");
  uart_puts(s);
}

von Rolf Magnus (Gast)


Lesenswert?

Du schreibst in einer Endlosschleife den Wert aus dem String in ein 
lokales x, gibst aber in deiner ISR das globale x aus.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas H. schrieb:
1
 ISR (TIMER3_OVF_vect)
2
 {
3
   itoa (x, s, 10);
4
   strcat ( s, "  ");
5
   uart_puts(s);
6
 }
Mal vorab:
In einer Interruptroutine werden keine String-Operationen und schon 
gleich gar keine UART-Zeichenkettensenderoutinen ausgeführt. Eine 
Interruptroutine muss so schnell wie möglich beendet werden.

Eine Frage: warum speicherst du den Wert 2x? Einmal als String und 
einmal als Zahl? Das ist nämlich dein Problem.

Ich würde den Wert nur als Variable x speichern, und bei Bedarf aus 
dieser in einen String (zum Senden) umwandeln. Oder andersrum: sofort 
nachdem der String empfangen wurde, wird er in eine Zahl umgewandelt.

Andreas H. schrieb:
1
 volatile uint16_t x;
2
  :
3
  {
4
     uint16_t x = atoi( tmp );
5
     OCR4C = x;
6
  }
7
  :
8
  itoa (x, s, 10);  // welches x denn?
Na toll...
Als Tipp: es sind auch andere Variablennamen anstelle von x zulässig. 
Z.B. auch reloadwert o.ä.

von Rolf Magnus (Gast)


Lesenswert?

Lothar Miller schrieb:
> Ich würde den Wert nur als Variable x speichern, und bei Bedarf aus
> dieser in einen String (zum Senden) umwandeln. Oder andersrum: sofort
> nachdem der String empfangen wurde, wird er in eine Zahl umgewandelt.

Genau das macht er doch. Da der String aber in der Empfangs-ISR Zeichen 
für Zeichen eintröpfelt, muß er sich ja den bisherigen String irgendwo 
speichern, bis er komplett ist. Bisher wartet er aber mit der 
Konvertierung nicht, bis der String komplett angekommen ist, sondern 
läßt sie ständig laufen.

von Karl H. (kbuchegg)


Lesenswert?

Andreas H. schrieb:
> Würde
> mich freuen wenn mir jemand einen Tip geben könnte.

Dein ganzes Programm ist seltsam zusammengewürfelt.

Wozu den STring vorbelegen? Braucht doch kein Mensch. Du hast eine 
Variable x (die solltest du wirklich umbenennen). Von der hängt alles 
weitere ab. Also gibst du der einen Startwert.

Und wenn über die UART ein neuer kompletter String reingekommen ist, 
dann wandelst du den in eine Zahl und weist diesem x diese Zahl zu. Wo 
ist das Problem?
1
int main(void)
2
{
3
  x = 128;
4
5
...
6
7
  uart_init(); // USART initialisieren
8
9
  sei();
10
11
  while (1)
12
  {
13
    if( uart_str_complete ) {
14
      x = atoi( uart_string );
15
      uart_str_complete  = 0;
16
17
      OCR4C = x;
18
19
      itoa( x, s, 10 );
20
      strcat( s, "  " );
21
      uart_puts( s );
22
    }
23
  }
24
}

Und den Timer 3 Overflow legst du klammheimlich ganz schnell wieder 
still.

von Andreas H. (Gast)


Lesenswert?

Hallo,
Kreuzigt mich doch bitte nicht gleich. Beschäftige mich erst seit 3 
Wochen mit Mikrokontrollern.
Das mit der ISR muss ich mir dann mal überlegen wie ich den String 
ungefähr alle 4s neu ausgeben kann.
Die Ausgabe klappt ja bisher auch. Mein Problem war eigentlich, dass ich 
bevor ich eine Eingabe mache immer nur Null rausbekomme obwohl in 
uart_string eigentlich 2200 stehen müsste.
Werde mich aber trotzdem mal darum kümmern eure Tipps einzuarbeiten.
Gruß Andreas

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas H. schrieb:
> Das mit der ISR muss ich mir dann mal überlegen wie ich den String
> ungefähr alle 4s neu ausgeben kann.
Du setzt mit einem Timerinterrupt nach 4 Sekunden ein Flag und fragst 
das in der Hauptschleife ab. Denn: ob der String nach 4,000000 Sekunden 
oder z.B. nach 4,000062 Sekunden verschickt wird, das dürfte recht egal 
sein...
Dann könnte das etwa so aussehen:
1
volatile char Flag_4sec = 0;
2
3
TimerISR() // initialisiert so, dass sie z.B. alle 10ms aufgrufen wird
4
{ 
5
   :
6
   if (++Cnt_4sec == 400) {
7
     Flag_4sec= 1;
8
     Cnt_4sec = 0;
9
   }
10
   :
11
   :
12
}
13
14
UART_ISR()
15
{
16
:
17
:
18
}
19
20
main()
21
{
22
  
23
  while(1) {  // die ewige Hauptschleife
24
     :
25
     if (Flag_4sec) {
26
        SendeDenWertMitDemUART(x);
27
        Flag_4sec = 0;
28
     }
29
     
30
     if (uart_str_complete) {
31
        x = StringInDieVariableEinlesen();
32
        uart_str_complete = 0; 
33
     }
34
     :
35
  }
36
}

von Andreas H. (Gast)


Lesenswert?

Ok, danke euch allen für die Hilfe. Das Programm funktioniert jetzt so 
wie es soll mit dem Tipp von Karl Heinz Buchegger.
Und, ja, es sieht zusammengewürfelt aus da ich mich durch mehrere 
Tutorials gewühlt habe und mir den entsprechenden Quellcode 
zusammengebastelt hab. Werde das nachher mal gründlich aufräumen und 
ausmisten müssen.

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.