Forum: Mikrocontroller und Digitale Elektronik kommunikation zwischen uc und pc


von Floyd (Gast)


Lesenswert?

hallo zusammen!
ich möchte gerne eine permanente kommunikation zwischen uc (atmega16) 
und pc aufrecht erhalten. datenaustausch über rs232 funktioniert in 
beide richtungen - leider nur unter einschränkungen (es können nur 8 
bits gesendet werden). in abhängigkeit von bestimmten einflüssen, sollen 
zwei mototen mit pwm geregelt werden. jeder motor für sich läuft, doch 
ich weiß nicht wie ich eine unterscheidung der motoren realisieren kann.

beispiel:

pc sagt: motor 1 -> pwm 255
         motor 2 -> pmw 180

wie schaffe ich es, dass der uc beim lesen von udr mehr als 8 bit 
einliest?

hatte gedacht dass ich vom pc aus z.b. 1255 verschicke, wobei die 1 für 
den ersen motor steht.

benutze das avr studio.

würde mich über anregungen oder verbesserungsvorschläge freuen.

gruß
  Floyd

von Karl heinz B. (kbucheg)


Lesenswert?

Was du brauchst ist ein Protokoll.

Denk dir was aus: zb.
  1. Byte  Kennung des Motors
  2. Byte  PWM Wert

> wie schaffe ich es, dass der uc beim lesen von udr mehr als 8 bit
> einliest?

Gar nicht. Die Einheit der Übertragung ist ein Byte (8 bit).
Wenn dir das nicht reicht, dann benutz halt mehrere Byte, siehe oben.





von johnny.m (Gast)


Lesenswert?

Alle seriellen Übertragungsarten, die von µCs zur Verfügung gestellt 
werden, sind zunächst 8-Bit-Interfaces. Wenn Du mehr als 8 Bit 
übertragen willst, musst Du ein Protokoll vereinbaren und "Frames" aus 
mehreren Bytes versenden. Das ist eigentlich kein großes Problem. Du 
schickst also z.B. immer 2 Bytes, von denen das erste die "Adresse" 
(also z.B. die Nummer) des Motors enthält und das zweite den gewünschten 
Wert für die Geschwindigkeit.

von Floyd (Gast)


Lesenswert?

könnt ihr mir ein bischen starthilfe für die erstellung eines solchen 
protokolls geben?

von Peter S. (psavr)


Lesenswert?

Du kannst ja mehrere Bytes nacheinader senden.

Ich würde aber keine binären Werte über die RS-232 senden, sondern nur 
ASCII-Characters. Dies vermeidet allfällige Probleme mit Steuerzeichen. 
[Carridge Return] [Line-Feed] [Backspace] [Delete] etc...

Deine Zahl würde dann als "1255/n" bzw. [0x31,0x32,0x35,0x35,0x13] 
übermittelt.

So kannst Du die Werte auch direkt im via ein Terminalprogramm eingeben 
oder anzeigen lassen.

MfG  Peter

von Peter S. (psavr)


Lesenswert?

Oder noch besser: übermittle die Werte in einer besser lesbaren Form, 
z.B. mit Kommandos wie zum Beispiel:

M1-255
M2-180

von Floyd (Gast)


Lesenswert?

vom pc solche kommandos abschicken geht ja, doch ich weiß nicht ganz wie 
es dann im uc weitergeht. um einen sochen string einzulesen, muss ich 
doch mehrmals udr auslesen. wie kann ich das machen ohne daten zu 
verliehren?

von johnny.m (Gast)


Lesenswert?

Dafür gibts nen Interrupt, der ausgelöst wird, wenn ein Zeichen 
empfangen wurde. Wenn Deine Strings immer gleich lang sind (was zu 
empfehlen ist), dann musst Du nur warten, bis ein String komplett ist 
und ihn dann auswerten. Wenn Du als letztes Zeichen einen Nullterminator 
sendest, kannst Du auch eine Fehlerüberprüfung machen.

Beispiel:
1. Byte: Nummer des Motors
2. bis 4. Byte: Wert für PWM
5. Byte: Nullterminator
Du brauchst also nur einen Puffer mit 5 Bytes, in den nacheinander in 
der Interrupt-Routine die ankommenden Zeichen geschrieben werden und 
dessen aktuelle Schreibposition durch einen Zähler überwacht wird. Wenn 
der Puffer voll ist (also der Zähler z.B. in diesem Beispiel den Wert 4 
erreicht hat), kann der String ausgewertet werden.

von Floyd (Gast)


Lesenswert?

hi johnny.m
genau so würde ich das gerne umsetzen wollen! könntest du mir mit 
entsprechendem c-code auf die sprünge helfen? bis jetzt sieht das ganze 
bei mir so aus:

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

