Forum: Mikrocontroller und Digitale Elektronik AVR-C Serial Problem ATmega8


von Num10 (Gast)


Lesenswert?

Hi,
ich brauche Hilfe bei meinem AVR-C Code fuer einen ATmega8. Um Kontext 
zu schaffen: Dieser Code soll mithilfe des ADCs die Spannung eines 
Spannungsteilers einlesen. Diese value wird an die Funktion 
'Update_CtrlCntr1' uebergen, welche sie so umrechnet, dass ich fuer den 
Counter 1 eine Compare-value bekomme, die die ON-Time eines von diesem 
Counter generiertem 50-Hz Signal bestimmt.
Wie man im Code unten sieht, wird der character cmd als 'f' gesetzt. In 
diesem Mode liest der ADC bei jedem Durchlauf der while Schleife eine 
neue value von dem Spannungsteiler ein und updated so die ON-Time des 
PWM-Signals.
Nun, ich moechte diesen ATmega mithilfe eines Arduinos steuern, heisst, 
ich sende ueber Serial die characters 'A', 'B', 'C', ... und moechte, 
dass die in der Switch Case Anweisung spezifizierte mot_control value 
benutzt wird und solange cmd nicht 'f' ist, der ADC nicht aktiv ist (Die 
mot_control value also fest ist). Mithilfe der 'Serial_avail()' 
Funktion wird, wie es der Name verratet, gecheckt, ob das 'RXC' (Receive 
Complete) Bit gesetzt ist, bzw. etwas empfangen wurde, also etwas im 
'UDR' Register ist. Nur falls diese Funktion eine '1' zurueckgibt, soll 
der command eingelesen und verarbeitet werden.

Mein Problem ist, dass der ATmega zwar das PWM-Signal richtig generiert 
und den ADC richtig einliest, aber nicht auf die vom Arduino gesendeten 
Bytes reagiert. Generell, wenn ich in der 'default' Sektion der Switch 
Case Anweisung NICHT den cmd direkt wieder auf 'f' setze, die anfangs 
anliegende Spannung eingelesen wird und gesetzt BLEIBT, egal ob ich die 
Spannung veraendere. Also wird sofort in die Switch Case Anweisung 
geleitet und die Variable cmd geaendert. Ein Senden von commands bringt 
nichts. Ich hab mir das jetzt schon ziemlich oft durchgeschaut, etwas 
veraendert, komm jedoch nicht auf den Fehler. Kann sein, dass es etwas 
offensichtliches ist, wer weiss.

Unten hier liegen die Codes fuer die Main-Funktion, Serial_init 
Funktion, Serial_avail Funktion und Serial_read Funktion.

Vielen Dank im Voraus!
1
int main(void)
2
{
3
    Serial_init( MYUBRR );
4
  Init_CtrlCntr1();  // Control Counter 1
5
  Init_ADC();
6
7
  int mot_ctrl;    // motor control value (10-bit because ADC)
8
  char cmd = 'f';    //cmd = 'command' received by Arduino
9
10
    while(1)
11
    {
12
    if (cmd == 'f') mot_ctrl = ADC_read ( ADC_INPUT );
13
    Update_CtrlCntr1 ( mot_ctrl );
14
15
    if(Serial_avail() == 1){
16
      cmd = Serial_readChr();
17
      switch(cmd){
18
        case 'A': mot_ctrl = 0; break;
19
        case 'B': mot_ctrl = 114; break;
20
        case 'C': mot_ctrl = 228; break;
21
        case 'D': mot_ctrl = 341; break;
22
        case 'E': mot_ctrl = 455; break;
23
        case 'F': mot_ctrl = 569; break;
24
        case 'G': mot_ctrl = 683; break;
25
        case 'H': mot_ctrl = 796; break;
26
        case 'I': mot_ctrl = 910; break;
27
        case 'J': mot_ctrl = 1024; break;
28
        default: cmd = 'f'; break;
29
      }
30
    }
31
    }
32
}
1
void Serial_init(int ubrr){
2
  UBRRH = 0;
3
  UBRRL = 0;
4
  UCSRA = 0;
5
  UCSRB = 0;
6
  UCSRC = 0;
7
8
  // Set UBRR for BAUD
9
  UBRRH = ubrr >> 8;
10
  UBRRL = ubrr & 0xFF;
11
  // Enable Receiver and/or Transmitter (Here only RX)
12
  UCSRB |= (1 << RXEN);
13
  // Set Modes (Here Asynchronous modes, no parity, 8 data bits, 1 stop bit)
14
  UCSRC |= (1 << UCSZ1) | (1 << UCSZ0);
15
}
1
int Serial_avail(){
2
  if ( UCSRA & (1<<RXC) ) return 1;
3
  else return 0;
4
}
1
char Serial_readChr(){
2
  // Return Value currently in the Data Register
3
  return UDR;
4
}
Hier auch noch die Berechnung der UBRR-value mit der Baudrate und dem 
Clock-Speed.
1
#define F_CPU 1000000L
2
#define BAUD 4800L
3
4
#define MYUBRR (F_CPU/(16*BAUD)-1)

