' ********************************************************************
' *                  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_PH2.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 Camba 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...
'             strange bug with non-matched if-endif...

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 EklepH      PORTA.2    ' output 1 op het PCB!   Laagste klep  - black  bit 0
    $define EklepV      PORTA.1       '       ok     pin3
    $define FklepH      PORTA.0    ' output 2 op het PCB!          pin2   - brown  bit 1
    $define FklepV      PORTA.3       '       ok     pin5

    $define GklepH      PORTA.4    ' output 3 PCB, laukhuff solenoid - pink/black   bit 2
    $define GklepV      PORTA.5
    $define PinkH      PORTE.0    ' output 4 pinkgat - oranje/zwart                bit 3
    $define PinkV      PORTE.1

    $define DuimH      PORTE.2     ' output 5 duimgat - wit - 24V solenoid        bit4
    $define DuimV      PORTB.4
    $define Hol8      PORTB.3     ' output 6 NC - geen mosfets gemonteerd !!! (PCB stuk!)
    $define Vel8      PORTB.2     ' NC

    $define AklepH    PORTB.1     ' output 7 Kleine tubular solenoid, 12V        bit 5
    $define AklepV    PORTB.0     ' A-klep, nomally closed
    $define Hol6      PORTD.7     ' output 8 NC
    $define Vel6      PORTD.6

    $define Hol5      PORTD.5     ' output 9 NC
    $define Vel5      PORTD.4
    $define Hol4      PORTC.5     ' output 10 nc
    $define Vel4      PORTC.4

    $define Hol3      PORTD.3     ' output 11 NC
    $define Vel3      PORTD.2
    $define Hol2      PORTC.0     ' output 12 NC
    $define Vel2      PORTC.1

    $define EyeR      PORTC.2     ' output 13 Eye light
    $define EyeR_velo PORTC.3     ' not to be used
    $define EyeL      PORTD.0     ' output 14 Eye light
    $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 = 3 '14
Symbol LastTask = 2
Symbol MinVel = 20
 ' we could have different constants for the coils...

Symbol Hold_Time = 65535      ' houdtijd voor de kleppen - this is 1.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 EyeL_velo
    Low EyeR_velo
    Low Vel2
    Low Vel3
    Low Vel4
    Low Vel5
    Low Vel6
    Low AklepV
    Low Vel8
    Low DuimV
    Low PinkV
    Low GklepV
    Low FklepV
    Low EklepV
    Low EyeL
    Low EyeR
    Low Hol2
    Low Hol3
    Low Hol4
    Low Hol5
    Low Hol6
    Low AklepH
    Low Hol8
    Low DuimH
    Low PinkH
    Low GklepH
    Low FklepH
    Low EklepH
    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 EklepV 
                                                 Clear FklepV
                                                 Clear GklepV
                                                 Clear DuimV
                                                 Clear AklepV
                                                 Clear PinkV
                                                 Set TimVals[1]       ' cancel velo-timers
                                                ' If CC69 = 255 Then
                                                 TimVals[2] = Cnt + Eye_Time
                                                ' EndIf                            
                                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 EklepH 
                                                 Else 
                                                    Set EklepH
                                                    If i.0 = 0 Then Set EklepV
                                                 EndIf
                                                 If klep.1 = 0 Then 
                                                    Clear FklepH 
                                                 Else 
                                                    Set FklepH
                                                    If i.1 = 0 Then Set FklepV 
                                                 EndIf
                                                 If klep.2 = 0 Then 
                                                    Clear GklepH 
                                                 Else 
                                                    Set GklepH
                                                    If i.2 = 0 Then Set GklepV
                                                 EndIf
                                                 If klep.3 = 0 Then 
                                                    Clear PinkH 
                                                 Else 
                                                    Set PinkH
                                                    If i.3 = 0 Then Set PinkV
                                                 EndIf
                                                 If klep.4 = 0 Then 
                                                    Clear DuimH
                                                 Else 
                                                    Set DuimH
                                                    If i.4 = 0 Then Set DuimV 
                                                 EndIf                                                 
                                                 If klep.5 = 0 Then 
                                                    Clear AklepH
                                                 Else 
                                                    Set AklepH
                                                    If i.5 = 0 Then Set AklepV 
                                                 EndIf
                                                
                                                 'If CC69 = 255 Then
                                                    Set TimVals[2] ' clear timer!
                                                    Set EyeL      ' eyes on
                                                    Set EyeR      ' eyes on
                                                 '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 EklepH  
                       Clear FklepH
                       Clear GklepH
                       Clear PinkH
                       Clear DuimH
                       Clear AklepH
                       Set TimVals[0]     
                       Clear old_note 
               Case 1
                       Clear EklepV
                       Clear FklepV
                       Clear GklepV
                       Clear PinkV
                       Clear DuimV
                       Clear AklepV
                       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 69                 ' automated lights on/off
                If value = 0 Then
                    Clear CC69
                Else
                    Set CC69
                EndIf
           Case 123
                AllNotesOff ()
    EndSelect
    Set Ctrl                                  'mandatory reset
