verylowfreqのアイコン画像
verylowfreq 2023年04月24日作成 (2023年04月24日更新)
製作品 製作品 閲覧数 898
verylowfreq 2023年04月24日作成 (2023年04月24日更新) 製作品 製作品 閲覧数 898

AVRで30分専用タイマーを作る

AVRで30分専用タイマーを作る

AVR ATtiny44Aを使いたくて、「30分専用タイマー」を作りました。手軽なハンダ付けがしたくて、回路はできる限り単純化しています。

Made a 30-minutes timer to use an AVR ATtiny44A with simple circuit for easy soldering.

解説動画 / My video:

ここに動画が表示されます

概要 / Abstraction

使用したマイコン / Microcontroller:
AVR ATtiny44A: ROM 4KB, RAM 256Byte, GPIO 11 (12)

機能 / Function:
約30分を計測する。だいたいの経過時間を見れる。

大きさ / Size:
70x52x26 mm
秋月電子C基板を使用。

回路 / Schematic

Schematic

LEDの電流制限抵抗は省略しました。抵抗を入れるとハンダ付け箇所が増えるので。
ピン数が足りないので、ブザー(圧電素子)とボタンを1ピンで処理しています。
動作クロックは内蔵RC発振です。ピン数が足りないのと、部品点数が増えてしまうので。時間の精度はいまいちですが、厳密な計測をしたいわけではないので、妥協です。

ファームウェア / Firmware

ATtinyCoreを使うとArduino環境で扱えるので、Arduinoの範囲からあまり逸れない範囲で書きました。
Arduino code with ATtinyCore board library.

/** MIT License (c) 2023 verylowfreq */

#include <avr/sleep.h>

const unsigned long TIMER_TIME_MILLIS = 30L * 60 * 1000;

#define LED_COUNT 10
int pins_led[LED_COUNT] = { 0,1,2,3,4,5,6,7,10,9 };
int pins_led_count = sizeof(pins_led) / sizeof(pins_led[0]);

// ブザーとボタンが接続されたピン番号
const int PIN_BUTTON_BUZZER = 8;

// ボタン押下時の割り込み関数
// スリープから復帰するためだけなので、なにもしない
EMPTY_INTERRUPT(INT0_vect);

// 中断可能なブザーを、指定時間だけ鳴らす
// @param duration - 鳴らす最大時間(ミリ秒)
// @param stop_if_presseed - ボタンが押されたらブザーを中断するか?
// @returns ボタン押下により中断したらtrue
bool sound_buzzer(unsigned int duration, bool stop_if_pressed) {
  bool stop_by_pressed = false;
  duration = duration;
  while (duration > 0) {
    pinMode(PIN_BUTTON_BUZZER, INPUT_PULLUP);
    delayMicroseconds(200);

    pinMode(PIN_BUTTON_BUZZER, OUTPUT);
    digitalWrite(PIN_BUTTON_BUZZER, LOW);
    delayMicroseconds(500);
    pinMode(PIN_BUTTON_BUZZER, INPUT_PULLUP);
    delayMicroseconds(300);

    if (stop_if_pressed && digitalRead(PIN_BUTTON_BUZZER) == LOW) {
      stop_by_pressed = true;
      break;
    }
    --duration;
  }
  pinMode(PIN_BUTTON_BUZZER, OUTPUT);
  digitalWrite(PIN_BUTTON_BUZZER, LOW);

  return stop_by_pressed;
}

void wait_button_released(void) {
  pinMode(PIN_BUTTON_BUZZER, INPUT_PULLUP);

  do {
    delay(10);
  } while (digitalRead(PIN_BUTTON_BUZZER) == LOW);

  delay(10);
}

void standby(void) {
  pinMode(PIN_BUTTON_BUZZER, INPUT_PULLUP);

  // ボタン押下されていたら、離されるまで待機する
  wait_button_released();

  // INT0のLOWレベル割り込みを有効にする
  MCUCR = MCUCR & 0xfc;
  GIMSK |= _BV(INT0);
  // 常時走っているTimer0を一時的に止める
  PRR |= _BV(PRTIM0);
  // 最も消費電力の少ないスタンバイモードを指定する
  set_sleep_mode(SLEEP_MODE_STANDBY);
  
  // スリープモードへ入る
  sleep_mode();

  // INT0割り込みを無効にする
  GIMSK &= ~_BV(INT0);
  // Timer0を再開する(時間系の関数を呼び出す前に)
  PRR &= ~_BV(PRTIM0);

  // ボタンが離されるのを待機する
  wait_button_released();

  sound_buzzer(250, false);

  digitalWrite(pins_led[LED_COUNT - 1], HIGH);
  delay(500);
  digitalWrite(pins_led[LED_COUNT - 1], LOW);
  delay(500);
}


