2
\$\begingroup\$

i made some kind of a meteo station. I get some temperatures (celsius degrees), UV radiation levels (UV index), solar radiation (W/m\$^2\$) and soil moisture. These values are shown in a C# application GUI, and are written to a text file every minute.

I used 6 sensors:

  • 2 analogue temperature sensors MCP9700
  • 2 analogue solar radiation sensors by Davis
  • 1 resistive soil moisture sensor by Davis
  • 1 analogue UV radiation sensor (don't have this one yet)

I used PIC24FJ256GB106 for:

  • sampling the 6 analogue voltages from the sensors
  • making an USB connection with the PC (transfer mode - interrupt) for sending ADC samples to the C# application on PC

The board is self powered (not powered over bus).

The board with op amps, is for amplifying the signals from sensors.

The problem with this meteo station is that it worked ok for about two weeks. After that, i got some connection issues over the USB.

From time to time, the C# application can't send data to the uC. But, after a while (from a minute, to three days), the communication is working properly again. For long time debugging, i created a log file(err.log) for writing errors. I found that the problem is in my wrapper function WriteFileManagedBuffer(...) for function WriteFile(...).

The wrapper function is used for preventing the USB communication to block. In this function a timeout interval is set, and even if I set a longer timeout interval, it still elapses.

In the main loop i always check if a write or read error to USB handles occurs (I check this every 10 seconds). If an error occurs I try to remake the connection. And even with this, it still takes alot of time to remake the connection and my error log file gets bigger than the file for actual data.

I used microchip's USB framework for making this application.

I uploaded the source code for the C# application, for uC board, a simple schematic, and a err.log file that contains some errors.

The C# application is created with Microsoft Visual C# Express edition 2008, the uc source code is created with Mplab Ide V8.84 (C30 compiler). The meteo station runs on Windows XP Professional, Service Pack 2.

I hope that someone will have enough patience to look over my code. Thanks in advance for the time spent.

Later edit 0.0: First of all thank you for replying.

@jippie Here is the function in C# where I get the timeout error: When the timeout error occurs, the 'case (int)WAIT_TIMEOUT:' from the switch statement is gets executed.

    //--------------------------------------------------------------------------------------------------------------------------
    //FUNCTION: WriteFileManagedBuffer()
    //PURPOSE:  Wrapper function to call WriteFile()
    //
    //INPUT:    Uses managed versions of the same input parameters as WriteFile() uses.
    //
    //OUTPUT:   Returns boolean indicating if the function call was successful or not.
    //          Also returns the number of bytes written. 
    //
    //Notes:    Wrapper function used to call the WriteFile() function.  
    //-------------------------------------------------------------------------------------------------------------------------- 
    public unsafe bool WriteFileManagedBuffer(SafeFileHandle hFile, byte[] OUTBuffer, uint nNumberOfBytesToWrite, ref uint lpNumberOfBytesWritten, IntPtr lpOverlapped)
    {
        DateTime dt = DateTime.Now;
        NativeOverlapped hidOverlapped = new NativeOverlapped();
        IntPtr pOverlapped = IntPtr.Zero;
        IntPtr eventObject = IntPtr.Zero;
        Boolean success = false;
        int result = 0;

        eventObject = CreateEvent(IntPtr.Zero, false, false, "");
        hidOverlapped.OffsetLow = 0;
        hidOverlapped.OffsetHigh = 0;
        hidOverlapped.EventHandle = eventObject;

        try
        {
            pOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(hidOverlapped));  // Allocate some umanaged RAM for the overlapped data structure.
            Marshal.StructureToPtr(hidOverlapped, pOverlapped, false);          // Copy the overlapped structure to the allocated unmanaged RAM

            if (!WriteHandleToUSBDevice.IsInvalid)
            {
                success = WriteFile(hFile, OUTBuffer, nNumberOfBytesToWrite, ref lpNumberOfBytesWritten, pOverlapped);
                result = WaitForSingleObject(eventObject, 1000);//setting the timeout interval  
                if (!success)
                {
                    switch (result)
                    {
                        case (int)WAIT_OBJECT_0: //the state of the specified object is signaled
                            GetOverlappedResult(ReadHandleToUSBDevice, pOverlapped, ref lpNumberOfBytesWritten, false);
                            success = true;
                            break;
                        case (int)WAIT_TIMEOUT: //the timeout interval elapsed, and the object's state is nonsignaled
                            Console.WriteLine("WriteFile exceeded timout. Err: " + result + " @ " + dt.ToString() + ":" + dt.Millisecond);
                            log("WriteFile exceeded timeout. Err: " + result);
                            CancelIo(ReadHandleToUSBDevice);
                            success = false;
                            break;
                        default:
                            Console.WriteLine("WriteFile unknown error. Err: " + result + " @ " + dt.ToString() + ":" + dt.Millisecond);
                            log("WriteFile unknown error. Err: " + result);
                            CancelIo(ReadHandleToUSBDevice);
                            success = false;
                            break;
                    }
                }
            }
        }
        catch
        {
            Console.WriteLine("Write File Exception. @ " + dt.ToString() + ":" + dt.Millisecond);
            log("Write File Exception");
            success = false;
        }
        finally
        {
            if (pOverlapped != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(pOverlapped);
            }
        }

        return success;
    }

