子供向けの簡単なおもちゃを作る(ATTiny85のPWMで音を出す)

ProjectArduino, ATTiny, Embedded, HW, Japanese

Toy based on ATTiny85
Toy based on ATTiny85

姪と甥に会いに行くのだけど、姪へのお土産はあるが、甥へのお土産がないので、作ってみた。対象年齢2歳ぐらい。

MakerFaireTokyoの帰り道、ボタンを押すとLEDが光るおもちゃを、知人の子が割と面白そうに触っていたので、それを真似して、とりあえず光らせて、音を出してみようと思った。

材料

  • ATTiny85
    ArduinoというかAVRで自前で音出したことなかったので、練習に。
  • ケース
    USBスピーカーのケースがちょうど良かった。むき出しにすると破壊されたり、部品を誤飲されたりする危険とかあるので。むしろこのケースみてこれを思いついた。
    https://jp.creative.com/p/speakers/creative-muvo-2c
  • 電池ボックス
    ATTiny85は動作電圧:2.7V ~ 5.5Vなので、単4x3の、約4.5Vを直接つないだ。
  • スピーカー
    以前どこかで買っていた圧電スピーカー
  • LED
  • LEDで光らせる対象
    透明PLAフィラメントを使い、フリーの素材を3Dプリンタで印刷した
  • タクタイルスイッチ
    子供が押しやすいように大きめのものを

開発環境

Arduinoを書き込み機として用いて、ArduinoIDE上で開発します。

Arduino IDE に ATtiny45/85/2313 他の開発環境を組み込む

Development environment
Development environment

音の出し方

tone関数が使えなかったので、SIN波を狙った周波数でPWMにセットするようなものを作った。
基準となるSIN波はあらかじめ計算して配列に格納しておく。

Sine curve
Sine curve

あとは、ボタンと連動させて、ドレミの音階を変えたり、LEDを光らせたりすればOK

動かしてみた様子

参考

ソースコード


#include <avr/pgmspace.h>

const uint8_t sin_wav[] PROGMEM = {
140,153,165,177,189,199,210,219,227,235,241,246,250,253,255,255,254,252,248,243,238,231,223,214,204,193,182,170,158,145,133,
120,107,95,83,71,60,49,40,31,23,16,11,6,3,1,0,0,2,5,9,15,21,29,38,47,57,68,80,92,104,117,
};

const int msc_freq[] = {//Scale Hz CDEFGAC
  261,293,329,349,391,440,492,523
};

const uint8_t sin_wav_len = 62;

volatile uint8_t msc = 0;//CDEFGA(440Hz)BC
volatile int msc_cnt=0;
volatile int msc_cnt_max = 1;

volatile char btn_stat=0;
volatile char sound_stat=0;
volatile int sound_duration=0;
const int int_freq=20000;//20KHz

#define PIN_BTN  0
#define PIN_LED  3
#define PIN_PWM1 1
#define PIN_PWM2 4

void button(){
  btn_stat = !btn_stat;
  digitalWrite(PIN_LED,btn_stat);
  msc_cnt_max = int_freq / msc_freq[msc%8];
  msc++;
  sound_stat=1;
}

void setup () {
  msc_cnt_max = int_freq / msc_freq[0];

  pinMode(PIN_LED, OUTPUT); //LED
  pinMode(PIN_BTN, INPUT_PULLUP); //Button
  digitalWrite(PIN_LED,btn_stat);

  //PWM and Interruption Setting
  PLLCSR = 1<<PCKE | 1<<PLLE;     
 
  TIMSK = 0;                              
  TCCR1 = 1<<PWM1A | 2<<COM1A0 | 1<<CS10; 
  GTCCR = 1<<PWM1B | 2<<COM1B0;           
  OCR1A = 128; OCR1B = 128;               

  TCCR0A = 3<<WGM00;                      
  TCCR0B = 1<<WGM02 | 2<<CS00;            
  TIMSK = 1<<OCIE0A;                      
  OCR0A = (1000000 / int_freq)-1; //20KHz

  pinMode(PIN_PWM2, OUTPUT); //PWM
  pinMode(PIN_PWM1, OUTPUT); //PWM
}

void loop () { 
  //Check button status
  static char last=0;
  static uint8_t bcnt=0;
  char s = digitalRead(PIN_BTN);
  if(s==0){
    if(last==0){
      bcnt++;
      if(bcnt>250)bcnt=250;
    }
    last=0;
  }else{
    if(bcnt>240){
      button();
    }
    last=1;
    bcnt=0;
  }
}

ISR (TIMER0_COMPA_vect) {
  if(sound_stat==0) return;
  sound_duration++;
  if(sound_duration>int_freq){
    sound_duration=0;
    sound_stat=0;
  }

  int point = ((int)sin_wav_len * msc_cnt) / msc_cnt_max;
  char val = pgm_read_byte(&sin_wav[point]);
  OCR1A = val; 
  OCR1B = val ^ 255;

  msc_cnt++;
  
  if (msc_cnt >= msc_cnt_max) {
    msc_cnt=0;
  }
}

中身と配線

Internal wiring
Internal wiring
Without a model
Without a model
Wiring
Wiring