18F14K50で、簡易オシロスコープ
2011年11月13日
18F14K50で、USB通信の続き。
先の記事で、とりあえずHIDでの通信ができるようになったと書いた。USBの仕様は膨大かつ複雑であり、これをブラックボックスとして扱わないと、先に進めない。当分の間、この複雑な仕様の部分は「改訂新版 PICで楽しむ USB 機器自作のすすめ」に頼って進めることにする。
HIDでは、一定のタイミングで、PCからPICへ、PICからPCへ、通信が行われているらしい。これは、デスクリプタでもそう宣言しているし、ネットで色々調べてみても、普通はそのような形で行くようだ。まずは、この通信がどのようなタイミングで行われているか、調べてみた。
本で行われている方法、つまり、「((USBDeviceState >= CONFIGURED_STATE)&&(USBSuspendControl!=1))」の場合は、PCからPICへ通信が行われたときだけ、真になるようだ。もし18F14K50が大量のRAMを実装しているならば、ADコンバータからの値を順次読み取ってまずRAMに保存しておき、その後、RAMに保存された情報をPCからの命令に従ってPICから送付する形でよい。PICを使ったオシロスコープでは、そういったやり方で行っているものもあるようだ。
ただし、18F14K50のRAMの実装量はあまり多くない。USB受信用に64 bytes、送信用に64 bytesを割り当てているが、使用可能なRAMの残りは70 bytesほどしかないようである。70 bytesでは、データー保存領域としては、ぜんぜん役に立たないと言ってよい。
他方、1 msecの間のデータ保存領域として、70 bytesは十分である。もともと、PICのA/Dコンバーターの速度は20-40 μsecほどで、10 bitsのデーターだから、これだけあれば十分入る。従って、1 msecの間測定を行い、それを順次PCに送ればよい。
先に作った周波数測定器を接続して色々調べた結果、USBのイベントのうち、「EVENT_SOF」が、通信があろうと無かろうと1 msecごとに発生することがわかった。これを、測定の開始と、データーの送信のトリガーに使えばよいだろう。
さらに、PICの内部タイマーを設定して、その値を送信するプログラムをPICに仕込んで調べた結果、「EVENT_SOF」とデーターの送受信は、次のようなタイミングで行われることがわかった。
0 μsec EVENT_SOF
120 μsec PICからPCへ送信
210 μsec PCからPICへ受信
1000 μsec EVENT_SOF
おそらく、PIC18F14K50の仕様としては、EVENT_SOFの0.1 msec後にPICから、さらにその0.1 msec後にPCから送信が行われるのであろう。なので、PC側では0.1 msec以内に、PIC側では0.9 msec以内にレスポンスすれば、送受信とも、64 bytes/msecのスピードで行われると思われる。PICとPCの実行速度の差から、このような仕様にしてあるのではなかろうか。
ただ、今回のオシロスコープでは、0.9 msec以内のレスポンスは、無理である。というのは、1 msec の間、ずっと測定を続けないといけないので、1 msec 弱、測定が続く。0.9 msec以内にレスポンスするようにプログラミングすれば、1 msecの間に約 0.1 msec、つまり、全体の10%の期間、測定が行われないことになってしまう。
そこで、一度PCから測定の指令がくれば、一定の間(例えば1秒間)、PICからは連続的に、1 msecごとの送信を行う形にした。具体的には、送信用のバッファを2つ用意し、一つはUSB送信に用い、もう一つはアナログデーターのシグナルの保存に用いる。1 msecごとに、この2つのバッファーを交換すれば、測定を続けながらUSB送信も連続的に行うことができる。
そんなで出来たのが、下のコード。とりあえず、家庭用のコンセントの周波数60 Hzを読み取ることが出来た。後は、PC用のソフトを仕上げるだけだ。
先の記事で、とりあえずHIDでの通信ができるようになったと書いた。USBの仕様は膨大かつ複雑であり、これをブラックボックスとして扱わないと、先に進めない。当分の間、この複雑な仕様の部分は「改訂新版 PICで楽しむ USB 機器自作のすすめ」に頼って進めることにする。
HIDでは、一定のタイミングで、PCからPICへ、PICからPCへ、通信が行われているらしい。これは、デスクリプタでもそう宣言しているし、ネットで色々調べてみても、普通はそのような形で行くようだ。まずは、この通信がどのようなタイミングで行われているか、調べてみた。
本で行われている方法、つまり、「((USBDeviceState >= CONFIGURED_STATE)&&(USBSuspendControl!=1))」の場合は、PCからPICへ通信が行われたときだけ、真になるようだ。もし18F14K50が大量のRAMを実装しているならば、ADコンバータからの値を順次読み取ってまずRAMに保存しておき、その後、RAMに保存された情報をPCからの命令に従ってPICから送付する形でよい。PICを使ったオシロスコープでは、そういったやり方で行っているものもあるようだ。
ただし、18F14K50のRAMの実装量はあまり多くない。USB受信用に64 bytes、送信用に64 bytesを割り当てているが、使用可能なRAMの残りは70 bytesほどしかないようである。70 bytesでは、データー保存領域としては、ぜんぜん役に立たないと言ってよい。
他方、1 msecの間のデータ保存領域として、70 bytesは十分である。もともと、PICのA/Dコンバーターの速度は20-40 μsecほどで、10 bitsのデーターだから、これだけあれば十分入る。従って、1 msecの間測定を行い、それを順次PCに送ればよい。
先に作った周波数測定器を接続して色々調べた結果、USBのイベントのうち、「EVENT_SOF」が、通信があろうと無かろうと1 msecごとに発生することがわかった。これを、測定の開始と、データーの送信のトリガーに使えばよいだろう。
さらに、PICの内部タイマーを設定して、その値を送信するプログラムをPICに仕込んで調べた結果、「EVENT_SOF」とデーターの送受信は、次のようなタイミングで行われることがわかった。
0 μsec EVENT_SOF
120 μsec PICからPCへ送信
210 μsec PCからPICへ受信
1000 μsec EVENT_SOF
おそらく、PIC18F14K50の仕様としては、EVENT_SOFの0.1 msec後にPICから、さらにその0.1 msec後にPCから送信が行われるのであろう。なので、PC側では0.1 msec以内に、PIC側では0.9 msec以内にレスポンスすれば、送受信とも、64 bytes/msecのスピードで行われると思われる。PICとPCの実行速度の差から、このような仕様にしてあるのではなかろうか。
ただ、今回のオシロスコープでは、0.9 msec以内のレスポンスは、無理である。というのは、1 msec の間、ずっと測定を続けないといけないので、1 msec 弱、測定が続く。0.9 msec以内にレスポンスするようにプログラミングすれば、1 msecの間に約 0.1 msec、つまり、全体の10%の期間、測定が行われないことになってしまう。
そこで、一度PCから測定の指令がくれば、一定の間(例えば1秒間)、PICからは連続的に、1 msecごとの送信を行う形にした。具体的には、送信用のバッファを2つ用意し、一つはUSB送信に用い、もう一つはアナログデーターのシグナルの保存に用いる。1 msecごとに、この2つのバッファーを交換すれば、測定を続けながらUSB送信も連続的に行うことができる。
そんなで出来たのが、下のコード。とりあえず、家庭用のコンセントの周波数60 Hzを読み取ることが出来た。後は、PC用のソフトを仕上げるだけだ。
/********************************************************************
* Oscillo scope using PIC18F14K50
* HID class USB interface
********************************************************************/
/** ファイルインクルード **************************/
#include "./USB/usb.h"
#include "HardwareProfile.h"
#include "./USB/usb_function_hid.h"
/** コンフィギュレーション ***********************/
#pragma config CPUDIV = NOCLKDIV ,USBDIV = OFF, PCLKEN = ON
#pragma config FOSC = HS, PLLEN = ON, HFOFST = OFF
#pragma config PWRTEN = ON, BOREN = OFF, MCLRE = OFF, BORV = 30
#pragma config WDTEN = OFF, LVP = OFF, FCMEN = OFF, IESO = OFF
#pragma config CP0 = OFF, XINST = OFF
/******** USB関連バッファ、変数定義 ****/
#pragma udata usbram2
unsigned char ReceivedDataBuffer[64];
unsigned char SendBuf1[64];
unsigned char SendBuf2[64];
unsigned char* SendBuf=SendBuf1;
/***** Structures of buffers *****
ReceivedDataBuffer[0]:
0x30: Check connection.
0x40: Read analog input for indicated time
ReceiveDataBuffer[1]:
Process ID (incriments every request)
ReceiveDataBuffer[2]:
Measuring mode (0x30, 0x31, or 0x32)
ReceiveDataBuffer[3-4]:
Length of measurment in msec (<1000)
ReceiveDataBuffer[5-63]:
Reserved
SendBuff[0-2]:
Copy of ReceivedDataBuffer
SendBuff[3-4]
Measurement remaining time in msec
SendBuff[5-6]
Timer1 value when the first measurement
SendBuff[7-8]
Timer1 value when the last measurement
SendBuff[9-15]
Reserved
SendBuff[16-63]
Measured analog values
**********************************/
#pragma udata
USB_HANDLE USBOutHandle = 0;
USB_HANDLE USBInHandle = 0;
/** 関数プロトタイピング ****************************/
void init(void);
void main(void);
void ProcessIO(void);
void measureAnalog(void);
void YourHighPriorityISRCode();
void YourLowPriorityISRCode();
void USBCBSendResume(void);
/************ 割り込みベクタ定義 ***********/
#pragma code REMAPPED_HIGH_INTERRUPT_VECTOR = 0x08
void Remapped_High_ISR (void)
{
_asm goto YourHighPriorityISRCode _endasm
}
#pragma code REMAPPED_LOW_INTERRUPT_VECTOR = 0x18
void Remapped_Low_ISR (void)
{
_asm goto YourLowPriorityISRCode _endasm
}
/************* 割り込み処理関数 ************/
#pragma code
#pragma interrupt YourHighPriorityISRCode
void YourHighPriorityISRCode()
{
USBDeviceTasks();
}
#pragma interruptlow YourLowPriorityISRCode
void YourLowPriorityISRCode()
{
}
/***** global vars *****/
// Triggered by EVENT_SOF
unsigned char esof=0;
// Command and ID
unsigned char command=0x30;
unsigned char commandID=0;
// Measuring time by msec.
unsigned int timesMeasure=0;
// Measuring mode
// 0x30: only AN10(RB4)
// 0x31: AN10(RB4) and AN6(RC2)
// 0x32: AN10(RB4), AN6(RC2), and AN7(RC3)
unsigned char measureMode=0x30;
/***** Macros as functions *****/
// Oscilattion for making -5V
#define ReverseRB6() LATBbits.LATB6=!LATBbits.LATB6
// Check if USB is connected.
#define USBconnected() ((USBDeviceState >= CONFIGURED_STATE)&&(USBSuspendControl!=1))
#pragma code
/*************** initialize PIC18f14K50 *********************/
void init(void){
/* IO pins configurations */
//analog: RB4(AN10), RB5(AN11), RC0(AN4), RC1(AN5)
ANSEL = 0x30; // ANS4 and ANS5
ANSELH = 0xC0; // ANS10 and ANS11
TRISA = 0xFF; // A0-5 are not used as I/O, all set as input.
LATB = 0;
TRISB = 0x10; // RB4: input, others: output
TRISC = 0xFF; // all input
/* ADC initialize */
ADCON0 = 0; // Stopped
ADCON1 = 0x00; // VDD and Vss
//ADCON2 = 0xBE; // Right-justified, 20TAD, Fosc/64
//ADCON2 = 0xA6; // Right-justified, 8TAD, Fosc/64
ADCON2 = 0xB6; // Right-justified, 16TAD, Fosc/64
/* USB */
USBDeviceInit(); // Init USB
USBInHandle = 0;
USBOutHandle = 0;
USBDeviceAttach(); // Enable inturrupt
// Timer settings
T0CON=0x82; // Timer0 ON, 16 bit, internal clock, 1:8 prescaller (1500 clocks / msec)
T1CON=0xF9; // Timer1 ON, 16 bit, internal Fosc/4, 1:8 prescaller (1500 clocks / msec)
}
/*************** main *******************/
void main(void)
{
unsigned char i,j;
init();
/*********** main loop ***************/
while(1)
{
if(!USBconnected()){
// USB not connected.
LATBbits.LATB7=1;
timesMeasure=0;
continue;
}
LATBbits.LATB7=0;
/***** Request from PC ******/
if(!HIDRxHandleBusy(USBOutHandle))
{
ProcessIO();
/* Receive next request */
USBOutHandle = HIDRxPacket(HID_EP,(BYTE*)&ReceivedDataBuffer,64);
}
/*
* Each events occurs in following order
* 0 usec EVENT_SOF
* 120 usec Data sent to PC
* 210 usec Data from PC (EVENT_TRANSFER??)
* 1000 usec EVENT_SOF
*/
//EVENT_SOF
if (esof) {
// EVENT_SOF event occurs every 1 msec.
// So, send the data every 1 msec.
esof=0;
if (0<timesMeasure) {
USBInHandle=HIDTxPacket(HID_EP,(BYTE*)&SendBuf[0],64);
// Swap Send Buffer and alternative Buffer
if (SendBuf==SendBuf1) {
SendBuf=SendBuf2;
} else {
SendBuf=SendBuf1;
}
// Call for analog measurement
measureAnalog();
if (timesMeasure==0) {
// The last data will be sent here.
USBInHandle=HIDTxPacket(HID_EP,(BYTE*)&SendBuf[0],64);
}
}
}
}
}
void ProcessIO(){
unsigned char i,j;
// Request from PC
switch(ReceivedDataBuffer[0]){
case 0x30:
SendBuf[0]=ReceivedDataBuffer[0];
SendBuf[1]=ReceivedDataBuffer[1];
SendBuf[2]=ReceivedDataBuffer[2];
SendBuf[3]='O';
SendBuf[4]='K';
timesMeasure=0;
USBInHandle=HIDTxPacket(HID_EP,(BYTE*)&SendBuf[0],64);
break;
case 0x40:
command = ReceivedDataBuffer[0];
commandID = ReceivedDataBuffer[1];
measureMode = ReceivedDataBuffer[2];
timesMeasure=((unsigned int)ReceivedDataBuffer[4])<<8 + ReceivedDataBuffer[3];
if (timesMeasure<1000) timesMeasure=1000;
TMR1H=0xFB;
TMR1L=0x90;
break;
default:
timesMeasure=0;
break;
}
}
void measureAnalog(){
unsigned char i,j;
// Copy the command
SendBuf[0]=command;
SendBuf[1]=commandID;
SendBuf[2]=measureMode;
// remaining measurement times
timesMeasure--;
SendBuf[3]=timesMeasure & 0xff;
SendBuf[4]=timesMeasure >> 8;
// Read Timer1
SendBuf[5]=TMR1L;
SendBuf[6]=TMR1H;
i=16;
TMR0L=0;
// Following routine takes about 41.4 usec/cycle.
switch(measureMode){
case 0x30:
// 0x30: only AN10(RB4)
for(j=0;j<24;j++){
ADCON0=0x29; // Read AN10(RB4) (analog input)
ADCON0bits.GO=1;
while(ADCON0bits.NOT_DONE);
SendBuf[i++]=ADRESL;
SendBuf[i++]=ADRESH | (PORTC&0xFC);
if (j<23) while(TMR0L<61);
TMR0L=0;
}
break;
case 0x31:
// 0x31: AN10(RB4) and AN6(RC2)
for(j=0;j<12;j++){
ADCON0=0x29; // Read AN10(RB4) (analog input)
ADCON0bits.GO=1;
while(ADCON0bits.NOT_DONE);
SendBuf[i++]=ADRESL;
SendBuf[i++]=ADRESH | (PORTC&0xFC);
while(TMR0L<61);
TMR0L=0;
ADCON0=0x19; // Read AN6(RC2) (analog input)
ADCON0bits.GO=1;
while(ADCON0bits.NOT_DONE);
SendBuf[i++]=ADRESL;
SendBuf[i++]=ADRESH | (PORTC&0xFC);
if (j<11) while(TMR0L<61);
TMR0L=0;
}
break;
case 0x32:
// 0x32: AN10(RB4), AN6(RC2), and AN7(RC3)
for(j=0;j<8;j++){
ADCON0=0x29; // Read AN10(RB4) (analog input)
ADCON0bits.GO=1;
while(ADCON0bits.NOT_DONE);
SendBuf[i++]=ADRESL;
SendBuf[i++]=ADRESH | (PORTC&0xFC);
while(TMR0L<61);
TMR0L=0;
ADCON0=0x19; // Read AN6(RC2) (analog input)
ADCON0bits.GO=1;
while(ADCON0bits.NOT_DONE);
SendBuf[i++]=ADRESL;
SendBuf[i++]=ADRESH | (PORTC&0xFC);
while(TMR0L<61);
TMR0L=0;
ADCON0=0x1D; // Read AN7(RC3) (analog input)
ADCON0bits.GO=1;
while(ADCON0bits.NOT_DONE);
SendBuf[i++]=ADRESL;
SendBuf[i++]=ADRESH | (PORTC&0xFC);
if (j<7) while(TMR0L<61);
TMR0L=0;
}
break;
default:
break;
}
SendBuf[7]=TMR1L;
SendBuf[8]=TMR1H;
}
/******************************************************************
************** USB Callback Functions *****************************
*******************************************************************/
/******************************************************************
* Function: void USBCBSuspend(void)
******************************************************************/
void USBCBSuspend(void)
{
}
/*******************************************************************
* Function: void USBCBWakeFromSuspend(void)
*******************************************************************/
void USBCBWakeFromSuspend(void)
{
}
/********************************************************************
* Function: void USBCB_SOF_Handler(void)
*******************************************************************/
void USBCB_SOF_Handler(void)
{
// This event is called every 1 msec.
esof=1;
}
/*******************************************************************
* Function: void USBCBErrorHandler(void)
*******************************************************************/
void USBCBErrorHandler(void)
{
}
/*******************************************************************
* Function: void USBCBCheckOtherReq(void)
*******************************************************************/
void USBCBCheckOtherReq(void)
{
USBCheckHIDRequest();
}//end
/*******************************************************************
* Function: void USBCBStdSetDscHandler(void)
*******************************************************************/
void USBCBStdSetDscHandler(void)
{
}//end
/*******************************************************************
* Function: void USBCBInitEP(void)
*******************************************************************/
void USBCBInitEP(void)
{
//enable the HID endpoint
USBEnableEndpoint(HID_EP,USB_IN_ENABLED|USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
//Re-arm the OUT endpoint for the next packet
USBOutHandle = HIDRxPacket(HID_EP,(BYTE*)&ReceivedDataBuffer,64);
}
/*******************************************************************
* Function: void USBCBSendResume(void)
******************************************************************/
void USBCBSendResume(void)
{
static WORD delay_count;
if(USBGetRemoteWakeupStatus() == TRUE)
{
//Verify that the USB bus is in fact suspended, before we send
//remote wakeup signalling.
if(USBIsBusSuspended() == TRUE)
{
USBMaskInterrupts();
//Clock switch to settings consistent with normal USB operation.
USBCBWakeFromSuspend();
USBSuspendControl = 0;
USBBusIsSuspended = FALSE; //So we don't execute this code again,
delay_count = 3600U;
do
{
delay_count--;
}while(delay_count);
//Now drive the resume K-state signalling onto the USB bus.
USBResumeControl = 1; // Start RESUME signaling
delay_count = 1800U; // Set RESUME line for 1-13 ms
do
{
delay_count--;
}while(delay_count);
USBResumeControl = 0; //Finished driving resume signalling
USBUnmaskInterrupts();
}
}
}
/*******************************************************************
* Function: BOOL USER_USB_CALLBACK_EVENT_HANDLER(
*******************************************************************/
BOOL USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, WORD size)
{
switch(event)
{
case EVENT_TRANSFER:
break;
case EVENT_SOF:
USBCB_SOF_Handler();
break;
case EVENT_SUSPEND:
USBCBSuspend();
break;
case EVENT_RESUME:
USBCBWakeFromSuspend();
break;
case EVENT_CONFIGURED:
USBCBInitEP();
break;
case EVENT_SET_DESCRIPTOR:
USBCBStdSetDscHandler();
break;
case EVENT_EP0_REQUEST:
USBCBCheckOtherReq();
break;
case EVENT_BUS_ERROR:
USBCBErrorHandler();
break;
case EVENT_TRANSFER_TERMINATED:
break;
default:
break;
}
return TRUE;
}コメント
岩岡修平 (2018年5月16日 08:41:41)
はじめまして。
突然のご連絡をさせて頂くこと、大変恐れ入ります。
当方、ウェブマーケティングを行なっております、岩岡と申します。
こちらのデジタルオシロスコープのブログなどを拝見させて頂き、是非とも現在クライアントと行っているプロジェクトにお力添えを頂けないかと思い、ご連絡をさせて頂いております。
電子関連部品にご精通されているとお見受けさせて頂いた次第で、当クライアントもそのような方にご協力頂けることを理想としております。
ご依頼したい内容に関しましては、下記の通りとなっております。
特定電子部品の内容に関して
・新規記事をご執筆、掲載して頂く 又は
・既存記事内に追記して頂く
つきましては内容に応じて、謝礼金をお支払いさせて頂く次第です。
上記の内容について、管理人様の方で承っておりますでしょうか?
お忙しいなか恐縮ではありますが、お返事頂けますと誠に幸いです。
また貴重なお時間をあて最後までお読みいただき、誠にありがとうございます。
どうぞよろしくお願い致します。
岩岡修平
shuhei.iwaoka@gmail.com
突然のご連絡をさせて頂くこと、大変恐れ入ります。
当方、ウェブマーケティングを行なっております、岩岡と申します。
こちらのデジタルオシロスコープのブログなどを拝見させて頂き、是非とも現在クライアントと行っているプロジェクトにお力添えを頂けないかと思い、ご連絡をさせて頂いております。
電子関連部品にご精通されているとお見受けさせて頂いた次第で、当クライアントもそのような方にご協力頂けることを理想としております。
ご依頼したい内容に関しましては、下記の通りとなっております。
特定電子部品の内容に関して
・新規記事をご執筆、掲載して頂く 又は
・既存記事内に追記して頂く
つきましては内容に応じて、謝礼金をお支払いさせて頂く次第です。
上記の内容について、管理人様の方で承っておりますでしょうか?
お忙しいなか恐縮ではありますが、お返事頂けますと誠に幸いです。
また貴重なお時間をあて最後までお読みいただき、誠にありがとうございます。
どうぞよろしくお願い致します。
岩岡修平
shuhei.iwaoka@gmail.com
Katsumi (2018年5月16日 11:23:02)
連絡を、どうも有り難うございます。
残念ながら、私、そういったプロフェッショナルな仕事の請け負いをする事が出来ません。このブログやその他の場所で公表している私のプロジェクトを、ご利用なさるなどは出来ますが(多くがLGPL/GPLライセンスです)、新規のプロジェクトを担当する事は出来ません。
残念ながら、私、そういったプロフェッショナルな仕事の請け負いをする事が出来ません。このブログやその他の場所で公表している私のプロジェクトを、ご利用なさるなどは出来ますが(多くがLGPL/GPLライセンスです)、新規のプロジェクトを担当する事は出来ません。