ATmega328pb for SFOC control with two as5600(I2C)

Hi guys, as you know 328p has only 1 channel I2C, which limits the use of 2 AS5600 at the same time. so I made a board with 328pb, two I2Cs for as5600 sensor. but there are some problems with the code

> TwoWire Wire1(PE0, PE1);

// I2C magnetic sensor instance
    MagneticSensorI2C sensor1 = MagneticSensorI2C(AS5600_I2C);
    MagneticSensorI2C sensor2 = MagneticSensorI2C(AS5600_I2C);

// example of stm32 defining 2nd bus
// To be tested in mega328pb, PE0 & PE1 are the second I2C port for 328pb
TwoWire Wire1(PE0, PE1);

I add the lib of minicore, and selsect 328pb, but the IDE didnt know "TwoWire Wire1(PE0, PE1)"

Arduino: 1.8.8 (Windows 10), Board: "ATmega328, Yes (UART0), EEPROM retained, 328PB, BOD 4.3V, LTO disabled, External 16 MHz"

atmega328pb_bldc_magnetic_i2c:53:23: error: no matching function for call to 'TwoWire::TwoWire(int, int)'

 TwoWire Wire1(PE0, PE1);

                   ^

In file included from C:\Users\310210741\Documents\Arduino\libraries\Simple_FOC\src/sensors/MagneticSensorI2C.h:5:0,

             from C:\Users\310210741\Documents\Arduino\libraries\Simple_FOC\src/SimpleFOC.h:103,

             from C:\Users\310210741\Downloads\Simple FOC\Datasheet & referance\Arduino-FOC-minimal\library_source\atmega328pb_bldc_magnetic_i2c\atmega328pb_bldc_magnetic_i2c.ino:39:

C:\Users\310210741\Documents\Arduino\hardware\MiniCore-master\avr\libraries\Wire\src/Wire.h:115:5: note: candidate: TwoWire::TwoWire(int, void (*)(), void (*)(), void (*)(uint8_t), void (*)(uint32_t), uint8_t (*)(uint8_t, uint8_t*, uint8_t, uint8_t), uint8_t (*)(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t), uint8_t (*)(const uint8_t*, uint8_t), void (*)(uint8_t), void (*)(), void (*)(), void (*)(void (*)(uint8_t*, int)), void (*)(uint8_t*, int), void (*)(void (*)()), void (*)())

 TwoWire(int bufferLength,

 ^~~~~~~

C:\Users\310210741\Documents\Arduino\hardware\MiniCore-master\avr\libraries\Wire\src/Wire.h:115:5: note:   candidate expects 15 arguments, 2 provided

C:\Users\310210741\Documents\Arduino\hardware\MiniCore-master\avr\libraries\Wire\src/Wire.h:87:7: note: candidate: constexpr TwoWire::TwoWire(const TwoWire&)

 class TwoWire : public Stream

   ^~~~~~~

C:\Users\310210741\Documents\Arduino\hardware\MiniCore-master\avr\libraries\Wire\src/Wire.h:87:7: note:   candidate expects 1 argument, 2 provided

exit status 1
no matching function for call to 'TwoWire::TwoWire(int, int)'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

I know it’s write for stm32, is there any .h file that need to modify to adapt to 328pb? I checked into the define of TwoWire class(wire.h), but that’s bit of complex for me -_-…

Nice board!

I have a feeling that Wire1 doesn’t need to know the pins. You could try something like this:

#include <Arduino.h>
#include <SimpleFOC.h>
#include <Wire1.h>

MagneticSensorI2C sensor1 = MagneticSensorI2C(AS5600_I2C);
MagneticSensorI2C sensor2 = MagneticSensorI2C(AS5600_I2C);

void setup() {
  Wire.begin();
  Wire1.begin();
  sensor1.init();
  sensor2.init(&Wire1);
}

void loop() {
  // put your main code here, to run repeatedly:
}

Yes! it works Willians! How could you be so smart!
After assembling the PCBA, I would test the code and share the results~
thanks!

1 Like

@zdldcyy - good to hear its working. Others who have run into this problem have successfully used two MagneticSensorAnalog classes (AS5600 also supports analog mode) but your approach will have less noise so is better if you have the pins.

Hi Williams, I made the PCBA and ran the dual sensor test, and it worked. but when it came to the second motor velocity or angle loop control, it failed the control. I cannot understand, I have already set up the wire1 sensor.

#include <SimpleFOC.h>
#include <Wire1.h>
MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);

// BLDC motor & driver instance
BLDCMotor motor = BLDCMotor(11);
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 10, 11, 8);

