Interrupts using PIC18f46k20 microcontroller

Thread Starter

iamtobey

Joined Apr 3, 2013
3
I am new fairly new to C++ and i'm having problems Initializing the required resisters needed for my project on motor speed control. I have used the data sheet(link below) but cannot get around the problem.http://ww1.microchip.com/downloads/en/devicedoc/41303g.pdf

The two problems I am encountering

interrupts not occurring. Code for interrupts process is below

when I enable global interrupts i.e. 'INCONbits.GIE =1' nothing displays on the pic (leds and screen) as if it where in sleep mode.

Initialization.
Rich (BB code):
 void InitializeSystem(void)
{

OSCCON = 0b01110000;
OSCTUNEbits.PLLEN = 0;                  // turn off the PLL

// Setup I/O Ports.

    ANSEL = 0x00;                          // all digital   check if needed*
TRISA = 0;              // TRISA outputs
LATA = 0b11110000;        // drive PORTA pins low

oled_res = 1;               // do not reset LCD yet
oled_cs = 1;                // do not select the LCD

TRISB = 0b11111111;


LATC = 0b00101000;
TRISC = 0b00000000;

TRISD = 0;              // TRISD is LED output
LATD = 0;               // turn off LEDS

TRISE = 0b00000111;

// Set up global interrupts
    RCONbits.IPEN = 1;          // Enable priority levels on interrupts
INTCONbits.GIE = 1;    
INTCONbits.RBIE = 1;
INTCONbits.RBIF = 0;


//configure buttons
WPUB = 0b00001111;
INTCON2bits.RBPU = 0;       // turn on weak pull ups for RB0-RB3

INTCONbits.INT0IF = 0;                  // clear RB0 INT
INTCONbits.INT0IE = 1;                  // enable RB0 INT
INTCON2bits.INTEDG0 = 0;        // interrupt on falling edge 

INTCON3bits.INT1IF = 0;                 // clear RB1 INT
INTCON3bits.INT1IE = 1;                 // enable RB1 INT   
INTCON2bits.INTEDG1 = 0;            // interrupt on falling edge


INTCON3bits.INT2IF = 0;                 // clear RB2 INT
INTCON3bits.INT2IE = 1;                 // enable RB2 INT
INTCON2bits.INTEDG2 = 0;            // interrupt on falling edge




// Setup TMR1
// Configure Timer 1
T1CON   = 0b11111101;
INTCONbits.TMR0IE=1;
INTCONbits.TMR0IF=0;
INTCON2bits.TMR0IP=0;

// Configure MSSP for SPI master mode
SSPCON1 = 0b00110010;                   // clock idle high, Clk /64

SSPSTAT = 0b00000000;

PIR1bits.TMR1IF = 0; 
PIE1bits.TMR1IE = 1; 



OSCCON = 0b01110000;
OSCTUNEbits.PLLEN = 1;  // Puts the PIC at 64MHz
CCP1CON = 0b00001100;
PSTRCON = 0b00010001;

    // setup timer 2 for pwm
    T2CON = 0b00000111;// Prescale = 1:16, timer on, postscale not used with CCP module
PR2 = 255;     // Configure the Timer2 period
TRISCbits.TRISC2  = 0;              // Make CCP1 pin as output
CCP1CON = 0x0C;           // Configure CCP1 module in PWM mode
T2CON = 0x01;    // Set Prescaler to be 4, hence PWM frequency is set to 4.88KHz.                 
T2CON |= 0x04;            // Enable the Timer2, hence enable the PWM.
SetPWMDutyCycle(pwm);       // Intialize the PWM to 0.5 duty cycle



     } // end InitializeSystem
void SetPWMDutyCycle(unsigned int DutyCycle)      // Give a value in between 0 and
                                                      // 1024 for DutyCycle    
{
CCPR1L   = DutyCycle>>2;            // Put MSB 8 bits in CCPR1L
CCP1CON &= 0xCF;                    // Make bit4 and 5 zero
CCP1CON |= (0x30&(DutyCycle<<4));   // Assign Last 2 LSBs to CCP1CON
}
Interrupt Service
Rich (BB code):
#pragma code InterruptVectorLow = 0x18
void InterruptVectorLow (void)
{
  _asm  
     goto InterruptServiceLow //jump to interrupt routine
  _endasm
}

