Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


Betriebsystem für Bascom

Die im Text angesprochenen Bascom-Libraries befindet sich im Community-Download-Bereich, zusammen mit einem einfachen Beispielprogramm.

MYSYSLIB.ZIP

  • Bascom-Libraries --> In das Bascom-Lib Verzeichnis kopieren
    • Mybuffer.LBX Interne Hilfsfunktionen
    • Myunit.LBX UNIT Kontrolle
    • Mymsgque.LBX MESSAGE-QUEUE Kontrolle
    • Myrncom.LBX Kommunikations Module
  • Bascom-include-Files--> In ein Bascom Projekt- Verzeichnis kopieren
    • Myunit.BAS
    • Mymsgque.BAS
    • Myrncom.BAS
  • Bascom-Demo-TestProgramm
    • SYS.BAS, ausgelegt f. RNBFRA 1.2 ebenfalls in ein Projekt-Verzeichnis. Letztlich ist das Programm etwa 3700 Byte groß, also auch mit einer BasCom DemoVersion zu behandeln
  • Kommunikations-Test-Parter f. PC
    • RN_SERVER.EXE f. XP, W98, W2k
    • MFC42(d).DLL, was das jemandem fehlen sollte

Wenn alles verbunden und gestartet ist, steuert der PC im Sekundentakt die 4 LEDs auf der RNBFRA Karte.

Vorwort, Sinn und Zweck

Im Wesentlichen ist es die Aufgabe eine Betriebssystems, das Zusammenspiel der verschiedenen Programmteile, aus denen ja so eine µC-Applikation besteht, zu regeln. Die Nachteile des gewissen Overheads und der natürlich erforderlichen Restriktionen´sollten durch die Vorteile der Standardisierung und Modularisierung deutlich aufgewogen werden.

Bascom bietet für sehr viele AVR-intere Geräte und Peripherie Funktionen an, die oft ganz einfach durch "Config" Anweisungen initiiert werden können und dem Programmierer viel Detailentwicklungsarbeit abnehmen. Ein unschätzbarer Vorzug. Was aber immer wieder bleibt, ist das Problem des Zusammenwirkens dieser Komponenten und die gewünschte Mehrgleisigkeit, "Multithreading", des gesamten Projekts.

Es soll hier ein Konzept vorgestellt werden, das hauptsächlich in Hinblick auf eine Standardisierung der Rechner-Kommunikation entwickelt wurde. Also µC <-> PC und µC <-> Peripherie.

Das verwendete "Message-Queueing" ist natürlich keine neue Erfindung, jeder Windows-Programmierer kennt das. Das Problem war es, eine Form zu schaffen, die sich in Bascom so eingliedern läßt, daß auch die "normale" Verwendung des Compilers möglich ist. Nur an den Schnittstellen zu den Sys-Funktionen müssen die Konventionen eingehalten werden.

Anmerkung: Dieses Multi-tasking beruht natürlich darauf, daß so ein einzelnes "Task-Slice" kurz gehalten wird und keine wesentlichen Wait-States beinhaltet. Schließlich muß für die anderen ja auch was übrigbleiben. Auch ausführlich "FOR" Schleifen sollten zu Einzel-Slices umgewandelt werden, d.h. bei jedem Aufruf EIN Step. Was daraus folgt, ist, daß die einzelnen Tasks im Grunde als "State-Machine" arbeiten, z.B.

  • FOR-Start
  • FOR-Step
  • FOR-Limit

Noch eins: Das System betrachtet den Anwender nicht als seinen natürlichen Feind, d.h. etliche Formal-Prüfungen der Call-Parameter mit Fehlercodes etc. sind nicht implementiert. Bitte also das Ganze pfleglich und mit etwas Liebe zu behandeln.

Begriffe

  • Units: Ein Programmteil, der sozusagen mitspielen will, wird als Unit od. "Einheit" bezeichnet. In den meisten Fällen wird so eine Unit die verschiedenen Funktionen umfassen, die für die Bedienung eines bestimmten Device od. "Gerät" erforderlich sind.
  • Class/Ident: Es gibt Geräteklassen, die sich durch ein Set von "Methoden" definieren, die auf eines oder mehrere tatsächliche Geräte angewendet werden können. Anschauliches Beispiel: die Klasse "ADC", die für 8 Channels angewendet werden kann. Grundsätzlich werden die ADC-Channels ja völlig gleich behandelt, aber für jeden Kanal gelten unter Umständen verschiedene Parameter: Natürlich die Kanal-Nummer 0-7, aber auch Umrechnungsfaktoren und Grenzwerte

