USBホストになれるCH559開発ボードを購入

概要

USBホストになれて、安価なICであるCH559の開発ボードが販売されていたので購入してみました。USBキーボードやマウスを接続して、値をかんたんに取得することができます。UARTなどを使ってESP32などに通信も可能です。

CH559とは?

8ビットの組み込み向けのMCUみたいです。M5StickCのUSBシリアルに使われているCH552と同じシリーズになります。実はCH552はUSBシリアル変換用のICではなく、USBデバイスにもなれるMCUです。つまり、USBシリアル専用のチップではなく、パソコンからみたらUSBシリアルとみえ、M5StickCのシリアルとUARTに変換をするようなファームウエアを転送して動かしています。

CH559はさらにUSBホストとしても動きます。USBホストは他のUSBデバイスを動かす側になります。USBマウスやキーボードを接続して、入力を取得することもできます。そしてMCUなので、PWMやGPIOも搭載しています。

購入した開発ボード

私が購入したのは上記のボードで1500円ぐらいでした。

もう少し小型のものですが、5枚で4000円のものもありました。こちらは回路図がついているのでわかりやすいですね。

チップ自体は10枚で1500円ぐらいが相場だと思います。結構安いですよね。

公式資料

資料は中国語になります。そのため英語版のページからだとダウンロードできるものが少ないです。中国語版からダウンロードするのをおすすめします。

WCHISPTool_Setup.exe

転送ツールです。必ず使うのでダウンロードしてください。

CH559DS1.PDF

データシートです。中国語なので、実際のところ読めません、、、

上記に英語に訳してくれているものもあります。中国語と英語のどっちが読みやすいかは微妙なところです、、、

CH559EVT.ZIP

サンプルプログラム集です。これが重要ですが、中国語でコメントが入っているので英語ページからはダウンロードできません。データシートも英語ページにはなかったですね。

非公式資料

上記にWindowsでコンパイルできる環境こみで、USBキーボード・マウスを利用できて、UARTで通信をするセットがあります。

まずはこちらを動かすのをおすすめします。公式のサンプル集はデバッガがないとたぶん動きがよくわからないと思います。

ビルド方法

Windows環境のみの解説です。

GitHubから環境ダウンロード

先程のGitHubからZIPファイルをダウンロードして解凍します。

ビルド

  • compile.bat

を実行します。リポジトリにビルド済みのファイルがあるので、実際には実行しなくても大丈夫なんですが、とりあえずビルドしてみます。

転送ツール起動

  • WCHISPTool_Setup.exe

上記のツールをセットアップして、WCHISPToolを実行します。

一番上にタブがあって、「8 Bit CH55X series」に変更してからChipをCH559に変更します。

ダウンロードモードで接続する

この開発ボードにはUSBが3口あります。上がUSB HOST用の口で、写真ではUSBマウスを接続しています。真ん中にあるマイクロUSBと下にあるType-Aは実は同じ配線です。USBホストとして利用する場合には、下のType-Aに接続。USBデバイスとしてパソコンと接続するときには真ん中のマイクロUSBで接続します。

とはいえ、このボードにはなぜなUSB Type-A to Type-Aケーブルが付属しています。このケーブルを利用することで、下のType-Aからパソコンに接続することができます。マイクロUSBからもOTAケーブルを使えばUSBマウスなどが接続できます。

重要なことは、マイクロUSBと下のType-Aに同時に接続しないでください。マイクロUSBをパソコンに接続して、下のType-AにUSBマウスを接続した場合、パソコンにUSBマウスが認識されます。導通しているのでそんな動きになります。

安全のためにはマイクロUSB端子を利用するのはやめて、Type-A to Aケーブルを使った方が同時に接続するリスクがなくなると思います。

さて、ダウンロードモードでの接続方法ですが、ボードの右上にボタンが2つあります。上のボタンがダウンロードボタンで、下がリセットボタンです。ダウンロードボタンを押しながらパソコンに接続することで、ダウンロードモードになります。

ダウンロードモードで接続すると、上記のようにDeviceListに認識されると思います。

プログラムの転送

User Fileで先程ビルドしたCH559USB.hexファイルを選択して、Downloadボタンを押すとプログラムの転送がはじまります。

動作確認

