Shop Learn P1 Docs P2 Docs
Propeller Flip with analog inputs for PWM what is needed — Parallax Forums

Propeller Flip with analog inputs for PWM what is needed

ardillolamboardillolambo Posts: 11
edited 2022-09-01 10:20 in Propeller 1

Hello there,
New to the propeller world, got myself a flip that I want to do some PWM. What external device I need to accomplish this

Regards,

Comments

  • MicksterMickster Posts: 2,222

    @ardillolambo said:
    Hello there,
    New to the propeller world, got myself a flip that I want to do some PWM. What external device I need to accomplish this

    Regards,

    Read up on the counters (2 per cog) and then there's:
    https://github.com/parallaxinc/propeller/tree/master/libraries/community/p1/All

    Craig

  • JonnyMacJonnyMac Posts: 8,238

    @ardillolambo said:
    Hello there,
    New to the propeller world, got myself a flip that I want to do some PWM. What external device I need to accomplish this

    Regards,

    What do you want to control with the PWM? LEDs? Motors. As Craig points out, the counters (and cog code) can be used to create two high(ish) frequency PWM channels -- you can do this without assembly. If, on the other hand, you want to control a bunch of standard LEDs, you'll want an assembly driver. I have and LED driver that allows up to 8 channels that has been used in a couple commercial products. I didn't need more than 8 channels, so I stopped there.

    Your title talks about analog inputs. There are lots of easy-to-use ADCs. How many inputs do you want to read?

  • ok...I see. Forget to mention I came from the arduino platform. Anyways Im trying to develop PWM for DC motors that are driven by an analog signal (lets say temperature). I know that the flip do not have ADC neither DAC, so what peripherals do I need to accomplish this?
    Im good with 4 analog inputs and 4 analog outputs.

  • JonnyMacJonnyMac Posts: 8,238
    edited 2022-08-30 20:49

    There are no real analog (variable voltage) outputs on a standard Arduino, either; the analogWrite() function is PWM. I've attached two objects that have been used in commercial products from JonyJib.com (so I know they work really well).

    The file jm_adc0834_ez.spin allows up to four analog inputs using a 3-wire SPI interface. Since the device doesn't send data while receiving it, the DI and DO pins can be tied together with a resistor to save an IO on the Propeller -- you get four inputs on three pins. There is an 8-channel version of this chip (and the driver is identical) if you need more analog inputs. This object does not consume any Propeller cogs (it runs in the main).

    For motor control, you'll usually want higher PWM frequencies so the motors don't make noise. The file jm_dual_pwm_ez.spin will give you control over one or two motors using a common h-bridge chip (so you have forward and reverse control). This object does use a cog to create the fixed-frequency, variable duty-cycle PWM for motors.

    These objects should teach you a bit about the Propeller (which doesn't hide things like the Arduino does). Do not modify these objects, just link them to your program and call them as is. These objects have been vetted commercially. If you find one or the other doesn't include a feature you'd like, send me a note and I'll consider it. Given you're new to the Propeller, I suggest you focus on learning Spin (it's easy, you won't have trouble) and understanding the architecture of the Propeller).

  • thanks for the info, I'll check them out. I was thinking driving a mosfet to do the PWM, so i dont need those h bridges, still possible?

  • JonnyMacJonnyMac Posts: 8,238
    edited 2022-08-30 21:21

    If you don't need forward and reverse control, the attached object will do the trick. It uses the same mechanics to create the PWM signal, but there is no direction control or negative values allowed. Again, this will consume a cog for two PWM signals (with frequency up to 38kHz).

  • no, no rotation change. My application will be cooling fans they just go one way. Its ok using a cog for every two fan motors, actually i was designing for a cog per fan motor

  • JM,

    I checked the attachment. I'm sorry this is in spin which I'm learning as I go, I'm more a c++ guy. Sorry about my ignorance but I did not see the PWM output being driven by an analog signal, I'm missing something?

  • JonnyMacJonnyMac Posts: 8,238
    edited 2022-08-31 02:56

    I did not see the PWM output being driven by an analog signal, I'm missing something?

    One step at a time. Learn to do PWM output first, then you can consider how to do analog input.

    actually i was designing for a cog per fan motor

    Cogs are precious, especially with the P1 -- get into the habit of not wasting them. With 2 counter modules per cog, you can control two unidirectional motor outputs at up to 38kHz PWM frequency (with my "ez" [Spin only] driver). I find that fast enough for the motors I've worked with.

    My friends and customer have certain expectations about my code and I'm able to meet that by starting every program with a template. I've attached my default template as you may find it helpful. Save the template file to your templates folder, and all the other files to your Library folder so that they're available to every project. You can point to your template and Propeller Tool will create a new file from it when you want.

    I don't have a lot of time, but it only took a few minutes to knock together a demo of the PWM object. Start a project and include it in the objects section:

      fans : "jm_dual_pwm_ez"                                       ' * dual PWM control
    

    You need to define the pins that will connect to the FETs that drive your fans. I have a section in my code for this:

    con { app io pins }
    
      FAN_B    =  9  { O }                                          ' fan speed control outputs
      FAN_A    =  8  { O }
    

    What Parallax calls objects are really class files, and you need to instantiate each object. There is usually a .start() method in the object. Here's how I started the fans object.

      fans.start(FAN_A, FAN_B, 20_000, 100)                         ' set fans, 20kHz, 0..100%
    

    My code includes a setup() method; this is the place where I put these calls. Note that the PWM object expects two pins (you can use -1 as the parameter if only one pin is required, the PWM frequency, and the top end (100%) value. I made the demo by specifying 100 as my top value; this lets me set the fan speed from 0 to 100% in 1% increments.

    Finally, set the speed. For simplicity I wrote static values so that I could capture on a logic analyzer to prove that the code works.

      fans.set(0, 33)                                               ' set fan A to 33%
      fans.set(1, 75)                                               ' set fan B to 75%
    

    Easy-peasy, lemon squeezy.

    This the output captured on a logic analyzer.

    If you want to run the code in a loop to watch on a 'scope, you could do something like this (not in the demo):

      repeat speed from 0 to 100
        fans.set(0, speed)
        time.pause(10)
    

    This will cycle the first output between 0 and 100%. Again, this is test code for edification.

    Spend a little time each day experimenting and you'll get up to speed with the Propeller very quickly. Once you understand it's architecture, you could try the FlexProp compiler which understands C.

  • JonnyMacJonnyMac Posts: 8,238

    If you were running your fans with an Arduino using analogWrite(), you could use the attached object (included in the demo). I normally use this with LEDs which is why it includes gamma correction. As I stated above, you should always try to save cogs when you can. If 1kHz is okay with your fans, this will let you run up to 8 of them (outputs must be contiguous) using a single cog. This uses assembly to bit-bang the PWM; it does not use the counter modules.

    Here's the output on a logic analyzer.

  • jmgjmg Posts: 14,968

    @ardillolambo said:
    no, no rotation change. My application will be cooling fans they just go one way. Its ok using a cog for every two fan motors, actually i was designing for a cog per fan motor

    ...

    ...Sorry about my ignorance but I did not see the PWM output being driven by an analog signal, I'm missing something?

    You should post more info on exactly what your cooling fans expect.

  • evanhevanh Posts: 13,628

    I've just been experimenting with this sort of thing. And I gotta say wow! when it comes to comparing filtering of PWM (Pulse Width Modulation) vs PDM (Pulse Density Modulation) for a DC voltage level. PDM's dynamic period just whips the Smile off PWM.

    Just with a simple R-C filter, one resistor and one capacitor, as the analogue components of the DAC, it can be made using much smaller values and therefore faster responding. Nothing complicated at all. In fact the Prop1's "Duty Cycle" counter mode produces PDM natively, therefore it's actually simpler to do PDM.

    So no driver code needed either. No timers because there's no fixed period. Just setup the counter CTRx register with the output pin and Duty mode, then the set level is as simple as loading FRQx register. It takes effect immediately.

  • evanhevanh Posts: 13,628

    Noooo! That damn word filter strikes again.

  • MicksterMickster Posts: 2,222

    @evanh

    Eh....what? I'm interested in this stuff but no clue what's happening, here....mode 48?
    Are you simulating or do you have hardware?

    Mode:
    0 = Counter Disabled
    8 = PLL Internal (Video) *
    16 = PLL Single-Ended *
    24 = PLL Differential *
    32 = NCO/PWM Single Ended – frqx is added to phsx each system clock; apin = phsx[31]
    40 = NCO/PWM Differential – frqx is added to phsx each system clock; apin=phsx[31]; bpin=!phsx[31]
    48 = DUTY Single-Ended – frqx is added to phsx each system clock; apin=carry
    56 = DUTY Differential – frqx is added to phsx each system clock; apin=carry; bpin=!carry
    64 = POS detector - frqx is added to phsx each system clock when apin is high
    72 = POS detector with feedback - frqx is added to phsx each system clock when apin is high (1)
    80 = POSEDGE detector - frqx is added to phsx each system clock when apin goes from low to high
    88 = POSEDGE detector with feedback - frqx is added to phsx each system clock when apin goes from low to high
    96 = NEG detector - frqx is added to phsx each system clock when apin is low
    104 = NEG detector with feedback - frqx is added to phsx each system clock when apin is low (1)
    112 = NEGEDGE detector - frqx is added to phsx each system clock when apin goes from high to low
    120 = NEGEDGE detector with feedback - frqx is added to phsx each system clock when apin goes from high to low
    (1)
    128 = LOGIC never – Counter off
    136 = LOGIC !A & !B - frqx is added to phsx each system clock when apin is low AND bpin is low
    144 = LOGIC A & !B - frqx is added to phsx each system clock when apin is high AND bpin is low
    152 = LOGIC !B - frqx is added to phsx each system clock when bpin is low
    160 = LOGIC !A & B - frqx is added to phsx each system clock when apin is low AND bpin is high
    168 = LOGIC !A - frqx is added to phsx each system clock when apin is low
    176 = LOGIC A <> B - frqx is added to phsx each system clock when apin is not equal to bpin
    184 = LOGIC !A | !B - frqx is added to phsx each system clock when apin is low OR bpin is low
    192 = LOGIC A & B - frqx is added to phsx each system clock when apin is high AND bpin is high
    200 = LOGIC A = B - frqx is added to phsx each system clock when apin is equal to bpin
    208 = LOGIC A - frqx is added to phsx each system clock when apin is high
    216 = LOGIC A | !B - frqx is added to phsx each system clock when apin is high OR bpin is low
    224 = LOGIC B - frqx is added to phsx each system clock when bpin is high
    232 = LOGIC !A | B - frqx is added to phsx each system clock when apin is low OR bpin is high
    240 = LOGIC A | B - frqx is added to phsx each system clock when apin is high OR bpin is high
    248 = LOGIC always

    Craig

  • evanhevanh Posts: 13,628
    edited 2022-08-31 11:44

    I would have numbered it mode 6 from the 5-bit mode field but, yep, 48 is the correct one in your list. DAC use is documented in Parallax's AN001 Counters V2.0 document.

    I hadn't paid any of this a lot of attention until I recently went to make a PWM DAC and found a large trade-off of resolution verses speed. I wasn't prepared to learn designing a second or third order filter to improve speed so thought why not try this other mode out. After all it doesn't need software polling to maintain it. That's a bonus worth checking at least. And damn, the speed improvement was astonishing! The saw-tooth vanishes and the capacitor value became a couple of orders smaller with ease.

    Here's the set level I'm using. I've written it to take percentage with two decimal places as its input parameter:

    PUB  SetDuty( percent )    ' percent in hundredths: 0_00 .. 100_00
        if percent
            frqb := ((((percent << 16 + 50) / 100) << 8 + 50) / 100) << 8 - 1    ' duty = 4G * percent / 10000
        else
            frqb := 0
    

    EDIT: Doh! Fixed a big mistake in the function. Apologies, it isn't a verbatim copy of my existing code.

  • evanhevanh Posts: 13,628
    edited 2022-08-31 11:36

    If you have a 64-bit divide function then the above line of code would look a lot cleaner.

    EDIT: Actually, I might have some assembly that would do such a divide if it was, say, part of a larger driver. This is modified from another driver I've been hacking to how I want it:

    Fraction
    '32 bit UNSIGNED divide that yields a 32 bit fraction and remainder
    'Y0 (FRACTION) = Y1/X0, Y1 = REMAINDER
    '  NOTE: The Divider (X0) must be larger than the Dividend (Y1)
    '  If they are equal then best to skip executing this
            mov Mt1, #32
            mov My0, #0   wc        ' clear carry
    :loop
            rcl My1, #1    ' X0 divisor must be greater than one
            cmpsub  My1, Mx0   wc,wr
            rcl My0, #1   wc
            djnz    Mt1, #:loop
    Fraction_ret
            ret
    
  • evanhevanh Posts: 13,628

    I definitely miss the inline Pasm feature of Spin2.

  • MicksterMickster Posts: 2,222

    I honestly don't know what to think about PWM filtering when it comes to motor control. I use a very rudimentary filter and the output looks horrendous on the scope but in the context of a closed-loop servo axis, I could not wish for better stability. I guess the motor itself is essentially a LPF.

    Not even sure of the benefits of higher resolution because I see no difference between an 8bit or 16bit motor command.

    Always been a mystery to me.

    Craig

  • evanhevanh Posts: 13,628
    edited 2022-08-31 13:16

    My code is for a DC set level (voltage). Not specific to motors since they don't need a steady DC level.

  • MicksterMickster Posts: 2,222
    edited 2022-08-31 14:15
  • msrobotsmsrobots Posts: 3,562

    @evanh said:
    I definitely miss the inline Pasm feature of Spin2.

    You can use inline pasm on P1 with FlexSpin

    Mike

  • evanhevanh Posts: 13,628

    Yeah, I'm trying not to exclude Proptool for the moment. The application is not demanding and the customer likes to dabble with the resulting code I've written too.

  • GenetixGenetix Posts: 1,684

    ardillolambo,

    Spin is the Propeller's native language and the interpreter is built into it.

    Spin is great for those of us who grew up on BASIC and used a BASIC Stamp.

    There are several C compilers for the Propeller but officially Parallax only supports SimpleIDE.
    If you use BlockyProp, it will generate C code should you be interested.

    Perhaps one of the C experts could chime in if there is any Arduino-like libraries.

Sign In or Register to comment.