www.mikrocontroller.net

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


Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...
#define F_CPU 8000000UL
#define BAUD 9600UL

void init_usart()
{
     uint16_t ubrr = (F_CPU / (BAUD * 8L) -1 );
 
     /* Set baud rate */
     UBRRH = (uint8_t) (ubrr>>8);
     UBRRL = (uint8_t) (ubrr);

     /*
     Enable receiver and transmitter; RXCIE aktiviert Interruptbetrieb
     zum Empfangen von Daten am
     */
     UCSRA = (1<<U2X);
     UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);

     /* Set frame format: 8data, 2stop bit */
     UCSRC = (1<<USBS)|(3<<UCSZ0)| (1<<URSEL);
 
}

int uart_putc( unsigned char data )
 {
     /* Wait for empty transmit buffer */
     while ( !( UCSRA & (1<<UDRE)) )
     ;
     /* Put data into buffer, sends the data */
     UDR = data;
     return 1;
 }
 
 void uart_puts (char *s)
 {
         while (*s)
         {
             uart_putc(*s);
             s++;
         }
 
}

Die Ausgabe erfolgt z.B. mit:
char s[7];
itoa(OCR1A,s,10);
uart_puts(s); // -> FUNKTIONIERT

// ABER

uart_puts("abc");
// ODER 
char abc[] = "abc";
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?!?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mein Tipp:

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

Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier ist der komplette Code. Uart wird nicht zwingend benoetigt, soll 
nur zum debuggen sein...
#define F_CPU 8000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>

#define BAUD 9600UL

#define OCR1_MIN 350
#define OCR1_MAX 1150

#define HORI_MITTE 750
#define VERTI_MITTE 750
#define HORI OCR1A
#define VERTI OCR1B

#define T_1MS 131

//Variablendeklaration
uint16_t ms = 0;
uint16_t us = 0;

struct Point{
    uint16_t x; //aka VERTI aka OCR1A
    uint16_t y; // aka HORI aka OCR1B
}lastPoint;

struct Point *p;

void init_servo(){

    // 16bit Timer1
    TCCR1A |= (1<<COM1A1) | (1<<COM1B1);
    TCCR1B |= (1<<WGM13) | (1<<CS11);
        
    ICR1 = 0x2710; // 10000 und entspricht 20ms bei 8MHz Takt
    TCNT1 = 0x00; //Timer startet bei 0

    // 8bit Timer0
    TCCR0 |= (1<<CS01) | (1<<CS00); // prescaler=256 -> macht 31,250kHz 
    
    
    /*
     *
     *  TIMSK hier deaktivieren um den Timer bei Bedarf fuer Movements zu
     *  starten (z.B. aus der move Funktion heraus)
     *
     */
    TIMSK |= (1<<TOIE0); // timer overflow interrupt auf enabled setzen
    TCNT0 = T_1MS; // bei jedem Overflow sind 250us vergangen -> macht 4000Hz
    DDRB = 0xff; //PortB auf Ausgang schalten

}

void init_usart()
{
    // alt: uint16_t ubrr = (uint16_t) ((uint32_t) ((F_CPU/(16*BAUD))-1));

    uint16_t ubrr = (F_CPU / (BAUD * 8L) -1 );

    /* Set baud rate */
    UBRRH = (uint8_t) (ubrr>>8);
    UBRRL = (uint8_t) (ubrr); 
    /*
    Enable receiver and transmitter; RXCIE aktiviert Interruptbetrieb
    zum Empfangen von Daten am
    */
    UCSRA = (1<<U2X);
    UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
    /* Set frame format: 8data, 2stop bit */
    UCSRC = (1<<USBS)|(3<<UCSZ0)| (1<<URSEL);

}

int uart_putc( unsigned char data )
{
    /* Wait for empty transmit buffer */
    while ( !( UCSRA & (1<<UDRE)) )
    ;
    /* Put data into buffer, sends the data */
    UDR = data;
    return 1;
}

void uart_puts (char *s)
{
        while (*s)
        { 
            uart_putc(*s);
            s++;
        }

}


ISR(TIMER0_OVF_vect) 
{
    ms++;
    if(ms == 1000) // eine Millisekunde vergangen
    {
        ms = 0;
    //    PORTB = ~PORTB;
    }


    TCNT0 = T_1MS;
}

void center(void) // speed ist ein wert von 0 (min) bis 7 (max)
{
    // alles auf Mittelstellung
    HORI = HORI_MITTE;
    VERTI = VERTI_MITTE;
    p->x = HORI;
    p->y = VERTI;
}

