' ******************************************************************** ' * PIC firmware for Synchrochord * ' * Synchronous Motor Driver * ' * coded by dr.Godfried-Willem Raes * ' * http://www.logosfoundation.org/instrum_gwr/synchrochord/picworks * ' * source code development directory: * ' * SynMotor3.bas * ' ******************************************************************** ' 01.02.2012: Hardware design finished. Start coding ' On reception of note on, the motor should rotate at the right speed ' On noteoff's the motor does not have to be stopped ' Timer0 in the 8254 chip, programmed to operate in mode 3 ' Timer1 should output the same frequency, but with a variable delay (phase) ' this can be done by steering gate1 after a delay. ' Timer2 is used for the stroboscope and is stopped on note OFF's ' PortD seems to give problems. Something wrong with the initialisation? ' TrisE, bit 4 must be cleared !!! otherwize, port D works in PSP mode!!! ' PSP mode might be usefull here though. ' Breadboard nearly finished and ready for firmware. ' Phase delay = 1/4 th of timer period for 90 degree shift ' Phase delay = 1/3 th of timer period for 120 degree shift ' 02.02.2012: Further writing of firmware. ' First version 1.0 flashed. ' some wiring bugs discovered. ' CC60 for motor force to be implemented. ' 03.02.2012: Strobo light changed in hardware: now using a p-channel mosfet. ' The counter output is high at rest, thus the LED will be off without counter output. ' hardware bug in bit numbering on the databus. Reversal of the bytes solved this. ' Hence the occurences of var Rev 8 in this code. ' Counter0 and 2 work o.k. now, only Counter1 refuses to startup after the delay time. ' It works when we reload the timer in the timeout procedure. ' 04.02.2012: Precize debug using the Tektronix scope. The gate pins had a bug in the declaration here. ' Corrected according to the schematic drawing. Now works perfectly o.k. The phase delay ' as measured on the score is somewhat smaller than 90 degrees. We corrected this in the ' phase lookup calculation ' PWM steering using CC60 tested o.k., as yet without a motor though. ' Note that changing motor speed at a rate faster than ca. 4 notes a second, makes absolutely so sense. ' This entails that an implementation of vibrato would be plain improssible. ' This be firmware version 1.1 ' 05.02.2012: start implementation of pitch-bend, to enable phasing effects. ' This will be a range of -64 cents to + 63 cents. Tested o.k. on the tektronix scope. ' This be firmware version 1.2 ' 08.02.2012: First test with motor... not very succesfull sofar... ' 09.02.2012: implement different phase shifts. ' 11.02.2012: still we cannot get the motor to turn. Phase must be quadrature it seems. (90 degrees) ' but one of the windings may have to be steered unipolar in an ac-servo motor... ' 820nF caps placed in the bootstrap drivers. Bypass caps and protection diodes added. (MUR856) ' Back to 90 degrees, quadrature phase shift. Still not working. ' 13.02.2012: Hardware test and repair: upper IR2104 fused. Diode gone. Timer (section for counter1) burned out... ' Seems that out frequencies are too low for the driver to work. It's better with 10k between the ' 30V power and pin 8 of the IR2104. ' Firmware modified to make debugging possible without midi setup. ' 15.02.2012: back to 90 degrees phase-shift. ' we will have to implement a f/sqr(Hz) scaling for motor voltage. Without this the current is way ' too high on low frequencies. Placing an AC cap. in series with the motor windings seems like an easy ' solution, however it would require two caps 61mF in value, bipolar. This is impossible. ' VHz lookup implemented as an alternative for the PWM on the driver enable input. ' PWM frequency increased to 15624Hz. ' SMOKE STACK... nothing wrong with this firmware however. We save it as SYNMotor.bas ' 17.02.2012: Completely different hardware approach. Using a Nanotech driver. ' Here the firmware has to generate only one synchronous clock. The strobo can have the same code. ' This source code renamed to SYNMotor2.bas ' 18.02.2012: Rewrite continued. ' 25.02.2012: Project taken up again. The motor now works. ' Counter clock frequency must be x 8 now, so the 4MHz clock without dividers. Hardware modified. ' CC63 added for direction of rotation ' CC65 added for timeout after note-off ' 26.02.2012: Timeout on noteoff added. (CC65). YellowLED now gives an indication for this. ' Seems we need ramp-up and down timers to overcome motor protection errors. ' ca.0.2 sec. per semitone step. ' speedup is way less critical than slowdown. ' From note 83 on we have slip on the motor. This can be compensated with the pitch bend. ' Counter2 in now clocked at 500kHz, counters 0 and 1 at 4MHz. Counter 1 not used. ' 27.02.2012: Better than disabling the motor on slowdown, is to stop the clock as this brakes the motor. ' 04.03.2012: Motor controller changed: we now try to use a IB106 type rated for 6 Amps. ' This controller can only do full step or half step modes. First tests o.k. ' Counter2 changed to 1MHz clock. (Strobo) ' AN0 input enabled for frequency measurement. ' 06.03.2012: We could use the PWM outputs to steer motor current if we use an optor for the current sense resistor. ' Blue LED added to indicate ramping down. This uses port RA1 ' Sensor connected to RA0=AN1, but not implemented yet in this firmware. ' 07.03.2012: Further refinements and tests. ' Counter 2 now clocked with 2MHz ' Motor works in halfstepmode. (8 clocks per revolution) ' Start implementation of rampup. Now this source code is called SYNMotor3.bas ' So now we brake by reversing the direction of rotation during a fixed rampdowntime depending on the traject ' and we speedup by running through all chromatic positions. ' For notes < 75 no rampup is used nor for chromatic steps climbing. ' Log rampup-curve implemented with a lookup-table. ' 08.03.2012: Braking and ramping down coding improved. ' RampUp indication with orange LED added. ' 08.03.2012: CC61 introduced for motor current control via optor circuit. Note that it works inverted! ' 14.03.2012: SYNMotor3.bas firmware tested. ' 28.07.2012: project taken up again... ' 04.08.2012: Optor circuitry not yet mounted. Motor voltage increased to 48V for better torque. ' 15.09.2012: this version placed in Synchrochord. Include "18F4620.inc" ' for the Synchrochord motor driver (40MHz) Include "ADC.inc" ' not used yet. ' Mapping defines for midi-events on pin outputs and inputs for programming the timer chip ' control lines for the timer chip $define Dta PORTD 'PORTD.0 - pin 19 Databus 'PORTD.1 ' pin 20 'PORTD.2 ' pin 21 'PORTD.3 ' pin 22 'PORTD.4 ' pin 27 'PORTD.5 ' pin 28 'PORTD.6 ' pin 29 'PORTD.7 ' pin 30 $define gate0 PORTE.2 ' pin 10 ' must be high to be enabled $define gate1 PORTE.1 ' pin 9 ' count starts on gate high $define gate2 PORTE.0 ' pin 8 $define A0 PORTB.0 ' pin 33 adres lines for counters $define A1 PORTB.1 ' pin 34 $define write PORTB.2 ' pin 35 $define readBack PORTB.3 ' pin 36 ' keep this one high ' control lines for motor current control ' $define IR1 PORTC.1 ' pin 16 $define IR2 PORTC.2 ' pin 17 when high, the current is at minimum, when low we have maximum current. ' debug tools $define YellowLed PORTC.3 ' pin 18 flashes on note-off $define GreenLed PORTC.0 ' pin 15 flashing on note-on ' this led is ON when a note withing the range is playing. ' OFF when a note within range is switched off. $define Debug_Led PORTB.5 ' pin 38 for testing - red led - watchdog $define Enbl PORTC.5 ' pin 24 -high=enabled, low=disabled for motor controller - for SMC ' for the IB 106 it's inversed!!! : low= enabled, high= disabled $define dir PORTC.4 ' pin 23 = CW/CWW , cc63 $define tacho PORTA.0 ' pin 2 - analog in $define RampLED PORTA.1 ' pin 3 - blue LED to indicate ramping down. (braking) $define SpeedUpLED PORTA.2 ' pin 4 - orange LED to indicate speedup in progress (accellerating) 'not yet used: ' $define x PORTB.4 ' pin 37 ' $define x PORTA.4 ' pin 6 ' $define x PORTA.5 ' pin 7 ' $define x PORTA.3 ' pin 5 'Symbol PWMminF = 3906 Symbol PWMFreq2 = PWMminF * 2 ' PWMminF is processor dependent. Symbol PWMFreq3 = PWMminF * 3 ' declared in the processor include Symbol PWMFreq4 = PWMminF * 4 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 = %11000001 '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 LowTes = 39 Symbol HighTes = 87 'initialisations for the midi input parser: Symbol Midichannel = 10 ' SynchroChord_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 ' 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 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 Dim j As Word System ' 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 oldnote As Byte System Dim nownoteUp As Byte System ' for ramping up Dim endnoteUp As Byte System ' for ramping up Dim NowNoteDown As Byte System Dim EndNoteDown As Byte System 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 Cents As Byte System Dim veltim0 As Dword System ' 32 bit, faster, replaces the arrays Dim veltim1 As Dword System ' rampdowntimer Dim veltim2 As Dword System ' rampuptimer Dim VelFlags0 As Byte System ' so we can have only 8 tasks ' VelFlags0.0 = timeout timer. ' VelFlags0.1 = rampdown timer ' VelFlags0.2 = rampup timer Dim Period As Word System Dim PeriodLsb As Period.Byte0 Dim PeriodMsb As Period.Byte1 ' Dim CC60 As Byte System ' pwm controller channel 1 - not used here Dim CC61 As Byte System ' pwm controller channel 2 - motor current control , 8 bits ' Dim CC62 As Byte System ' phase controller 60-90-120-180 ' Dim oldCC62 As Byte System Dim CC63 As Byte System ' cw/ccw controller Dim CC66 As Byte System ' global on/off switch Dim PowerOn As CC66.0 ' Dim st As Byte System ' Dim b1 As Byte System ' Dim b2 As Byte System Dim Idx As Byte System ' lookup table 0-48, 4 octaves ' index = midinote - lowtes Dim FreqLookup[49] As Word ' lookuptable for note to period in 2 us units ' Dim PhaseLookup[49] As Word ' pitch dependent phase shift time lookup in 32us units Dim CentsLookup[49] As Word ' lookup for cent correction in pitch bends ' Dim VHz[49] As Byte ' 8 bit V/Hz lookup for PWM Dim Timeout As Dword System ' must be dword as we need longer than a second. Dim RampDownTime As Word System ' could become a constant Dim RUpTime As Word System Dim Olddir As Byte System ' only bit 0 is used Dim RampUpTime[49] As Byte Dim StroboNote As Byte System '----------------------------------------------------------------------------------------- ' Load the USART Interrupt handler and buffer read subroutines into memory Include "SynMotor_Midi_Irq.inc" ' for UART,Timer0, Timer3 Interrupt 'Clear ' clear all RAM '----------------------------------------------------------------------------------------- ' Main program starts here MAIN: High Debug_Led DelayMS 10 ' wait for stability Low Debug_Led High Write High ReadBack ' will always stay high Low RampLED Low SpeedUpLed Clear VelFlags0 Low gate0 ' disable Low gate1 Low gate2 ' disable counters HPWM 1, 255, PWMminF ' IR1 HPWM 2, 255, PWMminF ' IR2 set to minimum motor current CC61 = 255 GoSub Freq_Lookup ' read the period lookup table for the pitches GoSub PowerDown GoSub Cents_Lookup ' read the cent lookup for pitch bend support GoSub RampUp_Lookup High Enbl ' disable motor ctrl. - ib106 Low Dir Clear Olddir.0 Timeout = 260096 ' dword, since > 65024 - can be changed with a controller RampDownTime = 1024 High GreenLed 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 ' Open the ADC: ' Fosc/32 ' Right justified for 10-bit operation ' Tad value of 0 ' Vref+ at Vcc : Vref- at Gnd ' Make AN0 an analogue input OpenADC(ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_0_TAD, ADC_REF_VDD_VSS, ADC_1ANA) GoSub Init_Counters Clear GreenLed ' start the main 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 > Pitchbend_Status Then ' 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 End If 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 ' notePres = 255 ' pres = 255 Case Control_Status ' controllers and switches statusbyte = Bytein Set Ctrl Set value ' Case ProgChange_Status ' could be used for different lookup tables ' statusbyte = Bytein ' prog = 255 ' Case Aftertouch_Status ' for fingered vibrato, freq.changes during notes ' statusbyte = Bytein ' Set aft Case Pitchbend_Status statusbyte = Bytein Set pblsb Set pbmsb 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... If noteUit >= LowTes Then If noteUit <= HighTes Then Clear gate2 ' switch off stroboscope Clear GreenLed ' do not stop the motor , but program the time-out timer: If Timeout > 0 Then Cnt.Word0 = CntLw ' read counter veltim0 = Cnt + Timeout ' add the timeout value Set VelFlags0.0 EndIf If VelFlags0.1 = 1 Then Dir = Olddir.0 ' reset direction of rotation Clear VelFlags0.1 ' stop rampdown timer if active Clear RampLed EndIf Clear VelFlags0.2 ' stop reampup timer if active Clear SpeedUpLed Clear Enbl ' otherwize the motor might stop if welflags0.1 was set. IB106 Set YellowLed ' indicate note-off EndIf EndIf Set noteUit '= 255 'reset EndIf GoTo Check_Timers Case NoteOn_Status If noteAan = 255 Then noteAan = Bytein Else velo = Bytein If velo = 0 Then If noteAan >= LowTes Then If noteAan <= HighTes Then Clear gate2 ' equivalent to noteoff Clear GreenLed ' do not stop the motor , but program the time-out timer: If Timeout > 0 Then Cnt.Word0 = CntLw ' read counter veltim0 = Cnt + Timeout ' add the timeout value Set VelFlags0.0 EndIf If VelFlags0.1 = 1 Then Dir = Olddir.0 Clear RampLed Clear VelFlags0.1 Clear VelFlags0.2 Clear SpeedUpLed Clear Enbl 'ib106 ON Set YellowLed EndIf EndIf Else If noteAan >= LowTes Then If noteAan <=HighTes Then Set GreenLed Select Case noteAan Case oldnote + 1 ' we never ramp on semitones Idx = noteAan - LowTes ' idx always reflects the motor frequency setting Period = FreqLookup[Idx] ' now we have PeriodLSB and PeriodMSB ' counters must be reprogrammed Clear A0 ' set adres to counter 0 Clear A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write ' counter will start on the next clock, so 2us delay possible ' stroboscope counter: Set A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write Set gate0 ' start counter0 Clear Enbl ' start motor - ib106 oldnote = noteAan If VelFlags0.1 = 1 Then Dir = Olddir.0 ' in this case we have to restore the direction of rotation Clear VelFlags0.1 ' stop possible slowdown timer: problem if braking is active!!! Clear VelFlags0.0 Set gate2 ' start stroboscope StroboNote = noteAan Clear YellowLed ' indicate note playing Case > oldnote If noteAan < 75 Then ' try without ramping on speeddup Idx = noteAan - LowTes ' idx always reflects the motor frequency setting Period = FreqLookup[Idx] ' now we have PeriodLSB and PeriodMSB ' Clear gate0 ' Clear gate1 ' counters must be reprogrammed Clear A0 ' set adres to counter 0 Clear A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write ' counter will start on the next clock, so 2us delay possible ' stroboscope counter: Set A1 ' set adres to counter 2 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write Set gate0 ' start counter0 Clear Enbl ' start motor - ib106 oldnote = noteAan If VelFlags0.1 = 1 Then ' in this case we have to restore the direction of rotation Dir = Olddir.0 Clear RampLED Clear VelFlags0.1 ' stop possible slowdown timer EndIf Clear VelFlags0.0 Set gate2 ' start stroboscope StroboNote = noteAan Clear YellowLed ' indicate note playing Else ' rampupcoding for notes 75 to 87: If oldnote >= LowTes Then RUpTime.Byte1 = RampUpTime[oldnote - LowTes] ' byte array Else RUpTime.Byte1 = RampUpTime[0] EndIf endnoteUp = noteAan ' set destination nownoteUp = oldnote Clear gate2 ' stop stroboscope, will start again at end of ramp Clear YellowLed ' note-off has ended Set gate0 ' counter 0 running If VelFlags0.1 = 1 Then Dir = Olddir.0 Clear VelFlags0.1 Clear VelFlags0.0 Clear Enbl ' motor ON Cnt.Word0 = CntLw ' read counter veltim2 = Cnt + RUpTime ' add the rampuptime Set VelFlags0.2 Set SpeedUpLed EndIf ' Case < oldnote ' ' in this case we implement some form of braking. ' ' we leave the motor powered, but toggle the direction of rotation ' 'set Enbl ' stop motor to let it slowdown ' 'Clear gate0 ' stop clock to brake motor ' Idx = noteAan - LowTes ' idx always reflects the motor frequency setting ' Period = FreqLookup[Idx] ' now we have PeriodLSB and PeriodMSB ' ' counters must be reprogrammed ' Clear A0 ' set adres to counter 0 ' Clear A1 ' dta = PeriodLsb Rev 8 ' lsb ' Clear Write ' Set Write ' dta = PeriodMsb Rev 8 ' Clear Write ' Set Write ' counter will start on the next clock, so 0.25us delay possible ' ' stroboscope counter: ' ' set adres to counter 2 ' Set A1 ' dta = PeriodLsb Rev 8 ' lsb ' Clear Write ' Set Write ' dta = PeriodMsb Rev 8 ' msb ' Clear Write ' Set Write ' Set gate2 ' Clear YellowLed ' Clear VelFlags0.0 ' ' program timer to restart the motor/clock after the rampdown required time ' RampDownTime.Byte1 = oldnote - noteAan ' proportional to the interval ' ' to be tested with the hardware, <<8 was too short ' veltim1 = RampDownTime << 2 ' x 4 ' Cnt.Word0 = CntLw ' read counter ' veltim1 = Cnt + veltim1 ' add the slowdown time ' Set VelFlags0.1 ' Set RampLED ' blue LED to indicate ramping ' btg Dir ' invert motor direction of rotation for braking ' ' now olddir.0 will be the inversion of Dir ' we have to slowdown the motor ' oldnote = noteAan ' maybe better do this on timer runout ' 'endNote = noteAan Case < oldnote btg Dir ' brake by changing direction of rotation Clear gate2 ' stop stroboscope Clear VelFlags0.0 Clear YellowLed Clear VelFlags0.2 Clear SpeedUpLed EndNoteDown = noteAan NowNoteDown = oldnote - 1 ' program counter for a semitone down: Idx = NowNoteDown - LowTes ' idx always reflects the motor frequency setting Period = FreqLookup[Idx] ' now we have PeriodLSB and PeriodMSB Clear A0 Clear A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write ' counter will start on the next clock, so 0.25us delay possible 'oldnote = NowNoteDown - done after timeout ' program periodic task to step down: Cnt.Word0 = CntLw veltim1 = Cnt + RampDownTime Set VelFlags0.1 Set RampLED Case = oldnote Clear Enbl ' motor on ib106 Set gate0 If noteAan <> StroboNote Then ' reprogram stroboscope-counter Idx = noteAan - LowTes Period = FreqLookup[Idx] Set A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write ' counter will start on the next clock, so 0.25us delay possible StroboNote = noteAan EndIf Set gate2 ' (re)start flashing strobo 'Clear VelFlags0.0 ' clear time-out timer: If VelFlags0.1 = 1 Then Dir = Olddir.0 ' in case we were still braking Clear VelFlags0 ' clear all running tasks, as oldnote is updated all the time. Clear YellowLed EndSelect EndIf EndIf Set noteAan '= 255 'reset !!! EndIf EndIf ' Case Keypres_Status 'used for lite flashing ' If notePres = 255 Then ' notePres = Bytein ' Else ' pres = Bytein ' 'GoSub KeyPres ' EndIf Case Control_Status 'this is where the action takes place for controllers If Ctrl = 255 Then Ctrl = Bytein Else value = Bytein GoSub Controller EndIf ' Case ProgChange_Status ' If prog = 255 Then 'single byte message ' prog = Bytein ' GoSub ProgChange ' EndIf ' Case Aftertouch_Status ' for changing vibrato frequency during notes ' If aft = 255 Then ' aft = Bytein ' GoSub Aftertouch ' EndIf Case Pitchbend_Status ' note that pitchbend is send as status, lsb,msb in this order! If pblsb = 255 Then pblsb = Bytein ' we do not use the lsb Else pbmsb = Bytein ' now do the action... reprogram counter0 on the fly ' pitch bend should be reset on each new note. If pbmsb >= 64 Then Cents = pbmsb - 64 ' 0 to 63 GoSub Pitchbend_Up Else Cents = 64 - pbmsb ' 1 to 64 GoSub Pitchbend_Down EndIf Set pblsb EndIf End Select EndIf Check_Timers: ' here we check the Task counters and compare them with the 32 bit cnt value ' using the Velflags dword variable: If VelFlags0.0 = 1 Then ' on this board we use this timer for timeout Cnt.Word0 = CntLw ' read counter If Cnt >= veltim0 Then ' counter has been programmed on reception of a note-off Clear YellowLed ' for debug. Set Enbl ' stop motor IB106 Clear VelFlags0.0 ' one shot Clear oldnote EndIf EndIf ' two timers will be required for ramp-up and down. ' If VelFlags0.1 = 1 Then ' rampdown timer, this is a one-shot ' Cnt.Word0 = CntLw ' If Cnt >= veltim1 Then ' 'Set gate0 ' restart clock (we didn't stop it...) ' btg Dir ' now olddir.0 should be identical again to Dir ' Clear Enbl ' turn motor back on (not switched off...) IB106 ' Clear VelFlags0.1 ' one shot ' Clear RampLED ' clear Blue LED ' 'oldnote = endnote ' in theory, destination reached. May be wrong. ' EndIf ' EndIf ' rampdown-timer If VelFlags0.1 = 1 Then If NowNoteDown = EndNoteDown Then ' destination reached oldnote = EndNoteDown Clear VelFlags0.1 ' stop task btg Dir ' restore direction of rotation ' restart stroboscope: Idx = EndNoteDown - LowTes Period = FreqLookup[Idx] Set A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write Set gate2 ' start flashing StroboNote = EndNoteDown Clear RampLED ' indicate end of braking Else ' slowing down Cnt.Word0 = CntLw If Cnt >= veltim1 Then Dec NowNoteDown ' decr NowNoteDown Idx = NowNoteDown - LowTes Period = FreqLookup[Idx] Clear A0 Clear A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write ' counter will start on the next clock, so 0.25us delay possible oldnote = NowNoteDown ' always remember where we are veltim1 = Cnt + RampDownTime ' reprogram the timer for the next step EndIf EndIf End If ' rampup-timer: -only required in the highest octave , from note 75 on. If VelFlags0.2 = 1 Then If nownoteUp = endnoteUp Then ' destination reached oldnote = endnoteUp Clear VelFlags0.2 ' restart stroboscope: Idx = endnoteUp - LowTes Period = FreqLookup[Idx] Set A1 ' set adres to counter 2 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write Set gate2 StroboNote = endnoteUp Clear SpeedUpLED Else Cnt.Word0 = CntLw If Cnt >= veltim2 Then ' this is a periodic timer Inc nownoteUp Idx = nownoteUp - LowTes ' set index to next chromatic step Period = FreqLookup[Idx] ' now we have PeriodLSB and PeriodMSB ' counter 0 must be reprogrammed Clear A0 ' set adres to counter 0 Clear A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write ' counter will start on the next clock, so 0.25us delay possible oldnote = nownoteUp ' always remember where we are RUpTime.Byte1 = RampUpTime[Idx] veltim2 = Cnt + RUpTime ' reprogram the timer for the next step EndIf EndIf EndIf GoTo LOOP ' end of the main loop 'KeyPres: ' ' not implemented on this board for ' notePres = 255 'Return 'ProgChange: ' prog = 255 'this is not realy required 'Return Pitchbend_Up: ' this should be disabled during ramps!!! If VelFlags0.1 + VelFlags0.2 = 0 Then j = CentsLookup[Idx] * Cents ' cents is a byte 0-64 Period = FreqLookup[Idx] - j ' shorter period is higher pitch dta = PeriodLsb Rev 8 ' place lsb on databus Clear A0 ' set adres to counter 0 Clear A1 Clear Write ' strobe lsb Set Write dta = PeriodMsb Rev 8 ' place msb on databus Clear Write ' strobe msb Set write Clear VelFlags0.0 Clear YellowLed EndIf Return Pitchbend_Down: If VelFlags0.1 + VelFlags0.2 = 0 Then ' disable pitchbend during ramping j = CentsLookup[Idx] * Cents Period = FreqLookup[Idx] + j ' longer period is lower pitch dta = PeriodLsb Rev 8 ' place lsb on databus Clear A0 ' set adres to counter 0 Clear A1 Clear Write ' strobe lsb Set Write dta = PeriodMsb Rev 8 ' place msb on databus Clear Write ' strobe msb Set write Clear VelFlags0.0 Clear YellowLed EndIf Return 'Aftertouch: ' 'this is the channel aftertouch, affecting any playing note ' 'used for fingered vibrato ' 'the value of aft is used to set or modify the vibrato speed. Minimum freq=19Hz/2 = 9.54Hz ' coding using timer3: ' If aft > 0 Then ' Clear T3CONBITS_TMR3ON ' stop timer ' CC31 = aft ' VibratoPeriod = CC31 << 9 '* CC31 ' Tim3 = VibratoPeriod ' Set T3CONBITS_TMR3ON ' restart timer ' Else ' Clear CC31 ' Clear VibratoPeriod ' Clear T3CONBITS_TMR3ON ' stop timer ' EndIf ' Set aft ' reset 'Return Controller: Select Ctrl ' Case 60 ' ' controller for motor force - for experiments ' If value = 0 Then ' CC60 = 0 ' Else ' CC60 = value << 1 ' make 8-bit ' Inc CC60 ' 2-255 ' HPWM 1, CC60, PWMminF ' EndIf Case 61 ' controller for motor current - steers an optor circuit If value = 0 Then CC61 = 0 HPWM 2, 0, PWMminF Else CC61 = value << 1 ' make 8-bit Inc CC61 ' 2-255 HPWM 2, CC61, PWMminF EndIf Case 63 ' direction of rotation for motor. CC63 = value If CC63.6 = 1 Then Set Dir ' >= 64 Set Olddir.0 Else Clear Dir ' < 64 Clear Olddir.0 EndIf Case 65 ' timeout controller - now dword Timeout = value << 11 ' should be around 4 seconds Case 66 'on/off for the robot If value = 0 Then Clear PowerOn 'CC66.0 =0 GoSub PowerDown Else Set PowerOn 'CC66.0 =1 GoSub PowerUp EndIf Case 123 GoSub PowerDown End Select Set Ctrl 'mandatory reset Return PowerUp: ' send the control for the counters GoSub Init_Counters If VelFlags0.1 = 1 Then Dir = Olddir.0 'Clear oldnote ' counters should be programmed now, but since the gates are OFF, there will be no output signal yet ' HPWM 1, VHz[0], PWMminF ' IR1 - enable ' HPWM 2, VHz[0], PWMminF ' IR2 ' start motor: Set gate0 ' start timer Clear Enbl ' motor should rotate IB106 oldnote = 39 Clear VelFlags0 ' stop all running timers Clear YellowLed Return PowerDown: If VelFlags0.1 = 1 Then Dir = Olddir.0 Clear VelFlags0 'stop the all running timers Clear CntHw ' reset timer ' HPWM 1, 0, PWMminF ' IR1 ' HPWM 2, 0, PWMminF ' IR2 Clear gate0 Clear gate2 Clear oldnote Clear nownoteUp Clear endnoteUp Clear YellowLed Clear GreenLed Set Enbl ' stopmotor IB106 Return Init_Counters: ' disable motor: Set Enbl ' IB106 ' Initialize/ reset the Intel timers with the lowest frequency: [ also done in the call to powerdown...] Period = FreqLookup[0] Set Write ' make high ' control word = 00110110 ' counter0, 16bit, mode 3 Set A0 ' set adres to control word register Set A1 dta = %01101100 ' %00110110 rev 8 ' control word Clear Write ' strobe WR Set Write Clear A0 ' set adres to counter 0 Clear A1 dta = PeriodLsb Rev 8 ' lsb on databus Clear Write ' strobe WR Set Write dta = PeriodMsb Rev 8 ' msb on bus Clear Write ' strobe Set Write ' control word = 01110110 ' counter1 Set A0 Set A1 dta = %01101110 ' %01110110 rev 8 = control word Clear Write Set Write 'Set A0 ' set adres to counter 1 to write the data Clear A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write ' control word = 10110110 ' counter2 'Set A0 Set A1 dta = %01101101 '%10110110 rev 8 Clear Write Set Write Clear A0 ' set adres to counter2 to write the data 'Set A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write ' counters should be programmed now, but since the gates are OFF, there will be no output signal yet Return Freq_Lookup: ' should contain the 16-bit periods to be programmed in the timer chip for all midi notes 'Idx[0] ' index = midinoot - lowtes ' with a 500kHz clock, one unit is 2 microseconds ' values = 5000000/ notefrequency ' we need lsb msb, but the syntax does not allow the use of FreqLookup[0].byte0 and FreqLoopup[0].byte1 ' TABLE CHECKED 02.02.2012 ' Note: if we need higher frequencies in the steppermotor controller implementation, its better to double ' the counter clock freq. than to halve the values in this table. This in the interest of precision. ' Now we work with a 4MHz clock, so the units become 0.25 microseconds. ' note 39 - lowtes = 0 - this makes f_out=62Hz, or with 8 steps/rev, a speed of 7.75Hz ' since one revolution plucks 10 times, we get a frequency of 77Hz = Midi 39. ' formula: countval = (nrofplectra * clockfrequency) / (nrofclocksperrevolution * notefrequency) FreqLookup[0] = 64282 ' midi note 39 frequency = 77.78175 FreqLookup[1] = 60675 ' midi note 40 frequency = 82.40689 FreqLookup[2] = 57269 ' midi note 41 frequency = 87.30706 FreqLookup[3] = 54055 ' midi note 42 frequency = 92.49861 FreqLookup[4] = 51021 ' midi note 43 frequency = 97.99886 FreqLookup[5] = 48157 ' midi note 44 frequency = 103.8262 FreqLookup[6] = 45455 ' midi note 45 frequency = 110 FreqLookup[7] = 42903 ' midi note 46 frequency = 116.5409 FreqLookup[8] = 40495 ' midi note 47 frequency = 123.4708 FreqLookup[9] = 38223 ' midi note 48 frequency = 130.8128 FreqLookup[10] = 36077 ' midi note 49 frequency = 138.5913 FreqLookup[11] = 34052 ' midi note 50 frequency = 146.8324 FreqLookup[12] = 32141 ' midi note 51 frequency = 155.5635 FreqLookup[13] = 30337 ' midi note 52 frequency = 164.8138 FreqLookup[14] = 28635 ' midi note 53 frequency = 174.6141 FreqLookup[15] = 27027 ' midi note 54 frequency = 184.9972 FreqLookup[16] = 25510 ' midi note 55 frequency = 195.9977 FreqLookup[17] = 24079 ' midi note 56 frequency = 207.6524 FreqLookup[18] = 22727 ' midi note 57 frequency = 220 FreqLookup[19] = 21452 ' midi note 58 frequency = 233.0819 FreqLookup[20] = 20248 ' midi note 59 frequency = 246.9417 FreqLookup[21] = 19111 ' midi note 60 frequency = 261.6256 FreqLookup[22] = 18039 ' midi note 61 frequency = 277.1826 FreqLookup[23] = 17026 ' midi note 62 frequency = 293.6648 FreqLookup[24] = 16071 ' midi note 63 frequency = 311.127 FreqLookup[25] = 15169 ' midi note 64 frequency = 329.6276 FreqLookup[26] = 14317 ' midi note 65 frequency = 349.2282 FreqLookup[27] = 13514 ' midi note 66 frequency = 369.9944 FreqLookup[28] = 12755 ' midi note 67 frequency = 391.9954 FreqLookup[29] = 12039 ' midi note 68 frequency = 415.3047 FreqLookup[30] = 11364 ' midi note 69 frequency = 440 FreqLookup[31] = 10726 ' midi note 70 frequency = 466.1638 FreqLookup[32] = 10124 ' midi note 71 frequency = 493.8833 FreqLookup[33] = 9556 ' midi note 72 frequency = 523.2512 FreqLookup[34] = 9019 ' midi note 73 frequency = 554.3653 FreqLookup[35] = 8513 ' midi note 74 frequency = 587.3296 FreqLookup[36] = 8035 ' midi note 75 frequency = 622.254 FreqLookup[37] = 7584 ' midi note 76 frequency = 659.2551 FreqLookup[38] = 7159 ' midi note 77 frequency = 698.4565 FreqLookup[39] = 6757 ' midi note 78 frequency = 739.9889 FreqLookup[40] = 6378 ' midi note 79 frequency = 783.9909 FreqLookup[41] = 6020 ' midi note 80 frequency = 830.6094 FreqLookup[42] = 5682 ' midi note 81 frequency = 880.0001 FreqLookup[43] = 5363 ' midi note 82 frequency = 932.3276 FreqLookup[44] = 5062 ' midi note 83 frequency = 987.7667 FreqLookup[45] = 4778 ' midi note 84 frequency = 1046.502 FreqLookup[46] = 4510 ' midi note 85 frequency = 1108.73 FreqLookup[47] = 4257 ' midi note 86 frequency = 1174.659 FreqLookup[48] = 4018 ' midi note 87 frequency = 1244.508 Return Cents_Lookup: ' in 2us units For i = 0 To 47 Period = FreqLookup[i] - FreqLookup [i+1] ' size of a semitone CentsLookup[i] = Period >> 7 ' divide by 128 to obtain cents. ' -64 cents to + 63 cents Next CentsLookup[48] = CentsLookup[47] ' CentsLookup[0] = 28 , CentsLookup[47] = Period = FreqLookup[0] Return RampUp_Lookup: RampUpTime[0] = 1 RampUpTime[1] = 1 RampUpTime[2] = 1 RampUpTime[3] = 1 RampUpTime[4] = 1 RampUpTime[5] = 1 RampUpTime[6] = 1 RampUpTime[7] = 1 RampUpTime[8] = 1 RampUpTime[9] = 1 RampUpTime[10] = 1 RampUpTime[11] = 1 RampUpTime[12] = 1 RampUpTime[13] = 1 RampUpTime[14] = 1 RampUpTime[15] = 1 RampUpTime[16] = 1 RampUpTime[17] = 1 RampUpTime[18] = 1 RampUpTime[19] = 1 RampUpTime[20] = 1 RampUpTime[21] = 1 RampUpTime[22] = 1 RampUpTime[23] = 1 ' 62 RampUpTime[24] = 1 ' 63 RampUpTime[25] = 2 ' 64 RampUpTime[26] = 3 ' 65 RampUpTime[27] = 4 ' 66 RampUpTime[28] = 5 ' 67 RampUpTime[29] = 6 ' 68 RampUpTime[30] = 7 ' 69 RampUpTime[31] = 8 ' 70 RampUpTime[32] = 9 ' 71 RampUpTime[33] = 10 ' 72 RampUpTime[34] = 11 ' 73 RampUpTime[35] = 12 ' 74 ' from here on following an E12 series RampUpTime[36] = 14 ' 75 RampUpTime[37] = 16 ' 76 RampUpTime[38] = 20 ' 77 RampUpTime[39] = 24 ' 78 RampUpTime[40] = 30 ' 79 RampUpTime[41] = 36 ' 80 RampUpTime[42] = 44 ' 81 RampUpTime[43] = 54 ' 82 RampUpTime[44] = 66 ' 83 RampUpTime[45] = 78 ' 84 RampUpTime[46] = 94 ' 85 RampUpTime[47] = 112 ' 86 RampUpTime[48] = 136 ' 87 Return 'VHz_Lookup: ' For i = 0 To 40 ' 'VHz[i] = ((Note/ 12) - 3) * 64 ' 8 bit range ' VHz[i] = 8 + (i * 6) ' for note 39 , i=0 and VHz[i] = 8 ' ' for note 79 , i=40 and VHz[i] = 248 ' Next ' For i = 41 To 48 ' VHz[i] = 255 ' Next 'Return '[EOF]