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

Post a Reply

Please log in to post a reply.

Did you know that LEDs (light emitting diodes) only conduct current in one direction, like normal diodes? Learn more...