つれづれなる備忘録

日々の発見をあるがままに綴る

TinkercadによるArduinoシミュレーション24 ~ ライブラリの追加

1. Tinkercadのライブラリについて

 今回はTinkercadのライブラリに入っていないものを使用する方法を紹介する。ただし、ユーザ側からヘッダーファイルやソースファイルをアップロードすることはできないので、基本的にはTinkercad側からシステムのアップグレードなどでライブラリが追加されない限りは、新たなライブラリを使用することはできない。なお現状使用可能なライブラリはEEPROM, IRremote, LiquidCrystal, Keypad, Neopixel, Servo, SoftwareSerial, Wire, SD, SPI, Stepperとなっている。これらのライブラリは#includeとすればロードすることができる。

2. ライブラリ追加方法

 Tinkercadに入っていないライブラリを使用するには、そのライブラリのヘッダーファイルとソースファイルをコードにコピーする。(Circuit Simulator参照) ただし、コピーするヘッダー、ソースに他のライブラリが含まれていて、Tinkercadでカバーしていないと他のライブラリのヘッダー、ソースファイルの中身もコピーすることになるのでかなり冗長になってしまう。 Circuit Simulatorにあるようにタイマーに関するライブラリの1つであるMsTimer2を例として、スケッチは少々長いので最下部に示す。 Setup関数内のMsTimer2::set(1000, overflow);は1000ms(1s)でオーバーフローし、そのときに関数overflow()を呼び出すように設定する。次にMsTimer2::start();として割り込みを有効にする。 overflow()では割り込みが発生したらmillis()を用いてプログラム開始からの経過時間をシリアルモニタに表示するようにしている。

3. まとめ

 今回はTinkercadに入っていないライブラリの使用方法について紹介した。

// ****** from here MsTimer2.h ******
namespace MsTimer2 {
    extern unsigned long msecs;
    extern void (*func)();
    extern volatile unsigned long count;
    extern volatile char overflowing;
    extern volatile unsigned int tcnt2;
    
    void set(unsigned long ms, void (*f)());
    void start();
    void stop();
    void _overflow();
}

// ****** from here MsTimer2.cpp ******
unsigned long MsTimer2::msecs;
void (*MsTimer2::func)();
volatile unsigned long MsTimer2::count;
volatile char MsTimer2::overflowing;
volatile unsigned int MsTimer2::tcnt2;
#if defined(__arm__) && defined(TEENSYDUINO)
static IntervalTimer itimer;
#endif

void MsTimer2::set(unsigned long ms, void (*f)()) {
    float prescaler = 0.0;
    
    if (ms == 0)
        msecs = 1;
    else
        msecs = ms;
        
    func = f;

#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
    TIMSK2 &= ~(1<<TOIE2);
    TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
    TCCR2B &= ~(1<<WGM22);
    ASSR &= ~(1<<AS2);
    TIMSK2 &= ~(1<<OCIE2A);
    
    if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) {  // prescaler set to 64
        TCCR2B |= (1<<CS22);
        TCCR2B &= ~((1<<CS21) | (1<<CS20));
        prescaler = 64.0;
    } else if (F_CPU < 1000000UL) { // prescaler set to 8
        TCCR2B |= (1<<CS21);
        TCCR2B &= ~((1<<CS22) | (1<<CS20));
        prescaler = 8.0;
    } else { // F_CPU > 16Mhz, prescaler set to 128
        TCCR2B |= ((1<<CS22) | (1<<CS20));
        TCCR2B &= ~(1<<CS21);
        prescaler = 128.0;
    }
#elif defined (__AVR_ATmega8__)
    TIMSK &= ~(1<<TOIE2);
    TCCR2 &= ~((1<<WGM21) | (1<<WGM20));
    TIMSK &= ~(1<<OCIE2);
    ASSR &= ~(1<<AS2);
    
    if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) {  // prescaler set to 64
        TCCR2 |= (1<<CS22);
        TCCR2 &= ~((1<<CS21) | (1<<CS20));
        prescaler = 64.0;
    } else if (F_CPU < 1000000UL) { // prescaler set to 8
        TCCR2 |= (1<<CS21);
        TCCR2 &= ~((1<<CS22) | (1<<CS20));
        prescaler = 8.0;
    } else { // F_CPU > 16Mhz, prescaler set to 128
        TCCR2 |= ((1<<CS22) && (1<<CS20));
        TCCR2 &= ~(1<<CS21);
        prescaler = 128.0;
    }