パソコンにUSBシリアルを接続して、CH559のUARTから受信してみました。CH559の上の方にUSBマウスを接続して動かすと通信が発生しています。しかし文字化けがはげしくて正しく受信できません。確認したことろ文字列ではなく、数値データで転送をしているため受信側もプログラムを動かす必要があるみたいです。

GitHubのファイルを確認するとArduino用の受信スケッチも同梱されていました。

信号レベル確認

念の為、信号レベルを確認してみます。CH559のTXから出力は3.44Vぐらいでした。これぐらいならESP32に直接接続しても大丈夫ですね。

M5StickCへの接続

CH559M5StickC
5V5V→
RX26
TX36
GGND

こんな感じの配線になりました。CH559側はパソコンとのUSBを外して、USB2個とも利用できるようにしたほうがよいと思います。

M5StickCで受信用スケッチを動かす

#include "M5StickC.h"

uint8_t  uartRxBuff[1024];
int  rxPos = 0;
int  cmdLength = 0;
uint8_t  cmdType = 0;
long lastRxReceive = 0;

String deviceType[] = {"UNKNOWN", "POINTER", "MOUSE", "RESERVED", "JOYSTICK", "GAMEPAD", "KEYBOARD", "KEYPAD", "MULTI_AXIS", "SYSTEM"};
String keyboardstring;
void setup(void) {
  M5.begin();
  Serial1.begin(1000000, SERIAL_8N1, 36, 26);
  delay(1000);
  Serial.println("OK There");
}

void loop() {
  while (Serial1.available()) {
    lastRxReceive = millis();
    //Serial.print("h0x");//Only for Debug
    //Serial.print(Serial1.peek(),HEX);//Only for Debug
    //Serial.print(" ");//Only for Debug
    uartRxBuff[rxPos] = Serial1.read();
    if (rxPos == 0 && uartRxBuff[rxPos] == 0xFE) {
      cmdType = 1;
    } else if (rxPos == 1 && cmdType == 1) {
      cmdLength = uartRxBuff[rxPos];
    } else if (rxPos == 2 && cmdType == 1) {
      cmdLength += (uartRxBuff[rxPos] << 8);
      //printf( "Length: %i\n", cmdLength);//Only for Debug
    } else if (cmdType == 0 && uartRxBuff[rxPos] == '\n') {
      printf("No COMMAND Received\n");
      for (uint8_t i = 0; i < rxPos; i ++ ) {
        printf( "0x%02X ", uartRxBuff[i]);
      }
      printf("\n");
      rxPos = 0;
      cmdType = 0;
    }
    if ((rxPos > 0 && rxPos == cmdLength + 11 && cmdType) || rxPos > 1024) {
      filterCommand(cmdLength, uartRxBuff);
      for (int i = 0; i < rxPos; i ++ ) {
        //printf( "0x%02X ", uartRxBuff[i]);//Only for Debug
      }
      //printf("\n");//Only for Debug
      rxPos = 0;
      cmdType = 0;
    } else {
      rxPos++;
    }
  }
  rxPos = 0;

  if (Serial.available()) {
    Serial1.write(Serial.read());
  }
}

#define MSG_TYPE_CONNECTED      0x01
#define MSG_TYPE_DISCONNECTED   0x02
#define MSG_TYPE_ERROR          0x03
#define MSG_TYPE_DEVICE_POLL    0x04
#define MSG_TYPE_DEVICE_STRING  0x05
#define MSG_TYPE_DEVICE_INFO    0x06
#define MSG_TYPE_HID_INFO       0x07
#define MSG_TYPE_STARTUP        0x08

