Timer based interrupt in PIC microcontroller using mikroC for PIC - c

I am facing a problem while implementing a timer based interrupt in mikroC for PIC.
I want to toggle a port pin for 8 times if there is a keypress at PORTC.F0 and there should be a delay of say 100ms between the toggles.
Normally this would be very easy using a delay function
for (i=0;i<=8;i++)
{
PORTB.F0=~PORTB.F0;
Delay_ms(100);
}
But during the period, any other keypresses are missed by the system. So I thought of implementing the solution using interrupts.
#define SW PORTC.F0
char ttime,i;
volatile flag;
void Inittimer()
{
T1CON = 0x01;
TMR1IF_bit = 0;
TMR1H = 0x06;
TMR1L = 0x00;
TMR1IE_bit = 1;
INTCON = 0xC0;
}
void Interrupt()
{
if (TMR1IF_bit)
{
TMR1IF_bit = 0;
TMR1H = 0x06;
TMR1L = 0x00;
ttime--;
if (ttime==0)
{
flag=1;
}
}
}
void main()
{
Inittimer1();
TRISB = 0;
TRISC.F0 = 1;
PORTB = 0x00;
while(1)
{
if (SW==0)
{
ttime=3;
}
if (flag==1)
{
for (i=0;i<=8;i++)
{
PORTB=~PORTB;
flag=0;
}
}
}
}
Nothing is working. Can somebody please help me to correct the code?

Well this doesn't look right:
if (flag==1)
{
for (i=0;i<=8;i++)
{
PORTB=~PORTB;
flag=0;
}
}
When you first see that flag is set, you immediately loop and toggle the output 8 times, without waiting for flag to turn back to 1. That's not right, it's overly simplified.
You need to look for the flag, then toggle the output and clear the flag, and wait for it to to get set again, maintaining the counter in parallel. The for loop is not the proper structure for this, since it will "lock out" the rest of the program and might cause keypresses to be missed.

When you initialize your timer:
void Inittimer()
{
T1CON = 0x01;
TMR1IF_bit = 0;
TMR1H = 0x06; // No prescaler? I doubt your clock speed is 40-some KHz!
TMR1L = 0x00;
TMR1IE_bit = 1;
INTCON = 0xC0;
}
Why don't you control the LED directly from the ISR ?
if (ttime)
PORTB.F0 = (--ttime & 1); // ttime is not decremented when led is not blinking.
else
PORTB.F0 = 0; // ensures the LED is off.
To start blinking 8 times:
if (SW==0)
{
PORTB.F0 = 1;
ttime = 16;
}
Note that with a 100ms clock interrupt, the first 'blink' of the LED may last up to 200ms... This is why many like to work with a faster timer interrupt (this has usually other uses as well), controlling the led would require adding a soft post-scaler
if (blinking)
{
if (--blinkTimer == 0)
{
blinkTimer = BLINK_DELAY; // whatever number it takes for 100ms.
PORTB.F0 = (--blinking & 1);
}
}
else
{
PORTB.F0 = 0
}
To start blinking:
if (SW==0)
{
blinking = (2 * BLINKS) - 1;
blinkTimer = BLINK_DELAY;
PORTB.F0 = 1;
}
This should get you a more even first blink.

Related

TIMER Logic used in PIC15F1516 microcontroller

