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

Buffer error!

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

Buffer error!
PostPosted: Thu Jul 17, 2014 6:11 pm     Reply with quote

Hi,

I'm trying to catch commands with the rs232 pic hardware.

I want the buffer to start buffering when i send the start char ($) and stop buffering (* <- end delimiter) then display the data to the LCD.

Take a look to the modified code (yes, i'm using the circular buffer example from CCS)

Code:

#include <18F4685.h>
#FUSES HS,NOMCLR,NOLVP,NOPBADEN,BROWNOUT,BORV46,PUT

#use delay(clock=12M)
#use rs232(baud=9600, xmit=PIN_C6,rcv=PIN_C7,errors)

#include <lcd4x40.c>


#define BUFFER_SIZE 40
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
int8 buffermode = 0;
byte tmprx;



//****************************d*************************************************
char version[4] = "0.1"; /// change when releasing a new version !!!!!! <<<---
//*****************************************************************************

#int_rda
void serial_isr() {
   int t;
   tmprx = getc();
   if (buffermode = 1){
   buffer[next_in]=tmprx;
   t=next_in;
   next_in=(next_in+1) % BUFFER_SIZE;
   if(next_in==next_out)
     next_in=t;           // Buffer full !!
     }
  if (tmprx == "$"){buffermode = 1;} //start the buffering
  if (tmprx == "*"){buffermode = 3;} //buffer full an ready to be read
     
}

#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 main() 
{
   int8 i;
   enable_interrupts(int_rda);
   enable_interrupts(global);
   lcd_init1();
   delay_ms(100);
   lcd_init2();
   delay_ms(100);
   

   while(1)
   {
   if (buffermode == 3) //buffer is ready then we read it!
      {
         for(i=0 ;i < 40; i++)
         {
         lcd_putc1(buffer[i]);
         }
         next_in = 0; //reset buffer data
         next_out = 0; //reset buffer data
         buffermode = 0; //set the wait for $ flag before buffering
      }
   }
}



Why the $ and * serial char flags? Because I want other commands has well.

example:

$insert text here* = display lcd message
! = start a relay
% = led on



My code doesn't work i'm clueless why...

Btw, I've tested the lcd code before trying this more advanced code Smile



PCWHD 5.025

thank you very much!
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!


Last edited by ELCouz on Tue Jul 22, 2014 8:24 am; edited 2 times in total
jeremiah



Joined: 20 Jul 2010
Posts: 1354

View user's profile Send private message

PostPosted: Thu Jul 17, 2014 7:23 pm     Reply with quote

Some basic issues:

you modified the ccs example. Your buffersize is now 40. This is a no no for that example. You either need to change the buffersize to a power of two or change the logic that uses the % sign (modulus operator) to be a faster implementation. Modulus operations are slow when not powers of two and thus shouldn't be used in an ISR if you intend to use a non power of two buffersize.

Your comparisons to $ and * are using " instead of ' like a character should. This will make your comparisons fail since you are comparing to a random address rather than the characters you want.
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Thu Jul 17, 2014 7:26 pm     Reply with quote

jeremiah wrote:
Some basic issues:

you modified the ccs example. Your buffersize is now 40. This is a no no for that example. You either need to change the buffersize to a power of two or change the logic that uses the % sign (modulus operator) to be a faster implementation. Modulus operations are slow when not powers of two and thus shouldn't be used in an ISR if you intend to use a non power of two buffersize.

Your comparisons to $ and * are using " instead of ' like a character should. This will make your comparisons fail since you are comparing to a random address rather than the characters you want.


Because my LCD is 40 char long. 64 bytes is too much no?

Oops thanks for pointing me out my typo...I'll try in a few hours when I have access to my board Smile

thank you again! Smile
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Ttelmah



Joined: 11 Mar 2010
Posts: 19538

View user's profile Send private message

PostPosted: Fri Jul 18, 2014 1:09 am     Reply with quote

The replacement code is:

Code:

   //replace  next_in=(next_in+1) % BUFFER_SIZE;
   if (++next_in==BUFFER_SIZE)
      next_in=0;


It's one of those things that has been mentioned here many times, and should really have a warning in the example.

Code:

#include <18F4685.h>
#FUSES HS,NOMCLR,NOLVP,NOPBADEN,BROWNOUT,BORV46,PUT

#use delay(clock=12M)
#use rs232(baud=9600, xmit=PIN_C6,rcv=PIN_C7,errors)

#include <lcd4x40.c>

#define BUFFER_SIZE 40
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
int1 have_line=FALSE; //flag to main 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);
}

