CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

UART receive buffer
Goto page Previous  1, 2
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Sat Jul 09, 2016 11:19 pm     Reply with quote

Deep breath.

You say that you are sending:
"!160707,16,1,0.35,10".

Your receive code though is looking for a null terminator.

You do realise, that if you send a string, _the null terminator does not get sent_.

The null terminator, is used inside C to mark the end of a string. When a string is printed, the print stops when it gets to this character.

This is why when sending the standard is to use your own character as a terminator. Very commonly the line feed. Then the receive code looks for the line feed, and substitutes the terminator.
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 4:01 am     Reply with quote

Hi Ttelmah
Thanks for that. I hadn't thought of that.
I am pleased to say that the serial routines from SeeCwriter work flawlessly, so I have abandoned the ex_sisr methods.
I have been trying the checksum facility, which is new to me.
I see that he gets the checksum value by XORing the variable with each byte of the message, from the last to the first character. In his routines, it has a value of 16 for the string I am sending. However, when I try and do the same to generate the checksum at the transmit program, I get a value of nearly 90,000, so I'm foxed!!

Brian
temtronic



Joined: 01 Jul 2010
Posts: 9632
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 5:19 am     Reply with quote

re:
However, when I try and do the same to generate the checksum at the transmit program, I get a value of nearly 90,000, so I'm foxed!!
...


If you mean from the PI.. maybe it's cause the PI declare integers as 16 bits not 8 bits? or you don't clear out the checksum variable before doing the calculation ? Come to think of it 90,000 is even bigger than 65535( 16 bits) so 'something' is weird.
Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 5:36 am     Reply with quote

The Pi uses int32 as standard (it is a 32bit processor). You need to & the result with 0xFF.
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 8:03 am     Reply with quote

Thanks for that.
The other problem now!
I can send one of 2 strings, one sets the RTC and starts with a ?. The other clears the internal eeprom and sends a *.
(Each includes <STX> and <ETX>)
On first powering up, sending either string does as expected. However, if say I have sent the RTC string and the clock has set and then I send the reset string, it sets the time and day again. Sending the reset string again causes a reset.
Similarly, sending reset works, but then sending RTC resets again. The next sending of RTC string sets the RTC. I am using ResetMsg(&msg) each time.
Oh dear!!

Brian
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 8:33 am     Reply with quote

Whatever 'flag' you are setting to say 'I have found this particular message', is not being reset when you finish parsing the message.

Down to how your parser is written.....
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 11:02 am     Reply with quote

I am obviously misunderstanding what is going on and it's driving me nuts!
As I understand it, once the msg.done flag is set, the received message is in msg.ch.
I pass to my processing routine a pointer to msg.ch. It processes the data into an array, rcvData. As soon as I have used the info in rcvData, I clear it.
However, when the next message arrives and I process msg.ch, I get the same data as before, as rcvData gets filled with the same info.
This is what I don't understand. I have tried to clear msg.ch, but no difference. I don't know what else I am supposed to do!
Oh to see the wood for the trees!

Brian
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 11:32 am     Reply with quote

Your approach sounds far too complex.

Simply send your original string, with a line feed after it.

Then in the receive code, use:
Code:

//your header for processor
#use rs232(UART1, baud=9600, ERRORS)

#define BUFFER_SIZE 32
char buffer[BUFFER_SIZE][2];
int8 in_use=0;
int1 have_line=FALSE;
#define READ_BUFFER &buffer[0][in_use^1]
//This gives the address of the 'out of use' buffer

#int_rda
void serial_isr(void)
{
   int8 t; 
   static int8 next_in=0;

   t=getc();
   if (t=='\n')
   {
       buffer[next_in]='\0';
       next_in=0;
       in_use ^=1; //toggle buffer
       have_line=TRUE;
   }
   else
   {
       buffer[next_in][in_use]=t;
       if (++next_in==BUFFER_SIZE-1)
           next_in--; //lock at end of buffer
    }
}
//This uses two 'line' buffers, instead of a circular buffer.
//When a line feed is seen, it terminates the current one, sets the
//flag 'have_line', and starts using the second buffer.

//Then you need a function 'process' defined, expecting a pointer
//to a character array.

//Then in your main
void main(void)
{
    enable_interrupts(GLOBAL);
    enable_interrupts(INT_RDA);
    //and anything else you need

    while (TRUE)
    {
        if (have_line)
        {
            have_line=FALSE;
            process(READ_BUFFER);
        }
    }
}