This is the interrupt code from mcu. From here USBHandlePackets() is called, where i actually write the ADC values to buffer to be sent over USB.

#if defined(USB_INTERRUPT) 
  #if defined(__18CXX)
    void USBDeviceTasks(void)
  #elif defined(__C30__)
  void __attribute__((interrupt,auto_psv)) _USB1Interrupt()
#elif defined(__PIC32MX__)
  void __attribute__((interrupt(),vector(45))) _USB1Interrupt( void ) 
#endif
#else
void USBDeviceTasks(void)
#endif
{
   BYTE i;

#ifdef USB_SUPPORT_OTG
if (USBOTGSRPIsReady())//SRP Time Out Check
{
    if (USBT1MSECIF && USBT1MSECIE)
    {
        if (USBOTGGetSRPTimeOutFlag())
        {
            if (USBOTGIsSRPTimeOutExpired())
            {
                USB_OTGEventHandler(0,OTG_EVENT_SRP_FAILED,0,0);
            }       
        }

        USBClearInterruptFlag(USBT1MSECIFReg,USBT1MSECIFBitNum);//Clear Interrupt Flag
    }
}
#endif

#if defined(USB_POLLING)
//If the interrupt option is selected then the customer is required
//  to notify the stack when the device is attached or removed from the
//  bus by calling the USBDeviceAttach() and USBDeviceDetach() functions.
if (USB_BUS_SENSE != 1)
{
     // Disable module & detach from bus
     U1CON = 0;             

     // Mask all USB interrupts              
     U1IE = 0;          

     //Move to the detached state                  
     USBDeviceState = DETACHED_STATE;

     #ifdef  USB_SUPPORT_OTG    
         //Disable D+ Pullup
         U1OTGCONbits.DPPULUP = 0;

         //Disable HNP
         USBOTGDisableHnp();

         //Deactivate HNP
         USBOTGDeactivateHnp();

         //If ID Pin Changed State
         if (USBIDIF && USBIDIE)
         {  
             //Re-detect & Initialize
              USBOTGInitialize();

              //Clear ID Interrupt Flag
              USBClearInterruptFlag(USBIDIFReg,USBIDIFBitNum);
         }
     #endif

     #ifdef __C30__
         //USBClearInterruptFlag(U1OTGIR, 3); 
     #endif
        //return so that we don't go through the rest of 
        //the state machine
     USBClearUSBInterrupt();
     return;
}

#ifdef USB_SUPPORT_OTG
//If Session Is Started Then
else
{
    //If SRP Is Ready
    if (USBOTGSRPIsReady())
    {   
        //Clear SRPReady
        USBOTGClearSRPReady();

        //Clear SRP Timeout Flag
        USBOTGClearSRPTimeOutFlag();

        //Indicate Session Started
        UART2PrintString( "\r\n***** USB OTG B Event - Session Started  *****\r\n" );
    }
}
#endif  //#ifdef USB_SUPPORT_OTG

//if we are in the detached state
if(USBDeviceState == DETACHED_STATE)
{
    //Initialize register to known value
    U1CON = 0;                          

    // Mask all USB interrupts
    U1IE = 0;                                

    //Enable/set things like: pull ups, full/low-speed mode, 
    //set the ping pong mode, and set internal transceiver
    SetConfigurationOptions();

    // Enable module & attach to bus
    while(!U1CONbits.USBEN){U1CONbits.USBEN = 1;}

    //moved to the attached state
    USBDeviceState = ATTACHED_STATE;

    #ifdef  USB_SUPPORT_OTG
        U1OTGCON |= USB_OTG_DPLUS_ENABLE | USB_OTG_ENABLE;  
    #endif
}
#endif  //#if defined(USB_POLLING)

if(USBDeviceState == ATTACHED_STATE)
{
    /*
     * After enabling the USB module, it takes some time for the
     * voltage on the D+ or D- line to rise high enough to get out
     * of the SE0 condition. The USB Reset interrupt should not be
     * unmasked until the SE0 condition is cleared. This helps
     * prevent the firmware from misinterpreting this unique event
     * as a USB bus reset from the USB host.
     */

    if(!USBSE0Event)
    {
        USBClearInterruptRegister(U1IR);// Clear all USB interrupts
        #if defined(USB_POLLING)
            U1IE=0;                        // Mask all USB interrupts
        #endif
        USBResetIE = 1;             // Unmask RESET interrupt
        USBIdleIE = 1;             // Unmask IDLE interrupt
        USBDeviceState = POWERED_STATE;
    }
}

#ifdef  USB_SUPPORT_OTG
    //If ID Pin Changed State
    if (USBIDIF && USBIDIE)
    {  
        //Re-detect & Initialize
        USBOTGInitialize();

        USBClearInterruptFlag(USBIDIFReg,USBIDIFBitNum);
    }
#endif

/*
 * Task A: Service USB Activity Interrupt
 */
if(USBActivityIF && USBActivityIE)
{
    USBClearInterruptFlag(USBActivityIFReg,USBActivityIFBitNum);
    #if defined(USB_SUPPORT_OTG)
        U1OTGIR = 0x10;        
    #else
        USBWakeFromSuspend();
    #endif
}

/*
 * Pointless to continue servicing if the device is in suspend mode.
 */
if(USBSuspendControl==1)
{
    USBClearUSBInterrupt();
    return;
}

/*
 * Task B: Service USB Bus Reset Interrupt.
 * When bus reset is received during suspend, ACTVIF will be set first,
 * once the UCONbits.SUSPND is clear, then the URSTIF bit will be asserted.
 * This is why URSTIF is checked after ACTVIF.
 *
 * The USB reset flag is masked when the USB state is in
 * DETACHED_STATE or ATTACHED_STATE, and therefore cannot
 * cause a USB reset event during these two states.
 */
if(USBResetIF && USBResetIE)
{
    USBDeviceInit();

    //Re-enable the interrupts since the USBDeviceInit() function will
    //  disable them.  This will do nothing in a polling setup
    USBUnmaskInterrupts();

    USBDeviceState = DEFAULT_STATE;

    #ifdef USB_SUPPORT_OTG
         //Disable HNP
         USBOTGDisableHnp();

         //Deactivate HNP
         USBOTGDeactivateHnp();
    #endif

    USBClearInterruptFlag(USBResetIFReg,USBResetIFBitNum);
}

/*
 * Task C: Service other USB interrupts
 */
if(USBIdleIF && USBIdleIE)
{ 
    #ifdef  USB_SUPPORT_OTG 
        //If Suspended, Try to switch to Host
        USBOTGSelectRole(ROLE_HOST);
    #else
        USBSuspend();
    #endif

    USBClearInterruptFlag(USBIdleIFReg,USBIdleIFBitNum);
}

if(USBSOFIF)
{
    if(USBSOFIE)
    {
        USB_SOF_HANDLER(EVENT_SOF,0,1);
    }    
    USBClearInterruptFlag(USBSOFIFReg,USBSOFIFBitNum);

    #if defined(USB_ENABLE_STATUS_STAGE_TIMEOUTS)
        //Supporting this feature requires a 1ms timebase for keeping track of the timeout interval.
        #if(USB_SPEED_OPTION == USB_LOW_SPEED)
            #warning "Double click this message.  See inline code comments."
            //The "USB_ENABLE_STATUS_STAGE_TIMEOUTS" feature is optional and is
            //not strictly needed in all applications (ex: those that never call 
            //USBDeferStatusStage() and don't use host to device (OUT) control
            //transfers with data stage).  
            //However, if this feature is enabled and used, it requires a timer 
            //(preferrably 1ms) to decrement the USBStatusStageTimeoutCounter.  
            //In USB Full Speed applications, the host sends Start-of-Frame (SOF) 
            //packets at a 1ms rate, which generates SOFIF interrupts.
            //These interrupts can be used to decrement USBStatusStageTimeoutCounter as shown 
            //below.  However, the host does not send SOF packets to Low Speed devices.  
            //Therefore, some other method  (ex: using a general purpose microcontroller 
            //timer, such as Timer0) needs to be implemented to call and execute the below code
            //at a once/1ms rate, in a low speed USB application.
            //Note: Pre-condition to executing the below code: USBDeviceInit() should have
            //been called at least once (since the last microcontroller reset/power up), 
            //prior to executing the below code.
        #endif

        //Decrement our status stage counter.
        if(USBStatusStageTimeoutCounter != 0u)
        {
            USBStatusStageTimeoutCounter--;
        }
        //Check if too much time has elapsed since progress was made in 
        //processing the control transfer, without arming the status stage.  
        //If so, auto-arm the status stage to ensure that the control 
        //transfer can [eventually] complete, within the timing limits
        //dictated by section 9.2.6 of the official USB 2.0 specifications.
        if(USBStatusStageTimeoutCounter == 0)
        {
            USBCtrlEPAllowStatusStage();    //Does nothing if the status stage was already armed.
        } 
    #endif
}
if(USBStallIF && USBStallIE)
{
    USBStallHandler();
}

if(USBErrorIF && USBErrorIE)
{
    USB_ERROR_HANDLER(EVENT_BUS_ERROR,0,1);
    USBClearInterruptRegister(U1EIR);               // This clears UERRIF

    //On PIC18, clearing the source of the error will automatically clear
    //  the interrupt flag.  On other devices the interrupt flag must be 
    //  manually cleared. 
    #if defined(__C32__) || defined(__C30__)
        USBClearInterruptFlag( USBErrorIFReg, USBErrorIFBitNum );
    #endif
}

/*
 * Pointless to continue servicing if the host has not sent a bus reset.
 * Once bus reset is received, the device transitions into the DEFAULT
 * state and is ready for communication.
 */
if(USBDeviceState < DEFAULT_STATE)
{
    USBClearUSBInterrupt();
    return; 
}  

/*
 * Task D: Servicing USB Transaction Complete Interrupt
 */
if(USBTransactionCompleteIE)
{
    for(i = 0; i < 4u; i++) //Drain or deplete the USAT FIFO entries.  If the USB FIFO ever gets full, USB bandwidth 
    {                       //utilization can be compromised, and the device won't be able to receive SETUP packets.
        if(USBTransactionCompleteIF)
        {
            //Save and extract USTAT register info.  Will use this info later.
            USTATcopy.Val = U1STAT;
            endpoint_number = USBHALGetLastEndpoint(USTATcopy);

            USBClearInterruptFlag(USBTransactionCompleteIFReg,USBTransactionCompleteIFBitNum);

            //Keep track of the hardware ping pong state for endpoints other
            //than EP0, if ping pong buffering is enabled.
            #if (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG) 
                if(USBHALGetLastDirection(USTATcopy) == OUT_FROM_HOST)
                {
                    ep_data_out[endpoint_number].bits.ping_pong_state ^= 1;
                }   
                else
                {
                    ep_data_in[endpoint_number].bits.ping_pong_state ^= 1;
                }         
            #endif    

            //USBCtrlEPService only services transactions over EP0.
            //It ignores all other EP transactions.
            if(endpoint_number == 0)
            {
                USBCtrlEPService();
            }
            else
            {
                USB_TRANSFER_COMPLETE_HANDLER(EVENT_TRANSFER, (BYTE*)&USTATcopy.Val, 0);
            }
        }//end if(USBTransactionCompleteIF)
        else
            break;  //USTAT FIFO must be empty.
    }//end for()
}//end if(USBTransactionCompleteIE)   

USBClearUSBInterrupt();

USBHandlePackets();

}//end of USBDeviceTasks()

