プログラミング編 UART接続でX68000Zからラズパイを操作!

プログラミングについて

ブログの主のねこぽんは独学のみで趣味のプログラミングをしています。まちがった知識や用語、偏った部分も多分にあると思います。このブログでプログラミングに興味をもち、プログラミングの流れだけをつかんだら他のプログラミングの本や本格的なブログへステップアップしていってもらえたら、とおもいます。

Contents

UARTであそぼう!

プログラムはパズルだ!

ねこっぴ

今回はX-Basic回とPython回で使ったサンプルを少しパワーアップしてお互いUARTで通信できるようにするにゃよ!

ぴぽこ

どんな感じの予定か結果を先にみてみましょ。

X68000Zから操作中!

X68000Zから操作中!

ねこっぴ

X68000Zのキーボードで操作あたり判定はラズパイで処理
わかりやすいようにラズパイ側の表示もそのままにゃけど、これを応用すればラズパイ側の表示はしないで処理のみを徹底的にさせて結果だけをX68000Zに戻すということが出来ますにゃ!

ぴぽこ

今回あたりから、趣味のプログラミングの醍醐味、各所にパズル要素が出てきてるね。

ねこっぴ

障害物部分もそうだにゃ。X,Yを増減したあとに障害物を確認したら障害物があった場合に数値を戻さなきゃいけない、どうしよう みたいな。

ぴぽこ

やりかたは人それぞれでいいよね。効率よく組んで速度を求めるとかも楽しいかもだけど、まずは自分でわかりやすくがいいよね。

UART接続確認、ファイルの準備。

今回はラズパイ3B+でテストをしていきました。

ねこっぴ

ここで物理的な接続方法を確認にゃよ。
接続確認したら今回のサンプルファイルをダウンロードして準備にゃ。

サンプルプログラム ダウンロード

BasicN02_set.zip X68000Z用 HUMAN302(BasicN02).XDF / ラズパイ用 UART-(BasicN02).py

各ファイルをラズパイ、X68000ZmのSDをカードに移動。

UARTプログラミングで遊ぶためには config.sysで RSDRV.SYSが有効になってないとなので、起動すれば有効になるFDイメージを用意しています。

ラズパイ側準備

X68000Z側準備

ねこっぴ

いつもHDDから起動で遊んでて、あれ、FD起動できないぞ? の場合

ねこっぴ

BOOTのとこを、「2HD0」にします!2HD0を選択すると、起動時にSDカード入ってない場合はHDDを起動するからいつもこれでいいかも。
「RS232C」部分は19200にしないとなんだけど、起動時にRSDRV.SYSが有効になってないと19200にできないからここではさわらないにゃ!

ねこっぴ

今回のサンプル HUMAN302(BasicN02).XDF を 起動。
念のために再度SwitchでRS232Cを確認にゃ。
19200bpsになってなかったら変えてね。

ねこっぴ

Switch を終了させて Basic起動
サンプルファイルBasicN02.basをロードするにゃよ。

X68000Z側 サンプルプログラムを確認

ねこっぴ

Switch を終了させて Basic起動
サンプルファイルBasicN02.basをロードするにゃよ。

ぴぽこ

ブロック分けされてるとなんとなくメージわくねー。

1:まえおき

   10 cls ※画面クリア
   20 screen 1,2,1,1 ※画面設定(テキトーです)
   30 int x=30,y=15,ai ※変数宣言(整数)
   40 str a,sousin_word ※変数宣言(文字列)
   50 ai=fopen("AUX","rw") ※こういうもんだと無心でコピペ

2:すでにX68000Zに転送されて溜まってる通信を捌かせる

   60 while not ai=0 
   70  c=fgerc(ai)
   80 endwhile

3:障害物を設置(見た目だけ)。キャラと座標を表示のサブルーチンへ

   90 locate 10,10:print"#"
  100 locate 38,20:print"#"
  110 pri()

4:メインループ キー入力まちの永遠ループ

  130 while not(a = "e")
  140  a = inkey$(0)
  150  if a = "4" then han(4)
  160  if a = "6" then han(6)
  170  if a = "5" then han(5)
  180  if a = "8" then han(8)
  190 endwhile:end

