pic32/libraries/SoftPWMServo/SoftPWMServo.cpp
/********************  private functions *************************************/
// Primary worker function for SoftPWM
// Is called from within CoreTimer interrupt from wiring.c
// Schedules next CoreTimer interrupt based upon what needs to happen next 
// - another falling edge, or all rising edges, or 1ms CoreTimer interrupt.
private functions
Primary worker function for SoftPWM
Is called from within CoreTimer interrupt from wiring.c
Schedules next CoreTimer interrupt based upon what needs to happen next
another falling edge, or all rising edges, or 1ms CoreTimer interrupt.
private functions Primary worker function for SoftPWM Is called from within CoreTimer interrupt from wiring . c Schedules next CoreTimer interrupt based upon what needs to happen next another falling edge or all rising edges or 1ms CoreTimer interrupt .
uint32_t HandlePWMServo(uint32_t CurrentCount)
{
    uint32_t NextTime = 0;                  
    static ChanType * CurChanP = NULL;      
    bool DoItAgain = false;                 
    uint32_t NextTimeAcc = CurrentCount;    
    uint32_t TempCoreTimer;                 
    do 
    {
        if (RisingEdge)
        {
            CurChanP = ISRFirstChanP;
            if (CurChanP != NULL)
            {
                while (CurChanP != NULL)
                {
                    if (CurChanP->PWMValue != 0)
                    {
                        if (
                            (CurChanP->IsServo && !ServoFrameCounter)
                            ||
                            (!CurChanP->IsServo)
                        )
                        {
                            *(CurChanP->SetPort) |= CurChanP->Bit;
                        }
                    }
                    CurChanP = CurChanP->NextChanP;
                }
                CurChanP = ISRFirstChanP;
                NextTime = CurChanP->PWMValue;
                CurChanP->NextEdgeTime = NextTime;
                CurrentTime = 0;
                RisingEdge = false;
            }
            else
            {
                NextTime = FrameTime;
                if (InactiveBufferReady)
                {
                    if (ActiveBuffer)
                    {
                        ActiveBuffer = 0;
                        InactiveBuffer = 1;
                    }
                    else
                    {
                        ActiveBuffer = 1;
                        InactiveBuffer = 0;
                    }
                    ISRFirstChanP = FirstChanP[ActiveBuffer];
                    InactiveBufferReady = false;
                }
            }
            ServoFrameCounter++;
            if (ServoFrameCounter == ServoFrames)
            {
                ServoFrameCounter = 0;
            }
        }
        else
        {
            if (CurChanP->PWMValue < FrameTime || CurChanP->IsServo)
            {
                *(CurChanP->ClearPort) |= CurChanP->Bit;
            }
            CurrentTime += CurChanP->NextEdgeTime;
            while (
                (CurChanP->NextChanP != NULL)
                &&
                (CurChanP->PWMValue == CurChanP->NextChanP->PWMValue)
            ) 
            {
                CurChanP = CurChanP->NextChanP;
                if (CurChanP->PWMValue < FrameTime || CurChanP->IsServo)
                {
                    *(CurChanP->ClearPort) |= CurChanP->Bit;
                }
            }
            if (CurChanP->NextChanP == NULL)
            {
                NextTime = FrameTime - CurrentTime;
                RisingEdge = true;
                if (InactiveBufferReady)
                {
                    if (ActiveBuffer)
                    {
                        ActiveBuffer = 0;
                        InactiveBuffer = 1;
                    }
                    else
                    {
                        ActiveBuffer = 1;
                        InactiveBuffer = 0;
                    }
                    ISRFirstChanP = FirstChanP[ActiveBuffer];
                    InactiveBufferReady = false;
                }
            }
            else
            {
                CurChanP = CurChanP->NextChanP;
                if (CurChanP != NULL)
                {
                    CurChanP->NextEdgeTime = CurChanP->PWMValue - CurrentTime;
                }
                NextTime = CurChanP->NextEdgeTime;
            }
        }       
        NextTimeAcc += NextTime;
        TempCoreTimer = readCoreTimer();
        if ((NextTimeAcc - TempCoreTimer) <= EXTRA_ISR_EXIT_CYCLES)
        {
            DoItAgain = true;
             We need to wait until the time when we _would_ have actually let the
             CoreTimer fire has come and gone. We also put a fudge factor in here to
             simulate the number of cycles necessary to get into the ISR and to the 
             point where the top of the do-while loop starts executing.
            while(readCoreTimer() < (NextTimeAcc + EXTRA_ISR_ENTRY_CYCLES))
            {
            }
        }
        else
        {
            DoItAgain = false;
        }
    }
     We will continue to stay in this loop (which encompasses almost the entire function)
     until our next edge (PWM or 1ms) is far enough in the future that we can spend the
     cycles leaving the ISR and coming back in again.
    while (DoItAgain);    
     Return 0 so we don't run the 1ms 'stuff' in the main Core Timer ISR, or 1
     so that we do (this is set only once, to a 1, if we have the 1ms tick
     fire off. Since we don't clear RetVal during this ISR, any time we enter
     the ISR and have the 1ms tick happen, we will be telling the wiring.c code
     to run the 1ms code.)
    return(NextTimeAcc);
}
uint32_t HandlePWMServo ( uint32_t CurrentCount ) { uint32_t NextTime = 0 ; static ChanType * CurChanP = NULL ; bool DoItAgain = false ; uint32_t NextTimeAcc = CurrentCount ; uint32_t TempCoreTimer ; do { if ( RisingEdge ) { CurChanP = ISRFirstChanP ; if ( CurChanP != NULL ) { while ( CurChanP != NULL ) { if ( CurChanP -> PWMValue != 0 ) { if ( ( CurChanP -> IsServo && ! ServoFrameCounter ) || ( ! CurChanP -> IsServo ) ) { * ( CurChanP -> SetPort ) |= CurChanP -> Bit ; } } CurChanP = CurChanP -> NextChanP ; } CurChanP = ISRFirstChanP ; NextTime = CurChanP -> PWMValue ; CurChanP -> NextEdgeTime = NextTime ; CurrentTime = 0 ; RisingEdge = false ; } else { NextTime = FrameTime ; if ( InactiveBufferReady ) { if ( ActiveBuffer ) { ActiveBuffer = 0 ; InactiveBuffer = 1 ; } else { ActiveBuffer = 1 ; InactiveBuffer = 0 ; } ISRFirstChanP = FirstChanP [ ActiveBuffer ] ; InactiveBufferReady = false ; } } ServoFrameCounter ++ ; if ( ServoFrameCounter == ServoFrames ) { ServoFrameCounter = 0 ; } } else { if ( CurChanP -> PWMValue < FrameTime || CurChanP -> IsServo ) { * ( CurChanP -> ClearPort ) |= CurChanP -> Bit ; } CurrentTime += CurChanP -> NextEdgeTime ; while ( ( CurChanP -> NextChanP != NULL ) && ( CurChanP -> PWMValue == CurChanP -> NextChanP -> PWMValue ) ) { CurChanP = CurChanP -> NextChanP ; if ( CurChanP -> PWMValue < FrameTime || CurChanP -> IsServo ) { * ( CurChanP -> ClearPort ) |= CurChanP -> Bit ; } } if ( CurChanP -> NextChanP == NULL ) { NextTime = FrameTime - CurrentTime ; RisingEdge = true ; if ( InactiveBufferReady ) { if ( ActiveBuffer ) { ActiveBuffer = 0 ; InactiveBuffer = 1 ; } else { ActiveBuffer = 1 ; InactiveBuffer = 0 ; } ISRFirstChanP = FirstChanP [ ActiveBuffer ] ; InactiveBufferReady = false ; } } else { CurChanP = CurChanP -> NextChanP ; if ( CurChanP != NULL ) { CurChanP -> NextEdgeTime = CurChanP -> PWMValue - CurrentTime ; } NextTime = CurChanP -> NextEdgeTime ; } } NextTimeAcc += NextTime ; TempCoreTimer = readCoreTimer ( ) ; if ( ( NextTimeAcc - TempCoreTimer ) <= EXTRA_ISR_EXIT_CYCLES ) { DoItAgain = true ; while ( readCoreTimer ( ) < ( NextTimeAcc + EXTRA_ISR_ENTRY_CYCLES ) ) { } } else { DoItAgain = false ; } } while ( DoItAgain ) ; return ( NextTimeAcc ) ; }
private functions
Primary worker function for SoftPWM
Is called from within CoreTimer interrupt from wiring.c
Schedules next CoreTimer interrupt based upon what needs to happen next
 another falling edge, or all rising edges, or 1ms CoreTimer interrupt.
