|
|
View previous topic :: View next topic |
Author |
Message |
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Tue Oct 09, 2007 7:22 am |
|
|
I'll post what I know and use, and maybe one of the pros will come along and offer some pros and cons and better advice.
General idea is to use the ISR to put the arriving characters into a circular or 'ring' buffer as they arrive. The buffer should always be large enough to prevent over-runs or the head catching up with the tail.
In your main code you have some routine to pull characters out of the ring buffer and do what you want with them. Your code will know that there is something in the buffer if the head index is different than the tail index. That is normally implemented with KBHIT.
Your function to retrieve characters from the ring buffer will probably want to use a linear buffer. (i.e. function pulls characters from the ring and places in the linear.) The linear buffer is used because you have a fixed message length as opposed to an unknown message length.
Look at this:
http://www.ccsinfo.com/forum/viewtopic.php?t=20211&highlight=ring+buffer
Also, search buffer linear ring circular. Look at the ex_sisr.c(?) example. It is an example of the ring buffer from the ISR.
Good luck,
John |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Tue Oct 09, 2007 7:59 am |
|
|
Thanks very much John, that certainly helps clarify things but I still have a couple of questions...... I am looking at the example you suggested and am currently running the following code.
Code: |
#define BUFFER_SIZE 64
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
BYTE ch;
BYTE clear = 0;
#int_rda
void serial_isr() {
int t;
buffer[next_in]=getc();
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
next_in=t; // Buffer full !!
}
#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);
}
void clearBuffer()
{
int i;
for (i=0;i<BUFFER_SIZE;i++) buffer[i]=clear;
}
void main() {
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);
clearBuffer();
do {
while(bkbhit)
{
ch = bgetc();
putc( ch );
}
} while (TRUE);
}
|
....and my header file is:
Code: |
#include <18F452.h>
#device ICD=TRUE
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES H4 //High speed osc with HW enabled 4X PLL
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOOSCSEN //Oscillator switching is disabled, main oscillator is source
#FUSES NOBROWNOUT //No brownout reset
#FUSES BORV20 //Brownout reset at 2.0V
#FUSES NOPUT //No Power Up Timer
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES NOWRTB //Boot block not write protected
#FUSES NOWRTC //configuration not registers write protected
#FUSES NOCPD //No EE protection
#FUSES NOCPB //No Boot Block code protection
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#use delay(clock=39321600)
#use rs232(baud=38400,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,brgh1ok,errors,brgh1ok)
#use rs232(baud=38400,parity=N,xmit=PIN_C6,bits=8,stream=DEBUG_MONITOR)
|
So I should be running at 39.3216MHz and at a baud rate of 38400 but I still keep seeing random numbers sometimes when I log a break point in the CCS compiler. I'm not actually viewing the values transmitted back out on rs232 I am relying on being able to log the data as it comes in.
I am still unclear as to where I would begin to store the data I need to keep? Is it in place of :
Code: |
while(bkbhit)
{
ch = bgetc();
putc( ch );
}
|
As I know that I want to keep 16 chars of data I would have another array defined as:
#define DATA_SIZE 16
BYTE data[DATA_SIZE];
BYTE data_in = 0;
BYTE data_out = 0;
How would you then handle this? Let's say I want to send each set of 16chars out on another RS232 stream would you do something like:
Code: |
while(bkbhit)
{
data[data_in] = bgetc();
data_in=(data_in+1) % DATA_SIZE;
//once data is full send all chars out via rs232??
}
|
|
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Tue Oct 09, 2007 8:18 am |
|
|
The code you are using is the CCS 'standard' ring buffer. If you continue having problems bump it up to 128 characters deep. You may also want to search for Ttelmah's optimized code for binary length arrays (32,64,128,256,etc.)
Sorry, I'm not somewhere where I can post an example, but the general idea I would use is:
Get rid of the KBHIT in you main loop and a dd a function: service_ring_buffer.
In the service ring buffer function I would add the while KBHIT. Within the while loop remove the characters from the ring buffer. See if it is your start character. If so, put it in the linear buffer. Keep track of how many characters are in the linear buffer. See if you've received enough characters, if not continue receiving, if so then print your linear buffer out?
Something like that. I'm not sure how to best deal with the optimimzation probably necessary.
This may all be different based upon your final requirements. And if you're trying to test/debug this with an ICD I'm not sure how it will affect things. |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Tue Oct 09, 2007 9:09 am |
|
|
Thanks for that explanation it has really helped me - I have modified my software now and will post the full code below. One problem is that startPos does not see to be the right number and I can't see why? Other than that is this the kind of thing you meant?
Code: |
#include "C:\FLAIR01\pleasework1.h"
#define BUFFER_SIZE 32
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
#define DATA_SIZE 32
BYTE data[DATA_SIZE];
BYTE clear = 0;
BYTE ch;
BYTE p;
//flags
int1 start = 0;
int1 end = 0;
int1 startSignalFound = 0;
int16 startPos;
int t;
static int8 state=0;
#int_rda
void serial_isr() {
p = getc();
buffer[next_in]=p;
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
{
next_in=t; // Buffer full !!
}
if(RS232_errors != 144)
{
output_bit(PIN_B1,1);
}
}
#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);
}
/*
CLEAR BUFFER
Clears the buffer and fills it with zeros.
*/
void clearBuffer()
{
int i;
for (i=0;i<BUFFER_SIZE;i++) buffer[i]=clear;
}
/*
CLEAR DATA
Clears the data and fills it with zeros.
*/
void clearDaTa()
{
int i;
for (i=0;i<DATA_SIZE;i++) data[i]=clear;
}
/*
STORE DATA
Aftyer the 4byte start signal is found in the buffer, the next 16 characters
are moved to the data array.
*/
void storeData()
{
int j;
clearData();
//get new data
for (j=0;j<DATA_SIZE;j++)
{
data[j]=buffer[startPos];
startPos=(startPos+1) % BUFFER_SIZE;
}
startSignalFound = 0;
//ADD : send data out on rs232......
}
/*
SERVICE RING BUFFER
Method which removces chars from the buffer one by one and check each for
start char then rest of start signal. If startSignal then add char to data
buffer until dataCount is 16. The set datacount to zero, start to 0.
*/
void service_ring_buffer()
{
while(bkbhit)
{
ch = bgetc();
//check for start bit
switch (state) {
case 0:
if (ch==0) state=1;
break;
case 1:
if (ch==0) state=2;
else state=0;
break;
case 2:
if (ch==128) {
state=3;
break;
}
else if (ch==0) state=1;
else state=0;
break;
case 3:
if (ch==127) {
startSignalFound = 1;
storeData();
startPos = t;
break;
}
else if (ch==0) state=1;
else state=0;
break;
}
}
}
void main() {
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);
clearBuffer();
output_bit(PIN_B0,0);
do {
service_ring_buffer();
} while (TRUE);
}
|
|
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Tue Oct 09, 2007 10:13 am |
|
|
I would post the linear buffer I use, but it is from some code I paid for and don't feel comfortable plopping it out on the web.
I'll give you some hints (I always retain more when I have to figure it out, so I figure it's better this way anyway.)
When you store data, you clear the buffer after every character. So, you never end up with more than one character in it. Not what you want is it?
Handle filling the buffer in you service function alone. Not a bunch of different calls. Keep track of your index (the total characters received there as well.) Use a global array to hold the characters. It should be fairly simple, no need to handle wrapping in the buffer, because the buffer won't ever overflow if you count the characters going in.
So, increment your index after you store a good character in your linear buffer (just an array). After you have processed the full data string (i.e. index == total length) then set a flag for another function to check and output your buffer contents.
Sorry so scattered. Reply with any specific confusion.
It's looking good,
John |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Tue Oct 09, 2007 11:44 am |
|
|
The choice between a linear and circular buffer can and should be made based on what kind of data stream you are receiving.
For a continuous data stream a circular buffer is better. A repeating data pattern like a sync byte followed by a 8 data bytes. This could be called packets but there is no pause between packets and a circular buffer will just work better.
For a data stream that arrives in packets a linear buffer is better.
A linear buffer will always have the packets data at the same spot within the buffer. For example you can always know the first byte in the buffer is an address. If you use a circular buffer for packet data you will have much more complex code that will run slower and take more space. |
|
|
Wayne_ Guest
|
A couple of things |
Posted: Wed Oct 10, 2007 9:55 am |
|
|
First,
you don't need to actually clear any of the buffers!
You know the ring buffer is empty when next_in == next_out so the function should be
void clear_buffer() {
next_in = next_out;
}
Again you completely fill the linear buffer when you do a copy so why are you clearing it ?
Another option you could go for is an ring of linear buffers!
BYTE buffer[BUFFER_SIZE][DATA_SIZE];
So in your ISR look for the end flag and then store the next 16 bytes in the current [ring][] buffer.
one important issue. is it possible to recieve the end flag as data within the serial input ? |
|
|
jemly
Joined: 13 May 2007 Posts: 34
|
|
Posted: Thu Oct 11, 2007 10:17 am |
|
|
Hi all - thank you for all the amazing help! It's taken me a while to reply because after having implemented some of your suggestions we were still seeing numbers that seemed entirely wrong. To cut a long story short we were being misinformed and tyhe numbers we were receiving on rs232 are in fact correct! We've now got a successful program running on a PIC 18F4680 on a 40Mhz clock receiving data via rs232 50 times per second and we then extract the data we need and send it straight out on a CAN bus at 1Mbps.
One of the bits of misinformation was that what we have called the start signal is in fact an end signal. I am going to post the serial data code that I am now running so that if there is still anything I am not doing very efficiently someone might be able to suggest improvements?!
Code: |
#int_rda
void serial_isr() {
p = getc();
buffer[next_in]=p;
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
{
next_in=t; // Buffer full !!
}
if(RS232_ERRORS != 144)
{
output_bit(ERROR_LED,1);
}
}
#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);
}
/*
CLEAR BUFFER
Clears the buffer and fills it with zeros.
*/
void clearBuffer()
{
int i;
for (i=0;i<BUFFER_SIZE;i++) buffer[i]=clear;
}
/*
CLEAR DATA
Clears the data and fills it with zeros.
*/
void clearData()
{
int i;
for (i=0;i<DATA_SIZE;i++) data[i]=clear;
for (i=0;i<8;i++)
{
out_data01[i]=clear;
out_data02[i]=clear;
out_data03[i]=clear;
out_data04[i]=clear;
out_data05[i]=clear;
}
for (i=0;i<4;i++)
{
checksum[i]=clear;
}
}
/*
STORE DATA
Aftyer the 4byte start signal is found in the buffer, the next 16 characters
are moved to the data array.
DATA PATTERN
36 bytes of data
4 bytes checksum
4 bytes end signal (0,0,128,127)
*/
void storeData()
{
int j;
count=0;
checkSumPass = false;
//position in buffer where good data is found
startPos = next_out;
//store checksum startPos and account for wrap around
if(startPos < 8)
{
startCS = BUFFER_SIZE - (4 - startPos);
}
else
{
startCS = startPos - 8;
}
//store position of last character of positional data
startPos = startPos - 8;
//build checksum
for (gpos=0;gpos<4;gpos++)
{
checksum[gpos]=buffer[startCS];
startCS++;
}
//set start of real data
if(startPos > DATA_SIZE)
{ //start of real data is
startPos = startPos - DATA_SIZE;
}
else
{
startPos = BUFFER_SIZE - (DATA_SIZE - startPos);
}
//switch on led to signal data found
output_bit(START_FOUND_LED,1);
//get data into data buffer
for (j=0;j<DATA_SIZE;j++)
{
data[j]=buffer[startPos];
startPos=(startPos+1) % BUFFER_SIZE;
}
for (i=0;i<8;i++) {
out_data01[i]=data[count];
out_data02[i]=data[count+8];
out_data03[i]=data[count+16];
out_data04[i]=data[count+24];
if(i<4)
{
out_data05[i]=data[count+32];
}
count++;
}
if ( can_tbe() )
{
result01=can_putd(tx_id01, out_data01, tx_len,tx_pri,tx_ext,tx_rtr); //put data on transmit buffer
result02=can_putd(tx_id02, out_data02, tx_len,tx_pri,tx_ext,tx_rtr); //put data on transmit buffer
result03=can_putd(tx_id03, out_data03, tx_len,tx_pri,tx_ext,tx_rtr); //put data on transmit buffer
result04=can_putd(tx_id04, out_data04, tx_len,tx_pri,tx_ext,tx_rtr); //put data on transmit buffer
result05=can_putd(tx_id05, out_data05, tx_len,tx_pri,tx_ext,tx_rtr); //put data on transmit buffer
if (result01 != 0xFF) { //success, a transmit buffer was open
//! for (i=0;i<8;i++) {
//! out_data[i]=count;
//! }
//! count++;
//output_bit(PIN_D0,0);
}
else { //fail, no transmit buffer was open
output_bit(CAN_ERROR_LED,1);
}
}
startSignalFound = 0;
output_bit(START_FOUND_LED,0);
}
/*
SERVICE RING BUFFER
Method which removes chars from the buffer one by one and check each for
start char then rest of start signal. If startSignal then add char to data
buffer until dataCount is 16. The set datacount to zero, start to 0.
*/
void service_ring_buffer()
{
while(bkbhit)
{
ch = bgetc();
//check for start bit
switch (state) {
case 0:
if (ch==0) state=1;
break;
case 1:
if (ch==0) state=2;
else state=0;
break;
case 2:
if (ch==128) {
state=3;
break;
}
else if (ch==0) state=1;
else state=0;
break;
case 3:
if (ch==127) {
startSignalFound = 1;
storeData();
break;
}
else if (ch==0) state=1;
else state=0;
break;
}
}
}
void main() {
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);
can_init();
enable_interrupts(global);
enable_interrupts(int_rda);
//ch = "0";
clearBuffer();
clearData();
output_bit(ERROR_LED,0);
output_bit(START_FOUND_LED,0);
do {
service_ring_buffer();
} while (TRUE);
}
|
Thanks especially to you jecottrell! |
|
|
|
|
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
|