|
|
View previous topic :: View next topic |
Author |
Message |
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
missing packets on RDA |
Posted: Tue Sep 07, 2010 5:45 am |
|
|
Hi all,
Quick question:
18F4520 CCS PCH 4.107
I have structured comms into packets of 20 hex signs which I stream at once, another device is receiving it. I make a flooder which is sending 20x packet at once. My INT_RDA routines are correct and buffering. In parallel with a bus I have monitoring software. Now my bus setup between devices is RS232 -> MAX485 <...bus RS485...> MAX485 <- RS232. What is happening? My RX device is missing on occasions single packets. I see it being sent from TX device. This is my C setup:
Code: |
#use delay (clock=40M, oscillator=10M)
#fuses MCLR, BROWNOUT , NOPROTECT, WDT1024, PUT, NOCPD // Config: ext reset, no code protect, no watchdog, high speed clock
#use rs232(RESTART_WDT,baud=115200,xmit=Tx_Out,rcv=Rx_In,parity=n,bits=8,stop=1,ERRORS,TIMEOUT=5)
setup_wdt(WDT_ON); //set watch dog timer
disable_interrupts(GLOBAL); //clear interrupts
set_timer0(0); //clear timers
set_rtcc(0); //clear timers
setup_timer_0(RTCC_INTERNAL | RTCC_8_BIT | T0_DIV_1); //set timer 0 parameters
setup_adc_ports(ALL_ANALOG | AN0_TO_AN7 | VSS_VDD); //set AD ports
setup_adc(ADC_CLOCK_INTERNAL | ADC_CLOCK_DIV_8);
setup_comparator(NC_NC_NC_NC); //turn off comparators
enable_interrupts(GLOBAL); //clear interrupts
enable_interrupts(INT_RTCC); //enable TIMERs
enable_interrupts(int_rda); //enable serial port
|
INT_RDA in very short schema works like this:
Code: |
do {
c = getc();
strntemp[next_in] = c;
next_in++;
max_buffer++;
} while (max_buffer < 20); //*
|
And TX in short is done like this:
Code: |
if (!kbhit()) {
printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",...packet data...);
delay_custom_ms(70);
}
|
and timer:
Code: |
#int_RTCC //interruption timer 0 for background tasks
void timer0_isr(void) {
timer_tick++;
if (timer_tick > 128 ) {
timer_tick = 0;
}
timer_flag = 1;
}
|
Is it possible that Timer or ADC is activating from time to time and is preventing RDA to kick on? I am running of ideas where the problem may be. The packet will be missing like once in 10 tries or more. Program is to big to be listed, so I post only ideas how it works. I would appreciate suggestions where or what directions to look for a problem.
Thank you for help. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Tue Sep 07, 2010 9:54 am |
|
|
so these are just translaters from RS232-> RS485 -> RS232 ??
In your TX code, you aren't looking for kbhit(). You're looking to have data in the receive buffer. What you have isn't correct.
In your INT_RDA, take out the loop do {} while();
You never want to get stuck in the loop for the whole packet. Just the chars received.
What you should have is a bigger circular buffer (like maybe 10packets worth which is 200bytes which is a single byte sized pointer.)
look at the EX_SISR.c example for those circular buffers.
Then while there is data in the buffer -- send it.
But don't hang up in your INT_RDA routine looking for more chars than the receiver register can hold.
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Tue Sep 07, 2010 3:27 pm |
|
|
thnx,
Quote: | In your TX code, you aren't looking for kbhit(). You're looking to have data in the receive buffer. What you have isn't correct. |
so how do I check if the bus is currently busy then?
I understood that this is going to tell me that bus is busy because there is CHAR waiting to be collected?:
thank you _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Tue Sep 07, 2010 3:50 pm |
|
|
Linuxbuilders wrote: | thnx,
so how do I check if the bus is currently busy then?
I understood that this is going to tell me that bus is busy because there is CHAR waiting to be collected?:
|
Well, kbhit is looking to the RS232 end. There is no "busy bus" condition because there are separate send and receive lines.
So let's look at the RS485 line (which should be a separate #use statement with maybe a stream declaration).
RS485. A shared bus system... kinda like Ethernet. Have you ever heard the term CSMA/CD? I'll let you look it up... but essentially we're in the same market.
When you have a shared bus, how do you know it's busy?
Let's say you and I are in the same room. We are both devices -- and might both be bus masters. Or maybe I'm the bus master and you and (I'll drag PCM Programmer into this) PCM are slaves.
In the case of a master and two slaves:
I talk. You listen. If I ask, I'm going to ADDRESS one of you. When I'm done, you talk.
That one's easy. "Only speak when spoken to."
Now let's cover the harder one. PCM and I are BOTH masters. (well, PCM is more a master than I am... but I digress):
We all have ears. We all have mouths.
PCM and I start talking at the same time. What's the result? Confusion. What I hear is garbled with PCM. What PCM hears is garbled with me. You are told when that happens to ignore us both until we work it out.
PCM and I decide that when we "jam" each other like that, we'll stop, pick a random number and then whoever counts down to 0 first gets to talk first.
Let's say PCM wins this time and he gets to go first. I will hear him talking. So I will just wait until he's done and then count to 5 and start talking. (he has to do the same thing if he finishes his speech and then wants to talk again.)
CSMA/CD has the same concepts:
* EVERYONE must listen before talking.
* If any slave or master is receiving DATA, the rule will be to wait for some fixed amount of time before being allowed a turn.
* IF by chance 2 devices start to talk at the same time (and we do this by listening to what we hear. When it doesn't match, there's a collision), we stop, pick a random number and then count down. The device that hits 0 first, gets to go first.
----------------------------------------------------------------------------
How's that sound for starts?
I'm sure others will have more to add that I'm forgetting.
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Wed Sep 08, 2010 4:31 am |
|
|
Thnx Ben, with all respect I can understand a difference between RS485 and RS232 and I can see that RS485 works differently because it is one way communication, but for some "special" reason I do not use native protocol for it. So lets see what kbhit does? It is saying that there is CHAR ready in a buffer or? It is saying that:
Quote: |
If the RS232 is under software control this function returns TRUE if the start
bit of a character is being sent on the RS232 RCV pin. If the RS232 is
hardware this function returns TRUE if a character has been received and is
waiting in the hardware buffer for getc() to read. This function may be used to
poll for data without stopping and waiting for the data to appear. Note that in
the case of software RS232 this function should be called at least 10 times the
bit rate to ensure incoming data is not lost.
|
So what does it really say? It is saying that there is a buffer taken and waiting to be emptied, I empty buffer after I get INT and load my message into it. So in my understanding I cannot TX when I do not empty RX buffer. It is very easy way to say that I cannot TX because I did not take RX out of the buffer (which is happening only during INT). Or? It is very easy way to see that there is communication going on and I am not allowed to TX until it is finished.
The only problem is an implementation which I probably didn't get right.
Now lets go back to holy INT_RDA and sitting inside INT. I do not care, I can be there even for few ms but of course if I do not have tooo then I would not do it. This is my INT_RDA routine. It will show what I want to do. If I am not meant to sit inside INT then how do I put it together outside of it and keep it doing the some thing? I see no way to do it outside of my int.
Makes sense?
Quote: |
void rx_bus_buffer() {
buffer_no++;
if (buffer_no > (copy_buffer_size-2)) {
buffer_no = 0;
} //if
} //void
#int_RDA
void commsbus(void) {
int next_in_bus = 0;
buffer_no = 0;
int temp_capture[20];
int c;
do {
c = fgetc(bus);
temp_capture[next_in_bus] = c;
next_in_bus++;
} while((temp_capture[0] == 0x0F ) && (c != 0x0d) && (next_in_bus < 20)) ;
rx_bus_buffer_full_tick = 0;
do {
rx_bus_buffer();
rx_bus_buffer_full_tick++;
if (rx_bus_buffer_full_tick > (copy_buffer_size-2)) {
error_rx_bus_buffer_full = 1;
buffer_no = (copy_buffer_size-1);
break;
} //*
} while (packet_bus[buffer_no] == 1); //* if rx buffer is marked as taken do not replace it
memcpy(capture_bus[buffer_no],temp_capture,20);
packet_bus[buffer_no] = 1;
next_in_bus = 0;
}
|
_________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Wed Sep 08, 2010 8:59 am |
|
|
Linuxbuilders wrote: | Thnx Ben, with all respect I can understand a difference between RS485 and RS232 and I can see that RS485 works differently because it is one way communication, but for some "special" reason I do not use native protocol for it. So lets see what kbhit does? It is saying that there is CHAR ready in a buffer or? It is saying that:
|
RS485 doesn't have any particular "protocol" that I know of. So when you say you don't use the native protocol, I have no idea what you're talking about.
However, the problem still remains the same.
What I'm hearing is that you think that because the PIC receives a char over one bus, then that must mean it's completely ok to go just send it over another bus even though it might be busy.
Quote: |
Quote: |
If the RS232 is under software control this function returns TRUE if the start
bit of a character is being sent on the RS232 RCV pin. If the RS232 is
hardware this function returns TRUE if a character has been received and is
waiting in the hardware buffer for getc() to read. This function may be used to
poll for data without stopping and waiting for the data to appear. Note that in
the case of software RS232 this function should be called at least 10 times the
bit rate to ensure incoming data is not lost.
|
So what does it really say? It is saying that there is a buffer taken and waiting to be emptied, I empty buffer after I get INT and load my message into it. So in my understanding I cannot TX when I do not empty RX buffer. It is very easy way to say that I cannot TX because I did not take RX out of the buffer (which is happening only during INT). Or? It is very easy way to see that there is communication going on and I am not allowed to TX until it is finished.
The only problem is an implementation which I probably didn't get right.
Now lets go back to holy INT_RDA and sitting inside INT. I do not care, I can be there even for few ms but of course if I do not have tooo then I would not do it. This is my INT_RDA routine. It will show what I want to do. If I am not meant to sit inside INT then how do I put it together outside of it and keep it doing the some thing? I see no way to do it outside of my int.
Makes sense?
Quote: |
void rx_bus_buffer() {
buffer_no++;
if (buffer_no > (copy_buffer_size-2)) {
buffer_no = 0;
} //if
} //void
#int_RDA
void commsbus(void) {
int next_in_bus = 0;
buffer_no = 0;
int temp_capture[20];
int c;
do {
c = fgetc(bus);
temp_capture[next_in_bus] = c;
next_in_bus++;
} while((temp_capture[0] == 0x0F ) && (c != 0x0d) && (next_in_bus < 20)) ;
rx_bus_buffer_full_tick = 0;
do {
rx_bus_buffer();
rx_bus_buffer_full_tick++;
if (rx_bus_buffer_full_tick > (copy_buffer_size-2)) {
error_rx_bus_buffer_full = 1;
buffer_no = (copy_buffer_size-1);
break;
} //*
} while (packet_bus[buffer_no] == 1); //* if rx buffer is marked as taken do not replace it
memcpy(capture_bus[buffer_no],temp_capture,20);
packet_bus[buffer_no] = 1;
next_in_bus = 0;
}
|
|
You are spinning a while looking for the WHOLE packet inside of an ISR. This is STILL bad and you need to get rid of that.
Start with this:
go look at EX_SISR.c
At a minimum you want circular buffers on both the receive sides of both buses.
But get the RS232 side working first.
The ISR should only get the chars immediately in the receive register and put them in a FIFO buffer and then exit. NOT wait until the end of the packet.
Get in. Save the immediately available data. Get out.
In your main() you then look for the existence of data. If it's there, start sending it --- one byte at a time.
OR, if you are cooking (modifying the format) of the inbound data, set a flag in the ISR when you think you should have the end of the packet. (like the CR/LF sequence). When the MAIN routine sees this flag, then it goes to work taking the inbound packet and massaging it into the desired output format.
Now, all of this goes with the assumption that you want bidirectional streaming.
If that is not the case... if the case is that the flow looks like this:
PC -> RS232 -> PIC -> RS485 -> PIC -> ??
and never the other way... then you don't even need an ISR.
You can just run in main to get your packet and then when it's done, toss it out of the RS485 end. As long as the PC never has to get BACK any data, then it this whole discussion is moot.
If you want, here's a packet receiver I wrote for one app..
It uses FIXED LENGTH PACKETs, so it's not a circular type FIFO that just keeps going. It gets a packet. Then processes it... then waits for another.
The user asking me to write it understands (since they told me this is how they wanted it) that the data can't be a continuous stream.
There's a processing delay. it could be 1mS or 10's of mS.
but it's typically not too long.
What's NOT included in this code is the Timer0 ISR that counts a timeout timer. If the full packet isn't received within a couple of seconds, the state machine resets. (that's the variable packet_timeout)
Code: |
struct packet_map {
unsigned long sync;
unsigned long length;
unsigned long type;
unsigned long data;
unsigned long checksum;
};
struct packet_map pkt_temp;
static unsigned int pkt_index = 0;
static unsigned int pkt_state = 0;
static unsigned int last_byte = 0;
//===================================================================================================================
/*
Summary: Processes via interrupt bytes from the PC Controller
Param: none
Returns: nothing
*/
#int_rda
void rda_isr ( void ) {
char c;
// This will stash incoming bytes into a buffer and when it hits the byte count limit, it will
// set a flag and stop receiving until the packet is processed.
c = fgetc(CPI); // always grab the char from the buffer so no overruns occur
switch (pkt_state) {
// Waiting for packet
case 0: if ( (c == 0x44) && (last_byte == 0x50) ) {
pkt_temp.sync = 0x4450;
pkt_index = 2; // move the index
packet_timeout=2; // 1-2second packet timeout (remember the second timer can be anywhere. so a setting of 2 really means 1-2 seconds.
pkt_state++; // move to next state
} else {
last_byte = c; // or move our current byte to our last byte.
}
break;
// filling packet until full
case 1: if ( packet_timeout ) {
*(((int8 *) &pkt_temp) + pkt_index) = c; // stash the incoming byte
pkt_index++; // increment our index
if ( pkt_index > 9 ) { // We operate on fixed packet lengths. Period.
have_packet = 1; // set the packet flag
pkt_state++; // Move to next state
}
}
break;
// have packet, waiting for release to get next.
case 2: if (! have_packet) {
pkt_state = 0;
}
break;
}
}
void comm_process_packets ( struct packet_map pkt_buffer ) {
unsigned int i;
unsigned long cmd, checksum;
for (i = 0; i < sizeof(pkt_temp) ; i++ ) {
*(((int8 *) &pkt_temp) + i) = 0; // empty out the old packet.
}
have_packet = 0; // reset the packet flag so we can get the next one. We have a copy.
// ok, we have a copy now
// do the CRC - just add 'em up and check against original.
checksum = 0;
checksum += pkt_buffer.sync;
checksum += pkt_buffer.length;
checksum += pkt_buffer.type;
checksum += pkt_buffer.data;
if ( portb.bit0 ) { // addition to bypass checksum check if portb.bit0 == 0
if ( checksum != pkt_buffer.checksum ) { // if the checksum fails, exit.
return;
}
}
// Put meat of what to do with a packet here.
cmd = pkt_buffer.type;
switch (cmd) {
// Configure Compass
// 'CC'
case 0x4343: state.compass_cal = bit_test(pkt_buffer.data, 0);
break;
// ===============================================================
// Configure Debug Output
// 'CD'
case 0x4344: debug = make8( pkt_buffer.data, 0 );
break;
// ===============================================================
// Program Relays
// 'PR'
case 0x5052: portd = make8( pkt_buffer.data, 0 );
break;
// ===============================================================
// Program Valve
// 'PV'
case 0x5056: state.valve_manual = bit_test(pkt_buffer.data, 0);
portb.valve = bit_test(pkt_buffer.data, 1);
break;
}
}
===============================================
===============================================
|
And then back in MAIN(), this is part of the free spinning loop:
Code: | if (have_packet) { comm_process_packets(pkt_temp); }
|
_________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Fri Sep 10, 2010 2:27 am |
|
|
Hi, sorry for delay, didn't have much time to write back.
As native I understand examples from CCS.
How does your program cope with 2 way comms from 200 devices on the bus?
My problem is not sitting inside of INT - it is still relatively short time. Problem is TX hit in the middle of another packet flying around on the bus because PIC detects a window between collecting two CHARS.
That is how it looks inside of packet being sent by one device to the server:
<start of packet from device 1>....<detected free window by device 2 - TRY to TX to device 3>....<end of packet from device 1> - it is microseconds but still detectable by device 2.
It breaks my packet in half and that is why I lose it. So there is two ways to fix it. One is look for <start><end> and when it is present then put delay on TX. So when kbhit is valid and <end> wasn't present then put delay on TX until <end> and then TX. Second solution is hardware. I am 100% positive that I can get it right in the software. So far I have changed TX and RX buffers to be processed once in a cycle of main{}. This is eliminating problem with constant messages and puts bus in nice order. I send 10 max at once from one device if required. This way I have stable bus and less collisions. I agree that my int is long but because I do have 200+ devices in the network I need it to be long. I have no way to do buffering, error checking, etc outside of INT. If you look at my program you will see what I do there. I have circular buffer, I check if it is empty and then fill it up. If I do not have free spot then I put it in spare and send error that buffer is full. I am still thinking how to get out of INT with most of it and do this outside but because my program is at 70% and I put any extra buffering then I may find zero space for anything extra. On another hand I do the job at once inside of INT what from the point of my program makes no difference in operation of the device. If I will try to do it outside I may find that I will have lost more data because before I process one packet I will have another 10 written in.
Makes sense?
I will be happy to get help to redesign my INT to the point of collecting a data and doing rest of job outside but I am somehow not able to imagine it differently than you see it in example above.
Thank you. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Fri Sep 10, 2010 2:46 am |
|
|
and it looks like this:
Quote: |
global var in main.c:
int collect = 0x0D;
#int_RDA
void commsbus(void) {
int next_in_bus = 0;
buffer_no = 0;
int temp_capture[20];
do {
collect = fgetc(bus);
temp_capture[next_in_bus] = collect;
next_in_bus++;
} while((temp_capture[0] == 0x0F ) && (collect != 0x0d) && (next_in_bus < 20)) ;
...
}
and TX routine:
if ((!kbhit(bus)) && (collect == 0x0D)) { // if there is not CHAR waiting in a buffer and broadcast is present then send away
fprintf(bus,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",0x0F,data,...,0x0D);
.... |
coll bananas? _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Fri Sep 10, 2010 9:40 am |
|
|
Linuxbuilders wrote: |
global var in main.c:
int collect = 0x0D;
#int_RDA
void commsbus(void) {
int next_in_bus = 0;
buffer_no = 0;
int temp_capture[20];
do {
collect = fgetc(bus);
temp_capture[next_in_bus] = collect;
next_in_bus++;
} while((temp_capture[0] == 0x0F ) && (collect != 0x0d) && (next_in_bus < 20)) ; THIS IS BAD AND NEEDS TO GO AWAY!! You will be stuck in this ISR for at least 1.7mS waiting for 20 chars.
...
}
and TX routine:
if ((!kbhit(bus)) && (collect == 0x0D)) { // if there is not CHAR waiting in a buffer and broadcast is present then send away
fprintf(bus,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",0x0F,data,...,0x0D);
.... |
Am I seeing/understanding this right?
You are waiting to TX information on the 485 bus, so you wait until the RS232 bus is idle!?!
If the PIC is not doing any sort of translation with the data, just spewing it from one bus to the other, you need to build two FIFO buffers that just do the flow. Because one bus can get stalled waiting to send data, that FIFO might need to be a little bigger.
RS232 -> INT_RDA -> FIFO 1 -> RS485
RS232 <- FIFO 2 <- INT_RDA2 <- RS485
(and you should probably use a PIC w/2 hardware UARTs) _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
icefield
Joined: 09 May 2005 Posts: 20 Location: Canada
|
|
Posted: Fri Sep 10, 2010 3:15 pm |
|
|
For reliable comms, you need to first decide to use polling or interrupts. Your code is trying to mix the two and that is asking for problems.
If you use polling (kbhit/getc), then do not implement an interrupt routine.
If you use interrupts (faster than polling), the interrupt routine must be extremely short. All it should do is execute getc (or a direct register read - even faster) to get the character and put it into a ring buffer. Don't do anything else. It should be just a few lines of code. Don't check kbhit (of course there is a character - you got an interrupt!!!). Don't do any protocol/collision analysis. Just get the character and store it away.
Now, in your main application, you can have all manner of loops that get characters (and check for available characters) from the ring buffer, NOT the serial port. Of course, you need to initialize the ring buffer to be empty in your startup code. Never use kbhit in an interrupt-driven serial scheme - there is no point!
It also sounds like you need to implement some sort of collision detection and resend request of you are going to have multiple transmitters on your RS485.
Hope this helps! |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Fri Sep 10, 2010 9:56 pm |
|
|
thnx
Quote: |
For reliable comms, you need to first decide to use polling or interrupts. Your code is trying to mix the two and that is asking for problems.
|
...will look into it
Quote: |
It also sounds like you need to implement some sort of collision detection and resend request of you are going to have multiple transmitters on your RS485.
|
...done in mail loop
Quote: |
You are waiting to TX information on the 485 bus, so you wait until the RS232 bus is idle!?!
If the PIC is not doing any sort of translation with the data, just spewing it from one bus to the other, you need to build two FIFO buffers that just do the flow. Because one bus can get stalled waiting to send data, that FIFO might need to be a little bigger.
|
...sorry, it is not - it is just one uart port and no translation - get data - process it - respond if required... the previous problem with 18f45j11 was different and is already sorted out. This one is related to 18f4520 which is only one port and no moving data around. I will look into getting all shorted out but probably it will be lots of changes in the way my program works. Problem with missing packets was pure collision. I have sorted it out. Now i need to get as much as possible out of INT and do all work inside of main loop. I am not sure how I do it...but this is different story...
thnx _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Sat Sep 11, 2010 11:05 am |
|
|
Linuxbuilders wrote: |
...sorry, it is not - it is just one uart port and no translation - get data - process it - respond if required... the previous problem with 18f45j11 was different and is already sorted out. This one is related to 18f4520 which is only one port and no moving data around. I will look into getting all shorted out but probably it will be lots of changes in the way my program works. Problem with missing packets was pure collision. I have sorted it out. Now i need to get as much as possible out of INT and do all work inside of main loop. I am not sure how I do it...but this is different story...
|
Ohhh! We've switched problems mid-problem.
Well, if you look at the code I posted above, that would work just dandy for what you want to do (provided you change it as needed -- use it as framework... )
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
|
|
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
|