#int_rda
void serial_isr()
{
   char tmprx; //don't make more things 'global' that are needed
   int1 get_data=FALSE;
   tmprx = getc();
   if (get_data)
   {
      buffer[next_in]=tmprx;
      t=next_in;
      if (++next_in==BUFFER_SIZE)
         next_in=0;
      if(next_in==next_out)
         next_in=t;           // Buffer full !!
   }
   if (tmprx == '$')
       get_data=TRUE; //start the buffering
   if (tmprx == '*')
   {
       have_line=TRUE; //Flag main code that data is here
       get_data=FALSE;
   }   
}

void main()
{
   int8 i;
   enable_interrupts(int_rda);
   enable_interrupts(global);
   delay_ms(300); //you want a delay _before_ init.
   lcd_init1();
   lcd_init2();
 
   while(1)
   {
      if (have_data) //buffer is ready then we read it!
      {
         have_data=FALSE;
         do
         {
             lcd_putc(bgetc());
         } while bkbhit;
         //There is no guarantee that you have 40 characters...
      }
   }
}


Now comments:

1) The change away from '%'.
2) Character values for the tests, not strings.
3) You could have any number of characters between the $ and *. Counting for the 40 characters will not handle if this is not 40. That's the point of the bkbhit test.
4) The '%' test in the external code doesn't matter. It is though, slower and bulkier than the alternative test shown, when using a non binary buffer size.
5) Having a buffer larger than 40 characters will only matter if the message is ever longer than 40 characters.
6) There is no need to re-initialise the buffer.
7) There wants to be a delay _before_ calling lcd_init.

Your version flag is omitted from this.
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Fri Jul 18, 2014 3:51 am     Reply with quote

Thank you Ttelmah for your help!

The code had littles variables declaring errors so I've fixed them.

Code:
#include <18F4685.h>
#FUSES HS,NOMCLR,NOLVP,NOPBADEN,BROWNOUT,BORV46,PUT

#use delay(clock=12M)
#use rs232(baud=9600, xmit=PIN_C6,rcv=PIN_C7,errors)

#include <lcd4x40.c>

#define BUFFER_SIZE 40
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
int1 have_line=FALSE; //flag to main 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);
}

#int_rda
void serial_isr()
{
   int t;
   char tmprx; //don't make more things 'global' that are needed
   int1 get_data=FALSE;
   tmprx = getc();
   if (get_data)
   {
      buffer[next_in]=tmprx;
      t=next_in;
      if (++next_in==BUFFER_SIZE)
         next_in=0;
      if(next_in==next_out)
         next_in=t;           // Buffer full !!
   }
   if (tmprx == '$')
       get_data=TRUE; //start the buffering
   if (tmprx == '*')
   {
       have_line=TRUE; //Flag main code that data is here
       get_data=FALSE;
   }   
}
void main() 
{
   enable_interrupts(int_rda);
   enable_interrupts(global);
   delay_ms(300);
   lcd_init1();
   lcd_init2();
   printf(lcd_putc1,"Program ready");
   

   while(1)
   {
      if (have_line) //buffer is ready then we read it!
      {
         have_line=FALSE;
         do
         {
             lcd_putc1(bgetc());
         } while bkbhit;
         //There is no guarantee that you have 40 characters...
      }
   }
}



