|
|
View previous topic :: View next topic |
Author |
Message |
hello188
Joined: 02 Jun 2010 Posts: 74
|
RS-485 Driver IC and #INT_RDA |
Posted: Sat Oct 06, 2012 12:47 am |
|
|
Hi, I am using SP485, RS485 Driver IC to test single master, multi-slave communication using bus topology.
I have 2 questions.
1) Everything else about the IC seems to be working fine except when I drive the DE and /RE pin high to send out bytes, the RO pin drops to about 2.0V. It triggers #INT_RDA interrupt in 3.3V CPU. When I drive DE and /RE pin low, then RO pin goes back to 5V. Is it normal? or is it some kind of hardware configuration error?
Then, If i should disable_interrupt(INT_RDA) when sending out bytes, would it just disable interrupt and make the receive module stuck with OERR(Overrun Error that should be cleared manually)??
2) In order to save processing time of the CPU, I use buffered transmission described in EX_STISR.h
However, As I am using half duplex channel, I have to switch my driver to receive mode after sending the last character.
so,
Code: | #int_tbe
void serial_isr() {
if(t_next_in!=t_next_out)
{
putc(t_buffer[t_next_out]);
t_next_out=(t_next_out+1) % T_BUFFER_SIZE;
}
else
_DE_PIN = 0;
//delay_us(20);
disable_interrupts(int_tbe);
} |
I inserted one line of code to switch the driver to Receive mode(DE pin and /RE pin are shorted to a GPIO pin of PIC).
However, If I do that and observe the result in ternimal program, the last character gets jumbled. I have to add the commented out delay_us function above. Doesn't INT_TBE occur when the transmit buffer has finished sending out the character in its buffer?? Why do I have to keep the driver longer??
Thank you |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19505
|
|
Posted: Sat Oct 06, 2012 1:37 am |
|
|
You need a pull up resistor on the RO pin.
When you turn the receive buffer 'off', the receive data output is undriven. Something like 10KR from the line to the +ve rail, makes sure that when this happens the line does not float low enough to be seen as data.
Your INT_TBE needs to change. INT_TBE triggers to say that the transmit buffer is empty. This though does _not_ mean that transmission has finished. Problem is that there is still a character in the shift register at this point. Normally this is a good thing (you can load the next character 'ready' to be sent), but when you want to control the buffer lines, this is a problem.
The following code is how I handle this:
Code: |
#byte TXSTA=getenv("SFR:TXSTA")
#bit TRMT=TXSTA.1
int1 interrupt_isenabled(int16 val) { //return true if interrupt is enabled
int16 address=0xF00;
int8 mask;
address+=make8(val,1);
mask=make8(val,0);
return ((*(int8 *)address & mask)!=0);
}
#int_tbe
void serial_isr() {
if(t_next_in!=t_next_out) {
putc(t_buffer[t_next_out]);
//t_next_out=(t_next_out+1) % T_BUFFER_SIZE;
if (++t_next_out==T_BUFFER_SIZE) t_next_out=0;
}
else {
enable_interrupts(INT_TIMER0);
set_timer0(-2000); //adjust to suit character time...
clear_interrupt(INT_TIMER0);
disable_interrupts(int_tbe);
}
}
#INT_TIMER0
void stoptx(void) {
//here we may need to turn off the enable pin
disable_interrupts(INT_TIMER0);
if (t_next_in!=t_next_out) return; //transmission has restarted
if (interrupt_isenabled(INT_TBE)) return; //verify nothing is being sent
while (TRMT==0) ; //wait for data to stop sending
DE_PIN=0; //and turn off enable
}
|
Now quite complex. Some comments:
1) Avoid using the '% buffersize' limit to the buffer. This _only_ works well if the buffer is a binary (2,4,8,16,32 etc.) size. It also takes longer than simply testing if the increment would go over the end, and setting to zero if so. It is a case where the CCS examples are 'poor'....
2) Now, I have timer0 (use whatever timer you want), set to count instruction cycles. Running serial at 57600bps, and 48MHz clock, means a 'character' is ((48000000/4)/57600)*10 = 2083 counts. About 40 counts will have already passed, and another 30 will pass by the time you reach the timer interrupt handler, so I load the timer with -2000 (which with the way maths works in the compiler/PIC, gives 63536 loaded into the timer), disable the int_tbe, and enable the timer interrupt. This time should mean the interrupt will trigger just before the shift register empties.
Then when the timer triggers, I now 'recheck'. Has data appeared in the buffer again, or INT_TBE been turned back on?. If so, I don't want to disable the transmission, just return. Then need to verify the shift register _has_ emptied. This is the 'TRMT' bit in the processor. Because of the timing, it may have finished, or 'not quite', so test and wait for it to change (should only be a uSec or so).
Advantage here is that I can be doing other things instead of waiting (over 2000 instructions of something), but it is complex.
Controlling the enable line and not just sitting and waiting for the character to send, does require some care.
The alternative (simpler, avoids using a delay in the INT_TBE, and doesn't wait any longer than needed, and doesn't need a timer) is:
Code: |
#byte TXSTA=getenv("SFR:TXSTA")
#bit TRMT=TXSTA.1
#int_tbe
void serial_isr() {
if(t_next_in!=t_next_out) {
putc(t_buffer[t_next_out]);
if (++t_next_out==T_BUFFER_SIZE) t_next_out=0;
}
else {
disable_interrupts(int_tbe);
while (TRMT==0) ; //wait for data to stop sending
DE_PIN=0; //and turn off enable
}
}
|
I suspect from your short delay needed, that you are operating the RS485 quite fast. In which case the above approach is probably the 'best'.
Best Wishes |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sat Oct 06, 2012 3:08 am |
|
|
Quote: | 1) Avoid using the '% buffersize' limit to the buffer. This _only_ works well if the buffer is a binary (2,4,8,16,32 etc.) size. It also takes longer than simply testing if the increment would go over the end, and setting to zero if so. It is a case where the CCS examples are 'poor'.... |
Yes, it makes only sense for power of two buffer sizes. Otherwise it would require a divider. But the code is smaller
by one instruction for int8 buffer pointers with PCH. With PCD the results are different e.g. according odd and even
pointer location and compiler oddities.
All in all, I agree with Ttelmah that the compare method is more flexible as it allows fine tuning of buffer sizes. |
|
|
hello188
Joined: 02 Jun 2010 Posts: 74
|
|
Posted: Sun Oct 07, 2012 6:45 pm |
|
|
Thanks.
i understand that simply checking buffer index's upper bound is more efficient than using % sign if buffer size is not some integer power of 2.
Code: | //t_next_out=(t_next_out+1) % T_BUFFER_SIZE;
if (++t_next_out==T_BUFFER_SIZE) t_next_out=0; |
So, I am just wondering, If i just simply code like below
Code: | if (++t_next_out==T_BUFFER_SIZE) t_next_out=0; |
and set the T_BUFFER_SIZE to some power of 2, The compiler would recognize that it is using % operation(actually using & operator to mask the lower log2(T_BUFFER_SIZE)) is more efficient and translate accordingly?
Also, I don't understand the interrupt_isenabled part. Can you please give me more explanation??
Code: | int1 interrupt_isenabled(int16 val) { //return true if interrupt is enabled
int16 address=0xF00;
int8 mask;
address+=make8(val,1);
mask=make8(val,0);
return ((*(int8 *)address & mask)!=0);
} |
Thank you |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Sun Oct 07, 2012 7:57 pm |
|
|
Actually, if you can choose a power of 2 as a buffer size, I claim this is the simplest way to increment the index (assuming the buffer size is 16 here):
Code: |
bit_clear(++t_next_out, 4);
|
The stylistic drawback here is that the #defined quantity T_BUFFER_SIZE doesn't exist. You could substitute a #defined quantity for the number 4, however (3 for a buffer size of 8, or 5 for 32). |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19505
|
|
Posted: Mon Oct 08, 2012 1:04 am |
|
|
interrupt_isenabled, is a routine to use interrupt 'names' exactly as CCS defines them, and test if the interrupt is enabled.
Key thing is that if more data has been sent, since the trigger saying 'transmission wants to stop', the interrupt will have been enabled again. Hence if you test if the interrupt is once more 'enabled' you know if this has happened. Now, CCS gives you routines to enable an interrupt, clear an interrupt etc., but not one to test if an interrupt is enabled. I therefore generated one.
There interrupt 'numbers', have as the bottom byte, the 'bit mask' for the interrupt bit they are working with, and as their next byte the address of the interrupt register-0xF00 (for PIC18's). So I generate the register address needed (second byte + 0xF00), and then use the mask to test if the interrupt is on/off.
Best Wishes |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19505
|
|
Posted: Mon Oct 08, 2012 1:48 am |
|
|
Don't get hooked on the efficiency bit about the various ways of limiting the buffer size.
Provided you don't end up generating the limit using division (which is what % can do, if the size is non binary, which is the real 'killer'), you are talking a worst case of just five machine instructions for any of the other routes, including the addition. For a binary size the bit_clear method is the smallest, which you can make 'sort of friendly', with:
Code: |
#define buffer_bit_size 4
#define buffer_size (2^buffer_bit_size)
//etc..
|
So you actually define the buffer 'size' as how many bits long it is, rather than a 'size', and let the compiler do the rest, forcing it to be 'binary'.
Best Wishes |
|
|
hello188
Joined: 02 Jun 2010 Posts: 74
|
|
Posted: Fri Oct 12, 2012 11:55 pm |
|
|
Thanks. Now, I understand.
I have one more question.
I am using RS-485(half-duplex differential signal) to communicate between two PIC board. However, I noticed that the error rate was unacceptable(around 15%), So, I checked the waveform on the oscilloscope, and it was ridiculously bad.
Then I added an 120 Ohm resistor between the A and B line. and the voltage wave form magically cleared to square wave.
What I do not understand is that I thought that termination resistor was to minimize the reflection in a signal line that uses cable with characteristic impedance. Why do I have to use if for communication between about 10 centimeter with virtually no characteristic impedence??
Thank you for your support.
I always appreciate it. |
|
|
|
|
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
|