#elif defined (__AVR_ATmega128__)
    TIMSK &= ~(1<<TOIE2);
    TCCR2 &= ~((1<<WGM21) | (1<<WGM20));
    TIMSK &= ~(1<<OCIE2);
    
    if ((F_CPU >= 1000000UL) && (F_CPU <= 16000000UL)) {  // prescaler set to 64
        TCCR2 |= ((1<<CS21) | (1<<CS20));
        TCCR2 &= ~(1<<CS22);
        prescaler = 64.0;
    } else if (F_CPU < 1000000UL) { // prescaler set to 8
        TCCR2 |= (1<<CS21);
        TCCR2 &= ~((1<<CS22) | (1<<CS20));
        prescaler = 8.0;
    } else { // F_CPU > 16Mhz, prescaler set to 256
        TCCR2 |= (1<<CS22);
        TCCR2 &= ~((1<<CS21) | (1<<CS20));
        prescaler = 256.0;
    }
#elif defined (__AVR_ATmega32U4__)
    TCCR4B = 0;
    TCCR4A = 0;
    TCCR4C = 0;
    TCCR4D = 0;
    TCCR4E = 0;
    if (F_CPU >= 16000000L) {
        TCCR4B = (1<<CS43) | (1<<PSR4);
        prescaler = 128.0;
    } else if (F_CPU >= 8000000L) {
        TCCR4B = (1<<CS42) | (1<<CS41) | (1<<CS40) | (1<<PSR4);
        prescaler = 64.0;
    } else if (F_CPU >= 4000000L) {
        TCCR4B = (1<<CS42) | (1<<CS41) | (1<<PSR4);
        prescaler = 32.0;
    } else if (F_CPU >= 2000000L) {
        TCCR4B = (1<<CS42) | (1<<CS40) | (1<<PSR4);
        prescaler = 16.0;
    } else if (F_CPU >= 1000000L) {
        TCCR4B = (1<<CS42) | (1<<PSR4);
        prescaler = 8.0;
    } else if (F_CPU >= 500000L) {
        TCCR4B = (1<<CS41) | (1<<CS40) | (1<<PSR4);
        prescaler = 4.0;
    } else {
        TCCR4B = (1<<CS41) | (1<<PSR4);
        prescaler = 2.0;
    }
    tcnt2 = (int)((float)F_CPU * 0.001 / prescaler) - 1;
    OCR4C = tcnt2;
    return;
#elif defined(__arm__) && defined(TEENSYDUINO)
    // nothing needed here
#else
#error Unsupported CPU type
#endif

    tcnt2 = 256 - (int)((float)F_CPU * 0.001 / prescaler);
}

void MsTimer2::start() {
    count = 0;
    overflowing = 0;
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || defined (__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
    TCNT2 = tcnt2;
    TIMSK2 |= (1<<TOIE2);
#elif defined (__AVR_ATmega128__)
    TCNT2 = tcnt2;
    TIMSK |= (1<<TOIE2);
#elif defined (__AVR_ATmega8__)
    TCNT2 = tcnt2;
    TIMSK |= (1<<TOIE2);
#elif defined (__AVR_ATmega32U4__)
    TIFR4 = (1<<TOV4);
    TCNT4 = 0;
    TIMSK4 = (1<<TOIE4);
#elif defined(__arm__) && defined(TEENSYDUINO)
    itimer.begin(MsTimer2::_overflow, 1000);
#endif
}

void MsTimer2::stop() {
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || defined (__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
    TIMSK2 &= ~(1<<TOIE2);
#elif defined (__AVR_ATmega128__)
    TIMSK &= ~(1<<TOIE2);
#elif defined (__AVR_ATmega8__)
    TIMSK &= ~(1<<TOIE2);
#elif defined (__AVR_ATmega32U4__)
    TIMSK4 = 0;
#elif defined(__arm__) && defined(TEENSYDUINO)
    itimer.end();
#endif
}

void MsTimer2::_overflow() {
    count += 1;
    
    if (count >= msecs && !overflowing) {
        overflowing = 1;
        count = count - msecs; // subtract ms to catch missed overflows
                    // set to 0 if you don't want this.
        (*func)();
        overflowing = 0;
    }
}

#if defined (__AVR__)
#if defined (__AVR_ATmega32U4__)
ISR(TIMER4_OVF_vect) {
#else
ISR(TIMER2_OVF_vect) {
#endif
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || defined (__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
    TCNT2 = MsTimer2::tcnt2;
#elif defined (__AVR_ATmega128__)
    TCNT2 = MsTimer2::tcnt2;
#elif defined (__AVR_ATmega8__)
    TCNT2 = MsTimer2::tcnt2;
#elif defined (__AVR_ATmega32U4__)
    // not necessary on 32u4's high speed timer4
#endif
    MsTimer2::_overflow();
}
#endif // AVR
// ****** end of MsTimer2.cpp ******

void overflow() {
  Serial.print("Over flow!: ");
  Serial.print(millis());
  Serial.println("ms");
}

void setup() {
  Serial.begin(9600);
  MsTimer2::set(1000, overflow);
  MsTimer2::start();
}

void loop() {
  // loopは空
}