Bascom BASCOM ; Erste Schritte zum Ausprobieren

Bascom kennt die Assembler-typische Schreibweise mit den geschobenen Bits nicht
Assemblertypisch ist diese Schreibweise nicht. Als ich im letzten Jahrtausend noch mehr in Assembler programmiert habe, ist mir diese Schreibweise nie begegnet.

Bei den Xmegas, mit denen ich Atmel vor 10 Jahren kennengelernt habe, benutzt man üblicherweise die Bitmap-Schreibweise. Gewundert habe ich mich über diese seltsame Bitschiebeschreibweise erst, als ich bei einigen älteren AVRs darüber gestolpert bin.

Ist aber wohl mehr Gewohnheit. Bei Bitmaps weiß ich auf den ersten Blick, welches Bit gemeint ist, bei Bitposition muß ich immer erst nachzählen. ;)
Wer von klein auf nur Bitschubserei kennengelernt hat, für den erscheinen wohl Bitmaps umständlicher.

btw
Bei einigen Controllern ...
bei den Xmegas haben die timer zwei oder vier compare/capture-register d.h. man kann alle für compare oder capture benutzen, und ein PER-register für den TOP-Wert. Außerdem kann man zwei timer zu einem 32-bit-register verketten und noch einiges mehr.
Bei den Xtinies wurde das wieder etwas reduziert, A kann nur compare, B nur capture…

Aber das führt zu weit von bascom weg. clear_timern kann bei allen drei timern, also auch bei den 8-bit-timern, nur 0 oder 1 annehmen. Eine Erklärung dafür habe ich aber auch nicht gefunden.
 
Bitmap-Schreibweise
Erklär mal - kann mir nichts drunter vorstellen.
Die "alte-AVR-typische" ist mir klar: hinter dem Bitnamen verbirgt sich ja die als Konstante hinterlegte Bitposition. Durch das schieben bzw potenzieren wird daraus der Wert des Bits berechnet. VerORt bzw addiert man diese Werte, erhält man das Byte als Konstante.
welches Bit gemeint ist, bei Bitposition muß ich immer erst nachzählen.
???
Die Bitposition selbst brauchst Du doch gar nicht, darum gehts ja gerade - Du verwendest die Namen, und der Compiler sucht sich die Werte aus der Definitionsdatei.
Wenn Du also zB beim Mega8 für Timer1 die Bits CS10, CS12 und WGM12 im Timer and Counter 1 Control Register B setzen willst brauchst Du weder Registeradresse noch Bit-Nummern wissen, sondern schreibst die Byte-Konstante durch:


CodeBox Assembler
LDI Rd, (1<<CS10)|(1<<CS12)|(1<<WGM12)
OUT TCCR1B, Rd

Bei Bascom sieht das dann etwa so aus:


CodeBox BascomAVR
TCCR1B=(2^CS10)+(2^CS12)+(2^WGM12)
Möglicherweise kann man das nicht direkt zuweisen sondern muß Bascom die konstante vorher bekannt machen:


CodeBox BascomAVR
Const Blablub=(2^CS10)+(2^CS12)+(2^WGM12)
...
TCCR1B=Blablub
 
Jetzt müsste ich ein Interrupt mit einer For-Next Schleife einfügen. Aber wie wird dieses Interrupt bezeichnet?

$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 40
$swstack = 16
$framesize = 32
$baud = 19200

Ddrc = &B_11_1111
Portc = &B00_0000

Ddrd = &B_1111_1111
Portd = &B0000_0000

Rot Alias Portd.5
Gelb Alias Portd.6
Gruen Alias Portd.7

Ocr2 = 249
Config Timer2 = Ctc , Prescale = 256 , Clear_timer = 1
On Oc2 Isr_timer2
Enable Oc2
On Timer2 Isr_rot
Enable Timer2
Enable Interrupts

Main:
Reset Watchdog
Goto Main

Isr_rot:
Ocr2 = 249
Toggle Rot
Return
 