Anmerkung: Aus Sicht des Systems sind das einfach zwei Bytes, die zusammen auf diesem Rechner eindeutig sein müssen.

  • Tags: Jeder Class/Ident-Ausprägung wird ein Tag ("Etikett") zugeordnet, das einen vom System verwalteten Bereich bezeichnet, der für die Kommunikation Unit <-> System erforderlich ist. Tags werden bei jedem Aufruf an das System verwednet und bei Aufrufen durch das System immer mitgeliefert.
  • Message-Queueing: An sich können alle Funktionen eines Units "normal" im Rahmen des Bascom verwendet, also aufgerufen werden. Doch jede Unit bietet wenigstens eine "Sub"-function an, die auch indirekt über Messages angesprochen werden kann und daher auch ein standardisiertes Format haben muß.
  DECLARE SUB unit-entry ( Tag AS WORD, Byval cmd as BYTE, Byval Param as WORD ) 

Das ist auch die SUB, die beim Einlangen einer Nachricht über das Netzwerk aufgerufen wird.

  • Tag ist das erwähnte Etikett, mit dem die SUB alle ihre Referenzen zu privaten Daten und individuellen Parametern (im Flash Speicher) verwaltet.
  • Cmd ist eine Aufrufspezifischer Code, der über die Art des Aufrufes Auskunft gibt. Einige Codes sind fest durch das System vergeben, andere können beliebig von der Geräteklasse für spezielle Befehle festgelgt werden
  • Param ist ein Beiwert zu CMD, der daher völlig unterschiedliche Bedeutung haben kann.

TAG-Referenzen

Sysunits.png

Das System findet durch den Tag die zugeordnete SUB-Routine (Vector). Die Unit hingegen findet durch die System-Function "Unit_REF()" ihre privaten Daten, und kann auch durch "Unit_parbyte(tag), Unit_parword(tag),Unit_parlong(tag),Unit_parsing(tag)" ihre individuellen Parameter abfragen.

Die ersten beiden Bytes der Flash-Parameter sind genormt und beinhalten den Class-Code und Identifier der Unit. Die übrigen sind vom System nicht festgelegt und können beliebige Werte beinhalten.

Message-Queue

Sysmsg.png

  • Durch msg_enqueue() wird ein indirekter Aufruf in die Queue gestellt. Es gibt keine Festlegung darüber, wann und wo dieser Call erfolgen kann.
  • Die Abarbeitung erfolgt FIFO durch msg_dequeue(). Bei jedem Aufruf wird EINE Message (wenn eine vorhanden ist) abgearbeitet. Auch für diesen Aufruf gibt es keinen zwingenden Platz, doch ist es zweckmäßig und empfehlenswert, ihn nur einmal an zentraler Stelle (DO..LOOP) zu tätigen.

Anmerkung: Enqueue() und Dequeue() führen sowohl Sub-Vector als auch Tag. "Vector" hat Vorrang, wenn er aber nicht angegeben wird (NULL), wird eben der Standard-Unit-Vector verwendet. Sonderfall: Bei Angabe von "Vector" ist die Befüllung von TAG nicht zwingend. Dadurch kann auch jede x-beliebige sub-function aufgerufen werden, nur das Declare-Format MUSS stimmen. ("TAG" ist beim Aufruf dann eben NULL).

SYS.BAS Demo

Statt umfangreicher theoretischer Abhandlungen einfach das Sys.BAS Demoprogramm mit einigen Kommentaren.

$regfile = "m32def.dat"
$crystal = 8000000

Const Tmr_c_prescale = 64
Const Tmr_c_preload = 131

$baud = 9600
$hwstack = 64
$swstack = 256
$framesize = 64


Const Max_c_unit = 8
Const Max_c_msg = 16

Const Rx_bufsize = 48

$include "myrncom.bas"
$include "myunit.bas"
$include "mymsgque.bas"

