無料ブログはココログ

« 2021年4月 | トップページ | 2021年6月 »

2021年5月

2021年5月30日 (日)

C-SizeDCC Throttle

C-SizeDCC基板に7セグLEDテンキー基板を重ねる

アナログ動力車を自動運転するための個人用C-SizeDCC基板でしたが、7セグLEDテンキーのArduino電子工作をテーマにスケッチを進める過程で、手動操作のDCCコントローラが出来そうな予感がしたので作ってみました。モード変更はジャンパを切り換えて電源再投入が必要で使い勝手は良くありませんが、ほゞミント缶IV同様の機能を組み込めました。
テンキーでアドレス設定するので、素早い変更が出来ず複数の車両運転には不向きですが、ランダムなアドレスの車両一台だけなら便利に使えそうです。老眼が進んで液晶画面が辛くなって来たのでリバイバルの7セグ表示は楽チンです。

テンキーの操作はこんな感じです。[ADDR]モードの緑ボタンでOPS CVにも対応しました。スケッチを進める過程で一部ボタン名と機能に不一致が生じましたが、ウェブページの「C基板サイズDCCコントローラ」項目にスケッチをアップして、試作で余った基板はFC2ショッピングカートに頒布登録しました。

Tenkeythrottle

2021年5月18日 (火)

Arduinoでもっと電子工作を8

Lesson7

TM1637は7セグLEDの他に16個のキー読み出しが出来ます。前回の基板にタクトスイッチをはんだ付けして、押したテンキーの数値を7セグLEDに表示してみます。keyscan()関数は反転されたデータが返るので、keyConverter()関数を作って都合の良い値に変換しました。keyscan()の値はLesson6で紹介したSerial.printで確かめました。

Segkey

Arduinoフォーラム(forum.arduino.cc)にアタッチされていた、キースキャンを追加したTM1637ライブラリを拝借して、クラス定義をTM1637keyにしました。

グローバル定義

//定義ファイルインクルード
#include "TM1637key.h"

//デバッグを有効にする
#define DEBUG

//I/Oポート番号定義
#define PIN_CLK 13
#define PIN_DIO 11

//TM1637keyライブラリをクラス定義して初期化
TM1637 tm1637key(PIN_CLK, PIN_DIO);

//グローバル変数定義
byte KeyBuff[] = {0x7f,0x7f,0x7f,0x7f}; //4桁のブランクデータ
byte gKeyByte; //ローカル変数定義

//function
byte keyConverter(byte inkey);

//Task Schedule
unsigned long gPreviousL1 = 0;

セットアップと実行

void setup() {
 Serial.begin(115200);
 while (!Serial);
 
 tm1637key.init();
 tm1637key.set(BRIGHT_TYPICAL);
 
 tm1637key.display(KeyBuff); //ブランク表示

 gPreviousL1 = millis();
}

