Ward I'm going to try controlling the boards with an Arduino to see if the issue is on the hardware side.
So using this code on arduino all three boards work.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41);
void setup() {
Serial.begin(9600);
Serial.println("16 channel PWM test!");
pwm1.begin();
pwm1.setPWMFreq(1600); // This is the maximum PWM frequency
pwm2.begin();
pwm2.setPWMFreq(1600); // This is the maximum PWM frequency
pwm1.setPWM(0, 0, 1000);
pwm2.setPWM(1, 0, 2000);
}
void loop()
{
}
Ward Could it be that the board sends a NACK and therefore it fails? Would this mean it is a hardware issue?
No idea really, maybe this is some extra configuration to be done on the driver for it to work properly. Have a look at the file in/usr/include/linux/i2c.h on the board, if that helps.
The weird thing is that at the point where I only create a single instance of the PCA9685 class it worked fine. Once I created a second instance not a single ẁriteRegister() call succeeded anymore. Reverting back to only creating one instance of the class doesn't make a difference.
Ward The weird thing is that at the point where I only create a single instance of the PCA9685 class it worked fine. Once I created a second instance not a single ẁriteRegister() call succeeded anymore. Reverting back to only creating one instance of the class doesn't make a difference.
You mean now it no longer works even with just one instance? Have you tried a hard reboot (power off and back on)?
There is also another thing: the address you write to is actually specified in the ioctl(i2C_file, I2C_SLAVE, i2C_address) cal in I2c::initI2C_RW. I think that you could change that at any time. So, could you try to factor that out so that it gets called once before each transaction, and so that all your instances use an underlying basic object that only opens the file descriptor, but each call to write() is preceded by an ioctl()?
Also, do you get any relevant message if you run dmesg after the error occurs?
giuliomoro As a further troubleshooting step, I would make sure that all the I2C transactions only happen in a single thread at a time, for instance: call all the begin() from setup() first , then have a single AuxiliaryTask that handles all the ongoing I2C transactions, and only start it after you called begin() on all the objects.
So I've commented out the writeRegister() calls in begin() and called then from an auxiliaryTask but that still returns 121..
giuliomoro You mean now it no longer works even with just one instance? Have you tried a hard reboot (power off and back on)?
Yes. I've been working on this issue for several days, powering the Bela off when I go home.
giuliomoro There is also another thing: the address you write to is actually specified in the ioctl(i2C_file, I2C_SLAVE, i2C_address) cal in I2c::initI2C_RW. I think that you could change that at any time. So, could you try to factor that out so that it gets called once before each transaction, and so that all your instances use an underlying basic object that only opens the file descriptor, but each call to write() is preceded by an ioctl()?
I'll go try this now.
giuliomoro Also, do you get any relevant message if you run dmesg after the error occurs?
dmesg | grep 'i2c' gives me the following. I'm not really sure what else to look for indmesg results..
[ 0.539865] omap_i2c 44e0b000.i2c: could not find pctldev for node /ocp/l4_wkup@44c00000/scm@210000/pinmux@800/pinmux_i2c0_pins, deferring probe
[ 0.539932] omap_i2c 4802a000.i2c: could not find pctldev for node /ocp/l4_wkup@44c00000/scm@210000/pinmux@800/pinmux_i2c1_pins, deferring probe
[ 0.541655] omap_i2c 4819c000.i2c: bus 2 rev0.11 at 100 kHz
[ 0.882372] i2c /dev entries driver
[ 0.998351] input: tps65217_pwr_but as /devices/platform/ocp/44e0b000.i2c/i2c-0/0-0024/input/input0
[ 1.030820] omap_i2c 44e0b000.i2c: bus 0 rev0.11 at 400 kHz
[ 1.034640] omap_i2c 4802a000.i2c: bus 1 rev0.11 at 100 kHz
you could do dmesg -w in another window while the terminal is running, so you will see new messages as they are printed out. There will be plenty of logging from the Bela driver (all sorts of stuff about mapping interrupts), at every start/stop, but possibly you will see some extra errors (though I wouldn't bet on it).
We ordered three of those boards to help troubleshooting from our side, but I am afraid they will be in after Christmas!
giuliomoro There is also another thing: the address you write to is actually specified in the ioctl(i2C_file, I2C_SLAVE, i2C_address) cal in I2c::initI2C_RW. I think that you could change that at any time. So, could you try to factor that out so that it gets called once before each transaction, and so that all your instances use an underlying basic object that only opens the file descriptor, but each call to write() is preceded by an ioctl()?
So like this? This also returns 121
/***** i2cbasic.h *****/
#pragma once
#include "I2c.h"
class I2CBasic : public I2c
{
public:
I2CBasic();
~I2CBasic();
bool writeRegister(unsigned address, unsigned reg, unsigned val);
int readI2C() { return 0; }
};
I2CBasic i2cBasic1;
I2CBasic i2cBasic2;
unsigned int address1 = 40;
unsigned int address2 = 41;
bool setup(BelaContext* context, void*)
{
// init the first one from scratch
if(i2cBasic1.initI2C_RW(bus, address1, 0) > 0)
{
int err = errno;
fprintf(stderr, "Something happened while initializing the I2c connection. \tError code %i\n", err);
return false;
}
// the second instance re-uses the same open file as the first one
i2cBasic2.setFileHandleFrom(i2cBasic1);
// and we only sets the address
i2cBasic2.setAddress(address2);
...
// ... so that each call to writeRegister will have the `ioctl()` (to set the register) followed by
// `write()`, with both objects acting on the same file handler
if(!i2cBasic1.writeRegister(...))
return false;
...
if(!i2cBasic2.writeRegister(...))
return false;
return true;
}
This is a bit ugly, and could definitely be refactored, but just see if this helps in any way. The example I linked above (10-Instruments/d-box) actually successfully opens the /dev/i2c... file twice, once for each instance, so I am not sure this would help, but worth giving it a try.
The only problems I encountered were weak/wobbly "jumpers" for the addresses: I don't have a soldering iron here, so I just taped some stranded wire across the pads. Initially, I was cutting the plastic sleeve off. This proved to be fairly unreliable, that a given board would occasionally (often!) change address. When that happened while the program was running, I would then get the 121 error. However, even if one of the boards failed (because of wrong address), the others would still work, and when two boards were on the same address, you could just address them both in one go!
However, now that I leave the sleeve in place, as in the pic below, this is much more reliable:
I tried running the above also in an AuxiliaryTask, and it all works as expected. E.g.:
So, I am not sure what is the problem you are encountering, but I would suggest:
- check your hardware connections and the jumper
- make sure i2cdetect reliably detects the correct address. I run it like this watch -n0.2 i2cdetect -y -r 1 so it refreshes automatically every 0.2 seconds. Remember to stop this when running the Bela program, as it may interfere with that one, as both use the bus.
- make sure you use 0x if you are specifying the hex address (and viceversa, don't use it if you are not). Pretty basic, I know, but it got me a couple of times today!
Rethinking about the whole thing, if I was to place a bet on what your issue was, I would bet on this, as it would explain all of the above:
giuliomoro - make sure you use 0x if you are specifying the hex address (and viceversa, don't use it if you are not). Pretty basic, I know, but it got me a couple of times today!
giuliomoro - make sure you use 0x if you are specifying the hex address (and viceversa, don't use it if you are not). Pretty basic, I know, but it got me a couple of times today!
I'm building a 6 track synthesizer/instrument. The panel you are seeing is an interface to edit, store and recall presets. The LED's are going to be used to display the track you are editing (color) and what it's current value is (brightness).
The instrument is taking shape but not done yet, still a lot of work on both the hard- and software side. I'll make a more in-depth post in the future about what it does and how it sounds.