CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Interrupt Driven PSP Port Communication Problems

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
bfemmel



Joined: 18 Jul 2004
Posts: 40
Location: San Carlos, CA.

View user's profile Send private message Visit poster's website

Interrupt Driven PSP Port Communication Problems
PostPosted: Mon Apr 18, 2005 10:38 am     Reply with quote

I am having problems with communications over the PSP port. We use it to communicate between a Windows CE system and a PIC18F6520. I recently rewrote the PSP routines to be interrupt driven since we were having problems with the previous communications routines after we revised the CE code from VB to C++. The C++ code was much faster than the VB code and showed a bug in the code prompting the rewrite.

The first problem was that we started missing bytes and having the communications hang. It seems that the CE code was so fast that if the PIC received or sent a byte and then set the external ready bit to signal the CE computer that the CE computer may have serviced the PSP port again before the PIC had left the interrupt routine. That at first did not sound like a problem to me, I just figured that the PIC would exit the interrupt and reenter right away to service the port again. But this was not happening. When I checked the Assembly listing I noticed that the code clears the PSP interrupt bit at the end of the routine right before it jumps back to the interrupt handler:

BCF F9E.7
GOTO 0084

This seems a bit strange to me. Is there a simple way around this? I would think that the interrupt should be cleared on entering the routine not on exiting. Right now I am setting a flag in the INT _PSP instead of setting the external PIC_Ready bit and then setting that external signal in my main loop outside of the interrupt. Of course this makes the PSP interrupt kind of a mute thing. I would do just as well with a polled routine because of that problem.

The second problem, and one that I have not fixed yet, is that even after we fixed the first problem, we still get a lot of errors in the communications, usually bad data in the command or status response bytes. We can lessen the errors by sprinkling the C++ code with 1 millisecond sleep statements but why do we need to do that?

Here is an example of the C++ Code that reads from and writes to the PIC without any sleep statements. The 0x188 port address is the external PIC Ready bit which is cleared in hardware whenever a read or write is done to port 0x180 which is the PSP port of the PIC. When we insert sleep(1) statements in between all the reads and writes we end up with communications that is a lot more reliable.

Code:
      /* Send a two byte command to the PIC */
      while ((pPIC->m_prlcRead(0x188) & 0x01) == 0);
      pPIC->m_prlcWrite(0x180, 0x52);
      while ((pPIC->m_prlcRead(0x188) & 0x01) == 0);
      pPIC->m_prlcWrite(0x180, 0x50);
     
      /* Read the status response from the PIC */
      while ((pPIC->m_prlcRead(0x188) & 0x01) == 0);
      sData = pPIC->m_prlcRead(0x180) << 8;
      while ((pPIC->m_prlcRead(0x188) & 0x01) == 0);
      sData += pPIC->m_prlcRead(0x180);

      if (sData == 0x7270)    // Checks for bad status returned
      {                       // Read two words of data from PIC
         while ((pPIC->m_prlcRead(0x188) & 0x01) == 0);
         asBlock[0] = pPIC->m_prlcRead(0x180) << 8;
         while ((pPIC->m_prlcRead(0x188) & 0x01) == 0);
         asBlock[0] += pPIC->m_prlcRead(0x180);
         WAITREADSHORT;

         while ((pPIC->m_prlcRead(0x188) & 0x01) == 0);
         asBlock[1] = pPIC->m_prlcRead(0x180) << 8;
         while ((pPIC->m_prlcRead(0x188) & 0x01) == 0);
         asBlock[1] += pPIC->m_prlcRead(0x180);
      }


Here is the code from my PSP port service routine.

