Aplicación de interfaz C # con PIC24FJ256GB106 a través de USB

2

hice una especie de estación de meteo. Obtengo algunas temperaturas (grados centígrados), niveles de radiación UV (índice UV), radiación solar (W / m \ $ ^ 2 \ $) y humedad del suelo. Estos valores se muestran en una GUI de la aplicación C # y se escriben en un archivo de texto cada minuto.

Utilicé 6 sensores:

  • 2 sensores de temperatura analógicos MCP9700
  • 2 sensores de radiación solar analógicos de Davis
  • 1 sensor de humedad del suelo resistivo por Davis
  • 1 sensor de radiación UV analógico (todavía no tiene este)

Utilicé PIC24FJ256GB106 para:

  • muestreando los 6 voltajes analógicos de los sensores
  • establecer una conexión USB con la PC (modo de transferencia - interrupción) para enviar muestras ADC a la aplicación C # en la PC

La placa es autoalimentada (no se alimenta a través del bus).

La placa con amplificadores operacionales, es para amplificar las señales de los sensores.

El problema con esta estación de meteo es que funcionó bien durante aproximadamente dos semanas. Después de eso, tengo algunos problemas de conexión a través del USB.

De vez en cuando, la aplicación C # no puede enviar datos a la unidad uC. Pero, después de un tiempo (de un minuto a tres días), la comunicación vuelve a funcionar correctamente. Para una depuración prolongada, creé un archivo de registro (err.log) para escribir errores. Encontré que el problema está en mi función de contenedor WriteFileManagedBuffer (...) para la función WriteFile (...).

La función de envoltura se usa para evitar que la comunicación USB se bloquee. En esta función, se establece un intervalo de tiempo de espera, e incluso si configuro un intervalo de tiempo de espera más largo, todavía transcurre.

En el bucle principal siempre verifico si ocurre un error de escritura o lectura en los identificadores USB (lo verifico cada 10 segundos). Si ocurre un error, intento rehacer la conexión. E incluso con esto, todavía se necesita mucho tiempo para rehacer la conexión y mi archivo de registro de errores se vuelve más grande que el archivo para los datos reales.

Usé el marco USB de microchip para hacer esta aplicación.

Subí el código fuente de la aplicación C #, para la placa de control de usuario, un esquema simple y un archivo err.log que contiene algunos errores

La aplicación C # se crea con Microsoft Visual C # Express edición 2008, el código fuente uc se crea con Mplab Ide V8.84 (compilador C30). La estación meteo se ejecuta en Windows XP Professional, Service Pack 2.

Espero que alguien tenga suficiente paciencia para revisar mi código. Gracias de antemano por el tiempo dedicado.

Edición posterior 0.0: En primer lugar, gracias por responder.

@jippie Aquí está la función en C # donde obtengo el error de tiempo de espera: Cuando se produce el error de tiempo de espera, se ejecuta el 'case (int) WAIT_TIMEOUT:' de la instrucción switch.

    //--------------------------------------------------------------------------------------------------------------------------
    //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;
    }

Este es el código de interrupción de mcu. Desde aquí se llama USBHandlePackets (), donde realmente escribo los valores de ADC en el búfer para enviarlos a través de 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()

Y aquí está la función USBHandlePackets ():

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 Puede encontrar el código original (código C # y mcu) en Microchip Solutions v2012-04-03 / USB / Device - HID - Custom Demos. Entonces, es el controlador de microchip (pila USB de microchip).

    
pregunta Adrian

0 respuestas

Lea otras preguntas en las etiquetas