CCS-CによるPIC-USBのコントロール

 C18を使ってPIC-USBをコントロールする方法については、『PICで楽しむUSB機器自作のすすめ』などに詳しく解説してあります。(残念ながら、CDCが主) ですが、従来から使っているCCS-Cによる解説はとても少ないようです。さらに、マイクロチップ社のフレームワークの中の汎用USBドライバを使うPC側のソフトは VC++によるサンプル(上記の書籍でも同様) で、これも従来から使っているVB6やVB.NETなどではすぐには利用できません。
 PIC側のコンパイラはCCS-Cを使う、パソコン側はVB6またはVB.NETを使ってUSB-I/Fを動かすことを目標にテストしました。また、VB6で動かせたものがVB.NETに変換すると対応されていない関数があり(ポインタに関するもの)、これに悩まされましたが、これも何とか対応しました。
 上の書籍に紹介してある・・・PIC側はC18コンパイラ(期間限定・FREE)、PC側はVC++ ・・・でのテストは一通りいたしました。ですが、できれば結構つかえるCCS-CがあるのにC18を追加購入することに納得できませんし、PC側のソフトも従来の資産(VB6/VB.NET) を活用してそれに組み込みたいと思いました。(2009/10/20) Vi


su al Basic 6.0
USB2.0デバイスがUSB3.0ポートで動作しない場合の解決法 (2017/1/10 追加)

