Bascom BASCOM ; Erste Schritte zum Ausprobieren

Jede Interruptquelle besitzt eine bestimmte Adresse im Programmspeicher, die automatisch angesprungen wird, wenn der Interrupt lokal und global freigegeben war, zugeschlagen hat, und aufgrund seiner Priorität jetzt dran ist (also vereinfacht gesagt: abgearbeitet werden soll). Diese Adressen sind in der mit der $regfile-Direktive festgelegten Controller-Definitionsdatei (oder eben der Default-Einstellung bei den Options) hinterlegt, und insbesondere mit Namen für die Konstanten Adressen versehen.
Dieser Adressbereich, in der sich die ganzen Interrupt-Einsprungadressen wird als Interrupt Vektor Table bezeichnet. Da dort immer nur eine Instruktion reinpaßt, wird üblicherweise von dort in die eigentlichen Teilprogramme gesprungen, die den Interrupt behandeln sollen. Diese einzelnen Teilprogramme werden dann entsprechend als Interrupt Service Routine bezeichnet.
On blablabla ist jetzt die Bascom Anweisung, die für einen bestimmten Interrupt das Sprungziel (also die Adresse der ISR) in der IVT einträgt.
Der erste Parameter (in Deiner Frage URXC) muß also ein, im Regfile definierter Interrupt sein - also auch genau so geschrieben werden.
Der zweite Parameter (in Deiner Frage OnUartRx) muß eine, irgendwo im Code folgende Subroutine sein.
Bei Bascom gibts dazu (inzwischen) zwei Möglichkeiten:
Entweder - wie hier bisher - ein Label (Die Zeilen mit dem Doppelpunkt am Ende) als Sprungziel, und ein Return am Ende der ISR.
Oder eine echte deklarierte Sobroutine, die durch Sub und End Sub eingeschlossen wird....

Den Namen dieses Teilprogrammes/Subroutine kannst Du beliebig wählen/festlegen. Aufruf und Ziel müssen natürlich übereinstimmen. Thomas verwendet klingende Namen, um gleich beim lesen des Codes zu sehen, worums geht.

So, der ADC ist - wie auch der UART oder die Timer - ein mehr oder weniger eigenständiges periphäres Hardware Modul im Controller. Wie bei alle periphären Modulen hat Dein Programm darauf nur über bestimmte I/O-Register zugriff.
Mit Config ADC .... legst Du also fest, was Bascom für Dich in die Register des ADC-Moduls schreiben soll. Prinzipiell könntest Du aber auch selbst die Register beschreiben.
(Nebenbemerkung: eine Hochsprache ist eben eine Ebene höher abstrahiert, kapselt Dich von tieferen Innereien ab. Manche Hochsprachen sind da strenger - bei Bascom darfst Du hingegen trotzdem die tieferen Innereien manipulieren. Insbesondere dann vorteilhaft, wenn der Hochsprachen-Befehl irgendeine exotische Möglichkeit eines Controllers eben nicht berücksichtigt).

In der Configurationszeile hatten wir bisher also den Vorteiler des ADC automatisch festlegen lassen (Bascom stellt den auf möglichst schnell aber maximal 200kHz ein), und die Referenz auf interne AVcc eingestellt, und daß der ADC immer nur 'ne Einzelmessung vornehmen soll. Dadurch wird der ADC selbst gestartet (enabled, der Takt liegt an, und die Referenz wird entsprechend aufgeschaltet, 'ne ggf gewählte interne Spannungsreferenz wird auch gestartet).
Das folgende Start ADC sollte somit unnötig sein.
Der ADC ist "bereit".

Zielvariable=Getadc(Kanal) schaltet jetzt erstmal den gewählten Kanal auf den ADC (ADC-Multiplexer), löst dann eine Conversion aus, wartet bis die Messung fertig ist (25 Takte des vorgeteilten Taktes), liest des Ergebnis-I/O-Register aus, und weist dieses Ergebnis der Zielvariable zu.
Es wird also nach dem Start der eigentlichen Messung gewartet, bis der ADC ein Ergebnis hat.
Der ADC kann aber auch einen Interrupt auslösen, wenn er fertig ist.
Bleiben wir trotzdem erstmal im Einzel-Schuß-Modus.
Nach Config ADC = ... ist der ADC also bereit. Getadc könne wir nicht verwenden - das würde ja warten. Wir müssen also noch selbst den zu messenden Kanal auf den Multiplexer schalten (ADMUX). Wenn sich der Kanal nicht ändern soll, muß das natürlich nur einmal gemacht werden.
Die eigentliche Messung wird dann durch setzen des ADSC-Bits (ADC Start Conversion) im ADCSRA-Register (ADC Control an Status Register) ausgelöst. (sollte beim Mega8 sogar mit SBI ADCSRA, ADSC gehen). Wenn die Messung fertig ist, soll nun ein Interrupt erfolgen, der das Ergebnis ausliest und zuweist.

