フォト
2017年5月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ

2017年5月18日 (木)

Arduinoで半角カタカナのローマ字入力

Arduinoで半角カタカナのローマ字入力を行うプログラムを作成しました。
用途的にはArduino STM32用で稼働するTiny BASIC用ですが、Arduino UNOでも
利用出来ます。

01

パソコンの半角入力ではなく、Arduino側でローマ字を半角カタカナに変換して
出力した内容を表示しています。

半角カタカナを正しく表示するには、感じコードをSJIS設定する実用があります。

02

スケッチ
変換テーブルが大きです。
Arduino UNOではフラッシュメモリに配置等の工夫が必要ですね。

//
// ローマ字半角カタカナ入力
// 作成 2017/05/02 by たま吉さん
// 修正 2017/05/17,ltu(ッ)抜けの対応
//

#include <string.h>

#define RK_ENTRY_NUM (sizeof(RomaKama)/sizeof(RomaKama[0]))

//子音状態遷移コード
enum {
  _romaji_top = 0,
  _romaji_b, _romaji_c, _romaji_d, _romaji_f, _romaji_g,  _romaji_h, _romaji_j, _romaji_k, _romaji_l, _romaji_m,
  _romaji_n, _romaji_p, _romaji_q, _romaji_r, _romaji_s,  _romaji_t, _romaji_v, _romaji_w, _romaji_x, _romaji_y,
  _romaji_z, _romaji_by,_romaji_ch,_romaji_cy,_romaji_dh, _romaji_dw,_romaji_dy,_romaji_fw,_romaji_fy,_romaji_gw,
  _romaji_gy,_romaji_hy,_romaji_jy,_romaji_kw,_romaji_ky, _romaji_lt,_romaji_ly,_romaji_my,_romaji_ny,_romaji_py,
  _romaji_qw,_romaji_qy,_romaji_ry,_romaji_sh,_romaji_sw,_romaji_sy, _romaji_th,_romaji_ts,_romaji_tw,_romaji_ty,
  _romaji_vy,_romaji_wh,_romaji_xt,_romaji_xy,_romaji_zy,  
};

