Seeeduino XIAOの拡張ボードのブザーを使用する

組み込みエンジニア

こんにちは、ENGかぴです。

Seeeduino XIAO用の拡張ボードはSDカードスロットやOLEDによる液晶画面での表示ができます。またブザーが実装されており標準ライブラリでブザーの音程を指定や鳴らす出力期間を指定するなど任意の条件でブザーを鳴らすことができます。

Seeeduino XIAO用拡張ボード(秋月電子)を使用しています。Seeeduino XIAOを使って動作確認を行ったことを下記リンクにまとめています。

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

Seeeduino XIAOの拡張ボードとブザー

Seeeduino XIAO専用の拡張ボードがSeeed Studioから製作されています。このボードを使用するとOLEDに文字を表示したりSDカードを使用したりGrove端子を利用してセンサーと接続することができます。下記記事に拡張ボードについてとOLEDライブラリ追加の方法をまとめています。

Seeeduino XIAOの拡張ボードのOLEDを使用する

拡張ボードを使用していな場合のブザーの使い方について下記記事にまとめています。

Seeeduino XIAOの標準ライブラリでブザーを鳴らす方法

これらの記事で確認したOLEDの使用方法とブザーを鳴らす方法を使ってボタンを押すとOLEDにカウント数を表示しながらブザーを鳴らして動作確認を行います。

標準ライブラリでブザーを鳴らす

Seeeduino XIAOはArduinoと互換性があるため標準ライブラリが使用できます。tone()関数でパルス波形を出力することでブザーを鳴らすことができます。

#define PIN_PULSE  3 //拡張基板では3を指定する

void loop() {

  //3回ピピピとブザーを鳴らす例
  for( uint8_t i=0; i < 3; i++ ){
    tone(PIN_PULSE,3000, 100);
    delay(200);
  }
 //音程を変化させながらブザーを鳴らす例 
  tone(PIN_PULSE,2000 + freq*20, 100);

  if( ++btncnt % 10 == 0 ){
    if( ++freq >= 100){
      freq = 0;
    }
  }
}

tone()関数の引数1にパルス波形を出力するピン番号を指定します。拡張基板では3が割り振られているので3ピンを指定します。

引数2に振動波形の周波数を指定します。周波数を高くするほどブザーの音程が高くなります。ブザーの対象帯域の周波数を指定しますが低すぎるとブザーが鳴らない場合や高すぎるとブザーが聞こえなくなります。経験上1000Hz~3500Hzあたりで使用できれば十分だと感じています。

引数3にパルス波形の出力期間を指定します。引数3に値を指定しない場合は出力を続ける指定となります。

注意事項としてパルス波形が出力される前にtone()関数を上書きすると状態変化によるウェイトなどによって出力されない状態になることがあります。頻繁なtone()の上書きが発生しないようにすることが必要です。

OLEDライブラリの使用例

#include <U8x8lib.h>
#include <Wire.h>

U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8( SCL, SDA, U8X8_PIN_NONE);

void setup() {
  u8x8.begin(); //初期化
  u8x8.setFlipMode(1);  // set number from 1 to 3
  u8x8.setFont(u8x8_font_5x8_r); //フォントを選択
}

OLEDライブラリの追加や初期化の方法はSeeeduino XIAOの拡張ボードのOLEDを使用するを参考にしてください。

簡単に説明すると2つのライブラリをインクルードしU8X8_SSD1306_128X64_NONAME_SW_I2Cのオブジェクトの実体としてu8x8(任意でよい)を宣言しsetup()内でメンバー関数であるbegin()・setFlipMode()・setFont()などを使用してOLEDの初期化を行います。

  u8x8.setCursor(0, 4);
  u8x8.clearLine(4);
  u8x8.print("count:");
  u8x8.print(btncnt);

setCursor()で文字表示する座標を指定します。第1引数に行番号、第2引数に列番号を指定します。座標を指定した後はprint()関数で文字を書き込んで表示します。例ではcount:の文字列の後ろに変数btncntの数字を表示します。

種別内容
SetCursor()データを書き込む初期位置(表示スタート位置)にカーソルを合わせます。
print()
println()
文字列を指定して書き込み(表示)ます。
println()は文字列の最後に改行コードが入ります。
clearLine()指定した列の文字をクリアします。
今回使用したメンバー関数

文字列を同じ列に上書きして表示したい場合はclearLine()で上書き前の文字列を消してから表示します。

