Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Balkonkraftwerk Speicher und Wechselrichter Tests und Tutorials

(Initialisierung)
Zeile 66: Zeile 66:
 
   
 
   
  
===Initialisierung===
+
==SYS.BAS Demo==
*Generell: 
+
Statt umfangreicher theoretischer Abhandlungen einfach das Sys.BAS Demoprogramm mit einigen Kommentaren.  
Die Libraries müssen in das Bascom-Library-Directory gestellt werden.  
+
 
+
 
<pre>
 
<pre>
Const Max_c_unit = 4
+
$regfile = "m32def.dat"
Const Max_c_msg = 16
+
$crystal = 8000000
  
Const rx_buffersize = 24
+
Const Tmr_c_prescale = 64
 +
Const Tmr_c_preload = 131
  
$INCLUDE "myunit.bas"
+
$baud = 9600
$INCLUDE "mymsgque.bas"
+
$hwstack = 64
$INCLUDE "myrncom.bas"
+
$swstack = 256
 +
$framesize = 64
  
</pre>
 
  
 +
Const Max_c_unit = 8
 +
Const Max_c_msg = 16
 +
 +
Const Rx_bufsize = 48
 +
 +
$include "myrncom.bas"
 +
$include "myunit.bas"
 +
$include "mymsgque.bas"
 +
</pre>
 
Hier erfolgen die Definitionen für die Library
 
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_unit gibt an, wieviele Units maximal verwendet werden können.  
 
*Max_c_msg gibt an, wieviele Messages gleichzeitig 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.
 
*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.
  
=====Ein-queuen einer Message=====
 
 
<pre>
 
<pre>
Declare Function Msg_enqueue(Byval Vector As Word ,
+
'-------------------------------------------------
                          Byval Tag As Word , Byval Cmd As Byte , Byval Param As Word) As Byte
+
'          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
 
</pre>
 
</pre>
 +
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.
 +
<pre>
 +
'-------------------------------------------------
 +
'          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
  
Dim Enqresult As Byte
 
    Enqresult = Msg_enqueue(0, Tag, Cmd, Param)
 
  
Hier wurde der Call-Vector NULL angegeben, was der Normalfall ist. Daher wird dann auch der normale Unit-Entry verwendet.
+
Const Pwr_default = &HFF                                    'I2C Powerport
 +
Const Out_default = &H00                                    'I2C Extension-Out Port
  
'''Anmerkung''': Das Enqueueing ist so abgesichtert, daß es auch in einer ISR verwendet werden kann, was ja auch eine typische Anwendung darstellt:
+
</pre>
 
+
Das ist dem RNBFRA-User alles vertraut, braucht man wohl nicht zu erklären.
=====Abarbeiten der Message-Queue=====
+
 
<pre>
 
<pre>
Declare Sub Msg_dequeue(Byval Cmd As Byte , Byval Param As Word)
+
'-------------------------------------------------
 +
'          Class IDs
 +
'-------------------------------------------------
 +
Const Cls_pcf = &H42                                        ' ASCII "B"
 
</pre>
 
</pre>
 
+
Das sollte man für den Demo-Test nicht ändern, da es auch im PC-Programm so verwendet wird.  
Klassisch und zweckmäßig ist das De-Queueing an zentraler Stelle, im Main- DO..LOOP
+
 
<pre>
 
<pre>
  Do
+
'-------------------------------------------------
      Call Msg_dequeue(0 , 0 , 0)
+
'          CLASS entries    R8:R9=Flash Param  (restore indiv.params)
  Loop
+
'-------------------------------------------------
 +
Declare Sub Pcfclass(byval Tag As Word , Byval Cmd As Byte , Byval Param As Word)
 
</pre>
 
</pre>
 
+
Die Deklaration der UNIT-Haupt-Funktion. Der Name ist beliebig, das Format muß aber genau so sein.  
====Timed-Message-Queue====
+
Hier handelt es sich um Queue-Entries, die verzögert durchgeführt werden. Param wird dann für die Angabe des Zählers verwendet.
+
    Enqresult = Time_entry(0, Tag, Cmd, Counter)
+
Damit das funktioniert, muß natürlich ein Timer laufen. Vom Verfasser wird gerne der Timer0 für ein Millisekunden-Intervall eingestellt. Das heißt dann, daß der genannte Zähler in mS Einheiten heruntergezählt wird
+
 
