IV-27M 蛍光表示管を Arduino で動かしてみる。

土曜日に基本の点灯まで行いました IV-27M 蛍光表示管ですが、シフトレジスタにデータ流す順番とかの整理もかねて配線を一部やりなおしました。とりあえずブレッドボードに乗っている部品から下記の回路図を書き出しもしました。

IV-27M CLOCK01

これに、Arduino UNO を接続しまして、シリアルポートから受信した数字を表示するようにプログラムを作成。これは、仕込んでおきたかった機能で、ゴースト確認用に数文字おきに表示とかしたいときなど便利です。
24V ではブランキングタイム入れてない状態でもゴーストほとんど出てないですが、24V の電源をもう少しあげてみたらハッキリ出るかも知れないので、一応ということです。

#include <SPI.h>
#include <stdio.h>
#include <string.h>

/* ------------------------------------------------------------
 -- Grobal Variables
 ------------------------------------------------------------ */
unsigned int msCount = 0;
boolean msCountOVF;
unsigned char vfdDigit = 0;
unsigned char vfdSeg[14];
unsigned char vfdDot[14];
char str01[20];

String inputString = "";     // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete


/* ------------------------------------------------------------
 -- Interrupt hundler (every 0.5ms)
 ------------------------------------------------------------ */
ISR(TIMER2_COMPA_vect) {

  sendVfd();
  msCount++;
  if(msCount >= 2000) {  // 1seconds 
    msCount = 0;
    msCountOVF = true;
  }
  // Reset interrupt flag and counter.
  TIFR2 &= ~(1<<OCF2A);
}

/* ------------------------------------------------------------
 -- Initial Setup
 ------------------------------------------------------------ */
void setup() {

  pinMode(4, OUTPUT);  // SCLR
  pinMode(5, OUTPUT);  // RCK

  // initialize SPI
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);

  //
  Serial.begin(9600);

  // Setup TIMER2
  // a. Disable the Timer/Counter2 interrupts by clearing OCIE2x and TOIE2.
  // b. Select clock source by setting AS2 as appropriate.
  // c. Write new values to TCNT2, OCR2x, and TCCR2x.
  // d. To switch to asynchronous operation: Wait for TCN2xUB, OCR2xUB, and TCR2xUB.
  // e. Clear the Timer/Counter2 Interrupt Flags.
  // f. Enable interrupts, if needed.

  // TIMSK2 2=OCIE2B 1=OCIE2A 0=TOIE2
  // Disable interrupt enable
  TIMSK2 &= ~(1<<OCIE2B);
  TIMSK2 &= ~(1<<OCIE2A);
  TIMSK2 &= ~(1<<TOIE2);
  // ASSR – Asynchronous Status Register
  // 6=EXCLK 5=AS2 4=TCN2UB 3=OCR2AUB 2=OCR2BUB 1=TCR2AUB 0=TCR2BUB  
  // AS2=0 CLKIO,  AS2=1 TOSC1
  ASSR &= ~(1<<AS2);
  // Waveform Generation Mode set to NORMAL mode WGM22,21,20=0
  TCCR2A &= ~(1<<WGM20);
  TCCR2A |= (1<<WGM21);
  TCCR2B &= ~(1<<WGM22);
  // TCCR2B – Timer/Counter Control Register B
  // 7=FOC2A 6=FOC2B 3=WGM22 2=CS22 1=CS21 0=CS20
  // CS22 CS21 CS20 Description
  // 0    0    0    No clock source (Timer/Counter stopped).
  // 0    0    1    clkT2S/(No prescaling)
  // 0    1    0    clkT2S/8 (From prescaler)
  // 0    1    1    clkT2S/32 (From prescaler)
  // 1    0    0    clkT2S/64 (From prescaler)
  // 1    0    1    clkT2S/128 (From prescaler)
  // 1    1    0    clkT2S/256 (From prescaler)
  // 1    1    1    clkT2S/1024 (From prescaler)
  TCCR2B |= (1<<CS22);
  TCCR2B &= ~(1<<CS21);
  TCCR2B &= ~(1<<CS20);
  TCNT2 = 0;
  OCR2A = 124; // 16MHz / 64 = 4us. 4us * 125 = 0.5ms
  // Clear interrupt flag
  // TIFR2 – Timer/Counter2 Interrupt Flag Register
  // 2=OCF2B 1=OCF2A 0=TOV2
  TIFR2 &= ~(1<<OCF2A);
  // TIMSK2 2=OCIE2B 1=OCIE2A 0=TOIE2
  // Enable interrupt
  TIMSK2 |= (1<<OCIE2A);
}