Then 'process', receives the address of the start of a line containing your data. Terminated with a null (so string functions etc., will work), and this will stay unchanged, till the next line is complete. Since you only call it when a line is seen, no need to clear things etc. etc.. The buffer will stop filling when it gets full (31 characters max).
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 1:31 pm     Reply with quote

Hi Ttelmah, Thanks for the code.
I have set it up but the compiler is complaining on the line:
buffer[next_in] = '\0';
saying "Expecting LVALUE such as variable name or *expression!
Do I presume correctly that it should read buffer[next_in][0] = '\0'; ?

Brian
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

PostPosted: Sun Jul 10, 2016 3:03 pm     Reply with quote

Hi Ttelmah, The program is working but I am getting gibberish!
If I send just an * plus\n, I get **,,1100,,,610,700.83,,

sending a string as ?YYDDMMhhmmss plus \n, I get something like:
?*1166007711,,1100,,,610,700.83,,
I'm sure it's me! Here's the listing:

Code:
#include <18LF4620.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WDT128                   //Watch Dog Timer uses 1:128 Postscale
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#FUSES H4

#use delay(clock=40MHz,crystal=10MHz)

#use rs232(UART1, baud=9600, ERRORS)
#include <Flex_LCD420.c>
#define BUFFER_SIZE 32
char buffer[BUFFER_SIZE][2];
int8 in_use = 0;
int1 have_line = FALSE;
#define READ_BUFFER &buffer[0][in_use^1]
//This gives the address of the 'out of use' buffer
char rcvData[30][12];
void process(char *p);

#int_rda
void serial_isr(void)
{
 int8 t; 
 static int8 next_in = 0;

 t = getc();
 if(t == '\n')
    {
     buffer[next_in][0] = '\0';
     next_in = 0;
     in_use ^= 1; //toggle buffer
     have_line = TRUE;
    }
   else
    {
     buffer[next_in][IN_USE] = t;
     if(++next_in == BUFFER_SIZE - 1)
      next_in--; //lock at end of buffer
   }
}
//This uses two 'line' buffers, instead of a circular buffer.
//When a line feed is seen, it terminates the current one, sets the
//flag 'have_line', and starts using the second buffer.
//---------------------------------------------------------------------------
void main()
{
lcd_init();
lcd_load_custom_chars();

enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
//and anything else you need
while(TRUE)
 {
  if(have_line)
   {
    have_line = FALSE;
    process(READ_BUFFER);
   }
  delay_ms(300);
 }
}
//---------------------------------------------------------------------------
void process(char* p)
{
int i=0, j=0;

while(*p != '\n')
 {
  if(*p != ',')
   {
    rcvData[j][i] = *p;
    i++;
   }//end if *p..
  else
   {
    rcvData[j][i] = '\0';
    i = 0;
    j++;
  }// end else
 p++;
 }//end while
lcd_putc("\f");
//printf(lcd_putc,"items %u", j+1);
lcd_gotoxy(1,2);
for(i = 0; i < j;i++)
 printf(lcd_putc,"%s,", rcvData[i]);

for(i=0;i!=30;i++)
 for(j=0;j!=12;j++)
  rcvData[i][j] = '\0';
}
//---------------------------------------------------------------------------


Brian
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 6:44 am     Reply with quote

BLL wrote:
Hi Ttelmah, Thanks for the code.
I have set it up but the compiler is complaining on the line:
buffer[next_in] = '\0';
saying "Expecting LVALUE such as variable name or *expression!
Do I presume correctly that it should read buffer[next_in][0] = '\0'; ?

Brian


No, it needs to write to the in_use buffer. So:

read buffer[next_in][in_use] = '\0';

Otherwise it'll alternatively terminate the wrong buffer!.

This would probably give your garbage.... Very Happy
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

PostPosted: Mon Jul 11, 2016 12:25 pm     Reply with quote

Hi Ttelmah, Thanks for the correction, but it still gives gibberish! I did find that my parsing routine was not bomb-proof and rewrote it in a simple test program:
Code:

//test decoding of data strings from RasPi
#include <Decoder.h>
#include <string.h>
#include <Flex_LCD420.c>

int8 j = 0;
char info[30];
char rcvData[20][40];

