Die Seite ist noch im Aufbau ...
;; test1.asm .equ DDRL=0x10A ;defining address of DDRL using a hexadezimal number .equ PORTL=0x10B ;defining address of PORTL main: ldi r16, 0xFF ;load immediate register16 with hexadezimal FF sts DDRL, r16 ;store to Data Direction Register L, will set all bits on PORTL aus output ldi r16, 0x55 ;lets select every second LED sts PORTL, r16 ;store to port L, will turn on some LEDs connected to PORTL L1: rjmp L1 ;relative jump back to label L1, so it will stay in ths loop for ever (until reset).Beim laufen lassen dieses Programms sollte jede zweite LED am PORTL leuchten.
> avra test1.asm #Warnung ignorieren > avrdude -p m2560 -c stk500v2 -P /dev/ttyUSB0 -U flash:w:test1.hex:iSollte das nicht auf Anhieb funktionieren, siehe nächstes Kapitel.
Alle Hinweise beziehen sich auf das Arbeiten unter Linux (z.B. Kubuntu-11.10)
> avrdude -p m2560 -c stk500v2 -P /dev/ttyUSB0 -v(Das Zeichen ">" ist das Bereitschaftszeichen und muss nicht mit eingetippt werden)
Sollte "/dev/ttyUSB0" nicht gefunden werden, oder einem andern Gerät zugewiesen sein:
- USB-Kabel ausstecken und mit dem Befehl "ls /dev/tty*" prüfen was schon vorhanden ist.
- USB-Kabel einstecken und wieder "ls /dev/tty*" um zu schauen was neu da ist.
Wenn jetzt neu z.B. "/dev/ttyUSB2" erkannt wird, dann dieses beim Verbindungstest verwenden.
Unter MacOSX könnte es z.B. auch "/dev/tty.serial-0001" sein.
Wenn diese Fehlermeldung kommt:
avrdude: stk500v2_command(): command failed
Dann ist eventuell die externe Stromversorgung nicht eingesteckt.
Wenn alles funktioniert, sollte unter anderem folgendes angezeigt werden:
avrdude: Device signature = 0x1e9801 avrdude: safemode: lfuse reads as F7 avrdude: safemode: hfuse reads as D5 avrdude: safemode: efuse reads as FD avrdude: safemode: lfuse reads as F7 avrdude: safemode: hfuse reads as D5 avrdude: safemode: efuse reads as FD avrdude: safemode: Fuses OK
Wichtig mit dem myAvrStamp256 auf dem MK3-Board ist, dass die Fusebits so gesetzt sind, dass der Fullswing-Modus des Quarz-Oszillators gesetzt ist. Falls nicht läuft alles sehr instabil. Zum Beispiel geht ein Programm nur gerade nach neuem Programmieren, aber dann bei einem Reset nicht mehr. Oder manchmal gehts, manchmal dann wieder nicht mehr.
Deshalb ist es wichtig dass "lfuse" so gesetzt ist:
avrdude: safemode: lfuse reads as F7Dies so setzen kann man mit diesem Befehl:
> avrdude -p m2560 -c stk500v2 -P /dev/ttyUSB0 -U lfuse:w:0xF7:m
;; lcdledtest.asm .equ PORTA = 0x02 .equ DDRA = 0x01 main: ldi r16, (1<<5) out DDRA, r16 ldi r16, (1<<5) ;(1<<5) fuer Licht ein, oder (0<<5) fuer Licht aus out PORTA, r16 rjmp main
In diesem Paket ist eine provisorische Umgehung dieses Problems enthalten: rolfslcdtreiber.tar.gz
;; beisp1.asm .include "m2560def.inc" ; Definitionen fuer den ATmega2560 ; Reset and interrupt vectors ; Meaning begin: jmp main ; Power On Reset jmp err_vect ; External Int0 (INT0_vect) jmp err_vect ; External Int1 jmp err_vect ; External Int2 jmp err_vect ; External Int3 jmp err_vect ; External Int4 jmp err_vect ; External Int5 jmp err_vect ; External Int6 jmp err_vect ; External Int7 jmp err_vect ; Pin Change Interrupt PCINT0 jmp err_vect ; Pin Change Interrupt PCINT1 jmp err_vect ; Pin Change Interrupt PCINT2 jmp err_vect ; Watchdog Time-out WDT jmp err_vect ; Timer/Counter2 Compare Match A (TIMER2_COMPA_vect) jmp err_vect ; Timer/Counter2 Compare Match B (TIMER2_COMPB_vect) jmp err_vect ; Timer/Counter2 Overflow (TIMER2_OVF_vect) jmp err_vect ; Timer/Counter1 Capture Event (TIMER1_CAPT_vect) jmp err_vect ; Timer/Counter1 Compare Match A (TIMER1_COMPA_vect) jmp err_vect ; Timer/Counter1 Compare Match B (TIMER1_COMPB_vect) jmp err_vect ; Timer/Counter1 Compare Match C (TIMER1_COMPC_vect) jmp err_vect ; Timer/Counter1 Overflow (TIMER1_OVF_vect) jmp err_vect ; Timer/Counter0 Compare Match A (TIMER0_COMPA_vect) jmp err_vect ; Timer/Counter0 Compare Match B (TIMER0_COMPB_vect) jmp err_vect ; Timer/Counter0 Overflow (TIMER0_OVF_vect) jmp err_vect ; SPI Serial Transfer Complete (SPI_STC_vect) jmp err_vect ; USART0 Rx Complete (USART0_RX_vect) jmp err_vect ; USART0 Data Register Empty (USART0_UDRE_vect) jmp err_vect ; USART0 Tx Complete (USART0_TX_vect) jmp err_vect ; Analog Comparator (ANALOG_COMP_vect) jmp err_vect ; ADC Conversion Complete (ADC_vect) jmp err_vect ; EEPROM Ready (EE_READY_vect) jmp err_vect ; Timer/Counter3 Capture Event (TIMER3_CAPT_vect) jmp err_vect ; Timer/Counter3 Compare Match A (TIMER3_COMPA_vect) jmp err_vect ; Timer/Counter3 Compare Match B (TIMER3_COMPB_vect) jmp err_vect ; Timer/Counter3 Compare Match C (TIMER3_COMPC_vect) jmp err_vect ; Timer/Counter3 Overflow (TIMER3_OVF_vect) jmp err_vect ; USART1 Rx Complete (USART1_RX_vect) jmp err_vect ; USART1 Data Register Empty (USART1_UDRE_vect) jmp err_vect ; USART1 Tx Complete (USART1_TX_vect) jmp err_vect ; TWI (I2C) 2-wire Serial Interface (TWI_vect) jmp err_vect ; Store Program Memory Ready (SPM_READY_vect) jmp err_vect ; Timer/Counter4 Capture Event (TIMER4_CAPT_vect) jmp err_vect ; Timer/Counter4 Compare Match A (TIMER4_COMPA_vect) jmp err_vect ; Timer/Counter4 Compare Match B (TIMER4_COMPB_vect) jmp err_vect ; Timer/Counter4 Compare Match C (TIMER4_COMPC_vect) jmp err_vect ; Timer/Counter4 Overflow (TIMER4_OVF_vect) jmp err_vect ; Timer/Counter5 Capture Event (TIMER5_CAPT_vect) jmp err_vect ; Timer/Counter5 Compare Match A (TIMER5_COMPA_vect) jmp err_vect ; Timer/Counter5 Compare Match B (TIMER5_COMPB_vect) jmp err_vect ; Timer/Counter5 Compare Match C (TIMER5_COMPC_vect) jmp err_vect ; Timer/Counter5 Overflow (TIMER5_OVF_vect) jmp err_vect ; USART2 Rx Complete (USART2_RX_vect) jmp err_vect ; USART2 Data Register Empty (USART2_UDRE_vect) jmp err_vect ; USART2 Tx Complete (USART2_TX_vect) jmp err_vect ; USART3 Rx Complete (USART3_RX_vect) jmp err_vect ; USART3 Data Register Empty (USART3_UDRE_vect) jmp err_vect ; USART3 Tx Complete (USART3_TX_vect) err_vect: ; do something when unexpected Interrupt occures push r16 ldi r16, 0xAA ; Zur Fehleranzeige z.B. jede 2. LED leuchten lassen sts PORTL, r16 ; oder auskommentieren wenn keine Fehlerbehandlung erwuenscht. pop r16 reti ; return from interrupt ; jmp begin ; or jump to make a reset main: ldi r16, low(RAMEND) out SPL, r16 ; Init Stackpointer L ldi r16, high(RAMEND) out SPH, r16 ; Init Stackpointer H ldi r16, 0xFF ; Lade Register r16 mit hexadezimal FF sts DDRL, r16 ; auf DDRL speichern um gesamten PORTL als Ausgabe zu definieren ldi r16, 0x07 ; z.B. erste 3 LEDs einschalten mainloop: sts PORTL, r16 ; auf PORTL speichern um LEDs leuchten zu lassen clc ; clear carry - Uebertrag-Bit loeschen rol r16 ; rotate left - nach links rotieren brcc L1 ; branch if carry clear - springe wenn Ubertrags-Bit aus ist ori r16, 1 ; otherwise: set bit 0 - sonst: setze Bit 0 in r16 L1: rcall wait rjmp mainloop ; relative jump back to label mainloop, will stay in ths loop for ever (until reset). wait: ; waiting subroutine push r24 ; save registers on stack - Register auf dem Stack sichern push r25 push r16 ldi r16, 10 ; counter for outer loop w1: ldi r24, low(-32000) ; Registerpaar r25:r24 als 16-Bit Register verwenden. Low Byte in r24, High Byte in r25 ldi r25, high(-32000) ; use registers r25:24 as 16 bit register, low byte r24, high byte r25 w2: adiw r24, 1 ; r25:r24 um 1 erhoehen - increment register pair brne w2 ; branch if not equal, doing loop until r25:r24 reaches zero - Schlaufe widerholen bis Wert auf 0 dec r16 brne w1 ; make outer loop 10 times pop r16 ; restore registers from stack - Register vom Stack wieder zurueckholen pop r25 pop r24 ret(Im Gegensatz zum Atmega8 müssen hier in der Interrupt-Tabelle wirklich "jmp" Befehle und _nicht_ "rjmp" Befehle verwendet werden. Statt "jmp err_vect" direkt ein "reti" einsetzen wäre hier auch falsch.)
# makefile A=avra N=m2560 P=stk500v2 TTY=/dev/ttyUSB0 all: beisp1.hex beisp1.hex: beisp1.asm $A beisp1.asm install: beisp1.hex avrdude -p $N -c $P -P $(TTY) -U flash:w:beisp1.hex:i clean: rm -f *~ *.obj *.cofDie Einrückungen müssen jeweils ein Tabulator sein. Genaueres über makefiles siehe Kapitel Makefiles auf der übergeordneten Site.
> make > make installWenn jetzt da beim ersten "make" Fehlermeldungen kommen:
> find /usr/ -name "m2560*.inc" #im System suchen > cp /usr/share/avra/m2560def.inc . #vom gefundenen ins aktuelle Verzeichnis kopierenWenns jetzt solche Fehler gibt:
m2560def.inc(44) : Error : Unknown mnemonic/macro: #pragmaDie kopierte Datei m2560def.inc editieren und die entsprechenden Zeilen mit ";" auskommentieren.
m2560def.inc(47) : Error : Unknown device: ATmega2560Das lässt sich beheben durch anpassen von avra und neu compilieren. Dazu z.B. unter avra-1.3.0/src/device.c in der "struct device device_list[]" folgende Zeile einfügen:
{ "ATmega2560", 131072, 0x200, 8192, 4096, DF_NO_ESPM},Wenn jetzt alles klappt, so sollte das Programm nach dem "make install" automatisch starten, und die LEDs am PORTL entsprechend blinken.
Fotsetzung folgt ...
// test1.c #include <avr/io.h> int main() { DDRL = 0xFF; //Data Direction Register L auf Hexadezimal FF setzen: Alle Bits am PORTL als Ausgabe PORTL= 0xAA; //Daten auf PortL ausgeben (z.B. jede zweite LED einschalten) while(1) //mainloop will never end - diese Schlaufe wird nie verlassen { } return 0; //wird nie erreicht }Und hier das makefile dazu:
# makefile M=atmega2560 N=m2560 C=avr-gcc -mmcu=$M -Wall -Os -c L=avr-gcc -mmcu=$M -Wall -Os TTY=/dev/ttyUSB0 P=stk500v2 all: test1.hex test1.hex: test1.c $L test1.c -o test1.elf avr-objcopy -O ihex -R .eeprom test1.elf test1.hex avr-objdump -h -S test1.elf > test1.lss install: test1.hex avrdude -p $N -c $P -P $(TTY) -U flash:w:test1.hex:i clean: rm -f *~ *.elf *.lssWenn wir das Programm mit "make" übersetzt haben, dann haben wir neben dem test1.hex noch eine neue Datei test1.lss, in der das Assembler-Programm, das der Compiler erzeugt hat, zu finden ist. Man vergleiche mit unseren ersten einfachen Assembler-Programmen.
Fotsetzung folgt ...
Der Bootloader von der myAVR-Seite ist mangels Dokumentation unbrauchbar (zumindest unter Linux).
Einen Bootloader selbst zu schreiben sollte aber ziemlich einfach sein:
AVR_Bootloader_in_C
Hier ein erster Ansatz eines eigenen Bootloaders inklusive makefile mit Umgehung des 64KB-Problems:
cbootloader.tar.gz
Erste Tests mit diesem selbst geschriebenen Bootloader waren schon mal erfolgreich.
Zum Beispiel das Testbeispiel "asmbeispiele/flashcheck" funktioniert. (siehe Downloads)
Funktioniert allerdings bisher nur unter Linux. Unter MacOSX würde es eigentlich auch funktionieren, nur kann ich da keine geeignete Baudrate im "hexprog" einstellen. (Die MacOSX-Version von "hexprog" könnte auch unter Windows gehen, habe ich bisher aber noch nicht probiert.)
Das Ansprechen von UART3 im Atmega2560 funktioniert fast genau gleich wie hier beschrieben: UART-Tutorial bei mikrocontroller.net
Ein entsprechnendes Beispiel für den Atmega2560 hier:
// uart_test.c #include <avr/io.h> #include <inttypes.h> #ifndef bool #define bool int8_t #endif #define F_CPU 16000000UL #define BAUD 500000UL // Baudrate // Berechnungen: #define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) // clever runden #define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // Reale Baudrate #define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler. #if ((BAUD_ERROR<990) || (BAUD_ERROR>1010)) #error Systematischer Fehler der Baudrate groesser 1% und damit zu hoch! #endif void uart_init() { UBRR3 = UBRR_VAL; UCSR3B = (1<<RXEN3) | (1<<TXEN3); // UART3 RX und TX einschalten //UCSR3C = (1<<USBS3)|(3<<UCSZ30); // Asynchron 8N2 UCSR3C = (3<<UCSZ30); // Asynchron 8N1 } void uart_putc(char c) { while (!(UCSR3A & (1<<UDRE3))) ;// warten bis Senden moeglich UDR3 = c; // sende Zeichen } bool uart_getcheck() { return (UCSR3A & (1<<RXC3)); //true wenn ein Zeichen verfuegbar } char uart_getc(void) { while (!(UCSR3A & (1<<RXC3))) ;// warten bis Zeichen verfuegbar return UDR3; // Zeichen aus UDR an Aufrufer zurueckgeben } void uart_write(const char *s) { char c; while((c= *s++)!=0) uart_putc(c); } void uart_getzeile(char *zeile,int max) { char c; while(1) {if(uart_getcheck()) { c = uart_getc(); if(c=='\n' || max<=1) {*zeile=0; return;} if(c>=' ') {*zeile++ = c; --max;} } } } int main() { char zeile[80]; uart_init(); uart_write("Hallo Welt\n"); //wenn Gegenstelle ein Windows-Programm ist, eventuell "\r\n" machen. while(1) { uart_getzeile(zeile,80); uart_write(zeile); uart_write("-ok\n"); } return 0; }Auf dem Computer braucht es noch ein Programm, das mit den Daten, die der Microkontroller sendet, etwas anfangen kann, und auch Daten sendet, mit denen das Microkontroller-Programm was anfangen kann.