/* ------------------------------------------------------------
 -- Main loop
 ------------------------------------------------------------ */
void loop() {

  int i;
  int charLen;
  // serial input done
  // 9600 bps
  // help&#91;RET&#93; -> print help message
  // HH MM SS[RET] -> set time
  //
  if (stringComplete == true) {
    //Serial.println(inputString); 
    if(inputString.equals("help\r")) {
      Serial.println("help: I'm alive");
    } 
    else {
      for(i = 0; i < 14; i++) {
        vfdSeg&#91;i&#93; = 0;
        str01&#91;i&#93; = 0;
      }
      charLen = inputString.length();
      if(charLen > 15) {
        charLen = 15;
      }
      inputString.toCharArray(str01, charLen);
      for(i = 0; i < 14; i++) {
        convVfdSeg(i);
      }
    }
    inputString = "";
    stringComplete = false;
  }

}

/* ------------------------------------------------------------
 -- sendVfd
 ------------------------------------------------------------ */
void sendVfd() {

  unsigned int vfdBit;
  unsigned char vfdBitH;
  unsigned char vfdBitL;

  // HC595 SCLR(negedge: shift register clear)
  digitalWrite(4,LOW);
  digitalWrite(4,HIGH);  

  // HC595 RCK (posedge: data latch)
  digitalWrite(5,LOW);
  digitalWrite(5,HIGH);

  // send to shift register
  vfdBit = (1 << vfdDigit);
  vfdBitL = vfdBit & 0x00FF;
  vfdBitH = (vfdBit >> 8);
  //if(vfdDot[vfdDigit] != 0) {
  //  digit |= 0x80;
  // }

  SPI.transfer(vfdBitH);
  SPI.transfer(vfdBitL);
  SPI.transfer(vfdSeg[vfdDigit]);  


  // HC595 RCK (posedge: data latch)
  digitalWrite(5,LOW);
  digitalWrite(5,HIGH);  

  vfdDigit++;
  if(vfdDigit > 13) {
    vfdDigit = 0;
  }
}

/* ------------------------------------------------------------
 -- sendVfd
 ------------------------------------------------------------ */
void convVfdSeg(int digit) {

  switch(str01[digit]) {
  case '0': 
    vfdSeg[digit] = 0xFC; 
    break;
  case '1': 
    vfdSeg[digit] = 0x60; 
    break;
  case '2': 
    vfdSeg[digit] = 0xda; 
    break;
  case '3': 
    vfdSeg[digit] = 0xf2; 
    break;
  case '4': 
    vfdSeg[digit] = 0x66; 
    break;
  case '5': 
    vfdSeg[digit] = 0xb6; 
    break;
  case '6': 
    vfdSeg[digit] = 0xbe; 
    break;
  case '7': 
    vfdSeg[digit] = 0xe0; 
    break;
  case '8': 
    vfdSeg[digit] = 0xfe; 
    break;
  case '9': 
    vfdSeg[digit] = 0xe6; 
    break;
  case '-': 
    vfdSeg[digit] = 0x02; 
    break;
  }
}


/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read(); 
    // add it to the inputString:
    inputString += inChar;
    Serial.write(inChar);
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\r') {
      stringComplete = true;
      Serial.write('\r');
      Serial.write('\n');
    } 
  }
}

IV-27M 蛍光表示管点灯試験

2014-07-12 11.54.16