5:判定をラズパイに依頼ルーチン (詳細あり)

  210 func han(k)
  220  cx=x:cy=y:lp=0:c=0 ※サブルーチン内での変数設定
  230  if k=4 then cx=cx-1
  240  if k=6 then cx=cx+1
  250  if k=5 then cy=cy+1
  260  if k=8 then cy=cy-1
  270  sousin_word =itoa(cx)+"*"+itoa(cy)
  280  fwrites(sousin_word,ai)
  290  fwrites(chr$(10),ai)
  300  while lp=0
  310   c=fgetc(ai):locate 10,2:print chr$(c)
  320   if chr$(c)="Y" and k=4 then kesi():x=x-1:pri():lp=1
  330   if chr$(c)="Y" and k=6 then kesi():x=x+1:pri():lp=1
  340   if chr$(c)="Y" and k=5 then kesi():y=y+1:pri():lp=1
  350   if chr$(c)="Y" and k=8 then kesi():y=y-1:pri():lp=1
  360   if chr$(c)="N" then lp=1
  370  endwhile
  380 endfunc
さらなる詳細の説明。 ここを押してね。

一歩先の未来を仮で設定

  230  if k=4 then cx=cx-1
  240  if k=6 then cx=cx+1
  250  if k=5 then cy=cy+1
  260  if k=8 then cy=cy-1

一歩先の未来の座標を文字列としてラズパイに送信 (改行コードもセットで送信で一区切り)

  270  sousin_word =itoa(cx)+"*"+itoa(cy) ※仮想移動は 「*」として送信
  280  fwrites(sousin_word,ai)
  290  fwrites(chr$(10),ai)

通信受信まち(310行) 進めるかどうかは Y/N で帰ってくる 障害物無いときは進む。

  300  while lp=0
  310   c=fgetc(ai):locate 10,2:print chr$(c)
  320   if chr$(c)="Y" and k=4 then kesi():x=x-1:pri():lp=1
  330   if chr$(c)="Y" and k=6 then kesi():x=x+1:pri():lp=1
  340   if chr$(c)="Y" and k=5 then kesi():y=y+1:pri():lp=1
  350   if chr$(c)="Y" and k=8 then kesi():y=y-1:pri():lp=1
  360   if chr$(c)="N" then lp=1
  370  endwhile

6:キャラを消す

  400 func kesi()
  410  locate x,y:print" "
  420 endfunc

7:キャラと座標を表示 (詳細あり)

  440 func pri()
  450  locate x,y:print"O"
  460  locate 2,2:print x
  470  locate 5,2:print y
  480  sousin_word =itoa(x)+"@"+itoa(y)
  490  fwrites(sousin_word,ai)
  500  fwrites(chr$(10),ai)
  510 endfunc
さらなる詳細の説明。 ここを押してね。

キャラ表示時に今現在の座標をラズパイに送信

480  sousin_word =itoa(x)+"@"+itoa(y) ※実際の移動は 「@」として送信
490  fwrites(sousin_word,ai)
500  fwrites(chr$(10),ai)

ラズパイ側 サンプルプログラムを確認

ねこっぴ

Thonnyで開いた状態のプログラム見ていくにゃ。

ぴぽこ

tkinterはボタンとかキー入力のイベント待ちはできるけど、UARTの受信待ちはどうすればいいんだろって番長悩んでたの解決してるぽいね。

ねこっぴ

とりあえず時間でサブルーチン開始の命令あったからそれ使ったみたいにゃ。64行目のこの1行。 0.5秒後にサブルーチンへGO!

1:使用モジュールインポート

import serial #シリアルポート使用
import tkinter

2:UART通信の設定 こういうもんだと 無心でコピペ

ser = serial.Serial('/dev/ttyAMA0',19200,timeout=None)
X=360 #キャラ単位の移動だと12ぐらい X68000Z側 30 の 12倍  
Y=180 #キャラ単位の移動だと12ぐらい X68000Z側 15 の 12倍  

3:障害物位置をリスト化 ほんとはLabel数をリスト数から反映して増やせればいいんだけど今回はスキップ

SankakuList = ["10*10","38*20"]

4:キャラ位置表示(label位置再指定) サブルーチン

def print_Ichi():
    label1.place(x=X,y=Y)
    label2["text"] = str(int(X/12)) + "," + str(int(Y/12)) #こちらは12で割ってX68000Zと合わせる