I am using PIC15F1516 microcontroller, and doing reverse engineering to understand the code without any documentation. I failed to understand use of timer and logic implemented in main.c
Timer0RegValue = 64536.0
Prescalar = 1
Delay = ( (65536-Timer0RegValue)*(Prescalar*4) ) / Fosc = 0.001 Sec
I can understand in the main function timer is updated with value of 1mSec, however, I could not get after how much time ProcessTenthSec() and ProcessTwelfthSec() will get executed?
void TMR1_Initialize(void)
{
//Set the Timer to the options selected in the GUI
//T1CKPS 1:1; T1OSCEN disabled; nT1SYNC synchronize; TMR1CS FOSC/4; TMR1ON disabled;
T1CON = 0x00;
//T1GSS T1G; TMR1GE disabled; T1GTM disabled; T1GPOL low; T1GGO_nDONE done; T1GSPM disabled;
T1GCON = 0x00;
//TMR1H 252;
TMR1H = 0xFC;
//TMR1L 24;
TMR1L = 0x18;
// Load the TMR value to reload variable
// 6396= 0xFC18
// Delay = ( (65536-Timer0RegValue)*(Prescalar*4) ) / Fosc;
// Delay = ( (65536-6396)*(1*4) ) / 500000;
// Delay =
timer1ReloadVal=(TMR1H << 8) | TMR1L;
// Clearing IF flag before enabling the interrupt.
PIR1bits.TMR1IF = 0;
// Enabling TMR1 interrupt.
//PIE1bits.TMR1IE = 1;
// Set Default Interrupt Handler
//TMR1_SetInterruptHandler(TMR1_DefaultInterruptHandler);
// Start TMR1
//TMR1_StartTimer();
}
#define TENTH_SEC_RELOAD 156
#define TWELFTH_SEC_RELOAD 130
void main(void)
{
// initialize the device
SYSTEM_Initialize();
// When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits
// Use the following macros to:
// Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable();
// Enable the Peripheral Interrupts
INTERRUPT_PeripheralInterruptEnable();
byTenthSec = TENTH_SEC_RELOAD
byTwelfthSec = TWELFTH_SEC_RELOAD
while (1)
{
if(PIR1bits.TMR1IF)
{
T1CONbits.TMR1ON = 0;
//TMR1H 252;
TMR1H = 0xFC;
//TMR1L 24;
TMR1L = 0x18;
T1CONbits.TMR1ON = 1;
PIR1bits.TMR1IF = 0;
if(--byTenthSec == 0)
{
byTenthSec = TENTH_SEC_RELOAD;
ProcessTenthSec();
}
if(--byTwelfthSec == 0)
{
if(byTwelfthFix--)
{
byTwelfthSec = TWELFTH_SEC_RELOAD ;
}
else
{
byTwelfthFix = 3;
byTwelfthSec = TWELFTH_SEC_RELOAD + 1;
}
ProcessTwelfthSec();
}
}
//if an ADC conversion is in progress do a low priority task
if(uHiPriTasks.HighPriority)
{
if(uHiPriTasks.One)
Function1();
else if(uHiPriTasks.Two)
Function2();
else if(uHiPriTasks.Three)
Function3();
else if(uHiPriTasks.four)
Function4();
else if(uHiPriTasks.five)
Function4();
}
if(uWatchdog.byWatchdog == WDT_ALL_TASKS_COMPLETE)
{
//reset the watchdog task structure
uWatchdog.byWatchdog = 0;
//restart the watchdog timer
CLRWDT();
}
}
}

How can I add a delay of 90 minutes when a port has gone from 0 to 1?

I have a PIC12F1822 of which there is an LED and relay as an output and a trigger as an input.
When the trigger is equal to one, I would like the to have the LED and relay stay turned on for 90 minutes.
The problem is that I cannot use a delay since __delayms has a limit.
How can this be done?
#include <xc.h>
#define _XTAL_FREQ 4000000
#define LED PORTAbits.RA5
#define RELAY PORTAbits.RA4
#define TRIGGER PORTAbits.RA2
int main()
{
// OSCCON = 0b01101000 ;
OSCCONbits.IRCF = 0b1101 ;
TRISAbits.TRISA5 = 0; //LED Output PIN
TRISAbits.TRISA4 = 0; //Output for Relay
TRISAbits.TRISA2 = 1; //INPUT trigger from comparator
while(1)
{
if (TRIGGER == 1)
{
LED = 1; // LED ON
__delay_ms(1000); // 1 Second Delay
LED = 0; // LED OFF
__delay_ms(1000); // 1 Second Delay
}
else
{
LED = 0; // LED OFF
}
}
return 0;
}
why not wrap the 1s delay in a function providing a delay_s(uint32_t seconds) and in a function providing a delay_m(uint32_t minutes)
void delay_s(uint32_t seconds)
{
while(seconds){
__delay_ms(1000);
seconds--;
}
}
void delay_m(uint32_t minutes)
{
while(minutes){
delay_s(60);
minutes--;
}
}
But:
You should be aware that this totally blocks your µC's program.
It will not react on any key press. This may be ok but may be you want the delay to be reset to 90min if the key is pressed during the delay.
I would suggest to implement the whole thing with interrupts and timers.
Set up a timer that triggers once every second and countdown a global variable (volatile!) within the isr that was set by your main loop. If the counter reaches 0 your isr may disable your output.
This way your µC can process other things in the mainloop and is not blocked by a __delay-function.
Simple. State machines and timer interrupts and a minute countdown. Something like this:
Disclaimer - coded blindly
#define MINUTES_90 5400UL /* 90 seconds */
typedef enum
{
/** Code operational */
p_state_idle,
/** Code waiting for 90 minutes */
p_state_waiting
}p_state_t;
static unsigned long gSecondsRemaining = MINUTES_90;
int main()
{
p_state_t gState = p_state_running;
OPTION_REGbits.PSA = 0; /* Prescaler assigned */
OPTION_REGbits.PS = 0b111; /* 256 prescaler */
OPTION_REGbits.TMR0CS = 0; /* Fosc / 4 */
INTCONbits.TMR0IE = 1; /* Timer 0 interrupt enabled */
INTCONbits.PEIE = 1; /* Peripheral interrupts enabled */
INTCONbits.GIE = 1; /* Global interrupts enabled */
/** Default LED off */
LED = 0;
while (1)
{
switch (gState)
{
case p_state_idle:
if (TRIGGER == 1)
{
LED = 1; // LED ON
gSecondsRemaining = MINUTES_90; /* Reset timer countdown */
gState = p_state_waiting;
}
break;
case p_state_waiting:
/** can sleep here */
if (gSecondsRemaining == 0)
{
gState = p_state_idle;
LED = 0;
}
break;
}
}
}
void interrupt ISR()
{
static unsigned char gSecond = 15;
/** approx 15 Hz ? */
if (INTCONbits.TMR0IF)
{
INTCONbits.TMR0IF = 0;
if (gSecond > 0)
gSecond--;
if (gSecond == 0)
{
if (gSecondsRemaining > 0)
gSecondsRemaining--;
gSecond = 15;
}
}
}
You could solve this with an inline assembly function with some loops. Have a look here: example: 30 min delay

