How to generate a 45 kHz signal with certain specifications.

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

cross mob
HappyPotamus
Level 1
Level 1
10 sign-ins 5 sign-ins First reply posted

Hello everyone,

 

I am new to PSoC and embedded systems in general. I am using a CY8CPROTO-063-BLE PSoC 6 PROTOTYPING KIT. I am trying to generate a signal of 45 kHz over 1 second, where it pulses for 10 ms of the entire second and the rest is a flat line. I have been attempting to replicate this small project for a long time, and I am really frustrated (Sorry I am a newbie and don't know nor understand much about PSoC). 

HappyPotamus_0-1631188547782.png

This is basically similar to an example provided by the company itself, but they did not include any code with it. I guess the idea here is to use a timer counter (OneSecTImer in the pic) to time the other timer counter (Counter in the pic) to work every second, whereas the PWM (FreqGen in the pic) is what generates a signal for the Counter to capture. I am not sure if I am supposed to use a UART or not, but I included it and have been using printf() function to print the desired outputs to PuTTY program. The clocks of the FreqGen and Counter are at 1 MHz, whereas the clock for the OneSecTimer is at 1 kHz (I am not sure if these values are acceptable).

HappyPotamus_1-1631188765502.png

This picture is the settings for the OneSecTimer (also I am not sure if I am choosing the correct period, but I am aware that the count starts from 0 hence why I used 999u).

 

HappyPotamus_1-1631193398402.png

These are the settings for the PWM FreqGen. I am sure I had done the period wrong, as I had assumed that the period is supposed to be related to the desired 45 kHz output. And I had read on some other post that the compare should be half of the period (In the code I used one of the PDL commands to rewrite the period and compare).

 

Counter settings.png

These are the settings for the Counter, with the count input mode as rising edge. I am not sure if my settings will lead me to me to my desired goal.

 

This is the code I am using in the CM4, and nothing on CM0 other than the default generated code.

 

 

#include "project.h"
#include <stdio.h>

//----NOT SURE IF THESE ARE NECESSARY AND WHAT IS THEIR ROLE EXACTLY----//
#define PWM_FreqGen (PWM_FreqGen_HW)
#define OneSecTimer (OneSecTimer_HW)
#define Counter (Counter_HW)
#define MY_Counter_CNT_NUM   (0UL)
#define MY_Counter_CNT_MASK  (1UL << MY_Counter_CNT_NUM)
#define MY_TCPWM_FreqGen_NUM   (0UL)
//----------------------------------------------------------------------//

void Counter_Int_Handler()                                   //Counter interrupt handler
{
    NVIC_ClearPendingIRQ(Counter_Int_cfg.intrSrc);
}

int main(void)
{
    __enable_irq();                                          //Enabling global interrupts
    UART_Start();           
    OneSecTimer_Start();
    FreqGen_Start();
    Counter_Start();
    
    Cy_SysInt_Init(&Counter_Int_cfg,Counter_Int_Handler);   //Calling the interrupt but what for?
    NVIC_EnableIRQ(Counter_Int_cfg.intrSrc);
    
    float period = 25;
    float compare = period/2;
    
    printf ("\ec");                                         //Clearing previous PuTTY outputs
    printf("Link successful\r\n");
    
    for(;;)
    { 
        Cy_TCPWM_PWM_SetPeriod0(FreqGen_HW,FreqGen_CNT_NUM,period);     
        Cy_TCPWM_PWM_SetCompare0(FreqGen_HW, FreqGen_CNT_NUM,compare);
        
        //-----------------------------TESTING BLOCK-----------------------------//
        float status = Cy_TCPWM_PWM_GetStatus(Counter_HW,Counter_CNT_NUM);
        float compare0 = Cy_TCPWM_PWM_GetCompare0(Counter_HW,Counter_CNT_NUM);
        float compare1 = Cy_TCPWM_PWM_GetCompare1(Counter_HW,Counter_CNT_NUM);
        float period0 = Cy_TCPWM_PWM_GetPeriod0(Counter_HW,Counter_CNT_NUM);
        float period1 = Cy_TCPWM_PWM_GetPeriod1(Counter_HW,Counter_CNT_NUM);
        
        float statusmask = Cy_TCPWM_PWM_GetStatus(Counter_HW,Counter_CNT_MASK);
        float compare0mask = Cy_TCPWM_PWM_GetCompare0(Counter_HW,Counter_CNT_MASK);
        float compare1mask = Cy_TCPWM_PWM_GetCompare1(Counter_HW,Counter_CNT_MASK);
        float period0mask = Cy_TCPWM_PWM_GetPeriod0(Counter_HW,Counter_CNT_MASK);
        float period1mask = Cy_TCPWM_PWM_GetPeriod1(Counter_HW,Counter_CNT_MASK);
        //-----------------------------------------------------------------------//
        
        if(status >= 45000){ //What I actually want this condition to be is to acces this statement when frequency is 45 kHz or greater
            printf("First condition loop executed.\r\n");
            printf("status: %f",status); printf("\n\r");
            printf("compare0: %f",compare0);printf("\n\r");
            printf("compare1: %f",compare1); printf("\n\r");
            printf("period0: %f",period0); printf("\n\r");
            printf("period1: %f",period1); printf("\n\r");
            float GeneratedSignal = Cy_GPIO_Read(GeneratedSignal_PORT,GeneratedSignal_NUM);
            printf("GeneratedSignal: %f",GeneratedSignal); printf("\n\r");
            
            printf("Using mask:\r\n");
            printf("statusmask: %f",statusmask); printf("\n\r");
            printf("compare0mask: %f",compare0mask);printf("\n\r");
            printf("compare1mask: %f",compare1mask); printf("\n\r");
            printf("period0mask: %f",period0mask); printf("\n\r");
            printf("period1mask: %f",period1mask); printf("\n\r");
            
            Cy_GPIO_Write(RED_PORT,RED_NUM,!Cy_GPIO_Read(RED_PORT,RED_NUM));    
            CyDelay(500);
            Cy_GPIO_Write(RED_PORT,RED_NUM,!Cy_GPIO_Read(RED_PORT,RED_NUM));
            CyDelay(500);
                                } 
        
        else {                                                                  //Ignore the else statement
            //printf("Signal out of desired range!\r\nCurrent Signal value of Status = %f",status); printf(" Hz\r\n"); 
            //printf("Compared to a signal of %f",capture); printf(" Hz\r\n");
            printf("First condition loop executed.\r\n");
            printf("status: %f",status); printf("\n\r");
            printf("compare0: %f",compare0);printf("\n\r");
            printf("compare1: %f",compare1); printf("\n\r");
            printf("period0: %f",period0); printf("\n\r");
            printf("period1: %f",period1); printf("\n\r");
            float GeneratedSignal = Cy_GPIO_Read(GeneratedSignal_PORT,GeneratedSignal_NUM);
            printf("GeneratedSignal: %f",GeneratedSignal); printf("\n\r");
            
            for (int i = 9; i >= 0; i--){
            Cy_GPIO_Write(RED_PORT,RED_NUM,!Cy_GPIO_Read(RED_PORT,RED_NUM));
            CyDelay(100);
                                        }
              }
        printf("Next cycle~\r\n");
        CyDelay(1000);
        //Cy_TCPWM_PWM_Disable(FreqGen_HW, FreqGen_CNT_NUM); Do I need to disable and enable this?
    }
}

 

 

 I had tried many different readable parameters as an attempt to find the generated signal, but I am left with so many question. Before the questions, here is the picture of the output on PuTTY. Another note, retarget I/O is ticked.

HappyPotamus_2-1631194041903.png

I have no idea what most of these numbers output actually mean as the numbers seem like gibberish right now.

For my questions:

1) Is the logic of the build correct and what are the settings that I need to change.

