Forum: Compiler & IDEs UART: Daten zum AVR senden


von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo an alle

Ich möchte nun Daten vom PC zum AVR (Mega8) senden, um sie dort zu
verarbeiten. Ich will eine Zahl einlesen und damit rechenen. Als UArt
Lib wwill ich die Procyon Lib verwenden.

Das senden verstehe ich und funktioniert auch

Ich möchte am Terminal eine Zahl eingeben und nach einem ENTER, wird
die Zahl zum AVR geschickt.

Nur mit dem Senden komme ich nicht zurecht

Mit uartReceiveByte() lese ich die Daten aus dem Receive Buffer aus,
oder? Könnte mir da jemand das genauer erklären?

zahl1 = uartreceiveByte();

Nun stehet in der Variable zahl1 der String, der gesendet wurde, oder??

Danach muss ich die Zahl mit atoi() umwandeln, oder?
Das mit den Interupts verstehe ich auch nicht. Gibt es dazu kein
Tutorial?

Nun kann ich sie zum Rechnen weiterverwenden. Sehe ich das Richtig?
Könnte mir da jemand helfen??

Danke im Voraus

Gruß Robert

von Stefan (Gast)


Lesenswert?

Nö.

Obwohl deine Frage ziemlich komfus ist, versuche ich mal eine Antwort.

Also mit dem Senden und Empfangen schickst du bloss Bytes hin und her.
Was die Bytes darstellen, also Texte (Strings) oder Zahlen in
irgendwelchen Formaten (Ganzzaghlen, Gleitkommzahlen) musst du
entscheiden und im Empfänger auseinanderklamüstern.

Mit atol() bist du schon auf dem richtigen Weg. diese Funktion wandelt
einen Puffer aus Bytes in eine long Zahl. Du musst ggf. noch beachten,
ob der puffer eine abschliessende Null haben muss oder nicht.

Du musst dann die einzeln über uartreceiveByte() reinkommenden Bytes in
einen Puffer schreiben und den Puffer mit atol in einen Zahl umwandeln.

Üblicherweise legst du zuerst einen ausreichend grossen Pufferbereich
an. Für eine long Zahl mit 4 Bytes etwa so

unsigned char puffer[4];

Dann brauchst du eine Schleife, um die vier Bytes einzulesen.

{
   int i;
   for (i=0; i < 4; i++)
      puffer[i] =  uartreceiveByte();
}

Dann holst du dir die Zahl aus dem Puffer.

zahl = (<typ>) atol(puffer);

Das klappt natürlich nur, wenn der Sender die Bytes genauso auf die
Reise geschickt hat, dass atol was sinnvolles draus machen kann.
Stichwort an dieser Stelle ist die Endianess. Dazu Findest du was in
den Artikeln hier. Und wie <typ> ausdrücken soll, musst du dire klar
sein, ob du nur mit positiven oder mit positiven+negativen Zahlen
rechnen willst.

Wenn du weisst, dass z.B. positive long Ganzzahlen gesendet werden und
wenn die Endianess passt, kannst du die Zahl auch direkt
zusammensetzen. Im Beispiel wird angenommen, dass das höchstwertige
Byte zuerst gesendet wird

{
   int i;
   unsigned long zahl = 0;
   for (i=0; i < 4; i++)
   {
       zahl *= 256;
       zahl +=  uartreceiveByte();
   }
   ... hier kannst du mit zahl rechnen...
}

von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo Stefan

Danke, erstmal.

Ich hab nun einen Repeater geschrieben, der die Empfangen Zeichen am
LCD Display ausgeben soll:

#include <avr/io.h>
#include <stdlib.h>
#include "uart.h"
#include "lcd.h"

int main(void) {

  uartInit();
  uartSetBaudRate(19200);
  lcd_init(LCD_DISP_ON);

  while(1)
  {
    lcd_putc(uartGetByte());
  }
}

Jedoch bekomm ich immer eine Fehlermeldung beim Linken:

> "make.exe" all

-------- begin --------
avr-gcc (GCC) 3.4.3
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is
NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.


Linking: main.elf
avr-gcc -mmcu=atmega8 -I. -gstabs -DF_CPU=8000000UL  -Os
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall
-Wstrict-prototypes -Wa,-adhlns=main.o -If:/Programme/AVRlib -std=gnu99
-MD -MP -MF .dep/main.elf.d main.o lcd.o uart.o --output main.elf
-Wl,-Map=main.map,--cref    -lm
uart.o(.text+0x48): In function `uartInitBuffers':
uart.c:71: undefined reference to `bufferInit'
uart.o(.text+0x56):uart.c:73: undefined reference to `bufferInit'
uart.o(.text+0xd2): In function `uartReceiveByte':
uart.c:145: undefined reference to `bufferGetFromFront'
uart.o(.text+0x144): In function `uartAddToTxBuffer':
uart.c:187: undefined reference to `bufferAddToEnd'
uart.o(.text+0x154): In function `uartSendTxBuffer':
uart.c:196: undefined reference to `bufferGetFromFront'
uart.o(.text+0x194): In function `__vector_13':
uart.c:240: undefined reference to `bufferGetFromFront'
uart.o(.text+0x208): In function `__vector_11':
uart.c:277: undefined reference to `bufferAddToEnd'
make.exe: *** [main.elf] Error 1

> Process Exit Code: 2

Was ist da los?? Ich versteh es nicht.

Kann mein Repeater so funktionieren?

Danke im Voraus

Gruß Robert

von Stefan (Gast)


Lesenswert?

Was ist da los? Dir scheint im Projekt (makefile) eine Quelldatei oder
Library zu fehlen und der Linker kotzt. In der fehlenden Datei werden
die Symbole `bufferInit' etc. angelegt. Wo hast du das makefile her?
Selbstangelegt?

