ATtiny85のスリープモードを使ってみよう

こんにちは、くのへ@MasazaneKunoheです。

今日はATtiny85のスリープモードの使い方を紹介します!

スリープモードについて

スリープモードとは、ATtiny85のようなマイクロコンピュータを必要最低限の機能のみ生かして休眠させることです。
これにより、マイクロコンピュータが消費する電力をぐっと抑えることが出来ます。

完全に停止させるわけではなく、こちらの記事で紹介した「ピンチェンジ割込み」機能をキックにして復旧させることが出来ます。

ずっとマイクロコンピュータを起こしておく必要が無い場合、、、例えば、音を検知するまでは何もしない装置などではスリープモードにしておくと消費電力を抑えることが出来るため便利です。

スリープモードについて

ATtiny85で使用できるスリープモードには以下のような種類があります。(データシートより)

難解ですね、、、。

私なりに要約してみました。こちらです↓

モード名消費電力概要コード
Power_downモード最も抑えられるCPUクロックと全ての周辺機器が停止する。
クロックとI/Oラインの状態は維持される。
SLEEP_MODE_PWR_DOWN
ADC Noise
Reductionモード
Power_downモードよりは高いCPUクロックと大部分の周辺機器は停止し、ADC変換のみ機能する状態。
クロックとI/Oラインの状態は維持される。
SLEEP_MODE_ADC
IdleモードADC Noise
Reductionモードよりも高い
CPUクロックは動作し、マイクロコントローラーはタスクを実行できる。
クロックとI/Oラインの状態は維持される。
但しCPUコアは停止している。
SLEEP_MODE_IDLE

幾つか種類がありますが、この記事を読んでいる方はスリープモードとしてPower_downモードをイメージしていると思います。
そのPower_downモードを使いこなせれば、ほとんどのシチュエーションに対応できると思いますので、Power_downモードを一つ覚えておけばいいかな、と思います。(私もアイドルモードとAD変換雑音低減モードのことを十分に理解出来ていません。。。難しい、、、)

実装方法

スリープモードを使うためには以下のようなプログラミングを行います。
①Sleepモードに関するライブラリをincludeする
 (Arduinoがデフォルトでライブラリを持っているので、ライブラリをインストールする必要はない)
②Sleepモードを使うためのプログラムを書く。
③Sleepモードから復帰するために、「割込み」機能を使うためのプログラムを書く。

このうち、次の解説では①②を実装してみます。
その次の解説で③まで実装してみたいと思います。

サンプルコード(最もシンプル版)

Sleepモードの最もシンプルなコード(スリープまで。復旧無し。)は以下の通りです。

#include <avr/sleep.h> // スリープ用ライブラリの読み込み

void setup() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // スリープモードを設定
}

void loop() {
  sleep_enable(); // スリープモードを有効にします
  sleep_cpu(); // CPUをスリープさせます。ここでATtiny85は休眠します。

  sleep_disable(); // スリープモードを無効にします

}

解説

スリープ用のライブラリ読み込み

次のコードでスリープ用のライブラリを読み込んでいます。

#include <avr/sleep.h> // スリープ用ライブラリの読み込み

このsleep.hというライブラリは、デフォルトでArduinoIDEにインストールされているため、新たなライブラリをインストールする必要はありません。

スリープモードの設定

次のコードでスリープモードを指定しています。

set_sleep_mode(SLEEP_MODE_PWR_DOWN);

この引数「SLEEP_MODE_PWR_DOWN」がPower_downモードを挿しています。
前章にて解説した、次の表の「コード」列に書いてある内容を引数として入力すると、Power_downモードやほかのモードを呼び出せます。

