' ********************************************************************
' *                  PIC firmware for the <Ekla> robot               *
' *                      pulse and hold coding                       *
' *                  coded by dr.Godfried-Willem Raes                *
' * http://www.logosfoundation.org/instrum_gwr/ekla/picworks         *
' *                  source code development directory:              *
' *                            ekla_PH1.bas                          *
' ********************************************************************
' 23.01.2016: first prototype PCB coded. These boards serve 14 notes each.
' 26.04.2018: Starting from the player piano code with note repeats,
'             coding for <Pos>, using the 18F4620 processor
' 27.04.2018: Tektronix measurements:
'             repeat frequencies:
'                        127 = 30 ms, so repeat frequency is 16 Hz (16 notes a second)
' 09.12.2019: Upgrade start to better timer0 implementation.
'             Velo1 = 660us
'             Velo64 = 26.5ms
'             velo127 = 52.2ms   glitchfree now!
' 21.02.2021: Board 2 replaced, as we had a persistent fault on note 55.
'             Did we upgrade the other PIC's in 2019?   After the diary for <pos> we did.(09.12.2019)
' 2022: same boards also used for <RorO>
'----------------------------------------------------------------------------------------------------
' 05.05.2024: Starting code for <Cemba>
'             4 PCB's good for 56 notes.
' 16.06.2024: midi channel changed to 13, the hub board translates whatever channel to this one.
'             First series of 4 PICs programmed.
' 17.06.2024: First tests on the instrument.
'             note repetitions appear not very reliable.
'             velo's need some scaling.
' 18.06.2024: musical tests with some midi files.
' 19.06.2024: 4 different velo scalings implemented, a low string demands somewhat higher velo values.
'             measurements for board 1:
'             velo 1 = 9 ms
'             velo 20 = 12.5 ms
'             velo 64 = 32 ms
'             velo 127 = 50 ms
'             We could also differentiate for back keys versus white ones, as the forces involved are different.
'             Chromatic notes for board1 : 37, 39, 42, 44, 46
'                             for board2 : 49, 51, 54, 56, 58, 61
'                             for board3 : 63, 66, 68, 70, 73, 75
'                             for board4 : 78, 80, 82, 85, 87
' 20.06.2024: Looks like automated note repeats are pretty useless here..
'             also, we can improve the code by switching the hold on, on reception of note-on's
' 27.06.2024: note repetitions ommitted from code. To enable, just define reps.
'             velocities rescaled
'             lite on  note 120 implemented for board 4
'             Hub firmware upgraded as well
' 28.06.2024: Two different lookup tables implemented: for 1 register and for 2 registers.
'             the controller for this selection must come from the hub board.
'             Controller 67 is implemented here to do just this.
' 01.07.2024: prog.change and reg.1, 2 etc. improved. Half bubble sort applied.
'             now 2 regs use the sum of the individual reg. velo's
'             Controller 67 now takes the values 0,1,2,3.
' 02.07.2024: bubble sorts added and code improved for speed.
' 03.07.2024: Cleanup. This be version 1.0
' 02.09.2024: client Marc Sinan complaints: shaky behavior observed...
'             measurement and evaluation session under heavy midi loads required.
'             $reps code removed, although that only contributes to readability.
'             velo-hold tests passed. Problem must be somewhere else...
' 11.09.2024: Source code prepared for passing over to Kristof Lauwers.
' -----------------------------------------------------------------------------
' 16.08.2026: Code for <Ekla> robot, starting from Cemba and/or Flut.
'             This is code for the second board! 12 V valves and eyes
' 18.08.2026: Lookup voor de vingerzettingen voor de kleppen op dit board afgewerkt.
'             eerste versie min of meer klaar...
'             Start coding for the PH1 board with bidirectional solenoids

