PSoC4, using SysTick timer and NVIC

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

cross mob
Anonymous
Not applicable

Hello,

I am looking for help or advice on programming the PSoC4 for a specific counting task.

My current project is to build an accurate audio frequency counter by capturing the number of 1MHz clock pulses that occur during one or more periods of the audio signal.  At low frequencies one period is enough, but at higher frequencies I need to measure over many periods to get an accurate result (a count of 50000 or more).  I am using an external crystal with the PSoC4 for accuracy.

Thus I need two counter, one to capture the total count, and one to keep count of the number of input periods.  Unfortunately the PSoC4 does not have enough logic resources to implement two counters that are long enough, plus the status registers needed to send the counts to the processor.  So I am wondering if I can somehow utilise the SysTick timer and/or the Nested Vectored Interrupt Controller (NVIC) in the processor to directly count the number of periods.  It would in effect need to capture pulses at a maximum 50kHz rate, and I'm not sure if it could handle interrupts at this rate.

So, can advise me if this is feasible?   If it is, I would need some help with writing the code to handle interrupts, as this is an area where I have little experience.  I always find that examples of code that performs a similar task is the best starting point, so it would be ideal if anyone can point me towards existing code samples.

I realise I could always use a "larger" PSoC part, but the PSoC4 is what I have, and I'd like to stick with it if possible.

Many thanks for any help anyone can offer.

0 Likes
26 Replies
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

A basic frequency counter is a gate coupled to a counter enable/read/status register.

   

The SysTick is clocked by the CPU clock, so you could use it to get the gate generated.

   

    

   

 

   

          http://www.cypress.com/?id=4&rID=94607

   

 

   

Then use a 24 bit UDB counter,  a 20 bit basic counter in Utility logic component library, or a

   

combination of 7 bit counter and software counting its overflow, since that will be relatively low

   

speed.

   

 

   

   

 

   

What is the range, resolution ( I see the 1 Mhz), and accuracy of F you want to measure ?

   

 

   

Regards, Dana.

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Since you solved the first part of your project by introducing a crystal driven clock, the rest will be easy enough. Instead of counting interrupt-driven the single pulses, which would consume a lot of MIPS at 1 MHz, you can use a counter and an interrupt when the counter overflows. Both counters, counting the pulses and counting the number pf periods could use this combination of software and hardware method without getting busted the amount of resources availlable.

   

Nonetheless you may use the Systick-timer and generate an interrupt when it counted down. Have a look into the System Reference Guide (accessible from Creator Help-menu) chapter 15.

   

 

   

Bob

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

When counting low frequencies a reciprocal counter is more practical.

   

 

   

Attached info, more than you want to know about counters.

   

 

   

    

   

          

   

AN52-1 Fundamentals of Time and Frequency

   

 http://www.keysight.com/main/facet.jspx?&cc=US&lc=eng&k=an52-1&sm=g

   

AN200-3 Fundamentals of Time Interval Measurement

   

http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCAQFjAA&url=http%3A%2F%2Fwww.le...

   

AN200-4 Understanding Frequency Counter Specifications

   

http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCwQFjAA&url=http%3A%2F%2Fwww.le...

   

AN-1289 Science of Timekeeping

   

http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCcQFjAA&url=http%3A%2F%2Flitera...

   

   

 

   

Regards, Dana.

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Dana,

   

I'd like to know more about counters

   

 

   

Bob

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given
    AN52-1 Fundamentals of Time and Frequency   
   
        
       
        
   
    AN200-3 Fundamentals of Time Interval Measurement   
   
        
       
        
   
    AN200-4 Understanding Frequency Counter Specifications   
   
        
       
        
   
    AN-1289 Science of Timekeeping   
   
        
   
0 Likes
Anonymous
Not applicable

Hi Dana, Bob,

Thank you, you've given me a lot of info, which will take a bit of digesting -- I havn't yet looked at all the links you've given to counters.

Anyway, to answer your first questions:
 
 Range is 7Hz to 100kHz.
 I'd like accuracy of one part in 50000 at all frequencies.  This is probably well beyond what my analog circuitry can achieve in stability, but I have set that as a target.
 A 1MHz counting clock will give about 143000 counts at the low end, which is OK.
 At the high end I'll get 10 counts per period, so will need to count for >5000 periods to get the resolution I want.

