The second Timer v2.80 does not cause an interrupt although everything works separately.

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
RomanBalian
Level 1
Level 1
10 sign-ins 5 replies posted 5 sign-ins

In my project there are three interrupts (isr).

The first one is on the button (start), the second one periodically calls a subroutine with "IDAC" using "Timer" to output signal counts. The third also calls the subroutine, but which stops the work (timer is set to Run Mode: One Shot).

These two timers start simultaneously in the start subroutine. But there is a problem, everything works except that the stop subroutine is not called.

I want to note that the timer works, I checked it with a trigger and LED. Also the same interrupt (which doesn't work) when I connect it to the button likewise the connected interrupt "start" it works.

I am using cy8ckit-042 psoc 4 pioneer kit.

0 Likes
1 Solution

RomanBalian,

I believe I finally have an answer to your issue.

As I said, I was getting the same results as you did.  In effect, the STOP isr did not execute.  At first, I couldn't understand why.  The coding was fairly straight-forward.

After a number of experiments and reading the issue is that ALL your isr interrupts are at the same level.  This is normally not an issue but there is an timing issue in your specific code.

Len_CONSULTRON_0-1669480495521.png

One of my experiments was to not enable the isr_IDAC.   When I did this, the isr_Stop would FINALLY get executed.

The root of the issue is that in the isr_IDAC routine you are implementing a fairly complicated floating calculation:

C = C0 + A * (exp(1/((-a)*t*F))*sin(2 * M_PI * F * t));

Floating point operations take significantly more time than integer operations.  The isr_IDAC routine get executed and by the time the floating calculation is completed, the isr_IDAC is retriggered. 

Since both the isr_IDAC and isr_Stop interrupts are active, the isr_IDAC is re-executed BEFORE the isr_STOP. 

Once I changed the isr_Stop interrupt to a higher priority (either 0 to 1), the isr_Stop now has priority over isr_IDAC and the code now works.

My "code fix" is one of many solutions.  If this solution is not acceptable, let's discuss other solutions.

Note:  BiBi's suggestion is valid.  isr_<name>_Start() and isr_<name>_StartEx() are nearly equivalent.  The exception is that the _StartEx() loads the isr vector and then enables the interrupt .

Len
"Engineering is an Art. The Art of Compromise."

View solution in original post

8 Replies
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

RomanBalian,

If you are willing to share your project here, this will help someone to better consult with you as to what is the issue.

I'll make a guess as to the issue.

When a timer is set to Run mode = One shot.  You have to _Start() the component.  (I'm assuming you're already doing this.)  Once the timer completes one cycle, you need to reset the timer to reload the count values for the next cycle.

Again, without having your project, this is just a rough guess.

Len
"Engineering is an Art. The Art of Compromise."
RomanBalian
Level 1
Level 1
10 sign-ins 5 replies posted 5 sign-ins

Yes of course, sorry for not adding earlier. I hope I archived correctly.

0 Likes

RomanBalian,

After trying your project, I appear to get the same results as you.  Strange.  I'm still trying to figure why.  You code is fairly simple and straight-forward. 

I haven't given up.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Hello.

I use Creator 4.2 so I can't open the project.
However, unzipping the archive and looking at Init() function, I would comment out these three lines:
isr_IDAC_Start();
isr_Start_Start();
isr_Stop_Start();

since these are initializing the default interrupt ISR's and likely confusing the initialization of custom interrupt ISR's:
isr_IDAC_StartEx(IDAC);
isr_Start_StartEx(START);
isr_Stop_StartEx(STOP);

Just shooting blindly, but that's my take on it.  There could be other issues that I can't see.

0 Likes

RomanBalian,

I believe I finally have an answer to your issue.

As I said, I was getting the same results as you did.  In effect, the STOP isr did not execute.  At first, I couldn't understand why.  The coding was fairly straight-forward.

After a number of experiments and reading the issue is that ALL your isr interrupts are at the same level.  This is normally not an issue but there is an timing issue in your specific code.

Len_CONSULTRON_0-1669480495521.png

One of my experiments was to not enable the isr_IDAC.   When I did this, the isr_Stop would FINALLY get executed.

The root of the issue is that in the isr_IDAC routine you are implementing a fairly complicated floating calculation:

C = C0 + A * (exp(1/((-a)*t*F))*sin(2 * M_PI * F * t));

Floating point operations take significantly more time than integer operations.  The isr_IDAC routine get executed and by the time the floating calculation is completed, the isr_IDAC is retriggered. 

Since both the isr_IDAC and isr_Stop interrupts are active, the isr_IDAC is re-executed BEFORE the isr_STOP. 

Once I changed the isr_Stop interrupt to a higher priority (either 0 to 1), the isr_Stop now has priority over isr_IDAC and the code now works.

My "code fix" is one of many solutions.  If this solution is not acceptable, let's discuss other solutions.

Note:  BiBi's suggestion is valid.  isr_<name>_Start() and isr_<name>_StartEx() are nearly equivalent.  The exception is that the _StartEx() loads the isr vector and then enables the interrupt .

Len
"Engineering is an Art. The Art of Compromise."

Thanks for all the help, I solved this by changing the interrupt priority, setting the interrupt to not be processed until the calculated value is output, and seeming to set the second timer to Continuous.

I will attach the project in the future, maybe it will be useful to someone.

0 Likes

Roman,

As Dennis indicated there are other ways to solve this problem.

Dennis' recommendation is to limit the time spent at interrupt level.  This is ALWAYS a wise thing if it can be avoided.  The general principle of interrupt coding that I learned from my mentors is: "Get in.  Get out quickly".   

In other words, the interrupt is the earliest form of multitasking found in computers.  However while in the interrupt virtually all other code is blocked from executing including other interrupts.  Therefore, when an event causes an interrupt:

  • find out why the interrupt was caused
  • execute the minimum code to satisfy the interrupt.
    • If data is to be read, copy it to a RAM buffer and signal the main() task to process it.
    • If data is to be written, copy it from a RAM buffer.
    • DO NOT USE blocking functions.  This might cause significant delays to the rest of your code or possibly a code lockup condition.

Your use of the complex float equation in the interrupt was taking so long to compute that the next isr_IDAC interrupt occurred before completing the interrupt.  Although the float equation is technically not a blocking function, it effectively is acting like one.

There are other solutions.

Consider this:  Your sampling rate for the IDAC value is too fast for the PSoC4 at this CPU clock (24MHz).  Two quick fixes:

  • Lower the sample rate.  It is currently at 24 MHz/2400 = 10KHz.  Lower it to 5KHz.
  • Set the CPU clock (currently at 24MHz) to 48MHz.   This will compute the complex float equation faster.  Note: to still achieve the 10KHz IDAC rate, you will have to change the Timer_Samples period from 2400 to 4800.
Len
"Engineering is an Art. The Art of Compromise."
0 Likes
DennisS_46
Employee
Employee
100 sign-ins 50 likes received 50 solutions authored

Roman:
As Len pointed out, your interrupt routines are stepping on each other.
Another way to minimize the problem is to minimize the time spent in interrupts.

By example, I have a 2 kHz interrupt triggered by a PWM:

DennisS_46_0-1669659974118.png

In main I declare the interrupt:   

DennisS_46_1-1669660057523.png

In main.c, I start the interrupt and enable global interrupts:

DennisS_46_2-1669660186045.png

At the end of main I have the interrupt handler

DennisS_46_3-1669660274414.png

This clears the interrupt and sets a flag.
Back in main.c, if the flag is set, I call my AGC function

DennisS_46_4-1669660414671.png

AGC() is contained in a separate file, just to make main.c a little less messy.

DennisS_46_5-1669660581144.png

The first part is to set a pin (sync_1) high so I can track the time to execute the interrupt;
Then AGC_TR is set low, and my AGC process starts with an ADC sample to measure the signal level.
Then the gain is adjusted up or down as appropriate.
Sync_1 is set low at the end of the interrupt. Total interrupt time is less than one microsecond.

It may look a little convoluted, but the absolute minimum of time is spent actually in the interrupt.
This design has only one interrupt, but the technique is even more import when you have more interrupts or when the interrupt triggers a long calculation or procedure because it keeps interrupts from stepping on each other.

---- Dennis Seguine, Senior PSoC Applications Engineer