CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Wave form edge detection
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
matthewmilford



Joined: 09 Feb 2021
Posts: 23

View user's profile Send private message

Wave form edge detection
PostPosted: Mon Jun 14, 2021 2:28 pm     Reply with quote

So, I have been working on and off for a few months on a program that uses zero cross detection to drive a load (think light bulb at least for prototype) with a triac. I am working now on being able to control which edge it will react to (low to high or high to low). I want to be able to do full or half wave. This is the function that I have, it works fine on half wave but will not do full wave. I have it rigged up on a scope and the pic is getting a good square wave on B0 (the zcd pin). I have run the function from either main or ext interrupt routine and see no difference.
Thoughts as to why this wouldn't catch on both changes when waveform = 1?
Is there an error in this function or should I be looking elsewhere?
Code:

void wave()                                                                             
{
    if(waveform == 1) //full
    {
        if(input(inputsignal) == 1)
        {
            delay_cycles(1);
            ext_int_edge(H_TO_L);
        }
        else
        {
            delay_cycles(1);
            ext_int_edge(L_TO_H);
        }
    }
    if(waveform == 0)    //half
    {
        ext_int_edge(L_TO_H);
    }
}


MplabX V. 5.35
Pic18f26k42
CCs V. 5.102 pch
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Jun 14, 2021 3:11 pm     Reply with quote

What are the high and low voltages of the input waveform ?
What's the frequency of the input waveform ?
What's the Vdd voltage of the PIC ?

Post your INT_EXT interrupt routine.
matthewmilford



Joined: 09 Feb 2021
Posts: 23

View user's profile Send private message

PostPosted: Mon Jun 14, 2021 3:56 pm     Reply with quote

The Voltage on the input is 0 to 5VDC
The Frequency is 60 Hz
The VDD on the PIC18F26K42 is 5Volts

Code:
#INT_EXT            // external interrupt ISR
void EXT_ISR()
{
   disable_interrupts(GLOBAL);
   clear_interrupt(INT_EXT);
   //ZC = ZC+1;
   heartbeat = heartbeat+1;
   repeat = repeat+1;
   masterspeedcheck = masterspeedcheck +1;
   ramplockout++;
   delay_cycles(1);
   if(speed>0)
   {
        if(HZ == 60)
        {
            delay_cycles(1);
            enable_interrupts(INT_TIMER1);
           
            set = 600 * speed;
            set_timer1(set);
        }
        if(HZ == 50)
        {
            //figure out timing here
        }
   }
   enable_interrupts(GLOBAL);
}
vtrx



Joined: 11 Oct 2017
Posts: 142

View user's profile Send private message

PostPosted: Mon Jun 14, 2021 4:30 pm     Reply with quote

I have an old project that might help you.

https://drive.google.com/file/d/1Mtg-7c0hUOR3MuQMq_IW-IGOVSt56aoF/view?usp=sharing
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Mon Jun 14, 2021 11:43 pm     Reply with quote

OK.
As I read this, you are saying that you have a AC waveform, scaled and
shifted, so it is giving 0-5v AC, into a PIC input. You are then trying to use this
to detect rising and falling edges using the interrupt pin?.
Not going to work.

Now before talking about this, a 'screaming error'. You must _never_, have
'enable_interrupts(GLOBAL), inside a PIC interrupt handler.
This can result in the chip completely crashing.

The PIC, when it handles an interrupt _automatically_ disables the global
interrupt, until it exits from the handler routine. This is automatic.
The interrupt is re-enabled, the instruction after the code exits the handler,
it must not be enabled before this point. You are re-enabling this before all
of the 'exit' code (there are several instructions to restore the registers
built into the handler when it exits). Result you are potentially enabling
the interrupts inside the interrupt handler. If an interrupt occurs at this
point it will result in the register contents being lost. Absolute disaster.
This is an absolute 'no no' for the PIC.

You do not need to have the disable, and_must not_ have the enable.

