2016년 5월 31일 화요일

ESP8266 and STM32F4


Original Article : http://blog.naver.com/simonsayz/220724527073


ESP8266  -  The Wrong Solution or Good...
요즘 많이 사용하는 ESP8266을 고속 통신이 필요해서 테스트를 해보았습니다.

인터넷 상에서, 너무 좋은 정보들이 많이 있기는 한데, 개인적으로 정말 필요했던, ESP8266 (ESP12, WROOM02)의 물리적인 속도는 어디까지 나오는가에 대한 정보를 찾을 수가 없었습니다.


그래서 직접 TCP/IP 성능을 직접 확인하기로 하고, 각종 테스트를 진행해보았습니다.


상황 , 전제조건
 - 개인 프로젝트용으로 초당 1MB/s ​속도로 스트리밍이 되어야 함.
 - STM32F4의 통신 모듈로 사용해야 함.
 - 인터넷에 있는 내용은 전체가 그런것은 아니지만, 주객전도된 내용이 많음.
   (16Mhz Atmega가 80,160Mhz ESP8266을 통신 모듈로 사용해서,  배보다 배꼽이 더 큼. 

     Atmega가 나쁘다는 것이 아니고, ESP8266 성능이 상대적으로 너무 뛰어남. 접근성에서 Atmega가 좋은 것은 인정)

주의
  bps = bit / sec ,   Bps = byte /sec


실험 결과
 - AT Command 방식으로는 해결 않됨. (성능을 향상하기 위해서, Firmware 직접 작성 필수)
 - ESP8266ex는 160Mhz 클럭사용해야 함.
 - 전체 시스템 통합을 위해서, PC를 사용할 경우,
 - STM32F4 (168Mhz) 사용할 경우, UART는 10.5Mbps까지 가능 (USART1,6사용시), 나머지 Uart,Usart는 5.25Mbps까지 가능
 - ESP8266 Uart는 9Mbps까지는 가능
    ( PC로 테스트하기 위해서는 PL2303HXD를 사용해야 함, FT232BM 사용시 장비 최고 속도 미만인 관계로, 테스트 불가)
 -  ESP8266 단독 실행시 1.4MB/s 정도까지 전송속도가 보장됨 (잘 작성했을 경우)
 - STM32F4의 경우, USART를 사용해도 연동시 7Mbps정도만 가능 (보드에 따라서 성능차이 발생함)
     Resampling x8 사용시, x16 사용시 5Mbps까지 가능
 - 연동을 위해서는 SPI를 사용해야함.
     SPI 통신의 문제점을 해결하기 위해서, Uart도 사용해야 함.  (Master : ESP8266 / Slave : STM32F4 )
     잘 작성을 한다면, SPI만으로도 가능하지만, 응답을 받는 ESP8266 CPU 낭비를 없애기 위해서

     기본 구성도는 ...​
     PC(Mobile) -> Wifi -> ESP8266 -> SPI -> STM32 (Receive)
                                                           Uart <- span="" stm32=""> (Send)


 - ESP8266 코드 작성시, 프로토콜의 특성상, TCP<->Uart Transparent Bridge 예제와 같이 작성하면 망함.
    (TCP/IP Nagle때문에, 비대칭 송수신 통신일 경우, 패킷을 모아주어야 함, 특히 무선인터넷에서는 잘못 작성할 경우, 10배정도 차이가
     발생할 수도 있음.  응답은 한개의 패킷으로  / 개인 프로젝트에서 비대칭 (PC->Embedded System (패킷多),
     Embedded System->PC(패킷小) )
   

최종 결론 (/w STM32F4)
 - 1MBps가 요즘 장비(예. 단말기 - 휴대폰)에서는 저속이나, Embedded 장비에서는 매우 고속임 (bit로 환산할 경우, 10Mbps)
    (cf. 115200bps ~= 11520Bps =  11.5KBps)
 - 고속 통신을 하려면, Interrupt 방식은 Cortex M4라도 절대 사용하면 않됨
    (모두 DMA방식을 사용해야함)
 - SDIO는 DMA Stream,Channel이 고정되어 있으므로, DMA 매핑을 할때 0순위로 선정되어야 함.
 - DMA 방식으로 적당히 작성시, 100KBps정도까지 가능
 - Optimized State Machine으로 작성할 경우, 500KB까지 가능
    (손발이 오그라질때까지 최적화를 할 경우, 900KB까지 가능할 것으로 예상)



느낌점
 - 하드웨어가 $2이라도, $1000 소프트웨어가 붙어서, $10000 제품이 될 수도 있음.
   정말 공개 소프트웨어 개발자분들을 존경해야 함.
 - 최근 초등학생을 위한 미국에서 진행하는 S/W 캠프도 있다고 듣고, 엄청 놀랐음.
    (어렸을때 좋은 경험은 될수 있겠지만, 금액이 너무 어마 어마 해서... 손발이 곱계 정도로 짜면서 고민해야 이해할 수 있는 부분을

     금전으로 해결하는 것 같아서, 슬퍼졌음)
    ​     
참고. 하드웨어 통신 속도
 // ---------------------------------------------------------------------------
 //                      Speed    Over16      Over8
 //                               ----------- -----------
 //                               42Mhz 84Mhz 42Mhz 84Mhz
 //                               APB1  APB2  APB1  APB2   FPS    Limit
 //  --------------------------------------------------------------------------
 //  115200 x 8 x 1    =   921600  O     O     O     O      3.75
 //  115200 x 8 x 2    =  1843200  O     O     O     O      7.50
 //  115200 x 8 x 2.8  =  2580480  -     O     O     O     10.50
 //                       2625000  -     O     O     O     10.68
 //  115200 x 8 x 3    =  2764800  -     O     O     O     11.25
 //                       3000000  -     O     O     O     12.20   FT232BM
 //  115200 x 8 x 4    =  3686400  -     O     O     O     15.00
 //  115200 x 8 x 5    =  4608000  -     -           O     18.75   ESP8266 80Mhz(DataSheet p19)
 //                       5250000  -     -           O     21.36
 //  115200 x 8 x 6    =  5529600  -     -           O     22.50
 //  115200 x 8 x 7    =  6451200  -     -           O     26.25
 //  115200 x 8 x 8    =  7372800  -     -           O     30.00
 //  ###################  8000000  -     -                         ESP8266 160Mhz
 //  115200 x 8 x 9    =  8294400  -     -           O     33.75
 //  115200 x 8 x 9.55 =  8801280  -     -           O     35.81   LPix V1.3b
 //  115200 x 8 x 10   =  9216000  -     -           O x   37.50
 //                      10000000  -     -           O x   40.69
 //  115200 x 8 x 11   = 10137600  -     -           O x   41.25
 //                      10500000  -     -           O     42.7    STM32F4
 //  115200 x 8 x 12   = 11059200  -     -
 //  115200 x 8 x 13   = 11980800  -     -
 //                      12000000                                  PL2303HXD



 



2015년 12월 26일 토요일

Five shades of gpio (Story of the bare matal) / GPIO 의 다섯개의 그레이

This is the short story related GPIO in STM32F4X , Story of the bare metal

요즘 전세계적으로 하드웨어에 대한 관심(Maker Movement)이 다시 일어나고 있는데, 주변에 물어볼 수 있는 사람이 없어서 (제가 질문한게 그게 아닌데, 당최 무신 소린지 다들 자신이 이해한 정도로만 설명해서)  저는 전공이 삽질이라서, 또 뻘짓을 해보았습니다.


개발 시간이 짧은 상업 프로젝트에서는 FPGA가 정답이라고 생각을 하지만, 똑깥은 뻘짓을 호기심에 하는 개발자들이 있을 것 같아서, 글을 남깁니다. 자고 일어나면 세상이 변하기에, 동일한 시행착오를 하지 않았으면 하는 마음에...


CPU           : STM32F405, STM32F415
Test Board  : MINI-M4 for STM32  ( http://www.mikroe.com/mini/stm32/ )
Compiler     : MikroE Pascal 4.7.0  


 
Fig 1. Test Board
        1. Mini-M4 STM32F4 / Ref. http://www.mikroe.com/mini/stm32/
        2. MaxPaper LPix v1.1 /Ref. http://www.maxpaer.com

 
Fig2. Toggling 84Mhz using GPIO Register ( Fully piped line processing )

 
Fig 3. Toggling 83Mhz using BSRR


 
Fig 4. Toggling 21Mhz using Bit-Band

 
Fig 5. Toggling 19Mhz using DMA (M2M, No Burst Mode, 16bit)

  
Fig 6. Toggling 17Mhz using Pascal



2014년 5월 12일 월요일

Pascal USB Bluk Stack for Stellaris / Cortex-M3

USB 2.0 Stack을 Pascal로 구현해 보았습니다. 예전에 열정을 갖고 작성했었는데, 초보자분들께 도움이 되실까 해서, 공개합니다.
USB 2.0 Stack for Bulk Transfer written pascal. Two years ago, I wrote the USB Driver for Stellaris. Stack is now open source. :)


 
 
//------------------------------------------------------------------------------
//
//  USB Bulk Library for LM3S9B95,LMS9B96
//
//  #1. Environment
//  -------------------------------------------------------------------------
//      Eval Board : MikroMedia for Stellaris (LM3S9B95)
//      Compiler   : MikroPascal PRO for ARM v.2.0.0
//
//  #2. Author
//  -------------------------------------------------------------------------
//      simon,choi / south korea (최원식옹의 스텔라리스 USB 라이브러리)
//      Blog       :
http://blog.naver.com/simonsayz
//      E-Mail     :  simonsayz@naver.com
//
//  #3. History
//  ---------------------------------------------------------------------------
//      2012.03.01 : Start
//      2012.03.02 : Convert USBLib in StellarisWare
//      2012.03.05 : RX FIFO Read
//      2012.03.10 : PLL Setting Bug Fixed
//      2012.03.11 : Rewrite Lib
//      2012.03.12 : Enumeration (EP0)
//      2012.03.14 : Data Xfer   (EP1)
//
//  #4. Maximum Speed Test (Full Speed : 12Mhz)
//  ---------------------------------------------------------------------------
//      19 x 64 x 1000     1216000 [Bytes/s]
//      Example.  /wo Rcv  1066667 [Bytes/s]  88% [64000KB Block]
//                /w  Rcv   565771 [Bytes/s]  47%
//                /w  Rcv  1064007 [Bytes/s]
//                /w  Rcv  1066667 [Bytes/s]
//
//                    Snd   941592 [Bytes/s]
//
//  #5. Enumeration List
//      Env. C:\StellarisWare\boards\dk-lm3s9b96\usb_dev_bulk & XP
//  ---------------------------------------------------------------------------
//                                    Ty Rq Value Index Length
//      PC->Dev (GetDesc, Devic    )  80 06 00 01 00 00 40 00
//      Dev->PC (Answer            )  12 01 10 01 FF 00 00 40 BE 1C 03 00 00 01 01 02 03 01
//      PC->Dev (Set Addr          )  00 05 01 00 00 00 00 00    // ZL Ack by Client
//      PC->Dev (GetDesc, Device   )  80 06 00 01 00 00 12 00    // Dev Desc (Ver,PktSize...)
//      Dev->PC (Answser           )  12 01 10 01 FF 00 00 40 BE 1C 03 00 00 01 01 02 03 01
//      PC->Dev (GetDesc, Config   )  80 06 00 02 00 00 09 00
//      Dev->PC (Config Answer     )  09 02 20 00 01 01 05 C0 FA // Cfg.Desc
//      PC->Dev (GetDesc, String   )  80 06 00 03 00 00 FF 00
//      Dev->PC (Lang: EN          )  04 03 09 04                // Lang Desc.
//      PC->Dev (GetDesc,Str Inx3  )  80 06 03 03 09 04 FF 00    // "12345678"
//      Dev->PC (        Str Inx3  )  12 03 31 00 32 00 33 00 34 00 35 00 36 00 37 00 38 00
//      PC->Dev (GetDesc. Cfg Full )  80 06 00 02 00 00 FF 00    // Cfg. All
//      Dev->PC (Config Answer     )  09 02 20 00 01 01 05 C0 FA // Cfg Desc(Self Pwr,500mA)
//                                    09 04 00 00 02 FF 00 00 04 // I/F Desc (Ep#:2,IF:4)
//                                          07 05 81 02 40 00 00 // Ep  Desc (Ep,81,I,Bulk,64,0)
//                                          07 05 01 02 40 00 00 // Ep  Desc (Ep,01,O,Bulk,64,0)
//      PC->Dev (GetDesc,String    )  80 06 00 03 00 00 FF 00
//      Dev->PC (Lang: EN          )  04 03 09 04
//      PC->Dev (GetDesc,Str Inx2  )  80 06 02 03 09 04 FF 00    // "Genera..."
//      Dev->PC (        Str Inx2  )  28 03 47 00 65 00 6E 00 65 00 72 00 69 00
//      PC->Dev (GetDesc,String    )  80 06 00 03 00 00 FF 00
//      Dev->PC (Lang: EN          )  04 03 09 04
//      PC->Dev (GetDesc,Str Inx2  )  80 06 02 03 09 04 FF 00   // "General..."
//      Dev->PC (        Str Inx2  )  28 03 47 00 65 00 6E 00 65 00 72 00 69 00
//      PC->Dev (GetDesc, Device   )  80 06 00 01 00 00 12 00   //
//      Dev->PC (         Device   )  12 01 10 01 FF 00 00 40 BE 1C 03 00 00 01
//      PC->Dev (GetDesc, Config   )  80 06 00 02 00 00 09 00
//      Dev->PC (         Config   )  09 02 20 00 01 01 05 C0 FA
//      PC->Dev (GetDesc,          )  80 06 00 02 00 00 20 00
//                                    09 02 20 00 01 01 05 C0 FA
//      Dev->PC (Config Answer     )  09 04 00 00 02 FF 00 00 04
//                                          07 05 81 02 40 00 00
//                                          07 05 01 00 40 00 00
//      PC->Dev (Get.Status        )  80 00 00 00 00 00 02 00
//      Dev->PC (Self Power OK     )  01 00
//      PC->Dev (SetConfig         )  00 09 01 00 00 00 00 00 // All Enum Done.
//
//
//  bmRequestType               bRequest
//  ---------------------------------------------------
//  Std    Std       Std
//  Device Interface EndPoint
//  ---------------------------------------------------
//  0x80   0x81      0x82       0 #USBDGetStatus
//  0x00   0x01      0x02       1  USBDClearFeature,
//                              -
//  0x00   0x01      0x02       3  USBDSetFeature,
//                              -
//  0x00                        5 #USBDSetAddress,
//  0x80                        6 #USBDGetDescriptor,
//  0x00                        7  USBDSetDescriptor,
//  0x80                        8  USBDGetConfiguration,
//  0x00                        9 #USBDSetConfiguration,
//         0x81                 A  USBDGetInterface,
//         0x01                 B  USBDSetInterface,
//                   0x82       C  USBDSyncFrame
//
//  bmRequest
//  0 00 0:0000
//  x            : 0 : Host To Device
//                 1 : Device to Host
//    xx         : 00 : Standard
//                 01 : Class
//                 10 : Vendor
//                 11 : Reserved
//  1 01 0:0000
//
//
//
//  Command List
//  ---------------------------------------------------------------------------
//  Cmd_SetMode(    Mode,Address : DWord)    ReadMode    Address
//                                           WriteMode   Addesss
//                                           DialogMode  Address
//                                           Execute -
//              PC->Dev : SetUpPkt : $60 $D8 $0000 $0000 $0040
//              PC->Dev : Data[64] ( Mode4, Address4 )
//
//  Cmd_GetMode(Var Mode,Address : DWord);
//              PC->Dev : SetupPkt : $E0 $D8 $0000 $0000 $0040
//              Dev->PC : Data[64]
//
//  Pin Layout
//      USBDM       : USB0DM       p70
//      USBDP       : USB0DP       p71
//      USB-DET     : USB0VBUS PB1 p67
//      USB-ID      : USB0ID   PB0 p66
//
unit Drv_USB;
Uses
 Drv_PasBase,Drv_LM3S9B95,Drv_TFT,Drv_SD;
Const
 //
 _cUSB_Polling   = $1234;
 _cUSB_Interrupt = $2345;
 // TUSBStatus
 _tusNone        = $0000;
 _tusReset       = $0001;
 _tusSuspend     = $0002;
 _tusResume      = $0003;
 _tusDisconnect  = $0004;
 _tusSOF         = $0005;
 _tusReady       = $0006;
 // TUSBPktPhase
 _pSetup         = $0001;
 _pData          = $0002;
 // TUSBModeState
 _tmsNone        = $00;
 _tmsXferStart   = $01;
 _tmsStop        = $02;
 _tmsPictShow    = $03;
 _tmsMovShow     = $04;
 _tmsSDTest      = $05;
 //
 _CmdReqGet      = $80 or $60; // bmRequestType  Get+UserFunc
 _CmdReqSet      = $00 or $60; // bmRequestType  Set+UserFunc
 _CmdReqFunc     = $D8; //        bRequest
 _DirIn          = $01; // PC <- dev="" nbsp="" span="">
 _DirOut         = $02; // PC  -> Dev
 _DirInOut       = $03; // PC <-> Dev
                       //             RT Req Val  Inx  Len   Data
 _CmdNone        = $00;
 _CmdSetStatus   = $01; // PC  -> Dev  60 D8 0001 0000 0040 : dir addr cnt
 _CmdGetStatus   = $02; // PC <- 0000="" 0002="" :="" addr="" cnt="" d8="" dev="" dir="" e0="" nbsp="" span="">
 _CmdSetAction   = $03; // PC  -> Dev  80 D8 0003 0000 0040 : modeState
//
Type
 TUSBOperation  = DWord;
 Pointer        = ^Byte;
 TUSBBufDW      = Array[16] of DWord;
 pUSBBufDW      = ^TUSBBufDW;
 TUSBBuf        = Array[64] of Char;
 TUSBStatus     = DWord;
 TUSBPktPhase   = DWord;
 TUSBState      = DWord;
 TUSBModeState  = DWord;
 TUSB           = Record
                   Operation  : TUSBOperation;
                   Ready      : Boolean;
                   Status     : TUSBStatus;
                   PktPhase   : TUSBPktPhase;
                   CtlReqType : DWord;
                   CtlReq     : DWord;
                   CtlValue   : DWord;
                   CtlLength  : DWord;
                   ModeDir    : DWord;         // Internal Operation
                   ModeAddr   : DWord;
                   ModeCnt    : DWord;
                   ModeState  : TUSBModeState;
                   Addr       : Dword;
                   AddrSet    : Boolean;
                   BufR       : TUSBBuf;
                   BufW       : TUSBBuf;
                  End;
             
 Var
  _gUSB : TUSB;
              
//
Procedure USB_Init(Operation : TUSBOperation);
Procedure USB_Handler;
//
Function  USB_PutSD (Var USB : TUSB) : Boolean;
Function  USB_GetSD (Var USB : TUSB) : Boolean;
Implementation
Const
  //
  cDescDev = 1;   // Device    Descriptor
  cDescCfg = 2;   // Config    Descriptor
  cDescStr = 3;   // String    Descriptor
  cDescIF  = 4;   // Interface Descriptor
  cDescEP  = 5;   // EndPoint  Descriptor
  //
  cEpBulk  = 1;   // Ref. EndPoint Descriptor [In,Out];
  // Device Description -------------------------------------------------------
  cDevDesc : Array[18] of Byte =
              ($12,      // bLength             [Always]
               $01,      // bDescriptorType     [Device Description:$01]
               $10,$01,  // bcdUSB              [USB Version] // $10,$01  $00,$02
               $FF,      // DeviceClass         [Vendor Specific]
               $00,      // bDeviceSubClass     [User Defined]
               $00,      // bDeviceProtocol     [User Defined]
                64,      // bMaxPacketSize      [8,16,32,64]
               $BE,$1C,  // idVendor            [*Stellaris]
               $03,$00,  // idProduct           [*User Define]
               $00,$01,  // bcdDevice           [*Prod Version]
               $01,      // iManufacturer       [Manufacturer Str Inx]
               $02,      // iProduct            [Product Str Inx]
               $03,      // iSerialNumber       [Serial Number Inx]
               $01);     // bNumConfigurations  [# of Config]
  // Config Description -------------------------------------------------------
  cCfgDesc : Array[32] of Byte = (
               // Configuration Descriptor ------------------------------------
               $09,           // bLength             [Always $09]
               $02,           // bDescriptorType     [Configuration Description:$02]
               $20,$00,       // wTotalLength        [Total Size = Cfg + Sub ...]
               $01,           // bNumInterfaces      [Interface #]
               $01,           // bConfigurationValue [1]
               $05,           // iConfiguration      [Config Str Inx]
               $C0,           // bmAttributes        [11.. = Reserved, Self Powered]
               $FA,           // bMaxPower           [$FA x 2mA = 500 mA ]
               // Inteface Descriptor -----------------------------------------
               $09,           // bLength             [Always $09]
               $04,           // bDescriptorType     [Interface Description:$04]
               $00,           // bInterfaceNumber    [Interface ID]
               $00,           // bAlternateSetting   [Alternated Service #]
               $02,           // bNumEndpoint        [2 EP Point(1,81)]
               $FF,           // bInterfaceClass     [*User Define]
               $00,           // bInterfaceSubclass  [Subclass code]
               $00,           // bInterfaceProtocol  [Protocol code]
               $04,           // iInterface          [Interface Str Inx]
               // EndPoint Descriptor (IN) ------------------------------------
               $07,           // bLength             [Always $07]
               $05,           // bDescriptorType     [EndPoint : $05]
               $80 or cEpBulk,// bEndpointAddress    [Addr : 81] <------------------ span="">
               $02,           // bmAttributes        [Bulk]
               $40,$00,       // wMaxPacketSize      [Pkt 64bytes, Ref. Device Desc]
               $00,           // bInterval           [Svc Interval , NAK rate]
               // EndPoint Descriptor (OUT) -----------------------------------
               $07,           // bLength             [Always $07]
               $05,           // bDescriptorType     [EndPoint : $05]
               $00 or cEpBulk,// bEndpointAddress    [Addr : 01] <------------------ span="">
               $02,           // bmAttributes        [Bulk]
               $40,$00,       // wMaxPacketSize      [Pkt 64bytes, Ref. Device Desc]
               $00);          // bInterval           [Svc Interval , NAK rate]
  // String Description -------------------------------------------------------
  cStrLang   : Array[2] of byte = ($09,$04); // $0409 US
  cStrDescs  : Array[5] of String[64] =
               ('Texas Instruments',         // Str Inx 1, Manu   Dev Desc
                'Generic Bulk Device',       // Str Inx 2, Prod   Dev Desc
                '12345678',                  // Str Inx 3, Serial Dev Desc
                'Bulk Data Interface',       // Str Inx 4, I/F    I/F Desc
                'Bulk Data Configuration');  // Str Inx 5, Cfg    Cfg Desc
//------------------------------------------------------------------------------
//
// H/W Driver Function
//
//------------------------------------------------------------------------------
Const
  _EP0_MAX_PACKET_SIZE           = 64;
  _USB_DEV_EP0_IN_PKTPEND        = $00000002; // Transmit data packet pending
  _USB_DEV_EP0_OUT_PKTRDY        = $00000001; // Receive data packet ready
  _USB_DEV_EP0_SENT_STALL        = $00000004; // Stall was sent on this endpoint
  _USB_DEV_EP0_SETUP_END         = $00000010; // Control transaction ended before
  _USB_DEV_RX_DATA_ERROR         = $00080000; // CRC error on the data
  _USB_DEV_RX_FIFO_FULL          = $00020000; // RX FIFO full
  _USB_DEV_RX_OVERRUN            = $00040000; // OUT packet was not loaded due to
  _USB_DEV_RX_PKT_RDY            = $00010000; // Data packet ready
  _USB_DEV_RX_SENT_STALL         = $00400000; // Stall was sent on this endpoint
  _USB_DEV_TX_SENT_STALL         = $00000020; // Stall was sent on this endpoint
  _USB_DEV_TX_UNDERRUN           = $00000004; // IN received with no data ready
  _USB_EP_0                      = $00000000; // Endpoint 0
  _USB_EP_1                      = $00000010; // Endpoint 1
  _USB_EP_AUTO_CLEAR             = $00000004; // Auto clear feature enabled
  _USB_EP_AUTO_SET               = $00000001; // Auto set feature enabled
  _USB_EP_DEV_IN                 = $00002000; // Device IN endpoint
  _USB_EP_DEV_OUT                = $00000000; // Device OUT endpoint
  _USB_EP_DMA_MODE_0             = $00000008; // Enable DMA access using mode 0
  _USB_EP_DMA_MODE_1             = $00000010; // Enable DMA access using mode 1
  _USB_EP_HOST_OUT               = $00002000; // Host OUT endpoint
  _USB_EP_MODE_BULK              = $00000100; // Bulk endpoint
  _USB_EP_MODE_INT               = $00000200; // Interrupt endpoint
  _USB_EP_MODE_ISOC              = $00000000; // Isochronous endpoint
  _USB_EP_MODE_MASK              = $00000300; // Mode Mask
  _USB_FIFO_SZ_64                = $00000003; // 64 byte FIFO
  _USB_INTCTRL_ALL               = $000003FF; // All control interrupt sources
  _USB_INTCTRL_DISCONNECT        = $00000020; // Disconnect Detected
  _USB_INTCTRL_MODE_DETECT       = $00000200; // Mode value valid
  _USB_INTCTRL_POWER_FAULT       = $00000100; // Power Fault detected
  _USB_INTCTRL_RESET             = $00000004; // Reset signaled
  _USB_INTCTRL_RESUME            = $00000002; // Resume detected
  _USB_INTCTRL_SOF               = $00000008; // Start of Frame Detected
  _USB_INTCTRL_STATUS            = $000000FF; // Status Interrupts
  _USB_INTCTRL_SUSPEND           = $00000001; // Suspend detected
  _USB_INTEP_0                   = $00000001; // Endpoint 0 Interrupt
  _USB_INTEP_ALL                 = $FFFFFFFF; // Host IN Interrupts
  _USB_INTEP_DEV_IN              = $0000FFFE; // Device IN Interrupts
  _USB_INTEP_DEV_OUT             = $FFFE0000; // Device OUT Interrupts
  _USB_INTEP_HOST_IN             = $FFFE0000; // Host IN Interrupts
  _USB_INTEP_HOST_OUT            = $0000FFFE; // Host OUT Interrupts
  _USB_INTEP_RX_SHIFT            = 16; // usb.c
  _USB_O_FADDR                   = $00000000; // USB Device Functional Address
  _USB_RX_EPSTATUS_SHIFT         = 16;
  _USB_STATE_IDLE                =  0;        // usbdevicepriv.h   TEP0State
  _USB_STATE_RX                  =  3;
  _USB_STATE_STALL               =  5;
  _USB_STATE_STATUS              =  4;
  _USB_STATE_TX                  =  1;
  _USB_STATE_TX_CONFIG           =  2;
  _USB_TRANS_IN                  = $00000102; // Normal IN transaction
  _USB_TRANS_IN_LAST             = $0000010a; // Final IN transaction (for endpoint 0 in device mode)
  _USB_TXCSRH1_DMAEN             = $00000010; // DMA Request Enable
  _USB_TXCSRH1_DMAMOD            = $00000004; // DMA Request Mode
  _USB_RTYPE_TYPE_M              = $60;
  // Direct Access [Fast Act]
  _USB0_BASE_TXIS                = $40050002;
  _USB0_BASE_FIFO0               = $40050020;  // USB FIFO Endpoint 0
  _USB0_BASE_CSRL0               = $40050102;  // USB Control and Status Endpoint 0 Low
  _USB0_BASE_COUNT0              = $40050108;  // USB Receive Byte Count Endpoint
  _USB0_BASE_TXCSRL1             = $40050112;
  _USB0_BASE_RXCSRL1             = $40050116;
  //
  _urGetStat                     =   0;
  _urSetAddr                     =   5;
  _urGetDesc                     =   6;
  _urSetConf                     =   9;
  //
  _cStrDesc                      =   3;
  _udHost2Dev                    = $00;
  _udDev2Host                    = $80;
 
Type
  TUSBRequest        = Record // usblib.h
                        bmRequestType : Byte; // 80
                        bRequest      : Byte; // 00
                        wValue        : Word; // 80
                        wIndex        : Word; // 80
                        wLength       : Word; // 80
                       End;
  pUSBRequest        = ^TUSBRequest;
  TStrDesc           = Record
                        Len           : Byte;
                        StrDesc       : Byte;
                        Dat           : Array[64] of Byte;
                       End;
  pStrDesc           = ^TStrDesc;
Var
 _lStrDesc : TStrDesc;
//-----------------------------------------------------------------------------
//
//  Original Code : C:\StellarisWare\driverlib\usb.c
//
//   Function  USB_EP_TO_Index(x : DWord) : DWord;//
//   Function  EP_OFFSET(Endpoint : DWord) : DWord;
//   Procedure USBIndexWrite(ulBase, ulEndpoint,ulIndexedReg,ulValue,ulSize : DWord);
//   Procedure USBIntEnableControl(ulBase,ulFlags : DWord);
//   Function  USBIntStatusControl(ulBase : DWord) : DWord;
//   Procedure USBIntEnableEndpoint(ulBase,ulFlags : DWord);
//   Function  USBIntStatusEndpoint(ulBase : DWord) : DWord;
//   Function  USBEndpointStatus(ulBase, ulEndpoint : DWord) : DWord;
//   Procedure USBDevEndpointStatusClear(ulBase,ulEndpoint,ulFlags : DWord);
//   Procedure USBDevConnect( ulBase : DWord);
//   Procedure USBDevDisconnect(ulBase : DWord);
//   Procedure USBDevAddrSet(ulBase, ulAddress : DWord);
//   Procedure USBDevEndpointConfigSet(ulBase,ulEndpoint,ulMaxPacketSize,ulFlags : DWord);
//   Procedure USBFIFOConfigSet(ulBase,ulEndpoint,ulFIFOAddress,ulFIFOSize,ulFlags : DWord);
//   Function  USBEndpointDataAvail(ulBase,ulEndpoint : DWord) : DWord;
//   Function  USBEndpointDataGet(ulBase, ulEndpoint : DWord; pucData : Pointer; Var pulSize : DWord) : LongInt;
//   Procedure USBDevEndpointDataAck(ulBase, ulEndpoint : DWord; bIsLastPacket : Boolean);
//   Function  USBEndpointDataPut(ulBase, ulEndpoint : DWord; pucData  : Pointer; ulSize : DWord) : LongInt;
//   Function  USBEndpointDataSend(ulBase, ulEndpoint,ulTransType : DWord) : LongInt;
//   Procedure USBOTGMode(ulBase : DWord);
//
//-----------------------------------------------------------------------------
// Utility 함수
// usb.c
// Function EP_OFFSET(Endpoint : DWord) : DWord;
//
Procedure USBIndexWrite(ulBase, ulEndpoint,ulIndexedReg,ulValue,ulSize : DWord);
 Var
  ulIndex : DWord;
 Begin
  // Save the old index in case it was in use.
  HWRegBR(ulBase + _USB_O_EPIDX,ulIndex);
  // Set the index.
  HWRegBW(ulBase + _USB_O_EPIDX,ulEndpoint);
  // Determine the size of the register value.
  Case (ulSize = 1) of
   True  : HWRegBW(ulBase + ulIndexedReg,ulValue);
   False : HWRegHW(ulBase + ulIndexedReg,ulValue);
  End;
  // Restore the old index in case it was in use.
  HWRegBW(ulBase + _USB_O_EPIDX, ulIndex);
 End;
// usb.c
Procedure USBIntEnableControl(ulBase,ulFlags : DWord);
 Var
  ulVal : DWord;
 begin
  If(ulFlags and _USB_INTCTRL_STATUS) then
   begin
    HWRegBR(ulBase + _USB_O_IE,ulVal);
    HWRegBW(ulBase + _USB_O_IE,ulVal or ulFlags);
   end;
  // Enable the power fault interrupt.
  If(ulFlags and _USB_INTCTRL_POWER_FAULT) then
   HWRegW(ulBase + _USB_O_EPCIM,USB_EPCIM_PF);
  // Enable the ID pin detect interrupt.
  If(ulFlags and _USB_INTCTRL_MODE_DETECT) then
   HWRegW(_USB0_BASE + _USB_O_IDVIM,USB_IDVIM_ID);
 end;
// usb.c
Function USBIntStatusControl(ulBase : DWord) : DWord;
 Var
  ulStatus      : DWord;
  _USB0_IS      : Byte ; absolute 0x4005000A; volatile; sfr;
  _USB0_EPCISC  : Dword; absolute 0x4005040C; volatile; sfr;
  _USB0_IDVISC  : Dword; absolute 0x4005044C; volatile; sfr;
 begin
  ulStatus := _USB0_IS;
  If(_USB0_EPCISC and _USB_EPCISC_PF) then
   begin
    ulStatus := ulStatus or _USB_INTCTRL_POWER_FAULT;
    _USB0_EPCISC := _USB0_EPCISC or _USB_EPCISC_PF; // Clear power fault int
   end;
  If(_USB0_IDVISC and _USB_IDVRIS_ID) then
   begin
    ulStatus := ulStatus or _USB_INTCTRL_MODE_DETECT;
    _USB0_IDVISC := _USB0_IDVISC or _USB_IDVRIS_ID; // Clear id detection int
   end;
  Result := ulStatus;
 end;
// usb.
Procedure USBIntEnableEndpoint(ulBase,ulFlags : DWord);
 begin
  // Enable any transmit endpoint interrupts.
  HWRegHW(ulBase + _USB_O_TXIE, HWRegHR(ulBase + _USB_O_TXIE) or
          ulFlags and ( _USB_INTEP_HOST_OUT or _USB_INTEP_DEV_IN or _USB_INTEP_0));
  // Enable any receive endpoint interrupts.
  HWRegHW(ulBase + _USB_O_RXIE, HWRegHR(ulBase + _USB_O_RXIE) or
        ((ulFlags and (_USB_INTEP_HOST_IN or _USB_INTEP_DEV_OUT)) shr
         _USB_INTEP_RX_SHIFT));
 end;
 
// usb.c , Pascal Compiler Bug -> hand coding SIMON
Function USBIntStatusEndpoint(ulBase : DWord) : DWord;
 Var
  TXIS : ^DWord;
 begin
  TXIS   := _USB0_BASE_TXIS;
  Result := TXIS^;  // TXIS[16] + RXIS[16]
 end;
// usb.c/ Device Mode Result
Function USBEndpointStatus(ulBase, ulEndpoint : DWord) : DWord;
 Var
  TXCSRL : ^DWord;
  RXCSRL : ^DWord;
 begin
  //
  TXCSRL := _USB0_BASE_TXCSRL1 + (ulEndPoint-$10);
  RXCSRL := _USB0_BASE_RXCSRL1 + (ulEndPoint-$10);
  //
  Result := (TXCSRL^ and $0000FFFF) or
            (RXCSRL^ shl 16)
 end;
 
// usb.c
Procedure USBDevEndpointStatusClear(ulBase,ulEndpoint,ulFlags : DWord);
 Var
  ulVal : DWord;
 begin
  // If this is endpoint 0, then the bits have different meaning and map
  // into the TX memory location.
  Case (ulEndpoint = _USB_EP_0) of
   True : begin // EP0
           HWRegBR( _USB0_Base_CSRL0, ulVal);
           // Set the Serviced RxPktRdy bit to clear the RxPktRdy.
           if(ulFlags and _USB_DEV_EP0_OUT_PKTRDY) then
            HWRegBW(_USB0_Base_CSRL0, ulVal or _USB_CSRL0_RXRDYC);
           // Set the serviced Setup End bit to clear the SetupEnd status.
           if(ulFlags and _USB_DEV_EP0_SETUP_END) then
            HWRegBW(_USB0_Base_CSRL0, ulVal or _USB_CSRL0_SETENDC);
           // Clear the Sent Stall status flag.
           if(ulFlags and _USB_DEV_EP0_SENT_STALL) then
            HWRegBW(_USB0_Base_CSRL0, ulVal and Not(_USB_DEV_EP0_SENT_STALL));
          end;
  False : begin // EP1 ~
           ulEndpoint := ulEndpoint - $10;
           HWRegBR(_USB0_Base_TXCSRL1 + ulEndpoint,ulVal);
           HWRegBW(_USB0_Base_TXCSRL1 + ulEndpoint,
                   ulVal and Not(ulFlags and $00000020)); // _USB_DEV_TX_SENT_STALL or _USB_DEV_TX_UNDERRUN
           HWRegBR(_USB0_Base_RXCSRL1 + ulEndpoint,ulVal);
           HWRegBW(_USB0_Base_RXCSRL1 + ulEndpoint,
                   ulVal and Not((ulFlags and $004C0000) shr 16));
          //  Not((ulFlags and (_USB_DEV_RX_SENT_STALL or _USB_DEV_RX_DATA_ERROR or
          //                      _USB_DEV_RX_OVERRUN)) shr _USB_RX_EPSTATUS_SHIFT));
          end;
  end;
 end;
// usb.c
Procedure USBDevConnect( ulBase : DWord);
 Var
  ulVal : DWord;
 begin
  // Enable connection to the USB bus.
  HWRegBR(ulBase + _USB_O_POWER,ulVal);
  HWRegBW(ulBase + _USB_O_POWER,ulVal or _USB_POWER_SOFTCONN);
 end;
// usb.c
Procedure USBDevDisconnect(ulBase : DWord);
 Var
  ulVal : DWord;
 begin
  // Disable connection to the USB bus.
  HWRegBR(ulBase + _USB_O_POWER,ulVal);
  HWRegBW(ulBase + _USB_O_POWER,ulVal and Not(_USB_POWER_SOFTCONN));
 end;
// usb.c
Procedure USBDevAddrSet(ulBase, ulAddress : DWord);
 begin
  // Set the function address in the correct location.
  HWRegBW(ulBase + _USB_O_FADDR, byte(ulAddress));
 end;
// usb.c
Procedure USBDevEndpointConfigSet(ulBase,ulEndpoint,ulMaxPacketSize,ulFlags : DWord);
 Var
  ulRegister : DWord;
 begin
  Case (ulFlags and _USB_EP_DEV_IN) of
   True : begin
           // Set the maximum packet size.
           HWRegHW(ulBase + (ulEndpoint- $10) + _USB_O_TXMAXP1, ulMaxPacketSize);
           ulRegister := 0;
           If (ulFlags and _USB_EP_AUTO_SET) then
            ulRegister := ulRegister or _USB_TXCSRH1_AUTOSET;
           // Configure the DMA mode.
           Case (ulFlags and _USB_EP_DMA_MODE_1) of
            True : ulRegister := ulRegister or _USB_TXCSRH1_DMAEN or _USB_TXCSRH1_DMAMOD;
            False: If(ulFlags and _USB_EP_DMA_MODE_0) then
                    ulRegister := ulRegister or USB_TXCSRH1_DMAEN;
           End;
          // Enable isochronous mode if requested.
          if((ulFlags and _USB_EP_MODE_MASK) = _USB_EP_MODE_ISOC) then
           ulRegister := ulRegister or USB_TXCSRH1_ISO;
          // Write the transmit control value.
          HWRegBW(ulBase + (ulEndpoint-$10) + _USB_O_TXCSRH1,ulRegister);
          // Reset the Data toggle to zero.
          HWRegBW(ulBase + (ulEndpoint-$10) + _USB_O_TXCSRL1,_USB_TXCSRL1_CLRDT);
         end;
   False:begin
          // Set the MaxPacketSize.
          HWRegHW(ulBase + (ulEndpoint-$10) + _USB_O_RXMAXP1,ulMaxPacketSize);
          ulRegister := 0;
          if(ulFlags and _USB_EP_AUTO_CLEAR) then
            ulRegister := _USB_RXCSRH1_AUTOCL;
          // Configure the DMA mode.
          Case (ulFlags and _USB_EP_DMA_MODE_1) of
           True : ulRegister := ulRegister or _USB_RXCSRH1_DMAEN or _USB_RXCSRH1_DMAMOD;
           False: If (ulFlags and _USB_EP_DMA_MODE_0) then
                   ulRegister := ulRegister or _USB_RXCSRH1_DMAEN;
          End;
          // Enable isochronous mode if requested.
          if((ulFlags and _USB_EP_MODE_MASK) = _USB_EP_MODE_ISOC) then
           ulRegister := ulRegister or _USB_RXCSRH1_ISO;
          // Write the receive control value.
          HWRegBW(ulBase + (ulEndpoint-$10) + _USB_O_RXCSRH1,ulRegister);
          // Reset the Data toggle to zero.
          HWRegBW(ulBase + (ulEndpoint-$10) + _USB_O_RXCSRL1,_USB_RXCSRL1_CLRDT);
         end;
  end;
 end;
// usb.c
Procedure USBFIFOConfigSet(ulBase,ulEndpoint,ulFIFOAddress,ulFIFOSize,ulFlags : DWord);
 begin
  // See if the transmit or receive FIFO is being configured.
  Case (ulFlags and (_USB_EP_HOST_OUT or _USB_EP_DEV_IN)) of
   True : // Set the transmit FIFO location and size for this endpoint.
          begin
           USBIndexWrite(ulBase, ulEndpoint shr 4, _USB_O_TXFIFOSZ, ulFIFOSize, 1);
           USBIndexWrite(ulBase, ulEndpoint shr 4, _USB_O_TXFIFOADD,ulFIFOAddress shr 3, 2);
          end;
   False: // Set the receive FIFO location and size for this endpoint.
          begin
           USBIndexWrite(ulBase, ulEndpoint shr 4, _USB_O_RXFIFOSZ, ulFIFOSize, 1);
           USBIndexWrite(ulBase, ulEndpoint shr 4, _USB_O_RXFIFOADD,ulFIFOAddress shr 3, 2);
          end;
  end;
 end;
// usb.c
Function USBEndpointDataAvail(ulBase,ulEndpoint : DWord) : DWord;
 Var
  ulRegister : DWord;
  ulFIFO     : ^DWord;
 begin
  // Get the address of the receive status register to use, based on the endpoint.
  Case (ulEndpoint = _USB_EP_0) of
   True  : ulRegister := _USB0_BASE_CSRL0;
   False : ulRegister := _USB0_BASE_RXCSRL1 + (ulEndpoint-$10);
  End;
  // Is there a packet ready in the FIFO?
  ulFIFO := ulRegister;
  If (ulFIFO^ and _USB_CSRL0_RXRDY) = 0 then
   begin
    Result := 0;
    Exit;
   end;
  // Return the byte count in the FIFO.
  ulFIFO := _USB0_BASE_COUNT0 + ulEndpoint;
  Result := ulFIFO^;
 end;
//
Procedure USBDevEndpointDataAck(ulBase, ulEndpoint : DWord; bIsLastPacket : Boolean);
 Var
  ulVal : DWord;
 begin
  // Determine which endpoint is being acked.
  Case (ulEndpoint = _USB_EP_0) of
   True : // Clear RxPktRdy, and optionally DataEnd, on endpoint zero.
          Case bIsLastPacket of
           True : HWRegBW(ulBase + _USB_O_CSRL0,_USB_CSRL0_RXRDYC or _USB_CSRL0_DataEnd);
           False: HWRegBW(ulBase + _USB_O_CSRL0,_USB_CSRL0_RXRDYC                      );
          End;
   False: // Clear RxPktRdy on all other endpoints.
          Begin
           HWRegBR(ulBase + _USB_O_RXCSRL1 + (ulEndpoint-$10),ulVal);
           HWRegBW(ulBase + _USB_O_RXCSRL1 + (ulEndpoint-$10),
                   ulVal and Not(Dword(_USB_RXCSRL1_RXRDY)));
          End;
  End;
 End;
 
//                   R0             R1                          R2
Procedure USBGetData(ulFIFO :DWord; Var Buf : Array[1] of Byte; Lp : DWord);
 begin
  asm
   Loop:
   LDR  R3,[R0,#0];  // Load Data
   STR  R3,[R1,#0];  // Save Data to Buf
   SUBS R2, R2, #4;  // Dec Lp
   ADD  R1, R1, #4;  // Pointer Move +1
   BNE  Loop;        //
  end;
 end;
// 추후 Word단위 수정 요망
//                   R0             R1                          R2
Procedure USBPutData(ulFIFO :DWord; Var Buf : Array[1] of Byte; Lp : DWord);
 begin
  asm
   Loop:
   LDRB  R3,[R1,#0];  // Save Data to Buf
   STRB  R3,[R0,#0];  // Load Data
   SUBS R2, R2, #1;  // Dec Lp
   ADD  R1, R1, #1;  // Pointer Move +1
   BNE  Loop;        //
  end;
 end;
 
// usb.c
Function USBEndpointDataGet (ulBase, ulEndpoint : DWord;
                             pucData : Pointer; Var pulSize : DWord) : LongInt;
 Var
  ulRegister  : DWord;
  ulByteCount : DWord;
  ulFIFO      : DWord;
  ulVal       : ^DWord;
  Bv          : Byte;
  Lp          : Byte;
 begin
  // Get the address of the receive status register to use, based on the endpoint.
  Case (ulEndpoint = _USB_EP_0) of
   True  : ulRegister := _USB0_BASE_CSRL0;
   False : ulRegister := _USB0_BASE_RXCSRL1 + (ulEndpoint-$10);
  End;
  // Don't allow reading of data if the RxPktRdy bit is not set.
  ulVal := ulRegister;
  If (ulVal^ and _USB_CSRL0_RXRDY) = 0 then
   begin
    pulSize :=  0;
    Result  := -1;
    Exit;
   end;
  // Get the byte count in the FIFO.
  ulVal := _USB0_BASE_COUNT0 + ulEndPoint;
  ulByteCount := ulVal^;
  If  (ulByteCount >= pulSize) then
   ulByteCount := pulSize;
  // Return the number of bytes we are going to read.
  pulSize := ulByteCount;
  // Calculate the FIFO address.
  ulFIFO := _USB0_BASE_FIFO0 + (ulEndpoint shr 2);
  //
 {
  Lp := 0;
  While (ulByteCount > 0) do
   begin
    HWRegBR(ulFIFO,Bv);
    _gUSB.BufR[lp] := Bv;
    Inc(lp);
    Dec(ulByteCount);
   end;
  }
  //USBGetData(ulFIFO,_gUSB.BufR,ulByteCount);
  USBGetData(ulFIFO,pucData,ulByteCount);
  // Success.
  Result := 0;
 end;
// usb.c
Function USBEndpointDataPut( ulBase, ulEndpoint : DWord;
                             pucData  : Pointer; ulSize : DWord) : LongInt;
 Var
  ulFIFO     : DWord;
  ucTxPktRdy : Byte;
  ulVal      : DWord;
 begin
  // Get the bit position of TxPktRdy based on the endpoint.
  Case (ulEndpoint = _USB_EP_0) of
   True  : ucTxPktRdy := _USB_CSRL0_TXRDY;
   False : ucTxPktRdy := _USB_TXCSRL1_TXRDY;
  End;
  // Don't allow transmit of data if the TxPktRdy bit is already set.
  HWRegBR(ulBase + _USB_O_CSRL0 + ulEndpoint,ulVal);
  If (ulVal and ucTxPktRdy) then
   begin
    Result := -1;
    Exit;
   end;
  // Calculate the FIFO address.
  ulFIFO := ulBase + _USB_O_FIFO0 + (ulEndpoint shr 2);
  // Write the data to the FIFO.
{
  While (ulSize > 0) do
   begin
    HWRegBW(ulFIFO,pucData^); // SIMON / 최적화 필요
    Inc(pucData);
    Dec(ulSize);
   end;
}
  USBPutData(ulFIFO,pucData,ulSize);
  
  // Success.
  Result := 0;
 end;
//
Function USBEndpointDataSend(ulBase, ulEndpoint,ulTransType : DWord) : LongInt;
 Var
  ulTxPktRdy : DWord;
  ulVal      : DWord;
 begin
  // Get the bit position of TxPktRdy based on the endpoint.
  if(ulEndpoint = _USB_EP_0) then
   ulTxPktRdy := ulTransType and $ff
  else
   ulTxPktRdy := (ulTransType shr 8) and $ff;
  // Don't allow transmit of data if the TxPktRdy bit is already set.
  HWRegBR(ulBase + _USB_O_CSRL0 + ulEndpoint,ulVal);
  If (ulVal and _USB_CSRL0_TXRDY) then
   begin
    Result := -1;
    Exit;
   end;
  // Set TxPktRdy in order to send the data.
  HWRegBW(ulBase + _USB_O_CSRL0 + ulEndpoint, ulTxPktRdy);
  // Success.
  Result := 0;
 end;
// usb.c
Procedure USBOTGMode(ulBase : DWord);
 begin
  // Disable the override of the USB controller mode when running on an OTG device.
  HWRegBW(ulBase + _USB_O_GPCS,0);
 end;
//------------------------------------------------------------------------------
// Utility Function
//------------------------------------------------------------------------------
//
Procedure utGetStrDesc(inx : DWord; Var ugDesc : TStrDesc);
 Var
  uLp  : DWord;
  uLen : DWord;
 Begin
  //
  If Inx > 5 then Inx := 2; // Str Product
  //
  ugDesc.StrDesc := _cStrDesc;
  //
  Case inx of
   0    : Begin
           ugDesc.Len := 4;
           ugDesc.Dat[0] := cStrlang[0];
           ugDesc.Dat[1] := cStrlang[1];
          End
   else   Begin
           Dec(inx); // Set Inx
           uLen := Length(cStrDescs[inx]);
           ugDesc.Len := uLen*2 + 2; // Size + StrDesc + Str
           For uLp := 0 to uLen-1 do
            begin
             ugDesc.Dat[uLp*2+0] := cStrDescs[inx][uLp];
             ugDesc.Dat[uLp*2+1] := 0;
            end;
          End;
  End;
 End;
//------------------------------------------------------------------------------
//
// S/W Driver Function
//
//------------------------------------------------------------------------------
//
Procedure User_Reply(Var USB : TUSB);
 Var
  lRetCode : Integer;
 begin
  USB.BufW[0] := $10;
  USB.BufW[1] := $20;
  lRetcode := USBEndpointDataPut(_USB0_BASE,$10,@USB.BufR,64);
  If lRetcode = -1 then Exit;
  USBEndpointDataSend(_USB0_BASE,$10,_USB_TRANS_In_Last);
 end;
// 단 USB.ModeCnt > 0 이상
Procedure Bulk_DevToPc_Start(Var USB : TUSB);
 Var
  lRetCode : Integer;
 begin
  // 첫 번째 패킷 전송
  lRetcode := USBEndpointDataPut(_USB0_BASE,$10,@USB.BufW,64);
  If lRetcode = -1 then Exit;
  //
  Case (USB.ModeCnt = 1) of
   True  : USBEndpointDataSend(_USB0_BASE,$10,_USB_TRANS_In_Last);
   False : USBEndpointDataSend(_USB0_BASE,$10,_USB_TRANS_In     );
  End;
 end;
//
Procedure Bulk_DevToPC_Send(Var USB : TUSB; ulEPStatus : DWord);
 Var
  lRetCode : Integer;
  ulLength : DWord;
 begin
  USBDevEndpointStatusClear(_USB0_BASE,$10 {cEpBulk},ulEPStatus);
  //If _gUSB.UserCmd <>
  Dec(USB.ModeCnt);
  //
  Case (USB.ModeCnt = 0) of
   True : // 종료
          begin
           LCD_Log(clWhite,'Bulk End');
           Exit;
          end;
   False: // 다음 블럭
          begin
           ulLength := 64;
           lRetcode := USBEndpointDataPut(_USB0_BASE,$10,@USB.BufW,ulLength);
           If lRetcode = -1 then Exit;
           Case (USB.ModeCnt = 1) of
            True  : USBEndpointDataSend(_USB0_BASE,$10,_USB_TRANS_In_Last);
            False : USBEndpointDataSend(_USB0_BASE,$10,_USB_TRANS_In     );
           End;
          end;
  End;
 end;

// HandleEndpoints           ( usbdbulk.c )
//       ProcessDataFromHost ( usbdbulk.c )
//             USBDBulkPacketRead
//       ProcessDataToHost   ( usbdbulk.c )
//             USBDBulkPacketWrite
Procedure Process_EP1(Var USB :TUSB; ulStatus :DWord); // Ep[1~x]
 Var
  ulEPStatus : DWord;
  ulSize     : DWord;
  lRetCode   : Integer;
 begin
  // Host -> Dev : Bulk Out // Process_DataFromHost, USBDBulkPacketRead -------
  If (ulStatus and $00020000) then   // Hard Coding , Simon 추후 변경
   begin
    ulEPStatus := USBEndpointStatus  (_USB0_BASE, $10 {cEpBulk} );
    // $0003:0000 ->  RX_FIFO_FULL & RX_PKT_RDY
    If (ulEPStatus and _USB_DEV_RX_PKT_RDY) then
     begin
      ulSize := USBEndpointDataAvail (_USB0_Base, $10 {cEpBulk} );
      USBEndPointDataGet             (_USB0_Base, $10,@_gUSB.BufR,ulSize    );
      //
      USBDevEndpointStatusClear      (_USB0_BASE, $10 {cEpBulk} , ulEPStatus);
      USBDevEndpointDataAck          (_USB0_Base, $10 {cEpBulk} , True);
      If USB.ModeDir = _DirInOut then
       User_Reply(USB);
     end;
   end;
  // Dev -> Host  : Bulk In ( Process_DataToHost(ulStatus) )-------------------
  If (ulStatus and $00000002) then   // Hard Coding, Simon 추후 변경
   begin
    If USB.ModeDir <> _DirIn then Exit; // DirIn [PC<-dev span="">
    ulEPStatus := USBEndpointStatus  (_USB0_BASE, $10 {cEpBulk} );
    Bulk_DevToPC_Send(USB,ulEPStatus);
   end;
 end;
// Process User Command :
Procedure Process_EP0_User(Var USB : TUSB);
 Var
  pUSBBuf   : pUSBBufDW;
 begin
 //
  Case USB.PktPhase of
   _pSetup : // Check Get Set Request
             If USB.CtlReq = _CmdReqFunc then
             Case (USB.CtlReqType and _udDev2Host) > 0 of
              True : // Get Request  ( No Data Phase )
                     begin
                      USBDevEndpointDataAck(_USB0_BASE,_USB_EP_0,false);
                      Case USB.CtlValue of
                       _CmdGetStatus : // 적당한 응답 (Dir + Addr + Cnt)
                                       begin
                                        USBEndpointDataPut (_USB0_BASE,_USB_EP_0,@USB.ModeDir,12);
                                        USBEndpointDataSend(_USB0_BASE,_USB_EP_0,_USB_TRANS_IN_LAST);
                                       end;
                      End;
                     end;
              False: // Set Request -> Read Data
                     Case USB.CtlLength = 0 of
                      True : USBDevEndpointDataAck(_USB0_BASE,_USB_EP_0,True);
                      False: Begin
                              USBDevEndpointDataAck(_USB0_BASE,_USB_EP_0,false);
                              USB.PktPhase   := _pData;
                             End;
                     end;
             end;
   _pData  : // Data Phase for User Command
            begin
             USBDevEndpointDataAck(_USB0_BASE,_USB_EP_0,True);
             If (USB.CtlReq = _CmdReqFunc) then
              Case USB.CtlValue of
               _CmdSetStatus : begin
                                pUSBBuf       := Pointer(@USB.BufR);
                                USB.ModeDir   := pUSBBuf^[0];
                                USB.ModeAddr  := pUSBBuf^[1];
                                USB.ModeCnt   := pUSBBuf^[2];
                               end;
               _CmdSetAction : begin
                                pUSBBuf       := Pointer(@USB.BufR);
                                USB.ModeState := pUSBBuf^[0];
                                // Simon : Future Works...
                                If USB.ModeState = _tmsStop then
                                 SD_MovStop := True;
                                // Bulk_DevToPc_Start(USB);
                               end;
              end;
            end;
  End;
 end;
// Ref. USBDeviceEnumHandler (usbdenum.c)
Procedure Process_EP0(Var USB : TUSB);
 Var
  EP0Stat   : DWord;
  pDat      : Pointer;
  dSize     : DWord;
  wData     : Word;
  pRequest  : pUSBRequest;
  pUSBBuf   : pUSBBufDW;
 begin
  Ep0Stat := USBEndpointStatus(_USB0_BASE, _USB_EP_0); // -값  발생 (Simon)
  If USB.AddrSet then
   begin
    USBDevAddrSet(_USB0_Base,USB.Addr);
    USB.AddrSet := False;
   end;
  If (Ep0Stat and _USB_DEV_EP0_SENT_STALL) then
   USBDevEndpointStatusClear(_USB0_BASE, _USB_EP_0,_USB_DEV_EP0_SENT_STALL);
  If (Ep0Stat and _USB_DEV_EP0_OUT_PKTRDY) = 0 then Exit;
  // Receive Data & Process ---------------------------------------------------
  dSize   := _EP0_MAX_PACKET_SIZE;
  USBEndpointDataGet(_USB0_BASE,_USB_EP_0,@USB.BufR,dSize);
  If dSize = 0 then Exit;
  //
  Case USB.PktPhase of
   _pSetup : begin
              pRequest       := Pointer(@USB.BufR);
              USB.CtlReqType := pRequest^.bmRequestType;
              USB.CtlReq     := pRequest^.bRequest;
              USB.CtlValue   := pRequest^.wValue;
              USB.CtlLength  := pRequest^.wLength;
              Case (pRequest^.bmRequestType and _USB_RTYPE_TYPE_M) = 0 of
               True : // Standard
                      Case pRequest^.bRequest of
                       _urGetStat  : // 0
                                     begin
                                      wData := $0001;
                                      USBDevEndpointDataAck(_USB0_BASE,_USB_EP_0,false);
                                      USBEndpointDataPut   (_USB0_BASE,_USB_EP_0,@wData,SizeOf(wData));
                                      USBEndpointDataSend  (_USB0_BASE,_USB_EP_0,_USB_TRANS_IN_LAST);
                                      //LCD_Log(clLime,'GetStat');
                                     end;
                       _urSetConf  : // 9
                                     begin
                                      USBDevEndpointDataAck(_USB0_BASE, _USB_EP_0,True);
                                      USB.Status := _tusReady;
                                      // LCD_Log(clLime,'SetConf');
                                      USB.Ready  := True;
                                      //
                                      USBDevEndpointConfigSet(_USB0_BASE,_USB_EP_1, 64,_USB_EP_DEV_IN  or _USB_EP_MODE_BULK);
                                      USBDevEndpointConfigSet(_USB0_BASE,_USB_EP_1, 64,_USB_EP_DEV_OUT or _USB_EP_MODE_BULK);
                                      USBFIFOConfigSet       (_USB0_BASE,_USB_EP_1, 64,_USB_FIFO_SZ_64, _USB_EP_DEV_IN );
                                      USBFIFOConfigSet       (_USB0_BASE,_USB_EP_1,128,_USB_FIFO_SZ_64, _USB_EP_DEV_OUT);
                                     end;
                       _urSetAddr  : // 5
                                     begin
                                      USBDevEndpointDataAck(_USB0_BASE, _USB_EP_0,True);
                                      USB.Addr    := pRequest^.wValue;
                                      USB.AddrSet := True;
                                      //LCD_Log(clLime,'SetAddr');
                                     end;
                       _urGetDesc  : // 6
                                     Begin
                                      Case Hi(pRequest^.wValue) of
                                       $01 : // Device
                                             begin
                                              pDat  := @cDevDesc;
                                              dSize := SizeOf(cDevDesc);
                                              //LCD_Log(clLime,'gd Device');
                                             end;
                                       $02 : // Configuration
                                             begin
                                              pDat  := @cCfgDesc;
                                              Case pRequest^.wLength of
                                               $09  : dSize := $09               // Cfg Desc Only
                                               else   dSize := SizeOf(cCfgDesc); // Cfg + Device + EP (Full)
                                              end;
                                              //Case pRequest^.wLength of
                                              // $09  : LCD_Log(clLime,'Cfg mini')
                                              // else   LCD_Log(clLime,'Cfg full');
                                              //End;
                                             end;
                                       $03 : // String
                                             begin
                                              utGetStrDesc(Lo(pRequest^.wValue),_lStrDesc);
                                              pDat  := @_lStrDesc;
                                              dSize := _lStrDesc.Len;
                                              IntToStr(Lo(pRequest^.wValue),Str);
                                              //LCD_Log(clLime,'sd'+Str);
                                             end
                                       else  begin
                                              //LCD_Log(clLime,'Exit');
                                              Exit;
                                             end;
                                      End;
                                      // Send Ack + Data (Asumption : Always DataLen < 64 )
                                      USBDevEndpointDataAck(_USB0_BASE,_USB_EP_0,false);
                                      USBEndpointDataPut   (_USB0_BASE,_USB_EP_0,pDat,dSize);
                                      USBEndpointDataSend  (_USB0_BASE,_USB_EP_0,_USB_TRANS_IN_LAST);
                                     End;
                      end;
               False: // Non-Standard
                      Process_EP0_User(USB);
              end;
             End;
   _pData  : begin
              Case (USB.CtlReqType and _USB_RTYPE_TYPE_M) = 0 of
               True : // Standard
                      USBDevEndpointDataAck(_USB0_BASE, _USB_EP_0,True);
               False: // Non Standard
                      Case USB.CtlReq = _CmdReqFunc of
                       True  : Process_EP0_User(USB);
                       False : USBDevEndpointDataAck(_USB0_BASE, _USB_EP_0,True);
                      End;
              End;
              USB.PktPhase := _pSetup;
             end;
  end;
 end;
//
Procedure USB_Handler;
 Var
  ulStatus   : DWord;
 begin
  // System Event
  ulStatus := USBIntStatusControl(_USB0_Base);
  If (ulStatus and $2F) then
   begin
    If (ulStatus and _USB_INTCTRL_RESET     ) then _gUSB.Status := _tusReset;
    If (ulStatus and _USB_INTCTRL_SUSPEND   ) then _gUSB.Status := _tusSuspend;
    If (ulStatus and _USB_INTCTRL_RESUME    ) then _gUSB.Status := _tusResume;
    If (ulStatus and _USB_INTCTRL_DISCONNECT) then _gUSB.Status := _tusDisconnect;
    If (ulStatus and _USB_INTCTRL_SOF       ) then _gUSB.Status := _tusSOF;
   end;
  // Ep0 Event
  ulStatus := USBIntStatusEndpoint(_USB0_Base);
  If (ulStatus and _USB_INTEP_0 ) then Process_EP0(_gUSB);
  // Ep1~15 Event
  If _gUSB.Ready                  then Process_EP1(_gUSB,ulStatus);
 end;
// USBDeviceIntHandlerInternal (usbdenum.c)
Procedure USB0Int(); iv IVT_INT_USB0;  ics ICS_AUTO; //ics ICS_OFF;  // ics ICS_AUTO;
 begin
  USB_Handler;
 end;
//
Procedure USB_Init(Operation : TUSBOperation);
 Var
  Lp : DWord;
 begin
   // S/W Init ----------------------------------------------------------------
   _gUSB.Operation  := Operation;
   _gUSB.Ready      := False;
   _gUSB.Status     := _tusNone;
   _gUSB.PktPhase   := _pSetup;
   _gUSB.CtlReqType := 0;
   _gUSB.CtlReq     := 0;
   _gUSB.CtlValue   := 0;
   _gUSB.CtlLength  := 0;
   _gUSB.ModeDir    := _DirInOut;
   _gUSB.ModeAddr   := 0;
   _gUSB.ModeCnt    := 0;
   _gUSB.ModeState  := _tmsNone;
   _gUSB.Addr       := 0;
   _gUSB.AddrSet    := False;
   // H/W Init ----------------------------------------------------------------
   SysCtlPeripheralReset (_SYSCTL_PERIPH_USB0); // Reset the USB controller.
   SysCtlPeripheralEnable(_SYSCTL_PERIPH_USB0); // Enable Clocking to the USB controller.
   SysCtlUSBPLLEnable();                        // Turn on USB Phy clock.
   USBOTGMode(_USB0_BASE);
   //--------------------------------------------------------------------------
   USBIntStatusControl  (_USB0_BASE); // 모든 상태 초기화
   USBIntStatusEndpoint (_USB0_BASE);
   // Enable USB Interrupts.
   USBIntEnableControl  (_USB0_BASE,
                         _USB_INTCTRL_SUSPEND    or //$01
                         _USB_INTCTRL_RESUME     or //$02
                         _USB_INTCTRL_RESET      or //$04
                         _USB_INTCTRL_SOF        or //$08
                         _USB_INTCTRL_DISCONNECT);  //$20
   USBIntEnableEndpoint(_USB0_BASE, _USB_INTEP_ALL);
   USBDevDisconnect(_USB0_BASE);
   Delay_ms(100);
   USBDevConnect   (_USB0_BASE); // Attach the device using the soft connect.
   //
   Case Operation = _cUSB_Interrupt of
    True  : IntEnable       (_INT_USB0 ); // Enable the USB interrupt.
    False : For Lp := 0 to 2000 do
             begin
              Delay_ms(1);
              USB_Handler;
              If _gUSB.Ready then Exit;
             end;
   End;
  end;
//------------------------------------------------------------------------------
//
//
//
//------------------------------------------------------------------------------
// Result : USB.ModeState
Procedure USB_ReadBuf(Var USB : TUSB; Var Buf : Array[1] of Byte; MaxCnt : DWord);
 Var
  ulStatus   : DWord;
  ulEPStatus : DWord;
  ulSize     : DWord;
  ulLp       : Integer;
 begin
  USB.ModeState := _tmsNone;
  ulLp := 0; // 64 x 8 = 512
  Repeat
   // Ep0 Event
   ulStatus := USBIntStatusEndpoint(_USB0_Base);
   If (ulStatus and _USB_INTEP_0 ) then Process_EP0(_gUSB);
   If Not(_gUSB.Ready)             then Exit;
   If USB.ModeState = _tmsStop then Exit;
   // Host -> Dev : Bulk Out // Process_DataFromHost, USBDBulkPacketRead -------
   If (ulStatus and $00020000) then   // Hard Coding , Simon 추후 변경
    begin
     ulEPStatus := USBEndpointStatus  (_USB0_BASE, $10 {cEpBulk} );
     // $0003:0000 ->  RX_FIFO_FULL & RX_PKT_RDY
     If (ulEPStatus and _USB_DEV_RX_PKT_RDY) then
      begin
       ulSize := USBEndpointDataAvail (_USB0_Base, $10 {cEpBulk} );
       USBEndPointDataGet             (_USB0_Base, $10, @Buf[ulLp*64],ulSize);
       USBDevEndpointStatusClear      (_USB0_BASE, $10 {cEpBulk} , ulEPStatus);
       USBDevEndpointDataAck          (_USB0_Base, $10 {cEpBulk} , True);
       Inc(ulLp);
      end
    end;
   //
  Until ( ulLp >= MaxCnt );
 end;
// Result : USB.ModeState
Procedure USB_WriteBuf(Var USB : TUSB; Var Buf : Array[1] of Byte; MaxCnt : DWord);
 Var
  ulStatus   : DWord;
  ulEPStatus : DWord;
  ulSize     : DWord;
  ulLp       : Integer;
 begin
  USB.ModeState := _tmsNone;
  ulLp := 0; // 64 x 8 = 512
  //
  USBEndpointDataPut (_USB0_BASE,$10,@Buf[ulLp*64],64);
  USBEndpointDataSend(_USB0_BASE,$10,_USB_TRANS_In   );
  Inc(ulLp);
  //
  Repeat
   // Ep0 Event
   ulStatus := USBIntStatusEndpoint(_USB0_Base);
   If (ulStatus and _USB_INTEP_0 ) then Process_EP0(_gUSB);
   If Not(_gUSB.Ready)             then Exit;
   If USB.ModeState = _tmsStop     then Exit;
   // Host -> Dev : Bulk Out // Process_DataFromHost, USBDBulkPacketRead -------
   If (ulStatus and $00000002) then   // Hard Coding , Simon 추후 변경
    begin
     ulEPStatus := USBEndpointStatus  (_USB0_BASE, $10 {cEpBulk} );
     //
     USBDevEndpointStatusClear(_USB0_BASE,$10 {cEpBulk},ulEPStatus);
     USBEndpointDataPut       (_USB0_BASE,$10,@Buf[ulLp*64],64);
     // SIMON
     Case (ulLp = (MaxCnt-1))of
      True  : USBEndpointDataSend (_USB0_BASE,$10,_USB_TRANS_In_Last);
      False : USBEndpointDataSend (_USB0_BASE,$10,_USB_TRANS_In     );
     End;
     Inc(ulLp);
    end;
  Until ( ulLp >= MaxCnt );
  //
  While (true) do
   begin
    // Ep0 Event
    ulStatus := USBIntStatusEndpoint(_USB0_Base);
    //If _gUSB.Ready                  then Process_EP1(_gUSB,ulStatus);
    If Not(_gUSB.Ready)             then Exit;
    If USB.ModeState = _tmsStop     then Exit;
    // Host -> Dev : Bulk Out // Process_DataFromHost, USBDBulkPacketRead -------
    If (ulStatus and $00000002) then   // Hard Coding , Simon 추후 변경
     begin
      ulEPStatus := USBEndpointStatus  (_USB0_BASE, $10 {cEpBulk} );
      USBDevEndpointStatusClear(_USB0_BASE,$10 {cEpBulk},ulEPStatus);
      Exit;
     end;
   end;
 end;
//  PC -> Read USB Read ->  Write
Function  USB_PutSD(Var USB : TUSB) : Boolean;
 Var
  Lp  : DWord;
  Inx : DWord;
  Rst : Boolean;
 Label
  JumpEnd;
 begin
  //
  Result := True;
  //
  Rst := SD_Write_MultiBlock_Start (USB.ModeAddr);
  If Not(Rst) then Result := False;
  For Lp := 0 to USB.ModeCnt-1 do
   begin
    USB_ReadBuf(_gUSB,Buf512,8);
    If _gUSB.ModeState = _tmsStop then
     begin
      LCD_log(clRed,'Stop');
      Goto JumpEnd;
     end;
    Rst := SD_Write_MultiBlock(Buf512);
    If Not(Rst) then Result := False;
   end;
  //
  JumpEnd:
  _gUSB.ModeState := _tmsNone;
  SD_Write_MultiBlock_Stop;
 end;
 
//
Function  USB_GetSD (Var USB : TUSB) : Boolean;
 Var
  Lp  : DWord;
  Inx : DWord;
  Rst : Boolean;
 Label
  JumpEnd;
 begin
  //
  Result := True;
  //
  Rst := SD_Read_MultiBlock_Start (USB.ModeAddr);
  If Not(Rst) then Result := False;
  //
  For Lp := 0 to USB.ModeCnt-1 do
   begin
    Rst := SD_Read_MultiBlock(Buf512);
    If Not(Rst) then Result := False;
    USB_WriteBuf(_gUSB,Buf512,8);
    If _gUSB.ModeState = _tmsStop then
     begin
      LCD_log(clRed,'Stop');
      Goto JumpEnd;
     end;
   end;
  //
  JumpEnd:
  _gUSB.ModeState := _tmsNone;
  SD_Read_MultiBlock_Stop;
 end;
end.