void filterCommand(int buffLength, unsigned char *msgbuffer) {
  int cmdLength = buffLength;
  unsigned char msgType = msgbuffer[3];
  unsigned char devType = msgbuffer[4];
  unsigned char device = msgbuffer[5];
  unsigned char endpoint = msgbuffer[6];
  unsigned char idVendorL = msgbuffer[7];
  unsigned char idVendorH = msgbuffer[8];
  unsigned char idProductL = msgbuffer[9];
  unsigned char idProductH = msgbuffer[10];
  switch (msgType) {
    case MSG_TYPE_CONNECTED:
      Serial.print("Device Connected on port");
      Serial.println(device);
      break;
    case MSG_TYPE_DISCONNECTED:
      Serial.print("Device Disconnected on port");
      Serial.println(device);
      break;
    case MSG_TYPE_ERROR:
      Serial.print("Device Error ");
      Serial.print(device);
      Serial.print(" on port ");
      Serial.println(devType);
      break;
    case MSG_TYPE_DEVICE_POLL:
      Serial.print("Device HID Data from port: ");
      Serial.print(device);
      Serial.print(" , Length: ");
      Serial.print(cmdLength);
      Serial.print(" , Type: ");
      Serial.print (deviceType[devType]);
      Serial.print(" , ID: ");
      for (int j = 0; j < 4; j++) {
        Serial.print("0x");
        Serial.print(msgbuffer[j + 7], HEX);
        Serial.print(" ");
      }
      Serial.print(" ,  ");
      for (int j = 0; j < cmdLength; j++) {
        Serial.print("0x");
        Serial.print(msgbuffer[j + 11], HEX);
        Serial.print(" ");
      }
      Serial.println();
      break;
    case MSG_TYPE_DEVICE_STRING:
      Serial.print("Device String port ");
      Serial.print(devType);
      Serial.print(" Name: ");
      for (int j = 0; j < cmdLength; j++)
        Serial.write(msgbuffer[j + 11]);
      Serial.println();
      break;
    case MSG_TYPE_DEVICE_INFO:
      Serial.print("Device info from port");
      Serial.print(device);
      Serial.print(", Descriptor: ");
      for (int j = 0; j < cmdLength; j++) {
        Serial.print("0x");
        Serial.print(msgbuffer[j + 11], HEX);
        Serial.print(" ");
      }
      Serial.println();
      break;
    case MSG_TYPE_HID_INFO:
      Serial.print("HID info from port");
      Serial.print(device);
      Serial.print(", Descriptor: ");
      for (int j = 0; j < cmdLength; j++) {
        Serial.print("0x");
        Serial.print(msgbuffer[j + 11], HEX);
        Serial.print(" ");
      }
      Serial.println();
      break;
    case MSG_TYPE_STARTUP:
      Serial.println("USB host ready");
      break;
  }
}

もともと入っているスケッチほぼそのままです。M5StickCの初期化と、UARTのピン番号等を書き換えています。

実行結果

Device HID Data from port: 0 , Length: 4 , Type: MOUSE , ID: 0x5E 0x4 0xFE 0x4  ,  0x0 0x4 0x2 0x0 
Device HID Data from port: 0 , Length: 4 , Type: MOUSE , ID: 0x5E 0x4 0x39 0x0  ,  0x0 0x1 0x0 0x0 
Device HID Data from port: 0 , Length: 4 , Type: MOUSE , ID: 0x5E 0x4 0x39 0x0  ,  0x0 0xFF 0x0 0x0 
Device HID Data from port: 0 , Length: 4 , Type: MOUSE , ID: 0x5E 0x4 0x39 0x0  ,  0x0 0xFF 0x0 0x0 

こちらはUSBマウスを接続した場合の出力です。

Device HID Data from port: 1 , Length: 8 , Type: KEYBOARD , ID: 0x3C 0x41 0x13 0x21  ,  0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 
Device HID Data from port: 1 , Length: 8 , Type: KEYBOARD , ID: 0x3C 0x41 0x13 0x21  ,  0x0 0x0 0x21 0x0 0x0 0x0 0x0 0x0 
Device HID Data from port: 1 , Length: 8 , Type: KEYBOARD , ID: 0x3C 0x41 0x13 0x21  ,  0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 
Device HID Data from port: 1 , Length: 8 , Type: KEYBOARD , ID: 0x3C 0x41 0x13 0x21  ,  0x0 0x0 0x21 0x0 0x0 0x0 0x0 0x0 

こちらはUSBキーボードです。両方ちゃんと取れていますね。ただし、どのキーを押したらどんなデータが飛んでくるのかは自分で調べて対応をする必要があります。

まとめ

実際これでどんなことができるかは、もう少し調べてから別の記事にしていきたいと思います。チップ単体は安いのですが、まだボードの形で安いものは出回っていないようです。USBホスト機能は便利なのですが、なかなかお手軽なものってないんですよね。

ちなみに、この開発ボードは電源を5Vと3.3Vに変更するジャンパがあるのですが、どんな動きをするのかがまったくわかりません。情報が少なすぎる気がするんですよね。

コメント