While looking good on paper, running the program result nothing being display on the lcd.

After program ready, I've tried many combination possible... out of luck!

I feel that the have a line is never triggered.

ex: $12345656789* nothing
$abc* nothing
$ihavefortycharactersnowhehehethisislong* nothing

Also I've tested my usb to rs232 ttl cable (FTDI TTL-232R5V) work really good with the PIC.
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
alan



Joined: 12 Nov 2012
Posts: 357
Location: South Africa

View user's profile Send private message

PostPosted: Fri Jul 18, 2014 4:23 am     Reply with quote

This should
Code:
int1 get_data=FALSE

probably be
Code:
static int1 get_data=FALSE


Regards
Ttelmah



Joined: 11 Mar 2010
Posts: 19538

View user's profile Send private message

PostPosted: Fri Jul 18, 2014 6:55 am     Reply with quote

Indeed it should, otherwise the variable gets cleared every time the routine is called. Hence data will never be 'got'.....
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sat Jul 19, 2014 8:12 am     Reply with quote

It works !!!

EDIT: Also thanks to jeremiah for pointing me the obvious hehe



Some code fine tuning.

-Modified the ISR to skip the * char in the buffer

-Clear the lcd screen when a buffer dump happen

- Increased the buffer to 64 (had some garbage / missing char in the buffer)




Code:

#include <18F4685.h>
#FUSES HS,NOMCLR,NOLVP,NOPBADEN,BROWNOUT,BORV46,PUT

#use delay(clock=12M)
#use rs232(baud=9600, xmit=PIN_C6,rcv=PIN_C7,errors)

#include <lcd4x40.c>

#define BUFFER_SIZE 64
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
int1 have_line=FALSE; //flag to main 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);
}

#int_rda
void serial_isr()
{
   int t;
   char tmprx; //don't make more things 'global' that are needed
   static int1 get_data=FALSE;
   tmprx = getc();
   if (get_data && tmprx != '*')
   {
      buffer[next_in]=tmprx;
      t=next_in;
      if (++next_in==BUFFER_SIZE)
         next_in=0;
      if(next_in==next_out)
         next_in=t;           // Buffer full !!
   }
   if (tmprx == '$')
       get_data=TRUE; //start the buffering
   if (tmprx == '*')
   {
       have_line=TRUE; //Flag main code that data is here
       get_data=FALSE;
   }   
}
void main() 
{
   enable_interrupts(int_rda);
   enable_interrupts(global);
   delay_ms(300);
   lcd_init1();
   lcd_init2();
   printf(lcd_putc1,"Program ready");
   

   while(1)
   {
      if (have_line) //buffer is ready then we read it!
      {
      lcd_putc1('\f'); //clear the screen when receiving the buffer data
         have_line=FALSE;
         do
         {
             lcd_putc1(bgetc());
         } while bkbhit;
         //There is no guarantee that you have 40 characters...
      }
   }
}


_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Ttelmah



Joined: 11 Mar 2010
Posts: 19538

View user's profile Send private message

PostPosted: Sat Jul 19, 2014 8:19 am     Reply with quote

Flag the thread as 'solved'.

Well done... Smile
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sat Jul 19, 2014 8:29 am     Reply with quote

One last thing..

If I pass a short char command let say : @ = turn led on

What would be the best way to avoid conflict with the ISR buffer?

ex: prevent the [email protected]* being catched by the ISR command routine (turn the led on in middle of my text command)

I know everyone have their way to do it. But this is just to get me started in a correct manner.

thank you guys!

EDIT: i was thinking not using alphabetic command (ESC char, Del char etc)
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Ttelmah



Joined: 11 Mar 2010
Posts: 19538

View user's profile Send private message

PostPosted: Sat Jul 19, 2014 8:50 am     Reply with quote

Tests generically are very fast. The compiler just subtracts the 'test' value, and does a jump if the result is zero (or not zero, depending on the way round the test is done). Consider something like:
Code:

