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

Serial Communication to Device TX Line Issue

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



Joined: 25 Mar 2008
Posts: 13

View user's profile Send private message

Serial Communication to Device TX Line Issue
PostPosted: Wed Jun 04, 2008 2:39 pm     Reply with quote

I have an application that communicates with a device via serial port UART. In the field tests, units have stopped communicating for no apparent reason. The external device has a display that indicates a valid polling message has been recognized. The status LED on the PIC16F689 board indicates that the processor is operating properly. The #USERS232 Statement contains the ERRORS parameter.

My tests in the lab have run hundreds of thousands of communication sequences without creating this malfunction. Several different meter setups as well. But, placing them into the field has somehow created
a change.

The UART TX and RX lines are optoisolated from the meter. It appears that noise or something's affecting the serial TX line. A power reset will bring it back to life.

Can the TX line on the UART lock up? If so, how can it be reset?

I'm reluctant to post code because it involves NDA with the protocols, etc.

This board also utilizes the second RS232 port using the software UART.

Has anyone had experience filtering noise from UART signal lines?

Thanks in Advance!
RLScott



Joined: 10 Jul 2007
Posts: 465

View user's profile Send private message

Re: Serial Communication to Device TX Line Issue
PostPosted: Wed Jun 04, 2008 2:49 pm     Reply with quote

Are you checking for and clearing framing and overrun errors?

Robert Scott
Real-Time Specialties
superflycfi



Joined: 25 Mar 2008
Posts: 13

View user's profile Send private message

PostPosted: Wed Jun 04, 2008 3:00 pm     Reply with quote

No checking at this time, please explain method? I assumed the errors are reset by the compiler. I use a character array for input buffers and the character framing, character buffer counter reset on error, and crc calculations are taken care of.

It's evident that the PIC to meter TX communication isn't happening. So, it has to be:

1. Pic's not transmitting the characters out.
2. Noise is disturbing the line.
3. Meter's not receiving or recognizing.

Problem is, I cannot duplicate it in the lab! And as soon the the installation in the field is disturbed, they work again!

Thanks!

I'm going to post modified code here in a bit.
superflycfi



Joined: 25 Mar 2008
Posts: 13

View user's profile Send private message

Code
PostPosted: Wed Jun 04, 2008 3:06 pm     Reply with quote

Code:


#include "16F689.h"

#fuses XT,NOWDT

#use delay(clock=4000000)

#use rs232(baud=9600,xmit=PIN_B7,rcv=PIN_B5,ERRORS,STREAM=COM_A,BRGH1OK) // COM A Meter
#use rs232(baud=9600,xmit=PIN_B6,rcv=PIN_B4,STREAM=COM_B) // COM B C3I 485

#define POLY 0x8408


unsigned int32 loopcounter;


byte unit_id;
byte seconds;
byte holdseconds;
byte retry_count;

long crc16(char *data_p, long length);
int poll_psem();
void checkPSEMTerminal();
void check485terminal();
write_485(byte mchar);

byte m_inbuffer[54];
byte inbuffer[11];
byte m_poll[20];
byte psem_request;
byte x;
byte y;
byte psem_size;
byte check_high;
byte check_low;
byte oo;
byte valid_data;
byte psem_busy;
byte timer_since_data;
long crc;
int i;
byte a;

//******************************************************************************
// MAIN FUNCTION FOR FIRMWARE
//******************************************************************************

void main()
{
 

  set_tris_c(3);
  set_tris_b(48);

  setup_wdt(WDT_2304MS);
  valid_data=0;
  output_low(PIN_C3);
  output_low(PIN_C2); // RS485 Enable low
  timer_since_data=0;
  loopcounter=0;
  x=0;
  y=0;
  psem_busy=0;
  seconds=0;


  for(;;)
  {
   
   output_low(PIN_C2); // RS485 Enable low   
   
 
   restart_wdt();

    if(psem_busy==1)
    {
      checkPSEMTerminal();
    }


   
   
    if(psem_busy!=1)
    {
      check485terminal();
    }



    loopcounter++;

    if(loopcounter >=24000)
    {
      seconds++;
      loopcounter=0;
    }

    if(seconds !=holdseconds)
    {
      holdseconds=seconds;

     
      timer_since_data++;
      if(timer_since_data >=120)
      {
        timer_since_data=0;
   
   for(;;){
   output_high(PIN_C3);
   delay_ms(200);
   output_low(PIN_C3);
   delay_ms(200);
   
   
   }
   
   
   
   
   
    // create watchdog event
 
 
 
 
 
 //reset_cpu();
      }

      if(seconds > 60)
        seconds=0;

      unit_id=1;

      if(input(PIN_C0) == 1 && input(PIN_C1) ==0)unit_id=2;
      if(input(PIN_C0) == 0 && input(PIN_C1) ==1)unit_id=3;


      if(seconds ==10 || seconds==45)
      {
       
   
        poll_psem();


   
       }


      if(seconds==12)psem_busy=0;
      if(seconds==47)psem_busy=0;
      if(seconds==15)psem_busy=0;
      if(seconds==50)psem_busy=0;

    }
  } 
}  // END MAIN