2)If all the settings are now correct, would the attached code output the actual generated signal by the PWM?

3)How do I modify the code or correct so it pulses the 45 kHz signal over 10 ms out of a whole second.

4)Why does the PWM interrupt cause the loop to execute only once when the interrupt is called in void main in the first place?

5)When I do comment out the interrupt, the entire loop keeps on looping correctly with the LEDs blinking as I had written in the code. On the other hand, when I do include the interrupt, only the printf() functions execute and then it feels like the rest of the loop is frozen and not executing.

6)Do I need to add an ADC in order to actually be able to read and generate a signal or is the logic build provided sufficient?

I know this is very long, and thank you to everyone who will help or just tag a long in the journey of learning PSoC and solving this small project.

Best regards,

Issa Hoballah.

0 Likes
1 Solution
Ekta_N
Moderator
Moderator
Moderator
750 replies posted First like given 250 solutions authored

Hello @HappyPotamus 

As per my understanding, you want to generate a pulse signal with which has a frequency of 45 kHz for a period of 10 ms? Let me know if my understanding is correct.

In this case you can have a simple setup:

1. You should have timer (Input clock = 1Mhz, Period = 9999u). Configure the timer to generate an interrupt on overflow. With this configuration the timer should generate an interrupt every 10 ms. So, this can be used to keep a track of the 10 ms when you want to generate the 45kHz signal.

Ekta_0-1631605381271.png

2. Use a PWM to generate the 45kHz signal. Say, if the PWM is clocked at 1Mhz then setting the period as 21u and compare as 11u will almost give a 45 Mhz signal with a duty cycle of 50 % as can we seen in the image below.

 

Ekta_1-1631605478066.png

The pwm output signal in this case would give you your desired signal.

