Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] 89c51 PCA interrupt disabled mysteriously - help please!

Status
Not open for further replies.

afhoke

Newbie level 2
Newbie level 2
Joined
Apr 24, 2013
Messages
2
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,281
Visit site
Activity points
1,296
Hello,

I'm using an AT89C51RC2. I'm using one of the PCA modules a timer, and the timer fires an interrupt when it matches the value in a compare register. The ISR works the first few times and then is mysteriously disabled: the IEN0 byte changes from 0xC0 to 0x40. I'm pretty sure IEN0 can't be changed by hardware, but I can't figure out which part of my code is changing it, after it is first initialized. The program continues to run with no other known errors, but the ISR stops running once IEN0 has changed.

Is it possible that simply reading IEN0 in order to printf it could change its value? I don't know how else it could be getting changed. Any help would be much appreciated.

Andy

Here is my code:
Code:
#include <at89c51ed2.h>  //also includes 8052.h and 8051.h
#include <mcs51reg.h>
//#include <8052.h>   // also included in at89c51ed2.h
#include <stdio.h>
#include "ADCdrive.h"

// Global functions.  See function definitions for descriptions.
void putchar (char c);
char getchar ();
char getputchar ();
void getstr(char s[SIZE], int length);
void serialinit();
// Timer 0 functions
void init_t0();
void start_t0();
unsigned long int read_t0();
// PWM functions
void init_pwms();
void start_pwms();
void set_pwms(unsigned char duty256);
void stop_pwms();
void init_TPCA();


// Global variables
volatile unsigned char r, col;  // current column and row of LCD cursor
//volatile unsigned char overflows;
volatile unsigned char cnt;
const unsigned char ccap_array[256] = {0,3,5,8,11,13,16,19,21,24,27,29,32,35,37,40,42,45,48,50,53,55,58,61,63,
66,68,71,73,76,78,81,83,86,88,90,93,95,98,100,102,105,107,109,112,114,116,118,121,123,125,127,129,
132,134,136,138,140,142,144,146,148,150,152,154,155,157,159,161,163,164,166,168,170,171,173,174,
176,178,179,181,182,184,185,186,188,189,190,192,193,194,195,196,197,199,200,201,202,203,204,205,
205,206,207,208,209,209,210,211,211,212,213,213,214,214,214,215,215,216,216,216,216,217,217,217,
217,217,217,217,217,217,217,217,217,217,216,216,216,216,215,215,214,214,214,213,213,212,211,211,
210,209,209,208,207,206,205,205,204,203,202,201,200,199,197,196,195,194,193,192,190,189,188,186,
185,184,182,181,179,178,176,174,173,171,170,168,166,164,163,161,159,157,155,154,152,150,148,146,
144,142,140,138,136,134,132,129,127,125,123,121,118,116,114,112,109,107,105,102,100,98,95,93,90,
88,86,83,81,78,76,73,71,68,66,63,61,58,55,53,50,48,45,42,40,37,35,32,29,27,24,21,19,16,13,11,8,5,3
};

// All processor XRAM should be enabled before the call to main().
// This can be done in a user supplied _sdcc_external_startup() function.
// See section 3.11.1 of sdccman.pdf for SDCC revision 2.6.0.
_sdcc_external_startup()
{
AUXR |= 0x0C; // enable all 1k of internal XRAM (see p25 of manual)
}

void main()
{
    char adcin, vpos = 2;
    int vdig;
    long unsigned int halfper;

    P3_2 = 0;   // used for debugging

    serialinit();

    // initialization functions
    SPIinit();      // initialize SPI peripheral to talk to MAX1112 ADC
    //init_t0();      // initialize timer 0
    //init_pwms();    // initialize PWMS
    init_TPCA();    // initialize PCA timer (fires interrupt each PWM period to update duty cycle)

    start_pwms();

    while(1)
    {
        adcin = ADCread();
        vdig = (int)adcin;  // convert ADC value from twos complement to signed integer

        // Check voltage polarity
        if (adcin>0)
        {
            if (!vpos) // last value was negative
            {
                start_t0();     // time positive half-cycle
            }
            vpos = 1;
        }

        else
        {
            if (vpos == 1) // last value was positive
            {
                printf("CMOD %x, IE %x, IEN0 %x, CCAPM2 %x, CCAP2L %x, CCAP2H %x, CCON %x, cnt %x \n\r", CMOD, IE, IEN0, CCAPM2, CCAP2L, CCAP2H, CCON, cnt);

                halfper = read_t0();    // half of period in us
                printf("halfper = %lu us\n\r", halfper);

            }
            vpos = 0;
        }

        getchar();
    }
}