Hier erfolgen die Definitionen für die Library

  • Die Timer-Werte sind für 1 mS Interrupt bei 8MHZ ausgelegt.
  • Max_c_unit gibt an, wieviele Units maximal verwendet werden können.
  • Max_c_msg gibt an, wieviele Messages gleichzeitig verwendet werden können.
  • rx_buffersize gibt die Größe des (Ring)Buffers für den RNCOM-Empfang an.
  • Include: in den definitions-includes werden alle notwendigen Werte festgelegt.
'-------------------------------------------------
'           Common Commands
'-------------------------------------------------
Const Cmd_nop = 0
Const Cmd_setup = 1
Const Cmd_init = 2

Const Cmd_time = 4
Const Cmd_set = 5
Const Cmd_msg = 16

Das sind Muster für festgelegte Codes für Kommandos. Dem System ist es gleich, was man definiert. Es muß halt in ein Byte reinpassen.

'-------------------------------------------------
'           RNBFRA I2C
'-------------------------------------------------
Config I2cdelay = 5
Config Scl = Portc.0                                        'Ports fuer IIC-Bus
Config Sda = Portc.1
'-------------------------------------------------
'           RNBFRA I2C Addresses
'-------------------------------------------------
Const Pwr_adr = &H74                                        'I2C Adress Powerport
Const Out_adr = &H72                                        'I2C Adress Extension-Out Port
Const Exp_adr = &H7E                                        'I2C Read Adress Expansion-In Port


Const Pwr_default = &HFF                                    'I2C Powerport
Const Out_default = &H00                                    'I2C Extension-Out Port

Das ist dem RNBFRA-User alles vertraut, braucht man wohl nicht zu erklären.

'-------------------------------------------------
'           Class IDs
'-------------------------------------------------
Const Cls_pcf = &H42                                        ' ASCII "B"

Das sollte man für den Demo-Test nicht ändern, da es auch im PC-Programm so verwendet wird.

'-------------------------------------------------
'           CLASS entries     R8:R9=Flash Param  (restore indiv.params)
'-------------------------------------------------
Declare Sub Pcfclass(byval Tag As Word , Byval Cmd As Byte , Byval Param As Word)

Die Deklaration der UNIT-Haupt-Funktion. Der Name ist beliebig, das Format muß aber genau so sein.

'=========================================================================================
' Timer0 Prescale for 1mS Interrupt
'=========================================================================================
Config Timer0 = Timer , Prescale = Tmr_c_prescale           'Timer 1mS
On Timer0 Interrupt_ticker                                  ' Timer for Timer Queue

Hier wird Timer0 initialisiert.Das System selbst verwendet den Timer nicht. Nur um "getimete" Messages abzusetzen, braucht man natürlich schon irgendeinen Timer

'-------------------------------------------------
' frequently used units
'-------------------------------------------------
Dim Pwrtag As Word
Dim Outtag As Word
Dim Exptag As Word

'-------------------------------------------------
'           work data
'-------------------------------------------------
Dim Par_byte As Byte
Dim Par_word As Word
Dim Par_addr As Word
Dim Mytag As Word
Dim Tempb As Byte

'-------------------------------------------------
'   enable
'-------------------------------------------------
   Enable Timer0
   Enable Interrupts

Es werden 3 Units mit der selben Funktion erzeugt. Jede Unit steuert einen PCF-Chip auf dem RNBFRA-Board.

'-------------------------------------------------
'           setup Units
'-------------------------------------------------
' RNBFRA PCF Ports

      Exptag = Unit_new()
      Par_addr = Loadlabel(exp_par)                         ' PCF Inp-Port
      Call Pcfclass(exptag , Cmd_setup , Par_addr)

      Outtag = Unit_new()
      Par_addr = Loadlabel(out_par)                         ' PCF OUT-Port
      Call Pcfclass(outtag , Cmd_setup , Par_addr)

      Pwrtag = Unit_new()
      Par_addr = Loadlabel(pwr_par)                         ' PCF PWR-Port
      Call Pcfclass(pwrtag , Cmd_setup , Par_addr)

Nur zur Erläuterung habe ich die Unit-Paramter gleich hier eingefügt, in SYS.BAS sind die natürlich weiter hinten.

'-----------------------------------------------
'        Class parameter
'-----------------------------------------------
'         class   idn  devadr    default      timeout mS
Out_par:
   Data Cls_pcf , 1 , Out_adr , Out_default , 0%            ' passive device
Pwr_par:
   Data Cls_pcf , 2 , Pwr_adr , Pwr_default , 0%            ' passive device
Exp_par:
   Data Cls_pcf , 3 , Exp_adr , 0 , 1500%                   ' auto sensor device

Für jede Unit gibt es vorgegebene Werte:

  • Die ersten beiden Bytes müssen das Unit eindeutig identifizierbar machen, der Rest ist wahlfrei
    • Die Ident-Nummer 1 (bei SYS.BAS die Geräteklasse)
    • Die Ident-Nummer 2 (bei SYS.BAS das spezielle Gerät dieser Klasse)
  • Wahlfrei, was eben die Unit zum Arbeiten braucht
    • die I2C Addresse des PCF, der zu bedienen ist
    • Ein Anfangswert für diese Port. Beim Input (3) ist das sinnlos,
    • dafür steht nur bei diesem als nächstes der Wert 1500 als automatischer Wiederholungswert. Beim 1mS Timer ergibt das 1.5 Sekunden.
'-------------------------------------------------
'           main loop
'-------------------------------------------------
   Do
      Gosub Rxinput
      Call Msg_dequeue(0 , 0 , 0)
   Loop
End

Die Hauptschleife ist ziemlich einfach gehalten. Es wird jedesmal abgefragt, ob der PC was gesendet hat, danach wird geguckt, ob eine Message zum Abarbeiten ist. Und so geht's die ganze Zeit dahin.

'==============================================================================
' Timer 0  interrupt
'==============================================================================
Interrupt_ticker:
   Timer0 = Tmr_c_preload
   Gosub Time_scan
Return

Die Timer ISR. Jedesmal wird mit "Time-Scan" geprüft, ob eine "Getimete" Message fällig ist. Wenn ja, wird sie aktiviert.

'==============================================================================
' Workout Frame
'==============================================================================
Dim Class As Byte                                           'destination Class
Dim Ident As Byte                                           'destination Ident
Dim Msg As Word                                             'Message-Base
Dim Leng As Byte

Rxinput:
   Msg = Com_msg()
   If Msg <> 0 Then
      Leng = Com_rdbyte()
      If Leng >= 3 Then
         Class = Com_rdbyte()
         Ident = Com_rdbyte()
         Mytag = Unit_find_clsid(class , Ident)
         If Mytag <> 0 Then
            Call Unit_call(mytag , Cmd_msg , 0)
         End If
      End If
      Call Com_msgdone(msg)
   End If
   Return

Durch "Com-MSG()" wird gefragt, ob eine (komplette) Message eingetroffen ist. Entspricht etwa dem "Ischarwaiting()" vom Bascom selbst. Ist was da, wird das erste Byte der Message gelesen, das die Länge enthält. Nur, wenn die plausibel ist, wird die Zieladresse gelesen. Mit

        Mytag = Unit_find_clsid(class , Ident)

wird geschaut, ob wir so ein Unit auch haben. Wenn ja, wird mit

           Call Unit_call(mytag , Cmd_msg , 0)

die Funktion aufgerufen, mit dem Kommando cmd_msg, der Beiwert (argument) ist in diesem Falle NULL.

Verarbeitet oder nicht, auf jeden Fall wird der noch reservierte Message-Bereich im UART-Buffer wieder freigegeben

     Call Com_msgdone(msg)

Und damit hat sich's.


Die Unit "PCF"

Das ist ein Muster, das man auch ganz anders machen kann. Festgelegt ist nur der Einstieg und das Format. Natürlich erwartet das System, daß das Kommando durchgeführt wird, aber alle Details sind Sache der Anwenders.

'------------------------------------------------------------------------------
'        UNITS
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
'        Class PCF
'        private class data
'------------------------------------------------------------------------------
Const Max_c_pcf = 3
Dim Pcfidx As Byte

Dim Pcf_bck(max_c_pcf) As Word                              ' backlink to tag
Dim Pcf_value(max_c_pcf) As Byte                            ' private data