// カタカタ文字列変換テーブル
 const char RomaKama[][5][4]  =  {
  //  a       e       i       o        u
  { "\xb1", "\xb4", "\xb2", "\xb5", "\xb3", },                                          //[]  : ア エ イ オ ウ
  { "\xca\xde", "\xcd\xde", "\xcb\xde", "\xce\xde", "\xcc\xde", },                      //[b] : バ ベ ビ ボ ブ
  { "\xb6", "\xbe", "\xbc", "\xba", "\xb8", },                                          //[c] : カ セ シ コ ク
  { "\xc0\xde", "\xc3\xde", "\xc1\xde", "\xc4\xde", "\xc2\xde", },                      //[d] : ダ デ ヂ ド ヅ
  { "\xcc\xa7", "\xcc\xaa", "\xcc\xa8", "\xcc\xab", "\xcc", },                          //[f] : ファ フェ フィ フォ フ
  { "\xb6\xde", "\xb9\xde", "\xb7\xde", "\xba\xde", "\xb8\xde", },                      //[g] : ガ ゲ ギ ゴ グ
  { "\xca", "\xcd", "\xcb", "\xce", "\xcc", },                                          //[h] : ハ ヘ ヒ ホ フ
  { "\xbc\xde\xac", "\xbc\xde\xaa", "\xbc\xde", "\xbc\xde\xae", "\xbc\xde\xad", },      //[j] : ジャ ジェ ジ ジョ ジュ
  { "\xb6", "\xb9", "\xb7", "\xba", "\xb8", },                                          //[k] : カ ケ キ コ ク
  { "\xa7", "\xaa", "\xa8", "\xab", "\xa9", },                                          //[l] : ァ ェ ィ ォ ゥ
  { "\xcf", "\xd2", "\xd0", "\xd3", "\xd1", },                                          //[m] : マ メ ミ モ ム
  { "\xc5", "\xc8", "\xc6", "\xc9", "\xc7", },                                          //[n] : ナ ネ ニ ノ ヌ
  { "\xca\xdf", "\xcd\xdf", "\xcb\xdf", "\xce\xdf", "\xcc\xdf", },                      //[p] : パ ペ ピ ポ プ
  { "\xb8\xa7", "\xb8\xaa", "\xb8\xa8", "\xb8\xab", "\xb8", },                          //[q] : クァ クェ クィ クォ ク
  { "\xd7", "\xda", "\xd8", "\xdb", "\xd9", },                                          //[r] : ラ レ リ ロ ル
  { "\xbb", "\xbe", "\xbc", "\xbf", "\xbd", },                                          //[s] : サ セ シ ソ ス
  { "\xc0", "\xc3", "\xc1", "\xc4", "\xc2", },                                          //[t] : タ テ チ ト ツ
  { "\xb3\xde\xa7", "\xb3\xde\xaa", "\xb3\xde\xa8", "\xb3\xde\xab", "\xb3\xde", },      //[v] : ヴァ ヴェ ヴィ ヴォ ヴ
  { "\xdc", "\xb3\xaa", "\xb3\xa8", "\xa6", "\xb3", },                                  //[w] : ワ ウェ ウィ ヲ ウ
  { "\xa7", "\xaa", "\xa8", "\xab", "\xa9", },                                          //[x] : ァ ェ ィ ォ ゥ
  { "\xd4", "\xa8", "\xb2", "\xd6", "\xd5", },                                          //[y] : ヤ ィェ イ ヨ ユ
  { "\xbb\xde", "\xbe\xde", "\xbc\xde", "\xbf\xde", "\xbd\xde", },                      //[z] : ザ ゼ ジ ゾ ズ
  { "\xcb\xde\xac", "\xcb\xde\xaa", "\xcb\xde\xa8", "\xcb\xde\xae", "\xcb\xde\xad",},   //[by] : ビャ ビェ ビィ ビョ ビュ
  { "\xc1\xac", "\xc1\xaa", "\xc1", "\xc1\xae", "\xc1\xad", },                          //[ch] : チャ チェ チ チョ チュ
  { "\xc1\xac", "\xc1\xaa", "\xc1\xa8", "\xc1\xae", "\xc1\xad", },                      //[cy] : チャ チェ チィ チョ チュ
  { "\xc3\xde\xac", "\xc3\xde\xaa", "\xc3\xde\xa8", "\xc3\xde\xae", "\xc3\xde\xad", },  //[dh] : デャ デェ ディ デョ デュ
  { "\xc4\xde\xa7", "\xc4\xde\xaa", "\xc4\xde\xa8", "\xc4\xde\xab", "\xc4\xde\xa9", },  //[dw] : ドァ ドェ ドィ ドォ ドゥ
  { "\xc1\xde\xac", "\xc1\xde\xaa", "\xc1\xde\xa8", "\xc1\xde\xae", "\xc1\xde\xad", },  //[dy] : ヂャ ヂェ ヂィ ヂョ ヂュ
  { "\xcc\xa7", "\xcc\xaa", "\xcc\xa8", "\xcc\xab", "\xcc\xa9", },                      //[fw] : ファ フェ フィ フォ フゥ
  { "\xcc\xac", "\xcc\xaa", "\xcc\xa8", "\xcc\xae", "\xcc\xad", },                      //[fy] : フャ フェ フィ フョ フュ
  { "\xb8\xde\xa7", "\xb8\xde\xaa", "\xb8\xde\xa8", "\xb8\xde\xab", "\xb8\xde\xa9", },  //[gw] : グァ グェ グィ グォ グゥ
  { "\xb7\xde\xac", "\xb7\xde\xaa", "\xb7\xde\xa8", "\xb7\xde\xae", "\xb7\xde\xad", },  //[gy] : ギャ ギェ ギィ ギョ ギュ
  { "\xcb\xac", "\xcb\xaa", "\xcb\xa8", "\xcb\xae", "\xcb\xad", },                      //[hy] : ヒャ ヒェ ヒィ ヒョ ヒュ
  { "\xbc\xde\xac", "\xbc\xde\xaa", "\xbc\xde\xa8", "\xbc\xde\xae", "\xbc\xde\xad", },  //[jy] : ジャ ジェ ジィ ジョ ジュ
  { "\xb8\xa7", "\x00", "\x00", "\x00", "\x00", },                                      //[kw] : クァ NG NG NG NG
  { "\xb7\xac", "\xb7\xaa", "\xb7\xa8", "\xb7\xae", "\xb7\xad", },                      //[ky] : キャ キェ キィ キョ キュ
  { "\x00", "\x00", "\x00", "\x00", "\xaf", },                                          //[lt] : NG NG NG NG ッ
  { "\xac", "\xaa", "\xa8", "\xae", "\xad", },                                          //[ly] : ャ ェ ィ ョ ュ
  { "\xd0\xac", "\xd0\xaa", "\xd0\xa8", "\xd0\xae", "\xd0\xad", },                      //[my] : ミャ ミェ ミィ ミョ ミュ
  { "\xc6\xac", "\xc6\xaa", "\xc6\xa8", "\xc6\xae", "\xc6\xad", },                      //[ny] : ニャ ニェ ニィ ニョ ニュ
  { "\xcb\xdf\xac", "\xcb\xdf\xaa", "\xcb\xdf\xa8", "\xcb\xdf\xae", "\xcb\xdf\xad", },  //[py] : ピャ ピェ ピィ ピョ ピュ
  { "\xb8\xa7", "\xb8\xaa", "\xb8\xa8", "\xb8\xab", "\xb8\xa9", },                      //[qw] : クァ クェ クィ クォ クゥ
  { "\xb8\xac", "\xb8\xaa", "\xb8\xa8", "\xb8\xae", "\xb8\xad", },                      //[qy] : クャ クェ クィ クョ クュ
  { "\xd8\xac", "\xd8\xaa", "\xd8\xa8", "\xd8\xae", "\xd8\xad", },                      //[ry] : リャ リェ リィ リョ リュ
  { "\xbc\xac", "\xbc\xaa", "\xbc", "\xbc\xae", "\xbc\xad", },                          //[sh] : シャ シェ シ ショ シュ
  { "\xbd\xa7", "\xbd\xaa", "\xbd\xa8", "\xbd\xab", "\xbd\xa9", },                      //[sw] : スァ スェ スィ スォ スゥ
  { "\xbc\xac", "\xbc\xaa", "\xbc\xa8", "\xbc\xae", "\xbc\xad", },                      //[sy] : シャ シェ シィ ショ シュ
  { "\xc3\xac", "\xc3\xaa", "\xc3\xa8", "\xc3\xae", "\xc3\xad", },                      //[th] : テャ テェ ティ テョ テュ
  { "\xc2\xa7", "\xc2\xaa", "\xc2\xa8", "\xc2\xab", "\xc2", },                          //[ts] : ツァ ツェ ツィ ツォ ツ
  { "\xc4\xa7", "\xc4\xaa", "\xc4\xa8", "\xc4\xab", "\xc4\xa9", },                      //[tw] : トァ トェ トィ トォ トゥ
  { "\xc1\xac", "\xc1\xaa", "\xc1\xa8", "\xc1\xae", "\xc1\xad", },                      //[ty] : チャ チェ チィ チョ チュ
  { "\xb3\xde\xac", "\xb3\xde\xaa", "\xb3\xde\xa8", "\xb3\xde\xae", "\xb3\xde\xad", },  //[vy] : ヴャ ヴェ ヴィ ヴョ ヴュ
  { "\xb3\xa7", "\xb3\xaa", "\xb3\xa8", "\xb3\xab", "\xb3", },                          //[wh] : ウァ ウェ ウィ ウォ ウ
  { "\x00", "\x00", "\x00", "\x00", "\xaf", },                                          //[xt] : NG NG NG NG ッ
  { "\xac", "\xaa", "\xaa", "\xae", "\xad", },                                          //[xy] : ャ ェ ェ ョ ュ
  { "\xbc\xde\xac", "\xbc\xde\xaa", "\xbc\xde\xa8", "\xbc\xde\xae", "\xbc\xde\xad", },  //[zy] : ジャ ジェ ジィ ジョ ジュ
};