And here is the USBHandlePackets() function:

void USBHandlePackets()
{   
if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) return;//        
User Application USB tasks

if(!HIDRxHandleBusy(USBOutHandle))              //Check if data was received from the host.
{
    switch(ReceivedDataBuffer[0])               //Look at the data the host sent, to see what kind of application specific command it sent.
    {
        case 0x37:  //Read POT command.  Uses ADC to measure an analog voltage on one of the ANxx I/O pins, and returns the result to the host
            {
                if(!HIDTxHandleBusy(USBInHandle))
                { 
                    if(contor.Val==1023) test_val=0;
                    if(contor.Val==0) test_val=1;

                    if(test_val==0) contor.Val--;
                    if(test_val==1) contor.Val++;


                    ToSendDataBuffer[0]  = 0x37;

                    ToSendDataBuffer[1]  = w.Val;   //Measured analog voltage LSB
                    ToSendDataBuffer[2]  = w.Val>>8;    //Measured analog voltage MSB

                    ToSendDataBuffer[3]  = w1.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[4]  = w1.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[5]  = w2.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[6]  = w2.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[7]  = w3.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[8]  = w3.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[9]  = w4.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[10] = w4.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[11] = w5.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[12] = w5.Val>>8;   //Measured analog voltage MSB


                    /*ToSendDataBuffer[1]  = contor.Val;    //Measured analog voltage LSB
                    ToSendDataBuffer[2]  = contor.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[3]  = contor.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[4]  = contor.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[5]  = contor.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[6]  = contor.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[7]  = contor.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[8]  = contor.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[9]  = contor.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[10] = contor.Val>>8;   //Measured analog voltage MSB

                    ToSendDataBuffer[11] = contor.Val;  //Measured analog voltage LSB
                    ToSendDataBuffer[12] = contor.Val>>8;   //Measured analog voltage MSB*/

                    USBInHandle = HIDTxPacket(HID_EP,(BYTE*)&ToSendDataBuffer[0],64);
                }                   
            }
            break;
    }
    USBOutHandle = HIDRxPacket(HID_EP,(BYTE*)&ReceivedDataBuffer,64);//Re-arm the OUT endpoint for the next packet
}
} 

