無料ブログはココログ

電子工作

2021年8月24日 (火)

Arduinoでもっと電子工作を11

Lesson10

ネタ切れで中断したArduino電子工作ですが、RailComアドレスリーダをIIC(I2C)デバイスに出来たので、IICの続編とします。
ミント缶は2台目のミント缶からスロットルデータを受信するだけですが、アドレスリーダはマスターからの要求でアドレスを返すIICデバイスにしなければなりません。方法が分からないので、先ずはレーザ距離計で使ったVL53L0Xライブラリでマスターのコードを参照しました。
因みに、疑問だったIICアドレスも、Arduinoでは7ビットでRead/Writeビットはお任せの旨、同ライブラリのコメント欄に記述がありました。

 Wire.beginTransmission(address); //ADDRESS_DEFAULT 0b0101001
 Wire.write(reg); //読みだすデバイスのレジスタを指定
 last_status = Wire.endTransmission();

 Wire.requestFrom(address, (uint8_t)1); //1バイトのデータをリクエスト
 value = Wire.read();

まだスレーブの構築が分からないので、"Wire.requestFrom"でネット検索したら参考になるコードが見つかりました。Rephtone 電子工作
その結果デバイス側は次のコードでいけそうです。
//初期設定
void setup() {
 Wire.begin(myID);
 Wire.onReceive(receiveEvent);
 Wire.onRequest(requestEvent);
}
//マスターがデバイスのレジスタを指定した時
void receiveEvent() {
 while(Wire.available() > 0) {
  registerIndex = Wire.read();
 }
}
//マスターがデータを要求した時
void requestEvent() {
 Wire.write(data[registerIndex]);
}

 

2021年6月22日 (火)

Arduinoでもっと電子工作を10

Lesson9

C-SizeDCC基板本体のIIC(I2C)ポートを使った通信を行います。当初はミント缶IVを2台接続した2列車同時運転が目的でしたが、その後自動運転基板やRailComアドレスリーダなど、用途が増えました。今回は7セグLEDテンキー基板をミント缶用のテンキースロットルにするためのコードを考えます。
(※RailComアドレスリーダは、これからマルチ接続を実現するための改造を行う予定です)

Csizedcc_masterCsizedcc_slave

スロットルがIICマスター、トラックに繋がる方がスレーブです。マスターの固定アドレスとスロットルのアドレス選択で2列車同時運転が可能です。

IICライブラリ

IIC通信をミント缶で使うためのライブラリを作りました。内容の詳細までは触れませんが、概略、初期化・IIC送信・IIC受信の構成です。スロットルで使うのは初期化と送信です。

CSIICtrans.h
//二重インクルード防止のおまじない?
#ifndef CSIICTRANS_H
#define CSIICTRANS_H

#include &ltWire.h> //Arduino I2Cライブラリをインクルード
//ミント缶専用IICコマンド
#define CMD_DIR 1 //進行方向
#define CMD_SPD 2 //スピード設定
#define CMD_FN 3 //ファンクション
#define CMD_ACC 4 //ターンアウト

class CSIICTRANS {
private: //CSIICtrans.cppのみ使用可能
 byte Booster;
public: //CSIICtrans.cpp以外からもアクセス可能
 //変数定義
 int TargetID; //スレーブID
 //ファンクション定義
 void IICstart(byte inBooster, int inAdr); //初期化
 void IICsend(word inAddr, byte inCmd, byte inDat, byte inStat=0); //送信
 void IICwrite(byte len, word inAddr, byte inCmd, byte inDat, byte inStat=0); //Wireを実行
};
#endif //おまじない終わり

CSIICtrans.cpp
#include "CSIICtrans.h"

void CSIICTRANS::IICstart(byte inBooster, int inAdr) {
 if(inBooster == 0) { // スロットルモード
  Wire.begin(); // マスターに設定
 }
}

void CSIICTRANS::IICsend(word inAddr, byte inCmd, byte inDat, byte inStat=0) {
 byte dat = (byte)inDat;
 switch(inCmd) {
  case CMD_FN:
   IICwrite(len, inAddr, 0b01100000 | (dat & 0x0f), (dat & 0x10) | inStat);
       //(サイズ、アドレス、コマンド + Fn No、Fn Dir)
   break;
 }
}
//Wireライブラリを実行する
void CSIICTRANS::IICwrite(byte len, word inAddr, byte inCmd, byte inDat, byte inStat=0) {
 byte aData[4] = {0,0,0,0};
 byte pt = 0;

 aData[pt++] = inAddr;
 aData[pt++] = inCmd;
 aData[pt++] = inDat;
 Wire.beginTransmission(TargetID);
 Wire.write(aData, len);
 Wire.endTransmission();
}

2021年6月 6日 (日)