#define   F_CPU 3686400
#include <util/delay.h>

unsigned char buffer1;

void uart_init(void)
{
  UBRRH = 0x00;
  UBRRL = 0x17;
  UCSRB = 0x98;
  UCSRC = 0x86;
}

unsigned char USART_RX(void)
{
while(!(UCSRA&(1<<RXC)))
  ;
return UDR;
}


int main(void)
{

PORTD=0x00;
DDRD=0x20;

TCCR1A=0x81;
TCCR1B=0x05;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

uart_init();

while(1)
{
         // will sehen was am uc angekommen ist
  USART_RX();
  buffer1 = UDR;


// Ende vom while
}
// Ende der main
}

von Karl heinz B. (kbucheg)


Lesenswert?

Als erstes würde ich mir mal eine String-Ausgabe machen:
1
// Sendet ein einzelnes Zeichen
2
void uart_putc( char c )
3
{
4
  // Warte bis die Sendeeinheit bereit ist
5
  while( !(USR & ( 1<<UDRE) ) )
6
    ;
7
  UDR = c;
8
}
9
10
// Sende einen C-String
11
void uart_puts( const char* str )
12
{
13
  while( *str ) {
14
    uart_putc( *str );
15
    str++;
16
  }
17
}

Damit kannst du schon mal solche Dinge machen:
1
int main()
2
{
3
  while( 1 )
4
    uart_puts( "Hallo du\n" );
5
}

oder auch
1
int main()
2
{
3
  char Text[] = "Hallo Welt\n";
4
  while( 1 )
5
    uart_puts( Text );
6
}

Dann gehts weiter: Du brauchst eine Funktion, die nicht
nur ein einzelnes Zeichen empfängt, sondern eine Zeile
davon. Als Zeile definieren wir mal: Alles was bis zum
Return Zeichen geht:
1
unsigned char USART_RX(void)
2
{
3
  while(!(UCSRA&(1<<RXC)))
4
    ;
5
  return UDR;
6
}
7
8
void uart_gets( char* Input )
9
{
10
  char c = USART_RX();
11
  uart_putc( c );   // und gleich wieder zurückschicken, damit
12
                    // der Benutzer auch was sieht
13
14
  while( c != '\n' ) {
15
    *Input = c;
16
    Input++;
17
    c = USART_RX();
18
    uart_putc( c );
19
  }
20
  *Input = '\0';
21
}

Damit kann man dann schon so was machen:
1
#include <string.h>
2
3
.... // hier die uart Funktionen
4
5
int main()
6
{
7
  char Buffer[20];
8
9
  while( 1 ) {
10
    uart_gets( Buffer );   // Auf eine Zeile vom Benutzer warten
11
12
    //
13
    // Testweise die komplette Zeile zurückschicken, so wie
14
    // sie vom µC empfangen wurde
15
    //
16
    uart_puts( "Ich habe verstanden : " );
17
    uart_puts( Buffer );
18
    uart_putc( '\n' );
19
20
    // war die Eingabe vom Benutzer der Text 'Floyd'
21
    if( strcmp( Buffer, "Floyd" ) == 0 ) {
22
      // Wenn ja, dann begrüsse ihn
23
      uart_puts( "Hallo Floyd\n" );
24
      uart_puts( "Wie gehts\n" );
25
    }
26
  }
27
}

Wenn du dich jetzt mit einem Terminalprogramm an den
µC klemmst, muss der brav jede Eingabe wieder zurück-
schicken. Jedesmal wenn du Return drückst, zeigt
er dir die zuletzt gelesene komplette Zeile an.


Und so gehts dann weiter:
Du vereinbarst mit dir, dass das erste Zeichen in der
Übertragung die Motornummer ist, das nächste Zeichen
ignorierst du und daran anschliessend kommt der PWM Wert.

An das erste Zeichen kommst du ganz einfach mit Buffer[0] ran.
Ab Buffer[1] steht dann eine 'Zahl' in ihrer ASCII Form.
Die Funktion atoi() kann diese ganz leicht in eine tatsächliche
Zahl wandeln.
1
....
2
3
   int MotorNr;
4
5
   while( 1 ) {
6
7
     uart_gets( Buffer );
8
9
     // zu testzwecken. Ist immer gut zu kontrollieren
10
     // was der µC tatsächlich empfängt
11
     uart_puts( "Ihre Eingabe: " );
12
     uart_puts( Buffer );
13
     uart_putc( '\n' );
14
15
     MotorNr = Buffer[0] - '0';    // Die Motornummer nummerisch machen
16
     PWMWert = atoi( &Buffer[2] ); // Den PWM Wert umrechnen lassen
17
18
     if( MotorNr == 0 )
19
       uart_puts( "Setze Motor 0 auf neuen Wert\n" );
20
21
     else if( MotorNr == 1 )
22
       uart_puts( "Setze Motor 1 auf neuen Wert\n" );
23
24
     else
25
       uart_puts( "Diesen Motor gibt es nicht!\n" );
26
  }