モード名消費電力概要コード
Power_downモード最も抑えられるCPUクロックと全ての周辺機器が停止する。
クロックとI/Oラインの状態は維持される。
SLEEP_MODE_PWR_DOWN
ADC Noise
Reductionモード
Power_downモードよりは高いCPUクロックと大部分の周辺機器は停止し、ADC変換のみ機能する状態。
クロックとI/Oラインの状態は維持される。
SLEEP_MODE_ADC
IdleモードADC Noise
Reductionモードよりも高い
CPUクロックは動作し、マイクロコントローラーはタスクを実行できる。
クロックとI/Oラインの状態は維持される。
但しCPUコアは停止している。
SLEEP_MODE_IDLE

まあ、PowerDownモード以外はイマイチ動作が良く分からないから使わないんだけどね。ガハハ。

スリープを実行する

最後にスリープさせたいところに次のコードを記載します。

  sleep_enable(); // スリープモードを有効にします
  sleep_cpu(); // CPUをスリープさせます。ここでATtiny85は休眠します。

  sleep_disable(); // スリープモードを無効にします

まず、sleep_enable(); と書きます。
これで「set_sleep_mode(スリープモード)」で設定したスリープモードが有効となります。
有効化すると、Loopが周回せずにLoop内のコードが一番下まで来たところでスリープします。

次にsleep_cpu(); と書きます。これで、その場でスリープします。
サンプルコードではsleep_cpu();でスリープするので、復旧した時は、この次の行から再スタートします。

最後に、sleep_disable(); と書きます。これでスリープモードが有効ではなくなり、Loopが再び周回するようになります。復旧するとsleep_cpu()の直後から動き出すので、sleep_deisable()でスリープモードを無効化させるようにしています(まあ、このサンプルコードだと復旧する方法がないんだけどねw)。

ポイントはsleep_cpu(); という命令文であり、その場で休眠に入り、ここでコードは止まります

スリープ状態から復旧させるためには、タイマーで復旧させるなどの方法がありますが、電子工作では「割込み」で復旧させる用途が主な使い方かな、と思います。

次のサンプルコードで復旧も実装してみます。

サンプルコード(ピンチェンジ割込みによる復旧を追加)

こちらにピンチェンジ割込みを使ってスリープモードから復旧可能なようにしたコードを示します。(黄色マーキング部が追加コードです)

ピンチェンジ割込みについてはこちらの記事を参照ください。

#include "PinChangeInterrupt.h" //ピンチェンジ用ライブラリの読み込み
#include <avr/sleep.h> // スリープ用ライブラリの読み込み

//ピンチェンジ割込みピンの設定 7ピン(PB2ピン)をピンチェンジ割込みピンにする
#define PCINTPin 2

void setup() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // スリープモードを設定
    attachPCINT(digitalPinToPCINT(PCINTPin),WakeUP, CHANGE); // ピンチェンジ割込みを有効化
}

void loop() {
  sleep_enable(); // スリープモードを有効にします
  sleep_cpu(); // CPUをスリープさせます。ここでATtiny85は休眠します。

  sleep_disable(); // スリープモードを無効にします

}

void WakeUP(){
  //何も処理しなくてOK。
}

ピンチェンジ割込みを有効化し、7ピン(PB2ピン)の電圧が変化した時にWakeUPという関数が動作するだけの処理を追加したものです。

PowerDownモードのスリープはピンチェンジ割込みが発生すると復旧します。
割込み時の処理は何でもよく、今回のサンプルコードのように何も処理しなくてもOKです。とにかく何かの割込みが発生するとATtiny85がスリープから目覚めるのです。

これで、sleep_cpu(); でスリープ状態に入っていたプログラムを次の行のコードに進めるが出来ます。
ただし目覚めたものの、スリープモードが解除されたわけではないので、このままだとLoopの一番下までコードが実行されると再びスリープしてしまいます。

そこで、復旧した直後にsleep_disable(); を動作させることで、スリープモードを解除し、Loopが周回出来るようにするのです。

まあ、このサンプルコードだと、Loopした直後にsleep_enable(); とsleep_cpu(); が働き、すぐに再び休眠に入ってしまうんだけどね、ガハハ。

スリープモードを使った例

