I've made several posts in the PSOC Smarter Life Challenge, but never used the PSOC in my own designs. After I've seen the possibilities in the challenge, I came across a project at my work where I realized that PSOC could be a nice solution.

We're using some haptic feedback voice coils that need to be driven with a sine wave at a specific frequency. We're also using an EtherCAT based system that should control these motors. To make control easier, I wanted to make something where a digital input would come in, and a sine wave would come out if the input line was asserted for a channel. The other thing I wanted is to synchronize the outputs with the sine wave produced; the output should only be active for a multiple of one sine wave.

 

At first I wanted to use DMA for this, but the PSOC4 doesn't have that and the PSOC5  was too large for the design (and I wanted to play around with the PSOC4, to be honest).

After a day of tinkering, this was the result:


Noname.png

 

I'm using PWM1 to generate the PWM of the sine wave. The Digital comparator is contolling the 8 parallel D-flipflops whose outputs are AND-ed with the PWM signal. In my code, I'm writing the sine table sample number to 'DDS_count'. At the first sample, the comparators' output is changing state, and the DFFs latch the input values. The DDS_Sample_Clock determines how fast I'm going through my sine table. It generates an interrupt on which a new sample is written to the PWM block.

To get the software running, I only had to follow this post:Using Math Functions in PSoC Creator for PSoC5's GCC Compiler - Cypress to resolve an error with the linker. This post:Introduction to Interrupts for PSoC 4 Hardware Blocks - Cypress helped me to get going with the interrupts. Here's the code:

 

#include <project.h>
#include <math.h>
#define DDS_LENGTH 100
uint8_t dds_table[DDS_LENGTH] = {0,10,20,30,40,50,60,70,80,90};

CY_ISR(DDS_sample_clock_timer)
{   
    uint32 int_source;
    int_source = DDS_Sample_Clock_GetInterruptSourceMasked();
    DDS_count_Control = DDS_count_Read() + 1;
    if(DDS_count_Read() == DDS_LENGTH)
        DDS_count_Control = 0;
    PWM_1_WriteCompare(dds_table[DDS_count_Read()]<<8);
    DDS_Sample_Clock_ClearInterrupt(int_source);
     
}

void CreateSineTable(void)
{
    int i;
    for(i = 0 ; i < DDS_LENGTH ; i++)
    {
        dds_table[i] = 126*(1.0-cos(i*6.28/DDS_LENGTH));
    }
}


int main()
{
    CreateSineTable();
    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    PWM_1_Start();
    //DDS_max_count_Control = DDS_LENGTH;
    DDS_count_Control = 0;
    DDS_Sample_Clock_Start();
    DDS_Sample_Clock_SetInterruptMode(DDS_Sample_Clock_INTR_MASK_TC);
    DDS_sample_clock_timer_Start();
    DDS_sample_clock_timer_StartEx(DDS_sample_clock_timer);
    CyGlobalIntEnable;  /* Uncomment this line to enable global interrupts. */
    for(;;)
    {
        /* Place your application code here. */
    }
}

 

That looks simple doesn't it? To enhance the resolution, I only need to increase DDS_LENGTH, and change the DDS_Sample_Clock period.

 

The only thing that I've encountered is that it sometimes would be nice to get access to one of the values in a building block in the CySch directly. For example, it would be nice to directly use the output of an ADC to the DDS_Sample_clock Period value to change the output frequency. I don't  kinow whether/how that's possible