How to Use USB HAL

group HAL_Usage_Chapter

This section provides a detailed explanation on utilizing USB HAL, accompanied by sample code for better understanding.

Hardware Setup

Example
void usb_hw_init(void)
{
    hal_usb_speed_set(HAL_USB_SPEED_HIGH);
    hal_usb_init();
    hal_usb_phy_power_on();
    hal_usb_mac_init();
}

Prepare to Enumerate

  • Set up the hardware as required above.

  • Implement the HAL USB composite driver.

  • Create task to handle interrupts.

  • Initialize the interrupts.

  • USB soft connect.

Example
uint8_t isr_pending = 0;
void usb_common_isr_handler(T_HAL_USB_COMMON_ISR isr, T_HAL_USB_ISR_PARAM *param)
{
    if process in task
    {
        isr_pending++;
        //send message to USB irq task
    }
}

void usb_common_isr_enter(void)
{
    NVIC_DisableIRQ(USB_IP_IRQn);
}

void usb_common_isr_exit(void)
{
    if(isr_pending == 0)
    {
        NVIC_EnableIRQ(USB_IP_IRQn);
    }
}

HAL_USB_COMMON_ISR_HOOKS common_isr_hooks =
{
    .enter = usb_common_isr_enter,
    .handler = usb_common_isr_handler,
    .exit = usb_common_isr_exit,
}
void usb_isr_proc_task(void *)
{
    while(1)
    {
        if receive msg from USB common ISR
        {
            //process common ISR
        }
        isr_pending--;
        if(isr_pending == 0)
        {
            NVIC_EnableIRQ(USB_IP_IRQn);
        }
    }
}

void usb_isr_proc_task_create(void)
{
    //create task usb_isr_proc_task
}

void usb_isr_init(void)
{
    RamVectorTableUpdate(USB_IP_VECTORn, usb_common_isr_handler);
    NVIC_SetPriority(USB_IP_IRQn, 4);
    NVIC_EnableIRQ(USB_IP_IRQn);
}

void usb_start(void)
{
    usb_hw_init();//Setup hardware
    usb_isr_proc_task_create();//Create task to handle interrupts
    usb_isr_init();//Initialize interrupts
    hal_usb_soft_attach();//USB soft connect

    //os scheduler start
}

Endpoint Setup

The endpoint should be enabled when receiving set_alt request and the alternate setting contains the endpoint. Please refer to Prepare to Enumerate to know how to receive setup packets.

Example
int setup_cb(uint8_t *data)
{
   if setup request is set_alt
   {
        void *ep_handle = hal_usb_ep_handle_get(ep addr);
        if alternate value is m and the alternate setting m contains the endpoint epN
        {
            hal_usb_ep_enable(ep_handle , ep desc);

            // bulk & interrupt endpoints
            //if endpoint direction is OUT, allocate URB
            T_HAL_USB_REQUEST_BLOCK *ep_urb = hal_usb_urb_alloc(len);
            ...initialize URB...(refer to Data Transfer)
            //prepare to receive data
            hal_usb_ep_rx(ep_handle, ep_urb);

            // isochronous endpoints
            //allocate URB
            T_HAL_USB_ISO_REQUEST_BLOCK *ep_urb = hal_usb_iso_urb_alloc(len);
            ...initialize URB...(refer to Data Transfer)
            //start transfer--For both send and receive
            hal_usb_iso_ep_start(ep_handle, ep_urb);
        }
        else
        {
            // isochronous endpoints
            hal_usb_iso_ep_stop(ep_handle, ep_urb);

            // all endpoints
            hal_usb_ep_disable(ep_handle);
        }
   }
}

Data Transfer

Data transfer entity is defined in T_HAL_USB_REQUEST_BLOCK for control/interrupt/bulk transfers and T_HAL_USB_ISO_REQUEST_BLOCK for isochronous transfer.

To setup data transfer:

  • Setup endpoint, refer to Endpoint Setup in How to Use USB HAL.

  • Allocate URB.

  • Start transfer.

Example
 // Control transfer

 //enable endpoint 0
 void *ep0_handle = hal_usb_ep_handle_get(0);
 hal_usb_ep_enable(ep0_handle , NULL);

 int ep0_request_complete(T_HAL_USB_REQUEST_BLOCK *urb)
 {
     //for OUT data stage
     process data in urb->buf
 }

 //allocate URB
 T_HAL_USB_REQUEST_BLOCK *ep0_urb = hal_usb_urb_alloc(len);
 ep0_urb->length = length of data to transfer
 memcpy(ep0_urb->buf, data, ep0_urb->length)
 ep0_urb->complete = ep0_request_complete;
 ep0_urb->ep_handle = ep0_handle;

 //start transfer--For both send and receive
 //hal_usb_ep0_trx MUST be called in setup callback in \ref T_HAL_USB_DRIVER
 //for TX, if ep0_urb->length is 0, it means send ACK to host
 hal_usb_ep0_trx(ep0_handle, ep0_urb);

// \b bulk \b & \b interrupt \b transfer

//enable ep
void *ep_handle = hal_usb_ep_handle_get(ep addr);
hal_usb_ep_enable(ep_handle , ep desc);

int ep_request_complete(T_HAL_USB_REQUEST_BLOCK *urb)
{
     //for OUT data stage
     process data in urb->buf
     ...reinit urb if needed...
     hal_usb_ep_rx(ep_handle, ep_urb);

}

//allocate URB
T_HAL_USB_REQUEST_BLOCK *ep_urb = hal_usb_urb_alloc(len);
ep_urb->complete = ep_request_complete;
ep_urb->ep_handle = ep_handle;
ep_urb->buf_proc_intrvl = process interval;
ep_urb->data_per_frame = data length per host polling;

//start transfer
//send
if support zero length packet
{
     ep_urb->zlp = (ep max packet size ==  ep_urb->length);
}
else
{
     ep_urb->zlp = 0;
}
hal_usb_ep_tx(ep_handle, ep_urb);
//Receive
hal_usb_ep_rx(ep_handle, ep_urb);

// \b isochronous \b transfer

int iso_ep_request_complete(T_HAL_USB_ISO_REQUEST_BLOCK *urb, uint8_t proc_buf_num)
{
     T_HAL_USB_ISO_PKT_INFO *iso_pkt = NULL;
     uint8_t *buf = NULL;
     uint8_t pkt_cnt = 0;

     if(proc_buf_num == 0)
     {
         iso_pkt = urb->iso_pkt0;
         buf = urb->buf0;
     }
     else
     {
         iso_pkt = urb->iso_pkt1;
         buf = urb->buf1;
     }
     pkt_cnt = iso_pkt->pkt_cnt;
     for(uint8_t i = 0; i < pkt_cnt; i++)
     {
         if(iso_pkt[i].status == 0)
         {
         //process data store in buf + iso_pkt[i].offset,
           and data length is  iso_pkt[i].actual
         }
     }
     return 0;
}

//enable ep
void *ep_handle = hal_usb_ep_handle_get(ep addr);
hal_usb_ep_enable(ep_handle , ep desc);

//allocate URB
T_HAL_USB_ISO_REQUEST_BLOCK *iso_ep_urb = hal_usb_iso_urb_alloc(len);
iso_ep_urb->length = length of data to transfer
memcpy(iso_ep_urb->buf, data, iso_ep_urb->length)
iso_ep_urb->complete = iso_ep_request_complete;
iso_ep_urb->ep_handle = ep_handle;

//start transfer--For both send and receive
hal_usb_iso_ep_start(ep_handle, iso_ep_urb);

Power Manager

To limit power consumption, the USB PHY will partially power down when the device suspends by calling hal_usb_suspend_enter in HAL_USB_COMMON_ISR_SUSPEND. If you quit the suspend state, a suspendn interrupt will be triggered, and hal_usb_suspend_exit should be called in the suspendn interrupt, followed by HAL_USB_COMMON_ISR_RESUME.

Example
 void usb_common_isr_handler(T_HAL_USB_COMMON_ISR isr, T_HAL_USB_ISR_PARAM *param)
 {
     if (isr == HAL_USB_COMMON_ISR_SUSPEND)
     {
         hal_usb_suspend_enter();
     }
 }

void usb_suspendn_isr_handler(void)
{
   hal_usb_suspend_exit();
}

HAL_USB_SUSPENDN_ISR_HOOKS usb_suspendn_isr_hooks =
{
     .enter = NULL,
     .handler = usb_suspendn_isr_handler,
     .exit = NULL,
 };
 //enable suspendn interrupt
 hal_usb_suspendn_isr_handler_update(&usb_suspendn_isr_hooks);