Neben dem Einzelschuß-Modus kann der ADC auch auf Dauerfeuer geschaltet werden. Nach einer Messung legt er das Ergebnis (wie immer) in seinem Ergebnis-Register ab, und startet sofort'ne neue Messung. Aus dem Ergebnis-Register ist also immer das aktuellste ergebnis ablesbar. Wenn immer nur derselbe Kanal gemessen werden soll, kann man dadurch auf Interrupts oder warten ganz verzichten, sondern schaut nur bei Bedarf ins Ergebnis-Register.
Auch im Dauerfeuer-Modus kann(!) natürlich der Interrupt bei fertig verwendet werden, zB um den Kanal für die nächste Messung zu wechseln und so...

Bei vielen aktuelleren Controllern gibt es außerdem noch einen "spezielleren" Einzelschuß-Modus, wo andere periphäre Hardware-Module auf den Auslöser drücken dürfen - die Timer, der Analog-Comperator, ein externer IRQ usw...
(im Prinzip eine Vorstufe des flexiblen Event-Systems der neuen X-Core-Controller).

Um mich mit dem Problem aus #416 zu beschäftigen fehlt mir gerade die Zeit...
 
Also:
On yyy zzz
yyy ist der Event- also Interruptname des Controllers. der ist fest.
zzz ist die Routine die es behandelt. Ich nehme als Name typischerweise Onyyy, kann aber jeder beliebiger Name sein. Manchmal wandel ich es auch etwas ab, OnUrxc ist mir dann doch etwas zu kryptisch.

Hintergrundwissen:
Der Controller ist eigentlich recht dumm. Bekommt er Strom erwartet er an Speicherzelle 0000 einen Befehl. Den arbeitet er ab und springt zum Nächsten, also 0002 (Befehle sind 2 Byte breit).
Wenn aber Interrupts genutzt werden ist dies etwas anders. Da springt der Controller bei einem Interrupt nicht zu 0000 sondern zu einer Speicheradresse die in der Interrupt Vector Table (IVT) hinterlegt ist. Jede "Zelle" der IVT fasst nur einen Befehl (bei größeren Controllern 2). Typisch sind also die Befehle (Assembler) RJMP und RETI (falls unbenutzt). In Bascom wären das Goto und Return.

Mal ein Beispiel für einen ATtiny2313:


CodeBox Assembler
// Include chip defination
.INCLUDE <tn2313def.inc>

// Interrupt vector table
.ORG    0x0000                RJMP Init
.ORG    INT0addr            RETI
.ORG    INT1addr            RETI
.ORG    ICP1addr            RETI
.ORG    OC1Aaddr            RJMP OnOC1A
.ORG    OVF1addr            RETI
.ORG    OVF0addr            RETI
.ORG    URXC0addr            RETI
.ORG    UDRE0addr            RETI
.ORG    UTXC0addr            RETI
.ORG    ACIaddr                RETI
.ORG    PCIaddr                RETI
.ORG    OC1Baddr            RETI
.ORG    OC0Aaddr            RETI
.ORG    OC0Baddr            RETI
.ORG    USI_STARTaddr        RETI
.ORG    USI_OVFaddr            RETI
.ORG    ERDYaddr            RETI
.ORG    WDTaddr                RETI
.ORG    INT_VECTORS_SIZE


.ORG definiert an welcher Stelle des Programmspeichers geschrieben werden soll. 0x0000 ist das Init, also wenn der Controller Strom bekommt. OC1Aaddr ist die Adresse in der IVT welche angesprungen wird wenn der Timer1, bzw. dessen Comperator A Alarm schlägt (=On yyy zzz). Alle anderen Zeilen sind eigentlich unnötig, sind nur pro-forma drin. Nicht aktivierte Interrupts (!=Enable) werden nicht angesprungen. Hier zur Fehlersicherheit mit RETI gefüllt (=Return). Zum Schluss wird sicherheitshalber an das Ende der IVT gesprungen.
Ignoriere das "addr" am Ende und du hast die Namen für yyy. Die sind aber je nach Chip unterschiedlich.