//----------Interrupt Service Routine-------------------------------------
#pragma interrupt InterruptServiceHigh
void InterruptServiceHigh(void)
{

if(INTCONbits.RBIF)
{
    INTCONbits.RBIF = 0;

    if(PORTBbits.RB6!=0)                                         
        {
            UpperCounter++; //increment UpperCounter if RB6 goes high                           
            speed_counter++; // increment the counter used to calculate  
                                              //speed                                               
            LATDbits.LATD7=~LATDbits.LATD7;  // LED7 changes state each   
                                                             //rising edge                      
            Delay10TCYx(1);         
        }



   else if(PORTBbits.RB7!=0)    
             //count pulses from ch B                                       
        {
            LowerCounter++; //increment LowerCounter if RB7 goes high                                                                    
            speed_counter++;                                    
                    LATDbits.LATD0=~LATDbits.LATD0; //LED0 changes state each  
                                                            //rising edge                   
            Delay10TCYx(1);             
        }



     else if(PORTBbits.RB5 != 0)
                   //count pulses from ch Z                                                                   
        {
            LowerCounter=0;  //reset LowerCounter                           
                    UpperCounter=0;   //reset UpperCounter                          
            Delay10TCYx(1);             
        }
}
}

#pragma interruptlow InterruptServiceLow// "interruptlow" pragma for low priority
void InterruptServiceLow(void)
{

if(INTCONbits.TMR0IF)
       {
    position2=speed_counter;                                            

    speed=((((position2-position1)/64)*60)/2.1);        
        //calculate speed. TMR0 on 16bit prescalar1:8     
    TMR0H = 0;    
            //  clear timer - always write upper byte first                                        
    TMR0L = 0;
            INTCONbits.TMR0IF = 0;    
             // clear (reset) flag                                   
    LATDbits.LATD2=~LATDbits.LATD2;
    position1=speed_counter;
 }
}
 

tshuck

Joined Oct 18, 2012
3,534
You are never setting up IOCB for your interrupt-on-change, though that shouldn't result in what you are setting, unless you aren't doing anything in main (). Posting the test of the code may help.

You should never enable interrupts before you are ready to handle them, so your global interrupt enable is not set until the end of the initialize code.

Additionally, you haven't enabled low priority interrupts and have not set up TMR0.
 

Thread Starter

iamtobey

Joined Apr 3, 2013
3
You are never setting up IOCB for your interrupt-on-change, though that shouldn't result in what you are setting, unless you aren't doing anything in main (). Posting the test of the code may help.

You should never enable interrupts before you are ready to handle them, so your global interrupt enable is not set until the end of the initialize code.

Additionally, you haven't enabled low priority interrupts and have not set up TMR0.
thanks for your reply.

full code with observations corrected below
Rich (BB code):
// High priority interrupt vector

#pragma code InterruptVectorHigh = 0x08
void InterruptVectorHigh (void)
{
  _asm
    goto InterruptServiceHigh //jump to interrupt routine
  _endasm
}

//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Low priority interrupt vector

#pragma code InterruptVectorLow = 0x18
void InterruptVectorLow (void)
{
  _asm
    goto InterruptServiceLow //jump to interrupt routine
  _endasm
}

//----------Interrupt Service Routine-------------------------------------
#pragma interrupt InterruptServiceHigh
void InterruptServiceHigh(void)
{

	if(INTCONbits.RBIF)
	{
		INTCONbits.RBIF = 0;
        
		if(PORTBbits.RB6!=0)										//count pulses from ch A
			{
				UpperCounter++;										//increment UpperCounter if RB6 goes high
				speed_counter++;											// increment the counter used to calculate speed
				LATDbits.LATD7=~LATDbits.LATD7;						// LED7 changes state each rising edge
				Delay10TCYx(1);			
			}

	

	   else	if(PORTBbits.RB7!=0)											//count pulses from ch B
			{
				LowerCounter++;										//increment LowerCounter if RB7 goes high
				speed_counter++;									
				LATDbits.LATD0=~LATDbits.LATD0;						//LED0 changes state each rising edge
				Delay10TCYx(1);				
			}

				

         else if(PORTBbits.RB5 != 0)											//count pulses from ch Z
			{
				LowerCounter=0;								//reset LowerCounter
		     	UpperCounter=0;								//reset UpperCounter
				Delay10TCYx(1);				
			}
	}
}

