Skip to content

Commit d328a74

Browse files
committed
Merge branch 'devel' into libfibre
2 parents 2caf0d3 + a9b2349 commit d328a74

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+2558
-1553
lines changed

.github/workflows/nightly.yaml

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ jobs:
1515
runs-on: ${{ matrix.os }}
1616
steps:
1717
- name: Install odrivetool
18+
shell: bash
1819
run: |
20+
pip3 list | grep setuptools || echo "setuptools not installed" # show version
21+
pip3 install setuptools # TODO: this should be a dependency of odrive
22+
pip3 install odrive
23+
env:
1924
# TODO: this is a workaround for https://github.com/pypa/setuptools/issues/2353 and we
2025
# can remove it as soon as python3-setuptools on GitHub's ubuntu-latest
2126
# moves to version 50.1+.
22-
pip3 list | grep setuptools # show version
23-
export SETUPTOOLS_USE_DISTUTILS=stdlib
24-
pip3 install odrive
27+
SETUPTOOLS_USE_DISTUTILS: stdlib
2528

2629
# This one currently fails because Github Actions runs pip as non-root
2730
#- name: Check if udev rules were set up properly

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Unreleased Features
22
Please add a note of your changes below this heading if you make a Pull Request.
33
### Added
4+
* Added phase balance check to motor calibration and MOTOR_ERROR_UNBALANCED_PHASES to error enums
45
* Added polarity and phase offset calibration for hall effect encoders
56
* [Mechanical brake support](docs/mechanical-brakes.md)
67
* Added periodic sending of encoder position on CAN
@@ -61,6 +62,7 @@ Please add a note of your changes below this heading if you make a Pull Request.
6162
* `<axis>.encoder.config.offset` was renamed to ``<axis>.encoder.config.phase_offset`
6263
* `<axis>.encoder.config.offset_float` was renamed to ``<axis>.encoder.config.phase_offset_float`
6364
* `<odrv>.config.brake_resistance == 0.0` is no longer a valid way to disable the brake resistor. Use `<odrv>.config.enable_brake_resistor` instead.
65+
* `<odrv>.can.set_baud_rate()` was removed. The baudrate is now automatically updated when writing to `<odrv>.can.config.baud_rate`.
6466

6567
# Releases
6668
## [0.5.1] - 2020-09-27

Firmware/Board/v3/Src/can.c

+38-23
Original file line numberDiff line numberDiff line change
@@ -56,31 +56,46 @@
5656

5757
/* USER CODE END 0 */
5858

