Shop OBEX P1 Docs P2 Docs Learn Events
Need Help with Simple PWM Program. — Parallax Forums

Need Help with Simple PWM Program.

Patrick ColemanPatrick Coleman Posts: 43
edited 2016-03-07 05:37 in Propeller 1
Hello !

Background:
Im needing help with some programming. I need to move pat the basic stamp series, and I don't like the STM32 / arm development environment. its really convoluted.
Id like to learn this new Propeller IC and its C / SPIN programming.

Question:
Is there a simple PWM program I can write which follows the pseudo code below (I need it in C, I think its faster than spin):
Start

Pin Z high for X microseconds.
Pin Z low for Y microseconds.

repeat endlessly . . .

I need to start learning, but cant find any examples, but for those that are for steppers and servos.

I also don't want the "count-clock-cycles-and-waste-them" program method, that's lame.
«1

Comments

  • The Simpletools Library has 3 built in C functions,pwm_start, pwm_set, and pwm_stop that makes using PWM on a Propeller 1 easy and painless. pwm_start sets up a PWM function in another cog and you decide the length of the PWM cycle in microseconds. pwm_set gives you two channels of PWM and you decide the output pin, the channel, and the length of the high time. pwm_stop just shuts the function down and releases the cog.
    To get a description of each of these three functions start up SimpleIDE and click on the HELP button at the top. Select Simple Library Reference and in the following page near the top and click on simpletools.h under the Utilities heading. Scroll down to the Timed I/O subsection and find pwm_start, pwm_set, andpwm_stop. Click on these links for a detailed description.
  • When you say "count clock cycles and waste them", that might mean different things to different peoplel.

    On the Prop it's pretty normal to use waitcnt in a loop to get precise timing, so I'd write your code like this:
    pub PWM | highTime, lowTime, loopCount
    
      highTime = (_clkfreq / 1_000_000) * HighMicros;  'assumes HighMicros and LowMicros are already defined
      lowTime = (_clkfreq / 1_000_000) * LowMicros;
    
      loopCount = cnt;  'grab the current clock time
    
      repeat
        outa[pin] := 1;
        loopCount += highTime;
        waitcnt(loopCount);
    
        outa[pin] := 0;
        loopCount += lowTime;
        waitcnt(loopCount);
    

    You'd have to be careful to keep the high & low times to probably 10 microseconds or better, because Spin isn't terribly fast.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2016-03-07 20:06
    Depending on your pwm frequency (< ~35kHz), you can run a Spin cog and use the two counters to take care of the PWM waveform. I do this all the time for dual motor control, without the fuss of PASM or the mania of C.
  • Patrick ColemanPatrick Coleman Posts: 43
    edited 2016-03-08 06:05
    Im trying to use :
    #include "simpletools.h" // Include simple tools

    int main() // Main function
    {

    while(1);
    {
    low(15);
    pulse_out(15,17); // pin 15, 17 uS

    high(15);
    pulse_out(15,23); // pin 15, 23 uS
    }
    }

    but my oscilloscope says it doesn't work ?

    im also looking here: https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries/Utility/libsimpletools/html/simpletools_8h.html#a57f683188c3dd35ee0574ff349835682
  • DavidZemonDavidZemon Posts: 2,973
    edited 2016-03-08 14:55
    Im trying to use :
    #include "simpletools.h" // Include simple tools

    int main() // Main function
    {

    while(1);
    {
    low(15);
    pulse_out(15,17); // pin 15, 17 uS

    high(15);
    pulse_out(15,23); // pin 15, 23 uS
    }
    }

    but my oscilloscope says it doesn't work ?

    im also looking here: https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries/Utility/libsimpletools/html/simpletools_8h.html#a57f683188c3dd35ee0574ff349835682

    Depending on the memory model, that may not be enough time. Try bumping it to 100 us and then if that doesn't work, 10,000 us.
  • It's not terribly useful to have your main loop consumed with a pwm output -- in the Propeller, it's best to launch a cog. The counters can be setup for PWM by running a simple loop. As I indicated, if your PWM frequency is lower than about 35kHz, you can do this in Spin (I don't program in C). For simple things like this I will create an embedded object. Here's the code (full file attached) to create a simple 2-channel PWM generator in Spin.
    var
    
      long  duty1
      long  duty2
      long  period
    
      long  pwmstack[32]
      
    
    pub start_pwm(p1, p2, freq)
    
      period := clkfreq / (1 #> freq <# 35_000)                     ' limit pwm frequency
    
      cognew(run_pwm(p1, p2), @pwmstack)                            ' launch pwm cog
    
    
    pub set_duty(ch, level)
    
      level := 0 #> level <# 100                                    ' limit duty cycle
    
      if (ch == 1)
        duty1 := -period * level / 100
      elseif (ch == 2)
        duty2 := -period * level / 100    
      
    
    pri run_pwm(p1, p2) | t                                         ' start with cognew
    
      if (p1 => 0)
        ctra := (%00100 << 26) | p1                                 ' pwm mode
        frqa := 1                            
        phsa := 0
        dira[p1] := 1                                               ' make pin an output
    
      if (p2 => 0)
        ctrb := (%00100 << 26) | p2
        frqb := 1
        phsb := 0
        dira[p2] := 1
    
      t := cnt                                                      ' sync loop timing
      repeat
        phsa := duty1
        phsb := duty2
        waitcnt(t += period)
    
  • Hal AlbachHal Albach Posts: 747
    edited 2016-03-08 18:19
    And if you wish to see it implemented in C:
    #include "simpletools.h"
    
    //    Global variables
    volatile int pwmR          = 0;           // global motor 
    volatile int pwmL          = 0;           //    speed variables
    
    #define pwmB                20            // pin 20 Left Motor PWM
    #define pwmA                24            // pin 24 Right motor PWM
    
      //  more definitions as needed
    
    void main void
    {
      #define PWM_PERIOD 100                  // 100 micro seconds
    
      pwm_start(PWM_PERIOD);                  //  Start PWM.  Per. 100 us, or 10 KHz
                                              // PWM value equals duty cycle
    
      pwmR = 50;                          // 50% duty cycle
      pwmL = 50;
    
    // turn the motors at half speed (approx.)
      pwm_set(pwmB, 0, pwmR);             // pwmA or B is the prop pin
      pwm_set(pwmA, 1, pwmL);             // pwmL or R is the pwm duty cycle
                                          // 0 or 1 is the PWM channel
    
    //  (more code & stuff)
    
    }
    
    

    Parts of this code were extracted from my robot project where I use a pair of LM18200 motor drivers.
    I do not use the pwm_stop function since the motor routines actually reside in a while(1) loop.

    Repeat the pwm_set() function when you want to change speed with a new 3rd parameter
    I tossed in a few definitions because many C programmers loathe seeing numbers in the code, but I did violate that twice here ;)
  • I totally missed that you were trying to do this in C.

    From looking at your code, I suspect that your only mistake is not setting your pin as an output. When the Prop starts up, all pins are inputs by default.

    Note that I am directly setting the pin outputs instead of using pulse_out.
    #include "simpletools.h" // Include simple tools
    
    int main() // Main function
    {
      set_pause_dt(CLKFREQ/1000000);  // set the pause unit to us (default is ms)
      set_direction( 15, 1 );         // set pin 15 as an output
    
      while(1);
      {
        low(15);      // set pin 15 low
        pause(17);    // wait for 17 uS
    
        high(15);     // set pin 15 high
        pause(23);    // wait for 23 uS
      }
    }
    

    Your original code used pulse_out(), which only does the timing for you on the high side - it turns on the output, waits, then turns it off, so you could use that instead, like this:
    #include "simpletools.h" // Include simple tools
    
    int main() // Main function
    {
      set_pause_dt(CLKFREQ/1000000);  // set the pause unit to us (default is ms)
      low(15);                        // make sure the pin starts in the low state
      set_direction(15, 1);           // set pin 15 as an output
    
      while(1);
      {
        pause(17);          // wait for 17 uS
        pulse_out(15, 23);  // set 15 high, wait for 23 uS, set it low again
      }
    }
    
  • JasonDorie wrote: »
    I totally missed that you were trying to do this in C.

    From looking at your code, I suspect that your only mistake is not setting your pin as an output. When the Prop starts up, all pins are inputs by default.

    Note that I am directly setting the pin outputs instead of using pulse_out.

    low() and high() set the DIRA register. Source
    void high(int pin)                            // high function definition
    {
      int mask = 1 << pin;                        // Set up mask
      OUTA |= mask;                               // Bitwise OR w/ OUTA & DIRA
      DIRA |= mask;
    }
    
  • I did notice, on reading the docs, that pulse_out will toggle the existing pin state, so that might make things look funny. Doesn't explain why he saw no output on his scope though, which is why I (mistakenly) thought the direction setting might be it.
  • Hello Patrick.

    Since you've used the BASIC Stamp then you will find Spin to be very easy to learn since it's about 3/4 the same as PBASIC.
    The Propeller Education Kit text is the best introduction to Spin that I've seen though I do wish Andy had used Constants more.
    Since Spin is the Propeller's native language most code is written in Spin and uses Assembly (PASM) when speed is needed.
    https://www.parallax.com/product/122-32305

    I myself never felt comfortable with C but Andy's Propeller C Tutorials on the Learn website are very well written and made C a lot friendlier.
    http://learn.parallax.com/propeller-c-tutorials

    JonnyMac mentioned the Cog Counters and Andy wrote a whole chapter on using them in the Propeller Education Text (Chapter 7).
    https://www.parallax.com/sites/default/files/downloads/122-32305-PE-Kit-Labs-Fundamentals-Text-v1.2.pdf
  • Ok im looking through your comments, really appreciate them.

    now ive got a primitive program running, my oscilloscope says so. But . . . it only runs in ram I guess. as soon as I unplug the USB cord the propeller stops working ! when I press the load to EEPROM and run button it just gives an error.

    code here:
    #include "simpletools.h" // Include simple tools

    int main()
    // Main functio
    { // Display test messa
    while(1)
    {
    low(15);

    pause(1);
    //pulse_out(15,100);
    //high(15);
    pulse_out(15,1000);
    }
    }

    its junk code but does what I want, which is a transformer waveform to play with a time period.
  • Do you have an EEPROM connected? The propeller requires an external one.
  • JasonDorie wrote: »
    Do you have an EEPROM connected? The propeller requires an external one.

    Ooops.

    so it wont run even in ram programmed then disconnected with the battery attached, and the USB cord out ?
  • kwinnkwinn Posts: 8,697
    JasonDorie wrote: »
    Do you have an EEPROM connected? The propeller requires an external one.

    Ooops.

    so it wont run even in ram programmed then disconnected with the battery attached, and the USB cord out ?

    No. Pulling out the USB cord will usually reset propeller.
  • It's not a limitation of the Prop itself, but the way the USB interface chip works.
  • its working now !
    1000 x 750 - 75K
  • Excellent! Happy to hear it.
  • Patrick ColemanPatrick Coleman Posts: 43
    edited 2016-03-13 00:34
    Well ive got a few sparks at 3-5 kV but the waveform doesn't have enough high or low range using the "pause(x)" function, which I expected.

    so im looking at your guys' code examples.

    Ive tried this:
    #include "simpletools.h" // Include simple tools

    int main() // Main function
    {
    set_pause_dt(CLKFREQ/1000000); // set the pause unit to us (default is ms)
    low(15); // make sure the pin starts in the low state
    set_direction(15, 1); // set pin 15 as an output

    while(1);
    {
    pause(17); // wait for 17 uS
    pulse_out(15, 23); // set 15 high, wait for 23 uS, set it low again
    }
    }

    but my oscilloscope says it doesn't work.
  • I gave you an example that does 25kHz PWM with variable duty cycle. Sure, it's in Spin (the Propeller's native tongue), but it works. Does your circuit care about the language you use, or getting the proper waveform?
  • Patrick ColemanPatrick Coleman Posts: 43
    edited 2016-03-13 04:45
    JonnyMac wrote: »
    I gave you an example that does 25kHz PWM with variable duty cycle. Sure, it's in Spin (the Propeller's native tongue), but it works. Does your circuit care about the language you use, or getting the proper waveform?

    I need to vary the on and off time pretty wide. Id like to use C , as I cant understand spin or how to open those file type.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2016-03-13 06:09
    Spin is pretty simple - looks like a cross between C and Basic. You use the Propeller Tool from Parallax's site to edit / compile, but if you're more comfortable with C that works just fine too.
  • I guess I should learn C, as everything I need to do is speed sensitive for my drones.
  • Patrick ColemanPatrick Coleman Posts: 43
    edited 2016-03-14 06:18
    Its still not working. How long does the pause(x) command take in clock cyles to execute? im getting a delay - - I think its in the delay time. Is there a way to use a hardware timer from one of the cogs?
  • It would do you well to learn the Propeller architecture before getting too far into your project -- if speed is the real issue at hand, nothing beats PASM. That said, you don't want to start there. Even Formula 1 drivers start in go-karts.
    I need to vary the on and off time pretty wide. Id like to use C , as I cant understand spin or how to open those file type.
    I've never been presented a specification with the term, "pretty wide." How do you define that?

    If you can't understand Spin I maintain you don't have a chance at learning C. Sorry if that seems harsh, but Spin is far easier (and in my opinion, more elegant). Spin was designed for the multi-core Propeller and is tightly aligned with the architecture. Please believe me that learning Spin will not be a waste of your time, even if C does become the best choice for your application. Learn the Propeller, then learn how to apply your language choice to it.

    FTR, delays in the Propeller are handled with a command called waitcnt -- anything offered by a HLL is a wrapper for this.
    Is there a way to use a hardware timer from one of the cogs?
    As I've stated many times, you can use the Propeller counters to generate pulses; if you do this within a precisely-time loop (possible with waitcnt), you get a nice PWM output. I've attached another demo; this one uses an external version of my 2-channel pwm engine (yes, still in Spin). This version lets you change the frequency 1 to 38kHz, and the duty cycle (0.1 to 100.0 in 0.1Hz increments). Open a terminal window to change a parameter (see the DAT section with parser info for commands).

    You can load this into and program the Propeller with Propeller Tool or Propeller IDE -- both a are available from Parallax.

    Maybe Dave or one of the other C gurus will port a version of my pwm object to a C library (I don't use C which is why I don't have the library already).
  • As Jon states, all waits in the Prop are handled by waitcnt, which waits for the internal clock to reach a specific value.

    All waits are implemented as some variant of this:
      waitcnt( CLKFREQ/1000 + CNT );
    

    It'll go a little faster if you pre-calc the delay, since CLKFREQ on the Prop isn't a constant, though it's very typically 80,000,000.

    In Spin, the language overhead per statement is just under 400 clocks, so you need to wait a minimum of that, or you'll end up waiting until the clock register rolls over, which takes about 53 seconds.

    In C, it depends on what you're compiling as. CMM is about 2x the speed of Spin. LMM is nearly machine language speed, so a wait of 30 or 40 clocks minimum would likely be enough.

    You said before "the waveform doesn't have enough high or low range using the pause() function" - Pause can wait for an arbitrary amount of time - minutes, seconds, microseconds, . . . so it seems unlikely that's an issue.

    At its simplest, PWM in C is this (most of the text is comments):
      int microsec = CLKFREQ/1000000;    // divide the clock rate by a million to get ticks per microsecond
      int highClocks = microsec * 10;    // highClocks will be 10 microseconds, or 800 clocks at 80mhz
      int lowClocks = microsec * 90;
    
      DIRA |= (1<<15);       // set pin 15 as an output by enabling bit 15 in the DIRA register
    
      while( true )          // loop forever
      {
        OUTA |= (1<<15);     // Enable bit 15 in the output register (same as high(15), but faster)
        waitcnt( CNT + highClocks );  // add highClocks to the current clock, wait for the clock to reach that value
    
        OUTA &= ~(1<<15);    // Clear bit 15 in the output register  (same as low(15) but faster)
        waitcnt( CNT + lowClocks );  // add lowClocks to the current clock, wait for the clock to reach that value
      }
    
  • Patrick ColemanPatrick Coleman Posts: 43
    edited 2016-03-16 19:46
    JonnyMac wrote: »
    I've never been presented a specification with the term, "pretty wide." How do you define that?

    1 uS increments and 200 kHz with variable duty cycle (up to 90%). 5 uS or there abouts would be 200 kHz. the STM32 has hardware timers that are programmed, I think in the way JasonDorie shows.

    My crystal says 5.000 does that mean I'm running my prop at 5MHz unless I'm upping it with a PLL ? (I don't know if I am.)
    It does seem like the code overhead is a limiting factor, I think, from what my oscilloscope says. At some point I see a 50% fast wave, that refuses to go faster in Hz (fewer uS).

  • pjvpjv Posts: 1,903
    Patrick,

    I'm with JonnyMac.

    Using PASM for programming the Propeller, you should be able to get an ON time of any number from 1 usec or longer, to as long as you like, and for an OFF time, the same thing. So just pick your numbers, and you can get 12.5 nano second granularity.

    PASM code would be in the order of a dozen or two instructions..... so, time to learn assembler. We can help if you pursue that route.

    Cheers,

    Peter (pjv)


  • pjv wrote: »
    you can get 12.5 nano second granularity

    That's only a single clock cycle at 80 MHz. You'll only be able to get that kind of granularity with a counter right? And the counters can be set from any language - spin, pasm, C, tachyon, etc etc etc
  • pjvpjv Posts: 1,903
    David,

    You can get single clock granularity from the WAITCNT instruction. But I doubt you could get a programmed ON or OFF period as short as 1 usec from Spin.

    Not sure about C or Tachyon as I am unfamiliar with those.

    The hardware counters can also work.

    Cheers,

    Peter (pjv)

Sign In or Register to comment.