Interrupt handling with 8051 C using Keil uVision - c

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.

Related

System Hangs by writing delay after Timer_init()

I am facing a strange issue while programming PIC18f45k22. I am initiating system modules and then I write a blocking delay for 3 seconds to wait for a touch screen to start (because I need to send some information to this screen). But when I do this I discovered that when I initiate timer before writing the delay, I discover that the system freezes.
This is the code that I used
NOTE: all the functions work fine without any problem.
void main(void) {
Sys_Init();
__delay_ms(3000);
while(1){
//Code
}
System initiation code
void Sys_Init(void) {
Delay_XTAL();
OSCILLATOR_Init();
DIGITAL_PIN_STATE();
ANALOG_PIN_STATE();
ADC_INIT(); //change clock select pins when oscillator changes
Spi_Init();
DHT22_init();
Uart_Init(9600); //initiate UART (change baud values when oscillator value changed)
Timer1_Init(); //initiate timer one (change prescaler and counter value if oscillator value changes)
}
Timer code
void Timer1_Init(void){ //for interrupt program use this function
//Prescaler 1:1; TMR1 Preload = 61536; Actual Interrupt Time : 1 ms
T1CON = 0x01;
TMR1IF = 0;
TMR1H = 0xF0;
TMR1L = 0x60;
TMR1IE = 1;
INTCON = 0xC0;
}
Interrupt code
void __interrupt() ISR (void){
Timer1();
}
void Timer1(void){ //for interrupt program
if (TMR1IF){
cnt1++;
TMR1H = 0xF0; //overflow every 1 ms
TMR1L = 0x60;
TMR1IF = 0;
}
}

Using TC0 on the SAML21G18B with Arduino Mattairtech distribution

I'm trying to use the SAML21G18B MCU and the Mattairtech Arduino distribution, I need to configure TC0 to generate interrupts. Unfortunatly none of the existing SAMD timer libraries work with this MCU/distribution so I've been trying to configure and use TC0 from scratch ...
Mattairtech core on Github
I've taken the analogWrite() code from the Mattairtech core and hacked it into what I hope is something that configures TC0 for normal PWM - the same as when its used for analogWrite().
It compiles (YAY!) but I attempted to read from the count register to see if it is counting, and I get nothing. I also have no idea how to configure it to generate an interrupt on a match with the CC0 register.
All my code is below, any help, pointers, tips to get the interrupts working would be appreciated.
void TC0_Handler() {
//interrupt handler ...
}
static void SYNC_TC(Tc* TCx){
while(TCx->COUNT16.SYNCBUSY.reg & (TC_SYNCBUSY_SWRST | TC_SYNCBUSY_ENABLE | TC_SYNCBUSY_CTRLB | TC_SYNCBUSY_STATUS | TC_SYNCBUSY_COUNT));
}
//initialise TCO
void initTc0(bool sixteenBit, uint16_t value){
uint8_t timerCh = 0;
Tc* TCx = (Tc*)TC0;
//GCLK setup
GCLK->PCHCTRL[GCM_TC0_TC1].reg = (GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0);
while ( (GCLK->PCHCTRL[GCM_TC0_TC1].reg & GCLK_PCHCTRL_CHEN) == 0 ); // wait for sync
//Disable TC
TCx->COUNT16.CTRLA.bit.ENABLE = 0;
SYNC_TC(TCx);
// Set Timer counter Mode to 16 bits, normal PWM
if(sixteenBit) TCx->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
else{
// Set Timer counter Mode to 8 bits, normal PWM
TCx->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8;
SYNC_TC(TCx);
// Set PER to maximum counter value
TCx->COUNT8.PER.reg = 0xFF;
}
SYNC_TC(TCx);
// Set TCx as normal PWM
TCx->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_NPWM;
SYNC_TC(TCx);
// Set the initial value for CC
if(sixteenBit) TCx->COUNT16.CC[timerCh].reg = (uint16_t) value;
else TCx->COUNT8.CC[timerCh].reg = (uint8_t) value;
SYNC_TC(TCx);
// Enable TCx
TCx->COUNT16.CTRLA.bit.ENABLE = 1;
SYNC_TC(TCx);
}
//Set the CCBuf register
void setTc(bool sixteenBit, uint16_t value){
uint8_t timerCh = 0;
Tc* TCx = (Tc*)TC0;
if(sixteenBit) TCx->COUNT16.CCBUF[timerCh].reg = (uint16_t)value;
else TCx->COUNT8.CCBUF[timerCh].reg = (uint8_t)value;
SYNC_TC(TCx);
}
uint16_t getCount(){
Tc* TCx = (Tc*)TC0;
TCx->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC;
return TCx->COUNT16.COUNT.reg;
}
void setup() {
// put your setup code here, to run once:
initTc0(1, 0);
setTc(1, 500);
Serial.begin(1000000);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(getCount());
delay(100);
}
Ok, I answered my own question by hacking the tone() function. It uses TC1 to generate periodic interrupts that toggle a pin on and off to generate the tone. The following code lets you configure the timer with a clock prescaler and a value for CC - the timer counts up and when it matches CC the interrupt is called and it starts over.
This works for TC0, and is easily edited for TC1
#include "variant.h"
#include "sam.h"
//***************Use this for TC0
#define TCTimer TC0
#define timerIRQ TC0_IRQn
//Timer handler function if using TC0 - put whatever ISR code you want in here.
void TC0_Handler (void){
//clear the interrupt flag
TCTimer->COUNT16.INTFLAG.bit.MC0 = 1;
isrCounts++;
}
//***************Use this for TC1
/*
#define TCTimer TC1
#define timerIRQ TC1_IRQn
//Timer handler function if using TC1 - put whatever ISR code you want in here.
void TC1_Handler (void){
//clear the interrupt flag
TCTimer->COUNT16.INTFLAG.bit.MC0 = 1;
isrCounts++;
}*/
//Wait for registers to synchronise
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.SYNCBUSY.reg);
#define TONE_TC_TOP 0xFFFF
#define TONE_TC_CHANNEL 0
//Reset the timer hardware
static inline void resetTC (Tc* TCx){
// Disable TCx
TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
WAIT_TC16_REGS_SYNC(TCx)
// Reset TCx
TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
WAIT_TC16_REGS_SYNC(TCx)
while (TCx->COUNT16.CTRLA.bit.SWRST);
}
//setup the timer with a prescaler clock value, and the CC value.
//The timer incriments until it hits CC, then fires an interrupt and starts over
//With prescaler at 1 and CC at 65535 it interrupts 733 times per second
//Reduce CC to get a faster interrupt rate
//Prescaler must only be 1,2,4,8,16,64,25 or 1024
void setupTCTimer(uint32_t prescaler, uint32_t ccVal ){
NVIC_DisableIRQ(timerIRQ);
NVIC_ClearPendingIRQ(timerIRQ);
NVIC_SetPriority(timerIRQ, 0);
// Enable GCLK for timer used
GCLK->PCHCTRL[GCM_TC0_TC1].reg = (GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0);
while ( (GCLK->PCHCTRL[GCM_TC0_TC1].reg & GCLK_PCHCTRL_CHEN) == 0 ); // wait for sync
uint32_t prescalerConfigBits;
uint32_t ccValue;
ccValue = ccVal;//toneMaxFrequency / frequency - 1;
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1;
switch(prescaler){
case 0: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
case 1: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
case 2: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2; break;
case 4: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4; break;
case 8: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8; break;
case 16: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16; break;
case 64: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64; break;
case 256: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256; break;
case 1024: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024; break;
default: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
}
resetTC(TCTimer);
uint16_t tmpReg = 0;
tmpReg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits
tmpReg |= prescalerConfigBits;
TCTimer->COUNT16.CTRLA.reg |= tmpReg;
WAIT_TC16_REGS_SYNC(TCTimer)
TCTimer->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
WAIT_TC16_REGS_SYNC(TCTimer)
TCTimer->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue;
WAIT_TC16_REGS_SYNC(TCTimer)
// Enable the TCTimer interrupt request
TCTimer->COUNT16.INTENSET.bit.MC0 = 1;
}
//Start the timer
//you need to call setupTCTimer with prescaler and CC values before you start the timer
void startTCTimer(){
// Enable TCTimer
TCTimer->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
WAIT_TC16_REGS_SYNC(TCTimer)
NVIC_EnableIRQ(timerIRQ);
timerActive = true;
}
//stop the timer
//This resets it so you need to call setupTCTimer with prescaler and CC values before restarting
void stopTCTimer(){
resetTC(TCTimer);
timerActive = false;
}

