Eng-Tips is the largest engineering community on the Internet

Intelligent Work Forums for Engineering Professionals

  • Congratulations waross on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

RS-485 Control line issue

Status
Not open for further replies.

subspacedarkspace

Electrical
Jul 10, 2006
8
0
0
US
Hi,

I am using PIC16F876A to relay data from RS-485 devices multi-drop daisy chain configuration (one master – multiple slaves) by means of Asynchronous serial module to a processing unit by means of I2C bus module.

Hardware Configurations:
RS-485 chip PIC16F876A
------------- -------------
Ro ------> RC7
DI ------> RC6
DE ------> RC5
RE' ------> RC5

My communication protocol is one master will always be in transmitting mode and the slaves will be in receiving mode. Slaves are only allowed to transmit when they are polled by the master. Otherwise the slaves have to be listening to master all the time.
When the microcontroller starts for the first time, RC5 is initialized to 1. After every issued poll, I clear RC2 so that I can enable receiver and start receiving data from targeted slave device. Unfortunately, RC5 refuses to go low, the line stays high, and I can not receive any data.

The following is the main code that the PIC16F876A will keep executing all the time:
==============================================================
while(TRUE) // Create an infinite loop
{
RS_485_crc_CALC(sData, 3); // Calculate the crc value for the outgoing poll command

sData[2] = (255 - crcValue); // append the calculated crc value to the end of outgoing poll command

RC5 = 1; // Enter transmittion mode :: Enable Tx Driver and Disable Rx receiver.

for (count1 = 0; count1 < 3 ; count1++) // loop :: this is the length of outgoing poll command.
{
putchar(sData[count1]); // Transmit one character at time.
}

RC5 = 0; // Enter receiving mode :: Enable Rx Receiver and Disable Tx transmitter

getchar(&c); // Get first incoming character
sendBuffer[5] = c; // Append first character to I2C Data array buffer
getchar(&c); // Get Second incoming character
sendBuffer[6] = c; // Append second character to I2C data array buffer
getchar(&c); // get third incoming character
sendBuffer[7] = c; // Append third character to I2C data array buffer

// Heart beat indicator to indicate that the pic is reaching this line.
if (!LEDTime) // Check TIMER1 counter
{
RA4 = (RA4 == FALSE); // Toggle the LED
LEDTime = TIMER1_LED_DELAY; // Restart TIMER 1
}

// Reset the PIC watch dog, tipical time is 18ms before automatic reset from memory location 0x00000
CLRWDT();
}

Please, can you help. This is getting very criticle as I am falling behind my schedule because of this issue.
 
Replies continue below

Recommended for you

With the usart enabled it overrides the tris bits.

p.48
When enabling peripheral functions, care should be
taken in defining TRIS bits for each PORTC pin. Some
peripherals override the TRIS bit to make a pin an
output, while other peripherals override the TRIS bit to
make a pin an input. Since the TRIS bit override is in
effect while the peripheral is enabled, read-modifywrite
instructions (BSF, BCF, XORWF) with TRISC as the
destination, should be avoided. The user should refer
to the corresponding peripheral section for the correct

p.113 of the data sheet.
Bit SPEN (RCSTA<7>) and bits TRISC<7:6> have to be
set in order to configure pins RC6/TX/CK and RC7/RX/DT
as the Universal Synchronous Asynchronous Receiver
Transmitter.
Try setting TRISC bit 6.
if that dont work.
Try clearing SPEN and then clear RC5 this should make it go low.

This link has a schematic and software at the bottom of
the page. You'll have to change the settings to match your processor,but the 873 and 877 code should work.In the
schematic they just tie RE to return.



Hope this helps
 
I also think that when using the usart you don't
write to port c bit 5, I think it handles it through
Setting and clearing the TXSTA registers TXEN bit.

I'll check more into it tomarrow.

I have to go marinade my liver.

 
subspacedarkspace; Off hand I can't tell you...
But I can suggest some troubleshooting ideas.

Write a test routine that just toggles the RC5. It could be tied to the wrong place on your board. I just spent time because I'd hooked SDO to SDO and SDI to SDI on a digital pot. Instead of the correct SDO to SDI. That left my SDO pin never EVER changing state.

If you can't make it toggle with a short test routine loop. Then lift the pin off the board using an Exacto knife and a soldering pen. Try it then.

Keith Cress
Flamin Systems, Inc.-
 
To MadCow: thank you for the info.

I think I forgot to mention that I am using USART and I2C.

1. The USART is used to communicate between my board with PIC16F876A and RS-485 Devices.
2. The I2C is used to communicate between my board and the Main Frame (processing unit).

On power up, the Pic first execute the Init_program() function. this function sets and initializes all registers in the PIC.
===> such as: PORTC = 0x00;
TRISC = 0x9F;

SSPCON = 0x36; // Init. I2C module
SSPEN = 1;

