USB device stack with Nucleo F401RE support. NOTE: the default clock config needs to be changed to in order for USB to work.

Fork of USBDevice by Tomas Cerskus

Slightly modified original USBDevice library to support F401RE.

On F401RE the data pins of your USB connector should be attached to PA12 (D+) and PA11(D-). It is also required to connect the +5V USB line to PA9.

F401RE requires 48MHz clock for USB. Therefore in order for this to work you will need to change the default clock settings:

Clock settings for USB

#include "stm32f4xx_hal.h"

RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    error("RTC error: LSI clock initialization failed."); 
}

NOTE: Changing the clock frequency might affect the behavior of other libraries. I only tested the Serial library.

UPDATE: Clock settings should not to be changed anymore! Looks like the newer mbed library has the required clock enabled.

USBDevice/USBHAL_KL25Z.cpp

Committer:
tolaipner
Date:
2014-03-30
Revision:
24:4ed3e25c3edc
Parent:
20:d38b72fed893

File content as of revision 24:4ed3e25c3edc:

/* Copyright (c) 2010-2011 mbed.org, MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#if defined(TARGET_KL25Z) | defined(TARGET_KL46Z) | defined(TARGET_K20D5M)

#include "USBHAL.h"

USBHAL * USBHAL::instance;

static volatile int epComplete = 0;

// Convert physical endpoint number to register bit
#define EP(endpoint) (1<<(endpoint))

// Convert physical to logical
#define PHY_TO_LOG(endpoint)    ((endpoint)>>1)

// Get endpoint direction
#define IN_EP(endpoint)     ((endpoint) & 1U ? true : false)
#define OUT_EP(endpoint)    ((endpoint) & 1U ? false : true)

#define BD_OWN_MASK        (1<<7)
#define BD_DATA01_MASK     (1<<6)
#define BD_KEEP_MASK       (1<<5)
#define BD_NINC_MASK       (1<<4)
#define BD_DTS_MASK        (1<<3)
#define BD_STALL_MASK      (1<<2)

#define TX    1
#define RX    0
#define ODD   0
#define EVEN  1
// this macro waits a physical endpoint number
#define EP_BDT_IDX(ep, dir, odd) (((ep * 4) + (2 * dir) + (1 *  odd)))

#define SETUP_TOKEN    0x0D
#define IN_TOKEN       0x09
#define OUT_TOKEN      0x01
#define TOK_PID(idx)   ((bdt[idx].info >> 2) & 0x0F)

// for each endpt: 8 bytes
typedef struct BDT {
    uint8_t   info;       // BD[0:7]
    uint8_t   dummy;      // RSVD: BD[8:15]
    uint16_t  byte_count; // BD[16:32]
    uint32_t  address;    // Addr
} BDT;


// there are:
//    * 16 bidirectionnal endpt -> 32 physical endpt
//    * as there are ODD and EVEN buffer -> 32*2 bdt
__attribute__((__aligned__(512))) BDT bdt[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
uint8_t * endpoint_buffer[(NUMBER_OF_PHYSICAL_ENDPOINTS - 2) * 2];
uint8_t * endpoint_buffer_iso[2*2];

static uint8_t set_addr = 0;
static uint8_t addr = 0;

static uint32_t Data1  = 0x55555555;

static uint32_t frameNumber() {
    return((USB0->FRMNUML | (USB0->FRMNUMH << 8)) & 0x07FF);
}

uint32_t USBHAL::endpointReadcore(uint8_t endpoint, uint8_t *buffer) {
    return 0;
}

USBHAL::USBHAL(void) {
    // Disable IRQ
    NVIC_DisableIRQ(USB0_IRQn);

    // fill in callback array
    epCallback[0] = &USBHAL::EP1_OUT_callback;
    epCallback[1] = &USBHAL::EP1_IN_callback;
    epCallback[2] = &USBHAL::EP2_OUT_callback;
    epCallback[3] = &USBHAL::EP2_IN_callback;
    epCallback[4] = &USBHAL::EP3_OUT_callback;
    epCallback[5] = &USBHAL::EP3_IN_callback;
    epCallback[6] = &USBHAL::EP4_OUT_callback;
    epCallback[7] = &USBHAL::EP4_IN_callback;
    epCallback[8] = &USBHAL::EP5_OUT_callback;
    epCallback[9] = &USBHAL::EP5_IN_callback;
    epCallback[10] = &USBHAL::EP6_OUT_callback;
    epCallback[11] = &USBHAL::EP6_IN_callback;
    epCallback[12] = &USBHAL::EP7_OUT_callback;
    epCallback[13] = &USBHAL::EP7_IN_callback;
    epCallback[14] = &USBHAL::EP8_OUT_callback;
    epCallback[15] = &USBHAL::EP8_IN_callback;
    epCallback[16] = &USBHAL::EP9_OUT_callback;
    epCallback[17] = &USBHAL::EP9_IN_callback;
    epCallback[18] = &USBHAL::EP10_OUT_callback;
    epCallback[19] = &USBHAL::EP10_IN_callback;
    epCallback[20] = &USBHAL::EP11_OUT_callback;
    epCallback[21] = &USBHAL::EP11_IN_callback;
    epCallback[22] = &USBHAL::EP12_OUT_callback;
    epCallback[23] = &USBHAL::EP12_IN_callback;
    epCallback[24] = &USBHAL::EP13_OUT_callback;
    epCallback[25] = &USBHAL::EP13_IN_callback;
    epCallback[26] = &USBHAL::EP14_OUT_callback;
    epCallback[27] = &USBHAL::EP14_IN_callback;
    epCallback[28] = &USBHAL::EP15_OUT_callback;
    epCallback[29] = &USBHAL::EP15_IN_callback;


    // choose usb src as PLL
    SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL_MASK);

    // enable OTG clock
    SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;

    // Attach IRQ
    instance = this;
    NVIC_SetVector(USB0_IRQn, (uint32_t)&_usbisr);
    NVIC_EnableIRQ(USB0_IRQn);

    // USB Module Configuration
    // Reset USB Module
    USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
    while(USB0->USBTRC0 & USB_USBTRC0_USBRESET_MASK);

    // Set BDT Base Register
    USB0->BDTPAGE1=(uint8_t)((uint32_t)bdt>>8);
    USB0->BDTPAGE2=(uint8_t)((uint32_t)bdt>>16);
    USB0->BDTPAGE3=(uint8_t)((uint32_t)bdt>>24);

    // Clear interrupt flag
    USB0->ISTAT = 0xff;

    // USB Interrupt Enablers
    USB0->INTEN |= USB_INTEN_TOKDNEEN_MASK |
                   USB_INTEN_SOFTOKEN_MASK |
                   USB_INTEN_ERROREN_MASK  |
                   USB_INTEN_USBRSTEN_MASK;

    // Disable weak pull downs
    USB0->USBCTRL &= ~(USB_USBCTRL_PDE_MASK | USB_USBCTRL_SUSP_MASK);

    USB0->USBTRC0 |= 0x40;
}

USBHAL::~USBHAL(void) { }

void USBHAL::connect(void) {
    // enable USB
    USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
    // Pull up enable
    USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
}

void USBHAL::disconnect(void) {
    // disable USB
    USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
    // Pull up disable
    USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;

    //Free buffers if required:
    for (int i = 0; i<(NUMBER_OF_PHYSICAL_ENDPOINTS - 2) * 2; i++) {
        free(endpoint_buffer[i]);
        endpoint_buffer[i] = NULL;
    }
    free(endpoint_buffer_iso[2]);
    endpoint_buffer_iso[2] = NULL;
    free(endpoint_buffer_iso[0]);
    endpoint_buffer_iso[0] = NULL;
}

void USBHAL::configureDevice(void) {
    // not needed
}

void USBHAL::unconfigureDevice(void) {
    // not needed
}

void USBHAL::setAddress(uint8_t address) {
    // we don't set the address now otherwise the usb controller does not ack
    // we set a flag instead
    // see usbisr when an IN token is received
    set_addr = 1;
    addr = address;
}

bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags) {
    uint32_t handshake_flag = 0;
    uint8_t * buf;

    if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
        return false;
    }

    uint32_t log_endpoint = PHY_TO_LOG(endpoint);

    if ((flags & ISOCHRONOUS) == 0) {
        handshake_flag = USB_ENDPT_EPHSHK_MASK;
        if (IN_EP(endpoint)) {
            if (endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)] == NULL)
                endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)] = (uint8_t *) malloc (64*2);
            buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)][0];
        } else {
            if (endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)] == NULL)
                endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)] = (uint8_t *) malloc (64*2);
            buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)][0];
        }
    } else {
        if (IN_EP(endpoint)) {
            if (endpoint_buffer_iso[2] == NULL)
                endpoint_buffer_iso[2] = (uint8_t *) malloc (1023*2);
            buf = &endpoint_buffer_iso[2][0];
        } else {
            if (endpoint_buffer_iso[0] == NULL)
                endpoint_buffer_iso[0] = (uint8_t *) malloc (1023*2);
            buf = &endpoint_buffer_iso[0][0];
        }
    }

    // IN endpt -> device to host (TX)
    if (IN_EP(endpoint)) {
        USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag |        // ep handshaking (not if iso endpoint)
                                              USB_ENDPT_EPTXEN_MASK;  // en TX (IN) tran
        bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = (uint32_t) buf;
        bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = 0;
    }
    // OUT endpt -> host to device (RX)
    else {
        USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag |        // ep handshaking (not if iso endpoint)
                                              USB_ENDPT_EPRXEN_MASK;  // en RX (OUT) tran.
        bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].byte_count = maxPacket;
        bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address    = (uint32_t) buf;
        bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info       = BD_OWN_MASK | BD_DTS_MASK;
        bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info       = 0;
    }

    Data1 |= (1 << endpoint);

    return true;
}

// read setup packet
void USBHAL::EP0setup(uint8_t *buffer) {
    uint32_t sz;
    endpointReadResult(EP0OUT, buffer, &sz);
}

void USBHAL::EP0readStage(void) {
    Data1 &= ~1UL;  // set DATA0
    bdt[0].info = (BD_DTS_MASK | BD_OWN_MASK);
}

void USBHAL::EP0read(void) {
    uint32_t idx = EP_BDT_IDX(PHY_TO_LOG(EP0OUT), RX, 0);
    bdt[idx].byte_count = MAX_PACKET_SIZE_EP0;
}

uint32_t USBHAL::EP0getReadResult(uint8_t *buffer) {
    uint32_t sz;
    endpointReadResult(EP0OUT, buffer, &sz);
    return sz;
}

void USBHAL::EP0write(uint8_t *buffer, uint32_t size) {
    endpointWrite(EP0IN, buffer, size);
}

void USBHAL::EP0getWriteResult(void) {
}

void USBHAL::EP0stall(void) {
    stallEndpoint(EP0OUT);
}

EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize) {
    endpoint = PHY_TO_LOG(endpoint);
    uint32_t idx = EP_BDT_IDX(endpoint, RX, 0);
    bdt[idx].byte_count = maximumSize;
    return EP_PENDING;
}

EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t * buffer, uint32_t *bytesRead) {
    uint32_t n, sz, idx, setup = 0;
    uint8_t not_iso;
    uint8_t * ep_buf;

    uint32_t log_endpoint = PHY_TO_LOG(endpoint);

    if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
        return EP_INVALID;
    }

    // if read on a IN endpoint -> error
    if (IN_EP(endpoint)) {
        return EP_INVALID;
    }

    idx = EP_BDT_IDX(log_endpoint, RX, 0);
    sz  = bdt[idx].byte_count;
    not_iso = USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK;

    //for isochronous endpoint, we don't wait an interrupt
    if ((log_endpoint != 0) && not_iso && !(epComplete & EP(endpoint))) {
        return EP_PENDING;
    }

    if ((log_endpoint == 0) && (TOK_PID(idx) == SETUP_TOKEN)) {
        setup = 1;
    }

    // non iso endpoint
    if (not_iso) {
        ep_buf = endpoint_buffer[idx];
    } else {
        ep_buf = endpoint_buffer_iso[0];
    }

    for (n = 0; n < sz; n++) {
        buffer[n] = ep_buf[n];
    }

    if (((Data1 >> endpoint) & 1) == ((bdt[idx].info >> 6) & 1)) {
        if (setup && (buffer[6] == 0))  // if no setup data stage,
            Data1 &= ~1UL;              // set DATA0
        else
            Data1 ^= (1 << endpoint);
    }

    if (((Data1 >> endpoint) & 1)) {
        bdt[idx].info = BD_DTS_MASK | BD_DATA01_MASK | BD_OWN_MASK;
    }
    else {
        bdt[idx].info = BD_DTS_MASK | BD_OWN_MASK;
    }

    USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
    *bytesRead = sz;

    epComplete &= ~EP(endpoint);
    return EP_COMPLETED;
}

EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
    uint32_t idx, n;
    uint8_t * ep_buf;

    if (endpoint > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
        return EP_INVALID;
    }

    // if write on a OUT endpoint -> error
    if (OUT_EP(endpoint)) {
        return EP_INVALID;
    }

    idx = EP_BDT_IDX(PHY_TO_LOG(endpoint), TX, 0);
    bdt[idx].byte_count = size;


    // non iso endpoint
    if (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPHSHK_MASK) {
        ep_buf = endpoint_buffer[idx];
    } else {
        ep_buf = endpoint_buffer_iso[2];
    }

    for (n = 0; n < size; n++) {
        ep_buf[n] = data[n];
    }

    if ((Data1 >> endpoint) & 1) {
        bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
    } else {
        bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
    }

    Data1 ^= (1 << endpoint);

    return EP_PENDING;
}

EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint) {
    if (epComplete & EP(endpoint)) {
        epComplete &= ~EP(endpoint);
        return EP_COMPLETED;
    }

    return EP_PENDING;
}

void USBHAL::stallEndpoint(uint8_t endpoint) {
    USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
}

void USBHAL::unstallEndpoint(uint8_t endpoint) {
    USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
}

bool USBHAL::getEndpointStallState(uint8_t endpoint) {
    uint8_t stall = (USB0->ENDPOINT[PHY_TO_LOG(endpoint)].ENDPT & USB_ENDPT_EPSTALL_MASK);
    return (stall) ? true : false;
}

void USBHAL::remoteWakeup(void) {
    // [TODO]
}


void USBHAL::_usbisr(void) {
    instance->usbisr();
}


void USBHAL::usbisr(void) {
    uint8_t i;
    uint8_t istat = USB0->ISTAT;

    // reset interrupt
    if (istat & USB_ISTAT_USBRST_MASK) {
        // disable all endpt
        for(i = 0; i < 16; i++) {
            USB0->ENDPOINT[i].ENDPT = 0x00;
        }

        // enable control endpoint
        realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
        realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);

        Data1 = 0x55555555;
        USB0->CTL |=  USB_CTL_ODDRST_MASK;

        USB0->ISTAT   =  0xFF;  // clear all interrupt status flags
        USB0->ERRSTAT =  0xFF;  // clear all error flags
        USB0->ERREN   =  0xFF;  // enable error interrupt sources
        USB0->ADDR    =  0x00;  // set default address

        return;
    }

    // resume interrupt
    if (istat & USB_ISTAT_RESUME_MASK) {
        USB0->ISTAT = USB_ISTAT_RESUME_MASK;
    }

    // SOF interrupt
    if (istat & USB_ISTAT_SOFTOK_MASK) {
        USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
        // SOF event, read frame number
        SOF(frameNumber());
    }

    // stall interrupt
    if (istat & 1<<7) {
        if (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK)
            USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
        USB0->ISTAT |= USB_ISTAT_STALL_MASK;
    }

    // token interrupt
    if (istat & 1<<3) {
        uint32_t num  = (USB0->STAT >> 4) & 0x0F;
        uint32_t dir  = (USB0->STAT >> 3) & 0x01;
        uint32_t ev_odd = (USB0->STAT >> 2) & 0x01;

        // setup packet
        if ((num == 0) && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == SETUP_TOKEN)) {
            Data1 &= ~0x02;
            bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
            bdt[EP_BDT_IDX(0, TX, ODD)].info  &= ~BD_OWN_MASK;

            // EP0 SETUP event (SETUP data received)
            EP0setupCallback();

        } else {
            // OUT packet
            if (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == OUT_TOKEN) {
                if (num == 0)
                    EP0out();
                else {
                    epComplete |= (1 << EP(num));
                    if ((instance->*(epCallback[EP(num) - 2]))()) {
                        epComplete &= ~(1 << EP(num));
                    }
                }
            }

            // IN packet
            if (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == IN_TOKEN) {
                if (num == 0) {
                    EP0in();
                    if (set_addr == 1) {
                        USB0->ADDR = addr & 0x7F;
                        set_addr = 0;
                    }
                }
                else {
                    epComplete |= (1 << (EP(num) + 1));
                    if ((instance->*(epCallback[EP(num) + 1 - 2]))()) {
                        epComplete &= ~(1 << (EP(num) + 1));
                    }
                }
            }
        }

        USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
    }

    // sleep interrupt
    if (istat & 1<<4) {
        USB0->ISTAT |= USB_ISTAT_SLEEP_MASK;
    }

    // error interrupt
    if (istat & USB_ISTAT_ERROR_MASK) {
        USB0->ERRSTAT = 0xFF;
        USB0->ISTAT |= USB_ISTAT_ERROR_MASK;
    }
}


#endif