Wir wollen drei Units verwenden, also werden die verwendeten Daten als array dreifach angelegt. für den PCF haben wir eigentlich nur ein Byte, das den Soll oder Istwert des PCF aufnehmen kann.

'---------------------------------------------------------------------------
Sub Pcfclass(byval Tag As Word , Byval Cmd As Byte , Byval Param As Word)
Local Pcf_index As Byte

   Pcf_index = Unit_ref(tag)                                ' get private ref

   Select Case Cmd

Command SETUP

'----------------------------------------------------------------------------
'         SETUP Command
'----------------------------------------------------------------------------
   Case Cmd_setup:
      Call Unit_flg_set(tag , 1)                            ' set active
      Par_word = Loadlabel(pcfclass)
      Call Unit_vec_set(tag , Par_word)                     'set vector
      Call Unit_par_set(tag , Param)                        'set FLASH parameter
      Incr Pcfidx                                           'make private Index
      Call Unit_ref_set(tag , Pcfidx)                       'store it
      Pcf_index = Pcfidx
      Pcf_bck(pcf_index) = Tag                              ' Backlink to unit
      Pcf_value(pcf_index) = Unit_parbyte(tag , 3)          ' default value
      Pcf_index = Unit_ref(tag)                             ' set received value
      I2cinit

In den folgenden Zeilen unterscheidet der Code, ob der jeweilige PCF als INPUT oder als OUTPUT zu verwenden ist.

  • Ist ein TImeout angegeben, soll es wohl INPUT sein, d.h. es wird ein Timer aufgesetzt
  • Sonst ist es wohl OUTPUT, daher wird der Anfangswert lt. Parameter an der PCF gesendet.
      Par_word = Unit_parword(tag , 4)                      ' refresh time
      If Par_word <> 0 Then
         Gosub Pcf_refresh_time                             ' sensor device
      Else
         Gosub Pcf_send_val                                 ' act.device
      End If

Command MESSAGE (from PC)

'----------------------------------------------------------------------------
'         MESSAGE RECEIVED  Command
'----------------------------------------------------------------------------
   Case Cmd_msg:
      Tempb = Com_rdbyte()                                  ' ignore source class
      Tempb = Com_rdbyte()                                  ' ignore source ident
      Tempb = Com_rdbyte()                                  ' data byte
      Pcf_value(pcf_index) = Tempb                            ' store
      Gosub Pcf_send_val

Der empfangene Wert wird zum PCF gesendet.

Command SET

Wird im Beispiel nicht verwendet.

'----------------------------------------------------------------------------
'         SET DEVICE Command
'----------------------------------------------------------------------------
   Case Cmd_set:
      Pcf_value(pcf_index) = Param                            ' store
      Gosub Pcf_send_val

Command TIME

Wenn der TImer (1500, s.o.) abgelaufen ist, wird die Unit mit diesem Kommando aufgerufen. Es wird der aktuelle Wert vom PCF geholt, und als Message an den PC geschickt. Danach wird ein neuer Timer aufgesetzt.

'----------------------------------------------------------------------------
'         TIME ELAPSE   Command
'----------------------------------------------------------------------------
   Case Cmd_time:
      Gosub Pcf_sens_val                                    ' read PCF Device
'----------------------------------------------------------------------------
'send Message to host
      Call Com_txstart()
' write empfänger--------
      Tempb = 0
      Call Com_txbyte(tempb)                                ' dest class
      Call Com_txbyte(tempb)                                ' ident
' write absender---------
      Tempb = Unit_parbyte(tag , 0)                         '
      Call Com_txbyte(tempb)                                ' source class
      Tempb = Unit_parbyte(tag , 1)                         ' source ident
      Call Com_txbyte(tempb)
' write data-------------
      Tempb = Pcf_value(pcf_index)
      Call Com_txbyte(tempb)                                ' Data
' finish message---------
      Call Com_txclose()
'----------------------------------------------------------------------------

      Gosub Pcf_refresh_time                                ' refresh timer

   Case Else:

   End Select
   Exit Sub

Noch einige Sub's für die Unit.

Pcf_refresh_time:
   Par_word = Unit_parword(tag , 4)                         ' refresh time
   Par_byte = Time_entry(0 , Tag , Cmd_time , Par_word)     'new timer
   Return