59-
CAN_HandleTypeDef hcan1;
60-
61-
/* CAN1 init function */
62-
void MX_CAN1_Init(void)
63-
{
64-
65-
hcan1.Instance = CAN1;
66-
hcan1.Init.Prescaler = 8;
67-
hcan1.Init.Mode = CAN_MODE_NORMAL;
68-
hcan1.Init.SyncJumpWidth = CAN_SJW_4TQ;
69-
hcan1.Init.TimeSeg1 = CAN_BS1_16TQ;
70-
hcan1.Init.TimeSeg2 = CAN_BS2_4TQ;
71-
hcan1.Init.TimeTriggeredMode = DISABLE;
72-
hcan1.Init.AutoBusOff = ENABLE;
73-
hcan1.Init.AutoWakeUp = ENABLE;
74-
hcan1.Init.AutoRetransmission = ENABLE;
75-
hcan1.Init.ReceiveFifoLocked = DISABLE;
76-
hcan1.Init.TransmitFifoPriority = DISABLE;
77-
if (HAL_CAN_Init(&hcan1) != HAL_OK)
78-
{
79-
_Error_Handler(__FILE__, __LINE__);
59+
CAN_HandleTypeDef hcan1 = {
60+
.Instance = CAN1,
61+
.Init = {
62+
.Prescaler = 8,
63+
.Mode = CAN_MODE_NORMAL,
64+
.SyncJumpWidth = CAN_SJW_4TQ,
65+
.TimeSeg1 = CAN_BS1_16TQ,
66+
.TimeSeg2 = CAN_BS2_4TQ,
67+
.TimeTriggeredMode = DISABLE,
68+
.AutoBusOff = ENABLE,
69+
.AutoWakeUp = ENABLE,
70+
.AutoRetransmission = ENABLE,
71+
.ReceiveFifoLocked = DISABLE,
72+
.TransmitFifoPriority = DISABLE,
8073
}
74+
};
8175

82-
}
83-
76+
/* CAN1 init function */
77+
//void MX_CAN1_Init(void)
78+
//{
79+
//
80+
// hcan1.Instance = CAN1;
81+
// hcan1.Init.Prescaler = 8;
82+
// hcan1.Init.Mode = CAN_MODE_NORMAL;
83+
// hcan1.Init.SyncJumpWidth = CAN_SJW_4TQ;
84+
// hcan1.Init.TimeSeg1 = CAN_BS1_16TQ;
85+
// hcan1.Init.TimeSeg2 = CAN_BS2_4TQ;
86+
// hcan1.Init.TimeTriggeredMode = DISABLE;
87+
// hcan1.Init.AutoBusOff = ENABLE;
88+
// hcan1.Init.AutoWakeUp = ENABLE;
89+
// hcan1.Init.AutoRetransmission = ENABLE;
90+
// hcan1.Init.ReceiveFifoLocked = DISABLE;
91+
// hcan1.Init.TransmitFifoPriority = DISABLE;
92+
// if (HAL_CAN_Init(&hcan1) != HAL_OK)
93+
// {
94+
// _Error_Handler(__FILE__, __LINE__);
95+
// }
96+
//
97+
//}
98+
//
8499
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
85100
{
86101

Firmware/Board/v3/Src/spi.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,18 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
101101
PC11 ------> SPI3_MISO
102102
PC12 ------> SPI3_MOSI
103103
*/
104-
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
104+
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_12;
105105
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
106-
GPIO_InitStruct.Pull = GPIO_PULLUP; // required for disconnect detection on SPI encoders
106+
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // Idle clock and MOSI low.
107107
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
108108
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
109109
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
110110

111+
// MISO pull-up required for disconnect detection on SPI encoders with even parity
112+
GPIO_InitStruct.Pin = GPIO_PIN_11;
113+
GPIO_InitStruct.Pull = GPIO_PULLUP;
114+
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
115+
111116
/* SPI3 DMA Init */
112117
/* SPI3_TX Init */
113118
hdma_spi3_tx.Instance = DMA1_Stream7;

Firmware/Board/v3/board.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,6 @@ bool board_init() {
357357
// mode initialization won't override the CAN mode.
358358
if (odrv.config_.gpio_modes[15] != ODriveIntf::GPIO_MODE_CAN_A || odrv.config_.gpio_modes[16] != ODriveIntf::GPIO_MODE_CAN_A) {
359359
odrv.misconfigured_ = true;
360-
} else {
361-
MX_CAN1_Init();
362360
}
363361
}
364362

Firmware/Drivers/STM32/stm32_spi_arbiter.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ bool Stm32SpiArbiter::start() {
3636
HAL_SPI_DeInit(hspi_);
3737
hspi_->Init = task.config;
3838
HAL_SPI_Init(hspi_);
39+
__HAL_SPI_ENABLE(hspi_);
3940
}
4041
task.ncs_gpio.write(false);
4142

Firmware/Makefile

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ PROGRAMMER_CMD=$(if $(value PROGRAMMER),-c 'hla_serial $(PROGRAMMER)',)
99

1010
include tup.config # source build configuration to get CONFIG_BOARD_VERSION
1111

12+
ifeq ($(shell python -c "import sys; print(sys.version_info.major)"), 3)
13+
PY_CMD := python -B
14+
else
15+
PY_CMD := python3 -B
16+
endif
17+
1218
ifneq (,$(findstring v3.,$(CONFIG_BOARD_VERSION)))
13-
OPENOCD := openocd -f interface/stlink.cfg $(PROGRAMMER_CMD) -f target/stm32f4x.cfg -c init
19+
OPENOCD := openocd -f interface/stlink-v2.cfg $(PROGRAMMER_CMD) -f target/stm32f4x.cfg -c init
1420
GDB := arm-none-eabi-gdb --ex 'target extended-remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f4x.cfg" -c "gdb_port pipe; log_output openocd.log"' --ex 'monitor reset halt'
1521
else ifneq (,$(findstring v4.,$(CONFIG_BOARD_VERSION)))
1622
OPENOCD := openocd -f interface/stlink.cfg $(PROGRAMMER_CMD) -f target/stm32f7x.cfg -c 'reset_config none separate' -c init
@@ -23,7 +29,7 @@ $(info board version: $(CONFIG_BOARD_VERSION))
2329

2430
all:
2531
@tup --quiet --no-environ-check
26-
@python interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py
32+
@$(PY_CMD) interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py
2733

2834
fibre:
2935
(cd fibre-cpp && tup --no-environ-check)

Firmware/MotorControl/axis.cpp

+8-4
Original file line numberDiff line numberDiff line change
@@ -313,14 +313,18 @@ bool Axis::start_closed_loop_control() {
313313
motor_.current_control_.Idq_setpoint_src_.connect_to(&motor_.Idq_setpoint_);
314314
motor_.current_control_.Vdq_setpoint_src_.connect_to(&motor_.Vdq_setpoint_);
315315

316+
bool is_acim = motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM;
317+
// phase
316318
OutputPort<float>* phase_src = sensorless_mode ? &sensorless_estimator_.phase_ : &encoder_.phase_;
317-
motor_.current_control_.phase_src_.connect_to(phase_src);
318319
acim_estimator_.rotor_phase_src_.connect_to(phase_src);
319-
320+
OutputPort<float>* stator_phase_src = is_acim ? &acim_estimator_.stator_phase_ : phase_src;
321+
motor_.current_control_.phase_src_.connect_to(stator_phase_src);
322+
// phase vel
320323
OutputPort<float>* phase_vel_src = sensorless_mode ? &sensorless_estimator_.phase_vel_ : &encoder_.phase_vel_;
321-
motor_.phase_vel_src_.connect_to(phase_vel_src);
322-
motor_.current_control_.phase_vel_src_.connect_to(phase_vel_src);
323324
acim_estimator_.rotor_phase_vel_src_.connect_to(phase_vel_src);
325+
OutputPort<float>* stator_phase_vel_src = is_acim ? &acim_estimator_.stator_phase_vel_ : phase_vel_src;
326+
motor_.phase_vel_src_.connect_to(stator_phase_vel_src);
327+
motor_.current_control_.phase_vel_src_.connect_to(stator_phase_vel_src);
324328

325329
if (sensorless_mode) {
326330
// Make the final velocity of the loĉk-in spin the setpoint of the

Firmware/MotorControl/axis.hpp

-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ class Axis : public ODriveIntf::AxisIntf {
187187
AxisState requested_state_ = AXIS_STATE_STARTUP_SEQUENCE;
188188
std::array<AxisState, 10> task_chain_ = { AXIS_STATE_UNDEFINED };
189189
AxisState& current_state_ = task_chain_.front();
190-
uint32_t loop_counter_ = 0;
191190
Homing_t homing_;
192191
CAN_t can_;
193192

Firmware/MotorControl/encoder.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#ifndef __ENCODER_HPP
22
#define __ENCODER_HPP
33

4-
#include <arm_math.h>
4+
class Encoder;
5+
6+
#include <board.h> // needed for arm_math.h
57
#include <Drivers/STM32/stm32_spi_arbiter.hpp>
68
#include "utils.hpp"
79
#include <autogen/interfaces.hpp>

Firmware/MotorControl/main.cpp

+13-16
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ uint32_t _reboot_cookie __attribute__ ((section (".noinit")));
2626
extern char _estack; // provided by the linker script
2727

2828

29-
ODriveCAN::Config_t can_config;
30-
ODriveCAN *odCAN = nullptr;
3129
ODrive odrv{};
3230

3331

@@ -88,7 +86,7 @@ void StatusLedController::update() {
8886
static bool config_read_all() {
8987
bool success = board_read_config() &&
9088
config_manager.read(&odrv.config_) &&
91-
config_manager.read(&can_config);
89+
config_manager.read(&odrv.can_.config_);
9290
for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) {
9391
success = config_manager.read(&encoders[i].config_) &&
9492
config_manager.read(&axes[i].sensorless_estimator_.config_) &&
@@ -108,7 +106,7 @@ static bool config_read_all() {
108106
static bool config_write_all() {
109107
bool success = board_write_config() &&
110108
config_manager.write(&odrv.config_) &&
111-
config_manager.write(&can_config);
109+
config_manager.write(&odrv.can_.config_);
112110
for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) {
113111
success = config_manager.write(&encoders[i].config_) &&
114112
config_manager.write(&axes[i].sensorless_estimator_.config_) &&
@@ -127,7 +125,7 @@ static bool config_write_all() {
127125

128126
static void config_clear_all() {
129127
odrv.config_ = {};
130-
can_config = {};
128+
odrv.can_.config_ = {};
131129
for (size_t i = 0; i < AXIS_COUNT; ++i) {
132130
encoders[i].config_ = {};
133131
axes[i].sensorless_estimator_.config_ = {};
@@ -145,7 +143,7 @@ static void config_clear_all() {
145143
}
146144

147145
static bool config_apply_all() {
148-
bool success = true;
146+
bool success = odrv.can_.apply_config();
149147
for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) {
150148
success = encoders[i].apply_config(motors[i].config_.motor_type)
151149
&& axes[i].controller_.apply_config()
@@ -174,6 +172,12 @@ bool ODrive::save_configuration(void) {
174172
&& config_manager.start_store(&config_size)
175173
&& config_write_all()
176174
&& config_manager.finish_store();
175+
176+
// FIXME: during save_configuration we might miss some interrupts
177+
// because the CPU gets halted during a flash erase. Missing events
178+
// (encoder updates, step/dir steps) is not good so to be sure we just
179+
// reboot.
180+
NVIC_SystemReset();
177181
}
178182

179183
return success;
@@ -265,24 +269,23 @@ void vApplicationIdleHook(void) {
265269
odrv.system_stats_.max_stack_usage_usb = stack_size_usb_thread - uxTaskGetStackHighWaterMark(usb_thread) * sizeof(StackType_t);
266270
odrv.system_stats_.max_stack_usage_uart = stack_size_uart_thread - uxTaskGetStackHighWaterMark(uart_thread) * sizeof(StackType_t);
267271
odrv.system_stats_.max_stack_usage_startup = stack_size_default_task - uxTaskGetStackHighWaterMark(defaultTaskHandle) * sizeof(StackType_t);
268-
odrv.system_stats_.max_stack_usage_can = odCAN->stack_size_ - uxTaskGetStackHighWaterMark(odCAN->thread_id_) * sizeof(StackType_t);
272+
odrv.system_stats_.max_stack_usage_can = odrv.can_.stack_size_ - uxTaskGetStackHighWaterMark(odrv.can_.thread_id_) * sizeof(StackType_t);
269273

270274
odrv.system_stats_.stack_size_axis = axes[0].stack_size_;
271275
odrv.system_stats_.stack_size_usb = stack_size_usb_thread;
272276
odrv.system_stats_.stack_size_uart = stack_size_uart_thread;
273277
odrv.system_stats_.stack_size_startup = stack_size_default_task;
274-
odrv.system_stats_.stack_size_can = odCAN->stack_size_;
278+
odrv.system_stats_.stack_size_can = odrv.can_.stack_size_;
275279

276280
odrv.system_stats_.prio_axis = osThreadGetPriority(axes[0].thread_id_);
277281
odrv.system_stats_.prio_usb = osThreadGetPriority(usb_thread);
278282
odrv.system_stats_.prio_uart = osThreadGetPriority(uart_thread);
279283
odrv.system_stats_.prio_startup = osThreadGetPriority(defaultTaskHandle);
280-
odrv.system_stats_.prio_can = osThreadGetPriority(odCAN->thread_id_);
284+
odrv.system_stats_.prio_can = osThreadGetPriority(odrv.can_.thread_id_);
281285

282286
status_led_controller.update();
283287
}
284288
}
285-
286289
}
287290

288291
/**
@@ -431,9 +434,6 @@ void ODrive::control_loop_cb(uint32_t timestamp) {
431434
axis.max_endstop_.update();
432435
}
433436

434-
MEASURE_TIME(axis.task_times_.can_heartbeat)
435-
odCAN->send_cyclic(axis);
436-
437437
MEASURE_TIME(axis.task_times_.controller_update)
438438
axis.controller_.update(); // uses position and velocity from encoder
439439

@@ -827,9 +827,6 @@ extern "C" int main(void) {
827827
sem_can = osSemaphoreCreate(osSemaphore(sem_can), 1);
828828
osSemaphoreWait(sem_can, 0);
829829

830-
// Construct all objects.
831-
odCAN = new ODriveCAN(can_config, &hcan1);
832-
833830
// Create main thread
834831
osThreadDef(defaultTask, rtos_main, osPriorityNormal, 0, stack_size_default_task / sizeof(StackType_t));
835832
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

Firmware/MotorControl/motor.cpp

+16-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
#include <algorithm>
88

9-
#define CURRENT_ADC_LOWER_BOUND (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MIN_VOLT / 3.3f)
10-
#define CURRENT_ADC_UPPER_BOUND (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MAX_VOLT / 3.3f)
9+
static constexpr auto CURRENT_ADC_LOWER_BOUND = (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MIN_VOLT / 3.3f);
10+
static constexpr auto CURRENT_ADC_UPPER_BOUND = (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MAX_VOLT / 3.3f);
1111

1212
/**
1313
* @brief This control law adjusts the output voltage such that a predefined
@@ -29,6 +29,7 @@ struct ResistanceMeasurementControlLaw : AlphaBetaFrameController {
2929
if (Ialpha_beta.has_value()) {
3030
actual_current_ = Ialpha_beta->first;
3131
test_voltage_ += (kI * current_meas_period) * (target_current_ - actual_current_);
32+
I_beta_ += (kIBetaFilt * current_meas_period) * (Ialpha_beta->second - I_beta_);
3233
} else {
3334
actual_current_ = 0.0f;
3435
test_voltage_ = 0.0f;
@@ -63,11 +64,17 @@ struct ResistanceMeasurementControlLaw : AlphaBetaFrameController {
6364
return test_voltage_ / target_current_;
6465
}
6566

67+
float get_Ibeta() {
68+
return I_beta_;
69+
}
70+
6671
const float kI = 1.0f; // [(V/s)/A]
72+
const float kIBetaFilt = 80.0f;
6773
float max_voltage_ = 0.0f;
6874
float actual_current_ = 0.0f;
6975
float target_current_ = 0.0f;
7076
float test_voltage_ = 0.0f;
77+
float I_beta_ = 0.0f; // [A] low pass filtered Ibeta response
7178
std::optional<float> test_mod_ = NAN;
7279
};
7380

@@ -325,7 +332,7 @@ bool Motor::setup() {
325332
max_dc_calib_ = 0.1f * max_allowed_current_;
326333

327334
if (!gate_driver_.init())
328-
return true;
335+
return false;
329336

330337
return true;
331338
}
@@ -435,6 +442,12 @@ bool Motor::measure_phase_resistance(float test_current, float max_voltage) {
435442
success = false;
436443
}
437444

445+
float I_beta = control_law.get_Ibeta();
446+
if (is_nan(I_beta) || (abs(I_beta) / test_current) > 0.1f) {
447+
disarm_with_error(ERROR_UNBALANCED_PHASES);
448+
success = false;
449+
}
450+
438451
return success;
439452
}
440453

0 commit comments

Comments
 (0)