NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Strobe Scope with Phase adjustment

June 14, 2020
by scootergarrett
scootergarrett's Avatar

I'm trying to make a strobe scope that takes an input signal from some sensor (hall effect, laser, doesn't really matter at this point) and flashes a light at a user selected phase. For the rest of this example lets say 180deg. Said another way, an incoming 500Hz signal (2ms period) the program would detect the incoming signal pulse and flash 1ms later

Example Plot

I was using the delay functions, but was having issues with high phase angles (300deg and up), I think caused by interrupts overflow, also it seems like the wrong way. Now I'm trying to use the TCNT1 counter to both measure the time between input pulses and control then the output is turned on and off. I think there is a way so when the counter TCNT1 hits OCR1A and OCR1B it will change OC1A (PB1) state on and off respectively.

I have some code that just toggles the state of PB1 when it hist OCR1A.

// for NerdKits with ATmega328 //

#define StrobePin B,1
#define InputTrigger B,5

#define StrobeOn SET(StrobePin)
#define StrobeOff CLEAR(StrobePin)

volatile uint16_t FrequencyCount;

int main()
{
    double Freq;

    // Set up pin conditions //
    OUTPUT(StrobePin);
    INPUT(InputTrigger);
    PULLUP_ON(InputTrigger);

    // Set up UART //
    uart_init();
    FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
    stdin = stdout = &uart_stream;

    // Set up PB5 for pin change interrupts //
    PCICR |= (1<<PCIE0);
    PCMSK0 |= (1<<PCINT5);

    // Working on the timing part //
    TCCR1B = (1<<CS11) | (1<<CS10);

    // WIP //
    TCCR1A |= (1<<COM1A0);
    TCCR1B |= (1<<COM1A0);

    sei();          // Enable interrupts
    delay_ms(10);

    // Will keep sending frequency every 1/2 second //
    while( true )
    {
        Freq = (1843200.0 / FrequencyCount / 8.0);

        OCR1A = FrequencyCount/2; // Just makes the OCR1A event half way between 0 and the FrequencyCount

        printf_P(PSTR("Freq: %.1lf\n\r"), Freq);
        delay_ms(500);
    }

    return 0;
}

/// Interrupt code ///
ISR(PCINT0_vect)
{
    if(READ(InputTrigger))
    {
        FrequencyCount = TCNT1;
        TCNT1 = 0;
    }

    return;
}

Plot

Is anyone here? and really good with the timer settings?

July 03, 2020
by scootergarrett
scootergarrett's Avatar

I got something working well. I have boiled down my code (and this solution) to just the strobe timing problem. The code I've been using is more complex then needed to enplane the the tricky/ clever part.

I wanted a light to pulse very quickly based on a sensor detecting cyclical motion; similar to a timing light flashing with it detects a spark. High intensity flash synced with a rotation object will make it appear to hold still. In my case I wanted the ability to flash not when the sensor detects a passing mark but a given phase later. This way I could slowly adjust the phase angle giving the illusion of slow motion.

First the code:

// for NerdKits with ATmega328 //

#define StrobePin B,1
#define InputTrigger B,0
#define TestPin B,2

volatile uint16_t PhaseShift;           // The amount to shift the phase
volatile uint16_t A2;                   // The clock cycle to turn off Strobe
volatile bool EveryOther;               // Used to only flash every other trigger. High phase angles get messed up if the clear command is after the next trigger

// Sets up for phase lock mode //
void SetupForPhase()
{
    // Set up the timer speed, input capture mode, timer interrupts, and output compare output mode //
    TCCR1B = (1<<CS11) | (1<<CS10);     // Set clock divider to CLK / 64 (Overflows at ~8Hz)

    TCCR1B &= ~(1<<ICES1);  // Falling edge triggers capture
    TCCR1B |= (1<<ICNC1);   // Use noise canceler

    TIMSK1 |= (1<<ICIE1);   // Set ICFn to interrupt (TIMER1_CAPT_vect)
    TIMSK1 |= (1<<OCIE1A);  // Set OC1A to interrupt (TIMER1_COMPA_vect)
    TIMSK1 |= (1<<TOIE1);   // Set the overflow interrupt (TIMER1_OVF_vect)

    TCCR1A |= (1<<COM1A1);  // Set the TCCR1A bit so its in clear or set mode (determined by interrupts)
}

int main()
{
    // Set up pin conditions //
    OUTPUT(StrobePin);
    INPUT(InputTrigger);
    PULLUP_ON(InputTrigger);
    OUTPUT(TestPin);
    CLEAR(TestPin);

    PhaseShift = 90;       // Set to desired phase angle (0-360)

    SetupForPhase();

    sei();                  // Enable interrupts
    /// Loop letting interrupts do work ///
    while(true);
}