Pcf_send_val:
   Par_byte = Unit_parbyte(tag , 2)                         ' device address
   Tempb = Pcf_value(pcf_index)
   I2cstart
   I2cwbyte Par_byte
   I2cwbyte Tempb
   I2cstop
   Tempb = 0
   Return
Pcf_sens_val:
   Par_byte = Unit_parbyte(tag , 2)                         ' device address
   Incr Par_byte                                            ' I2C Read addr
   I2cstart
   I2cwbyte Par_byte
   I2crbyte Tempb , Nack
   I2cstop
   Pcf_value(pcf_index) = Tempb
   Return
End Sub

Befehls-Referenz

UNIT

Anlegen einer Unit

Durch die Konstante

Const Max_c_unit = 8

wurde der Platz für 8 Units bereitgestellt.

 DIM tag AS WORD
 tag = Unit_new()

Wenn noch Platz war, enthält tag als Ergebnis die Speicheradresse für die neue Unit. Mit diesem Wert sind die folgenden Befehle möglich:

     Call Unit_flg_set(tag , 1)                            ' set active

dadurch ist der Unit-Bereich endgültig reserviert.

     Par_word = Loadlabel(pcfclass)
     Call Unit_vec_set(tag , Par_word)                     'set vector

pcfclass ist der Name der Funktion, die normalerweise vom System angesprungen wird, wenn irgendetwas von der Unit verlangt wird.

     Par_word = Loadlabel(Unitparam)
     Call Unit_par_set(tag , Byval Par-Word)

Unitparam ist ein Label im Flash-Speicher, wo mit "DATA" die für das Unit spezifischen Konstanten angelegt wurden. Die ersten beiden Konstanten-Bytes müssen zusammen eine eindeutige ID für die Unit bilden.

     Call Unit_ref_set(tag , Pcfidx)                       'store private data referenz

Pcfidx ist ein Byte, überlicherweise ein Index, mit dessen Hilfe die Unit immer zu ihren ganz privaten SRAM daten finden kann.


Finden des TAG einer Unit

Ist der tag der Unit nicht bekannt, aber der Unit-Code (s.o.), dann

DIM tag AS WORD
tag = Unit_find_clsid(ident-byte-1, ident-byte-2)

Aufruf einer Unit

Der genormte (direkte) Aufruf

 CALL Unit_call(tag , Cmd-code-byte , Cmd-Param-word )

Cmd-code-byte , Cmd-Param-word sind, vom System her gesehen, beliebige Werte, mit denen die Unit halt was anfangen können soll.

Das entspricht dem folgenden Aufruf:

call Pcfclass(Tag , Cmd-code-byte , Cmd-Param-word )

Die folgenden Aufrufe kann die Sub nun tätigen:

UnitFlag = Unit_flg(tag) 
UnitEntry = Unit_vec(tag)
UnitParam = Unit_par(tag)
UnitRef = Unit_ref(tag) 

Die erwähnten Unit-Parameter aus dem Flash-Speicher können mit Angabe des Byte-Offset gelesen werden

Declare Function Unit_parbyte(tag As Word , Byval Offset As Byte) As Byte
Declare Function Unit_parword(tag As Word , Byval Offset As Byte) As Word
Declare Function Unit_parintg(tag As Word , Byval Offset As Byte) As Integer
Declare Function Unit_parlong(tag As Word , Byval Offset As Byte) As Long
Declare Function Unit_parsing(tag As Word , Byval Offset As Byte) As Single

Wenn erforderlich, können die Werte aber auch sequentiell mittels "READ" gelesen werden

MESSAGES & TIMER

Declare Sub Msg_dequeue(byval Tag As Word , Byval Cmd As Byte , Byval Param As Word)

Das ist der Befehl, wo in der Befehls-Warteschlange nach dem nächsten Auftrag gesucht wird. Ist einer da, wird er durchgeführt, ansonsten geschieht nichts. Typisch findet man diesen Befehl zentral in der Haupt DO..LOOP Schleife (s.o. im SYS.BAS - Beispiel)

Declare Sub Time_scan()

