技術者見習いのメモ書き

電子工作や神社めぐりなどの趣味の話を中心に書いていきます。 Twitter:@hakura_riku

USB機能付き格安マイコンCH552TのUSBシリアル通信プログラム解説

はじめに

前回の記事ではUSBディスクリプタの解説を主におこないました. 今回の記事ではCH552TのUSBシリアルプログラムの解説をおこなっていきます.

USB通信の構造

アプリケーションレベルのUSB通信では仮想の通信線を「パイプ」と呼び, 必要に応じてパイプの数を設定します. パイプを使った通信ではホスト(パソコンなど)からデータを転送することを「OUT」, デバイス(マイコンなど)からデータを転送することを「IN」と呼びます. アプリケーションレベルでの通信構造を図に示します.

f:id:hakura03:20190430152826p:plain
アプリケーションレベルの通信構造

(参考文献[3] より引用)

USBデバイスを接続した直後はデフォルトパイプ(エンドポイント0)でコントロール転送のみが可能な状態となっています. コントロール転送を用いてコンフィギュレーション(ディスクリプタを用いた初期設定)を行い, デバイスの使用方法を設定します. 設定後はエンドポイントが追加され, パイプが構築されます[3].

USBSerialプログラムについて

USBSerialのプログラムはUSB_Serial.cに書かれています.

github.com

分かりづらい部分

はじめに, このプログラムを最初に見たときに私が分からなかった部分を解説します.

以下に示すプログラムはKeil C51でメモリの絶対アドレスを指定するためのコードです. ここではエンドポイントという通信用のFIFOバッファのメモリを確保しています. SDCCで同じプログラムをコンパイルする場合はメモリ指定の書き方が異なるので注意してください.

UINT8X   Ep0Buffer[THIS_ENDP0_SIZE] _at_ 0x0000;
UINT8X  Ep2Buffer[2*MAX_PACKET_SIZE] _at_ 0x0008;
UINT8X  Ep1Buffer[MAX_PACKET_SIZE] _at_ 0x00a0;

以下に示すプログラムは「UsbSetupBuf」の定義です. CH554.Hで定義している型「PUSB_SETUP_REQ」を用いて, 先ほど定義したメモリ「Ep0Buffer」のデータをキャストしています. このプログラムでは「PUSB_SETUP_REQ」で指定されている名称が出てくるのでプログラムを見る際には「PUSB_SETUP_REQ」を確認しておくといいです.

#define UsbSetupBuf     ((PUSB_SETUP_REQ)Ep0Buffer)

数値の送信方法

紹介したシリアル通信プログラムではASCIIコードでの通信を前提としています. つまり, ASCIIコードに則った数字は送受信できますが, 純粋な数値を送受信することはできません. 数値を送信するためには以下に示す変換プログラムを用いる必要があります. このプログラムは数値を「i_data」に入れると, ASCIIの文字コードに変換して「str_t」に入れてくれるプログラムです. このプログラムはUSB_Serial.c内に書かれているのでそれを使用してください.

void u16str(UINT16 i_data, char *str_t)
{
    int n,i;
    if(str_t)
    {
        str_t[0] = '\0';
        n = 0;
        
        do
        {
            i = n;
            do
            {
                str_t[i+1] = str_t[i];
            } while (i--);
            str_t[0] = 0x30+(i_data % 10);
            i_data /= 10;
            n++;
        } while (i_data);
    }
}

USBの割り込み処理

CH552TをパソコンのUSBポートに接続すると, 始めに「case UIS_TOKEN_SETUP | 0:」(エンドポイント0を使ったコントロール転送)に入りデバイスの設定をおこないます. 「case UIS_TOKEN_SETUP | 0:」内では「UsbSetupBuf」に入った値に応じた初期設定をおこないます. この設定を完了すると他のエンドポイントが使用可能になります.

「case UIS_TOKEN_OUT | 2: 」,「case UIS_TOKEN_IN | 2: 」はエンドポイント2で実際の通信処理をおこなうための部分となっています.

