nichiconのアイコン画像
nichicon 2021年09月27日作成 (2022年09月25日更新)
製作品 製作品 閲覧数 6062
nichicon 2021年09月27日作成 (2022年09月25日更新) 製作品 製作品 閲覧数 6062

カードサイズの小型オシロスコープ

カードサイズの小型オシロスコープ

超小型のマイコンボードSeeeduino XIAOを使用したクレジットカードサイズのオシロスコープを作りました。
このマイコンボードに載っているマイコンのADコンバータは350kspsで動作します。
Arduino UNOや8bit PICマイコンと比べて3倍以上高速でサンプリングできます。
今回はこのサイズと高速ADCを活かしてオシロスコープを作りました。
グラフィックLCDの制御には、しなぷすのハード製作記さんのMGLCDライブラリを使用しています。

部品

ver 1.0
・マイコンボード Seeeduino XIAO x1
・グラフィックLCD AQM1248A x1
・抵抗10kΩ x1
・抵抗1MΩ x1
・2ピンL型ピンヘッダ x1

ver 2.0
・マイコンボード Seeeduino XIAO x1
・グラフィックLCD AQM1248A x1
・抵抗10kΩ x5
・抵抗15kΩ x1
・タクトスイッチ x3
・3ピンL型ピンヘッダ x1

回路図

ver 1.0
注意:回路図ではLCDに5Vが供給されていますが正しくは3.3Vです。LCDは3V3のピンに繋いでください。
キャプションを入力できます

ver 2.0
サンプリングレートを変更するためにタクトスイッチを付けました。
キャプションを入力できます

プログラム

ver 1.0

#include <MGLCD.h>
#include <MGLCD_SPI.h>
#include <SPI.h>

#define ADCpin A1
#define CS 7
#define DI 6
#define MAX_FREQ 8000000L

MGLCD_AQM1248A_SPI MGLCD(MGLCD_SpiPin2(CS, DI), MAX_FREQ);

#define time_width 50 //micro seconds | time_width > 3
uint16_t buf[100] ;
uint32_t t0, dt ;
uint8_t n, valY, lastY ;

void set_screen(void){
  MGLCD.Line(0,45, 0,47) ;
  MGLCD.Line(10,45, 10,47) ;
  MGLCD.Line(20,45, 20,47) ;
  MGLCD.Line(30,45, 30,47) ;
  MGLCD.Line(40,45, 40,47) ;
  MGLCD.Line(50,45, 50,47) ;
  MGLCD.Line(60,45, 60,47) ;
  MGLCD.Line(70,45, 70,47) ;
  MGLCD.Line(80,45, 80,47) ;
  MGLCD.Line(90,45, 90,47) ;
  MGLCD.Line(100,0, 100,47) ;

  MGLCD.Line(99,33, 101,33) ;
  MGLCD.Line(99,19, 101,19) ;
  MGLCD.Line(99,5, 101,5) ;

  MGLCD.Locate(17,0) ;
  MGLCD.print("3.3V") ;
  MGLCD.Locate(17,1) ;
  MGLCD.print("500u") ;
}

void setup() {
  // put your setup code here, to run once:
  pinMode(ADCpin, INPUT) ;
  MGLCD.Reset() ;
}

void loop() {
  // put your main code here, to run repeatedly:
  for(n=0; n<100; n++){
    t0 = micros() ;
    dt = 0 ;
    buf[n] = analogRead(ADCpin) ;
    while(dt < time_width){
      dt = micros() - t0 ;
    }
  }

  MGLCD.ClearScreen() ;
  set_screen() ;
  lastY = 47-(buf[0]/22) ;
  for(n=0; n<99; n++){
    valY = 47-(buf[n+1]/22) ;
    MGLCD.Line(n,lastY, n+1,valY) ;
    lastY = valY ;
  }
  delay(1000) ;
}

ver 2.0
タクトスイッチを追加してサンプリングレートを変更できるようにしました。
キャプションを入力できます

#include <MGLCD.h>
#include <MGLCD_SPI.h>
#include <SPI.h>