3. Finally as per my understanding you want a flat signal after 10ms since you have mentioned "where it pulses for 10 ms of the entire second and the rest is a flat line". You can create an interrupt handler for the timer which will be triggered after 10 secs (on overflow) and disable the PWM in it so that you get a flat line.

In order to understand how to deal with the timer and interrupt you can refer to the following code example: https://www.cypress.com/documentation/code-examples/ce220169-psoc-6-mcu-periodic-interrupt-using-tcp... which shows TCPWM Component configured as a Timer/Counter to generate a periodic interrupt. And perform the desired task whenever the interrupt occurs.

Thanks 
Ekta

View solution in original post

0 Likes
5 Replies
Ekta_N
Moderator
Moderator
Moderator
750 replies posted First like given 250 solutions authored

Hello @HappyPotamus 

As per my understanding, you want to generate a pulse signal with which has a frequency of 45 kHz for a period of 10 ms? Let me know if my understanding is correct.

In this case you can have a simple setup:

1. You should have timer (Input clock = 1Mhz, Period = 9999u). Configure the timer to generate an interrupt on overflow. With this configuration the timer should generate an interrupt every 10 ms. So, this can be used to keep a track of the 10 ms when you want to generate the 45kHz signal.

Ekta_0-1631605381271.png

2. Use a PWM to generate the 45kHz signal. Say, if the PWM is clocked at 1Mhz then setting the period as 21u and compare as 11u will almost give a 45 Mhz signal with a duty cycle of 50 % as can we seen in the image below.

 

Ekta_1-1631605478066.png

The pwm output signal in this case would give you your desired signal.

3. Finally as per my understanding you want a flat signal after 10ms since you have mentioned "where it pulses for 10 ms of the entire second and the rest is a flat line". You can create an interrupt handler for the timer which will be triggered after 10 secs (on overflow) and disable the PWM in it so that you get a flat line.

In order to understand how to deal with the timer and interrupt you can refer to the following code example: https://www.cypress.com/documentation/code-examples/ce220169-psoc-6-mcu-periodic-interrupt-using-tcp... which shows TCPWM Component configured as a Timer/Counter to generate a periodic interrupt. And perform the desired task whenever the interrupt occurs.

Thanks 
Ekta

0 Likes
HappyPotamus
Level 1
Level 1
10 sign-ins 5 sign-ins First reply posted

Hello @Ekta_N ,

First of all, I want to thank you so much for taking from your time to read and reply to my issue and helping.

Your understanding is correct, that is exactly what I am intending to do.

Regarding the PWM signal, is it possible to give the period and compare float values? I am asking this as the application I intend on implementing requires high accuracy. If I cannot give it a float input from the configuration table, is it possible to pass a float for the Period0 and Compare0 values using: Cy_TCPWM_PWM_SetPeriod0(PWM_HW,PWM_CNT_NUM,22.22);
Cy_TCPWM_PWM_SetCompare0(PWM_HW, PWM_CNT_NUM,11.11);

if it is not possible, is there another way to generate more accurate signals?

Also, is it possible to just connect a digital output pin to the PWM output and print it to PuTTY using printf command? And if I want to read the values using a physical oscilloscope, how can I do that?

I tried implementing you suggestion, and I don't think I am doing the code part correctly.

 

HappyPotamus_0-1631615520383.png

This is the build with identical setting according to your suggestions, and I added a UART so I can output to PuTTY and a LED for testing purposes.

The code is as follows:

 

#include "project.h"
#include <stdio.h>

void Counter_Int_Handler()                                   //Counter interrupt handler
{   
    PWM_Enable();
    float generatedb4disable = Cy_GPIO_Read(GEN_PORT,GEN_NUM);
    printf("Signal before disable: %f",generatedb4disable);
    printf(" Hz \r\n\n");
        
    Cy_GPIO_Write(LED_PORT,LED_NUM,!Cy_GPIO_Read(LED_PORT,LED_NUM));
    PWM_Disable();
    CyDelay(10);
    Cy_GPIO_Write(LED_PORT,LED_NUM,!Cy_GPIO_Read(LED_PORT,LED_NUM));
    
    float generateddisable = Cy_GPIO_Read(GEN_PORT,GEN_NUM);
    printf("Signal after disable: %f",generateddisable);
    printf(" Hz \r\n\n");
    
    NVIC_ClearPendingIRQ(SysInt_1_cfg.intrSrc);             //Clearing the interrupt
    CyDelay(990);
}