von Stefan F. (Gast)


Lesenswert?

Für die Berechnung der Baudrate gibt es Makro in der avr-libc.
https://www.nongnu.org/avr-libc/user-manual/group__util__setbaud.html

Darin sieht die Formel anders aus. Auf 
https://www.mikrocontroller.net/articles/AVR-Tutorial:_UART#UART_konfigurieren 
gibt es dazu auch den "Wichtigen Hinweis 1"

Vielleicht ist das dein Knackpunkt. Ich würde an deiner Stelle auch den 
Transmitter einschalten und alle empfangenen Zeichen als Echo zurück an 
den PC senden. Dann siehst du, ob die Kommunikation funktioniert.

von S. Landolt (Gast)


Lesenswert?

War mir zuviel Text, habe ich nicht gelesen. Aber:

1. Es wird MYUBRR definiert, aber ubrr zugewiesen.
2. Es handelt sich doch um C, warum also dieser Klimmzug mit UBRRH und 
UBRRL statt gleich UBRR=MYUBRR;?
3. und vor allem:
1
UCSRC |= (1 << UCSZ1) | (1 << UCSZ0);
aus dem Datenblatt:
"• Bit 7 – URSEL: Register Select
This bit selects between accessing the UCSRC or the UBRRH Register. ... 
The URSEL must be one when writing the UCSRC."
Am besten UCSRC gar nicht anfassen, der Reset-Wert passt schon.

von S. Landolt (Gast)


Lesenswert?

PS:
Okay, mein Punkt 1 hat sich erledigt - da habe ich denn doch zu wenig 
gelesen.

von Num10 (Gast)


Angehängte Dateien:

Lesenswert?

Vielen Dank, hab dies versucht, jedoch keinen Erfolg mit der Formel 
erzielen koennen.
Hab deinen Vorschlag das empfangene direkt wieder an den Arduino 
zurueckzusenden versucht, bekomme aber gar nichts zurueck. Bekomme, wie 
man in der angehaengten Datei sieht, nur am Anfang, wenn ich den Arduino 
(mit dem der ATmega versorgt wird) einstecke und den Serial Monitor 
oeffne etwas zurueck... und zwar anscheinend nichts.
Hier auch noch meine Veraenderungen im AVR-Code, will nicht was 
offensichtliches wieder stundenlang suchen...
1
int main(void)
2
{
3
    Serial_init( MYUBRR );
4
  Init_CtrlCntr1();    // Control Counter 1
5
  Init_ADC();
6
7
  int mot_ctrl;      // motor control value (10-bit because ADC)
8
  char cmd = 'f';      //cmd = 'command' received by Arduino
9
10
    while(1)
11
    {
12
    if (cmd == 'f') mot_ctrl = ADC_read ( ADC_INPUT );
13
    Update_CtrlCntr1 ( mot_ctrl );
14
15
    if(Serial_avail() == 1){
16
      cmd = Serial_readChr();
17
      Serial_sendChr(cmd);
18
      switch(cmd){
19
        case 'A': mot_ctrl = 0; break;
20
        case 'B': mot_ctrl = 114; break;
21
        case 'C': mot_ctrl = 228; break;
22
        case 'D': mot_ctrl = 341; break;
23
        case 'E': mot_ctrl = 455; break;
24
        case 'F': mot_ctrl = 569; break;
25
        case 'G': mot_ctrl = 683; break;
26
        case 'H': mot_ctrl = 796; break;
27
        case 'I': mot_ctrl = 910; break;
28
        case 'J': mot_ctrl = 1024; break;
29
        default: cmd = 'f'; break;
30
      }
31
    }
32
    }
33
}
1
void Serial_init(int ubrr){
2
  UBRRH = 0;
3
  UBRRL = 0;
4
  UCSRA = 0;
5
  UCSRB = 0;
6
  UCSRC = 0;
7
8
  // Set UBRR for BAUD
9
  UBRRH = ubrr >> 8;
10
  UBRRL = ubrr & 0xFF;
11
  // Enable Receiver and/or Transmitter (Here only RX)
12
  UCSRB |= (1 << RXEN) | (1 << TXEN);
13
  // Set Modes (Here Asynchronous modes, no parity, 8 data bits, 1 stop bit)
14
  UCSRC |= (1 << UCSZ1) | (1 << UCSZ0);
15
}
1
void Serial_sendChr(char csend){
2
      while (!(UCSRA & (1<<UDRE)))  /* warten bis Senden moeglich */
3
      {}
4
5
      UDR = csend;                      /* sende Zeichen */
6
      return 0;
7
}

