Take’s diary

Macとマイコンに関すること--ワクワクの製作日記

MicroPython Pyboard を使ってカラーディスプレイ(μOled128)を動かす

ちょっとEdison関連は一休み。続きのメモは次回に回します。

 ところでMicroPython(Pyboard)って知ってますか。STM32F405RGを使ってPython環境でGPIOを制御するという少々毛色の変わったボードです。簡単に言えばパソコン初期のBASICマシンをPythonに置き換えて、IOを制御しちゃおうみたいな、超お手軽Arduinoとでも言っておきましょうか。

                        f:id:TAKEsan:20150812190516j:plain

 このボードの日本での紹介記事が非常に少なく、また、皆さん具体的なソース部分の公開をされていないので、ちっとも面白くありません。そこでこんなの作ってみたので今回は作り方を書いてみます。

 

                             

        ↑ iPhoneでは動画が再生できないようです。ゴメンなさい。

 さわってみたら、まーすごいこと!!。本当にPythonでお手軽にIO制御できちゃいます。i2cも複数もってますし、サーボなんか標準コマンドでダイレクト制御できちゃいます。さらに時間割り込みや、SDカードで大きなプログラム実行やデータロガーにもなっちゃいます。そして加速度センサーを内蔵し、簡単なコマンドで制御できちゃったりします。強いて弱点をあげればWifiが弱いことと、私のPyboardはMAClinuxとの相性が多少悪い(慣れればあまり問題なし)ことぐらいでしょうか。今後のバージョンアップに期待するか、ボードの改良に期待です

 インタプリタなので複雑な画像処理系には弱いのですが、今回の様にインテリジェント型のディスプレイをつなげれば、難なくその部分さえクリアできます。

 標準PythonのようにPipで自由にライブラリをインストールするわけにはいきません(今の所自分で作るしかない)。ただし必要十分なライブラリは標準で入っています。

  こんなスンバラシイボードが、なぜ日本で広く販売されないのが不思議。今回のように表示装置を付けて子供に教えたら夢中になっちゃうでしょうね。

  • Mac:認識するまで時間がかかる。特にSD。Macがスリープすると再開したときシステムエラーになってしまう(単独実行が原則なので極端な問題とならないが、最初は焦った)
  • linux:やはり時間がかかり、場合によってはシステム自体がなかなか起動しない(あとから接続する)        

μOLED-128-G2について

 このディスプレイは発色が素晴らしいのと、シリアルコマンドが充実してるので、Pyboardのようなマイコンとの相性が非常に良好です。今回の参考動画は、私の撮影方法が悪く、発色の良さが分かりませんが、実物を手にして電源を入れるとエエ〜ッ!!と思いますよ。

          f:id:TAKEsan:20150812191450p:plain

 Windows環境では、このディスプレイ独自のIDEで単独実行可能ですが、ここではシリアルモードで使います。日本で販売しているものでは最初からシリアルモードになっているので、別に変更が必要ありませんが、ボーレイトは変更する必要があります。専用ソフトで設定を変更します。めんどくさいので今回はカット。どうしてもわからないときはメールください。昔はパソコン側で好きなボーレイトでデータを送れば自動的に認識してくれたのですが、今は違うみたいです。文字を表示する程度なら初期の設定のままで特に問題ありません。今回はグラフィックスがどのくらいのスピードで実現できるかテストしてみたかったので、115200にしています。

 なお、ディスプレイ側の現在のボーレートは、電源を入れると本体に表示されます。

Pyboardについて。

 Macで接続する場合。

 今説明したように、場合により機器の認識に時間がかかります。ディスクトップにPyboadのアイコンが表示されたらターミナルソフトのつなぎどきです。Pyboardをコンピューターが認識し無いと当然つながりません。待てなくなったらディスクユーティリティを開くと、認識する場合があります。

 Macのターミナルを起動し、ls /dev でターミナルの名称を確認。screen /dev/tty.⚪︎⚪︎⚪︎⚪︎ 9800  で接続。何も表示されなかったら再度Enterまたはcontrol+c で、もうお馴染みのPythonモードになります。 

 boot.py、main.pyがすでに書き込まれてるんで、main.pyの中身を変更すればUSB接続時にこれが実行されます。screen接続して無反応の場合は、control+cを打ち込めばプロンプトが表示されます。再度main.pyを実行したい時はcontrol+dでOK。つまりMacでソースを修正しながら実行が可能です。SDカードの場合は一応2つのプログラムを入れておけばSDカードからの起動となるので、本体のファイル領域が狭くとも全然問題ありません。

 最初に貼り付けた動画は、Pyboardの標準ディスプレイ(高くて能力がイマイチ)用に公開されているライフゲームマンデルブロートのソースを変更して実行してみました。スピードの遅いCPU(168 MHz)でのインタプリタ言語によるプログラムですが、かなり満足できる結果だと思いませんか? 