#define ADCpin A3
#define CS 7
#define DI 6
#define MAX_FREQ 8000000L
#define up_SW   4
#define down_SW 2

MGLCD_AQM1248A_SPI MGLCD(MGLCD_SpiPin2(CS, DI), MAX_FREQ) ;

const uint16_t time_table[15] = {5, 10, 20, 30, 40, 50, 60, 80, 100, 200, 300, 500, 1000, 2000, 5000} ;
static char table_index = 0 ;

uint16_t buf[100] ;
uint32_t t0, dt ;
uint8_t n, valY, lastY ;
uint8_t i ;

void display_rate(void){
  uint16_t time_width, dt_display ;
  MGLCD.Locate(17, 1) ;
  MGLCD.print("    ") ;
  MGLCD.Locate(17, 1) ;
  time_width = time_table[table_index] ;
  if(time_width > 99){
    dt_display = time_width/100 ;
    MGLCD.printf("%dm", dt_display) ;
  }else{
    dt_display = time_width*10 ;
    MGLCD.printf("%du", dt_display) ;
  }
}

void set_screen(void){
  uint16_t dt_display ;
  
  MGLCD.Line(0,45, 0,47) ;
  MGLCD.Line(10,45, 10,47) ;
  MGLCD.Line(20,45, 20,47) ;
  MGLCD.Line(30,45, 30,47) ;
  MGLCD.Line(40,45, 40,47) ;
  MGLCD.Line(50,45, 50,47) ;
  MGLCD.Line(60,45, 60,47) ;
  MGLCD.Line(70,45, 70,47) ;
  MGLCD.Line(80,45, 80,47) ;
  MGLCD.Line(90,45, 90,47) ;
  MGLCD.Line(100,0, 100,47) ;

  MGLCD.Line(99,33, 101,33) ;
  MGLCD.Line(99,19, 101,19) ;
  MGLCD.Line(99,5, 101,5) ;
  
  display_rate() ;
}

void setup() {
  // put your setup code here, to run once:
  pinMode(ADCpin, INPUT) ;
  pinMode(up_SW, INPUT) ;
  pinMode(down_SW, INPUT) ;
  MGLCD.Reset() ;
}

void loop() {
  // put your main code here, to run repeatedly:
  for(n=0; n<100; n++){
    t0 = micros() ;
    dt = 0 ;
    buf[n] = analogRead(ADCpin) ;
    while(dt < (uint32_t)time_table[table_index]){
      dt = micros() - t0 ;
    }
  }
  MGLCD.ClearScreen() ;
  set_screen() ;
  MGLCD.Locate(17,0) ;
  MGLCD.print("5.0V") ;
  MGLCD.Locate(17,5) ;
  MGLCD.print("0.0V") ;
  lastY = 47-(buf[0]/22) ;
  for(n=0; n<99; n++){
    valY = 47-(buf[n+1]/22) ;
    MGLCD.Line(n,lastY, n+1, valY) ;
    lastY = valY ;
  }
  
  t0 = millis() ;
  dt = 0 ;
  while(dt < 1000){
    if(digitalRead(up_SW) == 1){
      table_index++ ;
      if(table_index > 14){
        table_index = 14 ;
      }
      while(digitalRead(up_SW) == 1) ;
      delay(20) ;
    }else if(digitalRead(down_SW) == 1){
      table_index-- ;
      if(table_index < 0){
        table_index = 0 ;
      }
      while(digitalRead(down_SW) == 1) ;
      delay(20) ;
    }
    dt = millis() - t0 ;
  }
  
}

ver2.1
真ん中のボタンを押すと画面を停止できるようにしました。
これにより波形をキャプチャできるようになりました。
また、縦軸の目盛りを入力3.3V仕様の時から変更していなかったので、現在の最大5Vの入力に合わせて変更しました。

#include <MGLCD.h>
#include <MGLCD_SPI.h>
#include <SPI.h>

#define ADCpin A3
#define CS 7
#define DI 6
#define MAX_FREQ 8000000L
#define up_SW   4
#define down_SW 2
#define cen_SW  5

MGLCD_AQM1248A_SPI MGLCD(MGLCD_SpiPin2(CS, DI), MAX_FREQ) ;