Ich würde es nicht Interrupt betiteln. Interrupt ist Hardwareseitig.
Und so ganz passt das nicht.
Ocr2 musst du nur 1x setzen, der Wert bleibt bestehen.
Das ist halt die Eigenheit vom CTC Mode. 249 ist jetzt der Maximalwert des Timers, und bleibt es auch, bis du ihm was Anderes sagst.

Aber da war ja noch etwas mit Dim und If ;)
For - Next brauchen wir hier nicht.
 
Ich dachte halt, mit For - Next kann ich eine bestimmte Zeitspanne vorgeben. Mit Dim Variable As Byte ist es bis 255. Kann ich If Variable Then Gruen * 60 oder so ähnlich schreiben. Ich muss doch irgendwie auf diese Wartezeiten kommen.
 
Du musst unterscheiden zwischen blockierenden Sachen wie Wait, auch For Next wird immer blockierend abgearbeitet, und ich nenne es mal Eventgesteuerten Abläufen. Letzteres ist halt die Interrupt Methode.

Aber was macht For Next?
Im Endeffekt nur eine Variable von Wert x bis y erhöhen und den Inhalt dabei ausführen.

Durch den Timer und die innere If hast du ja einen Taktgeber im Sekundentakt.
Zähl doch einfach die Sekunden.
Hoch zählen kannst du selber (Incr)
Und Verzweigen (If) auch.
Und jetzt kannst du sagen, bei Sekunde 0 mach das, bei 10 mach das, ... bei 60 geh wieder auf 0.
Die Werte kannst du natürlich frei wählen.

Eleganter wäre es hier vielleicht mit Select Case, aber ich weiß nicht ob Bascom das unterstützt. Ist aber mit If genau so gut machbar, nur etwas unleserlicher.
 
Ich habe das "Incr" ganz vergessen. Und "Select case" versteht bascom. Das ist auch eine Anwendung, die ich noch nicht kenne aber öffters schon gesehen habe.

Ich habe mir überlegt, ob ich auf Assembler oder C umsteigen werde. Was meinst Du dazu?
 
Select Case ist quasi eine ineinander verschachtelte If.
Sagen wir du hast eine Zählervariable die ständig ihren Wert ändert.
Jetzt willst du bei Wert 1 dies ausführen, bei Wert 2 jenes und bei Wert 3 welches.
Könnte man mit If machen:


CodeBox BascomAVR
If Wert = 1 Then
   dieses
ElseIf Wert = 2 Then
   jenes
ElseIf Wert = 3 Then
   welches
End If

Oder eben mit Select Case:


CodeBox BascomAVR
Select Case Wert
   Case 1
      dieses
   Case 2
      jenes
   Case 3
      welches
End Select

(Codes sind freihand getippt und nicht auf Fehler getestet, das Prinzip sollte aber klar sein)

Case ist etwas übersichtlicher, wohingegen If allerdings flexibler ist, du kannst ja mehrere Bedienungen gleichzeitig abfragen.
Übersetzt wird aber beides gleich, von daher ist hier nur die Optik entscheidend.

Assembler, C und umsteigen...
Da wird dir vermutlich jeder was Anderes sagen. Jede Sprache hat ihre Vor- und Nachteile. Ich selber nutze Assembler für fast alle Projekte, nur bei komplexen Sachen nehme ich LunaAVR (ist Bascom ähnlich).
Wo liegen die Unterschiede...
Bascom/LunaAVR und C sind Hochsprachen. Assembler ist Maschinensprache.
Hochsprachen nehmen dir viel Arbeit ab, du kommst schneller zum Ziel. Der Nachteil hierbei ist aber, du weißt nicht wie der Compiler es wirklich umsetzt, was zu ungewollten Nebeneffekten führen kann. Bei Assembler bist du selber Schuld wenn was nicht richtig funktioniert. Die CPU verarbeitet 1zu1 deine Befehle (nicht das was Bascom irgendwie interpretiert hat). Assembler nutzt aber für die Befehle ausschließlich Abkürzungen, die man erst mal lernen oder nachschlagen muss.
Basic zu C, das ist so eine Sache. Ich kann mich mit der C Syntax einfach nicht anfreunden mit dem ganzen Geklammere und den ständigen Semikolons am Zeilenende, aber nicht überall... Wenn man es nicht gewöhnt ist, ist es wie Topfschlagen im Minenfeld, was Flüchtigkeitsfehler angeht.

