Library to use Arduino USB host shield on mbed

Dependents:   USBHOST_PS5

ArduinoのUSB Host Shield 2.0をmbedで使えるようにしたライブラリです。
大体のコードがArduinoからそのまま移植可能です。

Arduino UNOやMega用のホストシールド以外にもミニサイズのホストシールドでも使用可能です https://os.mbed.com/media/uploads/kotakku/dffgfddswa.png

シールドについて

3.3VのI/O用にシールドの改造が必要になりますがネット上に記事がたくさんあるのでそちらを参考にしてください

接続例

https://os.mbed.com/media/uploads/kotakku/esgsvfvhjrekldkcjxvb.png

使い方

Arduinoのコードと違うのはUSBのインスタンスの宣言部分のみです。
ピンを自分で指定できるようにしたので使いやすくなりました。

仕様

  • Arduinoのmillis関数、micros関数の移植のために内部でTimerクラスを使用しています。

main.cpp

#include "mbed.h"
#include <PS3BT.h>
#include <usbhub.h>

Serial pc(USBTX, USBRX, 115200);

//Nucleo f303k8用
USB Usb(A6, A5, A4, A3, A2); // mosi, miso, sclk, ssel, intr
BTD Btd(&Usb);
PS3BT PS3(&Btd);

int main()
{
    bool printAngle = false;

    if (Usb.Init() == -1)
    {
        pc.printf("\r\nOSC did not start");
        while (1); // Halt
    }
    pc.printf("\r\nPS3 USB Library Started");

    while (1)
    {
        Usb.Task();
        
        if (PS3.PS3Connected || PS3.PS3NavigationConnected) {
            if (PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117)
            {
                pc.printf("\r\nLeftHatX: %d", PS3.getAnalogHat(LeftHatX));
                pc.printf("\tLeftHatY: %d", PS3.getAnalogHat(LeftHatY));
                if (PS3.PS3Connected)
                { // The Navigation controller only have one joystick
                    pc.printf("\tRightHatX: %d", PS3.getAnalogHat(RightHatX));
                    pc.printf("\tRightHatY: %d", PS3.getAnalogHat(RightHatY));
                }
            }
            // Analog button values can be read from almost all buttons
            if (PS3.getAnalogButton(L2) || PS3.getAnalogButton(R2))
            {
                pc.printf("\r\nL2: %d", PS3.getAnalogButton(L2));
                if (!PS3.PS3NavigationConnected)
                {
                    pc.printf("\tR2: %d", PS3.getAnalogButton(R2));
                }
            }
            if (PS3.getButtonClick(PS))
            {
                PS3.disconnect();
                pc.printf("\r\nPS");
            }
    
            if (PS3.getButtonClick(TRIANGLE))
                pc.printf("\r\nTriangle");
            if (PS3.getButtonClick(CIRCLE))
                pc.printf("\r\nCircle");
            if (PS3.getButtonClick(CROSS))
                pc.printf("\r\nCross");
            if (PS3.getButtonClick(SQUARE))
                pc.printf("\r\nSquare");
    
            if (PS3.getButtonClick(UP))
            {
                pc.printf("\r\nUp");
                PS3.setLedOff();
                PS3.setLedOn(CONTROLLER_LED4);
            }
            if (PS3.getButtonClick(RIGHT))
            {
                pc.printf("\r\nRight");
                PS3.setLedOff();
                PS3.setLedOn(CONTROLLER_LED1);
            }
            if (PS3.getButtonClick(DOWN))
            {
                pc.printf("\r\nDown");
                PS3.setLedOff();
                PS3.setLedOn(CONTROLLER_LED2);
            }
            if (PS3.getButtonClick(LEFT))
            {
                pc.printf("\r\nLeft");
                PS3.setLedOff();
                PS3.setLedOn(CONTROLLER_LED3);
            }
    
            if (PS3.getButtonClick(L1))
                pc.printf("\r\nL1");
            if (PS3.getButtonClick(L3))
                pc.printf("\r\nL3");
            if (PS3.getButtonClick(R1))
                pc.printf("\r\nR1");
            if (PS3.getButtonClick(R3))
                pc.printf("\r\nR3");
    
            if (PS3.getButtonClick(SELECT))
            {
                pc.printf("\r\nSelect - ");
                PS3.printStatusString();
            }
            if (PS3.getButtonClick(START))
            {
                pc.printf("\r\nStart");
                printAngle = !printAngle;
            }
            if (printAngle)
            {
                pc.printf("\r\nPitch: %.3lf", PS3.getAngle(Pitch));
                pc.printf("\tRoll: %.3lf", PS3.getAngle(Roll));
            }
        }
        else
        {
            pc.printf("not connect\n");
        }
    }
}

USB_Host/UsbCore.h

Committer:
kotakku
Date:
2020-01-18
Revision:
0:b1ce54272580
Child:
1:da31140f2a1c

File content as of revision 0:b1ce54272580:

/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Contact information
-------------------