void process(char*);
//---------------------------------------------------------------------------
void main()
{
int8 i;
lcd_init();
lcd_load_custom_chars();
memset(rcvData, '\0', sizeof(rcvData));

strcpy(info, "?16,07,11,14:20:30\n");

//to be valid, data must end in an LF and not only be an LF
//jump over command char at info[0]
if(info[strlen(info)-1] == '\n' && strlen(info) > 1)
 process(&info[1]);
 
for(i = 0; i < j + 1; i++)
 printf(lcd_putc,"%s,",rcvData[i]);
lcd_putc("\b");
lcd_putc(' ');
memset(rcvData, '\0', sizeof(rcvData));
}
//---------------------------------------------------------------------------
void process(char *p)
{
int8 i = 0;
do
 {
  if(*p != ',')
   {
    rcvData[j][i] = *p;
    i++;
   }//end if *p..
  else
   {
    rcvData[j][i] = '\0';
    i = 0;
    j++;
   }//end else
  p++;
 }while(*p != '\n');
 
return(j);
}
//----------------------------------------------------------------------------

With this, only a string with one or more characters before a LF is processed. The processing is done correctly, regardless of the string length.
I incorporated just the function into your code, so process(READ_MESSAGE); calls my function. It doesn't work correctly.
If I put a printf line at the start of process, to read just the 0th character, this reads correctly.
It is interesting to note that sending a string starting with ?160711 shows as ??116600771111. The characters are there but duplicated! This must shed light on the problem, but I can't fathom it.
Brian
jeremiah



Joined: 20 Jul 2010
Posts: 1411

View user's profile Send private message

PostPosted: Wed Jul 13, 2016 8:52 am     Reply with quote

I tried your process function and found it still had quite a few holes and led to some gibberish outputs when hitting edge conditions.

Last night I took some time to lay out some functions to do the work load. I went back to a same old circular buffer implementation and did the line acquiring and parsing in two separate main functions.

Note that I don't have the same CHIP or LCD as you so I had to test with what I had, but the following code worked for me rather well and handled all the edge conditions I could come up with in a short time. Note that my LCD functions are a bit different so you will have to adjust to your liking. I didn't have a \b character implemented, so I just left the trailing ',' characters. Also, I didn't put in any code to handle an input where a \n follows a ',' (neither did your old code).

Compiled with 5.059 and tested on my dev board with LCD
Code:


#include "pic.h"  //my FUSES, #use delay(), #use rs232(), pin defs, etc.
#include "lcd.h"  //my lcd driver...different than yours
#include <string.h>


///////////////////////////////////////////////////////////////////////////////
// Interrupt driven serial routines
///////////////////////////////////////////////////////////////////////////////
#define BUFFER_SIZE 255
volatile BYTE buffer[BUFFER_SIZE];
volatile BYTE next_in = 0;
volatile BYTE next_out = 0;
#if BUFFER_SIZE >= 256
   #error Size of buffer ( BUFFER_SIZE ) is too large for the index type
#endif

#int_rda
void serial_isr() {
   BYTE temp_next;
   BYTE temp_char;
   
   temp_char = getc();
   temp_next = next_in + 1;
   
   //Wrap index if it reaches buffer size
   if(temp_next >= BUFFER_SIZE){
      temp_next = 0;
   }
   
   //if there is space, the add character
   if(temp_next != next_out){
      buffer[next_in] = temp_char;
      next_in = temp_next;
   }
}

#define bkbhit (next_in != next_out)

BYTE bgetc() {
   BYTE c;
   BYTE temp_next;
   
   temp_next = next_out + 1;
   if(temp_next >= BUFFER_SIZE){
      temp_next = 0;
   }

   while(!bkbhit) ;
   c=buffer[next_out];
   next_out = temp_next;
   return(c);
}




///////////////////////////////////////////////////////////////////////////////
// Data processing routines
///////////////////////////////////////////////////////////////////////////////
#define DATA_ROWS 20
#define DATA_COLS 40
#define TERMINATOR '\n'
#define SEPARATOR  ','
#define END_OF_STR '\0'

//I use this type to easily pass a 2D array by reference
typedef struct{
   char rx_data[DATA_ROWS][DATA_COLS];
} rx_data_t;