private functions Primary worker function for SoftPWM Is called from within CoreTimer interrupt from wiring . c Schedules next CoreTimer interrupt based upon what needs to happen next another falling edge or all rising edges or 1ms CoreTimer interrupt .
// The number of CoreTimer counts into the future when our next edge should occur // Pointer to the current channel we're operating on // True if we don't have time to leave the ISR and come back in // Records the sum of NextTime values while we stay in the do-while loop // Hold current core timer value so only one read is necessary // This do-while loop keeps us inside this function as long as there are // edges to process. Only once we have enough time to leave and get back // in for the next edge do we break out of the while loop. // If it's time to do the rising edge of all enabled channels- // Start at the first channel // Check to see if we have zero channels actually loaded with data // For each channel that's active, set its pin high // But only if it is not at %0 // Make an exception for servos - only set them // high when ServoFrameCounter is zero. // Advance to next channel to check it // Now start back at the beginning again, for the setting pins low part // And load up the time for the next (first) edge (falling edge of first channel) // And mark this time as the beginning of the PWM cycle // We don't want to do this again until next time // We have no channels actually turned on (enabled or otherwise) // So just load the NextTime with the duration of the whole PWM cycle and do this // all over again. // Don't set SoftPWMRisingEdge to FALSE - leave it TRUE so we just keep doing this // If it's time to swap buffers, then do that here // And have ISR use FirstChanP from new active buffer // Tell mainline code we've swapped // Count this frame, for the servo pins // If we're reached our ServoFrames, limit, then set to zero to mark that // the next frame will have all servos do their rising edges. // Now we have a falling edge. So we need to set some channel's pin low here. // Always set the next bit low, if the channel is not at 100% // But if it's a servo, always set it low. // Record how much time has passed (where are we in the frame) // Check for more channels that have this same time - but only if we // haven't hit the end of the list yet. (Channels with same edges // will have PWMValues that are the same.) // Now start working on the next channel in the linked list // Only touch the output if it's not at 100% or if it is a servo // pin (they always go low). // Set this bit low // Have we run out of active channels? // Yup, so set the next interrupt should happen at the beginning of the next frame // And make all of our channels go high then // If it's time to swap buffers, then do that here // And have ISR use FirstChanP from new active buffer // Tell mainline code we've swapped // Now start working on the next channel in the linked list // Time to compute the NextEdgeTime for the next channel // But only if we're not at the end. // Compute the next channel's NextEdgeTime based upon our current time and it's PWMValue // Just load up the time of the next falling edge // Each time through the main do-while loop, keep adding in the times between // edges (that would have caused CoreTimer fires if we had left the ISR). Then, // when there is finally time to leave the ISR and come back in to get to the // next edge, schedule the next edge to be the sum of all of those NextTimes. // Determine if we have enough time to leave the ISR, return to the mainline  // code, and have the CoreTimer ISR fire again to get to our next edge time. // If not, then just stay in the do-while loop (by setting DoItAgain) and  // don't leave, and just pretend that we let the CoreTimer fire. // There's a fudge factor added in to where we think we are in time so that // we can compensate for the number of CoreTimer counts it takes to leave // the ISR. // // We need to wait until the time when we _would_ have actually let the // CoreTimer fire has come and gone. We also put a fudge factor in here to // simulate the number of cycles necessary to get into the ISR and to the  // point where the top of the do-while loop starts executing. // We will continue to stay in this loop (which encompasses almost the entire function) // until our next edge (PWM or 1ms) is far enough in the future that we can spend the // cycles leaving the ISR and coming back in again. // Return 0 so we don't run the 1ms 'stuff' in the main Core Timer ISR, or 1 // so that we do (this is set only once, to a 1, if we have the 1ms tick // fire off. Since we don't clear RetVal during this ISR, any time we enter // the ISR and have the 1ms tick happen, we will be telling the wiring.c code // to run the 1ms code.)