Ich würde eine Textsuche nach diesen Begriffen über die Quelldateien
machen und so herausfinden, wo diese Symbole definiert werden. Das
Lesen der Doku der verwendeten UART Bibliothek wäre ein anderer Weg.
Möglicherweise gibt es keine extra Datei dafür, sondern du musst diese
Symbole im eigenen Quelltext anlegen.

So funktioniert der "Repeater" wahrscheinlich nicht lang.
LCD-Ausgabefunktionen sind typisch ziemlich spartanisch und das
lcd_putc() wird nach 8, 16, 20, 40 ? Zeichen rechts am Anschlag
stehen.

Das direkte, ungefilterte Weitergeben der empfangenen Zeichen ist auch
nicht doll. Wenn sauber Ascii-Zeichen ankommen, klappt das. Wenn
Zeichen kommen, die Control-Zeichen fürs LCD sind, kann es knallen.

Besser wäre es, wenn du die empfangenen Zeichen z.B. in eine
Hex-Darstellung umformst. Also beim Empfang vom Byte 0xFF das umformen
in zwei Ascii-Zeichen 'F''F'. So ein paar pro LCD Zeile ausgeben,
dann die nächste Zeile, dann (bei zweizeiligem LCD) scrollen (Löschen,
neu mit veränderter Zeile ausgeben).

von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo Stefan

Hier einmal meine Makefile:

# Hey Emacs, this is a -*- makefile -*-
#----------------------------------------------------------------------- 
-----
# WinAVR Makefile Template written by Eric B. Weddington, Jörg Wunsch,
et al.
#
# Released to the Public Domain
#
# Additional material for this makefile was written by:
# Peter Fleury
# Tim Henigan
# Colin O'Flynn
# Reiner Patommel
# Markus Pfaff
# Sander Pool
# Frederik Rouleau
#
#----------------------------------------------------------------------- 
-----
# On command line:
#
# make all = Make software.
#
# make clean = Clean out built project files.
#
# make coff = Convert ELF to AVR COFF.
#
# make extcoff = Convert ELF to AVR Extended COFF.
#
# make program = Download the hex file to the device, using avrdude.
#                Please customize the avrdude settings below first!
#
# make debug = Start either simulavr or avarice as specified for
debugging,
#              with avr-gdb or avr-insight as the front end for
debugging.
#
# make filename.s = Just compile filename.c into the assembler code
only.
#
# make filename.i = Create a preprocessed source file for use in
submitting
#                   bug reports to the GCC project.
#
# To rebuild project do "make clean" then "make all".
#----------------------------------------------------------------------- 
-----


# MCU name
MCU = atmega8


# Processor frequency.
#     This will define a symbol, F_CPU, in all source code files equal
to the
#     processor frequency. You can then use this symbol in your source
code to
#     calculate timings. Do NOT tack on a 'UL' at the end, this will
be done
#     automatically to create a 32-bit value in your source code.
F_CPU = 8000000


# Output format. (can be srec, ihex, binary)
FORMAT = ihex


# Target file name (without extension).
TARGET = main


# List C source files here. (C dependencies are automatically
generated.)
SRC = $(TARGET).c
SRC += lcd.c
SRC += uart.c


# List Assembler source files here.
#     Make them always end in a capital .S.  Files ending in a
lowercase .s
#     will not be considered source files but generated files
(assembler
#     output from the compiler), and will be deleted upon "make
clean"!
#     Even though the DOS/Win* filesystem matches both .s and .S the
same,
#     it will preserve the spelling of the filenames, and gcc itself
does
#     care about how the name is spelled on its command-line.
ASRC =


# Optimization level, can be [0, 1, 2, 3, s].
#     0 = turn off optimization. s = optimize for size.
#     (Note: 3 is not always the best optimization level. See avr-libc
FAQ.)
OPT = s


# Debugging format.
#     Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.
#     AVR Studio 4.10 requires dwarf-2.
#     AVR [Extended] COFF format requires stabs, plus an avr-objcopy
run.
DEBUG = stabs


# List any extra directories to look for include files here.
#     Each directory must be seperated by a space.
#     Use forward slashes for directory separators.
#     For a directory that has spaces, enclose it in quotes.
EXTRAINCDIRS = f:/Programme/AVRlib


# Compiler flag to set the C Standard level.
#     c89   = "ANSI" C
#     gnu89 = c89 plus GCC extensions
#     c99   = ISO C99 standard (not yet fully implemented)
#     gnu99 = c99 plus GCC extensions
CSTANDARD = -std=gnu99


# Place -D or -U options here
CDEFS = -DF_CPU=$(F_CPU)UL


# Place -I options here
CINCS =



#---------------- Compiler Options ----------------
#  -g*:          generate debugging information
#  -O*:          optimization level
#  -f...:        tuning, see GCC manual and avr-libc documentation
#  -Wall...:     warning level
#  -Wa,...:      tell GCC to pass this to the assembler.
#    -adhlns...: create assembler listing
CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS) $(CINCS)
CFLAGS += -O$(OPT)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct
-fshort-enums
CFLAGS += -Wall -Wstrict-prototypes
CFLAGS += -Wa,-adhlns=$(<:.c=.lst)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)


#---------------- Assembler Options ----------------
#  -Wa,...:   tell GCC to pass this to the assembler.
#  -ahlms:    create listing
#  -gstabs:   have the assembler create line number information; note
that
#             for use in COFF files, additional information about
filenames
#             and function names needs to be present in the assembler
source
#             files -- see avr-libc docs [FIXME: not yet described
there]
#  -listing-cont-lines: Sets the maximum number of continuation lines
of hex
#       dump that will be displayed for a given single line of source
input.
ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs,--listing-cont-lines=100


#---------------- Library Options ----------------
# Minimalistic printf version
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min

