Lighthouses 2

Just like the Knightrider 2 this is another port of one of my experiments to the Arduino Due. This time I port the lighthouses experiment. The challenge with this experiment is not the pin mappings but the timer. For the original lighthouses experiment I used the MsTimer2 library. See the crucial piece of code below.

#include <MsTimer2.h>

// ...

void setup() {
    MsTimer2::set(1, blink);
    MsTimer2::start();
}

Unfortunately this does not compile for the Due. Searching the internet I found out that ARM Cortex processors provide a “systicks” timer which typically runs at 100 Hz. As it turns out Atmel decided to implement it differently such that it runs at 1 kHz. Lucky me. This is exactly what I need. So the next question was how to register for the systicks.

A short call of “grep sysTick -i -r .” revealed the relevant source lines.

./hardware/arduino/sam/cores/arduino/cortex_handlers.c:extern int sysTickHook(void);
./hardware/arduino/sam/cores/arduino/cortex_handlers.c: if (sysTickHook())
./hardware/arduino/sam/cores/arduino/hooks.c:int sysTickHook(void) __attribute__ ((weak, alias("__false")));

Looking into the files it becomes clear that sysTickHook is implemented by Arduino as a weak symbol. That is: if I define this symbol it will overwrite the default. However my first attempt below did not work.

// sysTicks will be triggered once per 1 ms
int sysTickHook(void) {
    blink();
    return 0;
}

Some more searching turned out that CMSIS low level system API is compiled in C instead of C++. So the compiler must be explicitly told that this is a C symbol.

extern "C" {
    // sysTicks will be triggered once per 1 ms
    int sysTickHook(void) {
        blink();
        return 0;
    }
}

And hurray it works. The full solution now looks as exhibited below.

//
//  www.blinkenlight.net
//
//  Copyright 2015 Udo Klein
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see http://www.gnu.org/licenses/


uint8_t ledpin(const int8_t led) {
    return led<14? led: led+(54-14);
}

uint8_t setOutput(uint8_t led) {
    pinMode(ledpin(led), OUTPUT);
    return 0;
}

template <uint8_t led, uint32_t d1, uint32_t d2, uint32_t d3, uint32_t d4,
                       uint32_t d5, uint32_t d6, uint32_t d7, uint32_t d8>
void light_my_fire() {
    static uint32_t phase = setOutput(led);

    phase = phase < d1+d2+d3+d4+d5+d6+d7+d8-1? phase+1: 0;

    digitalWrite(ledpin(led), phase < d1                  ? HIGH:
                              phase < d1+d2               ? LOW:
                              phase < d1+d2+d3            ? HIGH:
                              phase < d1+d2+d3+d4         ? LOW:
                              phase < d1+d2+d3+d4+d5      ? HIGH:
                              phase < d1+d2+d3+d4+d5+d6   ? LOW:
                              phase < d1+d2+d3+d4+d5+d6+d7? HIGH:
                                                            LOW);
}

extern "C" {
    // sysTicks will be triggered once per 1 ms
    int sysTickHook(void) {
        blink();
        return 0;
    }
}

void blink() {
    light_my_fire<0,  200, 2800,  200, 2800,  200, 5800,    0,    0>();  // Norderney
    light_my_fire<1, 3000, 3000, 3000, 3000,    0, 8500,    0,    0>();  // Pilsum
    light_my_fire<2,  700, 2300,  700, 2300,  700, 2300,  700, 5300>();  // Campen
    light_my_fire<3, 6000, 6000,    0,    0,    0,    0,    0,    0>();  // Schillig
    light_my_fire<4, 6000, 3000,    0,    0,    0,    0,    0,    0>();  // Voslap
    light_my_fire<5, 6000, 1000,    0,    0,    0,    0,    0,    0>();  // Tossens
    light_my_fire<6, 1000, 1000,    0,    0,    0,    0,    0,    0>();  // test1
    light_my_fire<7,  500,  500,    0,    0,    0,    0,    0,    0>();  // test2
}


void setup() {}
void loop() {}

3 Responses to Lighthouses 2

  1. Martin says:

    Thank you very much for sharing your code. I’m looking to implement something very similar, but get the following error when compiling your example. Any ideas what might be going wrong here?

    Lighthouse:48: error: previous declaration of ‘int sysTickHook()’ with ‘C++’ linkage
    int sysTickHook(void) {
    ^
    Lighthouse:48: error: conflicts with new declaration with ‘C’ linkage
    int sysTickHook(void) {
    ^
    exit status 1
    previous declaration of ‘int sysTickHook()’ with ‘C++’ linkage

    Thanks in advance!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.