void setup() {
  // LEDを消灯状態で初期化する
  for (int i = 0; i < pins_led_count; i++) {
    pinMode(pins_led[i], OUTPUT);
    digitalWrite(pins_led[i], LOW);
  }

  // 起動時のブザー
  sound_buzzer(250, false);

  delay(1);
  digitalWrite(PIN_BUTTON_BUZZER, LOW);
  delay(1);
  pinMode(PIN_BUTTON_BUZZER, INPUT_PULLUP);
  standby();
}


void loop() {

  // タイマーはスタート済みか?
  static bool is_started = false;
  // タイマーがスタートした時間 (ミリ秒。millis()の返り値)
  static unsigned long start_millis = 0;
  // 点灯させるLEDの番号 [0..9]
  static int led_index = -1;
  // LED点滅について、前回の状態変更の時間
  static unsigned long prev_led_blink_changed_millis = 0;
  // LEDはいま点灯させているか
  static bool is_led_on = true;
  // ボタンが押され始めたか?
  static bool is_button_pressing_started = false;
  // ボタンが押され始めた時間 (ミリ秒。millis()の返り値)
  static unsigned long button_pressed_start_millis = 0;
  // 最初の1区分目のブザーをもう鳴らしたか?
  static bool fired_one_of_ten_buzzer = false;

  // 必要ならタイマーをスタートする

  if (!is_started) {
    is_started = true;
    start_millis = millis();
    led_index = -1;
    prev_led_blink_changed_millis = start_millis;
    is_led_on = true;
    is_button_pressing_started = false;
    button_pressed_start_millis = 0;
    fired_one_of_ten_buzzer = false;
  }

  // LEDを更新する

  // いったんすべて消灯する
  for (int i = 0; i < pins_led_count; i++) {
    digitalWrite(pins_led[i], LOW);
  }

  if (led_index < 0) {
    // 異常時には両端を点灯する
    digitalWrite(pins_led[0], HIGH);
    digitalWrite(pins_led[9], HIGH);
    delay(1);
    digitalWrite(pins_led[0], LOW);
    digitalWrite(pins_led[9], LOW);
    delay(4);

  } else if (is_led_on) {
    if (millis() - prev_led_blink_changed_millis >= 500) {
      // LEDは消灯に遷移する
      is_led_on = false;
      prev_led_blink_changed_millis = millis();
    } else {
      // LEDを点灯させる
      digitalWrite(pins_led[led_index], HIGH);
      delay(1);
      digitalWrite(pins_led[led_index], LOW);
      delay(2);
    }
  } else {
    // LEDは消灯させたまま
    if (millis() - prev_led_blink_changed_millis >= 500) {
      // 次回は点灯させる
      is_led_on = true;
      prev_led_blink_changed_millis = millis();
    }
  }

  // ボタンの状態を処理する

  pinMode(PIN_BUTTON_BUZZER, INPUT_PULLUP);
  delayMicroseconds(100);
  bool button_pressed = digitalRead(PIN_BUTTON_BUZZER) == LOW;
  if (!is_button_pressing_started && button_pressed) {
    // ボタンの押下が始まったとき
    is_button_pressing_started = true;
    button_pressed_start_millis = millis();

  } else if (is_button_pressing_started) {
    // ボタンの押下がはじまっていたとき

    unsigned long button_pressed_time = millis() - button_pressed_start_millis;
    if (button_pressed_time > 1000) {
      // 押下が長押ししきい値を超えている場合
      is_started = false;
      standby();
      return;

    } else if (!button_pressed) {
      // 長押しではなく、ボタンの押下が終了していた時
      is_button_pressing_started = false;
    }
  }

  // タイマーの経過時間に応じた処理

  unsigned long elapsed_millis = millis() - start_millis;

  // タイマーの最初の1区分が経過したときは短いブザーを鳴らす
  if (!fired_one_of_ten_buzzer && elapsed_millis > (TIMER_TIME_MILLIS / LED_COUNT)) {
    fired_one_of_ten_buzzer = true;
    digitalWrite(pins_led[0], HIGH);
    sound_buzzer(1000, true);
  }

  // タイマーの時間が完了したとき
  if (elapsed_millis > TIMER_TIME_MILLIS) {
    // Time-up
    sound_buzzer(5000, true);
    is_started = false;
    standby();
    return;
  }

  // 経過時間に応じて次回のLEDの点灯を更新する
  led_index = elapsed_millis / 1000UL / (TIMER_TIME_MILLIS / 1000UL / LED_COUNT);
}

感想

  • ブザーの音量はそこそこ。3Vで直接駆動しているので、仕方ないか。
  • CR2032の電池持ちはまだ不明。
  • 背面はアクリル板でカバーしているものの正面はむき出して、ボタンの誤操作がたまにあるので、正面側のカバーもなにかしら用意したい。
1
verylowfreqのアイコン画像
"verylowfreq" あるいは 「三峰スズ」(VTuber 2023年2月より) です。趣味で電子工作や3Dプリンターを楽しんでいます!
ログインしてコメントを投稿する