Include "18F4620.inc"
' Mapping defines for midi-events on pin outputs and inputs:
' This board handles only one-directional solenoids.
    ' for <Ekla> it ought to  be:
    $define GgatH      PORTA.2     ' bit 0               dikke gele draad  1= klep toe
    $define GgatV      PORTA.1       '       ok     pin3
    $define GgatHo      PORTA.0    ' output 2 op het PCB!          pin2  dunne gele draad  1 = klep open
    $define GgatVo      PORTA.3       '       ok     pin5

    $define AgatH      PORTA.4    ' output 3 PCB,    bit 1       dikke groene draad
    $define AgatV      PORTA.5
    $define AgatHo      PORTE.0    ' output 4  dunne groene draad    
    $define AgatVo      PORTE.1

    $define BgatH      PORTE.2     ' output 5 bit2   blauw dik
    $define BgatV      PORTB.4
    $define BgatHo      PORTB.3     ' output 6       blauw dun
    $define BgatVo      PORTB.2     ' 

    $define CgatH    PORTB.1     ' output 7         bit 3
    $define CgatV    PORTB.0     
    $define CgatHo      PORTD.7     ' output 8 
    $define CgatVo      PORTD.6

    $define DgatH      PORTD.5     ' output 9 bit 4
    $define DgatV      PORTD.4
    $define DgatHo      PORTC.5     ' output 10 
    $define DgatVo      PORTC.4

    $define EgatH      PORTD.3     ' output 11 NC  bit 5
    $define EgatV      PORTD.2
    $define EgatHo      PORTC.0     ' output 12 NC
    $define EgatVo      PORTC.1

   ' $define EyeR      PORTC.2     ' output 13 
   ' $define EyeR_velo PORTC.3     ' not to be used
   ' $define EyeL      PORTD.0     ' output 14 
   ' $define EyeL_velo PORTD.1     ' not to be used
    'red LED for debug:
    $define Debug_Led   PORTB.5   ' for testing - red led - watchdog - pin 38

Declare All_Digital = True        ' makes all analog pins, digital for I/O
Clear SSPCON1.5                   ' make sure RC3 is free for use.
' configure the input and output pins:
TRISA = %11000000     'bits set to 0 are output, 1 = input  - bits 6 and 7 are the clock!
TRISB = %11100000     'bits 6 and 7 are for the ICP, bit 5 is the red LED
TRISC = %11000000     'RC6 en RC7 zijn USART  I/O and must be set to input
TRISD = %00000000     'all bits can be used
TRISE = %11101000     'low nibble only, bit3 (RE3) is MCLR
                      'bit 4 most be zero to disable PSP mode on port D  - gwr 20.01.2012
                      'this was the bug we had!
'constant definitions:
Symbol Ekla_low_note = 54   
Symbol Ekla_high_note = 105 

'initialisations for the midi input parser:
Symbol Midichannel = 6                        ' Ekla_Channel internal
Symbol NoteOff_Status = 128 + Midichannel      ' 2 bytes follow
Symbol NoteOn_Status  = 144 + Midichannel
Symbol Keypres_Status = 160 + Midichannel
Symbol Control_Status = 176 + Midichannel
Symbol ProgChange_Status = 192 + Midichannel   ' 1 byte message
Symbol Aftertouch_Status = 208 + Midichannel   ' 1 byte follows
Symbol Pitchbend_Status  = 224 + Midichannel   ' lsb msb follow
Symbol NrTasks = 2 '6 '14
Symbol LastTask = 1 '5
Symbol MinVel = 20


Symbol Hold_Time = 21845      ' houdtijd voor de kleppen - this is 0.5 seconds
                              ' timeunits are 22.8us here 
'Symbol Eye_Time = 8192        ' should be 187 ms, time the eyes stay on after a note-off

