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 *.cof
Die 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 *.lss
Wenn 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.