CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

External interrupt problem using pic 16F877

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
dgiral1993



Joined: 17 Jul 2014
Posts: 5

View user's profile Send private message Send e-mail

External interrupt problem using pic 16F877
PostPosted: Thu Jul 17, 2014 6:58 pm     Reply with quote

Hi guys, I'm trying to implement a PID control using a microcontroller, the main problem is that external interrupt is just working once. I'm using the interrupt to setting a new set point to my PID control. I really appreciate if you can figure out what the problem is.

Here is my code.

Thanks for your attention

**********************************************************
Code:

#include <16F877A.h>
#device ADC=10
#use DELAY(clock=4M)
#fuses XT, NOWDT,PUT,NOWRT
#include <math.h>
#define lcd_data_port getenv("sfr:portd")
#include <lcd.c>
#define use_portb_kbd true
#include <kbd.c>

float sp; // GLOBAL SET POINT

#INT_EXT
void setpoint(){
delay_ms(1);     
float k;
char z;
int i;
float num,x;

i=3;
num=0;
printf(lcd_putc,"\fDIGIT SET POINT 1\n"); 
while(i<=3){               
   k=kbd_getc();
   z=k;
   if (k!=0){  x=(k-48.0)*pow(10.0,i);
      num+=x;               
      delay_ms(10);
      lcd_gotoxy(1,2);
      lcd_putc(z);
               
      if(i==0){lcd_gotoxy(1,2);
         printf(lcd_putc,"\f %.1f",num);
         delay_ms(600);
         lcd_putc("\f");}
               
      i--;
     }
  }
sp= num;
}

void main()
{  // INTERRUPTIONS
   SET_TRIS_B(0X01);
   port_b_pullups(TRUE);   
   EXT_INT_EDGE(L_TO_H);
   enable_interrupts(INT_EXT);
   enable_interrupts(global);
   //************************************
   
   //PID CONTROL VARIABLES AND DECLARATION
   int16 value,control;
   float a,b,c;
   float rT,eT,iT,dT,yT,uT,iT0,eT0;
   float max,min;
   //************************************************
   
   //KBD AND LCD INICIALIZATION
   lcd_init();
   kbd_init();
   //****************************
   
   //PID VARIABLES INICIALIZATION
   min=0.0;
   max=1000.0;
   iT0=0.0;
   eT0=0.0;
   a=0.1243;
   b=0.0062;
   c=0.6214;
   //**********************************
   
   //PWM AND AD CONVERTER CONFIGURATION
   setup_timer_2(T2_DIV_BY_4,249,1);   
   setup_ccp1(ccp_pwm);
   setup_adc_ports(all_analog);
   setup_adc(adc_clock_internal);
   set_adc_channel(0);
   //*********************************
   
   while(TRUE){
      delay_us(100);
     
      // CONTROL PID PROCESS
      value=read_adc();
      delay_ms(1);
      yT=5000.0*value/1024.0;       
      rT=sp;
      eT=rT-yT;
      iT=b*eT+iT0;
      dT=c*(eT-eT0);
      uT=iT+a*eT+dT;
     
      if(uT>max){uT=max;}
      else{if(uT<min){uT=min;}}
     
      control=uT;     
      set_pwm1_duty(control);
      iT0=iT;
      eT0=eT;
      delay_ms(100); //SAMPLING FREQUENCY
   }
}

Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Fri Jul 18, 2014 1:28 am     Reply with quote

A few comments:-

1) You need to read the CCS manual, microchip data sheet, and .h file to get a complete picture.
2) Microchip data sheet explains you need to read portB in your ISR to reset the interrupt flag, hence only one action.
3) Let CCS compiler take care of tris. It's better at it than you.
4) In your ISR, get in, get out, ASAP. Avoid delays, printf to RS232 or LCD.

Mike

You've got delay_ms() both inside and outside your ISR. The compiler should have complained or thrown a wobbly.
Ttelmah



Joined: 11 Mar 2010
Posts: 19470

View user's profile Send private message

PostPosted: Fri Jul 18, 2014 1:47 am     Reply with quote

Seriously, don't do the work in the interrupt handler.

Have the interrupt set a flag. Nothing else. Then in your main loop, test for this being set, and if it is, turn off the PID, and call the setup routine.

I see Mike has said exactly the same thing... Smile

I assume this is something 'slow' like a heater?. Otherwise the PID loop time is several orders of magnitude to slow. For a slow device like a heater, then this is probably OK, but otherwise you need to rethink.

As a general comment. Follow the C standard, and declare your variables at the start of the code section, not after you have made function calls. It can cause problems.
Then you set all the channels as analog, and only use AN0 - don't. Only select channels you are going to use.
Then is ADC_CLOCK_INTERNAL recommended at your clock rate?. - Read the data sheet.
dgiral1993



