Attiny412 - String über USART

Gast #6304378
Lesenswert?

Hallo zusammen

Auf einem Attiny412 versuche ich einen string / ein char-array zu 
printen. Allerdings funktioniert das nicht, und ich weiss nicht wieso.

Mein Code ist:
1
#define F_CPU 16000000UL
2
#define USART0_BAUD_RATE(BAUD_RATE)                                            \
3
  ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)
4

5
#include <avr/io.h>
6
#include <string.h>
7
#include <util/delay.h>
8

9
static void USART0_sendChar(char c) {
10
  while (!(USART0.STATUS & USART_DREIF_bm)) {
11
    __asm__("NOP");
12
  }
13
  USART0.TXDATAL = c;
14
}
15

16
static void USART0_init(void) {
17
  PORTA.DIR |= PIN6_bm;
18
  USART0.BAUD = (uint16_t)USART0_BAUD_RATE(115200);
19
  USART0.CTRLB |= USART_TXEN_bm;
20
}
21

22
int main(void) {
23
  CCP = 0xD8;
24
  CLKCTRL.MCLKCTRLB &= ~(1 << 0); // disable clock prescaler
25

26
  USART0_init();
27

28
  PORTA.DIR |= PIN2_bm; // LED
29
  while (1) {
30
    PORTA.OUT |= PIN2_bm; // enable LED
31
    _delay_ms(1000);
32
    char hello[] = "Hi\r\n";
33

34
    for (size_t i = 0; i < 4; i++) {
35
      USART0_sendChar(hello[i]);
36
    }
37
    USART0_sendChar('t');
38
    USART0_sendChar(hello[0]);
39
    USART0_sendChar(hello[1]);
40
    USART0_sendChar(hello[2]);
41
    USART0_sendChar(hello[3]);
42

43
    PORTA.OUT &= ~PIN2_bm; // disable LED
44
    _delay_ms(1000);
45
  }
46
  return 1;
47
}

Der Output im Terminal ist dann:
1
$ cat /dev/ttyUSB0 | xxd -c 9 -g 1
2
00000000: 00 00 00 00 74 48 69 0d 0a  ....tHi..
3
00000009: 00 00 00 00 74 48 69 0d 0a  ....tHi..
4
00000012: 00 00 00 00 74 48 69 0d 0a  ....tHi..

Wie ihr seht, wird das Array in der Schleife nur als "\0\0\0" geprinted. 
das "t" habe ich nur als delimiter mitgeschickt, damit man sieht ab wo 
die Schleife fertig ist.

Kompiliert habe ich mit
1
../toolchain/avr8-gnu-toolchain-linux_x86_64/bin/avr-gcc -Wall -g -Os -mmcu=attiny412 -I ./attiny_dfp/include/ -B ./attiny_dfp/gcc/dev/attiny412 -o main.bin main.c

Und die verwendete Version ist
1
../toolchain/avr8-gnu-toolchain-linux_x86_64/bin/avr-gcc -v
2
Using built-in specs.
3
Reading specs from ../toolchain/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/5.4.0/device-specs/specs-avr2
4
COLLECT_GCC=../toolchain/avr8-gnu-toolchain-linux_x86_64/bin/avr-gcc
5
COLLECT_LTO_WRAPPER=../toolchain/avr8-gnu-toolchain-linux_x86_64/bin/../libexec/gcc/avr/5.4.0/lto-wrapper
6
Target: avr
7
Configured with: /home/toolsbuild/workspace/avr8-gnu-toolchain/src/gcc/configure LDFLAGS=-L/home/toolsbuild/workspace/avr8-gnu-toolchain/avr8-gnu-toolchain-linux_x86_64-hostlibs/lib CPPFLAGS= --target=avr --host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --prefix=/home/toolsbuild/workspace/avr8-gnu-toolchain/avr8-gnu-toolchain-linux_x86_64 --libdir=/home/toolsbuild/workspace/avr8-gnu-toolchain/avr8-gnu-toolchain-linux_x86_64/lib --enable-languages=c,c++ --with-dwarf2 --enable-doc --disable-shared --disable-libada --disable-libssp --disable-nls --with-avrlibc=yes --with-mpfr=/home/toolsbuild/workspace/avr8-gnu-toolchain/avr8-gnu-toolchain-linux_x86_64-hostlibs --with-gmp=/home/toolsbuild/workspace/avr8-gnu-toolchain/avr8-gnu-toolchain-linux_x86_64-hostlibs --with-mpc=/home/toolsbuild/workspace/avr8-gnu-toolchain/avr8-gnu-toolchain-linux_x86_64-hostlibs --with-pkgversion=AVR_8_bit_GNU_Toolchain_3.6.2_1759 --with-bugurl=http://www.atmel.com
8
Thread model: single
9
gcc version 5.4.0 (AVR_8_bit_GNU_Toolchain_3.6.2_1759)

