'*************************************************** ' IRQ coding for midi-in * ' by * ' Godfried-Willem Raes * ' Version 2.5 * '*************************************************** ' Interrupt-driven serial buffer for USART MIDI receive using a hardware high priority interrupt. ' This subroutine replaces the compiler's Hserin/Hrsin and HrsOut commands library subroutines ' For use with 18F devices only ' 25.09.2010 - first redaction ' 12.10.2010 - included in Pedal firmware ' 20.04.2011 - included in Fa firmware, hub ' 30.04.2011 - now user low prior. irq for sampling the sensor ' include renamed Midi_Hub_Irq.inc ' Timers include no longer required. ' 23.05.2011 - 32 bit timer coding improved ' 20.08.2013 - used for ' low interrupts will not happen here! ' 20.04.2015 - adapted to compiler upgrade. ' some minor changes. ' 06.06.2015 - trying to get it to work properly again... ' works fine on ' 27.08.2015 - included in code for ' 16.03.2016 - included in code for ' 25.04.2016 - included in the code for with Low interrupt for timer3. ' 22.06.2016 - using sawposition monitoring in the low IRQ ' 03.07.2016 = sawpos monitoring in the Tim3 interrupt dropped. ' 04.07.2016 - sawpos monitoring added again in the ISR ' 09.07.2016 - sawpos monitoring in ISR using flags only. ' the motor commands are handled in the main loop. ' This operation was done in an attempt to get rid of the ' interference of timer3 operation with the HPWM commands. #Disable HRSIn, HRSOut ' Disable the compiler's library subroutines for Hrsin ' statements starting with # are ASM compiler directives ' Create the SYSTEM variables and aliases for the Buffered commands Dim PP0 As Byte System ' \ Dim PP0H As Byte System ' / Storage for FSR0L\H registers Dim FSR0SaveInt As PP0 ' alias Dim FSR0SaveIntH As PP0H ' alias Dim USART_FSR0_Save As FSR0SaveInt.Word ' Alias the FSR0L\H storage to PP0\H Dim USART_FSR1_Save As Word System ' Storage for FSR1L\H registers Dim USART_FSR0 As FSR0L.Word ' alias Dim USART_FSR1 As FSR1L.Word ' alias Dim IndexIn As Byte System ' Pointer to the next empty location in the buffer Dim IndexOut As Byte System ' Pointer to the location of the oldest character in the buffer ' Create the actual serial buffer in high memory ' Reserve_RAM 256 ' Reserve some RAM at the top of memory as a serial buffer (MAX 255) ' 40 could be enough ' remmed out 15.04.2015, compiler compaint... ' Symbol USART_BufferSize = __Reserve_Ram_Size ' The amount of RAM reserved is the buffer size (MAX 255) ' Dim Ringbuffer[USART_BufferSize+1] As Byte At __Start_Of_Reserve_Ram ' Array for holding received characters '25.09.2010: by setting it to 256 'we get an automatic overflow to 0 Hence: ' Symbol USART_BufferSize = 255 ' The amount of RAM reserved is the buffer size (MAX 255) ' Dim Ringbuffer[256] As Byte At __Start_Of_Reserve_Ram ' Array for holding received characters ' now declared in the main program to avoid memory corruption Reminders = On Warnings = On ' Point to the high priority hardware interrupt handler On_Hardware_Interrupt GoTo High_Prior_Interrupt On_Low_Interrupt GoTo Low_Prior_Interrupt GoTo _Over_IRQ_Handler ' Jump over the subroutines '---------------------------------------------------------------- ' USART ring buffer interrupt handler for MIDI and 32-bit timer0 ' Input : Interrupt is triggered on reception of a byte from the USART and on timer0 overflow ' Output : Array Ringbuffer holds the bytes received ' : Byte IndexIn points to the current location within the buffer ' Notes : Does not indicate if an error occurs but does clear the error flags and carries on High_Prior_Interrupt: ' Context Save ' added in after compiler upgrade 20.04.2015- not required for High IRQ _Timer0_IRQ: If INTCONbits_T0IF = 1 Then Clear INTCONbits_T0IF ' Clear the Timer0 Overflow flag ' Inc Cnt.Word1 ' would it be slower to do Inc Cnt.Word1 instead of CntHw here? ' CntLw = 5 ' to correct prescaler errors on writing to tmr0L 'Inc CntLw Btg Debug_Led 'PORTB,5 ' asm bit toggle - debug led watchdog Retfie Fast EndIf _UART_IRQ: If PIR1bits_RCIF = 1 Then ' Was it a USART1 byte Receive that triggered the interrupt ? Movlw 6 ' Yes. So Mask out unwanted bits Andwf RCSTA,w ' Check for errors Bnz _Uart_Error ' Was either error status bit set? USART_FSR1_Save = USART_FSR1 ' Save FSR1L\H registers Inc IndexIn ' Move up the buffer index (0-255) ' if the buffer is smaller, but still a power of 2, we can 'implement circularity simply as: 'IndexIn = IndexIn & ~USART_Buffersize 'or, if it's faster, mask the highest limit bit: 'Clear IndexIn.7 = for a 128 byte buffer 'Clear IndexIn.6 = for a 64 byte buffer 'If IndexIn >= USART_BufferSize Then ' End of buffer reached ? ' Clear IndexIn ' Yes. So clear _USART_IndexIn 'EndIf USART_FSR1 = VarPtr Ringbuffer ' Point FSR1L\H to _USART_RingBuffer USART_FSR1 = USART_FSR1 + IndexIn ' Add the buffer position to FSR1L\H INDF1 = RCREG ' Place the received character into the buffer USART_FSR1 = USART_FSR1_Save ' Restore FSR1L\H registers 'Toggle PORTB.5 'debug - checked. This works. Retfie Fast ' Exit from the interrupt, restoring the WREG, STATUS, and BSR registers _Uart_Error: WREG = RCREG ' empty the 2 byte UART buffer WREG = RCREG Clear RCSTAbits_CREN ' Clear receiver status Set RCSTAbits_CREN EndIf Retfie Fast ' Context Restore @HRSIn: ; assembly FUNCTION redefinition If IndexIn <> IndexOut Then Inc IndexOut ' Increment IndexOut pointer (0 to 255) USART_FSR0_Save = USART_FSR0 ' Save FSR0L\H registers USART_FSR0 = VarPtr Ringbuffer ' Point FSR0L\H to Ringbuffer USART_FSR0 = USART_FSR0 + IndexOut ' Add the buffer position to FSR0L\H WREG = INDF0 ' Read buffer location (IndexOut) into WREG PP0 = WREG ' Also place it into PP0 USART_FSR0 = USART_FSR0_Save ' Restore FSR0\H registers Set STATUSbits_C ' Set CARRY flag to indicate a byte received Ret ' compulsary here! Else WREG = 255 PP0 = WREG ' \ Btfss STATUS,C ' Return with the CARRY flag clear to indicate timed out Ret 'compulsary here EndIf Ret 'compulsary here '-------------------------------------------------------------------------------- ' Initialise the USART1 interrupt ' Input : None ' Output : None ' Notes : Enables interrupt on USART1 receive. ' : Enables global and peripheral interrupts ' : If Prioritised interrupts are used, the USART interrupt priority is made high $define InitUSARTInterrupt () Init_Usart_Interrupt 'added 31.08.2010 gwr Init_Usart_Interrupt Macro GoSub _Init_Usart Endm #ifdef Init_Usart_Interrupt#Req _Init_Usart: Clear IndexIn ' Clear the buffer internal pointer Clear IndexOut ' Clear the buffer external pointer Set PIE1bits_RCIE ' Enable interrupt on USART1 receive #ifdef __Low_Interrupts_Enabled ' Are we using low priority interrupts as well ? Set IPR1bits_RCIP ' Yes. So USART1 Receive Interrupt to High priority #endif Set INTCONbits_GIE ' enable general interrups Set INTCONbits_PEIE ' Enable global and peripheral interrupts 'Set INTCONbits_T0IE ' Enable Timer0 overflow interrupt, done on opentimer0() Return #endif '-------------------------------------------------------------------------------- ' Clear the Serial Buffer ' Input : None ' Output : None ' Notes : Also resets the index pointers to the serial buffer $define ClearSerialBuffer () Clear_Serial_Buffer ' added 31.08.2010 - gwr Clear_Serial_Buffer Macro GoSub _Clear_Usart_Buffer Endm #ifdef Clear_Serial_Buffer#Req _Clear_Usart_Buffer: PIE1bits_RCIE = 0 ' Disable interrupt on USART1 receive Clear Ringbuffer ' Clear the serial buffer Clear IndexIn ' Clear the buffer internal pointer Clear IndexOut ' Clear the buffer external pointer Set PIE1bits_RCIE ' Re-Enable interrupt on USART1 receive Return #endif '------------------------------------------------------- ' Low Priority Interrupt on Timer3 overflow ' in Flex used to generate the stepping frequency for the motor ' here it also keeps track of the slide position and autoregulation Low_Prior_Interrupt: Timer3_ISR: Context Save ' Save the contents of WREG, STATUS, and BSR Clear PIR2bits_TMR3IF ' Clear the Timer3 interrupt flag. Tim3 = Period ' period wordt bepaald bij ontvangst van een note on Btg Mot_Klok ' portA.5 - the motor steps on the falling edge of the clock ' so if mot_klok is low, we just performed a step If mot_enable = 0 Then Inc rotcnt ' counter for avoiding multiple triggering on rotation sensor ' first we handle the sensors: If sensor1 = 1 Then ' running cw and reaching the end If mot_dir = 0 Then ' if running cw , so further towards the sensor Minpos = sawpos Maxpos = Minpos + Traject Set calibrated_Low Set Mot_Klok ' clock high Toggle mot_dir ' should go ccw now Clear mot_enable ' should start the motor ccw If Ack1 = 0 Then ' the following should only be done once! 'Set Ack1 ' set so that we dont do this twice or more.... flags = %00000001 ' = 1 the flag should be cleared in the main loop EndIf ' else, mot dir was changed, so we should check sawpos EndIf GoTo exit_ISR ' as long as this sensor is touched the motor should run ccw 'Set mot_dir Else ' in this case we are no longer in contact with the sensor Clear Ack1 EndIf If sensor2 = 1 Then ' end sensor reached If mot_dir = 1 Then ' if running ccw Maxpos = sawpos MinPos = Maxpos - Traject Set Calibrated_High Set mot_Klok Toggle mot_dir ' should go cw now Clear mot_enable ' clear= start motor ' set= stop motor If Ack2 = 0 Then 'Set Ack2 ' set acknowledge flag flags = %00000010 ' = 2 EndIf EndIf GoTo exit_ISR 'Clear mot_dir ' as long as this sensor is touched we should run cw Else Clear Ack2 EndIf ' if we end up here, we can freely move to sollpos Select SawPos Case SollPos If mot_enable = 0 Then ' if the motor is running Set Mot_Klok Toggle mot_enable ' stop motor - becomes set If Ack3 = 0 Then ' Set Ack3 flags = %00000100 ' = 4 EndIf EndIf Case < SollPos ' we have to run ccw - bending inwards If mot_dir = 0 Then ' if we are running cw Set mot_Klok Toggle mot_dir ' make it ccw Clear mot_enable ' run If Ack4 = 0 Then ' Set Ack4 flags = %00001000 ' = 8 GoTo exit_ISR EndIf EndIf ' Set mot_dir ccw Clear mot_enable ' run Case > SollPos ' we have to run cw - stretching If mot_dir = 1 Then ' if we are running ccw Set mot_Klok Toggle mot_dir ' make it cw Clear mot_enable If ack5 = 0 Then ' Set Ack5 flags = %00010000 ' =16 GoTo exit_ISR EndIf EndIf ' Clear mot_dir cw Clear mot_enable ' run End Select ' endif for power on exit_ISR: Context Restore ' Restore the contents of WREG, STATUS, and BSR, then exit interrupt ' Retfie ' not required, the compiler generates this automatically. '-------------------------------------------------------------------------------- ' Replace the library HRSOUT with this routine for MIDI-Output ' Sets the RS485 Tranceiver's pin High before transmitting Asm- HRSOut EndAsm Btfss PIR1bits_TXIF ' Wait till transmitter ready Bra ($ - 2) Movwf TXREG ' Send the byte Return ' Exit the subroutine _Over_IRQ_Handler: