'**************************************************************** '* Name : Harma_motor.BAS * '* Author : Godfried-Willem RAES * '* Notice : Copyleft (c) 2015 Logosoft Public Domain * '* Date : 20.02.2015 * '* Version : 1.1 * '* Notes : For Siemens motor controller * '**************************************************************** ' special PCB for Reed Organ motor controllers ' we try to use an intelligent algorithm for wind pressure. ' 21.02.2015: Tests: intelligent mode works but has slow response. ' maybe it's not a good idea to use a task structure here.. ' 22.02.2015: Further development and experiments. ' Here we update the PWM for the motor as fast as data flow in. ' Of course we count on integration in the hardware. ' This version V1.1 tested under various conditions. ' 24,02,2015: Controllers added for testing purposes ' green and blue led added for monitoring. Include "18F2525.inc" ' (40MHz) 'Include "18F2520.inc" 'also possible. (40MHz) 'Include "18F25K20.inc" 'for test & debug on an Amicus board. (64MHz) ' Mapping defines for midi-events on pin outputs and inputs: $define Motor_On PORTC.5 ' motor on/off switch $define Motor_Dir PORTC.4 ' direction of rotation $define Motor_PWM PORTC.1 ' motor speed control $define Acknowledge PORTC.2 ' error acknowledge $define BlueLED PORTA.1 $define GreenLED PORTA.0 'red LED for debug: $define Debug_Led PORTB.5 ' for testing - red led - watchdog ' configure the input and output pins: Clear SSPCON1.5 'RC3 must be available for I/O TRISA = %01000100 '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 = 9 ' Harma 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 fPWM = PWMminF * 4 ' in avoidance of audible artifacts 'Symbol MinSpeed = 15 ' changed to variable, mapped on controller #69 for testing 'Symbol Airflowrange = 127 - MinSpeed ' we could also make this a variable and let it grow with decreasing volume as well. Symbol MaxSpeed = 255 ' 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 ' Declare Hserial_Clear = On ' should clear on errors. Bytes get lost of course... ' 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 Tim3 As TMR3L.Word 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 ' midi variables Dim statusbyte As Byte System Dim noteUit As Byte System ' note off + release value Dim release As Byte System Dim noteAan As Byte System ' note on + release value Dim velo As Byte System Dim notePres As Byte System ' note pressure + pressure value Dim pres As Byte System Dim Ctrl As Byte System ' continuous controller + value Dim value As Byte System Dim prog As Byte System ' program change + program-byte Dim reg1 As prog.0 ' so the sum reg1 + reg2 + reg3 + reg4 reflects the number of registers drawn. Dim reg2 As prog.1 Dim reg3 As prog.2 Dim reg4 As prog.3 Dim regs As Byte System ' Dim aft As Byte System ' channel aftertouch ' Dim pblsb As Byte System ' pitch bend lsb ' Dim pbmsb As Byte System ' pitch bend msb Dim veltim As Dword System ' 32 bit velo Dim VelFlags As Word System ' bits 0 - 15 used as flags for active timers Dim VelFlags0 As VelFlags.Byte0 ' alias for bits 0-7 Dim CC66 As Byte System ' global on/off switch Dim st As Byte System Dim b1 As Byte System Dim b2 As Byte System Dim Volume As Byte System ' CC7 value Dim Motorspeed As Byte System ' calculated continuously ' Dim nrnotes As Byte System Dim pw As Byte System ' Motor_PWM Dim Motor_mode As Byte System ' CC68 value - zero by default Dim Airflow As Word System ' calculated with the note-on/off commands Dim Minspeed As Byte System Dim Airflowrange As Byte System '----------------------------------------------------------------------------------------- ' 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 "Harma_Mot_Irq.inc" ' our own version for UART And Timer0/3 Interrupt 'Include "Timers.inc" ' required for velo support with timed pulses and periods. 'framework for a multitasker: Dim Task_rsi[NrTasks] As Word 'task reschedule interval (period), if 0 the task is not active 'max. value limited to 65535. For longer periods, it will have to 'become dword!!! Dim Velmsb[NrTasks] As Word 'the application for velo-timers, is in fact just a one-shot task Dim VelLsb[NrTasks] As Word Dim Note_Air[61] As Byte ' lookup met de luchtbehoefte in funktie van de toonhoogte 'DeclareDwordArray(TimeVals , NrTasks) 'alternative using the macro's. [not yet used] ' assigning values syntax: DwordArray Timvals,[i], value ' reading values syntax: value = DwordArray TimVals,[i] 'make sure we initialize the used pins on start up: 'fault?: there should be no executable statements outside the main program. 'Set Motor_PWM ' this switches motor to lowest speed Set Acknowledge ' so output will be 0 or off Set Motor_On ' 0 or OFF Set Motor_Dir ' 0 or OFF Low Debug_Led Low GreenLED Low BlueLED HPWM 2, 255, fPWM ' connected to RC1 'HPWM 1, 0, fPWM ' connected to RC2 Clear CC66 Clear Airflow Clear Motor_mode ' intelligent mode by default 'GoSub Air_Lookup ' square root table GoSub Note_Air_Lookup_16 ' this is the default lookup on startup. It can be changed with ctrl#70 Volume = 80 ' default on startup Minspeed = 0 ' was 15 in previous version, defined as constant Airflowrange = 127 - Minspeed '----------------------------------------------------------------------------------------- ' Main program starts here MAIN: High Debug_Led DelayMS 50 ' wait for stability Low Debug_Led Clear VelFlags0 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 bit, 0=16 bit ' bit 5 = 1 pin input, 0= Internal Clk0 ' bit 4 = HL or LH transition when bit5 =1 ' bit 3 = 1= bypass prescaler, 0= input from prescaler ' bit 2-0 = prescaler select: 111= 1:256 ' Setup the High priorities for the interrupts ' open 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 ' 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 > Control_Status Then ' here higher statusses are not implemented. If Bytein > 253 Then '254 = midiclock, 255= reset 'midiclock can interrupt all other msg's... '255 had to be intercepted since thats what we 'get when no new byte flows in (?) GoTo Check_Timers 'throw away... Else Clear statusbyte 'reset the status byte GoTo Check_Timers 'throw away End If EndIf If StBit =1 Then 'should be faster than If Bytein > 127 Then 'status byte received, bit 7 is set Clear statusbyte 'if on another channel, the statusbyte needs a reset Select Bytein 'eqv to Select case ByteIn Case NoteOff_Status statusbyte = Bytein Set noteUit '= 255 'reset value. Cannot be 0 !!! Set release '= 255 '0 is a valid midi note! Case NoteOn_Status statusbyte = Bytein Set noteAan '= 255 Set velo '= 255 Case Keypres_Status ' used for lights statusbyte = Bytein Set notePres '= 255 Set pres '= 255 Case Control_Status ' 7, 66, 67, 68, 123 statusbyte = Bytein Set Ctrl '= 255 Set value '= 255 Case ProgChange_Status statusbyte = Bytein Set 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 29 To 89 i = Note_Air[noteUit - 29] If Airflow >= i Then Airflow = Airflow - i Else Clear Airflow EndIf Motorspeed = Volume + (Airflow Min Airflowrange) + Minspeed End Select Set noteUit '= 255 'reset EndIf GoTo Check_Timers Case NoteOn_Status If noteAan = 255 Then noteAan = Bytein Else velo = Bytein If velo = 0 Then Select noteAan Case 29 To 89 i = Note_Air[noteAan - 29] If Airflow >= i Then Airflow = Airflow - i ' we also tried not to recalculate Motorspeed here. On purpose, but it's not an improvement. Else Clear Airflow EndIf Motorspeed = Volume + (Airflow Min Airflowrange) + Minspeed End Select Set noteAan '= 255 'reset !!! GoTo Check_Timers 'jump out EndIf Select noteAan Case 29 To 89 'If nrnotes < 60 Then Inc nrnotes ' should run 1 - 60 ' maybe we should ceil this, as 60 is quite unrealistic... ' we tried working with nrnotes, but it does not lead to usefull results. Airflow = Airflow + Note_Air[noteAan - 29] ' to have fast response, we should send the motor commands from here... Motorspeed = Volume + (Airflow Min Airflowrange) + Minspeed ' * regs '; to be checked. End Select Set noteAan '= 255 'reset EndIf GoTo Check_Timers Case Keypres_Status 'not used here If notePres = 255 Then notePres = Bytein Else pres = Bytein ' GoSub KeyPres EndIf GoTo Check_Timers Case Control_Status 'this is where the action takes place for controllers If Ctrl = 255 Then Ctrl = Bytein Else value = Bytein GoSub Controller EndIf GoTo Check_Timers Case ProgChange_Status 'als geen registers aanstaan, moet ook de motor bijna niet draaien. If prog = 255 Then prog = Bytein regs = reg1 + reg2 + reg3 + reg4 ' so this runs 0,1,2,3,4 ' Motor_mode ' 0 = intelligent wind management ' ' >0 = motor speed in function of ctrl 7 only. If Motor_mode = 0 Then If regs > 0 Then Motorspeed = Volume + (Airflow Min Airflowrange) + Minspeed '??? Else Motorspeed = Minspeed EndIf Else Motorspeed = Volume << 1 End If 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.1 = 1 Then veltim.Word1 = Velmsb[1] veltim.Word0 = VelLsb[1] Cnt.Word0 = CntLw ' read counter If Cnt >= veltim Then ' Error reset task Clear VelFlags0.1 ' stop task Set Acknowledge ' inverted, so this switches to off again End If Else If CntHw > 0xFF Then Clear CntHw EndIf ' dynamic motor control: If Motor_mode = 0 Then ' steer motor continuously: (intelligent mode) ' the motorspeed variable is calculated in the main loop If regs = 0 Then Motorspeed = Minspeed ' Else ' Motorspeed = Volume + (Airflow Min Airflowrange) + MinSpeed End If Else Motorspeed = Volume << 1 End If pw = 255 - Motorspeed HPWM 2, pw, fPWM ' connected to RC1 GoTo LOOP ' end of the main loop KeyPres: Set notePres '= 255 Return ProgChange: Set prog '= 255 'this is not realy required Return 'Pitchbend: ' 'only implemented on dsPIC based robots ' Set pblsb '= 255 'Return 'Aftertouch: ' 'this is the channel aftertouch, affecting all notes ' Set aft '= 255 'not mandatory 'Return Controller: Select Ctrl Case 7 ' this is the wind controller Volume = value If Motor_mode = 0 Then ' flag set with ctrl 68 Motorspeed = Volume + (Airflow Min Airflowrange) + Minspeed Else Motorspeed = value << 1 ' make 8 bit End If Case 66 'on/off for the robot 'power down will also reset all controllers to default values If value = 0 Then Clear Airflow Set Motor_On ' inverted! - so this switches the motor OFF Clear CC66 Clear VelFlags0 Clear Motor_mode ' default to intelligent mode Clear GreenLED Clear BlueLED Volume = 80 ' default volume setting Minspeed = 0 Airflowrange = 127 - Minspeed Else Clear Motor_On ' inverted - so motor is switched ON Set CC66 Set GreenLED EndIf Case 67 ' error reset controller , the value is irrelevant here ' must set Acknowledge for a certain time (need task here) Clear Acknowledge ' inverted! Set VelFlags0.1 Cnt.Word0 = CntLw Task_rsi[1] = 2000 veltim = Cnt + Task_rsi[1] Velmsb[1] = veltim.Word1 VelLsb[1] = veltim.Word0 Case 68 If value = 0 Then Clear Motor_mode ' intelligent mode Clear BlueLED Else Set Motor_mode ' user steered mode Set BlueLED End If Case 69 ' new for testing minspeed. Minspeed = value Airflowrange = 127 - Minspeed If Motor_mode = 0 Then ' flag set with ctrl 68 Motorspeed = Volume + (Airflow Min Airflowrange) + Minspeed Else Motorspeed = Volume << 1 ' make 8 bit End If Case 70 ' for testing different windflow lookup tables Select value Case 0 GoSub Note_Air_Lookup_16 Case 1 GoSub Note_Air_Lookup_8 Case 2 GoSub Note_Air_Lookup_4 End Select Case 123 Clear Airflow ' all notes off. Motorspeed = Minspeed + Volume End Select Set Ctrl '= 255 'mandatory reset Return 'Air_Lookup: ' ' poging om de luchtbehoefte af te leiden van het aantal spelende noten ' ' dit bleek niet goed te werken, gezien de grote verschillen in luchtbehoefte voor de diverse noten. ' For i = 1 To 60 ' Air[i] = Sqr[i] ' 1 to 7 ' Next i 'Return Note_Air_Lookup_16: ' lookup met het luchtverbruik in funktie van de toonhoogte. For i = 29 To 89 Note_Air[i -29] = ((89 - i) / 4) + 1 ' so these values run between 16 and 1 Next i Return Note_Air_Lookup_8: ' lookup met het luchtverbruik in funktie van de toonhoogte. For i = 29 To 89 Note_Air[i -29] = ((89 - i) / 8) + 1 ' so these values run between 8 and 1 Next i Return Note_Air_Lookup_4: ' lookup met het luchtverbruik in funktie van de toonhoogte. For i = 29 To 89 Note_Air[i -29] = ((89 - i) / 16) + 1 ' so these values run between 4 and 1 Next i Return '[EOF]