<pre>
 
<pre>
Config Timer0 = Timer , Prescale = 64                      'Timer 1mS
+
'=========================================================================================
Const Tmr_c_preload = 131
+
' Timer0 Prescale for 1mS Interrupt
 +
'=========================================================================================
 +
Config Timer0 = Timer , Prescale = Tmr_c_prescale          'Timer 1mS
 
On Timer0 Interrupt_ticker                                  ' Timer for Timer Queue
 
On Timer0 Interrupt_ticker                                  ' Timer for Timer Queue
 
</pre>
 
</pre>
 +
Hier wird Timer0 initialisiert.Das System selbst verwendet den Timer nicht. Nur um "getimete" Messages abzusetzen, braucht man natürlich schon irgendeinen Timer
 +
<pre>
 +
'-------------------------------------------------
 +
' 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
  
<pre>
+
'-------------------------------------------------
Interrupt_ticker:
+
'  enable
   Timer0 = Tmr_c_preload
+
'-------------------------------------------------
   Gosub Time_scan
+
   Enable Timer0
Return
+
   Enable Interrupts
 
</pre>
 
</pre>
Bei jedem Aufruf von "Time_scan" wird bei allen Timer-Einträgen eins heruntergezählt. Bei null wird dann der Queue-Eintrag aktiv gesetzt.
+
Es werden 3 Units mit der selben Funktion erzeugt. Jede Unit steuert einen PCF-Chip auf dem RNBFRA-Board.
 +
<pre>
 +
'-------------------------------------------------
 +
'          setup Units
 +
'-------------------------------------------------
 +
' RNBFRA PCF Ports
  
'''Anmerkung''':  Wenn eine Unit-Sub regelmäßig immer wieder aufgerufen werden will, muß sie jedesmal einen neuen Timer-entry aufsetzen. Das ist derzeit so gelöst, weil sonst zwei Arten Timer-Entries geführt werden müßten: Mit auto-repeat und single-shot. Da wird man in folgenden Versionen noch weitersehen.
+
      Exptag = Unit_new()
 +
      Par_addr = Loadlabel(exp_par)                        ' PCF Inp-Port
 +
      Call Pcfclass(exptag , Cmd_setup , Par_addr)
  
====Anlegen einer Unit====
+
      Outtag = Unit_new()
Als Beispiel eine eigentlich völlig sinnlose Klasse, die nur demonstrieren soll, wie das Ganze funktioniert
+
      Par_addr = Loadlabel(out_par)                        ' PCF OUT-Port
*Definitionen und Parameter
+
      Call Pcfclass(outtag , Cmd_setup , Par_addr)
Const Cls_any = &H20
+
Irgendein Symbol, das allerdings in der gesamten µC Applikation eindeutig sein muß, wird als Klassen-Code festgelegt.
+
Declare Sub Anyclass(byval Tag As Word , Byval Cmd As Byte , Byval Param As Word)
+
Das ist der default-Unit-Entry
+
  
An einer geeigneten Stelle (nach "END") werden die Unit-Paramter deklariert.  
+
      Pwrtag = Unit_new()
 +
      Par_addr = Loadlabel(pwr_par)                        ' PCF PWR-Port
 +
      Call Pcfclass(pwrtag , Cmd_setup , Par_addr)
 +
</pre>
 +
Nur zur Erläuterung habe ich die Unit-Paramter gleich hier eingefügt, in SYS.BAS sind die natürlich weiter hinten.  
 
<pre>
 
<pre>
  Any_par:
+
'-----------------------------------------------
   Data Cls_any , 1 , &H78 , 16255% , 300000& , 3.55!
+
'        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
 
</pre>
 
</pre>
Obligat als erstes der Klassencode, dann der Identifier. Der Rest ist eben irgendwas, was diese eine konkrete Unit zum arbeiten benötigt.
+
Für jede Unit gibt es vorgegebene Werte:
 
+
*Die ersten Byten Bytes '''müssen''' den Unit-Code (Klasse, Ident) beinhalten, der Rest ist wahlfrei
*Eintragen in der Tabelle
+
**Klassencode
Natürlich NACH Unit-Init()
+
**Die Ident-Nummer 1-3
 +
