mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-08-18 18:20:15 +08:00
Merge branch 'devel' into fw4
This commit is contained in:
+1
-1
@@ -48,7 +48,7 @@ env:
|
||||
|
||||
# Various protocol combinations
|
||||
- CONFIG_BOARD_VERSION=v3.4-24V CONFIG_USB_PROTOCOL=native-stream CONFIG_UART_PROTOCOL=native
|
||||
- CONFIG_BOARD_VERSION=v3.4-24V CONFIG_USB_PROTOCOL=stdout CONFIG_UART_PROTOCOL=ascii
|
||||
- CONFIG_BOARD_VERSION=v3.4-24V CONFIG_USB_PROTOCOL=stdout CONFIG_UART_PROTOCOL=stdout
|
||||
- CONFIG_BOARD_VERSION=v3.4-24V CONFIG_USB_PROTOCOL=none CONFIG_UART_PROTOCOL=none
|
||||
|
||||
script:
|
||||
|
||||
@@ -132,7 +132,7 @@ extern USBD_CDC_ItfTypeDef USBD_Interface_fops_FS;
|
||||
* @{
|
||||
*/
|
||||
|
||||
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len);
|
||||
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len, uint8_t endpoint_pair);
|
||||
|
||||
/* USER CODE BEGIN EXPORTED_FUNCTIONS */
|
||||
/* USER CODE END EXPORTED_FUNCTIONS */
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
* @brief Defines for configuration of the Usb device.
|
||||
* @{
|
||||
*/
|
||||
#define MS_VendorCode 'P'
|
||||
|
||||
/*---------- -----------*/
|
||||
#define USBD_MAX_NUM_INTERFACES 1
|
||||
@@ -97,7 +98,7 @@
|
||||
/*---------- -----------*/
|
||||
#define USBD_MAX_STR_DESC_SIZ 512
|
||||
/*---------- -----------*/
|
||||
#define USBD_SUPPORT_USER_STRING 0
|
||||
#define USBD_SUPPORT_USER_STRING 1
|
||||
/*---------- -----------*/
|
||||
#define USBD_DEBUG_LEVEL 0
|
||||
/*---------- -----------*/
|
||||
|
||||
@@ -133,6 +133,8 @@ extern USBD_DescriptorsTypeDef FS_Desc;
|
||||
|
||||
/* USER CODE BEGIN EXPORTED_FUNCTIONS */
|
||||
|
||||
uint8_t * USBD_UsrStrDescriptor(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length);
|
||||
|
||||
/* USER CODE END EXPORTED_FUNCTIONS */
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,13 +52,15 @@
|
||||
#define CDC_IN_EP 0x81 /* EP1 for data IN */
|
||||
#define CDC_OUT_EP 0x01 /* EP1 for data OUT */
|
||||
#define CDC_CMD_EP 0x82 /* EP2 for CDC commands */
|
||||
#define ODRIVE_IN_EP 0x83 /* EP3 IN: ODrive device TX endpoint */
|
||||
#define ODRIVE_OUT_EP 0x03 /* EP3 OUT: ODrive device RX endpoint */
|
||||
|
||||
/* CDC Endpoints parameters: you can fine tune these values depending on the needed baudrates and performance. */
|
||||
#define CDC_DATA_HS_MAX_PACKET_SIZE 512 /* Endpoint IN & OUT Packet size */
|
||||
#define CDC_DATA_HS_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */
|
||||
#define CDC_DATA_FS_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */
|
||||
#define CDC_CMD_PACKET_SIZE 8 /* Control Endpoint Packet size */
|
||||
|
||||
#define USB_CDC_CONFIG_DESC_SIZ 67
|
||||
#define USB_CDC_CONFIG_DESC_SIZ (67 + 39)
|
||||
#define CDC_DATA_HS_IN_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE
|
||||
#define CDC_DATA_HS_OUT_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE
|
||||
|
||||
@@ -103,7 +105,7 @@ typedef struct _USBD_CDC_Itf
|
||||
int8_t (* Init) (void);
|
||||
int8_t (* DeInit) (void);
|
||||
int8_t (* Control) (uint8_t, uint8_t * , uint16_t);
|
||||
int8_t (* Receive) (uint8_t *, uint32_t *);
|
||||
int8_t (* Receive) (uint8_t *, uint32_t *, uint8_t);
|
||||
|
||||
}USBD_CDC_ItfTypeDef;
|
||||
|
||||
@@ -156,9 +158,9 @@ uint8_t USBD_CDC_SetTxBuffer (USBD_HandleTypeDef *pdev,
|
||||
uint8_t USBD_CDC_SetRxBuffer (USBD_HandleTypeDef *pdev,
|
||||
uint8_t *pbuff);
|
||||
|
||||
uint8_t USBD_CDC_ReceivePacket (USBD_HandleTypeDef *pdev);
|
||||
uint8_t USBD_CDC_ReceivePacket (USBD_HandleTypeDef *pdev, uint8_t endpoint_pair);
|
||||
|
||||
uint8_t USBD_CDC_TransmitPacket (USBD_HandleTypeDef *pdev);
|
||||
uint8_t USBD_CDC_TransmitPacket (USBD_HandleTypeDef *pdev, uint8_t endpoint_pair);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
+282
-195
File diff suppressed because it is too large
Load Diff
@@ -68,7 +68,9 @@
|
||||
#define USBD_IDX_PRODUCT_STR 0x02
|
||||
#define USBD_IDX_SERIAL_STR 0x03
|
||||
#define USBD_IDX_CONFIG_STR 0x04
|
||||
#define USBD_IDX_INTERFACE_STR 0x05
|
||||
#define USBD_IDX_INTERFACE_STR 0x05
|
||||
#define USBD_IDX_ODRIVE_INTF_STR 0x06
|
||||
#define USBD_IDX_MICROSOFT_DESC_STR 0xEE
|
||||
|
||||
#define USB_REQ_TYPE_STANDARD 0x00
|
||||
#define USB_REQ_TYPE_CLASS 0x20
|
||||
|
||||
@@ -151,7 +151,7 @@ extern USBD_HandleTypeDef hUsbDeviceFS;
|
||||
static int8_t CDC_Init_FS(void);
|
||||
static int8_t CDC_DeInit_FS(void);
|
||||
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length);
|
||||
static int8_t CDC_Receive_FS(uint8_t* pbuf, uint32_t *Len);
|
||||
static int8_t CDC_Receive_FS(uint8_t* pbuf, uint32_t *Len, uint8_t endpoint_pair);
|
||||
|
||||
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
|
||||
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
|
||||
@@ -287,10 +287,10 @@ static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
|
||||
* @param Len: Number of data received (in bytes)
|
||||
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
|
||||
*/
|
||||
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
|
||||
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len, uint8_t endpoint_pair)
|
||||
{
|
||||
/* USER CODE BEGIN 6 */
|
||||
usb_process_packet(Buf, *Len);
|
||||
usb_process_packet(Buf, *Len, endpoint_pair);
|
||||
|
||||
return (USBD_OK);
|
||||
/* USER CODE END 6 */
|
||||
@@ -307,7 +307,7 @@ static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
|
||||
* @param Len: Number of data to be sent (in bytes)
|
||||
* @retval USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY
|
||||
*/
|
||||
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
|
||||
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len, uint8_t endpoint_pair)
|
||||
{
|
||||
uint8_t result = USBD_OK;
|
||||
/* USER CODE BEGIN 7 */
|
||||
@@ -323,7 +323,7 @@ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
|
||||
memcpy(UserTxBufferFS, Buf, Len);
|
||||
// Update Len
|
||||
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, Len);
|
||||
result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
|
||||
result = USBD_CDC_TransmitPacket(&hUsbDeviceFS, endpoint_pair);
|
||||
/* USER CODE END 7 */
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -154,6 +154,23 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle)
|
||||
*/
|
||||
void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
USBD_StatusTypeDef ret = USBD_OK;
|
||||
USBD_HandleTypeDef *pdev = hpcd->pData;
|
||||
USBD_SetupReqTypedef *req = &pdev->request;
|
||||
USBD_ParseSetupRequest(req, (uint8_t *)hpcd->Setup);
|
||||
if ( ( USB_REQ_TYPE_VENDOR == (req->bmRequest & USB_REQ_TYPE_MASK) ) && ( MS_VendorCode == req->bRequest ) )
|
||||
{
|
||||
pdev->ep0_state = USBD_EP0_SETUP;
|
||||
pdev->ep0_data_len = pdev->request.wLength;
|
||||
|
||||
ret = pdev->pClass->Setup(pdev, req);
|
||||
|
||||
if( (req->wLength == 0) && (ret == USBD_OK) )
|
||||
{
|
||||
USBD_CtlSendStatus(pdev);
|
||||
}
|
||||
return;
|
||||
}
|
||||
USBD_LL_SetupStage((USBD_HandleTypeDef*)hpcd->pData, (uint8_t *)hpcd->Setup);
|
||||
}
|
||||
|
||||
@@ -312,7 +329,7 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
|
||||
pdev->pData = &hpcd_USB_OTG_FS;
|
||||
|
||||
hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
|
||||
hpcd_USB_OTG_FS.Init.dev_endpoints = 4;
|
||||
hpcd_USB_OTG_FS.Init.dev_endpoints = 6;
|
||||
hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL;
|
||||
hpcd_USB_OTG_FS.Init.dma_enable = DISABLE;
|
||||
hpcd_USB_OTG_FS.Init.ep0_mps = DEP0CTL_MPS_64;
|
||||
@@ -329,7 +346,8 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
|
||||
|
||||
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80);
|
||||
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
|
||||
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80);
|
||||
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x40); // CDC IN endpoint
|
||||
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x40); // ODrive IN endpoint
|
||||
}
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,8 @@
|
||||
#define USBD_PID_FS 0x0D32
|
||||
#define USBD_PRODUCT_XSTR(s) USBD_PRODUCT_STR(s)
|
||||
#define USBD_PRODUCT_STR(s) #s
|
||||
#define USBD_PRODUCT_STRING_FS ODrive version HW_VERSION_MAJOR.HW_VERSION_MINOR
|
||||
#define USBD_PRODUCT_STRING_FS ODrive HW_VERSION_MAJOR.HW_VERSION_MINOR CDC Interface
|
||||
#define NATIVE_STRING ODrive HW_VERSION_MAJOR.HW_VERSION_MINOR Native Interface
|
||||
#define USBD_SERIALNUMBER_STRING_FS "000000000001"
|
||||
#define USBD_CONFIGURATION_STRING_FS "CDC Config"
|
||||
#define USBD_INTERFACE_STRING_FS "CDC Interface"
|
||||
@@ -114,6 +115,50 @@
|
||||
|
||||
/* USER CODE BEGIN 0 */
|
||||
|
||||
// MS OS String descriptor to tell Windows that it may query for other descriptors
|
||||
// It's a standard string descriptor.
|
||||
// Windows will only query for OS descriptors once!
|
||||
// Delete the information about already queried devices in registry by deleting:
|
||||
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\VVVVPPPPRRRR
|
||||
__ALIGN_BEGIN uint8_t USBD_MS_OS_StringDescriptor[] __ALIGN_END =
|
||||
{
|
||||
0x12, // bLength 1 0x12 Length of the descriptor
|
||||
0x03, // bDescriptorType 1 0x03 Descriptor type
|
||||
// qwSignature 14 ‘MSFT100’ Signature field
|
||||
0x4D, 0x00, // 'M'
|
||||
0x53, 0x00, // 'S'
|
||||
0x46, 0x00, // 'F'
|
||||
0x54, 0x00, // 'T'
|
||||
0x31, 0x00, // '1'
|
||||
0x30, 0x00, // '0'
|
||||
0x30, 0x00, // '0'
|
||||
MS_VendorCode, // bMS_VendorCode 1 Vendor-specific Vendor code
|
||||
0x00 // bPad 1 0x00 Pad field
|
||||
};
|
||||
|
||||
// redefined further down
|
||||
__ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END;
|
||||
/**
|
||||
* @brief UsrStrDescriptor
|
||||
* return non standard string descriptor
|
||||
* @param pdev: device instance
|
||||
* @param index : descriptor index (0xEE for MS OS String Descriptor)
|
||||
* @param length : pointer data length
|
||||
* @retval pointer to descriptor buffer
|
||||
*/
|
||||
uint8_t * USBD_UsrStrDescriptor(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length)
|
||||
{
|
||||
*length = 0;
|
||||
if (USBD_IDX_MICROSOFT_DESC_STR == index) {
|
||||
*length = sizeof (USBD_MS_OS_StringDescriptor);
|
||||
return USBD_MS_OS_StringDescriptor;
|
||||
} else if (USBD_IDX_ODRIVE_INTF_STR == index) {
|
||||
USBD_GetString((uint8_t *)USBD_PRODUCT_XSTR(NATIVE_STRING), USBD_StrDesc, length);
|
||||
return USBD_StrDesc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* USER CODE END 0 */
|
||||
|
||||
/** @defgroup USBD_DESC_Private_Macros USBD_DESC_Private_Macros
|
||||
@@ -189,16 +234,17 @@ __ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
|
||||
0x00, /*bcdUSB */
|
||||
#endif /* (USBD_LPM_ENABLED == 1) */
|
||||
0x02,
|
||||
0x02, /*bDeviceClass*/
|
||||
// Notify OS that this is a composite device
|
||||
0xEF, /*bDeviceClass*/
|
||||
0x02, /*bDeviceSubClass*/
|
||||
0x00, /*bDeviceProtocol*/
|
||||
0x01, /*bDeviceProtocol*/
|
||||
USB_MAX_EP0_SIZE, /*bMaxPacketSize*/
|
||||
LOBYTE(USBD_VID), /*idVendor*/
|
||||
HIBYTE(USBD_VID), /*idVendor*/
|
||||
LOBYTE(USBD_PID_FS), /*idProduct*/
|
||||
HIBYTE(USBD_PID_FS), /*idProduct*/
|
||||
0x00, /*bcdDevice rel. 2.00*/
|
||||
0x02,
|
||||
0x03, /* bNumInterfaces */
|
||||
USBD_IDX_MFC_STR, /*Index of manufacturer string*/
|
||||
USBD_IDX_PRODUCT_STR, /*Index of product string*/
|
||||
USBD_IDX_SERIAL_STR, /*Index of serial number string*/
|
||||
|
||||
@@ -11,7 +11,10 @@ Please add a note of your changes below this heading if you make a Pull Request.
|
||||
* System stats (e.g. stack usage) are exposed under `<odrv>.system_stats`
|
||||
|
||||
### Changed
|
||||
* The DFU script now verifies the flash after writing
|
||||
* DFU script updates
|
||||
* Verify the flash after writing
|
||||
* Automatically download firmware from GitHub releases if no file is provided
|
||||
* Retain configuration during firmware updates
|
||||
* Refactor python tools
|
||||
* The scripts `explore_odrive.py`, `liveplotter.py`, `drv_status.py` and `rate_test.py` have been merged into one single `odrivetool` script. Running this script without any arguments provides the shell that `explore_odrive.py` used to provide.
|
||||
* The command line options of `odrivetool` have changed compared to the original `explore_odrive.py`. See `odrivetool --help` for more details.
|
||||
@@ -19,11 +22,15 @@ Please add a note of your changes below this heading if you make a Pull Request.
|
||||
* No need to restart the `odrivetool` shell when devices get disconnected and reconnected
|
||||
* ODrive accesses from within python tools are now thread-safe. That means you can read from the same remote property from multiple threads concurrently.
|
||||
* The liveplotter (`odrivetool liveplotter`, formerly `liveplotter.py`) does no longer steal focus and closes as expected
|
||||
* Add commands `odrivetool backup-config` and `odrivetool restore-config`
|
||||
* (experimental: start liveplotter from `odrivetool` shell by typing `start_liveplotter(lambda: odrv0.motor0.encoder.encoder_state)`)
|
||||
* `make write_otp` command to burn the board version onto the ODrive's one-time programmable memory. If you have an ODrive v3.4 or older, you can run this once for a better firmware update user experience in the future. Run the command without any options for more details. Once set, the board version is exposed through the `hw_version_[...]` properties.
|
||||
* bake Git-derived firmware version into firmware binary. The firmware version is exposed through the `fw_version_[...]` properties.
|
||||
* Set thread priority of USB pump thread above protocol thread
|
||||
* GPIO3 not sensitive to edges by default
|
||||
* The device now appears as a composite device on USB. One subdevice is still a CDC device (virtual COM port), the other subdevice is a vendor specific class. This should resolve several issues that were caused by conflicting kernel drivers or OS services.
|
||||
* Add WinUSB descriptors. This will tell Windows >= 8 to automatically load winusb.sys for the ODrive (only for the vendor specific subdevice). This makes it possible to use the ODrive from userspace via WinUSB with zero configuration. The Python tool currently still uses libusb so Zadig is still required.
|
||||
* Add a configuration to enable the ASCII protocol on USB at runtime. This will only enable the ASCII protocol on the USB CDC subdevice, not the vendor specific subdevice so the python tools will still be able to talk to the ODrive.
|
||||
|
||||
### Fixed
|
||||
* Enums now transported with correct underlying type on native protocol
|
||||
|
||||
@@ -34,6 +34,8 @@ void save_configuration(void) {
|
||||
&motor_configs,
|
||||
&axis_configs)) {
|
||||
//printf("saving configuration failed\r\n"); osDelay(5);
|
||||
} else {
|
||||
user_config_loaded_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,9 @@ extern SystemStats_t system_stats_;
|
||||
|
||||
// @brief general user configurable board configuration
|
||||
struct BoardConfig_t {
|
||||
bool enable_uart = false;
|
||||
bool enable_uart = true;
|
||||
bool enable_i2c_instead_of_can = false;
|
||||
bool enable_ascii_protocol_on_usb = true;
|
||||
float brake_resistance = 0.47f; // [ohm]
|
||||
float dc_bus_undervoltage_trip_level = 8.0f; //<! [V] minimum voltage below which the motor stops operating
|
||||
float dc_bus_overvoltage_trip_level = 1.08f * HW_VERSION_VOLTAGE; //<! [V] maximum voltage above which the motor stops operating.
|
||||
|
||||
@@ -43,8 +43,6 @@ if tup.getconfig("USB_PROTOCOL") == "native" or tup.getconfig("USB_PROTOCOL") ==
|
||||
FLAGS += "-DUSB_PROTOCOL_NATIVE"
|
||||
elseif tup.getconfig("USB_PROTOCOL") == "native-stream" then
|
||||
FLAGS += "-DUSB_PROTOCOL_NATIVE_STREAM_BASED"
|
||||
elseif tup.getconfig("USB_PROTOCOL") == "ascii" then
|
||||
FLAGS += "-DUSB_PROTOCOL_ASCII"
|
||||
elseif tup.getconfig("USB_PROTOCOL") == "stdout" then
|
||||
FLAGS += "-DUSB_PROTOCOL_STDOUT"
|
||||
elseif tup.getconfig("USB_PROTOCOL") == "none" then
|
||||
|
||||
@@ -139,6 +139,7 @@ static inline auto make_obj_tree() {
|
||||
// TODO: changing this currently requires a reboot - fix this
|
||||
make_protocol_property("enable_uart", &board_config.enable_uart),
|
||||
make_protocol_property("enable_i2c_instead_of_can" , &board_config.enable_i2c_instead_of_can), // requires a reboot
|
||||
make_protocol_property("enable_ascii_protocol_on_usb", &board_config.enable_ascii_protocol_on_usb),
|
||||
make_protocol_property("dc_bus_undervoltage_trip_level", &board_config.dc_bus_undervoltage_trip_level),
|
||||
make_protocol_property("dc_bus_overvoltage_trip_level", &board_config.dc_bus_overvoltage_trip_level)
|
||||
),
|
||||
@@ -179,10 +180,10 @@ void communication_task(void * ctx) {
|
||||
auto endpoint_provider = EndpointProvider_from_MemberList<tree_type>(*tree_ptr);
|
||||
set_application_endpoints(&endpoint_provider);
|
||||
|
||||
serve_on_uart();
|
||||
serve_on_usb();
|
||||
start_uart_server();
|
||||
start_usb_server();
|
||||
if (board_config.enable_i2c_instead_of_can) {
|
||||
serve_on_i2c();
|
||||
start_i2c_server();
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
} i2c1_packet_output;
|
||||
BidirectionalPacketBasedChannel i2c1_channel(i2c1_packet_output);
|
||||
|
||||
void serve_on_i2c() {
|
||||
void start_i2c_server() {
|
||||
// CAN H = SDA
|
||||
// CAN L = SCL
|
||||
HAL_I2C_EnableListen_IT(&hi2c1);
|
||||
|
||||
@@ -16,7 +16,7 @@ struct I2CStats_t {
|
||||
|
||||
extern I2CStats_t i2c_stats_;
|
||||
|
||||
void serve_on_i2c(void);
|
||||
void start_i2c_server(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ static void uart_server_thread(void * ctx) {
|
||||
};
|
||||
}
|
||||
|
||||
void serve_on_uart() {
|
||||
void start_uart_server() {
|
||||
// DMA is set up to recieve in a circular buffer forever.
|
||||
// We dont use interrupts to fetch the data, instead we periodically read
|
||||
// data out of the circular buffer into a parse buffer, controlled by a state machine
|
||||
|
||||
@@ -12,7 +12,7 @@ extern "C" {
|
||||
|
||||
extern osThreadId uart_thread;
|
||||
|
||||
void serve_on_uart(void);
|
||||
void start_uart_server(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -12,8 +12,12 @@
|
||||
#include <cmsis_os.h>
|
||||
#include <freertos_vars.h>
|
||||
|
||||
#include <odrive_main.h>
|
||||
#include "ascii_protocol.h"
|
||||
|
||||
static uint8_t* usb_buf;
|
||||
static uint32_t usb_len;
|
||||
static uint8_t active_endpoint_pair;
|
||||
|
||||
// FIXME: the stdlib doesn't know about CMSIS threads, so this is just a global variable
|
||||
static thread_local uint32_t deadline_ms = 0;
|
||||
@@ -39,12 +43,12 @@ public:
|
||||
// transmit packet
|
||||
uint8_t status = CDC_Transmit_FS(
|
||||
const_cast<uint8_t*>(buffer) /* casting this const away is safe because...
|
||||
well... it's not actually. Stupid STM. */, length);
|
||||
well... it's not actually. Stupid STM. */, length, active_endpoint_pair);
|
||||
if (status != USBD_OK) {
|
||||
osSemaphoreRelease(sem_usb_tx);
|
||||
return -1;
|
||||
}
|
||||
usb_stats_.tx_cnt = 0;
|
||||
usb_stats_.tx_cnt++;
|
||||
return 0;
|
||||
}
|
||||
} usb_packet_output;
|
||||
@@ -90,27 +94,30 @@ static void usb_server_thread(void * ctx) {
|
||||
if (sem_stat == osOK) {
|
||||
usb_stats_.rx_cnt++;
|
||||
deadline_ms = timeout_to_deadline(PROTOCOL_SERVER_TIMEOUT_MS);
|
||||
if (active_endpoint_pair == CDC_OUT_EP && board_config.enable_ascii_protocol_on_usb) {
|
||||
ASCII_protocol_parse_stream(usb_buf, usb_len, usb_stream_output);
|
||||
} else {
|
||||
#if defined(USB_PROTOCOL_NATIVE)
|
||||
usb_channel.process_packet(usb_buf, usb_len);
|
||||
usb_channel.process_packet(usb_buf, usb_len);
|
||||
#elif defined(USB_PROTOCOL_NATIVE_STREAM_BASED)
|
||||
usb_native_stream_input.process_bytes(usb_buf, usb_len);
|
||||
#elif defined(USB_PROTOCOL_ASCII)
|
||||
ASCII_protocol_parse_stream(usb_buf, usb_len, usb_stream_output);
|
||||
usb_native_stream_input.process_bytes(usb_buf, usb_len);
|
||||
#endif
|
||||
USBD_CDC_ReceivePacket(&hUsbDeviceFS); // Allow next packet
|
||||
}
|
||||
USBD_CDC_ReceivePacket(&hUsbDeviceFS, active_endpoint_pair); // Allow next packet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called from CDC_Receive_FS callback function, this allows the communication
|
||||
// thread to handle the incoming data
|
||||
void usb_process_packet(uint8_t *buf, uint32_t len) {
|
||||
void usb_process_packet(uint8_t *buf, uint32_t len, uint8_t endpoint_pair) {
|
||||
usb_buf = buf;
|
||||
usb_len = len;
|
||||
active_endpoint_pair = endpoint_pair;
|
||||
osSemaphoreRelease(sem_usb_rx);
|
||||
}
|
||||
|
||||
void serve_on_usb() {
|
||||
void start_usb_server() {
|
||||
// Start USB communication thread
|
||||
osThreadDef(usb_server_thread_def, usb_server_thread, osPriorityNormal, 0, 512);
|
||||
usb_thread = osThreadCreate(osThread(usb_server_thread_def), NULL);
|
||||
|
||||
@@ -21,8 +21,8 @@ typedef struct {
|
||||
|
||||
extern USBStats_t usb_stats_;
|
||||
|
||||
void usb_process_packet(uint8_t *buf, uint32_t len);
|
||||
void serve_on_usb(void);
|
||||
void usb_process_packet(uint8_t *buf, uint32_t len, uint8_t endpoint_pair);
|
||||
void start_usb_server(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import odrive.remote_object
|
||||
from odrive.utils import OperationAbortedException
|
||||
|
||||
def get_dict(obj, is_config_object):
|
||||
result = {}
|
||||
for (k,v) in obj._remote_attributes.items():
|
||||
if isinstance(v, odrive.remote_object.RemoteProperty) and is_config_object:
|
||||
result[k] = v.get_value()
|
||||
elif isinstance(v, odrive.remote_object.RemoteObject):
|
||||
sub_dict = get_dict(v, k == 'config')
|
||||
if sub_dict != {}:
|
||||
result[k] = sub_dict
|
||||
return result
|
||||
|
||||
def set_dict(obj, path, config_dict):
|
||||
errors = []
|
||||
for (k,v) in config_dict.items():
|
||||
name = path + ("." if path != "" else "") + k
|
||||
if not k in obj._remote_attributes:
|
||||
errors.append("Could not restore {}: property not found on device".format(name))
|
||||
continue
|
||||
remote_attribute = obj._remote_attributes[k]
|
||||
if isinstance(remote_attribute, odrive.remote_object.RemoteObject):
|
||||
errors += set_dict(remote_attribute, name, v)
|
||||
else:
|
||||
try:
|
||||
remote_attribute.set_value(v)
|
||||
except Exception as ex:
|
||||
errors.append("Could not restore {}: {}".format(name, str(ex)))
|
||||
return errors
|
||||
|
||||
def get_temp_config_filename(device):
|
||||
serial_number = odrive.utils.get_serial_number_str(device)
|
||||
safe_serial_number = ''.join(filter(str.isalnum, serial_number))
|
||||
return os.path.join(tempfile.gettempdir(), 'odrive-config-{}.json'.format(safe_serial_number))
|
||||
|
||||
def backup_config(device, filename, logger):
|
||||
"""
|
||||
Exports the configuration of an ODrive to a JSON file.
|
||||
If no file name is provided, the file is placed into a
|
||||
temporary directory.
|
||||
"""
|
||||
|
||||
if filename is None:
|
||||
filename = get_temp_config_filename(device)
|
||||
|
||||
logger.info("Saving configuration to {}...".format(filename))
|
||||
|
||||
if os.path.exists(filename):
|
||||
if not odrive.utils.yes_no_prompt("The file {} already exists. Do you want to override it?".format(filename), True):
|
||||
raise OperationAbortedException()
|
||||
|
||||
data = get_dict(device, False)
|
||||
with open(filename, 'w') as file:
|
||||
json.dump(data, file)
|
||||
logger.info("Configuration saved.")
|
||||
|
||||
def restore_config(device, filename, logger):
|
||||
"""
|
||||
Restores the configuration stored in a file
|
||||
"""
|
||||
|
||||
if filename is None:
|
||||
filename = get_temp_config_filename(device)
|
||||
|
||||
with open(filename) as file:
|
||||
data = json.load(file)
|
||||
|
||||
logger.info("Restoring configuration from {}...".format(filename))
|
||||
errors = odrive.configuration.set_dict(device, "", data)
|
||||
|
||||
for error in errors:
|
||||
logger.info(error)
|
||||
if errors:
|
||||
logger.warn("Some of the configuration could not be restored.")
|
||||
|
||||
device.save_configuration()
|
||||
logger.info("Configuration restored.")
|
||||
+292
-196
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,8 @@
|
||||
import usb.util
|
||||
import time
|
||||
import fractions
|
||||
import array
|
||||
from odrive.dfuse.DfuState import DfuState
|
||||
|
||||
DFU_REQUEST_SEND = 0x21
|
||||
DFU_REQUEST_RECEIVE = 0xa1
|
||||
@@ -12,6 +15,9 @@ DFU_CLRSTATUS = 0x04
|
||||
DFU_GETSTATE = 0x05
|
||||
DFU_ABORT = 0x06
|
||||
|
||||
SIZE_MULTIPLIERS = {' ': 1, 'K': 1024, 'M' : 1024*1024}
|
||||
MAX_TRANSFER_SIZE = 2048
|
||||
|
||||
# Order is LSB first
|
||||
def address_to_4bytes(a):
|
||||
return [ a % 256, (a >> 8)%256, (a >> 16)%256, (a >> 24)%256 ]
|
||||
@@ -24,6 +30,7 @@ class DfuDevice:
|
||||
self.intf = None
|
||||
#self.dev.reset()
|
||||
self.cfg.set()
|
||||
self.sectors = list(self.get_device_sectors())
|
||||
|
||||
def alternates(self):
|
||||
return [(usb.util.get_string(self.dev, intf.iInterface), intf) for intf in self.cfg]
|
||||
@@ -98,3 +105,115 @@ class DfuDevice:
|
||||
|
||||
return status
|
||||
|
||||
## High level functions ##
|
||||
# by ODrive Robotics
|
||||
|
||||
def get_device_sectors(self):
|
||||
"""
|
||||
Returns a list of all sectors on the device.
|
||||
Each sector is represented as a dictionary with the following keys:
|
||||
- name: name of the associated memory region (e.g. "Internal Flash")
|
||||
- alt: USB alternate setting associated with this memory region
|
||||
- addr: Start address of the sector (e.g. 0x08004000 for the second flash sectors)
|
||||
- baseaddr: Start address of the memory region associated with the sector
|
||||
(e.g. 0x08000000 for all flash sectors)
|
||||
- len: Number of bytes in the sector
|
||||
"""
|
||||
for name, alt in self.alternates():
|
||||
# example for name:
|
||||
# '@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg'
|
||||
label, baseaddr, layout = name.split('/')
|
||||
baseaddr = int(baseaddr, 0) # convert hex to decimal
|
||||
addr = baseaddr
|
||||
|
||||
for sector in layout.split(','):
|
||||
repeat, size = map(int, sector[:-2].split('*'))
|
||||
size *= SIZE_MULTIPLIERS[sector[-2].upper()]
|
||||
mode = sector[-1]
|
||||
|
||||
while repeat > 0:
|
||||
# TODO: verify if the section is writable
|
||||
yield {
|
||||
'name': label.strip().strip('@'),
|
||||
'alt': alt,
|
||||
'baseaddr': baseaddr,
|
||||
'addr': addr,
|
||||
'len': size,
|
||||
'mode': mode
|
||||
}
|
||||
|
||||
addr += size
|
||||
repeat -= 1
|
||||
|
||||
def set_alternate_safe(self, alt):
|
||||
self.set_alternate(alt)
|
||||
if self.get_state() == DfuState.DFU_ERROR:
|
||||
self.clear_status()
|
||||
self.wait_while_state(DfuState.DFU_ERROR)
|
||||
|
||||
#def clear_error(self)
|
||||
def set_address_safe(self, addr):
|
||||
self.set_address(addr)
|
||||
status = self.wait_while_state(DfuState.DFU_DOWNLOAD_BUSY)
|
||||
if status[1] != DfuState.DFU_DOWNLOAD_IDLE:
|
||||
raise RuntimeError("An error occured. Device Status: %r" % status)
|
||||
# take device out of DFU_DOWNLOAD_SYNC and into DFU_IDLE
|
||||
self.abort()
|
||||
status = self.wait_while_state(DfuState.DFU_DOWNLOAD_SYNC)
|
||||
if status[1] != DfuState.DFU_IDLE:
|
||||
raise RuntimeError("An error occured. Device Status: %r" % status)
|
||||
|
||||
|
||||
def erase_sector(self, sector):
|
||||
self.set_alternate_safe(sector['alt'])
|
||||
self.erase(sector['addr'])
|
||||
status = self.wait_while_state(DfuState.DFU_DOWNLOAD_BUSY, timeout=sector['len']/32)
|
||||
if status[1] != DfuState.DFU_DOWNLOAD_IDLE:
|
||||
raise RuntimeError("An error occured. Device Status: %r" % status)
|
||||
|
||||
def write_sector(self, sector, data):
|
||||
self.set_alternate_safe(sector['alt'])
|
||||
self.set_address_safe(sector['addr'])
|
||||
|
||||
transfer_size = fractions.gcd(sector['len'], MAX_TRANSFER_SIZE)
|
||||
|
||||
blocks = [data[i:i + transfer_size] for i in range(0, len(data), transfer_size)]
|
||||
for blocknum, block in enumerate(blocks):
|
||||
#print('write to {:08X} ({} bytes)'.format(
|
||||
# sector['addr'] + blocknum * TRANSFER_SIZE, len(block)))
|
||||
self.write(blocknum, block)
|
||||
status = self.wait_while_state(DfuState.DFU_DOWNLOAD_BUSY)
|
||||
if status[1] != DfuState.DFU_DOWNLOAD_IDLE:
|
||||
raise RuntimeError("An error occured. Device Status: %r" % status)
|
||||
|
||||
def read_sector(self, sector):
|
||||
"""
|
||||
Reads data from the specified sector
|
||||
Returns: a byte array containing the data
|
||||
"""
|
||||
self.set_alternate_safe(sector['alt'])
|
||||
self.set_address_safe(sector['addr'])
|
||||
|
||||
transfer_size = fractions.gcd(sector['len'], MAX_TRANSFER_SIZE)
|
||||
#blocknum_offset = int((sector['addr'] - sector['baseaddr']) / transfer_size)
|
||||
|
||||
|
||||
data = array.array(u'B')
|
||||
for blocknum in range(int(sector['len'] / transfer_size)):
|
||||
#print('read at {:08X}'.format(sector['addr'] + blocknum * TRANSFER_SIZE))
|
||||
deviceBlock = self.read(blocknum, transfer_size)
|
||||
data.extend(deviceBlock)
|
||||
self.abort() # take device into DFU_IDLE
|
||||
return data
|
||||
|
||||
def jump_to_application(self, address):
|
||||
self.set_address_safe(address)
|
||||
#self.set_address(address)
|
||||
#status = self.wait_while_state(DfuState.DFU_DOWNLOAD_BUSY)
|
||||
#if status[1] != DfuState.DFU_DOWNLOAD_IDLE:
|
||||
# raise RuntimeError("An error occured. Device Status: {}".format(status[1]))
|
||||
|
||||
self.leave()
|
||||
status = self.wait_while_state(DfuState.DFU_MANIFEST_SYNC)
|
||||
if status[1] != DfuState.DFU_MANIFEST:
|
||||
raise RuntimeError("An error occured. Device Status: {}".format(status[1]))
|
||||
|
||||
@@ -66,7 +66,7 @@ def find_all(path, serial_number,
|
||||
obj.__dict__['_json_data'] = json_data['members']
|
||||
obj.__dict__['_json_crc'] = json_crc16
|
||||
|
||||
device_serial_number = format(obj.serial_number, 'x').upper() if hasattr(obj, 'serial_number') else "[unknown serial number]"
|
||||
device_serial_number = odrive.utils.get_serial_number_str(obj)
|
||||
if serial_number != None and device_serial_number != serial_number:
|
||||
printer("Ignoring device with serial number {}".format(device_serial_number))
|
||||
return
|
||||
|
||||
@@ -27,7 +27,7 @@ MOTOR_TYPE_HIGH_CURRENT = 0
|
||||
#MOTOR_TYPE_LOW_CURRENT = 1
|
||||
MOTOR_TYPE_GIMBAL = 2
|
||||
|
||||
CTRL_MODE_VOLTAGE_CONTROL = 0,
|
||||
CTRL_MODE_CURRENT_CONTROL = 1,
|
||||
CTRL_MODE_VELOCITY_CONTROL = 2,
|
||||
CTRL_MODE_VOLTAGE_CONTROL = 0
|
||||
CTRL_MODE_CURRENT_CONTROL = 1
|
||||
CTRL_MODE_VELOCITY_CONTROL = 2
|
||||
CTRL_MODE_POSITION_CONTROL = 3
|
||||
|
||||
@@ -52,7 +52,7 @@ def did_discover_device(odrive, logger, app_shutdown_token):
|
||||
# Publish new ODrive to interactive console
|
||||
interactive_variables[interactive_name] = odrive
|
||||
globals()[interactive_name] = odrive # Add to globals so tab complete works
|
||||
logger.info("{} to ODrive {:012X} as {}".format(verb, serial_number, interactive_name))
|
||||
logger.notify("{} to ODrive {:012X} as {}".format(verb, serial_number, interactive_name))
|
||||
|
||||
# Subscribe to disappearance of the device
|
||||
odrive.__channel__._channel_broken.subscribe(lambda: did_lose_device(interactive_name, logger, app_shutdown_token))
|
||||
|
||||
@@ -46,20 +46,30 @@ class USBBulkTransport(odrive.protocol.PacketSource, odrive.protocol.PacketSink)
|
||||
if platform.system() != 'Windows':
|
||||
self.dev.reset()
|
||||
|
||||
interface_number = 1
|
||||
#self.dev.set_configuration() # no args: set first configuration
|
||||
|
||||
# Find the best interface
|
||||
self.cfg = self.dev.get_active_configuration()
|
||||
custom_interfaces = [i for i in self.cfg.interfaces() if i.bInterfaceClass == 0x00 and i.bInterfaceSubClass == 0x01]
|
||||
cdc_interfaces = [i for i in self.cfg.interfaces() if i.bInterfaceClass == 0x0a and i.bInterfaceSubClass == 0x00]
|
||||
all_compatible_interfaces = custom_interfaces + cdc_interfaces
|
||||
if len(all_compatible_interfaces) == 0:
|
||||
raise Exception("the device has no compatible interfaces")
|
||||
self.intf = all_compatible_interfaces[0]
|
||||
|
||||
# Try to detach kernel driver from interface
|
||||
#interface_number = 1
|
||||
try:
|
||||
if self.dev.is_kernel_driver_active(interface_number):
|
||||
self.dev.detach_kernel_driver(interface_number)
|
||||
if self.dev.is_kernel_driver_active(self.intf.bInterfaceNumber):
|
||||
self.dev.detach_kernel_driver(self.intf.bInterfaceNumber)
|
||||
self._printer("Detached Kernel Driver")
|
||||
else:
|
||||
self._printer("Kernel Driver was not attached")
|
||||
except NotImplementedError:
|
||||
pass #is_kernel_driver_active not implemented on Windows
|
||||
|
||||
self.dev.set_configuration() # no args: set first configuration
|
||||
self.cfg = self.dev.get_active_configuration()
|
||||
self.intf = self.cfg[(1,0)] # this implicitly claims the interface
|
||||
# write endpoint
|
||||
# find write endpoint (first OUT endpoint)
|
||||
self.epw = usb.util.find_descriptor(self.intf,
|
||||
# match the first OUT endpoint
|
||||
custom_match = \
|
||||
lambda e: \
|
||||
usb.util.endpoint_direction(e.bEndpointAddress) == \
|
||||
@@ -67,9 +77,8 @@ class USBBulkTransport(odrive.protocol.PacketSource, odrive.protocol.PacketSink)
|
||||
)
|
||||
assert self.epw is not None
|
||||
self._printer("EndpointAddress for writing {}".format(self.epw.bEndpointAddress))
|
||||
# read endpoint
|
||||
# find read endpoint (first IN endpoint)
|
||||
self.epr = usb.util.find_descriptor(self.intf,
|
||||
# match the first IN endpoint
|
||||
custom_match = \
|
||||
lambda e: \
|
||||
usb.util.endpoint_direction(e.bEndpointAddress) == \
|
||||
@@ -154,15 +163,18 @@ def discover_channels(path, serial_number, callback, cancellation_token, channel
|
||||
known_devices = []
|
||||
def device_matcher(device):
|
||||
#print(" test {:04X}:{:04X}".format(device.idVendor, device.idProduct))
|
||||
if (device.bus, device.address) in known_devices:
|
||||
return False
|
||||
if bus != None and device.bus != bus:
|
||||
return False
|
||||
if address != None and device.address != address:
|
||||
return False
|
||||
if serial_number != None and device.serial_number != serial_number:
|
||||
return False
|
||||
if (device.idVendor, device.idProduct) not in ODRIVE_VID_PID_PAIRS:
|
||||
try:
|
||||
if (device.bus, device.address) in known_devices:
|
||||
return False
|
||||
if bus != None and device.bus != bus:
|
||||
return False
|
||||
if address != None and device.address != address:
|
||||
return False
|
||||
if serial_number != None and device.serial_number != serial_number:
|
||||
return False
|
||||
if (device.idVendor, device.idProduct) not in ODRIVE_VID_PID_PAIRS:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ data_rate = 100
|
||||
plot_rate = 10
|
||||
num_samples = 1000
|
||||
|
||||
class OperationAbortedException(Exception):
|
||||
pass
|
||||
|
||||
def start_liveplotter(get_var_callback):
|
||||
"""
|
||||
Starts a liveplotter.
|
||||
@@ -157,6 +160,11 @@ def setup_udev_rules(logger):
|
||||
subprocess.run(["udevadm", "trigger"], check=True)
|
||||
logger.info('udev rules configured successfully')
|
||||
|
||||
def get_serial_number_str(device):
|
||||
if hasattr(device, 'serial_number'):
|
||||
return format(device.serial_number, 'x').upper()
|
||||
else:
|
||||
return "[unknown serial number]"
|
||||
|
||||
## Exceptions ##
|
||||
|
||||
@@ -357,9 +365,30 @@ class Logger():
|
||||
def success(self, text):
|
||||
self.print_colored(self._prefix + text, Logger.COLOR_GREEN)
|
||||
def info(self, text):
|
||||
self.print_colored(self._prefix + text, Logger.COLOR_DEFAULT)
|
||||
def notify(self, text):
|
||||
self.print_colored(self._prefix + text, Logger.COLOR_CYAN)
|
||||
def warn(self, text):
|
||||
self.print_colored(self._prefix + text, Logger.COLOR_YELLOW)
|
||||
def error(self, text):
|
||||
# TODO: write to stderr
|
||||
self.print_colored(self._prefix + text, Logger.COLOR_RED)
|
||||
|
||||
def yes_no_prompt(question, default=None):
|
||||
if default is None:
|
||||
question += " [y/n] "
|
||||
elif default == True:
|
||||
question += " [Y/n] "
|
||||
elif default == False:
|
||||
question += " [y/N] "
|
||||
|
||||
while True:
|
||||
print(question, end='')
|
||||
|
||||
choice = input().lower()
|
||||
if choice in {'yes', 'y'}:
|
||||
return True
|
||||
elif choice in {'no', 'n'}:
|
||||
return False
|
||||
elif choice == '' and default is not None:
|
||||
return default
|
||||
|
||||
+18
-8
@@ -4,6 +4,20 @@ import subprocess
|
||||
import os
|
||||
import sys
|
||||
|
||||
def version_str_to_tuple(version_string):
|
||||
"""
|
||||
Converts a version string to a tuple of the form
|
||||
(major, minor, revision, prerelease)
|
||||
|
||||
Example: "fw-v0.3.6-23" => (0, 3, 6, True)
|
||||
"""
|
||||
regex=r'.*v([0-9a-zA-Z]+).([0-9a-zA-Z]+).([0-9a-zA-Z]+)(.*)'
|
||||
return (int(re.sub(regex, r"\1", version_string)),
|
||||
int(re.sub(regex, r"\2", version_string)),
|
||||
int(re.sub(regex, r"\3", version_string)),
|
||||
(re.sub(regex, r"\4", version_string) != ""))
|
||||
|
||||
|
||||
def get_version_from_git():
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
try:
|
||||
@@ -12,19 +26,15 @@ def get_version_from_git():
|
||||
cwd=script_dir)
|
||||
git_tag = git_tag.decode(sys.stdout.encoding).rstrip('\n')
|
||||
|
||||
regex=r'.*v([0-9a-zA-Z]).([0-9a-zA-Z]).([0-9a-zA-Z])(.*)'
|
||||
package_version_major = int(re.sub(regex, r"\1", git_tag))
|
||||
package_version_minor = int(re.sub(regex, r"\2", git_tag))
|
||||
package_version_revision = int(re.sub(regex, r"\3", git_tag))
|
||||
package_version_unreleased = (re.sub(regex, r"\4", git_tag) != "")
|
||||
(major, minor, revision, is_prerelease) = version_str_to_tuple(git_tag)
|
||||
|
||||
if package_version_unreleased:
|
||||
package_version_revision += 1
|
||||
if is_prerelease:
|
||||
revision += 1
|
||||
return git_tag, major, minor, revision, is_prerelease
|
||||
|
||||
except Exception as ex:
|
||||
print(ex)
|
||||
return "[unknown version]", 0, 0, 0, 1
|
||||
return git_tag, package_version_major, package_version_minor, package_version_revision, package_version_unreleased
|
||||
|
||||
def get_version_str(git_only=False):
|
||||
"""
|
||||
|
||||
+50
-7
@@ -8,7 +8,8 @@ import sys
|
||||
import argparse
|
||||
import os
|
||||
import odrive.discovery
|
||||
from odrive.utils import Logger, Event
|
||||
from odrive.utils import Logger, Event, OperationAbortedException
|
||||
from odrive.configuration import *
|
||||
|
||||
# Flush stdout by default
|
||||
# Source:
|
||||
@@ -35,8 +36,26 @@ shell_parser.add_argument("--no-ipython", action="store_true",
|
||||
"instead of the IPython shell, "
|
||||
"even if IPython is installed.")
|
||||
|
||||
dfu_parser = subparsers.add_parser('dfu', help="Upgrade the ODrive device firmware")
|
||||
dfu_parser.add_argument('file', metavar='HEX', help='The .hex file to be flashed. Make sure your firmware board version matches the actual board version.')
|
||||
dfu_parser = subparsers.add_parser('dfu', help="Upgrade the ODrive device firmware."
|
||||
"If no serial number is specified, the first ODrive that is found is updated")
|
||||
dfu_parser.add_argument('file', metavar='HEX', nargs='?',
|
||||
help='The .hex file to be flashed. Make sure target board version '
|
||||
'of the firmware file matches the actual board version. '
|
||||
'You can download the latest release manually from '
|
||||
'https://github.com/madcowswe/ODrive/releases. '
|
||||
'If no file is provided, the script automatically downloads '
|
||||
'the latest firmware.')
|
||||
|
||||
|
||||
dfu_parser = subparsers.add_parser('backup-config', help="Saves the configuration of the ODrive to a JSON file")
|
||||
dfu_parser.add_argument('file', nargs='?',
|
||||
help="Path to the file where to store the data. "
|
||||
"If no path is provided, the configuration is stored in {}.".format(tempfile.gettempdir()))
|
||||
|
||||
dfu_parser = subparsers.add_parser('restore-config', help="Restores the configuration of the ODrive from a JSON file")
|
||||
dfu_parser.add_argument('file', nargs='?',
|
||||
help="Path to the file that contains the configuration data. "
|
||||
"If no path is provided, the configuration is loaded from {}.".format(tempfile.gettempdir()))
|
||||
|
||||
code_generator_parser = subparsers.add_parser('generate-code', help="Process a jinja2 template, passing the ODrive's JSON data as data input")
|
||||
code_generator_parser.add_argument("-t", "--template", type=argparse.FileType('r'),
|
||||
@@ -119,12 +138,14 @@ try:
|
||||
elif args.command == 'dfu':
|
||||
print_version()
|
||||
import odrive.dfu
|
||||
odrive.dfu.launch_dfu(args, app_shutdown_token)
|
||||
odrive.dfu.launch_dfu(args, logger, app_shutdown_token)
|
||||
|
||||
elif args.command == 'liveplotter':
|
||||
from odrive.utils import start_liveplotter
|
||||
print("Waiting for ODrive...")
|
||||
my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number)
|
||||
my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number,
|
||||
search_cancellation_token=app_shutdown_token,
|
||||
channel_termination_token=app_shutdown_token)
|
||||
|
||||
# If you want to plot different values, change them here.
|
||||
# You can plot any number of values concurrently.
|
||||
@@ -134,14 +155,18 @@ try:
|
||||
elif args.command == 'drv-status':
|
||||
from odrive.utils import print_drv_regs
|
||||
print("Waiting for ODrive...")
|
||||
my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number)
|
||||
my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number,
|
||||
search_cancellation_token=app_shutdown_token,
|
||||
channel_termination_token=app_shutdown_token)
|
||||
print_drv_regs("Motor 0", my_odrive.axis0.motor)
|
||||
print_drv_regs("Motor 1", my_odrive.axis1.motor)
|
||||
|
||||
elif args.command == 'rate-test':
|
||||
from odrive.utils import rate_test
|
||||
print("Waiting for ODrive...")
|
||||
my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number)
|
||||
my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number,
|
||||
search_cancellation_token=app_shutdown_token,
|
||||
channel_termination_token=app_shutdown_token)
|
||||
rate_test(my_odrive)
|
||||
|
||||
elif args.command == 'udev-setup':
|
||||
@@ -154,8 +179,26 @@ try:
|
||||
channel_termination_token=app_shutdown_token)
|
||||
generate_code(my_odrive, args.template, args.output)
|
||||
|
||||
elif args.command == 'backup-config':
|
||||
from odrive.configuration import backup_config
|
||||
print("Waiting for ODrive...")
|
||||
my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number,
|
||||
search_cancellation_token=app_shutdown_token,
|
||||
channel_termination_token=app_shutdown_token)
|
||||
backup_config(my_odrive, args.file, logger)
|
||||
|
||||
elif args.command == 'restore-config':
|
||||
from odrive.configuration import restore_config
|
||||
print("Waiting for ODrive...")
|
||||
my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number,
|
||||
search_cancellation_token=app_shutdown_token,
|
||||
channel_termination_token=app_shutdown_token)
|
||||
restore_config(my_odrive, args.file, logger)
|
||||
|
||||
else:
|
||||
raise Exception("unknown command: " + args.command)
|
||||
|
||||
except OperationAbortedException:
|
||||
logger.info("Operation aborted.")
|
||||
finally:
|
||||
app_shutdown_token.set()
|
||||
|
||||
+3
-3
@@ -142,7 +142,7 @@ try:
|
||||
if isinstance(test, ODriveTest):
|
||||
def odrv_test_thread(odrv_name):
|
||||
odrv_ctx = odrives_by_name[odrv_name]
|
||||
logger.info('* running {} on {}...'.format(type(test).__name__, odrv_name))
|
||||
logger.notify('* running {} on {}...'.format(type(test).__name__, odrv_name))
|
||||
try:
|
||||
test.check_preconditions(odrv_ctx,
|
||||
logger.indent(' {}: '.format(odrv_name)))
|
||||
@@ -171,7 +171,7 @@ try:
|
||||
try:
|
||||
if not app_shutdown_token.is_set():
|
||||
# Run test on this axis
|
||||
logger.info('* running {} on {}...'.format(type(test).__name__, axis_name))
|
||||
logger.notify('* running {} on {}...'.format(type(test).__name__, axis_name))
|
||||
try:
|
||||
test.check_preconditions(axis_ctx,
|
||||
logger.indent(' {}: '.format(axis_name)))
|
||||
@@ -203,7 +203,7 @@ try:
|
||||
try:
|
||||
if not app_shutdown_token.is_set():
|
||||
# Run test on this axis
|
||||
logger.info('* running {} on {}...'.format(type(test).__name__, coupling_name))
|
||||
logger.notify('* running {} on {}...'.format(type(test).__name__, coupling_name))
|
||||
try:
|
||||
test.check_preconditions(coupled_axes[0], coupled_axes[1],
|
||||
logger.indent(' {}: '.format(coupling_name)))
|
||||
|
||||
Reference in New Issue
Block a user