Hi, can someone help point me in the right direction. I have a simple program that takes push button inputs and then sends them by IR led link to a receiver. It uses the printf function to drive the LED and the KBI on P0 for the p/button inputs.
The program compiles ok and the printf works if I do a printf("rtrt") just before entering the main loop where the program is supposed to sit until a KBI is detected. Problem is I cannot trigger the interrupt. Additionally, I get an L16 'unused code, ignored for . . . etc' message for the interrupt service routine. So, I think that the compiler or linker is not seeing the isr. I don't think its a hardware issue. I've checked wiring and voltage level changes on the P0 pins.
Any thoughts on this?
thanks
Jason
I had a bored moment, so I attempted to tidy up the mess.
After seeing the use of printf in the ISR and the missing breaks, I've decided to not look any further at the OPs specific question.
/*------------------------------------------------------------------------------ IR Link Jason van Ryan ------------------------------------------------------------------------------*/ extern void keypad_isr(void); /* prototype declaration for keypad iser */ #include <REG922.H> /* special function register declarations */ /* for the Philips P89C922 device */ #include <STDIO.H> /* prototype declarations for I/O functions */ /*----------------------------------------------------------------------------*/ /* push button function defines */ #define power 1 #define mute 2 #define vup 4 #define vdn 8 #define sel 16 #define bat 32 #define test 64 /* put the controller into continuous Tx mode which /* cycles through all the commands and measures battery */ #define bat_test 48 /* continuosly Tx the battery voltage */ /*----------------------------------------------------------------------------*/ void keypad_isr(void) interrupt 7 using 0 { unsigned int command; EKBI=0; /* disable the keypad int */ command = (P0 & 32); /* save the button status and the power status */ //pstat = (P0 & ~32); /* still to add pstat routines here */ TR0 = 1; /* start timer 0. the 38KHz carrier is now running */ switch (command) /* one of the input buttons was pushed - execute the command */ { case power: printf ("pwr\n"); case mute: printf ("mute\n"); case vup: printf ("vup\n"); case vdn: printf ("vdn\n"); case sel: printf ("sel\n"); case test: printf ("test\n"); } TR0=0; /* done, turn the carrier off */ TxD = 0; /* make sure the TxD pin is LOW after transmission */ KBCON &= 0xFE; /* clear interrupt flag */ EKBI=1; /* enable the keypad interrupt again. Done and exit to main */ } /*--------------------------------------------------------------------------------*/ void main() { /* first, set up the keypad */ EA=0; /* configure the ports - P0 is all inputs, TxD PP, P1.2 open drain */ P0M1 |= 0xFF; P0M2 &= 0x00; P1M1 &= 0xFE; P1M1 |= 0x24; P1M2 &= 0xDF; P1M2 |= 0x05; P3M1 |= 0x03; P3M2 &= 0xFC; /* set up the keypad interrupt pattern */ KBPATN = 0x3F; // define pattern KBMASK = 0x3F; // define P0 pins that trigger interrupt - port line 0 to 5 KBCON = 0x00; // pattern must not match KBCON = 0x02; // pattern must match - this line for test purposes // set isr priority to 1 IP1 &= 0xFD; IP1H &= 0xFD; IP1 |= 0x02; /* set up the carrier freq - use T0 and toggling P1.2 */ TMOD &= 0xF0; /* T0 is enabled with TR0 = 1; T1 is enables when INT1 is low */ TMOD |= 0x02; /* T0 is set to 8 bit auto reload timer - cycle time is 26.3uS = 37.4KHz */ TAMOD &= 0xFE; TH0 = 0xCF; /* this is the reload value for TL0. Note, the counter increments every 2 clock cycles */ TL0 = 0xCF; P1M1 |= 0x04; /* T0 pin is an open-drain output */ P1M2 |= 0x04; AUXR1 |= 0x10; /* enable toggle output - but do not run the timer until an interrupt */ /* enable keypad interrupt */ /* set up the UART */ SCON = 0x52; /* initialize UART */ BRGR0 = 0x01; /* 300 baud */ BRGR1 = 0x60; BRGCON = 0x03; KBCON &= 0xFE; /* clear KBI interrupt flag */ EKBI = 1; EA=1; T0=1; /* WERYRTY just to test if we are running */ printf ("test 123456789 123456789 \n"); TxD = 0; /* make sure TxD port line is LOW */ T0=0; /* turn carroer off */ while (1) {}; /* wait here for keypad interrupt */ }
Why would you do that?
I'm not an expert Andy, that's why I've posted the problem up on the site
Why can't I use printf in an isr?
Do not print in the ISR - just do the absolute minimum you can get away with there.
You define a constant here:
#define bat 32
How many bits are you extracting here?
command = (P0 & 32); /* save the button status and
Remember how many bits you extracted before - how many case statements are possible?
switch (command) /* one of the input buttons was pushed - execute the command */ { case power: printf ("pwr\n"); case mute: printf ("mute\n"); case vup: printf ("vup\n"); case vdn: printf ("vdn\n"); case sel: printf ("sel\n"); case test: printf ("test\n"); }
And what do you think happen after printf("pwr\n") - if we ignore the fact that you should not use printf() in the ISR unless someone have proven to you that printf() is reentrant and that your processor is fast enough to take a detour through one of the largest functions in the RTL.
there is nothing in the programming model that prevents you from using 'printf' in an ISR. but it is a "heavy" call in terms of time it takes to complete (bad idea for an ISR) and the code size it consumes (bad idea for a processor with limited on board flash size). it is possible and might be suitable for your needs - but it is not very likely.
Remember Tamir that this is a C51 thread. Consider what will happen if printf() is called from both the ISR and main() at the same time. And since printf() will call a lot of helper functions, a call to printf() may clash with a number of other RTL functions too.
yes of course I should have mentioned that, too.
I've just got the p/button switches connected straight to the input lines - so p/button 1 goes to P0.0, p/button 2 goes to P0.1 etc up to the 6th p/button on P0.5. There's no scanning or mux'ing.
This application is not time critical - there's plenty of time but the point on keeping th e isr's RTL lite is taken.
Specifically though, why is the MCU not jumping to the isr on KBI?
Note the title of my post - I didn't mention printf!
I queried the point of having a prototype for an ISR.
It is pointless - but is it harmless...?
I do not like to see a function prototype that is not matching the ISR - the prototype indicates that it is a normal function, not an ISR.
If the compiler decides to trust the function prototype, and treats void keypad_isr(void) as a normal function, then it can be thrown away since there will be no caller.
By the way - did you consider the bit values you have defined and what your command variable will be set to?
Suggestion JUST copy the port data into a externally (IE globally) accessible variable. Then set a flag (IE bit) globally to indicate an interrupt happened.
bit key_press; unsigned char key_state;
Or something akin to that. Then play with the data elsewhere in your code.
The event and data from P0 should be handled elsewhere. The flag will indicate if you have an interrupt or not however. That should at least indicate if the interrupt happened.
Stephen
E.G. ince the KBD int can kick the uC out of sleep it is a LEVEL interrupt. Thus you must set the match to whatever the keys read when entring the ISR or you get an eternal interrupt.
You need to read the KBD ISR description in the datasheet 47 times or so to get it.
Erik
good point - because one of the things I want to do is put the micro into power down mode
I think I've picked up enough tips here to re-structure the program.
Thanks
A question relating to PCON. If I put the micro into total power down, I assume this is the same as executing some type of 'halt' instruction - so the stack pointer, instruction fetch etc are all preserved. I then generate a KBI by pushing a button, and the micro exectutes the isr and after reti goes to the next statement after the total power down mode statement. The User Manual is not clear on what happens after wake-up (unless I missed it).
Long time since I worked with the 8051, but yes - the common way for processors to implement power save is that they sleep until a wakeup event. Then they perform the interrupt and then continue with the next instruction.
To continue to sleep, you would have to do something like:
for (;;) { sleep(); // sleep until interrupt or reset if (should_wake_up_permanently) break; if (have_a_command) { handle_command(); } }
Note that depending on processor architecture and debugging interface, JTAG dongles etc may not support sleep mode, so in some situations, you may have to conditionally remove the sleep command in debug builds, unless you just debug in the simulator.
Note that this has nothing to do with Keil - it is purely a hardware feature of the specific chip.
"If I put the micro into total power down, I assume this is the same as executing some type of 'halt' instruction"
Never assume - always check in the Datasheet.
The datasheet will document precisely what is and is not preserved in the various "low power" modes available on the specific chip.
IIRC, "total power down" does exactly as the name suggests; so nothing is preserved - getting out of "total power down" is equivalent to a Reset. If you want anything preserved, you will have to use one of the other "low power" modes - the less-than-total powerdown ones! Again, check this in the Datasheet.
I think there's an NXP app note that illustrates this?