Forum: Compiler & IDEs String ueber UART ausgeben geht nur teilweise


von Chris (Gast)


Lesenswert?

Hallo zusammen,
ich habe da ein merkwürdiges Verhalten, das ich mir nicht erklären kann. 
Wenn ich versuche einen String über den Uart des Atmega8 auszugeben sehe 
ich nur Fragezeichen auf dem Terminal. Wenn ich allerdings die funktion 
itoa() benutze werden die Zahlen korrekt angezeigt...
Ich habe schon sämtliche Baudraten zw. 9600 und 57600 ausprobiert. Der 
Atmega läuft mit einem externen 8MHz Quarz...
1
#define F_CPU 8000000UL
2
#define BAUD 9600UL
3
4
void init_usart()
5
{
6
     uint16_t ubrr = (F_CPU / (BAUD * 8L) -1 );
7
 
8
     /* Set baud rate */
9
     UBRRH = (uint8_t) (ubrr>>8);
10
     UBRRL = (uint8_t) (ubrr);
11
12
     /*
13
     Enable receiver and transmitter; RXCIE aktiviert Interruptbetrieb
14
     zum Empfangen von Daten am
15
     */
16
     UCSRA = (1<<U2X);
17
     UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
18
19
     /* Set frame format: 8data, 2stop bit */
20
     UCSRC = (1<<USBS)|(3<<UCSZ0)| (1<<URSEL);
21
 
22
}
23
24
int uart_putc( unsigned char data )
25
 {
26
     /* Wait for empty transmit buffer */
27
     while ( !( UCSRA & (1<<UDRE)) )
28
     ;
29
     /* Put data into buffer, sends the data */
30
     UDR = data;
31
     return 1;
32
 }
33
 
34
 void uart_puts (char *s)
35
 {
36
         while (*s)
37
         {
38
             uart_putc(*s);
39
             s++;
40
         }
41
 
42
}

Die Ausgabe erfolgt z.B. mit:
1
char s[7];
2
itoa(OCR1A,s,10);
3
uart_puts(s); // -> FUNKTIONIERT
4
5
// ABER
6
7
uart_puts("abc");
8
// ODER 
9
char abc[] = "abc";
10
uart_puts(abc); // FUNKTIONIERT NICHT

Woran kann das liegen?

PS: Ich muss noch dazusagen, das der Atmega über den MySmartUSB progger 
mit Spannung via USB Port versorgt wird. Kann es sein dass die Spannung 
nicht stabil genug ist?!?

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:

> uart_puts("abc");
> // ODER
> char abc[] = "abc";
> uart_puts(abc); // FUNKTIONIERT NICHT
>
> Woran kann das liegen?

Das sollte iegentlich kein Problem sein.
Zeig mal mehr Code aus der Umgebung.
Idealerweise den kompletten Code.

> PS: Ich muss noch dazusagen, das der Atmega über den MySmartUSB progger
> mit Spannung via USB Port versorgt wird. Kann es sein dass die Spannung
> nicht stabil genug ist?!?

Eher unwahrscheinlich.

von Stefan E. (sternst)


Lesenswert?

Mein Tipp:

Fehler bei der Hex-File-Erstellung (.data Section nicht hinzugefügt).

von Chris (Gast)


Lesenswert?

