Hi Leute, ich versuche gerade ein kleines Programm funktiontauglich in den Bootloaderbereich zu bekommen. Es scheint aber dass ich irgendwie auf dem Schlauch stehe. Ich benutze einen Atmega16 inc. AVR Studio Folgendes Programm funktioniert ab Adresse 0x0000 so wie es soll. PortA wechselt nach jedem empfangenen Byte über die UART seinen Zustand. ------------------------------------------------------------------------ - #include <avr/io.h> #include <avr/portpins.h> #include <avr/interrupt.h> volatile int debug = 0; int main(void) { DDRA = 0xFF; SREG |= (1<<7); UCSRB |= (1<<TXEN) | (1<<RXEN); UCSRB |= (1<<RXCIE); UBRRL= 26; //Baud = 19200 while(1) { if (debug%2) PORTA = 0x00; else PORTA = 0xFF; } return 0; } //**************************************** ISR ( USART_RXC_vect ) { while (!(UCSRA & (1<<RXC))); unsigned char dummy = UDR; debug++; } ------------------------------------------------------------------------ - Ok, nun will ich das Programm samt Vektortabelle verschieben. Dafür habe ich das Linker Script um die Option -Ttext=0x3800 erweitert. Wenn ich das Hex File auslese, steht der Code auch an der richtigen Stelle im Flash und nicht mehr an Adresse 0x0000. Um die Vektoren mit zu verschieben habe ich den Code um die Zeilen GICR = (1<<IVCE); GICR = (1<<IVSEL); direkt unter "int main(void) {" erweitert In den Fuses Häckchen bei BOOTRST BootSZ = Boot Flash Size=1024 words start address=$1C00 Hoffe jemand kann mir einen Hinweis geben. MFG Mike
Mike schrieb: > Wenn ich das Hex File auslese, steht der Code auch an der richtigen > Stelle im Flash und nicht mehr an Adresse 0x0000. Steht auch die ISR, bzw der Sprung in die ISR an der richtigen Stelle? Mike schrieb: > GICR = (1<<IVCE); > GICR = (1<<IVSEL); Mal den Compiler-Output (disassembler) kontrolliert? IVSEL-Setzen erfordert eine "Timed Sequence", das kann dieses Codestück nicht garantieren. Ggfs per Inline-ASM machen.
>Steht auch die ISR, bzw der Sprung in die ISR an der richtigen Stelle? Wie kann ich im Hexfile explizit nach dem ISR Eintrag schauen? >Mal den Compiler-Output (disassembler) kontrolliert? >IVSEL-Setzen erfordert eine "Timed Sequence", das kann dieses Codestück >nicht garantieren. >Ggfs per Inline-ASM machen. Das hatte ich auch schon gelesen. Da aber der C Code auch im Datenblatt steht und ich die Codeoptimierung auf O0 gestellt habe, ging ich davon aus dass das auch richtig funktionieren sollte. Bin gerade dabei alles ohne Interrupts zu machen, brauche nicht unbedingt welche deswegen ist es wohl ohnehin besser. Würde das aber dennoch gerne geklärt haben ... mit ASM habe ich noch nie was gemacht. Auf die Schnelle habe ich mal folgendes versucht. asm volatile ( "ldi r16, (1<<IVCE) \n\t" "out GICR, r16 \n\t" "ldi r16, (1<<IVSEL) \n\t" "out GICR, r16 \n\t" ); Bringt ne komische Fehlermeldung im AVR Studio ccvbfsXd.s:43: Error: constant value required Hier verstehe ich gar nichts mehr :)
Mach's mal so: //Application unsigned char nTemp = GICR; GICR = nTemp | (1<<IVCE); GICR = nTemp & ~(1<<IVSEL); bzw. //Boot unsigned char nTemp = GICR; GICR = nTemp | (1<<IVCE); GICR = nTemp | (1<<IVSEL); das funktioniert. Warum so und nicht wie es im Datenblatt steht? Das, was im Datenblatt steht ist einfach falsch. Das haben die bei Atmel auch irgendwann auch einmal selber erkannt und schreiben es bei den neueren Controllern richtig rein. Bei dieser (falschen) Variante GICR = (1<<IVCE); GICR = (1<<IVSEL); wird das Bit direkt im SFR geändert. Das geht aber nicht direkt, da man im SFR nicht rechnen kann. Das bedeutet Byte lesen >> im Akku ändern >> Byte wieder zurückschreiben. Sowas nannt man auch Read-Modify-Write. Davon kriegt man auch im Debugger nichts mit, da der CPU-Core das ganz von selbst so macht, dauert aber einfach zu lange. Sodaß die Lizenz zum Ändern schon abgelaufen ist, wenn man das IVSEL auf gleiche Weise verändert. Bei der Variante oben wird das Byte vor Setzen des IVCE in ein Register geladen (Rx) und dort verändert. Im Gegensatz zum SFR kann ein Register Rx aber selber rechnen. Das ist dann keine Read-Modify-Write Operation sondern passiert in einem statt in 3 Taktzyklen im Register selber. Somit wird das IVSEL innerhalb von 4 Taktzyklen geändert. Ob man das nun in C oder Assembler programmiert ist völlig belanglos. Mfg
@Thomas, danke für die verständliche Erklärung. Leider funktionieren die Interrupts bei mir immer noch nicht :( Ich habe das noch mal mit einem Timer Interrupt versucht. Hier verhält es sich genauso. PORTA wechselt jede Sekunde seinen Zustand wenn ich das Programm ab 0x000 in den Flash schreibe. Das gleiche Programm ab 0x3800 funktioniert dann nicht mehr. Unten nochmal mein Code, im Anhang die 2 Hex Files ... vielleicht kann sich das mal jemand anschauen oder bei sich testen. MFG Mike ----------- #include <avr/io.h> #include <avr/portpins.h> #include <avr/interrupt.h> volatile int cnt = 0; int main(void) { unsigned char nTemp = GICR; GICR = nTemp | (1<<IVCE); GICR = nTemp | (1<<IVSEL); DDRA = 0xFF; SREG |= (1<<7); TIMSK |= (1<<TOIE0); TCCR0 |= (1<<CS01); while(1) { PORTA = 0x00; while(cnt<10000); cnt = 0; PORTA = 0xFF; while(cnt<10000); cnt = 0; } } //------------------------------------------------------------- ISR ( TIMER0_OVF_vect ) { TCNT0 = 162; cnt++; }
Nun funktioniert es. Ich habe die Optimerung von -O0 auf -Os eingestellt. Kann mir vielleicht jemand erklären warum der Code optimiert läuft und unoptimiert nicht ?? Im Anhang nochmal das optimierte Hexfile. MFG
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.