unsigned int8 process
   (char *        input,
    rx_data_t *   output,
    unsigned int8 max_rows,
    unsigned int8 max_cols)
{
   unsigned int8 row = 0;
   unsigned int8 col = 0;
   
   if(max_rows == 0 || max_cols == 0){
      return 0;
   }
   
   while(*input != TERMINATOR){
     
      switch(*input){
     
         //catches case where \0 happens before a , or \n
         case END_OF_STR:
            output->rx_data[row++][col] = END_OF_STR;
            return row;
            break;
         
         //finish current row and start another
         case SEPARATOR:
            output->rx_data[row++][col] = END_OF_STR;
            if(row >= max_rows){
               return row;
            }
            col = 0;
            break;
         
         //normal character copy
         default:   
            output->rx_data[row][col++] = *input;
            if(col >= max_cols){
               output->rx_data[row++][col-1] = END_OF_STR;
               return row;
            }
            break;
      }
      input++;
   }
   
   //last character finished so convert \n to \0
   output->rx_data[row++][col] = END_OF_STR;
   return row;
}


BOOLEAN wait_on_line(char * buffer, unsigned int8 size){
   BYTE index = 0;
   
   if(size == 0){
      return FALSE;
   }
   
   while(index < (size-1)){  //need room for \0
      if(bkbhit){
         buffer[index] = bgetc();
         
         switch(buffer[index++]){
            //catches if '\0' happens inadvertantly
            case END_OF_STR:
               return FALSE;
               break;
               
            //found an line so done
            case TERMINATOR:
               buffer[index] = END_OF_STR; /***********IMPORTANT************/
               return TRUE;
               break;
               
            default:
               break;
         }
         
      }
   }
   
   //never found a line and ran out of space
   buffer[size-1] = END_OF_STR;  /***********IMPORTANT************/
   return FALSE;
}



///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
void main() {

   BYTE data[BUFFER_SIZE];
   int8 i,numRows;
   rx_data_t rcv_data;
   
   lcd_init();  //default pin settings
   delay_ms(1000);  //need time for the LCD to powerup ok
   lcd_powerOn();  //send configuration commands
   lcd_clear();   //clears the display and sets cursor to 0,0

   enable_interrupts(int_rda);
   enable_interrupts(global);   

   printf("\r\n\Running...\r\n");

               // The program will delay for 10 seconds and then display
               // any data that came in during the 10 second delay

   printf("\r\nBuffered data => \r\n");
   do {
   
      if(wait_on_line(data,sizeof(data))){
     
         if(strlen(data) > 1){
                 
            lcd_clear();
     
            memset(&rcv_data, '\0', sizeof(rx_data_t));
           
            numRows = process(data,
                              &rcv_data,
                              DATA_ROWS,
                              DATA_COLS);
           
            printf("Data: %s => ", data);
               
            for(i = 0; i < numRows; i++){
               printf(lcd_putc,"%s,",rcv_data.rx_data[i]);
               printf("%s,",rcv_data.rx_data[i]);
            }
            printf("\r\n");
         }else{
            printf("Empty String\r\n");
         }
      }else{
         printf("Error waiting on line\r\n");
      }
     
   } while (TRUE);
}


#include "lcd.c"
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

PostPosted: Wed Jul 13, 2016 9:06 am     Reply with quote

Hi Ttelmah

I have finally got the code working, although I don't understand why the problem and why the solution works!
I confirmed that buffer was getting 2 of everything. So, if I sent ?160713,
buffer contained ??116600771133. I don't see why.

The only cure was to copy the contents of buffer to a string:

Code:
//this eliminates the double characters!
char data[BUFFER_SIZE];
    for(l=0; l < BUFFER_SIZE-1; l++)
     data[l] = buffer[l][!IN_USE];


I then passed this string to the process function and all was fine.

It now works as expected, but I would like to know why the buffer problem and why the solution works!!!

Thanks for your help.

Brian
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

PostPosted: Wed Jul 13, 2016 9:11 am     Reply with quote

Hi Jeremiah
Thank you so much for taking the time to produce the code. I shall study it and try it.
You have probably seen my post to Ttelmah, saying that his code is now working fine after a very strange problem.
Considering, I am only sending short strings of data very infrequently, I have been surprised at the problems I have had.
So far, I am not getting any bad data and all is fine.
Thank you for your help.

Brian
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page Previous  1, 2
Page 2 of 2

 
Jump to:  
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