const uint16_t time_table[15] = {5, 10, 20, 30, 40, 50, 60, 80, 100, 200, 300, 500, 1000, 2000, 5000} ;
static signed char table_index = 0 ;

uint16_t buf[100] ;
uint32_t t0, dt ;
uint8_t n, valY, lastY ;
uint8_t i ;

uint16_t max_sample, min_sample ;
float k, min_volt, max_volt, disp_ratio ;

bool pause_flag;

void display_rate(void){
  uint16_t time_width, dt_display ;
  MGLCD.Locate(17, 1) ;
  MGLCD.print("    ") ;
  MGLCD.Locate(17, 1) ;
  time_width = time_table[table_index] ;
  if(time_width > 99){
    dt_display = time_width/100 ;
    MGLCD.printf("%dm", dt_display) ;
  }else{
    dt_display = time_width*10 ;
    MGLCD.printf("%du", dt_display) ;
  }
}

void set_screen(void){
  uint16_t dt_display ;
  
  MGLCD.Line(0,45, 0,47) ;
  MGLCD.Line(10,45, 10,47) ;
  MGLCD.Line(20,45, 20,47) ;
  MGLCD.Line(30,45, 30,47) ;
  MGLCD.Line(40,45, 40,47) ;
  MGLCD.Line(50,45, 50,47) ;
  MGLCD.Line(60,45, 60,47) ;
  MGLCD.Line(70,45, 70,47) ;
  MGLCD.Line(80,45, 80,47) ;
  MGLCD.Line(90,45, 90,47) ;
  MGLCD.Line(100,0, 100,47) ;
  
  /*
  MGLCD.Line(99,33, 101,33) ;
  MGLCD.Line(99,19, 101,19) ;
  MGLCD.Line(99,5, 101,5) ;
  */
  MGLCD.Line(99,47, 101,47);
  MGLCD.Line(99,38, 101,38);
  MGLCD.Line(99,29, 101,29);
  MGLCD.Line(99,19, 101,19);
  MGLCD.Line(99,10, 101,10);
  
  display_rate() ;
}

void setup() {
  pinMode(ADCpin, INPUT) ;
  pinMode(up_SW, INPUT) ;
  pinMode(down_SW, INPUT) ;
  pinMode(cen_SW, INPUT) ;
  MGLCD.Reset() ;
}

void loop() {
  max_sample = 0 ;
  min_sample = 1023 ;
  for(n=0; n<100; n++){
    t0 = micros() ;
    dt = 0 ;
    buf[n] = analogRead(ADCpin) ;
    /*
    if(buf[n] > max_sample){
      max_sample = buf[n] ;
    }else if(buf[n] < min_sample){
      min_sample = buf[n] ;
    }
    */
    while(dt < (uint32_t)time_table[table_index]){
      dt = micros() - t0 ;
    }
  }
  /*
  k = (max_sample - min_sample)/47 ;
  max_volt = max_sample*0.00322 ;
  min_volt = min_sample*0.00322 ;
  MGLCD.ClearScreen() ;
  set_screen() ;
  MGLCD.Locate(17,0) ;
  MGLCD.printf("%1.1f", max_volt) ;
  MGLCD.Locate(17,5) ;
  MGLCD.printf("%1.1f", min_volt) ;
  m = 0 ;
  mark[0] = 0 ;
  mark[1] = 0 ;
  lastY = 47-(buf[0]/k);
  for(n=0; n<99; n++){
    valY = 47-(buf[n+1]/k) ;
    if(lastY < 24 && valY > 24){
      if(m < 2){
        mark[m] = n ;
        m++ ;
      }
    }
    MGLCD.Line(n,lastY, n+1, valY) ;
    lastY = valY ;
  }
  */
  //max_volt = 3.3 ;
  //min_volt = 0.0 ;
  MGLCD.ClearScreen() ;
  set_screen() ;
  MGLCD.Locate(17,0) ;
  MGLCD.print("5.0V") ;
  MGLCD.Locate(17,5) ;
  MGLCD.print("0.0V") ;
  lastY = 47-(buf[0]/22) ;
  for(n=0; n<99; n++){
    valY = 47-(buf[n+1]/22) ;
    MGLCD.Line(n,lastY, n+1, valY) ;
    lastY = valY ;
  }
  
  t0 = millis() ;
  dt = 0 ;
  while(dt < 500){
    if(digitalRead(cen_SW)){
      pause_flag = true;
      while(digitalRead(cen_SW) == 1);
      delay(20);
    }
    while(pause_flag == true){
      if(digitalRead(cen_SW)){
        pause_flag = false;
        while(digitalRead(cen_SW) == 1);
        delay(20);
      }
    }
    if(digitalRead(up_SW)){
      table_index++ ;
      if(table_index > 14){
        table_index = 14 ;
      }
      while(digitalRead(up_SW) == 1) ;
      delay(20) ;
    }else if(digitalRead(down_SW)){
      table_index-- ;
      if(table_index < 0){
        table_index = 0 ;
      }
      while(digitalRead(down_SW) == 1) ;
      delay(20) ;
    }
    dt = millis() - t0 ;
  }
  
}

