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

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プリンターを楽しんでいます!
ログインしてコメントを投稿する