EndProc

Proc PowerDown()
     ' must perform a controller reset!!!
               Set TimVals
               Clear EyeL_velo
               Clear EyeR_velo
               Clear AklepV
               Clear DuimV
               Clear PinkV
               Clear GklepV
               Clear FklepV
               Clear EklepV
               Clear EyeL
               Clear EyeR
               Clear AklepH
               Clear DuimH
               Clear PinkH
               Clear GklepH
               Clear FklepH
               Clear EklepH
               Set Resort_flag
EndProc

Proc AllNotesOff ()
                Set TimVals
                Clear EyeL_velo
                Clear EyeR_velo
                Clear AklepV
                Clear DuimV
                Clear PinkV
                Clear GklepV
                Clear FklepV
                Clear EklepV
                Clear EyeL
                Clear EyeR
                Clear AklepH
                Clear DuimH
                Clear PinkH
                Clear GklepH
                Clear FklepH
                Clear EklepH
                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 second pulse-hold board
' rechterhand:
' bit 0 = E-klep, noot 54 , f#    - in rust open
' bit 1 = F-klep, noot 55, G - in rust gesloten
' bit 2 = G#-klep, noot 58, G# - in rust gesloten
' bit 3 = F-vingergat, noot ? - in rust gesloten en bekrachtigd!
' linkerhand:
' bit 4 = duimgat F - in rust gesloten met veer en bekrachtigd! (24V spoel)
' bit 5 = A klep, in rust gesloten, noot 71
' bits 6,7 : niet gebruikt
' if bits are set, the solenoid must be activated
' if bits are 0, the solenoid must be unpowered.
' klep 1 - o.k.
' klep 2 - o.k.
' klep 3 - o.k.
' klep 4 - f-gat bit 3, 1 voor noten 57 - 72, 76 - 93, 96-98   o.k.
' duim - bit 4   : in rust gesloten., 1 voor noten 68, 69,71,72  o.k.
' klep 5 - bit 5 : in rust gesloten. 1 voor de noten 70, 71, 72
' bits 6 en 7 worden niet gebruikt
For i = 0 To 53
    Kleppen[i] = 0
Next i
Kleppen[54] = %00000001     ' o.k. mi greep, fa#      - klep 1, bit0, alleen aktief voor 54, 73, 88, 91
Kleppen[55] = %00000000     ' o.k. fa greep, sol
Kleppen[56] = %00000010     ' fa# greep,     sol#     - klep 2, bit1, alleen aktief voor 56, 75, 97, 98
Kleppen[57] = %00001000     ' sol greep,     la
Kleppen[58] = %00001100     ' lab greep,     sib      - klep 3, bit 2, voor noten 58, 61 , 76, 82, 87, 90, 92, 95
Kleppen[59] = %00001000     ' la greep,      si
Kleppen[60] = %00001000     ' sib greep
Kleppen[61] = %00001100     ' si  greep, do# 
Kleppen[62] = %00001000     ' do greep, re
Kleppen[63] = %00001000     ' do# greep
Kleppen[64] = %00001000     ' re greep
Kleppen[65] = %00001000     ' mib greep      fa
Kleppen[66] = %00001000              ' mi' greep
Kleppen[67] = %00001000              ' fa' greep
Kleppen[68] = %00011000      ' duim!
Kleppen[69] = %00011000              ' sol' greep
Kleppen[70] = %00101000
Kleppen[71] = %00111000              ' la' greep
Kleppen[72] = %00111000
Kleppen[73] = %00000001    ' o.k. si greep, duodecime boven mi-greep, do#
Kleppen[74] = %00000000
Kleppen[75] = %00000010    ' klep2 ok
Kleppen[76] = %00001100    ' klep 3 ok
Kleppen[77] = %00001000
Kleppen[78] = %00001000
Kleppen[79] = %00001000
Kleppen[80] = %00001000
Kleppen[81] = %00001000
Kleppen[82] = %00001100
Kleppen[83] = %00001000
Kleppen[84] = %00001000
Kleppen[85] = %00001000
Kleppen[86] = %00001000
Kleppen[87] = %00001100   ' klep3 ok
Kleppen[88] = %00001001   ' klep 1 ok
Kleppen[89] = %00001000
Kleppen[90] = %00001100
Kleppen[91] = %00001001   ' klep 1 ok
Kleppen[92] = %00001100   ' klep 3 ok
Kleppen[93] = %00001000
Kleppen[94] = %00000000   
Kleppen[95] = %00000100   ' klep 3 ok
Kleppen[96] = %00001000
Kleppen[97] = %00001010    ' klep 2 ok
Kleppen[98] = %00001010    ' klep 2 ok
Kleppen[99] = %00001010
Kleppen[100] = %00001000
Kleppen[101] = %00001000
Kleppen[102] = %00001000
Kleppen[103] = %00001000
Kleppen[104] = %00101000
Kleppen[105] = %00101000
For i = 106 To 127
    Kleppen[i] = 0
Next i
EndProc



'[EOF]
