View previous topic :: View next topic |
Author |
Message |
jemly
Joined: 13 May 2007 Posts: 34
|
RS232 Questions : 18F452 38400 baud, data looks wrong |
Posted: Tue Oct 02, 2007 9:02 am |
|
|
I'm am trying to receive RS232 data being sent by a piece of software on the PC. The software sends the data out 50 times a second at a baud rate of 38400. I've started with the example file supplied with CCS and have modifed the settings for my needs so my use directive looks like this:
#use delay(clock=19660800)
#use rs232(baud=38400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,errors,brgh1ok)
I am successfully receiving data in the #int_rda ISR and am watching the data as it arrives in the log window. We keep receiving values that we know are not being sent from the PC - is it possible that the data type is an issue here? Is there any other reason the data I am watching arrive seems completely wrong?? |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Tue Oct 02, 2007 10:13 am |
|
|
How much data 50 times a second? (total bytes?)
What does your ISR look like?
Can you slow things down from the PC side for debugging? |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Tue Oct 02, 2007 11:27 am |
|
|
They send 44 bytes of data - 36 bytes of the info we need to use, followed by 4 bytes of checksum data, and finally 4 bytes of signal data to show the end of this section is reached.
We can't slow things down from the PC side at all unfortunately. What I'd like to see is a real time log of the data coming in - at the moment if I use the log window in CCS to log my breakpoint in the #int_rda ISR I see the buffer array (size 44) and I can see it filling up with numbers but the numbers are incorrect. What data type is shown in the log or watch window by default and how can I change it?
Or am I doing this the wrong way and is there a better way to check the data coming in? I'll post the ISR routine tmrw when I'm back in the office. |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Tue Oct 02, 2007 1:09 pm |
|
|
My math shows that is a nonstop data-stream. I'm not sure how much processor time will be left for doing anything other than receiving data.
Do you need every 44byte packet? (If not you can ignore some of them to regain some processor time.)
What is the format of the packet? Is there a fixed header that indicates the start of the packet?
Details... more details. |
|
|
Ttelmah Guest
|
|
Posted: Tue Oct 02, 2007 3:02 pm |
|
|
Obvious question for 'unexpected data'. How is the connection made?.
Best Wishes |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Wed Oct 03, 2007 2:22 am |
|
|
Thanks for the responses:
jecottrell - that is exactly what we are thinking - there is just so much data! Strangely they don't send a start byte they send 4 bytes of data as an end signal. Each packet is 36 bytes of data we need to use, then 4 bytes of checksum, then the 4 byte end signal.
How could we selectively ignore the incoming data to say only read every other packet?
Ttelmah - Sorry I'm not quite sure what you mean? Could you clarify (apologies for my ignorance!)
My ISR is like this:
Code: |
#int_rda
void serial_isr() {
int t;
byte p;
int i;
foundPt = 0;
p = getc();
buffer[next_in]=p;
if(next_in > 3)
{
if(buffer[next_in] == 127 & buffer[next_in - 1] == 128 & buffer[next_in - 2] == 0 & buffer[next_in - 3] == 0)
{
startSignalFound = true;
foundPt = next_in +1;
}
}
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
{
next_in=t; // Buffer full !!
for (i=0;i<16;i++) buffer[i]=' ';
}
// If any errors occur clear the rs232 buffer and reset the index.
if((rs232_errors!=144)&&(rs232_errors!=0))
{
next_in=0;
for (i=0;i<16;i++) buffer[i]=' ';
}
}
|
My main is like this:
Code: |
void main() {
int i;
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(SPI_SS_DISABLED);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
enable_interrupts(global);
enable_interrupts(int_rda);
do {
while(bkbhit)
{
output_bit(PIN_B3,1);
putc(bgetc());
}
output_bit(PIN_B3,0);
if(startSignalFound)
{
for (i=0;i<16;i++) data[i] = buffer[foundPt];
}
} while (TRUE);
}
|
and the only other bits of code are:
Code: |
#define bkbhit (next_in!=next_out)
BYTE bgetc() {
BYTE c;
while(!bkbhit) ;
c=buffer[next_out];
next_out=(next_out+1) % BUFFER_SIZE;
return(c);
}
|
This is all based on the PICC serial example.
Any help massively appreciated! |
|
|
Foppie
Joined: 16 Sep 2005 Posts: 138 Location: The Netherlands
|
|
Posted: Wed Oct 03, 2007 4:15 am |
|
|
To selectively ingore incoming data you could try something like this:
Code: | #INT_RDA
void serial_isr()
{
char c;
c = getc();
if (ready_to_receive)
{
buffer[index] = c;
index++;
if (index == 4) //4 bytes received
{
if (!((buffer[0] == 0) &&
(buffer[1] == 0) &&
(buffer[2] == 128) &&
(buffer[3] == 127)))
{
buffer[0] = buffer[1];
buffer[1] = buffer[2];
buffer[2] = buffer[3];
index--;
}
}
if (index == 44)
{
ready_to_receive = false;
data_received = true;
}
}
} |
this works with these globals:
Code: | int index = 0;
char buffer[44];
int1 ready_to_receive = false;
int1 data_received = false; |
As you can see I use the 4 bytes end signal as the start of the packet.
You can now, in your code wait for data_received to go to 1 and then you know that a packet has been received that must be evaluated.
to start searching for the next data packet you need something like:
Code: | void search_again()
{
data_received = false;
index = 0;
ready_to_receive = true; //must be last!
} |
Note that buffer is -NOT- a string. |
|
|
Ttelmah Guest
|
|
Posted: Wed Oct 03, 2007 4:49 am |
|
|
How is the actual electical connection made to the pins?.
Have you simply tested sending a smaller, and slower message, from a terminal program on the PC, to verify that the wiring is working?.
Remember that the pins on the PIC expect to see 'logic level' (5v) serial data, that is level inverted from what is on the 'RS232' wires. A wrong connection, would result in garbage bing received...
Best Wishes |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Wed Oct 03, 2007 4:56 am |
|
|
Thanks foppie, I will give that a go. Ttelmah the wiring is absolutely fine and has been fully tested. It's definitely a speed and/or software problem. |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Wed Oct 03, 2007 7:33 am |
|
|
jecottrell wrote: | My math shows that is a nonstop data-stream. I'm not sure how much processor time will be left for doing anything other than receiving data.
|
10 bits/byte * 44 bytes * 50 messages / 38400 bits/sec = 0.573 seconds or 57% duty cycle. That is not too bad, far from non-stop. Am I missing something? _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
Ttelmah Guest
|
|
Posted: Wed Oct 03, 2007 7:34 am |
|
|
OK. Lets make some comments. First thing, change your buffer size to 64bytes. The problem is that a 44 byte buffer, implies that the function:
next_in=(next_in+1) % BUFFER_SIZE;
Will take a _lot_ of time. It involves performing a division by 44, and then taking the remainder of this. Typically something over 130 instruction times. The same function done with a 'binary multiple' buffer size (2,4,8,16 etc. bytes), takes only a couple of instructions (it is performed using a logical '&').
Now, the idea shown by Foppie, is another way of removing this problem, and speeding up the search in the buffer for the data you want.
Your 'rate', is not impossible. It only represents one character every 260uSec, and even at your clock rate, you have 1280 instruction times between interrupts. However thre is potential huge problem in the timing of the ISR. Array accesses are _slow_ Typically take anything from 10 to 20 machine instructions each. Each time round the ISR, you are performing five such accesses, to do the logic test for what data you have found. Think differently about this. Consider a 'state machine'.
Something like:
Code: |
#int_rda
void serial_isr() {
static int8 state=0;
int t;
byte p;
int i;
foundPt = 0;
p = getc();
buffer[next_in]=p;
//increment 'next_in' _first_. Remember it needs to wrap
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
switch (state) {
case 0:
//Now, I am slightly 'worried' by your logic. Next_in, could perfectly
//well be '0', with a legitimate packet received. Remember it _loops_
if (p==0) state=1;
break;
case 1:
if (p==0) state=2;
else state=0;
break;
case 2:
if (p==128) {
state=3;
break;
else if (p==0) state=1;
else state=0;
break;
case 3:
if (p==127) {
startSignalFound = true;
foundPt = next_in; //This has already been incremented
break;
}
else if (p==0) state=1;
else state=0;
break;
}
}
|
Now, there are some real problems with your code. You are using a circular buffer, but at times are treating it as a linear buffer. remember if the address has wrapped at the top of the buffer, the _last_ address, would be right at the other end of the buffer, not one byte back. Also, why overwrite data on errors. This is taking far too long. just reset the pointers if needed. |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Wed Oct 03, 2007 8:37 am |
|
|
SherpaDoug wrote: | 10 bits/byte * 44 bytes * 50 messages / 38400 bits/sec = 0.573 seconds or 57% duty cycle. That is not too bad, far from non-stop. Am I missing something? |
Strangely enough, I used the exact same math, but came up with a result of just under 38400, repeatedly! Even, re-checked it just now while using your numbers and got the same result once. But after that always got the correct answer. I use a 20y/o TI solar calculator that I pull out of my desk drawer for quickie calcs. I'm starting to get the feeling it may be the culprit. (I know for a fact it can't do sin/cos/tan calcs correctly.... geez, that almost killed me trying to figure that one out.)
The nice thing is, when I try to help, it usually attracts all the pros to correct my crap and the OP gets quality help....quick. |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Wed Oct 03, 2007 10:49 am |
|
|
Wow thank you so much - that all makes great sense. I probably won't be able to post again until Tuesday unfortunately but many thanks for all the time you have put into this. I'll post questions and / or final solutions as and when I get them. |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Tue Oct 09, 2007 6:11 am |
|
|
Ok I firstly am now running my 18F452 at 39.3216 MHz by using a 9.830400 MHz oscillator and internal 4xPLL. At least I hope I have ! Here are my fuse settings:
Code: |
#FUSES H4 //High speed osc with HW enabled 4X PLL
#use delay(clock=39321600)
|
I have made some changes to the code but am still getting similar results - I have also realised that I do not fully understand how to use buffers and flags to ensure I never miss any received data.
Could someone give me an explanation - not necessarily code but just an overview of how it should happen? i.e. buffer[i] will be filled with up to 64 characters at any one time, when position 643 is filled it will strat being filled from position 0 again. Before anything gets overwritten I need to be able to ensure that I have taken out the data that I need. How do I ensure this happens?
Right now what I really need is a program that will receive all incoming data, each time the start condition is seen it should save the next 32 characters which will then be immediately sent out via RS232 on a different stream. I am sorry for asking for so much help but I do not seem to be making much headway...... many thanks. |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Tue Oct 09, 2007 6:11 am |
|
|
Sorry - where it says 'position 643' above, it should read 'position 63'. |
|
|
|