Jump to content
Schiffsmodell.net

Programm 2: LED-Blinker


Torsten

Recommended Posts

HIER GEHTS ZU DEN FRAGEN

 

Hallo Leute,

 

ich weiß... ein Blinklicht ist so ziemlich das Langweiligste, was man sich vorstellen kann, aber ich möchte an diesem kleinen Programm kurz die Grundlagen erläutern, die für ein Bascom-Programm erforderlich sind.

Die Ansage hier war absolute Einsteiger-Tauglichkeit, somit bitte ich um Verständnis bei allen, die schon etwas fortgeschrittener unterwegs sind.

 

Hier zunächst einmal der Quellcode, den ihr wie gewohnt kompilieren und dann mit dem Burn-O-Mat´en übertragen könnt:

 

'******************************************************
'
' (c)2012 SCHIFFSMODELL.NET
'
'******************************************************
'Projekt: Atmel-Programmierung für Einsteiger
'
'Prozessor: ATMega 16
'Bascom-Version: 2.0.7.5
'
'Programm 2: LED-Blinker
'
'Hardware:
'Atmel Evaluation Board V2.01
'
'20.11.2012 T. Gietenbruch
'
'******************************************************
'======================================================
'System-Einstellungen
'======================================================
'Definition für Mega 16
$regfile "m16def.dat"
'Angabe der Taktfrequenz (1Mhz)
$crystal = 1000000

'======================================================
'Konfigurationen
'======================================================
'Ein- und Ausgänge festlegen
Config Portd.5 = Output

'======================================================
'ALIAS-Zuweisungen
'======================================================
Led_1 Alias Portd.5

'======================================================
'Hauptprogramm-Schleife
'======================================================
'Hier gehts los
Do
Toggle Led_1
Waitms 500
'Hier ist Ende -> zurück nach DO
Loop

'Programmende (nur formal)
End

 

Im Anschluß möchte ich auf die Befehle eingehen, die hier verwendet wurden:

 

$regfile "m16def.dat"

 

Dies ist eine Direktive für den Compiler, in der ihm mitgeteilt wird, für welchen Chip das Programm übersetzt werden soll.

Sie verweist auf das jeweilige DAT-File, dass im Installationsverzeichnis von Bascom hinterlegt ist. Dort kann man sich für andere Chips auch die richtige Bezeichnung heraussuchen - ist eigenlich immer das Typenkürzel, gefolgt von DEF.DAT

 

 

$crystal = 1000000

 

Ebenfalls eine Direktive, die dem Compiler verrät, mit welcher Taktfrequenz der Chip später betrieben wird. Die Angabe ist in Hz (also hier 1MHz) und muss mit den Einstellungen der FuseBits (M16-Datenblatt Seite 29...) übereinstimmen, sonst funktionieren LCD-Ansteuerungen und viele andere Befehle nicht korrekt.

Wenn das Programm "rumspinnt", liegt hier oft der Fehler... ;)

 

 

Config Portd.5 = Output

 

Mit diesem Befehl wird der Port D.5 (LED 1 auf dem Board) als Ausgang definiert. Nicht schwer zu erraten... mit =INPUT wäre ein Eingang daraus geworden.

So ziemlich jeder PIN des Chips kann wahlweise als Ein- oder Ausgang verwendet werden, die Umschaltung ist sogar zur Laufzeit im Programm möglich.

Allerdings sind bestimmte PIN´s an bestimmte Funktionen gebunden, z.B. Hardware-Interrupts, Analogwert-Verarbeitung, PWM-Erzeugung und noch ein paar andere nützliche Dinge.

Wenn man diese Features im Programm nutzen möchte, muss man sich die entsprechenden PIN´s also freihalten.

 

 

Led_1 Alias Portd.5

 

Die ALIAS-Zuweisung ist ein kleiner, aber nützlicher Trick, um die Programme etwas übersichtlicher zu gestalten. Prinzipiell ersetzt der Compiler nur den jeweiligen Begriff im Programmcode.

Im vorliegenden Fall wird also sozusagen überall im Programm "Led_1" mit "Portd.5" ersetzt.

Wer so wie ich an beginnender Altersvergesslichkeit leidet und sich einfach nicht mehr merken kann, an welchem der 40 PIN´s die verdammte LED nun angeschlossen ist, wird diese Funktion schnell kennen und lieben lernen... :mrgreen:

Diese Zuweisung benötigt ebenso wie Kommentare keinen Speicher im Chip - man darf also durchaus exzessiv damit umgehen.

 

 

Do

..

Loop

 

Das ist unsere Hauptprogramm-Schleife. Unerlässlich, wenn man ein Programm zyklisch ablaufen lassen möchte.

Das Programm wird immer zeilenweise abgearbeitet, wenn "LOOP" erreicht wird, erfolgt der Rücksprung nach "DO".

 

 

