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:

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