今回の接続方法

  ちょっと厚めのレザーを土台にして、2つのボードを取り付けてます。今回も必要最小限で機能的なケースができました。ピンがむき出しのところは、サーボが標準コネクタごと刺さります。2個ぐらいなら、本体USB供給電源だけで、なんのストレスなくとても簡単なサンプルソースで動きます。(ピンソケットは自分で付けました)

          f:id:TAKEsan:20150820111355j:plain

         f:id:TAKEsan:20150812195935j:plain

        f:id:TAKEsan:20150812200037j:plain

 

今回のライブラリ

 以下のソースをgfx.pyとして作成して、Pyboadに刺したSDカードにコピーします。μOLED-128-G2のグラフィックライブラリです。リセットが必要なので、μOLED-128のリセット端子と今回はPyboardのX12ピンを接続してます。このライブラリは4Dsystems社の公開コマンド群をPyshon用に書き直しました。

 各コマンドの使用方法は、4D Systems | uOLED-128-G2 と

http://www.4dsystems.com.au/productpages/GOLDELOX/downloads/GOLDELOX_serialcmdmanual_R_1_7.pdf

を参照して下さい。GetAck()の使い方がミソです。メインプログラム開始時は必ずgfx_cls()を実行する必要があります(待ち時間を2回入れてますが、接続機種により、リセットできない場合は、要調整)。Python3が動く環境ならそのまま使えます。基本図形とフォント表示コマンドですが、皆さんの高度な言語能力でさらに修正して使って下さい。(μOLED-160-G2、μOLED-96-G2でもOK。G1シリーズの場合は、G2にファームを書き換えれば動きます。なぜか昔買った160の方がスピードが速いような気がします

import time
from pyb import Pin
from pyb import UART
uart=UART(6,115200)
p_out=Pin('X12',Pin.OUT_PP)
endx=0

def GetAck():
a=uart.read(1)

def gfx_cls():
p_out.low()
time.sleep(0.02)
p_out.high()
time.sleep(5)
cmd=bytes([0xFF,0xd7])
uart.write(cmd)
GetAck()

def gfx_cl():
cmd=bytes([0xFF,0xd7])
uart.write(cmd)
GetAck()

def gfx_BGcolour(Color):
cmd=bytes([0xff,0x6e,Color>>8,Color & 0xff])
uart.write(cmd)
GetAck()

def gfx_movecursor(lin,col):
cmd=bytes([0xff,0xe4,0x00,lin & 0xff,0x00,col & 0xff])
uart.write(cmd)
GetAck()

def gfx_putc(chx):
cmd=bytes([0xff,0xfe,0x00])+chx
uart.write(cmd)
GetAck()


def gfx_chh(height):
cmd=bytes([0x00,0x01])+height
uart.write(cmd)
GetAck()

def gfx_chw(width):
cmd=bytes([0x00,0x01])+width
uart.write(cmd)
GetAck()

def gfx_putstr(str):
cmd=bytes([0x00,0x06])
for char in str:
cmd=cmd+char
cmd=cmd+bytes([0x00])
uart.write(cmd)
GetAck()

def gfx_texfgcolor(Color):
cmd=bytes([0xff,0x7f,Color>>8,Color & 0xff])
uart.write(cmd)
GetAck()

def gfx_texbkcolor(Color):
cmd=bytes([0xff,0x7e,Color>>8,Color & 0xff])
uart.write(cmd)
GetAck()

def gfx_texh(height):
cmd=bytes([0xff,0x7b,height>>8,height & 0xff])
uart.write(cmd)
GetAck()

def gfx_texw(width):
cmd=bytes([0xff,0x7c,width>>8,width & 0xff])
uart.write(cmd)
GetAck()

def gfx_texBold(mode):
uart.write(bytes([0xff,0x76,mode>>8,mode & 0xff]))
GetAck()

def gfx_texInverse(mode):
cmd=bytes([0xff,0x74,mode>>8,mode & 0xff])
uart.write(cmd)
GetAck()

def gfx_texItalic(mode):
cmd=bytes([0xff,0x75,mode>>8,mode & 0xff])
uart.write(cmd)
GetAck()

def gfx_texOpacity(mode):
cmd=bytes([0xff,0x77,mode>>8,mode & 0xff])
uart.write(cmd)
GetAck()

def gfx_texUnderline(mode):
cmd=bytes([0xff,0x73,mode>>8,mode & 0xff])
uart.write(cmd)
GetAck()

def gfx_Circle(x,y,rad,Color):
cmd=bytes([0xff,0xcd,0x00,x & 0xff,0x00,y & 0xff,0x00,rad & 0xff,Color>>8,Color & 0xff])
uart.write(cmd)
GetAck()

def gfx_FillCircle(x,y,rad,Color):
uart.write(bytes([0xff,0xcc,0x00,x & 0xff,0x00,y & 0xff,0x00,rad & 0xff,Color>>8,Color & 0xff]))
GetAck()

def gfx_Line(x1,y1,x2,y2,Color):
cmd=bytes([0xff,0xd2,0x00,x1 & 0xff,0x00,y1 & 0xff,0x00,x2 & 0xff,0x00,y2 & 0xff,Color>>8,Color & 0xff])
uart.write(cmd)
GetAck()

def gfx_Rect(x1,y1,x2,y2,Color):
cmd=bytes([0xff,0xcf,0x00,x1 & 0xff,0x00,y1 & 0xff,0x00,x2 & 0xff,0x00,y2 & 0xff,Color>>8,Color & 0xff])
uart.write(cmd)
GetAck()

def gfx_FillRect(x1,y1,x2,y2,Color):
cmd=bytes([0xff,0xce,0x00,x1 & 0xff,0x00,y1 & 0xff,0x00,x2 & 0xff,0x00,y2 & 0xff,Color>>8,Color & 0xff])
uart.write(cmd)
GetAck()

def gfx_Triangle(x1,y1,x2,y2,x3,y3,Color):
cmd=bytes([0xff,0xc9,0x00,x1 & 0xff,0x00,y1 & 0xff,0x00,x2 & 0xff,0x00,y2 & 0xff,0x00,x3 & 0xff,0x00,y3 & 0xff,Color>>8,Color & 0xff])
uart.write(cmd)
GetAck()

def gfx_Pixel(x,y,Color):
uart.write(bytes([0xff,0xcb,0x00,x & 0xff,0x00,y & 0xff,Color>>8,Color & 0xff]))
a=uart.read(1)

def gfx_Clip(value):
cmd=bytes([0xff,0x6c,0x00,value & 0xff])
uart.write(cmd)
GetAck()

def gfx_ClipWin(x1,y1,x2,y2):
cmd=bytes([0xff,0xbf,0x00,x1 & 0xff,0x00,y1 & 0xff,0x00,x2 & 0xff,0x00,y2 & 0xff])
uart.write(cmd)
GetAck()

def gfx_ExtClip():
cmd=bytes([0xff,0xbc])
uart.write(cmd)
GetAck()


def gfx_GetPixel(x,y):
uart.write(bytes([0xff,0xca,0x00,x & 0xff,0x00,y & 0xff]))
a=uart.read(1)
a1=uart.read(1)+uart.read(1)
if a1==b'\x00\x00':
return 0
else:
return 1

def RGB_color(red, green, blue):
return min(red, 31) << 11 | min(green, 63) << 5 | min(blue, 31)



今回のテストプログラム。

main.py としてPyboadに刺したSDカードにコピーする。


from gfx import *

gfx_cls()
dold=[]
dnew=[]
dold=[[0 for x in range(22)] for y in range(26)]
dnew=[[0 for x in range(22)] for y in range(26)]

def in_set(c):
z=0
for i in range(40):
z=z*z+c
if abs(z)>60:
return 0
return 1

gfx_movecursor(0,0)
gfx_putstr(" Generation=")
for i in range(2):
gfx_movecursor(0,14)
gfx_putstr(" ")
xr=pyb.rng() & 8
for ssy in range(21):
for ssx in range(25):
if (pyb.rng() & 8) == xr:
dold[ssx][ssy]=1
for loops in range(50):
for sy in range(21):
for sx in range(25):
test=dold[sx-1][sy-1]+dold[sx][sy-1]+dold[sx+1][sy-1]+dold[sx-1][sy]+dold[sx+1][sy]+dold[sx-1][sy+1]+dold[sx][sy+1]+dold[sx+1][sy+1]
if dold[sx][sy]==1:
if (test ==2) or (test == 3):
dnew[sx][sy]=1
else:
dnew[sx][sy]=0
else :
if test == 3:
dnew[sx][sy]=1
gfx_movecursor(0,14)
gfx_texBold(1)
gfx_putstr(str(loops))
colorx=pyb.rng() & 0xffff
for sy in range(21):
for sx in range(25):
if (dnew[sx][sy] != dold[sx][sy]):
dold[sx][sy] = dnew[sx][sy]
if dnew[sx][sy] == 1:
gfx_FillCircle(4+5*sx,15+5*sy,2,colorx)
else:
gfx_FillCircle(4+5*sx,15+5*sy,2,0)
gfx_cls()
while True:
xx=pyb.rng()%0xffff
bbb=0
for u in range(128):
for v in range(128):
c=(u/45-2)+(v/22-3)*0.5j
if in_set(c):
gfx_Pixel(u,v,xx)

 このほかに、カメラ付きでOpenCVが動くボードが販売されようとしていて、欲しくなっちゃいますが、よく考えたら今まで紹介してたEdison+SimpleCV+αでそれ以上のことができるので、いらないかも。

takesan.hatenablog.com

 

 次回はEdison再開!!