Moin Leute,
Ich hab da gerade ein kleines Problem mit meiner Testumgebung
fuer einen CH32V003.
Mein System ist im Prinzip ein Eigenbau der zu 90%
hierauf basiert:
https://github.com/cnlohr/ch32v003fun
Ich hab z.b ein kleines Testprogramm das eine LED an einem Pin
blinken laesst und es funktioniert.
Ich linke nun ein weiteres Testprogramm hinzu OHNE das ich daraus
irgendwas benutzte! Sofort haengt sich mein Testprogramm weg.
Grund dafuer ist folgendes:
Alleine die deklaration dieses Systick_Handler sorgt dafuer
das mein Programm nicht laeuft. Der Grund wird auch klar wenn man
ins mapfile schaut:
Ohne Systick:
Der Linker fummelt mir also wohl die Interrupttabelle kaputt.
So sieht die uebersetztung aus:
make
riscv-none-embed-gcc --specs=nosys.specs -g -Os -flto
-ffunction-sections -static-libgcc -march=rv32ec -mabi=ilp32e -nostdlib
-I./riscv_core/extralibs -I./riscv_core -I./riscv_core/inc
-I./riscv_core/attic -T ./riscv_core/inc/ch32v003fun.ld
-Wl,-Map=mapfile.txt -lc -lm -Wl,--gc-sections -L./riscv_core/misc
-lgcc riscv_core/attic/startup_ch32v003.c timer.c main.c -o
template.elf
riscv-none-embed-objcopy -O ihex template.elf template.hex
riscv-none-embed-objcopy -O binary template.elf template.bin
Ich vermute mal das ich da noch irgendeine Option vergessen habe?
Oder woran kann das sonst liegen?
Der Handler selbst wird im StartUp-Code weak definiert:
Vanye R. schrieb:> Ich linke nun ein weiteres Testprogramm hinzu
Was soll das werden?
Zwei Programme auf diese Art zu kombinieren kann nicht funktionieren,
egal wie du es dir vorgestellt hast.
Vanye R. schrieb:> Ich linke nun ein weiteres Testprogramm hinzu
Zeige bitte den vollständigen Quelltext.
Was ist in timer.c drin, was ist in main.c drin?
Vanye R. schrieb:> Mein System ist im Prinzip ein Eigenbau der zu 90%> hierauf basiert:
Und worin unterscheiden sich die restlichen 10%?
> Zeige bitte den vollständigen Quelltext.
Koennte ich tun, aber zu lang. Ausserdem habt ihr meine Frage
nicht verstanden.
Darum noch mal ein Versuch.
1
SRCS=./riscv_core/attic/startup_ch32v003.c
2
#SRCS +=timer.c
3
SRCS+=main.c
Das obige ist ein Ausschnitt aus dem Makefile welches zu einem
funktionierendem Programm fuehrt, nehme ich timer.c mit ins
Programm so funktioniert das Programm nicht. Ich entferne
also nur "#" und mache ein make clean;make
Mit anderen Worten es wird KEINE einzige Programmzeile aus
dieser Datei genutzt. Sie ist dann einfach nur vorhanden,
nichts daraus wird aufgerufen.
Lediglich die Codegenerierung vom Linker aendert sich.
Also die Position oder Eintraege von Interrupttabellen.
Aber es wird NICHTS davon genutzt! Kein Eintrag wird
aufgerufen, nichts ist aktiviert.
Und mein eigener Code laeuft auch garnicht erst! Selbst
ein Initalisieren eines GPIO und high setzen in der
ersten Zeile funktioniert dann nicht mehr. main
wird also nicht mehr erreicht.
Vanye
Neben Compiler und Linker braucht es für einen funktionierende toolchain
auch noch startup code und vor allem ein linkerscript, das dem linker
sagt, wie das alles zusammenzubauen ist.
In linkerscript findest du alle Antworten zu deiner Frage.
Oliver
Vanye R. schrieb:> Ausserdem habt ihr meine Frage nicht verstanden.
Du hast Dich nun auch sehr merkwürdig ausgedrückt:
Vanye R. schrieb:> Ich linke nun ein weiteres Testprogramm hinzu OHNE das ich daraus> irgendwas benutzte!Vanye R. schrieb:> Aber es wird NICHTS davon genutzt!
Du scheinst einen Interrupthandler angelegt zu haben. Was soll den der
Linker damit Deiner Ansicht nach machen? Ob Du die nötige Peripherie
initialisierst, die den Interrupt schlussendlich auslöst, das kann der
Linker nicht wissen. Also muss er den Interrupthandler wie einen
Interrupthandler behandeln.
Und dabei kommt er wohl aus den von Oliver beschriebenen Gründen
durcheinander.
Worin bestehen denn die 10% Unterschied zum Code von cnlohr? Vielleicht
liegen die in genau diesem Linkerskript und dem Drumherum?
Vanye R. schrieb:> Der Linker fummelt mir also wohl die Interrupttabelle kaputt.
Die RISC-V Toolchain ist mir fremd, aber ich nehme an, was da passiert
ist analog zu ARM.
Der Startup-Code enthält weak Stubs für die Interrupt-Service Routinen.
Sobald Du gleichnamige "echte" Routinen mit gleichem Namen dazulinkst,
werden die aktiv.
Das ist nicht "Kaputtfummeln", sondern ein Dienst am Programmierer.
Markus F. schrieb:> Sobald Du gleichnamige "echte" Routinen mit gleichem Namen dazulinkst,> werden die aktiv.
Dann müsste allerdings die Reihenfolge der Einträge für vector_handler
und systick_handler andersherum sein, sofern man davon ausgehen kann,
daß die beiden zu Beginn gezeigten Tabellen in ihrer Reihenfolge
aufsteigend nach Vektor sortiert sind.
"vector_handler" sollte daher in beiden Fällen an der gleichen Stelle
stehen, und das tuts nicht.
Da kann ich vanyes Irritation nachvollziehen.
> Der Startup-Code enthält weak Stubs für die Interrupt-Service Routinen.> Sobald Du gleichnamige "echte" Routinen mit gleichem Namen dazulinkst,> werden die aktiv.
DAs weiss ich. Ich habs ja im Ausgangspost selber zitiert. Hier die
Datei aus der ich zitiert habe und die 1:1 bei mir verwendet wird:
https://github.com/cnlohr/ch32v003fun/blob/master/attic/ch32v003evt/startup_ch32v003.c
Und im uebrigen sollte da nichts aktiv werden. Der Linker sollte die
Adresse meines Codes dann anstelle des weak-defaults einsetzen. Das
passiert nicht korrekt, aber selbst das sollte noch egal sein weil man
den Interrupt dann ja auch noch aktivieren muss. Das mache ich aber
nicht wie ich schon zweimal betont habe.
Aber kann sein das ich da was verwechselt habe (Tabelle und Position der
Firmware). Ich muss mir das am Wochenende nochmal genauer anschauen.
Vanye
Vanye R. schrieb:> Das> passiert nicht korrekt, aber selbst das sollte noch egal sein weil man> den Interrupt dann ja auch noch aktivieren muss. Das mache ich aber> nicht wie ich schon zweimal betont habe.
Der linker analysiert keine Programme, um herauszufinden, ob du da
irgendwo einen Interrupt aktivierst oder nicht. Selbst der Compiler
macht und kann das nicht.
Oliver
> Selbst der Compiler macht und kann das nicht.
Eben, deshalb ja mein hinweis das mein Programm uebersetzt wird
wenn timer.c includiert wird aber auch wenn nicht! Da sind also schon
Funktionen drin, genauer gesagt die Initialisierung fuer den
Timer und die Interruptfunktion. Aber die werden eben nicht
aufgerufen/genutzt! Sonst wuerde der Linker ja sofort mit einem Fehler
aussteigen sobald ich die Funktion entferne.
> im Startup Code raus und lege einfach ein array an die Stelle
Ja, ich fand das auch etwas komisch und kenne das sonst etwas anders,
allerdings ist der Source ja nicht von mir und scheint in anderen
Projekten zu funktionieren.
Vanye
Vanye R. schrieb:> Da sind also schon> Funktionen drin, genauer gesagt die Initialisierung fuer den> Timer und die Interruptfunktion. Aber die werden eben nicht> aufgerufen/genutzt!
Ja, aber das ist völlig irrelevant. Du hast einen Interrupthandler
definiert, der wird in die Interrupttabelle eingetragen, egal, ob Du
irgendeine Initialisierung aufrufst oder nicht.
> Ja, aber das ist völlig irrelevant. Du hast einen Interrupthandler> definiert, der wird in die Interrupttabelle eingetragen, egal, ob Du> irgendeine Initialisierung aufrufst oder nicht.
Dessen bin ich mir bewusst, aber das ist ja irrelevant solange der
Interrupt nicht eingeschaltet wird, was nicht der Fall ist.
Wenn ich keine Interruptfunktion im Programm habe wird ja folgendes
in der Tabelle eingetragen:
void DefaultIRQHandler( void )
{
// Infinite Loop
asm volatile( "1: j 1b" );
}
Damit funktioniert mein Programm weil auch das natuerlich niemals
aufgerufen wird. Es ist ja schliesslich nicht aktiviert.
Vanye
Daraus schliesse ich das der SystTick_Handler erstmal nicht aufgerufen
wird, denn dann wuerde es ja sofort in obiger Endlosschleife enden.
Zustimmung?
In meiner main schalte ich dann eine LED ein und aus und hab dazwischen
eine brutale Wartefunktion die einfach Zyklen verschwendet. Ich sehe
dann die LED wie erwartet blinken. Alles laeuft!
Jetzt binde ich meine timer.c im Makefile ein und sorge dafuer das in
timer.c keine Funktion befindet die SysTick_Handler heisst. Alles
laeuft, led blinkt.
Jetzt schreibe ich da folgendes rein:
1
voidSysTick_Handler(void)
2
{
3
}
Alles laeuft. LED blinkt. Warum auch nicht. Sollte ja nicht aufgerufen
werden oder? Allerdings wenn doch, hat das ja keine Auswirkungen.
Okay, jetzt folgendes
1
staticvolatileuint32_tmsTicks;
2
3
voidSysTick_Handler(void)
4
{
5
// msTicks++;
6
7
volatileinti;
8
asmvolatile("nop");
9
i++;
10
11
// SysTick->SR = 0;
12
}
Meine LED blinkt, alles geht!
Dann kommentiere ich die msTicks Zeile aus und das Programm haengt sich
weg!
Es gibt also zwei Merkwuerdigkeiten:
1. Wieso wird Systick_Handler ueberhaubt aufgerufen?
2. Wenn schon, wieso ist gerade der zugriff auf msTicks so giftig?
Ich seh schon kommen das ich RiscV Assembler lernen muss, seufz!
Vanye
Vanye R. schrieb:> void SysTick_Handler(void) __attribute__((interrupt));
wozu ist das Attribut interrupt? Stimmen die damit nicht überein und
wird dann das weak DefaultHandler nicht überschrieben?
Beim Default Handler wird das auch nicht gemacht.
Edit:
Mr. Lohr schreibt das interrupt crucial wäre, aber beim Default Handler
ist die ISR naked. Doku zu Attribut interrupt sagt das da noch ein
Argument hingehört.
Bzw. bei RISC-V ist default machine.
https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Function-Attributes.html
ok, naked ist beim DefaultHandler valid weil das nur eine Endlos asm
Schleife ist.
Vanye R. schrieb:> Ja, ich fand das auch etwas komisch und kenne das sonst etwas anders,> allerdings ist der Source ja nicht von mir und scheint in anderen> Projekten zu funktionieren.
Mein Fehler, hab erst jetzt gesehen dass besagtes Vektorarray im Startup
Code angelegt ist.
Vielleicht gibt es einen Hardfault? Du kannst ja mal probieren deine LED
im
So, ich hab jetzt laufen. Ich hab mir sogar bereits ueber den Systick
meine delay funktion ans laufen bekommen.
Die Loesung bestand daran an den CFLAGS noch ein...
-fdata-sections
...anzuhaengen. Aber ehrlich gesagt warum es damit laeuft ist
mir noch ein raetzel. Aber es scheint zu laufen.
Vanye
Noch ein kleiner Tip fuer den Einsteiger. Wer bei den Teilen
einen Portleitung auf eine alternative Funktion umlegt,
der muss den Takt dafuer einschalten.
RCC->APB2PCENR |= RCC_AFIOEN;
Ich glaube das hat mich bestimmt eine Stunde gekostet
da man irgendwie nicht erwartet das dafuer auch ein Takt
gebraucht wird!
Vanye