' ****************************************************************************** ' * Korn_Hub.bas * ' * Robot Control Firmware with MIDI-Input * ' * for Proton+ compiler on 18F2520 microchip controller * ' * Godfried-Willem Raes * ' ****************************************************************************** ' board description: MidiHub board, rev. 3, nov.2006 [batch produced 2010] ' 26.10.2010: Project start. Working base: puff-eye code. ' Programmer used: PicKIT2 (microchip) ' CC10: soll-position for motor. ' CC31: motor speed control ' Calibration procedure in 3 passes, copied from Puff eyes ' CC90, algo's 1,2,3,4 implemented for PIR sensors as used in Puff. ' 29.10.2010: Two Pepperl + Fuchs sensors added: analog inputs. ' 30.10.2010: New hardware finished. ' 31.10.2010: First firmware version: We keep CenterVal constant at 2^15 ' change: motor controller now set for 1/8 step ' thus trajects become a factor 8 larger ' bug: motpos needed to be initialized! ' to implement: accel on startup and decel approaching goal. ' Version 1.1: Complete calibration implemented. Takes quite some time... ' 01.11.2010: Version 1.2 flashed: bad. ' ReadADC() does not work on higher channels than 0 ' ADin does work!!! ' Now at least the calibration code works nicely. ' small accelleration build in on motor starts. ' Version 1.3 flashed: the PIR code is still not functioning ' Version 1.4: with slower PIR sampling rate and longer motor ' acceleration values ' 88 system vars used (only 95 available) ' 02.11.2010: Evaluation session with users. ' controller for horizontal position now #12: panning. ' code cleanup. ' removal of the connection between the center windings of the motor ' improved motor movement substantially. However discontinuous behaviour ' still occurs. ' Now at version 1.5 ' MidiOut procedures added for debugging. ' 03.11.2010: Reading TMR0L or TMR0H directly does NOT work! ' bug killed: we forgot to set the highword of Cnt . Now done in the IRQ ' TO DO: check left right in the PIR sensors and improve body seeking ' algorithm ' Now at version 1.9 ' 04.11.2010: CC32 added for changing the accelleration curve for the motor. ' CC12 removed and replaced with CC10, the standard for panning ' CC101 added for midi output data rate PIR sensors ' flashing of RED led light implemented. ' Now at version 2.0 - first real release version ' 90 system var's used. ' 05.11.2010: Eye lights mapping error repaired. ' bug in clear Velflags0 when motpos=sollpos repaired. This ' switched off flashing lites as well.... ' Now at version 2.1 ' 06.11.2010: decelleration on approach of sollpos implemented as well now. ' minor bugs in clear of velflags0 removed (interference with lites) ' Now at version 2.2 .Now 93 systems vars used (95 is maximum!!!) ' 07.11.2010: Version 2.3 - with some improvement in the slowdown/speedup algorithm ' both ramps are linear, symmetric and controlled with CC32. ' all 95 available system variables are now used. ' Components to be controlled: lights: ' X15-2, RC3 - pin 14 blue LED assembly (eye_left) ' X15-3 RC0 - pin 11 blue LED assembly (eye_right) ' stepper motor (via controller board) X11 & X12 ' required signals: ' clock (pulse duration >= 10 microseconds, trig on negative edge) ' RB - X12-2 pin 21 ' RB - X12-3 pin 22 RED 1W LED (mounted on PC-board) ' direction RC4 - X11-3 pin 16 ' enable RC5 - X11-2 pin 15 ' 2 sensor inputs (analog microswitches)- position end sensors - ' left X16-3 (RA4 pin 5) Pepperl+Fuchs NAMUR types ' right X16-4 (RA3 pin 4) ' 2 sensor inputs pyrodetectors left X16-1 (RA6 pin 7) ' right X16-2 (RA5 pin 6) ' pwm not used in Korn ' X17-2 ' X17-3 'Include "18F2525.inc" 'version for the korn-hub board. Include "18F2520.inc" 'Include "18F25K20.inc" 'for test & debug on an Amicus board. 'constant definitions: 'initialisations for the midi input parser: Symbol Midichannel = 12 ' Korn_Channel 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 = 5 '8 ' maximum 16 , sofar we use only 3 tasks here. Symbol AccelMax = 64 '40 '64 ' default acceleration range for motor startup 'status bytes for midi-output messages: Symbol PBCh0 = 224 Symbol PBCh1 = 225 Symbol PBCh2 = 226 Symbol PBCh3 = 227 Symbol PBCh4 = 228 Symbol PBCh5 = 229 Symbol PBCh6 = 230 Symbol PBCh7 = 231 Symbol PBCh8 = 232 Symbol PBCh9 = 233 Symbol PBCh10 = 234 Symbol PBCh11 = 235 Symbol PBCh12 = 236 Symbol PBCh13 = 237 Symbol PBCh14 = 238 Symbol PBCh15 = 239 'Symbol Proxval = 400 ' real range = 345 (infinity) to 897 (full contact) ' 400 corresponds to 1.952 V from the sensor. ' Setup the USART Declare Hserial_Baud = 31250 ' Set baud rate for 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... This must be 31250 for MIDI ' Create variables (ordered such that all words start on an even boundary) ' Dim CntHw As Word System 'only used in the timer0 interrupt. ' replaced by incrementing Cnt.Highword in the IRQ handler Dim Cnt As Dword System ' 32 bit counter Dim CntLw As TMR0L.Word 'this is the trick to read both TMR0L and TMR0H 'it makes Cntlw the low word of cnt ' Dim S06 As TMR0H.7 ' 0.6 Hz ' Dim S12 As TMR0H.6 ' 1.2 Hz ' Dim S24 As TMR0H.5 ' Dim S48 As TMR0H.4 ' Dim S96 As TMR0H.3 ' Dim S192 As TMR0H.2 ' Dim S384 As TMR0H.1 ' Dim S768 As TMR0H.0 ' 76.8 Hz ' Dim S3070 As TMR0L.7 ' 307 Hz ' Dim S6140 As TMR0L.6 ' Dim S12290 As TMR0L.5 ' Dim S24580 As TMR0L.4 ' Dim S49150 As TMR0L.3 ' Dim S98300 As TMR0L.2 ' Dim S196610 As TMR0L.1 ' Dim S393220 As TMR0L.0 '39322 Hz Dim Bytein As Byte System ' midi byte read from buffer Dim StBit As Bytein.7 ' highest bit of ByteIn ' 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 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 VelFlags As Word System ' bits 0 - 15 used as flags for active velo-timers Dim VelFlags0 As VelFlags.Byte0 ' alias for bits 0-7 ' Dim VelFlags1 As VelFlags.Byte1 ' bits 8-15 Dim CC66 As Byte System ' global on/off switch Dim st As Byte System Dim b1 As Byte System Dim b2 As Byte System Dim acc As Byte System 'for accelleration on motor starts. Dim Block As Byte System Dim BlockLeft As Block.0 Dim BlockRight As Block.1 Dim MotPos As Word System ' actual position of the motor Dim MinPos As Word System ' extreme left position Dim MaxPos As Word System ' extreme right position Dim CenterPos As Word System ' central position, in principle = 32768 Dim sollPos As Word System ' set with controller 21 Dim Traj As Word System ' traject in nr. of steps. Dim CC90 As Byte System ' robotic mode: automation of motor with sensors Dim CC100 As Byte System Dim MotorPeriod As Word System ' motor speed, CC31 Dim LeftPirCnt As Byte System Dim RightPirCnt As Byte System Dim tmp As Word System Dim PF_Left As Word System 'for reading the analog input from the PF sensors Dim PF_Right As Word System '10 bit values Dim ProxvalLeft As Word System Dim ProxvalRight As Word System Dim CalibFlag As Byte System Dim calibleft As CalibFlag.0 Dim Calibright As CalibFlag.1 Dim PirClock As Byte System Dim PirClockbit As PirClock.0 'toggles at the PIR sampling rate Dim PirMoveBit As PirClock.1 'toggles at the PIR task rate Dim MotMonbit As PirClock.2 Dim PFLeftbit As PirClock.3 Dim PFRightbit As PirClock.4 Dim SollPosBit As PirClock.5 Dim PirRateLBit As PirClock.6 Dim PirRateRBit As PirClock.7 Dim lsb As Byte System Dim msb As Byte System Dim TimVal As Word System 'used as holdvar for ReadTimer0 () Dim CC32 As Byte System 'added 04.11.2010 Dim CC101 As Byte System 'data rate controller for midi output Dim Lites As Byte System 'bits used as flags Dim Remweg As Word System 'added 06.11.2010 Dim slowdown As Byte System Dim xtraTim As Byte System ' Dim Moving as Byte System we can better use Velflags0 to know whether we are moving. '----------------------------------------------------------------------------------------- ' Load the USART Interrupt handler And buffer read subroutines into memory Include "Midi_Irq.inc" ' our own version for UART And Timer0 Interrupt Include "Timers.inc" ' required for velo support with timed pulses and periods. 'Include "DwordArrays.inc" ' support for dword arrays. Include "ADC.inc" ' ADC macros 'first framework for a multitasker: Dim Task_rsi[NrTasks] As Word 'task reschedule interval (period), if 0 the task is not active 'oly used here for lites, since we use the MotorPeriod vaiable as rsi. Dim Velmsb[NrTasks] As Word 'the application for velo-timers, is in fact just a one-shot task Dim VelLsb[NrTasks] As Word 'DeclareDwordArray(TimeVals , NrTasks) 'alternative using the macro's. [not yet used] ' Mapping defines for midi-events on pin outputs and inputs: ' There are only 16 pins free: RB0-5, RC0, RC3-5, RA0-RA5 $define Eye_Left PORTC.3 ' left eye light front , pin 11 X15 $define Eye_Right PORTC.0 ' right eye light front, pin 14 X15 $define Motor_Enable PORTC.5 ' pin 16 X11-2 $define Motor_Dir PORTC.4 ' pin 15 X11-3 $define Motor_Clock PORTB.0 ' pin 21 X12-2 $define RedLite PORTB.1 ' pin 22 X12-3 $define PIR_Right PORTA.5 ' pin 7 X16-1 INPUT from PIR right $define PIR_Left PORTA.4 ' pin 6 X16-2 INPUT from PIR left ' $define PF_Left PORTA.1 ' pin 3 X16-3 INPUT from end sensor left - was bug!!! ' $define PF_Right PORTA.0 ' pin 2 X16-4 INPUT from end sensor right $define Green PORTA.3 ' led for debug and development $define Yellow PORTA.2 ' led for debug and development ' $define PWM1 PORTC.1 ' PWM channel -RC1 HPWM1 X17 ' $define PWM2 PORTC.2 ' PWM channel -RC2 HPWM0 X17 'red LED for debug: $define Debug_Led PORTB.5 ' mapped on midi note 0 for testing- watchdog ' configure the input and output pins: TRISA = %01110011 'bits set to 0 are output, 1 = input TRISB = %11100000 TRISC = %11000000 '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 ADCON1 = %11000000 'Set analogue input on PortA.0 and Port A.1 'make sure we initialize those pins to 0 on start up: Low Eye_Left Low Eye_Right High Motor_Enable 'motor disabled at start High Motor_Dir Low Motor_Clock 'to make it high at rest on the controller input. Low RedLite Low Debug_Led Input PIR_Left Input PIR_Right Low Green Low Yellow Clear Lites Clear VelFlags '----------------------------------------------------------------------------------------- ' Main program starts here MAIN: High Debug_Led DelayMS 100 ' wait for stability Low Debug_Led 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 ' following only required for pulse outputs: ' Note that we can only use timer0 since the other timers ' are used by the PWM subsystem ' 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 Opentimer0 (Timer_INT_On & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256) ' Setup the High priorities for the interrupts ' Open the ADC: ' Fosc/32 ' Right justified for 10-bit operation ' Tad value of 0 ' Vref+ at Vcc : Vref- at Gnd ' Make AN0 and AN1 a analogue inputs ' OpenADC(ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_0_TAD, ADC_CH0 & ADC_CH1 & ADC_REF_VDD_VSS, ADC_2ANA) ' Gosub Ana_Debug 'loops forever!!! GoSub Init_Korn ' required initialisation DelayMS 1000 GoSub Calibrate ' perform calibration on a cold boot DelayMS 200 ' start the main program loop: Start_Loop: ' Create an infinite loop Bytein = HRSIn ' Read data from the serial buffer, with no timeout ' Start the midi parser. Midi_Parse: If Bytein > ProgChange_Status Then ' was Pitchbend_Status Then , but here this is 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 Midi_Parse_End 'throw away... Else Clear statusbyte 'reset the status byte GoTo Midi_Parse_End '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 noteUit = 255 'reset value. Cannot be 0 !!! release = 255 '0 is a valid midi note! Case NoteOn_Status statusbyte = Bytein noteAan = 255 velo = 255 ' Case Keypres_Status ' statusbyte = Bytein ' notePres = 255 ' pres = 255 Case Control_Status statusbyte = Bytein Ctrl = 255 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 Midi_Parse_End 'disregard Case NoteOff_Status If noteUit = 255 Then noteUit = Bytein Else release = Bytein 'message complete, so we can do the action... Select noteUit Case 121 Clear Lites.1 Low Eye_Left Clear VelFlags0.3 'blue Case 122 Clear Lites.2 Clear VelFlags0.4 Low Eye_Right 'blue Case 123 Clear Lites.0 Clear VelFlags0.2 Low Redlite 'red led light End Select noteUit = 255 'reset EndIf GoTo Midi_Parse_End Case NoteOn_Status If noteAan = 255 Then noteAan = Bytein Else velo = Bytein If velo = 0 Then Select noteAan Case 121 Clear Lites.1 Clear VelFlags0.3 Low Eye_Left Case 122 Clear Lites.2 Clear VelFlags0.4 Low Eye_Right Case 123 Clear Lites.0 Clear VelFlags0.2 Low Redlite End Select noteAan = 255 'reset !!! GoTo Midi_Parse_End 'jump out EndIf Select noteAan Case 121 Set Lites.1 High Eye_Left If velo < 127 Then Set VelFlags0.3 Cnt.Word0 = Readtimer0 () 'CntLw 'read timer Task_rsi[3] = (~velo & 127) << 9 veltim = Cnt + Task_rsi[3] 'add the period duration Velmsb[3] = veltim.Word1 VelLsb[3] = veltim.Word0 Else Clear VelFlags0.3 EndIf Case 122 Set Lites.2 High Eye_Right If velo < 127 Then Set VelFlags0.4 Cnt.Word0 = Readtimer0 () 'CntLw 'read timer Task_rsi[4] = (~velo & 127) << 9 veltim = Cnt + Task_rsi[4] 'add the period duration Velmsb[4] = veltim.Word1 VelLsb[4] = veltim.Word0 Else Clear VelFlags0.4 EndIf Case 123 Set Lites.0 High Redlite If velo < 127 Then Set VelFlags0.2 Cnt.Word0 = Readtimer0 () 'CntLw 'read timer Task_rsi[2] = (~velo & 127) << 9 veltim = Cnt + Task_rsi[2] 'add the period duration Velmsb[2] = veltim.Word1 VelLsb[2] = veltim.Word0 Else Clear VelFlags0.2 EndIf Case Else noteAan = 255 GoTo Midi_Parse_End End Select noteAan = 255 'reset EndIf GoTo Midi_Parse_End ' Case Keypres_Status ' If notePres = 255 Then ' notePres = Bytein ' Else ' pres = Bytein ' GoSub KeyPres ' EndIf ' GoTo Midi_Parse_End Case Control_Status If Ctrl = 255 Then Ctrl = Bytein Else value = Bytein GoSub Controller EndIf GoTo Midi_Parse_End ' Case ProgChange_Status ' If prog = 255 Then 'single byte message ' prog = Bytein 'weak coding... ' GoSub ProgChange ' EndIf End Select EndIf Midi_Parse_End: 'jump out of parser label PIR_Sensors: If CC90 > 0 Then TimVal = ReadTimer0 () If PirClockbit <> TimVal.8 Then ' 154 Hz PirClockbit = TimVal.8 On CC90 - 1 GoSub Task_Pir1, Task_Pir2, Task_Pir3 End If TimVal = ReadTimer0 () If PirMoveBit <> TimVal.15 Then ' 1.2 Hz PirMoveBit = TimVal.15 On CC90 - 1 GoSub PirMove1, PirMove2, PirMove3 End If End If 'end sensor polling If CC100 > 3 Then If CC100.2 = 1 Then 'motor position monitoring TimVal = ReadTimer0 () If MotMonbit <> TimVal.10 Then 'rate= 38.4 Hz MotMonbit = TimVal.10 pblsb = (MotPos - MinPos) & 0x007F '// 128 pbmsb = (MotPos - MinPos) >>7 ' / 128 HRSOut PBCh2, pblsb, pbmsb EndIf EndIf If CC100.3 = 1 Then 'output PF sensor left data TimVal = ReadTimer0 () If PFLeftbit <> TimVal.8 Then ' rate = 154 Hz PFLeftbit = TimVal.8 PF_Left = ADIn 0 pbmsb = PF_Left >>7 '/ 128 pblsb = PF_Left & 0x007F '// 128 HRSOut PBCh3, pblsb, pbmsb EndIf EndIf If CC100.4 = 1 Then 'output PF sensor right data TimVal = ReadTimer0 () If PFRightbit <> TimVal.8 Then '154Hz PFRightbit = TimVal.8 PF_Left = ADIn 1 pbmsb = PF_Left >>7 ' = / 128 pblsb = PF_Left & 0x007F ' = mod 128 HRSOut PBCh4, pblsb, pbmsb EndIf EndIf If CC100.5 = 1 Then 'Loopspeed monitoring TimVal = ReadTimer0 () pblsb = TimVal & 0x007F HRSOut PBCh5, pblsb, 0 EndIf If CC100.6 = 1 Then TimVal = ReadTimer0 () If SollPosBit <> TimVal.10 Then '38Hz SollPosBit = TimVal.10 pblsb = (sollPos - MinPos) & 0x007F '// 128 pbmsb = (sollPos - MinPos) >> 7 ' / 128 HRSOut PBCh6, pblsb, pbmsb EndIf EndIf EndIf Check_Timers: ' here we check the Task counters and compare them with the cnt value ' using the Velflags dword variable: If VelFlags0 > 0 Then 'if any bit is set here, there is a timer running 'Cnt.HighWord = CntHw 'we forgot this one!!! - now done in the IRQ Cnt.LowWord = ReadTimer0 () 'CntLw 'read counter If VelFlags0.0 = 1 Then veltim.Word1 = Velmsb[0] veltim.Word0 = VelLsb[0] If Cnt >= veltim Then GoSub Task0 EndIf EndIf If VelFlags0.1 = 1 Then veltim.Word1 = Velmsb[1] veltim.Word0 = VelLsb[1] If Cnt >= veltim Then GoSub Task1 EndIf EndIf If VelFlags0.2 = 1 Then veltim.Word1 = Velmsb[2] veltim.Word0 = VelLsb[2] If Cnt >= veltim Then GoSub Task2 'flashing red LED EndIf EndIf If VelFlags0.3 = 1 Then veltim.Word1 = Velmsb[3] veltim.Word0 = VelLsb[3] If Cnt >= veltim Then GoSub Task3 'left eye LED EndIf EndIf If VelFlags0.4 = 1 Then veltim.Word1 = Velmsb[4] veltim.Word0 = VelLsb[4] If Cnt >= veltim Then GoSub Task4 'flashing red LED EndIf EndIf EndIf GoTo Start_Loop 'end of the main loop KeyPres: 'the note to which the pressure should be applied is passed in NotePres, the value in Pres 'modulate the loudness of playing notes. notePres = 255 Return ProgChange: prog = 255 'this is not realy required Return Pitchbend: 'only implemented on dsPIC based robots pblsb = 255 Return Aftertouch: 'this is the channel aftertouch, affecting all notes aft = 255 'not mandatory Return Controller: Select Ctrl Case 10 'panning 'in 1/8 mode this becomes 8 times larger: 'the traject is 1450, so the multiplier becomes 1450/128 = 11.33 Select value Case 0 Ctrl = 255 GoSub CalibrateLeft Return Case < 64 sollPos = 64 - value sollPos = CenterPos - (sollPos * 12) If sollPos < MinPos Then sollPos = MinPos Case 64 sollPos = CenterPos Case 127 Ctrl = 255 GoSub CalibrateRight Return Case Else sollPos = value - 64 sollPos = CenterPos + (sollPos * 12) If sollPos > MaxPos Then sollPos = MaxPos End Select Ctrl = 255 If sollPos < MotPos Then Clear VelFlags0.1 'make sure there is no task active for the other direction... Remweg = MotPos - sollPos 'move left GoSub MoveLeft Return EndIf If sollPos > MotPos Then 'move right Clear VelFlags0.0 Remweg = sollPos - MotPos GoSub MoveRight Return EndIf If sollPos = MotPos Then Clear Remweg High Motor_Enable Clear VelFlags0.0 Clear VelFlags0.1 Return EndIf Case 31 'can be used to change the speed of the motor 'MotorPeriod = 135 - value 'for 1/8 step mode 'better range gives: MotorPeriod = 95 - (value >>1) ' value = 0 - 62 Ctrl = 255 ' so motorperiod = 95 - 33 Return 'would be 3.2ms to 0.825ms Case 32 'can be used to modify the accelleration curve of the motor on starts. 'on startup CC32 is initialized to accelmax, or value 64. 'since V2.2: also applied to the decelleration on approaching sollpos CC32 = value Ctrl = 255 Return Case 66 'on/off for robot If value = 0 Then GoSub AllNotesOff EndIf CC66 = value GoTo Ctrl_Parse_End Case 67 Ctrl = 255 If value = 64 Then GoSub CalibrateLeft EndIf GoTo Ctrl_Parse_End Case 68 Ctrl = 255 If value = 64 Then GoSub CalibrateRight EndIf GoTo Ctrl_Parse_End Case 70, 71 If value = 64 Then GoSub Calibrate End If GoTo Ctrl_Parse_End Case 90 CC90 = value 'robotic sensor modes If CC90 > 3 Then CC90 = 0 'only 3 algos implemented sofar GoTo Ctrl_Parse_End Case 100 'midi output controller CC100 = value 'bit 0 = send Left Pir data as pitchbend on channel 0 lsb-msb 'bit 1 = send Right Pir data as pitchbend channel 1 lsb-msb 'bit 2 = send MotPos as pitchbend channel 2 lsb-msb 'bit 3 = send PF_Left sensor as pitchbend channel 3 lsb-msb 'bit 4 = send PF_Right sensor as pitchbend channel 4 lsb-msb 'bit 5 = loopspeed as pitchbend channel 5 'bit 6 = sollpos monitoring Case 101 'midi output data rate for PIR midi output - possible values are 1-8 If value > 0 Then If value < 9 Then CC101 = 16 - value 'CC101 = 15 --> data rate ca. 1.2Hz value = 1 'CC101 = 14 --> data rate ca. 2.4Hz value = 2 'CC101 = 13 --> data rate ca. 4.8Hz value = 3 'CC101 = 12 --> data rate ca. 9.6Hz value = 4 'CC101 = 11 --> data rate ca 20Hz value = 5 'CC101 = 10 --> data rate ca 40Hz value = 6 'CC101 = 9 --> data rate ca 80Hz value = 7 'CC101 = 8 --> data rate ca. 160Hz value = 8 EndIf EndIf If CC101 < 8 Then CC101 = 12 '10Hz If CC101 > 15 Then CC101 = 12 Case 123 'all notes off GoSub AllNotesOff End Select Ctrl_Parse_End: Ctrl = 255 'mandatory reset Return AllNotesOff: Low Eye_Right Low Eye_Left sollPos = MotPos 'interrupt any ongoing movement task High Motor_Enable 'ínverted! - so this is disabled. Low Redlite Low Motor_Clock 'inverted! Clear CC90 'reset PIR activity Clear CC100 Clear VelFlags0 'stops all tasks Clear Lites Return Init_Korn: 'instrument must be in a central position on startup 'if it is touching any of the sensors, initialisation will go wrong!!! Clear CC90 Clear CC100 CC32 = AccelMax CC101 = 12 'gives 10Hz data rate for PIR midi output MinPos = 0 MaxPos = 65535 CenterPos = 32768 '2 ^15 MotPos = 32768 'assume this on init. MotorPeriod = 112 High Motor_Enable 'unpower motor Low Redlite Low Green Low Yellow Low Motor_Clock Clear VelFlags0 'no timer active on start up Clear CalibFlag PF_Left = ADIn 0 'ReadADC(0) ProxvalLeft = ADIn 0 'ReadADC(0) 'sensor waarde onbekrachtigd. ProxvalLeft = ProxvalLeft + (PF_Left >> 2) '25% extra Low Eye_Left PF_Right = ADIn 1 'ReadADC(1) ProxvalRight = ADIn 1 'ReadADC(1) ProxvalRight = ProxvalRight + (PF_Right >> 2) Low Eye_Right Clear BlockLeft Clear BlockRight Clear Lites Return MoveLeft: 'sollpos < motpos If BlockLeft = 1 Then 'can we move left? Clear BlockRight 'make the right movement possible MotPos = MinPos sollPos = MinPos High Motor_Enable 'unpower the motor as we cannot move. Clear VelFlags0.0 'disable the task. Return EndIf Clear BlockRight 'we can clear this since we move the other sense. PF_Left = ADIn 0 'ReadADC(0) If PF_Left < ProxvalLeft Then 'check endsensor Low Motor_Enable 'motor should become enabled now High Motor_Dir 'rotate left High Green acc = CC32 'AccelMax PulseOut Motor_Clock, 10, High 'was 12 Dec MotPos 'load task0: Set VelFlags0.0 'Cnt.HighWord = CntHw Cnt.LowWord = ReadTimer0 () 'CntLw 'read timer veltim = Cnt + MotorPeriod + acc 'add the period duration for the motor pulses Velmsb[0] = veltim.Word1 VelLsb[0] = veltim.Word0 Else 'endsensor reached Set BlockLeft Clear BlockRight MotPos = MinPos sollPos = MinPos High Motor_Enable 'unpower, disabled Clear VelFlags0.0 Low Green EndIf Return MoveRight: 'sollpos > motpos If BlockRight = 1 Then 'ímpossible to move right Clear BlockLeft 'make left movement possible MotPos = MaxPos 'sollPos sollPos = MotPos Clear VelFlags0.1 High Motor_Enable 'unpower motor Return EndIf Clear BlockLeft 'since we get a command to right, we can clear this one PF_Right = ADIn 1 'ReadADC(1) If PF_Right < ProxvalRight Then 'make sure we are not at the end. Low Motor_Enable 'inverted: 0 enables the motor Low Motor_Dir 'direction of rotation High Yellow acc = CC32 'defaults to AccelMax PulseOut Motor_Clock, 10, High ' 12 microsecond pulse (10 microsecond is minimum) Inc MotPos 'load task1: Set VelFlags0.1 'Cnt.HighWord = CntHw Cnt.LowWord = ReadTimer0 () 'CntLw 'read timer veltim = Cnt + MotorPeriod + acc 'add the period duration for the motor pulses Velmsb[1] = veltim.Word1 VelLsb[1] = veltim.Word0 Else 'endsensor was reached... Set BlockRight Clear BlockLeft MotPos = MaxPos sollPos = MotPos Clear VelFlags0.1 High Motor_Enable 'disable motor power Low Yellow End If Return CalibrateLeft: 'callibration procedure to calculate the extreme left position If BlockLeft = 0 Then sollPos = 0 High Green Clear VelFlags0.1 GoSub MoveLeft 'this starts the task, but we may never return here.. Set calibleft Else Clear VelFlags0.0 Set calibleft EndIf Return CalibrateRight: If BlockRight = 0 Then sollPos = 65535 '32998 High Yellow Clear VelFlags0.0 GoSub MoveRight Set Calibright Else Clear VelFlags0.1 Set Calibright EndIf Return Calibrate: 'this procedure does the complete calibration. It takes a few seconds to complete. 'it measures minpos and maxpos. These values are changed nowhere else. 'move extreme left: PF_Left = ADIn 0 ' ReadADC(0) If PF_Left < ProxvalLeft Then ' no stop reached Clear BlockLeft ' make left movement possible Else Set BlockLeft ' left stop reached Clear BlockRight ' make right movement possible EndIf If BlockLeft = 0 Then ' if we can move to the left High Motor_Dir ' rotate left Low Motor_Enable ' make active Repeat PulseOut Motor_Clock, 10, High DelayMS 2 ' pulsing frequency PF_Left = ADIn 0 ' ReadADC(0) Until PF_Left >= ProxvalLeft ' loop until left endsensor reached Set BlockLeft ' make any further movement to left impossible Clear BlockRight EndIf High Motor_Enable 'unpower motor High RedLite DelayMS 100 Low Redlite 'move extreme right: [here we count the steps!!!] PF_Right = ADIn 1 'ReadADC(1) If PF_Right < ProxvalRight Then ' no stop reached Clear BlockRight ' make right movement possible Clear Traj ' reset traject variable Else Set BlockRight ' right stop reached Clear BlockLeft ' make left movement possible EndIf If BlockRight = 0 Then ' if we can move to the right Low Motor_Dir ' rotate right Low Motor_Enable ' make active Repeat PulseOut Motor_Clock, 10, High DelayMS 2 Inc Traj ' increment the traject counter PF_Right = ADIn 1 'ReadADC(1) Until PF_Right >= ProxvalRight Set BlockRight ' make any further movement to right impossible Clear BlockLeft EndIf High Motor_Enable ' unpower motor High RedLite DelayMS 100 Low Redlite Traj = Traj >> 1 ' divide by 2 MotPos = 32768 + Traj MaxPos = MotPos 'so maxpos will be ca. 33493 MinPos = 32768 - Traj 'so minpos will be ca. 32043 CenterPos = 32768 'output the traject via midi out: lsb = Traj & 0x007F '14 bit format!!! msb = Traj >> 7 '/ 128 HRSOut PBCh7, lsb, msb 'for debug - gives half traject!!! 'result: Traj = 724 or 725 , so we have 1448 or 1450 for the full movement ' measured 02.11.2010 'move back to center position PF_Left = ADIn 0 ' ReadADC(0) If PF_Left < ProxvalLeft Then ' no stop reached Clear BlockLeft ' make left movement possible Else Set BlockLeft ' left stop reached Clear BlockRight ' make right movement possible EndIf If BlockLeft = 0 Then ' must be free to move High Motor_Dir ' rotate left Low Motor_Enable ' powerup motor Repeat PulseOut Motor_Clock, 10, High Dec MotPos DelayMS 1 PF_Left = ADIn 0 ' ReadADC(0) DelayMS 1 PF_Right = ADIn 1 ' ReadADC(1) Until MotPos = CenterPos ' error check: If PF_Right > ProxvalRight Then ' rood licht aan High Redlite EndIf If PF_Left > ProxvalLeft Then ' rood licht aan High RedLite EndIf sollPos = MotPos High Green High Yellow EndIf High Motor_Enable ' power down the motor. Return Task0: 'LEFT movement task for horizontal motor. 'called when a timer runs out 'recharge the timer, if no stop flag is set. ' by incrementing or decrementing the rsi (reschedule interval), we can 'make gentle speedups and slowdowns for motors, pwm's and fades. PF_Left = ADIn 0 ' ReadADC(0) If PF_Left >= ProxvalLeft Then 'end position reached Set BlockLeft Clear BlockRight Clear VelFlags0.0 Low Green High Motor_Enable ' power down MotPos = MinPos ' do not change minpos! sollPos = MinPos Return EndIf If MotPos = sollPos Then Clear VelFlags0.0 ' stop task, as position is reached Low Green High Motor_Enable ' disable motor Return End If ' now we are certain that a step to the left is possible and required: PulseOut Motor_Clock, 10, High If MotPos > 0 Then Dec MotPos If acc > 0 Then Dec acc 'speedup counter EndIf 'version 2.3, with slowdown Remweg = MotPos - sollPos If Remweg < CC32 Then slowdown = CC32 - Remweg.Byte0 Else Clear slowdown End If xtraTim = acc Max slowdown ' set the timer to reload task0: 'Cnt.HighWord = CntHw Cnt.LowWord = ReadTimer0 () 'CntLw 'read timer veltim = Cnt + MotorPeriod + xtraTim 'add the period duration for the motor pulses Velmsb[0] = veltim.Word1 VelLsb[0] = veltim.Word0 Return Task1: 'turn motor to the right: PF_Right = ADIn 1 ' ReadADC(1) If PF_Right >= ProxvalRight Then Set BlockRight Clear BlockLeft Clear VelFlags0.1 Low Yellow High Motor_Enable MotPos = MaxPos sollPos = MaxPos Return EndIf If MotPos = sollPos Then Clear VelFlags0.1 'stop task, as position is reached Low Yellow High Motor_Enable 'disable motor Return End If ' now we are certain that a step to the right is possible: PulseOut Motor_Clock, 10, High If MotPos < 65535 Then Inc MotPos If acc > 0 Then Dec acc EndIf 'version 2.3, with slowdown Remweg = sollPos - MotPos If Remweg < CC32 Then slowdown = CC32 - Remweg.Byte0 Else Clear slowdown EndIf xtraTim = acc Max slowdown ' set the timer to reload task1: 'Cnt.HighWord = CntHw Cnt.LowWord = ReadTimer0 () 'CntLw 'read timer veltim = Cnt + MotorPeriod + xtraTim 'add the period duration for the motor pulses Velmsb[1] = veltim.Word1 VelLsb[1] = veltim.Word0 Return Task2: 'red 1W LED light flashing task. If Lites.0 = 0 Then Clear VelFlags0.2 'stop task, as lite is switched off Low RedLite Else 'reload task2 - light Set VelFlags0.2 'can just stay set Cnt.LowWord = ReadTimer0 () veltim = Cnt + Task_rsi[2] 'add the period duration Velmsb[2] = veltim.Word1 VelLsb[2] = veltim.Word0 Toggle RedLite EndIf Return Task3: 'left eye LED light flashing task. If Lites.1 = 0 Then Clear VelFlags0.3 'stop task, as lite is switched off Low Eye_Left Else 'reload task3 - light Set VelFlags0.3 'can just stay set Cnt.LowWord = ReadTimer0 () veltim = Cnt + Task_rsi[3] 'add the period duration Velmsb[3] = veltim.Word1 VelLsb[3] = veltim.Word0 Toggle Eye_Left EndIf Return Task4: 'red 1W LED light flashing task. If Lites.2 = 0 Then Clear VelFlags0.4 'stop task, as lite is switched off Low Eye_Right Else 'reload task4 - light Set VelFlags0.4 'can just stay set Cnt.LowWord = ReadTimer0 () veltim = Cnt + Task_rsi[4] 'add the period duration Velmsb[4] = veltim.Word1 VelLsb[4] = veltim.Word0 Toggle Eye_Right EndIf Return 'periodic PIR sampling tasks: Task_Pir1: If PIR_Left =1 Then If LeftPirCnt < 255 Then Inc LeftPirCnt ' ceil to 255 Else If LeftPirCnt > 0 Then Dec LeftPirCnt ' ceil to 0 EndIf If PIR_Right =1 Then If RightPirCnt < 255 Then Inc RightPirCnt ' ceil to 255 Else If RightPirCnt > 0 Then Dec RightPirCnt EndIf If CC100.0 = 1 Then TimVal = ReadTimer0 () If PirRateLBit <> GetBit TimVal, CC101 Then ' not working: Timval.CC101 then PirRateLBit = GetBit TimVal,CC101 pblsb = LeftPirCnt & 0x007F pbmsb = LeftPirCnt >> 7 '/ 128 HRSOut PBCh0, pblsb, pbmsb EndIf EndIf If CC100.1 = 1 Then TimVal = ReadTimer0 () If PirRateRBit <> GetBit TimVal, CC101 Then PirRateRBit = GetBit TimVal,CC101 pblsb = RightPirCnt & 0x007F '// 128 pbmsb = RightPirCnt >> 7 '/ 128 HRSOut PBCh1, pblsb, pbmsb EndIf EndIf Return Task_Pir2: 'sampling task for the PIR sensors 'clever integrator... very nerveous version LeftPirCnt = LeftPirCnt >> 1 'shift right 1 position = / 2 LeftPirCnt.7 = PIR_Left 'set the new bit in the msb RightPirCnt = RightPirCnt >> 1 RightPirCnt.7 = PIR_Right 'both vars are now always confined to the 0-255 range If CC100.0 = 1 Then TimVal = ReadTimer0 () If PirRateLBit <> GetBit TimVal,CC101 Then PirRateLBit = GetBit TimVal,CC101 pblsb = LeftPirCnt & 0x007F '// 128 pbmsb = LeftPirCnt >> 7 '/ 128 HRSOut PBCh0, pblsb, pbmsb EndIf EndIf If CC100.1 = 1 Then TimVal = ReadTimer0 () If PirRateRBit <> GetBit TimVal,CC101 Then PirRateRBit = GetBit TimVal,CC101 pblsb = RightPirCnt & 0x007F '// 128 pbmsb = RightPirCnt >> 7 '/ 128 HRSOut PBCh1, pblsb, pbmsb EndIf End If Return Task_Pir3: 'sampling task for the PIR sensors 'clever integrator... relaxed version LeftPirCnt = LeftPirCnt << 1 'shift left 1 position = * 2 LeftPirCnt.0 = PIR_Left 'set the new bit in the lsb RightPirCnt = RightPirCnt << 1 RightPirCnt.0 = PIR_Right 'both vars are now always confined to the 0-255 range If CC100.0 = 1 Then TimVal = ReadTimer0 () If PirRateLBit <> GetBit TimVal,CC101 Then PirRateLBit = GetBit TimVal,CC101 pblsb = LeftPirCnt & 0x007F '// 128 pbmsb = LeftPirCnt >> 7 '/ 128 HRSOut PBCh0, pblsb, pbmsb EndIf EndIf If CC100.1 = 1 Then TimVal = ReadTimer0 () If PirRateRBit <> GetBit TimVal,CC101 Then PirRateRBit = GetBit TimVal,CC101 pblsb = RightPirCnt & 0x007F '// 128 pbmsb = RightPirCnt >> 7 '/ 128 HRSOut PBCh1, pblsb, pbmsb EndIf End If Return 'movement tasks based on PIR sampling: PirMove1: 'If VelFlags0.0 = 0 and VelFlags0.1 = 0 Then 'wait till previous move ended! If VelFlags0.0 | VelFlags0.1 = 0 Then sollPos = 255 - RightPirCnt sollPos = CenterPos + (sollPos <<3) tmp = 255 - LeftPirCnt tmp = tmp << 3 sollPos = sollPos - tmp If sollPos < MotPos Then 'High Eye_Left High Eye_Right Low Eye_Left GoSub MoveLeft EndIf If sollPos > MotPos Then 'High Eye_Right High Eye_Left Low Eye_Right GoSub MoveRight EndIf If sollPos = MotPos Then Low Eye_Right Low Eye_Left High Motor_Enable End If EndIf Return PirMove2: 'If VelFlags0.0 = 0 and Velflags0.1 = 0 Then If VelFlags0.0 | VelFlags0.1 = 0 Then sollPos = 255 - RightPirCnt sollPos = CenterPos + (sollPos << 3) tmp = 255 - LeftPirCnt sollPos = sollPos - (tmp << 3) Select sollPos Case < MotPos High Eye_Left GoSub MoveLeft Case > MotPos High Eye_Right GoSub MoveRight Case = MotPos High Motor_Enable 'power down High Eye_Right High Eye_Left End Select EndIf If LeftPirCnt = 255 Then Low Eye_Left If RightPirCnt = 255 Then Low Eye_Right Return PirMove3: 'If VelFlags0.0 = 0 and Velflags0.1 = 0 Then If VelFlags0.0 | VelFlags0.1 = 0 Then If RightPirCnt > LeftPirCnt Then sollPos = RightPirCnt - LeftPirCnt sollPos = CenterPos - (sollPos << 3) 'was + Else sollPos = LeftPirCnt - RightPirCnt sollPos = CenterPos + (sollPos << 3) 'was - End If Select sollPos Case < MotPos High Eye_Left GoSub MoveLeft Case > MotPos High Eye_Right GoSub MoveRight Case = MotPos High Motor_Enable 'power down High Eye_Right High Eye_Left End Select EndIf If LeftPirCnt = 255 Then Low Eye_Left If RightPirCnt = 255 Then Low Eye_Right Return 'Ana_debug: ''Declare Adin_Res = 10 ' 10-bit result required ''Declare Adin_Tad = FRC ' RC OSC chosen ''Declare Adin_Stime = 50 ' Allow 50us sample time ' Repeat ' PF_Left = ADin 0 'ReadADC(0) ' pbmsb = PF_Left / 128 ' pblsb = PF_Left >> 3 ' hrsout 128, pbmsb, pblsb ' PF_Right = ADIn 1 'ReadADC(1) 'not working!!! ' pbmsb = PF_Right / 128 ' pblsb = PF_Right >> 3 ' HrsOut 144, pbmsb, pblsb ' delayms 10 ' Until 1 <> 1 'Return '[EOF]