Jump to content
Sign in to follow this  
felipe

Interrupts ! They Are Driving Me Nuts !

Recommended Posts

Hi everybody, I´m trying to make an IR remote control with PIC 16F628A. Escencially the software configures the ports and then waits for an interrupt from keyboard, once in interrupt service rutine, it scans the keyboard, transmits proper key code, and sets timer 1for interrupt to transmit repetition code, or not(it depend on a new keyboard scan and whether the detected key is the same as allready pressed or not). I simulated it and it seems to work O.K, but in real life is a mess. So here´s some of the code and I would highly welcome any input, thanks.

 

//********************************************************************************

***

#include <system.h>

#pragma CLOCK_FREQ 4000000

#pragma DATA _CONFIG, _HS_OSC & _WDT_OFF & _CP_OFF & _LVP_OFF & _MCLRE_OFF & _PWRTE_ON & _BODEN_OFF

unsigned char adressHigh = 0b00010000;

unsigned char adressLow = 0b00010000;

unsigned short keyValue = 0;

unsigned char k1, k2, k3, k4, k5, k6,k7, k8, k9,k10, k11, k12, k13, k14, k15,

k16, k17, k18, k19, k20, k21, k22, k23, k24;

void TimerOn(void);

bool keyPressed = false;

unsigned char selectedKey = 0;

void CargarTeclas(void);

void ConfigPorts(void);

void ConfigTimer1(void);

void TransmitStartCode(void);

void TransmitRepCode(void);

void TransmitKeyCode(unsigned char);

void TransmitPulso(void);

void TransmitBit0(void);

void TransmitBit1(void);

unsigned char ScanKeys(void);

void interrupt(void)

{

clear_bit(intcon, GIE);

clear_bit(intcon, RBIE);

clear_bit(intcon, PEIE);

clear_bit(pie1, TMR1IE);

clear_bit(intcon, RBIF);

clear_bit(pir1, TMR1IF);

clear_bit(t1con, TMR1ON);

delay_ms(15);

unsigned char key = ScanKeys();

if (key != 0)

{

if ((key == selectedKey) && (keyPressed == true))

{

 

clear_bit(pir1, TMR1IF);//Borro la bandera TMR1 interrupt

set_bit(intcon, PEIE);//Habilita interrupcion de perifericos

set_bit(pie1, TMR1IE);//Habilita inrrupcion por timer1 overflow

clear_bit(intcon, RBIE);

clear_bit(intcon, RBIF);

ConfigTimer1();

TimerOn();

TransmitRepCode();

set_bit(intcon, GIE);

 

 

}

else

{

keyPressed = true;

selectedKey = key;

clear_bit(pir1, TMR1IF);//Borro la bandera TMR1 interrupt

set_bit(intcon, PEIE);//Habilita interrupcion de perifericos

set_bit(pie1, TMR1IE);//Habilita inrrupcion por timer1 overflow

clear_bit(intcon, RBIE);

clear_bit(intcon, RBIF);

ConfigTimer1();

TimerOn();

TransmitKeyCode(key);

set_bit(intcon, GIE);

 

 

}

}

else

{

keyPressed = false;

selectedKey = 0;

clear_bit(t1con, TMR1ON);

clear_bit(pir1, TMR1IF);

clear_bit(pie1, TMR1IE);

clear_bit(intcon, PEIE);

clear_bit(intcon, RBIF);

set_bit(intcon, RBIE);

set_bit(intcon, GIE);

}

}

 

 

void main()

{

 

CargarTeclas();

ConfigPorts();

clear_bit(intcon, RBIF);

set_bit(intcon, RBIE);

set_bit(intcon, GIE);

nop();

sleep();

 

 

}

void ConfigPorts(void)

{

 

cmcon = 0x07;

trisa = 0;

porta = 0;

trisb = 0;

portb = 0b01110000;

trisb = 0b01110000;

clear_bit(option_reg, 7);//activo las resistencias pull up en portb

}

 

void ConfigTimer1(void)

{

t1con = 0b00110100;

 

}

void TimerOn(void)

{

tmr1l = 0x00;

tmr1h = 0x00;

tmr1l = 0x43;

tmr1h = 0xCB;

set_bit(t1con, TMR1ON);

}

Share this post


Link to post
Share on other sites

you want to be in and out of your ISR as fast as you can. So once in the ISR, clear the interrupt and set

a flag that your main can respond to. Looks like you are doing way to much in your interrupt routine.

Share this post


Link to post
Share on other sites
you want to be in and out of your ISR as fast as you can. So once in the ISR, clear the interrupt and set

a flag that your main can respond to. Looks like you are doing way to much in your interrupt routine.

 

There is nothing wrong with doing all your work in an interrupt routine and have your main routine simply sleep waiting for an interrupt if there is no 'background' processing to do.

 

For this particular code:

 

sleep() should be in an infinite loop.

 

You shouldn't touch GIE in the interrupt routine as it's reset by the interrupt logic and set by the return from the interrupt routine.

 

Orin.

Share this post


Link to post
Share on other sites

You don't need to touch the enable bits either. It appears that you're getting into the interrupt on either RBIF or TMR1IF. Just make sure you clear the flag bit before you leave the interrupt routine which you already are. In main() you're turning off the tmr1 interrupt before you sleep but this is not the case in the interrupt routine. Was this intentional?