//  Send a character to terminal emulator
void putchar (char c)
{
	//while (!TI);				// compare asm code generated for these three lines
	//while (TI == 0);
	while ((SCON & 0x02) == 0);    // wait for TX ready, spin on TI
	SBUF = c;  	// load serial port with transmit value
	TI = 0;  	// clear TI flag
}

// Retrieve a character from terminal emulator
char getchar ()
{
    //while (!RI);                // compare asm code generated for these three lines
    //while ((SCON & 0x01) == 0);  // wait for character to be received, spin on RI
	while (RI == 0);
	RI = 0;			// clear RI flag
	return SBUF;  	// return character from SBUF
}

// Retrieve a character from terminal emulator and echo it.
char getputchar ()
{
	char cc;
    //while (!RI);                // compare asm code generated for these three lines
    //while ((SCON & 0x01) == 0);  // wait for character to be received, spin on RI
	while (RI == 0);
	RI = 0;			// clear RI flag
    cc = SBUF;
	putchar(cc);    // echo character
	return cc;  	// return character from SBUF
}

// Get a string and echo it to terminal emulator
//void getstr (char s[SIZE], int length)
void getstr (char s[], int length)
{
	int i = 0;

	do
	{
		s[i] = getchar();
		putchar(s[i]);
		i++;
		//printf("(int)s[i-1] = %d   ", (int)s[i-1]);
	}while (i <= SIZE && (int)s[i-1] > 31 && i <= length);
	s[i-1] = '\0';
}

// Serial port initialization code found at https://sites.google.com/site/controlandelectronics/serial-port-uart-using-8051
void serialinit()
{
        TMOD = 0x20;
        SCON = 0x50;
        TH1  = 0xFD;
        TL1  = 0xFD;
        TR1  = 1;
        TI = 1;
}

// Initialize Timer 0
void init_t0()
{
    IE |= 0x82;     // enable global interrupts and T0 interrupt
    TMOD |= 0x01;   // set timer 0 to 16 bit mode
}

void start_t0()
{
    //overflows = 0;      // initialize timer overflow counter
    TL0 = 0;
    TH0 = 0;
    TCON |= 0x10;       // start timer zero  (set TCON.4 to 1)
    //printf("\n\rStarting timer 0.  TCON = %d. overflows = %ld.  TL0 = %d.  TH0 = %d.\n\r", TCON, overflows, TL0, TH0);
}

// Stops T0 and returns the total time since it was started in microseconds (including any past overflows.)
long unsigned int read_t0()
{
    unsigned long int t, t2ns, t2us;
    //float t2f;
    //printf("Size of int is %d.  Size of long is %d.  Size of unsigned long is %d.\n\r", (char)sizeof(int), (char)sizeof(long), (char)sizeof(unsigned long));

    //TCON &= 0x0EF;     // stop timer 0  (set TCON.4 to 0)

    //printf("TL0 = %d.  TH0 = %d.   TCON = %d.  overflows = %ld.\n\r", TL0, TH0, TCON, overflows);
    t = (unsigned long int)TL0 + (unsigned long int)256*(unsigned long int)TH0;  // calculate T0 value
    //printf("Timer 0 value is %ld.\n\r", t);

    /* Timer calculation:
            Counter speed = 11.0592 MHz / 12 = 921600 Hz
            Total time = t/921600 s
                       = 10000*t/9216 us
            */
    t2us = 10000*t/9216;                                //  time in ms
    //t2us = t2ns/100;                                  // time in milliseconds
    return t2us;
}