*Wahlfrei
 +
*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 der Wert 1500 als automatischer Wiederholungswert. Beim 1mS Timer ergibt das 1.5 Sekunden.
 
<pre>
 
<pre>
Dim Par_addr As Word      'Hilfsfeld
+
'-------------------------------------------------
Dim Anytag As Word       
+
'          main loop
 
+
'-------------------------------------------------
       Anytag = Unit_new(unitbase)      
+
  Do
 +
       Gosub Rxinput
 +
      Call Msg_dequeue(0 , 0 , 0)
 +
  Loop
 +
End
 +
</pre>
 +
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.
 +
<pre>
 +
'==============================================================================
 +
' Timer 0  interrupt
 +
'==============================================================================
 +
Interrupt_ticker:
 +
  Timer0 = Tmr_c_preload
 +
  Gosub Time_scan
 +
Return
 
</pre>
 
</pre>
Die Funktion gibt den eindeutigen TAG der Unit zurück. Jetzt muß die Unit ihre Werte eintragen. Für den Aufruf übergeben wir die Adresse der
+
Die Timer ISR. Jedesmal wird mit  "Time-Scan" geprüft, ob eine "Getimete" Message fällig ist. Wenn ja, wird sie aktiviert.
Parameter
+
  
 
<pre>
 
<pre>
       Par_addr = Loadlabel(any_par)                         ' individial parameter
+
'==============================================================================
       Call Anyclass(mytag , Cmd_setup , Par_addr) 'CMD_SETUP ist festgelegt
+
' 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
 
</pre>
 
</pre>
 +
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.
  
*Setup der Unit
+
 
Das macht die Unit am besten selbst, da sie durch die mitgelieferten Paramter modifizierbar ist.  
+
===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.
 
<pre>
 
<pre>
'-----------------------------------------------
+
'------------------------------------------------------------------------------
'        Class ANY
+
'        UNITS
'-------------------------------------------------
+
'------------------------------------------------------------------------------
 +
'------------------------------------------------------------------------------
 +
'        Class PCF
 
'        private class data
 
'        private class data
'-------------------------------------------------
+
'------------------------------------------------------------------------------
Const Max_c_any = 2
+
Const Max_c_pcf = 3
Dim Anyidx As Byte
+
Dim Pcfidx As Byte
  
Dim Any_bck(max_c_any) As Word
+
Dim Pcf_bck(max_c_pcf) As Word                             ' backlink to tag
Dim Any_dta(max_c_any) As Byte
+
Dim Pcf_value(max_c_pcf) As Byte                           ' private data
 
</pre>
 
</pre>
WENN die Klasse /Unit private Daten im SRAM braucht, muß sie sie natürlich dimensionieren. Und zwar am besten
+
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.  
in Tabellenform, das kommt Bascom entgegen. (Max_c_any ist die maximale Anzahl der möglichen Units dieser Klasse)
+
  
Mögliches Grundmuster der Funktion, die anhand CMD entscheidet, was zu tun ist
 
 
<pre>
 
<pre>
 
'---------------------------------------------------------------------------
 
'---------------------------------------------------------------------------
Sub Anyclass(byval Tag As Byte , Byval Cmd As Byte , Byval Param As Word)
+
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
 
   Select Case Cmd
  Case Cmd_setup:
 
 
  Case Cmd_show:
 
 
  Case Else:
 
      Print Str(tag) ; " CMD1:" ; Str(cmd) ; " PAR1:" ; Str(param)
 
  End Select
 
End Sub
 
 
</pre>
 
</pre>
  
Wenn die Klasse irgendein Command nicht kennt, zeigt sie es hier im Beispiel einfach an und macht sonst nichts.
+
===Command SETUP===
 
+
Wichtig ist '''CMD_SETUP''', das Setzen des Sprungzieles und der Parameter
+
 
<pre>
 
<pre>
Local Par_word As Word
+
'----------------------------------------------------------------------------
       Par_word = Loadlabel(anyclass)
+
'        SETUP Command
       Call Unit_vec_set(tag , Par_word)
+
'----------------------------------------------------------------------------
       Call Unit_par_set(tag , Param)
+
  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
 
</pre>
 