@ajs410 You can find the original code (C# and mcu code) in Microchip Solutions v2012-04-03/USB/Device - HID - Custom Demos. So, it's microchip's driver (USB stack from microchip).

\$\endgroup\$
  • \$\begingroup\$ I think you'll find more subject matter experts at stackoverflow.com \$\endgroup\$ – jippie Jul 26 '12 at 19:25
  • 3
    \$\begingroup\$ Adrian - You included a lot of files, probably nobody will take the effort looking at them if you can't tell us in more detail where to look and what to look for. Please add the related snippets of the code to your question. \$\endgroup\$ – jippie Jul 26 '12 at 21:46
  • 1
    \$\begingroup\$ What driver are you using? Microchip's or WinUSB? Also, unless you know you need an interrupt endpoint, you should use bulk. USB interrupt endpoints do not mean what you think they mean in terms of classical embedded processing and 99% of the time a bulk endpoint will work just the same and with potentially fewer headaches. \$\endgroup\$ – ajs410 Jul 26 '12 at 22:48
  • 1
    \$\begingroup\$ @jippie Lets show embedded systems a little love. \$\endgroup\$ – Kortuk Aug 4 '12 at 11:43
  • 1
    \$\begingroup\$ To get good answers, I suggest you distill your question down to no more than one screen full. I have some experience with PICs and USB but not a spare month to read your question. \$\endgroup\$ – Olin Lathrop Aug 4 '12 at 11:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.