void USBInterrupt( void ) interrupt INT_NO_USB using 1                       
{   
    UINT8 len; 
    if(UIF_TRANSFER)                                                           
   {
    switch (USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP))
    {
          case UIS_TOKEN_OUT | 2:                                                              
                        LEN = USB_RX_LEN; 
                       RecieveData();
                      //EcoSendData(RecBuf);             
                        break;
           case UIS_TOKEN_IN | 2:                                                 
                      UEP2_T_LEN = 0;                                                                            
                     UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_R_RES | UEP_R_RES_ACK;                           
                        UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_NAK;            
                        break;
            case UIS_TOKEN_SETUP | 0:                                                 
            len = USB_RX_LEN;
            if(len == (sizeof(USB_SETUP_REQ)))
            {   
       SetReqtp = UsbSetupBuf->bRequestType;
               SetupLen = UsbSetupBuf->wLengthL;
                            
               len = 0;                                                                                                                        
               SetupReq = UsbSetupBuf->bRequest;
               if(SetReqtp == 0xc0)
                           {
                                  Ep0Buffer[0] = DataBuf[num];
                                  Ep0Buffer[1] = DataBuf[num+1];
                                  len = 2;
                                  if(num<24)
                                  { 
                                    num += 2;
                                    }
                                    else
                                    {
                                        num = 24;
                                    }
                           }
                         else if(SetReqtp == 0x40)
                           {
                                len = 9;                                                        
                           }
                           else
                           { 
                                switch(SetupReq)                                                
                                {
                                     case USB_GET_DESCRIPTOR:
                                                switch(UsbSetupBuf->wValueH)
                                                {
                                                       case 1:                                              
                                                                 pDescr = DevDesc;                                
                                                                 len = sizeof(DevDesc);                                    
                                                       break;    
                                                       case 2:                                                              
                                                                 pDescr = CfgDesc;                                
                                                                 len = sizeof(CfgDesc);
                                                       break;   
                                                       default:
                                                                 len = 0xff;                                      
                                                       break;
                                                 }
                                             if ( SetupLen > len ) SetupLen = len;                  
                                             len = SetupLen >= 8 ? 8 : SetupLen;                    
                                             memcpy(Ep0Buffer,pDescr,len);                          
                                             SetupLen -= len;
                                             pDescr += len;
                                               break;                        
                                    case USB_SET_ADDRESS:
                                               SetupLen = UsbSetupBuf->wValueL;                       
                                               break;
                                    case USB_GET_CONFIGURATION:
                                             Ep0Buffer[0] = UsbConfig;
                                             if ( SetupLen >= 1 ) len = 1;
                                             break;
                                    case USB_SET_CONFIGURATION:
                                             UsbConfig = UsbSetupBuf->wValueL;
                                             break;
                                    default:
                                               len = 0xff;                                            
                                               break;    
                                   }
                            }
                      }
                        else
                        {
                                len = 0xff;                                                     
                        }

                          if(len == 0xff)
                          {
                                  SetupReq = 0xFF;
                                  UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;//STALL                    
                          }
                          else if(len <= 8)                                                   
                          {
                                  UEP0_T_LEN = len;
                                  UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;  
                          }
                          else
                          {
                                  UEP0_T_LEN = 0;                                                       
                                  UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;                       
                          }
                        break;
                 case UIS_TOKEN_IN | 0:                                                         //endpoint0 IN
                          switch(SetupReq)
                          {
                               case USB_GET_DESCRIPTOR:
                                      len = SetupLen >= 8 ? 8 : SetupLen;                               
                                            memcpy( Ep0Buffer, pDescr, len );                                 
                                            SetupLen -= len;
                                            pDescr += len;
                                            UEP0_T_LEN = len;
                                            UEP0_CTRL ^= bUEP_T_TOG;                                          
                                      break;
                               case USB_SET_ADDRESS:
                                            USB_DEV_AD = USB_DEV_AD & bUDA_GP_BIT | SetupLen;
                                            UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                                      break;
                               default:
                                      UEP0_T_LEN = 0;                                                
                                      UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                                      break;
                          }
                          break;
                 case UIS_TOKEN_OUT | 0:                                                 // endpoint0 OUT
                            len = USB_RX_LEN;
                            UEP0_T_LEN = 0;                                                    
                            UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_ACK;                                                          
                          break;
                    default:
                          break;
                }
                UIF_TRANSFER = 0;                                         
    }
    if(UIF_BUS_RST)                                             
    {
            USB_DEV_AD = 0x00;
            UIF_SUSPEND = 0;
            UIF_TRANSFER = 0;
            UIF_BUS_RST = 0;                                     
    }
      if (UIF_SUSPEND) 
        {                                                      
            UIF_SUSPEND = 0;
            if ( USB_MIS_ST & bUMS_SUSPEND ) 
            {                                                                          
                while ( XBUS_AUX & bUART0_TX );                                          
                SAFE_MOD = 0x55;
                SAFE_MOD = 0xAA;
                WAKE_CTRL = bWAK_BY_USB | bWAK_RXD0_LO;                                  
                PCON |= PD;                                                              
                SAFE_MOD = 0x55;
                SAFE_MOD = 0xAA;
                WAKE_CTRL = 0x00;
            }
    } 
      else 
      {                                                                            
          USB_INT_FG = 0x00;                                                         
      }      
}

おわりに

シリアル通信プログラムについての解説をおこないました. 次回はUSBマウスのプログラムについて紹介します.

参考文献

[1]べーた, ‘‘Cerevo TechBlog [21日目]激安中華USBマイコンは使えるのか, ” https://tech-blog.cerevo.com/archives/6068/.

[2]江苏沁恒股份有限公司, ‘‘8 位增强型USB 单片机CH552、CH551,” https://datasheet.lcsc.com/szlcsc/Jiangsu-Qin-Heng-CH552T_C111367.pdf.

[3]USB通信プログラミングテクニック, ‘‘USBの基本アーキテクチャ, ” http://www.picfun.com/usb02.html.

USB機能付き格安マイコンCH552TでUSBシリアル通信をおこなう方法

はじめに

前回までの記事でCH552Tの回路と開発環境について解説しました. 今回はCH552のUSBシリアルのプログラムを紹介します.

マイコンプログラムの最初はLチカをおこなうことが多いですが, 参考文献[1]のブログでLチカとタイマ割り込みのプログラムが紹介されているのでHello Worldのプログラムを公開したいと思います. 

 ※Lチカとタイマ割り込みのプログラムの解説記事がコメント欄などから要望があれば作成します.

プログラム

作成したサンプルプログラムを以下のリンクに置いておきます.

github.com

このプログラムは公式が配布しているCH554用シリアル通信プログラムをCH552, CH551用に改造したプログラムです. CH55Xシリーズのサンプルプログラムは互換性があり基本的に流用可能ですが, 使用するマイコンによっては実装されていない機能があるので適時修正が必要です.

もしもCH554のプログラムをCH552, CH551で利用する場合はUSBホスト機能に関するコードを削除してください.

※そのままCH554のプログラムをCH552, CH551に書き込んだ場合はブートローダーが破損するため書き込みが二度とできなくなります.

改造元のプログラムは一番初めの記事で紹介したサンプルプログラムの中にあります.

e_dragon / WCH / source / — Bitbucket

サンプルコードの使い方

サンプルコードが置いてあるgithubのリンクからサンプルプログラムをダウンロードします.

第1回の記事を参考にプロジェクトファイルを作成してください.

hakura03.hatenablog.com

作成したプロジェクトファイルの中にダウンロードしたファイルをコピーします.

f:id:hakura03:20190418222425p:plain
ダウンロードしたファイルをコピー

uVision5を起動し, 「Souse Group 1」の項目で右クリックして画像で示す「Add Existing Files to Group 'Source Group 1'」を選択します.

f:id:hakura03:20190418222711p:plain
ファイルの追加

「Add Existing Files to Group 'Source Group 1'」の画面が出てくるので, 「main.c」「USB_Serial.c」を追加します. そして, 「Public」フォルダ内にある「Debug.C」を追加します.

f:id:hakura03:20190418223550p:plain
ファイルの追加2

「main.c」をダブルクリックして開き, 「Rebuild」「Translate」を押してください. エラーが出なければコンパイルが成功しています.

f:id:hakura03:20190418224237p:plain
ビルド

f:id:hakura03:20190418224446p:plain
translate

書き込み

書き込みにはWCHISPToolが必要です. インストールしたWCHISPToolを起動し, 「8 Bit CH55X series」のタブを開きます.

f:id:hakura03:20190418225531p:plain
タブを開く

次に「Chip model」の欄からCH552を選択します.

f:id:hakura03:20190418225742p:plain
チップ選択

最後に「User File」の部分で自分が作成したプログラムのヘックスファイルを参照します. ヘックスファイルは作成したプロジェクトフォルダの「Objects」内にあります.

f:id:hakura03:20190418230024p:plain
ヘックスファイル参照

これで書き込み準備は完了です. 第2回の記事で紹介した回路を作成し書き込みをおこないます.

hakura03.hatenablog.com

USBをPCに差し込む際にCH552T書き込み回路についているボタンを押しながら差し込みます. 適切にブートローダーが起動できた場合は, WCHISPToolにCH552の表示が出ます.

f:id:hakura03:20190418230720p:plain
CH552の表示

「Download」ボタンを押すことでCH552に書き込むことができます.

プログラムの動作確認

今回のプログラムではボーレートが「57600bps」に設定されています. Arduino IDEのシリアルモニタで確認した結果を画像に示します.

f:id:hakura03:20190418231412p:plain
シリアルモニタ

USBデバイスクラスについて

サンプルプログラムを理解するために「USB_Serial.c」に記述されているUSBデバイスクラスについて解説します. 完全新規のUSBデバイスを開発する場合は本来なら, USBホスト(パソコン側など)とUSBデバイス(マイコン側など)双方のプログラムを作成する必要があります. しかしUSBデバイスクラスの規格に基づいてUSBデバイスを製作する場合はUSBホスト側のプログラムを作成する必要は基本的にありません. デバイスクラスが用いられる代表的な製品としてキーボードやマウスなどのパソコン用のデバイスがあります.デバイスクラスの表を以下に示します.

表1 USBのデバイスクラスコードについて

バイスクラス名 クラスコード(bDevice Class)
Audio 0x01
CDC(Communication Data Class) 0x02
HID(Human Interface Devices) 0x03
Image 0x06
MTP(Media Transfer Protocol) 0x06
Printer 0x07
MSC(Mass Storage Class) 0x08
USB Hub 0x09
CCID 0x0B
UVC(USB Video Class) 0x0E
PHDC 0x0F
Wireless Controller Bluetooth 0xE0

(参考文献[4] より引用)

WCHが販売するUSBシリアル変換IC「CH340」としてCH552Tを認識させるため一部のディスクリプタで異なる設定をおこないますが, 基本的にはCDCクラスと同じプログラムを作成します. CDCクラスはUSBを利用して通信をおこなうために規定された規格でシリアル通信をおこなうことができます[4].

USB・シリアル変換IC CH340G: 半導体 秋月電子通商-電子部品・ネット通販

サンプルプログラムのUSBディスクリプタについて

USBディスクリプタとはUSBの属性などを記述する部分です. ディスクリプタの設定を適切におこなうことで, USBデバイスのデバイスクラスや エンドポイントの設定などをおこなうことができます. 今回のサンプルプログラムに記述されているディスクリプタを以下に示します.

UINT8C DevDesc[18]={0x12,0x01,0x10,0x01,0xff,0x00,0x02,0x08, 0x86,0x1a,0x23,0x55,0x04,0x03,0x00,0x00,0x00,0x01};

UINT8C CfgDesc[39]={0x09,0x02,0x27,0x00,0x01,0x01,0x00,0x80,0xf0,0x09,0x04,0x00,0x00,0x03,0xff,0x01,0x02,0x00,0x07,0x05,0x82,0x02,0x20,0x00,0x00,0x07,0x05,0x02,0x02,0x20,0x00,0x00,0x07,0x05,0x81,0x03,0x08,0x00,0x01};  

バイスディスクリプタ

以下に示すデバイスディスクリプタの表を用いて「DevDesc[18]」の解説をおこないます.

表2 デバイスディスクリプタの解説とサンプルプログラムでの設定

Field Offset Size(byte) 詳細 今回の設定値
bLength 0 1 ディスクリプタの全体長。デバイスディスクリプタは 18(0x12)Byte 固定。 0x12
bDescriptorType 1 1 ディスクリプタの種別番号。DEVICE は 1。 0x01
bcdUSB 2 2 USB のバージョン(BCD)。0x100(USB 1.0?)、0x110(USB 1.1)、0x200(USB 2.0)、0x300(USB 3.0) 0x10,0x01
bDeviceClass 4 1 クラス番号 (USBクラス)。0x0 と 0xFF 以外は USB-IF が予約している。 0xff
bDeviceSubClass 5 1 サブクラス番号。0x0 と 0xFF 以外は クラス番号に依存する。 0x00
bDeviceProtocol 6 1 プロトコル番号。0x0 と 0xFF 以外は クラス、サブクラス番号に依存する。 0x02
bMaxPacketSize0 7 1 エンドポイント0の最大パケットサイズ(Byte)。速度別に規定がある。 0x08
idVendor 8 2 ベンダID(VID)。USB-IF より ID を発行(有償)してもらう必要有り。 0x86,0x1a
idProduct 10 2 プロダクトID(PID)。ベンダー各社がプロダクト毎に ID を自由に振りふれる。 0x23,0x55
bcdDevice 12 2 バイスのバージョン番号(BCD)。 0x04,0x03
iManufacturer 14 1 製造者の ストリングディスクリプタ の Index 番号。0の場合、指定無し。 0x00
iProduct 15 1 Product の ストリングディスクリプタ の Index 番号。0の場合、指定無し。 0x00
iSerialNumber 16 1 シリアルナンバーのストリングディスクリプタの Index 番号。0の場合、指定無し。複数接続を前提とするならば指定が必要。 0x00
bNumConfigurations 17 1 コンフィグレーションディスクリプタの個数。複数の Configuration より複数の Interface の方が好まれるケースが多い。 0x01

(参考文献[5] より引用)

バイスディスクリプタの4バイト目「bDeviceClass」の部分でCDCクラスを意味する「0x02」ではなく「0xff」となっています. 今回のプログラムでは「CH340」としてデバイスを認識させるためWCHが公式で出しているCH340のデバイスドライバを使用するからです. Windowsで使用する場合はWindows7以前のOSの場合別途デバイスドライバをインストールする必要があります. Windows8, Windows10ではCH340が標準サポートになっているので必要ないと考えられます[6].

wch.cn

コンフィグレーションディスクリプタ

以下に示すデバイスディスクリプタの表を用いてコンフィグレージョンディスクリプタの解説をおこないます. コンフィグレーションディスクリプタは「CfgDesc[39]」に含まれています. 「CfgDesc[39]」にはインターフェイスディスクリプタ, エンドポイントディスクリプタも含まれているので注意が必要です.

//「CfgDesc[39]」に含まれるコンフィグレーションディスクリプタ
0x09,0x02,0x27,0x00,0x01,0x01,0x00,0x80,0xf0

表3 コンフィグレージョンディスクリプタの解説とサンプルプログラムでの設定

Field Offset Size(byte) 詳細 今回の設定値
bLength 0 1 Descriptorの全体長。CONFIGURATIONは9(0x09)Byte固定 0x09
bDescriptorType 1 1 Descriptor Types。CONFIGURATIONは2。 0x02
wTotalLength 2 2 当該、および従属ディスクリプタの全長 0x27,0x00
bNumInterface 4 1 インターフェイスディスクリプタの個数 0x01
bConfigurationValue 5 1 Configuration番号。SET_CONFIGURATION時のID。 0x01
iConfiguration 6 1 ConfigurationのストリングディスクリプタのIndex番号。0の場合、指定なし 0x00
bmAttribures 7 1 bit7:予約(1), bit6:Self Power(0/1), bit5:Remote Wakeup機能(0/1), bit4-0:予約(0) 0x80
bMaxPower 8 1 必要とするバスからの電流の1/2(mA)。最大 500(=0xFA*2)mA。値の1/2を設定するのは 1 byte に収めたかったため。USB 3.0では消費電流量bMaxPowerの単位が2mAから8mAへ変更されている 0xf0

(参考文献[5] より引用)

インターフェイスディスクリプタ

//「CfgDesc[39]」に含まれるインターフェイスディスクリプタ
0x09,0x04,0x00,0x00,0x03,0xff,0x01,0x02,0x00

表4 インターフェイスディスクリプタの解説とサンプルプログラムでの設定

Field Offset Size(byte) 詳細 今回の設定値
bLength 0 1 ディスクリプタの長さ。INTERFACE は 9(0x9)Byte 固定。 0x09
bDescriptorType 1 1 ディスクリプタの種別。INTERFACE は 4。 0x04
bInterfaceNumber 2 1 インターフェイスの識別番号。複合デバイス(Composite device)の場合、異なった値が設定されなければならない。デフォルトは 0。 0x00
bAlternateSetting 3 1 代替設定を選択するための値。bInterfaceNumberが同じで bAlternateSetting が異なる別々のディスクリプタを持っている場合、ホスト側からのSET_INTERFACEコマンドで切り替えることが出来る。デフォルトは 0。 0x00
bNumEndpoints 4 1 エンドポイント数(ただし エンドポイント0 は除く) 0x03
bInterfaceClass 5 1 クラス番号(USBクラス)。bDeviceClass(デバイスディスクリプタ) の設定が 0 だった場合、この設定が参照される。0x0 は未定義、0xFF は Vendor-specific。 0xff
bInterfaceSubClass 6 1 サブクラス番号。0x0 と 0xFF 以外は クラス番号に依存する。HIDの時はブートプロトコルに対応しているかどうかのフラグとして使われる。 0x01
bInterfaceProtocol 7 1 プロトコル番号。0x0 と 0xFF 以外は クラス、サブクラス番号に依存する。HIDのときはbInterfaceSubClassが指定されている時だけ意味を持つ。 0x02
iInterface 8 1 Interface の STRING Descriptor の Index 番号。0の場合、指定無し。 0x00

(参考文献[5] より引用)

エンドポイントディスクリプタ

//「CfgDesc[39]」に含まれるエンドポイントディスクリプタ
0x07,0x05,0x82,0x02,0x20,0x00,0x00   

0x07,0x05,0x02,0x02,0x20,0x00,0x00  

0x07,0x05,0x81,0x03,0x08,0x00,0x01  

エンドポイントディスクリプタは複数あるので表5には今回の設定値を記載しません.

表5 エンドポイントディスクリプタの解説

Field Offset Size(byte) 詳細
bLength 0 1 Descriptor の全体長。ENDPOINT は 7(0x7)Byte 固定。
bDescriptorType 1 1 ディスクリプタの種別番号。ENDPOINT は 5。
bEndpointAddress 2 1 bit 7 : IN=1, OUT=0, bit 6-4 : 予約(0), bit 3-0 : エンドポイント番号
bmAttributes 3 1 bit 7-2 : 予約(0), bit 1-0 : 00=コントロール転送、01=アイソクロナス転送、10=バルク転送、11=インタラプト転送
wMaxPacketSize 4 2 最大パケット長。
bInterval 6 0 最大待ち時間。転送方式、速度によって意味合いが異なる。

(参考文献[5] より引用)

以上が今回のプログラムのディスクリプタです. CH55Xシリーズを用いてオリジナルの機器を作ることを考えた場合, 自分でディスクリプタの設定を決める必要があるのでディスクリプタについて頭の片隅に置いておくと開発がスムーズに進むと思います.

おわりに

サンプルプログラムの導入方法とディスクリプタについて解説をおこないました. 次回はサンプルプログラム「USB_Serial.c」について解説しようと考えています.

参考文献

[1]べーた, ‘‘Cerevo TechBlog [21日目]激安中華USBマイコンは使えるのか, ” https://tech-blog.cerevo.com/archives/6068/.

[2]江苏沁恒股份有限公司, ‘‘8 位增强型USB 单片机CH552、CH551,” https://datasheet.lcsc.com/szlcsc/Jiangsu-Qin-Heng-CH552T_C111367.pdf.

[3]Infinitegra, ‘‘Infinitegra TechBlog USBデバイスクラス,” http://www.infinitegra.co.jp/blog/?p=43.

[4]AVR-CDC, ‘‘ソフトウェア USB による仮想 COM ポート,”http://www.recursion.jp/prose/avrcdc/driverj.html.

[5]おなかすいたwiki, ‘USB/ディスクリプタ,”http://wiki.onakasuita.org/pukiwiki/?USB%2F%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%82%BF.

[6]ネクストステップ サポートBlog, ‘‘Arduino NANO 互換品(CH340チップ使用)のデバイスドライバー,”https://support.next-step.asia/tag/ch340-usb%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB%E5%A4%89%E6%8F%9B%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90/.

[7]Electrodragon, ‘‘Electrodragon Wiki,”https://www.electrodragon.com/w/WCH.

USB機能付き格安マイコンCH552Tの開発環境構築について

はじめに

CH552Tの開発環境構築について解説します.

CH55Xシリーズはintel8051というマイコン魔改造した物なので8051の開発環境でコンパイルしたコードならWCH社公式のISPToolでマイコンにプログラムを書き込むことができると思います.

今回はElectrodragon WikiでCH55Xシリーズのサンプルプログラムが公開されているKeilC51(uVision)を使って開発環境を構築します[1].

無料評価版Keil C51(uVision)の入手方法

無料評価版Keil C51(uVision)のインストーラはarm Keilの公式サイトからダウンロードすることができます.

※無料評価版ではオブジェクトサイズが32KBまで制限されています[3].

Keil Product Downloads

f:id:hakura03:20190330150457p:plain
Fig. 1 arm Keil公式サイト画面

C51を選択するとFig. 2の登録画面が出ます. 必要事項を記入するとインストーラをダウンロードできます.

f:id:hakura03:20190330150453p:plain
Fig. 2 登録画面

あとはインストールするだけです.

注意事項

windowsのユーザー名が日本語だとユーザーフォルダ名が日本語になるためKeil C51(uVision)のインストールに失敗します. ユーザー名が日本語の場合は英語名のユーザーアカウントを作ってインストールしてください.

Keil C51の有料版はKeil C51 COMPLER ASSEBLER KIT LICの場合241,560円でPK51 PROFESSIONAL DEVELOPER'SKIT LIC(1年間のライセンス)の場合154,430円だそうです. ※アメリカからの輸入品扱いとなるため価格は為替によって変動します.

学生ライセンスなども存在しないため個人で有料版を購入することはあまり現実的ではありません. コードサイズ上限以上のプログラムを作成する場合はSDCCを利用した方がいいと思います.

CH55Xシリーズ用のプロジェクトファイルの作り方

uVisionを起動したら赤丸で示す部分を選択してください.

f:id:hakura03:20190330153214p:plain
Fig. 3 uVison起動画面

赤丸で示す部分を選択するとプロジェクト製作画面がでるので好きな名前をつけてください.

f:id:hakura03:20190330153137p:plain
Fig. 4 プロジェクト名称決定画面

Keil C51は公式でCH55Xシリーズをサポートしているわけでは無く, intel8051系のマイコンAT89C51やAT89S51用のコンパイラを持っているだけです[1][4]. そのためプロジェクトファイルを作る際にはAT89C51もしくはAT89S51を選択してください. OKを選択するとプロジェクトファイルが作られます.

f:id:hakura03:20190330153935p:plain
Fig. 5 CPU選択画面

uVisionの標準設定ではHEXファイルを出力してくれません[1]. HEXファイルを出力するように設定を変更する必要があります. Fig. 6に示すOptions for Tergetを選択してください.

f:id:hakura03:20190330155436p:plain
Fig. 6 HEXファイル出力設定1

Options for Tergetを選択するとFig. 7に示す画面が出てきます. 赤線で示すOutputタブを開き, 赤丸で示すCreate HEX Fileのチェックボックスにチェックをつけてください.

f:id:hakura03:20190330155923p:plain
Fig. 7 HEXファイル出力設定2

HEXファイルの出力とは関係ありませんが, TargetタブのMemory Model をLarage : variables in XDATAに設定しておくと便利です.

f:id:hakura03:20190330160212p:plain
Fig. 8 メモリサイズ設定

※HEXファイルの設定とメモリサイズの設定はプロジェクトファイルごとにおこなう必要があります.

サンプルプログラムの入手先

Electrodragonが配布しているサンプルプログラムは以下のリンクにあります.

e_dragon / WCH / source / — Bitbucket

ファイルをダウンロードしたら「e_dragon-wch-ed9fe291571d\CH55x\EVT\CH554EVT\EXAM」内にあるCH554.uvproj以外を作成したプロジェクトフォルダ内にコピーしてください.

f:id:hakura03:20190330230518p:plain
Fig. 9 プロジェクトフォルダへコピー

CH554はCH55Xシリーズの一つでCH552にUSBホスト機能を追加したマイコンとなっています. そのためCH554のプログラムはUSBホスト機能などCH552に実装されていない機能のプログラムを削除もしくは使用しないことでCH552でも利用できます.

WCHISPToolの入手先

WCHISPToolはWCH社が公式で提供しているプログラムです. CH55Xシリーズなどにプログラムを書き込むために使用します.

WCHISPTool_Setup.exe - 江苏沁恒股份有限公司

WCHISPToolのSetup.exeをダウンロードしてインストールしてください.

おわりに

CH552の開発環境構築について解説をおこないました. Lチカなどの基本的なプログラムについては参考文献[1]のブログで紹介されているので, 次の記事ではCDCクラスを用いたUSBシリアル通信をCH552でおこなう方法について解説したいと考えています.

参考文献

[1]べーた, ‘‘Cerevo TechBlog [21日目]激安中華USBマイコンは使えるのか, ” https://tech-blog.cerevo.com/archives/6068/.

[2]江苏沁恒股份有限公司, ‘‘8 位增强型USB 单片机CH552、CH551,” https://datasheet.lcsc.com/szlcsc/Jiangsu-Qin-Heng-CH552T_C111367.pdf.

[3]boochow, ‘‘楽しくやろう。ARMの開発環境「MDK」と「µVision」でmbedのデバッグを試してみた,” https://blog.boochow.com/article/441231592.html

[4]Electrodragon, ‘‘Electrodragon Wiki,”https://www.electrodragon.com/w/WCH.

USB機能付き格安マイコンCH552Tの入手方法と回路について

はじめに

中国のICメーカーWCH社が出しているマイコンCH552Tについて紹介したいと思います. このマイコンはUSBペリフェラル機能がついている格安マイコンです[1]. 参考文献[1]のブログで紹介されているCH551Gと同じシリーズのマイコンです.

CH552Tについて

主なスペックは以下のとおりです.

動作電圧 プログラム用のROM クロック周波数
3.3V~5V 16KB  最大24MHz  

※ROMの2KB分はブートローダーに占有されているため, 実際に使用可能な容量は14KBになっています[2].

CH551Gとの違いはADC機能が追加されていることと, ピン数が20ピンになっていることぐらいです.

Google翻訳を使って日本語に翻訳したデータシートを以下のリンクに置いておきます. 中国語のデータシートと合わせて使用してください.

CH552T_データシート - Google ドライブ

CH552Tの入手方法

2019年3月時点では秋月電子マルツ電波では取り扱っていないため, AliExpressもしくはLCSCから購入するしかありません.

購入先 個数 価格 送料
AliExpress 1個 38円 473円
LCSC 1個 47円 約13ドル

※LCSCは大量購入すると部品単価が下がります. (10個購入すると1つ当たり約35円)

基板を設計してJLCPCB(LCSCがやっている基板屋)に部品と共に注文すると送料をまとめてくれるのでLCSCがおすすめです。

CH552Tの利点と欠点

CH552Tを利用して感じた利点と欠点について解説します.

最大の利点はその安さです. 一つ当たり40円を切るUSB機能のついたマイコンはそう無いと思います. 同じようにUSB機能を持ちHIDデバイスを作れるArduino Pro Micro(Leonardo)はスイッチサイエンスで購入すると2000円近くします. 約50分の1の価格で同じ事ができるので, USBデバイスを作りたいと考えている方には非常におすすめです. また, 書き込み装置が不要なのでArduinoと同じ感覚で書き込むことができます.

欠点は公式のIDEが無いことだと思います. KeilC51を用いて開発することができますが, 無料評価版だとコードサイズに制限があります. また, 2019年3月現在日本のCH552Tの使用者が少ないため日本語の資料が本ブログと参考文献[1]しかないです.

※コードサイズの上限は他のエディタでプログラムを書きSDCCを用いてコンパイルすることで回避することもできます[3].

※中国語圏の使用者はそれなりにいるので中国の電子工作フォーラム「电子工程世界」にGoogle翻訳を使って中国語で質問することで開発メーカWCH社のサポートを受けることができます (動かないコードの修正方法など) [4].

回路について

CH552TとUSBを繋ぐ回路を以下に示します.

f:id:hakura03:20190330102307p:plain
Fig. 1 CH552T回路

20ピン(3.3V)とUSBのD+をスイッチで繋いでいるのはブートローダーを起動するためです. パソコンのUSBポートに20ピンとD+を1~2秒ほどショートさせながらつなぐことでブートローダーを起動できます[1].

また, 20ピンのデカップリングコンデンサはVCCが3.6V未満の場合は必要ないそうです[2].

中国語データシートの用語解説

CH552Tの公式データシートは参考文献[2]の中国語版しかありません(有志が英語に翻訳したもはあります). そこで, CH552Tのデータシートを読む際に役立った頻出の中国語の解説をおこないます.

中国語 日本語
ビット
字节 バイト
数据 データ
端口 ポート
串口 シリアルポート
设备 バイス
控制 制御
寄存器 レジスタ
地址 アドレス
模式 モード
波特率 ボーレート
端点 エンドポイント
中断 割り込み
标志 フラグ
复位 リセット

表の単語と中国語の漢字が日本や台湾で使われている漢字の簡略版であることを念頭においてデータシートを見ると書いてあることが何となく分かります ( 関→关, 電→电 など ).

おわりに

CH552Tの紹介と回路についての解説を簡単におこないました. 次回の記事ではCH552Tの開発環境構築について解説をおこないます.

参考文献

[1]べーた, ‘‘Cerevo TechBlog [21日目]激安中華USBマイコンは使えるのか, ” https://tech-blog.cerevo.com/archives/6068/.

[2]江苏沁恒股份有限公司, ‘‘8 位增强型USB 单片机CH552、CH551,” https://datasheet.lcsc.com/szlcsc/Jiangsu-Qin-Heng-CH552T_C111367.pdf.

[3]Blinkinlabs, ’’GitHub CH554 software development kit for SDCC,” https://github.com/Blinkinlabs/ch554_sdcc.

[4]电子工程世界, http://www.eeworld.com.cn/.