void loop() {
 byte aKeybyte;
 
 if( millis() - gPreviousL1 > 1000) {
  //Reset task
  gPreviousL1 = millis();
  
  aKeybyte = tm1637key.keyscan();
  if (aKeybyte != 255) {
   #ifdef DEBUG
   Serial.println(aKeybyte);
   #endif
   //7セグ表示に都合の良い値に変換
   gKeyByte = keyConverter(aKeybyte);
   //7セグに表示する
   DisplayKey(gKeyByte);
 }
}

デバッグ出力

247
246
245
244
243
242
241
240
239
238

スキャンデータ変換関数

byte keyConverter(byte inKey)
{
 byte aKey_bit = inKey & 0x1f; //上位3ビットをマスク
 
 aKey_bit = aKey_bit ^ 0x1f; //ビットを反転
 aKey_bit -= 8; //7セグに表示できる値にする
 
 return aKey_bit; //結果を返す
}

表示関数

void DisplayKey(byte inDat)
{
 //直前の表示を上位桁にシフト
 for (byte i=3; i>0; i--) {
  KeyBuff[i] = KeyBuff[i - 1];
 }
 //押されたキーを最下位桁に表示
 KeyBuff[0] = inDat;
 
 //display(uint8_t, int8_t)関数のオーバーロードを使う
 tm1637key.display(KeyBuff);
}

 

2021年5月15日 (土)

Arduinoでもっと電子工作を7

Lesson6

デバッグ出力

Arduino nano Every本体のUART通信を動かしてみましょう。普段はスケッチ書き込みに使いますが、Arduino IDEの[ツール]-[シリアルモニタ]で受信データを表示すると、デバッグに役立ちます。実例としてDesktop StationさんのDSCoreM.cppのコードを引用します。無断引用で申し訳ございません。DS製品をお持ちの方は、一度はダウンロードされていると思いますのでご容赦ください。
#ifdef DEBUG
 Serial.println(F("CV read Successed!"));
#endif

こんな具合でプログラム中、動作を確認したい箇所に挿入して、結果をArduino IDEで確認します。

Serial

Serialコードを有効にするには、初期化でSerial.begin(baudo);を実行します。

グローバル定義

#define DEBUG

セットアップ

void setup() {
 Serial.begin(115200); //115200ボー
 while (!Serial); //Serialが有効になるまで待つ
}

実行

void loop() {
}
loopで実行されるコードにSerial.printを挿入してデバッグ情報をUARTに出力します。
#ifdef コードは条件コンパイルで#defineで定義されている時のみコンパイルコードに含みます。デバッグが完了したら#define DEBUGをコメントアウトして、不用意なUART出力を防ぐと共にビルドサイズを縮小します。Serialは意外にフラッシュ領域を圧迫するので、特に領域不足のミント缶は影響大です。

Serial1

ATMEGA4809は4チャンネルのUARTを実装していて、ArduinoでSerial1からSerial4まで用意されている様ですが、ピンアサインは固定なのかユーザ選択なのか、今のところ複数のUARTを使う予定は無いので未調査です。

2021年5月10日 (月)

Arduinoでもっと電子工作を6

Lesson5

C-Size DCC基板にはSPI通信用のスルーホールがあります。ミント缶と同じOLEDが使えますが、秋月電子にはSPIモード製品の取り扱いが無いので、TM1637ディスプレイドライバーを使って7セグLEDの表示を行います。TM1637のライブラリは、基板製作でお世話になっているSeeedStudioのDigitalTubeを利用しました。
サンプルコードに忠実に、TM1637クラス内の関数を使って初期化と表示を実行したら、簡単に7SegLED表示が出来ました。display()関数の第一パラメータBitAddrは0が最下位です。

Tm1637

ロープロファイルピン

秋月電子のArduino Nano Everyはピンヘッダーがハンダ付けされていないセミキットを使用します。別途ロープロファイルピンとソケットを購入して、高さを抑えて表示基板を重ねます。CサイズDCC基板には連結ピンヘッダ6/9/3をハンダ付け、LED基板のボトム面にロープロファイルソケットをハンダ付けして基板を重ねると、基板の間隔は16mmでした。単体のスペーサは高価なので14mmスペーサーセット(P-01861)を利用してナットセット(P-01885)で嵩上げしても良いかと思います。

Lowprofil

スケッチ

グローバル定義

//定義ファイルインクルード
#include "TM1637.h"

//I/Oポート番号定義
#define PIN_CLK 13
#define PIN_DIO 11

//TM1637ライブラリをクラス定義して初期化
TM1637 tm1637(PIN_CLK, PIN_DIO);

//グローバル変数定義
word gCount = 0;

unsigned long gPreviousL1 = 0; //Task Schedule

セットアップと実行

void setup() {
 tm1637.init();
 tm1637.set(BRIGHT_TYPICAL);
 
 gPreviousL1 = millis();
}

void loop() {
 if( millis() - gPreviousL1 > 1000) {
  //Reset task
  gPreviousL1 = millis();
  
  gCount++;
  if (gCount > 9999) {
   gCount = 0;
  }
  DisplayNum(gCount);
 }
}

7セグに表示する関数

// loop()から1秒毎に呼び出され、カウント値を4桁7セグLEDに表示します
void DisplayNum(word inCount)
{
 byte colval = inCount / 1000;
 
 tm1637.display(3, colval);
 colval = inCount % 1000;
 tm1637.display(2, colval / 100);
 colval = colval % 100;
 tm1637.display(1, colval / 10);
 tm1637.display(0, colval % 10);
}

2021年5月 6日 (木)

Arduinoでもっと電子工作を5

Lesson4

型宣言

Arduinoは型宣言が必要...との指摘を頂きました。一連の記事で毎回グローバル定義に出てきますので、ここらでデータ型をご紹介します。Arduino 日本語リファレンスからの引用です。

バイト数 範囲 備考
bool 1byte true - false  
char 1byte -128 - +127 char myChar='A'; char myChar=65;
unsigned char 1byte 0 - 255 byte型の使用を推奨
byte 1byte 0 - 255  
int 2byte -32768 - +32767 符合あり
unsigned int 2byte 0 - 65535  
word 2byte 0 - 65535  
long 4byte   符合あり
unsigned long 4byte    
float 4byte   浮動小数点型
double 8byte   Dueで有効
void     値を返さない関数、値の無い引数

陥りやすいのは範囲のオーバーフローと、よく使いがちなintで符合反転に気づかない事でしょうか。皆さんは間違ったbool型を使う事は無いと思いますが、つい先日コピペして気づかずカウンターにbool型を使ってしまい、時間を浪費しました。

幅指定整数型

詳細未確認で恐縮ですが、インクルードファイル(xxx.h)でbyte型などを定義するとエラーで弾かれる事があります。スケッチをビルドするとArduino.hヘッダーファイルが暗黙的にインクルードされるとの事ですが、ヘッダーファイルのそものは例外のようです。システムでインクルードされるホルダを調べたところ、Common.hに typedef uint8_t byte;の記述がありました。自作のxxx.hファイルには#include <Arduino.h>を記述するか、uint8_t、uint16_tなどの幅指定型で定義すると良いでしょう。

 

2021年5月 3日 (月)

Arduinoでもっと電子工作を4

Lesson3

基板上のモータードライバーでPWM出力します。Lesson2と同じく電源は12VのACアダプタを使用します。LEDはDCCトラックのモニター用で点灯は方極性のみですが、極性切替のプログラムコードを追加すればアナログ動力車用のコントローラとして機能します。但しnano EveryのD9、D10はタイマー0がPWMに割り付けられているので、PWM周波数を高速化するとmillis()などのシステムタイマー関連のパラメータをすべて変更しなければなりません。PWM出力専用に用意したTB67H450モータードライバー用のD3、D6で出力する事をお勧めします。

Pwm_low_20210503133501Pwm_high_20210503133501

グローバル定義

#define PIN_VR A7
#define PIN_A 9
#define PIN_B 10

//Task Schedule
unsigned long gPreviousL1;

I/Oポート初期化

void setup() {
 // put your setup code here, to run once:
 analogWrite(PIN_B, 0);
}

PWM制御を実行

PWMを出力するには、digitalWriteに代えてanalogWriteを使います。DAコンバータでもないのにanalogWriteは妙ですが、Arduinoの約束事です。DesktopStationさんのピュアアナログアダプタを使えば真のアナログになるので良しとしましょう。
PWMのデューティ比を決めるボリュームの値はanalogReadで取得します。こちらは本当にアナログ入力ですね。

void loop() {
 // put your main code here, to run repeatedly:
 byte aVol;
 if( (millis() - gPreviousL1) > 100) {
  //Reset task
  gPreviousL1 = millis();
  
  aVol = (analogRead(PIN_VR) >> 2); // 1020 -> 255
  analogWrite(PIN_A, aVol);
 }
}

 

2021年5月 1日 (土)

Arduinoでもっと電子工作を3

Lesson2

基板上のモータードライバーでLチカします。書き溜めていた原稿をアップするのを忘れてましたので、連日の更新です。電源は鉄道模型で使用する12VのACアダプタを使用します。ターミナルブロックに何か接続するのは危険です。基板上のDCCパケットモニタLEDでLチカを確認します。

Lchika2

I/Oポート番号定義

モータードライバーの入力ピンに繋がるポートを定義します。
#define NANO_LED 13
#define PIN_A 9
#define PIN_B 10

byte gFalshCount = 0;
byte gLED_State = 0;

//Task Schedule
unsigned long gPreviousL1;

I/Oポートと変数を初期化

void setup() {
 // put your setup code here, to run once:
 pinMode(NANO_LED, OUTPUT);
 pinMode(PIN_A, OUTPUT);
 pinMode(PIN_B, OUTPUT);
 digitalWrite(PIN_B, LOW);

 gPreviousL1 = millis();
}

Lチカ実行

void loop() {
 // put your main code here, to run repeatedly:
 if( (millis() - gPreviousL1) > 100) {
  //Reset task
  gPreviousL1 = millis();
  
  gFalshCount++; // 0.1秒毎にカウンターに1を加える
  if (gFalshCount > 4) {
   gLED_State ^= HIGH; // 0.5秒でLEDステータスを反転
   digitalWrite(NANO_LED, gLED_State);
   digitalWrite(PIN_A, gLED_State); // モータードライバーにLEDステータスを出力
   gFalshCount = 0; // カウンターを0に戻して繰り返し
  }
 }
}

« 2021年4月 | トップページ | 2021年6月 »