//******************************************************************************
// Create PSEM Poll For Serial Data From Meter
//******************************************************************************
int poll_psem()
{
  output_low(PIN_C3);
  delay_ms(500);
  output_high(PIN_C3);
  delay_ms(500);
  output_low(PIN_C3);
   
 
 
  output_low(PIN_C2);
  valid_data=0;
  psem_busy=1;
  x = 0;

 

  m_poll[0]=238;
  m_poll[1] = 0;
  m_poll[2] = 0;
  m_poll[3] = 0;
  m_poll[4] = 0;
  m_poll[5] = 8;
  m_poll[6] = 63;
  m_poll[7] = 8;
  m_poll[8] = 19;
  m_poll[9] = 0;
  m_poll[10]= 0;
  m_poll[11] = 0;
  m_poll[12] = 0;
  m_poll[13] = 32;

  crc = crc16(m_poll,14);
  m_poll[14] = make8(crc,1);
  m_poll[15] = make8(crc,0);

  for(oo=0;oo<16;oo++)
  {
    fputc(m_poll[oo],COM_A);
  }

return 0;
}

//******************************************************************************
// crc 16 Calculation Function
//******************************************************************************

  long crc16(char *data_p, long length)
   {
      unsigned char i;
      unsigned long xdata;
      unsigned long crc;

      crc = 0xffff;

      if (length == 0 || length < 0)
         return (~crc);

      do
      {
         for (i=0, xdata=(unsigned int)0xff & *data_p++;
               i < 8; i++, xdata >>= 1) {
            if ((crc & 0x0001) ^ (xdata & 0x0001))
               crc = (crc >> 1) ^ POLY;
            else  crc >>= 1;
         }
      } while (--length);

      crc = ~crc;
      xdata = crc;
      crc = (crc << 8) | ((xdata >> 8) & 0xff);

      return (crc);
   }



//******************************************************************************
// Reads PSEM Serial Data From Meter
//******************************************************************************
void checkPSEMTerminal()
{
  byte chRead;

  if(kbhit(COM_A))
  {

    chRead = fgetc(COM_A);
    m_inbuffer[x] = chRead;
    m_inbuffer[x+1]=0;
    x++;
    if(x >=52)x=0;
   
    if(x==1)
    {
      if(chRead==6)
      {
        x=0;
      }

      if(chRead==21)
      {
       
        x=0;

   for(a=0;a<20;a++){
   restart_wdt();
   output_high(PIN_C3);
   delay_ms(200);
   output_low(PIN_C3);
   delay_ms(200);
   
   
   }
   a=0;



       // retry_count++;
       // if(retry_count < 3)poll_psem();
      }

    } // x is 1

  if(x >=6)
  {
    psem_request=1;
    if(psem_request !=0)
    {
      if(x==m_inbuffer[5]+8)
      {
        psem_size = m_inbuffer[5];
        crc = crc16((char*)&m_inbuffer[0],m_inbuffer[5]+6); // was +6
        check_high = make8(crc,1);
        check_low = make8(crc,0);

        if(m_inbuffer[psem_size+6]==check_high)
        {
          if(m_inbuffer[psem_size+7]==check_low)
          {

   
           
            timer_since_data=0;
            retry_count=0;
            delay_ms(5);
            fputc(6,COM_A); // ACK To Meter
            psem_busy=0;
            valid_data=1;
            output_high(PIN_C3);
          }
        }
      }
    }
  }


} // KBHIT

}  // END FUNCTION

