|
|
View previous topic :: View next topic |
Author |
Message |
dgiral1993
Joined: 17 Jul 2014 Posts: 5
|
External interrupt problem using pic 16F877 |
Posted: Thu Jul 17, 2014 6:58 pm |
|
|
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
|
|
Posted: Fri Jul 18, 2014 1:28 am |
|
|
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: 19539
|
|
Posted: Fri Jul 18, 2014 1:47 am |
|
|
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...
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
|
|
Posted: Fri Jul 18, 2014 1:18 pm |
|
|
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: 19539
|
|
Posted: Fri Jul 18, 2014 3:08 pm |
|
|
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
|
|
Posted: Fri Jul 18, 2014 3:46 pm |
|
|
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: 19539
|
|
Posted: Sat Jul 19, 2014 3:35 am |
|
|
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
|
|
Posted: Sat Jul 19, 2014 6:26 am |
|
|
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: 19539
|
|
Posted: Sat Jul 19, 2014 8:13 am |
|
|
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: 9243 Location: Greensville,Ontario
|
|
Posted: Sat Jul 19, 2014 11:57 am |
|
|
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
|
|
Posted: Sat Jul 19, 2014 11:59 am |
|
|
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
|
|
Posted: Tue Jul 22, 2014 8:53 am |
|
|
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
|
|
Posted: Tue Jul 22, 2014 2:22 pm |
|
|
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. |
|
|
|
|
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
|