// 例外([nn])
const char RomaKama_nn[4]  ="\xdd"; // 'ン'

// 母音テーブル
const char BoonTable[]  = { 
  'a','e','i','o','u',
};

// 子音テーブル
const char ShionTable[]  = {
  'b' ,'c' ,'d' ,'f' ,'g' ,'h' ,'j' ,'k' ,'l' ,'m' ,'n' ,'p' ,'q' ,'r' ,'s' ,'t' ,'v' ,'w' ,'x' ,'y' ,'z',
};

// 2文字子音テーブル Xh系列
const char Shion_Xh_Table[][2]  = {
  { _romaji_c, _romaji_ch },{ _romaji_d, _romaji_dh },{ _romaji_s, _romaji_sh }, { _romaji_t, _romaji_th },
  { _romaji_w, _romaji_wh },  
};

// 2文字子音テーブル Xw系列
const char Shion_Xw_Table[][2]  = {
  { _romaji_d, _romaji_dw },{ _romaji_f, _romaji_fw },{ _romaji_g, _romaji_gw },{ _romaji_k, _romaji_kw }, 
  { _romaji_q, _romaji_qw },{ _romaji_s, _romaji_sw },{ _romaji_t, _romaji_tw }, 
};

// 2文字子音テーブル Xt系列
const char Shion_Xt_Table[][2]  = {
  {  _romaji_x, _romaji_xt } ,  {  _romaji_l, _romaji_lt } ,
};

// 2文字子音テーブル Xy系列
const char Shion_Xy_Table[][2]  = {
  { _romaji_b, _romaji_by },{ _romaji_c, _romaji_xy },{ _romaji_d, _romaji_dy },{ _romaji_f, _romaji_fy },
  { _romaji_g, _romaji_gy },{ _romaji_h, _romaji_hy },{ _romaji_j, _romaji_jy },{ _romaji_k, _romaji_ky },
  { _romaji_l, _romaji_ly },{ _romaji_m, _romaji_my },{ _romaji_n, _romaji_ny },{ _romaji_p, _romaji_py }, 
  { _romaji_q, _romaji_qy },{ _romaji_r, _romaji_ry },{ _romaji_s, _romaji_sy },{ _romaji_t, _romaji_ty },
  { _romaji_v, _romaji_vy },{ _romaji_x, _romaji_xy },{ _romaji_z, _romaji_zy }, 
};

int16_t romaji_sts = _romaji_top;  // 状態遷移コード
uint8_t flgTsu = false;            // 小さいツ付加フラグ
char kataStr[6];                   // 確定カタカナ文字列

// 文字コードから母音コード(0~4)に変換する
inline int16_t charToBoonCode(uint8_t c) {
  for (uint8_t i=0; i < sizeof(BoonTable); i++)
    if (c == BoonTable[i])
       return i;
  return -1;
}