#pragma interruptlow InterruptServiceLow// "interruptlow" pragma for low priority
void InterruptServiceLow(void)
{

	if(INTCONbits.TMR0IF)
    {
		position2=speed_counter;											

		speed=((((position2-position1)/64)*60)/2.1);				// calculate speed. TMR0 on 16bit prescalar1:8 
        // calculation shows  -> rpm = (FREQ_out*60)/N                //N= number of slots at disc
		TMR0H = 0;                     								// clear timer - always write upper byte first
   		TMR0L = 0;
        INTCONbits.TMR0IF = 0;          							// clear (reset) flag
		LATDbits.LATD2=~LATDbits.LATD2;
		position1=speed_counter;
	}
}
/**** E N D   I N T E R R U P T   S E R V I C E ******************************************/


void PressMe (unsigned char *ptrButtonPressed) // this function waits for a button press
{
	*ptrButtonPressed = 0;
	Delay10KTCYx(90); //Debounce switch

	while(*ptrButtonPressed == 0)
	{
		if(PORTBbits.RB0 != 1)
		{
			*ptrButtonPressed = 1;
		}
		else if(PORTBbits.RB1 != 1)
		{
			*ptrButtonPressed = 2;
		}
		else if(PORTBbits.RB2 != 1)
		{
			*ptrButtonPressed = 3;
		}
		else if(PORTBbits.RB3 != 1)
		{
			*ptrButtonPressed = 4;
		}
		else
		*ptrButtonPressed = 0;
	}
}





	void main(void){

	char Sbuffer[90];
	unsigned char ButtonPressed = 0; 						/*This variable is  is used to tell the programme whether RB0, RB1, RB2 or RB3 has been pressed*/						
	unsigned char *ptrButtonPressed; 						//Pointers to pass to functions
	ptrButtonPressed = &ButtonPressed;

	InitializeSystem();

	// These are the functions you need to use to initialise the display
	oled_init();
	oled_clear();
	oled_refresh();
	Delay10KTCYx(250);	
 	sprintf(Sbuffer,"\nHello Friend, \n\nWelcome to our MOTOR");	// Print to string buffer
	oled_puts_2x(Sbuffer);					// double height
	oled_refresh();							// And refresh the display so we can see things!
	Delay10KTCYx(500);
	Delay10KTCYx(500);
    Delay10KTCYx(500);
    Delay10KTCYx(500);
    Delay10KTCYx(500);
    Delay10KTCYx(500);
	Delay10KTCYx(500);
    Delay10KTCYx(500);


		while (1)
		{
				
		MainMenu(ptrButtonPressed);
		
		}		
	}
	