After the execution of Init_program(), the PIC then execute Serial_init() subroutine. this subroutine sets and initializes the USART.
===> such as: PROTC = 0x00;
bitset(TRISC, 7); // TRISC<7> = 1 :: input
bitclr(TRISC, 6); // TRISC<6> = 0 :: output
bitclr(TRISC, 5); // TRISC<5> = 0 :: output

RCSTA = 0x90;
TXSTA = 0x24;

p.77 of the data sheet:
To enable the serial port, SSP Enable bit, SSPEN
(SSPCON<5>), must be set. This configures the SDI, SDO, SCK and SS pins as serial port pins. For the pins to behave as the serial port function, some must have their data direction bits (in the TRIS register) appropriately programmed. That is:
• SDI is automatically controlled by the SPI module
• SDO must have TRISC<5> bit cleared <--------------======================== ???
• SCK (Master mode) must have TRISC<3> bit
cleared
• SCK (Slave mode) must have TRISC<3> bit set
• SS must have TRISC<4> bit set

since, I am using I2C, I do not know if I2C is controlling RC5 <SDO>.
__________________________________________________________

to itsmoked: thank you for your suggestion.

I did left pin RC5 and I hooked it up to oscillascope. the oscillascope showed me a train of pulses of pulse width = 480Hz :: ~3.3KHz ON STATE, and ~555Hz OFF STATE.

so I think there are no proplems with my PIC.

The only thing that I can think off is that I am having timming managment issues between my board and RS-485 devices.
 
Whoa!! With the pin lifted run your program and see if you get the expected pin transitions. If you do then you have a board problem. If you don't you have a cpu\peripheral\SW problem.

Did you try toggling the pin with just a test routine while it was still connected? Put the little 'toggle test" routine up front before initializing the peripherals. If that works you can move it down your code until it stops working. This can show you the way.

Keith Cress
Flamin Systems, Inc.-
 
Hiya-

I believe that your TRIS registers are NOT set correctly. I have found that by setting BOTH the corresponding bits in the TRIS register to INPUT for TXDATA and RXDATA.

This is from a 16F877, but it's close enough to a 876.

bsf TRISC,6 ;set port C:6 for usart
bsf TRISC,7 ;set port C:7 for usart

Hope this helps. If not, then I can pass along some sample code that I know works.

Cheers,

Rich S.
 
Hi,

I am back. I was asked to work on PCB board for the last 2 weeks, so I did not have the chance to go over the suggestions.

to itsmoked :: I did try toggling the pin while it is not connected and while it is connected. the pin seems fine and does toggle correctly. by the way I did remove some components of the board. I think I overkilled the board by using components to protect and monitor A and B lines.

None the less, I do not think that I have a hardware issues so far. it is propably a timming issue between transmitter and receiver.

here is again the hardware and software discriptions:

I am using daisy chain topology for my network. The slave is programmed to listen to the lines and never talk unless otherwise the slave is being polled by the MASTER.

For such communications to occure, the Master send out a polling command, in return the slave with the designated address must reply back by sending back the poll command to the MASTER.

So far I was successfully able to make the MASTER poll the slave device. Now, the MASTER can poll any slave device.

But, I could not make the MASTER successfully to receive any thing back from the slave device.

Hardware configurations:
-----------------------------
all of the RS-485 chips are connected as follow to the PIC uP:

RS-485 pin PIC16F876
------------- -------------
RO RC7(RX)
RE' RC5
DE RC5
DI RC6(TX)

120ohm termination resistor on the Master side



Software Configurations:
-----------------------------
Compiler: HT-PIC C

The following is the software code:
====================================