Now back to the waveform. The first thing to understand is that digital
inputs don't switch high/low at half the voltage. You have a choice on
this PIC of setting inputs to be TTL compatible, or Schmitt. This is
controlled by the INVLx register. If set as TTL, the 'high' voltage is
anything above 2v, and the 'low' voltage is anything below 0.8v.
If set as Schmitt, the thresholds are 4v, and 1v. In neither case is this
anywhere near 2.5v. Also the detected level will remain where it is all
the while the voltage changes between these levels. Result your 'detection'
points, will not be anywhere near the zero crossing point of the waveform.
To use detection like this, you need the input AC waveform turned into a
genuine square wave, with a comparator at the mid point of the AC
waveform.

So, do you have hardware generating a digital signal reflecting the cycles
correctly?. If not, you need this.
Then fix the code. Get rid of your enable/disable interrupt global in the
interrupt handler, and add this to the 'must never do this' list for the PIC.
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Jun 15, 2021 5:14 am     Reply with quote

Ok, I have to ask ...
why aren't you using the ZCD interrupt flag ?

I don't use that PIC, got the datasheet, quick read of section 29.. the Zero Cross detector peripheral(page 429, sigh..). Looks simple enough to set which edge to detect, have the flag set when triggered.
Your code looks like it's not using the ZCD peripheral at all.

As others point out, NOT a good idea to put an AC signal on any PIC I/O pin.
To use the ZCD, you'd need to disable all other peripheral functions to that pin (B0).

Maybe I'm missing 'something' ?
Jay
matthewmilford



Joined: 09 Feb 2021
Posts: 23

View user's profile Send private message

PostPosted: Tue Jun 15, 2021 6:19 am     Reply with quote

I did not use the zcd that is built in due to not wanting to tie in ac power into the pic. I have an external zcd chip that feeds in a very nice isolated 0-5v square wave that matches up well with the sin wave. So I am not putting AC into the pic.

Ttelmah, I fixed the "screaming error". I was talking with an older programmer collogue of mine from whom I got the idea that this was necessary and he said that at one point you had to do this manually due to an error in the code back in the day. Glad to know that was fixed.
I am trying to detect based on a square wave, the pic has no input anywhere that is ac waveform. The square wave signal does correctly reflect the cycles. It changes states every time there is a zero crossing and goes from 0-5v.

So is it not able to do this edge detection on a square wave?
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Tue Jun 15, 2021 7:31 am     Reply with quote

OK. Glad you have got a proper square wave. Very Happy

You never enabled interrupts like this on a PIC. You often (usually) did
have to do this on other processors. For example on the old Z80. On these
processors the enable does not occur till the instruction _after_ the flag is
set, allowing a return instruction to be used as the next instruction
without recursion happening.

There was never a software error requiring you to do this on the PIC,
since if you did this it would actually prevent the hardware system from
working.
There was a software fault some time ago, where the GIE could become
disabled, but this had to be handled by re-enabling this in the main code,
not in the interrupt handler.

The point is that on the PIC, the actual chip hardware automatically disables
GIE, when the handler is called (in fact on the PIC18, it is the GIE for the
high or low priority that is disabled, corresponding to the priority of the
handler). The processor returns from the interrupt using the 'RETFIE'
instruction, which is ReTurnFromInterruptEnable. Which enables the interrupt after the return. This has to happen, because of the way
that the PIC handles interrupts (they are dealt with in the very first phase
of each instruction), so if you do the enable 'in code', the interrupts are
already enabled before you can return from the handler. Disaster....

It should perfectly be possible to do this edge detection on the square
wave. Get rid of the global interrupt disable/enable, and see what happens.

Now some comments:
1) you do realise that setting the interrupt edge, does not enable the
interrupt. You don't show the interrupt being enabled anywhere...
2) setting the edge, can result in the interrupt itself being triggered.
So you need:
Code:

            disable_interrupts(INT_EXT);
            ext_int_edge(H_TO_L);
            clear_interrupts(INT_EXT);
            enable_interrupts(INT_EXT);


Why don't you just change the edge in the handler itself?. This is the
normal way of doing this. In here you don't need the disable/enable,
and the interrupt will be cleared before the routine exits.
matthewmilford



Joined: 09 Feb 2021
Posts: 23

View user's profile Send private message

PostPosted: Tue Jun 15, 2021 9:42 am     Reply with quote

I removed the global interrupt stuff (except the stop button to shut it off). No change with that.

I am aware that setting the edge doesn't enable it.
Code:

void main()
{
    lcd_init();
   
    output_drive(triac_gate);
    output_low(triac_gate);
   
   
    clear_interrupt(INT_EXT);   
    enable_interrupts(INT_EXT);       
    enable_interrupts(GLOBAL);
   

That is the beginning of my main but not in a while true loop hence it runs on startup and enables it. Sorry for not posting the whole code up front, but it is quite long and I didn't figure that anyone would want to read all that to find the 30 lines that are relevant to the issue Smile.

So when I am setting the edge it can trigger the interrupt, so should I change the edge inside the interrupt itself so that if it just got triggered
H to L then inside the interrupt I tell it trigger L to H and let it toggle for full wave functionality? Something like this?

Code:

//***********************************************************
#INT_EXT            // external interrupt ISR
void EXT_ISR()
{
   clear_interrupt(INT_EXT);
   ZC = ZC+1;
   heartbeat = heartbeat+1;
   repeat = repeat+1;
   masterspeedcheck = masterspeedcheck +1;
   ramplockout++;
   delay_cycles(1);
   if(speed>0)
   {
       //deal with waveform
        if(waveform ==1)
        {
            if(input(inputsignal) == 1)
            {
                delay_cycles(1);
                ext_int_edge(H_TO_L);
            }
            else
            {
                delay_cycles(1);
                ext_int_edge(L_TO_H);
            }
           
        }
        if(HZ == 60)
        {
            delay_cycles(1);
            enable_interrupts(INT_TIMER1);

            set = 600 * speed;
            set_timer1(set);
        }
        if(HZ == 50)
        {
            //figure out timing here
        }
   }
}

where input signal is defined as this and is the same signal that is used for the zcd.
Code:
#define inputsignal   PIN_B0
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Tue Jun 15, 2021 10:06 am     Reply with quote

Basically yes.
You don't need the delays though.
The interrupt toggle should just happen all the time. No test for
'waveform==1' or 'speed'.
These should only be affecting the generation of the output.

I'm puzzled by your reference to ZCD. If you are generating an external
ZCD based square wave, you don't want the chip's internal ZCD. If you
are using the chip's ZCD, this makes it's input pin into an analog pin.
As an analog pin, it won't work for INT_EXT. This may be why you are
having oddities. If you are using the PIC's ZCD, then you don't want
the square wave generator. Instead use the ZCD interrupt.
matthewmilford



Joined: 09 Feb 2021
Posts: 23

View user's profile Send private message

PostPosted: Tue Jun 15, 2021 12:32 pm     Reply with quote

I have the delays in there to use as break points for trouble shooting. The waveform and speed are to control when it executes full wave run format. I don't want it to trigger when speed is 0 (its off) or when waveform is != 1 (then it should run half wave. Would any of these things throw off the performance of the full wave or just not necessary strictly speaking to get the full wave going? So far this is still only getting low to high transitions.

Also pardon my reference to the zcd, that is the signal from the zcd that carries the square wave to the chip and is used to trigger INT_EXT.
matthewmilford



Joined: 09 Feb 2021
Posts: 23

View user's profile Send private message

PostPosted: Tue Jun 15, 2021 2:43 pm     Reply with quote

So an update.... I was going back to the basics and found an interesting issue. When I run this external interrupt module where "inputsignal" is my 0-5v waveform it will never get into the else. (It thinks its always high) Thus never firing on one side of the externally detected zero crossing. Any thoughts as to why this might be?

Code:

#INT_EXT            // external interrupt ISR
void EXT_ISR()
{
   clear_interrupt(INT_EXT);
   ZC = ZC+1;
   heartbeat = heartbeat+1;
   repeat = repeat+1;
   masterspeedcheck = masterspeedcheck +1;
   ramplockout++;
   //delay_cycles(1);

   if(input(inputsignal) == 1)
   {
        lcd_gotoxy(15,4);
        printf(lcd_putc,"TOP");
   }
   
   else
   {
       lcd_gotoxy(15,4);
        printf(lcd_putc,"BOTTOM");
        delay_cycles(1);
   }
   
   delay_cycles(1);
   
    enable_interrupts(INT_TIMER1);
    set = 600 * speed;
    set_timer1(set);
PrinceNai



Joined: 31 Oct 2016
Posts: 478
Location: Montenegro

View user's profile Send private message

PostPosted: Tue Jun 15, 2021 3:50 pm     Reply with quote

Hi,

You said your interrupt fires only on L-H transition. If it is so, I believe that you'll always get TOP on your LCD. You write to LCD inside interrupt routine. I don't think you ever leave it (you do, only to come back at once), since I'm almost sure that writing to LCD takes longer than it takes to get another interrupt. If you suspect that code has anything to do with interrupt not firing the other way, why don't you lose it all and just write a test program with only while(1) loop in main and external interrupt routine without any if's before testing if your input is high or low after you get there? I'd also try the advice of Mr.Ttelmah to disable all interrupts before changing the direction and then clear int_ext before enabling them again, as he mentioned in an earlier post. Those delays of one cycle don't change anything, leave them there to be able to set a breakpoint.
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Wed Jun 16, 2021 12:24 am     Reply with quote

Key thing is an old adage.
Keep interrupt handlers fast.
Your old code did not show any prints and lcd operations. These should not
be in an interrupt.
However your new code has got rid of the edge selection. Not surprising
then that it only triggers on one edge.....
So lets setup INT_EXT so that it triggers on both edges:
Code:

INT_EXT            // external interrupt ISR
void EXT_ISR()
{
   int1 high;

   if(input(inputsignal) == 1)
   {
        high=TRUE;
        delay_cycles(1);
        ext_int_edge(H_TO_L);
   }
   else
   {
        high=FALSE;
        delay_cycles(1);
        ext_int_edge(L_TO_H);
   }
   //Now do the things that you want to stop
   //If you need anything to be different on the rising or falling
   //edge, test 'high' for this.
   //If these don't want to happen at some point then have
   //a flag for this and don't do these things when the flag is set.
   ZC = ZC+1;
   heartbeat = heartbeat+1;
   repeat = repeat+1;
   masterspeedcheck = masterspeedcheck +1;
   ramplockout++;
   delay_cycles(1);

   if(speed>0)
   {
       //deal with waveform
        if(waveform ==1)
        {

           
        }
        if(HZ == 60)
        {
            delay_cycles(1);
            enable_interrupts(INT_TIMER1);

            set = 600 * speed;
            set_timer1(set);
        }
        if(HZ == 50)
        {
            //figure out timing here
        }
   }
}


The compiler already clears the interrupt for you at the exit of the handler.

Leave INT_EXT enabled and running all the time. Just start and stop the
internal parts of the routine when you want to do your operations.
matthewmilford



Joined: 09 Feb 2021
Posts: 23

View user's profile Send private message

PostPosted: Wed Jun 16, 2021 11:17 am     Reply with quote

Okay so an update on what I have been doing. I was thinking about it last night and realized exactly what you pointed out with the edge... duh I need those. Anyway, I took your suggestion and stripped it down to the bare bones and actually started a new test program to see if I could replicate the behavior. Here is where I am currently at. It goes into the if(input(inputsignal)==1) always. If I switch the 1 to 0 same behavior.

B0 (input signal) is not always high or low it has a good 0-5v square wave.

So it will only pick up one transition. L to H or H to L depending upon which I have in the if vs else loop. (As currently written it will se L to H) See following full program.

Code:

#include <18f26k42.H>
#device ADC = 10

#include <BMLCD420.c>

#include <stdio.h>
#include <time.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

#fuses HS, XT, NOWDT, NOPROTECT, NOPUT, NOLVP
#use delay(clock = 4MHz)

#define inputsignal   PIN_B0
#define gate2 PIN_B2


#INT_EXT            // external interrupt ISR
void EXT_ISR()
{
    int1 high;

   if(input(inputsignal) == 1)
   {
        high=TRUE;
        delay_cycles(1);
        ext_int_edge(L_TO_H);
   }
   
   else
   {
        high=FALSE;
        delay_cycles(1);       
        ext_int_edge(H_TO_L);
   }

    delay_ms(2);
    output_high(gate2);
    delay_us(50);
    output_low(gate2); 
 
}

void main()
{
    output_drive(gate2);
    output_low(gate2);
   
    clear_interrupt(INT_EXT);   
    enable_interrupts(INT_EXT);       
    enable_interrupts(GLOBAL);
   
    while(TRUE)
    {
        delay_cycles(1);

    }
}
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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