Simple USBHost library for Nucleo F446RE/F411RE/F401RE FRDM-KL46Z/KL25Z/F64F LPC4088/LPC1768

Dependencies:   FATFileSystem

Dependents:   F401RE-BTstack_example F401RE-USBHostMSD_HelloWorld

Fork of KL46Z-USBHost by Norimasa Okamoto

簡易USBホストライブラリです。
official-USBHostの下位互換で対応プログラムを僅かな修正で動かすことが出来ます。

Platforms

  • Nucleo F446RE
  • Nucleo F411RE
  • Nucleo F401RE
  • FRDM-K64F
  • FRDM-KL46Z
  • FRDM-KL25Z
  • LPC4088
  • LPC1768

Nucleo F446RE/F411RE/F401REのUSB接続方法

ST morphoUSB
U5V (CN10-8)VBUS (1 RED)
PA11 (CN10-14)DM  (2 WHITE)
PA12 (CN10-12)DP  (3 GREEN)
GND (CN10-20)GND (4 BLACK)

Examples

Import programF446RE-USBHostMouse_HelloWorld

USBHostMouse Hello World for ST-Nucleo-F446RE

Import programF401RE-USBHostMSD_HelloWorld

Simple USBHost MSD(USB flash drive) for Nucleo F401RE/FRDM-KL46Z test program

Import programF401RE-USBHostC270_example

Simple USBHost WebCam test program

Import programK64F_USBHostC270_example

Simple USBHost C270 example

Import programF401RE-BTstack_example

BTstack for Nucleo F401RE/FRDM-KL46Z example program

Import programUSBHostRSSI_example

Bluetooth device discovery example program.

Import programKL46Z-USBHostGPS_HelloWorld

Simple USBHost GPS Dongle Receiver for FRDM-KL46Z test program

USBHost/USBHALHost_F401RE.cpp

Committer:
va009039
Date:
2016-05-01
Revision:
23:4ab8bc835303
Parent:
19:47978c25c9b8

File content as of revision 23:4ab8bc835303:

#if defined(TARGET_NUCLEO_F401RE)||defined(TARGET_NUCLEO_F411RE)||defined(TARGET_NUCLEO_F446RE)
#include "USBHALHost.h"
#include <algorithm>

#ifdef _USB_DBG
extern RawSerial pc;
//RawSerial pc(USBTX,USBRX);
#include "mydebug.h"
#define USB_DBG(...) do{pc.printf("[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);pc.printf(__VA_ARGS__);pc.puts("\n");} while(0);
#define USB_DBG_HEX(A,B) debug_hex<RawSerial>(pc,A,B)

#else
#define USB_DBG(...) while(0)
#define USB_DBG_HEX(A,B) while(0)
#endif

#undef USB_TEST_ASSERT
void usb_test_assert_internal(const char *expr, const char *file, int line);
#define USB_TEST_ASSERT(EXPR) while(!(EXPR)){usb_test_assert_internal(#EXPR,__FILE__,__LINE__);}

#define USB_TRACE1(A) while(0)

#define USB_INFO(...) do{fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");}while(0);

__IO bool attach_done = false;

void delay_ms(uint32_t t)
{
    HAL_Delay(t);
}

// usbh_conf.c
extern HCD_HandleTypeDef hhcd_USB_OTG_FS;

void HAL_HCD_MspInit(HCD_HandleTypeDef* hhcd)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  if(hhcd->Instance==USB_OTG_FS)
  {
    /* Peripheral clock enable */
    __USB_OTG_FS_CLK_ENABLE();
  
    /**USB_OTG_FS GPIO Configuration    
    PA11     ------> USB_OTG_FS_DM
    PA12     ------> USB_OTG_FS_DP 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Peripheral interrupt init*/
    /* Sets the priority grouping field */
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0);
    HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
  }
}