この記事で作った音楽を鳴らすコードに手を加え、スリープおよび復旧できるようにしてみます。

こちらがそのコードです。黄色マーキングした部分がスリープモードおよび復旧に関する部分です。

#include "PinChangeInterrupt.h"
#include "avr/sleep.h"

//ピンの設定
#define soundPin 4 //D4ピン(3番ピン)を音の出力ピンとする

//音階データ

//おさかな天国
int scale[] = {
  783,783,783,783,783,783,783,880,987,0,0,0,0,0,880,987,
  1046,0,1046,0,1046,1046,0,987,880,0,0,0,0,0,0,0,

  739,739,739,739,739,739,739,783,880,0,0,0,0,0,880,880,
  783,0,783,0,783,783,0,659,783,0,0,0,0,0,0,0
};

//音の長さ
int time[] ={
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
  250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250
};

//配列の長さ
int scaleLen = sizeof(scale) / sizeof(int);

//loop用のカウンター
int i=0;

//ピンチェンジ割込みピン
#define PCINTPin 2
//スリープさせるためのピン
#define SleepPin 1


void setup() {
  // put your setup code here, to run once:

  pinMode(soundPin, OUTPUT);

  pinMode(SleepPin, INPUT_PULLUP);


  attachPCINT(digitalPinToPCINT(PCINTPin),PCINT_Awake, CHANGE);

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);

  pinMode(3,INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:

  SoundMusic();
  
  if(digitalRead(SleepPin)==LOW){
    delay(10);
    if(digitalRead(SleepPin)==LOW){

  //スリープさせる
      sleep_enable();
      sleep_cpu();

  //スリープ復帰時はここから再開
    //スリープ機能を停止する
      sleep_disable();
    }
  }
}

void SoundMusic()
{
  if (i < scaleLen){
    tone(soundPin, scale[i], time[i] * 0.9);  //
    delay(time[i]);
    i++;
  }else{
    //i = 0;
  }   
}

void PCINT_Awake()
{
// 空の命令をする(PCINTが動けばスリープは解除される
}

回路はこちら

スイッチS1を押すと、7ピン(PB2)がHighになります。(10kΩで外部プルダウンしています)
スイッチS2を押すと、6ピン(PB1)がLowになる仕組みです。(プログラムで内部プルアップしてます)

上記の回路をブレッドボードで実装した例がこちらです。

これを実行してみると、このような動作をします

おぉ、、、
音楽が途中で止まっている(スリープした!)
電流値も40mAから0.25mA程度まで減ってますね!!

これでスリープモードも操れるようになりました!!

ヨシ!!


今日はATtiny85のスリープモードを紹介しました。

普段使わないけどスタンバイ状態にしたい電子工作グッヅを作るときには、スリープモードを使えると電池の消費を抑えることが出来るので便利ですよ。

ではまた別の記事で会いましょう。
ではでは~

🦅バサバサ~

参考資料

外部割込みで復旧は難しい

今回、ピンチェンジ割込みを使ってスリープモードから復旧しました。
ここで、筆者は本当は外部割込みでスリープモードを復旧させたかったのですが、外部割込みで復旧させる方法は今のところ見つけられていません。

データシートのこの部分がミソなのだと思われますが、「レベル割込み」とは何を挿しているのか、データシートには記載ありませんでした。

ということで、外部割込みを使って復旧させるのは今のところ成功していません。

sleep_mode(); について

今回の記事では次のコードでスリープさせました。

  sleep_enable(); // スリープモードを有効にします
  sleep_cpu(); // CPUをスリープさせます。ここでATtiny85は休眠します。

  sleep_disable(); // スリープモードを無効にします

実は、この3つのコードがセットになっているコードが存在します。
それが次のコードです。

  sleep_mode();

このsleep_modeの内部では、enable、cpu、disableが実行されています。
なので、上の3行と1行のコードは等価です。

コードをシンプルに書きたい場合はsleep_modeを使ってください。