Ich persönlich rate es jedem an sich zumindest mal mit Assembler zu befassen. Ob man die Sprache nachher wirklich für seine Projekte nutzt oder nicht ist egal. Allerdings bekommt man so ein ganz anderes Auge und viel mehr Verständnis über die Hardware und was "unter der Haube" wirklich passiert. Das hilft auch bei der Fehlersuche. Nur wirkliche Ergebnisse, wie mal eben innerhalb einer Schulstunde eine LED zum Blinken bekommen, das ist eher unwahrscheinlich. Das muss dir da bewusst sein.
 
Assembler mal eine LED zum blinken bringen, dachte ich, alles weitere kommt dann schon. Na ja, so habe ich mit Bascom bzw mit Arduino, welches ja C ist, auch angefangen. Allerdings hatte mich das mit den klammern auch gestört. Dann bekam ich das Bascom Lehrbuch und die CD und habe mich damit befasst.

Ok, select case sollte jetzt auch klar sein. Dann versuche ich mal mein Glück.
 
Select case habe ich mal die Ampel in etwa so geschrieben.
Select Case A
Case 1: Rot=1, Gelb =0, Gruen =0
Case 2: Rot =1, Gelb =1, Gruen =0
Case 3: Rot =0, Gelb =1, Gruen =0
Case 4: Rot =0, Gelb =1, Gruen =1
Case 5: Rot =0, Gelb =1, Gruen =0
End Select
Aber woher weiß jetzt case 1 bis 5 wie lange an/aus sein soll?
Habe die Bascom Hilfe dazu genommen.
 
Man separiert Befehle nicht mit Komma sondern mit Doppelpunkt. Komma nur bei Parametern ;)
Hier geht es noch so, du kannst aber auch untereinander schreiben, falls es sonst zu lang wird.
Sonst kann man das so machen, zum Testen auf jeden Fall sinnvoll.

Jetzt stellt sich die Frage wo A erhöht wird und wie oft (Frequenz).

Edit: Also Schritt 4 ist mir neu ;)
 
Zuletzt bearbeitet:
Wenn die Datei, die mit der Anweisung

CodeBox C
#include    <avr/io.h>
automatisch für jeden Controller eingebunden wird (in C im AtmelStudio, für Assembler heißt sie wohl [controler]def.inc und man muß den Typ selbst festlegen), so aufgebaut wäre, wie bei den Xmegas, enthielte sie u.a. folgendes:
Code:
#define    TC1_OFF_bm            0x00
#define    TC1_DIV1_bm           0x01
#define    TC1_DIV8_bm           0x02
#define    TC1_DIV64_bm          0x03
#define    TC1_DIV256_bm         0x04
#define    TC1_DIV1024_bm        0x05
#define    TC1_EXT_FALLING_bm    0x06
#define    TC1_EXT_RISING_bm     0x07

#define    TC1_CS_gm             0x07
...
#define    SM_IDLE_bm                    0x00
#define    SM_ADC_NOISE_REDUCTION_bm     0x02
#define    SM_POWER_DOWN_bm              0x04
#define    SM_POWER_SAVE_bm              0x06
#define    SM_STANDBY_bm                 0x0C
#define    SM_EXTENDED_STANDBY_bm        0x0E

#define    SM_gm                         0x0E
Statt 0x0E kann man auch schreiben 0b00001110 (oder 14, aber dann sieht man die Bits nicht).
Das ist eine Bitmap. Da braucht man weder die einzelnen Bitpositionen der Bits SM0, SM1 und SM2 zu kennen, noch braucht man sie alle einzeln zu schreiben und zu schieben. (Das macht zwar der Präprozessor, aber ich finde die Schreibweise einfach total unübersichtlich)
In der Datei würde dann auch nicht nur stehen:

CodeBox C
#define    TC1_ICIE_bm    0x20
sondern auch :

