Menu

PCA9685 PWM Driver - formal release and demos

Anobium
2018-07-14
2018-08-06
1 2 3 > >> (Page 1 of 3)
  • Anobium

    Anobium - 2018-07-14

    The PCA9685 is an I2C-bus controlled 16-channel LED controller optimized for servoes and Red/Green/Blue/Amber (RGBA) color backlighting applications.

    Each output has its own 12-bit resolution (4096 steps) fixed frequency individual PWM controller that operates at a programmable frequency from a typical of 24 Hz to 1526 Hz with a duty cycle that is adjustable from 0 % to 100 % to allow the LED to be set to a specific brightness value.

    The board is available from many sources. A typical board looks like this.

    Library

    The library supports hardware I2C and software I2C. Download from here https://sourceforge.net/p/gcbasic/code/HEAD/tree/GCBASIC/trunk/include/PCA9685.h?format=raw

    The public methods are show below:

    '    PCA9685_SetFreqency ( frequency_wordvalue ) a value from 24 to 1526
    '    PCA9685_SetChannelDuty(  channel as byte , duty as word )  where duty is 0 to 100%
    '    PCA9685_SetChannelOnOnly ( channel as byte , OnValue as word ) where Onvalue is 0 to 4095
    '    PCA9685_SetChannelOffOnly ( channel as byte , OffValue as word ) where Offvalue is 0 to 4095
    '    PCA9685_WriteChannel( channel as byte, OnValue as word, OffValue  as word ) ranges of 0 to 4095
    

    There are a LOT of constants but these are the key constants to use.

    PCA9685_ADDRESS
    PWM_DUTY
    PCA9685_ALL_CHANNELS
    PCA9685_LED0, PCA9685_LED1 etc to PCA9685_LED15
    PCA9685_LED_ON
    PCA9685_LED_OFF
    

    Demonstrations

    A full suite of demonstrations are in GitHub. The demos cover simple ON and OFF, simple usage, variable Duty (16bit and Percentage) and change frequency.

    See https://github.com/Anobium/Great-Cow-BASIC-Demonstration-Sources/tree/master/PWM%20Solutions/PCA9685%20Driver%20Examples

    Performance of the PCA9685

    The PCA9685 is a 12bit PWM channel device and the method used to set the frequency is inverserly inaccurate meaning the higher the frequency the less accurate the actual frequency output. The graph below shows this issue.

    The frequency is set with a byte value of 0-255. The PRE_SCALE register defines the frequency at which the outputs modulate. The prescale value is determined with the formula shown in Equation 1 in the datasheet.

    For example, for an output default frequency of 200 Hz with an oscillator clock frequency of 25 MHz, the internal clock, the register is "0x1e", the maximum PWM frequency is 1526 Hz if the PRE_SCALE register is set "0x03", the minimum PWM frequency is 24 Hz if the PRE_SCALE register is set "0xFF".

    So, if you select a frequency of 1221 to 1524... the PRE_SCALE register = "0x04" you loose accurancy. This is a limitation of the PCA9685.

    Essentially, anything above the 250 hz value is inaccurate and you should use an external clock source to resolve. If you do use an external clock - just change PCA9685_INTERNAL_CLOCK_FREQUENCY in your user program.

    Enjoy

     

    Last edit: Anobium 2018-07-14
  • bed

    bed - 2018-07-14

    You just secretly wrote a new library.
    Great!

     
  • stan cartwright

    stan cartwright - 2018-07-14

    here's 8 servos...lack of power for more
    https://youtu.be/k3cwF1ddRs8

     
  • stan cartwright

    stan cartwright - 2018-07-15

    This is a sg90 , 50Hz 550uS pulses. Can you see what's wrong with this and all 12 servos I got from ebay. Hint... what's the pot for and normally to do this is replace pot with 2 resistors and get rid of end stop
    https://youtu.be/NYsOUQRzavc
    I was trying to find angular range vs pwm

     
  • stan cartwright

    stan cartwright - 2018-07-15

    I did some experimenting and at 50Hz a sg90 servo has a range of pca9685 110 - 0 degrees to
    pca9685 530 - 179 degrees
    so that's 420/180 or not easy to convert.
    There is inconsistency of the 0 and 180 degree pca9685 values/pulse widths for different sg90 servos...please see video https://youtu.be/-EV7D7Zte6g
    I noticed the batch of 12 have a 0 to>190 degree range with values from 124 to 590. A value of 122 and the servo continually turns.
    This difference between servos may be trouble.

     

    Last edit: stan cartwright 2018-07-15
  • stan cartwright

    stan cartwright - 2018-07-15

    I saw the difference, I'm not sending them back.
    explains a lot :)

     
  • stan cartwright

    stan cartwright - 2018-07-15

    Here's a normal sg90 and a mg90 (metal gears) running the same pulse width. See the ranges are different. https://youtu.be/a7rkUCKkQaE

     
  • stan cartwright

    stan cartwright - 2018-07-15

    This wouldn't work cos scale is to integer :(
    would a word output version of scale be hard to convert degrees to pca9685 0 - 4097 values?
    It scales as 2.333333 pca units per degree from experimenting.

      for servo = 10 to 170 ; degrees
        servopos=scale (servo,0,179,120,530)
        PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos )
    

    Thanks for making the driver. Has anyone made a 6 leg walker with gcb?
    I cheated and bought acrylic leg parts and chassis but the coding will be trial an error for one leg-3 servos.
    There are rpi and arduino kits and the 24 or 32 channel servo controller uses win gui software to show angles of all servos...when set up.
    I'm just waiting on parts.

     
    • Anobium

      Anobium - 2018-07-16

      @Stan.
      Scale is an integer correct. +/- 32767. And, the value you need it between 0 - 4085.

      So, no idea what the issue. I am guessing that you need to use factorisation - but,guessing.

       
  • stan cartwright

    stan cartwright - 2018-07-16

    Not 0 - 4085 as PCA9685_WriteChannel (PCA9685_LED0 ,0, 210 ) would be servo 0 degrees
    and PCA9685_WriteChannel (PCA9685_LED0 ,0, 580 ) would be servo 180 degrees so the range is 580-210=370 so 0 to 180 degrees needs scaling to 0 to 370 and add the result to 210.

    0----210 servo range 580 ----4085
    ------0...............................180..................

     
  • stan cartwright

    stan cartwright - 2018-07-16

    Thanks.
    integer_variable = Scale (value , fromLow , fromHigh , toLow , toHigh [, calibration] )
    Is integer_variable a byte or word variable? must be word??
    This seems to work as I posted.adding result to 210 was wrong. The range values need trial error for particular servo model.

    do ;rotates servo left right
      for servo = 10 to 170
        servopos=scale (servo,0,179,220,530) ;220,530 needs experimenting
        PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos )
        wait 10 ms
      next
    wait 1 s
      for servo = 170 to 10
        servopos=scale (servo,0,179,220,530)
        PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos )
    wait 10 ms
      next
    wait 1 s
    loop
    
     
  • Anobium

    Anobium - 2018-07-16

    I just updated https://github.com/Anobium/Great-Cow-BASIC-Help/blob/master/source/scale.adoc

    Calibration is an integer as you can adjust up and down.

     
    • stan cartwright

      stan cartwright - 2018-07-16

      Thanks. Turned out it didn't need calibration offset.

       
  • stan cartwright

    stan cartwright - 2018-07-16

    I hope others find this interesting as feedback for using this device.
    Finding one- don't feed a servo a pulse width it can't handle unattended as it will get hot and not work anymore :(

      for servo = 0 to 179 ;degrees
        servopos=scale (servo,0,179,210,530)
        PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos )
    

    Does work ok, smooth movement.
    Without a protractor and a jig and a pointer on the servo horn I can't test degrees accuracy.
    It's choose a servo and see it's shortest pulse width before it buzzes and doesn't move
    then it's longest pulse width before it buzzes and doesn't move.
    Using pca9685 values gives more precision than scaled degrees but degrees are easier to imagine/work with.
    edit -- yes,after experimenting with, going to use above code for project. With caibrating for one servo it will be ok for same ones if I scale range to less than 180 degrees as 180 is iffy to achieve and happy with less.
    Scaled degrees will be fine as the servos will be reading table values which will be coarse movement.
    All so easy with gcb.

     

    Last edit: stan cartwright 2018-07-16
  • stan cartwright

    stan cartwright - 2018-07-16

    If it's relevant, using pca9685with arduino uno I can't flash hex arduino uno. Error -1 or 1.
    unplugging usb lead and reconnecting sorts it until next flash hex. Thought mentioning.

     
  • stan cartwright

    stan cartwright - 2018-07-17

    Another pca9685 board arrive today. I've bridged the A0 solder connection on the second board as it's the first extra board. It's address is the base address plus one...from data sheet.
    I think PCA9685_Initialise works for all devices on the bus but I don't know how to address the second board.
    I think I change the i2c address for second board and use it like the first ie same commands for led0 to led16...but controlling second board.
    Any tips welcome.

     

    Last edit: stan cartwright 2018-07-17
  • Anobium

    Anobium - 2018-07-17

    You will need to patch the library. All pretty simple but something that I cannot do for a day or two.

    You simply need to add another define for the address of the second device and then adap write device methods.

     
  • stan cartwright

    stan cartwright - 2018-07-17

    Using two i2c addresses is answered in the forum 12 month back somewhere I seem to rememberif that would be relevent.

     
  • stan cartwright

    stan cartwright - 2018-07-17

    From this for servo https://servodatabase.com/servo/towerpro/mg90
    I got pca9685 values of 0 degrees=86...400mS and 179 degrees=520...2.41mS
    It's working ok for degrees for this mg90 servo.

    #define PWM_Freq 50 ;50Hz for servo
    PCA9685_SetFreqency ( 50 )
    dim servo as byte
    dim servopos as word
    dim deg0,deg179 as word
    ;
    deg179=520 ;2.41mS
    deg0=86 ;400 mS
    ;
    servo=90 ;servo degrees wanted
    servopos=scale (servo,0,179,deg0,deg179)
    PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos )
    
     
  • Anobium

    Anobium - 2018-07-23

    Next release. Supports up to four devices.

    Revised PCA9685.h to support multiple devices. As shown below:

               #define PCA9685_ADDRESS_1 0x80
    '    #define PCA9685_ADDRESS_2 0x82
    '    #define PCA9685_ADDRESS_3 0x84
    '    #define PCA9685_ADDRESS_4 0x86
    

    You must used PCA9685_ADDRESS_1 as minimum. All public methods assume PCA9685_ADDRESS_1 as the default address therefore you do not need to pass this address as a parameter.

    The library supports hardware I2C and software I2C. Download from here
    GitHub

    The public methods are show below:

    PCA9685_SetFreqency ( frequency_wordvalue [, device IC2 Address] ) a value from 24 to 1526
    PCA9685_SetChannelDuty(  channel as byte , duty as word [, device IC2 Address] )  where duty is 0 to 100%
    PCA9685_SetChannelOnOnly ( channel as byte , OnValue as word [, device IC2 Address] ) where Onvalue is 0 to 4095
    PCA9685_SetChannelOffOnly ( channel as byte , OffValue as word [, device IC2 Address] ) where Offvalue is 0 to 4095
    PCA9685_WriteChannel( channel as byte, OnValue as word, OffValue  as word [, device IC2 Address] ) ranges of 0 to 4095
    

    @Stan. Test carefully. I cannot test. You need cease using PCA9685_ADDRESS and use PCA9685_ADDRESS_1 - note the suffixes.

     
  • stan cartwright

    stan cartwright - 2018-07-23

    thankyou.
    in demo https://github.com/Anobium/Great-Cow-BASIC-Demonstration-Sources/blob/master/PWM%20Solutions/PCA9685%20Driver%20Examples/PCA9685_ChangingDuty12bit_HardwareI2C.gcb
    has it changed
    'The call IS required to setup the device
    PCA9685_Initialise

    PCA9685_SetFreqency ( 38 )
    

    does PCA9685_Initialise init all devices?
    should PCA9685_SetFreqency ( 38 ) be PCA9685_SetFreqency ( 38 ),pca9685_address_1?

    my code still only drives one device.

    'Set the frequency using the Great Cow BASIC PWM constant
    #define PWM_Freq 50 ;50Hz for servo
    PCA9685_SetFreqency ( 50,PCA9685_ADDRESS_1 )
    PCA9685_SetFreqency ( 50,PCA9685_ADDRESS_2 )
    dim servo as byte
    dim servopos as word ;servo angle
    dim deg0,deg180 as word
    
    deg180=520 ;2.41mS
    deg0=110 ;85=400 mS 140=650ms
    servo=0 ;servo degrees wanted
    
    do
      for servo = 0 to 90 ;degrees
        servopos=scale (servo,0,179,deg0,deg180)
        PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos,PCA9685_ADDRESS_1 )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos,PCA9685_ADDRESS_1 )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos,PCA9685_ADDRESS_2 )
        wait 3 ms
      next
    wait 5 s
      for servo = 90 to 179 ;degrees
        servopos=scale (servo,0,179,deg0,deg180)
        PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos,PCA9685_ADDRESS_1 )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos,PCA9685_ADDRESS_1 )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos,PCA9685_ADDRESS_2 )
        wait 3 ms
      next
    wait 5 s
      for servo = 179 to 90 ;degrees
        servopos=scale (servo,0,179,deg0,deg180)
        PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos,PCA9685_ADDRESS_1 )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos,PCA9685_ADDRESS_1 )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos,PCA9685_ADDRESS_2 )
        wait 3 ms
      next
    wait 5 s
      for servo = 90 to 0 ;degrees
        servopos=scale (servo,0,179,deg0,deg180)
        PCA9685_WriteChannel (PCA9685_LED0 ,0, servopos,PCA9685_ADDRESS_1 )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos,PCA9685_ADDRESS_1 )
        PCA9685_WriteChannel (PCA9685_LED15 ,0, servopos,PCA9685_ADDRESS_2 )
    wait 3 ms
      next
    wait 5 s
    
    loop
    
     
1 2 3 > >> (Page 1 of 3)

Log in to post a comment.