# Floating point printf version (requires MATH_LIB = -lm below)
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt

# If this is left blank, then it will use the Standard printf version.
PRINTF_LIB =
#PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)


# Minimalistic scanf version
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min

# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt

# If this is left blank, then it will use the Standard scanf version.
SCANF_LIB =
#SCANF_LIB = $(SCANF_LIB_MIN)
#SCANF_LIB = $(SCANF_LIB_FLOAT)


MATH_LIB = -lm



#---------------- External Memory Options ----------------

# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# used for variables (.data/.bss) and heap (malloc()).
#EXTMEMOPTS =
-Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff

# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# only used for heap (malloc()).
#EXTMEMOPTS =
-Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff

EXTMEMOPTS =



#---------------- Linker Options ----------------
#  -Wl,...:     tell GCC to pass this to linker.
#    -Map:      create map file
#    --cref:    add cross reference to  map file
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)



#---------------- Programming Options (avrdude) ----------------

# Programming hardware: alf avr910 avrisp bascom bsd
# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500
#
# Type: avrdude -c ?
# to get a full listing.
#
AVRDUDE_PROGRAMMER = stk200

# com1 = lpt1. Use lpt1 to connect to parallel port.
AVRDUDE_PORT = lpt1
# programmer connected to serial device

AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep


# Uncomment the following if you want avrdude's erase cycle counter.
# Note that this counter needs to be initialized first using -Yn,
# see avrdude manual.
#AVRDUDE_ERASE_COUNTER = -y

# Uncomment the following if you do not wish a verification to be
# performed after programming the device.
#AVRDUDE_NO_VERIFY = -V

# Increase verbosity level.  Please use this when submitting bug
# reports about avrdude. See
<http://savannah.nongnu.org/projects/avrdude>;
# to submit bug reports.
#AVRDUDE_VERBOSE = -v -v

AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)



#---------------- Debugging Options ----------------

# For simulavr only - target MCU frequency.
DEBUG_MFREQ = $(F_CPU)

# Set the DEBUG_UI to either gdb or insight.
# DEBUG_UI = gdb
DEBUG_UI = insight

# Set the debugging back-end to either avarice, simulavr.
DEBUG_BACKEND = avarice
#DEBUG_BACKEND = simulavr

# GDB Init Filename.
GDBINIT_FILE = __avr_gdbinit

# When using avarice settings for the JTAG
JTAG_DEV = /dev/com1

# Debugging port used to communicate between GDB  avarice  simulavr.
DEBUG_PORT = 4242

# Debugging host used to communicate between GDB  avarice  simulavr,
normally
#     just set to localhost unless doing some sort of crazy debugging
when
#     avarice is running on a different computer.
DEBUG_HOST = localhost



#======================================================================= 
=====


# Define programs and commands.
SHELL = sh
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
AVRDUDE = avrdude
REMOVE = rm -f
COPY = cp
WINSHELL = cmd


# Define Messages
# English
MSG_ERRORS_NONE = Errors: none
MSG_BEGIN = -------- begin --------
MSG_END = --------  end  --------
MSG_SIZE_BEFORE = Size before:
MSG_SIZE_AFTER = Size after:
MSG_COFF = Converting to AVR COFF:
MSG_EXTENDED_COFF = Converting to AVR Extended COFF:
MSG_FLASH = Creating load file for Flash:
MSG_EEPROM = Creating load file for EEPROM:
MSG_EXTENDED_LISTING = Creating Extended Listing:
MSG_SYMBOL_TABLE = Creating Symbol Table:
MSG_LINKING = Linking:
MSG_COMPILING = Compiling:
MSG_ASSEMBLING = Assembling:
MSG_CLEANING = Cleaning project:




# Define all object files.
OBJ = $(SRC:.c=.o) $(ASRC:.S=.o)

# Define all listing files.
LST = $(SRC:.c=.lst) $(ASRC:.S=.lst)


# Compiler flags to generate dependency files.
GENDEPFLAGS = -MD -MP -MF .dep/$(@F).d


# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)





# Default target.
all: begin gccversion sizebefore build sizeafter end

build: elf hex eep lss sym extcoff

elf: $(TARGET).elf
hex: $(TARGET).hex
eep: $(TARGET).eep
lss: $(TARGET).lss
sym: $(TARGET).sym



# Eye candy.
# AVR Studio 3.x does not check make's exit code but relies on
# the following magic strings to be generated by the compile job.
begin:
  @echo
  @echo $(MSG_BEGIN)

end:
  @echo $(MSG_END)
  @echo


# Display size of file.
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
ELFSIZE = $(SIZE) $(TARGET).elf

sizebefore:
  @if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE);
$(ELFSIZE); \
  2>/dev/null; echo; fi

sizeafter:
  @if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER);
$(ELFSIZE); \
  2>/dev/null; echo; fi



# Display compiler version information.
gccversion :
  @$(CC) --version



# Program the device.
program: $(TARGET).hex $(TARGET).eep
  $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
$(AVRDUDE_WRITE_EEPROM)


# Generate avr-gdb config/init file which does the following:
#     define the reset signal, load the target file, connect to target,
and set
#     a breakpoint at main().
gdb-config:
  @$(REMOVE) $(GDBINIT_FILE)
  @echo define reset >> $(GDBINIT_FILE)
  @echo SIGNAL SIGHUP >> $(GDBINIT_FILE)
  @echo end >> $(GDBINIT_FILE)
  @echo file $(TARGET).elf >> $(GDBINIT_FILE)
  @echo target remote $(DEBUG_HOST):$(DEBUG_PORT)  >> $(GDBINIT_FILE)
ifeq ($(DEBUG_BACKEND),simulavr)
  @echo load  >> $(GDBINIT_FILE)
endif
  @echo break main >> $(GDBINIT_FILE)