5:メインループ (詳細あり)

def jyusin_loop():

    global X
    global Y
    
    jyusinOK= False #ループ離脱用。True/False 2択の変数
    while jyusinOK == False:
     line = ser.readline()
     Pri = str(line)
     Pri= line.decode('ShiftJIS')
     Pri = Pri.replace('\n', '')
 
     if Pri.count("*") == 1:
        hit=False
        for num in SankakuList:
            if num == Pri:
                hit=True
                break
            
        if hit==False:
            sousin="Y"
        else:
            sousin="N"
            
        enc2 = sousin.encode()
        #print ("x68へ送信:" + str(enc2))
        ser.write(enc2)#X68に状態を送信      
        
     if Pri.count("@") == 1:
        Plisplit = Pri.split("@")
        X=(int(Plisplit[0]))*12
        Y=(int(Plisplit[1]))*12
        print_Ichi()
        jyusinOK= True
        root.after(1, jyusin_loop)
さらなる詳細の説明。 ここを押してね。

X68000Zから受け取ったデータの整理 こういうもんだセットとして使用で可

 line = ser.readline()
 Pri = str(line)
 Pri= line.decode('ShiftJIS')
 Pri = Pri.replace('\n', '')

X68000Zから受け取ったデータの種類判断

if Pri.count("*") == 1: # *が入ったデータは未来位置 もし未来位置を受け取ったら
#-----------------------------
if Pri.count("@") == 1: # @ が入ったデータは現在位置 もし現在位置を受け取ったら

未来位置に障害物があるかの判定

        hit=False
        for num in SankakuList: #障害物リストを全部ループ(2個入ってるから2回ループ)
            if num == Pri: #num = 1回目 「10*10」 2回目 「38*20」 が入る
                hit=True   #一致したら 障害物があるということ
                break      #もう一致してるのにさらなるループは処理遅くなるので離脱
            
        if hit==False:
            sousin="Y"   #障害物が無い場合は 行けるサインの「Y」
        else:
            sousin="N"   #障害物がある場合は 行けないの「N」
            
        enc2 = sousin.encode() # X68に結果を送信  こういうもんだセット
        ser.write(enc2) 

現在位置データが送られてきた場合の処理

        Plisplit = Pri.split("@") #@を堺に文字列を分割。
        X=(int(Plisplit[0]))*12 #文字列を整数に変換 12倍してラズパイ向けに合わせてます。
        Y=(int(Plisplit[1]))*12 #文字列を整数に変換 12倍してラズパイ向けに合わせてます。
        print_Ichi()
        jyusinOK= True #UART受信ルーチン 一旦離脱。
        root.after(1, jyusin_loop) 即受信ルーチンへ戻らせます。

6:ウインドウ作成

root = tkinter.Tk()
root.title("key")
root.geometry("744x348") #X68000Zで「A」上下左右移動させてカウントしたら 62×29 だったので その12倍

7:障害物設置 (見た目のみ)

label3 = tkinter.Label(root,text="■")
label3.place(x=10*12,y=10*12)
label4 = tkinter.Label(root,text="■")
label4.place(x=38*12,y=20*12)

8:キャラと現在位置用の label設置。UART受信ループへ。

label1 = tkinter.Label(root,text="◎")
label1.place(x=X,y=Y)
label2 = tkinter.Label(root,text="")
label2.place(x=0,y=0)
print_Ichi()
root.after(500, jyusin_loop) #0.5秒後にUART受信ループへGO
root.mainloop()

ラズパイ側 実行、X68000Z側 RUN

ねこっぴ

サンプルプログラムコードを眺めつつ 動かしてみるとなんとなくわかってくるかも。

ぴぽこ

障害物 あるなしを Y/N で返してるけど、 他のアルファベットで M だったら街に入った! とか応用できそうだねー。

ねこっぴ

あとは、今んとこ番長はTkinter初挑戦なのでlableの複製がプログラムから出来るかどうかわからないけど、せっかく障害物をリスト化してるからそのリストから障害物を描画する方向で他の方法を考えたりすると障害物簡単に増やせるにゃね。

全角文字列の送受信はお問い合わせにて。

タイガ

全角文字列のやりとりについて気になる方は お問い合わせ から
お気軽に連絡くださいだダイガ!



番長が実際に購入したもの一覧
Contents