'**************************************************************** '* Name : Melautom_Mot.BAS * '* Author : Godfried-Willem RAES * '* Notice : Copyleft (c) 2022 Logosoft Public Domain * '* Date : 31-01-2017 * '* Updated : 25.11.2022 '* Version : 2.1 * '* Notes : Based On Whisper code model * '* Re-uses coding for Propeller 1 and its board 2015 * '* Recoded for Melauton 2017, rev. 25.11.2022 * '**************************************************************** 'Original coding for Propeller1 : '06.01.2015: PIC: 18F2525 Propeller1 board. 2 PWM motor, lites ' pin 2 : Analog input RA0 ' pin 12: Propeller motor RC1 ' pin 13: brake pwm RC2 ' pin 18: midi in RX RC7 ' pin 26: Debug led RB5 'New coding for Melauton: '25.01.2017: Board re-used for Melauton, motor control. ' Wind pressure had to be a function of CC7 as well as of ' the number of notes playing and their wind consumption ' Sampling of the pressure sensor in the low IRQ ' ADC and PWM should work in 10-bit mode. ' ADC in 10bit mode: cfr. coding for hub ' PWM in 10bit mode: '31.01.2017: First testing on the actual robot. ' Motor control should either be placed in the low irq, or ' we could set a flag in the low irq, handled in the main loop. ' Controller 68 added. '01.02.2017: Macro's added for 10-bit PWM operation '02.02.2017: First testing on Melauton. Version 1.0 ' more modes added to ctrl 68 '04.02.2017: two more modes added to ctrl 68 ' softer lookup added '07.02.2017: Notes[] array added, so we know which notes are playing. ' modes 6 and 7 added to ctrl 68 '23.11.2022: last version was march 2017. Now rechecking... ' The 2017 version does not compile anymore... ' 18F2525.inc file updated. ' 10-bit HPWM include updated. ' irq file updated: now uses the version written for '24.11.2022: CC69 added for minimum speed. ' time-out code added to switch motor off when no notes are playing ' There still is a lot of room for improvements in this code! '25.11.2022: Motor control code further improved: best algo should be with ctrl 68 @ 9 ' CC70 introduced. ' now using floats. ' PWM base frequency now: 39kHz (measured) ' loopcount: 42kHz, bij CC68 @ 9, can jump to 88kHz '26.11.2022: Gosub's replaced with Sub's and Proc's Include "18F2525.inc" 'version for the motor board. (40MHz) Include "HPWM10.inc" 'macro's for 10-bit HPWM ' Mapping defines for midi-events on pin outputs and inputs: $define Motor PORTC.1 ' - pwm2 - motor - Radial compressor $define BrakePWM PORTC.2 ' - pwm1 - motor - BrakePWM ' not connected here. $define Debug_Led PORTB.5 ' red led - watchdog $define OnOff_Led PORTC.0 ' led not mounted, but used on debug board! $define Loopspeed PORTB.4 ' nc, for measurement only ' configure the input and output pins: Clear SSPCON1.5 'RC3 must be available for I/O TRISA = %01000111 '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 'constant definitions: 'initialisations for the midi input parser: Symbol Midichannel = 1 ' Melauton_Channel Symbol NoteOff_Status = 128 + Midichannel ' 2 bytes follow Symbol NoteOn_Status = 144 + Midichannel Symbol Keypres_Status = 160 + Midichannel ' 2 bytes follow 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 'application specific constants Symbol NrTasks = 2 ' maximum 16 Symbol Lasttask = NrTasks - 1 Symbol fPWM = 21000 ' in avoidance of audible artifacts ' not sure this works in 10-bit mode... Symbol CC68_default = 1 ' pure manual, no intelligence Symbol CC69_default = 80 ' minimum motorspeed Symbol CC70_default = 64 Symbol CC71_default = 4167 * 10 ' 1 s Symbol CC72_default = 4167 * 50 ' 5 s Symbol LowTes = 60 Symbol HighTes = 84 Symbol Maxspeed = 1023 ' 10-bit range ' 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 All_Digital = True ' not the case here!!! ' Declare Hserial_Clear = On ' should clear on errors. Bytes get lost of course... OpenAnalog2 ' replacing HPWM2 OpenAnalog1 ' replacing HPWM1 ' Create variables Dim Cnt As Dword System Dim CntHw As Cnt.Word1 ' used in the timer0 interrupt, to create a 32 bit timer Dim CntLw As TMR0L.Word ' this is the trick to read both TMR0L and TMR0H ' it makes Cntlw the low word of cnt ' We still have to copy the contents of Lw to Cnt Dim time As Cnt ' was : Dword 'frozen copy of Cnt Dim MaxTim As time.30 ' overflow flag used to reset timer Dim Tim3 As TMR3L.Word ' 16 bit counter for sampler Dim Bytein As Byte System ' midi byte read from buffer Dim StBit As Bytein.7 ' highest bit of ByteIn Dim i As Byte ' general purpose counter Dim b As Byte ' midi variables Dim Ringbuffer[256] As Byte ' Array for holding received midi messages 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 CC7 As Byte Dim Volume As Word Dim Old_Volume As Word Dim CC66 As Byte System ' global on/off switch Dim PowerOn As CC66.0 Dim CC68 As Byte System Dim CC69 As Byte Dim CC70 As Byte Dim CC71 As Dword Dim CC72 As Dword Dim Minspeed As CC69 ' alias Dim st As Byte System Dim b1 As Byte System Dim b2 As Byte System ' Dim pw1 As Word ' motor speed ' Dim pw2 As Word ' braking force - not used here Dim Sigma As Word ' som van het NoWi array Dim Trajekt As Word ' = maxspeed - volume ' Dim Stepsize As Float Dim scalefactor As Float ' normalised value for CC70 Dim f As Float ' for analog in: sensor freescale Dim Newval As Word Dim Sensorval As Word Dim sollspeed As Word Dim ZeroPres As Word ' sensor reading with motor off Dim nrnotes As Byte ' number of notes playing ' variables for the timers: Dim idx As Byte System ' timer indexes Dim Nxt As Dword ' first timer to time-out Dim Timvals[NrTasks] As Dword ' timer values for the tasks and events Dim Resort As Byte Dim Resort_flag As Resort.0 ' flag to signal the requirement to resort timers Dim Off_flag As Resort.1 ' set when motor was switched off Dim Motor_flag As Resort.2 ' set when motor pwm needs update ' dim time0 as dword ' dim time1 as dword '----------------------------------------------------------------------------------------- ' Load the USART Interrupt handler And buffer read subroutines into memory 'Include "ADC.inc" ' Load the ADC macros into the program - used in the IRQ include. Include "Melauton_Mot_Irq.inc" ' our own version for UART And Timer0/3 Interrupt Dim Dur[128] As Word ' lookup table for velo to duration conversion. Dim NoWi[128] As Byte ' lookup table for wind consumption Dim Notes[128] As Byte Dim WindReq As Word ' total required wind factor 'make sure we initialize the pins on start up: Low Debug_Led Low OnOff_Led WriteAnalog2 0 ' connected to RC1 - Motor WriteAnalog1 0 ' connected to RC2 - brake - used for debugging only. Clear CC66 ' power off CC68 = CC68_default CC69 = CC69_default ' = minspeed CC70 = CC70_default CC71 = CC71_default CC72 = CC72_default 'time0 = CC71 * 4167 'time1 = CC72 * 4167 scalefactor = 0.5 Clear PowerOn ' redundant Clear WindReq Clear CC7 Clear nrnotes Clear Notes ' all notes off Set Timvals Set idx Clear Resort_flag ' could be done in 1 step by clearing Resort Clear Off_flag Clear Motor_flag Old_Volume = Volume Dur_Lookup () Note_Wind_Lookup3 () ' for 1:4 scaling Note_Wind_Lookup4 ' for 1:2 we should call Note_Wind_Lookup2 here. ' for 1:3, call Note_Wind_Lookup3 ' for 1:1, call Note_Wind_Lookup1 '----------------------------------------------------------------------------------------- ' Main program starts here MAIN: High Debug_Led DelayMS 50 ' 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 ' 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) in macro file. 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 ' Open the ADC: ' initialize with the value on startup - 10 bit resolution ADCON1 = %00001100 ' 3 channels, 0,1 and 2 Declare Adin_Res 10 'Declare Adin_Tad 64_FOSC Declare Adin_Stime 100 ' data acquisition time in us Set ADCON2.7 ' right justified - this seems not to be the default! ZeroPres = ADIn 0 ' should be around 512 DelayUS 1 'L_sensorVal = ADIn 1 'DelayUS 1 'R_sensorVal = ADIn 2 'DelayUS 1 ' open and start timer3 for sampling: Clear T3CON Clear PIR2BITS_TMR3IF ' clear IRQ flag Set PIE2BITS_TMR3IE ' irq on ' Clear Tim3 ' Clear TMR3L And TMR3H registers Set RCONbits_IPEN ' Enable priority interrupts Clear IPR2bits_TMR3IP ' Set Timer3 as a low priority interrupt source ' we can also set T3Con in one instruction as: T3CON = %10110000 ' 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 set to 0 for Whisper! ' maximum count = 52.42ms, 1 tick =0.8uS, lowest freq.=19Hz HRSOut Control_Status, 66, 0 ' dummy to make it work... ' start the main program loop: Do Cnt.Word0 = CntLw ' read timer 0 ' Start the midi parser. Bytein = GetMidiIn () Clear Motor_flag Midi_Parse: If Bytein > Control_Status Then ' here higher statusses are not implemented. If Bytein > 253 Then '254 = midiclock, 255= reset 'midiclock can interrupt all other msg's... '255 had to be intercepted since thats what we 'get when no new byte flows in (?) GoTo Check_Timers 'throw away... Else Clear statusbyte 'reset the status byte GoTo Check_Timers 'throw away End If EndIf If StBit =1 Then 'should be faster than If Bytein > 127 Then 'status byte received, bit 7 is set Clear statusbyte 'if on another channel, the statusbyte needs a reset Select Bytein 'eqv to Select case ByteIn Case NoteOff_Status statusbyte = Bytein Set noteUit 'reset value. Cannot be 0 !!! Set release '0 is a valid midi note! Case NoteOn_Status statusbyte = Bytein Set noteAan Set velo Case Keypres_Status ' used for lights statusbyte = Bytein Set notePres Set pres Case Control_Status ' only 123,66 and 68 statusbyte = Bytein Set Ctrl Set value ' Case ProgChange_Status ' statusbyte = Bytein ' prog = 255 ' Case Aftertouch_Status ' statusbyte = Bytein ' aft = 255 ' Case Pitchbend_Status ' statusbyte = Bytein ' pblsb = 255 ' pbmsb = 255 End Select Else 'midi byte is 7 bits Select statusbyte Case 0 'not a message for this channel GoTo Check_Timers 'disregard Case NoteOff_Status If noteUit = 255 Then noteUit = Bytein Else release = Bytein 'message complete, so we can do the action... Select noteUit Case LowTes To HighTes Clear Off_flag ' here a fault condition is possible if users send ' a noteoff command for a non-sounding note... hence the ' introduction of flag-array Notes[] If Notes[noteUit] = 1 Then If WindReq >= NoWi[noteUit] Then WindReq = WindReq - NoWi[noteUit] Else Clear WindReq ' no wind required anymore... EndIf If nrnotes > 0 Then Dec nrnotes Notes[noteUit] = 0 'else ' de noot was niet aan... EndIf If nrnotes = 0 Then Timvals[0] = time + CC71 ' 41666 ' 1s waittime Else Set Timvals[0] ' clear timer Set Timvals[1] ' clear all timers EndIf ' bereken de winddruk: f = (WindReq * Trajekt) / Sigma ' sigma is a float! f = f * scalefactor sollspeed = f + Volume ' - must be changed in the loop ' if volume changes... Old_Volume = Volume Set Resort_flag Set Motor_flag EndSelect Set noteUit 'reset EndIf Case NoteOn_Status If noteAan = 255 Then noteAan = Bytein Else velo = Bytein If velo = 0 Then Select noteAan Case LowTes To HighTes Clear Off_flag If Notes[noteAan] = 1 Then If WindReq >= NoWi[noteAan] Then WindReq = WindReq - NoWi[noteAan] Else Clear WindReq EndIf If nrnotes > 0 Then Dec nrnotes Notes[noteAan] = 0 If nrnotes = 0 Then Timvals[0] = time + CC71 ' 41666 ' 1s waittime Else Set Timvals[0] ' clear timer Set Timvals[1] EndIf Set Resort_flag ' bereken de winddruk: f = (WindReq * Trajekt) / Sigma f = f * scalefactor sollspeed = f + Volume Old_Volume = Volume Set Motor_flag 'else ' in dit geval was de noot niet aan... EndIf EndSelect Set noteAan 'reset !!! EndIf If PowerOn > 0 Then Select noteAan Case LowTes To HighTes Clear Off_flag If Notes[noteAan] = 0 Then WindReq = WindReq + NoWi[noteAan] Inc nrnotes Notes[noteAan] = 1 Set Timvals[0] Set Timvals[1] Set Resort_flag ' bereken de winddruk: f = (WindReq * Trajekt) / Sigma f = f * scalefactor sollspeed = f + Volume Old_Volume = Volume Set Motor_flag 'else ' in dit geval was de noot al aan EndIf EndSelect EndIf Set noteAan 'reset EndIf Case Keypres_Status 'used for repeat speed modulation If notePres = 255 Then notePres = Bytein Else pres = Bytein KeyPres () EndIf Case Control_Status 'where the action takes place for controllers If Ctrl = 255 Then Ctrl = Bytein Else value = Bytein Controller () EndIf End Select EndIf If Resort_flag = 1 Then idx = SortTimers () ' so we resort only if an incoming midi command or a motor process changed something EndIf Check_Timers: ' here we check the Task counters and compare them with the 32 bit cnt value ' no timers used in this code... If idx < NrTasks Then ' we moeten alleen checken wanneer er een timer loopt If time >= Nxt Then ' nagaan of de eerstvolgende timer afgelopen is... ' in dit geval is de eerste timer afgelopen en moeten we de juiste aktie ondernemen: Set Nxt.31 ' timer reset, is immers afgelopen ' aan de hand van idx weten we welke timer dit is Select idx Case 0 ' this is a time-out timer Set Timvals[0] ' cancel timer WriteAnalog2 Minspeed sollspeed = Minspeed ' start the motor full-off-timer: Timvals[1] = time + CC72 ' 208333 ' d.i. = 5 s , in 24 us unit. Clear Motor_flag Case 1 ' motor-off-timer afgelopen. WriteAnalog2 0 Clear sollspeed Set Timvals[1] Set Off_flag Clear Motor_flag 'Case Else ' ' in dit geval is idx geset ' GoTo jumpout EndSelect idx = SortTimers () ' find a new nxt and idx EndIf Else ' idx > 1, no timers running, so to avoid overflows, we can reset the timer If MaxTim = 1 Then Clear Cnt Set Timvals EndIf 'EndIf ' Motor control loop: 'if Idx > NrTasks then If Motor_flag = 1 Then Select CC68 Case 0 ' intelligent mode - requires the pressure sensor! ' compare wind pressure read with the sensor with the sollspeed for the motor Set Timvals ' no timers here! If WindReq = 0 Then If PowerOn = 0 Then WriteAnalog2 0 ' motor off when no notes are playing Else WriteAnalog2 Minspeed EndIf Else 'sollspeed = (CC7 * WindReq) >> 7 ' to be tested 'If sollspeed > 1023 Then sollspeed = 1023 sollspeed = Volume ' CC7 << 3 ' 0- 1023 Select Sensorval Case sollspeed ' do nothing Case < sollspeed If PowerOn = 1 Then WriteAnalog2 sollspeed ' HPWM 2, sollspeed, fPWM ' accelerate EndIf Case > sollspeed WriteAnalog2 0 'HPWM 2, 0, fPWM ' WriteAnalog1 Sensorval - sollspeed 'HPWM 1, Sensorval - sollspeed, fPWM ' braking, if implemented EndSelect EndIf Case 1 ' windpressure only controlled with CC7 WriteAnalog2 Volume 'CC7 << 3 '0 - 1023 'HPWM 2, CC7 << 1 , fPWM Case 2, 4 ' semi-intelligent mode If PowerOn = 0 Then WriteAnalog2 0 Else If Off_flag = 0 Then sollspeed = (WindReq * CC7) >> 7 sollspeed = Minspeed + sollspeed WriteAnalog2 sollspeed Else WriteAnalog2 0 EndIf EndIf Case 3 ' only compensating for the number of notes playing If nrnotes = 0 Then If PowerOn = 0 Then WriteAnalog2 0 EndIf Else sollspeed = CC7 << 2 ' 0- 508 sollspeed = Minspeed + sollspeed + (nrnotes * 15) ' 0-1010 WriteAnalog2 sollspeed EndIf Case 5 ' only compensating for the number of notes playing If nrnotes = 0 Then If PowerOn = 0 Then WriteAnalog2 0 EndIf Else sollspeed = CC7 << 2 ' 0- 508 sollspeed = Minspeed + sollspeed + (nrnotes * 10) ' 0-885 WriteAnalog2 sollspeed EndIf Case 6 ' only compensating for the number of notes playing If nrnotes = 0 Then If PowerOn = 0 Then WriteAnalog2 0 EndIf Else sollspeed = CC7 << 2 ' 0- 508 sollspeed = sollspeed + CC7 ' 0 - 635 sollspeed = Minspeed + sollspeed + (nrnotes * 10) ' 0-1012 WriteAnalog2 sollspeed EndIf Case 7 ' only compensating for the number of notes playing If nrnotes = 0 Then If PowerOn = 0 Then WriteAnalog2 0 EndIf Else sollspeed = CC7 << 2 ' 0- 508 sollspeed = sollspeed + CC7 + CC7 ' 0 - 762 sollspeed = Minspeed + sollspeed + (nrnotes * 5) ' 0-1014 WriteAnalog2 sollspeed EndIf Case 8 ' using 1:3 lookup ' semi-intelligent mode If PowerOn = 0 Then WriteAnalog2 0 Else If Off_flag = 0 Then sollspeed = (WindReq * CC7) >> 7 sollspeed = Minspeed + sollspeed WriteAnalog2 sollspeed Else WriteAnalog2 0 EndIf EndIf Case Else '9, 10, 11, 12 If Off_flag = 0 Then ' here we use the 1:3 lookup with scaling steered by ctrl.70 'sollspeed = (Windreq * Trajekt) / Sigma ' calculated in reception of note on/off's 'sollspeed = sollspeed * Scalefactor If Volume <> Old_Volume Then ' this can only happen when CC7 is received sollspeed = sollspeed - Old_Volume sollspeed = sollspeed + Volume Old_Volume = Volume EndIf 'sollspeed = sollspeed + Volume ' this would always steer the motor to maximum... If sollspeed < Minspeed Then sollspeed = Minspeed WriteAnalog2 sollspeed Else WriteAnalog2 0 EndIf EndSelect Clear Motor_flag EndIf EndIf ' here we insert a bit toggle command to measure the loop speed Btg Loopspeed ' 29Khz up to 88kHz Loop ' end of the main loop Proc SortTimers (), Byte 'look up the next smallest timer value in the Timvals array ' zoek de de volgende kleinste timer waarde: Set Result Set Nxt.31 ' nxt is set on entry. - for speed, we just set the highest bit For i = 0 To Lasttask If Timvals[i] < Nxt Then Nxt = Timvals[i] ' NrTasks dword comparisons Result = i EndIf Next i Clear Resort_flag EndProc Sub KeyPres () 'the note to which the pressure should be applied is passed in NotePres, the value in Pres 'here we use it for flashing lights on Horny. ' Select notePres ' End Select Set notePres EndSub ' Sub ProgChange () ' Set prog 'this is not realy required 'EndSub 'Sub Pitchbend () ' 'only implemented on dsPIC based robots ' Set pblsb 'EndSub 'Sub Aftertouch () ' 'this is the channel aftertouch, affecting all notes ' Set aft 'not mandatory 'EndSub Sub Controller () Select Ctrl Case 7 ' volume controller used to set wind pressure CC7 = value Volume = CC7 << 3 If CC68 = 1 Then WriteAnalog2 Volume ' CC7 << 3 ' this works fine Clear Motor_flag EndIf Trajekt = Maxspeed - Volume ' 10-bit value Case 66 'on/off for the robot If value = 0 Then Clear PowerOn 'CC66.0 PowerDown () Clear OnOff_Led Else Set PowerOn 'CC66.0 -- CC66 becomes 1 CC68 = CC68_default CC69 = CC69_default Set OnOff_Led EndIf Case 68 ' operational mode for the wind control ' 0 = intelligent mode (default at start up) needs sensor! ' 1 = wind is controlled by CC7 only ' 2 = semi-intelligent motor control CC7 + windreq , 1:4 scaling ' 3 = id. but proportional to nr of notes (faktor 15) ' 4 = semi-intelligent CC7 + windreq but using the 1:2 scaling ' 5 = proportional to nr. of notes, with faktor 10 ' 6 = same as 5 but higher scaling ' 7 = proportional to nr. of notes, with faktor 5 ' 8 = semi-intelligent CC7 + windreq using the 1:3 scaling - added 23.11.2022 ' 9 = newest algo - 1:3 with scaling ' 10 = idem with 1:2 scaling ' 11 = idem with 1:4 scaling ' 12 = idem with 1:1 scaling If value <> CC68 Then Select CC68 Case 2, 11 Note_Wind_Lookup4 () ' 1:4 scaling Case 4, 10 Note_Wind_Lookup2 () ' 1:2 scaling Case 8, 9 Note_Wind_Lookup3 () ' 1:3 scaling Case Else Note_Wind_Lookup1 () ' 1 unit per note EndSelect CC68 = value If CC68 > 12 Then CC68 = 12 EndIf Set Motor_flag Case 69 ' sets the minimum speed for the motor Minspeed = value ' 7 bit is o.k. here ' note that Minspeed is an alias for CC69 Case 70 CC70 = value ' scaling factor - modulatiediepte ' genormaliseerd wordt dit: scalefactor = CC70 / 127.0 ' 0-1 , float! Case 71 CC71 = value * 4167 ' 0.1s units Case 72 CC72 = value * 4167 Case 123 AllNotesOff () End Select Set Ctrl ' mandatory reset EndSub Sub AllNotesOff () WriteAnalog1 0 'HPWM 1, 0, fPWM ' connected to RC2 - brake WriteAnalog2 Minspeed 'HPWM 2, 0, fPWM ' connected to RC1 - motor Set Timvals[0] Timvals[1] = time + CC71 '41666 ' so after 1 second , the motor will stop. Set Resort_flag Clear WindReq Clear nrnotes Clear Notes ' array Clear Motor_flag EndSub Sub PowerDown () ' full stop of the motor. WriteAnalog1 0 ' connected to RC2 - Brake WriteAnalog2 0 ' connected to RC1 - motor Clear WindReq Clear CC7 Clear Volume CC68 = CC68_default ' reset to default CC69 = CC69_default CC70 = CC70_default CC71 = CC71_default CC72 = CC72_default scalefactor = 0.5 Clear nrnotes Clear Notes ' array Set Timvals Clear Motor_flag EndSub Sub Dur_Lookup () 'this lookup is for a good scaling of the velocity byte on event periodicity ' the values are calculated based on a timer resolution of 24 microseconds. ' The values must be containable in a word (16 bits!) ' This is the simple Power Basic program wherewith the lookup was calculated: 'FUNCTION PBMAIN ()AS LONG ' OPEN "Whisper_Dur_scales.inc" FOR OUTPUT AS #1 ' LOCAL unit, fastest, slowest, velo_traject AS DOUBLE ' LOCAL velo, i AS DWORD ' unit = 0.000024 ' in seconds (24 microseconds) ' fastest = 1 / 16 ' 16 Hz ' slowest = 1.5 ' 0.66 Hz ' velo_traject = slowest - fastest ' PRINT# 1, "Lookup for Whisper durations in periodic events" ' FOR i = 1 TO 127 ' velo = velo_traject / i ' PRINT i; velo, ' PRINT# 1 ,"Dur[";i;"] ="; velo ' NEXT i 'DO: LOOP UNTIL INKEY$ <> "" 'END FUNCTION Dur[ 1 ] = 59896 Dur[ 2 ] = 29948 Dur[ 3 ] = 19965 Dur[ 4 ] = 14974 Dur[ 5 ] = 11979 Dur[ 6 ] = 9983 Dur[ 7 ] = 8557 Dur[ 8 ] = 7487 Dur[ 9 ] = 6655 Dur[ 10 ] = 5990 Dur[ 11 ] = 5445 Dur[ 12 ] = 4991 Dur[ 13 ] = 4607 Dur[ 14 ] = 4278 Dur[ 15 ] = 3993 Dur[ 16 ] = 3743 Dur[ 17 ] = 3523 Dur[ 18 ] = 3328 Dur[ 19 ] = 3152 Dur[ 20 ] = 2995 Dur[ 21 ] = 2852 Dur[ 22 ] = 2723 Dur[ 23 ] = 2604 Dur[ 24 ] = 2496 Dur[ 25 ] = 2396 Dur[ 26 ] = 2304 Dur[ 27 ] = 2218 Dur[ 28 ] = 2139 Dur[ 29 ] = 2065 Dur[ 30 ] = 1997 Dur[ 31 ] = 1932 Dur[ 32 ] = 1872 Dur[ 33 ] = 1815 Dur[ 34 ] = 1762 Dur[ 35 ] = 1711 Dur[ 36 ] = 1664 Dur[ 37 ] = 1619 Dur[ 38 ] = 1576 Dur[ 39 ] = 1536 Dur[ 40 ] = 1497 Dur[ 41 ] = 1461 Dur[ 42 ] = 1426 Dur[ 43 ] = 1393 Dur[ 44 ] = 1361 Dur[ 45 ] = 1331 Dur[ 46 ] = 1302 Dur[ 47 ] = 1274 Dur[ 48 ] = 1248 Dur[ 49 ] = 1222 Dur[ 50 ] = 1198 Dur[ 51 ] = 1174 Dur[ 52 ] = 1152 Dur[ 53 ] = 1130 Dur[ 54 ] = 1109 Dur[ 55 ] = 1089 Dur[ 56 ] = 1070 Dur[ 57 ] = 1051 Dur[ 58 ] = 1033 Dur[ 59 ] = 1015 Dur[ 60 ] = 998 Dur[ 61 ] = 982 Dur[ 62 ] = 966 Dur[ 63 ] = 951 Dur[ 64 ] = 936 Dur[ 65 ] = 921 Dur[ 66 ] = 908 Dur[ 67 ] = 894 Dur[ 68 ] = 881 Dur[ 69 ] = 868 Dur[ 70 ] = 856 Dur[ 71 ] = 844 Dur[ 72 ] = 832 Dur[ 73 ] = 820 Dur[ 74 ] = 809 Dur[ 75 ] = 799 Dur[ 76 ] = 788 Dur[ 77 ] = 778 Dur[ 78 ] = 768 Dur[ 79 ] = 758 Dur[ 80 ] = 749 Dur[ 81 ] = 739 Dur[ 82 ] = 730 Dur[ 83 ] = 722 Dur[ 84 ] = 713 Dur[ 85 ] = 705 Dur[ 86 ] = 696 Dur[ 87 ] = 688 Dur[ 88 ] = 681 Dur[ 89 ] = 673 Dur[ 90 ] = 666 Dur[ 91 ] = 658 Dur[ 92 ] = 651 Dur[ 93 ] = 644 Dur[ 94 ] = 637 Dur[ 95 ] = 630 Dur[ 96 ] = 624 Dur[ 97 ] = 617 Dur[ 98 ] = 611 Dur[ 99 ] = 605 Dur[ 100 ] = 599 Dur[ 101 ] = 593 Dur[ 102 ] = 587 Dur[ 103 ] = 582 Dur[ 104 ] = 576 Dur[ 105 ] = 570 Dur[ 106 ] = 565 Dur[ 107 ] = 560 Dur[ 108 ] = 555 Dur[ 109 ] = 550 Dur[ 110 ] = 545 Dur[ 111 ] = 540 Dur[ 112 ] = 535 Dur[ 113 ] = 530 Dur[ 114 ] = 525 Dur[ 115 ] = 521 Dur[ 116 ] = 516 Dur[ 117 ] = 512 Dur[ 118 ] = 508 Dur[ 119 ] = 503 Dur[ 120 ] = 499 Dur[ 121 ] = 495 Dur[ 122 ] = 491 Dur[ 123 ] = 487 Dur[ 124 ] = 483 Dur[ 125 ] = 479 Dur[ 126 ] = 475 Dur[ 127 ] = 472 EndSub Sub Note_Wind_Lookup1 () ' lookup for wind-consumption in function of the reedsize/note. ' this is a dummy where we assume each reed required only 1 and the ' same wind quantity For i = 60 To 84 NoWi[i] = 1 Next i ' all notes-cluster makes windreq = 25 Sigma = GetSigma () EndSub Sub Note_Wind_Lookup4 () ' lookup for wind-consumption in function of the reedsize/note. ' this is a 1 on 4 scaling, a bit exagerated NoWi[60] = 64 NoWi[61] = 61 NoWi[62] = 58 NoWi[63] = 56 NoWi[64] = 53 NoWi[65] = 50 NoWi[66] = 48 NoWi[67] = 45 NoWi[68] = 42 NoWi[69] = 40 NoWi[70] = 37 NoWi[71] = 34 NoWi[72] = 32 NoWi[73] = 30 NoWi[74] = 29 NoWi[75] = 28 NoWi[76] = 26 NoWi[77] = 25 NoWi[78] = 24 NoWi[79] = 22 NoWi[80] = 21 NoWi[81] = 20 NoWi[82] = 18 NoWi[83] = 17 NoWi[84] = 16 ' all notes-cluster makes windreq = 896, so the sum does not exceed 10 bits ' if we set minspeed to 127 and add 896, we have 1023. Sigma = GetSigma () EndSub Sub Note_Wind_Lookup2 () ' lookup for wind-consumption in function of the reedsize/note. ' this is a 1 on 2 scaling NoWi[60] = 32 NoWi[61] = 32 NoWi[62] = 31 NoWi[63] = 30 NoWi[64] = 29 NoWi[65] = 28 NoWi[66] = 28 NoWi[67] = 27 NoWi[68] = 27 NoWi[69] = 26 NoWi[70] = 25 NoWi[71] = 25 NoWi[72] = 24 NoWi[73] = 23 NoWi[74] = 23 NoWi[75] = 22 NoWi[76] = 22 NoWi[77] = 21 NoWi[78] = 20 NoWi[79] = 19 NoWi[80] = 19 NoWi[81] = 18 NoWi[82] = 18 NoWi[83] = 17 NoWi[84] = 16 ' sum of all notes is = 602 ' if minspeed = 127 we get 729, so still some room for volume control Sigma = GetSigma () EndSub Sub Note_Wind_Lookup3 () ' lookup for wind-consumption in function of the reedsize/note. ' this is a 1 on 3 scaling NoWi[60] = 48 NoWi[61] = 46 NoWi[62] = 45 NoWi[63] = 43 NoWi[64] = 42 NoWi[65] = 41 NoWi[66] = 40 NoWi[67] = 39 NoWi[68] = 38 NoWi[69] = 36 NoWi[70] = 35 NoWi[71] = 33 NoWi[72] = 32 NoWi[73] = 31 NoWi[74] = 30 NoWi[75] = 29 NoWi[76] = 27 NoWi[77] = 25 NoWi[78] = 23 NoWi[79] = 23 NoWi[80] = 21 NoWi[81] = 19 NoWi[82] = 18 NoWi[83] = 17 NoWi[84] = 16 ' sum of all notes is = 797 ' if minspeed = 127 we get 924, so still some room for volume control Sigma = GetSigma () EndSub Proc GetSigma (), Word ' to be recalculated whenever we use another lookup table 'Clear Sigma Clear Result For i = 60 To 84 Result = Result + NoWi[i] Next i 'Trajekt = Maxspeed - Volume ' Maxspeed = 1023 , volume =(CC7 << 3) calculated on reception of CC7 'Stepsize = Trajekt / Sigma EndProc ' ' could also be code as a function with a define: ' ' note the essential ' signs! ' $define Getsigma (sigma) ' ' Clear sigma ' ' For i = 60 To 84 ' ' sigma = sigma + NoWi[i] ' ' Next i ' ' Trajekt = Maxspeed - (CC7 << 3) ' ' stepsize = Trajekt / sigma '[EOF]