CodeBox C
#define    TC1_ICIE_bp        5
In der jetzigen Form der Datei steht nur die zweite Definition und das angehängte _bp fehlt, weil ja für alles nur die BitPosition angegeben ist.
Bei den Xmegas sind jeweils Bitmap, Bitposition und Groupmap und Groupposition angegeben (SM0..2 und CS10..12 sind im obigen Beispiel Gruppen).
Du verwendest die Namen
Natürlich verwende ich Namen für die Bitmasken.
Wenn ich dem Prescaler sagen will, er soll durch 1024 teilen, schreibe ich

CodeBox C
TCCR1B |= TC1_DIV1024_bm
und um sicherzugehen, daß alle Bits für den Prescaler auf Null sind schreibe ich davor noch

CodeBox C
TCCR1B &= ~TC1_CS_gm
Daß die WG-bits auf zwei Register aufgeteilt sind, ist hier etwas umständlich. Beim Xmega sind alle in einem.
So wie Du schreibst

CodeBox Assembler
LDI Rd, (1<<CS10)|(1<<CS12)|(1<<WGM12)
OUT TCCR1B, Rd
löschst Du übrigens die Bits ICNC1 und ICES1.
Um das zu vermeiden, müßtest Du (wenn ich mich recht erinnere) schreiben

CodeBox Assembler
LDI Rd, (1<<CS10)|(1<<CS11)|(1<<CS12)|(1<<WGM12)|(1<<WGM13)
COM Rd
IN Re, TCCR1B
AND Re, Rd
LDI Rd, (1<<CS10)|(1<<CS12)|(1<<WGM12)
OR Re, Rd
OUT TCCR1B, Re
Da ist es doch wesentlich einfacher, gleich die Namen der Bitmaps TC1_CS_gm und TC1_DIV1024_bm zu benutzen. Die entsprechenden WG-Bits kann man mit den dazu passend definierten Masken auch gleich löschen und setzten.

Aber zurück zu Bascom. Ich weiß nicht ob die Atmel Toolchain benutzt wird oder was eigenes, aber da hier vieles vereinfacht und vor dem Benutzer verborgen wurde, nehme ich mal an, daß das Zweite zutrifft.
Wer den Bascom Compiler installiert hat, kann ja mal in der Datei [controler]def.dat nachsehen, wie das dort definiert wurde.
 
Wie @TommyB schon schrieb, jede Sprache hat ihre Vor- und Nachteile.

Ich benutze gerne C, weil es eine der mächtigsten und nach Assembler die maschinennächste Sprache ist. Es gibt zwar neuere und wohl auch mächtigere Sprachen, wie Haskell, Rust o.ä., die brauchen aber leistungsstarke Prozessoren. Daher bleibe ich für AVRs bei C.

In Assembler schreibe ich nur noch zeitkritische Programmteile. Für Programme, die mehr als eine Bildschirmseite umfassen, ist eine Hochsprache einfach praktischer.

Wenn Du wissen willst, wie der Prozessor und die Peripherie genau funktionieren, dann wirst Du von Assembler am meisten profitieren.
 
Hatte ich eigentlich schon gesagt wo ;)
Durch den Timer und die innere If hast du ja einen Taktgeber im Sekundentakt.
Zähl doch einfach die Sekunden.
Hoch zählen kannst du selber (Incr)
Und Verzweigen (If) auch.
Und jetzt kannst du sagen, bei Sekunde 0 mach das, bei 10 mach das, ... bei 60 geh wieder auf 0.
 
Ich weiß nicht, ob das so richtig ist. Kann ja leider keine Tests machen, nicht einmal kompilieren.
$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 40
$swstack = 16
$framesize = 32
$baud = 19200

Dim Count As Byte
Dim A As Byte

Ddrc = &B_11_1111
Portc = &B00_0000

Ddrd = &B_1111_1111
Portd = &B0000_0000

Rot Alias Portd.5
Gelb Alias Portd.6
Gruen Alias Portd.7

Ocr2 = 249
Config Timer2 = Ctc , Prescale = 256 , Clear_timer = 1
On Oc2 Isr_timer2
Enable Oc2
Enable Interrupts

Main:
Reset Watchdog
Goto Main