Circuits At Home, LTD
Web      :  http://www.circuitsathome.com
e-mail   :  support@circuitsathome.com
 */

// warning
// #define _usb_h_
// #define MBED_H

#if !defined(_usb_h_) || defined(USBCORE_H)
#error "Never include UsbCore.h directly; include Usb.h instead"
#else
#define USBCORE_H

// Not used anymore? If anyone uses this, please let us know so that this may be
// moved to the proper place, settings.h.
//#define USB_METHODS_INLINE


/* Common setup data constant combinations  */
#define bmREQ_GET_DESCR USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE   //get descriptor request type
#define bmREQ_SET USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE         //set request type for all but 'set feature' and 'set interface'
#define bmREQ_CL_GET_INTF USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE //get interface request type

// D7           data transfer direction (0 - host-to-device, 1 - device-to-host)
// D6-5         Type (0- standard, 1 - class, 2 - vendor, 3 - reserved)
// D4-0         Recipient (0 - device, 1 - interface, 2 - endpoint, 3 - other, 4..31 - reserved)

// USB Device Classes
#define USB_CLASS_USE_CLASS_INFO 0x00                                                                     // Use Class Info in the Interface Descriptors
#define USB_CLASS_AUDIO 0x01                                                                              // Audio
#define USB_CLASS_COM_AND_CDC_CTRL 0x02                                                                   // Communications and CDC Control
#define USB_CLASS_HID 0x03                                                                                // HID
#define USB_CLASS_PHYSICAL 0x05                                                                           // Physical
#define USB_CLASS_IMAGE 0x06                                                                              // Image
#define USB_CLASS_PRINTER 0x07                                                                            // Printer
#define USB_CLASS_MASS_STORAGE 0x08                                                                       // Mass Storage
#define USB_CLASS_HUB 0x09                                                                                // Hub
#define USB_CLASS_CDC_DATA 0x0a                                                                           // CDC-Data
#define USB_CLASS_SMART_CARD 0x0b                                                                         // Smart-Card
#define USB_CLASS_CONTENT_SECURITY 0x0d                                                                   // Content Security
#define USB_CLASS_VIDEO 0x0e                                                                              // Video
#define USB_CLASS_PERSONAL_HEALTH 0x0f                                                                    // Personal Healthcare
#define USB_CLASS_DIAGNOSTIC_DEVICE 0xdc                                                                  // Diagnostic Device
#define USB_CLASS_WIRELESS_CTRL 0xe0                                                                      // Wireless Controller
#define USB_CLASS_MISC 0xef                                                                               // Miscellaneous
#define USB_CLASS_APP_SPECIFIC 0xfe                                                                       // Application Specific
#define USB_CLASS_VENDOR_SPECIFIC 0xff                                                                    // Vendor Specific

// Additional Error Codes
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED 0xD1
#define USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE 0xD2
#define USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS 0xD3
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL 0xD4
#define USB_ERROR_HUB_ADDRESS_OVERFLOW 0xD5
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL 0xD6
#define USB_ERROR_EPINFO_IS_NULL 0xD7
#define USB_ERROR_INVALID_ARGUMENT 0xD8
#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE 0xD9
#define USB_ERROR_INVALID_MAX_PKT_SIZE 0xDA
#define USB_ERROR_EP_NOT_FOUND_IN_TBL 0xDB
#define USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET 0xE0
#define USB_ERROR_FailGetDevDescr 0xE1
#define USB_ERROR_FailSetDevTblEntry 0xE2
#define USB_ERROR_FailGetConfDescr 0xE3
#define USB_ERROR_TRANSFER_TIMEOUT 0xFF

#define USB_XFER_TIMEOUT 5000 // (5000) USB transfer timeout in milliseconds, per section 9.2.6.1 of USB 2.0 spec
//#define USB_NAK_LIMIT         32000   // NAK limit for a transfer. 0 means NAKs are not counted
#define USB_RETRY_LIMIT 3     // 3 retry limit for a transfer
#define USB_SETTLE_DELAY 200  // settle delay in milliseconds

#define USB_NUMDEVICES 16       //number of USB devices
//#define HUB_MAX_HUBS          7       // maximum number of hubs that can be attached to the host controller
#define HUB_PORT_RESET_DELAY 20 // hub port reset delay 10 ms recomended, can be up to 20 ms

/* USB state machine states */
#define USB_STATE_MASK 0xf0

#define USB_STATE_DETACHED 0x10
#define USB_DETACHED_SUBSTATE_INITIALIZE 0x11
#define USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE 0x12
#define USB_DETACHED_SUBSTATE_ILLEGAL 0x13
#define USB_ATTACHED_SUBSTATE_SETTLE 0x20
#define USB_ATTACHED_SUBSTATE_RESET_DEVICE 0x30
#define USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE 0x40
#define USB_ATTACHED_SUBSTATE_WAIT_SOF 0x50
#define USB_ATTACHED_SUBSTATE_WAIT_RESET 0x51
#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE 0x60
#define USB_STATE_ADDRESSING 0x70
#define USB_STATE_CONFIGURING 0x80
#define USB_STATE_RUNNING 0x90
#define USB_STATE_ERROR 0xa0

class USBDeviceConfig
{
public:
    virtual uint8_t Init(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed __attribute__((unused)))
    {
        return 0;
    }

    virtual uint8_t ConfigureDevice(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed __attribute__((unused)))
    {
        return 0;
    }

    virtual uint8_t Release()
    {
        return 0;
    }

    virtual uint8_t Poll()
    {
        return 0;
    }

    virtual uint8_t GetAddress()
    {
        return 0;
    }

    virtual void ResetHubPort(uint8_t port __attribute__((unused)))
    {
        return;
    } // Note used for hubs only!

    virtual bool VIDPIDOK(uint16_t vid __attribute__((unused)), uint16_t pid __attribute__((unused)))
    {
        return false;
    }

    virtual bool DEVCLASSOK(uint8_t klass __attribute__((unused)))
    {
        return false;
    }

    virtual bool DEVSUBCLASSOK(uint8_t subklass __attribute__((unused)))
    {
        return true;
    }
};

/* USB Setup Packet Structure   */
typedef struct
{

    union {                    // offset   description
        uint8_t bmRequestType; //   0      Bit-map of request type

        struct
        {
            uint8_t recipient : 5; //          Recipient of the request
            uint8_t type : 2;      //          Type of request
            uint8_t direction : 1; //          Direction of data X-fer
        } __attribute__((packed));
    } ReqType_u;
    uint8_t bRequest; //   1      Request

    union {
        uint16_t wValue; //   2      Depends on bRequest

        struct
        {
            uint8_t wValueLo;
            uint8_t wValueHi;
        } __attribute__((packed));
    } wVal_u;
    uint16_t wIndex;  //   4      Depends on bRequest
    uint16_t wLength; //   6      Depends on bRequest
} __attribute__((packed)) SETUP_PKT, *PSETUP_PKT;

// Base class for incoming data parser

class USBReadParser
{
public:
    virtual void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) = 0;
};

class USB : public MAX3421E
{
    AddressPoolImpl<USB_NUMDEVICES> addrPool;
    USBDeviceConfig *devConfig[USB_NUMDEVICES];
    uint8_t bmHubPre;

private:
    static Timer arduinoTimer; // for millis() & micros() function in Arduino
public:
    static uint32_t read_ms() { return arduinoTimer.read_ms(); }
    static uint32_t read_us() { return arduinoTimer.read_us(); }
    
public:
    USB(PinName mosi, PinName miso, PinName sclk, PinName ssel, PinName intr);

    void SetHubPreMask()
    {
        bmHubPre |= bmHUBPRE;
    };

    void ResetHubPreMask()
    {
        bmHubPre &= (~bmHUBPRE);
    };

    AddressPool &GetAddressPool()
    {
        return (AddressPool &)addrPool;
    };

    uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
    {
        for (uint8_t i = 0; i < USB_NUMDEVICES; i++)
        {
            if (!devConfig[i])
            {
                devConfig[i] = pdev;
                return 0;
            }
        }
        return USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS;
    };

    void ForEachUsbDevice(UsbDeviceHandleFunc pfunc)
    {
        addrPool.ForEachUsbDevice(pfunc);
    };
    uint8_t getUsbTaskState(void);
    void setUsbTaskState(uint8_t state);

    EpInfo *getEpInfoEntry(uint8_t addr, uint8_t ep);
    uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr);

    /* Control requests */
    uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr);
    uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t *dataptr);

    uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p);

    uint8_t getStrDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t index, uint16_t langid, uint8_t *dataptr);
    uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr);
    uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value);
    /**/
    uint8_t ctrlData(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr, bool direction);
    uint8_t ctrlStatus(uint8_t ep, bool direction, uint16_t nak_limit);
    uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval = 0);
    uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *data);
    uint8_t dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit);

    void Task(void);

    uint8_t DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed);
    uint8_t Configuring(uint8_t parent, uint8_t port, bool lowspeed);
    uint8_t ReleaseDevice(uint8_t addr);

    uint8_t ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
                    uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t *dataptr, USBReadParser *p);
    
private:
    void init();
    uint8_t SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit);
    uint8_t OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data);
    uint8_t InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval = 0);
    uint8_t AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed);
};

#if 0 //defined(USB_METHODS_INLINE)
//get device descriptor

inline uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {
        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, dataptr));
}
//get configuration descriptor

inline uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {
        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, dataptr));
}
//get string descriptor

inline uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t nuint8_ts, uint8_t index, uint16_t langid, uint8_t* dataptr) {
        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, nuint8_ts, dataptr));
}
//set address

inline uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {
        return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, NULL));
}
//set configuration

inline uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {
        return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, NULL));
}

Timer USB::arduinoTimer;

#endif // defined(USB_METHODS_INLINE)

#endif /* USBCORE_H */