無料ブログはココログ

« 2021年5月 | トップページ | 2021年7月 »

2021年6月

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月 | トップページ | 2021年7月 »