Um die Tabelle selbst musst du dich aber nicht kümmern, außer in Assembler.
Das macht Bascom für dich, anhand der On Befehle, und Enable Interrupts.

So, BTT.
Vom Prinzip ist es nichts anders wie mit dem Timer.
Du musst den ADC initialisieren (hattest du früher ja schon).
Dann musst du Bascom sagen dass er die Interrupt Routine (OnADC) dort findet. Genau so wie beim Timer.
Dann musst du den ADC Interrupt aktivieren (Enable)
Jetzt brauchen wir noch Teilprogramm 4 (OnADC). Das fügen wir mal unten an.
Um nur mal etwas zu sehen, vor allem welche Werte gemessen werden, wäre eine Ausgabe des ADC Wertes an die serielle Schnittstelle sinnvoll. Hattest du ja auch schon mal.
Wieder keine Änderung in der Main Loop, der Controller arbeitet nur on-demand.

Edit: LotadaC war schneller ^^
 
Der Assembler Code ist nur eine Init vom Interrupt und kein vollständiges Programm?
Ich habe den Code jetzt so geschrieben. Leider keine Ausgabe im Terminal zu sehen.

CodeBox BascomAVR
$regfile = "m8def.dat"                  'Controllerdefinitionsdatei einbinden
$crystal = 8000000                      'Systemtakt angeben (Baudrate)
$hwstack = 40                           'Stacks
$swstack = 16
$framesize = 32
$baud = 19200                           'Baudrate UART
Dim Zeichen As Byte                     'UART-Empfang
Dim C As Byte
Dim Fla As Byte
Dim Pause As Byte
Dim Pausex2 As Byte
Dim Tov1cnt As Byte
Dim Countdown As Word
Dim Poti As Word
Ddrc = &B_11_1111                       '1 ist Ausgang 0 ist Eingang
Portc = &B00_0000                       'auf Hi oder Low setzen
Ddrd = &B_1111_1011
Portd = &B0000_0100
Rot Alias Portd.7
Blau Alias Portd.6
Gruen Alias Portc.3
Key1 Alias Pind.2
'Phasenkorrekter 8-Bit-PWM, PWM-Frequenz=245Hz
Config Timer1 = Pwm , Prescale = 64 , Compare A Pwm = Clear Down , Compare B Pwm = Clear Down
Config Adc = Single , Prescaler = Auto , Reference = Avcc
On Urxc Onuartrx
Enable Urxc
On Timer1 Ontimer1overflow
Enable Timer1
On Int0 Onkey1pressed
On Adc Onadc
Enable Adc
Enable Int0
Enable Interrupts
Main:
   'If Ischarwaiting() = 1 Then Gosub Onuartrx
   'If Tifr.tov1 = 1 Then Gosub Ontimer1overflow
   'If Key1 = 0 Then Gosub Onkey1pressed
   Goto Main
Onuartrx:
   Zeichen = Inkey()                    'dann Zeichen aus Puffer lesen
   If Zeichen = "1" Then                'Wenn Zeichen = "1"
      Rot = 1                           '...
      Print "Led An"
   Elseif Zeichen = "0" Then            'Wenn Zeichen = "0"
      Rot = 0                           '...
      Print "Led Aus"
   Elseif Zeichen = "t" Then            'Wenn Zeichen = "t"
      Toggle Rot                        '...
      Print "Toggle Led"
   Elseif Zeichen = "?" Then            'Wenn Zeichen = "?"
      If Rot = 0 Then                   'Dann wenn Led aus ist...
         Print "Led ist aus"
      Elseif Rot = 1 Then               'sonst wenn Led an ist...
         Print "Led ist an"
      End If
   Elseif Zeichen = 0 Then              'bei 0x00 PWM deaktivieren
      Config Timer1 = Pwm , Prescale = 64 , Compare A Pwm = Disconnect , Compare B Pwm = Disconnect
   Elseif Zeichen = 255 Then            'bei 0xFF PWM aktivieren
      Config Timer1 = Pwm , Prescale = 64 , Compare A Pwm = Clear Down , Compare B Pwm = Clear Down
   Else
      Countdown = Zeichen * 60
      Config Timer1 = Pwm , Prescale = 64 , Compare A Pwm = Clear Down , Compare B Pwm = Clear Down
   End If