void setup() {
 
  // initialise magnetic sensor hardware
  Wire1.setClock(400000);
  Wire1.begin();
  sensor.init(&Wire1);
  // link the motor to the sensor
  motor.linkSensor(&sensor);

  // driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 5;
  driver.init();
  // link the motor and the driver
  motor.linkDriver(&driver);

  // set motion control loop to be used
  motor.controller = ControlType::velocity;

  // contoller configuration 
  // default parameters in defaults.h

  // velocity PI controller parameters
  motor.PID_velocity.P = 0.2;
  motor.PID_velocity.I = 20;
  motor.PID_velocity.D = 0;
  // default voltage_power_supply
  motor.voltage_limit = 5;

  motor.PID_velocity.output_ramp = 1000;

  motor.LPF_velocity.Tf = 0.02;

  // use monitoring with serial 
  Serial.begin(115200);
  // comment out if not needed
  motor.useMonitoring(Serial);

  // initialize motor
  motor.init();
  // align sensor and start FOC
  motor.initFOC();

  Serial.println("Motor ready.");
  Serial.println("Set the target velocity using serial terminal:");
  _delay(1000);
}

// velocity set point variable
float target_velocity = 0;

void loop() {

  motor.loopFOC();

  motor.move(target_velocity);
  
  // user communication
  serialReceiveUserCommand();
}
void serialReceiveUserCommand() {
  
  // a string to hold incoming data
  static String received_chars;
  
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the string buffer:
    received_chars += inChar;
    // end of user input
    if (inChar == '\n') {
      
      // change the motor target
      target_velocity = received_chars.toFloat();
      Serial.print("Target velocity: ");
      Serial.println(target_velocity);
      
      // reset the command buffer 
      received_chars = "";
    }
  }
}

from the serial port window it always says
"
MOT: Align sensor.
MOT: Sensor failed to notice movement
"
I think this means the sensor wasn’t detected by the system. Do you have any suggestions?
Thanks!

MOT: Sensor failed to notice movement

…is a message displayed after calibration. What is supposed to happen is that the motor moves 5 steps to right and then five steps to left. The motor asks the sensor for angle after each position. In your case the angle reported by sensor didn’t change.

Did the motor move at all?

Perhaps you can get it to print out the angle during each calibration step?

5v for power supply is too low for most drivers. Most start working at 6-8V, some at 12V. Have you tried this in velocity_openloop mode? Be careful/cautious with voltage limit, motors might get hot at 5V.

I usually start with powersupply of 12v and a voltage limit of 1 or 2v. I’ll increase that voltage limit when I’m happy board and motor are not getting hot.

Yes Williams, something wrong with my motor and now i fix the problem.
when i tried two motor move together with 2 i2c port sensor, the ide telled me that:
"Sketch uses 20772 bytes (64%) of program storage space. Maximum is 32256 bytes.
Global variables use 1866 bytes (91%) of dynamic memory, leaving 182 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.
"
download succeeded without any error, but the motor and the serial port has no reactions at all. I not sure whether it’s related to the low memory, i used 91% of them after all.
i used the example in lib “steer by wire”, and change it to 2 as5600(which use 2 I2C ports). if it was the reason of low memory, i’m afrid i have to give up this solution…

below is my code for referance:

#include <Wire1.h>
#include <SimpleFOC.h>
BLDCMotor motor1 = BLDCMotor(11);
BLDCDriver3PWM driver1 = BLDCDriver3PWM(3, 5, 6, 7);
MagneticSensorI2C sensor1 = MagneticSensorI2C(AS5600_I2C);

BLDCMotor motor2 = BLDCMotor(11);
BLDCDriver3PWM driver2 = BLDCDriver3PWM(9, 10, 11, 8);
MagneticSensorI2C sensor2 = MagneticSensorI2C(AS5600_I2C);

void setup() {

  // initialise encoder hardware
  sensor1.init();
  sensor2.init(&Wire1);
  
  // link the motor to the sensor
  motor1.linkSensor(&sensor1);
  motor2.linkSensor(&sensor2);
  
  // config drivers
  driver1.init();
  driver2.init();
  // link the motor to the driver
  motor1.linkDriver(&driver1);
  motor2.linkDriver(&driver2);

  // set control loop type to be used
  motor1.controller = ControlType::voltage;
  motor2.controller = ControlType::velocity;

  motor2.LPF_velocity.Tf = 0.02;
  motor2.PID_velocity.I = 40;

  // use monitoring with serial for motor init
  // monitoring port
  Serial.begin(115200);
  // enable monitoring
  //motor1.useMonitoring(Serial);
  //motor2.useMonitoring(Serial);

  // initialise motor
  motor1.init();
  motor2.init();
  // align encoder and start FOC
  motor1.initFOC();
  motor2.initFOC();

  Serial.println("Interactive gauge ready!");
  _delay(1000);
}


void loop() {
  // iterative setting FOC phase voltage
  motor1.loopFOC();
  motor2.loopFOC();

  // iterative function setting the outter loop target
  motor1.move(5*(motor2.shaft_velocity/10 - motor1.shaft_angle));
  motor2.move(10*dead_zone(motor1.shaft_angle));
  
}