' Setup the USART
Declare Hserial_Baud = 31250                   ' Set baud rate for the USART to MIDI specs.
Declare Hserial_TXSTA = 0x24                   ' instead of the normal 0x20 - ?? 0x24
' Declare Hserial_Clear = On                   ' should clear on errors. Bytes get lost of course...
' Create variables:
    Dim Cnt As Dword System                       '32 bit counter
    Dim CntHw As Cnt.Word1 ' Word System          'used in the timer0 interrupt, where it is incremented
    Dim CntLw As TMR0L.Word                       'this is the trick to read both TMR0L and TMR0H
                                                  'it makes Cntlw the low word of cnt, when we use cnt.word0=CntLw
    Dim time As Cnt                               'alias
'    Dim Tim1 As TMR1L.Word                        'not used here
'    Dim Tim2 As TMR2                              'not used here
'    Dim Cnt3 As Dword System
'    Dim Cnt3Hw As Cnt3.Word1
'    Dim Tim3 As TMR3L.Word                        ' same trick for timer3
'    Dim Sr as TMR0L.7                             '512 S/s   - this works but these DO NOT WORK!!!:
                                                  ' Dim Sr as CntLw.Byte1   does not
                                                  ' Dim Sr As TMR0H.0  'sampling rate bit, 256 S/s
                                                  ' DIM Sr as CntLw.8
                                                  ' As TMR0H.1  would be 128 S/s
                                                  ' As TMR0H.2  would be 64 S/s
                                                  ' As TMR0H.3  would be 32 S/s
                                                  ' As TMR0H.4  would be 16 S/s
    Dim Bytein As Byte System                    ' midi byte read from buffer
    Dim StBit As Bytein.7                         ' highest bit of ByteIn
    Dim i As Byte System                          ' general purpose counter
    ' midi variables
    Dim statusbyte As Byte System
    Dim noteUit As Byte System                    ' note off + release value
    Dim release As Byte System
    Dim noteAan As Byte System                    ' note on + release value
    Dim velo As Byte System
    Dim notePres As Byte                          ' note pressure + pressure value
    Dim pres As Byte
    Dim Ctrl As Byte System                       ' continuous controller + value
    Dim value As Byte System
    Dim prog As Byte System                       ' program change + program-byte
'    Dim aft  As Byte System                       ' channel aftertouch
'    Dim pblsb As Byte System                      ' pitch bend lsb
'    Dim pbmsb As Byte System                      ' pitch bend msb

    Dim CC69 As Byte                             ' 0= light automation off, 1 = automation ON
    Dim CC66 As  Byte System                     ' global on/off switch
    Dim PowerOn As CC66.0                        ' handled on the hub board.

    Dim klep As Byte                      ' 6 bits used
    Dim playing_note As Byte
    Dim old_note As Byte                  ' previous note   
    
    Dim maxtim As time.31                 ' overflow bit, will cause timer reset after 1h45
    Dim TimVals[NrTasks] As Dword         ' lijst met timer waarden - de kleinste is eerst aan de beurt
    Dim Nxt As Dword System               ' waarde voor de eerstvolgende timer
    Dim idx As Byte System                ' index voor de eerstvolgende timer
    Dim Ringbuffer[256] As Byte
    Dim Resort As Byte
    Dim Resort_flag As Resort.0           ' flag to signal the requirement to resort timers
    
'-----------------------------------------------------------------------------------------
' Load the USART Interrupt handler and buffer read subroutines into memory
Include "EklaPH2_irq.inc"                 ' for UART,Timer0, Timer3 Interrupt
'Clear                           ' clear all RAM
'-----------------------------------------------------------------------------------------
Dim vels[128] As Word                    ' velocity lookup table in use
Dim Kleppen[128] As Byte