Angehängt findet ihr noch das binary sowie ein lst file vom kompilierten 
C-Code.

Besten Dank für eure Hilfe!
Angehängte Dateien:
Gast #6304457
Lesenswert?

S. Landolt schrieb:
> Wenn ich auch "mal ins Blaue tippen" darf (als Assemblerprogrammierer):
> da nur der Aufruf in der Schleife nicht funktioniert, vermute ich ein
> Problem mit C, vielleicht dieses 'size_t'.

Dachte auch an ein Compiler Problem. Das 'size_t' hatte ich auch bereits 
ausgetauscht durch int und int16_t - was alles nichts genützt hat. Wenn 
ich 'i' über UART ausgebe mit 'USART0_sendChar((char) i + 48)' wird 
schön 0, 1, 2, 3 ausgegeben...
Gast #6304619
Lesenswert?

Neuling schrieb:

> Angehängt findet ihr noch das binary sowie ein lst file vom kompilierten
> C-Code.

Ich hab' mir nur das lst-File angeschaut. Mal abgesehen davon, dass der 
Optimizer da eine ziemlich ineffiziente Arbeit abliefert, ist der 
generierte Code ziemlich sicher sachlich korrekt.

Sprich: Das sollte eigentlich funktionieren. Keine Ahnung, warum's nicht 
tut.

Grob zur Erklärung, was da (in dem nicht funktionierenden Bereich) 
passiert:

Es wird auf dem Stack Platz für 5 Bytes geschaffen (4 Chars+NUL, also 
OK), dann wird die Stringkonstante aus dem Flash dorthin kopiert, 
allerdings über das SRAM-Mapping des Flash. Dann wird sie wieder 
zeichenweise vom Stack geholt und an die (offensichtlich ja 
funktionierende) Ausgaberoutine verfüttert.

Der Stack wurde zu Programmbegin auch korrekt initialisiert, zeigt also 
in's SRAM, wie es sich gehört.

Die einzige Unsicherheit besteht IMHO darin, ob die Stringkonstante 
tatsächlich an der erwarteten Stelle im SRAM-Mapping des Flash liegt. 
Von anderen XMega-Erben sind ja bereits Bugs genau bei dieser Sache 
bekannt...

