Giter Club home page Giter Club logo

pwm-pca9685-rs's People

Contributors

eldruin avatar jeroenvervaeke avatar mhthies avatar nils-van-zuijlen avatar ryankurte avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pwm-pca9685-rs's Issues

Servos don't work

Hey,
I don't know why but I can't get my servos to work with this library.
Leds are working fine but my servos won't move. I tried everything I can imagine to make it work but I can't and to verify my components are fine I used an esp8266 + arduino with which everything works.

Here is the last iteration of code I tried which is as similar as possible to the arduino code that works.

#![deny(unsafe_code)]
#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_semihosting as _;
use pwm_pca9685::{Channel, Pca9685, SlaveAddr};
use stm32f1xx_hal::{
    delay::Delay,
    i2c::{BlockingI2c, DutyCycle, Mode},
    pac,
    prelude::*,
};

#[entry]
fn main() -> ! {
    let cp = cortex_m::Peripherals::take().unwrap();
    let dp = pac::Peripherals::take().unwrap();

    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();

    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
    let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);

    let scl = gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh);
    let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh);
    let mut delay = Delay::new(cp.SYST, clocks);

    let i2c = BlockingI2c::i2c1(
        dp.I2C1,
        (scl, sda),
        &mut afio.mapr,
        Mode::Fast {
            frequency: 400_000.hz(),
            duty_cycle: DutyCycle::Ratio2to1,
        },
        clocks,
        &mut rcc.apb1,
        1000,
        10,
        1000,
        1000,
    );

    let mut pwm = Pca9685::new(i2c, SlaveAddr::default());
    pwm.enable().unwrap();
    pwm.set_prescale(100).unwrap();

    loop {
        for i in 0..4095 {
            pwm.set_channel_on_off(Channel::C0, 0, i % 4095).unwrap();
        }

        for a in 0..=180 {
            pwm.set_channel_on_off(Channel::C10, 0, pulse_width(a))
                .unwrap();
            delay.delay_ms(20_u32);
        }

        delay.delay_ms(500_u32);

        for a in 0..=180 {
            pwm.set_channel_on_off(Channel::C10, 0, pulse_width(180 - a))
                .unwrap();
            delay.delay_ms(20_u32);
        }

        delay.delay_ms(500_u32);
    }
}