// 文字コードから子音コードに変換する
inline int16_t charToShionCode(uint8_t c) {
  for (uint8_t i=0; i < sizeof(ShionTable); i++)
    if (c == ShionTable[i])
       return i+1;
  return -1;
}

// ローマ字カタカタ変換
// 直前の状態遷移から次の状態に遷移する
char* pRomaji2Kana(uint8_t c) {
  int16_t code;
  char*   ptr;

  // 小文字変換
  if (c >= 'A' && c <= 'Z')
    c = c - 'A' + 'a';

  // 文字範囲チェック
  // (後で長音・濁音・半濁音・句点・読点の許可する対応の追加)
  if (c < 'a' || c > 'z')
    goto STS_ERROR;
  
  code = charToBoonCode(c); // 母音チェック
  if (code >= 0) {
    // 母音の場合,文字列を確定する
    if (romaji_sts >= _romaji_top && romaji_sts <= _romaji_zy) {
      ptr = (char*)RomaKama[romaji_sts][code];
      goto STS_DONE;    // 変換完了
    } else
      goto STS_ERROR;  // 変換エラー
  } else {
    // 母音でない場合、子音コードを取得
    code = charToShionCode(c);
    if (code < 0) 
       goto STS_ERROR; // 子音でない(エラー)         
    if (romaji_sts == _romaji_top) {
      // 初期状態で子音コードなら次の状態に遷移
      romaji_sts = code;
      goto STS_NEXT;
    } else if (romaji_sts >= _romaji_b && romaji_sts <= _romaji_z) {
      // 1文字子音受理済みからの子音入力の対応
      if ( romaji_sts == code) {
        // 同じ子音が連続
        if (!flgTsu) {
           if (code == _romaji_n) {
             // nn('ン')の場合
             ptr = (char*)RomaKama_nn;
             goto STS_DONE;    // 変換完了             
           } else {
             flgTsu = true;  // 小さい'ツ'の先頭付加フラグの設定
             goto STS_NEXT;
           }
        } else
          // 既に小さい'ツ'の先頭付加フラグがセットされている場合はエラー
          goto STS_ERROR;
      } else {
        // 2文字子音への遷移チェック
        switch(code) {
        case _romaji_h:
          for (uint16_t i=0; i < (sizeof(Shion_Xh_Table)/sizeof(Shion_Xh_Table[0])); i++)
            if ( Shion_Xh_Table[i][0] == romaji_sts) {
              romaji_sts =  Shion_Xh_Table[i][1];
              goto STS_NEXT;
            }
          goto STS_ERROR;
        case _romaji_w:
          for (uint16_t i=0; i < (sizeof(Shion_Xw_Table)/sizeof(Shion_Xw_Table[0])); i++)
            if ( Shion_Xw_Table[i][0] == romaji_sts) {
              romaji_sts =  Shion_Xw_Table[i][1];
              goto STS_NEXT;
            }
          goto STS_ERROR;
        case _romaji_t:
          for (uint16_t i=0; i < (sizeof(Shion_Xt_Table)/sizeof(Shion_Xt_Table[0])); i++)
            if ( Shion_Xt_Table[i][0] == romaji_sts) {
              romaji_sts =  Shion_Xt_Table[i][1];
              goto STS_NEXT;
            }
          goto STS_ERROR;
        case _romaji_y:
          for (uint16_t i=0; i < (sizeof(Shion_Xy_Table)/sizeof(Shion_Xy_Table[0])); i++)
            if ( Shion_Xy_Table[i][0] == romaji_sts) {
              romaji_sts =  Shion_Xy_Table[i][1];
              goto STS_NEXT;
            }
          goto STS_ERROR;
        default:
          goto STS_ERROR;
        }     
      }
    }
  }
  
STS_NEXT:  // 次の状態へ
  return NULL;
  
STS_ERROR: // [状態遷移エラー]
  romaji_sts = _romaji_top;  // 状態の初期化
  flgTsu = false;            // 小さい'ツ'の先頭付加フラグクリア
  return NULL;
  
STS_DONE:  // [ローマ字カタカナ変換 遷移完了]
  if (flgTsu) {
    kataStr[0] = 0xaf; // 'ッ' の設定
    strcpy(kataStr+1, ptr);
    ptr = kataStr;
  }
  romaji_sts = _romaji_top;  // 状態の初期化
  flgTsu = false;            // 小さい'ツ'の先頭付加フラグクリア
  return ptr;

}

void setup() {
  Serial.begin(115200);
}

void loop() {
  char c;
  char* ptr;
  if (Serial.available()) {
    c = Serial.read();
    if (c == '\n') {
      Serial.println();
    } else {
      ptr = pRomaji2Kana(c);
      if (ptr) {
        Serial.print(ptr);
      }
    }
  }
}

スケッチの作成は次のような状態遷移表(クリックで拡大表示)をEXCELで作成し、
EXCELの関数やらを駆使してC言語用のテーブルを自動生成してます。
EXCELはプログラムを作成のツールとして重宝します。

