|
|
View previous topic :: View next topic |
Author |
Message |
gurdeepsembi
Joined: 22 Dec 2010 Posts: 14 Location: UK
|
PIC18F2520 code halting. Suspect interrupts clashing |
Posted: Thu Jan 27, 2011 10:11 am |
|
|
Hi all,
I have a simple program in which I read the status of all my inputs (with some code to handle debounce) using timer1. I use timer3 to increment other variables in my code.
I also have a position sensor (gear wheel sensor which gives me a pulse every time it detects a gear tooth). I basically turn on an LED when I get to a certain position and turn it off when I get to my zero position.
The position sensor is wired into the CCP1 input. I am using the CCP input because a future requirement is to measure the pulse frequency (not implemented) and from what I have read it seemed best to use the CCP input for this. The maximum pulse frequency is 9.5 Hz.
The problem I am getting is that when I have the program running with the debugger in MPLAB, the program halts. If I press the Run button in MPLAB the program continues. This 'program halt' only happens when the motor is running.
I suspect the CCP1 and TIMER1 and TIMER3 interrupts clash at some point and cause the program to halt. It does not happen all the time and I suspect this is because the pulses from the sensor do not always clash with the TIMER1 and TIMER3 interrupts.
Has anybody ever run into a similar problem and know a workaround? Or am I completely wrong in thinking the interrupts may be clashing?
The CCS C Compiler Version is PCH v4.114.
My code is below:
Code: |
//--------------------------------------------------------------------------------
// Set PIC type and settings
//--------------------------------------------------------------------------------
#include <18F2520.h>
#device ICD=TRUE //disable this for production as it removes code protection
//#device adc=8
#FUSES NOWDT //Do Not Use Watch Dog Timer
//#FUSES WDT4096 //Watch Dog Timer uses 1:4096 Postscale - 1 WD cycle is 4ms - With postscale, timeout is 16.38 secs
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES BROWNOUT //Enable brownout reset
#FUSES BORV27 //Brownout reset at 2.7V
#FUSES NOPUT //No Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES DEBUG //Debug mode for use with ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES NOWRTC //configuration not registers write protected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES MCLR //Master Clear pin enabled
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOCPB //No Boot Block code protection
#FUSES NOWRTB //Boot block not write protected
#use delay(clock=8000000,RESTART_WDT)
//--------------------------------------------------------------------------------
// define inputs and outputs
//--------------------------------------------------------------------------------
#define IP1 PIN_C0 // Open PB (NO) / Open KSW (NO)
#define IP2 PIN_C1 // Close PB (NO) / Close KSW (NO)
#define IP_POS_SENS PIN_C2 // Position Sensor
#define IP4 PIN_C3 // Close Limit (NC)
#define IP5 PIN_C4 // SAC Loop(NC)
#define IP6 PIN_C5 // Photocell Input(NC)
#define IP7 PIN_C6 // Inverter Hard Safety Feedback
#define IP8 PIN_C7 // Safety Relay Interface (NC)
//Daughter Board Inputs
#define IP9 PIN_B0 // Open Limit (NC)
#define IP10 PIN_B3 // Free Entry Loop / Entry Card Reader
#define IP11 PIN_B2 // Free Exit Loop / Exit Card Reader
#define OP_BUZZER_OB PIN_A4 // On Board Buzzer
#define CONF_SW1 PIN_B7 // Configuration Dip Switch 1
#define CONF_SW2 PIN_B6 // Configuration Dip Switch 2
#define CONF_SW3 PIN_B5 // Configuration Dip Switch 3
#define CONF_SW4 PIN_B4 // Configuration Dip Switch 4
#define CONF_SW5 PIN_B3 // Configuration Dip Switch 5
#define CONF_SW6 PIN_B2 // Configuration Dip Switch 6
#define CONF_SW7 PIN_B1 // Configuration Dip Switch 7
#define CONF_SW8 PIN_B0 // Configuration Dip Switch 8
//--------------------------------------------------------------------------------
// define global variables
//--------------------------------------------------------------------------------
int gate_direction = 2;
int16 cnt_timeout_timer = 0;
int en_timeout_timer = 0;
int Config_Timeout_Timer_Max = 100;
int cnt_tmr_100ms = 0;
int cnt_tmr_1s = 0;
//int cnt_tmr_2s = 0;
int in1_val1 =0, in1_val2 =1, in1_val3 =0;
int in2_val1 =0, in2_val2 =1, in2_val3 =0;
int in3_val1 =0, in3_val2 =1, in3_val3 =0;
int in4_val1 =0, in4_val2 =1, in4_val3 =0;
int in5_val1 =0, in5_val2 =1, in5_val3 =0;
int in6_val1 =0, in6_val2 =1, in6_val3 =0;
int in7_val1 =0, in7_val2 =1, in7_val3 =0;
int in8_val1 =0, in8_val2 =1, in8_val3 =0;
int in9_val1 =0, in9_val2 =1, in9_val3 =0;
//int in10_val1 =0, in10_val2 =1, in10_val3 =0;
int in11_val1 =0, in11_val2 =1, in11_val3 =0;
int in12_val1 =1, in12_val2 =1, in12_val3 =1;
int in13_val1 =0, in13_val2 =1, in13_val3 =0;
//int in14_val1 =0, in14_val2 =1, in14_val3 =0;
float distance_per_pulse = 12.7; // pitch of chain is 25.4mm, CCP interrupt is fired every time a change is detected on the CCP1 pin, so for every rising and falling edge the interrupt is fired.
float current_position = 0;
#int_ccp1
void ccp1_isr(void)
{
if (gate_direction == 2) //Opening
{
current_position = current_position + distance_per_pulse;
}
if (gate_direction == 1) //Closing
{
current_position = current_position - distance_per_pulse;
}
}
//--------------------------------------------------------------------------------
// Read inputs and age previous readings
//--------------------------------------------------------------------------------
void read_inputs_100ms(void)
{
//age previous readings
//read current status of pin
in1_val1 = in1_val2;
in1_val2 = in1_val3;
in1_val3 = input(IP1);
in2_val1 = in2_val2;
in2_val2 = in2_val3;
in2_val3 = input(IP2);
in3_val1 = in3_val2;
in3_val2 = in3_val3;
in3_val3 = input(IP_POS_SENS);
in4_val1 = in4_val2;
in4_val2 = in4_val3;
in4_val3 = input(IP4);
in5_val1 = in5_val2;
in5_val2 = in5_val3;
in5_val3 = input(IP5);
in6_val1 = in6_val2;
in6_val2 = in6_val3;
in6_val3 = input(IP6);
in7_val1 = in7_val2;
in7_val2 = in7_val3;
in7_val3 = input(IP7);
in8_val1 = in8_val2;
in8_val2 = in8_val3;
in8_val3 = input(IP8);
in9_val1 = in9_val2;
in9_val2 = in9_val3;
in9_val3 = input(IP9);
/*
in10_val1 = in10_val2;
in10_val2 = in10_val3;
in10_val3 =input();
*/
in11_val1 = in11_val2;
in11_val2 = in11_val3;
in11_val3 = input(IP11);
in12_val1 = in12_val2;
in12_val2 = in12_val3;
in12_val3 = input(IP10);
}
void read_inputs_1s(void)
{
in13_val1 = in13_val2;
in13_val2 = in13_val3;
in13_val3 = input(IP2);
}
//--------------------------------------------------------------------------------
// Timer 0 is used to poll the inputs
// Routine to service Timer0 interrupt - will occur every 2.048 ms
//--------------------------------------------------------------------------------
#int_timer0 //identifier for start of timer0 interrupt service routine
void timer0interrupt()
{
//----------------------------------------
// 100ms debounce inputs
//----------------------------------------
if (cnt_tmr_100ms == 1) // 32.77 ms
{
read_inputs_100ms();
}
if (cnt_tmr_100ms == 2) // 65.54 ms
{
read_inputs_100ms();
}
if (cnt_tmr_100ms == 3) // 98.30 ms
{
read_inputs_100ms();
}
if (cnt_tmr_100ms < 3)
{
cnt_tmr_100ms ++;
}
else
{
cnt_tmr_100ms = 0;
}
//----------------------------------------
// 1s debounce inputs
//----------------------------------------
if (cnt_tmr_1s == 10) // 327.68 ms
{
read_inputs_1s();
}
if (cnt_tmr_1s == 20) // 655.36 ms
{
read_inputs_1s();
}
if (cnt_tmr_1s == 30) // 983.04 ms
{
read_inputs_1s();
}
if (cnt_tmr_1s < 30)
{
cnt_tmr_1s ++;
}
else
{
cnt_tmr_1s = 0;
}
}
//--------------------------------------------------------------------------------
// Timer 3 is used to provide a 0.5s tick for the counters
// Routine to service Timer1 interrupt - will occur every 0.524s
//--------------------------------------------------------------------------------
#int_timer3 //identifier for start of timer3 interrupt service routine
void timer3interrupt()
{
//--------------------------------------------------------------------------------
if (en_timeout_timer == 1)
{
if (cnt_timeout_timer <= (Config_Timeout_Timer_Max*4))
{
cnt_timeout_timer++;
}
}
}
//--------------------------------------------------------------------------------
// initialise the PIC
//--------------------------------------------------------------------------------
void initialise_pic(void)
{
// Setup ports // 76543210
SET_TRIS_A( 0xC0 ); // 11000000 1 - Input, 0 - Output
SET_TRIS_B( 0xFF ); // 11111111 1 - Input, 0 - Output
SET_TRIS_C( 0xFF ); // 11111111 1 - Input, 0 - Output
//switch off buzzer
output_low(OP_BUZZER_OB);
// Setup Internal Clock
setup_oscillator(OSC_8MHZ);
// Setup Interrupts
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER3);
enable_interrupts(INT_EEPROM);
//CCP1 for Capture mode
setup_ccp1(CCP_CAPTURE_RE);
// Clear the CCP1 interrupt flag before we enable
// CCP1 interrupts, so that we don't get an unwanted
// immediate interrupt (which might happen).
clear_interrupt(INT_CCP1);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
}
void main()
{
initialise_pic (); // initialise the uC
//setup timers
setup_timer_0(RTCC_INTERNAL|RTCC_8_BIT|RTCC_DIV_256);
setup_timer_3(T3_INTERNAL|T3_DIV_BY_8);
//start timers
set_timer0(0);
set_timer3(0);
while(1)
{
if (current_position > 200)
{
gate_direction = 1;
output_high(OP_BUZZER_OB);
}
if (current_position < 0)
{
gate_direction = 2;
output_low(OP_BUZZER_OB);
}
}
}
|
Thanks and I would appreciate any help I can get. This has me stumped at the moment! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19499
|
|
Posted: Thu Jan 27, 2011 10:46 am |
|
|
Comments:
1) Stop using floats!......
Even a subtraction, takes a long time using float arithmetic. Make your position, a 'scaled integer'. (say 1/100th mm steps), and perform this arithmetic in integers. A single 'float' subtraction, takes 283uSec at your clock rate. An int32 subtraction, takes 7.5uSec. Nearly 40* faster.....
2) Rethink your debounce. You are propagating data through some 35 separate bytes, every time through the timer interrupt. Ouch.....
Normal approach here, would be to read the entire port _byte_ as one operation 8 bits in one go. Propagate this, and you have 8* the data being handled for each memory movement. Again huge saving.
3) Buffer all multi-byte variables, when reading them outside the ISR. So:
Code: |
disable_interrupts(GLOBAL);
local_value=ISR_value;
enable_interrupts(GLOBAL);
|
Then do tests on the 'local' value, not the value that is being updated in the ISR. Problem here is if the ISR is called, after one or more bytes has already been handled in the main code, the value seen, is then effectively garbage.
4) Noise. Your fault, has all the symptoms of electrical noise from the motor....
5) Initialise your timers, before enabling their interrupts. Minor, but in some cases can lead to unexpected interrupt triggers.
6) You are not using fast_io, so all your TRIS settings _will_ be overridden, when the code accesses the ports.
Best Wishes |
|
|
gurdeepsembi
Joined: 22 Dec 2010 Posts: 14 Location: UK
|
|
Posted: Thu Jan 27, 2011 11:54 am |
|
|
Hi Ttelmah,
Thanks for all your suggestions. I will definitely implement them. I have not been coding with PICs for very long although I have used other programming languages (mostly on a PC) so thanks for pointing out best practices, much appreciated.
After you pointed out that this is a classic case of noise, I disabled all the other interrupts and used only the CCP interrupt and I was still getting the halting. Furthermore, the halt only happens if the motor is just starting to move (When the most current is used and therefore most noise) so it all seems to point towards the noise on the sensor signal.
I will get a function generator and use that for the signal and see if the 'halting' still happens, if not then that will be proof that the cause is noise and I will have to look at ways of conditioning the signal before it is connected to the PIC. If you have any suggestions on ways to condition the signal then that would be great!
Thanks again. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
Motor problem |
Posted: Thu Jan 27, 2011 5:00 pm |
|
|
I had a brush motor noise problem ~20 years ago.
You could use a stepper (either driven from the PIC or discrete logic).
Verify your software without the noise.
Deal with the motor after that. |
|
|
gurdeepsembi
Joined: 22 Dec 2010 Posts: 14 Location: UK
|
|
Posted: Thu Jan 27, 2011 5:38 pm |
|
|
Hi Mike,
The PCB with the PIC controls an inverter using 3 of the outputs. The inverter then drives the motor.
I think the noise may be coming in from the sensor itself and is probably getting induced into the sensor via radiated emissions from the motor. This is only guesswork at the moment but from my quick tests, as soon as I disconnect the sensor the problem goes away, I want to test this some more tomorrow just to make sure.
I am going to try and put some filtering onto the sensor and see what effect that has but first I want to try a function generator to replicate the sensor signal and if I don't get a problem then it's definitely noise from the sensor.
With your motor, where was the noise getting in from? The power lines for the PIC?
Thanks for your help. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
Motor noise |
Posted: Tue Feb 08, 2011 5:33 am |
|
|
Hi Gurdeepsembi,
I encountered my motor noise problem such a long time ago that most of the detail is lost in the mists of time. At that stage I'd not even heard of PICs.
I was using pyro-electric sensors to detect 10-micron wavelength radiation with a response down to DC. The idea was to chop the radiation reaching the sensor with a bow-tie shaped rotating blade, amplify the sensor signal, then use a phase sensitive rectifier to recover a DC signal.
The DC brush motor I was using created noise which swamped the sensor signal. A series of quick tests yielded little improvement. Rather than waste more time I opted for a stepper, which turned out to be simpler and cheaper.
With the benefit of more experience I maybe could re-visit the problem.
First task I would attack the noise at source. Conducted noise in the cables with LCR networks, and radiated by shielding the entire motor. Shielding has to be fairly complete. Small holes or slits can leak field, which readily converts to conducted noise when it encounters cables.
Tracking this kind of noise is not straightforward; you have to be wary when interpreting the signals observed on either an oscilloscope or spectrum analyser.
Best of luck. |
|
|
|
|
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
|