|
|
View previous topic :: View next topic |
Author |
Message |
vinniewryan
Joined: 29 Jul 2009 Posts: 154 Location: at work
|
kbhit problem with 16f684 |
Posted: Fri Apr 30, 2010 9:25 pm |
|
|
I am using a 16f684, compiler version 4.03, and I'm having a problem with the KBHIT function.
Currently my RX pin is pin_a0, and I am able to receive values perfectly fine through this pin, until I try using kbhit. To be more specific, here's an example:
This WORKS:
Code: |
void main()
{
if(state==0)
{
var=getc();
state=1;
}
}
|
'var' successfully equals whatever I send over the rs232, but it's constantly checking kbhit which slows down the cycle time by too much.
This does NOT WORK:
Code: |
void main()
{
if(state==0)
{
if(kbhit())
{
var=getc();
state=1;
}
}
|
The code runs perfectly fast and fine, but never checks kbhit or executes 'getc()', and therefore 'var' never changes.
My question I suppose is, for one is there any reason this problem is occurring, and second, is there a function other than kbhit that I can use to check if a value is available via rs232 pin_a0? Below is the actual code that runs just fine, other than the above mentioned malfunction.
Code: | #include "16f684.h"
#use delay(clock=4000000)
#Fuses NOFCMEN,NOPROTECT,NOMCLR,BROWNOUT,CPD,NOPUT,IESO,INTRC_IO,NOWDT
#use rs232(FORCE_SW, baud=2400, rcv=PIN_a0)
int8 A=0;
int8 var=0;
int8 state=0;
int16 O=500; //OUTPUT time
int8 ON=1;
int16 OT=0;
int16 frame=0;
int16 fpms=25; //frames per milisecond
int16 milisecond=0;
int1 milisecondspassed=0;
int8 second=0;
int1 secondspassed=0;
int1 minutespassed=0;
int16 timer=0;
int16 timer1=0;
void output()
{
if(ON==1)
{
timer++;
if(timer1<O)
{
if(timer>3000)
{
timer=0;
}
else if(timer>2000)
{
output_high(pin_c1);
}
else if(timer>1000)
{
output_high(pin_c0);
}
else if(timer>0)
{
output_high(pin_a2);
}
timer1++;
}
if(timer1>=O)
{
output_low(pin_c1);
output_low(pin_c0);
output_low(pin_a2);
state=0;
timer1=0;
}
}
if(ON==0)
{
state=0;
output_low(pin_a2);
output_low(pin_c0);
output_low(pin_c1);
}
}
void system()
{
if(state==0)
{
if(kbhit())
{
getc();
var=getc();
}
else
{
var=0;
}
if(var!=0)
{
if(var!=10)
{
if(var!=20)
{
var=0;
}
}
}
else
{
A++;
if(A>200)
{
OT=0;
}
}
if(var==10) //++
{
output_high(pin_c2); //LED
O+=(O/10);
state=1;
OT++;
A=0;
var=0;
if(OT>40)
{
OT=0;
if(ON==0)
{
ON=1;
}
if(ON==1)
{
ON=0;
}
}
}
if(var==20) //--
{
output_high(pin_c2); //LED
ON=1;
OT=0;
O-=(O/10);
state=1;
var=0;
}
if(O>900)
{
O=900;
}
if(O<50)
{
O=50;
}
output_low(pin_c2); //LED
}
output();
}
void main()
{
while(1)
{
milisecondspassed=0;
secondspassed=0;
minutespassed=0;
frame++;
if(frame==fpms)
{
frame=0;
milisecondspassed=1;
milisecond++;
}
if(milisecond==1000)
{
milisecond=0;
secondspassed=1;
second++;
}
if(second==60)
{
second=0;
minutespassed=1;
}
system();
}
} |
_________________ Vinnie Ryan |
|
|
vinniewryan
Joined: 29 Jul 2009 Posts: 154 Location: at work
|
|
Posted: Sat May 01, 2010 12:35 pm |
|
|
After more testing, I've found that when I use PIN_A1 instead, I get a similar problem, but slightly different. When using kbhit and pin_a1, the code runs just fine when there is a byte ready in the rs232, but when no byte is available, the code suddenly runs extremely slow. _________________ Vinnie Ryan |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Sat May 01, 2010 12:55 pm |
|
|
The problem is using software RS232.
With software RS232, 'kbhit', simply checks is the serial input line is low at the _instant_ you call kbhit.....
Miss the start bit, and you have missed the character.
Best Wishes |
|
|
vinniewryan
Joined: 29 Jul 2009 Posts: 154 Location: at work
|
|
Posted: Sat May 01, 2010 1:36 pm |
|
|
I see what you mean. So a workaround might be to check Khbit, and if it IS high (active), to use getc maybe 5 times in a row with a function that's looking for a certain value, and if that value is met, then it can be considered a successful read. Like this?
Code: |
//TX is sending the number '10' via rs232 when I press a button
void main()
{
if(state==0)
{
if(kbhit()) //IF line is active
{
state=1;
}
}
if(state==1)
{
if(counter<5) //check getc() 5 times
{
var=getc();
counter++;
if(var==10) //IF 10 is successfully read
{
//do something;
var=0;
counter=0;
state=0;
}
else //IF 10 is NOT read, reset var
{
var=0;
}
}
else if(counter==5) //IF 10 is not read after 5 times, reset everything
{
counter=0;
var=0;
state=0;
}
}
}
|
_________________ Vinnie Ryan |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat May 01, 2010 10:16 pm |
|
|
Another possible solution is to use the External interrupts pin (INT)
with the Ex_sisr.c example. Then you will get an interrupt on the leading
edge of the start bit, and the byte will be read in the #int_ext interrupt
routine, and put into a circular buffer. Then your main code can all
bkbhit() to see if the buffer holds any characters. If so, then you can
get them with bgetc(). The example file is here:
Quote: | c:\program files\picc\examples\ex_sisr.c |
You would need to change that code so it uses #int_ext instead of #int_rda.
This code would have to be added near the start of main(), to enable
the #int_ext interrupts to detect the leading edge of the start bit.
Code: |
ext_int_edge( H_TO_L );
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
|
This thread has some sample code. It has some other stuff in it
related to getting a float as a string, but you don't need that part.
http://www.ccsinfo.com/forum/viewtopic.php?t=36039&start=4
I didn't try to understand what your code is doing, so it's up to you to
decide if the delay caused by the interrupt routine reading the incoming
byte is acceptable. This delay could occur at any time during your code
execution (if a character comes in), and it would be about 4 ms per haracter. |
|
|
vinniewryan
Joined: 29 Jul 2009 Posts: 154 Location: at work
|
|
Posted: Wed May 05, 2010 4:05 pm |
|
|
Thanks for the info and suggestion PCM, I tried it out and it worked, but the delay was slightly too much.
To get around the problem I simply used a second MCU to strictly receive the serial data, and send high and low signals to input pins on my main MCU. Given more time I could have probably come up with a better fix, but time is always my enemy at my job.
If anyone's curious about the code I used, here it is. This PIC simply receives serial data from an RF receiver and outputs high and low signals to another pic for communication. There are a lot of counters which help the code determine the difference between a serial byte, or RF noise.
Code: | #include "16f684.h"
#use delay(clock=4000000)
#Fuses NOFCMEN,NOPROTECT,NOMCLR,BROWNOUT,CPD,NOPUT,IESO,INTRC_IO,NOWDT
#use rs232(FORCE_SW, baud=2400, rcv=PIN_a1)
//Copyright ©2010 Vincent Westly Ryan
//ALL RIGHTS RESERVED
int8 var=0;
int8 state=0;
int8 counter=0;
int8 counter2=0;
int8 counter1=0;
void system()
{
if(state==0)
{
if(kbhit())
{
var=getc();
state=1;
}
else
{
if(counter2<50)
{
counter2++;
}
else
{
counter=0;
counter1=0;
var=0;
}
}
}
if(state==1)
{
if(var==10)
{
counter2=0;
if(counter<5)
{
counter++;
state=0;
}
else
{
counter=0;
output_high(pin_c0);
state=2;
var=0;
}
}
else if(var==20)
{
counter2=0;
if(counter1<5)
{
counter1++;
state=0;
}
else
{
counter1=0;
output_high(pin_a2);
state=2;
var=0;
}
}
else
{
var=0;
state=0;
}
}
if(state==2)
{
if(input(pin_a0))
{
output_low(pin_c0);
output_low(pin_a2);
state=0;
}
}
}
void main()
{
while(1)
{
system();
}
} |
_________________ Vinnie Ryan |
|
|
|
|
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
|