void InitializeSystem(void)
{

	OSCCON = 0b01110000;
	OSCTUNEbits.PLLEN = 0; 					// turn off the PLL

	// Setup I/O Ports.
	
    ANSEL = 0x00;                          // all digital   check if needed*
	TRISA = 0;								// TRISA outputs
	LATA = 0b11110000;						// drive PORTA pins low

	oled_res = 1;							// do not reset LCD yet
	oled_cs = 1;							// do not select the LCD

	TRISB = 0b11111111;


	LATC = 0b00101000;
	TRISC = 0b00000000;

	TRISD = 0;								// TRISD is LED output
	LATD = 0;								// turn off LEDS

	TRISE = 0b00000111;



	//configure buttons
	WPUB = 0b00001111;
	INTCON2bits.RBPU = 0; 					// turn on weak pull ups for RB0-RB3

	INTCONbits.INT0IF = 0;					// clear RB0 INT
	INTCONbits.INT0IE = 1;					// enable RB0 INT
	INTCON2bits.INTEDG0 = 0;				// interrupt on falling edge 

	INTCON3bits.INT1IF = 0;					// clear RB1 INT
	INTCON3bits.INT1IE = 1;					// enable RB1 INT	
	INTCON2bits.INTEDG1 = 0;				// interrupt on falling edge


	INTCON3bits.INT2IF = 0;					// clear RB2 INT
	INTCON3bits.INT2IE = 1;					// enable RB2 INT
	INTCON2bits.INTEDG2 = 0;				// interrupt on falling edge
         
  
    

	// Setup TMR0
	T0CON = 0b00000010;          // 16- bit prescaller 1:8
	INTCONbits.TMR0IE=1;
	INTCONbits.TMR0IF=0;
	INTCON2bits.TMR0IP=0;

	// Configure Timer 1
	T1CON 	= 0b11111101;



	// Configure MSSP for SPI master mode
	SSPCON1 = 0b00110010;					// clock idle high,   Clk /64

	SSPSTAT = 0b00000000;

	PIR1bits.TMR1IF = 0; 
	PIE1bits.TMR1IE = 1; 



	OSCCON = 0b01110000;
	OSCTUNEbits.PLLEN = 1;	// Puts the PIC at 64MHz
	CCP1CON = 0b00001100;
	PSTRCON = 0b00010001;

    // setup timer 2 for pwm
    T2CON = 0b00000111;// Prescale = 1:16, timer on, postscale not used with CCP module
	PR2 = 255;     // Configure the Timer2 period
	TRISCbits.TRISC2  = 0;              // Make CCP1 pin as output
	CCP1CON = 0x0C;           // Configure CCP1 module in PWM mode
	T2CON = 0x01;             // Set Prescaler to be 4, hence PWM frequency is set to 4.88KHz.
	T2CON |= 0x04;            // Enable the Timer2, hence enable the PWM.
	SetPWMDutyCycle(pwm);       // Intialize the PWM to 0.5 duty cycle

	// Set up global interrupts
	IOCB = 1;	
    RCONbits.IPEN = 1;          			// Enable priority levels on interrupts
	INTCONbits.RBIE = 1;
	INTCONbits.RBIF = 0;
    INTCONbits.GIE = 1;
    INTCONbits.PEIE = 1;
   
   T0CONbits.TMR0ON = 1;   // START TIMER0


    } // end InitializeSystem
	void SetPWMDutyCycle(unsigned int DutyCycle)      // Give a value in between 0 and 1024 for DutyCycle
	{
	CCPR1L   = DutyCycle>>2;        	// Put MSB 8 bits in CCPR1L
	CCP1CON &= 0xCF;                	// Make bit4 and 5 zero
	CCP1CON |= (0x30&(DutyCycle<<4));   // Assign Last 2 LSBs to CCP1CON
	}
 
Last edited:

nsaspook