int main(void)
{
    UART_Start();  
    Timer_Start();
    PWM_Start();
    Cy_GPIO_Write(LED_PORT,LED_NUM,1);
    
    printf ("\033[3J");                                     //Clearing scrollback buffer
    printf ("\ec");                                         //Clearing previous PuTTY outputs
    printf("UART link established \r\n\n");
    
    __enable_irq(); 
    for(;;)
    {
        /*float generated = Cy_GPIO_Read(GEN_PORT,GEN_NUM);
        printf("Signal before handler: %f",generated);
        printf(" Hz\r\n");*/
        
        Cy_SysInt_Init(&SysInt_1_cfg,Counter_Int_Handler);   //Configuring the interrupt
        NVIC_EnableIRQ(SysInt_1_cfg.intrSrc);                //Calling the interrupt handler
        
        /*float generatedhandler = Cy_GPIO_Read(GEN_PORT,GEN_NUM);
        printf("Signal after handler: %f",generatedhandler);
        printf(" Hz \r\n\n");
        
        Cy_GPIO_Write(LED_PORT,LED_NUM,!Cy_GPIO_Read(LED_PORT,LED_NUM));
        CyDelay(1000);
        Cy_GPIO_Write(LED_PORT,LED_NUM,!Cy_GPIO_Read(LED_PORT,LED_NUM));*/
    }
}

 

First code note is that why doesn't the interrupt stop executing and go back to the main for loop? the interrupt is executing itself in an infinite loop.

Second code note, if the first note is resolved, would the logic behind the code I had written work? or am I still not grasping the correct concept?

Third, I still can't read the output from the PWD, is my approach also wrong regarding that?

Best regards and thanks,

 HappyPotamus.

 

0 Likes
HappyPotamus
Level 1
Level 1
10 sign-ins 5 sign-ins First reply posted

Update, thank you again @Ekta_N , I think got it to work now, I am able to receive a 10 ms square wave signal from the PWM once every second at a 45 kHz frequency 🙂  But I think I still have the problem where I am forever in the handler and never going back to the for loop, how can I fix that?

I will paste my final code in case someone needs it.

 

#include "project.h"
#include <stdio.h>

void Counter_Int_Handler()                                                //Counter interrupt handler
{   
    printf("Handler is here \r\n");
    Cy_TCPWM_ClearInterrupt(Timer_HW, Timer_CNT_NUM, CY_TCPWM_INT_ON_TC); //Clearing the interrupt
    Cy_GPIO_Inv(LED_PORT, LED_NUM);
    CyDelay(10);
    Cy_GPIO_Inv(LED_PORT, LED_NUM);
    PWM_Disable();
    CyDelay(990);
    PWM_Enable();
    printf("Just making sure \r\n\n");
}

int main(void)
{
    UART_Start();  
    Timer_Start();
    PWM_Start();
    
    Cy_GPIO_Write(LED_PORT,LED_NUM,1);
    
    Cy_SysInt_Init(&SysInt_1_cfg,Counter_Int_Handler);                    //Configuring the interrupt
    NVIC_ClearPendingIRQ(SysInt_1_cfg.intrSrc);                           //Clearing the interrupt
    NVIC_EnableIRQ(SysInt_1_cfg.intrSrc);                                 //Calling the interrupt handler
    __enable_irq();                                                       //Enable global interrupts
    
    //(void)Cy_TCPWM_Counter_Init(Timer_HW, Timer_CNT_NUM, &Timer_config);
    Cy_TCPWM_Enable_Multiple(Timer_HW, Timer_CNT_MASK);  //
    Cy_TCPWM_TriggerReloadOrIndex(Timer_HW, Timer_CNT_MASK); 
    
    printf ("\033[3J");                                                   //Clearing PuTTY's scrollback buffer
    printf ("\ec");                                                       //Clearing previous PuTTY outputs
    printf("UART link established \r\n\n");
    
    __enable_irq(); 
    for(;;)
    {   
        Cy_SysPm_Sleep(CY_SYSPM_WAIT_FOR_INTERRUPT);
    }
}

 

0 Likes
Ekta_N
Moderator
Moderator
Moderator
750 replies posted First like given 250 solutions authored

Hi @HappyPotamus ,

Glad that the suggestion worked for you.

Could you let me know if you have configured the timer to run continuously? In that case, the counter will count till period generate an interrupt on overflow and the whole cycle will be repeated again. So the interrupt is always called. There are two ways to resolve:

1. Configure the timer/ Counter as a oneshot.

2. Set a flag in the interrupt handler when you disable the PWM. In the main for loop disable the timer if the flag is set.

Regards
Ekta 

 

0 Likes

Hi again @Ekta_N ,

Yes, I have configured it to run continuously, I didn't know that is how the logic behind it works. I will try both suggestions and tell you if either or both of them work, but first, I will learn what flags are in the first place.

EDIT: I tried to configure the timer as one shot. It does execute the entire for statement once, but then won't loop again.

I am learning the concept of flags now, I will try to apply it and edit the reply or write a new one then.

Regards,

HappyPotamus.

0 Likes