ISR(USART_RXC_vect)
{
    //hier den Code fuer Datenempfang einfuegen
    //UDR muss gelesen werden, da sonst der Interrupt immer wieder angerannt kommt :)
    char s[7];
    char tab[] = "\t";
    itoa(OCR1A,s,10);
    switch(UDR)
    {
        case 0x61: // 'a'
            if((OCR1A - 20) < OCR1_MIN)
                OCR1A = OCR1_MIN;
            else
                OCR1A -= 20;
            uart_puts(s);
            uart_puts(tab);
            break;
        case 0x64: // 'd' 
            if((OCR1A + 20) > OCR1_MAX)
                OCR1A = OCR1_MAX;
            else
                OCR1A += 20;
            break;
        case 0x77: // 'w'
            if((OCR1B - 20) < OCR1_MIN)
                OCR1B = OCR1_MIN;
            else
                OCR1B -= 20;
            break;
        case 0x73: // 's'
            if((OCR1B + 20) > OCR1_MAX)
                OCR1B = OCR1_MAX;
            else
                OCR1B += 20;
            break;
        case 'm':
                center();
            break;
        case 'o': // PB1 togglen laser an/aus
            if(PORTB == (1<<PB1))
                PORTB = ~(1<<PB1);
            else
                PORTB = (1<<PB1);
            break;
        default:
        break;
    }

}

int main( void )
{
    init_usart();
    init_servo();

    //enable global interrupts to enable the ISR
    sei();

    // Pointer auf aktuelle ServoWerte
    p = &lastPoint;
    
    //Servos auf Mittelstellung
    center(); 

    int16_t grad = 555;

    char res[10];
    itoa(grad,res,10);
    char abc[7] = "abc";

    while(1)
    {

        _delay_ms(1000);
        uart_puts(res); 
        uart_puts(abc);

    }

    return 0;
}


Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
int main()
{
  Initialisierung
  Grundzustand herstellen

  sei()
  while( 1 ) {
    und los gehts
  }
}

da gibts am wenigsten Überraschungen.

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

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

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

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

Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
PROJECT = uartmitpwm
#TARGET = attiny26
TARGET = atmega8
CC = avr-gcc
OBJCOPY = avr-objcopy
CFLAGS = -mmcu=$(TARGET) -Os -Wall $(DEBUG)
INCLUDES = -I.
SRC = $(PROJECT).c
OBJ = $(SRC:.c=.o)
UISP_OPTS = -dserial=/dev/tty.SLAB_USBtoUART -dprog=avr910 -dpart=M8

# "-c" means compile but don't link
.c.o:
  $(RM) $@
  $(CC) -c $(CFLAGS) $(INCLUDES) $*.c


# Targets...

all: $(OBJ)

$(PROJECT).S:
  $(CC) -S -c $(PROJECT).c

$(PROJECT).hex: $(PROJECT).elf
  $(OBJCOPY) -j .text -O ihex $(PROJECT).elf $(PROJECT).hex

$(PROJECT).bin: $(PROJECT).elf
  $(OBJCOPY) -j .text -O binary $(PROJECT).elf $(PROJECT).bin

$(PROJECT).elf: $(OBJ)
  $(CC) $(CFLAGS) -o $(PROJECT).elf -Wl,-Map,$(PROJECT).map $(OBJ)

simulate: $(PROJECT).bin
  simulavr -d atmega8 -P simulavr-disp $(PROJECT).bin

identify:
  uisp $(UISP_OPTS) --rd_fuses

erase:
  uisp $(UISP_OPTS) --erase

upload: $(PROJECT).hex
  avrdude -p $(TARGET) -P /dev/cu.SLAB_USBtoUART -c avr911  -v  -U flash:w:$(PROJECT).hex:i

clean:
  rm -f *.o *.elf *.map *.hex *.bin $(PROJECT)

Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

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

Ist aber genau das Problem:
$(PROJECT).hex: $(PROJECT).elf
  $(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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst schrieb:
> Karl heinz Buchegger schrieb:
>
>> (An die Sache mit der .data Section glaub ich nicht.
>
> Ist aber genau das Problem:
>
> $(PROJECT).hex: $(PROJECT).elf
>   $(OBJCOPY) -j .text -O ihex $(PROJECT).elf $(PROJECT).hex
> 
> 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 :-)

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.