03

一見、単純なローマ字 => 半角カタカナ 対応のテーブルを使った方が簡単と思ったのですが
意外と難しいく、1文字入力する都度、状況(状態)に応じて変換した方が楽でした。

例えば、"ガッチャマン"と入力する場合、
ローマ字入力では、gattyamann を入力しますが、
単純なローマ字、カタカナテーブルを使って行おうとすると
  ga    => ガ
 ttya  => ッチャ
 ma   => マ
  nn    => ン

のように、可変長文字列を切り出しにて検索する等、ちょと面倒になってきます。
その切り出し処理に、結局は状態の管理が必要になります。
入力された1文字づつ処理した方が単純になります。

2017年5月14日 (日)

豊四季 Tiny BASIC for Arduino STM32の動作テスト(2)

現在取り組んでいる「豊四季 Tiny BASIC for Arduino STM32」の動作確認の続きです。

今回は、ファミコン用ゲームパッドを利用するプログラムを作成しての検証です。

Dscn6614

利用したのは、Aliexpressで入手したゲームパットです。350円くらいで入手しました。

01

ファミコン関連の周辺機器は"NES"で検索して探せますが、コネクタがUS版では
ちょっと異なります。今回はDSUB9ピンのものを利用しました。

Dscn6603

ファミコン用のゲームパッドのボタン状態の読み取りのプロトコルは、
シフトレジスタからのデータ取得の手順となります。

大まかな内部の回路は次のような感じになります。
シフトレジスタCD4021相当のプロトコルにてボタン情報の取得が行えます。
DSUB9ピンのうち、5ピン(GND、VCC、CLOCK、LATCH、DATA)を利用します。

回路図

Photo

Blue Pillボード(TinyBASIC)には、次の結線を行います。
  PB12 2: DATA(INPUT)
  PB13  3: LATCH(OUTPUT)
  PB14  4: CLOCK(OUTPUT)
  3.3V  6: 3.3V
  GND   8: GND

  Dscn6613

  ブレッドボードに接続して利用するために、次のような感じのコネクタを作成しました。

  Dscn6607

  D-SUBオスピンは秋月電子で入手しました。
  このコネクタ、端子がちょっと特殊で2.54ピッチの基板には乗りません。
 
  仕方がなく、1.27ピッチの基板に乗せました。ちょっと面倒でした。
  ブレッドボード用の変換基板があるので、それを買うべきでした。 

04


プログラム

ボタン情報を読み取り、各ボタンの状態0、1を表示します。
50行から100行がシフトレジスタから1ビットずづボタン情報を取得しています。
変数Rに読み取り結果をセットします。
110行でその変数Rの値を2進数で表示しています。

10 CLS
20 GPIO PB12,INPUT_FL
30 GPIO PB13,OUTPUT
40 GPIO PB14,OUTPUT
50 OUT PB13,HIGH:OUT PB13,LOW
60 R=0
70 FOR I=0 TO 7
80 R=R+IN(PB12)<<I
90 OUT PB14,HIGH:OUT PB14,LOW
100 NEXT I
110 LOCATE 0,0:?BIN$(R,8)
120 WAIT 100
130 GOTO 50


実行すると次のような感じになります。
ボタンを押している場合は0、離している場合は1となります。
ボタンは同時押しもOKです。

Dscn6617

画像のビットとボタンの関係は次の通りです。
10111111

左の上位ビットからゲームパッドのボタン⑦⑥⑤④③②①⓪に対応しています。
上のビット情報では⑥のボタンを押した状態です。

Dscn6609

ゲームパッド上にボタンは10個ありますが、
右上の2つはその下のボタンの連打(ターボ)用です。
連打ボタンを押し続けると、オン、オフを自動で繰り返します。

TinyBASICにはシフトレジスタから値を読み取るSHIFTIN()関数があります。
SHIFTIN()関数を使うと、プログラムがちょっと短くかつ処理の高速化が出来ます。

10 CLS
20 GPIO PB12,INPUT_FL
30 GPIO PB13,OUTPUT
40 GPIO PB14,OUTPUT
50 OUT PB13,HIGH:OUT PB13,LOW
60 R=SHIFTIN(PB12,PB14,LSB,LOW)
70 LOCATE 0,0:?BIN$(R,8)
80 WAIT 100
90 GOTO 50

SHIFTIN()関数を利用した記述すると、先ほどの60行~100行の5行は
50行~60行の2行に短縮出来ました。
10行から40行は初期設定なので、実質2行でボタン情報が取得できます。

今後、ゲームなんかに利用していくことにします。

さて、今回の検証でTinyBASICのSHIFTIN()関数の引数を追加しました。
当初、Arduinoのshiftin()と同じ機能そのまま実装したのですが
Arduinoのshiftin()の仕様ではCD4021には対応出来ません。

CD4021ではCLOCKがLOWの場合にデータ取得するのですが、
shiftin()はCLOCKがHIGHの場合にデータを取得にした対応していません。
その仕様が分からず、正しくデータが取得出来ないことにハマりました。

