フォト
2017年8月
    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年4月 | トップページ | 2017年6月 »

2017年5月の5件の記事

2017年5月28日 (日)

豊四季 Tiny BASIC for Arduino STM32の動作テスト(3) - LEDドットマトリックス

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

LEDドライバ、MAX7219を使った8x8LEDドットマトリックスの制御をやってみました。

02

利用したモジュールはAliexpressで以前入手したものです。
1個150円程度で購入できます。amazonでも1個200円くらい入手出来るようです。

Free shipping! 1PCS MAX7219 dot matrix module

01

実際の動作


思っていたより、スムーズに表示出来ました。
以前IchigoJamでやった時は、1文字1秒かかりスクロール表示なんて無理な状況でした。
さすが、IchigoJamの100倍近い性能、難なく動かせました。

プログラムソース

1 'MAX7219(PB13: CLOCK, PB12: CS, PB14: DAT)
100 GOSUB "INIT"
110 M="ネコニコンバンワ!"
115 T=100
120 L=LEN(M)
130 FOR S=1 TO L
140 C=ASC(M,S)
150 GOSUB "SCROLLIN(C,T)"
160 NEXT S
170 END
200 "SCROLLIN(C,T)"
210 FOR J=0 TO 5
220 FOR K=0 TO 7
230 @(K)=(@(K)<<1)|(PEEK(C*8+FNT+K)>>(7-J))
250 NEXT K
255 GOSUB "DISP(@)"
260 WAIT T
270 NEXT J
280 RETURN
300 "DISP(@)"
310 FOR I=0 TO 7
320 R=I+1
330 V=@(I)&$FF
340 GOSUB "WRITE(R,V)"
350 NEXT I
360 RETURN
400 "INIT"
410 GPIO PB12,OUTPUT
420 GPIO PB13,OUTPUT
430 GPIO PB14,OUTPUT
440 @(0)=$0B,7,$0A,0,$0C,1,9,0,$0F,0
450 FOR I=0 TO 8 STEP 2
460 R=@(I):V=@(I+1)
470 GOSUB "WRITE(R,V)"
480 NEXT I
485 FOR I=0 TO 7:@(I)=0:NEXT I
490 RETURN
500 "WRITE(R,V)"
510 OUT PB12,LOW
520 SHIFTOUT PB14,PB13,MSB,R
530 SHIFTOUT PB14,PB13,MSB,V
540 OUT PB12,HIGH:OUT PB12,LOW
550 RETURN

プログラムの構造的には次のような感じになっています。
ボトムアップ的(下位から上位)に解説します。

・510~550行:ラベル "WRITE(R,V)"
MAX7219へのコマンド送信を行うサブルーチン
変数Rがレジスタ、変数Vがレジスタに設定する値です。
ボード上の端子PB12がCS、PB14がデータ、PB13がCLKへの出力です。
シフト操作によってR、Vの16ビットを送信しています。

ちなみに、ラベル名の"WRITE(R,V)"は単なるラベル名で、(R,V)は引数として
渡していることを分かり易くするためラベルに付けているだけです。

・400~490行:ラベル "INIT"
MAX7219の初期化処理
PB12,PB13,PB14を出力設定にしたのち、
"WRITE(R,V)"を呼び出して、440行のレジスタその設定値の内容を送信しています。
また、485行で8x8ドットの表示用バッファ(配列@(0)~@(7)を初期化しています。
レジスタ設定では輝度等の設定を行っています。

・300~360行:ラベル "DISP(@)"
8x8ドットパターンの表示処理
8x8ドットの表示用バッファ(配列@(0)~@(7))の内容をMAX7219に送信しています。
送信には"WRITE(R,V)"を呼び出しています。

・200~280行:ラベル "SCROLLIN(C,T)"
1文字単位のスクロール挿入処理
変数Cで指定した文字をスルロールしながら挿入します。
スクロールする際の1ドット毎のウェイトを変数Tで指定します。
8x8ドットの表示用バッファ(配列@(0)~@(7))に1ドットスクロールしたデータを書き込み
"DISP(@)"を呼び出して実際の表示を行っています。

文字のドットデータは、フォントデータを利用しています。
フォントデータの格納アドレスは定数FNTにて参照できます。

・100~170行:メイン処理
  下位のモジュールを使って文字列表示を行っています。
   変数Mに文字列、変数Tにスクロール速度を指定します。
   このレイアでは、MAX7219の使い方は意識しないで表示したい文字を指定して
   表示するだけです。
   
処理的に余裕がありそうなので、手持ちの四連結もMAX7219も制御出来そうです。
そのうちチャレンジ試してみよう..

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) - Tiny BASIC(4)

今回は、内部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月 | トップページ | 2017年6月 »