|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jul 21, 2006 1:42 am |
|
|
Quote: | sometimes, after an interrupt, the ADC oversampling accumulator are corrupted. |
If you modify an int16 or int32 global variable inside an interrupt service
routine (isr) and then you read it outside of the isr, you must turn off
interrupts while you're reading the variable. That's because to read
a 32-bit variable, the PIC must execute 8 instructions. It can't read
it in just one instruction. If an interrupt occurs between any of those
8 instructions, the int32 value will be changed. Then when control
returns to the main() code, and the remaining bytes to be read may
be different than they were before the interrupt. Your program will
appear to have a corrupted variable.
Here is the .LST file code which shows the 8 instructions required to read
the global int32 variable:
Code: |
0000 00390 .... temp = isr_value;
0055 082C 00391 MOVF 2C,W
0056 00B0 00392 MOVWF 30
0057 082B 00393 MOVF 2B,W
0058 00AF 00394 MOVWF 2F
0059 082A 00395 MOVF 2A,W
005A 00AE 00396 MOVWF 2E
005B 0829 00397 MOVF 29,W
005C 00AD 00398 MOVWF 2D
|
To fix the problem, add the lines shown in bold below:
Quote: |
#include <16F877.h>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
int32 isr_value;
#int_timer0
void timer0_isr(void)
{
isr_value++;
}
//======================
void main()
{
int32 temp;
while(1)
{
disable_interrupts(GLOBAL);
temp = isr_value;
enable_interrupts(GLOBAL);
}
} |
|
|
|
Murph
Joined: 20 Jul 2006 Posts: 4
|
|
Posted: Fri Jul 21, 2006 3:25 am |
|
|
Thank you for so fast reply.
In a old version of code I disabled the interrupt during the accumulator exchanging values but the error still happened
In a successive version, where the oversampling was entirely made out of the isr routine, again the problem happened
Now, I discard the oversampling result if a interrupt occurred and all appear right, but this is not the right manner
Murph |
|
|
Murph
Joined: 20 Jul 2006 Posts: 4
|
|
Posted: Mon Jul 24, 2006 8:48 am |
|
|
Hallo PCM programmer,
I prepared some lines of code generating the bug.
After the initialization, the main() function repeatedly call AdcOversampling() that increases and checks the variable acc_sample. There is yet the interrupt routine that stops the execution and makes only a delay.
If you run this code and send a sequence of interrupt you will find the AdcOversampling() end to work because the value of acc_sample exceeds the maximum possible value.
Where is the problem?
Thanks for your aid
Murph
Code: | #include <16F819>
#fuses XT, NOWDT, PUT, NOBROWNOUT, NOLVP, NOWRT, NOCPD, NOPROTECT
#use delay(clock = 4000000)
// Special Function Registers
#byte STATUS_REG = 0x03
#byte OPTION_REG = 0x81
#byte INTCON_REG = 0x0B
// Flag interrupt
#bit INT_EXT_FLAG = INTCON_REG.1
// Definizioni IO
#byte PORT_A = 0x05
#byte PORT_B = 0x06
#byte TRIS_A = 0x85
#byte TRIS_B = 0x86
#bit SYSTEM_LED = PORT_B.4
#bit PIN_DEBUG = PORT_B.5
void SystemInit(void);
void AdcOversampling(void);
void Rs232IsrRx(void);
void main(void);
// ****************************************************************************
void SystemInit(void)
{
TRIS_A = 0b11100001;
PORT_A = 0b00000000;
TRIS_B = 0b00000011;
PORT_B = 0b00000000;
// RB0 = External Interrupt on Falling Edge
ext_int_edge(H_TO_L);
enable_interrupts(INT_EXT);
// Usa IO veloci
#use fast_io(A)
#use fast_io(B)
// Abilita interrupt
enable_interrupts(GLOBAL);
}
// ****************************************************************************
void AdcOversampling(void)
{
unsigned int16 num_sample;
unsigned int32 acc_sample;
acc_sample = 0;
for (num_sample = 1; num_sample < 16384; num_sample++)
{
acc_sample = acc_sample + 0x1FF;
if (acc_sample > 10000000)
{
for(;;)
{
SYSTEM_LED = ~SYSTEM_LED;
delay_ms(50);
}
}
}
}
// ****************************************************************************
#INT_EXT
void Rs232IsrRx(void)
{
// Disabilita interrupt
disable_interrupts(GLOBAL);
INT_EXT_FLAG = FALSE;
PIN_DEBUG = TRUE;
delay_ms(10);
PIN_DEBUG = FALSE;
enable_interrupts(GLOBAL);
}
// ****************************************************************************
void main(void)
{
SystemInit();
for (;;)
{
// Acquisisce campione con oversampling
AdcOversampling();
// Lampeggio LED sistema
SYSTEM_LED = ~SYSTEM_LED;
}
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jul 24, 2006 12:10 pm |
|
|
Remove the lines shown in bold below. They are not necessary, and
the last one can cause the program to crash. Never do this.
Quote: |
#INT_EXT
void Rs232IsrRx(void)
{
// Disabilita interrupt
disable_interrupts(GLOBAL);
INT_EXT_FLAG = FALSE;
PIN_DEBUG = TRUE;
delay_ms(10);
PIN_DEBUG = FALSE;
enable_interrupts(GLOBAL);
} |
This thread explains why the enable_interrupts(GLOBAL) line can cause
the program to crash:
http://www.ccsinfo.com/forum/viewtopic.php?t=26117 |
|
|
Murph
Joined: 20 Jul 2006 Posts: 4
|
|
Posted: Tue Jul 25, 2006 6:26 am |
|
|
Magic PCM Programmer,
now the code works.
This is my first work with PIC micro and I do the mistake to believe, cause the simple structure of the device, that the programmer must to say to uP everything what to do.
Thank you
Murph |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Jul 25, 2006 6:49 am |
|
|
Just one more note on your code Code: | INT_EXT_FLAG = FALSE; |
You are aware that by default the compiler will clear the interrupt flag at the end of the interrupt routine also? Maybe not a problem in your application but at least double work.
You can add the NOCLEAR directive to the #int_ext line to prevent the compiler from clearing the interrupt, or remove your line from the program for easier reading. |
|
|
|
|
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
|