そのため、TinyBASICのSHIFTIN()では第4引数(省略時可能)、データ取得の
CLOCKの状態を指定出来るようにしました。

やはり、実際に利用してみないと問題点は気付かないですね。

2017年5月11日 (木)

豊四季 Tiny BASIC for Arduino STM32の動作テスト(1)

現在取り組んでいる「豊四季 Tiny BASIC for Arduino STM32」
色々と機能が充実してきましたが、テスト不十分です。
そこで動作確認テストとして、BASICにてプログラムを色々と作成して
検証して行こうと思います。

まずは、PWM出力機能を使ってサーボーモーターを制御をやってみました。

01

上記写真がその様子です。安価なサーボモーターSG90をアナログジョイスティックで
グリグリと動かすという制御です。

利用したパーツについては次の通りです。

サーボーモーター SG90
  Aliexpressで入手したものです。秋月電子やAmazonでも入手出来ます。

  Dscn6601

利用方法については秋月電子の製品紹介ページのPDFを参照参考にしました。
データシートによると、
   ・利用電圧 4.8V ~ 5V
   ・PWMのパルス 50Hz (周期 20ms)
   ・-90°~  0° ~ 90°の可動域
   ・Duty Cycle 0.4ms ~ 1.45ms ~ 2.4ms が上記の範囲に対応
とのことです。

アナログジョイスティック
Aliexpressで入手したものです。1個100円くらいだったと思います。
amazonを探すと同じようなものが販売されているようです。

Dscn6602

X,Y方向のアナログ入力、ヘッドを押すとボタンのON/OFF入力が出来ます。
今回はX方向のアナログ入力のみ利用します。

Blue Pillボードとの接続
・SG90
    VCC(赤) => 5V
    GND(茶) => GND
    PWM (オレンジ)  => PA8

・アナログジョイスティック
   VCC => 3.3V
   GND => GND
   X => PB0


プログラム  

1 'サーボモーターセイギョ
5 CLS
10 GPIO PB00,ANALOG
20 GPIO PA08,PWM
30 P=MAP(ANA(PB00),0,4095,102,491)
40 POUT PA08,P,50
45 D=MAP(P,102,491,-90,90)
47 LOCATE 0,0:?#3,D
50 GOTO 30

動作の様子


プログラムの説明

やっていることは単純です、10行、20行は利用するIOピンの設定です。

30行でアナログジョイスティックかあらアナログ値を取得しています。
アナログ入力値の範囲はジョイスティックの操作にて0~4095となりますが、
その値は102~491の範囲にスケール変換して変数Pに設定しています。
MAP関数はArduinoのmap()関数を同等の機能です。

このスケール変換は、SG90の仕様からPWM出力するduty値が4095を100%とした場合、
    -90°=>  102/4095
       0°=>  297/4095
     90°=>  491/4095
であるため、ジョイスティックの入力0~4095を102~491に対応させて、
ジョイスティックの入力で全可動域を操作出来るようにしています。

03

40行のPOUTで50Hz(周期20ms)で0.50~2.40ms幅のパルスを出力しています。
45~47行は画面に角度を表示しています。

このPWM出力いよるサーボーモーター制御、いくつか問題が発生しました。
モーターは結構、消費電力が大きいのかBlue Pillボードから5Vを供給すると、
PS/2キーボードが微妙不安定になったり、USB経由のシリアル出力をすると
フリーズしたりしました。

最初はプログラム的な不具合かと思ったのですが、電源を別から供給することで安定しました。
モーター制御は電源周りには注意が必要ですね。


2017年5月10日 (水)

次はSTM32ボードを積極的に使ていきたい(18)

今回は、内部RTCの時刻を電池で保持する調査です。

「次はSTM32ボードを積極的に使ていきたい(12)」で内部RTCを使った時刻表示、
現在取り組んでいる豊四季Tiny BASICでも利用可能です。
DATE、SETDATE、GETDATE、GETTIME等のコマンドをサポートしています。

Blue PillボードではVBAT端子からの電池によるRTCバックアップが可能です。
手持ちの部品で電池によるバックアップを試してみました。

Dscn6561

次のような感じの結線を行ってVBATに電源を供給します。
VDDにはBlue Pillボード上の3.3Vに接続しています。

02

利用部品
  ・ショットキーバリアダイオード 1N5819 x 2
  ・電解コンデンサ 100μF x 1
  ・ボタン電池 CR2032(3V) x 1

ダイオードはは別の物でもOKです。
順方向の降下電圧が低いものが良いでしょう(1N5819は0.45Vです)。
VBATは2.0V以上であれば良いようです。

実際の接続の様子

Dscn6594

とりあえずこの電池で、電源を落としても時刻が初期化されなくなりました。

2017年4月30日 (日)

次はSTM32ボードを積極的に使ていきたい(17)

Blue PillボードでのSPI2本同時利用のテストです。
NTSCビデオ出力しながら、SDカードからBitmapファイルを読んで表示してみました。

01