Toggle Led_1

 

Mit dem Toggle-Befehl wird ein Bit umgeschaltet - aus 0 (FALSE) wird 1 (TRUE) und umgekehrt. Hier also LED 1 bzw. Port D.5 -> siehe ALIAS-Zuweisung weiter oben.

 

 

Waitms 500

 

Den WAIT-Befehl gibt es in verschiedenen Ausprägungen:

Waitus -> µSekunden

Waitms -> Millisekunden

Wait -> Sekunden

 

Eine bequeme Sache, meint man... allerdings macht das Programm an dieser Stelle auch genau das, was der Befehl aussagt - es wartet für 500ms.

Da die Programm-Abarbeitung somit komplett unterbrochen wird, ist der Befehl kaum zu gebrauchen, wenn der Prozessor neben einer blinkenden LED noch andere Dinge zu tun hat, aber dazu später mehr.

In jedem Fall sollte man WAIT aber nur in homöopathischen Dosierungen anwenden.

 

So, das war´s für heute!

 

Grüße

 

Torsten

Edited by Torsten
Link to comment

So... und um die Sache nun ein wenig spannender zu machen, versuchen wir mal, den doch etwas problematischen WAIT-Befehl zu vermeiden.

 

Dabei lernen wir etwas über Timer und Interrupts - also ereignisgesteuerte Unterbrechungen des Hauptprogramms.

Der Mega16 hat drei interne Timer(0, 1 und 2), wobei Timer 0 und 2 8 Bit erfassen (also von 0..255 laufen). Timer 1 hat 16 Bit (läuft also von 0..65535)

Wenn ein Timer überläuft (also die Grenze seines Zahlenbereichs erreicht hat), kann er einen Interrupt auslösen, um auf dieses Ereignis zu reagieren. Dafür kann jedem Timer eine eigene Interrupt Service Routine (=ISR oder auch Unterprogramm) zugewiesen werden. Nach dem Überlauf setzt sich der Timer wieder auf 0 und das Spiel fängt von vorne an.

 

Die Timer werden über den Systemtakt hochgezählt, in unserem Fall also 1000000 mal pro Sekunde. Damit würde ein 8-Bit-Timer 1000000:255=3921 mal pro Sekunde durchlaufen und entprechend oft den Interrupt auslösen. Der 16-Bit-Timer würde 1000000:65535=15 mal pro Sekunde durchlaufen.

 

Über den Prescale-Faktor kann angegeben werden, ob der Timer bei jedem Systemtakt oder nur jedem n-ten Takt um Eins erhöht wird. Somit lässt sich der Durchlauf eines Timers entsprechend verlangsamen.

Für Prescale sind nur die Werte 1, 8, 64, 256 und 1024 zulässig.

 

Also, das Grundprinzip in schematischer Auflistung:

 

1. Hauptprogramm läuft, Timer zählt hoch

2. Timer erreicht Maximalwert -> Hauptprogramm wird verlassen, Unterprogramm wird aufgerufen

3. Unterprogramm wird abgearbeitet -> Rücksprung ins Hauptprogramm

 

Der Chip "merkt" sich bei jedem Interrupt-Ereignis, an welcher Stelle er das Hauptprogramm verlassen hat und springt am Ende des Unterprogramms wieder an diese Stelle zurück, um das Programm fortzusetzen.

 

Nachfolgend findet ihr wieder den Code, die neuen Befehle erkläre ich weiter unten:

 

'******************************************************
'
' (c)2012 SCHIFFSMODELL.NET
'
'******************************************************
'Projekt: Atmel-Programmierung für Einsteiger
'
'Prozessor: ATMega 16
'Bascom-Version: 2.0.7.5
'
'Programm 2a: LED-Blinker mit Timer
'
'Hardware:
'Atmel Evaluation Board V2.01
'
'21.11.2012 T. Gietenbruch
'
'******************************************************
'======================================================
'System-Einstellungen
'======================================================
'Definition für Mega 16
$regfile "m16def.dat"
'Angabe der Taktfrequenz (1Mhz)
$crystal = 1000000
'======================================================
'Konstanten
'======================================================
Const Preload = 0
'=====================================================
'Deklarationen
'======================================================
Dim Time_s As Byte
Dim Pulse As Bit
Dim Pulse_s As Bit
'======================================================
'Konfigurationen
'======================================================
'Timer0 einrichten
Config Timer0 = Timer , Prescale = 1024
Stop Timer0

'ISR-Zuweisung für Timer0
On Timer0 Timecount

'Ein- und Ausgänge festlegen
Config Portd.5 = Output
Config Portd.6 = Output

'======================================================
'ALIAS-Zuweisungen
'======================================================
Led_1 Alias Portd.5
Led_2 Alias Portd.6