// T0 Interrupt service routine
void isr_one(void) __interrupt (1)
{
	//overflows++;         // increment number of overflows
	//printf("ISR 1;  Overflows: %d.\n\r", overflows);
}

// Send pulse width modified square waves to two output pins
void start_pwms()
{
    CCAP0H = 0x0F3;     // Set PWM0 duty cycle to 5%  (0.95*256 = 0xF3)
    CCAP1H = 0x66;      // Set PWM1 duty cycle to 60% (0.4*256 = 0x66)

    CCAPM0 |= 0x42;     // Put PCA 0 in PWM mode (8 bit)  (output: P1.3)
    CCAPM1 |= 0x42;     // Put PCA 1 in PWM mode (8 bit)  (output: P1.4)
}


// Send PWM square waves with specified duty cycle to two output pins
//   PCA 1 has complement of PCA 0 duty cycle
//   duty cycle = duty256/256
void set_pwms(unsigned char duty256) __critical
{
    CCAP0H = duty256;     // Set PWM0 duty cycle to specified value  (duty cycle = duty256/256)
    CCAP1H = 0xFF - duty256;      // Set PWM1 duty cycle to 1-(PWM0 duty)
}

//  Stop sending PWM signals to outputt pins
void stop_pwms()
{
    printf("\n\rStopping both PWMs.\n\r");
    CCAPM0 &= 0x0BD;     // Remove PCA 0 from PWM mode (8 bit)  (output: P1.3)
    CCAPM1 &= 0x0BD;     // Remove PCA 1 from PWM mode (8 bit)  (output: P1.4)
    printf("\n\rEnter next command.\n\r");
}

// Initialize PWMs
void init_pwms ()
{
    CMOD &= 0x0f9;      // Set PCAs to run at 1/6 of peripheral clock (datasheet p33)  (measured f_PWM = 3597 Hz)  ; can also be set to T0 overflow frequency
    CCON |= 0x40;       // Enable PCAs (programmable counter arrays)
}

// Initialize PCA timer (PCA = programmable counter array)
void init_TPCA () __critical
{
    CMOD &= 0x0f9;      // Set PCAs to run at 1/6 of peripheral clock (datasheet p33)  (measured f_PWM = 3597 Hz)  ; can also be set to T0 overflow frequency
    CMOD |= 0x01;       // Enable PCA timer overflow interrupt
    //IE |= 0xC0;         // Enable PCA interrupt, and enable interrupts globally
    IEN0 |= 0xC0;         // Enable PCA interrupt, and enable interrupts globally
    CCAPM2 = 0x09;      // Put PCA module 2 in timer mode with interrupt enabled (table 25 on p37) (ECOM2 bit is set automatically when CCAP2H is set, below)
    CCAP2L = 0xff;      // Low byte of counter compare register
    CCAP2H = 0x01;      // High byte of counter compare register.  Setting this also sets CCAPM2.6 = ECOM2.  See Fig. 14 on p40.
    //CCAPM2 |= 0x40;     // Set ECOM2
    CCON |= 0x40;       // Enable PCAs
    printf("CMOD %x, IE %x, IEN0 %x, CCAPM2 %x, CCAP2L %x, CCAP2H %x, CCON %x \n\r", CMOD, IE, IEN0, CCAPM2, CCAP2L, CCAP2H, CCON);
}

// PCA Interrupt service routine
void isr_six(void) __interrupt (6)
{
	P3_2 = 1;
	CCON &= 0xFB;       // Clear PCA 2 interrupt flag
	P3_2 = 0;
	CCAP2H++;           // Increment high byte of compare register so interrupt will occur every 256 timer counts
	P3_2 = 1;
        cnt++;
        P3_2 = 0;
	set_pwms(ccap_array[cnt]);
	P3_2 = 1;
	P3_2 = 0;
}
 

FYI, in case anyone else encounters this problem: the problem was that init_TPCA(), where the interrupt was enabled, was a __critical function. As soon as the function exited the interrupt was disabled. The ISR ran a few times because it took some time to process the printf at the end of the function. Removing the __critical keyword fixed the problem.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top