Return
Ontimer1overflow:
   Set Tifr.tov1                        'Überlaufflag zurücksetzen
   Incr Tov1cnt                         'überlaufzähler inkrementieren
   If Pause = Tov1cnt Then              'entspricht dem 2ten "waitms pause"
      Fla = Rnd(200)
      Fla = Fla + 55
      Pwm1a = Fla
   End If
   If Pausex2 = Tov1cnt Then            'entspricht dem ersten "waitms pause"
      Pwm1b = 255 - Fla
      Pause = Rnd(10)
      Pause = Pause * 6
      Pausex2 = Pause
      Shift Pausex2 , Left              'pausex2=pause*2
      Tov1cnt = 0
   End If
   Incr C
   If C = 245 Then                      'Überläufe zählen (245)
      C = 0                             'Zähler zurücksetzen
      Toggle Gruen                      'LED toggeln
      If Countdown = 0 Then
         Config Timer1 = Pwm , Prescale = 64 , Compare A Pwm = Disconnect , Compare B Pwm = Disconnect
      Else
         Decr Countdown
      End If
   End If
Return
Onkey1pressed:
   Config Timer1 = Pwm , Prescale = 64 , Compare A Pwm = Clear Down , Compare B Pwm = Clear Down
   Countdown = 720
Return
Onadc:
   Poti = Getadc(0)
   Print "Poti: " ; Adc
Return
 
Lass dich nicht verwirren, wie gesagt, Bascom kümmert sich um die IVT.
Aber ja, vom Prinzip hast du Recht. Aus Sicht des Controllers kommt erst die IVT (das ist Pflicht), dann dein Code. Normalerweise beginnend mit Init, dann Main, dann der Rest. Aber es ist dein Code, könntest du auch anders legen.
War nur etwas Hintergrundwissen.

Der Code sieht an sich gut aus. Die Frage ist nur ob er das macht was er soll ;)
 
In der Zeile 99 steht Print "Poti: " ; Adc
anstatt Print "ADC: " ; Poti
Aber auch das ist nicht die Ursache, dass im Terminal nichts angezeigt wird. Die Eingaben (1, 0, t, ?) werden ausgegeben. Wo liegt der Fehler?
 
Die Ursache liegt darin dass OnADC nie aufgerufen wird.
Warum nicht?
Weil du ihn als Single Shot konfiguriert hast, und nicht als Free Running.

Richtig wäre:
Config Adc = Free, Prescaler = Auto, Reference = Avcc
;)

Noch mal: Single: Du musst die Messung selber anstoßen. Free: Sobald die Messung fertig ist wird direkt die nächste gestartet.

Edit: Ich weiß jetzt grad nicht mehr, ob man die erste Messung selbst anstoßen muss... :hmmmm:
 
Zuletzt bearbeitet:
Okay, Free war die Lösung, aber es gibt nur den Wert "0" aus.

Auf dem ersten Blick sah das wie Werbung aus. Wenn ich jetzt gebeten werde zu spenden, was ich nie vermutet hatte, muss ich mir ein anderes Hobby suchen.
Du möchtest das Forum unterstützen? Klick mich oder werde Premium Mitglied.
Du möchtest mich unterstützen? Klick mich.
 
Das wundert mich jetzt. Alles richtig angeschlossen? Probier noch mal mit dem Poti.

Und nein, zumindest keine Werbung im Sinne wie du dachtest.
Ich habe das in meiner Signatur drin um erstens das Forum zu unterstützen, und ggf. auch mich, falls wer möchte.
Ohne das jetzt hier groß auszuführen, lies mal meinen Blogbeitrag:
Der Kaffespende Link steht hier übrigens auf jeder Seite, allerdings ganz unten rechts, wo er kaum auffällt.
Ich wollte ihn nur mal etwas deutlicher erwähnen :)
 
10k Poti (in Mittelstellung) und 10k Widerstand davor. Das war zu viel. Widerstand weg und Poti runter gedreht, wurden die Werte höher. Ein 1k Poti würde reichen, warum auch immer.
 
Also normalerweise ist der ADC extrem hochohmig, also sehr sensibel.
War zumindest bei denen die ich nutze (ATmega48/88/168/328 und ATtiny13A) immer so.
Jetzt muss mal einer der Bascom Experten ran. Muss man erst noch Digital I/O deaktivieren?
 
