Wenn du dir mal den Unterschied zwischen einer Deklaration und einer
Definition vergegenwärtigst, dann kannst du das auch bereits aus der
Fehlermeldung deuten.
Über die fehlende Deklaration beklagt sich bereits der Compiler,
denn er hat zu dem Zeitpunkt, da er main.c compiliert, keine Kennung
davon erhalten, was denn uart_string für ein Objekt sein soll. Falls
uart_string jedoch ordnungsgemäß deklariert wäre, am Ende aber in
keiner einzigen Objektdatei zu finden wäre, würde sich der Linker
über eine fehlende Definition dieses Symbols beschweren (Objekte und
Funktionen hält der Linker nur noch bedingt auseinander; für ihn sind
das alles nur noch Symbole, die er auflösen muss, d. h. denen er
letztlich einen Platz in einem Segment zuweist, um damit das Symbol
einer Adresse zuordnen zu können).