diff --git a/BuildScript.py b/BuildScript.py index a65e23c..e57eb9d 100644 --- a/BuildScript.py +++ b/BuildScript.py @@ -3,7 +3,7 @@ @author Daniel Starke @copyright Copyright 2020-2022 Daniel Starke @date 2020-10-20 -@version 2022-03-27 +@version 2022-05-29 """ import os @@ -1615,6 +1615,25 @@ def getMcuVariant(boardId, cpu, mcu): return {"error": "Invalid value in CPU field for board %s. Possible values for %s are: %s." % (boardId, result["partNumber"] + result["variant"], cores)} return result +def getOpenOcdTarget(mcu, warn = False): + """ Returns the PlatformIO OpenOCD target name for the given MCU. """ + # dual bank firmware download is not support + from platformio.project.config import ProjectConfig + projectConfig = ProjectConfig() + corePath = projectConfig.get_optional_dir("core") + if corePath: + targetPath = os.path.join(corePath, "packages", "tool-openocd", "scripts", "target") + pattern = re.compile("^stm32[^_]+\.cfg$", re.I) + files = [f for f in os.listdir(targetPath) if os.path.isfile(os.path.join(targetPath, f)) and pattern.match(f)] + lowerMcu = mcu.lower() + for file in files: + target = file.replace(".cfg", "") + if re.match("^" + target.replace("x", ".*") + ".*$", lowerMcu, re.I): + return target; + if warn: + print("Warning: OpenOCD target not found for: " + mcu, file=sys.stderr) + return None + def getSvdFile(mcu, warn = False): """ Returns the PlatformIO SVD file name for the given MCU. """ from platformio.project.config import ProjectConfig @@ -1632,12 +1651,15 @@ def getSvdFile(mcu, warn = False): if warn: print("Warning: SVD file name list file not found: " + svdMapPath, file=sys.stderr) return None - + # add target specific pre-processor defines if __name__ == 'SCons.Script': Import("env") + if not "stm32cube" in map(str.lower, env.GetProjectOption("framework")): + Return() + global_env = DefaultEnvironment() board = env.BoardConfig() @@ -1649,7 +1671,13 @@ def getSvdFile(mcu, warn = False): env.Append(CPPDEFINES=userMcu) # for this library global_env.Append(CPPDEFINES=userMcu) # for all other components env.Append(CPPDEFINES=[mcu["partNumber"] + mcu["variant"], mcu["partNumber"], mcu["define"], "ARDUINO=" + str(ARDUINO), "ARDUINO_ARCH_STM32"]) # for this library - global_env.Append(CPPDEFINES=[mcu["partNumber"] + mcu["variant"], mcu["partNumber"], mcu["define"], "ARDUINO=" + str(ARDUINO), "ARDUINO_ARCH_STM32"], LINKFLAGS=["-Wl,-u,_printf_float,-u,_scanf_float"]) # for all other components + global_env.Append(CPPDEFINES=[mcu["partNumber"] + mcu["variant"], mcu["partNumber"], mcu["define"], "ARDUINO=" + str(ARDUINO), "ARDUINO_ARCH_STM32"]) # for all other components + # include float support for printf/scanf like functions + cppDefines = env.get("CPPDEFINES", []) + if not "STM32CUBEDUINO_DISABLE_PRINTF_FLOAT" in cppDefines: + global_env.Append(LINKFLAGS=["-Wl,-u,_printf_float"]) + if not "STM32CUBEDUINO_DISABLE_SCANF_FLOAT" in cppDefines: + global_env.Append(LINKFLAGS=["-Wl,-u,_scanf_float"]) # helper application to create PlatformIO board ini files @@ -1669,6 +1697,7 @@ def getSvdFile(mcu, warn = False): print("Error: " + mcu["error"], file=sys.stderr) sys.exit(1) + ocdTarget = getOpenOcdTarget(mcu["partNumber"], True) svdFile = getSvdFile(mcu["partNumber"], True) boardName = "" @@ -1691,7 +1720,7 @@ def getSvdFile(mcu, warn = False): "onboard_tools": [ "stlink" ], - "openocd_target": "{mcu:.7s}x", + "openocd_target": "{OCD}", "svd_path": "{SVD}" }}, "frameworks": "stm32cube", @@ -1717,6 +1746,7 @@ def getSvdFile(mcu, warn = False): PROD=mcu["define"], dev=sys.argv[2].lower()[:11], DEV=sys.argv[2].upper()[:11], + OCD=(ocdTarget if ocdTarget else ""), SVD=(svdFile if svdFile else ""), RAM=mcu["ram"] * 1024, FLASH=mcu["flash"] * 1024, diff --git a/README.md b/README.md index 34e5d23..796539f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ Getting Started =============== A getting started guide can be found [here](doc/starting.md). -Some small examples are also included [here](examples). +Some small examples are also included [here](examples). +Note that the examples are configured to be compiled from within this repository. Done ==== @@ -52,7 +53,8 @@ Done - [x] shiftOut()/shiftIn() - [x] SPI (untested) - [x] I2C -- [ ] [EEPROM](https://www.st.com/resource/en/application_note/dm00311483-eeprom-emulation-techniques-and-software-for-stm32-microcontrollers-stmicroelectronics.pdf) +- [x] EEPROM +- [ ] [EEPROM flash emulation](https://www.st.com/resource/en/application_note/dm00311483-eeprom-emulation-techniques-and-software-for-stm32-microcontrollers-stmicroelectronics.pdf) - [x] Pluggable USB - [x] USB CDC - [ ] USB HID @@ -76,64 +78,71 @@ Done Special Macros ============== -|Name |Meaning -|-------------------------------------|--------------------------------------------------------------- -|`ARDUINO` |Defines the compatible Arduino version. -|`ARDUINO_API_VERSION` |Defines the compatible Arduino API version. -|`ARDUINO_ARCH_STM32` |Defines the Arduino target architecture. -|`STM32CUBEDUINO` |Defines the version of the STM32CubeDuino library as string. -|`STM32CUBEDUINO` |Defines the version of the STM32CubeDuino library as string. -|`STM32CUBEDUINO_MAJOR` |Defines the major version number of the STM32CubeDuino library. -|`STM32CUBEDUINO_MINOR` |Defines the minor version number of the STM32CubeDuino library. -|`STM32CUBEDUINO_PATCH` |Defines the patch version number of the STM32CubeDuino library. -|`STM32CUBEDUINO_VERSION(x, y, z)` |Transforms the given major, minor and patch version number into a single number for comparison. -|`STM32CUBEDUINO_VERSION_CODE` |Defines the version of the STM32CubeDuino library as single number for comparison. -|`STM32CUBEDUINO_SMALL_FLASH` |May be defined by the user to trade off speed for small flash usage. -|`STM32CUBEDUINO_FLOAT_STRING` |May be defined by the user to implement only single precision float to string conversion functions. -|`STM32CUBEDUINO_LEGACY_API` |May be defined by the user to remove non-Arduino functions for better compatibility. -|`STM32CUBEDUINO_INTERNALS` |May be defined by the user to get access to some internal functions. These are not automatically exported. The user needs to declare a function prototype to access these. -|`STM32CUBEDUINO_CUSTOM_IRQ_HANDLERS` |May be defined by the user to define custom STM32 HAL IRQ handlers. All STM32CubeDuino specific handlers will be disabled. -|`STM32CUBEDUINO_MAP_ALL_IRQS` |May be defined by the user to unconditionally map all STM32 HAL IRQ handlers to STM32CubeDino specific ones. -|`STM32CUBEDUINO_DISABLE_ADC` |May be defined by the user to disable ADC related STM32CubeDino functions. -|`STM32CUBEDUINO_DISABLE_CDC` |May be defined by the user to disable USB serial (CDC) related STM32CubeDino functions. -|`STM32CUBEDUINO_DISABLE_DAC` |May be defined by the user to disable DAC related STM32CubeDino functions. -|`STM32CUBEDUINO_DISABLE_EXTI` |May be defined by the user to disable external interrupt related STM32CubeDino functions. -|`STM32CUBEDUINO_DISABLE_I2C` |May be defined by the user to disable I2C related STM32CubeDino functions. -|`STM32CUBEDUINO_DISABLE_PWM` |May be defined by the user to disable PWM related STM32CubeDino functions. -|`STM32CUBEDUINO_DISABLE_SERIAL` |May be defined by the user to disable hardware serial related STM32CubeDino functions. -|`STM32CUBEDUINO_DISABLE_SPI` |May be defined by the user to disable SPI related STM32CubeDino functions. -|`STM32CUBEDUINO_DISABLE_TIMER` |May be defined by the user to disable hardware timer related STM32CubeDino functions (not PWM). -|`STM32CUBEDUINO_DISABLE_USB` |May be defined by the user to disable USB related STM32CubeDino functions. -|`NO_GPL` |May be defined by the user to exclude GPL licensed code. This affects only support functions included for better Arduino AVR compatibility. -|`SERIAL_RX_BUFFER_SIZE` |May be defined by the user to change the serial reception buffer size. Defaults to 64 bytes. -|`SERIAL_TX_BUFFER_SIZE` |May be defined by the user to change the serial transmission buffer size. Defaults to 64 bytes. -|`TWOWIRE_RX_BUFFER_SIZE` |May be defined by the user to change the I2C reception buffer size. Defaults to 32 bytes. -|`TWOWIRE_TX_BUFFER_SIZE` |May be defined by the user to change the I2C transmission buffer size. Defaults to 32 bytes. -|`SPI_TRANSFER_TIMEOUT` |May be defined by the user to change the SPI timeout in milliseconds. Set to HAL_MAX_DELAY for no timeout. Defaults to 1000ms. -|`ACTIVATE_USB_PORT` |May be defined by the user with custom logic to force a USB enumeration, e.g. by pulling down D+. -|`USB_EP_SIZE` |May be defined by the user to change the USB single endpoint size. Defaults to 32 or 64 bytes depending on the target platform. -|`USB_PRODUCT` |May be defined by the user to change the USB product name. Defaults to `"USB IO Board"`. -|`USB_MANUFACTURER` |May be defined by the user to change the USB manufacturer name. Defaults to `"STMicroelectronics"` depending on `USB_VID`. -|`I_CACHE_DISABLED` |May be defined by the user to disable instruction cache. -|`D_CACHE_DISABLED` |May be defined by the user to disable data cache. -|`HAVE_HWSERIAL0` |Defined if the hardware serial API is available. -|`HAVE_CDCSERIAL` |Defined if the CDC (serial USB) API is available. -|`USBCON` |Defined if the USB API is available. -|`USB_ENDPOINTS` |Defined with the number of available USB endpoints. -|`EXTI_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for external interrupts. -|`EXTI_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for external interrupts. -|`I2C_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for I2C interrupts. -|`I2C_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for I2C interrupts. -|`SYSTICK_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for SYSTICK interrupts. -|`SYSTICK_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for SYSTICK interrupts. -|`TIMER_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for TIMER interrupts. -|`TIMER_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for TIMER interrupts. -|`UART_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for UART/USART interrupts. -|`UART_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for UART/USART interrupts. -|`USB_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for USB interrupts. -|`USB_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for USB interrupts. -|`USB_VID` |Needs to be defined in `board.hpp` to set the USB vendor ID. E.g. `0x2341` for Arduino LLC. -|`USB_PID` |Needs to be defined in `board.hpp` to set the USB product ID. E.g. `0x8036` for Leonardo (CDC ACM, HID). +|Name |Meaning +|--------------------------------------|--------------------------------------------------------------- +|`ARDUINO` |Defines the compatible Arduino version. +|`ARDUINO_API_VERSION` |Defines the compatible Arduino API version. +|`ARDUINO_ARCH_STM32` |Defines the Arduino target architecture. +|`STM32CUBEDUINO` |Defines the version of the STM32CubeDuino library as string. +|`STM32CUBEDUINO` |Defines the version of the STM32CubeDuino library as string. +|`STM32CUBEDUINO_MAJOR` |Defines the major version number of the STM32CubeDuino library. +|`STM32CUBEDUINO_MINOR` |Defines the minor version number of the STM32CubeDuino library. +|`STM32CUBEDUINO_PATCH` |Defines the patch version number of the STM32CubeDuino library. +|`STM32CUBEDUINO_VERSION(x, y, z)` |Transforms the given major, minor and patch version number into a single number for comparison. +|`STM32CUBEDUINO_VERSION_CODE` |Defines the version of the STM32CubeDuino library as single number for comparison. +|`STM32CUBEDUINO_SMALL_FLASH` |May be defined by the user to trade off speed for small flash usage. +|`STM32CUBEDUINO_FLOAT_STRING` |May be defined by the user to implement only single precision float to string conversion functions. +|`STM32CUBEDUINO_LEGACY_API` |May be defined by the user to remove non-Arduino functions for better compatibility. +|`STM32CUBEDUINO_INTERNALS` |May be defined by the user to get access to some internal functions. These are not automatically exported. The user needs to declare a function prototype to access these. +|`STM32CUBEDUINO_CUSTOM_IRQ_HANDLERS` |May be defined by the user to define custom STM32 HAL IRQ handlers. All STM32CubeDuino specific handlers will be disabled. +|`STM32CUBEDUINO_MAP_ALL_IRQS` |May be defined by the user to unconditionally map all STM32 HAL IRQ handlers to STM32CubeDino specific ones. +|`STM32CUBEDUINO_DISABLE_ADC` |May be defined by the user to disable ADC related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_CDC` |May be defined by the user to disable USB serial (CDC) related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_DAC` |May be defined by the user to disable DAC related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_EEPROM` |May be defined by the user to disable EEPROM related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_EXTI` |May be defined by the user to disable external interrupt related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_I2C` |May be defined by the user to disable I2C related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_PWM` |May be defined by the user to disable PWM related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_PRINTF_FLOAT` |May be defined by the user to disable float support in printf like functions. +|`STM32CUBEDUINO_DISABLE_SCANF_FLOAT` |May be defined by the user to disable float support in scanf like functions. +|`STM32CUBEDUINO_DISABLE_SERIAL` |May be defined by the user to disable hardware serial related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_SPI` |May be defined by the user to disable SPI related STM32CubeDino functions. +|`STM32CUBEDUINO_DISABLE_STRING` |May be defined by the user to disable the Arduino String class and related functions. +|`STM32CUBEDUINO_DISABLE_TIMER` |May be defined by the user to disable hardware timer related STM32CubeDino functions (not PWM). +|`STM32CUBEDUINO_DISABLE_USB` |May be defined by the user to disable USB related STM32CubeDino functions. +|`NO_GPL` |May be defined by the user to exclude GPL licensed code. This affects only support functions included for better Arduino AVR compatibility. +|`SERIAL_RX_BUFFER_SIZE` |May be defined by the user to change the serial reception buffer size. Defaults to 64 bytes. +|`SERIAL_TX_BUFFER_SIZE` |May be defined by the user to change the serial transmission buffer size. Defaults to 64 bytes. +|`TWOWIRE_RX_BUFFER_SIZE` |May be defined by the user to change the I2C reception buffer size. Defaults to 32 bytes. +|`TWOWIRE_TX_BUFFER_SIZE` |May be defined by the user to change the I2C transmission buffer size. Defaults to 32 bytes. +|`SPI_TRANSFER_TIMEOUT` |May be defined by the user to change the SPI timeout in milliseconds. Set to `HAL_MAX_DELAY` for no timeout. Defaults to 1000ms. +|`ACTIVATE_USB_PORT` |May be defined by the user with custom logic to force a USB enumeration, e.g. by pulling down D+. +|`USB_EP_SIZE` |May be defined by the user to change the USB single endpoint size. Defaults to 32 or 64 bytes depending on the target platform. +|`USB_RX_SIZE` |May be defined by the user to change the USB reception buffer size. This needs to be at least `2 * USB_EP_SIZE`. Defaults to `2 * USB_EP_SIZE`. +|`USB_TX_SIZE` |May be defined by the user to change the USB transmission buffer size. This needs to be a multiple of `USB_EP_SIZE` and at least two times its size. Defaults to `2 * USB_EP_SIZE`. +|`USB_PRODUCT` |May be defined by the user to change the USB product name. Defaults to `"USB IO Board"`. +|`USB_MANUFACTURER` |May be defined by the user to change the USB manufacturer name. Defaults to `"STMicroelectronics"` depending on `USB_VID`. +|`I_CACHE_DISABLED` |May be defined by the user to disable instruction cache. +|`D_CACHE_DISABLED` |May be defined by the user to disable data cache. +|`HAVE_HWSERIAL0` |Defined if the hardware serial API is available. +|`HAVE_CDCSERIAL` |Defined if the CDC (serial USB) API is available. +|`USBCON` |Defined if the USB API is available. +|`USB_ENDPOINTS` |Defined with the number of available USB endpoints. +|`EXTI_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for external interrupts. +|`EXTI_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for external interrupts. +|`I2C_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for I2C interrupts. +|`I2C_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for I2C interrupts. +|`SYSTICK_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for SYSTICK interrupts. +|`SYSTICK_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for SYSTICK interrupts. +|`TIMER_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for TIMER interrupts. +|`TIMER_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for TIMER interrupts. +|`UART_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for UART/USART interrupts. +|`UART_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for UART/USART interrupts. +|`USB_IRQ_PRIO` |Needs to be defined in `board.hpp` to set the priority for USB interrupts. +|`USB_IRQ_SUBPRIO` |Needs to be defined in `board.hpp` to set the sub-priority for USB interrupts. +|`USB_VID` |Needs to be defined in `board.hpp` to set the USB vendor ID. E.g. `0x2341` for Arduino LLC. +|`USB_PID` |Needs to be defined in `board.hpp` to set the USB product ID. E.g. `0x8036` for Leonardo (CDC ACM, HID). +|`USB_TX_TRANSACTIONAL` |Needs to be defined in `board.hpp` if the used STM32 HAL library handles multi-packet USB transactions with a single `HAL_PCD_DataInStageCallback()` call. Implementation Notes ==================== @@ -181,7 +190,22 @@ FAQ **A:** Most likely not. The code was written with the assumption that GCC is used to simplify code design in several places. **Q:** How can I simply reduce the binary size? -**A:** Define the macros `STM32CUBEDUINO_SMALL_FLASH` and `STM32CUBEDUINO_FLOAT_STRING` in the board definition or via build flag. This trades off execution speed for flash size. Additionally, the build flag `-ffast-math` can improve code size. Also try to disable unused periphery by defining `STM32CUBEDUINO_DISABLE_USB` for example. +**A:** There are several simple ways to reduce the needed flash size. Some with speed penalties. +- Define the macros `STM32CUBEDUINO_SMALL_FLASH` and `STM32CUBEDUINO_FLOAT_STRING` in the board definition or via build flag. This trades off execution speed for flash size. +- Adding the build flag `-ffast-math` can improve code size. +- Avoid floating point math all together. +- Try to disable unused periphery and functions by defining `STM32CUBEDUINO_DISABLE_PRINTF_FLOAT` for example. See the complete list of disabling macros above. +- Use the compiler flag `-Os`. This should be active by default. + +**Q:** How can I improve execution speed/throughput? +**A:** The biggest benefit usually has the use of better algorithm. Minor improvements can be archives with the following steps. +- Add the build flag `-ffast-math` for faster, but less IEEE 754 conformance in floating point math. +- Use FIFO sizes to a power of 2 (e.g. for `USB_TX_SIZE`). This makes the compiler use simple binary AND which is much faster than integer division. +- Use larger periphery buffers. +- Try offloading tasks to DMA channels. Note that the Arduino API offers no functions for this. You can use the STM32 HAL API for example. +- Pass larger structures by reference/pointer instead of copy. Pass native types (e.g. `int`) by value. +- Use `const` were possible. +- Enable instruction and data cache via STM32 HAL API if available for the target MCU. E.g. via `SCB_EnableICache()`. Both is usually enabled by default in STM32CubeDuino. **Q:** How do I generate a custom `initVariant()` function for my board? **A:** Download the [STM32CubeIDE](https://www.st.com/en/development-tools/stm32cubeide.html). Create a new `STM32 Project` for the used chip in there and activate all relevant peripherals. Complete the clock configuration and generate the code. The content from the generated `SystemClock_Config()` function in `main.c` is the base for the new `initVariant()` function. Finally, replace all calls to `Error_Handler()` with `systemErrorHandler()`. See also [Getting Started](doc/starting.md). diff --git a/etc/test/build.sh b/etc/test/build.sh index c689379..49fb9d6 100644 --- a/etc/test/build.sh +++ b/etc/test/build.sh @@ -3,7 +3,7 @@ # @author Daniel Starke # @copyright Copyright 2022 Daniel Starke # @date 2022-03-20 -# @version 2022-03-29 +# @version 2022-04-18 # # Running from within the library root directory. # The following environment variables are used: @@ -56,8 +56,11 @@ done <<"_LIST" -DSTM32CUBEDUINO_DISABLE_EXTI -DSTM32CUBEDUINO_DISABLE_I2C -DSTM32CUBEDUINO_DISABLE_PWM +-DSTM32CUBEDUINO_DISABLE_PRINTF_FLOAT +-DSTM32CUBEDUINO_DISABLE_SCANF_FLOAT -DSTM32CUBEDUINO_DISABLE_SERIAL -DSTM32CUBEDUINO_DISABLE_SPI +-DSTM32CUBEDUINO_DISABLE_STRING -DSTM32CUBEDUINO_DISABLE_TIMER -DSTM32CUBEDUINO_DISABLE_USB _LIST diff --git a/examples/BluePill/Blinky/src/bluepill/board.cpp b/examples/BluePill/Blinky/src/bluepill/board.cpp index 3437fc8..fdc9e23 100644 --- a/examples/BluePill/Blinky/src/bluepill/board.cpp +++ b/examples/BluePill/Blinky/src/bluepill/board.cpp @@ -33,8 +33,7 @@ void initVariant() { systemErrorHandler(); } /* 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.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; diff --git a/examples/GreenPill/Blinky/boards/greenpill.json b/examples/GreenPill/Blinky/boards/greenpill.json new file mode 100644 index 0000000..a042e3c --- /dev/null +++ b/examples/GreenPill/Blinky/boards/greenpill.json @@ -0,0 +1,37 @@ +{ + "build": { + "core": "stm32", + "cpu": "cortex-m0plus", + "mcu": "stm32l053c8t6", + "product_line": "STM32L053xx", + "extra_flags": "-DUSB_VID=0x2341 -DUSB_PID=0x8036" + }, + "debug": { + "default_tools": [ + "stlink" + ], + "jlink_device": "STM32L053C8", + "onboard_tools": [ + "stlink" + ], + "openocd_target": "stm32l0", + "svd_path": "STM32L053x.svd" + }, + "frameworks": "stm32cube", + "name": "greenpill", + "upload": { + "maximum_ram_size": 8192, + "maximum_size": 65536, + "protocol": "stlink", + "protocols": [ + "jlink", + "stlink", + "blackmagic", + "serial", + "mbed", + "dfu" + ] + }, + "url": "https://stm32-base.org/boards/STM32L053C8T6-Green-Pill", + "vendor": "Generic" +} diff --git a/examples/GreenPill/Blinky/platformio.ini b/examples/GreenPill/Blinky/platformio.ini new file mode 100644 index 0000000..4b9357c --- /dev/null +++ b/examples/GreenPill/Blinky/platformio.ini @@ -0,0 +1,16 @@ +[platformio] +workspace_dir = bin +src_dir = src +lib_dir = ../../../.. + +[common] +build_flags = -Wall -Wextra -Wformat -pedantic -Wshadow -Wconversion -Wparentheses -Wunused -Wno-missing-field-initializers + +[env:greenpill] +platform = ststm32 +platform_packages = toolchain-gccarmnoneeabi@1.90201.191206 +framework = stm32cube +board = greenpill +build_flags = -fno-strict-aliasing -I${PROJECTSRC_DIR}/greenpill -DNO_GPL +src_build_flags = ${common.build_flags} +debug_tool = stlink diff --git a/examples/GreenPill/Blinky/src/greenpill/board.cpp b/examples/GreenPill/Blinky/src/greenpill/board.cpp new file mode 100644 index 0000000..172ba63 --- /dev/null +++ b/examples/GreenPill/Blinky/src/greenpill/board.cpp @@ -0,0 +1,52 @@ +/** + * @file board.cpp + * @author Daniel Starke + * @copyright Copyright 2022 Daniel Starke + * @date 2022-05-29 + * @version 2022-05-29 + */ +#include +#include + + +/* exported variables */ +HardwareSerial Serial1(USART1, getIrqNumFor(USART1), PA_10, PA_9, 4, 4); + + +/** + * Initializes this board by configuring the system clock base. + */ +void initVariant() { + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + + /* Configure the main internal regulator output voltage */ + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + /* Initializes the RCC Oscillators according to the specified parameters in the RCC_OscInitTypeDef structure. */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_12; + RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_3; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { + systemErrorHandler(); + } + /* 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_DIV1; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) { + systemErrorHandler(); + } + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USB; + PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; + PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { + systemErrorHandler(); + } +} diff --git a/examples/GreenPill/Blinky/src/greenpill/board.hpp b/examples/GreenPill/Blinky/src/greenpill/board.hpp new file mode 100644 index 0000000..5cfa4fc --- /dev/null +++ b/examples/GreenPill/Blinky/src/greenpill/board.hpp @@ -0,0 +1,47 @@ +/** + * @file board.hpp + * @author Daniel Starke + * @copyright Copyright 2022 Daniel Starke + * @date 2022-05-29 + * @version 2022-05-29 + */ +#ifndef __GREENPILL_HPP__ +#define __GREENPILL_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef __STM32L053xx_H +#error Missing include of stm32l053xx.h. Please define STM32L053xx. +#endif + + +#define USB_IRQ_PRIO 0 +#define USB_IRQ_SUBPRIO 0 + +#define UART_IRQ_PRIO 1 +#define UART_IRQ_SUBPRIO 0 + +#define EXTI_IRQ_PRIO 3 +#define EXTI_IRQ_SUBPRIO 0 + +#define TIMER_IRQ_PRIO 4 +#define TIMER_IRQ_SUBPRIO 0 + +#define I2C_IRQ_PRIO 5 +#define I2C_IRQ_SUBPRIO 0 + + +/* pin aliases */ +/* may be PA_0 for some variants */ +#define LED_BUILTIN PA_1 + + +#endif /* __GREENPILL_HPP__ */ diff --git a/examples/GreenPill/Blinky/src/main.cpp b/examples/GreenPill/Blinky/src/main.cpp new file mode 100644 index 0000000..30234f9 --- /dev/null +++ b/examples/GreenPill/Blinky/src/main.cpp @@ -0,0 +1,38 @@ +/** + * @file main.cpp + * @author Daniel Starke + * @copyright Copyright 2022 Daniel Starke + * @date 2022-05-29 + * @version 2022-05-29 + * + * Simple blinky and EEPROM example with serial output. + */ +#include +#include + + +void setup() { + Serial1.begin(115200); + delay(100); + pinMode(LED_BUILTIN, OUTPUT); + Serial1.println("begin"); + /* EEPROM */ + Serial1.print("EEPROM size: "); + Serial1.println(EEPROM.length()); + /* boot count */ + uint32_t bootCount; + EEPROM.get(0, bootCount); + Serial1.print("boot count: "); + Serial1.println(bootCount); + bootCount++; + EEPROM.put(0, bootCount); +} + + +void loop() { + static bool value = false; + Serial1.println(value ? "LOW" : "HIGH"); + digitalWrite(LED_BUILTIN, uint32_t(value)); + value = !value; + delay(500); +} diff --git a/keywords.txt b/keywords.txt index 2e2d895..0bc4ad0 100644 --- a/keywords.txt +++ b/keywords.txt @@ -2,7 +2,7 @@ # @author Daniel Starke # @copyright Copyright 2022 Daniel Starke # @date 2022-02-19 -# @version 2022-03-20 +# @version 2022-05-29 # @see https://arduino.github.io/arduino-cli/0.21/library-specification/ # # Coloring description: @@ -35,11 +35,15 @@ STM32CUBEDUINO_MAP_ALL_IRQS LITERAL1 STM32CUBEDUINO_DISABLE_ADC LITERAL1 STM32CUBEDUINO_DISABLE_CDC LITERAL1 STM32CUBEDUINO_DISABLE_DAC LITERAL1 +STM32CUBEDUINO_DISABLE_EEPROM LITERAL1 STM32CUBEDUINO_DISABLE_EXTI LITERAL1 STM32CUBEDUINO_DISABLE_I2C LITERAL1 STM32CUBEDUINO_DISABLE_PWM LITERAL1 +STM32CUBEDUINO_DISABLE_PRINTF_FLOAT LITERAL1 +STM32CUBEDUINO_DISABLE_SCANF_FLOAT LITERAL1 STM32CUBEDUINO_DISABLE_SERIAL LITERAL1 STM32CUBEDUINO_DISABLE_SPI LITERAL1 +STM32CUBEDUINO_DISABLE_STRING LITERAL1 STM32CUBEDUINO_DISABLE_TIMER LITERAL1 STM32CUBEDUINO_DISABLE_USB LITERAL1 diff --git a/library.json b/library.json index 025318b..dc17df1 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "stm32cubeduino", - "version": "1.1.0", + "version": "1.2.0", "description": "Port of the Arduino API for the STM32 Cube framework.", "keywords": "arduino, stm32, stm32cube, stm32cubeduino", "platforms": "ststm32", diff --git a/library.properties b/library.properties index c99bc4b..ffccc5b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=STM32CubeDuino -version=1.1.0 +version=1.2.0 author=Daniel Starke maintainer=Daniel Starke sentence=Port of the Arduino API for the STM32 Cube framework. diff --git a/src/EEPROM.h b/src/EEPROM.h new file mode 100644 index 0000000..df744df --- /dev/null +++ b/src/EEPROM.h @@ -0,0 +1,466 @@ +/** + * @file EEPROM.h + * @author Daniel Starke + * @copyright Copyright 2022 Daniel Starke + * @date 2022-05-29 + * @version 2022-05-29 + */ +#ifndef __EEPROM_H__ +#define __EEPROM_H__ + +#include +#include + + +#if !defined(STM32CUBEDUINO_DISABLE_EEPROM) && defined(DATA_EEPROM_BASE) /* STM32 HAL FLASH EX header was included */ + +#ifndef E2END +/** Last valid EEPROM cell index. */ +#define E2END ((DATA_EEPROM_END) - (DATA_EEPROM_BASE)) +#endif + + +/** + * EEPROM cell reference and I/O handling class. + */ +class EERef { +private: + size_t index; +public: + /** + * Constructor. + * + * @param[in] idx - EEPROM cell index + */ + inline EERef(const size_t idx): + index(idx) + {} + + /** + * Reads the value at the current EEPROM cell index. + * + * @return stored value + */ + inline uint8_t operator* () const { + if (this->index < E2END) { + return *reinterpret_cast(DATA_EEPROM_BASE + this->index); + } + return 0; + } + + /** + * Reads the value at the current EEPROM cell index. + * + * @return stored value + */ + inline operator uint8_t() const { + return **this; + } + + /** + * Writes the value at the passed reference to the + * current EEPORM cell. + * + * @param[in] ref - write value from this EEPROM cell + * @return current EEPROM cell + */ + inline EERef & operator= (const EERef & ref) { + *this = *ref; + return *this; + } + + /** + * Writes the given value to the current EEPROM cell. + * + * @param[in] val - write this value + * @return current EEPROM cell + */ + inline EERef & operator= (const uint8_t val) { + if (this->index < E2END) { + HAL_FLASHEx_DATAEEPROM_Unlock(); + HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, DATA_EEPROM_BASE + this->index, val); + HAL_FLASHEx_DATAEEPROM_Lock(); + } + return *this; + } + + /** + * Adds the given value to the current EEPROM cell value. + * + * @param[in] val - add this value + * @return current EEPROM cell + */ + inline EERef & operator+= (const uint8_t val) { + *this = uint8_t(**this + val); + return *this; + } + + /** + * Subtracts the given value from the current EEPROM cell value. + * + * @param[in] val - subtract this value + * @return current EEPROM cell + */ + inline EERef & operator-= (const uint8_t val) { + *this = uint8_t(**this - val); + return *this; + } + + /** + * Multiplies the given value to the current EEPROM cell value. + * + * @param[in] val - multiply this value + * @return current EEPROM cell + */ + inline EERef & operator*= (const uint8_t val) { + *this = uint8_t(**this * val); + return *this; + } + + /** + * Divides the given value from the current EEPROM cell value. + * + * @param[in] val - divide by this value + * @return current EEPROM cell + */ + inline EERef & operator/= (const uint8_t val) { + *this = uint8_t(**this / val); + return *this; + } + + /** + * XOR the given value to the current EEPROM cell value. + * + * @param[in] val - XOR this value + * @return current EEPROM cell + */ + inline EERef & operator^= (const uint8_t val) { + *this = **this ^ val; + return *this; + } + + /** + * Modulo the given value from the current EEPROM cell value. + * + * @param[in] val - modulo by this value + * @return current EEPROM cell + */ + inline EERef & operator%= (const uint8_t val) { + *this = uint8_t(**this % val); + return *this; + } + + /** + * AND the given value to the current EEPROM cell value. + * + * @param[in] val - AND this value + * @return current EEPROM cell + */ + inline EERef & operator&= (const uint8_t val) { + *this = **this & val; + return *this; + } + + /** + * OR the given value to the current EEPROM cell value. + * + * @param[in] val - OR this value + * @return current EEPROM cell + */ + inline EERef & operator|= (const uint8_t val) { + *this = **this | val; + return *this; + } + + /** + * Shift left by the given value for the current EEPROM cell value. + * + * @param[in] val - shift left by this value + * @return current EEPROM cell + */ + inline EERef & operator<<= (const uint8_t val) { + *this = uint8_t(**this << val); + return *this; + } + + /** + * Shift right by the given value for the current EEPROM cell value. + * + * @param[in] val - shift right by this value + * @return current EEPROM cell + */ + inline EERef & operator>>= (const uint8_t val) { + *this = uint8_t(**this >> val); + return *this; + } + + /** + * Writes the current EEPROM cell value only if the new value differs. + * + * @param[in] val - write this value + * @return current EEPORM cell + */ + inline EERef & update(const uint8_t val) { + if (val != *this) { + *this = val; + } + return *this; + } + + /** + * Increments (prefix) the current EEPROM cell value by one. + * + * @return current EEPORM cell + */ + inline EERef & operator++ () { + *this += 1; + return *this; + } + + /** + * Increments (postfix) the current EEPROM cell value by one. + * + * @return current EEPORM cell + */ + inline uint8_t operator++ (int) { + const uint8_t ret(*this); + *this += 1; + return ret; + } + + /** + * Decrements (prefix) the current EEPROM cell value by one. + * + * @return current EEPORM cell + */ + inline EERef & operator-- () { + *this -= 1; + return *this; + } + + /** + * Decrements (postfix) the current EEPROM cell value by one. + * + * @return current EEPORM cell + */ + inline uint8_t operator-- (int) { + const uint8_t ret(*this); + *this -= 1; + return ret; + } +}; + + +/** + * EEPROM cell pointer handling class. + */ +class EEPtr { +private: + size_t index; +public: + /** + * Constructor. + * + * @param[in] idx - EEPROM cell index + */ + inline EEPtr(const size_t idx): + index(idx) + {} + + /** + * Returns the current EEPROM cell index. + * + * @return current EEPROM cell index + */ + operator size_t() const { + return this->index; + } + + /** + * Sets the current EEPROM cell index. + * + * @param[in] idx - new EEPROM cell index + * @return current EEPROM cell pointer + */ + EEPtr & operator= (const size_t idx) { + this->index = idx; + return *this; + } + + /** + * Compares two EEPROM cell pointers. + * + * @param[in] ptr - rhs pointer + * @return true if different, else false + */ + bool operator!= (const EEPtr & ptr) { + return this->index != ptr.index; + } + + /** + * Returns the current EEPROM cell index. + * + * @return current EEPROM cell reference + */ + EERef operator* () { + return this->index; + } + + /** + * Increments (prefix) the current EEPROM cell index by one. + * + * @return current EEPROM cell index + */ + EEPtr & operator++ () { + this->index++; + return *this; + } + + /** + * Increments (postfix) the current EEPROM cell index by one. + * + * @return current EEPROM cell index + */ + EEPtr operator++ (int) { + return this->index++; + } + + /** + * Decrements (prefix) the current EEPROM cell index by one. + * + * @return current EEPROM cell index + */ + EEPtr & operator-- () { + this->index--; + return *this; + } + + /** + * Decrements (postfix) the current EEPROM cell index by one. + * + * @return current EEPROM cell index + */ + EEPtr operator-- (int) { + return this->index--; + } +}; + + +/** + * EEPROM base class. + */ +class EEPROMClass { +public: + /** + * Returns a reference to the EEPROM cell at the given index. + * + * @param[in] idx - EEPROM cell index + * @return EEPROM cell reference + */ + inline EERef operator[] (const size_t idx) { + return EERef(idx); + } + + /** + * Reads the value from the given EEPROM cell index. + * + * @param[in] idx - EEPROM cell index + * @return stored value + */ + inline uint8_t read(const size_t idx) const { + return *EERef(idx); + } + + /** + * Writes a value to the given EEPROM cell index. + * + * @param[in] idx - EEPROM cell index + * @param[in] val - write this value + */ + inline void write(const size_t idx, const uint8_t val) { + EERef ref(idx); + ref = val; + } + + /** + * Writes a value to the given EEPROM cell index if + * the value differs from the stored one. + * + * @param[in] idx - EEPROM cell index + * @param[in] val - write this value + */ + inline void update(const size_t idx, const uint8_t val) { + EERef(idx).update(val); + } + + /** + * C++11 iterator compatible `begin()` function. + * + * @return pointer to the first EEPROM cell + */ + inline EEPtr begin() { + return EEPtr(0); + } + + /** + * C++11 iterator compatible `end()` function. + * + * @return pointer to one after the last EEPROM cell + */ + inline EEPtr end() { + return length(); + } + + /** + * Returns the number of valid EEPROM cells. + * + * @return maximum EEPROM cell count + */ + inline size_t length() const { + return E2END + 1; + } + + /** + * Reads a value of an arbitrary type from the given + * EEPROM cell index in host endian order. + * + * @param[in] idx - EEPROM cell index + * @param[out] val - output value reference + * @return value read + */ + template + T & get(const size_t idx, T & val) const { + EEPtr inPtr = idx; + uint8_t * outPtr = reinterpret_cast(&val); + for (size_t n = 0; n < sizeof(T); ++n, ++inPtr) { + *outPtr++ = *inPtr; + } + return val; + } + + /** + * Writes a value of an arbitrary type to the given + * EEPROM cell index in host endian order. + * + * @param[in] idx - EEPROM cell index + * @param[in] val - write this value + * @return value written + */ + template + const T & put(const size_t idx, const T & val) { + EEPtr outPtr = idx; + const uint8_t * inPtr = reinterpret_cast(&val); + for (size_t n = 0; n < sizeof(T); ++n, ++outPtr) { + EERef(outPtr).update(*inPtr++); + } + return val; + } +}; + + +/** EEPROM instance. */ +static EEPROMClass EEPROM; + + +#endif /* DATA_EEPROM_BASE */ +#endif /* __EEPROM_H__ */ diff --git a/src/Print.cpp b/src/Print.cpp index 8a391a9..0cae806 100644 --- a/src/Print.cpp +++ b/src/Print.cpp @@ -3,7 +3,7 @@ * @author Daniel Starke * @copyright Copyright 2019-2022 Daniel Starke * @date 2019-03-09 - * @version 2022-03-22 + * @version 2022-04-17 */ #include #include @@ -92,6 +92,7 @@ int Print::availableForWrite(void) { } +#ifndef STM32CUBEDUINO_DISABLE_STRING /** * Writes out the given string. * @@ -112,6 +113,7 @@ size_t Print::print(const __FlashStringHelper * str) { size_t Print::print(const String & str) { return this->write(str.c_str(), str.length()); } +#endif /* not STM32CUBEDUINO_DISABLE_STRING */ /** @@ -226,6 +228,7 @@ size_t Print::print(const Printable & obj) { } +#ifndef STM32CUBEDUINO_DISABLE_STRING /** * Writes out the given string with a line break. * @@ -246,6 +249,7 @@ size_t Print::println(const __FlashStringHelper * str) { size_t Print::println(const String & str) { return this->print(str) + this->println(); } +#endif /* not STM32CUBEDUINO_DISABLE_STRING */ /** diff --git a/src/Print.h b/src/Print.h index 87dd56a..ed7f7bb 100644 --- a/src/Print.h +++ b/src/Print.h @@ -1,9 +1,9 @@ /** * @file Print.h * @author Daniel Starke - * @copyright Copyright 2019-2020 Daniel Starke + * @copyright Copyright 2019-2022 Daniel Starke * @date 2019-03-09 - * @version 2020-11-02 + * @version 2022-04-17 */ #ifndef __PRINT_H__ #define __PRINT_H__ @@ -42,8 +42,10 @@ class Print { size_t write(const char * buffer, size_t size); virtual int availableForWrite(void); +#ifndef STM32CUBEDUINO_DISABLE_STRING size_t print(const __FlashStringHelper * str); size_t print(const String & str); +#endif /* not STM32CUBEDUINO_DISABLE_STRING */ size_t print(const char str[]); size_t print(char c); size_t print(unsigned char val, int base = DEC); @@ -54,8 +56,10 @@ class Print { size_t print(FloatType val, int base = 2); size_t print(const Printable & obj); +#ifndef STM32CUBEDUINO_DISABLE_STRING size_t println(const __FlashStringHelper * str); size_t println(const String & str); +#endif /* not STM32CUBEDUINO_DISABLE_STRING */ size_t println(const char str[]); size_t println(char c); size_t println(unsigned char val, int base = DEC); diff --git a/src/Stream.cpp b/src/Stream.cpp index 16a2a34..ba63dc3 100644 --- a/src/Stream.cpp +++ b/src/Stream.cpp @@ -1,9 +1,9 @@ /** * @file Stream.cpp * @author Daniel Starke - * @copyright Copyright 2019-2020 Daniel Starke + * @copyright Copyright 2019-2022 Daniel Starke * @date 2019-03-10 - * @version 2020-06-05 + * @version 2022-04-17 */ #include "Arduino.h" #include "Stream.h" @@ -201,6 +201,7 @@ size_t Stream::readBytesUntil(char terminator, uint8_t * buffer, size_t length) } +#ifndef STM32CUBEDUINO_DISABLE_STRING String Stream::readString(void) { String res; for (int c = this->timedRead(); c >= 0; c = this->timedRead()) { @@ -217,6 +218,7 @@ String Stream::readStringUntil(char terminator) { } return res; } +#endif /* not STM32CUBEDUINO_DISABLE_STRING */ int Stream::timedRead(void) { diff --git a/src/Stream.h b/src/Stream.h index 44f69e3..fd4d1d4 100644 --- a/src/Stream.h +++ b/src/Stream.h @@ -1,9 +1,9 @@ /** * @file Stream.h * @author Daniel Starke - * @copyright Copyright 2019-2020 Daniel Starke + * @copyright Copyright 2019-2022 Daniel Starke * @date 2019-03-10 - * @version 2020-05-17 + * @version 2022-04-17 */ #ifndef __STREAM_H__ #define __STREAM_H__ @@ -61,8 +61,10 @@ class Stream : public Print { size_t readBytesUntil(char terminator, char * buffer, size_t length); size_t readBytesUntil(char terminator, uint8_t * buffer, size_t length); +#ifndef STM32CUBEDUINO_DISABLE_STRING String readString(void); String readStringUntil(char terminator); +#endif /* not STM32CUBEDUINO_DISABLE_STRING */ protected: int timedRead(void); int timedPeek(void); diff --git a/src/USBCore.cpp b/src/USBCore.cpp index 223ece8..f530346 100644 --- a/src/USBCore.cpp +++ b/src/USBCore.cpp @@ -3,7 +3,7 @@ * @author Daniel Starke * @copyright Copyright 2020-2022 Daniel Starke * @date 2020-05-21 - * @version 2022-03-25 + * @version 2022-04-18 * * Control Endpoint: * @verbatim @@ -180,6 +180,7 @@ }() +/** STM32 HAL specific variable needed in usbFrameNumber(). */ extern uint32_t USBx_BASE; @@ -453,6 +454,85 @@ static void usbTriggerSend(_UsbTxBuffer & buf, const uint8_t epNum, const bool a } +/** + * Send data to an endpoint. Block until the transmission finished. + * This function is needed to handle flags that may get passed together + * with the endpoint in `USB_Send()`. + * + * @param[in] flags - `TRANSFER_ZERO`, `TRANSFER_RELEASE` and/or `TRANSFER_PGM` + * @param[in] ep - endpoint number + * @param[in] data - data buffer + * @param[in] len - data length + * @return bytes transmitted + */ +static uint32_t sendHelper(const uint8_t flags, const uint32_t ep, const void * data, uint32_t len) { + if ( ! _usbConfiguration ) return uint32_t(-1); + const uint8_t epNum = uint8_t(ep & 0xF); + if (usbTxBuffer(epNum) == NULL) { + if ( ! sendOrBlock(USB_ENDPOINT_IN(ep), data, len, true) ) return 0; + } else { + _UsbTxBuffer & buf = *usbTxBuffer(epNum); + bool canWait = false; + const bool zlp = (len == 0); + const bool interruptsEnabled = ((__get_PRIMASK() & 0x1) == 0); + const uint16_t epMask = uint16_t(1 << uint16_t(epNum)); + if ( interruptsEnabled ) { + const uint32_t irqExecutionNumber = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk; + if (irqExecutionNumber == 0 || NVIC_GetPriority(IRQn_Type(irqExecutionNumber - 16)) > usbNvicGetLowestPriority()) { + /* USB interrupt is enabled and we were not called from an interrupt with higher priority */ + canWait = true; + } + } + /* wait until space is available and add to queue */ + if ( canWait ) { + /* wait until space is available and add to queue */ + buf.commitLock = true; + __DMB(); __DSB(); __ISB(); /* data and instruction barrier */ + const uint8_t * dataBuf = reinterpret_cast(data); + for (uint32_t i = 0; i < len; dataBuf++, i++) { + if ( buf.fifo.push(((flags & TRANSFER_ZERO) == 0) ? *dataBuf : 0) ) continue; + while ( buf.fifo.full() ) { + if ((txPendingEp & epMask) == 0) { + usbTriggerSend(buf, epNum, false); + break; + } + __WFI(); + } + buf.fifo.blockingPush(((flags & TRANSFER_ZERO) == 0) ? *dataBuf : 0); + } + buf.commitLock = false; + } else { + /* add to queue or fail if no space is available, because there is currently no chance to get data out */ + uint32_t written; + if ((flags & TRANSFER_ZERO) == 0) { + written = buf.fifo.write(reinterpret_cast(data), len); + if (written < len && (txPendingEp & epMask) == 0) { + usbTriggerSend(buf, epNum, false); + written += buf.fifo.write(reinterpret_cast(data) + written, uint32_t(len - written)); + } + } else { + /* write only zero bytes */ + for (written = 0; written < len && buf.fifo.push(0); written++); + if (written < len && (txPendingEp & epMask) == 0) { + usbTriggerSend(buf, epNum, false); + for (; written < len && buf.fifo.push(0); written++); + } + } + len = written; + } + /* the interrupt routine may send out the queued data at this point */ + __DMB(); __DSB(); __ISB(); /* data and instruction barrier */ + if ((txPendingEp & epMask) != 0) return len; + if ((flags & TRANSFER_RELEASE) != 0 || zlp) { + /* start endpoint transmission from idle state */ + buf.fifo.commitBlock(); + usbTriggerSend(buf, epNum, zlp); + } + } + return len; +} + + /** * Receives data on the given endpoint. The operation fails if there is still * a previous data reception ongoing. It may be set blocking until completion. @@ -609,7 +689,9 @@ void USBDeviceClass::standby() { */ bool USBDeviceClass::handleClassInterfaceSetup(USBSetup & setup) { bool res = PluggableUSB().setup(setup); - if (res == false || setup.bmRequestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) this->sendZlp(0); + if (res == false || setup.bmRequestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { + this->sendZlp(0); + } return res; } @@ -647,7 +729,9 @@ bool USBDeviceClass::handleStandardSetup(USBSetup & setup) { switch (wValue) { case ENDPOINT_HALT: isEndpointHalt = false; - if ((ep & 0xF) != 0) HAL_PCD_EP_ClrStall(hPcdUsb, ep); + if ((ep & 0xF) != 0) { + HAL_PCD_EP_ClrStall(hPcdUsb, ep); + } this->sendZlp(0); break; case DEVICE_REMOTE_WAKEUP: @@ -667,7 +751,9 @@ bool USBDeviceClass::handleStandardSetup(USBSetup & setup) { case ENDPOINT_HALT: /* halt endpoint */ isEndpointHalt = true; - if ((ep & 0xF) != 0 && setup.wLength == 0) HAL_PCD_EP_SetStall(hPcdUsb, ep); + if ((ep & 0xF) != 0 && setup.wLength == 0) { + HAL_PCD_EP_SetStall(hPcdUsb, ep); + } this->sendZlp(0); break; case DEVICE_REMOTE_WAKEUP: @@ -817,7 +903,9 @@ uint32_t USBDeviceClass::sendControl(const void * data, uint32_t len) { uint32_t USBDeviceClass::recvControl(void * data, uint32_t len) { const uint32_t receivedLen = this->available(USB_ENDPOINT_OUT(0)); const uint32_t received = min(len, receivedLen); - if (received > 0) memcpy(data, _usbCtrlRecvBuf, size_t(received)); + if (received > 0) { + memcpy(data, _usbCtrlRecvBuf, size_t(received)); + } return received; } @@ -984,58 +1072,7 @@ void USBDeviceClass::initEP(uint32_t ep, uint32_t config) { * @return bytes transmitted */ uint32_t USBDeviceClass::send(uint32_t ep, const void * data, uint32_t len) { - if ( ! _usbConfiguration ) return uint32_t(-1); - const uint8_t epNum = uint8_t(ep & 0xF); - if (usbTxBuffer(epNum) == NULL) { - if ( ! sendOrBlock(USB_ENDPOINT_IN(ep), data, len, true) ) return 0; - } else { - _UsbTxBuffer & buf = *usbTxBuffer(epNum); - bool canWait = false; - const bool zlp = (len == 0); - const bool interruptsEnabled = ((__get_PRIMASK() & 0x1) == 0); - const uint16_t epMask = uint16_t(1 << uint16_t(epNum)); - if ( interruptsEnabled ) { - const uint32_t irqExecutionNumber = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk; - if (irqExecutionNumber == 0 || NVIC_GetPriority(IRQn_Type(irqExecutionNumber - 16)) > usbNvicGetLowestPriority()) { - /* USB interrupt is enabled and we were not called from an interrupt with higher priority */ - canWait = true; - } - } - /* wait until space is available and add to queue */ - if ( canWait ) { - /* wait until space is available and add to queue */ - buf.commitLock = true; - __DMB(); __DSB(); __ISB(); /* data and instruction barrier */ - const uint8_t * dataBuf = reinterpret_cast(data); - for (uint32_t i = 0; i < len; dataBuf++, i++) { - if ( buf.fifo.push(*dataBuf) ) continue; - while ( buf.fifo.full() ) { - if ((txPendingEp & epMask) == 0) { - usbTriggerSend(buf, epNum, false); - break; - } - __WFI(); - } - buf.fifo.blockingPush(*dataBuf); - } - buf.commitLock = false; - } else { - /* add to queue or fail if no space is available, because there is currently no chance to get data out */ - uint32_t written = buf.fifo.write(reinterpret_cast(data), len); - if (written < len && (txPendingEp & epMask) == 0) { - usbTriggerSend(buf, epNum, false); - written += buf.fifo.write(reinterpret_cast(data), len); - } - len = written; - } - /* the interrupt routine may send out the queued data at this point */ - __DMB(); __DSB(); __ISB(); /* data and instruction barrier */ - if ((txPendingEp & epMask) != 0) return len; - /* start endpoint transmission from idle state */ - buf.fifo.commitBlock(); - usbTriggerSend(buf, epNum, zlp); - } - return len; + return sendHelper(TRANSFER_RELEASE, ep, data, len); } @@ -1231,31 +1268,71 @@ void USBDeviceClass::stall(uint32_t ep) { } +/** + * USB device class instance. + */ USBDeviceClass USBDevice; /* Arduino legacy functions */ +/** + * Sends the given data on the control endpoint. + * + * @param[in] flags - `TRANSFER_ZERO`, `TRANSFER_RELEASE` and/or `TRANSFER_PGM` + * @param[in] data - data buffer + * @param[in] len - data length + * @return processed data length + */ int USB_SendControl(uint8_t /* flags */, const void * data, int len) { return int(USBDevice.sendControl(data, len)); } +/** + * Received data from the control endpoint. This function blocks until + * some data has been received. + * + * @param[out] data - data buffer + * @param[in] len - buffer size + * @return received data length + */ int USB_RecvControl(void * data, int len) { return int(USBDevice.recvControl(data, len)); } +/** + * Received data from the control endpoint. This function blocks until + * some data has been received. + * + * @param[out] data - data buffer + * @param[in] len - buffer size + * @return received data length + */ int USB_RecvControlLong(void * data, int len) { return int(USBDevice.recvControl(data, len)); } +/** + * Returns how many bytes have been received on the given endpoint. + * + * @param[in] ep - endpoint + * @return received bytes count + */ uint8_t USB_Available(uint8_t ep) { const uint32_t available = USBDevice.available(ep); return uint8_t(min(255, available)); } +/** + * Returns how many bytes can be written to the given endpoint + * without blocking. + * + * @param[in] ep - endpoint + * @return number of bytes available for write + */ uint8_t USB_SendSpace(uint8_t ep) { const uint16_t epMask = uint16_t(1 << uint16_t(ep & 0xF)); if ((txPendingEp & epMask) != 0) { @@ -1272,21 +1349,49 @@ uint8_t USB_SendSpace(uint8_t ep) { } +/** + * Send data to an endpoint. Block until the transmission finished. + * + * @param[in] ep - endpoint number; can be combined with `TRANSFER_ZERO`, `TRANSFER_RELEASE` and/or `TRANSFER_PGM` + * @param[in] data - data buffer + * @param[in] len - data length + * @return bytes transmitted + */ int USB_Send(uint8_t ep, const void * data, int len) { - return int(USBDevice.send(ep, data, len)); + return int(sendHelper(ep & 0xF0, ep & 0xF, data, len)); } +/** + * Non-blocking receive. + * + * @param[in] ep - endpoint number + * @param[out] data - data buffer + * @param[in] len - buffer size + * @return Number of bytes copied to the passed data buffer. + * @remarks -1 is returned if no USB device is connected for compatibility reasons. + */ int USB_Recv(uint8_t ep, void * data, int len) { return int(USBDevice.recv(ep, data, len)); } +/** + * Receive one byte if available. + * + * @param[in] ep - endpoint number + * @return received byte or -1 if unavailable + */ int USB_Recv(uint8_t ep) { return USBDevice.recv(ep); } +/** + * Flush buffers of the given endpoint. + * + * @param[in] ep - endpoint + */ void USB_Flush(uint8_t ep) { USBDevice.flush(ep); } @@ -1576,7 +1681,7 @@ void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef * /* hPcd */, uint8_t ep) { * @remarks Output of the SOF signal needs to be explicitly enabled at USB HAL initialization. */ void HAL_PCD_SOFCallback(PCD_HandleTypeDef * /* hPcd */) { - //uint32_t frameNumber = hPcdUsb->Instance->FNR & USB_FNR_FN; + //uint32_t frameNumber = usbFrameNumber(); } diff --git a/src/Version.h b/src/Version.h index 4aa67ee..81ce693 100644 --- a/src/Version.h +++ b/src/Version.h @@ -3,7 +3,7 @@ * @author Daniel Starke * @copyright Copyright 2020-2022 Daniel Starke * @date 2020-06-07 - * @version 2022-04-03 + * @version 2022-05-29 * * Inspired from Linux Kernel. Usage example: * @code .cpp @@ -19,7 +19,7 @@ /** Derived from `ARDUINO` which is defined in `BuildScript.py` */ #define ARDUINO_API_VERSION ARDUINO -#define STM32CUBEDUINO "1.1.0" +#define STM32CUBEDUINO "1.2.0" #define STM32CUBEDUINO_MAJOR 1 #define STM32CUBEDUINO_MINOR 1 #define STM32CUBEDUINO_PATCH 0 diff --git a/src/WString.cpp b/src/WString.cpp index 80d44b8..7006e2a 100644 --- a/src/WString.cpp +++ b/src/WString.cpp @@ -3,7 +3,7 @@ * @author Daniel Starke * @copyright Copyright 2019-2022 Daniel Starke * @date 2019-03-10 - * @version 2022-03-22 + * @version 2022-04-17 */ #include #include @@ -14,6 +14,9 @@ const char * _charMap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#ifndef STM32CUBEDUINO_DISABLE_STRING + + namespace { template struct as_unsigned; template <> struct as_unsigned { typedef unsigned int type; }; @@ -1021,3 +1024,6 @@ StringSumHelper & operator+ (const StringSumHelper & o, double val) { if ( ! res.concat(val) ) res.invalidate(); return res; } + + +#endif /* not STM32CUBEDUINO_DISABLE_STRING */ diff --git a/src/WString.h b/src/WString.h index b8eab8d..3366d71 100644 --- a/src/WString.h +++ b/src/WString.h @@ -1,9 +1,9 @@ /** * @file WString.h * @author Daniel Starke - * @copyright Copyright 2019-2020 Daniel Starke + * @copyright Copyright 2019-2022 Daniel Starke * @date 2019-03-10 - * @version 2020-11-02 + * @version 2022-04-17 */ #ifndef __WSTRING_H__ #define __WSTRING_H__ @@ -12,6 +12,9 @@ #include +#ifndef STM32CUBEDUINO_DISABLE_STRING + + /* for compatibility */ class __FlashStringHelper; #define F(str) (reinterpret_cast(PSTR(str))) @@ -178,4 +181,7 @@ class StringSumHelper : public String { }; +#endif /* not STM32CUBEDUINO_DISABLE_STRING */ + + #endif /* __WSTRING_H__ */