Teensy 3.2 Interrups to Blink LED's at Different Rates

Status
Not open for further replies.

bringfire

Active member
I am working on a Data Logger/GPS wayfinder using the Teensy 3.2, that uses LED buttons as the interface for letting the user know what state the Data Logger is in at any given time, i.e.

blinking one LED fast = currently logging
blinking a different LED = something else
etc...

I am currently using the Metro library or millis() to manage the blinking rates, but I am continuously having to poll for an updated time, to determine whether to turn on or off the LED's, and this leads to gaps in timing, which leads to erratic blinking.

I would like the LED's to blink at whatever rate I set for them regardless of what other functions I am executing at any given time (within human perceivable limits of course, I know that nothing is simultaneous), and be able to change that rate at different points in my program to suit the desired LED blink rate.

Which leads me to the possibility of using timer interrupts or other interrupts to accomplish the task.

Looking at the example given on this page: https://www.pjrc.com/teensy/td_timing_IntervalTimer.html I can see how setting up a timer to blink an LED at a given rate might work out well, but it is unclear if using this method will allow me to change the interval during the course of my program, and how to accomplish this.

Can I call this method at any point in my program to change the timer settings? Or can it only be set once at the beginning?

myTimer.begin(blinkLED, 150000);

If so, then this seems to be the clearest path. If not, then I'll need to find another way.

In general, am I on the right track in thinking that interrupts are the way to proceed?

Thanks in advance for any help and advice.
 
Can I call this method at any point in my program to change the timer settings? Or can it only be set once at the beginning?

myTimer.begin(blinkLED, 150000);
As far as I remember, you can do this, but you need to call myTimer.end(); between two .begins.
Only four timers may be active simultaneously, even less if you use other libraries that use timers.

In general, am I on the right track in thinking that interrupts are the way to proceed?

Thanks in advance for any help and advice.

Yeah it's certainly a viable solution. The other way I could imagine is finding out why your loop() takes so long to complete that it visibly influences the LED blinking. Maybe you could find and fix that?
Side note: using the special elapsedMillis data type makes it easier to blink LEDs than using millis(), may result in cleaner code: https://www.pjrc.com/teensy/td_timing_elaspedMillis.html

Ben
 
As far as I remember, you can do this, but you need to call myTimer.end(); between two .begins.
Only four timers may be active simultaneously, even less if you use other libraries that use timers.

Great! I will look into this as a possibility. I'll have to check to see how/if the Debounce library I am using utilizes interrupts.

Yeah it's certainly a viable solution. The other way I could imagine is finding out why your loop() takes so long to complete that it visibly influences the LED blinking. Maybe you could find and fix that?

The function that is causing me problems with blink timing is the function that retrieves/encodes/writes the GPS serial data to the SPI flash chip. I can look into speeding it up. I thought about adding a bunch of millis() or ellapsedMillis() checks between every line of the code, but that seemed a very dirty way to solve the problem...although ellapsedMillis() would certainly clean that approach up a bit.

Thanks for the tips! Time to dive into interrupts..actually pretty excited about the opportunity to learn more about using them.

Aaron
 
There is no need to waste a precious timer. You already have the systick timer running at 1000Hz which uses an interrupt. Here is a demonstration sketch overriding the systick timer interrupt. To change the blink mode simply assign a new value to a LedCtrlArray member.
Code:
const size_t LED_COUNT = 4;

struct LedCtrl {
    // LED on time in milliseconds
    volatile uint32_t led_on_time;
    // LED off time in milliseconds
    volatile uint32_t led_off_time;
    // count milliseconds in current on + off cycle
    volatile uint32_t led_timer_counter;
};

const LedCtrl LED_BLINK_FAST = { 100, 100, 0 };
const LedCtrl LED_BLINK_SLOW = { 500, 500, 0 };
const LedCtrl LED_NOBLINK_ON = { 100, 0, 0 };
const LedCtrl LED_NOBLINK_OFF = { 0, 100, 0 };

typedef LedCtrl LedCtrlArray[LED_COUNT];
LedCtrlArray led_ctrl_array;

const uint8_t led_pins[] = { 13, 15, 16, 17 };

void systick_isr() {
    // Original ISR code
    systick_millis_count++;

    // Our ISR code
    for(size_t led_nr = 0; led_nr < LED_COUNT; led_nr++) {
        auto& led_ctrl = led_ctrl_array[led_nr];
        auto led_cycle_time = led_ctrl.led_on_time + led_ctrl.led_off_time;
        if(led_ctrl.led_timer_counter < led_ctrl.led_on_time) {
            digitalWrite(led_pins[led_nr], HIGH);
            led_ctrl.led_timer_counter++;
        } else if(led_ctrl.led_timer_counter < led_cycle_time) {
            digitalWrite(led_pins[led_nr], LOW);
            led_ctrl.led_timer_counter++;
        } else {
            led_ctrl.led_timer_counter = 0;
        }
    }
}


void setup() {
    for(auto pin : led_pins) pinMode(pin, OUTPUT);
    led_ctrl_array[0] = LED_BLINK_FAST;  // LED on pin 13 blinks fast
    led_ctrl_array[1] = LED_BLINK_SLOW;  // LED on pin 15 blinks slowly
    led_ctrl_array[2] = LED_NOBLINK_ON;  // LED on pin 16 turned on
    led_ctrl_array[3] = LED_NOBLINK_OFF; // LED on pin 17 turned off
}

void loop() {
}

I'm writing the output pins in each interrupt cycle so that synchronization glitches get automagically fixed. Both main program and ISR may read/write led_ctrl_array concurrently and this way I can avoid disabling interrupts.
 
@tni

This is super helpful! I'll look at this carefully and trying implementing it.

Many thanks for the help.

Aaron
 
@tni's code works great...I'm cobbling together an understanding of tni's code posted above.

Somethings that were implicit in the code that I had to research/learn:

The "systick_isr()" function is already defined, perhaps here: http://www.pjrc.com/teensy/beta/mk20dx128.c so that is why it works and doesn't require any further definition to be recognized as an interrupt routine.

The "systick_isr()" function contains by default the line of code "systick_millis_count++;" as tni's code comment indicates. The other parts of the systick_isr() function are added.

This seems like a different approach from the one posted here: https://www.pjrc.com/teensy/interrupts.html

I'll have to dig deeper to understand to what extent these approaches are different and might be used for different reasons.

Thanks again for the big headstart.:D



Aaron
 
The guidance on interrupts on the linked page applies - keep it short and essential without calling anything time consuming (digital I/O changes are fast) - except this one is already active with all the setup done as it is provides a basic system function.

When you create your own 'copy' of that code it replaces the supplied version - so the default behavior has to be replicated or it will be lost.
 
Status
Not open for further replies.
Back
Top