UART Problem (Assembler zu C) III

#716705
Lesenswert?

Hallo,
ich habe jetzt mal ein Programm geschrieben, was nur das Sendet, was es 
Empfängt. Und es kommt wieder zum gleichen Phänomen. Wenn ich 1 sende 
bekomme ich F1 zurück bei 2 F2 usw. Bei 15 D5 aber wieso zum teufel wird 
dann die erste gesendete 15 vom Rechner korrekt erkannt? Im Anhang die 
aktuelle Komplettversion.
Angehängte Dateien:
Persönliche Seite #716842
Lesenswert?

An eine falsche Initialisierung vom UART glaube ich nicht. Ich hatte ja 
deinen Code bzw. die Initialisierung auf einem Atmega32 und angepasstem 
F_CPU laufen lassen und das funktionierte mit einem PC auf der 
Gegenseite. Ich habe aber keinen Atmega162 zum testen, vielleicht jemand 
anderes?

Du könntest aber nachsehen, ob der ASM-Code aus der LSS-Datei die 
UART-Register genauso initialisiert wie der disassemblierte Code (Suche 
im Forum µC & Elektronik nach Disassemb*) des funktionsfähigen Programms 
aus den ASM-Quellen.

Warum schlage ich so was kompliziertes wie ein Disassemblieren vor? Bist 
du 1000% sicher, dass das lauffähige Programm aus bereits gezeigten 
ASM-Quellen entstanden ist?

Vorherige Threads:
Beitrag "UART Programm (Umwandlung von Assembler zu C)"
Beitrag "UART Problem (Assembler zu C) II"
Persönliche Seite #716899
Lesenswert?

Ein Unterschied ist bei der Initialisierung beim C-Code im Vergleich zum 
ASM-Code:

Im C-Code werden UCSRxC und UCSRxB verODERt gesetzt, während sie im 
ASM-Code direkte Werte bekommen.

Der neue C-Code sähe so aus:
1
  //Baudrate 9k6 UART0
2
  UBRR0H = UBRR_VAL >> 8;   // ASM: 0
3
  UBRR0L = UBRR_VAL & 0xFF; // ASM: 0x2F = 47 dito in C
4
  
5
  //Baudrate 9k6 UART1
6
  UBRR1H = UBRR_VAL >> 8;
7
  UBRR1L = UBRR_VAL & 0xFF;
8

9
  //Uebertragungsart  8N2
10
  UCSR1C = (1<<URSEL1)|(1<<USBS1)|(1<<UCSZ11)|(1<<UCSZ01); 
11
  // ASM: -0x72 = 0x8E = 10001110 = Bits 7,3,2,1
12
  // in C OK      
13
  UCSR0C = (1<<URSEL0)|(1<<USBS0)|(1<<UCSZ10)|(1<<UCSZ00);
14

15
  //Interrupts, Senden und Empfangen aktivieren
16
  UCSR1B = (1<<RXCIE1)|(1<<RXEN1)|(1<<TXEN1); 
17
  // ASM: Bits 7,4,3
18
  // in C OK
19
  UCSR0B = (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
Gast #717138
Lesenswert?

Ein Blick in's 'Register Summary' sagt, dass du da Bit namen 
verwechselt, bzw einen Zahlendreher hast:
1
//Uebertragungsart  8N2
2
  UCSR1C = (1<<URSEL1)|(1<<USBS1)|(1<<UCSZ11)|(1<<UCSZ01);
3
                                                      ^^
4
                                                  UCSZ10 
5
  // ASM: -0x72 = 0x8E = 10001110 = Bits 7,3,2,1
6
  // in C OK      
7
  UCSR0C = (1<<URSEL0)|(1<<USBS0)|(1<<UCSZ10)|(1<<UCSZ00);
8
                                          ^^
9
                                      UCSZ01
hth. Jörg

ps.: Ich weiß das, weil ich den Fehler schon mal behoben hab, nicht weil 
ich das datenblatt auswendig kann ;)
#717408
Lesenswert?

so ich hab mal alles nach und nach gelöscht und es scheint daran zu 
liegen:
1
uint8_t SendHeader [] = {0x3a,'V', 'A', 'L', 0x00, 'V','M', 0x00,0x01,0x00,0x01,'A', 0xff,0xff,0xff,0xff,0xff,0xff,0xff,'V','a', 'r', 'i', 'a', 'b', 'l', 'e', 'R', 0x0A,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd0};
2
uint8_t SendEheader [] = {0x3A,0x45,0x4E,0x44,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x56};

aber wieso wird HeaderCounter0 nicht erhöht wie hängt denn das zusammen?
#717435
Lesenswert?

