Problem using parallel a sensor with I2C and a motor with a Timer (PWM)

Hi,

i am using an Arduino Mega board and try to read out an acceleration sensor via I2C and to controll a motor with a PWM Signal from one of the Timer.

My Problem is that the Programm crashes when i use a high speedvalue for the motor while the external motor power is connected. If i disconnect the external motor power the programm runs and reads out the sensor-values. When I disconnect the Sensor from I2C and run the Programm then I can control the motor with the external power supply. Only if I use both the I2C-connected Sensor and the motor with external power the programm stops running.

I already changed the board from an Arduino Uno which i used before and i changed the used Timer for the motor control. But both changes had no effect.

I am not sure if this can be a problem of the Timer interrupts (because Wire-lib uses interrupts and I use a Timer for the motor controll) or if the error is at some other place.

I would be glad if someone has an idea what is going wrong in that application and how to solve it.

because Wire-lib uses interrupts

Where have you found timer specific code in the Wire library?

My Problem is that the Programm crashes

How does that look like when the Arduino crashes? I just seen it in an endless loop (software error) and resetting all the time (in most cases a power problem) but never saw it crashing and doing nothing.

Show us your code (the whole) and your wiring.

Hi,

the crash looks like there is no longer an output in the Serial Monitor. If i use the programm with low speedValues for the motor (like 30 with a max of 400) the the programm runs and gives the wanted results. If i use higher speedvalues then the output on the Serial Monitor stops after a few lines and it is not longer possible to insert new speedvalues to the programm.

The code is:

#include <Shield2.h>
#include <Wire.h>

Shield md;

#define AccAddress  0x53
#define GyroAddress 0x68

int speedValue=0;   // speedValue for motor
int speedV =0;

int parseInput() // parse input from Serial to a signed number
{
  String inString = "";
  int sign =1;
  int inChar=0;
  do
  {
    inChar = Serial.read();
    if ((char)inChar =='-') 
    {
      sign =-1;
    }
    if (isDigit(inChar)) 
    {
      inString += (char)inChar; 
    } 
    if (inChar == '\n') 
    {
      return sign*inString.toInt(); 
    }
    Serial.println("reading");
  } 
  while (inChar != '\n');
}

void setup()
{
  Wire.begin();
  Serial.begin(115200);
  md.init();
}

void writeTo(int _dev_address, byte address, byte val) {
  Wire.beginTransmission(_dev_address); // start transmission to device
  Wire.write(address);             // send register address
  Wire.write(val);                 // send value to write
  Wire.endTransmission();         // end transmission
}

void loop()
{

  writeTo(AccAddress,0x2D,0x08); // Power CTL : ON
  writeTo(AccAddress,0x31,1); // DataFormat for acc-Sensor  

  Wire.beginTransmission(GyroAddress);
  Wire.write(0x1D);
  Wire.requestFrom(GyroAddress,6);
  byte temp[6];
  int i=0;
  while (Wire.available())
  {
    temp[i++]=Wire.read();
  }
  int tempval1 =  ((temp[0]<<8) |temp[1]);
  int tempval2 = ((temp[2]<<8) |temp[3]);
  int tempval3 = ((temp[4]<<8) |temp[5]);
  Wire.endTransmission();

  Wire.beginTransmission(AccAddress);
  Wire.write(0x32);
  Wire.requestFrom(AccAddress,6);

  i=0;
  while (Wire.available())
  {
    temp[i++]=Wire.read();
  }
  double tempvala1 =  (((temp[1]<<8) |temp[0]));
  double tempvala2 = (((temp[3]<<8) |temp[2]));
  double tempvala3 = (((temp[5]<<8) |temp[4]));
  Wire.endTransmission();


  if (Serial.available() > 0)
  {  
    speedValue = parseInput();
  }
  if (speedValue <-400 || speedValue > 400) 
  {
    speedValue = speedV;
  }
  else speedV = speedValue;
  md.setSpeedv(speedValue);
   
  double a1 = tempvala1*2/65536;
  double a2 = tempvala2*2/65536;
  double a3 = tempvala3*2/65536;
  double a0 = sqrt(a1*a1+a2*a2+a3*a3); 
  Serial.print(a1);
  Serial.print(" | ");
  Serial.print(a2);
  Serial.print(" | ");
  Serial.print(a3);
  Serial.print(" | a: ");
  Serial.println(a0);
}

The Functions from "Shield2.h" are:

void Shield::init()
{
  pinMode(_FF1 ,INPUT);
  pinMode(_FF2 ,INPUT);
  pinMode(_PWM ,OUTPUT);
  pinMode(_DIR ,OUTPUT);
  pinMode(_CS  ,INPUT);
 
  // PWM frequency calculation
  // 16MHz / 1 (prescaler) / 2 (phase-correct) / 400 (top) = 20kHz
  TCCR1A = 0b10100000;
  TCCR1B = 0b00010001;
  ICR1 = 400;
}