</pre>
Bascom läßt eine direkte Angabe eine Sprungzieles nicht zu. Daher der Umweg über eine word-variable. In dem Beispiel wird einfach die eigene Adresse, also der Name der SUB als Unit-Vector eingetragen. Die Unit-Parameter vom FLASH werden einfach direkt aus dem Aufruf übernommen. Gibt es keine Parameter(NULL), macht das zwar nichts, aber dann kann die Unit nicht von den Kommunikations-Funktionen gefunden werden.  
+
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.
Nun wird ein privates Datenbereich reserviert. Der Klassenspezifische Index wird (bei jedem setup) erhöht und als Datenreferenz in der Unit-Tabelle eingetragen
+
 
<pre>
 
<pre>
       Incr Anyidx
+
       Par_word = Unit_parword(tag , 4)                     ' refresh time
      Call Unit_ref_set(tag , Anyidx)
+
      If Par_word <> 0 Then
 
+
        Gosub Pcf_refresh_time                            ' sensor device
       Any_bck(anyidx) = Tag
+
       Else
       Any_dta(anyidx) = 0
+
        Gosub Pcf_send_val                                ' act.device
 +
       End If
 
</pre>
 
</pre>
Die Klasse schreibt in ihre privaten Daten den Unit-Tag als Backlink dazu, damit sie auch von dort aus zu ihrem Tag findet. Das ist bei ISR- Funktionen oft nötig.  
+
===Command MESSAGE (from PC)===
any_dta(anyidx) = 0 ist eben irgendwas.  
+
<pre>
 +
'----------------------------------------------------------------------------
 +
'        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
 +
</pre>
 +
Der empfangene Wert wird zum PCF gesendet.  
  
'''Ab nun ist die Unit im System eingetragen und verwendbar'''.  
+
===Command SET===
 
+
Wird im Beispiel nicht verwendet.
Als Beispiel ein anderes Command: '''CMD_SHOW'''
+
 
<pre>
 
<pre>
Local Par_class As Byte
+
'----------------------------------------------------------------------------
Local Par_ident As Byte
+
'        SET DEVICE Command
Local Par_devad As Byte
+
'----------------------------------------------------------------------------
Local Par_word As Word
+
   Case Cmd_set:
Local Par_long As Long
+
       Pcf_value(pcf_index) = Param                            ' store
Local Par_sing As Single
+
       Gosub Pcf_send_val
 
+
   Case Cmd_show:
+
       Par_class = Unit_parbyte(tag , 0)     ' Der Klassencode steht immer an der ersten Stelle der Parameter
+
      Par_ident = Unit_parbyte(tag , 1)    ' ebenso Ident
+
 
+
       Par_devad = Unit_parbyte(tag , 2)     ' die Parameter werden einfach geholt und angezeigt.
+
      Par_word = Unit_parword(tag , 3)      ' das Daten-Offset muß man wissen, auch was dann dort steht
+
      Par_long = Unit_parlong(tag , 5)      ' es ist sicher sinnvoll, "CONST" zu definieren
+
      Par_sing = Unit_parsing(tag , 9)
+
      Print "Class/Id:" ; Str(par_class) ; "/" ; Str(par_ident) ; " Device:0x" ; Hex(par_devad)
+
      Print " word:" ; Str(par_word) ; " Long:" ; Str(par_long) ; " Single:" ; Str(par_sing)
+
 
</pre>
 
</pre>
  
====Aufrufbeispiele====
+
===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.
 
<pre>
 
<pre>
'------------ AUFBAU-----------------------------
+
'----------------------------------------------------------------------------
       Anytag = Unit_new(unitbase)
+
'        TIME ELAPSE  Command
       Par_addr = Loadlabel(any_par)                        ' individial parameter1
+
'----------------------------------------------------------------------------
       Call Anyclass(mytag , Cmd_setup , Par_addr)
+
  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()
 +
'----------------------------------------------------------------------------
  
       Anytag = Unit_new(unitbase)
+
       Gosub Pcf_refresh_time                                ' refresh timer
      Par_addr = Loadlabel(any_par2)                        ' individial parameter2
+
      Call Anyclass(mytag , Cmd_setup , Par_addr)
+
  
'------------ CALLS-----------------------------
+
  Case Else:
' suchen des Tags nach Class und Ident
+
      Anytag = Unit_find_clsid(Cls_any , 1)
+
  