#int_rda
void serial_isr()
{
   int t;
   char tmprx; //don't make more things 'global' that are needed
   static int1 get_data=FALSE;
   tmprx = getc();
   if (bit_test(temprx,7))
   {
      //Will only get here if the character is >127
      //Test here for what you want, and do the operation     

      return; //exit so stuff does not go to buffer
   }
   if (get_data && tmprx != '*')
   {
      buffer[next_in]=tmprx;
      t=next_in;
      if (++next_in==BUFFER_SIZE)
         next_in=0;
      if(next_in==next_out)
         next_in=t;           // Buffer full !!
   }
   if (tmprx == '$')
       get_data=TRUE; //start the buffering
   if (tmprx == '*')
   {
       have_line=TRUE; //Flag main code that data is here
       get_data=FALSE;
   }   
}

This uses bit 7 of the character as a 'flag' to say this is a command. Then test for the actual character you want, and exit without ever going to the buffer code.

If you want the LED to go on/off when the character is received, then set/clear it in the interrupt. Otherwise set/clear a flag, and in the main code, after 'have_line' is seen, then set/clear the LED at this point.
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Sat Jul 19, 2014 9:08 am     Reply with quote

Thanks Ttelmah.

Using the extended ASCII table part is a clever trick! Smile

Note taken & thread solved.

Have a nice day.
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Mon Jul 21, 2014 7:19 pm     Reply with quote

Updated the code to drive the 4x40 correctly...

Code:
#include <18F4685.h>
#FUSES HS,NOMCLR,NOLVP,NOPBADEN,BROWNOUT,BORV46,PUT

#use delay(clock=12M)
#use rs232(baud=9600, xmit=PIN_C6,rcv=PIN_C7,errors)

#include <lcd4x40.c>

#define BUFFER_SIZE 196
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
int1 have_line=FALSE; //flag to main 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);
}

#int_rda
void serial_isr()
{
   int t;
   char tmprx; //don't make more things 'global' that are needed
   static int1 get_data=FALSE;
   tmprx = getc();

   if (get_data && tmprx != '*')
   {
      buffer[next_in]=tmprx;
      t=next_in;
      if (++next_in==BUFFER_SIZE)
         next_in=0;
      if(next_in==next_out)
         next_in=t;           // Buffer full !!
   }
   if (tmprx == '$')
       get_data=TRUE; //start the buffering
   if (tmprx == '*')
   {
       have_line=TRUE; //Flag main code that data is here
       get_data=FALSE;
   }   
}
void main() 
{
int i;
   enable_interrupts(int_rda);
   enable_interrupts(global);
   delay_ms(300);
   lcd_init1();
   lcd_init2();
   printf(lcd_putc1,"Program ready");
   

   while(1)
   {
      if (have_line) //buffer is ready then we read it!
      {
      lcd_putc1('\f'); //clear the screen when receiving the buffer data
      lcd_putc2('\f');
         have_line=FALSE;
         do
         {
             for(i=0 ;i < 80; i++) {lcd_putc1(bgetc());} //split for the first LCD module
             for(i=80 ;i < 160; i++) {lcd_putc2(bgetc());} //show the rest on the second module
             
         } while bkbhit;
         //There is no guarantee that you have 40 characters...
      }
   }
}


This is the final working code Smile
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
ELCouz



Joined: 18 Jul 2007
Posts: 427
Location: Montreal,Quebec

View user's profile Send private message

PostPosted: Mon Jul 21, 2014 11:33 pm     Reply with quote

One last question, I promise...

Some times the buffer order get screwed up.

What would be the procedure to reinitialize the buffer?

I've tried resetting the buffer with

Code:
next_in = 0; //reset buffer data
next_out = 0; //reset buffer data


Doesn't seem to work...

It would also help me reset the buffer when it miss the * end char so the buffer ISR would stop capturing after 160 chars (kind of a buffer timeout)

Any ideas??

thanks!
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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