verylowfreq
2023年04月24日作成 (2023年04月24日更新)
製作品 1202
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
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
さんが
2023/04/24
に
編集
をしました。
(メッセージ: 初版)
-
verylowfreq
さんが
2023/04/24
に
編集
をしました。
ログインしてコメントを投稿する