平成27年 5月 20日
MPLAB Harmonyをライブラリ化する USBドライバをライブラリ化(その2)
関数の仕様に合わせて、関数内部を記述しますが、大半はMPLAB Harmonyの出力したソースリストを編集するだけです。
具体的には、system_init.c, system_tasks.c, system_interrupt.c の記述をコピーして編集します。ソースファイルは既存のapp.cに追加する部分と新規に作成するlib_usb_cdc_dual..cの二つです。
最初はlib_usb_cdc_dual.cです。こちらは先のソースファイルのコピーを編集します。
lib_usb_cdc_dual.c
#include "peripheral/peripheral.h"
#include "system_config.h"
#include "app.h"
#include "system_definitions.h"
#include "lib_usb_cdc_dual.h"
// from system_init.c
/****************************************************
* Endpoint Table needed by the Device Layer.
****************************************************/
uint8_t __attribute__((aligned(512))) endPointTable[USB_DEVICE_ENDPOINT_TABLE_SIZE];
/****************************************************
* USB Device Layer Initialization Data
****************************************************/
const USB_DEVICE_INIT usbDevInitData =
{
/* System module initialization */
.moduleInit = SYS_MODULE_POWER_RUN_FULL,
/* Identifies peripheral (PLIB-level) ID */
.usbID = USB_ID_1,
/* Stop in idle */
.stopInIdle = false,
/* Suspend in sleep */
.suspendInSleep = false,
/* Interrupt Source for USB module */
.interruptSource = INT_SOURCE_USB_1,
/* Endpoint table */
.endpointTable= endPointTable,
/* Number of function drivers registered to this instance of the
USB device layer */
.registeredFuncCount = 2,
/* Function driver table registered to this instance of the USB device layer*/
.registeredFunctions = (USB_DEVICE_FUNCTION_REGISTRATION_TABLE*)funcRegistrationTable,
/* Pointer to USB Descriptor structure */
.usbMasterDescriptor = (USB_DEVICE_MASTER_DESCRIPTOR*)&usbMasterDescriptor,
/* USB Device Speed */
.deviceSpeed = USB_SPEED_FULL,
};
// *****************************************************************************
// *****************************************************************************
// Section: System Data
// *****************************************************************************
// *****************************************************************************
/* Structure to hold the object handles for the modules in the system. */
SYSTEM_OBJECTS sysObj;
// *****************************************************************************
// *****************************************************************************
// Section: Module Initialization Data
// *****************************************************************************
// *****************************************************************************
/*** System Device Control Initialization Data ***/
const SYS_DEVCON_INIT sysDevconInit =
{
.moduleInit = {0},
};
//SYS_Initialize()を参考に作成
bool USBCDC_init(uint8_t ip, uint8_t is)
{
/* Core Processor Initialization */
sysObj.sysDevcon = SYS_DEVCON_Initialize(SYS_DEVCON_INDEX_0, (SYS_MODULE_INIT*)&sysDevconInit);
/* Initialize Middleware */
/* Set priority of USB interrupt source */
SYS_INT_VectorPrioritySet(INT_VECTOR_USB1, ip);
/* Set Sub-priority of USB interrupt source */
SYS_INT_VectorSubprioritySet(INT_VECTOR_USB1, is);
/* Initialize the USB device layer */
sysObj.usbDevObject0 = USB_DEVICE_Initialize (USB_DEVICE_INDEX_0 , ( SYS_MODULE_INIT* ) & usbDevInitData);
/* Initialize the Application */
APP_Initialize();
}
//from system_tasks.c
//SYS_Tasks()を参考に作成
void USBCDC_task(void)
{
/* Maintain the state machines of all library modules executing polled in
the system. */
/* Maintain system services */
SYS_DEVCON_Tasks(sysObj.sysDevcon);
/* Maintain Device Drivers */
/* Maintain USB Stack */
/* Device layer tasks routine */
USB_DEVICE_Tasks(sysObj.usbDevObject0);
/* Maintain the application's state machine. */
APP_Tasks();
}
次は、USBシリアルに対する読み書き関数をapp.cに追加します。
追加と同時に元プログラムで、各USARTの送受信を変則的に接続している記述を削除します。
最後に、読み出しと書き込みの終了を示すフラグの初期値が不適当なので、これを変更します。
この部分は全部を記載するのは長くなりすぎるので、部分的な記載に留めます。この一連の文書の最後(今回ではありません)には、このプロジェクトの圧縮ファイルを添付しますので、全体での変更はそちらで確認してください。
app.c(ソースの終わり付近のみを記載)
case APP_STATE_WAIT_FOR_CONFIGURATION:
/* Check if the device was configured */
if(appData.isConfigured)
{
/* If the device is configured then lets start
* the application */
appData.state = APP_STATE_CHECK_IF_CONFIGURED;
/* Schedule a read on COM1 and COM2 */
appData.appCOMPortObjects[COM1].isWriteComplete = true; //修正
appData.appCOMPortObjects[COM2].isWriteComplete = true; //修正
appData.appCOMPortObjects[COM1].isReadComplete = false;
appData.appCOMPortObjects[COM2].isReadComplete = false;
USB_DEVICE_CDC_Read(COM1,
&appData.appCOMPortObjects[COM1].readTransferHandle,
com1ReadBuffer, APP_READ_BUFFER_SIZE);
USB_DEVICE_CDC_Read(COM2,
&appData.appCOMPortObjects[COM2].readTransferHandle,
com2ReadBuffer, APP_READ_BUFFER_SIZE);
}
break;
case APP_STATE_CHECK_IF_CONFIGURED:
if(appData.isConfigured)
{
/* This means this device is still configured */
appData.state = APP_STATE_CHECK_FOR_READ_COMPLETE;
}
else
{
APP_StateReset();
appData.state = APP_STATE_WAIT_FOR_CONFIGURATION;
break;
}
//break;
case APP_STATE_CHECK_FOR_READ_COMPLETE:
//ここにあった処理は削除
appData.state = APP_STATE_CHECK_FOR_WRITE_COMPLETE;
break;
case APP_STATE_CHECK_FOR_WRITE_COMPLETE:
//ここにあった処理は削除
appData.state = APP_STATE_CHECK_IF_CONFIGURED;
break;
case APP_STATE_ERROR:
break;
default:
break;
}
}
//以下はプログラムを追加
#define CH_CHECK(ch, ret) if(ch != 1 && ch != 2) return ret; \
if(!appData.isConfigured) return ret;
uint8_t maxPacket;
//正常ならtrueを返す。
//falseを返すときでも*cntには0を入れる
bool USBCDC_getData(uint8_t ch, uint8_t **buf, uint8_t *cnt)
{
*buf = NULL;
*cnt = 0;
CH_CHECK(ch, false);
ch--;
if(appData.appCOMPortObjects[ch].isReadComplete == true){
if(ch == 0) *buf = com1ReadBuffer;
else *buf = com2ReadBuffer;
USB_DEVICE_IRP * irp = (USB_DEVICE_IRP *)
appData.appCOMPortObjects[ch].readTransferHandle;
*cnt = irp->size;
if(maxPacket < *cnt) maxPacket = *cnt;
return true;
}else
return false;
}
//受信データの処理が終わったことを通信部に知らせる。
//以後、次の受信動作を開始
bool USBCDC_finishGetData(uint8_t ch)
{
CH_CHECK(ch, false);
ch--;
uint8_t *buf = (ch==0) ? com1ReadBuffer : com2ReadBuffer;
appData.appCOMPortObjects[ch].isReadComplete = false;
USB_DEVICE_CDC_Read(ch,
&appData.appCOMPortObjects[ch].readTransferHandle,
buf, APP_READ_BUFFER_SIZE);
return true;
}
//送信中・USBのconfig中などで送信不可の時にはNULLを返す
bool USBCDC_getPutBuf(uint8_t ch, uint8_t **buf, uint8_t *bufSize)
{
*buf = NULL;
*bufSize = 0;
CH_CHECK(ch, false);
ch--;
if(appData.appCOMPortObjects[ch].isWriteComplete == true){
if(ch == 0){*buf = com1WriteBuffer; *bufSize = sizeof(com1WriteBuffer);}
else{ *buf = com2WriteBuffer; *bufSize = sizeof(com2WriteBuffer);}
}else
; //不要
return true;
}
//送信処理。cntは必ずUSBCDC_getPutBuf()のbufSize以下
//bufはUSBCDC_getPutBuf()の返り値で無くてもよい
bool USBCDC_putData(uint8_t ch, uint8_t *buf, uint8_t cnt)
{
CH_CHECK(ch, false);
ch--;
if(appData.appCOMPortObjects[ch].isWriteComplete == true){
appData.appCOMPortObjects[ch].isWriteComplete = false;
USB_DEVICE_CDC_Write(ch,
&appData.appCOMPortObjects[ch].writeTransferHandle, buf, cnt,
USB_DEVICE_CDC_TRANSFER_FLAGS_DATA_COMPLETE);
return true;
}
return false;
}
void USBCDC_CB(void)
{
USB_DEVICE_Tasks_ISR(sysObj.usbDevObject0);
}
/*******************************************************************************
End of File
*/
フラグの変更では、isWriteCompleteの初期値をfalseからtrueに変更します。これはこのフラグが書き込み動作を行っていないので、バッファにデータを書き込んでよいという意味のため、この方が自然です。
USARTの読み出しと書き込みでは、送受信用バッファのアドレスと受信した文字数または一度に送信可能な文字数を関数の引数部分に返します。関数の戻り値としては処理が成功したかどうかを返します。
以上のヘッダーファイルlib_usb_cdc_dual.hとCソースlib_usb_cdc_dual.cをプロジェクトに追加し、逆にsystem_init.c,
system_tasks.cをプロジェクトから削除します。
最後にmain関数を変更します。
main.c
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <xc.h>
#include "pic32def.h" //ここではEI()マクロのみ定義しています
#include "lib_usb_cdc_dual.h"
/*** DEVCFG0 ***/
#pragma config DEBUG = ON
#pragma config ICESEL = ICS_PGx2
#pragma config PWP = 0xff
#pragma config BWP = OFF
#pragma config CP = OFF
/*** DEVCFG1 ***/
#pragma config FNOSC = PRIPLL
#pragma config FSOSCEN = OFF
#pragma config IESO = OFF
#pragma config POSCMOD = HS
#pragma config OSCIOFNC = OFF
#pragma config FPBDIV = DIV_1
#pragma config FCKSM = CSDCMD
#pragma config WDTPS = PS1048576
#pragma config FWDTEN = OFF
/*** DEVCFG2 ***/
#pragma config FPLLIDIV = DIV_4
#pragma config FPLLMUL = MUL_20
#pragma config FPLLODIV = DIV_1
#pragma config UPLLIDIV = DIV_4
#pragma config UPLLEN = ON
/*** DEVCFG3 ***/
#pragma config USERID = 0xffff
#pragma config FSRSSEL = PRIORITY_7
#pragma config FMIIEN = OFF
#pragma config FETHIO = OFF
#pragma config FCANIO = OFF
#pragma config FUSBIDIO = OFF
#pragma config FVBUSONIO = OFF
void init(void)
{
sysPerrformanceConfig(80000000);
INTCONbits.MVEC = 1; //割り込みマルチベクタ
USBCDC_init(1, 1);
}
/* CDC通信を単なるオーム返しにする
*/
void cdc_transLow(uint8_t ch)
{
uint8_t *wrBuf, wrBufSize;
USBCDC_getPutBuf(ch, &wrBuf, &wrBufSize);
if(wrBuf != NULL){
uint8_t *rdBuf, rdCnt, i;
USBCDC_getData(ch, &rdBuf, &rdCnt);
if(rdCnt != 0){
memcpy((void*)wrBuf, (void*)rdBuf, rdCnt);
USBCDC_putData(ch, wrBuf, rdCnt);
USBCDC_finishGetData(ch);
}
}
}
void cdc_trans(void)
{
cdc_transLow(1);
cdc_transLow(2);
}
int main(void)
{
init();
EI();
for(;;){
USBCDC_task();
cdc_trans();
}
return 0;
}
見ての通り、USBのタスク処理とは別にUSARTとして送受信したデータの処理を別の関数として切り出してあります。これで他の用途にも書き換えが簡単です。
これで、2CH分のループバック動作が確認できます。次には、これをライブラリ化するためのコンフィグレーションを作成します。