追加情報
2009/12/20: トランジスタ技術2010年1月号に、Windows XP(SP2), Vista, Windows 7までサポートされているUSBデバイス・ドライバの作成が不要の汎用的なドライバであるWinUSBを使った例が掲載されています。さっそくこれを使ったテスト(汎用USBドライバ使用)をいたしました。結果はPIC側のファームウェアは、[ 2 ]-(1)で紹介しているものがそのまま使えます。
紹介されているリンク先( http://www.lvr.com/winusb.htm )のPC側ソフトの例はVB2008とVC#2008です、そこのPICのファームウェアの例にCCS-Cがありませんが [ 2 ]-(1)が使えます。 PIC側をまったく変更しないで使えるのが嬉しいです。
2011/9/12: WinUSBを使用する、VC++2008/CLI によるサンプル用ソフト(I/Oモニター, 移植が容易)を作成しました。WDK(無償)をインストールしなければならないですが、VC++のWinUSBに関する記述自体はVB.NET/VC#に比べると驚くほどシンプルになっています。WDKで入手できるヘッダーファイルの貢献でしょう。

2011/10/5: さらに、同じくWinUSBを使用する、VC# によるサンプル用ソフトを作成しました。VCとは言っても、VC++とVC#は全く違っており少し戸惑いましたが、文字列の操作についてはVC#の方が取り扱いやすく対応は意外と容易でした。コードを記述するとき、VC++では "." ,"->" , "::" の区別があり、これは過去の互換性遺産の上に成り立っているのではないかと感じるところがありますが、こういったことを含めてVC#はすっきりしているなと思いました。
2011/12/5: 現在、Windows上で動かすサンプルソフトと共に提供しているUSB-I/Oシリーズを、Linux(Ubuntu 10.04 LTS)上で動かすテストをしました。インターフェイス2011年8月号でも紹介されている Libusbを使ってです。 Libusbには 0.1 と 1.0 がありますが、この両方において、問題なく製品が動作できることを確認しました(ファームウェアは[ 2 ]-(1)で紹介しているもの)。私にとりましては、1997年ころにLinuxを少し扱って以来のことですので、Linuxが格段に取り扱いやすくなっているのに驚きました。GUIについても JAVAの充実, Eclipse(開発環境) の充実などがやはり驚きです。これなら生産現場においても、無償のLinuxと開発ツール群で充分対応できるのではないかと思いました。リナックスにも多数のディストリビューションがあり、選択の自由と裏腹に導入の難しさがありましたが、Ubuntuの登場と、その日本語フォーラムなどの充実によって障害は低くなってきていると思います。さてそれで、USB-I/Oの基本的な動作確認はできましたので、今後、各製品のGUI(Java)を使ったLinuxサンプルを提供できるようにしてまいります。
 Ubuntu日本語フォーラム  ->  
https://forums.ubuntulinux.jp/index.php
2015/6/11: ユーザー様の機能追加のご要望があり、数年ぶりに「プログラマブルAC(2CH)タイマーボード(PMCT」のバージョンアップをいたしました。その際、CCS-Cを使用しているUSB通信のコマンド追加作業をいたしました。この基板では 18F2550 を使っています。パソコンとの間に汎用USBドライバ(WinUSB)を使った通信を確立して基板の各種設定をコマンドで実行しておりますが、そのソースを PMCT通販のページ にてご提供することにいたしました。


[] CDCサンプル

   CDC(Communication Device Class: RS232C-USB変換用のクラス)を使用すると、PC側は特別なドライバは必要なく(Windows側に標準で内蔵)、仮想COMポートとして動作するので、従来のRS232C使用のアプリはCOM番号を指定するだけで利用できます。(cdc_NTXPVista.inf
 (1)  PIC側ソフト
   CCS−CによるCDCのPICのソフトは、サンプルが提供されています。(ex_usb_serial2.cなど) それをテスト用に自分のハードに合わせました。LCDの制御に後閑さん紹介のソースを組み込んでいます。

#include <18F2550.h>
#include <stdlib.h> // Standard Library


//configure a 20MHz crystal to operate at 48MHz
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN

#use delay(clock=48000000)
#use rs232(baud=19200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

#define OFF 0
#define ON 1
typedef unsigned int Uint;
typedef unsigned long Ulng;
typedef signed int Sint;
typedef signed long Slng;


// Includes all USB code and interrupts, as well as the CDC API
// Can't edit file in \Program Files Holder.
#include "usb_cdc.h" //9600bps->115200


#use fixed_io(b_outputs=PIN_B1,PIN_B2,PIN_B3,PIN_B4,PIN_B5,PIN_B6,PIN_B7)
#use fast_io(C)

///// LCD Initial Setting
#define mode 0x01 // tris data
#define input_x input_B
#define output_x output_B
#define set_tris_x set_tris_B
#define stb PIN_B3
#define rs PIN_B2
#include "lcd_lib.c"

const char verStr[] = "USBtest_2_090924";

//======== USART Valuable ========
int Rnum = 0; // Receive Number
char Rbuf[22]; // Receuve Buffer
short Rcmflg = FALSE; // Command Receive Flag
#include "SciCommand.c"

//======== USART receive Interrupt Process ========
#int_rda
void isr_rcv()
{
char data;

data = getc(); // Get RCV char
if (data == 0x0d || data == 0x0A) // CRorLF?
{
Rbuf[Rnum] = 0; // String tarminator
Rcmflg = TRUE; // RCV CMD Flag On
exe_command(); // Execute RS232C Command
}
else
{
Rbuf[Rnum] = data; // To buffer
Rnum++; // Increment Index
if (Rnum > 20) Rnum = 0; // Check Upper Limit
}
}


void main() {
BYTE i, j, address, value;

// LCD Setting
lcd_init(); // Initialize LCD
lcd_clear(); // All CLS LCD
lcd_cmd(0x0C); // Cursol:OFF, Brink:Off
lcd_cmd(0x80); printf(lcd_data, "Hello! USBtest_2");

// Enable Interrupt
enable_interrupts(INT_RDA); // USART Enable
enable_interrupts(GLOBAL); // Global INT Enable

// USB Initialize
usb_cdc_init();
usb_init();

printf("Hello! USBtest_2\r\n");

while(!usb_cdc_connected()) {}

while (TRUE) {
usb_task();
if (usb_enumerated()) {
printf(usb_cdc_putc, "\r\n\nEEPROM:\r\n"); // Display contents of the first 64
for(i=0; i<=3; ++i) { // bytes of the data EEPROM in hex
for(j=0; j<=15; ++j) {
printf(usb_cdc_putc, "%2x ", read_eeprom( i*16+j ) );
}
printf(usb_cdc_putc, "\n\r");
}
printf(usb_cdc_putc, "\r\nLocation to change: ");
address = gethex_usb();
printf(usb_cdc_putc, "\r\nNew value: ");
value = gethex_usb();

write_eeprom( address, value );
}
//gethex_usb wait for 2 characters. So can't do exe_command immediatly.
//Moved to handle it in isr_rcv(interrupt).
//exe_command(); // Execute RS232C Command
}
}

 (2)  PC側ソフト
  CDCに関しては、テストのためにはハイパーターミナルやテラタームが利用できるのでとても楽です。アプリケーションもVB6などで簡単に製作できますので、ここでは省略します。

【 動作状況 】
  上の図の115200bpsの方がUSB-RS232C(CDC)による通信で、サンプルの内容としては内蔵EEPROMにアドレス(Location)とデータ(New value)をPCのキーボードから指定して書き込み再表示するという動作をしています。下の方の図は、内蔵のSCIによるものです。




[] 汎用USBドライバ使用のサンプル
 (1)  PIC側ソフト
   CCS-Cによる『MPUSBAPI.DLL』『mchpusb.sys』『mchpusb.inf』をターゲットにしたサンプルは以下の通りです。PCとのデータのやり取りはアスキー文字列で行うようにしました。PC側では、文字列を送りまた文字列で受信するようにしており、RS232Cのターミナルソフト(ハイパーターミナルなど)に似たインターフェイスのモニターソフトを制作し、色々なコマンドを送ってその応答をチェックしています。それでなくとも、アスキー文字列でのやり取りですので、TextBoxを使った簡単な送受信のソフトはすぐにできると思います。(サンプル:CmUsbByTxt.vbp)

追記 】 上記の追加情報でも紹介しましたが、このコードは、PC側のデバイスドライバをWinUSBにしてもそのまま動作します。紹介されているダウンロード元 ( http://www.lvr.com/winusb.htm )から降ろしたwinusb_vb.sln (VB2008ソルーション)を起動して走らせ、'V' などのコマンドを送ると画面に応答が表示されますので動作をすぐに確認できます。INFファイルの変更の仕方とVBソフト内のGUID記述の関連などについて良く理解する必要がありますのでトラ技の2010年1月号104~106ページをご参照ください。ダウンロード元には今のところは、CCS-Cのサンプルは無いので、このコードがそのまま使えます。(2009年12月20日)


// *************************************************************************
// UsbApiTst2.c
// *************************************************************************


#include <18F2550.h>
#include <string.h>
//configure a 20MHz crystal to operate at 48MHz
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN
#use delay(clock=48000000)
#use rs232(baud=19200, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)

#define OFF 0
#define ON 1
typedef unsigned int Uint;
typedef unsigned long Ulng;
typedef signed int Sint;
typedef signed long Slng;


// =========================================================================
// Definition for CCS-C Compiler.
// Give a value which is define at usb.h.
// =========================================================================

#define USB_HID_DEVICE FALSE // not use HID
#define USB_EP1_TX_ENABLE USB_ENABLE_BULK // Active EP1(EndPoint1) IN Bulk/Interrupt
#define USB_EP1_RX_ENABLE USB_ENABLE_BULK // Active EP1(EndPoint1) OUT Bulk/Interrupt
#define USB_EP1_TX_SIZE 64 // Buffer Tx EndPoint1(4)
#define USB_EP1_RX_SIZE 20 // Buffer Rx EndPoint1(3)

#include <pic18_usb.h> // Microchip PIC18Fxx5x , CCS PIC USB Driver
#include <PicUSB.h> // Description for configure of the USB
#include <usb.c> // Handles usb ,tokens and descriptions

#define LED0 PIN_C0
#define LED1 PIN_C1
#define LED2 PIN_C2

#define LED_ON output_low
#define LED_OFF output_high


///// LCD Initial Setting
#define mode 0x03 // tris data
#define input_x input_B
#define output_x output_B
#define set_tris_x set_tris_B
#define stb PIN_B3
#define rs PIN_B2
#include "lcd_lib.c"

char UsbRxBuf[USB_EP1_RX_SIZE]; // Receive buffer (20 bytes)
char UsbTxBuf[USB_EP1_TX_SIZE]; // Send buffer (30 bytes)

const char verStr[] = "UsbApi_Test2_091021";
char CR[] = "\r";

void writeUSB(void);

//======== USART Valuable ========
int Rnum = 0; // Receive Number
char Rbuf[22]; // Receuve Buffer
short Rcmflg = FALSE; // Command Receive Flag
#include "SciCommand.c"

//======== USART receive Interrupt Process ========
#int_rda
void isr_rcv()
{
char data;

data = getc(); // Get RCV char
if (data == 0x0d || data == 0x0A) // CRorLF?
{
Rbuf[Rnum] = 0; // String tarminator
Rcmflg = TRUE; // RCV CMD Flag On
exe_command(); // Execute RS232C Command
}
else
{
Rbuf[Rnum] = data; // To buffer
Rnum++; // Increment Index
if (Rnum > 20) Rnum = 0; // Check Upper Limit
}
}

void writeUSB(void)
{ //Attempt to create a pointer to a constant
strcat(UsbTxBuf, CR);
usb_put_packet(1,UsbTxBuf,strlen(UsbTxBuf),USB_DTS_TOGGLE);
}

void udspnsup(void) // for the command that is not supported
{
strcpy(UsbTxBuf, "?\r"); writeUSB();
}

void usndOK(void)
{
strcpy(UsbTxBuf, "OK\r"); writeUSB();
}


void main(void)
{
int i, AD[4];
char *p, c;

set_tris_b(0x03); // 0x0F -> 0x03
set_tris_c(0xF8); // add

// LCD Setting
lcd_init(); // Initialize LCD
lcd_clear(); // All CLS LCD
lcd_cmd(0x0C); // Cursol:OFF, Brink:Off
lcd_cmd(0x80); printf(lcd_data, "Hello! UsbApiTst");

setup_adc_ports(AN0_TO_AN3); // A/D
setup_adc(ADC_CLOCK_INTERNAL);

// Enable Interrupt
enable_interrupts(INT_RDA); // USART Enable
enable_interrupts(GLOBAL); // Global INT Enable

LED_ON(LED0);
LED_OFF(LED1);
LED_OFF(LED2);

printf("Hello! UsbApiTst2\r\n");

usb_init(); // Initialize of USB
usb_task(); // Prepare USB & reset USB
usb_wait_for_enumeration(); // Wait till PicUSB consists on PC

LED_OFF(LED0);
LED_ON(LED1);
LED_OFF(LED2);


while (TRUE)
{
if(usb_enumerated()) // PicUSB is configured
{
if (usb_kbhit(1)) // RX EndPoint1 has data(one packet) from PC ?
{
usb_get_packet(1, UsbRxBuf, USB_EP1_RX_SIZE); // EP1 -> Buf
p = UsbRxBuf;

switch(c = toupper(*p++)) {
case 'T': // for Test
switch(c = toupper(*p++)) {
case '0':
sprintf(UsbTxBuf, "RX: %d", UsbRxBuf[0]); writeUSB();
break;
case '1':
break;
case '2':
break;
case '3':
sprintf(UsbTxBuf, "-------TEST-------"); writeUSB();
break;
case '4':
break;
}
break;
case 'A': //Read A/D
for(i=0; i<4; i++) {
set_adc_channel(i); // CH A0
delay_us(10); // wait
AD[i] = read_adc(); // Read
}
sprintf(UsbTxBuf,"CH0~3:%u,%u,%u,%u",AD[0],AD[1],AD[2],AD[3]); writeUSB();
break;
case 'L': //Write LCD
for(i=0; i<USB_EP1_RX_SIZE;i++) {
if(UsbRxBuf[i]==13) {
UsbRxBuf[i] = '\0'; break;
}
}
lcd_cmd(0xC0); printf(lcd_data, p);
break;
case 'D': //LED On/off D21:Led2->On
c = *p++;
switch(c) {
case '0': if(*p=='0') LED_OFF(LED0); else LED_ON(LED0);
break;
case '1': if(*p=='0') LED_OFF(LED1); else LED_ON(LED1);
break;
case '2': if(*p=='0') LED_OFF(LED2); else LED_ON(LED2);
break;
default: udspnsup(); break;
}
break;
case 'S': //Input SW check
i = input_b() & 0x03;
sprintf(UsbTxBuf, "IN_B:%X", i); writeUSB();
break;
case 'V': strcpy(UsbTxBuf, verStr); writeUSB(); // Version
break;
case '?': //dspHelp(false);
strcpy(UsbTxBuf, "Tx |Test:0~4"); writeUSB(); delay_ms(10);
strcpy(UsbTxBuf, "A |Read A/D"); writeUSB(); delay_ms(10);
strcpy(UsbTxBuf, "Dnv|LED10,1off"); writeUSB(); delay_ms(10);
strcpy(UsbTxBuf, "S |Read SW"); writeUSB(); delay_ms(10);
strcpy(UsbTxBuf, "L..|LCD WR"); writeUSB(); delay_ms(10);
strcpy(UsbTxBuf, "V |Version"); writeUSB(); delay_ms(10);
strcpy(UsbTxBuf, "? |Help"); writeUSB(); delay_ms(10);
UsbTxBuf[0] = '\0'; writeUSB(); //To stop unnecessary Transmit
break;
default : udspnsup(); break;
}
}
}
else
{
puts("USB cable was cut off");
reset_cpu();
}
}
}
 【 動作状況 】
   茶色の文字が送信文字列、青の文字が受信文字列です。




 
(2)  PC側ソフト(VB6)
   VB6によるサンプルは次の通りです。リストは各種の定義と送受信の部分だけですので、これを標準モジュールとして組み込み、アプリの部分からこれを呼び出して使えば良いです。

'======================================================================================
' Routines for to call MPUSBAPI.DLL
'======================================================================================

Option Explicit

'--------------------------------------------------------------------------------------
' Function definition contents in VB
'--------------------------------------------------------------------------------------
Public Declare Function MPUSBGetDLLVersion Lib "mpusbapi.dll" () As Long
Public Declare Function MPUSBGetDeviceCount Lib "mpusbapi.dll" (ByVal pVID_PID As String) As Long
Public Declare Function MPUSBOpen Lib "mpusbapi.dll" (ByVal instance As Long, ByVal pVID_PID As String, ByVal pEP As String, ByVal dwDir As Long, ByVal dwReserved As Long) As Long
Public Declare Function MPUSBClose Lib "mpusbapi.dll" (ByVal handle As Long) As Long
Public Declare Function MPUSBRead Lib "mpusbapi.dll" (ByVal handle As Long, ByVal pData As Long, ByVal dwLen As Long, ByRef pLength As Long, ByVal dwMilliseconds As Long) As Long
Public Declare Function MPUSBWrite Lib "mpusbapi.dll" (ByVal handle As Long, ByVal pData As Long, ByVal dwLen As Long, ByRef pLength As Long, ByVal dwMilliseconds As Long) As Long
Public Declare Function MPUSBReadInt Lib "mpusbapi.dll" (ByVal handle As Long, ByVal pData As Long, ByVal dwLen As Long, ByRef pLength As Long, ByVal dwMilliseconds As Long) As Long

'--------------------------------------------------------------------------------------
' Constantes for WIN32 API
'--------------------------------------------------------------------------------------
Public Const INVALID_HANDLE_VALUE = -1
Public Const ERROR_INVALID_HANDLE = 6&

'--------------------------------------------------------------------------------------
' Function of WIN32 API
'--------------------------------------------------------------------------------------
Public Declare Function GetLastError Lib "kernel32" () As Long
Public Declare Function timeGetTime Lib "winmm.dll" () As Long

'--------------------------------------------------------------------------------------
' Constantes for connected PIC
'--------------------------------------------------------------------------------------
Public Const vid_pid = "vid_04d8&pid_0011" ' VID:04D8(Microchip)/PID:0011 要!・小文字
Public Const out_pipe = "\MCHP_EP1"
Public Const in_pipe = "\MCHP_EP1"

Public Const MPUSB_FAIL = 0
Public Const MPUSB_SUCCESS = 1

Public Const MP_WRITE = 0
Public Const MP_READ = 1

'--------------------------------------------------------------------------------------
' Variable of IN_PIPE and OUT_PIPE
'--------------------------------------------------------------------------------------
Public myInPipe As Long
Public myOutPipe As Long

'-------------------------------------- 追加
Public Const BUF_SIZE = 65
Public Send_Buf(0 To BUF_SIZE - 1) As Byte
Public Rec_Buf(0 To BUF_SIZE - 1) As Byte
Public LenRcv As Long
Public TST$


Function GetRecStrings(ByVal tout As Long) As String '受信Byte配列を文字列にして返す(CRは削除)
Dim i%, rec$ '受信していなければ、tout後 NULLを返す
'デリミタをVbCR(13)とする
rec = ""
If (MPUSBRead(myInPipe, VarPtr(Rec_Buf(0)), BUF_SIZE, LenRcv, tout)) Then
rec = Rec_Buf
rec = StrConv(rec, vbUnicode) '変換する
For i = 1 To LenRcv
If Mid(rec, i, 1) = vbCr Then
rec = Left(rec, i - 1): Exit For 'VbCRの前を抜き出す
End If
Next
End If
GetRecStrings = rec
End Function

'【パケットの受信】
'受信している1パケットをMPUSBRead()しない限り、次のパケットは捨てられるようである。


'--------------------------------------------------------------------------------------
' Open peripheral
'--------------------------------------------------------------------------------------
Sub OpenMPUSBDevice()
Dim tempPipe As Long
Dim count As Long

tempPipe = INVALID_HANDLE_VALUE
count = MPUSBGetDeviceCount(vid_pid)

If count > 0 Then
myOutPipe = MPUSBOpen(0, vid_pid, out_pipe, MP_WRITE, 0)
myInPipe = MPUSBOpen(0, vid_pid, in_pipe, MP_READ, 0)

If myOutPipe = INVALID_HANDLE_VALUE Or myInPipe = INVALID_HANDLE_VALUE Then
MsgBox Str(myOutPipe) + " " + Str(myInPipe) + " When open pipes, error occurs"
myOutPipe = INVALID_HANDLE_VALUE
myInPipe = INVALID_HANDLE_VALUE
End If
Else
MsgBox "Peripheral device is not connected"
End If
End Sub

'--------------------------------------------------------------------------------------
' Close peripheral
'--------------------------------------------------------------------------------------
Sub CloseMPUSBDevice()
If myOutPipe <> INVALID_HANDLE_VALUE Then
MPUSBClose (myOutPipe)
myOutPipe = INVALID_HANDLE_VALUE
End If

If myInPipe <> INVALID_HANDLE_VALUE Then
MPUSBClose (myInPipe)
myInPipe = INVALID_HANDLE_VALUE
End If
End Sub

'--------------------------------------------------------------------------------------
' Function: Send_Receive
'
' SendData: Sending data buffer
' SendLength: Sending data length
' ReceiveData: Receiving data buffer
' ReceiveLength: Receiving data length
' SendDelay: Sending Time-out(ms)
' ReceiveDelay: Receiving Time-out(ms)
'--------------------------------------------------------------------------------------

Function Send_Receive(ByRef SendData() As Byte, SendLength As Long, _
ByRef ReceiveData() As Byte, ByRef ReceiveLength As Long, _
ByVal SendDelay As Long, ByVal ReceiveDelay As Long) As Long


Dim SentDataLength As Long
Dim ExpectedReceiveLength As Long

ExpectedReceiveLength = ReceiveLength
'パイプが使える状態かを確認してから送受信に入る
If (myOutPipe <> INVALID_HANDLE_VALUE And myInPipe <> INVALID_HANDLE_VALUE) Then
'送信
If (MPUSBWrite(myOutPipe, VarPtr(SendData(0)), SendLength, SentDataLength, SendDelay) = MPUSB_SUCCESS) Then
'受信
If (MPUSBRead(myInPipe, VarPtr(ReceiveData(0)), ExpectedReceiveLength, ReceiveLength, ReceiveDelay) = MPUSB_SUCCESS) Then
'受信データ長の確認
If (ReceiveLength = ExpectedReceiveLength) Then
Send_Receive = 1 ' All OK
Exit Function
ElseIf (ReceiveLength < ExpectedReceiveLength) Then
Send_Receive = 2 ' Received it, but it's broken
Exit Function
End If
Else
CheckInvalidHandle ' Message of Error
End If
Else
CheckInvalidHandle ' Message of Error
End If
End If

Send_Receive = 0 ' Operation failed

End Function

'--------------------------------------------------------------------------------------
' Show the error type
'--------------------------------------------------------------------------------------
Sub CheckInvalidHandle()
If (GetLastError() = ERROR_INVALID_HANDLE) Then
' It is usually caused by unconnected states of the devices
CloseMPUSBDevice
Else
MsgBox "Error code: " + Str(GetLastError())
End If
End Sub

'--------------------------------------------------------------------------------------
' Function : Send
'
' SendData: Sending data buffer
' SendLength: Sending data length
' SendDelay: Sending Time-out(ms)
'--------------------------------------------------------------------------------------
Function Send(ByRef SendData() As Byte, SendLength As Long, ByVal SendDelay As Long) As Long
Dim SentDataLength As Long

If (myOutPipe <> INVALID_HANDLE_VALUE And myInPipe <> INVALID_HANDLE_VALUE) Then
If (MPUSBWrite(myOutPipe, VarPtr(SendData(0)), SendLength, SentDataLength, SendDelay) = MPUSB_SUCCESS) Then
Send = 1 ' All OK
Exit Function
Else
CheckInvalidHandle ' Message of Error
End If
End If
Send = 0 ' Operation failed
End Function


 
(3)  PC側ソフト(VB2005)
   VB2005によるサンプルは次の通りです。VarPtr関数はサポートされておらずまた定数のvbUnicodeなどもサポートされていないので、それに代わる方法を使っています。リストは、各種の定義と送受信の部分だけですので、これを標準モジュールとして組み込み、アプリの部分からこれを呼び出して使えば良いです。

追記 】 この変換によるソフトのUSB入出力の速度は、やや落ちています。VB.NETの場合は、WinUSBによる方法が良いと思います。WinUSBによるテストでは、VB6による速度とほぼ同等になっています。

Option Strict Off
Option Explicit On

Imports VB = Microsoft.VisualBasic
Imports System.Runtime.InteropServices

Module VBMPUSBAPI
'======================================================================================
' Routines for to call MPUSBAPI.DLL
'======================================================================================

'--------------------------------------------------------------------------------------
' Function definition contents in VB.NET
'--------------------------------------------------------------------------------------
Public Declare Function MPUSBGetDLLVersion Lib "mpusbapi.dll" () As Integer
Public Declare Function MPUSBGetDeviceCount Lib "mpusbapi.dll" (ByVal pVID_PID As String) As Integer
Public Declare Function MPUSBOpen Lib "mpusbapi.dll" (ByVal instance As Integer, ByVal pVID_PID As String, ByVal pEP As String, ByVal dwDir As Integer, ByVal dwReserved As Integer) As Integer
Public Declare Function MPUSBClose Lib "mpusbapi.dll" (ByVal handle As Integer) As Integer
Public Declare Function MPUSBRead Lib "mpusbapi.dll" (ByVal handle As Integer, ByVal pData As Integer, ByVal dwLen As Integer, ByRef pLength As Integer, ByVal dwMilliseconds As Integer) As Integer
Public Declare Function MPUSBWrite Lib "mpusbapi.dll" (ByVal handle As Integer, ByVal pData As Integer, ByVal dwLen As Integer, ByRef pLength As Integer, ByVal dwMilliseconds As Integer) As Integer
Public Declare Function MPUSBReadInt Lib "mpusbapi.dll" (ByVal handle As Integer, ByVal pData As Integer, ByVal dwLen As Integer, ByRef pLength As Integer, ByVal dwMilliseconds As Integer) As Integer

'--------------------------------------------------------------------------------------
' Constantes for WIN32 API
'--------------------------------------------------------------------------------------
Public Const INVALID_HANDLE_VALUE As Short = -1
Public Const ERROR_INVALID_HANDLE As Short = 6

'--------------------------------------------------------------------------------------
' Function of WIN32 API
'--------------------------------------------------------------------------------------
Public Declare Function GetLastError Lib "kernel32" () As Integer
Public Declare Function timeGetTime Lib "winmm.dll" () As Integer

'--------------------------------------------------------------------------------------
' Constantes for connected PIC
'--------------------------------------------------------------------------------------
Public Const vid_pid As String = "vid_04d8&pid_0011" ' VID:04D8(Microchip)/PID:0011 要!・小文字
Public Const out_pipe As String = "\MCHP_EP1"
Public Const in_pipe As String = "\MCHP_EP1"

Public Const MPUSB_FAIL As Short = 0
Public Const MPUSB_SUCCESS As Short = 1

Public Const MP_WRITE As Short = 0
Public Const MP_READ As Short = 1

'--------------------------------------------------------------------------------------
' Variable of IN_PIPE and OUT_PIPE
'--------------------------------------------------------------------------------------
Public myInPipe As Integer
Public myOutPipe As Integer

'-------------------------------------- 追加
Public Const BUF_SIZE As Short = 65
Public Send_Buf(BUF_SIZE - 1) As Byte
Public Rec_Buf(BUF_SIZE - 1) As Byte
Public LenRcv As Integer
Public TST As String


Function GetRecStrings(ByVal tout As Integer) As String '受信Byte配列を文字列にして返す(CRは削除)
Dim i As Short '受信していなければ、tout後 NULLを返す
Dim rec As String = "" 'デリミタをVbCR(13)とする
'ガベージコレクションでオブジェクトが移動されないようにする(VarPtrがサポートされないため以下の方法でアドレスを得る)
Dim gch As GCHandle = GCHandle.Alloc(Rec_Buf, GCHandleType.Pinned)
Dim adrs As Integer = gch.AddrOfPinnedObject().ToInt32()

If (MPUSBRead(myInPipe, adrs, BUF_SIZE, LenRcv, tout)) Then
For i = 0 To LenRcv - 1 'StrConv(rec, vbUnicode) はアップグレードされないので以下の方法を使う
If Rec_Buf(i) <> 13 Then
rec = rec & Chr(Rec_Buf(i)) 'VbCRの前まで
End If
Next
End If
GetRecStrings = rec
'ハンドルを開放する
gch.Free()
End Function

'【VB6 ->VB.NET】変換時に変換不可であった VarPtr, StrConv(rec, vbUnicode) については・・・
'(1) VarPtr についてはガベージコレクションを停止しそして再開する方法を調べ出して使用した。
'(2) vbUnicodeがサポートされていないので、rec = rec & Chr(Rec_Buf(i))を繰り返した。

'--------------------------------------------------------------------------------------
' Open peripheral
'--------------------------------------------------------------------------------------
Sub OpenMPUSBDevice()
Dim tempPipe As Integer
Dim count As Integer

tempPipe = INVALID_HANDLE_VALUE
count = MPUSBGetDeviceCount(vid_pid)

If count > 0 Then
myOutPipe = MPUSBOpen(0, vid_pid, out_pipe, MP_WRITE, 0)
myInPipe = MPUSBOpen(0, vid_pid, in_pipe, MP_READ, 0)

If myOutPipe = INVALID_HANDLE_VALUE Or myInPipe = INVALID_HANDLE_VALUE Then
MsgBox(Str(myOutPipe) & " " & Str(myInPipe) & " When open pipes, error occurs")
myOutPipe = INVALID_HANDLE_VALUE
myInPipe = INVALID_HANDLE_VALUE
End If
Else
MsgBox("Peripheral device is not connected")
End If
End Sub

'--------------------------------------------------------------------------------------
' Close peripheral
'--------------------------------------------------------------------------------------
Sub CloseMPUSBDevice()
If myOutPipe <> INVALID_HANDLE_VALUE Then
MPUSBClose(myOutPipe)
myOutPipe = INVALID_HANDLE_VALUE
End If

If myInPipe <> INVALID_HANDLE_VALUE Then
MPUSBClose(myInPipe)
myInPipe = INVALID_HANDLE_VALUE
End If
End Sub

'--------------------------------------------------------------------------------------
' Function: Send_Receive
'
' SendData: Sending data buffer
' SendLength: Sending data length
' ReceiveData: Receiving data buffer
' ReceiveLength: Receiving data length
' SendDelay: Sending Time-out(ms)
' ReceiveDelay: Receiving Time-out(ms)
'--------------------------------------------------------------------------------------

Function Send_Receive(ByRef SendData() As Byte, ByRef SendLength As Integer, ByRef ReceiveData() As Byte, ByRef ReceiveLength As Integer, ByVal SendDelay As Integer, ByVal ReceiveDelay As Integer) As Integer


Dim SentDataLength As Integer
Dim ExpectedReceiveLength As Integer
'ガベージコレクションがオブジェクトを移動できないようにする(VarPtrが使えないため)
Dim gch As GCHandle = GCHandle.Alloc(SendData, GCHandleType.Pinned)
Dim adrs As Integer = gch.AddrOfPinnedObject().ToInt32()
Dim gch2 As GCHandle = GCHandle.Alloc(ReceiveData, GCHandleType.Pinned)
Dim adrs2 As Integer = gch2.AddrOfPinnedObject().ToInt32()

ExpectedReceiveLength = ReceiveLength

If (myOutPipe <> INVALID_HANDLE_VALUE And myInPipe <> INVALID_HANDLE_VALUE) Then
If (MPUSBWrite(myOutPipe, adrs, SendLength, SentDataLength, SendDelay) = MPUSB_SUCCESS) Then
'読み出し:元々第3引数はExpectedReceiveLengthである。これでエラーが出るのでBUF_SIZEとするとOKになる。
If (MPUSBRead(myInPipe, adrs2, BUF_SIZE, ReceiveLength, ReceiveDelay) = MPUSB_SUCCESS) Then
If (ReceiveLength = ExpectedReceiveLength) Then
Send_Receive = 1 ' All OK
Exit Function
ElseIf (ReceiveLength < ExpectedReceiveLength) Then
Send_Receive = 2 ' Received it, but it's broken
Exit Function
End If
Else
CheckInvalidHandle() ' Message of Error
End If
Else
CheckInvalidHandle() ' Message of Error
End If
End If

Send_Receive = 0 ' Operation failed
gch.Free()
gch2.Free()
End Function

'--------------------------------------------------------------------------------------
' Show the error type
'--------------------------------------------------------------------------------------
Sub CheckInvalidHandle()
If (GetLastError() = ERROR_INVALID_HANDLE) Then
' It is usually caused by unconnected states of the devices
CloseMPUSBDevice()
Else
MsgBox("Error code: " & Str(GetLastError()))
End If
End Sub

'--------------------------------------------------------------------------------------
' Function : Send
'
' SendData: Sending data buffer
' SendLength: Sending data length
' SendDelay: Sending Time-out(ms)
'--------------------------------------------------------------------------------------
Function Send(ByRef SendData() As Byte, ByRef SendLength As Integer, ByVal SendDelay As Integer) As Integer
Dim SentDataLength As Integer
'ガベージコレクションがオブジェクトを移動できないようにする(VarPtrが使えないため)
Dim gch As GCHandle = GCHandle.Alloc(SendData, GCHandleType.Pinned)
Dim adrs As Integer = gch.AddrOfPinnedObject().ToInt32()

If (myOutPipe <> INVALID_HANDLE_VALUE And myInPipe <> INVALID_HANDLE_VALUE) Then
If (MPUSBWrite(myOutPipe, adrs, SendLength, SentDataLength, SendDelay) = MPUSB_SUCCESS) Then
Send = 1 ' All OK
Exit Function
Else
CheckInvalidHandle() ' Message of Error
End If
End If
Send = 0 ' Operation failed
gch.Free()
End Function
End Module


[
] テストに使用した回路
PIC18F2550を使用しています。また、PICkit2を接続したままディバッグできるようにしています。LCDやRS232Cは、USBの評価自体には必要はないのでなくとも良いです。ただ、RS232Cはディバッグに役にたつと思います。


Reported by TokioYamada@ADK


汎用製品通販のページへ      高速フーリエ変換(dsPIC-FFT)へ      USB-IOのページに戻 る