Ach ja, mit einem ATtiny13a ich hatte eigentlich die Absicht, die Kerze in einer bestimmten Zeit abzuschalten. So wie mit dem Atmega8 mit Energiesparmodus.
Aber den Code einfach auf den Tiny drauf geht nicht, da im Datenblatt überhaupt kein TIFR gefunden habe. Da muss es wohl eine andere Möglichkeit geben.
 
Ne, da ist die Hardware zu unterschiedlich. Der Tiny13(a) hat keinen 16bittigen Timer1.
Aber den 8bittigen Timer0. muss man dann umstricken.

Was den ADC angeht kann ich grad so nichts zu sagen. Und LotadaC erwähnte ja bereits dass er grade kaum Zeit hat.
Normalerweise von GND zu (Aref) je nach dem was eingestellt ist. Da die AVRs im Bereich >50 Megaohm liegen sollte es eigentlich keinen Unterschied machen ob 1k oder 10k oder gar 100k Poti.
 
Ich habe doch so ein Atmega8 Board, ist irgendwo abgebildet. LotadaC meinte, dass der Aref direkt mit Vcc verbunden ist. Kann das eine Ursache sein? Muss der Aref einen extra Stromversorger haben?
 
Jain. Aref ist der Referenzpunkt für deinen ADC. Also was da an Spannung anliegt definiert die maximale Eingangsspannung.
Jetzt kann Aref aber Ein- und Ausgang sein. Der Controller kann selber eine Spannung generieren (typisch rund 1,1V und 2,56V) oder es auf VCC legen. Oder eben eine externe Spannung erwarten. Da bei dir der Pin direkt mit VCC verbunden ist müsstest du schon Avcc oder VCC(?) nehmen (im Config ADC Befehl). Tust du ja aber. Also Poti zwischen VCC und GND, Mitte an ADC. Dann müsstest du Werte einstellen können die zumindest - wenn nicht genau - von 0..1023 gehen.
 
wenn nicht genau - von 0..1023 gehen.
Das hat ja auch alles in #205 geklappt. Nur dass damals aus den 10Bit eine 8Bit gemacht wurde (Shift Poti , Right , 2 in Zeile 58).
Wenn ich den Code

CodeBox BascomAVR
Do
   Wert = Getadc(0)
   Print "ADC: " ; Wert
   Waitms 1500
Loop
End
nehme funktioniert es auch. Nur bei #423, mit dem selben Poti und 10k zwischen Schleifer und ADC kommt nur "0". Wenn ich den 10k entferne und den Poti runter drehe kommen dann ziehmlich zum Schluss die höheren Zahlen. Ist doch eigenartig.
 
Hallo zusammen,

ich habe nun das Thema nicht komplett durchgelesen (nur die letzten Beiträge), es kann somit sein, dass ich etwas übersehen habe. Ausserdem programmiere ich ja nicht in BascomAVR und habe den Bascom Sourcecode nur überflogen.

Für mich sieht es fast so aus, als wäre der Mikrocontroller defekt.

Ursache könnte sein:

(1) ADCn Pin defekt. Wenn irgendwann einmal die normale IO Funktion des ADCn Pin genutzt wurde und zwei Signale gegeneinander gearbeitet haben. Oder wenn eine zu hohe (über VCC) oder zu negative Spannung (unter 0V) am ADCn Pin lag (Schutzdioden defekt).

(2) AREF Bereich defekt. Wenn zum Beispiel der AREF Pin an einer externen Spannung angeschlossen war (zB VCC oder GND) und die interne Referenzspannung eingestellt wurde.

Dirk :ciao:
 
Oder es hat den Poti zerlegt, das könnte auch sein, ist mir auch mal passiert. Einfach mal mit Ohmmeter messen ob er noch seine rund 10k von einer zur anderen Seite hat.
Kann schnell mal passieren wenn man ihn als variablen Spannungsteiler nutzt (wie hier), der Schleifer aber auf einem Ausgang / VCC / GND liegt.
 
Wie ich aber schon sagte, bei anderen Codes, mit der selben Hardware funktioniert es. Ich muss heute Abend mal auf einen Steckboard aufbauen, nicht mein Atmega8 Board und dann nochmal testen.
Hat jemand (ich habe schon verschiedene gesehen) einen Schaltplan für die Versorgunsspannung bei Verwendung von ADC. Ich meine mit interner Aref Spannung und Kondensator und so.
 

Ü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)