' ******************************************************************** ' * PIC firmware for Ijspaleis 2 * ' * coded by dr.Godfried-Willem Raes * ' * source code development directory: * ' * http://www.logosfoundation.org/elektron/IcePal2/ * ' ******************************************************************** ' editor: Amicus or Proton editor ' always keep together with all referenced includes ' 27.08.2011: sampler for FSR's implemented in the low interrupt code ' data acquisition limited to 8 bits resolution ' 31.08.2011: analog input configuration bug killed. RA4 cannot be analog input! We must use RA5 ' watch out for differences between 18F2520 and 18F25K20 !!! ' Checked against Microchip manual. We need the ANSEL and ANSELH registers for the 18F25K20! ' 11.09.2011: implementation of longer velo-pulses, extending the 16bit limit we had hitherto. ' 14.09.2011: Sensor values inverted. ' threshold values subtracted from adc readings ' 15.09.2011: << shift instructions now in two steps, avoiding overflow bugs. ' Now version 1.3 ' 26.10.2011: maybe we should have separate shifts for dripsize and dripfrequency ' This be version 1.4 ' Note-on/off coding changed. ' ********************************************************************** ' 20.09.2013: Code for Icepal1 used as a start off for Icepal2 ' here we have 4 drippers ' 4 analog inputs as well as a digital switch are possible via the flatcable connector on the midi-hub board ' 2 pwm outputs as yet uncommisioned ' 2 more outputs free ' 2 more switch inputs possible via X16 ' Board: MidiHub board. ' TO DO: add new periodic timers (cfr. Whisper) ' adapt to precize specifications. ' Should it work stand-alone? ' 21.09.2013: Toggle switch added for on/off control. This is connected to port B.4 ' Laura request: randomize dripping... ' we use the Random function and let it point to a value in a lookup table. ( Dur[]) ' The FSR's or the potmeters values are AND'ed with the RND function here. ' Thus we have manual control over the range. ' This is version 1.1 - compiles o.k. ' 23.09.2013: fine tuning required. rnd had logic bug ' rnd function required 4 new dword variables!: veltim4,5,6,7 'Include "18F2525.inc" '(40MHz, 5V) Include "18F2520.inc" '(40MHz, 5V) 'Include "18F25K20.inc" 'for test & debug on an Amicus board. (64MHz, 3V3) ' this include is not required with the Amicus IDE but is ' essential for the Proton Compiler. 'POORTEN INITIALISEREN 'uitgangen $define drup1 PORTC.5 'elektroventiel 1 X11-2 $define drup2 PORTC.4 'elektroventiel 2 x11-3 $define drup3 PORTB.0 'elektroventiel 3 X12-2 $define drup4 PORTB.1 'elektroventiel 4 X12-3 $define Switch PORTB.4 'manual on off switch $define Debug_Led PORTB.5 'for testing - yellow led - watchdog 'analoge ingangen $define FSR0 PORTA.0 'sensor 1 AN0 $define FSR1 PORTA.1 'sensor 2 AN1 $define FSR2 PORTA.2 'sensor 3 AN2 $define FSR3 PORTA.3 'sensor 4 AN3 ' $define FSR4 PORTA.5 ' fout: PORTA.4 'sensor 5 AN4 'midi seriele datalijnen (usart), overriding build in USB '$define MIDI_Out PORTC.6 'MIDI uit '$define MIDI_In PORTC.7 'MIDI in Clear SSPCON1.5 'RC3 must be available for I/O TRISA = %10001111 'A0, A1, A2, A3 = input / A4, A5 = output, A6, A7 = clock! TRISB = %11010000 'BO, B1, B2, B3, B5 = output / B4, B6, B7 = input TRISC = %11000000 'C0, C1, C2, C3, C4, C5= output / C6, C7 = input 'RC1 en RC2 zijn pwm outputs and must be set to output 'RC6 en RC7 zijn USART I/O and must be set to input 'CONSTANTEN: 'initialisations for the midi input parser: Symbol Midichannel = 5 ' Midi kanaal 5 Symbol NoteOff_Status = 128 + Midichannel ' 2 bytes follow Symbol NoteOn_Status = 144 + Midichannel ' 2 bytes follow Symbol Keypres_Status = 160 + Midichannel ' 2 bytes follow Symbol Control_Status = 176 + Midichannel ' 2 bytes follow Symbol ProgChange_Status = 192 + Midichannel ' 1 byte message Symbol Aftertouch_Status = 208 + Midichannel ' 1 byte follows Symbol Pitchbend_Status = 224 + Midichannel ' lsb msb follow 'application specific constants Symbol NrTasks = 8 ' maximum 16 'Symbol PWMminF = 3906 Symbol PWMFreq2 = PWMminF * 2 ' PWMminF is processor dependent. Symbol PWMFreq3 = PWMminF * 3 ' declared in the processor include Symbol PWMFreq4 = PWMminF * 4 'Setup the USART Declare Hserial_Baud = 31250 ' Set baud rate for the USART to MIDI specs. Declare Hserial_TXSTA = 0x24 ' Hserial_TXSTA, sets the respective hardware register, TXSTA, to the value ' in the Declare. See the PIC18F25K20 or PIC18F25K22 data sheet for the device ' used for more information regarding this register. The TXSTA register BRGH ' bit (bit 2) controls the high speed mode for the baud rate generator. ' Certain baud rates at certain oscillator speeds require this bit to be set ' to operate properly. To do this, set Hserial_TXSTA to a value of $24 ' instead of the default $20. Refer to the data ' sheet for the hardware serial port baud rate tables and additional ' information. Dim Cnt As Dword System Dim CntHw As Cnt.Word1 ' alias required in timer0 interrupt 'timer0 interrupt, to create a 32 bit timer Dim CntLw As TMR0L.Word 'this is the trick to read both TMR0L and TMR0H 'it makes Cntlw the low word of cnt 'We still have to copy the contents of Lw to Cnt Dim Tim3 As TMR3L.Word 'timer by stating Cnt.word0 = CntLw ' 16 bit counter for sampler 'Dim Sr as TMR0L.7 '512 S/s ' 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 Dim j As Byte System 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 System ' note pressure + pressure value Dim pres As Byte System 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 veltim As Dword System ' 32 bit velo Dim veltim4 As Dword System ' in exchange for task_rsi[4] etc. being only 16 bits Dim veltim5 As Dword System Dim veltim6 As Dword System Dim veltim7 As Dword System ' var's for the core of the multitasker: Dim VelFlags As Word System 'bits 0 - 15 used as flags for active timers 'Velflags=Velflags0 and Velflags1 'in deze code wordt enkel Velflags0 gebruikt, Velflags1 is voorzien indien de code wordt uitgebreid Dim VelFlags0 As VelFlags.Byte0 'alias for bits 0-7 Dim VelFlags1 As VelFlags.Byte1 'bits 8-15 - not used in this code. ' for midi steering of functional parameters: Dim CC30 As Byte System ' dripsize 0 Dim CC31 As Byte System ' dripsize 1 Dim CC32 As Byte System ' dripsize 2 Dim CC33 As Byte System ' dripsize 3 Dim CC40 As Byte System ' threshold for drip Sensor 0 Dim CC41 As Byte System ' id. for drip 1 Dim CC42 As Byte System ' id. for drip 2 Dim CC43 As Byte System ' id. drip 3 Dim CC60 As Byte System ' disable FSR's (so midi notes work - otherwise they are overriden by response to FSR's) Dim Shift As Byte System ' changeable with controller 100 ' used to shift veltim's to the left Dim st As Byte System Dim b1 As Byte System Dim b2 As Byte System Dim Sensor[4] As Byte ' 4 8-bit values read from de FSR's in the IRQ routine Dim ChCnt As Byte System ' channel-counter for sampling used in the low-IRQ ' Dim FSR4_Oldval As Byte System ' for reduction of midi data flow Dim tBit As Byte System ' for sampling rate bit Dim ADvalue As Word System ' the 8 bit result will be in the highbyte of ADvalue Dim ADresult As ADvalue.Byte1 Dim ADreg As ADRESH ' 8 bit of the adc result register Dim wRnd As Word System ' random number Dim bRnd As wRnd.Byte0 ' 1 to 255 Dim Dur[128] As Word ' lookup for periods '----------------------------------------------------------------------------------------- 'Load the USART Interrupt handler And buffer read subroutines into memory Include "ADC.inc" ' Load the ADC macros into the program Include "Laura_Irq.inc" ' our own version for UART And Timer0/3 Interrupt Dim Task_rsi[NrTasks] As Word 'task reschedule interval (period), if 0 the task is not active 'max. value limited to 65535. For longer periods, it will have to become dword!!! 'creëer een word array met nrtasks(aantal voorheen bepaald) elementen ' task_rsi[0-3] = dripsize's ' task_rsi[4-7] = dripping frequency Dim Velmsb[NrTasks] As Word 'the application for velo-timers, is in fact just a one-shot task 'creëer een word array met nrtasks(aantal voorheen bepaald) elementen Dim VelLsb[NrTasks] As Word 'creëer een word array met nrtasks(aantal voorheen bepaald) elementen 'make sure we initialize those pins (enkel uitgangen, geen ingangen!) on start up: Low drup1 Low drup2 Low drup3 Low drup4 Low Debug_Led 'HPWM 1, 0, PWMminF 'HPWM channel 1(RC2), duty cycle, frequency 'HPWM 2, 0, PWMminF 'HPWM channel 2(RC1) CC30 = 4'8 ' defaults for dripsize on startup CC31 = 4'8 CC32 = 4'8 CC33 = 4 CC40 = 3 '20 ' default Sensor threshold CC41 = 3 '20 CC42 = 3 '20 CC43 = 3 'CC60 = 0 Task_rsi[0] = CC30 << 8 ' dripsize Task_rsi[1] = CC31 << 8 Task_rsi[2] = CC32 << 8 Task_rsi[3] = CC33 << 8 Clear ChCnt ' channel counter for ADC Clear Sensor[0] Clear Sensor[1] Clear Sensor[2] Clear Sensor[3] Shift = 0 ' with value 0 we get the best result on our LED test board 'Shift = 3 ' initial value to preserve the present. ' if we get it working, this may be replaced with any ' value between 0 and 8. ' note: higher values make drips larger and repetition rate much slower ' tested o.k. on LED board gwr. '----------------------------------------------------------------------------------------- 'MAIN PROGRAM MAIN: High Debug_Led DelayMS 50 ' wait for stability Low Debug_Led Clear VelFlags GoSub Dur_Lookup 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 'OPEN TIMER: Opentimer0 (Timer_INT_On & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256) in macro file. Clear T1CON ' timer1 Clear IntConBits_T0IF ' clear interrupt flag Set INTCONBITS_T0IE ' enable interrupt on overflow T0CON = %10000111 ' bit 7 = enable/disable ' bit 6 = 1=8 bit, 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 'OPEN ADC & INLEZEN WAARDEN SENSOREN ' Fosc/64 ' Left justified for 10-bit operation, with 8 bit highword ' Tad value of 0 ' Vref+ at Vcc : Vref- at Gnd ' Make AN0 an analogue input 'Declare Adin_Res = 8 ' 8-bit result required - shouldn't we left justify then? YES ' in fact the conversion is always performed as 10 bits. Declare Adin_Tad = 64_Fosc 'ADC_FOSC_32 Declare Adin_Stime = 25 ' 50 ' following works in the Proton compiler but not in the Amicus version of it... 'OpenADC(ADC_FOSC_64 & ADC_LEFT_JUST & ADC_0_TAD, ADC_REF_VDD_VSS, ADC_4ANA) ' Note: 18F2520 has ADC_V5, whilst 18F25K20 has ADC_V8, channel selection bits are different!!! 'could be replaced with: 'ANSEL = %00011111 ' 5 analog channels - specific for the 18F25K20 chip! ' this register is undefined for the 18F2520 ' ANSELH = %00000000 ' must be cleared! ADCON2 = %00001110 'bit 7: ADFM: A/D result format select bit / 1= right justified, 0=left justified 'bit 6= ongebruikt, dus 0 'bit 5-3: A/D acquisition Time select bits / 001= 2 TAD 'bit 2-0: A/D conversion clock select bits / 110= FOSC/64 ' This is the same for 18F2520 and 18F25K20 ADCON1 = %00001010 'bit 7-6: ongebruikt, dus 0 'bit 5: Voltage reference configuration bit (VREF- source) / 0=VSS 'bit 4: Voltage reference configuration bit (VREF+ source) / 0=VDD 'bit 3-0: 1010: AN0-AN4: analoog (worden gebruikt voor inlezen van sensoren), 'AN5-AN12: digitaal (worden niet gebruikt) ' for 18F2520 1010 is o.k., for 18F25K20 it should be 00011111 in ANSEL instead ' voor Fa ADCON1 = %00001110 wat betekent AN0 analoog, An1-12: digitaal ' voor Korn ADCON1 = %00001101 dus, AN0 en AN1 analoog, rest, digitaal ' voor de 18F25K20 is de implementatie anders!!! ' bit 7-6: NOT IMPLEMENTED ' bit 5: VREF- source ' bit 4: VREF+ source ' bit 3-0: NOT IMPLEMENTED ' ADCON1 = %00000000 'ADCON0 = %00000001 'bit 7-6: ongebruikt, dus 0 'bit 5-2: Analog Channel Select bits / 0000 = Channel O (AN0) ' 0001 = Channel 1 (AN1) ' 0010 = AN2 ' 0011 = AN3 ' 0100 = AN4 'bit 1 = A/D conversion status bit / bit 1 = 1 (A/D conversion in progress) / bit 1=0 (A/D idle) ' Setting this bit starts a conversion. 'niet duidelijk of bit 1 1 of o moet zijn, bij Godfried 0 ondanks inlezen sensor op AN0 'bit 0 moet gelijk zijn aan 1 (A/D converter module is enabled) (ADON bit) 'code Godfried ADCON0 = %00000001 'voor de 18F25K20 is het: ' bits 5-2: channel select bits. We need only bits 4-2 (000-100), leave bit5 = 0 ' to convert channel0 : set bit 2 (make 1) ' set GO bit in ADCON0 ' nu, poll GO bit in ADCON0, if 0 then conversion completed: ' lees 8 bit result in ADRESH ADCON0 = %00000011 ' startup with channel AN0 and ADC enabled. ' low level coding: ' For i = 0 To 4 ' j = (i << 2) | 1 ' shift channel selection bits 2 places left, set bit 0 ' ADCON0 = j ' connect S&H to correct channel ' Set ADCON0.1 ' start conversion by setting bit 1 ' While ADCON0.1 = 1 ' wait for bit 1 to become 0 ' Wend ' Sensor[i] = ADRESH ' read 8-bit result ' Next i ' basic coding: For i = 0 To 3 ADvalue = ADIn i ' mag i wel direct het kanaal zijn??? of, moet het via ADCON0 geset worden? Sensor[i] = ADresult ' highbyte of ADvalue = 8 bit Next i ' coding using adc.inc: ' For i = 0 to 3 ' ADvalue = ReadAdc (i) ' does not compile,as i must be a constant. ' Sensor[i] = ADResult ' next i ' Sensor[0] = ADIn 0 'ReadADC(0)'initialize with the value on startup 'Read ADC: Read the results of an A/D conversion. '(0)= AN0 ' Sensor[1] = ADIn 1 'ReadADC(1) '(1)=AN1 ' Sensor[2] = ADIn 2 'ReadADC(2) '(2)=AN2 ' Sensor[3] = ADIn 3 'ReadADC(3) '(3)=AN3 'OPEN TIMER: open and start timer3 for sampling: Clear T3CON Clear PIR2BITS_TMR3IF ' clear IRQ flag Set PIE2BITS_TMR3IE ' irq on 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, lowest freq.=19Hz ' start the main multitasking program loop: LOOP: ' Create an infinite loop Bytein = HRSIn ' Read data from the serial buffer, with no timeout ' 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 (?) GoTo Check_Timers 'throw away... Else Clear statusbyte 'reset the status byte GoTo Check_Timers 'throw away End If 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 '= 255 'reset value. Cannot be 0 !!! Set release '= 255 '0 is a valid midi note! Case NoteOn_Status statusbyte = Bytein Set noteAan '= 255 Set velo '= 255 Case Keypres_Status 'used for lights - no longer. statusbyte = Bytein Set notePres '= 255 Set pres '= 255 Case Control_Status ' this is the main thing to listen to.... statusbyte = Bytein Set Ctrl '= 255 Set value '= 255 ' Case ProgChange_Status ' statusbyte = Bytein ' prog = 255 ' Case Aftertouch_Status ' statusbyte = Bytein ' aft = 255 ' Case Pitchbend_Status ' statusbyte = Bytein ' pblsb = 255 ' pbmsb = 255 End Select 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 1 Clear VelFlags0.0 'bit 0 of Velflags0 Clear VelFlags0.4 Clear Drup1 Case 2 Clear VelFlags0.1 Clear VelFlags0.5 Clear Drup2 Case 3 Clear VelFlags0.2 Clear VelFlags0.6 Clear Drup3 Case 4 Clear VelFlags0.3 Clear VelFlags0.7 Clear Drup4 End Select Set noteUit '= 255 'reset EndIf GoTo Check_Timers Case NoteOn_Status If noteAan = 255 Then noteAan = Bytein Else velo = Bytein If velo = 0 Then Select noteAan Case 1 Clear VelFlags0.0 'bit 0 of Velflags0 Clear VelFlags0.4 Clear Drup1 Case 2 Clear VelFlags0.1 Clear VelFlags0.5 Clear Drup2 Case 3 Clear VelFlags0.2 Clear VelFlags0.6 Clear Drup3 Case 4 Clear VelFlags0.3 Clear VelFlags0.7 Clear Drup4 End Select Set noteAan '= 255 'reset !!! GoTo Check_Timers 'jump out Else Select noteAan ' as it is here, the dripping is only controlled by midi timing. ' midi will override interactive operation. Case 1 ' open the valve - dont forget to close with note0ff Clear VelFlags0.0 Clear VelFlags0.4 Set Drup1 ' velocity byte steers the period of the dripping 'Task_rsi[3] = (128-velo) << 9 'If Task_rsi[3] < (Task_rsi[0] << 1) Then Task_rsi[3] = Task_rsi[0] << 1 ' ' to be tested on robot 'veltim = Task_rsi[3] << Shift ' new 'Cnt.Word0 = CntLw 'veltim = veltim + Cnt ' old: Cnt + Task_rsi[3] 'Velmsb[3] = veltim.Word1 'VelLsb[3] = veltim.Word0 'Set VelFlags0.3 Case 2 Clear VelFlags0.1 Clear VelFlags0.5 Set Drup2 'Task_rsi[4] = (128-velo) <<9 'If Task_rsi[4] < (Task_rsi[1] << 1) Then Task_rsi[4] = Task_rsi[1] << 1 ' ' to be tested on robot 'veltim = Task_rsi[4] << Shift 'Cnt.Word0 = CntLw 'veltim = veltim + Cnt ' Cnt + Task_rsi[4] 'Velmsb[4] = veltim.Word1 'VelLsb[4] = veltim.Word0 'Set VelFlags0.4 Case 3 Clear VelFlags0.2 Clear VelFlags0.6 Set Drup3 'Task_rsi[5] = (128-velo) <<9 'If Task_rsi[5] < (Task_rsi[2] << 1) Then Task_rsi[5] = Task_rsi[2] << 1 ' ' to be tested on robot 'veltim = Task_rsi[5] << Shift 'Cnt.Word0 = CntLw 'veltim = veltim + Cnt ' Cnt + Task_rsi[5] 'Velmsb[5] = veltim.Word1 'VelLsb[5] = veltim.Word0 'Set VelFlags0.5 Case 4 Clear VelFlags0.3 Clear VelFlags0.7 Set Drup4 End Select EndIf Set noteAan '= 255 'reset EndIf GoTo Check_Timers Case Keypres_Status 'used for lite flashing speed modulation If notePres = 255 Then notePres = Bytein Else pres = Bytein GoSub KeyPres EndIf GoTo Check_Timers Case Control_Status 'this is where the action takes place for controllers If Ctrl = 255 Then Ctrl = Bytein Else value = Bytein GoSub Controller EndIf GoTo Check_Timers ' Case ProgChange_Status ' If prog = 255 Then 'single byte message ' prog = Bytein 'weak coding... ' GoSub ProgChange 'subroutine ' EndIf End Select EndIf 'einde midi input parser Check_Timers: ' here we check the Task counters and compare them with the 32 bit cnt value ' using the Velflags dword variable: ' Velflags0.0 - 3: dripsize tasks ' Velflags0.4 - 7: dripping frequency If VelFlags0 > 0 Then 'if any bit is set here, there is a timer running If VelFlags0.0 = 1 Then veltim.Word1 = Velmsb[0] 'the high word of Dword veltim.Word0 = VelLsb[0] 'the low word of Dword Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then Clear drup1 ' sluit ventiel. Clear VelFlags0.0 ' stop task. It's a oneshot!!! EndIf EndIf If VelFlags0.1 = 1 Then veltim.Word1 = Velmsb[1] veltim.Word0 = VelLsb[1] Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then Clear drup2 Clear VelFlags0.1 EndIf EndIf If VelFlags0.2 = 1 Then veltim.Word1 = Velmsb[2] veltim.Word0 = VelLsb[2] Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then Clear drup3 Clear VelFlags0.2 EndIf EndIf If VelFlags0.3 = 1 Then veltim.Word1 = Velmsb[3] veltim.Word0 = VelLsb[3] Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then Clear drup4 Clear VelFlags0.3 EndIf EndIf ' following 4 tasks steer the frequency of the dripping If VelFlags0.4 = 1 Then veltim.Word1 = Velmsb[4] veltim.Word0 = VelLsb[4] Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then ' load task0 to start a drip: ' rsi in 20us units! veltim = Task_rsi[0] + Cnt ' add period duration 32 bits addition Velmsb[0] = veltim.Word1 VelLsb[0] = veltim.Word0 Set VelFlags0.0 ' gets reset after each drip Set Drup1 ' open valve ' and now reprogram the dripping frequency with randomize. i = Sensor[0] >> 1 Random wRnd veltim4 = Dur[i] + wRnd veltim = veltim4 + Cnt Velmsb[4] = veltim.Word1 VelLsb[4] = veltim.Word0 EndIf EndIf If VelFlags0.5 = 1 Then veltim.Word1 = Velmsb[5] veltim.Word0 = VelLsb[5] Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then ' reload task1 to start a new drip: veltim = Task_rsi[1] << Shift veltim = veltim + Cnt Velmsb[1] = veltim.Word1 VelLsb[1] = veltim.Word0 Set VelFlags0.1 ' gets reset after each drip Set Drup2 ' and now reprogram the dripping frequency i = Sensor[1] >> 1 Random wRnd veltim5 = Dur[i] + wRnd veltim = veltim5 + Cnt Velmsb[5] = veltim.Word1 VelLsb[5] = veltim.Word0 EndIf EndIf If VelFlags0.6 = 1 Then veltim.Word1 = Velmsb[6] veltim.Word0 = VelLsb[6] Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then ' load task2 to start a drip: 'Task_rsi[2] = dripsize in 20us units! veltim = Task_rsi[2] << Shift veltim = veltim + Cnt Velmsb[2] = veltim.Word1 VelLsb[2] = veltim.Word0 Set VelFlags0.2 ' gets reset after each drip Set Drup3 ' and now reprogram the dripping frequency i = Sensor[2] >> 1 Random wRnd veltim6 = Dur[i] + wRnd veltim = veltim6 + Cnt Velmsb[6] = veltim.Word1 VelLsb[6] = veltim.Word0 EndIf EndIf If VelFlags0.7 = 1 Then veltim.Word1 = Velmsb[7] veltim.Word0 = VelLsb[7] Cnt.Word0 = CntLw 'read counter If Cnt >= veltim Then veltim = Task_rsi[3] << Shift veltim = veltim + Cnt Velmsb[3] = veltim.Word1 VelLsb[3] = veltim.Word0 Set VelFlags0.3 ' gets reset after each drip Set Drup4 ' and now reprogram the dripping frequency i = Sensor[3] >> 1 Random wRnd veltim7 = Dur[i] + wRnd veltim = veltim7 + Cnt Velmsb[7] = veltim.Word1 VelLsb[7] = veltim.Word0 EndIf EndIf Else If CntHw > 0xFF Then Clear CntHw EndIf FSR_Check: If Switch = 0 Then ' switch = port B.4 Clear VelFlags Clear Drup1 Clear Drup2 Clear Drup3 Clear Drup4 Set CC60 ' this disables the automated dripping Else Clear CC60 ' enable automated dripping End If If CC60 = 0 Then 'set cc60 > 0 to disable the sensors (necessary to make midi note drips possible) ' multitasking code: ' here we reprogram tasks 4,5,6,7 in function of readings from the Sensor's or the potmeters in the IRQ ' geen kracht op de FSR geeft minimale (threshold) spanning op Sensor[x] ' maximale kracht op de FSR of potmeter naar rechts geeft Sensor[x] = 255 If Sensor[0] < CC40 Then Clear VelFlags0.4 ' stop dripping if voltage measured is below the threshold value Clear Drup1 ' close valve Else If VelFlags0.4 = 0 Then ' makes sure it starts up... [21.09.2013 ???] i = Sensor[0] >> 1 ' make 7 bits Random wRnd ' for randomisation: veltim4 = Dur[i] + wRnd ' using the lookup : Cnt.Word0 = CntLw 'read counter veltim = veltim4 + Cnt Velmsb[4] = veltim.Word1 VelLsb[4] = veltim.Word0 EndIf Set VelFlags0.4 ' start dripping End If If Sensor[1] < CC41 Then Clear VelFlags0.5 ' stop dripping Clear Drup2 Else If VelFlags0.5 = 0 Then ' makes sure it starts up... i = Sensor[1] >> 1 Random wRnd veltim5 = Dur[i] + wRnd Cnt.Word0 = CntLw 'read counter veltim = veltim5 + Cnt Velmsb[5] = veltim.Word1 VelLsb[5] = veltim.Word0 EndIf Set VelFlags0.5 End If If Sensor[2] < CC42 Then Clear VelFlags0.6 ' stop dripping Clear Drup3 Else If VelFlags0.6 = 0 Then ' makes sure it starts up... i = Sensor[2] >> 1 Random wRnd veltim6 = Dur[i] + wRnd Cnt.Word0 = CntLw 'read counter veltim = veltim6 + Cnt Velmsb[6] = veltim.Word1 VelLsb[6] = veltim.Word0 EndIf Set VelFlags0.6 End If If Sensor[3] < CC43 Then Clear VelFlags0.7 ' stop dripping Clear Drup4 Else If VelFlags0.7 = 0 Then ' makes sure it starts up... i = Sensor[3] >> 1 Random wRnd veltim6 = Dur[i] + wRnd Cnt.Word0 = CntLw 'read counter veltim = veltim7 + Cnt Velmsb[7] = veltim.Word1 VelLsb[7] = veltim.Word0 EndIf Set VelFlags0.7 End If End If GoTo LOOP ' end of the main loop KeyPres: 'not implemented here Set notePres '= 255 Return ProgChange: Set prog '= 255 Return Pitchbend: 'only implemented on dsPIC based robots Set pblsb '= 255 Return Aftertouch: 'this is the channel aftertouch, affecting all notes Set aft '= 255 'not mandatory Return Controller: Select Ctrl Case 30 CC30 = value Task_rsi[0] = CC30 << 8 ' dripsize Case 31 CC31 = value Task_rsi[1] = CC31 << 8 ' dripsize Case 32 CC32 = value Task_rsi[2] = CC32 << 8 ' dripsize Case 33 CC33 = value Task_rsi[3] = CC33 << 8 Case 40 CC40 = value ' Sensor 1 threshold Case 41 CC41 = value Case 42 CC42 = value Case 43 CC43 = value Case 60 CC60 = value Clear VelFlags0.4 Clear VelFlags0.5 Clear VelFlags0.6 Clear VelFlags0.7 Case 66 'on/off for the robot If value = 0 Then GoSub PowerDown 'subroutine EndIf Case 100 If value < 9 Then ' mandatory limit Shift = value ' number of *2 multiplies for the timers EndIf 'HRSOut NoteOn_Status, Ctrl, value Case 123 GoSub AllNotesOff 'subroutine End Select Set Ctrl '= 255 'mandatory reset Return AllNotesOff: Clear VelFlags Clear Drup1 Clear Drup2 Clear Drup3 Clear Drup4 Return PowerDown: Clear VelFlags Clear Drup1 Clear Drup2 Clear Drup3 Clear Drup4 Clear CntHw ' reset 32-bit counter Return Dur_Lookup: 'this lookup is for a good scaling of the velocity byte on event periodicity ' the values are calculated based on a timer resolution of 24 microseconds. ' The values must be containable in a word (16 bits!) ' This is the simple Power Basic program wherewith the lookup was calculated: 'FUNCTION PBMAIN ()AS LONG ' OPEN "Whisper_Dur_scales.inc" FOR OUTPUT AS #1 ' LOCAL unit, fastest, slowest, velo_traject AS DOUBLE ' LOCAL velo, i AS DWORD ' unit = 0.000024 ' in seconds (24 microseconds) ' fastest = 1 / 16 ' 16 Hz ' slowest = 1.5 ' 0.66 Hz ' velo_traject = slowest - fastest ' PRINT# 1, "Lookup for durations in periodic events" ' FOR i = 1 TO 127 ' velo = velo_traject / i ' PRINT i; velo, ' PRINT# 1 ,"Dur[";i;"] ="; velo ' NEXT i 'DO: LOOP UNTIL INKEY$ <> "" 'END FUNCTION Dur[ 0 ] = 65535 ' maximum value Dur[ 1 ] = 59896 Dur[ 2 ] = 29948 Dur[ 3 ] = 19965 Dur[ 4 ] = 14974 Dur[ 5 ] = 11979 Dur[ 6 ] = 9983 Dur[ 7 ] = 8557 Dur[ 8 ] = 7487 Dur[ 9 ] = 6655 Dur[ 10 ] = 5990 Dur[ 11 ] = 5445 Dur[ 12 ] = 4991 Dur[ 13 ] = 4607 Dur[ 14 ] = 4278 Dur[ 15 ] = 3993 Dur[ 16 ] = 3743 Dur[ 17 ] = 3523 Dur[ 18 ] = 3328 Dur[ 19 ] = 3152 Dur[ 20 ] = 2995 Dur[ 21 ] = 2852 Dur[ 22 ] = 2723 Dur[ 23 ] = 2604 Dur[ 24 ] = 2496 Dur[ 25 ] = 2396 Dur[ 26 ] = 2304 Dur[ 27 ] = 2218 Dur[ 28 ] = 2139 Dur[ 29 ] = 2065 Dur[ 30 ] = 1997 Dur[ 31 ] = 1932 Dur[ 32 ] = 1872 Dur[ 33 ] = 1815 Dur[ 34 ] = 1762 Dur[ 35 ] = 1711 Dur[ 36 ] = 1664 Dur[ 37 ] = 1619 Dur[ 38 ] = 1576 Dur[ 39 ] = 1536 Dur[ 40 ] = 1497 Dur[ 41 ] = 1461 Dur[ 42 ] = 1426 Dur[ 43 ] = 1393 Dur[ 44 ] = 1361 Dur[ 45 ] = 1331 Dur[ 46 ] = 1302 Dur[ 47 ] = 1274 Dur[ 48 ] = 1248 Dur[ 49 ] = 1222 Dur[ 50 ] = 1198 Dur[ 51 ] = 1174 Dur[ 52 ] = 1152 Dur[ 53 ] = 1130 Dur[ 54 ] = 1109 Dur[ 55 ] = 1089 Dur[ 56 ] = 1070 Dur[ 57 ] = 1051 Dur[ 58 ] = 1033 Dur[ 59 ] = 1015 Dur[ 60 ] = 998 Dur[ 61 ] = 982 Dur[ 62 ] = 966 Dur[ 63 ] = 951 Dur[ 64 ] = 936 Dur[ 65 ] = 921 Dur[ 66 ] = 908 Dur[ 67 ] = 894 Dur[ 68 ] = 881 Dur[ 69 ] = 868 Dur[ 70 ] = 856 Dur[ 71 ] = 844 Dur[ 72 ] = 832 Dur[ 73 ] = 820 Dur[ 74 ] = 809 Dur[ 75 ] = 799 Dur[ 76 ] = 788 Dur[ 77 ] = 778 Dur[ 78 ] = 768 Dur[ 79 ] = 758 Dur[ 80 ] = 749 Dur[ 81 ] = 739 Dur[ 82 ] = 730 Dur[ 83 ] = 722 Dur[ 84 ] = 713 Dur[ 85 ] = 705 Dur[ 86 ] = 696 Dur[ 87 ] = 688 Dur[ 88 ] = 681 Dur[ 89 ] = 673 Dur[ 90 ] = 666 Dur[ 91 ] = 658 Dur[ 92 ] = 651 Dur[ 93 ] = 644 Dur[ 94 ] = 637 Dur[ 95 ] = 630 Dur[ 96 ] = 624 Dur[ 97 ] = 617 Dur[ 98 ] = 611 Dur[ 99 ] = 605 Dur[ 100 ] = 599 Dur[ 101 ] = 593 Dur[ 102 ] = 587 Dur[ 103 ] = 582 Dur[ 104 ] = 576 Dur[ 105 ] = 570 Dur[ 106 ] = 565 Dur[ 107 ] = 560 Dur[ 108 ] = 555 Dur[ 109 ] = 550 Dur[ 110 ] = 545 Dur[ 111 ] = 540 Dur[ 112 ] = 535 Dur[ 113 ] = 530 Dur[ 114 ] = 525 Dur[ 115 ] = 521 Dur[ 116 ] = 516 Dur[ 117 ] = 512 Dur[ 118 ] = 508 Dur[ 119 ] = 503 Dur[ 120 ] = 499 Dur[ 121 ] = 495 Dur[ 122 ] = 491 Dur[ 123 ] = 487 Dur[ 124 ] = 483 Dur[ 125 ] = 479 Dur[ 126 ] = 475 Dur[ 127 ] = 472 Return '[EOF]