void Shield::setSpeedv(int speed)
{
  unsigned char reverse = 0;
  if (speed < 0)
  {
    speed = -speed;  
    reverse = 1;
  }
  if (speed > 400)  
    speed = 400;
  OCR1A = speed;
  if (reverse) { digitalWrite(_DIR,LOW);}
  else { digitalWrite(_DIR,HIGH); }
}

with the pinmapping:

 static const unsigned char _FF1 = 12;
 static const unsigned char _FF2 =  9;
 static const unsigned char _PWM = 11;
 static const unsigned char _DIR = 13;
 static const unsigned char _CS  = A0;

The Sensor is connected to the Pins 20 (SDA) and 21 (SCL) to the 3.3V and to GND.

I hope you have an idea where this problem can come from.

The Sensor is connected to the Pins 20 (SDA) and 21 (SCL) to the 3.3V and to GND.

Two problems here:

  1. Your sensor operates on 3V3 and your arduino is giving him 5V on the I2C lines (the Wire library activates the internal weak pullups). Some sensor may be fried by that, your's seem to be quite tolerant but still you have only the weak pullups (if I remember correctly about 20k).
  2. If you insert pullups (2k2) to 3V3 you have the correct value for the sensor but you're at the limit (60-70% of VCC means logical 1, so for 5V it's 3V-3V5).

The correct solution is to insert a level converter (Logic Level Converter - Microcontroller Accessories Interface - Boxtec Onlineshop).

Putting your motor to higher speed means it consumes more current and probably the voltage drops a bit. This may be enough to disable the internal pullups to put the I2C lines to a HIGH level within an acceptable time frame. A few misses on the SCK line may be enough to let your Arduino wait forever for an answer from your sensor.

Hi,

thanks for your fast reply. I have one more question about it.

Putting your motor to higher speed means it consumes more current

This is clear...

and probably the voltage drops a bit

Here comes my question. The motor uses an external power supply with high current and voltage. The 5V and 3.3V lines of the arduino board get there power from the USB port of the PC. So i thought the logical circuit of the Arduino-Board should not be affected by the motor current. Is this wrong?

No not inherently, I don't think so but you might check out the IIC hanging thing and insure that your program can't get stuck in a communications loop waiting fir a response. If nothing else check it and fix it if required it would then be one less possible suspect and ... remember you have no real good starting point anyway so most anything could be the villain. Troubleshooting code like anything else is very often eliminating the good guy's until you find the bad one.

Doc

pylon:

The Sensor is connected to the Pins 20 (SDA) and 21 (SCL) to the 3.3V and to GND.

Two problems here:

  1. Your sensor operates on 3V3 and your arduino is giving him 5V on the I2C lines (the Wire library activates the internal weak pullups). Some sensor may be fried by that, your's seem to be quite tolerant but still you have only the weak pullups (if I remember correctly about 20k).

The internal pull-ups are very weak, with external 4k7 pull-ups to 3V3 the bus will function happily at 3V3, since the lines are pulled up to only 3.5V or so. You can also go and remove the pull-up enabling in twi_init() in twi.c if it bothers you. The pull ups are nominally 30k so are too weak to do any damage.

  1. If you insert pullups (2k2) to 3V3 you have the correct value for the sensor but you're at the limit (60-70% of VCC means logical 1, so for 5V it's 3V-3V5).

The Arduino input high threshold is guaranteed <= 0.6 Vdd, so is <= 3.0V - this is compatible with 3V3 CMOS signals (just, lower noise immunity but it works). Table 28-1 in the 48/88/168/328 datasheet. Furthermore with the weak pull ups helping the noise margin is 0.5V rather than 0.3V, so it actually helps!

The correct solution is to insert a level converter (Logic Level Converter - Microcontroller Accessories Interface - Boxtec Onlineshop).

For most signals from the Arduino I'd agree, but the I2C bus I claim is an exception. The level converter you link to won't work for the I2C bus though, since its not bi-directional. The I2C bus is open-drain specifically to allow easy-interworking with 5 and 3.3V I believe.

There's an app note that speaks directly to this NXP AN10441 (attached) issue and I've used it several time with perfect success. Since the 2 pull-ups must be there for each side if separated you are only adding 2 small mosfet's to a design a BSS138 works well here and I've used 2N7000's with good results.

Doc

AN10441.pdf (52.4 KB)

Hi,

inserting pull-ups 4k7 to 3V3 does not makes it really better. It takes a bit longer until the programm stops but it is not stable.

Do you know a combined 6-DOF sensor (like: SparkFun 6 Degrees of Freedom IMU Digital Combo Board - ITG3200/ADXL345 - SEN-10121 - SparkFun Electronics) which operates at 5V with I2C or with an PWM output for the single values? I could only find analog ones or some which operates at 3.3V with I2C. Are there also sensors with PWM output?