[  You mention that a reciprocal counter is more practical for low frequencies.  If I understand correctly that is what I'm doing -- I am measuring the input signal period and converting to frequency in the processor.  ]

 You both seem to suggest using a modest number of counting stages in hardware, and capturing its overflow to continue the count in software.   This seems to me to be the simplest way, and if I understand correctly would not involve using SysTick timer.
 
 Now my problem is: How to get the overflow pulse to the processor ?
 I think it will need to be an interrupt, so should I use the Interrupt Component ?
  (Up til now I have only used the Status Register Component to capture data)

The Interrupt Component datasheet gives some basic API info on coding, but a more complete example would be very helpful.  I have looked through the example projects on the PSoC Creator Start page, but cannot find anything that demonstrates Interrupts.  I wonder if you can give me a link to an example?

Many thanks again.  Regards,  Ken.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

Using SysTick and the basic counter set at 20 bits or greater you will

   

not have to count rollover pulses to effect cascade, the counter is big

   

enough to capture largest value.

   

 

   

Two ways to implement ISR, one w/o using ISR component, look at your

   

component (like a counter/timer) ISR file (your enabling an interrupt

   

in its global properties or with API call creates this file ) you will see

   

placeholders for definitions/variable declarations at top of file and another

   

in the ISR place itself. Putting your code there prevents compiler from

   

overwriting that code on rebuilds.

   

 

   

The other way is using ISR component, that creates its own file, or you

   

can create C ISR, shown in ap note.

   

 

   

    

   

         

   

http://www.cypress.com/?rID=38267     AN54460 - PSoC® 3, PSoC 4, and PSoC 5LP Interrupts

   

 

   

Remember to declare variables volatile used in ISR.

   

                

   

http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword    Volatile

   

 

   

If you declare a value, like a global in main.c -

   

 

   

uint32 volatile myfreq = 0;

   

 

   

And then inc that in ISR which is located in another file other than main.c, you

   

have to declare that variable as external in that file -

   

 

   

extern uint32 volatile myfreq;

   

 

   

 

   

 

   

Regards, Dana.

0 Likes
Anonymous
Not applicable

Hi Dana,

I am using an 18 bit basic counter to count the 1MHz pulses.  This is big enough.

For counting the number of signal pulses at higher frequencies I reckon I should use the ISR component to allow counting in software.  I have downloaded app note AN90799 on Interrupts, and this gives very comprehensive guidance plus examples, so I think I should be able to work out what to do from that.  Thanks for directing me to that app note.

Your further notes in your reply are helpful, and suggest it should not be too hard.

So now I will go away and get on with it, although I might need to come back again if I get stuck !

Many thanks again.  Regards,  Ken.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

If you are trying to run your ISR at a very high frequency you will eat up

   

all the machine MIPS. I can't speak to a fixed value, but << 100 Khz

   

would be prudent I think. Use HW for >> 100 Khz.

   

 

   

Regards, Dana.

0 Likes
Anonymous
Not applicable

My plan is to have a few stages of division in hardware first, so that the frequency I will be counting with interrupts is about 1kHz ( ~1 pulse per mS).  Do you think that will be OK ?

Regards,  Ken.

0 Likes
odissey1
Level 9
Level 9
First comment on KBA 1000 replies posted 750 replies posted

You can find PSoC3 implementation of the reciprocal counter by "HLi" here (FreqMeter bundle):

   

http://www.cypress.com/?app=forum&id=2232&rID=66882

   

 

   

PSoC5 here:

   

http://blog.hendriklipka.de/archives/2013/12/psoc_frequency_counter_3.html

   

 

   

And explanaltion here:

   

http://blog.hendriklipka.de/archives/2012/04/freqmeter_intro.html

   

 

   

and demo here:

   

https://www.youtube.com/watch?v=4oK3syZszUU

   

 

   

 

   

odissey1

0 Likes
Anonymous
Not applicable

Hi,

   


I have just downloaded the freq counter by "HLi".
This is a really good starting point for my project.
Also, I was unaware of Logisim, so have downloaded that as well.
It seems to be a useful and easy to use simulator.  Up til now I have used the free version of ModelSim from Mentor, but this is really intended for FPGA design, so is not well suited to general purpose logic simulation.

   


Many thanks for posting these links for me.

   

Regards,  Ken.

0 Likes
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

As the mentioned author, I should add some corrections:

   
        
  • the PSoC5 / PSoC3 implementation in that article is not a reciprocal counter, but a simple gated one
  •     
  • (For that, the earlier articles show the PSoC4 implementation, so the OP might want to look at that)
  •     
  • the introduction OTOH is about a reciprocal counter, that I implemented in a CPLD back then.
  •    
   

I have an implementation of a reciprocal frequency meter on a PSoC5 somewhere, since this was the intended next step in my PSoC series. But I never came around to write that part 😞

   

Also, its difficult to fit that in a PSoC4. There are just enough UDBs for two 16bit counters. One could use the fixed-function-block, but I doubt they can be chained so easily to get two 32bit counters. Maybe I can get around that somewhere in the future...

0 Likes
Anonymous
Not applicable

Hello again,

Thanks HLi for your correction note, but no problem, I am now back on track with my frequency meter project.

   

As an aside, Logisim is straightforward and easy to use, but has one major drawback -- no built-in waveform viewer.

Now the rest of this reply is on a slightly different topic, but still but related to pulse width/frequency measurement.  So I am wondering if I should start a new thread?  I will put my question here for the moment, as follows:
 -------------------------------------------------------------------------------

I've had further thougts on how to implement a reciprocal fequency counter, using SysTick Timer.
 
Set number of ticks to the maximum, eg, TICK = 0x00FFFFFF.
 
On a rising signal edge, use interrupt to enable the SysTick timer with the API  SysTick_Config(TICKS).
 
On next rising signal edge, use another interrupt to read the SysTick Current Value Register.
 
The difference from max will be an accurate measure of length of one signal pulse.
  (Assuming an accurate external xtal)
 
BUT, my problem is:  How do I read the SysTick Current Value Register?
 
I have read the Knowledge Base Article "Using the SysTick Timer in PSoC® 4 – KBA91374".
It lists the SysTick Timer Registers, including Current Value Register, and says that
"...in the PSoC 4 family, these registers can be accessed using a pointer to the structure
 defined in the core_cm0.h file."
 
This implies that what I suggest can be done, but how do I go about writing the code?
I've had a look at the core_cm0.h file, but it is well beyond my very limited understanding of c code.
I've also had a look in the Cortex M0 User Guide, but that is equally unhelpful for a code rookie.
 
Can you offer advice ?   Many thanks.

0 Likes
lock attach
Attachments are accessible only for community members.
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

Something like this -

   

 

   

    uint32 * MySys = (uint32 *) SysTick_BASE;           // Pointer to systick structure of registers,
                                                                                          // SysTick_BASE is defined in core_cm0.h
   
    uint32 SysCntVal = 0;                                                // The value of SysTick counter you are trying to retrieve
 

    for(;;)
    {
        /* Place your application code here. */
       
        SysCntVal = *( MySys + 0x0008 );                    // SysTick counter is offset from base address by 0x0008 in the structure

   

                                                                                       // defined in core_cm0.h
       
    }

   

 

   

Using pointers, attached -

   

 

   

Regards, Dana.
 

0 Likes
Anonymous
Not applicable

Hello again,

Many thanks for your reply.  My apologies that this post is so long.
I think I am nearly there, but have got stuck again.

I have written the code in a short test project, just to try out the SysTick register operations.
The code is as below, and follows your guidance.
The associated hardware is minimal, and actually has no bearing on the SysTick operations.
The code also includes some ISR, but that also has no relevance, I think.
As you will see, I have an LCD where I can display some variables. 

   

The important bit is the first few line of the while loop.
The Build is OK, no errors.

First I invoke SysTick_Config with systick_return, and systick_return is displayed as 0,
  so that suggests the SysTick register is loaded correctly.
My understanding is that the timer count down starts immediately.
I allow 30mS to elapse, then try to read the current value register to variable current_val.
I expect this value to be roughly 15,000,000, but it is always displayed as 0.
 ( 32MHz external clock for 30mS is approx 1 million counts)

I have tried using *(MySys + 0x000), *(MySys + 0x004), *(MySys + 0x008), *(MySys + 0x00C),
  to read each SysTick register in turn.
The first one, CSR, returns 7, which I think is correct.
The other three all return 0.

So I'm at a loss as to understanding what is going on.

Can you advise ?
Regards,   Ken.



/*******************************************************************************
* File Name: main.c
* Description:
*    Minimal code to investigate using SysTick, and later also ISR.
*******************************************************************************/
#include <device.h>
#include <stdio.h>

int flag = 0;
u_int *MySys = (u_int *) SysTick_BASE;      // Pointer to systick structure of registers,
                                            // SysTick_BASE is defined in core_cm0.h
CY_ISR_PROTO(Ken_ISR);  // ISR prototype declaration

/*******************************************************************************
* User ISR Function Definition,  Function Name is Ken_ISR
********************************************************************************/
CY_ISR(Ken_ISR)
{
 //  This is the code to be executed when interrupt occurs,
 //    ie  load the SysTick reg and read its value on alternate interrupts.
 //    For now, just toggle flag
    if (flag == 0){
        flag = 1;    
    } 
    else {
        flag = 0;     
    }
}        

/*******************************************************************************
* Function Name:   MAIN
*******************************************************************************/
int main()
{         
    int current_val = 999;  // An arbitrary value, just to be sure it changes on running
    int systick_return = 999; //   Ditto
    int mon_pulse = 0;
    uint32 ticks = 0x00FFFFFF ;  // The max value allowed.
   
    isr_1_StartEx(Ken_ISR);   // Initialise the custom ISR
    LCD_Start();              // Initialise the LCD 
    Comp_1_Start() ;          // Initialise the Comparator
       
    while(1u)  // Loops forever.
    {
        CyGlobalIntEnable;    // Enable global interrupts
       
        // Map systick ISR to the user defined ISR.
        // SysTick_IRQn is already defined in core_cm0_psoc4.h file as -1
        CyIntSetSysVector((SysTick_IRQn + 16), Ken_ISR);
       
        systick_return = SysTick_Config(ticks);  // Enable Systick timer with desired no. of ticks. in this case Max
                                                 //  If OK, systick_return will be 0; if fails it will be 1
        CyDelay(30u);                      // Allow 30mS to elapse
        current_val = *( MySys + 0x008 );  // Then read SysTick current value register
                                           //  Expect this value to be roughly 15,000,000
       
        mon_pulse++;                       // Just to verify that it is running
        if (mon_pulse >100) mon_pulse=0;                  
                           
        LCD_ClearDisplay();   // Clear the display        
        LCD_Position(0, 0);   // First line of LCD
        LCD_PrintString("Monitor ");
        LCD_PrintU32Number(mon_pulse);               
        LCD_Position(1, 0);    // Second line of LCD
        LCD_PrintString("Count=");
        LCD_PrintU32Number(current_val) ;
        LCD_PrintString(" Ret=");
        LCD_PrintU32Number(systick_return) ; 

    }
} // End of MAIN
/* ******************************** END OF FILE **************************************/

 

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

It would be a lot easier for forum if you post a project archive -

   

 

   

    

   

          

   

“File”                                                             Creator

   

“Create Workspace Bundle”

   

  

   

Regards, Dana.

0 Likes
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

Hi Dana,

I suspected that just pasting code into the post was not the right way to do things.
So my apologies, and I have attached the workspace bundle file.

Regards,   Ken.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

I think you want to move this code outside your while loop, before the

   

while { }, you keep resetting SysTick....

   

 

   

        CyGlobalIntEnable;    // Enable global interrupts
       
        // Map systick ISR to the user defined ISR.
        // SysTick_IRQn is already defined in core_cm0_psoc4.h file as -1
        CyIntSetSysVector((SysTick_IRQn + 16), Ken_ISR);
       
        systick_return = SysTick_Config(ticks);  // Enable Systick timer with desired no. of ticks. in this case Max

   

 

   

On another topic, consider using a buffer to handle LCD display, If

   

you are in a  fast loop writing to LCD you get "artifacts" on LCD, can look like

   

crap. So reaction is to put on a delay, so MIPS are thrown away for no good

   

reason. Use of buffer eliminates this. Example, you have a 2 x 16 display, so you

   

set up a 2 x 17 array, fill last element both rows with null. Then everytime you want

   

to write to LCD you first check the buff char for char to see if it is same as what you

   

want to write. If it is you do nothing. If it needs update, you only update chars that

   

have changed, then write buffer out to LCD. Now no delay is required.

   

 

   

Regards, Dana.
 

0 Likes
Anonymous
Not applicable

I have moved the lines of code you suggest outside of the while loop.
Result is same as before.

I have tried also moving the lines:

     CyDelay(30u);                      // Allow 30mS to elapse
     current_val = *( MySys + 0x008 );  // Then read SysTick current value register

 outside of the while loop.
 
Again, result is same as before, the current_val is displayed as 0.

I think this is similar to my first code, except that I'm performing the sysTick operations just once, instead of repeating them forever.  So I should expect to see a non-zero value for current_val.



Your comments on using a buffer to handle LCD display are spot-on; I had indeed been getting a rubbish quality display, and used the delay to fix it.  What you suggest sound good, so I will try it now.  Many thanks.

Regards,   Ken.
 

0 Likes
lock attach
Attachments are accessible only for community members.
odissey1
Level 9
Level 9
First comment on KBA 1000 replies posted 750 replies posted

I assume that you want to measure time elapsed between consecutive SysTick measurements. For that code is below. If problem was stating SysTick interrupt, see commented code in the project attached.

   

odissey1

   

 

   

 #define SYSTICK_MAXVAL 0x00FFFFFF //max allowed SysTick counter value for 24bit

   

...

   

int main()
{
      
    uint32 * MySys = (uint32 *) SysTick_BASE;   // Pointer to systick structure of registers (see core_cm0.h)
    uint32 SysCntVal;                           // The value of SysTick counter you are trying to retrieve
 
    for(;;)
    {
        SysTick_Config(SYSTICK_MAXVAL); //reset counter set to max value, 1-time,  will not reload
        SysCntVal=*( MySys + 0x0002 ); //record current value
        //CyDelayUs(1000); // 100us: 2449; 1000us: 24051;
        CyDelay(100);  //   10ms: 240056, 100ms: 2400055, must be less than SYSTICK_MAXVAL
        SysCntVal -= *( MySys + 0x0002 );// Decrement from previous value                                           
 
        //alternative way...
        //SysTick_Config(SYSTICK_MAXVAL); //set to max 24bit value, 1-time counter,  will not reload
        ///CyDelayUs(1000); // 100us: 2461; 1000us: 24063;
        //CyDelay(10);  //   10ms: 240067, 100ms: 2400067, must be less than SYSTICK_MAXVAL
        //SysCntVal = SYSTICK_MAXVAL - *( MySys + 0x0002 ); // ~67 ticks error offset
       
        sprintf(strMsg1, "%d\r\n", SysCntVal); //report ticks elapsed
        UART_PutString(strMsg1);
       
        CyDelay(50); 
    }
}
 

0 Likes
Anonymous
Not applicable

Hello again,

Success at last!
I have tried the code you suggest in last post, and it works!
I can now read the contents of the current value register, by setting the
address pointer offset from SysTick_BASE as 2 instead of 8.
This was cause of all the problems.

But I am puzzled as to why 2 is the offset, when the core_cm0.h file and your own
earlier posts indicate that the offset should be 8.

The following is a snippet from core_cm0.h starting at line 408:


/** \brief  Structure type to access the System Timer (SysTick).
 */
typedef struct
{
  __IO uint32_t CTRL;                    /*!< Offset: 0x000 (R/W)  SysTick Control and Status Register */
  __IO uint32_t LOAD;                    /*!< Offset: 0x004 (R/W)  SysTick Reload Value Register       */
  __IO uint32_t VAL;                     /*!< Offset: 0x008 (R/W)  SysTick Current Value Register      */
  __I  uint32_t CALIB;                   /*!< Offset: 0x00C (R/ )  SysTick Calibration Register        */
 
Any comment on this?

Many thanks,   Ken.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

That is odd in light of the fact the offsets should be multiples

   

of 4 from the base......as they are all uint32s ?

   

 

   

Regards, Dana.

0 Likes
odissey1
Level 9
Level 9
First comment on KBA 1000 replies posted 750 replies posted

Here is major simplification (after some tinkering). Use SysTick->VAL directly as shown below. This code behaves correctly under size/speed optimization.

   

odissey1

   

 

   

 

   

#define SYSTICK_MAXVAL 0x00FFFFFF //max allowed SysTick counter value for 24bit

   

uint32 SysCntVal; // The value of SysTick counter you are trying to retrieve
 

   

float x=1.0f; //some variable to work with    
        
      SysTick_Config(SYSTICK_MAXVAL); //reset counter set to max value, 1-time,  will not reload

   

      x+=(float) 0.0001; //do something.. 

   

      SysCntVal = SYSTICK_MAXVAL - (SysTick->VAL); //get elapsed ticks (min offset 3 ticks)
           
      sprintf(strMsg1, "%d, %f\r\n", SysCntVal, x); //report result
      UART_PutString(strMsg1);

0 Likes
Anonymous
Not applicable

 "But I am puzzled as to why 2 is the offset,"

   

You have cast the value of SysTick_BASE as a pointer to 32 bit values. Each time you increment it (or add one to it) It's going to point to the next 32 bit value which is four bytes more than the previous address. If you're going to fetch the second 32 bit value past your pointer (to 32 bit values) then 2 is the correct offset. The compiler will multiply that by the size of the data it points to to come up with 8.

   

Make sense?

0 Likes
Anonymous
Not applicable

Ah, right, thanks for that.

   

Yes, that does make sense.  I will need to be more careful in how I declare things -- I am

   

 fairly new to the C language.

0 Likes