|
|
View previous topic :: View next topic |
Author |
Message |
superflycfi
Joined: 25 Mar 2008 Posts: 13
|
Serial Communication to Device TX Line Issue |
Posted: Wed Jun 04, 2008 2:39 pm |
|
|
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
|
Re: Serial Communication to Device TX Line Issue |
Posted: Wed Jun 04, 2008 2:49 pm |
|
|
Are you checking for and clearing framing and overrun errors?
Robert Scott
Real-Time Specialties |
|
|
superflycfi
Joined: 25 Mar 2008 Posts: 13
|
|
Posted: Wed Jun 04, 2008 3:00 pm |
|
|
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
|
Code |
Posted: Wed Jun 04, 2008 3:06 pm |
|
|
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
|
|
Posted: Wed Jun 04, 2008 7:37 pm |
|
|
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
|
|
Posted: Thu Jun 05, 2008 8:59 am |
|
|
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
|
|
Posted: Fri Jun 06, 2008 2:49 am |
|
|
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
|
|
Posted: Fri Jun 06, 2008 3:39 am |
|
|
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
|
|
Posted: Mon Jun 09, 2008 10:10 am |
|
|
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
|
|
Posted: Mon Jun 09, 2008 10:30 am |
|
|
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
|
|
Posted: Mon Jun 09, 2008 10:37 am |
|
|
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
|
|
Posted: Thu Jun 26, 2008 10:05 am |
|
|
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;
} |
|
|
|
|
|
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
|