上書きする文字列が短い場合、文字が残ったまま上書きするため表示がおかしくなることがあります。文字列の長さを統一している場合はそのまま上書きしても問題になることはありません。

動作確認

動作確認用の回路図
動作確認用の回路図

Seeeduino XIAOをSeeeduino拡張ボードに挿入しSW1を実装します。DIはプルダウン付きのDIにしているためSW1を押したときにショートすることはありません。SW1を押すとOLEDにボタンを押した回数を表示しブザーを鳴らします。

OLEDによるカウント数の表示
OLEDによるカウント数の表示

初期画面では4行目に「count start!」を表示します。SW1を押すと「count:」の文字列の後ろに押した回数を表示します。SW1を押すごとにブザーを鳴らしますが10回押すごとに周波数が20Hz高くなるようにしてます。100回分音程を変更するようにしているため2000Hz~4000Hz間のブザーの音程を確認することができます。

スポンサーリンク

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

#include <U8x8lib.h>
#include <Wire.h>
#include <TimerTC3.h>

#define PIN_DI1 0
#define PIN_DI2 1
#define PIN_PULSE  3
#define DIFILT_MAX 4
#define TIM_DIFILT 1
#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベースタイマとなる

typedef struct DIFILT{
  uint8_t wp;
  uint8_t buf[DIFILT_MAX];
  uint8_t di;
};

int16_t timDifilter;
int8_t cnt10ms;
DIFILT diData[2];
bool flg[2];
uint16_t btncnt;
uint16_t freq;
 
U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(SCL, SDA, U8X8_PIN_NONE);
 /* Local function prototypes */
void mainTimer(void);
void DiFilter(void);
void TimerCnt();

void setup(void) {
  uint8_t i;

  u8x8.begin();
  u8x8.setFlipMode(3);  // set number from 1 to 3
  u8x8.setFont(u8x8_font_5x8_r);
  u8x8.setCursor(0, 0);
  u8x8.print("Seeeduino OLED");
  u8x8.setCursor(0, 1);
  u8x8.print("       Ver1.00");
  u8x8.setCursor(0, 3);
  u8x8.print("push button:1");
  u8x8.setCursor(0, 4);
  u8x8.print("count start!");  

  TimerTc3.initialize(1000);
  TimerTc3.attachInterrupt(TimerCnt);

  pinMode(PIN_DI1,INPUT_PULLDOWN);//ブザー音程変更用
  pinMode(PIN_DI2,INPUT_PULLDOWN);//今回は使用しないが実装している

  i=0;
  while( i < 10){
    mainTimer();
    DiFilter();
    delay(10);
    i++;
  }

  for( uint8_t i=0; i < 3; i++ ){
    tone(PIN_PULSE,3000, 100);
    delay(200);
  }
}
 
void loop(void) {

  mainTimer();
  DiFilter();

  if( diData[0].di == 1 ){
    if( flg[0] == false ){
      flg[0] = true;

      tone(PIN_PULSE,2000 + freq*20, 100);

      if( ++btncnt % 10 == 0 ){
        if( ++freq >= 100){
          freq = 0;
        }
      }

      u8x8.setCursor(0, 4);
      u8x8.clearLine(4);
      u8x8.print("count:");
      u8x8.print(btncnt);
    }
  }
  else{
    flg[0] = false;
  }
}
/* callback function add */
void TimerCnt(){
  ++cnt10ms;
}
/* タイマ管理 */
void mainTimer(void){

  if( cnt10ms >= BASE_CNT ){
    cnt10ms -=BASE_CNT;
    //10msごとにここに遷移する
    if( timDifilter > TIME_UP ){
      timDifilter--;
    }
  }
}
/* DIフィルタ */
void DiFilter(void){
  bool boo = true;
  uint8_t i;

  if( timDifilter == TIME_UP ){
    timDifilter = TIM_DIFILT;

    diData[0].buf[diData[0].wp] = digitalRead(PIN_DI1);
    diData[1].buf[diData[1].wp] = digitalRead(PIN_DI2);

    for(uint8_t no=0; no < 2; no++){
      for( i=1; i < sizeof(diData[no].buf);i++){
        if( diData[no].buf[i - 1] != diData[no].buf[i]){
          boo = false;
        }
      }

      if(boo){ //データがすべて一致なので採用する
        diData[no].di = diData[no].buf[0];
      }
      if( ++diData[no].wp >= sizeof(diData[no].buf)){
        diData[no].wp = 0;
      }
    }
  }
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

Code Camp完全オンラインのプログラミング個人レッスン【無料体験】

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました