ラベルにPCからの
USB受信文字列を表示
(→プロジェクトファイル(Harmony Ver.2.04版 ) ダウンロード)
PCのUSB経由で受信した文字列をラベルウィジェットに表示する例を紹介します。 本プログラムは、PIC32MZ評価ボード(→購入方法)用サンプルプログラム "USB(デバイスモード)CDCクラスPIC-PC間 文字列送受信" ( →URL)の受信文字列をグラフィック液晶INT035のラベルウィジェットに表示したものです。 2つのプログラム仕様の差違は、ラベルウィジェットへの文字列表示有無のみです。 したがって PC側のアプリケーションはまったく同じで下記となります。
|
<仕様>
・PCとPIC32MZ間でUSB接続による文字列の送受信をおこなう。
・USBのライブラリはHarmonyのCDCクラスをつかうこと
・PC側から文字列に対して PIC側からそれぞれに対応した文字列を返信すること
送受信の文字列は 以下とする
PCからの送信文字列 | PICからの返信文字列 | |
Japan\r | Tokyo | |
America\r | Washington | |
U.K.\r | London | |
France\r | Paris | |
How are you?\ | Pardon? |
・PICのキャラくタ液晶はI2Cインターフェースのキャラクタ液晶とし、以下を表示のこと。
1行目 …… PCからの受信文字列
2行目 …… PCへの送信文字列
・グラフィック液晶INT035にラベルウィジェットを表示して、文字列としてPCから受信した受信文字列を表示のこと。
表示例: 文字列 "Japan\r " を表示した場合
・ラベルウィジェットのデフォルト文字列は "My Label" とする。
・USB 接続確認用としてPCからPICへ送信する文字列は "Are you Ready ?" とする。
・PC側のアプリケーションは マイクロソフトのVC# またはVC++で作成のこと。 実行ファイルを以下からダウンロードできます。
PC開発環境 | 実行ファイル | 備考 |
VC#(Visual Studio 2017) | URL | |
VC++(Visual Studio 2017) | URL | 実行には、Visual Studio 2012, 2017 の Visual C++ 再頒布可能パッケージが必要です。 |
・PCのアプリケーションプログラムの基本動作は、以下とする。
尚、USBコネクタの脱着を検出し USB接続状態表示(接続完了/未接続 及びランプ表示の灰色/オレンジ色)に反映のこと。
<回路図>(→PDFファイル)
<外観> PIC32MZ評価ボード(→購入方法)を使った実験品の外観です。
汎用モジュール評価ボード(段積みボード)には本テーマと関係ない部品が多々実装されています。
<全体> | <USBコネクタ部拡大> |
<動作結果>(→ 動画:1080pのHD動画を見ることができます。)
"USB(デバイスモード)CDCクラスPIC-PC間 文字列送受信" と重複する部分は省略しています。
ラベルのデフォルト文字列 "My Label " を表示 |
USB接続状態確認文字列 "Are you Ready ?" を受信してラベルに表示 |
"Japan\r" を受信してラベルに表示 |
![]() |
![]() |
![]() |
"America\r" を受信してラベルに表示 |
"U.K.\r" を受信してラベルに表示 |
"France\r" を受信してラベルに表示 |
![]() |
![]() |
![]() |
"How are you ?\r" を受信して表示 |
||
![]() |
<解説>以下に、記載してある内容は要点だけです。 詳細はプロジェクトファイルを精読願います
(以下は、Harmony v2.04 をもとに作成しています。最新のバージョンとは異なることがあるかもしれませんので注意してください。)
■ MHC設定
■ Options
■Pin Settings
項目 | ポート設定(その1) |
MHC | ![]() |
備考 | I2Cポート設定 RA2: SCL2 RA3: SCLA |
■ MHC設定2 Graphics Composerの設定
ラベルウィジェットを使って 以下のような Screen1 を作成します。 作成するScreenは1つです。 作成要領については、
Graphics Composerの使い方 ( → URL )を参照願います。
ラベルウィジェットに表示する文字列のString_Assetの名前は "String001"とします。 この "String001"は、プロジェクトファイル
にある libaria_events.c ファイル内にある関数の引数の一部としてでてきます。( → URL)
■ INT035用グラフィックライブラリの追加要領
タッチパネルに関する部分は 本テーマの場合省略可能です。
INT035用グラフィックライブラリを以下の要領で組み込みます。
以下の説明ではプロジェクトフォルダは"C:\microchip\harmony\v2_04\_WK\sample204\gfx1\01
035 Led OnOff" です。
① | drv_gfx_generic.h |
![]() "C:\microchip\harmony\v2_04\_WK\sample204\gfx1 \01 035 Led OnOff\firmware\src\system_config\default\framework\gfx\driver\controller\generic\drv_gfx_generic.h" 上記パスにあるdrv_gfx_generic.hを開きファイル内容をすべて削除したあと、ys_drv_gfx_int035.h(以下参照)のファイル内容をコピーして貼り付けます。 (備考) "C:\microchip\harmony\v2_04\framework\gfx\driver\controller\generic\templates\drv_gfx_generic.h.ftl" 上記ファイルの内容をys_drv_gfx_int035.hの内容に変更しておくとMHCが自動的に所要の drv_gfx_generic.hを生成してくれます。また煩わしいプロジェクトファイル再生成時のチェックが発生しなくなります。 以下、ys_drv_gfx_int035.h |
|
② | drv_gfx_generic.c |
![]() "C:\microchip\harmony\v2_04\_WK\sample204\gfx1 \01 035 Led OnOff\firmware\src\system_config\default\framework\gfx\driver\controller\generic\drv_gfx_generic.c" 上記パスにあるdrv_gfx_generic.cを開きファイル内容をすべて削除したあと、ys_drv_gfx_int035.c(以下参照)のファイル内容をコピーして貼り付けます。 (備考) "C:\microchip\harmony\v2_04\framework\gfx\driver\controller\generic\templates\drv_gfx_generic.c.ftl" 上記ファイルの内容をys_drv_gfx_int035.cの内容に変更しておくとMHCが自動的に所要の drv_gfx_generic.hを生成してくれます。また煩わしいプロジェクトファイル再生成時のチェックが発生しなくなります。 以下、ys_drv_gfx_int035.c |
|
③ | gfx_driver_def.c |
![]() "C:\microchip\harmony\v2_04\_WK\sample204\gfx1 \01 035 Led OnOff\firmware\src\system_config\default\framework\gfx\hal\gfx_driver_def.c" 上記パスにあるgfx_driver_def.cを開きファイル内容をすべて削除したあと、ys_gfx_driver_def_int035.c(以下参照)のファイル内容をコピーして貼り付けます。 以下、ys_gfx_driver_def_int035.c |
|
④ | drv_touch_generic.h |
![]() "C:\microchip\harmony\v2_04\framework\driver\touch\generic\drv_touch_generic.h" drv_touch_generic.hの内容を削除したあと、ys_drv_touch_generic_int035.h(以下参照)のファイル内容をコピーして貼り付けます。 (備考) このファイルはMHCの内部ファイルを変更することになるので、以降MHCが自動的に所要の drv_touch_generic.hを生成してくれます。 以下、ys_drv_touch_generic_int035.h |
|
⑤ | drv_touch_generic.c |
![]() "C:\microchip\harmony\v2_04\framework\driver\touch\generic\drv_touch_generic.c" drv_touch_generic.cの内容を削除したあと、ys_drv_touch_generic_int035.c(以下参照)のファイル内容をコピーして貼り付けます。 (備考) このファイルはMHCの内部ファイルを変更することになるので、以降MHCが自動的に所要の drv_touch_generic.hを生成してくれます。 以下、ys_drv_touch_generic_int035.c |
■ I2Cインターフェースのキャラクタ液晶表示ライブラリの追加要領
キャラクタ液晶表示のライブラリ lcd_ACM1602_lib_i2c.h と lcd_ACM1602_lib_i2c.cを main.cと同じフォルダにコピーして、
プロジェクトに追加します。
(使い方 → I2Cインターフェースキャラクタ液晶表示制御 参照)
プロジェクトへの追加は以下のようにします。
■app.hに、青字部の修正、追加をします。
➀USB受信バッファーの容量を設定します。
#define APP_READ_BUFFER_SIZE 512 //USB受信データバッファ容量
➁列挙型変数 APP_STATES の定義を 既存の変数はコメントアウト後、以下のように変更し所要の変数を定義ます。
typedef enum
{
/* Application's state machine's initial state. */
APP_STATE_INIT=0,
/* Application waits for device configuration*/
APP_STATE_WAIT_FOR_CONFIGURATION,
/* Wait for a character receive */
APP_STATE_SCHEDULE_READ,
/* A character is received from host */
APP_STATE_WAIT_FOR_READ_COMPLETE,
/* Wait for the TX to get completed */
APP_STATE_SCHEDULE_WRITE,
/* Wait for the write to complete */
APP_STATE_WAIT_FOR_WRITE_COMPLETE,
/* Application Error state*/
APP_STATE_ERROR
// /* Application's state machine's initial state. */
// APP_STATE_INIT=0,
// APP_STATE_SERVICE_TASKS,
//
// /* TODO: Define states used by the application state machine. */
} APP_STATES;
③構造体 APP_DATAのメンバーを既存のメンバーをコメントアウト後、以下のように変更し、宣言します。
typedef struct
{
/* Device layer handle returned by device layer open function */
USB_DEVICE_HANDLE deviceHandle;
/* Application's current state*/
APP_STATES state;
/* Set Line Coding Data */
USB_CDC_LINE_CODING setLineCodingData;
/* Device configured state */
bool isConfigured;
/* Get Line Coding Data */
USB_CDC_LINE_CODING getLineCodingData;
/* Control Line State */
USB_CDC_CONTROL_LINE_STATE controlLineStateData;
/* Read transfer handle */
USB_DEVICE_CDC_TRANSFER_HANDLE readTransferHandle;
/* Write transfer handle */
USB_DEVICE_CDC_TRANSFER_HANDLE writeTransferHandle;
/* True if a character was read */
bool isReadComplete;
/* True if a character was written*/
bool isWriteComplete;
/* Flag determines SOF event occurrence */
bool sofEventHasOccurred;
/* Break data */
uint16_t breakData;
/* Application CDC read buffer */
uint8_t * readBuffer;
// /* The application's current state */
// APP_STATES state;
//
// /* TODO: Define any additional data used by the application. */
} APP_DATA;
以下、app.h
■app.cに、青字部の修正、追加をします。
① MHCが作成したapp.cを修正するより、マイクロチップのサンプルソフトcdc_com_port_singleのapp.c
("C:\microchip\harmony\v2_04\apps\usb\device\cdc_com_port_single\firmware\src\app.c")を修正した方が
簡単なので app.cを入れ替えます。
②インクルードファイルを追加します。stdio.hがないと、sprintf( )がコンパイラのバージョンによってコンパイルでエラーが
でることがあります。
#include "stdio.h"
④USB制御の読込バッファーのメモリ変数を 16バイト単位で KSEG1(カーネルセグメント1)領域に確保して
キャッシュ化してはいけない旨定義します。
/* Macro defines USB internal DMA Buffer criteria*/
#define APP_MAKE_BUFFER_DMA_READY __attribute__((coherent)) __attribute__((aligned(16)))
//coherent: キャッシュ化不可、指定メモリ領域…KSEG1(カーネルセグメント1)
//aligned(16):16バイト単位で要メモリ確保
uint8_t APP_MAKE_BUFFER_DMA_READY readBuffer[APP_READ_BUFFER_SIZE];
⑤プログラムで使用される変数、受信文字列 及び返信文字列などの宣言、定義を行います。
尚、uint8_t APP_MAKE_BUFFER_DMA_READY _IamReadyは、charとは C言語上かなり異なった挙動をするので
返信用文字列は同様の内容を別々に定義します。
int delay_Clock = 200000000; //システムクロック:200MHz
char Buf[32];
int ix = 0;
char* str;
char* str2;
char Ready[] = "Are you Ready ?\r";
char Japan[] = "Japan\r";
char America[] = "America\r";
char UK[] = "U.K.\r";
char France[] = "France\r";
char IamFine[] = "I am fine\r";
char IamReady[] = "I am Ready";
//返信用 "Tokyo","Washington","London","Paris","Pardon
?"には、
//char と uint8_t APP_MAKE_BUFFER_DMA_READYの2つを定義
char Tokyo[] = "Tokyo";
char Washington[] = "Washington";
char London[] = "London";
char Paris[] = "Paris";
char Pardon[] = "Pardon ?";
uint8_t APP_MAKE_BUFFER_DMA_READY _IamReady[] = "I am Ready\r\n";
uint8_t APP_MAKE_BUFFER_DMA_READY _Tokyo[] = "Tokyo";
uint8_t APP_MAKE_BUFFER_DMA_READY _Washington[] = "Washington";
uint8_t APP_MAKE_BUFFER_DMA_READY _London[] = "London";
uint8_t APP_MAKE_BUFFER_DMA_READY _Paris[] = "Paris";
uint8_t APP_MAKE_BUFFER_DMA_READY _Pardon[] = "Pardon ?";
⑥NOP()による遅延関数 delay_us( )、delay_ms( )を定義しておきます。
void delay_us(volatile unsigned int usec) //1μsec遅延
{
volatile int count;
count = (int)(delay_Clock/20000000)*usec;
do //実測 at 200MH (Clock=200000000)
{ //delay_us(1000):1000.4μsec delay_us(100):100.6μsec delay_us(10):10.5μsec delay_us(1):1.5μsec
asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP");asm("NOP");
……
……
⑦USB初期化に係る関数です。APP_USBDeviceEventHandler ( )のUSB_DEVICE_CDC_EventHandlerSet(
):の中で呼び出されています。
USB_DEVICE_CDC_EVENT_RESPONSE APP_USBDeviceCDCEventHandler
(
USB_DEVICE_CDC_INDEX index ,
USB_DEVICE_CDC_EVENT event ,
void * pData,
uintptr_t userData
)
……
……
return USB_DEVICE_CDC_EVENT_RESPONSE_NONE;
}
⑧USB初期化に係る関数です。APP_Tasks ( )のUSB_DEVICE_EventHandlerSet( ):の中で呼び出されています。
void APP_USBDeviceEventHandler ( USB_DEVICE_EVENT event, void * eventData,
uintptr_t context )
{
USB_DEVICE_EVENT_DATA_CONFIGURED *configuredEventData;
……
……
case USB_DEVICE_EVENT_ERROR:
default:
break;
}
}
⑨USB制御がリセットする場合に機能する関数です。USBコネクタを抜き差しした場合などに機能しています。
bool APP_StateReset(void) //USBコネクタを抜き差しした場合などに機能する
{
/* This function returns true if the device
* was reset */
bool retVal;
……
……
return(retVal);
}
⑩APP_Initialize( )関数の中で USBのデバイスハンドル、仮想UARTの通信条件(通信速度:9600bps、データ長:8ビット)、
USB受信バッファなど いろいろな変数を初期化しています。
/* Device Layer Handle */
appData.deviceHandle = USB_DEVICE_HANDLE_INVALID ;
/* Device configured status */
appData.isConfigured = false;
/* Initial get line coding state */
appData.getLineCodingData.dwDTERate = 9600;
appData.getLineCodingData.bParityType = 0;
appData.getLineCodingData.bParityType = 0;
appData.getLineCodingData.bDataBits = 8;
/* Set up the read buffer */
appData.readBuffer = &readBuffer[0];
⑪I2Cインターフェースのキャラクタ液晶を初期化して、 液晶に "USB device CDC Start !!"と表示しています。
lcd_ACM1602_init_i2c(); //I2Cインターフェース式液晶初期化
lcd_ACM1602_cmd_i2c(0x0C); //カーソル:0FF、ブリンク:0FF
lcd_ACM1602_cmd_i2c(0x80); //1行目の先頭へ
lcd_ACM1602_str_i2c("USB device CDC ");
delay_ms(1000);
lcd_ACM1602_cmd_i2c(0xC0); //2行目の先頭へ
lcd_ACM1602_str_i2c(" Start !! ");
delay_ms(2000);
⑫USBを初期化して、システムの構築をしています。電源投入の起動時 及びUSBコネクタ抜き差しなどが発生した場合に
このステートが機能します。 通常のPC-PIC間のデータ読み書きに係る送受信にはこのステートは関与しません。
switch(appData.state)
{
case APP_STATE_INIT:
/* Open the device layer */
appData.deviceHandle = USB_DEVICE_Open( USB_DEVICE_INDEX_0, DRV_IO_INTENT_READWRITE
);
……
……
/* If the device is configured then lets start reading */
appData.state = APP_STATE_SCHEDULE_READ;
}
break;
⑬PCからのデータを読込バッファーに読み込むステートです。system_tasks( )から呼ばれるサイクルで繰り返し呼び
出されています。
case APP_STATE_WAIT_FOR_READ_COMPLETE:
if(APP_StateReset())
{
break;
}
/* Check if a character was received or a switch was pressed.
* The isReadComplete flag gets updated in the CDC event handler. */
if(appData.isReadComplete)
……
……
appData.state = APP_STATE_SCHEDULE_WRITE;
}
break;
⑭ USBで文字列を受信後 以下の3つが実行されているステートです。
★ ラベルウィジェットへの受信文字列の表示する。
★ 読み込み文字列を判別して、PCへ文字列を返信する。
★ 受信文字列と返信文字列をキャラクタ液晶に表示する。
・ リセット信号が入った場合は 送信処理はしないでこのステートを終了するようになっています。
・ for(ix = 0; ix < (APP_READ_BUFFER_SIZE + 1); ix++)Buf_gfx[ix]
= readBuffer[ix]; でUSBの受信バッファーを
1バイトづつ 別のバッファにコピーしています。 readBufferはキャッシュ化不可で、メモリ領域も規定され制約があり また
USBモジュールの内部動作で予期せぬ値に変わっていることがあるようです。早めに別メモリにコピーしておきます。
・ MyLibariaEvents();関数で ラベルウィジェットへの書き込みを実行しています。 この関数はlibaria_event.cで定義されています。
・ 読み込みバッファーの文字列を判別して 対応する文字列を選び返信しています。
・ 受信文字列と返信文字列をキャラクタ液晶に表示しています。
case APP_STATE_SCHEDULE_WRITE:
if(APP_StateReset()) //リセットが発生した場合
{
break; //(送信処理をしないで)このステート終了
}
/* Setup the write */
appData.writeTransferHandle = USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID;
appData.isWriteComplete = false;
appData.state = APP_STATE_WAIT_FOR_WRITE_COMPLETE;
//グラフィック画面更新
for(ix = 0; ix < (APP_READ_BUFFER_SIZE + 1); ix++)Buf_gfx[ix] =
readBuffer[ix]; //★USBバッファをコピー
MyLibariaEvents(); //グラフィック関連イベント実行 ---> libaria_event.c
if(strcmp(Buf_gfx,Ready) == 0)
{
str = _IamReady;
}
else if(strcmp(Buf_gfx,Japan) == 0) //Japanの場合 → Tokyo
{
str = _Tokyo;
}
else if(strcmp(Buf_gfx,America) == 0) //Americaの場合 → Washinton
{
str = _Washington;
}
else if(strcmp(Buf_gfx,UK) == 0) // U.K.の場合 → London
{
str = _London;
}
else if(strcmp(Buf_gfx,France) == 0) //Franceの場合 → Paris
{
str = _Paris;
}
else //その他の場合 → Pardon ?
{
str = _Pardon;
}
//文字列を返信
appData.readBuffer[0] = appData.readBuffer[0] + 1;
USB_DEVICE_CDC_Write(USB_DEVICE_CDC_INDEX_0,
&appData.writeTransferHandle,
str, 12, //12バイト: 最大送信データサイズ //12バイト: _IamReady[] =
"I am Ready\r\n";
//strのサイズが12バイトより小さい場合 unknownデータが送られるので要注意
USB_DEVICE_CDC_TRANSFER_FLAGS_DATA_COMPLETE);
//I2C制御キャラクタ液晶表示
lcd_ACM1602_cmd_i2c(0x80); //1行目の先頭へ
sprintf(Buf,"%s ",Buf_gfx);//受信文字列
lcd_ACM1602_str_i2c(Buf); //液晶表示
lcd_ACM1602_cmd_i2c(0xC0); //2行目の先頭へ
sprintf(Buf,"%s ",str); //送信文字列
lcd_ACM1602_str_i2c(Buf); // 開始メッセージ1行目表示
for(ix = 0; ix < 20; ix++)readBuffer[ix] = '\0'; //バッファークリア //必須
break;
以下、app.c
■libaria_events.cに、青字部を追加をします。
➀ laString構造体文字列 str1を宣言します。 C言語文字列を 直接ラベルウィジェットに書き込むことはできません。
#include "stdio.h"
char charBuf[20];
extern char Buf_gfx[APP_READ_BUFFER_SIZE]; //
laString str1;
➁配列をC言語文字列に変換後、更にlaString文字列に変換して ラベルウィジェットに表示しています。
・ sprintfで 配列を文字列に変換しています。
・ laString laString_CreateFromCharBuffer()関数でC言語文字列をlaString構造体文字列に変換しています。
以下は、MPLAB Harmony Helpに記載されている laString_CreateFromCharBuffer()関数に係る記載です。
関数GFXU_StringFontIndexLookup()の第2引数string_String001のString001は グラフィックスコンポーザで
定義した String Assetの名前です。
laString_CreateFromCharBuffer FunctionC LIB_EXPORT laString laString_CreateFromCharBuffer( const char* chr, GFXU_FontAsset* fnt ); Parameters Parameters Description const char* chr pointer to a const char* buffer, can be NULL GFXU_FontAsset* fnt pointer to a font asset, can be NULL Returns laString - created string object Remarks Makes an internal copy of the input buffer for the string object. Caller is responsible for the allocated memory but does not need to preserve the input buffer to maintain the string buffer state. Caller must also ensure that the font contains all the glyphs for the string or rendering may not be possible. |
・ laLabelWidget_SetText()関数を使い ラベルウィジェットに文字列を書き出します。
void MyLibariaEvents(void) //ラベルウィジェット更新 //APP_Tasks ()からの呼び出し
{
sprintf(charBuf, "%s", Buf_gfx); //文字列に変換
str1 = laString_CreateFromCharBuffer(charBuf, //laStringオブジェクト作成、定義
GFXU_StringFontIndexLookup(&stringTable, string_String001, 0));
laLabelWidget_SetText(LabelWidget1, str1); //ラベルウィジェットに表示
}
以下、libaria_events.c
■system_init.cに、青字部を追加をします。
➀ SYS_Initialize ( )関数の中の 先頭に以下を記述して、 INT035が立ち上がるまでPICのプログラムが動作を
開始しないようにします。
delay_ms(350); //INT035の内部回路が立ち上がるまで待つ
<注記> デスクリプタについて
USBの内部構成を決めるデスクリプタはMHCで設定すればMHCが自動的に system_init.cの中に作成してくれます。 ソースコードレベルで
自分で作成するする部分は何もありません。 以下に於いて、デスクリプタ部分は緑色になっています。
以下、system_init.c