Joined: 17 Jul 2014
Posts: 5

View user's profile Send private message Send e-mail

PostPosted: Fri Jul 18, 2014 1:18 pm     Reply with quote

Well, I overwrite my code taking into account your advices.

I remove all my SET POINT routine from the interrupt handler.

In ISR, i'm just setting a bit variable which reminds me when the interrupt flag is set, and also, I'm reading the portB to clear the interrupt flag.

It's still not working, I still can't call the interrupt routine more than once.

I also read manual and datasheet and I applied all the recommendations.

What else could it be having troubles?.

Here's my new code.

Code:

#include <16F877A.h>
#device ADC=10
#use DELAY(clock=4M)
#fuses XT, NOWDT,NOPUT,NOWRT
#include <math.h>
#define lcd_data_port getenv("sfr:portd")
#include <lcd.c>
#define use_portb_kbd true
#include <kbd.c>

short flag=FALSE;
int8 temp;
float setpoint();


#INT_EXT
void int_isr(){
   
      flag=TRUE;
      temp=input_B();
       
}
void main()
{  // INTERRUPTIONS
   
   port_b_pullups(TRUE);
   delay_us(10);     
   clear_interrupt (INT_EXT);       
   EXT_INT_EDGE(L_TO_H);
   enable_interrupts(INT_EXT);
   enable_interrupts(global);
   //************************************
   
   //PID CONTROL VARIABLES AND DECLARATION
   int16 value,control;
   float a,b,c;
   float rT,eT,iT,dT,yT,uT,iT0,eT0;
   float max,min;
   float sp;
   //************************************************
   
   //KBD AND LCD INICIALIZATION
   lcd_init();
   kbd_init();
   //****************************
   
   //PID VARIABLES INICIALIZATION
   min=0.0;
   max=1000.0;
   iT0=0.0;
   eT0=0.0;
   a=0.1243;
   b=0.0062;
   c=0.6214;
   sp=0.0;
   //**********************************
   
   //PWM AND AD CONVERTER CONFIGURATION
   setup_timer_2(T2_DIV_BY_4,249,1);   
   setup_ccp1(ccp_pwm);
   setup_adc_ports(RA0_ANALOG);
   setup_adc(adc_clock_internal);
   set_adc_channel(0);
   //*********************************
   
   while(TRUE){
   
      if(flag==TRUE){           
                  flag=FALSE;
                  sp=setpoint(); }
      else{
      // CONTROL PID PROCESS
      delay_us(100);
      value=read_adc();
      delay_ms(1);
      yT=5000.0*value/1024.0;       
      rT=sp;
      eT=rT-yT;
      iT=b*eT+iT0;
      dT=c*(eT-eT0);
      uT=iT+a*eT+dT;
     
      if(uT>max){uT=max;}
      else{if(uT<min){uT=min;}}
     
      control=uT;     
      set_pwm1_duty(control);
      iT0=iT;
      eT0=eT;
      delay_ms(100); //SAMPLIGN FREQUENCY
      }     
   }
}

float setpoint(){
      delay_ms(1);     
      float k;
      char z;
      int i;
      float num,x;
      i=3;
      num=0;
      printf(lcd_putc,"\fDIGIT SET POINT 1\n"); 
      while(i<=3){               
            k=kbd_getc();
            z=k;
            if (k!=0){  x=(k-48.0)*pow(10.0,i);
                        num+=x;               
                        delay_ms(10);
                        lcd_gotoxy(1,2);
                        lcd_putc(z);
               
                        if(i==0){lcd_gotoxy(1,2);
                                 printf(lcd_putc,"\f %.1f",num);
                                 delay_ms(600);
                                 lcd_putc("\f");}
               
                        i--;
         }
      }
      return num;
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19470

View user's profile Send private message

PostPosted: Fri Jul 18, 2014 3:08 pm     Reply with quote

OK.
Better.

How is ''i' ever going to get >3?.

You need to break out of the loop after displaying in i==0.....
dgiral1993



Joined: 17 Jul 2014
Posts: 5

View user's profile Send private message Send e-mail

PostPosted: Fri Jul 18, 2014 3:46 pm     Reply with quote

Hi Ttelmah, if you look the row below the "if(i==0)", there's an iteration for "i". As "i" is an integer (from 0 to 255) when "i" reaches 0,after that loop is finished.

I've probed this SET POINT function in other codes and it's worked perfectly.
I'm just reusing this function in my new application.

I thought the problem was that Flag wasn't being set during ISR routine. but I've probed it using different methods and I found flag was set "TRUE" IN the ISR, and after program finishes ISR routine and runs out to infinite loop again, flag was cleared "FALSE". I think that's how an interrupt must work. I am right, aren't I?.

So if flag is cleared and I'm right about how an interrupt works, could it be another problem why ISR routine is not activated more than once ?

Thanks for your attention.
Ttelmah



Joined: 11 Mar 2010
Posts: 19470

View user's profile Send private message

PostPosted: Sat Jul 19, 2014 3:35 am     Reply with quote

You have i being reduced, not increased. Problem is that an int8, will _wrap_. If i==0, when you decrement it, it'll be 255 on the loop test....
dgiral1993



Joined: 17 Jul 2014
Posts: 5

View user's profile Send private message Send e-mail

PostPosted: Sat Jul 19, 2014 6:26 am     Reply with quote

Ttelmah, as you say if I continue decreasing "i", when it got 0,after that it'll be 255, with that in mind, the loop condition isn't accomplished, so SetPOINT routine is now finished.
Ttelmah



Joined: 11 Mar 2010
Posts: 19470

View user's profile Send private message

PostPosted: Sat Jul 19, 2014 8:13 am     Reply with quote

Problem is that it is not guaranteed to work.

If (for instance) ANSI mode is selected, the loop will continue.
If the type definitions are changed. The loop will continue.
If you use a different compiler. The loop will probably continue.

You are 'relying' on the chip behaving in a particular way, that it is not warranted to do, anywhere in the manual....

Danger.

It's like the magician playing the 'catch a bullet in their teeth' trick, who then let somebody else load the gun.

Have you looked at just how large the pow function is?. Absolutely pointless, and stupid programming on a PIC. All you need is a large integer, that starts at 1000, and divided by 10 each time round the loop.

You also have variables declared after the first function is called. This is not legal in traditional C. CCS allows it, but has the habit of producing silly errors when it is used. Stick to traditional C.

Then you have the one that is probably causing your problem. Key-bounce.
Switches _will_ make and break several times when a key is pressed. Result the function will be called again after it exits.
temtronic



Joined: 01 Jul 2010
Posts: 9208
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sat Jul 19, 2014 11:57 am     Reply with quote

As Mr. T point out switches BOUNCE !!!

Before proceeding, create a small program that simply displays every switch(button) pressing. Within the ISR just set a flag('button_flag') saying 'true' and read PortB, in main have a simple IF condition that when button_flag is true, set flag to false, increment a variable(count) and display 'count' on a local LCD or send to PC terminal program.

Make sure it works 100 times out of 100 presses !!

WHEN that works THEN proceed wit the 'real program'.

You MUST be 100% confident that each and every button press gets seen once and only once!

hth
jay
erhane



Joined: 01 Jul 2014
Posts: 41

View user's profile Send private message Send e-mail

PostPosted: Sat Jul 19, 2014 11:59 am     Reply with quote

remove printf parts and try again. Do not use serial communication in interrupt routines. if you should, disable interrupts then use printf then clear and enable interrupts again.
dgiral1993



Joined: 17 Jul 2014
Posts: 5

View user's profile Send private message Send e-mail

PostPosted: Tue Jul 22, 2014 8:53 am     Reply with quote

Hi guys, I wanna tell you that I did everything you told me to do.

I checked the pow function and that's true, SO LARGE !!!!. I changed It by a decreasing by ten Variable, and it works the same way.

I also checked the KEY-PRESS bounce with a probe program, and It's working perfectly.

I think I've found the mistake. It turns out that in PORT B is connected a 4x3 keyboard, as "kbd.c" configuration, the only free pin is number 1 (INTERRUPT EXTERNAL as we know), I erased keyboard code lines from my program and it worked perfectly.

I'm using a PIC 16F877A, and I don't have any PORT free. In PORTD I have the lcd display, PORTC i'm using it one pin for PWM, and PORTA one pin for ADC conversion.

How can I connect my keyboard in order to code works well ?
Should I edit kbd.c so It can works on pins I have free ?
or What else can I do ?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Jul 22, 2014 2:22 pm     Reply with quote

Keypad and lcd on the same pins (on PortB):
http://www.ccsinfo.com/forum/viewtopic.php?t=26229

However, if you are using pin B0 for the external interrupt, doesn't that
leave 7 pins free on PortB ? That's enough for the 4x3 keypad.
I don't see what your problem is.

If you're using pins B6 and B7 for external components, you probably
can't run the hardware debugger. But it doesn't matter. Just program
the board in "Release" mode (not Debug mode). The Release/Debug
drop-down box at the top of MPLAB controls the mode. Also, remove
any DEBUG fuse and also any #device ICD=TRUE statement.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group