LPC1343 USB Virtual COMをカスタマイズ!

出来た事

1.LPC1343でUSB Virtual COMのExample pjtをモディファイしてカスタマイズ

開発Listへ戻る

前回、Example codeをそのままコンパイルしてUSBシリアル変換を実現しましたが
はっきり言って単なるUSBシリアル変換ならFT232RLを買えばいいんですよ。
そんなモンをLPC1343で作ってもまったく意味がない。(と感じてる)

USBを使ってコマンド転送(設定変更などの指示)とか、内部処理結果などのデータ取得が出来れば
おおっ!!結構やるね!みたいになります(私の周りではですが・・・)


今回、NXPのサンプルコードを眺めていたら結構簡単に出来そうな感じでした!
(ポジティブ(^^)/)


まず、Example codeをみてみます。USB-シリアル変換機能を実現するExample pjtのコードです。
見てみるといっても概略です。詳細はぜんぜんわかっていません。
インポートしたusbcdcのpjt内のsrcフォルダ内のvcomdemo.cを見ます。
(インポートの仕方などは前回のエントリ参照)

vcomdemo.c
まずmain関数

int main (void) {
/* Enable Timer32_1, IOCON, and USB blocks */
LPC_SYSCON->SYSAHBCLKCTRL |= (EN_TIMER32_1 | EN_IOCON | EN_USBREG);

USBIOClkConfig();
VCOM_Init(); // VCOM Initialization
USB_Init(); // USB Initialization
USB_Connect(TRUE); // USB Connect

while (!USB_Configuration) ; // wait until USB is configured

while (1) { // Loop forever
VCOM_Serial2Usb();
VCOM_CheckSerialState();
VCOM_Usb2Serial();
} // end while
} // end main ()

大事なところは青字のところです。
まぁ記載を見ると大体意味がわかるんですが。。。

1.VCOM_Serial2USB()は シリアルデータ(UART Rx受信データ)をUSBに変換してLPC1343からPCに送信します。

2.VCOM_CheckSerialState() こいつはステータスなんでしょうが、削除するとPCがLPC1343をVirtual COMとして
認識できなくなりました。盲目に入れておきましょう。

3.VCOM_Usb2Serial()はPCからUSBで送信されてきたデータをシリアルに変換してUARTのTxとして出力します。


3については、USB受信データをそのまま利用出来れば良いです。シリアルはいりません。
1はUARTの受信データではなく内部信号をUSBで送信できれば良いわけです。
つまり、1,3からシリアル部分を切り離すことが出来ればいいのです。



VCOM_Serial2USB

void VCOM_Serial2Usb(void) {
static char serBuf [USB_CDC_BUFSIZE];
int numBytesRead, numAvailByte;

ser_AvailChar (&numAvailByte); //UART RXのバッファ確認
if (numAvailByte > 0) {   // UART Rxのバッファになんか入ってる? 
if (CDC_DepInEmpty) {   // USBの送信処理終わってる?
numBytesRead = ser_Read (&serBuf[0], &numAvailByte); //UART Rxのバッファに入ってるもを移動+有効byte数も取り出す。

CDC_DepInEmpty = 0;  // USBをBusyにする
USB_WriteEP (CDC_DEP_IN, (unsigned char *)&serBuf[0], numBytesRead); //USBで有効Byte数のデータを送信する。
}
}
}

コメントの説明で大体わかるかと思いますが。
UARTの受信部分はデータを溜め込んでいます。
それを numBytesRead = ser_Read (&serBuf[0], &numAvailByte); で取り出しています。

USBの送信も時間がかかるので、USB送信中に入力されたUART Rxの信号をロストしないように
別のバッファを持っているということでしょう。

シリアル部分は要りませんので送信データと有効Byte数を独自で作成できれば良いようです。
(上記関数内でのserBuf とnumBytesReadをカスタマイズする)


VCOM_Usb2Serial

void VCOM_Usb2Serial(void) {
static char serBuf [32];
int numBytesToRead, numBytesRead, numAvailByte;

CDC_OutBufAvailChar (&numAvailByte); //USBから受けたデータのバッファ確認
if (numAvailByte > 0) {  //USBから有効なデータを受けている?
numBytesToRead = numAvailByte > 32 ? 32 : numAvailByte;
numBytesRead = CDC_RdOutBuf (&serBuf[0], &numBytesToRead); //バッファの内容移動 + 有効byte数取り出し
ser_Write (&serBuf[0], &numBytesRead);  //Uartで送信する。
}
}

似たような感じです。
USBで受信したデータを一度バッファに溜め込んで
別のバッファに移し、移したデータをUARTで送信しています。

こちらもUARTで送信中にUSBに新たなデータが入力された場合にデータロストしないように
バッファがあります。

ココでは結構簡単で、ser_Writeがまったく不要です。

numBytesRead = CDC_RdOutBuf (&serBuf[0], &numBytesToRead);後の
serBufが読み込んだ値、numByteReadが有効byte数ですから取り出せればOKです。


モディファイ内容

キーボードで's'と入力したら40桁の数字を帰ってくるようにする。
USB Virtual COM経由
Teratermで確認

まず VCOM_Usb2Serialです。

char VCOM_Usb2Serial(void) {
static char serBuf [32];
int numBytesToRead, numBytesRead, numAvailByte;

CDC_OutBufAvailChar (&numAvailByte);
if (numAvailByte > 0) {
numBytesToRead = numAvailByte > 32 ? 32 : numAvailByte;
numBytesRead = CDC_RdOutBuf (&serBuf[0], &numBytesToRead);
// ser_Write (&serBuf[0], &numBytesRead);
return(serBuf[0]); // 1文字だけ返す
}
}

