View previous topic :: View next topic |
Author |
Message |
tuflus Guest
|
kbhit() and interrupts |
Posted: Mon May 12, 2008 3:47 pm |
|
|
Hello PicC gurus
I have run into a problem involving kbhit(), a software rs232 port and int_ext. I am building a fingerprint identification system. I use the hardware serial port on an 18F252 to talk to a fingerprint sensor, and a software serial port to talk to a PC. When a finger is placed on the sensor, int_ext is activated and a flag is raised, so the appropriate process can take place on the PIC. Relevant code is as follows:
Code: |
#use rs232(BAUD=57600, XMIT=PIN_B4, RCV=PIN_B3, BITS=8, PARITY=N, STREAM=SERVER)
void main(void)
{
while(true) {
if(kbhit(SERVER)) {
switch(c = fgetc(SERVER)) {
disable_interrupts(INT_EXT);
handleCommand(c);
enable_interrupts(INT_EXT);
break;
}
}
if(fingerDetectedFlag) {
disable_interrupts(INT_EXT);
handleAccess();
fingerDetectedFlag = false;
enable_interrupts(INT_EXT);
}
delay_us(5);
}
}
#int_ext
void ext_isr()
{
fingerDetectedFlag = true;
delay_ms(100); // Debounce
}
|
Well, the problem is that the PIC won't detect characters coming from the PC serial interface when I first turn it on. If I then interrupt by placing my finger on the sensor, handle that and return to the main loop, then kbhit() correctly detects characters from the PC. It's like the interrupt handling process somehow "fixes" whatever problem i'm having. Any ideas? |
|
|
tuflus Guest
|
|
Posted: Mon May 12, 2008 3:50 pm |
|
|
Disregard the "switch" statement in the above code. It's just a mistake I made while editing things |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon May 12, 2008 4:13 pm |
|
|
Quote: |
I use the hardware serial port on an 18F252 to talk to a fingerprint
sensor, and a software serial port to talk to a PC.
#use rs232(BAUD=57600, XMIT=PIN_B4, RCV=PIN_B3, BITS=8, PARITY=N, STREAM=SERVER) |
There is no external interrupt available on pin B3. It's only available
on pins B0, B1, and B2.
The hardware UART uses pins C6 and C7.
Is the "server" the PC ? Your post refers to a sensor and the PC,
so I would expect the streams to be labeled the same way. |
|
|
tuflus Guest
|
|
Posted: Mon May 12, 2008 4:22 pm |
|
|
Yes, the label "SERVER" is the PC. Sorry for not clearing that up
I don't use B3 as an external interrupt, I use B0. And as I said, the "SERVER" stream is a software UART, since I need the hardware UART for the sensor. I use pins B3 and B4 for it, I understand I can use any pins I want for a software UART. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon May 12, 2008 4:48 pm |
|
|
OK, I misunderstood what you were doing.
Quote: | PIC won't detect characters coming from the PC serial interface when I first turn it on |
Because you have the 100ms delay in your #int_ext routine, you should
clear the int_ext interrupt flag before you enable interrupts. Also clear
your software flag. Example:
Code: |
fingerDetectedFlag = FALSE;
clear_interrupt(INT_EXT);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
|
Also, make sure that you have the ERRORS parameter in your
#use rs232() statement for the hardware UART. |
|
|
Humberto
Joined: 08 Sep 2003 Posts: 1215 Location: Buenos Aires, La Reina del Plata
|
|
Posted: Mon May 12, 2008 5:08 pm |
|
|
Doing this:
Code: |
while(true) {
if(kbhit(SERVER))
........
........
|
expecting to capture an incoming char from the PC it is not the best way to use an MCU.
I think that the code shown is only a test example, I mean it is doing nothing more
than expecting a char. Fortunately most applications need to do other useful things,
hence the "waiting loop" is going to be longer, at 57600 bauds there are a high possibility
to lose some characters.
There are a more effective way to implement a software UART without the need of
overhead the MCU doing nothing. The key is to use an external interrupt capabilities of
the MCU just to capture the START bit of the incoming char, then inside the #INT_EXT
you can use the function getc() to get the whole character. You can use any of the 3
external Interrupts of the 18F252.
If you search in this forum, you will find out very helpful examples where we discussed
this point many times.
http://www.ccsinfo.com/forum/viewtopic.php?t=25564&highlight=intext
Humberto |
|
|
tuflus Guest
|
|
Posted: Mon May 12, 2008 5:21 pm |
|
|
Humberto wrote: | Doing this:
expecting to capture an incoming char from the PC it is not the best way to use an MCU. I think that the code shown is only a test example, I mean it is doing nothing more than expecting a char.
Humberto |
Thanks for the input. In fact the code shown is not a test example, in this particular case the MCU is not doing anything else than polling the pins for a start bit and reacting to int_ext. I know this is not the case in most applications, but in mine it is.
Once I interrupt for the first time, things start working smoothly. I am pretty sure missing the start bit is not my problem here. |
|
|
tuflus Guest
|
|
Posted: Mon May 12, 2008 5:33 pm |
|
|
Ok, I just found something out. It's not the interrupt that "fixes" things for me. It's the fact than inside my interrupt handler I call "fgetc(SERVER)". Apparently this call is what allows kbhit(SERVER) to start working right subsequently. What would be the fix? (short of calling fgetc on my init function :p) |
|
|
KU5D
Joined: 10 Feb 2008 Posts: 46 Location: Asheville, North Carolina
|
|
Posted: Mon May 12, 2008 5:42 pm |
|
|
What else do you have going on in your main()? I'm assuming this is test code, so we don't see everything happening in your main(). You could choose a pin, set it at the beginning of your main() loop and clear it at the end, then measure the time it takes to get through the whole loop using a scope.
The reason for the question is this...for kbhit() to work on a soft uart, you need to sample that port at at least ten times the bit rate to reliably capture a character. At 57.6 this is pretty demanding. A hardware uart will take care of this for you to a degree. It takes care of itself until it has a character, then it can fire an interrupt. A software uart can't do this.
For a test, you could try slowing down your PC stream to say 9600. This has a BYTE rate of about 1 ms, so if your main() has a better chance of firing kbhit() when a character is received.
***edit*** you posted while I was writing this. Don't call getc() for your PC (SERVER) stream until you have a kbhit(). _________________ Confidence is the feeling you have right before you fully understand the situation... |
|
|
tuflus Guest
|
|
Posted: Mon May 12, 2008 6:03 pm |
|
|
KU5D wrote: | you posted while I was writing this. Don't call getc() for your PC (SERVER) stream until you have a kbhit(). |
The problem is, kbhit() always returns zero until I call fgetc(SERVER). Apparently something happens in that function that allows kbhit() to start working normally. |
|
|
KU5D
Joined: 10 Feb 2008 Posts: 46 Location: Asheville, North Carolina
|
|
Posted: Mon May 12, 2008 6:31 pm |
|
|
Exactly. You have a soft uart looking for a kbhit() at 57.6. Unless your main() has nothing else to do, you're likely going to miss it and it will never fire. Kbhit() fires when it has a character, then you call getc() to grab the character and toss it into a buffer, etc. I think the call to getc() in your interrupt is forcing the situation and gives the impression that it's working. Again, I'd try to slow down and see what's happening on your soft uart.
Additionally, blindly calling getc() will hang your stuff forever if there's nothing there.
But, one thing at a time... _________________ Confidence is the feeling you have right before you fully understand the situation... |
|
|
tuflus Guest
|
|
Posted: Mon May 12, 2008 6:43 pm |
|
|
Okay, I know polling for the start bit is not the best way of doing things. I know 57600 is quite fast for a software UART. Seriously, I know But I am also sure that the problem here is NOT that the MCU is not polling fast enough.
I've got new code. I've been working on debugging this and I have tracked down the problem. Turns out interrupts have nothing to do with it. It's the interaction between kbhit() and fgetc(). This will make it clear, i hope:
Code: |
void main(void)
{
char command;
/* If this next line gets commented out, kbhit() never detects ANY incoming characters. If it's left in, I have to send any char as soon as the PIC starts up, but then kbhit() works perfectly and detects characters every single time. */
fgetc(SERVER);
while(true) {
if(kbhit(SERVER)) {
disable_interrupts(INT_EXT);
command = fgetc(SERVER);
handleCommand(command);
enable_interrupts(INT_EXT);
}
/* You can ignore this next part. It makes no difference whatsoever */
if(fingerDetectedFlag) {
disable_interrupts(INT_EXT);
handleAccess();
fingerDetectedFlag = false;
clear_interrupt(INT_EXT);
enable_interrupts(INT_EXT);
}
delay_us(5);
}
}
|
As you can see, the reason I thought int_ext had something to do with my problem is that inside handleAccess() there is a call to fgetc(SERVER). It's this call that makes my code start working right.
So, why is kbhit not detecting anything at all at first, and then starts detecting perfectly AFTER a call to fgetc(SERVER)? That is my question.
I hope I have cleared up the question now. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon May 12, 2008 7:53 pm |
|
|
To give us complete information, can you post:
1. The PIC's oscillator frequency.
2. The compiler version.
3. The #fuses statement. |
|
|
KU5D
Joined: 10 Feb 2008 Posts: 46 Location: Asheville, North Carolina
|
|
Posted: Mon May 12, 2008 8:07 pm |
|
|
The biggest subtle difference between a soft uart and a hardware uart: you're not 'polling' for a start bit with the soft uart, it looks for a complete character. The hardware uart can actually trigger an interrupt on the start bit. Unless your going quite fast, I think you're missing the character. I concur with the above...how fast are you running? _________________ Confidence is the feeling you have right before you fully understand the situation... |
|
|
tuflus Guest
|
|
Posted: Mon May 12, 2008 8:14 pm |
|
|
1. It's running a 20 MHz
2. I've tested the code with versions 3.200 and 4.032. Same results
3. These are my fuses:
Code: | /* PIC setup */
#define CLOCK_SPEED 20000000 // In Hz
#fuses NOWDT // No Watchdog Timer
#fuses HS // High speed oscillator (> 4mhz)
#fuses PUT // Enable Power Up Timer
#fuses NOPROTECT // Code not protected from reading
#fuses NOWRT // Program memory not write protected
#fuses NOBROWNOUT // Don't reset when a brownout is detected
#fuses NOLVP // No Low Voltage Programming on B3(PIC16) or B5(PIC18)
#fuses NOCPD // No EE protection
#fuses NODEBUG // No Debug mode for ICD
#use delay(CLOCK=CLOCK_SPEED)
#zero_ram |
The main reason I don't think I'm missing characters is that after I do my first fgetc(SERVER), then I start getting 100% of the characters on the software UART. It's like calling fgetc() somehow "fixes" things.
If I had a problem with the serial bitrate and polling, I would expect to have problems recieving characters regardless of whether fgetc() has been called before or not, right? |
|
|
|