This commit is contained in:
2025-05-13 01:34:53 +03:00
parent 427735e23d
commit 83f3f1c7d4
945 changed files with 633484 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
---
BasedOnStyle: Google
AllowShortCaseLabelsOnASingleLine: 'true'
IndentWidth: '4'
ColumnLimit: '0'
...
+31
View File
@@ -0,0 +1,31 @@
#build folder
autogen/
build/
deploy/
.dep/
tup_build.sh
#markdown preview output
README.html
#autogenerated project files
.cproject
.mxproject
Odrive.xml
#Eclipse stuff
.settings/
.project
# VSCode stuff
/.vscode/.cortex-debug.*.state.json
# STM32CubeMX (in case you put it in this folder, or a symlink)
STM32CubeMX
#gdb log
openocd.log
# OS-specific files
.DS_Store
+112
View File
@@ -0,0 +1,112 @@
{
"configurations": [
{
"name": "ODrive v3.6 Windows",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F",
"${workspaceFolder}/fibre-cpp/include"
],
"compilerPath": "${ARM_GCC_ROOT}/bin/arm-none-eabi-g++.exe",
"intelliSenseMode": "gcc-arm",
"defines": [
"__arm__",
"STM32F405xx",
"FPU_FPV4",
"USE_HAL_DRIVER",
"HW_VERSION_MAJOR=3",
"HW_VERSION_MINOR=6",
"HW_VERSION_VOLTAGE=56",
"FIBRE_ENABLE_SERVER",
"FIBRE_ENABLE_CLIENT",
"__weak=\"__attribute__((weak))\"",
"__packed=\"__attribute__((__packed__))\"",
"__GNUC__"
],
"compilerArgs": [
"-mthumb",
"-mcpu=cortex-m4",
"-mfpu=fpv4-sp-d16",
"-mfloat-abi=hard",
"-specs=nosys.specs",
"-specs=nano.specs",
"-u _printf_float",
"-u _scanf_float"
],
"cStandard": "c11",
"cppStandard": "c++17"
},
{
"name": "ODrive 3.6 Linux",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F",
"${workspaceFolder}/fibre-cpp/include"
],
"compilerPath": "arm-none-eabi-g++",
"intelliSenseMode": "gcc-arm",
"defines": [
"__arm__",
"STM32F405xx",
"FPU_FPV4",
"USE_HAL_DRIVER",
"HW_VERSION_MAJOR=3",
"HW_VERSION_MINOR=6",
"HW_VERSION_VOLTAGE=56",
"FIBRE_ENABLE_SERVER",
"FIBRE_ENABLE_CLIENT",
"__weak=\"__attribute__((weak))\"",
"__packed=\"__attribute__((__packed__))\"",
"__GNUC__"
],
"compilerArgs": [
"-mthumb",
"-mcpu=cortex-m4",
"-mfpu=fpv4-sp-d16",
"-mfloat-abi=hard",
"-specs=nosys.specs",
"-specs=nano.specs",
"-u _printf_float",
"-u _scanf_float"
],
"cStandard": "c11",
"cppStandard": "c++17"
},
{
"name": "ODrive 3.6 Mac",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F",
"${workspaceFolder}/fibre-cpp/include"
],
"intelliSenseMode": "gcc-arm",
"defines": [
"__arm__",
"STM32F405xx",
"FPU_FPV4",
"USE_HAL_DRIVER",
"HW_VERSION_MAJOR=3",
"HW_VERSION_MINOR=6",
"HW_VERSION_VOLTAGE=56",
"FIBRE_ENABLE_SERVER",
"FIBRE_ENABLE_CLIENT",
"__weak=\"__attribute__((weak))\"",
"__packed=\"__attribute__((__packed__))\"",
"__GNUC__"
],
"compilerArgs": [
"-mthumb",
"-mcpu=cortex-m4",
"-mfpu=fpv4-sp-d16",
"-mfloat-abi=hard",
"-specs=nosys.specs",
"-specs=nano.specs",
"-u _printf_float",
"-u _scanf_float"
],
"cStandard": "c11",
"cppStandard": "c++17"
}
],
"version": 4
}
+106
View File
@@ -0,0 +1,106 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
// For the Cortex-Debug extension
"type": "cortex-debug",
"servertype": "openocd",
"request": "launch",
"name": "Debug ODrive v3.x - ST-Link",
"executable": "${workspaceRoot}/build/ODriveFirmware.elf",
"configFiles": [
"interface/stlink-v2.cfg",
"target/stm32f4x_stlink.cfg",
],
"svdFile": "${workspaceRoot}/Board/v3/STM32F40x.svd",
"cwd": "${workspaceRoot}"
},
{
// For the Cortex-Debug extension
"type": "cortex-debug",
"servertype": "openocd",
"request": "launch",
"name": "Debug ODrive v4.x - ST-Link",
"executable": "${workspaceRoot}/build/ODriveFirmware.elf",
"configFiles": [
"interface/stlink.cfg",
"target/stm32f7x.cfg",
],
"openOCDLaunchCommands": [
"reset_config none separate"
],
"svdFile": "${workspaceRoot}/Private/v4/STM32F722.svd",
"cwd": "${workspaceRoot}"
},
{
// For the Cortex-Debug extension
"type": "cortex-debug",
"servertype": "openocd",
"request": "launch",
"name": "Debug ODrive v3.x - ST-Link - FreeRTOS",
"executable": "${workspaceRoot}/build/ODriveFirmware.elf",
"rtos": "FreeRTOS",
"configFiles": [
"interface/stlink-v2.cfg",
"target/stm32f4x_stlink.cfg",
],
"svdFile": "${workspaceRoot}/Board/v3/STM32F40x.svd",
"cwd": "${workspaceRoot}"
},
{
// For the Cortex-Debug extension
// ssh -t odrv3 -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink-v2.cfg' '-f' 'target/stm32f4x_stlink.cfg'\""
"type": "cortex-debug",
"servertype": "external",
"gdbTarget": "localhost:3333",
"preLaunchCommands": [
"load"
],
"request": "launch",
"name": "Debug ODrive v3.x - Remote",
"executable": "${workspaceRoot}/build/ODriveFirmware.elf",
"configFiles": [
"interface/stlink-v2.cfg",
"target/stm32f4x_stlink.cfg",
],
"svdFile": "${workspaceRoot}/Board/v3/STM32F40x.svd",
"cwd": "${workspaceRoot}"
},
{
// For the Cortex-Debug extension
// ssh -t odrv4 -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink.cfg' '-f' 'target/stm32f7x.cfg' -c 'reset_config none separate'\""
"type": "cortex-debug",
"servertype": "external",
"gdbTarget": "localhost:3333",
"preLaunchCommands": [
"load"
],
"request": "launch",
"name": "Debug ODrive v4.x - Remote",
"executable": "${workspaceRoot}/build/ODriveFirmware.elf",
"configFiles": [
"interface/stlink.cfg",
"target/stm32f7x.cfg",
],
"svdFile": "${workspaceRoot}/Private/v4/STM32F722.svd",
"cwd": "${workspaceRoot}"
},
{
// For the Cortex-Debug extensions
"type": "cortex-debug",
"servertype": "bmp",
"request": "launch",
"name": "Debug ODrive v3.x - Black Magic Probe",
"executable": "${workspaceRoot}/build/ODriveFirmware.elf",
"device": "STM32F4xx",
"BMPGDBSerialPort": "${env:BMP_PORT}",
"interface": "swd",
"targetId": 1,
"armToolchainPath": "${env:ARM_GCC_ROOT}/bin/",
"cwd": "${workspaceRoot}"
}
]
}
+36
View File
@@ -0,0 +1,36 @@
{
"C_Cpp.intelliSenseEngine": "Default",
"C_Cpp.intelliSenseEngineFallback": "Disabled",
"files.associations": {
"memory": "cpp",
"utility": "cpp",
"deque": "cpp",
"vector": "cpp",
"array": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"exception": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"ostream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"algorithm": "cpp"
}
}
+40
View File
@@ -0,0 +1,40 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "make",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"panel": "new"
},
"problemMatcher": [
"$gcc"
]
},
{
"label": "flash - ST-Link",
"type": "shell",
"command": "make flash",
"problemMatcher": []
},
{
"label": "flash - Black Magic Probe",
"type": "shell",
"command": "make flashbmp",
"problemMatcher": []
},
{
"label": "openocd",
"type": "shell",
"command": "openocd -f \"interface/stlink-v2.cfg\" -f \"target/stm32f4x_stlink.cfg\" -c \"gdb_port 3333; log_output openocd.log\"",
"problemMatcher": []
}
]
}
@@ -0,0 +1,173 @@
/*
FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
All rights reserved
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
This file is part of the FreeRTOS distribution.
FreeRTOS is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License (version 2) as published by the
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
***************************************************************************
>>! NOTE: The modification to the GPL is included to allow you to !<<
>>! distribute a combined work that includes FreeRTOS without being !<<
>>! obliged to provide the source code for proprietary components !<<
>>! outside of the FreeRTOS kernel. !<<
***************************************************************************
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. Full license text is available on the following
link: http://www.freertos.org/a00114.html
***************************************************************************
* *
* FreeRTOS provides completely free yet professionally developed, *
* robust, strictly quality controlled, supported, and cross *
* platform software that is more than just the market leader, it *
* is the industry's de facto standard. *
* *
* Help yourself get started quickly while simultaneously helping *
* to support the FreeRTOS project by purchasing a FreeRTOS *
* tutorial book, reference manual, or both: *
* http://www.FreeRTOS.org/Documentation *
* *
***************************************************************************
http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
the FAQ page "My application does not run, what could be wrong?". Have you
defined configASSERT()?
http://www.FreeRTOS.org/support - In return for receiving this top quality
embedded software for free we request you assist our global community by
participating in the support forum.
http://www.FreeRTOS.org/training - Investing in training allows your team to
be as productive as possible as early as possible. Now you can receive
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
Ltd, and the world's leading authority on the world's leading RTOS.
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.
1 tab == 4 spaces!
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html.
*----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* Section where include file can be added */
/* USER CODE END Includes */
/* Ensure stdint is only used by the compiler, and not the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
#define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ((TickType_t)1000)
#define configMAX_PRIORITIES ( 7 )
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)65536)
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configCHECK_FOR_STACK_OVERFLOW 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
/* USER CODE BEGIN 1 */
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
/* USER CODE END 1 */
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
/* IMPORTANT: This define MUST be commented when used with STM32Cube firmware,
to prevent overwriting SysTick_Handler defined within STM32Cube HAL */
/* #define xPortSysTickHandler SysTick_Handler */
/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
#define configAPPLICATION_ALLOCATED_HEAP 1 // ucHeap allocated in freertos.c
/* USER CODE END Defines */
#endif /* FREERTOS_CONFIG_H */
@@ -0,0 +1,95 @@
/**
******************************************************************************
* File Name : ADC.h
* Description : This file provides code for the configuration
* of the ADC instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __adc_H
#define __adc_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern ADC_HandleTypeDef hadc1;
extern ADC_HandleTypeDef hadc2;
extern ADC_HandleTypeDef hadc3;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
extern void _Error_Handler(char *, int);
void MX_ADC1_Init(void);
void MX_ADC2_Init(void);
void MX_ADC3_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ adc_H */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,143 @@
/*
* @brief Contains board specific configuration for ODrive v3.x
*/
#ifndef __BOARD_CONFIG_H
#define __BOARD_CONFIG_H
#include <stdbool.h>
// STM specific includes
#include <stm32f4xx_hal.h>
#include <gpio.h>
#include <spi.h>
#include <tim.h>
#include <can.h>
#include <i2c.h>
#include <usb_device.h>
#include <main.h>
#include "cmsis_os.h"
#include <arm_math.h>
#include <Drivers/STM32/stm32_system.h>
#if HW_VERSION_MINOR <= 3
#define SHUNT_RESISTANCE (675e-6f)
#else
#define SHUNT_RESISTANCE (500e-6f)
#endif
#define AXIS_COUNT (2)
// Total count of GPIOs, including encoder pins, CAN pins and a dummy GPIO0.
// ODrive v3.4 and earlier don't have GPIOs 6, 7 and 8 but to keep the numbering
// consistent we just leave a gap in the counting scheme.
#define GPIO_COUNT (17)
#define CAN_FREQ (2000000UL)
#if HW_VERSION_MINOR >= 5 && HW_VERSION_VOLTAGE >= 48
#define DEFAULT_BRAKE_RESISTANCE (2.0f) // [ohm]
#else
#define DEFAULT_BRAKE_RESISTANCE (0.47f) // [ohm]
#endif
#define DEFAULT_ERROR_PIN 0
#define DEFAULT_MIN_DC_VOLTAGE 8.0f
#define DEFAULT_GPIO_MODES \
ODriveIntf::GPIO_MODE_DIGITAL, \
ODriveIntf::GPIO_MODE_UART_A, \
ODriveIntf::GPIO_MODE_UART_A, \
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_PULL_DOWN, \
ODriveIntf::GPIO_MODE_ENC1, \
ODriveIntf::GPIO_MODE_ENC1, \
ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN, \
ODriveIntf::GPIO_MODE_CAN_A, \
ODriveIntf::GPIO_MODE_CAN_A,
#define TIM_TIME_BASE TIM14
// Run control loop at the same frequency as the current measurements.
#define CONTROL_TIMER_PERIOD_TICKS (2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1))
#define TIM1_INIT_COUNT (TIM_1_8_PERIOD_CLOCKS / 2 - 1 * 128) // TODO: explain why this offset
// The delta from the control loop timestamp to the current sense timestamp is
// exactly 0 for M0 and TIM1_INIT_COUNT for M1.
#define MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA (TIM_1_8_PERIOD_CLOCKS / 2 + 1 * 128)
#ifdef __cplusplus
#include <Drivers/DRV8301/drv8301.hpp>
#include <Drivers/STM32/stm32_gpio.hpp>
#include <Drivers/STM32/stm32_spi_arbiter.hpp>
#include <MotorControl/pwm_input.hpp>
#include <MotorControl/thermistor.hpp>
using TGateDriver = Drv8301;
using TOpAmp = Drv8301;
#include <MotorControl/motor.hpp>
#include <MotorControl/encoder.hpp>
extern std::array<Axis, AXIS_COUNT> axes;
extern Motor motors[AXIS_COUNT];
extern OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT];
extern Encoder encoders[AXIS_COUNT];
extern Stm32Gpio gpios[GPIO_COUNT];
struct GpioFunction { int mode = 0; uint8_t alternate_function = 0xff; };
extern std::array<GpioFunction, 3> alternate_functions[GPIO_COUNT];
extern USBD_HandleTypeDef& usb_dev_handle;
extern Stm32SpiArbiter& ext_spi_arbiter;
extern UART_HandleTypeDef* uart_a;
extern UART_HandleTypeDef* uart_b;
extern UART_HandleTypeDef* uart_c;
extern PwmInput pwm0_input;
#endif
// Period in [s]
#define CURRENT_MEAS_PERIOD ( (float)2*TIM_1_8_PERIOD_CLOCKS*(TIM_1_8_RCR+1) / (float)TIM_1_8_CLOCK_HZ )
static const float current_meas_period = CURRENT_MEAS_PERIOD;
// Frequency in [Hz]
#define CURRENT_MEAS_HZ ( (float)(TIM_1_8_CLOCK_HZ) / (float)(2*TIM_1_8_PERIOD_CLOCKS*(TIM_1_8_RCR+1)) )
static const int current_meas_hz = CURRENT_MEAS_HZ;
#if HW_VERSION_VOLTAGE >= 48
#define VBUS_S_DIVIDER_RATIO 19.0f
#elif HW_VERSION_VOLTAGE == 24
#define VBUS_S_DIVIDER_RATIO 11.0f
#else
#error "unknown board voltage"
#endif
// Linear range of the DRV8301 opamp output: 0.3V...5.7V. We set the upper limit
// to 3.0V so that it's symmetric around the center point of 1.65V.
#define CURRENT_SENSE_MIN_VOLT 0.3f
#define CURRENT_SENSE_MAX_VOLT 3.0f
// This board has no board-specific user configurations
static inline bool board_read_config() { return true; }
static inline bool board_write_config() { return true; }
static inline void board_clear_config() { }
static inline bool board_apply_config() { return true; }
void system_init();
bool board_init();
void start_timers();
#endif // __BOARD_CONFIG_H
@@ -0,0 +1,91 @@
/**
******************************************************************************
* File Name : CAN.h
* Description : This file provides code for the configuration
* of the CAN instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __can_H
#define __can_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern CAN_HandleTypeDef hcan1;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
extern void _Error_Handler(char *, int);
void MX_CAN1_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ can_H */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,88 @@
/**
******************************************************************************
* File Name : dma.h
* Description : This file contains all the function prototypes for
* the dma.c file
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __dma_H
#define __dma_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* DMA memory to memory transfer handles -------------------------------------*/
extern void _Error_Handler(char*, int);
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_DMA_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __dma_H */
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,87 @@
/**
******************************************************************************
* File Name : gpio.h
* Description : This file contains all the functions prototypes for
* the gpio
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __gpio_H
#define __gpio_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_GPIO_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ pinoutConfig_H */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,91 @@
/**
******************************************************************************
* File Name : I2C.h
* Description : This file provides code for the configuration
* of the I2C instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __i2c_H
#define __i2c_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern I2C_HandleTypeDef hi2c1;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
extern void _Error_Handler(char *, int);
void MX_I2C1_Init(uint8_t addr);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ i2c_H */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,181 @@
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H__
#define __MAIN_H__
/* Includes ------------------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stm32f4xx_hal.h"
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 1 \
|| HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 2
#include "prev_board_ver/main_V3_2.h"
#elif HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 3 \
|| HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 4
#include "prev_board_ver/main_V3_4.h"
#else
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
#define TIM_1_8_CLOCK_HZ 168000000
#define TIM_1_8_PERIOD_CLOCKS 3500
#define TIM_1_8_DEADTIME_CLOCKS 20
#define TIM_APB1_CLOCK_HZ 84000000
#define TIM_APB1_PERIOD_CLOCKS 4096
#define TIM_APB1_DEADTIME_CLOCKS 40
#define TIM_1_8_RCR 2
#define M0_nCS_Pin GPIO_PIN_13
#define M0_nCS_GPIO_Port GPIOC
#define M1_nCS_Pin GPIO_PIN_14
#define M1_nCS_GPIO_Port GPIOC
#define M1_ENC_Z_Pin GPIO_PIN_15
#define M1_ENC_Z_GPIO_Port GPIOC
#define M0_IB_Pin GPIO_PIN_0
#define M0_IB_GPIO_Port GPIOC
#define M0_IC_Pin GPIO_PIN_1
#define M0_IC_GPIO_Port GPIOC
#define M1_IC_Pin GPIO_PIN_2
#define M1_IC_GPIO_Port GPIOC
#define M1_IB_Pin GPIO_PIN_3
#define M1_IB_GPIO_Port GPIOC
#define GPIO_1_Pin GPIO_PIN_0
#define GPIO_1_GPIO_Port GPIOA
#define GPIO_2_Pin GPIO_PIN_1
#define GPIO_2_GPIO_Port GPIOA
#define GPIO_3_Pin GPIO_PIN_2
#define GPIO_3_GPIO_Port GPIOA
#define GPIO_4_Pin GPIO_PIN_3
#define GPIO_4_GPIO_Port GPIOA
#define M1_TEMP_Pin GPIO_PIN_4
#define M1_TEMP_GPIO_Port GPIOA
#define AUX_TEMP_Pin GPIO_PIN_5
#define AUX_TEMP_GPIO_Port GPIOA
#define VBUS_S_Pin GPIO_PIN_6
#define VBUS_S_GPIO_Port GPIOA
#define M1_AL_Pin GPIO_PIN_7
#define M1_AL_GPIO_Port GPIOA
#define GPIO_5_Pin GPIO_PIN_4
#define GPIO_5_GPIO_Port GPIOC
#define M0_TEMP_Pin GPIO_PIN_5
#define M0_TEMP_GPIO_Port GPIOC
#define M1_BL_Pin GPIO_PIN_0
#define M1_BL_GPIO_Port GPIOB
#define M1_CL_Pin GPIO_PIN_1
#define M1_CL_GPIO_Port GPIOB
#define GPIO_6_Pin GPIO_PIN_2
#define GPIO_6_GPIO_Port GPIOB
#define AUX_L_Pin GPIO_PIN_10
#define AUX_L_GPIO_Port GPIOB
#define AUX_H_Pin GPIO_PIN_11
#define AUX_H_GPIO_Port GPIOB
#define EN_GATE_Pin GPIO_PIN_12
#define EN_GATE_GPIO_Port GPIOB
#define M0_AL_Pin GPIO_PIN_13
#define M0_AL_GPIO_Port GPIOB
#define M0_BL_Pin GPIO_PIN_14
#define M0_BL_GPIO_Port GPIOB
#define M0_CL_Pin GPIO_PIN_15
#define M0_CL_GPIO_Port GPIOB
#define M1_AH_Pin GPIO_PIN_6
#define M1_AH_GPIO_Port GPIOC
#define M1_BH_Pin GPIO_PIN_7
#define M1_BH_GPIO_Port GPIOC
#define M1_CH_Pin GPIO_PIN_8
#define M1_CH_GPIO_Port GPIOC
#define M0_ENC_Z_Pin GPIO_PIN_9
#define M0_ENC_Z_GPIO_Port GPIOC
#define M0_AH_Pin GPIO_PIN_8
#define M0_AH_GPIO_Port GPIOA
#define M0_BH_Pin GPIO_PIN_9
#define M0_BH_GPIO_Port GPIOA
#define M0_CH_Pin GPIO_PIN_10
#define M0_CH_GPIO_Port GPIOA
#define GPIO_7_Pin GPIO_PIN_15
#define GPIO_7_GPIO_Port GPIOA
#define nFAULT_Pin GPIO_PIN_2
#define nFAULT_GPIO_Port GPIOD
#define GPIO_8_Pin GPIO_PIN_3
#define GPIO_8_GPIO_Port GPIOB
#define M0_ENC_A_Pin GPIO_PIN_4
#define M0_ENC_A_GPIO_Port GPIOB
#define M0_ENC_B_Pin GPIO_PIN_5
#define M0_ENC_B_GPIO_Port GPIOB
#define M1_ENC_A_Pin GPIO_PIN_6
#define M1_ENC_A_GPIO_Port GPIOB
#define M1_ENC_B_Pin GPIO_PIN_7
#define M1_ENC_B_GPIO_Port GPIOB
/* ########################## Assert Selection ############################## */
/**
* @brief Uncomment the line below to expanse the "assert_param" macro in the
* HAL drivers code
*/
/* #define USE_FULL_ASSERT 1U */
/* USER CODE BEGIN Private defines */
#endif
/* USER CODE END Private defines */
#ifdef __cplusplus
extern "C" {
#endif
void _Error_Handler(char *, int);
#define Error_Handler() _Error_Handler(__FILE__, __LINE__)
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,133 @@
/**
******************************************************************************
* File Name : mxconstants.h
* Description : This file contains the common defines of the application
******************************************************************************
*
* COPYRIGHT(c) 2016 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
#define M0_nCS_Pin GPIO_PIN_13
#define M0_nCS_GPIO_Port GPIOC
#define M1_nCS_Pin GPIO_PIN_14
#define M1_nCS_GPIO_Port GPIOC
#define M1_DC_CAL_Pin GPIO_PIN_15
#define M1_DC_CAL_GPIO_Port GPIOC
#define M0_IB_Pin GPIO_PIN_0
#define M0_IB_GPIO_Port GPIOC
#define M0_IC_Pin GPIO_PIN_1
#define M0_IC_GPIO_Port GPIOC
#define M1_IC_Pin GPIO_PIN_2
#define M1_IC_GPIO_Port GPIOC
#define M1_IB_Pin GPIO_PIN_3
#define M1_IB_GPIO_Port GPIOC
#define VBUS_S_Pin GPIO_PIN_0
#define VBUS_S_GPIO_Port GPIOA
#define M1_TEMP_Pin GPIO_PIN_1
#define M1_TEMP_GPIO_Port GPIOA
#define AUX_I_Pin GPIO_PIN_2
#define AUX_I_GPIO_Port GPIOA
#define GPIO_4_Pin GPIO_PIN_3
#define GPIO_4_GPIO_Port GPIOA
#define GPIO_3_Pin GPIO_PIN_4
#define GPIO_3_GPIO_Port GPIOA
#define GPIO_2_Pin GPIO_PIN_5
#define GPIO_2_GPIO_Port GPIOA
#define AUX_V_Pin GPIO_PIN_6
#define AUX_V_GPIO_Port GPIOA
#define M1_AL_Pin GPIO_PIN_7
#define M1_AL_GPIO_Port GPIOA
#define AUX_TEMP_Pin GPIO_PIN_4
#define AUX_TEMP_GPIO_Port GPIOC
#define M0_TEMP_Pin GPIO_PIN_5
#define M0_TEMP_GPIO_Port GPIOC
#define M1_BL_Pin GPIO_PIN_0
#define M1_BL_GPIO_Port GPIOB
#define M1_CL_Pin GPIO_PIN_1
#define M1_CL_GPIO_Port GPIOB
#define GPIO_1_Pin GPIO_PIN_2
#define GPIO_1_GPIO_Port GPIOB
#define AUX_L_Pin GPIO_PIN_10
#define AUX_L_GPIO_Port GPIOB
#define AUX_H_Pin GPIO_PIN_11
#define AUX_H_GPIO_Port GPIOB
#define EN_GATE_Pin GPIO_PIN_12
#define EN_GATE_GPIO_Port GPIOB
#define M0_AL_Pin GPIO_PIN_13
#define M0_AL_GPIO_Port GPIOB
#define M0_BL_Pin GPIO_PIN_14
#define M0_BL_GPIO_Port GPIOB
#define M0_CL_Pin GPIO_PIN_15
#define M0_CL_GPIO_Port GPIOB
#define M1_AH_Pin GPIO_PIN_6
#define M1_AH_GPIO_Port GPIOC
#define M1_BH_Pin GPIO_PIN_7
#define M1_BH_GPIO_Port GPIOC
#define M1_CH_Pin GPIO_PIN_8
#define M1_CH_GPIO_Port GPIOC
#define M0_DC_CAL_Pin GPIO_PIN_9
#define M0_DC_CAL_GPIO_Port GPIOC
#define M0_AH_Pin GPIO_PIN_8
#define M0_AH_GPIO_Port GPIOA
#define M0_BH_Pin GPIO_PIN_9
#define M0_BH_GPIO_Port GPIOA
#define M0_CH_Pin GPIO_PIN_10
#define M0_CH_GPIO_Port GPIOA
#define M0_ENC_Z_Pin GPIO_PIN_15
#define M0_ENC_Z_GPIO_Port GPIOA
#define nFAULT_Pin GPIO_PIN_2
#define nFAULT_GPIO_Port GPIOD
#define M1_ENC_Z_Pin GPIO_PIN_3
#define M1_ENC_Z_GPIO_Port GPIOB
#define M0_ENC_A_Pin GPIO_PIN_4
#define M0_ENC_A_GPIO_Port GPIOB
#define M0_ENC_B_Pin GPIO_PIN_5
#define M0_ENC_B_GPIO_Port GPIOB
#define M1_ENC_A_Pin GPIO_PIN_6
#define M1_ENC_A_GPIO_Port GPIOB
#define M1_ENC_B_Pin GPIO_PIN_7
#define M1_ENC_B_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,92 @@
/* Private define ------------------------------------------------------------*/
#define TIM_1_8_CLOCK_HZ 168000000
#define TIM_1_8_PERIOD_CLOCKS 3500
#define TIM_1_8_DEADTIME_CLOCKS 20
#define TIM_APB1_CLOCK_HZ 84000000
#define TIM_APB1_PERIOD_CLOCKS 4096
#define TIM_APB1_DEADTIME_CLOCKS 40
#define TIM_1_8_RCR 2
#define M0_nCS_Pin GPIO_PIN_13
#define M0_nCS_GPIO_Port GPIOC
#define M1_nCS_Pin GPIO_PIN_14
#define M1_nCS_GPIO_Port GPIOC
#define M1_DC_CAL_Pin GPIO_PIN_15
#define M1_DC_CAL_GPIO_Port GPIOC
#define M0_IB_Pin GPIO_PIN_0
#define M0_IB_GPIO_Port GPIOC
#define M0_IC_Pin GPIO_PIN_1
#define M0_IC_GPIO_Port GPIOC
#define M1_IC_Pin GPIO_PIN_2
#define M1_IC_GPIO_Port GPIOC
#define M1_IB_Pin GPIO_PIN_3
#define M1_IB_GPIO_Port GPIOC
#define VBUS_S_Pin GPIO_PIN_0
#define VBUS_S_GPIO_Port GPIOA
#define M1_TEMP_Pin GPIO_PIN_1
#define M1_TEMP_GPIO_Port GPIOA
#define AUX_I_Pin GPIO_PIN_2
#define AUX_I_GPIO_Port GPIOA
#define GPIO_4_Pin GPIO_PIN_3
#define GPIO_4_GPIO_Port GPIOA
#define GPIO_3_Pin GPIO_PIN_4
#define GPIO_3_GPIO_Port GPIOA
#define GPIO_3_EXTI_IRQn EXTI4_IRQn
#define GPIO_2_Pin GPIO_PIN_5
#define GPIO_2_GPIO_Port GPIOA
#define AUX_V_Pin GPIO_PIN_6
#define AUX_V_GPIO_Port GPIOA
#define M1_AL_Pin GPIO_PIN_7
#define M1_AL_GPIO_Port GPIOA
#define AUX_TEMP_Pin GPIO_PIN_4
#define AUX_TEMP_GPIO_Port GPIOC
#define M0_TEMP_Pin GPIO_PIN_5
#define M0_TEMP_GPIO_Port GPIOC
#define M1_BL_Pin GPIO_PIN_0
#define M1_BL_GPIO_Port GPIOB
#define M1_CL_Pin GPIO_PIN_1
#define M1_CL_GPIO_Port GPIOB
#define GPIO_1_Pin GPIO_PIN_2
#define GPIO_1_GPIO_Port GPIOB
#define GPIO_1_EXTI_IRQn EXTI2_IRQn
#define AUX_L_Pin GPIO_PIN_10
#define AUX_L_GPIO_Port GPIOB
#define AUX_H_Pin GPIO_PIN_11
#define AUX_H_GPIO_Port GPIOB
#define EN_GATE_Pin GPIO_PIN_12
#define EN_GATE_GPIO_Port GPIOB
#define M0_AL_Pin GPIO_PIN_13
#define M0_AL_GPIO_Port GPIOB
#define M0_BL_Pin GPIO_PIN_14
#define M0_BL_GPIO_Port GPIOB
#define M0_CL_Pin GPIO_PIN_15
#define M0_CL_GPIO_Port GPIOB
#define M1_AH_Pin GPIO_PIN_6
#define M1_AH_GPIO_Port GPIOC
#define M1_BH_Pin GPIO_PIN_7
#define M1_BH_GPIO_Port GPIOC
#define M1_CH_Pin GPIO_PIN_8
#define M1_CH_GPIO_Port GPIOC
#define M0_DC_CAL_Pin GPIO_PIN_9
#define M0_DC_CAL_GPIO_Port GPIOC
#define M0_AH_Pin GPIO_PIN_8
#define M0_AH_GPIO_Port GPIOA
#define M0_BH_Pin GPIO_PIN_9
#define M0_BH_GPIO_Port GPIOA
#define M0_CH_Pin GPIO_PIN_10
#define M0_CH_GPIO_Port GPIOA
#define M0_ENC_Z_Pin GPIO_PIN_15
#define M0_ENC_Z_GPIO_Port GPIOA
#define nFAULT_Pin GPIO_PIN_2
#define nFAULT_GPIO_Port GPIOD
#define M1_ENC_Z_Pin GPIO_PIN_3
#define M1_ENC_Z_GPIO_Port GPIOB
#define M0_ENC_A_Pin GPIO_PIN_4
#define M0_ENC_A_GPIO_Port GPIOB
#define M0_ENC_B_Pin GPIO_PIN_5
#define M0_ENC_B_GPIO_Port GPIOB
#define M1_ENC_A_Pin GPIO_PIN_6
#define M1_ENC_A_GPIO_Port GPIOB
#define M1_ENC_B_Pin GPIO_PIN_7
#define M1_ENC_B_GPIO_Port GPIOB
@@ -0,0 +1,91 @@
/* Private define ------------------------------------------------------------*/
#define TIM_1_8_CLOCK_HZ 168000000
#define TIM_1_8_PERIOD_CLOCKS 3500
#define TIM_1_8_DEADTIME_CLOCKS 20
#define TIM_APB1_CLOCK_HZ 84000000
#define TIM_APB1_PERIOD_CLOCKS 4096
#define TIM_APB1_DEADTIME_CLOCKS 40
#define TIM_1_8_RCR 2
#define M0_nCS_Pin GPIO_PIN_13
#define M0_nCS_GPIO_Port GPIOC
#define M1_nCS_Pin GPIO_PIN_14
#define M1_nCS_GPIO_Port GPIOC
#define M1_DC_CAL_Pin GPIO_PIN_15
#define M1_DC_CAL_GPIO_Port GPIOC
#define M0_IB_Pin GPIO_PIN_0
#define M0_IB_GPIO_Port GPIOC
#define M0_IC_Pin GPIO_PIN_1
#define M0_IC_GPIO_Port GPIOC
#define M1_IC_Pin GPIO_PIN_2
#define M1_IC_GPIO_Port GPIOC
#define M1_IB_Pin GPIO_PIN_3
#define M1_IB_GPIO_Port GPIOC
#define GPIO_1_Pin GPIO_PIN_0
#define GPIO_1_GPIO_Port GPIOA
#define GPIO_2_Pin GPIO_PIN_1
#define GPIO_2_GPIO_Port GPIOA
#define GPIO_3_Pin GPIO_PIN_2
#define GPIO_3_GPIO_Port GPIOA
#define GPIO_3_EXTI_IRQn EXTI2_IRQn
#define GPIO_4_Pin GPIO_PIN_3
#define GPIO_4_GPIO_Port GPIOA
#define M1_TEMP_Pin GPIO_PIN_4
#define M1_TEMP_GPIO_Port GPIOA
#define AUX_I_Pin GPIO_PIN_5
#define AUX_I_GPIO_Port GPIOA
#define VBUS_S_Pin GPIO_PIN_6
#define VBUS_S_GPIO_Port GPIOA
#define M1_AL_Pin GPIO_PIN_7
#define M1_AL_GPIO_Port GPIOA
#define AUX_TEMP_Pin GPIO_PIN_4
#define AUX_TEMP_GPIO_Port GPIOC
#define M0_TEMP_Pin GPIO_PIN_5
#define M0_TEMP_GPIO_Port GPIOC
#define M1_BL_Pin GPIO_PIN_0
#define M1_BL_GPIO_Port GPIOB
#define M1_CL_Pin GPIO_PIN_1
#define M1_CL_GPIO_Port GPIOB
#define GPIO_5_Pin GPIO_PIN_2
#define GPIO_5_GPIO_Port GPIOB
#define AUX_L_Pin GPIO_PIN_10
#define AUX_L_GPIO_Port GPIOB
#define AUX_H_Pin GPIO_PIN_11
#define AUX_H_GPIO_Port GPIOB
#define EN_GATE_Pin GPIO_PIN_12
#define EN_GATE_GPIO_Port GPIOB
#define M0_AL_Pin GPIO_PIN_13
#define M0_AL_GPIO_Port GPIOB
#define M0_BL_Pin GPIO_PIN_14
#define M0_BL_GPIO_Port GPIOB
#define M0_CL_Pin GPIO_PIN_15
#define M0_CL_GPIO_Port GPIOB
#define M1_AH_Pin GPIO_PIN_6
#define M1_AH_GPIO_Port GPIOC
#define M1_BH_Pin GPIO_PIN_7
#define M1_BH_GPIO_Port GPIOC
#define M1_CH_Pin GPIO_PIN_8
#define M1_CH_GPIO_Port GPIOC
#define M0_DC_CAL_Pin GPIO_PIN_9
#define M0_DC_CAL_GPIO_Port GPIOC
#define M0_AH_Pin GPIO_PIN_8
#define M0_AH_GPIO_Port GPIOA
#define M0_BH_Pin GPIO_PIN_9
#define M0_BH_GPIO_Port GPIOA
#define M0_CH_Pin GPIO_PIN_10
#define M0_CH_GPIO_Port GPIOA
#define M0_ENC_Z_Pin GPIO_PIN_15
#define M0_ENC_Z_GPIO_Port GPIOA
#define nFAULT_Pin GPIO_PIN_2
#define nFAULT_GPIO_Port GPIOD
#define M1_ENC_Z_Pin GPIO_PIN_3
#define M1_ENC_Z_GPIO_Port GPIOB
#define M0_ENC_A_Pin GPIO_PIN_4
#define M0_ENC_A_GPIO_Port GPIOB
#define M0_ENC_B_Pin GPIO_PIN_5
#define M0_ENC_B_GPIO_Port GPIOB
#define M1_ENC_A_Pin GPIO_PIN_6
#define M1_ENC_A_GPIO_Port GPIOB
#define M1_ENC_B_Pin GPIO_PIN_7
#define M1_ENC_B_GPIO_Port GPIOB
@@ -0,0 +1,91 @@
/**
******************************************************************************
* File Name : SPI.h
* Description : This file provides code for the configuration
* of the SPI instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __spi_H
#define __spi_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern SPI_HandleTypeDef hspi3;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
extern void _Error_Handler(char *, int);
void MX_SPI3_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ spi_H */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,452 @@
/**
******************************************************************************
* @file stm32f4xx_hal_conf.h
* @brief HAL configuration file.
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT(c) 2018 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4xx_HAL_CONF_H
#define __STM32F4xx_HAL_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* ########################## Module Selection ############################## */
/**
* @brief This is the list of modules to be used in the HAL driver
*/
#define HAL_MODULE_ENABLED
#define HAL_ADC_MODULE_ENABLED
/* #define HAL_CRYP_MODULE_ENABLED */
#define HAL_CAN_MODULE_ENABLED
/* #define HAL_CRC_MODULE_ENABLED */
/* #define HAL_CRYP_MODULE_ENABLED */
/* #define HAL_DAC_MODULE_ENABLED */
/* #define HAL_DCMI_MODULE_ENABLED */
/* #define HAL_DMA2D_MODULE_ENABLED */
/* #define HAL_ETH_MODULE_ENABLED */
/* #define HAL_NAND_MODULE_ENABLED */
/* #define HAL_NOR_MODULE_ENABLED */
/* #define HAL_PCCARD_MODULE_ENABLED */
/* #define HAL_SRAM_MODULE_ENABLED */
/* #define HAL_SDRAM_MODULE_ENABLED */
/* #define HAL_HASH_MODULE_ENABLED */
#define HAL_I2C_MODULE_ENABLED
/* #define HAL_I2S_MODULE_ENABLED */
/* #define HAL_IWDG_MODULE_ENABLED */
/* #define HAL_LTDC_MODULE_ENABLED */
/* #define HAL_RNG_MODULE_ENABLED */
/* #define HAL_RTC_MODULE_ENABLED */
/* #define HAL_SAI_MODULE_ENABLED */
/* #define HAL_SD_MODULE_ENABLED */
/* #define HAL_MMC_MODULE_ENABLED */
#define HAL_SPI_MODULE_ENABLED
#define HAL_TIM_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
/* #define HAL_USART_MODULE_ENABLED */
/* #define HAL_IRDA_MODULE_ENABLED */
/* #define HAL_SMARTCARD_MODULE_ENABLED */
/* #define HAL_WWDG_MODULE_ENABLED */
#define HAL_PCD_MODULE_ENABLED
/* #define HAL_HCD_MODULE_ENABLED */
/* #define HAL_DSI_MODULE_ENABLED */
/* #define HAL_QSPI_MODULE_ENABLED */
/* #define HAL_QSPI_MODULE_ENABLED */
/* #define HAL_CEC_MODULE_ENABLED */
/* #define HAL_FMPI2C_MODULE_ENABLED */
/* #define HAL_SPDIFRX_MODULE_ENABLED */
/* #define HAL_DFSDM_MODULE_ENABLED */
/* #define HAL_LPTIM_MODULE_ENABLED */
/* #define HAL_EXTI_MODULE_ENABLED */
#define HAL_GPIO_MODULE_ENABLED
#define HAL_DMA_MODULE_ENABLED
#define HAL_RCC_MODULE_ENABLED
#define HAL_FLASH_MODULE_ENABLED
#define HAL_PWR_MODULE_ENABLED
#define HAL_CORTEX_MODULE_ENABLED
/* ########################## HSE/HSI Values adaptation ##################### */
/**
* @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
* This value is used by the RCC HAL module to compute the system frequency
* (when HSE is used as system clock source, directly or through the PLL).
*/
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
#if !defined (HSE_STARTUP_TIMEOUT)
#define HSE_STARTUP_TIMEOUT ((uint32_t)100U) /*!< Time out for HSE start up, in ms */
#endif /* HSE_STARTUP_TIMEOUT */
/**
* @brief Internal High Speed oscillator (HSI) value.
* This value is used by the RCC HAL module to compute the system frequency
* (when HSI is used as system clock source, directly or through the PLL).
*/
#if !defined (HSI_VALUE)
#define HSI_VALUE ((uint32_t)16000000U) /*!< Value of the Internal oscillator in Hz*/
#endif /* HSI_VALUE */
/**
* @brief Internal Low Speed oscillator (LSI) value.
*/
#if !defined (LSI_VALUE)
#define LSI_VALUE ((uint32_t)32000U) /*!< LSI Typical Value in Hz*/
#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz
The real value may vary depending on the variations
in voltage and temperature.*/
/**
* @brief External Low Speed oscillator (LSE) value.
*/
#if !defined (LSE_VALUE)
#define LSE_VALUE ((uint32_t)32768U) /*!< Value of the External Low Speed oscillator in Hz */
#endif /* LSE_VALUE */
#if !defined (LSE_STARTUP_TIMEOUT)
#define LSE_STARTUP_TIMEOUT ((uint32_t)5000U) /*!< Time out for LSE start up, in ms */
#endif /* LSE_STARTUP_TIMEOUT */
/**
* @brief External clock source for I2S peripheral
* This value is used by the I2S HAL module to compute the I2S clock source
* frequency, this source is inserted directly through I2S_CKIN pad.
*/
#if !defined (EXTERNAL_CLOCK_VALUE)
#define EXTERNAL_CLOCK_VALUE ((uint32_t)12288000U) /*!< Value of the External audio frequency in Hz*/
#endif /* EXTERNAL_CLOCK_VALUE */
/* Tip: To avoid modifying this file each time you need to use different HSE,
=== you can define the HSE value in your toolchain compiler preprocessor. */
/* ########################### System Configuration ######################### */
/**
* @brief This is the HAL system configuration section
*/
#define VDD_VALUE ((uint32_t)3300U) /*!< Value of VDD in mv */
#define TICK_INT_PRIORITY ((uint32_t)6U) /*!< tick interrupt priority */
#define USE_RTOS 0U
#define PREFETCH_ENABLE 1U
#define INSTRUCTION_CACHE_ENABLE 1U
#define DATA_CACHE_ENABLE 1U
/* ########################## Assert Selection ############################## */
/**
* @brief Uncomment the line below to expanse the "assert_param" macro in the
* HAL drivers code
*/
/* #define USE_FULL_ASSERT 1U */
/* ################## Ethernet peripheral configuration ##################### */
/* Section 1 : Ethernet peripheral configuration */
/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */
#define MAC_ADDR0 2U
#define MAC_ADDR1 0U
#define MAC_ADDR2 0U
#define MAC_ADDR3 0U
#define MAC_ADDR4 0U
#define MAC_ADDR5 0U
/* Definition of the Ethernet driver buffers size and count */
#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */
#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */
#define ETH_RXBUFNB ((uint32_t)4U) /* 4 Rx buffers of size ETH_RX_BUF_SIZE */
#define ETH_TXBUFNB ((uint32_t)4U) /* 4 Tx buffers of size ETH_TX_BUF_SIZE */
/* Section 2: PHY configuration section */
/* DP83848_PHY_ADDRESS Address*/
#define DP83848_PHY_ADDRESS 0x01U
/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/
#define PHY_RESET_DELAY ((uint32_t)0x000000FFU)
/* PHY Configuration delay */
#define PHY_CONFIG_DELAY ((uint32_t)0x00000FFFU)
#define PHY_READ_TO ((uint32_t)0x0000FFFFU)
#define PHY_WRITE_TO ((uint32_t)0x0000FFFFU)
/* Section 3: Common PHY Registers */
#define PHY_BCR ((uint16_t)0x0000U) /*!< Transceiver Basic Control Register */
#define PHY_BSR ((uint16_t)0x0001U) /*!< Transceiver Basic Status Register */
#define PHY_RESET ((uint16_t)0x8000U) /*!< PHY Reset */
#define PHY_LOOPBACK ((uint16_t)0x4000U) /*!< Select loop-back mode */
#define PHY_FULLDUPLEX_100M ((uint16_t)0x2100U) /*!< Set the full-duplex mode at 100 Mb/s */
#define PHY_HALFDUPLEX_100M ((uint16_t)0x2000U) /*!< Set the half-duplex mode at 100 Mb/s */
#define PHY_FULLDUPLEX_10M ((uint16_t)0x0100U) /*!< Set the full-duplex mode at 10 Mb/s */
#define PHY_HALFDUPLEX_10M ((uint16_t)0x0000U) /*!< Set the half-duplex mode at 10 Mb/s */
#define PHY_AUTONEGOTIATION ((uint16_t)0x1000U) /*!< Enable auto-negotiation function */
#define PHY_RESTART_AUTONEGOTIATION ((uint16_t)0x0200U) /*!< Restart auto-negotiation function */
#define PHY_POWERDOWN ((uint16_t)0x0800U) /*!< Select the power down mode */
#define PHY_ISOLATE ((uint16_t)0x0400U) /*!< Isolate PHY from MII */
#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020U) /*!< Auto-Negotiation process completed */
#define PHY_LINKED_STATUS ((uint16_t)0x0004U) /*!< Valid link established */
#define PHY_JABBER_DETECTION ((uint16_t)0x0002U) /*!< Jabber condition detected */
/* Section 4: Extended PHY Registers */
#define PHY_SR ((uint16_t)0x10U) /*!< PHY status register Offset */
#define PHY_SPEED_STATUS ((uint16_t)0x0002U) /*!< PHY Speed mask */
#define PHY_DUPLEX_STATUS ((uint16_t)0x0004U) /*!< PHY Duplex mask */
/* ################## SPI peripheral configuration ########################## */
/* CRC FEATURE: Use to activate CRC feature inside HAL SPI Driver
* Activated: CRC code is present inside driver
* Deactivated: CRC code cleaned from driver
*/
#define USE_SPI_CRC 0U
/* Includes ------------------------------------------------------------------*/
/**
* @brief Include module's header file
*/
#ifdef HAL_RCC_MODULE_ENABLED
#include "stm32f4xx_hal_rcc.h"
#endif /* HAL_RCC_MODULE_ENABLED */
#ifdef HAL_EXTI_MODULE_ENABLED
#include "stm32f4xx_hal_exti.h"
#endif /* HAL_EXTI_MODULE_ENABLED */
#ifdef HAL_GPIO_MODULE_ENABLED
#include "stm32f4xx_hal_gpio.h"
#endif /* HAL_GPIO_MODULE_ENABLED */
#ifdef HAL_DMA_MODULE_ENABLED
#include "stm32f4xx_hal_dma.h"
#endif /* HAL_DMA_MODULE_ENABLED */
#ifdef HAL_CORTEX_MODULE_ENABLED
#include "stm32f4xx_hal_cortex.h"
#endif /* HAL_CORTEX_MODULE_ENABLED */
#ifdef HAL_ADC_MODULE_ENABLED
#include "stm32f4xx_hal_adc.h"
#endif /* HAL_ADC_MODULE_ENABLED */
#ifdef HAL_CAN_MODULE_ENABLED
#include "stm32f4xx_hal_can.h"
#endif /* HAL_CAN_MODULE_ENABLED */
#ifdef HAL_CRC_MODULE_ENABLED
#include "stm32f4xx_hal_crc.h"
#endif /* HAL_CRC_MODULE_ENABLED */
#ifdef HAL_CRYP_MODULE_ENABLED
#include "stm32f4xx_hal_cryp.h"
#endif /* HAL_CRYP_MODULE_ENABLED */
#ifdef HAL_DMA2D_MODULE_ENABLED
#include "stm32f4xx_hal_dma2d.h"
#endif /* HAL_DMA2D_MODULE_ENABLED */
#ifdef HAL_DAC_MODULE_ENABLED
#include "stm32f4xx_hal_dac.h"
#endif /* HAL_DAC_MODULE_ENABLED */
#ifdef HAL_DCMI_MODULE_ENABLED
#include "stm32f4xx_hal_dcmi.h"
#endif /* HAL_DCMI_MODULE_ENABLED */
#ifdef HAL_ETH_MODULE_ENABLED
#include "stm32f4xx_hal_eth.h"
#endif /* HAL_ETH_MODULE_ENABLED */
#ifdef HAL_FLASH_MODULE_ENABLED
#include "stm32f4xx_hal_flash.h"
#endif /* HAL_FLASH_MODULE_ENABLED */
#ifdef HAL_SRAM_MODULE_ENABLED
#include "stm32f4xx_hal_sram.h"
#endif /* HAL_SRAM_MODULE_ENABLED */
#ifdef HAL_NOR_MODULE_ENABLED
#include "stm32f4xx_hal_nor.h"
#endif /* HAL_NOR_MODULE_ENABLED */
#ifdef HAL_NAND_MODULE_ENABLED
#include "stm32f4xx_hal_nand.h"
#endif /* HAL_NAND_MODULE_ENABLED */
#ifdef HAL_PCCARD_MODULE_ENABLED
#include "stm32f4xx_hal_pccard.h"
#endif /* HAL_PCCARD_MODULE_ENABLED */
#ifdef HAL_SDRAM_MODULE_ENABLED
#include "stm32f4xx_hal_sdram.h"
#endif /* HAL_SDRAM_MODULE_ENABLED */
#ifdef HAL_HASH_MODULE_ENABLED
#include "stm32f4xx_hal_hash.h"
#endif /* HAL_HASH_MODULE_ENABLED */
#ifdef HAL_I2C_MODULE_ENABLED
#include "stm32f4xx_hal_i2c.h"
#endif /* HAL_I2C_MODULE_ENABLED */
#ifdef HAL_I2S_MODULE_ENABLED
#include "stm32f4xx_hal_i2s.h"
#endif /* HAL_I2S_MODULE_ENABLED */
#ifdef HAL_IWDG_MODULE_ENABLED
#include "stm32f4xx_hal_iwdg.h"
#endif /* HAL_IWDG_MODULE_ENABLED */
#ifdef HAL_LTDC_MODULE_ENABLED
#include "stm32f4xx_hal_ltdc.h"
#endif /* HAL_LTDC_MODULE_ENABLED */
#ifdef HAL_PWR_MODULE_ENABLED
#include "stm32f4xx_hal_pwr.h"
#endif /* HAL_PWR_MODULE_ENABLED */
#ifdef HAL_RNG_MODULE_ENABLED
#include "stm32f4xx_hal_rng.h"
#endif /* HAL_RNG_MODULE_ENABLED */
#ifdef HAL_RTC_MODULE_ENABLED
#include "stm32f4xx_hal_rtc.h"
#endif /* HAL_RTC_MODULE_ENABLED */
#ifdef HAL_SAI_MODULE_ENABLED
#include "stm32f4xx_hal_sai.h"
#endif /* HAL_SAI_MODULE_ENABLED */
#ifdef HAL_SD_MODULE_ENABLED
#include "stm32f4xx_hal_sd.h"
#endif /* HAL_SD_MODULE_ENABLED */
#ifdef HAL_MMC_MODULE_ENABLED
#include "stm32f4xx_hal_mmc.h"
#endif /* HAL_MMC_MODULE_ENABLED */
#ifdef HAL_SPI_MODULE_ENABLED
#include "stm32f4xx_hal_spi.h"
#endif /* HAL_SPI_MODULE_ENABLED */
#ifdef HAL_TIM_MODULE_ENABLED
#include "stm32f4xx_hal_tim.h"
#endif /* HAL_TIM_MODULE_ENABLED */
#ifdef HAL_UART_MODULE_ENABLED
#include "stm32f4xx_hal_uart.h"
#endif /* HAL_UART_MODULE_ENABLED */
#ifdef HAL_USART_MODULE_ENABLED
#include "stm32f4xx_hal_usart.h"
#endif /* HAL_USART_MODULE_ENABLED */
#ifdef HAL_IRDA_MODULE_ENABLED
#include "stm32f4xx_hal_irda.h"
#endif /* HAL_IRDA_MODULE_ENABLED */
#ifdef HAL_SMARTCARD_MODULE_ENABLED
#include "stm32f4xx_hal_smartcard.h"
#endif /* HAL_SMARTCARD_MODULE_ENABLED */
#ifdef HAL_WWDG_MODULE_ENABLED
#include "stm32f4xx_hal_wwdg.h"
#endif /* HAL_WWDG_MODULE_ENABLED */
#ifdef HAL_PCD_MODULE_ENABLED
#include "stm32f4xx_hal_pcd.h"
#endif /* HAL_PCD_MODULE_ENABLED */
#ifdef HAL_HCD_MODULE_ENABLED
#include "stm32f4xx_hal_hcd.h"
#endif /* HAL_HCD_MODULE_ENABLED */
#ifdef HAL_DSI_MODULE_ENABLED
#include "stm32f4xx_hal_dsi.h"
#endif /* HAL_DSI_MODULE_ENABLED */
#ifdef HAL_QSPI_MODULE_ENABLED
#include "stm32f4xx_hal_qspi.h"
#endif /* HAL_QSPI_MODULE_ENABLED */
#ifdef HAL_CEC_MODULE_ENABLED
#include "stm32f4xx_hal_cec.h"
#endif /* HAL_CEC_MODULE_ENABLED */
#ifdef HAL_FMPI2C_MODULE_ENABLED
#include "stm32f4xx_hal_fmpi2c.h"
#endif /* HAL_FMPI2C_MODULE_ENABLED */
#ifdef HAL_SPDIFRX_MODULE_ENABLED
#include "stm32f4xx_hal_spdifrx.h"
#endif /* HAL_SPDIFRX_MODULE_ENABLED */
#ifdef HAL_DFSDM_MODULE_ENABLED
#include "stm32f4xx_hal_dfsdm.h"
#endif /* HAL_DFSDM_MODULE_ENABLED */
#ifdef HAL_LPTIM_MODULE_ENABLED
#include "stm32f4xx_hal_lptim.h"
#endif /* HAL_LPTIM_MODULE_ENABLED */
/* Exported macro ------------------------------------------------------------*/
#ifdef USE_FULL_ASSERT
/**
* @brief The assert_param macro is used for function's parameters check.
* @param expr: If expr is false, it calls assert_failed function
* which reports the name of the source file and the source
* line number of the call that failed.
* If expr is true, it returns no value.
* @retval None
*/
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_HAL_CONF_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,81 @@
/**
******************************************************************************
* @file stm32f4xx_it.h
* @brief This file contains the headers of the interrupt handlers.
******************************************************************************
*
* COPYRIGHT(c) 2018 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4xx_IT_H
#define __STM32F4xx_IT_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void DebugMon_Handler(void);
void SysTick_Handler(void);
void DMA1_Stream0_IRQHandler(void);
void DMA1_Stream2_IRQHandler(void);
void DMA1_Stream4_IRQHandler(void);
void DMA1_Stream5_IRQHandler(void);
void DMA1_Stream6_IRQHandler(void);
void DMA1_Stream7_IRQHandler(void);
//void ADC_IRQHandler(void);
void CAN1_TX_IRQHandler(void);
void CAN1_RX0_IRQHandler(void);
void CAN1_RX1_IRQHandler(void);
void CAN1_SCE_IRQHandler(void);
void USART2_IRQHandler(void);
void TIM8_TRG_COM_TIM14_IRQHandler(void);
void TIM5_IRQHandler(void);
void SPI3_IRQHandler(void);
void UART4_IRQHandler(void);
void OTG_FS_IRQHandler(void);
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_IT_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,105 @@
/**
******************************************************************************
* File Name : TIM.h
* Description : This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __tim_H
#define __tim_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern TIM_HandleTypeDef htim1;
extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim3;
extern TIM_HandleTypeDef htim4;
extern TIM_HandleTypeDef htim5;
extern TIM_HandleTypeDef htim8;
extern TIM_HandleTypeDef htim13;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
extern void _Error_Handler(char *, int);
void MX_TIM1_Init(void);
void MX_TIM2_Init(void);
void MX_TIM3_Init(void);
void MX_TIM4_Init(void);
void MX_TIM5_Init(void);
void MX_TIM8_Init(void);
void MX_TIM13_Init(void);
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ tim_H */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,93 @@
/**
******************************************************************************
* File Name : USART.h
* Description : This file provides code for the configuration
* of the USART instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __usart_H
#define __usart_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart2;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
extern void _Error_Handler(char *, int);
void MX_UART4_Init(void);
void MX_USART2_UART_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ usart_H */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,114 @@
/**
******************************************************************************
* @file : usb_device.h
* @version : v1.0_Cube
* @brief : Header for usb_device.c file.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USB_DEVICE__H__
#define __USB_DEVICE__H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include "stm32f4xx_hal.h"
#include "usbd_def.h"
/* USER CODE BEGIN INCLUDE */
/* USER CODE END INCLUDE */
/** @addtogroup USBD_OTG_DRIVER
* @{
*/
/** @defgroup USBD_DEVICE USBD_DEVICE
* @brief Device file for Usb otg low level driver.
* @{
*/
/** @defgroup USBD_DEVICE_Exported_Variables USBD_DEVICE_Exported_Variables
* @brief Public variables.
* @{
*/
/** USB device core handle. */
extern USBD_HandleTypeDef hUsbDeviceFS;
/**
* @}
*/
/** @defgroup USBD_DEVICE_Exported_FunctionsPrototype USBD_DEVICE_Exported_FunctionsPrototype
* @brief Declaration of public functions for Usb device.
* @{
*/
/** USB Device initialization function. */
void MX_USB_DEVICE_Init(void);
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __USB_DEVICE__H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,158 @@
/**
******************************************************************************
* @file : usbd_cdc_if.h
* @version : v1.0_Cube
* @brief : Header for usbd_cdc_if.c file.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_CDC_IF_H__
#define __USBD_CDC_IF_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "usbd_cdc.h"
/* USER CODE BEGIN INCLUDE */
/* USER CODE END INCLUDE */
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
* @brief For Usb device.
* @{
*/
/** @defgroup USBD_CDC_IF USBD_CDC_IF
* @brief Usb VCP device module
* @{
*/
/** @defgroup USBD_CDC_IF_Exported_Defines USBD_CDC_IF_Exported_Defines
* @brief Defines.
* @{
*/
/* USER CODE BEGIN EXPORTED_DEFINES */
/* Define size for the receive and transmit buffer over CDC */
/* It's up to user to redefine and/or remove those define */
#define USB_RX_DATA_SIZE 64
#define USB_TX_DATA_SIZE 64
#define APP_RX_DATA_SIZE USB_RX_DATA_SIZE
#define APP_TX_DATA_SIZE USB_TX_DATA_SIZE
/* USER CODE END EXPORTED_DEFINES */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Exported_Types USBD_CDC_IF_Exported_Types
* @brief Types.
* @{
*/
/* USER CODE BEGIN EXPORTED_TYPES */
/* USER CODE END EXPORTED_TYPES */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Exported_Macros USBD_CDC_IF_Exported_Macros
* @brief Aliases.
* @{
*/
/* USER CODE BEGIN EXPORTED_MACRO */
/* USER CODE END EXPORTED_MACRO */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Exported_Variables USBD_CDC_IF_Exported_Variables
* @brief Public variables.
* @{
*/
/** CDC Interface callback. */
extern USBD_CDC_ItfTypeDef USBD_Interface_fops_FS;
/* USER CODE BEGIN EXPORTED_VARIABLES */
/* USER CODE END EXPORTED_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Exported_FunctionsPrototype USBD_CDC_IF_Exported_FunctionsPrototype
* @brief Public functions declaration.
* @{
*/
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 */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __USBD_CDC_IF_H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,204 @@
/**
******************************************************************************
* @file : usbd_conf.h
* @version : v1.0_Cube
* @brief : Header for usbd_conf.c file.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_CONF__H__
#define __USBD_CONF__H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stm32f4xx.h"
#include "stm32f4xx_hal.h"
/* USER CODE BEGIN INCLUDE */
/* USER CODE END INCLUDE */
/** @addtogroup USBD_OTG_DRIVER
* @brief Driver for Usb device.
* @{
*/
/** @defgroup USBD_CONF USBD_CONF
* @brief Configuration file for Usb otg low level driver.
* @{
*/
/** @defgroup USBD_CONF_Exported_Variables USBD_CONF_Exported_Variables
* @brief Public variables.
* @{
*/
/**
* @}
*/
/** @defgroup USBD_CONF_Exported_Defines USBD_CONF_Exported_Defines
* @brief Defines for configuration of the Usb device.
* @{
*/
#define MS_VendorCode 'P'
/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES 1
/*---------- -----------*/
#define USBD_MAX_NUM_CONFIGURATION 1
/*---------- -----------*/
#define USBD_MAX_STR_DESC_SIZ 512
/*---------- -----------*/
#define USBD_SUPPORT_USER_STRING_DESC 1
/*---------- -----------*/
#define USBD_DEBUG_LEVEL 0
/*---------- -----------*/
#define USBD_LPM_ENABLED 0
/*---------- -----------*/
#define USBD_SELF_POWERED 1
/****************************************/
/* #define for FS and HS identification */
#define DEVICE_FS 0
#define DEVICE_HS 1
/**
* @}
*/
/** @defgroup USBD_CONF_Exported_Macros USBD_CONF_Exported_Macros
* @brief Aliases.
* @{
*/
/* Memory management macros */
/** Alias for memory allocation. */
#define USBD_malloc malloc
/** Alias for memory release. */
#define USBD_free free
/** Alias for memory set. */
#define USBD_memset memset
/** Alias for memory copy. */
#define USBD_memcpy memcpy
/** Alias for delay. */
#define USBD_Delay HAL_Delay
/* DEBUG macros */
#if (USBD_DEBUG_LEVEL > 0)
#define USBD_UsrLog(...) printf(__VA_ARGS__);\
printf("\n");
#else
#define USBD_UsrLog(...)
#endif
#if (USBD_DEBUG_LEVEL > 1)
#define USBD_ErrLog(...) printf("ERROR: ") ;\
printf(__VA_ARGS__);\
printf("\n");
#else
#define USBD_ErrLog(...)
#endif
#if (USBD_DEBUG_LEVEL > 2)
#define USBD_DbgLog(...) printf("DEBUG : ") ;\
printf(__VA_ARGS__);\
printf("\n");
#else
#define USBD_DbgLog(...)
#endif
/**
* @}
*/
/** @defgroup USBD_CONF_Exported_Types USBD_CONF_Exported_Types
* @brief Types.
* @{
*/
/**
* @}
*/
/** @defgroup USBD_CONF_Exported_FunctionsPrototype USBD_CONF_Exported_FunctionsPrototype
* @brief Declaration of public functions for Usb device.
* @{
*/
/* Exported functions -------------------------------------------------------*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __USBD_CONF__H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,158 @@
/**
******************************************************************************
* @file : usbd_desc.h
* @version : v1.0_Cube
* @brief : Header for usbd_desc.c file.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USBD_DESC__H__
#define __USBD_DESC__H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "usbd_def.h"
/* USER CODE BEGIN INCLUDE */
/* USER CODE END INCLUDE */
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
* @{
*/
/** @defgroup USBD_DESC USBD_DESC
* @brief Usb device descriptors module.
* @{
*/
/** @defgroup USBD_DESC_Exported_Defines USBD_DESC_Exported_Defines
* @brief Defines.
* @{
*/
/* USER CODE BEGIN EXPORTED_DEFINES */
/* USER CODE END EXPORTED_DEFINES */
/**
* @}
*/
/** @defgroup USBD_DESC_Exported_TypesDefinitions USBD_DESC_Exported_TypesDefinitions
* @brief Types.
* @{
*/
/* USER CODE BEGIN EXPORTED_TYPES */
/* USER CODE END EXPORTED_TYPES */
/**
* @}
*/
/** @defgroup USBD_DESC_Exported_Macros USBD_DESC_Exported_Macros
* @brief Aliases.
* @{
*/
/* USER CODE BEGIN EXPORTED_MACRO */
/* USER CODE END EXPORTED_MACRO */
/**
* @}
*/
/** @defgroup USBD_DESC_Exported_Variables USBD_DESC_Exported_Variables
* @brief Public variables.
* @{
*/
/** Descriptor for the Usb device. */
extern USBD_DescriptorsTypeDef FS_Desc;
/* USER CODE BEGIN EXPORTED_VARIABLES */
/* USER CODE END EXPORTED_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_DESC_Exported_FunctionsPrototype USBD_DESC_Exported_FunctionsPrototype
* @brief Public functions declaration.
* @{
*/
/* USER CODE BEGIN EXPORTED_FUNCTIONS */
uint8_t * USBD_UsrStrDescriptor(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length);
/* USER CODE END EXPORTED_FUNCTIONS */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __USBD_DESC__H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,87 @@
# This file is partially autogenerated. If you let CubeMX generate the code,
# it will edit this file to update the source and include list. This editing
# is not very robust, so be careful with changing the format of this file.
######################################
# source
######################################
C_SOURCES = \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c \
Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c \
Src/stm32f4xx_hal_timebase_TIM.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c \
Src/tim.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c \
Src/dma.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c \
Src/freertos.c \
Src/main.c \
Src/usbd_conf.c \
Src/spi.c \
Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c \
Src/usart.c \
Middlewares/Third_Party/FreeRTOS/Source/croutine.c \
Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c \
Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c \
Src/usbd_cdc_if.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c \
Src/adc.c \
Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c \
Middlewares/Third_Party/FreeRTOS/Source/list.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c \
Src/stm32f4xx_hal_msp.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c \
Src/usbd_desc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c \
Src/stm32f4xx_it.c \
Src/usb_device.c \
Src/can.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c \
Middlewares/Third_Party/FreeRTOS/Source/queue.c \
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c \
Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c \
Src/system_stm32f4xx.c \
Src/gpio.c \
Middlewares/Third_Party/FreeRTOS/Source/tasks.c \
Middlewares/Third_Party/FreeRTOS/Source/timers.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c \
Middlewares/Third_Party/FreeRTOS/Source/event_groups.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c \
Src/i2c.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c
ASM_SOURCES = \
startup_stm32f405xx.s
#######################################
# Includes
#######################################
AS_INCLUDES =
C_INCLUDES = \
-IMiddlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F \
-IMiddlewares/Third_Party/FreeRTOS/Source/include \
-IMiddlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS \
-IMiddlewares/ST/STM32_USB_Device_Library/Core/Inc \
-IMiddlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc \
-IDrivers/STM32F4xx_HAL_Driver/Inc \
-IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy \
-IDrivers/CMSIS/Device/ST/STM32F4xx/Include \
-IDrivers/CMSIS/Include \
-IInc
# CubeMX insists on this line
Inc
@@ -0,0 +1,698 @@
#MicroXplorer Configuration settings - do not modify
ADC1.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_6
ADC1.Channel-1\#ChannelInjectedConversion=ADC_CHANNEL_6
ADC1.ClockPrescaler=ADC_CLOCK_SYNC_PCLK_DIV4
ADC1.ContinuousConvMode=DISABLE
ADC1.DMAContinuousRequests=DISABLE
ADC1.DataAlign=ADC_DATAALIGN_RIGHT
ADC1.DiscontinuousConvMode=DISABLE
ADC1.EOCSelection=ADC_EOC_SINGLE_CONV
ADC1.EnableAnalogWatchDog=false
ADC1.ExternalTrigConv=ADC_SOFTWARE_START
ADC1.ExternalTrigConvEdge=ADC_EXTERNALTRIGCONVEDGE_NONE
ADC1.ExternalTrigInjecConv=ADC_EXTERNALTRIGINJECCONV_T1_TRGO
ADC1.ExternalTrigInjecConvEdge=ADC_EXTERNALTRIGINJECCONVEDGE_RISING
ADC1.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,NbrOfConversionFlag,master,ClockPrescaler,Resolution,DataAlign,ScanConvMode,ContinuousConvMode,DiscontinuousConvMode,DMAContinuousRequests,EOCSelection,NbrOfConversion,ExternalTrigConvEdge,InjNumberOfConversion,EnableAnalogWatchDog,Rank-1\#ChannelInjectedConversion,Channel-1\#ChannelInjectedConversion,SamplingTime-1\#ChannelInjectedConversion,InjectedOffset-1\#ChannelInjectedConversion,InjectedConvMode,ExternalTrigInjecConvEdge,ExternalTrigInjecConv,ExternalTrigConv
ADC1.InjNumberOfConversion=1
ADC1.InjectedConvMode=None
ADC1.InjectedOffset-1\#ChannelInjectedConversion=0
ADC1.NbrOfConversion=1
ADC1.NbrOfConversionFlag=1
ADC1.Rank-0\#ChannelRegularConversion=1
ADC1.Rank-1\#ChannelInjectedConversion=1
ADC1.Resolution=ADC_RESOLUTION_12B
ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_3CYCLES
ADC1.SamplingTime-1\#ChannelInjectedConversion=ADC_SAMPLETIME_3CYCLES
ADC1.ScanConvMode=DISABLE
ADC1.master=1
ADC2.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_13
ADC2.Channel-1\#ChannelInjectedConversion=ADC_CHANNEL_10
ADC2.ClockPrescaler=ADC_CLOCK_SYNC_PCLK_DIV4
ADC2.ContinuousConvMode=DISABLE
ADC2.DMAContinuousRequests=DISABLE
ADC2.DataAlign=ADC_DATAALIGN_RIGHT
ADC2.DiscontinuousConvMode=DISABLE
ADC2.EOCSelection=ADC_EOC_SINGLE_CONV
ADC2.EnableAnalogWatchDog=false
ADC2.ExternalTrigConv=ADC_EXTERNALTRIGCONV_T8_TRGO
ADC2.ExternalTrigConvEdge=ADC_EXTERNALTRIGCONVEDGE_RISING
ADC2.ExternalTrigInjecConv=ADC_EXTERNALTRIGINJECCONV_T1_TRGO
ADC2.ExternalTrigInjecConvEdge=ADC_EXTERNALTRIGINJECCONVEDGE_RISING
ADC2.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,NbrOfConversionFlag,ClockPrescaler,Resolution,DataAlign,ScanConvMode,ContinuousConvMode,DiscontinuousConvMode,DMAContinuousRequests,EOCSelection,NbrOfConversion,InjNumberOfConversion,EnableAnalogWatchDog,Rank-1\#ChannelInjectedConversion,Channel-1\#ChannelInjectedConversion,SamplingTime-1\#ChannelInjectedConversion,InjectedOffset-1\#ChannelInjectedConversion,ExternalTrigInjecConvEdge,ExternalTrigConvEdge,InjectedConvMode,ExternalTrigInjecConv,ExternalTrigConv
ADC2.InjNumberOfConversion=1
ADC2.InjectedConvMode=None
ADC2.InjectedOffset-1\#ChannelInjectedConversion=0
ADC2.NbrOfConversion=1
ADC2.NbrOfConversionFlag=1
ADC2.Rank-0\#ChannelRegularConversion=1
ADC2.Rank-1\#ChannelInjectedConversion=1
ADC2.Resolution=ADC_RESOLUTION_12B
ADC2.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_3CYCLES
ADC2.SamplingTime-1\#ChannelInjectedConversion=ADC_SAMPLETIME_3CYCLES
ADC2.ScanConvMode=DISABLE
ADC3.Channel-7\#ChannelRegularConversion=ADC_CHANNEL_12
ADC3.Channel-8\#ChannelInjectedConversion=ADC_CHANNEL_11
ADC3.ClockPrescaler=ADC_CLOCK_SYNC_PCLK_DIV4
ADC3.ContinuousConvMode=DISABLE
ADC3.DMAContinuousRequests=DISABLE
ADC3.DataAlign=ADC_DATAALIGN_RIGHT
ADC3.DiscontinuousConvMode=DISABLE
ADC3.EOCSelection=ADC_EOC_SINGLE_CONV
ADC3.EnableAnalogWatchDog=false
ADC3.ExternalTrigConv=ADC_EXTERNALTRIGCONV_T8_TRGO
ADC3.ExternalTrigConvEdge=ADC_EXTERNALTRIGCONVEDGE_RISING
ADC3.ExternalTrigInjecConv=ADC_EXTERNALTRIGINJECCONV_T1_TRGO
ADC3.ExternalTrigInjecConvEdge=ADC_EXTERNALTRIGINJECCONVEDGE_RISING
ADC3.IPParameters=Rank-7\#ChannelRegularConversion,Channel-7\#ChannelRegularConversion,SamplingTime-7\#ChannelRegularConversion,NbrOfConversionFlag,ClockPrescaler,Resolution,DataAlign,ScanConvMode,ContinuousConvMode,DiscontinuousConvMode,DMAContinuousRequests,EOCSelection,NbrOfConversion,ExternalTrigConvEdge,InjNumberOfConversion,EnableAnalogWatchDog,Rank-8\#ChannelInjectedConversion,Channel-8\#ChannelInjectedConversion,SamplingTime-8\#ChannelInjectedConversion,InjectedOffset-8\#ChannelInjectedConversion,ExternalTrigInjecConvEdge,InjectedConvMode,ExternalTrigInjecConv,ExternalTrigConv
ADC3.InjNumberOfConversion=1
ADC3.InjectedConvMode=None
ADC3.InjectedOffset-8\#ChannelInjectedConversion=0
ADC3.NbrOfConversion=1
ADC3.NbrOfConversionFlag=1
ADC3.Rank-7\#ChannelRegularConversion=1
ADC3.Rank-8\#ChannelInjectedConversion=1
ADC3.Resolution=ADC_RESOLUTION_12B
ADC3.SamplingTime-7\#ChannelRegularConversion=ADC_SAMPLETIME_3CYCLES
ADC3.SamplingTime-8\#ChannelInjectedConversion=ADC_SAMPLETIME_3CYCLES
ADC3.ScanConvMode=DISABLE
CAN1.AutoWakeUp=ENABLE
CAN1.BS1=CAN_BS1_6TQ
CAN1.BS2=CAN_BS2_5TQ
CAN1.CalculateTimeBit=500
CAN1.CalculateTimeQuantum=166.66666666666669
CAN1.IPParameters=CalculateTimeQuantum,CalculateTimeBit,Prescaler,TimeSeg1,TimeSeg2,AutoWakeUp,BS1,BS2
CAN1.Prescaler=7
CAN1.TimeSeg1=CAN_BS1_6TQ
CAN1.TimeSeg2=CAN_BS2_5TQ
Dma.ADC1.2.Direction=DMA_PERIPH_TO_MEMORY
Dma.ADC1.2.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.ADC1.2.Instance=DMA2_Stream0
Dma.ADC1.2.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.ADC1.2.MemInc=DMA_MINC_ENABLE
Dma.ADC1.2.Mode=DMA_CIRCULAR
Dma.ADC1.2.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.ADC1.2.PeriphInc=DMA_PINC_DISABLE
Dma.ADC1.2.Priority=DMA_PRIORITY_LOW
Dma.ADC1.2.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode
Dma.Request0=UART4_RX
Dma.Request1=UART4_TX
Dma.Request2=ADC1
Dma.Request3=SPI3_TX
Dma.Request4=SPI3_RX
Dma.RequestsNb=5
Dma.SPI3_RX.4.Direction=DMA_PERIPH_TO_MEMORY
Dma.SPI3_RX.4.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.SPI3_RX.4.Instance=DMA1_Stream0
Dma.SPI3_RX.4.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.SPI3_RX.4.MemInc=DMA_MINC_ENABLE
Dma.SPI3_RX.4.Mode=DMA_NORMAL
Dma.SPI3_RX.4.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.SPI3_RX.4.PeriphInc=DMA_PINC_DISABLE
Dma.SPI3_RX.4.Priority=DMA_PRIORITY_MEDIUM
Dma.SPI3_RX.4.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode
Dma.SPI3_TX.3.Direction=DMA_MEMORY_TO_PERIPH
Dma.SPI3_TX.3.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.SPI3_TX.3.Instance=DMA1_Stream5
Dma.SPI3_TX.3.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.SPI3_TX.3.MemInc=DMA_MINC_ENABLE
Dma.SPI3_TX.3.Mode=DMA_NORMAL
Dma.SPI3_TX.3.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.SPI3_TX.3.PeriphInc=DMA_PINC_DISABLE
Dma.SPI3_TX.3.Priority=DMA_PRIORITY_MEDIUM
Dma.SPI3_TX.3.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode
Dma.UART4_RX.0.Direction=DMA_PERIPH_TO_MEMORY
Dma.UART4_RX.0.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.UART4_RX.0.Instance=DMA1_Stream2
Dma.UART4_RX.0.MemDataAlignment=DMA_MDATAALIGN_BYTE
Dma.UART4_RX.0.MemInc=DMA_MINC_ENABLE
Dma.UART4_RX.0.Mode=DMA_CIRCULAR
Dma.UART4_RX.0.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
Dma.UART4_RX.0.PeriphInc=DMA_PINC_DISABLE
Dma.UART4_RX.0.Priority=DMA_PRIORITY_LOW
Dma.UART4_RX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode
Dma.UART4_TX.1.Direction=DMA_MEMORY_TO_PERIPH
Dma.UART4_TX.1.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.UART4_TX.1.Instance=DMA1_Stream4
Dma.UART4_TX.1.MemDataAlignment=DMA_MDATAALIGN_BYTE
Dma.UART4_TX.1.MemInc=DMA_MINC_ENABLE
Dma.UART4_TX.1.Mode=DMA_NORMAL
Dma.UART4_TX.1.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
Dma.UART4_TX.1.PeriphInc=DMA_PINC_DISABLE
Dma.UART4_TX.1.Priority=DMA_PRIORITY_LOW
Dma.UART4_TX.1.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode
FREERTOS.FootprintOK=true
FREERTOS.INCLUDE_uxTaskGetStackHighWaterMark=1
FREERTOS.INCLUDE_vTaskDelayUntil=1
FREERTOS.IPParameters=Tasks01,INCLUDE_vTaskDelayUntil,configTOTAL_HEAP_SIZE,FootprintOK,configCHECK_FOR_STACK_OVERFLOW,INCLUDE_uxTaskGetStackHighWaterMark,configUSE_IDLE_HOOK
FREERTOS.Tasks01=defaultTask,0,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL
FREERTOS.configCHECK_FOR_STACK_OVERFLOW=1
FREERTOS.configTOTAL_HEAP_SIZE=65536
FREERTOS.configUSE_IDLE_HOOK=1
File.Version=6
KeepUserPlacement=true
Mcu.Family=STM32F4
Mcu.IP0=ADC1
Mcu.IP1=ADC2
Mcu.IP10=TIM1
Mcu.IP11=TIM2
Mcu.IP12=TIM3
Mcu.IP13=TIM4
Mcu.IP14=TIM5
Mcu.IP15=TIM8
Mcu.IP16=TIM13
Mcu.IP17=UART4
Mcu.IP18=USB_DEVICE
Mcu.IP19=USB_OTG_FS
Mcu.IP2=ADC3
Mcu.IP3=CAN1
Mcu.IP4=DMA
Mcu.IP5=FREERTOS
Mcu.IP6=NVIC
Mcu.IP7=RCC
Mcu.IP8=SPI3
Mcu.IP9=SYS
Mcu.IPNb=20
Mcu.Name=STM32F405RGTx
Mcu.Package=LQFP64
Mcu.Pin0=PC13-ANTI_TAMP
Mcu.Pin1=PC14-OSC32_IN
Mcu.Pin10=PA1
Mcu.Pin11=PA2
Mcu.Pin12=PA3
Mcu.Pin13=PA4
Mcu.Pin14=PA5
Mcu.Pin15=PA6
Mcu.Pin16=PA7
Mcu.Pin17=PC4
Mcu.Pin18=PC5
Mcu.Pin19=PB0
Mcu.Pin2=PC15-OSC32_OUT
Mcu.Pin20=PB1
Mcu.Pin21=PB2
Mcu.Pin22=PB10
Mcu.Pin23=PB11
Mcu.Pin24=PB12
Mcu.Pin25=PB13
Mcu.Pin26=PB14
Mcu.Pin27=PB15
Mcu.Pin28=PC6
Mcu.Pin29=PC7
Mcu.Pin3=PH0-OSC_IN
Mcu.Pin30=PC8
Mcu.Pin31=PC9
Mcu.Pin32=PA8
Mcu.Pin33=PA9
Mcu.Pin34=PA10
Mcu.Pin35=PA11
Mcu.Pin36=PA12
Mcu.Pin37=PA13
Mcu.Pin38=PA14
Mcu.Pin39=PA15
Mcu.Pin4=PH1-OSC_OUT
Mcu.Pin40=PC10
Mcu.Pin41=PC11
Mcu.Pin42=PC12
Mcu.Pin43=PD2
Mcu.Pin44=PB3
Mcu.Pin45=PB4
Mcu.Pin46=PB5
Mcu.Pin47=PB6
Mcu.Pin48=PB7
Mcu.Pin49=PB8
Mcu.Pin5=PC0
Mcu.Pin50=PB9
Mcu.Pin51=VP_FREERTOS_VS_ENABLE
Mcu.Pin52=VP_SYS_VS_tim14
Mcu.Pin53=VP_TIM1_VS_ClockSourceINT
Mcu.Pin54=VP_TIM1_VS_no_output4
Mcu.Pin55=VP_TIM13_VS_ClockSourceINT
Mcu.Pin56=VP_USB_DEVICE_VS_USB_DEVICE_CDC_FS
Mcu.Pin6=PC1
Mcu.Pin7=PC2
Mcu.Pin8=PC3
Mcu.Pin9=PA0-WKUP
Mcu.PinsNb=57
Mcu.ThirdPartyNb=0
Mcu.UserConstants=TIM_1_8_CLOCK_HZ,168000000;TIM_1_8_PERIOD_CLOCKS,3500;TIM_1_8_DEADTIME_CLOCKS,20;TIM_APB1_CLOCK_HZ,84000000;TIM_APB1_PERIOD_CLOCKS,4096;TIM_APB1_DEADTIME_CLOCKS,40;TIM_1_8_RCR,2
Mcu.UserName=STM32F405RGTx
MxCube.Version=4.27.0
MxDb.Version=DB.4.0.270
NVIC.ADC_IRQn=true\:5\:0\:false\:false\:true\:true\:true
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:true
NVIC.CAN1_RX0_IRQn=true\:6\:0\:true\:false\:true\:true\:true
NVIC.CAN1_RX1_IRQn=true\:6\:0\:true\:false\:true\:true\:true
NVIC.CAN1_SCE_IRQn=true\:6\:0\:true\:false\:true\:true\:true
NVIC.CAN1_TX_IRQn=true\:6\:0\:true\:false\:true\:true\:true
NVIC.DMA1_Stream0_IRQn=true\:5\:0\:false\:false\:true\:true\:false
NVIC.DMA1_Stream2_IRQn=true\:5\:0\:false\:false\:true\:true\:true
NVIC.DMA1_Stream4_IRQn=true\:5\:0\:false\:false\:true\:true\:false
NVIC.DMA1_Stream5_IRQn=true\:5\:0\:false\:false\:true\:true\:false
NVIC.DMA2_Stream0_IRQn=true\:5\:0\:false\:false\:false\:true\:false
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:true
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:true
NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:true
NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:true
NVIC.OTG_FS_IRQn=true\:5\:0\:false\:false\:true\:true\:true
NVIC.PendSV_IRQn=true\:15\:0\:false\:false\:false\:true\:true
NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
NVIC.SPI3_IRQn=true\:5\:0\:false\:false\:true\:true\:true
NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:false\:false\:true
NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:true\:true
NVIC.TIM1_UP_TIM10_IRQn=true\:0\:0\:false\:false\:false\:false\:true
NVIC.TIM5_IRQn=true\:5\:0\:false\:false\:true\:true\:true
NVIC.TIM8_TRG_COM_TIM14_IRQn=true\:0\:0\:false\:false\:true\:false\:false
NVIC.TIM8_UP_TIM13_IRQn=true\:0\:0\:false\:false\:false\:false\:true
NVIC.TimeBase=TIM8_TRG_COM_TIM14_IRQn
NVIC.TimeBaseIP=TIM14
NVIC.UART4_IRQn=true\:5\:0\:false\:false\:true\:true\:true
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:true
PA0-WKUP.GPIOParameters=GPIO_PuPd,GPIO_Label
PA0-WKUP.GPIO_Label=GPIO_1
PA0-WKUP.GPIO_PuPd=GPIO_PULLDOWN
PA0-WKUP.Locked=true
PA0-WKUP.Signal=SharedStack_PA0
PA0-WKUP.Stacked=true
PA1.GPIOParameters=GPIO_PuPd,GPIO_Label
PA1.GPIO_Label=GPIO_2
PA1.GPIO_PuPd=GPIO_NOPULL
PA1.Locked=true
PA1.Signal=SharedStack_PA1
PA1.Stacked=true
PA10.GPIOParameters=GPIO_Label
PA10.GPIO_Label=M0_CH
PA10.Locked=true
PA10.Signal=S_TIM1_CH3
PA11.Mode=Device_Only
PA11.Signal=USB_OTG_FS_DM
PA12.Mode=Device_Only
PA12.Signal=USB_OTG_FS_DP
PA13.Mode=Serial_Wire
PA13.Signal=SYS_JTMS-SWDIO
PA14.Mode=Serial_Wire
PA14.Signal=SYS_JTCK-SWCLK
PA15.GPIOParameters=GPIO_Label
PA15.GPIO_Label=GPIO_7
PA15.Locked=true
PA15.Signal=GPIO_Input
PA2.GPIOParameters=GPIO_Label
PA2.GPIO_Label=GPIO_3
PA2.Locked=true
PA2.Signal=S_TIM5_CH3
PA3.GPIOParameters=GPIO_Label
PA3.GPIO_Label=GPIO_4
PA3.Locked=true
PA3.Signal=S_TIM5_CH4
PA4.GPIOParameters=GPIO_Label
PA4.GPIO_Label=M1_TEMP
PA4.Locked=true
PA4.Signal=ADCx_IN4
PA5.GPIOParameters=GPIO_Label
PA5.GPIO_Label=AUX_TEMP
PA5.Locked=true
PA5.Signal=ADCx_IN5
PA6.GPIOParameters=GPIO_Label
PA6.GPIO_Label=VBUS_S
PA6.Locked=true
PA6.Signal=ADCx_IN6
PA7.GPIOParameters=GPIO_Label
PA7.GPIO_Label=M1_AL
PA7.Locked=true
PA7.Mode=PWM Generation1 CH1 CH1N
PA7.Signal=TIM8_CH1N
PA8.GPIOParameters=GPIO_Label
PA8.GPIO_Label=M0_AH
PA8.Locked=true
PA8.Signal=S_TIM1_CH1
PA9.GPIOParameters=GPIO_Label
PA9.GPIO_Label=M0_BH
PA9.Locked=true
PA9.Signal=S_TIM1_CH2
PB0.GPIOParameters=GPIO_Label
PB0.GPIO_Label=M1_BL
PB0.Locked=true
PB0.Mode=PWM Generation2 CH2 CH2N
PB0.Signal=TIM8_CH2N
PB1.GPIOParameters=GPIO_Label
PB1.GPIO_Label=M1_CL
PB1.Locked=true
PB1.Mode=PWM Generation3 CH3 CH3N
PB1.Signal=TIM8_CH3N
PB10.GPIOParameters=GPIO_Label
PB10.GPIO_Label=AUX_L
PB10.Locked=true
PB10.Signal=S_TIM2_CH3
PB11.GPIOParameters=GPIO_Label
PB11.GPIO_Label=AUX_H
PB11.Locked=true
PB11.Signal=S_TIM2_CH4
PB12.GPIOParameters=GPIO_Label
PB12.GPIO_Label=EN_GATE
PB12.Locked=true
PB12.Signal=GPIO_Output
PB13.GPIOParameters=GPIO_Label
PB13.GPIO_Label=M0_AL
PB13.Locked=true
PB13.Mode=PWM Generation1 CH1 CH1N
PB13.Signal=TIM1_CH1N
PB14.GPIOParameters=GPIO_Label
PB14.GPIO_Label=M0_BL
PB14.Locked=true
PB14.Mode=PWM Generation2 CH2 CH2N
PB14.Signal=TIM1_CH2N
PB15.GPIOParameters=GPIO_Label
PB15.GPIO_Label=M0_CL
PB15.Locked=true
PB15.Mode=PWM Generation3 CH3 CH3N
PB15.Signal=TIM1_CH3N
PB2.GPIOParameters=GPIO_Label
PB2.GPIO_Label=GPIO_6
PB2.Locked=true
PB2.Signal=GPIO_Input
PB3.GPIOParameters=GPIO_Label
PB3.GPIO_Label=GPIO_8
PB3.Locked=true
PB3.Signal=GPIO_Input
PB4.GPIOParameters=GPIO_Label
PB4.GPIO_Label=M0_ENC_A
PB4.Signal=S_TIM3_CH1
PB5.GPIOParameters=GPIO_Label
PB5.GPIO_Label=M0_ENC_B
PB5.Signal=S_TIM3_CH2
PB6.GPIOParameters=GPIO_Label
PB6.GPIO_Label=M1_ENC_A
PB6.Signal=S_TIM4_CH1
PB7.GPIOParameters=GPIO_Label
PB7.GPIO_Label=M1_ENC_B
PB7.Signal=S_TIM4_CH2
PB8.Locked=true
PB8.Signal=SharedStack_PB8
PB8.Stacked=true
PB9.Locked=true
PB9.Signal=SharedStack_PB9
PB9.Stacked=true
PC0.GPIOParameters=GPIO_Label
PC0.GPIO_Label=M0_IB
PC0.Signal=ADCx_IN10
PC1.GPIOParameters=GPIO_Label
PC1.GPIO_Label=M0_IC
PC1.Signal=ADCx_IN11
PC10.Mode=Full_Duplex_Master
PC10.Signal=SPI3_SCK
PC11.Mode=Full_Duplex_Master
PC11.Signal=SPI3_MISO
PC12.Mode=Full_Duplex_Master
PC12.Signal=SPI3_MOSI
PC13-ANTI_TAMP.GPIOParameters=PinState,GPIO_Label
PC13-ANTI_TAMP.GPIO_Label=M0_nCS
PC13-ANTI_TAMP.Locked=true
PC13-ANTI_TAMP.PinState=GPIO_PIN_SET
PC13-ANTI_TAMP.Signal=GPIO_Output
PC14-OSC32_IN.GPIOParameters=PinState,GPIO_Label
PC14-OSC32_IN.GPIO_Label=M1_nCS
PC14-OSC32_IN.Locked=true
PC14-OSC32_IN.PinState=GPIO_PIN_SET
PC14-OSC32_IN.Signal=GPIO_Output
PC15-OSC32_OUT.GPIOParameters=GPIO_Label
PC15-OSC32_OUT.GPIO_Label=M1_ENC_Z
PC15-OSC32_OUT.Locked=true
PC15-OSC32_OUT.Signal=GPIO_Input
PC2.GPIOParameters=GPIO_Label
PC2.GPIO_Label=M1_IC
PC2.Signal=ADCx_IN12
PC3.GPIOParameters=GPIO_Label
PC3.GPIO_Label=M1_IB
PC3.Signal=ADCx_IN13
PC4.GPIOParameters=GPIO_Label
PC4.GPIO_Label=GPIO_5
PC4.Locked=true
PC4.Signal=GPIO_Input
PC5.GPIOParameters=GPIO_Label
PC5.GPIO_Label=M0_TEMP
PC5.Signal=ADCx_IN15
PC6.GPIOParameters=GPIO_Label
PC6.GPIO_Label=M1_AH
PC6.Locked=true
PC6.Signal=S_TIM8_CH1
PC7.GPIOParameters=GPIO_Label
PC7.GPIO_Label=M1_BH
PC7.Locked=true
PC7.Signal=S_TIM8_CH2
PC8.GPIOParameters=GPIO_Label
PC8.GPIO_Label=M1_CH
PC8.Locked=true
PC8.Signal=S_TIM8_CH3
PC9.GPIOParameters=GPIO_Label
PC9.GPIO_Label=M0_ENC_Z
PC9.Locked=true
PC9.Signal=GPIO_Input
PCC.Checker=false
PCC.Line=STM32F405/415
PCC.MCU=STM32F405RGTx
PCC.PartNumber=STM32F405RGTx
PCC.Seq0=0
PCC.Series=STM32F4
PCC.Temperature=25
PCC.Vdd=3.3
PD2.GPIOParameters=GPIO_PuPd,GPIO_Label
PD2.GPIO_Label=nFAULT
PD2.GPIO_PuPd=GPIO_PULLUP
PD2.Locked=true
PD2.Signal=GPIO_Input
PH0-OSC_IN.Mode=HSE-External-Oscillator
PH0-OSC_IN.Signal=RCC_OSC_IN
PH1-OSC_OUT.Mode=HSE-External-Oscillator
PH1-OSC_OUT.Signal=RCC_OSC_OUT
PinOutPanel.RotationAngle=0
ProjectManager.AskForMigrate=true
ProjectManager.BackupPrevious=false
ProjectManager.CompilerOptimize=2
ProjectManager.ComputerToolchain=false
ProjectManager.CoupleFile=true
ProjectManager.CustomerFirmwarePackage=
ProjectManager.DefaultFWLocation=true
ProjectManager.DeletePrevious=true
ProjectManager.DeviceId=STM32F405RGTx
ProjectManager.FirmwarePackage=STM32Cube FW_F4 V1.21.0
ProjectManager.FreePins=false
ProjectManager.HalAssertFull=false
ProjectManager.HeapSize=0x3C00
ProjectManager.KeepUserCode=true
ProjectManager.LastFirmware=true
ProjectManager.LibraryCopy=1
ProjectManager.MainLocation=Src
ProjectManager.NoMain=false
ProjectManager.PreviousToolchain=SW4STM32
ProjectManager.ProjectBuild=false
ProjectManager.ProjectFileName=Odrive.ioc
ProjectManager.ProjectName=Odrive
ProjectManager.StackSize=0x800
ProjectManager.TargetToolchain=Makefile
ProjectManager.ToolChainLocation=
ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-MX_GPIO_Init-GPIO-false-HAL-true,2-MX_DMA_Init-DMA-false-HAL-true,3-MX_ADC1_Init-ADC1-false-HAL-true,4-MX_ADC2_Init-ADC2-false-HAL-true,5-MX_CAN1_Init-CAN1-false-HAL-true,6-MX_TIM1_Init-TIM1-false-HAL-true,7-MX_TIM8_Init-TIM8-false-HAL-true,8-MX_TIM3_Init-TIM3-false-HAL-true,9-MX_TIM4_Init-TIM4-false-HAL-true,10-MX_SPI3_Init-SPI3-false-HAL-true,11-MX_ADC3_Init-ADC3-false-HAL-true,12-SystemClock_Config-RCC-false-HAL-true,13-MX_TIM2_Init-TIM2-false-HAL-true,14-MX_USB_DEVICE_Init-USB_DEVICE-false-HAL-true,15-MX_UART4_Init-UART4-false-HAL-true,16-MX_TIM5_Init-TIM5-false-HAL-true,17-MX_TIM13_Init-TIM13-false-HAL-true
RCC.48MHZClocksFreq_Value=48000000
RCC.AHBFreq_Value=168000000
RCC.APB1CLKDivider=RCC_HCLK_DIV4
RCC.APB1Freq_Value=42000000
RCC.APB1TimFreq_Value=84000000
RCC.APB2CLKDivider=RCC_HCLK_DIV2
RCC.APB2Freq_Value=84000000
RCC.APB2TimCLKDivider=1
RCC.APB2TimFreq_Value=84000000
RCC.CortexFreq_Value=168000000
RCC.EthernetFreq_Value=168000000
RCC.FCLKCortexFreq_Value=168000000
RCC.FamilyName=M
RCC.HCLKFreq_Value=168000000
RCC.HSE_VALUE=8000000
RCC.HSI_VALUE=16000000
RCC.I2SClocksFreq_Value=192000000
RCC.IPParameters=48MHZClocksFreq_Value,AHBFreq_Value,APB1CLKDivider,APB1Freq_Value,APB1TimFreq_Value,APB2CLKDivider,APB2Freq_Value,APB2TimCLKDivider,APB2TimFreq_Value,CortexFreq_Value,EthernetFreq_Value,FCLKCortexFreq_Value,FamilyName,HCLKFreq_Value,HSE_VALUE,HSI_VALUE,I2SClocksFreq_Value,LSE_VALUE,LSI_VALUE,MCO2PinFreq_Value,PLLCLKFreq_Value,PLLM,PLLN,PLLQ,PLLQCLKFreq_Value,RTCFreq_Value,RTCHSEDivFreq_Value,SYSCLKFreq_VALUE,SYSCLKSource,VCOI2SOutputFreq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,VcooutputI2S
RCC.LSE_VALUE=32768
RCC.LSI_VALUE=32000
RCC.MCO2PinFreq_Value=168000000
RCC.PLLCLKFreq_Value=168000000
RCC.PLLM=4
RCC.PLLN=168
RCC.PLLQ=7
RCC.PLLQCLKFreq_Value=48000000
RCC.RTCFreq_Value=32000
RCC.RTCHSEDivFreq_Value=4000000
RCC.SYSCLKFreq_VALUE=168000000
RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
RCC.VCOI2SOutputFreq_Value=384000000
RCC.VCOInputFreq_Value=2000000
RCC.VCOOutputFreq_Value=336000000
RCC.VcooutputI2S=192000000
SH.ADCx_IN10.0=ADC1_IN10,IN10
SH.ADCx_IN10.1=ADC2_IN10,IN10
SH.ADCx_IN10.2=ADC3_IN10,IN10
SH.ADCx_IN10.ConfNb=3
SH.ADCx_IN11.0=ADC1_IN11,IN11
SH.ADCx_IN11.1=ADC2_IN11,IN11
SH.ADCx_IN11.2=ADC3_IN11,IN11
SH.ADCx_IN11.ConfNb=3
SH.ADCx_IN12.0=ADC1_IN12,IN12
SH.ADCx_IN12.1=ADC2_IN12,IN12
SH.ADCx_IN12.2=ADC3_IN12,IN12
SH.ADCx_IN12.ConfNb=3
SH.ADCx_IN13.0=ADC1_IN13,IN13
SH.ADCx_IN13.1=ADC2_IN13,IN13
SH.ADCx_IN13.2=ADC3_IN13,IN13
SH.ADCx_IN13.ConfNb=3
SH.ADCx_IN15.0=ADC1_IN15,IN15
SH.ADCx_IN15.1=ADC2_IN15,IN15
SH.ADCx_IN15.ConfNb=2
SH.ADCx_IN4.0=ADC1_IN4,IN4
SH.ADCx_IN4.1=ADC2_IN4,IN4
SH.ADCx_IN4.ConfNb=2
SH.ADCx_IN5.0=ADC1_IN5,IN5
SH.ADCx_IN5.1=ADC2_IN5,IN5
SH.ADCx_IN5.ConfNb=2
SH.ADCx_IN6.0=ADC1_IN6,IN6
SH.ADCx_IN6.1=ADC2_IN6,IN6
SH.ADCx_IN6.ConfNb=2
SH.S_TIM1_CH1.0=TIM1_CH1,PWM Generation1 CH1 CH1N
SH.S_TIM1_CH1.ConfNb=1
SH.S_TIM1_CH2.0=TIM1_CH2,PWM Generation2 CH2 CH2N
SH.S_TIM1_CH2.ConfNb=1
SH.S_TIM1_CH3.0=TIM1_CH3,PWM Generation3 CH3 CH3N
SH.S_TIM1_CH3.ConfNb=1
SH.S_TIM2_CH3.0=TIM2_CH3,PWM Generation3 CH3
SH.S_TIM2_CH3.ConfNb=1
SH.S_TIM2_CH4.0=TIM2_CH4,PWM Generation4 CH4
SH.S_TIM2_CH4.ConfNb=1
SH.S_TIM3_CH1.0=TIM3_CH1,Encoder_Interface
SH.S_TIM3_CH1.ConfNb=1
SH.S_TIM3_CH2.0=TIM3_CH2,Encoder_Interface
SH.S_TIM3_CH2.ConfNb=1
SH.S_TIM4_CH1.0=TIM4_CH1,Encoder_Interface
SH.S_TIM4_CH1.ConfNb=1
SH.S_TIM4_CH2.0=TIM4_CH2,Encoder_Interface
SH.S_TIM4_CH2.ConfNb=1
SH.S_TIM5_CH3.0=TIM5_CH3,Input_Capture3_from_TI3
SH.S_TIM5_CH3.ConfNb=1
SH.S_TIM5_CH4.0=TIM5_CH4,Input_Capture4_from_TI4
SH.S_TIM5_CH4.ConfNb=1
SH.S_TIM8_CH1.0=TIM8_CH1,PWM Generation1 CH1 CH1N
SH.S_TIM8_CH1.ConfNb=1
SH.S_TIM8_CH2.0=TIM8_CH2,PWM Generation2 CH2 CH2N
SH.S_TIM8_CH2.ConfNb=1
SH.S_TIM8_CH3.0=TIM8_CH3,PWM Generation3 CH3 CH3N
SH.S_TIM8_CH3.ConfNb=1
SH.SharedStack_PA0.0=GPIO_EXTI0+0
SH.SharedStack_PA0.1=UART4_TX,Asynchronous
SH.SharedStack_PA0.ConfNb=2
SH.SharedStack_PA1.0=GPIO_Input+0
SH.SharedStack_PA1.1=UART4_RX,Asynchronous
SH.SharedStack_PA1.ConfNb=2
SH.SharedStack_PB8.0=CAN1_RX,Master
SH.SharedStack_PB8.1=I2C1_SCL
SH.SharedStack_PB8.ConfNb=2
SH.SharedStack_PB9.0=CAN1_TX,Master
SH.SharedStack_PB9.1=I2C1_SDA
SH.SharedStack_PB9.ConfNb=2
SPI3.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16
SPI3.CLKPhase=SPI_PHASE_2EDGE
SPI3.CalculateBaudRate=2.625 MBits/s
SPI3.DataSize=SPI_DATASIZE_16BIT
SPI3.Direction=SPI_DIRECTION_2LINES
SPI3.FirstBit=SPI_FIRSTBIT_MSB
SPI3.IPParameters=Mode,CalculateBaudRate,BaudRatePrescaler,DataSize,FirstBit,CLKPhase,VirtualType,Direction
SPI3.Mode=SPI_MODE_MASTER
SPI3.VirtualType=VM_MASTER
TIM1.Channel-Output\ Compare4\ No\ Output=TIM_CHANNEL_4
TIM1.Channel-PWM\ Generation1\ CH1\ CH1N=TIM_CHANNEL_1
TIM1.Channel-PWM\ Generation2\ CH2\ CH2N=TIM_CHANNEL_2
TIM1.Channel-PWM\ Generation3\ CH3\ CH3N=TIM_CHANNEL_3
TIM1.CounterMode=TIM_COUNTERMODE_CENTERALIGNED3
TIM1.DeadTime=TIM_1_8_DEADTIME_CLOCKS
TIM1.IPParameters=Channel-PWM Generation2 CH2 CH2N,Channel-PWM Generation3 CH3 CH3N,OCNPolarity_1,OCNPolarity_2,OCNPolarity_3,CounterMode,OCMode_PWM-PWM Generation1 CH1 CH1N,OCMode_PWM-PWM Generation2 CH2 CH2N,OCMode_PWM-PWM Generation3 CH3 CH3N,Period,DeadTime,OffStateRunMode,OffStateIDLEMode,Channel-Output Compare4 No Output,TIM_MasterOutputTrigger,Channel-PWM Generation1 CH1 CH1N,RepetitionCounter
TIM1.OCMode_PWM-PWM\ Generation1\ CH1\ CH1N=TIM_OCMODE_PWM2
TIM1.OCMode_PWM-PWM\ Generation2\ CH2\ CH2N=TIM_OCMODE_PWM2
TIM1.OCMode_PWM-PWM\ Generation3\ CH3\ CH3N=TIM_OCMODE_PWM2
TIM1.OCNPolarity_1=TIM_OCNPOLARITY_HIGH
TIM1.OCNPolarity_2=TIM_OCNPOLARITY_HIGH
TIM1.OCNPolarity_3=TIM_OCNPOLARITY_HIGH
TIM1.OffStateIDLEMode=TIM_OSSI_ENABLE
TIM1.OffStateRunMode=TIM_OSSR_ENABLE
TIM1.Period=TIM_1_8_PERIOD_CLOCKS
TIM1.RepetitionCounter=TIM_1_8_RCR
TIM1.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
TIM13.IPParameters=Period
TIM13.Period=(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR+1)) * (TIM_APB1_CLOCK_HZ / TIM_1_8_CLOCK_HZ)
TIM2.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
TIM2.Channel-PWM\ Generation4\ CH4=TIM_CHANNEL_4
TIM2.CounterMode=TIM_COUNTERMODE_CENTERALIGNED3
TIM2.IPParameters=Channel-PWM Generation3 CH3,Channel-PWM Generation4 CH4,CounterMode,Period,OCMode_PWM-PWM Generation3 CH3,OCMode_PWM-PWM Generation4 CH4,OCPolarity_3,Pulse-PWM Generation4 CH4
TIM2.OCMode_PWM-PWM\ Generation3\ CH3=TIM_OCMODE_PWM2
TIM2.OCMode_PWM-PWM\ Generation4\ CH4=TIM_OCMODE_PWM2
TIM2.OCPolarity_3=TIM_OCPOLARITY_LOW
TIM2.Period=TIM_APB1_PERIOD_CLOCKS
TIM2.Pulse-PWM\ Generation4\ CH4=TIM_APB1_PERIOD_CLOCKS+1
TIM3.EncoderMode=TIM_ENCODERMODE_TI12
TIM3.IC1Filter=4
TIM3.IC1Polarity=TIM_ICPOLARITY_RISING
TIM3.IC2Filter=4
TIM3.IC2Polarity=TIM_ICPOLARITY_RISING
TIM3.IPParameters=EncoderMode,IC1Polarity,IC2Polarity,IC1Filter,IC2Filter,Period
TIM3.Period=0xffff
TIM4.EncoderMode=TIM_ENCODERMODE_TI12
TIM4.IC1Filter=4
TIM4.IC1Polarity=TIM_ICPOLARITY_RISING
TIM4.IC2Filter=4
TIM4.IC2Polarity=TIM_ICPOLARITY_RISING
TIM4.IPParameters=EncoderMode,IC1Polarity,IC2Polarity,IC1Filter,IC2Filter,Period
TIM4.Period=0xffff
TIM5.Channel-Input_Capture3_from_TI3=TIM_CHANNEL_3
TIM5.Channel-Input_Capture4_from_TI4=TIM_CHANNEL_4
TIM5.ICFilter_CH3=15
TIM5.ICFilter_CH4=15
TIM5.ICPolarity_CH3=TIM_INPUTCHANNELPOLARITY_BOTHEDGE
TIM5.ICPolarity_CH4=TIM_INPUTCHANNELPOLARITY_BOTHEDGE
TIM5.IPParameters=Channel-Input_Capture3_from_TI3,Channel-Input_Capture4_from_TI4,ICFilter_CH3,ICFilter_CH4,ICPolarity_CH3,ICPolarity_CH4,Period
TIM5.Period=0xFFFFFFFF
TIM8.Channel-Output\ Compare4\ No\ Output=TIM_CHANNEL_4
TIM8.Channel-PWM\ Generation1\ CH1\ CH1N=TIM_CHANNEL_1
TIM8.Channel-PWM\ Generation2\ CH2\ CH2N=TIM_CHANNEL_2
TIM8.Channel-PWM\ Generation3\ CH3\ CH3N=TIM_CHANNEL_3
TIM8.CounterMode=TIM_COUNTERMODE_CENTERALIGNED3
TIM8.DeadTime=TIM_1_8_DEADTIME_CLOCKS
TIM8.IPParameters=Channel-PWM Generation3 CH3 CH3N,Channel-PWM Generation2 CH2 CH2N,CounterMode,Period,OffStateRunMode,OffStateIDLEMode,DeadTime,OCMode_PWM-PWM Generation1 CH1 CH1N,OCMode_PWM-PWM Generation2 CH2 CH2N,OCMode_PWM-PWM Generation3 CH3 CH3N,Channel-Output Compare4 No Output,TIM_MasterOutputTrigger,Channel-PWM Generation1 CH1 CH1N,RepetitionCounter
TIM8.OCMode_PWM-PWM\ Generation1\ CH1\ CH1N=TIM_OCMODE_PWM2
TIM8.OCMode_PWM-PWM\ Generation2\ CH2\ CH2N=TIM_OCMODE_PWM2
TIM8.OCMode_PWM-PWM\ Generation3\ CH3\ CH3N=TIM_OCMODE_PWM2
TIM8.OffStateIDLEMode=TIM_OSSI_ENABLE
TIM8.OffStateRunMode=TIM_OSSR_ENABLE
TIM8.Period=TIM_1_8_PERIOD_CLOCKS
TIM8.RepetitionCounter=TIM_1_8_RCR
TIM8.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
UART4.IPParameters=VirtualMode
UART4.VirtualMode=Asynchronous
USB_DEVICE.APP_RX_DATA_SIZE-CDC_FS=64
USB_DEVICE.APP_TX_DATA_SIZE-CDC_FS=64
USB_DEVICE.CLASS_NAME_FS=CDC
USB_DEVICE.IPParameters=VirtualMode-CDC_FS,VirtualModeFS,CLASS_NAME_FS,MANUFACTURER_STRING-CDC_FS,PRODUCT_STRING_CDC_FS,VID-CDC_FS,PID_CDC_FS,SERIALNUMBER_STRING_CDC_FS,APP_RX_DATA_SIZE-CDC_FS,APP_TX_DATA_SIZE-CDC_FS
USB_DEVICE.MANUFACTURER_STRING-CDC_FS=ODrive Robotics
USB_DEVICE.PID_CDC_FS=0x0D32
USB_DEVICE.PRODUCT_STRING_CDC_FS=ODrive v3.3
USB_DEVICE.SERIALNUMBER_STRING_CDC_FS=000000000001
USB_DEVICE.VID-CDC_FS=0x1209
USB_DEVICE.VirtualMode-CDC_FS=Cdc
USB_DEVICE.VirtualModeFS=Cdc_FS
USB_OTG_FS.IPParameters=VirtualMode,vbus_sensing_enable
USB_OTG_FS.VirtualMode=Device_Only
USB_OTG_FS.vbus_sensing_enable=DISABLE
VP_FREERTOS_VS_ENABLE.Mode=Enabled
VP_FREERTOS_VS_ENABLE.Signal=FREERTOS_VS_ENABLE
VP_SYS_VS_tim14.Mode=TIM14
VP_SYS_VS_tim14.Signal=SYS_VS_tim14
VP_TIM13_VS_ClockSourceINT.Mode=Enable_Timer
VP_TIM13_VS_ClockSourceINT.Signal=TIM13_VS_ClockSourceINT
VP_TIM1_VS_ClockSourceINT.Mode=Internal
VP_TIM1_VS_ClockSourceINT.Signal=TIM1_VS_ClockSourceINT
VP_TIM1_VS_no_output4.Mode=Output Compare4 No Output
VP_TIM1_VS_no_output4.Signal=TIM1_VS_no_output4
VP_USB_DEVICE_VS_USB_DEVICE_CDC_FS.Mode=CDC_FS
VP_USB_DEVICE_VS_USB_DEVICE_CDC_FS.Signal=USB_DEVICE_VS_USB_DEVICE_CDC_FS
board=Odrive
@@ -0,0 +1,193 @@
/*
*****************************************************************************
**
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32F405RGTx Device with
** 1024KByte FLASH, 128KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20020000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x3C00; /* required amount of heap */
_Min_Stack_Size = 0x800; /* required amount of stack */
_heap_end_max = _estack - _Min_Stack_Size;
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 768K
NVM (r) : ORIGIN = 0x80C0000, LENGTH = 256K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.testdata)
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_siccmram = LOADADDR(.ccmram);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
* Oskar: Added NOLOAD to remove this section from .bin outputs
*/
.ccmram (NOLOAD):
{
. = ALIGN(4);
_sccmram = .; /* create a global symbol at ccmram start */
*(.ccmram)
*(.ccmram*)
. = ALIGN(4);
_eccmram = .; /* create a global symbol at ccmram end */
} >CCMRAM /*AT> FLASH*/
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,465 @@
/**
******************************************************************************
* File Name : ADC.c
* Description : This file provides code for the configuration
* of the ADC instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "adc.h"
#include "gpio.h"
#include "dma.h"
/* USER CODE BEGIN 0 */
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 1 \
|| HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 2
#include "prev_board_ver/adc_V3_2.c"
#elif HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 3 \
|| HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 4
#include "prev_board_ver/adc_V3_4.c"
#else
/* USER CODE END 0 */
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
ADC_HandleTypeDef hadc3;
DMA_HandleTypeDef hdma_adc1;
/* ADC1 init function */
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_6;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* ADC2 init function */
void MX_ADC2_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.ScanConvMode = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc2.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_13;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_10;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc2, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* ADC3 init function */
void MX_ADC3_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc3.Init.Resolution = ADC_RESOLUTION_12B;
hadc3.Init.ScanConvMode = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc3.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc3.Init.NbrOfConversion = 1;
hadc3.Init.DMAContinuousRequests = DISABLE;
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_12;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_11;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc3, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN10
PC1 ------> ADC1_IN11
PC2 ------> ADC1_IN12
PC3 ------> ADC1_IN13
PA4 ------> ADC1_IN4
PA5 ------> ADC1_IN5
PA6 ------> ADC1_IN6
PC5 ------> ADC1_IN15
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|M0_TEMP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = M1_TEMP_Pin|AUX_TEMP_Pin|VBUS_S_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
else if(adcHandle->Instance==ADC2)
{
/* USER CODE BEGIN ADC2_MspInit 0 */
/* USER CODE END ADC2_MspInit 0 */
/* ADC2 clock enable */
__HAL_RCC_ADC2_CLK_ENABLE();
/**ADC2 GPIO Configuration
PC0 ------> ADC2_IN10
PC1 ------> ADC2_IN11
PC2 ------> ADC2_IN12
PC3 ------> ADC2_IN13
PA4 ------> ADC2_IN4
PA5 ------> ADC2_IN5
PA6 ------> ADC2_IN6
PC5 ------> ADC2_IN15
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|M0_TEMP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = M1_TEMP_Pin|AUX_TEMP_Pin|VBUS_S_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN ADC2_MspInit 1 */
/* USER CODE END ADC2_MspInit 1 */
}
else if(adcHandle->Instance==ADC3)
{
/* USER CODE BEGIN ADC3_MspInit 0 */
/* USER CODE END ADC3_MspInit 0 */
/* ADC3 clock enable */
__HAL_RCC_ADC3_CLK_ENABLE();
/**ADC3 GPIO Configuration
PC0 ------> ADC3_IN10
PC1 ------> ADC3_IN11
PC2 ------> ADC3_IN12
PC3 ------> ADC3_IN13
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN ADC3_MspInit 1 */
/* USER CODE END ADC3_MspInit 1 */
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */
/* USER CODE END ADC1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN10
PC1 ------> ADC1_IN11
PC2 ------> ADC1_IN12
PC3 ------> ADC1_IN13
PA4 ------> ADC1_IN4
PA5 ------> ADC1_IN5
PA6 ------> ADC1_IN6
PC5 ------> ADC1_IN15
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|M0_TEMP_Pin);
HAL_GPIO_DeInit(GPIOA, M1_TEMP_Pin|AUX_TEMP_Pin|VBUS_S_Pin);
/* ADC1 DMA DeInit */
HAL_DMA_DeInit(adcHandle->DMA_Handle);
/* ADC1 interrupt Deinit */
/* USER CODE BEGIN ADC1:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC1:ADC_IRQn disable */
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
}
else if(adcHandle->Instance==ADC2)
{
/* USER CODE BEGIN ADC2_MspDeInit 0 */
/* USER CODE END ADC2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC2_CLK_DISABLE();
/**ADC2 GPIO Configuration
PC0 ------> ADC2_IN10
PC1 ------> ADC2_IN11
PC2 ------> ADC2_IN12
PC3 ------> ADC2_IN13
PA4 ------> ADC2_IN4
PA5 ------> ADC2_IN5
PA6 ------> ADC2_IN6
PC5 ------> ADC2_IN15
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|M0_TEMP_Pin);
HAL_GPIO_DeInit(GPIOA, M1_TEMP_Pin|AUX_TEMP_Pin|VBUS_S_Pin);
/* ADC2 interrupt Deinit */
/* USER CODE BEGIN ADC2:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC2:ADC_IRQn disable */
/* USER CODE BEGIN ADC2_MspDeInit 1 */
/* USER CODE END ADC2_MspDeInit 1 */
}
else if(adcHandle->Instance==ADC3)
{
/* USER CODE BEGIN ADC3_MspDeInit 0 */
/* USER CODE END ADC3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC3_CLK_DISABLE();
/**ADC3 GPIO Configuration
PC0 ------> ADC3_IN10
PC1 ------> ADC3_IN11
PC2 ------> ADC3_IN12
PC3 ------> ADC3_IN13
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin);
/* ADC3 interrupt Deinit */
/* USER CODE BEGIN ADC3:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC3:ADC_IRQn disable */
/* USER CODE BEGIN ADC3_MspDeInit 1 */
/* USER CODE END ADC3_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
#endif // END ADC Include
/* USER CODE END 1 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,177 @@
/**
******************************************************************************
* File Name : CAN.c
* Description : This file provides code for the configuration
* of the CAN instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "can.h"
#include "gpio.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
CAN_HandleTypeDef hcan1 = {
.Instance = CAN1,
.Init = {
.Prescaler = 8,
.Mode = CAN_MODE_NORMAL,
.SyncJumpWidth = CAN_SJW_4TQ,
.TimeSeg1 = CAN_BS1_16TQ,
.TimeSeg2 = CAN_BS2_4TQ,
.TimeTriggeredMode = DISABLE,
.AutoBusOff = ENABLE,
.AutoWakeUp = ENABLE,
.AutoRetransmission = ENABLE,
.ReceiveFifoLocked = DISABLE,
.TransmitFifoPriority = DISABLE,
}
};
/* CAN1 init function */
//void MX_CAN1_Init(void)
//{
//
// hcan1.Instance = CAN1;
// hcan1.Init.Prescaler = 8;
// hcan1.Init.Mode = CAN_MODE_NORMAL;
// hcan1.Init.SyncJumpWidth = CAN_SJW_4TQ;
// hcan1.Init.TimeSeg1 = CAN_BS1_16TQ;
// hcan1.Init.TimeSeg2 = CAN_BS2_4TQ;
// hcan1.Init.TimeTriggeredMode = DISABLE;
// hcan1.Init.AutoBusOff = ENABLE;
// hcan1.Init.AutoWakeUp = ENABLE;
// hcan1.Init.AutoRetransmission = ENABLE;
// hcan1.Init.ReceiveFifoLocked = DISABLE;
// hcan1.Init.TransmitFifoPriority = DISABLE;
// if (HAL_CAN_Init(&hcan1) != HAL_OK)
// {
// _Error_Handler(__FILE__, __LINE__);
// }
//
//}
//
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspInit 0 */
/* USER CODE END CAN1_MspInit 0 */
/* CAN1 clock enable */
__HAL_RCC_CAN1_CLK_ENABLE();
/**CAN1 GPIO Configuration
PB8 ------> CAN1_RX
PB9 ------> CAN1_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* CAN1 interrupt Init */
HAL_NVIC_SetPriority(CAN1_TX_IRQn, 9, 0);
HAL_NVIC_EnableIRQ(CAN1_TX_IRQn);
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 9, 0);
HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 9, 0);
HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 9, 0);
HAL_NVIC_EnableIRQ(CAN1_SCE_IRQn);
/* USER CODE BEGIN CAN1_MspInit 1 */
/* USER CODE END CAN1_MspInit 1 */
}
}
void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{
if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspDeInit 0 */
/* USER CODE END CAN1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_CAN1_CLK_DISABLE();
/**CAN1 GPIO Configuration
PB8 ------> CAN1_RX
PB9 ------> CAN1_TX
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);
/* CAN1 interrupt Deinit */
HAL_NVIC_DisableIRQ(CAN1_TX_IRQn);
HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);
HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn);
HAL_NVIC_DisableIRQ(CAN1_SCE_IRQn);
/* USER CODE BEGIN CAN1_MspDeInit 1 */
/* USER CODE END CAN1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,114 @@
/**
******************************************************************************
* File Name : dma.c
* Description : This file provides code for the configuration
* of all the requested memory to memory DMA transfers.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "dma.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/*----------------------------------------------------------------------------*/
/* Configure DMA */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* Enable DMA controller clock
*/
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 4, 0); // SPI RX - must have lower priority than SPI TX
// and higher priority than the control loop handler
HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
/* DMA1_Stream2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
/* DMA1_Stream4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
/* DMA1_Stream5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
/* DMA1_Stream6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
/* DMA1_Stream7_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 3, 0); // SPI TX - must have higher priority than SPI RX
// and higher priority than the control loop handler
HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn);
/* DMA2_Stream0_IRQn interrupt configuration */
// Dear STM, no we _don't_ want to fire an interrupt for this DMA
// (it's not possible to deselect this in CubeMX)
//HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 5, 0);
//HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,177 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
/* USER CODE END Variables */
osThreadId defaultTaskHandle;
const uint32_t stack_size_default_task = 2048; // Bytes
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void * argument);
extern void MX_USB_DEVICE_Init(void);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/* Hook prototypes */
void vApplicationIdleHook(void);
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName);
/* USER CODE BEGIN 2 */
__weak void vApplicationIdleHook( void )
{
/* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle
task. It is essential that code added to this hook function never attempts
to block in any way (for example, call xQueueReceive() with a block time
specified, or call vTaskDelay()). If the application makes use of the
vTaskDelete() API function (as this demo application does) then it is also
important that vApplicationIdleHook() is permitted to return to its calling
function, because it is the responsibility of the idle task to clean up
memory allocated by the kernel to any task that has since been deleted. */
}
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
__weak void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName)
{
/* Run time stack overflow checking is performed if
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
called if a stack overflow is detected. */
}
/* USER CODE END 4 */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* Create the thread(s) */
/* definition and creation of defaultTask */
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, stack_size_default_task / sizeof(StackType_t));
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
/* USER CODE BEGIN RTOS_THREADS */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
}
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void * argument)
{
/* init code for USB_DEVICE */
MX_USB_DEVICE_Init();
/* USER CODE BEGIN StartDefaultTask */
/* USER CODE END StartDefaultTask */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/* USER CODE END Application */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,136 @@
/**
******************************************************************************
* File Name : gpio.c
* Description : This file provides code for the configuration
* of all used GPIO pins.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "gpio.h"
/* USER CODE BEGIN 0 */
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 1 \
|| HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 2
#include "prev_board_ver/gpio_V3_2.c"
#elif HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 3 \
|| HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR == 4
#include "prev_board_ver/gpio_V3_4.c"
#else
/* USER CODE END 0 */
/*----------------------------------------------------------------------------*/
/* Configure GPIO */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, M0_nCS_Pin|M1_nCS_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(EN_GATE_GPIO_Port, EN_GATE_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PCPin PCPin */
GPIO_InitStruct.Pin = M0_nCS_Pin|M1_nCS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : PCPin PCPin PCPin */
GPIO_InitStruct.Pin = M1_ENC_Z_Pin|GPIO_5_Pin|M0_ENC_Z_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = EN_GATE_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(EN_GATE_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = nFAULT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(nFAULT_GPIO_Port, &GPIO_InitStruct);
}
/* USER CODE BEGIN 2 */
#endif // End GPIO Include
/* USER CODE END 2 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,186 @@
/**
******************************************************************************
* File Name : I2C.c
* Description : This file provides code for the configuration
* of the I2C instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "i2c.h"
#include "gpio.h"
#include "dma.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
I2C_HandleTypeDef hi2c1;
DMA_HandleTypeDef hdma_i2c1_rx;
DMA_HandleTypeDef hdma_i2c1_tx;
/* I2C1 init function */
void MX_I2C1_Init(uint8_t addr)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = addr << 1;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
if(i2cHandle->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspInit 0 */
/* USER CODE END I2C1_MspInit 0 */
/* I2C1 clock enable */
__HAL_RCC_I2C1_CLK_ENABLE();
/* I2C1 DMA Init */
/* I2C1_RX Init */
hdma_i2c1_rx.Instance = DMA1_Stream0;
hdma_i2c1_rx.Init.Channel = DMA_CHANNEL_1;
hdma_i2c1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_i2c1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_rx.Init.Mode = DMA_CIRCULAR;
hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_i2c1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_i2c1_rx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(i2cHandle,hdmarx,hdma_i2c1_rx);
/* I2C1_TX Init */
hdma_i2c1_tx.Instance = DMA1_Stream6;
hdma_i2c1_tx.Init.Channel = DMA_CHANNEL_1;
hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_i2c1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_i2c1_tx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(i2cHandle,hdmatx,hdma_i2c1_tx);
/* I2C1 interrupt Init */
HAL_NVIC_SetPriority(I2C1_EV_IRQn, 9, 0);
HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
HAL_NVIC_SetPriority(I2C1_ER_IRQn, 9, 0);
HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
/* USER CODE BEGIN I2C1_MspInit 1 */
/* USER CODE END I2C1_MspInit 1 */
}
}
void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle)
{
if(i2cHandle->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspDeInit 0 */
/* USER CODE END I2C1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_I2C1_CLK_DISABLE();
/**I2C1 GPIO Configuration
PB8 ------> I2C1_SCL
PB9 ------> I2C1_SDA
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);
/* I2C1 DMA DeInit */
HAL_DMA_DeInit(i2cHandle->hdmarx);
HAL_DMA_DeInit(i2cHandle->hdmatx);
/* I2C1 interrupt Deinit */
HAL_NVIC_DisableIRQ(I2C1_EV_IRQn);
HAL_NVIC_DisableIRQ(I2C1_ER_IRQn);
/* USER CODE BEGIN I2C1_MspDeInit 1 */
/* USER CODE END I2C1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,280 @@
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"
#include "cmsis_os.h"
#include "adc.h"
#include "can.h"
#include "dma.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "usb_device.h"
#include "gpio.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void MX_FREERTOS_Init(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* => Nope. We provide our own.
*
* @retval None
*/
//int main(void)
//{
#if 0
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_ADC2_Init();
// MX_CAN1_Init(); // CAN or I2C called in main.cpp instead
MX_TIM1_Init();
MX_TIM8_Init();
MX_TIM3_Init();
MX_TIM4_Init();
MX_SPI3_Init();
MX_ADC3_Init();
MX_TIM2_Init();
MX_UART4_Init();
MX_TIM5_Init();
MX_TIM13_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Call init function for freertos objects (in freertos.c) */
MX_FREERTOS_Init();
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
#endif
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
/**Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM14 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM14) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @param file: The file name as string.
* @param line: The line in file as a number.
* @retval None
*/
void _Error_Handler(char *file, int line)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
while(1) // TODO: do something more useful
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,390 @@
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
ADC_HandleTypeDef hadc3;
DMA_HandleTypeDef hdma_adc1;
/* ADC1 init function */
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_0;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* ADC2 init function */
void MX_ADC2_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.ScanConvMode = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc2.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_13;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_10;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc2, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* ADC3 init function */
void MX_ADC3_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc3.Init.Resolution = ADC_RESOLUTION_12B;
hadc3.Init.ScanConvMode = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc3.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc3.Init.NbrOfConversion = 1;
hadc3.Init.DMAContinuousRequests = DISABLE;
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_12;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_11;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc3, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN10
PC1 ------> ADC1_IN11
PC2 ------> ADC1_IN12
PC3 ------> ADC1_IN13
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA2 ------> ADC1_IN2
PA6 ------> ADC1_IN6
PC4 ------> ADC1_IN14
PC5 ------> ADC1_IN15
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|AUX_TEMP_Pin|M0_TEMP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = VBUS_S_Pin|M1_TEMP_Pin|AUX_I_Pin|AUX_V_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
else if(adcHandle->Instance==ADC2)
{
/* USER CODE BEGIN ADC2_MspInit 0 */
/* USER CODE END ADC2_MspInit 0 */
/* ADC2 clock enable */
__HAL_RCC_ADC2_CLK_ENABLE();
/**ADC2 GPIO Configuration
PC0 ------> ADC2_IN10
PC1 ------> ADC2_IN11
PC2 ------> ADC2_IN12
PC3 ------> ADC2_IN13
PA0-WKUP ------> ADC2_IN0
PA1 ------> ADC2_IN1
PA2 ------> ADC2_IN2
PA6 ------> ADC2_IN6
PC4 ------> ADC2_IN14
PC5 ------> ADC2_IN15
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|AUX_TEMP_Pin|M0_TEMP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = VBUS_S_Pin|M1_TEMP_Pin|AUX_I_Pin|AUX_V_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN ADC2_MspInit 1 */
/* USER CODE END ADC2_MspInit 1 */
}
else if(adcHandle->Instance==ADC3)
{
/* USER CODE BEGIN ADC3_MspInit 0 */
/* USER CODE END ADC3_MspInit 0 */
/* ADC3 clock enable */
__HAL_RCC_ADC3_CLK_ENABLE();
/**ADC3 GPIO Configuration
PC0 ------> ADC3_IN10
PC1 ------> ADC3_IN11
PC2 ------> ADC3_IN12
PC3 ------> ADC3_IN13
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN ADC3_MspInit 1 */
/* USER CODE END ADC3_MspInit 1 */
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */
/* USER CODE END ADC1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN10
PC1 ------> ADC1_IN11
PC2 ------> ADC1_IN12
PC3 ------> ADC1_IN13
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA2 ------> ADC1_IN2
PA6 ------> ADC1_IN6
PC4 ------> ADC1_IN14
PC5 ------> ADC1_IN15
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|AUX_TEMP_Pin|M0_TEMP_Pin);
HAL_GPIO_DeInit(GPIOA, VBUS_S_Pin|M1_TEMP_Pin|AUX_I_Pin|AUX_V_Pin);
/* ADC1 interrupt Deinit */
/* USER CODE BEGIN ADC1:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC1:ADC_IRQn disable */
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
}
else if(adcHandle->Instance==ADC2)
{
/* USER CODE BEGIN ADC2_MspDeInit 0 */
/* USER CODE END ADC2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC2_CLK_DISABLE();
/**ADC2 GPIO Configuration
PC0 ------> ADC2_IN10
PC1 ------> ADC2_IN11
PC2 ------> ADC2_IN12
PC3 ------> ADC2_IN13
PA0-WKUP ------> ADC2_IN0
PA1 ------> ADC2_IN1
PA2 ------> ADC2_IN2
PA6 ------> ADC2_IN6
PC4 ------> ADC2_IN14
PC5 ------> ADC2_IN15
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|AUX_TEMP_Pin|M0_TEMP_Pin);
HAL_GPIO_DeInit(GPIOA, VBUS_S_Pin|M1_TEMP_Pin|AUX_I_Pin|AUX_V_Pin);
/* ADC2 interrupt Deinit */
/* USER CODE BEGIN ADC2:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC2:ADC_IRQn disable */
/* USER CODE BEGIN ADC2_MspDeInit 1 */
/* USER CODE END ADC2_MspDeInit 1 */
}
else if(adcHandle->Instance==ADC3)
{
/* USER CODE BEGIN ADC3_MspDeInit 0 */
/* USER CODE END ADC3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC3_CLK_DISABLE();
/**ADC3 GPIO Configuration
PC0 ------> ADC3_IN10
PC1 ------> ADC3_IN11
PC2 ------> ADC3_IN12
PC3 ------> ADC3_IN13
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin);
/* ADC3 interrupt Deinit */
/* USER CODE BEGIN ADC3:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC3:ADC_IRQn disable */
/* USER CODE BEGIN ADC3_MspDeInit 1 */
/* USER CODE END ADC3_MspDeInit 1 */
}
}
@@ -0,0 +1,386 @@
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
ADC_HandleTypeDef hadc3;
DMA_HandleTypeDef hdma_adc1;
/* ADC1 init function */
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_6;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* ADC2 init function */
void MX_ADC2_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.ScanConvMode = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc2.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_13;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_10;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc2, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* ADC3 init function */
void MX_ADC3_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
ADC_InjectionConfTypeDef sConfigInjected;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc3.Init.Resolution = ADC_RESOLUTION_12B;
hadc3.Init.ScanConvMode = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc3.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc3.Init.NbrOfConversion = 1;
hadc3.Init.DMAContinuousRequests = DISABLE;
hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_12;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
*/
sConfigInjected.InjectedChannel = ADC_CHANNEL_11;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
if (HAL_ADCEx_InjectedConfigChannel(&hadc3, &sConfigInjected) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN10
PC1 ------> ADC1_IN11
PC2 ------> ADC1_IN12
PC3 ------> ADC1_IN13
PA4 ------> ADC1_IN4
PA5 ------> ADC1_IN5
PA6 ------> ADC1_IN6
PC4 ------> ADC1_IN14
PC5 ------> ADC1_IN15
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|AUX_TEMP_Pin|M0_TEMP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = M1_TEMP_Pin|AUX_I_Pin|VBUS_S_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
else if(adcHandle->Instance==ADC2)
{
/* USER CODE BEGIN ADC2_MspInit 0 */
/* USER CODE END ADC2_MspInit 0 */
/* ADC2 clock enable */
__HAL_RCC_ADC2_CLK_ENABLE();
/**ADC2 GPIO Configuration
PC0 ------> ADC2_IN10
PC1 ------> ADC2_IN11
PC2 ------> ADC2_IN12
PC3 ------> ADC2_IN13
PA4 ------> ADC2_IN4
PA5 ------> ADC2_IN5
PA6 ------> ADC2_IN6
PC4 ------> ADC2_IN14
PC5 ------> ADC2_IN15
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|AUX_TEMP_Pin|M0_TEMP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = M1_TEMP_Pin|AUX_I_Pin|VBUS_S_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN ADC2_MspInit 1 */
/* USER CODE END ADC2_MspInit 1 */
}
else if(adcHandle->Instance==ADC3)
{
/* USER CODE BEGIN ADC3_MspInit 0 */
/* USER CODE END ADC3_MspInit 0 */
/* ADC3 clock enable */
__HAL_RCC_ADC3_CLK_ENABLE();
/**ADC3 GPIO Configuration
PC0 ------> ADC3_IN10
PC1 ------> ADC3_IN11
PC2 ------> ADC3_IN12
PC3 ------> ADC3_IN13
*/
GPIO_InitStruct.Pin = M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN ADC3_MspInit 1 */
/* USER CODE END ADC3_MspInit 1 */
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */
/* USER CODE END ADC1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC1 GPIO Configuration
PC0 ------> ADC1_IN10
PC1 ------> ADC1_IN11
PC2 ------> ADC1_IN12
PC3 ------> ADC1_IN13
PA4 ------> ADC1_IN4
PA5 ------> ADC1_IN5
PA6 ------> ADC1_IN6
PC4 ------> ADC1_IN14
PC5 ------> ADC1_IN15
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|AUX_TEMP_Pin|M0_TEMP_Pin);
HAL_GPIO_DeInit(GPIOA, M1_TEMP_Pin|AUX_I_Pin|VBUS_S_Pin);
/* ADC1 interrupt Deinit */
/* USER CODE BEGIN ADC1:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC1:ADC_IRQn disable */
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
}
else if(adcHandle->Instance==ADC2)
{
/* USER CODE BEGIN ADC2_MspDeInit 0 */
/* USER CODE END ADC2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC2_CLK_DISABLE();
/**ADC2 GPIO Configuration
PC0 ------> ADC2_IN10
PC1 ------> ADC2_IN11
PC2 ------> ADC2_IN12
PC3 ------> ADC2_IN13
PA4 ------> ADC2_IN4
PA5 ------> ADC2_IN5
PA6 ------> ADC2_IN6
PC4 ------> ADC2_IN14
PC5 ------> ADC2_IN15
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin
|AUX_TEMP_Pin|M0_TEMP_Pin);
HAL_GPIO_DeInit(GPIOA, M1_TEMP_Pin|AUX_I_Pin|VBUS_S_Pin);
/* ADC2 interrupt Deinit */
/* USER CODE BEGIN ADC2:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC2:ADC_IRQn disable */
/* USER CODE BEGIN ADC2_MspDeInit 1 */
/* USER CODE END ADC2_MspDeInit 1 */
}
else if(adcHandle->Instance==ADC3)
{
/* USER CODE BEGIN ADC3_MspDeInit 0 */
/* USER CODE END ADC3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC3_CLK_DISABLE();
/**ADC3 GPIO Configuration
PC0 ------> ADC3_IN10
PC1 ------> ADC3_IN11
PC2 ------> ADC3_IN12
PC3 ------> ADC3_IN13
*/
HAL_GPIO_DeInit(GPIOC, M0_IB_Pin|M0_IC_Pin|M1_IC_Pin|M1_IB_Pin);
/* ADC3 interrupt Deinit */
/* USER CODE BEGIN ADC3:ADC_IRQn disable */
/**
* Uncomment the line below to disable the "ADC_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(ADC_IRQn); */
/* USER CODE END ADC3:ADC_IRQn disable */
/* USER CODE BEGIN ADC3_MspDeInit 1 */
/* USER CODE END ADC3_MspDeInit 1 */
}
}
@@ -0,0 +1,86 @@
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, M0_nCS_Pin|M1_nCS_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, M1_DC_CAL_Pin|M0_DC_CAL_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(EN_GATE_GPIO_Port, EN_GATE_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PCPin PCPin PCPin PCPin */
GPIO_InitStruct.Pin = M0_nCS_Pin|M1_nCS_Pin|M1_DC_CAL_Pin|M0_DC_CAL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : PAPin PAPin */
GPIO_InitStruct.Pin = GPIO_4_Pin|GPIO_2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = GPIO_3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIO_3_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = GPIO_1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIO_1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = EN_GATE_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(EN_GATE_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = M0_ENC_Z_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(M0_ENC_Z_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = nFAULT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(nFAULT_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = M1_ENC_Z_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(M1_ENC_Z_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
HAL_NVIC_SetPriority(EXTI4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}
@@ -0,0 +1,71 @@
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, M0_nCS_Pin|M1_nCS_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, M1_DC_CAL_Pin|M0_DC_CAL_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(EN_GATE_GPIO_Port, EN_GATE_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PCPin PCPin PCPin PCPin */
GPIO_InitStruct.Pin = M0_nCS_Pin|M1_nCS_Pin|M1_DC_CAL_Pin|M0_DC_CAL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = GPIO_3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIO_3_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PAPin PAPin */
GPIO_InitStruct.Pin = GPIO_4_Pin|M0_ENC_Z_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PBPin PBPin */
GPIO_InitStruct.Pin = GPIO_5_Pin|M1_ENC_Z_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = EN_GATE_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(EN_GATE_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = nFAULT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(nFAULT_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
}
@@ -0,0 +1,216 @@
/**
******************************************************************************
* File Name : SPI.c
* Description : This file provides code for the configuration
* of the SPI instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "spi.h"
#include "gpio.h"
#include "dma.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
SPI_HandleTypeDef hspi3;
DMA_HandleTypeDef hdma_spi3_tx;
DMA_HandleTypeDef hdma_spi3_rx;
/* SPI3 init function */
void MX_SPI3_Init(void)
{
hspi3.Instance = SPI3;
hspi3.Init.Mode = SPI_MODE_MASTER;
hspi3.Init.Direction = SPI_DIRECTION_2LINES;
hspi3.Init.DataSize = SPI_DATASIZE_16BIT;
hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi3.Init.NSS = SPI_NSS_SOFT;
hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi3.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(spiHandle->Instance==SPI3)
{
/* USER CODE BEGIN SPI3_MspInit 0 */
/* USER CODE END SPI3_MspInit 0 */
/* SPI3 clock enable */
__HAL_RCC_SPI3_CLK_ENABLE();
/**SPI3 GPIO Configuration
PC10 ------> SPI3_SCK
PC11 ------> SPI3_MISO
PC12 ------> SPI3_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // Idle clock and MOSI low.
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// MISO pull-up required for disconnect detection on SPI encoders with even parity
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* SPI3 DMA Init */
/* SPI3_TX Init */
hdma_spi3_tx.Instance = DMA1_Stream7;
hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0;
hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE;
if(spiHandle->Init.DataSize == SPI_DATASIZE_8BIT){
hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
} else {
hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
}
hdma_spi3_tx.Init.Mode = DMA_NORMAL;
hdma_spi3_tx.Init.Priority = DMA_PRIORITY_HIGH; // SPI TX must have higher priority than SPI RX
hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(spiHandle,hdmatx,hdma_spi3_tx);
/* SPI3_RX Init */
hdma_spi3_rx.Instance = DMA1_Stream0;
hdma_spi3_rx.Init.Channel = DMA_CHANNEL_0;
hdma_spi3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi3_rx.Init.MemInc = DMA_MINC_ENABLE;
if (spiHandle->Init.DataSize == SPI_DATASIZE_8BIT) {
hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
} else {
hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
}
hdma_spi3_rx.Init.Mode = DMA_NORMAL;
hdma_spi3_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_spi3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi3_rx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(spiHandle,hdmarx,hdma_spi3_rx);
/* SPI3 interrupt Init */
//HAL_NVIC_SetPriority(SPI3_IRQn, 3, 0);
//HAL_NVIC_EnableIRQ(SPI3_IRQn);
/* USER CODE BEGIN SPI3_MspInit 1 */
/* USER CODE END SPI3_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
if(spiHandle->Instance==SPI3)
{
/* USER CODE BEGIN SPI3_MspDeInit 0 */
/* USER CODE END SPI3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI3_CLK_DISABLE();
/**SPI3 GPIO Configuration
PC10 ------> SPI3_SCK
PC11 ------> SPI3_MISO
PC12 ------> SPI3_MOSI
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12);
/* SPI3 DMA DeInit */
HAL_DMA_DeInit(spiHandle->hdmatx);
HAL_DMA_DeInit(spiHandle->hdmarx);
/* SPI3 interrupt Deinit */
HAL_NVIC_DisableIRQ(SPI3_IRQn);
/* USER CODE BEGIN SPI3_MspDeInit 1 */
/* USER CODE END SPI3_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,102 @@
/**
******************************************************************************
* File Name : stm32f4xx_hal_msp.c
* Description : This file provides code for the MSP Initialization
* and de-Initialization codes.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
extern void _Error_Handler(char *, int);
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* Initializes the Global MSP.
*/
void HAL_MspInit(void)
{
/* USER CODE BEGIN MspInit 0 */
/* USER CODE END MspInit 0 */
__HAL_RCC_SYSCFG_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE();
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* System interrupt init*/
/* MemoryManagement_IRQn interrupt configuration */
HAL_NVIC_SetPriority(MemoryManagement_IRQn, 0, 0);
/* BusFault_IRQn interrupt configuration */
HAL_NVIC_SetPriority(BusFault_IRQn, 0, 0);
/* UsageFault_IRQn interrupt configuration */
HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0);
/* SVCall_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SVCall_IRQn, 3, 0);
/* DebugMonitor_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DebugMonitor_IRQn, 0, 0);
/* PendSV_IRQn interrupt configuration */
HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
/* USER CODE BEGIN MspInit 1 */
/* USER CODE END MspInit 1 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,158 @@
/**
******************************************************************************
* @file stm32f4xx_hal_timebase_TIM.c
* @brief HAL time base based on the hardware TIM.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "stm32f4xx_hal_tim.h"
/** @addtogroup STM32F7xx_HAL_Examples
* @{
*/
/** @addtogroup HAL_TimeBase
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim14;
uint32_t uwIncrementState = 0;
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
* @brief This function configures the TIM14 as a time base source.
* The time source is configured to have 1ms time base with a dedicated
* Tick interrupt priority.
* @note This function is called automatically at the beginning of program after
* reset by HAL_Init() or at any time when clock is configured, by HAL_RCC_ClockConfig().
* @param TickPriority: Tick interrupt priorty.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
RCC_ClkInitTypeDef clkconfig;
uint32_t uwTimclock = 0;
uint32_t uwPrescalerValue = 0;
uint32_t pFLatency;
/*Configure the TIM14 IRQ priority */
HAL_NVIC_SetPriority(TIM8_TRG_COM_TIM14_IRQn, TickPriority ,0);
/* Enable the TIM14 global Interrupt */
HAL_NVIC_EnableIRQ(TIM8_TRG_COM_TIM14_IRQn);
/* Enable TIM14 clock */
__HAL_RCC_TIM14_CLK_ENABLE();
/* Get clock configuration */
HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
/* Compute TIM14 clock */
uwTimclock = 2*HAL_RCC_GetPCLK1Freq();
/* Compute the prescaler value to have TIM14 counter clock equal to 1MHz */
uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000) - 1);
/* Initialize TIM14 */
htim14.Instance = TIM14;
/* Initialize TIMx peripheral as follow:
+ Period = [(TIM14CLK/1000) - 1]. to have a (1/1000) s time base.
+ Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
+ ClockDivision = 0
+ Counter direction = Up
*/
htim14.Init.Period = (1000000 / 1000) - 1;
htim14.Init.Prescaler = uwPrescalerValue;
htim14.Init.ClockDivision = 0;
htim14.Init.CounterMode = TIM_COUNTERMODE_UP;
if(HAL_TIM_Base_Init(&htim14) == HAL_OK)
{
/* Start the TIM time Base generation in interrupt mode */
return HAL_TIM_Base_Start_IT(&htim14);
}
/* Return function status */
return HAL_ERROR;
}
/**
* @brief Suspend Tick increment.
* @note Disable the tick increment by disabling TIM14 update interrupt.
* @param None
* @retval None
*/
void HAL_SuspendTick(void)
{
/* Disable TIM14 update Interrupt */
__HAL_TIM_DISABLE_IT(&htim14, TIM_IT_UPDATE);
}
/**
* @brief Resume Tick increment.
* @note Enable the tick increment by Enabling TIM14 update interrupt.
* @param None
* @retval None
*/
void HAL_ResumeTick(void)
{
/* Enable TIM14 Update interrupt */
__HAL_TIM_ENABLE_IT(&htim14, TIM_IT_UPDATE);
}
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,416 @@
/**
******************************************************************************
* @file stm32f4xx_it.c
* @brief Interrupt Service Routines.
******************************************************************************
*
* COPYRIGHT(c) 2018 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "stm32f4xx.h"
#include "stm32f4xx_it.h"
#include "cmsis_os.h"
#include <stdbool.h>
/* USER CODE BEGIN 0 */
#include <Drivers/STM32/stm32_system.h>
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
extern PCD_HandleTypeDef hpcd_USB_OTG_FS;
extern ADC_HandleTypeDef hadc1;
extern ADC_HandleTypeDef hadc2;
extern ADC_HandleTypeDef hadc3;
extern CAN_HandleTypeDef hcan1;
extern DMA_HandleTypeDef hdma_spi3_tx;
extern DMA_HandleTypeDef hdma_spi3_rx;
extern SPI_HandleTypeDef hspi3;
extern TIM_HandleTypeDef htim5;
extern TIM_HandleTypeDef htim8;
extern DMA_HandleTypeDef hdma_uart4_rx;
extern DMA_HandleTypeDef hdma_uart4_tx;
extern DMA_HandleTypeDef hdma_usart2_rx;
extern DMA_HandleTypeDef hdma_usart2_tx;
extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart2;
extern TIM_HandleTypeDef htim14;
/******************************************************************************/
/* Cortex-M4 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
COUNT_IRQ(NonMaskableInt_IRQn);
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
/* USER CODE END NonMaskableInt_IRQn 1 */
}
__attribute__((used))
void get_regs(void** stack_ptr) {
TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM
TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM
void* volatile r0 __attribute__((unused)) = stack_ptr[0];
void* volatile r1 __attribute__((unused)) = stack_ptr[1];
void* volatile r2 __attribute__((unused)) = stack_ptr[2];
void* volatile r3 __attribute__((unused)) = stack_ptr[3];
void* volatile r12 __attribute__((unused)) = stack_ptr[4];
void* volatile lr __attribute__((unused)) = stack_ptr[5]; // Link register
void* volatile pc __attribute__((unused)) = stack_ptr[6]; // Program counter
void* volatile psr __attribute__((unused)) = stack_ptr[7]; // Program status register
void* volatile cfsr __attribute__((unused)) = (void*)SCB->CFSR; // Configurable fault status register
void* volatile cpacr __attribute__((unused)) = (void*)SCB->CPACR;
void* volatile fpccr __attribute__((unused)) = (void*)FPU->FPCCR;
volatile bool preciserr __attribute__((unused)) = (uint32_t)cfsr & 0x200;
volatile bool ibuserr __attribute__((unused)) = (uint32_t)cfsr & 0x100;
volatile int stay_looping = 1;
while(stay_looping);
}
/**
* @brief This function handles Hard fault interrupt.
*/
__attribute__((naked))
void HardFault_Handler(void)
{
__asm(
" tst lr, #4 \n\t"
" ite eq \n\t"
" mrseq r0, msp \n\t"
" mrsne r0, psp \n\t"
" b get_regs \n\t"
);
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
COUNT_IRQ(MemoryManagement_IRQn);
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM
TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
/* USER CODE BEGIN MemoryManagement_IRQn 1 */
/* USER CODE END MemoryManagement_IRQn 1 */
}
/**
* @brief This function handles Pre-fetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
/* USER CODE BEGIN BusFault_IRQn 0 */
COUNT_IRQ(BusFault_IRQn);
/* USER CODE END BusFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM
TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM
/* USER CODE END W1_BusFault_IRQn 0 */
}
/* USER CODE BEGIN BusFault_IRQn 1 */
/* USER CODE END BusFault_IRQn 1 */
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
/* USER CODE BEGIN UsageFault_IRQn 0 */
COUNT_IRQ(UsageFault_IRQn);
/* USER CODE END UsageFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM
TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM
/* USER CODE END W1_UsageFault_IRQn 0 */
}
/* USER CODE BEGIN UsageFault_IRQn 1 */
/* USER CODE END UsageFault_IRQn 1 */
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
COUNT_IRQ(DebugMonitor_IRQn);
/* USER CODE END DebugMonitor_IRQn 0 */
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
COUNT_IRQ(SysTick_IRQn);
/* USER CODE END SysTick_IRQn 0 */
osSystickHandler();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/******************************************************************************/
/* STM32F4xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f4xx.s). */
/******************************************************************************/
/**
* @brief This function handles DMA1 stream0 global interrupt.
*/
void DMA1_Stream0_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream0_IRQn 0 */
COUNT_IRQ(DMA1_Stream0_IRQn);
/* USER CODE END DMA1_Stream0_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi3_rx);
/* USER CODE BEGIN DMA1_Stream0_IRQn 1 */
/* USER CODE END DMA1_Stream0_IRQn 1 */
}
/**
* @brief This function handles DMA1 stream2 global interrupt.
*/
void DMA1_Stream2_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream2_IRQn 0 */
COUNT_IRQ(DMA1_Stream2_IRQn);
/* USER CODE END DMA1_Stream2_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_uart4_rx);
/* USER CODE BEGIN DMA1_Stream2_IRQn 1 */
/* USER CODE END DMA1_Stream2_IRQn 1 */
}
/**
* @brief This function handles DMA1 stream4 global interrupt.
*/
void DMA1_Stream4_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream4_IRQn 0 */
COUNT_IRQ(DMA1_Stream4_IRQn);
/* USER CODE END DMA1_Stream4_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_uart4_tx);
/* USER CODE BEGIN DMA1_Stream4_IRQn 1 */
/* USER CODE END DMA1_Stream4_IRQn 1 */
}
/**
* @brief This function handles DMA1 stream5 global interrupt.
*/
void DMA1_Stream5_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream5_IRQn 0 */
COUNT_IRQ(DMA1_Stream5_IRQn);
/* USER CODE END DMA1_Stream5_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart2_rx);
/* USER CODE BEGIN DMA1_Stream5_IRQn 1 */
/* USER CODE END DMA1_Stream5_IRQn 1 */
}
/**
* @brief This function handles DMA1 stream6 global interrupt.
*/
void DMA1_Stream6_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream6_IRQn 0 */
COUNT_IRQ(DMA1_Stream6_IRQn);
/* USER CODE END DMA1_Stream6_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart2_tx);
/* USER CODE BEGIN DMA1_Stream6_IRQn 1 */
/* USER CODE END DMA1_Stream6_IRQn 1 */
}
/**
* @brief This function handles DMA1 stream7 global interrupt.
*/
void DMA1_Stream7_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream7_IRQn 0 */
COUNT_IRQ(DMA1_Stream7_IRQn);
/* USER CODE END DMA1_Stream7_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi3_tx);
/* USER CODE BEGIN DMA1_Stream7_IRQn 1 */
/* USER CODE END DMA1_Stream7_IRQn 1 */
}
/**
* @brief This function handles CAN1 TX interrupts.
*/
void CAN1_TX_IRQHandler(void)
{
/* USER CODE BEGIN CAN1_TX_IRQn 0 */
COUNT_IRQ(CAN1_TX_IRQn);
/* USER CODE END CAN1_TX_IRQn 0 */
HAL_CAN_IRQHandler(&hcan1);
/* USER CODE BEGIN CAN1_TX_IRQn 1 */
/* USER CODE END CAN1_TX_IRQn 1 */
}
/**
* @brief This function handles CAN1 RX0 interrupts.
*/
void CAN1_RX0_IRQHandler(void)
{
/* USER CODE BEGIN CAN1_RX0_IRQn 0 */
COUNT_IRQ(CAN1_RX0_IRQn);
/* USER CODE END CAN1_RX0_IRQn 0 */
HAL_CAN_IRQHandler(&hcan1);
/* USER CODE BEGIN CAN1_RX0_IRQn 1 */
/* USER CODE END CAN1_RX0_IRQn 1 */
}
/**
* @brief This function handles CAN1 RX1 interrupt.
*/
void CAN1_RX1_IRQHandler(void)
{
/* USER CODE BEGIN CAN1_RX1_IRQn 0 */
COUNT_IRQ(CAN1_RX1_IRQn);
/* USER CODE END CAN1_RX1_IRQn 0 */
HAL_CAN_IRQHandler(&hcan1);
/* USER CODE BEGIN CAN1_RX1_IRQn 1 */
/* USER CODE END CAN1_RX1_IRQn 1 */
}
/**
* @brief This function handles CAN1 SCE interrupt.
*/
void CAN1_SCE_IRQHandler(void)
{
/* USER CODE BEGIN CAN1_SCE_IRQn 0 */
COUNT_IRQ(CAN1_SCE_IRQn);
/* USER CODE END CAN1_SCE_IRQn 0 */
HAL_CAN_IRQHandler(&hcan1);
/* USER CODE BEGIN CAN1_SCE_IRQn 1 */
/* USER CODE END CAN1_SCE_IRQn 1 */
}
/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
/**
* @brief This function handles TIM8 trigger and commutation interrupts and TIM14 global interrupt.
*/
void TIM8_TRG_COM_TIM14_IRQHandler(void)
{
/* USER CODE BEGIN TIM8_TRG_COM_TIM14_IRQn 0 */
COUNT_IRQ(TIM8_TRG_COM_TIM14_IRQn);
/* USER CODE END TIM8_TRG_COM_TIM14_IRQn 0 */
HAL_TIM_IRQHandler(&htim8);
HAL_TIM_IRQHandler(&htim14);
/* USER CODE BEGIN TIM8_TRG_COM_TIM14_IRQn 1 */
/* USER CODE END TIM8_TRG_COM_TIM14_IRQn 1 */
}
/**
* @brief This function handles SPI3 global interrupt.
*/
void SPI3_IRQHandler(void)
{
/* USER CODE BEGIN SPI3_IRQn 0 */
COUNT_IRQ(SPI3_IRQn);
/* USER CODE END SPI3_IRQn 0 */
HAL_SPI_IRQHandler(&hspi3);
/* USER CODE BEGIN SPI3_IRQn 1 */
/* USER CODE END SPI3_IRQn 1 */
}
/**
* @brief This function handles UART4 global interrupt.
*/
void UART4_IRQHandler(void)
{
/* USER CODE BEGIN UART4_IRQn 0 */
COUNT_IRQ(UART4_IRQn);
/* USER CODE END UART4_IRQn 0 */
HAL_UART_IRQHandler(&huart4);
/* USER CODE BEGIN UART4_IRQn 1 */
/* USER CODE END UART4_IRQn 1 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,763 @@
/**
******************************************************************************
* @file system_stm32f4xx.c
* @author MCD Application Team
* @version V2.6.0
* @date 04-November-2016
* @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
*
* This file provides two functions and one global variable to be called from
* user application:
* - SystemInit(): This function is called at startup just after reset and
* before branch to main program. This call is made inside
* the "startup_stm32f4xx.s" file.
*
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
* by the user application to setup the SysTick
* timer or configure other parameters.
*
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
* be called whenever the core clock is changed
* during program execution.
*
*
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/** @addtogroup CMSIS
* @{
*/
/** @addtogroup stm32f4xx_system
* @{
*/
/** @addtogroup STM32F4xx_System_Private_Includes
* @{
*/
#include "stm32f4xx.h"
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)25000000) /*!< Default value of the External oscillator in Hz */
#endif /* HSE_VALUE */
#if !defined (HSI_VALUE)
#define HSI_VALUE ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/
#endif /* HSI_VALUE */
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_TypesDefinitions
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Defines
* @{
*/
/************************* Miscellaneous Configuration ************************/
/*!< Uncomment the following line if you need to use external SRAM or SDRAM as data memory */
#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\
|| defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx)
/* #define DATA_IN_ExtSRAM */
#endif /* STM32F40xxx || STM32F41xxx || STM32F42xxx || STM32F43xxx || STM32F469xx || STM32F479xx ||\
STM32F412Zx || STM32F412Vx */
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F446xx) || defined(STM32F469xx) || defined(STM32F479xx)
/* #define DATA_IN_ExtSDRAM */
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F446xx || STM32F469xx ||\
STM32F479xx */
/*!< Uncomment the following line if you need to relocate your vector Table in
Internal SRAM. */
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
/******************************************************************************/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Macros
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Variables
* @{
*/
/* This variable is updated in three ways:
1) by calling CMSIS function SystemCoreClockUpdate()
2) by calling HAL API function HAL_RCC_GetHCLKFreq()
3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
Note: If you use this function to configure the system clock; then there
is no need to call the 2 first functions listed above, since SystemCoreClock
variable is updated automatically.
*/
uint32_t SystemCoreClock = 16000000;
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
* @{
*/
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
static void SystemInit_ExtMemCtl(void);
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Functions
* @{
*/
/**
* @brief Setup the microcontroller system
* Initialize the FPU setting, vector table location and External memory
* configuration.
* @param None
* @retval None
*/
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset CFGR register */
RCC->CFGR = 0x00000000;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Disable all interrupts */
RCC->CIR = 0x00000000;
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
/**
* @brief Update SystemCoreClock variable according to Clock Register Values.
* The SystemCoreClock variable contains the core clock (HCLK), it can
* be used by the user application to setup the SysTick timer or configure
* other parameters.
*
* @note Each time the core clock (HCLK) changes, this function must be called
* to update SystemCoreClock variable value. Otherwise, any configuration
* based on this variable will be incorrect.
*
* @note - The system frequency computed by this function is not the real
* frequency in the chip. It is calculated based on the predefined
* constant and the selected clock source:
*
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
*
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
*
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
* or HSI_VALUE(*) multiplied/divided by the PLL factors.
*
* (*) HSI_VALUE is a constant defined in stm32f4xx_hal_conf.h file (default value
* 16 MHz) but the real value may vary depending on the variations
* in voltage and temperature.
*
* (**) HSE_VALUE is a constant defined in stm32f4xx_hal_conf.h file (its value
* depends on the application requirements), user has to ensure that HSE_VALUE
* is same as the real frequency of the crystal used. Otherwise, this function
* may have wrong result.
*
* - The result of this function could be not correct when using fractional
* value for HSE crystal.
*
* @param None
* @retval None
*/
void SystemCoreClockUpdate(void)
{
uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
/* Get SYSCLK source -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
case 0x00: /* HSI used as system clock source */
SystemCoreClock = HSI_VALUE;
break;
case 0x04: /* HSE used as system clock source */
SystemCoreClock = HSE_VALUE;
break;
case 0x08: /* PLL used as system clock source */
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
SYSCLK = PLL_VCO / PLL_P
*/
pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
if (pllsource != 0)
{
/* HSE used as PLL clock source */
pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
}
else
{
/* HSI used as PLL clock source */
pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
}
pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
SystemCoreClock = pllvco/pllp;
break;
default:
SystemCoreClock = HSI_VALUE;
break;
}
/* Compute HCLK frequency --------------------------------------------------*/
/* Get HCLK prescaler */
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
/* HCLK frequency */
SystemCoreClock >>= tmp;
}
#if defined (DATA_IN_ExtSRAM) && defined (DATA_IN_ExtSDRAM)
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx)
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f4xx.s before jump to main.
* This function configures the external memories (SRAM/SDRAM)
* This SRAM/SDRAM will be used as program data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
{
__IO uint32_t tmp = 0x00;
register uint32_t tmpreg = 0, timeout = 0xFFFF;
register __IO uint32_t index;
/* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface clock */
RCC->AHB1ENR |= 0x000001F8;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);
/* Connect PDx pins to FMC Alternate function */
GPIOD->AFR[0] = 0x00CCC0CC;
GPIOD->AFR[1] = 0xCCCCCCCC;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xAAAA0A8A;
/* Configure PDx pins speed to 100 MHz */
GPIOD->OSPEEDR = 0xFFFF0FCF;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOD->PUPDR = 0x00000000;
/* Connect PEx pins to FMC Alternate function */
GPIOE->AFR[0] = 0xC00CC0CC;
GPIOE->AFR[1] = 0xCCCCCCCC;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xAAAA828A;
/* Configure PEx pins speed to 100 MHz */
GPIOE->OSPEEDR = 0xFFFFC3CF;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* No pull-up, pull-down for PEx pins */
GPIOE->PUPDR = 0x00000000;
/* Connect PFx pins to FMC Alternate function */
GPIOF->AFR[0] = 0xCCCCCCCC;
GPIOF->AFR[1] = 0xCCCCCCCC;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xAA800AAA;
/* Configure PFx pins speed to 50 MHz */
GPIOF->OSPEEDR = 0xAA800AAA;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* No pull-up, pull-down for PFx pins */
GPIOF->PUPDR = 0x00000000;
/* Connect PGx pins to FMC Alternate function */
GPIOG->AFR[0] = 0xCCCCCCCC;
GPIOG->AFR[1] = 0xCCCCCCCC;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0xAAAAAAAA;
/* Configure PGx pins speed to 50 MHz */
GPIOG->OSPEEDR = 0xAAAAAAAA;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
/* Connect PHx pins to FMC Alternate function */
GPIOH->AFR[0] = 0x00C0CC00;
GPIOH->AFR[1] = 0xCCCCCCCC;
/* Configure PHx pins in Alternate function mode */
GPIOH->MODER = 0xAAAA08A0;
/* Configure PHx pins speed to 50 MHz */
GPIOH->OSPEEDR = 0xAAAA08A0;
/* Configure PHx pins Output type to push-pull */
GPIOH->OTYPER = 0x00000000;
/* No pull-up, pull-down for PHx pins */
GPIOH->PUPDR = 0x00000000;
/* Connect PIx pins to FMC Alternate function */
GPIOI->AFR[0] = 0xCCCCCCCC;
GPIOI->AFR[1] = 0x00000CC0;
/* Configure PIx pins in Alternate function mode */
GPIOI->MODER = 0x0028AAAA;
/* Configure PIx pins speed to 50 MHz */
GPIOI->OSPEEDR = 0x0028AAAA;
/* Configure PIx pins Output type to push-pull */
GPIOI->OTYPER = 0x00000000;
/* No pull-up, pull-down for PIx pins */
GPIOI->PUPDR = 0x00000000;
/*-- FMC Configuration -------------------------------------------------------*/
/* Enable the FMC interface clock */
RCC->AHB3ENR |= 0x00000001;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
FMC_Bank5_6->SDCR[0] = 0x000019E4;
FMC_Bank5_6->SDTR[0] = 0x01115351;
/* SDRAM initialization sequence */
/* Clock enable command */
FMC_Bank5_6->SDCMR = 0x00000011;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Delay */
for (index = 0; index<1000; index++);
/* PALL command */
FMC_Bank5_6->SDCMR = 0x00000012;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Auto refresh command */
FMC_Bank5_6->SDCMR = 0x00000073;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* MRD register program */
FMC_Bank5_6->SDCMR = 0x00046014;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Set refresh count */
tmpreg = FMC_Bank5_6->SDRTR;
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C<<1));
/* Disable write protection */
tmpreg = FMC_Bank5_6->SDCR[0];
FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF);
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001011;
FMC_Bank1->BTCR[3] = 0x00000201;
FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx */
#if defined(STM32F469xx) || defined(STM32F479xx)
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001091;
FMC_Bank1->BTCR[3] = 0x00110212;
FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif /* STM32F469xx || STM32F479xx */
(void)(tmp);
}
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */
#elif defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f4xx.s before jump to main.
* This function configures the external memories (SRAM/SDRAM)
* This SRAM/SDRAM will be used as program data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
{
__IO uint32_t tmp = 0x00;
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F446xx) || defined(STM32F469xx) || defined(STM32F479xx)
#if defined (DATA_IN_ExtSDRAM)
register uint32_t tmpreg = 0, timeout = 0xFFFF;
register __IO uint32_t index;
#if defined(STM32F446xx)
/* Enable GPIOA, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG interface
clock */
RCC->AHB1ENR |= 0x0000007D;
#else
/* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface
clock */
RCC->AHB1ENR |= 0x000001F8;
#endif /* STM32F446xx */
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);
#if defined(STM32F446xx)
/* Connect PAx pins to FMC Alternate function */
GPIOA->AFR[0] |= 0xC0000000;
GPIOA->AFR[1] |= 0x00000000;
/* Configure PDx pins in Alternate function mode */
GPIOA->MODER |= 0x00008000;
/* Configure PDx pins speed to 50 MHz */
GPIOA->OSPEEDR |= 0x00008000;
/* Configure PDx pins Output type to push-pull */
GPIOA->OTYPER |= 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOA->PUPDR |= 0x00000000;
/* Connect PCx pins to FMC Alternate function */
GPIOC->AFR[0] |= 0x00CC0000;
GPIOC->AFR[1] |= 0x00000000;
/* Configure PDx pins in Alternate function mode */
GPIOC->MODER |= 0x00000A00;
/* Configure PDx pins speed to 50 MHz */
GPIOC->OSPEEDR |= 0x00000A00;
/* Configure PDx pins Output type to push-pull */
GPIOC->OTYPER |= 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOC->PUPDR |= 0x00000000;
#endif /* STM32F446xx */
/* Connect PDx pins to FMC Alternate function */
GPIOD->AFR[0] = 0x000000CC;
GPIOD->AFR[1] = 0xCC000CCC;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xA02A000A;
/* Configure PDx pins speed to 50 MHz */
GPIOD->OSPEEDR = 0xA02A000A;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOD->PUPDR = 0x00000000;
/* Connect PEx pins to FMC Alternate function */
GPIOE->AFR[0] = 0xC00000CC;
GPIOE->AFR[1] = 0xCCCCCCCC;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xAAAA800A;
/* Configure PEx pins speed to 50 MHz */
GPIOE->OSPEEDR = 0xAAAA800A;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* No pull-up, pull-down for PEx pins */
GPIOE->PUPDR = 0x00000000;
/* Connect PFx pins to FMC Alternate function */
GPIOF->AFR[0] = 0xCCCCCCCC;
GPIOF->AFR[1] = 0xCCCCCCCC;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xAA800AAA;
/* Configure PFx pins speed to 50 MHz */
GPIOF->OSPEEDR = 0xAA800AAA;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* No pull-up, pull-down for PFx pins */
GPIOF->PUPDR = 0x00000000;
/* Connect PGx pins to FMC Alternate function */
GPIOG->AFR[0] = 0xCCCCCCCC;
GPIOG->AFR[1] = 0xCCCCCCCC;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0xAAAAAAAA;
/* Configure PGx pins speed to 50 MHz */
GPIOG->OSPEEDR = 0xAAAAAAAA;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx)
/* Connect PHx pins to FMC Alternate function */
GPIOH->AFR[0] = 0x00C0CC00;
GPIOH->AFR[1] = 0xCCCCCCCC;
/* Configure PHx pins in Alternate function mode */
GPIOH->MODER = 0xAAAA08A0;
/* Configure PHx pins speed to 50 MHz */
GPIOH->OSPEEDR = 0xAAAA08A0;
/* Configure PHx pins Output type to push-pull */
GPIOH->OTYPER = 0x00000000;
/* No pull-up, pull-down for PHx pins */
GPIOH->PUPDR = 0x00000000;
/* Connect PIx pins to FMC Alternate function */
GPIOI->AFR[0] = 0xCCCCCCCC;
GPIOI->AFR[1] = 0x00000CC0;
/* Configure PIx pins in Alternate function mode */
GPIOI->MODER = 0x0028AAAA;
/* Configure PIx pins speed to 50 MHz */
GPIOI->OSPEEDR = 0x0028AAAA;
/* Configure PIx pins Output type to push-pull */
GPIOI->OTYPER = 0x00000000;
/* No pull-up, pull-down for PIx pins */
GPIOI->PUPDR = 0x00000000;
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */
/*-- FMC Configuration -------------------------------------------------------*/
/* Enable the FMC interface clock */
RCC->AHB3ENR |= 0x00000001;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
/* Configure and enable SDRAM bank1 */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCR[0] = 0x00001954;
#else
FMC_Bank5_6->SDCR[0] = 0x000019E4;
#endif /* STM32F446xx */
FMC_Bank5_6->SDTR[0] = 0x01115351;
/* SDRAM initialization sequence */
/* Clock enable command */
FMC_Bank5_6->SDCMR = 0x00000011;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Delay */
for (index = 0; index<1000; index++);
/* PALL command */
FMC_Bank5_6->SDCMR = 0x00000012;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Auto refresh command */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCMR = 0x000000F3;
#else
FMC_Bank5_6->SDCMR = 0x00000073;
#endif /* STM32F446xx */
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* MRD register program */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCMR = 0x00044014;
#else
FMC_Bank5_6->SDCMR = 0x00046014;
#endif /* STM32F446xx */
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Set refresh count */
tmpreg = FMC_Bank5_6->SDRTR;
#if defined(STM32F446xx)
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000050C<<1));
#else
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C<<1));
#endif /* STM32F446xx */
/* Disable write protection */
tmpreg = FMC_Bank5_6->SDCR[0];
FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF);
#endif /* DATA_IN_ExtSDRAM */
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F446xx || STM32F469xx || STM32F479xx */
#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\
|| defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx)
#if defined(DATA_IN_ExtSRAM)
/*-- GPIOs Configuration -----------------------------------------------------*/
/* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
RCC->AHB1ENR |= 0x00000078;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);
/* Connect PDx pins to FMC Alternate function */
GPIOD->AFR[0] = 0x00CCC0CC;
GPIOD->AFR[1] = 0xCCCCCCCC;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xAAAA0A8A;
/* Configure PDx pins speed to 100 MHz */
GPIOD->OSPEEDR = 0xFFFF0FCF;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOD->PUPDR = 0x00000000;
/* Connect PEx pins to FMC Alternate function */
GPIOE->AFR[0] = 0xC00CC0CC;
GPIOE->AFR[1] = 0xCCCCCCCC;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xAAAA828A;
/* Configure PEx pins speed to 100 MHz */
GPIOE->OSPEEDR = 0xFFFFC3CF;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* No pull-up, pull-down for PEx pins */
GPIOE->PUPDR = 0x00000000;
/* Connect PFx pins to FMC Alternate function */
GPIOF->AFR[0] = 0x00CCCCCC;
GPIOF->AFR[1] = 0xCCCC0000;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xAA000AAA;
/* Configure PFx pins speed to 100 MHz */
GPIOF->OSPEEDR = 0xFF000FFF;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* No pull-up, pull-down for PFx pins */
GPIOF->PUPDR = 0x00000000;
/* Connect PGx pins to FMC Alternate function */
GPIOG->AFR[0] = 0x00CCCCCC;
GPIOG->AFR[1] = 0x000000C0;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0x00085AAA;
/* Configure PGx pins speed to 100 MHz */
GPIOG->OSPEEDR = 0x000CAFFF;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
/*-- FMC/FSMC Configuration --------------------------------------------------*/
/* Enable the FMC/FSMC interface clock */
RCC->AHB3ENR |= 0x00000001;
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001011;
FMC_Bank1->BTCR[3] = 0x00000201;
FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx */
#if defined(STM32F469xx) || defined(STM32F479xx)
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001091;
FMC_Bank1->BTCR[3] = 0x00110212;
FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif /* STM32F469xx || STM32F479xx */
#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx)|| defined(STM32F417xx)\
|| defined(STM32F412Zx) || defined(STM32F412Vx)
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN);
/* Configure and enable Bank1_SRAM2 */
FSMC_Bank1->BTCR[2] = 0x00001011;
FSMC_Bank1->BTCR[3] = 0x00000201;
FSMC_Bank1E->BWTR[2] = 0x0FFFFFFF;
#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || STM32F412Zx || STM32F412Vx */
#endif /* DATA_IN_ExtSRAM */
#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || STM32F427xx || STM32F437xx ||\
STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx || STM32F412Zx || STM32F412Vx */
(void)(tmp);
}
#endif /* DATA_IN_ExtSRAM && DATA_IN_ExtSDRAM */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,729 @@
/**
******************************************************************************
* File Name : TIM.c
* Description : This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "tim.h"
#include "gpio.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim4;
TIM_HandleTypeDef htim5;
TIM_HandleTypeDef htim8;
TIM_HandleTypeDef htim13;
/* TIM1 init function */
void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
htim1.Init.Period = TIM_1_8_PERIOD_CLOCKS;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = TIM_1_8_RCR;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_OC_Init(&htim1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.OCMode = TIM_OCMODE_TIMING;
if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = TIM_1_8_DEADTIME_CLOCKS;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_TIM_MspPostInit(&htim1);
}
/* TIM2 init function */
void MX_TIM2_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
htim2.Init.Period = TIM_APB1_PERIOD_CLOCKS;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.Pulse = TIM_APB1_PERIOD_CLOCKS+1;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_TIM_MspPostInit(&htim2);
}
/* TIM3 init function */
void MX_TIM3_Init(void)
{
TIM_Encoder_InitTypeDef sConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 0;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xffff;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter = 4;
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sConfig.IC2Filter = 4;
if (HAL_TIM_Encoder_Init(&htim3, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* TIM4 init function */
void MX_TIM4_Init(void)
{
TIM_Encoder_InitTypeDef sConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 0xffff;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter = 4;
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sConfig.IC2Filter = 4;
if (HAL_TIM_Encoder_Init(&htim4, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* TIM5 init function */
void MX_TIM5_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
htim5.Instance = TIM5;
htim5.Init.Prescaler = 0;
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = 0xFFFFFFFF;
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_IC_Init(&htim5) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 15;
if (HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, TIM_CHANNEL_3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, TIM_CHANNEL_4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* TIM8 init function */
void MX_TIM8_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
htim8.Instance = TIM8;
htim8.Init.Prescaler = 0;
htim8.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
htim8.Init.Period = TIM_1_8_PERIOD_CLOCKS;
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = TIM_1_8_RCR;
if (HAL_TIM_PWM_Init(&htim8) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = TIM_1_8_DEADTIME_CLOCKS;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_TIM_MspPostInit(&htim8);
}
/* TIM13 init function */
void MX_TIM13_Init(void)
{
htim13.Instance = TIM13;
htim13.Init.Prescaler = 0;
htim13.Init.CounterMode = TIM_COUNTERMODE_UP;
htim13.Init.Period = (2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR+1)) * ((float)TIM_APB1_CLOCK_HZ / (float)TIM_1_8_CLOCK_HZ) - 1;
htim13.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_Base_Init(&htim13) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* TIM1 clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
/* USER CODE BEGIN TIM1_MspInit 1 */
/* USER CODE END TIM1_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM13)
{
/* USER CODE BEGIN TIM13_MspInit 0 */
/* USER CODE END TIM13_MspInit 0 */
/* TIM13 clock enable */
__HAL_RCC_TIM13_CLK_ENABLE();
/* USER CODE BEGIN TIM13_MspInit 1 */
/* USER CODE END TIM13_MspInit 1 */
}
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle)
{
if(tim_pwmHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspInit 0 */
/* USER CODE END TIM2_MspInit 0 */
/* TIM2 clock enable */
__HAL_RCC_TIM2_CLK_ENABLE();
/* USER CODE BEGIN TIM2_MspInit 1 */
/* USER CODE END TIM2_MspInit 1 */
}
else if(tim_pwmHandle->Instance==TIM8)
{
/* USER CODE BEGIN TIM8_MspInit 0 */
/* USER CODE END TIM8_MspInit 0 */
/* TIM8 clock enable */
__HAL_RCC_TIM8_CLK_ENABLE();
/* USER CODE BEGIN TIM8_MspInit 1 */
/* USER CODE END TIM8_MspInit 1 */
}
}
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
{
if(tim_encoderHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
/* USER CODE END TIM3_MspInit 0 */
/* TIM3 clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
else if(tim_encoderHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspInit 0 */
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
/* USER CODE BEGIN TIM4_MspInit 1 */
/* USER CODE END TIM4_MspInit 1 */
}
}
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle)
{
if(tim_icHandle->Instance==TIM5)
{
/* USER CODE BEGIN TIM5_MspInit 0 */
/* USER CODE END TIM5_MspInit 0 */
/* TIM5 clock enable */
__HAL_RCC_TIM5_CLK_ENABLE();
/* TIM5 interrupt Init */
HAL_NVIC_SetPriority(TIM5_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM5_IRQn);
/* USER CODE BEGIN TIM5_MspInit 1 */
/* USER CODE END TIM5_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(timHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspPostInit 0 */
/* USER CODE END TIM1_MspPostInit 0 */
/**TIM1 GPIO Configuration
PB13 ------> TIM1_CH1N
PB14 ------> TIM1_CH2N
PB15 ------> TIM1_CH3N
PA8 ------> TIM1_CH1
PA9 ------> TIM1_CH2
PA10 ------> TIM1_CH3
*/
GPIO_InitStruct.Pin = M0_AL_Pin|M0_BL_Pin|M0_CL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = M0_AH_Pin|M0_BH_Pin|M0_CH_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM1_MspPostInit 1 */
/* USER CODE END TIM1_MspPostInit 1 */
}
else if(timHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspPostInit 0 */
/* USER CODE END TIM2_MspPostInit 0 */
/**TIM2 GPIO Configuration
PB10 ------> TIM2_CH3
PB11 ------> TIM2_CH4
*/
GPIO_InitStruct.Pin = AUX_L_Pin|AUX_H_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM2_MspPostInit 1 */
/* USER CODE END TIM2_MspPostInit 1 */
}
else if(timHandle->Instance==TIM8)
{
/* USER CODE BEGIN TIM8_MspPostInit 0 */
/* USER CODE END TIM8_MspPostInit 0 */
/**TIM8 GPIO Configuration
PA7 ------> TIM8_CH1N
PB0 ------> TIM8_CH2N
PB1 ------> TIM8_CH3N
PC6 ------> TIM8_CH1
PC7 ------> TIM8_CH2
PC8 ------> TIM8_CH3
*/
GPIO_InitStruct.Pin = M1_AL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF3_TIM8;
HAL_GPIO_Init(M1_AL_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = M1_BL_Pin|M1_CL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF3_TIM8;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = M1_AH_Pin|M1_BH_Pin|M1_CH_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF3_TIM8;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN TIM8_MspPostInit 1 */
/* USER CODE END TIM8_MspPostInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspDeInit 0 */
/* USER CODE END TIM1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM1_CLK_DISABLE();
/* TIM1 interrupt Deinit */
/* USER CODE BEGIN TIM1_MspDeInit 1 */
/* USER CODE END TIM1_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM13)
{
/* USER CODE BEGIN TIM13_MspDeInit 0 */
/* USER CODE END TIM13_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM13_CLK_DISABLE();
/* TIM13 interrupt Deinit */
/* USER CODE BEGIN TIM13:TIM8_UP_TIM13_IRQn disable */
/**
* Uncomment the line below to disable the "TIM8_UP_TIM13_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(TIM8_UP_TIM13_IRQn); */
/* USER CODE END TIM13:TIM8_UP_TIM13_IRQn disable */
/* USER CODE BEGIN TIM13_MspDeInit 1 */
/* USER CODE END TIM13_MspDeInit 1 */
}
}
void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef* tim_pwmHandle)
{
if(tim_pwmHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspDeInit 0 */
/* USER CODE END TIM2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM2_CLK_DISABLE();
/* USER CODE BEGIN TIM2_MspDeInit 1 */
/* USER CODE END TIM2_MspDeInit 1 */
}
else if(tim_pwmHandle->Instance==TIM8)
{
/* USER CODE BEGIN TIM8_MspDeInit 0 */
/* USER CODE END TIM8_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM8_CLK_DISABLE();
/* TIM8 interrupt Deinit */
/* USER CODE BEGIN TIM8:TIM8_UP_TIM13_IRQn disable */
/**
* Uncomment the line below to disable the "TIM8_UP_TIM13_IRQn" interrupt
* Be aware, disabling shared interrupt may affect other IPs
*/
/* HAL_NVIC_DisableIRQ(TIM8_UP_TIM13_IRQn); */
/* USER CODE END TIM8:TIM8_UP_TIM13_IRQn disable */
/* USER CODE BEGIN TIM8_MspDeInit 1 */
/* USER CODE END TIM8_MspDeInit 1 */
}
}
void HAL_TIM_Encoder_MspDeInit(TIM_HandleTypeDef* tim_encoderHandle)
{
if(tim_encoderHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspDeInit 0 */
/* USER CODE END TIM3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM3_CLK_DISABLE();
/**TIM3 GPIO Configuration
PB4 ------> TIM3_CH1
PB5 ------> TIM3_CH2
*/
HAL_GPIO_DeInit(GPIOB, M0_ENC_A_Pin|M0_ENC_B_Pin);
/* USER CODE BEGIN TIM3_MspDeInit 1 */
/* USER CODE END TIM3_MspDeInit 1 */
}
else if(tim_encoderHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspDeInit 0 */
/* USER CODE END TIM4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM4_CLK_DISABLE();
/**TIM4 GPIO Configuration
PB6 ------> TIM4_CH1
PB7 ------> TIM4_CH2
*/
HAL_GPIO_DeInit(GPIOB, M1_ENC_A_Pin|M1_ENC_B_Pin);
/* USER CODE BEGIN TIM4_MspDeInit 1 */
/* USER CODE END TIM4_MspDeInit 1 */
}
}
void HAL_TIM_IC_MspDeInit(TIM_HandleTypeDef* tim_icHandle)
{
if(tim_icHandle->Instance==TIM5)
{
/* USER CODE BEGIN TIM5_MspDeInit 0 */
/* USER CODE END TIM5_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM5_CLK_DISABLE();
/**TIM5 GPIO Configuration
PA2 ------> TIM5_CH3
PA3 ------> TIM5_CH4
*/
HAL_GPIO_DeInit(GPIOA, GPIO_3_Pin|GPIO_4_Pin);
/* TIM5 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM5_IRQn);
/* USER CODE BEGIN TIM5_MspDeInit 1 */
/* USER CODE END TIM5_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,267 @@
/**
******************************************************************************
* File Name : USART.c
* Description : This file provides code for the configuration
* of the USART instances.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usart.h"
#include "gpio.h"
#include "dma.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
UART_HandleTypeDef huart4;
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_uart4_rx;
DMA_HandleTypeDef hdma_uart4_tx;
DMA_HandleTypeDef hdma_usart2_rx;
DMA_HandleTypeDef hdma_usart2_tx;
/* UART4 init function */
void MX_UART4_Init(void)
{
huart4.Instance = UART4;
//huart4.Init.BaudRate = 115200; // Provisionally this can be changed to 921600 for faster transfers, the low power Arduinos will not keep up.
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* USART2 init function */
void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
//huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspInit 0 */
/* USER CODE END UART4_MspInit 0 */
/* UART4 clock enable */
__HAL_RCC_UART4_CLK_ENABLE();
/* UART4 DMA Init */
/* UART4_RX Init */
hdma_uart4_rx.Instance = DMA1_Stream2;
hdma_uart4_rx.Init.Channel = DMA_CHANNEL_4;
hdma_uart4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_uart4_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_uart4_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_uart4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_uart4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_uart4_rx.Init.Mode = DMA_CIRCULAR;
hdma_uart4_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_uart4_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_uart4_rx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(uartHandle,hdmarx,hdma_uart4_rx);
/* UART4_TX Init */
hdma_uart4_tx.Instance = DMA1_Stream4;
hdma_uart4_tx.Init.Channel = DMA_CHANNEL_4;
hdma_uart4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_uart4_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_uart4_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_uart4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_uart4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_uart4_tx.Init.Mode = DMA_NORMAL;
hdma_uart4_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_uart4_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_uart4_tx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(uartHandle,hdmatx,hdma_uart4_tx);
/* UART4 interrupt Init */
HAL_NVIC_SetPriority(UART4_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(UART4_IRQn);
/* USER CODE BEGIN UART4_MspInit 1 */
/* USER CODE END UART4_MspInit 1 */
}
else if(uartHandle->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspInit 0 */
/* USER CODE END USART2_MspInit 0 */
/* USART2 clock enable */
__HAL_RCC_USART2_CLK_ENABLE();
/* USART2 DMA Init */
/* USART2_RX Init */
hdma_usart2_rx.Instance = DMA1_Stream5;
hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);
/* USART2_TX Init */
hdma_usart2_tx.Instance = DMA1_Stream6;
hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4;
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_tx.Init.Mode = DMA_NORMAL;
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(uartHandle,hdmatx,hdma_usart2_tx);
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 10, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspInit 1 */
/* USER CODE END USART2_MspInit 1 */
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspDeInit 0 */
/* USER CODE END UART4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_UART4_CLK_DISABLE();
/* UART4 DMA DeInit */
HAL_DMA_DeInit(uartHandle->hdmarx);
HAL_DMA_DeInit(uartHandle->hdmatx);
/* UART4 interrupt Deinit */
HAL_NVIC_DisableIRQ(UART4_IRQn);
/* USER CODE BEGIN UART4_MspDeInit 1 */
/* USER CODE END UART4_MspDeInit 1 */
}
else if(uartHandle->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspDeInit 0 */
/* USER CODE END USART2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART2_CLK_DISABLE();
/* USART2 DMA DeInit */
HAL_DMA_DeInit(uartHandle->hdmarx);
HAL_DMA_DeInit(uartHandle->hdmatx);
/* USART2 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspDeInit 1 */
/* USER CODE END USART2_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,121 @@
/**
******************************************************************************
* @file : usb_device.c
* @version : v1.0_Cube
* @brief : This file implements the USB Device
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usb_device.h"
#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_cdc.h"
#include "usbd_cdc_if.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USB Device Core handle declaration. */
USBD_HandleTypeDef hUsbDeviceFS;
/*
* -- Insert your variables declaration here --
*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/*
* -- Insert your external function declaration here --
*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* Init USB device Library, add supported class and start the library
* @retval None
*/
void MX_USB_DEVICE_Init(void)
{
/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */
/* USER CODE END USB_DEVICE_Init_PreTreatment */
/* Init Device Library, add supported class and start the library. */
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);
USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);
USBD_Start(&hUsbDeviceFS);
/* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */
/* USER CODE END USB_DEVICE_Init_PostTreatment */
}
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,343 @@
/**
******************************************************************************
* @file : usbd_cdc_if.c
* @version : v1.0_Cube
* @brief : Usb device for Virtual Com Port.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usbd_cdc_if.h"
/* USER CODE BEGIN INCLUDE */
#include "cmsis_os.h"
#include <communication/interface_usb.h>
#include <freertos_vars.h>
/* USER CODE END INCLUDE */
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
* @brief Usb device library.
* @{
*/
/** @addtogroup USBD_CDC_IF
* @{
*/
/** @defgroup USBD_CDC_IF_Private_TypesDefinitions USBD_CDC_IF_Private_TypesDefinitions
* @brief Private types.
* @{
*/
/* USER CODE BEGIN PRIVATE_TYPES */
/* USER CODE END PRIVATE_TYPES */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Private_Defines USBD_CDC_IF_Private_Defines
* @brief Private defines.
* @{
*/
/* USER CODE BEGIN PRIVATE_DEFINES */
/* USER CODE END PRIVATE_DEFINES */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Private_Macros USBD_CDC_IF_Private_Macros
* @brief Private macros.
* @{
*/
/* USER CODE BEGIN PRIVATE_MACRO */
/* USER CODE END PRIVATE_MACRO */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Private_Variables USBD_CDC_IF_Private_Variables
* @brief Private variables.
* @{
*/
/* USER CODE BEGIN PRIVATE_VARIABLES */
/* USER CODE END PRIVATE_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Exported_Variables USBD_CDC_IF_Exported_Variables
* @brief Public variables.
* @{
*/
extern USBD_HandleTypeDef hUsbDeviceFS;
/* USER CODE BEGIN EXPORTED_VARIABLES */
/* USER CODE END EXPORTED_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_CDC_IF_Private_FunctionPrototypes USBD_CDC_IF_Private_FunctionPrototypes
* @brief Private functions declaration.
* @{
*/
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, uint8_t endpoint_pair);
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
/**
* @}
*/
USBD_CDC_ItfTypeDef USBD_Interface_fops_FS =
{
CDC_Init_FS,
CDC_DeInit_FS,
CDC_Control_FS,
CDC_Receive_FS
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes the CDC media low layer over the FS USB IP
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CDC_Init_FS(void)
{
/* USER CODE BEGIN 3 */
/* Set Application Buffers */
osMessagePut(usb_event_queue, 1, 0);
return (USBD_OK);
/* USER CODE END 3 */
}
/**
* @brief DeInitializes the CDC media low layer
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CDC_DeInit_FS(void)
{
/* USER CODE BEGIN 4 */
osMessagePut(usb_event_queue, 2, 0);
return (USBD_OK);
/* USER CODE END 4 */
}
/**
* @brief Manage the CDC class requests
* @param cmd: Command code
* @param pbuf: Buffer containing command data (request parameters)
* @param length: Number of data to be sent (in bytes)
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
{
/* USER CODE BEGIN 5 */
switch (cmd)
{
case CDC_SEND_ENCAPSULATED_COMMAND:
break;
case CDC_GET_ENCAPSULATED_RESPONSE:
break;
case CDC_SET_COMM_FEATURE:
break;
case CDC_GET_COMM_FEATURE:
break;
case CDC_CLEAR_COMM_FEATURE:
break;
/*******************************************************************************/
/* Line Coding Structure */
/*-----------------------------------------------------------------------------*/
/* Offset | Field | Size | Value | Description */
/* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/
/* 4 | bCharFormat | 1 | Number | Stop bits */
/* 0 - 1 Stop bit */
/* 1 - 1.5 Stop bits */
/* 2 - 2 Stop bits */
/* 5 | bParityType | 1 | Number | Parity */
/* 0 - None */
/* 1 - Odd */
/* 2 - Even */
/* 3 - Mark */
/* 4 - Space */
/* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */
/*******************************************************************************/
case CDC_SET_LINE_CODING:
break;
case CDC_GET_LINE_CODING:
pbuf[0] = (uint8_t)(115200);
pbuf[1] = (uint8_t)(115200 >> 8);
pbuf[2] = (uint8_t)(115200 >> 16);
pbuf[3] = (uint8_t)(115200 >> 24);
pbuf[4] = 0; // stop bits (1)
pbuf[5] = 0; // parity (none)
pbuf[6] = 8; // number of bits (8)
break;
case CDC_SET_CONTROL_LINE_STATE:
break;
case CDC_SEND_BREAK:
break;
default:
break;
}
return (USBD_OK);
/* USER CODE END 5 */
}
/**
* @brief Data received over USB OUT endpoint are sent over CDC interface
* through this function.
*
* @note
* This function will block any OUT packet reception on USB endpoint
* untill exiting this function. If you exit this function before transfer
* is complete on CDC interface (ie. using DMA controller) it will result
* in receiving more data while previous ones are still not sent.
*
* @param Buf: Buffer of data to be received
* @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, uint8_t endpoint_pair)
{
/* USER CODE BEGIN 6 */
usb_rx_process_packet(Buf, *Len, endpoint_pair);
return (USBD_OK);
/* USER CODE END 6 */
}
/**
* @brief CDC_Transmit_FS
* Data to send over USB IN endpoint are sent over CDC interface
* through this function.
* @note
*
*
* @param Buf: Buffer of data to be sent
* @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 endpoint_pair)
{
uint8_t result = USBD_OK;
/* USER CODE BEGIN 7 */
//Check length
if (Len > USB_TX_DATA_SIZE)
return USBD_FAIL;
USBD_CDC_HandleTypeDef* hcdc = (USBD_CDC_HandleTypeDef*) hUsbDeviceFS.pClassData;
// Select EP
USBD_CDC_EP_HandleTypeDef* hEP_Tx;
if (endpoint_pair == CDC_IN_EP) {
hEP_Tx = &hcdc->CDC_Tx;
} else if (endpoint_pair == ODRIVE_IN_EP) {
hEP_Tx = &hcdc->ODRIVE_Tx;
} else {
return USBD_FAIL;
}
// Check for ongoing transmission
if (hEP_Tx->State != 0)
return USBD_BUSY;
result = USBD_CDC_TransmitPacket(&hUsbDeviceFS, Buf, Len, endpoint_pair);
/* USER CODE END 7 */
return result;
}
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,801 @@
/**
******************************************************************************
* @file : usbd_conf.c
* @version : v1.0_Cube
* @brief : This file implements the board support package for the USB device library
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include "stm32f4xx_hal.h"
#include "usbd_def.h"
#include "usbd_core.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
PCD_HandleTypeDef hpcd_USB_OTG_FS;
void _Error_Handler(char * file, int line);
/* External functions --------------------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* Private functions ---------------------------------------------------------*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/*******************************************************************************
LL Driver Callbacks (PCD -> USB Device Library)
*******************************************************************************/
/* MSP Init */
void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
/* USER CODE END USB_OTG_FS_MspInit 0 */
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(OTG_FS_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
/* USER CODE END USB_OTG_FS_MspInit 1 */
}
}
void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle)
{
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspDeInit 0 */
/* USER CODE END USB_OTG_FS_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USB_OTG_FS_CLK_DISABLE();
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);
/* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspDeInit 1 */
/* USER CODE END USB_OTG_FS_MspDeInit 1 */
}
}
/**
* @brief Setup stage callback
* @param hpcd: PCD handle
* @retval None
*/
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);
}
/**
* @brief Data Out stage callback.
* @param hpcd: PCD handle
* @param epnum: Endpoint number
* @retval None
*/
void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff);
}
/**
* @brief Data In stage callback.
* @param hpcd: PCD handle
* @param epnum: Endpoint number
* @retval None
*/
void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
USBD_LL_DataInStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff);
}
/**
* @brief SOF callback.
* @param hpcd: PCD handle
* @retval None
*/
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
{
USBD_LL_SOF((USBD_HandleTypeDef*)hpcd->pData);
}
/**
* @brief Reset callback.
* @param hpcd: PCD handle
* @retval None
*/
void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
{
USBD_SpeedTypeDef speed = USBD_SPEED_FULL;
/* Set USB current speed. */
switch (hpcd->Init.speed)
{
case PCD_SPEED_HIGH:
speed = USBD_SPEED_HIGH;
break;
case PCD_SPEED_FULL:
speed = USBD_SPEED_FULL;
break;
default:
speed = USBD_SPEED_FULL;
break;
}
USBD_LL_SetSpeed((USBD_HandleTypeDef*)hpcd->pData, speed);
/* Reset Device. */
USBD_LL_Reset((USBD_HandleTypeDef*)hpcd->pData);
}
/**
* @brief Suspend callback.
* When Low power mode is enabled the debug cannot be used (IAR, Keil doesn't support it)
* @param hpcd: PCD handle
* @retval None
*/
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
{
/* Inform USB library that core enters in suspend Mode. */
USBD_LL_Suspend((USBD_HandleTypeDef*)hpcd->pData);
__HAL_PCD_GATE_PHYCLOCK(hpcd);
/* Enter in STOP mode. */
/* USER CODE BEGIN 2 */
// TODO: do we really want this?
if (hpcd->Init.low_power_enable)
{
/* Set SLEEPDEEP bit and SleepOnExit of Cortex System Control Register */
SCB->SCR |= (uint32_t)((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk));
}
/* USER CODE END 2 */
}
/**
* @brief Resume callback.
* When Low power mode is enabled the debug cannot be used (IAR, Keil doesn't support it)
* @param hpcd: PCD handle
* @retval None
*/
void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd)
{
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
USBD_LL_Resume((USBD_HandleTypeDef*)hpcd->pData);
}
/**
* @brief ISOOUTIncomplete callback.
* @param hpcd: PCD handle
* @param epnum: Endpoint number
* @retval None
*/
void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
USBD_LL_IsoOUTIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum);
}
/**
* @brief ISOINIncomplete callback.
* @param hpcd: PCD handle
* @param epnum: Endpoint number
* @retval None
*/
void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
USBD_LL_IsoINIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum);
}
/**
* @brief Connect callback.
* @param hpcd: PCD handle
* @retval None
*/
void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd)
{
USBD_LL_DevConnected((USBD_HandleTypeDef*)hpcd->pData);
}
/**
* @brief Disconnect callback.
* @param hpcd: PCD handle
* @retval None
*/
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
{
USBD_LL_DevDisconnected((USBD_HandleTypeDef*)hpcd->pData);
}
/*******************************************************************************
LL Driver Interface (USB Device Library --> PCD)
*******************************************************************************/
/**
* @brief Initializes the low level portion of the device driver.
* @param pdev: Device handle
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
/* Init USB Ip. */
if (pdev->id == DEVICE_FS) {
/* Link the driver to the stack. */
hpcd_USB_OTG_FS.pData = pdev;
pdev->pData = &hpcd_USB_OTG_FS;
hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
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;
hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
hpcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE;
hpcd_USB_OTG_FS.Init.lpm_enable = DISABLE;
hpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE;
hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE;
if (HAL_PCD_Init(&hpcd_USB_OTG_FS) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
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, 0x40); // CDC IN endpoint
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 3, 0x40); // ODrive IN endpoint
}
return USBD_OK;
}
/**
* @brief De-Initializes the low level portion of the device driver.
* @param pdev: Device handle
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_DeInit(USBD_HandleTypeDef *pdev)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_DeInit(pdev->pData);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Starts the low level portion of the device driver.
* @param pdev: Device handle
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_Start(pdev->pData);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Stops the low level portion of the device driver.
* @param pdev: Device handle
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_Stop(USBD_HandleTypeDef *pdev)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_Stop(pdev->pData);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Opens an endpoint of the low level driver.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @param ep_type: Endpoint type
* @param ep_mps: Endpoint max packet size
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_mps)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_EP_Open(pdev->pData, ep_addr, ep_mps, ep_type);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Closes an endpoint of the low level driver.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_CloseEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_EP_Close(pdev->pData, ep_addr);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Flushes an endpoint of the Low Level Driver.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_FlushEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_EP_Flush(pdev->pData, ep_addr);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Sets a Stall condition on an endpoint of the Low Level Driver.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_StallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_EP_SetStall(pdev->pData, ep_addr);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Clears a Stall condition on an endpoint of the Low Level Driver.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_ClearStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_EP_ClrStall(pdev->pData, ep_addr);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Returns Stall condition.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @retval Stall (1: Yes, 0: No)
*/
uint8_t USBD_LL_IsStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
{
PCD_HandleTypeDef *hpcd = (PCD_HandleTypeDef*) pdev->pData;
if((ep_addr & 0x80) == 0x80)
{
return hpcd->IN_ep[ep_addr & 0x7F].is_stall;
}
else
{
return hpcd->OUT_ep[ep_addr & 0x7F].is_stall;
}
}
/**
* @brief Assigns a USB address to the device.
* @param pdev: Device handle
* @param dev_addr: Device address
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *pdev, uint8_t dev_addr)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_SetAddress(pdev->pData, dev_addr);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Transmits data over an endpoint.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @param pbuf: Pointer to data to be sent
* @param size: Data size
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_EP_Transmit(pdev->pData, ep_addr, pbuf, size);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Prepares an endpoint for reception.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @param pbuf: Pointer to data to be received
* @param size: Data size
* @retval USBD status
*/
USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size)
{
HAL_StatusTypeDef hal_status = HAL_OK;
USBD_StatusTypeDef usb_status = USBD_OK;
hal_status = HAL_PCD_EP_Receive(pdev->pData, ep_addr, pbuf, size);
switch (hal_status) {
case HAL_OK :
usb_status = USBD_OK;
break;
case HAL_ERROR :
usb_status = USBD_FAIL;
break;
case HAL_BUSY :
usb_status = USBD_BUSY;
break;
case HAL_TIMEOUT :
usb_status = USBD_FAIL;
break;
default :
usb_status = USBD_FAIL;
break;
}
return usb_status;
}
/**
* @brief Returns the last transfered packet size.
* @param pdev: Device handle
* @param ep_addr: Endpoint number
* @retval Recived Data Size
*/
uint32_t USBD_LL_GetRxDataSize(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
{
return HAL_PCD_EP_GetRxCount((PCD_HandleTypeDef*) pdev->pData, ep_addr);
}
#if (USBD_LPM_ENABLED == 1)
/**
* @brief Send LPM message to user layer
* @param hpcd: PCD handle
* @param msg: LPM message
* @retval None
*/
void HAL_PCDEx_LPM_Callback(PCD_HandleTypeDef *hpcd, PCD_LPM_MsgTypeDef msg)
{
switch (msg)
{
case PCD_LPM_L0_ACTIVE:
if (hpcd->Init.low_power_enable)
{
SystemClock_Config();
/* Reset SLEEPDEEP bit of Cortex System Control Register. */
SCB->SCR &= (uint32_t)~((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk));
}
__HAL_PCD_UNGATE_PHYCLOCK(hpcd);
USBD_LL_Resume(hpcd->pData);
break;
case PCD_LPM_L1_ACTIVE:
__HAL_PCD_GATE_PHYCLOCK(hpcd);
USBD_LL_Suspend(hpcd->pData);
/* Enter in STOP mode. */
if (hpcd->Init.low_power_enable)
{
/* Set SLEEPDEEP bit and SleepOnExit of Cortex System Control Register. */
SCB->SCR |= (uint32_t)((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk));
}
break;
}
}
#endif /* (USBD_LPM_ENABLED == 1) */
/**
* @brief Delays routine for the USB Device Library.
* @param Delay: Delay in ms
* @retval None
*/
void USBD_LL_Delay(uint32_t Delay)
{
HAL_Delay(Delay);
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,445 @@
/**
******************************************************************************
* @file : usbd_desc.c
* @version : v1.0_Cube
* @brief : This file implements the USB device descriptors.
******************************************************************************
* This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* Copyright (c) 2018 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_conf.h"
/* USER CODE BEGIN INCLUDE */
#include <MotorControl/odrive_main.h>
/* USER CODE END INCLUDE */
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
* @{
*/
/** @addtogroup USBD_DESC
* @{
*/
/** @defgroup USBD_DESC_Private_TypesDefinitions USBD_DESC_Private_TypesDefinitions
* @brief Private types.
* @{
*/
/* USER CODE BEGIN PRIVATE_TYPES */
/* USER CODE END PRIVATE_TYPES */
/**
* @}
*/
/** @defgroup USBD_DESC_Private_Defines USBD_DESC_Private_Defines
* @brief Private defines.
* @{
*/
#define USBD_VID 0x1209
#define USBD_LANGID_STRING 1033
#define USBD_MANUFACTURER_STRING "ODrive Robotics"
#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 HW_VERSION_MAJOR.HW_VERSION_MINOR CDC Interface
#define NATIVE_STRING ODrive HW_VERSION_MAJOR.HW_VERSION_MINOR Native Interface
#define USBD_CONFIGURATION_STRING_FS "CDC Config"
#define USBD_INTERFACE_STRING_FS "CDC Interface"
#define USB_SIZ_BOS_DESC 0x0C
/* USER CODE BEGIN PRIVATE_DEFINES */
/* USER CODE END PRIVATE_DEFINES */
/**
* @}
*/
/* 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
* @brief Private macros.
* @{
*/
/* USER CODE BEGIN PRIVATE_MACRO */
/* USER CODE END PRIVATE_MACRO */
/**
* @}
*/
/** @defgroup USBD_DESC_Private_FunctionPrototypes USBD_DESC_Private_FunctionPrototypes
* @brief Private functions declaration.
* @{
*/
uint8_t * USBD_FS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_FS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_FS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_FS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_FS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_FS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t * USBD_FS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
#ifdef USB_SUPPORT_USER_STRING_DESC
uint8_t * USBD_FS_USRStringDesc(USBD_SpeedTypeDef speed, uint8_t idx, uint16_t *length);
#endif /* USB_SUPPORT_USER_STRING_DESC */
#if (USBD_LPM_ENABLED == 1)
uint8_t * USBD_FS_USR_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
#endif /* (USBD_LPM_ENABLED == 1) */
/**
* @}
*/
/** @defgroup USBD_DESC_Private_Variables USBD_DESC_Private_Variables
* @brief Private variables.
* @{
*/
USBD_DescriptorsTypeDef FS_Desc =
{
USBD_FS_DeviceDescriptor
, USBD_FS_LangIDStrDescriptor
, USBD_FS_ManufacturerStrDescriptor
, USBD_FS_ProductStrDescriptor
, USBD_FS_SerialStrDescriptor
, USBD_FS_ConfigStrDescriptor
, USBD_FS_InterfaceStrDescriptor
#if (USBD_LPM_ENABLED == 1)
, USBD_FS_USR_BOSDescriptor
#endif /* (USBD_LPM_ENABLED == 1) */
};
#if defined ( __ICCARM__ ) /* IAR Compiler */
#pragma data_alignment=4
#endif /* defined ( __ICCARM__ ) */
/** USB standard device descriptor. */
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
0x12, /*bLength */
USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
#if (USBD_LPM_ENABLED == 1)
0x01, /*bcdUSB */ /* changed to USB version 2.01
in order to support LPM L1 suspend
resume test of USBCV3.0*/
#else
0x00, /*bcdUSB */
#endif /* (USBD_LPM_ENABLED == 1) */
0x02,
// Notify OS that this is a composite device
0xEF, /*bDeviceClass*/
0x02, /*bDeviceSubClass*/
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*/
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*/
USBD_MAX_NUM_CONFIGURATION /*bNumConfigurations*/
};
/* USB_DeviceDescriptor */
/** BOS descriptor. */
#if (USBD_LPM_ENABLED == 1)
#if defined ( __ICCARM__ ) /* IAR Compiler */
#pragma data_alignment=4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN uint8_t USBD_FS_BOSDesc[USB_SIZ_BOS_DESC] __ALIGN_END =
{
0x5,
USB_DESC_TYPE_BOS,
0xC,
0x0,
0x1, /* 1 device capability*/
/* device capability*/
0x7,
USB_DEVICE_CAPABITY_TYPE,
0x2,
0x2, /* LPM capability bit set*/
0x0,
0x0,
0x0
};
#endif /* (USBD_LPM_ENABLED == 1) */
/**
* @}
*/
/** @defgroup USBD_DESC_Private_Variables USBD_DESC_Private_Variables
* @brief Private variables.
* @{
*/
#if defined ( __ICCARM__ ) /* IAR Compiler */
#pragma data_alignment=4
#endif /* defined ( __ICCARM__ ) */
/** USB lang indentifier descriptor. */
__ALIGN_BEGIN uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END =
{
USB_LEN_LANGID_STR_DESC,
USB_DESC_TYPE_STRING,
LOBYTE(USBD_LANGID_STRING),
HIBYTE(USBD_LANGID_STRING)
};
#if defined ( __ICCARM__ ) /* IAR Compiler */
#pragma data_alignment=4
#endif /* defined ( __ICCARM__ ) */
/* Internal string descriptor. */
__ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END;
/**
* @}
*/
/** @defgroup USBD_DESC_Private_Functions USBD_DESC_Private_Functions
* @brief Private functions.
* @{
*/
/**
* @brief Return the device descriptor
* @param speed : Current device speed
* @param length : Pointer to data length variable
* @retval Pointer to descriptor buffer
*/
uint8_t * USBD_FS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
*length = sizeof(USBD_FS_DeviceDesc);
return USBD_FS_DeviceDesc;
}
/**
* @brief Return the LangID string descriptor
* @param speed : Current device speed
* @param length : Pointer to data length variable
* @retval Pointer to descriptor buffer
*/
uint8_t * USBD_FS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
*length = sizeof(USBD_LangIDDesc);
return USBD_LangIDDesc;
}
/**
* @brief Return the product string descriptor
* @param speed : Current device speed
* @param length : Pointer to data length variable
* @retval Pointer to descriptor buffer
*/
uint8_t * USBD_FS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
if(speed == 0)
{
USBD_GetString((uint8_t *)USBD_PRODUCT_XSTR(USBD_PRODUCT_STRING_FS), USBD_StrDesc, length);
}
else
{
USBD_GetString((uint8_t *)USBD_PRODUCT_XSTR(USBD_PRODUCT_STRING_FS), USBD_StrDesc, length);
}
return USBD_StrDesc;
}
/**
* @brief Return the manufacturer string descriptor
* @param speed : Current device speed
* @param length : Pointer to data length variable
* @retval Pointer to descriptor buffer
*/
uint8_t * USBD_FS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);
return USBD_StrDesc;
}
/**
* @brief Return the serial number string descriptor
* @param speed : Current device speed
* @param length : Pointer to data length variable
* @retval Pointer to descriptor buffer
*/
uint8_t * USBD_FS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
USBD_GetString ((uint8_t *)serial_number_str, USBD_StrDesc, length);
return USBD_StrDesc;
}
/**
* @brief Return the configuration string descriptor
* @param speed : Current device speed
* @param length : Pointer to data length variable
* @retval Pointer to descriptor buffer
*/
uint8_t * USBD_FS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
if(speed == USBD_SPEED_HIGH)
{
USBD_GetString((uint8_t *)USBD_CONFIGURATION_STRING_FS, USBD_StrDesc, length);
}
else
{
USBD_GetString((uint8_t *)USBD_CONFIGURATION_STRING_FS, USBD_StrDesc, length);
}
return USBD_StrDesc;
}
/**
* @brief Return the interface string descriptor
* @param speed : Current device speed
* @param length : Pointer to data length variable
* @retval Pointer to descriptor buffer
*/
uint8_t * USBD_FS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
if(speed == 0)
{
USBD_GetString((uint8_t *)USBD_INTERFACE_STRING_FS, USBD_StrDesc, length);
}
else
{
USBD_GetString((uint8_t *)USBD_INTERFACE_STRING_FS, USBD_StrDesc, length);
}
return USBD_StrDesc;
}
#if (USBD_LPM_ENABLED == 1)
/**
* @brief Return the BOS descriptor
* @param speed : Current device speed
* @param length : Pointer to data length variable
* @retval Pointer to descriptor buffer
*/
uint8_t * USBD_FS_USR_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
*length = sizeof(USBD_FS_BOSDesc);
return (uint8_t*)USBD_FS_BOSDesc;
}
#endif /* (USBD_LPM_ENABLED == 1) */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,593 @@
/*
* @brief Contains board specific variables and initialization functions
*/
#include <board.h>
#include <odrive_main.h>
#include <low_level.h>
#include <Drivers/STM32/stm32_timer.hpp>
#include <adc.h>
#include <dma.h>
#include <tim.h>
#include <usart.h>
#include <freertos_vars.h>
// this should technically be in task_timer.cpp but let's not make a one-line file
bool TaskTimer::enabled = false;
extern "C" void SystemClock_Config(void); // defined in main.c generated by CubeMX
#define ControlLoop_IRQHandler OTG_HS_IRQHandler
#define ControlLoop_IRQn OTG_HS_IRQn
// This array is placed at the very start of the ram (0x20000000) and will be
// used during manufacturing to test the struct that will go to the OTP before
// _actually_ putting anything into OTP. This avoids bulk-destroying STM32's if
// we introduce unintended breakage in our manufacturing scripts.
uint8_t __attribute__((section(".testdata"))) fake_otp[FLASH_OTP_END + 1 - FLASH_OTP_BASE] = {0, 0, 0, HW_VERSION_MAJOR, HW_VERSION_MINOR, HW_VERSION_VOLTAGE};
Stm32SpiArbiter spi3_arbiter{&hspi3};
Stm32SpiArbiter& ext_spi_arbiter = spi3_arbiter;
UART_HandleTypeDef* uart_a = &huart4;
UART_HandleTypeDef* uart_b = &huart2; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2
UART_HandleTypeDef* uart_c = nullptr;
Drv8301 m0_gate_driver{
&spi3_arbiter,
{M0_nCS_GPIO_Port, M0_nCS_Pin}, // nCS
{}, // EN pin (shared between both motors, therefore we actuate it outside of the drv8301 driver)
{nFAULT_GPIO_Port, nFAULT_Pin} // nFAULT pin (shared between both motors)
};
Drv8301 m1_gate_driver{
&spi3_arbiter,
{M1_nCS_GPIO_Port, M1_nCS_Pin}, // nCS
{}, // EN pin (shared between both motors, therefore we actuate it outside of the drv8301 driver)
{nFAULT_GPIO_Port, nFAULT_Pin} // nFAULT pin (shared between both motors)
};
const float fet_thermistor_poly_coeffs[] =
{363.93910201f, -462.15369634f, 307.55129571f, -27.72569531f};
const size_t fet_thermistor_num_coeffs = sizeof(fet_thermistor_poly_coeffs)/sizeof(fet_thermistor_poly_coeffs[1]);
OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT] = {
{
15, // adc_channel
&fet_thermistor_poly_coeffs[0], // coefficients
fet_thermistor_num_coeffs // num_coeffs
}, {
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3
4, // adc_channel
#else
1, // adc_channel
#endif
&fet_thermistor_poly_coeffs[0], // coefficients
fet_thermistor_num_coeffs // num_coeffs
}
};
OffboardThermistorCurrentLimiter motor_thermistors[AXIS_COUNT];
Motor motors[AXIS_COUNT] = {
{
&htim1, // timer
0b110, // current_sensor_mask
1.0f / SHUNT_RESISTANCE, // shunt_conductance [S]
m0_gate_driver, // gate_driver
m0_gate_driver, // opamp
fet_thermistors[0],
motor_thermistors[0]
},
{
&htim8, // timer
0b110, // current_sensor_mask
1.0f / SHUNT_RESISTANCE, // shunt_conductance [S]
m1_gate_driver, // gate_driver
m1_gate_driver, // opamp
fet_thermistors[1],
motor_thermistors[1]
}
};
Encoder encoders[AXIS_COUNT] = {
{
&htim3, // timer
{M0_ENC_Z_GPIO_Port, M0_ENC_Z_Pin}, // index_gpio
{M0_ENC_A_GPIO_Port, M0_ENC_A_Pin}, // hallA_gpio
{M0_ENC_B_GPIO_Port, M0_ENC_B_Pin}, // hallB_gpio
{M0_ENC_Z_GPIO_Port, M0_ENC_Z_Pin}, // hallC_gpio
&spi3_arbiter // spi_arbiter
},
{
&htim4, // timer
{M1_ENC_Z_GPIO_Port, M1_ENC_Z_Pin}, // index_gpio
{M1_ENC_A_GPIO_Port, M1_ENC_A_Pin}, // hallA_gpio
{M1_ENC_B_GPIO_Port, M1_ENC_B_Pin}, // hallB_gpio
{M1_ENC_Z_GPIO_Port, M1_ENC_Z_Pin}, // hallC_gpio
&spi3_arbiter // spi_arbiter
}
};
// TODO: this has no hardware dependency and should be allocated depending on config
Endstop endstops[2 * AXIS_COUNT];
MechanicalBrake mechanical_brakes[AXIS_COUNT];
SensorlessEstimator sensorless_estimators[AXIS_COUNT];
Controller controllers[AXIS_COUNT];
TrapezoidalTrajectory trap[AXIS_COUNT];
std::array<Axis, AXIS_COUNT> axes{{
{
0, // axis_num
1, // step_gpio_pin
2, // dir_gpio_pin
(osPriority)(osPriorityHigh + (osPriority)1), // thread_priority
encoders[0], // encoder
sensorless_estimators[0], // sensorless_estimator
controllers[0], // controller
motors[0], // motor
trap[0], // trap
endstops[0], endstops[1], // min_endstop, max_endstop
mechanical_brakes[0], // mechanical brake
},
{
1, // axis_num
#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 5
7, // step_gpio_pin
8, // dir_gpio_pin
#else
3, // step_gpio_pin
4, // dir_gpio_pin
#endif
osPriorityHigh, // thread_priority
encoders[1], // encoder
sensorless_estimators[1], // sensorless_estimator
controllers[1], // controller
motors[1], // motor
trap[1], // trap
endstops[2], endstops[3], // min_endstop, max_endstop
mechanical_brakes[1], // mechanical brake
},
}};
#if (HW_VERSION_MINOR == 1) || (HW_VERSION_MINOR == 2)
Stm32Gpio gpios[] = {
{nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match
{GPIOB, GPIO_PIN_2}, // GPIO1
{GPIOA, GPIO_PIN_5}, // GPIO2
{GPIOA, GPIO_PIN_4}, // GPIO3
{GPIOA, GPIO_PIN_3}, // GPIO4
{nullptr, 0}, // GPIO5 (doesn't exist on this board)
{nullptr, 0}, // GPIO6 (doesn't exist on this board)
{nullptr, 0}, // GPIO7 (doesn't exist on this board)
{nullptr, 0}, // GPIO8 (doesn't exist on this board)
{GPIOB, GPIO_PIN_4}, // ENC0_A
{GPIOB, GPIO_PIN_5}, // ENC0_B
{GPIOA, GPIO_PIN_15}, // ENC0_Z
{GPIOB, GPIO_PIN_6}, // ENC1_A
{GPIOB, GPIO_PIN_7}, // ENC1_B
{GPIOB, GPIO_PIN_3}, // ENC1_Z
{GPIOB, GPIO_PIN_8}, // CAN_R
{GPIOB, GPIO_PIN_9}, // CAN_D
};
#elif (HW_VERSION_MINOR == 3) || (HW_VERSION_MINOR == 4)
Stm32Gpio gpios[] = {
{nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match
{GPIOA, GPIO_PIN_0}, // GPIO1
{GPIOA, GPIO_PIN_1}, // GPIO2
{GPIOA, GPIO_PIN_2}, // GPIO3
{GPIOA, GPIO_PIN_3}, // GPIO4
{GPIOB, GPIO_PIN_2}, // GPIO5
{nullptr, 0}, // GPIO6 (doesn't exist on this board)
{nullptr, 0}, // GPIO7 (doesn't exist on this board)
{nullptr, 0}, // GPIO8 (doesn't exist on this board)
{GPIOB, GPIO_PIN_4}, // ENC0_A
{GPIOB, GPIO_PIN_5}, // ENC0_B
{GPIOA, GPIO_PIN_15}, // ENC0_Z
{GPIOB, GPIO_PIN_6}, // ENC1_A
{GPIOB, GPIO_PIN_7}, // ENC1_B
{GPIOB, GPIO_PIN_3}, // ENC1_Z
{GPIOB, GPIO_PIN_8}, // CAN_R
{GPIOB, GPIO_PIN_9}, // CAN_D
};
#elif (HW_VERSION_MINOR == 5) || (HW_VERSION_MINOR == 6)
Stm32Gpio gpios[GPIO_COUNT] = {
{nullptr, 0}, // dummy GPIO0 so that PCB labels and software numbers match
{GPIOA, GPIO_PIN_0}, // GPIO1
{GPIOA, GPIO_PIN_1}, // GPIO2
{GPIOA, GPIO_PIN_2}, // GPIO3
{GPIOA, GPIO_PIN_3}, // GPIO4
{GPIOC, GPIO_PIN_4}, // GPIO5
{GPIOB, GPIO_PIN_2}, // GPIO6
{GPIOA, GPIO_PIN_15}, // GPIO7
{GPIOB, GPIO_PIN_3}, // GPIO8
{GPIOB, GPIO_PIN_4}, // ENC0_A
{GPIOB, GPIO_PIN_5}, // ENC0_B
{GPIOC, GPIO_PIN_9}, // ENC0_Z
{GPIOB, GPIO_PIN_6}, // ENC1_A
{GPIOB, GPIO_PIN_7}, // ENC1_B
{GPIOC, GPIO_PIN_15}, // ENC1_Z
{GPIOB, GPIO_PIN_8}, // CAN_R
{GPIOB, GPIO_PIN_9}, // CAN_D
};
#else
#error "unknown GPIOs"
#endif
std::array<GpioFunction, 3> alternate_functions[GPIO_COUNT] = {
/* GPIO0 (inexistent): */ {{}},
#if HW_VERSION_MINOR >= 3
/* GPIO1: */ {{{ODrive::GPIO_MODE_UART_A, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}},
/* GPIO2: */ {{{ODrive::GPIO_MODE_UART_A, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}},
/* GPIO3: */ {{{ODrive::GPIO_MODE_UART_B, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}},
#else
/* GPIO1: */ {{}},
/* GPIO2: */ {{}},
/* GPIO3: */ {{}},
#endif
/* GPIO4: */ {{{ODrive::GPIO_MODE_UART_B, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}},
/* GPIO5: */ {{}},
/* GPIO6: */ {{}},
/* GPIO7: */ {{}},
/* GPIO8: */ {{}},
/* ENC0_A: */ {{{ODrive::GPIO_MODE_ENC0, GPIO_AF2_TIM3}}},
/* ENC0_B: */ {{{ODrive::GPIO_MODE_ENC0, GPIO_AF2_TIM3}}},
/* ENC0_Z: */ {{}},
/* ENC1_A: */ {{{ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}, {ODrive::GPIO_MODE_ENC1, GPIO_AF2_TIM4}}},
/* ENC1_B: */ {{{ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}, {ODrive::GPIO_MODE_ENC1, GPIO_AF2_TIM4}}},
/* ENC1_Z: */ {{}},
/* CAN_R: */ {{{ODrive::GPIO_MODE_CAN_A, GPIO_AF9_CAN1}, {ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}}},
/* CAN_D: */ {{{ODrive::GPIO_MODE_CAN_A, GPIO_AF9_CAN1}, {ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}}},
};
#if HW_VERSION_MINOR <= 2
PwmInput pwm0_input{&htim5, {0, 0, 0, 4}}; // 0 means not in use
#else
PwmInput pwm0_input{&htim5, {1, 2, 3, 4}};
#endif
extern USBD_HandleTypeDef hUsbDeviceFS;
USBD_HandleTypeDef& usb_dev_handle = hUsbDeviceFS;
bool check_board_version(const uint8_t* otp_ptr) {
return (otp_ptr[3] == HW_VERSION_MAJOR) &&
(otp_ptr[4] == HW_VERSION_MINOR) &&
(otp_ptr[5] == HW_VERSION_VOLTAGE);
}
void system_init() {
// Reset of all peripherals, Initializes the Flash interface and the Systick.
HAL_Init();
// Configure the system clock
SystemClock_Config();
// If the OTP is pristine, use the fake-otp in RAM instead
const uint8_t* otp_ptr = (const uint8_t*)FLASH_OTP_BASE;
if (*otp_ptr == 0xff) {
otp_ptr = fake_otp;
}
// Ensure that the board version for which this firmware is compiled matches
// the board we're running on.
if (!check_board_version(otp_ptr)) {
for (;;);
}
}
bool board_init() {
// Initialize all configured peripherals
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_ADC2_Init();
MX_TIM1_Init();
MX_TIM8_Init();
MX_TIM3_Init();
MX_TIM4_Init();
MX_SPI3_Init();
MX_ADC3_Init();
MX_TIM2_Init();
MX_TIM5_Init();
MX_TIM13_Init();
// External interrupt lines are individually enabled in stm32_gpio.cpp
HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
HAL_NVIC_SetPriority(EXTI2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
HAL_NVIC_SetPriority(EXTI3_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
HAL_NVIC_SetPriority(EXTI4_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
HAL_NVIC_SetPriority(ControlLoop_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(ControlLoop_IRQn);
HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn);
if (odrv.config_.enable_uart_a) {
uart_a->Init.BaudRate = odrv.config_.uart_a_baudrate;
MX_UART4_Init();
}
if (odrv.config_.enable_uart_b) {
uart_b->Init.BaudRate = odrv.config_.uart_b_baudrate;
MX_USART2_UART_Init();
}
if (odrv.config_.enable_i2c_a) {
// Set up the direction GPIO as input
get_gpio(3).config(GPIO_MODE_INPUT, GPIO_PULLUP);
get_gpio(4).config(GPIO_MODE_INPUT, GPIO_PULLUP);
get_gpio(5).config(GPIO_MODE_INPUT, GPIO_PULLUP);
osDelay(1); // This has no effect but was here before.
i2c_stats_.addr = (0xD << 3);
i2c_stats_.addr |= get_gpio(3).read() ? 0x1 : 0;
i2c_stats_.addr |= get_gpio(4).read() ? 0x2 : 0;
i2c_stats_.addr |= get_gpio(5).read() ? 0x4 : 0;
MX_I2C1_Init(i2c_stats_.addr);
}
if (odrv.config_.enable_can_a) {
// The CAN initialization will (and must) init its own GPIOs before the
// GPIO modes are initialized. Therefore we ensure that the later GPIO
// mode initialization won't override the CAN mode.
if (odrv.config_.gpio_modes[15] != ODriveIntf::GPIO_MODE_CAN_A || odrv.config_.gpio_modes[16] != ODriveIntf::GPIO_MODE_CAN_A) {
odrv.misconfigured_ = true;
}
}
// Ensure that debug halting of the core doesn't leave the motor PWM running
__HAL_DBGMCU_FREEZE_TIM1();
__HAL_DBGMCU_FREEZE_TIM8();
__HAL_DBGMCU_FREEZE_TIM13();
Stm32Gpio drv_enable_gpio = {EN_GATE_GPIO_Port, EN_GATE_Pin};
// Reset both DRV chips. The enable pin also controls the SPI interface, not
// only the driver stages.
drv_enable_gpio.write(false);
delay_us(40); // mimumum pull-down time for full reset: 20us
drv_enable_gpio.write(true);
delay_us(20000); // mimumum pull-down time for full reset: 20us
return true;
}
void start_timers() {
CRITICAL_SECTION() {
// Temporarily disable ADC triggers so they don't trigger as a side
// effect of starting the timers.
hadc1.Instance->CR2 &= ~(ADC_CR2_JEXTEN);
hadc2.Instance->CR2 &= ~(ADC_CR2_EXTEN | ADC_CR2_JEXTEN);
hadc3.Instance->CR2 &= ~(ADC_CR2_EXTEN | ADC_CR2_JEXTEN);
/*
* Synchronize TIM1, TIM8 and TIM13 such that:
* 1. The triangle waveform of TIM1 leads the triangle waveform of TIM8 by a
* 90° phase shift.
* 2. Each TIM13 reload coincides with a TIM1 lower update event.
*/
Stm32Timer::start_synchronously<3>(
{&htim1, &htim8, &htim13},
{TIM1_INIT_COUNT, 0, TIM1_INIT_COUNT / 2 /* TIM13 is on a clock that's only have as fast as TIM1 */}
);
hadc1.Instance->CR2 |= (ADC_EXTERNALTRIGINJECCONVEDGE_RISING);
hadc2.Instance->CR2 |= (ADC_EXTERNALTRIGCONVEDGE_RISING | ADC_EXTERNALTRIGINJECCONVEDGE_RISING);
hadc3.Instance->CR2 |= (ADC_EXTERNALTRIGCONVEDGE_RISING | ADC_EXTERNALTRIGINJECCONVEDGE_RISING);
__HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_JEOC);
__HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_JEOC);
__HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_JEOC);
__HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_EOC);
__HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_EOC);
__HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_EOC);
__HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_OVR);
__HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_OVR);
__HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_OVR);
__HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE);
__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_UPDATE);
}
}
static bool fetch_and_reset_adcs(
std::optional<Iph_ABC_t>* current0,
std::optional<Iph_ABC_t>* current1) {
bool all_adcs_done = (ADC1->SR & ADC_SR_JEOC) == ADC_SR_JEOC
&& (ADC2->SR & (ADC_SR_EOC | ADC_SR_JEOC)) == (ADC_SR_EOC | ADC_SR_JEOC)
&& (ADC3->SR & (ADC_SR_EOC | ADC_SR_JEOC)) == (ADC_SR_EOC | ADC_SR_JEOC);
if (!all_adcs_done) {
return false;
}
vbus_sense_adc_cb(ADC1->JDR1);
if (m0_gate_driver.is_ready()) {
std::optional<float> phB = motors[0].phase_current_from_adcval(ADC2->JDR1);
std::optional<float> phC = motors[0].phase_current_from_adcval(ADC3->JDR1);
if (phB.has_value() && phC.has_value()) {
*current0 = {-*phB - *phC, *phB, *phC};
}
}
if (m1_gate_driver.is_ready()) {
std::optional<float> phB = motors[1].phase_current_from_adcval(ADC2->DR);
std::optional<float> phC = motors[1].phase_current_from_adcval(ADC3->DR);
if (phB.has_value() && phC.has_value()) {
*current1 = {-*phB - *phC, *phB, *phC};
}
}
ADC1->SR = ~(ADC_SR_JEOC);
ADC2->SR = ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR);
ADC3->SR = ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR);
return true;
}
extern "C" {
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
HAL_SPI_TxRxCpltCallback(hspi);
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
HAL_SPI_TxRxCpltCallback(hspi);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
if (hspi == &hspi3) {
spi3_arbiter.on_complete();
}
}
void TIM5_IRQHandler(void) {
COUNT_IRQ(TIM5_IRQn);
pwm0_input.on_capture();
}
volatile uint32_t timestamp_ = 0;
volatile bool counting_down_ = false;
void TIM8_UP_TIM13_IRQHandler(void) {
COUNT_IRQ(TIM8_UP_TIM13_IRQn);
// Entry into this function happens at 21-23 clock cycles after the timer
// update event.
__HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE);
// If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current
// If we are counting down, we just sampled in SVM vector 7, with zero current
bool counting_down = TIM8->CR1 & TIM_CR1_DIR;
bool timer_update_missed = (counting_down_ == counting_down);
if (timer_update_missed) {
motors[0].disarm_with_error(Motor::ERROR_TIMER_UPDATE_MISSED);
motors[1].disarm_with_error(Motor::ERROR_TIMER_UPDATE_MISSED);
return;
}
counting_down_ = counting_down;
timestamp_ += TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1);
if (!counting_down) {
TaskTimer::enabled = odrv.task_timers_armed_;
// Run sampling handlers and kick off control tasks when TIM8 is
// counting up.
odrv.sampling_cb();
NVIC->STIR = ControlLoop_IRQn;
} else {
// Tentatively reset all PWM outputs to 50% duty cycles. If the control
// loop handler finishes in time then these values will be overridden
// before they go into effect.
TIM1->CCR1 =
TIM1->CCR2 =
TIM1->CCR3 =
TIM8->CCR1 =
TIM8->CCR2 =
TIM8->CCR3 =
TIM_1_8_PERIOD_CLOCKS / 2;
}
}
void ControlLoop_IRQHandler(void) {
COUNT_IRQ(ControlLoop_IRQn);
uint32_t timestamp = timestamp_;
// Ensure that all the ADCs are done
std::optional<Iph_ABC_t> current0;
std::optional<Iph_ABC_t> current1;
if (!fetch_and_reset_adcs(&current0, &current1)) {
motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING);
motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING);
}
// If the motor FETs are not switching then we can't measure the current
// because for this we need the low side FET to conduct.
// So for now we guess the current to be 0 (this is not correct shortly after
// disarming and when the motor spins fast in idle). Passing an invalid
// current reading would create problems with starting FOC.
if (!(TIM1->BDTR & TIM_BDTR_MOE_Msk)) {
current0 = {0.0f, 0.0f};
}
if (!(TIM8->BDTR & TIM_BDTR_MOE_Msk)) {
current1 = {0.0f, 0.0f};
}
motors[0].current_meas_cb(timestamp - TIM1_INIT_COUNT, current0);
motors[1].current_meas_cb(timestamp, current1);
odrv.control_loop_cb(timestamp);
// By this time the ADCs for both M0 and M1 should have fired again. But
// let's wait for them just to be sure.
MEASURE_TIME(odrv.task_times_.dc_calib_wait) {
while (!(ADC2->SR & ADC_SR_EOC));
}
if (!fetch_and_reset_adcs(&current0, &current1)) {
motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING);
motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING);
}
motors[0].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT, current0);
motors[1].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1), current1);
motors[0].pwm_update_cb(timestamp + 3 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT);
motors[1].pwm_update_cb(timestamp + 3 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1));
// If we did everything right, the TIM8 update handler should have been
// called exactly once between the start of this function and now.
if (timestamp_ != timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) {
motors[0].disarm_with_error(Motor::ERROR_CONTROL_DEADLINE_MISSED);
motors[1].disarm_with_error(Motor::ERROR_CONTROL_DEADLINE_MISSED);
}
odrv.task_timers_armed_ = odrv.task_timers_armed_ && !TaskTimer::enabled;
TaskTimer::enabled = false;
}
void I2C1_EV_IRQHandler(void) {
COUNT_IRQ(I2C1_EV_IRQn);
HAL_I2C_EV_IRQHandler(&hi2c1);
}
void I2C1_ER_IRQHandler(void) {
COUNT_IRQ(I2C1_ER_IRQn);
HAL_I2C_ER_IRQHandler(&hi2c1);
}
extern PCD_HandleTypeDef hpcd_USB_OTG_FS; // defined in usbd_conf.c
void OTG_FS_IRQHandler(void) {
COUNT_IRQ(OTG_FS_IRQn);
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
}
}
@@ -0,0 +1,518 @@
/**
******************************************************************************
* @file startup_stm32f405xx.s
* @author MCD Application Team
* @brief STM32F405xx Devices vector table for GCC based toolchains.
* This module performs:
* - Set the initial SP
* - Set the initial PC == Reset_Handler,
* - Set the vector table entries with the exceptions ISR address
* - Branches to main in the C library (which eventually
* calls main()).
* After Reset the Cortex-M4 processor is in Thread mode,
* priority is Privileged, and the Stack is set to Main.
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT 2017 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
/**
* @brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* @param None
* @retval : None
*/
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
bl early_start_checks
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
bx lr
.size Reset_Handler, .-Reset_Handler
/**
* @brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
* @param None
* @retval None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
*******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* External Interrupts */
.word WWDG_IRQHandler /* Window WatchDog */
.word PVD_IRQHandler /* PVD through EXTI Line detection */
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
.word FLASH_IRQHandler /* FLASH */
.word RCC_IRQHandler /* RCC */
.word EXTI0_IRQHandler /* EXTI Line0 */
.word EXTI1_IRQHandler /* EXTI Line1 */
.word EXTI2_IRQHandler /* EXTI Line2 */
.word EXTI3_IRQHandler /* EXTI Line3 */
.word EXTI4_IRQHandler /* EXTI Line4 */
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
.word CAN1_TX_IRQHandler /* CAN1 TX */
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */
.word CAN1_SCE_IRQHandler /* CAN1 SCE */
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
.word TIM2_IRQHandler /* TIM2 */
.word TIM3_IRQHandler /* TIM3 */
.word TIM4_IRQHandler /* TIM4 */
.word I2C1_EV_IRQHandler /* I2C1 Event */
.word I2C1_ER_IRQHandler /* I2C1 Error */
.word I2C2_EV_IRQHandler /* I2C2 Event */
.word I2C2_ER_IRQHandler /* I2C2 Error */
.word SPI1_IRQHandler /* SPI1 */
.word SPI2_IRQHandler /* SPI2 */
.word USART1_IRQHandler /* USART1 */
.word USART2_IRQHandler /* USART2 */
.word USART3_IRQHandler /* USART3 */
.word EXTI15_10_IRQHandler /* External Line[15:10]s */
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */
.word FSMC_IRQHandler /* FSMC */
.word SDIO_IRQHandler /* SDIO */
.word TIM5_IRQHandler /* TIM5 */
.word SPI3_IRQHandler /* SPI3 */
.word UART4_IRQHandler /* UART4 */
.word UART5_IRQHandler /* UART5 */
.word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */
.word TIM7_IRQHandler /* TIM7 */
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word CAN2_TX_IRQHandler /* CAN2 TX */
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */
.word CAN2_SCE_IRQHandler /* CAN2 SCE */
.word OTG_FS_IRQHandler /* USB OTG FS */
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */
.word USART6_IRQHandler /* USART6 */
.word I2C3_EV_IRQHandler /* I2C3 event */
.word I2C3_ER_IRQHandler /* I2C3 error */
.word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */
.word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */
.word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */
.word OTG_HS_IRQHandler /* USB OTG HS */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word HASH_RNG_IRQHandler /* Hash and Rng */
.word FPU_IRQHandler /* FPU */
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_IRQHandler
.thumb_set PVD_IRQHandler,Default_Handler
.weak TAMP_STAMP_IRQHandler
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Stream0_IRQHandler
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler
.weak DMA1_Stream1_IRQHandler
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler
.weak DMA1_Stream2_IRQHandler
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler
.weak DMA1_Stream3_IRQHandler
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler
.weak DMA1_Stream4_IRQHandler
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler
.weak DMA1_Stream5_IRQHandler
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler
.weak DMA1_Stream6_IRQHandler
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler
.weak ADC_IRQHandler
.thumb_set ADC_IRQHandler,Default_Handler
.weak CAN1_TX_IRQHandler
.thumb_set CAN1_TX_IRQHandler,Default_Handler
.weak CAN1_RX0_IRQHandler
.thumb_set CAN1_RX0_IRQHandler,Default_Handler
.weak CAN1_RX1_IRQHandler
.thumb_set CAN1_RX1_IRQHandler,Default_Handler
.weak CAN1_SCE_IRQHandler
.thumb_set CAN1_SCE_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_TIM9_IRQHandler
.thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler
.weak TIM1_UP_TIM10_IRQHandler
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_TIM11_IRQHandler
.thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak TIM3_IRQHandler
.thumb_set TIM3_IRQHandler,Default_Handler
.weak TIM4_IRQHandler
.thumb_set TIM4_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak OTG_FS_WKUP_IRQHandler
.thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler
.weak TIM8_BRK_TIM12_IRQHandler
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler
.weak TIM8_UP_TIM13_IRQHandler
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler
.weak TIM8_TRG_COM_TIM14_IRQHandler
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler
.weak TIM8_CC_IRQHandler
.thumb_set TIM8_CC_IRQHandler,Default_Handler
.weak DMA1_Stream7_IRQHandler
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler
.weak FSMC_IRQHandler
.thumb_set FSMC_IRQHandler,Default_Handler
.weak SDIO_IRQHandler
.thumb_set SDIO_IRQHandler,Default_Handler
.weak TIM5_IRQHandler
.thumb_set TIM5_IRQHandler,Default_Handler
.weak SPI3_IRQHandler
.thumb_set SPI3_IRQHandler,Default_Handler
.weak UART4_IRQHandler
.thumb_set UART4_IRQHandler,Default_Handler
.weak UART5_IRQHandler
.thumb_set UART5_IRQHandler,Default_Handler
.weak TIM6_DAC_IRQHandler
.thumb_set TIM6_DAC_IRQHandler,Default_Handler
.weak TIM7_IRQHandler
.thumb_set TIM7_IRQHandler,Default_Handler
.weak DMA2_Stream0_IRQHandler
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler
.weak DMA2_Stream1_IRQHandler
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler
.weak DMA2_Stream2_IRQHandler
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler
.weak DMA2_Stream3_IRQHandler
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler
.weak DMA2_Stream4_IRQHandler
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler
.weak CAN2_TX_IRQHandler
.thumb_set CAN2_TX_IRQHandler,Default_Handler
.weak CAN2_RX0_IRQHandler
.thumb_set CAN2_RX0_IRQHandler,Default_Handler
.weak CAN2_RX1_IRQHandler
.thumb_set CAN2_RX1_IRQHandler,Default_Handler
.weak CAN2_SCE_IRQHandler
.thumb_set CAN2_SCE_IRQHandler,Default_Handler
.weak OTG_FS_IRQHandler
.thumb_set OTG_FS_IRQHandler,Default_Handler
.weak DMA2_Stream5_IRQHandler
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler
.weak DMA2_Stream6_IRQHandler
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler
.weak DMA2_Stream7_IRQHandler
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler
.weak USART6_IRQHandler
.thumb_set USART6_IRQHandler,Default_Handler
.weak I2C3_EV_IRQHandler
.thumb_set I2C3_EV_IRQHandler,Default_Handler
.weak I2C3_ER_IRQHandler
.thumb_set I2C3_ER_IRQHandler,Default_Handler
.weak OTG_HS_EP1_OUT_IRQHandler
.thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler
.weak OTG_HS_EP1_IN_IRQHandler
.thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler
.weak OTG_HS_WKUP_IRQHandler
.thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler
.weak OTG_HS_IRQHandler
.thumb_set OTG_HS_IRQHandler,Default_Handler
.weak HASH_RNG_IRQHandler
.thumb_set HASH_RNG_IRQHandler,Default_Handler
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
@@ -0,0 +1,176 @@
#include "drv8301.hpp"
#include "utils.hpp"
#include "cmsis_os.h"
#include "board.h"
const SPI_InitTypeDef Drv8301::spi_config_ = {
.Mode = SPI_MODE_MASTER,
.Direction = SPI_DIRECTION_2LINES,
.DataSize = SPI_DATASIZE_16BIT,
.CLKPolarity = SPI_POLARITY_LOW,
.CLKPhase = SPI_PHASE_2EDGE,
.NSS = SPI_NSS_SOFT,
.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16,
.FirstBit = SPI_FIRSTBIT_MSB,
.TIMode = SPI_TIMODE_DISABLE,
.CRCCalculation = SPI_CRCCALCULATION_DISABLE,
.CRCPolynomial = 10,
};
bool Drv8301::config(float requested_gain, float* actual_gain) {
// Calculate gain setting: Snap down to have equal or larger range as
// requested or largest possible range otherwise
// for reference:
// 20V/V on 500uOhm gives a range of +/- 150A
// 40V/V on 500uOhm gives a range of +/- 75A
// 20V/V on 666uOhm gives a range of +/- 110A
// 40V/V on 666uOhm gives a range of +/- 55A
uint16_t gain_setting = 3;
float gain_choices[] = {10.0f, 20.0f, 40.0f, 80.0f};
while (gain_setting && (gain_choices[gain_setting] > requested_gain)) {
gain_setting--;
}
if (actual_gain) {
*actual_gain = gain_choices[gain_setting];
}
RegisterFile new_config;
new_config.control_register_1 =
(21 << 6) // Overcurrent set to approximately 150A at 100degC. This may need tweaking.
| (0b01 << 4) // OCP_MODE: latch shut down
| (0b0 << 3) // 6x PWM mode
| (0b0 << 2) // don't reset latched faults
| (0b00 << 0); // gate-drive peak current: 1.7A
new_config.control_register_2 =
(0b0 << 6) // OC_TOFF: cycle by cycle
| (0b00 << 4) // calibration off (normal operation)
| (gain_setting << 2) // select gain
| (0b00 << 0); // report both over temperature and over current on nOCTW pin
bool regs_equal = (regs_.control_register_1 == new_config.control_register_1)
&& (regs_.control_register_2 == new_config.control_register_2);
if (!regs_equal) {
regs_ = new_config;
state_ = kStateUninitialized;
enable_gpio_.write(false);
}
return true;
}
bool Drv8301::init() {
uint16_t val;
if (state_ == kStateReady) {
return true;
}
// Reset DRV chip. The enable pin also controls the SPI interface, not only
// the driver stages.
enable_gpio_.write(false);
delay_us(40); // mimumum pull-down time for full reset: 20us
state_ = kStateUninitialized; // make is_ready() ignore transient errors before registers are set up
enable_gpio_.write(true);
osDelay(20); // t_spi_ready, max = 10ms
// Write current configuration
bool wrote_regs = write_reg(kRegNameControl1, regs_.control_register_1)
&& write_reg(kRegNameControl1, regs_.control_register_1)
&& write_reg(kRegNameControl1, regs_.control_register_1)
&& write_reg(kRegNameControl1, regs_.control_register_1)
&& write_reg(kRegNameControl1, regs_.control_register_1) // the write operation tends to be ignored if only done once (not sure why)
&& write_reg(kRegNameControl2, regs_.control_register_2);
if (!wrote_regs) {
return false;
}
// Wait for configuration to be applied
delay_us(100);
state_ = kStateStartupChecks;
bool is_read_regs = read_reg(kRegNameControl1, &val) && (val == regs_.control_register_1)
&& read_reg(kRegNameControl2, &val) && (val == regs_.control_register_2);
if (!is_read_regs) {
return false;
}
if (get_error() != FaultType_NoFault) {
return false;
}
// There could have been an nFAULT edge meanwhile. In this case we shouldn't
// consider the driver ready.
CRITICAL_SECTION() {
if (state_ == kStateStartupChecks) {
state_ = kStateReady;
}
}
return state_ == kStateReady;
}
void Drv8301::do_checks() {
if (state_ != kStateUninitialized && !nfault_gpio_.read()) {
state_ = kStateUninitialized;
}
}
bool Drv8301::is_ready() {
return state_ == kStateReady;
}
Drv8301::FaultType_e Drv8301::get_error() {
uint16_t fault1, fault2;
if (!read_reg(kRegNameStatus1, &fault1) ||
!read_reg(kRegNameStatus2, &fault2)) {
return (FaultType_e)0xffffffff;
}
return (FaultType_e)((uint32_t)fault1 | ((uint32_t)(fault2 & 0x0080) << 16));
}
bool Drv8301::read_reg(const RegName_e regName, uint16_t* data) {
tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0);
if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) {
return false;
}
delay_us(1);
tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0);
rx_buf_ = 0xffff;
if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), (uint8_t *)(&rx_buf_), 1, 1000)) {
return false;
}
delay_us(1);
if (rx_buf_ == 0xbeef) {
return false;
}
if (data) {
*data = rx_buf_ & 0x07FF;
}
return true;
}
bool Drv8301::write_reg(const RegName_e regName, const uint16_t data) {
// Do blocking write
tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Write, regName, data);
if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) {
return false;
}
delay_us(1);
return true;
}
@@ -0,0 +1,145 @@
#ifndef __DRV8301_HPP
#define __DRV8301_HPP
#include "stdbool.h"
#include "stdint.h"
#include <Drivers/gate_driver.hpp>
#include <Drivers/STM32/stm32_spi_arbiter.hpp>
#include <Drivers/STM32/stm32_gpio.hpp>
class Drv8301 : public GateDriverBase, public OpAmpBase {
public:
typedef enum : uint32_t {
FaultType_NoFault = (0 << 0), //!< No fault
// Status Register 1
FaultType_FETLC_OC = (1 << 0), //!< FET Low side, Phase C Over Current fault
FaultType_FETHC_OC = (1 << 1), //!< FET High side, Phase C Over Current fault
FaultType_FETLB_OC = (1 << 2), //!< FET Low side, Phase B Over Current fault
FaultType_FETHB_OC = (1 << 3), //!< FET High side, Phase B Over Current fault
FaultType_FETLA_OC = (1 << 4), //!< FET Low side, Phase A Over Current fault
FaultType_FETHA_OC = (1 << 5), //!< FET High side, Phase A Over Current fault
FaultType_OTW = (1 << 6), //!< Over Temperature Warning fault
FaultType_OTSD = (1 << 7), //!< Over Temperature Shut Down fault
FaultType_PVDD_UV = (1 << 8), //!< Power supply Vdd Under Voltage fault
FaultType_GVDD_UV = (1 << 9), //!< DRV8301 Vdd Under Voltage fault
FaultType_FAULT = (1 << 10),
// Status Register 2
FaultType_GVDD_OV = (1 << 23) //!< DRV8301 Vdd Over Voltage fault
} FaultType_e;
Drv8301(Stm32SpiArbiter* spi_arbiter, Stm32Gpio ncs_gpio,
Stm32Gpio enable_gpio, Stm32Gpio nfault_gpio)
: spi_arbiter_(spi_arbiter), ncs_gpio_(ncs_gpio),
enable_gpio_(enable_gpio), nfault_gpio_(nfault_gpio) {}
/**
* @brief Prepares the gate driver's configuration.
*
* If the gate driver was in ready state and the new configuration is
* different from the old one then the gate driver will exit ready state.
*
* In any case changes to the configuration only take effect with a call to
* init().
*/
bool config(float requested_gain, float* actual_gain);
/**
* @brief Initializes the gate driver to the configuration prepared with
* config().
*
* Returns true on success or false otherwise (e.g. if the gate driver is
* not connected or not powered or if config() was not yet called).
*/
bool init();
/**
* @brief Monitors the nFAULT pin.
*
* This must be run at an interval of <8ms from the moment the init()
* functions starts to run, otherwise it's possible that a temporary power
* loss is missed, leading to unwanted register values.
* In case of power loss the nFAULT pin can be low for as little as 8ms.
*/
void do_checks();
/**
* @brief Returns true if and only if the DRV8301 chip is in an initialized
* state and ready to do switching and current sensor opamp operation.
*/
bool is_ready() final;
/**
* @brief This has no effect on this driver chip because the drive stages are
* always enabled while the chip is initialized
*/
bool set_enabled(bool enabled) final { return true; }
FaultType_e get_error();
float get_midpoint() final {
return 0.5f; // [V]
}
float get_max_output_swing() final {
return 1.35f / 1.65f; // +-1.35V, normalized from a scale of +-1.65V to +-0.5
}
private:
enum CtrlMode_e {
DRV8301_CtrlMode_Read = 1 << 15, //!< Read Mode
DRV8301_CtrlMode_Write = 0 << 15 //!< Write Mode
};
enum RegName_e {
kRegNameStatus1 = 0 << 11, //!< Status Register 1
kRegNameStatus2 = 1 << 11, //!< Status Register 2
kRegNameControl1 = 2 << 11, //!< Control Register 1
kRegNameControl2 = 3 << 11 //!< Control Register 2
};
struct RegisterFile {
uint16_t control_register_1;
uint16_t control_register_2;
};
static inline uint16_t build_ctrl_word(const CtrlMode_e ctrlMode,
const RegName_e regName,
const uint16_t data) {
return ctrlMode | regName | (data & 0x07FF);
}
/** @brief Reads data from a DRV8301 register */
bool read_reg(const RegName_e regName, uint16_t* data);
/** @brief Writes data to a DRV8301 register. There is no check if the write succeeded. */
bool write_reg(const RegName_e regName, const uint16_t data);
static const SPI_InitTypeDef spi_config_;
// Configuration
Stm32SpiArbiter* spi_arbiter_;
Stm32Gpio ncs_gpio_;
Stm32Gpio enable_gpio_;
Stm32Gpio nfault_gpio_;
RegisterFile regs_; //!< Current configuration. If is_ready_ is
//!< true then this can be considered consistent
//!< with the actual file on the DRV8301 chip.
// We don't put these buffers on the stack because we place the stack in
// a RAM section which cannot be used by DMA.
uint16_t tx_buf_, rx_buf_;
enum {
kStateUninitialized,
kStateStartupChecks,
kStateReady,
} state_ = kStateUninitialized;
};
#endif // __DRV8301_HPP
@@ -0,0 +1,234 @@
#include "stm32_gpio.hpp"
#define N_EXTI 16
struct subscription_t {
GPIO_TypeDef* port = nullptr;
void (*callback)(void*) = nullptr;
void* ctx = nullptr;
} subscriptions[N_EXTI];
const Stm32Gpio Stm32Gpio::none{nullptr, 0};
/**
* @brief Returns the IRQ number associated with a certain pin.
* Note that all GPIOs with the same pin number map to the same IRQn,
* no matter which port they belong to.
*/
static inline IRQn_Type get_irq_number(uint16_t pin_number) {
switch (pin_number) {
case 0: return EXTI0_IRQn;
case 1: return EXTI1_IRQn;
case 2: return EXTI2_IRQn;
case 3: return EXTI3_IRQn;
case 4: return EXTI4_IRQn;
case 5:
case 6:
case 7:
case 8:
case 9: return EXTI9_5_IRQn;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15: return EXTI15_10_IRQn;
default: return (IRQn_Type)0; // impossible
}
}
#define GPIO_MODE 0x00000003U
#define GPIO_OUTPUT_TYPE 0x00000010U
bool Stm32Gpio::config(uint32_t mode, uint32_t pull, uint32_t speed) {
if (port_ == GPIOA) {
__HAL_RCC_GPIOA_CLK_ENABLE();
} else if (port_ == GPIOB) {
__HAL_RCC_GPIOB_CLK_ENABLE();
} else if (port_ == GPIOC) {
__HAL_RCC_GPIOC_CLK_ENABLE();
} else if (port_ == GPIOD) {
__HAL_RCC_GPIOD_CLK_ENABLE();
} else if (port_ == GPIOE) {
__HAL_RCC_GPIOE_CLK_ENABLE();
} else if (port_ == GPIOF) {
__HAL_RCC_GPIOF_CLK_ENABLE();
} else if (port_ == GPIOG) {
__HAL_RCC_GPIOG_CLK_ENABLE();
} else if (port_ == GPIOH) {
__HAL_RCC_GPIOH_CLK_ENABLE();
} else {
return false;
}
size_t position = get_pin_number();
// The following code is mostly taken from HAL_GPIO_Init
/* Configure IO Direction mode (Input, Output, Alternate or Analog) */
uint32_t temp = port_->MODER;
temp &= ~(GPIO_MODER_MODER0 << (position * 2U));
temp |= ((mode & GPIO_MODE) << (position * 2U));
port_->MODER = temp;
/* In case of Output or Alternate function mode selection */
if((mode == GPIO_MODE_OUTPUT_PP) || (mode == GPIO_MODE_AF_PP) ||
(mode == GPIO_MODE_OUTPUT_OD) || (mode == GPIO_MODE_AF_OD))
{
/* Check the Speed parameter */
assert_param(IS_GPIO_SPEED(speed));
/* Configure the IO Speed */
temp = port_->OSPEEDR;
temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));
temp |= (speed << (position * 2U));
port_->OSPEEDR = temp;
/* Configure the IO Output Type */
temp = port_->OTYPER;
temp &= ~(GPIO_OTYPER_OT_0 << position) ;
temp |= (((mode & GPIO_OUTPUT_TYPE) >> 4U) << position);
port_->OTYPER = temp;
}
/* Activate the Pull-up or Pull down resistor for the current IO */
temp = port_->PUPDR;
temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));
temp |= ((pull) << (position * 2U));
port_->PUPDR = temp;
return true;
}
bool Stm32Gpio::subscribe(bool rising_edge, bool falling_edge, void (*callback)(void*), void* ctx) {
uint32_t pin_number = get_pin_number();
if (pin_number >= N_EXTI) {
return false; // invalid pin number
}
struct subscription_t& subscription = subscriptions[pin_number];
GPIO_TypeDef* no_port = nullptr;
if (!__atomic_compare_exchange_n(&subscription.port, &no_port, port_, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
return false; // already in use
}
// The following code is mostly taken from HAL_GPIO_Init
__HAL_RCC_SYSCFG_CLK_ENABLE();
uint32_t temp = SYSCFG->EXTICR[pin_number >> 2U];
temp &= ~(0x0FU << (4U * (pin_number & 0x03U)));
temp |= ((uint32_t)(GPIO_GET_INDEX(port_)) << (4U * (pin_number & 0x03U)));
SYSCFG->EXTICR[pin_number >> 2U] = temp;
if (rising_edge) {
EXTI->RTSR |= (uint32_t)pin_mask_;
} else {
EXTI->RTSR &= ~((uint32_t)pin_mask_);
}
if (falling_edge) {
EXTI->FTSR |= (uint32_t)pin_mask_;
} else {
EXTI->FTSR &= ~((uint32_t)pin_mask_);
}
EXTI->EMR &= ~((uint32_t)pin_mask_);
EXTI->IMR |= (uint32_t)pin_mask_;
// Clear any previous triggers
__HAL_GPIO_EXTI_CLEAR_IT(pin_mask_);
subscription.ctx = ctx;
subscription.callback = callback;
return true;
}
void Stm32Gpio::unsubscribe() {
uint32_t pin_number = get_pin_number();
if (pin_number >= N_EXTI) {
return; // invalid pin number
}
struct subscription_t& subscription = subscriptions[pin_number];
if (subscription.port != port_) {
return; // the subscription was not for this GPIO
}
EXTI->IMR |= (uint32_t)pin_mask_;
__HAL_GPIO_EXTI_CLEAR_IT(pin_mask_);
// At this point no more interrupts will be triggered for this GPIO
subscription.callback = nullptr;
subscription.ctx = nullptr;
subscription.port = nullptr; // after this line, the subscription can be reused (possibly by another thread)
}
void maybe_handle(uint16_t exti_number) {
if(__HAL_GPIO_EXTI_GET_IT(1 << exti_number) == RESET) {
return; // This interrupt source did not trigger the interrupt line
}
__HAL_GPIO_EXTI_CLEAR_IT(1 << exti_number);
if (exti_number >= N_EXTI) {
return;
}
subscription_t& subscription = subscriptions[exti_number];
if (subscription.callback) {
(*subscription.callback)(subscription.ctx);
}
}
extern "C" {
/** @brief Entrypoint for the EXTI line 0 interrupt. */
void EXTI0_IRQHandler(void) {
maybe_handle(0);
}
/** @brief Entrypoint for the EXTI line 1 interrupt. */
void EXTI1_IRQHandler(void) {
maybe_handle(1);
}
/** @brief Entrypoint for the EXTI line 2 interrupt. */
void EXTI2_IRQHandler(void) {
maybe_handle(2);
}
/** @brief Entrypoint for the EXTI line 3 interrupt. */
void EXTI3_IRQHandler(void) {
maybe_handle(3);
}
/** @brief Entrypoint for the EXTI line 4 interrupt. */
void EXTI4_IRQHandler(void) {
maybe_handle(4);
}
/** @brief Entrypoint for the EXTI lines 5-9 interrupt. */
void EXTI9_5_IRQHandler(void) {
maybe_handle(5);
maybe_handle(6);
maybe_handle(7);
maybe_handle(8);
maybe_handle(9);
}
/** @brief This function handles EXTI lines 10-15 interrupt. */
void EXTI15_10_IRQHandler(void) {
maybe_handle(10);
maybe_handle(11);
maybe_handle(12);
maybe_handle(13);
maybe_handle(14);
maybe_handle(15);
}
}
@@ -0,0 +1,82 @@
#ifndef __STM32_GPIO_HPP
#define __STM32_GPIO_HPP
#include <gpio.h>
class Stm32Gpio {
public:
static const Stm32Gpio none;
Stm32Gpio() : port_(nullptr), pin_mask_(0) {}
Stm32Gpio(GPIO_TypeDef* port, uint16_t pin) : port_(port), pin_mask_(pin) {}
operator bool() const { return port_ && pin_mask_; }
/**
* @brief Configures the GPIO with the specified parameters.
*
* This can be done regardless of the current state of the GPIO.
*
* If any subscription is in place, it is not disabled by this function.
*/
bool config(uint32_t mode, uint32_t pull, uint32_t speed = GPIO_SPEED_FREQ_LOW);
void write(bool state) {
if (port_) {
HAL_GPIO_WritePin(port_, pin_mask_, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
}
bool read() {
return port_ && (port_->IDR & pin_mask_);
}
/**
* @brief Subscribes to external interrupts on the specified GPIO.
*
* Before calling this function the gpio should most likely be configured as
* input (however this is not mandatory, the interrupt works in output mode
* too).
* Also you need to enable the EXTIx_IRQn interrupt vectors in the NVIC,
* otherwise the subscription won't have any effect.
*
* Only one subscription is allowed per pin number. I.e. it is not possible
* to set up a subscription for both PA0 and PB0 at the same time.
*
* This function is thread-safe with respect to all other public functions
* of this class.
*
* Returns true if the subscription was set up successfully or false otherwise.
*/
bool subscribe(bool rising_edge, bool falling_edge, void (*callback)(void*), void* ctx);
/**
* @brief Unsubscribes from external interrupt on the specified GPIO.
*
* If no subscription was active for this GPIO, calling this function has no
* effect.
*
* This function is thread-safe with respect to all other public functions
* of this class, however it must not be called from an interrupt routine
* running at a higher priority than the interrupt that is being unsubscribed.
*
* After this function returns the callback given to subscribe() will no
* longer be invoked.
*/
void unsubscribe();
uint16_t get_pin_number() {
uint16_t pin_number = 0;
uint16_t pin_mask = pin_mask_ >> 1;
while (pin_mask) {
pin_mask >>= 1;
pin_number++;
}
return pin_number;
}
GPIO_TypeDef* port_;
uint16_t pin_mask_; // TODO: store pin_number_ instead of pin_mask_
};
#endif // __STM32_GPIO_HPP
@@ -0,0 +1,499 @@
/*
* Flash-based Non-Volatile Memory (NVM)
*
* This file supports storing and loading persistent configuration based on
* the STM32 builtin flash memory.
*
* The STM32F405xx has 12 flash sectors of heterogeneous size. We use the last
* two sectors for configuration data. These pages have a size of 128kB each.
* Setting any bit in these sectors to 0 is always possible, but setting them
* to 1 requires erasing the whole sector.
*
* We consider each sector as an array of 64-bit fields except the first N bytes, which we
* instead use as an allocation block. The allocation block is a compact bit-field (2 bit per entry)
* that keeps track of the state of each field (erased, invalid, valid).
*
* One sector is always considered the valid (read) sector and the other one is the
* target for the next write access: they can be considered to be ping-pong or double buffred.
*
* When writing a block of data, instead of always erasing the whole writable sector the
* new data is appended in the erased area. This presumably increases flash life span.
* The writable sector is only erased if there is not enough space for the new data.
*
* On startup, if there is exactly one sector
* whose last non-erased value has the state "valid" that sector is considered
* the valid sector. In any other case the selection is undefined.
*
*
* To write a new block of data atomically we first mark all associated fields
* as "invalid" (in the allocation table) then write the data and then mark the
* fields as "valid" (in the direction of increasing address).
*/
#include "stm32_nvm.h"
#include <string.h>
#if defined(STM32F405xx)
#include <stm32f405xx.h>
#include <stm32f4xx_hal.h>
// 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_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
#elif defined(STM32F722xx)
#include <stm32f722xx.h>
#include <stm32f7xx_hal.h>
// refer to page 68 of datasheet:
// https://www.st.com/resource/en/reference_manual/dm00305990-stm32f72xxx-and-stm32f73xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
#define FLASH_SECTOR_A FLASH_SECTOR_1
#define FLASH_SECTOR_A_BASE (const volatile uint8_t*)0x8004000UL
#define FLASH_SECTOR_A_SIZE 0x4000UL
#define FLASH_SECTOR_B FLASH_SECTOR_2
#define FLASH_SECTOR_B_BASE (const volatile uint8_t*)0x8008000UL
#define FLASH_SECTOR_B_SIZE 0x4000UL
#else
#error "unknown flash sector size"
#endif
typedef enum {
VALID = 0,
INVALID = 1,
ERASED = 3
} field_state_t;
typedef struct {
size_t index; //!< next field to be written to (can be equal to n_data)
const uint32_t sector_id; //!< HAL ID of this sector
const size_t n_data; //!< number of 64-bit fields in this sector
const size_t n_reserved; //!< number of 64-bit fields in this sector that are reserved for the allocation table
const volatile uint8_t* const alloc_table;
const volatile uint64_t* const data;
} sector_t;
sector_t sectors[] = { {
.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_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
};
HAL_FLASH_Unlock();
HAL_FLASH_ClearError();
uint32_t sector_error;
if (HAL_FLASHEx_Erase(&erase_struct, &sector_error) != HAL_OK)
goto fail;
sector->index = sector->n_reserved;
HAL_FLASH_Lock();
return 0;
fail:
HAL_FLASH_Lock();
//printf("erase failed: %u \r\n", HAL_FLASH_GetError());
return HAL_FLASH_GetError(); // non-zero
}
// @brief Writes states into the allocation table.
// The write operation goes in the direction of increasing indices.
// @param state: 11: erased, 10: writing, 00: valid data
// @returns 0 on success or a non-zero error code otherwise
int set_allocation_state(sector_t *sector, size_t index, size_t count, field_state_t state) {
if (index < sector->n_reserved)
return -1;
if (index + count >= sector->n_data)
return -1;
// expand state to state for 4 values
const uint8_t states = (state << 0) | (state << 2) | (state << 4) | (state << 6);
// handle unaligned start
uint8_t mask = ~(0xff << ((index & 0x3) << 1));
count += index & 0x3;
index -= index & 0x3;
HAL_FLASH_Unlock();
HAL_FLASH_ClearError();
// write states
for (; count >= 4; count -= 4, index += 4) {
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (uintptr_t)&sector->alloc_table[index >> 2], states | mask) != HAL_OK)
goto fail;
mask = 0;
}
// handle unaligned end
if (count) {
mask |= ~(0xff >> ((4 - count) << 1));
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (uintptr_t)&sector->alloc_table[index >> 2], states | mask) != HAL_OK)
goto fail;
}
HAL_FLASH_Lock();
return 0;
fail:
HAL_FLASH_Lock();
return HAL_FLASH_GetError(); // non-zero
}
// @brief Reads the allocation table from behind to determine how many fields match the
// reference state.
// @param sector: The sector on which to perform the search
// @param max_index: The maximum index that should be considered
// @param ref_state: The reference state
// @param state: Set to the first encountered state that is unequal to ref_state.
// Set to ref_state if all encountered states are equal to ref_state.
// @returns The smallest index that points to a field with ref_state.
// This value is at least sector->n_reserved and at most max_index.
size_t scan_allocation_table(sector_t *sector, size_t max_index, field_state_t ref_state, field_state_t *state) {
const uint8_t ref_states = (ref_state << 0) | (ref_state << 2) | (ref_state << 4) | (ref_state << 6);
size_t index = (((max_index + 3) >> 2) << 2); // start at the max index but round up to a multiple of 4
size_t ignore = index - max_index;
uint8_t states = ref_states;
//printf("scan from %08x to %08x for %02x\r\n", index, sector->n_reserved, ref_states); osDelay(5);
// read 4 states at a time
for (; index >= (sector->n_reserved + 4); index -= 4) {
states = sector->alloc_table[(index - 1) >> 2];
if (ignore) { // ignore the upper 1, 2 or 3 states if max_index was unaligned
uint8_t ignore_mask = ~(0xff >> (ignore << 1));
states = (states & ~ignore_mask) | (ref_states & ignore_mask);
ignore = 0;
}
if (states != ref_states)
break;
}
// once we encounterd a byte with any state mismatch determine which of the 4 states it is
for (; ((states >> 6) == (ref_states & 0x3)) && (index > sector->n_reserved); index--) {
states <<= 2;
}
*state = states >> 6;
//printf("(it's %02x)\r\n", index); osDelay(5);
return index;
}
// Loads the head of the NVM data.
// If this function fails subsequent calls to NVM functions (other than NVM_init or NVM_erase)
// cause undefined behavior.
// @returns 0 on success or a non-zero error code otherwise
int NVM_init(void) {
field_state_t sector0_state, sector1_state;
sectors[0].index = scan_allocation_table(&sectors[0], sectors[0].n_data,
ERASED, &sector0_state);
sectors[1].index = scan_allocation_table(&sectors[1], sectors[1].n_data,
ERASED, &sector1_state);
//printf("sector states: %02x, %02x\r\n", sector0_state, sector1_state); osDelay(5);
// Select valid sector on a best effort basis
// (in unfortunate cases valid_sector might actually point
// to an invalid or erased sector)
read_sector_ = 0;
if (sector1_state == VALID)
read_sector_ = 1;
// count the number of valid fields
sector_t *read_sector = &sectors[read_sector_];
uint8_t first_nonvalid_state;
size_t min_valid_index = scan_allocation_table(read_sector, read_sector->index,
VALID, &first_nonvalid_state);
n_valid_ = read_sector->index - min_valid_index;
n_staging_area_ = 0;
int status = 0;
/*// bring non-valid sectors into a known state
this is not absolutely required
if (sector0_state != VALID)
status |= erase(&sectors[0]);
if (sector1_state != VALID)
status |= erase(&sectors[1]);
*/
return status;
}
// @brief Erases all data in the NVM.
//
// If this function fails subsequent calls to NVM functions (other than NVM_init or NVM_erase)
// cause undefined behavior.
// Caution: this function may take a long time (like 1 second)
//
// @returns 0 on success or a non-zero error code otherwise
int NVM_erase(void) {
read_sector_ = 0;
sectors[0].index = sectors[0].n_reserved;
sectors[1].index = sectors[1].n_reserved;
int state = 0;
state |= erase(&sectors[0]);
state |= erase(&sectors[1]);
return state;
}
// @brief Returns the maximum number of bytes that can be read using NVM_read.
// This holds until NVM_commit is called.
size_t NVM_get_max_read_length(void) {
return n_valid_ << 3;
}
// @brief Returns the maximum length (in bytes) that can passed to NVM_start_write.
// This holds until NVM_commit is called.
size_t NVM_get_max_write_length(void) {
sector_t *target = &sectors[1 - read_sector_];
return (target->n_data - target->n_reserved) << 3;
}
// @brief Reads from the latest committed block in the non-volatile memory.
// The function either succeeds or leaves the provided buffer unmodified.
// @param offset: offset in bytes (0 meaning the beginning of the valid area)
// @param data: buffer to write to
// @param length: length in bytes (if (offset + length) is out of range, the function fails)
// @returns 0 on success or a non-zero error code otherwise
int NVM_read(size_t offset, uint8_t *data, size_t length) {
if (offset + length > (n_valid_ << 3))
return -1;
sector_t *read_sector = &sectors[read_sector_];
const uint8_t *src_ptr = ((const uint8_t *)&read_sector->data[read_sector->index - n_valid_]) + offset;
memcpy(data, src_ptr, length);
return 0;
}
// @brief Starts an atomic write operation.
//
// The most recent valid NVM data is not modified or invalidated until NVM_commit is called.
// The length must be at most equal to the size indicated by NVM_get_max_write_length().
//
// @param length: Length of the staging block that should be created
int NVM_start_write(size_t length) {
int status = 0;
sector_t *target = &sectors[1 - read_sector_];
length = (length + 7) >> 3; // round to multiple of 64 bit
if (length > target->n_data - target->n_reserved)
return -1;
// make room for the new data
if (length > target->n_data - target->index)
if ((status = erase(target)))
return status;
// invalidate the fields we're about to write
status = set_allocation_state(target, target->index, length, INVALID);
if (status)
return status;
n_staging_area_ = length;
return 0;
}
// @brief Writes to the current data block that was opened with NVM_start_write.
//
// The operation fails if (offset + length) is larger than the length passed to NVM_start_write.
// The most recent valid NVM data is not modified or invalidated until NVM_commit is called.
// Warning: Writing different data to the same area multiple times during a single transaction
// will cause data corruption.
//
// @param offset: The offset in bytes, 0 being the beginning of the staging block.
// @param data: Pointer to the data that should be written
// @param length: Data length in bytes
int NVM_write(size_t offset, uint8_t *data, size_t length) {
if (offset + length > (n_staging_area_ << 3))
return -1;
sector_t *target = &sectors[1 - read_sector_];
HAL_FLASH_Unlock();
HAL_FLASH_ClearError();
// handle unaligned start
for (; (offset & 0x3) && length; ++data, ++offset, --length)
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE,
((uintptr_t)&target->data[target->index]) + offset, *data) != HAL_OK)
goto fail;
// write 32-bit values (64-bit doesn't work)
for (; length >= 4; data += 4, offset += 4, length -=4)
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
((uintptr_t)&target->data[target->index]) + offset, *(uint32_t*)data) != HAL_OK)
goto fail;
// handle unaligned end
for (; length; ++data, ++offset, --length)
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE,
((uintptr_t)&target->data[target->index]) + offset, *data) != HAL_OK)
goto fail;
HAL_FLASH_Lock();
return 0;
fail:
HAL_FLASH_Lock();
return HAL_FLASH_GetError(); // non-zero
}
// @brief Commits the new data to NVM atomically.
int NVM_commit(void) {
sector_t *read_sector = &sectors[read_sector_];
sector_t *write_sector = &sectors[1 - read_sector_];
// mark the newly-written fields as valid
int status = set_allocation_state(write_sector, write_sector->index, n_staging_area_, VALID);
if (status)
return status;
write_sector->index += n_staging_area_;
n_valid_ = n_staging_area_;
n_staging_area_ = 0;
read_sector_ = 1 - read_sector_;
// invalidate the other sector
if (read_sector->index < read_sector->n_data) {
status = set_allocation_state(read_sector, read_sector->index, 1, INVALID);
read_sector->index += 1;
} else {
status = erase(read_sector);
}
return status;
}
#include <cmsis_os.h>
#include <stdio.h>
/** @brief Call this at startup to test/demo the NVM driver
Expected output when starting with a fully erased NVM
[1st boot]
=== NVM TEST ===
NVM is empty
write 0x00, ..., 0x25 to NVM
new data committed to NVM
[2nd boot]
=== NVM TEST ===
NVM contains 40 valid bytes:
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 ff ff
write 0xbd, ..., 0xe2 to NVM
new data committed to NVM
[3rd boot]
=== NVM TEST ===
NVM contains 40 valid bytes:
bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc
cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc
dd de df e0 e1 e2 ff ff
write 0xcb, ..., 0xf0 to NVM
new data committed to NVM
*/
void NVM_demo(void) {
const size_t len = 38;
uint8_t data[len];
int progress = 0;
uint8_t seed = 0;
osDelay(100);
printf("=== NVM TEST ===\r\n"); osDelay(5);
//NVM_erase();
if (progress++, NVM_init() != 0)
goto fail;
// load bytes from NVM and print them
size_t available = NVM_get_max_read_length();
if (available) {
printf("NVM contains %d valid bytes:\r\n", available); osDelay(5);
uint8_t buf[available];
if (progress++, NVM_read(0, buf, available) != 0)
goto fail;
for (size_t pos = 0; pos < available; ++pos) {
seed += buf[pos];
printf(" %02x", buf[pos]);
if ((((pos + 1) % 16) == 0) || ((pos + 1) == available))
printf("\r\n");
osDelay(2);
}
} else {
printf("NVM is empty\r\n"); osDelay(5);
}
// store new bytes in NVM (data based on seed)
printf("write 0x%02x, ..., 0x%02x to NVM\r\n", seed, seed + len - 1); osDelay(5);
for (size_t i = 0; i < len; i++)
data[i] = seed++;
if (progress++, NVM_start_write(len) != 0)
goto fail;
if (progress++, NVM_write(0, data, len / 2))
goto fail;
if (progress++, NVM_write(len / 2, &data[len / 2], len - (len / 2)))
goto fail;
if (progress++, NVM_commit())
goto fail;
printf("new data committed to NVM\r\n"); osDelay(5);
return;
fail:
printf("NVM test failed at %d!\r\n", progress);
}
@@ -0,0 +1,33 @@
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __NVM_H
#define __NVM_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdlib.h>
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
int NVM_init(void);
int NVM_erase(void);
size_t NVM_get_max_read_length(void);
size_t NVM_get_max_write_length(void);
int NVM_read(size_t offset, uint8_t *data, size_t length);
int NVM_start_write(size_t length);
int NVM_write(size_t offset, uint8_t *data, size_t length);
int NVM_commit(void);
void NVM_demo(void);
#ifdef __cplusplus
}
#endif
#endif //__NVM_H
@@ -0,0 +1,129 @@
#include "stm32_spi_arbiter.hpp"
#include "stm32_system.h"
#include "utils.hpp"
#include <cmsis_os.h>
bool equals(const SPI_InitTypeDef& lhs, const SPI_InitTypeDef& rhs) {
return (lhs.Mode == rhs.Mode)
&& (lhs.Direction == rhs.Direction)
&& (lhs.DataSize == rhs.DataSize)
&& (lhs.CLKPolarity == rhs.CLKPolarity)
&& (lhs.CLKPhase == rhs.CLKPhase)
&& (lhs.NSS == rhs.NSS)
&& (lhs.BaudRatePrescaler == rhs.BaudRatePrescaler)
&& (lhs.FirstBit == rhs.FirstBit)
&& (lhs.TIMode == rhs.TIMode)
&& (lhs.CRCCalculation == rhs.CRCCalculation)
&& (lhs.CRCPolynomial == rhs.CRCPolynomial);
}
bool Stm32SpiArbiter::acquire_task(SpiTask* task) {
return !__atomic_exchange_n(&task->is_in_use, true, __ATOMIC_SEQ_CST);
}
void Stm32SpiArbiter::release_task(SpiTask* task) {
task->is_in_use = false;
}
bool Stm32SpiArbiter::start() {
if (!task_list_) {
return false;
}
SpiTask& task = *task_list_;
if (!equals(task.config, hspi_->Init)) {
HAL_SPI_DeInit(hspi_);
hspi_->Init = task.config;
HAL_SPI_Init(hspi_);
__HAL_SPI_ENABLE(hspi_);
}
task.ncs_gpio.write(false);
HAL_StatusTypeDef status = HAL_ERROR;
if (hspi_->hdmatx->State != HAL_DMA_STATE_READY || hspi_->hdmarx->State != HAL_DMA_STATE_READY) {
// This can happen if the DMA or interrupt priorities are not configured properly.
status = HAL_BUSY;
} else if (task.tx_buf && task.rx_buf) {
status = HAL_SPI_TransmitReceive_DMA(hspi_, (uint8_t*)task.tx_buf, task.rx_buf, task.length);
} else if (task.tx_buf) {
status = HAL_SPI_Transmit_DMA(hspi_, (uint8_t*)task.tx_buf, task.length);
} else if (task.rx_buf) {
status = HAL_SPI_Receive_DMA(hspi_, task.rx_buf, task.length);
}
if (status != HAL_OK) {
task.ncs_gpio.write(true);
}
return status == HAL_OK;
}
void Stm32SpiArbiter::transfer_async(SpiTask* task) {
task->next = nullptr;
// Append new task to task list.
// We could try to do this lock free but we could also use our time for useful things.
SpiTask** ptr = &task_list_;
CRITICAL_SECTION() {
while (*ptr)
ptr = &(*ptr)->next;
*ptr = task;
}
// If the list was empty before, kick off the SPI arbiter now
if (ptr == &task_list_) {
if (!start()) {
if (task->on_complete) {
(*task->on_complete)(task->on_complete_ctx, false);
}
}
}
}
// TODO: this currently only works when called in a CMSIS thread.
bool Stm32SpiArbiter::transfer(SPI_InitTypeDef config, Stm32Gpio ncs_gpio, const uint8_t* tx_buf, uint8_t* rx_buf, size_t length, uint32_t timeout_ms) {
volatile uint8_t result = 0xff;
SpiTask task = {
.config = config,
.ncs_gpio = ncs_gpio,
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.length = length,
.on_complete = [](void* ctx, bool success) { *(volatile uint8_t*)ctx = success ? 1 : 0; },
.on_complete_ctx = (void*)&result,
.is_in_use = false,
.next = nullptr
};
transfer_async(&task);
while (result == 0xff) {
osDelay(1); // TODO: honor timeout
}
return result;
}
void Stm32SpiArbiter::on_complete() {
if (!task_list_) {
return; // this should not happen
}
// Wrap up transfer
task_list_->ncs_gpio.write(true);
if (task_list_->on_complete) {
(*task_list_->on_complete)(task_list_->on_complete_ctx, true);
}
// Start next task if any
SpiTask* next = nullptr;
CRITICAL_SECTION() {
next = task_list_ = task_list_->next;
}
if (next) {
start();
}
}
@@ -0,0 +1,94 @@
#ifndef __STM32_SPI_ARBITER_HPP
#define __STM32_SPI_ARBITER_HPP
#include "stm32_gpio.hpp"
#include <spi.h>
class Stm32SpiArbiter {
public:
struct SpiTask {
SPI_InitTypeDef config;
Stm32Gpio ncs_gpio;
const uint8_t* tx_buf;
uint8_t* rx_buf;
size_t length;
void (*on_complete)(void*, bool);
void* on_complete_ctx;
bool is_in_use = false;
struct SpiTask* next;
};
Stm32SpiArbiter(SPI_HandleTypeDef* hspi): hspi_(hspi) {}
/**
* Reserves the task for the caller if it's not in use currently.
*
* This can be used by the caller to ensure that the task structure is not
* overwritten while it's in use in a preceding transfer.
*
* Example:
*
* if (acquire_task(&task)) {
* transfer_async(&task)
* }
*
* A call to release_task() makes the task available for use again.
*/
static bool acquire_task(SpiTask* task);
/**
* Releases the task so that the next call to `acquire_task()` returns true.
* This should usually be called inside the on_complete() callback after
* the rx buffer has been processed.
*/
static void release_task(SpiTask* task);
/**
* @brief Enqueues a non-blocking transfer.
*
* Once the transfer completes, fails or is aborted, the callback is invoked.
*
* This function is thread-safe with respect to all other public functions
* of this class.
*
* @param task: Contains all configuration data for this transfer.
* The struct pointed to by this argument must remain valid and
* unmodified until the completion callback is invoked.
*/
void transfer_async(SpiTask* task);
/**
* @brief Executes a blocking transfer.
*
* If the SPI is busy this function waits until it becomes available or
* the specified timeout passes, whichever comes first.
*
* Returns true on successful transfer or false otherwise.
*
* This function is thread-safe with respect to all other public functions
* of this class.
*
* @param config: The SPI configuration to apply for this transfer.
* @param ncs_gpio: The active low GPIO to actuate during this transfer.
* @param tx_buf: Buffer for the outgoing data to be sent. Can be null unless
* rx_buf is null too.
* @param rx_buf: Buffer for the incoming data to be sent. Can be null unless
* tx_buf is null too.
*/
bool transfer(SPI_InitTypeDef config, Stm32Gpio ncs_gpio, const uint8_t* tx_buf, uint8_t* rx_buf, size_t length, uint32_t timeout_ms);
/**
* @brief Completion method to be called from HAL_SPI_TxCpltCallback,
* HAL_SPI_RxCpltCallback and HAL_SPI_TxRxCpltCallback.
*/
void on_complete();
private:
bool start();
SPI_HandleTypeDef* hspi_;
SpiTask* task_list_ = nullptr;
};
#endif // __STM32_SPI_ARBITER_HPP
@@ -0,0 +1,4 @@
#include "stm32_system.h"
uint32_t irq_counters[254]; // 14 core interrupts, 240 NVIC interrupts
@@ -0,0 +1,70 @@
#ifndef __STM32_SYSTEM_H
#define __STM32_SYSTEM_H
#if defined(STM32F405xx)
#include <stm32f405xx.h>
#elif defined(STM32F722xx)
#include <stm32f722xx.h>
#else
#error "unknown STM32 microcontroller"
#endif
// C/C++ definitions
#ifdef __cplusplus
extern "C" {
#endif
// Uncomment the following line to sacrifice 1kB of RAM for the ability to
// monitor the number of times each interrupt fires.
//#define ENABLE_IRQ_COUNTER
#ifdef ENABLE_IRQ_COUNTER
extern uint32_t irq_counters[];
#define COUNT_IRQ(irqn) (++irq_counters[irqn + 14])
#define GET_IRQ_COUNTER(irqn) irq_counters[irqn + 14]
#else
#define COUNT_IRQ(irqn) ((void)0)
#define GET_IRQ_COUNTER(irqn) 0
#endif
static inline uint32_t cpu_enter_critical() {
uint32_t primask = __get_PRIMASK();
__disable_irq();
return primask;
}
static inline void cpu_exit_critical(uint32_t priority_mask) {
__set_PRIMASK(priority_mask);
}
#ifdef __cplusplus
}
#endif
// C++ only definitions
#ifdef __cplusplus
struct CriticalSectionContext {
CriticalSectionContext(const CriticalSectionContext&) = delete;
CriticalSectionContext(const CriticalSectionContext&&) = delete;
void operator=(const CriticalSectionContext&) = delete;
void operator=(const CriticalSectionContext&&) = delete;
operator bool() { return true; };
CriticalSectionContext() : mask_(cpu_enter_critical()) {}
~CriticalSectionContext() { cpu_exit_critical(mask_); }
uint32_t mask_;
bool exit_ = false;
};
#ifdef __clang__
#define CRITICAL_SECTION() for (CriticalSectionContext __critical_section_context; !__critical_section_context.exit_; __critical_section_context.exit_ = true)
#else
#define CRITICAL_SECTION() if (CriticalSectionContext __critical_section_context{})
#endif
#endif
#endif // __STM32_SYSTEM_H
@@ -0,0 +1,83 @@
#ifndef __STM32_TIMER_HPP
#define __STM32_TIMER_HPP
#include "stm32_system.h"
#include <tim.h>
#include <array>
class Stm32Timer {
public:
/**
* @brief Starts multiple timers deterministically and synchronously from the
* specified offset.
*
* All timers are atomically (*) put into the following state (regardless of
* their previous state/configuration):
* - TIMx_CNT will be initialized according to the corresponding counter[i] parameter.
* - If the timer is in center-aligned mode, it will be set to up-counting direction.
* - The update repetition counter is reset to TIMx_RCR (if applicable).
* - The prescaler counter is reset.
* - Update interrupts are disabled.
* - The counter put into running state.
*
* This function is implemented by generating an update event on all selected timers.
* That means as a side effect all things that are connected to the update event
* except the interrupt routine itself (i.e. ADCs, DMAs, slave timers, etc) will
* be triggered.
*
* Also you probably want to disable any connected PWM outputs to prevent glitches.
*
* (*) Best-effort atomically. There will be skew of a handful of clock cycles
* but it's always the same given the compiler version and configuration.
*/
template<size_t I>
static void start_synchronously(std::array<TIM_HandleTypeDef*, I> timers, std::array<size_t, I> counters) {
start_synchronously_impl(timers, counters, std::make_index_sequence<I>());
}
private:
#pragma GCC push_options
#pragma GCC optimize (3)
template<size_t I, size_t ... Is>
static void start_synchronously_impl(std::array<TIM_HandleTypeDef*, I> timers, std::array<size_t, I> counters, std::index_sequence<Is...>) {
for (size_t i = 0; i < I; ++i) {
TIM_HandleTypeDef* htim = timers[i];
// Stop the timer so we can start all of them later more atomically.
htim->Instance->CR1 &= ~TIM_CR1_CEN;
// Generate update event to force all of the timer's registers into
// a known state.
__HAL_TIM_DISABLE_IT(htim, TIM_IT_UPDATE);
htim->Instance->EGR |= TIM_EGR_UG;
__HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
// Load counter with the desired value.
htim->Instance->CNT = counters[i];
}
register volatile uint32_t* cr_addr[I];
register uint32_t cr_val[I];
for (size_t i = 0; i < I; ++i) {
cr_addr[i] = &timers[i]->Instance->CR1;
cr_val[i] = timers[i]->Instance->CR1 | TIM_CR1_CEN;
}
// Restart all timers as atomically as possible.
// By inspection we find that this is compiled to the following code:
// f7ff faa0 bl 800bdd0 <cpu_enter_critical()>
// f8c9 6000 str.w r6, [r9]
// f8c8 5000 str.w r5, [r8]
// 603c str r4, [r7, #0]
// f7ff fa9d bl 800bdd8 <cpu_exit_critical(unsigned long)>
uint32_t mask = cpu_enter_critical();
int dummy[I] = {(*cr_addr[Is] = cr_val[Is], 0)...};
(void)dummy;
cpu_exit_critical(mask);
}
#pragma GCC pop_options
};
#endif // __STM32_TIMER_HPP
@@ -0,0 +1,42 @@
#ifndef __GATE_DRIVER_HPP
#define __GATE_DRIVER_HPP
struct GateDriverBase {
/**
* @brief Unlocks or locks the gate signals of the gate driver.
*
* While locked the PWM inputs are ignored and the switches are always in
* OFF state.
* Not all gate drivers implement this function and may return true even if
* the gate driver was not locked.
*/
virtual bool set_enabled(bool enabled) = 0;
/**
* @brief Returns false if the gate driver is in a state where the output
* drive stages are disarmed or not properly configured (e.g. because they
* are not initialized or there was a fault condition).
*/
virtual bool is_ready() = 0;
};
struct OpAmpBase {
/**
* @brief Returns false if the opamp is in a state where it's not operating
* with the latest configured gain (e.g. because it was not initialized or
* there was a fault condition).
*/
virtual bool is_ready() = 0;
/**
* @brief Returns the neutral voltage of the OpAmp in Volts
*/
virtual float get_midpoint() = 0;
/**
* @brief Returns the maximum voltage swing away from the midpoint voltage (in Volts)
*/
virtual float get_max_output_swing() = 0;
};
#endif // __GATE_DRIVER_HPP
@@ -0,0 +1,20 @@
/*
* Since at least FreeRTOS V7.5.3 uxTopUsedPriority is no longer
* present in the kernel, so it has to be supplied by other means for
* OpenOCD's threads awareness.
*
* Add this file to your project, and, if you're using --gc-sections,
* ``--undefined=uxTopUsedPriority'' (or
* ``-Wl,--undefined=uxTopUsedPriority'' when using gcc for final
* linking) to your LDFLAGS; same with all the other symbols you need.
*/
#include "FreeRTOS.h"
#ifdef __GNUC__
#define USED __attribute__((used))
#else
#define USED
#endif
const int USED uxTopUsedPriority = configMAX_PRIORITIES - 1;
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Oskar Weigl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+109
View File
@@ -0,0 +1,109 @@
# This is only a stub for various commands.
# Tup is used for the actual compilation.
BUILD_DIR = build
FIRMWARE = $(BUILD_DIR)/ODriveFirmware.elf
FIRMWARE_HEX = $(BUILD_DIR)/ODriveFirmware.hex
PROGRAMMER_CMD=$(if $(value PROGRAMMER),-c 'hla_serial $(PROGRAMMER)',)
include tup.config # source build configuration to get CONFIG_BOARD_VERSION
ifeq ($(shell python -c "import sys; print(sys.version_info.major)"), 3)
PY_CMD := python -B
else
PY_CMD := python3 -B
endif
ifneq (,$(findstring v3.,$(CONFIG_BOARD_VERSION)))
OPENOCD := openocd -f interface/stlink-v2.cfg $(PROGRAMMER_CMD) -f target/stm32f4x.cfg -c init
GDB := arm-none-eabi-gdb --ex 'target extended-remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f4x.cfg" -c "gdb_port pipe; log_output openocd.log"' --ex 'monitor reset halt'
else ifneq (,$(findstring v4.,$(CONFIG_BOARD_VERSION)))
OPENOCD := openocd -f interface/stlink.cfg $(PROGRAMMER_CMD) -f target/stm32f7x.cfg -c 'reset_config none separate' -c init
GDB := arm-none-eabi-gdb --ex 'target extended-remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f7x.cfg" -c "reset_config none separate" -c "gdb_port pipe; log_output openocd.log"' --ex 'monitor reset halt'
else
$(error unknown board version)
endif
$(info board version: $(CONFIG_BOARD_VERSION))
all:
@mkdir -p autogen
@$(PY_CMD) ../tools/odrive/version.py --output autogen/version.c
@tup --quiet -no-environ-check
@$(PY_CMD) interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py
@$(PY_CMD) interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/arduino_enums_template.j2 --output ../Arduino/ODriveArduino/ODriveEnums.h
@cd ../tools/ && $(PY_CMD) create_can_dbc.py
# Copy libfibre files to odrivetool if they were built
@ ! test -f "fibre-cpp/build/libfibre-linux-amd64.so" || cp fibre-cpp/build/libfibre-linux-amd64.so ../tools/odrive/pyfibre/fibre/
@ ! test -f "fibre-cpp/build/libfibre-linux-armhf.so" || cp fibre-cpp/build/libfibre-linux-armhf.so ../tools/odrive/pyfibre/fibre/
@ ! test -f "fibre-cpp/build/libfibre-linux-aarch64.so" || cp fibre-cpp/build/libfibre-linux-aarch64.so ../tools/odrive/pyfibre/fibre/
@ ! test -f "fibre-cpp/build/libfibre-macos-x86.dylib" || cp fibre-cpp/build/libfibre-macos-x86.dylib ../tools/odrive/pyfibre/fibre/
@ ! test -f "fibre-cpp/build/libfibre-windows-amd64.dll" || cp fibre-cpp/build/libfibre-windows-amd64.dll ../tools/odrive/pyfibre/fibre/
libfibre-linux-armhf:
docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/fibre-linux-armhf-build:/build/build -w /build fibre-compiler configs/linux-armhf.config
cp /tmp/fibre-linux-armhf-build/libfibre-*.so ../tools/odrive/pyfibre/fibre/
libfibre-all:
docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/linux-amd64.config
cp /tmp/libfibre-build/libfibre-linux-amd64.so ../tools/odrive/pyfibre/fibre/
docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/linux-armhf.config
cp /tmp/libfibre-build/libfibre-linux-armhf.so ../tools/odrive/pyfibre/fibre/
docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/linux-aarch64.config
cp /tmp/libfibre-build/libfibre-linux-aarch64.so ../tools/odrive/pyfibre/fibre/
docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/macos-x86.config
cp /tmp/libfibre-build/libfibre-macos-x86.dylib ../tools/odrive/pyfibre/fibre/
docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/windows-amd64.config
cp /tmp/libfibre-build/libfibre-windows-amd64.dll ../tools/odrive/pyfibre/fibre/
docker run -it -v "`pwd`/fibre-cpp":/build -v /tmp/libfibre-build:/build/build -w /build fibre-compiler configs/wasm.config
cp /tmp/libfibre-build/libfibre-wasm.* ../GUI/fibre-js/
clean:
-rm -fR .dep $(BUILD_DIR)
flash-stlink2: all
$(OPENOCD) \
-c 'reset halt' \
-c 'flash write_image erase $(FIRMWARE)' \
-c 'reset run' \
-c exit
gdb-stlink2:
$(GDB) $(FIRMWARE)
# Erase entire STM32
erase-stlink2:
$(OPENOCD) -c 'reset halt' -c 'flash erase_sector 0 0 last' -c exit
# Sometimes the STM32 will get it's protection bits set for unknown reasons. Unlock it with this command
unlock-stlink2:
$(OPENOCD) -c 'reset halt' -c 'stm32f2x unlock 0'
flash-bmp: all
arm-none-eabi-gdb --ex 'target extended-remote $(BMP_PORT)' \
--ex 'monitor swdp_scan' \
--ex 'attach 1' \
--ex 'load' \
--ex 'detach' \
--ex 'quit' \
$(FIRMWARE)
gdb-bmp: all
arm-none-eabi-gdb --ex 'target extended-remote /dev/stlink' \
--ex 'monitor swdp_scan' \
--ex 'attach 1' \
--ex 'load' $(FIRMWARE)
dfu: all
python ../tools/odrivetool $(if $(value SERIAL_NUMBER),--serial-number $(SERIAL_NUMBER),) dfu $(FIRMWARE_HEX)
flash: flash-stlink2
gdb: gdb-stlink2
erase: erase-stlink2
unlock: unlock-stlink2
.PHONY: stlink2-config flash-stlink2 gdb-stlink2 erase-stlink2 unlock-stlink2
.PHONY: flash-bmp gdb-bmp
.PHONY: all clean flash gdb erase unlock dfu fibre
@@ -0,0 +1,45 @@
#include "acim_estimator.hpp"
#include <board.h>
void AcimEstimator::update(uint32_t timestamp) {
std::optional<float> rotor_phase = rotor_phase_src_.present();
std::optional<float> rotor_phase_vel = rotor_phase_vel_src_.present();
std::optional<float2D> idq = idq_src_.present();
if (!rotor_phase.has_value() || !rotor_phase_vel.has_value() || !idq.has_value()) {
active_ = false;
return;
}
auto [id, iq] = *idq;
float dt = (float)(timestamp - last_timestamp_) / (float)TIM_1_8_CLOCK_HZ;
last_timestamp_ = timestamp;
if (!active_) {
// Skip first iteration and use it to reset state
rotor_flux_ = 0.0f;
phase_offset_ = 0.0f;
active_ = true;
return;
}
// Note that the effect of the current commands on the real currents is actually 1.5 PWM cycles later
// However the rotor time constant is (usually) so slow that it doesn't matter
// So we elect to write it as if the effect is immediate, to have cleaner code
// acim_rotor_flux is normalized to units of [A] tracking Id; rotor inductance is unspecified
float dflux_by_dt = config_.slip_velocity * (id - rotor_flux_);
rotor_flux_ += dflux_by_dt * dt;
float slip_velocity = config_.slip_velocity * (iq / rotor_flux_);
// Check for issues with small denominator.
if (is_nan(slip_velocity) || (std::abs(slip_velocity) > 0.1f / dt)) {
slip_velocity = 0.0f;
}
slip_vel_ = slip_velocity; // reporting only
stator_phase_vel_ = *rotor_phase_vel + slip_velocity;
phase_offset_ = wrap_pm_pi(phase_offset_ + slip_velocity * dt);
stator_phase_ = wrap_pm_pi(*rotor_phase + phase_offset_);
}
@@ -0,0 +1,36 @@
#ifndef __ACIM_ESTIMATOR_HPP
#define __ACIM_ESTIMATOR_HPP
#include <component.hpp>
#include <cmath>
#include <autogen/interfaces.hpp>
class AcimEstimator : public ComponentBase {
public:
struct Config_t {
float slip_velocity = 14.706f; // [rad/s electrical] = 1/rotor_tau
};
void update(uint32_t timestamp) final;
// Config
Config_t config_;
// Inputs
InputPort<float> rotor_phase_src_;
InputPort<float> rotor_phase_vel_src_;
InputPort<float2D> idq_src_;
// State variables
bool active_ = false;
uint32_t last_timestamp_ = 0;
float rotor_flux_ = 0.0f; // [A]
float phase_offset_ = 0.0f; // [A]
// Outputs
OutputPort<float> slip_vel_ = 0.0f; // [rad/s electrical]
OutputPort<float> stator_phase_vel_ = 0.0f; // [rad/s] rotor flux angular velocity estimate
OutputPort<float> stator_phase_ = 0.0f; // [rad] rotor flux phase angle estimate
};
#endif // __ACIM_ESTIMATOR_HPP
@@ -0,0 +1,123 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Library
* Title: arm_cos_f32.c
* Description: Fast cosine calculation for floating-point values
*
* $Date: 27. January 2017
* $Revision: V.1.5.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <board.h>
#include "arm_math.h"
#include "arm_common_tables.h"
/**
* @ingroup groupFastMath
*/
/**
* @defgroup cos Cosine
*
* Computes the trigonometric cosine function using a combination of table lookup
* and linear interpolation. There are separate functions for
* Q15, Q31, and floating-point data types.
* The input to the floating-point version is in radians and in the range [0 2*pi) while the
* fixed-point Q15 and Q31 have a scaled input with the range
* [0 +0.9999] mapping to [0 2*pi). The fixed-point range is chosen so that a
* value of 2*pi wraps around to 0.
*
* The implementation is based on table lookup using 256 values together with linear interpolation.
* The steps used are:
* -# Calculation of the nearest integer table index
* -# Compute the fractional portion (fract) of the table index.
* -# The final result equals <code>(1.0f-fract)*a + fract*b;</code>
*
* where
* <pre>
* b=Table[index+0];
* c=Table[index+1];
* </pre>
*/
/**
* @addtogroup cos
* @{
*/
/**
* @brief Fast approximation to the trigonometric cosine function for floating-point data.
* @param[in] x input value in radians.
* @return cos(x).
*/
float32_t our_arm_cos_f32(
float32_t x)
{
float32_t cosVal, fract, in; /* Temporary variables for input, output */
uint16_t index; /* Index variable */
float32_t a, b; /* Two nearest output values */
int32_t n;
float32_t findex;
/* input x is in radians */
/* Scale the input to [0 1] range from [0 2*PI] , divide input by 2*pi, add 0.25 (pi/2) to read sine table */
in = x * 0.159154943092f + 0.25f;
/* Calculation of floor value of input */
n = (int32_t) in;
/* Make negative values towards -infinity */
if (in < 0.0f)
{
n--;
}
/* Map input value to [0 1] */
in = in - (float32_t) n;
/* Calculation of index of the table */
findex = (float32_t)FAST_MATH_TABLE_SIZE * in;
index = (uint16_t)findex;
/* when "in" is exactly 1, we need to rotate the index down to 0 */
if (index >= FAST_MATH_TABLE_SIZE) {
index = 0;
findex -= (float32_t)FAST_MATH_TABLE_SIZE;
}
/* fractional value calculation */
fract = findex - (float32_t) index;
/* Read two nearest values of input value from the cos table */
a = sinTable_f32[index];
b = sinTable_f32[index+1];
/* Linear interpolation process */
cosVal = (1.0f-fract)*a + fract*b;
/* Return the output value */
return (cosVal);
}
/**
* @} end of cos group
*/
@@ -0,0 +1,123 @@
/* ----------------------------------------------------------------------
* Project: CMSIS DSP Library
* Title: arm_sin_f32.c
* Description: Fast sine calculation for floating-point values
*
* $Date: 27. January 2017
* $Revision: V.1.5.1
*
* Target Processor: Cortex-M cores
* -------------------------------------------------------------------- */
/*
* Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <board.h>
#include "arm_math.h"
#include "arm_common_tables.h"
/**
* @ingroup groupFastMath
*/
/**
* @defgroup sin Sine
*
* Computes the trigonometric sine function using a combination of table lookup
* and linear interpolation. There are separate functions for
* Q15, Q31, and floating-point data types.
* The input to the floating-point version is in radians and in the range [0 2*pi) while the
* fixed-point Q15 and Q31 have a scaled input with the range
* [0 +0.9999] mapping to [0 2*pi). The fixed-point range is chosen so that a
* value of 2*pi wraps around to 0.
*
* The implementation is based on table lookup using 256 values together with linear interpolation.
* The steps used are:
* -# Calculation of the nearest integer table index
* -# Compute the fractional portion (fract) of the table index.
* -# The final result equals <code>(1.0f-fract)*a + fract*b;</code>
*
* where
* <pre>
* b=Table[index+0];
* c=Table[index+1];
* </pre>
*/
/**
* @addtogroup sin
* @{
*/
/**
* @brief Fast approximation to the trigonometric sine function for floating-point data.
* @param[in] x input value in radians.
* @return sin(x).
*/
float32_t our_arm_sin_f32(
float32_t x)
{
float32_t sinVal, fract, in; /* Temporary variables for input, output */
uint16_t index; /* Index variable */
float32_t a, b; /* Two nearest output values */
int32_t n;
float32_t findex;
/* input x is in radians */
/* Scale the input to [0 1] range from [0 2*PI] , divide input by 2*pi */
in = x * 0.159154943092f;
/* Calculation of floor value of input */
n = (int32_t) in;
/* Make negative values towards -infinity */
if (x < 0.0f)
{
n--;
}
/* Map input value to [0 1] */
in = in - (float32_t) n;
/* Calculation of index of the table */
findex = (float32_t)FAST_MATH_TABLE_SIZE * in;
index = (uint16_t)findex;
/* when "in" is exactly 1, we need to rotate the index down to 0 */
if (index >= FAST_MATH_TABLE_SIZE) {
index = 0;
findex -= (float32_t)FAST_MATH_TABLE_SIZE;
}
/* fractional value calculation */
fract = findex - (float32_t) index;
/* Read two nearest values of input value from the sin table */
a = sinTable_f32[index];
b = sinTable_f32[index+1];
/* Linear interpolation process */
sinVal = (1.0f-fract)*a + fract*b;
/* Return the output value */
return (sinVal);
}
/**
* @} end of sin group
*/
@@ -0,0 +1,602 @@
#include <stdlib.h>
#include <functional>
#include "gpio.h"
#include "odrive_main.h"
#include "utils.hpp"
#include "communication/interface_can.hpp"
Axis::Axis(int axis_num,
uint16_t default_step_gpio_pin,
uint16_t default_dir_gpio_pin,
osPriority thread_priority,
Encoder& encoder,
SensorlessEstimator& sensorless_estimator,
Controller& controller,
Motor& motor,
TrapezoidalTrajectory& trap,
Endstop& min_endstop,
Endstop& max_endstop,
MechanicalBrake& mechanical_brake)
: axis_num_(axis_num),
default_step_gpio_pin_(default_step_gpio_pin),
default_dir_gpio_pin_(default_dir_gpio_pin),
thread_priority_(thread_priority),
encoder_(encoder),
sensorless_estimator_(sensorless_estimator),
controller_(controller),
motor_(motor),
trap_traj_(trap),
min_endstop_(min_endstop),
max_endstop_(max_endstop),
mechanical_brake_(mechanical_brake)
{
encoder_.axis_ = this;
sensorless_estimator_.axis_ = this;
controller_.axis_ = this;
motor_.axis_ = this;
trap_traj_.axis_ = this;
min_endstop_.axis_ = this;
max_endstop_.axis_ = this;
mechanical_brake_.axis_ = this;
}
Axis::LockinConfig_t Axis::default_calibration() {
Axis::LockinConfig_t config;
config.current = 10.0f; // [A]
config.ramp_time = 0.4f; // [s]
config.ramp_distance = 1 * M_PI; // [rad]
config.accel = 20.0f; // [rad/s^2]
config.vel = 40.0f; // [rad/s]
config.finish_distance = 100.0f * 2.0f * M_PI; // [rad]
config.finish_on_vel = false;
config.finish_on_distance = true;
config.finish_on_enc_idx = true;
return config;
}
Axis::LockinConfig_t Axis::default_sensorless() {
Axis::LockinConfig_t config;
config.current = 10.0f; // [A]
config.ramp_time = 0.4f; // [s]
config.ramp_distance = 1 * M_PI; // [rad]
config.accel = 200.0f; // [rad/s^2]
config.vel = 400.0f; // [rad/s]
config.finish_distance = 100.0f; // [rad]
config.finish_on_vel = true;
config.finish_on_distance = false;
config.finish_on_enc_idx = false;
return config;
}
static void step_cb_wrapper(void* ctx) {
reinterpret_cast<Axis*>(ctx)->step_cb();
}
bool Axis::apply_config() {
config_.parent = this;
decode_step_dir_pins();
watchdog_feed();
return true;
}
void Axis::clear_config() {
config_ = {};
config_.step_gpio_pin = default_step_gpio_pin_;
config_.dir_gpio_pin = default_dir_gpio_pin_;
config_.can.node_id = axis_num_;
}
static void run_state_machine_loop_wrapper(void* ctx) {
reinterpret_cast<Axis*>(ctx)->run_state_machine_loop();
reinterpret_cast<Axis*>(ctx)->thread_id_valid_ = false;
}
// @brief Starts run_state_machine_loop in a new thread
void Axis::start_thread() {
osThreadDef(thread_def, run_state_machine_loop_wrapper, thread_priority_, 0, stack_size_ / sizeof(StackType_t));
thread_id_ = osThreadCreate(osThread(thread_def), this);
thread_id_valid_ = true;
}
/**
* @brief Blocks until at least one complete control loop has been executed.
*/
bool Axis::wait_for_control_iteration() {
osSignalWait(0x0001, osWaitForever); // this might return instantly
osSignalWait(0x0001, osWaitForever); // this might be triggered at the
// end of a control loop iteration
// which was started before we entered
// this function
osSignalWait(0x0001, osWaitForever);
return true;
}
// step/direction interface
void Axis::step_cb() {
if (step_dir_active_) {
dir_gpio_.read() ? ++steps_ : --steps_;
controller_.input_pos_updated();
}
}
void Axis::decode_step_dir_pins() {
step_gpio_ = get_gpio(config_.step_gpio_pin);
dir_gpio_ = get_gpio(config_.dir_gpio_pin);
}
// @brief (de)activates step/dir input
void Axis::set_step_dir_active(bool active) {
if (active) {
// Subscribe to rising edges of the step GPIO
if (!step_gpio_.subscribe(true, false, step_cb_wrapper, this)) {
odrv.misconfigured_ = true;
}
step_dir_active_ = true;
} else {
step_dir_active_ = false;
// Unsubscribe from step GPIO
// TODO: if we change the GPIO while the subscription is active and then
// unsubscribe then the unsubscribe is for the wrong pin.
step_gpio_.unsubscribe();
}
}
// @brief Do axis level checks and call subcomponent do_checks
// Returns true if everything is ok.
bool Axis::do_checks(uint32_t timestamp) {
// Sub-components should use set_error which will propegate to this error_
motor_.effective_current_lim();
motor_.do_checks(timestamp);
// Check for endstop presses
if (min_endstop_.config_.enabled && min_endstop_.rose() && !(current_state_ == AXIS_STATE_HOMING)) {
error_ |= ERROR_MIN_ENDSTOP_PRESSED;
} else if (max_endstop_.config_.enabled && max_endstop_.rose() && !(current_state_ == AXIS_STATE_HOMING)) {
error_ |= ERROR_MAX_ENDSTOP_PRESSED;
}
return check_for_errors();
}
// @brief Feed the watchdog to prevent watchdog timeouts.
void Axis::watchdog_feed() {
watchdog_current_value_ = get_watchdog_reset();
}
// @brief Check the watchdog timer for expiration. Also sets the watchdog error bit if expired.
bool Axis::watchdog_check() {
if (!config_.enable_watchdog) return true;
// explicit check here to ensure that we don't underflow back to UINT32_MAX
if (watchdog_current_value_ > 0) {
watchdog_current_value_--;
return true;
} else {
error_ |= ERROR_WATCHDOG_TIMER_EXPIRED;
return false;
}
}
bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed,
std::function<bool(bool)> loop_cb) {
CRITICAL_SECTION() {
// Reset state variables
open_loop_controller_.Idq_setpoint_ = {0.0f, 0.0f};
open_loop_controller_.Vdq_setpoint_ = {0.0f, 0.0f};
open_loop_controller_.phase_ = 0.0f;
open_loop_controller_.phase_vel_ = 0.0f;
open_loop_controller_.max_current_ramp_ = lockin_config.current / lockin_config.ramp_time;
open_loop_controller_.max_voltage_ramp_ = lockin_config.current / lockin_config.ramp_time;
open_loop_controller_.max_phase_vel_ramp_ = lockin_config.accel;
open_loop_controller_.target_current_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? lockin_config.current : 0.0f;
open_loop_controller_.target_voltage_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? 0.0f : lockin_config.current;
open_loop_controller_.target_vel_ = lockin_config.vel;
open_loop_controller_.total_distance_ = 0.0f;
motor_.current_control_.enable_current_control_src_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL;
motor_.current_control_.Idq_setpoint_src_.connect_to(&open_loop_controller_.Idq_setpoint_);
motor_.current_control_.Vdq_setpoint_src_.connect_to(&open_loop_controller_.Vdq_setpoint_);
motor_.current_control_.phase_src_.connect_to(&open_loop_controller_.phase_);
acim_estimator_.rotor_phase_src_.connect_to(&open_loop_controller_.phase_);
motor_.phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_);
motor_.current_control_.phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_);
acim_estimator_.rotor_phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_);
}
wait_for_control_iteration();
motor_.arm(&motor_.current_control_);
bool subscribed_to_idx_once = false;
bool success = false;
float dir = lockin_config.vel >= 0.0f ? 1.0f : -1.0f;
while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_) {
bool reached_target_vel = std::abs(open_loop_controller_.phase_vel_.any().value_or(0.0f) - lockin_config.vel) <= std::numeric_limits<float>::epsilon();
bool reached_target_dist = open_loop_controller_.total_distance_.any().value_or(0.0f) * dir >= lockin_config.finish_distance * dir;
// Check if terminal condition is reached
bool terminal_condition = (reached_target_vel && lockin_config.finish_on_vel)
|| (reached_target_dist && lockin_config.finish_on_distance)
|| (encoder_.index_found_ && lockin_config.finish_on_enc_idx);
if (terminal_condition) {
success = true;
break;
}
// Activate index pin as soon as target velocity was reached. This is
// to avoid hitting the index from the wrong direction.
if (reached_target_vel && !encoder_.index_found_ && !subscribed_to_idx_once) {
encoder_.set_idx_subscribe(true);
subscribed_to_idx_once = true;
}
if (loop_cb)
if (!loop_cb(reached_target_vel))
break;
// TODO: use new sync function instead
asm volatile ("" ::: "memory");
osDelay(1);
}
if (!success || !remain_armed) {
motor_.disarm();
}
return success;
}
bool Axis::start_closed_loop_control() {
bool sensorless_mode = config_.enable_sensorless_mode;
if (sensorless_mode) {
// TODO: restart if desired
if (!run_lockin_spin(config_.sensorless_ramp, true)) {
return false;
}
}
// Hook up the data paths between the components
CRITICAL_SECTION() {
if (sensorless_mode) {
controller_.pos_estimate_linear_src_.disconnect();
controller_.pos_estimate_circular_src_.disconnect();
controller_.pos_wrap_src_.disconnect();
controller_.vel_estimate_src_.connect_to(&sensorless_estimator_.vel_estimate_);
} else if (controller_.config_.load_encoder_axis < AXIS_COUNT) {
Axis* ax = &axes[controller_.config_.load_encoder_axis];
controller_.pos_estimate_circular_src_.connect_to(&ax->encoder_.pos_circular_);
controller_.pos_wrap_src_.connect_to(&controller_.config_.circular_setpoint_range);
controller_.pos_estimate_linear_src_.connect_to(&ax->encoder_.pos_estimate_);
controller_.vel_estimate_src_.connect_to(&ax->encoder_.vel_estimate_);
} else {
controller_.pos_estimate_circular_src_.disconnect();
controller_.pos_estimate_linear_src_.disconnect();
controller_.pos_wrap_src_.disconnect();
controller_.vel_estimate_src_.disconnect();
controller_.set_error(Controller::ERROR_INVALID_LOAD_ENCODER);
return false;
}
// To avoid any transient on startup, we intialize the setpoint to be the current position
controller_.control_mode_updated();
controller_.input_pos_updated();
// Avoid integrator windup issues
controller_.vel_integrator_torque_ = 0.0f;
motor_.torque_setpoint_src_.connect_to(&controller_.torque_output_);
motor_.direction_ = sensorless_mode ? 1.0f : encoder_.config_.direction;
motor_.current_control_.enable_current_control_src_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL;
motor_.current_control_.Idq_setpoint_src_.connect_to(&motor_.Idq_setpoint_);
motor_.current_control_.Vdq_setpoint_src_.connect_to(&motor_.Vdq_setpoint_);
bool is_acim = motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM;
// phase
OutputPort<float>* phase_src = sensorless_mode ? &sensorless_estimator_.phase_ : &encoder_.phase_;
acim_estimator_.rotor_phase_src_.connect_to(phase_src);
OutputPort<float>* stator_phase_src = is_acim ? &acim_estimator_.stator_phase_ : phase_src;
motor_.current_control_.phase_src_.connect_to(stator_phase_src);
// phase vel
OutputPort<float>* phase_vel_src = sensorless_mode ? &sensorless_estimator_.phase_vel_ : &encoder_.phase_vel_;
acim_estimator_.rotor_phase_vel_src_.connect_to(phase_vel_src);
OutputPort<float>* stator_phase_vel_src = is_acim ? &acim_estimator_.stator_phase_vel_ : phase_vel_src;
motor_.phase_vel_src_.connect_to(stator_phase_vel_src);
motor_.current_control_.phase_vel_src_.connect_to(stator_phase_vel_src);
if (sensorless_mode) {
// Make the final velocity of the loĉk-in spin the setpoint of the
// closed loop controller to allow for smooth transition.
float vel = config_.sensorless_ramp.vel / (2.0f * M_PI * motor_.config_.pole_pairs);
controller_.input_vel_ = vel;
controller_.vel_setpoint_ = vel;
}
}
// In sensorless mode the motor is already armed.
if (!motor_.is_armed_) {
wait_for_control_iteration();
motor_.arm(&motor_.current_control_);
}
return true;
}
bool Axis::stop_closed_loop_control() {
motor_.disarm();
return check_for_errors();
}
bool Axis::run_closed_loop_control_loop() {
start_closed_loop_control();
set_step_dir_active(config_.enable_step_dir);
while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_) {
osDelay(1);
}
set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on);
stop_closed_loop_control();
return check_for_errors();
}
// Slowly drive in the negative direction at homing_speed until the min endstop is pressed
// When pressed, set the linear count to the offset (default 0), and then go to position 0
bool Axis::run_homing() {
// TODO: theoretically this check should be inside the update loop,
// otherwise someone could disable the endstop while homing is in progress.
if (!min_endstop_.config_.enabled) {
return error_ |= ERROR_HOMING_WITHOUT_ENDSTOP, false;
}
controller_.config_.control_mode = Controller::CONTROL_MODE_VELOCITY_CONTROL;
controller_.config_.input_mode = Controller::INPUT_MODE_VEL_RAMP;
controller_.input_pos_ = 0.0f;
controller_.input_pos_updated();
controller_.input_vel_ = -controller_.config_.homing_speed;
controller_.input_torque_ = 0.0f;
homing_.is_homed = false;
error_ &= ~ERROR_MIN_ENDSTOP_PRESSED;
bool done = false;
start_closed_loop_control();
// Driving toward the endstop
while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_ && !(done = min_endstop_.get_state())) {
osDelay(1);
}
stop_closed_loop_control();
controller_.input_vel_ = 0.0f;
if (!done) {
return false;
}
error_ &= ~ERROR_MIN_ENDSTOP_PRESSED; // clear this error since we deliberately drove into the endstop
std::optional<float> pos_estimate_local = encoder_.pos_estimate_.any();
if (pos_estimate_local == std::nullopt || !pos_estimate_local.has_value()){
return error_ |= ERROR_UNKNOWN_POSITION, false;
}
controller_.config_.control_mode = Controller::CONTROL_MODE_POSITION_CONTROL;
controller_.config_.input_mode = Controller::INPUT_MODE_TRAP_TRAJ;
// Initialize closed loop control, and then set the desired location.
start_closed_loop_control();
controller_.input_pos_ = pos_estimate_local.value() + min_endstop_.config_.offset;
controller_.pos_setpoint_ = pos_estimate_local.value();
controller_.vel_setpoint_ = 0.0f;
controller_.input_pos_updated();
// Synchronization issue. Ensure trajectory_done is false prior to the while loop, so that
// the controller has time to run move_to_pos() on the next update()
controller_.trajectory_done_ = false;
while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_ && !(done = controller_.trajectory_done_)) {
osDelay(1);
}
stop_closed_loop_control();
if (!done) {
return false;
}
// Set the current position to 0, the target to zero, and make sure we're path planning from 0 to 0
encoder_.set_linear_count(0);
const auto load_encoder_axis = controller_.config_.load_encoder_axis;
if(load_encoder_axis != axis_num_ && load_encoder_axis < AXIS_COUNT) {
axes[load_encoder_axis].encoder_.set_linear_count(0);
}
controller_.input_pos_ = 0.0f;
controller_.pos_setpoint_ = 0.0f;
controller_.vel_setpoint_ = 0.0f;
controller_.input_pos_updated();
// Force encoder estimate to update
osDelay(1);
homing_.is_homed = true;
return check_for_errors();
}
bool Axis::run_idle_loop() {
last_drv_fault_ = motor_.gate_driver_.get_error();
mechanical_brake_.engage();
set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on);
while (requested_state_ == AXIS_STATE_UNDEFINED) {
motor_.setup();
osDelay(1);
}
return check_for_errors();
}
// Infinite loop that does calibration and enters main control loop as appropriate
void Axis::run_state_machine_loop() {
for (;;) {
// Load the task chain if a specific request is pending
if (requested_state_ != AXIS_STATE_UNDEFINED) {
size_t pos = 0;
if (requested_state_ == AXIS_STATE_STARTUP_SEQUENCE) {
if (config_.startup_motor_calibration)
task_chain_[pos++] = AXIS_STATE_MOTOR_CALIBRATION;
if (config_.startup_encoder_index_search && encoder_.config_.use_index)
task_chain_[pos++] = AXIS_STATE_ENCODER_INDEX_SEARCH;
if (config_.startup_encoder_offset_calibration)
task_chain_[pos++] = AXIS_STATE_ENCODER_OFFSET_CALIBRATION;
if (config_.startup_homing)
task_chain_[pos++] = AXIS_STATE_HOMING;
if (config_.startup_closed_loop_control)
task_chain_[pos++] = AXIS_STATE_CLOSED_LOOP_CONTROL;
task_chain_[pos++] = AXIS_STATE_IDLE;
} else if (requested_state_ == AXIS_STATE_FULL_CALIBRATION_SEQUENCE) {
task_chain_[pos++] = AXIS_STATE_MOTOR_CALIBRATION;
if (encoder_.config_.mode == ODriveIntf::EncoderIntf::MODE_HALL)
task_chain_[pos++] = AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION;
if (encoder_.config_.use_index)
task_chain_[pos++] = AXIS_STATE_ENCODER_INDEX_SEARCH;
task_chain_[pos++] = AXIS_STATE_ENCODER_OFFSET_CALIBRATION;
task_chain_[pos++] = AXIS_STATE_IDLE;
} else if (requested_state_ != AXIS_STATE_UNDEFINED) {
task_chain_[pos++] = requested_state_;
task_chain_[pos++] = AXIS_STATE_IDLE;
}
task_chain_[pos++] = AXIS_STATE_UNDEFINED; // TODO: bounds checking
requested_state_ = AXIS_STATE_UNDEFINED;
// Auto-clear any invalid state error
error_ &= ~ERROR_INVALID_STATE;
}
// Note that current_state is a reference to task_chain_[0]
// Run the specified state
// Handlers should exit if requested_state != AXIS_STATE_UNDEFINED
bool status;
switch (current_state_) {
case AXIS_STATE_MOTOR_CALIBRATION: {
// These error checks are a hacky way to force legacy behavior
// when an error is raised. TODO: remove this when we overhaul
// the error architecture
// (https://github.com/madcowswe/ODrive/issues/526).
//if (odrv.any_error())
// goto invalid_state_label;
status = motor_.run_calibration();
} break;
case AXIS_STATE_ENCODER_INDEX_SEARCH: {
//if (odrv.any_error())
// goto invalid_state_label;
if (!motor_.is_calibrated_)
goto invalid_state_label;
status = encoder_.run_index_search();
} break;
case AXIS_STATE_ENCODER_DIR_FIND: {
//if (odrv.any_error())
// goto invalid_state_label;
if (!motor_.is_calibrated_)
goto invalid_state_label;
status = encoder_.run_direction_find();
// Help facilitate encoder.is_ready without reboot
if (status)
encoder_.apply_config(motor_.config_.motor_type);
} break;
case AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION: {
if (!motor_.is_calibrated_)
goto invalid_state_label;
status = encoder_.run_hall_polarity_calibration();
} break;
case AXIS_STATE_ENCODER_HALL_PHASE_CALIBRATION: {
if (!motor_.is_calibrated_)
goto invalid_state_label;
if (!encoder_.config_.hall_polarity_calibrated) {
encoder_.set_error(ODriveIntf::EncoderIntf::ERROR_HALL_NOT_CALIBRATED_YET);
goto invalid_state_label;
}
status = encoder_.run_hall_phase_calibration();
} break;
case AXIS_STATE_HOMING: {
Controller::ControlMode stored_control_mode = controller_.config_.control_mode;
Controller::InputMode stored_input_mode = controller_.config_.input_mode;
status = run_homing();
controller_.config_.control_mode = stored_control_mode;
controller_.config_.input_mode = stored_input_mode;
} break;
case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: {
//if (odrv.any_error())
// goto invalid_state_label;
if (!motor_.is_calibrated_)
goto invalid_state_label;
status = encoder_.run_offset_calibration();
} break;
case AXIS_STATE_LOCKIN_SPIN: {
//if (odrv.any_error())
// goto invalid_state_label;
if (!motor_.is_calibrated_ || encoder_.config_.direction==0)
goto invalid_state_label;
status = run_lockin_spin(config_.general_lockin, false);
} break;
case AXIS_STATE_CLOSED_LOOP_CONTROL: {
//if (odrv.any_error())
// goto invalid_state_label;
if (!motor_.is_calibrated_ || (encoder_.config_.direction==0 && !config_.enable_sensorless_mode))
goto invalid_state_label;
watchdog_feed();
status = run_closed_loop_control_loop();
} break;
case AXIS_STATE_IDLE: {
run_idle_loop();
status = true;
} break;
default:
invalid_state_label:
error_ |= ERROR_INVALID_STATE;
status = false; // this will set the state to idle
break;
}
// If the state failed, go to idle, else advance task chain
if (!status) {
std::fill(task_chain_.begin(), task_chain_.end(), AXIS_STATE_UNDEFINED);
current_state_ = AXIS_STATE_IDLE;
} else {
std::rotate(task_chain_.begin(), task_chain_.begin() + 1, task_chain_.end());
task_chain_.back() = AXIS_STATE_UNDEFINED;
}
}
}
@@ -0,0 +1,214 @@
#ifndef __AXIS_HPP
#define __AXIS_HPP
class Axis;
#include "encoder.hpp"
#include "acim_estimator.hpp"
#include "sensorless_estimator.hpp"
#include "controller.hpp"
#include "open_loop_controller.hpp"
#include "trapTraj.hpp"
#include "endstop.hpp"
#include "mechanical_brake.hpp"
#include "low_level.h"
#include "utils.hpp"
#include "task_timer.hpp"
#include <array>
class Axis : public ODriveIntf::AxisIntf {
public:
struct LockinConfig_t {
float current = 10.0f; // [A]
float ramp_time = 0.4f; // [s]
float ramp_distance = 1 * M_PI; // [rad]
float accel = 20.0f; // [rad/s^2]
float vel = 40.0f; // [rad/s]
float finish_distance = 100.0f; // [rad]
bool finish_on_vel = false;
bool finish_on_distance = false;
bool finish_on_enc_idx = false;
};
struct TaskTimes {
TaskTimer thermistor_update;
TaskTimer encoder_update;
TaskTimer sensorless_estimator_update;
TaskTimer endstop_update;
TaskTimer can_heartbeat;
TaskTimer controller_update;
TaskTimer open_loop_controller_update;
TaskTimer acim_estimator_update;
TaskTimer motor_update;
TaskTimer current_controller_update;
TaskTimer dc_calib;
TaskTimer current_sense;
TaskTimer pwm_update;
};
static LockinConfig_t default_calibration();
static LockinConfig_t default_sensorless();
static LockinConfig_t default_lockin();
struct CANConfig_t {
uint32_t node_id = 0;
bool is_extended = false;
uint32_t heartbeat_rate_ms = 100;
uint32_t encoder_rate_ms = 10;
uint32_t motor_error_rate_ms = 0;
uint32_t encoder_error_rate_ms = 0;
uint32_t controller_error_rate_ms = 0;
uint32_t sensorless_error_rate_ms = 0;
uint32_t encoder_count_rate_ms = 0;
uint32_t iq_rate_ms = 0;
uint32_t sensorless_rate_ms = 0;
uint32_t bus_vi_rate_ms = 0;
};
struct Config_t {
bool startup_motor_calibration = false; //<! run motor calibration at startup, skip otherwise
bool startup_encoder_index_search = false; //<! run encoder index search after startup, skip otherwise
// this only has an effect if encoder.config.use_index is also true
bool startup_encoder_offset_calibration = false; //<! run encoder offset calibration after startup, skip otherwise
bool startup_closed_loop_control = false; //<! enable closed loop control after calibration/startup
bool startup_homing = false; //<! enable homing after calibration/startup
bool enable_step_dir = false; //<! enable step/dir input after calibration
// For M0 this has no effect if enable_uart is true
bool step_dir_always_on = false; //<! Keep step/dir enabled while the motor is disabled.
//<! This is ignored if enable_step_dir is false.
//<! This setting only takes effect on a state transition
//<! into idle or out of closed loop control.
bool enable_sensorless_mode = false;
float watchdog_timeout = 0.0f; // [s]
bool enable_watchdog = false;
// Defaults loaded from hw_config in load_configuration in main.cpp
uint16_t step_gpio_pin = 0;
uint16_t dir_gpio_pin = 0;
LockinConfig_t calibration_lockin = default_calibration();
LockinConfig_t sensorless_ramp = default_sensorless();
LockinConfig_t general_lockin;
CANConfig_t can;
// custom setters
Axis* parent = nullptr;
void set_step_gpio_pin(uint16_t value) { step_gpio_pin = value; parent->decode_step_dir_pins(); }
void set_dir_gpio_pin(uint16_t value) { dir_gpio_pin = value; parent->decode_step_dir_pins(); }
};
struct Homing_t {
bool is_homed = false;
};
struct CAN_t {
uint32_t last_heartbeat = 0;
uint32_t last_encoder = 0;
uint32_t last_motor_error = 0;
uint32_t last_encoder_error = 0;
uint32_t last_controller_error = 0;
uint32_t last_sensorless_error = 0;
uint32_t last_encoder_count = 0;
uint32_t last_iq = 0;
uint32_t last_sensorless = 0;
uint32_t last_bus_vi = 0;
};
Axis(int axis_num,
uint16_t default_step_gpio_pin,
uint16_t default_dir_gpio_pin,
osPriority thread_priority,
Encoder& encoder,
SensorlessEstimator& sensorless_estimator,
Controller& controller,
Motor& motor,
TrapezoidalTrajectory& trap,
Endstop& min_endstop,
Endstop& max_endstop,
MechanicalBrake& mechanical_brake);
bool apply_config();
void clear_config();
void start_thread();
bool wait_for_control_iteration();
void step_cb();
void set_step_dir_active(bool enable);
void decode_step_dir_pins();
bool do_checks(uint32_t timestamp);
void watchdog_feed();
bool watchdog_check();
// True if there are no errors
bool inline check_for_errors() {
return error_ == ERROR_NONE;
}
bool start_closed_loop_control();
bool stop_closed_loop_control();
bool run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed,
std::function<bool(bool)> loop_cb = {} );
bool run_closed_loop_control_loop();
bool run_homing();
bool run_idle_loop();
constexpr uint32_t get_watchdog_reset() {
return static_cast<uint32_t>(std::clamp<float>(config_.watchdog_timeout, 0, UINT32_MAX / (current_meas_hz + 1)) * current_meas_hz);
}
void run_state_machine_loop();
// hardware config
int axis_num_;
uint16_t default_step_gpio_pin_;
uint16_t default_dir_gpio_pin_;
osPriority thread_priority_;
Config_t config_;
Encoder& encoder_;
AcimEstimator acim_estimator_;
SensorlessEstimator& sensorless_estimator_;
Controller& controller_;
OpenLoopController open_loop_controller_;
Motor& motor_;
TrapezoidalTrajectory& trap_traj_;
Endstop& min_endstop_;
Endstop& max_endstop_;
MechanicalBrake& mechanical_brake_;
TaskTimes task_times_;
osThreadId thread_id_ = 0;
const uint32_t stack_size_ = 2048; // Bytes
volatile bool thread_id_valid_ = false;
// variables exposed on protocol
Error error_ = ERROR_NONE;
bool step_dir_active_ = false; // auto enabled after calibration, based on config.enable_step_dir
int64_t steps_ = 0; // Steps counted at interface
uint32_t last_drv_fault_ = 0;
// updated from config in constructor, and on protocol hook
Stm32Gpio step_gpio_;
Stm32Gpio dir_gpio_;
AxisState requested_state_ = AXIS_STATE_STARTUP_SEQUENCE;
std::array<AxisState, 10> task_chain_ = { AXIS_STATE_UNDEFINED };
AxisState& current_state_ = task_chain_.front();
Homing_t homing_;
CAN_t can_;
// watchdog
uint32_t watchdog_current_value_= 0;
};
#endif /* __AXIS_HPP */
@@ -0,0 +1,182 @@
#ifndef __COMPONENT_HPP
#define __COMPONENT_HPP
#include <stdint.h>
#include <optional>
#include <variant>
class ComponentBase {
public:
/**
* @brief Shall run the update action of this component.
*
* This function gets called in a low priority interrupt context and is
* allowed to call CMSIS functions.
*
* @param timestamp: The timestamp (in HCLK ticks) for which this update
* is run.
*/
virtual void update(uint32_t timestamp) = 0;
};
template<typename T>
class InputPort;
/**
* @brief An output port stores a value for consumption by a connecting input
* port.
*
* Output ports are supposed to be reset at the beginning of a control loop
* iteration. This ensures that connecting input ports don't use an outdated
* value and, more importantly, ensures proper handling if the producer of the
* value is incapable of producing the value for any reason.
*
* Member functions of this class are not thread-safe unless noted otherwise.
*/
template<typename T>
class OutputPort {
public:
/**
* @brief Initializes the output port with the specified value.
*
* An initialization value is required for any() to work properly.
* present() and previous() cannot be used to fetch the
* initialization value.
*/
OutputPort(T val) : content_(val) {}
/**
* @brief Updates the underlying value of this output port.
*/
void operator=(T value) {
content_ = value;
age_ = 0;
}
/**
* @brief Marks the contained value as outdated. The value is not actually
* deleted and can still be accessed through some of the member functions
* of this class.
*/
void reset() {
// This will eventually overflow to 0 so present() could
// theoretically return a very old value however it is very likely that
// the motor will be long disarmed by then.
age_++;
}
/**
* @brief Returns the value from this control loop iteration or std::nullopt
* if the value was not yet set during this control loop iteration.
*/
std::optional<T> present() {
if (age_ == 0) {
return content_;
} else {
return std::nullopt;
}
}
/**
* @brief Returns the value from exactly the previous control loop iteration.
*
* If during the last iteration no value was set or the value was already
* overwritten during this control loop iteration then this function returns
* std::nullopt.
*/
std::optional<T> previous() {
if (age_ == 1) {
return content_;
} else {
return std::nullopt;
}
}
/**
* @brief Returns the value contained in this output port with disregard of
* when the value was set.
*
* This function is thread-safe if load/store operations of T are atomic.
*/
std::optional<T> any() {
return content_;
}
private:
uint32_t age_ = 2; // Age in number of control loop iterations
T content_;
};
/**
* @brief An input port provides a value from the source to which it's configured.
*
* The source can be one of:
* - an internally stored value
* - an externally stored value (referenced by a pointer)
* - an external OutputPort (referenced by a pointer)
* - none (all queries will return std::nullopt)
*
* Member functions of this class are not thread-safe unless otherwise noted.
*/
template<typename T>
class InputPort {
public:
void connect_to(OutputPort<T>* input_port) {
content_ = input_port;
}
void connect_to(T* input_ptr) {
content_ = input_ptr;
}
void disconnect() {
content_ = (OutputPort<T>*)nullptr;
}
std::optional<T> present() {
if (content_.index() == 2) {
OutputPort<T>* ptr = std::get<2>(content_);
return ptr ? ptr->present() : std::nullopt;
} else if (content_.index() == 1) {
T* ptr = std::get<1>(content_);
return ptr ? std::make_optional(*ptr) : std::nullopt;
} else {
return std::get<0>(content_);
}
}
// TODO: probably it makes sense to let the application define that it's
// ok for this input port to fetch the value from the last iteration.
// This would provide a general way to resolve same-iteration data path cycles.
//std::optional<T> previous() {
// if (content_.index() == 2) {
// OutputPort<T>* ptr = std::get<2>(content_);
// return ptr ? ptr->previous() : std::nullopt;
// } else if (content_.index() == 1) {
// T* ptr = std::get<1>(content_);
// return ptr ? std::make_optional(*ptr) : std::nullopt;
// } else {
// return std::get<0>(content_);
// }
//}
std::optional<T> any() {
if (content_.index() == 2) {
OutputPort<T>* ptr = std::get<2>(content_);
return ptr ? ptr->any() : std::nullopt;
} else if (content_.index() == 1) {
T* ptr = std::get<1>(content_);
return ptr ? std::make_optional(*ptr) : std::nullopt;
} else {
return std::get<0>(content_);
}
}
private:
std::variant<T, T*, OutputPort<T>*> content_;
};
#endif // __COMPONENT_HPP
@@ -0,0 +1,451 @@
#include "odrive_main.h"
#include <algorithm>
#include <numeric>
bool Controller::apply_config() {
config_.parent = this;
update_filter_gains();
return true;
}
void Controller::reset() {
// pos_setpoint is initialized in start_closed_loop_control
vel_setpoint_ = 0.0f;
vel_integrator_torque_ = 0.0f;
torque_setpoint_ = 0.0f;
mechanical_power_ = 0.0f;
electrical_power_ = 0.0f;
}
void Controller::set_error(Error error) {
error_ |= error;
last_error_time_ = odrv.n_evt_control_loop_ * current_meas_period;
}
//--------------------------------
// Command Handling
//--------------------------------
void Controller::move_to_pos(float goal_point) {
axis_->trap_traj_.planTrapezoidal(goal_point, pos_setpoint_, vel_setpoint_,
axis_->trap_traj_.config_.vel_limit,
axis_->trap_traj_.config_.accel_limit,
axis_->trap_traj_.config_.decel_limit);
axis_->trap_traj_.t_ = 0.0f;
trajectory_done_ = false;
}
void Controller::move_incremental(float displacement, bool from_input_pos = true){
if(from_input_pos){
input_pos_ += displacement;
} else{
input_pos_ = pos_setpoint_ + displacement;
}
input_pos_updated();
}
void Controller::start_anticogging_calibration() {
// Ensure the cogging map was correctly allocated earlier and that the motor is capable of calibrating
if (axis_->error_ == Axis::ERROR_NONE) {
config_.anticogging.calib_anticogging = true;
}
}
float Controller::remove_anticogging_bias()
{
auto& cogmap = config_.anticogging.cogging_map;
auto sum = std::accumulate(std::begin(cogmap), std::end(cogmap), 0.0f);
auto average = sum / std::size(cogmap);
for(auto& val : cogmap) {
val -= average;
}
return average;
}
/*
* This anti-cogging implementation iterates through each encoder position,
* waits for zero velocity & position error,
* then samples the current required to maintain that position.
*
* This holding current is added as a feedforward term in the control loop.
*/
bool Controller::anticogging_calibration(float pos_estimate, float vel_estimate) {
float pos_err = input_pos_ - pos_estimate;
if (std::abs(pos_err) <= config_.anticogging.calib_pos_threshold / (float)axis_->encoder_.config_.cpr &&
std::abs(vel_estimate) < config_.anticogging.calib_vel_threshold / (float)axis_->encoder_.config_.cpr) {
config_.anticogging.cogging_map[std::clamp<uint32_t>(config_.anticogging.index++, 0, 3600)] = vel_integrator_torque_;
}
if (config_.anticogging.index < 3600) {
config_.control_mode = CONTROL_MODE_POSITION_CONTROL;
input_pos_ = config_.anticogging.index * axis_->encoder_.getCoggingRatio();
input_vel_ = 0.0f;
input_torque_ = 0.0f;
input_pos_updated();
return false;
} else {
config_.anticogging.index = 0;
config_.control_mode = CONTROL_MODE_POSITION_CONTROL;
input_pos_ = 0.0f; // Send the motor home
input_vel_ = 0.0f;
input_torque_ = 0.0f;
input_pos_updated();
anticogging_valid_ = true;
config_.anticogging.calib_anticogging = false;
return true;
}
}
void Controller::set_input_pos_and_steps(float const pos) {
input_pos_ = pos;
if (config_.circular_setpoints) {
float const range = config_.circular_setpoint_range;
axis_->steps_ = (int64_t)(fmodf_pos(pos, range) / range * config_.steps_per_circular_range);
} else {
axis_->steps_ = (int64_t)(pos * config_.steps_per_circular_range);
}
}
bool Controller::control_mode_updated() {
if (config_.control_mode >= CONTROL_MODE_POSITION_CONTROL) {
std::optional<float> estimate = (config_.circular_setpoints ?
pos_estimate_circular_src_ :
pos_estimate_linear_src_).any();
if (!estimate.has_value()) {
return false;
}
pos_setpoint_ = *estimate;
set_input_pos_and_steps(*estimate);
}
return true;
}
void Controller::update_filter_gains() {
float bandwidth = std::min(config_.input_filter_bandwidth, 0.25f * current_meas_hz);
input_filter_ki_ = 2.0f * bandwidth; // basic conversion to discrete time
input_filter_kp_ = 0.25f * (input_filter_ki_ * input_filter_ki_); // Critically damped
}
static float limitVel(const float vel_limit, const float vel_estimate, const float vel_gain, const float torque) {
float Tmax = (vel_limit - vel_estimate) * vel_gain;
float Tmin = (-vel_limit - vel_estimate) * vel_gain;
return std::clamp(torque, Tmin, Tmax);
}
bool Controller::update() {
std::optional<float> pos_estimate_linear = pos_estimate_linear_src_.present();
std::optional<float> pos_estimate_circular = pos_estimate_circular_src_.present();
std::optional<float> pos_wrap = pos_wrap_src_.present();
std::optional<float> vel_estimate = vel_estimate_src_.present();
std::optional<float> anticogging_pos_estimate = axis_->encoder_.pos_estimate_.present();
std::optional<float> anticogging_vel_estimate = axis_->encoder_.vel_estimate_.present();
if (axis_->step_dir_active_) {
if (config_.circular_setpoints) {
if (!pos_wrap.has_value()) {
set_error(ERROR_INVALID_CIRCULAR_RANGE);
return false;
}
input_pos_ = (float)(axis_->steps_ % config_.steps_per_circular_range) * (*pos_wrap / (float)(config_.steps_per_circular_range));
} else {
input_pos_ = (float)(axis_->steps_) / (float)(config_.steps_per_circular_range);
}
}
if (config_.anticogging.calib_anticogging) {
if (!anticogging_pos_estimate.has_value() || !anticogging_vel_estimate.has_value()) {
set_error(ERROR_INVALID_ESTIMATE);
return false;
}
// non-blocking
anticogging_calibration(*anticogging_pos_estimate, *anticogging_vel_estimate);
}
// TODO also enable circular deltas for 2nd order filter, etc.
if (config_.circular_setpoints) {
if (!pos_wrap.has_value()) {
set_error(ERROR_INVALID_CIRCULAR_RANGE);
return false;
}
input_pos_ = fmodf_pos(input_pos_, *pos_wrap);
}
// Update inputs
switch (config_.input_mode) {
case INPUT_MODE_INACTIVE: {
// do nothing
} break;
case INPUT_MODE_PASSTHROUGH: {
pos_setpoint_ = input_pos_;
vel_setpoint_ = input_vel_;
torque_setpoint_ = input_torque_;
} break;
case INPUT_MODE_VEL_RAMP: {
float max_step_size = std::abs(current_meas_period * config_.vel_ramp_rate);
float full_step = input_vel_ - vel_setpoint_;
float step = std::clamp(full_step, -max_step_size, max_step_size);
vel_setpoint_ += step;
torque_setpoint_ = (step / current_meas_period) * config_.inertia;
} break;
case INPUT_MODE_TORQUE_RAMP: {
float max_step_size = std::abs(current_meas_period * config_.torque_ramp_rate);
float full_step = input_torque_ - torque_setpoint_;
float step = std::clamp(full_step, -max_step_size, max_step_size);
torque_setpoint_ += step;
} break;
case INPUT_MODE_POS_FILTER: {
// 2nd order pos tracking filter
float delta_pos = input_pos_ - pos_setpoint_; // Pos error
if (config_.circular_setpoints) {
if (!pos_wrap.has_value()) {
set_error(ERROR_INVALID_CIRCULAR_RANGE);
return false;
}
delta_pos = wrap_pm(delta_pos, *pos_wrap);
}
float delta_vel = input_vel_ - vel_setpoint_; // Vel error
float accel = input_filter_kp_*delta_pos + input_filter_ki_*delta_vel; // Feedback
torque_setpoint_ = accel * config_.inertia; // Accel
vel_setpoint_ += current_meas_period * accel; // delta vel
pos_setpoint_ += current_meas_period * vel_setpoint_; // Delta pos
} break;
case INPUT_MODE_MIRROR: {
if (config_.axis_to_mirror < AXIS_COUNT) {
std::optional<float> other_pos = axes[config_.axis_to_mirror].encoder_.pos_estimate_.present();
std::optional<float> other_vel = axes[config_.axis_to_mirror].encoder_.vel_estimate_.present();
std::optional<float> other_torque = axes[config_.axis_to_mirror].controller_.torque_output_.present();
if (!other_pos.has_value() || !other_vel.has_value() || !other_torque.has_value()) {
set_error(ERROR_INVALID_ESTIMATE);
return false;
}
pos_setpoint_ = *other_pos * config_.mirror_ratio;
vel_setpoint_ = *other_vel * config_.mirror_ratio;
torque_setpoint_ = *other_torque * config_.torque_mirror_ratio;
} else {
set_error(ERROR_INVALID_MIRROR_AXIS);
return false;
}
} break;
// case INPUT_MODE_MIX_CHANNELS: {
// // NOT YET IMPLEMENTED
// } break;
case INPUT_MODE_TRAP_TRAJ: {
if(input_pos_updated_){
move_to_pos(input_pos_);
input_pos_updated_ = false;
}
// Avoid updating uninitialized trajectory
if (trajectory_done_)
break;
if (axis_->trap_traj_.t_ > axis_->trap_traj_.Tf_) {
// Drop into position control mode when done to avoid problems on loop counter delta overflow
config_.control_mode = CONTROL_MODE_POSITION_CONTROL;
pos_setpoint_ = axis_->trap_traj_.Xf_;
vel_setpoint_ = 0.0f;
torque_setpoint_ = 0.0f;
trajectory_done_ = true;
} else {
TrapezoidalTrajectory::Step_t traj_step = axis_->trap_traj_.eval(axis_->trap_traj_.t_);
pos_setpoint_ = traj_step.Y;
vel_setpoint_ = traj_step.Yd;
torque_setpoint_ = traj_step.Ydd * config_.inertia;
axis_->trap_traj_.t_ += current_meas_period;
}
anticogging_pos_estimate = pos_setpoint_; // FF the position setpoint instead of the pos_estimate
} break;
case INPUT_MODE_TUNING: {
autotuning_phase_ = wrap_pm_pi(autotuning_phase_ + (2.0f * M_PI * autotuning_.frequency * current_meas_period));
float c = our_arm_cos_f32(autotuning_phase_);
float s = our_arm_sin_f32(autotuning_phase_);
pos_setpoint_ = input_pos_ + autotuning_.pos_amplitude * s; // + pos_amp_c * c
vel_setpoint_ = input_vel_ + autotuning_.vel_amplitude * c;
torque_setpoint_ = input_torque_ + autotuning_.torque_amplitude * -s;
} break;
default: {
set_error(ERROR_INVALID_INPUT_MODE);
return false;
}
}
// Never command a setpoint beyond its limit
if(config_.enable_vel_limit) {
vel_setpoint_ = std::clamp(vel_setpoint_, -config_.vel_limit, config_.vel_limit);
}
const float Tlim = axis_->motor_.max_available_torque();
torque_setpoint_ = std::clamp(torque_setpoint_, -Tlim, Tlim);
// Position control
// TODO Decide if we want to use encoder or pll position here
float gain_scheduling_multiplier = 1.0f;
float vel_des = vel_setpoint_;
if (config_.control_mode >= CONTROL_MODE_POSITION_CONTROL) {
float pos_err;
if (config_.circular_setpoints) {
if (!pos_estimate_circular.has_value() || !pos_wrap.has_value()) {
set_error(ERROR_INVALID_ESTIMATE);
return false;
}
// Keep pos setpoint from drifting
pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap);
// Circular delta
pos_err = pos_setpoint_ - *pos_estimate_circular;
pos_err = wrap_pm(pos_err, *pos_wrap);
} else {
if (!pos_estimate_linear.has_value()) {
set_error(ERROR_INVALID_ESTIMATE);
return false;
}
pos_err = pos_setpoint_ - *pos_estimate_linear;
}
vel_des += config_.pos_gain * pos_err;
// V-shaped gain shedule based on position error
float abs_pos_err = std::abs(pos_err);
if (config_.enable_gain_scheduling && abs_pos_err <= config_.gain_scheduling_width) {
gain_scheduling_multiplier = abs_pos_err / config_.gain_scheduling_width;
}
}
// Velocity limiting
float vel_lim = config_.vel_limit;
if (config_.enable_vel_limit) {
vel_des = std::clamp(vel_des, -vel_lim, vel_lim);
}
// Check for overspeed fault (done in this module (controller) for cohesion with vel_lim)
if (config_.enable_overspeed_error) { // 0.0f to disable
if (!vel_estimate.has_value()) {
set_error(ERROR_INVALID_ESTIMATE);
return false;
}
if (std::abs(*vel_estimate) > config_.vel_limit_tolerance * vel_lim) {
set_error(ERROR_OVERSPEED);
return false;
}
}
// TODO: Change to controller working in torque units
// Torque per amp gain scheduling (ACIM)
float vel_gain = config_.vel_gain;
float vel_integrator_gain = config_.vel_integrator_gain;
if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) {
float effective_flux = axis_->acim_estimator_.rotor_flux_;
float minflux = axis_->motor_.config_.acim_gain_min_flux;
if (std::abs(effective_flux) < minflux)
effective_flux = std::copysignf(minflux, effective_flux);
vel_gain /= effective_flux;
vel_integrator_gain /= effective_flux;
// TODO: also scale the integral value which is also changing units.
// (or again just do control in torque units)
}
// Velocity control
float torque = torque_setpoint_;
// Anti-cogging is enabled after calibration
// We get the current position and apply a current feed-forward
// ensuring that we handle negative encoder positions properly (-1 == motor->encoder.encoder_cpr - 1)
if (anticogging_valid_ && config_.anticogging.anticogging_enabled) {
if (!anticogging_pos_estimate.has_value()) {
set_error(ERROR_INVALID_ESTIMATE);
return false;
}
float anticogging_pos = *anticogging_pos_estimate / axis_->encoder_.getCoggingRatio();
torque += config_.anticogging.cogging_map[std::clamp(mod((int)anticogging_pos, 3600), 0, 3600)];
}
float v_err = 0.0f;
if (config_.control_mode >= CONTROL_MODE_VELOCITY_CONTROL) {
if (!vel_estimate.has_value()) {
set_error(ERROR_INVALID_ESTIMATE);
return false;
}
v_err = vel_des - *vel_estimate;
torque += (vel_gain * gain_scheduling_multiplier) * v_err;
// Velocity integral action before limiting
torque += vel_integrator_torque_;
}
// Velocity limiting in current mode
if (config_.control_mode < CONTROL_MODE_VELOCITY_CONTROL && config_.enable_torque_mode_vel_limit) {
if (!vel_estimate.has_value()) {
set_error(ERROR_INVALID_ESTIMATE);
return false;
}
torque = limitVel(config_.vel_limit, *vel_estimate, vel_gain, torque);
}
// Torque limiting
bool limited = false;
if (torque > Tlim) {
limited = true;
torque = Tlim;
}
if (torque < -Tlim) {
limited = true;
torque = -Tlim;
}
// Velocity integrator (behaviour dependent on limiting)
if (config_.control_mode < CONTROL_MODE_VELOCITY_CONTROL) {
// reset integral if not in use
vel_integrator_torque_ = 0.0f;
} else {
if (limited) {
// TODO make decayfactor configurable
vel_integrator_torque_ *= 0.99f;
} else {
vel_integrator_torque_ += ((vel_integrator_gain * gain_scheduling_multiplier) * current_meas_period) * v_err;
}
// integrator limiting to prevent windup
vel_integrator_torque_ = std::clamp(vel_integrator_torque_, -config_.vel_integrator_limit, config_.vel_integrator_limit);
}
float ideal_electrical_power = 0.0f;
if (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL) {
ideal_electrical_power = axis_->motor_.current_control_.power_ - \
SQ(axis_->motor_.current_control_.Iq_measured_) * 1.5f * axis_->motor_.config_.phase_resistance - \
SQ(axis_->motor_.current_control_.Id_measured_) * 1.5f * axis_->motor_.config_.phase_resistance;
}
else {
ideal_electrical_power = axis_->motor_.current_control_.power_;
}
mechanical_power_ += config_.mechanical_power_bandwidth * current_meas_period * (torque * *vel_estimate * M_PI * 2.0f - mechanical_power_);
electrical_power_ += config_.electrical_power_bandwidth * current_meas_period * (ideal_electrical_power - electrical_power_);
// Spinout check
// If mechanical power is negative (braking) and measured power is positive, something is wrong
// This indicates that the controller is trying to stop, but torque is being produced.
// Usually caused by an incorrect encoder offset
if (mechanical_power_ < config_.spinout_mechanical_power_threshold && electrical_power_ > config_.spinout_electrical_power_threshold) {
set_error(ERROR_SPINOUT_DETECTED);
return false;
}
torque_output_ = torque;
// TODO: this is inconsistent with the other errors which are sticky.
// However if we make ERROR_INVALID_ESTIMATE sticky then it will be
// confusing that a normal sequence of motor calibration + encoder
// calibration would leave the controller in an error state.
error_ &= ~ERROR_INVALID_ESTIMATE;
return true;
}
@@ -0,0 +1,136 @@
#ifndef __CONTROLLER_HPP
#define __CONTROLLER_HPP
class Controller : public ODriveIntf::ControllerIntf {
public:
struct Anticogging_t {
uint32_t index = 0;
float cogging_map[3600];
bool pre_calibrated = false;
bool calib_anticogging = false;
float calib_pos_threshold = 1.0f;
float calib_vel_threshold = 1.0f;
float cogging_ratio = 1.0f;
bool anticogging_enabled = true;
};
struct Autotuning_t {
float frequency = 0.0f;
float pos_amplitude = 0.0f;
float vel_amplitude = 0.0f;
float torque_amplitude = 0.0f;
};
struct Config_t {
ControlMode control_mode = CONTROL_MODE_POSITION_CONTROL; //see: ControlMode_t
InputMode input_mode = INPUT_MODE_PASSTHROUGH; //see: InputMode_t
float pos_gain = 20.0f; // [(turn/s) / turn]
float vel_gain = 1.0f / 6.0f; // [Nm/(turn/s)]
// float vel_gain = 0.2f / 200.0f, // [Nm/(rad/s)] <sensorless example>
float vel_integrator_gain = 2.0f / 6.0f; // [Nm/(turn/s * s)]
float vel_limit = 2.0f; // [turn/s] Infinity to disable.
float vel_limit_tolerance = 1.2f; // ratio to vel_lim. Infinity to disable.
float vel_integrator_limit = INFINITY; // Vel. integrator clamping value. Infinity to disable.
float vel_ramp_rate = 1.0f; // [(turn/s) / s]
float torque_ramp_rate = 0.01f; // Nm / sec
bool circular_setpoints = false;
float circular_setpoint_range = 1.0f; // Circular range when circular_setpoints is true. [turn]
uint32_t steps_per_circular_range = 1024;
float inertia = 0.0f; // [Nm/(turn/s^2)]
float input_filter_bandwidth = 2.0f; // [1/s]
float homing_speed = 0.25f; // [turn/s]
Anticogging_t anticogging;
float gain_scheduling_width = 10.0f;
bool enable_gain_scheduling = false;
bool enable_vel_limit = true;
bool enable_overspeed_error = true;
bool enable_torque_mode_vel_limit = true; // enable velocity limit in current control mode (requires a valid velocity estimator)
uint8_t axis_to_mirror = -1;
float mirror_ratio = 1.0f;
float torque_mirror_ratio = 0.0f;
uint8_t load_encoder_axis = -1; // default depends on Axis number and is set in load_configuration(). Set to -1 to select sensorless estimator.
float mechanical_power_bandwidth = 20.0f; // [rad/s] filter cutoff for mechanical power for spinout detction
float electrical_power_bandwidth = 20.0f; // [rad/s] filter cutoff for electrical power for spinout detection
float spinout_electrical_power_threshold = 10.0f; // [W] electrical power threshold for spinout detection
float spinout_mechanical_power_threshold = -10.0f; // [W] mechanical power threshold for spinout detection
// custom setters
Controller* parent;
void set_input_filter_bandwidth(float value) { input_filter_bandwidth = value; parent->update_filter_gains(); }
void set_steps_per_circular_range(uint32_t value) { steps_per_circular_range = value > 0 ? value : steps_per_circular_range; }
void set_control_mode(ControlMode value) { control_mode = value; parent->control_mode_updated(); }
};
bool apply_config();
void reset();
void set_error(Error error);
constexpr void input_pos_updated() {
input_pos_updated_ = true;
}
bool control_mode_updated();
void set_input_pos_and_steps(float pos);
bool select_encoder(size_t encoder_num);
// Trajectory-Planned control
void move_to_pos(float goal_point);
void move_incremental(float displacement, bool from_goal_point);
// TODO: make this more similar to other calibration loops
void start_anticogging_calibration();
float remove_anticogging_bias();
bool anticogging_calibration(float pos_estimate, float vel_estimate);
float get_anticogging_value(uint32_t index) {
return (index < 3600) ? config_.anticogging.cogging_map[index] : 0.0f;
}
void update_filter_gains();
bool update();
Config_t config_;
Axis* axis_ = nullptr; // set by Axis constructor
Error error_ = ERROR_NONE;
float last_error_time_ = 0.0f;
// Inputs
InputPort<float> pos_estimate_linear_src_;
InputPort<float> pos_estimate_circular_src_;
InputPort<float> vel_estimate_src_;
InputPort<float> pos_wrap_src_;
float pos_setpoint_ = 0.0f; // [turns]
float vel_setpoint_ = 0.0f; // [turn/s]
// float vel_setpoint = 800.0f; <sensorless example>
float vel_integrator_torque_ = 0.0f; // [Nm]
float torque_setpoint_ = 0.0f; // [Nm]
float input_pos_ = 0.0f; // [turns]
float input_vel_ = 0.0f; // [turn/s]
float input_torque_ = 0.0f; // [Nm]
float input_filter_kp_ = 0.0f;
float input_filter_ki_ = 0.0f;
Autotuning_t autotuning_;
float autotuning_phase_ = 0.0f;
bool input_pos_updated_ = false;
bool trajectory_done_ = true;
bool anticogging_valid_ = false;
float mechanical_power_ = 0.0f; // [W]
float electrical_power_ = 0.0f; // [W]
// Outputs
OutputPort<float> torque_output_ = 0.0f;
// custom setters
void set_input_pos(float value) { set_input_pos_and_steps(value); input_pos_updated(); }
};
#endif // __CONTROLLER_HPP
@@ -0,0 +1,10 @@
#ifndef __CURRENT_LIMITER_HPP
#define __CURRENT_LIMITER_HPP
class CurrentLimiter {
public:
virtual ~CurrentLimiter() = default;
virtual float get_current_limit(float base_current_lim) const = 0;
};
#endif // __CURRENT_LIMITER_HPP
@@ -0,0 +1,842 @@
#include "odrive_main.h"
#include <Drivers/STM32/stm32_system.h>
#include <bitset>
Encoder::Encoder(TIM_HandleTypeDef* timer, Stm32Gpio index_gpio,
Stm32Gpio hallA_gpio, Stm32Gpio hallB_gpio, Stm32Gpio hallC_gpio,
Stm32SpiArbiter* spi_arbiter) :
timer_(timer), index_gpio_(index_gpio),
hallA_gpio_(hallA_gpio), hallB_gpio_(hallB_gpio), hallC_gpio_(hallC_gpio),
spi_arbiter_(spi_arbiter)
{
}
static void enc_index_cb_wrapper(void* ctx) {
reinterpret_cast<Encoder*>(ctx)->enc_index_cb();
}
bool Encoder::apply_config(ODriveIntf::MotorIntf::MotorType motor_type) {
config_.parent = this;
update_pll_gains();
if (config_.pre_calibrated) {
if (config_.mode == Encoder::MODE_HALL && config_.hall_polarity_calibrated)
is_ready_ = true;
if (config_.mode == Encoder::MODE_SINCOS)
is_ready_ = true;
if (motor_type == Motor::MOTOR_TYPE_ACIM)
is_ready_ = true;
}
return true;
}
void Encoder::setup() {
HAL_TIM_Encoder_Start(timer_, TIM_CHANNEL_ALL);
set_idx_subscribe();
mode_ = config_.mode;
spi_task_.config = {
.Mode = SPI_MODE_MASTER,
.Direction = SPI_DIRECTION_2LINES,
.DataSize = SPI_DATASIZE_16BIT,
.CLKPolarity = (mode_ == MODE_SPI_ABS_AEAT || mode_ == MODE_SPI_ABS_MA732) ? SPI_POLARITY_HIGH : SPI_POLARITY_LOW,
.CLKPhase = SPI_PHASE_2EDGE,
.NSS = SPI_NSS_SOFT,
.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16,
.FirstBit = SPI_FIRSTBIT_MSB,
.TIMode = SPI_TIMODE_DISABLE,
.CRCCalculation = SPI_CRCCALCULATION_DISABLE,
.CRCPolynomial = 10,
};
if (mode_ == MODE_SPI_ABS_MA732) {
abs_spi_dma_tx_[0] = 0x0000;
}
if(mode_ & MODE_FLAG_ABS){
abs_spi_cs_pin_init();
if (axis_->controller_.config_.anticogging.pre_calibrated) {
axis_->controller_.anticogging_valid_ = true;
}
}
}
void Encoder::set_error(Error error) {
vel_estimate_valid_ = false;
pos_estimate_valid_ = false;
error_ |= error;
axis_->error_ |= Axis::ERROR_ENCODER_FAILED;
}
bool Encoder::do_checks(){
return error_ == ERROR_NONE;
}
//--------------------
// Hardware Dependent
//--------------------
// Triggered when an encoder passes over the "Index" pin
// TODO: only arm index edge interrupt when we know encoder has powered up
// (maybe by attaching the interrupt on start search, synergistic with following)
void Encoder::enc_index_cb() {
if (config_.use_index) {
set_circular_count(0, false);
if (config_.use_index_offset)
set_linear_count((int32_t)(config_.index_offset * config_.cpr));
if (config_.pre_calibrated) {
is_ready_ = true;
if(axis_->controller_.config_.anticogging.pre_calibrated){
axis_->controller_.anticogging_valid_ = true;
}
} else {
// We can't use the update_offset facility in set_circular_count because
// we also set the linear count before there is a chance to update. Therefore:
// Invalidate offset calibration that may have happened before idx search
is_ready_ = false;
}
index_found_ = true;
}
// Disable interrupt
index_gpio_.unsubscribe();
}
void Encoder::set_idx_subscribe(bool override_enable) {
if (config_.use_index && (override_enable || !config_.find_idx_on_lockin_only)) {
if (!index_gpio_.subscribe(true, false, enc_index_cb_wrapper, this)) {
odrv.misconfigured_ = true;
}
} else if (!config_.use_index || config_.find_idx_on_lockin_only) {
index_gpio_.unsubscribe();
}
}
void Encoder::update_pll_gains() {
pll_kp_ = 2.0f * config_.bandwidth; // basic conversion to discrete time
pll_ki_ = 0.25f * (pll_kp_ * pll_kp_); // Critically damped
// Check that we don't get problems with discrete time approximation
if (!(current_meas_period * pll_kp_ < 1.0f)) {
set_error(ERROR_UNSTABLE_GAIN);
}
}
void Encoder::check_pre_calibrated() {
// TODO: restoring config from python backup is fragile here (ACIM motor type must be set first)
if (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_ACIM) {
if (!is_ready_)
config_.pre_calibrated = false;
if (mode_ == MODE_INCREMENTAL && !index_found_)
config_.pre_calibrated = false;
}
}
// Function that sets the current encoder count to a desired 32-bit value.
void Encoder::set_linear_count(int32_t count) {
// Disable interrupts to make a critical section to avoid race condition
uint32_t prim = cpu_enter_critical();
// Update states
shadow_count_ = count;
pos_estimate_counts_ = (float)count;
tim_cnt_sample_ = count;
//Write hardware last
timer_->Instance->CNT = count;
cpu_exit_critical(prim);
}
// Function that sets the CPR circular tracking encoder count to a desired 32-bit value.
// Note that this will get mod'ed down to [0, cpr)
void Encoder::set_circular_count(int32_t count, bool update_offset) {
// Disable interrupts to make a critical section to avoid race condition
uint32_t prim = cpu_enter_critical();
if (update_offset) {
config_.phase_offset += count - count_in_cpr_;
config_.phase_offset = mod(config_.phase_offset, config_.cpr);
}
// Update states
count_in_cpr_ = mod(count, config_.cpr);
pos_cpr_counts_ = (float)count_in_cpr_;
cpu_exit_critical(prim);
}
bool Encoder::run_index_search() {
config_.use_index = true;
index_found_ = false;
set_idx_subscribe();
bool success = axis_->run_lockin_spin(axis_->config_.calibration_lockin, false);
return success;
}
bool Encoder::run_direction_find() {
int32_t init_enc_val = shadow_count_;
Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin;
lockin_config.finish_distance = lockin_config.vel * 3.0f; // run for 3 seconds
lockin_config.finish_on_distance = true;
lockin_config.finish_on_enc_idx = false;
lockin_config.finish_on_vel = false;
bool success = axis_->run_lockin_spin(lockin_config, false);
if (success) {
// Check response and direction
if (shadow_count_ > init_enc_val + 8) {
// motor same dir as encoder
config_.direction = 1;
} else if (shadow_count_ < init_enc_val - 8) {
// motor opposite dir as encoder
config_.direction = -1;
} else {
config_.direction = 0;
}
}
return success;
}
bool Encoder::run_hall_polarity_calibration() {
Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin;
lockin_config.finish_distance = lockin_config.vel * 3.0f; // run for 3 seconds
lockin_config.finish_on_distance = true;
lockin_config.finish_on_enc_idx = false;
lockin_config.finish_on_vel = false;
auto loop_cb = [this](bool const_vel) {
if (const_vel)
sample_hall_states_ = true;
// No need to cancel early
return true;
};
config_.hall_polarity_calibrated = false;
states_seen_count_.fill(0);
bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb);
sample_hall_states_ = false;
if (success) {
std::bitset<8> state_seen;
std::bitset<8> state_confirmed;
for (int i = 0; i < 8; i++) {
if (states_seen_count_[i] > 0)
state_seen[i] = true;
if (states_seen_count_[i] > 50)
state_confirmed[i] = true;
}
if (!(state_seen == state_confirmed)) {
set_error(ERROR_ILLEGAL_HALL_STATE);
return false;
}
// Hall effect sensors can be arranged at 60 or 120 electrical degrees.
// Out of 8 possible states, 120 and 60 deg arrangements each miss 2 states.
// ODrive assumes 120 deg separation - if a 60 deg setup is used, it can
// be converted to 120 deg states by flipping the polarity of one sensor.
uint8_t states = state_seen.to_ulong();
uint8_t hall_polarity = 0;
auto flip_detect = [](uint8_t states, unsigned int idx)->bool {
return (~states & 0xFF) == (1<<(0+idx) | 1<<(7-idx));
};
if (flip_detect(states, 0)) {
hall_polarity = 0b000;
} else if (flip_detect(states, 1)) {
hall_polarity = 0b001;
} else if (flip_detect(states, 2)) {
hall_polarity = 0b010;
} else if (flip_detect(states, 3)) {
hall_polarity = 0b100;
} else {
set_error(ERROR_ILLEGAL_HALL_STATE);
return false;
}
config_.hall_polarity = hall_polarity;
config_.hall_polarity_calibrated = true;
}
return success;
}
bool Encoder::run_hall_phase_calibration() {
Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin;
lockin_config.finish_distance = lockin_config.vel * 30.0f; // run for 30 seconds
lockin_config.finish_on_distance = true;
lockin_config.finish_on_enc_idx = false;
lockin_config.finish_on_vel = false;
auto loop_cb = [this](bool const_vel) {
if (const_vel)
sample_hall_phase_ = true;
// No need to cancel early
return true;
};
// TODO: There is a race condition here with the execution in Encoder::update.
// We should evaluate making thread execution synchronous with the control loops
// at least optionally.
// Perhaps the new loop_sync feature will give a loose timing guarantee that may be sufficient
calibrate_hall_phase_ = true;
config_.hall_edge_phcnt.fill(0.0f);
hall_phase_calib_seen_count_.fill(0);
bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb);
if (error_ & ERROR_ILLEGAL_HALL_STATE)
success = false;
if (success) {
// Check deltas to dicern rotation direction
float delta_phase = 0.0f;
for (int i = 0; i < 6; i++) {
int next_i = (i == 5) ? 0 : i+1;
delta_phase += wrap_pm_pi(config_.hall_edge_phcnt[next_i] - config_.hall_edge_phcnt[i]);
}
// Correct reverse rotation
if (delta_phase < 0.0f) {
config_.direction = -1;
for (int i = 0; i < 6; i++)
config_.hall_edge_phcnt[i] = wrap_pm_pi(-config_.hall_edge_phcnt[i]);
} else {
config_.direction = 1;
}
// Normalize edge timing to 1st edge in sequence, and change units to counts
float offset = config_.hall_edge_phcnt[0];
for (int i = 0; i < 6; i++) {
float& phcnt = config_.hall_edge_phcnt[i];
phcnt = fmodf_pos((6.0f / (2.0f * M_PI)) * (phcnt - offset), 6.0f);
}
} else {
config_.hall_edge_phcnt = hall_edge_defaults;
}
calibrate_hall_phase_ = false;
return success;
}
// @brief Turns the motor in one direction for a bit and then in the other
// direction in order to find the offset between the electrical phase 0
// and the encoder state 0.
bool Encoder::run_offset_calibration() {
const float start_lock_duration = 1.0f;
// Require index found if enabled
if (config_.use_index && !index_found_) {
set_error(ERROR_INDEX_NOT_FOUND_YET);
return false;
}
if (config_.mode == MODE_HALL && !config_.hall_polarity_calibrated) {
set_error(ERROR_HALL_NOT_CALIBRATED_YET);
return false;
}
// We use shadow_count_ to do the calibration, but the offset is used by count_in_cpr_
// Therefore we have to sync them for calibration
shadow_count_ = count_in_cpr_;
CRITICAL_SECTION() {
// Reset state variables
axis_->open_loop_controller_.Idq_setpoint_ = {0.0f, 0.0f};
axis_->open_loop_controller_.Vdq_setpoint_ = {0.0f, 0.0f};
axis_->open_loop_controller_.phase_ = 0.0f;
axis_->open_loop_controller_.phase_vel_ = 0.0f;
float max_current_ramp = axis_->motor_.config_.calibration_current / start_lock_duration * 2.0f;
axis_->open_loop_controller_.max_current_ramp_ = max_current_ramp;
axis_->open_loop_controller_.max_voltage_ramp_ = max_current_ramp;
axis_->open_loop_controller_.max_phase_vel_ramp_ = INFINITY;
axis_->open_loop_controller_.target_current_ = axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? axis_->motor_.config_.calibration_current : 0.0f;
axis_->open_loop_controller_.target_voltage_ = axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? 0.0f : axis_->motor_.config_.calibration_current;
axis_->open_loop_controller_.target_vel_ = 0.0f;
axis_->open_loop_controller_.total_distance_ = 0.0f;
axis_->open_loop_controller_.phase_ = axis_->open_loop_controller_.initial_phase_ = wrap_pm_pi(0 - config_.calib_scan_distance / 2.0f);
axis_->motor_.current_control_.enable_current_control_src_ = (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL);
axis_->motor_.current_control_.Idq_setpoint_src_.connect_to(&axis_->open_loop_controller_.Idq_setpoint_);
axis_->motor_.current_control_.Vdq_setpoint_src_.connect_to(&axis_->open_loop_controller_.Vdq_setpoint_);
axis_->motor_.current_control_.phase_src_.connect_to(&axis_->open_loop_controller_.phase_);
axis_->acim_estimator_.rotor_phase_src_.connect_to(&axis_->open_loop_controller_.phase_);
axis_->motor_.phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_);
axis_->motor_.current_control_.phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_);
axis_->acim_estimator_.rotor_phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_);
}
axis_->wait_for_control_iteration();
axis_->motor_.arm(&axis_->motor_.current_control_);
// go to start position of forward scan for start_lock_duration to get ready to scan
for (size_t i = 0; i < (size_t)(start_lock_duration * 1000.0f); ++i) {
if (!axis_->motor_.is_armed_) {
return false; // TODO: return "disarmed" error code
}
if (axis_->requested_state_ != Axis::AXIS_STATE_UNDEFINED) {
axis_->motor_.disarm();
return false; // TODO: return "aborted" error code
}
osDelay(1);
}
int32_t init_enc_val = shadow_count_;
uint32_t num_steps = 0;
int64_t encvaluesum = 0;
CRITICAL_SECTION() {
axis_->open_loop_controller_.target_vel_ = config_.calib_scan_omega;
axis_->open_loop_controller_.total_distance_ = 0.0f;
}
// scan forward
while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) {
bool reached_target_dist = axis_->open_loop_controller_.total_distance_.any().value_or(-INFINITY) >= config_.calib_scan_distance;
if (reached_target_dist) {
break;
}
encvaluesum += shadow_count_;
num_steps++;
osDelay(1);
}
// Check response and direction
if (shadow_count_ > init_enc_val + 8) {
// motor same dir as encoder
config_.direction = 1;
} else if (shadow_count_ < init_enc_val - 8) {
// motor opposite dir as encoder
config_.direction = -1;
} else {
// Encoder response error
set_error(ERROR_NO_RESPONSE);
axis_->motor_.disarm();
return false;
}
// Check CPR
float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr));
float expected_encoder_delta = config_.calib_scan_distance / elec_rad_per_enc;
calib_scan_response_ = std::abs(shadow_count_ - init_enc_val);
if (std::abs(calib_scan_response_ - expected_encoder_delta) / expected_encoder_delta > config_.calib_range) {
set_error(ERROR_CPR_POLEPAIRS_MISMATCH);
axis_->motor_.disarm();
return false;
}
CRITICAL_SECTION() {
axis_->open_loop_controller_.target_vel_ = -config_.calib_scan_omega;
}
// scan backwards
while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) {
bool reached_target_dist = axis_->open_loop_controller_.total_distance_.any().value_or(INFINITY) <= 0.0f;
if (reached_target_dist) {
break;
}
encvaluesum += shadow_count_;
num_steps++;
osDelay(1);
}
// Motor disarmed because of an error
if (!axis_->motor_.is_armed_) {
return false;
}
axis_->motor_.disarm();
config_.phase_offset = encvaluesum / num_steps;
int32_t residual = encvaluesum - ((int64_t)config_.phase_offset * (int64_t)num_steps);
config_.phase_offset_float = (float)residual / (float)num_steps + 0.5f; // add 0.5 to center-align state to phase
is_ready_ = true;
return true;
}
static bool decode_hall(uint8_t hall_state, int32_t* hall_cnt) {
switch (hall_state) {
case 0b001: *hall_cnt = 0; return true;
case 0b011: *hall_cnt = 1; return true;
case 0b010: *hall_cnt = 2; return true;
case 0b110: *hall_cnt = 3; return true;
case 0b100: *hall_cnt = 4; return true;
case 0b101: *hall_cnt = 5; return true;
default: return false;
}
}
void Encoder::sample_now() {
switch (mode_) {
case MODE_INCREMENTAL: {
tim_cnt_sample_ = (int16_t)timer_->Instance->CNT;
} break;
case MODE_HALL: {
// do nothing: samples already captured in general GPIO capture
} break;
case MODE_SINCOS: {
sincos_sample_s_ = get_adc_relative_voltage(get_gpio(config_.sincos_gpio_pin_sin)) - 0.5f;
sincos_sample_c_ = get_adc_relative_voltage(get_gpio(config_.sincos_gpio_pin_cos)) - 0.5f;
} break;
case MODE_SPI_ABS_AMS:
case MODE_SPI_ABS_CUI:
case MODE_SPI_ABS_AEAT:
case MODE_SPI_ABS_RLS:
case MODE_SPI_ABS_MA732:
{
abs_spi_start_transaction();
// Do nothing
} break;
default: {
set_error(ERROR_UNSUPPORTED_ENCODER_MODE);
} break;
}
// Sample all GPIO digital input data registers, used for HALL sensors for example.
for (size_t i = 0; i < sizeof(ports_to_sample) / sizeof(ports_to_sample[0]); ++i) {
port_samples_[i] = ports_to_sample[i]->IDR;
}
}
bool Encoder::read_sampled_gpio(Stm32Gpio gpio) {
for (size_t i = 0; i < sizeof(ports_to_sample) / sizeof(ports_to_sample[0]); ++i) {
if (ports_to_sample[i] == gpio.port_) {
return port_samples_[i] & gpio.pin_mask_;
}
}
return false;
}
void Encoder::decode_hall_samples() {
hall_state_ = (read_sampled_gpio(hallA_gpio_) ? 1 : 0)
| (read_sampled_gpio(hallB_gpio_) ? 2 : 0)
| (read_sampled_gpio(hallC_gpio_) ? 4 : 0);
}
bool Encoder::abs_spi_start_transaction() {
if (mode_ & MODE_FLAG_ABS){
if (Stm32SpiArbiter::acquire_task(&spi_task_)) {
spi_task_.ncs_gpio = abs_spi_cs_gpio_;
spi_task_.tx_buf = (uint8_t*)abs_spi_dma_tx_;
spi_task_.rx_buf = (uint8_t*)abs_spi_dma_rx_;
spi_task_.length = 1;
spi_task_.on_complete = [](void* ctx, bool success) { ((Encoder*)ctx)->abs_spi_cb(success); };
spi_task_.on_complete_ctx = this;
spi_task_.next = nullptr;
spi_arbiter_->transfer_async(&spi_task_);
} else {
return false;
}
}
return true;
}
uint8_t ams_parity(uint16_t v) {
v ^= v >> 8;
v ^= v >> 4;
v ^= v >> 2;
v ^= v >> 1;
return v & 1;
}
uint8_t cui_parity(uint16_t v) {
v ^= v >> 8;
v ^= v >> 4;
v ^= v >> 2;
return ~v & 3;
}
void Encoder::abs_spi_cb(bool success) {
uint16_t pos;
if (!success) {
goto done;
}
switch (mode_) {
case MODE_SPI_ABS_AMS: {
uint16_t rawVal = abs_spi_dma_rx_[0];
// check if parity is correct (even) and error flag clear
if (ams_parity(rawVal) || ((rawVal >> 14) & 1)) {
goto done;
}
pos = rawVal & 0x3fff;
} break;
case MODE_SPI_ABS_CUI: {
uint16_t rawVal = abs_spi_dma_rx_[0];
// check if parity is correct
if (cui_parity(rawVal)) {
goto done;
}
pos = rawVal & 0x3fff;
} break;
case MODE_SPI_ABS_RLS: {
uint16_t rawVal = abs_spi_dma_rx_[0];
pos = (rawVal >> 2) & 0x3fff;
} break;
case MODE_SPI_ABS_MA732: {
uint16_t rawVal = abs_spi_dma_rx_[0];
pos = (rawVal >> 2) & 0x3fff;
} break;
default: {
set_error(ERROR_UNSUPPORTED_ENCODER_MODE);
goto done;
} break;
}
pos_abs_ = pos;
abs_spi_pos_updated_ = true;
if (config_.pre_calibrated) {
is_ready_ = true;
}
done:
Stm32SpiArbiter::release_task(&spi_task_);
}
void Encoder::abs_spi_cs_pin_init(){
// Decode and init cs pin
#if HW_VERSION_MAJOR == 4
if (mode_ == MODE_SPI_ABS_MA732)
abs_spi_cs_gpio_ = {GPIOA, GPIO_PIN_15};
else
#else
abs_spi_cs_gpio_ = get_gpio(config_.abs_spi_cs_gpio_pin);
#endif
abs_spi_cs_gpio_.config(GPIO_MODE_OUTPUT_PP, GPIO_PULLUP);
// Write pin high
abs_spi_cs_gpio_.write(true);
}
// Note that this may return counts +1 or -1 without any wrapping
int32_t Encoder::hall_model(float internal_pos) {
int32_t base_cnt = (int32_t)std::floor(internal_pos);
float pos_in_range = fmodf_pos(internal_pos, 6.0f);
int pos_idx = (int)pos_in_range;
if (pos_idx == 6) pos_idx = 5; // in case of rounding error
int next_i = (pos_idx == 5) ? 0 : pos_idx+1;
float below_edge = config_.hall_edge_phcnt[pos_idx];
float above_edge = config_.hall_edge_phcnt[next_i];
// if we are blow the "below" edge, we are the count under
if (wrap_pm(pos_in_range - below_edge, 6.0f) < 0.0f)
return base_cnt - 1;
// if we are above the "above" edge, we are the count over
else if (wrap_pm(pos_in_range - above_edge, 6.0f) > 0.0f)
return base_cnt + 1;
// otherwise we are in the nominal count (or completely lost)
return base_cnt;
}
bool Encoder::update() {
// update internal encoder state.
int32_t delta_enc = 0;
int32_t pos_abs_latched = pos_abs_; //LATCH
switch (mode_) {
case MODE_INCREMENTAL: {
//TODO: use count_in_cpr_ instead as shadow_count_ can overflow
//or use 64 bit
int16_t delta_enc_16 = (int16_t)tim_cnt_sample_ - (int16_t)shadow_count_;
delta_enc = (int32_t)delta_enc_16; //sign extend
} break;
case MODE_HALL: {
decode_hall_samples();
if (sample_hall_states_) {
states_seen_count_[hall_state_]++;
}
if (config_.hall_polarity_calibrated) {
int32_t hall_cnt;
if (decode_hall((hall_state_ ^ config_.hall_polarity), &hall_cnt)) {
if (calibrate_hall_phase_) {
if (sample_hall_phase_ && last_hall_cnt_.has_value()) {
int mod_hall_cnt = mod(hall_cnt - last_hall_cnt_.value(), 6);
size_t edge_idx;
if (mod_hall_cnt == 0) { goto skip; } // no count - do nothing
else if (mod_hall_cnt == 1) { // counted up
edge_idx = hall_cnt;
} else if (mod_hall_cnt == 5) { // counted down
edge_idx = last_hall_cnt_.value();
} else {
set_error(ERROR_ILLEGAL_HALL_STATE);
return false;
}
auto maybe_phase = axis_->open_loop_controller_.phase_.any();
if (maybe_phase) {
float phase = maybe_phase.value();
// Early increment to get the right divisor in recursive average
hall_phase_calib_seen_count_[edge_idx]++;
float& edge_phase = config_.hall_edge_phcnt[edge_idx];
if (hall_phase_calib_seen_count_[edge_idx] == 1)
edge_phase = phase;
else {
// circularly wrapped recursive average
edge_phase += (phase - edge_phase) / hall_phase_calib_seen_count_[edge_idx];
edge_phase = wrap_pm_pi(edge_phase);
}
}
}
skip:
last_hall_cnt_ = hall_cnt;
return true; // Skip all velocity and phase estimation
}
delta_enc = hall_cnt - count_in_cpr_;
delta_enc = mod(delta_enc, 6);
if (delta_enc > 3)
delta_enc -= 6;
} else {
if (!config_.ignore_illegal_hall_state) {
set_error(ERROR_ILLEGAL_HALL_STATE);
return false;
}
}
}
} break;
case MODE_SINCOS: {
float phase = fast_atan2(sincos_sample_s_, sincos_sample_c_);
int fake_count = (int)(1000.0f * phase);
//CPR = 6283 = 2pi * 1k
delta_enc = fake_count - count_in_cpr_;
delta_enc = mod(delta_enc, 6283);
if (delta_enc > 6283/2)
delta_enc -= 6283;
} break;
case MODE_SPI_ABS_RLS:
case MODE_SPI_ABS_AMS:
case MODE_SPI_ABS_CUI:
case MODE_SPI_ABS_AEAT:
case MODE_SPI_ABS_MA732: {
if (abs_spi_pos_updated_ == false) {
// Low pass filter the error
spi_error_rate_ += current_meas_period * (1.0f - spi_error_rate_);
if (spi_error_rate_ > 0.05f) {
set_error(ERROR_ABS_SPI_COM_FAIL);
return false;
}
} else {
// Low pass filter the error
spi_error_rate_ += current_meas_period * (0.0f - spi_error_rate_);
}
abs_spi_pos_updated_ = false;
delta_enc = pos_abs_latched - count_in_cpr_; //LATCH
delta_enc = mod(delta_enc, config_.cpr);
if (delta_enc > config_.cpr/2) {
delta_enc -= config_.cpr;
}
}break;
default: {
set_error(ERROR_UNSUPPORTED_ENCODER_MODE);
return false;
} break;
}
shadow_count_ += delta_enc;
count_in_cpr_ += delta_enc;
count_in_cpr_ = mod(count_in_cpr_, config_.cpr);
if(mode_ & MODE_FLAG_ABS)
count_in_cpr_ = pos_abs_latched;
// Memory for pos_circular
float pos_cpr_counts_last = pos_cpr_counts_;
//// run pll (for now pll is in units of encoder counts)
// Predict current pos
pos_estimate_counts_ += current_meas_period * vel_estimate_counts_;
pos_cpr_counts_ += current_meas_period * vel_estimate_counts_;
// Encoder model
auto encoder_model = [this](float internal_pos)->int32_t {
if (config_.mode == MODE_HALL)
return hall_model(internal_pos);
else
return (int32_t)std::floor(internal_pos);
};
// discrete phase detector
float delta_pos_counts = (float)(shadow_count_ - encoder_model(pos_estimate_counts_));
float delta_pos_cpr_counts = (float)(count_in_cpr_ - encoder_model(pos_cpr_counts_));
delta_pos_cpr_counts = wrap_pm(delta_pos_cpr_counts, (float)(config_.cpr));
delta_pos_cpr_counts_ += 0.1f * (delta_pos_cpr_counts - delta_pos_cpr_counts_); // for debug
// pll feedback
pos_estimate_counts_ += current_meas_period * pll_kp_ * delta_pos_counts;
pos_cpr_counts_ += current_meas_period * pll_kp_ * delta_pos_cpr_counts;
pos_cpr_counts_ = fmodf_pos(pos_cpr_counts_, (float)(config_.cpr));
vel_estimate_counts_ += current_meas_period * pll_ki_ * delta_pos_cpr_counts;
bool snap_to_zero_vel = false;
if (std::abs(vel_estimate_counts_) < 0.5f * current_meas_period * pll_ki_) {
vel_estimate_counts_ = 0.0f; //align delta-sigma on zero to prevent jitter
snap_to_zero_vel = true;
}
// Outputs from Encoder for Controller
pos_estimate_ = pos_estimate_counts_ / (float)config_.cpr;
vel_estimate_ = vel_estimate_counts_ / (float)config_.cpr;
// TODO: we should strictly require that this value is from the previous iteration
// to avoid spinout scenarios. However that requires a proper way to reset
// the encoder from error states.
float pos_circular = pos_circular_.any().value_or(0.0f);
pos_circular += wrap_pm((pos_cpr_counts_ - pos_cpr_counts_last) / (float)config_.cpr, 1.0f);
pos_circular = fmodf_pos(pos_circular, axis_->controller_.config_.circular_setpoint_range);
pos_circular_ = pos_circular;
//// run encoder count interpolation
int32_t corrected_enc = count_in_cpr_ - config_.phase_offset;
// if we are stopped, make sure we don't randomly drift
if (snap_to_zero_vel || !config_.enable_phase_interpolation) {
interpolation_ = 0.5f;
// reset interpolation if encoder edge comes
// TODO: This isn't correct. At high velocities the first phase in this count may very well not be at the edge.
} else if (delta_enc > 0) {
interpolation_ = 0.0f;
} else if (delta_enc < 0) {
interpolation_ = 1.0f;
} else {
// Interpolate (predict) between encoder counts using vel_estimate,
interpolation_ += current_meas_period * vel_estimate_counts_;
// don't allow interpolation indicated position outside of [enc, enc+1)
if (interpolation_ > 1.0f) interpolation_ = 1.0f;
if (interpolation_ < 0.0f) interpolation_ = 0.0f;
}
float interpolated_enc = corrected_enc + interpolation_;
//// compute electrical phase
//TODO avoid recomputing elec_rad_per_enc every time
float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr));
float ph = elec_rad_per_enc * (interpolated_enc - config_.phase_offset_float);
if (is_ready_) {
phase_ = wrap_pm_pi(ph) * config_.direction;
phase_vel_ = (2*M_PI) * *vel_estimate_.present() * axis_->motor_.config_.pole_pairs * config_.direction;
}
return true;
}
@@ -0,0 +1,154 @@
#ifndef __ENCODER_HPP
#define __ENCODER_HPP
class Encoder;
#include <board.h> // needed for arm_math.h
#include <Drivers/STM32/stm32_spi_arbiter.hpp>
#include "utils.hpp"
#include <autogen/interfaces.hpp>
#include "component.hpp"
class Encoder : public ODriveIntf::EncoderIntf {
public:
static constexpr uint32_t MODE_FLAG_ABS = 0x100;
static constexpr std::array<float, 6> hall_edge_defaults =
{0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
struct Config_t {
Mode mode = MODE_INCREMENTAL;
float calib_range = 0.02f; // Accuracy required to pass encoder cpr check
float calib_scan_distance = 16.0f * M_PI; // rad electrical
float calib_scan_omega = 4.0f * M_PI; // rad/s electrical
float bandwidth = 1000.0f;
int32_t phase_offset = 0; // Offset between encoder count and rotor electrical phase
float phase_offset_float = 0.0f; // Sub-count phase alignment offset
int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder,
float index_offset = 0.0f;
bool use_index = false;
bool pre_calibrated = false; // If true, this means the offset stored in
// configuration is valid and does not need
// be determined by run_offset_calibration.
// In this case the encoder will enter ready
// state as soon as the index is found.
int32_t direction = 0; // direction with respect to motor
bool use_index_offset = true;
bool enable_phase_interpolation = true; // Use velocity to interpolate inside the count state
bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state
bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111
uint8_t hall_polarity = 0;
bool hall_polarity_calibrated = false;
std::array<float, 6> hall_edge_phcnt = hall_edge_defaults;
uint16_t abs_spi_cs_gpio_pin = 1;
uint16_t sincos_gpio_pin_sin = 3;
uint16_t sincos_gpio_pin_cos = 4;
// custom setters
Encoder* parent = nullptr;
void set_use_index(bool value) { use_index = value; parent->set_idx_subscribe(); }
void set_find_idx_on_lockin_only(bool value) { find_idx_on_lockin_only = value; parent->set_idx_subscribe(); }
void set_abs_spi_cs_gpio_pin(uint16_t value) { abs_spi_cs_gpio_pin = value; parent->abs_spi_cs_pin_init(); }
void set_pre_calibrated(bool value) { pre_calibrated = value; parent->check_pre_calibrated(); }
void set_bandwidth(float value) { bandwidth = value; parent->update_pll_gains(); }
};
Encoder(TIM_HandleTypeDef* timer, Stm32Gpio index_gpio,
Stm32Gpio hallA_gpio, Stm32Gpio hallB_gpio, Stm32Gpio hallC_gpio,
Stm32SpiArbiter* spi_arbiter);
bool apply_config(ODriveIntf::MotorIntf::MotorType motor_type);
void setup();
void set_error(Error error);
bool do_checks();
void enc_index_cb();
void set_idx_subscribe(bool override_enable = false);
void update_pll_gains();
void check_pre_calibrated();
void set_linear_count(int32_t count);
void set_circular_count(int32_t count, bool update_offset);
bool calib_enc_offset(float voltage_magnitude);
bool run_index_search();
bool run_direction_find();
bool run_hall_polarity_calibration();
bool run_hall_phase_calibration();
bool run_offset_calibration();
void sample_now();
bool read_sampled_gpio(Stm32Gpio gpio);
void decode_hall_samples();
int32_t hall_model(float internal_pos);
bool update();
TIM_HandleTypeDef* timer_;
Stm32Gpio index_gpio_;
Stm32Gpio hallA_gpio_;
Stm32Gpio hallB_gpio_;
Stm32Gpio hallC_gpio_;
Stm32SpiArbiter* spi_arbiter_;
Axis* axis_ = nullptr; // set by Axis constructor
Config_t config_;
Error error_ = ERROR_NONE;
bool index_found_ = false;
bool is_ready_ = false;
int32_t shadow_count_ = 0;
int32_t count_in_cpr_ = 0;
float interpolation_ = 0.0f;
OutputPort<float> phase_ = 0.0f; // [rad]
OutputPort<float> phase_vel_ = 0.0f; // [rad/s]
float pos_estimate_counts_ = 0.0f; // [count]
float pos_cpr_counts_ = 0.0f; // [count]
float delta_pos_cpr_counts_ = 0.0f; // [count] phase detector result for debug
float vel_estimate_counts_ = 0.0f; // [count/s]
float pll_kp_ = 0.0f; // [count/s / count]
float pll_ki_ = 0.0f; // [(count/s^2) / count]
float calib_scan_response_ = 0.0f; // debug report from offset calib
int32_t pos_abs_ = 0;
float spi_error_rate_ = 0.0f;
OutputPort<float> pos_estimate_ = 0.0f; // [turn]
OutputPort<float> vel_estimate_ = 0.0f; // [turn/s]
OutputPort<float> pos_circular_ = 0.0f; // [turn]
bool pos_estimate_valid_ = false;
bool vel_estimate_valid_ = false;
int16_t tim_cnt_sample_ = 0; //
static const constexpr GPIO_TypeDef* ports_to_sample[] = { GPIOA, GPIOB, GPIOC };
uint16_t port_samples_[sizeof(ports_to_sample) / sizeof(ports_to_sample[0])];
// Updated by low_level pwm_adc_cb
uint8_t hall_state_ = 0x0; // bit[0] = HallA, .., bit[2] = HallC
std::optional<uint8_t> last_hall_cnt_ = std::nullopt; // Used to find hall edges for calibration
bool calibrate_hall_phase_ = false;
bool sample_hall_states_ = false;
bool sample_hall_phase_ = false;
std::array<int, 8> states_seen_count_; // for hall polarity calibration
std::array<int, 6> hall_phase_calib_seen_count_;
float sincos_sample_s_ = 0.0f;
float sincos_sample_c_ = 0.0f;
bool abs_spi_start_transaction();
void abs_spi_cb(bool success);
void abs_spi_cs_pin_init();
bool abs_spi_pos_updated_ = false;
Mode mode_ = MODE_INCREMENTAL;
Stm32Gpio abs_spi_cs_gpio_;
uint32_t abs_spi_cr1;
uint32_t abs_spi_cr2;
uint16_t abs_spi_dma_tx_[1] = {0xFFFF};
uint16_t abs_spi_dma_rx_[1];
Stm32SpiArbiter::SpiTask spi_task_;
constexpr float getCoggingRatio(){
return 1.0f / 3600.0f;
}
};
#endif // __ENCODER_HPP
@@ -0,0 +1,32 @@
#include <odrive_main.h>
void Endstop::update() {
debounceTimer_.update();
last_state_ = endstop_state_;
if (config_.enabled) {
bool last_pin_state = pin_state_;
pin_state_ = get_gpio(config_.gpio_num).read();
// If the pin state has changed, reset the timer
if (pin_state_ != last_pin_state)
debounceTimer_.reset();
if (debounceTimer_.expired())
endstop_state_ = config_.is_active_high ? pin_state_ : !pin_state_; // endstop_state is the logical state
} else {
endstop_state_ = false;
}
}
bool Endstop::apply_config() {
debounceTimer_.reset();
if (config_.enabled) {
debounceTimer_.start();
} else {
debounceTimer_.stop();
}
debounceTimer_.setIncrement(config_.debounce_ms * 0.001f);
return true;
}
@@ -0,0 +1,48 @@
#ifndef __ENDSTOP_HPP
#define __ENDSTOP_HPP
#include "timer.hpp"
class Endstop {
public:
struct Config_t {
float offset = 0;
uint32_t debounce_ms = 50;
uint16_t gpio_num = 0;
bool enabled = false;
bool is_active_high = false;
// custom setters
Endstop* parent = nullptr;
void set_gpio_num(uint16_t value) { gpio_num = value; parent->apply_config(); }
void set_enabled(uint32_t value) { enabled = value; parent->apply_config(); }
void set_debounce_ms(uint32_t value) { debounce_ms = value; parent->apply_config(); }
};
Endstop::Config_t config_;
Axis* axis_ = nullptr;
bool apply_config();
void update();
constexpr bool get_state(){
return endstop_state_;
}
constexpr bool rose(){
return (endstop_state_ != last_state_) && endstop_state_;
}
constexpr bool fell(){
return (endstop_state_ != last_state_) && !endstop_state_;
}
bool endstop_state_ = false;
private:
bool last_state_ = false;
bool pin_state_ = false;
float pos_when_pressed_ = 0.0f;
Timer<float> debounceTimer_;
};
#endif
@@ -0,0 +1,37 @@
[
{
"name": "",
"id": 0,
"type": "json"
},
{
"name": "subscriptions",
"id": 1,
"type": "int32[]"
},
{
"name": "motor0",
"id": 2,
"type": "tree",
"content": [
{
"name": "pos_setpoint",
"id": 3,
"type": "float",
"access": "rw"
},
{
"name": "pos_gain",
"id": 4,
"type": "float",
"access": "rw"
},
{
"name": "vel_setpoint",
"id": 5,
"type": "float",
"access": "rw"
}
]
}
]
@@ -0,0 +1,193 @@
#include "foc.hpp"
#include <board.h>
Motor::Error AlphaBetaFrameController::on_measurement(
std::optional<float> vbus_voltage,
std::optional<std::array<float, 3>> currents,
uint32_t input_timestamp) {
std::optional<float2D> Ialpha_beta;
if (currents.has_value()) {
// Clarke transform
Ialpha_beta = {
(*currents)[0],
one_by_sqrt3 * ((*currents)[1] - (*currents)[2])
};
}
return on_measurement(vbus_voltage, Ialpha_beta, input_timestamp);
}
Motor::Error AlphaBetaFrameController::get_output(
uint32_t output_timestamp, float (&pwm_timings)[3],
std::optional<float>* ibus) {
std::optional<float2D> mod_alpha_beta;
Motor::Error status = get_alpha_beta_output(output_timestamp, &mod_alpha_beta, ibus);
if (status != Motor::ERROR_NONE) {
return status;
} else if (!mod_alpha_beta.has_value() || is_nan(mod_alpha_beta->first) || is_nan(mod_alpha_beta->second)) {
return Motor::ERROR_MODULATION_IS_NAN;
}
auto [tA, tB, tC, success] = SVM(mod_alpha_beta->first, mod_alpha_beta->second);
if (!success) {
return Motor::ERROR_MODULATION_MAGNITUDE;
}
pwm_timings[0] = tA;
pwm_timings[1] = tB;
pwm_timings[2] = tC;
return Motor::ERROR_NONE;
}
void FieldOrientedController::reset() {
v_current_control_integral_d_ = 0.0f;
v_current_control_integral_q_ = 0.0f;
vbus_voltage_measured_ = std::nullopt;
Ialpha_beta_measured_ = std::nullopt;
power_ = 0.0f;
}
Motor::Error FieldOrientedController::on_measurement(
std::optional<float> vbus_voltage, std::optional<float2D> Ialpha_beta,
uint32_t input_timestamp) {
// Store the measurements for later processing.
i_timestamp_ = input_timestamp;
vbus_voltage_measured_ = vbus_voltage;
Ialpha_beta_measured_ = Ialpha_beta;
return Motor::ERROR_NONE;
}
ODriveIntf::MotorIntf::Error FieldOrientedController::get_alpha_beta_output(
uint32_t output_timestamp, std::optional<float2D>* mod_alpha_beta,
std::optional<float>* ibus) {
if (!vbus_voltage_measured_.has_value() || !Ialpha_beta_measured_.has_value()) {
// FOC didn't receive a current measurement yet.
return Motor::ERROR_CONTROLLER_INITIALIZING;
} else if (abs((int32_t)(i_timestamp_ - ctrl_timestamp_)) > MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA) {
// Data from control loop and current measurement are too far apart.
return Motor::ERROR_BAD_TIMING;
}
// TODO: improve efficiency in case PWM updates are requested at a higher
// rate than current sensor updates. In this case we can reuse mod_d and
// mod_q from a previous iteration.
if (!Vdq_setpoint_.has_value()) {
return Motor::ERROR_UNKNOWN_VOLTAGE_COMMAND;
} else if (!phase_.has_value() || !phase_vel_.has_value()) {
return Motor::ERROR_UNKNOWN_PHASE_ESTIMATE;
} else if (!vbus_voltage_measured_.has_value()) {
return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE;
}
auto [Vd, Vq] = *Vdq_setpoint_;
float phase = *phase_;
float phase_vel = *phase_vel_;
float vbus_voltage = *vbus_voltage_measured_;
std::optional<float2D> Idq;
// Park transform
if (Ialpha_beta_measured_.has_value()) {
auto [Ialpha, Ibeta] = *Ialpha_beta_measured_;
float I_phase = phase + phase_vel * ((float)(int32_t)(i_timestamp_ - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ);
float c_I = our_arm_cos_f32(I_phase);
float s_I = our_arm_sin_f32(I_phase);
Idq = {
c_I * Ialpha + s_I * Ibeta,
c_I * Ibeta - s_I * Ialpha
};
Id_measured_ += I_measured_report_filter_k_ * (Idq->first - Id_measured_);
Iq_measured_ += I_measured_report_filter_k_ * (Idq->second - Iq_measured_);
} else {
Id_measured_ = 0.0f;
Iq_measured_ = 0.0f;
}
float mod_to_V = (2.0f / 3.0f) * vbus_voltage;
float V_to_mod = 1.0f / mod_to_V;
float mod_d;
float mod_q;
if (enable_current_control_) {
// Current control mode
if (!pi_gains_.has_value()) {
return Motor::ERROR_UNKNOWN_GAINS;
} else if (!Idq.has_value()) {
return Motor::ERROR_UNKNOWN_CURRENT_MEASUREMENT;
} else if (!Idq_setpoint_.has_value()) {
return Motor::ERROR_UNKNOWN_CURRENT_COMMAND;
}
auto [p_gain, i_gain] = *pi_gains_;
auto [Id, Iq] = *Idq;
auto [Id_setpoint, Iq_setpoint] = *Idq_setpoint_;
float Ierr_d = Id_setpoint - Id;
float Ierr_q = Iq_setpoint - Iq;
// Apply PI control (V{d,q}_setpoint act as feed-forward terms in this mode)
mod_d = V_to_mod * (Vd + v_current_control_integral_d_ + Ierr_d * p_gain);
mod_q = V_to_mod * (Vq + v_current_control_integral_q_ + Ierr_q * p_gain);
// Vector modulation saturation, lock integrator if saturated
// TODO make maximum modulation configurable
float mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / std::sqrt(mod_d * mod_d + mod_q * mod_q);
if (mod_scalefactor < 1.0f) {
mod_d *= mod_scalefactor;
mod_q *= mod_scalefactor;
// TODO make decayfactor configurable
v_current_control_integral_d_ *= 0.99f;
v_current_control_integral_q_ *= 0.99f;
} else {
v_current_control_integral_d_ += Ierr_d * (i_gain * current_meas_period);
v_current_control_integral_q_ += Ierr_q * (i_gain * current_meas_period);
}
} else {
// Voltage control mode
mod_d = V_to_mod * Vd;
mod_q = V_to_mod * Vq;
}
// Inverse park transform
float pwm_phase = phase + phase_vel * ((float)(int32_t)(output_timestamp - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ);
float c_p = our_arm_cos_f32(pwm_phase);
float s_p = our_arm_sin_f32(pwm_phase);
float mod_alpha = c_p * mod_d - s_p * mod_q;
float mod_beta = c_p * mod_q + s_p * mod_d;
// Report final applied voltage in stationary frame (for sensorless estimator)
final_v_alpha_ = mod_to_V * mod_alpha;
final_v_beta_ = mod_to_V * mod_beta;
*mod_alpha_beta = {mod_alpha, mod_beta};
if (Idq.has_value()) {
auto [Id, Iq] = *Idq;
*ibus = mod_d * Id + mod_q * Iq;
power_ = vbus_voltage * (*ibus).value();
}
return Motor::ERROR_NONE;
}
void FieldOrientedController::update(uint32_t timestamp) {
CRITICAL_SECTION() {
ctrl_timestamp_ = timestamp;
enable_current_control_ = enable_current_control_src_;
Idq_setpoint_ = Idq_setpoint_src_.present();
Vdq_setpoint_ = Vdq_setpoint_src_.present();
phase_ = phase_src_.present();
phase_vel_ = phase_vel_src_.present();
}
}
@@ -0,0 +1,66 @@
#ifndef __FOC_HPP
#define __FOC_HPP
#include "phase_control_law.hpp"
#include "component.hpp"
/**
* @brief Field oriented controller.
*
* This controller can run in either current control mode or voltage control
* mode.
*/
class FieldOrientedController : public AlphaBetaFrameController, public ComponentBase {
public:
void update(uint32_t timestamp) final;
void reset() final;
ODriveIntf::MotorIntf::Error on_measurement(
std::optional<float> vbus_voltage,
std::optional<float2D> Ialpha_beta,
uint32_t input_timestamp) final;
ODriveIntf::MotorIntf::Error get_alpha_beta_output(
uint32_t output_timestamp,
std::optional<float2D>* mod_alpha_beta,
std::optional<float>* ibus) final;
// Config - these values are set while this controller is inactive
std::optional<float2D> pi_gains_; // [V/A, V/As] should be auto set after resistance and inductance measurement
float I_measured_report_filter_k_ = 1.0f;
// Inputs
bool enable_current_control_src_ = false;
InputPort<float2D> Idq_setpoint_src_;
InputPort<float2D> Vdq_setpoint_src_;
InputPort<float> phase_src_;
InputPort<float> phase_vel_src_;
// These values are set atomically by the update() function and read by the
// calculate() function in an interrupt context.
uint32_t ctrl_timestamp_; // [HCLK ticks]
bool enable_current_control_ = false; // true: FOC runs in current control mode using I{dq}_setpoint, false: FOC runs in voltage control mode using V{dq}_setpoint
std::optional<float2D> Idq_setpoint_; // [A] only used if enable_current_control_ == true
std::optional<float2D> Vdq_setpoint_; // [V] feed-forward voltage term (or standalone setpoint if enable_current_control_ == false)
std::optional<float> phase_; // [rad]
std::optional<float> phase_vel_; // [rad/s]
// These values (or some of them) are updated inside on_measurement() and get_alpha_beta_output()
uint32_t i_timestamp_;
std::optional<float> vbus_voltage_measured_; // [V]
std::optional<float2D> Ialpha_beta_measured_; // [A, A]
float Id_measured_; // [A]
float Iq_measured_; // [A]
float v_current_control_integral_d_ = 0.0f; // [V]
float v_current_control_integral_q_ = 0.0f; // [V]
//float mod_to_V_ = 0.0f;
//float mod_d_ = 0.0f;
//float mod_q_ = 0.0f;
//float ibus_ = 0.0f;
float final_v_alpha_ = 0.0f; // [V]
float final_v_beta_ = 0.0f; // [V]
float power_ = 0.0f; // [W] dot product of Vdq and Idq
};
#endif // __FOC_HPP
@@ -0,0 +1,411 @@
/* Includes ------------------------------------------------------------------*/
#include <board.h>
#include <cmsis_os.h>
#include <cmath>
#include <stdint.h>
#include <stdlib.h>
#include <adc.h>
#include <gpio.h>
#include <main.h>
#include <spi.h>
#include <tim.h>
#include <utils.hpp>
#include "odrive_main.h"
/* Private defines -----------------------------------------------------------*/
// #define DEBUG_PRINT
/* Private macros ------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Global constant data ------------------------------------------------------*/
constexpr float adc_full_scale = static_cast<float>(1UL << 12UL);
constexpr float adc_ref_voltage = 3.3f;
const uint32_t stack_size_analog_thread = 1024; // Bytes
/* Global variables ----------------------------------------------------------*/
// This value is updated by the DC-bus reading ADC.
// Arbitrary non-zero inital value to avoid division by zero if ADC reading is late
float vbus_voltage = 12.0f;
float ibus_ = 0.0f; // exposed for monitoring only
bool brake_resistor_armed = false;
bool brake_resistor_saturated = false;
float brake_resistor_current = 0.0f;
osThreadId analog_thread = 0;
/* Private constant data -----------------------------------------------------*/
/* CPU critical section helpers ----------------------------------------------*/
/* Safety critical functions -------------------------------------------------*/
/*
* This section contains all accesses to safety critical hardware registers.
* Specifically, these registers:
* Motor0 PWMs:
* Timer1.MOE (master output enabled)
* Timer1.CCR1 (counter compare register 1)
* Timer1.CCR2 (counter compare register 2)
* Timer1.CCR3 (counter compare register 3)
* Motor1 PWMs:
* Timer8.MOE (master output enabled)
* Timer8.CCR1 (counter compare register 1)
* Timer8.CCR2 (counter compare register 2)
* Timer8.CCR3 (counter compare register 3)
* Brake resistor PWM:
* Timer2.CCR3 (counter compare register 3)
* Timer2.CCR4 (counter compare register 4)
*
* The following assumptions are made:
* - The hardware operates as described in the 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
* This assumption also requires for instance that there are no radiation
* caused hardware errors.
* - After startup, all variables used in this section are exclusively modified
* by the code in this section (this excludes function parameters)
* This assumption also requires that there is no memory corruption.
* - This code is compiled by a C standard compliant compiler.
*
* Furthermore:
* - Between calls to safety_critical_arm_motor_pwm and
* safety_critical_disarm_motor_pwm the motor's Ibus current is
* set to the correct value and update_brake_resistor is called
* at a high rate.
*/
// @brief Arms the brake resistor
void safety_critical_arm_brake_resistor() {
CRITICAL_SECTION() {
for (size_t i = 0; i < AXIS_COUNT; ++i) {
axes[i].motor_.I_bus_ = 0.0f;
}
brake_resistor_armed = true;
#if HW_VERSION_MAJOR == 3
htim2.Instance->CCR3 = 0;
htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1;
#endif
}
}
// @brief Disarms the brake resistor and by extension
// all motor PWM outputs.
// After calling this, the brake resistor can only be armed again
// by calling safety_critical_arm_brake_resistor().
void safety_critical_disarm_brake_resistor() {
bool brake_resistor_was_armed = brake_resistor_armed;
CRITICAL_SECTION() {
brake_resistor_armed = false;
#if HW_VERSION_MAJOR == 3
htim2.Instance->CCR3 = 0;
htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1;
#endif
}
// Check necessary to prevent infinite recursion
if (brake_resistor_was_armed) {
for (auto& axis: axes) {
axis.motor_.disarm();
}
}
}
// @brief Updates the brake resistor PWM timings unless
// the brake resistor is disarmed.
void safety_critical_apply_brake_resistor_timings(uint32_t low_off, uint32_t high_on) {
if (high_on - low_off < TIM_APB1_DEADTIME_CLOCKS) {
odrv.disarm_with_error(ODrive::ERROR_BRAKE_DEADTIME_VIOLATION);
}
CRITICAL_SECTION() {
if (brake_resistor_armed) {
#if HW_VERSION_MAJOR == 3
// Safe update of low and high side timings
// To avoid race condition, first reset timings to safe state
// ch3 is low side, ch4 is high side
htim2.Instance->CCR3 = 0;
htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1;
htim2.Instance->CCR3 = low_off;
htim2.Instance->CCR4 = high_on;
#endif
}
}
}
/* Function implementations --------------------------------------------------*/
void start_adc_pwm() {
// Disarm motors
for (auto& axis: axes) {
axis.motor_.disarm();
}
for (Motor& motor: motors) {
// Init PWM
int half_load = TIM_1_8_PERIOD_CLOCKS / 2;
motor.timer_->Instance->CCR1 = half_load;
motor.timer_->Instance->CCR2 = half_load;
motor.timer_->Instance->CCR3 = half_load;
// Enable PWM outputs (they are still masked by MOE though)
motor.timer_->Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_1);
motor.timer_->Instance->CCER |= (TIM_CCxN_ENABLE << TIM_CHANNEL_1);
motor.timer_->Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_2);
motor.timer_->Instance->CCER |= (TIM_CCxN_ENABLE << TIM_CHANNEL_2);
motor.timer_->Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_3);
motor.timer_->Instance->CCER |= (TIM_CCxN_ENABLE << TIM_CHANNEL_3);
}
// Enable ADC and interrupts
__HAL_ADC_ENABLE(&hadc1);
__HAL_ADC_ENABLE(&hadc2);
__HAL_ADC_ENABLE(&hadc3);
// Warp field stabilize.
osDelay(2);
start_timers();
// Start brake resistor PWM in floating output configuration
#if HW_VERSION_MAJOR == 3
htim2.Instance->CCR3 = 0;
htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1;
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
#endif
if (odrv.config_.enable_brake_resistor) {
safety_critical_arm_brake_resistor();
}
}
// @brief ADC1 measurements are written to this buffer by DMA
uint16_t adc_measurements_[ADC_CHANNEL_COUNT] = { 0 };
// @brief Starts the general purpose ADC on the ADC1 peripheral.
// The measured ADC voltages can be read with get_adc_voltage().
//
// ADC1 is set up to continuously sample all channels 0 to 15 in a
// round-robin fashion.
// DMA is used to copy the measured 12-bit values to adc_measurements_.
//
// The injected (high priority) channel of ADC1 is used to sample vbus_voltage.
// This conversion is triggered by TIM1 at the frequency of the motor control loop.
void start_general_purpose_adc() {
ADC_ChannelConfTypeDef sConfig;
// Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = ADC_CHANNEL_COUNT;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
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)
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
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) {
odrv.misconfigured_ = true; // TODO: this is a bit of an abuse of this flag
return;
}
}
HAL_ADC_Start_DMA(&hadc1, reinterpret_cast<uint32_t*>(adc_measurements_), ADC_CHANNEL_COUNT);
}
// @brief Returns the ADC voltage associated with the specified pin.
// This only works if the GPIO was not used for anything else since bootup, otherwise
// it must be put to analog mode first.
// Returns -1.0f if the pin has no associated ADC1 channel.
//
// On ODrive 3.3 and 3.4 the following pins can be used with this function:
// GPIO_1, GPIO_2, GPIO_3, GPIO_4 and some pins that are connected to
// on-board sensors (M0_TEMP, M1_TEMP, AUX_TEMP)
//
// The ADC values are sampled in background at ~30kHz without
// any CPU involvement.
//
// Details: each of the 16 conversion takes (15+26) ADC clock
// cycles and the ADC, so the update rate of the entire sequence is:
// 21000kHz / (15+26) / 16 = 32kHz
// The true frequency is slightly lower because of the injected vbus
// measurements
float get_adc_voltage(Stm32Gpio gpio) {
return get_adc_relative_voltage(gpio) * adc_ref_voltage;
}
float get_adc_relative_voltage(Stm32Gpio gpio) {
const uint16_t channel = channel_from_gpio(gpio);
return get_adc_relative_voltage_ch(channel);
}
// @brief Given a GPIO_port and pin return the associated adc_channel.
// returns UINT16_MAX if there is no adc_channel;
uint16_t channel_from_gpio(Stm32Gpio gpio) {
uint32_t channel = UINT32_MAX;
if (gpio.port_ == GPIOA) {
if (gpio.pin_mask_ == GPIO_PIN_0)
channel = 0;
else if (gpio.pin_mask_ == GPIO_PIN_1)
channel = 1;
else if (gpio.pin_mask_ == GPIO_PIN_2)
channel = 2;
else if (gpio.pin_mask_ == GPIO_PIN_3)
channel = 3;
else if (gpio.pin_mask_ == GPIO_PIN_4)
channel = 4;
else if (gpio.pin_mask_ == GPIO_PIN_5)
channel = 5;
else if (gpio.pin_mask_ == GPIO_PIN_6)
channel = 6;
else if (gpio.pin_mask_ == GPIO_PIN_7)
channel = 7;
} else if (gpio.port_ == GPIOB) {
if (gpio.pin_mask_ == GPIO_PIN_0)
channel = 8;
else if (gpio.pin_mask_ == GPIO_PIN_1)
channel = 9;
} else if (gpio.port_ == GPIOC) {
if (gpio.pin_mask_ == GPIO_PIN_0)
channel = 10;
else if (gpio.pin_mask_ == GPIO_PIN_1)
channel = 11;
else if (gpio.pin_mask_ == GPIO_PIN_2)
channel = 12;
else if (gpio.pin_mask_ == GPIO_PIN_3)
channel = 13;
else if (gpio.pin_mask_ == GPIO_PIN_4)
channel = 14;
else if (gpio.pin_mask_ == GPIO_PIN_5)
channel = 15;
}
return channel;
}
// @brief Given an adc channel return the voltage as a ratio of adc_ref_voltage
// returns -1.0f if the channel is not valid.
float get_adc_relative_voltage_ch(uint16_t channel) {
if (channel < ADC_CHANNEL_COUNT)
return (float)adc_measurements_[channel] / adc_full_scale;
else
return -1.0f;
}
//--------------------------------
// IRQ Callbacks
//--------------------------------
void vbus_sense_adc_cb(uint32_t adc_value) {
constexpr float voltage_scale = adc_ref_voltage * VBUS_S_DIVIDER_RATIO / adc_full_scale;
vbus_voltage = adc_value * voltage_scale;
}
// @brief Sums up the Ibus contribution of each motor and updates the
// brake resistor PWM accordingly.
void update_brake_current() {
float Ibus_sum = 0.0f;
for (size_t i = 0; i < AXIS_COUNT; ++i) {
if (axes[i].motor_.is_armed_) {
Ibus_sum += axes[i].motor_.I_bus_;
}
}
float brake_duty = 0.0f;
float brake_current = 0.0f;
if (odrv.config_.enable_brake_resistor) {
if (!(odrv.config_.brake_resistance > 0.0f)) {
odrv.disarm_with_error(ODrive::ERROR_INVALID_BRAKE_RESISTANCE);
return;
}
// Don't start braking until -Ibus > regen_current_allowed
brake_current = -Ibus_sum - odrv.config_.max_regen_current;
brake_duty = brake_current * odrv.config_.brake_resistance / vbus_voltage;
if (odrv.config_.enable_dc_bus_overvoltage_ramp && (odrv.config_.brake_resistance > 0.0f) && (odrv.config_.dc_bus_overvoltage_ramp_start < odrv.config_.dc_bus_overvoltage_ramp_end)) {
brake_duty += std::max((vbus_voltage - odrv.config_.dc_bus_overvoltage_ramp_start) / (odrv.config_.dc_bus_overvoltage_ramp_end - odrv.config_.dc_bus_overvoltage_ramp_start), 0.0f);
}
if (is_nan(brake_duty)) {
// Shuts off all motors AND brake resistor, sets error code on all motors.
odrv.disarm_with_error(ODrive::ERROR_BRAKE_DUTY_CYCLE_NAN);
return;
}
if (brake_duty >= 0.95f) {
brake_resistor_saturated = true;
}
// Duty limit at 95% to allow bootstrap caps to charge
brake_duty = std::clamp(brake_duty, 0.0f, 0.95f);
// This cannot result in NaN (safe for race conditions) because we check
// brake_resistance != 0 further up.
brake_current = brake_duty * vbus_voltage / odrv.config_.brake_resistance;
Ibus_sum += brake_duty * vbus_voltage / odrv.config_.brake_resistance;
} else {
brake_duty = 0;
}
brake_resistor_current = brake_current;
ibus_ += odrv.ibus_report_filter_k_ * (Ibus_sum - ibus_);
if (Ibus_sum > odrv.config_.dc_max_positive_current) {
odrv.disarm_with_error(ODrive::ERROR_DC_BUS_OVER_CURRENT);
return;
}
if (Ibus_sum < odrv.config_.dc_max_negative_current) {
odrv.disarm_with_error(ODrive::ERROR_DC_BUS_OVER_REGEN_CURRENT);
return;
}
int high_on = (int)(TIM_APB1_PERIOD_CLOCKS * (1.0f - brake_duty));
int low_off = high_on - TIM_APB1_DEADTIME_CLOCKS;
if (low_off < 0) low_off = 0;
safety_critical_apply_brake_resistor_timings(low_off, high_on);
}
/* Analog speed control input */
static void update_analog_endpoint(const struct PWMMapping_t *map, int gpio)
{
float fraction = get_adc_voltage(get_gpio(gpio)) / 3.3f;
float value = map->min + (fraction * (map->max - map->min));
fibre::set_endpoint_from_float(map->endpoint, value);
}
static void analog_polling_thread(void *)
{
while (true) {
for (int i = 0; i < GPIO_COUNT; i++) {
struct PWMMapping_t *map = &odrv.config_.analog_mappings[i];
if (fibre::is_endpoint_ref_valid(map->endpoint))
update_analog_endpoint(map, i);
}
osDelay(10);
}
}
void start_analog_thread() {
osThreadDef(analog_thread_def, analog_polling_thread, osPriorityLow, 0, stack_size_analog_thread / sizeof(StackType_t));
analog_thread = osThreadCreate(osThread(analog_thread_def), NULL);
}
@@ -0,0 +1,63 @@
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __LOW_LEVEL_H
#define __LOW_LEVEL_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <cmsis_os.h>
#include <stdbool.h>
#include <adc.h>
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
#define ADC_CHANNEL_COUNT 16
extern const float adc_full_scale;
extern const float adc_ref_voltage;
/* Exported variables --------------------------------------------------------*/
extern float vbus_voltage;
extern float ibus_;
extern bool brake_resistor_armed;
extern bool brake_resistor_saturated;
extern float brake_resistor_current;
extern uint16_t adc_measurements_[ADC_CHANNEL_COUNT];
extern osThreadId analog_thread;
extern const uint32_t stack_size_analog_thread;
/* Exported macro ------------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
void safety_critical_arm_brake_resistor();
void safety_critical_disarm_brake_resistor();
void safety_critical_apply_brake_resistor_timings(uint32_t low_off, uint32_t high_on);
// called from STM platform code
extern "C" {
void vbus_sense_adc_cb(uint32_t adc_value);
void pwm_in_cb(TIM_HandleTypeDef *htim);
}
// Initalisation
void start_adc_pwm();
void start_pwm(TIM_HandleTypeDef* htim);
void sync_timers(TIM_HandleTypeDef* htim_a, TIM_HandleTypeDef* htim_b,
uint16_t TIM_CLOCKSOURCE_ITRx, uint16_t count_offset,
TIM_HandleTypeDef* htim_refbase = nullptr);
void start_general_purpose_adc();
void pwm_in_init();
void start_analog_thread();
// ADC getters
uint16_t channel_from_gpio(Stm32Gpio gpio);
float get_adc_voltage(Stm32Gpio gpio);
float get_adc_relative_voltage(Stm32Gpio gpio);
float get_adc_relative_voltage_ch(uint16_t channel);
void update_brake_current();
#ifdef __cplusplus
}
#endif
#endif //__LOW_LEVEL_H
@@ -0,0 +1,846 @@
#define __MAIN_CPP__
#include "odrive_main.h"
#include "nvm_config.hpp"
#include "usart.h"
#include "freertos_vars.h"
#include "usb_device.h"
#include <communication/interface_usb.h>
#include <communication/interface_uart.h>
#include <communication/interface_i2c.h>
#include <communication/interface_can.hpp>
osSemaphoreId sem_usb_irq;
osMessageQId uart_event_queue;
osMessageQId usb_event_queue;
osSemaphoreId sem_can;
#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")));
extern char _estack; // provided by the linker script
ODrive odrv{};
ConfigManager config_manager;
class StatusLedController {
public:
void update();
};
StatusLedController status_led_controller;
void StatusLedController::update() {
#if HW_VERSION_MAJOR == 4
uint32_t t = HAL_GetTick();
bool is_booting = std::any_of(axes.begin(), axes.end(), [](Axis& axis){
return axis.current_state_ == Axis::AXIS_STATE_UNDEFINED;
});
if (is_booting) {
return;
}
bool is_armed = std::any_of(axes.begin(), axes.end(), [](Axis& axis){
return axis.motor_.is_armed_;
});
bool any_error = odrv.any_error();
if (is_armed) {
// Fast green pulsating
const uint32_t period_ms = 256;
const uint8_t min_brightness = 0;
const uint8_t max_brightness = 255;
uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness;
brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt
status_led.set_color(rgb_t{(uint8_t)(any_error ? brightness / 2 : 0), (uint8_t)brightness, 0});
} else if (any_error) {
// Red pulsating
const uint32_t period_ms = 1024;
const uint8_t min_brightness = 0;
const uint8_t max_brightness = 255;
uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness;
brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt
status_led.set_color(rgb_t{(uint8_t)brightness, 0, 0});
} else {
// Slow blue pulsating
const uint32_t period_ms = 4096;
const uint8_t min_brightness = 50;
const uint8_t max_brightness = 160;
uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness;
brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt
status_led.set_color(rgb_t{0, 0, (uint8_t)brightness});
}
#endif
}
static bool config_read_all() {
bool success = board_read_config() &&
config_manager.read(&odrv.config_) &&
config_manager.read(&odrv.can_.config_);
for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) {
success = config_manager.read(&encoders[i].config_) &&
config_manager.read(&axes[i].sensorless_estimator_.config_) &&
config_manager.read(&axes[i].controller_.config_) &&
config_manager.read(&axes[i].trap_traj_.config_) &&
config_manager.read(&axes[i].min_endstop_.config_) &&
config_manager.read(&axes[i].max_endstop_.config_) &&
config_manager.read(&axes[i].mechanical_brake_.config_) &&
config_manager.read(&motors[i].config_) &&
config_manager.read(&motors[i].fet_thermistor_.config_) &&
config_manager.read(&motors[i].motor_thermistor_.config_) &&
config_manager.read(&axes[i].config_);
}
return success;
}
static bool config_write_all() {
bool success = board_write_config() &&
config_manager.write(&odrv.config_) &&
config_manager.write(&odrv.can_.config_);
for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) {
success = config_manager.write(&encoders[i].config_) &&
config_manager.write(&axes[i].sensorless_estimator_.config_) &&
config_manager.write(&axes[i].controller_.config_) &&
config_manager.write(&axes[i].trap_traj_.config_) &&
config_manager.write(&axes[i].min_endstop_.config_) &&
config_manager.write(&axes[i].max_endstop_.config_) &&
config_manager.write(&axes[i].mechanical_brake_.config_) &&
config_manager.write(&motors[i].config_) &&
config_manager.write(&motors[i].fet_thermistor_.config_) &&
config_manager.write(&motors[i].motor_thermistor_.config_) &&
config_manager.write(&axes[i].config_);
}
return success;
}
static void config_clear_all() {
odrv.config_ = {};
odrv.can_.config_ = {};
for (size_t i = 0; i < AXIS_COUNT; ++i) {
encoders[i].config_ = {};
axes[i].sensorless_estimator_.config_ = {};
axes[i].controller_.config_ = {};
axes[i].controller_.config_.load_encoder_axis = i;
axes[i].trap_traj_.config_ = {};
axes[i].min_endstop_.config_ = {};
axes[i].max_endstop_.config_ = {};
axes[i].mechanical_brake_.config_ = {};
motors[i].config_ = {};
motors[i].fet_thermistor_.config_ = {};
motors[i].motor_thermistor_.config_ = {};
axes[i].clear_config();
}
}
static bool config_apply_all() {
bool success = odrv.can_.apply_config();
for (size_t i = 0; (i < AXIS_COUNT) && success; ++i) {
success = encoders[i].apply_config(motors[i].config_.motor_type)
&& axes[i].controller_.apply_config()
&& axes[i].min_endstop_.apply_config()
&& axes[i].max_endstop_.apply_config()
&& motors[i].apply_config()
&& motors[i].motor_thermistor_.apply_config()
&& axes[i].apply_config();
}
return success;
}
bool ODrive::save_configuration(void) {
bool success;
CRITICAL_SECTION() {
bool any_armed = std::any_of(axes.begin(), axes.end(),
[](auto& axis){ return axis.motor_.is_armed_; });
if (any_armed) {
return false;
}
size_t config_size = 0;
success = config_manager.prepare_store()
&& config_write_all()
&& config_manager.start_store(&config_size)
&& config_write_all()
&& config_manager.finish_store();
// FIXME: during save_configuration we might miss some interrupts
// because the CPU gets halted during a flash erase. Missing events
// (encoder updates, step/dir steps) is not good so to be sure we just
// reboot.
NVIC_SystemReset();
}
return success;
}
void ODrive::erase_configuration(void) {
NVM_erase();
// FIXME: this reboot is a workaround because we don't want the next save_configuration
// to write back the old configuration from RAM to NVM. The proper action would
// be to reset the values in RAM to default. However right now that's not
// practical because several startup actions depend on the config. The
// other problem is that the stack overflows if we reset to default here.
NVIC_SystemReset();
}
void ODrive::enter_dfu_mode() {
if ((hw_version_major_ == 3) && (hw_version_minor_ >= 5)) {
__asm volatile ("CPSID I\n\t":::"memory"); // disable interrupts
_reboot_cookie = 0xDEADBEEF;
NVIC_SystemReset();
} else {
/*
* DFU mode is only allowed on board version >= 3.5 because it can burn
* the brake resistor FETs on older boards.
* If you really want to use it on an older board, add 3.3k pull-down resistors
* to the AUX_L and AUX_H signals and _only then_ uncomment these lines.
*/
//__asm volatile ("CPSID I\n\t":::"memory"); // disable interrupts
//_reboot_cookie = 0xDEADFE75;
//NVIC_SystemReset();
}
}
bool ODrive::any_error() {
return error_ != ODrive::ERROR_NONE
|| std::any_of(axes.begin(), axes.end(), [](Axis& axis){
return axis.error_ != Axis::ERROR_NONE
|| axis.motor_.error_ != Motor::ERROR_NONE
|| axis.sensorless_estimator_.error_ != SensorlessEstimator::ERROR_NONE
|| axis.encoder_.error_ != Encoder::ERROR_NONE
|| axis.controller_.error_ != Controller::ERROR_NONE;
});
}
uint64_t ODrive::get_drv_fault() {
#if AXIS_COUNT == 1
return motors[0].gate_driver_.get_error();
#elif AXIS_COUNT == 2
return (uint64_t)motors[0].gate_driver_.get_error() | ((uint64_t)motors[1].gate_driver_.get_error() << 32ULL);
#else
#error "not supported"
#endif
}
void ODrive::clear_errors() {
for (auto& axis: axes) {
axis.motor_.error_ = Motor::ERROR_NONE;
axis.controller_.error_ = Controller::ERROR_NONE;
axis.sensorless_estimator_.error_ = SensorlessEstimator::ERROR_NONE;
axis.encoder_.error_ = Encoder::ERROR_NONE;
axis.encoder_.spi_error_rate_ = 0.0f;
axis.error_ = Axis::ERROR_NONE;
}
error_ = ERROR_NONE;
if (odrv.config_.enable_brake_resistor) {
safety_critical_arm_brake_resistor();
}
}
extern "C" {
void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed portCHAR *pcTaskName) {
for(auto& axis: axes){
axis.motor_.disarm();
}
safety_critical_disarm_brake_resistor();
for (;;); // TODO: safe action
}
void vApplicationIdleHook(void) {
if (odrv.system_stats_.fully_booted) {
odrv.system_stats_.uptime = xTaskGetTickCount();
odrv.system_stats_.min_heap_space = xPortGetMinimumEverFreeHeapSize();
uint32_t min_stack_space[AXIS_COUNT];
std::transform(axes.begin(), axes.end(), std::begin(min_stack_space), [](auto& axis) { return uxTaskGetStackHighWaterMark(axis.thread_id_) * sizeof(StackType_t); });
odrv.system_stats_.max_stack_usage_axis = axes[0].stack_size_ - *std::min_element(std::begin(min_stack_space), std::end(min_stack_space));
odrv.system_stats_.max_stack_usage_usb = stack_size_usb_thread - uxTaskGetStackHighWaterMark(usb_thread) * sizeof(StackType_t);
odrv.system_stats_.max_stack_usage_uart = stack_size_uart_thread - uxTaskGetStackHighWaterMark(uart_thread) * sizeof(StackType_t);
odrv.system_stats_.max_stack_usage_startup = stack_size_default_task - uxTaskGetStackHighWaterMark(defaultTaskHandle) * sizeof(StackType_t);
odrv.system_stats_.max_stack_usage_can = odrv.can_.stack_size_ - uxTaskGetStackHighWaterMark(odrv.can_.thread_id_) * sizeof(StackType_t);
odrv.system_stats_.max_stack_usage_analog = stack_size_analog_thread - uxTaskGetStackHighWaterMark(analog_thread) * sizeof(StackType_t);
odrv.system_stats_.stack_size_axis = axes[0].stack_size_;
odrv.system_stats_.stack_size_usb = stack_size_usb_thread;
odrv.system_stats_.stack_size_uart = stack_size_uart_thread;
odrv.system_stats_.stack_size_startup = stack_size_default_task;
odrv.system_stats_.stack_size_can = odrv.can_.stack_size_;
odrv.system_stats_.stack_size_analog = stack_size_analog_thread;
odrv.system_stats_.prio_axis = osThreadGetPriority(axes[0].thread_id_);
odrv.system_stats_.prio_usb = osThreadGetPriority(usb_thread);
odrv.system_stats_.prio_uart = osThreadGetPriority(uart_thread);
odrv.system_stats_.prio_startup = osThreadGetPriority(defaultTaskHandle);
odrv.system_stats_.prio_can = osThreadGetPriority(odrv.can_.thread_id_);
odrv.system_stats_.prio_analog = osThreadGetPriority(analog_thread);
status_led_controller.update();
}
}
}
/**
* @brief Runs system-level checks that need to be as real-time as possible.
*
* This function is called after every current measurement of every motor.
* It should finish as quickly as possible.
*/
void ODrive::do_fast_checks() {
if (!(vbus_voltage >= config_.dc_bus_undervoltage_trip_level))
disarm_with_error(ERROR_DC_BUS_UNDER_VOLTAGE);
if (!(vbus_voltage <= config_.dc_bus_overvoltage_trip_level))
disarm_with_error(ERROR_DC_BUS_OVER_VOLTAGE);
}
/**
* @brief Floats all power phases on the system (all motors and brake resistors).
*
* This should be called if a system level exception ocurred that makes it
* unsafe to run power through the system in general.
*/
void ODrive::disarm_with_error(Error error) {
CRITICAL_SECTION() {
for (auto& axis: axes) {
axis.motor_.disarm_with_error(Motor::ERROR_SYSTEM_LEVEL);
}
safety_critical_disarm_brake_resistor();
error_ |= error;
}
}
/**
* @brief Runs the periodic sampling tasks
*
* All components that need to sample real-world data should do it in this
* function as it runs on a high interrupt priority and provides lowest possible
* timing jitter.
*
* All function called from this function should adhere to the following rules:
* - Try to use the same number of CPU cycles in every iteration.
* (reason: Tasks that run later in the function still want lowest possible timing jitter)
* - Use as few cycles as possible.
* (reason: The interrupt blocks other important interrupts (TODO: which ones?))
* - Not call any FreeRTOS functions.
* (reason: The interrupt priority is higher than the max allowed priority for syscalls)
*
* Time consuming and undeterministic logic/arithmetic should live on
* control_loop_cb() instead.
*/
void ODrive::sampling_cb() {
n_evt_sampling_++;
MEASURE_TIME(task_times_.sampling) {
for (auto& axis: axes) {
axis.encoder_.sample_now();
}
}
}
/**
* @brief Runs the periodic control loop.
*
* This function is executed in a low priority interrupt context and is allowed
* to call CMSIS functions.
*
* Yet it runs at a higher priority than communication workloads.
*
* @param update_cnt: The true count of update events (wrapping around at 16
* bits). This is used for timestamp calculation in the face of
* potentially missed timer update interrupts. Therefore this counter
* must not rely on any interrupts.
*/
void ODrive::control_loop_cb(uint32_t timestamp) {
last_update_timestamp_ = timestamp;
n_evt_control_loop_++;
// TODO: use a configurable component list for most of the following things
MEASURE_TIME(task_times_.control_loop_misc) {
// Reset all output ports so that we are certain about the freshness of
// all values that we use.
// If we forget to reset a value here the worst that can happen is that
// this safety check doesn't work.
// TODO: maybe we should add a check to output ports that prevents
// double-setting the value.
for (auto& axis: axes) {
axis.acim_estimator_.slip_vel_.reset();
axis.acim_estimator_.stator_phase_vel_.reset();
axis.acim_estimator_.stator_phase_.reset();
axis.controller_.torque_output_.reset();
axis.encoder_.phase_.reset();
axis.encoder_.phase_vel_.reset();
axis.encoder_.pos_estimate_.reset();
axis.encoder_.vel_estimate_.reset();
axis.encoder_.pos_circular_.reset();
axis.motor_.Vdq_setpoint_.reset();
axis.motor_.Idq_setpoint_.reset();
axis.open_loop_controller_.Idq_setpoint_.reset();
axis.open_loop_controller_.Vdq_setpoint_.reset();
axis.open_loop_controller_.phase_.reset();
axis.open_loop_controller_.phase_vel_.reset();
axis.open_loop_controller_.total_distance_.reset();
axis.sensorless_estimator_.phase_.reset();
axis.sensorless_estimator_.phase_vel_.reset();
axis.sensorless_estimator_.vel_estimate_.reset();
}
uart_poll();
odrv.oscilloscope_.update();
}
for (auto& axis : axes) {
MEASURE_TIME(axis.task_times_.endstop_update) {
axis.min_endstop_.update();
axis.max_endstop_.update();
}
}
MEASURE_TIME(task_times_.control_loop_checks) {
for (auto& axis: axes) {
// look for errors at axis level and also all subcomponents
bool checks_ok = axis.do_checks(timestamp);
// make sure the watchdog is being fed.
bool watchdog_ok = axis.watchdog_check();
if (!checks_ok || !watchdog_ok) {
axis.motor_.disarm();
}
}
}
for (auto& axis: axes) {
// Sub-components should use set_error which will propegate to this error_
MEASURE_TIME(axis.task_times_.thermistor_update) {
axis.motor_.fet_thermistor_.update();
axis.motor_.motor_thermistor_.update();
}
MEASURE_TIME(axis.task_times_.encoder_update)
axis.encoder_.update();
}
// Controller of either axis might use the encoder estimate of the other
// axis so we process both encoders before we continue.
for (auto& axis: axes) {
MEASURE_TIME(axis.task_times_.sensorless_estimator_update)
axis.sensorless_estimator_.update();
MEASURE_TIME(axis.task_times_.controller_update) {
if (!axis.controller_.update()) { // uses position and velocity from encoder
axis.error_ |= Axis::ERROR_CONTROLLER_FAILED;
}
}
MEASURE_TIME(axis.task_times_.open_loop_controller_update)
axis.open_loop_controller_.update(timestamp);
MEASURE_TIME(axis.task_times_.motor_update)
axis.motor_.update(timestamp); // uses torque from controller and phase_vel from encoder
MEASURE_TIME(axis.task_times_.current_controller_update)
axis.motor_.current_control_.update(timestamp); // uses the output of controller_ or open_loop_contoller_ and encoder_ or sensorless_estimator_ or acim_estimator_
}
// Tell the axis threads that the control loop has finished
for (auto& axis: axes) {
if (axis.thread_id_) {
osSignalSet(axis.thread_id_, 0x0001);
}
}
get_gpio(odrv.config_.error_gpio_pin).write(odrv.any_error());
}
/** @brief For diagnostics only */
uint32_t ODrive::get_interrupt_status(int32_t irqn) {
if ((irqn < -14) || (irqn >= 240)) {
return 0xffffffff;
}
uint8_t priority = (irqn < -12)
? 0 // hard fault and NMI always have maximum priority
: NVIC_GetPriority((IRQn_Type)irqn);
uint32_t counter = GET_IRQ_COUNTER((IRQn_Type)irqn);
bool is_enabled = (irqn < 0)
? true // processor interrupt vectors are always enabled
: NVIC->ISER[(((uint32_t)(int32_t)irqn) >> 5UL)] & (uint32_t)(1UL << (((uint32_t)(int32_t)irqn) & 0x1FUL));
return priority | ((counter & 0x7ffffff) << 8) | (is_enabled ? 0x80000000 : 0);
}
/** @brief For diagnostics only */
uint32_t ODrive::get_dma_status(uint8_t stream_num) {
DMA_Stream_TypeDef* streams[] = {
DMA1_Stream0, DMA1_Stream1, DMA1_Stream2, DMA1_Stream3, DMA1_Stream4, DMA1_Stream5, DMA1_Stream6, DMA1_Stream7,
DMA2_Stream0, DMA2_Stream1, DMA2_Stream2, DMA2_Stream3, DMA2_Stream4, DMA2_Stream5, DMA2_Stream6, DMA2_Stream7
};
if (stream_num >= 16) {
return 0xffffffff;
}
DMA_Stream_TypeDef* stream = streams[stream_num];
bool is_reset = (stream->CR == 0x00000000)
&& (stream->NDTR == 0x00000000)
&& (stream->PAR == 0x00000000)
&& (stream->M0AR == 0x00000000)
&& (stream->M1AR == 0x00000000)
&& (stream->FCR == 0x00000021);
uint8_t channel = ((stream->CR & DMA_SxCR_CHSEL_Msk) >> DMA_SxCR_CHSEL_Pos);
uint8_t priority = ((stream->CR & DMA_SxCR_PL_Msk) >> DMA_SxCR_PL_Pos);
return (is_reset ? 0 : 0x80000000) | ((channel & 0x7) << 2) | (priority & 0x3);
}
uint32_t ODrive::get_gpio_states() {
// TODO: get values that were sampled synchronously with the control loop
uint32_t val = 0;
for (size_t i = 0; i < GPIO_COUNT; ++i) {
val |= ((gpios[i].read() ? 1UL : 0UL) << i);
}
return val;
}
/**
* @brief Main thread started from main().
*/
static void rtos_main(void*) {
// Init USB device
MX_USB_DEVICE_Init();
// Start ADC for temperature measurements and user measurements
start_general_purpose_adc();
//osDelay(100);
// Init communications (this requires the axis objects to be constructed)
init_communication();
// Start pwm-in compare modules
// must happen after communication is initialized
pwm0_input.init();
// Set up the CS pins for absolute encoders (TODO: move to GPIO init switch statement)
for(auto& axis : axes){
if(axis.encoder_.config_.mode & Encoder::MODE_FLAG_ABS){
axis.encoder_.abs_spi_cs_pin_init();
}
}
// Try to initialized gate drivers for fault-free startup.
// If this does not succeed, a fault will be raised and the idle loop will
// periodically attempt to reinit the gate driver.
for(auto& axis: axes){
axis.motor_.setup();
}
for(auto& axis: axes){
axis.encoder_.setup();
}
for(auto& axis: axes){
axis.acim_estimator_.idq_src_.connect_to(&axis.motor_.Idq_setpoint_);
}
// Start PWM and enable adc interrupts/callbacks
start_adc_pwm();
start_analog_thread();
// Wait for up to 2s for motor to become ready to allow for error-free
// startup. This delay gives the current sensor calibration time to
// converge. If the DRV chip is unpowered, the motor will not become ready
// but we still enter idle state.
for (size_t i = 0; i < 2000; ++i) {
bool motors_ready = std::all_of(axes.begin(), axes.end(), [](auto& axis) {
return axis.motor_.current_meas_.has_value();
});
if (motors_ready) {
break;
}
osDelay(1);
}
for (auto& axis: axes) {
axis.sensorless_estimator_.error_ &= ~SensorlessEstimator::ERROR_UNKNOWN_CURRENT_MEASUREMENT;
}
// Start state machine threads. Each thread will go through various calibration
// procedures and then run the actual controller loops.
// TODO: generalize for AXIS_COUNT != 2
for (size_t i = 0; i < AXIS_COUNT; ++i) {
axes[i].start_thread();
}
odrv.system_stats_.fully_booted = true;
// Main thread finished starting everything and can delete itself now (yes this is legal).
vTaskDelete(defaultTaskHandle);
}
/**
* @brief Carries out early startup tasks that need to run before any static
* initializers.
* This function gets called from the startup assembly code.
*/
extern "C" void early_start_checks(void) {
if(_reboot_cookie == 0xDEADFE75) {
/* The STM DFU bootloader enables internal pull-up resistors on PB10 (AUX_H)
* and PB11 (AUX_L), thereby causing shoot-through on the brake resistor
* FETs and obliterating them unless external 3.3k pull-down resistors are
* present. Pull-downs are only present on ODrive 3.5 or newer.
* On older boards we disable DFU by default but if the user insists
* there's only one thing left that might save it: time.
* The brake resistor gate driver needs a certain 10V supply (GVDD) to
* make it work. This voltage is supplied by the motor gate drivers which get
* disabled at system reset. So over time GVDD voltage _should_ below
* dangerous levels. This is completely handwavy and should not be relied on
* so you are on your own on if you ignore this warning.
*
* This loop takes 5 cycles per iteration and at this point the system runs
* on the internal 16MHz RC oscillator so the delay is about 2 seconds.
*/
for (size_t i = 0; i < (16000000UL / 5UL * 2UL); ++i) {
__NOP();
}
_reboot_cookie = 0xDEADBEEF;
}
/* We could jump to the bootloader directly on demand without rebooting
but that requires us to reset several peripherals and interrupts for it
to function correctly. Therefore it's easier to just reset the entire chip. */
if(_reboot_cookie == 0xDEADBEEF) {
_reboot_cookie = 0xCAFEFEED; //Reset bootloader trigger
__set_MSP((uintptr_t)&_estack);
// http://www.st.com/content/ccc/resource/technical/document/application_note/6a/17/92/02/58/98/45/0c/CD00264379.pdf/files/CD00264379.pdf
void (*builtin_bootloader)(void) = (void (*)(void))(*((uint32_t *)0x1FFF0004));
builtin_bootloader();
}
/* The bootloader might fail to properly clean up after itself,
so if we're not sure that the system is in a clean state we
just reset it again */
if(_reboot_cookie != 42) {
_reboot_cookie = 42;
NVIC_SystemReset();
}
}
/**
* @brief Main entry point called from assembly startup code.
*/
extern "C" int main(void) {
// This procedure of building a USB serial number should be identical
// to the way the STM's built-in USB bootloader does it. This means
// that the device will have the same serial number in normal and DFU mode.
uint32_t uuid0 = *(uint32_t *)(UID_BASE + 0);
uint32_t uuid1 = *(uint32_t *)(UID_BASE + 4);
uint32_t uuid2 = *(uint32_t *)(UID_BASE + 8);
uint32_t uuid_mixed_part = uuid0 + uuid2;
serial_number = ((uint64_t)uuid_mixed_part << 16) | (uint64_t)(uuid1 >> 16);
uint64_t val = serial_number;
for (size_t i = 0; i < 12; ++i) {
serial_number_str[i] = "0123456789ABCDEF"[(val >> (48-4)) & 0xf];
val <<= 4;
}
serial_number_str[12] = 0;
// Init low level system functions (clocks, flash interface)
system_init();
// Load configuration from NVM. This needs to happen after system_init()
// since the flash interface must be initialized and before board_init()
// since board initialization can depend on the config.
size_t config_size = 0;
bool success = config_manager.start_load()
&& config_read_all()
&& config_manager.finish_load(&config_size)
&& config_apply_all();
if (success) {
odrv.user_config_loaded_ = config_size;
} else {
config_clear_all();
config_apply_all();
}
odrv.misconfigured_ = odrv.misconfigured_
|| (odrv.config_.enable_uart_a && !uart_a)
|| (odrv.config_.enable_uart_b && !uart_b)
|| (odrv.config_.enable_uart_c && !uart_c);
// Init board-specific peripherals
if (!board_init()) {
for (;;); // TODO: handle properly
}
// Init GPIOs according to their configured mode
for (size_t i = 0; i < GPIO_COUNT; ++i) {
// Skip unavailable GPIOs
if (!get_gpio(i)) {
continue;
}
ODriveIntf::GpioMode mode = odrv.config_.gpio_modes[i];
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = get_gpio(i).pin_mask_;
// Set Alternate Function setting for this GPIO mode
if (mode == ODriveIntf::GPIO_MODE_DIGITAL ||
mode == ODriveIntf::GPIO_MODE_DIGITAL_PULL_UP ||
mode == ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN ||
mode == ODriveIntf::GPIO_MODE_MECH_BRAKE ||
mode == ODriveIntf::GPIO_MODE_STATUS ||
mode == ODriveIntf::GPIO_MODE_ANALOG_IN) {
GPIO_InitStruct.Alternate = 0;
} else {
auto it = std::find_if(
alternate_functions[i].begin(), alternate_functions[i].end(),
[mode](auto a) { return a.mode == mode; });
if (it == alternate_functions[i].end()) {
odrv.misconfigured_ = true; // this GPIO doesn't support the selected mode
continue;
}
GPIO_InitStruct.Alternate = it->alternate_function;
}
switch (mode) {
case ODriveIntf::GPIO_MODE_DIGITAL: {
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} break;
case ODriveIntf::GPIO_MODE_DIGITAL_PULL_UP: {
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} break;
case ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN: {
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} break;
case ODriveIntf::GPIO_MODE_ANALOG_IN: {
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
} break;
case ODriveIntf::GPIO_MODE_UART_A: {
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;
if (!odrv.config_.enable_uart_a) {
odrv.misconfigured_ = true;
}
} break;
case ODriveIntf::GPIO_MODE_UART_B: {
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;
if (!odrv.config_.enable_uart_b) {
odrv.misconfigured_ = true;
}
} break;
case ODriveIntf::GPIO_MODE_UART_C: {
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;
if (!odrv.config_.enable_uart_c) {
odrv.misconfigured_ = true;
}
} break;
case ODriveIntf::GPIO_MODE_CAN_A: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
if (!odrv.config_.enable_can_a) {
odrv.misconfigured_ = true;
}
} break;
case ODriveIntf::GPIO_MODE_I2C_A: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
if (!odrv.config_.enable_i2c_a) {
odrv.misconfigured_ = true;
}
} break;
//case ODriveIntf::GPIO_MODE_SPI_A: { // TODO
//} break;
case ODriveIntf::GPIO_MODE_PWM: {
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} 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;
} 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;
} 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;
} break;
case ODriveIntf::GPIO_MODE_MECH_BRAKE: {
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} break;
case ODriveIntf::GPIO_MODE_STATUS: {
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} break;
default: {
odrv.misconfigured_ = true;
continue;
}
}
HAL_GPIO_Init(get_gpio(i).port_, &GPIO_InitStruct);
}
// Init usb irq binary semaphore, and start with no tokens by removing the starting one.
osSemaphoreDef(sem_usb_irq);
sem_usb_irq = osSemaphoreCreate(osSemaphore(sem_usb_irq), 1);
osSemaphoreWait(sem_usb_irq, 0);
// Create an event queue for UART
osMessageQDef(uart_event_queue, 4, uint32_t);
uart_event_queue = osMessageCreate(osMessageQ(uart_event_queue), NULL);
// Create an event queue for USB
osMessageQDef(usb_event_queue, 7, uint32_t);
usb_event_queue = osMessageCreate(osMessageQ(usb_event_queue), NULL);
osSemaphoreDef(sem_can);
sem_can = osSemaphoreCreate(osSemaphore(sem_can), 1);
osSemaphoreWait(sem_can, 0);
// Create main thread
osThreadDef(defaultTask, rtos_main, osPriorityNormal, 0, stack_size_default_task / sizeof(StackType_t));
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
// Start scheduler
osKernelStart();
for (;;);
}
@@ -0,0 +1,13 @@
#include <odrive_main.h>
void MechanicalBrake::engage() {
if (odrv.config_.gpio_modes[config_.gpio_num] == ODriveIntf::GPIO_MODE_MECH_BRAKE){
get_gpio(config_.gpio_num).write(config_.is_active_low ? 0 : 1);
}
}
void MechanicalBrake::release() {
if (odrv.config_.gpio_modes[config_.gpio_num] == ODriveIntf::GPIO_MODE_MECH_BRAKE){
get_gpio(config_.gpio_num).write(config_.is_active_low ? 1 : 0);
}
}
@@ -0,0 +1,25 @@
#ifndef __MECHANICAL_BRAKE_HPP
#define __MECHANICAL_BRAKE_HPP
#include <autogen/interfaces.hpp>
class MechanicalBrake : public ODriveIntf::MechanicalBrakeIntf {
public:
struct Config_t {
uint16_t gpio_num = 0;
bool is_active_low = true;
// custom setters
MechanicalBrake* parent = nullptr;
void set_gpio_num(uint16_t value) { gpio_num = value; }
};
MechanicalBrake() {}
MechanicalBrake::Config_t config_;
Axis* axis_ = nullptr;
void release();
void engage();
};
#endif // __MECHANICAL_BRAKE_HPP
@@ -0,0 +1,733 @@
#include "motor.hpp"
#include "axis.hpp"
#include "low_level.h"
#include "odrive_main.h"
#include <algorithm>
static constexpr auto CURRENT_ADC_LOWER_BOUND = (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MIN_VOLT / 3.3f);
static constexpr auto CURRENT_ADC_UPPER_BOUND = (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MAX_VOLT / 3.3f);
/**
* @brief This control law adjusts the output voltage such that a predefined
* current is tracked. A hardcoded integrator gain is used for this.
*
* TODO: this might as well be implemented using the FieldOrientedController.
*/
struct ResistanceMeasurementControlLaw : AlphaBetaFrameController {
void reset() final {
test_voltage_ = 0.0f;
test_mod_ = std::nullopt;
}
ODriveIntf::MotorIntf::Error on_measurement(
std::optional<float> vbus_voltage,
std::optional<float2D> Ialpha_beta,
uint32_t input_timestamp) final {
if (Ialpha_beta.has_value()) {
actual_current_ = Ialpha_beta->first;
test_voltage_ += (kI * current_meas_period) * (target_current_ - actual_current_);
I_beta_ += (kIBetaFilt * current_meas_period) * (Ialpha_beta->second - I_beta_);
} else {
actual_current_ = 0.0f;
test_voltage_ = 0.0f;
}
if (std::abs(test_voltage_) > max_voltage_) {
test_voltage_ = NAN;
return Motor::ERROR_PHASE_RESISTANCE_OUT_OF_RANGE;
} else if (!vbus_voltage.has_value()) {
return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE;
} else {
float vfactor = 1.0f / ((2.0f / 3.0f) * *vbus_voltage);
test_mod_ = test_voltage_ * vfactor;
return Motor::ERROR_NONE;
}
}
ODriveIntf::MotorIntf::Error get_alpha_beta_output(
uint32_t output_timestamp,
std::optional<float2D>* mod_alpha_beta,
std::optional<float>* ibus) final {
if (!test_mod_.has_value()) {
return Motor::ERROR_CONTROLLER_INITIALIZING;
} else {
*mod_alpha_beta = {*test_mod_, 0.0f};
*ibus = *test_mod_ * actual_current_;
return Motor::ERROR_NONE;
}
}
float get_resistance() {
return test_voltage_ / target_current_;
}
float get_Ibeta() {
return I_beta_;
}
const float kI = 1.0f; // [(V/s)/A]
const float kIBetaFilt = 80.0f;
float max_voltage_ = 0.0f;
float actual_current_ = 0.0f;
float target_current_ = 0.0f;
float test_voltage_ = 0.0f;
float I_beta_ = 0.0f; // [A] low pass filtered Ibeta response
std::optional<float> test_mod_ = NAN;
};
/**
* @brief This control law toggles rapidly between positive and negative output
* voltage. By measuring how large the current ripples are, the phase inductance
* can be determined.
*
* TODO: this method assumes a certain synchronization between current measurement and output application
*/
struct InductanceMeasurementControlLaw : AlphaBetaFrameController {
void reset() final {
attached_ = false;
}
ODriveIntf::MotorIntf::Error on_measurement(
std::optional<float> vbus_voltage,
std::optional<float2D> Ialpha_beta,
uint32_t input_timestamp) final
{
if (!Ialpha_beta.has_value()) {
return {Motor::ERROR_UNKNOWN_CURRENT_MEASUREMENT};
}
float Ialpha = Ialpha_beta->first;
if (attached_) {
float sign = test_voltage_ >= 0.0f ? 1.0f : -1.0f;
deltaI_ += -sign * (Ialpha - last_Ialpha_);
} else {
start_timestamp_ = input_timestamp;
attached_ = true;
}
last_Ialpha_ = Ialpha;
last_input_timestamp_ = input_timestamp;
return Motor::ERROR_NONE;
}
ODriveIntf::MotorIntf::Error get_alpha_beta_output(
uint32_t output_timestamp, std::optional<float2D>* mod_alpha_beta,
std::optional<float>* ibus) final
{
test_voltage_ *= -1.0f;
float vfactor = 1.0f / ((2.0f / 3.0f) * vbus_voltage);
*mod_alpha_beta = {test_voltage_ * vfactor, 0.0f};
*ibus = 0.0f;
return Motor::ERROR_NONE;
}
float get_inductance() {
// Note: A more correct formula would also take into account that there is a finite timestep.
// However, the discretisation in the current control loop inverts the same discrepancy
float dt = (float)(last_input_timestamp_ - start_timestamp_) / (float)TIM_1_8_CLOCK_HZ; // at 216MHz this overflows after 19 seconds
return std::abs(test_voltage_) / (deltaI_ / dt);
}
// Config
float test_voltage_ = 0.0f;
// State
bool attached_ = false;
float sign_ = 0;
// Outputs
uint32_t start_timestamp_ = 0;
float last_Ialpha_ = NAN;
uint32_t last_input_timestamp_ = 0;
float deltaI_ = 0.0f;
};
Motor::Motor(TIM_HandleTypeDef* timer,
uint8_t current_sensor_mask,
float shunt_conductance,
TGateDriver& gate_driver,
TOpAmp& opamp,
OnboardThermistorCurrentLimiter& fet_thermistor,
OffboardThermistorCurrentLimiter& motor_thermistor) :
timer_(timer),
current_sensor_mask_(current_sensor_mask),
shunt_conductance_(shunt_conductance),
gate_driver_(gate_driver),
opamp_(opamp),
fet_thermistor_(fet_thermistor),
motor_thermistor_(motor_thermistor) {
apply_config();
fet_thermistor_.motor_ = this;
motor_thermistor_.motor_ = this;
}
/**
* @brief Arms the PWM outputs that belong to this motor.
*
* Note that this does not activate the PWM outputs immediately, it just sets
* a flag so they will be enabled later.
*
* The sequence goes like this:
* - Motor::arm() sets the is_armed_ flag.
* - On the next timer update event Motor::timer_update_cb() gets called in an
* interrupt context
* - Motor::timer_update_cb() runs specified control law to determine PWM values
* - Motor::timer_update_cb() calls Motor::apply_pwm_timings()
* - Motor::apply_pwm_timings() sets the output compare registers and the AOE
* (automatic output enable) bit.
* - On the next update event the timer latches the configured values into the
* active shadow register and enables the outputs at the same time.
*
* The sequence can be aborted at any time by calling Motor::disarm().
*
* @param control_law: An control law that is called at the frequency of current
* measurements. The function must return as quickly as possible
* such that the resulting PWM timings are available before the next
* timer update event.
* @returns: True on success, false otherwise
*/
bool Motor::arm(PhaseControlLaw<3>* control_law) {
axis_->mechanical_brake_.release();
CRITICAL_SECTION() {
control_law_ = control_law;
// Reset controller states, integrators, setpoints, etc.
axis_->controller_.reset();
axis_->acim_estimator_.rotor_flux_ = 0.0f;
if (control_law_) {
control_law_->reset();
}
if (!odrv.config_.enable_brake_resistor || brake_resistor_armed) {
armed_state_ = 1;
is_armed_ = true;
} else {
error_ |= Motor::ERROR_BRAKE_RESISTOR_DISARMED;
}
}
return true;
}
/**
* @brief Updates the phase PWM timings unless the motor is disarmed.
*
* If the motor is armed, the PWM timings come into effect at the next update
* event (and are enabled if they weren't already), unless the motor is disarmed
* prior to that.
*
* @param tentative: If true, the update is not counted as "refresh".
*/
void Motor::apply_pwm_timings(uint16_t timings[3], bool tentative) {
CRITICAL_SECTION() {
if (odrv.config_.enable_brake_resistor && !brake_resistor_armed) {
disarm_with_error(ERROR_BRAKE_RESISTOR_DISARMED);
}
TIM_HandleTypeDef* htim = timer_;
TIM_TypeDef* tim = htim->Instance;
tim->CCR1 = timings[0];
tim->CCR2 = timings[1];
tim->CCR3 = timings[2];
if (!tentative) {
if (is_armed_) {
// Set the Automatic Output Enable so that the Master Output Enable
// bit will be automatically enabled on the next update event.
tim->BDTR |= TIM_BDTR_AOE;
}
}
// If a timer update event occurred just now while we were updating the
// timings, we can't be sure what values the shadow registers now contain,
// so we must disarm the motor.
// (this also protects against the case where the update interrupt has too
// low priority, but that should not happen)
//if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE)) {
// disarm_with_error(ERROR_CONTROL_DEADLINE_MISSED);
//}
}
}
/**
* @brief Disarms the motor PWM.
*
* After this function returns, it is guaranteed that all three
* motor phases are floating and will not be enabled again until
* arm() is called.
*/
bool Motor::disarm(bool* p_was_armed) {
bool was_armed;
CRITICAL_SECTION() {
was_armed = is_armed_;
if (is_armed_) {
gate_driver_.set_enabled(false);
}
is_armed_ = false;
armed_state_ = 0;
TIM_HandleTypeDef* timer = timer_;
timer->Instance->BDTR &= ~TIM_BDTR_AOE; // prevent the PWMs from automatically enabling at the next update
__HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(timer);
control_law_ = nullptr;
}
// Check necessary to prevent infinite recursion
if (was_armed) {
update_brake_current();
}
if (p_was_armed) {
*p_was_armed = was_armed;
}
return true;
}
// @brief Tune the current controller based on phase resistance and inductance
// This should be invoked whenever one of these values changes.
// TODO: allow update on user-request or update automatically via hooks
void Motor::update_current_controller_gains() {
// Calculate current control gains
float p_gain = config_.current_control_bandwidth * config_.phase_inductance;
float plant_pole = config_.phase_resistance / config_.phase_inductance;
current_control_.pi_gains_ = {p_gain, plant_pole * p_gain};
}
bool Motor::apply_config() {
config_.parent = this;
is_calibrated_ = config_.pre_calibrated;
update_current_controller_gains();
return true;
}
// @brief Set up the gate drivers
bool Motor::setup() {
fet_thermistor_.update();
motor_thermistor_.update();
// Solve for exact gain, then snap down to have equal or larger range as requested
// or largest possible range otherwise
constexpr float kMargin = 0.90f;
constexpr float max_output_swing = 1.35f; // [V] out of amplifier
float max_unity_gain_current = kMargin * max_output_swing * shunt_conductance_; // [A]
float requested_gain = max_unity_gain_current / config_.requested_current_range; // [V/V]
float actual_gain;
if (!gate_driver_.config(requested_gain, &actual_gain))
return false;
// Values for current controller
phase_current_rev_gain_ = 1.0f / actual_gain;
// Clip all current control to actual usable range
max_allowed_current_ = max_unity_gain_current * phase_current_rev_gain_;
max_dc_calib_ = 0.1f * max_allowed_current_;
if (!gate_driver_.init())
return false;
return true;
}
void Motor::disarm_with_error(Motor::Error error){
error_ |= error;
axis_->error_ |= Axis::ERROR_MOTOR_FAILED;
last_error_time_ = odrv.n_evt_control_loop_ * current_meas_period;
disarm();
}
bool Motor::do_checks(uint32_t timestamp) {
gate_driver_.do_checks();
if (!gate_driver_.is_ready()) {
disarm_with_error(ERROR_DRV_FAULT);
return false;
}
if (!motor_thermistor_.do_checks()) {
disarm_with_error(ERROR_MOTOR_THERMISTOR_OVER_TEMP);
return false;
}
if (!fet_thermistor_.do_checks()) {
disarm_with_error(ERROR_FET_THERMISTOR_OVER_TEMP);
return false;
}
return true;
}
float Motor::effective_current_lim() {
// Configured limit
float current_lim = config_.current_lim;
// Hardware limit
if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) {
current_lim = std::min(current_lim, 0.98f*one_by_sqrt3*vbus_voltage); //gimbal motor is voltage control
} else {
current_lim = std::min(current_lim, axis_->motor_.max_allowed_current_);
}
// Apply thermistor current limiters
current_lim = std::min(current_lim, motor_thermistor_.get_current_limit(config_.current_lim));
current_lim = std::min(current_lim, fet_thermistor_.get_current_limit(config_.current_lim));
effective_current_lim_ = current_lim;
return effective_current_lim_;
}
//return the maximum available torque for the motor.
//Note - for ACIM motors, available torque is allowed to be 0.
float Motor::max_available_torque() {
if (config_.motor_type == Motor::MOTOR_TYPE_ACIM) {
float max_torque = effective_current_lim_ * config_.torque_constant * axis_->acim_estimator_.rotor_flux_;
max_torque = std::clamp(max_torque, 0.0f, config_.torque_lim);
return max_torque;
} else {
float max_torque = effective_current_lim_ * config_.torque_constant;
max_torque = std::clamp(max_torque, 0.0f, config_.torque_lim);
return max_torque;
}
}
std::optional<float> Motor::phase_current_from_adcval(uint32_t ADCValue) {
// Make sure the measurements don't come too close to the current sensor's hardware limitations
if (ADCValue < CURRENT_ADC_LOWER_BOUND || ADCValue > CURRENT_ADC_UPPER_BOUND) {
error_ |= ERROR_CURRENT_SENSE_SATURATION;
return std::nullopt;
}
int adcval_bal = (int)ADCValue - (1 << 11);
float amp_out_volt = (3.3f / (float)(1 << 12)) * (float)adcval_bal;
float shunt_volt = amp_out_volt * phase_current_rev_gain_;
float current = shunt_volt * shunt_conductance_;
return current;
}
//--------------------------------
// Measurement and calibration
//--------------------------------
// TODO check Ibeta balance to verify good motor connection
bool Motor::measure_phase_resistance(float test_current, float max_voltage) {
ResistanceMeasurementControlLaw control_law;
control_law.target_current_ = test_current;
control_law.max_voltage_ = max_voltage;
arm(&control_law);
for (size_t i = 0; i < 3000; ++i) {
if (!((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_)) {
break;
}
osDelay(1);
}
bool success = is_armed_;
//// De-energize motor
//if (!enqueue_voltage_timings(motor, 0.0f, 0.0f))
// return false; // error set inside enqueue_voltage_timings
disarm();
config_.phase_resistance = control_law.get_resistance();
if (is_nan(config_.phase_resistance)) {
// TODO: the motor is already disarmed at this stage. This is an error
// that only pretains to the measurement and its result so it should
// just be a return value of this function.
disarm_with_error(ERROR_PHASE_RESISTANCE_OUT_OF_RANGE);
success = false;
}
float I_beta = control_law.get_Ibeta();
if (is_nan(I_beta) || (abs(I_beta) / test_current) > 0.2f) {
disarm_with_error(ERROR_UNBALANCED_PHASES);
success = false;
}
return success;
}
bool Motor::measure_phase_inductance(float test_voltage) {
InductanceMeasurementControlLaw control_law;
control_law.test_voltage_ = test_voltage;
arm(&control_law);
for (size_t i = 0; i < 1250; ++i) {
if (!((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_)) {
break;
}
osDelay(1);
}
bool success = is_armed_;
//// De-energize motor
//if (!enqueue_voltage_timings(motor, 0.0f, 0.0f))
// return false; // error set inside enqueue_voltage_timings
disarm();
config_.phase_inductance = control_law.get_inductance();
// TODO arbitrary values set for now
if (!(config_.phase_inductance >= 2e-6f && config_.phase_inductance <= 4000e-6f)) {
error_ |= ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE;
success = false;
}
return success;
}
// TODO: motor calibration should only be a utility function that's called from
// the UI on explicit user request. It should take its parameters as input
// arguments and return the measured results without modifying any config values.
bool Motor::run_calibration() {
float R_calib_max_voltage = config_.resistance_calib_max_voltage;
if (config_.motor_type == MOTOR_TYPE_HIGH_CURRENT
|| config_.motor_type == MOTOR_TYPE_ACIM) {
if (!measure_phase_resistance(config_.calibration_current, R_calib_max_voltage))
return false;
if (!measure_phase_inductance(R_calib_max_voltage))
return false;
} else if (config_.motor_type == MOTOR_TYPE_GIMBAL) {
// no calibration needed
} else {
return false;
}
update_current_controller_gains();
is_calibrated_ = true;
return true;
}
void Motor::update(uint32_t timestamp) {
// Load torque setpoint, convert to motor direction
std::optional<float> maybe_torque = torque_setpoint_src_.present();
if (!maybe_torque.has_value()) {
error_ |= ERROR_UNKNOWN_TORQUE;
return;
}
float torque = direction_ * *maybe_torque;
// Load setpoints from previous iteration.
auto [id, iq] = Idq_setpoint_.previous()
.value_or(float2D{0.0f, 0.0f});
// Load effective current limit
float ilim = axis_->motor_.effective_current_lim_;
// Autoflux tracks old Iq (that may be 2-norm clamped last cycle) to make sure we are chasing a feasable current.
if ((axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) && config_.acim_autoflux_enable) {
float abs_iq = std::abs(iq);
float gain = abs_iq > id ? config_.acim_autoflux_attack_gain : config_.acim_autoflux_decay_gain;
id += gain * (abs_iq - id) * current_meas_period;
id = std::clamp(id, config_.acim_autoflux_min_Id, 0.9f * ilim); // 10% space reserved for Iq
} else {
id = std::clamp(id, -ilim*0.99f, ilim*0.99f); // 1% space reserved for Iq to avoid numerical issues
}
// Convert requested torque to current
if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) {
iq = torque / (axis_->motor_.config_.torque_constant * std::max(axis_->acim_estimator_.rotor_flux_, config_.acim_gain_min_flux));
} else {
iq = torque / axis_->motor_.config_.torque_constant;
}
// 2-norm clamping where Id takes priority
float iq_lim_sqr = SQ(ilim) - SQ(id);
float Iq_lim = (iq_lim_sqr <= 0.0f) ? 0.0f : sqrt(iq_lim_sqr);
iq = std::clamp(iq, -Iq_lim, Iq_lim);
if (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL) {
Idq_setpoint_ = {id, iq};
}
// This update call is in bit a weird position because it depends on the
// Id,q setpoint but outputs the phase velocity that we depend on later
// in this function.
// A cleaner fix would be to take the feedforward calculation out of here
// and turn it into a separate component.
MEASURE_TIME(axis_->task_times_.acim_estimator_update)
axis_->acim_estimator_.update(timestamp);
float vd = 0.0f;
float vq = 0.0f;
std::optional<float> phase_vel = phase_vel_src_.present();
if (config_.R_wL_FF_enable) {
if (!phase_vel.has_value()) {
error_ |= ERROR_UNKNOWN_PHASE_VEL;
return;
}
vd -= *phase_vel * config_.phase_inductance * iq;
vq += *phase_vel * config_.phase_inductance * id;
vd += config_.phase_resistance * id;
vq += config_.phase_resistance * iq;
}
if (config_.bEMF_FF_enable) {
if (!phase_vel.has_value()) {
error_ |= ERROR_UNKNOWN_PHASE_VEL;
return;
}
vq += *phase_vel * (2.0f/3.0f) * (config_.torque_constant / config_.pole_pairs);
}
if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) {
// reinterpret current as voltage
Vdq_setpoint_ = {vd + id, vq + iq};
} else {
Vdq_setpoint_ = {vd, vq};
}
}
/**
* @brief Called when the underlying hardware timer triggers an update event.
*/
void Motor::current_meas_cb(uint32_t timestamp, std::optional<Iph_ABC_t> current) {
// TODO: this is platform specific
//const float current_meas_period = static_cast<float>(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) / TIM_1_8_CLOCK_HZ;
TaskTimerContext tmr{axis_->task_times_.current_sense};
n_evt_current_measurement_++;
bool dc_calib_valid = (dc_calib_running_since_ >= config_.dc_calib_tau * 7.5f)
&& (abs(DC_calib_.phA) < max_dc_calib_)
&& (abs(DC_calib_.phB) < max_dc_calib_)
&& (abs(DC_calib_.phC) < max_dc_calib_);
if (armed_state_ == 1 || armed_state_ == 2) {
current_meas_ = {0.0f, 0.0f, 0.0f};
armed_state_ += 1;
} else if (current.has_value() && dc_calib_valid) {
current_meas_ = {
current->phA - DC_calib_.phA,
current->phB - DC_calib_.phB,
current->phC - DC_calib_.phC
};
} else {
current_meas_ = std::nullopt;
}
// Run system-level checks (e.g. overvoltage/undervoltage condition)
// The motor might be disarmed in this function. In this case the
// handler will continue to run until the end but it won't have an
// effect on the PWM.
odrv.do_fast_checks();
if (current_meas_.has_value()) {
// Check for violation of current limit
// If Ia + Ib + Ic == 0 holds then we have:
// Inorm^2 = Id^2 + Iq^2 = Ialpha^2 + Ibeta^2 = 2/3 * (Ia^2 + Ib^2 + Ic^2)
float Itrip = effective_current_lim_ + config_.current_lim_margin;
float Inorm_sq = 2.0f / 3.0f * (SQ(current_meas_->phA)
+ SQ(current_meas_->phB)
+ SQ(current_meas_->phC));
// Hack: we disable the current check during motor calibration because
// it tends to briefly overshoot when the motor moves to align flux with I_alpha
if (Inorm_sq > SQ(Itrip)) {
disarm_with_error(ERROR_CURRENT_LIMIT_VIOLATION);
}
} else if (is_armed_) {
// Since we can't check current limits, be safe for now and disarm.
// Theoretically we could continue to operate if there is no active
// current limit.
disarm_with_error(ERROR_UNKNOWN_CURRENT_MEASUREMENT);
}
if (control_law_) {
Error err = control_law_->on_measurement(vbus_voltage,
current_meas_.has_value() ?
std::make_optional(std::array<float, 3>{current_meas_->phA, current_meas_->phB, current_meas_->phC})
: std::nullopt,
timestamp);
if (err != ERROR_NONE) {
disarm_with_error(err);
}
}
}
/**
* @brief Called when the underlying hardware timer triggers an update event.
*/
void Motor::dc_calib_cb(uint32_t timestamp, std::optional<Iph_ABC_t> current) {
const float dc_calib_period = static_cast<float>(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) / TIM_1_8_CLOCK_HZ;
TaskTimerContext tmr{axis_->task_times_.dc_calib};
if (current.has_value()) {
const float calib_filter_k = std::min(dc_calib_period / config_.dc_calib_tau, 1.0f);
DC_calib_.phA += (current->phA - DC_calib_.phA) * calib_filter_k;
DC_calib_.phB += (current->phB - DC_calib_.phB) * calib_filter_k;
DC_calib_.phC += (current->phC - DC_calib_.phC) * calib_filter_k;
dc_calib_running_since_ += dc_calib_period;
} else {
DC_calib_.phA = 0.0f;
DC_calib_.phB = 0.0f;
DC_calib_.phC = 0.0f;
dc_calib_running_since_ = 0.0f;
}
}
void Motor::pwm_update_cb(uint32_t output_timestamp) {
TaskTimerContext tmr{axis_->task_times_.pwm_update};
n_evt_pwm_update_++;
Error control_law_status = ERROR_CONTROLLER_FAILED;
float pwm_timings[3] = {NAN, NAN, NAN};
std::optional<float> i_bus;
if (control_law_) {
control_law_status = control_law_->get_output(
output_timestamp, pwm_timings, &i_bus);
}
// Apply control law to calculate PWM duty cycles
if (is_armed_ && control_law_status == ERROR_NONE) {
uint16_t next_timings[] = {
(uint16_t)(pwm_timings[0] * (float)TIM_1_8_PERIOD_CLOCKS),
(uint16_t)(pwm_timings[1] * (float)TIM_1_8_PERIOD_CLOCKS),
(uint16_t)(pwm_timings[2] * (float)TIM_1_8_PERIOD_CLOCKS)
};
apply_pwm_timings(next_timings, false);
} else if (is_armed_) {
if (!(timer_->Instance->BDTR & TIM_BDTR_MOE) && (control_law_status == ERROR_CONTROLLER_INITIALIZING)) {
// If the PWM output is armed in software but not yet in
// hardware we tolerate the "initializing" error.
i_bus = 0.0f;
} else {
disarm_with_error(control_law_status);
}
}
if (!is_armed_) {
// If something above failed, reset I_bus to 0A.
i_bus = 0.0f;
} else if (is_armed_ && !i_bus.has_value()) {
// If the motor is armed then i_bus must be known
disarm_with_error(ERROR_UNKNOWN_CURRENT_MEASUREMENT);
i_bus = 0.0f;
}
I_bus_ = *i_bus;
if (*i_bus < config_.I_bus_hard_min || *i_bus > config_.I_bus_hard_max) {
disarm_with_error(ERROR_I_BUS_OUT_OF_RANGE);
}
update_brake_current();
}
@@ -0,0 +1,139 @@
#ifndef __MOTOR_HPP
#define __MOTOR_HPP
class Axis; // declared in axis.hpp
class Motor;
#include <board.h>
#include <autogen/interfaces.hpp>
#include "foc.hpp"
class Motor : public ODriveIntf::MotorIntf {
public:
// NOTE: for gimbal motors, all units of Nm are instead V.
// example: vel_gain is [V/(turn/s)] instead of [Nm/(turn/s)]
// example: current_lim and calibration_current will instead determine the maximum voltage applied to the motor.
struct Config_t {
bool pre_calibrated = false; // can be set to true to indicate that all values here are valid
int32_t pole_pairs = 7;
float calibration_current = 10.0f; // [A]
float resistance_calib_max_voltage = 2.0f; // [V] - You may need to increase this if this voltage isn't sufficient to drive calibration_current through the motor.
float phase_inductance = 0.0f; // to be set by measure_phase_inductance
float phase_resistance = 0.0f; // to be set by measure_phase_resistance
float torque_constant = 0.04f; // [Nm/A] for PM motors, [Nm/A^2] for induction motors. Equal to 8.27/Kv of the motor
MotorType motor_type = MOTOR_TYPE_HIGH_CURRENT;
// Read out max_allowed_current to see max supported value for current_lim.
// float current_lim = 70.0f; //[A]
float current_lim = 10.0f; //[A]
float current_lim_margin = 8.0f; // Maximum violation of current_lim
float torque_lim = std::numeric_limits<float>::infinity(); //[Nm].
// Value used to compute shunt amplifier gains
float requested_current_range = 60.0f; // [A]
float current_control_bandwidth = 1000.0f; // [rad/s]
float inverter_temp_limit_lower = 100;
float inverter_temp_limit_upper = 120;
float acim_gain_min_flux = 10; // [A]
float acim_autoflux_min_Id = 10; // [A]
bool acim_autoflux_enable = false;
float acim_autoflux_attack_gain = 10.0f;
float acim_autoflux_decay_gain = 1.0f;
bool R_wL_FF_enable = false; // Enable feedforwards for R*I and w*L*I terms
bool bEMF_FF_enable = false; // Enable feedforward for bEMF
float I_bus_hard_min = -INFINITY;
float I_bus_hard_max = INFINITY;
float I_leak_max = 0.1f;
float dc_calib_tau = 0.2f;
// custom property setters
Motor* parent = nullptr;
void set_pre_calibrated(bool value) {
pre_calibrated = value;
parent->is_calibrated_ = parent->is_calibrated_ || parent->config_.pre_calibrated;
}
void set_phase_inductance(float value) { phase_inductance = value; parent->update_current_controller_gains(); }
void set_phase_resistance(float value) { phase_resistance = value; parent->update_current_controller_gains(); }
void set_current_control_bandwidth(float value) { current_control_bandwidth = value; parent->update_current_controller_gains(); }
};
Motor(TIM_HandleTypeDef* timer,
uint8_t current_sensor_mask,
float shunt_conductance,
TGateDriver& gate_driver,
TOpAmp& opamp,
OnboardThermistorCurrentLimiter& fet_thermistor,
OffboardThermistorCurrentLimiter& motor_thermistor);
bool arm(PhaseControlLaw<3>* control_law);
void apply_pwm_timings(uint16_t timings[3], bool tentative);
bool disarm(bool* was_armed = nullptr);
bool apply_config();
bool setup();
void update_current_controller_gains();
void disarm_with_error(Error error);
bool do_checks(uint32_t timestamp);
float effective_current_lim();
float max_available_torque();
std::optional<float> phase_current_from_adcval(uint32_t ADCValue);
bool measure_phase_resistance(float test_current, float max_voltage);
bool measure_phase_inductance(float test_voltage);
bool run_calibration();
void update(uint32_t timestamp);
// These functions are called as appropriate from the board.cpp file.
void current_meas_cb(uint32_t timestamp, std::optional<Iph_ABC_t> current);
void dc_calib_cb(uint32_t timestamp, std::optional<Iph_ABC_t> current);
void pwm_update_cb(uint32_t output_timestamp);
// hardware config
TIM_HandleTypeDef* const timer_;
const uint8_t current_sensor_mask_;
const float shunt_conductance_;
TGateDriver& gate_driver_;
TOpAmp& opamp_;
OnboardThermistorCurrentLimiter& fet_thermistor_;
OffboardThermistorCurrentLimiter& motor_thermistor_;
Config_t config_;
Axis* axis_ = nullptr; // set by Axis constructor
//private:
uint32_t n_evt_current_measurement_ = 0;
uint32_t n_evt_pwm_update_ = 0;
// variables exposed on protocol
Error error_ = ERROR_NONE;
float last_error_time_ = 0.0f;
// Do not write to this variable directly!
// It is for exclusive use by the safety_critical_... functions.
bool is_armed_ = false;
uint8_t armed_state_ = 0;
bool is_calibrated_ = false; // Set in apply_config()
std::optional<Iph_ABC_t> current_meas_;
Iph_ABC_t DC_calib_ = {0.0f, 0.0f, 0.0f};
float dc_calib_running_since_ = 0.0f; // current sensor calibration needs some time to settle
float I_bus_ = 0.0f; // this motors contribution to the bus current
float phase_current_rev_gain_ = 0.0f; // Reverse gain for ADC to Amps (to be set by DRV8301_setup)
FieldOrientedController current_control_;
float effective_current_lim_ = 10.0f; // [A]
float max_allowed_current_ = 0.0f; // [A] set in setup()
float max_dc_calib_ = 0.0f; // [A] set in setup()
InputPort<float> torque_setpoint_src_; // Usually points to the Controller object's output
InputPort<float> phase_vel_src_; // Usually points to the Encoder object's output
float direction_ = 0.0f; // if -1 then positive torque is converted to negative Iq
OutputPort<float2D> Vdq_setpoint_ = {{0.0f, 0.0f}}; // fed to the FOC
OutputPort<float2D> Idq_setpoint_ = {{0.0f, 0.0f}}; // fed to the FOC
PhaseControlLaw<3>* control_law_;
};
#endif // __MOTOR_HPP
@@ -0,0 +1,192 @@
/*
* Convenience functions to load and store multiple objects from and to NVM.
*
* The NVM stores consecutive one-to-one copies of arbitrary objects.
* The types of these objects are passed as template arguments to Config<Ts...>.
*/
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdlib.h>
#include <Drivers/STM32/stm32_nvm.h>
#include <fibre/../../crc.hpp>
/* Private defines -----------------------------------------------------------*/
#define CONFIG_CRC16_INIT 0xabcd
#define CONFIG_CRC16_POLYNOMIAL 0x3d65
/* Private macros ------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Global constant data ------------------------------------------------------*/
/* Global variables ----------------------------------------------------------*/
/* Private constant data -----------------------------------------------------*/
// IMPORTANT: if you change, reorder or otherwise modify any of the fields in
// the config structs without changing its total length, make sure to increment this number:
static constexpr uint16_t config_version = 0x0001;
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Function implementations --------------------------------------------------*/
/**
* @brief Manages configuration load and store operations from and to NVM
*
* Usage:
* 1. start_load()
* 2. read() (as often needed)
* 3. finish_load() (to see if all reads were successful and the CRC in the end is valid)
*
* 1. prepare_store()
* 2. write() (as often as needed)
* 3. start_store()
* 4. write() (same sequence as before)
* 5. finish_store()
*
* The two store passes are required in order to measure the size on the first
* pass. If the size increases between the first and second pass, finish_store()
* will return an error.
*/
class ConfigManager {
public:
/**
* @brief Starts a load operation. This can be called at any time, even half
* way through a previous load operation.
*/
bool start_load() {
if (NVM_init() != 0) {
return (load_state = kLoadStateFailed), false;
}
load_offset = 0;
load_crc16 = CONFIG_CRC16_INIT ^ config_version;
load_state = kLoadStateInProgress;
return true;
}
/**
* @brief Loads the next chunk from NVM.
* Note that this may return true even if invalid data was read. The user
* will know the final verdict by the return value of finish_load().
*/
template<typename T>
bool read(T* val) {
if (load_state != 1) {
return (load_state = kLoadStateFailed), false;
}
size_t size = sizeof(T);
if (NVM_read(load_offset, (uint8_t *)val, size) != 0)
return (load_state = kLoadStateFailed), false;
load_crc16 = calc_crc16<CONFIG_CRC16_POLYNOMIAL>(load_crc16, (uint8_t *)val, size);
load_offset += size;
return true;
}
/**
* @brief Checks the final state of the load operation.
* If this function returns false, it is possible that previous read()
* operations actually returned garbage.
*/
bool finish_load(size_t* occupied_size) {
if (occupied_size) {
*occupied_size = load_offset + 2;
}
uint16_t crc16_calculated = load_crc16;
uint16_t crc16_loaded;
if (!read(&crc16_loaded)) {
return (load_state = kLoadStateFailed), false;
}
bool result = (load_state == 1) && (crc16_loaded == crc16_calculated);
load_state = kLoadStateIdle;
return result;
}
/**
* @brief Starts preparation of a new store operation.
*/
bool prepare_store() {
if (store_state != kStoreStateIdle) {
// it might be possible to restart the store process from other states but let's be safe
return (store_state = kStoreStateFailed), false;
}
store_offset = 0;
store_crc16 = CONFIG_CRC16_INIT ^ config_version;
store_state = kStoreStatePreparing;
return true;
}
template<typename T>
bool write(T* val) {
if (store_state == kStoreStateInProgress) {
if (NVM_write(store_offset, (uint8_t*)val, sizeof(T)) != 0) {
return (store_state = kStoreStateFailed), false;
}
} else if (store_state != kStoreStatePreparing) {
return (store_state = kStoreStateFailed), false;
}
store_crc16 = calc_crc16<CONFIG_CRC16_POLYNOMIAL>(store_crc16, (uint8_t *)val, sizeof(T));
store_offset += sizeof(T);
return true;
}
/**
* @brief Finishes the prepare pass and starts the actual store pass.
*/
bool start_store(size_t* occupied_size) {
if (occupied_size) {
*occupied_size = store_offset + 2;
}
if (store_state != kStoreStatePreparing) {
return (store_state = kStoreStateFailed), false;
}
store_offset += 2; // account for CRC16
if (store_offset > NVM_get_max_write_length()) {
return (store_state = kStoreStateFailed), false;
}
if (NVM_start_write(store_offset) != 0) {
return (store_state = kStoreStateFailed), false;
}
store_offset = 0;
store_crc16 = CONFIG_CRC16_INIT ^ config_version;
store_state = kStoreStateInProgress;
return true;
}
/**
* @brief Commits the store operation.
* If this function succeeds, the new configuration was successfully saved.
* If this function fails, the old configuration was not touched.
*/
bool finish_store() {
uint16_t crc16 = store_crc16;
if (!write(&crc16)) {
return (store_state = kStoreStateFailed), false;
}
if (NVM_commit() != 0) {
return (store_state = kStoreStateFailed), false;
}
store_state = kStoreStateIdle;
return true;
}
enum {
kLoadStateIdle = 0,
kLoadStateInProgress = 1,
kLoadStateFailed = 2
} load_state = kLoadStateIdle;
size_t load_offset;
size_t load_crc16;
enum {
kStoreStateIdle = 0,
kStoreStatePreparing = 1,
kStoreStateInProgress = 2,
kStoreStateFailed = 3
} store_state = kStoreStateIdle;
size_t store_offset;
size_t store_crc16;
};
@@ -0,0 +1,253 @@
#ifndef __ODRIVE_MAIN_H
#define __ODRIVE_MAIN_H
// Hardware configuration
#include <board.h>
#ifdef __cplusplus
#include <communication/interface_usb.h>
#include <communication/interface_i2c.h>
#include <communication/interface_uart.h>
#include <task_timer.hpp>
extern "C" {
#endif
// OS includes
#include <cmsis_os.h>
// extern const float elec_rad_per_enc;
extern uint32_t _reboot_cookie;
extern uint64_t serial_number;
extern char serial_number_str[13];
#ifdef __cplusplus
}
typedef struct {
bool fully_booted;
uint32_t uptime; // [ms]
uint32_t min_heap_space; // FreeRTOS heap [Bytes]
uint32_t max_stack_usage_axis; // minimum remaining space since startup [Bytes]
uint32_t max_stack_usage_usb;
uint32_t max_stack_usage_uart;
uint32_t max_stack_usage_startup;
uint32_t max_stack_usage_can;
uint32_t max_stack_usage_analog;
uint32_t stack_size_axis;
uint32_t stack_size_usb;
uint32_t stack_size_uart;
uint32_t stack_size_startup;
uint32_t stack_size_can;
uint32_t stack_size_analog;
int32_t prio_axis;
int32_t prio_usb;
int32_t prio_uart;
int32_t prio_startup;
int32_t prio_can;
int32_t prio_analog;
USBStats_t& usb = usb_stats_;
I2CStats_t& i2c = i2c_stats_;
} SystemStats_t;
struct PWMMapping_t {
endpoint_ref_t endpoint = {0, 0};
float min = 0;
float max = 0;
};
// @brief general user configurable board configuration
struct BoardConfig_t {
ODriveIntf::GpioMode gpio_modes[GPIO_COUNT] = {
DEFAULT_GPIO_MODES
};
bool enable_uart_a = true;
bool enable_uart_b = false;
bool enable_uart_c = false;
uint32_t uart_a_baudrate = 115200;
uint32_t uart_b_baudrate = 115200;
uint32_t uart_c_baudrate = 115200;
bool enable_can_a = true;
bool enable_i2c_a = false;
ODriveIntf::StreamProtocolType uart0_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT;
ODriveIntf::StreamProtocolType uart1_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT;
ODriveIntf::StreamProtocolType uart2_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT;
ODriveIntf::StreamProtocolType usb_cdc_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT;
float max_regen_current = 0.0f;
float brake_resistance = DEFAULT_BRAKE_RESISTANCE;
bool enable_brake_resistor = false;
float dc_bus_undervoltage_trip_level = DEFAULT_MIN_DC_VOLTAGE; //<! [V] minimum voltage below which the motor stops operating
float dc_bus_overvoltage_trip_level = 1.07f * HW_VERSION_VOLTAGE; //<! [V] maximum voltage above which the motor stops operating.
//<! This protects against cases in which the power supply fails to dissipate
//<! the brake power if the brake resistor is disabled.
//<! The default is 26V for the 24V board version and 52V for the 48V board version.
/**
* If enabled, if the measured DC voltage exceeds `dc_bus_overvoltage_ramp_start`,
* the ODrive will sink more power than usual into the the brake resistor
* in an attempt to bring the voltage down again.
*
* The brake duty cycle is increased by the following amount:
* vbus_voltage == dc_bus_overvoltage_ramp_start => brake_duty_cycle += 0%
* vbus_voltage == dc_bus_overvoltage_ramp_end => brake_duty_cycle += 100%
*
* Remarks:
* - This feature is active even when all motors are disarmed.
* - This feature is disabled if `brake_resistance` is non-positive.
*/
bool enable_dc_bus_overvoltage_ramp = false;
float dc_bus_overvoltage_ramp_start = 1.07f * HW_VERSION_VOLTAGE; //!< See `enable_dc_bus_overvoltage_ramp`.
//!< Do not set this lower than your usual vbus_voltage,
//!< unless you like fried brake resistors.
float dc_bus_overvoltage_ramp_end = 1.07f * HW_VERSION_VOLTAGE; //!< See `enable_dc_bus_overvoltage_ramp`.
//!< Must be larger than `dc_bus_overvoltage_ramp_start`,
//!< otherwise the ramp feature is disabled.
float dc_max_positive_current = INFINITY; // Max current [A] the power supply can source
float dc_max_negative_current = -0.01f; // Max current [A] the power supply can sink. You most likely want a non-positive value here. Set to -INFINITY to disable.
uint32_t error_gpio_pin = DEFAULT_ERROR_PIN;
PWMMapping_t pwm_mappings[4];
PWMMapping_t analog_mappings[GPIO_COUNT];
};
struct TaskTimes {
TaskTimer sampling;
TaskTimer control_loop_misc;
TaskTimer control_loop_checks;
TaskTimer dc_calib_wait;
};
// Forward Declarations
class Axis;
class Motor;
// TODO: move
// this is technically not thread-safe but practically it might be
#define DEFINE_ENUM_FLAG_OPERATORS(ENUMTYPE) \
inline ENUMTYPE operator | (ENUMTYPE a, ENUMTYPE b) { return static_cast<ENUMTYPE>(static_cast<std::underlying_type_t<ENUMTYPE>>(a) | static_cast<std::underlying_type_t<ENUMTYPE>>(b)); } \
inline ENUMTYPE operator & (ENUMTYPE a, ENUMTYPE b) { return static_cast<ENUMTYPE>(static_cast<std::underlying_type_t<ENUMTYPE>>(a) & static_cast<std::underlying_type_t<ENUMTYPE>>(b)); } \
inline ENUMTYPE operator ^ (ENUMTYPE a, ENUMTYPE b) { return static_cast<ENUMTYPE>(static_cast<std::underlying_type_t<ENUMTYPE>>(a) ^ static_cast<std::underlying_type_t<ENUMTYPE>>(b)); } \
inline ENUMTYPE &operator |= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast<ENUMTYPE&>(reinterpret_cast<std::underlying_type_t<ENUMTYPE>&>(a) |= static_cast<std::underlying_type_t<ENUMTYPE>>(b)); } \
inline ENUMTYPE &operator &= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast<ENUMTYPE&>(reinterpret_cast<std::underlying_type_t<ENUMTYPE>&>(a) &= static_cast<std::underlying_type_t<ENUMTYPE>>(b)); } \
inline ENUMTYPE &operator ^= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast<ENUMTYPE&>(reinterpret_cast<std::underlying_type_t<ENUMTYPE>&>(a) ^= static_cast<std::underlying_type_t<ENUMTYPE>>(b)); } \
inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast<ENUMTYPE>(~static_cast<std::underlying_type_t<ENUMTYPE>>(a)); }
#include "autogen/interfaces.hpp"
// ODrive specific includes
#include <utils.hpp>
#include <low_level.h>
#include <encoder.hpp>
#include <sensorless_estimator.hpp>
#include <controller.hpp>
#include <current_limiter.hpp>
#include <thermistor.hpp>
#include <trapTraj.hpp>
#include <endstop.hpp>
#include <mechanical_brake.hpp>
#include <axis.hpp>
#include <oscilloscope.hpp>
#include <communication/communication.h>
#include <communication/can/odrive_can.hpp>
// Defined in autogen/version.c based on git-derived version numbers
extern "C" {
extern const unsigned char fw_version_major_;
extern const unsigned char fw_version_minor_;
extern const unsigned char fw_version_revision_;
extern const unsigned char fw_version_unreleased_;
}
static Stm32Gpio get_gpio(size_t gpio_num) {
return (gpio_num < GPIO_COUNT) ? gpios[gpio_num] : GPIO_COUNT ? gpios[0] : Stm32Gpio::none;
}
// general system functions defined in main.cpp
class ODrive : public ODriveIntf {
public:
bool save_configuration() override;
void erase_configuration() override;
void reboot() override { NVIC_SystemReset(); }
void enter_dfu_mode() override;
bool any_error();
void clear_errors() override;
float get_adc_voltage(uint32_t gpio) override {
return ::get_adc_voltage(get_gpio(gpio));
}
int32_t test_function(int32_t delta) override {
static int cnt = 0;
return cnt += delta;
}
void do_fast_checks();
void sampling_cb();
void control_loop_cb(uint32_t timestamp);
Axis& get_axis(int num) { return axes[num]; }
uint32_t get_interrupt_status(int32_t irqn);
uint32_t get_dma_status(uint8_t stream_num);
uint32_t get_gpio_states();
uint64_t get_drv_fault();
void disarm_with_error(Error error);
Error error_ = ERROR_NONE;
float& vbus_voltage_ = ::vbus_voltage; // TODO: make this the actual variable
float& ibus_ = ::ibus_; // TODO: make this the actual variable
float ibus_report_filter_k_ = 1.0f;
const uint64_t& serial_number_ = ::serial_number;
// Hardware version is compared with OTP on startup to ensure that we're
// running on the right board version.
const uint8_t hw_version_major_ = HW_VERSION_MAJOR;
const uint8_t hw_version_minor_ = HW_VERSION_MINOR;
const uint8_t hw_version_variant_ = HW_VERSION_VOLTAGE;
// the corresponding macros are defined in the autogenerated version.h
const uint8_t fw_version_major_ = ::fw_version_major_;
const uint8_t fw_version_minor_ = ::fw_version_minor_;
const uint8_t fw_version_revision_ = ::fw_version_revision_;
const uint8_t fw_version_unreleased_ = ::fw_version_unreleased_; // 0 for official releases, 1 otherwise
bool& brake_resistor_armed_ = ::brake_resistor_armed; // TODO: make this the actual variable
bool& brake_resistor_saturated_ = ::brake_resistor_saturated; // TODO: make this the actual variable
float& brake_resistor_current_ = ::brake_resistor_current;
SystemStats_t system_stats_;
// Edit these to suit your capture needs
Oscilloscope oscilloscope_{
nullptr, // trigger_src
0.5f, // trigger_threshold
nullptr // data_src TODO: change data type
};
ODriveCAN can_;
BoardConfig_t config_;
uint32_t user_config_loaded_ = 0;
bool misconfigured_ = false;
uint32_t test_property_ = 0;
uint32_t last_update_timestamp_ = 0;
uint32_t n_evt_sampling_ = 0;
uint32_t n_evt_control_loop_ = 0;
bool task_timers_armed_ = false;
TaskTimes task_times_;
const bool otp_valid_ = ((uint8_t*)FLASH_OTP_BASE)[0] != 0xff;
};
extern ODrive odrv; // defined in main.cpp
#endif // __cplusplus
#endif /* __ODRIVE_MAIN_H */
@@ -0,0 +1,30 @@
#include "open_loop_controller.hpp"
#include <board.h>
void OpenLoopController::update(uint32_t timestamp) {
auto [prev_Id, prev_Iq] = Idq_setpoint_.previous().value_or(float2D{0.0f, 0.0f});
auto [prev_Vd, prev_Vq] = Vdq_setpoint_.previous().value_or(float2D{0.0f, 0.0f});
float phase = phase_.previous().value_or(initial_phase_);
float phase_vel = phase_vel_.previous().value_or(0.0f);
(void)prev_Iq; // unused
(void)prev_Vq; // unused
float dt = (float)(timestamp - timestamp_) / (float)TIM_1_8_CLOCK_HZ;
Idq_setpoint_ = {
std::clamp(target_current_, prev_Id - max_current_ramp_ * dt, prev_Id + max_current_ramp_ * dt),
0.0f
};
Vdq_setpoint_ = {
std::clamp(target_voltage_, prev_Vd - max_voltage_ramp_ * dt, prev_Vd + max_voltage_ramp_ * dt),
0.0f
};
phase_vel = std::clamp(target_vel_, phase_vel - max_phase_vel_ramp_ * dt, phase_vel + max_phase_vel_ramp_ * dt);
phase_vel_ = phase_vel;
phase_ = wrap_pm_pi(phase + phase_vel * dt);
total_distance_ = total_distance_.previous().value_or(0.0f) + phase_vel * dt;
timestamp_ = timestamp;
}
@@ -0,0 +1,32 @@
#ifndef __OPEN_LOOP_CONTROLLER_HPP
#define __OPEN_LOOP_CONTROLLER_HPP
#include "component.hpp"
#include <cmath>
#include <autogen/interfaces.hpp>
class OpenLoopController : public ComponentBase {
public:
void update(uint32_t timestamp) final;
// Config
float max_current_ramp_ = INFINITY; // [A/s]
float max_voltage_ramp_ = INFINITY; // [V/s]
float max_phase_vel_ramp_ = INFINITY; // [rad/s^2]
// Inputs
float target_vel_ = 0.0f;
float target_current_ = 0.0f;
float target_voltage_ = 0.0f;
float initial_phase_ = 0.0f;
// State/Outputs
uint32_t timestamp_ = 0;
OutputPort<float2D> Idq_setpoint_ = {{0.0f, 0.0f}};
OutputPort<float2D> Vdq_setpoint_ = {{0.0f, 0.0f}};
OutputPort<float> phase_ = 0.0f;
OutputPort<float> phase_vel_ = 0.0f;
OutputPort<float> total_distance_ = 0.0f;
};
#endif // __OPEN_LOOP_CONTROLLER_HPP
@@ -0,0 +1,27 @@
#include "oscilloscope.hpp"
// if you use the oscilloscope feature you can bump up this value
#define OSCILLOSCOPE_SIZE 4096
void Oscilloscope::update() {
float trigger_data = trigger_src_ ? *trigger_src_ : 0.0f;
float trigger_threshold = trigger_threshold_;
float sample_data = data_src_ ? **data_src_ : 0.0f;
if (trigger_data < trigger_threshold) {
ready_ = true;
}
if (ready_ && trigger_data >= trigger_threshold) {
capturing_ = true;
ready_ = false;
}
if (capturing_) {
if (pos_ < OSCILLOSCOPE_SIZE) {
data_[pos_++] = sample_data;
} else {
pos_ = 0;
capturing_ = false;
}
}
}
@@ -0,0 +1,31 @@
#ifndef __OSCILLOSCOPE_HPP
#define __OSCILLOSCOPE_HPP
#include <autogen/interfaces.hpp>
// if you use the oscilloscope feature you can bump up this value
#define OSCILLOSCOPE_SIZE 4096
class Oscilloscope : public ODriveIntf::OscilloscopeIntf {
public:
Oscilloscope(float* trigger_src, float trigger_threshold, float** data_src)
: trigger_src_(trigger_src), trigger_threshold_(trigger_threshold), data_src_(data_src) {}
float get_val(uint32_t index) override {
return index < OSCILLOSCOPE_SIZE ? data_[index] : NAN;
}
void update();
const uint32_t size_ = OSCILLOSCOPE_SIZE;
const float* trigger_src_;
const float trigger_threshold_;
float* const * data_src_;
float data_[OSCILLOSCOPE_SIZE] = {0};
size_t pos_ = 0;
bool ready_ = false;
bool capturing_ = false;
};
#endif // __OSCILLOSCOPE_HPP
@@ -0,0 +1,97 @@
#ifndef __PHASE_CONTROL_LAW_HPP
#define __PHASE_CONTROL_LAW_HPP
#include <autogen/interfaces.hpp>
#include <variant>
template<size_t N_PHASES>
class PhaseControlLaw {
public:
/**
* @brief Called when this controller becomes the active controller.
*/
virtual void reset() = 0;
/**
* @brief Informs the control law about a new set of measurements.
*
* This function gets called in a high priority interrupt context and should
* run fast.
*
* Beware that all inputs can be NAN.
*
* @param vbus_voltage: The most recently measured DC link voltage. Can be
* std::nullopt if the measurement is not available or valid for any
* reason.
* @param currents: The most recently measured (or inferred) phase currents
* in Amps. Can be std::nullopt if no valid measurements are available
* (e.g. because the opamp isn't started or because the sensors were
* saturated).
* @param input_timestamp: The timestamp (in HCLK ticks) corresponding to
* the vbus_voltage and current measurement.
*/
virtual ODriveIntf::MotorIntf::Error on_measurement(
std::optional<float> vbus_voltage,
std::optional<std::array<float, N_PHASES>> currents,
uint32_t input_timestamp) = 0;
/**
* @brief Shall calculate the PWM timings for the specified target time.
*
* This function gets called in a high priority interrupt context and should
* run fast.
*
* Beware that this function can be called before a call to on_measurement().
*
* @param output_timestamp: The timestamp (in HCLK ticks) corresponding to
* the middle of the time span during which the output will be
* active.
* @param pwm_timings: This array referenced by this argument shall be
* filled with the desired PWM timings. Each item corresponds to one
* phase and must lie in [0.0f, 1.0f].
* The function is not required to return valid PWM timings in case
* of an error.
* @param ibus: The variable pointed to by this argument is set to the
* estimated DC current around the output timestamp when the desired
* PWM timings get applied.
* The function is not required to return a valid I_bus estimate in
* case of an error.
*
* @returns: An error code or ERROR_NONE. If the function returns an error
* the motor gets disarmed with one exception: If the controller
* never returned valid PWM timings since it became active then it
* is allowed to return ERROR_CONTROLLER_INITIALIZING without
* triggering a motor disarm. In this phase the PWMs will not yet
* be truly active.
*/
virtual ODriveIntf::MotorIntf::Error get_output(
uint32_t output_timestamp,
float (&pwm_timings)[N_PHASES],
std::optional<float>* ibus) = 0;
};
class AlphaBetaFrameController : public PhaseControlLaw<3> {
private:
ODriveIntf::MotorIntf::Error on_measurement(
std::optional<float> vbus_voltage,
std::optional<std::array<float, 3>> currents,
uint32_t input_timestamp) final;
ODriveIntf::MotorIntf::Error get_output(
uint32_t output_timestamp,
float (&pwm_timings)[3],
std::optional<float>* ibus) final;
protected:
virtual ODriveIntf::MotorIntf::Error on_measurement(
std::optional<float> vbus_voltage,
std::optional<float2D> Ialpha_beta,
uint32_t input_timestamp) = 0;
virtual ODriveIntf::MotorIntf::Error get_alpha_beta_output(
uint32_t output_timestamp,
std::optional<float2D>* mod_alpha_beta,
std::optional<float>* ibus) = 0;
};
#endif // __PHASE_CONTROL_LAW_HPP

Some files were not shown because too many files have changed in this diff Show More