Joined Aug 27, 2009
13,086
Just make sure you understand that the meaning of .GIE and .PEIE change when priority levels are enabled. Another thing to think about when using both low and high ISRs is the interaction of non-ATOMIC (multi-instruction) operations on variables. For example: The value of speed_counter (if it's more than 8 bits) is assigned to another variable in the LOW ISR but speed_counter is changed in the HIGH ISR. There is a small window during the assignment of speed_counter if it's not ATOMIC to the HIGH ISR that the value might change in the middle of the code because the HIGH ISR changed the value mid-step. To be sure the operations are handled correctly you should use a memory barrier to protect those operations that might be affected by asynchronous timing. Using 'volatile' to be sure we don't cache the value, adding the 'near' modifier (Access RAM no banking) and/or just disabling HIGH interrupts during operations of a shared variable within the LOW ISR or main.
 

Thread Starter

iamtobey

Joined Apr 3, 2013
3
Just make sure you understand that the meaning of .GIE and .PEIE change when priority levels are enabled. Another thing to think about when using both low and high ISRs is the interaction of non-ATOMIC (multi-instruction) operations on variables. For example: The value of speed_counter (if it's more than 8 bits) is assigned to another variable in the LOW ISR but speed_counter is changed in the HIGH ISR. There is a small window during the assignment of speed_counter if it's not ATOMIC to the HIGH ISR that the value might change in the middle of the code because the HIGH ISR changed the value mid-step. To be sure the operations are handled correctly you should use a memory barrier to protect those operations that might be affected by asynchronous timing. Using 'volatile' to be sure we don't cache the value, adding the 'near' modifier (Access RAM no banking) and/or just disabling HIGH interrupts during operations of a shared variable within the LOW ISR or main.
Ahh. I didn't even think off that. That is a problem that i will try to deal with when i get there. For now, the interrupts don't seem to be occurring so the Speed_counter aint counting nothing. Thank you for your observations though, I will have a look and try tomake appropriate corrections.
 

ErnieM

Joined Apr 24, 2011
8,377
I oft use "bigger than byte" quantities inside C18 C code where the value is changed inside an ISR. I insure I copy it correctly by this code snippet:

Rich (BB code):
   while (A != B) A = B;
I made a small macro to write this code for me but don't have it handy at the moment on this iPad. I will post again later if I remember.

Here's the macro. I only use 1 variable inside the ISR so it's a bit non-general, but you can fix that easily:

Rich (BB code):
  // "thread safe" macro to read current Ticks
  #define GetTicks(Dest) while(Dest != Ticks) Dest = Ticks
  #define ResetTicks()   while(Ticks != 0) Ticks = 0
 
Last edited:

nsaspook

Joined Aug 27, 2009
13,086
Maybe "thread safer" :) but with C18 without mutual-exclusion during the multi-instruction sequence compare there are still some cases where a ISR could cause problems. (mainly during byte rollover) If the increase in interrupt latency is not a problem disabling interrupts is usually more robust and quicker.

C18 -Opa- optimized generated code using long A,B;.

while (A != B) A = B;

INTCONbits.GIE = 0;
A = B;
INTCONbits.GIE = 1;

Rich (BB code):
5:             void main(void)
6:             {
7:                 while (A != B) A = B;
00A6  0E0A     MOVLW 0xA
00A8  6EE9     MOVWF FSR0L, ACCESS
00AA  0E0F     MOVLW 0xF
00AC  6EEA     MOVWF FSR0H, ACCESS
00AE  50EE     MOVF POSTINC0, W, ACCESS
00B0  010F     MOVLB 0xF
00B2  190E     XORWF 0xE, W, BANKED
00B4  E10C     BNZ 0xCE
00B6  50EE     MOVF POSTINC0, W, ACCESS
00B8  010F     MOVLB 0xF
00BA  190F     XORWF 0xF, W, BANKED
00BC  E108     BNZ 0xCE
00BE  50EE     MOVF POSTINC0, W, ACCESS
00C0  010F     MOVLB 0xF
00C2  1910     XORWF 0x10, W, BANKED
00C4  E104     BNZ 0xCE
00C6  50EE     MOVF POSTINC0, W, ACCESS
00C8  010F     MOVLB 0xF
00CA  1911     XORWF 0x11, W, BANKED
00CC  E100     BNZ 0xCE
00CE  E009     BZ 0xE2
00D0  CF0E     MOVFF B, A
00D2  FF0A     NOP
00D4  CF0F     MOVFF 0xF0F, 0xF0B
00D6  FF0B     NOP
00D8  CF10     MOVFF 0xF10, 0xF0C
00DA  FF0C     NOP
00DC  CF11     MOVFF 0xF11, 0xF0D
00DE  FF0D     NOP
00E0  D7E2     BRA main
8:             
9:                 INTCONbits.GIE = 0;
00E2  9EF2     BCF INTCON, 7, ACCESS
10:                A = B;
00E4  CF0E     MOVFF B, A
00E6  FF0A     NOP
00E8  CF0F     MOVFF 0xF0F, 0xF0B
00EA  FF0B     NOP
00EC  CF10     MOVFF 0xF10, 0xF0C
00EE  FF0C     NOP
00F0  CF11     MOVFF 0xF11, 0xF0D
00F2  FF0D     NOP
11:                INTCONbits.GIE = 1;
00F4  8EF2     BSF INTCON, 7, ACCESS
12:            
13:            }
00F6  0012     RETURN 0
 
Top