画面の解像度は448x216ドットです。
表示内容は青空文庫の蜘蛛の糸をビットマップ画像にしたものです。

02

動いている様子


プログラムは以前 Arduino UNO用に作成したものを組み合わせて、作成しました。
下記のスケッチを流用しました。
   aitendo 16x16LEDマトリックスの制御 (5)
   http://nuneno.cocolog-nifty.com/blog/2016/06/aitendo-16x16-2.html

Arduinoの資産が流用出来るのは、大変うれしいです。

これはこれで、何かに使えそう..

2017年4月27日 (木)

次はSTM32ボードを積極的に使ていきたい(16)

現在取り組んでいる Blue Pillボード用のTiny BASIC、PWM出力を追加しようと調査中です。

Arduino STM32には、標準でPWM出力を行う機能があります。

PA6からデューティ比50%のパルスを出力するには次のように行います。
  pinMode(PA6, PWM);
  pwmWrite(PA6, 0);
  pwmWrite(PA6, 65535/2);


出力結果は次のような感じのパルスとなります。

01

周波数366Hzとちょっと低めですが、デューティ比50%のパルスが出力出来ます。
(550Hzくらいの時もあり、利用するピンによって違うのかもしれません。謎)

pwmWrite(PA6, 0)をやったのは、
Arduino STM32では、pinMode(PA6, PWM)ピン設定だけで、デューティ比50%のパルスが
勝手に出力されてしまうので、設定直後に出力を止める必要があるためです。

標準のAPIでも使えないことはないのですが、
もうちょっと周波数の早いパルスを出したいですね。そこで関数を自作してみました。
//
// Arduino STM32
// PWM出力サンプル(周波数,デューティ比指定)
// 2017/04/27 by たま吉さん
//

#if F_CPU == 72000000L
#define TIMER_DIV 72
#else if  F_CPU == 48000000L
#define TIMER_DIV 48
#endif

#define PWM_PIN PA8
#include <libmaple/timer.h>

//
// PWM出力
// 引数
//   pin     PWM出力ピン
//   freq    出力パルス周波数(0 ~ 65535)
//   dcycle  デューティ比 (0~ 4095:4095で100%)
// 戻り値
//   0 正常
//   1 異常(PWMを利用出来ないピンを利用した)
//
uint8_t pwm_out(uint8_t pin, uint16_t freq, uint16_t duty) {
  uint32_t dc;
  
  timer_dev *dev = PIN_MAP[pin].timer_device;     // ピン対応のタイマーデバイスの取得 
  uint8_t cc_channel = PIN_MAP[pin].timer_channel;  // ピン対応のタイマーチャンネルの取得

  if (! (dev && cc_channel) ) 
    return 1;  

  uint32_t f =1000000/(uint32_t)freq;  // 周波数をカウント値に換算
  dc = f*(uint32_t)duty/4095;
  timer_set_prescaler(dev, TIMER_DIV);  // システムクロックを1MHzに分周
  timer_set_reload(dev, f);             // リセットカウント値を設定 
  timer_set_mode(dev, cc_channel,TIMER_PWM);
  timer_set_compare(dev,cc_channel,dc);    // 比較レジスタの初期値指定(デューティ比 0)
  return 0;
}

void setup() {
  pinMode(PWM_PIN, PWM);
  pwmWrite(PWM_PIN,0);
  pwm_out(PWM_PIN, 20480, 4095/2);
}

void loop() {

}


20kHzのパルスを出してみました。
デューティ比の指定は、アナログ入力の分解能が12ビット(0~4095)なので
スケールをそれに合わせまあした。

02

しっかりと出力出来ました。

PWM出力はタイマー資源を使います。
TinyBASICではTIMER1とTIMER3がまだ未使用なので、
このタイマーに対応するGPIOピンで利用出来るようにしたいと思います。

追記
上手く行ったと思ったら、なぜかTIMER1だけ動かない。調査中..
対応し、スケッチを修正しました。初期化がまずかったようです。

2017年4月20日 (木)

次はSTM32ボードを積極的に使ていきたい(15)

移植&機能追加中のBlue Pill用豊四季タイニーBASIC、
コマンド等を追加しました。

豊四季タイニーBASIC for Arduino STM32 V0.7
https://github.com/Tamakichi/ttbasic_arduino/tree/ttbasic_arduino_ps2_ntsc

色々と遊べるようになってきました。

ビットマップ表示



スクロール表示


試しに、簡単な下記のプログラムでIchigoJamとBlue Pillボードで性能比較してみました。

10 FOR I=0 TO 30000
20 A=(12345/67+89)*10
30 NEXT I

;結果次の通りです。

処理にかかった時間(TICK()で測定)
  IchigoJam       84.3秒
  Blue Pillボード  0.8秒
マイコンレベルのスペックでIchigoJamの3倍くらいの性能が出ればいいと思っていたところ、
なんと100倍近い性能!