Isr_timer2:
Incr Count
If Count = 0 Then
Case1
Elseif Count = 10 Then
Case2
Elseif Count = 20 Then
Case3
Elseif Count = 30 Then
Case4
End If
Select Case A
Case 1: Gruen = 1 : Gelb =0 : Rot = 0
Case 2: Gruen = 0 : Gelb =1 : Rot = 0
Case 3: Gruen = 0 : Gelb =0 : Rot = 1
Case 4: Gruen = 0 : Gelb =1 : Rot = 1
End Select
Return
 
Ähm, nein, das ist nu komplett durcheinander geraten.
Vom Frequenzzähler der Code. Timer1 brauchen wir hier nicht, wir wollen ja nur den Sekundentakt, also Tick1s.
Darin einfach eine Veriable erhöhen und auswerten (vorher definieren). Die letzte Select Case ist richtig, bis auf eine Kleinigkeit. Man fängt bei 0 an zu zählen ;)
 
Ich habe jetzt deinen Code aus #573 genommen und Count As Word dimensioniert. Somit kann ich If Count 3750 Then case1 für 30 Sekunden einstellen. Aber diese blinkt in diesem Takt. Weitere Überlegungen sind in Arbeit. Kannst mir aber weiter helfen. Danke!
 
Ungetestet, aber ich würds so machen:


CodeBox BascomAVR
' Compiler konfiguration
$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 40
$swstack = 16
$framesize = 32
$baud = 19200

' Globale Variablen
Dim Count As Byte
Dim Sekunden As Byte

' Aliase
Rot Alias Portd.7
Gruen Alias Portc.3
Blau Alias Portd.6
Key Alias Pinb.0


' Initialisiere Hardware
Init:

   ' Hardware konfiguration
   Ddrb = &B1111_1110
   Portb = &B0000_0001
   Ddrc = &B__11_1111
   Portc = &B__00_0000
   Ddrd = &B1111_0111
   Portd = &B0000_1000

   ' Konfiguration Timer 2 (CTC, 8MHz/256/249 = 8ms)
   Config Timer2 = Ctc, Prescale = 256, Clear_timer = 1
   Ocr2 = 249                     ' Setze Maximalwert für Timer 2 (CTC)
   On Oc2 Isr_oc2                 ' Setze ISR für Timer 2 Overflow Compare
   Enable Oc2                     ' Aktiviere Timer 2 Overflow Compare Int.

   ' Konfiguration Watchdog
   Config Watchdog = 2048         ' Setze Watchdog Timeout auf ~2sec
   Start Watchdog                 ' Aktiviere den Wachhund

   ' Aktiviere Interrupts (global)
   Enable Interrupts

' Goto Main ' -- nicht benötigt, Routine kommt eh als nächstes


' Haupt Anwendungsschleife
Main:

   ' Den Wachhund zurücksetzen
   Reset Watchdog

   ' Einschlafen (bis zum nächsten Interrupt)
   Config Powermode = Idle

GoTo Main ' und zurück


' ISR wenn Timer 2 compare event eintritt (alle 8ms, bei aktueller Konfiguration)
Isr_oc2:

   ' Erhohe internen Zähler
   Incr Count

   ' Wenn Zähler einen gewissen Wert erreicht hat, auf 0 setzen und abzweigen
   If Count = 125 Then
      Count = 0
      Gosub Tick_1s
   End If

Return


' Diese Routine wird jede Sekunde ausgeführt
Tick_1s:

   ' Sekunden erhöhen
   Incr Sekunden

   ' Werte nur zum testen.
   ' Aber 1, 2, 3, 4, 5 kann man später den Schaltzeiten anpassen.
   Select Case Sekunden
      Case 1 : Gruen = 1 : Gelb = 0 : Rot = 0
      Case 2 : Gruen = 0 : Gelb = 1 : Rot = 0
      Case 3 : Gruen = 0 : Gelb = 0 : Rot = 1
      Case 4 : Gruen = 0 : Gelb = 1 : Rot = 1
      Case 5 : Sekunden = 0
   End Select

Return

Jetzt frag ich dich aber: Warum?
 
Warum was?
Ich weiß jetzt nicht, ob case1 = eine Sekunde ist oder in case5 die Sekunden festgelegt werden.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)