編集履歴一覧に戻る
verylowfreqのアイコン画像

verylowfreq が 2023年04月24日10時51分36秒 に編集

初版

タイトルの変更

+

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

タグの変更

+

AVR

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

製作品

本文の変更

+

AVR ATtiny44Aを使いたくて、「30分専用タイマー」を作りました。手軽なハンダ付けがしたくて、回路はできる限り単純化しています。 Made a 30-minutes timer to use an AVR ATtiny44A with simple circuit for easy soldering. 解説動画 / My video: https://www.youtube.com/watch?v=1MgynYdFvoQ ## 概要 / Abstraction 使用したマイコン / Microcontroller: AVR ATtiny44A: ROM 4KB, RAM 256Byte, GPIO 11 (12) 機能 / Function: 約30分を計測する。だいたいの経過時間を見れる。 大きさ / Size: 70x52x26 mm 秋月電子C基板を使用。 ## 回路 / Schematic ![Schematic](https://camo.elchika.com/2e325ab52aa766e7192fb831bd476c42d11e0a11/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f32376633303665652d656131352d343334662d626333342d6162343139336638333839392f38396330353835652d616638302d343335322d393930382d303930316164663637663338/) 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の電池持ちはまだ不明。 - 背面はアクリル板でカバーしているものの正面はむき出して、ボタンの誤操作がたまにあるので、正面側のカバーもなにかしら用意したい。