float dead_zone(float x){
  return abs(x) < 0.2 ? 0 : x;
}

Great to hear you got it working!

You are really pushing the atmega328p to it’s limits. There is a minimal simplefoc example/branch somewhere which encourages you to only import what you need. Perhaps that would help

Hey @zdldcyy,
I’m happy you were able to make it compile ad work a bit!

There is a few steps to downsize the code. One of them is definitely to build the minimal version of the library. You can follow the tutorial in the readme file of the minimal branch. It will not take you more than 10-15 minutes to create.

Furthermore, one of the biggest dynamical memory consumers is the sine table used to approximate the sine functions (to make the commutation faster). But in your case the performance is not as important. So what I would suggest you do, after you have created the minimal repository following the instructions in the readme file, is to open BLDCMotor.cpp file and replace all the _sin() and _cos() functions by sin() and cos(). A priori you those will be the lines: 283,284, 319,320.

Once you have changed these lines your dynamical memory use will be up to 20% smaller.

Let me know how it went!

Hey Antun~
that’s really a great suggestion for me, you know, I almost throw away my baord until i saw your message :joy:
i tried the minimal version before, but failed. the ide telled me that:

C:\Users\Davi\AppData\Local\Temp\arduino_build_995143\sketch\src\drivers\BLDCDriver3PWM.cpp.o: In function `BLDCDriver3PWM::init()’:
C:\Users\Davi\AppData\Local\Temp\arduino_build_995143\sketch\src\drivers/BLDCDriver3PWM.cpp:53:

undefined reference to '_configure3PWM(long, int, int, int)'

C:\Users\Davi\AppData\Local\Temp\arduino_build_995143\sketch\src\drivers\BLDCDriver3PWM.cpp.o: In function `BLDCDriver3PWM::setPwm(float, float, float)’:
C:\Users\Davi\AppData\Local\Temp\arduino_build_995143\sketch\src\drivers/BLDCDriver3PWM.cpp:71:

undefined reference to `_writeDutyCycle3PWM(float, float, float, int, int, int’

collect2.exe: error: ld returned 1 exit status

I compiled with Minicore lib, seems that some files was missing.
then I changed the core from Mega328pb to Mega328p(still Minicore lib) and compiled again, it succeeded. that confused me a lot.
Finally i gave up Minimal version code and used full verison of SFOC, still, with Minicore lib and 328pb core, compile and download ok. I think the code are almost the same between Minimal version and full version. is there any possibility that i missed certain lib file?

Hey @zdldcyy,

I think it is one more time the Atmega328pb support issue. It is due to the compiler flags that we have been using to identify the Atmega328p chips.
Could you please replace the line 3 in the drivers/hardware_specific/atmega328_mcu.cpp with:

#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega328PB__)

And replace the lines 6 and 22 in the file common/time_utils.cpp with the line:

#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328PB__)

Let me know how it goes :smiley:

Hey, I don’t know what the others think, but you might find reading 2 I2C sensors for closed loop control on an ATMega328P is a bit slow anyways… it will limit your FOC iteration rate to 325Hz per motor - a bit too slow perhaps?
This because it is doing 4 x call to getAngle via I2C for each iteration, 2 x for each motor, once each in getAngle() (called by loopFOC) and getVelocity() (called by move()).

I think if you modify the MagneticSensorI2C class so that the getVelocity() function uses the value returned by the previous call to getAngle(), you will have only 2 sensor reads per iteration instead of 4 and this will improve things a lot.

Here’s a picture:

That is for one motor, on an ATMega328P. The top line shows the call to loopFOC(), the second line show the call to move() and the third line shows the call to getAngle()… So the bracketed section is the time for one complete iteration…

Hey @runger, could you do a github issue for this topic. I would like to implement this in the code but my todo list is growing rapidly and github issues I will not forget :smiley:

Absolutely: https://github.com/simplefoc/Arduino-FOC/issues/49

Hey @runger ~
that’s very intresting discovery. How about run this two task in multi-thread? one FOC loop, one getAngle(). I’m not sure what would heppen.

Hey @Antun_Skuric,
so many thanks for your suggestions, i have succeed running two motors together with minimal version code in velocity, voltage and position loop with 2 as5600.
1

I found a phenomenon that when i use minimal version code, the motor runs very smoothly; while using full version code, the motor shocks a lot and very noisy, even zero speed. (i didn’t change from “_sin” & “_cos” to “sin” & “cos” in minimal version code)

Cool!
Im happy to hear that :smiley:

Full version does not have the compiler flags that you added and it will not go figure the timers right.
We will include the new files to the new version and then there should be no real difference in terms of smoothness. :slight_smile: