Skip to content

Commit 41b75e3

Browse files
Merge pull request #11 from simplefoc/dev
Align with SimpleFOC 2.2.1 release
2 parents fe7a3ab + 14f416e commit 41b75e3

31 files changed

+996
-66
lines changed

.github/workflows/ccpp.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Library Compile
2+
on: push
3+
jobs:
4+
build:
5+
name: Test compile
6+
runs-on: ubuntu-latest
7+
strategy:
8+
matrix:
9+
arduino-boards-fqbn:
10+
- arduino:avr:uno # arudino uno
11+
- arduino:sam:arduino_due_x # arduino due
12+
- arduino:samd:nano_33_iot # samd21
13+
- adafruit:samd:adafruit_metro_m4 # samd51
14+
- esp32:esp32:esp32 # esp32
15+
- esp32:esp32:esp32s2 # esp32s2
16+
- STM32:stm32:GenF1:pnum=BLUEPILL_F103C8 # stm32 bluepill
17+
- STM32:stm32:Nucleo_64:pnum=NUCLEO_F411RE # stm32 nucleo
18+
- arduino:mbed_rp2040:pico # rpi pico
19+
include:
20+
- arduino-boards-fqbn: arduino:avr:uno
21+
sketch-names: '**.ino'
22+
required-libraries: Simple FOC
23+
- arduino-boards-fqbn: arduino:sam:arduino_due_x
24+
required-libraries: Simple FOC
25+
sketch-names: '**.ino'
26+
- arduino-boards-fqbn: arduino:samd:nano_33_iot
27+
required-libraries: Simple FOC
28+
sketch-names: '**.ino'
29+
- arduino-boards-fqbn: arduino:mbed_rp2040:pico
30+
required-libraries: Simple FOC
31+
sketch-names: '**.ino'
32+
- arduino-boards-fqbn: adafruit:samd:adafruit_metro_m4
33+
platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
34+
required-libraries: Simple FOC
35+
sketch-names: '**.ino'
36+
# - arduino-boards-fqbn: esp32:esp32:esp32doit-devkit-v1
37+
# platform-url: https://dl.espressif.com/dl/package_esp32_index.json
38+
# required-libraries: Simple FOC
39+
# sketch-names: '**.ino'
40+
- arduino-boards-fqbn: esp32:esp32:esp32 # esp32
41+
platform-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
42+
required-libraries: Simple FOC
43+
sketch-names: '**.ino'
44+
- arduino-boards-fqbn: esp32:esp32:esp32s2 # esp32s2
45+
platform-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
46+
required-libraries: Simple FOC
47+
sketch-names: '**.ino'
48+
- arduino-boards-fqbn: STM32:stm32:GenF1:pnum=BLUEPILL_F103C8
49+
platform-url: https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json
50+
required-libraries: Simple FOC
51+
sketch-names: '**.ino'
52+
- arduino-boards-fqbn: STM32:stm32:Nucleo_64:pnum=NUCLEO_F411RE
53+
platform-url: https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json
54+
required-libraries: Simple FOC
55+
sketch-names: '**.ino'
56+
# Do not cancel all jobs / architectures if one job fails
57+
fail-fast: false
58+
steps:
59+
- name: Checkout
60+
uses: actions/checkout@master
61+
- name: Compile all examples
62+
uses: ArminJo/arduino-test-compile@master
63+
with:
64+
arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }}
65+
required-libraries: ${{ matrix.required-libraries }}
66+
platform-url: ${{ matrix.platform-url }}
67+
sketch-names: ${{ matrix.sketch-names }}
68+
sketches-exclude: ${{ matrix.sketches-exclude }}

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# SimpleFOC Driver and Support Library
22

3+
![Library Compile](https://github.com/simplefoc/Arduino-FOC-drivers/workflows/Library%20Compile/badge.svg)
4+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5+
6+
37
This library contains an assortment of drivers and supporting code for SimpleFOC.
48

59
The intent is to keep the core of SimpleFOC clean, and thus easy to maintain, understand and port to different platforms. In addition to this core, there are various drivers and supporting code which has grown around SimpleFOC, and which we would like to make available to the community.
@@ -17,6 +21,9 @@ What's here? See the sections below. Each driver or function should come with it
1721
- [AS5048A SPI driver](src/encoders/as5048a/) - SPI driver for the AMS AS5048A absolute position magnetic rotary encoder IC.
1822
- [AS5047 SPI driver](src/encoders/as5047/) - SPI driver for the AMS AS5047P and AS5047D absolute position magnetic rotary encoder ICs.
1923
- [MA730 SPI driver](src/encoders/ma730/) - SPI driver for the MPS MagAlpha MA730 absolute position magnetic rotary encoder IC.
24+
- [MA730 SSI driver](src/encoders/ma730/) - SSI driver for the MPS MagAlpha MA730 absolute position magnetic rotary encoder IC.
25+
- [AS5145 SSI driver](src/encoders/as5145/) - SSI driver for the AMS AS5145 and AS5045 absolute position magnetic rotary encoder ICs.
26+
- [TLE5012B SPI driver](src/encoders/tle5012b/) - SPI (half duplex) driver for TLE5012B absolute position magnetic rotary encoder IC.
2027
- [STM32 Hardware Encoder](src/encoders/stm32hwencoder/) - Hardware timer based encoder driver for ABI type quadrature encoders.
2128

2229
### Communications

examples/drivers/drv8316/drv8316_3pwm.ino renamed to examples/drivers/drv8316/3pwm/drv8316_3pwm.ino

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
#include "Arduino.h"
55
#include <Wire.h>
66
#include <SimpleFOC.h>
7-
#include <Math.h>
7+
#include <SimpleFOCDrivers.h>
88
#include "drivers/drv8316/drv8316.h"
99

1010

1111

1212

1313
BLDCMotor motor = BLDCMotor(11);
14-
DRV8316Driver3PWM driver = DRV8316Driver3PWM(A3,A4,2,7,false); // MKR1010 3-PWM
14+
DRV8316Driver3PWM driver = DRV8316Driver3PWM(2,3,4,7,false); // use the right pins for your setup!
1515
#define ENABLE_A 0
1616
#define ENABLE_B 1
1717
#define ENABLE_C 6

examples/drivers/drv8316/drv8316_6pwm.ino renamed to examples/drivers/drv8316/6pwm/drv8316_6pwm.ino

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
#include "Arduino.h"
55
#include <Wire.h>
66
#include <SimpleFOC.h>
7-
#include <Math.h>
7+
#include <SimpleFOCDrivers.h>
88
#include "drivers/drv8316/drv8316.h"
99

1010

1111

1212

1313
BLDCMotor motor = BLDCMotor(11);
14-
DRV8316Driver6PWM driver = DRV8316Driver6PWM(A3,0,A4,1,2,6,7,false); // MKR1010 6-PWM
14+
DRV8316Driver6PWM driver = DRV8316Driver6PWM(0,1,2,3,4,6,7,false); // use the right pins for your setup!
1515

1616

1717

keywords.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
SimpleFOC KEYWORD1
2-
DRV8316 KEYWORD1
2+
DRV8316 KEYWORD1
3+
AS5048A KEYWORD1
4+
AS5047 KEYWORD1
5+
AS5145 KEYWORD1
6+
MA730 KEYWORD1
7+
TLE5012B KEYWORD1
8+
I2CCommander KEYWORD1
9+

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ paragraph=SimpleFOC runs BLDC and Stepper motors using the FOC algorithm. This l
77
category=Device Control
88
url=https://docs.simplefoc.com
99
architectures=*
10-
includes=DRV8316.h
10+
includes=SimpleFOCDrivers.h
1111
depends=Simple FOC

src/comms/i2c/I2CCommander.cpp

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,6 @@ void I2CCommander::addMotor(FOCMotor* motor){
2323

2424

2525

26-
I2CCommander_Motor_Status I2CCommander::getMotorStatus(uint8_t motorNum){
27-
if (motorNum<numMotors){
28-
if (motors[motorNum]->zero_electric_angle==NOT_SET) // TODO detect open-loop uninitialized state!
29-
return I2CCommander_Motor_Status::MOT_UNINITIALIZED;
30-
if (motors[motorNum]->enabled==0)
31-
return I2CCommander_Motor_Status::MOT_DISABLED;
32-
if (motors[motorNum]->shaft_velocity >= I2CCOMMANDER_MIN_VELOCITY_FOR_MOTOR_MOVING)
33-
return I2CCommander_Motor_Status::MOT_MOVING;
34-
return I2CCommander_Motor_Status::MOT_IDLE;
35-
}
36-
return I2CCommander_Motor_Status::MOT_UNKNOWN;
37-
};
38-
39-
40-
4126

4227
bool I2CCommander::readBytes(void* valueToSet, uint8_t numBytes){
4328
if (_wire->available()>=numBytes){
@@ -85,10 +70,19 @@ void I2CCommander::onRequest(){
8570

8671

8772

73+
/*
74+
Reads values from I2C bus and updates the motor's values.
75+
76+
Currently this isn't really thread-safe, but works ok in practice on 32-bit MCUs.
8877
78+
Do not use on 8-bit architectures where the 32 bit writes may not be atomic!
79+
80+
Plan to make this safe: the writes should be buffered, and not actually executed
81+
until in the main loop by calling commander->run();
82+
the run() method disables interrupts while the updates happen.
83+
*/
8984
bool I2CCommander::receiveRegister(uint8_t motorNum, uint8_t registerNum, int numBytes) {
9085
int val;
91-
float floatval;
9286
switch (registerNum) {
9387
case REG_MOTOR_ADDRESS:
9488
val = _wire->read(); // reading one more byte is definately ok, since numBytes>1
@@ -230,7 +224,23 @@ bool I2CCommander::receiveRegister(uint8_t motorNum, uint8_t registerNum, int nu
230224

231225

232226

227+
/*
228+
Reads values from motor/sensor and writes them to I2C bus. Intended to be run
229+
from the Wire.onRequest interrupt.
230+
231+
Assumes atomic 32 bit reads. On 8-bit arduino this assumption does not hold and this
232+
code is not safe on those platforms. You might read "half-written" floats.
233+
234+
A solution might be to maintain a complete set of shadow registers in the commander
235+
class, and update them in the run() method (which runs with interrupts off). Not sure
236+
of the performance impact of all those 32 bit read/writes though. In any case, since
237+
I use only 32 bit MCUs I'll leave it as an excercise to the one who needs it. ;-)
233238
239+
On 32 bit platforms the implication is that reads will occur atomically, so data will
240+
be intact, but they can occur at any time during motor updates, so different values might
241+
not be in a fully consistent state (i.e. phase A current might be from the current iteration
242+
but phase B current from the previous iteration).
243+
*/
234244
bool I2CCommander::sendRegister(uint8_t motorNum, uint8_t registerNum) {
235245
// read the current register
236246
switch(registerNum) {
@@ -239,8 +249,7 @@ bool I2CCommander::sendRegister(uint8_t motorNum, uint8_t registerNum) {
239249
_wire->write((uint8_t)lastcommandregister);
240250
_wire->write((uint8_t)lastcommanderror+1);
241251
for (int i=0;(i<numMotors && i<28); i++) { // at most 28 motors, so we can fit in one packet
242-
uint8_t status = (uint8_t)getMotorStatus(i);
243-
_wire->write(status);
252+
_wire->write(motors[motorNum]->motor_status);
244253
}
245254
break;
246255
case REG_MOTOR_ADDRESS:
@@ -377,7 +386,8 @@ bool I2CCommander::sendRegister(uint8_t motorNum, uint8_t registerNum) {
377386
writeFloat(motors[motorNum]->current_limit);
378387
break;
379388
case REG_MOTION_DOWNSAMPLE:
380-
_wire->write((uint32_t)motors[motorNum]->motion_downsample);
389+
_wire->write((int)motors[motorNum]->motion_downsample); // TODO int will have different sizes on different platforms
390+
// but using uint32 doesn't compile clean on all, e.g. RP2040
381391
break;
382392

383393
case REG_ZERO_ELECTRIC_ANGLE:
@@ -393,11 +403,12 @@ bool I2CCommander::sendRegister(uint8_t motorNum, uint8_t registerNum) {
393403
writeFloat(motors[motorNum]->phase_resistance);
394404
break;
395405
case REG_POLE_PAIRS:
396-
_wire->write((uint32_t)motors[motorNum]->pole_pairs);
406+
_wire->write((int)motors[motorNum]->pole_pairs);
397407
break;
398408

399409
case REG_SYS_TIME:
400-
_wire->write(millis());
410+
// TODO how big is millis()? Same on all platforms?
411+
_wire->write((int)millis());
401412
break;
402413
case REG_NUM_MOTORS:
403414
_wire->write(numMotors);

src/comms/i2c/I2CCommander.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ class I2CCommander {
2626
void onReceive(int numBytes);
2727
void onRequest();
2828

29-
I2CCommander_Motor_Status getMotorStatus(uint8_t motorNum);
30-
3129
protected:
3230
void writeFloat(float value);
3331
bool readBytes(void* valueToSet, uint8_t numBytes);

src/comms/i2c/I2CCommanderRegisters.h

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
#ifndef __I2CCOMMANDERREGISTERS_H__
32
#define __I2CCOMMANDERREGISTERS_H__
43

@@ -62,23 +61,6 @@ typedef enum : uint8_t {
6261

6362

6463

65-
// TODO stati are work in progress. For example, it would be good to differentiate between not moving because stalled,
66-
// and not moving because target set-point has been reached. It would be good to be able to track initialization state
67-
// and error state.
68-
typedef enum : uint8_t {
69-
MOT_UNINITIALIZED = 0x00, // Motor is not initialized
70-
MOT_INITIALIZING = 0x01, // TODO - this status is not used at the moment, can't detect it
71-
72-
MOT_DISABLED = 0x02, // motor is disabled
73-
MOT_IDLE = 0x03, // motor is enabled, but not moving (current_velocity==target==0 in velocity mode,
74-
// or position==target in position mode)
75-
MOT_MOVING = 0x04, // motor is moving
76-
77-
MOT_ERROR = 0x05, // TODO - motor is disabled because an error has occurred
78-
MOT_INIT_FAILED = 0x0F, // motor initialization failed
79-
MOT_UNKNOWN = 0xFF // incorrect motor number
80-
} I2CCommander_Motor_Status;
81-
8264

8365

8466
#endif

src/comms/i2c/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ This code takes the point of view that the motor driver (the "muscle") is the I2
99

1010
This is new code, and has not been extensively tested. Your milage may vary. That said, basic use cases have been tested, and we would certainly appreciate feedback and help with testing it out.
1111

12+
In particular, there are concurrency issues with reading/writing the SimpleFOC motor values from I2C while the motor is running. These should be solved soon in an upcoming version.
13+
14+
**Do not run on 8-bit MCUs!** The code currently assumes atomic 32 bit reads, so running on Arduino UNO or Nano is unfortunately a no-go.
15+
1216
## Using
1317

1418
As would be expected for I2C, each target device needs a unique I2C address on its bus, and setting up and discovering these addresses is out-of-scope for I2CCommander. Setting up and configuring the TwoWire objects (which pins, speed, etc...) is also out of scope and finished, initialized TwoWire objects must be passed to I2CCommander. If you don't specify a different reference, the standard *Wire* object is assumed.

0 commit comments

Comments
 (0)