|
|
View previous topic :: View next topic |
Author |
Message |
ELCouz
Joined: 18 Jul 2007 Posts: 427 Location: Montreal,Quebec
|
Buffer error! |
Posted: Thu Jul 17, 2014 6:11 pm |
|
|
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
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: 1355
|
|
Posted: Thu Jul 17, 2014 7:23 pm |
|
|
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
|
|
Posted: Thu Jul 17, 2014 7:26 pm |
|
|
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
thank you again! _________________ Regards,
Laurent
-----------
Here's my first visual theme for the CCS C Compiler. Enjoy! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19544
|
|
Posted: Fri Jul 18, 2014 1:09 am |
|
|
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
|
|
Posted: Fri Jul 18, 2014 3:51 am |
|
|
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
|
|
Posted: Fri Jul 18, 2014 4:23 am |
|
|
This should
Code: | int1 get_data=FALSE |
probably be
Code: | static int1 get_data=FALSE |
Regards |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19544
|
|
Posted: Fri Jul 18, 2014 6:55 am |
|
|
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
|
|
Posted: Sat Jul 19, 2014 8:12 am |
|
|
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: 19544
|
|
Posted: Sat Jul 19, 2014 8:19 am |
|
|
Flag the thread as 'solved'.
Well done... |
|
|
ELCouz
Joined: 18 Jul 2007 Posts: 427 Location: Montreal,Quebec
|
|
Posted: Sat Jul 19, 2014 8:29 am |
|
|
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: 19544
|
|
Posted: Sat Jul 19, 2014 8:50 am |
|
|
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
|
|
|
ELCouz
Joined: 18 Jul 2007 Posts: 427 Location: Montreal,Quebec
|
|
Posted: Mon Jul 21, 2014 7:19 pm |
|
|
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 _________________ Regards,
Laurent
-----------
Here's my first visual theme for the CCS C Compiler. Enjoy! |
|
|
ELCouz
Joined: 18 Jul 2007 Posts: 427 Location: Montreal,Quebec
|
|
Posted: Mon Jul 21, 2014 11:33 pm |
|
|
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! |
|
|
|
|
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
|