1: /*==========================================================================*/
2: /* Send 8-bit char using USART and Interrupt handler */
3: /*--------------------------------------------------------------------------*/
4: void putchar (unsigned char data_out)
5: {
6: serial_time = 10; // format timeout value for serial comm
7:
8: while (!TXIF && serial_time) // Wait until transmit reg empty using interrupt flag: TXIF
9: {
10: line_error();
11: }
12:
13: TXREG = data_out; // Write to Transmiter register
14:
15: return; // Resume PIC normal operation
16: }
17:
18: /*==========================================================================*/
19: /* receive 8-bit char using USART and Interrupt handler */
20: /*--------------------------------------------------------------------------*/
21: bit getchar(unsigned char *data_in)
22: {
23: serial_time = 10; // format timeout value for serial comm
24:
25: while (!RCIF && serial_time) // Wait until character is received using Interrupt flag: RCIF
26: { // Loops until user send info.
27:
28: line_error();
29: }
30:
31: if (!serial_time)
32: {
33: return (FALSE);
34: }
35:
36: *data_in = RCREG; // assign incoming data to DATA_IN.
37:
38: return(TRUE); // Resume PIC normal operation
39: }
40:
41: //==============------------- MAIN PROGRAM ---------------==================
42:
43: /*========================================================================*/
44: /* The Main Program */
45: /*------------------------------------------------------------------------*/
46: main(void)
47: {
48: unsigned char bydata; // counter variable
49: unsigned char sData[3]; // outgoing buffer from MASTER to SLAVES
50: unsigned char sData2[3]; // incoming buffer from SLAVES to MASTER
51:
52:
53: InitProc(); // Initialize uP and registery
54: serial_init(31,1); // Init. serial port
55:
56: sData[0] = 0x01; // SLAVE device address
57: sData[1] = 0x0C; // Poll command
58: crcValue = 0xFF; // initialize crcValue;
59:
60: sData2[0] = 0x00; // Clear Incoming buffer.
61: sData2[1] = 0x00;
62: sData2[2] = 0x00;
63:
64:
65: while(1)
66: {
67: bydata = 0x00; // reset counter
68:
69: RS_485_crc_CALC(sData, 3); // Calculate crcValue
70:
71: sData[2] = (255 - crcValue); // append crc value to outgoing buffer
72:
73: crcValue = 0xFF; // Reset crcValue
74:
75: // Prepare the MAster to transmit data to slaves
76: //-----------------------------------------------
77: RC5 = 1; // enable RS-485 driver (transmitter)
78: TXEN = 1; // enable transmitter
79: CREN = 0; // disable receiver
80:
81: for (bydata = 0; bydata < 3 ; bydata++)
82: {
83: putchar(sData[bydata]); // Send out the data to slaves
84: }
85:
86: // Prepare the master to receive data from slave
87: //------------------------------------------------
88: TXEN = 0; // Disable Transmitter
89: CREN = 1; // Enable Receiver
90: RC5 = 0; // Enable RS-485 Receiver
91:
92: for (bydata = 0; bydata < 3; bydata++)
93: {
94: getchar(&sData2[bydata]); // receive incoming data from slaves
95: }
96:
97: // Heart Beat Indicator :: inidicate that the pic has reached the end of code cycle.
98: if (!LEDTime) // Check Countdown
99: {
100: RA4 = (RA4 == FALSE);
101: LEDTime = TIMER1_LED_DELAY; // Restart
102: }
103:
104: CLRWDT();
105:
106: }
107:
108: }
109:


The above code does not work.


But if you insert the following lines:

for(i=0; i<95; i++); between lines :: 83 and 84.
for(i=0; i<5000; i++); between lines :: 103 and 104.


The slave successfully receives the poll command, but still the MASTER can not receive the response back from the slave.


ANY SUGGESTIONS?
 
So many places for a problem.

Have you looked at the actual data with a scope on the remote's end to see that the correct data is actually getting to the remote?

It appears with your added hack this does occur...

If yes, have you seen the correct response return from the remote at the CPU's RX pin?

We can beat our heads against the SW if this is actually the problem.

Keith Cress
Flamin Systems, Inc.-
 
I agree with Keith. Take a look at what's actually going out on the RS485 lines. The timing of the direction pin (RC5 in this case) is critical. When you send a character to a UART, there is a period of time (dependent on the baud rate) where it is being sent out. If you change the direction signal too soon, you will truncate your outgoing data. If you change the direction signal too late, you can lose the beginning of the response data coming back from your slave.
 
For now, program everything to send and respond with ASCII uppercase 'U' characters.

... Lots of edges to see on the scope.



Mike Halloran
Pembroke Pines, FL, USA
 
>> Have you looked at the actual data with a scope on the remote's end to see that the correct data is actually getting to the remote?

Yes, here is a picture, where CH2 is RC5 and CH1 is line A of RS-485.
91_1179933028.jpg


>> It appears with your added hack this does occur...

Yes, but why. is it because I am adding a delay between every transmitted character. keep in mind that I also have a delay within the putchar() subroutine.

>> If yes, have you seen the correct response return from the remote at the CPU's RX pin?

No, and here is a picture, where CH2 is RC5 and CH1 is RO of the Master RS-485 chip.
85_1179933072.jpg


from the above picture, I think that I am changing the direction pin RC5 too soon. I will try to rewrite the subroutine for receiving incoming data.
 
subspacedarkspace; Ah, now there is some nice info.

Top picture: You are dropping RC5 right on the ragged edge. More importantly it appears you may be raising it too late so if you turn it on too early the slave may not get the start bit correctly.

Okay here's the big pain with your task. You want to run the direction control on the 485 driver. What does one use to determine when to switch from send to receive? Always a bit of a problem. The one you're having!

The reason is because most PICs don't signal to you, in any useful way, that the serial shift register has gone empty! Which, of course, is when it's time to turn around the link.

They will tell you when the TX buffer is empty but that is a byte at 'today's' baud rate away from the TX shift register being empty. Take too long the slave may start a response, guess to short, and the slave gets a framing error.

You can try to keep track of all this with timers and counters as you have, but it can get tricky.

One useful, solid method, is to actually receive the echo-back, of the transmission. When you receive the last character of your sent message... The whole message has been completely sent by definition. Turn the line around now!

Set up your code to essentially check off each character of your polling message as it comes back. A quick RXbuffer full interrupt - do the compare. When the last character comes home... You be done!

Keith Cress
Flamin Systems, Inc.-
 
Status
Not open for further replies.
Back
Top