fn map_to_range(x: u32, in_min: u32, in_max: u32, out_min: u32, out_max: u32) -> u32 {
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

fn pulse_width(angle: u32) -> u16 {
    let pulse_wide: u32 = map_to_range(angle, 0, 180, 600, 2600);
    let quotient: f64 = pulse_wide as f64 / 1000000.0;
    let analog_value = (quotient * 60.0 * 4096.0) as u16 % 4095;
    return analog_value;
}

Of course I also tried the example code but the servos don't work either. (5v are supplied through the terminal block)

can't get the library to work

Hey,
I'm trying to control a servo and some LEDs with the library but I can't get it to output anything at all. To confirm that everything is connected correctly I used a simple python script doing exactly the same and it worked. What could I be doing wrong?

Rust program that doesn't work:

use linux_embedded_hal::I2cdev;
use pwm_pca9685::{Address, Channel, Pca9685};
use std::{thread, time::Duration};

fn main() {
    let dev = I2cdev::new("/dev/i2c-1").unwrap();
    let address = Address::default();
    let mut pwm = Pca9685::new(dev, address).unwrap();

    // This corresponds to a frequency of 60 Hz.
    pwm.set_prescale(100).unwrap();

    loop {
        println!("Setting to 150");
        pwm.set_channel_on_off(Channel::C0, 0, 150).unwrap();
        thread::sleep(Duration::from_secs(3));
        println!("Setting to 600");
        pwm.set_channel_on_off(Channel::C0, 0, 600).unwrap();
        thread::sleep(Duration::from_secs(3));
    }
}

Python script that works:

from __future__ import division
import time

import Adafruit_PCA9685

pwm = Adafruit_PCA9685.PCA9685()

servo_min = 150
servo_max = 600

pwm.set_pwm_freq(60)

print('Moving servo on channel 0, press Ctrl-C to quit...')
while True:
    print(servo_min)
    pwm.set_pwm(0, 0, servo_min)
    time.sleep(3)
    print(servo_max)
    pwm.set_pwm(0, 0, servo_max)
    time.sleep(3)

Add async support

I want to use this crate in a project that does networking with embassy tools, which rely on async.

I can use it in synchronous mode with the embedded-hal traits, but that means I will be wasting cycles waiting on the I2C device while I could be handling UDP requests or WiFi pings.

I am willing to help with the support, but want to know if you want it in this crate or if I should fork it.

Add `enable` call to examples

As already mentioned in #7 it is pretty confusing that the examples in README.md don't call the enable method. It would be easier for beginners to have a ready-to-copy example which works without having to change code yourself.

Update to embedded-hal 1.0

Are there any plans to update to the new stable release of embedded-hal 1.0? It might be a little tricky since a couple big changes have been made since 0.2:

  • all blocking I2C operations are grouped together under a single trait: embedded_hal::i2c::I2c
  • the I2C trait now supports both 7bit and 10bit addresses using a generic address parameter

Thankfully, the actual write and write_read methods don't seem to have changed at all, aside from the address size.

Updating using fewer writes

Hey!

We're working on some cool home lighting stuff using, among others, multiple PCA9685s for a ton of LED strips. We're looking to reduce the number of syscalls and the overhead on the I2C bus due to small transactions, because we might actually be hitting some limits there ๐Ÿ˜…

So, I was wondering: Instead of setting ON and OFF for each channel separately, it seems to be more efficient to just set them at the same time in order to save some bandwidth? I came up with something like this:

    fn write_quad_register(
        &mut self,
        address: u8,
        value0: u16,
        value1: u16,
    ) -> Result<(), Error<E>> {
        if self.config.is_low(BitFlagMode1::AutoInc) {
            let config = self.config;
            self.write_mode1(config.with_high(BitFlagMode1::AutoInc))?;
        }
        self.i2c
            .write(
                self.address,
                &[
                    address,
                    value0 as u8,
                    (value0 >> 8) as u8,
                    value1 as u8,
                    (value1 >> 8) as u8,
                ],
            )
            .map_err(Error::I2C)
    }

However, I'm not too proficient in Rust (especially with macros) and don't know how to nicely integrate this with your impl_channel_match macro. I came up with this:

macro_rules! impl_channel_match_quad {
    ($s:ident, $channel:expr, $value0:expr, $value1:expr, $($C:ident, $reg:ident),*) => {
        match $channel {
            $(
                Channel::$C  => $s.write_quad_register(Register::$reg, $value0, $value1),
            )*
        }
    };
}

impl<I2C, E> Pca9685<I2C>
where
    I2C: hal::blocking::i2c::Write<Error = E>,
{
...
    /// Set the `ON` and `OFF` counters for the selected channel.
    /// 
    /// Note that the full off setting takes precedence over the `on` settings.
    /// See section 7.3.3 "LED output and PWM control" of the datasheet for
    /// further details.
    pub fn set_channel_on_off(
        &mut self,
        channel: Channel,
        on: u16,
        off: u16,
    ) -> Result<(), Error<E>> {
        if on > 4095 || off > 4095 {
            return Err(Error::InvalidInputData);
        }
        impl_channel_match_quad!(
            self, channel, on, off, C0, C0_ON_L, C1, C1_ON_L, C2, C2_ON_L, C3, C3_ON_L, C4,
            C4_ON_L, C5, C5_ON_L, C6, C6_ON_L, C7, C7_ON_L, C8, C8_ON_L, C9, C9_ON_L, C10,
            C10_ON_L, C11, C11_ON_L, C12, C12_ON_L, C13, C13_ON_L, C14, C14_ON_L, C15, C15_ON_L,
            All, ALL_C_ON_L
        )
    }
...
}

I didn't open a PR yet because, as mentioned, I'm not too comfortable with Rust yet. But I'd be happy to hear your feedback and then open one!

Alternatively, or taking this one step further: Would it be possible to write all of the 64 LED output registers in one transaction? Reading through the Linux i2cdev docs gives me the feeling that 32 bytes might be the limit for one transaction (at least when using SMBus?), so this might actually become two writes instead of one.

Please excuse if I'm talking nonsense - I don't do much embedded programming, so I am a bit confused...

Improving latency of `set_channel_`

Hi! First all I love this library and I'm new to embedded programming, so hopefully the questions I'm asking are relevant.

I'm trying to control a stepper motor with this driver over i2c from my rasbperry pi. I'm setting the driver to 1526hz. Very roughly, this is my setup:

    let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
    let address = pwm_pca9685::Address::from(0x60);
    let mut pwm = pwm_pca9685::Pca9685::new(dev, address).unwrap();
    pwm.enable().unwrap();
    pwm.set_prescale(3).unwrap(); // 1526 Hz
    
    // use the pwm controller here

My stepper motor code does calculations for the step and microstepping (not important now), and then calls:

        self.pwm.set_channel_on_off(self.pwma, 0, CURVE[aidx] * 16).unwrap();
        self.pwm.set_channel_on_off(self.pwmb, 0, CURVE[bidx] * 16).unwrap();
        self.pwm.set_channel_on_off(self.ain2, 0, power & 0b0001 * 0x0FFF).unwrap();
        self.pwm.set_channel_on_off(self.bin1, 0, power & 0b0010 * 0x0FFF).unwrap();
        self.pwm.set_channel_on_off(self.ain1, 0, power & 0b0100 * 0x0FFF).unwrap();
        self.pwm.set_channel_on_off(self.bin2, 0, power & 0b1000 * 0x0FFF).unwrap();

The important part here is that I am setting set_channel_on_off 6 times per step. When I time my application it takes approximately 600 micoseconds per call to set_channel_on_off. If I just use set_channel_off it takes 400 microseconds (improvement). I looked at set_all_on_off hoping batching would work, but that takes longer than each call sequentially.

I'm trying to run my motor at faster speeds, and I'm currently blocked because each of these calls is (relatively) slow. I'm trying to figure out what to do. Do you have any idea what the latency would be here? I suspect this is either in how quicly I2C reads the value or perhaps because I'm using dev fs implementation of I2C, but researching these topics is coming up pretty empty. I'm not sure if setting the i2c baud rate would improve this or not, so wanted to ask here for ideas

Additional information: I'm running on raspberry pi with isolcpus and taskset, built in release mode so I shouldn't be having any problems with OS scheduling or code performance. I'm calculating the timing as the average of several thousand invocations of my step code, so the latency isn't from the timing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.