27
}

Verbinde dich wieder mit dem Terminalprogramm zum µC und
gib ein:
0 200
(und natürlich Return drücken)

Der µC sollte antworten mit:
Ihre Eingabe: 0 200
Setze Motor 0 auf neuen Wert

1 154
Ihre Eingabe: 1 154
Setzte Motor 1 auf neuen Wert
2 300
Diesen Motor gibt es nicht!

von Floyd (Gast)


Lesenswert?

hallo Karl Heinz!
vielen dank für deine ausführlichen beschreibungen und code-auszüge. sie 
haben mein problem vollständig gelöst. leider kann ich das ergebnis erst 
in zwei tagen begutachten, da sich mein uc gestern abend verabschiedet 
hat...

vielen dank!

gruß,
  Floyd

von Karl heinz B. (kbucheg)


Lesenswert?

Ich hoffe mal, dassich nicht allzuviele Fehler eingebaut habe.
Ein Fehler ist beim direkten eintippen in da Forum immer
drinnen.

Wenn du noch Probleme hast: gerne wieder.

von Floyd (Gast)


Lesenswert?

hab gerade noch einen uc gekauft, da ich neugierig geworden bin.
ist mir jetzt wirklich schon peinlich, aber ich hab da ein kleines 
problem mit dem code. definiere ich buffer im uc läuft die 
identifizierung einwandfrei. lese ich jedoch über den uart den string 
ein, wird nur "uart_gets(Buffer)" aufgerufen und danach ist schluß. 
woran liegt es? hab gerade echt ein brett vorm kopf...

der code:

#include <avr/io.h>
#include <stdio.h>
#include <string.h>


int MotorNr;
char Buffer[20];
//char Buffer[]="1 255\n";

// Sendet ein einzelnes Zeichen
void uart_putc( char c )
{
  // Warte bis die Sendeeinheit bereit ist
  while( !(UCSRA & ( 1<<UDRE) ) )
    ;
  UDR = c;
}

// Sende einen C-String
void uart_puts( const char* str )
{
  while( *str ) {
    uart_putc( *str );
    str++;
  }
}

// Ausgabe mit printf über USART
static int uart_putchar(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
                                             _FDEV_SETUP_WRITE);

static int
    uart_putchar(char c, FILE *stream)
    {

      if (c == '\n')
        uart_putchar('\r', stream);
      loop_until_bit_is_set(UCSRA, UDRE);
      UDR = c;
      return 0;
    }

unsigned char USART_RX(void)
{
  while(!(UCSRA&(1<<RXC)))
    ;
  return UDR;
}

void uart_gets( char* Input )
{
  char c = USART_RX();
  uart_putc( c );

  while( c != '\n' )
  {
    *Input = c;
    Input++;
    c = USART_RX();
    uart_putc( c );
  }
  *Input = '\0';
}


void uart_init(void)
{
  UBRRH = 0x00;
  UBRRL = 0x17;
  UCSRA = 0x00;
  UCSRB = 0x18;
  UCSRC = 0x86;
}


int main(void)
{

PORTD=0x00;
DDRD=0xB0;

TCCR1A=0xA1;
TCCR1B=0x0B;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// USART initialisieren
uart_init();


while(1)
{

uart_gets(Buffer);

uart_puts( "Ihre Eingabe: " );
uart_puts( Buffer );
uart_putc( '\n' );

MotorNr = Buffer[0] - '0';    // Die Motornummer nummerisch machen
OCR1AL = atoi( &Buffer[2] ); // Den PWM Wert umrechnen lassen

if( MotorNr == 0 )
{
uart_puts( "Setze Motor 0 auf neuen Wert\n" );
stdout = &mystdout;
printf(" Dieser ist: %u\n",OCR1AL);
}

else if( MotorNr == 1 )
{
uart_puts( "Setze Motor 1 auf neuen Wert\n" );
stdout = &mystdout;
printf(" Dieser ist: %u\n",OCR1AL);
}

else
uart_puts( "Diesen Motor gibt es nicht!\n" );

// Ende vom while
}
// Ende der main
}


vielen dank im voraus.

Floyd

von Stefan (Gast)


Lesenswert?

Dein uart_gets() liest ein bis ein '\n' (=LINEFEED) kommt. Schickt dein 
PC Terminalprogramm überhaupt solche Zeichen oder schickt es beim 
Drücken von RETURN nur '\r' (=CARRIAGE RETURN)?

von Floyd (Gast)


Lesenswert?

jetzt ja. das war die antwort. danke!

gruß,
 Floyd

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.