|
|
View previous topic :: View next topic |
Author |
Message |
dgi
Joined: 07 Jan 2004 Posts: 11 Location: Philadelphia, USA
|
global interrupts disabled mysteriously |
Posted: Wed Dec 06, 2006 10:20 am |
|
|
I am using CCS 3.249 (having read that it is the most stable) to program a PIC18F2420, crystal clocked at 20 Mhz.
My program sets up a single periodic interrupt to perform a high priority polling task, and enables it like this:
Code: |
setup_timer_2 ( T2_DIV_BY_1, 250, 5);
// At 20mhz, the timer will increment every 200ns,
// will overflow every 50us,
// and will interrupt every 250us.
// Dividing by 4 gives a millisecond count
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
|
In my main() loop, I run a "user interface" which reads the UART and parses various commands. My problem is that after running for several minutes, the interrupt is somehow disabled although the user interface keeps working. By checking the INTCON register like this:
Code: |
#BIT GIE = 0xFF2.7
#BIT TMR2E = 0xF9D.1
|
and then reading the values of these bits, I find that what has happened is somehow the global interrupt enable "GIE" has been permanently disabled. However, there is no code anywhere in my program that disables global interrupts. In other words, I never call "disable_interrupts". Setting GIE back to 1 from the main() loop starts the interrupt again. It seems that either the compiler or the PIC itself is disabling interrupts (this happens automatically during execution of the interrupt) and forgetting to enable them again.
Has anyone ever seen something like this, or, do you know of any code that might have the unexpected side-effect of disabling global interrupts? I have a strncmp() in my program to parse commands, which is the only exotic thing besides simple integer arithmetic, checksumming a command line, and some printf()s. |
|
|
Ttelmah Guest
|
|
Posted: Wed Dec 06, 2006 10:23 am |
|
|
What is in your interrupt handler?.
The interrupts are disabled by the hardware, when the handler is called. If this has a problem, and overruns, or jumps back into the main, then you will see the effect described.
Best Wishes |
|
|
dgi
Joined: 07 Jan 2004 Posts: 11 Location: Philadelphia, USA
|
|
Posted: Wed Dec 06, 2006 1:16 pm |
|
|
Thank you Ttelmah for the advice. I haven't found anything wrong in the interrupt yet, but I may have found the problem in main(). My printf actually itself calls printf:
Code: |
VOID AppendChecksum(char nextChar) // used for OUTGOING messages
{
// call \n for a line break without ETX
// use \r to end with 03 (ETX) character
IF ((nextChar == '\n') || (nextChar == '\r'))
{
printf(send_uart, " %X\n\r", OutgoingChecksumRunningTotal);
OutgoingChecksumRunningTotal = 0;
if (nextChar == '\r')
send_uart(3); // ETX = \x03
}
ELSE
{
OutgoingChecksumRunningTotal += nextChar;
send_uart(nextChar);
}
}
main()
...
printf (AppendChecksum, "uptime %lu seconds\r",uptime);
|
Could this somehow result in disabling global interrupts? Is it bad to use recursive function calls? The code for printf is not included in the LST file so it's hard to trace what the compiler is doing. |
|
|
Ttelmah Guest
|
|
Posted: Thu Dec 07, 2006 6:20 am |
|
|
Probably not directly.
Though recursion (which this is), is not supported, printf itself is normally coded as 'inline', and in this case, the printf being called uses a different output routine to the caller, so it might actually work!.
However though it'll probably crash the code, I'd not expect it to disable interrupts.
The way to do this, is to not use printf in the output routine. Just output the two hex digits yourself.
The other things that would cause the problem, is if printf is being called in the interrupt handler, since then interrupts will be disabled in printf...
Best Wishes |
|
|
davekelly
Joined: 04 Oct 2006 Posts: 53 Location: Berkshire, England
|
|
Posted: Thu Dec 07, 2006 6:48 am |
|
|
I am using 3.249 also, for the 18F242
I have also experienced this problem, and am still trying to track it down. |
|
|
dgi
Joined: 07 Jan 2004 Posts: 11 Location: Philadelphia, USA
|
|
Posted: Thu Dec 07, 2006 9:24 am |
|
|
Thank you for the guidance, folks.
Working or not, I removed all recursive function calls as described above. However, I still have the problem of interrupts getting disabled after several minutes of the program running.
I still don't know why the global interrupt enable bit gets cleared in the middle of execution. I don't know whether it is a problem in the interrupt, in the main program, or in the compiler. But here is what I have done so far, which seems to help. I instrumented my code so that I can figure out exactly where GIE get disabled:
Code: |
#BIT GIE = 0xFF2.7
void main()
{
intproblem = 0; // means no problem
while(true)
{
dosomething;
if (!(GIE) && !(intproblem) ) intproblem = (__line__ % 256);
dosomethingelse;
if (!(GIE) && !(intproblem) ) intproblem = (__line__ % 256);
dosomething;
if (!(GIE) && !(intproblem) ) intproblem = (__line__ % 256);
/// etc ...
|
Then, by checking the value of intproblem, I can find out where GIE first got cleared.
So far, GIE has been getting cleared during various string parsing operations such as strncmp() and printf(). Each time it happens, I rewrite the offending code to use different functions. Now my code has been running for 12 hours without a problem. However it is very unsatisfying to fail to find the source of the problem and positively fix it.
Let us know, Davekelly, if you learn what caused your problem. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Dec 07, 2006 11:27 am |
|
|
Quote: | However, there is no code anywhere in my program that disables global interrupts |
There might be code in the CCS library functions that does this.
My advice is to create a complete .LST file and search it for all
references to register FF2. Look for a line of code like this:
This is clearing the GIE bit in the INTCON register.
To generate a full .LST file, edit the 18F2420.H file and comment out
the #nolist statement at the start of the file. Example:
When you do that, you will see the ASM code for library functions
that are normally hidden from the .LST file. |
|
|
Spiteful
Joined: 09 Jul 2007 Posts: 2
|
|
Posted: Mon Jul 09, 2007 7:54 am |
|
|
I have same problem
CCS v3.236
PIC12F629
defined:
unsigned int8 TRISIO;
#locate TRISIO=0x85
#bit TRISIO5=0x85.5
#bit TRISIO4=0x85.4
#bit TRISIO3=0x85.3
#bit TRISIO2=0x85.2
#bit TRISIO1=0x85.1
#bit TRISIO0=0x85.0
compilled:
.................... // pin A5 as input
.................... TRISIO5 = 1;
0345: BSF 03.5
0346: BSF 05.5
0347: BCF 03.5
0348: CLRF 2E
0349: BTFSC 0B.7
034A: BSF 2E.7
034B: BCF 0B.7
....................
when 2E is:
02E @INTERRUPT_AREA
What can i do with it? Why compiller clearing 2E and test GIE? |
|
|
Ttelmah Guest
|
|
Posted: Mon Jul 09, 2007 8:42 am |
|
|
0x2E, contains a value used by the compiler to reflect the interrupt status.
The compiler is checking if interrupts are enabled, and changing the value of bit 7 in this register to reflect this.
On these chips, if you change the TRIS, it can result in an interrupt on change being triggered.
This not 'the same problem'. The original poster had the GIE bit being changed. In your case, it is only being tested. In your case, there may well be some logic to this (though you would have to look at where 0x2E is used to work out the implications).
Best Wishes |
|
|
Spiteful
Joined: 09 Jul 2007 Posts: 2
|
|
Posted: Mon Jul 09, 2007 11:47 pm |
|
|
Thanks. |
|
|
nurquhar
Joined: 05 Aug 2006 Posts: 149 Location: Redditch, UK
|
|
Posted: Thu Mar 15, 2012 5:49 am |
|
|
Compiler v4.130
I found this post after having similar problem with printf() turning off the global interrupt enable.
In my case I am using printf() with my own putc() to handle a data output from a timer interrupt function. My putc() accesses some 8 bit ints used within the timer isr(), this should be safe because the accesses will be atomic. CCS, as described above turns off GIE in printf() prior to calling my putc() because it thinks I will be changing a variable used with an interrupt. Although in my case this is misguided and not neccessary the compiler does not generate any Warnings to give me a clue as to why my program hangs unexpectedly.
If CCS is smart enough to put the GIE off when it sees something being used inside AND outside the interrupt (even though I don't want it) it could least generate a warning
Even better it could have looked at the access and decided they are 8bit and thererfor safe. THus no need to turn GIE off. Perhaps this is asking too much of the compiler.
I am fixing my code by using a somewhat brutel GIE = 1 inside my putc().
This is my code and build info :
Code: |
#ignore_warnings NONE
#include <12F1822.h>
#device adc=8
//////// Program memory: 2048x14 Data RAM: 112 Stack: 16
//////// I/O: 12 Analog Pins: 8
//////// Data EEPROM: 256
//////// C Scratch area: 20 ID Location: 8000
//////// Fuses: LP,XT,HS,RC,INTRC_IO,ECL,ECM,ECH,NOWDT,WDT_SW,WDT_NOSL,WDT
//////// Fuses: PUT,NOPUT,NOMCLR,MCLR,PROTECT,NOPROTECT,CPD,NOCPD,NOBROWNOUT
//////// Fuses: BROWNOUT_SW,BROWNOUT_NOSL,BROWNOUT,CLKOUT,NOCLKOUT,NOIESO
//////// Fuses: IESO,NOFCMEN,FCMEN,WRT,WRT_EECON400,WRT_EECON200,NOWRT
//////// Fuses: PLL_SW,PLL,NOSTVREN,STVREN,BORV25,BORV19,DEBUG,NODEBUG,NOLVP
//////// Fuses: LVP
////////
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOCPD //No EE protection
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOMCLR //Master Clear pin enabled
//#FUSES MCLR //Master Clear pin enabled
#FUSES PUT //Power Up Timer
#FUSES BROWNOUT //Reset when brownout detected
#FUSES NOIESO //Internal External Switch Over mode disabled
//#FUSES IESO //Internal External Switch Over mode enabled
#FUSES NOFCMEN //Fail-safe clock monitor enabled
//#FUSES NODEBUG //No Debug mode for ICD
#FUSES DEBUG //Debug mode for ICD
//#FUSES WDT_NOSL
#FUSES NOWRT //Program Memory Write Protected
//#FUSES PLL // PLL Enabled
#FUSES PLL_SW // PLL under software control, disabled
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES BORV24 //Brownout reset at 2.5V
#FUSES NOCLKOUT //Output clock on OSC2
#use delay (clock=32000000)
#use fast_io(A)
#use rs232(UART1, baud=9600,parity=N,bits=8,stop=1)
#byte APFCON = getenv("SFR:APFCON")
#bit RXDTSEL = getenv("bit:RXDTSEL")
#bit TXCKSEL = getenv("bit:TXCKSEL")
#byte IOCAP = getenv("SFR:IOCAP")
#byte IOCAN = getenv("SFR:IOCAN")
#byte IOCAF = getenv("SFR:IOCAF")
#bit GIE = getenv("bit:GIE")
#byte INTCON = getenv("SFR:INTCON")
#byte TMR0 = getenv("SFR:TMR0")
// RTS Monitor Input
#define RTS PIN_A2
#define UART_TX PIN_A4
#define UART_RX PIN_A5 // PC TX Pin
// Tris Reg A --543210 - 0=O/P 1==I/P
#define TRIS_A 0b00001110
#define IOCA 0b00100100
// Pulse width modulation communication
int8 pwmc1 ; // Control/Status
int8 pwmc_ch ; // Data
int8 pwmc_cnt ; // Data Count
int timer0_isr_cnt ;
int pwmc_putc_cnt ;
int putc_intcon ;
#define PWMC1_IO 0 // State of TX op
#define PWMC1_MARK 1 // Put marker pulse shape
#define PWMC1_D0 2 // Put data bit pulse shape
#define PWMC_TM 64 // No of counter ticks
#define PWMC_TMR1X (PWMC_TM)
#define PWMC_TMR2X (2 * PWMC_TM)
//#define PWMC_POS_LOGIC
#ifdef PWMC_POS_LOGIC
#define PWMC_IO_LOW() output_low(UART_TX)
#define PWMC_IO_HIGH() output_high(UART_TX)
#define PWMC_IO_STATE() input_state(UART_TX)
#else
#define PWMC_IO_LOW() output_high(UART_TX)
#define PWMC_IO_HIGH() output_low(UART_TX)
#define PWMC_IO_STATE() !input_state(UART_TX)
#endif
#INT_TIMER0
void timer0_isr()
{
// Debug - keep track of ints
timer0_isr_cnt++ ;
if(PWMC_IO_STATE()) {
// Set the timer according to bit pattern
if(bit_test(pwmc1, PWMC1_MARK)) {
TMR0 -= PWMC_TMR1X ;
} else if(bit_test(pwmc1, PWMC1_D0)) {
TMR0 -= PWMC_TMR2X ;
} else {
TMR0 -= PWMC_TMR1X ;
}
bit_clear(pwmc1, PWMC1_IO) ;
PWMC_IO_LOW() ;
} else {
if(bit_test(pwmc1, PWMC1_MARK)) {
TMR0 -= PWMC_TMR1X ;
} else if(bit_test(pwmc1, PWMC1_D0)) {
TMR0 -= PWMC_TMR1X ;
} else {
TMR0 -= PWMC_TMR2X ;
}
bit_set(pwmc1, PWMC1_IO) ;
PWMC_IO_HIGH() ;
// Get next bit
if(pwmc_cnt) {
bit_clear(pwmc1, PWMC1_MARK) ;
if(pwmc_ch & 0x01) {
bit_set(pwmc1, PWMC1_D0) ;
} else {
bit_clear(pwmc1, PWMC1_D0) ;
}
shift_right(&pwmc_ch,1,0) ;
pwmc_cnt-- ;
} else {
bit_set(pwmc1, PWMC1_MARK) ;
}
}
}
void pwmc_putc(int ch)
{
// Debug - keep track of putc calls
pwmc_putc_cnt++ ;
// FIX - Force GIE back on after printf turned it off !!
//GIE = 1 ;
// Debug - keep copy of GIE
putc_intcon = INTCON ;
// Wait for empty
while(pwmc_cnt) ; // <= View of variable used in the interrupt, not a problem
pwmc_ch = ch ; // <= Change of variable used in the interrupt, causes compiler to clear GIE in printf, no warning given
pwmc_cnt = 8 ; // <= Change of variable used in the interrupt, causes compiler to clear GIE in printf, no warning given
}
/*** NOT USED YET
void pwmc_puts(char *s)
{
char *p;
for (p = s; *p != '\0'; p++)
pwmc_putc(*p) ;
pwmc_putc('\r') ;
pwmc_putc('\n') ;
}
***/
void pwmc_init()
{
PWMC_IO_HIGH() ;
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256) ;
}
void pwmc_start()
{
PWMC_IO_HIGH() ;
TMR0 = 256 - PWMC_TMR1X ;
clear_interrupt(int_timer1);
enable_interrupts(int_timer0);
bit_set(pwmc1, PWMC1_IO) ; // Default to high
bit_set(pwmc1, PWMC1_MARK) ; // Default to mark bit
bit_clear(pwmc1, PWMC1_D0 ) ; // Default to '1' bit
pwmc_cnt = 0 ;
}
void main()
{
int8 ch ;
// Switch to internal 32Mhz Osc, ie 4xPLL x 8Mhz = 32Mhz
setup_oscillator(OSC_8MHZ | OSC_NORMAL | OSC_PLL_ON);
setup_comparator(NC_NC);
// Set port directions for fast I/O
set_tris_a(TRIS_A) ;
// Set pullups RA5(PC_RX)
port_a_pullups(0x20) ;
// Keep PC TX PIN High
output_high(UART_RX) ;
timer0_isr_cnt = 0 ;
pwmc_putc_cnt = 0 ;
enable_interrupts(global);
pwmc_init() ;
pwmc_start() ;
// To HW UART
printf("Start\r") ;
ch = 0x41 ;
while(1) {
printf(pwmc_putc, "Hello World %u ", ch) ;
delay_ms(10) ;
}
}
|
Executing: "C:\Program Files\PICCv4\Ccsc.exe" +FM "H-BridgeManager01.c" +DF +LN +T +A +M -Z +ICD +Y=9 +EA
>>> Warning 203 "H-BridgeManager01.c" Line 226(1,1): Condition always TRUE
Memory usage: ROM=22% RAM=14% - 31%
0 Errors, 1 Warnings.
Build Successful.
Loaded N:\Projects\Nusoft\2WireComms\PIC\H-BridgeManager01.cof.
BUILD SUCCEEDED: Thu Mar 15 11:19:48 2012
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Mar 15, 2012 11:53 am |
|
|
Seriously, 'of course' it has to disable interrupts. I'm amazed it doesn't just explode!....
You do realise that the routine you use in a printf, is called repeatedly for every character in the output?. Your 'pwmc_putc' routine will be called up to 16 times, with three of the calls depending on the value of 'ch'. Each call then changes the value of pwmc_ch, inside itself, and the routine must ensure that variables don't change between successive calls.
If you want to send text characters to the ISR, use sprintf to generate a string, and transfer these yourself. |
|
|
nurquhar
Joined: 05 Aug 2006 Posts: 149 Location: Redditch, UK
|
|
Posted: Fri Mar 16, 2012 1:30 am |
|
|
Dear Ttelmah
Yes you are correct pwmc_putc does alter the pwmc_ch. But the code has been carefully designed ! pwmc_ch is only altered in the interrupt when pwmc_cnt IS NOT zero. pwmc_putc only alters it when pwmc_cnt IS zero. |
|
|
nurquhar
Joined: 05 Aug 2006 Posts: 149 Location: Redditch, UK
|
|
Posted: Fri Mar 16, 2012 4:27 am |
|
|
Thinking some more about using printf() inconjuction with a user supplied putc(). It seems there must be many many applications that use it to push data into a FIFO buffer that then use an interrupt handler to pop the data for output to the hardware.
This is a pretty standard scenario for an interrupt driven serial port transmitter, a model I first used on Z80's back in the 80's. For the buffer you have a head & tail pointer and a contents count. The interrupt takes data out at the head pointer and reduces the count. The putc() puts data in at the tail pointer and increases the count. The count is used for the buffers tests, ie buffer full, empty or space left.
If you can use an atomic operation operation for the buffer contents count there should be no need to disable interrupts for the buffer push in the putc() function. If you were using a 16bit buffer count on an 8bit CPU then you would have to disable interrupts whist the count is being changed in putc() to prevent the interrupt "seeing" a half changed value.
My code above effectivly uses a this model but with a 1 byte buffer size.
My beef with the CCS complier is that it does'nt warn that it is disabling the interrupts prior to entering the users code.
What would be nice to have is way of telling the compiler that it does not need to take responsibility of checking if a certain variable is used inside and outside an interrupt handler and disable interrupts to protect it.
Some new preprocessor directive like this might do it.
#INT_IGNORE
int8 pwmc_ch ;
You never know CCS might be looking for something to do |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri Mar 16, 2012 4:48 am |
|
|
The point is the compiler can't tell this. It looks for simple things. "can this routine modify something that is inside another routine called in the interrupt". If answer is 'yes', is _has_ to protect the code.
You can write the code so there is no possibility of such a crossover, and if so, then the compiler will not implement it's protection. |
|
|
|
|
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
|