' Main program starts here
MAIN:
    Low Debug_Led
    DelayMS 10                       ' wait for stability
    Low EgatVo
    Low EgatV
    Low DgatVo
    Low DgatV
    Low CgatVo
    Low CgatV
    Low BgatVo
    Low BgatV
    Low AgatVo
    Low AgatV
    Low GgatVo
    Low GgatV
    Low EgatHo
    Low EgatH
    Low DgatHo
    Low DgatH
    Low CgatHo
    Low CgatH
    Low BgatHo
    Low BgatH
    Low AgatHo
    Low AgatH
    Low GgatHo
    Low GgatH
    Set TimVals

    Init_Usart_Interrupt             ' Initiate the USART serial buffer interrupt
                                     ' this procedure is in the include file
    Clear_Serial_Buffer              ' Clear the serial buffer and reset its pointers
                                     ' in the include as well
                                     ' Configure Timer0 for:
'                                      Clear TMR0L and TMR0H registers
'                                      Interrupt on Timer0 overflow
'                                      16-bit operation
'                                      Internal clock source  40MHz
'                                      1:256 Prescaler : thus 40MHz / 256 = 156.250kHz
'                                      6.4 us per clock-tick
'        Opentimer0 (Timer_INT_On & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256)
'        replacing above macro with in-line coding:
         Clear T1CON
         Clear IntConBits_T0IF      ' clear interrupt flag
         Set INTCONBITS_T0IE        ' enable interrupt on overflow
         T0CON = %10000111           ' bit 7 = enable/disable
                                     ' bit 6 = 1=8 bot, 0=16 bit
                                     ' bit 5 = 1 pin input, 0= Internal Clk0
                                     ' bit 4 = HL or LH transition when bit5 =1
                                     ' bit 3 = 1= bypass prescaler, 0= input from prescaler
                                     ' bit 2-0 = prescaler select: 111= 1:256
                                     ' Setup the High priorities for the interrupts
' TIMER1: if enabled, all midi-in is blocked, so it must interfere with the UART
             ' Configure Timer1 for:
'                        Clear TMR1L and TMR1H registers
'                        Interrupt on Timer1 overflow
'                        16-bit read/write mode
'                        Internal clock source
'                        1:8 Prescaler
'
'    OpenTimer1(TIMER_INT_ON & T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_8)    ' dit kompileert o.k.

' TIMER2: if enabled, the UART stops working...
'    Opentimer2 (Timer_Int_On & T2_POST_1_16 & T2_PS_1_16)     ' dit lukt... maar de timer is nodig voor de UART...

' TIMER3:
' Configure Timer3 for:
'                        Interrupt on Timer3 overflow
'                        16-bit read/write mode
'                        Internal clock source
'                        1:8 Prescaler
'                        Don’t sync external clock input
'    T3_OSC1En_On ()     ' macro
'   OpenTimer3(TIMER_INT_ON & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8 & T3_SYNC_EXT_OFF) ' fout, but == voorbeeld???
'   Opentimer3 (Timer_Int_On & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8 & T3_SYNC_EXT_OFF & T3_SOURCE_CCP)  ' fout
'    OpenTimer3 (Timer_INT_ON & T3_16BIT_RW  & T3_PS_1_8 & T3_SYNC_EXT_OFF)                                ' fout
'    OpenTimer3 (0xFFFF & Timer_INT_On & T3_16BIT_RW)
'    OpenTimer3(T3_8BIT_RW & T3_SOURCE_EXT & T3_PS_1_1 & T3_SYNC_EXT_OFF)  ' copied from manual, fout!!!
 '   OpenTimer3(TIMER_INT_OFF & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8)
 ' doing it this way seems to work:
    Clear T3CON
    Clear PIR2BITS_TMR3IF ' clear IRQ flag
    Set PIE2BITS_TMR3IE   ' irq on
    'T3_OSC1En_On ()       ' macro  - sets T3CON
