Skip to content

Commit

Permalink
Merge pull request #313 from tinymovr/develop
Browse files Browse the repository at this point in the history
Version 1.6.3
  • Loading branch information
yconst authored Jan 17, 2024
2 parents d89a4b0 + 6c4348b commit b05b8d2
Show file tree
Hide file tree
Showing 19 changed files with 1,144 additions and 208 deletions.
2 changes: 1 addition & 1 deletion firmware/src/adc/adc.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ void ADC_DTSE_Init(void)
PAC55XX_ADC->DTSETRIGENT0TO3.TRIG1CFGIDX = 12; // DTSE Trigger 1 Sequence Configuration Entry Index
PAC55XX_ADC->DTSETRIGENT0TO3.TRIG1EDGE = ADCDTSE_TRIGEDGE_RISING; // PWMA0 rising edge

pac5xxx_timer_a_ccctr1_value_set((timer_freq_hz / 2 / PWM_FREQ_HZ) - 2);
pac5xxx_timer_a_ccctr1_value_set((TIMER_FREQ_HZ/(2*PWM_FREQ_HZ)) - 2);

//===== Setup DTSE Sequence B (sense current) - Starts at Entry 12 =====
pac5xxx_dtse_seq_config(12, ADC0, EMUX_AIO10, 0, 0);
Expand Down
2 changes: 1 addition & 1 deletion firmware/src/can/can_endpoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


uint8_t (*avlos_endpoints[79])(uint8_t * buffer, uint8_t * buffer_len, Avlos_Command cmd) = {&avlos_protocol_hash, &avlos_uid, &avlos_fw_version, &avlos_hw_revision, &avlos_Vbus, &avlos_Ibus, &avlos_power, &avlos_temp, &avlos_calibrated, &avlos_errors, &avlos_save_config, &avlos_erase_config, &avlos_reset, &avlos_enter_dfu, &avlos_scheduler_errors, &avlos_controller_state, &avlos_controller_mode, &avlos_controller_warnings, &avlos_controller_errors, &avlos_controller_position_setpoint, &avlos_controller_position_p_gain, &avlos_controller_velocity_setpoint, &avlos_controller_velocity_limit, &avlos_controller_velocity_p_gain, &avlos_controller_velocity_i_gain, &avlos_controller_velocity_deadband, &avlos_controller_velocity_increment, &avlos_controller_current_Iq_setpoint, &avlos_controller_current_Id_setpoint, &avlos_controller_current_Iq_limit, &avlos_controller_current_Iq_estimate, &avlos_controller_current_bandwidth, &avlos_controller_current_Iq_p_gain, &avlos_controller_current_max_Ibus_regen, &avlos_controller_current_max_Ibrake, &avlos_controller_voltage_Vq_setpoint, &avlos_controller_calibrate, &avlos_controller_idle, &avlos_controller_position_mode, &avlos_controller_velocity_mode, &avlos_controller_current_mode, &avlos_controller_set_pos_vel_setpoints, &avlos_comms_can_rate, &avlos_comms_can_id, &avlos_motor_R, &avlos_motor_L, &avlos_motor_pole_pairs, &avlos_motor_type, &avlos_motor_offset, &avlos_motor_direction, &avlos_motor_calibrated, &avlos_motor_I_cal, &avlos_motor_errors, &avlos_encoder_position_estimate, &avlos_encoder_velocity_estimate, &avlos_encoder_type, &avlos_encoder_bandwidth, &avlos_encoder_calibrated, &avlos_encoder_errors, &avlos_traj_planner_max_accel, &avlos_traj_planner_max_decel, &avlos_traj_planner_max_vel, &avlos_traj_planner_t_accel, &avlos_traj_planner_t_decel, &avlos_traj_planner_t_total, &avlos_traj_planner_move_to, &avlos_traj_planner_move_to_tlimit, &avlos_traj_planner_errors, &avlos_homing_velocity, &avlos_homing_max_homing_t, &avlos_homing_retract_dist, &avlos_homing_warnings, &avlos_homing_stall_detect_velocity, &avlos_homing_stall_detect_delta_pos, &avlos_homing_stall_detect_t, &avlos_homing_home, &avlos_watchdog_enabled, &avlos_watchdog_triggered, &avlos_watchdog_timeout };
uint32_t avlos_proto_hash = 4118115615;
uint32_t avlos_proto_hash = 3526126264;