// Interrupt when the trigger pin falls, uses the time between triggers to determine
// the next on and off times, sets the next OCR1A time and set to turn on OC1A at match
ISR(TIMER1_CAPT_vect)
{
    if(EveryOther)
    {
        TCNT1 = 0;                                      // Reset the timer, do this first as to include the time doing the following math in the next counting

        EveryOther = false;
        OCR1A = (uint32_t)ICR1*PhaseShift/720 + 15;     // Set OCR1A to next time to turn on, add 15 so this interrupt can finish before having TCNT1 == OCR1A
        A2 = OCR1A + ICR1/250;                          // Work out the OCR1A time to turn off, time on plus ~.4%, quick flash is the best

        TCCR1A |= (1<<COM1A0);                          // SET to turn pin on when TCNT1 == OCR1A
    }
    else
        EveryOther = true;
}

// Interrupt for when OCR1A == TCNT1 the first time it will set OC1A on match
// automatically, then set the off time into OCR1A
// This changes to clear the output on the next match, and set the next match time
ISR(TIMER1_COMPA_vect)
{
    TCCR1A &= ~(1<<COM1A0);     // Set to turn pin off when TCNT1 == OCR1A
    OCR1A = A2;                 // Set OCR1A to the turn off time
}

// If the timer overflows set the test pin //
ISR(TIMER1_OVF_vect)
{
    SET(TestPin);   // Set the test pin to show an overflow
}

I used the 16bit timer and a lot of the features the MCU will do on its own. I was running into issues at high phase angles (close to 360deg) when the interrupt would happen before the light had turned off, my solution was to blink every other time the sensor sent a pulse. Circuit Diagram Note I re boot loaded making PD7 The reprogram pin, because PB0 functionality with the timer .I marked up the block diagram to show the 'flow' of the program. Also a Timing diagram with actions 1-4 doing the following

1) Puts TCNT1 into ICR1, Interrupts (TIMER1_CAPT_vect) Resets TCNT1, Calculates the time B and C based on ICR1, Sets OCR1A to point B, Sets to turn on OC1A on match , Clears EveryOther.

2) Sets OCR1A, Interrupts (TIMER1_COMPA_vect): Sets OCR1A to point C (saved as A2), Sets to turn off OC1A on match.

3) Clears OCR1A.

4) Puts TCNT1 into ICR1, Interrupts (TIMER1_CAPT_vect) just sets EveryOther.

July 03, 2020
by scootergarrett
scootergarrett's Avatar

Sorry picture links didn't work reposing

I used the 16bit timer and a lot of the features the MCU will do on its own. I was running into issues at high phase angles (close to 360deg) when the interrupt would happen before the light had turned off, my solution was to blink every other time the sensor sent a pulse. Circuit Diagram Note I re boot loaded making PD7 The reprogram pin, because PB0 functionality with the timer .I marked up the block diagram to show the 'flow' of the program. Also a Timing diagram with actions 1-4 doing the following

July 03, 2020
by scootergarrett
scootergarrett's Avatar

The results are hard to show. I have a set up with a fan spinning with a magnet attached that a hall effect sensors is picking up. I'm using a 10W LED and turning off all the lights. the fan appears to hold still but when I record it the video is flashy, shutter speed issues. Here are some scope pictures, (side note the analog discovery 2 is amazing)

input signal-blue ouput signal-yellow

90 deg phase at 750Hz

90 deg phase at 100Hz

Now I'm done

July 13, 2020
by bluestang
bluestang's Avatar

I am here, but sorry i am no good with Timers.

August 27, 2020
by sask55
sask55's Avatar

Hi

Interesting approach.

I assume there is some reason you have decided to use a phase angle approach in this project. It seams this approach is more suited to giving the appearance of stopping the motion at a selected angular rotation position then giving the illusion of slow motion. i.e if the rotation position of the trigger timing is at 0 degrees (12 o'clock) but you wish give the illusion that the trigger position is at 90 degrees (3 o'clock) then this setup is ideal. You can select any angular position you like to give the illusion of no movement i.e. stopped at that position.

However, if your goal is to give the illusion of slow motion in ether direction then is seams to me what you want is a flashing strobe that is out of sink with the input signal. Instead of varying the phase angle the system would simple increase (appears to move backwards) or decrease (appease to move forwards) the frequency of the strobe by relatively small amounts. The system could measure the frequency of the input signals. The output could be set to flash at any factor of that frequency to give the illusion of a stopped rotation. This assumes that there is limited other sources of light and is most effective at strobe frequencies above ~30 HZ. The frequency adjustment can easily be calculated and applied to give the illusion of any speed of slow motion rotation that is selected.

August 28, 2020
by scootergarrett
scootergarrett's Avatar

Once I got it working at a set phase angel I just made the phase angle variable slowly increase 0-360

Post a Reply

Please log in to post a reply.

Did you know that interrupts can be used to trigger pieces of code when events happen? Learn more...