ich werd verrückt...das gleiche mit dem End_header. Ich hab das 
geschrieben:
1
  while(1) {
2
  
3
  
4
    if(ucUDR_valid0) {
5
    
6
            if(State0 == REC_FOOTER) {
7
      Transmit0(EndCounter0);
8
    }

und wenn ich am Computer so lange gesendet habe, bis ich dort bin 
schickt der µC entweder 35 oder 25 zurück keine Spur von einer Variable, 
die Hochzählt.
Angehängte Dateien:
Persönliche Seite #717598
Lesenswert?

Wenn das Rausnehmen von fetten Feldern die Situation verbessert, ist 
bestimmt die Speicherauslastung an der Grenze. Wie ist denn der 
Speicherfüllgrad?

Bei http://www.mikrocontroller.net/articles/AVR-GCC#Tipps_.26_Tricks 
unter der Beschreibung des Tools avr-size nachsehen, Werte für die 
ELF-Datei ermitteln und mit dem Datenblatt vergleichen.

Das SRAM muss ausserdem noch einen Stack aufnehmen, d.h. data+bss Grösse 
muss entsprechend kleiner als die SRAM Grösse sein.

ADD:
> Atmega162
> Flash (Kbytes)  16
> EEPROM (Kbytes) 0.5
> SRAM (Bytes)    1024

Da bist du mit zwei Feldern zu je 384 Bytes schon anständig am SRAM 
füllen!
Persönliche Seite #717807
Lesenswert?

An der Ausgabe vom avr-size siehst du es. Dein Ziel muss sein:

FLASH-ROM >= text + data
SRAM >= data + bss + stack

Lediglich der Platzbedarf vom Stack (für die Rücksprungadressen beim 
Aufruf von Funktionen und ggf. Funktionsargumente soweit nicht in 
Registern übergeben) fehlt noch in der Berechnung. Den kann man 
abschätzen oder mit Versuchen ermitteln. Bei deinem Programm ist das 
nicht viel; vielleicht 50 Bytes?

Im C-Programm sind die Übergabevariablen aus den UART-ISRs und die 
Verwaltung der state machine hinzugekommen. Die Art und den Platzbedarf 
der Variablen im ASM-Programm kenne ich nicht. Bist du sicher, dass dort 
die gleichen Variablen benutzt werden?
#717817
Lesenswert?

also im ASM hab ichs so:
1
 .equ var0      = 0x0142
2

3
 .equ var1      = 0x02E2
4

5
 .equ Vartheta    = 0x0482
1
VariablenHeader:
2

3
.db 0x3a,'V', 'A', 'L', 0x00, 'V','M', 0x00,0x01,0x00
4

5
.db 0x01,'A', 0xff,0xff,0xff,0xff,0xff,0xff,0xff,'V' 
6

7
.db 'a', 'r', 'i', 'a', 'b', 'l', 'e', 'R', 0x0A,0xff  
8

9
.db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff  
10

11
.db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd0
12

13

14

15
Endheader:
16

17
.db 0x3A,0x45,0x4E,0x44,0xff,0xff,0xff,0xff,0xff,0xff
18

19
.db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
20

21
.db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
22

23
.db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
24

25
.db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x56

Das sind bei den Variablen sogar jeweils 32 Bytes mehr.
Persönliche Seite #717837
Lesenswert?

Die Zahlen sind nicht aussagekräftig. Es handelt sich bestimmt um 
Anfangsadressen von Variablen aber nicht um deren Länge. Die Länge aller 
Variablen plus der Stackbedarf bestimmt den benötigten Platz im SRAM.

In einem der Listings hattest du mal dies drin:

0x0100 ist die Startadresse vom internen SRAM in der 
Speicherkonfiguration A beim Atmega162 (Figure 9 im Datenblatt).
1
 ;interner RAM
2
 .equ Header    = 0x0100      ; 50 Bytes
3
 .equ Content   = 0x0132      ; 16 Bytes
4
 .equ var0      = 0x0142      ; 416 Bytes
5
 .equ var1      = 0x02E2      ; 416 Bytes
6
 .equ Vartheta  = 0x0482      ; 16 Bytes
7
 .equ Varr      = 0x0492      ; 16 Bytes
8
 .equ wandeldatalow  = 0x04a2 ; 1 Byte
9
 .equ wandeldatahigh = 0x04a3 ; 1 Byte
10
 .equ wandeltemp  = 0x04a4    ; 1 Byte ?
11
;                             ==========
12
;                               933 Bytes

Wenn man annimmt, dass die Variablen hintereinander stehen, kann man 
z.B. für Header einen Platzbedarf von 0x0132 - 0x0100 = 50 Bytes 
annehmen.

Schwierig wird es, bei der letzten Variablen wandeltemp. Da muss man im 
ASM-Listung nachsehen wie die benutzt wird. Vom Vergleich mit den 
anderen Wandeldingsen her möglicherweise als Byte.

Dann wäre hier der Platzbedarf insgesamt: 0x04a5-0x0100 = 0x03a5 = 933 
Bytes für die Variablen. Der Rest vom SRAM (1024-933 = 91) kann als 
Stack benutzt werden.

Vergleicht man ASM mit C sieht man, dass z.B. oben nur eine Variable für 
den 50-Bytes Header angelegt ist, während du im C Programm 4 Stück 
angelegt hast (Header Uart0, Header Uart1, SendHeader und 
SendEHeader)...
#717841
Lesenswert?

stimmt jetzt weis ich wie das zustande kommt. im asm kann theoretisch 
immer nur ein uart senden und empfangen im c theoretisch beide. deshalb 
habe ich die im ASM zusammengefasst. Wie es in der Praxis aussieht weis 
ich nicht...ob da beide Rechner zugleich senden können. Auserdem wird 
der Endheader nicht abgespeichert. Da ich den nicht brauche kann ich den 
im C auch löschen. Ich probier mal, was da noch zu optimieren geht.

Edit:

mit den Endheadern weg bin ich jetzt bei:
   text    data     bss     dec     hex filename
    734     100     923    1757     6dd /New.elf

also immer noch zuviel.
Persönliche Seite #717845
Lesenswert?

Wie Platz im SRAM sparen?

Möglichkeit 1: Platz hast du ja im FLASH_ROM genug. Du könntest z.B. die 
festen Werte von VariablenHeader und Endheader aus dem FLASH-ROM aus 
ausgeben und nicht aus dem SRAM heraus.
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29

Möglichkeit 2: Benutze Felder wenn möglich mehrfach. Header und Content 
aus dem ASM-Listing scheinen keine Unterscheidung in Uart0/Uart2 zu 
brauchen. Vielleicht kannst du auch SendHeader und SendEHeader 
vermeiden, indem du deine Arbeitsvariable Header durch eine Funktion mit 
den Werten füllen lässt.
#717857
Lesenswert?

Naja im ASM brauchen die keine Unterscheidung, da das Programm ja in der 
Interrupt Routine festhängt. Aber im C können ja theoretisch beide 
Rechner zu gleich senden und ausgewertet werden.

wobei das ganze mit 2 Uarts Parallel evtl durch diese Stelle beindert 
werden könnte:
1
    if(ucUDR_valid0) {
2
      
3
      //Interrupts deaktivieren
4
      cli();              
5
      //Byte in c einlesen
6
      c = ucUDR0; //
7
      
8
      //ucUDR_valid0 zurücksetzen
9
      ucUDR_valid0 = 0;
10
      //Interrupts aktivieren
11
      sei();

aber man kann ja eigentlich auch nur das Interrupt für Uart 1/0 
deaktivieren oder?

Das ist eben die Frage wenn es gehen würde, dass man mit Zwei Rechnern 
zugleich kommunizieren kann das könnte man die Festen Daten in den Flash 
laden wenn nicht könnte man Die Daten zusammenfassen. Müsste aber dann 
die 2. Uart blockieren, so das nicht was überschrieben wird.
Persönliche Seite #718200
Lesenswert?

Der nächste Interrupt geht ja nicht verloren, sondern staut sich auf. 
Wenn sei die Interrupts global wieder zulässt, wird der angestaute 
Interrupt ausgelöst.

Ein Interrupt kann verloren gehen, wenn bei gesperrtem Interrupts die 
gleiche Interruptquelle mehrfach einen Interrupt melden will. Das ist 
hier im Fall der UARTs IMHO uninteressant - käme es zu diesem Fall, 
wären sowieso schon Zeichen verloren.

Dazu kommt es aber nicht, denn die Codestelle zwischen cli/sei ist sehr 
kurz im Vergleich zu der Zeit, die ein Zeichen bei 9600 Baud braucht, um 
vollständig im UART zu landen. Zweitens sind beim Atmega162 die UARTs 
für zwei Zeichen gepuffert.

Wenn du unbedingt ohne cli/sei arbeiten willst, kannst du das mit einer 
weiteren Hilfsvariable realisieren, die die ISR beim Schreibzugriff in 
main Däumchen drehen lässt.
1
volatile unsigned char main_ist_aktiv;
2

3
ISR...
4
{
5
   if (!main_ist_aktiv)
6
   {
7
      ucdata0 = UDR0;
8
      ucdata0_valid = 1;
9
   }
10
   else
11
   {
12
      // nix machen, der "gleiche" Interrupt 
13
      // kommt nach dem return gleich wieder
14
      // und hoffentlich hat main_ist_aktiv 
15
      // dann gewechselt
16
   }
17
}
18

19
int main(void)
20
{
21
   while(1)
22
   {
23
      if (ucdata0_valid) 
24
      {
25
         unsigned char c;
26
         main_ist_aktiv = 1;
27
         c = ucdata0;
28
         ucdata0_valid = 0;
29
         main_ist_aktiv = 1;
30
      }
31
   }
32
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren