Atmel's USB hardware using DMA

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok this is not a chip specific issue...but I have seen the same behaviour with AT32UC3A256 and ATSAME70 series chips.

I am using the chip in host mode using USB DMA...trying to talk to USB modem (data cards).

No issue with OUT pipes.

I get random pipe errors for IN pipes, mostly during high bandwidth data (1-2Mbps). I have spent numerous days trying out different codes and still no good.

Here is what I do:

 

//Main USB interrupt ISR:
void USB_Interrupt()
{
    unsigned long status = INT_STATUS_REG & INT_MASK_REG;
    
    if(status & SOF_FLAG)
    {
        SOF_IntHandler();
        return;
    }
    
    unsigned char pID = GetPipeInterrupt();
    if(pID < USB_NUM_OF_PIPES)
    {
        USB_PipeIntHandler(pID, PIPE_INT_STATUS_REG);
        return;
    }
    
    pID = GetPipe_DMA_Interrupt();
    if(pID < USB_NUM_OF_PIPES)
    {
        USB_Pipe_DMA_IntHandler(pID);
        return;
    }

    //Handle other host-device interrupts.
    if(status & OTHER_FLAG)
    {
        //Process other status flags;
        return;
    }
}


//Inside the SOF_InterruptHandler() call we basically
//Either start off the pipe transaction or if transaction already
//started I check timeout in pending mode. So its like:
void SOF_IntHandler(void)
{
    //For each interface and then for each pipe in interface:
    if(pipe->Status == PROCESS_PIPE)
    {
        USB_PipeProcess();
    }
    else    //In PENDING State.
    {
        if(--pipe->Timeout == 0)
        {
            //This is where I terminate reading/writing to pipe.
            //Timeout is set to 200ms when started in PROCESS_PIPE.
            USB_PipeAbort();
        }
    }
}


//So now I have the PipeProcess for IN pipe as such:
//Calling this function from SOF_IntHandler() starts of
//IN transfer at the start of SOF frame.
void PipeProcess_IN( params )
{
    if (pipe->Length)
    {
        //If Application level has not read in previous transfer...
        return;
    }

    unsigned long status = DMA_STATUS_REG;
    unsigned long remaining;
    
    if(status & DMA_CHNL_ENABLED)
    {
        remaining = status >> CHNL_BUFF_COUNTER_Pos;
        USB->INRQ = ((remaining + pipe->Size - 1)/pipe->Size - 1) & INRQ_Msk;
        //No need to start DMA again as it is already enabled.
    }
    else
    {
        //Set appropriate INRQ numbers...
        USB->INRQ = (pipe->BufferSize/pipe->Size - 1) & INRQ_Msk;
        
        //Start DMA transfer off.
        USB_DMA.Address = pipe->Buffer;
        USB_DMA.Ctrl = (pipe->BufferSize << CHNL_BUFF_COUNTER_Pos) |  //Number of bytes to transfer.
                        TXFR_END_OPTION  |       //Enable closing of transfer.
                        TXFR_END_INTERRUPT |       //Enable transfer close interrupt.
                        BUFFER_END_INTERRUPT|       //Enable transfer buffer full interrupt.
                        BURST_LOCK_ENABLE |
                        CHNL_ENABLE;
    }
    
    USB_DMA_INT_CLEAR_REG = USB_DMA_NAK;  //Clear NAK interrupt flag.
    USB_DMA_INT_SET_REG = USB_DMA_NAK;   //Enable NAK interrupt.
    
    pipe->Timeout = 200 + 1 for each INRQ;
    pipe->Status = STATE_PENDING;
    
    USB_PIPE_REG = UNFREEZE_PIPE;    //Unfreeze the pipe.
}


//Here is where I process IN pipe interrupt.
//Essentially it gets called by USB_PipeIntHandler() but for IN pipe type.
void USB_IN_PipeInterrupt(unsigned char pID, unsigned long status)
{
    if(status & NAK)
    {
        ...        //Set register to CLEAR the NAK interrupt.
        ...        //Set register to DISABLE NAK interrupt.
        ...        //Set register to FREEZE pipe.
        pipe->State = PROCESS_PIPE; //Set pipe state to PROCESS_PIPE.
        return;
    }
    
    if(status & RXSTALL)
    {
        //Set register to clear STALL;
        //Set register to reset data toggle;
        return;
    }
    if(status & PIPE_ERROR)
    {
        //This is where we catch error occasionally...
        USB_PipeError();
        return;
    }
}


//Here is where I process IN pipe DMA interrup.t
//Essentially it gets called by USB_Pipe_DMA_IntHandler() for IN pipe type.
void USB_IN_Pipe_DMA_Interrupt(unsigned char pID, )
{
    unsigned long status = DMA_INT_STATUS_REG;
    
    if(status & DMA_STATUS_END_INTERRUPT)
    {
        ...  //Set register to FREEZE pipe.
        ...  //Set register to CLEAR NAK interrupt (pending).
        ...  //Set register to DISABLE NAK interrup.
        
        //We do not wish to bring up NAK interrupt after
        //this stage unnecessarily.
        
        //Pipe has received data and this data is ready to be processed.
        pipe->Length = pipe->BufferSize - (status >> CHNL_BUFF_COUNTER_Pos);
        pipe->State  = USB_PIPE_STATE_PROCESS; 
    }
    
    if(status & DMA_STATUS_BUFF_END_INTERRUPT)
    {
        //My buffer is full, got to read it up. Pipe is auto frozen as buffer is full.
        //Pipe is auto frozen in the case of DMA complete.
        if(!PipeIsFrozen(pID))
        {
            while(!PipeIsFrozen(pID));
        }
        
        .....  //Set register to DISABLE NAK interrupt.
        
        //Pipe has received data and this data is ready to be processed.
        pipe->Length = pipe->BufferSize;
        pipe->State = USB_PIPE_STATE_PROCESS;
        return;
    }
}

 

Last Edited: Thu. Jan 19, 2017 - 11:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Has no one got any comments on this?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can't help thinking "General Electronics" might not be the right forum to ask. I'd target either a UC3 or SAM forum if I were you. If you pick one I'll move the thread.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok please move it to SAM forums..thankS.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So no one can help on this?

Ok then here are some general questions/wonders about USB:

1) uController is in host mode talking to a composite CDC device. Often with Diagnostic port (IN, OUT, INT), and Modem Data port (IN, OUT) pipes.

2) It seems, the device responds with a lot of NAK to my INs.

3) The ASF code examples do not seem to care about NAKs. They keep INing...this delays other pipes being INed. Are they using timeout as a bailout?

4) If I unfreeze all pipes in a for loop, which pipe gets to actually send IN/OUT request first? How does USB macro handle that?

5) If I raise NAK interrupt upon receiving NAK and then in the interrupt routine I freeze the pipe, it seems only then other pipes get a chance to IN. This gives great response with modem device, as I said, around 2mpbs or so data transfer I see pipe error.

6) Things seem to improve a little when I placed coupling (22+22) = 44pf Caps across D+ and D- lines to GND plane.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It has become clear to me that Atmel's usb hardware does not stop INing on the same pipe upon receiving NAK. I have to raise NAK interrupt and freeze the pipe. Only then other unfrozen pipe waiting in line gets a chance to IN or OUT.