土曜日、朝からお昼までのワークで点灯試験というか基本回路の確認まできたところの状態。朝一は、あるていどちゃんと配線したほうがいいよなーと思いつつ作業初めましたが、途中でブチギレ。結局スパゲティになっちゃいました(笑)

2014-07-12 08.36.55

作業開始直後の切れる前に IV-27M 蛍光表示管のリード線をはんだ付け。ブレッドボードからジャンプワイヤ刺せるようにしておきました。

そのあと、ブレッドボードに 74HC595 のシフトレジスタと、TD62783 のソースドライバを配線して蛍光表示管と接続します。CPU は Arduino UNO です。以前作成した LD8113 蛍光表示管キットのテスト用に組んだ簡単な Arduino スケッチがあるのでを流用して 6 桁から 14 桁まで増やす修正入れて書き込み。最後にブレッドボードに組んだシフトレジスタと接続して完成でございます。

点灯させてみた感じは、14桁ダイナミック点灯したわりには明るさあったのがラッキー。ヒーター直流点灯も左右で極端に明るさ違ういうこともないのでラッキー。Aruduino UNO の USB 給電でも問題なく動いているので消費電力も低めかな。というのもラッキーなところです。
気になる点が減ったので、よかったです。数少ないけど頒布品にしましょうかねぇ。

Arduino で SPI 転送テスト

2013-05-31 23.47.00

てっとり早く動作確認するのに便利なので、Arduino よく使います。IN-12b ニキシー管 8 桁表示に使うのですが、表示させるデータを受信するポートを 3つ用意しようと思ってます。 UART, TWI, SPI です。今回は一番簡単な SPI からです。

マスター側は、 1秒毎に SS を LOW → SPI送信(数バイト) → SS を HIGH を繰り返すだけ。

#include <SPI.h>

char val = 'A';

void setup() {
  
  pinMode(10, OUTPUT);
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV32);
}

void loop() {

  digitalWrite(10, LOW);
  for(char i = 0; i < 4; i++) {
    SPI.transfer(val + i);
  }
  SPI.transfer('\r');
  digitalWrite(10, HIGH);  

  if(val++ > 0x58) {
    val = 'A';
  }
  delay(1000);
}

スレーブ側は、受信データを割り込み処理で取り込んで、改行来たら液晶に表示するだけの処理です

#include
#include
#include

int col = 0;
LiquidCrystal lcd(2, 3, 4, A0, A1, A2, A3);
char lcdf = 0;
String strSPI = “”;
boolean strSPIcomplete = false;
int charcnt = 0;

// —————————————————————————–
// — Interrupt Service Routine (SPI transfer complete)
// —————————————————————————–
ISR(SPI_STC_vect) {
char ch;

ch = SPDR;
charcnt++;
strSPI += ch;
if(ch == ‘\r’) {
strSPIcomplete = true;
}
}