Timer based interrupt in PIC microcontroller using mikroC for PIC

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.

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.

Why isn't Timer2 Interrupt firing up?

I am trying to enable timer2 interrupt to use it for PWM purposes. In this case, I just turn on an LED and when timer 2 interrupt occurs I turn it off but the timer interrupt never occurs. Everything looks good to me so I don't understand why Timer2 is not firing up. I am using PIC18F87J11, here is the datasheet.
/*
File: main.c
Date: 2011-SEP-4
Target: PIC18F87J11
IDE: MPLAB 8.76
Compiler: C18 3.40
*/
#include <p18cxxx.h>
#include<usart.h>
#include <pwm.h>
#include <delays.h>
#pragma config FOSC = INTOSC, WDTEN = OFF, XINST = OFF
#pragma interrupt HighISR
void main(void) {
unsigned int i;
/* set FOSC clock to 8MHZ */
OSCCON = 0b01110000;
/* turn off 4x PLL */
OSCTUNE = 0x00;
/* make all ADC inputs digital I/O */
ANCON0 = 0xFF;
ANCON1 = 0xFF;
PR2 = 124; // Period
TMR2=0;
// 1/16 prescalar
T2CONbits.T2CKPS0 = 1;
T2CONbits.T2CKPS1 = 0;
PIE1bits.TMR2IE == 1; // Enables the TMR2 to PR2 match interrupt
// Enable Timer 2
T2CONbits.TMR2ON = 1;
INTCONbits.PEIE = 1; // Enable Perpherial Interrupt
INTCONbits.GIE = 1; // Enable Global Interrupt
TRISDbits.TRISD6 = 0; // Turn on LED
LATDbits.LATD6 = 1;
while (1);
}
#pragma code highVector=0x08
void HighVector(void) {
_asm goto HighISR _endasm
}
#pragma code /* return to default code section */
// Timer Interrupt
void HighISR(void) {
if (PIR1bits.TMR2IF == 1) {
LATDbits.LATD6 = 0; // Turn off LED to indicate it came thru
PIR1bits.TMR2IF = 0;
}
}
Thanks!
Found my mistake
PIE1bits.TMR2IE == 1;
It should be PIE1bits.TMR2IE = 1;

Resources