' Direkter Aufruf
+
  End Select
      Call Anyclass(anytag , Cmd_show , 0)
+
  Exit Sub
 +
</pre>
  
DIM Wrk_idx AS BYTE
+
Noch einige Sub's für die Unit.
 
+
<pre>
' Indirekter Aufruf über die Queue
+
Pcf_refresh_time:
      Wrk_idx = Msg_enqueue(0 , Anytag , Cmd_show , 0)
+
  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
 +
</pre>
  
' Auswerten der Queue
+
==Befehls-Referenz==
      Call Msg_dequeue(0 , 0 , 0)
+
===UNIT===
 
+
<pre>
 
+
Declare Sub Unit_call(tag As Word , Byval Cmd As Byte , Byval Param As Word)
END
+
Declare Function Unit_new() As Word
 
+
Declare Function Unit_find_clsid(byval Class As Byte , Byval Ident As Byte) As Word
Any_par:
+
Declare Sub Unit_vec_set(tag As Word , Byval Vec As Word)
  Data Cls_any , 1 , &H78 , 16255% , 300000& , 3.55!
+
Declare Sub Unit_par_set(tag As Word , Byval Par As Word)
Any_par2:
+
Declare Sub Unit_ref_set(tag As Word , Byval Flg As Byte)
  Data Cls_any , 2 , &H44 , 255% , 250000& , 1.44!
+
Declare Sub Unit_flg_set(tag As Word , Byval Flg As Byte)
 +
Declare Function Unit_flg(tag As Word) As Byte
 +
Declare Function Unit_vec(tag As Word) As Word
 +
Declare Function Unit_par(tag As Word) As Word
 +
Declare Function Unit_ref(tag As Word) As Byte
 +
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
 +
</pre>
 +
===MESSAGES & TIMER===
 +
<pre>
 +
Declare Function Msg_enqueue(byval Vector As Word , Byval Tag As Word , Byval Cmd As Byte , Byval Param As Word) As Byte
 +
Declare Sub Msg_dequeue(byval Tag As Word , Byval Cmd As Byte , Byval Param As Word)
 +
Declare Function Time_entry(byval Vector As Word , Byval Tag As Word , Byval Cmd As Byte , Byval Param As Word) As Byte
 +
Declare Sub Time_scan()
 +
</pre>
 +
===Kommunikation===
 +
<pre>
 +
Declare Sub Com_msgdone(msg As Word)
 +
Declare Function Com_msg() As Word
 +
Declare Sub Com_txstart()
 +
Declare Sub Com_txclose()
 +
Declare Sub Com_txbyte(byval Val As Byte)
 +
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
 
</pre>
 
</pre>
  

Version vom 8. März 2006, 20:24 Uhr

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
  • 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.

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


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
  • 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" 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. (Beim Netzwerk-Nachrichten zeigt er auf die eingelangten Daten, die von der Unit zu bearbeiten sind).

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 Byten Bytes müssen den Unit-Code (Klasse, Ident) beinhalten, der Rest ist wahlfrei
    • Klassencode
    • Die Ident-Nummer 1-3
  • Wahlfrei
  • 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 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

 Declare Sub Unit_call(tag As Word , Byval Cmd As Byte , Byval Param As Word)
 Declare Function Unit_new() As Word
 Declare Function Unit_find_clsid(byval Class As Byte , Byval Ident As Byte) As Word
 Declare Sub Unit_vec_set(tag As Word , Byval Vec As Word)
 Declare Sub Unit_par_set(tag As Word , Byval Par As Word)
 Declare Sub Unit_ref_set(tag As Word , Byval Flg As Byte)
 Declare Sub Unit_flg_set(tag As Word , Byval Flg As Byte)
 Declare Function Unit_flg(tag As Word) As Byte
 Declare Function Unit_vec(tag As Word) As Word
 Declare Function Unit_par(tag As Word) As Word
 Declare Function Unit_ref(tag As Word) As Byte
 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

MESSAGES & TIMER

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

Kommunikation

 Declare Sub Com_msgdone(msg As Word)
 Declare Function Com_msg() As Word
 Declare Sub Com_txstart()
 Declare Sub Com_txclose()
 Declare Sub Com_txbyte(byval Val As Byte)
 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

Autor

Siehe auch


LiFePO4 Speicher Test