// stm32f4xx_it.c
extern "C" {
void HAL_HCD_Connect_Callback(HCD_HandleTypeDef *hhcd)
{
    USB_TRACE1(hhcd);
    attach_done = true;
}

} // extern "C"

USBHALHost* USBHALHost::instHost;

USBHALHost::USBHALHost() {
    instHost = this;
}

void USBHALHost::init() {
    hhcd_USB_OTG_FS.Instance = USB_OTG_FS;
    hhcd_USB_OTG_FS.Init.Host_channels = 8;
    hhcd_USB_OTG_FS.Init.speed = HCD_SPEED_FULL;
    hhcd_USB_OTG_FS.Init.dma_enable = DISABLE;
    hhcd_USB_OTG_FS.Init.phy_itface = HCD_PHY_EMBEDDED;
    hhcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
    hhcd_USB_OTG_FS.Init.low_power_enable = ENABLE;
    hhcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE;
    hhcd_USB_OTG_FS.Init.use_external_vbus = DISABLE;

    HAL_HCD_Init(&hhcd_USB_OTG_FS);
    HAL_HCD_Start(&hhcd_USB_OTG_FS);

    bool lowSpeed = wait_attach();
    delay_ms(200);
    HAL_HCD_ResetPort(&hhcd_USB_OTG_FS);
    delay_ms(100); // Wait for 100 ms after Reset
    addDevice(NULL, 0, lowSpeed);
}

bool USBHALHost::wait_attach() {
    Timer t;
    t.reset();
    t.start();
    while(!attach_done) {
        if (t.read_ms() > 5*1000) {
            t.reset();
            USB_INFO("Please attach USB device.");
        }
    }
    wait_ms(100);
    return HAL_HCD_GetCurrentSpeed(&hhcd_USB_OTG_FS) == USB_OTG_SPEED_LOW;
}

int USBHALHost::token_setup(USBEndpoint* ep, SETUP_PACKET* setup, uint16_t wLength) {
    const uint8_t ep_addr = 0x00;
    HC hc;
    USBDeviceConnected* dev = ep->getDevice();
    hc.Init(ep_addr, 
        dev->getAddress(),
        dev->getSpeed() ? HCD_SPEED_LOW : HCD_SPEED_FULL,
        EP_TYPE_CTRL, ep->getSize());

    setup->wLength = wLength;
    hc.SubmitRequest((uint8_t*)setup, 8, true); // PID_SETUP
    while(hc.GetURBState() == URB_IDLE);

    switch(hc.GetURBState()) {
        case URB_DONE:
            LastStatus = ACK;
            break;
        default:
            LastStatus = 0xff;
            break;
    }
    ep->setData01(DATA1);
    return 8;
}

int USBHALHost::token_in(USBEndpoint* ep, uint8_t* data, int size, int retryLimit) {
    switch(ep->getType()) {
        case CONTROL_ENDPOINT:
            return token_ctl_in(ep, data, size, retryLimit);
        case INTERRUPT_ENDPOINT:
            return token_int_in(ep, data, size);
        case BULK_ENDPOINT:
            return token_blk_in(ep, data, size, retryLimit);
    }
    return -1;
}

int USBHALHost::token_ctl_in(USBEndpoint* ep, uint8_t* data, int size, int retryLimit) {
    const uint8_t ep_addr = 0x80;
    HC hc;
    USBDeviceConnected* dev = ep->getDevice();
    hc.Init(ep_addr, 
        dev->getAddress(),
        dev->getSpeed() ? HCD_SPEED_LOW : HCD_SPEED_FULL,
        EP_TYPE_CTRL, ep->getSize());

    hc.SetToggle((ep->getData01() == DATA0) ? 0 : 1);

    hc.SubmitRequest(data, size);
    while(hc.GetURBState() == URB_IDLE);

    switch(hc.GetURBState()) {
        case URB_DONE:
            LastStatus = ACK;
            break;
        default:
            LastStatus = 0xff;
            return -1;
    }
    ep->toggleData01();
    return hc.GetXferCount();
}

int USBHALHost::token_int_in(USBEndpoint* ep, uint8_t* data, int size) {
    HC hc;
    USBDeviceConnected* dev = ep->getDevice();
    hc.Init(
        ep->getAddress(),
        dev->getAddress(),
        dev->getSpeed() ? HCD_SPEED_LOW : HCD_SPEED_FULL,
        EP_TYPE_INTR, ep->getSize());

    hc.SetToggle((ep->getData01() == DATA0) ? 0 : 1);

    hc.SubmitRequest(data, size);
    while(hc.GetURBState() == URB_IDLE);
    switch(hc.GetURBState()) {
        case URB_DONE:
            switch(hc.GetState()) {
                case HC_XFRC:
                    LastStatus = ep->getData01();
                    ep->toggleData01();
                    return hc.GetXferCount();
                case HC_NAK:
                    LastStatus = NAK;
                    return -1;
            }
            break;
    }
    LastStatus = STALL;
    return -1;
}

int USBHALHost::token_blk_in(USBEndpoint* ep, uint8_t* data, int size, int retryLimit) {
    HC hc;
    USBDeviceConnected* dev = ep->getDevice();
    hc.Init(
        ep->getAddress(),
        dev->getAddress(),
        HCD_SPEED_FULL,
        EP_TYPE_BULK, ep->getSize());

    hc.SetToggle((ep->getData01() == DATA0) ? 0 : 1);

    int retry = 0;
    do {
        hc.SubmitRequest(data, size);
        while(hc.GetURBState() == URB_IDLE);

        switch(hc.GetURBState()) {
            case URB_DONE:
                switch(hc.GetState()) {
                    case HC_XFRC:
                        LastStatus = ep->getData01();
                        ep->toggleData01();
                        return hc.GetXferCount();
                    case HC_NAK:
                        LastStatus = NAK;
                        if (retryLimit > 0) {
                            delay_ms(1 + 10 * retry);
                        }
                        break;
                    default:
                        break;
                }
                break;
            case URB_STALL:
                LastStatus = STALL;
                return -1;
            default:
                LastStatus = STALL;
                delay_ms(500 + 100 * retry);
                break;
        }
    }while(retry++ < retryLimit);
    return -1;
}

int USBHALHost::token_out(USBEndpoint* ep, const uint8_t* data, int size, int retryLimit) {
    switch(ep->getType()) {
        case CONTROL_ENDPOINT:
            return token_ctl_out(ep, data, size, retryLimit);
        case INTERRUPT_ENDPOINT:
            return token_int_out(ep, data, size);
        case BULK_ENDPOINT:
            return token_blk_out(ep, data, size, retryLimit);
    }
    return -1;
}

int USBHALHost::token_ctl_out(USBEndpoint* ep, const uint8_t* data, int size, int retryLimit) {
    const uint8_t ep_addr = 0x00;
    HC hc;
    USBDeviceConnected* dev = ep->getDevice();
    hc.Init(ep_addr, 
        dev->getAddress(),
        dev->getSpeed() ? HCD_SPEED_LOW : HCD_SPEED_FULL,
        EP_TYPE_CTRL, ep->getSize());

    hc.SetToggle((ep->getData01() == DATA0) ? 0 : 1);

    do {
        hc.SubmitRequest((uint8_t*)data, size);
        while(hc.GetURBState() == URB_IDLE);

        switch(hc.GetURBState()) {
            case URB_DONE:
                LastStatus = ACK;
                ep->toggleData01();
                return size;

            default:
                break;
        }
        delay_ms(1);
    }while(retryLimit-- > 0); 
    return -1;
}

int USBHALHost::token_int_out(USBEndpoint* ep, const uint8_t* data, int size) {
    HC hc;
    USBDeviceConnected* dev = ep->getDevice();
    hc.Init(
        ep->getAddress(),
        dev->getAddress(),
        dev->getSpeed() ? HCD_SPEED_LOW : HCD_SPEED_FULL,
        EP_TYPE_INTR, ep->getSize());

    hc.SetToggle((ep->getData01() == DATA0) ? 0 : 1);

    hc.SubmitRequest((uint8_t*)data, size);
    while(hc.GetURBState() == URB_IDLE);
    if (hc.GetURBState() != URB_DONE) {
        return -1;
    }
    ep->toggleData01();
    return size;
}

int USBHALHost::token_blk_out(USBEndpoint* ep, const uint8_t* data, int size, int retryLimit) {
    HC hc;
    USBDeviceConnected* dev = ep->getDevice();
    hc.Init(
        ep->getAddress(), dev->getAddress(),
        HCD_SPEED_FULL, EP_TYPE_BULK, ep->getSize());

    hc.SetToggle((ep->getData01() == DATA0) ? 0 : 1);

    int retry = 0;
    do {
        hc.SubmitRequest((uint8_t*)data, size);
        while(hc.GetURBState() == URB_IDLE);

        switch(hc.GetURBState()) {
            case URB_DONE:
                switch(hc.GetState()) {
                    case HC_XFRC: // ACK
                        LastStatus = ep->getData01();
                        ep->toggleData01();
                        return size;
                    default:
                        break;
                }
                break;
            case URB_NOTREADY: // HC_NAK
                LastStatus = NAK;
                delay_ms(100 * retry);
                break;
            default:
                LastStatus = STALL;
                return -1;
        }
    } while(retry++ < retryLimit);
    return -1;
}

int USBHALHost::token_iso_in(USBEndpoint* ep, uint8_t* data, int size) {
    HC* hc = ep->getHALData<HC*>();
    if (hc == NULL) {
        hc = new HC;
        ep->setHALData<HC*>(hc);
        USBDeviceConnected* dev = ep->getDevice();
        hc->Init(
            ep->getAddress(), dev->getAddress(),
            HCD_SPEED_FULL, EP_TYPE_ISOC, ep->getSize());
    }
    hc->SubmitRequest(data, size);
    while(hc->GetURBState() == URB_IDLE);
    return hc->GetXferCount();
}

int USBHALHost::multi_token_in(USBEndpoint* ep, uint8_t* data, size_t total, bool block) {
    if (total == 0) {
        return token_in(ep);
    }
    int retryLimit = block ? 10 : 0;
    int read_len = 0;
    for(int n = 0; read_len < total; n++) {
        int size = std::min((int)total-read_len, ep->getSize());
        int result = token_in(ep, data+read_len, size, retryLimit);
        if (result < 0) {
            if (block) {
                return -1;
            }
            if (LastStatus == NAK) {
                if (n == 0) {
                    return -1;
                }
                break;
            }
            return result;
        }
        read_len += result;
        if (result < ep->getSize()) {
            break;
        }
    }
    return read_len;
}

int USBHALHost::multi_token_out(USBEndpoint* ep, const uint8_t* data, size_t total) {
    if (total == 0) {
        return token_out(ep);
    }
    int write_len = 0;
    for(int n = 0; write_len < total; n++) {
        int size = std::min((int)total-write_len, ep->getSize());
        int result = token_out(ep, data+write_len, size);
        if (result < 0) {
            if (LastStatus == NAK) {
                if (n == 0) {
                    return -1;
                }
                break;
            }
            USB_DBG("token_out result=%d %02x", result, LastStatus);
            return result;
        }
        write_len += result;
        if (result < ep->getSize()) {
            break;
        }
    }
    return write_len;
}
void USBHALHost::multi_token_inNB(USBEndpoint* ep, uint8_t* data, int size) {
    USB_TRACE1(size);
    USB_TEST_ASSERT(ep->getState() != USB_TYPE_PROCESSING);
    ep->setBuffer(data, size);
    ep->setState(USB_TYPE_PROCESSING);
}

USB_TYPE USBHALHost::multi_token_inNB_result(USBEndpoint* ep) {
    USB_TEST_ASSERT(ep->getState() == USB_TYPE_PROCESSING);
    uint8_t* buf = ep->getBufStart();
    int size = ep->getBufSize();
    int result = multi_token_in(ep, buf, size, false);
    USB_TRACE1(result);
    if (result < 0) {
        return USB_TYPE_PROCESSING;
    }
    ep->setLengthTransferred(result);
    ep->setState(USB_TYPE_IDLE);
    return USB_TYPE_OK;

}

void USBHALHost::setToggle(USBEndpoint* ep, uint8_t toggle) {
    USB_TEST_ASSERT(toggle == 1);
    ep->setData01(toggle == 0 ? DATA0 : DATA1);
}

uint8_t HC::slot = 0x00;

HC::HC() {
    static const int start = 1;
    uint8_t mask = (1<<start);
    for(int i = start; i < 8; i++, mask <<= 1) {
        if (!(slot & mask)) {
            slot |= mask;
            _ch = i;
            return;
        }
    }
    _ch = 0; // ERROR!!!
}

HC::HC(int ch) {
    _ch = ch;
    slot |= (1<<_ch);
}

HC::~HC() {
    slot &= ~(1<<_ch);
}

HAL_StatusTypeDef HC::Init(uint8_t epnum, uint8_t dev_address, uint8_t speed, uint8_t ep_type, uint16_t mps) {
    _ep_addr = epnum;
    _ep_type = ep_type;
    return HAL_HCD_HC_Init(&hhcd_USB_OTG_FS, _ch,
                           epnum, dev_address, speed, ep_type, mps);
}

HAL_StatusTypeDef HC::SubmitRequest(uint8_t* pbuff, uint16_t length, bool setup) {
    uint8_t direction = (_ep_addr & 0x80) ? DIR_IN : DIR_OUT;
    if (_ep_type == EP_TYPE_CTRL) {
        HCD_HCTypeDef* hc = &hhcd_USB_OTG_FS.hc[_ch];
        if (setup) {
            hc->data_pid = HC_PID_SETUP;
            hc->toggle_out = 0;
        } else {
            if (direction == DIR_IN) {
                if (hc->toggle_in == 0) {
                    hc->data_pid = HC_PID_DATA0;
                } else {
                    hc->data_pid = HC_PID_DATA1;
                }
            } else { // OUT
                if (hc->toggle_out == 0) {
                    hc->data_pid = HC_PID_DATA0;
                } else {
                    hc->data_pid = HC_PID_DATA1;
                }
            }
        }
        hc->xfer_buff = pbuff;
        hc->xfer_len  = length;
        hc->urb_state = URB_IDLE;
        hc->xfer_count = 0;
        hc->ch_num = _ch;
        hc->state = HC_IDLE;
  
        return USB_HC_StartXfer(hhcd_USB_OTG_FS.Instance, hc, 0);
    }
    return HAL_HCD_HC_SubmitRequest(&hhcd_USB_OTG_FS, _ch,
                                    direction, _ep_type, 0, pbuff, length, 0);
}

HCD_URBStateTypeDef HC::GetURBState() {
    return HAL_HCD_HC_GetURBState(&hhcd_USB_OTG_FS, _ch);
}

HCD_HCStateTypeDef HC::GetState() {
    return HAL_HCD_HC_GetState(&hhcd_USB_OTG_FS, _ch);
}

uint32_t HC::GetXferCount() {
    return HAL_HCD_HC_GetXferCount(&hhcd_USB_OTG_FS, _ch);
}

void HC::SetToggle(uint8_t toggle) {
    if (_ep_addr & 0x80) { // IN
        hhcd_USB_OTG_FS.hc[_ch].toggle_in = toggle;
    } else { // OUT
        hhcd_USB_OTG_FS.hc[_ch].toggle_out = toggle;
    }
}

#endif