この理由は、IchigoJamが中間コード形式を採用していないためだと思われます。
IchigoJamが"12345"を30,000回文字列から2バイト整数に変換してから計算するのに対し、
tinyBASICでは、最初に中間コードに変換した状態でプログラムを実行します。
"12345"は実行時に2バイト整数に変換されている状態です。
中間コード変換はかなり、効果があることが分かりました。


Blue Pillボードを使った TinyBASICの構成は次のような感じです。

接続図(クリックで拡大表示します)

02

利用可能市販パーツ
 

2017年4月13日 (木)

次はSTM32ボードを積極的に使ていきたい(14)

豊四季版tiny BASIC をBlue Pill用に移植&機能追加中です。

02

オリジナル版は、シリアル接続で行単位のプログラム入力でしたが、
ビデオ出力とPS/2キーボード対応、フルスクリーンエディタ対応等の機能追加して
IchigoJamのような感じで利用出来るようになってきました。

03

04

グラフィック描画や、GPIOを使った入出力、I2C、シリアル通信、RTC対応等
色々と詰め込中です。

豊四季タイニーBASIC for Arduino STM32 V0.6
https://github.com/Tamakichi/ttbasic_arduino/tree/ttbasic_arduino_ps2_ntsc

2017年4月 9日 (日)

次はSTM32ボードを積極的に使ていきたい(13)

コントローラーにILI9341を使った液晶モジュールの動作確認
メモしておかないと結線忘れるので記録に残しておきます。

Dscn6523


Arduino STM32にはILI9341を使った液晶モジュールを使うためのライブラリが用意されています。

ライブラリ
   Adafruit_GFX_AS         Adafruit GFX Graphics Library(Arduino版の修正版 _ASが付く)
   Adafruit_ILI9341_STM   ILI9341搭載液晶モジュールコントローラ

ライブラリのサンプルスケッチを使って、手持ちのILI9341搭載液晶モジュールを
動作させてみました(SDカード、タッチパネルも使えるのですが今回は表示のみです)。


  接続(クリックすると拡大表示します)

01


サンプルスケッチはそのままでは動かす、ピン割り付け部分の修正が必要です。
下記のように接続しました。CS、RESET、D/Cは任意のピンが利用出来ます。
SPI接続のピンA5,A6,A7は変更できません。

    02


graphicstest.inoの修正箇所

#include "SPI.h"
#include "Adafruit_GFX_AS.h"
#include "Adafruit_ILI9341_STM.h"

#define TFT_CS  PA0                  
#define TFT_RST PA1
#define TFT_DC  PA2

            
//Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC);
Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  Serial.begin(115200);
  delay(3000);
  Serial.println("ILI9341 Test!"); 
 
  tft.begin();

RESETをBlue Pillのリセットと連動させる場合はBlue PillのRSTへの接続でもOkです。
その場合は、TFT_RSTを-1にするか、コメントアウトしているコンストラクタの方を
利用して初期化します。

Adafruit GFX Graphics Libraryを使って自分で好きなように動かしたい場合は、
Adafruitのサイトの公開情報が参考になります。
  ・Adafruit GFX Graphics Library
   https://learn.adafruit.com/adafruit-gfx-graphics-library/

Arduino STM32に付属しているAdafruit GFX Graphics Libraryは、フォントが削られているので、
上記リンクからライブラリをダウンロードしてフォントだけコピーしたほうがよいかも。
(訂正) Arduino Stm32版では、setFont()が削られていて使えないようです。

Arduino UNOに比べると描画速度が非常に速いです。
Arduino UNOではSPIのクロックが8MHzなのに対して、Blue Pillはで36MHzです。

次のYoutubeの動画によると、Arduino Unoの12倍の速度で描画出来るようです。
 
   Generic STM32 vs Arduino Pro Mini/Nano speed comparison
   

2017年4月 4日 (火)

Arduino IDEでのchar型の変数の振舞

最近のArduino IDEでは従来のAVRマイコンほ他に、ARM系のボードもサポートしています。
スケッチもある程度、他のマイコンボードへの流用が出来ます。

私もAVR系のArduino UnoのスケッチをSTM32のボードに流用したりするのですが、

動作が異なる不可解な現象が発生し、ちょっとハマりました。

色々と突き詰めていくと、
AVRマイコンとARMマイコンではchar型の扱いが異なることが判明しました。
次のスケッチはchar型の変数aに-1を格納して表示する例です。
void setup() {
  Serial.begin(115200);

  char a;
  a= -1;
  Serial.print("a=");
  Serial.println(a,DEC);
  Serial.print("-1=");
  Serial.println(-1,DEC);

}

void loop() {

}

Arduino UNOの実行結果   Arduino Duo、M0、Bulle Pillボードの実行結果

Arduino          Arduinom0

if 文判定でも差異は明確に現れました。

まあ、char型の変数にマイナス値を使うようなプログラムはあまりないですが、
先日、ゲームの移植でちょっとハマってしまいました。

同じ開発環境でこの不統一はちょっとやめてほしいなぁ

char型についてはちょっと気を付ける必要があります。
私は、int8_t、uint8_tとC99的に記述しています。

«次はSTM32ボードを積極的に使ていきたい(12)