'======================================================
'Initialisierungen vor Programmstart
'======================================================
'Timer0 mit Vorgabewert laden
Timer0 = Preload
'Timer0 Interrupt scharfmachen
Enable Timer0
'Interrupts freigeben
Enable Interrupts
'Timer0 starten
Start Timer0

'======================================================
'Hauptprogramm-Schleife
'======================================================
Do
'Die LED´s folgen den Timer-Pulsen
Led_1 = Pulse_s
Led_2 = Pulse

'Hier ist Ende -> zurück nach DO
Loop

'======================================================
'ISR für Timer0
'======================================================
Timecount:
'Timer0 mit Vorgabewert laden
Timer0 = Preload
'Variable hochzählen (+1)
Incr Time_s
'Direkter Pulse
Toggle Pulse
'Verlangsamter Pulse
If Time_s >= 4 Then
  Toggle Pulse_s
  Time_s = 0
End If
'Und zurück ins Hauptprogramm
Return

'Programmende (nur formal)
End

 

 

Const Preload = 0

 

Ist im Grunde verwandt mit der ALIAS-Zuweisung, der Compiler ersetzt überall im Code das Wort "Preload" gegen den Wert 0, oder was immer eingetragen ist. Ein komfortabler Weg, um Programme etwas flexibel parametrieren zu können.

In unserem Fall kann mit Preload dem Timer ein Startwert vorgegeben werden.

 

Mit Konstanten kann man sogar rechnen:

Const Wert_a = 5

Const Wert_b = 10

Const Ergebnis = Wert_a + Wert_b

 

Da so der Compiler für uns rechnet und keinerlei Variablen deklariert werden müssen, wird im Prozessor auch kein Speicher verbraucht. Allerdings ist es nicht möglich, im Hauptprogramm schreibend auf eine Konstate zuzugreifen, da braucht man dann schon eine Variable.

 

 

Dim Time_s As Byte

Dim Pulse As Bit

 

Mit DIM werden Variablen deklariert, die später im Programm schreibend und lesend verwendet werden können. Der Datentyp (Byte, Bit, Word, etc.) muss mit angegeben werden.

 

 

Config Timer0 = Timer , Prescale = 1024

 

Hier wird der Timer0 (8 Bit) als Timer mit einem Prescale-Wert von 1024 konfiguriert, er zählt also alle 1024 System-Takte um 1 hoch. Timer können auch als Counter konfiguriert werden, um z.B. Impulse an einem Eingang zu zählen.

 

 

Stop Timer0

Start Timer0

 

Der Timer0 wird angehalten bzw. gestartet, der letzte Wert bleibt dabei gespeichert.

 

 

On Timer0 Timecount

 

Dem Timer0 wird das Unterprogramm zugewiesen, dass bei seinem Überlauf aufgerufen werden soll.

 

 

Timer0 = Preload

 

Der Timer wird auf einen Startwert gesetzt, ab dem der Timer hochgezählt wird. So kann die Zeit für einen Timer-Durchlauf verkürzt werden.

 

 

Enable Timer0

 

Der Interrupt für den Timer0 wird freigegeben, Mit DISABLE wird er wieder gesperrt.

 

 

Enable Interrupts

 

Das generelle Interrupt-Handling wird global freigegeben. Mit DISABLE kann es wieder gesperrt werden. Der Prozessor führt dann keinerei Interrupts mehr aus.

 

 

Timecount:

..

Return

 

Ein Unterprogramm, in dem Fall Timer0 zugewiesen (On Timer0 Timecount).

Mit Erreichen des RETURN-Befehls kehrt der Prozessor ins Hauptprogramm zurück.

Timecount: ist das Label (der Name) des Unterprogramms.

 

Unterprogramme können auch mit dem Befehl GOSUB aufgerufen werden.

 

 

Incr Time_s

 

Die Variable Time_s wird um 1 erhöht, man könnte auch schreiben: Time_s = Time_s + 1. Um 1 verringern kann man mit dem Befehl DECR.

 

 

If Time_s >= 4 Then

..

End If

 

IF...THEN...END IF ist eine bedingte Verzweigung. Wenn die bei IF angegebene Bedingung wahr (TRUE), also in unserem Fall Time_s größer oder gleich 4 ist, wird der nachfolgende Code ausgeführt, ansonsten geht es nach dem END IF weiter.

 

 

So, das war´s wieder mal für heute.

 

Wer mag, kann ja mit dem Programm einmal ein paar Fingerübungen veranstalten:

 

a) Den Preload-Wert für den Timer ändern (0 bis 200 macht Sinn).

 

b) Systemtakt in den FuseBits auf 8MHz setzen ($Crystal nicht vergessen) und schauen, was die LED´s dann so treiben.

 

c) In der Zeile If Time_s >= 4 Then statt 4 mal einen kleineren oder grösseren Wert eintragen (oder gleich eine Konstante dafür definieren)

 

Viele Grüße

 

Torsten

Edited by Torsten
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.