*
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
BasedOnStyle: Google
|
||||
AllowShortCaseLabelsOnASingleLine: 'true'
|
||||
IndentWidth: '4'
|
||||
ColumnLimit: '0'
|
||||
...
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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>© 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>© 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(¤t0, ¤t1)) {
|
||||
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(¤t0, ¤t1)) {
|
||||
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>© 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, §or_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)§or->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)§or->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(§ors[0], sectors[0].n_data,
|
||||
ERASED, §or0_state);
|
||||
sectors[1].index = scan_allocation_table(§ors[1], sectors[1].n_data,
|
||||
ERASED, §or1_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 = §ors[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(§ors[0]);
|
||||
if (sector1_state != VALID)
|
||||
status |= erase(§ors[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(§ors[0]);
|
||||
state |= erase(§ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[read_sector_];
|
||||
sector_t *write_sector = §ors[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;
|
||||
@@ -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.
|
||||
@@ -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
Reference in New Issue
Block a user