From 030a57ed2b4a491991ca60a1dd23f4d946c93d4f Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 14:59:06 +0200 Subject: [PATCH 01/22] Start board support for yardforce 500B --- .gitignore | 3 +- stm32/ros_usbnode/.gitignore | 5 +- .../boards/genericSTM32F401VC.json | 47 +++++++++++++++++++ stm32/ros_usbnode/platformio.ini | 20 +++++++- 4 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 stm32/ros_usbnode/boards/genericSTM32F401VC.json diff --git a/.gitignore b/.gitignore index 0f59212a..83be7cc0 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store -sync_msg.sh \ No newline at end of file +sync_msg.sh +.idea/ diff --git a/stm32/ros_usbnode/.gitignore b/stm32/ros_usbnode/.gitignore index 81aeaf39..ae66dcaf 100755 --- a/stm32/ros_usbnode/.gitignore +++ b/stm32/ros_usbnode/.gitignore @@ -7,4 +7,7 @@ __pycache__ *.o raspi_remote_upload.py platformio.ini -*.bin \ No newline at end of file +*.bin +CMakeLists.txt +CMakeListsPrivate.txt +cmake-build* diff --git a/stm32/ros_usbnode/boards/genericSTM32F401VC.json b/stm32/ros_usbnode/boards/genericSTM32F401VC.json new file mode 100644 index 00000000..766fee9b --- /dev/null +++ b/stm32/ros_usbnode/boards/genericSTM32F401VC.json @@ -0,0 +1,47 @@ +{ + "build": { + "core": "stm32", + "cpu": "cortex-m4", + "extra_flags": "-DSTM32F4xx -DSTM32F401xC -DSTM32F401VC", + "f_cpu": "84000000L", + "mcu": "stm32f401vct6", + "product_line": "STM32F401xx" + }, + "connectivity": [ + "can", + "ethernet" + ], + "debug": { + "default_tools": [ + "stlink" + ], + "jlink_device": "STM32F401VC", + "onboard_tools": [ + "stlink" + ], + "openocd_target": "stm32f4x", + "svd_path": "STM32F40x.svd" + }, + "frameworks": [ + "stm32cube" + ], + "name": "STM32F401VC (48k RAM, 256k Flash)", + "upload": { + "disable_flushing": false, + "maximum_ram_size": 49152, + "maximum_size": 262144, + "protocol": "stlink", + "protocols": [ + "jlink", + "stlink", + "blackmagic", + "mbed", + "dfu" + ], + "require_upload_port": true, + "use_1200bps_touch": false, + "wait_for_upload_port": false + }, + "url": "https://www.st.com/en/microcontrollers-microprocessors/stm32f401vc.html", + "vendor": "Generic" +} \ No newline at end of file diff --git a/stm32/ros_usbnode/platformio.ini b/stm32/ros_usbnode/platformio.ini index da65b55b..7a980378 100644 --- a/stm32/ros_usbnode/platformio.ini +++ b/stm32/ros_usbnode/platformio.ini @@ -14,9 +14,9 @@ default_envs = Yardforce500 [env] platform = ststm32 framework = stm32cube -board = genericSTM32F103VC [env:Yardforce500] +board = genericSTM32F103VC platform_packages = toolchain-gccarmnoneeabi@~1.90301.0 platformio/tool-stm32duino@^1.0.1 @@ -31,7 +31,25 @@ debug_tool = stlink monitor_speed = 115200 monitor_port = /dev/ttyAMA0 +[env:Yardforce500B] +board = genericSTM32F401VC +platform_packages = + toolchain-gccarmnoneeabi@~1.90301.0 + platformio/tool-stm32duino@^1.0.1 + platformio/tool-openocd@^2.1100.211028 + platformio/tool-dfuutil@^1.11.0 +build_flags = -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom +build_src_filter = +extra_scripts = + pre:patch_usb.py + pre:add_swo_viewer.py +debug_tool = stlink +monitor_speed = 115200 +monitor_port = /dev/ttyAMA0 + [env:Yardforce500_REMOTE_UPLOAD] +board = genericSTM32F103VC +>>>>>>> 5125415 (Start board support for yardforce 500B) build_flags = -DBOARD_YARDFORCE500 -Wl,--undefined,_printf_float -Isrc/ros/ros_lib -Isrc/ros/ros_custom extra_scripts = raspi_remote_upload.py custom_mowgli_host = 10.146.111.222 From 54f42f147676273467da3144840eb71d9fbc01b2 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 15:20:52 +0200 Subject: [PATCH 02/22] Replace board specific includes with generic one --- stm32/ros_usbnode/include/board.h | 28 ++++++++++++++----- stm32/ros_usbnode/include/imu/imu.h | 2 +- stm32/ros_usbnode/include/imu/wt901.h | 2 +- stm32/ros_usbnode/include/main.h | 2 +- stm32/ros_usbnode/include/panel.h | 2 +- stm32/ros_usbnode/include/stm32f_board_hal.h | 22 +++++++++++++++ stm32/ros_usbnode/include/usb_device.h | 3 +- stm32/ros_usbnode/include/usbd_conf.h | 3 +- stm32/ros_usbnode/platformio.ini | 7 ++--- stm32/ros_usbnode/src/blademotor.c | 3 +- stm32/ros_usbnode/src/drivemotor.c | 4 +-- stm32/ros_usbnode/src/emergency.c | 6 ++-- stm32/ros_usbnode/src/i2c.c | 2 +- stm32/ros_usbnode/src/main.c | 4 +-- stm32/ros_usbnode/src/panel.c | 3 +- .../src/ros/ros_custom/cpp_main.cpp | 2 +- stm32/ros_usbnode/src/ros/ros_custom/nbt.cpp | 2 +- stm32/ros_usbnode/src/ros/ros_custom/nbt.h | 2 +- .../src/ros/ros_custom/ringbuffer.cpp | 2 +- .../src/ros/ros_lib/STM32Hardware.h | 2 +- stm32/ros_usbnode/src/soft_i2c.c | 2 +- stm32/ros_usbnode/src/ultrasonic_sensor.c | 3 +- stm32/ros_usbnode/src/usbd_conf.c | 3 +- 23 files changed, 69 insertions(+), 42 deletions(-) create mode 100644 stm32/ros_usbnode/include/stm32f_board_hal.h diff --git a/stm32/ros_usbnode/include/board.h b/stm32/ros_usbnode/include/board.h index 2c8df1e4..9b23bf01 100755 --- a/stm32/ros_usbnode/include/board.h +++ b/stm32/ros_usbnode/include/board.h @@ -18,7 +18,6 @@ extern "C" * BOARD SELECTION * the specific board setting are set a the end of this file ********************************************************************************/ - #define BOARD_YARDFORCE500 1 //#define BOARD_LUV1000RI 1 /* definition type don't modify */ @@ -35,7 +34,8 @@ extern "C" #define PANEL_TYPE_YARDFORCE_LUV1000RI 2 #define PANEL_TYPE_YARDFORCE_900_ECO 3 -#if defined(BOARD_YARDFORCE500) +#if BOARD_YARDFORCE500_VARIANT_ORIG +#define VALID_BOARD_DEFINED 1 #define PANEL_TYPE PANEL_TYPE_YARDFORCE_500_CLASSIC #define BLADEMOTOR_LENGTH_RECEIVED_MSG 16 #define DEBUG_TYPE DEBUG_TYPE_UART @@ -47,7 +47,21 @@ extern "C" #define OPTION_ULTRASONIC 0 #define OPTION_BUMPER 0 -#elif defined(BOARD_LUV1000RI) +#elif BOARD_YARDFORCE500_VARIANT_B +// TODO: Are those options valid? +#define VALID_BOARD_DEFINED 1 +#define PANEL_TYPE PANEL_TYPE_YARDFORCE_500_CLASSIC +#define BLADEMOTOR_LENGTH_RECEIVED_MSG 16 +#define DEBUG_TYPE DEBUG_TYPE_UART + +#define MAX_MPS 0.5 // Allow maximum speed of 1.0 m/s +#define PWM_PER_MPS 300.0 // PWM value of 300 means 1 m/s bot speed so we divide by 4 to have correct robot speed but still progressive speed +#define TICKS_PER_M 300.0 // Motor Encoder ticks per meter +#define WHEEL_BASE 0.325 // The distance between the center of the wheels in meters + +#define OPTION_ULTRASONIC 0 +#define OPTION_BUMPER 0 +#elif defined(BOARD_LUV1000RI) // TODO: This currently can't be selected via platformio #define PANEL_TYPE PANEL_TYPE_YARDFORCE_LUV1000RI #define BLADEMOTOR_LENGTH_RECEIVED_MSG 14 @@ -60,10 +74,6 @@ extern "C" #define PWM_PER_MPS 300.0 // PWM value of 300 means 1 m/s bot speed so we divide by 4 to have correct robot speed but still progressive speed #define TICKS_PER_M 300.0 // Motor Encoder ticks per meter #define WHEEL_BASE 0.285 // The distance between the center of the wheels in meters - -#else - -#error "No board selection" #endif //#define I_DONT_NEED_MY_FINGERS 1 // disables EmergencyController() (no wheel lift, or tilt sensing and stopping the blade anymore) @@ -264,6 +274,10 @@ extern "C" #define SOFT_I2C_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE(); #endif +#if !VALID_BOARD_DEFINED +#error "No valid board has been defined, this likely is a mismatch between this file and platformio.ini" +#endif + #ifdef __cplusplus } #endif diff --git a/stm32/ros_usbnode/include/imu/imu.h b/stm32/ros_usbnode/include/imu/imu.h index 02bbc518..c5b4c4f6 100644 --- a/stm32/ros_usbnode/include/imu/imu.h +++ b/stm32/ros_usbnode/include/imu/imu.h @@ -6,7 +6,7 @@ extern "C" { #endif -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" typedef struct { diff --git a/stm32/ros_usbnode/include/imu/wt901.h b/stm32/ros_usbnode/include/imu/wt901.h index baf2e7b7..15b13500 100644 --- a/stm32/ros_usbnode/include/imu/wt901.h +++ b/stm32/ros_usbnode/include/imu/wt901.h @@ -18,7 +18,7 @@ /****************************************************************************** * Includes *******************************************************************************/ -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" /** * @brief Test for WT901 diff --git a/stm32/ros_usbnode/include/main.h b/stm32/ros_usbnode/include/main.h index ae148ca5..8c7ad074 100644 --- a/stm32/ros_usbnode/include/main.h +++ b/stm32/ros_usbnode/include/main.h @@ -22,7 +22,7 @@ extern "C" { /* Includes ------------------------------------------------------------------*/ -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ diff --git a/stm32/ros_usbnode/include/panel.h b/stm32/ros_usbnode/include/panel.h index 9eba8e9f..69602462 100755 --- a/stm32/ros_usbnode/include/panel.h +++ b/stm32/ros_usbnode/include/panel.h @@ -2,7 +2,7 @@ #define __PANEL_H #include "board.h" -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #ifdef __cplusplus diff --git a/stm32/ros_usbnode/include/stm32f_board_hal.h b/stm32/ros_usbnode/include/stm32f_board_hal.h new file mode 100644 index 00000000..68bd36c5 --- /dev/null +++ b/stm32/ros_usbnode/include/stm32f_board_hal.h @@ -0,0 +1,22 @@ +#ifndef __STM32F_BOARD_HAL_H +#define __STM32F_BOARD_HAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if BOARD_YARDFORCE500_VARIANT_ORIG +#include "stm32f1xx_hal.h" +#include "stm32f1xx_hal_uart.h" +#include "stm32f1xx_hal_adc.h" +#elif BOARD_YARDFORCE500_VARIANT_B +#include "stm32f4xx_hal.h" +#include "stm32f4xx_hal_uart.h" +#include "stm32f4xx_hal_adc.h" +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stm32/ros_usbnode/include/usb_device.h b/stm32/ros_usbnode/include/usb_device.h index e996e24b..51e020df 100755 --- a/stm32/ros_usbnode/include/usb_device.h +++ b/stm32/ros_usbnode/include/usb_device.h @@ -27,8 +27,7 @@ #endif /* Includes ------------------------------------------------------------------*/ -#include "stm32f1xx.h" -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include "usbd_def.h" /* USER CODE BEGIN INCLUDE */ diff --git a/stm32/ros_usbnode/include/usbd_conf.h b/stm32/ros_usbnode/include/usbd_conf.h index 04a8e278..42e900e9 100755 --- a/stm32/ros_usbnode/include/usbd_conf.h +++ b/stm32/ros_usbnode/include/usbd_conf.h @@ -31,8 +31,7 @@ #include #include #include "main.h" -#include "stm32f1xx.h" -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" /* USER CODE BEGIN INCLUDE */ diff --git a/stm32/ros_usbnode/platformio.ini b/stm32/ros_usbnode/platformio.ini index 7a980378..16ea1276 100644 --- a/stm32/ros_usbnode/platformio.ini +++ b/stm32/ros_usbnode/platformio.ini @@ -22,7 +22,7 @@ platform_packages = platformio/tool-stm32duino@^1.0.1 platformio/tool-openocd@^2.1100.211028 platformio/tool-dfuutil@^1.11.0 -build_flags = -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom +build_flags = -DBOARD_YARDFORCE500_VARIANT_ORIG=1 -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom build_src_filter = extra_scripts = pre:patch_usb.py @@ -38,7 +38,7 @@ platform_packages = platformio/tool-stm32duino@^1.0.1 platformio/tool-openocd@^2.1100.211028 platformio/tool-dfuutil@^1.11.0 -build_flags = -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom +build_flags = -DBOARD_YARDFORCE500_VARIANT_B=1 -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom build_src_filter = extra_scripts = pre:patch_usb.py @@ -49,8 +49,7 @@ monitor_port = /dev/ttyAMA0 [env:Yardforce500_REMOTE_UPLOAD] board = genericSTM32F103VC ->>>>>>> 5125415 (Start board support for yardforce 500B) -build_flags = -DBOARD_YARDFORCE500 -Wl,--undefined,_printf_float -Isrc/ros/ros_lib -Isrc/ros/ros_custom +build_flags = -DBOARD_YARDFORCE500_VARIANT_ORIG=1 -Wl,--undefined,_printf_float -Isrc/ros/ros_lib -Isrc/ros/ros_custom extra_scripts = raspi_remote_upload.py custom_mowgli_host = 10.146.111.222 custom_mowgli_user = ubuntu diff --git a/stm32/ros_usbnode/src/blademotor.c b/stm32/ros_usbnode/src/blademotor.c index e95fe7a5..79feef08 100644 --- a/stm32/ros_usbnode/src/blademotor.c +++ b/stm32/ros_usbnode/src/blademotor.c @@ -16,8 +16,7 @@ #include #include -#include "stm32f1xx_hal.h" -#include "stm32f1xx_hal_uart.h" +#include "stm32f_board_hal.h" #include "main.h" #include "board.h" diff --git a/stm32/ros_usbnode/src/drivemotor.c b/stm32/ros_usbnode/src/drivemotor.c index d7cf1166..4ca6162a 100644 --- a/stm32/ros_usbnode/src/drivemotor.c +++ b/stm32/ros_usbnode/src/drivemotor.c @@ -15,8 +15,8 @@ *******************************************************************************/ #include #include -#include "stm32f1xx_hal.h" -#include "stm32f1xx_hal_uart.h" + +#include "stm32f_board_hal.h" #include "main.h" #include "ros/ros_custom/cpp_main.h" diff --git a/stm32/ros_usbnode/src/emergency.c b/stm32/ros_usbnode/src/emergency.c index 3e1edc40..cb6b60ed 100755 --- a/stm32/ros_usbnode/src/emergency.c +++ b/stm32/ros_usbnode/src/emergency.c @@ -15,9 +15,9 @@ #include #include #include -#include "stm32f1xx_hal.h" -#include "stm32f1xx_hal_uart.h" -#include "stm32f1xx_hal_adc.h" + +#include "stm32f_board_hal.h" + // stm32 custom #include "board.h" #include "main.h" diff --git a/stm32/ros_usbnode/src/i2c.c b/stm32/ros_usbnode/src/i2c.c index d0f600d2..78f69db5 100755 --- a/stm32/ros_usbnode/src/i2c.c +++ b/stm32/ros_usbnode/src/i2c.c @@ -19,7 +19,7 @@ #include "board.h" #include "main.h" #include "imu/imu.h" -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include "i2c_lis3dh.h" I2C_HandleTypeDef I2C_Handle; diff --git a/stm32/ros_usbnode/src/main.c b/stm32/ros_usbnode/src/main.c index 122f09ec..9617549f 100644 --- a/stm32/ros_usbnode/src/main.c +++ b/stm32/ros_usbnode/src/main.c @@ -19,9 +19,7 @@ #include #include #include -#include "stm32f1xx_hal.h" -#include "stm32f1xx_hal_uart.h" -#include "stm32f1xx_hal_adc.h" +#include "stm32f_board_hal.h" #include "main.h" // stm32 custom #include "board.h" diff --git a/stm32/ros_usbnode/src/panel.c b/stm32/ros_usbnode/src/panel.c index 40e2aebf..68211a23 100755 --- a/stm32/ros_usbnode/src/panel.c +++ b/stm32/ros_usbnode/src/panel.c @@ -13,8 +13,7 @@ #include #include -#include "stm32f1xx_hal.h" -#include "stm32f1xx_hal_uart.h" +#include "stm32f_board_hal.h" #include "panel.h" #include "board.h" diff --git a/stm32/ros_usbnode/src/ros/ros_custom/cpp_main.cpp b/stm32/ros_usbnode/src/ros/ros_custom/cpp_main.cpp index bfeba146..8e2ed6c9 100755 --- a/stm32/ros_usbnode/src/ros/ros_custom/cpp_main.cpp +++ b/stm32/ros_usbnode/src/ros/ros_custom/cpp_main.cpp @@ -23,7 +23,7 @@ #include "drivemotor.h" #include "blademotor.h" #include "ultrasonic_sensor.h" -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include "ringbuffer.h" #include "ros.h" #include "ros/time.h" diff --git a/stm32/ros_usbnode/src/ros/ros_custom/nbt.cpp b/stm32/ros_usbnode/src/ros/ros_custom/nbt.cpp index 51575e84..1a4dd5cb 100755 --- a/stm32/ros_usbnode/src/ros/ros_custom/nbt.cpp +++ b/stm32/ros_usbnode/src/ros/ros_custom/nbt.cpp @@ -5,7 +5,7 @@ * Author: Itamar Eliakim */ -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include "nbt.h" //NBT - Non Blocking Timer diff --git a/stm32/ros_usbnode/src/ros/ros_custom/nbt.h b/stm32/ros_usbnode/src/ros/ros_custom/nbt.h index d5b84ef0..54559201 100755 --- a/stm32/ros_usbnode/src/ros/ros_custom/nbt.h +++ b/stm32/ros_usbnode/src/ros/ros_custom/nbt.h @@ -8,7 +8,7 @@ #ifndef NBT_H_ #define NBT_H_ #include "main.h" -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include #ifdef __cplusplus diff --git a/stm32/ros_usbnode/src/ros/ros_custom/ringbuffer.cpp b/stm32/ros_usbnode/src/ros/ros_custom/ringbuffer.cpp index 877923bb..c83227ba 100755 --- a/stm32/ros_usbnode/src/ros/ros_custom/ringbuffer.cpp +++ b/stm32/ros_usbnode/src/ros/ros_custom/ringbuffer.cpp @@ -23,7 +23,7 @@ * 2013-05-08 Grissiom reimplement */ -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include "ringbuffer.h" #include diff --git a/stm32/ros_usbnode/src/ros/ros_lib/STM32Hardware.h b/stm32/ros_usbnode/src/ros/ros_lib/STM32Hardware.h index 9cee1a99..1c4aeb72 100755 --- a/stm32/ros_usbnode/src/ros/ros_lib/STM32Hardware.h +++ b/stm32/ros_usbnode/src/ros/ros_lib/STM32Hardware.h @@ -2,7 +2,7 @@ #define _STM32_HARDWARE_H_ #include "main.h" -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include "usbd_cdc_if.h" #include "ringbuffer.h" diff --git a/stm32/ros_usbnode/src/soft_i2c.c b/stm32/ros_usbnode/src/soft_i2c.c index bd5171e7..2e7eedef 100644 --- a/stm32/ros_usbnode/src/soft_i2c.c +++ b/stm32/ros_usbnode/src/soft_i2c.c @@ -14,7 +14,7 @@ ****************************************************************************** */ -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include "soft_i2c.h" #include "board.h" diff --git a/stm32/ros_usbnode/src/ultrasonic_sensor.c b/stm32/ros_usbnode/src/ultrasonic_sensor.c index bcc1ddb3..4760945c 100644 --- a/stm32/ros_usbnode/src/ultrasonic_sensor.c +++ b/stm32/ros_usbnode/src/ultrasonic_sensor.c @@ -1,7 +1,6 @@ #include #include -#include "stm32f1xx_hal.h" -#include "stm32f1xx_hal_uart.h" +#include "stm32f_board_hal.h" // stm32 custom #include "board.h" diff --git a/stm32/ros_usbnode/src/usbd_conf.c b/stm32/ros_usbnode/src/usbd_conf.c index 974e7cc2..c67cb32d 100755 --- a/stm32/ros_usbnode/src/usbd_conf.c +++ b/stm32/ros_usbnode/src/usbd_conf.c @@ -19,8 +19,7 @@ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ -#include "stm32f1xx.h" -#include "stm32f1xx_hal.h" +#include "stm32f_board_hal.h" #include "usbd_def.h" #include "usbd_core.h" #include "usbd_cdc.h" From f96b4e45a61480614d3e4592e4d1ca551e2e91ca Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 16:01:37 +0200 Subject: [PATCH 03/22] Port adc.c to STM32f4 --- stm32/ros_usbnode/include/adc.h | 2 +- stm32/ros_usbnode/src/adc.c | 169 +++++++++++++++++---------- stm32/ros_usbnode/src/perimeter.c | 4 + stm32/ros_usbnode/src/stm32f1xx_it.c | 4 +- 4 files changed, 112 insertions(+), 67 deletions(-) diff --git a/stm32/ros_usbnode/include/adc.h b/stm32/ros_usbnode/include/adc.h index 712186fb..98f281d0 100644 --- a/stm32/ros_usbnode/include/adc.h +++ b/stm32/ros_usbnode/include/adc.h @@ -62,7 +62,7 @@ extern union FtoU charge_current_offset; *******************************************************************************/ void TIM2_Init(void); -void ADC2_Init(void); +void ADC_Charging_Init(void); void ADC_input(void); diff --git a/stm32/ros_usbnode/src/adc.c b/stm32/ros_usbnode/src/adc.c index 71b32df8..b0485323 100644 --- a/stm32/ros_usbnode/src/adc.c +++ b/stm32/ros_usbnode/src/adc.c @@ -32,22 +32,22 @@ const float beta = 3380; *******************************************************************************/ typedef enum { - ADC2_CHANNEL_CURRENT = 0, - ADC2_CHANNEL_CHARGEVOLTAGE, - ADC2_CHANNEL_BATTERYVOLTAGE, - ADC2_CHANNEL_CHARGERINPUTVOLTAGE, - ADC2_CHANNEL_NTC, - ADC2_CHANNEL_MAX, -} ADC2_channelSelection_e; + ADC_CHARGING_CHANNEL_CURRENT = 0, + ADC_CHARGING_CHANNEL_CHARGEVOLTAGE, + ADC_CHARGING_CHANNEL_BATTERYVOLTAGE, + ADC_CHARGING_CHANNEL_CHARGERINPUTVOLTAGE, + ADC_CHARGING_CHANNEL_NTC, + ADC_CHARGING_CHANNEL_MAX, +} ADC_Charging_channelSelection_e; /****************************************************************************** * Module Variable Definitions *******************************************************************************/ TIM_HandleTypeDef TIM2_Handle; // Time Base for ADC -ADC_HandleTypeDef ADC2_Handle; +ADC_HandleTypeDef ADC_Charging_Handle; RTC_HandleTypeDef hrtc = {0}; -ADC2_channelSelection_e adc2_eChannelSelection = ADC2_CHANNEL_CURRENT; +ADC_Charging_channelSelection_e adc_charging_eChannelSelection = ADC_CHARGING_CHANNEL_CURRENT; volatile uint16_t adc_u16BatteryVoltage = 0; volatile uint16_t adc_u16Current = 0; @@ -69,7 +69,7 @@ union FtoU charge_current_offset; /****************************************************************************** * Function Prototypes *******************************************************************************/ -void adc2_SetChannel(ADC2_channelSelection_e channel); +void adc_charging_SetChannel(ADC_Charging_channelSelection_e channel); /****************************************************************************** * Public Functions @@ -142,9 +142,17 @@ void TIM2_Init(void) * @param None * @retval None */ -void ADC2_Init(void) +void ADC_Charging_Init(void) { + // Configuration: ADC1 for Yardforce 500B + // ADC2 for Yardforce 500 original +#if BOARD_YARDFORCE500_VARIANT_ORIG __HAL_RCC_ADC2_CLK_ENABLE(); + ADC_TypeDef *Charging_ADC = ADC2; +#elif BOARD_YARDFORCE500_VARIANT_B + __HAL_RCC_ADC1_CLK_ENABLE(); + ADC_TypeDef *Charging_ADC = ADC1; +#endif __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; @@ -173,34 +181,61 @@ void ADC2_Init(void) /** Common config */ - ADC2_Handle.Instance = ADC2; - ADC2_Handle.Init.ScanConvMode = ADC_SCAN_DISABLE; - ADC2_Handle.Init.ContinuousConvMode = DISABLE; - ADC2_Handle.Init.DiscontinuousConvMode = DISABLE; - ADC2_Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_CC2; - ADC2_Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; - ADC2_Handle.Init.NbrOfConversion = 1; - if (HAL_ADC_Init(&ADC2_Handle) != HAL_OK) + ADC_Charging_Handle.Instance = Charging_ADC; + // technically ADC_SCAN_DISABLE on STM32f1, but this is compatible with STM32f1 und STM32f4, and zero is zero + ADC_Charging_Handle.Init.ScanConvMode = DISABLE; + ADC_Charging_Handle.Init.ContinuousConvMode = DISABLE; + ADC_Charging_Handle.Init.DiscontinuousConvMode = DISABLE; + ADC_Charging_Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_CC2; + ADC_Charging_Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; + ADC_Charging_Handle.Init.NbrOfConversion = 1; + +#if BOARD_YARDFORCE500_VARIANT_B + ADC_Charging_Handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; + ADC_Charging_Handle.Init.Resolution = ADC_RESOLUTION_12B; + ADC_Charging_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; + ADC_Charging_Handle.Init.DMAContinuousRequests = DISABLE; + ADC_Charging_Handle.Init.EOCSelection = ADC_EOC_SINGLE_CONV; +#endif + + if (HAL_ADC_Init(&ADC_Charging_Handle) != HAL_OK) { Error_Handler(); } - adc2_eChannelSelection = ADC2_CHANNEL_CURRENT; - adc2_SetChannel(adc2_eChannelSelection); + adc_charging_eChannelSelection = ADC_CHARGING_CHANNEL_CURRENT; + adc_charging_SetChannel(adc_charging_eChannelSelection); - HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(ADC1_2_IRQn); +#if BOARD_YARDFORCE500_VARIANT_ORIG + IRQn_Type used_ADC_irq = ADC1_2_IRQn; +#elif BOARD_YARDFORCE500_VARIANT_B + IRQn_Type used_ADC_irq = ADC_IRQn; +#endif + + HAL_NVIC_SetPriority(used_ADC_irq, 0, 0); + HAL_NVIC_EnableIRQ(used_ADC_irq); +#if BOARD_YARDFORCE500_VARIANT_ORIG // calibrate - important for accuracy ! - HAL_ADCEx_Calibration_Start(&ADC2_Handle); - HAL_ADC_Start_IT(&ADC2_Handle); + HAL_ADCEx_Calibration_Start(&ADC_Charging_Handle); + + // TODO: The STM32f4 does not have a function to calibrate the ADC, + // so we either need manual calibration or just assume it is + // calibrated correctly all the time +#endif + HAL_ADC_Start_IT(&ADC_Charging_Handle); HAL_TIM_OC_Start(&TIM2_Handle, TIM_CHANNEL_2); /* USER CODE BEGIN RTC_MspInit 0 */ __HAL_RCC_PWR_CLK_ENABLE(); /* USER CODE END RTC_MspInit 0 */ /* Enable BKP CLK enable for backup registers */ + +#if BOARD_YARDFORCE500_VARIANT_ORIG + // The STM32f4 seems to not require this, but the STM32f1 does + // TODO: Check if this is true __HAL_RCC_BKP_CLK_ENABLE(); +#endif /* Peripheral clock enable */ __HAL_RCC_RTC_ENABLE(); /* USER CODE BEGIN RTC_MspInit 1 */ @@ -266,45 +301,45 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) } #endif - if (hadc == &ADC2_Handle) + if (hadc == &ADC_Charging_Handle) { - uint16_t l_u16Rawdata = ADC2_Handle.Instance->DR; + uint16_t l_u16Rawdata = ADC_Charging_Handle.Instance->DR; - switch (adc2_eChannelSelection) + switch (adc_charging_eChannelSelection) { - case ADC2_CHANNEL_CURRENT: + case ADC_CHARGING_CHANNEL_CURRENT: adc_u16Current = l_u16Rawdata; break; - case ADC2_CHANNEL_CHARGEVOLTAGE: + case ADC_CHARGING_CHANNEL_CHARGEVOLTAGE: adc_u16ChargerVoltage = l_u16Rawdata; break; - case ADC2_CHANNEL_BATTERYVOLTAGE: + case ADC_CHARGING_CHANNEL_BATTERYVOLTAGE: adc_u16BatteryVoltage = l_u16Rawdata; break; - case ADC2_CHANNEL_CHARGERINPUTVOLTAGE: + case ADC_CHARGING_CHANNEL_CHARGERINPUTVOLTAGE: adc_u16ChargerInputVoltage = l_u16Rawdata; break; - case ADC2_CHANNEL_NTC: + case ADC_CHARGING_CHANNEL_NTC: adc_u16Input_NTC = l_u16Rawdata; break; - case ADC2_CHANNEL_MAX: + case ADC_CHARGING_CHANNEL_MAX: default: /* should not get here */ break; } - adc2_eChannelSelection++; - if (adc2_eChannelSelection == ADC2_CHANNEL_MAX) - adc2_eChannelSelection = ADC2_CHANNEL_CURRENT; - adc2_SetChannel(adc2_eChannelSelection); + adc_charging_eChannelSelection++; + if (adc_charging_eChannelSelection == ADC_CHARGING_CHANNEL_MAX) + adc_charging_eChannelSelection = ADC_CHARGING_CHANNEL_CURRENT; + adc_charging_SetChannel(adc_charging_eChannelSelection); - HAL_ADC_Start_IT(&ADC2_Handle); + HAL_ADC_Start_IT(&ADC_Charging_Handle); } } @@ -314,69 +349,75 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) * Private Functions *******************************************************************************/ -void adc2_SetChannel(ADC2_channelSelection_e channel) +void adc_charging_SetChannel(ADC_Charging_channelSelection_e channel) { ADC_ChannelConfTypeDef sConfig = {0}; +#if BOARD_YARDFORCE500_VARIANT_ORIG + uint32_t adc_SampleTime = ADC_SAMPLETIME_239CYCLES_5; +#elif BOARD_YARDFORCE500_VARIANT_B + uint32_t adc_SampleTime = ADC_SAMPLETIME_480CYCLES; +#endif + switch (channel) { - case ADC2_CHANNEL_CURRENT: + case ADC_CHARGING_CHANNEL_CURRENT: sConfig.Channel = ADC_CHANNEL_1; // PA1 Charge Current - sConfig.Rank = ADC_REGULAR_RANK_1; - sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; - if (HAL_ADC_ConfigChannel(&ADC2_Handle, &sConfig) != HAL_OK) + sConfig.Rank = 1; + sConfig.SamplingTime = adc_SampleTime; + if (HAL_ADC_ConfigChannel(&ADC_Charging_Handle, &sConfig) != HAL_OK) { Error_Handler(); } break; - case ADC2_CHANNEL_CHARGEVOLTAGE: + case ADC_CHARGING_CHANNEL_CHARGEVOLTAGE: sConfig.Channel = ADC_CHANNEL_2; // PA2 Charge Voltage - sConfig.Rank = ADC_REGULAR_RANK_1; - sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; - if (HAL_ADC_ConfigChannel(&ADC2_Handle, &sConfig) != HAL_OK) + sConfig.Rank = 1; + sConfig.SamplingTime = adc_SampleTime; + if (HAL_ADC_ConfigChannel(&ADC_Charging_Handle, &sConfig) != HAL_OK) { Error_Handler(); } break; - case ADC2_CHANNEL_BATTERYVOLTAGE: + case ADC_CHARGING_CHANNEL_BATTERYVOLTAGE: sConfig.Channel = ADC_CHANNEL_3; // PA3 Battery - sConfig.Rank = ADC_REGULAR_RANK_1; - sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; - if (HAL_ADC_ConfigChannel(&ADC2_Handle, &sConfig) != HAL_OK) + sConfig.Rank = 1; + sConfig.SamplingTime = adc_SampleTime; + if (HAL_ADC_ConfigChannel(&ADC_Charging_Handle, &sConfig) != HAL_OK) { Error_Handler(); } break; - case ADC2_CHANNEL_CHARGERINPUTVOLTAGE: + case ADC_CHARGING_CHANNEL_CHARGERINPUTVOLTAGE: sConfig.Channel = ADC_CHANNEL_7; // PA7 Charger Input voltage - sConfig.Rank = ADC_REGULAR_RANK_1; - sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; - if (HAL_ADC_ConfigChannel(&ADC2_Handle, &sConfig) != HAL_OK) + sConfig.Rank = 1; + sConfig.SamplingTime = adc_SampleTime; + if (HAL_ADC_ConfigChannel(&ADC_Charging_Handle, &sConfig) != HAL_OK) { Error_Handler(); } break; - case ADC2_CHANNEL_NTC: + case ADC_CHARGING_CHANNEL_NTC: sConfig.Channel = ADC_CHANNEL_13; // PC2 - sConfig.Rank = ADC_REGULAR_RANK_1; - sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; - if (HAL_ADC_ConfigChannel(&ADC2_Handle, &sConfig) != HAL_OK) + sConfig.Rank = 1; + sConfig.SamplingTime = adc_SampleTime; + if (HAL_ADC_ConfigChannel(&ADC_Charging_Handle, &sConfig) != HAL_OK) { Error_Handler(); } break; - case ADC2_CHANNEL_MAX: + case ADC_CHARGING_CHANNEL_MAX: default: /* should not get here */ sConfig.Channel = ADC_CHANNEL_3; // PA3 Battery - sConfig.Rank = ADC_REGULAR_RANK_1; - sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; - if (HAL_ADC_ConfigChannel(&ADC2_Handle, &sConfig) != HAL_OK) + sConfig.Rank = 1; + sConfig.SamplingTime = adc_SampleTime; + if (HAL_ADC_ConfigChannel(&ADC_Charging_Handle, &sConfig) != HAL_OK) { Error_Handler(); } diff --git a/stm32/ros_usbnode/src/perimeter.c b/stm32/ros_usbnode/src/perimeter.c index 75368397..f4fd460c 100644 --- a/stm32/ros_usbnode/src/perimeter.c +++ b/stm32/ros_usbnode/src/perimeter.c @@ -69,7 +69,11 @@ void perimeter_SetCoil(perimeter_CoilNumber_e idx); /****************************************************************************** * Public Functions *******************************************************************************/ +// TODO: We can't use ADC1 here for the Yardforce 500B, since it is used for charging already! void Perimeter_vInit(void){ +#if BOARD_YARDFORCE500_VARIANT_B + return; // TODO: How to handle? +#endif ADC_ChannelConfTypeDef sConfig = {0}; diff --git a/stm32/ros_usbnode/src/stm32f1xx_it.c b/stm32/ros_usbnode/src/stm32f1xx_it.c index 842aa7bc..43aa9120 100755 --- a/stm32/ros_usbnode/src/stm32f1xx_it.c +++ b/stm32/ros_usbnode/src/stm32f1xx_it.c @@ -76,7 +76,7 @@ extern DMA_HandleTypeDef hdma_uart4_tx; extern DMA_HandleTypeDef hdma_uart4_rx; DMA_HandleTypeDef hdma_adc; -extern ADC_HandleTypeDef ADC2_Handle; +extern ADC_HandleTypeDef ADC_Charging_Handle; /* USER CODE BEGIN EV */ @@ -225,7 +225,7 @@ void SysTick_Handler(void) */ void ADC1_2_IRQHandler(void) { - HAL_ADC_IRQHandler(&ADC2_Handle); + HAL_ADC_IRQHandler(&ADC_Charging_Handle); } From 463c23a1de9c08d068a00c14e29d8b7fa127f163 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 16:21:25 +0200 Subject: [PATCH 04/22] Port blademotor.c to STM32f4 --- stm32/ros_usbnode/include/board.h | 13 ++++- stm32/ros_usbnode/src/blademotor.c | 80 ++++++++++++++++++---------- stm32/ros_usbnode/src/stm32f1xx_it.c | 8 +-- 3 files changed, 67 insertions(+), 34 deletions(-) diff --git a/stm32/ros_usbnode/include/board.h b/stm32/ros_usbnode/include/board.h index 9b23bf01..7e4f0336 100755 --- a/stm32/ros_usbnode/include/board.h +++ b/stm32/ros_usbnode/include/board.h @@ -35,6 +35,11 @@ extern "C" #define PANEL_TYPE_YARDFORCE_900_ECO 3 #if BOARD_YARDFORCE500_VARIANT_ORIG +/////////////////////////// +// Yardforce 500 CLASSIC // +/////////////////////////// +#define BLADEMOTOR_USART_INSTANCE USART3 + #define VALID_BOARD_DEFINED 1 #define PANEL_TYPE PANEL_TYPE_YARDFORCE_500_CLASSIC #define BLADEMOTOR_LENGTH_RECEIVED_MSG 16 @@ -48,7 +53,13 @@ extern "C" #define OPTION_ULTRASONIC 0 #define OPTION_BUMPER 0 #elif BOARD_YARDFORCE500_VARIANT_B +///////////////////// +// Yardforce 500 B // +///////////////////// + // TODO: Are those options valid? +#define BLADEMOTOR_USART_INSTANCE USART6 + #define VALID_BOARD_DEFINED 1 #define PANEL_TYPE PANEL_TYPE_YARDFORCE_500_CLASSIC #define BLADEMOTOR_LENGTH_RECEIVED_MSG 16 @@ -238,8 +249,6 @@ extern "C" #ifdef BLADEMOTOR_USART_ENABLED /* blade motor PAC 5223 (USART3) */ -#define BLADEMOTOR_USART_INSTANCE USART3 - #define BLADEMOTOR_USART_RX_PIN GPIO_PIN_11 #define BLADEMOTOR_USART_RX_PORT GPIOB diff --git a/stm32/ros_usbnode/src/blademotor.c b/stm32/ros_usbnode/src/blademotor.c index 79feef08..5536e22a 100644 --- a/stm32/ros_usbnode/src/blademotor.c +++ b/stm32/ros_usbnode/src/blademotor.c @@ -46,8 +46,8 @@ typedef enum { *******************************************************************************/ UART_HandleTypeDef BLADEMOTOR_USART_Handler; // UART Handle -DMA_HandleTypeDef hdma_uart3_rx; -DMA_HandleTypeDef hdma_uart3_tx; +DMA_HandleTypeDef hdma_uart_blade_rx; +DMA_HandleTypeDef hdma_uart_blade_tx; static BLADEMOTOR_STATE_e blademotor_eState = BLADEMOTOR_INIT_1; @@ -103,11 +103,17 @@ void BLADEMOTOR_Init(void) // enable port and usart clocks BLADEMOTOR_USART_GPIO_CLK_ENABLE(); - BLADEMOTOR_USART_USART_CLK_ENABLE(); + + // Initiale USART3 for STM32f1 and USART6 for STM32f4 +#if BOARD_YARDFORCE500_VARIANT_ORIG + __HAL_RCC_USART3_CLK_ENABLE(); +#elif BOARD_YARDFORCE500_VARIANT_B + __HAL_RCC_USART6_CLK_ENABLE(); +#endif // RX GPIO_InitStruct.Pin = BLADEMOTOR_USART_RX_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; HAL_GPIO_Init(BLADEMOTOR_USART_RX_PORT, &GPIO_InitStruct); @@ -118,7 +124,7 @@ void BLADEMOTOR_Init(void) GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; HAL_GPIO_Init(BLADEMOTOR_USART_TX_PORT, &GPIO_InitStruct); - BLADEMOTOR_USART_Handler.Instance = BLADEMOTOR_USART_INSTANCE;// USART3 + BLADEMOTOR_USART_Handler.Instance = BLADEMOTOR_USART_INSTANCE; BLADEMOTOR_USART_Handler.Init.BaudRate = 115200; // Baud rate BLADEMOTOR_USART_Handler.Init.WordLength = UART_WORDLENGTH_8B; // The word is 8 Bit format BLADEMOTOR_USART_Handler.Init.StopBits = USART_STOPBITS_1; // A stop bit @@ -131,42 +137,60 @@ void BLADEMOTOR_Init(void) DB_TRACE(" * Blade Motor UART initialized\r\n"); /* UART4 DMA Init */ - /* UART4_RX Init */ - hdma_uart3_rx.Instance = DMA1_Channel3; - hdma_uart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; - hdma_uart3_rx.Init.PeriphInc = DMA_PINC_DISABLE; - hdma_uart3_rx.Init.MemInc = DMA_MINC_ENABLE; - hdma_uart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; - hdma_uart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; - hdma_uart3_rx.Init.Mode = DMA_NORMAL; - hdma_uart3_rx.Init.Priority = DMA_PRIORITY_LOW; - if (HAL_DMA_Init(&hdma_uart3_rx) != HAL_OK) + /* UART4_RX Init */ +#if BOARD_YARDFORCE500_VARIANT_ORIG + hdma_uart_blade_rx.Instance = DMA1_Channel3; +#elif BOARD_YARDFORCE500_VARIANT_B + hdma_uart_blade_tx.Instance = DMA2_Stream1; + hdma_uart_blade_tx.Init.Channel = DMA_CHANNEL_5; + hdma_uart_blade_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; +#endif + hdma_uart_blade_tx.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_uart_blade_rx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_uart_blade_rx.Init.MemInc = DMA_MINC_ENABLE; + hdma_uart_blade_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_uart_blade_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_uart_blade_rx.Init.Mode = DMA_NORMAL; + hdma_uart_blade_rx.Init.Priority = DMA_PRIORITY_LOW; + if (HAL_DMA_Init(&hdma_uart_blade_rx) != HAL_OK) { Error_Handler(); } - __HAL_LINKDMA(&BLADEMOTOR_USART_Handler,hdmarx,hdma_uart3_rx); + __HAL_LINKDMA(&BLADEMOTOR_USART_Handler, hdmarx, hdma_uart_blade_rx); /* UART4 DMA Init */ /* UART4_TX Init */ - hdma_uart3_tx.Instance = DMA1_Channel2; - hdma_uart3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; - hdma_uart3_tx.Init.PeriphInc = DMA_PINC_DISABLE; - hdma_uart3_tx.Init.MemInc = DMA_MINC_ENABLE; - hdma_uart3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; - hdma_uart3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; - hdma_uart3_tx.Init.Mode = DMA_NORMAL; - hdma_uart3_tx.Init.Priority = DMA_PRIORITY_HIGH; - if (HAL_DMA_Init(&hdma_uart3_tx) != HAL_OK) +#if BOARD_YARDFORCE500_VARIANT_ORIG + hdma_uart_blade_tx.Instance = DMA1_Channel2; +#elif BOARD_YARDFORCE500_VARIANT_B + hdma_uart_blade_tx.Instance = DMA2_Stream6; + hdma_uart_blade_tx.Init.Channel = DMA_CHANNEL_5; + hdma_uart_blade_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; +#endif + hdma_uart_blade_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_uart_blade_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_uart_blade_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_uart_blade_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_uart_blade_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_uart_blade_tx.Init.Mode = DMA_NORMAL; + hdma_uart_blade_tx.Init.Priority = DMA_PRIORITY_HIGH; + if (HAL_DMA_Init(&hdma_uart_blade_tx) != HAL_OK) { Error_Handler(); } - __HAL_LINKDMA(&BLADEMOTOR_USART_Handler,hdmatx,hdma_uart3_tx); + __HAL_LINKDMA(&BLADEMOTOR_USART_Handler, hdmatx, hdma_uart_blade_tx); // enable IRQ - HAL_NVIC_SetPriority(USART3_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(USART3_IRQn); +#if BOARD_YARDFORCE500_VARIANT_ORIG + IRQn_Type usart_irq = USART3_IRQn; +#elif BOARD_YARDFORCE500_VARIANT_B + IRQn_Type usart_irq = USART6_IRQn; +#endif + + HAL_NVIC_SetPriority(usart_irq, 0, 0); + HAL_NVIC_EnableIRQ(usart_irq); __HAL_UART_ENABLE_IT(&BLADEMOTOR_USART_Handler, UART_IT_TC); blademotor_eState = BLADEMOTOR_INIT_1; diff --git a/stm32/ros_usbnode/src/stm32f1xx_it.c b/stm32/ros_usbnode/src/stm32f1xx_it.c index 43aa9120..be0fdb8b 100755 --- a/stm32/ros_usbnode/src/stm32f1xx_it.c +++ b/stm32/ros_usbnode/src/stm32f1xx_it.c @@ -70,8 +70,8 @@ extern DMA_HandleTypeDef hdma_uart1_tx; extern DMA_HandleTypeDef hdma_uart1_rx; extern DMA_HandleTypeDef hdma_usart2_rx; extern DMA_HandleTypeDef hdma_usart2_tx; -extern DMA_HandleTypeDef hdma_uart3_tx; -extern DMA_HandleTypeDef hdma_uart3_rx; +extern DMA_HandleTypeDef hdma_uart_blade_tx; +extern DMA_HandleTypeDef hdma_uart_blade_rx; extern DMA_HandleTypeDef hdma_uart4_tx; extern DMA_HandleTypeDef hdma_uart4_rx; DMA_HandleTypeDef hdma_adc; @@ -294,7 +294,7 @@ void DMA1_Channel1_IRQHandler(void) */ void DMA1_Channel2_IRQHandler(void) { - HAL_DMA_IRQHandler(&hdma_uart3_tx); + HAL_DMA_IRQHandler(&hdma_uart_blade_tx); } /** @@ -302,7 +302,7 @@ void DMA1_Channel2_IRQHandler(void) */ void DMA1_Channel3_IRQHandler(void) { - HAL_DMA_IRQHandler(&hdma_uart3_rx); + HAL_DMA_IRQHandler(&hdma_uart_blade_rx); } /** From 696028c370358bca3567ca1c68fbfd87d0673718 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 16:26:17 +0200 Subject: [PATCH 05/22] Port drivemotor.c to STM32f4 --- stm32/ros_usbnode/src/drivemotor.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/stm32/ros_usbnode/src/drivemotor.c b/stm32/ros_usbnode/src/drivemotor.c index 4ca6162a..23ba9e17 100644 --- a/stm32/ros_usbnode/src/drivemotor.c +++ b/stm32/ros_usbnode/src/drivemotor.c @@ -145,7 +145,7 @@ void DRIVEMOTOR_Init(void) // RX GPIO_InitStruct.Pin = DRIVEMOTORS_USART_RX_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; HAL_GPIO_Init(DRIVEMOTORS_USART_RX_PORT, &GPIO_InitStruct); @@ -158,7 +158,10 @@ void DRIVEMOTOR_Init(void) HAL_GPIO_Init(DRIVEMOTORS_USART_TX_PORT, &GPIO_InitStruct); // Alternate Pin Set ? +#if BOARD_YARDFORCE500_VARIANT_ORIG + // TODO: This function does not exist on the STM32f4, simply not needed? __HAL_AFIO_REMAP_USART2_ENABLE(); +#endif DRIVEMOTORS_USART_Handler.Instance = DRIVEMOTORS_USART_INSTANCE; // USART2 DRIVEMOTORS_USART_Handler.Init.BaudRate = 115200; // Baud rate @@ -172,7 +175,13 @@ void DRIVEMOTOR_Init(void) /* USART2 DMA Init */ /* USART2_RX Init */ +#if BOARD_YARDFORCE500_VARIANT_ORIG hdma_usart2_rx.Instance = DMA1_Channel6; +#elif BOARD_YARDFORCE500_VARIANT_B + hdma_usart2_rx.Instance = DMA1_Stream5; + hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4; + hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; +#endif 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; @@ -188,7 +197,13 @@ void DRIVEMOTOR_Init(void) __HAL_LINKDMA(&DRIVEMOTORS_USART_Handler, hdmarx, hdma_usart2_rx); // USART2_TX Init */ - hdma_usart2_tx.Instance = DMA1_Channel7; +#if BOARD_YARDFORCE500_VARIANT_ORIG + hdma_usart2_tx.Instance = DMA1_Channel7; +#elif BOARD_YARDFORCE500_VARIANT_B + hdma_usart2_tx.Instance = DMA1_Stream6; + hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4; + hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; +#endif 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; From 8e96ff87cca6583ad63527559850c8e894ebfe79 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 16:29:19 +0200 Subject: [PATCH 06/22] Port perimeter.c to STM32f4 --- stm32/ros_usbnode/src/perimeter.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/stm32/ros_usbnode/src/perimeter.c b/stm32/ros_usbnode/src/perimeter.c index f4fd460c..ca670c68 100644 --- a/stm32/ros_usbnode/src/perimeter.c +++ b/stm32/ros_usbnode/src/perimeter.c @@ -69,12 +69,10 @@ void perimeter_SetCoil(perimeter_CoilNumber_e idx); /****************************************************************************** * Public Functions *******************************************************************************/ -// TODO: We can't use ADC1 here for the Yardforce 500B, since it is used for charging already! +// TODO: We can't use ADC1 exclusively for perimeter for the Yardforce 500B, since it is used for charging already! +// This could be multiplexed, but this is a task for later as perimeter sensing is not needed for now. void Perimeter_vInit(void){ -#if BOARD_YARDFORCE500_VARIANT_B - return; // TODO: How to handle? -#endif - +#if BOARD_YARDFORCE500_VARIANT_ORIG ADC_ChannelConfTypeDef sConfig = {0}; __HAL_RCC_ADC1_CLK_ENABLE(); @@ -150,6 +148,7 @@ void Perimeter_vInit(void){ perimeter_SetCoil(idxCoil); HAL_ADC_Start_DMA(&ADC_Handle,(uint32_t*)&pu16_PerimeterADC_buffer[0],PERIMETER_NBPTS); __HAL_DMA_DISABLE_IT(&hdma_adc,DMA_IT_HT); +#endif } void Perimeter_vApp(void){ From 75e84cbc7f94e378bcc5ff51999f8f12f56a1ee2 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 16:34:42 +0200 Subject: [PATCH 07/22] Port panel.c to STM32f4 --- stm32/ros_usbnode/src/panel.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/stm32/ros_usbnode/src/panel.c b/stm32/ros_usbnode/src/panel.c index 68211a23..85a91183 100755 --- a/stm32/ros_usbnode/src/panel.c +++ b/stm32/ros_usbnode/src/panel.c @@ -86,7 +86,7 @@ void PANEL_Init(void) // RX GPIO_InitStruct.Pin = PANEL_USART_RX_PIN; - GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; HAL_GPIO_Init(PANEL_USART_RX_PORT, &GPIO_InitStruct); @@ -111,8 +111,14 @@ void PANEL_Init(void) /* UART1 DMA Init */ - /* UART1_RX Init */ + /* UART1_RX Init */ +#if BOARD_YARDFORCE500_VARIANT_ORIG hdma_uart1_rx.Instance = DMA1_Channel5; +#elif BOARD_YARDFORCE500_VARIANT_B + hdma_uart1_rx.Instance = DMA1_Stream0; + hdma_uart1_rx.Init.Channel = DMA_CHANNEL_3; + hdma_uart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; +#endif hdma_uart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_uart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_uart1_rx.Init.MemInc = DMA_MINC_ENABLE; @@ -129,7 +135,13 @@ void PANEL_Init(void) /* UART4 DMA Init */ /* UART4_TX Init */ +#if BOARD_YARDFORCE500_VARIANT_ORIG hdma_uart1_tx.Instance = DMA1_Channel4; +#elif BOARD_YARDFORCE500_VARIANT_B + hdma_uart1_tx.Instance = DMA1_Stream1; + hdma_uart1_tx.Init.Channel = DMA_CHANNEL_3; + hdma_uart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; +#endif hdma_uart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_uart1_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_uart1_tx.Init.MemInc = DMA_MINC_ENABLE; From 26a2a4f65785b4c0159413a6433bc6c36028acf8 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 16:38:19 +0200 Subject: [PATCH 08/22] Port soft_i2c.c to STM32f4 --- stm32/ros_usbnode/src/soft_i2c.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stm32/ros_usbnode/src/soft_i2c.c b/stm32/ros_usbnode/src/soft_i2c.c index 2e7eedef..027bb5f1 100644 --- a/stm32/ros_usbnode/src/soft_i2c.c +++ b/stm32/ros_usbnode/src/soft_i2c.c @@ -57,6 +57,9 @@ void __attribute__ ((optimize(0))) TIMER__Wait_us (uint32_t nCount) void SW_I2C_Init(void) { /* PB3, PB4 are used by the JTAG - we need to disable it, as we use SWD anyhow we dont need it */ + +#if BOARD_YARDFORCE500_VARIANT_ORIG + // TODO: Check if some equivalent of all of this is needed for the STM32f4 RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // Enable A.F. clock if( (SW_I2C1_SDA_PIN == GPIO_PIN_3) || (SW_I2C1_SCL_PIN == GPIO_PIN_3)){ @@ -66,8 +69,9 @@ void SW_I2C_Init(void) { AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_NOJNTRST; // JTAG is disabled } - - SOFT_I2C_GPIO_CLK_ENABLE(); +#endif + + SOFT_I2C_GPIO_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; From 6e6c32694b12eaa5f0ba12213a1642b375a0a3ea Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 17:09:36 +0200 Subject: [PATCH 09/22] Port main.c to STM32f4 --- stm32/ros_usbnode/include/board.h | 8 +++- stm32/ros_usbnode/include/main.h | 4 ++ stm32/ros_usbnode/src/main.c | 71 +++++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/stm32/ros_usbnode/include/board.h b/stm32/ros_usbnode/include/board.h index 7e4f0336..233eb33a 100755 --- a/stm32/ros_usbnode/include/board.h +++ b/stm32/ros_usbnode/include/board.h @@ -52,6 +52,8 @@ extern "C" #define OPTION_ULTRASONIC 0 #define OPTION_BUMPER 0 + +#define BOARD_HAS_MASTER_USART 1 #elif BOARD_YARDFORCE500_VARIANT_B ///////////////////// // Yardforce 500 B // @@ -63,7 +65,7 @@ extern "C" #define VALID_BOARD_DEFINED 1 #define PANEL_TYPE PANEL_TYPE_YARDFORCE_500_CLASSIC #define BLADEMOTOR_LENGTH_RECEIVED_MSG 16 -#define DEBUG_TYPE DEBUG_TYPE_UART +#define DEBUG_TYPE DEBUG_TYPE_SWO #define MAX_MPS 0.5 // Allow maximum speed of 1.0 m/s #define PWM_PER_MPS 300.0 // PWM value of 300 means 1 m/s bot speed so we divide by 4 to have correct robot speed but still progressive speed @@ -85,6 +87,8 @@ extern "C" #define PWM_PER_MPS 300.0 // PWM value of 300 means 1 m/s bot speed so we divide by 4 to have correct robot speed but still progressive speed #define TICKS_PER_M 300.0 // Motor Encoder ticks per meter #define WHEEL_BASE 0.285 // The distance between the center of the wheels in meters + +#define BOARD_HAS_MASTER_USART 0 #endif //#define I_DONT_NEED_MY_FINGERS 1 // disables EmergencyController() (no wheel lift, or tilt sensing and stopping the blade anymore) @@ -206,6 +210,7 @@ extern "C" #define HALLSTOP_PORT GPIOD #define HALLSTOP_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() +#if BOARD_HAS_MASTER_USART /* either J6 or J18 can be the master USART port */ #ifdef MASTER_J6 /* USART1 (J6 Pin 1 (TX) Pin 2 (RX)) */ @@ -229,6 +234,7 @@ extern "C" #define MASTER_USART_USART_CLK_ENABLE() __HAL_RCC_UART4_CLK_ENABLE() #define MASTER_USART_IRQ UART4_IRQn #endif +#endif #ifdef DRIVEMOTORS_USART_ENABLED /* drive motors PAC 5210 (USART2) */ diff --git a/stm32/ros_usbnode/include/main.h b/stm32/ros_usbnode/include/main.h index 8c7ad074..c4f85c5e 100644 --- a/stm32/ros_usbnode/include/main.h +++ b/stm32/ros_usbnode/include/main.h @@ -107,7 +107,9 @@ void LED_Init(); void TF4_Init(); void RAIN_Sensor_Init(); void PAC5210RESET_Init(); +#if BOARD_YARDFORCE500_VARIANT_ORIG void MASTER_USART_Init(); +#endif void DRIVEMOTORS_USART_Init(); void SystemClock_Config(); void ADC1_Init(void); @@ -118,7 +120,9 @@ void MX_DMA_Init(void); void Emergency_Init(void); // UART Wrapper functions to hide HAL bullshit ... +#if BOARD_YARDFORCE500_VARIANT_ORIG void MASTER_Transmit(uint8_t *buffer, uint8_t len); +#endif void DRIVEMOTORS_Transmit(uint8_t *buffer, uint8_t len); // Sensor Wrapper functions diff --git a/stm32/ros_usbnode/src/main.c b/stm32/ros_usbnode/src/main.c index 9617549f..1e2e4567 100644 --- a/stm32/ros_usbnode/src/main.c +++ b/stm32/ros_usbnode/src/main.c @@ -60,16 +60,20 @@ static nbt_t main_buzzer_nbt; #if (DEBUG_TYPE != DEBUG_TYPE_UART) && (OPTION_ULTRASONIC == 1) static nbt_t main_ultrasonicsensor_nbt; #endif +#if BOARD_HAS_MASTER_USART volatile uint8_t master_tx_busy = 0; static uint8_t master_tx_buffer_len; static char master_tx_buffer[255]; +#endif uint8_t do_chirp_duration_counter; uint8_t do_chirp = 0; openmower_status_e main_eOpenmowerStatus = OPENMOWER_STATUS_IDLE; +#if BOARD_YARDFORCE500_VARIANT_ORIG UART_HandleTypeDef MASTER_USART_Handler; // UART Handle +#endif // Drive Motors DMA DMA_HandleTypeDef hdma_uart4_rx; @@ -92,11 +96,18 @@ int main(void) HAL_Init(); SystemClock_Config(); +#if BOARD_YARDFORCE500_VARIANT_ORIG + // TODO: Check if some equivalent is needed for the STM32f4 __HAL_RCC_AFIO_CLK_ENABLE(); +#endif __HAL_RCC_PWR_CLK_ENABLE(); MX_DMA_Init(); + +#if BOARD_YARDFORCE500_VARIANT_ORIG + // Init debug USART MASTER_USART_Init(); +#endif DB_TRACE("\r\n"); DB_TRACE(" __ ___ ___\r\n"); @@ -110,11 +121,11 @@ int main(void) LED_Init(); DB_TRACE(" * LED initialized\r\n"); TIM2_Init(); - ADC2_Init(); + ADC_Charging_Init(); #ifdef OPTION_PERIMETER Perimeter_vInit(); #endif - DB_TRACE(" * ADC1 initialized\r\n"); + DB_TRACE(" * Charging ADC initialized\r\n"); TIM3_Init(); HAL_TIM_PWM_Start(&TIM3_Handle, TIM_CHANNEL_4); TIM4_Init(); @@ -291,6 +302,9 @@ int main(void) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } +#if BOARD_YARDFORCE500_VARIANT_ORIG +// The STM32f1 has enough USARTs to use one for debugging + /** * @brief Init the Master Serial Port - this what connects to the upstream controller * @retval None @@ -366,6 +380,7 @@ void MASTER_USART_Init() __HAL_UART_ENABLE_IT(&MASTER_USART_Handler, UART_IT_TC); } +#endif /** * @brief Init LED @@ -491,19 +506,32 @@ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; - RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ +#if BOARD_YARDFORCE500_VARIANT_ORIG RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_LSI; +#elif BOARD_YARDFORCE500_VARIANT_B + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_HSE; +#endif RCC_OscInitStruct.HSEState = RCC_HSE_ON; +#if BOARD_YARDFORCE500_VARIANT_ORIG RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; +#endif RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.LSIState = RCC_LSI_ON; + RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; +#if BOARD_YARDFORCE500_VARIANT_ORIG RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; +#elif BOARD_YARDFORCE500_VARIANT_B + RCC_OscInitStruct.PLL.PLLM = 4; + RCC_OscInitStruct.PLL.PLLN = 72; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; + RCC_OscInitStruct.PLL.PLLQ = 3; +#endif if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); @@ -521,6 +549,11 @@ void SystemClock_Config(void) { Error_Handler(); } + +#if BOARD_YARDFORCE500_VARIANT_ORIG + // TODO: Is something like this needed for variant B? + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC | RCC_PERIPHCLK_USB | RCC_PERIPHCLK_RTC; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV8; PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5; @@ -529,6 +562,7 @@ void SystemClock_Config(void) { Error_Handler(); } +#endif } /** @@ -652,7 +686,10 @@ void TIM4_Init(void) Error_Handler(); } +#if BOARD_YARDFORCE500_VARIANT_ORIG + // TODO: check if something equivalent is needed for the STM32f4 __HAL_AFIO_REMAP_TIM4_ENABLE(); // to use PD14 it is a full remap +#endif __HAL_RCC_GPIOD_CLK_ENABLE(); /**TIM4 GPIO Configuration @@ -675,6 +712,7 @@ void MX_DMA_Init(void) __HAL_RCC_DMA1_CLK_ENABLE(); __HAL_RCC_DMA2_CLK_ENABLE(); +#if BOARD_YARDFORCE500_VARIANT_ORIG /* DMA interrupt init */ /* DMA1_Channel1_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0); @@ -711,6 +749,21 @@ void MX_DMA_Init(void) /* DMA1_Channel3_IRQn interrupt configuration (BLADE MOTOR) */ HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn); +#elif BOARD_YARDFORCE500_VARIANT_B + /* DMA interrupt init */ + /* DMA1_Stream5_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); + /* DMA1_Stream6_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); + /* DMA2_Stream1_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn); + /* DMA2_Stream6_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); +#endif } /* @@ -823,7 +876,11 @@ void vprint(const char *fmt, va_list argp) ITM_SendChar(string[i]); } #elif DEBUG_TYPE == DEBUG_TYPE_UART - MASTER_Transmit((unsigned char *)string, strlen(string)); +#if BOARD_YARDFORCE500_VARIANT_ORIG + MASTER_Transmit((unsigned char *)string, strlen(string)); +#else +#error "This board does not suport debugging via UART" +#endif #endif } } @@ -839,6 +896,7 @@ void debug_printf(const char *fmt, ...) va_end(argp); } +#if BOARD_YARDFORCE500_VARIANT_ORIG /* * Send message via MASTER USART (DMA Normal Mode) */ @@ -854,6 +912,7 @@ void MASTER_Transmit(uint8_t *buffer, uint8_t len) memcpy(master_tx_buffer, buffer, master_tx_buffer_len); HAL_UART_Transmit_DMA(&MASTER_USART_Handler, (uint8_t *)master_tx_buffer, master_tx_buffer_len); // send message via UART } +#endif /* * Initialize Watchdog - not tested yet (by Nekraus) @@ -934,6 +993,7 @@ void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { +#if BOARD_HAS_MASTER_USART if (huart->Instance == MASTER_USART_INSTANCE) { if (__HAL_USART_GET_FLAG(&MASTER_USART_Handler, USART_FLAG_TC)) @@ -941,6 +1001,7 @@ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) master_tx_busy = 0; } } +#endif } void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart) @@ -955,6 +1016,7 @@ void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart) */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { +#if BOARD_HAS_MASTER_USART if (huart->Instance == MASTER_USART_INSTANCE) { #if (DEBUG_TYPE != DEBUG_TYPE_UART) && (OPTION_ULTRASONIC == 1) @@ -969,4 +1031,5 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { DRIVEMOTOR_ReceiveIT(); } +#endif } From 6af29b90e9a35d205c9a7fa3c2b83f884a2c7401 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 24 Jun 2023 17:38:07 +0200 Subject: [PATCH 10/22] Port compiles --- stm32/ros_usbnode/include/stm32f4xx_it.h | 75 ++ stm32/ros_usbnode/platformio.ini | 9 +- stm32/ros_usbnode/src/charger.c | 4 + .../src/proxy_inc/stm32f1/startup_stm32f1xx.s | 0 .../{ => proxy_inc/stm32f1}/stm32f1xx_it.c | 0 .../src/proxy_inc/stm32f1/usbd_conf.c | 659 +++++++++++++++++ .../src/proxy_inc/stm32f4/startup_stm32f4xx.s | 431 +++++++++++ .../src/proxy_inc/stm32f4/stm32f4xx_it.c | 399 +++++++++++ .../src/proxy_inc/stm32f4/usbd_conf.c | 671 ++++++++++++++++++ stm32/ros_usbnode/src/startup_stm32f.s | 5 + stm32/ros_usbnode/src/stm32f_it.c | 5 + stm32/ros_usbnode/src/usbd_conf.c | 664 +---------------- 12 files changed, 2260 insertions(+), 662 deletions(-) create mode 100644 stm32/ros_usbnode/include/stm32f4xx_it.h create mode 100644 stm32/ros_usbnode/src/proxy_inc/stm32f1/startup_stm32f1xx.s rename stm32/ros_usbnode/src/{ => proxy_inc/stm32f1}/stm32f1xx_it.c (100%) create mode 100644 stm32/ros_usbnode/src/proxy_inc/stm32f1/usbd_conf.c create mode 100644 stm32/ros_usbnode/src/proxy_inc/stm32f4/startup_stm32f4xx.s create mode 100644 stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c create mode 100644 stm32/ros_usbnode/src/proxy_inc/stm32f4/usbd_conf.c create mode 100644 stm32/ros_usbnode/src/startup_stm32f.s create mode 100755 stm32/ros_usbnode/src/stm32f_it.c mode change 100755 => 100644 stm32/ros_usbnode/src/usbd_conf.c diff --git a/stm32/ros_usbnode/include/stm32f4xx_it.h b/stm32/ros_usbnode/include/stm32f4xx_it.h new file mode 100644 index 00000000..bf411324 --- /dev/null +++ b/stm32/ros_usbnode/include/stm32f4xx_it.h @@ -0,0 +1,75 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm32f4xx_it.h + * @brief This file contains the headers of the interrupt handlers. + ****************************************************************************** + * @attention + * + * Copyright (c) 2023 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_IT_H +#define __STM32F4xx_IT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ +void NMI_Handler(void); +void HardFault_Handler(void); +void MemManage_Handler(void); +void BusFault_Handler(void); +void UsageFault_Handler(void); +void SVC_Handler(void); +void DebugMon_Handler(void); +void PendSV_Handler(void); +void SysTick_Handler(void); +void WWDG_IRQHandler(void); +void DMA1_Stream5_IRQHandler(void); +void DMA1_Stream6_IRQHandler(void); +void ADC_IRQHandler(void); +void USART2_IRQHandler(void); +void DMA2_Stream1_IRQHandler(void); +void OTG_FS_IRQHandler(void); +void DMA2_Stream6_IRQHandler(void); +void USART6_IRQHandler(void); +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F4xx_IT_H */ diff --git a/stm32/ros_usbnode/platformio.ini b/stm32/ros_usbnode/platformio.ini index 16ea1276..2054d439 100644 --- a/stm32/ros_usbnode/platformio.ini +++ b/stm32/ros_usbnode/platformio.ini @@ -14,6 +14,11 @@ default_envs = Yardforce500 [env] platform = ststm32 framework = stm32cube +build_src_filter = + +<*> + -<.git/> + -<.svn/> + - [env:Yardforce500] board = genericSTM32F103VC @@ -23,8 +28,7 @@ platform_packages = platformio/tool-openocd@^2.1100.211028 platformio/tool-dfuutil@^1.11.0 build_flags = -DBOARD_YARDFORCE500_VARIANT_ORIG=1 -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom -build_src_filter = -extra_scripts = +extra_scripts = pre:patch_usb.py pre:add_swo_viewer.py debug_tool = stlink @@ -39,7 +43,6 @@ platform_packages = platformio/tool-openocd@^2.1100.211028 platformio/tool-dfuutil@^1.11.0 build_flags = -DBOARD_YARDFORCE500_VARIANT_B=1 -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom -build_src_filter = extra_scripts = pre:patch_usb.py pre:add_swo_viewer.py diff --git a/stm32/ros_usbnode/src/charger.c b/stm32/ros_usbnode/src/charger.c index f9436120..2d75c76d 100644 --- a/stm32/ros_usbnode/src/charger.c +++ b/stm32/ros_usbnode/src/charger.c @@ -139,7 +139,11 @@ uint8_t chargecontrol_is_charging = 0; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(CHARGE_GPIO_PORT, &GPIO_InitStruct); + +#if BOARD_YARDFORCE500_VARIANT_ORIG + // TODO: Is something equivalent needed for the STM32f4? __HAL_AFIO_REMAP_TIM1_ENABLE(); // to use PE8/9 it is a full remap +#endif // Charge CH1/CH1N PWM Timer diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f1/startup_stm32f1xx.s b/stm32/ros_usbnode/src/proxy_inc/stm32f1/startup_stm32f1xx.s new file mode 100644 index 00000000..e69de29b diff --git a/stm32/ros_usbnode/src/stm32f1xx_it.c b/stm32/ros_usbnode/src/proxy_inc/stm32f1/stm32f1xx_it.c similarity index 100% rename from stm32/ros_usbnode/src/stm32f1xx_it.c rename to stm32/ros_usbnode/src/proxy_inc/stm32f1/stm32f1xx_it.c diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f1/usbd_conf.c b/stm32/ros_usbnode/src/proxy_inc/stm32f1/usbd_conf.c new file mode 100644 index 00000000..c67cb32d --- /dev/null +++ b/stm32/ros_usbnode/src/proxy_inc/stm32f1/usbd_conf.c @@ -0,0 +1,659 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : Target/usbd_conf.c + * @version : v2.0_Cube + * @brief : This file implements the board support package for the USB device library + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f_board_hal.h" +#include "usbd_def.h" +#include "usbd_core.h" +#include "usbd_cdc.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_FS; +void Error_Handler(void); + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +/* USER CODE BEGIN PFP */ +/* Private function prototypes -----------------------------------------------*/ + +/* USER CODE END PFP */ + +/* Private functions ---------------------------------------------------------*/ +static USBD_StatusTypeDef USBD_Get_USB_Status(HAL_StatusTypeDef hal_status); +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state); +#else +void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state); +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ + +/******************************************************************************* + LL Driver Callbacks (PCD -> USB Device Library) +*******************************************************************************/ +/* MSP Init */ + +void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle) +{ + if(pcdHandle->Instance==USB) + { + /* USER CODE BEGIN USB_MspInit 0 */ + + /* USER CODE END USB_MspInit 0 */ + /* Peripheral clock enable */ + __HAL_RCC_USB_CLK_ENABLE(); + + /* Peripheral interrupt init */ + HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); + /* USER CODE BEGIN USB_MspInit 1 */ + + /* USER CODE END USB_MspInit 1 */ + } +} + +void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle) +{ + if(pcdHandle->Instance==USB) + { + /* USER CODE BEGIN USB_MspDeInit 0 */ + + /* USER CODE END USB_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_USB_CLK_DISABLE(); + + /* Peripheral interrupt Deinit*/ + HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn); + + /* USER CODE BEGIN USB_MspDeInit 1 */ + + /* USER CODE END USB_MspDeInit 1 */ + } +} + +/** + * @brief Setup stage callback + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#else +void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#else +void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_DataInStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff); +} + +/** + * @brief SOF callback. + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_SOFCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_SOF((USBD_HandleTypeDef*)hpcd->pData); +} + +/** + * @brief Reset callback. + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ResetCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_SpeedTypeDef speed = USBD_SPEED_FULL; + + if ( hpcd->Init.speed != PCD_SPEED_FULL) + { + Error_Handler(); + } + /* Set Speed. */ + 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + /* Inform USB library that core enters in suspend Mode. */ + USBD_LL_Suspend((USBD_HandleTypeDef*)hpcd->pData); + /* Enter in STOP mode. */ + /* USER CODE BEGIN 2 */ + 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + /* 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#else +void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_IsoOUTIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum); +} + +/** + * @brief ISOINIncomplete callback. + * @param hpcd: PCD handle + * @param epnum: Endpoint number + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#else +void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_IsoINIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum); +} + +/** + * @brief Connect callback. + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ConnectCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_DevConnected((USBD_HandleTypeDef*)hpcd->pData); +} + +/** + * @brief Disconnect callback. + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + 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. */ + /* Link the driver to the stack. */ + hpcd_USB_FS.pData = pdev; + pdev->pData = &hpcd_USB_FS; + + hpcd_USB_FS.Instance = USB; + hpcd_USB_FS.Init.dev_endpoints = 8; + hpcd_USB_FS.Init.speed = PCD_SPEED_FULL; + hpcd_USB_FS.Init.low_power_enable = DISABLE; + hpcd_USB_FS.Init.lpm_enable = DISABLE; + hpcd_USB_FS.Init.battery_charging_enable = DISABLE; + if (HAL_PCD_Init(&hpcd_USB_FS) != HAL_OK) + { + Error_Handler( ); + } + +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) + /* Register USB PCD CallBacks */ + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback); + + HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_FS, PCD_DataOutStageCallback); + HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_FS, PCD_DataInStageCallback); + HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_FS, PCD_ISOOUTIncompleteCallback); + HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_FS, PCD_ISOINIncompleteCallback); +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ + /* USER CODE BEGIN EndPoint_Configuration */ + HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18); + HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58); + /* USER CODE END EndPoint_Configuration */ + /* USER CODE BEGIN EndPoint_Configuration_CDC */ + HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0xC0); + HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0x110); + HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82 , PCD_SNG_BUF, 0x100); + /* USER CODE END EndPoint_Configuration_CDC */ + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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, uint16_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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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, uint16_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); + + usb_status = USBD_Get_USB_Status(hal_status); + + return usb_status; +} + +/** + * @brief Returns the last transferred packet size. + * @param pdev: Device handle + * @param ep_addr: Endpoint number + * @retval Received 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); +} + +/** + * @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); +} + +/** + * @brief Static single allocation. + * @param size: Size of allocated memory + * @retval None + */ +void *USBD_static_malloc(uint32_t size) +{ + static uint32_t mem[(sizeof(USBD_CDC_HandleTypeDef)/4)+1];/* On 32-bit boundary */ + return mem; +} + +/** + * @brief Dummy memory free + * @param p: Pointer to allocated memory address + * @retval None + */ +void USBD_static_free(void *p) +{ + +} + +/** + * @brief Software Device Connection + * @param hpcd: PCD handle + * @param state: Connection state (0: disconnected / 1: connected) + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state) +#else +void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + /* USER CODE BEGIN 6 */ + if (state == 1) + { + /* Configure Low connection state. */ + + } + else + { + /* Configure High connection state. */ + + } + /* USER CODE END 6 */ +} + +/** + * @brief Returns the USB status depending on the HAL status: + * @param hal_status: HAL status + * @retval USB status + */ +USBD_StatusTypeDef USBD_Get_USB_Status(HAL_StatusTypeDef hal_status) +{ + USBD_StatusTypeDef usb_status = USBD_OK; + + 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; +} diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f4/startup_stm32f4xx.s b/stm32/ros_usbnode/src/proxy_inc/stm32f4/startup_stm32f4xx.s new file mode 100644 index 00000000..7368496c --- /dev/null +++ b/stm32/ros_usbnode/src/proxy_inc/stm32f4/startup_stm32f4xx.s @@ -0,0 +1,431 @@ +/** + ****************************************************************************** + * @file startup_stm32f401xc.s + * @author MCD Application Team + * @brief STM32F401xCxx 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 + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + + .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 */ + ldr r0, =_sdata + ldr r1, =_edata + ldr r2, =_sidata + movs r3, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r4, [r2, r3] + str r4, [r0, r3] + adds r3, r3, #4 + +LoopCopyDataInit: + adds r4, r0, r3 + cmp r4, r1 + bcc CopyDataInit + +/* Zero fill the bss segment. */ + ldr r2, =_sbss + ldr r4, =_ebss + movs r3, #0 + b LoopFillZerobss + +FillZerobss: + str r3, [r2] + adds r2, r2, #4 + +LoopFillZerobss: + cmp r2, r4 + bcc FillZerobss + +/* Call the clock system initialization function.*/ + bl SystemInit +/* 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 */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .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 0 /* Reserved */ + .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 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ + .word 0 /* Reserved */ + .word SDIO_IRQHandler /* SDIO */ + .word TIM5_IRQHandler /* TIM5 */ + .word SPI3_IRQHandler /* SPI3 */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .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 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .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 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word FPU_IRQHandler /* FPU */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word SPI4_IRQHandler /* SPI4 */ + +/******************************************************************************* +* +* 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 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 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 DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_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 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 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 FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c b/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c new file mode 100644 index 00000000..305a7931 --- /dev/null +++ b/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c @@ -0,0 +1,399 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file stm32f4xx_it.c + * @brief Interrupt Service Routines. + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include "board.h" +#include "main.h" +#include "panel.h" +#include "stm32f1xx_it.h" +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN TD */ + +/* USER CODE END TD */ + +/* 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 PV */ + +uint16_t cnt_uart4_overrun = 0; +uint16_t cnt_usart2_overrun = 0; + +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +/* Private user code ---------------------------------------------------------*/ +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +/* External variables --------------------------------------------------------*/ +extern UART_HandleTypeDef MASTER_USART_Handler; +extern UART_HandleTypeDef DRIVEMOTORS_USART_Handler; +extern UART_HandleTypeDef BLADEMOTOR_USART_Handler; +extern PCD_HandleTypeDef hpcd_USB_FS; + + +extern DMA_HandleTypeDef hdma_uart1_tx; +extern DMA_HandleTypeDef hdma_uart1_rx; +extern DMA_HandleTypeDef hdma_usart2_rx; +extern DMA_HandleTypeDef hdma_usart2_tx; +extern DMA_HandleTypeDef hdma_uart_blade_tx; +extern DMA_HandleTypeDef hdma_uart_blade_rx; +extern DMA_HandleTypeDef hdma_uart4_tx; +extern DMA_HandleTypeDef hdma_uart4_rx; +extern DMA_HandleTypeDef hdma_adc; + +extern ADC_HandleTypeDef ADC_Charging_Handle; + +/* USER CODE BEGIN EV */ + +/* USER CODE END EV */ + +/******************************************************************************/ +/* Cortex-M3 Processor Interruption and Exception Handlers */ +/******************************************************************************/ +/** + * @brief This function handles Non maskable interrupt. + */ +void NMI_Handler(void) +{ + /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ + + /* USER CODE END NonMaskableInt_IRQn 0 */ + /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ + while (1) + { + } + /* USER CODE END NonMaskableInt_IRQn 1 */ +} + +/** + * @brief This function handles Hard fault interrupt. + */ +void HardFault_Handler(void) +{ + /* USER CODE BEGIN HardFault_IRQn 0 */ + + /* USER CODE END HardFault_IRQn 0 */ + while (1) + { + /* USER CODE BEGIN W1_HardFault_IRQn 0 */ + /* USER CODE END W1_HardFault_IRQn 0 */ + } +} + +/** + * @brief This function handles Memory management fault. + */ +void MemManage_Handler(void) +{ + /* USER CODE BEGIN MemoryManagement_IRQn 0 */ + + /* USER CODE END MemoryManagement_IRQn 0 */ + while (1) + { + /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */ + /* USER CODE END W1_MemoryManagement_IRQn 0 */ + } +} + +/** + * @brief This function handles Prefetch fault, memory access fault. + */ +void BusFault_Handler(void) +{ + /* USER CODE BEGIN BusFault_IRQn 0 */ + + /* USER CODE END BusFault_IRQn 0 */ + while (1) + { + /* USER CODE BEGIN W1_BusFault_IRQn 0 */ + /* USER CODE END W1_BusFault_IRQn 0 */ + } +} + +/** + * @brief This function handles Undefined instruction or illegal state. + */ +void UsageFault_Handler(void) +{ + /* USER CODE BEGIN UsageFault_IRQn 0 */ + + /* USER CODE END UsageFault_IRQn 0 */ + while (1) + { + /* USER CODE BEGIN W1_UsageFault_IRQn 0 */ + /* USER CODE END W1_UsageFault_IRQn 0 */ + } +} + +/** + * @brief This function handles System service call via SWI instruction. + */ +void SVC_Handler(void) +{ + /* USER CODE BEGIN SVCall_IRQn 0 */ + + /* USER CODE END SVCall_IRQn 0 */ + /* USER CODE BEGIN SVCall_IRQn 1 */ + + /* USER CODE END SVCall_IRQn 1 */ +} + +/** + * @brief This function handles Debug monitor. + */ +void DebugMon_Handler(void) +{ + /* USER CODE BEGIN DebugMonitor_IRQn 0 */ + + /* USER CODE END DebugMonitor_IRQn 0 */ + /* USER CODE BEGIN DebugMonitor_IRQn 1 */ + + /* USER CODE END DebugMonitor_IRQn 1 */ +} + +/** + * @brief This function handles Pendable request for system service. + */ +void PendSV_Handler(void) +{ + /* USER CODE BEGIN PendSV_IRQn 0 */ + + /* USER CODE END PendSV_IRQn 0 */ + /* USER CODE BEGIN PendSV_IRQn 1 */ + + /* USER CODE END PendSV_IRQn 1 */ +} + +/** + * @brief This function handles System tick timer. + */ +void SysTick_Handler(void) +{ + /* USER CODE BEGIN SysTick_IRQn 0 */ + + /* USER CODE END SysTick_IRQn 0 */ + HAL_IncTick(); + /* USER CODE BEGIN SysTick_IRQn 1 */ + + /* USER CODE END SysTick_IRQn 1 */ +} + +/******************************************************************************/ +/* STM32F1xx 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_stm32f1xx.s). */ +/******************************************************************************/ + +/** + * @brief This function handles ADC 1 & 2 global interrupt. + */ +void ADC1_2_IRQHandler(void) +{ + HAL_ADC_IRQHandler(&ADC_Charging_Handle); +} + + +/** + * @brief This function handles USART1 global interrupt. + */ +void USART1_IRQHandler(void) +{ + /* USER CODE BEGIN USART1_IRQn 0 */ + + /* USER CODE END USART1_IRQn 0 */ + HAL_UART_IRQHandler(&PANEL_USART_Handler); + + /* USER CODE BEGIN USART1_IRQn 1 */ + + /* USER CODE END USART1_IRQn 1 */ +} + +/** + * @brief This function handles UART global interrupt. (DRIVE MOTOR) + */ +void USART2_IRQHandler(void) +{ + + uint32_t status = USART2->SR; + if (status & USART_SR_ORE){ // overrun error + cnt_usart2_overrun++; + } + + HAL_UART_IRQHandler(&DRIVEMOTORS_USART_Handler); +} + +/** + * @brief This function handles UART global interrupt. (BLADE MOTOR) + */ +void USART3_IRQHandler(void) +{ + HAL_UART_IRQHandler(&BLADEMOTOR_USART_Handler); +} + + +/** + * @brief This function handles UART4 global interrupt. + */ +void UART4_IRQHandler(void) +{ + HAL_UART_IRQHandler(&MASTER_USART_Handler); +} + +/** + * @brief This function handles DMA1 channel1 global interrupt. + */ +void DMA1_Channel1_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */ + + /* USER CODE END DMA1_Channel1_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_adc); + /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */ + + /* USER CODE END DMA1_Channel1_IRQn 1 */ +} + +/** + * @brief This function handles DMA1 channel2 global interrupt. + */ +void DMA1_Channel2_IRQHandler(void) +{ + HAL_DMA_IRQHandler(&hdma_uart_blade_tx); +} + +/** + * @brief This function handles DMA1 channel3 global interrupt. + */ +void DMA1_Channel3_IRQHandler(void) +{ + HAL_DMA_IRQHandler(&hdma_uart_blade_rx); +} + +/** + * @brief This function handles DMA1 channel4 global interrupt. + */ +void DMA1_Channel4_IRQHandler(void) +{ + HAL_DMA_IRQHandler(&hdma_uart1_tx); +} + +/** + * @brief This function handles DMA1 channel5 global interrupt. + */ +void DMA1_Channel5_IRQHandler(void) +{ + HAL_DMA_IRQHandler(&hdma_uart1_rx); +} + + +/** + * @brief This function handles DMA1 channel6 global interrupt. (DRIVE MOTOR UART) + */ +void DMA1_Channel6_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */ + + /* USER CODE END DMA1_Channel6_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_usart2_rx); + /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */ + + /* USER CODE END DMA1_Channel6_IRQn 1 */ +} + +/** + * @brief This function handles DMA1 channel7 global interrupt. (DRIVE MOTOR UART) + */ +void DMA1_Channel7_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Channel7_IRQn 0 */ + + /* USER CODE END DMA1_Channel7_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_usart2_tx); + /* USER CODE BEGIN DMA1_Channel7_IRQn 1 */ + + /* USER CODE END DMA1_Channel7_IRQn 1 */ +} + +/** + * @brief This function handles DMA2 channel3 global interrupts. + */ +void DMA2_Channel3_IRQHandler(void) +{ + /* USER CODE BEGIN DMA2_Channel3_IRQn 0 */ + + /* USER CODE END DMA2_Channel3_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_uart4_rx); + /* USER CODE BEGIN DMA2_Channel3_IRQn 1 */ + + /* USER CODE END DMA2_Channel3_IRQn 1 */ +} + +/** + * @brief This function handles DMA2 channel4 and channel5 global interrupts. + */ +void DMA2_Channel4_5_IRQHandler(void) +{ + /* USER CODE BEGIN DMA2_Channel4_5_IRQn 0 */ + + /* USER CODE END DMA2_Channel4_5_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_uart4_tx); + /* USER CODE BEGIN DMA2_Channel4_5_IRQn 1 */ + + /* USER CODE END DMA2_Channel4_5_IRQn 1 */ +} + + + +/** + * @brief This function handles USB low priority or CAN RX0 interrupts. + */ +void USB_LP_CAN1_RX0_IRQHandler(void) +{ + /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */ + + /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */ + HAL_PCD_IRQHandler(&hpcd_USB_FS); + /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */ + + /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */ +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f4/usbd_conf.c b/stm32/ros_usbnode/src/proxy_inc/stm32f4/usbd_conf.c new file mode 100644 index 00000000..7c1bbe24 --- /dev/null +++ b/stm32/ros_usbnode/src/proxy_inc/stm32f4/usbd_conf.c @@ -0,0 +1,671 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : Target/usbd_conf.c + * @version : v1.0_Cube + * @brief : This file implements the board support package for the USB device library + ****************************************************************************** + * @attention + * + * Copyright (c) 2023 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx.h" +#include "stm32f4xx_hal.h" +#include "usbd_def.h" +#include "usbd_core.h" + +#include "usbd_cdc.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(void); + +/* External functions --------------------------------------------------------*/ +void SystemClock_Config(void); + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +/* USER CODE BEGIN PFP */ +/* Private function prototypes -----------------------------------------------*/ +USBD_StatusTypeDef USBD_Get_USB_Status(HAL_StatusTypeDef hal_status); + +/* 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 = {0}; + if(pcdHandle->Instance==USB_OTG_FS) + { + /* USER CODE BEGIN USB_OTG_FS_MspInit 0 */ + + /* USER CODE END USB_OTG_FS_MspInit 0 */ + + __HAL_RCC_GPIOA_CLK_ENABLE(); + /**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, 0, 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#else +void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#else +void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_DataInStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff); +} + +/** + * @brief SOF callback. + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_SOFCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_SOF((USBD_HandleTypeDef*)hpcd->pData); +} + +/** + * @brief Reset callback. + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ResetCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_SpeedTypeDef speed = USBD_SPEED_FULL; + + if ( hpcd->Init.speed != PCD_SPEED_FULL) + { + Error_Handler(); + } + /* Set Speed. */ + 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + /* 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 */ + 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + /* 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 + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#else +void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_IsoOUTIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum); +} + +/** + * @brief ISOINIncomplete callback. + * @param hpcd: PCD handle + * @param epnum: Endpoint number + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#else +void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_IsoINIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum); +} + +/** + * @brief Connect callback. + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_ConnectCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + USBD_LL_DevConnected((USBD_HandleTypeDef*)hpcd->pData); +} + +/** + * @brief Disconnect callback. + * @param hpcd: PCD handle + * @retval None + */ +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) +static void PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) +#else +void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ +{ + 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 = 4; + hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL; + hpcd_USB_OTG_FS.Init.dma_enable = DISABLE; + 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( ); + } + +#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) + /* Register USB PCD CallBacks */ + HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback); + + HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_OTG_FS, PCD_DataOutStageCallback); + HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_OTG_FS, PCD_DataInStageCallback); + HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOOUTIncompleteCallback); + HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOINIncompleteCallback); +#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ + HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80); + HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40); + HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80); + } + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + 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); + + usb_status = USBD_Get_USB_Status(hal_status); + + return usb_status; +} + +/** + * @brief Returns the last transferred packet size. + * @param pdev: Device handle + * @param ep_addr: Endpoint number + * @retval Received 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); +} + +#ifdef USBD_HS_TESTMODE_ENABLE +/** + * @brief Set High speed Test mode. + * @param pdev: Device handle + * @param testmode: test mode + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_SetTestMode(USBD_HandleTypeDef *pdev, uint8_t testmode) +{ + UNUSED(pdev); + UNUSED(testmode); + + return USBD_OK; +} +#endif /* USBD_HS_TESTMODE_ENABLE */ + +/** + * @brief Static single allocation. + * @param size: Size of allocated memory + * @retval None + */ +void *USBD_static_malloc(uint32_t size) +{ + static uint32_t mem[(sizeof(USBD_CDC_HandleTypeDef)/4)+1];/* On 32-bit boundary */ + return mem; +} + +/** + * @brief Dummy memory free + * @param p: Pointer to allocated memory address + * @retval None + */ +void USBD_static_free(void *p) +{ + +} + +/** + * @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); +} + +/** + * @brief Returns the USB status depending on the HAL status: + * @param hal_status: HAL status + * @retval USB status + */ +USBD_StatusTypeDef USBD_Get_USB_Status(HAL_StatusTypeDef hal_status) +{ + USBD_StatusTypeDef usb_status = USBD_OK; + + 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; +} diff --git a/stm32/ros_usbnode/src/startup_stm32f.s b/stm32/ros_usbnode/src/startup_stm32f.s new file mode 100644 index 00000000..bd07c757 --- /dev/null +++ b/stm32/ros_usbnode/src/startup_stm32f.s @@ -0,0 +1,5 @@ +#if BOARD_YARDFORCE500_VARIANT_ORIG +#include "proxy_inc/stm32f1/startup_stm32f1xx.s" +#elif BOARD_YARDFORCE500_VARIANT_B +#include "proxy_inc/stm32f4/startup_stm32f4xx.s" +#endif diff --git a/stm32/ros_usbnode/src/stm32f_it.c b/stm32/ros_usbnode/src/stm32f_it.c new file mode 100755 index 00000000..867d04e4 --- /dev/null +++ b/stm32/ros_usbnode/src/stm32f_it.c @@ -0,0 +1,5 @@ +#if BOARD_YARDFORCE500_VARIANT_ORIG +#include "proxy_inc/stm32f1/stm32f1xx_it.c" +#elif BOARD_YARDFORCE500_VARIANT_B +#include "proxy_inc/stm32f4/stm32f4xx_it.c" +#endif diff --git a/stm32/ros_usbnode/src/usbd_conf.c b/stm32/ros_usbnode/src/usbd_conf.c old mode 100755 new mode 100644 index c67cb32d..d6f13645 --- a/stm32/ros_usbnode/src/usbd_conf.c +++ b/stm32/ros_usbnode/src/usbd_conf.c @@ -1,659 +1,5 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : Target/usbd_conf.c - * @version : v2.0_Cube - * @brief : This file implements the board support package for the USB device library - ****************************************************************************** - * @attention - * - * Copyright (c) 2022 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file - * in the root directory of this software component. - * If no LICENSE file comes with this software, it is provided AS-IS. - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Includes ------------------------------------------------------------------*/ -#include "stm32f_board_hal.h" -#include "usbd_def.h" -#include "usbd_core.h" -#include "usbd_cdc.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_FS; -void Error_Handler(void); - -/* USER CODE BEGIN 0 */ - -/* USER CODE END 0 */ - -/* USER CODE BEGIN PFP */ -/* Private function prototypes -----------------------------------------------*/ - -/* USER CODE END PFP */ - -/* Private functions ---------------------------------------------------------*/ -static USBD_StatusTypeDef USBD_Get_USB_Status(HAL_StatusTypeDef hal_status); -/* USER CODE BEGIN 1 */ - -/* USER CODE END 1 */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state); -#else -void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state); -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ - -/******************************************************************************* - LL Driver Callbacks (PCD -> USB Device Library) -*******************************************************************************/ -/* MSP Init */ - -void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle) -{ - if(pcdHandle->Instance==USB) - { - /* USER CODE BEGIN USB_MspInit 0 */ - - /* USER CODE END USB_MspInit 0 */ - /* Peripheral clock enable */ - __HAL_RCC_USB_CLK_ENABLE(); - - /* Peripheral interrupt init */ - HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); - /* USER CODE BEGIN USB_MspInit 1 */ - - /* USER CODE END USB_MspInit 1 */ - } -} - -void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle) -{ - if(pcdHandle->Instance==USB) - { - /* USER CODE BEGIN USB_MspDeInit 0 */ - - /* USER CODE END USB_MspDeInit 0 */ - /* Peripheral clock disable */ - __HAL_RCC_USB_CLK_DISABLE(); - - /* Peripheral interrupt Deinit*/ - HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn); - - /* USER CODE BEGIN USB_MspDeInit 1 */ - - /* USER CODE END USB_MspDeInit 1 */ - } -} - -/** - * @brief Setup stage callback - * @param hpcd: PCD handle - * @retval None - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) -#else -void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - 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 - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) -#else -void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - 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 - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) -#else -void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - USBD_LL_DataInStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff); -} - -/** - * @brief SOF callback. - * @param hpcd: PCD handle - * @retval None - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_SOFCallback(PCD_HandleTypeDef *hpcd) -#else -void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - USBD_LL_SOF((USBD_HandleTypeDef*)hpcd->pData); -} - -/** - * @brief Reset callback. - * @param hpcd: PCD handle - * @retval None - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_ResetCallback(PCD_HandleTypeDef *hpcd) -#else -void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - USBD_SpeedTypeDef speed = USBD_SPEED_FULL; - - if ( hpcd->Init.speed != PCD_SPEED_FULL) - { - Error_Handler(); - } - /* Set Speed. */ - 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 - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) -#else -void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - /* Inform USB library that core enters in suspend Mode. */ - USBD_LL_Suspend((USBD_HandleTypeDef*)hpcd->pData); - /* Enter in STOP mode. */ - /* USER CODE BEGIN 2 */ - 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 - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) -#else -void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - /* 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 - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) -#else -void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - USBD_LL_IsoOUTIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum); -} - -/** - * @brief ISOINIncomplete callback. - * @param hpcd: PCD handle - * @param epnum: Endpoint number - * @retval None - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) -#else -void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - USBD_LL_IsoINIncomplete((USBD_HandleTypeDef*)hpcd->pData, epnum); -} - -/** - * @brief Connect callback. - * @param hpcd: PCD handle - * @retval None - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_ConnectCallback(PCD_HandleTypeDef *hpcd) -#else -void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - USBD_LL_DevConnected((USBD_HandleTypeDef*)hpcd->pData); -} - -/** - * @brief Disconnect callback. - * @param hpcd: PCD handle - * @retval None - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) -#else -void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - 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. */ - /* Link the driver to the stack. */ - hpcd_USB_FS.pData = pdev; - pdev->pData = &hpcd_USB_FS; - - hpcd_USB_FS.Instance = USB; - hpcd_USB_FS.Init.dev_endpoints = 8; - hpcd_USB_FS.Init.speed = PCD_SPEED_FULL; - hpcd_USB_FS.Init.low_power_enable = DISABLE; - hpcd_USB_FS.Init.lpm_enable = DISABLE; - hpcd_USB_FS.Init.battery_charging_enable = DISABLE; - if (HAL_PCD_Init(&hpcd_USB_FS) != HAL_OK) - { - Error_Handler( ); - } - -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) - /* Register USB PCD CallBacks */ - HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback); - - HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_FS, PCD_DataOutStageCallback); - HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_FS, PCD_DataInStageCallback); - HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_FS, PCD_ISOOUTIncompleteCallback); - HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_FS, PCD_ISOINIncompleteCallback); -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ - /* USER CODE BEGIN EndPoint_Configuration */ - HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18); - HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58); - /* USER CODE END EndPoint_Configuration */ - /* USER CODE BEGIN EndPoint_Configuration_CDC */ - HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0xC0); - HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0x110); - HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82 , PCD_SNG_BUF, 0x100); - /* USER CODE END EndPoint_Configuration_CDC */ - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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, uint16_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); - - usb_status = USBD_Get_USB_Status(hal_status); - - 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, uint16_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); - - usb_status = USBD_Get_USB_Status(hal_status); - - return usb_status; -} - -/** - * @brief Returns the last transferred packet size. - * @param pdev: Device handle - * @param ep_addr: Endpoint number - * @retval Received 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); -} - -/** - * @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); -} - -/** - * @brief Static single allocation. - * @param size: Size of allocated memory - * @retval None - */ -void *USBD_static_malloc(uint32_t size) -{ - static uint32_t mem[(sizeof(USBD_CDC_HandleTypeDef)/4)+1];/* On 32-bit boundary */ - return mem; -} - -/** - * @brief Dummy memory free - * @param p: Pointer to allocated memory address - * @retval None - */ -void USBD_static_free(void *p) -{ - -} - -/** - * @brief Software Device Connection - * @param hpcd: PCD handle - * @param state: Connection state (0: disconnected / 1: connected) - * @retval None - */ -#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) -static void PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state) -#else -void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state) -#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ -{ - /* USER CODE BEGIN 6 */ - if (state == 1) - { - /* Configure Low connection state. */ - - } - else - { - /* Configure High connection state. */ - - } - /* USER CODE END 6 */ -} - -/** - * @brief Returns the USB status depending on the HAL status: - * @param hal_status: HAL status - * @retval USB status - */ -USBD_StatusTypeDef USBD_Get_USB_Status(HAL_StatusTypeDef hal_status) -{ - USBD_StatusTypeDef usb_status = USBD_OK; - - 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; -} +#if BOARD_YARDFORCE500_VARIANT_ORIG +#include "proxy_inc/stm32f1/usbd_conf.c" +#elif BOARD_YARDFORCE500_VARIANT_B +#include "proxy_inc/stm32f4/usbd_conf.c" +#endif From 45d8c262932b9a76b91bbe4808ab09b800c27690 Mon Sep 17 00:00:00 2001 From: slashphotos Date: Sat, 1 Jul 2023 00:58:46 +0200 Subject: [PATCH 11/22] Fix hardware initialization for stm32f4. Fix some bugs. Currently the software keeps restarting. --- stm32/ros_usbnode/include/board.h | 15 ++ stm32/ros_usbnode/include/stm32f4xx_it.h | 11 +- stm32/ros_usbnode/src/adc.c | 1 + stm32/ros_usbnode/src/blademotor.c | 8 +- stm32/ros_usbnode/src/charger.c | 3 + stm32/ros_usbnode/src/drivemotor.c | 6 + stm32/ros_usbnode/src/i2c.c | 3 + stm32/ros_usbnode/src/main.c | 33 +++- stm32/ros_usbnode/src/panel.c | 6 + .../src/proxy_inc/stm32f4/stm32f4xx_it.c | 163 ++++++++---------- .../src/proxy_inc/stm32f4/usbd_conf.c | 60 +++---- stm32/ros_usbnode/src/usb_device.c | 3 +- 12 files changed, 178 insertions(+), 134 deletions(-) diff --git a/stm32/ros_usbnode/include/board.h b/stm32/ros_usbnode/include/board.h index 233eb33a..4a290480 100755 --- a/stm32/ros_usbnode/include/board.h +++ b/stm32/ros_usbnode/include/board.h @@ -190,6 +190,9 @@ extern "C" /* Play button - (LOW when pressed) */ #define PLAY_BUTTON_PIN GPIO_PIN_7 +#if BOARD_YARDFORCE500_VARIANT_B +#define PLAY_BUTTON_PIN GPIO_PIN_9 +#endif #define PLAY_BUTTON_PORT GPIOC #define PLAY_BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() @@ -254,6 +257,7 @@ extern "C" #endif #ifdef BLADEMOTOR_USART_ENABLED +#if BOARD_YARDFORCE500_VARIANT_ORIG /* blade motor PAC 5223 (USART3) */ #define BLADEMOTOR_USART_RX_PIN GPIO_PIN_11 #define BLADEMOTOR_USART_RX_PORT GPIOB @@ -263,6 +267,17 @@ extern "C" #define BLADEMOTOR_USART_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define BLADEMOTOR_USART_USART_CLK_ENABLE() __HAL_RCC_USART3_CLK_ENABLE() +#elif BOARD_YARDFORCE500_VARIANT_B +/* blade motor PAC 5223 (USART6) */ +#define BLADEMOTOR_USART_RX_PIN GPIO_PIN_7 +#define BLADEMOTOR_USART_RX_PORT GPIOC + +#define BLADEMOTOR_USART_TX_PIN GPIO_PIN_6 +#define BLADEMOTOR_USART_TX_PORT GPIOC + +#define BLADEMOTOR_USART_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() +#define BLADEMOTOR_USART_USART_CLK_ENABLE() __HAL_RCC_USART6_CLK_ENABLE() +#endif #endif #ifdef PANEL_USART_ENABLED diff --git a/stm32/ros_usbnode/include/stm32f4xx_it.h b/stm32/ros_usbnode/include/stm32f4xx_it.h index bf411324..225b3a15 100644 --- a/stm32/ros_usbnode/include/stm32f4xx_it.h +++ b/stm32/ros_usbnode/include/stm32f4xx_it.h @@ -56,13 +56,16 @@ void DebugMon_Handler(void); void PendSV_Handler(void); void SysTick_Handler(void); void WWDG_IRQHandler(void); -void DMA1_Stream5_IRQHandler(void); -void DMA1_Stream6_IRQHandler(void); +void DMA2_Stream1_IRQHandler(void); +void DMA2_Stream6_IRQHandler(void); void ADC_IRQHandler(void); +void USART1_IRQHandler(void); void USART2_IRQHandler(void); -void DMA2_Stream1_IRQHandler(void); +void DMA1_Stream1_IRQHandler(void); void OTG_FS_IRQHandler(void); -void DMA2_Stream6_IRQHandler(void); +void DMA1_Stream0_IRQHandler(void); +void DMA1_Stream5_IRQHandler(void); +void DMA1_Stream6_IRQHandler(void); void USART6_IRQHandler(void); /* USER CODE BEGIN EFP */ diff --git a/stm32/ros_usbnode/src/adc.c b/stm32/ros_usbnode/src/adc.c index b0485323..d3ba0a63 100644 --- a/stm32/ros_usbnode/src/adc.c +++ b/stm32/ros_usbnode/src/adc.c @@ -194,6 +194,7 @@ void ADC_Charging_Init(void) ADC_Charging_Handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; ADC_Charging_Handle.Init.Resolution = ADC_RESOLUTION_12B; ADC_Charging_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; + ADC_Charging_Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; ADC_Charging_Handle.Init.DMAContinuousRequests = DISABLE; ADC_Charging_Handle.Init.EOCSelection = ADC_EOC_SINGLE_CONV; #endif diff --git a/stm32/ros_usbnode/src/blademotor.c b/stm32/ros_usbnode/src/blademotor.c index 5536e22a..7ca6d3b4 100644 --- a/stm32/ros_usbnode/src/blademotor.c +++ b/stm32/ros_usbnode/src/blademotor.c @@ -141,11 +141,11 @@ void BLADEMOTOR_Init(void) #if BOARD_YARDFORCE500_VARIANT_ORIG hdma_uart_blade_rx.Instance = DMA1_Channel3; #elif BOARD_YARDFORCE500_VARIANT_B - hdma_uart_blade_tx.Instance = DMA2_Stream1; - hdma_uart_blade_tx.Init.Channel = DMA_CHANNEL_5; - hdma_uart_blade_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + hdma_uart_blade_rx.Instance = DMA2_Stream1; + hdma_uart_blade_rx.Init.Channel = DMA_CHANNEL_5; + hdma_uart_blade_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; #endif - hdma_uart_blade_tx.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_uart_blade_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_uart_blade_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_uart_blade_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_uart_blade_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; diff --git a/stm32/ros_usbnode/src/charger.c b/stm32/ros_usbnode/src/charger.c index 2d75c76d..8a3a8aae 100644 --- a/stm32/ros_usbnode/src/charger.c +++ b/stm32/ros_usbnode/src/charger.c @@ -138,6 +138,9 @@ uint8_t chargecontrol_is_charging = 0; GPIO_InitStruct.Pin = CHARGE_LOWSIDE_PIN|CHARGE_HIGHSIDE_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF1_TIM1; +#endif HAL_GPIO_Init(CHARGE_GPIO_PORT, &GPIO_InitStruct); #if BOARD_YARDFORCE500_VARIANT_ORIG diff --git a/stm32/ros_usbnode/src/drivemotor.c b/stm32/ros_usbnode/src/drivemotor.c index 23ba9e17..85c919a3 100644 --- a/stm32/ros_usbnode/src/drivemotor.c +++ b/stm32/ros_usbnode/src/drivemotor.c @@ -148,6 +148,9 @@ void DRIVEMOTOR_Init(void) GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF7_USART2; +#endif HAL_GPIO_Init(DRIVEMOTORS_USART_RX_PORT, &GPIO_InitStruct); // TX @@ -155,6 +158,9 @@ void DRIVEMOTOR_Init(void) GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF7_USART2; +#endif HAL_GPIO_Init(DRIVEMOTORS_USART_TX_PORT, &GPIO_InitStruct); // Alternate Pin Set ? diff --git a/stm32/ros_usbnode/src/i2c.c b/stm32/ros_usbnode/src/i2c.c index 78f69db5..fa2d6321 100755 --- a/stm32/ros_usbnode/src/i2c.c +++ b/stm32/ros_usbnode/src/i2c.c @@ -35,6 +35,9 @@ void I2C_Init(void) GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; +#endif HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* Peripheral clock enable */ diff --git a/stm32/ros_usbnode/src/main.c b/stm32/ros_usbnode/src/main.c index 1e2e4567..aa334cde 100644 --- a/stm32/ros_usbnode/src/main.c +++ b/stm32/ros_usbnode/src/main.c @@ -100,7 +100,6 @@ int main(void) // TODO: Check if some equivalent is needed for the STM32f4 __HAL_RCC_AFIO_CLK_ENABLE(); #endif - __HAL_RCC_PWR_CLK_ENABLE(); MX_DMA_Init(); @@ -507,6 +506,10 @@ void SystemClock_Config(void) RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; +#if BOARD_YARDFORCE500_VARIANT_B + __HAL_RCC_PWR_CLK_ENABLE(); + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2); +#endif /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ @@ -537,6 +540,21 @@ void SystemClock_Config(void) Error_Handler(); } +#if BOARD_YARDFORCE500_VARIANT_B + /** Initializes the CPU, AHB and APB buses clocks + */ + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK + |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) + { + Error_Handler(); + } + #elif /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; @@ -549,7 +567,7 @@ void SystemClock_Config(void) { Error_Handler(); } - +#endif #if BOARD_YARDFORCE500_VARIANT_ORIG // TODO: Is something like this needed for variant B? RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; @@ -633,6 +651,9 @@ void TIM3_Init(void) GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; +#endif HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } @@ -699,6 +720,9 @@ void TIM4_Init(void) GPIO_InitStruct.Pin = GPIO_PIN_14; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF2_TIM4; +#endif HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); } @@ -1023,7 +1047,9 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) ULTRASONICSENSOR_ReceiveIT(); #endif } - else if (huart->Instance == BLADEMOTOR_USART_INSTANCE) + else +#endif + if (huart->Instance == BLADEMOTOR_USART_INSTANCE) { BLADEMOTOR_ReceiveIT(); } @@ -1031,5 +1057,4 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { DRIVEMOTOR_ReceiveIT(); } -#endif } diff --git a/stm32/ros_usbnode/src/panel.c b/stm32/ros_usbnode/src/panel.c index 85a91183..fe2cdcde 100755 --- a/stm32/ros_usbnode/src/panel.c +++ b/stm32/ros_usbnode/src/panel.c @@ -89,12 +89,18 @@ void PANEL_Init(void) GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF7_USART1; +#endif HAL_GPIO_Init(PANEL_USART_RX_PORT, &GPIO_InitStruct); // TX GPIO_InitStruct.Pin = PANEL_USART_TX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF7_USART1; +#endif HAL_GPIO_Init(PANEL_USART_TX_PORT, &GPIO_InitStruct); PANEL_USART_USART_CLK_ENABLE(); diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c b/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c index 305a7931..2d90ffa2 100644 --- a/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c +++ b/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c @@ -21,7 +21,7 @@ #include "board.h" #include "main.h" #include "panel.h" -#include "stm32f1xx_it.h" +#include "stm32f4xx_it.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ @@ -72,8 +72,6 @@ extern DMA_HandleTypeDef hdma_usart2_rx; extern DMA_HandleTypeDef hdma_usart2_tx; extern DMA_HandleTypeDef hdma_uart_blade_tx; extern DMA_HandleTypeDef hdma_uart_blade_rx; -extern DMA_HandleTypeDef hdma_uart4_tx; -extern DMA_HandleTypeDef hdma_uart4_rx; extern DMA_HandleTypeDef hdma_adc; extern ADC_HandleTypeDef ADC_Charging_Handle; @@ -83,7 +81,7 @@ extern ADC_HandleTypeDef ADC_Charging_Handle; /* USER CODE END EV */ /******************************************************************************/ -/* Cortex-M3 Processor Interruption and Exception Handlers */ +/* Cortex-M4 Processor Interruption and Exception Handlers */ /******************************************************************************/ /** * @brief This function handles Non maskable interrupt. @@ -214,20 +212,25 @@ void SysTick_Handler(void) } /******************************************************************************/ -/* STM32F1xx Peripheral Interrupt Handlers */ +/* 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_stm32f1xx.s). */ +/* please refer to the startup file (startup_stm32f4xx.s). */ /******************************************************************************/ /** - * @brief This function handles ADC 1 & 2 global interrupt. + * @brief This function handles ADC1 global interrupt. */ -void ADC1_2_IRQHandler(void) +void ADC_IRQHandler(void) { - HAL_ADC_IRQHandler(&ADC_Charging_Handle); -} + /* USER CODE BEGIN ADC_IRQn 0 */ + + /* USER CODE END ADC_IRQn 0 */ + HAL_ADC_IRQHandler(&hdma_adc); + /* USER CODE BEGIN ADC_IRQn 1 */ + /* USER CODE END ADC_IRQn 1 */ +} /** * @brief This function handles USART1 global interrupt. @@ -259,139 +262,117 @@ void USART2_IRQHandler(void) } /** - * @brief This function handles UART global interrupt. (BLADE MOTOR) - */ -void USART3_IRQHandler(void) -{ - HAL_UART_IRQHandler(&BLADEMOTOR_USART_Handler); -} - - -/** - * @brief This function handles UART4 global interrupt. - */ -void UART4_IRQHandler(void) -{ - HAL_UART_IRQHandler(&MASTER_USART_Handler); -} - -/** - * @brief This function handles DMA1 channel1 global interrupt. + * @brief This function handles USART6 global interrupt. */ -void DMA1_Channel1_IRQHandler(void) +void USART6_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */ + /* USER CODE BEGIN USART6_IRQn 0 */ - /* USER CODE END DMA1_Channel1_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_adc); - /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */ + /* USER CODE END USART6_IRQn 0 */ + HAL_UART_IRQHandler(&BLADEMOTOR_USART_Handler); + /* USER CODE BEGIN USART6_IRQn 1 */ - /* USER CODE END DMA1_Channel1_IRQn 1 */ + /* USER CODE END USART6_IRQn 1 */ } + /** - * @brief This function handles DMA1 channel2 global interrupt. + * @brief This function handles DMA2 stream1 global interrupt. */ -void DMA1_Channel2_IRQHandler(void) +void DMA2_Stream1_IRQHandler(void) { + /* USER CODE BEGIN DMA2_Stream1_IRQn 0 */ + + /* USER CODE END DMA2_Stream1_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_uart_blade_tx); -} + /* USER CODE BEGIN DMA2_Stream1_IRQn 1 */ -/** - * @brief This function handles DMA1 channel3 global interrupt. - */ -void DMA1_Channel3_IRQHandler(void) -{ - HAL_DMA_IRQHandler(&hdma_uart_blade_rx); + /* USER CODE END DMA2_Stream1_IRQn 1 */ } /** - * @brief This function handles DMA1 channel4 global interrupt. + * @brief This function handles DMA2 stream6 global interrupt. */ -void DMA1_Channel4_IRQHandler(void) +void DMA2_Stream6_IRQHandler(void) { - HAL_DMA_IRQHandler(&hdma_uart1_tx); -} + /* USER CODE BEGIN DMA2_Stream6_IRQn 0 */ -/** - * @brief This function handles DMA1 channel5 global interrupt. - */ -void DMA1_Channel5_IRQHandler(void) -{ - HAL_DMA_IRQHandler(&hdma_uart1_rx); -} + /* USER CODE END DMA2_Stream6_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_uart_blade_rx); + /* USER CODE BEGIN DMA2_Stream6_IRQn 1 */ + /* USER CODE END DMA2_Stream6_IRQn 1 */ +} /** - * @brief This function handles DMA1 channel6 global interrupt. (DRIVE MOTOR UART) + * @brief This function handles DMA1 stream1 global interrupt. */ -void DMA1_Channel6_IRQHandler(void) +void DMA1_Stream1_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */ + /* USER CODE BEGIN DMA1_Stream1_IRQn 0 */ - /* USER CODE END DMA1_Channel6_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_usart2_rx); - /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */ + /* USER CODE END DMA1_Stream1_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_uart1_tx); + /* USER CODE BEGIN DMA1_Stream1_IRQn 1 */ - /* USER CODE END DMA1_Channel6_IRQn 1 */ + /* USER CODE END DMA1_Stream1_IRQn 1 */ } /** - * @brief This function handles DMA1 channel7 global interrupt. (DRIVE MOTOR UART) + * @brief This function handles DMA1 stream0 global interrupt. */ -void DMA1_Channel7_IRQHandler(void) +void DMA1_Stream0_IRQHandler(void) { - /* USER CODE BEGIN DMA1_Channel7_IRQn 0 */ + /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */ - /* USER CODE END DMA1_Channel7_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_usart2_tx); - /* USER CODE BEGIN DMA1_Channel7_IRQn 1 */ + /* USER CODE END DMA1_Stream0_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_uart1_rx); + /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */ - /* USER CODE END DMA1_Channel7_IRQn 1 */ + /* USER CODE END DMA1_Stream0_IRQn 1 */ } + /** - * @brief This function handles DMA2 channel3 global interrupts. + * @brief This function handles DMA1 stream5 global interrupt. (DRIVE MOTOR UART) */ -void DMA2_Channel3_IRQHandler(void) +void DMA1_Stream5_IRQHandler(void) { - /* USER CODE BEGIN DMA2_Channel3_IRQn 0 */ + /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */ - /* USER CODE END DMA2_Channel3_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_uart4_rx); - /* USER CODE BEGIN DMA2_Channel3_IRQn 1 */ + /* USER CODE END DMA1_Stream5_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_usart2_rx); + /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */ - /* USER CODE END DMA2_Channel3_IRQn 1 */ + /* USER CODE END DMA1_Stream5_IRQn 1 */ } /** - * @brief This function handles DMA2 channel4 and channel5 global interrupts. + * @brief This function handles DMA1 stream6 global interrupt. (DRIVE MOTOR UART) */ -void DMA2_Channel4_5_IRQHandler(void) +void DMA1_Stream6_IRQHandler(void) { - /* USER CODE BEGIN DMA2_Channel4_5_IRQn 0 */ + /* USER CODE BEGIN DMA1_Stream6_IRQn 0 */ - /* USER CODE END DMA2_Channel4_5_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_uart4_tx); - /* USER CODE BEGIN DMA2_Channel4_5_IRQn 1 */ + /* USER CODE END DMA1_Stream6_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_usart2_tx); + /* USER CODE BEGIN DMA1_Stream6_IRQn 1 */ - /* USER CODE END DMA2_Channel4_5_IRQn 1 */ + /* USER CODE END DMA1_Stream6_IRQn 1 */ } - - /** - * @brief This function handles USB low priority or CAN RX0 interrupts. + * @brief This function handles USB On The Go FS global interrupt. */ -void USB_LP_CAN1_RX0_IRQHandler(void) +void OTG_FS_IRQHandler(void) { - /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */ + /* USER CODE BEGIN OTG_FS_IRQn 0 */ - /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */ - HAL_PCD_IRQHandler(&hpcd_USB_FS); - /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */ + /* USER CODE END OTG_FS_IRQn 0 */ + HAL_PCD_IRQHandler(&hpcd_USB_FS); + /* USER CODE BEGIN OTG_FS_IRQn 1 */ - /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */ + /* USER CODE END OTG_FS_IRQn 1 */ } /* USER CODE BEGIN 1 */ diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f4/usbd_conf.c b/stm32/ros_usbnode/src/proxy_inc/stm32f4/usbd_conf.c index 7c1bbe24..149c89fa 100644 --- a/stm32/ros_usbnode/src/proxy_inc/stm32f4/usbd_conf.c +++ b/stm32/ros_usbnode/src/proxy_inc/stm32f4/usbd_conf.c @@ -39,7 +39,7 @@ /* USER CODE END PV */ -PCD_HandleTypeDef hpcd_USB_OTG_FS; +PCD_HandleTypeDef hpcd_USB_FS; void Error_Handler(void); /* External functions --------------------------------------------------------*/ @@ -321,42 +321,42 @@ 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 = 4; - hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL; - hpcd_USB_OTG_FS.Init.dma_enable = DISABLE; - 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) + hpcd_USB_FS.pData = pdev; + pdev->pData = &hpcd_USB_FS; + + hpcd_USB_FS.Instance = USB_OTG_FS; + hpcd_USB_FS.Init.dev_endpoints = 4; + hpcd_USB_FS.Init.speed = PCD_SPEED_FULL; + hpcd_USB_FS.Init.dma_enable = DISABLE; + hpcd_USB_FS.Init.phy_itface = PCD_PHY_EMBEDDED; + hpcd_USB_FS.Init.Sof_enable = DISABLE; + hpcd_USB_FS.Init.low_power_enable = DISABLE; + hpcd_USB_FS.Init.lpm_enable = DISABLE; + hpcd_USB_FS.Init.vbus_sensing_enable = DISABLE; + hpcd_USB_FS.Init.use_dedicated_ep1 = DISABLE; + if (HAL_PCD_Init(&hpcd_USB_FS) != HAL_OK) { Error_Handler( ); } #if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U) /* Register USB PCD CallBacks */ - HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback); - HAL_PCD_RegisterCallback(&hpcd_USB_OTG_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback); - - HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_OTG_FS, PCD_DataOutStageCallback); - HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_OTG_FS, PCD_DataInStageCallback); - HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOOUTIncompleteCallback); - HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_OTG_FS, PCD_ISOINIncompleteCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback); + HAL_PCD_RegisterCallback(&hpcd_USB_FS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback); + + HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_FS, PCD_DataOutStageCallback); + HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_FS, PCD_DataInStageCallback); + HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_FS, PCD_ISOOUTIncompleteCallback); + HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_FS, PCD_ISOINIncompleteCallback); #endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ - HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80); - HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40); - HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80); + HAL_PCDEx_SetRxFiFo(&hpcd_USB_FS, 0x80); + HAL_PCDEx_SetTxFiFo(&hpcd_USB_FS, 0, 0x40); + HAL_PCDEx_SetTxFiFo(&hpcd_USB_FS, 1, 0x80); } return USBD_OK; } diff --git a/stm32/ros_usbnode/src/usb_device.c b/stm32/ros_usbnode/src/usb_device.c index 9116a692..3118b8eb 100755 --- a/stm32/ros_usbnode/src/usb_device.c +++ b/stm32/ros_usbnode/src/usb_device.c @@ -64,7 +64,7 @@ USBD_HandleTypeDef hUsbDeviceFS; void MX_USB_DEVICE_Init(void) { /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */ - +#if !(BOARD_YARDFORCE500_VARIANT_B) /* Rendering hardware reset harmless (no need to replug USB cable): */ GPIO_InitTypeDef GPIO_InitStruct = {0}; @@ -82,6 +82,7 @@ void MX_USB_DEVICE_Init(void) HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_Delay(5); +#endif /* Hardware reset rendered harmless! */ /* USER CODE END USB_DEVICE_Init_PreTreatment */ From e4aa722c955ab51dc05958e781b749f894d5f71c Mon Sep 17 00:00:00 2001 From: slashphotos Date: Sat, 1 Jul 2023 23:35:33 +0200 Subject: [PATCH 12/22] Add alternate function to blademotor usart pins --- stm32/ros_usbnode/src/blademotor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stm32/ros_usbnode/src/blademotor.c b/stm32/ros_usbnode/src/blademotor.c index 7ca6d3b4..2e77452f 100644 --- a/stm32/ros_usbnode/src/blademotor.c +++ b/stm32/ros_usbnode/src/blademotor.c @@ -116,12 +116,18 @@ void BLADEMOTOR_Init(void) GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF8_USART6; +#endif HAL_GPIO_Init(BLADEMOTOR_USART_RX_PORT, &GPIO_InitStruct); // TX GPIO_InitStruct.Pin = BLADEMOTOR_USART_TX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; +#if BOARD_YARDFORCE500_VARIANT_B + GPIO_InitStruct.Alternate = GPIO_AF8_USART6; +#endif HAL_GPIO_Init(BLADEMOTOR_USART_TX_PORT, &GPIO_InitStruct); BLADEMOTOR_USART_Handler.Instance = BLADEMOTOR_USART_INSTANCE; From 3433f277e1fe1f78c04c6e7f3d0b2c88885030af Mon Sep 17 00:00:00 2001 From: slashphotos Date: Tue, 4 Jul 2023 23:16:05 +0200 Subject: [PATCH 13/22] Fix infinite check of blademotor DMA and UART caused by RX/TX mistake. Use correct handle for ADC interrupt. --- stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c b/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c index 2d90ffa2..682e1c07 100644 --- a/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c +++ b/stm32/ros_usbnode/src/proxy_inc/stm32f4/stm32f4xx_it.c @@ -226,7 +226,7 @@ void ADC_IRQHandler(void) /* USER CODE BEGIN ADC_IRQn 0 */ /* USER CODE END ADC_IRQn 0 */ - HAL_ADC_IRQHandler(&hdma_adc); + HAL_ADC_IRQHandler(&ADC_Charging_Handle); /* USER CODE BEGIN ADC_IRQn 1 */ /* USER CODE END ADC_IRQn 1 */ @@ -284,7 +284,7 @@ void DMA2_Stream1_IRQHandler(void) /* USER CODE BEGIN DMA2_Stream1_IRQn 0 */ /* USER CODE END DMA2_Stream1_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_uart_blade_tx); + HAL_DMA_IRQHandler(&hdma_uart_blade_rx); /* USER CODE BEGIN DMA2_Stream1_IRQn 1 */ /* USER CODE END DMA2_Stream1_IRQn 1 */ @@ -298,7 +298,7 @@ void DMA2_Stream6_IRQHandler(void) /* USER CODE BEGIN DMA2_Stream6_IRQn 0 */ /* USER CODE END DMA2_Stream6_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_uart_blade_rx); + HAL_DMA_IRQHandler(&hdma_uart_blade_tx); /* USER CODE BEGIN DMA2_Stream6_IRQn 1 */ /* USER CODE END DMA2_Stream6_IRQn 1 */ From 86f1353bc0e355abca4e5207087d6fa7ea3b8625 Mon Sep 17 00:00:00 2001 From: slashphotos Date: Fri, 7 Jul 2023 19:54:42 +0200 Subject: [PATCH 14/22] Fix bug in ADC init. Use the right trigger. --- stm32/ros_usbnode/src/adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32/ros_usbnode/src/adc.c b/stm32/ros_usbnode/src/adc.c index d3ba0a63..0698b6e1 100644 --- a/stm32/ros_usbnode/src/adc.c +++ b/stm32/ros_usbnode/src/adc.c @@ -194,7 +194,7 @@ void ADC_Charging_Init(void) ADC_Charging_Handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; ADC_Charging_Handle.Init.Resolution = ADC_RESOLUTION_12B; ADC_Charging_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; - ADC_Charging_Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; + ADC_Charging_Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_CC2; ADC_Charging_Handle.Init.DMAContinuousRequests = DISABLE; ADC_Charging_Handle.Init.EOCSelection = ADC_EOC_SINGLE_CONV; #endif From d79052af2eed9bab98e8aa98eda7536922bdc5a8 Mon Sep 17 00:00:00 2001 From: Benjamin Lundgren Date: Thu, 31 Aug 2023 21:05:25 +0100 Subject: [PATCH 15/22] Fix 500b system clock --- stm32/ros_usbnode/platformio.ini | 2 +- stm32/ros_usbnode/src/adc.c | 2 +- stm32/ros_usbnode/src/main.c | 32 +++++++------------------------- 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/stm32/ros_usbnode/platformio.ini b/stm32/ros_usbnode/platformio.ini index 2054d439..6eaff3c1 100644 --- a/stm32/ros_usbnode/platformio.ini +++ b/stm32/ros_usbnode/platformio.ini @@ -42,7 +42,7 @@ platform_packages = platformio/tool-stm32duino@^1.0.1 platformio/tool-openocd@^2.1100.211028 platformio/tool-dfuutil@^1.11.0 -build_flags = -DBOARD_YARDFORCE500_VARIANT_B=1 -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom +build_flags = -DBOARD_YARDFORCE500_VARIANT_B=1 -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom -DHSE_VALUE=8000000u extra_scripts = pre:patch_usb.py pre:add_swo_viewer.py diff --git a/stm32/ros_usbnode/src/adc.c b/stm32/ros_usbnode/src/adc.c index 0698b6e1..b28b1fb3 100644 --- a/stm32/ros_usbnode/src/adc.c +++ b/stm32/ros_usbnode/src/adc.c @@ -191,7 +191,7 @@ void ADC_Charging_Init(void) ADC_Charging_Handle.Init.NbrOfConversion = 1; #if BOARD_YARDFORCE500_VARIANT_B - ADC_Charging_Handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; + ADC_Charging_Handle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; ADC_Charging_Handle.Init.Resolution = ADC_RESOLUTION_12B; ADC_Charging_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; ADC_Charging_Handle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_CC2; diff --git a/stm32/ros_usbnode/src/main.c b/stm32/ros_usbnode/src/main.c index aa334cde..ba7d736a 100644 --- a/stm32/ros_usbnode/src/main.c +++ b/stm32/ros_usbnode/src/main.c @@ -511,18 +511,14 @@ void SystemClock_Config(void) __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2); #endif /** Initializes the RCC Oscillators according to the specified parameters - * in the RCC_OscInitTypeDef structure. - */ -#if BOARD_YARDFORCE500_VARIANT_ORIG + * in the RCC_OscInitTypeDef structure. + */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_LSI; -#elif BOARD_YARDFORCE500_VARIANT_B - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_HSE; -#endif RCC_OscInitStruct.HSEState = RCC_HSE_ON; #if BOARD_YARDFORCE500_VARIANT_ORIG RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; -#endif RCC_OscInitStruct.HSIState = RCC_HSI_ON; +#endif RCC_OscInitStruct.LSIState = RCC_LSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; @@ -531,33 +527,19 @@ void SystemClock_Config(void) RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; #elif BOARD_YARDFORCE500_VARIANT_B RCC_OscInitStruct.PLL.PLLM = 4; - RCC_OscInitStruct.PLL.PLLN = 72; - RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; - RCC_OscInitStruct.PLL.PLLQ = 3; + RCC_OscInitStruct.PLL.PLLN = 144; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; + RCC_OscInitStruct.PLL.PLLQ = 6; #endif if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } -#if BOARD_YARDFORCE500_VARIANT_B /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; - RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; - RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; - RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; - RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; - - if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) - { - Error_Handler(); - } - #elif - /** Initializes the CPU, AHB and APB buses 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_DIV2; @@ -567,7 +549,7 @@ void SystemClock_Config(void) { Error_Handler(); } -#endif + #if BOARD_YARDFORCE500_VARIANT_ORIG // TODO: Is something like this needed for variant B? RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; From 69d6568cf312043afe36968d40b31087eafe4d54 Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:33:42 +0100 Subject: [PATCH 16/22] Fix UART pin definition so interrupt fires (Drive) --- stm32/ros_usbnode/src/drivemotor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stm32/ros_usbnode/src/drivemotor.c b/stm32/ros_usbnode/src/drivemotor.c index 85c919a3..e1fce60b 100644 --- a/stm32/ros_usbnode/src/drivemotor.c +++ b/stm32/ros_usbnode/src/drivemotor.c @@ -144,7 +144,7 @@ void DRIVEMOTOR_Init(void) DRIVEMOTORS_USART_USART_CLK_ENABLE(); // RX - GPIO_InitStruct.Pin = DRIVEMOTORS_USART_RX_PIN; + GPIO_InitStruct.Pin = DRIVEMOTORS_USART_TX_PIN|DRIVEMOTORS_USART_RX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; @@ -154,7 +154,7 @@ void DRIVEMOTOR_Init(void) HAL_GPIO_Init(DRIVEMOTORS_USART_RX_PORT, &GPIO_InitStruct); // TX - GPIO_InitStruct.Pin = DRIVEMOTORS_USART_TX_PIN; + GPIO_InitStruct.Pin = DRIVEMOTORS_USART_TX_PIN|DRIVEMOTORS_USART_RX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; From e4dbbf9f14a8413e3e7063583d0e22bf60fb0c1d Mon Sep 17 00:00:00 2001 From: wjcloudy <56305354+wjcloudy@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:34:28 +0100 Subject: [PATCH 17/22] Fix UART pin definition so interrupt fires (Blade) --- stm32/ros_usbnode/src/blademotor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stm32/ros_usbnode/src/blademotor.c b/stm32/ros_usbnode/src/blademotor.c index 2e77452f..2f989a48 100644 --- a/stm32/ros_usbnode/src/blademotor.c +++ b/stm32/ros_usbnode/src/blademotor.c @@ -112,7 +112,7 @@ void BLADEMOTOR_Init(void) #endif // RX - GPIO_InitStruct.Pin = BLADEMOTOR_USART_RX_PIN; + GPIO_InitStruct.Pin = BLADEMOTOR_USART_TX_PIN|BLADEMOTOR_USART_RX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; @@ -122,7 +122,7 @@ void BLADEMOTOR_Init(void) HAL_GPIO_Init(BLADEMOTOR_USART_RX_PORT, &GPIO_InitStruct); // TX - GPIO_InitStruct.Pin = BLADEMOTOR_USART_TX_PIN; + GPIO_InitStruct.Pin = BLADEMOTOR_USART_TX_PIN|BLADEMOTOR_USART_RX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; #if BOARD_YARDFORCE500_VARIANT_B @@ -279,4 +279,4 @@ void BLADEMOTOR_ReceiveIT(void) /****************************************************************************** * Private Functions -*******************************************************************************/ \ No newline at end of file +*******************************************************************************/ From 1c0535b50f64e91b2a413811660946d639bcce09 Mon Sep 17 00:00:00 2001 From: Jeremy Salwen Date: Fri, 10 May 2024 21:28:49 -0400 Subject: [PATCH 18/22] Add back __HAL_RCC_PWR_CLK_ENABLE() call for 500 classic This was possibly the bug that caused the 500b branch not to work on the 500 classic? --- stm32/ros_usbnode/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32/ros_usbnode/src/main.c b/stm32/ros_usbnode/src/main.c index ba7d736a..5157aa53 100644 --- a/stm32/ros_usbnode/src/main.c +++ b/stm32/ros_usbnode/src/main.c @@ -97,8 +97,8 @@ int main(void) SystemClock_Config(); #if BOARD_YARDFORCE500_VARIANT_ORIG - // TODO: Check if some equivalent is needed for the STM32f4 __HAL_RCC_AFIO_CLK_ENABLE(); + __HAL_RCC_PWR_CLK_ENABLE(); #endif MX_DMA_Init(); From b465a18f65bb827b536f0b46517e313205c54bb3 Mon Sep 17 00:00:00 2001 From: Jeremy Salwen Date: Fri, 10 May 2024 21:33:29 -0400 Subject: [PATCH 19/22] Fix double definition warning in board.h for 500b --- stm32/ros_usbnode/include/board.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stm32/ros_usbnode/include/board.h b/stm32/ros_usbnode/include/board.h index 4a290480..b75d56a0 100755 --- a/stm32/ros_usbnode/include/board.h +++ b/stm32/ros_usbnode/include/board.h @@ -189,9 +189,10 @@ extern "C" #define WHEEL_LIFT_RED_PORT GPIOD /* Play button - (LOW when pressed) */ -#define PLAY_BUTTON_PIN GPIO_PIN_7 #if BOARD_YARDFORCE500_VARIANT_B #define PLAY_BUTTON_PIN GPIO_PIN_9 +#else +#define PLAY_BUTTON_PIN GPIO_PIN_7 #endif #define PLAY_BUTTON_PORT GPIOC #define PLAY_BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() From 3aa228509b179e94a554a34a957c28fcc74812b0 Mon Sep 17 00:00:00 2001 From: Jeremy Salwen Date: Fri, 10 May 2024 22:41:39 -0400 Subject: [PATCH 20/22] Remove accidentally committed files --- stm32/mainboard_firmware/debug.cfg | 7 ------- stm32/mainboard_firmware/orig_firmware.bin | Bin 262144 -> 0 bytes 2 files changed, 7 deletions(-) delete mode 100644 stm32/mainboard_firmware/debug.cfg delete mode 100644 stm32/mainboard_firmware/orig_firmware.bin diff --git a/stm32/mainboard_firmware/debug.cfg b/stm32/mainboard_firmware/debug.cfg deleted file mode 100644 index 59fa6fd3..00000000 --- a/stm32/mainboard_firmware/debug.cfg +++ /dev/null @@ -1,7 +0,0 @@ -source [find interface/stlink-v2.cfg] -source [find target/stm32f4x.cfg] -init - -tpiu config internal - uart off 84000000 -itm ports on - diff --git a/stm32/mainboard_firmware/orig_firmware.bin b/stm32/mainboard_firmware/orig_firmware.bin deleted file mode 100644 index b472226f124a52d79b8acecb454af5136f791871..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262144 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%0D}`9&Q;nv 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 Date: Mon, 29 Jul 2024 00:32:12 -0400 Subject: [PATCH 21/22] Fix startup assembly bugs in 500b Root cause: 1. .s files are not processed by the preprocessor, since they are passed directly to the assembler. Only .S files are processed by gcc, which uses the preprocessor before sending to the assembler. Thus the #includes in startup_stdm32f.s had no effect. 2. The proxy_inc/**/*.s files were accidentally *not* filtered from the sources like the .c files in the same directory. Thus the startup code for both boards was unconditionally included. 3. The two bugs cancelled each other out for the 500b, resulting in the correct startup code being built. However, it was incorrectly including the 500b's startup code in the non-500b build. --- stm32/ros_usbnode/platformio.ini | 1 + .../stm32f1/{startup_stm32f1xx.s => startup_stm32f1xx.S} | 0 .../stm32f4/{startup_stm32f4xx.s => startup_stm32f4xx.S} | 0 stm32/ros_usbnode/src/startup_stm32f.S | 5 +++++ stm32/ros_usbnode/src/startup_stm32f.s | 5 ----- 5 files changed, 6 insertions(+), 5 deletions(-) rename stm32/ros_usbnode/src/proxy_inc/stm32f1/{startup_stm32f1xx.s => startup_stm32f1xx.S} (100%) rename stm32/ros_usbnode/src/proxy_inc/stm32f4/{startup_stm32f4xx.s => startup_stm32f4xx.S} (100%) create mode 100644 stm32/ros_usbnode/src/startup_stm32f.S delete mode 100644 stm32/ros_usbnode/src/startup_stm32f.s diff --git a/stm32/ros_usbnode/platformio.ini b/stm32/ros_usbnode/platformio.ini index 6eaff3c1..8a319ae7 100644 --- a/stm32/ros_usbnode/platformio.ini +++ b/stm32/ros_usbnode/platformio.ini @@ -19,6 +19,7 @@ build_src_filter = -<.git/> -<.svn/> - + - [env:Yardforce500] board = genericSTM32F103VC diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f1/startup_stm32f1xx.s b/stm32/ros_usbnode/src/proxy_inc/stm32f1/startup_stm32f1xx.S similarity index 100% rename from stm32/ros_usbnode/src/proxy_inc/stm32f1/startup_stm32f1xx.s rename to stm32/ros_usbnode/src/proxy_inc/stm32f1/startup_stm32f1xx.S diff --git a/stm32/ros_usbnode/src/proxy_inc/stm32f4/startup_stm32f4xx.s b/stm32/ros_usbnode/src/proxy_inc/stm32f4/startup_stm32f4xx.S similarity index 100% rename from stm32/ros_usbnode/src/proxy_inc/stm32f4/startup_stm32f4xx.s rename to stm32/ros_usbnode/src/proxy_inc/stm32f4/startup_stm32f4xx.S diff --git a/stm32/ros_usbnode/src/startup_stm32f.S b/stm32/ros_usbnode/src/startup_stm32f.S new file mode 100644 index 00000000..9db75d55 --- /dev/null +++ b/stm32/ros_usbnode/src/startup_stm32f.S @@ -0,0 +1,5 @@ +#if BOARD_YARDFORCE500_VARIANT_ORIG +#include "proxy_inc/stm32f1/startup_stm32f1xx.S" +#elif BOARD_YARDFORCE500_VARIANT_B +#include "proxy_inc/stm32f4/startup_stm32f4xx.S" +#endif diff --git a/stm32/ros_usbnode/src/startup_stm32f.s b/stm32/ros_usbnode/src/startup_stm32f.s deleted file mode 100644 index bd07c757..00000000 --- a/stm32/ros_usbnode/src/startup_stm32f.s +++ /dev/null @@ -1,5 +0,0 @@ -#if BOARD_YARDFORCE500_VARIANT_ORIG -#include "proxy_inc/stm32f1/startup_stm32f1xx.s" -#elif BOARD_YARDFORCE500_VARIANT_B -#include "proxy_inc/stm32f4/startup_stm32f4xx.s" -#endif From 3acdbe2aa1e9763b6f93698154ab2618818b4e61 Mon Sep 17 00:00:00 2001 From: Jeremy Salwen Date: Sat, 10 Aug 2024 21:53:43 -0400 Subject: [PATCH 22/22] Add framework-stm32cubef4 to platform.ini @brupje needed this to compile with his version of platformio. --- stm32/ros_usbnode/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/stm32/ros_usbnode/platformio.ini b/stm32/ros_usbnode/platformio.ini index 8a319ae7..c04e7c02 100644 --- a/stm32/ros_usbnode/platformio.ini +++ b/stm32/ros_usbnode/platformio.ini @@ -43,6 +43,7 @@ platform_packages = platformio/tool-stm32duino@^1.0.1 platformio/tool-openocd@^2.1100.211028 platformio/tool-dfuutil@^1.11.0 + platformio/framework-stm32cubef4@^1.26.2 build_flags = -DBOARD_YARDFORCE500_VARIANT_B=1 -Wl,--undefined,_printf_float -O2 -Isrc/ros/ros_lib -Isrc/ros/ros_custom -DHSE_VALUE=8000000u extra_scripts = pre:patch_usb.py