Code:
#INT_PSP
void PSP_Service( void ) {

   if ( psp_input_full() )   {    // get the byte sent from host
   
      if ( psp_overflow() )  {               // something definitely went wrong
         PSP_Buffer[0] = PSP_DATA;           // clear out the register
         PSP_BufferLen = PSP_BUFFER_ERROR;   // signal an error to reset communication
      }  /* end of ( psp_overflow() ) */

      else   {                               // if it's not an overflow then input the command and data
         if ( PSP_BufferIndex == 0 )    {    // If this is the first byte received
            Cmd_Timeout = CMD_TIMOUT_DELAY;  // Reset the Command Timeout to track a RCV that hangs
            commStatFlag = PSP_RCV;          // Let's others know we are in the receive state
         }  /* end of PSP_BufferIndex ==0 */
         
         if (PSP_BufferIndex < MAX_PSP_BUFFER_SIZE)    {    // should always run through here if all is well
            PSP_Buffer[PSP_BufferIndex++] = PSP_DATA;       // put the data into a buffer
         }
         else                                   // Buffer overrun error
            PSP_BufferLen = PSP_BUFFER_ERROR;   // Set this to indicate an error and reset communications
           
         if (PSP_BufferIndex == 2)  {
            switch ( PSP_Buffer[0] )   {
               case 0x41:     // Read an ADC value
               case 0x53:     // Read motor speed
                  PSP_BufferLen = 2;
                  break;

               case 0x48:     // Set heater power
                  PSP_BufferLen = 4;
                  break;

               default:       // unrecognized command
                  PSP_BufferLen = PSP_BUFFER_ERROR;      // Set this to indicate error and reset PSP
                  break;   
            }  /* end command length switch */
         
         }  /* end of PSP_Buffer == 2 */
     
         if (PSP_BufferLen == PSP_BUFFER_ERROR)  {    // if there was an error, reset comm parameters
            Start_PSP_Wait();                         // set the comm status to wait and reset the buffers
         } 
         else if (PSP_BufferLen > PSP_BufferIndex)    // have not reached end data yet so set PIC_Ready
            setReadyBit = TRUE;                       // Next time through main loop, set PIC Ready
//            Set_PIC_Ready();                          // sets the external bit to signal the host
         else
            rcvComplete = TRUE;     // TRUE signals that all bytes have been received and that
                                    // this is the end of the input string!
                                    // we won't set the PIC_READY until the command is complete and the
                                    // transmit buffer is set up. That will happen in the CheckCommand routine
      }  /* end of psp_overflow() = False */
   }  /* end of psp_input_full() */
   
   else if ( !psp_output_full() )    {                // Check to see if the output buffer is empty
      if (PSP_BufferLen > PSP_BufferIndex) {          // if there is still more to send ...
         PSP_DATA = PSP_Buffer[PSP_BufferIndex++];    // put the buffer data into the port and increment counter
         setReadyBit = TRUE;                          // Next time through main loop, set PIC Ready
      }  /* end of "still more data to send" */
      else                                            // if the transmission is complete ...
         Start_PSP_Wait();                            // set the comm status to wait and reset the buffers
   }  /* end of send byte */
}  /* end of PSP_Service() Interrupt */


Does anyone have any ideas as to what I should check next in trying to debug this problem? I am at a loss for what to look at next. I know that this is difficult without the schematic for the hardware and a more complete listing from my code but given the fact that the code worked fine with the VB code and now has a problem with the faster C++ code, I am assuming that the parts of the code that empty the buffer, perform the command and refill the buffer with command response status and data are still working fine. I am just looking for a direction to go in debugging this.

Many Thanks - Bruce
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Apr 18, 2005 12:22 pm     Reply with quote

Quote:

I noticed that the code clears the PSP interrupt bit at the end of the
routine right before it jumps back to the interrupt handler:
Is there a simple way around this?


Do a search in the CCS manual for the keyword: NOCLEAR
bfemmel



Joined: 18 Jul 2004
Posts: 40
Location: San Carlos, CA.

View user's profile Send private message Visit poster's website

PostPosted: Mon Apr 18, 2005 2:41 pm     Reply with quote

PCM programmer;

Thanks for the tip on instructing the compiler to not clear the int flag. I added the NOCLEAR option to the interrupt declaration and then added a bit clear to the beginning of the routine like this;
Code:

   bit_clear(*0xF9E,7);            // clears the interrupts at the beginning of the routine instead of the end

and that part works just peachy. Now the interrupt runs in a tight loop to get the data as required instead of shelling out to the main loop to say it is ready for another byte.

Now if I could only get the data going back and forth to be as good. It seems that when we rush things using the interrupts and not putting the sleep in the C++ code on the host that the data transmission gets garbled a good percentage of the time.

I am still very confused. Confused I guess I need to break down and get a logic analyzer.

Any more ideas from anyone would be welcome.

Bruce
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group