diff --git a/.github/workflows/Arduino-build.yml b/.github/workflows/Arduino-build.yml index ea669c2833..089f934f7e 100644 --- a/.github/workflows/Arduino-build.yml +++ b/.github/workflows/Arduino-build.yml @@ -9,6 +9,7 @@ on: - '*.json' - '**.md' - docs/** + - doxygen/** - mkdocs.yml - requirements-docs.txt - keywords.txt @@ -24,6 +25,7 @@ on: - '*.json' - '**.md' - docs/** + - doxygen/** - mkdocs.yml - requirements-docs.txt - keywords.txt diff --git a/.github/workflows/Cmake.yml b/.github/workflows/Cmake.yml index 016f8bd9f1..b51a47960c 100644 --- a/.github/workflows/Cmake.yml +++ b/.github/workflows/Cmake.yml @@ -10,6 +10,7 @@ on: - '*.json' - '**.md' - docs/** + - doxygen/** - mkdocs.yml - requirements-docs.txt - keywords.txt @@ -22,6 +23,7 @@ on: - '*.json' - '**.md' - docs/** + - doxygen/** - mkdocs.yml - requirements-docs.txt - keywords.txt diff --git a/.github/workflows/PIO-build.yml b/.github/workflows/PIO-build.yml index 1014542e9e..33be52ca53 100644 --- a/.github/workflows/PIO-build.yml +++ b/.github/workflows/PIO-build.yml @@ -10,6 +10,7 @@ on: - '*.json' - '**.md' - docs/** + - doxygen/** - mkdocs.yml - requirements-docs.txt - keywords.txt @@ -26,6 +27,7 @@ on: - '*.json' - '**.md' - docs/** + - doxygen/** - mkdocs.yml - requirements-docs.txt - keywords.txt diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml index 29d017ee6b..6c25b752fe 100644 --- a/.github/workflows/docs-check.yml +++ b/.github/workflows/docs-check.yml @@ -10,6 +10,13 @@ on: - boards.txt - mkdocs.yml - requirements-docs.txt + - doxygen/** + - cores/** + - libraries/** + - variants/** + - system/** + - "!system/Drivers/**" + - "!system/Middlewares/**" - .github/workflows/docs-check.yml permissions: @@ -26,6 +33,16 @@ jobs: with: python-version: "3.x" - run: python -m pip install -r requirements-docs.txt + - name: Install Doxygen + run: sudo apt-get update && sudo apt-get install --yes doxygen + - name: Build Doxygen API reference + run: | + STM32_CORE_VERSION=$(sed -n 's/^version=//p' platform.txt) + export STM32_CORE_VERSION + doxygen doxygen/Doxyfile + test -f build/doxygen/index.html - run: mkdocs build --strict + - name: Check the Doxygen API reference is part of the site + run: grep -q 'name="generator" content="Doxygen' site/doxygen/index.html - name: Check generated supported boards page is committed run: git diff --exit-code -- docs/supported-boards/index.md || { echo "::error::Run 'python docs/hooks/supported_boards.py' and commit docs/supported-boards/index.md"; exit 1; } diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 80571f5dda..bc8049f143 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -11,6 +11,13 @@ on: - boards.txt - mkdocs.yml - requirements-docs.txt + - doxygen/** + - cores/** + - libraries/** + - variants/** + - system/** + - "!system/Drivers/**" + - "!system/Middlewares/**" - .github/workflows/publish-docs.yml workflow_dispatch: @@ -33,6 +40,14 @@ jobs: with: python-version: "3.x" - run: python -m pip install -r requirements-docs.txt + - name: Install Doxygen + run: sudo apt-get update && sudo apt-get install --yes doxygen + - name: Build Doxygen API reference + run: | + STM32_CORE_VERSION=$(sed -n 's/^version=//p' platform.txt) + export STM32_CORE_VERSION + doxygen doxygen/Doxyfile + test -f build/doxygen/index.html - name: Configure Git author run: | git config user.name "github-actions[bot]" @@ -68,11 +83,13 @@ jobs: env: VERSION: ${{ github.ref_name }} run: | - if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Core releases have a major version below 1000: this excludes the + # legacy date tags such as 2018.1.18, which would otherwise sort last. + if [[ ! "$VERSION" =~ ^[0-9]{1,3}\.[0-9]+\.[0-9]+$ ]]; then echo "Unsupported documentation release tag: $VERSION" >&2 exit 1 fi - latest=$(git tag --list | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) + latest=$(git tag --list | grep -E '^[0-9]{1,3}\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) if git ls-remote --exit-code --heads pages gh-pages >/dev/null; then git fetch pages gh-pages:gh-pages fi diff --git a/CI/update/README.md b/CI/update/README.md index d9950cc210..8876b1d00b 100644 --- a/CI/update/README.md +++ b/CI/update/README.md @@ -1,26 +1,73 @@ -# stm32variant - -By default, generates: - - *PeripheralPins.c* - - *PinNamesVar.h* - - *variant_generic.cpp* - - *variant_generic.h* - - *boards_entry.txt* - - *generic_clock.c* - -for all xml files description available in [STM32CubeMX](http://www.st.com/en/development-tools/stm32cubemx.html) database. -By default, the database used is the one from GitHub: - https://github.com/STMicroelectronics/STM32_open_pin_data -The internal database from STM32CubeMX directory can also be used. -The path have to be defined in `update_config.json`. - -After file generation, review them carefully and please report any issue +# Update scripts + +Run scripts from the `Arduino_Core_STM32` repository root unless noted +otherwise. Most scripts use settings in `CI/update/update_config.json`; they +create default settings when needed. + +## Generate generic variants + +`python CI/update/stm32variant.py` generates `PeripheralPins.c`, +`PinNamesVar.h`, `variant_generic.cpp`, `variant_generic.h`, `boards_entry.txt`, +and `generic_clock.c` from STM32CubeMX XML pin descriptions. By default, it +fetches the `STM32_open_pin_data` database from GitHub and processes all +available descriptions. Use `--list` to list descriptions or +`--series ` to generate a selected series. Use `--cube` to use the +STM32CubeMX database instead; set `CUBEMX_DIRECTORY` in +`CI/update/update_config.json` for that database. The configuration also uses +`STM32CUBECLT_PATH` for STM32 target information. + +`python CI/update/stm32variantv2.py` generates the same files from JSON pinout +descriptions in the newer STM32Cube device-pack repositories. Use `--list` to +list available pinout files or `--series ` to limit generation to one +supported series. The repositories are managed by `stm32cubev2.py`; their +location is configured with `REPOV2_LOCAL_PATH`. + +After generation, review the files carefully and report any issue [here](https://github.com/stm32duino/Arduino_Core_STM32/issues). +## Update STM32Cube content + +`python CI/update/stm32cube.py` manages the HAL drivers and CMSIS device files +from the STM32Cube repositories. By default, it updates all supported series. +Use `--check` to report version differences without updating, `--series +` to select a series, or `--path ` to use a local STM32Cube +repository instead of fetching from GitHub. By default, updates are prepared +in a working copy; use `--local` to update the current core repository. + +`python CI/update/stm32cubev2.py` performs the corresponding update for the +newer STM32Cube repository layout, including HAL, LL, and CMSIS files. It +supports the same `--check`, `--series`, `--path`, and `--local` options. + +## Generate support files + +`python CI/update/stm32wrapper.py` regenerates the HAL, LL, CMSIS, and related +wrapper files using content already present in the core. Optional +`--core ` and `--cmsis ` arguments select alternate core +and CMSIS directories. + +`python CI/update/stm32svd.py` updates the SVD files in the configured +STM32duino SVD repository. Set `STM32CUBECLT_PATH` to an STM32CubeCLT +installation and `STM32_SVD_PATH` to the SVD repository in +`CI/update/update_config.json`. The script replaces the repository's `svd` +directory with files selected from that CubeCLT installation. + +`python CI/update/fqbn.py` prints the STM32 board FQBNs available in an +installed Arduino core, including each board's `pnum` choices. Use `--board +` to filter the output with a regular expression and `--path +` to specify the directory containing `arduino-cli`. + +## Update ArduinoCore-API + +`python CI/update/stm32api.py` synchronizes the ArduinoCore-API submodule with +the latest upstream tag, applies eligible STM32duino pull requests and local +patches, and reshapes the tree for STM32 use. It requires the submodule to be +initialized, a clean submodule worktree, and the GitHub CLI (`gh`) to retrieve +pull-request data. + ## Update the STM32duino library list -Run `python CI/update/stm32libraries.py` from the `Arduino_Core_STM32` repository -to fetch public STM32duino repositories that contain a root-level +Run `python CI/update/stm32libraries.py` from the `Arduino_Core_STM32` +repository to fetch public STM32duino repositories that contain a root-level `library.properties` and refresh the generated section in `docs/libraries/index.md`. The script groups libraries by their declared category, puts expansion-board libraries in a separate section, includes @@ -28,9 +75,10 @@ declared dependencies (linking STM32duino dependencies when available), and sorts entries alphabetically. Review and commit the updated documentation manually; this script is not run by CI. -`REPO_STM32DUINO` in `update_config.json` selects the local directory where -library repositories are cloned or fetched. Absolute paths are used as -configured; relative paths are resolved from the `Arduino_Core_STM32` repository -root. If the file or setting is missing, the script writes the default -`REPO_STM32DUINO` path (the parent of the core repository root), asks you to check the -configuration, and exits; run it again after reviewing the generated setting. +`REPO_STM32DUINO` in `CI/update/update_config.json` selects the local directory +where library repositories are cloned or fetched. Absolute paths are used as +configured; relative paths are resolved from the `Arduino_Core_STM32` +repository root. If the file or setting is missing, the script writes the +default `REPO_STM32DUINO` path (the parent of the core repository root), asks +you to check the configuration, and exits; run it again after reviewing the +generated setting. diff --git a/CI/update/gen_wrapper.sh b/CI/update/gen_wrapper.sh deleted file mode 100644 index 0ec33fa7c3..0000000000 --- a/CI/update/gen_wrapper.sh +++ /dev/null @@ -1,210 +0,0 @@ -#!/bin/bash - -set -o nounset # Treat unset variables as an error - -# Base path -Core_path=../.. -SrcWrapper_path=$Core_path/libraries/SrcWrapper -HALDrivers_path=$Core_path/system/Drivers -CMSIS_Device_ST_path=$Core_path/system/Drivers/CMSIS/Device/ST -CMSIS_DSP_path=$Core_path/libraries/CMSIS_DSP -# CMSIS outside of the core. To be updated to match environment -CMSIS_path=$Core_path/../ArduinoModule-CMSIS/CMSIS_5 -CMSIS_DSPSrc_path=$CMSIS_path/CMSIS/DSP/Source - -# Out sources files -HALoutSrc_path=$SrcWrapper_path/src/HAL -LLoutSrc_path=$SrcWrapper_path/src/LL -CMSIS_DSP_outSrc_path=$CMSIS_DSP_path/src - -# Out include files -LLoutInc_path=$Core_path/cores/arduino/stm32/LL - -# Out startup files -CMSIS_Startupfile=$Core_path/cores/arduino/stm32/stm32_def_build.h - -# Variables -series=($(find $HALDrivers_path -maxdepth 1 -type d -name "STM32*" | sed -e "s#$HALDrivers_path/STM32##g" -e "s#xx_HAL_Driver##g" | sort)) - -all_LL_file=stm32yyxx_ll.h - -# Create the file -print_C_header() { - # if [[ $1 = *"template"* ]]; then - # echo "#if 0" > "$1" - # else - # touch "$1" - # fi - touch "$1" -} - -# Add some pragma to ll header files to avoid several warnings -# which will be corrected along Cube update -print_LL_header() { - upper=$(echo "$1" | awk '{print toupper($1)}' | sed -e "s/\./_/g") - echo "#ifndef _${upper}_ -#define _${upper}_ -/* LL raised several warnings, ignore them */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored \"-Wunused-parameter\" -#pragma GCC diagnostic ignored \"-Wstrict-aliasing\" -" > $LLoutInc_path/"$1" -} - -print_CMSIS_Startup_header() { - echo "#ifndef _STM32_DEF_BUILD_ -#define _STM32_DEF_BUILD_ - -#if !defined(CMSIS_STARTUP_FILE) && !defined(CUSTOM_STARTUP_FILE)" > $CMSIS_Startupfile -} - -print_CMSIS_Startup_footer() { - echo " #else - #error UNKNOWN CHIP - #endif -#else - #warning \"No CMSIS startup file defined, custom one should be used\" -#endif /* !CMSIS_STARTUP_FILE && !CUSTOM_STARTUP_FILE */ -#endif /* _STM32_DEF_BUILD_ */" >> $CMSIS_Startupfile -} - -print_CMSIS_Startup_list() { - # Handle first elements - # File name - local f - local upper - f=$(echo "${list[0]}" | awk -F/ '{print $NF}') - upper=$(echo "$f" | awk -F'[_.]' '{print toupper($2)}' | tr X x) - echo " #if defined($upper) - #define CMSIS_STARTUP_FILE \"$f\"" >> $CMSIS_Startupfile - - if [ ${#list[@]} -gt 1 ]; then - for fp in "${list[@]:1}"; do - # File name - f=$(echo "$fp" | awk -F/ '{print $NF}') - upper=$(echo "$f" | awk -F'[_.]' '{print toupper($2)}' | tr X x) - upper="${upper//MP15xx/MP1xx}" - echo " #elif defined($upper) - #define CMSIS_STARTUP_FILE \"$f\"" >> $CMSIS_Startupfile - done - fi -} - -# main -# Check if this is the right place -if [ ! -d $HALDrivers_path ]; then - echo "Could not find $HALDrivers_path!" - echo "Launch $0 from scripts folder!" - exit 1 -fi - -# Remove old file -rm -f $HALoutSrc_path/* $LLoutSrc_path/* $LLoutInc_path/* $CMSIS_Startupfile - -# Search all files for each series -for serie in "${series[@]}"; do - if [ -d $HALDrivers_path/STM32"${serie}"xx_HAL_Driver/Src ]; then - lower=$(echo "$serie" | awk '{print tolower($0)}') - echo -n "Generating for $serie..." - - # Generate stm32yyxx_[hal|ll]*.c file - filelist=($(find $HALDrivers_path/STM32"${serie}"xx_HAL_Driver/Src -maxdepth 1 -name "stm32${lower}xx_*.c")) - for fp in "${filelist[@]}"; do - # No need for template - if [[ $fp = *"template"* ]]; then - continue - fi - outp=$HALoutSrc_path - # File name - f=$(echo "$fp" | awk -F/ '{print $NF}') - # Compute generic file name - g="${f//$lower/yy}" - if [[ $f = *"_ll_"* ]]; then - outp=$LLoutSrc_path - fi - if [ ! -f $outp/"$g" ]; then - print_C_header $outp/"$g" - fi - # Amend file name under serie switch - { - echo "#ifdef STM32${serie}xx" - echo " #include \"$f\"" - echo "#endif" - } >> $outp/"$g" - done - - # Generate stm32yyxx_ll_*.h file - filelist=($(find $HALDrivers_path/STM32"${serie}"xx_HAL_Driver/Inc -maxdepth 2 -name "stm32${lower}xx_ll_*.h")) - for fp in "${filelist[@]}"; do - # File name - f=$(echo "$fp" | awk -F/ '{print $NF}') - # Compute generic file name - g="${f//$lower/yy}" - - if [ ! -f $LLoutInc_path/"$g" ]; then - print_LL_header "$g" - fi - # Amend full LL header file - echo "#include \"$g\"" >> $LLoutInc_path/${all_LL_file}.tmp - # Amend file name under serie switch - { - echo "#ifdef STM32${serie}xx" - echo " #include \"$f\"" - echo "#endif" - } >> $LLoutInc_path/"$g" - done - echo "done" - fi -done - -# Filter full LL header file -if [ ! -f $LLoutInc_path/$all_LL_file ]; then - print_LL_header $all_LL_file -fi -echo "/* Include Low Layers drivers */" >> $LLoutInc_path/${all_LL_file} -sort -u $LLoutInc_path/${all_LL_file}.tmp >> $LLoutInc_path/${all_LL_file} -rm -f $LLoutInc_path/${all_LL_file}.tmp - -# Search all template file to end "#if 0" -# filelist=($(find $HALoutSrc_path -maxdepth 1 -name "stm32*_template.c")) -# for fp in "${filelist[@]}"; do -# echo "#endif /* 0 */" >> "$fp" -# done - -# Search all LL header files to end guard -filelist=($(find $LLoutInc_path -maxdepth 1 -name "stm32yyxx_ll*.h")) -for fp in "${filelist[@]}"; do - upper=$(basename "$fp" | awk '{print toupper($1)}' | sed -e "s/\./_/g") - echo "#pragma GCC diagnostic pop" >> "$fp" - echo "#endif /* _${upper}_ */" >> "$fp" -done - -# CMSIS startup files -list=($(find $CMSIS_Device_ST_path -name "startup_*.s" | grep gcc | sort -du)) -if [ ${#list[@]} -ne 0 ]; then - echo "Number of startup files: ${#list[@]}" - print_CMSIS_Startup_header - print_CMSIS_Startup_list - print_CMSIS_Startup_footer -else - echo "No startup files found!" -fi - -# CMSIS DSP C source files -if [ ! -d $CMSIS_DSPSrc_path ]; then - echo "Could not find $CMSIS_DSPSrc_path!" - echo "Update 'CMSIS_path' in $0 to point on ArduinoModule-CMSIS folder." - echo "Skip CMSIS DSP generation." -else - # Remove old file - rm -fr "${CMSIS_DSP_outSrc_path:?}"/*/ - - filelist=($(find $CMSIS_DSPSrc_path -name "*.c" | sed -e "s#$CMSIS_DSPSrc_path/##g" | sort -u)) - for fp in "${filelist[@]}"; do - # Directory name - d=$(echo "$fp" | awk -F/ '{print $(NF-1)}') - if [ ! -d $CMSIS_DSP_outSrc_path/"$d" ]; then - mkdir $CMSIS_DSP_outSrc_path/"$d" - echo "#include \"../Source/$d/$d.c\"" > $CMSIS_DSP_outSrc_path/"$d"/"$d".c - fi - done -fi diff --git a/CI/update/templates/generic_clock.c b/CI/update/templates/generic_clock.c index 10f37de526..59951ea226 100644 --- a/CI/update/templates/generic_clock.c +++ b/CI/update/templates/generic_clock.c @@ -15,9 +15,7 @@ #include "pins_arduino.h" /** - * @brief System Clock Configuration - * @param None - * @retval None + * @brief System Clock Configuration */ WEAK void SystemClock_Config(void) { diff --git a/README.md b/README.md index cbabf0a712..b7edab4df7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ * [Getting Started](https://github.com/stm32duino/Arduino_Core_STM32#getting-started)
* [Supported boards](docs/supported-boards/index.md)
* [Troubleshooting](https://github.com/stm32duino/Arduino_Core_STM32#troubleshooting)
-* [Documentation](https://stm32duino.github.io/latest/) +* [Documentation](https://stm32duino.github.io/latest/)
+* [API reference (Doxygen)](https://stm32duino.github.io/dev/doxygen/) ## Introduction @@ -53,7 +54,7 @@ Users can add an STM32-based board by following [Add a board variant](https://st ## Supported boards -See the [supported boards list](docs/supported-boards/index.md). +See the [supported boards list](https://stm32duino.github.io/dev/supported-boards/). ## Next release diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 3118c23ab2..80c7774303 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -17,10 +17,18 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file Arduino.h + * @brief Main include file of the Arduino core for STM32. + * + * Includes the Arduino API, the wiring extensions and the pins of the selected variant. + */ + #ifndef Arduino_h #define Arduino_h #ifndef GCC_VERSION +/** GCC version encoded as major * 10000 + minor * 100 + patch level. */ #define GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) diff --git a/cores/arduino/Serial.cpp b/cores/arduino/Serial.cpp index 6699ce2e03..6ee2e1a42a 100644 --- a/cores/arduino/Serial.cpp +++ b/cores/arduino/Serial.cpp @@ -22,6 +22,11 @@ Modified 3 December 2013 by Matthijs Kooijman */ +/** + * @file Serial.cpp + * @brief Hardware serial (Uart class) implementation and SerialN / SerialLPN instances. + */ + #include #include "Arduino.h" #include "Serial.h" diff --git a/cores/arduino/Serial.h b/cores/arduino/Serial.h index f831ce3bcc..54b3537244 100644 --- a/cores/arduino/Serial.h +++ b/cores/arduino/Serial.h @@ -21,6 +21,11 @@ Modified 3 December 2013 by Matthijs Kooijman */ +/** + * @file Serial.h + * @brief Hardware serial (Uart class) and the SerialN / SerialLPN instances. + */ + #ifndef HardwareSerial_h #define HardwareSerial_h @@ -38,19 +43,43 @@ // NOTE: a "power of 2" buffer size is recommended to dramatically // optimize all the modulo operations for ring buffers. #if !defined(SERIAL_TX_BUFFER_SIZE) + /** + * Size in bytes of the transmit ring buffer. + * Can be defined before including this file. + */ #define SERIAL_TX_BUFFER_SIZE 64 #endif #if !defined(SERIAL_RX_BUFFER_SIZE) + /** + * Size in bytes of the receive ring buffer. + * Can be defined before including this file. + */ #define SERIAL_RX_BUFFER_SIZE 64 #endif #if (SERIAL_TX_BUFFER_SIZE>256) + /** + * Index type of the transmit buffer, 16 bits when the buffer is larger than + * 256 bytes. + */ typedef uint16_t tx_buffer_index_t; #else + /** + * Index type of the transmit buffer, 8 bits when the buffer is at most + * 256 bytes. + */ typedef uint8_t tx_buffer_index_t; #endif #if (SERIAL_RX_BUFFER_SIZE>256) + /** + * Index type of the receive buffer, 16 bits when the buffer is larger than + * 256 bytes. + */ typedef uint16_t rx_buffer_index_t; #else + /** + * Index type of the receive buffer, 8 bits when the buffer is at most + * 256 bytes. + */ typedef uint8_t rx_buffer_index_t; #endif @@ -58,11 +87,24 @@ // But it brings an build error due to ambiguous // call of overloaded Uart(int, int) // So defining a dedicated type +/** + * Half-duplex mode selection of the Uart(void *, HalfDuplexMode_t) constructor. + */ typedef enum { - HALF_DUPLEX_DISABLED, - HALF_DUPLEX_ENABLED + HALF_DUPLEX_DISABLED, /**< Full-duplex, RX and TX pins are used. */ + HALF_DUPLEX_ENABLED /**< Half-duplex, only the TX pin is used. */ } HalfDuplexMode_t; +/* + * Supported frame configs (data bits, parity, stop bits), passed to Uart::begin(): + * - 8 data bits: SERIAL_8N1, SERIAL_8N2, SERIAL_8E1, SERIAL_8E2, SERIAL_8O1, SERIAL_8O2 + * - 7 data bits with parity: SERIAL_7E1, SERIAL_7E2, SERIAL_7O1, SERIAL_7O2 + * - 7 data bits without parity (SERIAL_7N1, SERIAL_7N2) and 6 data bits with + * parity (SERIAL_6E1, SERIAL_6E2, SERIAL_6O1, SERIAL_6O2): only if the UART + * supports 7-bit word length, otherwise they are undefined below. + * Parity is N (none), E (even) or O (odd). + * Any other config calls Error_Handler(). + */ // below configs are not supported by STM32 #undef SERIAL_PARITY_MARK #undef SERIAL_PARITY_SPACE @@ -105,105 +147,361 @@ typedef enum { #undef SERIAL_6O2 #endif +/** + * @brief STM32 hardware UART. Implements the Arduino Stream and HardwareSerial API. + * + * Use one of the constructors to bind a UART to pins or to a peripheral, then call begin(). + */ class Uart : public arduino::HardwareSerial { protected: - // Has any byte been written to the UART since begin() + /** + * Has any byte been written to the UART since begin(). + * Used by flush() to skip the wait. + */ bool _written; + /** + * Hardware inversion of the RX line, set by setRxInvert(). + * Applied by begin(). + */ bool _rx_invert; + /** + * Hardware inversion of the TX line, set by setTxInvert(). + * Applied by begin(). + */ bool _tx_invert; + /** + * Hardware inversion of the data bits, set by setDataInvert(). + * Applied by begin(). + */ bool _data_invert; // Don't put any members after these buffers, since only the first // 32 bytes of this struct can be accessed quickly using the ldd // instruction. + /** + * Receive ring buffer, filled by _rx_complete_irq() and emptied by read(). + */ unsigned char _rx_buffer[SERIAL_RX_BUFFER_SIZE]; + /** + * Transmit ring buffer, filled by write() and emptied by _tx_complete_irq(). + */ unsigned char _tx_buffer[SERIAL_TX_BUFFER_SIZE]; + /** Low-level serial object: pins, ring buffer indexes and HAL handle. */ serial_t _serial; public: + /** + * @brief Create a UART from Arduino digital pin numbers. + * + * @param _rx Digital pin used for RX. + * @param _tx Digital pin used for TX. + * @param _rts Digital pin used for RTS flow control, NUM_DIGITAL_PINS if unused. + * @param _cts Digital pin used for CTS flow control, NUM_DIGITAL_PINS if unused. + */ Uart(pin_size_t _rx, pin_size_t _tx, pin_size_t _rts = NUM_DIGITAL_PINS, pin_size_t _cts = NUM_DIGITAL_PINS); + /** + * @brief Create a UART from STM32 pin names. + * + * @param _rx Pin used for RX. + * @param _tx Pin used for TX. + * @param _rts Pin used for RTS flow control, NC if unused. + * @param _cts Pin used for CTS flow control, NC if unused. + */ Uart(PinName _rx, PinName _tx, PinName _rts = NC, PinName _cts = NC); + /** + * @brief Create a UART from an STM32 USART/UART peripheral. + * Pins are taken from the variant. + * + * @param peripheral USART/UART peripheral instance, e.g. USART1. + * @param halfDuplex HALF_DUPLEX_ENABLED to use the TX pin only. + */ Uart(void *peripheral, HalfDuplexMode_t halfDuplex = HALF_DUPLEX_DISABLED); + /** + * @brief Create a half-duplex UART on a single digital pin. + * The pin is used as TX and RX is NC. + * + * @param _rxtx Digital pin used for TX. + */ Uart(pin_size_t _rxtx); + /** + * @brief Create a half-duplex UART on a single STM32 pin. + * The pin is used as TX and RX is NC. + * + * @param _rxtx Pin used for TX. + */ Uart(PinName _rxtx); + /** + * @brief Start the UART with 8 data bits, no parity and 1 stop bit (SERIAL_8N1). + * + * @param baud Baud rate in bits per second. + */ void begin(unsigned long baud) override { begin(baud, SERIAL_8N1); } + /** + * @brief Start the UART with the given baud rate and frame format. + * + * @param baud Baud rate in bits per second. + * @param config Frame format, e.g. SERIAL_8N1 or SERIAL_8E1. + */ void begin(unsigned long baud, uint16_t config) override ; // Implement all pure virtuals + /** + * @brief Wait for pending transmission, stop the UART, release its pins and + * clear the receive buffer. + */ void end() override; + /** + * @brief Get the number of bytes available to read. + * + * @return Number of bytes in the receive buffer. + */ int available(void) override; + /** + * @brief Read the next byte without removing it from the receive buffer. + * + * @return Next byte, or -1 if none is available. + */ int peek(void) override; + /** + * @brief Read and remove the next byte from the receive buffer. + * + * @return Next byte, or -1 if none is available. + */ int read(void) override; + /** + * @brief Wait until all queued bytes are transmitted. + */ void flush() override; - size_t write(uint8_t) override; + /** + * @brief Write one byte. + * + * @param c Byte to write. + * @return Number of bytes written: 1, or 0 if the UART is not started. + */ + size_t write(uint8_t c) override; + /** + * @brief Check whether the UART is started. + * + * @return true if begin() was called and the UART is ready. + */ operator bool() override { return _ready; } // STM32 extension + /** + * @brief Get the number of bytes that can be written without blocking. + * + * @return Free space in the transmit buffer, in bytes. + */ int availableForWrite(void); + /** + * @brief Wait until all queued bytes are transmitted, or until a timeout elapses. + * + * @param timeout Maximum wait in milliseconds. 0 waits without limit. + * On timeout, the pending transmit data is discarded. + */ void flush(uint32_t timeout); + /** + * @brief Write one byte, truncating the value to 8 bits. + * + * @param n Value to write. + * @return Number of bytes written. + */ inline size_t write(unsigned long n) { return write((uint8_t)n); } + /** + * @brief Write one byte, truncating the value to 8 bits. + * + * @param n Value to write. + * @return Number of bytes written. + */ inline size_t write(long n) { return write((uint8_t)n); } + /** + * @brief Write one byte, truncating the value to 8 bits. + * + * @param n Value to write. + * @return Number of bytes written. + */ inline size_t write(unsigned int n) { return write((uint8_t)n); } + /** + * @brief Write one byte, truncating the value to 8 bits. + * + * @param n Value to write. + * @return Number of bytes written. + */ inline size_t write(int n) { return write((uint8_t)n); } + /** + * @brief Write a buffer of bytes. + * + * @param buffer Data to write. + * @param size Number of bytes in buffer. + * @return Number of bytes written, 0 if the UART is not started. + */ size_t write(const uint8_t *buffer, size_t size); using Print::write; // pull in write(str) from Print + /** + * @brief Set the RX pin by Arduino pin number. Call before begin(). + * + * @param _rx Digital pin used for RX. + */ void setRx(pin_size_t _rx); + /** + * @brief Set the TX pin by Arduino pin number. Call before begin(). + * + * @param _tx Digital pin used for TX. + */ void setTx(pin_size_t _tx); + /** + * @brief Set the RX pin by STM32 pin name. Call before begin(). + * + * @param _rx Pin used for RX. + */ void setRx(PinName _rx); + /** + * @brief Set the TX pin by STM32 pin name. Call before begin(). + * + * @param _tx Pin used for TX. + */ void setTx(PinName _tx); // Enable HW flow control on RTS, CTS or both + /** + * @brief Enable hardware RTS flow control on a digital pin. + * Call before begin(). + * + * @param _rts Digital pin used for RTS. + */ void setRts(pin_size_t _rts); + /** + * @brief Enable hardware CTS flow control on a digital pin. + * Call before begin(). + * + * @param _cts Digital pin used for CTS. + */ void setCts(pin_size_t _cts); + /** + * @brief Enable hardware RTS and CTS flow control on digital pins. + * Call before begin(). + * + * @param _rts Digital pin used for RTS. + * @param _cts Digital pin used for CTS. + */ void setRtsCts(pin_size_t _rts, pin_size_t _cts); + /** + * @brief Enable hardware RTS flow control on an STM32 pin. + * Call before begin(). + * + * @param _rts Pin used for RTS. + */ void setRts(PinName _rts); + /** + * @brief Enable hardware CTS flow control on an STM32 pin. + * Call before begin(). + * + * @param _cts Pin used for CTS. + */ void setCts(PinName _cts); + /** + * @brief Enable hardware RTS and CTS flow control on STM32 pins. + * Call before begin(). + * + * @param _rts Pin used for RTS. + * @param _cts Pin used for CTS. + */ void setRtsCts(PinName _rts, PinName _cts); // Enable half-duplex mode by setting the Rx pin to NC // This needs to be done before the call to begin() + /** + * @brief Enable half-duplex mode by setting the RX pin to NC. + * Call before begin(). + */ void setHalfDuplex(void); + /** + * @brief Check whether the UART is in half-duplex mode. + * + * @return true if the RX pin is NC. + */ bool isHalfDuplex(void) const; + /** + * @brief Enable reception in half-duplex mode after the pending transmission ends. + */ void enableHalfDuplexRx(void); // Enable HW Rx/Tx/data inversion // This needs to be done before the call to begin() + /** + * @brief Invert the RX line in hardware. Call before begin(). + */ void setRxInvert(void); + /** + * @brief Invert the TX line in hardware. Call before begin(). + */ void setTxInvert(void); + /** + * @brief Invert the data bits in hardware. Call before begin(). + */ void setDataInvert(void); friend class STM32LowPower; // Interrupt handlers + /** + * @brief RX interrupt handler. Stores the received byte in the receive buffer. + * + * @note Called from the UART interrupt context: it must stay short and + * never block. + * If the receive buffer is full, the received byte is dropped. + * + * @param obj UART object that received the byte. + */ static void _rx_complete_irq(serial_t *obj); + /** + * @brief TX interrupt handler. Starts the next chunk of queued data, if any. + * + * @note Called from the UART interrupt context when a transfer completes: + * it must stay short and never block. A chunk never wraps around the end of + * the transmit buffer, so a wrapped buffer is sent in two transfers. + * + * @param obj UART object whose transfer completed. + * @return -1 if a new transfer was started, 0 if the transmit buffer is empty. + */ static int _tx_complete_irq(serial_t *obj); #if defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY) // Could be used to mix Arduino API and STM32Cube HAL API (ex: DMA). Use at your own risk. #if defined(USE_HALV2_DRIVER) + /** + * @brief Get the underlying HAL UART handle. + * + * @return Pointer to the HAL UART handle. + */ hal_uart_handle_t *getHandle(void) #else + /** + * @brief Get the underlying HAL UART handle. + * + * @return Pointer to the HAL UART handle. + */ UART_HandleTypeDef *getHandle(void) #endif { @@ -212,53 +510,109 @@ class Uart : public arduino::HardwareSerial { #endif // HAL_UART_MODULE_ENABLED && !HAL_UART_MODULE_ONLY private: + /** true once begin() has successfully initialized the UART. */ bool _ready; + /** + * true once the receiver is enabled in half-duplex mode, see enableHalfDuplexRx(). + */ bool _rx_enabled; + /** Frame format (SERIAL_xxx) given to begin(), kept to restart the UART. */ uint16_t _config; + /** Baud rate given to begin(), kept to restart the UART. */ unsigned long _baud; + /** + * @brief Initialize the UART object with STM32 pin names. + * + * @param _rx Pin used for RX. + * @param _tx Pin used for TX. + * @param _rts Pin used for RTS, NC if unused. + * @param _cts Pin used for CTS, NC if unused. + */ void init(PinName _rx, PinName _tx, PinName _rts = NC, PinName _cts = NC); + /** + * @brief Reconfigure the UART for low-power operation. + */ void configForLowPower(void); }; #if defined(USART1) + /** + * @brief Hardware serial port 1 (USART1). + */ extern Uart Serial1; #endif #if defined(USART2) + /** + * @brief Hardware serial port 2 (USART2). + */ extern Uart Serial2; #endif #if defined(USART3) + /** + * @brief Hardware serial port 3 (USART3). + */ extern Uart Serial3; #endif #if defined(UART4) || defined(USART4) + /** + * @brief Hardware serial port 4 (UART4 or USART4). + */ extern Uart Serial4; #endif #if defined(UART5) || defined(USART5) + /** + * @brief Hardware serial port 5 (UART5 or USART5). + */ extern Uart Serial5; #endif #if defined(USART6) + /** + * @brief Hardware serial port 6 (USART6). + */ extern Uart Serial6; #endif #if defined(UART7) || defined(USART7) + /** + * @brief Hardware serial port 7 (UART7 or USART7). + */ extern Uart Serial7; #endif #if defined(UART8) || defined(USART8) + /** + * @brief Hardware serial port 8 (UART8 or USART8). + */ extern Uart Serial8; #endif #if defined(UART9) + /** + * @brief Hardware serial port 9 (UART9). + */ extern Uart Serial9; #endif #if defined(UART10) || defined(USART10) + /** + * @brief Hardware serial port 10 (UART10 or USART10). + */ extern Uart Serial10; #endif #if defined(LPUART1) + /** + * @brief Low-power serial port 1 (LPUART1). + */ extern Uart SerialLP1; #endif #if defined(LPUART2) + /** + * @brief Low-power serial port 2 (LPUART2). + */ extern Uart SerialLP2; #endif #if defined(LPUART3) + /** + * @brief Low-power serial port 3 (LPUART3). + */ extern Uart SerialLP3; #endif #endif diff --git a/cores/arduino/Tone.cpp b/cores/arduino/Tone.cpp index f46fc0e9f1..dbc704cef0 100644 --- a/cores/arduino/Tone.cpp +++ b/cores/arduino/Tone.cpp @@ -19,6 +19,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file Tone.cpp + * @brief Tone generation: tone() and noTone(), based on a hardware timer. + */ + #include "Arduino.h" #include "HardwareTimer.h" @@ -38,10 +43,8 @@ static timerPinInfo_t TimerTone_pinInfo = {NC, 0}; static HardwareTimer *TimerTone = NULL; /** - * @brief Tone Period elapsed callback in non-blocking mode - * @param htim : timer handle - * @retval None - */ + * @brief Tone Period elapsed callback in non-blocking mode. + */ static void tonePeriodElapsedCallback() { GPIO_TypeDef *port = get_GPIO_Port(STM_PORT(TimerTone_pinInfo.pin)); @@ -59,11 +62,8 @@ static void tonePeriodElapsedCallback() } /** - * @brief This function will reset the tone timer - * @param port : pointer to port - * @param pin : pin number to toggle - * @retval None - */ + * @brief Reset the tone timer and release the tone pin. + */ static void timerTonePinDeinit() { if (TimerTone != NULL) { @@ -75,6 +75,15 @@ static void timerTonePinDeinit() } } +/** + * @brief Configure the tone timer and output pin for a given frequency and duration. + * + * @param p Pin to output the tone on. + * @param frequency Tone frequency in Hertz. 0 pauses the timer, values above + * MAX_FREQ are ignored. + * @param duration Tone duration in milliseconds. 0 means the tone plays until + * noTone() is called. + */ static void timerTonePinInit(PinName p, uint32_t frequency, uint32_t duration) { uint32_t timFreq = 2 * frequency; @@ -103,7 +112,20 @@ static void timerTonePinInit(PinName p, uint32_t frequency, uint32_t duration) } } -// frequency (in hertz) and duration (in milliseconds). +/** + * @brief Generate a square wave of the given frequency on a pin. + * + * @note The tone is generated by the hardware timer TIMER_TONE, only one pin + * at a time can output a tone. A call on another pin is ignored until noTone() + * is called on the first one. + * Without a TIMER_TONE timer, this function does nothing. + * + * @param _pin Pin to output the tone on. + * @param frequency Frequency of the tone in Hertz. 0 pauses the tone, values + * above 65535 are ignored. + * @param duration Duration of the tone in milliseconds. 0 plays the tone until + * noTone() is called. + */ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { PinName p = digitalPinToPinName(_pin); @@ -120,6 +142,13 @@ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) } +/** + * @brief Stop tone generation on a pin, keeping the tone timer allocated. + * + * Use noTone(uint8_t, bool) with destruct set to true to also release the timer. + * + * @param _pin Pin on which to stop tone generation. + */ void noTone(uint8_t _pin) { noTone(_pin, false); diff --git a/cores/arduino/WInterrupts.cpp b/cores/arduino/WInterrupts.cpp index f25ad4599a..038e745d97 100644 --- a/cores/arduino/WInterrupts.cpp +++ b/cores/arduino/WInterrupts.cpp @@ -16,6 +16,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file WInterrupts.cpp + * @brief External interrupt functions: attachInterrupt() and detachInterrupt(). + */ #include "Arduino.h" @@ -70,6 +74,13 @@ void attachInterrupt(pin_size_t interruptNumber, callback_function_t callback, P stm32_interrupt_enable(p, callback, it_mode); } #endif +/** + * @brief Attach a function to an external interrupt on a pin, using a plain function pointer. + * + * @param interruptNumber Pin to attach the interrupt to. + * @param callback Function called when the interrupt fires. + * @param mode Trigger mode: CHANGE, RISING, FALLING, LOW or HIGH. + */ void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus mode) { #if !defined(HAL_EXTI_MODULE_DISABLED) @@ -82,6 +93,11 @@ void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callback, PinStatus #endif } +/** + * @brief Disable the external interrupt attached to a pin. + * + * @param interruptNumber Pin whose interrupt is disabled. + */ void detachInterrupt(pin_size_t interruptNumber) { #if !defined(HAL_EXTI_MODULE_DISABLED) diff --git a/cores/arduino/WMath.cpp b/cores/arduino/WMath.cpp index 4505c2bd54..879b34971f 100644 --- a/cores/arduino/WMath.cpp +++ b/cores/arduino/WMath.cpp @@ -16,11 +16,23 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file WMath.cpp + * @brief Pseudo-random number functions: randomSeed() and random(). + */ + extern "C" { #include "stdlib.h" #include "stdint.h" } +/** + * @brief Seed the pseudo-random number generator. + * + * A seed of 0 is ignored. + * + * @param dwSeed Seed value. + */ void randomSeed(unsigned long dwSeed) { if (dwSeed != 0) { @@ -28,6 +40,12 @@ void randomSeed(unsigned long dwSeed) } } +/** + * @brief Return a pseudo-random number in the range [0, howbig). + * + * @param howbig Upper bound, exclusive. 0 returns 0. + * @return Pseudo-random number. + */ long random(long howbig) { if (howbig == 0) { @@ -37,6 +55,13 @@ long random(long howbig) return rand() % howbig; } +/** + * @brief Return a pseudo-random number in the range [howsmall, howbig). + * + * @param howsmall Lower bound, inclusive. Returned directly if it is not below howbig. + * @param howbig Upper bound, exclusive. + * @return Pseudo-random number. + */ long random(long howsmall, long howbig) { if (howsmall >= howbig) { diff --git a/cores/arduino/WSerial.cpp b/cores/arduino/WSerial.cpp index 6bb665b0d5..abd3c315ea 100644 --- a/cores/arduino/WSerial.cpp +++ b/cores/arduino/WSerial.cpp @@ -1,5 +1,16 @@ +/** + * @file WSerial.cpp + * @brief serialEventRun(): dispatch of the serialEvent callbacks. + */ #include "WSerial.h" +/** + * @brief Call the serialEvent callback of each enabled serial port (hardware, + * USB, VirtIO) that has data available. + * + * A callback is only called if the sketch defines it. Called from main() after + * each loop() iteration. Weak, so a sketch can override it. + */ WEAK void serialEventRun(void) { #if defined(HAVE_HWSERIAL1) diff --git a/cores/arduino/WSerial.h b/cores/arduino/WSerial.h index 55448c1657..baa149a2a8 100644 --- a/cores/arduino/WSerial.h +++ b/cores/arduino/WSerial.h @@ -1,6 +1,15 @@ #ifndef WIRING_SERIAL_H #define WIRING_SERIAL_H +/** + * @file WSerial.h + * @brief Selection of the generic 'Serial' object and of the matching serialEvent() callback. + * + * The generic Serial is USB CDC, VirtIO or the hardware serial defined by + * SERIAL_UART_INSTANCE. + * ENABLE_xxx macros select the serial ports to enable, HAVE_xxx the ones + * present on the MCU. + */ #include "variant.h" #include "Serial.h" #if defined (USBCON) && defined(USBD_USE_CDC) @@ -23,6 +32,10 @@ #define HAVE_SERIALUSB #endif + /** + * @brief Weak USB CDC serial callback, called by serialEventRun(). Override + * to handle USB data. + */ extern void serialEventUSB(void) __attribute__((weak)); #endif /* USBCON && USBD_USE_CDC */ @@ -39,6 +52,10 @@ #define HAVE_SERIALVIRTIO #endif + /** + * @brief Weak VirtIO serial callback, called by serialEventRun(). Override + * to handle VirtIO data. + */ extern void serialEventVirtIO(void) __attribute__((weak)); #endif /* VIRTIOCON */ @@ -196,42 +213,94 @@ #endif #if defined(HAVE_HWSERIAL1) + /** + * @brief Weak callback for Serial1, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent1(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL2) + /** + * @brief Weak callback for Serial2, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent2(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL3) + /** + * @brief Weak callback for Serial3, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent3(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL4) + /** + * @brief Weak callback for Serial4, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent4(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL5) + /** + * @brief Weak callback for Serial5, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent5(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL6) + /** + * @brief Weak callback for Serial6, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent6(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL7) + /** + * @brief Weak callback for Serial7, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent7(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL8) + /** + * @brief Weak callback for Serial8, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent8(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL9) + /** + * @brief Weak callback for Serial9, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent9(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL10) + /** + * @brief Weak callback for Serial10, called by serialEventRun(). Override to + * handle incoming data. + */ extern void serialEvent10(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIALLP1) + /** + * @brief Weak callback for SerialLP1, called by serialEventRun(). Override + * to handle incoming data. + */ extern void serialEventLP1(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIALLP2) + /** + * @brief Weak callback for SerialLP2, called by serialEventRun(). Override + * to handle incoming data. + */ extern void serialEventLP2(void) __attribute__((weak)); #endif #if defined(HAVE_HWSERIALLP3) + /** + * @brief Weak callback for SerialLP3, called by serialEventRun(). Override + * to handle incoming data. + */ extern void serialEventLP3(void) __attribute__((weak)); #endif #endif /* HAL_UART_MODULE_ENABLED && !HAL_UART_MODULE_ONLY */ diff --git a/cores/arduino/abi.cpp b/cores/arduino/abi.cpp index 9583f3c409..2bbf128556 100644 --- a/cores/arduino/abi.cpp +++ b/cores/arduino/abi.cpp @@ -16,11 +16,21 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file abi.cpp + * @brief C++ ABI support: handlers for pure and deleted virtual function calls. + */ + #include extern "C" void __cxa_pure_virtual(void) __attribute__((__noreturn__)); extern "C" void __cxa_deleted_virtual(void) __attribute__((__noreturn__)); +/** + * @brief Called on a pure virtual function call. Halts the program. + * + * Does not return. + */ void __cxa_pure_virtual(void) { // We might want to write some diagnostics to uart in this case @@ -29,6 +39,11 @@ void __cxa_pure_virtual(void) ; } +/** + * @brief Called when a deleted virtual function is invoked. Halts the program. + * + * Does not return. + */ void __cxa_deleted_virtual(void) { // We might want to write some diagnostics to uart in this case diff --git a/cores/arduino/avr/dtostrf.c b/cores/arduino/avr/dtostrf.c index 6bd602d668..7db13e2fc5 100644 --- a/cores/arduino/avr/dtostrf.c +++ b/cores/arduino/avr/dtostrf.c @@ -18,11 +18,28 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file dtostrf.c + * @brief Emulation of the avr-libc dtostrf() function. + */ + #include #include #include #include +/** + * @brief Convert a double to a string with a fixed number of decimal places. + * + * Emulation of the avr-libc dtostrf() function. + * + * @param val Value to convert. + * @param width Minimum field width of the output string. Negative for left + * adjustment. Range -128 to 127. + * @param prec Number of digits after the decimal point. + * @param sout Buffer to write the result to. Must be large enough for the result. + * @return Pointer to sout. + */ char *dtostrf(double val, signed char width, unsigned char prec, char *sout) { //Commented code is the original version diff --git a/cores/arduino/board.c b/cores/arduino/board.c index 0ca19be820..dcdfd63963 100644 --- a/cores/arduino/board.c +++ b/cores/arduino/board.c @@ -1,11 +1,23 @@ +/** + * @file board.c + * @brief Board-level initialization: init(). + */ #include "board.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief C library initialization. Calls the constructors of static objects. + */ void __libc_init_array(void); +/** + * @brief Board-level initialization, called once before setup(). + * + * Weak, so a variant or sketch can override it. Calls hw_config_init(). + */ WEAK void init(void) { hw_config_init(); diff --git a/cores/arduino/board.h b/cores/arduino/board.h index dcd586bbdd..98e3bd2f34 100644 --- a/cores/arduino/board.h +++ b/cores/arduino/board.h @@ -1,3 +1,8 @@ +/** + * @file board.h + * @brief Aggregates the headers of the core and peripherals drivers (clock, + * analog, timer, uart, ...). + */ #ifndef _BOARD_H_ #define _BOARD_H_ diff --git a/cores/arduino/core_debug.c b/cores/arduino/core_debug.c index 8899b4c4d9..cdb93c319b 100644 --- a/cores/arduino/core_debug.c +++ b/cores/arduino/core_debug.c @@ -1,3 +1,8 @@ +/** + * @file core_debug.c + * @brief Out-of-line definitions of the inline core_debug functions, needed + * for C code. + */ #include "core_debug.h" // Ensure inline functions have a definition emitted for when they are diff --git a/cores/arduino/core_debug.h b/cores/arduino/core_debug.h index f5a74e62f3..c2934aec39 100644 --- a/cores/arduino/core_debug.h +++ b/cores/arduino/core_debug.h @@ -1,3 +1,7 @@ +/** + * @file core_debug.h + * @brief Debug message output helpers: core_debug() and vcore_debug(). + */ #ifndef _CORE_DEBUG_H #define _CORE_DEBUG_H @@ -10,12 +14,14 @@ extern "C" { #endif -/** Output a debug message +/** + * @brief Output a debug message on stderr. Does nothing when NDEBUG is defined. * - * @param format printf-style format string, followed by variables - * Note: By using the printf function of the library C this inflates the size of - * the code, use a lot of stack. An alternative, will be to implement a tiny + * @note Using the printf function of the C library inflates the size of the + * code and uses a lot of stack. An alternative would be to implement a tiny * and limited functionality implementation of printf. + * + * @param format printf-style format string, followed by the variables to print. */ inline void core_debug(const char *format, ...) { @@ -29,6 +35,13 @@ inline void core_debug(const char *format, ...) #endif /* NDEBUG */ } +/** + * @brief Output a debug message on stderr from a va_list. Does nothing when + * NDEBUG is defined. + * + * @param format printf-style format string. + * @param args Variable argument list matching format. + */ inline void vcore_debug(const char *format, va_list args) { #if !defined(NDEBUG) diff --git a/cores/arduino/hooks.c b/cores/arduino/hooks.c index a85150bca5..5e6eb03d6d 100644 --- a/cores/arduino/hooks.c +++ b/cores/arduino/hooks.c @@ -16,15 +16,20 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file hooks.c + * @brief Weak hooks overridable by libraries or sketches: yield() and dtr_togglingHook(). + */ + #include /** - * Empty yield() hook. + * @brief Empty yield() hook. * * This function is intended to be used by library writers to build - * libraries or sketches that supports cooperative threads. + * libraries or sketches that support cooperative threads. * - * Its defined as a weak symbol and it can be redefined to implement a + * It is defined as a weak symbol and it can be redefined to implement a * real cooperative scheduler. */ static void __empty() @@ -35,13 +40,16 @@ void yield(void) __attribute__((weak, alias("__empty"))); #ifdef DTR_TOGGLING_SEQ /** - * Empty dtr_toggling() hook. + * @brief Empty dtr_toggling() hook. * * This function is intended to be used by library writers to build * libraries or sketches that require DTR toggling feature. * - * Its defined as a weak symbol and it can be redefined to implement - * task to achieve in this case. + * It is defined as a weak symbol (dtr_togglingHook()) and it can be redefined + * to implement the task to achieve in this case. + * + * @param buf Buffer of the received data. + * @param len Pointer to the length of the received data. */ static void __empty_dtr_toggling(uint8_t *buf, uint32_t *len) { diff --git a/cores/arduino/itoa.c b/cores/arduino/itoa.c index e737b6f14d..fd34e75110 100644 --- a/cores/arduino/itoa.c +++ b/cores/arduino/itoa.c @@ -16,12 +16,25 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file itoa.c + * @brief Integer to ASCII conversion: itoa(), utoa(), ltoa() and ultoa(). + */ + #include #ifdef __cplusplus extern "C" { #endif +/** + * @brief Convert a long to a null-terminated string. + * + * @param value Value to convert. Negative values are only signed for radix 10. + * @param string Buffer to write the result to. Must be large enough for the result. + * @param radix Base to use, from 2 to 36. + * @return Pointer to string, or NULL if string is NULL or radix is out of range. + */ char *ltoa(long value, char *string, int radix) { char tmp[33]; @@ -69,6 +82,14 @@ char *ltoa(long value, char *string, int radix) return string; } +/** + * @brief Convert an unsigned long to a null-terminated string. + * + * @param value Value to convert. + * @param string Buffer to write the result to. Must be large enough for the result. + * @param radix Base to use, from 2 to 36. + * @return Pointer to string, or NULL if string is NULL or radix is out of range. + */ char *ultoa(unsigned long value, char *string, int radix) { char tmp[33]; @@ -106,11 +127,27 @@ char *ultoa(unsigned long value, char *string, int radix) return string; } +/** + * @brief Convert an int to a null-terminated string. + * + * @param value Value to convert. Negative values are only signed for radix 10. + * @param string Buffer to write the result to. Must be large enough for the result. + * @param radix Base to use, from 2 to 36. + * @return Pointer to string, or NULL if string is NULL or radix is out of range. + */ char *itoa(int value, char *string, int radix) { return ltoa(value, string, radix) ; } +/** + * @brief Convert an unsigned int to a null-terminated string. + * + * @param value Value to convert. + * @param string Buffer to write the result to. Must be large enough for the result. + * @param radix Base to use, from 2 to 36. + * @return Pointer to string, or NULL if string is NULL or radix is out of range. + */ char *utoa(unsigned value, char *string, int radix) { return ultoa(value, string, radix) ; diff --git a/cores/arduino/main.cpp b/cores/arduino/main.cpp index 67dbcfc44a..643dbd95ac 100644 --- a/cores/arduino/main.cpp +++ b/cores/arduino/main.cpp @@ -17,11 +17,23 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file main.cpp + * @brief Entry point of Arduino sketches: early initialization and main loop. + */ + #define ARDUINO_MAIN #include "Arduino.h" -// Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated objects that need HAL may fail. +/** + * @brief Early initialization, run before static object allocation. + * + * Sets the NVIC priority grouping, enables the caches if present, then calls init(). + * + * @note Forced to be called *first* (constructor priority 101), i.e. before + * static object allocation. Otherwise, statically allocated objects that need + * the HAL may fail. + */ __attribute__((constructor(101))) void premain() { @@ -42,8 +54,13 @@ __attribute__((constructor(101))) void premain() init(); } -/* - * \brief Main entry point of Arduino application +/** + * @brief Main entry point of the Arduino application. + * + * Calls initVariant() and setup() once, then runs loop() and serialEventRun() + * forever. When CORE_CALLBACK is defined, CoreCallback() is called before each loop(). + * + * @return 0. Never reached in practice, the main loop never ends. */ int main(void) { diff --git a/cores/arduino/pins_arduino.c b/cores/arduino/pins_arduino.c index 6f1b316956..20a82ce871 100644 --- a/cores/arduino/pins_arduino.c +++ b/cores/arduino/pins_arduino.c @@ -16,6 +16,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file pins_arduino.c + * @brief Conversions between pin numbers, analog inputs and PinName. + */ + #include "pins_arduino.h" #ifdef __cplusplus @@ -68,11 +73,6 @@ PinName analogInputToPinName(pin_size_t pin) return pn; } -/** - * @brief Return true if a digital pin is an analog input - * @param pin Dx, x or PYn - * @retval boolean true if analog or false - */ bool digitalpinIsAnalogInput(pin_size_t pin) { bool ret = false; @@ -91,11 +91,6 @@ bool digitalpinIsAnalogInput(pin_size_t pin) return ret; } -/** - * @brief Return the analog input linked to a digital pin - * @param pin Dx, x or PYn - * @retval analogInput valid analog input or NUM_ANALOG_INPUTS - */ pin_size_t digitalPinToAnalogInput(pin_size_t pin) { pin_size_t ret = NUM_ANALOG_INPUTS; diff --git a/cores/arduino/pins_arduino.h b/cores/arduino/pins_arduino.h index 7b109e9ec8..c678675c44 100644 --- a/cores/arduino/pins_arduino.h +++ b/cores/arduino/pins_arduino.h @@ -15,6 +15,10 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file pins_arduino.h + * @brief Pin number conversion helpers, port register macros and default pin definitions. + */ #ifndef _PINS_ARDUINO_H_ #define _PINS_ARDUINO_H_ #include @@ -26,13 +30,10 @@ #include "pins_arduino_analog.h" #include "pins_arduino_digital.h" -/* - * Pin number mask - * allows to retrieve the pin number without ALTx - */ +/** Pin number mask, allows to retrieve the pin number without ALTx. */ #define PNUM_MASK 0xFF -/* Pin not defined */ +/** Pin not defined. */ #define PNUM_NOT_DEFINED NUM_DIGITAL_PINS /* Avoid PortName issue */ @@ -42,7 +43,7 @@ _Static_assert(NUM_ANALOG_INPUTS <= MAX_ANALOG_INPUTS, "Core NUM_ANALOG_INPUTS limited to MAX_ANALOG_INPUTS"); /* Default for Arduino connector compatibility */ -/* SPI Definitions */ +/* SPI Definitions: default pin numbers, could be redefined in the variant */ #ifndef PIN_SPI_SS #define PIN_SPI_SS 10 #endif @@ -73,7 +74,7 @@ static const pin_size_t MOSI = PIN_SPI_MOSI; static const pin_size_t MISO = PIN_SPI_MISO; static const pin_size_t SCK = PIN_SPI_SCK; -/* I2C Definitions */ +/* I2C Definitions: default pin numbers, could be redefined in the variant */ #ifndef PIN_WIRE_SDA #define PIN_WIRE_SDA 14 #endif @@ -84,7 +85,7 @@ static const pin_size_t SCK = PIN_SPI_SCK; static const pin_size_t SDA = PIN_WIRE_SDA; static const pin_size_t SCL = PIN_WIRE_SCL; -/* I3C Definitions */ +/* I3C Definitions: default pin numbers, could be redefined in the variant */ #ifndef PIN_I3C_SDA #define PIN_I3C_SDA 14 #endif @@ -101,9 +102,20 @@ extern "C" { extern const PinName digitalPin[]; extern const pin_size_t analogInputPin[]; +/** + * @brief Value returned by digitalPinToInterrupt() when the pin cannot be used + * as an interrupt. + */ #define NOT_AN_INTERRUPT (pin_size_t)NC -/* Convert a digital pin number Dxx to a PinName PX_n */ +/** + * @brief Convert a digital pin number (Dxx) to a PinName (PX_n). + * + * The ALTX bits of the pin number are preserved. An analog pin number (Ax) is also accepted. + * + * @param p Digital pin number. + * @return PinName of the pin. NC if the pin is not valid. + */ #if NUM_ANALOG_INPUTS > 0 /* Note: Analog pin is also a digital pin */ #define digitalPinToPinName(p) ((((pin_size_t)(p) & PNUM_MASK) < NUM_DIGITAL_PINS) ? \ @@ -115,13 +127,26 @@ extern const pin_size_t analogInputPin[]; #define digitalPinToPinName(p) ((((pin_size_t)(p) & PNUM_MASK) < NUM_DIGITAL_PINS) ? \ (PinName)(digitalPin[(pin_size_t)(p) & PNUM_MASK] | ((p) & ALTX_MASK)) : NC) #endif /* NUM_ANALOG_INPUTS > 0 */ -/* Convert a PinName PX_n to a digital pin number */ +/** + * @brief Convert a PinName (PX_n) to a digital pin number. + * + * @param p PinName to convert. + * @return Digital pin number, with the ALTX bits preserved. + * NUM_DIGITAL_PINS if not found. + */ pin_size_t pinNametoDigitalPin(PinName p); -/* Convert an analog pin number to a digital pin number */ +/** + * @brief Convert an analog pin number (Ax) to a digital pin number. + * + * Used by the analogRead API to have A0 == 0, even if the analog pins are + * not contiguous in the digitalPin array. + * + * @param p Analog pin number. + * @return Digital pin number, with the ALTX bits preserved. NC if the pin is + * not valid, or NUM_DIGITAL_PINS when no analog input is defined. + */ #if NUM_ANALOG_INPUTS > 0 -/* Used by analogRead api to have A0 == 0 */ -/* Non contiguous analog pins definition in digitalPin array */ #define analogInputToDigitalPin(p) ((((pin_size_t)(p) & PNUM_MASK) < NUM_ANALOG_INPUTS) ? \ analogInputPin[(pin_size_t)(p) & PNUM_MASK] | ((pin_size_t)(p) & ALTX_MASK) : \ (((pin_size_t)(p) & PNUM_ANALOG_BASE) == PNUM_ANALOG_BASE) && \ @@ -131,31 +156,59 @@ pin_size_t pinNametoDigitalPin(PinName p); #define analogInputToDigitalPin(p) (NUM_DIGITAL_PINS) #endif /* NUM_ANALOG_INPUTS > 0 */ -/* Convert an analog pin number Ax to a PinName PX_n */ +/** + * @brief Convert an analog pin number (Ax) to a PinName (PX_n). + * + * @note The internal channels (ATEMP, ATEMP_ADC5, AVREF, AVBAT) are also + * handled when defined. + * + * @param pin Analog pin number, Ax. + * @return PinName of the analog input. NC if the pin is not an analog input. + */ PinName analogInputToPinName(pin_size_t pin); -/* All pins could manage EXTI */ +/** + * @brief Convert a digital pin number to an interrupt number. + * + * All pins could manage EXTI, so the pin number is used as interrupt number. + * + * @param p Digital pin number. + * @return The pin number if it is valid, NOT_AN_INTERRUPT otherwise. + */ #define digitalPinToInterrupt(p) (digitalPinIsValid(p) ? p : NOT_AN_INTERRUPT) +/** Check whether a digital pin has an I2C (SDA or SCL) function. */ #define digitalPinHasI2C(p) (pin_in_pinmap(digitalPinToPinName(p), PinMap_I2C_SDA) ||\ pin_in_pinmap(digitalPinToPinName(p), PinMap_I2C_SCL)) +/** Check whether a digital pin has a PWM (timer) function. */ #define digitalPinHasPWM(p) (pin_in_pinmap(digitalPinToPinName(p), PinMap_TIM)) +/** Check whether a digital pin has a UART (RX or TX) function. */ #define digitalPinHasSerial(p) (pin_in_pinmap(digitalPinToPinName(p), PinMap_UART_RX) ||\ pin_in_pinmap(digitalPinToPinName(p), PinMap_UART_TX)) +/** + * Check whether a digital pin has an SPI (MOSI, MISO, SCLK or SSEL) function. + */ #define digitalPinHasSPI(p) (pin_in_pinmap(digitalPinToPinName(p), PinMap_SPI_MOSI) ||\ pin_in_pinmap(digitalPinToPinName(p), PinMap_SPI_MISO) ||\ pin_in_pinmap(digitalPinToPinName(p), PinMap_SPI_SCLK) ||\ pin_in_pinmap(digitalPinToPinName(p), PinMap_SPI_SSEL)) +/** Get the GPIO port (GPIO_TypeDef pointer) of a digital pin. */ #define digitalPinToPort(p) (get_GPIO_Port(STM_PORT(digitalPinToPinName(p)))) +/** Get the bit mask of a digital pin in its GPIO port registers. */ #define digitalPinToBitMask(p) (STM_GPIO_PIN(digitalPinToPinName(p))) +/** Get the bit mask of an analog pin in its GPIO port registers. */ #define analogInPinToBit(p) (STM_GPIO_PIN(digitalPinToPinName(p))) +/** Get the address of the output data register of a GPIO port. */ #define portOutputRegister(P) (&(P->ODR)) +/** Get the address of the input data register of a GPIO port. */ #define portInputRegister(P) (&(P->IDR)) +/** Get the address of the set register of a GPIO port. */ #define portSetRegister(P) (&(P->BSRR)) +/* Get the address of the clear register of a GPIO port */ #if defined(STM32F2xx) || defined(STM32F4xx) || defined(STM32F7xx) /* For those series reset are in the high part so << 16U needed */ #define portClearRegister(P) (&(P->BSRR)) @@ -164,6 +217,7 @@ PinName analogInputToPinName(pin_size_t pin); #endif +/* Get the address of the mode register of a GPIO port */ #if defined(STM32F1xx) /* * Config registers split in 2 registers: @@ -175,25 +229,55 @@ PinName analogInputToPinName(pin_size_t pin); #else #define portModeRegister(P) (&(P->MODER)) #endif +/** + * Get the address of the mode register of a GPIO port. + * Same as portModeRegister(). + */ #define portConfigRegister(P) (portModeRegister(P)) +/** Check whether a digital pin number is valid, i.e. linked to a PinName. */ #define digitalPinIsValid(p) (digitalPinToPinName(p) != NC) -/* As some pin could be duplicated in digitalPin[] */ -/* return first occurrence of linked PinName (PYx) */ +/** + * @brief Get the first occurrence of a pin in digitalPin[]. + * + * Some pins could be duplicated in digitalPin[], this returns the digital pin + * number of the first occurrence of the linked PinName (PYx). + * + * @param p Digital pin number. + * @return Digital pin number of the first occurrence. + * NUM_DIGITAL_PINS if not found. + */ #define digitalPinFirstOccurence(p) (pinNametoDigitalPin(digitalPinToPinName(p))) /* Specific for Firmata */ #if defined(PIN_SERIAL_RX) && defined(PIN_SERIAL_TX) +/** + * Check whether a digital pin is the RX or TX pin of the default serial (Firmata). + */ #define pinIsSerial(p) ((digitalPinToPinName(p) == \ digitalPinToPinName(PIN_SERIAL_RX & PNUM_MASK)) ||\ (digitalPinToPinName(p) == \ digitalPinToPinName(PIN_SERIAL_TX & PNUM_MASK))) #endif -/* Convenient macro to handle Analog for Firmata */ +/** + * Convenient macro to handle Analog for Firmata, see digitalpinIsAnalogInput(). + */ #define pinIsAnalogInput digitalpinIsAnalogInput +/** + * @brief Check whether a digital pin is an analog input. + * + * @param pin Dx, x or PYn. + * @return true if the pin is an analog input, false otherwise. + */ bool digitalpinIsAnalogInput(pin_size_t pin); +/** + * @brief Get the analog input linked to a digital pin. + * + * @param pin Dx, x or PYn. + * @return Valid analog input number, or NUM_ANALOG_INPUTS if none. + */ pin_size_t digitalPinToAnalogInput(pin_size_t pin); #ifdef __cplusplus @@ -202,12 +286,15 @@ pin_size_t digitalPinToAnalogInput(pin_size_t pin); /* Default Definitions, could be redefined in variant.h */ #ifndef ADC_RESOLUTION + /** Default resolution of analogRead() values, in bits. */ #define ADC_RESOLUTION 10 #endif +/** Resolution of the DAC, in bits. */ #define DACC_RESOLUTION 12 #ifndef PWM_RESOLUTION + /** Default resolution of analogWrite() values, in bits. */ #define PWM_RESOLUTION 8 #endif @@ -217,9 +304,11 @@ _Static_assert((PWM_RESOLUTION > 0) &&(PWM_RESOLUTION <= 32), "PWM_RESOLUTION must be 0 < x <= 32!"); #ifndef PWM_FREQUENCY + /** Default PWM frequency of analogWrite(), in Hertz. */ #define PWM_FREQUENCY 1000 #endif #ifndef PWM_MAX_DUTY_CYCLE + /** Maximum PWM duty cycle value. */ #define PWM_MAX_DUTY_CYCLE 4095 #endif diff --git a/cores/arduino/pins_arduino_analog.h b/cores/arduino/pins_arduino_analog.h index 031bfdba23..0007c354b3 100644 --- a/cores/arduino/pins_arduino_analog.h +++ b/cores/arduino/pins_arduino_analog.h @@ -10,22 +10,28 @@ * ******************************************************************************* */ +/** + * @file pins_arduino_analog.h + * @brief Analog pin numbering: Arduino analog pins (A0...), PIN_Ax and ADC internal channels. + */ #ifndef _PINS_ARDUINO_ANALOG_H_ #define _PINS_ARDUINO_ANALOG_H_ #include "variant.h" #include "PinNames.h" -/* - * Pin number Analog base +/** + * @brief Pin number Analog base. + * * To check if a pin number is an analog pin: * ((p & PNUM_ANALOG_BASE) == PNUM_ANALOG_BASE) * if true then p is an analog pin number * else not */ #define PNUM_ANALOG_BASE 0xC0 -/* - * Pin number Analog index - * allows to retrieve the pin number index of an analog pin +/** + * @brief Pin number Analog index. + * + * Allows to retrieve the pin number index of an analog pin * in the analogInputPin[] array: * analogInputPin[p & PNUM_ANALOG_INDEX] */ @@ -33,25 +39,33 @@ /* Arduino analog pins */ #ifndef NUM_ANALOG_INPUTS + /** Number of Arduino analog pins (A0...). 0 when the variant defines none. */ #define NUM_ANALOG_INPUTS 0 #endif -/* Analog internal pin numbers follow the analog pin number */ +/** + * First analog internal pin number. + * Analog internal pin numbers follow the analog pin numbers. + */ #define NUM_ANALOG_INTERNAL_FIRST (PNUM_ANALOG_BASE + NUM_ANALOG_INPUTS) /* ADC internal channels (not a pins) */ /* Only used for analogRead() */ #if defined(ADC_CHANNEL_TEMPSENSOR) || defined(ADC_CHANNEL_TEMPSENSOR_ADC1) ||\ defined(LL_ADC_CHANNEL_TEMPSENSOR) + /** Internal temperature sensor channel, to use with analogRead(). */ #define ATEMP (NUM_ANALOG_INTERNAL_FIRST) #endif #if defined(ADC_CHANNEL_VREFINT) || defined(LL_ADC_CHANNEL_VREFINT) + /** Internal voltage reference channel, to use with analogRead(). */ #define AVREF (NUM_ANALOG_INTERNAL_FIRST + 1) #endif #ifdef ADC_CHANNEL_VBAT + /** Internal battery voltage channel, to use with analogRead(). */ #define AVBAT (NUM_ANALOG_INTERNAL_FIRST + 2) #endif #if defined(ADC5) && defined(ADC_CHANNEL_TEMPSENSOR_ADC5) + /** Internal temperature sensor channel of ADC5, to use with analogRead(). */ #define ATEMP_ADC5 (NUM_ANALOG_INTERNAL_FIRST + 3) #endif @@ -442,6 +456,7 @@ enum { , A63 = PIN_A63 #endif }; +/** Maximum number of Arduino analog pins supported by the core. */ #define MAX_ANALOG_INPUTS 64 #else #define MAX_ANALOG_INPUTS 0 diff --git a/cores/arduino/pins_arduino_digital.h b/cores/arduino/pins_arduino_digital.h index 29c6212981..f2570eba13 100644 --- a/cores/arduino/pins_arduino_digital.h +++ b/cores/arduino/pins_arduino_digital.h @@ -10,12 +10,29 @@ * ******************************************************************************* */ +/** + * @file pins_arduino_digital.h + * @brief Digital pin numbering: Arduino digital pins alias (Dx) and PYn_ALT0 pin numbers. + */ #ifndef _PINS_ARDUINO_DIGITAL_H_ #define _PINS_ARDUINO_DIGITAL_H_ #include "variant.h" -/* GPIO port (A to K + Z) * 16 pins: 192 */ -/* Arduino digital pins alias */ +/* + * Digital pin numbering scheme: + * - A digital pin number is an index in the digitalPin[] array of the variant, + * which gives the PinName (PX_n) of the pin. Only the low 8 bits + * (PNUM_MASK) are the index. + * - Dx (D0, D1, ...) is the Arduino alias of the pin number x. + * - PYn (PA0, PB3, ...) are the STM32 pin numbers defined in the variant.h. + * They are numbers like Dx, not PinName: PYn is the index of the pin Y n + * in digitalPin[], or an analog pin number (PIN_Ax) for pins which are + * also an analog input. + * - Bits 8 to 10 (ALTX_MASK) select an alternate PinName (ALT0 to ALT7), + * see PYn_ALTx below. + * GPIO port (A to K + Z) * 16 pins: 192 maximum. + */ +/** Arduino digital pins alias (D0 to D191), limited by NUM_DIGITAL_PINS. */ enum { D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, @@ -537,7 +554,14 @@ enum { /* * Each STM32 pin number PYn defined in the variant.h - * is also the alternate pin number PYn_ALT0 + * is also the alternate pin number PYn_ALT0. + * + * PYn_ALTx (x from 0 to 7) is the pin number PYn with the ALTx bits set + * (PYn | ALTx). It selects the alternate PinName PY_n_ALTx (e.g. PA_0_ALT1) of + * the pin, i.e. the duplicated entry of PYn in the peripheral pin maps + * (PeripheralPins.c), when the same pin has several possible functions + * (e.g. several timer channels or ADC instances). + * Only the PYn_ALT0 aliases are defined here, the other ones are defined by the variant. */ #ifdef PA0 #define PA0_ALT0 (PA0 | ALT0) diff --git a/cores/arduino/stm32/startup_stm32yyxx.S b/cores/arduino/stm32/startup_stm32yyxx.S index aef54aef98..0182c637dd 100644 --- a/cores/arduino/stm32/startup_stm32yyxx.S +++ b/cores/arduino/stm32/startup_stm32yyxx.S @@ -1,3 +1,8 @@ +/** + * @file startup_stm32yyxx.S + * @brief Includes the CMSIS startup file (CMSIS_STARTUP_FILE) of the selected + * device when using the HAL v1 driver. + */ #if !defined(USE_HALV2_DRIVER) #include "stm32_def_build.h" diff --git a/cores/arduino/stm32/startup_stm32yyxx.c b/cores/arduino/stm32/startup_stm32yyxx.c index 6ae7572e73..b026e19b7a 100644 --- a/cores/arduino/stm32/startup_stm32yyxx.c +++ b/cores/arduino/stm32/startup_stm32yyxx.c @@ -1,3 +1,8 @@ +/** + * @file startup_stm32yyxx.c + * @brief Includes the CMSIS startup file of the selected device when using the + * HAL v2 driver. + */ #ifndef _STARTUP_STM32YYXX_C_ #define _STARTUP_STM32YYXX_C_ diff --git a/cores/arduino/stm32/stm32_def_build.h b/cores/arduino/stm32/stm32_def_build.h index d83487a445..8a0ab4974f 100644 --- a/cores/arduino/stm32/stm32_def_build.h +++ b/cores/arduino/stm32/stm32_def_build.h @@ -1,3 +1,7 @@ +/** + * @file stm32_def_build.h + * @brief Selects the CMSIS startup file (CMSIS_STARTUP_FILE) matching the STM32 device. + */ #ifndef _STM32_DEF_BUILD_ #define _STM32_DEF_BUILD_ diff --git a/cores/arduino/stm32/stm32yyxx_hal_conf.h b/cores/arduino/stm32/stm32yyxx_hal_conf.h index 53a2437544..f96db9ef9b 100644 --- a/cores/arduino/stm32/stm32yyxx_hal_conf.h +++ b/cores/arduino/stm32/stm32yyxx_hal_conf.h @@ -1,3 +1,7 @@ +/** + * @file stm32yyxx_hal_conf.h + * @brief Selects the HAL modules to enable, for the HAL v1 and HAL v2 drivers. + */ #ifndef __STM32YYxx_HAL_CONF_H #define __STM32YYxx_HAL_CONF_H diff --git a/cores/arduino/utils.h b/cores/arduino/utils.h index 84b18cca10..de237df8cb 100644 --- a/cores/arduino/utils.h +++ b/cores/arduino/utils.h @@ -1,13 +1,18 @@ +/** + * @file utils.h + * @brief Preprocessor helpers: string concatenation and stringification. + */ #ifndef __UTILS_H #define __UTILS_H -/* Concatenate 2 strings */ +/** Concatenate 2 string literals. */ #define CONCAT(s1, s2) (s1 s2) -/* Concatenate 2 strings separated by space */ +/** Concatenate 2 string literals separated by a space. */ #define CONCATS(s1, s2) (s1" " s2) -/* Stringification */ +/** Stringification of a macro argument, after macro expansion. */ #define XSTR(s) STR(s) +/** Stringification of a macro argument, without macro expansion. */ #define STR(s) #s #endif diff --git a/cores/arduino/variant.h b/cores/arduino/variant.h index f08e5de422..cca3dab521 100644 --- a/cores/arduino/variant.h +++ b/cores/arduino/variant.h @@ -10,6 +10,11 @@ * ******************************************************************************* */ +/** + * @file variant.h + * @brief Includes the header of the selected variant (VARIANT_H) and the + * generic Serial selection. + */ #pragma once #include VARIANT_H diff --git a/cores/arduino/wiring.h b/cores/arduino/wiring.h index d74e8ed58f..6a0904d7d0 100644 --- a/cores/arduino/wiring.h +++ b/cores/arduino/wiring.h @@ -17,6 +17,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file wiring.h + * @brief STM32 wiring extensions: analog resolution and frequency, + * digitalToggle(), noTone(), attachInterrupt() and timing helper macros. + */ + #ifndef _WIRING_H_ #define _WIRING_H_ @@ -33,51 +39,74 @@ #include "dwt.h" #include "interrupt.h" -/* wirinig_analog*/ +/* wiring_analog */ #ifdef __cplusplus extern "C" { #endif -/* - * \brief Set the resolution of analogRead return values. Default is 10 bits (range from 0 to 1023). +/** + * @brief Set the resolution of analogRead return values. + * Default is 10 bits (range from 0 to 1023). + * + * @note The ADC is configured with the closest supported hardware resolution + * (capped to the maximum of the ADC) and the result is scaled to the requested + * resolution. Values outside the range 1 to 32 call Error_Handler(). + * + * @see analogRead(), analogWriteResolution() * - * \param res + * @param res Number of bits of the resolution, from 1 to 32. */ extern void analogReadResolution(int res); -/* - * \brief Set the resolution of analogWrite parameters. Default is 8 bits (range from 0 to 255). +/** + * @brief Set the resolution of analogWrite parameters. + * Default is 8 bits (range from 0 to 255). + * + * @note The PWM hardware resolution is capped to 16 bits and the value is + * scaled accordingly. Values outside the range 1 to 32 call Error_Handler(). + * + * @see analogWrite(), analogReadResolution(), analogWriteFrequency() * - * \param res + * @param res Number of bits of the resolution, from 1 to 32. */ extern void analogWriteResolution(int res); -/* - * \brief Set the frequency of analogWrite. Default is PWM_FREQUENCY (1000) in Hertz. +/** + * @brief Set the frequency of analogWrite. + * Default is PWM_FREQUENCY (1000) in Hertz. + * + * @see analogWrite(), analogWriteResolution() * - * \param freq + * @param freq PWM frequency in Hertz. */ extern void analogWriteFrequency(uint32_t freq); /* wiring_constants */ +/** Default analog reference, kept for AVR compatibility. */ #define DEFAULT 1 +/** External analog reference, kept for AVR compatibility. */ #define EXTERNAL 0 +/** Enable all interrupts. */ #define interrupts() __enable_irq() +/** Disable all interrupts. */ #define noInterrupts() __disable_irq() #ifndef _BV +/** Value of a bit mask with only the given bit set. */ #define _BV(bit) (1 << (bit)) #endif #ifndef cbi +/** Clear the bits of bitmask in the register pointed to by reg. */ #define cbi(reg, bitmask) *reg &= ~bitmask #endif #ifndef sbi +/** Set the bits of bitmask in the register pointed to by reg. */ #define sbi(reg, bitmask) *reg |= bitmask #endif /* wiring_digital */ /** - * \brief Toggle the value from a specified digital pin. + * @brief Toggle the value from a specified digital pin. * - * \param ulPin The number of the digital pin you want to toggle (int) + * @param pinNumber The number of the digital pin you want to toggle. */ extern void digitalToggle(pin_size_t pinNumber); @@ -86,13 +115,25 @@ extern void digitalToggle(pin_size_t pinNumber); #endif #ifdef __cplusplus - /* - * \brief Stop tone generation on pin. + /** + * @brief Stop tone generation on pin. * - * \param _pin + * @param _pin The pin on which to stop tone generation. + * @param destruct If true, the tone timer is deinitialized and deleted. + * If false, it is only paused. */ extern void noTone(uint8_t _pin, bool destruct); #if !defined(HAL_EXTI_MODULE_DISABLED) + /** + * @brief Attach a callback function to an external interrupt on a pin. + * + * @note LOW and HIGH level triggers are only available on series supporting + * level interrupts, otherwise they fall back to FALLING and RISING respectively. + * + * @param pin The pin to attach the interrupt to. + * @param callback Function called when the interrupt fires. It takes no arguments. + * @param mode Trigger mode: CHANGE, RISING, FALLING, LOW or HIGH. + */ extern void attachInterrupt(pin_size_t pin, callback_function_t callback, PinStatus mode); #endif #endif @@ -105,8 +146,11 @@ extern void digitalToggle(pin_size_t pinNumber); #endif // __cplusplus +/** Number of CPU clock cycles per microsecond. */ #define clockCyclesPerMicrosecond() ( SystemCoreClock / 1000000L ) +/** Convert a number of CPU clock cycles to microseconds. */ #define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (SystemCoreClock / 1000L) ) +/** Convert a number of microseconds to CPU clock cycles. */ #define microsecondsToClockCycles(a) ( (a) * (SystemCoreClock / 1000000L) ) #endif /* _WIRING_H_ */ diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index 603e6c2680..96b34f3c79 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -16,6 +16,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file wiring_analog.c + * @brief Analog input (ADC) and analog output (PWM or DAC) functions. + */ + #include "Arduino.h" #include "PinConfigured.h" @@ -80,7 +85,14 @@ static int _internalReadResolution = #endif /* ADC_RESOLUTION > MAX_ADC_RESOLUTION */ ; -/* Common utility function to map a value from one resolution to another */ +/** + * @brief Map a value from one resolution to another. + * + * @param value Value to convert. + * @param from Resolution of value, in bits. + * @param to Target resolution, in bits. + * @return Value scaled to the target resolution. + */ static inline uint32_t mapResolution(uint32_t value, uint32_t from, uint32_t to) { if (from != to) { @@ -154,13 +166,26 @@ void analogReadResolution(int res) } } +/** + * @brief Set the analog reference voltage. + * Not supported on this core, the call has no effect. + * + * @param mode Reference mode, ignored. + */ void analogReference(uint8_t mode) { (void)mode; } -// Perform the read operation on the selected analog pin. -// the initialization of the analog PIN is done through this function +/** + * @brief Read the analog value of a pin, scaled to the resolution set by analogReadResolution(). + * + * The initialization of the analog pin is done through this function. + * + * @param pinNumber Analog pin number, e.g. A0 or PA0. + * @return Converted value, between 0 and 2^resolution - 1. + * 0 if the pin has no ADC. + */ int analogRead(pin_size_t pinNumber) { pin_size_t value = 0; @@ -198,8 +223,17 @@ void analogWriteFrequency(uint32_t freq) _writeFreq = freq; } -// Output only works on the pins with Hardware Timer (PWM) or DAC support. -// For the other pins, default to digital output. +/** + * @brief Write an analog value to a pin, using PWM or DAC if the pin supports it. + * + * Output only works on the pins with Hardware Timer (PWM) or DAC support. + * For the other pins, it defaults to digital output: the value is scaled to + * 8 bits and the pin is set HIGH if it is 128 or more, LOW otherwise. + * The DAC takes precedence over PWM when a pin supports both. + * + * @param pinNumber Pin number to write to. + * @param value Value to write, scaled to the resolution set by analogWriteResolution(). + */ void analogWrite(pin_size_t pinNumber, int value) { PinName p = digitalPinToPinName(pinNumber); diff --git a/cores/arduino/wiring_digital.c b/cores/arduino/wiring_digital.c index 104ce20904..b366891715 100644 --- a/cores/arduino/wiring_digital.c +++ b/cores/arduino/wiring_digital.c @@ -16,6 +16,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file wiring_digital.c + * @brief Digital I/O functions: pinMode(), digitalWrite(), digitalRead() and digitalToggle(). + */ + #include "Arduino.h" #include "PinConfigured.h" @@ -26,6 +31,15 @@ extern "C" { extern pin_size_t g_anOutputPinConfigured[MAX_NB_PORT]; +/** + * @brief Set the mode of a pin. + * + * If the pin was used for PWM or DAC output (analogWrite()), the output is + * stopped first. Any unsupported mode calls Error_Handler(). + * + * @param pinNumber Pin number, Dx or x. + * @param pinMode INPUT, INPUT_PULLUP, INPUT_PULLDOWN, INPUT_ANALOG, OUTPUT or OUTPUT_OPEN_DRAIN. + */ void pinMode(pin_size_t pinNumber, PinMode pinMode) { PinName p = digitalPinToPinName(pinNumber); @@ -79,6 +93,12 @@ void pinMode(pin_size_t pinNumber, PinMode pinMode) } } +/** + * @brief Write a HIGH or LOW value to a digital pin. + * + * @param pinNumber Pin number, Dx or x. + * @param status HIGH or LOW. + */ void digitalWrite(pin_size_t pinNumber, PinStatus status) { PinName p = digitalPinToPinName(pinNumber); @@ -87,6 +107,12 @@ void digitalWrite(pin_size_t pinNumber, PinStatus status) } } +/** + * @brief Read the value of a digital pin. + * + * @param pinNumber Pin number, Dx or x. + * @return HIGH or LOW. LOW if the pin is invalid. + */ PinStatus digitalRead(pin_size_t pinNumber) { PinName p = digitalPinToPinName(pinNumber); diff --git a/cores/arduino/wiring_pulse.cpp b/cores/arduino/wiring_pulse.cpp index fcaf974e35..f811c34ea0 100644 --- a/cores/arduino/wiring_pulse.cpp +++ b/cores/arduino/wiring_pulse.cpp @@ -16,15 +16,28 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file wiring_pulse.cpp + * @brief Pulse width measurement: pulseIn() and pulseInLong(). + */ + #include "Arduino.h" -/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH - * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds - * to 3 minutes in length, but must be called at least a few dozen microseconds - * before the start of the pulse. +/** + * @brief Measures the length (in microseconds) of a pulse on the pin. + * + * Works on pulses from 2-3 microseconds to 3 minutes in length, but must be + * called at least a few dozen microseconds before the start of the pulse. * - * ATTENTION: - * This function relies on micros() so cannot be used in noInterrupt() context + * @note This function relies on micros() so cannot be used in noInterrupt() context. + * + * @param pin Pin number, Dx or x. + * @param state HIGH or LOW, the type of pulse to measure. + * @param timeout Maximum time to wait for the whole measure (end of any + * previous pulse, start and end of the pulse), in microseconds. + * Defaults to 1 second when omitted in C++. + * @return Length of the pulse in microseconds, or 0 if the pulse was not + * completed before the timeout. */ unsigned long pulseIn(pin_size_t pin, uint8_t state, unsigned long timeout) { @@ -60,13 +73,20 @@ unsigned long pulseIn(pin_size_t pin, uint8_t state, unsigned long timeout) return (micros() - start); } -/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH - * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds - * to 3 minutes in length, but must be called at least a few dozen microseconds - * before the start of the pulse. +/** + * @brief Measures the length (in microseconds) of a pulse on the pin. + * + * Same as pulseIn(); provided for compatibility with the AVR core. + * + * @note This function relies on micros() so cannot be used in noInterrupt() context. * - * ATTENTION: - * This function relies on micros() so cannot be used in noInterrupt() context + * @param pin Pin number, Dx or x. + * @param state HIGH or LOW, the type of pulse to measure. + * @param timeout Maximum time to wait for the whole measure (end of any + * previous pulse, start and end of the pulse), in microseconds. + * Defaults to 1 second when omitted in C++. + * @return Length of the pulse in microseconds, or 0 if the pulse was not + * completed before the timeout. */ unsigned long pulseInLong(pin_size_t pin, uint8_t state, unsigned long timeout) { diff --git a/cores/arduino/wiring_shift.c b/cores/arduino/wiring_shift.c index 84ff859cce..13024f7de2 100644 --- a/cores/arduino/wiring_shift.c +++ b/cores/arduino/wiring_shift.c @@ -16,12 +16,27 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file wiring_shift.c + * @brief Bit-banged serial shift functions: shiftIn() and shiftOut(). + */ + #include "Arduino.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Shift in a byte of data one bit at a time. + * + * Each bit is read while the clock is HIGH, then the clock is set back to LOW. + * + * @param dataPin Pin number to read the data from. + * @param clockPin Pin number to clock the data on. + * @param bitOrder LSBFIRST or MSBFIRST. + * @return The byte read. + */ uint8_t shiftIn(pin_size_t dataPin, pin_size_t clockPin, BitOrder bitOrder) { uint8_t value = 0 ; @@ -42,6 +57,16 @@ uint8_t shiftIn(pin_size_t dataPin, pin_size_t clockPin, BitOrder bitOrder) return value ; } +/** + * @brief Shift out a byte of data one bit at a time. + * + * Each bit is written to the data pin, then the clock is pulsed HIGH then LOW. + * + * @param dataPin Pin number to write the data to. + * @param clockPin Pin number to clock the data on. + * @param bitOrder LSBFIRST or MSBFIRST. + * @param val Byte to shift out. + */ void shiftOut(pin_size_t dataPin, pin_size_t clockPin, BitOrder bitOrder, uint8_t val) { uint8_t i ; diff --git a/cores/arduino/wiring_time.c b/cores/arduino/wiring_time.c index f622c7e1d7..90ce77376b 100644 --- a/cores/arduino/wiring_time.c +++ b/cores/arduino/wiring_time.c @@ -16,24 +16,49 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file wiring_time.c + * @brief Time functions: millis(), micros(), delay(), delayMicroseconds() and _gettimeofday(). + */ + #include "Arduino.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Get the number of milliseconds since the board started. + * + * @note Interrupts are not disabled while the counter is read, see the ToDo in the implementation. + * + * @return Milliseconds since startup. Overflows after about 49 days. + */ unsigned long millis(void) { - // ToDo: ensure no interrupts + // ToDo: ensure no interrupts while reading the counter return getCurrentMillis(); } -// Interrupt-compatible version of micros +/** + * @brief Get the number of microseconds since the board started. + * + * Interrupt-compatible version. + * + * @return Microseconds since startup. Overflows after about 70 minutes. + */ unsigned long micros(void) { return getCurrentMicros(); } +/** + * @brief Pause the program for the given number of milliseconds. + * + * Calls yield() while waiting, so cooperative threads can run. + * + * @param ms Number of milliseconds to pause. + */ void delay(unsigned long ms) { if (ms != 0) { @@ -45,19 +70,20 @@ void delay(unsigned long ms) } /** - * \brief Pauses the program for the amount of time (in microseconds) specified as parameter. + * @brief Pause the program for the amount of time (in microseconds) specified as parameter. * - * \param us the number of microseconds to pause (unsigned int) + * @param us Number of microseconds to pause. */ - void delayMicroseconds(unsigned int us) { #if defined(DWT_BASE) && !defined(DWT_DELAY_DISABLED) + // Busy wait on the DWT cycle counter int32_t start = dwt_getCycles(); int32_t cycles = us * (SystemCoreClock / 1000000); while ((int32_t)dwt_getCycles() - start < cycles); #else + // Busy wait on the SysTick down counter, accumulating the elapsed ticks __IO uint32_t currentTicks = SysTick->VAL; /* Number of ticks per millisecond */ const uint32_t tickPerMs = SysTick->LOAD + 1; @@ -76,17 +102,16 @@ void delayMicroseconds(unsigned int us) } /** - * \brief gives the number of seconds and microseconds since the Epoch - * - * based on millisecond since last power on. + * @brief Gives the number of seconds and microseconds since the Epoch. * - * \note The function is declared as weak to be overwritten in case of other - * implementations in user file (using RTC values for example). + * Based on milliseconds since last power on. * - * \param tv argument is a struct timeval - * \param tz argument is a struct timezone (unused) + * @note The function is declared as weak to be overwritten in case of other + * implementations in user file (using RTC values for example). * - * \return 0 + * @param tv Argument is a struct timeval. + * @param tz Argument is a struct timezone (unused). + * @return 0, always. */ int __attribute__((weak)) _gettimeofday(struct timeval *tv, void *tz) { diff --git a/docs/API.md b/docs/API.md index 0be3e91b35..359a8ed20e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -5,6 +5,9 @@ This page documents STM32-specific additions and extensions. For the standard Arduino API, see the [Arduino language reference](https://docs.arduino.cc/language-reference/) and the [ArduinoCore-API documentation](https://github.com/arduino/ArduinoCore-API). +> [!TIP] +> The source-level reference of the core, generated with Doxygen from the code comments (classes, structures, functions and their parameters), is available in the [Doxygen API reference](doxygen/index.html). It follows the documentation version selected in this site: use the version selector to switch between `dev` and the released versions. + ## Core This part describes the STM32 core functions. diff --git a/docs/development/Documentation-workflow.md b/docs/development/Documentation-workflow.md index 439355a37c..f3063a29dc 100644 --- a/docs/development/Documentation-workflow.md +++ b/docs/development/Documentation-workflow.md @@ -8,7 +8,8 @@ The STM32 core documentation is maintained in the `docs/` directory of the `stm3 2. Add new pages to the `nav` section in `mkdocs.yml`. The navigation is explicit, so a file that is not listed there will not appear in the sidebar. 3. Use relative links to other Markdown files and verify that image paths resolve from the page containing them. 4. The [Supported boards](../supported-boards/index.md) page is generated from `boards.txt`: do not edit it. After changing `boards.txt`, run `python docs/hooks/supported_boards.py` (or any MkDocs build) and commit the updated `docs/supported-boards/index.md`; the documentation check fails if it is out of date. Optional `# Docs: field=value` comments placed directly above a board option (`.menu.pnum.