' ******************************************************************** ' * 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: * ' * SynMotor.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... Include "18F4620.inc" ' for the Synchrochord motor driver (40MHz) ' 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 $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 the low and high side bridge drivers: $define IR1 PORTC.1 ' pin 16 enables for IR2104, should be PWM ports $define IR2 PORTC.2 ' pin 17 these are active when high ' debug tools $define YellowLed PORTC.3 ' pin 18 flashes during the phase delay time $define GreenLed PORTC.0 ' pin 15 used for debugging and operational control ' 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 'not yet used: ' $define x PORTC.5 ' pin 24 ' $define x PORTC.4 ' pin 23 ' $define x PORTB.4 ' pin 37 ' $define x PORTA.4 ' pin 6 ' $define x PORTA.5 ' pin 7 ' $define x PORTA.0 ' pin 2 ' could be used for the sensor ' $define x PORTA.3 ' pin 5 ' $define x PORTA.2 ' pin 4 ' $define x PORTA.1 ' pin 3 '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 = %11000000 'bits set to 0 are output, 1 = input - bits 6 and 7 are the clock! TRISB = %11100000 'bits 6 and 7 are for the ICP, bit 5 is the red LED TRISC = %11000000 'RC6 en RC7 zijn USART I/O and must be set to input TRISD = %00000000 'all bits can be used TRISE = %11101000 'low nibble only, bit3 (RE3) is MCLR 'bit 4 most be zero to disable PSP mode on port D - gwr 20.01.2012 'this was the bug we had! 'constant definitions: Symbol 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 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 ' VIBRATO TIMER - not used: we use Tim3 Dim VelFlags0 As Byte System ' so we can have only 8 tasks ' VelFlags0.0 = phase delay timer ' VelFlags0.1 = vibrato timer using timer 3 Dim Period As Word System Dim PeriodLsb As Period.Byte0 Dim PeriodMsb As Period.Byte1 Dim Delaytime As Word System Dim CC60 As Byte System ' pwm controller channel 1 Dim CC61 As Byte System ' pwm controller channel 2 Dim CC62 As Byte System ' phase controller 60-90-120-180 Dim oldCC62 As Byte System 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 '----------------------------------------------------------------------------------------- ' 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 gate0 Low gate1 Low gate2 ' disable counters HPWM 1, 0, PWMminF ' IR1 disable HPWM 2, 0, PWMminF ' IR2 GoSub Freq_Lookup ' read the period lookup table for the pitches GoSub PowerDown GoSub Cents_Lookup ' read the cent lookup for pitch bend support Set CC60 ' makes it 255 - motor pwm Set CC61 CC62 = 1 ' startup with 90 degree phase shift oldCC62 = CC62 Clear CC66 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: ' not used on this board. ' 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 GoSub Phase_Lookup_90 ' calculate the table with the phase shifts ' we have procs for 60, 90, 120 and 180 degrees GoSub VHz_Lookup ' ' for lab tests, such that without midi-in we get an output from the timers: ' High gate0 ' High gate1 ' High gate2 ' High IR1 ' High IR2 ' HPWM 1, 255, PWMminF ' IR1 enable ' HPWM 2, 255, PWMminF ' IR2 Clear GreenLed ' Set YellowLED ' 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 ' here higher statusses are not implemented. If Bytein > 253 Then '254 = midiclock, 255= reset 'midiclock can interrupt all other msg's... '255 had to be intercepted since thats what we 'get when no new byte flows in. Else Clear statusbyte 'reset the status byte 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 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 EndIf EndIf Else If noteAan >= LowTes Then If noteAan <=HighTes Then Set GreenLed If noteAan <> oldnote Then Idx = noteAan - LowTes ' idx always reflects the motor frequency setting Period = FreqLookup[Idx] ' now we have PeriodLSB and PeriodMSB Clear VelFlags0.0 ' the delay needs to be reprogrammed Clear gate0 Clear gate1 ' counters must be reprogrammed 'Set Write 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 Set A0 ' set adres to counter 1 , A1 is still 0 dta = PeriodLsb Rev 8 Clear Write Set Write dta = PeriodMsb Rev 8 Clear write Set write ' stroboscope counter: Clear A0 ' set adres to counter 2 Set A1 dta = PeriodLsb Rev 8 ' lsb Clear Write Set Write dta = PeriodMsb Rev 8 Clear Write Set Write Set gate2 ' start timer for the time delay Delaytime = PhaseLookup[Idx] ' for 32 us resolution Cnt.Word0 = CntLw ' read counter veltim0 = Cnt + Delaytime ' add the phase duration = Period/4 ' suppose our timer has a resolution of 32 microseconds Set VelFlags0.0 HPWM 2, VHz[Idx],PWMFreq4 HPWM 1, VHz[Idx],PWMFreq4 Set gate0 ' start counter0 , counter1 starts after the delay Set YellowLed oldnote = noteAan Else Set gate2 ' strobo aan EndIf EndIf EndIf Set noteAan '= 255 'reset !!! EndIf EndIf ' Case Keypres_Status 'used for lite flashing on Fa-Hub ' 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 and counter1 on the fly ' pitch bend should be reset on each new note. If gate1 + gate0 = 2 Then ' make sure both counters are running! ' this condition is to guarantee preservation of phase. If pbmsb >= 64 Then Cents = pbmsb - 64 ' 0 to 63 GoSub Pitchbend_Up Else Cents = 64 - pbmsb ' 1 to 64 GoSub Pitchbend_Down EndIf 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 phase control Cnt.Word0 = CntLw ' read counter If Cnt >= veltim0 Then ' counter has been programmed on reception of a note-on ' Set Write ' Set A0 ' set adres to counter 1 ' Clear A1 ' dta = PeriodLsb Rev 8 ' lsb ' Clear Write ' Set Write ' dta = PeriodMsb Rev 8 ' Clear Write ' Set Write Set Gate1 ' enable the timer 1 output Clear YellowLed ' for debug. Clear VelFlags0.0 ' one shot EndIf EndIf ' Velflags0.1: may be used in low priority irq for vibrato support. ' Cnt.Word0 = CntLw ' If Cnt >= veltim2 Then ' Clear VelFlags0.2 ' one shot ' 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: 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 Set A0 ' set adres to counter 1 dta = PeriodLsb Rev 8 ' place lsb on databus Clear Write ' strobe lsb Set Write dta = PeriodMsb Rev 8 ' place msb on databus Clear write ' write it to counter 1 Set write Return Pitchbend_Down: 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 Set A0 ' set adres to counter 1 dta = PeriodLsb Rev 8 ' place lsb on databus Clear Write ' strobe lsb Set Write dta = PeriodMsb Rev 8 ' place msb on databus Clear write ' write it to counter 1 Set write 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 'HPWM 2, CC60, PWMminF EndIf Case 61 ' controller for motor force - for experiments If value = 0 Then CC61 = 0 Else CC61 = value << 1 ' make 8-bit Inc CC61 ' 2-255 'HPWM 1, CC60, PWMminF HPWM 2, CC61, PWMminF EndIf Case 62 ' change phase lookup. - implemented for research only ' by default on startup 90 degrees phase shift, cc62 = 40 CC62 = value >> 5 ' preserve only the two highest bits If CC62 <> oldCC62 Then Select CC62 Case 0 ' 0-31 GoSub Phase_Lookup_60 Case 1 ' 32- 63 GoSub Phase_Lookup_90 Case 2 ' 64- 95 GoSub Phase_Lookup_120 Case 3 ' 96-127 GoSub Phase_Lookup_180 End Select oldCC62 = CC62 EndIf 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 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 Return PowerDown: Clear VelFlags0 'stop the all running timers Clear CntHw ' reset timer HPWM 1, 0, PWMminF ' IR1 HPWM 2, 0, PWMminF ' IR2 Clear gate0 Clear gate1 Clear gate2 Clear oldnote Clear YellowLed Clear GreenLed Return Init_Counters: ' 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 FreqLookup[0] = 64282 ' note 39 - lowtes = 0 FreqLookup[1] = 60674 FreqLookup[2] = 57269 FreqLookup[3] = 54055 FreqLookup[4] = 51021 FreqLookup[5] = 48157 FreqLookup[6] = 45455 FreqLookup[7] = 42903 FreqLookup[8] = 40495 FreqLookup[9] = 38223 FreqLookup[10] = 36077 FreqLookup[11] = 34052 FreqLookup[12] = 32141 ' note 51 FreqLookup[13] = 30337 FreqLookup[14] = 28635 FreqLookup[15] = 27027 FreqLookup[16] = 25510 FreqLookup[17] = 24079 FreqLookup[18] = 22727 FreqLookup[19] = 21452 FreqLookup[20] = 20248 FreqLookup[21] = 19111 FreqLookup[22] = 18039 FreqLookup[23] = 17026 FreqLookup[24] = 16071 ' note 63 FreqLookup[25] = 15169 FreqLookup[26] = 14317 FreqLookup[27] = 13514 FreqLookup[28] = 12755 FreqLookup[29] = 12039 FreqLookup[30] = 11364 FreqLookup[31] = 10726 FreqLookup[32] = 10124 FreqLookup[33] = 9556 FreqLookup[34] = 9019 FreqLookup[35] = 8513 FreqLookup[36] = 8035 ' note 75 FreqLookup[37] = 7584 FreqLookup[38] = 7159 FreqLookup[39] = 6757 FreqLookup[40] = 6378 FreqLookup[41] = 6020 FreqLookup[42] = 5682 FreqLookup[43] = 5363 FreqLookup[44] = 5062 FreqLookup[45] = 4778 FreqLookup[46] = 4510 FreqLookup[47] = 4257 FreqLookup[48] = 4018 ' note 87 Return Phase_Lookup_60: ' unit in the frequency table is 2us ' here the units are in 32 us increments. ' FreqLookup[x] >> 2 sets 90 degrees phase shift (1/4 period time) in 2us units ' to convert to 32 us units, we have to divide another time by 16, eqv. to >>4 For i = 0 To 48 PhaseLookup[i] = FreqLookup[i] >> 6 ' this is somewhat too small ' so we add a fraction: Period = FreqLookup[i] >> 8 PhaseLookup[i] = PhaseLookup[i] + Period ' now it's about right for 90 degrees. PhaseLookup[i] = PhaseLookup[i] + Period / 2 ' should be 120 degrees - to be verified. PhaseLookup[i] = PhaseLookup[i] >> 1 ' divide by 2 to get 60 degrees Next Period = FreqLookup[0] Return Phase_Lookup_90: ' unit in the frequency table is 2us ' here the units are in 32 us increments. ' FreqLookup[x] >> 2 sets 90 degrees phase shift (1/4 period time) in 2us units ' to convert to 32 us units, we have to divide another time by 16, eqv. to >>4 For i = 0 To 48 PhaseLookup[i] = FreqLookup[i] >> 6 ' this is somewhat too small ' so we add a fraction: Period = FreqLookup[i] >> 9 PhaseLookup[i] = PhaseLookup[i] + Period ' now it's about right for 90 degrees. Next Period = FreqLookup[0] Return Phase_Lookup_120: ' unit in the frequency table is 2us ' here the units are in 32 us increments. ' FreqLookup[x] >> 2 sets 90 degrees phase shift (1/4 period time) in 2us units ' to convert to 32 us units, we have to divide another time by 16, eqv. to >>4 For i = 0 To 48 PhaseLookup[i] = FreqLookup[i] >> 6 ' this is somewhat too small ' so we add a fraction: Period = FreqLookup[i] >> 8 PhaseLookup[i] = PhaseLookup[i] + Period ' now it's about right for 90 degrees. PhaseLookup[i] = PhaseLookup[i] + Period / 2 ' should be 120 degrees - to be verified. Next Period = FreqLookup[0] Return Phase_Lookup_180: ' do not understand how this could ever make the motor rotate though... ' unit in the frequency table is 2us ' here the units are in 32 us increments. ' FreqLookup[x] >> 1 sets 180 degrees phase shift (1/2 period time) in 2us units ' to convert to 32 us units, we have to divide another time by 16, eqv. to >>4 For i = 0 To 48 PhaseLookup[i] = FreqLookup[i] >> 5 ' this is somewhat too small ' so we add a fraction: Period = FreqLookup[i] >> 7 ' perfect this way, checked on the Tektronix PhaseLookup[i] = PhaseLookup[i] + Period ' now it's right for 180 degrees. Next Period = FreqLookup[0] ' reset variable 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 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]