uint32_t _avlos_get_proto_hash(void)
{
Expand Down
5 changes: 4 additions & 1 deletion firmware/src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,19 @@
#define BOARD_REV_IDX 21
#endif

#define TIMER_FREQ_HZ (ACLK_FREQ_HZ >> TXCTL_PS_DIV)

static const float one_by_sqrt3 = 0.57735026919f;
static const float two_by_sqrt3 = 1.15470053838f;
static const float threehalfpi = 4.7123889f;
static const float pi = PI;
static const float halfpi = PI * 0.5f;
static const float quarterpi = PI * 0.25f;
static const int32_t timer_freq_hz = ACLK_FREQ_HZ >> TXCTL_PS_DIV;
static const float twopi_by_enc_ticks = TWOPI / ENCODER_TICKS;
static const float twopi_by_hall_sectors = TWOPI / HALL_SECTORS;

_Static_assert (TIMER_FREQ_HZ % (2*PWM_FREQ_HZ) == 0, "Timer frequency not an integer multiple of PWM frequency");

typedef struct
{
float A;
Expand Down
6 changes: 3 additions & 3 deletions firmware/src/gatedriver/gatedriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ void gate_driver_set_duty_cycle(const FloatTriplet *dc);
//=============================================
static inline void m1_u_set_duty(const float duty)
{
uint16_t val = ((uint16_t)(duty * (timer_freq_hz/PWM_FREQ_HZ) )) >>1;
uint16_t val = ((uint16_t)(duty * (TIMER_FREQ_HZ/PWM_FREQ_HZ) )) >>1;
PAC55XX_TIMERA->CCTR4.CTR = val;
}
static inline void m1_v_set_duty(const float duty)
{
uint16_t val = ((uint16_t)(duty * (timer_freq_hz/PWM_FREQ_HZ) )) >>1;
uint16_t val = ((uint16_t)(duty * (TIMER_FREQ_HZ/PWM_FREQ_HZ) )) >>1;
PAC55XX_TIMERA->CCTR5.CTR = val;
}
static inline void m1_w_set_duty(const float duty)
{
uint16_t val = ((uint16_t)(duty * (timer_freq_hz/PWM_FREQ_HZ) )) >>1;
uint16_t val = ((uint16_t)(duty * (TIMER_FREQ_HZ/PWM_FREQ_HZ) )) >>1;
PAC55XX_TIMERA->CCTR6.CTR = val;
}

Expand Down
2 changes: 1 addition & 1 deletion firmware/src/timer/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void Timer_Init(void)
{
// Configure Timer A Controls
pac5xxx_timer_clock_config(TimerA, TXCTL_CS_ACLK, TXCTL_PS_DIV); // Configure timer clock input for ACLK, divider
pac5xxx_timer_base_config(TimerA, (timer_freq_hz/2/PWM_FREQ_HZ), AUTO_RELOAD,
pac5xxx_timer_base_config(TimerA, (TIMER_FREQ_HZ/(2*PWM_FREQ_HZ)), AUTO_RELOAD,
TxCTL_MODE_UPDOWN, TIMER_SLAVE_SYNC_DISABLE); // Configure timer frequency and count mode

// Configure Dead time generators
Expand Down
6 changes: 4 additions & 2 deletions studio/Python/tests/test_base_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@ def test_position_control(self):
Test position control
"""
self.check_state(0)
self.tm.motor.I_cal = 5
self.tm.controller.current.Iq_limit = 5
self.try_calibrate()
self.tm.controller.position_mode()
self.check_state(2)

for i in range(5):
self.tm.controller.position.setpoint = i * 10000 * ticks
self.tm.controller.position.setpoint = i * 3000 * ticks
time.sleep(0.25)
self.assertAlmostEqual(
i * 10000 * ticks, self.tm.encoder.position_estimate, delta=1000 * ticks
i * 3000 * ticks, self.tm.encoder.position_estimate, delta=1000 * ticks
)


Expand Down
33 changes: 22 additions & 11 deletions studio/Python/tests/test_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,19 +367,30 @@ def test_p_flux_braking(self):
# Ensure we're idle
self.check_state(0)
self.try_calibrate()

self.tm.controller.current.max_Ibrake = 10
self.tm.controller.current.max_Ibrake = 0
self.tm.controller.velocity_mode()
self.tm.controller.velocity.setpoint = 200000
time.sleep(0.4)
self.tm.controller.velocity.setpoint = 0
I_brake_vals = []
for _ in range(50):
I_brake_vals.append(self.tm.Ibus)
time.sleep(0.005)
time.sleep(0.5)
for v_set in [-250000, 250000]:
self.tm.controller.velocity.setpoint = v_set
time.sleep(0.4)
self.tm.controller.velocity.setpoint = 0
I_brake_vals = []
for _ in range(200):
I_brake_vals.append(self.tm.Ibus)
time.sleep(0.001)
time.sleep(0.2)
self.assertLess(min(I_brake_vals), -0.12 * A)
self.tm.controller.current.max_Ibrake = 10
for v_set in [-250000, 250000]:
self.tm.controller.velocity.setpoint = v_set
time.sleep(0.4)
self.tm.controller.velocity.setpoint = 0
I_brake_vals = []
for _ in range(200):
I_brake_vals.append(self.tm.Ibus)
time.sleep(0.001)
time.sleep(0.2)
self.assertGreater(min(I_brake_vals), -0.12 * A)
self.tm.controller.current.max_Ibrake = 0
self.assertGreater(min(I_brake_vals), -1 * A)
self.tm.controller.idle()
time.sleep(0.4)

Expand Down
3 changes: 1 addition & 2 deletions studio/Python/tests/test_dfu.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
from tinymovr import init_tee, destroy_tee
from tinymovr.config import (
get_bus_config,
create_device,
definitions
create_device
)

import unittest
Expand Down
12 changes: 10 additions & 2 deletions studio/Python/tinymovr/cli.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
"""Tinymovr Studio CLI
Usage:
tinymovr_cli [--bus=<bus>] [--chan=<chan>] [--bitrate=<bitrate>]
tinymovr_cli [--bus=<bus>] [--chan=<chan>] [--spec=<spec>] [--bitrate=<bitrate>]
tinymovr_cli -h | --help
tinymovr_cli --version
Options:
--bus=<bus> One or more interfaces to use, first available is used [default: canine,slcan_disco].
--chan=<chan> The bus device "channel".
--spec=<spec> A custom device spec to be added to the list of discoverable spec.
--bitrate=<bitrate> CAN bitrate [default: 1000000].
"""

import yaml
import can
import pkg_resources
import IPython
Expand All @@ -20,7 +22,7 @@
from tinymovr import init_tee, destroy_tee
from tinymovr.discovery import Discovery
from tinymovr.constants import app_name
from tinymovr.config import get_bus_config, configure_logging
from tinymovr.config import get_bus_config, configure_logging, add_spec

"""
Tinymovr CLI Module
Expand Down Expand Up @@ -49,6 +51,12 @@ def spawn():

logger = configure_logging()

spec_file = arguments["--spec"]
if spec_file:
with open(spec_file, 'r') as file:
spec_data = yaml.safe_load(file)
add_spec(spec_data, logger)

buses = arguments["--bus"].rsplit(sep=",")
channel = arguments["--chan"]
bitrate = int(arguments["--bitrate"])
Expand Down
3 changes: 1 addition & 2 deletions studio/Python/tinymovr/config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from tinymovr.config.config import (
get_bus_config,
configure_logging,
definitions,
create_device,
create_device_with_hash_msg,
ProtocolVersionError,
add_spec,
)
82 changes: 38 additions & 44 deletions studio/Python/tinymovr/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,30 @@
from tinymovr.codec import DataType
from tinymovr.channel import CANChannel

definitions = {"hash_uint32": {}, "name": {}}

for yaml_file in Path(files("tinymovr").joinpath("specs/")).glob("*.yaml"):
with open(str(yaml_file)) as def_raw:
definition = yaml.safe_load(def_raw)
tmp_node = deserialize(definition)
definitions["hash_uint32"][tmp_node.hash_uint32] = definition
definitions["name"][definition["name"]] = definition


class ProtocolVersionError(Exception):
def __init__(self, dev_id, version_str, *args, **kwargs):
self.dev_id = dev_id
self.version_str = cleanup_incomplete_version(version_str)
msg = (
"Incompatible protocol versions (hash mismatch) for device {}. "
"Firmware is compatible with Studio version {}.\n\n"
"Either upgrade studio and firmware, or install a compatible Studio version like so:\n\n"
"pip3 uninstall tinymovr\npip3 install tinymovr=={}".format(
self.dev_id, self.version_str, self.version_str
)
)
super().__init__(msg, *args, **kwargs)
specs = {"hash_uint32": {}}


def init_specs_dict():
global specs
for yaml_file in Path(files("tinymovr").joinpath("specs/")).glob("*.yaml"):
with open(str(yaml_file)) as def_raw:
spec = yaml.safe_load(def_raw)
add_spec(spec)


def add_spec(spec, logger=None):
if logger is None:
logger = logging.getLogger("tinymovr")

tmp_node = deserialize(spec)
hash_value = tmp_node.hash_uint32
if hash_value in specs["hash_uint32"]:
logger.warning("Provided spec with hash {} already exists in hash/name dictionary".format(hash_value))
else:
specs["hash_uint32"][hash_value] = spec


init_specs_dict()


def get_bus_config(suggested_types=None):
Expand All @@ -70,24 +71,22 @@ def create_device(node_id):
"""
chan = CANChannel(node_id)

# Temporarily using a default definition to get the protocol_hash
# This assumes that `protocol_hash` is standard across different definitions
# Get the first definition as a temp
tmp_definition = list(definitions["hash_uint32"].values())[0]
node = deserialize(tmp_definition)
# Temporarily using a default spec to get the protocol_hash
# This assumes that `protocol_hash` is standard across different specs
# Get the first spec as a temp
tmp_spec = list(specs["hash_uint32"].values())[0]
node = deserialize(tmp_spec)
node._channel = chan

# Check for the correct definition using the remote hash
# Check for the correct spec using the remote hash
protocol_hash = node.protocol_hash
device_definition = definitions["hash_uint32"].get(protocol_hash)
device_spec = specs["hash_uint32"].get(protocol_hash)

if not device_definition:
raise ValueError(f"No device definition found for hash {protocol_hash}.")
if not device_spec:
raise ValueError(f"No device spec found for hash {protocol_hash}.")

node = deserialize(device_definition)
node = deserialize(device_spec)
node._channel = chan
if node.hash_uint32 != protocol_hash:
raise ProtocolVersionError(node_id, "")

return node

Expand All @@ -101,17 +100,12 @@ def create_device_with_hash_msg(heartbeat_msg):
chan = CANChannel(node_id)

hash, *_ = chan.serializer.deserialize(heartbeat_msg.data[:4], DataType.UINT32)
device_definition = definitions["hash_uint32"].get(hash)
device_spec = specs["hash_uint32"].get(hash)

if not device_definition:
raise ValueError(f"No device definition found for hash {hash}.")
if not device_spec:
raise ValueError(f"No device spec found for hash {hash}.")

node = deserialize(device_definition)
if node.hash_uint32 != hash:
version_str = "".join([chr(n) for n in heartbeat_msg.data[4:]])
if not version_str.strip():
version_str = "1.3.1"
raise ProtocolVersionError(node_id, version_str)
node = deserialize(device_spec)

node._channel = chan
return node
Expand Down
4 changes: 2 additions & 2 deletions studio/Python/tinymovr/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from tinymovr.channel import ResponseError
from tinymovr.tee import get_tee
from tinymovr.constants import HEARTBEAT_BASE
from tinymovr.config import create_device_with_hash_msg, ProtocolVersionError
from tinymovr.config import create_device_with_hash_msg


class Discovery:
Expand Down Expand Up @@ -72,7 +72,7 @@ def _recv_cb(self, frame):
self._append_to_queue((node, node_id))
except ResponseError as e:
self.logger.error(e)
except ProtocolVersionError as e:
except ValueError as e:
self.logger.error(e)
self.incompatible_nodes.add(node_id)
self.pending_nodes.remove(node_id)
Expand Down
7 changes: 5 additions & 2 deletions studio/Python/tinymovr/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
display_file_open_dialog,
display_file_save_dialog,
magnitude_of,
hold_sema,
TimedGetter,
check_selected_items,
get_dynamic_attrs,
is_dark_mode
)
from tinymovr.gui.widgets import (
OurQTreeWidget,
NodeTreeWidgetItem,
AttrTreeWidgetItem,
FuncTreeWidgetItem,
OptionsTreeWidgetItem,
PlaceholderQTreeWidget,
IconComboBoxWidget,
ArgumentInputDialog
)
Expand Down
16 changes: 14 additions & 2 deletions studio/Python/tinymovr/gui/gui.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
"""Tinymovr Studio GUI
Usage:
tinymovr [--bus=<bus>] [--chan=<chan>] [--bitrate=<bitrate>] [--max-timeouts=<count>]
tinymovr [--bus=<bus>] [--chan=<chan>] [--spec=<spec>] [--bitrate=<bitrate>] [--max-timeouts=<count>]
tinymovr -h | --help
tinymovr --version
Options:
--bus=<bus> One or more interfaces to use, first available is used [default: canine,slcan_disco].
--chan=<chan> The bus device "channel".
--spec=<spec> A custom device spec to be added to the list of discoverable specs.
--bitrate=<bitrate> CAN bitrate [default: 1000000].
--max-timeouts=<count> Max timeouts before nodes are rescanned [default: 5].
"""

import sys
import yaml
import pkg_resources
from docopt import docopt
from PySide6.QtWidgets import QApplication
from tinymovr.gui import MainWindow, app_stylesheet, app_stylesheet_dark, is_dark_mode
from tinymovr.constants import app_name
from tinymovr.config import configure_logging, add_spec


"""
Expand All @@ -41,11 +44,20 @@
def spawn():
version = pkg_resources.require("tinymovr")[0].version
arguments = docopt(__doc__, version=app_name + " " + str(version))

logger = configure_logging()

spec_file = arguments["--spec"]
if spec_file:
with open(spec_file, 'r') as file:
spec_data = yaml.safe_load(file)
add_spec(spec_data, logger)

app = QApplication(sys.argv)
if is_dark_mode():
app.setStyleSheet(app_stylesheet_dark)
else:
app.setStyleSheet(app_stylesheet)
w = MainWindow(app, arguments)
w = MainWindow(app, arguments, logger)
w.show()
sys.exit(app.exec_())
Loading

0 comments on commit b05b8d2

Please sign in to comment.