Share this post


Link to post
Share on other sites

Hi everybody, first I want to thank to those who gave me some awnsers , mchristisen and orin.

The reason I tooked so long to post again is because I´ve been doing every thing possible to make that code work, with no results :( I tried not touching GIE, tried sleep() in infinite loop, tried not eneableing general interrupts (wich should make it fetch the next instruction instead of going to interrupt() function), but no luck. just to see if it was a problem in other part I tried changeing the

main() routine to this:

 

void main()

{

 

CargarTeclas();// Loads individual keys values

ConfigPorts();

ConfigTimer1();

while(1)

{

key = ScanKeys();

if (key != 0)

{

TransmitKeyCode(key);

key = 0;

}

}

}

 

Now this main() routine as you can see, is constantly scaning the keymatrix, if one key is pressed transmits that key code, and then returns to scanning. No interrupts, no sleep(), no preoblem :huh:

But that is no real solution, as this is supossed to work on two AAA batteries, and the processor HAS to go to sleep mode. So, this is what i need to do :

 

1)Do all the configuration(ports, timer, interrupts)

2)Configure it to go to interrupt on change of RB7:RB4, or Timer1 overflow.

3) Go to sleep mode

4) Once I have an interrupt, stop timer , scankeybord and throw key code.

5) Go back to sleep

 

Does anybody knows how to do this right ?

Share this post


Link to post
Share on other sites
You don't need to touch the enable bits either. It appears that you're getting into the interrupt on either RBIF or TMR1IF. Just make sure you clear the flag bit before you leave the interrupt routine which you already are. In main() you're turning off the tmr1 interrupt before you sleep but this is not the case in the interrupt routine. Was this intentional?

 

The idea is: once I have an interrupt, I scan the keyboard to detect wich key was pressed, wich can have three results :

A _ No key was pressed(i.e. after debounce no key is detected as valid)

B _ New key was pressed.

C _ The same key was pressed.

 

In case A, I stop the timer, clear al flags, and set for interrupt on RB7:RB4 change, go back to sleep.

 

I case B, I prepare the timer, transmit key code, interrupt on RB7:RB4 change is NOT set, and got back to sleep. Now since RB change interrupt is not set, the only thing that can wake the processor from sleep is Timer1 overflow change, wich will occur(hopefully) 108 ms after the start of the key code transmition. Once this overflow occurs it produces an interrupt in wich the keyboard will be scaned to see if a "key still pressed" code needs to be transmited, wich is done by function TransmitRepCode().

 

And in case C, I set timer1 , transmit repetition code(done by TransmitRepCode()) and go back to sleep. Waiting for Timer1 to overflow, to check again if the key still pressed and I nedd to transmit another repetition code, or stop the transmition and go back to sleep.

 

I hope this helps to understand what I need to do, so you or anybody can help me. Because really, I think I tryied everything. And the problems is with the interrupt() function, and retruring from it, and the sleep() function. Is the combinationg of needing to use interrupts and the sleep() function.

Also I think the interrupt() and sleep() functions are pourly documented in the BoostC manual.

And I haven´t seen examples that use them combined, if you or anybody knows one please let me know. I thank you for your advice and welcome yours and everybody`s ideas on how to solve this problem, wich is a very frustrating one.

Edited by felipe

Share this post


Link to post
Share on other sites

I think I solve it !! :( . As I was getting more and more frustrated, I decided to go for a simpler infrared code format wich doesn´t have separate repeat code, is an old bi-phase, six bit code format from siemens. Wich is simpler, so I could concentrate on getting the wake up on interrupt thing going.

Well as it turns out, once you come back from interrupt, the ports doesn´t retain the original configuration( analog comparators get turn on when they where off before interrupt). So now, I turn comparators off again, and load values in porta and portb, before coming back from interrupt.

Also I put a "while" loop in the interrupt service routine, to compare the value that portb should have with no key pressed, so if the key still pressed I scan the keyboard again BEFORE leaving the interrupt service routine. As soon as I have the time I think I´ll upload a post on dealing with wake up from sleep on interrupts, because I think it might benefit beginners like me.

Share this post


Link to post
Share on other sites
you want to be in and out of your ISR as fast as you can. So once in the ISR, clear the interrupt and set

a flag that your main can respond to. Looks like you are doing way to much in your interrupt routine.

 

There is nothing wrong with doing all your work in an interrupt routine and have your main routine simply sleep waiting for an interrupt if there is no 'background' processing to do.

 

For this particular code:

 

sleep() should be in an infinite loop.

 

You shouldn't touch GIE in the interrupt routine as it's reset by the interrupt logic and set by the return from the interrupt routine.

 

Orin.

 

 

On further thought, there is a major problem with the original code: sleep() will disable the clock to the timer, so the timer interrupt will never occur while asleep. So, you should not sleep if the timer is enabled. Note that you can't simply do: if ( !t1con.TMR1ON ) sleep(); as there is a possibility of taking an interrupt between the test and the sleep. The following is probably safe for the main loop:

 

for (;; )
{
intcon.GIE = 0; /* Disable interrupts */
if ( !t1con.TMR1ON )
{
	sleep(); /* An interrupt will wake the CPU up even with GIE clear */
}
intcon.GIE = 1; /* Enable interrupts.  If we slept and an interrupt is pending, we will take the interrupt now. */
}

 

Orin.

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...