Das ist die Sub, die bei jedem Timerinterrupt aufgerufen wird, am besten direkt in der ISR. Sie zählt in allen anstehenden Timer-Aufträgen den Counter hinunter, bei NULL wird dieser Auftrag aktiv (Und von "msg-dequeue" durchgeführt)

 Declare Function Msg_enqueue(byval Vector As Word , Byval Tag As Word , Byval Cmd As Byte , Byval Param As Word) As Byte
 Declare Function Time_entry(byval Vector As Word , Byval Tag As Word , Byval Cmd As Byte , Byval Param As Word) As Byte

Mit diesen Funktionen werden Aufträge an Units in die Warteschlange gestellt.

  • Bei msg_enqueue() ist Param einfach ein Wert, der dann auch an die Unit übergeben wird
  • Bei Time-entry() ist das die Anzahl Ticks, die "time-scan" herunterzählen soll.

Bei einem 1 mS Ticker sind also 65535 mS möglich, also etwa eine Minute.

Vector kann als Null übergeben werden, wenn tag vorhanden ist. Dann wird der standard-entry der Unit angesprungen. Anderenfalls hat Vector Vorrang, d.h. im Grunde kann jede x-beliebige SUB aufgerufen werden, die das Format

sub-name(Byval xxx As Word , Byval xxx As Byte , Byval xxxx As Word)

hat. Wieweit die dann mit den Call-Argumenten zurechtkommt (wenn sie sie wirklich braucht) ist Sache des Annwenders.

Kommunikation

Declare Function Com_msg() As Word

Mit dieser Fuktion wird gefragt, ob ein neues Paket über die UART reingekommen ist. Wenn nicht, wird NULL zurückgegeben. Sonst ist es die (Byte) Adresse der Länge dieser neuen Nachricht. Dieser Rückgabewert muß, egal, was mit der Nachricht gemacht wurde, dann der SUB

Declare Sub Com_msgdone(msg As Word)

übergeben werden, damit der Platz im Buffer wieder frei wird.

 Declare Function Com_rdbyte() As Byte
 Declare Function Com_rdword() As Word
 Declare Function Com_rdlong() As Long
 Declare Function Com_rdintg() As Integer
 Declare Function Com_rdsing() As Single

Mit diesen Funktionen kann das Paket gelesen werden (das erste Byte ist die Länge). Die Varianten gibt es, damit auch andere Datentypen direkt gelesen werden können, ohne daß sich Bascom beschwert.

Anmerkung: Mit diesen Funktionen wird das Bascom "serialin=buffered" ersetzt. Das heißt, die Daten werden asynchron mittels Interrupt empfangen. Dabei wird der Ringbuffer, den Bascom angelegt hat, für den Level-0 Empfang verwendet.

 Declare Sub Com_txstart()
 Declare Sub Com_txbyte(byval Val As Byte)
 Declare Sub Com_txclose()

Wie man sich vorstellen kann, wird eine zu sendende Nachricht mit -start eröffnet, mit -close beendet, die Daten werden dazwischen nach der Reihe reingeschrieben. Dzt. gibt es dazu noch nicht die Varianten für verschieden Datentypen, die muß ich nachreichen.

Die Sende-Subs sind dzt. noch nicht gepuffert, d.h. jeder zu sendende Byte geht direkt in das UDR-Register. Das hält zwar auf, doch ein gepufferter Output würde ein Prozedere für "Buffer-Overflow" erfordern, und das wollte ich mir in erster Lesung noch nicht antun.


Network Controller/PC: Die Kommunikation setzt auf dem dzt. Stand des Projektes auf, soweit es den Level-0 betrifft. Das Beispielprogramm SYS.BAS nimmt aber vorweg, das als Sende- und Empfangsadresse 2 Byte, also 16-Bit verwendet werden. Hier kann es noch zu Änderungen kommen, die dann aber auch hier berücksichtigt werden.

Anmerkungen

Die Kommunikations-Library Myrncom.BAS / LBX ist von den anderen Funktionen unabhängig. Man muß also die andern Libraries nicht zwangläufig verwenden. Umgekehrt geht das System auch ohne die Kommunikation-Module. Man sieht aus der Referenz, welche Calls dann halt möglich sind bzw. wegfallen.


Autor

Siehe auch