作り方

ユニバーサル基板に回路図通りに配線して作ってください。
現状、サンプリングの間隔は50μsに固定されています。
プログラムを改造してサンプリングレートを変更できるようにすると使いやすくなります。

(2021年10月10日追記)
ver 2.0でサンプリングレートを変更できるようになりました。
また、入力に分圧回路を付けて5V入力に対応しました。
ver 2.0はA3ピンが信号の入力です。

使い方

Seeeduino XIAOのType-Cコネクタにモバイルバッテリーなどで給電して、A1ピンに繋がっているL型ピンヘッダに信号を与えるとLCDに波形が表示されます。

(2022年9月25日追記)
ver2.1では真ん中のボタンを押すと画面を停止できます。もう一度押すと再開します。

今後の改良予定

LCDの下のスペースが空いているので、そこにSeeeduino XIAOを入れることでさらに小型化できると考えています。

また、SeeeduinoとLCDをプリント基板に直接つければ薄型にできるので、機会があったら基板を発注しようと思います。

現在、電圧レンジの自動調整機能を作っているので完成したら更新します。
サンプリングレートの自動調整も試してみましたが、難しいので手動で変更できる仕様にする予定です。→ver 2.0でサンプリングレート変更機能を実装しました。

現在はUSB給電式ですが、ver3.0でリポバッテリーを搭載したいと考えています。
→コイン電池を使った電源基板を製作しました。記事にまとめ次第投稿します。

1
5
nichiconのアイコン画像
独学で電子工作とプログラミングをしている大学生です。PICやArduino、ESPなどを使っています。Twitter(@_electro_master)もやってます。作る際は自己責任でお願いします。
  • nichicon さんが 2021/09/27 に 編集 をしました。 (メッセージ: 初版)
  • nichicon さんが 2021/09/30 に 編集 をしました。 (メッセージ: バグの修正&ver 2.0公開)
  • nichicon さんが 2021/10/10 に 編集 をしました。 (メッセージ: ver 2.0の回路図等を追加)
  • Opening
    3duilabのアイコン画像 3duilab 2021/10/19

    オシロスコープは難しいのに素敵な作品だと思います。ところでオシロスコープの信号入力は多くが1Mオームくらいのインピーダンスです。ここのインピーダンスが低いと接続時に電流が流れて電圧が下がるので正しく測定できない事があります。回路図から入力インピーダンスは25kオームと低めなのでオペアンプなどでバッファ回路を作りインピーダンス変換をすることをお勧めします。

    nichiconのアイコン画像 nichicon 2021/10/19

    ありがとうございます。
    入力の抵抗が小さいのは気になっていました。現在はサーボなどの信号を測定する目的で作っており、小型化のためにバッファは省いています。プリント基板や表面実装部品を用いればバッファも付けれるので考えてみます。

    1 件の返信が折りたたまれています
  • nichicon さんが 2021/12/07 に 編集 をしました。
  • nichicon さんが 2022/09/25 に 編集 をしました。 (メッセージ: ver2.1を公開)
ログインしてコメントを投稿する