'    Clear Tim3            ' Clear TMR3L And TMR3H registers
    Set RCONbits_IPEN      ' Enable priority interrupts
    Clear IPR2bits_TMR3IP   ' Set Timer3 as a low priority interrupt source
    ' we can also set T3Con in one instruction as:
    T3CON = %10110001               ' oef, now it works...
                            ' bit 7 = 16 bit mode
                            ' bit 6,3 = 0, 0
                            ' bit 5,4 = 1:8 prescale
                            ' bit 2 = 0
                            ' bit 1 = 0 Internal clock = Fosc/4
                            ' bit 0 : 1= enable timer 3, 0= disable
                            ' maximum count = 52.42ms, 1 tick =0.8uS, freq.=19Hz
    ' Set up priority interrupts.
  '  IPR1bits_TMR1IP = 0    ' Set Timer1 as a low priority interrupt source
  '  INTCONbits_PEIE = 1    ' Enable peripheral interrupts
  '  INTCONbits_GIE = 1     ' Enable global interrupts
                            ' read the default lookup tables in Vels[] and Velf[]
Velo_Lookup ()
Kleppen_lookup ()

HRSOut Control_Status, 120, 64           ' adding this dummy made the whole program work...
                              ' we have no explanation for this behavior.

                              ' start the main program loop:
Do                            ' Create an infinite loop
        Cnt.Word0 = CntLw            ' read timer 0
        Bytein = GetMidiIn ()
                                     ' Start the midi parser.
        Midi_Parse:
            If Bytein > Control_Status Then    ' here higher statusses are not implemented.
                If Bytein > 253 Then       '254 = midiclock, 255= reset
                                           'midiclock can interrupt all other msg's...
                                           '255 had to be intercepted since thats what we
                                           'get when no new byte flows in.
                Else
                    Clear statusbyte       'reset the status byte
                EndIf
                GoTo Check_Timers          'throw away...
            EndIf
            If StBit =1 Then               'should be faster than If Bytein > 127 Then
                                           'status byte received, bit 7 is set
                Clear statusbyte           'if on another channel, the statusbyte needs a reset
                Select Bytein              'eqv to Select case ByteIn
                       Case NoteOff_Status
                            statusbyte = Bytein
                            Set noteUit          'reset value. Cannot be 0 !!!
                            Set release          '0 is a valid midi note!
                       Case NoteOn_Status
                            statusbyte = Bytein
                            Set noteAan '= 255
                            Set velo '= 255
                       Case Keypres_Status          'not used here
                            statusbyte = Bytein
                            Set notePres
                            Set pres
                       Case Control_Status          ' controllers and switches
                            statusbyte = Bytein
                            Set Ctrl
                            Set value
'                       Case ProgChange_Status       ' used for different lookup tables
'                            statusbyte = Bytein
'                            prog = 255
'                       Case Aftertouch_Status
'                            statusbyte = Bytein
'                            Set aft
'                       Case Pitchbend_Status        ' not on this board.
'                            statusbyte = Bytein
'                            pblsb = 255
'                            pbmsb = 255
                EndSelect
            Else                                        'midi byte is 7 bits
                Select statusbyte
                       Case 0                           'not a message for this channel
                            GoTo Check_Timers           'disregard
                       Case NoteOff_Status
                            If noteUit = 255 Then
                                noteUit = Bytein
                            Else
                                release = Bytein        'message complete, so we can do the action...
                                Select noteUit
                                    Case playing_note
                                                 Clear playing_note
                                                 ' start note-off timer such that we hold valves unless
                                                 ' a new note comes in
                                                 TimVals[0] = Cnt + Hold_Time
                                                 old_note = noteUit       ' so the fingering now is that for old_note 
                                                 ' clear all velo-pulse outputs:
                                                 Clear GgatV 
                                                ' Clear GgatVo
                                                 Clear AgatV
                                                ' Clear AgatVo
                                                 Clear BgatV
                                                ' Clear BgatVo
                                                 Clear CgatV
                                                ' Clear CgatVo
                                                 Clear DgatV
                                                ' Clear DgatVo
                                                 Clear EgatV
                                                ' Clear EgatVo
                                                 Set TimVals[1]       ' cancel velo-timers                         
                                EndSelect
                                Set noteUit         ' reset
                                Set Resort_flag     ' mandatory, as we changed timers!
                            EndIf
                       Case NoteOn_Status
                            If noteAan = 255 Then
                                noteAan = Bytein
                            Else
                                velo = Bytein
                                    Select noteAan
                                        Case Ekla_low_note To Ekla_high_note 
                                                 ' cancel hold timer
                                                 Set TimVals[0]
                                                 klep = Kleppen[noteAan] 
                                                 i = Kleppen[old_note]
                                                 ' here we should only apply the velo pulse to valves that
                                                 ' are in the 0 state - we use minvel here instead of velo!!!
                                                 If klep.0 = 0 Then 
                                                    Clear GgatH 
                                                    Set GgatHo
                                                 Else 
                                                    Set GgatH
                                                    Clear GgatHo
                                                    If i.0 = 0 Then Set GgatV   ' velo puls bij sluiten klep
                                                 EndIf
                                                 If klep.1 = 0 Then 
                                                    Clear AgatH
                                                    Set AgatHo 
                                                 Else 
                                                    Set AgatH
                                                    Clear AgatHo
                                                    If i.1 = 0 Then Set AgatV 
                                                 EndIf
                                                 If klep.2 = 0 Then 
                                                    Clear BgatH 
                                                    Set BgatHo
                                                 Else 
                                                    Set BgatH
                                                    Clear BgatHo
                                                    If i.2 = 0 Then Set BgatV
                                                 EndIf
                                                 If klep.3 = 0 Then 
                                                    Clear CgatH
                                                    Set CgatHo 
                                                 Else 
                                                    Set CgatH
                                                    Clear CgatHo
                                                    If i.3 = 0 Then Set CgatV
                                                 EndIf
                                                 If klep.4 = 0 Then 
                                                    Clear DgatH
                                                    Set DgatHo
                                                 Else 
                                                    Set DgatH
                                                    Clear DgatHo
                                                    If i.4 = 0 Then Set DgatV 
                                                 EndIf                                                 
                                                 If klep.5 = 0 Then 
                                                    Clear EgatH
                                                    Set EgatHo
                                                 Else 
                                                    Set EgatH
                                                    Clear EgatHo
                                                    If i.5 = 0 Then Set EgatV 
                                                 EndIf
                                                 playing_note = noteAan    ' we forgot this! added 27.05.2020...
                                                 ' now we set the velo-pulse timer with the value of minvel:
                                                 velo = vels[MinVel]
                                                 TimVals[1] = Cnt + velo  
                                    EndSelect        
                                    Set noteAan                'reset !!!
                                    Set Resort_flag
                            EndIf
                       Case Control_Status    'this is where the action takes place for controllers
                            If Ctrl = 255 Then
                                Ctrl = Bytein
                            Else
                                value = Bytein
                                Controller()
                            EndIf
                EndSelect
            EndIf

If Resort_flag = 1 Then
    idx = SortTimers ()      ' so we resort only if an incoming midi command changed something
EndIf

Check_Timers:
If idx < NrTasks Then           ' we moeten alleen checken wanneer er een timer loopt
   If time >= Nxt Then         ' nagaan of de eerstvolgende timer afgelopen is...
        ' in dit geval is de eerste timer afgelopen en moeten we de juiste aktie ondernemen:
        Set Nxt.31                                    ' timer reset, is immers afgelopen
        ' aan de hand van idx weten we welke timer dit is
            Select idx
               Case 0                                     
                       Clear GgatH  
                       Clear GgatHo
                       Clear AgatH
                       Clear AgatHo
                       Clear BgatH
                       Clear BgatHo
                       Clear CgatH
                       Clear CgatHo
                       Clear DgatH
                       Clear DgatHo
                       Clear EgatH
                       Clear EgatHo
                       Set TimVals[0]     
                       Clear old_note 
               Case 1
                       Clear GgatV
                       Clear AgatV
                       Clear BgatV
                       Clear CgatV
                       Clear DgatV
                       Clear EgatV
                       Set TimVals[1]              ' set to infinite= disable
'               Case 2                             
'                        ' used for switching off the eyes after a note-off
'                       ' should only happen when CC69 = 255
'                       Set TimVals[2]         ' clear timer
'                       Clear EyeL
'                       Clear EyeR             
            EndSelect
        Set Resort_flag
  EndIf
      ' beveiliging tegen overflow crashes...
      If maxtim = 1 Then
                    Clear time
                    Set TimVals
      EndIf
Else
      ' idx > 13, no timers running, so to avoid overflows, we can reset the loop timer
      If maxtim = 1 Then Clear time     
EndIf
     'Btg PORTB.5                       ' average loop-speed: (Tektronix measurement)
                                       ' 5.5 us under no load  up to 10us jittering under full load
Loop

Proc SortTimers (), Byte
    'look up the next smallest timer value in the Timvals array
    ' zoek de de volgende kleinste timer waarde:
    Set Result
    Set Nxt.31
    Clear i
    Do
        If TimVals[i] < Nxt Then
            Nxt = TimVals[i]
            Result = i
        EndIf
        Inc i
    Loop Until i > LastTask
    Clear Resort_flag
EndProc

Proc ProgChange()
    Set prog
EndProc

Proc Pitchbend ()
    Set pblsb
EndProc

Proc Aftertouch ()
    Set aft                             ' reset
EndProc

Proc Controller ()
    Select Ctrl
           Case 66
                'on/off for the robot
                If value = 0 Then
                    PowerDown ()
                Else
                    AllNotesOff ()
                EndIf
                ' following handled on the hub board...
           Case 123
                AllNotesOff ()
    EndSelect
    Set Ctrl                                  'mandatory reset
EndProc

Proc PowerDown()
     ' must perform a controller reset!!!
               Set TimVals
               Clear EgatV
               Clear EgatVo
               Clear DgatV
               Clear DgatVo
               Clear CgatVo
               Clear CgatV
               Clear BgatVo
               Clear BgatV
               Clear AgatVo
               Clear AgatV
               Clear GgatVo
               Clear GgatV
               ' hold's
               Clear EgatH
               Clear EgatHo
               Clear DgatH
               Clear DgatHo
               Clear CgatHo
               Clear CgatH
               Clear BgatHo
               Clear BgatH
               Clear AgatHo
               Clear AgatH
               Clear GgatHo
               Clear GgatH
               Set Resort_flag
EndProc

Proc AllNotesOff ()
               Set TimVals
               Clear EgatV
               'Clear EgatVo
               Clear DgatV
               'Clear DgatVo
               'Clear CgatVo
               Clear CgatV
               'Clear BgatVo
               Clear BgatV
               'Clear AgatVo
               Clear AgatV
               'Clear GgatVo
               Clear GgatV
               ' hold's
               Clear EgatH
               Clear EgatHo
               Clear DgatH
               Clear DgatHo
               Clear CgatHo
               Clear CgatH
               Clear BgatHo
               Clear BgatH
               Clear AgatHo
               Clear AgatH
               Clear GgatHo
               Clear GgatH
                Set Resort_flag
EndProc

Proc Velo_Lookup ()
 ' lookup table for the velocity controlled pulse durations
 ' first test:
 ' resolution is 6.2 us.
 ' 21 = 130 us  (meting 21.01.2016)

' vels[1] = 88   ' should be 550 microseconds
 ' maxvel = 44ms, or 44ms / 6.2us = 7096 units
 ' stepsize = (7096 - 88) / 126 = 55.6