//******************************************************************************
// CHECK RS 485 Terminal For Interrogation By Device
//******************************************************************************



void check485terminal()
{
  byte dhRead;

  if(kbhit(COM_B))
  {
    dhRead = fgetc(COM_B);
    inbuffer[y]=dhRead;
    inbuffer[y+1]=0;
    y++;
    if(y>=11)y=0;

    if(y==1)
    {
      if(inbuffer[0]!=238)
      {
        y=0;
      }
    }

    if(y > 2)
    {
      if(y>=inbuffer[1])
      {
        y=0;
        crc = crc16(&inbuffer[0],8);
        check_high = make8(crc,1);
        check_low = make8(crc,0);

        if(check_high==inbuffer[8] && check_low == inbuffer[9])
        {

         if(valid_data==0)  // STATUS LED  ALLOW US TO SEE IF PIC IS RECEIVING RS485 COMMANDS IF VALID DATA IS FALSE
         {
           for(a=0;a<5;a++)
           {
             restart_wdt();
             output_high(PIN_C3);
             delay_ms(200);
             output_low(PIN_C3);
             delay_ms(1000);
   
           }
         }
         
         

         
         
         





          if(valid_data==1 && inbuffer[2]==unit_id)
          {
            output_high(PIN_C2);
            delay_ms(10);
            if(inbuffer[3]==1)
            {
              for(oo=0;oo<44;oo++)
              {
                fputc(m_inbuffer[oo],COM_B);
              }
            }
            fputc(check_high,COM_B);
            fputc(check_low,COM_B);

            delay_ms(10);
           
            output_low(PIN_C2);

          }
        }
      }
    }
  }
} // END FUNCTION


//******************************************************************************
// Write RS 485 Character To Terminal For Diagnostics
//******************************************************************************
write_485(byte mchar)
{
  output_high(PIN_C2);
  delay_ms(10);
  fputc(mchar,COM_B);
  delay_ms(10);
  output_low(PIN_C2);
return 0;
}




RLScott



Joined: 10 Jul 2007
Posts: 465

View user's profile Send private message

PostPosted: Wed Jun 04, 2008 7:37 pm     Reply with quote

superflycfi wrote:
No checking at this time, please explain method? I assumed the errors are reset by the compiler....

Framing errors do not need to be handled because they don't inhibit further reception, and they are cleared automatically by receiving the next good byte. But Overrun errors are another matter. The hardware UART has a 2-byte FIFO. If for some reason you allow 3 bytes to be received without your reading them, then an overrun error is set (OERR bit in the RCSTA register). If this bit gets set, then all further reception is inhibited until the receiver is reset (by clearing the CREN bit and then setting it). If you are sure your software never allows 3 bytes to accumulate in the receiver without reading them, then this should not be a problem. But it is easy enough to check. I don't know if the compiler does that automatically or not, but I doubt it.

Robert Scott
Real-Time Specialties
superflycfi



Joined: 25 Mar 2008
Posts: 13

View user's profile Send private message

PostPosted: Thu Jun 05, 2008 8:59 am     Reply with quote

Using the ERRORS parameter in the #useRS232 directive is supposed to cause the compiler to create code to reset that I believe. Question is: Will that affect the transmitting of characters out?
Ttelmah
Guest







PostPosted: Fri Jun 06, 2008 2:49 am     Reply with quote

Will a 'MCLR' reset, clear the problem, or does it require a power reset?.

If the former, then the gate itself is not locked up, just the UART circuitry. In this case, disabling and re-enabling the UART transmit component should clear it. It is possible to take any CMOS gate into a latch up state, by power cycling the chip, with voltages applied outside the supply rails. However on the PIC, the internal protection diodes on most pins make this quite hard to do. If the opto coupling is well implemented, and is reasonably close to the PIC, it should not be allowing anything like this to happen.

I'd really suspect something is getting hung up in the code, rather than the hardware UART. Possibly supply line noise, or a radiation event, corrupting part of the internal RAM.

I'd suggest changing your diagnostic output (or use another if available), so the LED gets turned on, whenever you go to send a character, and off as soon as it is sent. Something like:
Code:

void diag_putc(char val) {
    LED_ON; //Using whatever code you need
    putc(val);
    LED_OFF; //again using whatever code you need
}