// —————————————————————————–
// — Setup
// —————————————————————————–
void setup() {

pinMode(A0, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A2, OUTPUT);
pinMode(A3, OUTPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);

// initialize SPI
pinMode(SS, INPUT);
pinMode(SCK, INPUT);
pinMode(MOSI, INPUT);
pinMode(MISO, OUTPUT);
// SPI slave mode
SPCR &= ~(1< SPI 転送の場合クロックは、CPU クロック(今回は 16MHz)の 1/4 だったか、それ以下で使用するのが推奨と記憶にありましてマスター側には SPI.setClockDivider(SPI_CLOCK_DIV32); という記述を入れてます。最初は 8 分周など入れて、データ欠落したので最終的に 32 分周です。ジャンプワイヤで接続しただけなのでこんなもんということにしておきましょう。

ただ、SS ピンを 1 バイト転送毎に LOW – HIGH トグルさせると、クロック 16MHz でも問題なく転送出来ました。SS が LOW から HIGH になるとスレーブ側の受信ロジックのリセットがかかるとデータシートにはあるのですがこれのせいでしょうか?? まぁ、これは想定と違う使い方なので深くは追いません。

Arduino UNO R2,R3 と MEGA2560 R3 の Fuses と Lock bits

Evernote にも保存してますが、誰かが困ったときのリカバリに役立つかも知れないので貼っておく次第。UNO は DIP パッケージです。確認に使用したプログラムは Atmel Studio 6 から起動した Device Programming。インタフェースは AVRISP MKII です。

ATMEGA168 を内蔵オシレータ 8MHz のクロックで動作させる場合。Arduino IDE からは Arduino Pro or Pro Mini (3.3V, 8 MHz) w/ ATmega168 のボードタイプを使用します。F_CPU の define の関係でこれを使います。電圧は関係ありません。

---------------------------------------------------
Arduino ATMEGA168 internal 8MHz Fuses and Lock bits
---------------------------------------------------

BOOTSZ = 1024W_1C00
BOOTRST = [X]
RSTDISBL = [ ]
DWEN = [ ]
SPIEN = [X]
WDTON = [ ]
EESAVE = [ ]
BODLEVEL = DISABLED
CKDIV8 = [ ]
CKOUT = [ ]
SUT_CKSEL = INTRCOSC_8MHZ_6CK_14CK_65MS

EXTENDED = 0xF8 (valid)
HIGH = 0xDF (valid)
LOW = 0xE2 (valid)


LB = NO_LOCK
BLB0 = NO_LOCK
BLB1 = NO_LOCK

LOCKBIT = 0xFF (valid)

ATMEGA168 を外付けのクリスタルもしくはセラロックで動作させる場合。Arduino IDE からは Arduino Diecimila or Duemilanove w/ ATmega168 のボードタイプを使用します。

----------------------------------------------------
Arduino ATMEGA168 External 16MHz Fuses and Lock bits
----------------------------------------------------

BOOTSZ = 1024W_1C00
BOOTRST = [X]
RSTDISBL = [ ]
DWEN = [ ]
SPIEN = [X]
WDTON = [ ]
EESAVE = [ ]
BODLEVEL = DISABLED
CKDIV8 = [ ]
CKOUT = [ ]
SUT_CKSEL = EXTXOSC_8MHZ_XX_16KCK_14CK_65MS

EXTENDED = 0xF8 (valid)
HIGH = 0xDF (valid)
LOW = 0xFF (valid)


LB = NO_LOCK
BLB0 = NO_LOCK
BLB1 = NO_LOCK

LOCKBIT = 0xFF (valid)

こちらは、AVRISP MKII で読んだだけの値です。

————————————–
Arduino UNO R2, R3 Fuses and Lock bits
————————————–

———
>>Fuses<< --------- BODLEVEL = 2V7 RSTDISBL = [ ] DWEN = [ ] SPIEN = [X] WDTON = [ ] EESAVE = [X] BOOTSZ = 256W_3F00 BOOTRST = [X] CKDIV8 = [ ] CKOUT = [ ] SUT_CKSEL = EXTXOSC_8MHZ_XX_16KCK_14CK_65MS EXTENDED = 0xFD (valid) HIGH = 0xD6 (valid) LOW = 0xFF (valid) ------------- >>Lock bits<< ------------- LB = NO_LOCK BLB0 = NO_LOCK BLB1 = LPM_SPM_DISABLE LOCKBIT = 0xCF (valid) [/code]

これも、AVRISP MKII で読んだだけの値です。

—————————————
Arduino MEGA2560 R3 Fuses and Lock bits
—————————————

———
>>Fuses<< --------- BODLEVEL = 2V7 OCDEN = [ ] JTAGEN = [ ] SPIEN = [X] WDTON = [ ] EESAVE = [X] BOOTSZ = 4096W_1F000 BOOTRST = [X] CKDIV8 = [ ] CKOUT = [ ] SUT_CKSEL = EXTXOSC_8MHZ_XX_16KCK_65MS EXTENDED = 0xFD (valid) HIGH = 0xD0 (valid) LOW = 0xFF (valid) ------------- >>Lock bits<< ------------- LB = NO_LOCK BLB0 = NO_LOCK BLB1 = LPM_SPM_DISABLE LOCKBIT = 0xCF (valid) [/code]

Arduino UNO の RESET EN にスイッチを付ける

2013-05-25 13.19.11

はんだごて温める用事があったついでなのですが、Arduino UNO の基板に RESET ENABLE を有効/無効を簡単に切り替え出来るよう基盤用のスイッチを直付けしました。

Arduino IDE からプログラムを書き込むときに仮想シリアルポート経由でプログラムを送るのですが、そのとき仮想シリアルポートの DTR を使って ATMEGA328P をリセットしブートモードにし、プログラム書き込み出来るよう自動化されます。ですが、Arduino のコントロールのためにシリアル通信使うと、ターミナルソフトやシリアル通信するホストアプリケーションなどを起動しますとこの機能のおかげで Arduino がリセットされてしまいます。

開発時には便利な機能も動くようになれば邪魔になります。基板にはもともとランドがあってパターンカットするか、ハンダブリッジでやってねという簡便な方法でこの RESET EN の有効/無効を切り替えるようになっていますが、面倒臭いのでスイッチを付けたという次第。

まぁ、パターンカットしておいて、プログラム書き込み時に、左手で Arduino の RESET ボタン押すのと、右手で Arduino IDE の書き込みをクリック出来るようマウス持ってタイミング良くやれば問題ないのですがね。

LD8113 蛍光表示管の点灯回路試作

2013-03-18 12.17.46

先週の金、土曜日で単体での点灯をやってみまして。日曜は墓参りのためほとんど手つかず。月曜の午前中である程度まとまってきたかなという感じです。

Arduiono から SPI 通信で HC595 のシフトレジスタへ表示させるデータを送ります。蛍光管のアノードのビットと、グリッドのビットを流し込みしまして、シフトレジスタの出力が TD62783 のソースドライバに接続されます。TD62783 の先は蛍光管の各アノード、グリッドに接続。回路的には必要最低限の状態ですね。

2013-03-18 12.09.15

とりあえず、時計っぽい表示だけするプログラムを Arduino に入れまして、ダイナミック点灯させてみたというやつです。

今回は、カソードのバイアスもかけていないしドライバ側も素で接続しているだけのため、。1ms のブランキングタイムを経過した後に必要な桁を 1ms 点灯させる方法で実験しています。ブランキングタイムを設けることでゴーストは改善されます。

タイマ割り込みを 1ms でプログラムしちゃった関係で今はこれが精度の限界。タイマ割り込みを 100μs 毎にすれば、点灯させるデューティー比の設定範囲広げられるので、もうちょっとやってみましょう。

#include

/* ————————————————————
— Class instance
———————————————————— */

/* ————————————————————
— Grobal Variables
———————————————————— */
unsigned int msCount = 0;
boolean msCountOVF;
int hh = 11;
int mm = 58;
int ss = 0;
int vfdDigit = 0;
unsigned char a = 0;
unsigned char b = 0;

/* ————————————————————
— Interrupt hundler
———————————————————— */
ISR(TIMER2_COMPA_vect) {

// inclement millisecond counter
msCount++;
if(msCount >= 1000) {
msCount = 0;
msCountOVF = true;
}
// Reset interrupt flag and counter.
TCNT2 = 0;
TIFR2 &= ~(1< 5) {
vfdDigit = 0;
}
}

if(msCountOVF == true) {
ss++;
if(ss == 60) {
ss = 0;
mm++;
}
if(mm == 60) {
mm = 0;
hh++;
}
if(hh == 25) {
hh = 0;
}
msCountOVF = false;
}
}

/* ————————————————————
— Send vfd
———————————————————— */
void sendVfd(int digit, int num, int dot) {

// HC595 SCLR(shift register clear)
digitalWrite(3,LOW);
digitalWrite(3,HIGH);
// RCK positive edge data latch
digitalWrite(2,LOW);
digitalWrite(2,HIGH);
delay(1);

a = (1<

6LED 駆動回路図の作成と動作確認を実施。

CharliePlexing_6LED201302221213

動作確認とかは、これからですが 6LED ドライブの回路を KiCad でひいてみました。俗称 CharliePlexing の回路でして Wikipedia などにも記事があります。

手持ちは AT90LS2343 だけなので、これでテストするよりかは Arduino が手っ取り早いと思っています。電流制限の抵抗は電源電圧で変るためこの回路図では適当な値になっていますので注意ください。

で、Arduino UNO で3ピン分配線してみまして、テストプログラムを書き込みと動作実験を動画にあげておきました。このような点滅パターンをする Arduino スケッチのコンパイル後のサイズが 1500 バイト程度でした。このあと、8 ピンマイコン差し買えと電池駆動OKか確認出来たら KiCad で PCB だけは起しておこうと思います。

2013年2月23日加筆

2013-02-23 15.03.36

1.8V 以上の電圧で動く 8ピン AVR マイコン ATTINY13V を買ってまいりまして、組み直ししてそれっぽく光るようにプログラムしました。日本橋でも AVR マイコン買えますが地味に高かったりするのが難点です。これなら PIC のほうが値段的にはいいと思うのですが、PIC 使ったことないのでこれは別の課題です。

IN-9 バーグラフネオン管の表示具合をテスト その2

2013-01-19 23.22.29

ちょっとバイオリンやプラモや別件ありまして、これ置いてました。表示具合の確認ということで今回はステレオにしてみて表示出来るかの確認作業です。

結果、12V – 140V DC/DC の出力電圧が変動して、バーの表示がほぼある所で止まるという事象が発生。原因は DC/DC の容量不足ですね。MC34063A の昇圧で、Ipk が 3.5A (後日計算違いでもっと少ないのが判明) なんですが、インダクタは1.7A程度のものですしね。ACアダプタも1Aです。トロイダルで実験したらとか思いつつも入力12Vの10倍以上の電圧を出す磁力と必要な電流で結構発熱しそうですねぇ。パーツボックスにある絶縁トランス出してしまえばある意味即解決ですが、もうちょっと電流ひっぱれるようにならないかやってみましょう。タイミングコンデンサの容量見直すか?う~ん、やっぱキモはインダクタだよなぁ。白目

IN-9 バーグラフネオン管の表示具合をテスト

先日、オペアンプの絶対値回路の基本部分が出来て、それ以前に出来ていた IN-9 のドライブ回路を Arduino にて接続しまして、Analog 入力と PWM 出力を 1ms 毎に行なうようプログラムしてみました。これでそれっぽく表示出来るの? ということで確認一回目というやつです。まだステレオのうち片一方しか組んでないのでネオン管1本だけになっております。

レベル調整など無い状態ですが、まぁ、見た目はそれっぽくいけてますな。ということで、今後はレベル調整など少しづつハードとソフトの調整をかけていきたいと思います。

IN-9 バーグラフネオン管で VU メーター出来るか事前検討

前回までの Blog 記事で、バーグラフネオン管の表示テストやオペアンプの全波整流回路の事前確認を行ないました。VUメータみたいなものを作成するのあたっては、対数変換して表示を圧縮させたほうが人間の比率感覚とあうのでやるのがいいと。AVR マイコンで計算したらどうなるのか気になったのでちょっと検討。ATmega328 の A/D コンバータは10bitの分解能ですが、データシート見ますとリファレンス電圧にチップ内蔵 1.1V を選択することが出来ます。なんかいい電圧値ですね。今回は小型オーディオなどの出力で使えればいいと思いますので 0dBv = 0.775V で見てみました。

20130111213300

Wordpress でテーブルタグ使うのは面倒臭いので表計算の画面のコピペですいません。電圧だけで見てますが -40 から 3デシベルまでいけそうです。ということで

  • オーディオ入力を全波整流
  • ボルテージフォロア
  • マイコン A/D 入力
  • マイコンで対数計算
  • ネオン管出力(時定数持った振れ方を工夫)

の構成で出来そうな目処がついてきました。簡易的にレベル調整するところと、IN-9 の表示バラツキの吸収をどっかでしないといけませんが、悩むところは減りました。よかった。