Hier ist der komplette Code. Uart wird nicht zwingend benoetigt, soll 
nur zum debuggen sein...
1
#define F_CPU 8000000UL
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
#include <stdlib.h>
7
8
#define BAUD 9600UL
9
10
#define OCR1_MIN 350
11
#define OCR1_MAX 1150
12
13
#define HORI_MITTE 750
14
#define VERTI_MITTE 750
15
#define HORI OCR1A
16
#define VERTI OCR1B
17
18
#define T_1MS 131
19
20
//Variablendeklaration
21
uint16_t ms = 0;
22
uint16_t us = 0;
23
24
struct Point{
25
    uint16_t x; //aka VERTI aka OCR1A
26
    uint16_t y; // aka HORI aka OCR1B
27
}lastPoint;
28
29
struct Point *p;
30
31
void init_servo(){
32
33
    // 16bit Timer1
34
    TCCR1A |= (1<<COM1A1) | (1<<COM1B1);
35
    TCCR1B |= (1<<WGM13) | (1<<CS11);
36
        
37
    ICR1 = 0x2710; // 10000 und entspricht 20ms bei 8MHz Takt
38
    TCNT1 = 0x00; //Timer startet bei 0
39
40
    // 8bit Timer0
41
    TCCR0 |= (1<<CS01) | (1<<CS00); // prescaler=256 -> macht 31,250kHz 
42
    
43
    
44
    /*
45
     *
46
     *  TIMSK hier deaktivieren um den Timer bei Bedarf fuer Movements zu
47
     *  starten (z.B. aus der move Funktion heraus)
48
     *
49
     */
50
    TIMSK |= (1<<TOIE0); // timer overflow interrupt auf enabled setzen
51
    TCNT0 = T_1MS; // bei jedem Overflow sind 250us vergangen -> macht 4000Hz
52
    DDRB = 0xff; //PortB auf Ausgang schalten
53
54
}
55
56
void init_usart()
57
{
58
    // alt: uint16_t ubrr = (uint16_t) ((uint32_t) ((F_CPU/(16*BAUD))-1));
59
60
    uint16_t ubrr = (F_CPU / (BAUD * 8L) -1 );
61
62
    /* Set baud rate */
63
    UBRRH = (uint8_t) (ubrr>>8);
64
    UBRRL = (uint8_t) (ubrr); 
65
    /*
66
    Enable receiver and transmitter; RXCIE aktiviert Interruptbetrieb
67
    zum Empfangen von Daten am
68
    */
69
    UCSRA = (1<<U2X);
70
    UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
71
    /* Set frame format: 8data, 2stop bit */
72
    UCSRC = (1<<USBS)|(3<<UCSZ0)| (1<<URSEL);
73
74
}
75
76
int uart_putc( unsigned char data )
77
{
78
    /* Wait for empty transmit buffer */
79
    while ( !( UCSRA & (1<<UDRE)) )
80
    ;
81
    /* Put data into buffer, sends the data */
82
    UDR = data;
83
    return 1;
84
}
85
86
void uart_puts (char *s)
87
{
88
        while (*s)
89
        { 
90
            uart_putc(*s);
91
            s++;
92
        }
93
94
}
95
96
97
ISR(TIMER0_OVF_vect) 
98
{
99
    ms++;
100
    if(ms == 1000) // eine Millisekunde vergangen
101
    {
102
        ms = 0;
103
    //    PORTB = ~PORTB;
104
    }
105
106
107
    TCNT0 = T_1MS;
108
}
109
110
void center(void) // speed ist ein wert von 0 (min) bis 7 (max)
111
{
112
    // alles auf Mittelstellung
113
    HORI = HORI_MITTE;
114
    VERTI = VERTI_MITTE;
115
    p->x = HORI;
116
    p->y = VERTI;
117
}
118
119
ISR(USART_RXC_vect)
120
{
121
    //hier den Code fuer Datenempfang einfuegen
122
    //UDR muss gelesen werden, da sonst der Interrupt immer wieder angerannt kommt :)
123
    char s[7];
124
    char tab[] = "\t";
125
    itoa(OCR1A,s,10);
126
    switch(UDR)
127
    {
128
        case 0x61: // 'a'
129
            if((OCR1A - 20) < OCR1_MIN)
130
                OCR1A = OCR1_MIN;
131
            else
132
                OCR1A -= 20;
133
            uart_puts(s);
134
            uart_puts(tab);
135
            break;
136
        case 0x64: // 'd' 
137
            if((OCR1A + 20) > OCR1_MAX)
138
                OCR1A = OCR1_MAX;
139
            else
140
                OCR1A += 20;
141
            break;
142
        case 0x77: // 'w'
143
            if((OCR1B - 20) < OCR1_MIN)
144
                OCR1B = OCR1_MIN;
145
            else
146
                OCR1B -= 20;
147
            break;
148
        case 0x73: // 's'
149
            if((OCR1B + 20) > OCR1_MAX)
150
                OCR1B = OCR1_MAX;
151
            else
152
                OCR1B += 20;
153
            break;
154
        case 'm':
155
                center();
156
            break;
157
        case 'o': // PB1 togglen laser an/aus
158
            if(PORTB == (1<<PB1))
159
                PORTB = ~(1<<PB1);
160
            else
161
                PORTB = (1<<PB1);
162
            break;
163
        default:
164
        break;
165
    }
166
167
}
168
169
int main( void )
170
{
171
    init_usart();
172
    init_servo();
173
174
    //enable global interrupts to enable the ISR
175
    sei();
176
177
    // Pointer auf aktuelle ServoWerte
178
    p = &lastPoint;
179
    
180
    //Servos auf Mittelstellung
181
    center(); 
182
183
    int16_t grad = 555;
184
185
    char res[10];
186
    itoa(grad,res,10);
187
    char abc[7] = "abc";
188
189
    while(1)
190
    {
191
192
        _delay_ms(1000);
193
        uart_puts(res); 
194
        uart_puts(abc);
195
196
    }
197
198
    return 0;
199
}