von S. Landolt (Gast)


Lesenswert?

Und was ist nun mit dem UCSRC im geänderten Programm?

Beitrag #6126280 wurde von einem Moderator gelöscht.
von S. Landolt (Gast)


Lesenswert?

So wie ich das sehe, betreiben Sie die Schnittstelle mit 5 bit und 40 
Bd.

von Thomas E. (thomase)


Lesenswert?

Num10 schrieb:
1
UBRRH = ubrr >> 8;
2
3
UCSRC |= (1 << UCSZ1) | (1 << UCSZ0);


UBRRH und UCSRC haben beim Atmega8 dieselbe Adresse. Damit die Daten ins 
UCSRC geschrieben werden, muß das URSEL-Bit gesetzt werden. Sonst 
überschreibst du UBRRH und deine Baudrate stimmt nicht mehr.

Das ist das, was dir S. Landolt mit seinem Punkt 3 sagen wollte.

Also: RTFM, Seite 146.

Beitrag #6126285 wurde von einem Moderator gelöscht.
von Num10 (Gast)


Lesenswert?

S. Landolt schrieb:
> Und was ist nun mit dem UCSRC im geänderten Programm?

Sorry, hab erstmal das oben versucht, hab das erst gelesen.

Hab jetzt das URSEL Bit gesetzt und siehe da -> geht!
Vielen Dank! Tja, da lohnt es sich halt wirklich genau im Datenblatt zu 
lesen anstatt Code von einem anderen ATmega zu nehmen ;)

Falls irgendwer den Thread findet und das gleiche Problem hat:

Wenn man irgendwas beim USCRS Register beim ATMega 8 (vielleicht auch 
anderen ATmegas, muss man nachschauen) aendern will, z.B. die Anzahl an 
zu empfangenen Bits zu setzen usw. muss auch unbedingt das URSEL Bit in 
dem USCRS Bit gesetzt sein, sonst wird das UBRRH Register verwendet. 
Natuerlich wenn man aufs UBRRH Register schreiben will das Bit wieder 
auf 0 setzen.

von S. Landolt (Gast)


Lesenswert?

> Das ist das, was dir S. Landolt mit seinem Punkt 3 sagen wollte.

Genau.
  Aber noch deutlicher als "Am besten UCSRC gar nicht anfassen"?

Beitrag #6126322 wurde von einem Moderator gelöscht.
von S. Landolt (Gast)


Lesenswert?

S. Landolt schrieb:
> So wie ich das sehe, betreiben Sie die Schnittstelle mit 5 bit und 40
> Bd.

Falsch gesehen, das ist ärgerlich.
Also nochmal (und damit Thomas Eckmann bestätigen): das
1
 UCSRC = 0;
läuft ja in UBRRH, folglich bleibt UCSRC auf 8 bit; mit
1
 UCSRC |= (1 << UCSZ1) | (1 << UCSZ0);
wird UBRRH auf 6 gesetzt und somit die Übertragungsrate auf rund 40 Bd.

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.