Call this instead of the standard 'putc' for the serial. Then if the LED gets stuck on, you will know that the problem is the hardware UART, not something else in the code. However if the LED doesn't stay on, you know that you have to look elsewhere.

If it does stay on', and a MCLR reset will clear the system, you can either look at implementing a watchdog recovery, or add a 'timeout' to the putc, with an interrupt, and have this try disabling and re-enabling the UART.

Best Wishes
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Fri Jun 06, 2008 3:39 am     Reply with quote

A bug in check485terminal() :
Code:
    inbuffer[y]=dhRead;
    inbuffer[y+1]=0;    // Bug: this allows you to write at inbuffer[11], buffer overflow!
    y++;
    if(y>=11)y=0;

Besides the bug, try to avoid hard coded magic numbers like '11'. Instead use a define or the sizeof operator.

Fixed version
Code:
    inbuffer[y]=dhRead;
    inbuffer[y+1]=0;
    y++;
    if(y>=sizeof(y)-1)y=0;



A small inefficiency is that you are resetting the buffer as soon as it fills up to the maximum, this means you are effectively using the buffer up to size-1, wasting 1 byte of buffer space. With buffer y having a size of 11, the above code will reset on character 10 being received and this last character is thrown away.
superflycfi



Joined: 25 Mar 2008
Posts: 13

View user's profile Send private message

PostPosted: Mon Jun 09, 2008 10:10 am     Reply with quote

Thanks for the info so far! I have a built in status LED that indicates the PIC is still running. As far as the buffer overwrite bug, would this overrun write into the next declared variable? If so, it wouldn't be the problem because the program initializes it every time it's used in the polling function.

I've not used the MCLR, I did put a provision in before to execute a reset_cpu() if no data was received within a specified number of seconds.
My latest test code created a loop to fire the watchdog. I haven't tested that version in the field yet.

How can the UART be reset on the TX side or RX side? I'd like to try that!
I am certain that I'm having a TX problem as the device the unit polls has a display that indicates it has received valid data request.

Thanks!!

Jim
Ttelmah
Guest







PostPosted: Mon Jun 09, 2008 10:30 am     Reply with quote

The UART, has three 'enable' bits. 'SPEN', which turns the whole UART on/off, 'SREN', which enables the receive side, and 'TXEN', which enables the transmit components. These are very useful, since (for instance), you can enable the hardware UART, then turn off the transmit side, and use this pin for I/O, while still having the hardware serial reception. In your case, if the UART transmit circuitry alone, was 'locked', then you could just clear the TXEN bit, wait at least one instruction, and then set it again. So:
Code:

#byte TXSTA=0x98
#bit TXEN=TXSTA.5

TXEN=FALSE;
delay_cycles(2);
TXEN=TRUE;


As a comment, 'reset_cpu', doesn't do quite the same thing as a 'real' reset. It forces the code back to the start, but except on chips that have a software 'reset' instruction, the hardware is not reset.

Best Wishes
superflycfi



Joined: 25 Mar 2008
Posts: 13

View user's profile Send private message

PostPosted: Mon Jun 09, 2008 10:37 am     Reply with quote

Will a watchdog reset perform a hardware reset? I did implement code to do that rather than the reset_cpu(). Will the UART reset with the watchdog reset? I have verified that the unit does come back and work when I simulate a com malfunction by unplugging cable.

Not sure if noise is causing this issue, I simply have not been able to duplicate this problem in the lab. With several test units and 100's of thousands of com sequences.

Thanks!

Jim
cirrosis
Guest







PostPosted: Thu Jun 26, 2008 10:05 am     Reply with quote

Hello I have the seem problem, if i perform a MCRL reset or RESTART_CPU(),PIC rx again, now i have a count that restart_cpu, but do you know how restart usart only, or other solutions?.
I use a 18f452 4MHz, polling 100ms, code:
Code:
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,enable=PIN_C5,errors,RESTART_WDT,STREAM=RS485)
Code:
#int_RDA
void RDA_isr()
{
      trama_rx[byte_r]=fgetc(RS485);
      byte_r++;
      if(trama_rx[byte_r-1]==START){byte_r=0;byte_nuevo=0;}}
      if(byte_r==STOP){byte_r=0;byte_nuevo=1;}
      if (byte_r > MAX) byte_r = 0;
}
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