von Chris (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Fehler bei der Hex-File-Erstellung (.data Section nicht hinzugefügt).

Und woran erkenne ich das, bzw. wie stelle ich das ab?

von Stefan E. (sternst)


Lesenswert?

Chris schrieb:
> Stefan Ernst schrieb:
>> Fehler bei der Hex-File-Erstellung (.data Section nicht hinzugefügt).
>
> Und woran erkenne ich das, bzw. wie stelle ich das ab?

Wie erstellst du denn das Hex-File?

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:

Hmm.
Meine ursprüngliche Vermutung war, dass du irgendwo ein Array 
niederbügelst und wild in den Speicher schreibst und dabei zufällig den 
String erwischt hast.

Das einzige in dieser Richtung könnte hier stecken:

> int main( void )
> {
>     init_usart();
>     init_servo();
>
>     //enable global interrupts to enable the ISR
>     sei();
>
>     // Pointer auf aktuelle ServoWerte
>     p = &lastPoint;

Interrupts sind schon freigschaltet.
Wenn die USART vor deiser Zuweisung an p bereits einen Empfangsinterrupt 
gefeuert hat, besteht die Chance, dass p benutzt wird noch ehe es einen 
Wert erhalten hat.

Ich gebe zu, dass ist nicht besonders wahrscheinlich.
Trothdem sollte man es korrigieren:

Den sei() macht man immer erst, wenn ausnahmslos alles initialisiert 
ist. Sonst verliert man schnell den Überblick, ob man schon darf oder 
nicht. Am besten kommt der sei() als letzter Funktionsaufruf vor die 
Hauptschleife
1
int main()
2
{
3
  Initialisierung
4
  Grundzustand herstellen
5
6
  sei()
7
  while( 1 ) {
8
    und los gehts
9
  }
10
}

da gibts am wenigsten Überraschungen.

Aber abgesehen davon ist mir jetzt nichts aufgefallen, was den Effekt 
erklären könnte.

Noch Manöverkritik
1
    switch(UDR)
2
    {
3
        case 0x61: // 'a'

schreibs doch gleich so
1
    switch(UDR)
2
    {
3
        case 'a':

dann brauchts keinen Kommentar und man sieht auch im Code, was 
angestrebt wird.

von Chris (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Wie erstellst du denn das Hex-File?

Muss ich da noch ein Flag an den gcc uebergeben?
Hier ist mein Makefile:
1
PROJECT = uartmitpwm
2
#TARGET = attiny26
3
TARGET = atmega8
4
CC = avr-gcc
5
OBJCOPY = avr-objcopy
6
CFLAGS = -mmcu=$(TARGET) -Os -Wall $(DEBUG)
7
INCLUDES = -I.
8
SRC = $(PROJECT).c
9
OBJ = $(SRC:.c=.o)
10
UISP_OPTS = -dserial=/dev/tty.SLAB_USBtoUART -dprog=avr910 -dpart=M8
11
12
# "-c" means compile but don't link
13
.c.o:
14
  $(RM) $@
15
  $(CC) -c $(CFLAGS) $(INCLUDES) $*.c
16
17
18
# Targets...
19
20
all: $(OBJ)
21
22
$(PROJECT).S:
23
  $(CC) -S -c $(PROJECT).c
24
25
$(PROJECT).hex: $(PROJECT).elf
26
  $(OBJCOPY) -j .text -O ihex $(PROJECT).elf $(PROJECT).hex
27
28
$(PROJECT).bin: $(PROJECT).elf
29
  $(OBJCOPY) -j .text -O binary $(PROJECT).elf $(PROJECT).bin
30
31
$(PROJECT).elf: $(OBJ)
32
  $(CC) $(CFLAGS) -o $(PROJECT).elf -Wl,-Map,$(PROJECT).map $(OBJ)
33
34
simulate: $(PROJECT).bin
35
  simulavr -d atmega8 -P simulavr-disp $(PROJECT).bin
36
37
identify:
38
  uisp $(UISP_OPTS) --rd_fuses
39
40
erase:
41
  uisp $(UISP_OPTS) --erase
42
43
upload: $(PROJECT).hex
44
  avrdude -p $(TARGET) -P /dev/cu.SLAB_USBtoUART -c avr911  -v  -U flash:w:$(PROJECT).hex:i
45
46
clean:
47
  rm -f *.o *.elf *.map *.hex *.bin $(PROJECT)

von Chris (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Den sei() macht man immer erst, wenn ausnahmslos alles initialisiert
> ist. Sonst verliert man schnell den Überblick, ob man schon darf oder
> nicht. Am besten kommt der sei() als letzter Funktionsaufruf vor die
> Hauptschleife

Danke fuer deine Tipps. Ich werd sie mir in Zukunft zu Herzen nehmen.
Steh' ja noch ganz am Anfang der AVR Programmierung, da ist man noch 
lernfaehig :) Aber ich muss sagen, es macht Spass.

Gruss

von Karl H. (kbuchegg)


Lesenswert?

Chris schrieb:
> Karl heinz Buchegger schrieb:
>> Den sei() macht man immer erst, wenn ausnahmslos alles initialisiert
>> ist. Sonst verliert man schnell den Überblick, ob man schon darf oder
>> nicht. Am besten kommt der sei() als letzter Funktionsaufruf vor die
>> Hauptschleife
>
> Danke fuer deine Tipps. Ich werd sie mir in Zukunft zu Herzen nehmen.
> Steh' ja noch ganz am Anfang der AVR Programmierung, da ist man noch
> lernfaehig :) Aber ich muss sagen, es macht Spass.

Schade, dass ich dir da nicht mehr helfen kann.
Das Verhalten ist seltsam. All die üblichen Verdächtigen greifen nicht.

(An die Sache mit der .data Section glaub ich nicht. Da müsstest du 
schon wissen, mit welchen Compiler/Linker Optionen man das umstellt. 
Ausser Jörg und noch ein paar anderen kennt die aber kaum wer. Und 
ausserdem müsstest du dann nicht nachfragen :-)

von Stefan E. (sternst)


Lesenswert?

Karl heinz Buchegger schrieb:

> (An die Sache mit der .data Section glaub ich nicht.

Ist aber genau das Problem:
1
$(PROJECT).hex: $(PROJECT).elf
2
  $(OBJCOPY) -j .text -O ihex $(PROJECT).elf $(PROJECT).hex
Nur .text landet im Hex-File, nicht auch .data.

@ Chriss:
Füge noch ein "-j .data" hinzu.
Oder noch besser: benutze MFile oder zumindest sein Makefile-Template 
als Vorlage.

von Karl H. (kbuchegg)


Lesenswert?

Stefan Ernst schrieb:
> Karl heinz Buchegger schrieb:
>
>> (An die Sache mit der .data Section glaub ich nicht.
>
> Ist aber genau das Problem:
>
1
> $(PROJECT).hex: $(PROJECT).elf
2
>   $(OBJCOPY) -j .text -O ihex $(PROJECT).elf $(PROJECT).hex
3
>
> Nur .text landet im Hex-File, nicht auch .data.

I stand corrected :-)

Wer macht denn auch sowas!
Makefiles kopiert man doch einfach von einer vertrauenswürdigen Quelle 
und ändert tunlichst nur die Filenamen :-)

von Hc Z. (mizch)


Lesenswert?

Nein.  In der .text-section ist ganz hinten ein Spiegel der data 
section.  Die wird beim C-Start umkopiert.

Eine getrennte .data gibt es im fertigen Code nicht, wo sollte die auch 
landen außer im Flash?

von Stefan E. (sternst)


Lesenswert?

Hc Zimmerer schrieb:
> Nein.  In der .text-section ist ganz hinten ein Spiegel der data
> section.  Die wird beim C-Start umkopiert.

Nein, dieser "Spiegel" ist nicht in der .text-Section enthalten. Er wird 
erst "erzeugt", indem man die .data-Section dem Hex-File hinzufügt, und 
dadurch hinter .text mit ins Flash programmiert wird.

von Karl H. (kbuchegg)


Lesenswert?

Hab mir gerade ein vom AVR-Studio erzeugtest Makefile angesehen.
Da wird das so erzeugt

HEX_FLASH_FLAGS = -R .eeprom

%.hex: $(TARGET)
  avr-objcopy -O ihex $(HEX_FLASH_FLAGS)  $< $@


Also: Alle sections mit Ausnahme der .eeprom

von Chris (Gast)


Lesenswert?

Keine Ahnung wo ich das Makefile her hatte. Zumindest funktioniert es 
jetzt, obwohl ich noch nicht wirklich verstanden habe warum.
Vielleicht sollte ich mir mal ein Howto bzgl. Makefiles durchlesen...
Danke nochmals fuer eure Hinweise...
Gruss
Chris

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.