Trouble Migrating a PSoC4 Project to PSoC5 - Variable PWM

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

cross mob
lock attach
Attachments are accessible only for community members.
Anonymous
Not applicable

Hello again, everyone!

I have an application that requires me to run independent PWM frequencies of multiple valves.  Maximus pointed me to a thread that had his code doing something very similar so I thought if I could move his project to a PSoC5, I could figure out the best way to implement a similar approach in my existing project.  Unfortunately, when I moved his project over, there were a few issues (particularly with the PWM module not being compatible) so I tried to fix it best I can.  Unfortunately, it doesn't seem to work as expected.

Below is his block diagram (taken from http://www.cypress.com/forum/psoc-4-architecture/using-single-pwm-block-drive-multiple-pins-using-de..., thanks a lot, Maximus!):

Below is my updated block diagram:

pastedImage_1.png

I tried to leave as much the same as possible but since a lot of the outputs have changed, it might not be doing what I expected it to (it's very possible I have the wiring incorrect).

I took a look at the output and the counter seems to be working just fine (you can see P_00 counting every 4 signals) and the PWM output IS alternating between the output pins, so that portion is correct.

The issue seems like lie with the PWM output.  First off, I'm getting what appears to be negative voltages being generated (not sure if it's just ringing because it's moving too fast? or if the issue is with something I changed in code).

There is one line of code that I commented out that I honestly didn't know what it did (it was giving me an error and I was having trouble resolving it).

Below is the line I commented out in the ISR file:

//PWM_ClearInterrupt(PWM_INTR_MASK_TC);

It appears to be clearing the TC interrupt but I am guessing this is a relic from the previous PWM setup since the counter seems to be working as expected.

Below are some screenshots of my scope.

Yellow:  P_00 (looks to be working correctly)

Blue:  P_01 (seems to have some sort of ringing issue)

pastedImage_2.png

Yellow:  p0 (still shows the ringing issue coming from the PWM output.  Frequency is not changing)

Blue:  p1 (same issues as p0)

pastedImage_5.png

I'm attaching the project file of the updated project.  Any help someone could give me to help resolve this would be greatly appreciated.   This is such a great forum and I've learned so much from it already!  Thanks for all your help!

0 Likes
1 Solution

jeremy,

in the example, the period for all 4 PWMs was set to 20ms, with max allowable duty cycle 25% (5ms) (see picture attached). The Yellow trace (top line) shows PWM output on line 1 at max output, its pulse length will not go beyond 5ms. After that, the mux switches PWM output to next channel #2, #3, #4 and returns to #1. This is convenient for controlling servos, since they never need 100% DC.

SeqPWM_04_b..jpg

View solution in original post

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

At first you'll need to clear the PWM's interrupt flag by reading the status using PWM_ReadStatusRegister() API.

Your clock is not running as exact as you want to: 204.8kHz cannot be derived exactly from the clock. I would suggest to set the IMO to 40.1 MHz which is a bit more precise.

There is no safety logic in case the 2-bit counter and the ISR logic run out of sync. you may consider using a status register and check the counter for zero.

Bob

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

Bob,

You are correct, changing the resetting the PWM interrupt did get it up and working again, which makes sense in hindsight.

I ended up changing the clock frequencies around a bit to match the project I'm implementing it in so now the clock frequencies look like this:

pastedImage_0.png

I didn't see the option of 40.1 MHz thoug for the IMO.  My options were 3, 6, 12, 24, 48, and 62.6 MHz.

I ran the code and got a different output than I originally expected.  This is what Maximus' code looked like (scans through whole PWM duty cycle):

Cypress PSoC4M 4x10-bit PWM outputs using single TCPWM block - YouTube

This is what mine looked like (starts at 50% DC, goes to 100%, stalls, and then returns):

PWM output issues - YouTube

I went through debugging the code and it appears the that channel 1 starts at 0x200 (50% DC) and goes up to 100%, then sits there for a while so it appears to be an algorithm issue . . . except his video seems to suggest it works, which makes me think I have something wrong on my end (which is likely the case).

His code is shown below for the PWM update function:

void UpdatePWMvalues()

{

    //p1_movement = 1000;    //debugging..

    //p2_movement = 511;

    //p3_movement = 255;

    //p4_movement = 127;

  

    // PWM = offset + ampl * sine (W*T + phase)

    p1_movement = GetPWMvalue(512, 511, Omega * Time );

    p2_movement = GetPWMvalue(256, 255, Omega * Time );

    p3_movement = GetPWMvalue(128, 127, Omega * Time );

    p4_movement = GetPWMvalue(256, 255, 2.0 *Omega * Time );

}

//=========================================================

// PWM = offset + ampl * sine (phase)

//=========================================================

uint16_t GetPWMvalue(uint16_t offset, uint16_t amplitude, uint16_t phase)

{

    //float sine = sin( (float) (2.0*M_PI/1024.0) * phase );    // using math lib, total 1230us(@24MHz)

    float sine = mysine( phase );                              // using lookup table, total 650us(@24MHz)

    uint16_t result = offset + (uint16_t)(amplitude * sine);

  

    return (result & 1023); //trim to PWM period

}

float mysine_A1023_N1023(uint16_t THETA) // THETA = 0 to 1023

{

    const float scale = 1.0f/1023.0f; // sine peak amplitude -1023..0..1023

    uint16_t THETA_HLP;

    uint16_t THETA_TMP;

    uint16_t SINE_TMP;

    int16_t SINE_OUT; //signed

    

    //THETA = (THETA & 1023); // trim to 0-1023  

      

    if ((THETA & 511) == 256)                //At 90 degrees and 270 degrees

        SINE_TMP = 1023;                    // Sine peak amplitude

    else

    {

      

        if ((THETA & 256) == 256)            //bit MSB-1: 2&4 quadrants, reverse counting order

        {

            THETA_HLP = 256 - (THETA & 255);

            THETA_TMP = (THETA_HLP & 255);

        }  

        else                                //1&3 quadrants, Continue counting up by default

            THETA_TMP = (THETA & 255);

      

        //THETA_HLP = 256 - (THETA & 255);  //alternative - same result

        //THETA_TMP  = ((THETA&256)==0)? (THETA & 255) : (THETA_HLP & 255);  //

        SINE_TMP  = sine_1023_256[THETA_TMP] + (THETA_TMP<<2);  //restore sine from lookup table

    }      

    

  

    SINE_OUT = ((THETA & 512) == 0)? SINE_TMP : -SINE_TMP; // if (bit 8 == 0): 3&4 duadrant, else 1&2 quadrant

    return (SINE_OUT * scale); // sine -1..0..+1, resolution -1023..0..1023

}

Realistically, I'm not planning on using the SINE portion of this code but if I'm doing something wrong, I definitely want to try and catch it prior to running into these issues on my project.  If I can rule it out as algorithm related though, it won't directly affect me (though, admittedly, I'd like to understand the problem a bit more).  If it is timing related, then I'm clearly missing something crucial about the functionality of the code (maybe it's assuming it's working on a period of 20 ms and my clock isn't generating correctly and is scaled incorrectly?  Somehow there's a 50% offset?).

I'm uploading the updated code with the fixed timing and interrupt.  If anyone has the time to glance over it and help me understand what I'm missing.  Thanks for all your input!

Thanks for your help user_342122993​ and bob.marlowe​!

0 Likes

jeremy,

the issue with PSoC4 was not having enough space for multiple PWMs. But using PSoC5 you can implement multiple PWMs without much troubles. Is there a reason to use sequential PWM approach?

/odissey1 

0 Likes
Anonymous
Not applicable

Heh, yeah, and the reason is stupid.  Scope creep. 

Originally, the plan was set to have a single PWM period MUXed to 16 different valves.  I got that up and working easily enough and created a PCB for it.

After I ordered the PCB, it turns out they now want the ability to have independent periods.  Not a huge deal except for the fact that each valve pulls 250 mA of current and the trace widths I chose will only support 1A of current (it was never designed to be able to potentially have all 16 valves being used at the same time).

My options are to split them up into 4 independent PWM modules (which is still doable) but I still run into the issue that some of them are going to need to be able to operate from the same PWM module, which is why I'm looking at this solution.

I think next revision, I'll just move from a 2 layer board to a 4 layer board but that'll double the PCB cost.  That and the power supply needs to handle 4 amps of current so we go from a $15 supply to an $85+ supply so we'll see if they still end up wanting to pursue that down the road.

For now, I'm just looking for an interim solution.  It looks like I was able to take the above concept and apply it to my current solution, just hoping to understand what I'm missing in this example for learning purposes.

Anyway, thanks for the response, user_342122993​!

0 Likes

jeremy,

The "sequential PWM" was made to control servos. They typically require 0-2ms control pulse repeated at 50Hz (20ms period). So max duty cycle is 10%, and the rest of the time the PWM can be switched to control other servos. Up to 10 servos can be controlled, assuming that PWM duty cycle for each servo is not exceeding 10%, so the total sum of all DCs is less then 100%.

If you are trying to have this way 0-100% duty cycle for all PWMs outputs, that is simply not possible, as sum of all DCs must be less than 100%. For example, four sequential PWMs can each have maximum 25% duty cycle.

/odissey1

P.S. From past experience, 30AWG Teflon wire blows up at current ~10A! So the PCB will likely to survive 1 A current on each valve. So my advise is to put 16 8-bit PWMs and control each independently.

0 Likes
Anonymous
Not applicable

I'm not entirely sure I understand.  The video you shared on youtube showed it going from 0% DC to 100% DC, I thought (5 ms at a time with some space in between).  Did I misunderstand the video you shared or was that a demo for a different piece of code?

Realistically, I don't think my timing will be anything near as small as 2 ms (wouldn't work with the valves, from what I've seen).  I think I'm in the 100+ ms range.

The way I'm currently doing it to allow different pulse lengths for each valve is I'm basically setting the DC as 100% and changing the period, then switching between valves.  Realistically, I'm no longer even really using PWM as intended for this (I'm guessing there's probably a better module to use for this method). It's just what I had to do to try and adjust to the changing requirements. 

The trace width for the power lines is 24 mils, which, according to ExpressPCB's PCB tips (Tips for Designing PCBs - ExpressPCB ), puts me at just under 1A of current.  If I had done this board the right way, I would've made it a 4 layer board with a ground and power layer and then I wouldn't have to worry about trace widths.  Because I only thought I needed to power it one at a time, I have them daisy chained so there's a possibility of multiple valves going off at once and causing too much current over the line.

Realistically, my next revision will do exactly that (using a power plane eliminates concern about trace width).  Alternatively, I could cut all the ground and power traces and replace them with 23 gauge wire or smaller (it looks like that's what I'd need to use to be able to handle 4A of current) but, at that point, I might as well just order new PCBs.

So, based on the above, is there another module I should be using?  I actually could probably do it all with software, turn on a signal for x amount of time and then manually count through the lines but I'd rather do it with hardware pieces, given the choice.  What I'm using seems to work just fine, I guess but if there's a better way of doing it, I'd like to learn to do it the "right" way (even if this is a stop gap).  No reason not to turn this into a learning situation!

0 Likes

jeremy,

in the example, the period for all 4 PWMs was set to 20ms, with max allowable duty cycle 25% (5ms) (see picture attached). The Yellow trace (top line) shows PWM output on line 1 at max output, its pulse length will not go beyond 5ms. After that, the mux switches PWM output to next channel #2, #3, #4 and returns to #1. This is convenient for controlling servos, since they never need 100% DC.

SeqPWM_04_b..jpg

0 Likes
Anonymous
Not applicable

Oh, ok, I thought you meant in terms of each individual signal.  I follow what you're sharing.  Did you get a chance to watch the video I shared?  Mine was trying to go past the 25% point and unable to get below the 12.5% point (if that makes sense).  Here's the video I was referring to (from a previous post):

PWM output issues - YouTube

What you're saying makes perfect sense, my issue is I couldn't get mine to behave like yours.  Not sure if it's related to my clock settings, PSoC hardware differences between the 4 and 5, or an algorithm problem (the latter doesn't make much sense as it seems to have worked just fine with your project).

Let me know if my issue makes any more sense now.  Thanks again!

0 Likes