Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] PIC16F8887 Code Compiled with HI-TECH C Compiler from PIC16 - Cannot catch INT

Status
Not open for further replies.

zia.newversion

Member level 5
Joined
Mar 28, 2010
Messages
83
Helped
1
Reputation
2
Reaction score
1
Trophy points
1,288
Location
Pakistan
Activity points
2,188
PIC16F887 Code Compiled with HI-TECH C Compiler from PIC16 - Cannot catch INT

Hi there... I was experimenting with the 887 µC, and to test myself, I tried to play with the A2D converter. It could be done in a very simple manner, but I thought it'd be good to do it through interrupts. This program is supposed to take a sample on AN0 every 1 second and calculate a running average of last 8 samples.

For that, I programmed the timer to generate an interrupt every 128µs and at every INT (if it is indeed a TMR0 INT), a counter will be incremented. After the counter has passed 7812 (which would mean 1 second has passed), it'd call the AD conversion routine which is supposed to send the GO signal to AD converter module. After that the current interrupt service ends. The program resumes normal execution until the A2D-Conversion Complete interrupt arrives (which happens within 4µs or 8 instruction cycles after the TMR0 INT has triggered the A2D conversion).

I hope you are with me until here. Right... So, after the conversion is complete, another INT is generated and the ISR processed the interrupt (but this time it doesn't qualify for a TMR0 interrupt, but a AD interrupt instead). So the program goes on and takes the AD result and puts it into an array of 8 values (which is basically a stack for last 8 AD results) then calculates the average and then converts the final result to it's decimal equivalent. then every decimal place to its BCD equivalent and then sends it to the appropriate nibble of the each of PORTB and PORTC.

Here is the code, if you will:

Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// FOSC=8MHz
#include <htc.h>
__CONFIG(0x23C0);
__CONFIG(0x3EFF);
 
void ISR_TMR0();
void ISR_ADCONV();
void interrupt ISR();
void StartADConv();
void Out();
const double REZ            = 0.004887585533; //5V/1023
unsigned int COUNT          = 0x00;
unsigned int ADRES          = 0x00;
unsigned int AVG            = 0x00;
unsigned int READING[8]     = {0,0,0,0,0,0,0,0};
 
void main()
{
    TRISA   = 0x01;     // PORTA is OP
    TRISB   = 0x00;     // PORTB is OP
    TRISC   = 0x00;     // PORTC is OP
    TRISD   = 0x00;     // PORTD is OP
    TRISE   = 0x00;     // PORTE is OP
    PORTA   = 0x00;     // Clear PORTA
    PORTB   = 0x00;     // Clear PORTB
    PORTC   = 0x00;     // Clear PORTC
    PORTD   = 0x00;     // Clear PORTD
    PORTE   = 0x00;     // Clear PORTE
    ANSEL   = 0x01;     // RA0/AN0 as analog
    ANSELH  = 0x00;     // Everything else is digital
    OPTION  = 0x41;     // 1:4 Prescalar assigned to TMR0
    ADCON0  = 0x80;     // ADCON Freq = FOSC/32
    PEIE    = 1;        // Peripheral INT enabled
    T0IE    = 1;        // TMR0 INT enabled
    ADIE    = 1;        // ADCON INT enabled
    ADFM    = 1;        // ADCON format right justified
    ei();               // Enable the interrupts
    while(1)
    SLEEP();            // Sleep untill interrupt arrives
}
 
void interrupt ISR()
{
    if(T0IF)            // If INT was caused by TMR0
    ISR_TMR0();         // Call TMR0 INT module
    else if(ADIF)       // If INT was caused by ADCON
    ISR_ADCONV();       // Call ADCONV INT module
    Out();              // Whether or not if was successful
                        // --- do call the output module
    return;             // Get the f*** outta here
}
 
void ISR_TMR0()
{
    COUNT++;            // Increment the counter
    if(COUNT>7812)      // 0.5us x 256 x 7812 ~= 1s
    StartADConv();      // If 1s passed, start conv.
    T0IF = 0;           // Don't forget to clear INT flag
    return;             // Get the f*** outta here
}
 
void StartADConv()
{
    ADON    = 1;        // Turn AD mod ON
    GODONE  = 1;        // Start conversion
    return;
}
 
void ISR_ADCONV()
{
    ADRES = (ADRESH * 0x100) + ADRESL;  // Combine result chars to form int
    unsigned char i;
    for (i=7; i>0; i--)                 // Stack up last 7 readings
    {
        READING[i] = READING[i-1];
    }
    READING[0] = ADRES;                 // Put latest reading to 8th
    unsigned int s;
    s = 0;
    for (i=7; i>=0; i--)                // Sum all readings
    {
        s = s + READING[i];
    }
    AVG = s/0x08;                       // Divide sum by 8
}
 
void Out()
{
    unsigned int Op     = AVG*REZ*1000; // Convert ADC reading to corresponding
                                        // --- analog value and make it an int
    unsigned char D1    = Op/1000;      // Place before decimal
    Op = Op%1000;
    unsigned char D2    = Op/100;       // Place after decimal
    Op = Op%100;
    unsigned char D3    = Op/10;        // 2 places after decimal
    unsigned char D4    = Op%10;        // 3 places after decimal
 
    PORTB = D1;                 // D1 on first nibble of PORTB
    PORTB = PORTB+D2*0x10;      // D2 on second nibble of PORTB
    PORTC = D3;                 // D3 on first nibble of PORTC
    PORTC = PORTC+D3*0x10;      // D4 on first nibble of PORTC
}



I commented so it'd be documented and I wouldn't forget what piece of code does what. I guess these comments shall help you understand the code as well.

Now here is the problem:
I used Proteus ISIS to build the circuit and loaded the program. But the code 7-segment displays keep displaying 0000... I tried to investigate and loaded the program in MPLAB SIM, then executed the "Animate" command, to watch where it's going... After an awful lot of time, I came to know that it keeps sleeping. It is stuck in the SLEEP() macro and interrupt never comes!

I guess I must have done some mistakes in the conversion and display code as well, but how'd I know if it doesn't get into the interrupt and starts executing instructions other then SLEEP...

As far as I could proof-read, my code sees alright. Could someone else, who has worked with HI-TECH compiler before, help me out there? I shall be duly grateful.

PS. I have attached the ZIP archive containing the MPLAB Project, C Code file and Proteus DSN file.
 

Attachments

  • Un.Ejemplo.zip
    144.1 KB · Views: 100
Last edited:

Re: PIC16F887 Code Compiled with HI-TECH C Compiler from PIC16 - Cannot catch INT

Hi,
You have to start a first conversion to kick the first interrupt, just before your main while(1) loop.
also be carefull with (ADRESH * 0x100) + ADRESL. Depending on the compiler, (ADRESH * 0x100) may give a 8 bits result = 0x00. You should cast ADRESH to int before multiplying.
Regards,
 

Hey! Thanks for the reply.
About that (ADRESH * 0x100) + ADRESL, it's done. And by the way, HTC performs 8x8 multiplication with a 16-bit answer field software multiplier in assembly, so that wouldn't be a problem anyway. But that was a nice call.
About the interrupt kicking, I'm not so sure. Why do I have to do the conversion to kick the interrupt? The TMR0 INT is enabled anyway, and the program is supposed to perform the first conversion after the first timeout occurs. I do have to trigger forst conversion to kick the AD INT but the TMR0 interrupt should be services after the timeout in any case.
 

I assume you have disabled the watchdog timer, it's hard to tell when you set your config reg with a magic number, better to use the defined symbols.

You don't need a return in the interrupt routine, that is an error.
The compiler generates a return from interrupt instruction, which is different to a return.

Look at the disassembler file in Mplab to see what the compiler has done there.
 

Hey! Thanks for the reply.
About that (ADRESH * 0x100) + ADRESL, it's done. And by the way, HTC performs 8x8 multiplication with a 16-bit answer field software multiplier in assembly, so that wouldn't be a problem anyway. But that was a nice call.
About the interrupt kicking, I'm not so sure. Why do I have to do the conversion to kick the interrupt? The TMR0 INT is enabled anyway, and the program is supposed to perform the first conversion after the first timeout occurs. I do have to trigger forst conversion to kick the AD INT but the TMR0 interrupt should be services after the timeout in any case.
You are right, I have not analyzed the code enough.
You don't read enough of the DS: Page 74 : "Note: The Timer0 interrupt cannot wake the processor from Sleep since the timer is frozen during Sleep. "
I use the ADC interrupt to wake-up, using low speed internal clock, using the ADC interrupt for counting time too. This is not very accurate but enough in most cases. You may discard ADC results and keep only 1/n conversions.
You have to start the first conversion once before the while(1) main loop, then restart it each time in the ISR, after clearing the ADIF (mandatory: you forgot it). You should update your display in the main loop, just after the SLEEP().
AD conversion in SLEEP mode with the low speed internal clock is the best way to make low noise conversions since many things are stopped during SLEEP.
Regards,
 
I assume you have disabled the watchdog timer, it's hard to tell when you set your config reg with a magic number, better to use the defined symbols.

You don't need a return in the interrupt routine, that is an error.
The compiler generates a return from interrupt instruction, which is different to a return.

Look at the disassembler file in Mplab to see what the compiler has done there.

I have seen and used that technique in a few other programs as well. I guess the compiler just ignored the C "return" and generates a RETFIE in the disassembly anyway...

And yeah, you're right... I have disabled WDT.



---------- Post added at 19:56 ---------- Previous post was at 19:53 ----------

You are right, I have not analyzed the code enough.
You don't read enough of the DS: Page 74 : "Note: The Timer0 interrupt cannot wake the processor from Sleep since the timer is frozen during Sleep. "
I use the ADC interrupt to wake-up, using low speed internal clock, using the ADC interrupt for counting time too. This is not very accurate but enough in most cases. You may discard ADC results and keep only 1/n conversions.
You have to start the first conversion once before the while(1) main loop, then restart it each time in the ISR, after clearing the ADIF (mandatory: you forgot it). You should update your display in the main loop, just after the SLEEP().
AD conversion in SLEEP mode with the low speed internal clock is the best way to make low noise conversions since many things are stopped during SLEEP.
Regards,

That indeed works fine... I did it and it worked very fine.
However, I tried another, rather goofy method... I just replaced SLEEP() with a asm("nop")... Hehehe! That did the magic as well. Cheers... :)

---------- Post added at 19:59 ---------- Previous post was at 19:56 ----------

And by the way, I think the clock should keep running during SLEEP().
Does that happen only with this range of PIC16s or is this the case with everything that is available?
If that's universal, somebody's gotta tell Microchip about it. :p
 

And by the way, I think the clock should keep running during SLEEP().
Main clock is always stopped during sleep. You see "sleep" means "sleep": try to do nothing and keep cool (minimize power). If you want the processor to do something, that's not sleeping...
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top