キーボードの入力は人力なので非常に遅いです。
バッファに2byteもたまることは無いでしょう。

今回は1文字 1byteのみ検出するようにしました。
シリアルでの送信関数を削除し、関数の結果として1文字だけ返すようにしました。


次にVCOM_Serial2Usbです。

void VCOM_Serial2Usb(char in_char) {
// static char serBuf [USB_CDC_BUFSIZE];
// int numBytesRead, numAvailByte;
char test[USB_CDC_BUFSIZE]="1234567890123456789012345678901234567890"; //送信データ作成
// ser_AvailChar (&numAvailByte);
// if (numAvailByte > 0) { // serial buffer has some data.
if(in_char=='s'){ // USBで's'が入力されたら
if (CDC_DepInEmpty) { // 1 is empty so if empty
// numBytesRead = ser_Read (&serBuf[0], &numAvailByte);
CDC_DepInEmpty = 0;
// USB_WriteEP (CDC_DEP_IN, (unsigned char *)&serBuf[0], numBytesRead);
USB_WriteEP (CDC_DEP_IN, (unsigned char *)&test[0], 40); //作成した40桁の数値を送信
}
}
}

シリアル部分は要りませんので削除
USBで's'が入力されたら、独自で作成した40桁の数値をUSBで送信。
(なので、1文字 char 入力としています)

最後にmain関数です。

int main (void) {
/* Enable Timer32_1, IOCON, and USB blocks */
char test='0';

LPC_SYSCON->SYSAHBCLKCTRL |= (EN_TIMER32_1 | EN_IOCON | EN_USBREG);

USBIOClkConfig();

VCOM_Init(); // VCOM Initialization

USB_Init(); // USB Initialization
USB_Connect(TRUE); // USB Connect

while (!USB_Configuration) ; // wait until USB is configured

while (1) { // Loop forever
VCOM_Serial2Usb(test); // read serial port and initiate USB event
VCOM_CheckSerialState();
test=VCOM_Usb2Serial();
}
}

新しく関数間でcharの受け渡しがあるのでtestという変数で受け渡しを実現しています。


この変更後、Teratermでsを入力するとちゃんと期待した40桁の数値が表示されました。

ちなみにBaudrateの設定は関係ありません。
USB部分にBaudrateの概念が無いからです。

また以下にコピペすれば今回の機能が実現するコードを置いておきます。
vcomdemo.c全部をコピペで入れ替え

#include "LPC13xx.h"
#include "type.h"

#include "usb.h"
#include "usbcfg.h"
#include "usbhw.h"
#include "usbcore.h"
#include "cdc.h"
#include "cdcuser.h"
#include "serial.h"
#include "vcomdemo.h"
#include

#define EN_TIMER32_1 (1<<10)
#define EN_IOCON (1<<16)
#define EN_USBREG (1<<14)

/*----------------------------------------------------------------------------
Initializes the VCOM port.
Call this function before using VCOM_putchar or VCOM_getchar
*---------------------------------------------------------------------------*/
void VCOM_Init(void) {

CDC_Init ();
}


/*----------------------------------------------------------------------------
Reads character from serial port buffer and writes to USB buffer
*---------------------------------------------------------------------------*/
void VCOM_Serial2Usb(char in_char) {
// static char serBuf [USB_CDC_BUFSIZE];
// int numBytesRead, numAvailByte;
char test[USB_CDC_BUFSIZE]="1234567890123456789012345678901234567890";
// ser_AvailChar (&numAvailByte);
// if (numAvailByte > 0) { // serial buffer has some data.
if(in_char=='s'){
if (CDC_DepInEmpty) { // 1 is empty so if empty
// numBytesRead = ser_Read (&serBuf[0], &numAvailByte);
CDC_DepInEmpty = 0;
// USB_WriteEP (CDC_DEP_IN, (unsigned char *)&serBuf[0], numBytesRead);
USB_WriteEP (CDC_DEP_IN, (unsigned char *)&test[0], 40);
}
}
}

/*----------------------------------------------------------------------------
Reads character from USB buffer and writes to serial port buffer
*---------------------------------------------------------------------------*/
char VCOM_Usb2Serial(void) {
static char serBuf [32];
int numBytesToRead, numBytesRead, numAvailByte;

CDC_OutBufAvailChar (&numAvailByte);
if (numAvailByte > 0) {
numBytesToRead = numAvailByte > 32 ? 32 : numAvailByte;
numBytesRead = CDC_RdOutBuf (&serBuf[0], &numBytesToRead);
// ser_Write (&serBuf[0], &numBytesRead);
return(serBuf[0]);
}
}

/*----------------------------------------------------------------------------
checks the serial state and initiates notification
*---------------------------------------------------------------------------*/
void VCOM_CheckSerialState (void) {
unsigned short temp;
static unsigned short serialState;

temp = CDC_GetSerialState();
if (serialState != temp) {
serialState = temp;
CDC_NotificationIn(); // send SERIAL_STATE notification
}
}

/*----------------------------------------------------------------------------
Main Program
*---------------------------------------------------------------------------*/
int main (void) {
/* Enable Timer32_1, IOCON, and USB blocks */
char test='0';

LPC_SYSCON->SYSAHBCLKCTRL |= (EN_TIMER32_1 | EN_IOCON | EN_USBREG);

USBIOClkConfig();

VCOM_Init(); // VCOM Initialization

USB_Init(); // USB Initialization
USB_Connect(TRUE); // USB Connect

while (!USB_Configuration) ; // wait until USB is configured

while (1) { // Loop forever
VCOM_Serial2Usb(test); // read serial port and initiate USB event
VCOM_CheckSerialState();
test=VCOM_Usb2Serial();
} // end while
} // end main ()