debug: gdb-config $(TARGET).elf
ifeq ($(DEBUG_BACKEND), avarice)
  @echo Starting AVaRICE - Press enter when "waiting to connect"
message displays.
  @$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program
--file \
  $(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT)
  @$(WINSHELL) /c pause
else
  @$(WINSHELL) /c start simulavr --gdbserver --device $(MCU)
--clock-freq \
  $(DEBUG_MFREQ) --port $(DEBUG_PORT)
endif
  @$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE)



# Convert ELF to COFF for use in debugging / simulating in AVR Studio
or VMLAB.
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000


coff: $(TARGET).elf
  @echo
  @echo $(MSG_COFF) $(TARGET).cof
  $(COFFCONVERT) -O coff-avr $< $(TARGET).cof


extcoff: $(TARGET).elf
  @echo
  @echo $(MSG_EXTENDED_COFF) $(TARGET).cof
  $(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof



# Create final output files (.hex, .eep) from ELF output file.
%.hex: %.elf
  @echo
  @echo $(MSG_FLASH) $@
  $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@

%.eep: %.elf
  @echo
  @echo $(MSG_EEPROM) $@
  -$(OBJCOPY) -j .eeprom --set-section-flags .eeprom=alloc,load \
  --change-section-lma .eeprom=0 -O $(FORMAT) $< $@

# Create extended listing file from ELF output file.
%.lss: %.elf
  @echo
  @echo $(MSG_EXTENDED_LISTING) $@
  $(OBJDUMP) -h -S $< > $@

# Create a symbol table from ELF output file.
%.sym: %.elf
  @echo
  @echo $(MSG_SYMBOL_TABLE) $@
  $(NM) -n $< > $@



# Link: create ELF output file from object files.
.SECONDARY : $(TARGET).elf
.PRECIOUS : $(OBJ)
%.elf: $(OBJ)
  @echo
  @echo $(MSG_LINKING) $@
  $(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS)


# Compile: create object files from C source files.
%.o : %.c
  @echo
  @echo $(MSG_COMPILING) $<
  $(CC) -c $(ALL_CFLAGS) $< -o $@


# Compile: create assembler files from C source files.
%.s : %.c
  $(CC) -S $(ALL_CFLAGS) $< -o $@


# Assemble: create object files from assembler source files.
%.o : %.S
  @echo
  @echo $(MSG_ASSEMBLING) $<
  $(CC) -c $(ALL_ASFLAGS) $< -o $@

# Create preprocessed source for use in sending a bug report.
%.i : %.c
  $(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@


# Target: clean project.
clean: begin clean_list end

clean_list :
  @echo
  @echo $(MSG_CLEANING)
  $(REMOVE) $(TARGET).hex
  $(REMOVE) $(TARGET).eep
  $(REMOVE) $(TARGET).cof
  $(REMOVE) $(TARGET).elf
  $(REMOVE) $(TARGET).map
  $(REMOVE) $(TARGET).sym
  $(REMOVE) $(TARGET).lss
  $(REMOVE) $(OBJ)
  $(REMOVE) $(LST)
  $(REMOVE) $(SRC:.c=.s)
  $(REMOVE) $(SRC:.c=.d)
  $(REMOVE) .dep/*



# Include the dependency files.
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)


# Listing of phony targets.
.PHONY : all begin finish end sizebefore sizeafter gccversion \
build elf hex eep lss sym coff extcoff \
clean clean_list program debug gdb-config

Also soll ich das empfangene Zeichen mit atoh in einen Hex Wert
umwandeln. Wie kann ich den Wert dann in 2 Werte aufteilen??

Ich benutze ein 4 Zeiliges Display mit je 27 Zeichen (Pollin).

Den Zeilenumbruch kann ich so mahcen:
int main (void)
{
   uint8_t zeile = 0;
   uint8_t zeichen = 0;

while(1)
{
   lcd_puts(empfangenesZeichen);
   zeichen++;
   if(zeichen == 26)
      {
         lcd_puts("\n");
         zeichen = 0;
         zeile++;
       }
    if(zeile == 3)
       {
           lcd_clr();
           lcd_home();
           zeile = 0;
       }
}

Funktioniert das so?

Danke im Voraus

Gruß Robert

von Stefan (Gast)


Lesenswert?

Das Listing vom Riesenmakefile ist hier nicht wichtig und schreckt mich
vom Antworten ab. Wer findet da in dem Text noch die Frage? Dafür sind
Dateianhänge gedacht.

Schreib lieber woher du das makefile hast, ob du was daran geändert
hast und warum bzw. warum nicht.

Zurück zu den Grundlagen: In welcher Quelldatei werden die fehlenden
Symbole angelegt? Das muss irgendwo in der verwendeten Bibliothek
passieren (Suche im Quelltext) oder die verwendete Bibliothek verwendet
eine weitere Bibliothek (Suche in der Doku).

Ob die Zeilenumbrüche so funktionieren, hast du schneller ausprobiert,
also ich mich in deinen Code reinversetzen kann.

Setz vielleicht probeweise eine Schleife auf, die ein paar Zeichen ans
LCD rausgibt und schau ob dein Zeilenumbruchcode damit zurechtkommt.

Was meint die Doku der verwendeten LCD Bibliothek dazu? Hat die ein
Autoscrollen, dann könnte der Code von dir klappen. Wenn kein
Autoscrollen da ist, musst du mit irgendwelchen Positionsangaben
arbeiten.

Siehst du, wir diskutieren jetzt nicht mehr über das UART Empfangen
oder die Umwandlung eines Textes in eine Zahl oder über fehlende
Symbole sondern darüber wie man eine LCD Anzeige scrollt. Wo setzt du
die Prioritäten?

von Robert S. (razer) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo Stefan

Ich hab noch einmal die Makefile als Anhang anghängt. Ich kann den
Beitrag nicht ändern, oder?

Die Makefilevorlage hab ich aus dem I-net. Ich hab das ganze auf meinen
Prgrammer (STK200) angepasst. Weiters hab ich den Namen der main
angepasst, Taktfrequenz eingestellt und noch die 2 c Dateien (uart.c
und lcd.c) für das Linken angehängt.

Das LCD ist einmal Nebensache. Ich möchte einmal nur das die Daten beim
AVR ankommen.
Wann wird eine Zahl vom PC zum AVR gesendet?? Oder werden die Daten
sofort nach einer Eingabe gesendet und ich muss die einzelnen Daten
wieder zu einer Zahl zusammen setzten??

Dazu das Stichwort Endianess. Ich hab bei den Artiklen nichts gefunden
:(. Ich hab dann aber gegoogelt. Avr implementiert die Daten als littel
Endain, oder? Das heißt das niedrigste Byte wird auf der niedrigsten
Adresse abgelegt.

   uint8_t i;
   unsigned long zahl = 0;
   for (i=0; i < 4; i++)
   {
       zahl *= 256;
       zahl +=  uartreceiveByte();
   }

Bei deiner Routine wird die Zahl zusammen gesetzt. Die Zahl wird zuerst
mit 256 (1 Byte) multipliziert) damit dei Stellen passen, oder??

Wie funktioniert das gnaze aber dann mit Zahlen unter Null?

Danke im Voraus?

Gruß Robert

von Walter (Gast)


Lesenswert?

So wie ich das verstanden habe willst du eine Zahl als String an den AVR
schicken.
Dann geht aber das nicht mit

   for (i=0; i < 4; i++)
   {
       zahl *= 256;
       zahl +=  uartreceiveByte();
   }

sondern du musst die empfangenen Bytes in einen Puffer legen und den
dann per atol(puffer) in zahl schreiben.

Walter

von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo Walter

Achso. Als welchen Datentyp werden die Daten überhaupt über den UART
empfangen?? char?

uint8_t i;
unsigned char puffer[4];
uint32_t zahl;

for (i=0; i < 4; i++)
{
   puffer[i] = uartGetByte();
)
zahl = atol(puffer);

Kann das so funktionieren?
Wann wird den das ganze übertragen? Gleich nach der Eingabe?

Gruß Robert

von Walter (Gast)


Lesenswert?

ja, so geht's im Prinzip,
allerdings sollte puffer noch eins größer sein und in den letzten char
solltest du eine 0 schreiben damit atol auch zu einem richtigen Ende
findet.
Wie das ganze übertragen wird hängt von deinem Terminal bzw.
Sendeprogramm ab, normalerweise wird der String erst mit der
Return-Taste abgeschickt.
Geschickter Weise sollte das ganze aber auch mit unterschiedlichen
Stringlängen funktionieren, jetzt musst du ja immer 4-stellige Zahlen
eintippen

Grüße
Walter

von Stefan (Gast)


Lesenswert?

Robert sollte vielleicht festlegen/beschreiben, was er zu senden
gedenkt.

Wenn er auf dem PC ASCII-Zahlen eintippt und die direkt oder gepuffert
mit ENTER als Text verschickt, kommen am µC als ASCII Zeichen an. Die
kann er nach Zwischenpufferung auf dem µC mit atol() in eine Zahl
umwandeln.

Wenn er aus dem Sendeprogramm direkt eine Zahl/Variable als binäre
Bytes verschickt, kann er - unter Berücksichtigung der Endianess und
des Wertebereichs - die direkt zu einer Zahl zusammensetzen. Mein
Beispiel funktioniert sehr wohl, man muss halt vorher mehr überlegen,
was man überträgt.

Das Einfügen der beiden Dateien uart.c und lcd.c ins makefile war
anscheinend noch nicht ausreichend. Robert sollte die Doku der UART
Bibliothek genau lesen. Ich kann es nur zu dritten Mal schreiben. Es
kann eine zusätzliche Datei mit den Definitionen der fehlenden
Funktionen im makefile fehlen. Es kann ein Selbstschreiben von
Codeteilen im eigentlichen Programm notwendig sein... auch ein
fehlendes DEFINE ist möglich...

In der Doku wird auch stehen, was der Unterschied zwischen
uartreceiveByte() und uartGetByte() ist. Zu 100% steht dort auch, wann
ein Zeichen ankommt. Ganz besonders interessant ist, ob auf ein Zeichen
gewartet wird oder nicht.

Ob ein ENTER notwendig ist oder nicht, ist im wesentlichen Sache des
Sendeprogramms auf dem PC. Man kann ein Terminalprogramm benutzen,
welches nicht erst nach ENTER sendet (Bsp. Hyperterminal) oder eins,
bei dem man erst einen Sendepuffer beschreibt und dann mit ENTER oder
Send-Button losschickt (z.B. Br@y Terminal oder Hercules Terminal).

Robert, hast du einen Link auf die verwendete UART Bibliothek parat?
Zur Endianess kannst du dich bei
http://www.mikrocontroller.net/articles/Digitaltechnik informieren.

von Robert Schilling (Gast)


Lesenswert?

Hallo an alle!!

Ich möchte Erstmal nur ASCII Zeichen verschicken. Wie kann ich auf das
letzte Zeichen im char Array eine 0 schreiben?? Eventuell mit einer
Bitverschiebung nach links??

Als Terminal nehm ich den Hyperterminal von Windows. Wo ist der genaue
Unterschied zwischen beiden Funktionen zum Empfang von Daten?

Hier mal ein Auszug aus deer Bibliothek:
00193 //! Gets a single byte from the uart receive buffer.
00194 /// Returns the byte, or -1 if no byte is available
(getchar-style).
00195 int uartGetByte(void);
00196
00197 //! Gets a single byte from the uart receive buffer.
00198 /// Function returns TRUE if data was available, FALSE if not.
00199 /// Actual data is returned in variable pointed to by "data".
00200 /// Example usage:
00201 /// \code
00202 /// char myReceivedByte;
00203 /// uartReceiveByte( &myReceivedByte );
00204 /// \endcode
00205 u08 uartReceiveByte(u08* data);

Ich benütze wie oben geschrieben die Procyon Lib: Hallo an alle!!

Ich möchte Erstmal nur ASCII Zeichen verschicken. Wie kann ich auf das
letzte Zeichen im char Array eine 0 schreiben??

Als Terminal nehm ich den Hyperterminal von Windows.

Ich benütze die Procyon Lib:
http://hubbard.engr.scu.edu/embedded/avr/avrlib/docs/html/

Danke im Voraus

Gruß Robert

Danke im Voraus

Gruß Robert

von Stefan (Gast)


Lesenswert?

> letztes Zeichen 0

puffer[irgendeineposition] = 0;

Du kannst auch jedes Mal wenn du ein Zeichen in den Puffer schreibst,
das nächste Zeichen auf 0 setzen. Dann ist der Puffer stets schön
abgeschlossen.

puffer[i++] = nutzzeichen;
puffer[i] = 0;

> uartGetByte() und uartReceiveByte()

Beide Funktionen warten nicht auf ein Zeichen. Damit funktionieren alle
Beispiele oben nicht. Du musst immer abfragen, ob ein Zeichen empfangen
wurde, bevor du eine Auswertung bzw. Kopieroperation machst.

uartGetByte() liefert als Rückgabewert -1, wenn kein Zeichen empfangen
wurde bzw. 0 bis 255 für das empfangene Zeichen.

uartReceiveByte() liefert als Rückgabewert TRUE, wenn ein Zeichen
empfangen wurde und FALSE, wenn nicht. Wenn ein Zeichen empfangen
wurde, wird es an die Adresse kopiert, die im Funktionsargument
angegeben ist.

> Fehlende Dateien im Makefile

Mindestens buffer.c fehlt.

von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo Stefan

Danke, das mit der Erklärun des Arrys.. Super!!

Wenn ich uartReceiveByte() arbeite, muss ich warten bis ein Byte
empfangen ist oder??

Kann das so funktionieren:

if(uartReceiveByte(&testarray) == TRUE)
lcd_puts(testarray);

Danke im Voraus

Gruß Robert

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> if(uartReceiveByte(&testarray) == TRUE)

Ick.  Das sind Wahrheitswerte, die sollte man in einer
if-Anweisung dann auch direkt testen:

if (uartReceiveByte(&testarray))
  ...

von Stefan (Gast)


Lesenswert?

Wenn es echt ein test*array* ist ala

char testarray[ARRAYGROESSE];

geht das Beispiel schief. Du solltest in dem Fall die Adresse des
einzelnen Elements übergeben (i bspw. als Zähler für Arrayüberlauf
etc.) ala

if (uartReceiveByte(&testarray[i]))
   lcd_puts (testarray[i]);

von Robert S. (razer) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo an alle

So ich hab das ganze Jetzt einmal getstet. Es kommen auf jedenfall
Daten am µC an und werden auch über das LCD ausgegeben. Das Scrollen
nach meiner Methode funktioniert auch super. Leider werden aber nicht
die richtigen Zeichen dargestellt. Wenn ich zB die Taste "1" drücke
wird ein "b" oder ein "r" dargestellt. Das ist bei den meisten
Tasten so, dass zwei Zeichen dargestellt werden können

Was ist da los?

Am normalen Maschinschreibblock der Tastatur sind meisten nur
irgenwelche zeichen. Was ist da los??

Die Daten werden bei mir aber jetzt sofort nach dem Tastendruck
gesendet. Nicht nach einem Return. Wie kann ich das im Hyperterminal
änderen?? Oder ist es sinnvoller auf einen nicht Windows Terminal
auszuweichen??

Im Anhang hab ich derzeit meine Source File angehängt

Ich hoffe ihr könnt mir helfen

Danke im Voraus

Gruß Robert

von Stefan (Gast)


Lesenswert?

i ist beim Eintritt in die while(1) Schleife undefiniert. Wohin das
erste Zeichen gespeichert wird, ist wie Lotto. Irgendwo in einen 256
Zeichen grossen Bereich... Hoffentlich liegt da nichts wichtiges.

Also, wie bei zeichen und zeile gemacht, sauber initialisieren.

uint8_t i = 0;

Oder überlegen, was du programmieren willst. Denn das Array benutzt du
gar nicht. Du könntest stattdessen auch mit einer einfachen Variablen
arbeiten. Dann fällt auch i weg.

char meinchar;
...
if (uartReceiveByte(&meinchar))
  {
  lcd_putc(meinchar);
...

Wo stammt die LCD Library her? Kontrolliere, ob die Funktionsparameter
stimmen. Doku lesen.

Und die Procyon AVRlib hat auch LCD Funktionen mit dem entsprechenden
lcd.h - nicht dass du die falsche Includedatei einbindest und für die
verwendeten lcd-Funktionen die keine Prototypen hast.

Die Baudrate soll also 19200 sein. Das klappt nur, wenn F_CPU korrekt
definiert ist, weil die Library daraus die Einstellungen der UART
berechnet. Mit welcher Taktrate läuft dein µC und ist F_CPU mit den 8
MHz im makefile richtig definiert?

Und wie sind die anderen Daten? Wieviele Datenbits, welche Parity,
wieviele Stopbits? Ich finde in der Doku nichts dazu und nehme an es
wird das gängige 8n1 benutzt. Genau steht das in der Doku vom
verwendeten µC. Ist das auf der PC Gegenseite auch so eingestellt? Dran
denken, wenn man auf PC Seite die Übertragungsparameter ändert, die
Schnittstelle zu machen (Disconnect) und dann neu zu öffnen (Connect),
damit die Änderung auch von der Hardware übernommen wird.

Für das Senden mehrerer Bytes auf Tastendruck (ENTER) brauchst du ein
anderes Terminalprogramm als Hyperterminal.

von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo an alle

Es funktioniert nun so einigermaßen :-)
ICh hab ein anderes Terminalprogramm genommen(Bray) und auf einmal ging
alles. Ka warum??? Baudrate hab ich richtig eingestellt, denn das senden
hat funktioniert.

Dieser Terminal sendet jedoch auch gleich nach der Eingabe. Kennt
jemand einen Terminal, der nach einem Return sendet??

Nicht ASCII konforme Zeichen auf der Tastatur, werden natürlich nicht
richtig dargestellt aber das ist ja normal, oder??

Das sind aber jetzt immer ASCII Zeichen die gesendet werden. Wie geht
das nun genaau wenn ich das ganze als Zahl in eine Variable abspeichern
will.

char puffer[6] = '000000';
uint16_t zahl;
uint8_t i;

for(i=0; 1<6; i++)
{
   puffer[i] = 0;
   if(uartReceiveByte(&puffer[i++]
}
zahl = atol(puffer);

Kann das so funktionieren?? Ich weiß ich bin ein Noob...

Danke im Voraus

Gruß Robert

von Stefan (Gast)


Lesenswert?

Dein Puffer muss ein Element mehr Platz haben, als die Anzahl Ziffern,
die du reinspeichern willst. Den zusätzlichen Platz braucht das
abschliessende Nullbyte. Du kannst den Puffer so als Array von chars
anlegen:

char puffer[7] = { '0', '0', '0', '0', '0', '0', 0 };

oder so mit automatischen Anhängen der Null an einen String.

char puffer[7] = "000000";

Ich würde beides nicht machen.

uint16_t zahl;

uint16_t geht bis max. 65000 und ein paar Zerquetschte. D.h. nicht alle
sechsstelligen Zahlen passen hier rein. Genaugenommen passen sehr viel
mehr nicht rein als reinpassen.

uint8_t i;

for(i=0; 1<6; i++)
{
   puffer[i] = 0;
   if(uartReceiveByte(&puffer[i++]
}
zahl = atol(puffer);

ist Quatsch (1<6) und die Hälfte fehlt (Abschluss der if-Bedingung und
der ganze if-Anweisungsblock).

Die for-Schleife kannst du so nicht nehmen, weil die nach sechs Runden
ohne Empfang immer zu Ende ist. Daran denken uartReceiveByte() wartet
nicht auf Zeichen. Du musst solange Runden drehen, bis der Puffer voll
ist oder eine andere Eingabe als eine Ziffer kommt.

uint32_t zahl;
uint8_t i;

/* Es werden max. sechsstellige positive Zahlen übertragen */
i = 0;
puffer[i] = 0; /* Initialisierung vom Puffer */

do
{
   /* Zeichen da? */
   if (uartReceiveByte(&puffer[i]))
   {
      /* ja Zeichen ist da */

      /* Ist eine Ziffer reingekommen? */
      if (puffer[i] >= '0' && puffer[i] <= '9')
      {
         /* Ja war Ziffer */
         puffer[++i] = 0; /* Puffer hinter Eingabe abschliessen */
      }
      else
      {
         /* Nein war keine Ziffer, d.h. Ende der Zahleneingabe */
         /* Empfangenes Zeichen ausnullen + so Puffer abschliessen */
         puffer[i] = 0;
         break; /* do-while Schleife abbrechen */
      }
   }
} while (i < 6);

zahl = (uint32_t) atol(puffer);

von Robert S. (razer) Benutzerseite


Lesenswert?

Hey Stefan

Dein Code ist echt super. DANKE :-)
So ist es echt verständlich erklärt. Bei mir ist es so, dass ich am
meisten verstehe, wenn ich einen Code vor mir habe.

Ich werde das am NAchmittag mal in der Art testen freu

Danke Stefan

Gruß Robert

von Robert S. (razer) Benutzerseite


Lesenswert?

HAllo

Hab schon wieder eine Frage. Zahlen einlesen funktioniert. Mein
"Repeater" funktioniert auch.

Ich bin gerade am proggen eines kleinen Rechners. Jedoch komme ich bei
der Eingabe der Operation überhaupt nicht zusammen. Ic knoble schon
seit gestern am Nachmittag daran und finde noch immer keine Lösung :(
..
.
int getchar (void)
{
  char buffer;

  if(uartReceiveByte(&buffer));

  return(buffer);

}
.
.
.
char mainpuffer;
.
.
  do
  {
     mainpuffer = getchar();
  }
  while((mainpuffer != '+') || (mainpuffer != '*'));

Hier mal das Programm. uartReceiveByte() wartet ja nicht auf eine
Eingabe. Darum muss ich eine Warteschleife einbauen, oder?

Jedoch funktioniert das ganz so wich progge nicht. Was ist da los??

Das Zeichen in mainpuffer verwende ich danach in einer switch - case
Abfrage weiter

ICh hoffe, jemand kann mir helfen.

Danke im Voraus

Gruß Robert

von Stefan (Gast)


Lesenswert?

Warteschleife ist richtig, aber da sind mehrere Böcke drin.

Für den Fall dass nichts empfangen wurde, ist der Inhalt von buffer
nicht festgelegt zumal buffer ja auch nicht initialisiert wird. Es kann
im ungünstigsten Fall was sein, mit dem du an der Stelle gar nicht
gerechnet hast.

So wie du es jetzt programmiert hast, wird der Fall nicht abgefangen.
getchar() liefert einen Wert zwischen -127 und 127. Yes, negative
Zahlen, weil du buffer als char deklariert hast.

Und schau dir die Zeile mit dem if ganz genau an. Wo steht das ; und
was bedeutet das?

Du würdest dich beim Rückgabewert vom getchar() besser an
uartReceiveByte() orientieren. Also -1 zurüeckgeben, wenn nix da war
und 0 bis 255, wenn ein Zeichen da war.

Aber wenn ein fortgeschrittener Programmierer getchar() liest, erwartet
er die gleiche Funktion der gleichnamigen Funktion aus der C
Standardbibliothek. Du kannst ein eigenes getchar() schreiben, solltest
dich aber an der Standardfunktion bzw. was geht dort rein, was geht raus
orientieren. Wartet das normale getchar(), wenn ja, dann die eigene
Funktion auch wartend programmieren... Abgesehen davon, dass es bei
Namensgleichheit Fehlermeldungen hagelt, wenn du Includefiles der
Standardlibrary einbindest.

Kurz - ich würde meine Funktion eher my_getchar(), uart_getchar() oder
sonstwie nennen.

int uart_getchar(void)
{
   unsigned char buffer = '#';  // erleichtert das Debuggen ;-)

   if (uartReceiveByte(&buffer))
     return buffer;   // Zeichen 0x00..0xFF empfangen
   else
     return -1;       // kein Zeichen empfangen
}

Check nochmal die Bedingung in der do-while Schleife bzw. achte auf die
Warnings vom Compiler. Ist die Bedingung nicht immer wahr, auch wenn ein
+ oder ein * reinkommt?

Noch ein Tipp: Sei verschwenderisch mit Kommentaren. Viele Probleme
sieht man erst, wenn man sich und anderen umgangssprachlich formuliert,
was die Maschine machen soll. Du ahnst nicht, wieviel ich bei dem
Kommentieren wieder ändere. Und das Reindenken von Dritten ist bei
kommentiertem Code sowieso einfacher.

von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo Stefan

Danke wiedereinmal ;-)

Compilerwarnungen bekomm gar keine. Leider springt er aber nie aus der
Schleife heraus.

do
{
   mainpuffer = uart_getchar();
}
while((mainpuffer != '+') || (mainpuffer != '*'));

Warum ist das so??

Ich frage doch ab, ob in der Variable mainpuffer ein + ODER ein *
steht. Normal muss er ja wenn uart_getchar ein + oder * liefert aus der
Schleife aussteigen.

Danke om Voraus

Gruß Robert

von Werner B. (Gast)


Lesenswert?

while((mainpuffer != '+') || (mainpuffer != '*'));

ist ja auch IMMER erfüllt!

Wenn mainbuffer == '+' ist, dann ist es != '*', also die
abbruchbedingung NICHT erfüllt.
Und umgekehrt.

von Karl H. (kbuchegg)


Lesenswert?

> Ich frage doch ab, ob in der Variable mainpuffer ein + ODER ein *
> steht.

Nein das tust du eben nicht.

while((mainpuffer != '+') || (mainpuffer != '*'));

wird solange wiederholt, wie mainpuffer nicht gleich '+'
enthaelt oder mainpuffer nicht gleich '*' enthaelt.

Nun, wenn mainpuffer gleich einem '+' ist, dann ist es
immer noch ungleich einem '*'. Damit ist die Bedingung
aber wahr (da ODER), und auf gehts in die naechste Runde.

De-Morgan

   NICHT ( A ODER B )  <==>  ( NICHT A ) UND ( NICHT B )
   NICHT ( A UND B )   <==>  ( NICHT A ) ODER ( NICHT B )

d.h. du hats 2 Moeglichkeiten

   while( !( mainpuffer == '+' || mainpuffer == '*' ) );

das entspricht so ziemlich genau dem, was du umgangssprachlich
von dir gibst: Wiederhole solange, solange gilt, dass mainpuffer
nicht entweder '+' oder '*' enthaelt.

Oder du wendest De-Morgan an:

   while( mainpuffer != '+' && mainpuffer != '*' );

von Robert S. (razer) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo ihr beiden

Danke, wenn man es so sieht ist es ja logisch...

Da meine lcd_put_d() (Funktion zur Darstellung von ganzen Zahlen am
LCD) itoa() nutzt, kann ich damit nur maximal die Zahl 32767
darstellen.

Daher hab ich eine eigene Routine für uint32 Zahlen geschrieben:

int lcd_put_uint32(uint32_t data)
{
  char string[11];
  ultoa(data, string, 10);
  lcd_puts(string);
  return(0);
}

Mit der jetzigen Routine zum Einlesen der Zahlen kann ich ja maximal
6-stellige positive Zahlen einlesen (also 666666). Leider kann damit
maximal die Zahl 32767 (int16) einlesen.

Warum ist denn das so???

Zuerst hab ich gedacht, dass es meine Routine zum Anzeigen von 32 Bit
Variablen einen Fehler hat, aber das Ergebnis wird richtig angezeigt
(auch wenn es größer als 32767 ist).

Im Anhang hab ich meine main

Danke im Voraus

Gruß Robert

von Stefan (Gast)


Lesenswert?

Das passt nicht:

int getzahl (void)        // Rückgabe int -32768 bis 32767
{
  uint8_t i = 0;
  char puffer[7];
  uint32_t zahl;    // unsigned long 0 bis 4294967295

        ...

  zahl = (uint32_t) atol (puffer);
  return(zahl);     // Schnipp! Schnapp!
}

Spuckt der Compiler da kein Warning aus?

von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo Stefan

Compilerwarnungen hat es keine gegeben. Ich habs nun geändert in
uint32_t getzahl(); :-)

Danke Stefan

Gruß Robert

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.