Ein schöner Test wäre es, zwischen diesen beiden Zeilen

    char hello[] = "Hi\r\n";

    for (size_t i = 0; i < 4; i++) {

mal folgendes Stück Code einzufügen:

    hello[0]='O';
    hello[1]='o';
    hello[2]='p';
    hello[3]='s';

und sich dann die UART-Ausgabe anzuschauen.

Kommt das Oops, ist Mikrochip schuld.
Gast #6304630
Lesenswert?

Die Schleife ist korrekt und sollte so funktionieren.  Tut sie ja auch, 
sie sendet 4 Bytes, nur nicht die gewünschten.  Meine Vermutung: die 
Strings werden nicht korrekt ins RAM kopiert (falscher Startupcode?). 
Die folgenden Einzelausgaben (hello[0] etc) werden vom Compiler 
optimiert und benutzen den String gar nicht.
Gast #6304634
Lesenswert?

c-hater schrieb:
> Ein schöner Test wäre es, zwischen diesen beiden Zeilen
>
>     char hello[] = "Hi\r\n";
>
>     for (size_t i = 0; i < 4; i++) {
>
> mal folgendes Stück Code einzufügen:
>
>     hello[0]='O';
>     hello[1]='o';
>     hello[2]='p';
>     hello[3]='s';
>
> und sich dann die UART-Ausgabe anzuschauen.
>
> Kommt das Oops, ist Mikrochip schuld.

Tatsächlich, der Output ist dann:
1
$ cat /dev/ttyUSB0 | xxd -c 9 -g 1
2
00000000: 4f 6f 70 73 74 4f 6f 70 73  OopstOops
3
00000009: 4f 6f 70 73 74 4f 6f 70 73  OopstOops
4
00000012: 4f 6f 70 73 74 4f 6f 70 73  OopstOops
5
0000001b: 4f 6f 70 73 74 4f 6f 70 73  OopstOops

Was heisst das jetzt, respektive was genau läuft falsch?
Gast #6304646
Lesenswert?

c-hater schrieb:
> Das heißt, das du einen Hardware-Bug im Tiny412 gefunden hast. Der
> allerdings in ähnlicher Form bereits vom AVR128DAxx bekannt ist...

Na toll, hast du eine Idee wie der umgangen werden kann? Ich meine, das 
trifft dann ja auf alle Array's zu - nicht nur auf diesen String oder?
Gast #6304662
Lesenswert?

foobar schrieb:

> Das ist so früh aber eine mutige Behauptung - gibt noch genügend andere
> Möglichkeiten, z.B. unpassender Startupcode.

Der Startupcode ist im lst-File enthalten und macht alles richtig, 
nämlich rein garnix bezüglich des Mapping. Das Map-Fenster im 
SRAM-Bereich ist groß genug, dass es den gesamten Flash eines Tiny412 
abbilden kann. Es gibt also keinen Grund, irgendetwas am default Mapping 
zu ändern. Und übrigens auch keine Möglichkeit dafür (das ist der 
einzige Unterschied zur Sachlage bei den AVR128DAxx)...
Gast #6304672
Lesenswert?

c-hater schrieb:
> foobar schrieb:
>
>> Das ist so früh aber eine mutige Behauptung - gibt noch genügend andere
>> Möglichkeiten, z.B. unpassender Startupcode.
>
> Der Startupcode ist im lst-File enthalten und macht alles richtig,
> nämlich rein garnix bezüglich des Mapping. Das Map-Fenster im
> SRAM-Bereich ist groß genug, dass es den gesamten Flash eines Tiny412
> abbilden kann. Es gibt also keinen Grund, irgendetwas am default Mapping
> zu ändern. Und übrigens auch keine Möglichkeit dafür (das ist der
> einzige Unterschied zur Sachlage bei den AVR128DAxx)...

Hast du eine Idee, wie ich mit dem Fehler umgehen kann? Da ich mehrere 
attiny412 habe wäre es schade sie wegzuschmeissen. Wäre eine Möglichkeit 
immer wenn ich ein Array brauche, es mit
1
int arr[10];
2
for(int i=0; i<10; i++)
3
 arr[i] = 0;
zu initialisieren? Respektive überall wo möglich 'const int arr[] = { 
... };' zu verwenden?
Gast #6304685
Lesenswert?

Neuling schrieb:

> Na toll, hast du eine Idee wie der umgangen werden kann? Ich meine, das
> trifft dann ja auf alle Array's zu - nicht nur auf diesen String oder?

Frage1:
Ja, aber die wird dir nicht gefallen: Programmiere Assembler, denn 
kontrollierst du selber, wie du auf den Flash zugreifst...

Frage2:
Ja. Wird alle konstanten Daten betreffen, sofern der Compiler die 
Zugriffe nicht durch Rückgriff auf andere Codevarianten wegoptimiert, 
wie es ja bereits in deinem Originalcode passiert ist, bei dem zweiten 
Teil der Aufgabe hat er den Zugriff auf das bereits aufwendig im SRAM 
erzeugte Array durch ein paar immediate-loads ersetzt...

In dem geänderten Code konnte er das nicht mehr tun, weil wir an dem 
Array rumgepfuscht haben, deswegen gibt's auch im zweiten Teil der 
Ausgabe jetzt ein Oops...
Gast #6304686
Lesenswert?

>> Das ist so früh aber eine mutige Behauptung - gibt noch genügend andere
>> Möglichkeiten, z.B. unpassender Startupcode.
>
> Der Startupcode ist im lst-File enthalten und macht alles richtig,
> nämlich rein garnix bezüglich des Mapping.

Oh, sorry - hatte in das .lst gar nicht reingeschaut.  Sieht tatsächlich 
korrekt aus.  War mir noch einfällt, wäre, dass die .rodata-Section 
nicht nach 0x11e ins FLASH kopiert wurde (dann wäre aber FFs 
wahrscheinlicher als die 00s).  Das .hex könnte da Aufschluß geben. 
Wenn das auch korrekt ist, wird's eng ;-)
Gast #6304742
Lesenswert?

Hier das mit dem aktuellen 'Atmel Studio 7' kompilierte Programm des 
TOs.
Der String "Hi\r\n" - der im obigen Hex-File fehlt - ist im Datensatz 
mit dem Sternchen.
1
:1000000019C020C01FC01EC01DC01CC01BC01AC00C
2
:1000100019C018C017C016C015C014C013C012C034
3
:1000200011C010C00FC00EC00DC00CC00BC00AC064
4
:1000300009C008C011241FBECFEFCDBFDFE3DEBF74
5
:100040000FD072C0DDCF9091040895FD06C0E0E0AE
6
:10005000F8E00000948195FFFCCF8093020808959A
7
:10006000CF93DF93CDB7DEB72597CDBFDEBF88ED49
8
:1000700084BFE0E6F0E081818E7F8183E0E0F4E000
9
:10008000808180648083A0E0B8E08CE292E01896E2
10
:100090008D939C93199716968C911697806416965B
11
:1000A0008C93808184608083C12C6894DD24D2F895
12
:1000B0007E0125E0E20EF11CF601848184608483D8
13
:1000C000FFEF23ED80E3F15020408040E1F700C0D6
14
:1000D000000085E0ECE2F1E8DE01119601900D925E
15
:1000E0008A95E1F78E010F5F1F4FF80181918F0113
16
:1000F000AADF0E151F05C9F784E7A5DF88E4A3DF93
17
:1001000089E6A1DF8DE09FDF8AE09DDFF601848133
18
:100110008B7F8483FFEF23ED80E3F150204080400C
19
:0C012000E1F700C00000C8CFF894FFCF4A
20
:05012C0048690D0A0006*
21
:00000001FF
Gast #6304756
Lesenswert?

Mit AS7 passt das schon ...
1
readelf -S attiny412_pintChar.elf
2
    There are 13 section headers, starting at offset 0x3a20:
3
    Section Headers:
4
      [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
5
      [ 0]                   NULL            00000000 000000 000000 00      0   0  0
6
      [ 1] .data             PROGBITS        00803f00 00017b 000000 00  WA  0   0  1
7
      [ 2] .text             PROGBITS        00000000 000074 000102 00  AX  0   0  2
8
      [ 3] .rodata           PROGBITS        00008102 000176 000005 01 AMS  0   0  1
9
      [ 4] .comment          PROGBITS        00000000 00017b 000030 01  MS  0   0  1
10
      [ 5] .note.gnu.avr.dev NOTE            00000000 0001ac 00003c 00      0   0  4
11
      [ 6] .debug_info       PROGBITS        00000000 0001e8 001410 00      0   0  1
12
      [ 7] .debug_abbrev     PROGBITS        00000000 0015f8 0012fa 00      0   0  1
13
      [ 8] .debug_line       PROGBITS        00000000 0028f2 00001d 00      0   0  1
14
      [ 9] .debug_str        PROGBITS        00000000 00290f 00089e 00      0   0  1
15
      [10] .shstrtab         STRTAB          00000000 00399c 000082 00      0   0  1
16
      [11] .symtab           SYMTAB          00000000 0031b0 0004b0 10     12  22  4
17
      [12] .strtab           STRTAB          00000000 003660 00033c 00      0   0  1
18
    Key to Flags:
19
      W (write), A (alloc), X (execute), M (merge), S (strings)
20
      I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
21
      O (extra OS processing required) o (OS specific), p (processor specific)
Gast #6304779
Lesenswert?

In seinem objcopy zum Erstellen des hex-Files fehlt wohl einfach nur das 
"-j .rodata":
1
# avr-objcopy -j .text -j .rodata -j .data -O ihex /tmp/main.bin /dev/stdout 
2
:1000000019C020C01FC01EC01DC01CC01BC01AC00C
3
:1000100019C018C017C016C015C014C013C012C034
4
:1000200011C010C00FC00EC00DC00CC00BC00AC064
5
:1000300009C008C011241FBECFEFCDBFDFE3DEBF74
6
:100040000BD06BC0DDCF9091040895FD02C000007D
7
:10005000FACF809302080895CF93DF93CDB7DEB730
8
:100060002597CDBFDEBF88ED84BF809161008E7F74
9
:1000700080936100809100048064809300048CE28E
10
:1000800092E08093080890930908809106088064A4
11
:1000900080930608809100048460809300047E01B0
12
:1000A00025E0E20EF11C8091040484608093040436
13
:1000B0008FEF93EDE0E381509040E040E1F700C026
14
:1000C000000085E0EEE1F1E8DE01119601900D926D
15
:1000D0008A95E1F78E010F5F1F4FF80181918F0123
16
:1000E000B2DF0E151F05C9F784E7ADDF88E4ABDF8B
17
:1000F00089E6A9DF8DE0A7DF8AE0A5DF809104040F
18
:100100008B7F80930404FFEF23ED80E3F1502040C8
19
:0E0110008040E1F700C00000C6CFF894FFCF9A
20
:05011E0048690D0A0014
21
:00000001FF
Ohne das "-j .rodata" kommt sein oben gepostetes fehlerhaftes .hex raus.
Gast #6304803
Lesenswert?

Neuling schrieb:

> foobar schrieb:
>> In seinem objcopy zum Erstellen des hex-Files fehlt wohl einfach nur das
>> "-j .rodata":
>
> Ja, das wars :D Danke vielmals für eure super Hilfe :)

OK, also doch kein Mikrochip-Bug, sondern nur eine Instanz von: Mit 
Hochsprachen-Compilern (und Linkern) kann man massenhaft Probleme lösen, 
die man ohne sie überhaupt nicht hätte...
Gast #6304807
Lesenswert?

c-hater schrieb:
> OK, also doch kein Mikrochip-Bug, sondern nur eine Instanz von: Mit
> Hochsprachen-Compilern (und Linkern) kann man massenhaft Probleme lösen,
> die man ohne sie überhaupt nicht hätte...

Ja, das ist absolut so. Allerdings vereinfachen sie - sobald die 
Environment stimmt - vieles. C mag ich dabei aber auch nicht besonders, 
da es einfach ist mühsam zu findende Fehler einzubauen. Da hab ich 
lieber Rust (ist aber für AVR nicht wirklich geeignet da LLVM fehlt).

Und die Frage wäre schnell beantwortet gewesen - hätte ich das ganze 
Makefile gepostet...
Gast #6304868
Lesenswert?

Neuling schrieb:

> Und die Frage wäre schnell beantwortet gewesen - hätte ich das ganze
> Makefile gepostet...

Da fällt mir noch was ein: der Linker hätte doch eigentlich furchtbar 
meckern müssen, ohne diese Sektion gibt es doch eine nicht auflösbare 
Referenz, oder?

Also mir scheint, das sich das Gewicht verlagert, das Thema aber noch 
nicht wirklich durch ist...
Gast #6304914
Lesenswert?

Neuling schrieb:
> c-hater schrieb:
>> Da fällt mir noch was ein: der Linker hätte doch eigentlich furchtbar
>> meckern müssen, ohne diese Sektion gibt es doch eine nicht auflösbare
>> Referenz, oder?
>
> Naja, gelinkt wurde ja direkt beim kompilieren, und Meldungen/Warnungen
> gabs da keine...

Eben. Das genau ist das Problem, es hätte welche geben MÜSSEN...

Es kann ja schließlich nicht sein, dass irgendwas refenziert wird, was 
im Binary überhaupt nicht mehr enthalten ist.

Diese Situation muß ganz klar einen Fehler werfen. Wäre das passiert, 
würde möglicherweise dieser ganze Thread überhaupt nicht existieren...
Gast #6304985
Lesenswert?

c-hater schrieb:
> Es kann ja schließlich nicht sein, dass irgendwas refenziert wird, was
> im Binary überhaupt nicht mehr enthalten ist.

Im binary ist es ja enthalten, nur im hex nicht (die verwendeten 
optionen -j von avr-objcopy kopieren nur die angegebenen sections vom 
bin ins hex).

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