' For i = 2 To 127
'     vels[i] = vels[i-1] + 55
' Next i
'    resultaat:
 ' 1 = 88 units = 1ms
 ' 127 = 7096 units = 70ms   
 
 ' benadering 2: resolution = 10.5 us  (op grond van vorige meting)
 vels[1] = 52
 ' maxvel = 44ms or 44000/10.5 =  4190 units
 ' stepsize = (4190 - 52) / 126 = 32.8
 For i = 2 To 127
     vels[i] = vels[i-1] + 32.8
 Next i
 '   resultaat:       [23.01.2016, 18u10]
   ' 1 = 52 => 550 us
   ' 127 = 4190 => 44ms
   ' dit komt overeen met prog change 0 to boards 1 tot 9 in pp2-firmware versie 2005. 
 ' rescaling 09.12.2019:
 For i = 1 To 127
    vels[i] = vels[i] >> 1  ' divide by 2
 Next i
    ' meting 09.12.2019:
    '  velo = 1  => 660us
    '  velo = 64  => 26.5ms
    ' velo = 127  => 52.2 ms
EndProc

Proc Kleppen_lookup ()
' lookup for the solenoids steered by the first pulse-hold board
' rechterhand:
' bits 0,1,2
' linkerhand:
' bits 3,4,5
' bits 6 en 7 worden niet gebruikt
For i = 0 To 53
    Kleppen[i] = 0
Next i
Kleppen[54] = %00111111     ' 1= gesloten, 0 = open
Kleppen[55] = %00111111     
Kleppen[56] = %00111111     ' fa# greep,     sol#    
Kleppen[57] = %00111111     ' sol greep,     la
Kleppen[58] = %00111111     ' lab greep,     sib     
Kleppen[59] = %00111110     ' la greep,      si
Kleppen[60] = %00111101     ' sib greep
Kleppen[61] = %00111100     ' si  greep, do# 
Kleppen[62] = %00101000     ' do greep, re
Kleppen[63] = %00110101     ' do# greep
Kleppen[64] = %00110000     ' re greep
Kleppen[65] = %00101000     ' mib greep      fa
Kleppen[66] = %00100000              ' mi' greep
Kleppen[67] = %00010000              ' fa' greep
Kleppen[68] = %00110000      ' fa#
Kleppen[69] = %00010000              ' sol' greep
Kleppen[70] = %00010000
Kleppen[71] = %00010000              ' la' greep
Kleppen[72] = %00010000
Kleppen[73] = %00111111    ' o.k. si greep, duodecime boven mi-greep, do#
Kleppen[74] = %00111111    ' do greep
Kleppen[75] = %00111111    ' do#
Kleppen[76] = %00111111    ' re
Kleppen[77] = %00111111    ' mib
Kleppen[78] = %00111110    ' mi
Kleppen[79] = %00111101    ' fa
Kleppen[80] = %00111011    ' fa#
Kleppen[81] = %00111000    ' sol
Kleppen[82] = %00110100    ' sol#
Kleppen[83] = %00110000     'la
Kleppen[84] = %00101000    ' sib
Kleppen[85] = %00100000    ' si
Kleppen[86] = %00010000    ' do
Kleppen[87] = %00011111   ' do#
Kleppen[88] = %00011101   ' re
Kleppen[89] = %00011101   ' mib
Kleppen[90] = %00011000   ' mi
Kleppen[91] = %00110101   ' fa
Kleppen[92] = %00110111   ' fa#
Kleppen[93] = %00110111   ' sol
Kleppen[94] = %00100111   ' sol#
Kleppen[95] = %00101000   ' la
Kleppen[96] = %00101000   ' sib     ?
Kleppen[97] = %00110110    ' si
Kleppen[98] = %00100100    ' do
Kleppen[99] = %00011111    ' do# ?
Kleppen[100] = %00011101   ' re ?
Kleppen[101] = %00011101   ' mib
Kleppen[102] = %00011000   ' mi
Kleppen[103] = %00110101   ' fa
Kleppen[104] = %00110111   ' fa#
Kleppen[105] = %00110111   ' sol
For i = 106 To 127
    Kleppen[i] = 0
Next i
EndProc



'[EOF]
