'**************************************************************************** '* * '* 16 bit 24EP128MC202 microcontroller * '* Godfried-Willem Raes * '* 14-bit PWM and PWM base frequency 7.3kHz * '* with ramping and braking implemented * '* 2018: new board - now using DC motor * ' *************************************************************************** ' NOTE: Pickit3 must be used for programming these devices! ' Each interrupt has its own handler, simply write one handler for each interrupting ' device without having to work out which interrupt it is. ' Timers are more logical: specify the count going upwards that will trigger an ' interrupt rather than working out when the counter would underflow. ' 20.05.2017: Start coding for thunderwood : 3-phase 400Hz airplane fan motor ' 23.05.2017: crashes as soon as we switch storm on... ' not enough processing speed to handle the tim1 IRQ was the cause ' In principle it seems to work now. Motor speeds are too slow though. ' PCB produced. ' 24.05.2017: First tests on PCB ' Version 000: ' This version makes a 1 -90Hz motor controller, 3-phase, triangle connected. ' 25.05.2017: Version 001: ' 50Hz - 400Hz version using 8-bit pwm at 100kHz ' with a 8-bit sine-wave in 64-points per period. ' Braking implemented now ' 26.05.2017: Getting stuck in the main loop and thus missing midi commands... ' Try getting rid of floats. ' 1nF snubber caps added over motor windings. ' This be version 002. ' 26.05.2017: Integer math applied. Amplitude now controlled by phase shifting the sinewave. ' This works fine, but we are still missing midi commands when the motor runs at full ' speed. We note a lot of spikes on the midi data-input line... so we may have to add ' a small choke here. ' 27.05.2017: Code seems to run fine now. Hardware needed snubbers and Pi-filters. ' Loopspeed is now 434kHz with no load and drops to 363kHz worst case (Brake-condition). ' Brake time is now 0-1 second. ' instead of BUK454 we would better use IPP60R180C7XKSA1 mosfets. ' -------------------------------------------------------------------------------------------------- ' 29.08.2017: Starting from the code for Thunderwood storm. ' This should be a 0-50Hz motor controller for 3-phase induction motors. ' This uses a special PCB, with IPP60R180C7 power Mosfets. ' 30.08.2017: Back to a 64-points per period sinewave lookup ' PWM base frequency lowered to 29 kHz, still using 8 bit PWM resolution ' 31.08.2017: ADC working at 100 S/s sampling rate. This is for manual setting of motor speed. ' Afters hours of experimenting with settings, finally the ADC seems to work now... ' 01.09.2017: Continued research. ' 02.09.2017: Start-up problem detected... ' Flow chart bugs removed. ' Tacho not yet implemented. ' 03.09.2017: Tests with Siemens 3-phase motor. ' Problem: motor enable goes low when we raise the power supply voltage for the motor... ' 04.09.2017: Further testing, debugging and measurement. We suffer from motorboating. ' 06.09.2017: All mosfets burned out in the circuit. Driver chips and processor as well... ' 08.09.2017: PWM base frequency lowered to 7.6kHz, strict V/f scaling applied. ' Still not working on higher voltages than 40V... ' This version saved as Balsi_Motor_000.bas ' 10.09.2017: Attempting to recode for 16-bit PWM resolution and low PWM base frequency. ' nothing works amymore... ' Working at base freq 1.8 kHz now. ' Ramping added, not yet working well... ' 11.09.2017: Ramping now works with midi control. ' to be added: ramping with potmeter control. ' declared constant or parameter for ramping speed. ' Ramping added for potmeter control and for key pressure commands. ' The circuit still is not working with voltages higher than 40V... We are blowing ' IR2104 chips all the time... Erlarge the bootstrap capacitor? (now 47nF) ' 15.09.2017: New board made, using 7667 drivers and 1:1:1 transformers. ' This works best with PCM base frequencies between 20kHz and 40kHz. ' The board works fine, but the mosfet driver board fails... '------------------------------------------------------------------------------------------------------------- ' 07.12.2018: New code, for PWM control of 3 DC motors or solenoids. ' New PCB. ' PWM resolution is 16 bits ' sofar no tasks used. ' This should become the firmware for board 1: large siren and bidirectional solenoid damper ' The horn output is as yet uncommitted. ' 08.12.2018: Note-frequency table implemented ' first sketch of a PID autoregulator using the tacho for pitch ' controller 67 introduced to switch autoregulation on or off. By default OFF. ' 09.12.2018: ctrl 67 added for PID on/off ' PID should only start regulating from the moment the frequency reading is more or less stable... ' The irq0 enable/disable switch in IEC0.0 seems not working... ' Old X-tal microphone digged up to steer the tacho input. ' We could simplify the code by directly adressing the pwm port and not using timer1 IRQ at all. ' This version saved as Balsi_DC_Motor.bas ' Direct control of PDC1,2,3 registers seems to work better... Timer1 is free now. ' could be used for an extra PWM output... ' 10.12.2018: Midi implementation written out. ' 16.12.2018: No problems envountered after compiler upgrade. ' 05.01.2019: PCB finished with power components. Recheck midi implementation. ' Version 1.0 flashed. ' First test run. Observations: ' ctrl 66 on works, power off not working... ' horns not working ' siren fails intermittent, damper not working as it should. Tacho not yet connected. ' 07.01.2019: Hardware debug session. ' Potmeter control procedure bypassed and remmed. ' 10.01.2019: Further improvements... ' autorepeats added for claxon ' Version flashed ar version 1.0 ' 12.01.2019: simulation program written for the PID algo, in PBCC ' 13.01.2029: bug in tacho IRQ coding killed. ' note range for siren adapted. Ctrl 48 added for lsb of pitch ' 14.01.2019: siren mechanically repaired. ' bug in solenoid staying on after all-off ' we need 2 lookups: one for damper closed, one for damper open. ' 17.01.2019: Trying to choose a 4-times higher pwm frequency. (PTPER = 16384) This lowers resolution to 14 bits. ' should be no problem, as midi control is limited to 14 bits anyway. ' This be version 2.0 - firmware uploaded and tested o.k. ' 07.12.2020: Timer3 read bug killed. Recompiled but not yet uploaded. '------------------------------------------------------------------------------------------------------------- ' START: Device = 24EP128MC202 ' this code should also work on 24EP32MC202, 24EP64MC202, 24EP256MC202, 24EP512MC202 ' and dsPIC33EP32MC202, dsPIC33EP64MC202, dsPIC33EP128MC202, dsPIC33EP256MC202, dsPIC33EP512MC202 ' and dsPIC33EP32MC502, dsPIC33EP64MC502, dsPIC33EP128MC502, dsPIC33EP256MC502, dsPIC33EP512MC502 Clear ' port-assignments - hardware specifics '************************************** Symbol watchdog_led PORTB.5 ' red LED - pin 14 Symbol horn PORTB.6 ' pin 15 - 24V motorbike claxon , note 98 Symbol Orange_led PORTB.1 Symbol Green_led PORTA.1 ' follows CTRL #66: If ON midi control is active, if OFF potentiometer controls speed Symbol Yellow_led PORTB.0 ' ON when the motor is running. - changed. Now follows tacho and external IRQ ' note: RB7 used as input for tacho, via IRQ ' RA0 used as analog input for potentiometer speed control Config FPOR = ALTI2C1_OFF, ALTI2C2_OFF ' I2C support OFF Config FWDT = WDTPOST_PS256, WINDIS_OFF, PLLKEN_ON, FWDTEN_OFF ' FWDTEN_OFF disables the WDT (watchdog timer) RCON.5 = 0 ' clear the watchdog enable bit for reset = SWDTEN_OFF ' first configure the clock... ' select internal FRC at POR not using PLL: Config FOSCSEL = FNOSC_FRC, IESO_OFF DelayMS 10 ' make sure we assign the correct pins to use for the ICD (Pickit3) Config FICD = ICS_PGD1, JTAGEN_OFF ' this sets the pins used for the programmer ' enable clock switching and configure POSC for XT mode with 10MHz crystal Config FOSC = FCKSM_CSECMD, OSCIOFNC_OFF, POSCMD_XT Main_Setup: ' configure PLL prescaler, PLL postscaler, PLL divisor PLLFBD = 46 CLKDIV.7 = 0 'PLLPOST N2=2 CLKDIV.6 = 0 CLKDIV.0 = 0 'PLLPRE N1=2 CLKDIV.1 = 0 CLKDIV.2 = 0 CLKDIV.3 = 0 CLKDIV.4 = 0 Write_OSCCONH ($3) ' = __builtin_write_OSCCONH(0x03) Write_OSCCONL (OSCCON | %1) ' = __builtin_write_OSCCONL(OSCCON | 0x01) PLL_Setup(48, 2, 2, $0300) ' should set it to 120MHz operation with 10MHz X-tal ' Fosc = 10MHz * 48 / 2 * 2 = 120MHz ' now we get a 10MHz clock signal on pin OSC2, pin10... ' ----------------------------- DelayMS 1000 ' wait for stability Declare Xtal = 120 ' set to Fosc - the clock frequency is 60MHz (MIPS) ' thus 1 instruction cycle is 16.666 ns ' new 18.04.2017: but this makes no difference at all in our code. 'Declare Stack_Size 200 ' by default it's 60 words or 120 bytes 'Declare Stack_Expand 1 ' expand stack automaticaly if required. $define Enable_UART_RX ' midi-in '$define Enable_UART_TX ' midi-out $define Enable_ADC12bit ' precompiler instructions '$define Enable_ADC10bit $define Enable_PWM ' precompiler instructions $define Enable_Timer1 ' precompiler instructions - 16 bit timer - sampling rate timer $define Enable_Timer23 ' 32-bit timer '$define enable_Timer45 ' 32-bit timer $define Enable_Timer4 ' as 16 bit timer - for period measurement in tacho $define Enable_Timer5 ' as 16 bit timer - for ADC sampler 100 S/s $define Enable_Int0 ' enable external interrupt - for tacho 'Declare Hserial_Baud = 31250 ' USART1 baud rate - set to MIDI 'Declare Hrsout1_Pin = PORTB.8 ' RP40 ' Select the pin for TX with USART1 'Declare HRSin1_Pin = PORTB.9 ' RP41 PPS_Output(cOut_Pin_RP40, cOut_Fn_U1TX) ' Map UART1 TX pin to RP40 'RP35 PPS_Input(cIn_Pin_RP41, cIn_Fn_U1RX) ' Map UART1 RX pin to RP41 'RPI34 'constant initialisations for the midi input parser: Symbol Midichannel = 15 ' Balsi_Channel Symbol NoteOff_Status = 128 + Midichannel ' 2 bytes follow Symbol NoteOn_Status = 144 + Midichannel Symbol Keypres_Status = 160 + Midichannel Symbol Control_Status = 176 + Midichannel Symbol ProgChange_Status = 192 + Midichannel ' 1 byte message Symbol Aftertouch_Status = 208 + Midichannel ' 1 byte follows Symbol Pitchbend_Status = 224 + Midichannel ' lsb msb follow Symbol NrTasks = 3 ' used for braking and ramping and note repeats on horn Symbol LastTask = 2 ' = NrTasks - 1 ' following parameters are in 4.224 us units, using timer 2/3 Symbol PID_time = 10000 '2048 ' task period for PID pitch regulation ' 1000 = 4.224 ms or 236 Hz ' 10000 = 23.6Hz Symbol RampUp_time = 2000 '3008 ' periods for ramping up and down 1.6 ms with 400 Symbol RampDown_time = 2500 '3008 ' slowing down ramp period 2.1 ms with 500 Symbol PID_Fork_Default = 1 ' default for ctrl. 68 - PID lock range ' constants for PID algorithm: (should be floats...) Symbol kp = 1 ' proportionality constant Symbol ki = 0.01 ' integration constant Symbol kd = 0.7 ' differentiation constant ' following parameter is in 1.06 us units, using timer 1 'symbol Srate = 1875 ' fixed setting for timer1 - pwm refresh rate ' to be checked: pwm refresh rate must be faster than de PID refresh rate... ' 18750 geeft 50Hz checked ok. ' 1875 geeft 500Hz checked ok. ' midi-note mapping Symbol SirLo = 42 ' 29 on 07.12.2018 - to be measured! ' 13.01.2019: changed to 42 Symbol SirHi = 84 ' 86 on 07.12.2018 ' measurement 09.12.2018: 2.4V --> 29 ' 12V --> 74 50% PWM ' 1 PWM unit = 0.3662 mV ' note 86 = 100% PWM - 24V ' changed to 84, 13.01.2019, may still be too high. Symbol Horn_note = 98 ' 10.12.2018 , changed to 98, 05.01.2018 ' notes 96,97 are the horns on the hub board ' implemented on other boards, but not handled in this code: Symbol Small_Siren_note = 24 ' also uses controller 24 Symbol Sire3_note = 25 ' also uses controller 25 Symbol Bell_note = 28 Symbol Horn2_note = 99 ' maybe required to disable PID if this note sounds... Symbol Orange_lite_note = 120 ' also uses controller 120 'Symbol Orange_motor_note = 121 Symbol Blue_Lite_note = 122 'Symbol Blue_Motor_note = 123 Symbol LeftOrangePinker = 123 Symbol RightOrangePinker = 124 TRISA = %100001 TRISB = %0101011110000000 Output watchdog_led Output horn ' claxon 24V Output Orange_led Output Green_led ' power on/off led - ctrl.66 Output Yellow_led ' tacho led - follows sensor frequency ' note: we can monitor the siren motor pwm on the redundant PWM0 output, red led. Variables: ' variable declarations: ' variables for midi reception and parsing: ' if buffers are > 255, indexes must be word! Dim RCIdxIn As Byte 'Word ' Pointer to the next empty location in the buffer Dim RCIdxOut As Byte 'Word ' Pointer to the location of the oldest character in the buffer ' Dim RCBuffer[RCBufArraySize] As Byte ' Array for holding received characters in the uart Dim RCbuffer[256] As Byte ' Dim TXBuffer[TXBufArraysize] As Byte ' buffer for midi-output UART ' Dim TXIdxIn As Word ' Dim TXIdxOut As Word Dim Bytein As Byte ' midi byte read from buffer Dim StBit As Bytein.7 ' highest bit of ByteIn ' midi variables Dim statusbyte As Byte Dim noteUit As Byte ' note off + release value Dim release As Byte Dim noteAan As Byte ' note on + release value Dim velo As Byte Dim notePres As Byte ' note pressure + pressure value Dim pres As Byte Dim Ctrl As Byte ' continuous controller + value Dim value As Byte Dim prog As Byte ' program change + program-byte Dim aft As Byte ' channel aftertouch Dim pblsb As Byte ' pitch bend lsb Dim pbmsb As Byte ' pitch bend msb ' variables for the timing system: Dim time As Dword ' 4 us resolution - must be Dword! Dim otime As Dword Dim maxtim As time.31 ' overflow bit, will cause timer reset after 6 hours Dim i As Word Dim j As Word ' must be word: used in lookups Dim idx As Byte ' index for task-timers Dim Nxt As Dword ' must be dword Dim TimVals[NrTasks] As Dword Dim resort_flag As Bit Dim velflags As Word ' 16 bit variables and floats for the PWM system Dim pw1 As Word ' 16-bit unsigned Dim pw2 As Word Dim pw3 As Word Dim seinvolume As Byte ' for damper movement Dim sollvolume As Byte Dim Int0cnt As Byte ' for tacho Dim t1val As Word ' for tacho Dim freq As Word ' for tacho - in Hz units. - seinpitch Dim SollPitch As Word ' for PID regulator Dim Autoregulation As Byte ' only bit 0 used - steered by ctr.67 Dim PID_Fork As Byte ' for PID regulator lock-range Dim Siren_PWM As Word ' storage for the pwm value set with the received note. ' if no note is playing, this is reset to 0 Dim ctrl48 As Byte ' lsb controller for siren Dim tmp As Word ' for PID regulator: Dim fout As Float ' these variables must be signed! Dim oldfout As Float Dim iterm As Float Dim pterm As Float Dim dterm As Float Dim retval As Float ' for manual speed control via potmeter (or... sensor) Dim sample As Word ' holds the ADC value - 12 bits Dim Potflags As Byte Dim PotTog As Potflags.0 ' for periodicity timer Dim MotorRunning As Potflags.1 Dim Potvalue As Byte ' 7-bit value for spdidx Dim Horn_velo As Byte ' for note repeats ' Lookup tables: Dim Dur2[128] As Word ' durations for tasks (horn) Dim Dur[128] As Word ' chromatic mapping Dim NoteFreq[128] As Word ' lookup for midi note to frequency in Hz Dim NotePWM[85] As Word ' lookup table for midi-note to 16 bit PWM value Variable_Inits: GoSub Dur_Lookup1 GoSub Dur_Lookup2 GoSub Note_Freq_Lookup ' 128 frequencies GoSub Note_PWM_Lookup ' 128 pwm values Clear RCbuffer ' Rx Clear RCIdxIn ' Clear the buffer internal pointer Clear RCIdxOut ' Clear the buffer external pointer Clear time ' Clear TXBuffer ' Tx ' Clear TXIdxIn ' Clear TXIdxOut Set TimVals ' array Clear velflags Low horn Clear MotorRunning Clear pw1 Clear pw2 Clear pw3 Clear PDC1 Clear PDC2 Clear PDC3 seinvolume = 64 sollvolume = 64 Clear SollPitch Clear freq PID_Fork = PID_Fork_Default $ifdef Enable_Timer1 ' This is our coding for : ' 2017: in the 3-phase motorcontroller, this timer ' is used for the sample rate of the motor frequency. (sine wave lookup) ' used as sample frequency generator ' 2018: here used with a fixed sampling rate for updating the PWM output values ' as set with pw1, pw2, pw3 in the code ' used in version Balsi_DC_Motor_000.bas ' No longer used for PWM refreshes now... IPC0bits_T1IP0 = 1 ' set priority - this is very critical! IPC0bits_T1IP1 = 0 ' 4 = 100 , 5 = 101 IPC0bits_T1IP2 = 1 ' 6 = 110 Set TMR1 ' PR1 = Srate ' 18750 geeft 25Hz op de oranje led, dus 50Hz ' 937 should give 1000 Hz ' timing resolution is 1.06 us. ' prescaler: set to 10 for :64 - for balsi motor. ' with a 00 setting (:1) we supper up all machine cycles and the system freezes T1CON.5 = 1 ' set prescaler to :8 = 01 , :64 = 10 , :256 = 11 T1CON.4 = 0 ' prescaler :1 = 00 T1CON.15 = 1 ' start timer IFS0bits_T1IF = 0 ' clear Timer1 interrupt flag IEC0bits_T1IE = 1 ' enable or Disable the Timer1 interrupt $endif $ifdef Enable_Timer23 ' main loop clock ' resolution is 4.224 us T2CONBITs_T32 = 1 ' 32- bit timer start T2CONbits_TCS = 0 ' internal clock T2CONbits_TON = 1 T2CONbits_TSIDL = 0 T2CON.5 = 1 ' pre-scale 256 T2CON.4 = 1 ' id. TMR2 = 0 TMR3HLD = 0 Set PR2 Set PR3 IPC2bits_T3IP0 = 0 '1 ' set priority to 0 IPC2bits_T3IP1 = 0 IPC2bits_T3IP2 = 0 IFS0bits_T3IF = 0 ' clear IRQ flag IEC0bits_T3IE = 1 ' enable timer 3 interrupt $endif $ifdef enable_Timer45 T4CONBITs_T32 = 1 ' 32- bit timer start T4CONbits_TCS = 0 ' internal clock T4CONbits_TON = 1 T4CONbits_TSIDL = 0 T4CON.5 = 1 ' pre-scale 256 T4CON.4 = 1 TMR4 = 0 TMR5 = 0 PR4 = 65535 PR5 = 65535 IPC7bits_T5IP0 = 1 ' set priority to 3 IPC7bits_T5IP1 = 1 IPC7bits_T5IP2 = 0 IFS1bits_T5IF = 0 ' clear IRQ flag IEC1bits_T5IE = 1 ' enable timer 5 interrupt $endif $ifdef Enable_Timer4 ' used for time measurement in external interrupt - tacho T4CON.15 = 1 ' start timer in 16 bit mode T4CON.13 = 0 T4CON.6 = 0 ' prescaler set to 0: timing resolution should be 1/60MHz = 16.6666ns ' 01 = dive by 8 ' 10 = divide by 64 ' prescaler set to 11: divide by 256, so timing resolution becomes 4.266 us T4CON.5 = 1 T4CON.4 = 1 ' ------------------------ T4CON.3 = 0 ' operate as 16 bit timer T4CON.1 = 0 ' internal clock IPC6bits_T4IP0 = 1 ' set priority to 3 IPC6bits_T4IP1 = 1 IPC6bits_T4IP2 = 0 'PR4 = 1000 ' geeft 234 S/s Set PR4 ' longest period = 279 ms ' so lowest frequency would be 3.5 Hz ' highest frequency 234 kHz Clear IFS1.11 ' clear interrupt flag Set IEC1.11 ' enable interrupt $endif $ifdef Enable_Timer5 ' used as sampling clock generator in to read the potmeter ' on the analog input T5CON.15 = 1 ' start timer in 16 bit mode T5CON.13 = 0 T5CON.6 = 0 ' prescaler: T5CON.5 = 1 '0 T5CON.4 = 1 '0 ' ---------------------- T5CON.1 = 0 ' internal clock IPC7bits_T5IP0 = 1 ' set priority to 5 IPC7bits_T5IP1 = 0 IPC7bits_T5IP2 = 1 IFS1bits_T5IF = 0 ' clear IRQ flag 'PR5 = 29300 ' sampling rate should be 2048 S/s 'PR5 = 234 '58500 ' for 1024 S/s - gives 812 S/s measured. PR5 = 2340 ' should give 100 S/s IEC1bits_T5IE = 1 ' enable timer 5 interrupt $endif $ifdef Enable_UART_RX ' init UART1-receiver: 31250 Baud ' generates an interrupt for each byte received U1MODE.0 = 0 ' = U1MODEbits.STSEL = 0 ' 1 stop-bit U1MODE.1 = 0 ' U1MODEbits.PDSEL = 0 ' 8 bit no parity U1MODE.2 = 0 U1MODE.5 = 0 ' =U1MODE_bits.ABAUD = 0 ' no autobauding U1MODE.3 = 0 ' = U1MODEbits.BRGH =0 ' 0 = standard speed mode - 16 clocks per bit U1MODE.4 = 0 ' logic high is the idle state for the input serial data (default) U1MODE.7 = 0 ' wakeup disabled U1MODE.8 = 0 U1MODE.9 = 0 U1BRG = 119 ' 239 ' voor 120MHz 119 ' voor 60MHz ((60000000 / Hserial_baud) / 16) - 1 ' BRGVAL U1STA.5 = 0 ' no bit8 U1STA.6 = 0 ' U1STAbits.URXISEL = 0 ' receive interrupt mode selection bits U1STA.7 = 0 ' interrupt flag is set when a byte is received U1MODE.15 = 1 '=U1MODEbits.UARTEN = 1 U1MODE.12 = 0 ' disable IrDA IPC2.12 = 0 ' set priority level to 6 for receiver IPC2.13 = 1 ' 1 IPC2.14 = 1 ' tested with change to 4, 1 step lower then the note timers...[09.10.2016] ' back to 6 on 10.10.2016, as this was not the cause for the glitches. IEC0.11 = 1 ' enable IRQ for receiver IEC4.1 = 1 ' enable IRQ for error trapping on receiver $endif $ifdef Enable_UART_TX ' UART1 transmitter: 31250 Baud U1STA.15 = 0 ' interrupt generated when character transferred to tx buffer and buffer is empty U1STA.10 = 1 ' transmit enable bit U1STA.11 = 0 ' U1STA.9 = UTXBF Transmit buffer full 1= full, 0 = we can write ' U1STA.8 = TRMT read only bit 1=shift register is empty and transmit buffer is empty ' 0=buffer had data, transfer in progress or queued Clear IFS0.12 ' clear transmit interrupt flag Set IEC0.12 ' enable interrupt IPC3.0 = 0 IPC3.1 = 1 IPC3.2 = 1 ' set priority to 6 $endif $ifdef Enable_ADC10bit ' this should initialise the ADC to 10 bit sampling mode with 4 simultaneous sample and holds ' would be perfect for a quadrada interface with 4 radars. ' AD1CON1: AD1CON1.10 = 0 ' 1 = select 12-bit mode, 1 S&H AD12B bit ' 0 = 10 bit mode with 4 S&H's AD1CON1.13 = 0 ' continue adc in idle mode AD1CON1.9 = 0 ' conversion to right alligned integer 0000 dddd dddd dddd AD1CON1.8 = 0 ' id. AD1CON1.7 = 1'0 '1 ' auto-convert mode - internal counter ends sampling and starts conversion AD1CON1.6 = 1'0 '1 ' id. AD1CON1.5 = 1' '1 ' id ' if 111 the samp bit (ADC1CON1.1) is automaticaly cleared to end sampling and start conversion. ' setting these 3 bits to 0 configures manual mode AD1CON1.4 = 0 ' SSRCG sample trigger source group. Must be 0 'AD1CON1.2 = 1 ' 1 = sampling immediately after last conversion. SAMP bit is autoset. ' this overrides the sampling rate generator... AD1CON1.2 = 0 ' sampling starts when the SAMP bit is set AD1CON1.1 = 0 ' the SAMP bit is AD1CON1.1 ' when AD1CON1.0 is 1, the ADC conversion has completed. ' this causes an interrupt. ' AD1CON2: AD1CON2.15 = 0 ' ref AvDD and AsSS AD1CON2.14 = 0 AD1CON2.13 = 0 AD1CON2.10 = 0 ' 0=no scan ' bits 6-2 = 0 ==> generates interrupt after every sample conversion AD1CON2.6 = 0 AD1CON2.5 = 0 AD1CON2.4 = 0 AD1CON2.3 = 0 AD1CON2.2 = 0 AD1CON2.1 = 0 ' always fill buffer from the start adres AD1CON2.0 = 0 ' use channel input selects for sample MUXA ' AD1CON3: AD1CON3.15 = 0 ' 0 = clock derived from system clock ' 1 = clock derived from internal RC clock AD1CON3.12 = 1 ' 10000 = 16 Tad -autosample time bits AD1CON3.11 = 0 ' / AD1CON3.10 = 0 '/ AD1CON3.9 = 0 ' / AD1CON3.8 = 0 '/ AD1CON3.Byte0 = 16 '127 '16 ' ADCS ADC1 conversion clock select bits (sampling rate) ' 255 gives toggle period = 3.9kHz so sampling rate is 7.8kS/s [measured 18.03.2017] ' 127 should give 7.8kHz and SR = 15.6kS/s ' measured : 7.81285kHz, SR = 15.6257 kS/s ' 63 15.5kHz and SR = 31.2kS/s ' 31 31 kHz and SR = 62.4kS/s ' 15 61 kHz and SR = 124.8kS/s ' 16 gives 58kHz (measured 08.2016) ' in manual mode the sampling rate is controlled by timer5 in our coding ' this setting sets the conversion speed. AD1CON4.8 = 0 ' no DMA - results stored in ADC1BUF0 to ADC1BUFF registers AD1CON4.2 = 0 ' allocate just 1 word for the buffer AD1CON4.1 = 0 ' / AD1CON4.0 = 0 ' / Clear AD1CHS0 ' clear complete register. AN0 is input channel Clear AD1CSSH AD1CSSL.0 = 1 ' select AN0 for input scan ' Set AD1CSSH.9 ' select AN0 for input scan 21.03.2017 added??? Clear AD1CSSL AD1CSSL.0 = 1 ' select AN0 for input scan ANSELA.0 = 1 ' AN0 is analog input - this is crucial!!! ' now we have to enable the interrupt and find out how to transfer data to an array IEC0.13 = 1 ' 1 = enable IRQ ' 0 = disable IRQ 'IPC3 <6:4> set the priority level to 5 IPC3.6 = 1 IPC3.5 = 0 IPC3.4 = 1 AD1CON1.15 = 1 ' adc module ON - done at the end of the configuration $endif $ifdef Enable_ADC12bit ' 30.03.2017: used to sample the radar signal ' 01.04.2017: Sampling rate should be ca. 2048 S/s ' For the radar we try to set automatic sample and manual conversion mode. ' 09.04.2017: For radar we brought the sampling rate down to ca. 840 S/s ' Clearing AD1CON1.1 (SAMP bit) starts the conversion ' 29.08.2017: In the motor controller firmware used to read the value of the speed potmeter ANSELA.0 = 1 ' AN0 is analog input - this is crucial!!! ' AD1CON1: AD1CON1.15 = 0 ' ADC module must be off before changing to 12-bit mode AD1CON1.10 = 1 ' 1 = select 12-bit mode, 1 S&H AD12B bit ' 0 = 10 bit mode with 4 S&H's AD1CON1.13 = 0 ' continue adc in idle mode AD1CON1.9 = 0 ' conversion to right alligned integer 0000 dddd dddd dddd AD1CON1.8 = 0 ' id. ' setting AD1CON1.9 to 1 and AD1CON1.8 to 0 gives fractional format dddd dddd dddd 0000 ' 07.04.2017: set to fractional format for ease on integration ' so now we should have dddd dddd dddd 0000 ' did'nt help us any further, so set back to 0000 dddd dddd dddd ' SSRC bits AD1CON1.7 = 1'0 ' 1'0 '1 ' auto-convert mode - internal counter ends sampling and starts conversion AD1CON1.6 = 1'0 '1'0 '1 ' id. AD1CON1.5 = 1'0 '1' '1 ' id ' if 111 the samp bit (ADC1CON1.1) is automaticaly cleared to end sampling and start conversion. ' setting these 3 bits to 0 configures manual mode AD1CON1.4 = 0 ' SSRCG sample trigger source group. Must be 0 ' ASAM bit: 'AD1CON1.2 = 1 ' sampling immediately after last conversion. SAMP bit is autoset. AD1CON1.2 = 0 ' sampling starts when the SAMP bit is set ' the SAMP bit is AD1CON1.1 ' when AD1CON1.0 is 1, the ADC conversion has completed. ' this causes an interrupt. ' so now AD1CON1 should be &B0100000000000100 ' AD1CON2: AD1CON2.15 = 0 ' ref AvDD and AsSS AD1CON2.14 = 0 AD1CON2.13 = 0 AD1CON2.10 = 0 ' no scan AD1CON2.9 = 0 ' convert CH0 AD1CON2.8 = 0 ' / ' bits 6-2 = 0 ==> generates interrupt after every sample conversion AD1CON2.6 = 0 AD1CON2.5 = 0 AD1CON2.4 = 0 AD1CON2.3 = 0 AD1CON2.2 = 0 AD1CON2.1 = 0 ' always fill buffer from the start adres AD1CON2.0 = 0 ' use channel input selects for sample MUXA ' AD1CON3: ' AD1CON3.15 = 0 ' clock derived from system clock ' AD1CON3.12 = 1 ' 10000 = 16 Tad -autosample time bits must be >3 after datasheet ' AD1CON3.11 = 1 ' / ' AD1CON3.10 = 1 '/ ' AD1CON3.9 = 1 ' / ' AD1CON3.8 = 1 '/ ' the example code in the manual clears AD1CON2.byte1 completely.... Clear AD1CON3.Byte1 Set AD1CON3.Byte0 ' = 255 'AD1CON3.Byte0 = 32 ' 255 '127 '16 ' ADCS ADC1 conversion clock select bits (sampling rate) ' 255 gives toggle period = 3.9kHz so sampling rate is 7.8kS/s [measured 18.03.2017] ' 127 should give 7.8kHz and SR = 15.6kS/s ' measured : 7.81285kHz, SR = 15.6257 kS/s ' 63 15.5kHz and SR = 31.2kS/s ' 31 31 kHz and SR = 62.4kS/s ' 15 61 kHz and SR = 124.8kS/s ' 16 gives 58kHz (measured 08.2016) ' in manual mode the sampling rate is controlled by timer5 in our coding ' this setting sets the conversion speed. ' AD1CON4: 'AD1CON4.8 = 0 ' no DMA - results stored in ADC1BUF0 to ADC1BUFF registers 'AD1CON4.2 = 0 ' allocate just 1 word for the buffer DMABL<2:0> 'AD1CON4.1 = 0 ' / 'AD1CON4.0 = 0 ' / Clear AD1CON4 ' as in the manual for automatic sample and manual conversion Clear AD1CHS0 ' clear complete register. AN0 is input channel for CH0 Clear AD1CHS123 ' not implemented in 12 bit mode Clear AD1CSSH Clear AD1CSSL ' now we have to enable the interrupt and find out how to transfer data to an array IEC0.13 = 1 ' 1 = enable IRQ ' 0 = disable IRQ 'IPC3 <6:4> set the priority level to 5 ' IPC3.6 = 1 ' IPC3.5 = 0 ' IPC3.4 = 1 IFS0.13 = 0 ' clear interrupt flag AD1CON1.15 = 1 ' adc module ON $endif $ifdef Enable_Int0 ' can be used to measure the pitch of the siren using the on board tacho chip ' 08.12.2018:two irq's determine a single period of the input signal. ' we trigger on a positive going edge here. ' IFS0.0 = INT0IF bit interrupt flag ' IEC0.0 = INT0IE bit interrupt enable flag ' IPC0.0, IPC0.1, IPC0.2 interrupt priority bits, INT0IP bits IPC0.0 = 0 ' priority level set to 4 IPC0.1 = 0 IPC0.2 = 1 Set IEC0.0 ' 0 = disable interrupt on start-up ' 1 = enable interrupt on start-up ' we leave this interrupt enabled all the time Clear IFS0.0 ' clear irq flag INTCON2.0 = 0 ' 0 = irq on positive edge INT0EP bit ' 1 = irq on negative edge 'INTTREG.Byte0 = %00001000 ' do we need this? $endif $ifdef Enable_PWM ' PWM channel initialisation ------------------------------------------------------ ' CNPD1 ' enable internal pull down resistor to reset the FLT32 flag on init ' disable interrupts: IEC5.14 = 0 IEC5.15 = 0 IEC6.0 = 0 ' clear interrupt flags: Clear IFS5.14 Clear IFS5.15 Clear IFS6.0 ' pwm time base control register: PTCON 'PTCON.pwm Clear PTCONbits_PTEN ' = PTCON.15 = 0 ' pwmx disabled for setting the bits, should be enabled at the end. PTEN 'PTCON Timebase control register Clear PTCONbits_PTSIDL ' = PTCON.13 ' PTCONbits_SESTAT 'PTCON.12 ' status bit to read Clear PTCONbits_SEIEN ' PTCON.11 disable special event interrupt 'Set PTCONbits_EIPU ' PTCON.10 1 = active period register is updated immediately Clear PTCONbits_EIPU ' PTCON.10 0 = active period register updates occur on PWMx cycle boundaries : setting changed 09.10.2016 Clear PTCONbits_SYNCPOL ' PTCON.9 sync active high. Irrelevant here as we do not use sync Clear PTCONbits_SYNCOEN ' PTCON.8 = 0 ' disable sync output Clear PTCONbits_SYNCEN ' PTCON.7 = 0 ' no external sync Clear PTCONbits_SYNCSRC0 'PTCON.4 Clear PTCONbits_SYNCSRC1 'PTCON.5 Clear PTCONbits_SYNCSRC2 'PTCON.6 Clear PTCONbits_SEVTPS0 'PTCON.0 special event postscaler bits Clear PTCONbits_SEVTPS1 'PTCON.1 Clear PTCONbits_SEVTPS2 'PTCON.2 Clear PTCONbits_SEVTPS3 'PTCON.3 '%110 = / 64 ' timing resolution should become 64x8.5ns = 544ns = 0.544us ' PTPER register primary master time base period register 'PTPER = 64 '32678 ' master time base period register --> PWM frequency 16-bits ' 1 unit is 8.33ns with a 60MHz Fcy ' so 32678 would give 3673 Hz ' 65536 would give 1836 Hz ' this works o.k. now 'PWMCON1.9 = 0 ' ITB: Independent time base mode if set to 1 'PWMCON2.9 = 0 ' PTPER register steers time base if set to 0 'PWMCON3.9 = 0 ' we do not have a SPHASE1, 2, 3 register thus independent pwm control is not possible ' cfr manual example 14-14, p.44 Clear PWMCON1 Clear PWMCON2 Clear PWMCON3 'PTPER = 256 ' 8 bit resolution for PWM values - version Balsi_000 'Set PTPER ' 16 bit resolution ' if we make PTPER = 16383, we have 14 bit resolution and fpwm = 7324.64 Hz - verified. PTPER = 16383 ' 2^14 -1 'PTPER = 1024 ' 256 '1024 ' would this limit the useable range for PWM values??? ' 1024 gives a pwm base frequency of 117kHz 'PTPER = 4096 ' it gives 29kHz base freq ' with this setting we have 12-bit resolution, matching the ADC resolution 'Set PTPER ' set to 16-bit resolution = 1831.16 Hz = lowest freq. ' no divider, maximum timing resolution, if we clear all bits: Clear PTCON2bits_PCLKDIV0 ' PTCON2.0 ' bit0 Clear PTCON2bits_PCLKDIV1 ' PTCON2.1 ' bit1 Clear PTCON2bits_PCLKDIV2 ' PTCON2.2 ' bit2 ' with PTPER set to 65535 this gives 1831 Hz as base frequency ' try halving the PWM frequency once more: (:8) ' we tried this to keep temperature on mosfets lower. the base frequency for the PWM ' should become 58.36kHz - measured o.k. 30.08.2017 ' set PTCON2bits_PCLKDIV0 ' PTCON2.0 ' bit0 ' Set PTCON2bits_PCLKDIV1 ' PTCON2.1 ' bit1 ' should be divide by 8 ' Clear PTCON2bits_PCLKDIV2 ' PTCON2.2 ' bit2 ' we can also go down to 29KHz by dividing by 16: ' measurement 31.08.2017: 29183Hz with PTPER set to 256 ' Clear PTCON2bits_PCLKDIV0 ' PTCON2.0 ' bit0 ' Clear PTCON2bits_PCLKDIV1 ' PTCON2.1 ' bit1 ' should be divide by 16 ' Set PTCON2bits_PCLKDIV2 ' PTCON2.2 ' bit2 ' as all mosfet's burned out at this frequency, let get further down with the frequency: ' this should give 7296 Hz, the lowest possible setting for 8 bit resolution ' measured 07.09.2017: 7600 Hz ' CleaR PTCON2BITS_PCLKDIV0 ' BIT 0 ' SET PTCON2bits_PCLKDIV1 ' PTCON2.1 ' bit1 ' should be divide by 64 ' Set PTCON2bits_PCLKDIV2 ' PTCON2.2 ' bit2 PDC1 = 0 '32768 '127 'Clear PDC1 PDC2 = 0 '32768 '127 'Clear PDC2 PDC3 = 0 '32768 '127 'Clear PDC3 Clear PHASE1 Clear PHASE2 Clear PHASE3 ' Clear PHASE1 ' 0 graden ' PHASE2 = 85 '21845 ' 120 graden ' PHASE3 = 171 '43690 ' 240 graden Set MDC ' set to longest period irrelevant here. 'MDC = 256 ' seems to make no difference ' could we use it for master duty cycle control? --> NO ' The IOCON1, IOCON2, IOCON3 registers are protected and locked with a key. IOCON1.15 = 1 ' pwm module controls the PWM1H pin - pin 25 IOCON1.14 = 1 ' pwm module controls the PWM1L pin - pin 26 ' polarity bits: IOCON1.13 = 0 ' 1 - pwmH pin is active low '0 = pwmH pin is active high IOCON1.12 = 0 ' 1 = pwmL pin is active low '0 = pwmL pin is active high - radar FM ' set outputs: IOCON1.11 = 0 ' 10 = push pull mode, 01 = redundant mode, 00 = complimentary IOCON1.10 = 1 ' we used redundant mode here, for measurement purposes in Balsi IOCON1.9 = 0 ' no override IOCON1.8 = 0 'id. IOCON1.1 = 0 ' 1= swap pins, 0 = no pin swaps ' IOCON1 = %1100 0000 0000 0000 = &HC000 %1100 0100 0000 0010 = &HC402 ' IOCON1 = %1100 0100 0000 0000 = &HC400 for Balsi IOCON2.15 = 1 IOCON2.14 = 1 IOCON2.13 = 0 IOCON2.12 = 0 IOCON2.11 = 0 IOCON2.10 = 1 ' redundant mode IOCON2.9 = 0 IOCON2.8 = 0 ' IOCON2 = %1100 0000 0000 0000 = &HC000 ' &HC400 for Balsi IOCON3.15 = 1 IOCON3.14 = 1 IOCON3.13 = 0 IOCON3.12 = 1 IOCON3.11 = 0 IOCON3.10 = 1 ' redundant mode ' IOCON3 = IOCON2 = &HC800 ' the FCLCON1,2,3 and IOCON1,2,3 require a key to be written: ' pwm channel 1: -------------------------------------------- Mov #0xabcd, w10 ' etc... p. 226 pwm manual Mov #0x4321, w11 Mov #0xC003, w0 ' required value for FCLCON1 ' %1100000000000000 = &HC000 Mov w10, PWMKEY Mov w11, PWMKEY Mov w0, FCLCON1 ' set pwm ownership and polarity to IOCON1 ' IOCON1 = 0xC000 sets pwm mode to complementary outputs Mov #0xabcd, w10 ' etc... p. 226 pwm manual Mov #0x4321, w11 Mov #0xC400, w0 ' Mov #0xC402, w0 ' required value for IOCON1 Mov w10, PWMKEY Mov w11, PWMKEY Mov w0, IOCON1 ' pwm channel 2: -------------------------------------------- Mov #0xabcd, w10 ' etc... p. 226 pwm manual Mov #0x4321, w11 Mov #0xC003, w0 ' required value for FCLCON2 ' %1100000000000000 = &HC000 Mov w10, PWMKEY Mov w11, PWMKEY Mov w0, FCLCON2 ' set pwm ownership and polarity to IOCON2 Mov #0xabcd, w10 ' etc... p. 226 pwm manual Mov #0x4321, w11 Mov #0xC400, w0 'Mov #0xC800, w0 ' required value for IOCON2 - redundant Mov w10, PWMKEY Mov w11, PWMKEY Mov w0, IOCON2 ' pwm channel 3: ----------------------------------------------- Mov #0xabcd, w10 ' etc... p. 226 pwm manual Mov #0x4321, w11 Mov #0xC003, w0 ' required value for FCLCON3 ' %1100000000000000 = &HC000 Mov w10, PWMKEY Mov w11, PWMKEY Mov w0, FCLCON3 ' set pwm ownership and polarity to IOCON1 Mov #0xabcd, w10 ' etc... p. 226 pwm manual Mov #0x4321, w11 Mov #0xC400, w0 ' Mov #0xC800, w0 ' required value for IOCON3 Mov w10, PWMKEY Mov w11, PWMKEY Mov w0, IOCON3 ' common settings: PWMCON1.12 = 0 'fault interrupt disabled PWMCON2.12 = 0 PWMCON3.12 = 0 PWMCON1.11 = 0 'current limit disabled PWMCON2.11 = 0 PWMCON3.11 = 0 PWMCON1.10 = 0 'trigger event interrupts disabled PWMCON2.10 = 0 PWMCON3.10 = 0 ' PWMCON1.9 = 1 ' ITB: Independent time base mode if set to 1 ' PWMCON2.9 = 1 ' PTPER register steers time base if set to 0 ' PWMCON3.9 = 1 ' we do not have a SPHASE1, 2, 3 register thus independent pwm control is not possible ' PWMCON1.8 = 0 'PCD1 en SDC1 registers provide duty cycle info - ' PWMCON2.8 = 0 ' PWMCON3.8 = 0 PWMCON1.7 = 1 ' dead time disabled PWMCON2.7 = 1 ' dead time is guaranteed by the specs of the IR2104 driver chips. PWMCON3.7 = 1 ' if we use all 6-outputs to drive transformers or optocouplers, we need to implement a dead time here! ' PWMCON1.6 = 0 ' PWMCON2.6 = 0 ' PWMCON3.6 = 0 ' PWMCON1.3 = 0 ' PWMCON2.3 = 0 ' PWMCON3.3 = 0 ' PWMCON1.2 = 0 ' edge aligned mode CAM bit. Ignored when ITB = 0 ' PWMCON2.2 = 0 ' PWMCON3.2 = 0 ' PWMCON1.1 = 0 ' PWMCON2.1 = 0 ' PWMCON3.1 = 0 ' PWMCON1.0 = 1 ' PWMCON2.0 = 1 ' PWMCON3.0 = 1 ' The pwm1 to pwm3 values for the duty cycle are in the PDC1, PDC2 and PDC3 registers ' The phases in the PHASE1, PHASE2, PHASE3 registers, if not used for frequency. AUXCON1 = 0 AUXCON2 = 0 AUXCON3 = 0 Set PTCONbits_PTEN ' = PTCON.15 pwmx should be enabled at the end. $endif GoTo MAIN ' jump over interrupt handlers Interrupt_handling: Isr- T1Interrupt ' timer1 interrupt ' pwm refresher task ' 07.12.2018: Now we can use a fixed sampling rate ' This is the rate at which we refresh the PWM outputs. ' 09.12.2018: Changed: no longer used for PWM refresh Clear IFS0bits_T1IF ' Reset the Timer1 interrupt flag Clear TMR1 ' this is required! ' PR1 = Srate ' here we use a fixed rate. 18750 ' ' changed to full 16-bits pwm 10.09.2017 ' PDC1 = pw1 ' set the values for the PWM output channels ' PDC2 = pw2 ' pw1, pw2, pw3 must be unsigned word variables ' PDC3 = pw3 ' Toggle Orange_led ' = Scnt.0 ' speed measurement debug --> 09.12.2018: 250 Hz ' so refresh rate must be 500 S/s EndIsr- Isr- T3Interrupt Clear IFS0bits_T3IF ' 32-bit value reached (T2/T3 combined) ' this is our main timer! ' this interrupt should never happen... EndIsr- Isr- T4Interrupt ' used for frequency measurement with the external interrupt Clear IFS1bits_T4IF ' 16-bit value reached , clear interrupt flag ' Clear IEC1bits_T4IE ' disable interrupt Clear TMR4 EndIsr- Isr- T5Interrupt ' used as sampling rate clock source for the ADC ' 09.04.2017: Prescaler set to :256 ' 2018: only used for reading the potmeter. Of course another analog ' signal from a sensor could be connected here... ' sampling rate is 100 S/s Clear IFS1bits_T5IF ' 32-bit value reached if T4/T5 operation ib 32 bits ' 16-bit value reached if operated alone. Clear TMR5 ' Clear AD1CON1.1 ' start conversion of the automatically acquired sample ' shouldn't we set AD1CON1.1 instead??? Set AD1CON1.1 'Toggle yellow_led ' for debug, measurement of sampling rate ' 31.08.2017: 50Hz, so 100 S/s ' 08.12.2018: confirmed as 100 S/s EndIsr- Isr- U1RXInterrupt ' UART receive IRQ - midi receiver Clear IFS0.11 ' reset UART1 receive irq flag Inc RCIdxIn ' Move up the buffer index (0-255) ' if we make the buffer larger, we need to AND ' the index with the size of the buffer -1. ' RCIdxIn = (RCIdxIn + 1) & RCBufSize RCbuffer[RCIdxIn] = U1RXREG EndIsr- Isr- U1ErrInterrupt ' this irq is generated on UART1 receive errors ' this interrupt is enabled by setting IEC4.1 ' error handling: Clear IFS4.1 ' clear interrupt flag If U1STA.2 = 1 Then 'Set framing_error_led ' no longer happening EndIf If U1STA.1 = 1 Then ' = overrun error 'Set overrun_error_led ' not happening Clear U1STA.1 EndIf EndIsr- Isr- U1TXInterrupt ' UART send IRQ Clear IFS0bits_U1TXIF ' reset UART1 transmit IRQ flag EndIsr- Isr- AD1Interrupt ' this irq happens after every ADC conversion ' the sampling rate is controlled by timer 5 ' use of floats here crashes the compiler. ' even using a multiply seems impossible ' 31.08.2017: sampling rate down to 100 S/s sample = ADC1BUF0 ' 12-bit value format 0000 xxxx xxxx xxxx Clear IFS0.13 ' clear AD1 interrupt flag Clear AD1CON1.0 ' clear the conversion-done flag ' Toggle Yellow_led ' for debugging - gives 50Hz, so 100 S/s EndIsr- Isr- PWM1Interrupt Clear IFS5.14 EndIsr- Isr- PWM2Interrupt Clear IFS5.15 EndIsr Isr- PWM3Interrupt Clear IFS6.0 EndIsr- Isr- Int0Interrupt ' handler for external interrupt, from tacho ' here we measure the time between two non-successive rising edges Clear IFS0.0 ' clear interrupt flag Inc Int0cnt If Int0cnt.0 = 1 Then ' so we calculate only on every second interrupt ' as the tacho is frequency doubling... t1val = TMR4 ' this is the period now, in 4.266 us units ' t1val = t1val * 4.266 ' must be float... ' freq = 1000000 / t1val ' to be protected against too small values for t1val Select t1val Case < 200 ' higher than note 86 freq =0 ' invalid reading, freq higher than possible ' Case > 5860 ' lower than note 29 ' freq =0 ' invalid reading, freq lower than possible Case Else freq = 234411 / t1val ' 234411 = 1000000 / 4.266 - avoiding floats ' freq is now in Hz units, real world. ' range: 3.57Hz to 1178 Hz ' 46882 Hz. 5860Hz EndSelect Clear TMR4 Else Toggle Yellow_led ' for debug monitoring so this frequency will be that of the input signal. EndIf EndIsr- GetMidi: If RCIdxIn <> RCIdxOut Then 'RCIdxOut = (RCIdxOut + 1) & RCBufSize Inc RCIdxOut ' RCIdxOut = RCIdxOut & RCBufSize Bytein = RCbuffer[RCIdxOut] Else Set Bytein EndIf Return 'Proc mPlay (stat_byte As Byte, not_byte As Byte, vel_byte As Byte) ' ' can be used for all 3-byte midi messages ' TXIdxIn = (TXIdxIn + 1) & TXBufSize ' overflow at 1023 ' TXBuffer[TXIdxIn] = stat_byte ' TXIdxIn = (TXIdxIn + 1) & TXBufSize ' TXBuffer[TXIdxIn] = not_byte ' TXIdxIn = (TXIdxIn + 1) & TXBufSize ' TXBuffer[TXIdxIn] = vel_byte 'EndProc 'Proc mOut (stat_byte As Byte, dat_byte As Byte) ' ' can be used for all 2-byte midi messages ' TXIdxIn = (TXIdxIn + 1) & TXBufSize ' overflow at 1023 ' TXBuffer[TXIdxIn] = stat_byte ' TXIdxIn = (TXIdxIn + 1) & TXBufSize ' TXBuffer[TXIdxIn] = dat_byte 'EndProc MAIN: 'Set T1CON.15 ' start timer 1 While ' timer23 version: ' overflows after 5 hours. time.Word0 = TMR2 ' time is a dword var time.Word1 = TMR3HLD ' resolution is 4.224 us $ifdef Enable_UART_RX ' if receiver is enabled GoSub GetMidi ' Read data from the serial buffer ' Start the midi parser. ' bytes can come in at a maximum rate of 300us each 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 EndIf GoTo Midi_Parse_done '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 Set velo Case Keypres_Status statusbyte = Bytein Set notePres Set pres Case Control_Status ' controllers and switches statusbyte = Bytein Set Ctrl Set value Case ProgChange_Status ' tuning and piece selection statusbyte = Bytein Set prog Case Aftertouch_Status ' used by Radar 1 to send messages to Radar 2 statusbyte = Bytein Set aft Case Pitchbend_Status statusbyte = Bytein Set pblsb Set pbmsb EndSelect Else 'midi byte is 7 bits Select statusbyte Case 0 'not a message for this channel GoTo Midi_Parse_done 'disregard Case NoteOff_Status If noteUit = 255 Then noteUit = Bytein Else release = Bytein 'message complete, so we can do the action... Select noteUit Case Horn_note 'claxon Clear horn Clear velflags.2 Set TimVals[2] Set resort_flag Case SirLo To SirHi Clear pw1 ' motor off Clear PDC1 Clear Siren_PWM ' stop tacho interrupt - no reason to stop it. 'Clear IEC0.0 ' disable tacho interrupt 'Clear Yellow_led ' monitor for debug Set TimVals[0] ' PID OFF. Clear velflags.0 ' stop, with ramping on the damper sollvolume = release If seinvolume <> sollvolume Then TimVals[1] = time + RampUp_time Else Set TimVals[1] EndIf Set resort_flag EndSelect Set noteUit 'reset EndIf Case NoteOn_Status If noteAan = 255 Then noteAan = Bytein Else velo = Bytein If velo = 0 Then ' note off via velo=0 Select noteAan Case Horn_note Clear horn Clear velflags.2 Set TimVals[2] Set resort_flag Case SirLo To SirHi Clear pw1 Clear PDC1 Clear Siren_PWM 'Clear IEC0.0 ' stop tacho interrupt 'Clear Yellow_led ' monitor for debug Set TimVals[0] ' stop PID regulation Clear velflags.0 sollvolume = 64 If seinvolume <> sollvolume Then TimVals[1] = time + RampUp_time Else Set TimVals[1] EndIf Set resort_flag EndSelect Else Select noteAan Case Horn_note Horn_velo = velo If velo < 127 Then Set horn Set velflags.2 TimVals[2] = time + Dur2[velo] Else Set horn Clear velflags.2 Set TimVals[2] EndIf Set resort_flag Case SirLo To SirHi ' here we will need a good lookup for pwm value and pitch ' to be done: implement microtones. - done, using ctrl 48 Siren_PWM = NotePWM[noteAan] pw1 = NotePWM[noteAan] ' lookup - now 14 bits ' lsb values are added in the controller procedure ' this would contradict the function of the PID... ' so if the PID is on, ctrl 48 should be disabled. PDC1 = pw1 SollPitch = NoteFreq[noteAan] ' for PID regulator If Autoregulation = 1 Then ' ctrl. 67 TimVals[0] = time + PID_time Set velflags.0 ' activate the tacho interrupt for frequency measurement ' Set IEC0.0 ' enable IRQ - should be permanent ON ' Clear freq ' reset Clear IFS0.0 ' clear flag Clear TMR4 ' reset timer Else Set TimVals[0] ' PID task off Clear velflags.0 EndIf ' the velo-byte should steer the damper solenoid sollvolume = velo If seinvolume <> sollvolume Then TimVals[1] = time + RampUp_time Set velflags.1 Else Set TimVals[1] Clear velflags.1 EndIf Set resort_flag EndSelect EndIf Set noteAan EndIf Case Keypres_Status If notePres = 255 Then notePres = Bytein Else pres = Bytein Select notePres Case Horn_note Horn_velo = pres 'Case SirLo To SirHi ' ' ??? EndSelect Set notePres EndIf Case Control_Status 'this is where the action takes place for controllers If Ctrl = 255 Then Ctrl = Bytein Else value = Bytein GoSub Controller Set Ctrl EndIf Case ProgChange_Status If prog = 255 Then 'single byte message prog = Bytein Set prog EndIf Case Aftertouch_Status ' could be used for the second siren... If aft = 255 Then aft = Bytein Set aft EndIf Case Pitchbend_Status If pblsb = 255 Then pblsb = Bytein Else pbmsb = Bytein Set pblsb EndIf EndSelect EndIf $endif 'Potmeter_Control: ' procedure for manual potmeter-motor control: ' procedure to read the potmeter and set siren speed according to it. ' This should only work on the condition that no midi note is received for ' motor control. ' Hence, it should also work when CC66 is OFF. ' maybe it would be best to let it work only when CC66 is OFF. ' If Green_led = 0 Then ' If time.15 <> PotTog Then ' PotTog = time.15 ' flipflop ' Potvalue = sample >> 5 ' reduce the ADC 12 bit value to 7 bits ' ' potvalue must be byte ' ' here we can add an integrator. ' If Potvalue > 0 Then ' ' run motor ' If MotorRunning = 0 Then ' pw1 = sample << 4 ' make 16 bit ' PDC1 = pw1 ' Set MotorRunning ' flag ' Else ' pw1 = sample << 4 ' PDC1 = pw1 ' EndIf ' Else ' ' stop motor without braking ' ' we could add code for braking as well. ' ' Clear T1CON.15 ' stop timer ' pw1 = 0 ' Clear PDC1 ' Clear MotorRunning ' EndIf ' EndIf ' EndIf Midi_Parse_done: If resort_flag = 1 Then GoSub SortTimers EndIf Check_Timers: 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 ' here we need the measurement of the produced pitch from the tacho sensor ' here we could have a PID regulator to match the measured frequency in freq with the soll-frequency ' conditions: motor must be running ' tacho interrupt must be enabled. ' autoregulation must be on ' If IEC0.0 = 1 Then ' the tacho interrupt must be ON If Autoregulation = 1 Then If freq > 40 Then '8 then ' better place freq in the neighourhood of SollPitch... Select freq Case < SollPitch ' NoteFreq[noteAan - 1] If pw1 < NotePWM[noteAan + PID_Fork] Then Inc pw1 PDC1 = pw1 EndIf Case > SollPitch ' NoteFreq[noteAan + 1] If pw1 > NotePWM[noteAan - PID_Fork] Then Dec pw1 PDC1 = pw1 EndIf Case SollPitch ' no change - keep pw1 ' maybe we should do nothing here, and keep the regulation running ' Set Timvals[0] ' Clear velflags.0 EndSelect EndIf ' we let this task running as long as the note is sounding... Else Set TimVals.0 Clear velflags.0 EndIf 'EndIf Case 1 ' damper ramping Select seinvolume ' 7 bit value Case sollvolume ' goal reached ' stop ramping Set TimVals[1] ' cancel timer Clear velflags.1 Case < sollvolume ' increase pw2 Inc seinvolume If seinvolume > 63 Then ' for 16 bits: pw2 = (seinvolume - 64) << 10 ' for 14-bit: pw2 = (seinvolume - 64) << 8 PDC2 = pw2 Clear pw3 Clear PDC3 Else 'pw3 = (63 - value) << 10 ' was bug! pw3 = (63 - seinvolume) << 8 PDC3 = pw3 Clear pw2 Clear PDC2 EndIf TimVals[1] = time + RampUp_time Case > sollvolume Dec seinvolume If seinvolume > 63 Then pw2 = (seinvolume - 64) << 8 '10 PDC2 = pw2 Clear pw3 Clear PDC3 Else pw3 = (63 - seinvolume) << 8 '10 PDC3 = pw3 Clear pw2 Clear PDC2 EndIf TimVals[1] = time + RampDown_time EndSelect Case 2 ' horn-repeats If velflags.2 = 0 Then Set TimVals[2] Clear horn Else Toggle horn TimVals[2] = time + Dur2[Horn_velo] EndIf 'Case Else ' ' in dit geval is idx geset ' GoTo jumpout EndSelect GoSub SortTimers ' find a new nxt and idx EndIf ' beveiliging tegen overflow crashes... ' we never saw this happening. If maxtim = 1 Then Clear TMR2 Clear TMR3HLD Clear time Set TimVals ' reboot: GoTo MAIN EndIf Else ' no timers running, so to avoid overflows, we can safely reset the loop timer ' not sure whether this can ever happen with this code, as we always have timers active. ' verified: it does happen! (06.05.2017) If maxtim = 1 Then Clear TMR2 Clear TMR3HLD ' this resets maxtim EndIf EndIf $ifdef Enable_UART_TX Midi_OutPut: ' the internal buffer is only 4 bytes large! ' should we check for U1STA.9 to be 0? If this bit is 1, the buffer is full. If TXIdxOut <> TXIdxIn Then ' in dit geval is er een byte te versturen If IFS0bits_U1TXIF = 0 Then ' interrupt flag is reset ' added 06.05.2017: - this should make sequencing midi out unneeded. If U1STA.8 = 1 Then ' checking U1STA.9 =0 does not work TXIdxOut = (TXIdxOut + 1) & TXBufSize ' circular buffer U1TXREG = TXBuffer[TXIdxOut] EndIf EndIf EndIf $endif ' Toggle Orange_led ' for loopspeed measurement: 434kHz 29.08.2017 - at no load ' ca. 1us per cycle ' drops to 363kHz at full load watchdog_led = time.17 ' this is the red LED on each PIC. Wend '-------------------------- end of the main program loop SortTimers: 'look up the next smallest timer value in the Timvals array ' zoek de de volgende kleinste timer waarde: Set idx ' makes it 255 Set Nxt.31 ' nxt is set on entry. - for speed, we just set the highest bit Clear i Repeat If TimVals[i] < Nxt Then Nxt = TimVals[i] idx = i EndIf Inc i Until i = NrTasks Clear resort_flag Return PID: ' needs to be checked in a simulator in PBCC to find out possible constants. ' (BYVAL sollvalue AS SINGLE, BYVAL seinvalue AS SINGLE, BYVAL OPT kp AS SINGLE, BYVAL OPT ki AS SINGLE, BYVAL OPT kd AS SINGLE) EXPORT AS SINGLE ' ' The machine constants have to be passed on the first call only. ' ' Seinvalue is the measured reality value, generaly derived from a sample. ' ' Sollvalue is the goal we want to achieve. The function returns the correction ' ' factor for regulation and should be used in a regulation loop. ' ' STATIC oldfout, iterm AS SINGLE ' ' LOCAL fout, pterm, dterm AS SINGLE fout = SollPitch - freq ' sollvalue - seinvalue ' calculate the error pterm = kp * fout ' Proportionality term iterm = iterm + (ki * fout) ' Integration term dterm = kd * (fout - oldfout) oldfout = fout retval = pterm + iterm + dterm ' return value for the PID correction signal (factor) ' retval should be 1 when there is no fault anymore. Return ' ************************************************************************************** ' procedures for midi receiver: Controller: Select Ctrl Case 7 ' 07.12.2018 ' volume controller used for steering the damper on the siren. ' this is a birectional Laukhuff solenoid ' the value should steer the force of the solenoid. ' for gentle movement, we need to implement ramping here. ' an alternative would be to use the velo byte for the siren to steer the ' solenoid. ' 08.12.2018 - voorlopig ' with gradual movement: sollvolume = value If seinvolume <> sollvolume Then TimVals[1] = time + RampUp_time Set resort_flag Else Set TimVals[1] EndIf Case 48 ' this controller sets the lsb for the pitch of the large siren. ' this extends to control precision to 14 bits. ctrl48 = value If Autoregulation = 0 Then ' PID regulation contridicts manual fine control If Siren_PWM > 0 Then ' siren must be running ' algo for 16 bits: 'tmp = ctrl48 << 2 'pw1 = Siren_PWM + tmp ' algo for 14 bits: pw1 = Siren_PWM + ctrl48 PDC1 = pw1 EndIf EndIf Case 66 'on/off for the robot '11.09.2017: we now leave timer1 running at all times If value = 0 Then ' power down - should reset all controllers Set TimVals[0] Clear velflags.0 Set resort_flag Low Green_led pw1 = 0 Clear PDC1 sollvolume = 64 ' reset controller 7 If seinvolume <> sollvolume Then TimVals[1] = time + RampUp_time ' 3008 'Set resort_flag Else Set TimVals[1] EndIf Low horn Clear SollPitch Clear Autoregulation ' ctrl 67 reset Clear freq PID_Fork = PID_Fork_Default Clear ctrl48 Clear Siren_PWM Else High Green_led pw1 = 0 ' if we dont do this, the siren continues with he pitch set by the potmeter. Clear SollPitch Set TimVals[1] Clear freq Clear PDC1 Set resort_flag Clear Siren_PWM EndIf Case 67 ' set PID autoregulation ON of OFF If value > 0 Then Set Autoregulation.0 Else ' leave PDC1 were it is, or should we set it to NotePWM if Note is on? Clear Autoregulation.0 Set TimVals[0] Clear velflags.0 Set resort_flag EndIf Case 68 ' set PID_Fork, to steer the lock-in range for the PID regulator ' the default settint is 1 = PID_Fork_Default = 1 PID_Fork = value If PID_Fork > 12 Then PID_Fork = 12 ' limit range Case 123 Clear horn pw1 = 0 Clear PDC1 sollvolume = 64 If seinvolume <> sollvolume Then TimVals[1] = time + RampUp_time ' 3008 Set resort_flag Else Set TimVals[1] EndIf Set TimVals[0] Clear velflags.0 Clear Siren_PWM Clear ctrl48 EndSelect Set Ctrl 'mandatory reset Return Dur_Lookup1: ' in chromatic steps. Changed 17.04.2017 Set Dur[0] ' = 65536 = 0.276 " Dur[1] = 61857 Dur[2] = 58385 Dur[3] = 55108 Dur[4] = 52015 Dur[5] = 49095 Dur[6] = 46339 Dur[7] = 43738 Dur[8] = 41283 Dur[9] = 38966 Dur[10] = 36779 Dur[11] = 34714 For i = 12 To 23 Dur[i] = Dur[i-12] >> 1 ' 32767 ... Next i For i = 24 To 35 Dur[i] = Dur[i-24] >> 2 ' 16383 ... Next i For i = 36 To 47 Dur[i] = Dur[i-36] >> 3 ' 8192 ... Next i For i = 48 To 59 Dur[i] = Dur[i-48] >> 4 ' 4095 ... this should be the limit. Next i For i = 60 To 71 Dur[i] = Dur[i-60] >> 5 ' 2047 ... Next i For i = 72 To 83 Dur[i] = Dur[i-72] >> 6 ' 1023 ... Next i ' higher values are absurd here. ' we keep the old values just as placeholders. Dur[84]= 6048 ' freq= 8.26719576719577 Dur[85]= 5951 ' freq= 8.40194925222652 Dur[86]= 5855 ' freq= 8.5397096498719 Dur[87]= 5760 ' freq= 8.68055555555555 Dur[88]= 5668 ' freq= 8.82145377558222 Dur[89]= 5576 ' freq= 8.96700143472023 Dur[90]= 5486 ' freq= 9.11410864017499 Dur[91]= 5398 ' freq= 9.26268988514264 Dur[92]= 5311 ' freq= 9.41442289587648 Dur[93]= 5225 ' freq= 9.56937799043062 Dur[94]= 5141 ' freq= 9.72573429293912 Dur[95]= 5058 ' freq= 9.88533017002768 Dur[96]= 4977 ' freq= 10.0462125778581 Dur[97]= 4897 ' freq= 10.2103328568511 Dur[98]= 4818 ' freq= 10.3777501037775 Dur[99]= 4740 ' freq= 10.548523206751 Dur[100]= 4664 ' freq= 10.7204116638079 Dur[101]= 4589 ' freq= 10.8956199607758 Dur[102]= 4515 ' freq= 11.0741971207087 Dur[103]= 4442 ' freq= 11.2561909049977 Dur[104]= 4370 ' freq= 11.441647597254 Dur[105]= 4300 ' freq= 11.6279069767442 Dur[106]= 4231 ' freq= 11.8175372252422 Dur[107]= 4162 ' freq= 12.013455069678 Dur[108]= 4095 ' freq= 12.2100122100122 Dur[109]= 4029 ' freq= 12.4100273020601 Dur[110]= 3964 ' freq= 12.6135216952573 Dur[111]= 3901 ' freq= 12.8172263522174 Dur[112]= 3838 ' freq= 13.0276185513288 Dur[113]= 3776 ' freq= 13.2415254237288 Dur[114]= 3715 ' freq= 13.4589502018842 Dur[115]= 3655 ' freq= 13.6798905608755 Dur[116]= 3596 ' freq= 13.9043381535039 Dur[117]= 3538 ' freq= 14.1322781232335 Dur[118]= 3481 ' freq= 14.3636885952312 Dur[119]= 3425 ' freq= 14.5985401459854 Dur[120]= 3370 ' freq= 14.8367952522255 Dur[121]= 3316 ' freq= 15.0784077201448 Dur[122]= 3262 ' freq= 15.3280196198651 Dur[123]= 3210 ' freq= 15.5763239875389 Dur[124]= 3158 ' freq= 15.8328055731476 Dur[125]= 3107 ' freq= 16.0926939169617 Dur[126]= 3057 ' freq= 16.3559044815178 = 38.3365Hz 07.08.2016 Dur[127]= 3008 ' 04.08.2016: 8.35ms - 59.5Hz Return Dur_Lookup2: 'using timer23, 1 dur unit is 4.224 us Set Dur2[0] Dur2[1]= 23674 ' 07.08.2016: 2x100ms = 200ms full period --> 5Hz [o.k.14.08.2016] Dur2[2]= 22917 ' freq= 2.18178644674259 Dur2[3]= 22548 ' freq= 2.21749157353202 Dur2[4]= 22185 ' freq= 2.25377507324769 Dur2[5]= 21827 ' freq= 2.29074082558299 Dur2[6]= 21475 ' freq= 2.32828870779977 Dur2[7]= 21129 ' freq= 2.36641582658905 Dur2[8]= 20789 ' freq= 2.40511809129828 Dur2[9]= 20454 ' freq= 2.44450963136795 Dur2[10]= 20124 ' freq= 2.48459550785132 Dur2[11]= 19800 ' freq= 2.52525252525252 Dur2[12]= 19481 ' freq= 2.56660335711719 Dur2[13]= 19167 ' freq= 2.60865028434288 Dur2[14]= 18858 ' freq= 2.65139463357726 Dur2[15]= 18554 ' freq= 2.69483669289641 Dur2[16]= 18255 ' freq= 2.73897562311695 Dur2[17]= 17961 ' freq= 2.7838093647347 Dur2[18]= 17672 ' freq= 2.82933454051607 Dur2[19]= 17387 ' freq= 2.87571173865532 Dur2[20]= 17107 ' freq= 2.92278014847723 Dur2[21]= 16831 ' freq= 2.97070881112233 Dur2[22]= 16560 ' freq= 3.01932367149758 Dur2[23]= 16293 ' freq= 3.06880255324372 Dur2[24]= 16030 ' freq= 3.11915159076731 Dur2[25]= 15772 ' freq= 3.17017499365965 Dur2[26]= 15518 ' freq= 3.22206469905916 Dur2[27]= 15268 ' freq= 3.27482315954938 Dur2[28]= 15022 ' freq= 3.32845160431367 Dur2[29]= 14780 ' freq= 3.382949932341 Dur2[30]= 14542 ' freq= 3.43831660019254 Dur2[31]= 14307 ' freq= 3.4947927587894 Dur2[32]= 14077 ' freq= 3.55189315905378 Dur2[33]= 13850 ' freq= 3.6101083032491 Dur2[34]= 13627 ' freq= 3.66918617450649 Dur2[35]= 13407 ' freq= 3.72939509211606 Dur2[36]= 13191 ' freq= 3.79046319460238 Dur2[37]= 12978 ' freq= 3.85267375558638 Dur2[38]= 12769 ' freq= 3.91573341686898 Dur2[39]= 12564 ' freq= 3.97962432346386 Dur2[40]= 12361 ' freq= 4.04498017959712 Dur2[41]= 12162 ' freq= 4.1111659266568 Dur2[42]= 11966 ' freq= 4.17850576633796 Dur2[43]= 11773 ' freq= 4.24700586086809 Dur2[44]= 11583 ' 07.08.2016: geeft 10.1177 Hz Dur2[45]= 11397 ' freq= 4.38711941739054 Dur2[46]= 11213 ' freq= 4.45910996165165 Dur2[47]= 11032 ' freq= 4.53226976069616 Dur2[48]= 10855 ' freq= 4.60617227084293 Dur2[49]= 10680 ' freq= 4.6816479400749 Dur2[50]= 10508 ' freq= 4.75827940616673 Dur2[51]= 10338 ' freq= 4.83652544012381 Dur2[52]= 10172 ' freq= 4.91545418796697 Dur2[53]= 10008 ' freq= 4.99600319744204 Dur2[54]= 9846 ' freq= 5.07820434694292 Dur2[55]= 9688 ' freq= 5.16102394715111 Dur2[56]= 9532 ' freq= 5.24548887956357 Dur2[57]= 9378 ' freq= 5.33162721262529 Dur2[58]= 9227 ' freq= 5.4188793757451 Dur2[59]= 9078 ' freq= 5.50782110597048 Dur2[60]= 8932 ' freq= 5.59785042543663 Dur2[61]= 8788 ' freq= 5.68957669549385 Dur2[62]= 8646 ' freq= 5.78302105019662 Dur2[63]= 8507 ' 04.08.2016: 21Hz pulse duur = 23.8ms Dur2[64]= 8370 ' geeft nu 20ms pulsen freq= 25Hz 03.08.2016 ' 14.0017Hz 07.08.2016 Dur2[65]= 8235 ' freq= 6.07164541590771 Dur2[66]= 8102 ' freq= 6.17131572451246 Dur2[67]= 7972 ' freq= 6.27195183140993 Dur2[68]= 7843 ' freq= 6.37511156445238 Dur2[69]= 7717 ' freq= 6.47920176234288 Dur2[70]= 7593 ' freq= 6.58501251152377 Dur2[71]= 7470 ' freq= 6.69344042838019 Dur2[72]= 7350 ' freq= 6.80272108843537 Dur2[73]= 7231 ' freq= 6.91467293597013 Dur2[74]= 7115 ' freq= 7.02740688685875 Dur2[75]= 7000 ' freq= 7.14285714285714 Dur2[76]= 6888 ' freq= 7.25900116144018 Dur2[77]= 6777 ' freq= 7.37789582411096 Dur2[78]= 6667 ' freq= 7.49962501874906 Dur2[79]= 6560 ' freq= 7.62195121951219 Dur2[80]= 6454 ' freq= 7.74713356058258 Dur2[81]= 6350 ' freq= 7.8740157480315 Dur2[82]= 6248 ' freq= 8.00256081946223 Dur2[83]= 6147 ' freq= 8.13404912965674 Dur2[84]= 6048 ' freq= 8.26719576719577 Dur2[85]= 5951 ' freq= 8.40194925222652 Dur2[86]= 5855 ' freq= 8.5397096498719 Dur2[87]= 5760 ' freq= 8.68055555555555 Dur2[88]= 5668 ' freq= 8.82145377558222 Dur2[89]= 5576 ' freq= 8.96700143472023 Dur2[90]= 5486 ' freq= 9.11410864017499 Dur2[91]= 5398 ' freq= 9.26268988514264 Dur2[92]= 5311 ' freq= 9.41442289587648 Dur2[93]= 5225 ' freq= 9.56937799043062 Dur2[94]= 5141 ' freq= 9.72573429293912 Dur2[95]= 5058 ' freq= 9.88533017002768 Dur2[96]= 4977 ' freq= 10.0462125778581 Dur2[97]= 4897 ' freq= 10.2103328568511 Dur2[98]= 4818 ' freq= 10.3777501037775 Dur2[99]= 4740 ' freq= 10.548523206751 Dur2[100]= 4664 ' freq= 10.7204116638079 Dur2[101]= 4589 ' freq= 10.8956199607758 Dur2[102]= 4515 ' freq= 11.0741971207087 Dur2[103]= 4442 ' freq= 11.2561909049977 Dur2[104]= 4370 ' freq= 11.441647597254 Dur2[105]= 4300 ' freq= 11.6279069767442 Dur2[106]= 4231 ' freq= 11.8175372252422 Dur2[107]= 4162 ' freq= 12.013455069678 Dur2[108]= 4095 ' freq= 12.2100122100122 Dur2[109]= 4029 ' freq= 12.4100273020601 Dur2[110]= 3964 ' freq= 12.6135216952573 Dur2[111]= 3901 ' freq= 12.8172263522174 Dur2[112]= 3838 ' freq= 13.0276185513288 Dur2[113]= 3776 ' freq= 13.2415254237288 Dur2[114]= 3715 ' freq= 13.4589502018842 Dur2[115]= 3655 ' freq= 13.6798905608755 Dur2[116]= 3596 ' freq= 13.9043381535039 Dur2[117]= 3538 ' freq= 14.1322781232335 Dur2[118]= 3481 ' freq= 14.3636885952312 Dur2[119]= 3425 ' freq= 14.5985401459854 Dur2[120]= 3370 ' freq= 14.8367952522255 Dur2[121]= 3316 ' freq= 15.0784077201448 Dur2[122]= 3262 ' freq= 15.3280196198651 Dur2[123]= 3210 ' freq= 15.5763239875389 Dur2[124]= 3158 ' freq= 15.8328055731476 Dur2[125]= 3107 ' freq= 16.0926939169617 Dur2[126]= 3057 ' freq= 16.3559044815178 = 38.3365Hz 07.08.2016 Dur2[127]= 3008 ' 04.08.2016: 8.35ms - 59.5Hz ' rescale for horn: For i = 0 To 127 Dur2[i] = Dur2[i] >> 2 ' four times faster Next i Return ' midi-note to frequency lookup: Note_Freq_Lookup: NoteFreq[0]= 8 NoteFreq[1]= 9 NoteFreq[2]= 9 NoteFreq[3]= 10 NoteFreq[4]= 10 NoteFreq[5]= 11 NoteFreq[6]= 12 NoteFreq[7]= 12 NoteFreq[8]= 13 NoteFreq[9]= 14 NoteFreq[10]= 15 NoteFreq[11]= 15 NoteFreq[12]= 16 NoteFreq[13]= 17 NoteFreq[14]= 18 NoteFreq[15]= 19 NoteFreq[16]= 21 NoteFreq[17]= 22 NoteFreq[18]= 23 NoteFreq[19]= 24 NoteFreq[20]= 26 NoteFreq[21]= 28 NoteFreq[22]= 29 NoteFreq[23]= 31 NoteFreq[24]= 33 NoteFreq[25]= 35 NoteFreq[26]= 37 NoteFreq[27]= 39 NoteFreq[28]= 41 NoteFreq[29]= 44 ' seems lowest possible note NoteFreq[30]= 46 NoteFreq[31]= 49 NoteFreq[32]= 52 NoteFreq[33]= 55 NoteFreq[34]= 58 NoteFreq[35]= 62 NoteFreq[36]= 65 NoteFreq[37]= 69 NoteFreq[38]= 73 NoteFreq[39]= 78 NoteFreq[40]= 82 NoteFreq[41]= 87 NoteFreq[42]= 92 ' now this is lowest note 13.01.2018 NoteFreq[43]= 98 NoteFreq[44]= 104 NoteFreq[45]= 110 NoteFreq[46]= 117 NoteFreq[47]= 123 NoteFreq[48]= 131 NoteFreq[49]= 139 NoteFreq[50]= 147 NoteFreq[51]= 156 NoteFreq[52]= 165 NoteFreq[53]= 175 NoteFreq[54]= 185 NoteFreq[55]= 196 NoteFreq[56]= 208 NoteFreq[57]= 220 NoteFreq[58]= 233 NoteFreq[59]= 247 NoteFreq[60]= 262 NoteFreq[61]= 277 NoteFreq[62]= 294 NoteFreq[63]= 311 NoteFreq[64]= 330 NoteFreq[65]= 349 NoteFreq[66]= 370 NoteFreq[67]= 392 NoteFreq[68]= 415 NoteFreq[69]= 440 NoteFreq[70]= 466 NoteFreq[71]= 494 NoteFreq[72]= 523 NoteFreq[73]= 554 NoteFreq[74]= 587 ' bij 12V, of 50% PWM NoteFreq[75]= 622 NoteFreq[76]= 659 NoteFreq[77]= 698 NoteFreq[78]= 740 NoteFreq[79]= 784 NoteFreq[80]= 831 NoteFreq[81]= 880 NoteFreq[82]= 932 NoteFreq[83]= 988 NoteFreq[84]= 1047 ' now highest note... NoteFreq[85]= 1109 NoteFreq[86]= 1175 ' bij 24V, 100% PWM NoteFreq[87]= 1245 NoteFreq[88]= 1319 NoteFreq[89]= 1397 NoteFreq[90]= 1480 NoteFreq[91]= 1568 NoteFreq[92]= 1661 NoteFreq[93]= 1760 NoteFreq[94]= 1865 NoteFreq[95]= 1976 NoteFreq[96]= 2093 NoteFreq[97]= 2217 NoteFreq[98]= 2349 NoteFreq[99]= 2489 NoteFreq[100]= 2637 NoteFreq[101]= 2794 NoteFreq[102]= 2960 NoteFreq[103]= 3136 NoteFreq[104]= 3322 NoteFreq[105]= 3520 NoteFreq[106]= 3729 NoteFreq[107]= 3951 NoteFreq[108]= 4186 NoteFreq[109]= 4435 NoteFreq[110]= 4699 NoteFreq[111]= 4978 NoteFreq[112]= 5274 NoteFreq[113]= 5588 NoteFreq[114]= 5920 NoteFreq[115]= 6272 NoteFreq[116]= 6645 NoteFreq[117]= 7040 NoteFreq[118]= 7459 NoteFreq[119]= 7902 NoteFreq[120]= 8372 NoteFreq[121]= 8870 NoteFreq[122]= 9397 NoteFreq[123]= 9956 NoteFreq[124]= 10548 NoteFreq[125]= 11175 NoteFreq[126]= 11840 NoteFreq[127]= 12544 Return Note_PWM_Lookup: ' pwm = Notefreq[note] * 55.81 ' for 24V motor siren in 16 bit NotePWM[0]= 456 NotePWM[1]= 483 NotePWM[2]= 512 NotePWM[3]= 543 NotePWM[4]= 575 NotePWM[5]= 609 NotePWM[6]= 645 NotePWM[7]= 684 NotePWM[8]= 724 NotePWM[9]= 767 NotePWM[10]= 813 NotePWM[11]= 861 NotePWM[12]= 913 NotePWM[13]= 967 NotePWM[14]= 1024 NotePWM[15]= 1085 NotePWM[16]= 1150 NotePWM[17]= 1218 NotePWM[18]= 1291 NotePWM[19]= 1367 NotePWM[20]= 1449 NotePWM[21]= 1535 NotePWM[22]= 1626 NotePWM[23]= 1723 NotePWM[24]= 1825 NotePWM[25]= 1934 NotePWM[26]= 2049 NotePWM[27]= 2170 NotePWM[28]= 2300 NotePWM[29]= 2436 ' lowest possible F1 NotePWM[30]= 2581 NotePWM[31]= 2735 NotePWM[32]= 2897 NotePWM[33]= 3070 NotePWM[34]= 3252 NotePWM[35]= 3445 NotePWM[36]= 3650 NotePWM[37]= 3867 NotePWM[38]= 4097 NotePWM[39]= 4341 NotePWM[40]= 4599 NotePWM[41]= 4873 NotePWM[42]= 5162 ' new lowest note NotePWM[43]= 5469 NotePWM[44]= 5795 NotePWM[45]= 6139 NotePWM[46]= 6504 NotePWM[47]= 6891 NotePWM[48]= 7301 NotePWM[49]= 7735 NotePWM[50]= 8195 NotePWM[51]= 8682 NotePWM[52]= 9198 NotePWM[53]= 9745 NotePWM[54]= 10325 NotePWM[55]= 10939 NotePWM[56]= 11589 NotePWM[57]= 12278 NotePWM[58]= 13008 NotePWM[59]= 13782 NotePWM[60]= 14601 NotePWM[61]= 15470 NotePWM[62]= 16389 NotePWM[63]= 17364 NotePWM[64]= 18397 NotePWM[65]= 19490 NotePWM[66]= 20649 NotePWM[67]= 21877 NotePWM[68]= 23178 NotePWM[69]= 24556 NotePWM[70]= 26017 NotePWM[71]= 27564 NotePWM[72]= 29203 NotePWM[73]= 30939 NotePWM[74]= 32779 ' ref. note 50% PWM - 12V NotePWM[75]= 34728 NotePWM[76]= 36793 NotePWM[77]= 38981 NotePWM[78]= 41299 NotePWM[79]= 43755 NotePWM[80]= 46356 NotePWM[81]= 49113 NotePWM[82]= 52033 NotePWM[83]= 55127 NotePWM[84]= 58405 ' new highest note 'NotePWM[85]= 61878 'NotePWM[86]= 65535 ' limit 16 bit for 24 V - calculated as 65558 'NotePWM[87]= 69456 'NotePWM[88]= 73586 'NotePWM[89]= 77962 'NotePWM[90]= 82598 'NotePWM[91]= 87509 'NotePWM[92]= 92713 'NotePWM[93]= 98226 'NotePWM[94]= 104066 'NotePWM[95]= 110255 'NotePWM[96]= 116811 'NotePWM[97]= 123756 'NotePWM[98]= 131115 'NotePWM[99]= 138912 'NotePWM[100]= 147172 'NotePWM[101]= 155923 'NotePWM[102]= 165195 'NotePWM[103]= 175018 'NotePWM[104]= 185425 'NotePWM[105]= 196451 'NotePWM[106]= 208133 'NotePWM[107]= 220509 'NotePWM[108]= 233621 'NotePWM[109]= 247513 'NotePWM[110]= 262231 'NotePWM[111]= 277824 'NotePWM[112]= 294344 'NotePWM[113]= 311847 'NotePWM[114]= 330390 'NotePWM[115]= 350036 'NotePWM[116]= 370850 'NotePWM[117]= 392902 'NotePWM[118]= 416266 'NotePWM[119]= 441018 'NotePWM[120]= 467242 'NotePWM[121]= 495026 'NotePWM[122]= 524462 'NotePWM[123]= 555648 'NotePWM[124]= 588688 'NotePWM[125]= 623694 'NotePWM[126]= 660780 'NotePWM[127]= 700072 ' rescaling for 14 bits resolution: For i = 0 To 84 NotePWM[i] = NotePWM[i] >> 2 Next i Return '[EOF]