How to end external Interrupt Service Routine (ISR) in 8051

I wrote a simple led blinking code with hardware interrupt 0 of 8051.
When button is pressed it goes into interrupt service routine (ISR). After executing it should come back in main function but it is not coming.
This is my c code. Any positive reply will be appreciated.
sbit LED = P1^0;
void delay(int ms)
{
int i;
for(i=0;i<ms;i++)
{
TMOD = 0x01;
TH0 = 0xFC;
TL0 = 0x66;
TR0 = 1;
while(TF0==0);
TR0 = 0;
TF0 = 0;
}
}
void main(void)
{
P1 = 0x00;
/// P3 = 0x04;
IT0 = 1;
EX0 = 1;
EA = 1;
LED=1;
while(1)
{
LED=~LED;
delay(200);
}
return ;
}
void external0_isr() interrupt 0
{
EA=0;
LED =0 ;
delay(2000);
LED=1;
EX0=1;
EA=1;
return;
}
When you enter the button interrupt, you disable global interrupt EA=0;
That also disables the timer interrupt. Therefore, you program would hang at while(TF0==0) in your delay(2000) routine.

Can only read once from PIC16's RCO port

Been searching for an answer for this for a few days now. I'm trying to create this simple verification program where if I push a button it drives the voltage on RC0 to 5 volts, and when the button is unpushed, a pulldown resistor pulls the pin down to 0. The problem is, the code will only work the first time I push the button. After that it will not enter the if (RC0 == 1) loop. Here is my code snippet. What am I missing here?
void main(void){
/* Configure the oscillator for the device */
ConfigureOscillator();
/* Initialize I/O and Peripherals for application */
InitApp();
TRISB = 0x0F; // turn on RB7 for use as output
TRISC = 0xFF; // turn on RC0 for use as input
ANSEL = 0b11101111; // use RC0 as digital input
unsigned char time = 0;
while (1) {
//delay for 10 seconds
if (PORTCbits.RC0 == 1) {
while (time < 10) {
/* TODO */
RB7 = 1;
__delay_ms(100);
RB7 = 0;
__delay_ms(100);
time++;
}
}
if (PORTCbits.RC0 == 0){ //This if statement will always execute when
//RC0 == 0, but will not execute when I push
//the button. This really confuses me.
//Can RCO be given a value different than 1?
__delay_ms(2000);
}
RB7 = 0;
__delay_ms(500);
RB7 = 1;
__delay_ms(500);
}
}

Interrupt handling with 8051 C using Keil uVision

I've been doing a project about home automation in which I have to use timer interrupts with 8051 microcontroller. I've constructed the following code, however I couldn't manage to get interrupt working. It seems that the program does not go into timer ISR at all. I use a buton to simulate PIR input, therefore lampControl is triggered, no worries there. I use as a library.
Any ideas or help will be greately appreciated:
void timer0_isr(void) interrupt 1 //Timer 0 Interrupt
{
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
if (++lamp_interrupt_count == 6000)
{
sendCharacterShowAsHex(0x8F);
lamp_interrupt_count = 0;
TR0 = 0;
}
}
void main()
{
unsigned char chr;
IE = 0x93;
while(1)
{
serialInput();
if (getPIRInput() == 0x00)
{
lampControl(0x80);
}
....
....
....
}
void lampControl(unsigned char serial_data_in)
{
if (serial_data_in == 0x80)
{
sendCharacterShowAsHex(0x80);
//enable interrupts
IE = 0x93;
device_interrupt = 2; //Lamp
TMOD = 0x21; // Timer0 Gate=0, Mode 1, 16bit timer
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
}
else if(serial_data_in == 0x8F)
{
sendCharacterShowAsHex(0x8F);
}
}
You need to configure the timer and interrupts before you can use them.
In main() you need at least the following configuration bits set in order to be able to turn
the timer on with "TR0 = 1;" :
Set those bits first thing in main() and this should do the trick:
TMOD = 0x01; // 16-bit no auto reload
TH0 = 0xDC; //Set high and low bits to count 0xFFFF - 0xDC00 = 0x23FF counts
TL0 = 0x00;
ET0 = 1; // Enable timer0 interrupt
EA = 1; // Enable all interrupts
//TR0 = 1; //Enable Timer0 immediately
The rest of your code should run fine.
Note: you could change your interrupt function definition to:
"void timer0_isr(void) interrupt 1 using 1" to force it to use register bank 1 for the interrupt function operation.

Resources