Arduinoでもっと電子工作を9

Lesson8

週末はDCCをお休みして、汎用の電子工作で遊んでます。今回は、やはり定番の電子ルーレットを作ってみました。停止位置は良く見かける乱数関数は使わず、表示を見て意図的に止められる方法にしました。但し実際に狙った値で止まるかはあなたの腕次第です。

7seg1 7seg6 Num6

[ENT]ボタンを押すと開始、もう一度[ENT]を押すと減速して停止します。7個のセグメンを回転表示して、停止を押された時のセグメント位置で1から7の値を決定します。1セグメントだけを点灯させるため coding()を省略したdisplaySegment()関数をTM1637keyライブラリに追加しました。回転速度で難易度が変わるので、こちらに乱数を使いました。停止する時間も回転速度で変わります。
タスクスケジュールは50msec固定で、回転速度と停止時間はカウンターで可変します。
コードが多いのでzipファイルをアップしました。

// グローバル変数定義
byte gRun = 0;
byte gStart = 0;
byte gStop = 0;
byte gCounter;
byte gRouleCount;
byte gSegPos = 0;
byte gStopNum;

//Task Schedule
unsigned long gPreviousL1;

void loop() {
 // ローカル変数定義
 byte aKeybyte;
 
 if (millis() - gPreviousL1 > 50) {
  gPreviousL1 = millis();
  
  aKeybyte = keyConverter(tm1637key.keyscan());
  if (aKeybyte == 12) { // [ENT]
   gRun ^= 1; // 反転
   if (gRun == 1) { // Run
    gStart = random(0, 4); // 回転速度は0から4までの乱数でカウント
    gCounter = 0;
   }
   else {
    gStop = (128 - (gStart << 4)); // 停止タイム 6.4sec - 3.2sec
    gCounter = 0;
    gRouleCount = 0;
    gStopNum = gSegPos + 1; // セグメントカウンタ + 1
   }
  }

  if (gRun == 1) RunRoulette();
  else EndRoulette();
 }
}

// 回転表示
void RunRoulette() {
 gCounter++;
 if (gCounter > gStart) {
  gCounter = 0;
  DisplaySegment(); // 表示を動かす
 }
}

// 停止までの表示
void EndRoulette()
{
 if (gStop > 0) {
  gStop--;
  if (gStop == 0) {
    tm1637key.display(0, gStopNum); // 表示を止める
  }
  else {
   gCounter++;
   if (gCounter > gRouleCount) {
    gCounter = 0;
    DisplaySegment(); // 表示を動かす
    gRouleCount++; // 徐々に遅くする
   }
  }
 }
}

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月30日 (金)

Arduinoでもっと電子工作を2

Lesson1

手始めは定番のLチカから。先ずは外部回路を使わずArduino Nanoボード単体で行います。

Lchika

グローバル定義

inoファイルの文頭にグローバル定義を記述します。I/Oポート番号はプログラムコード内で指定するよりも、ここで定義した方が分かり易く後の変更も楽です。プログラム全般で共有する変数も定義します。nano Everyの場合はレジスタエミュレーションを使うと、nanoと同じくD13で内蔵LEDを指定する事が出来ます。

#define NANO_LED 13

byte gFalshCount = 0;
byte gLED_State = 0;

//Task Schedule
unsigned long gPreviousL1;

セットアップ処理

void setup() {
 // put your setup code here, to run once:
 pinMode( NANO_LED, OUTPUT);
 
 gPreviousL1 = millis();
}

ループ処理

void loop() {
 // put your main code here, to run repeatedly:
 if( (millis() - gPreviousL1) > 100) {
  //Reset task
  gPreviousL1 = millis();

  gFalshCount++;
  if (gFalshCount > 4) {
   gLED_State ^= HIGH;
   digitalWrite(NANO_LED, gLED_State);
   gFalshCount = 0;
  }
 }
}

スケッチ書き込み

  1. USBケーブルでパソコンとArduino Nano Everyを接続
  2. ボードの[Arduino megaAVR Boards]の[Arduino Nano Every]を選択
    Resisters emulation:はATMEGA328を選択
  3. Arduino Nanoまたはその互換器の場合は
    ボードの[Arduino AVR Boards]の[Arduino Nano]を選択
    旧タイプのArduino Nanoはプロセッサーで[Old Bootloader]を選択
  4. シリアルポートを選択
  5. Update アイコンの「マイコンボードに書き込む」でスケッチを書き込む

※書き込まずにエラーチェックをするにはVerifyアイコンをクリックします。

※megaAVR Boardsボードの完成度?

ビルドしたバージョンは1.8.7ですが、以前のバージョンで制御が働かないIOピンがありました。Everyでビルドする場合は常に最新版にアップデートしておく事をお勧めします。

 

より以前の記事一覧