10年前の工作物を活かすために、初代Raspberry PiでFT245RLを使用した。 Raspberry PiにはGPIOがあるので、これから何かを作る際に、FT245RLを使うことはあまり無いかもしれない。 (2022/09/18)。
秋月電子で購入.一個980円.10年以上前からあるけど、今現在でも売られている(2022/09/18)。 FTDI社のFT245RLにUSBコネクタ付けて,パスコンなど取り付けて,使い易くした製品. FTDI社の製品は,USB-シリアル変換器チップで有名なシリーズであるが, これは,USB-シリアル変換に,バッファとパラレル入出力を付けたものらしい.
USBケーブルで接続する方法には2種類ある。
がある。
これを、Linux, Windows, macOSマシンのUSBに接続するとシリアルポートとして見える。 昔は、FTDI社が配布している仮想シリアルドライバが必要だった。 でも現在は、標準的に含まれているのか、だいたい、挿せばシリアルとして見える。
例えば、Raspberry piでは、
$ ls -al /dev/ttyAMA0 crw--w---- 1 root tty 204, 64 Sep 18 02:33 /dev/ttyAMA0 $ ls -al /dev/serial/by-id/usb-FTDI_FT245R_USB_FIFO_AM00A7S3-if00-port0 lrwxrwxrwx 1 root root 13 Sep 6 20:07 /dev/serial/by-id/usb-FTDI_FT245R_USB_FIFO_AM00A7S3-if00-port0 -> ../../ttyUSB0
というように見える。実体は1個で、/dev/serial/by-idにある方は、/dev/ttyAMA0のシンボリックリンク。
ただ、FT245がシリアルとして見えて、読み書きできるが、その結果をパラレルピンに入出力するには、
を使用してデータのやり取りを行う必要がある。具体的には、
FT245RLからRaspberry Piにデータを送るためには,
という手順をする。逆に、Raspberry PiからFT245RLにデータを送る場合は
といった手順になる。パラレルピンに入出力される値のタイミングがどうでも良ければ、
だけで良いらしい。実際に、RXFとRDを直結したところ、
echo -en '\x55' > /dev/serial/by-id/usb-FTDI_FT245R_USB_FIFO_AM00A7S3-if00-port0
というような操作で、奇数ポートがhigh, 偶数ポートがlowになった。 追加の配線が必要だけど、出力にしか使わない場合は、これが一番簡単かもしれない。
FTDI社が配布しているライブラリーをインストールして呼び出すと, Bit-Bangモードという方式でパラレル入出力できる. Big-Bangモードは,外部からバッファー・パラレル端子間の変換タイミング信号を 与えなくても,内部で定期的に更新してくれるモード.
FTDI社のページ http://www.ftdichip.com/Drivers/D2XX.htm から,各種プラットフォーム用のドライバ/サンプルプログラムが入手可能. ここからダウンロードしたうちの,Bit Modeのサンプルが, お手軽にパラレル入出力するためのBit-Bangモードのサンプルプログラムだった.
以下はBit-BangモードをRaspberry Piから使う方法の説明。
http://www.ftdichip.com/Drivers/D2XX.htm からドライバを入手する。 Raspberry Piなので、OSはLinux、CPUはARMになる。 該当するドライバとして、以下が用意されていた。
使っているRaspberry PiのCPUを確認する必要がある。そのためにはuname -aコマンドを使う。
$ uname -a Linux raspberrypi 5.15.61+ #1579 Fri Aug 26 11:08:59 BST 2022 armv6l GNU/Linux
この結果、初代Raspberry PiはARMv6だとわかる。なので、それ用のドライバ
https://ftdichip.com/wp-content/uploads/2022/07/libftd2xx-arm-v6-hf-1.4.27.tgz
をダウンロードする。(Raspberry Pi 4ならばARMv7)。
適当なディレクトリを作って、curlコマンドでダウンロードして、展開する。
$ curl -O https://ftdichip.com/wp-content/uploads/2022/07/libftd2xx-arm-v6-hf-1.4.27.tgz $ tar xvfz libftd2xx-arm-v6-hf-1.4.27.tgz
するとreleaseというディレクトリができて、その中に展開される。そこでreleaseディレクトリに移動する。 この中のRead Me.txtファイルにこの先の手順が書いてある。それに従う。実際に行った手順は、以下。
$ cd build $ sudo cp libftd2xx.* /usr/local/lib $ sudo chmod 0755 /usr/local/lib/libftd2xx.so.1.4.27 $ sudo ln -sf /usr/local/lib/libftd2xx.so.1.4.27 /usr/local/lib/libftd2xx.so $ cd .. $ sudo cp ftd2xx.h /usr/local/include/ $ sudo ldconfig -v
examplesのディレクトリの中に,サンプルプログラムが多数ある. examplesの中でmakeすると全部コンパイルされる.それぞれのディレクトリで個別にmakeしても良い.
たとえば,Bit Modeのプログラムは,1バイトの値を出力して,読み込むサンプル. main.cがソースコード。
がコンパイルした結果で、実行できる。0xAAが出力されて読み込まれる。ので、パラレルピンが互い違いになる。 dynamicとstaticの違いは、ライブラリを動的にリンクする、静的にリンクするの違いだと思う。 別の環境に持って行って動かすことを考えると、その時にライブラリの引っ越しを忘れるかもしれない。 なので静的にリンクするほうが良いかと思う。
ブレッドボードの上に,LEDとスイッチを作って,接続した.
このスイッチを押すとLEDが光るCプログラムを,bitmodeのサンプルを元に作ってみた. LEDはData 0 (LSBのビット) に,スイッチはData 4に接続している. コンパイルの方法は冒頭のコメントの書かれている通り。 (Pukiwikiの関係で、逆スラッシュ¥が疑問符?に化けているので注意)
/* To build use the following gcc statement (assuming you have the d2xx library in the /usr/local/lib directory). gcc -o switchled main.c -L. -lftd2xx -Wl,-rpath /usr/local/lib */ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include "../ftd2xx.h" #include <unistd.h> //for usleep() // Globals FT_HANDLE ftHandle = NULL; void ft245close() { if(ftHandle != NULL) { FT_Close(ftHandle); ftHandle = NULL; printf("Closed device?n"); } } void ft245quit()//コントロールCで止められた時の修了処理 { ft245close(); exit(1); } void ft245open() { FT_STATUS ftStatus; ftStatus = FT_Open(0, &ftHandle);//接続されている0番目のデバイスを設定. if(ftStatus != FT_OK) { printf("FT_Open(0) failed = %d?n", ftStatus); exit(1); } FT_SetBaudRate(ftHandle, 9600);//読み書きする周期を指定 //上位4ビットを入力に,下位4ビットを出力に,Bit-Bangモードに設定 ftStatus = FT_SetBitMode(ftHandle, 0x0F/*1=out 0=in*/, 1/*bit-bang mode on*/); if(ftStatus != FT_OK) { printf("Failed to set bit mode?n"); exit(1); } } void ft245write( unsigned char data)//1バイト出力 { DWORD dwBytesInQueue = 0; FT_Write(ftHandle, &data, 1, &dwBytesInQueue); } unsigned char ft245get()//1バイト入力 { FT_STATUS ftStatus; unsigned char ucdata; ftStatus = FT_GetBitMode(ftHandle, &ucdata); if(ftStatus != FT_OK) { printf("Failed to get bit mode?n"); exit(1); } else return(ucdata); } int main(void) { unsigned char data; signal(SIGINT, ft245quit); // trap ctrl-c call quit fn ft245open(); for(;;) { data=ft245get(); printf("data=0x%X?n", data); if ( (data & 0x10) ==0 ) ft245write(0x01); else ft245write(0x00); usleep(100000L); } ft245close(); return 0; }
上記のbitmode-staticなどを起動すると、開けないというエラーが出ることがあった。
$ ./bitmode-static FT_Open(0) failed (error 3). Use lsmod to check if ftdi_sio (and usbserial) are present. If so, unload them using rmmod, as they conflict with ftd2xx.
OSがシリアルとしてこのUSBポートを使っているためらしい。 FTDI社のUSBシリアル変換チップが標準でサポートされていることが裏目に出ている様子。 そこで、指示に従い、
$ lsmod | grep ftdi_sio ftdi_sio 45056 0 usbserial 36864 1 ftdi_sio
で確認して、
$ sudo rmmod ftdi_sio
でunloadした。でもネットの情報を見ると、今は、modprobe -rコマンドを使うのがおすすめらしい。
$ sudo modprobe -r ftdi_sio
ただ、再起動するとUSBシリアルポートが復活する。FTDI社のUSBシリアルが標準で使えるのは嬉しいけど、毎回、モジュールを外す作業は面倒だし、忘れたら困る。 ということで、正しい方法かどうかわからないけど、ftdi_sioが読み込まれないように名前を変えた。
$ cd /lib/modules/`uname -r`/kernel/drivers/usb/serial $ sudo mv ftdi_sio.ko.xz ftdi_sio.ko.xz.bak
これで起動時にftdi_sioが読み込まれなくなった。
C言語だとネットワーク関連のプログラムが面倒なのでモダンな言語から使えるようにしたい。そこでPythonから使えるようにする。
上記で紹介したドライバがインストールされていることが前提である。
https://github.com/snmishra/ftd2xx からPython用のラッパーをダウンロードする。 ダウンロードすると ftd2xx-master/setup.py があるのでインストールする。
python setup.py build python setup.py install
というコマンドでインストールできるはずだけど、ここで、
$ python setup.py build Traceback (most recent call last): File "/home/pi/python/ftd2xx-master/setup.py", line 5, in <module> from distutils.command.build_py import build_py ModuleNotFoundError: No module named 'distutils.command'
というようなエラーが続出する。そのため、色々と再インストールした。
$ sudo apt install --reinstall python3-distutils $ sudo apt install --reinstall python3-setuptools $ sudo apt install --reinstall python3-git
そのあと、以下を実行。installの方はsudoが必要みたいだった。
python setup.py build sudo python setup.py install
少しエラーが出てるけど、気にしないで進めた。 プログラムの中で import ftd2xx とすれば使えた!
上のC言語のサンプルと同様な動作をするプログラムをPythonで作った。 サンプルは無さそうなので、 ftd2xx-git1/ftd2xx/ftd2xx.py などを見ながら自力で頑張る。 結果は以下。
#!/usr/bin/python # -*- coding:utf-8 -*- #DB4が接地されたらDB0に取り付けたLEDを点灯させるプログラム import ftd2xx import time def ft245open(): ftHandle = ftd2xx.open(0) ftd2xx.FTD2XX.setBaudRate(ftHandle, 9600) ftd2xx.FTD2XX.setBitMode(ftHandle, 0x0F, 1) return ftHandle def ft245write(ftHandle, data): #dataは1バイト配列で受け取る、例:b'\xFF' ftd2xx.FTD2XX.write(ftHandle, data) def ft245read(ftHandle): ftd2xx.FTD2XX.purge(ftHandle) return ftd2xx.FTD2XX.read(ftHandle,1) def ft245close(ftHandle): if ftHandle: ft245write(ftHandle, b'\x00') ftd2xx.FTD2XX.close(ftHandle) ftHandle = None print('Closed device') ftHandle = ft245open() #control-cを押したら終了 try: while 1: input=ft245read(ftHandle) print(input) if(input[0] & 0x10)==0: ft245write(ftHandle, b'\x01') else: ft245write(ftHandle, b'\x00') time.sleep(1) except KeyboardInterrupt: ft245close(ftHandle)
上で示したブレッドボードの設定で、
プログラム。 MQTTブローカはlocalhostで動いているという設定。ユーザ、パスワードの設定は無し。PythonでMQTTするプログラムに関しては、こちらを参照のこと。
#!/usr/bin/python import ftd2xx import time from paho.mqtt import client as mqtt_client ########### FT245 part ########### def ft245open(): ftHandle = ftd2xx.open(0) ftd2xx.FTD2XX.setBaudRate(ftHandle, 9600) ftd2xx.FTD2XX.setBitMode(ftHandle, 0x0F, 1) return ftHandle def ft245write(ftHandle, data): ftd2xx.FTD2XX.write(ftHandle, data) def ft245read(ftHandle): ftd2xx.FTD2XX.purge(ftHandle) return ftd2xx.FTD2XX.read(ftHandle,1) def ft245close(ftHandle): if ftHandle: ft245write(ftHandle, b'\x00') ftd2xx.FTD2XX.close(ftHandle) ftHandle = None print('\nClosed device') ########### MQTT part ########### address='localhost' port=1883 debug_topic='light/debug' sub_topic='light/setOn' pub_topic='light/getOn' client_id=f'python_856389663' #something random #username='' #passwor='' def on_connect(client, userdata, flags, rc): if rc==0: print("Connection established.") client.publish(debug_topic,"Python client connected.") else: print("Failed to connect: %d\n",rc) def on_message(client, userdata, msg): print(msg.payload) client.publish(debug_topic,"Python client on_message.") if(msg.payload == b'true'): ft245write(ftHandle, b'\x01') else: ft245write(ftHandle, b'\x00') ############ main part ############### ftHandle = ft245open() client=mqtt_client.Client(client_id) #client.username_pw_set(username,password) client.on_connect=on_connect client.connect(address,port) client.subscribe(sub_topic) client.on_message=on_message last_state=False try: while True: client.loop() input = ft245read(ftHandle) new_state=((input[0] & 0x10) == 0) if last_state != new_state: last_state=new_state if new_state: ft245write(ftHandle, b'\x01') client.publish(pub_topic,b'true') else: ft245write(ftHandle, b'\x00') client.publish(pub_topic,b'false') except KeyboardInterrupt: ft245close(ftHandle)
MQTTに対応すれば、Apple HomeKitからもアクセスできるようになる。どこからでも、iPhone, MacからLEDがon/offできるし、スイッチを押すとiPhoneの表示もon/offする。