Hallo,
Ich versuche gerade in das Thema µController-Programmierung
einzusteigen.
Hierzu habe ich mir einen kleinen USB Programmer gekauft (USB AVR LAB
mit der Firmware vom AVRISP mkII) und eine Hand voll ATmega8.
Das Grundverständnis, wie jetzt alles funktioniert ist da (Das Forum und
andere Seiten halfen mir da sehr)
Als kleines Projekt hatte ich vor eine einfache Blinksteuerung zu bauen,
die 10mal langsam und 10mal schnell blinkt.
Mein Qellcode steht unten und ich hab folgendes Problem: in Realität
blinkt die LED immer gleich schnell.
Ich habe jetzt schon lange gesucht und den Job des ATMega gemacht und
den Code per "Fuss" abgelaufen, ich finde da keinen Fehler:
Kommentare brauchen keinen Platz im µC ;-)
Die 5x NOP fürs Extrawarten sind lächerlich. Die sind bei lahmer
Werkseinstellung von 1 MHz in 5 Microsekunden (5/1000000s) weg!
Bei 50000 Schleifendurchläufen dauert eine Blinkperiode oder Pause in
der langen Routine 0,25s länger (bei 1 Mhz) als in der kurzen.
1
.include "m8def.inc"
2
3
.equ c1 = 50000 ; Wartezeit
4
5
Reset:
6
ldi r16,0xFF ; DDR auf Ausgang schalten
7
out DDRD,r16
8
9
ldi r16,0b11111111 ; Wert für LEDs 0..7 AUS (active-low)
10
ldi r17,0b00000000 ; Wert für LEDs 0..7 AN (active-low)
Hi
Ein kleiner Tipp. Der Atmega8, wie einige andere auch, haben Timer. Dazu
gibt es hier ein Tutorial.
Der Timer ruft in bestimmten Zyklen durch einen Interrupt ein kleines
Unterprogramm auf. Dort kannst du alle Funktionen nachbilden, die
zeitlich bezogen eine Arbeit vollziehen. Wie bspw. eine LED an- oder
auszuschalten. Für ein paar Übungen mag es reichen, dies im
Hauptprogramm zu tun und kleine Delayschleifen zu programmieren, aber
wenn das Programm wächst und etwas anderes machen soll, sind die mühsam
errechneten Schleifenzeiten zum Teufel. Daher lies dich mal in die Timer
ein. Es sollte als Ergebnis ein ms-Takt oder Sekundentakt herauskommen,
der dir entsprechende Programmteile aufruft.
Nutz die Möglichkeit von Variablen. Mit einer Ex-Or Anweisung kannst du
ein Bit in die andere Lage kippen
z.B.
LDI R16, 0b11111111
LDS R17, Ablagewert
EOR R17, R16
STS Ablagewert, R17
Wenn du diese 4 Anweisungen im Sekundentakt aufrufst, werden die Bits in
der Variablen "Ablagewert" entsprechen wechseln.
Gruß oldmax
Hey danke für die Antworten, ich werde mich mal in Timer einlesen, nur
soweit war ich zu dem Zeitpunkt als ich das Programm geschrieben habe
noch nicht^^
Wäre trotzdem schön wenn ich den Fehler finden könnte denn:
@Stefan:
Ich dachte das Programm macht pro Warteschleife 50000 mal 5x NOP?
mfg
vincent
Vincent schrieb:
> @Stefan:> Ich dachte das Programm macht pro Warteschleife 50000 mal 5x NOP?
Ja, das tut es auch. Wenn du deinen AVR mit 1000000 Hz (1 MHz)
betreibst, sieht der Unterschied zwischen kurzer und langer Routine bei
einer AN oder AUS-Phase für mich so aus:
50000 Schleifendurchläufe * 1/1000000 s pro Takt 1 Takt pro NOP 5
Anzahl NOPs = 0,25 s
10 mal Blinken (5x An + 5x Aus) => Die lange Routine läuft 2,5 s länger
als die kurze Routine. Mit einer Stoppuhr und Üben könnte man den
Unterschied wohl messen. Mit Zukucken und Schätzen würde ich keinen
Unterschied sehen.
Bei schnellerem Takten als 1 MHz verringert sich der Unterschied
zwischen beiden Routinen. Dann wird selbst das Handstoppen schwer.
welchen Unterschied hast du denn berechnet oder erwartet?
Hast du den Code mal im Simulator von AVR Studio getestet und dort mit
der Stoppuhr die Laufzeit ausgemessen? Ich habe dein Programm nicht auf
der Hardware gesehen oder im Simulator gehabt.
Abgesehen von zu kurzem Unterschied in der Laufzeit kann es noch andere
Ursachen für deine Beobachtung geben. Aber wie beim stehengebliebenen
Auto checke ich zuerst den Benzintank bevor ich über einen Fehler im den
Verteiler spekuliere...
Eine Ursache "im Motorraum" kann ein regelmäßiger Reset sein, der
innerhalb der kurzen Routine aktiv wird.
Bei deinem Programm kann man schlecht sehen, ob das Programm zur
Laufzeit überhaupt in die lange Routine kommt. Hast du mal überlegt, in
jeder Routine eine andere LEDs blinken zu lassen?
Auf Verdacht würde ich alle Resetmöglichkeiten kontrollieren bzw.
ausschliessen, d.h. die Watchdog-Fuse kontrollieren und die
Vektortabelle für die ISRs am Flashanfang freihalten (.org Anweisung).
Ich habe mal zur Simulation die Wartezeiten runtergeregelt und per Hand
(F11 - AVR Studio) Schritt für Schritt den Code abgefahren.
Laut Simulation durchfährt der AVR beide Routinen.
Naja wer weiß was das ist^^, ich werde jetzt mal mit Timern anfangen.
Mein großes Ziel ist es übrigens eine Schrittmotorsteuerung für
Fräsmaschinen zu entwickeln, welche DIN/ISO Code einliest. Die Steuerung
soll Quasi das komplette Fräsprogramm samt PC ablösen.
Quasi:
- Im warmem Wohnzimmer Fräsprogramm erstellen
- Dann auf SD-KARTE
- In den Keller und mittels Steuerung 0-Punkt bestimmen und Fräsprogramm
starten.
Umgesetzt wird das dann mittels LCD Display.
(Was gelernt und den Kampf gegen die Langeweile besiegt. ;-)
Schönes, aber engagiertes Projekt. Ich würde so etwas umfangreiches eher
in C machen als in Assembler. Dümmer wirst du durch Assemblerlernen aber
nicht.
In C programmieren schließt das Verständnis darüber, was der Controller
wirklich macht nicht aus. Kommt ganz darauf an ob man überhaupt wissen
will was der Controller genau macht.
Bring mit Assembler ein paar LEDs zum blinken und spiel dich mit Timern
und irgendeiner Schnittstelle (UART, SPI oder I2C), dann solltest du im
Prinzip wissen was im Controller passiert. Datenblatt musst du sowieso
lesen, egal welche Sprache du verwendest.
Die Konfiguration von Timern bzw einer Schnittstelle ist in C zwar nicht
viel leichter bzw kürzer, dafür ist es bei größeren Projekten durchaus
nicht blöd auf C zu setzen (Dokumentationstools, generische Module,
...).
Der Compiler ist auch nicht immer Perfekt und wenn man überprüfen will,
was er da aus dem C Code gemacht hat, dann muss man Assembler verstehen
können.