From b3ddadb36072cb3e1d69e78c7f02f29353f1109d Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 26 Jun 2020 11:31:38 +0200 Subject: [PATCH] further improve hardware independence --- Firmware/Board/v3/Inc/board.h | 44 +++++-- .../Class/CDC/Src/usbd_cdc.c | 10 +- Firmware/Board/v3/board.cpp | 65 ++++++----- .../nvm.c => Drivers/STM32/stm32_nvm.c} | 67 ++++++++--- .../nvm.h => Drivers/STM32/stm32_nvm.h} | 2 +- Firmware/Drivers/STM32/stm32_system.h | 4 +- Firmware/MotorControl/low_level.cpp | 110 ++---------------- Firmware/MotorControl/main.cpp | 54 +++++++-- Firmware/MotorControl/nvm_config.hpp | 3 +- Firmware/MotorControl/odrive_main.h | 26 +---- Firmware/MotorControl/pwm_input.cpp | 91 +++++++++++++++ Firmware/MotorControl/pwm_input.hpp | 22 ++++ Firmware/Tupfile.lua | 3 +- Firmware/communication/communication.cpp | 38 ++---- Firmware/communication/communication.h | 5 - Firmware/communication/interface_can.cpp | 1 - Firmware/communication/interface_uart.cpp | 35 +++--- Firmware/communication/interface_uart.h | 4 +- Firmware/communication/interface_usb.cpp | 4 +- Firmware/{Board/v3/Inc => }/freertos_vars.h | 0 Firmware/odrive-interface.yaml | 22 +++- tools/odrive/enums.py | 14 ++- 22 files changed, 358 insertions(+), 266 deletions(-) rename Firmware/{MotorControl/nvm.c => Drivers/STM32/stm32_nvm.c} (92%) rename Firmware/{MotorControl/nvm.h => Drivers/STM32/stm32_nvm.h} (98%) create mode 100644 Firmware/MotorControl/pwm_input.cpp create mode 100644 Firmware/MotorControl/pwm_input.hpp rename Firmware/{Board/v3/Inc => }/freertos_vars.h (100%) diff --git a/Firmware/Board/v3/Inc/board.h b/Firmware/Board/v3/Inc/board.h index 0888d4d1..2ee28877 100644 --- a/Firmware/Board/v3/Inc/board.h +++ b/Firmware/Board/v3/Inc/board.h @@ -14,11 +14,14 @@ #include #include #include +#include #include #include "cmsis_os.h" #include +#include + #if HW_VERSION_MINOR <= 3 #define SHUNT_RESISTANCE (675e-6f) #else @@ -38,13 +41,34 @@ #define DEFAULT_BRAKE_RESISTANCE (0.47f) // [ohm] #endif +#define DEFAULT_GPIO_MODES \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_UART0, \ + ODriveIntf::GPIO_MODE_UART0, \ + ODriveIntf::GPIO_MODE_ANALOG_IN, \ + ODriveIntf::GPIO_MODE_ANALOG_IN, \ + ODriveIntf::GPIO_MODE_ANALOG_IN, \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_ENC0, \ + ODriveIntf::GPIO_MODE_ENC0, \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_ENC1, \ + ODriveIntf::GPIO_MODE_ENC1, \ + ODriveIntf::GPIO_MODE_DIGITAL, \ + ODriveIntf::GPIO_MODE_CAN0, \ + ODriveIntf::GPIO_MODE_CAN0, + #define GPIO_AF_NONE ((uint8_t)0xff) #define TIM_TIME_BASE TIM14 #ifdef __cplusplus -#include #include +#include +#include +#include #include using TGateDriver = Drv8301; @@ -57,13 +81,18 @@ extern Motor motors[AXIS_COUNT]; extern OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT]; extern Encoder encoders[AXIS_COUNT]; extern Stm32Gpio gpios[GPIO_COUNT]; -extern uint8_t alternate_functions[GPIO_COUNT][6]; -extern uint32_t pwm_in_gpios[4]; +extern uint8_t alternate_functions[GPIO_COUNT][10]; extern PCD_HandleTypeDef& usb_pcd_handle; +extern USBD_HandleTypeDef& usb_dev_handle; -#include extern Stm32SpiArbiter& ext_spi_arbiter; + +extern UART_HandleTypeDef* uart0; +extern UART_HandleTypeDef* uart1; +extern UART_HandleTypeDef* uart2; + +extern PwmInput pwm0_input; #endif // Period in [s] @@ -112,13 +141,6 @@ const BoardHardwareConfig_t hw_configs[AXIS_COUNT] = { { } }; #endif -#define I2C_A0_PORT GPIO_3_GPIO_Port -#define I2C_A0_PIN GPIO_3_Pin -#define I2C_A1_PORT GPIO_4_GPIO_Port -#define I2C_A1_PIN GPIO_4_Pin -#define I2C_A2_PORT GPIO_5_GPIO_Port -#define I2C_A2_PIN GPIO_5_Pin - #if HW_VERSION_VOLTAGE >= 48 #define VBUS_S_DIVIDER_RATIO 19.0f #elif HW_VERSION_VOLTAGE == 24 diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c index 73313522..a3042278 100644 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c +++ b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c @@ -414,10 +414,10 @@ static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init(); /* Init Xfer states */ - hcdc->CDC_Tx.State =0; - hcdc->CDC_Rx.State =0; - hcdc->ODRIVE_Tx.State =0; - hcdc->ODRIVE_Rx.State =0; + hcdc->CDC_Tx.State = 0; + hcdc->CDC_Rx.State = 0; + hcdc->ODRIVE_Tx.State = 0; + hcdc->ODRIVE_Rx.State = 0; if(pdev->dev_speed == USBD_SPEED_HIGH ) { @@ -440,7 +440,7 @@ static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, USBD_LL_PrepareReceive(pdev, ODRIVE_OUT_EP, hcdc->ODRIVE_Rx.Buffer, - CDC_DATA_FS_OUT_PACKET_SIZE); + pdev->dev_speed == USBD_SPEED_HIGH ? CDC_DATA_HS_OUT_PACKET_SIZE : CDC_DATA_FS_OUT_PACKET_SIZE); } return ret; } diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index 860e1905..38143014 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -5,7 +5,6 @@ #include #include - #include #include @@ -21,6 +20,10 @@ extern "C" void SystemClock_Config(void); // defined in main.c generated by Cube Stm32SpiArbiter spi3_arbiter{&hspi3}; Stm32SpiArbiter& ext_spi_arbiter = spi3_arbiter; +UART_HandleTypeDef* uart0 = &huart4; +UART_HandleTypeDef* uart1 = nullptr; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2 +UART_HandleTypeDef* uart2 = nullptr; + Drv8301 m0_gate_driver{ &spi3_arbiter, {M0_nCS_GPIO_Port, M0_nCS_Pin}, // nCS @@ -163,42 +166,47 @@ Stm32Gpio gpios[GPIO_COUNT] = { #endif #define GPIO_AF_NONE ((uint8_t)0xff) -uint8_t alternate_functions[GPIO_COUNT][6] = { - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + +// The columns go like this: +// UART0 | UART1 | UART2 | CAN0 | I2C0 | SPI0 | PWM0 | ENC0 | ENC1 | ENC2 +uint8_t alternate_functions[GPIO_COUNT][10] = { + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, #if HW_VERSION_MINOR >= 3 - {GPIO_AF8_UART4, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF8_UART4, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF8_UART4, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF8_UART4, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, #else - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, #endif - {GPIO_AF_NONE, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM5, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM3, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM3, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF2_TIM4}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF2_TIM4}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF9_CAN1, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF_NONE}, - {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF9_CAN1, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM3, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM3, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM4, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF2_TIM4, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF9_CAN1, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, + {GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF9_CAN1, GPIO_AF4_I2C1, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE, GPIO_AF_NONE}, }; #if HW_VERSION_MINOR <= 2 -uint32_t pwm_in_gpios[4] = { 0, 0, 0, 4 }; // 0 means not in use +PwmInput pwm0_input{&htim5, {0, 0, 0, 4}}; // 0 means not in use #else -uint32_t pwm_in_gpios[4] = { 1, 2, 3, 4 }; +PwmInput pwm0_input{&htim5, {1, 2, 3, 4}}; #endif extern PCD_HandleTypeDef hpcd_USB_OTG_FS; // defined in usbd_conf.c PCD_HandleTypeDef& usb_pcd_handle = hpcd_USB_OTG_FS; +extern USBD_HandleTypeDef hUsbDeviceFS; +USBD_HandleTypeDef& usb_dev_handle = hUsbDeviceFS; void system_init() { // Reset of all peripherals, Initializes the Flash interface and the Systick. @@ -210,7 +218,6 @@ void system_init() { void board_init() { // Initialize all configured peripherals - // CAN is initialized in main.cpp instead MX_GPIO_Init(); MX_DMA_Init(); MX_ADC1_Init(); @@ -226,9 +233,9 @@ void board_init() { MX_TIM5_Init(); MX_TIM13_Init(); - HAL_UART_DeInit(&huart4); - huart4.Init.BaudRate = odrv.config_.uart0_baudrate; - HAL_UART_Init(&huart4); + HAL_UART_DeInit(uart0); + uart0->Init.BaudRate = odrv.config_.uart0_baudrate; + HAL_UART_Init(uart0); if (odrv.config_.enable_i2c0) { // Set up the direction GPIO as input @@ -311,7 +318,7 @@ void TIM8_UP_TIM13_IRQHandler(void) { } void TIM5_IRQHandler(void) { - pwm_in_cb(&htim5); + pwm0_input.on_capture(); } void ADC_IRQ_Dispatch(ADC_HandleTypeDef* hadc, void(*callback)(ADC_HandleTypeDef* hadc, bool injected)) { diff --git a/Firmware/MotorControl/nvm.c b/Firmware/Drivers/STM32/stm32_nvm.c similarity index 92% rename from Firmware/MotorControl/nvm.c rename to Firmware/Drivers/STM32/stm32_nvm.c index b21a0148..8d59f991 100644 --- a/Firmware/MotorControl/nvm.c +++ b/Firmware/Drivers/STM32/stm32_nvm.c @@ -30,22 +30,24 @@ * fields as "valid" (in the direction of increasing address). */ -#include "nvm.h" +#include "stm32_nvm.h" -#include -#include #include #if defined(STM32F405xx) +#include +#include + // refer to page 75 of datasheet: // http://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf -#define FLASH_SECTOR_10_BASE (const volatile uint8_t*)0x80C0000UL -#define FLASH_SECTOR_10_SIZE 0x20000UL -#define FLASH_SECTOR_11_BASE (const volatile uint8_t*)0x80E0000UL -#define FLASH_SECTOR_11_SIZE 0x20000UL +#define FLASH_SECTOR_A FLASH_SECTOR_10 +#define FLASH_SECTOR_A_BASE (const volatile uint8_t*)0x80C0000UL +#define FLASH_SECTOR_A_SIZE 0x20000UL +#define FLASH_SECTOR_B FLASH_SECTOR_11 +#define FLASH_SECTOR_B_BASE (const volatile uint8_t*)0x80E0000UL +#define FLASH_SECTOR_B_SIZE 0x20000UL -#define HAL_FLASH_ClearError() __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR | FLASH_FLAG_PGPERR) #else #error "unknown flash sector size" #endif @@ -66,30 +68,58 @@ typedef struct { } sector_t; sector_t sectors[] = { { - .sector_id = FLASH_SECTOR_10, - .n_data = FLASH_SECTOR_10_SIZE >> 3, - .n_reserved = (FLASH_SECTOR_10_SIZE >> 3) >> 5, - .alloc_table = FLASH_SECTOR_10_BASE, - .data = (uint64_t *)FLASH_SECTOR_10_BASE + .sector_id = FLASH_SECTOR_A, + .n_data = FLASH_SECTOR_A_SIZE >> 3, + .n_reserved = (FLASH_SECTOR_A_SIZE >> 3) >> 5, + .alloc_table = FLASH_SECTOR_A_BASE, + .data = (uint64_t *)FLASH_SECTOR_A_BASE }, { - .sector_id = FLASH_SECTOR_11, - .n_data = FLASH_SECTOR_11_SIZE >> 3, - .n_reserved = (FLASH_SECTOR_11_SIZE >> 3) >> 5, - .alloc_table = FLASH_SECTOR_11_BASE, - .data = (uint64_t *)FLASH_SECTOR_11_BASE + .sector_id = FLASH_SECTOR_B, + .n_data = FLASH_SECTOR_B_SIZE >> 3, + .n_reserved = (FLASH_SECTOR_B_SIZE >> 3) >> 5, + .alloc_table = FLASH_SECTOR_B_BASE, + .data = (uint64_t *)FLASH_SECTOR_B_BASE }}; uint8_t read_sector_; // 0 or 1 to indicate which sector to read from and which to write to size_t n_staging_area_; // number of 64-bit values that were reserved using NVM_start_write size_t n_valid_; // number of 64-bit fields that can be read +static const uint32_t FLASH_ERR_FLAGS = +#if defined(FLASH_FLAG_EOP) + FLASH_FLAG_EOP | +#endif +#if defined(FLASH_FLAG_OPERR) + FLASH_FLAG_OPERR | +#endif +#if defined(FLASH_FLAG_WRPERR) + FLASH_FLAG_WRPERR | +#endif +#if defined(FLASH_FLAG_PGAERR) + FLASH_FLAG_PGAERR | +#endif +#if defined(FLASH_FLAG_PGSERR) + FLASH_FLAG_PGSERR | +#endif +#if defined(FLASH_FLAG_PGPERR) + FLASH_FLAG_PGPERR | +#endif + 0; + +static void HAL_FLASH_ClearError() { + __HAL_FLASH_CLEAR_FLAG(FLASH_ERR_FLAGS); +} + + // @brief Erases a flash sector. This sets all bits in the sector to 1. // The sector's current index is reset to the minimum value (n_reserved). // @returns 0 on success or a non-zero error code otherwise int erase(sector_t *sector) { FLASH_EraseInitTypeDef erase_struct = { .TypeErase = FLASH_TYPEERASE_SECTORS, +#if defined(FLASH_OPTCR_nDBANK) .Banks = 0, // only used for mass erase +#endif .Sector = sector->sector_id, .NbSectors = 1, .VoltageRange = FLASH_VOLTAGE_RANGE_3 @@ -375,6 +405,7 @@ int NVM_commit(void) { #include +#include /** @brief Call this at startup to test/demo the NVM driver Expected output when starting with a fully erased NVM diff --git a/Firmware/MotorControl/nvm.h b/Firmware/Drivers/STM32/stm32_nvm.h similarity index 98% rename from Firmware/MotorControl/nvm.h rename to Firmware/Drivers/STM32/stm32_nvm.h index efffa174..8e0e8c2f 100644 --- a/Firmware/MotorControl/nvm.h +++ b/Firmware/Drivers/STM32/stm32_nvm.h @@ -1,5 +1,5 @@ /* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __NVML_H +#ifndef __NVM_H #define __NVM_H #ifdef __cplusplus diff --git a/Firmware/Drivers/STM32/stm32_system.h b/Firmware/Drivers/STM32/stm32_system.h index d7cc14e5..cdba1b77 100644 --- a/Firmware/Drivers/STM32/stm32_system.h +++ b/Firmware/Drivers/STM32/stm32_system.h @@ -3,13 +3,13 @@ #include -inline uint32_t cpu_enter_critical() { +static inline uint32_t cpu_enter_critical() { uint32_t primask = __get_PRIMASK(); __disable_irq(); return primask; } -inline void cpu_exit_critical(uint32_t priority_mask) { +static inline void cpu_exit_critical(uint32_t priority_mask) { __set_PRIMASK(priority_mask); } diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index c69e74a0..bb94ba7c 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -1,11 +1,6 @@ /* Includes ------------------------------------------------------------------*/ -// Because of broken cmsis_os.h, we need to include arm_math first, -// otherwise chip specific defines are ommited -#include -#include // Sets up the correct chip specifc defines required by arm_math -#include -#include +#include #include #include @@ -279,9 +274,9 @@ void start_general_purpose_adc() { hadc1.Init.NbrOfConversion = ADC_CHANNEL_COUNT; hadc1.Init.DMAContinuousRequests = ENABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; - if (HAL_ADC_Init(&hadc1) != HAL_OK) - { - _Error_Handler((char*)__FILE__, __LINE__); + if (HAL_ADC_Init(&hadc1) != HAL_OK) { + odrv.misconfigured_ = true; // TODO: this is a bit of an abuse of this flag + return; } // Set up sampling sequence (channel 0 ... channel 15) @@ -289,8 +284,10 @@ void start_general_purpose_adc() { for (uint32_t channel = 0; channel < ADC_CHANNEL_COUNT; ++channel) { sConfig.Channel = channel << ADC_CR1_AWDCH_Pos; sConfig.Rank = channel + 1; // rank numbering starts at 1 - if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) - _Error_Handler((char*)__FILE__, __LINE__); + if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { + odrv.misconfigured_ = true; // TODO: this is a bit of an abuse of this flag + return; + } } HAL_ADC_Start_DMA(&hadc1, reinterpret_cast(adc_measurements_), ADC_CHANNEL_COUNT); @@ -534,97 +531,6 @@ void update_brake_current() { } -/* RC PWM input --------------------------------------------------------------*/ - -void pwm_in_init() { - TIM_IC_InitTypeDef sConfigIC; - sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE; - sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; - sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; - sConfigIC.ICFilter = 15; - - uint32_t channels[] = {TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4}; - - for (size_t i = 0; i < 4; ++i) { - if (!fibre::is_endpoint_ref_valid(odrv.config_.pwm_mappings[i].endpoint)) - continue; - HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, channels[i]); - HAL_TIM_IC_Start_IT(&htim5, channels[i]); - } -} - -//TODO: These expressions have integer division by 1MHz, so it will be incorrect for clock speeds of not-integer MHz -#define TIM_2_5_CLOCK_HZ TIM_APB1_CLOCK_HZ -#define PWM_MIN_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 1000UL) // 1ms high is considered full reverse -#define PWM_MAX_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2000UL) // 2ms high is considered full forward -#define PWM_MIN_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 500UL) // ignore high periods shorter than 0.5ms -#define PWM_MAX_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2500UL) // ignore high periods longer than 2.5ms -#define PWM_INVERT_INPUT false - -/** - * @param channel: A channel number in [0, 3] - */ -void handle_pulse(int channel, uint32_t high_time) { - if (high_time < PWM_MIN_LEGAL_HIGH_TIME || high_time > PWM_MAX_LEGAL_HIGH_TIME) - return; - - if (high_time < PWM_MIN_HIGH_TIME) - high_time = PWM_MIN_HIGH_TIME; - if (high_time > PWM_MAX_HIGH_TIME) - high_time = PWM_MAX_HIGH_TIME; - float fraction = (float)(high_time - PWM_MIN_HIGH_TIME) / (float)(PWM_MAX_HIGH_TIME - PWM_MIN_HIGH_TIME); - float value = odrv.config_.pwm_mappings[channel].min + - (fraction * (odrv.config_.pwm_mappings[channel].max - odrv.config_.pwm_mappings[channel].min)); - - fibre::set_endpoint_from_float(odrv.config_.pwm_mappings[channel].endpoint, value); -} - -/** - * @param channel: A channel number in [0, 3] - */ -void pwm_in_cb_for_channel(int channel, uint32_t timestamp) { - static uint32_t last_timestamp[4] = { 0 }; - static bool last_pin_state[4] = { false }; - static bool last_sample_valid[4] = { false }; - - if (channel >= 4) - return; - Stm32Gpio gpio = get_gpio(pwm_in_gpios[channel]); - if (!gpio) - return; - bool current_pin_state = gpio.read(); - - if (last_sample_valid[channel] - && (last_pin_state[channel] != PWM_INVERT_INPUT) - && (current_pin_state == PWM_INVERT_INPUT)) { - handle_pulse(channel, timestamp - last_timestamp[channel]); - } - - last_timestamp[channel] = timestamp; - last_pin_state[channel] = current_pin_state; - last_sample_valid[channel] = true; -} - -void pwm_in_cb(TIM_HandleTypeDef *htim) { - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1)) { - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1); - pwm_in_cb_for_channel(0, htim->Instance->CCR1); - } - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2)) { - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2); - pwm_in_cb_for_channel(1, htim->Instance->CCR2); - } - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3)) { - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC3); - pwm_in_cb_for_channel(2, htim->Instance->CCR3); - } - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4)) { - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC4); - pwm_in_cb_for_channel(3, htim->Instance->CCR4); - } -} - - /* Analog speed control input */ static void update_analog_endpoint(const struct PWMMapping_t *map, int gpio) diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index d416e40f..fec1dd4c 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -20,8 +20,10 @@ osSemaphoreId sem_can; osThreadId usb_irq_thread; const uint32_t stack_size_usb_irq_thread = 2048; // Bytes +#if defined(STM32F405xx) // Place FreeRTOS heap in core coupled memory for better performance __attribute__((section(".ccmram"))) +#endif uint8_t ucHeap[configTOTAL_HEAP_SIZE]; uint32_t _reboot_cookie __attribute__ ((section (".noinit"))); @@ -164,7 +166,7 @@ static void usb_deferred_interrupt_thread(void * ctx) { // We have a new incoming USB transmission: handle it HAL_PCD_IRQHandler(&usb_pcd_handle); // Let the irq (OTG_FS_IRQHandler) fire again. - HAL_NVIC_EnableIRQ(OTG_FS_IRQn); + HAL_NVIC_EnableIRQ((usb_pcd_handle.Instance == USB_OTG_FS) ? OTG_FS_IRQn : OTG_HS_IRQn); } } } @@ -183,7 +185,6 @@ void vApplicationIdleHook(void) { if (odrv.system_stats_.fully_booted) { odrv.system_stats_.uptime = xTaskGetTickCount(); odrv.system_stats_.min_heap_space = xPortGetMinimumEverFreeHeapSize(); - odrv.system_stats_.min_stack_space_comms = uxTaskGetStackHighWaterMark(comm_thread) * sizeof(StackType_t); uint32_t min_stack_space[AXIS_COUNT]; std::transform(axes.begin(), axes.end(), std::begin(min_stack_space), [](auto& axis) { return uxTaskGetStackHighWaterMark(axes[1]->thread_id_) * sizeof(StackType_t); }); odrv.system_stats_.min_stack_space_axis = *std::min_element(std::begin(min_stack_space), std::end(min_stack_space)); @@ -195,7 +196,6 @@ void vApplicationIdleHook(void) { // Actual usage, in bytes, so we don't have to math odrv.system_stats_.stack_usage_axis = axes[0]->stack_size_ - odrv.system_stats_.min_stack_space_axis; - odrv.system_stats_.stack_usage_comms = stack_size_comm_thread - odrv.system_stats_.min_stack_space_comms; odrv.system_stats_.stack_usage_usb = stack_size_usb_thread - odrv.system_stats_.min_stack_space_usb; odrv.system_stats_.stack_usage_uart = stack_size_uart_thread - odrv.system_stats_.min_stack_space_uart; odrv.system_stats_.stack_usage_usb_irq = stack_size_usb_irq_thread - odrv.system_stats_.min_stack_space_usb_irq; @@ -224,7 +224,7 @@ static void rtos_main(void*) { // Start pwm-in compare modules // must happen after communication is initialized - pwm_in_init(); + pwm0_input.init(); // Setup hardware for all components for (size_t i = 0; i < AXIS_COUNT; ++i) { @@ -346,6 +346,11 @@ extern "C" int main(void) { config_apply_all(); } + odrv.misconfigured_ = odrv.misconfigured_ + || (odrv.config_.enable_uart0 && !uart0) + || (odrv.config_.enable_uart1 && !uart1) + || (odrv.config_.enable_uart2 && !uart2); + // Init board-specific peripherals board_init(); @@ -381,17 +386,29 @@ extern "C" int main(void) { odrv.misconfigured_ = true; } } break; - case ODriveIntf::GPIO_MODE_PWM0: { + case ODriveIntf::GPIO_MODE_UART1: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = alternate_functions[i][1]; + if (!odrv.config_.enable_uart1) { + odrv.misconfigured_ = true; + } + } break; + case ODriveIntf::GPIO_MODE_UART2: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = alternate_functions[i][2]; + if (!odrv.config_.enable_uart2) { + odrv.misconfigured_ = true; + } } break; case ODriveIntf::GPIO_MODE_CAN0: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = alternate_functions[i][2]; + GPIO_InitStruct.Alternate = alternate_functions[i][3]; if (!odrv.config_.enable_can0) { odrv.misconfigured_ = true; } @@ -400,22 +417,37 @@ extern "C" int main(void) { GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.Alternate = alternate_functions[i][3]; + GPIO_InitStruct.Alternate = alternate_functions[i][4]; if (!odrv.config_.enable_i2c0) { odrv.misconfigured_ = true; } } break; + case ODriveIntf::GPIO_MODE_SPI0: { + GPIO_InitStruct.Alternate = GPIO_AF_NONE; // TODO + } break; + case ODriveIntf::GPIO_MODE_PWM0: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = alternate_functions[i][6]; + } break; case ODriveIntf::GPIO_MODE_ENC0: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = alternate_functions[i][4]; + GPIO_InitStruct.Alternate = alternate_functions[i][7]; } break; case ODriveIntf::GPIO_MODE_ENC1: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStruct.Alternate = alternate_functions[i][5]; + GPIO_InitStruct.Alternate = alternate_functions[i][8]; + } break; + case ODriveIntf::GPIO_MODE_ENC2: { + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = alternate_functions[i][9]; } break; default: { GPIO_InitStruct.Alternate = GPIO_AF_NONE; diff --git a/Firmware/MotorControl/nvm_config.hpp b/Firmware/MotorControl/nvm_config.hpp index 7d48b5ef..957da707 100644 --- a/Firmware/MotorControl/nvm_config.hpp +++ b/Firmware/MotorControl/nvm_config.hpp @@ -9,9 +9,8 @@ #include #include -#include -#include "nvm.h" +#include #include diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 184140fc..9dd87ded 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -31,7 +31,6 @@ typedef struct { uint32_t uptime; // [ms] uint32_t min_heap_space; // FreeRTOS heap [Bytes] uint32_t min_stack_space_axis; // minimum remaining space since startup [Bytes] - uint32_t min_stack_space_comms; uint32_t min_stack_space_usb; uint32_t min_stack_space_uart; uint32_t min_stack_space_usb_irq; @@ -39,7 +38,6 @@ typedef struct { uint32_t min_stack_space_can; uint32_t stack_usage_axis; - uint32_t stack_usage_comms; uint32_t stack_usage_usb; uint32_t stack_usage_uart; uint32_t stack_usage_usb_irq; @@ -59,27 +57,15 @@ struct PWMMapping_t { // @brief general user configurable board configuration struct BoardConfig_t { ODriveIntf::GpioMode gpio_modes[GPIO_COUNT] = { - ODriveIntf::GPIO_MODE_DIGITAL, - ODriveIntf::GPIO_MODE_UART0, - ODriveIntf::GPIO_MODE_UART0, - ODriveIntf::GPIO_MODE_ANALOG_IN, - ODriveIntf::GPIO_MODE_ANALOG_IN, - ODriveIntf::GPIO_MODE_ANALOG_IN, - ODriveIntf::GPIO_MODE_DIGITAL, - ODriveIntf::GPIO_MODE_DIGITAL, - ODriveIntf::GPIO_MODE_DIGITAL, - ODriveIntf::GPIO_MODE_ENC0, - ODriveIntf::GPIO_MODE_ENC0, - ODriveIntf::GPIO_MODE_DIGITAL, - ODriveIntf::GPIO_MODE_ENC1, - ODriveIntf::GPIO_MODE_ENC1, - ODriveIntf::GPIO_MODE_DIGITAL, - ODriveIntf::GPIO_MODE_CAN0, - ODriveIntf::GPIO_MODE_CAN0, + DEFAULT_GPIO_MODES }; bool enable_uart0 = true; + bool enable_uart1 = false; + bool enable_uart2 = false; uint32_t uart0_baudrate = 115200; + uint32_t uart1_baudrate = 115200; + uint32_t uart2_baudrate = 115200; bool enable_can0 = true; bool enable_i2c0 = false; bool enable_ascii_protocol_on_usb = true; @@ -201,7 +187,7 @@ public: const uint64_t& serial_number_ = ::serial_number; -#if HW_VERSION_MAJOR == 3 +#if defined(STM32F405xx) // Determine start address of the OTP struct: // The OTP is organized into 16-byte blocks. // If the first block starts with "0xfe" we use the first block. diff --git a/Firmware/MotorControl/pwm_input.cpp b/Firmware/MotorControl/pwm_input.cpp new file mode 100644 index 00000000..176b7eb2 --- /dev/null +++ b/Firmware/MotorControl/pwm_input.cpp @@ -0,0 +1,91 @@ + +#include "pwm_input.hpp" +#include "odrive_main.h" + +void PwmInput::init() { + TIM_IC_InitTypeDef sConfigIC; + sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE; + sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; + sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; + sConfigIC.ICFilter = 15; + + uint32_t channels[] = {TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4}; + + for (size_t i = 0; i < 4; ++i) { + if (!fibre::is_endpoint_ref_valid(odrv.config_.pwm_mappings[i].endpoint)) + continue; + HAL_TIM_IC_ConfigChannel(htim_, &sConfigIC, channels[i]); + HAL_TIM_IC_Start_IT(htim_, channels[i]); + } +} + +//TODO: These expressions have integer division by 1MHz, so it will be incorrect for clock speeds of not-integer MHz +#define TIM_2_5_CLOCK_HZ TIM_APB1_CLOCK_HZ +#define PWM_MIN_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 1000UL) // 1ms high is considered full reverse +#define PWM_MAX_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2000UL) // 2ms high is considered full forward +#define PWM_MIN_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 500UL) // ignore high periods shorter than 0.5ms +#define PWM_MAX_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2500UL) // ignore high periods longer than 2.5ms +#define PWM_INVERT_INPUT false + +/** + * @param channel: A channel number in [0, 3] + */ +void handle_pulse(int channel, uint32_t high_time) { + if (high_time < PWM_MIN_LEGAL_HIGH_TIME || high_time > PWM_MAX_LEGAL_HIGH_TIME) + return; + + if (high_time < PWM_MIN_HIGH_TIME) + high_time = PWM_MIN_HIGH_TIME; + if (high_time > PWM_MAX_HIGH_TIME) + high_time = PWM_MAX_HIGH_TIME; + float fraction = (float)(high_time - PWM_MIN_HIGH_TIME) / (float)(PWM_MAX_HIGH_TIME - PWM_MIN_HIGH_TIME); + float value = odrv.config_.pwm_mappings[channel].min + + (fraction * (odrv.config_.pwm_mappings[channel].max - odrv.config_.pwm_mappings[channel].min)); + + fibre::set_endpoint_from_float(odrv.config_.pwm_mappings[channel].endpoint, value); +} + +/** + * @param channel: A channel number in [0, 3] + */ +void PwmInput::on_capture(int channel, uint32_t timestamp) { + static uint32_t last_timestamp[4] = { 0 }; + static bool last_pin_state[4] = { false }; + static bool last_sample_valid[4] = { false }; + + if (channel >= 4) + return; + Stm32Gpio gpio = get_gpio(gpios_[channel]); + if (!gpio) + return; + bool current_pin_state = gpio.read(); + + if (last_sample_valid[channel] + && (last_pin_state[channel] != PWM_INVERT_INPUT) + && (current_pin_state == PWM_INVERT_INPUT)) { + handle_pulse(channel, timestamp - last_timestamp[channel]); + } + + last_timestamp[channel] = timestamp; + last_pin_state[channel] = current_pin_state; + last_sample_valid[channel] = true; +} + +void PwmInput::on_capture() { + if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC1)) { + __HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC1); + on_capture(0, htim_->Instance->CCR1); + } + if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC2)) { + __HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC2); + on_capture(1, htim_->Instance->CCR2); + } + if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC3)) { + __HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC3); + on_capture(2, htim_->Instance->CCR3); + } + if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC4)) { + __HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC4); + on_capture(3, htim_->Instance->CCR4); + } +} diff --git a/Firmware/MotorControl/pwm_input.hpp b/Firmware/MotorControl/pwm_input.hpp new file mode 100644 index 00000000..679dee66 --- /dev/null +++ b/Firmware/MotorControl/pwm_input.hpp @@ -0,0 +1,22 @@ +#ifndef __PWM_INPUT_HPP +#define __PWM_INPUT_HPP + +#include +#include + +class PwmInput { +public: + PwmInput(TIM_HandleTypeDef* htim, std::array gpios) + : htim_(htim), gpios_(gpios) {} + + void init(); + void on_capture(); + +private: + void on_capture(int channel, uint32_t timestamp); + + TIM_HandleTypeDef* htim_; + std::array gpios_; +}; + +#endif // __PWM_INPUT_HPP \ No newline at end of file diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 91a015b5..7c787e71 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -186,7 +186,6 @@ sources = { 'MotorControl/arm_sin_f32.c', 'MotorControl/arm_cos_f32.c', 'MotorControl/low_level.cpp', - 'MotorControl/nvm.c', 'MotorControl/axis.cpp', 'MotorControl/motor.cpp', 'MotorControl/thermistor.cpp', @@ -195,8 +194,10 @@ sources = { 'MotorControl/controller.cpp', 'MotorControl/sensorless_estimator.cpp', 'MotorControl/trapTraj.cpp', + 'MotorControl/pwm_input.cpp', 'MotorControl/main.cpp', 'Drivers/STM32/stm32_gpio.cpp', + 'Drivers/STM32/stm32_nvm.c', 'Drivers/STM32/stm32_spi_arbiter.cpp', 'communication/can_simple.cpp', 'communication/communication.cpp', diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index dfd45eb6..faac6f59 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -30,50 +30,30 @@ uint64_t serial_number; char serial_number_str[13]; // 12 digits + null termination +float oscilloscope[OSCILLOSCOPE_SIZE] = {0}; +size_t oscilloscope_pos = 0; + /* Private constant data -----------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ - -osThreadId comm_thread; -const uint32_t stack_size_comm_thread = 4096; // Bytes -volatile bool endpoint_list_valid = false; - /* Private function prototypes -----------------------------------------------*/ /* Function implementations --------------------------------------------------*/ void init_communication(void) { printf("hi!\r\n"); - // Start command handling thread - osThreadDef(task_cmd_parse, communication_task, osPriorityNormal, 0, stack_size_comm_thread / sizeof(StackType_t)); - comm_thread = osThreadCreate(osThread(task_cmd_parse), NULL); + if (odrv.config_.enable_uart0 && uart0) { + start_uart_server(); + } - while (!endpoint_list_valid) - osDelay(1); -} - -float oscilloscope[OSCILLOSCOPE_SIZE] = {0}; -size_t oscilloscope_pos = 0; - -// Thread to handle deffered processing of USB interrupt, and -// read commands out of the UART DMA circular buffer -void communication_task(void * ctx) { - (void) ctx; // unused parameter - - // Allow main init to continue - endpoint_list_valid = true; - - start_uart_server(); start_usb_server(); + if (odrv.config_.enable_i2c0) { start_i2c_server(); } + if (odrv.config_.enable_can0) { odCAN->start_can_server(); } - - for (;;) { - osDelay(1000); // nothing to do - } } extern "C" { @@ -86,7 +66,7 @@ int _write(int file, const char* data, int len) { usb_stream_output_ptr->process_bytes((const uint8_t *)data, len, nullptr); #endif #ifdef UART_PROTOCOL_STDOUT - uart4_stream_output_ptr->process_bytes((const uint8_t *)data, len, nullptr); + uart_stream_output_ptr->process_bytes((const uint8_t *)data, len, nullptr); #endif return len; } diff --git a/Firmware/communication/communication.h b/Firmware/communication/communication.h index 6987aa11..92c4faf1 100644 --- a/Firmware/communication/communication.h +++ b/Firmware/communication/communication.h @@ -14,12 +14,7 @@ extern "C" { #include -extern osThreadId comm_thread; -extern const uint32_t stack_size_comm_thread; - void init_communication(void); -void initTree(); -void communication_task(void * ctx); #ifdef __cplusplus } diff --git a/Firmware/communication/interface_can.cpp b/Firmware/communication/interface_can.cpp index 20b8ea6a..cf612231 100644 --- a/Firmware/communication/interface_can.cpp +++ b/Firmware/communication/interface_can.cpp @@ -6,7 +6,6 @@ #include #include -#include // Specific CAN Protocols #include "can_simple.hpp" diff --git a/Firmware/communication/interface_uart.cpp b/Firmware/communication/interface_uart.cpp index aa195574..e899325d 100644 --- a/Firmware/communication/interface_uart.cpp +++ b/Firmware/communication/interface_uart.cpp @@ -22,10 +22,12 @@ static uint32_t dma_last_rcv_idx; // static thread_local uint32_t deadline_ms = 0; osThreadId uart_thread; +extern UART_HandleTypeDef* uart0; +static UART_HandleTypeDef* huart_ = uart0; // defined in board.cpp. const uint32_t stack_size_uart_thread = 4096; // Bytes -class UART4Sender : public StreamSink { +class UARTSender : public StreamSink { public: int process_bytes(const uint8_t* buffer, size_t length, size_t* processed_bytes) { // Loop to ensure all bytes get sent @@ -38,7 +40,7 @@ public: return -1; // transmit chunk memcpy(tx_buf_, buffer, chunk); - if (HAL_UART_Transmit_DMA(&huart4, tx_buf_, chunk) != HAL_OK) + if (HAL_UART_Transmit_DMA(huart_, tx_buf_, chunk) != HAL_OK) return -1; buffer += chunk; length -= chunk; @@ -51,12 +53,12 @@ public: size_t get_free_space() { return SIZE_MAX; } private: uint8_t tx_buf_[UART_TX_BUFFER_SIZE]; -} uart4_stream_output; -StreamSink* uart4_stream_output_ptr = &uart4_stream_output; +} uart_stream_output; +StreamSink* uart_stream_output_ptr = &uart_stream_output; -StreamBasedPacketSink uart4_packet_output(uart4_stream_output); -BidirectionalPacketBasedChannel uart4_channel(uart4_packet_output); -StreamToPacketSegmenter uart4_stream_input(uart4_channel); +StreamBasedPacketSink uart_packet_output(uart_stream_output); +BidirectionalPacketBasedChannel uart_channel(uart_packet_output); +StreamToPacketSegmenter uart_stream_input(uart_channel); static void uart_server_thread(void * ctx) { (void) ctx; @@ -65,13 +67,13 @@ static void uart_server_thread(void * ctx) { osDelay(1); // Check for UART errors and restart recieve DMA transfer if required - if (huart4.RxState != HAL_UART_STATE_BUSY_RX) { - HAL_UART_AbortReceive(&huart4); - HAL_UART_Receive_DMA(&huart4, dma_rx_buffer, sizeof(dma_rx_buffer)); + if (huart_->RxState != HAL_UART_STATE_BUSY_RX) { + HAL_UART_AbortReceive(huart_); + HAL_UART_Receive_DMA(huart_, dma_rx_buffer, sizeof(dma_rx_buffer)); dma_last_rcv_idx = 0; } // Fetch the circular buffer "write pointer", where it would write next - uint32_t new_rcv_idx = UART_RX_BUFFER_SIZE - huart4.hdmarx->Instance->NDTR; + uint32_t new_rcv_idx = UART_RX_BUFFER_SIZE - huart_->hdmarx->Instance->NDTR; if (new_rcv_idx > UART_RX_BUFFER_SIZE) { // defensive programming continue; } @@ -79,27 +81,28 @@ static void uart_server_thread(void * ctx) { // deadline_ms = timeout_to_deadline(PROTOCOL_SERVER_TIMEOUT_MS); // Process bytes in one or two chunks (two in case there was a wrap) if (new_rcv_idx < dma_last_rcv_idx) { - uart4_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, + uart_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, UART_RX_BUFFER_SIZE - dma_last_rcv_idx, nullptr); // TODO: use process_all ASCII_protocol_parse_stream(dma_rx_buffer + dma_last_rcv_idx, - UART_RX_BUFFER_SIZE - dma_last_rcv_idx, uart4_stream_output); + UART_RX_BUFFER_SIZE - dma_last_rcv_idx, uart_stream_output); dma_last_rcv_idx = 0; } if (new_rcv_idx > dma_last_rcv_idx) { - uart4_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, + uart_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, new_rcv_idx - dma_last_rcv_idx, nullptr); // TODO: use process_all ASCII_protocol_parse_stream(dma_rx_buffer + dma_last_rcv_idx, - new_rcv_idx - dma_last_rcv_idx, uart4_stream_output); + new_rcv_idx - dma_last_rcv_idx, uart_stream_output); dma_last_rcv_idx = new_rcv_idx; } }; } +// TODO: allow multiple UART server instances 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 - HAL_UART_Receive_DMA(&huart4, dma_rx_buffer, sizeof(dma_rx_buffer)); + HAL_UART_Receive_DMA(huart_, dma_rx_buffer, sizeof(dma_rx_buffer)); dma_last_rcv_idx = 0; // Start UART communication thread diff --git a/Firmware/communication/interface_uart.h b/Firmware/communication/interface_uart.h index 65033a6f..362d417c 100644 --- a/Firmware/communication/interface_uart.h +++ b/Firmware/communication/interface_uart.h @@ -3,7 +3,7 @@ #ifdef __cplusplus #include "fibre/protocol.hpp" -extern StreamSink* uart4_stream_output_ptr; +extern StreamSink* uart_stream_output_ptr; extern "C" { #endif @@ -13,7 +13,7 @@ extern "C" { extern osThreadId uart_thread; extern const uint32_t stack_size_uart_thread; -void start_uart_server(void); +void start_uart_server(); #ifdef __cplusplus } diff --git a/Firmware/communication/interface_usb.cpp b/Firmware/communication/interface_usb.cpp index bbdabb3d..28171eda 100644 --- a/Firmware/communication/interface_usb.cpp +++ b/Firmware/communication/interface_usb.cpp @@ -138,7 +138,7 @@ static void usb_server_thread(void * ctx) { CDC_interface.rx_buf, CDC_interface.rx_len, nullptr); #endif } - USBD_CDC_ReceivePacket(&hUsbDeviceFS, CDC_interface.out_ep); // Allow next packet + USBD_CDC_ReceivePacket(&usb_dev_handle, CDC_interface.out_ep); // Allow next packet } // Native Interface @@ -150,7 +150,7 @@ static void usb_server_thread(void * ctx) { usb_native_stream_input.process_bytes( ODrive_interface.rx_buf, ODrive_interface.rx_len, nullptr); #endif - USBD_CDC_ReceivePacket(&hUsbDeviceFS, ODrive_interface.out_ep); // Allow next packet + USBD_CDC_ReceivePacket(&usb_dev_handle, ODrive_interface.out_ep); // Allow next packet } } } diff --git a/Firmware/Board/v3/Inc/freertos_vars.h b/Firmware/freertos_vars.h similarity index 100% rename from Firmware/Board/v3/Inc/freertos_vars.h rename to Firmware/freertos_vars.h diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 6447f44e..ef43a496 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -52,14 +52,12 @@ interfaces: uptime: readonly uint32 min_heap_space: readonly uint32 min_stack_space_axis: readonly uint32 - min_stack_space_comms: readonly uint32 min_stack_space_usb: readonly uint32 min_stack_space_uart: readonly uint32 min_stack_space_can: readonly uint32 min_stack_space_usb_irq: readonly uint32 min_stack_space_startup: readonly uint32 stack_usage_axis: readonly uint32 - stack_usage_comms: readonly uint32 stack_usage_usb: readonly uint32 stack_usage_uart: readonly uint32 stack_usage_usb_irq: readonly uint32 @@ -101,7 +99,9 @@ interfaces: enable_uart0: type: bool - doc: 'TODO: changing this currently requires a reboot - fix this' + doc: Enables/disables UART0. You also need to set the corresponding GPIOs to GPIO_MODE_UART0. Changing this requires a reboot. + enable_uart1: {type: bool, doc: Not supported on ODrive v3.x.} + enable_uart2: {type: bool, doc: Not supported on ODrive v3.x.} uart0_baudrate: type: uint32 doc: | @@ -127,6 +127,8 @@ interfaces: For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the [STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf). + uart1_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} + uart2_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} enable_can0: type: bool doc: | @@ -219,6 +221,12 @@ interfaces: - A GPIO was set to a mode for which the corresponding feature was not enabled. Example: `GPIO_MODE_UART0` was used without enabling `config.enable_uart0`. + - A feature was enabled which is not supported on this hardware. + Example: `config.enable_uart2` set to true on ODrive v3.x. + - A GPIO was used as an interrupt input for two internal components + or two GPIOs that are mutually exclusive in their interrupt + capability were both used as interrupt input. + Example: `step_gpio_pin` of both axes were set to the same GPIO. axis0: {type: Axis, c_name: get_axis(0)} axis1: {type: Axis, c_name: get_axis(1)} @@ -886,11 +894,17 @@ valuetypes: The pin can be used for one or more of these functions: Sin/cos encoders, analog input, `get_adc_voltage`. Uart0: {doc: See `config.enable_uart0`.} - Pwm0: {doc: See `config.gpio0_pwm_mapping`.} + Uart1: {doc: This mode is not supported on ODrive v3.x.} + Uart2: {doc: This mode is not supported on ODrive v3.x.} Can0: {doc: See `config.enable_can0`.} I2c0: {doc: See `config.enable_i2c0`.} + Spi0: {doc: Note that the SPI pins on ODrive v3.x are hardwired so they + cannot be configured through software. Consequently, even though SPI0 + is exposed, this mode is of no use on ODrive v3.x.} + Pwm0: {doc: See `config.gpio0_pwm_mapping`.} Enc0: {doc: The pin is used by quadrature encoder 0.} Enc1: {doc: The pin is used by quadrature encoder 1.} + Enc2: {doc: This mode is not supported on ODrive v3.x.} ODrive.Can.Protocol: values: {Simple: } diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 9545e91a..17409599 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -7,11 +7,15 @@ GPIO_MODE_DIGITAL = 0 GPIO_MODE_ANALOG_IN = 1 GPIO_MODE_UART0 = 2 -GPIO_MODE_PWM0 = 3 -GPIO_MODE_CAN0 = 4 -GPIO_MODE_I2C0 = 5 -GPIO_MODE_ENC0 = 6 -GPIO_MODE_ENC1 = 7 +GPIO_MODE_UART1 = 3 +GPIO_MODE_UART2 = 4 +GPIO_MODE_CAN0 = 5 +GPIO_MODE_I2C0 = 6 +GPIO_MODE_SPI0 = 7 +GPIO_MODE_PWM0 = 8 +GPIO_MODE_ENC0 = 9 +GPIO_MODE_ENC1 = 10 +GPIO_MODE_ENC2 = 11 # ODrive.Can.Protocol PROTOCOL_SIMPLE = 0