[PIC] Help with Zero Crossing Detector with the 16F877A code on MPLAB XC8

Status
Not open for further replies.

sulfur101

Newbie level 6
Joined
Jul 20, 2022
Messages
13
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
77
so i have found this code and i like it but when i plug it into MPLAB XC8 it does not work I've tried looking everywhere and no luck and i am stuck can some one please tell me what i am doing wrong. also can yall help me adding a analog pin to change the phase angle because i am stuck i dont code on a regular basis so i just need help.

this was coded on mikro C but i am useing a PICKIT 4 and it dosent read so i have been trying to convert it to MPLABS XC8 but still nothing i am useing the PIC16F877A In C code and i have no idea what i am doing please help



Code:
unsigned char FlagReg;
sbit ZC at FlagReg.B0;

void interrupt(){
     if (INTCON.INTF){          //INTF flag raised, so external interrupt occured
        ZC = 1;
        INTCON.INTF = 0;
     }
}

void main() {
     PORTB = 0;
     TRISB = 0x01;              //RB0 input for interrupt
     PORTA = 0;
     ADCON1 = 7;                 //Disable ADC
     TRISA = 0xFF;                                //Make all PORTA inputs
     PORTD = 0;
     TRISD = 0;                 //PORTD all output
     OPTION_REG.INTEDG = 0;      //interrupt on falling edge
     INTCON.INTF = 0;           //clear interrupt flag
     INTCON.INTE = 1;           //enable external interrupt
     INTCON.GIE = 1;            //enable global interrupt

     while (1){
           if (ZC){ //zero crossing occurred
              delay_ms(2);        // Change to get different angle = 360*delay/16.67
              PORTD.B0 = 1; //Send a pulse
              delay_us(250);  //pulse width
              PORTD.B0 = 0;
              ZC = 0;
           }
     }
}


(here is my error message )

 
Last edited by a moderator:

'sbit' is MikroC specific and is not recognised by XC8. (This is the sort of error I was referring to in your other thread with the same title.) You have three options: 1) 'FlagReg' is not referred to anywhere else in your code, and Z0 refers to the least significant bit, so everywhere Z0 appears, simply write 'FlagReg'. 2) Everywhere where Z0 is set, use code such as 'FlagReg |= 0x01;' and everywhere it is cleared used 'FlagReg &= ~0x01;' 3) Create a struct that defines bit fields (read up on bitfields in C structs).
I strongly recommend the first option.
The other errors about undeclared identifiers are all due to you not including the 'xc.h' file at the start of the code file. In the MPLABx IDE you need to specify the MCU you are using and it will then tell the 'xc.h' include file which SFRs to define for you.
The errors such as 'INTCON.GIE' (again mentioned in the other thread) need to be changed to 'INTCONbits.GIE'. Also 'PORTD.f0' nedds to be changed to 'PORTDbits.RD0' - look at the data sheet for the MCU and you will see they specifiyt all of the bit names for all SFRs that the XC8 expects.
If you want to use the 'delay_xx' macros then you need first #define _XTAL_FREQ to be whatever your system clock frequency is, and then use the "_delay_ms(xx)" form. Look at the XC8 User Guide that explains all of this.
Susan
 
lol i feel stupid now thank you
--- Updated ---

when you are referring to Z0 you mean ZC
 
Last edited:

ok now i am getting this problem


newmain.c:55:16: error: member reference base type 'volatile unsigned char' is not a structure or union
OPTION_REG.INTEDG = 0; //interrupt on falling edge


C:
#include <stdio.h>

#include <stdlib.h>

#include <xc.h>

#define _XTAL_FREQ 20000000


#pragma config FOSC = HS

#pragma config WDTE = OFF

#pragma config PWRTE = OFF

#pragma config BOREN = OFF

#pragma config LVP = ON

#pragma config CPD = OFF

#pragma config WRT = OFF

#pragma config CP = OFF

unsigned char FlagReg;

void __interrupt() myISR(void)
{
      if (INTCONbits.INTF){          //INTF flag raised, so external interrupt occured
        FlagReg = 1;
        INTCONbits.INTF = 0;
     }
}

void main() {
     PORTB = 0;
     TRISB = 0x01;              //RB0 input for interrupt
     PORTA = 0;
     ADCON1 = 7;                 //Disable ADC
     TRISA = 0xFF;                                //Make all PORTA inputs
     PORTD = 0;
     TRISD = 0;                 //PORTD all output
OPTION_REG.INTEDG = 0;      //interrupt on falling edge
     INTCONbits.INTF = 0;           //clear interrupt flag
     INTCONbits.INTE = 1;           //enable external interrupt
     INTCONbits.GIE = 1;            //enable global interrupt

     while (1){
           if (FlagReg){ //zero crossing occurred
              __delay_ms(2);        // Change to get different angle = 360*delay/16.67
              RB0 = 1; //Send a pulse
            
              __delay_us(250);  //pulse width
              RB0 = 0;
              FlagReg = 0;
           }
     }
}
 

Why not follow the pattern for all other SFRs and use 'OPTION_REGbits.INTEDG'?

When I said:
The errors such as 'INTCON.GIE' (again mentioned in the other thread) need to be changed to 'INTCONbits.GIE'.
I was using that as an example - a 'template' if you like of how to refer to the bit fields. the "...such as..." is the clue here.
Susan
 


Why not follow the pattern for all other SFRs and use 'OPTION_REGbits.INTEDG'?

When I said:

I was using that as an example - a 'template' if you like of how to refer to the bit fields. the "...such as..." is the clue here.
Susan
ok i think i am figuring this out i need help with the interrupts and what i have been seeing from the other posts that there void interrupt is a little different to what i have and for the intcon i dont need all of them right

some of the stuff that i have edited was from another post with a similar problem with the interrupts.
 
Last edited:

I'm getting a little lost as to what you code really is right now but using the code in your Post #5 - you need to make the FlagReg variable 'volatile'.
Normally the compiler will assume that it knows the value of variable sand internal working registers based on your code. Therefore when you test for FlagReg in the main 'while' loop, (and by the way you probably should initialise it to 0 or check that the compiler always sets uninitialised global variables to 0) the compiler may well copy the value into an internal register and then continuously test that. When the ISR is triggered, it will change the value in the variable but the main loop is not reading that any more - it is using the register copy (because it is faster to access).
The 'volatile' attribute tells the compiler that things could be happening to that variable that it is not aware of as it generates code for the 'while' loop. In your case that is the ISR changing the value. Therefore the compiler knows to always copy the value from the variable and use that. (Other cases are typically hardware registers such as flags to show that some operation in a module has completed e.g. a value received by the UART or a timer flag set.)
Susan
 

Code:
void __interrupt() myISR(void)
  {
      if (INTCONbits.INTF == 1){          //INTF flag raised, so external interrupt occured
          PORTBbits.RB1 =1;
                 
          PORTBbits.RB1 =0;
        INTCONbits.INTF = 0;
     }
}

void main() {
    TRISB0 =1;
    TRISB1 =0;
    INTCONbits.INTF = 0;
    INTCONbits.GIE = 1;
    INTCONbits.INTE = 1;
    OPTION_REGbits.INTEDG = 1;
    PORTBbits.RB1 =0;
    ei();
    while(1){
        if (INTCONbits.INTF){ //zero crossing occurred
              __delay_ms(2);        // Change to get different angle = 360*delay/16.67
              RB0 = 1; //Send a pulse
           
              __delay_us(250);  //pulse width
              RB0 = 0;
              PORTBbits.RB1 = 0;
           }
    }
}

i tried this and messing around with it does give me an out put but when i change the delay to get a diffrent phase angle nada maybe this is a little simpler

also when i change INTCONbits.INTF = 0; to INTCONbits.INTF = 1;

i get this
 

There are a number of things you need to address.
You seem to have gone back to the original code - correct? If so then please show us ALL of that code.
Firstly, you need to be more precise with your error descriptions. When you say "...when I change the delay to get a different phase angle nada..." does that mean you are editing the source file and recompiling with a new constant in the '_delay_ms(2)' statement? Also does the 'nada' mean that you only get an output when the number of ms is 2 and no output for any other number, or does it mean that no matter what number you put in you get the same initial delay.
I'm also assuming that you are talking about the '_delay_ms' call and not the '_delay_us' call.
On this point I'm guessing that you have not defined _XTAL_FREQ in this version and that the delay function calls are therefore effectively doing nothing.
Can you please tell us what the various traces in the image are? I'm guessing the lower purple one is the input waveform that you are using to trigger the zero-crossing, and the yellow pulses are the output of RB1 as it is the only one that is defined as being an output.
Which brings me to the code itself.
You are polling the INTCONbits.INTF bit in your main loop which is fine, but if you mean to have this code raise the pulse then you will also need to clear the flag.
However, you also have the ISR set up to trigger when the INTCONbits.INTF bit gets set in the hardware.
Use one or the other - poll or use an interrupt but don;t use both.
What happens is that the hardware will detect the INTCONbits.INTF bit being set and will then call the ISR before it starts the next instruction. The ISR toggles RB1 (more on that later) and clears the INTF bit. The ISR then exists and the next instruction that was about to be executed when the interrupt was called is executed.
So what could those instructions be: basically the main loop will be a test of the INTCONbits.INTF bit plus a branch instruction back to the start of the loop at a minimum. (You will need to look at the code the compiler actually generates as it could well use several more instructions to handle the loop itself.) Therefore it comes down to a matter of luck if the test of the INTCONbits.INTF instruction actually sees the bit set. If the hardware sets the bit JUST as the instruction is starting to be executed then it will see the bit set. If the hardware sets the bit an any other time, then the ISR will have cleared it before the test is made in your main loop.
Therefore, use polling OR interrupt but not both.
I suspect what is happening is that the ISR is always capturing the INTCONbits.INTF it being set and clearing it so that you (almost) never go into the 'then' code of your main loop. That is another reason why you probably never see any changes that you are looking for with your 'phase change'.
In your main loop 'then' code, you toggle RB0. Two things wrong here. First, you have RB0 set as an input so you should not be trying to set it to something. It is not doing an harm as the GPIO hardware will set the PORT bit but not allow that to go on to the pin (see Figure 4-4 of the data sheet as to why). Second, RB0 is used as the INT signal that triggers the INTCONbits.INTF bit. Again fortunately the hardware will stop your code from interfering but the code is practically useless.
Also the last instruction in that sequence sets RB1 to 0. However it is never set to 1 (except in the ISR when it is immediately set back to 0) and so this is also a useless instruction,
In the ISR you have two consecutive instructions that toggle the RB1 pin and, if my guess is right that the yellow trace on your image is the RB1 pin, then this is working for you. However you run the risk of what is known as the 'read-modify-write' problem with these old chips. Read up on this (also called the RMW problem) on the Internet.
Enough for now....
Susan
 
Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…