diff --git a/.github/workflows/deploy_with_cache.yml b/.github/workflows/deploy_with_cache.yml index df2a273..a484f7a 100644 --- a/.github/workflows/deploy_with_cache.yml +++ b/.github/workflows/deploy_with_cache.yml @@ -60,8 +60,8 @@ jobs: conan config install https://github.com/libhal/conan-config2.git conan hal setup - - name: πŸ”¨ Build for cortex-m3 to cache compiler - run: conan create . -s:h build_type=Release -s:h os=baremetal -s:h arch=cortex-m3 --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing + - name: πŸ”¨ Build for cortex-m33f to cache compiler + run: conan create . -s:h build_type=Release -s:h os=baremetal -s:h arch=cortex-m33f --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing - name: πŸ“¦ Create tarball of conan packages run: tar -czf conan-packages.tar.gz -C ~/.conan2 p @@ -74,7 +74,7 @@ jobs: retention-days: 1 # Build jobs for each architecture - build_cortex_m3: + build_cortex_m33f: needs: setup_compiler_cache runs-on: ubuntu-24.04 env: @@ -109,124 +109,21 @@ jobs: - name: πŸ“¦ Extract conan packages tarball run: tar -xzf conan-packages.tar.gz -C ~/.conan2 - - name: πŸ“¦ Create `Debug` package for cortex-m3 - run: conan create . -s:h build_type=Debug -s:h os=baremetal -s:h arch=cortex-m3 --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing + - name: πŸ“¦ Create `Debug` package for cortex-m33f + run: conan create . -s:h build_type=Debug -s:h os=baremetal -s:h arch=cortex-m33f \ + --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing \ + -o:h flash_clkdiv=2 -o:h rp_revision=a2 -o:h flash_size="(16 * 1024 * 1024)" -o:h use_w25q_flash=True - - name: πŸ“¦ Create `MinSizeRel` package for cortex-m3 - run: conan create . -s:h build_type=MinSizeRel -s:h os=baremetal -s:h arch=cortex-m3 --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing + - name: πŸ“¦ Create `MinSizeRel` package for cortex-m33f + run: conan create . -s:h build_type=MinSizeRel -s:h os=baremetal -s:h arch=cortex-m33f \ + --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing \ + -o:h flash_clkdiv=2 -o:h rp_revision=a2 -o:h flash_size="(16 * 1024 * 1024)" -o:h use_w25q_flash=True - - name: πŸ“¦ Create `Release` package for cortex-m3 - run: conan create . -s:h build_type=Release -s:h os=baremetal -s:h arch=cortex-m3 --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing - - name: πŸ“‘ Sign into JFrog Artifactory - if: ${{ inputs.version != 'latest' }} - env: - PASSWORD: ${{ secrets.JFROG_LIBHAL_TRUNK_ID_TOKEN }} - JFROG_USER: ${{ secrets.JFROG_LIBHAL_TRUNK_ID_TOKEN_USER }} - run: conan remote login -p $PASSWORD libhal $JFROG_USER - - - name: πŸ†™ Upload package version ${{ inputs.version }} to conan repo - if: ${{ inputs.version != 'latest' }} - run: conan upload "libhal-arm-mcu/${{ inputs.version }}" --confirm -r=libhal - - build_cortex_m4: - needs: setup_compiler_cache - runs-on: ubuntu-24.04 - env: - VERBOSE: 1 - steps: - - uses: actions/checkout@v4.1.1 - if: ${{ inputs.version != 'latest' }} - with: - submodules: true - repository: ${{ inputs.repo }} - ref: ${{ inputs.version }} - - - uses: actions/checkout@v4.1.1 - if: ${{ inputs.version == 'latest' }} - with: - submodules: true - repository: ${{ inputs.repo }} - - - name: πŸ” setup libhal - run: | - pipx install conan>=${{ inputs.conan_version }} - conan --version - conan config install https://github.com/libhal/conan-config2.git - conan hal setup - - - name: πŸ“₯ Download conan package cache - uses: actions/download-artifact@v4 - with: - name: conan-packages-${{ inputs.upload_suffix }} - path: . - - - name: πŸ“¦ Extract conan packages tarball - run: tar -xzf conan-packages.tar.gz -C ~/.conan2 - - - name: πŸ“¦ Create `Debug` package for cortex-m4 - run: conan create . -s:h build_type=Debug -s:h os=baremetal -s:h arch=cortex-m4 --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing - - - name: πŸ“¦ Create `MinSizeRel` package for cortex-m4 - run: conan create . -s:h build_type=MinSizeRel -s:h os=baremetal -s:h arch=cortex-m4 --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing - - - name: πŸ“¦ Create `Release` package for cortex-m4 - run: conan create . -s:h build_type=Release -s:h os=baremetal -s:h arch=cortex-m4 --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing - - - name: πŸ“‘ Sign into JFrog Artifactory - if: ${{ inputs.version != 'latest' }} - env: - PASSWORD: ${{ secrets.JFROG_LIBHAL_TRUNK_ID_TOKEN }} - JFROG_USER: ${{ secrets.JFROG_LIBHAL_TRUNK_ID_TOKEN_USER }} - run: conan remote login -p $PASSWORD libhal $JFROG_USER - - - name: πŸ†™ Upload package version ${{ inputs.version }} to conan repo - if: ${{ inputs.version != 'latest' }} - run: conan upload "libhal-arm-mcu/${{ inputs.version }}" --confirm -r=libhal - - build_cortex_m4f: - needs: setup_compiler_cache - runs-on: ubuntu-24.04 - env: - VERBOSE: 1 - steps: - - uses: actions/checkout@v4.1.1 - if: ${{ inputs.version != 'latest' }} - with: - submodules: true - repository: ${{ inputs.repo }} - ref: ${{ inputs.version }} - - - uses: actions/checkout@v4.1.1 - if: ${{ inputs.version == 'latest' }} - with: - submodules: true - repository: ${{ inputs.repo }} - - - name: πŸ” setup libhal - run: | - pipx install conan>=${{ inputs.conan_version }} - conan --version - conan config install https://github.com/libhal/conan-config2.git - conan hal setup - - - name: πŸ“₯ Download conan package cache - uses: actions/download-artifact@v4 - with: - name: conan-packages-${{ inputs.upload_suffix }} - path: . - - - name: πŸ“¦ Extract conan packages tarball - run: tar -xzf conan-packages.tar.gz -C ~/.conan2 - - - name: πŸ“¦ Create `Debug` package for cortex-m4f - run: conan create . -s:h build_type=Debug -s:h os=baremetal -s:h arch=cortex-m4f --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing - - - name: πŸ“¦ Create `MinSizeRel` package for cortex-m4f - run: conan create . -s:h build_type=MinSizeRel -s:h os=baremetal -s:h arch=cortex-m4f --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing - - - name: πŸ“¦ Create `Release` package for cortex-m4f - run: conan create . -s:h build_type=Release -s:h os=baremetal -s:h arch=cortex-m4f --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing + - name: πŸ“¦ Create `Release` package for cortex-m33f + run: conan create . -s:h build_type=Release -s:h os=baremetal -s:h arch=cortex-m33f \ + --version=${{ inputs.version }} -pr:h ${{ inputs.compiler_profile }} --build=missing \ + -o:h flash_clkdiv=2 -o:h rp_revision=a2 -o:h flash_size="(16 * 1024 * 1024)" -o:h use_w25q_flash=True - name: πŸ“‘ Sign into JFrog Artifactory if: ${{ inputs.version != 'latest' }} @@ -237,4 +134,4 @@ jobs: - name: πŸ†™ Upload package version ${{ inputs.version }} to conan repo if: ${{ inputs.version != 'latest' }} - run: conan upload "libhal-arm-mcu/${{ inputs.version }}" --confirm -r=libhal + run: conan upload "libhal-picosdk/${{ inputs.version }}" --confirm -r=libhal diff --git a/CMakeLists.txt b/CMakeLists.txt index 86c4c63..0c73d3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,159 +14,27 @@ cmake_minimum_required(VERSION 3.15) -if(DEFINED ENV{PICO_SDK_PATH}) set(PICO_CXX_ENABLE_EXCEPTIONS 1) set(PICO_NO_PICOTOOL 1) include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake) -endif() set(CMAKE_COLOR_DIAGNOSTICS ON) -project(libhal-arm-mcu LANGUAGES CXX C ASM) - -# Should probably refine this to actually not compile -# drivers for all platforms all the time, but for now this -# serves my purposes -if(NOT DEFINED ENV{PICO_SDK_PATH}) -set(source_list - src/system_controller.cpp - src/dwt_counter.cpp - src/interrupt.cpp - src/systick_timer.cpp - - # lpc40 - src/lpc40/adc.cpp - src/lpc40/can.cpp - src/lpc40/clock.cpp - src/lpc40/dac.cpp - src/lpc40/dma.cpp - src/lpc40/i2c.cpp - src/lpc40/input_pin.cpp - src/lpc40/interrupt_pin.cpp - src/lpc40/interrupt.cpp - src/lpc40/output_pin.cpp - src/lpc40/pin.cpp - src/lpc40/power.cpp - src/lpc40/pwm.cpp - src/lpc40/spi.cpp - src/lpc40/dma_spi.cpp - src/lpc40/stream_dac.cpp - src/lpc40/uart.cpp - - # stm32_generic - src/stm32_generic/i2c.cpp - src/stm32_generic/pwm.cpp - src/stm32_generic/quadrature_encoder.cpp - src/stm32_generic/spi.cpp - src/stm32_generic/timer.cpp - src/stm32_generic/uart.cpp - - # stm32f1 - src/stm32f1/can.cpp - src/stm32f1/can2.cpp - src/stm32f1/clock.cpp - src/stm32f1/gpio.cpp - src/stm32f1/independent_watchdog.cpp - src/stm32f1/input_pin.cpp - src/stm32f1/interrupt.cpp - src/stm32f1/output_pin.cpp - src/stm32f1/pin.cpp - src/stm32f1/power.cpp - src/stm32f1/pwm.cpp - src/stm32f1/quadrature_encoder.cpp - src/stm32f1/timer.cpp - src/stm32f1/uart.cpp - src/stm32f1/spi.cpp - src/stm32f1/adc.cpp - src/stm32f1/usart.cpp - src/stm32f1/usb.cpp - - # stm32f411 - src/stm32f411/clock.cpp - src/stm32f411/dma.cpp - src/stm32f411/i2c.cpp - src/stm32f411/input_pin.cpp - src/stm32f411/interrupt.cpp - src/stm32f411/output_pin.cpp - src/stm32f411/pin.cpp - src/stm32f411/power.cpp - src/stm32f411/spi.cpp - src/stm32f411/uart.cpp - - # stm32f40 - src/stm32f40/output_pin.cpp -) - -set( - test_sources - # cortex_m - tests/dwt_counter.test.cpp - tests/interrupt.test.cpp - tests/main.test.cpp - tests/systick_timer.test.cpp - - # lpc40 - tests/lpc40/adc.test.cpp - tests/lpc40/can.test.cpp - tests/lpc40/i2c.test.cpp - tests/lpc40/input_pin.test.cpp - tests/lpc40/interrupt_pin.test.cpp - tests/lpc40/output_pin.test.cpp - tests/lpc40/pwm.test.cpp - tests/lpc40/spi.test.cpp - tests/lpc40/stream_dac.test.cpp - tests/lpc40/uart.test.cpp - - # stm32f1 - tests/stm32f1/can.test.cpp - tests/stm32f1/can2.test.cpp - tests/stm32f1/output_pin.test.cpp - tests/stm32f1/uart.test.cpp - tests/stm32f1/spi.test.cpp - tests/stm32f1/adc.test.cpp - tests/stm32f1/usb.test.cpp - - # stm32f411 - tests/stm32f411/output_pin.test.cpp - tests/stm32f411/spi.test.cpp -) - -else() -set(source_list - src/rp/gpio.cpp - src/rp/serial.cpp - src/rp/i2c.cpp - src/rp/pwm.cpp - src/rp/adc.cpp - src/rp/spi.cpp - src/rp/time.cpp - src/system_controller.cpp - src/dwt_counter.cpp - src/interrupt.cpp -) +project(libhal-picosdk LANGUAGES CXX C ASM) pico_sdk_init() -set(pico_dep - hardware_gpio_headers - pico_time_headers - pico_stdio_headers - hardware_sync_headers - hardware_i2c_headers - hardware_pwm_headers - hardware_adc_headers - hardware_spi_headers - hardware_uart_headers - hardware_timer_headers - hardware_dma_headers -) - -endif() - libhal_test_and_make_library( - LIBRARY_NAME libhal-arm-mcu + LIBRARY_NAME libhal-picosdk SOURCES - ${source_list} + src/gpio.cpp + src/serial.cpp + src/i2c.cpp + src/pwm.cpp + src/adc.cpp + src/spi.cpp + src/time.cpp + src/dwt_counter.cpp src/terminate_handler.cpp TEST_SOURCES @@ -183,6 +51,16 @@ libhal_test_and_make_library( libhal::util nonstd::ring-span-lite nonstd::scope-lite - ${pico_dep} + hardware_gpio_headers + pico_time_headers + pico_stdio_headers + hardware_sync_headers + hardware_i2c_headers + hardware_pwm_headers + hardware_adc_headers + hardware_spi_headers + hardware_uart_headers + hardware_timer_headers + hardware_dma_headers ) diff --git a/README.md b/README.md index 59ac700..a840d47 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# libhal-arm-mcu +# libhal-picosdk [![βœ… Demos Build](https://github.com/libhal/libhal-arm-mcu/actions/workflows/demos_test.yml/badge.svg)](https://github.com/libhal/libhal-arm-mcu/actions/workflows/demos_test.yml) [![βœ… Library Builds](https://github.com/libhal/libhal-arm-mcu/actions/workflows/library_test.yml/badge.svg)](https://github.com/libhal/libhal-arm-mcu/actions/workflows/library_test.yml) @@ -11,11 +11,6 @@ processor microcontrollers (MCUs). This is a platform library supporting generic ARM processor APIs and peripheral drivers from many different microcontrollers. -> [!NOTE] -> CI is failing due to a memory leak detected via ASAN when executing the unit tests on Linux. -> Everything passes without an error or leak detected on MacOS builds. -> This is the current cause of the CI to failure. Otherwise the rest of CI is passing. - ## πŸ“š Software APIs & Usage To learn about the available drivers and APIs see the headers @@ -77,60 +72,23 @@ Build type `Debug`, `MinSizeRel`, and `Release` are all available. ## πŸ’Ύ Flashing/Programming -There are a few ways to flash an LPC40 series MCU. The recommended methods are -via USB or using a debugger JTAG/SWD. - -### Flashing NXP MCUs +It is possible to flash RP-series chips via the file manager GUI, or from the +command line using picosdk's tools. -[`nxpprog`](https://github.com/libhal/nxpprog) is a script for programming and -flashing LPC40 series chips over serial/UART. Using it will require a USB to -serial/uart adaptor. +### Programming via Mass Storage Interface -See the README on [`nxpprog`](https://github.com/libhal/nxpprog), for details on -how to use NXPPROG. - -To install `nxpprog`: - -```bash -pipx install nxpprog -``` - -To flash command is: - -```bash -nxpprog --control --binary demos/build/lpc4078/MinSizeRel/blinker.elf.bin --device /dev/tty.usbserial-10 -``` +When the Pico microcontrollers enter boot mode, they appear as a mass storage device +on USB. By copying a uf2 firmware file into the directory of the mass storage interface, +it is possible to flash the microcontroller with no external tools. -- Replace `demos/build/lpc4078/MinSizeRel/blinker.elf.bin` with the path to the - binary you'd like to flash. -- Replace `/dev/tty.usbserial-10` with the path to your serial port on your - machine. +### Programming using picotool -### Flashing STM32 Processors - -[`stm32loader`](https://pypi.org/project/stm32loader/) is a script for -programming and flashing STM32 series chips over serial/UART. Using it will -require a USB to serial/uart adaptor. - -For more information, please refer to the README of -[`stm32loader`](https://pypi.org/project/stm32loader/). - -To install stm32loader: - -```bash -pipx install stm32loader -``` - -To flash command is: - -```bash -stm32loader -p /dev/tty.usbserial-10 -e -w -v demos/build/stm32f103c8/MinSizeRel/blinker.elf.bin -``` +By adding `picotool` as a dependency, it is possible to flash the RP chips. First, add it +as a `tool_requires` dependency in your project. Then, convert the `.elf` firmware to `.uf2` +via the `pico_add_extra_oututs()` CMake function. +Then run `source build/rp2350-arm-s/BUILDTYPEHERE/generators/conanbuild.sh`. This will add +picotool to your path. Then run `picotool load FIRMWAREFILE.uf2` to upload. -- Replace `demos/build/stm32f103c8/MinSizeRel/blinker.elf.bin` with the path to - the binary you'd like to flash. -- Replace `/dev/tty.usbserial-10` with the path to your serial port on your - machine. ### Using JTAG/SWD over PyOCD @@ -138,8 +96,8 @@ stm32loader -p /dev/tty.usbserial-10 -e -w -v demos/build/stm32f103c8/MinSizeRel processor devices over JTAG and SWD. This will require a JTAG or SWD debugger. The recommended debugger for the -LPC40 series of devices is the STLink v2 (cheap variants can be found on -Amazon). +RP-series chips is the Raspberry Pi Debug Adapter, although any debugger will +suffice. See [PyOCD Installation Page](https://pyocd.io/docs/installing) for installation details. @@ -147,8 +105,7 @@ details. For reference the flashing command is: ```bash -pyocd flash --target lpc4088 demos/build/lpc4078/MinSizeRel/blinker.elf.bin -pyocd flash --target stm32f103rc demos/build/stm32f103c8/MinSizeRel/blinker.elf.bin +pyocd flash --target rp2350 demos/build/lpc4078/MinSizeRel/blinker.elf.bin ``` Note that the targets for your exact part may not exist in `pyocd`. Because of @@ -186,7 +143,7 @@ platform library. No change needed. To perform a test build simple run `conan build .` as is done above with the desired target platform profile. -## ❌ Using `libhal-arm-mcu` in your library +## ❌ Using `libhal-picosdk` in your library This library is a platform library and as such should only be depended upon by applications. Platform libraries do not require ABI stability and thus do not @@ -247,77 +204,6 @@ The patch number will increment if: For now, you cannot expect ABI or API stability with anything in the `/include/libhal-arm-mcu/experimental` directory. -## 🏁 Startup & Initialization - -Startup is managed by the [`picolibc`](https://keithp.com/picolibc/) runtime. -In terms of startup `picolibc` has to manage doing two things. For one, it must -construct a minimal interrupt vector table with two entries. The 1st entry is -the address of the top of the stack. The 2nd entry is the address of the -function that will be executed on reset. `picolibc` sets this to its own -`_start` function. `_start` does the following: - -1. Sets the main stack registers -2. Write the `.data` section from read-only memory -3. Set the `.bss` section to all zeros -4. Enable FPU if present for the core architecture -5. Calls all globally constructed C++ objects -6. Calls `main()` - -If the `.data` or `.bss` sections must initialized manually, there are functions -provided: - -```C++ -#include - -hal::cortex_m::initialize_data_section(); -hal::cortex_m::initialize_bss_section(); -hal::cortex_m::initialize_floating_point_unit(); -``` - -### 🏎️ Setting Clock Speed - -To setting the CPU clock speed to the maximum of 120MHz, include the line below, -with the rest of the includes: - -```C++ -#include -#include -#include -// etc.. -#include -``` - -Next run the following command but replace `12.0_MHz` with the crystal -oscillator frequency connected to the microcontroller. This command REQUIRES -that there be a crystal oscillator attached to the microcontroller. Calling -this without the oscillator will cause the device to freeze as it will attempt -to use a clock that does not exist. - -```C++ -hal::lpc40::maximum(12.0_MHz); -hal::stm32f1::maximum(8.0_MHz); -hal::stm32f4::maximum(10.0_MHz); -// etc... -hal::rp2040::maximum(16.0_MHz); -``` - -To set the clock rate to the max speed using the internal oscillator: - -```C++ -hal::lpc40::maximum_speed_using_internal_oscillator(); -hal::stm32f1::maximum_speed_using_internal_oscillator(); -hal::stm32f4::maximum_speed_using_internal_oscillator(); -// etc... -hal::rp2040::maximum_speed_using_internal_oscillator(); -``` - -These APIs may not always exist for all systems, so be sure to check if the API -exists. - -#### πŸ•°οΈ Detailed Clock Tree Control - -Coming soon... - ## πŸ”Ž On Chip Software Debugging ### Using PyOCD (βœ… RECOMMENDED) @@ -325,7 +211,7 @@ Coming soon... In one terminal: ```bash -pyocd gdbserver --semihost -Osemihost_console_type=True --persist --target=lpc4088 +pyocd gdbserver --semihost -Osemihost_console_type=True --persist --target=rp2350 ``` In another terminal: @@ -393,11 +279,3 @@ See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details. Apache 2.0; see [`LICENSE`](LICENSE) for details. -## Source of initial files put into this library - -The original files came from the soon to be archived repos: - -- [`libhal/libhal-lpc40`](https://github.com/libhal/libhal-lpc40) -- [`libhal/libhal-stm32f1`](https://github.com/libhal/libhal-stm32f1) -- [`libhal/libhal-stm32f4`](https://github.com/libhal/libhal-stm32f4) -- [`libhal/libhal-armcortex`](https://github.com/libhal/libhal-armcortex) diff --git a/conanfile.py b/conanfile.py index 44d3097..d43a3d7 100644 --- a/conanfile.py +++ b/conanfile.py @@ -24,38 +24,19 @@ required_conan_version = ">=2.0.14" -class libhal_arm_mcu_conan(ConanFile): - name = "libhal-arm-mcu" +class libhal_picosdk_conan(ConanFile): + name = "libhal-picosdk" license = "Apache-2.0" - homepage = "https://github.com/libhal/libhal-arm-mcu" + homepage = "https://github.com/libhal/libhal-picosdk" description = ( - "A collection of libhal drivers and libraries for the " - "Cortex M series ARM processors and microcontrollers." + "Drivers that adapt Raspberry Pi Pico C/C++ SDK to libhal." ) topics = ( "arm", "cortex", "cortex-m", "cortex-m0", - "cortex-m0plus", - "cortex-m1", - "cortex-m3", - "cortex-m4", - "cortex-m4f", - "cortex-m7", - "cortex-m23", - "cortex-m55", - "cortex-m35p", "cortex-m33", - "lpc", - "lpc40", - "lpc40xx", - "lpc4072", - "lpc4074", - "lpc4078", - "lpc4088", - "stm32f1", - "stm32f103", "rp2040", "rp2350", ) @@ -68,28 +49,23 @@ class libhal_arm_mcu_conan(ConanFile): "platform": ["ANY"], "use_libhal_exceptions": [True, False], "use_picolibc": [True, False], - "use_default_linker_script": [True, False], "variant": [None, "ANY"], "board": [None, "ANY"], "replace_std_terminate": [True, False], "use_semihosting": [True, False], - "flash_size": [None, "ANY"], - "flash_clkdiv": [None, "ANY"], - "rp_revision": [None, "ANY"], + "flash_size": ["ANY"], + "flash_clkdiv": ["ANY"], + "rp_revision": ["ANY"], "use_w25q_flash": [True, False], } default_options = { "platform": "ANY", - "use_libhal_exceptions": True, + "board": None, + "use_libhal_exceptions": False, "use_picolibc": True, - "use_default_linker_script": True, "replace_std_terminate": True, "use_semihosting": True, "variant": None, - "board": None, - "flash_size": None, - "flash_clkdiv": None, - "rp_revision": None, "use_w25q_flash": False, } @@ -97,7 +73,6 @@ class libhal_arm_mcu_conan(ConanFile): "platform": "Specifies which platform to provide binaries and build information for", "use_libhal_exceptions": "Reserved for backwards compatibility. This option is currently unused and will become functional when libhal-exceptions is feature complete.", "use_picolibc": "Use picolibc as the libc runtime for ARM GCC. Note: ARM's LLVM fork always uses picolibc and ignores this option.", - "use_default_linker_script": "Enable automatic linker script selection based on the specified platform", "replace_std_terminate": "Replace the default std::terminate handler to reduce binary size by avoiding verbose text rendering", "use_semihosting": "Enables semihosting support, allowing the MCU to perform host based I/O like writing to stdout or reading from files via the debug port. With LLVM from arm-toolchain, semihosting is enabled via the compiler and must be disabled via a build profile option and not this option.", } @@ -126,24 +101,9 @@ def requirements(self): "prebuilt-picolibc/" + CV, options={"crt0": CRT0, "oslib": OSLIB} ) - if str(self.options.platform).startswith("rp2"): - self.requires("picosdk/2.2.1-alpha") - self.tool_requires("pioasm/2.2.0") - - def handle_stm32f1_linker_scripts(self): - linker_script_name = list(str(self.options.platform)) - # Replace the MCU number and pin count number with 'x' (don't care) - # to map to the linker script - linker_script_name[8] = "x" - linker_script_name[9] = "x" - linker_script_name = "".join(linker_script_name) + self.requires("picosdk/2.2.1-alpha") + self.tool_requires("pioasm/2.2.0") - self.cpp_info.exelinkflags.extend( - [ - "-L" + str(Path(self.package_folder) / "linker_scripts"), - "-T" + str(Path("libhal-stm32f1") / linker_script_name + ".ld"), - ] - ) def _macro(self, string): return string.upper().replace("-", "_") @@ -152,20 +112,18 @@ def generate(self): virt = VirtualBuildEnv(self) virt.generate() tc = CMakeToolchain(self) - if str(self.options.platform).startswith("rp2"): - tc.cache_variables["DO_NOT_BUILD_BOOT_HAL"] = True - tc.preprocessor_definitions["PICO_STDIO_SHORT_CIRCUIT_CLIB_FUNCS"] = "0" - if self.options.board: - tc.cache_variables["PICO_BOARD"] = str(self.options.board) - if ( - self.options.flash_size - or self.options.flash_clkdiv - or self.options.rp_revision - ): - tc.cache_variables["PICO_BOARD_HEADER_DIRS"] = str( - self.build_folder - ) - self.generate_rp_header() + tc.cache_variables["DO_NOT_BUILD_BOOT_HAL"] = True + tc.preprocessor_definitions["PICO_STDIO_SHORT_CIRCUIT_CLIB_FUNCS"] = "0" + tc.cache_variables["PICO_BOARD"] = self.getboard() + if ( + self.options.flash_size + or self.options.flash_clkdiv + or self.options.rp_revision + ): + tc.cache_variables["PICO_BOARD_HEADER_DIRS"] = str( + self.build_folder + ) + self.generate_rp_header() if self.options.variant: tc.preprocessor_definitions[ "LIBHAL_VARIANT_" + self._macro(str(self.options.variant)) @@ -177,36 +135,30 @@ def generate(self): cmake = CMakeDeps(self) cmake.generate() + def getboard(self): + if not self.options.board: + return "libhal_picosdk" + return self.options.board.value + def validate(self): - if str(self.options.platform).startswith("rp2"): - if self.options.use_default_linker_script: + if ( + self.options.flash_size + or self.options.flash_clkdiv + or self.options.rp_revision + ): + if not self.options.flash_size: + raise ConanInvalidConfiguration("Flash size must be set") + if not str(self.options.flash_clkdiv).isnumeric(): raise ConanInvalidConfiguration( - "Default linker scripts are not compatible with RP chips, use pico-sdk linker scripts instead" + "Flash clock divider is invalid value" ) - if not self.options.board: - raise ConanInvalidConfiguration("RP board not specified") - if "rp2350" in str(self.options.platform): - if not self.options.variant: - raise ConanInvalidConfiguration("RP2350 variant not specified") - if self.options.variant not in ["rp2350a", "rp2350b"]: - raise ConanInvalidConfiguration("Invalid RP2350 variant specified") - if not self.options.board: - raise ConanInvalidConfiguration( - "Board must be specified during build" - ) - if ( - self.options.flash_size - or self.options.flash_clkdiv - or self.options.rp_revision - ): - if not self.options.flash_size: - raise ConanInvalidConfiguration("Flash size must be set") - if not str(self.options.flash_clkdiv).isnumeric(): - raise ConanInvalidConfiguration( - "Flash clock divider is invalid value" - ) - if self.options.rp_revision.value not in ["a1", "a2"]: - raise ConanInvalidConfiguration("RP revision is invalid") + if self.options.rp_revision.value not in ["a1", "a2"]: + raise ConanInvalidConfiguration("RP revision is invalid") + if "rp2350" in str(self.options.platform): + if not self.options.variant: + raise ConanInvalidConfiguration("RP2350 variant not specified") + if self.options.variant not in ["rp2350a", "rp2350b"]: + raise ConanInvalidConfiguration("Invalid RP2350 variant specified") super().validate() def package(self): @@ -217,28 +169,29 @@ def package(self): ): copy( self, - f"{self.options.board.value}.h", + f"{self.getboard()}.h", dst=Path(self.package_folder).joinpath("include", "picosdk-board-defs"), src=self.build_folder, ) super().package() def package_info(self): - self.cpp_info.libs = ["libhal-arm-mcu"] - self.cpp_info.set_property("cmake_target_name", "libhal::arm-mcu") + self.cpp_info.libs = ["libhal-picosdk"] + self.cpp_info.set_property("cmake_target_name", "libhal::picosdk") self.cpp_info.set_property( "cmake_target_aliases", - ["libhal::lpc40", "libhal::stm32f1", "libhal::stm32f4", "libhal::rp2350"], + ["libhal::rp2350"], ) PLATFORM = str(self.options.platform) self.buildenv_info.define("LIBHAL_PLATFORM", PLATFORM) - self.buildenv_info.define("LIBHAL_PLATFORM_LIBRARY", "arm-mcu") + self.buildenv_info.define("LIBHAL_PLATFORM_LIBRARY", "picosdk") if str(self.options.platform).startswith("rp2"): - self.buildenv_info.define( - "PICO_BOARD_HEADER_DIRS", - str(Path(self.package_folder, "include", "picosdk-board-defs")), - ) + if self.options.flash_size: + self.buildenv_info.define( + "PICO_BOARD_HEADER_DIRS", + str(Path(self.package_folder, "include", "picosdk-board-defs")), + ) defines = [] if self.options.variant: defines.append( @@ -301,29 +254,8 @@ def setup_baremetal(self, platform: str): ] ) - if self.options.use_default_linker_script: - LINKER_SCRIPTS_PATH = Path(self.package_folder) / "linker_scripts" - # If the platform matches the linker script, just use that linker - # script - self.cpp_info.exelinkflags.append("-L" + str(LINKER_SCRIPTS_PATH)) - - FULL_LINKER_PATH: Path = LINKER_SCRIPTS_PATH / (platform + ".ld") - # if the file exists, then we should use it as the linker - if FULL_LINKER_PATH.exists(): - self.output.info(f"linker file '{FULL_LINKER_PATH}' found!") - self.cpp_info.exelinkflags.append("-T" + platform + ".ld") - else: - # if there is no match, then the linker script could be a - # pattern based on the name of the platform - self.append_linker_using_platform(platform) - - if self.settings.compiler == "gcc": - self.cpp_info.exelinkflags.append("-Tpicolibc_gcc.ld") - if self.settings.compiler == "clang": - self.cpp_info.exelinkflags.append("-Tpicolibc_llvm.ld") - package_folder = Path(self.package_folder) - LIB_PATH = package_folder / "lib" / "liblibhal-arm-mcu.a" + LIB_PATH = package_folder / "lib" / "liblibhal-picosdk.a" self.cpp_info.exelinkflags.extend( [ # Ensure that all symbols are added to the linker's symbol table @@ -337,21 +269,9 @@ def setup_baremetal(self, platform: str): ] ) - def append_linker_using_platform(self, platform: str): - if platform.startswith("stm32f1"): - linker_script_name = list(str(self.options.platform)) - # Replace the MCU number and pin count number with 'x' (don't care) - # to map to the linker script - linker_script_name[8] = "x" - linker_script_name[9] = "x" - linker_script_name = "".join(linker_script_name) - self.cpp_info.exelinkflags.append("-T" + linker_script_name + ".ld") - return - # Add additional script searching queries here - def generate_rp_header(self): platform = str(self.options.platform.value) - pico_board = self.options.board.value + pico_board = self.getboard() a2 = "1" if self.options.rp_revision.value == "a2" else "0" if platform.startswith("rp235"): if self.options.variant == "rp2350a": diff --git a/src/rp/adc.cpp b/src/adc.cpp similarity index 100% rename from src/rp/adc.cpp rename to src/adc.cpp diff --git a/src/rp/gpio.cpp b/src/gpio.cpp similarity index 100% rename from src/rp/gpio.cpp rename to src/gpio.cpp diff --git a/src/rp/i2c.cpp b/src/i2c.cpp similarity index 100% rename from src/rp/i2c.cpp rename to src/i2c.cpp diff --git a/src/interrupt.cpp b/src/interrupt.cpp deleted file mode 100644 index 53864a7..0000000 --- a/src/interrupt.cpp +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include - -#include -#include - -#include "interrupt_reg.hpp" - -namespace hal::cortex_m { -namespace { -/// Pointer to a statically allocated interrupt vector table -std::span vector_table{}; - -std::int32_t register_index(irq_t p_irq) -{ - constexpr irq_t register_width = 32; - return p_irq / register_width; -} - -void nvic_enable_irq(irq_t p_irq) -{ - auto* interrupt_enable = &nvic->iser[register_index(p_irq)]; - *interrupt_enable = 1 << p_irq; -} - -void nvic_disable_irq(irq_t p_irq) -{ - auto* interrupt_clear = &nvic->icer[register_index(p_irq)]; - *interrupt_clear = 1 << p_irq; -} - -void setup_default_vector_table(std::span p_vector_table) -{ - // Move vector span table forward so the negative irq numbers reach the - // correct array elements. - p_vector_table = p_vector_table.subspan(-core_interrupts); - - auto* table_reg = get_interrupt_vector_table_address(); - auto* original_stack = reinterpret_cast(table_reg)[0]; - auto* original_reset = reinterpret_cast(table_reg)[1]; - - p_vector_table[hal::value(irq::top_of_stack)] = original_stack; - p_vector_table[hal::value(irq::reset)] = original_reset; - p_vector_table[hal::value(irq::non_maskable_interrupt)] = - default_interrupt_handler; - p_vector_table[hal::value(irq::hard_fault)] = hard_fault_handler; - p_vector_table[hal::value(irq::memory_management_fault)] = - memory_management_fault_handler; - p_vector_table[hal::value(irq::bus_fault)] = bus_fault_handler; - p_vector_table[hal::value(irq::usage_fault)] = usage_fault_handler; - p_vector_table[hal::value(irq::reserve7)] = default_interrupt_handler; - p_vector_table[hal::value(irq::reserve8)] = default_interrupt_handler; - p_vector_table[hal::value(irq::reserve9)] = default_interrupt_handler; - p_vector_table[hal::value(irq::reserve10)] = default_interrupt_handler; - p_vector_table[hal::value(irq::software_call)] = default_interrupt_handler; - p_vector_table[hal::value(irq::reserve12)] = default_interrupt_handler; - p_vector_table[hal::value(irq::reserve13)] = default_interrupt_handler; - p_vector_table[hal::value(irq::pend_sv)] = default_interrupt_handler; - p_vector_table[hal::value(irq::systick)] = default_interrupt_handler; - - // Fill the interrupt handler and vector table with a function that does - // nothing functions. - std::ranges::fill(p_vector_table, &default_interrupt_handler); -} - -constexpr bool is_the_same_vector_buffer( - std::span p_vector_table) -{ - p_vector_table = p_vector_table.subspan(-core_interrupts); - return (p_vector_table.data() == vector_table.data() && - p_vector_table.size() == vector_table.size()); -} - -bool is_valid_irq_request(irq_t p_irq) -{ - if (not interrupt_vector_table_initialized()) { - return false; - } - - bool within_bounds = hal::value(irq::top_of_stack) <= p_irq && - p_irq <= static_cast(vector_table.size()); - - if (not within_bounds) { - return false; - } - - return true; -} -} // namespace - -void default_interrupt_handler() -{ - while (true) { - continue; - } -} - -void hard_fault_handler() -{ - while (true) { - continue; - } -} - -void memory_management_fault_handler() -{ - while (true) { - continue; - } -} - -void bus_fault_handler() -{ - while (true) { - continue; - } -} - -void usage_fault_handler() -{ - while (true) { - continue; - } -} - -void disable_all_interrupts() -{ -#if defined(__arm__) - asm volatile("cpsid i" : : : "memory"); -#endif -} - -void enable_all_interrupts() -{ -#if defined(__arm__) - asm volatile("cpsie i" : : : "memory"); -#endif -} - -bool interrupt_vector_table_initialized() -{ - auto* vector_base = reinterpret_cast(&vector_table[core_interrupts]); - - return get_interrupt_vector_table_address() == vector_base; -} - -std::span const get_vector_table() -{ - return vector_table; -} - -void enable_interrupt(irq_t p_irq, interrupt_pointer p_handler) -{ - if (not is_valid_irq_request(p_irq)) { - return; - } - - vector_table[p_irq] = p_handler; - - if (p_irq >= 0) { - nvic_enable_irq(p_irq); - } -} - -void disable_interrupt(irq_t p_irq) -{ - if (!is_valid_irq_request(p_irq)) { - return; - } - - if (p_irq < 0) { - return; - } - - nvic_disable_irq(p_irq); -} - -bool is_interrupt_enabled(irq_t p_irq) -{ - if (!is_valid_irq_request(p_irq)) { - return false; - } - - if (p_irq < 0) { - return true; - } - - uint32_t enable_register = nvic->iser[register_index(p_irq)]; - - return (enable_register & (1 << p_irq)) != 0U; -} - -bool verify_vector_enabled(irq_t p_irq, interrupt_pointer p_handler) -{ - if (!is_valid_irq_request(p_irq)) { - return false; - } - - // Check if the handler match - auto irq_handler = vector_table[p_irq]; - bool handlers_are_the_same = (irq_handler == p_handler); - - if (not handlers_are_the_same) { - return false; - } - - if (p_irq < 0) { - return true; - } - - uint32_t enable_register = nvic->iser[register_index(p_irq)]; - - return (enable_register & (1 << p_irq)) != 0U; -} - -void revert_interrupt_vector_table() -{ - disable_all_interrupts(); - - // Set all bits in the interrupt clear register to 1s to disable those - // interrupt vectors. - for (auto& clear_interrupt : nvic->icer) { - clear_interrupt = 0xFFFF'FFFF; - } - - // Reset vector table - vector_table = std::span(); -} - -void initialize_interrupts(std::span p_vector_table) -{ - // If initialize function has already been called before with this same - // buffer, return early. - if (is_the_same_vector_buffer(p_vector_table)) { - return; - } - - setup_default_vector_table(p_vector_table); - - disable_all_interrupts(); - - // Assign the vector within this scope to the global vector_table span so - // that it can be accessed in other functions. This is valid because the - // interrupt vector table has static storage duration and will exist - // throughout the duration of the application. - vector_table = p_vector_table.subspan(-core_interrupts); - - // Relocate the interrupt vector table the vector buffer. By default this - // will be set to the address of the start of flash memory for the MCU. - set_interrupt_vector_table_address( - reinterpret_cast(p_vector_table.data())); - - enable_all_interrupts(); -} -} // namespace hal::cortex_m - -// Single version for ALL Cortex-M processors -extern "C" -{ - /** - * @brief Hard fault handler that gracefully handles semihosting breakpoints - * - * This handler overrides picolibc's default hard fault handler to detect and - * skip semihosting BKPT instructions when no debugger is attached. Without - * this handler, applications linked with semihosting libraries will hang in - * an infinite loop when executed standalone. - * - * The handler checks if a hard fault was caused by a debug event (BKPT - * instruction). If so, it: - * 1. Clears the hard fault status - * 2. Advances the program counter past the BKPT instruction (2 bytes) - * 3. Sets R0 to -1 to indicate the semihosting operation failed - * 4. Returns to continue execution - * - * If the fault was caused by something other than a BKPT, the handler enters - * an infinite loop to halt execution, indicating a real hard fault condition. - * - * This implementation is based on SEGGER's hard fault handler reference: - * https://kb.segger.com/Arm_Cortex-M_interrupts - * - * Modified to use ARMv6-M (Cortex-M0) compatible instructions, making it - * compatible with all Cortex-M variants (M0/M0+/M1/M3/M4/M7/M23/M33/M55/M85). - * - * @note This handler is wrapped via linker flag -Wl,--wrap=arm_hardfault_isr - * to override picolibc's default implementation. - */ - __attribute__((naked)) void __wrap_arm_hardfault_isr(void) // NOLINT - { -#if defined(__CORTEX_M) - __asm volatile(" movs r0, #4 \n" - " mov r1, lr \n" - " tst r0, r1 \n" - " beq .use_msp \n" - " mrs r0, psp \n" - " b .check_debug \n" - ".use_msp: \n" - " mrs r0, msp \n" - ".check_debug: \n" - " ldr r1, =0xE000ED2C \n" - " ldr r2, [r1] \n" - " lsls r2, #1 \n" - ".hardfault_loop: \n" - " bcc .hardfault_loop \n" - " ldr r2, [r1] \n" - " str r2, [r1] \n" - " ldr r1, [r0, #24] \n" - " adds r1, r1, #2 \n" - " str r1, [r0, #24] \n" - " movs r1, #1 \n" - " negs r1, r1 \n" - " str r1, [r0, #0] \n" - " bx lr \n" - : - : - : "memory"); -#endif - } -} // extern "C" diff --git a/src/interrupt_reg.hpp b/src/interrupt_reg.hpp deleted file mode 100644 index b5638e0..0000000 --- a/src/interrupt_reg.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include - -namespace hal::cortex_m { - -/// Structure type to access the Nested Vectored Interrupt Controller (NVIC) -struct nvic_register_t -{ - /// Offset: 0x000 (R/W) Interrupt Set Enable Register - std::array iser; - /// Reserved 0 - std::array reserved0; - /// Offset: 0x080 (R/W) Interrupt Clear Enable Register - std::array icer; - /// Reserved 1 - std::array reserved1; - /// Offset: 0x100 (R/W) Interrupt Set Pending Register - std::array ispr; - /// Reserved 2 - std::array reserved2; - /// Offset: 0x180 (R/W) Interrupt Clear Pending Register - std::array icpr; - /// Reserved 3 - std::array reserved3; - /// Offset: 0x200 (R/W) Interrupt Active bit Register - std::array iabr; - /// Reserved 4 - std::array reserved4; - /// Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) - std::array ip; - /// Reserved 5 - std::array reserved5; - /// Offset: 0xE00 ( /W) Software Trigger Interrupt Register - u32 volatile stir; -}; - -/// NVIC address -inline constexpr auto nvic_address = static_cast(0xE000'E100UL); - -// NOLINTNEXTLINE(performance-no-int-to-ptr) -inline auto* nvic = reinterpret_cast(nvic_address); - -/// Place holder interrupt that performs no work -void nop(); -} // namespace hal::cortex_m diff --git a/src/lpc40/adc.cpp b/src/lpc40/adc.cpp deleted file mode 100644 index 4b31e4e..0000000 --- a/src/lpc40/adc.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include -#include -#include - -#include "adc_reg.hpp" - -namespace hal::lpc40 { - -namespace { -void setup(adc::channel const& p_channel) -{ - using namespace hal::literals; - - if (p_channel.clock_rate > 1.0_MHz) { - throw std::errc::invalid_argument; - } - - if (p_channel.index >= adc_reg_t::channel_length) { - throw std::errc::invalid_argument; - } - - power_on(peripheral::adc); - - // For proper operation, analog pins must be set to floating. - p_channel.adc_pin.function(p_channel.pin_function) - .resistor(hal::pin_resistor::none) - .open_drain(false) - .analog(true); - - auto const clock_frequency = get_frequency(peripheral::adc); - auto const clock_divider = clock_frequency / p_channel.clock_rate; - auto const clock_divider_int = static_cast(clock_divider); - - // Activate burst mode (continuous sampling), power on ADC and set clock - // divider. - hal::bit_modify(adc_reg->control) - .set() - .set() - .insert(clock_divider_int); - - // Enable channel. Must be done in a separate write to memory than power on - // and burst enable. - hal::bit_modify(adc_reg->control) - .set(bit_mask{ .position = p_channel.index, .width = 1 }); -} -} // namespace - -adc::adc(channel const& p_channel) - : m_sample(&adc_reg->data[p_channel.index]) -{ - setup(p_channel); -} - -adc::channel adc::get_predefined_channel_info(std::uint8_t p_channel) -{ - enum adc_function : uint8_t - { - pin_0123 = 0b001, - pin_4567 = 0b011 - }; - constexpr std::array channels{ - adc::channel{ - .adc_pin = pin(0, 23), - .index = 0, - .pin_function = adc_function::pin_0123, - }, - adc::channel{ - .adc_pin = pin(0, 24), - .index = 1, - .pin_function = adc_function::pin_0123, - }, - adc::channel{ - .adc_pin = pin(0, 25), - .index = 2, - .pin_function = adc_function::pin_0123, - }, - adc::channel{ - .adc_pin = pin(0, 26), - .index = 3, - .pin_function = adc_function::pin_0123, - }, - adc::channel{ - .adc_pin = pin(1, 30), - .index = 4, - .pin_function = adc_function::pin_4567, - }, - adc::channel{ - .adc_pin = pin(1, 31), - .index = 5, - .pin_function = adc_function::pin_4567, - }, - adc::channel{ - .adc_pin = pin(0, 12), - .index = 6, - .pin_function = adc_function::pin_4567, - }, - adc::channel{ - .adc_pin = pin(0, 13), - .index = 7, - .pin_function = adc_function::pin_4567, - }, - }; - - return channels[p_channel]; -} - -float adc::driver_read() -{ - constexpr auto full_scale_max = bit_limits<12, size_t>::max(); - constexpr auto full_scale_float = static_cast(full_scale_max); - // Read sample from peripheral memory - auto sample_integer = hal::bit_extract(*m_sample); - auto sample = static_cast(sample_integer); - return sample / full_scale_float; -} -} // namespace hal::lpc40 diff --git a/src/lpc40/adc_reg.hpp b/src/lpc40/adc_reg.hpp deleted file mode 100644 index 60d8cc9..0000000 --- a/src/lpc40/adc_reg.hpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include - -namespace hal::lpc40 { -/// adc register map -struct adc_reg_t -{ - /// Number of channels - static constexpr size_t channel_length = 8; - /// Offset: 0x000 A/D Control Register (R/W) - uint32_t volatile control; - /// Offset: 0x004 A/D Global Data Register (R/W) - uint32_t volatile global_data; - /// Offset: 0x008 Reserved 0 - std::array reserved0; - /// Offset: 0x00C A/D Interrupt Enable Register (R/W) - uint32_t volatile interrupt_enable; - /// Offset: 0x010-0x02C A/D Channel 0..7 Data Register (R/W) - std::array data; - /// Offset: 0x030 A/D Status Register (R/ ) - uint32_t const volatile stat; - /// Offset: 0x034 A/D Trim Calibration (R/W) - uint32_t volatile trim; -}; - -/// Namespace containing the bit_mask objects that are used to manipulate the -/// lpc40xx ADC Control register. -namespace adc_control_register { -/// In burst mode, sets the ADC channels to be automatically converted. -/// It bit position represents 1 channel with this 8 channel ADC. -/// In software mode, this should hold only a single 1 for the single -/// channel to be converted. -static constexpr auto channel_select = hal::bit_mask::from<0, 7>(); - -/// Sets the channel's clock divider. Potentially saving power if clock is -/// reduced further. -static constexpr auto clock_divider = hal::bit_mask::from<8, 15>(); - -/// Enable Burst Mode for the ADC. See BurstMode() method of this class to -/// learn more about what it is and how it works. -static constexpr auto burst_enable = hal::bit_mask::from<16>(); - -/// Power on the ADC -static constexpr auto power_enable = hal::bit_mask::from<21>(); - -/// In order to start a conversion a start code must be inserted into this -/// bit location. -static constexpr auto start_code = hal::bit_mask::from<24, 26>(); - -/// Not used in this driver, but allows the use of an external pins to -/// trigger a conversion. This flag indicates if rising or falling edges -/// trigger the conversion. -/// 1 = falling, 0 = rising. -static constexpr auto start_edge = hal::bit_mask::from<27>(); -}; // namespace adc_control_register - -/// Namespace containing the bit_mask objects that are used to manipulate the -/// lpc40xx ADC Global Data register. -namespace adc_data_register { -/// Result mask holds the latest result from the last ADC that was converted -static constexpr auto result = hal::bit_mask::from<4, 15>(); - -/// Converted channel mask indicates which channel was converted in the -/// latest conversion. -static constexpr auto converted_channel = hal::bit_mask::from<24, 26>(); - -/// Holds whether or not the ADC overran its conversion. -static constexpr auto overrun = hal::bit_mask::from<30>(); - -/// Indicates when the ADC conversion is complete. -static constexpr auto done = hal::bit_mask::from<31>(); -}; // namespace adc_data_register - -constexpr std::intptr_t lpc_apb0_base = 0x40000000UL; -constexpr std::intptr_t lpc_adc_addr = lpc_apb0_base + 0x34000; -// NOLINTNEXTLINE(performance-no-int-to-ptr) -inline auto* adc_reg = reinterpret_cast(lpc_adc_addr); -} // namespace hal::lpc40 diff --git a/src/lpc40/can.cpp b/src/lpc40/can.cpp deleted file mode 100644 index 9fb562e..0000000 --- a/src/lpc40/can.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "can_reg.hpp" - -namespace hal::lpc40 { -namespace { - -can_reg_t* get_can_reg(peripheral p_id) -{ - switch (p_id) { - case peripheral::can1: - return can_reg1; - case peripheral::can2: - default: - return can_reg2; - } -} - -/// Container for the LPC40xx CAN BUS registers -struct lpc_message -{ - /// TFI register contents - uint32_t frame = 0; - /// TID register contents - uint32_t id = 0; - /// TDA register contents - uint32_t data_a = 0; - /// TDB register contents - uint32_t data_b = 0; -}; - -void enable_acceptance_filter() noexcept -{ - can_acceptance_filter->acceptance_filter = - value(can_commands::accept_all_messages); -} - -[[maybe_unused]] bool has_data(can_reg_t* p_reg) noexcept -{ - return bit_extract(p_reg->gsr); -} - -can::message_t receive(can_reg_t* p_reg) noexcept -{ - static constexpr auto id_mask = bit_mask::from<0, 28>(); - can::message_t message; - - // Extract all of the information from the message frame - auto frame = p_reg->rfs; - auto remote_request = bit_extract(frame); - auto length = bit_extract(frame); - - message.is_remote_request = remote_request; - message.length = static_cast(length); - - // Get the frame ID - message.id = bit_extract(p_reg->rid); - - // Pull the bytes from RDA into the payload array - message.payload[0] = (p_reg->rda >> (0 * 8)) & 0xFF; - message.payload[1] = (p_reg->rda >> (1 * 8)) & 0xFF; - message.payload[2] = (p_reg->rda >> (2 * 8)) & 0xFF; - message.payload[3] = (p_reg->rda >> (3 * 8)) & 0xFF; - - // Pull the bytes from RDB into the payload array - message.payload[4] = (p_reg->rdb >> (0 * 8)) & 0xFF; - message.payload[5] = (p_reg->rdb >> (1 * 8)) & 0xFF; - message.payload[6] = (p_reg->rdb >> (2 * 8)) & 0xFF; - message.payload[7] = (p_reg->rdb >> (3 * 8)) & 0xFF; - - // Release the RX buffer and allow another buffer to be read. - p_reg->cmr = value(can_commands::release_rx_buffer); - - return message; -} - -/// Convert message into the registers LPC40xx can bus registers. -/// -/// @param message - message to convert. -can_lpc_message message_to_registers(can::message_t const& p_message) -{ - static constexpr auto highest_11_bit_number = 2048UL; - can_lpc_message registers; - - uint32_t message_frame_info = 0; - - if (p_message.id < highest_11_bit_number) { - message_frame_info = - bit_value(0) - .insert(p_message.length) - .insert(p_message.is_remote_request) - .insert(0U) - .to(); - } else { - message_frame_info = - bit_value(0) - .insert(p_message.length) - .insert(p_message.is_remote_request) - .insert(1U) - .to(); - } - - uint32_t data_a = 0; - data_a |= static_cast(p_message.payload[0] << (0UL * 8)); - data_a |= static_cast(p_message.payload[1] << (1UL * 8)); - data_a |= static_cast(p_message.payload[2] << (2UL * 8)); - data_a |= static_cast(p_message.payload[3] << (3UL * 8)); - - uint32_t data_b = 0; - data_b |= static_cast(p_message.payload[4U] << (0UL * 8UL)); - data_b |= static_cast(p_message.payload[5U] << (1UL * 8UL)); - data_b |= static_cast(p_message.payload[6U] << (2UL * 8UL)); - data_b |= static_cast(p_message.payload[7U] << (3UL * 8UL)); - - registers.frame = message_frame_info; - registers.id = p_message.id; - registers.data_a = data_a; - registers.data_b = data_b; - - return registers; -} -} // namespace - -void can::configure_baud_rate(can::port const& p_port, - can::settings const& p_settings) -{ - using namespace hal::literals; - - auto* reg = get_can_reg(p_port.id); - auto const can_frequency = get_frequency(p_port.id); - - bool external_oscillator_used = using_external_oscillator(); - bool baud_rate_above_100khz = p_settings.baud_rate > 100.0_kHz; - auto const valid_divider = - hal::calculate_can_bus_divider(can_frequency, p_settings.baud_rate); - - if ((baud_rate_above_100khz && not external_oscillator_used) || - not valid_divider) { - safe_throw(hal::operation_not_supported(this)); - } - - auto const dividers = valid_divider.value(); - auto const prescale = dividers.clock_divider - 1U; - auto const sync_jump_width = dividers.synchronization_jump_width - 1U; - - auto phase_segment1 = - (dividers.phase_segment1 + dividers.propagation_delay) - 1U; - auto phase_segment2 = dividers.phase_segment2 - 1U; - - constexpr auto segment2_bit_limit = - hal::bit_limits::max(); - - // Check if phase segment 2 does not fit - if (phase_segment2 > segment2_bit_limit) { - // Take the extra time quanta and add it to the phase 1 segment - auto const phase_segment2_remainder = phase_segment2 - segment2_bit_limit; - phase_segment1 += phase_segment2_remainder; - // Cap phase segment 2 to the max available in the bit field - phase_segment2 = segment2_bit_limit; - } - - std::uint8_t enable_triple_sampling = 0; - // The bus is sampled 3 times (recommended for low speeds, 100kHz is - // considered HIGH). - if (p_settings.baud_rate < 100.0_kHz) { - enable_triple_sampling = 1U; - } - - bit_modify(reg->btr) - .insert(sync_jump_width) - .insert(phase_segment1) - .insert(phase_segment2) - .insert(prescale) - .insert(enable_triple_sampling); -} - -void can::setup(can::port const& p_port, can::settings const& p_settings) -{ - auto* reg = get_can_reg(p_port.id); - - initialize_interrupts(); - - /// Power on CAN BUS peripheral - power_on(p_port.id); - - /// Configure pins - p_port.td.function(p_port.td_function_code); - p_port.rd.function(p_port.rd_function_code); - - // Enable reset mode in order to write to CAN registers. - bit_modify(reg->mod).set(); - - configure_baud_rate(p_port, p_settings); - enable_acceptance_filter(); - - // Disable reset mode, enabling the device - bit_modify(reg->mod).clear(); -} - -can::can(std::uint8_t p_port_number, can::settings const& p_settings) -{ - if (p_port_number == 1) { - m_port = can::port{ - .td = pin(0, 1), - .td_function_code = 1, - .rd = pin(0, 0), - .rd_function_code = 1, - .id = peripheral::can1, - .irq_number = irq::can, - }; - } else if (p_port_number == 2) { - m_port = can::port{ - .td = pin(2, 8), - .td_function_code = 1, - .rd = pin(2, 7), - .rd_function_code = 1, - .id = peripheral::can2, - .irq_number = irq::can, - }; - } else { - safe_throw(hal::operation_not_supported(this)); - } - - setup(m_port, p_settings); -} - -can::~can() -{ - auto* reg = get_can_reg(m_port.id); - // Disable generating an interrupt request by this CAN peripheral, but leave - // the interrupt enabled. We must NOT disable the interrupt via Arm's NVIC - // as it could be used by the other CAN peripheral. - bit_modify(reg->ier).clear(); -} - -/** - * @brief Construct a new can object - * - * @param p_port - CAN port information - */ -can::can(port const& p_port, can::settings const& p_settings) - : m_port(p_port) -{ - setup(p_port, p_settings); -} - -void can::driver_configure(can::settings const& p_settings) -{ - return setup(m_port, p_settings); -} - -void can::driver_send(message_t const& p_message) -{ - auto* reg = get_can_reg(m_port.id); - auto can_message_registers = message_to_registers(p_message); - - // Wait for one of the buffers to be free so we can transmit a message - // through it. - bool sent = false; - while (!sent) { - auto const status_register = reg->sr; - // Check if any buffer is available. - if (bit_extract(status_register) == - can_buffer_status::bus_off) { - throw std::errc::network_down; - } else if (bit_extract(status_register)) { - reg->tfi1 = can_message_registers.frame; - reg->tid1 = can_message_registers.id; - reg->tda1 = can_message_registers.data_a; - reg->tdb1 = can_message_registers.data_b; - reg->cmr = value(can_commands::send_tx_buffer1); - sent = true; - } else if (bit_extract(status_register)) { - reg->tfi2 = can_message_registers.frame; - reg->tid2 = can_message_registers.id; - reg->tda2 = can_message_registers.data_a; - reg->tdb2 = can_message_registers.data_b; - reg->cmr = value(can_commands::send_tx_buffer2); - sent = true; - } else if (bit_extract(status_register)) { - reg->tfi3 = can_message_registers.frame; - reg->tid3 = can_message_registers.id; - reg->tda3 = can_message_registers.data_a; - reg->tdb3 = can_message_registers.data_b; - reg->cmr = value(can_commands::send_tx_buffer3); - sent = true; - } - } -} - -void can::driver_bus_on() -{ - auto* reg = get_can_reg(m_port.id); - // When the device is in "bus-off" mode, the mode::reset bit is set to '1'. To - // re-enable the device, clear the reset bit. - bit_modify(reg->mod).clear(); -} - -void can::driver_on_receive(hal::callback p_receive_handler) -{ - auto* reg = get_can_reg(m_port.id); - // Save the handler - m_receive_handler = p_receive_handler; - - // Create a lambda that passes this object's reference to the stored handler - auto isr = [this, reg]() { - auto message = receive(reg); - m_receive_handler(message); - }; - - auto can_handler = static_callable(isr).get_handler(); - cortex_m::enable_interrupt(irq::can, can_handler); - - bit_modify(reg->ier).set(can_interrupts::received_message); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/can_reg.hpp b/src/lpc40/can_reg.hpp deleted file mode 100644 index b8c114f..0000000 --- a/src/lpc40/can_reg.hpp +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include -#include - -namespace hal::lpc40 { -struct can_acceptance_filter_ram_t -{ - /// Mask IDs - std::array mask; -}; - -struct can_acceptance_filter_t -{ - /// Offset: 0x00000000 - Acceptance Filter Register - u32 volatile acceptance_filter; - /// Offset: 0x00000004 - Standard Frame Individual Start Address Register - u32 volatile sff_sa; - /// Offset: 0x00000008 - Standard Frame Group Start Address Register - u32 volatile sff_grp_sa; - /// Offset: 0x0000000C - Extended Frame Start Address Register - u32 volatile eff_sa; - /// Offset: 0x00000010 - Extended Frame Group Start Address Register - u32 volatile eff_grp_sa; - /// Offset: 0x00000014 - End of AF Tables register - u32 volatile endoftable; - /// Offset: 0x00000018 - LUT Error Address register - u32 const volatile luterrad; - /// Offset: 0x0000001C - LUT Error Register - u32 const volatile luterr; - /// Offset: 0x00000020 - CAN Central Transmit Status Register - u32 volatile full_can_transmit_status; - /// Offset: 0x00000024 - FullCAN Interrupt and Capture registers 0 - u32 volatile fcanic0; - /// Offset: 0x00000028 - FullCAN Interrupt and Capture registers 1 - u32 volatile fcanic1; -}; - -struct can_central_reg_t -{ - u32 const volatile txsr; - u32 const volatile rxsr; - u32 const volatile msr; -}; - -struct can_reg_t -{ - /// Offset: 0x00000000 - Controls the operating mode of the CAN Controller - u32 volatile mod; - /// Offset: 0x00000004 - Command bits that affect the state - u32 volatile cmr; - /// Offset: 0x00000008 - Global Controller Status and Error Counters - u32 volatile gsr; - /// Offset: 0x0000000C - Interrupt status, Arbitration Lost Capture, Error - /// Code Capture - u32 const volatile icr; - /// Offset: 0x00000010 - Interrupt Enable Register - u32 volatile ier; - /// Offset: 0x00000014 - Bus Timing Register - u32 volatile btr; - /// Offset: 0x00000018 - Error Warning Limit - u32 volatile ewl; - /// Offset: 0x0000001C - Status Register - u32 const volatile sr; - /// Offset: 0x00000020 - Receive frame status - u32 volatile rfs; - /// Offset: 0x00000024 - Received Identifier - u32 volatile rid; - /// Offset: 0x00000028 - Received data bytes 1-4 - u32 volatile rda; - /// Offset: 0x0000002C - Received data bytes 5-8 - u32 volatile rdb; - /// Offset: 0x00000030 - Transmit frame info (Tx Buffer 1) - u32 volatile tfi1; - /// Offset: 0x00000034 - Transmit Identifier (Tx Buffer 1) - u32 volatile tid1; - /// Offset: 0x00000038 - Transmit data bytes 1-4 (Tx Buffer 1) - u32 volatile tda1; - /// Offset: 0x0000003C - Transmit data bytes 5-8 (Tx Buffer 1) - u32 volatile tdb1; - /// Offset: 0x00000040 - Transmit frame info (Tx Buffer 2) - u32 volatile tfi2; - /// Offset: 0x00000044 - Transmit Identifier (Tx Buffer 2) - u32 volatile tid2; - /// Offset: 0x00000048 - Transmit data bytes 1-4 (Tx Buffer 2) - u32 volatile tda2; - /// Offset: 0x0000004C - Transmit data bytes 5-8 (Tx Buffer 2) - u32 volatile tdb2; - /// Offset: 0x00000050 - Transmit frame info (Tx Buffer 3) - u32 volatile tfi3; - /// Offset: 0x00000054 - Transmit Identifier (Tx Buffer 3) - u32 volatile tid3; - /// Offset: 0x00000058 - Transmit data bytes 1-4 (Tx Buffer 3) - u32 volatile tda3; - /// Offset: 0x0000005C - Transmit data bytes 5-8 (Tx Buffer 3) - u32 volatile tdb3; -}; - -/// Container for the LPC40xx CAN BUS registers -struct can_lpc_message -{ - /// TFI register contents - u32 frame = 0; - /// TID register contents - u32 id = 0; - /// TDA register contents - u32 data_a = 0; - /// TDB register contents - u32 data_b = 0; -}; // namespace can_lpc_message - -/// https://www.nxp.com/docs/en/user-guide/UM10562.pdf (pg. 554) -enum class can_commands : u8 -{ - release_rx_buffer = 0x04, - send_tx_buffer1 = 0x21, - send_tx_buffer2 = 0x41, - send_tx_buffer3 = 0x81, - self_reception_send_tx_buffer1 = 0x30, - accept_all_messages = 0x02, -}; - -/// This struct holds bit timing values. It is used to configure the CAN bus -/// clock. It is HW mapped to a 32-bit register: BTR (pg. 562) -namespace can_bus_timing { -/// The peripheral bus clock is divided by this value -static constexpr auto prescalar = bit_mask::from<0, 9>(); - -/// Used to compensate for positive and negative edge phase errors -static constexpr auto sync_jump_width = bit_mask::from<14, 15>(); -/// The delay from the nominal Sync point to the sample point is (this value -/// plus one) CAN clocks. -static constexpr auto time_segment1 = bit_mask::from<16, 19>(); - -/// The delay from the sample point to the next nominal sync point isCAN -/// clocks. The nominal CAN bit time is (this value plus the value in -/// time_segment1 plus 3) CAN clocks. -static constexpr auto time_segment2 = bit_mask::from<20, 22>(); - -/// How many times the bus is sampled; 0 == once, 1 == 3 times -static constexpr auto sampling = bit_mask::from<23>(); -}; // namespace can_bus_timing - -/// This struct holds interrupt flags and capture flag status. It is HW mapped -/// to a 16-bit register: ICR (pg. 557) -namespace can_interrupts { -// ICR - Interrupt and Capture Register -// NOTE: Bits 1-10 are cleared by the CAN controller -// as soon as they are read. -// Bits 16-23 & 24-31 are released by the CAN -// controller as soon as they are read. - -/// Assert interrupt when a new message has been received -static constexpr auto received_message = bit_mask::from<0>(); - -/// Assert interrupt when TX Buffer 1 has finished or aborted its -/// transmission. -static constexpr auto tx1_ready = bit_mask::from<1>(); - -/// Assert interrupt when bus status or error status is asserted. -static constexpr auto error_warning = bit_mask::from<2>(); - -/// Assert interrupt on data overrun occurs -static constexpr auto data_overrun = bit_mask::from<3>(); - -/// Assert interrupt when CAN controller is sleeping and was woken up from -/// bus activity. -static constexpr auto wakeup = bit_mask::from<4>(); - -/// Assert interrupt when the CAN Controller has reached the Error Passive -/// Status (error counter exceeds 127) -static constexpr auto error_passive = bit_mask::from<5>(); - -/// Assert interrupt when arbitration is lost -static constexpr auto arbitration_lost = bit_mask::from<6>(); - -/// Assert interrupt on bus error -static constexpr auto bus_error = bit_mask::from<7>(); - -/// Assert interrupt when any message has been successfully transmitted. -static constexpr auto identifier_ready = bit_mask::from<8>(); - -/// Assert interrupt when TX Buffer 2 has finished or aborted its -/// transmission. -static constexpr auto tx2_ready = bit_mask::from<9>(); - -/// Assert interrupt when TX Buffer 3 has finished or aborted its -/// transmission. -static constexpr auto tx3_ready = bit_mask::from<10>(); - -/// Error Code Capture status bits to be read during an interrupt -static constexpr auto error_code_location = bit_mask::from<16, 20>(); -/// Indicates if the error occurred during transmission (0) or receiving (1) -static constexpr auto error_code_direction = bit_mask::from<21>(); -/// The type of bus error that occurred such as bit error, stuff error, etc -static constexpr auto error_code_type = bit_mask::from<22, 23>(); -/// Bit location of where arbitration was lost. -static constexpr auto arbitration_lost_loc = bit_mask::from<24, 31>(); -}; // namespace can_interrupts - -/// This struct holds CAN controller global status information. -/// It is a condensed version of the status register. -/// It is HW mapped to a 32-bit register: GSR (pg. 555) -namespace can_global_status { -/// If 1, receive buffer has at least 1 complete message stored -static constexpr auto receive_buffer = bit_mask::from<0>(); - -/// Bus status bit. If this is '1' then the bus is active, otherwise the bus -/// is bus off. -static constexpr auto bus_error = bit_mask::from<7>(); -}; // namespace can_global_status - -/// This struct holds CAN controller status information. It is HW mapped to a -/// 32-bit register: SR (pg. 564). Many of them are not here because they have -/// counter parts in GSR (global status register). -namespace can_buffer_status { -/// TX1 Buffer has been released -static constexpr auto tx1_released = bit_mask::from<2>(); - -/// TX2 Buffer has been released -static constexpr auto tx2_released = bit_mask::from<10>(); - -/// TX3 Buffer has been released -static constexpr auto tx3_released = bit_mask::from<18>(); - -/// Will be 0 if the device is Bus-On. -/// Will be 1 if the device is Bus-Off. -static constexpr auto bus_status = bit_mask::from<15>(); -static constexpr u32 bus_on = 0; -static constexpr u32 bus_off = 1; -}; // namespace can_buffer_status - -/// CAN BUS modes -namespace can_mode { -/// Reset CAN Controller, allows configuration registers to be modified. -static constexpr auto reset = bit_mask::from<0>(); - -/// Put device into Listen Only Mode, device will not acknowledge, messages. -static constexpr auto listen_only = bit_mask::from<1>(); - -/// Put device on self test mode. -static constexpr auto self_test = bit_mask::from<2>(); - -/// Enable transmit priority control. When enabled, allows a particular -static constexpr auto tx_priority = bit_mask::from<3>(); - -/// Put device to Sleep Mode. -static constexpr auto sleep_mode = bit_mask::from<4>(); - -/// Receive polarity mode. If 1 RD input is active high -static constexpr auto rx_polarity = bit_mask::from<5>(); - -/// Put CAN into test mode, which allows the TD pin to reflect its bits ot -/// the RD pin. -static constexpr auto test = bit_mask::from<7>(); -}; // namespace can_mode - -/// CAN Bus frame bit masks for the TFM and RFM registers -namespace can_frame_info { -/// The message priority bits (not used in this implementation) -static constexpr auto priority = bit_mask::from<0, 7>(); - -/// The length of the data -static constexpr auto length = bit_mask::from<16, 19>(); - -/// If set to 1, the message becomes a remote request message -static constexpr auto remote_request = bit_mask::from<30>(); - -/// If 0, the ID is 11-bits, if 1, the ID is 29-bits. -static constexpr auto format = bit_mask::from<31>(); -}; // namespace can_frame_info - -/// Pointer to the LPC CAN BUS acceptance filter peripheral in memory -inline auto* can_acceptance_filter = - reinterpret_cast(0x4003'C000); -inline auto* can_reg1 = reinterpret_cast(0x4004'4000); -inline auto* can_reg2 = reinterpret_cast(0x4004'8000); -} // namespace hal::lpc40 diff --git a/src/lpc40/clock.cpp b/src/lpc40/clock.cpp deleted file mode 100644 index e122ea1..0000000 --- a/src/lpc40/clock.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include - -#include - -#include -#include -#include - -#include "system_controller_reg.hpp" - -namespace hal::lpc40 { - -namespace { -hertz cpu_clock_rate = irc_frequency; -hertz emc_clock_rate = irc_frequency; -hertz usb_clock_rate = irc_frequency; -hertz spifi_clock_source_rate = irc_frequency; -hertz peripheral_clock_rate = irc_frequency / default_peripheral_divider; - -struct pll_registers -{ - uint32_t volatile* p_control; - uint32_t volatile* p_config; - uint32_t volatile* p_feed; - uint32_t const volatile* p_stat; -}; - -hertz setup_pll(clock_tree const& p_clock_config, - pll_registers const& p_pll_registers, - std::uint8_t p_pll_index) -{ - using namespace hal::literals; - - auto const& pll_config = p_clock_config.pll[p_pll_index]; - hertz fcco = 0.0_Hz; - - if (pll_config.enabled) { - hal::bit_modify(*p_pll_registers.p_config) - .insert( - static_cast(pll_config.multiply - 1U)); - - if (p_clock_config.use_external_oscillator == false && p_pll_index == 0) { - fcco = irc_frequency * static_cast(pll_config.multiply); - } else { - fcco = p_clock_config.oscillator_frequency * - static_cast(pll_config.multiply); - } - - // In the data sheet this is the divider, but it acts to multiply the - // frequency higher to a point where the fcco is stable. - // - // fcco must be between 156 MHz to 320 MHz. - uint32_t fcco_divide = 0; - for (auto divide_codes : { 0U, 1U, 2U, 3U }) { - // Multiply the fcco by 2^divide_code - hertz final_fcco = fcco * static_cast(1U << divide_codes); - if (156.0_MHz <= final_fcco && final_fcco <= 320.0_MHz) { - fcco_divide = divide_codes; - break; - } - } - - hal::bit_modify(*p_pll_registers.p_config) - .insert(fcco_divide); - // Enable PLL - *p_pll_registers.p_control = 1; - // Feed PLL in order to start the locking process - *p_pll_registers.p_feed = 0xAA; - *p_pll_registers.p_feed = 0x55; - - while (!hal::bit_extract(*p_pll_registers.p_stat)) { - continue; - } - } - - return fcco; -} - -void enable_external_oscillator(hertz p_oscillator_frequency) -{ - using namespace hal::literals; - - auto frequency = p_oscillator_frequency; - // Range select is 0 when 1.0_MHz < frequency < 20.0_MHz - std::uint32_t range_select_value = 0; - - if (20.0_MHz < frequency && frequency <= 25.0_MHz) { - range_select_value = 1; - } - - hal::bit_modify(system_controller_reg->scs) - .insert(range_select_value) - .set(); - - while ( - !hal::bit_extract(system_controller_reg->scs)) { - continue; - } -} -} // namespace - -void maximum(hertz p_external_crystal_frequency) -{ - static constexpr auto max_speed = 120.0_MHz; - auto const multiply = max_speed / p_external_crystal_frequency; - - clock_tree config{}; - config.oscillator_frequency = p_external_crystal_frequency; - config.use_external_oscillator = true; - config.cpu.use_pll0 = true; - config.cpu.divider = 1; - config.emc_half_cpu_divider = false; - config.peripheral_divider = 1; - config.usb.clock = usb_clock_source::pll0; - config.usb.divider = usb_divider::divide_by1; - config.spifi.clock = spifi_clock_source::pll0; - config.spifi.divider = 1; - config.pll[0].enabled = true; - config.pll[0].multiply = static_cast(multiply); - config.pll[1].enabled = false; - - configure_clocks(config); -} - -/** - * @brief Determins if the external oscillator is currently enabled and in use - * - * @return true - external oscillator is in use currently - * @return false - external oscillator is NOT in use currently - */ -bool using_external_oscillator() -{ - return hal::bit_extract( - system_controller_reg->scs); -} - -hertz get_frequency(peripheral p_peripheral) -{ - switch (p_peripheral) { - case peripheral::emc: - return emc_clock_rate; - case peripheral::usb: - return usb_clock_rate; - case peripheral::spifi: - return spifi_clock_source_rate; - case peripheral::cpu: - return cpu_clock_rate; - default: - return peripheral_clock_rate; - } -} - -void configure_clocks(clock_tree const& p_clock_tree) -{ - using namespace hal::literals; - - hertz system_clock = 0.0_Hz; - hertz pll0 = 0.0_Hz; - hertz pll1 = 0.0_Hz; - hertz cpu = 0.0_Hz; - hertz usb = 0.0_Hz; - hertz spifi = 0.0_Hz; - - // ========================================================================= - // Step 1. Select IRC as clock source for everything. - // Make sure PLLs are not clock sources for everything. - // ========================================================================= - // Set CPU clock to system clock - hal::bit_modify(system_controller_reg->cpu_clock_select) - .insert(0UL); - - // Set USB clock to system clock - hal::bit_modify(system_controller_reg->usb_clock_select) - .insert(value(usb_clock_source::system_clock)); - - // Set spifi clock to system clock - hal::bit_modify(system_controller_reg->spifi_clock_select) - .insert(value(spifi_clock_source::system_clock)); - - // Set the clock source to IRC (0) and not external oscillator. The next - // phase disables that clock source, which will stop the system if this is - // not switched. - system_controller_reg->clock_source_select = 0; - - // ========================================================================= - // Step 2. Disable PLLs - // ========================================================================= - // NOTE: The only bit in this register that is used is bit 0 which indicates - // enabled or disabled status, thus a single assignment is needed. - system_controller_reg->pll0con = 0; - system_controller_reg->pll1con = 0; - - // Disabling external oscillator if it is not going to be used - hal::bit_modify(system_controller_reg->scs) - .clear(oscillator::external_enable); - - // ========================================================================= - // Step 3. Select oscillator source for System Clock and Main PLL - // ========================================================================= - // Enable the external oscillator if we are using it, which would be the - // case if the alternative PLL is enabled or external oscillator is - // selected. - if (p_clock_tree.use_external_oscillator == true || - p_clock_tree.pll[1].enabled) { - enable_external_oscillator(p_clock_tree.oscillator_frequency); - } - - system_controller_reg->clock_source_select = - p_clock_tree.use_external_oscillator; - - if (p_clock_tree.use_external_oscillator) { - system_clock = p_clock_tree.oscillator_frequency; - } else { - system_clock = irc_frequency; - } - - // ========================================================================= - // Step 4. Configure PLLs - // ========================================================================= - pll0 = setup_pll(p_clock_tree, - { - .p_control = &system_controller_reg->pll0con, - .p_config = &system_controller_reg->pll0cfg, - .p_feed = &system_controller_reg->pll0feed, - .p_stat = &system_controller_reg->pll0stat, - }, - 0); - - pll1 = setup_pll(p_clock_tree, - { - .p_control = &system_controller_reg->pll1con, - .p_config = &system_controller_reg->pll1cfg, - .p_feed = &system_controller_reg->pll1feed, - .p_stat = &system_controller_reg->pll1stat, - }, - 1); - - // ========================================================================= - // Step 5. Set clock dividers for each clock source - // ========================================================================= - // Set CPU clock divider - hal::bit_modify(system_controller_reg->cpu_clock_select) - .insert(p_clock_tree.cpu.divider); - - // Set EMC clock divider - hal::bit_modify(system_controller_reg->emmc_clock_select) - .insert(p_clock_tree.emc_half_cpu_divider); - - // Set Peripheral clock divider - hal::bit_modify(system_controller_reg->peripheral_clock_select) - .insert(p_clock_tree.peripheral_divider); - - // Set USB clock divider - hal::bit_modify(system_controller_reg->usb_clock_select) - .insert(value(p_clock_tree.usb.divider)); - - // Set spifi clock divider - hal::bit_modify(system_controller_reg->spifi_clock_select) - .insert(p_clock_tree.spifi.divider); - - if (p_clock_tree.cpu.use_pll0) { - cpu = pll0; - } else { - cpu = system_clock; - } - - switch (p_clock_tree.usb.clock) { - case usb_clock_source::system_clock: - usb = system_clock; - break; - case usb_clock_source::pll0: - usb = pll0; - break; - case usb_clock_source::pll1: - usb = pll1; - break; - } - - switch (p_clock_tree.spifi.clock) { - case spifi_clock_source::system_clock: - spifi = system_clock; - break; - case spifi_clock_source::pll0: - spifi = pll0; - break; - case spifi_clock_source::pll1: - spifi = pll1; - break; - } - - cpu_clock_rate = cpu / static_cast(p_clock_tree.cpu.divider); - peripheral_clock_rate = - cpu / static_cast(p_clock_tree.peripheral_divider); - emc_clock_rate = - cpu / static_cast(p_clock_tree.emc_half_cpu_divider + 1); - usb_clock_rate = usb / static_cast(p_clock_tree.usb.divider); - spifi_clock_source_rate = - spifi / static_cast(p_clock_tree.spifi.divider); - - // ========================================================================= - // Step 6. Configure flash cycles per load - // ========================================================================= - system_controller_reg->power_boost = 0b00; - - if (cpu_clock_rate < 20.0_MHz) { - system_controller_reg->flashcfg = - static_cast(flash_configuration::clock1); - } else if (20.0_MHz <= cpu_clock_rate && cpu_clock_rate < 40.0_MHz) { - system_controller_reg->flashcfg = - static_cast(flash_configuration::clock2); - } else if (40.0_MHz <= cpu_clock_rate && cpu_clock_rate < 60.0_MHz) { - system_controller_reg->flashcfg = - static_cast(flash_configuration::clock3); - } else if (60.0_MHz <= cpu_clock_rate && cpu_clock_rate < 80.0_MHz) { - system_controller_reg->flashcfg = - static_cast(flash_configuration::clock4); - } else if (80.0_MHz <= cpu_clock_rate && cpu_clock_rate < 100.0_MHz) { - system_controller_reg->flashcfg = - static_cast(flash_configuration::clock5); - } else if (cpu_clock_rate >= 100.0_MHz) { - system_controller_reg->flashcfg = - static_cast(flash_configuration::clock5); - system_controller_reg->power_boost = 0b11; - } - - // ========================================================================= - // Step 7. Finally select the sources for each clock - // ========================================================================= - // Set CPU clock the source defined in the configuration - hal::bit_modify(system_controller_reg->cpu_clock_select) - .insert( - static_cast(p_clock_tree.cpu.use_pll0)); - - // Set USB clock the source defined in the configuration - hal::bit_modify(system_controller_reg->usb_clock_select) - .insert(static_cast(p_clock_tree.usb.clock)); - - // Set spifi clock the source defined in the configuration - hal::bit_modify(system_controller_reg->spifi_clock_select) - .insert( - static_cast(p_clock_tree.spifi.clock)); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/dac.cpp b/src/lpc40/dac.cpp deleted file mode 100644 index 849ee98..0000000 --- a/src/lpc40/dac.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -#include "dac_reg.hpp" - -namespace hal::lpc40 { - -dac::dac() -{ - pin dac_pin(0, 26); - dac_pin.analog(true); - dac_pin.dac(true); -} - -void dac::driver_write(float p_percentage) -{ - auto const bits_to_modify = static_cast( - p_percentage * - 1023); // getting the 10 most significant bits to set the value - hal::bit_modify(dac_reg->conversion_register.whole) - .insert(bits_to_modify); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/dac_reg.hpp b/src/lpc40/dac_reg.hpp deleted file mode 100644 index 5c1d5e8..0000000 --- a/src/lpc40/dac_reg.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace hal::lpc40 { -struct dac_registers -{ - union - { - u32 volatile whole; - std::array parts; - } conversion_register; - u32 volatile control; - u32 volatile count_value; -}; -namespace dac_converter_register { -// Holds the value that we want in the register -static constexpr auto value = hal::bit_mask::from<6, 15>(); - -static constexpr auto bias = hal::bit_mask::from<16>(); -} // namespace dac_converter_register -constexpr std::uintptr_t dac_address = 0x4008'C000; -// NOLINTNEXTLINE(performance-no-int-to-ptr) -inline auto* dac_reg = reinterpret_cast(dac_address); - -} // namespace hal::lpc40 diff --git a/src/lpc40/dma.cpp b/src/lpc40/dma.cpp deleted file mode 100644 index c1597bc..0000000 --- a/src/lpc40/dma.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace hal::lpc40 { -namespace { -struct dma_channel -{ - std::uint32_t volatile source_address; - std::uint32_t volatile destination_address; - std::uint32_t volatile linked_list_item; - std::uint32_t volatile control; - std::uint32_t volatile config; -}; - -struct gpdma -{ - std::uint32_t volatile interrupt_status; - std::uint32_t volatile interrupt_terminal_count_status; - std::uint32_t volatile interrupt_terminal_count_clear; - std::uint32_t volatile interrupt_error_status; - std::uint32_t volatile interrupt_error_clear; - std::uint32_t volatile raw_interrupt_terminal_count_status; - std::uint32_t volatile raw_interrupt_error_status; - std::uint32_t volatile enabled_channels; - std::uint32_t volatile SOFTBREQ; - std::uint32_t volatile SOFTSREQ; - std::uint32_t volatile SOFTLBREQ; - std::uint32_t volatile SOFTLSREQ; - std::uint32_t volatile config; - std::uint32_t volatile sync; -}; - -namespace dma_control { -constexpr auto transfer_size = hal::bit_mask::from<0, 11>(); -constexpr auto source_burst_size = hal::bit_mask::from<12, 14>(); -constexpr auto destination_burst_size = hal::bit_mask::from<15, 17>(); -constexpr auto source_transfer_width = hal::bit_mask::from<18, 20>(); -constexpr auto destination_transfer_width = hal::bit_mask::from<21, 23>(); -constexpr auto source_increment = hal::bit_mask::from<26>(); -constexpr auto destination_increment = hal::bit_mask::from<27>(); -constexpr auto enable_terminal_count_interrupt = hal::bit_mask::from<31>(); -} // namespace dma_control - -namespace dma_config { -// config masks -constexpr auto enable = hal::bit_mask::from<0>(); -constexpr auto source_peripheral = hal::bit_mask::from<5, 1>(); -constexpr auto destination_peripheral = hal::bit_mask::from<10, 6>(); -constexpr auto transfer_type = hal::bit_mask::from<13, 11>(); -constexpr auto terminal_count_interrupt_mask = hal::bit_mask::from<15>(); -} // namespace dma_config - -// NOLINTNEXTLINE(performance-no-int-to-ptr) -auto* dma_reg = reinterpret_cast(dma_reg_address); - -constexpr inline std::uintptr_t dma_channel_offset(unsigned p_channel) -{ - return 0x100 + (p_channel * 0x20); -} - -std::array dma_channel_reg{ - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(dma_reg_address + dma_channel_offset(0)), - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(dma_reg_address + dma_channel_offset(1)), - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(dma_reg_address + dma_channel_offset(2)), - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(dma_reg_address + dma_channel_offset(3)), - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(dma_reg_address + dma_channel_offset(4)), - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(dma_reg_address + dma_channel_offset(5)), - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(dma_reg_address + dma_channel_offset(6)), - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(dma_reg_address + dma_channel_offset(7)), -}; - -std::array, dma_channel_count> dma_callbacks{ - hal::cortex_m::default_interrupt_handler, - hal::cortex_m::default_interrupt_handler, - hal::cortex_m::default_interrupt_handler, - hal::cortex_m::default_interrupt_handler, - hal::cortex_m::default_interrupt_handler, - hal::cortex_m::default_interrupt_handler, - hal::cortex_m::default_interrupt_handler, - hal::cortex_m::default_interrupt_handler, -}; - -void handle_dma_interrupt() noexcept -{ - // The zero count from the LSB tells you where the least significant 1 is - // located. This allows the handled DMA interrupt callback to start at 0 and - // end at the last bit. - auto const status = std::countr_zero(dma_reg->interrupt_status); - auto const clear_mask = 1 << status; - - dma_reg->interrupt_terminal_count_clear = clear_mask; - dma_reg->interrupt_error_clear = clear_mask; - - // Call this channel's callback - dma_callbacks[status](); -} - -void initialize_dma() -{ - if (is_on(peripheral::gpdma)) { - return; - } - - power_on(peripheral::gpdma); - initialize_interrupts(); - hal::cortex_m::enable_interrupt(irq::dma, handle_dma_interrupt); - // Enable DMA & use default AHB endianness - dma_reg->config = 1; -} - -hal::atomic_spin_lock dma_spin_lock; -hal::basic_lock* dma_lock = &dma_spin_lock; -} // namespace - -void set_dma_lock(hal::basic_lock& p_lock) -{ - dma_lock = &p_lock; -} - -void setup_dma_transfer( - dma const& p_configuration, - hal::callback p_interrupt_callback) // NOLINT -{ - auto const config_value = - hal::bit_value() - .insert( - hal::value(p_configuration.source_peripheral)) - .insert( - hal::value(p_configuration.destination_peripheral)) - .set() - .insert( - hal::value(p_configuration.transfer_type)) - .set() - .to(); - - auto const control_value = - hal::bit_value() - .insert(p_configuration.length) - .insert( - hal::value(p_configuration.source_burst_size)) - .insert( - hal::value(p_configuration.destination_transfer_width)) - .insert( - hal::value(p_configuration.source_transfer_width)) - .insert( - hal::value(p_configuration.destination_transfer_width)) - .insert(p_configuration.source_increment) - .insert( - p_configuration.destination_increment) - .set() - .to(); - - std::lock_guard take_dma_lock(*dma_lock); - - initialize_dma(); - - // Busy wait until a channel is available - while (true) { - // Count the number of 1s until you reach a zero. That zero will be the - // available channel. If that zero is 8, then all 8 channels are currently - // in use and cannot service this request. - auto const available_channel = std::countr_one(dma_reg->enabled_channels); - - if (available_channel < 8) { - // Copy callback to the callbacks - dma_callbacks[available_channel] = p_interrupt_callback; - - // Clear previous interrupts on this channel, if there were any - dma_reg->interrupt_terminal_count_clear = 1 << available_channel; - dma_reg->interrupt_error_clear = 1 << available_channel; - - auto* dma_channel = dma_channel_reg[available_channel]; - - dma_channel->source_address = - reinterpret_cast(p_configuration.source); - dma_channel->destination_address = - reinterpret_cast(p_configuration.destination); - dma_channel->control = control_value; - // This will start the dma transfer - dma_channel->config = config_value; - break; - } - } -} -} // namespace hal::lpc40 diff --git a/src/lpc40/dma_spi.cpp b/src/lpc40/dma_spi.cpp deleted file mode 100644 index a6f3864..0000000 --- a/src/lpc40/dma_spi.cpp +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "spi_reg.hpp" - -namespace hal::lpc40 { -namespace { -inline spi_reg_t* get_spi_reg(peripheral p_id) -{ - switch (p_id) { - case peripheral::ssp0: - return spi_reg0; - case peripheral::ssp1: - return spi_reg1; - case peripheral::ssp2: - default: - return spi_reg2; - } -} - -inline bool still_sending(spi_reg_t* p_reg) -{ - return bit_extract(p_reg->sr); -} - -struct dma_peripheral_pair -{ - dma_peripheral rx; - dma_peripheral tx; -}; - -inline dma_peripheral_pair to_dma_peripheral_pair(dma_spi::bus_info& p_bus_info) -{ - switch (p_bus_info.peripheral_id) { - case peripheral::ssp0: - return { - .rx = dma_peripheral::spi0_rx_and_timer1_match1, - .tx = dma_peripheral::spi0_tx_and_timer1_match0, - }; - case peripheral::ssp1: - return { - .rx = dma_peripheral::spi1_rx_and_timer2_match1, - .tx = dma_peripheral::spi1_tx_and_timer2_match0, - }; - case peripheral::ssp2: - return { - .rx = dma_peripheral::spi2_rx_and_i2s_channel_1, - .tx = dma_peripheral::spi2_tx_and_i2s_channel_0, - }; - default: - hal::safe_throw(hal::operation_not_supported(&p_bus_info)); - break; - } -} -} // namespace - -dma_spi::dma_spi(std::uint8_t p_bus_number, - hal::io_waiter& p_waiter, - dma_spi::settings const& p_settings) - : m_io_waiter(&p_waiter) - , m_bus{} -{ - // UM10562: Chapter 7: LPC408x/407x I/O configuration page 13 - if (p_bus_number == 0) { - m_bus = { - .peripheral_id = peripheral::ssp0, - .clock = pin(0, 15), - .data_out = pin(0, 18), - .data_in = pin(0, 17), - .clock_function = 0b010, - .data_out_function = 0b010, - .data_in_function = 0b010, - }; - } else if (p_bus_number == 1) { - m_bus = { - .peripheral_id = peripheral::ssp1, - .clock = pin(0, 7), - .data_out = pin(0, 9), - .data_in = pin(0, 8), - .clock_function = 0b010, - .data_out_function = 0b010, - .data_in_function = 0b010, - }; - } else if (p_bus_number == 2) { - m_bus = { - .peripheral_id = peripheral::ssp2, - .clock = pin(1, 0), - .data_out = pin(1, 1), - .data_in = pin(1, 4), - .clock_function = 0b100, - .data_out_function = 0b100, - .data_in_function = 0b100, - }; - } else { - // "Supported spi busses are 0, 1, and 2!"; - hal::safe_throw(hal::operation_not_supported(this)); - } - - dma_spi::driver_configure(p_settings); -} // namespace hal::lpc40 - -dma_spi::~dma_spi() -{ - power_off(m_bus.peripheral_id); -} - -dma_spi::dma_spi(bus_info p_bus) - : m_bus(p_bus) -{ - if (p_bus.peripheral_id != peripheral::ssp0 && - p_bus.peripheral_id != peripheral::ssp1 && - p_bus.peripheral_id != peripheral::ssp2) { - hal::safe_throw(hal::operation_not_supported(this)); - } -} - -void dma_spi::driver_configure(settings const& p_settings) -{ - constexpr uint8_t spi_format_code = 0b00; - - auto* reg = get_spi_reg(m_bus.peripheral_id); - - // Power up peripheral - power_on(m_bus.peripheral_id); - - // Set SSP frame format to SPI - bit_modify(reg->cr0).insert(spi_format_code); - - // Set SPI to master mode by clearing - bit_modify(reg->cr1).clear(); - - // Setup operating frequency - auto const input_clock = get_frequency(m_bus.peripheral_id); - auto const clock_divider = input_clock / p_settings.clock_rate; - auto const prescaler = static_cast(clock_divider); - auto const prescaler_low = static_cast(prescaler & 0xFF); - auto const prescaler_high = static_cast(prescaler >> 8); - // Store lower half of prescalar in clock prescalar register - reg->cpsr = prescaler_low; - // Store upper 8 bit half of the prescalar in control register 0 - bit_modify(reg->cr0).insert(prescaler_high); - - // Set clock modes & bit size - // - // NOTE: In UM10562 page 611, you will see that DSS (Data Size Select) is - // equal to the bit transfer minus 1. So we can add 3 to our DataSize enum - // to get the appropriate transfer code. - constexpr std::uint8_t size_code_8bit = 0b111; - - bit_modify(reg->cr0) - .insert(p_settings.clock_idles_high) - .insert( - p_settings.data_valid_on_trailing_edge) - .insert(size_code_8bit); - - // Initialize SSP pins - m_bus.clock.function(m_bus.clock_function) - .analog(false) - .open_drain(false) - .resistor(pin_resistor::none); - m_bus.data_in.function(m_bus.data_in_function) - .analog(false) - .open_drain(false) - .resistor(pin_resistor::none); - m_bus.data_out.function(m_bus.data_out_function) - .analog(false) - .open_drain(false) - .resistor(pin_resistor::none); - - // Enable DMA - bit_modify(reg->dmacr) - .set() - .set(); - - // Enable SSP - bit_modify(reg->cr1).set(); -} - -void dma_spi::driver_transfer(std::span p_data_out, - std::span p_data_in, - hal::byte p_filler) -{ - auto& reg = *get_spi_reg(m_bus.peripheral_id); - - auto const dma_peripheral_pair = to_dma_peripheral_pair(m_bus); - auto const min = std::min(p_data_in.size(), p_data_out.size()); - - dma receive_configuration = { - .source = ®.dr, - .destination = p_data_in.data(), - .length = min, - .source_increment = false, - .destination_increment = true, - .transfer_type = dma_transfer_type::peripheral_to_memory, - .source_transfer_width = dma_transfer_width::bit_8, - .destination_transfer_width = dma_transfer_width::bit_8, - .source_peripheral = dma_peripheral_pair.rx, - .destination_peripheral = dma_peripheral::memory_or_timer0_match0, - .source_burst_size = dma_burst_size::bytes_1, - .destination_burst_size = dma_burst_size::bytes_1, - }; - - dma transmit_configuration = { - .source = p_data_out.data(), - .destination = ®.dr, - .length = min, - .source_increment = true, - .destination_increment = false, - .transfer_type = dma_transfer_type ::memory_to_peripheral, - .source_transfer_width = dma_transfer_width::bit_8, - .destination_transfer_width = dma_transfer_width::bit_8, - .source_peripheral = dma_peripheral::memory_or_timer0_match0, - .destination_peripheral = dma_peripheral_pair.tx, - .source_burst_size = dma_burst_size::bytes_1, - .destination_burst_size = dma_burst_size::bytes_1, - }; - - bool rx_done = false; - bool tx_done = false; - - auto receive_completion_handler = [this, &rx_done]() { - rx_done = true; - m_io_waiter->resume(); - }; - - auto transmit_completion_handler = [this, &tx_done]() { - tx_done = true; - m_io_waiter->resume(); - }; - - if (receive_configuration.length == 0) { - rx_done = true; - } else { - hal::lpc40::setup_dma_transfer(receive_configuration, - receive_completion_handler); - } - - if (transmit_configuration.length == 0) { - tx_done = true; - } else { - hal::lpc40::setup_dma_transfer(transmit_configuration, - transmit_completion_handler); - } - - while (not(rx_done && tx_done)) { - m_io_waiter->wait(); - } - - rx_done = false; - tx_done = false; - - if (p_data_in.size() == p_data_out.size()) { - return; - } else if (p_data_in.size() > p_data_out.size()) { - // In this case, we have more bytes we want to read back than bytes we want - // to transmit. - p_data_in = p_data_in.subspan(min); - receive_configuration.destination = p_data_in.data(); - receive_configuration.length = p_data_in.size(); - hal::lpc40::setup_dma_transfer(receive_configuration, - receive_completion_handler); - - transmit_configuration.source = &p_filler; - transmit_configuration.length = p_data_in.size(); - transmit_configuration.source_increment = false; - hal::lpc40::setup_dma_transfer(transmit_configuration, - transmit_completion_handler); - } else { - // In this case, we have more bytes we want to transmit than we want to - // read. So skip the receive dma transfer... - rx_done = true; - p_data_out = p_data_out.subspan(min); - transmit_configuration.source = p_data_out.data(); - transmit_configuration.length = p_data_out.size(); - hal::lpc40::setup_dma_transfer(transmit_configuration, - transmit_completion_handler); - } - - while (not(rx_done && tx_done)) { - m_io_waiter->wait(); - } - - // Stay in the loop until the bus is no longer active - while (still_sending(®)) { - m_io_waiter->wait(); - } -} -} // namespace hal::lpc40 diff --git a/src/lpc40/gpio_reg.hpp b/src/lpc40/gpio_reg.hpp deleted file mode 100644 index 7fa1daf..0000000 --- a/src/lpc40/gpio_reg.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include -#include - -namespace hal::lpc40 { -/// gpio peripheral register map -struct lpc_gpio_t -{ - /// Offset: 0x000 Determine pin direction (0 == Input, 1 = Output) (R/W) - std::uint32_t volatile direction; - /// Offset: 0x004 - 0x00C - std::array reserved0; - /// Offset: 0x010 (R/W) - std::uint32_t volatile mask; - /// Offset: 0x014 Pin status and output control (R/W) - std::uint32_t volatile pin; - /// Offset: 0x018 Write 1 to this to set output pin as 1 (HIGH voltage) (R/W) - std::uint32_t volatile set; - /// Offset: 0x01C Write 1 to this to Set output pin to 0 (LOW voltage) (R/W) - std::uint32_t volatile clear; -}; - -inline constexpr intptr_t ahb_base = 0x20080000UL; - -// NOLINTBEGIN(performance-no-int-to-ptr) -inline std::array gpio_reg{ - reinterpret_cast(ahb_base + 0x18000), - reinterpret_cast(ahb_base + 0x18020), - reinterpret_cast(ahb_base + 0x18040), - reinterpret_cast(ahb_base + 0x18060), - reinterpret_cast(ahb_base + 0x18080), - reinterpret_cast(ahb_base + 0x180a0), -}; -// NOLINTEND(performance-no-int-to-ptr) - -inline constexpr bit_mask pin_mask(std::uint8_t p_pin) -{ - return bit_mask{ .position = p_pin, .width = 1 }; -} -} // namespace hal::lpc40 diff --git a/src/lpc40/i2c.cpp b/src/lpc40/i2c.cpp deleted file mode 100644 index 41e7db7..0000000 --- a/src/lpc40/i2c.cpp +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "i2c_reg.hpp" - -namespace hal::lpc40 { -i2c_reg_t* get_i2c_reg(peripheral p_id) -{ - switch (p_id) { - case peripheral::i2c0: - return i2c_reg0; - case peripheral::i2c1: - return i2c_reg1; - case peripheral::i2c2: - default: - return i2c_reg2; - } -} - -void disable(i2c::bus_info& p_info) -{ - auto* reg = get_i2c_reg(p_info.peripheral_id); - - // Disable i2c interface - reg->control_clear = i2c_control::interface_enable; - - // Enable interrupt service routine. - cortex_m::disable_interrupt(p_info.irq_number); -} - -void i2c::interrupt() -{ - auto* reg = get_i2c_reg(m_bus.peripheral_id); - - auto state = i2c_host_state(reg->stat); - auto& data = reg->dat; - uint32_t clear_mask = 0; - uint32_t set_mask = 0; - bool transaction_finished = false; - - switch (state) { - case i2c_host_state::bus_error: { - m_status = error_state::io_error; - set_mask = i2c_control::assert_acknowledge | i2c_control::stop; - break; - } - case i2c_host_state::start_condition: - case i2c_host_state::repeated_start: { - if (m_write_iterator != m_write_end) { - data = to_8_bit_address(m_address, i2c_operation::write); - } else { - data = to_8_bit_address(m_address, i2c_operation::read); - } - break; - } - case i2c_host_state::peripheral_address_write_sent_received_ack: { - clear_mask = i2c_control::start; - if (m_write_iterator == m_write_end) { - transaction_finished = true; - set_mask = i2c_control::stop; - } else { - data = *m_write_iterator++; - } - break; - } - case i2c_host_state::peripheral_address_write_sent_received_nack: { - clear_mask = i2c_control::start; - transaction_finished = true; - m_status = error_state::no_such_device; - set_mask = i2c_control::stop; - break; - } - case i2c_host_state::transmitted_data_received_ack: { - if (m_write_iterator == m_write_end) { - if (m_read_iterator != m_read_end) { - set_mask = i2c_control::start; - } else { - transaction_finished = true; - set_mask = i2c_control::stop; - } - } else { - data = *(m_write_iterator++); - } - break; - } - case i2c_host_state::transmitted_data_received_nack: { - transaction_finished = true; - set_mask = i2c_control::stop; - break; - } - case i2c_host_state::arbitration_lost: { - set_mask = i2c_control::start; - break; - } - case i2c_host_state::peripheral_address_read_sent_received_ack: { - clear_mask = i2c_control::start; - if (m_read_iterator == m_read_end) { - set_mask = i2c_control::stop; - } - // If we only want 1 byte, make sure to nack that byte - else if (m_read_iterator + 1 == m_read_end) { - clear_mask |= i2c_control::assert_acknowledge; - } - // If we want more then 1 byte, make sure to ack the first byte - else { - set_mask = i2c_control::assert_acknowledge; - } - break; - } - case i2c_host_state::peripheral_address_read_sent_received_nack: { - clear_mask = i2c_control::start; - m_status = error_state::no_such_device; - transaction_finished = true; - set_mask = i2c_control::stop; - break; - } - case i2c_host_state::received_data_transmitted_ack: { - if (m_read_iterator != m_read_end) { - *m_read_iterator++ = static_cast(data); - } - // Check if the buffer has been exhausted - if (m_read_iterator + 1 == m_read_end) { - // Next state will be `received_data_transmitted_nack` - clear_mask = i2c_control::assert_acknowledge; - } else { - set_mask = i2c_control::assert_acknowledge; - } - break; - } - case i2c_host_state::received_data_transmitted_nack: { - transaction_finished = true; - if (m_read_iterator != m_read_end) { - *m_read_iterator++ = static_cast(data); - } - set_mask = i2c_control::stop; - break; - } - case i2c_host_state::do_nothing: { - break; - } - default: { - clear_mask = i2c_control::stop; - break; - } - } - - clear_mask |= i2c_control::interrupt; - - reg->control_set = set_mask; - reg->control_clear = clear_mask; - - if (transaction_finished) { - m_busy = false; - } -} - -i2c::i2c(std::uint8_t p_bus_number, - i2c::settings const& p_settings, - hal::io_waiter& p_waiter) - : m_waiter(&p_waiter) -{ - // UM10562: Chapter 7: LPC408x/407x I/O configuration page 13 - switch (p_bus_number) { - case 0: - /// Definition for i2c bus 0 for LPC40xx. - m_bus = { - .peripheral_id = peripheral::i2c0, - .irq_number = irq::i2c0, - .sda = pin(1, 30), - .sda_function = 0b100, - .scl = pin(1, 31), - .scl_function = 0b100, - }; - break; - case 1: - /// Definition for i2c bus 1 for LPC40xx. - m_bus = { - .peripheral_id = peripheral::i2c1, - .irq_number = irq::i2c1, - .sda = pin(0, 0), - .sda_function = 0b011, - .scl = pin(0, 1), - .scl_function = 0b011, - }; - break; - case 2: - /// Definition for i2c bus 2 for LPC40xx. - m_bus = { - .peripheral_id = peripheral::i2c2, - .irq_number = irq::i2c2, - .sda = pin(0, 10), - .sda_function = 0b010, - .scl = pin(0, 11), - .scl_function = 0b010, - }; - break; - default: { - safe_throw(hal::operation_not_supported(this)); - break; - } - } - - lpc40::initialize_interrupts(); - i2c::driver_configure(p_settings); -} - -i2c::~i2c() -{ - disable(m_bus); -} - -i2c::i2c(bus_info const& p_bus, - i2c::settings const& p_settings, - hal::io_waiter& p_waiter) - : m_bus(p_bus) - , m_waiter(&p_waiter) -{ - initialize_interrupts(); - i2c::driver_configure(p_settings); -} - -void i2c::driver_configure(settings const& p_settings) -{ - auto* reg = get_i2c_reg(m_bus.peripheral_id); - - // Setup i2c operating frequency - auto const input_clock = get_frequency(m_bus.peripheral_id); - auto const clock_divider = input_clock / p_settings.clock_rate; - auto const high_side_clocks = clock_divider * m_bus.duty_cycle; - auto const low_side_clocks = clock_divider - high_side_clocks; - - if (low_side_clocks < 1.0f || high_side_clocks < 1.0f) { - safe_throw(hal::operation_not_supported(this)); - } - - // Power on peripheral - power_on(m_bus.peripheral_id); - - // Setup pins for SDA and SCL - pin(m_bus.sda) - .function(m_bus.sda_function) - .resistor(pin_resistor::none) - .open_drain(true); - - pin(m_bus.scl) - .function(m_bus.scl_function) - .resistor(pin_resistor::none) - .open_drain(true); - - using high_t = std::remove_volatile_tduty_cycle_high)>; - using low_t = std::remove_volatile_tduty_cycle_low)>; - - reg->duty_cycle_high = static_cast(high_side_clocks); - reg->duty_cycle_low = static_cast(low_side_clocks); - - // Clear all transmission flags - reg->control_clear = i2c_control::assert_acknowledge | i2c_control::start | - i2c_control::stop | i2c_control::interrupt; - // Enable i2c interface - reg->control_set = i2c_control::interface_enable; - - setup_interrupt(); -} - -void i2c::setup_interrupt() -{ - // Create a lambda to call the interrupt() method - auto isr = [this]() { interrupt(); }; - - // A pointer to save the static_callable isr address to. - cortex_m::interrupt_pointer handler; - - switch (m_bus.irq_number) { - case irq::i2c0: - handler = static_callable(isr).get_handler(); - break; - case irq::i2c1: - handler = static_callable(isr).get_handler(); - break; - case irq::i2c2: - default: - handler = static_callable(isr).get_handler(); - break; - } - - // Enable interrupt service routine. - cortex_m::enable_interrupt(m_bus.irq_number, handler); -} - -void i2c::driver_transaction(hal::byte p_address, - std::span p_data_out, - std::span p_data_in, - hal::function_ref p_timeout) -{ - auto* reg = get_i2c_reg(m_bus.peripheral_id); - - m_status = error_state::no_error; - m_address = p_address; - m_write_iterator = p_data_out.begin(); - m_write_end = p_data_out.end(); - m_read_iterator = p_data_in.begin(); - m_read_end = p_data_in.end(); - m_busy = true; - - // Start the transaction - reg->control_set = i2c_control::start; - - // i2c::interrupt() will set this to false when the transaction has finished. - while (m_busy) { - try { - p_timeout(); - m_waiter->wait(); - } catch (...) { - // The expected exception is hal::timed_out, but it could be something - // else. Let rethrow the exception so the caller handle it. - // A better option here would be to use a std::scope_failure handler. - reg->control_set = i2c_control::stop; - throw; - } - } - - switch (m_status) { - case error_state::no_error: { - break; - } - case error_state::no_such_device: { - safe_throw(hal::no_such_device(m_address, this)); - break; - } - case error_state::io_error: { - safe_throw(hal::io_error(this)); - break; - } - case error_state::arbitration_lost: { - safe_throw(hal::resource_unavailable_try_again(this)); - break; - } - }; -} -} // namespace hal::lpc40 diff --git a/src/lpc40/i2c_reg.hpp b/src/lpc40/i2c_reg.hpp deleted file mode 100644 index 445c565..0000000 --- a/src/lpc40/i2c_reg.hpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -namespace hal::lpc40 { -struct i2c_reg_t -{ - /// Offset: 0x000 i2c control set register (r/w) - u32 volatile control_set; - /// offset: 0x004 i2c status register (r/ ) - u32 const volatile stat; - /// offset: 0x008 i2c data register (r/w) - u32 volatile dat; - /// offset: 0x00c i2c peripheral address register 0 (r/w) - u32 volatile address0; - /// offset: 0x010 sch duty cycle register high half word (r/w) - u32 volatile duty_cycle_high; - /// offset: 0x014 scl duty cycle register low half word (r/w) - u32 volatile duty_cycle_low; - /// offset: 0x018 i2c control clear register ( /w) - u32 volatile control_clear; - /// offset: 0x01c monitor mode control register (r/w) - u32 volatile monitor_mode_control; - /// offset: 0x020 i2c peripheral address register 1 (r/w) - u32 volatile address1; - /// offset: 0x024 i2c peripheral address register 2 (r/w) - u32 volatile address2; - /// offset: 0x028 i2c peripheral address register 3 (r/w) - u32 volatile address3; - /// offset: 0x02c data buffer register ( /w) - u32 const volatile data_buffer; - /// offset: 0x030 i2c peripheral address mask register 0 (r/w) - u32 volatile mask0; - /// offset: 0x034 i2c peripheral address mask register 1 (r/w) - u32 volatile mask1; - /// offset: 0x038 i2c peripheral address mask register 2 (r/w) - u32 volatile mask2; - /// offset: 0x03c i2c peripheral address mask register 3 (r/w) - u32 volatile mask3; -}; - -/// lpc40xx i2c peripheral control register flags -namespace i2c_control { -// AA -static constexpr auto assert_acknowledge = 1 << 2; -// SI -static constexpr auto interrupt = 1 << 3; -// STO -static constexpr auto stop = 1 << 4; -// STA -static constexpr auto start = 1 << 5; -// I2EN -static constexpr auto interface_enable = 1 << 6; -}; // namespace i2c_control - -/// lpc40xx i2c peripheral state numbers -enum class i2c_host_state : u8 -{ - bus_error = 0x00, - start_condition = 0x08, - repeated_start = 0x10, - peripheral_address_write_sent_received_ack = 0x18, - peripheral_address_write_sent_received_nack = 0x20, - transmitted_data_received_ack = 0x28, - transmitted_data_received_nack = 0x30, - arbitration_lost = 0x38, - peripheral_address_read_sent_received_ack = 0x40, - peripheral_address_read_sent_received_nack = 0x48, - received_data_transmitted_ack = 0x50, - received_data_transmitted_nack = 0x58, - own_address_received = 0xA0, - do_nothing = 0xF8 -}; - -inline i2c_reg_t* i2c_reg0 = reinterpret_cast(0x4001'C000); -inline i2c_reg_t* i2c_reg1 = reinterpret_cast(0x4005'C000); -inline i2c_reg_t* i2c_reg2 = reinterpret_cast(0x400A'0000); -} // namespace hal::lpc40 diff --git a/src/lpc40/input_pin.cpp b/src/lpc40/input_pin.cpp deleted file mode 100644 index 6e78dee..0000000 --- a/src/lpc40/input_pin.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include -#include -#include -#include -#include - -#include "gpio_reg.hpp" - -namespace hal::lpc40 { -input_pin::input_pin(std::uint8_t p_port, // NOLINT - std::uint8_t p_pin, - settings const& p_settings) // NOLINT - : m_port(p_port) - , m_pin(p_pin) -{ - configure(p_settings); -} - -void input_pin::driver_configure(settings const& p_settings) -{ - power_on(peripheral::gpio); - - bit_modify(gpio_reg[m_port]->direction).clear(pin_mask(m_pin)); - - // Pin mask is used to control which pins are updated or not through the - // pin, set, and clear registers. The mask bit corresponding to the pin must - // be set to 0 for the pin to be enabled. - bit_modify(gpio_reg[m_port]->mask).clear(pin_mask(m_pin)); - - pin(m_port, m_pin) - .function(0) - .dac(false) - .analog(false) - .open_drain(false) - .resistor(p_settings.resistor); -} - -bool input_pin::driver_level() -{ - auto pin_value = bit_extract(pin_mask(m_pin), gpio_reg[m_port]->pin); - return static_cast(pin_value); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/interrupt.cpp b/src/lpc40/interrupt.cpp deleted file mode 100644 index dd15631..0000000 --- a/src/lpc40/interrupt.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include - -namespace hal::lpc40 { -void initialize_interrupts() -{ - hal::cortex_m::initialize_interrupts(); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/interrupt_pin.cpp b/src/lpc40/interrupt_pin.cpp deleted file mode 100644 index 20a8905..0000000 --- a/src/lpc40/interrupt_pin.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include -#include -#include - -#include "gpio_reg.hpp" -#include "interrupt_pin_reg.hpp" - -namespace hal::lpc40 { - -void interrupt_pin_handler() -{ - std::uint32_t triggered_port = interrupt_pin_reg->status >> 2; - std::uint32_t triggered_pin = 0; - bit_mask triggered_pin_mask; - std::uint32_t status = 0; - - if (triggered_port == 0) { - // To keep the number of handler functions to a minimum, this library does - // not support separate handlers for rising and falling edges. Therefore - // it does not matter if a rising or falling edge triggered this - // interrupt. OR both status together and clear them together below. - status = interrupt_pin_reg->raising_status_port0 | - interrupt_pin_reg->falling_status_port0; - } else { - // Same documentation as the port 0 case but with port 2 here. - status = interrupt_pin_reg->raising_status_port2 | - interrupt_pin_reg->falling_status_port2; - } - - // Figure out which bit triggered this interrupt by checking the number of - // zeros starting from the least significant bit. If there is 5 zeros, - // then the next bit must be a 1 and thus, the pin that set off this - // interrupt is pin 5. If there are 0 zeros, then the first bit must be - // set to a 1 and thus pin 0 is what set off this interrupt. And so on for - // all other bits. - triggered_pin = static_cast(std::countr_zero(status)); - triggered_pin_mask = bit_mask::from(triggered_pin); - - if (triggered_port == 0) { - // Clear interrupt flag on port 0. This is important as not doing this - // will result in this interrupt being repeatedly called. - bit_modify(interrupt_pin_reg->clear_interrupt_port0) - .set(triggered_pin_mask); - } else { - bit_modify(interrupt_pin_reg->clear_interrupt_port2) - .set(triggered_pin_mask); - } - - bool pin_level = - bit_extract(triggered_pin_mask, gpio_reg[triggered_port]->pin); - - interrupt_pin_handlers[triggered_port][triggered_pin](pin_level); -} - -interrupt_pin::interrupt_pin(std::uint8_t p_port, // NOLINT - std::uint8_t p_pin, - settings const& p_settings) - : m_port(p_port) - , m_pin(p_pin) -{ - initialize_interrupts(); - interrupt_pin::driver_configure(p_settings); -} - -interrupt_pin::~interrupt_pin() -{ - if (m_port == 0) { - bit_modify(interrupt_pin_reg->enable_raising_port0).clear(pin_mask(m_pin)); - bit_modify(interrupt_pin_reg->enable_falling_port0).clear(pin_mask(m_pin)); - } else if (m_port == 2) { - bit_modify(interrupt_pin_reg->enable_raising_port2).clear(pin_mask(m_pin)); - bit_modify(interrupt_pin_reg->enable_falling_port2).clear(pin_mask(m_pin)); - } -} - -void interrupt_pin::driver_configure(settings const& p_settings) -{ - // Set pin as input - bit_modify(gpio_reg[m_port]->direction).clear(pin_mask(m_pin)); - - // Configure pin to use gpio function, use setting resistor and set the rest - // to false. - pin(m_port, m_pin) - .function(0) - .dac(false) - .analog(false) - .open_drain(false) - .resistor(p_settings.resistor); - - // Enable interrupt for gpio and use interrupt handler as our handler. - cortex_m::enable_interrupt(irq::gpio, interrupt_pin_handler); - - if (p_settings.trigger == trigger_edge::both || - p_settings.trigger == trigger_edge::rising) { - if (m_port == 0) { - bit_modify(interrupt_pin_reg->enable_raising_port0).set(pin_mask(m_pin)); - } else if (m_port == 2) { - bit_modify(interrupt_pin_reg->enable_raising_port2).set(pin_mask(m_pin)); - } - } - - if (p_settings.trigger == trigger_edge::both || - p_settings.trigger == trigger_edge::falling) { - if (m_port == 0) { - bit_modify(interrupt_pin_reg->enable_falling_port0).set(pin_mask(m_pin)); - } else if (m_port == 2) { - bit_modify(interrupt_pin_reg->enable_falling_port2).set(pin_mask(m_pin)); - } - } -} - -void interrupt_pin::driver_on_trigger(hal::callback p_callback) -{ - if (m_port == 0) { - interrupt_pin_handlers[0][m_pin] = p_callback; - } else { - interrupt_pin_handlers[1][m_pin] = p_callback; - } -} -} // namespace hal::lpc40 diff --git a/src/lpc40/interrupt_pin_reg.hpp b/src/lpc40/interrupt_pin_reg.hpp deleted file mode 100644 index 51607a6..0000000 --- a/src/lpc40/interrupt_pin_reg.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include - -namespace hal::lpc40 { -/// Matrix of gpio interrupt service routine handlers 32 x 2. Matrix does not -/// need to be initialized at startup to work because the only entries that -/// will be accessed are the entries that have been setup via -/// attach_interrupt. -inline std::array, 32>, 2> - interrupt_pin_handlers{}; - -/// interrupt register map -struct interrupt_pin_reg_t -{ - /// Offset: 0x080 GPIO overall Interrupt Status (RO) - uint32_t const volatile status; - /// Offset: 0x084 GPIO Interrupt Status for Rising edge for Port 0 (RO) - uint32_t const volatile raising_status_port0; - /// Offset: 0x088 GPIO Interrupt Status for Falling edge for Port 0 (RO) - uint32_t const volatile falling_status_port0; - /// Offset: 0x08C (WO) - uint32_t volatile clear_interrupt_port0; - /// Offset: 0x090 (R/W) - uint32_t volatile enable_raising_port0; - /// Offset: 0x094 (R/W) - uint32_t volatile enable_falling_port0; - /// Offset: 0x098 - 0x0A0 - std::array reserved0; - /// Offset: 0x0A4 (RO) - uint32_t const volatile raising_status_port2; - /// Offset: 0x0A8 (RO) - uint32_t const volatile falling_status_port2; - /// Offset: 0x0AC (WO) - uint32_t volatile clear_interrupt_port2; - /// Offset: 0x0B0 (R/W) - uint32_t volatile enable_raising_port2; - /// Offset: 0x0B4 (R/W) - uint32_t volatile enable_falling_port2; -}; - -inline interrupt_pin_reg_t* interrupt_pin_reg = - reinterpret_cast(0x4002'8080); -} // namespace hal::lpc40 diff --git a/src/lpc40/output_pin.cpp b/src/lpc40/output_pin.cpp deleted file mode 100644 index 0f9d348..0000000 --- a/src/lpc40/output_pin.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include - -#include "gpio_reg.hpp" - -namespace hal::lpc40 { -output_pin::output_pin(std::uint8_t p_port, // NOLINT - std::uint8_t p_pin, - output_pin::settings const& p_settings) - : m_port(p_port) - , m_pin(p_pin) -{ - configure(p_settings); // NOLINT -} - -void output_pin::driver_configure(settings const& p_settings) -{ - bit_modify(gpio_reg[m_port]->direction).set(pin_mask(m_pin)); - - pin(m_port, m_pin) - .function(0) - .dac(false) - .analog(false) - .open_drain(p_settings.open_drain) - .resistor(p_settings.resistor); -} - -void output_pin::driver_level(bool p_high) -{ - if (p_high) { - bit_modify(gpio_reg[m_port]->pin).set(pin_mask(m_pin)); - } else { - bit_modify(gpio_reg[m_port]->pin).clear(pin_mask(m_pin)); - } -} - -bool output_pin::driver_level() -{ - auto pin_value = bit_extract(pin_mask(m_pin), gpio_reg[m_port]->pin); - - return static_cast(pin_value); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/pin.cpp b/src/lpc40/pin.cpp deleted file mode 100644 index aa0e588..0000000 --- a/src/lpc40/pin.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include -#include - -#include "pin_reg.hpp" - -namespace hal::lpc40 { -pin const& pin::function(uint8_t p_function_code) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(p_function_code); - return *this; -} - -pin const& pin::resistor(hal::pin_resistor p_resistor) const -{ - uint8_t resistor_code = 0; - switch (p_resistor) { - case hal::pin_resistor::none: - resistor_code = 0b00; - break; - case hal::pin_resistor::pull_down: - resistor_code = 0b01; - break; - case hal::pin_resistor::pull_up: - resistor_code = 0b10; - break; - } - // The pin resistor enumeration matches the values for the LPC40xx so simply - // cast the enum to an int and this will work. - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(resistor_code); - return *this; -} - -pin const& pin::hysteresis(bool p_enable) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(p_enable); - return *this; -} - -pin const& pin::input_invert(bool p_enable) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(p_enable); - return *this; -} - -pin const& pin::analog(bool p_enable) const -{ - bool is_digital = !p_enable; - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(is_digital); - return *this; -} - -pin const& pin::digital_filter(bool p_enable) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(p_enable); - return *this; -} - -pin const& pin::highspeed_i2c(bool p_enable) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(p_enable); - return *this; -} - -pin const& pin::high_slew_rate(bool p_enable) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]).insert(p_enable); - return *this; -} - -pin const& pin::i2c_high_current(bool p_enable) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(p_enable); - return *this; -} - -pin const& pin::open_drain(bool p_enable) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(p_enable); - return *this; -} - -pin const& pin::dac(bool p_enable) const -{ - hal::bit_modify(pin_map->matrix[m_port][m_pin]) - .insert(p_enable); - return *this; -} -} // namespace hal::lpc40 diff --git a/src/lpc40/pin_reg.hpp b/src/lpc40/pin_reg.hpp deleted file mode 100644 index 0ea0766..0000000 --- a/src/lpc40/pin_reg.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include -#include - -namespace hal::lpc40 { -/// Pin map table for maping pins and ports to registers. -struct pin_map_t -{ - /// Register matrix that maps against the 6 ports and the 32 pins per port - std::array, 6> matrix; -}; - -/// The address of the IO connect peripheral -static constexpr intptr_t io_connect_address = 0x40000000UL + 0x2C000; - -// Source: "UM10562 LPC408x/407x User manual" table 83 page 132 -/// bit_mask for setting pin mux function code. -static constexpr auto pin_function = hal::bit_mask::from<0, 2>(); - -/// bit_mask for setting resistor mode of pin. -static constexpr auto pin_resistor = hal::bit_mask::from<3, 4>(); - -/// bit_mask for setting pin hysteresis mode. -static constexpr auto pin_hysteresis = hal::bit_mask::from<5>(); - -/// bit_mask for setting inputs as active low or active high. This will behave -/// badly if the pin is set to a mode that is an output or set to analog. See -/// user manual for more details. -static constexpr auto pin_input_invert = hal::bit_mask::from<6>(); - -/// bit_mask for setting a pin to analog mode. -static constexpr auto pin_analog_digital_mode = hal::bit_mask::from<7>(); - -/// bit_mask for enabling/disabling digital filter. This can be used to -/// ignore/reject noise, reflections, or signal bounce (from something like a -/// switch). -static constexpr auto pin_digital_filter = hal::bit_mask::from<8>(); - -/// bit_mask to enable/disable high speed I2C mode -static constexpr auto pin_i2c_highspeed = hal::bit_mask::from<8>(); - -/// bit_mask to change the slew rate of signal transitions for outputs for a -/// pin. -static constexpr auto pin_slew = hal::bit_mask::from<9>(); - -/// bit_mask to enable I2C high current drain. This can allow for even faster -/// I2C communications, as well as allow for more devices on the bus. -static constexpr auto pin_i2c_high_current = hal::bit_mask::from<9>(); - -/// bit_mask to enable/disable open drain mode. -static constexpr auto pin_open_drain = hal::bit_mask::from<10>(); - -/// bit_mask for enabling/disabling digital to analog pin mode. -static constexpr auto pin_dac_enable = hal::bit_mask::from<16>(); - -// NOLINTBEGIN(performance-no-int-to-ptr) -/// @return pin_map_t* - Return the address of the pin map peripheral -inline pin_map_t* pin_map = reinterpret_cast(io_connect_address); -// NOLINTEND(performance-no-int-to-ptr) -} // namespace hal::lpc40 diff --git a/src/lpc40/power.cpp b/src/lpc40/power.cpp deleted file mode 100644 index 82a004b..0000000 --- a/src/lpc40/power.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#include -#include - -#include "system_controller_reg.hpp" - -namespace hal::lpc40 { -void power_on(peripheral p_peripheral) -{ - hal::bit_modify(system_controller_reg->peripheral_power_control0) - .set(bit_mask::from(value(p_peripheral))); -} - -bool is_on(peripheral p_peripheral) -{ - return hal::bit_extract(bit_mask::from(value(p_peripheral)), - system_controller_reg->peripheral_power_control0); -} - -void power_off(peripheral p_peripheral) -{ - hal::bit_modify(system_controller_reg->peripheral_power_control0) - .clear(bit_mask::from(value(p_peripheral))); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/pwm.cpp b/src/lpc40/pwm.cpp deleted file mode 100644 index fd739a7..0000000 --- a/src/lpc40/pwm.cpp +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include - -#include "pwm_reg.hpp" - -namespace hal::lpc40 { -namespace { -[[nodiscard]] pwm_reg_t* get_pwm_reg(peripheral p_id) -{ - if (p_id == peripheral::pwm0) { - return pwm_reg0; - } - - return pwm_reg1; -} - -[[nodiscard]] uint32_t volatile& get_match_registers(pwm_reg_t* p_reg, - uint8_t p_match) -{ - switch (p_match) { - case 1: - return p_reg->match_register_1; - case 2: - return p_reg->match_register_2; - case 3: - return p_reg->match_register_3; - case 4: - return p_reg->match_register_4; - case 5: - return p_reg->match_register_5; - case 6: - return p_reg->match_register_6; - case 0: - default: - return p_reg->match_register_0; - } -} - -[[nodiscard]] float get_duty_cycle(pwm::channel p_channel, pwm_reg_t* p_reg) -{ - auto period = static_cast(get_match_registers(p_reg, p_channel.index)); - auto max_period = static_cast(get_match_registers(p_reg, 0)); - return period / max_period; -} - -[[nodiscard]] uint32_t calculate_duty_cycle(pwm_reg_t* p_reg, float p_percent) -{ - auto pwm_period = static_cast(get_match_registers(p_reg, 0)); - return static_cast(p_percent * pwm_period); -} - -void enable(pwm_reg_t* p_reg, bool p_enable) -{ - // When set to a 1, enables the TC (total count) register and begins - // counting. - static constexpr auto counter_enable = bit_mask::from<0>(); - - // When set to a 1, will reset the total count register. - static constexpr auto counter_reset = bit_mask::from<1>(); - - // Enables PWM mode. Without setting the match registers cannot operate - // with the timer. - static constexpr auto pwm_enable = bit_mask::from<3>(); - - if (p_enable) { - // Reset the Timer Counter - bit_modify(p_reg->timer_control_register).set(counter_reset); - // Clear reset and allow timer to count - bit_modify(p_reg->timer_control_register).clear(counter_reset); - // Enable PWM output - bit_modify(p_reg->timer_control_register).set(pwm_enable); - // Enable counting - bit_modify(p_reg->timer_control_register).set(counter_enable); - } else { - // Disable PWM output - bit_modify(p_reg->timer_control_register).clear(pwm_enable); - } -} - -void setup(pwm::channel& p_channel) -{ - /// Controls the counting mode of the PWM peripheral. When set to 0, counts - /// using the internal prescale counter which is driven by the peripheral - /// clock. Other modes involve use of an external clock source. - static constexpr auto mode = bit_mask::from<0, 1>(); - - /// If set to a 1, tells the PWM hardware to reset the PWM total count - /// register to be reset to 0 when it is equal to the match register 0. - static constexpr auto pwm0_reset = bit_mask::from<1>(); - - power_on(p_channel.peripheral_id); - - pwm_reg_t* reg = get_pwm_reg(p_channel.peripheral_id); - - // Set pre-scalar to 1 so the input frequency to the PWM peripheral is - // equal to the peripheral clock frequency. - reg->prescale_register = 0; - - // Set to 0 to cause the timer counter to increment after each peripheral - // clock tick. - reg->prescale_counter_register = 0; - - bit_modify(reg->counter_control_register).insert(0x0U); - bit_modify(reg->match_control_register).set(); - - // Enable this pwm channel - bit_modify(reg->pwm_control_register) - .set(bit_mask::from(8U + p_channel.index)); - - p_channel.pwm_pin.function(p_channel.pin_function); - - // Set duty cycle to zero - get_match_registers(reg, p_channel.index) = 0; - - // Enable the PWM output channel - enable(reg, true); -} -} // namespace - -pwm::pwm(std::uint8_t p_peripheral, // NOLINT - std::uint8_t p_channel) - : m_channel{} -{ - bool valid_driver = p_peripheral <= 1 && p_channel <= 6; - if (not valid_driver) { - // "LPC40 series microcontrollers only have PWM0 and PWM1." - safe_throw(hal::operation_not_supported(this)); - } - - m_channel.index = p_channel; - - if (p_peripheral == 0) { - m_channel.peripheral_id = peripheral::pwm0; - m_channel.pwm_pin = pin(3, 16 + (p_channel - 1)); - m_channel.pin_function = 0b010; - } else if (p_peripheral == 1) { - m_channel.peripheral_id = peripheral::pwm1; - m_channel.pwm_pin = pin(2, 0 + (p_channel - 1)); - m_channel.pin_function = 0b001; - } - - setup(m_channel); -} - -void pwm::driver_frequency(hertz p_frequency) -{ - pwm_reg_t* reg = get_pwm_reg(m_channel.peripheral_id); - - auto const input_clock = get_frequency(m_channel.peripheral_id); - - if (p_frequency >= input_clock) { - safe_throw(hal::operation_not_supported(this)); - } - - // Get the current duty cycle so we can match it to the updated frequency. - float previous_duty_cycle = get_duty_cycle(m_channel, reg); - - // In order to avoid PWM glitches, the PWM must be disabled while updating - // the MR0 register. Doing this will reset all counters to 0 and allow us to - // update MR0. - enable(reg, false); - - // Set frequency by setting match register 0 (the reset register) to the - // counts required to reach the desired frequency. - auto const new_frequency = input_clock / p_frequency; - - get_match_registers(reg, 0) = static_cast(new_frequency); - - // Re-enable PWM which will also reset all of the counters which allow - // setting the duty cycle of other PWM channels. - enable(reg, true); - - // Update the duty cycle based on the previous percentage - duty_cycle(previous_duty_cycle); -} - -void pwm::driver_duty_cycle(float p_duty_cycle) -{ - pwm_reg_t* reg = get_pwm_reg(m_channel.peripheral_id); - - // Set match register for this channel - get_match_registers(reg, m_channel.index) = - calculate_duty_cycle(reg, p_duty_cycle); - - // Setup pwm peripheral to update the duty cycle on the next reset cycle after - // a match_register_0 match occurs. This way the PWM duty cycle does ont - // change instantaneously. - bit_modify(reg->load_enable_register).set(bit_mask::from(m_channel.index)); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/pwm_reg.hpp b/src/lpc40/pwm_reg.hpp deleted file mode 100644 index c21321a..0000000 --- a/src/lpc40/pwm_reg.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include - -namespace hal::lpc40 { -/** - * @brief Register map for the lpc40xx PWM peripheral - * - */ -struct pwm_reg_t -{ - /// Offset: 0x000 Interrupt Register (R/W) - u32 volatile interrupt_register; - /// Offset: 0x004 Timer Control Register (R/W) - u32 volatile timer_control_register; - /// Offset: 0x008 Timer Counter Register (R/W) - u32 volatile timer_counter_register; - /// Offset: 0x00C Prescale Register (R/W) - u32 volatile prescale_register; - /// Offset: 0x010 Prescale Counter Register (R/W) - u32 volatile prescale_counter_register; - /// Offset: 0x014 Match Control Register (R/W) - u32 volatile match_control_register; - /// Offset: 0x018 Match Register 0 (R/W) - u32 volatile match_register_0; - /// Offset: 0x01C Match Register 1 (R/W) - u32 volatile match_register_1; - /// Offset: 0x020 Match Register 2 (R/W) - u32 volatile match_register_2; - /// Offset: 0x024 Match Register 3 (R/W) - u32 volatile match_register_3; - /// Offset: 0x028 Capture Control Register (R/W) - u32 volatile capture_control_register; - /// Offset: 0x02C Capture Register 0 (R/ ) - u32 const volatile capture_register_0; - /// Offset: 0x030 Capture Register 1 (R/ ) - u32 const volatile capture_register_1; - /// Offset: 0x034 Capture Register 2 (R/ ) - u32 const volatile capture_register_2; - /// Offset: 0x038 Capture Register 3 (R/ ) - u32 const volatile capture_register_3; - u32 reserved0; - /// Offset: 0x040 Match Register 4 (R/W) - u32 volatile match_register_4; - /// Offset: 0x044 Match Register 5 (R/W) - u32 volatile match_register_5; - /// Offset: 0x048 Match Register 6 (R/W) - u32 volatile match_register_6; - /// Offset: 0x04C PWM Control Register (R/W) - u32 volatile pwm_control_register; - /// Offset: 0x050 Load Enable Register (R/W) - u32 volatile load_enable_register; - std::array reserved1; - /// Offset: 0x070 Counter Control Register (R/W) - u32 volatile counter_control_register; -}; - -inline pwm_reg_t* pwm_reg0 = reinterpret_cast(0x4001'4000); -inline pwm_reg_t* pwm_reg1 = reinterpret_cast(0x4001'8000); - -} // namespace hal::lpc40 diff --git a/src/lpc40/spi.cpp b/src/lpc40/spi.cpp deleted file mode 100644 index 6ba9ece..0000000 --- a/src/lpc40/spi.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "spi_reg.hpp" - -namespace hal::lpc40 { -namespace { -inline spi_reg_t* get_spi_reg(peripheral p_id) -{ - switch (p_id) { - case peripheral::ssp0: - return spi_reg0; - case peripheral::ssp1: - return spi_reg1; - case peripheral::ssp2: - default: - return spi_reg2; - } -} - -inline bool tx_fifo_full(spi_reg_t* p_reg) -{ - return not bit_extract(p_reg->sr); -} -inline bool rx_fifo_not_empty(spi_reg_t* p_reg) -{ - return bit_extract(p_reg->sr); -} -inline bool still_sending(spi_reg_t* p_reg) -{ - return bit_extract(p_reg->sr); -} -} // namespace - -spi::spi(std::uint8_t p_bus_number, spi::settings const& p_settings) -{ - // UM10562: Chapter 7: LPC408x/407x I/O configuration page 13 - if (p_bus_number == 0) { - m_bus = { - .peripheral_id = peripheral::ssp0, - .clock = pin(0, 15), - .data_out = pin(0, 18), - .data_in = pin(0, 17), - .clock_function = 0b010, - .data_out_function = 0b010, - .data_in_function = 0b010, - }; - } else if (p_bus_number == 1) { - m_bus = { - .peripheral_id = peripheral::ssp1, - .clock = pin(0, 7), - .data_out = pin(0, 9), - .data_in = pin(0, 8), - .clock_function = 0b010, - .data_out_function = 0b010, - .data_in_function = 0b010, - }; - } else if (p_bus_number == 2) { - m_bus = { - .peripheral_id = peripheral::ssp2, - .clock = pin(1, 0), - .data_out = pin(1, 1), - .data_in = pin(1, 4), - .clock_function = 0b100, - .data_out_function = 0b100, - .data_in_function = 0b100, - }; - } else { - // "Supported spi busses are 0, 1, and 2!"; - hal::safe_throw(hal::operation_not_supported(this)); - } - - spi::driver_configure(p_settings); -} // namespace hal::lpc40 - -spi::~spi() -{ - power_off(m_bus.peripheral_id); -} - -spi::spi(bus_info p_bus) - : m_bus(p_bus) -{ -} - -void spi::driver_configure(settings const& p_settings) -{ - constexpr uint8_t spi_format_code = 0b00; - - auto* reg = get_spi_reg(m_bus.peripheral_id); - - // Power up peripheral - power_on(m_bus.peripheral_id); - - // Set SSP frame format to SPI - bit_modify(reg->cr0).insert(spi_format_code); - - // Set SPI to master mode by clearing - bit_modify(reg->cr1).clear(); - - // Setup operating frequency - auto const input_clock = get_frequency(m_bus.peripheral_id); - auto const clock_divider = input_clock / p_settings.clock_rate; - auto const prescaler = static_cast(clock_divider); - auto const prescaler_low = static_cast(prescaler & 0xFF); - auto const prescaler_high = static_cast(prescaler >> 8); - // Store lower half of prescalar in clock prescalar register - reg->cpsr = prescaler_low; - // Store upper 8 bit half of the prescalar in control register 0 - bit_modify(reg->cr0).insert(prescaler_high); - - // Set clock modes & bit size - // - // NOTE: In UM10562 page 611, you will see that DSS (Data Size Select) is - // equal to the bit transfer minus 1. So we can add 3 to our DataSize enum - // to get the appropriate transfer code. - constexpr std::uint8_t size_code_8bit = 0b111; - - bit_modify(reg->cr0) - .insert(p_settings.clock_idles_high) - .insert( - p_settings.data_valid_on_trailing_edge) - .insert(size_code_8bit); - - // Initialize SSP pins - m_bus.clock.function(m_bus.clock_function) - .analog(false) - .open_drain(false) - .resistor(pin_resistor::none); - m_bus.data_in.function(m_bus.data_in_function) - .analog(false) - .open_drain(false) - .resistor(pin_resistor::none); - m_bus.data_out.function(m_bus.data_out_function) - .analog(false) - .open_drain(false) - .resistor(pin_resistor::none); - - // Enable SSP - bit_modify(reg->cr1).set(); -} - -void spi::driver_transfer(std::span p_data_out, - std::span p_data_in, - hal::byte p_filler) -{ - auto* reg = get_spi_reg(m_bus.peripheral_id); - auto tx_interator = p_data_out.begin(); - auto rx_interator = p_data_in.begin(); - - // iterate until both reach their end - while (tx_interator != p_data_out.end() || rx_interator != p_data_in.end()) { - if (rx_interator != p_data_in.end() && rx_fifo_not_empty(reg)) { - *rx_interator++ = reg->dr; - } - if (tx_interator == p_data_out.end()) { - reg->dr = p_filler; - } else if (not tx_fifo_full(reg)) { - reg->dr = *tx_interator++; - } - } - - // Wait for bus activity to cease before leaving the function - while (still_sending(reg)) { - continue; - } -} -} // namespace hal::lpc40 diff --git a/src/lpc40/spi_reg.hpp b/src/lpc40/spi_reg.hpp deleted file mode 100644 index 357c200..0000000 --- a/src/lpc40/spi_reg.hpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include - -namespace hal::lpc40 { -struct spi_reg_t -{ - /*!< Offset: 0x000 Control Register 0 (R/W) */ - uint32_t volatile cr0; - /*!< Offset: 0x004 Control Register 1 (R/W) */ - uint32_t volatile cr1; - /*!< Offset: 0x008 Data Register (R/W) */ - uint32_t volatile dr; - /*!< Offset: 0x00C Status Register (R/ ) */ - uint32_t const volatile sr; - /*!< Offset: 0x010 Clock Prescale Register (R/W) */ - uint32_t volatile cpsr; - /*!< Offset: 0x014 Interrupt Mask Set and Clear Register (R/W) */ - uint32_t volatile imsc; - /*!< Offset: 0x018 Raw Interrupt Status Register (R/W) */ - uint32_t volatile ris; - /*!< Offset: 0x01C Masked Interrupt Status Register (R/W) */ - uint32_t volatile mis; - /*!< Offset: 0x020 SSPICR Interrupt Clear Register (R/W) */ - uint32_t volatile icr; - /*!< Offset: 0x024 SSPnDMACR DMA control register (R/W) */ - uint32_t volatile dmacr; -}; - -/// SSPn Control Register 0 -struct control_register0 // NOLINT -{ - /// Data Size Select. This field controls the number of bits transferred in - /// each frame. Values 0000-0010 are not supported and should not be used. - static constexpr auto data_bit = bit_mask::from<0, 3>(); - - /// Frame Format bitmask. - /// 00 = SPI, 01 = TI, 10 = Microwire, 11 = Invalid - static constexpr auto frame_bit = bit_mask::from<4, 5>(); - - /// If bit is set to 0 SSP controller maintains the bus clock low between - /// frames. - /// - /// If bit is set to 1 SSP controller maintains the bus clock high between - /// frames. - static constexpr auto polarity_bit = bit_mask::from<6>(); - - /// If bit is set to 0 SSP controller captures serial data on the first - /// clock transition of the frame, that is, the transition away from the - /// inter-frame state of the clock line. - /// - /// If bit is set to 1 SSP controller captures serial data on the second - /// clock transition of the frame, that is, the transition back to the - /// inter-frame state of the clock line. - static constexpr auto phase_bit = bit_mask::from<7>(); - - /// Bitmask for dividing the peripheral clock to set the SPI clock - /// frequency. - static constexpr auto divider_bit = bit_mask::from<8, 15>(); -}; - -/// SSPn Control Register 1 -struct control_register1 // NOLINT -{ - /// Setting this bit to 1 will enable the peripheral for communication. - static constexpr auto spi_enable = bit_mask::from<1>(); - - /// Setting this bit to 1 will enable spi slave mode. - static constexpr auto slave_mode_bit = bit_mask::from<2>(); -}; - -/// SSPn Status Register -struct status_register // NOLINT -{ - static constexpr auto transmit_fifo_not_full = bit_mask::from<1>(); - static constexpr auto receive_fifo_not_empty = bit_mask::from<2>(); - /// This bit is 0 if the SSPn controller is idle, or 1 if it is currently - /// sending/receiving a frame and/or the Tx FIFO is not empty. - static constexpr auto data_line_busy_bit = bit_mask::from<4>(); -}; - -/// SSPn dma Register -struct dma_register // NOLINT -{ - static constexpr auto receive_dma_enable = bit_mask::from<0>(); - static constexpr auto transmit_dma_enable = bit_mask::from<1>(); -}; - -inline spi_reg_t* spi_reg0 = reinterpret_cast(0x40088000); -inline spi_reg_t* spi_reg1 = reinterpret_cast(0x40030000); -inline spi_reg_t* spi_reg2 = reinterpret_cast(0x400AC000); -} // namespace hal::lpc40 diff --git a/src/lpc40/stream_dac.cpp b/src/lpc40/stream_dac.cpp deleted file mode 100644 index 2195637..0000000 --- a/src/lpc40/stream_dac.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include "dac_reg.hpp" - -namespace hal::lpc40 { - -namespace { -void setup_stream_dac() -{ - hal::lpc40::pin(0, 26).function(0b010).dac(true); - - // Double buffering enabled (1) - // Count enable (2) - // DMA enable (3) - dac_reg->control = (1 << 1) | (1 << 2) | (1 << 3); -} - -template -void dac_dma_write(hal::io_waiter& p_waiter, - typename hal::stream_dac::samples const& p_samples) -{ - // Setup sampling frequency - auto const input_clock = - hal::lpc40::get_frequency(hal::lpc40::peripheral::dac); - auto const clock_count_value = input_clock / p_samples.sample_rate; - dac_reg->count_value = clock_count_value; - - auto data_remaining = p_samples.data; - - while (not data_remaining.empty()) { - bool finished = false; - auto const transfer_amount = - std::min(data_remaining.size(), dma_max_transfer_size); - - if constexpr (std::is_same_v) { - hal::lpc40::setup_dma_transfer( - dma{ - .source = data_remaining.data(), - .destination = &dac_reg->conversion_register.parts[1], - .length = transfer_amount, - .source_increment = true, - .destination_increment = false, - .transfer_type = dma_transfer_type::memory_to_peripheral, - .source_transfer_width = dma_transfer_width::bit_8, - .destination_transfer_width = dma_transfer_width::bit_8, - .source_peripheral = dma_peripheral::memory_or_timer0_match0, - .destination_peripheral = dma_peripheral::dac, - }, - [&p_waiter, &finished]() { - finished = true; - p_waiter.resume(); - }); - } else if (std::is_same_v) { - hal::lpc40::setup_dma_transfer( - dma{ - .source = data_remaining.data(), - .destination = &dac_reg->conversion_register.whole, - .length = transfer_amount, - .source_increment = true, - .destination_increment = false, - .transfer_type = dma_transfer_type::memory_to_peripheral, - .source_transfer_width = dma_transfer_width::bit_16, - .destination_transfer_width = dma_transfer_width::bit_16, - .source_peripheral = dma_peripheral::memory_or_timer0_match0, - .destination_peripheral = dma_peripheral::dac, - }, - [&p_waiter, &finished]() { - finished = true; - p_waiter.resume(); - }); - } - - while (not finished) { - p_waiter.wait(); - } - - // Move data forward by the amount of data that was transferred - data_remaining = data_remaining.subspan(transfer_amount); - } -} -} // namespace - -stream_dac_u8::stream_dac_u8(hal::io_waiter& p_waiter) - : m_waiter(&p_waiter) -{ - setup_stream_dac(); -} - -void stream_dac_u8::driver_write(hal::stream_dac_u8::samples const& p_samples) -{ - dac_dma_write(*m_waiter, p_samples); -} - -stream_dac_u16::stream_dac_u16(hal::io_waiter& p_waiter) - : m_waiter(&p_waiter) -{ - setup_stream_dac(); -} - -void stream_dac_u16::driver_write(hal::stream_dac_u16::samples const& p_samples) -{ - dac_dma_write(*m_waiter, p_samples); -} -} // namespace hal::lpc40 diff --git a/src/lpc40/system_controller_reg.hpp b/src/lpc40/system_controller_reg.hpp deleted file mode 100644 index 3ccb84d..0000000 --- a/src/lpc40/system_controller_reg.hpp +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -namespace hal::lpc40 { -/// lpc40xx system controller register map -struct system_controller_t -{ - /// Offset: 0x000 (R/W) Flash Accelerator Configuration Register - uint32_t volatile flashcfg; - /// reserved 0 - std::array reserved0; - /// Offset: 0x080 (R/W) PLL0 Control Register - uint32_t volatile pll0con; - /// Offset: 0x084 (R/W) PLL0 Configuration Register - uint32_t volatile pll0cfg; - /// Offset: 0x088 (R/ ) PLL0 Status Register - uint32_t const volatile pll0stat; - /// Offset: 0x08C ( /W) PLL0 Feed Register - uint32_t volatile pll0feed; - /// reserved 1 - std::array reserved1; - /// Offset: 0x0A0 (R/W) PLL1 Control Register - uint32_t volatile pll1con; - /// Offset: 0x0A4 (R/W) PLL1 Configuration Register - uint32_t volatile pll1cfg; - /// Offset: 0x0A8 (R/ ) PLL1 Status Register - uint32_t const volatile pll1stat; - /// Offset: 0x0AC ( /W) PLL1 Feed Register - uint32_t volatile pll1feed; - /// reserved 2 - std::array reserved2; - /// Offset: 0x0C0 (R/W) Power Control Register - uint32_t volatile power_control; - /// Offset: 0x0C4 (R/W) Power Control for Peripherals Register - uint32_t volatile peripheral_power_control0; - /// Offset: 0x0C8 (R/W) Power Control for Peripherals Register - uint32_t volatile peripheral_power_control1; - /// reserved 3 - std::array reserved3; - /// Offset: 0x100 (R/W) External Memory Controller Clock Selection Register - uint32_t volatile emmc_clock_select; - /// Offset: 0x104 (R/W) CPU Clock Selection Register - uint32_t volatile cpu_clock_select; - /// Offset: 0x108 (R/W) USB Clock Selection Register - uint32_t volatile usb_clock_select; - /// Offset: 0x10C (R/W) Clock Source Select Register - uint32_t volatile clock_source_select; - /// Offset: 0x110 (R/W) CAN Sleep Clear Register - uint32_t volatile can_sleep_clear; - /// Offset: 0x114 (R/W) CAN Wake-up Flags Register - uint32_t volatile canwakeflags; - /// reserved 4 - std::array reserved4; - /// Offset: 0x140 (R/W) External Interrupt Flag Register - uint32_t volatile extint; - /// reserved 5 - std::array reserved5; - /// Offset: 0x148 (R/W) External Interrupt Mode Register - uint32_t volatile extmode; - /// Offset: 0x14C (R/W) External Interrupt Polarity Register - uint32_t volatile extpolar; - /// reserved 6 - std::array reserved6; - /// Offset: 0x180 (R/W) Reset Source Identification Register - uint32_t volatile reset_source_id; - /// reserved 7 - std::array reserved7; - /// Offset: 0x1A0 (R/W) System Controls and Status Register - uint32_t volatile scs; - /// Offset: 0x1A4 (R/W) Clock Dividers - uint32_t volatile irctrim; - /// Offset: 0x1A8 (R/W) Peripheral Clock Selection Register - uint32_t volatile peripheral_clock_select; - /// reserved 8 - std::array reserved8; - /// Offset: 0x1B0 (R/W) Power Boost control register - uint32_t volatile power_boost; - /// Offset: 0x1B4 (R/W) spifi clock select - uint32_t volatile spifi_clock_select; - /// Offset: 0x1B8 (R/W) LCD Configuration and clocking control Register - uint32_t volatile lcd_cfg; - /// reserved 9 - std::array reserved9; - /// Offset: 0x1C0 (R/W) USB Interrupt Status Register - uint32_t volatile usb_interrupt_status; - /// Offset: 0x1C4 (R/W) DMA Request Select Register - uint32_t volatile dmareqsel; - /// Offset: 0x1C8 (R/W) Clock Output Configuration Register - uint32_t volatile clkoutcfg; - /// Offset: 0x1CC (R/W) RESET Control0 Register - uint32_t volatile rstcon0; - /// Offset: 0x1D0 (R/W) RESET Control1 Register - uint32_t volatile rstcon1; - /// reserved 10 - std::array reserved10; - /// Offset: 0x1DC (R/W) sdram programmable delays - uint32_t volatile sdram_delay; - /// Offset: 0x1E0 (R/W) Calibration of programmable delays - uint32_t volatile emmc_calibration; -}; // namespace system_controller_t - -/// Namespace for PLL configuration bit masks -namespace pll_register { -/// In PLLCON register: When 1, and after a valid PLL feed, this bit -/// will activate the related PLL and allow it to lock to the requested -/// frequency. -static constexpr auto enable = bit_mask::from<0>(); - -/// In PLLCFG register: PLL multiplier value, the amount to multiply the -/// input frequency by. -static constexpr auto multiplier = bit_mask::from<0, 4>(); - -/// In PLLCFG register: PLL divider value, the amount to divide the output -/// of the multiplier stage to bring the frequency down to a -/// reasonable/usable level. -static constexpr auto divider = bit_mask::from<5, 6>(); - -/// In PLLSTAT register: if set to 1 by hardware, the PLL has accepted -/// the configuration and is locked. -static constexpr auto pll_lock = bit_mask::from<10>(); -}; // namespace pll_register - -/// Namespace of Oscillator register bit_masks -namespace oscillator { -/// IRC or Main oscillator select bit -static constexpr auto select = bit_mask::from<0>(); - -/// SCS: Main oscillator range select -static constexpr auto range_select = bit_mask::from<4>(); - -/// SCS: Main oscillator enable -static constexpr auto external_enable = bit_mask::from<5>(); - -/// SCS: Main oscillator ready status -static constexpr auto external_ready = bit_mask::from<6>(); -}; // namespace oscillator - -/// Namespace of Clock register bit_masks -namespace cpu_clock { -/// CPU clock divider amount -static constexpr auto divider = bit_mask::from<0, 4>(); - -/// CPU clock source select bit -static constexpr auto select = bit_mask::from<8>(); -}; // namespace cpu_clock - -/// Namespace of Peripheral register bit_masks -namespace peripheral_clock { -/// Main single peripheral clock divider shared across all peripherals, -/// except for USB and spifi. -static constexpr auto divider = bit_mask::from<0, 4>(); -}; // namespace peripheral_clock - -/// Namespace of EMC register bit_masks -namespace emc_clock { -/// EMC Clock Register divider bit -static constexpr auto divider = bit_mask::from<0>(); -}; // namespace emc_clock - -/// Namespace of USB register bit_masks -namespace usb_clock { -/// USB clock divider constant -static constexpr auto divider = bit_mask::from<0, 4>(); - -/// USB clock source select bit -static constexpr auto select = bit_mask::from<8, 9>(); -}; // namespace usb_clock - -/// Namespace of spifi register bit_masks -namespace spifi_clock { -/// spifi clock divider constant -static constexpr auto divider = bit_mask::from<0, 4>(); - -/// spifi clock source select bit -static constexpr auto select = bit_mask::from<8, 9>(); -}; // namespace spifi_clock - -constexpr intptr_t lpc_apb1_base = 0x40080000UL; -constexpr intptr_t lpc_sc_base = lpc_apb1_base + 0x7C000; - -// NOLINTBEGIN(performance-no-int-to-ptr) -/// @brief Pointer to system controller register -inline system_controller_t* system_controller_reg = - reinterpret_cast(lpc_sc_base); -// NOLINTEND(performance-no-int-to-ptr) -} // namespace hal::lpc40 diff --git a/src/lpc40/uart.cpp b/src/lpc40/uart.cpp deleted file mode 100644 index 981370f..0000000 --- a/src/lpc40/uart.cpp +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "uart_clock.hpp" -#include "uart_reg.hpp" - -namespace hal::lpc40 { - -uart_reg_t* get_uart_reg(peripheral p_id) -{ - switch (p_id) { - case peripheral::uart0: - return uart_reg0; - case peripheral::uart1: - return uart_reg1; - case peripheral::uart2: - return uart_reg2; - case peripheral::uart3: - return uart_reg3; - case peripheral::uart4: - default: - return uart_reg4; - } -} - -void configure_baud_rate(uart_reg_t* p_reg, uart_baud_t p_calibration) -{ - static constexpr auto divisor_access = bit_mask::from<7>(); - - auto divisor_latch_msb = - static_cast((p_calibration.divider >> 8) & 0xFF); - auto divisor_latch_lsb = static_cast(p_calibration.divider & 0xFF); - auto fractional_divider = static_cast( - (p_calibration.numerator & 0xF) | (p_calibration.denominator & 0xF) << 4); - - bit_modify(p_reg->line_control).set(divisor_access); - p_reg->group1.divisor_latch_lsb = divisor_latch_lsb; - p_reg->group2.divisor_latch_msb = divisor_latch_msb; - p_reg->fractional_divider = fractional_divider; - bit_modify(p_reg->line_control).clear(divisor_access); -} - -uint8_t get_line_control(serial::settings const& p_settings) -{ - bit_value line_control_object(0); - - // Set stop bit length - switch (p_settings.stop) { - case serial::settings::stop_bits::one: - line_control_object.clear(); - break; - case serial::settings::stop_bits::two: - line_control_object.set(); - break; - } - - // Set frame size to 8 = 0x3 - line_control_object.insert(0x3U); - - // Preset the parity enable and disable it if the parity is set to none - line_control_object.set(); - - // Set frame parity - switch (p_settings.parity) { - case serial::settings::parity::odd: - line_control_object.insert(0x0U); - break; - case serial::settings::parity::even: - line_control_object.insert(0x1U); - break; - case serial::settings::parity::forced1: - line_control_object.insert(0x2U); - break; - case serial::settings::parity::forced0: - line_control_object.insert(0x3U); - break; - case serial::settings::parity::none: - // Turn off parity if the parity is set to none - line_control_object.clear(); - break; - } - - return line_control_object.get(); -} - -void reset_uart_queue(uart_reg_t* p_reg) -{ - bit_modify(p_reg->group3.fifo_control) - .set() - .set(); -} - -inline bool has_data(uart_reg_t* p_reg) -{ - return bit_extract()>(p_reg->line_status); -} - -void uart::interrupt_handler() -{ - auto* reg = get_uart_reg(m_port.id); - [[maybe_unused]] auto line_status_value = reg->line_status; - auto interrupt_type = - bit_extract(reg->group3.interrupt_id); - if (interrupt_type == 0x2 || interrupt_type == 0x6) { - while (has_data(reg)) { - hal::byte new_byte{ reg->group1.receive_buffer }; - if (!m_receive_buffer.full()) { - m_receive_buffer.push_back(hal::byte{ new_byte }); - } - } - } -} - -void uart::setup_receive_interrupt() -{ - auto* reg = get_uart_reg(m_port.id); - // Create a lambda to call the interrupt() method - auto isr = [this]() { interrupt_handler(); }; - - // A pointer to save the static_callable isr address to. - cortex_m::interrupt_pointer handler; - - switch (m_port.irq_number) { - case irq::uart0: - handler = static_callable(isr).get_handler(); - break; - case irq::uart1: - handler = static_callable(isr).get_handler(); - break; - case irq::uart2: - handler = static_callable(isr).get_handler(); - break; - case irq::uart3: - handler = static_callable(isr).get_handler(); - break; - case irq::uart4: - default: - handler = static_callable(isr).get_handler(); - break; - } - - // Enable interrupt service routine. - cortex_m::enable_interrupt(m_port.irq_number, handler); - - // Enable uart interrupt signal - bit_modify(reg->group2.interrupt_enable) - .set(); - // 0x3 = 14 bytes in fifo before triggering a receive interrupt. - // 0x2 = 8 - // 0x1 = 4 - // 0x0 = 1 - bit_modify(reg->group3.fifo_control) - .insert(0x3U); -} - -uart::uart(std::uint8_t p_port_number, - std::span p_receive_working_buffer, - serial::settings const& p_settings) - : m_port{} - , m_receive_buffer(p_receive_working_buffer.begin(), - p_receive_working_buffer.end()) -{ - if (p_port_number == 0) { - // NOTE: required since LPC_UART0 is of type LPC_UART0_TypeDef in - // lpc17xx - // and LPC_UART_TypeDef in lpc40xx causing a "useless cast" warning when - // compiled for, some odd reason, for either one being compiled, which - // would make more sense if it only warned us with lpc40xx. - m_port = uart::port{ - .id = peripheral::uart0, - .irq_number = irq::uart0, - .tx = pin(0, 2), - .rx = pin(0, 3), - .tx_function = 0b001, - .rx_function = 0b001, - }; - } else if (p_port_number == 1) { - m_port = uart::port{ - .id = peripheral::uart1, - .irq_number = irq::uart1, - .tx = pin(2, 0), - .rx = pin(2, 1), - .tx_function = 0b010, - .rx_function = 0b010, - }; - } else if (p_port_number == 2) { - m_port = uart::port{ - .id = peripheral::uart2, - .irq_number = irq::uart2, - .tx = pin(2, 8), - .rx = pin(2, 9), - .tx_function = 0b010, - .rx_function = 0b010, - }; - } else if (p_port_number == 3) { - m_port = uart::port{ - .id = peripheral::uart3, - .irq_number = irq::uart3, - .tx = pin(4, 28), - .rx = pin(4, 29), - .tx_function = 0b010, - .rx_function = 0b010, - }; - } else if (p_port_number == 4) { - m_port = uart::port{ - .id = peripheral::uart4, - .irq_number = irq::uart4, - .tx = pin(1, 28), - .rx = pin(2, 9), - .tx_function = 0b101, - .rx_function = 0b011, - }; - } else { - // "Support UART ports for LPC40xx are UART0, UART2, UART3, and UART4."; - hal::safe_throw(hal::operation_not_supported(this)); - } - - initialize_interrupts(); - uart::driver_configure(p_settings); -} - -uart::uart(uart::port const& p_port, - std::span p_receive_working_buffer, - serial::settings const& p_settings) - : m_port(p_port) - , m_receive_buffer(p_receive_working_buffer.begin(), - p_receive_working_buffer.end()) -{ - initialize_interrupts(); - uart::driver_configure(p_settings); -} - -void uart::driver_configure(settings const& p_settings) -{ - auto* reg = get_uart_reg(m_port.id); - - // Validate the settings before configuring any hardware - auto baud_rate = static_cast(p_settings.baud_rate); - auto uart_frequency = get_frequency(m_port.id); - auto uart_frequency_hz = static_cast(uart_frequency); - auto baud_settings = calculate_baud(baud_rate, uart_frequency_hz); - - // For proper operation of the UART port, the divider must be greater than 2 - // If it is not the cause that means that the baud rate is too high for this - // device. - if (baud_settings.divider <= 2) { - safe_throw(hal::operation_not_supported(this)); - } - - // Power on UART peripheral - power_on(m_port.id); - - // Enable fifo for receiving bytes and to enable full access of the FCR - // register. - bit_modify(reg->group3.fifo_control).set(); - reg->line_control = get_line_control(p_settings); - - configure_baud_rate(reg, baud_settings); - - pin(m_port.tx).function(m_port.tx_function); - pin(m_port.rx) - .function(m_port.rx_function) - .resistor(hal::pin_resistor::pull_up); - - setup_receive_interrupt(); - - // Clear the buffer - uart::driver_flush(); - - // Reset the UART queues - reset_uart_queue(reg); -} - -bool finished_sending(uart_reg_t* p_reg) -{ - return bit_extract()>(p_reg->line_status); -} - -serial::write_t uart::driver_write(std::span p_data) -{ - auto* reg = get_uart_reg(m_port.id); - - for (auto const& byte : p_data) { - reg->group1.transmit_buffer = byte; - while (!finished_sending(reg)) { - continue; - } - } - return write_t{ .data = p_data }; -} - -serial::read_t uart::driver_read(std::span p_data) -{ - size_t count = 0; - for (auto& byte : p_data) { - if (m_receive_buffer.empty()) { - break; - } - - byte = m_receive_buffer.pop_front(); - count++; - } - - return read_t{ - .data = p_data.subspan(0, count), - .available = m_receive_buffer.size(), - .capacity = m_receive_buffer.capacity(), - }; -} - -void uart::driver_flush() -{ - while (!m_receive_buffer.empty()) { - m_receive_buffer.pop_back(); - } -} -} // namespace hal::lpc40 diff --git a/src/lpc40/uart_clock.hpp b/src/lpc40/uart_clock.hpp deleted file mode 100644 index c5080ea..0000000 --- a/src/lpc40/uart_clock.hpp +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include - -#include - -namespace hal::lpc40 { -/// Structure containing the exact clock divider and multiplier tuning values -/// for the uart peripheral -struct uart_baud_t -{ - /// main clock divider - uint32_t divider; - /// tuning numerator - uint32_t numerator; - /// tuning denominator - uint32_t denominator; -}; - -/// Structure holds the fractional tuning information that follows this -/// equation: -/// -/// 1 + (numerator / denominator) -/// -/// Thus the end value will always be 1.XXX value. The fractional value is used -/// to tune the baud rate down slightly to reach a target baud rate which -/// couldn't be achieved with -struct fractional_divider_t -{ - /// The tuning ratio multiplier to scale up baud rate by - int ratio; - /// The numerator value of the ratio must always be smaller than or equal to - /// the denominator - uint32_t numerator; - /// The denominator value of the ratio - uint32_t denominator; -}; - -/// Table of all of the possible fractional values allowed for the UART hardware -/// and their ratios -constexpr std::array fractional_table{ - fractional_divider_t{ .ratio = 1000, .numerator = 0, .denominator = 1 }, - fractional_divider_t{ .ratio = 1250, .numerator = 1, .denominator = 4 }, - fractional_divider_t{ .ratio = 1500, .numerator = 1, .denominator = 2 }, - fractional_divider_t{ .ratio = 1750, .numerator = 3, .denominator = 4 }, - fractional_divider_t{ .ratio = 1067, .numerator = 1, .denominator = 15 }, - fractional_divider_t{ .ratio = 1267, .numerator = 4, .denominator = 15 }, - fractional_divider_t{ .ratio = 1533, .numerator = 8, .denominator = 15 }, - fractional_divider_t{ .ratio = 1769, .numerator = 10, .denominator = 13 }, - fractional_divider_t{ .ratio = 1071, .numerator = 1, .denominator = 14 }, - fractional_divider_t{ .ratio = 1273, .numerator = 3, .denominator = 11 }, - fractional_divider_t{ .ratio = 1538, .numerator = 7, .denominator = 13 }, - fractional_divider_t{ .ratio = 1778, .numerator = 7, .denominator = 9 }, - fractional_divider_t{ .ratio = 1077, .numerator = 1, .denominator = 13 }, - fractional_divider_t{ .ratio = 1286, .numerator = 2, .denominator = 7 }, - fractional_divider_t{ .ratio = 1545, .numerator = 6, .denominator = 11 }, - fractional_divider_t{ .ratio = 1786, .numerator = 11, .denominator = 14 }, - fractional_divider_t{ .ratio = 1083, .numerator = 1, .denominator = 12 }, - fractional_divider_t{ .ratio = 1300, .numerator = 3, .denominator = 10 }, - fractional_divider_t{ .ratio = 1556, .numerator = 5, .denominator = 9 }, - fractional_divider_t{ .ratio = 1800, .numerator = 4, .denominator = 5 }, - fractional_divider_t{ .ratio = 1091, .numerator = 1, .denominator = 11 }, - fractional_divider_t{ .ratio = 1308, .numerator = 4, .denominator = 13 }, - fractional_divider_t{ .ratio = 1571, .numerator = 4, .denominator = 7 }, - fractional_divider_t{ .ratio = 1818, .numerator = 9, .denominator = 11 }, - fractional_divider_t{ .ratio = 1100, .numerator = 1, .denominator = 10 }, - fractional_divider_t{ .ratio = 1333, .numerator = 1, .denominator = 3 }, - fractional_divider_t{ .ratio = 1583, .numerator = 7, .denominator = 12 }, - fractional_divider_t{ .ratio = 1833, .numerator = 5, .denominator = 6 }, - fractional_divider_t{ .ratio = 1111, .numerator = 1, .denominator = 9 }, - fractional_divider_t{ .ratio = 1357, .numerator = 5, .denominator = 14 }, - fractional_divider_t{ .ratio = 1600, .numerator = 3, .denominator = 5 }, - fractional_divider_t{ .ratio = 1846, .numerator = 11, .denominator = 13 }, - fractional_divider_t{ .ratio = 1125, .numerator = 1, .denominator = 8 }, - fractional_divider_t{ .ratio = 1364, .numerator = 4, .denominator = 11 }, - fractional_divider_t{ .ratio = 1615, .numerator = 8, .denominator = 13 }, - fractional_divider_t{ .ratio = 1857, .numerator = 6, .denominator = 7 }, - fractional_divider_t{ .ratio = 1133, .numerator = 2, .denominator = 15 }, - fractional_divider_t{ .ratio = 1375, .numerator = 3, .denominator = 8 }, - fractional_divider_t{ .ratio = 1625, .numerator = 5, .denominator = 8 }, - fractional_divider_t{ .ratio = 1867, .numerator = 13, .denominator = 15 }, - fractional_divider_t{ .ratio = 1143, .numerator = 1, .denominator = 7 }, - fractional_divider_t{ .ratio = 1385, .numerator = 5, .denominator = 13 }, - fractional_divider_t{ .ratio = 1636, .numerator = 7, .denominator = 11 }, - fractional_divider_t{ .ratio = 1875, .numerator = 7, .denominator = 8 }, - fractional_divider_t{ .ratio = 1154, .numerator = 2, .denominator = 13 }, - fractional_divider_t{ .ratio = 1400, .numerator = 2, .denominator = 5 }, - fractional_divider_t{ .ratio = 1643, .numerator = 9, .denominator = 14 }, - fractional_divider_t{ .ratio = 1889, .numerator = 8, .denominator = 9 }, - fractional_divider_t{ .ratio = 1167, .numerator = 1, .denominator = 6 }, - fractional_divider_t{ .ratio = 1417, .numerator = 5, .denominator = 12 }, - fractional_divider_t{ .ratio = 1667, .numerator = 2, .denominator = 3 }, - fractional_divider_t{ .ratio = 1900, .numerator = 9, .denominator = 10 }, - fractional_divider_t{ .ratio = 1182, .numerator = 2, .denominator = 11 }, - fractional_divider_t{ .ratio = 1429, .numerator = 3, .denominator = 7 }, - fractional_divider_t{ .ratio = 1692, .numerator = 9, .denominator = 13 }, - fractional_divider_t{ .ratio = 1909, .numerator = 10, .denominator = 11 }, - fractional_divider_t{ .ratio = 1200, .numerator = 1, .denominator = 5 }, - fractional_divider_t{ .ratio = 1444, .numerator = 4, .denominator = 9 }, - fractional_divider_t{ .ratio = 1700, .numerator = 7, .denominator = 10 }, - fractional_divider_t{ .ratio = 1917, .numerator = 11, .denominator = 12 }, - fractional_divider_t{ .ratio = 1214, .numerator = 3, .denominator = 14 }, - fractional_divider_t{ .ratio = 1455, .numerator = 5, .denominator = 11 }, - fractional_divider_t{ .ratio = 1714, .numerator = 5, .denominator = 7 }, - fractional_divider_t{ .ratio = 1923, .numerator = 12, .denominator = 13 }, - fractional_divider_t{ .ratio = 1222, .numerator = 2, .denominator = 9 }, - fractional_divider_t{ .ratio = 1462, .numerator = 6, .denominator = 13 }, - fractional_divider_t{ .ratio = 1727, .numerator = 8, .denominator = 11 }, - fractional_divider_t{ .ratio = 1929, .numerator = 13, .denominator = 14 }, - fractional_divider_t{ .ratio = 1231, .numerator = 3, .denominator = 13 }, - fractional_divider_t{ .ratio = 1467, .numerator = 7, .denominator = 15 }, - fractional_divider_t{ .ratio = 1733, .numerator = 11, .denominator = 15 }, - fractional_divider_t{ .ratio = 1933, .numerator = 14, .denominator = 15 }, -}; - -/** - * @brief Find the closest fractional value to the target ratio - * - * @param p_ratio - target ratio to hit the target baud rate - * @return constexpr fractional_divider_t - fractional value representing the - * closest approximation to the target ratio. - */ -constexpr fractional_divider_t closest_fractional(int32_t p_ratio) -{ - fractional_divider_t result = fractional_table[0]; - auto difference = std::numeric_limits::max(); - - for (auto const& fraction : fractional_table) { - int32_t new_difference = hal::absolute_value(p_ratio - fraction.ratio); - if (new_difference < difference) { - result = fraction; - difference = new_difference; - } - } - - return result; -} -// NOLINTBEGIN(bugprone-easily-swappable-parameters) -/** - * @brief Calculate the baud rate register values - * - * @param p_baud_rate - the target baud rate - * @param p_frequency_hz - clock frequency driving uart peripheral - * @return constexpr uart_baud_t - returns the baud rate register values - */ -constexpr uart_baud_t calculate_baud(uint32_t p_baud_rate, - uint32_t p_frequency_hz) -{ - // The number of samples per UART frame bit. - // This is used as a multiplier for the baudrate. - constexpr uint32_t samples_per_bit_rate = 16; - constexpr uint64_t thousands = 1000; - - // Hold the result return value. - uart_baud_t result{}; - - uint64_t const frequency_1000 = p_frequency_hz * thousands; - uint32_t const sample_rate = p_baud_rate * samples_per_bit_rate; - - // Compute the integer divider for the baud rate - uint32_t const integer_divider = (p_frequency_hz / sample_rate); - - // Computer the integer divider for the baud rate except multiplied by 1000 - // in order to get 3 additional decimal places. - auto const divider_1000 = static_cast(frequency_1000 / sample_rate); - - // Save divider to result - result.divider = integer_divider; - - // Check if the integer divider is not zero because not doing that will cause - // a division error. Also note that an integer divider of 0 represents a - // failure. - if (integer_divider != 0) { - auto const multiplier_ratio = - static_cast(divider_1000 / integer_divider); - fractional_divider_t const fraction = closest_fractional(multiplier_ratio); - result.numerator = fraction.numerator; - result.denominator = fraction.denominator; - } - - return result; -} -// NOLINTEND(bugprone-easily-swappable-parameters) -} // namespace hal::lpc40 diff --git a/src/lpc40/uart_reg.hpp b/src/lpc40/uart_reg.hpp deleted file mode 100644 index bc0e8b9..0000000 --- a/src/lpc40/uart_reg.hpp +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include - -namespace hal::lpc40 { - -/// peripheral register map -struct uart_reg_t -{ - /// Union of registers overlapping offset address 0x000 - union union1 - { - /// (R/ ) Contains the next received character to be read - uint8_t const volatile receive_buffer; - /// ( /W) The next character to be transmitted is written here (DLAB = 0) - uint8_t volatile transmit_buffer; - /// (R/W) Least significant byte of the baud rate divisor value. The full - /// divisor is used to generate a baud rate from the fractional rate - /// divider (DLAB = 1) - uint8_t volatile divisor_latch_lsb; - /// Simply here to expand the size of the first union to 32-bits - uint32_t reserved0; - }; - /// Offset: 0x000 Registers overlapping offset address 0x000 - union1 group1; - /// Union of registers overlapping offset address 0x004 - union union2 - { - /// (R/W) Most significant byte of the baud rate divisor value. The full - /// divisor is used to generate a baud rate from the fractional rate - /// divider (DLAB = 1) - uint8_t volatile divisor_latch_msb; - /// (R/W) Contains individual interrupt enable bits for the 7 potential - /// UART interrupts (DLAB =0). - uint32_t volatile interrupt_enable; - }; - /// Offset: 0x004 Registers overlapping offset address 0x004 - union2 group2; - /// Union of registers overlapping offset address 0x008 - union union3 - { - /// (R/ ) Identifies which interrupt(s) are pending. - uint32_t const volatile interrupt_id; - /// ( /W) Controls UART FIFO usage and modes. - uint8_t volatile fifo_control; - }; - /// Offset: 0x008 Registers overlapping offset address 0x000 - union3 group3; - /// Offset: 0x00C (R/W) Contains controls for frame formatting and break - /// generation - uint8_t volatile line_control; - /// reserved 1 - std::array reserved1; - /// Offset: 0x014 (R/ ) Contains flags for transmit and receive status, - /// including line errors - uint8_t const volatile line_status; - /// reserved 2 - std::array reserved2; - /// Offset: 0x01C (R/W) 8-bit temporary storage for software - uint8_t volatile scratch_pad; - /// reserved 3 - std::array reserved3; - /// Offset: 0x020 (R/W) Contains controls for the auto-baud feature. - uint32_t volatile autobaud_control; - /// Offset: - uint8_t volatile icr; - /// reserved 4 - std::array reserved4; - /// Offset: 0x028 (R/W) Generates a clock input for the baud rate divider. - uint8_t volatile fractional_divider; - /// reserved 5 - std::array reserved5; - /// Offset: 0x030 (R/W) Turns off UART transmitter for use with software - /// flow control. - uint8_t volatile transmit_enable; -}; - -/// Line control bit fields -namespace uart_line_control { -/// Word Length Select: Reset = 0 -/// - 0x0 5-bit character -/// - 0x1 6-bit character -/// - 0x2 7-bit character -/// - 0x3 8-bit character -static constexpr auto word_length = bit_mask::from<0, 1>(); -/// Stop Bit Select: Reset 0 -/// - 0 1 stop bit. -/// - 1 2 stop bits. (1.5 if UnLCR[1:0]=00).) -static constexpr auto stop = bit_mask::from<2>(); -/// Parity Enable: Reset 0 -/// - 0 Disable parity generation and checking. -/// - 1 Enable parity generation and checking. -static constexpr auto parity_enable = bit_mask::from<3>(); -/// Parity Select 0 -/// - 0x0 Odd parity. Number of 1s in the transmitted character and the -/// attached parity bit will be odd. -/// - 0x1 Even Parity. Number of 1s in the transmitted character and the -/// attached parity bit will be even. -/// - 0x2 Forced 1 stick parity. -/// - 0x3 Forced 0 stick parity. -static constexpr auto parity = bit_mask::from<4, 5>(); -}; // namespace uart_line_control - -/// Interrupt enable bit fields -namespace uart_interrupt_enable { -/// RBR Interrupt Enable. Enables the Receive Data Available interrupt for -/// UARTn: Reset 0 It also controls the Character Receive Time-out -/// interrupt. -/// - 0 Disable the RDA interrupts. -/// - 1 Enable the RDA interrupts. -static constexpr auto receive_interrupt = bit_mask::from<0>(); -}; // namespace uart_interrupt_enable - -/// Interrupt ID bit fields -namespace uart_interrupt_id { -/// Interrupt identification. UnIER[3:1] identifies an interrupt -/// corresponding to the UARTn Rx or TX FIFO. All other combinations of -/// UnIER[3:1] not listed below are reserved (000,100,101,111). -/// - 0x3 1 - Receive Line Status (RLS). -/// - 0x2 2a - Receive Data Available (RDA). -/// - 0x6 2b - Character Time-out Indicator (CTI). -/// - 0x1 3 - THRE Interrupt -static constexpr auto id = bit_mask::from<1, 3>(); -}; // namespace uart_interrupt_id - -/// FIFO control bit fields -namespace uart_fifo_control { -/// FIFO Enable: Reset 0 -/// - 0 UARTn FIFOs are disabled. Must not be used in the application. -/// - 1 Active high enable for both UARTn Rx and TX FIFOs and UnFCR[7:1] -/// access. This bit must be set for proper UART operation. Any transition -/// on this bit will automatically clear the related UART FIFOs. -static constexpr auto fifo_enable = bit_mask::from<0>(); -/// RX FIFO Reset: Reset 0 -/// - 0 No impact on either of UARTn FIFOs. -/// - 1 Writing a logic 1 to UnFCR[1] will clear all bytes in UARTn Rx FIFO, -/// reset the pointer logic. This bit is self-clearing. -static constexpr auto rx_fifo_clear = bit_mask::from<1>(); -/// TX FIFO Reset: Reset 0 -/// - 0 No impact on either of UARTn FIFOs. -/// - 1 Writing a logic 1 to UnFCR[2] will clear all bytes in UARTn TX FIFO, -/// reset the pointer logic. This bit is self-clearing. -static constexpr auto tx_fifo_clear = bit_mask::from<2>(); -/// RX Trigger Level. These two bits determine how many receiver UARTn FIFO -/// characters must be written before an interrupt or DMA request is -/// activated: Reset 0 -/// - 0x0 Trigger level 0 (1 character or 0x01). -/// - 0x1 Trigger level 1 (4 characters or 0x04). -/// - 0x2 Trigger level 2 (8 characters or 0x08). -/// - 0x3 Trigger level 3 (14 characters or 0x0E). -static constexpr auto rx_trigger_level = bit_mask::from<6, 7>(); -}; // namespace uart_fifo_control - -inline uart_reg_t* uart_reg0 = reinterpret_cast(0x4000'C000); -inline uart_reg_t* uart_reg1 = reinterpret_cast(0x4001'0000); -inline uart_reg_t* uart_reg2 = reinterpret_cast(0x4008'8000); -inline uart_reg_t* uart_reg3 = reinterpret_cast(0x4009'C000); -inline uart_reg_t* uart_reg4 = reinterpret_cast(0x400A'4000); -} // namespace hal::lpc40 diff --git a/src/rp/pwm.cpp b/src/pwm.cpp similarity index 100% rename from src/rp/pwm.cpp rename to src/pwm.cpp diff --git a/src/rp/serial.cpp b/src/serial.cpp similarity index 94% rename from src/rp/serial.cpp rename to src/serial.cpp index c37b707..f7fc287 100644 --- a/src/rp/serial.cpp +++ b/src/serial.cpp @@ -131,24 +131,24 @@ void uart::driver_configure(settings const& options) serial::write_t uart::driver_write(std::span in) { - auto uart = get_uart(m_bus); + auto inst = get_uart(m_bus); size_t i = 0; for (; i < in.size_bytes(); ++i) { - if (!uart_is_writable(uart)) + if (!uart_is_writable(inst)) break; - uart_get_hw(uart)->dr = in[i]; + uart_get_hw(inst)->dr = in[i]; } return { in.subspan(0, i) }; } serial::read_t uart::driver_read(std::span out) { - auto uart = get_uart(m_bus); + auto inst = get_uart(m_bus); size_t i = 0; for (; i < out.size_bytes(); ++i) { - while (!uart_is_readable(uart)) + while (!uart_is_readable(inst)) break; - out[i] = (uint8_t)uart_get_hw(uart)->dr; + out[i] = (uint8_t)uart_get_hw(inst)->dr; } return { .data = out.subspan(0, i), .available = uart_is_readable(get_uart(m_bus)), diff --git a/src/rp/spi.cpp b/src/spi.cpp similarity index 100% rename from src/rp/spi.cpp rename to src/spi.cpp diff --git a/src/stm32_generic/i2c.cpp b/src/stm32_generic/i2c.cpp deleted file mode 100644 index c61bdbf..0000000 --- a/src/stm32_generic/i2c.cpp +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace hal::stm32_generic { - -struct i2c_cr1 -{ - /// 0: I2C Peripheral not under reset - /// 1: I2C Peripheral under reset state - static constexpr auto software_reset = hal::bit_mask::from<15>(); - /// 0: Releases SMBA pin high. Alert Response Address Header followed by NACK. - /// 1: Drives SMBA pin low. Alert Response Address Header followed by ACK - static constexpr auto smbus_alert = hal::bit_mask::from<13>(); - /// This bit is set and cleared by software, and cleared by hardware when PEC - /// is transferred or by a START or Stop condition or when PE=0. - /// 0: No PEC transfer - /// 1: PEC transfer - static constexpr auto pec = hal::bit_mask::from<12>(); - /// 0: ACK bit controls the (N)ACK of the current byte being received in the - /// shift register. The PEC bit indicates that current byte in shift register - /// is a PEC. - - /// 1: ACK bit controls the (N)ACK of the next byte which will be received in - /// the shift register. The PEC bit indicates that the next byte in the shift - /// register is a PEC - static constexpr auto pos = hal::bit_mask::from<11>(); - /// This bit is set and cleared by software and cleared by hardware when PE=0 - /// 0: no ack return - /// 1: Acknowledge returned after a byte is received (matched address or data) - static constexpr auto ack_enable = hal::bit_mask::from<10>(); - /// Master mode: - /// 0: no stop generation - /// 1: Stop generation after the current byte transfer or after the current - static constexpr auto stop = hal::bit_mask::from<9>(); - /// Master mode: - /// 0: no start generation - /// 1: repeated start generation - static constexpr auto start = hal::bit_mask::from<8>(); - /// 0: Clock stretching enable - /// 1: Clock stretching disable - static constexpr auto clock_stretch_disable = hal::bit_mask::from<7>(); - /// 0: General call disable Address 0x00 is NACKed - /// 1: General call enable Address 0x00 is ACKed - static constexpr auto general_call_enable = hal::bit_mask::from<6>(); - /// 0: PEC calculation disable - /// 1: PEC calculation enable - static constexpr auto pec_enable = hal::bit_mask::from<5>(); - /// 0: ARP disable - /// 1: ARP enable - static constexpr auto arp_enable = hal::bit_mask::from<4>(); - /// 0: SMBus device - /// 1: SMBus host - static constexpr auto smbus_type = hal::bit_mask::from<3>(); - /// 0: i2c mode - /// 1: SMBus mode - static constexpr auto smbus_mode = hal::bit_mask::from<1>(); - /// 0: peripheral disable - /// 1: peripheral enable - static constexpr auto peripheral_enable = hal::bit_mask::from<0>(); -}; - -struct i2c_cr2 -{ - /// 0: Next DMA EOT is not the last transfer - /// 1: Next DMA EOT is the last transfer - static constexpr auto last_dma = hal::bit_mask::from<12>(); - /// 0: DMA requests disabled - /// 1: DMA request enabled when TxE=1 or RxNE =1 - static constexpr auto dma_en = hal::bit_mask::from<11>(); - /// 0: TxE = 1 or RxNE = 1 does not generate any interrupt. - /// 1: TxE = 1 or RxNE = 1 generates Event Interrupt - static constexpr auto buffer_intr_en = hal::bit_mask::from<10>(); - /// 0: Event interrupt disabled - /// 1: Event interrupt enabled - /// This interrupt is generated when: - /// - SB = 1 (Master) - /// - ADDR = 1 (Master/Slave) - /// - ADD10= 1 (Master) - /// - STOPF = 1 (Slave) - /// - BTF = 1 with no TxE or RxNE event - /// - TxE event to 1 if ITBUFEN = 1 - /// - RxNE event to 1if ITBUFEN = 1 - static constexpr auto event_intr_en = hal::bit_mask::from<9>(); - /// 0: Error interrupt disabled - /// 1: Error interrupt enabled - /// This interrupt is generated when: - /// - BERR = 1 - /// - ARLO = 1 - /// - AF = 1 - /// - OVR = 1 - /// - PECERR = 1 - /// - TIMEOUT = 1 - /// - SMBALERT = 1 - static constexpr auto error_intr_en = hal::bit_mask::from<8>(); - /// APB clock frequency - /// how many MHz - static constexpr auto apb_frequency = hal::bit_mask::from<5, 0>(); -}; -struct i2c_sr1 -{ - static constexpr auto smb_alert = hal::bit_mask::from<15>(); - /// 0: No timeout error - /// 1: SCL remained LOW for 25 ms (Timeout) - /// or - /// Master cumulative clock low extend time more than 10 ms (Tlow:mext) - /// or - /// Slave cumulative clock low extend time more than 25 ms (Tlow:sext) - /// Cleared by software writing 0, or by hardware when PE=0 - static constexpr auto timeout_error = hal::bit_mask::from<14>(); - /// 0: no PEC error: receiver returns ACK after PEC reception (if ACK=1) - /// 1: PEC error: receiver returns NACK after PEC reception (whatever ACK) - /// Cleared by software writing 0, or by hardware when PE=0 - static constexpr auto pec_error = hal::bit_mask::from<12>(); - /// 0: No overrun/underrun - /// 1: Overrun or underrun - /// Cleared by software writing 0, or by hardware when PE=0 - static constexpr auto over_under_run = hal::bit_mask::from<11>(); - /// 0: No acknowledge failure - /// 1: Acknowledge failure - /// Cleared by software writing 0, or by hardware when PE=0 - static constexpr auto ack_failure = hal::bit_mask::from<10>(); - /// 0: No Arbitration Lost detected - /// 1: Arbitration Lost detected - /// Cleared by software writing 0, or by hardware when PE=0 - static constexpr auto arbitration_lost = hal::bit_mask::from<9>(); - /// 0: No misplaced Start or Stop condition - /// 1: Misplaced Start or Stop condition - /// Cleared by software writing 0, or by hardware when PE=0 - static constexpr auto bus_error = hal::bit_mask::from<8>(); - /// 0: Data register not empty - /// 1: Data register empty - /// Cleared by software writing to the DR register or by hardware after a - /// start or a stop condition or when PE=0 - static constexpr auto tx_empty = hal::bit_mask::from<7>(); - /// 0: Data register empty - /// 1: Data register not empty - /// Cleared by software reading or writing the DR register or by hardware when - /// PE=0.RxNE is not set in case of ARLO event - static constexpr auto rx_not_empty = hal::bit_mask::from<6>(); - /// used in slave mode - static constexpr auto stop_detection = hal::bit_mask::from<4>(); - /// 0: No ADD10 event occurred - /// 1: Master has sent first address byte (header) - /// Cleared by software by either a read or write in the DR register or by - /// Cleared by software reading the SR1 register followed by a write in the DR - /// register of the second address byte, or by hardware when PE=0. - static constexpr auto addr10 = hal::bit_mask::from<3>(); - /// 0: Data byte transfer not done - /// 1: Data byte transfer succeeded - /// Cleared by software by either a read or write in the DR register or by - /// hardware after a start or a stop condition in transmission or when PE=0. - static constexpr auto byte_transfered_finish = hal::bit_mask::from<2>(); - /// This bit is cleared by software reading SR1 register followed reading SR2, - /// or by hardware when PE=0. This interrupt is generated when: - /// 0: No end of address transmission - /// 1: End of address transmission - static constexpr auto addr = hal::bit_mask::from<1>(); - /// Cleared by software by reading the SR1 register followed by writing the DR - /// register, or by hardware when peripheral_enable=0 - /// 0: No Start condition - /// 1: Start condition generated. - static constexpr auto start = hal::bit_mask::from<0>(); -}; -struct i2c_sr2 -{ - - /// 0: No General Call - /// 1: General Call Address received when ENGC=1 - /// Cleared by hardware after a Stop condition or repeated Start condition, or - /// when PE=0 - static constexpr auto general_call_addr = hal::bit_mask::from<4>(); - /// 0: No communication on the bus - /// 1: Communication ongoing on the bus - /// It indicates a communication in progress on the bus. This information is - /// still updated when the interface is disabled (PE=0). - static constexpr auto trans_reciever = hal::bit_mask::from<2>(); - /// 0: No communication on the bus - /// 1: Communication ongoing on the bus - /// It indicates a communication in progress on the bus. This information is - /// still updated when the interface is disabled (PE=0). - static constexpr auto bus_busy = hal::bit_mask::from<1>(); - /// 0: Slave Mode - /// 1: Master Mode - /// Cleared by hardware after detecting a Stop condition on the bus or a loss - /// of arbitration (ARLO=1), or by hardware when PE=0. - static constexpr auto master = hal::bit_mask::from<0>(); -}; -struct i2c_ccr -{ - /// 0: Slow mode mode I2C - /// 1: Fast mode mode I2C - static constexpr auto i2c_mode_sel = hal::bit_mask::from<14>(); - /// 0: Fm mode tlow/thigh = 2 - /// 1: Fm mode tlow/thigh = 16/9 (see CCR) - static constexpr auto duty_cycle = hal::bit_mask::from<14>(); - /// refer to CH 18.6.8 in the User manual () for equation - static constexpr auto ccr = hal::bit_mask::from<11, 0>(); -}; -struct i2c_filter -{ - /// 0 : analog filter enable - /// 1 : analog filter disable - static constexpr auto analog = hal::bit_mask::from<4>(); - - /// Digital noise filter enabled and filtering capability up to n * TPCLK1 - static constexpr auto digital_filter = hal::bit_mask::from<3, 0>(); -}; - -/// i2c register map (RM0383 CH 18.6.11) -struct i2c_reg_t -{ - /// Control reg 1 (CH 18.6.1) - hal::u32 volatile cr1; - /// Control reg 2 (CH 18.6.2) - hal::u32 volatile cr2; - /// Own address reg 1 (CH 18.6.3) - hal::u32 volatile oar1; - /// Own address reg 2 (CH 18.6.4) - hal::u32 volatile oar2; - /// Data reg (CH 18.6.5) - hal::u32 volatile data_reg; - /// Status reg 1 (CH 18.6.6) - hal::u32 volatile sr1; - /// Status reg 2 (CH 18.6.7) - hal::u32 volatile sr2; - /// Clock control reg (CH 18.6.8) - hal::u32 volatile ccr; - /// Rise time reg (CH 18.6.9) - hal::u32 volatile trise; - /// Noise filter reg (CH 18.6.10) - hal::u32 volatile filter; -}; -namespace { -inline i2c_reg_t* get_i2c_reg(void* p_i2c) -{ - return reinterpret_cast(p_i2c); -} - -/** - * @brief Reads the status register - * - * just trying to make the code more readable - * - * @param p_i2c_reg Peripheral register pointers - */ -inline void read_status_reg2(i2c_reg_t const* p_i2c_reg) -{ - p_i2c_reg->sr2; -} -} // namespace - -i2c::i2c(void* p_i2c) -{ - m_i2c = p_i2c; -} - -i2c::i2c() -{ - m_i2c = nullptr; -} - -i2c::~i2c() -{ - auto i2c_reg = get_i2c_reg(m_i2c); - bit_modify(i2c_reg->cr1).clear(i2c_cr1::peripheral_enable); - bit_modify(i2c_reg->cr1).set(i2c_cr1::software_reset); - bit_modify(i2c_reg->cr1).clear(i2c_cr1::software_reset); -} - -void i2c::configure(hal::i2c::settings const& p_settings, hertz p_frequency) -{ - constexpr auto slow_mode_max_speed = 100_Hz; - auto const freq = static_cast(p_frequency); - - if (2_MHz > freq || freq > 50_MHz) { - safe_throw(hal::argument_out_of_domain(this)); - } - auto i2c_reg = get_i2c_reg(m_i2c); - bit_modify(i2c_reg->cr1).set(i2c_cr1::software_reset); - bit_modify(i2c_reg->cr1).clear(i2c_cr1::software_reset); - - bit_modify(i2c_reg->filter) - .set(i2c_filter::digital_filter) - .clear(i2c_filter::analog); - - /// I2C communication speed, fahb / (2 * ccr). The real frequency may - /// differ due to the analog noise filter input delay. - /// CH 18.6.8 in RM0383 (stm32f411 user manual) - - u16 const ccr_value = - freq / (2 * static_cast(p_settings.clock_rate)); - if (p_settings.clock_rate > slow_mode_max_speed) { - if (ccr_value > 4) { - bit_modify(i2c_reg->ccr) - .set(i2c_ccr::i2c_mode_sel) - .insert(ccr_value); - } else { - safe_throw(hal::argument_out_of_domain(this)); - } - } else { - bit_modify(i2c_reg->ccr) - .clear(i2c_ccr::i2c_mode_sel) - .insert(ccr_value); - } - - auto const ahb_freq = static_cast(p_frequency / 1_MHz); - bit_modify(i2c_reg->cr2) - .set(i2c_cr2::buffer_intr_en) - .set(i2c_cr2::event_intr_en) - .set(i2c_cr2::error_intr_en) - .insert(ahb_freq); - bit_modify(i2c_reg->cr1) - .set(i2c_cr1::peripheral_enable) - .set(i2c_cr1::ack_enable) - .clear(i2c_cr1::smbus_mode); -} - -void i2c::handle_i2c_event() noexcept -{ - auto i2c_reg = get_i2c_reg(m_i2c); - auto& status = i2c_reg->sr1; - auto& data = i2c_reg->data_reg; - - // [ ⚠️ WARNING] SOMETIMES STM32 I2C peripheral enable will just self reset. - // This will cause the i2c peripheral to misbehave, so the code enables the - // peripheral at each interrupt call to ensure this never happens. - bit_modify(i2c_reg->cr1).set(i2c_cr1::peripheral_enable); - if (bit_extract(status)) { - if (!m_data_out.empty()) { - data = to_8_bit_address(m_address, i2c_operation::write); - m_state = transmission_state::transmitter; - } else { - data = to_8_bit_address(m_address, i2c_operation::read); - m_state = transmission_state::reciever; - } - return; - } - - if (bit_extract(status)) { - if (m_state == transmission_state::reciever) { - switch (m_data_in.size()) { - case 1: { - bit_modify(i2c_reg->cr1).clear(i2c_cr1::ack_enable); - i2c_reg->sr2; - break; - } - case 2: { - bit_modify(i2c_reg->cr1).clear(i2c_cr1::ack_enable).set(i2c_cr1::pos); - read_status_reg2(i2c_reg); - m_data_in[1] = data; - m_data_in[0] = data; - break; - } - default: { - read_status_reg2(i2c_reg); - } - } - } else { - read_status_reg2(i2c_reg); - } - return; - } - - if (bit_extract(status)) { - if (!m_data_out.empty()) { - data = m_data_out[0]; - m_data_out = m_data_out.subspan(1); - } else { - if (!m_data_in.empty()) { - bit_modify(i2c_reg->cr1).set(i2c_cr1::start); - } else { - bit_modify(i2c_reg->cr1).clear(i2c_cr1::ack_enable); - bit_modify(i2c_reg->cr1).set(i2c_cr1::stop); - m_state = transmission_state::free; - } - } - return; - } - - if (bit_extract(status)) { - switch (m_data_in.size()) { - case 2: { - bit_modify(i2c_reg->cr1).clear(i2c_cr1::ack_enable); - m_data_in[0] = data; - bit_modify(i2c_reg->cr1).set(i2c_cr1::stop); - m_data_in[1] = data; - m_data_in[2] = data; - m_state = transmission_state::free; - - break; - } - - case 1: { - m_data_in[0] = data; - bit_modify(i2c_reg->cr1).set(i2c_cr1::stop); - m_state = transmission_state::free; - - break; - } - - case 0: { - bit_modify(i2c_reg->cr1).set(i2c_cr1::stop).clear(i2c_cr1::ack_enable); - [[maybe_unused]] auto a = data; - break; - } - - default: { - m_data_in[0] = data; - m_data_in = m_data_in.subspan(1); - } - } - } -} - -void i2c::handle_i2c_error() noexcept -{ - auto i2c_reg = get_i2c_reg(m_i2c); - auto& status = i2c_reg->sr1; - - if (bit_extract(status)) { - m_status = error_state::arbitration_lost; - } - - if (bit_extract(status)) { - /// we don't support stm32's timeout function, but have an external io - /// waiter - bit_modify(i2c_reg->sr1).clear(i2c_sr1::timeout_error); - } - - if (bit_extract(status)) { - m_status = error_state::no_such_device; - bit_modify(i2c_reg->sr1).clear(); - } - - if (bit_extract(status)) { - m_status = error_state::io_error; - bit_modify(i2c_reg->sr1).clear(); - } -} -void i2c::transaction(hal::byte p_address, - std::span p_data_out, - std::span p_data_in, - hal::function_ref p_timeout) -{ - m_status = error_state::no_error; - m_address = p_address; - m_data_out = p_data_out; - m_data_in = p_data_in; - m_state = transmission_state::transmitter; - auto i2c_reg = get_i2c_reg(m_i2c); - bit_modify(i2c_reg->cr1).set(i2c_cr1::ack_enable); - bit_modify(i2c_reg->cr1).set(i2c_cr1::peripheral_enable); - bit_modify(i2c_reg->cr1).clear(i2c_cr1::stop); - bit_modify(i2c_reg->cr1).set(i2c_cr1::start); - while (m_state != transmission_state::free) { - try { - p_timeout(); - handle_i2c_error(); - switch (m_status) { - case error_state::no_error: { - break; - } - case error_state::no_such_device: { - safe_throw(hal::no_such_device(m_address, this)); - break; - } - case error_state::io_error: { - safe_throw(hal::io_error(this)); - break; - } - case error_state::arbitration_lost: { - while (bit_extract(i2c_reg->sr2)) { - continue; - } - bit_modify(i2c_reg->cr1) - .set(i2c_cr1::peripheral_enable) - .set(i2c_cr1::ack_enable) - .clear(i2c_cr1::stop); - bit_modify(i2c_reg->cr1).set(i2c_cr1::start); - break; - } - } - - } catch (...) { - // The expected exception is hal::timed_out, but it could be something - // else. Let rethrow the exception so the caller handle it. - // A better option here would be to use a std::scope_failure handler. - bit_value(i2c_reg->cr1).clear(i2c_cr1::peripheral_enable); - throw; - } - }; -} -} // namespace hal::stm32_generic diff --git a/src/stm32_generic/pwm.cpp b/src/stm32_generic/pwm.cpp deleted file mode 100644 index bf6f09f..0000000 --- a/src/stm32_generic/pwm.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "timer.hpp" - -namespace hal::stm32_generic { - -namespace { -void setup_channel(timer_reg_t* p_reg, u8 p_channel, bool p_is_advanced) -{ - static constexpr auto main_output_enable = bit_mask::from<15>(); - static constexpr auto ossr = bit_mask::from<11>(); - - u8 const start_pos = (p_channel - 1) * 4; - auto const cc_enable = bit_mask::from(start_pos); - auto const cc_polarity = bit_mask::from(start_pos + 1); - - bit_modify(p_reg->cc_enable_register).set(cc_enable); - bit_modify(p_reg->cc_enable_register).clear(cc_polarity); - - if (p_is_advanced) { - bit_modify(p_reg->break_and_deadtime_register) - .clear(ossr) - .set(main_output_enable); // complementary channel stuff - } -} - -u32 volatile* setup(timer_reg_t* p_reg, int p_channel, bool p_is_advanced) -{ - static constexpr auto clock_division = bit_mask::from<8, 9>(); - static constexpr auto edge_aligned_mode = bit_mask::from<5, 6>(); - static constexpr auto direction = bit_mask::from<4>(); - - static constexpr auto output_compare_odd = bit_mask::from<4, 6>(); - static constexpr auto output_compare_even = bit_mask::from<12, 14>(); - static constexpr auto channel_output_select_odd = bit_mask::from<0, 1>(); - static constexpr auto channel_output_select_even = bit_mask::from<8, 9>(); - - static constexpr auto counter_enable = bit_mask::from<0>(); - static constexpr auto auto_reload_preload_enable = bit_mask::from<7>(); - static constexpr auto odd_channel_preload_enable = bit_mask::from<3>(); - static constexpr auto even_channel_preload_enable = bit_mask::from<11>(); - static constexpr auto ug_bit = bit_mask::from<0>(); - - // The PWM_MODE 1 makes it such that output will be high when Counter < CCR - constexpr auto pwm_mode_1 = 0b110U; - constexpr auto set_output = 0b00U; - - bit_modify(p_reg->control_register) - .insert(0b00U) - .insert(0b00U) - .clear(direction); - u32 volatile* compare_register = nullptr; - switch (p_channel) { - case 1: - // Preload enable must be done for corresponding OCxPE - bit_modify(p_reg->capture_compare_mode_register) - .insert(pwm_mode_1) - .insert(set_output) - .set(odd_channel_preload_enable); - compare_register = &p_reg->capture_compare_register; - break; - - case 2: - bit_modify(p_reg->capture_compare_mode_register) - .insert(pwm_mode_1) - .insert(set_output) - .set(even_channel_preload_enable); - compare_register = &p_reg->capture_compare_register_2; - break; - - case 3: - bit_modify(p_reg->capture_compare_mode_register_2) - .insert(pwm_mode_1) - .insert(set_output) - .set(odd_channel_preload_enable); - compare_register = &p_reg->capture_compare_register_3; - break; - - case 4: - bit_modify(p_reg->capture_compare_mode_register_2) - .insert(pwm_mode_1) - .insert(set_output) - .set(even_channel_preload_enable); - compare_register = &p_reg->capture_compare_register_4; - break; - default: - std::unreachable(); - } - - setup_channel(p_reg, p_channel, p_is_advanced); - bit_modify(p_reg->event_generator_register).set(ug_bit); - bit_modify(p_reg->control_register).set(counter_enable); - bit_modify(p_reg->control_register).set(auto_reload_preload_enable); - - // If the desired frequency is really low, and 16 bits are not enough - // to represent that, the prescalar value can be increased. Example: - // if prescalar = 2, 2 clock ticks would occur before incrementing - // the counter by 1. - p_reg->prescale_register = 0x0U; - - // The counter increments every clock cycle, and compares its value - // to the CCR to check whether the output should be high or low - p_reg->counter_register = 0x0U; - - // The ARR register is the top, once the counter reaches the ARR, - // it starts counting again. ARR can be increased on decreased - // depending on frequency. - p_reg->auto_reload_register = 0xFFFF; - - return compare_register; -} - -u32 volatile* common_setup(pwm_channel_info p_settings, timer_reg_t* p_reg) -{ - if (p_settings.channel <= 4) { - u32 volatile* p_compare_register_addr = - setup(p_reg, p_settings.channel, p_settings.is_advanced); - return p_compare_register_addr; - } else { - hal::safe_throw(hal::operation_not_supported(nullptr)); - } -} -} // namespace - -pwm::pwm(unsafe, void* p_reg, pwm_channel_info p_settings) - : m_reg(p_reg) -{ - timer_reg_t* reg = get_timer_reg(m_reg); - m_compare_register_addr = common_setup(p_settings, reg); -} - -void pwm::initialize(unsafe, void* p_reg, pwm_channel_info p_settings) -{ - m_reg = p_reg; - timer_reg_t* reg = get_timer_reg(m_reg); - m_compare_register_addr = common_setup(p_settings, reg); -} - -u32 pwm::frequency(u32 p_input_clock_frequency) -{ - timer_reg_t* reg = get_timer_reg(m_reg); - - // See page 419 in RM0008.pdf to find this equation: - // - // Bits 15:0 PSC[15:0]: Prescaler value - // The counter clock frequency CK_CNT is equal to fCK_PSC / (PSC[15:0] + 1) - // - auto const prescale_value = reg->prescale_register + 1; - auto const prescaled_clock = p_input_clock_frequency / prescale_value; - auto const final_frequency = prescaled_clock / reg->auto_reload_register; - - return final_frequency; -} - -void pwm::duty_cycle(u16 p_duty_cycle) -{ - // the output changes from high to low when the counter > ccr, therefore, we - // simply make the CCR equal to the required duty cycle fraction of the ARR - // value. - timer_reg_t* reg = get_timer_reg(m_reg); - - auto const reload_value = static_cast(reg->auto_reload_register); - auto const upscaled_value = reload_value * p_duty_cycle; - auto const normalized_ccr_value = - upscaled_value / std::numeric_limits::max(); - - *m_compare_register_addr = normalized_ccr_value; -} - -pwm_group_frequency::pwm_group_frequency(hal::unsafe, void* p_reg) - : m_reg(p_reg) -{ -} - -void pwm_group_frequency::set_group_frequency(pwm_timer_frequency p_frequency) -{ - timer_reg_t* reg = get_timer_reg(m_reg); - - // Calculate new frequency - auto const [frequency, clock_frequency] = p_frequency; - if (frequency >= clock_frequency) { - safe_throw(hal::operation_not_supported(this)); - } - - auto const possible_prescaler_value = - (clock_frequency / (frequency * std::numeric_limits::max())); - - u16 prescale = 0; - u16 auto_reload = 0xFFFF; - - if (possible_prescaler_value > 0) { - // If the frequency is low enough, the prescale can be increased, which - // will take more time to reach the ARR value - prescale = possible_prescaler_value; - } else { - // The frequency is too high, so the ARR value needs to be reduced. - auto_reload = static_cast(clock_frequency / frequency); - } - - // =========================================================================== - // Updating all PWM duty cycles - // =========================================================================== - - // NOTE: The capture_compare_register only contain 16-bits of information so - // we can legally cast them to u16 and lose no information. - auto const capture1 = static_cast(reg->capture_compare_register); - auto const capture2 = static_cast(reg->capture_compare_register_2); - auto const capture3 = static_cast(reg->capture_compare_register_3); - auto const capture4 = static_cast(reg->capture_compare_register_4); - auto const previous_auto_reload = reg->auto_reload_register; - - // Duty_new = (Duty_prev / AutoReload_prev) * AutoReload_new; - // - // Can also be written as: - // - // Duty_new = (Duty_prev * AutoReload_new) / AutoReload_prev; - // - // We choose the 2nd option because we can perform the operations with - // integers without loss of precision. - - auto const new_capture1_upscaled = static_cast(capture1 * auto_reload); - auto const new_capture2_upscaled = static_cast(capture2 * auto_reload); - auto const new_capture3_upscaled = static_cast(capture3 * auto_reload); - auto const new_capture4_upscaled = static_cast(capture4 * auto_reload); - - auto const new_capture1 = (new_capture1_upscaled / previous_auto_reload); - auto const new_capture2 = (new_capture2_upscaled / previous_auto_reload); - auto const new_capture3 = (new_capture3_upscaled / previous_auto_reload); - auto const new_capture4 = (new_capture4_upscaled / previous_auto_reload); - - reg->capture_compare_register = new_capture1; - reg->capture_compare_register_2 = new_capture2; - reg->capture_compare_register_3 = new_capture3; - reg->capture_compare_register_4 = new_capture4; - - // Set the prescale & auto reload register - reg->prescale_register = prescale; - reg->auto_reload_register = auto_reload; -} -} // namespace hal::stm32_generic diff --git a/src/stm32_generic/quadrature_encoder.cpp b/src/stm32_generic/quadrature_encoder.cpp deleted file mode 100644 index d648f9f..0000000 --- a/src/stm32_generic/quadrature_encoder.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include -#include - -#include "timer.hpp" - -namespace hal::stm32_generic { - -void setup_channel(int channel, timer_reg_t* p_reg) -{ - - constexpr auto odd_channel_input_mode = bit_mask::from<0, 1>(); - constexpr auto odd_channel_filter_select = bit_mask::from<4, 7>(); - - constexpr auto even_channel_input_mode = bit_mask::from<8, 9>(); - constexpr auto even_channel_filter_select = bit_mask::from<12, 15>(); - - constexpr auto input_select = 0b01U; - constexpr auto input_capture_filter = 0b0000U; - - // Select the TI1 and TI2 polarity by programming the CC1P and CC2P bits in - // the TIMx_CCER - // register. When needed, the user can program the input filter as well. - // find out which pin is what channel - switch (channel) { - case 1: - bit_modify(p_reg->capture_compare_mode_register) - .insert(input_select) - .insert(input_capture_filter); - break; - case 2: - bit_modify(p_reg->capture_compare_mode_register) - .insert(input_select) - .insert(input_capture_filter); - break; - default: - std::unreachable(); - } -} - -void setup_enable_register(int channel, timer_reg_t* p_reg) -{ - // The polarity pits is the second bit in a set of 4 bits per channel. - // Therefore if it is channel 1-> bit 1, channel 2-> bit->5. Application Note - // Pg: 353 - auto const polarity_start_pos = ((channel - 1) * 4) + 1; - - // Similar to polarity bits, capture_enable bits are the first bit in a set of - // 4 bits per channel. Application Note Pg: 353. - auto const input_capture_enable = ((channel - 1) * 4); - - auto const input_start_pos = bit_mask::from(input_capture_enable); - auto const polarity_inverted = bit_mask::from(polarity_start_pos); - - bit_modify(p_reg->cc_enable_register).clear(polarity_inverted); - bit_modify(p_reg->cc_enable_register).set(input_start_pos); -} - -quadrature_encoder::quadrature_encoder(hal::unsafe, - encoder_channels channels, - void* p_reg, - u32 p_pulses_per_rotation) -{ - initialize(hal::unsafe{}, channels, p_reg, p_pulses_per_rotation); -} -quadrature_encoder::quadrature_encoder(hal::unsafe) -{ -} -void quadrature_encoder::initialize(unsafe, - encoder_channels channels, - void* p_reg, - u32 p_pulses_per_rotation) -{ - m_reg = p_reg; - m_pulses_per_rotation = p_pulses_per_rotation; - timer_reg_t* timer_register = get_timer_reg(m_reg); - constexpr auto set_encoder_mode = bit_mask::from<0, 2>(); - // encoder counts up/down on only TI2FP1 level - constexpr auto encoder_mode_3 = 0b010U; - - setup_channel(channels.channel_a, timer_register); - setup_channel(channels.channel_b, timer_register); - setup_enable_register(channels.channel_a, timer_register); - setup_enable_register(channels.channel_b, timer_register); - timer_register->auto_reload_register = 0xFFFF; // Set max counter value - timer_register->counter_register = 0x8000; // Start at middle value - bit_modify(timer_register->peripheral_control_register) - .insert(encoder_mode_3); - constexpr auto counter_enable = bit_mask::from<0>(); - bit_modify(timer_register->control_register).set(counter_enable); -} -quadrature_encoder::read_t quadrature_encoder::driver_read() -{ - read_t reading; - timer_reg_t* timer_register = get_timer_reg(m_reg); - i32 diff_pulses = static_cast(timer_register->counter_register) - - 0x8000; // difference from start pos - // pulses * degrees / pulses = degrees. - reading.angle = - static_cast(diff_pulses) * (360 / m_pulses_per_rotation); - return reading; -} -} // namespace hal::stm32_generic diff --git a/src/stm32_generic/spi.cpp b/src/stm32_generic/spi.cpp deleted file mode 100644 index 8c9893a..0000000 --- a/src/stm32_generic/spi.cpp +++ /dev/null @@ -1,290 +0,0 @@ -#include - -#include -#include -#include - -namespace hal::stm32_generic { - -namespace { -struct spi_reg_t -{ - /*!< Offset: 0x000 Control Register 1 (R/W) */ - uint32_t volatile cr1; - /*!< Offset: 0x004 Control Register 2 (R/W) */ - uint32_t volatile cr2; - /*!< Offset: 0x008 Status Register (R/W) */ - uint32_t volatile sr; - /*!< Offset: 0x00C Data Register (R/W) */ - uint32_t volatile dr; - /*!< Offset: 0x010 CRC polynomial register (R/ ) */ - uint32_t const volatile crcpr; - /*!< Offset: 0x014 RX CRC register (R/W) */ - uint32_t volatile rxcrcr; - /*!< Offset: 0x018 TX CRC Register (R/W) */ - uint32_t volatile txcrcr; - /*!< Offset: 0x01C configuration register (R/W) */ - uint32_t volatile i2scfgr; - /*!< Offset: 0x020 prescaler register (R/W) */ - uint32_t volatile i2spr; -}; - -/// SPI Control Register 1 -struct control_register1 -{ - /// 0: first clock transistion is the first capture edge - /// 1: second clock transition is the first data capture edge - static constexpr auto clock_phase = bit_mask::from<0>(); - - /// 0: clock to 0 when idle - /// 1: clock to 1 when idle - static constexpr auto clock_polarity = bit_mask::from<1>(); - - /// 0: slave, 1: master - static constexpr auto master_selection = bit_mask::from<2>(); - - /// baudrate control: sets the clock rate to: - /// (peripheral clock frequency)/2**(n+1) - static constexpr auto baud_rate_control = bit_mask::from<5, 3>(); - - /// Peripheral Enable - /// 0: disable, 1: enable - static constexpr auto enable = bit_mask::from<6>(); - - /// Frame Format - /// 0: msb transmitted first - /// 1: lsb tranmitted first - static constexpr auto lsb_first = bit_mask::from<7>(); - - /// internal slave select - static constexpr auto internal_slave_select = bit_mask::from<8>(); - - /// Software slave management - /// 0: disable, 1: enable - static constexpr auto software_slave_management = bit_mask::from<9>(); - - /// Recieve only - /// 0: Full Duplex, 1: Output disable - static constexpr auto rx_only = bit_mask::from<10>(); - - /// Data frame format - /// 0: 8-bits, 1: 16-bit - static constexpr auto data_frame_format = bit_mask::from<11>(); - - /// CRC transfer next - /// 0: No CRC phase, 1: transfer CRC next - static constexpr auto crc_transfer_next = bit_mask::from<12>(); - - /// CRC enable - /// 0: disable, 1: enable - static constexpr auto crc_enable = bit_mask::from<13>(); - - /// Output enable in bidirectional mode - /// 0: output disabled, 1: output enable - static constexpr auto bidirectional_output_enable = bit_mask::from<14>(); - - /// Bidirectional data mode enable - /// 0: full-duplex, 1: half-duplex - static constexpr auto bidirectional_mode_enable = bit_mask::from<15>(); -}; - -/// SPI Control Register 2 -struct control_register2 -{ - /// Rx buffer DMA enable - static constexpr auto rx_dma_enable = bit_mask::from<0>(); - - /// Tx buffer DMA enable - static constexpr auto tx_dma_enable = bit_mask::from<1>(); - - /// Slave select output enable - /// 0: use a GPIO, 1: use the NSS pin - static constexpr auto slave_select_output_enable = bit_mask::from<2>(); - - /// Frame format - /// 0: Motorola mode, 1: TI mode - static constexpr auto frame_format = bit_mask::from<4>(); - - /// Error interupt enable - static constexpr auto error_interrupt_enable = bit_mask::from<5>(); - - /// Rx buffer empty interrupt enable - static constexpr auto rx_buffer_empty_interrupt_enable = bit_mask::from<6>(); - - /// Tx buffer empty interrupt enable - static constexpr auto tx_buffer_empty_interrupt_enable = bit_mask::from<7>(); -}; - -/// SPI Status Register -struct status_register -{ - /// Recieve buffer not empty - static constexpr auto rx_buffer_not_empty = bit_mask::from<0>(); - - /// Transmit buffer not empty - static constexpr auto tx_buffer_empty = bit_mask::from<1>(); - - /// Channel side (i2s only) - /// 0: left has been transmitted/received - /// 1: right has been transmitted/received - [[maybe_unused]] static constexpr auto i2s_channel_side = bit_mask::from<2>(); - - /// Underrun flag - [[maybe_unused]] static constexpr auto underrun_flag = bit_mask::from<3>(); - - /// CRC error flag - [[maybe_unused]] static constexpr auto crc_error_flag = bit_mask::from<4>(); - - /// Mode fault flag - [[maybe_unused]] static constexpr auto mode_fault_flag = bit_mask::from<5>(); - - /// Overrun flag - [[maybe_unused]] static constexpr auto overrun_flag = bit_mask::from<6>(); - - /// Busy flag - static constexpr auto busy_flag = bit_mask::from<7>(); - - /// frame format error flag - [[maybe_unused]] static constexpr auto frame_format_error_flag = - bit_mask::from<8>(); -}; - -/** - * @brief Convert a void* to an spi_reg_t for use in the driver. - * - * @param p_address - the address of the peripheral. If the address is outside - * of valid memory, then the driver will trigger an ARM Cortex Memory Fault - * Exception. If the address points to valid memory that is not an spi - * peripheral, then using the result of this function is UB. - * @return spi_reg_t& - reference to an spi register map pointed to by p_address - */ -spi_reg_t& to_reg(void* p_address) -{ - return *reinterpret_cast(p_address); -} - -inline bool busy(spi_reg_t& p_reg) -{ - return bit_extract(p_reg.sr); -} -inline bool tx_empty(spi_reg_t& p_reg) -{ - return bit_extract(p_reg.sr); -} -inline bool rx_not_empty(spi_reg_t& p_reg) -{ - return bit_extract(p_reg.sr); -} -} // namespace - -spi::spi(hal::unsafe, void* p_peripheral_address) - : m_peripheral_address(p_peripheral_address) -{ -} - -spi::~spi() -{ - auto& reg = to_reg(m_peripheral_address); - bit_modify(reg.cr1).clear(); -} - -void spi::configure(hal::spi::settings const& p_settings, - hal::hertz p_peripheral_clock_speed) -{ - using namespace hal::literals; - - auto& reg = to_reg(m_peripheral_address); - - auto const clock_divider = p_peripheral_clock_speed / p_settings.clock_rate; - auto prescaler = static_cast(clock_divider); - - if (prescaler <= 1) { - prescaler = 2; - } else if (prescaler > 256) { - hal::safe_throw(hal::operation_not_supported(this)); - } - - uint16_t baud_control = 15 - std::countl_zero(prescaler); - if (std::has_single_bit(prescaler)) { - baud_control--; - } - - bit_modify(reg.cr2) - .clear() - .clear() - // We set `slave_select_output_enable` because it is required for master - // mode to work. - .set() - .clear() - .clear() - .clear() - .clear(); - - bit_modify(reg.cr1) - .clear() - .clear() - .clear() - .clear() - .clear() - .clear() - .clear() - .insert(baud_control) - .insert(p_settings.clock_phase) - .insert(p_settings.clock_polarity) - .set() // same as disabled - .set() - .set() - .set(); -} - -void spi::transfer(std::span p_data_out, - std::span p_data_in, - hal::byte p_filler) -{ - auto& reg = to_reg(m_peripheral_address); - size_t max_length = std::max(p_data_in.size(), p_data_out.size()); - - // NOTE: This is a paranoid check to determine that there is no bus activity - // before proceeding - while (busy(reg)) { - continue; - } - - // The stm's spi driver needs to be internally told that it is selecting a - // device before it will emit anything on the pins. This will control the NSS - // pin if it is selected, otherwise, its just an internal enable signal. - bit_modify(reg.cr1).clear(); - - for (size_t index = 0; index < max_length; index++) { - hal::byte byte = 0; - - if (index < p_data_out.size()) { - byte = p_data_out[index]; - } else { - byte = p_filler; - } - - while (not tx_empty(reg)) { - continue; - } - - reg.dr = byte; - - while (not rx_not_empty(reg)) { - continue; - } - - byte = static_cast(reg.dr); - if (index < p_data_in.size()) { - p_data_in[index] = byte; - } - } - - bit_modify(reg.cr1).set(); - - // Wait for bus activity to cease before leaving the function - while (busy(reg)) { - continue; - } -} -} // namespace hal::stm32_generic diff --git a/src/stm32_generic/timer.cpp b/src/stm32_generic/timer.cpp deleted file mode 100644 index b819933..0000000 --- a/src/stm32_generic/timer.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include - -#include "timer.hpp" - -namespace hal::stm32_generic { - -namespace { - -void setup(timer_reg_t* p_reg) -{ - static constexpr auto auto_reload_preload_enable = hal::bit_mask::from(7); - static constexpr auto one_pulse_mode = hal::bit_mask::from(3); - static constexpr auto update_request_source = hal::bit_mask::from(2); - static constexpr auto update_interrupt_enable = hal::bit_mask::from(0); - - bit_modify(p_reg->control_register) - .set(auto_reload_preload_enable) - .set(one_pulse_mode) - .set(update_request_source); - - bit_modify(p_reg->interrupt_enable_register).set(update_interrupt_enable); -} - -} // namespace - -timer::timer(hal::unsafe) -{ -} - -bool timer::is_running() -{ - constexpr auto counter_enable = hal::bit_mask::from(0); - auto reg = get_timer_reg(m_reg); - - return (bit_extract(reg->control_register)); -} - -void timer::cancel() -{ - constexpr auto counter_enable = hal::bit_mask::from(0); - auto reg = get_timer_reg(m_reg); - - bit_modify(reg->control_register).clear(counter_enable); -} - -void timer::schedule(hal::time_duration p_delay, u32 p_timer_clock_frequency) -{ - timer::cancel(); - - constexpr auto counter_enable = hal::bit_mask::from(0); - constexpr auto update_generation = hal::bit_mask::from(0); - constexpr auto prescaler = hal::bit_mask::from(0, 15); - constexpr auto auto_reload_value = hal::bit_mask::from(0, 15); - constexpr auto counter = hal::bit_mask::from(0, 15); - - auto reg = get_timer_reg(m_reg); - - constexpr u32 scale_factor = decltype(p_delay)::period::den; - constexpr u16 prescaler_max_value = std::numeric_limits::max(); - constexpr u16 timer_max_ticks = std::numeric_limits::max(); - constexpr u16 timer_reset_value = 0; - - u32 const time_per_tick_ns = scale_factor / p_timer_clock_frequency; - // Check if CPU Frequency too fast - if (time_per_tick_ns == 0) { - safe_throw(hal::argument_out_of_domain(this)); - } - // Use 64-bit to prevent immediate overflow. If still too big after division, - // exception thrown. - u64 const ticks_required = p_delay.count() / time_per_tick_ns; - u64 const prescaler_value = ticks_required / timer_max_ticks; - if (prescaler_value > prescaler_max_value) { - safe_throw(hal::argument_out_of_domain(this)); - } - - u16 prescaler_ticks_required; - // When the delay amount is shorter than one clock cycle - if (ticks_required == 0) { - prescaler_ticks_required = 1; - } else { - u32 const prescaler_frequency = - p_timer_clock_frequency / (static_cast(prescaler_value) + 1); - u32 const prescaler_time_per_tick_ns = scale_factor / prescaler_frequency; - prescaler_ticks_required = p_delay.count() / prescaler_time_per_tick_ns; - } - - bit_modify(reg->prescale_register) - .insert(static_cast(prescaler_value)); - bit_modify(reg->auto_reload_register) - .insert(prescaler_ticks_required); - bit_modify(reg->counter_register).insert(timer_reset_value); - bit_modify(reg->event_generator_register).set(update_generation); - bit_modify(reg->control_register).set(counter_enable); -} - -void timer::initialize(hal::unsafe, - void* p_peripheral_address, - void (*initialize_interrupts_function)(), - cortex_m::irq_t p_irq, - cortex_m::interrupt_pointer p_handler) -{ - m_reg = p_peripheral_address; - initialize_interrupts_function(); - if (hal::cortex_m::is_interrupt_enabled(p_irq)) { - hal::safe_throw(hal::device_or_resource_busy(this)); - } - cortex_m::enable_interrupt(p_irq, p_handler); - timer_reg_t* reg = get_timer_reg(m_reg); - setup(reg); -} -} // namespace hal::stm32_generic diff --git a/src/stm32_generic/timer.hpp b/src/stm32_generic/timer.hpp deleted file mode 100644 index 50543bc..0000000 --- a/src/stm32_generic/timer.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -namespace hal::stm32_generic { - -struct timer_reg_t -{ - /// Offset: 0x00 Control Register (R/W) - hal::u32 volatile control_register; // sets up timers - /// Offset: 0x04 Control Register 2 (R/W) - hal::u32 volatile control_register_2; - /// Offset: 0x08 Peripheral Mode Control Register (R/W) - hal::u32 volatile peripheral_control_register; - /// Offset: 0x0C DMA/Interrupt enable register (R/W) - hal::u32 volatile interrupt_enable_register; - /// Offset: 0x10 Status Register register (R/W) - hal::u32 volatile status_register; - /// Offset: 0x14 Event Generator Register register (R/W) - hal::u32 volatile event_generator_register; - /// Offset: 0x18 Capture/Compare mode register (R/W) - hal::u32 volatile capture_compare_mode_register; // set up modes for - // channel - /// Offset: 0x1C Capture/Compare mode register (R/W) - hal::u32 volatile capture_compare_mode_register_2; - /// Offset: 0x20 Capture/Compare Enable register (R/W) - hal::u32 volatile cc_enable_register; - /// Offset: 0x24 Counter (R/W) - hal::u32 volatile counter_register; - /// Offset: 0x28 Prescalar (R/W) - hal::u32 volatile prescale_register; - /// Offset: 0x2C Auto Reload Register (R/W) - hal::u32 volatile auto_reload_register; // affects frequency - /// Offset: 0x30 Repetition Counter Register (R/W) - hal::u32 volatile repetition_counter_register; - /// Offset: 0x34 Capture Compare Register (R/W) - hal::u32 volatile capture_compare_register; // affects duty cycles - /// Offset: 0x38 Capture Compare Register (R/W) - hal::u32 volatile capture_compare_register_2; - // Offset: 0x3C Capture Compare Register (R/W) - hal::u32 volatile capture_compare_register_3; - // Offset: 0x40 Capture Compare Register (R/W) - hal::u32 volatile capture_compare_register_4; - /// Offset: 0x44 Break and dead-time register - hal::u32 volatile break_and_deadtime_register; - /// Offset: 0x48 DMA control register - hal::u32 volatile dma_control_register; - /// Offset: 0x4C DMA address for full transfer - hal::u32 volatile dma_address_register; -}; - -[[nodiscard]] inline timer_reg_t* get_timer_reg(void* p_reg) -{ - return reinterpret_cast(p_reg); -} -} // namespace hal::stm32_generic diff --git a/src/stm32_generic/uart.cpp b/src/stm32_generic/uart.cpp deleted file mode 100644 index 71fcd1e..0000000 --- a/src/stm32_generic/uart.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include - -#include -#include - -#include -#include -#include -#include - -namespace hal::stm32_generic { - -/// Namespace for the status registers (SR) bit masks -struct status_reg // NOLINT -{ - /// Indicates if the transmit data register is empty and can be loaded with - /// another byte. - static constexpr auto transit_empty = hal::bit_mask::from<7>(); -}; - -/// Namespace for the control registers (CR1, CR3) bit masks and predefined -/// settings constants. -struct control_reg // NOLINT -{ - /// When this bit is cleared the USART prescalers and outputs are stopped - /// and the end of the current byte transfer in order to reduce power - /// consumption. (CR1) - static constexpr auto usart_enable = hal::bit_mask::from<13>(); - - /// Enables DMA receiver (CR3) - static constexpr auto dma_receiver_enable = hal::bit_mask::from<6>(); - - /// This bit enables the transmitter. (CR1) - static constexpr auto transmitter_enable = hal::bit_mask::from<3>(); - - /// This bit enables the receiver. (CR1) - static constexpr auto receive_enable = hal::bit_mask::from<2>(); - - /// Enable USART + Enable Receive + Enable Transmitter - static constexpr auto control_settings1 = - hal::bit_value(0UL) - .set() - .set() - .set() - .to(); - - /// Make sure that DMA is enabled for receive only - static constexpr auto control_settings3 = - hal::bit_value(0UL) - .set() - .to(); -}; - -/// Namespace for the baud rate (BRR) registers bit masks -struct baud_rate_reg // NOLINT -{ - /// Mantissa of USART DIV - static constexpr auto mantissa = hal::bit_mask::from<4, 15>(); - - /// Fraction of USART DIV - static constexpr auto fraction = hal::bit_mask::from<0, 3>(); -}; - -struct usart_t -{ - std::uint32_t volatile status; - std::uint32_t volatile data; - std::uint32_t volatile baud_rate; - std::uint32_t volatile control1; - std::uint32_t volatile control2; - std::uint32_t volatile control3; - std::uint32_t volatile guard_time_and_prescale; -}; - -inline usart_t* to_usart(void* p_uart) -{ - return reinterpret_cast(p_uart); -} - -uart::uart(void* p_uart, std::span p_receive_buffer) - : m_uart(p_uart) - , m_receive_buffer(p_receive_buffer) - , m_read_index(0) -{ -} - -void uart::configure_baud_rate(hal::hertz p_frequency, - serial::settings const& p_settings) -{ - auto const clock_frequency = p_frequency; - float usart_divider = clock_frequency / (16.0f * p_settings.baud_rate); - - // Truncate off the decimal values - auto mantissa = static_cast(usart_divider); - - // Subtract the whole number to leave just the decimal - auto fraction = - static_cast(usart_divider - static_cast(mantissa)); - - auto fractional_int = static_cast(std::roundf(fraction * 16)); - - if (fractional_int >= 16) { - mantissa = static_cast(mantissa + 1U); - fractional_int = 0; - } - - to_usart(m_uart)->baud_rate = - hal::bit_value() - .insert(mantissa) - .insert(fractional_int) - .to(); -} - -void uart::configure_format(serial::settings const& p_settings) -{ - constexpr auto parity_selection = bit_mask::from<9>(); - constexpr auto parity_control = bit_mask::from<10>(); - constexpr auto word_length = bit_mask::from<12>(); - constexpr auto stop = bit_mask::from<12, 13>(); - - bool parity_enable = (p_settings.parity != serial::settings::parity::none); - bool parity = (p_settings.parity == serial::settings::parity::odd); - bool double_stop = (p_settings.stop == serial::settings::stop_bits::two); - std::uint16_t stop_value = (double_stop) ? 0b10U : 0b00U; - - // Parity codes are: 0 for Even and 1 for Odd, thus the expression above - // sets the bool to TRUE when odd and zero when something else. This value - // is ignored if the parity is NONE since parity_enable will be zero. - auto& uart_reg = *to_usart(m_uart); - bit_modify(uart_reg.control1) - .insert(parity_enable) - .insert(parity) - .insert(0U); - - bit_modify(uart_reg.control2).insert(stop_value); -} - -void uart::configure(serial::settings const& p_settings, hertz p_frequency) -{ - auto& uart_reg = *to_usart(m_uart); - uart_reg.control1 = control_reg::control_settings1; - - // NOTE: We leave control settings 2 alone as it is for features beyond - // basic UART such as USART clock, USART port network (LIN), and other - // things. - - uart_reg.control3 = control_reg::control_settings3; - configure_baud_rate(p_frequency, p_settings); - configure_format(p_settings); -} -// TODO(#86) Add DMA write support to stm32_generic/uart.cpp -serial::write_t uart::uart_write(std::span p_data) -{ - auto& uart_reg = *to_usart(m_uart); - - for (auto const& byte : p_data) { - while (not bit_extract(uart_reg.status)) { - continue; - } - // Load the next byte into the data register - uart_reg.data = byte; - } - - return { - .data = p_data, - }; -} - -serial::read_t uart::uart_read(std::span& p_data, - std::uint32_t const& p_dma_cursor_position) -{ - size_t count = 0; - - for (auto& byte : p_data) { - if (m_read_index == p_dma_cursor_position) { - break; - } - byte = m_receive_buffer[m_read_index++]; - m_read_index = m_read_index % m_receive_buffer.size(); - count++; - } - - return { - .data = p_data.first(count), - .available = 1, - .capacity = m_receive_buffer.size(), - }; -} -uint32_t volatile* uart::data_register() -{ - return &to_usart(m_uart)->data; -} -void uart::flush(std::uint32_t p_dma_cursor_position) -{ - m_read_index = p_dma_cursor_position; -} - -std::uint32_t uart::buffer_size() -{ - return m_receive_buffer.size(); -} -} // namespace hal::stm32_generic diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp deleted file mode 100644 index 07f86f1..0000000 --- a/src/stm32f1/adc.cpp +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pin.hpp" -#include "power.hpp" - -namespace hal::stm32f1 { -namespace { -/// adc register map -struct adc_reg_t -{ - /// Number of injected channels - static constexpr size_t injected_channel_length = 4; - /// Offset: 0x00 A/D Status Register (RC/W0) - hal::u32 volatile status; - /// Offset: 0x04 A/D Control Register 1 (R/W) - hal::u32 volatile control_1; - /// Offset: 0x08 A/D Control Register 2 (R/W) - hal::u32 volatile control_2; - /// Offset: 0x0C A/D Sample Time Register 1 (R/W) - hal::u32 volatile sample_time_1; - /// Offset: 0x10 A/D Sample Time Register 2 (R/W) - hal::u32 volatile sample_time_2; - /// Offset: 0x14-0x20 A/D Injected Channel 0..3 Data Offset Register (R/W) - std::array - injected_channel_data_offset; - /// Offset: 0x24 A/D Watchdog High Treshold Register (R/W) - hal::u32 volatile watchdog_high_threshold; - /// Offset: 0x28 A/D Watchdog Low Treshold Register (R/W) - hal::u32 volatile watchdog_low_threshold; - /// Offset: 0x2C A/D Regular Sequence Register 1 (R/W) - hal::u32 volatile regular_sequence_1; - /// Offset: 0x30 A/D Regular Sequence Register 2 (R/W) - hal::u32 volatile regular_sequence_2; - /// Offset: 0x34 A/D Regular Sequence Register 3 (R/W) - hal::u32 volatile regular_sequence_3; - /// Offset: 0x38 A/D Injected Sequence Register (R/W) - hal::u32 volatile injected_sequence; - /// Offset: 0x3C-0x48 A/D Injected Data Register 0..3 (R/ ) - std::array injected_data; - /// Offset: 0x4C A/D Regular Data Register (R/ ) - hal::u32 volatile regular_data; -}; - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Status register. -namespace adc_status_register { -/// This bit is set by hardware when the converted voltage crosses the values -/// programmed in the ADC_LTR and ADC_HTR registers. It is cleared by software. -[[maybe_unused]] static constexpr auto analog_watchdog_flag = - hal::bit_mask::from(0); - -/// This bit is set by hardware at the end of a group channel conversion -/// (regular or injected). It is cleared by software or by reading the ADC_DR. -static constexpr auto end_of_conversion = hal::bit_mask::from(1); - -/// This bit is set by hardware at the end of all injected group channel -/// conversion. It is cleared by software. -[[maybe_unused]] static constexpr auto injected_channel_end_of_conversion = - hal::bit_mask::from(2); - -/// This bit is set by hardware when injected channel conversion starts. It is -/// cleared by software. -[[maybe_unused]] static constexpr auto injected_channel_start_flag = - hal::bit_mask::from(3); - -/// This bit is set by hardware when regular channel conversion starts. It is -/// cleared by software. -[[maybe_unused]] static constexpr auto regular_channel_start_flag = - hal::bit_mask::from(4); -}; // namespace adc_status_register - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Control register 2. -namespace adc_control_register_2 { -/// This bit is set and cleared by software. If this bit holds a value of zero -/// and a 1 is written to it then it wakes up the ADC from Power Down state. -/// Conversion starts when this bit holds a value of 1 and a 1 is written to it. -/// The application should allow a delay of tSTAB between power up and start of -/// conversion. Refer to Figure 23. -/// 0: Disable ADC conversion/calibration and go to power down mode. -/// 1: Enable ADC and to start conversion -/// Note: If any other bit in this register apart from ADON is changed at the -/// same time, then conversion is not triggered. This is to prevent triggering -/// an erroneous conversion. -static constexpr auto ad_converter_on = hal::bit_mask::from(0); - -/// This bit is set and cleared by software. If set conversion takes place -/// continuously till this bit is reset. -[[maybe_unused]] static constexpr auto continuous_conversion = - hal::bit_mask::from(1); - -/// This bit is set by software to start the calibration. It is reset by -/// hardware after calibration is complete. -static constexpr auto ad_calibration = hal::bit_mask::from(2); - -/// This bit is set by software and cleared by hardware, and is used to reset -/// the ADC calibration. It is cleared after the calibration registers are -/// initialized. -[[maybe_unused]] static constexpr auto reset_calibration = - hal::bit_mask::from(3); - -/// This bit is set and cleared by software to enable or disable DMA mode. If -/// its 0 then its disabled, and if its 1 then its enabled. -[[maybe_unused]] static constexpr auto direct_memory_access_mode = - hal::bit_mask::from(8); - -/// This bit is set and cleared by software to determine which data alignment to -/// use. If its 0 then its right-aligned, if its 1 then its left-aligned. -[[maybe_unused]] static constexpr auto data_alignment = hal::bit_mask::from(11); - -/// These bits select the external event used to trigger the start of conversion -/// of an injected group. -[[maybe_unused]] static constexpr auto external_event_select_injected_group = - hal::bit_mask::from(12, 14); - -/// This bit is set and cleared by software to enable/disable the external -/// trigger used to start conversion of an injected channel group. -[[maybe_unused]] static constexpr auto - external_trigger_conversion_mode_injected_channels = hal::bit_mask::from(15); - -/// These bits select the external event used to trigger the start of conversion -/// of a regular group. -[[maybe_unused]] static constexpr auto external_event_select_regular_group = - hal::bit_mask::from(17, 19); - -/// This bit is set and cleared by software to enable/disable the external -/// trigger used to start conversion of a regular channel group. -[[maybe_unused]] static constexpr auto - external_trigger_conversion_mode_regular_channel = hal::bit_mask::from(20); - -/// This bit is set by software and cleared by software or by hardware as soon -/// as the conversion starts. It starts a conversion of a group of injected -/// channels (if JSWSTART is selected as trigger event by the JEXTSEL[2:0] bits. -[[maybe_unused]] static constexpr auto start_conversion_injected_channels = - hal::bit_mask::from(21); - -/// This bit is set by software to start conversion and cleared by hardware as -/// soon as conversion starts. It starts a conversion of a group of regular -/// channels if SWSTART is selected as trigger event by the EXTSEL[2:0] bits. -[[maybe_unused]] static constexpr auto start_conversion_regular_channels = - hal::bit_mask::from(22); - -/// This bit is set and cleared by software to enable/disable the temperature -/// sensor and VREFINT channel. In devices with dual ADCs this bit is present -/// only in ADC1. -[[maybe_unused]] static constexpr auto - temperature_sensor_and_reference_voltage_enable = hal::bit_mask::from(23); -}; // namespace adc_control_register_2 - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Regular Sequence register 3. -namespace adc_regular_sequence_register_3 { -/// First channel conversion in regular sequence. -static constexpr auto first_conversion = hal::bit_mask::from(0, 4); - -/// Second channel conversion in regular sequence. -[[maybe_unused]] static constexpr auto second_conversion = - hal::bit_mask::from(5, 9); - -/// Third channel conversion in regular sequence. -[[maybe_unused]] static constexpr auto third_conversion = - hal::bit_mask::from(10, 14); - -/// Fourth channel conversion in regular sequence. -[[maybe_unused]] static constexpr auto fourth_conversion = - hal::bit_mask::from(15, 19); - -/// Fifth channel conversion in regular sequence. -[[maybe_unused]] static constexpr auto fifth_conversion = - hal::bit_mask::from(20, 24); - -/// Sixth channel conversion in regular sequence. -[[maybe_unused]] static constexpr auto sixth_conversion = - hal::bit_mask::from(25, 29); -}; // namespace adc_regular_sequence_register_3 - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Regular Data register. -namespace adc_regular_data_register { -/// These bits are read only. They contain the conversion result from the -/// regular channels. The data is left or right-aligned depending on bit 11 in -/// ADC_CR2. -static constexpr auto regular_data = hal::bit_mask::from(0, 15); - -/// In ADC1: In dual mode, these bits contain the regular data of ADC2. Refer to -/// Section 11.9: Dual ADC mode. -/// In ADC2 and ADC3: these bits are not used. -[[maybe_unused]] static constexpr auto dual_mode_data = - hal::bit_mask::from(16, 31); -}; // namespace adc_regular_data_register - -adc_reg_t& to_reg(void* p_address) -{ - return *reinterpret_cast(p_address); -} - -pin_select to_pin_select(adc_pins const& p_pin) -{ - // Derive port and pin from the enum. - hal::u8 port = 0, pin = 0; - if (hal::value(p_pin) <= 7) { - port = 'A'; - pin = hal::value(p_pin); - } else if (hal::value(p_pin) <= 9) { - port = 'B'; - pin = hal::value(p_pin) - 8; - } else { - port = 'C'; - pin = hal::value(p_pin) - 10; - } - return { .port = port, .pin = pin }; -} -} // namespace - -adc_manager::adc_manager(peripheral p_id, hal::basic_lock& p_lock) - : m_lock(&p_lock) - , m_reg(nullptr) - , m_id(p_id) -{ - switch (p_id) { - case peripheral::adc1: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - m_reg = reinterpret_cast(0x4001'2400UL); - break; - case peripheral::adc2: - // NOLINTNEXTLINE(performance-no-int-to-ptr): Need - m_reg = reinterpret_cast(0x4001'2800UL); - break; - default: - hal::safe_throw(hal::argument_out_of_domain(this)); - break; - } - - auto& adc_reg = to_reg(m_reg); - - // Verify adc's clock is not higher than the maximum frequency. - auto const adc_frequency = frequency(m_id); - if (adc_frequency > 14.0_MHz) { - hal::safe_throw(hal::operation_not_supported(nullptr)); - } - - // Power on adc clock. - power_on(m_id); - - // Turns on and calibrates the adc only if its the first time power-on. This - // is to prevent accidentally toggling the start of a new conversion as it - // uses the same bit. - if (not bit_extract( - adc_reg.control_2)) { - // Power on the adc. - hal::bit_modify(adc_reg.control_2) - .set(); - - // Start adc calibration. ADC must have been in power-on state for a minimum - // of 2 clock cycles before starting calibration. - hal::bit_modify(adc_reg.control_2) - .set(); - - // Wait for calibration to complete. - while ( - bit_extract(adc_reg.control_2)) { - continue; - } - } -} -adc_manager::channel adc_manager::acquire_channel(adc_pins p_pin) -{ - return { *this, p_pin }; -} - -float adc_manager::read_channel(adc_pins p_pin) -{ - // Lock the lock. - std::lock_guard acquire_lock(*m_lock); - - auto& adc_reg = to_reg(m_reg); - // Set the specified channel to be sampled. - hal::bit_modify(adc_reg.regular_sequence_3) - .insert( - hal::value(p_pin)); - - // Start adc conversion. - hal::bit_modify(adc_reg.control_2) - .set(); - - // Wait for conversion to complete. - while ( - not bit_extract(adc_reg.status)) { - continue; - } - - auto constexpr full_scale_max = bit_limits<12, size_t>::max(); - auto constexpr full_scale_float = static_cast(full_scale_max); - // Read sample from peripheral's memory. - auto const sample_integer = - hal::bit_extract( - adc_reg.regular_data); - auto const sample = static_cast(sample_integer); - return sample / full_scale_float; -} - -adc_manager::channel::channel(adc_manager& p_manager, adc_pins p_pin) - : m_manager(&p_manager) - , m_pin(p_pin) -{ - // Set specified pin to analog input mode. - configure_pin(to_pin_select(m_pin), input_analog); -} - -float adc_manager::channel::driver_read() -{ - return m_manager->read_channel(m_pin); -} - -adc_manager::channel::~channel() -{ - reset_pin(to_pin_select(m_pin)); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/can.cpp b/src/stm32f1/can.cpp deleted file mode 100644 index bf36cbd..0000000 --- a/src/stm32f1/can.cpp +++ /dev/null @@ -1,1261 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "can_reg.hpp" -#include "pin.hpp" -#include "power.hpp" - -// This is needed to allow backwards compatibility with previous versions of the -// can APIs. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -namespace hal::stm32f1 { -namespace { -/// Enable/Disable controller modes -/// -/// @param mode - which mode to enable/disable -/// @param enable_mode - true if you want to enable the mode. False otherwise. -void set_master_mode(bit_mask p_mode, bool p_enable_mode) -{ - bit_modify(can1_reg->MCR).insert(p_mode, p_enable_mode); -} - -bool get_master_status(bit_mask p_mode) -{ - return bit_extract(p_mode, can1_reg->MSR); -} - -void enter_initialization() -{ - // Enter Initialization mode in order to write to CAN registers. - set_master_mode(master_control::initialization_request, true); - - // Wait to enter Initialization mode - while (not get_master_status(master_status::initialization_acknowledge)) { - continue; - } -} - -void exit_initialization() -{ - // Leave Initialization mode - set_master_mode(master_control::initialization_request, false); - - // Wait to leave initialization mode - while (get_master_status(master_status::initialization_acknowledge)) { - continue; - } -} - -void timed_exit_initialization(hal::steady_clock& p_clock, - hal::time_duration p_timeout_time) -{ - // Leave Initialization mode - set_master_mode(master_control::initialization_request, false); - - auto const deadline = hal::future_deadline(p_clock, p_timeout_time); - while (deadline > p_clock.uptime()) { - if (not get_master_status(master_status::initialization_acknowledge)) { - return; - } - } - hal::safe_throw(hal::timed_out(nullptr)); -} - -void configure_baud_rate(hal::u32 p_baud_rate) -{ - auto const can_frequency = frequency(peripheral::can1); - auto const valid_divider = - calculate_can_bus_divider(can_frequency, static_cast(p_baud_rate)); - - if (not valid_divider) { - hal::safe_throw(hal::operation_not_supported(nullptr)); - } - - auto const divisors = valid_divider.value(); - - auto const prescale = divisors.clock_divider - 1U; - auto const sync_jump_width = divisors.synchronization_jump_width - 1U; - - auto phase_segment1 = - (divisors.phase_segment1 + divisors.propagation_delay) - 1U; - auto phase_segment2 = divisors.phase_segment2 - 1U; - - constexpr auto segment2_bit_limit = - hal::bit_limits::max(); - - // Check if phase segment 2 does not fit - if (phase_segment2 > segment2_bit_limit) { - // Take the extra time quanta and add it to the phase 1 segment - auto const phase_segment2_remainder = phase_segment2 - segment2_bit_limit; - phase_segment1 += phase_segment2_remainder; - // Cap phase segment 2 to the max available in the bit field - phase_segment2 = segment2_bit_limit; - } - - bit_modify(can1_reg->BTR) - .insert(prescale) - .insert(phase_segment1) - .insert(phase_segment2) - .insert(sync_jump_width) - .clear(); -} - -void set_filter_bank_mode(filter_bank_master_control p_mode) -{ - bit_modify(can1_reg->FMR) - .insert(hal::value(p_mode)); -} - -void set_filter_type(hal::u8 p_filter, filter_type p_filter_type) -{ - auto const filter_bit_mask = bit_mask::from(p_filter); - bit_modify(can1_reg->FM1R).insert(filter_bit_mask, hal::value(p_filter_type)); -} - -void set_filter_scale(hal::u8 p_filter, filter_scale p_scale) -{ - auto const filter_bit_mask = bit_mask::from(p_filter); - bit_modify(can1_reg->FS1R).insert(filter_bit_mask, hal::value(p_scale)); -} - -void set_filter_fifo_assignment(hal::u8 p_filter, - can_peripheral_manager::fifo_assignment p_fifo) -{ - auto const filter_bit_mask = bit_mask::from(p_filter); - bit_modify(can1_reg->FFA1R).insert(filter_bit_mask, hal::value(p_fifo)); -} - -void set_filter_activation_state(hal::u8 p_filter, filter_activation p_state) -{ - auto const filter_bit_mask = bit_mask::from(p_filter); - bit_modify(can1_reg->FA1R).insert(filter_bit_mask, hal::value(p_state)); -} - -void allow_all_acceptance_filter() -{ - // Activate filter initialization mode (Set bit) - set_filter_bank_mode(filter_bank_master_control::initialization); - - // Deactivate filter 0 (Clear bit) - set_filter_activation_state(0, filter_activation::not_active); - - // Configure filter 0 to single 32-bit scale configuration (Set bit) - set_filter_scale(0, filter_scale::single_32_bit_scale); - - // Set ID and mask to 0, where a mask value of 0 means accept any message. - can1_reg->filter_registers[0].FR1 = 0; - can1_reg->filter_registers[0].FR2 = 0; - - // Set filter to mask mode - set_filter_type(0, filter_type::mask); - - // Assign filter 0 to FIFO 0 (Clear bit) - set_filter_fifo_assignment(0, can_peripheral_manager::fifo_assignment::fifo1); - - // Activate filter 0 (Set bit) - set_filter_activation_state(0, filter_activation::active); - - // Deactivate filter initialization mode (clear bit) - set_filter_bank_mode(filter_bank_master_control::active); -} - -struct can_data_registers_t -{ - /// TFI register contents - uint32_t frame = 0; - /// TID register contents - uint32_t id = 0; - /// TDA register contents - uint32_t data_a = 0; - /// TDB register contents - uint32_t data_b = 0; -}; - -/// Converts desired message to the CANx registers -can_data_registers_t convert_message_to_stm_can( - hal::can::message_t const& message) -{ - can_data_registers_t registers; - - auto frame_info = - bit_value(0U) - .insert(message.length) - .to(); - - uint32_t frame_id = 0; - - if (message.id >= (1UL << 11UL)) { - frame_id = - bit_value(0U) - .insert(true) - .insert(message.is_remote_request) - .insert( - value(mailbox_identifier::id_type::extended)) - .insert(message.id) - .to(); - } else { - frame_id = - bit_value(0U) - .insert(true) - .insert(message.is_remote_request) - .insert( - value(mailbox_identifier::id_type::standard)) - .insert(message.id) - .to(); - } - - uint32_t data_a = 0; - data_a |= message.payload[0] << (0 * 8); - data_a |= message.payload[1] << (1 * 8); - data_a |= message.payload[2] << (2 * 8); - data_a |= message.payload[3] << (3 * 8); - - uint32_t data_b = 0; - data_b |= message.payload[4] << (0 * 8); - data_b |= message.payload[5] << (1 * 8); - data_b |= message.payload[6] << (2 * 8); - data_b |= message.payload[7] << (3 * 8); - - registers.frame = frame_info; - registers.id = frame_id; - registers.data_a = data_a; - registers.data_b = data_b; - - return registers; -} - -/// Converts desired message to the CANx registers -can_data_registers_t convert_message_to_stm_can(hal::can_message const& message) -{ - can_data_registers_t registers; - - auto frame_info = - bit_value(0U) - .insert(message.length) - .to(); - - uint32_t frame_id = 0; - - if (message.extended) { - frame_id = - bit_value(0U) - .insert(true) - .insert(message.remote_request) - .set(mailbox_identifier::identifier_type) - .insert(message.id) - .to(); - } else { - frame_id = - bit_value(0U) - .insert(true) - .insert(message.remote_request) - .clear() - .insert(message.id) - .to(); - } - - uint32_t data_a = 0; - data_a |= message.payload[0] << (0 * 8); - data_a |= message.payload[1] << (1 * 8); - data_a |= message.payload[2] << (2 * 8); - data_a |= message.payload[3] << (3 * 8); - - uint32_t data_b = 0; - data_b |= message.payload[4] << (0 * 8); - data_b |= message.payload[5] << (1 * 8); - data_b |= message.payload[6] << (2 * 8); - data_b |= message.payload[7] << (3 * 8); - - registers.frame = frame_info; - registers.id = frame_id; - registers.data_a = data_a; - registers.data_b = data_b; - - return registers; -} - -bool is_bus_off() -{ - // True = Bus is in sleep mode - // False = Bus has left sleep mode. - return bit_extract(can1_reg->MCR); -} - -void setup_can(hal::u32 p_baud_rate, can_pins p_pins, bool p_self_test = false) -{ - power_on(peripheral::can1); - - set_master_mode(master_control::sleep_mode_request, false); - set_master_mode(master_control::no_automatic_retransmission, false); - set_master_mode(master_control::automatic_bus_off_management, false); - - enter_initialization(); - - // Ensure we have left initialization phase so the peripheral can operate - // correctly. If an exception is thrown at any point, this will ensure that - // the can peripheral is taken out of initialization. - nonstd::scope_exit on_exit(&exit_initialization); - - configure_baud_rate(p_baud_rate); - - switch (p_pins) { - case can_pins::pa11_pa12: - configure_pin({ .port = 'A', .pin = 11 }, input_pull_up); - configure_pin({ .port = 'A', .pin = 12 }, push_pull_alternative_output); - break; - case can_pins::pb9_pb8: - configure_pin({ .port = 'B', .pin = 8 }, input_pull_up); - configure_pin({ .port = 'B', .pin = 9 }, push_pull_alternative_output); - break; - case can_pins::pd0_pd1: - configure_pin({ .port = 'D', .pin = 0 }, input_pull_up); - configure_pin({ .port = 'D', .pin = 1 }, push_pull_alternative_output); - break; - } - - bit_modify(can1_reg->BTR).insert(p_self_test); - - remap_pins(p_pins); -} - -void setup_can(hal::u32 p_baud_rate, - can_pins p_pins, - can_peripheral_manager::self_test p_enable_self_test, - hal::steady_clock& p_clock, - hal::time_duration p_timeout_time) -{ - power_on(peripheral::can1); - - set_master_mode(master_control::sleep_mode_request, false); - set_master_mode(master_control::no_automatic_retransmission, false); - set_master_mode(master_control::automatic_bus_off_management, false); - - enter_initialization(); - - configure_baud_rate(p_baud_rate); - - switch (p_pins) { - case can_pins::pa11_pa12: - configure_pin({ .port = 'A', .pin = 11 }, input_pull_up); - configure_pin({ .port = 'A', .pin = 12 }, push_pull_alternative_output); - break; - case can_pins::pb9_pb8: - configure_pin({ .port = 'B', .pin = 8 }, input_pull_up); - configure_pin({ .port = 'B', .pin = 9 }, push_pull_alternative_output); - break; - case can_pins::pd0_pd1: - configure_pin({ .port = 'D', .pin = 0 }, input_pull_up); - configure_pin({ .port = 'D', .pin = 1 }, push_pull_alternative_output); - break; - } - - bit_modify(can1_reg->BTR) - .insert(hal::value(p_enable_self_test)); - - remap_pins(p_pins); - - timed_exit_initialization(p_clock, p_timeout_time); -} - -can::message_t read_receive_mailbox() -{ - can::message_t message{ .id = 0 }; - - uint32_t fifo0_status = can1_reg->RF0R; - uint32_t fifo1_status = can1_reg->RF1R; - - can_peripheral_manager::fifo_assignment fifo_select = - can_peripheral_manager::fifo_assignment::fifo1; - - if (bit_extract(fifo0_status)) { - fifo_select = can_peripheral_manager::fifo_assignment::fifo1; - } else if (bit_extract(fifo1_status)) { - fifo_select = can_peripheral_manager::fifo_assignment::fifo2; - } else { - // Error, tried to receive when there were no pending messages. - return message; - } - - uint32_t frame = can1_reg->fifo_mailbox[value(fifo_select)].RDTR; - uint32_t id = can1_reg->fifo_mailbox[value(fifo_select)].RIR; - - // Extract all of the information from the message frame - bool is_remote_request = bit_extract(id); - uint32_t length = bit_extract(frame); - uint32_t format = bit_extract(id); - - message.is_remote_request = is_remote_request; - message.length = static_cast(length); - - // Get the frame ID - if (format == value(mailbox_identifier::id_type::extended)) { - message.id = bit_extract(id); - } else { - message.id = bit_extract(id); - } - - auto low_read_data = can1_reg->fifo_mailbox[value(fifo_select)].RDLR; - auto high_read_data = can1_reg->fifo_mailbox[value(fifo_select)].RDHR; - - // Pull the bytes from RDL into the payload array - message.payload[0] = (low_read_data >> (0 * 8)) & 0xFF; - message.payload[1] = (low_read_data >> (1 * 8)) & 0xFF; - message.payload[2] = (low_read_data >> (2 * 8)) & 0xFF; - message.payload[3] = (low_read_data >> (3 * 8)) & 0xFF; - - // Pull the bytes from RDH into the payload array - message.payload[4] = (high_read_data >> (0 * 8)) & 0xFF; - message.payload[5] = (high_read_data >> (1 * 8)) & 0xFF; - message.payload[6] = (high_read_data >> (2 * 8)) & 0xFF; - message.payload[7] = (high_read_data >> (3 * 8)) & 0xFF; - - // Release the RX buffer and allow another buffer to be read. - if (fifo_select == can_peripheral_manager::fifo_assignment::fifo1) { - bit_modify(can1_reg->RF0R).set(); - } else if (fifo_select == can_peripheral_manager::fifo_assignment::fifo2) { - bit_modify(can1_reg->RF1R).set(); - } - - return message; -} - -hal::callback can_receive_handler{}; - -void handler_can_interrupt() -{ - auto const message = read_receive_mailbox(); - // Why is this here? Because there was an stm32f103c8 chip that may have a - // defect or was damaged in testing. That device was then able to set its - // length to 9. The actual data in the data registers were garbage data. Even - // if the device is damaged, its best to throw out those damaged frames then - // attempt to pass them to a handler that may not able to manage them. - if (message.length <= 8) { - can_receive_handler(message); - } -} - -struct v2 -{}; - -can_message read_receive_mailbox(v2) -{ - using fifo_assignment = can_peripheral_manager::fifo_assignment; - - can_message message{}; - - uint32_t fifo0_status = can1_reg->RF0R; - uint32_t fifo1_status = can1_reg->RF1R; - - fifo_assignment fifo_select = fifo_assignment::fifo1; - - if (bit_extract(fifo0_status)) { - fifo_select = fifo_assignment::fifo1; - } else if (bit_extract(fifo1_status)) { - fifo_select = fifo_assignment::fifo2; - } else { - // Error, tried to receive when there were no pending messages. - return message; - } - - uint32_t frame = can1_reg->fifo_mailbox[value(fifo_select)].RDTR; - uint32_t id = can1_reg->fifo_mailbox[value(fifo_select)].RIR; - - // Extract all of the information from the message frame - bool const is_remote_request = - bit_extract(id); - uint32_t const length = - bit_extract(frame); - uint32_t const format = bit_extract(id); - bool const is_extended = - format == value(mailbox_identifier::id_type::extended); - - message.remote_request = is_remote_request; - message.length = static_cast(length); - message.extended = is_extended; - - // Get the frame ID - if (is_extended) { - message.id = bit_extract(id); - } else { - message.id = bit_extract(id); - } - - auto low_read_data = can1_reg->fifo_mailbox[value(fifo_select)].RDLR; - auto high_read_data = can1_reg->fifo_mailbox[value(fifo_select)].RDHR; - - // Pull the bytes from RDL into the payload array - message.payload[0] = (low_read_data >> (0 * 8)) & 0xFF; - message.payload[1] = (low_read_data >> (1 * 8)) & 0xFF; - message.payload[2] = (low_read_data >> (2 * 8)) & 0xFF; - message.payload[3] = (low_read_data >> (3 * 8)) & 0xFF; - - // Pull the bytes from RDH into the payload array - message.payload[4] = (high_read_data >> (0 * 8)) & 0xFF; - message.payload[5] = (high_read_data >> (1 * 8)) & 0xFF; - message.payload[6] = (high_read_data >> (2 * 8)) & 0xFF; - message.payload[7] = (high_read_data >> (3 * 8)) & 0xFF; - - // Release the RX buffer and allow another buffer to be read. - if (fifo_select == can_peripheral_manager::fifo_assignment::fifo1) { - bit_modify(can1_reg->RF0R).set(); - } else if (fifo_select == can_peripheral_manager::fifo_assignment::fifo2) { - bit_modify(can1_reg->RF1R).set(); - } - - return message; -} - -void bus_on() -{ - constexpr auto bus_off_mask = error_status_register::bus_off; - bool const bus_off = bit_extract(can1_reg->ESR); - - if (not bus_off) { - return; // nothing to do here, return - } - - // RM0008 page 670 states that bus off can be recovered from by entering and - // Request to enter initialization mode - enter_initialization(); - - // Leave Initialization mode - exit_initialization(); -} -} // namespace - -can::can(can::settings const& p_settings, can_pins p_pins) -{ - setup_can(static_cast(p_settings.baud_rate), p_pins); - allow_all_acceptance_filter(); // Default behavior in the original design -} - -void can::enable_self_test(bool p_enable) -{ - enter_initialization(); - nonstd::scope_exit on_exit(&exit_initialization); - - if (p_enable) { - bit_modify(can1_reg->BTR).set(); - } else { - bit_modify(can1_reg->BTR).clear(); - } -} - -can::~can() -{ - hal::cortex_m::disable_interrupt(irq::can1_rx0); - power_off(peripheral::can1); -} - -void can::driver_configure(can::settings const& p_settings) -{ - enter_initialization(); - nonstd::scope_exit on_exit(&exit_initialization); - - configure_baud_rate(static_cast(p_settings.baud_rate)); - allow_all_acceptance_filter(); -} - -void can::driver_bus_on() -{ - hal::stm32f1::bus_on(); -} - -void can::driver_send(can::message_t const& p_message) -{ - if (is_bus_off()) { - hal::safe_throw(hal::operation_not_permitted(this)); - } - - can_data_registers_t registers = convert_message_to_stm_can(p_message); - std::optional available_mailbox{}; - - while (not available_mailbox) { - hal::u32 const status_register = can1_reg->TSR; - // Check if any buffer is available. - if (bit_extract( - status_register)) { - available_mailbox = 0; - } else if (bit_extract( - status_register)) { - available_mailbox = 1; - } else if (bit_extract( - status_register)) { - available_mailbox = 2; - } - } - - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto& mailbox = can1_reg->transmit_mailbox[*available_mailbox]; - // The above is removed from lint because the while loop is our check that the - // optional value has been set. - - bit_modify(mailbox.TDTR) - .insert(p_message.length); - mailbox.TDLR = registers.data_a; - mailbox.TDHR = registers.data_b; - mailbox.TIR = registers.id; -} - -void can::driver_on_receive(hal::callback p_handler) -{ - initialize_interrupts(); - can_receive_handler = p_handler; - - // Enable interrupt service routine. - cortex_m::enable_interrupt(irq::can1_rx0, handler_can_interrupt); - cortex_m::enable_interrupt(irq::can1_rx1, handler_can_interrupt); - - bit_modify(can1_reg->IER) - .set(); - bit_modify(can1_reg->IER) - .set(); -} -} // namespace hal::stm32f1 - -// ============================================================================= -// -// Split can driver code -// -// ============================================================================= - -namespace hal::stm32f1 { -namespace { -std::span can_receive_buffer{}; -hal::u32 receive_count{}; -hal::u32 current_baud_rate = 0; -can_interrupt::optional_receive_handler can_v2_receive_handler{}; -can_bus_manager::optional_bus_off_handler can_v2_bus_off_handler{}; -std::bitset<28> acquired_banks{}; -can_peripheral_manager::disable_ids disable_id{}; - -void handler_circular_buffer_interrupt() -{ - auto const message = read_receive_mailbox(v2{}); - // Why is this here? Because there was an stm32f103c8 chip that may have a - // defect or was damaged in testing. That device was then able to set its - // length to 9. The actual data in the data registers were garbage data. Even - // if the device is damaged, its best to throw out those damaged frames then - // attempt to pass them to a handler that may not able to manage them. - if (message.length > 8) { - return; - } - - if (can_v2_receive_handler) { - using tag = hal::can_interrupt::on_receive_tag; - (*can_v2_receive_handler)(tag{}, message); - } - - if (not can_receive_buffer.empty()) { - auto const write_index = receive_count++ % can_receive_buffer.size(); - can_receive_buffer[write_index] = message; - } -} - -void handler_status_change_interrupt() -{ - bool is_bus_off = bit_extract(can1_reg->ESR); - if (is_bus_off && can_v2_bus_off_handler) { - using tag = hal::can_bus_manager::bus_off_tag; - (*can_v2_bus_off_handler)(tag{}); - } -} - -hal::u16 standard_id_to_stm_filter(hal::u16 p_id) -{ - auto const reg = hal::bit_value() - .insert(p_id) - .clear(standard_filter_bank::rtr1) - .clear(standard_filter_bank::id_extension1) - .insert(standard_filter_bank::extended_id1, 0UL) - .to(); - return reg; -} - -hal::u32 extended_id_to_stm_filter(hal::u32 p_id) -{ - auto const reg = hal::bit_value() - .insert(p_id) - .clear(extended_filter_bank::rtr) - .clear(extended_filter_bank::id_extension) - .clear(extended_filter_bank::reserved) - .to(); - return reg; -} - -/** - * @brief Scan through the acquired banks and return the index to an available - * one. - * - * @return hal::u8 - returns the index of the filter banks that has been - * acquired for the caller. - * @throws hal::resource_unavailable_try_again - if no filter banks are - * available - */ -hal::u8 available_filter() -{ - for (std::size_t i = 0; i < acquired_banks.size(); i++) { - if (not acquired_banks.test(i)) { - acquired_banks.set(i); - return i; - } - } - - hal::safe_throw(hal::resource_unavailable_try_again(nullptr)); -} -} // namespace - -can_peripheral_manager::can_peripheral_manager(hal::u32 p_baud_rate, - can_pins p_pins, - disable_ids p_disabled_ids, - bool p_self_test) -{ - current_baud_rate = p_baud_rate; - disable_id = p_disabled_ids; - - setup_can(p_baud_rate, p_pins, p_self_test); - - initialize_interrupts(); - - // Setup interrupt service routines - cortex_m::enable_interrupt(irq::can1_rx0, handler_circular_buffer_interrupt); - cortex_m::enable_interrupt(irq::can1_rx1, handler_circular_buffer_interrupt); - cortex_m::enable_interrupt(irq::can1_sce, handler_status_change_interrupt); - - bit_modify(can1_reg->IER) - .set(); - bit_modify(can1_reg->IER) - .set(); -} - -can_peripheral_manager::can_peripheral_manager( - hal::u32 p_baud_rate, - hal::steady_clock& p_clock, - hal::time_duration p_timeout_time, - can_pins p_pins, - self_test p_enable_self_test, - disable_ids p_disabled_ids) -{ - current_baud_rate = p_baud_rate; - disable_id = p_disabled_ids; - setup_can(p_baud_rate, p_pins, p_enable_self_test, p_clock, p_timeout_time); - - initialize_interrupts(); - - // Setup interrupt service routines - cortex_m::enable_interrupt(irq::can1_rx0, handler_circular_buffer_interrupt); - cortex_m::enable_interrupt(irq::can1_rx1, handler_circular_buffer_interrupt); - cortex_m::enable_interrupt(irq::can1_sce, handler_status_change_interrupt); - - bit_modify(can1_reg->IER) - .set(); - bit_modify(can1_reg->IER) - .set(); -} - -void can_peripheral_manager::enable_self_test(bool p_enable) -{ - enter_initialization(); - nonstd::scope_exit on_exit(&exit_initialization); - - if (p_enable) { - bit_modify(can1_reg->BTR).set(); - } else { - bit_modify(can1_reg->BTR).clear(); - } -} - -can_peripheral_manager::transceiver::transceiver( - std::span p_receive_buffer) -{ - can_receive_buffer = p_receive_buffer; - receive_count = 0; -} - -u32 can_peripheral_manager::transceiver::driver_baud_rate() -{ - return current_baud_rate; -} - -void can_peripheral_manager::transceiver::driver_send( - can_message const& p_message) -{ - if (is_bus_off()) { - hal::safe_throw(hal::operation_not_permitted(this)); - } - - can_data_registers_t const registers = convert_message_to_stm_can(p_message); - std::optional available_mailbox{}; - - while (not available_mailbox) { - hal::u32 const status_register = can1_reg->TSR; - // Check if any buffer is available. - if (bit_extract( - status_register)) { - available_mailbox = 0; - } else if (bit_extract( - status_register)) { - available_mailbox = 1; - } else if (bit_extract( - status_register)) { - available_mailbox = 2; - } - } - - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto& mailbox = can1_reg->transmit_mailbox[*available_mailbox]; - // The above is removed from lint because the while loop is our check that the - // optional value has been set. - - bit_modify(mailbox.TDTR) - .insert(p_message.length); - mailbox.TDLR = registers.data_a; - mailbox.TDHR = registers.data_b; - mailbox.TIR = registers.id; -} - -std::span -can_peripheral_manager::transceiver::driver_receive_buffer() -{ - return can_receive_buffer; -} - -std::size_t can_peripheral_manager::transceiver::driver_receive_cursor() -{ - return receive_count % can_receive_buffer.size(); -} - -can_peripheral_manager::interrupt::interrupt() -{ - bit_modify(can1_reg->IER).set(interrupt_enable_register::bus_off); -} - -void can_peripheral_manager::interrupt::driver_on_receive( - optional_receive_handler p_callback) -{ - can_v2_receive_handler = p_callback; -} - -void can_peripheral_manager::bus_manager::driver_baud_rate(hal::u32 p_hertz) -{ - enter_initialization(); - // Ensure we have left initialization phase so the peripheral can operate - // correctly. If an exception is thrown at any point, this will ensure that - // the can peripheral is taken out of initialization. - nonstd::scope_exit on_exit(&exit_initialization); - - configure_baud_rate(p_hertz); - current_baud_rate = p_hertz; -} - -void can_peripheral_manager::bus_manager::driver_filter_mode(accept) -{ - // this does nothing for now. We should consider dropping this in favor of - // always using a filter to manager message acceptance. A single mask filter - // with its mask set to all ZEROs would do the trick. -} - -void can_peripheral_manager::bus_manager::driver_on_bus_off( - optional_bus_off_handler p_callback) -{ - can_v2_bus_off_handler = p_callback; -} - -void can_peripheral_manager::bus_manager::driver_bus_on() -{ - bus_on(); -} - -can_peripheral_manager::transceiver can_peripheral_manager::acquire_transceiver( - std::span p_receive_buffer) -{ - return can_peripheral_manager::transceiver{ p_receive_buffer }; -} - -can_peripheral_manager::bus_manager -can_peripheral_manager::acquire_bus_manager() -{ - return can_peripheral_manager::bus_manager{}; -} - -can_peripheral_manager::interrupt can_peripheral_manager::acquire_interrupt() -{ - return can_peripheral_manager::interrupt{}; -} - -// ============================================================================= -// -// Acquire Filters -// -// ============================================================================= - -can_peripheral_manager::identifier_filter_set -can_peripheral_manager::acquire_identifier_filter(fifo_assignment p_fifo) -{ - return identifier_filter_set{ available_filter(), p_fifo }; -} - -can_peripheral_manager::mask_filter_set -can_peripheral_manager::acquire_mask_filter(fifo_assignment p_fifo) -{ - return mask_filter_set{ available_filter(), p_fifo }; -} - -can_peripheral_manager::extended_identifier_filter_set -can_peripheral_manager::acquire_extended_identifier_filter( - fifo_assignment p_fifo) -{ - return extended_identifier_filter_set{ available_filter(), p_fifo }; -} - -can_peripheral_manager::extended_mask_filter -can_peripheral_manager::acquire_extended_mask_filter(fifo_assignment p_fifo) -{ - return extended_mask_filter{ available_filter(), p_fifo }; -} - -// ============================================================================= -// -// Filter Constructors -// -// ============================================================================= - -can_peripheral_manager::mask_filter::mask_filter(filter_resource p_resource) - : m_resource(p_resource) -{ -} - -can_peripheral_manager::identifier_filter::identifier_filter( - filter_resource p_resource) - : m_resource(p_resource) -{ -} - -can_peripheral_manager::extended_mask_filter::extended_mask_filter( - hal::u8 p_filter_index, - fifo_assignment p_fifo) - : m_filter_index(p_filter_index) -{ - set_filter_bank_mode(filter_bank_master_control::initialization); - - // On scope exit, whether via a return or an exception, invoke this. - nonstd::scope_exit on_exit([p_filter_index]() { - // Deactivate filter initialization mode (clear bit) - set_filter_bank_mode(filter_bank_master_control::active); - set_filter_activation_state(p_filter_index, filter_activation::active); - }); - - set_filter_activation_state(p_filter_index, filter_activation::not_active); - set_filter_scale(p_filter_index, filter_scale::single_32_bit_scale); - set_filter_type(p_filter_index, filter_type::mask); - set_filter_fifo_assignment(p_filter_index, p_fifo); - - auto const id_reg = extended_id_to_stm_filter(disable_id.extended); - auto const mask_reg = extended_id_to_stm_filter(0x1FFF'FFFF); - can1_reg->filter_registers[p_filter_index].FR1 = id_reg; - can1_reg->filter_registers[p_filter_index].FR2 = mask_reg; -} - -can_peripheral_manager::extended_identifier_filter::extended_identifier_filter( - filter_resource p_resource) - : m_resource(p_resource) -{ -} - -can_peripheral_manager::mask_filter_set::mask_filter_set(hal::u8 p_filter_index, - fifo_assignment p_fifo) - : filter{ - mask_filter{ { .filter_index = p_filter_index, .word_index = 0 } }, - mask_filter{ { .filter_index = p_filter_index, .word_index = 1 } }, - } -{ - // Required to change filter scale and type - set_filter_bank_mode(filter_bank_master_control::initialization); - - // On scope exit, whether via a return or an exception, invoke this. - nonstd::scope_exit on_exit([p_filter_index]() { - set_filter_bank_mode(filter_bank_master_control::active); - set_filter_activation_state(p_filter_index, filter_activation::active); - }); - - set_filter_activation_state(p_filter_index, filter_activation::not_active); - set_filter_scale(p_filter_index, filter_scale::dual_16_bit_scale); - set_filter_type(p_filter_index, filter_type::mask); - set_filter_fifo_assignment(p_filter_index, p_fifo); - - auto const disable_id_reg = standard_id_to_stm_filter(disable_id.standard); - auto const disable_mask_reg = standard_id_to_stm_filter(0x1FF); - auto const disable_mask = (disable_mask_reg << 16) | disable_id_reg; - - can1_reg->filter_registers[p_filter_index].FR1 = disable_mask; - can1_reg->filter_registers[p_filter_index].FR2 = disable_mask; -} - -can_peripheral_manager::identifier_filter_set::identifier_filter_set( - hal::u8 p_filter_index, - fifo_assignment p_fifo) - : filter{ - identifier_filter{ { .filter_index = p_filter_index, .word_index = 0 } }, - identifier_filter{ { .filter_index = p_filter_index, .word_index = 1 } }, - identifier_filter{ { .filter_index = p_filter_index, .word_index = 2 } }, - identifier_filter{ { .filter_index = p_filter_index, .word_index = 3 } }, - } -{ - // Required to change filter scale and type - set_filter_bank_mode(filter_bank_master_control::initialization); - - // On scope exit, whether via a return or an exception, invoke this. - nonstd::scope_exit on_exit([p_filter_index]() { - set_filter_bank_mode(filter_bank_master_control::active); - set_filter_activation_state(p_filter_index, filter_activation::active); - }); - - set_filter_activation_state(p_filter_index, filter_activation::not_active); - set_filter_scale(p_filter_index, filter_scale::dual_16_bit_scale); - set_filter_type(p_filter_index, filter_type::list); - set_filter_fifo_assignment(p_filter_index, p_fifo); - - auto const disable_reg = standard_id_to_stm_filter(disable_id.standard); - auto const disable_mask = (disable_reg << 16) | disable_reg; - can1_reg->filter_registers[p_filter_index].FR1 = disable_mask; - can1_reg->filter_registers[p_filter_index].FR2 = disable_mask; -} - -can_peripheral_manager::extended_identifier_filter_set:: - extended_identifier_filter_set(hal::u8 p_filter_index, fifo_assignment p_fifo) - : filter{ - can_peripheral_manager::extended_identifier_filter{ - { .filter_index = p_filter_index, .word_index = 0 } }, - can_peripheral_manager::extended_identifier_filter{ - { .filter_index = p_filter_index, .word_index = 1 } }, - } -{ - // Required to set filter scale and type - set_filter_bank_mode(filter_bank_master_control::initialization); - - // On scope exit, whether via a return or an exception, invoke this. - nonstd::scope_exit on_exit([p_filter_index]() { - set_filter_bank_mode(filter_bank_master_control::active); - set_filter_activation_state(p_filter_index, filter_activation::active); - }); - - set_filter_activation_state(p_filter_index, filter_activation::not_active); - set_filter_scale(p_filter_index, filter_scale::single_32_bit_scale); - set_filter_type(p_filter_index, filter_type::list); - set_filter_fifo_assignment(p_filter_index, p_fifo); - - auto const disable_mask = extended_id_to_stm_filter(disable_id.extended); - - can1_reg->filter_registers[p_filter_index].FR1 = disable_mask; - can1_reg->filter_registers[p_filter_index].FR2 = disable_mask; -} - -// ============================================================================= -// -// ::driver_allow()... -// -// ============================================================================= - -void can_peripheral_manager::identifier_filter::driver_allow( - std::optional p_id) -{ - auto const id = p_id.value_or(disable_id.standard); - - set_filter_activation_state(m_resource.filter_index, - filter_activation::not_active); - - auto& filter = can1_reg->filter_registers[m_resource.filter_index]; - auto const reg = standard_id_to_stm_filter(id); - - switch (m_resource.word_index) { - case 0: - hal::bit_modify(filter.FR1).insert(reg); - break; - case 1: - hal::bit_modify(filter.FR1).insert(reg); - break; - case 2: - hal::bit_modify(filter.FR2).insert(reg); - break; - case 3: - hal::bit_modify(filter.FR2).insert(reg); - break; - default: - hal::safe_throw(hal::operation_not_supported(this)); - break; - } - - set_filter_activation_state(m_resource.filter_index, - filter_activation::active); -} - -void can_peripheral_manager::mask_filter::driver_allow( - std::optional p_pair) -{ - auto const selected_pair = p_pair.value_or(pair{ - .id = disable_id.standard, - .mask = 0x1FF, - }); - - auto& filter = can1_reg->filter_registers[m_resource.filter_index]; - - set_filter_activation_state(m_resource.filter_index, - filter_activation::not_active); - - auto const id_reg = standard_id_to_stm_filter(selected_pair.id); - auto const mask_reg = standard_id_to_stm_filter(selected_pair.mask); - - if (m_resource.word_index == 0) { - hal::bit_modify(filter.FR1) - .insert(id_reg) - .insert(mask_reg); - } else { - hal::bit_modify(filter.FR2) - .insert(id_reg) - .insert(mask_reg); - } - - set_filter_activation_state(m_resource.filter_index, - filter_activation::active); -} - -void can_peripheral_manager::extended_identifier_filter::driver_allow( - std::optional p_id) -{ - auto const id = p_id.value_or(disable_id.extended); - auto const reg = extended_id_to_stm_filter(id); - - set_filter_activation_state(m_resource.filter_index, - filter_activation::not_active); - - auto& filter = can1_reg->filter_registers[m_resource.filter_index]; - - if (m_resource.word_index == 0) { - filter.FR1 = reg; - } else { - filter.FR2 = reg; - } - - set_filter_activation_state(m_resource.filter_index, - filter_activation::active); -} - -void can_peripheral_manager::extended_mask_filter::driver_allow( - std::optional p_pair) -{ - auto const selected_pair = p_pair.value_or(pair{ - .id = disable_id.standard, - .mask = 0x1FFF'FFFF, - }); - - auto const id_reg = extended_id_to_stm_filter(selected_pair.id); - auto const mask_reg = extended_id_to_stm_filter(selected_pair.mask); - - auto& filter = can1_reg->filter_registers[m_filter_index]; - - set_filter_activation_state(m_filter_index, filter_activation::not_active); - filter.FR1 = id_reg; - filter.FR2 = mask_reg; - set_filter_activation_state(m_filter_index, filter_activation::active); -} - -// ============================================================================= -// -// Filter Destructors -// -// ============================================================================= - -can_peripheral_manager::~can_peripheral_manager() -{ - hal::cortex_m::disable_interrupt(irq::can1_rx0); - hal::cortex_m::disable_interrupt(irq::can1_rx1); - hal::cortex_m::disable_interrupt(irq::can1_sce); - power_off(peripheral::can1); -} - -can_peripheral_manager::transceiver::~transceiver() -{ - can_receive_buffer = {}; -} - -can_peripheral_manager::interrupt::~interrupt() -{ - can_v2_receive_handler = std::nullopt; -} - -can_peripheral_manager::bus_manager::~bus_manager() -{ - can_v2_bus_off_handler = std::nullopt; -} - -// NOLINTNEXTLINE(bugprone-exception-escape) -can_peripheral_manager::identifier_filter_set::~identifier_filter_set() -{ - // Free filter bank for use by a different identifier filter - auto const index = filter[0].m_resource.filter_index; - acquired_banks.reset(index); - set_filter_activation_state(index, filter_activation::not_active); -} - -// NOLINTNEXTLINE(bugprone-exception-escape) -can_peripheral_manager::mask_filter_set::~mask_filter_set() -{ - auto const index = filter[0].m_resource.filter_index; - acquired_banks.reset(index); - set_filter_activation_state(index, filter_activation::not_active); -} - -can_peripheral_manager::extended_identifier_filter_set:: - // NOLINTNEXTLINE(bugprone-exception-escape) - ~extended_identifier_filter_set() -{ - auto const index = filter[0].m_resource.filter_index; - acquired_banks.reset(index); - set_filter_activation_state(index, filter_activation::not_active); -} - -// NOLINTNEXTLINE(bugprone-exception-escape) -can_peripheral_manager::extended_mask_filter::~extended_mask_filter() -{ - // Free filter bank for use by a different identifier filter - acquired_banks.reset(m_filter_index); - set_filter_activation_state(m_filter_index, filter_activation::not_active); -} -} // namespace hal::stm32f1 - -#pragma GCC diagnostic pop diff --git a/src/stm32f1/can2.cpp b/src/stm32f1/can2.cpp deleted file mode 100644 index f0bdeb3..0000000 --- a/src/stm32f1/can2.cpp +++ /dev/null @@ -1,1101 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "can_reg.hpp" -#include "pin.hpp" -#include "power.hpp" - -namespace hal::stm32f1 { -namespace { -/// Enable/Disable controller modes -/// -/// @param mode - which mode to enable/disable -/// @param enable_mode - true if you want to enable the mode. False otherwise. -void set_master_mode(bit_mask p_mode, bool p_enable_mode) -{ - bit_modify(can1_reg->MCR).insert(p_mode, p_enable_mode); -} - -bool get_master_status(bit_mask p_mode) -{ - return bit_extract(p_mode, can1_reg->MSR); -} - -void enter_initialization() -{ - // Enter Initialization mode in order to write to CAN registers. - set_master_mode(master_control::initialization_request, true); - - // Wait to enter Initialization mode - while (not get_master_status(master_status::initialization_acknowledge)) { - continue; - } -} - -void exit_initialization() -{ - // Leave Initialization mode - set_master_mode(master_control::initialization_request, false); - - // Wait to leave initialization mode - while (get_master_status(master_status::initialization_acknowledge)) { - continue; - } -} - -void timed_exit_initialization(hal::steady_clock& p_clock, - hal::time_duration p_timeout_time) -{ - // Leave Initialization mode - set_master_mode(master_control::initialization_request, false); - - auto const deadline = hal::future_deadline(p_clock, p_timeout_time); - while (deadline > p_clock.uptime()) { - if (not get_master_status(master_status::initialization_acknowledge)) { - return; - } - } - hal::safe_throw(hal::timed_out(nullptr)); -} - -void configure_baud_rate(hal::u32 p_baud_rate) -{ - auto const can_frequency = frequency(peripheral::can1); - auto const valid_divider = - calculate_can_bus_divider(can_frequency, static_cast(p_baud_rate)); - - if (not valid_divider) { - hal::safe_throw(hal::operation_not_supported(nullptr)); - } - - auto const divisors = valid_divider.value(); - - auto const prescale = divisors.clock_divider - 1U; - auto const sync_jump_width = divisors.synchronization_jump_width - 1U; - - auto phase_segment1 = - (divisors.phase_segment1 + divisors.propagation_delay) - 1U; - auto phase_segment2 = divisors.phase_segment2 - 1U; - - constexpr auto segment2_bit_limit = - hal::bit_limits::max(); - - // Check if phase segment 2 does not fit - if (phase_segment2 > segment2_bit_limit) { - // Take the extra time quanta and add it to the phase 1 segment - auto const phase_segment2_remainder = phase_segment2 - segment2_bit_limit; - phase_segment1 += phase_segment2_remainder; - // Cap phase segment 2 to the max available in the bit field - phase_segment2 = segment2_bit_limit; - } - - bit_modify(can1_reg->BTR) - .insert(prescale) - .insert(phase_segment1) - .insert(phase_segment2) - .insert(sync_jump_width) - .clear(); -} - -void set_filter_bank_mode(filter_bank_master_control p_mode) -{ - bit_modify(can1_reg->FMR) - .insert(hal::value(p_mode)); -} - -void set_filter_type(hal::u8 p_filter, filter_type p_filter_type) -{ - auto const filter_bit_mask = bit_mask::from(p_filter); - bit_modify(can1_reg->FM1R).insert(filter_bit_mask, hal::value(p_filter_type)); -} - -void set_filter_scale(hal::u8 p_filter, filter_scale p_scale) -{ - auto const filter_bit_mask = bit_mask::from(p_filter); - bit_modify(can1_reg->FS1R).insert(filter_bit_mask, hal::value(p_scale)); -} - -void set_filter_fifo_assignment(hal::u8 p_filter, can_fifo p_fifo) -{ - auto const filter_bit_mask = bit_mask::from(p_filter); - bit_modify(can1_reg->FFA1R).insert(filter_bit_mask, hal::value(p_fifo)); -} - -void set_filter_activation_state(hal::u8 p_filter, filter_activation p_state) -{ - auto const filter_bit_mask = bit_mask::from(p_filter); - bit_modify(can1_reg->FA1R).insert(filter_bit_mask, hal::value(p_state)); -} - -struct can_data_registers_t -{ - /// TFI register contents - uint32_t frame = 0; - /// TID register contents - uint32_t id = 0; - /// TDA register contents - uint32_t data_a = 0; - /// TDB register contents - uint32_t data_b = 0; -}; - -/// Converts desired message to the CANx registers -can_data_registers_t convert_message_to_stm_can(hal::can_message const& message) -{ - can_data_registers_t registers; - - auto frame_info = - bit_value(0U) - .insert(message.length) - .to(); - - uint32_t frame_id = 0; - - if (message.extended) { - frame_id = - bit_value(0U) - .insert(true) - .insert(message.remote_request) - .set(mailbox_identifier::identifier_type) - .insert(message.id) - .to(); - } else { - frame_id = - bit_value(0U) - .insert(true) - .insert(message.remote_request) - .clear() - .insert(message.id) - .to(); - } - - uint32_t data_a = 0; - data_a |= message.payload[0] << (0 * 8); - data_a |= message.payload[1] << (1 * 8); - data_a |= message.payload[2] << (2 * 8); - data_a |= message.payload[3] << (3 * 8); - - uint32_t data_b = 0; - data_b |= message.payload[4] << (0 * 8); - data_b |= message.payload[5] << (1 * 8); - data_b |= message.payload[6] << (2 * 8); - data_b |= message.payload[7] << (3 * 8); - - registers.frame = frame_info; - registers.id = frame_id; - registers.data_a = data_a; - registers.data_b = data_b; - - return registers; -} - -bool is_bus_off() -{ - // True = Bus is in sleep mode - // False = Bus has left sleep mode. - return bit_extract(can1_reg->MCR); -} - -void setup_can(hal::u32 p_baud_rate, - can_pins p_pins, - can_self_test p_enable_self_test, - hal::steady_clock& p_clock, - hal::time_duration p_timeout_time) -{ - power_on(peripheral::can1); - - set_master_mode(master_control::sleep_mode_request, false); - set_master_mode(master_control::no_automatic_retransmission, false); - set_master_mode(master_control::automatic_bus_off_management, false); - - enter_initialization(); - - configure_baud_rate(p_baud_rate); - - switch (p_pins) { - case can_pins::pa11_pa12: - configure_pin({ .port = 'A', .pin = 11 }, input_pull_up); - configure_pin({ .port = 'A', .pin = 12 }, push_pull_alternative_output); - break; - case can_pins::pb9_pb8: - configure_pin({ .port = 'B', .pin = 8 }, input_pull_up); - configure_pin({ .port = 'B', .pin = 9 }, push_pull_alternative_output); - break; - case can_pins::pd0_pd1: - configure_pin({ .port = 'D', .pin = 0 }, input_pull_up); - configure_pin({ .port = 'D', .pin = 1 }, push_pull_alternative_output); - break; - } - - bit_modify(can1_reg->BTR) - .insert(hal::value(p_enable_self_test)); - - remap_pins(p_pins); - - timed_exit_initialization(p_clock, p_timeout_time); -} - -can_message read_receive_mailbox() -{ - can_message message{}; - - auto const fifo0_status = can1_reg->RF0R; - auto const fifo1_status = can1_reg->RF1R; - - auto fifo_select = can_fifo::select1; - - if (bit_extract(fifo0_status)) { - fifo_select = can_fifo::select1; - } else if (bit_extract(fifo1_status)) { - fifo_select = can_fifo::select2; - } else { - // Error, tried to receive when there were no pending messages. - return message; - } - - uint32_t frame = can1_reg->fifo_mailbox[value(fifo_select)].RDTR; - uint32_t id = can1_reg->fifo_mailbox[value(fifo_select)].RIR; - - // Extract all of the information from the message frame - bool const is_remote_request = - bit_extract(id); - uint32_t const length = - bit_extract(frame); - uint32_t const format = bit_extract(id); - bool const is_extended = - format == value(mailbox_identifier::id_type::extended); - - message.remote_request = is_remote_request; - message.length = static_cast(length); - message.extended = is_extended; - - // Get the frame ID - if (is_extended) { - message.id = bit_extract(id); - } else { - message.id = bit_extract(id); - } - - auto low_read_data = can1_reg->fifo_mailbox[value(fifo_select)].RDLR; - auto high_read_data = can1_reg->fifo_mailbox[value(fifo_select)].RDHR; - - // Pull the bytes from RDL into the payload array - message.payload[0] = (low_read_data >> (0 * 8)) & 0xFF; - message.payload[1] = (low_read_data >> (1 * 8)) & 0xFF; - message.payload[2] = (low_read_data >> (2 * 8)) & 0xFF; - message.payload[3] = (low_read_data >> (3 * 8)) & 0xFF; - - // Pull the bytes from RDH into the payload array - message.payload[4] = (high_read_data >> (0 * 8)) & 0xFF; - message.payload[5] = (high_read_data >> (1 * 8)) & 0xFF; - message.payload[6] = (high_read_data >> (2 * 8)) & 0xFF; - message.payload[7] = (high_read_data >> (3 * 8)) & 0xFF; - - // Release the RX buffer and allow another buffer to be read. - if (fifo_select == can_fifo::select1) { - bit_modify(can1_reg->RF0R).set(); - } else if (fifo_select == can_fifo::select2) { - bit_modify(can1_reg->RF1R).set(); - } - - return message; -} - -void bus_on() -{ - constexpr auto bus_off_mask = error_status_register::bus_off; - bool const bus_off = bit_extract(can1_reg->ESR); - - if (not bus_off) { - return; // nothing to do here, return - } - - // RM0008 page 670 states that bus off can be recovered from by entering and - // Request to enter initialization mode - enter_initialization(); - - // Leave Initialization mode - exit_initialization(); -} - -hal::u16 standard_id_to_stm_filter(hal::u16 p_id) -{ - auto const reg = hal::bit_value() - .insert(p_id) - .clear(standard_filter_bank::rtr1) - .clear(standard_filter_bank::id_extension1) - .insert(standard_filter_bank::extended_id1, 0UL) - .to(); - return reg; -} - -hal::u32 extended_id_to_stm_filter(hal::u32 p_id) -{ - auto const reg = hal::bit_value() - .insert(p_id) - .clear(extended_filter_bank::rtr) - .clear(extended_filter_bank::id_extension) - .clear(extended_filter_bank::reserved) - .to(); - return reg; -} -} // namespace - -can_peripheral_manager_v2::can_peripheral_manager_v2( - hal::usize p_message_count, - std::pmr::polymorphic_allocator<> p_allocator, - hal::u32 p_baud_rate, - hal::steady_clock& p_clock, - hal::time_duration p_timeout_time, - can_pins p_pins, - can_self_test p_enable_self_test) - : m_buffer(p_allocator, p_message_count) - , m_current_baud_rate(p_baud_rate) -{ - setup_can(p_baud_rate, p_pins, p_enable_self_test, p_clock, p_timeout_time); - - initialize_interrupts(); - - hal::static_callable rx_handler( - [this]() { - auto const message = read_receive_mailbox(); - // Why is this here? Because there was an stm32f103c8 chip that may have a - // defect or was damaged in testing. That device was then able to set its - // length to 9. The actual data in the data registers were garbage data. - // Even if the device is damaged, its best to throw out those damaged - // frames then attempt to pass them to a handler that may not able to - // manage them. - if (message.length > 8) { - return; - } - - if (m_receive_handler) { - using tag = hal::can_interrupt::on_receive_tag; - (*m_receive_handler)(tag{}, message); - } - - m_buffer.push(message); - }); - - // Setup interrupt service routines - cortex_m::enable_interrupt(irq::can1_rx0, rx_handler.get_handler()); - cortex_m::enable_interrupt(irq::can1_rx1, rx_handler.get_handler()); - bit_modify(can1_reg->IER) - .set(); - bit_modify(can1_reg->IER) - .set(); -} - -hal::u8 can_peripheral_manager_v2::available_filter() -{ - for (std::size_t i = 0; i < m_acquired_banks.size(); i++) { - if (not m_acquired_banks.test(i)) { - m_acquired_banks.set(i); - // NOTE: This is only safe because m_acquired_banks bitset size is less - // than 256. - return static_cast(i); - } - } - - hal::safe_throw(hal::resource_unavailable_try_again(this)); -} - -void can_peripheral_manager_v2::release_filter(hal::u8 p_filter_bank) -{ - m_acquired_banks.reset(p_filter_bank); -} - -void can_peripheral_manager_v2::enable_self_test(bool p_enable) -{ - enter_initialization(); - nonstd::scope_exit on_exit(&exit_initialization); - - if (p_enable) { - bit_modify(can1_reg->BTR).set(); - } else { - bit_modify(can1_reg->BTR).clear(); - } -} - -void can_peripheral_manager_v2::bus_on() -{ - hal::stm32f1::bus_on(); -} - -hal::u32 can_peripheral_manager_v2::baud_rate() const -{ - return m_current_baud_rate; -} - -void can_peripheral_manager_v2::baud_rate(hal::u32 p_hertz) -{ - enter_initialization(); - // Ensure we have left initialization phase so the peripheral can operate - // correctly. If an exception is thrown at any point, this will ensure that - // the can peripheral is taken out of initialization. - nonstd::scope_exit on_exit(&exit_initialization); - configure_baud_rate(p_hertz); - m_current_baud_rate = p_hertz; -} - -void can_peripheral_manager_v2::send(can_message const& p_message) -{ - if (is_bus_off()) { - hal::safe_throw(hal::operation_not_permitted(this)); - } - - can_data_registers_t const registers = convert_message_to_stm_can(p_message); - std::optional available_mailbox{}; - i8 count_limit = 10; - - while (not available_mailbox) { - if (count_limit <= 0) { - hal::safe_throw(hal::resource_unavailable_try_again(this)); - } - - hal::u32 const status_register = can1_reg->TSR; - // Check if any buffer is available. - if (bit_extract( - status_register)) { - available_mailbox = 0; - } else if (bit_extract( - status_register)) { - available_mailbox = 1; - } else if (bit_extract( - status_register)) { - available_mailbox = 2; - } - - count_limit--; - } - - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto& mailbox = can1_reg->transmit_mailbox[*available_mailbox]; - // The above is removed from lint because the while loop is our check that the - // optional value has been set. - - bit_modify(mailbox.TDTR) - .insert(p_message.length); - mailbox.TDLR = registers.data_a; - mailbox.TDHR = registers.data_b; - mailbox.TIR = registers.id; -} - -void can_peripheral_manager_v2::on_receive( - can_interrupt::optional_receive_handler const& p_callback) -{ - m_receive_handler = p_callback; -} - -can_peripheral_manager_v2::~can_peripheral_manager_v2() -{ - hal::cortex_m::disable_interrupt(irq::can1_rx0); - hal::cortex_m::disable_interrupt(irq::can1_rx1); - hal::cortex_m::disable_interrupt(irq::can1_sce); - power_off(peripheral::can1); -} - -/** - * @brief Acquire an `hal::can_transceiver` implementation - * - * @return transceiver - object implementing the `hal::can_transceiver` - * interface for this can peripheral. - */ -hal::v5::strong_ptr acquire_can_transceiver( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager) -{ - struct transceiver : public hal::can_transceiver - { - public: - explicit transceiver( - hal::v5::strong_ptr const& p_manager) - : m_manager(p_manager) - { - } - - transceiver(transceiver const&) = delete; - transceiver& operator=(transceiver const&) = delete; - transceiver(transceiver&&) = delete; - transceiver& operator=(transceiver&&) = delete; - ~transceiver() override = default; - - private: - u32 driver_baud_rate() override - { - return m_manager->baud_rate(); - } - - void driver_send(can_message const& p_message) override - { - m_manager->send(p_message); - } - - std::span driver_receive_buffer() override - { - return m_manager->receive_buffer(); - } - - std::size_t driver_receive_cursor() override - { - return m_manager->receive_cursor(); - } - - hal::v5::strong_ptr m_manager; - }; - - return hal::v5::make_strong_ptr(p_allocator, p_manager); -} - -/** - * @brief Acquire an `hal::can_bus_manager` implementation - * - * @return bus_manager - object implementing the `hal::can_bus_manager` - * interface for this can peripheral. - */ -hal::v5::strong_ptr acquire_can_bus_manager( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager) -{ - class bus_manager : public hal::can_bus_manager - { - public: - bus_manager(hal::v5::strong_ptr const& p_manager) - : m_manager(p_manager) - { - hal::static_callable static_callable( - [this]() { - if (m_bus_off_handler) { - (*m_bus_off_handler)(hal::can_bus_manager::bus_off_tag{}); - } - }); - - cortex_m::enable_interrupt(irq::can1_sce, static_callable.get_handler()); - } - bus_manager(bus_manager const&) = delete; - bus_manager& operator=(bus_manager const&) = delete; - bus_manager(bus_manager&&) = delete; - bus_manager& operator=(bus_manager&&) = delete; - ~bus_manager() override - { - cortex_m::disable_interrupt(irq::can1_sce); - } - - private: - friend class can_peripheral_manager; - - void driver_baud_rate(hal::u32 p_hertz) override - { - m_manager->baud_rate(p_hertz); - } - - void driver_filter_mode(accept) override - { - // this does nothing for now. We should consider dropping this in favor of - // always using a filter to manager message acceptance. A single mask - // filter with its mask set to all ZEROs would do the trick. - } - - void driver_on_bus_off(optional_bus_off_handler p_callback) override - { - m_bus_off_handler = p_callback; - } - - void driver_bus_on() override - { - m_manager->bus_on(); - } - - hal::v5::strong_ptr m_manager; - can_bus_manager::optional_bus_off_handler m_bus_off_handler{}; - }; - - return hal::v5::make_strong_ptr(p_allocator, p_manager); -} - -/** - * @brief Acquire an `hal::can_interrupt` implementation - * - * @return interrupt - object implementing the `hal::can_interrupt` interface - * for this can peripheral. - */ -hal::v5::strong_ptr acquire_can_interrupt( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager) -{ - class interrupt : public hal::can_interrupt - { - public: - interrupt(hal::v5::strong_ptr const& p_manager) - : m_manager(p_manager) - { - } - interrupt(interrupt const&) = delete; - interrupt& operator=(interrupt const&) = delete; - interrupt(interrupt&&) = delete; - interrupt& operator=(interrupt&&) = delete; - ~interrupt() override - { - m_manager->on_receive(std::nullopt); - } - - private: - void driver_on_receive(optional_receive_handler p_callback) override - { - m_manager->on_receive(p_callback); - } - hal::v5::strong_ptr m_manager; - }; - - return hal::v5::make_strong_ptr(p_allocator, p_manager); -} - -/** - * @brief Acquire a set of 4x standard identifier filters - * - * @return identifier_filter_set - A set of 4x identifier filters. When - * destroyed, releases the filter resource it held on to. - */ -std::array, 4> -acquire_can_identifier_filter( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager, - can_fifo p_fifo) -{ - struct identifier_filter : public hal::can_identifier_filter - { - identifier_filter(can_filter_resource p_resource) - : m_resource(p_resource) - { - } - - void driver_allow(std::optional p_id) override - { - auto const id = p_id.value_or(0 /* disable_id.standard */); - - set_filter_activation_state(m_resource.filter_index, - filter_activation::not_active); - - auto& filter = can1_reg->filter_registers[m_resource.filter_index]; - auto const reg = standard_id_to_stm_filter(id); - - switch (m_resource.word_index) { - case 0: - hal::bit_modify(filter.FR1) - .insert(reg); - break; - case 1: - hal::bit_modify(filter.FR1) - .insert(reg); - break; - case 2: - hal::bit_modify(filter.FR2) - .insert(reg); - break; - case 3: - hal::bit_modify(filter.FR2) - .insert(reg); - break; - default: - hal::safe_throw(hal::operation_not_supported(this)); - break; - } - - set_filter_activation_state(m_resource.filter_index, - filter_activation::active); - } - - can_filter_resource m_resource; - }; - - struct filter_set - { - filter_set(hal::v5::strong_ptr const& p_manager, - can_fifo p_fifo) - : m_manager(p_manager) - , filters{ - identifier_filter{ { .filter_index = 0, .word_index = 0 } }, - identifier_filter{ { .filter_index = 0, .word_index = 1 } }, - identifier_filter{ { .filter_index = 0, .word_index = 2 } }, - identifier_filter{ { .filter_index = 0, .word_index = 3 } }, - } - { - auto const available_filter = p_manager->available_filter(); - for (auto& filter : filters) { - filter.m_resource.filter_index = available_filter; - } - // Required to change filter scale and type - set_filter_bank_mode(filter_bank_master_control::initialization); - - // On scope exit, whether via a return or an exception, invoke this. - nonstd::scope_exit on_exit([available_filter]() { - set_filter_bank_mode(filter_bank_master_control::active); - set_filter_activation_state(available_filter, - filter_activation::active); - }); - - set_filter_activation_state(available_filter, - filter_activation::not_active); - set_filter_scale(available_filter, filter_scale::dual_16_bit_scale); - set_filter_type(available_filter, filter_type::list); - set_filter_fifo_assignment(available_filter, p_fifo); - - auto const disable_reg = - standard_id_to_stm_filter(0 /* disable_id.standard*/); - auto const disable_mask = (disable_reg << 16) | disable_reg; - can1_reg->filter_registers[available_filter].FR1 = disable_mask; - can1_reg->filter_registers[available_filter].FR2 = disable_mask; - } - - ~filter_set() - { - auto const index = filters[0].m_resource.filter_index; - m_manager->release_filter(index); - set_filter_activation_state(index, filter_activation::not_active); - } - - hal::v5::strong_ptr m_manager; - std::array filters; - }; - - auto set = - hal::v5::make_strong_ptr(p_allocator, p_manager, p_fifo); - - return { - hal::v5::strong_ptr( - set, &filter_set::filters, 0), - hal::v5::strong_ptr( - set, &filter_set::filters, 1), - hal::v5::strong_ptr( - set, &filter_set::filters, 2), - hal::v5::strong_ptr( - set, &filter_set::filters, 3), - }; -} - -/** - * @brief Acquire a pair of two extended identifier filters - * - * @return extended_identifier_filter_set - A set of 2x extended identifier - * filters. - */ -std::array, 2> -acquire_can_extended_identifier_filter( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager, - can_fifo p_fifo) -{ - struct extended_id_filter : public hal::can_extended_identifier_filter - { - extended_id_filter(can_filter_resource p_resource) - : m_resource(p_resource) - { - } - - void driver_allow(std::optional p_id) override - { - auto const id = p_id.value_or(/*disable_id.extended*/ 0); - auto const reg = extended_id_to_stm_filter(id); - - set_filter_activation_state(m_resource.filter_index, - filter_activation::not_active); - - auto& filter = can1_reg->filter_registers[m_resource.filter_index]; - - if (m_resource.word_index == 0) { - filter.FR1 = reg; - } else { - filter.FR2 = reg; - } - - set_filter_activation_state(m_resource.filter_index, - filter_activation::active); - } - - can_filter_resource m_resource; - }; - - struct filter_set - { - filter_set(hal::v5::strong_ptr const& p_manager, - can_fifo p_fifo) - : m_manager(p_manager) - , m_filters({ - { can_filter_resource{ .filter_index = 0, .word_index = 0 } }, - { can_filter_resource{ .filter_index = 0, .word_index = 1 } }, - }) - { - auto const available_filter = p_manager->available_filter(); - - for (auto& filter : m_filters) { - filter.m_resource.filter_index = available_filter; - } - - // Required to set filter scale and type - set_filter_bank_mode(filter_bank_master_control::initialization); - - // On scope exit, whether via a return or an exception, invoke this. - nonstd::scope_exit on_exit([available_filter]() { - set_filter_bank_mode(filter_bank_master_control::active); - set_filter_activation_state(available_filter, - filter_activation::active); - }); - - set_filter_activation_state(available_filter, - filter_activation::not_active); - set_filter_scale(available_filter, filter_scale::single_32_bit_scale); - set_filter_type(available_filter, filter_type::list); - set_filter_fifo_assignment(available_filter, p_fifo); - - auto const disable_mask = - extended_id_to_stm_filter(/*disable_id.extended*/ 0); - - can1_reg->filter_registers[available_filter].FR1 = disable_mask; - can1_reg->filter_registers[available_filter].FR2 = disable_mask; - } - - // NOLINTNEXTLINE(bugprone-exception-escape) - ~filter_set() - { - auto const index = m_filters[0].m_resource.filter_index; - m_manager->release_filter(index); - set_filter_activation_state(index, filter_activation::not_active); - } - - hal::v5::strong_ptr m_manager; - std::array m_filters; - }; - - auto set = - hal::v5::make_strong_ptr(p_allocator, p_manager, p_fifo); - - return { - hal::v5::strong_ptr( - set, &filter_set::m_filters, 0), - hal::v5::strong_ptr( - set, &filter_set::m_filters, 1), - }; -} - -/** - * @brief Acquire a pair of mask filters - * - * @param p_manager - Manager for which to extract the filter - * @param p_fifo - Select the FIFO to store the received message - * @return hal::v5::strong_ptr - A set of 2x standard - * mask filters - */ -std::array, 2> -acquire_can_mask_filter( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager, - can_fifo p_fifo) -{ - struct mask_filter : public hal::can_mask_filter - { - mask_filter(can_filter_resource p_resource) - : m_resource(p_resource) - { - } - - void driver_allow(std::optional p_pair) override - { - auto const selected_pair = p_pair.value_or(pair{ - .id = 0, // disable_id.standard, - .mask = 0x1FF, - }); - - auto& filter = can1_reg->filter_registers[m_resource.filter_index]; - - set_filter_activation_state(m_resource.filter_index, - filter_activation::not_active); - - auto const id_reg = standard_id_to_stm_filter(selected_pair.id); - auto const mask_reg = standard_id_to_stm_filter(selected_pair.mask); - - if (m_resource.word_index == 0) { - hal::bit_modify(filter.FR1) - .insert(id_reg) - .insert(mask_reg); - } else { - hal::bit_modify(filter.FR2) - .insert(id_reg) - .insert(mask_reg); - } - - set_filter_activation_state(m_resource.filter_index, - filter_activation::active); - } - - can_filter_resource m_resource; - }; - - struct mask_filter_set - { - mask_filter_set( - hal::v5::strong_ptr const& p_manager, - hal::u8 p_filter_index, - can_fifo p_fifo) - : m_manager(p_manager) - , filter{ - mask_filter{ { .filter_index = p_filter_index, .word_index = 0 } }, - mask_filter{ { .filter_index = p_filter_index, .word_index = 1 } }, - } - { - // Required to change filter scale and type - set_filter_bank_mode(filter_bank_master_control::initialization); - - // On scope exit, whether via a return or an exception, invoke this. - nonstd::scope_exit on_exit([p_filter_index]() { - set_filter_bank_mode(filter_bank_master_control::active); - set_filter_activation_state(p_filter_index, filter_activation::active); - }); - - set_filter_activation_state(p_filter_index, - filter_activation::not_active); - set_filter_scale(p_filter_index, filter_scale::dual_16_bit_scale); - set_filter_type(p_filter_index, filter_type::mask); - set_filter_fifo_assignment(p_filter_index, p_fifo); - - auto const disable_id_reg = - standard_id_to_stm_filter(0 /* disable_id.standard*/); - auto const disable_mask_reg = standard_id_to_stm_filter(0x1FF); - auto const disable_mask = (disable_mask_reg << 16) | disable_id_reg; - - can1_reg->filter_registers[p_filter_index].FR1 = disable_mask; - can1_reg->filter_registers[p_filter_index].FR2 = disable_mask; - } - - // NOLINTNEXTLINE(bugprone-exception-escape) - ~mask_filter_set() - { - auto const index = filter[0].m_resource.filter_index; - m_manager->release_filter(index); - set_filter_activation_state(index, filter_activation::not_active); - } - - hal::v5::strong_ptr m_manager; - std::array filter; - }; - - auto set = hal::v5::make_strong_ptr( - p_allocator, p_manager, p_manager->available_filter(), p_fifo); - - return { - hal::v5::strong_ptr(set, &mask_filter_set::filter, 0), - hal::v5::strong_ptr(set, &mask_filter_set::filter, 1), - }; -} - -/** - * @brief Acquire an extended mask filter - * - * @param p_manager - Manager for which to extract the filter - * @param p_fifo - Select the FIFO to store the received message - * @return hal::v5::strong_ptr - An extended - * mask filter - */ -hal::v5::strong_ptr -acquire_can_extended_mask_filter( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager, - can_fifo p_fifo) -{ - struct extended_mask_filter : public hal::can_extended_mask_filter - { - extended_mask_filter( - hal::v5::strong_ptr const& p_manager, - can_fifo p_fifo) - : m_manager(p_manager) - , m_filter_index(p_manager->available_filter()) - { - set_filter_bank_mode(filter_bank_master_control::initialization); - - // On scope exit, whether via a return or an exception, invoke this. - nonstd::scope_exit on_exit([this]() { - // Deactivate filter initialization mode (clear bit) - set_filter_bank_mode(filter_bank_master_control::active); - set_filter_activation_state(m_filter_index, filter_activation::active); - }); - - set_filter_activation_state(m_filter_index, - filter_activation::not_active); - set_filter_scale(m_filter_index, filter_scale::single_32_bit_scale); - set_filter_type(m_filter_index, filter_type::mask); - set_filter_fifo_assignment(m_filter_index, p_fifo); - - auto const id_reg = extended_id_to_stm_filter(/*disable_id.extended*/ 0); - auto const mask_reg = extended_id_to_stm_filter(0x1FFF'FFFF); - can1_reg->filter_registers[m_filter_index].FR1 = id_reg; - can1_reg->filter_registers[m_filter_index].FR2 = mask_reg; - } - - void driver_allow(std::optional p_pair) override - { - auto const selected_pair = p_pair.value_or(pair{ - .id = 0, // disable_id.standard, - .mask = 0x1FFF'FFFF, - }); - - auto const id_reg = extended_id_to_stm_filter(selected_pair.id); - auto const mask_reg = extended_id_to_stm_filter(selected_pair.mask); - - auto& filter = can1_reg->filter_registers[m_filter_index]; - - set_filter_activation_state(m_filter_index, - filter_activation::not_active); - filter.FR1 = id_reg; - filter.FR2 = mask_reg; - set_filter_activation_state(m_filter_index, filter_activation::active); - } - - ~extended_mask_filter() override - { - m_manager->release_filter(m_filter_index); - set_filter_activation_state(m_filter_index, - filter_activation::not_active); - } - - hal::v5::strong_ptr m_manager; - hal::u8 m_filter_index; - }; - - return hal::v5::make_strong_ptr( - p_allocator, p_manager, p_fifo); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/can_reg.hpp b/src/stm32f1/can_reg.hpp deleted file mode 100644 index d77ba6b..0000000 --- a/src/stm32f1/can_reg.hpp +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include -#include - -namespace hal::stm32f1 { - -struct can_tx_mailbox_t -{ - u32 volatile TIR; - u32 volatile TDTR; - u32 volatile TDLR; - u32 volatile TDHR; -}; - -struct can_fifo_mailbox_t -{ - u32 volatile RIR; - u32 volatile RDTR; - u32 volatile RDLR; - u32 volatile RDHR; -}; - -struct can_filter_register_t -{ - u32 volatile FR1; - u32 volatile FR2; -}; - -/** - * @brief Controller Area Network - */ - -struct can_reg_t -{ - u32 volatile MCR; - u32 volatile MSR; - u32 volatile TSR; - u32 volatile RF0R; - u32 volatile RF1R; - u32 volatile IER; - u32 volatile ESR; - u32 volatile BTR; - std::array reserved0; - std::array transmit_mailbox; - std::array fifo_mailbox; - std::array reserved1; - u32 volatile FMR; - u32 volatile FM1R; - u32 volatile reserved2; - u32 volatile FS1R; - u32 volatile reserved3; - u32 volatile FFA1R; - u32 volatile reserved4; - u32 volatile FA1R; - std::array volatile reserved5; - // Limited to only 14 on connectivity line devices - std::array filter_registers; -}; - -inline auto* can1_reg = reinterpret_cast(0x4000'6400); - -/// This struct holds bit timing values. -/// It is HW mapped to a 32-bit register: BTR (pg. 683). -struct bus_timing // NOLINT -{ - /// Baud Rate Prescaler - static constexpr auto prescalar = bit_mask::from<0, 9>(); - /// Time Segment 1 - static constexpr auto time_segment1 = bit_mask::from<16, 19>(); - /// Time Segment 2 - static constexpr auto time_segment2 = bit_mask::from<20, 22>(); - /// Resynchronization Jump Width - static constexpr auto sync_jump_width = bit_mask::from<24, 25>(); - /// Loop back mode (debug) - static constexpr auto loop_back_mode = bit_mask::from<30>(); - /// Silent Mode (debug) - static constexpr auto silent_mode = bit_mask::from<31>(); -}; - -/// This struct holds bit values for master control of CANx. -/// It is HW mapped to a 32-bit register: MCR (pg. 674). -struct master_control // NOLINT -{ - /// Software sets this bit to request the CAN hardware to enter - /// initialization mode. - static constexpr auto initialization_request = bit_mask::from<0>(); - /// Software sets this bit to request the CAN hardware to enter sleep mode. - static constexpr auto sleep_mode_request = bit_mask::from<1>(); - /// Set the transmission order when several mailboxes are pending at the - /// same time. - static constexpr auto transmit_fifo_priority = bit_mask::from<2>(); - /// Lock the FIFO from receiving new messages. - static constexpr auto receive_fifo_locked = bit_mask::from<3>(); - /// Disable CAN hardware from retransmiting until successfully transmitted. - static constexpr auto no_automatic_retransmission = bit_mask::from<4>(); - /// Controls the behavior of the CAN hardware on message reception during - /// Sleep. - static constexpr auto automatic_wakeup_mode = bit_mask::from<5>(); - /// Controls the behavior of the CAN hardware on leaving Bus-Off state. - static constexpr auto automatic_bus_off_management = bit_mask::from<6>(); - /// Enable Time Triggered Communication mode. - static constexpr auto time_triggered_comm_mode = bit_mask::from<7>(); - /// Force a master reset of the bxCan and go to Sleep mode. - static constexpr auto can_master_reset = bit_mask::from<15>(); - /// Freeze CAN reception/transmission during debug. - static constexpr auto debug_freeze = bit_mask::from<16>(); -}; - -/// This struct holds bit assignments for the Master Status Register (MSR) -/// It is HW mapped to a 32-bit register: MSR (pg. 676). -struct master_status // NOLINT -{ - /// Indicates to the software that the CAN hardware is now in initialization - /// mode - static constexpr auto initialization_acknowledge = bit_mask::from<0>(); - /// Indicates to the software that the CAN hardware is now in Sleep mode. - static constexpr auto sleep_acknowledge = bit_mask::from<1>(); - /// Set by hardware when a bit of the ESR has been set - static constexpr auto error_interrupt = bit_mask::from<2>(); - /// Set by hardware to signal that a SOF bit has been set. - static constexpr auto wakeup_interrupt = bit_mask::from<3>(); - /// Set by hardware to signal that the bxCan has entered sleep. - static constexpr auto sleep_acknowledge_interrupt = bit_mask::from<4>(); - /// Indicates if the CAN is a Transmitter - static constexpr auto transmit_mode = bit_mask::from<8>(); - /// Indicates if the CAN is a receiver - static constexpr auto receive_mode = bit_mask::from<9>(); - /// Holds the last value of Rx - static constexpr auto last_sample_point = bit_mask::from<10>(); - /// Monitors the actual value of the CAN_Rx pin. - static constexpr auto can_rx_signal = bit_mask::from<11>(); -}; - -/// This struct holds CANx transmit status information. -/// It is HW mapped to a 32-bit register: TSR (pg. 677). -struct transmit_status // NOLINT -{ - /// Mailbox 0 - Set by hardware when the last request (transmit or abort) - /// has been completed - static constexpr auto request_completed_mailbox0 = bit_mask::from<0>(); - /// Mailbox 0 - Hardware updates this bit after each transmission attempt - static constexpr auto transmission_ok_mailbox0 = bit_mask::from<1>(); - /// Mailbox 0 - Set when the previous TX failed due to arbitration lost - static constexpr auto arbitration_lost_mailbox0 = bit_mask::from<2>(); - /// Mailbox 0 - Set when the previous TX failed due to an error - static constexpr auto transmission_error_mailbox0 = bit_mask::from<3>(); - /// Mailbox 0 - Set by software to abort the transmission for the mailbox - static constexpr auto abort_request_mailbox0 = bit_mask::from<7>(); - /// Mailbox 1 - Set by hardware when the last request (transmit or abort) - /// has been completed - static constexpr auto request_completed_mailbox1 = bit_mask::from<8>(); - /// Mailbox 1 - Hardware updates this bit after each transmission attempt - static constexpr auto transmission_ok_mailbox1 = bit_mask::from<9>(); - /// Mailbox 1 - Set when the previous TX failed due to arbitration lost - static constexpr auto arbitration_lost_mailbox1 = bit_mask::from<10>(); - /// Mailbox 1 - Set when the previous TX failed due to an error - static constexpr auto transmission_error_mailbox1 = bit_mask::from<11>(); - /// Mailbox 1 - Set by software to abort the transmission for the mailbox - static constexpr auto abort_request_mailbox1 = bit_mask::from<15>(); - /// Mailbox 2 - Set by hardware when the last request (transmit or abort) - /// has been completed - static constexpr auto request_completed_mailbox2 = bit_mask::from<16>(); - /// Mailbox 2 - Hardware updates this bit after each transmission attempt - static constexpr auto transmission_ok_mailbox2 = bit_mask::from<17>(); - /// Mailbox 2 - Set when the previous TX failed due to arbitration lost - static constexpr auto arbitration_lost_mailbox2 = bit_mask::from<18>(); - /// Mailbox 2 - Set when the previous TX failed due to an error - static constexpr auto transmission_error_mailbox2 = bit_mask::from<19>(); - /// Mailbox 2 - Set by software to abort the transmission for the mailbox - static constexpr auto abort_request_mailbox2 = bit_mask::from<23>(); - /// Number of empty mailboxes - static constexpr auto mailbox_code = bit_mask::from<24, 25>(); - /// Mailbox 0 - Set by hardware to indicate empty - static constexpr auto transmit_mailbox0_empty = bit_mask::from<26>(); - /// Mailbox 1 - Set by hardware to indicate empty - static constexpr auto transmit_mailbox1_empty = bit_mask::from<27>(); - /// Mailbox 2 - Set by hardware to indicate empty - static constexpr auto transmit_mailbox2_empty = bit_mask::from<28>(); - /// Set by hardware when more than one mailbox is pending and mailbox 0 has - /// lower priority - static constexpr auto lowest_priority_flag_mailbox0 = bit_mask::from<29>(); - /// Set by hardware when more than one mailbox is pending and mailbox 1 has - /// lower priority - static constexpr auto lowest_priority_flag_mailbox1 = bit_mask::from<30>(); - /// Set by hardware when more than one mailbox is pending and mailbox 2 has - /// lower priority - static constexpr auto lowest_priority_flag_mailbox2 = bit_mask::from<31>(); -}; - -/// This struct holds the bitmap for enabling CANx interrupts. -/// It is HW mapped to a 32-bit register: TSR (pg. 680). -struct interrupt_enable_register // NOLINT -{ - /// Transmit mailbox empty interrupt enable - static constexpr auto transmit_mailbox_empty = bit_mask::from<0>(); - /// FIFO 0 message pending interrupt enable - static constexpr auto fifo0_message_pending = bit_mask::from<1>(); - /// FIFO 0 full interrupt enable - static constexpr auto fifo0_full = bit_mask::from<2>(); - /// FIFO 0 overrun interrupt enable - static constexpr auto fifo0_overrun = bit_mask::from<3>(); - /// FIFO 1 message pending interrupt enable - static constexpr auto fifo1_message_pending = bit_mask::from<4>(); - /// FIFO 1 full interrupt enable - static constexpr auto fifo1_full = bit_mask::from<5>(); - /// FIFO 1 overrun interrupt enable - static constexpr auto fifo1_overrun = bit_mask::from<6>(); - /// Error warning interrupt enable - static constexpr auto error_warning = bit_mask::from<8>(); - /// Error passive interrupt enable - static constexpr auto error_passive = bit_mask::from<9>(); - /// Bus-off interrupt enable - static constexpr auto bus_off = bit_mask::from<10>(); - /// Last error code interrupt enable - static constexpr auto last_error_code = bit_mask::from<11>(); - /// Error interrupt enable - static constexpr auto error_interrupt = bit_mask::from<15>(); - /// Wakeup interrupt enable - static constexpr auto wakeup = bit_mask::from<16>(); - /// Sleep interrupt enable - static constexpr auto sleep = bit_mask::from<17>(); -}; - -/// Strut holding the masks for the error status register -struct error_status_register -{ - /// Set to 1 if the device has been put in to the bus off state - static constexpr auto bus_off = bit_mask::from<2>(); -}; - -/// This struct holds the bitmap for the mailbox identifier. -/// It is represents 32-bit register: CAN_TIxR(0 - 2) (pg. 685). -/// It is represents 32-bit register: CAN_RIxR(0 - 1) (pg. 688). -struct mailbox_identifier // NOLINT -{ - enum class id_type : std::uint8_t - { - standard = 0, - extended = 1, - }; - - /// Transmit - static constexpr auto transmit_mailbox_request = bit_mask::from<0>(); - /// Receive/Transmit - static constexpr auto remote_request = bit_mask::from<1>(); - /// Receive/Transmit - static constexpr auto identifier_type = bit_mask::from<2>(); - /// Receive/Transmit - static constexpr auto standard_identifier = bit_mask::from<21, 31>(); - /// Receive/Transmit - static constexpr auto extended_identifier = bit_mask::from<3, 31>(); -}; - -/// This struct holds the bitmap for data length control and time stamp. -/// It is represents 32-bit register: CAN_TDTxR(0 - 2) (pg. 686). -/// It is represents 32-bit register: CAN_RDTxR(0 - 1) (pg. 689). -struct frame_length_and_info // NOLINT -{ - /// Receive/Transmit - static constexpr auto data_length_code = bit_mask::from<0, 3>(); - /// Transmit - static constexpr auto transmit_global_time = bit_mask::from<8>(); - /// Receive - static constexpr auto filter_match_index = bit_mask::from<8, 15>(); - /// Receive/Transmit - static constexpr auto message_time_stamp = bit_mask::from<16, 31>(); -}; - -/// This struct holds the bitmap for the FIFOx Status -/// It is represents 32-bit register: CAN_RFxR(0 - 1) (pg. 680). -struct fifo_status // NOLINT -{ - /// Indicates how many messages are pending in the receive FIFO - static constexpr auto messages_pending = bit_mask::from<0, 1>(); - /// Set by hardware when three messages are stored in the FIFO. - static constexpr auto is_fifo_full = bit_mask::from<3>(); - /// Set by hardware when a new message has been released and passed the - /// filter while the FIFO is full. - static constexpr auto is_fifo_overrun = bit_mask::from<4>(); - /// Release the output mailbox of the FIFO. - static constexpr auto release_output_mailbox = bit_mask::from<5>(); -}; - -/// This struct holds the bitmap for the filter master control. -/// It is represents 32-bit register: CAN_FMR (pg. 691). -struct filter_master -{ - /// Initialization mode for filter banks - static constexpr auto initialization_mode = bit_mask::from<0>(); - /// Defines the start bank for CAN2 - static constexpr auto can2_start_bank = bit_mask::from<8, 13>(); -}; - -/// This enumeration labels the initialization state of a filter. -/// Used with CAN Filter Master Register (CAN_FMR) (pg. 691). -enum class filter_bank_master_control : std::uint8_t -{ - /// Active filters state - active = 0, - /// Initialization state for the filter - initialization = 1 -}; - -/// This enumeration labels the mode of a filter -/// Used with CAN Filter Mode Register (CAN_FM1R) (pg. 692) -enum class filter_type : std::uint8_t -{ - /// Mask what bits in the identifier to accept - mask = 0, - /// List the identifier to accept - list = 1 -}; - -/// This enumeration labels the scale of a filter -/// Used with CAN Filter Scale Register (CAN_FS1R) (pg. 692) -enum class filter_scale : std::uint8_t -{ - /// Use two 16 bit identifiers - dual_16_bit_scale = 0, - /// Use one 32 bit identifier - single_32_bit_scale = 1 -}; - -/// This enumeration labels the activation state of a filter -/// Used with CAN Filter Activation Register (CAN_FFA1R) (pg. 693) -enum class filter_activation : std::uint8_t -{ - /// Disable filter - not_active = 0, - /// Enable fIlter - active = 1 -}; - -struct can_id -{ - static constexpr auto standard_id = hal::bit_mask::from(10, 0); - static constexpr auto standard_id_part = hal::bit_mask::from(29, 0); -}; - -struct standard_filter_bank -{ - static constexpr auto standard_id1 = hal::bit_mask::from(5, 15); - static constexpr auto rtr1 = hal::bit_mask::from(4); - static constexpr auto id_extension1 = hal::bit_mask::from(3); - static constexpr auto extended_id1 = hal::bit_mask::from(0, 2); - - static constexpr auto sub_bank1 = hal::bit_mask::from(0, 15); - static constexpr auto sub_bank2 = hal::bit_mask::from(16, 31); -}; -struct extended_filter_bank -{ - static constexpr auto id = hal::bit_mask::from(3, 31); - static constexpr auto id_extension = hal::bit_mask::from(2); - static constexpr auto rtr = hal::bit_mask::from(1); - static constexpr auto reserved = hal::bit_mask::from(0); -}; -} // namespace hal::stm32f1 diff --git a/src/stm32f1/clock.cpp b/src/stm32f1/clock.cpp deleted file mode 100644 index a355060..0000000 --- a/src/stm32f1/clock.cpp +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -#include "flash_reg.hpp" -#include "rcc_reg.hpp" - -namespace hal::stm32f1 { - -namespace { -hal::hertz rtc_clock_rate = 0.0_Hz; // defaults to "no clock" -hal::hertz usb_clock_rate = 0.0_Hz; // pll is required thus undefined -hal::hertz pll_clock_rate = 0.0_Hz; // undefined until pll is enabled -hal::hertz ahb_clock_rate = internal_high_speed_oscillator; -hal::hertz apb1_clock_rate = internal_high_speed_oscillator; -hal::hertz apb2_clock_rate = internal_high_speed_oscillator; -hal::hertz timer_apb1_clock_rate = internal_high_speed_oscillator; -hal::hertz timer_apb2_clock_rate = internal_high_speed_oscillator; -hal::hertz adc_clock_rate = internal_high_speed_oscillator / 2; -} // namespace - -/// @attention If configuration of the system clocks is desired, one should -/// consult the user manual of the target MCU in use to determine -/// the valid clock configuration values that can/should be used. -/// The Initialize() method is only responsible for configuring the -/// clock system based on configurations in the -/// clock_configuration. Incorrect configurations may result in a -/// hard fault or cause the clock system(s) to supply incorrect -/// clock rate(s). -/// -/// @see Figure 11. Clock Tree -/// https://www.st.com/resource/en/reference_manual/cd00171190-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf#page=126 -void configure_clocks(clock_tree p_clock_tree) -{ - hal::hertz system_clock = 0.0_Hz; - - // ========================================================================= - // Step 1. Select internal clock source for everything. - // Make sure PLLs are not clock sources for everything. - // ========================================================================= - // Step 1.1 Set SystemClock to HSI - clock_configuration::reg().insert( - value(system_clock_select::high_speed_internal)); - - // Step 1.4 Reset RTC clock registers - rtc_register::reg().set(rtc_register::backup_domain_reset); - - // Manually clear the RTC reset bit - rtc_register::reg().clear(rtc_register::backup_domain_reset); - - // ========================================================================= - // Step 2. Disable PLL and external clock sources - // ========================================================================= - clock_control::reg() - // Step 2.1 Disable PLLs - .clear(clock_control::pll_enable) - // Step 2.1 Disable External Oscillators - .clear(clock_control::external_osc_enable); - - // ========================================================================= - // Step 3. Enable External Oscillators - // ========================================================================= - // Step 3.1 Enable High speed external Oscillator - if (p_clock_tree.high_speed_external > 1.0_MHz) { - clock_control::reg().set(clock_control::external_osc_enable); - - while (!bit_extract( - clock_control::reg().get())) { - continue; - } - } - - // Step 3.2 Enable Low speed external Oscillator - if (p_clock_tree.low_speed_external > 1.0_MHz) { - rtc_register::reg().set(rtc_register::low_speed_osc_enable); - - while (!bit_extract( - rtc_register::reg().get())) { - continue; - } - } - - // ========================================================================= - // Step 4. Set oscillator source for PLLs - // ========================================================================= - clock_configuration::reg() - .insert( - (p_clock_tree.pll.source == pll_source::high_speed_external_divided_by_2)) - .insert(value(p_clock_tree.pll.source)); - - // ========================================================================= - // Step 5. Setup PLLs and enable them where necessary - // ========================================================================= - if (p_clock_tree.pll.enable) { - clock_configuration::reg().insert( - value(p_clock_tree.pll.multiply)); - - clock_control::reg().set(); - - while (!bit_extract(clock_control::reg().get())) { - continue; - } - - switch (p_clock_tree.pll.source) { - case pll_source::internal_8mhz_divided_by_2: - pll_clock_rate = internal_high_speed_oscillator / 2; - break; - case pll_source::high_speed_external: - pll_clock_rate = p_clock_tree.high_speed_external; - break; - case pll_source::high_speed_external_divided_by_2: - pll_clock_rate = p_clock_tree.high_speed_external / 2; - break; - } - - // Multiply the PLL clock up to the correct rate. - float multiply = value(p_clock_tree.pll.multiply); - pll_clock_rate = pll_clock_rate * (multiply + 2.0f); - } - - // ========================================================================= - // Step 6. Setup peripheral dividers - // ========================================================================= - clock_configuration::reg() - // Step 6.1 Set USB divider - .insert( - value(p_clock_tree.pll.usb.divider)) - // Step 6.2 Set AHB divider - .insert(value(p_clock_tree.ahb.divider)) - // Step 6.3 Set APB1 divider - .insert( - value(p_clock_tree.ahb.apb1.divider)) - // Step 6.4 Set APB2 divider - .insert( - value(p_clock_tree.ahb.apb2.divider)) - // Step 6.5 Set ADC divider - .insert( - value(p_clock_tree.ahb.apb2.adc.divider)); - - // ========================================================================= - // Step 7. Set System Clock and RTC Clock - // ========================================================================= - uint32_t target_clock_source = value(p_clock_tree.system_clock); - - // Step 7.1 Set the Flash wait states appropriately prior to setting the - // system clock frequency. Failure to do this will cause the system - // to be unable to read from flash, resulting in the platform - // locking up. See p.60 of RM0008 for the Flash ACR register - if (p_clock_tree.system_clock == system_clock_select::pll) { - if (pll_clock_rate <= 24.0_MHz) { - // 0 Wait states - bit_modify(flash->acr).insert()>(0b000U); - } else if (24.0_MHz <= pll_clock_rate && pll_clock_rate <= 48.0_MHz) { - // 1 Wait state - bit_modify(flash->acr).insert()>(0b001U); - } else { - // 2 Wait states - bit_modify(flash->acr).insert()>(0b010U); - } - } - - // Step 7.2 Set system clock source - // NOTE: return error if clock = system_clock_select::high_speed_external - // and - // high speed external is not enabled. - clock_configuration::reg().insert( - value(p_clock_tree.system_clock)); - - while (bit_extract( - clock_configuration::reg().get()) != target_clock_source) { - continue; - } - - switch (p_clock_tree.system_clock) { - case system_clock_select::high_speed_internal: - system_clock = internal_high_speed_oscillator; - break; - case system_clock_select::high_speed_external: - system_clock = p_clock_tree.high_speed_external; - break; - case system_clock_select::pll: - system_clock = pll_clock_rate; - break; - } - - rtc_register::reg() - // Step 7.3 Set the RTC oscillator source - .insert(value(p_clock_tree.rtc.source)) - // Step 7.4 Enable/Disable the RTC - .insert(p_clock_tree.rtc.enable); - - // ========================================================================= - // Step 8. Define the clock rates for the system - // ========================================================================= - switch (p_clock_tree.ahb.divider) { - case ahb_divider::divide_by_1: - ahb_clock_rate = system_clock / 1; - break; - case ahb_divider::divide_by_2: - ahb_clock_rate = system_clock / 2; - break; - case ahb_divider::divide_by_4: - ahb_clock_rate = system_clock / 4; - break; - case ahb_divider::divide_by_8: - ahb_clock_rate = system_clock / 8; - break; - case ahb_divider::divide_by_16: - ahb_clock_rate = system_clock / 16; - break; - case ahb_divider::divide_by_64: - ahb_clock_rate = system_clock / 64; - break; - case ahb_divider::divide_by_128: - ahb_clock_rate = system_clock / 128; - break; - case ahb_divider::divide_by_256: - ahb_clock_rate = system_clock / 256; - break; - case ahb_divider::divide_by_512: - ahb_clock_rate = system_clock / 512; - break; - } - - switch (p_clock_tree.ahb.apb1.divider) { - case apb_divider::divide_by_1: - apb1_clock_rate = ahb_clock_rate / 1; - break; - case apb_divider::divide_by_2: - apb1_clock_rate = ahb_clock_rate / 2; - break; - case apb_divider::divide_by_4: - apb1_clock_rate = ahb_clock_rate / 4; - break; - case apb_divider::divide_by_8: - apb1_clock_rate = ahb_clock_rate / 8; - break; - case apb_divider::divide_by_16: - apb1_clock_rate = ahb_clock_rate / 16; - break; - } - - switch (p_clock_tree.ahb.apb2.divider) { - case apb_divider::divide_by_1: - apb2_clock_rate = ahb_clock_rate / 1; - break; - case apb_divider::divide_by_2: - apb2_clock_rate = ahb_clock_rate / 2; - break; - case apb_divider::divide_by_4: - apb2_clock_rate = ahb_clock_rate / 4; - break; - case apb_divider::divide_by_8: - apb2_clock_rate = ahb_clock_rate / 8; - break; - case apb_divider::divide_by_16: - apb2_clock_rate = ahb_clock_rate / 16; - break; - } - - switch (p_clock_tree.rtc.source) { - case rtc_source::no_clock: - rtc_clock_rate = 0.0_Hz; - break; - case rtc_source::low_speed_internal: - rtc_clock_rate = internal_low_speed_oscillator; - break; - case rtc_source::low_speed_external: - rtc_clock_rate = p_clock_tree.low_speed_external; - break; - case rtc_source::high_speed_external_divided_by_128: - rtc_clock_rate = p_clock_tree.high_speed_external / 128; - break; - } - - switch (p_clock_tree.pll.usb.divider) { - case usb_divider::divide_by_1: - usb_clock_rate = pll_clock_rate; - break; - case usb_divider::divide_by_1_point_5: - usb_clock_rate = (pll_clock_rate * 2) / 3; - break; - } - - switch (p_clock_tree.ahb.apb1.divider) { - case apb_divider::divide_by_1: - timer_apb1_clock_rate = apb1_clock_rate; - break; - default: - timer_apb1_clock_rate = apb1_clock_rate * 2; - break; - } - - switch (p_clock_tree.ahb.apb2.divider) { - case apb_divider::divide_by_1: - timer_apb2_clock_rate = apb2_clock_rate; - break; - default: - timer_apb2_clock_rate = apb2_clock_rate * 2; - break; - } - - switch (p_clock_tree.ahb.apb2.adc.divider) { - case adc_divider::divide_by_2: - adc_clock_rate = apb2_clock_rate / 2; - break; - case adc_divider::divide_by_4: - adc_clock_rate = apb2_clock_rate / 4; - break; - case adc_divider::divide_by_6: - adc_clock_rate = apb2_clock_rate / 6; - break; - case adc_divider::divide_by_8: - adc_clock_rate = apb2_clock_rate / 8; - break; - } -} - -/// @return the clock rate frequency of a peripheral -hal::hertz frequency(peripheral p_id) -{ - switch (p_id) { - case peripheral::i2s: - return pll_clock_rate; - case peripheral::usb: - return usb_clock_rate; - case peripheral::flitf: - return internal_high_speed_oscillator; - - // Arm Cortex running clock rate. - // This code does not utilize the /8 clock for the system timer, thus the - // clock rate for that subsystem is equal to the CPU running clock. - case peripheral::system_timer: - [[fallthrough]]; - case peripheral::cpu: - return ahb_clock_rate; - - // APB1 Timers - case peripheral::timer2: - [[fallthrough]]; - case peripheral::timer3: - [[fallthrough]]; - case peripheral::timer4: - [[fallthrough]]; - case peripheral::timer5: - [[fallthrough]]; - case peripheral::timer6: - [[fallthrough]]; - case peripheral::timer7: - [[fallthrough]]; - case peripheral::timer12: - [[fallthrough]]; - case peripheral::timer13: - [[fallthrough]]; - case peripheral::timer14: - return timer_apb1_clock_rate; - - // APB2 Timers - case peripheral::timer1: - [[fallthrough]]; - case peripheral::timer8: - [[fallthrough]]; - case peripheral::timer9: - [[fallthrough]]; - case peripheral::timer10: - [[fallthrough]]; - case peripheral::timer11: - return timer_apb2_clock_rate; - - case peripheral::adc1: - [[fallthrough]]; - case peripheral::adc2: - [[fallthrough]]; - case peripheral::adc3: - return adc_clock_rate; - default: { - auto id = value(p_id); - - if (id < apb1_bus) { - return ahb_clock_rate; - } - - if (apb1_bus <= id && id < apb2_bus) { - return apb1_clock_rate; - } - - if (apb2_bus <= id && id < beyond_bus) { - return apb2_clock_rate; - } - - return 0.0_Hz; - } - } - - return 0.0_Hz; -} - -void maximum_speed_using_internal_oscillator() -{ - using namespace hal::literals; - - configure_clocks(clock_tree{ - .high_speed_external = 0.0f, - .pll = { - .enable = true, - .source = pll_source::internal_8mhz_divided_by_2, - .multiply = pll_multiply::multiply_by_16, - .usb = { // NOTE: Cannot be used when using the internal oscillator - .divider = usb_divider::divide_by_1_point_5, - } - }, - .system_clock = system_clock_select::pll, - .ahb = { - .divider = ahb_divider::divide_by_1, - .apb1 = { - .divider = apb_divider::divide_by_2, - }, - .apb2 = { - .divider = apb_divider::divide_by_1, - .adc = { - .divider = adc_divider::divide_by_6, - } - }, - }, - }); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/dma.hpp b/src/stm32f1/dma.hpp deleted file mode 100644 index 93fae05..0000000 --- a/src/stm32f1/dma.hpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include -#include - -namespace hal::stm32f1::dma { -/// Namespace for the control registers (DMA->CCR) bit masks and predefined -/// settings constants. - -/// Declare this channel for Memory to memory mode -static constexpr auto memory_to_memory = hal::bit_mask::from<14>(); - -/// Configure the channel priority for this channel. -/// 0b00: Low -/// 0b01: Medium -/// 0b10: High -/// 0b11: Very high -static constexpr auto channel_priority = hal::bit_mask::from<12, 13>(); - -/// The size of each element of the memory. -/// 0b00: 8-bits -/// 0b01: 16-bits -/// 0b10: 32-bits -/// 0b11: Reserved -static constexpr auto memory_size = hal::bit_mask::from<10, 11>(); - -/// The peripheral register size. -/// 0b00: 8-bits -/// 0b01: 16-bits -/// 0b10: 32-bits -/// 0b11: Reserved -static constexpr auto peripheral_size = hal::bit_mask::from<8, 9>(); - -/// Activate memory increment mode, which will increment the memory address -/// with each transfer -static constexpr auto memory_increment_enable = hal::bit_mask::from<7>(); - -/// Activate memory increment mode, which will increment the peripheral -/// address with each transfer -static constexpr auto peripheral_increment_enable = hal::bit_mask::from<6>(); - -/// DMA will continuous load bytes into the buffer supplied in a circular -/// buffer manner. -static constexpr auto circular_mode = hal::bit_mask::from<5>(); - -/// Data transfer direction -/// 0: Read from peripheral -/// 1: Read from memory -static constexpr auto data_transfer_direction = hal::bit_mask::from<4>(); - -/// Enable interrupt on transfer error -static constexpr auto transfer_error_interrupt_enable = - hal::bit_mask::from<3>(); - -/// Enable interrupt on half of data transferred -static constexpr auto half_transfer_interrupt_enable = hal::bit_mask::from<2>(); - -/// Enable interrupt on complete transfer -static constexpr auto transfer_complete_interrupt_enable = - hal::bit_mask::from<1>(); - -/// Enable this DMA channel -static constexpr auto enable = hal::bit_mask::from<0>(); - -struct dma_channel_t -{ - std::uint32_t volatile configuration; - std::uint32_t volatile transfer_amount; - std::uint32_t volatile peripheral_address; - std::uint32_t volatile memory_address; - std::uint32_t volatile reserved; -}; - -struct dma_t -{ - std::uint32_t volatile interrupt_status; - std::uint32_t volatile interrupt_flag_clear; - std::array channel; -}; - -/// System control block address -inline constexpr auto dma1_addr = static_cast(0x4002'0000); -inline constexpr auto dma2_addr = static_cast(0x4002'0400); -// NOLINTBEGIN(performance-no-int-to-ptr) -inline auto* dma1 = reinterpret_cast(dma1_addr); -inline auto* dma2 = reinterpret_cast(dma2_addr); -// NOLINTEND(performance-no-int-to-ptr) -} // namespace hal::stm32f1::dma diff --git a/src/stm32f1/flash_reg.hpp b/src/stm32f1/flash_reg.hpp deleted file mode 100644 index bd090fe..0000000 --- a/src/stm32f1/flash_reg.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include - -namespace hal::stm32f1 { -struct flash_t -{ - u32 volatile acr; - u32 volatile keyr; - u32 volatile optkeyr; - u32 volatile sr; - u32 volatile cr; - u32 volatile ar; - u32 volatile reserved; - u32 volatile obr; - u32 volatile wrpr; - std::array reserved1; - u32 volatile keyr2; - u32 reserved2; - u32 volatile sr2; - u32 volatile cr2; - u32 volatile ar2; -}; - -/// Pointer to the flash control register -inline flash_t* flash = reinterpret_cast(0x4002'2000); -} // namespace hal::stm32f1 diff --git a/src/stm32f1/gpio.cpp b/src/stm32f1/gpio.cpp deleted file mode 100644 index 80cd74d..0000000 --- a/src/stm32f1/gpio.cpp +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pin.hpp" -#include "power.hpp" - -namespace hal::stm32f1 { -namespace { -struct exti_reg_t -{ - /// Offset: 0x00 Interrupt Mask Register (R/W) - u32 volatile interrupt_mask; - /// Offset: 0x04 Event Mask Register (R/W) - u32 volatile event_mask; - /// Offset: 0x08 Rising Trigger Selection Register (R/W) - u32 volatile rising_trigger_selection; - /// Offset: 0x0C Falling Trigger Selection Register (R/W) - u32 volatile falling_trigger_selection; - /// Offset: 0x10 Software Interrupt Event Register Register (R/W) - u32 volatile software_interrupt_event; - /// Offset: 0x14 Pending Register (RC/W1) - u32 volatile pending; -}; - -constexpr std::uintptr_t stm_exti_addr = 0x4001'0400UL; -// NOLINTNEXTLINE(performance-no-int-to-ptr) -inline auto* exti_reg = reinterpret_cast(stm_exti_addr); - -std::array>, 16> - interrupt_handlers = {}; - -void external_interrupt_isr() -{ - // Find first interrupt to service - static constexpr auto pr_mask = hal::bit_mask::from(0, 15); - auto pending_reg = bit_extract(pr_mask, exti_reg->pending); - auto const pin_index = std::countr_zero(pending_reg); - - if (pin_index <= 15 && interrupt_handlers[pin_index]) { - // Determine which port is selected for given pin - int const afio_exticr_num = pin_index / 4; - int const afio_exti_bit_offset = (pin_index % 4) * 4; - auto const port_mask = - hal::bit_mask::from(afio_exti_bit_offset, afio_exti_bit_offset + 3); - auto const port = - bit_extract(port_mask, alternative_function_io->exticr[afio_exticr_num]); - - // Grab input pin value - auto const& gpio_reg = hal::stm32f1::gpio_reg('A' + port); - auto const pin_value = bit_extract(bit_mask::from(pin_index), gpio_reg.idr); - - // Call and handle callback - (*interrupt_handlers[pin_index])(pin_value); - auto const pending_mask = hal::bit_mask::from(pin_index); - hal::bit_modify(exti_reg->pending).set(pending_mask); - } -} - -u8 peripheral_to_letter(peripheral p_peripheral) -{ - // The numeric value of `peripheral::gpio_a` to ``peripheral::gpio_g` are - // contiguous in numeric value thus we can map letters 'A' to 'G' by doing - // this math here. - auto const offset = value(p_peripheral) - value(peripheral::gpio_a); - return 'A' + offset; -} -} // namespace - -gpio_manager::gpio_manager(peripheral p_port) - : m_port(p_port) -{ - if (not is_on(m_port)) { - power_on(m_port); - } -} - -gpio_manager::input gpio_manager::acquire_input_pin( - u8 p_pin, - input_pin::settings const& p_settings) -{ - return { m_port, p_pin, p_settings }; -} - -gpio_manager::output gpio_manager::acquire_output_pin( - u8 p_pin, - output_pin::settings const& p_settings) -{ - return { m_port, p_pin, p_settings }; -} - -gpio_manager::interrupt gpio_manager::acquire_interrupt_pin( - u8 p_pin, - interrupt_pin::settings const& p_settings) -{ - return { m_port, p_pin, p_settings }; -} - -gpio_manager::input::input(peripheral p_port, - u8 p_pin, - input_pin::settings const& p_settings) - : m_pin({ .port = peripheral_to_letter(p_port), .pin = p_pin }) -{ - reset_pin(m_pin); - gpio_manager::input::driver_configure(p_settings); -} - -void gpio_manager::input::driver_configure(settings const& p_settings) -{ - reset_pin(m_pin); - - if (p_settings.resistor == pin_resistor::pull_up) { - configure_pin(m_pin, input_pull_up); - } else if (p_settings.resistor == pin_resistor::pull_down) { - configure_pin(m_pin, input_pull_down); - } else { - configure_pin(m_pin, input_float); - } -} - -bool gpio_manager::input::driver_level() -{ - auto const& reg = gpio_reg(m_pin.port); - auto const pin_value = bit_extract(bit_mask::from(m_pin.pin), reg.idr); - return static_cast(pin_value); -} - -gpio_manager::output::output(peripheral p_port, - u8 p_pin, - output_pin::settings const& p_settings) - : m_pin({ .port = peripheral_to_letter(p_port), .pin = p_pin }) -{ - throw_if_pin_is_unavailable(m_pin); - gpio_manager::output::driver_configure(p_settings); -} - -void gpio_manager::output::driver_configure(settings const& p_settings) -{ - reset_pin(m_pin); - if (p_settings.open_drain) { - configure_pin(m_pin, open_drain_gpio_output); - } else { - configure_pin(m_pin, push_pull_gpio_output); - } - // NOTE: The `resistor` field is ignored in this function -} - -void gpio_manager::output::driver_level(bool p_high) -{ - if (p_high) { - // The first 16 bits of the register set the output state - gpio_reg(m_pin.port).bsrr = 1 << m_pin.pin; - } else { - // The last 16 bits of the register reset the output state - gpio_reg(m_pin.port).bsrr = 1 << (16 + m_pin.pin); - } -} - -bool gpio_manager::output::driver_level() -{ - auto const& reg = gpio_reg(m_pin.port); - auto const pin_value = bit_extract(bit_mask::from(m_pin.pin), reg.idr); - return static_cast(pin_value); -} - -gpio_manager::interrupt::interrupt(peripheral p_port, - u8 p_pin, - interrupt_pin::settings const& p_settings) - : m_pin({ .port = peripheral_to_letter(p_port), .pin = p_pin }) -{ - throw_if_pin_is_unavailable(m_pin); - setup_interrupt(); - driver_configure(p_settings); -} - -void gpio_manager::interrupt::setup_interrupt() -{ - // Verify EXTI line is not already in use - auto const exti_mask = hal::bit_mask::from(m_pin.pin); - auto const interrupt_enabled = - bit_extract(exti_mask, exti_reg->interrupt_mask); - auto const event_enabled = bit_extract(exti_mask, exti_reg->event_mask); - if (interrupt_enabled || event_enabled) { - hal::safe_throw(hal::device_or_resource_busy(this)); - } - - // Determine location of port selection for given pin and set the port - int const afio_exticr_num = m_pin.pin / 4; - int const afio_exti_bit_offset = (m_pin.pin % 4) * 4; - auto const port_mask = - hal::bit_mask::from(afio_exti_bit_offset, afio_exti_bit_offset + 3); - hal::bit_modify(alternative_function_io->exticr[afio_exticr_num]) - .insert(port_mask, static_cast(m_pin.port - 'A')); - - initialize_interrupts(); - // Some EXTI lines share the same IRQ, this skips enabling if this is the - // case. - auto const irq = get_irq(); - if (not hal::cortex_m::is_interrupt_enabled(irq)) { - cortex_m::enable_interrupt(irq, external_interrupt_isr); - } -} - -void gpio_manager::interrupt::driver_configure(settings const& p_settings) -{ - auto const exti_mask = hal::bit_mask::from(m_pin.pin); - hal::bit_modify(exti_reg->interrupt_mask).clear(exti_mask); - - reset_pin(m_pin); - if (p_settings.resistor == pin_resistor::pull_up) { - configure_pin(m_pin, input_pull_up); - } else if (p_settings.resistor == pin_resistor::pull_down) { - configure_pin(m_pin, input_pull_down); - } else { - configure_pin(m_pin, input_float); - } - - if (p_settings.trigger == trigger_edge::rising) { - hal::bit_modify(exti_reg->rising_trigger_selection).set(exti_mask); - hal::bit_modify(exti_reg->falling_trigger_selection).clear(exti_mask); - } else if (p_settings.trigger == trigger_edge::falling) { - hal::bit_modify(exti_reg->rising_trigger_selection).clear(exti_mask); - hal::bit_modify(exti_reg->falling_trigger_selection).set(exti_mask); - } else { - hal::bit_modify(exti_reg->rising_trigger_selection).set(exti_mask); - hal::bit_modify(exti_reg->falling_trigger_selection).set(exti_mask); - } - - hal::bit_modify(exti_reg->interrupt_mask).set(exti_mask); -} - -void gpio_manager::interrupt::driver_on_trigger( - hal::callback p_callback) -{ - interrupt_handlers[m_pin.pin] = p_callback; -} - -cortex_m::irq_t gpio_manager::interrupt::get_irq() -{ - switch (m_pin.pin) { - case 0: - return static_cast(irq::exti0); - case 1: - return static_cast(irq::exti1); - case 2: - return static_cast(irq::exti2); - case 3: - return static_cast(irq::exti3); - case 4: - return static_cast(irq::exti4); - case 5: - [[fallthrough]]; - case 6: - [[fallthrough]]; - case 7: - [[fallthrough]]; - case 8: - [[fallthrough]]; - case 9: - return static_cast(irq::exti9_5); - case 10: - [[fallthrough]]; - case 11: - [[fallthrough]]; - case 12: - [[fallthrough]]; - case 13: - [[fallthrough]]; - case 14: - [[fallthrough]]; - case 15: - [[fallthrough]]; - default: - return static_cast(irq::exti15_10); - } -} - -hal::v5::strong_ptr acquire_input_pin( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager, - u8 p_pin, - input_pin::settings const& p_settings) -{ - return hal::v5::make_strong_ptr( - p_allocator, p_manager->acquire_input_pin(p_pin, p_settings)); -} - -hal::v5::strong_ptr acquire_output_pin( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_manager, - u8 p_pin, - output_pin::settings const& p_settings) -{ - return hal::v5::make_strong_ptr( - p_allocator, p_manager->acquire_output_pin(p_pin, p_settings)); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/independent_watchdog.cpp b/src/stm32f1/independent_watchdog.cpp deleted file mode 100644 index 7d9ac42..0000000 --- a/src/stm32f1/independent_watchdog.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -using namespace std::chrono_literals; - -namespace { -struct independent_watchdog_registers -{ - // setters are to not acidentily write in reserved memory - - void set_kr(uint16_t p_value) - { - constexpr hal::bit_mask writeable_kr = hal::bit_mask::from(0, 15); - hal::bit_modify(kr).insert(p_value); - } - void set_pr(uint8_t p_value) - { - constexpr hal::bit_mask writeable_pr = hal::bit_mask::from(0, 2); - hal::bit_modify(pr).insert(p_value); - } - void set_rlr(uint16_t p_value) - { - constexpr hal::bit_mask writeable_rlr = hal::bit_mask::from(0, 11); - hal::bit_modify(rlr).insert(p_value); - } - - uint32_t volatile kr; - uint32_t volatile pr; - uint32_t volatile rlr; - uint32_t volatile sr; -}; - -auto* const iwdg_regs = - reinterpret_cast(0x40003000); -// NOLINTBEGIN(performance-no-int-to-ptr) -uint32_t* const reset_status_register = - reinterpret_cast(0x40021000 + 0x24); -// NOLINTEND(performance-no-int-to-ptr) - -} // namespace - -namespace hal::stm32f1 { -void independent_watchdog::start() -{ - iwdg_regs->set_kr(0xCCCC); -} -void independent_watchdog::reset() -{ - iwdg_regs->set_kr(0xAAAA); -} - -void independent_watchdog::set_countdown_time(hal::time_duration p_wait_time) -{ - // convert to counts of 40khz clock (figure 11, pg. 126) - constexpr hal::time_duration nano_seconds_per_clock_cycle = - 1'000'000'000ns / 40'000; - long long cycle_count = p_wait_time / nano_seconds_per_clock_cycle; - // frq divider starts a /4 (table 96, pg. 495) - cycle_count = cycle_count >> 2; - if (cycle_count == 0) { - throw hal::operation_not_supported(nullptr); - } - - hal::byte frq_divider = 0; - while (cycle_count > 0x1000 && frq_divider <= 7) { - cycle_count = cycle_count >> 1; - frq_divider++; - } - if (frq_divider >= 7) { - throw hal::operation_not_supported(nullptr); - } else { - // register's shouldn't be edited when bits are 1 (sec. 19.4.4, pg. 498) - if (bit_extract(bit_mask::from(0, 1), iwdg_regs->sr)) { - throw hal::resource_unavailable_try_again(nullptr); - } - iwdg_regs->set_kr(0x5555); - iwdg_regs->set_pr(frq_divider); - iwdg_regs->set_rlr(cycle_count - 1); - } -} - -bool independent_watchdog::check_flag() -{ - // sec. 8.3.10, pg. 152 - bit_mask const flag = bit_mask::from(29); - return bit_extract(flag, *reset_status_register); -} - -void independent_watchdog::clear_flag() -{ - // sec. 8.3.10, pg. 152 - bit_mask const reset_flag = bit_mask::from(24); - bit_modify(*reset_status_register).set(reset_flag); -} - -} // namespace hal::stm32f1 diff --git a/src/stm32f1/input_pin.cpp b/src/stm32f1/input_pin.cpp deleted file mode 100644 index c8fae23..0000000 --- a/src/stm32f1/input_pin.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include - -#include "pin.hpp" - -namespace hal::stm32f1 { -input_pin::input_pin(u8 p_port, // NOLINT - u8 p_pin) // NOLINT - - : m_port(p_port) - , m_pin(p_pin) -{ - input_pin::driver_configure({}); -} - -void input_pin::driver_configure(settings const& p_settings) -{ - pin_select const pin = { .port = m_port, .pin = m_pin }; - reset_pin(pin); - if (p_settings.resistor == pin_resistor::pull_up) { - configure_pin(pin, input_pull_up); - } else if (p_settings.resistor == pin_resistor::pull_down) { - configure_pin(pin, input_pull_down); - } else { - configure_pin(pin, input_float); - } -} - -bool input_pin::driver_level() -{ - auto const pin_value = - bit_extract(bit_mask::from(m_pin), gpio_reg(m_port).idr); - return static_cast(pin_value); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/interrupt.cpp b/src/stm32f1/interrupt.cpp deleted file mode 100644 index 32a20f7..0000000 --- a/src/stm32f1/interrupt.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -namespace hal::stm32f1 { -void initialize_interrupts() -{ - hal::cortex_m::initialize_interrupts(); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/output_pin.cpp b/src/stm32f1/output_pin.cpp deleted file mode 100644 index 901a5ca..0000000 --- a/src/stm32f1/output_pin.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include - -#include "pin.hpp" - -namespace hal::stm32f1 { -output_pin::output_pin(u8 p_port, // NOLINT - u8 p_pin, // NOLINT - output_pin::settings p_settings) - : m_port(p_port) - , m_pin(p_pin) -{ - // Ignore result as this function is infallible - output_pin::driver_configure(p_settings); -} - -void output_pin::driver_configure(settings const& p_settings) -{ - pin_select const pin = { .port = m_port, .pin = m_pin }; - reset_pin(pin); - if (p_settings.open_drain) { - configure_pin(pin, open_drain_gpio_output); - } else { - configure_pin(pin, push_pull_gpio_output); - } - // NOTE: The `resistor` field is ignored in this function -} - -void output_pin::driver_level(bool p_high) -{ - if (p_high) { - // The first 16 bits of the register set the output state - gpio_reg(m_port).bsrr = 1 << m_pin; - } else { - // The last 16 bits of the register reset the output state - gpio_reg(m_port).bsrr = 1 << (16 + m_pin); - } -} - -bool output_pin::driver_level() -{ - auto const pin_value = - bit_extract(bit_mask::from(m_pin), gpio_reg(m_port).idr); - - return static_cast(pin_value); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/pin.cpp b/src/stm32f1/pin.cpp deleted file mode 100644 index a0d3eea..0000000 --- a/src/stm32f1/pin.cpp +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include - -#include "pin.hpp" -#include "power.hpp" -#include "rcc_reg.hpp" - -namespace hal::stm32f1 { -namespace { -/// Returns a bit mask indicating where the config bits are in the config -/// registers. -bit_mask config_mask(u8 p_pin) -{ - return { - .position = static_cast((p_pin * 4) % 32), - .width = 4, - }; -} -/// Returns a bit mask indicating where the odr bit is in the output data -/// register. -bit_mask odr_mask(u8 p_pin) -{ - return { - .position = static_cast(p_pin), - .width = 1, - }; -} - -/// Returns the configuration control register for the specific pin. -/// Pins 0 - 7 are in CRL and Pins 8 - 15 are in CRH. -uint32_t volatile& config_register(pin_select const& p_pin_select) -{ - if (p_pin_select.pin <= 7) { - return gpio_reg(p_pin_select.port).crl; - } - return gpio_reg(p_pin_select.port).crh; -} - -/// Returns the output data register for the specific pin. -uint32_t volatile& odr_register(pin_select const& p_pin_select) -{ - return gpio_reg(p_pin_select.port).odr; -} - -void safely_power_on(pin_select const& p_pin_select) -{ - // Ensure that AFIO is powered on before attempting to access it - if (not is_on(peripheral::afio)) { - power_on(peripheral::afio); - } - - switch (p_pin_select.port) { - case 'A': - if (not is_on(peripheral::gpio_a)) { - power_on(peripheral::gpio_a); - } - break; - case 'B': - if (not is_on(peripheral::gpio_b)) { - power_on(peripheral::gpio_b); - } - break; - case 'C': - if (not is_on(peripheral::gpio_c)) { - power_on(peripheral::gpio_c); - } - break; - case 'D': - if (not is_on(peripheral::gpio_d)) { - power_on(peripheral::gpio_d); - } - break; - case 'E': - if (not is_on(peripheral::gpio_e)) { - power_on(peripheral::gpio_e); - } - break; - default: - hal::safe_throw(hal::argument_out_of_domain(nullptr)); - } -} - -std::array gpio_reg_map{ - reinterpret_cast(0x4001'0800), // 'A' - reinterpret_cast(0x4001'0c00), // 'B' - reinterpret_cast(0x4001'1000), // 'C' - reinterpret_cast(0x4001'1400), // 'D' - reinterpret_cast(0x4001'1800), // 'E' - reinterpret_cast(0x4001'1c00), // 'F' - reinterpret_cast(0x4001'2000), // 'G' -}; - -bool is_pin_reset(pin_select p_pin_select) -{ - auto& config_reg = config_register(p_pin_select); - auto const current_configuration = - bit_extract(config_mask(p_pin_select.pin), config_reg); - - return reset_pin_config == current_configuration; -} -} // namespace - -gpio_t& gpio_reg(u8 p_port) -{ - auto const offset = p_port - 'A'; - return *gpio_reg_map.at(offset); -} - -void throw_if_pin_is_unavailable(pin_select p_pin_select) -{ - if (not is_pin_reset(p_pin_select)) { - hal::safe_throw(hal::device_or_resource_busy(nullptr)); - } -} - -void configure_pin(pin_select p_pin_select, pin_config_t p_config) -{ - // The GPIO pins PB3, PB4, and PA15 are default initalized to be used for - // JTAG purposes. This releases them if they are being configured - if ((p_pin_select.port == 'B' && p_pin_select.pin == 3) || - (p_pin_select.port == 'B' && p_pin_select.pin == 4) || - (p_pin_select.port == 'A' && p_pin_select.pin == 15)) { - release_jtag_pins(); - } - auto& config_reg = config_register(p_pin_select); - auto& odr_reg = odr_register(p_pin_select); - safely_power_on(p_pin_select); - throw_if_pin_is_unavailable(p_pin_select); - - auto const config = bit_value(0) - .insert(p_config.CNF1) - .insert(p_config.CNF0) - .insert(p_config.MODE) - .get(); - - bit_modify(config_reg).insert(config_mask(p_pin_select.pin), config); - bit_modify(odr_reg).insert(odr_mask(p_pin_select.pin), p_config.PxODR); -} - -void reset_pin(pin_select p_pin_select) -{ - auto& config_reg = config_register(p_pin_select); - config_reg = bit_modify(config_reg) - .insert(config_mask(p_pin_select.pin), reset_pin_config) - .to(); -} - -void release_jtag_pins() -{ - // Ensure that AFIO is powered on before attempting to access it - if (not is_on(peripheral::afio)) { - power_on(peripheral::afio); - } - // Set the JTAG Release code - bit_modify(alternative_function_io->mapr) - .insert()>(0b010U); -} - -void activate_mco_pa8(mco_source p_source) -{ - configure_pin({ .port = 'A', .pin = 8 }, push_pull_alternative_output); - bit_modify(rcc->cfgr).insert(value(p_source)); -} - -void reset_mco_pa8() -{ - reset_pin({ .port = 'A', .pin = 8 }); -} - -void remap_pins(can_pins p_pin_select) -{ - constexpr auto can_pin_remap = bit_mask::from<14, 13>(); - bit_modify(alternative_function_io->mapr) - .insert(value(p_pin_select)); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/pin.hpp b/src/stm32f1/pin.hpp deleted file mode 100644 index 17c8510..0000000 --- a/src/stm32f1/pin.hpp +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include -#include -#include -#include -#include - -namespace hal::stm32f1 { - -struct alternative_function_io_t -{ - u32 volatile evcr; - u32 volatile mapr; - std::array exticr; - u32 reserved0; - u32 volatile mapr2; -}; - -/** - * @brief GPIO register map - * - */ -struct gpio_t -{ - u32 volatile crl; - u32 volatile crh; - u32 volatile idr; - u32 volatile odr; - u32 volatile bsrr; - u32 volatile brr; - u32 volatile lckr; -}; - -/** - * @brief Map the CONFIG flags for each pin use case - * - */ -struct pin_config_t -{ - /// Configuration bit 1 - u8 CNF1; - /// Configuration bit 0 - u8 CNF0; - /// Mode bits - u8 MODE; - /// Output data register - u8 PxODR; -}; - -static constexpr pin_config_t push_pull_gpio_output = { - .CNF1 = 0, - .CNF0 = 0, - .MODE = 0b11, // Default to high speed 50 MHz - .PxODR = 0b0, // Default to 0 LOW Voltage -}; - -static constexpr pin_config_t open_drain_gpio_output = { - .CNF1 = 0, - .CNF0 = 1, - .MODE = 0b11, // Default to high speed 50 MHz - .PxODR = 0b0, // Default to 0 LOW Voltage -}; - -static constexpr pin_config_t push_pull_alternative_output = { - .CNF1 = 1, - .CNF0 = 0, - .MODE = 0b11, // Default to high speed 50 MHz - .PxODR = 0b0, // Default to 0 LOW Voltage -}; - -static constexpr pin_config_t open_drain_alternative_output = { - .CNF1 = 1, - .CNF0 = 1, - .MODE = 0b11, // Default to high speed 50 MHz - .PxODR = 0b0, // Default to 0 LOW Voltage -}; - -static constexpr pin_config_t input_analog = { - .CNF1 = 0, - .CNF0 = 0, - .MODE = 0b00, - .PxODR = 0b0, // Don't care -}; - -static constexpr pin_config_t input_float = { - .CNF1 = 0, - .CNF0 = 1, - .MODE = 0b00, - .PxODR = 0b0, // Don't care -}; - -static constexpr pin_config_t input_pull_down = { - .CNF1 = 1, - .CNF0 = 0, - .MODE = 0b00, - .PxODR = 0b0, // Pull Down -}; - -static constexpr pin_config_t input_pull_up = { - .CNF1 = 1, - .CNF0 = 0, - .MODE = 0b00, - .PxODR = 0b1, // Pull Up -}; - -constexpr auto cnf1 = bit_mask::from<3>(); -constexpr auto cnf0 = bit_mask::from<2>(); -constexpr auto mode = bit_mask::from<0, 1>(); - -/** - * @brief This is the default state of each pin when the stm32f1 resets - * - * This is used to determine if a pin is in the reset state prior to - * configuration. This is also used to set the pin state when `reset_pin` is - * called. - */ -static constexpr auto reset_pin_config = bit_value(0) - .insert(input_float.CNF1) - .insert(input_float.CNF0) - .insert(input_float.MODE) - .get(); - -/** - * @brief Construct pin manipulation object - * - * @param p_pin_select - the pin to configure - * @param p_config - Configuration to set the pin to - * @throw hal::argument_out_of_domain - pin select is outside of the range of - * available pins. - * @throw hal::device_or_resource_busy - pin has already been configured once - * from its reset state and thus is in use by something else in the code. - */ -void configure_pin(pin_select p_pin_select, pin_config_t p_config); - -/** - * @brief Set pin to the system reset state - * - * This releases control over the pin and allows the pin to be reused by other - * drivers. - * - * @param p_pin_select - the pin to configure - * @param p_config - Configuration to set the pin to - */ -void reset_pin(pin_select p_pin_select); - -/** - * @brief Throws an exception if a pin is not available - * - * Use this function to validate if a pin is available. - * - * @param p_pin_select - the pin to validate - * @throw hal::device_or_resource_busy - if the pin is not available, meaning it - * was not in the reset state. - */ -void throw_if_pin_is_unavailable(pin_select p_pin_select); - -/** - * @brief Remap can pins - * - * @param p_pin_select - pair of pins to select - */ -void remap_pins(can_pins p_pin_select); - -/** - * @brief Returns the gpio register based on the port - * - * @param p_port - port letter, must be from 'A' to 'G' - * @return gpio_t& - gpio register map - */ -gpio_t& gpio_reg(u8 p_port); - -constexpr auto gpio_peripheral_offset(peripheral p_peripheral) -{ - // The numeric value of `peripheral::gpio_a` to ``peripheral::gpio_g` are - // contiguous in numeric value thus we can map letters 'A' to 'G' by doing - // this math here. - return value(p_peripheral) - value(peripheral::gpio_a); -} - -inline auto* alternative_function_io = - reinterpret_cast(0x4001'0000); - -struct pin_remap -{ - static constexpr auto adc2_etrgreg_remap = hal::bit_mask::from<20>(); - static constexpr auto adc2_etrginj_remap = hal::bit_mask::from<19>(); - static constexpr auto adc1_etrgreg_remap = hal::bit_mask::from<18>(); - static constexpr auto adc1_etrginj_remap = hal::bit_mask::from<17>(); - static constexpr auto tim5ch4_iremap = hal::bit_mask::from<16>(); - static constexpr auto pd01_remap = hal::bit_mask::from<15>(); - static constexpr auto can1_remap = hal::bit_mask::from<13, 14>(); - static constexpr auto tim4_rempap = hal::bit_mask::from<12>(); - static constexpr auto tim3_rempap = hal::bit_mask::from<10, 11>(); - static constexpr auto tim2_rempap = hal::bit_mask::from<8, 9>(); - static constexpr auto tim1_rempap = hal::bit_mask::from<6, 7>(); - static constexpr auto usart3_remap = hal::bit_mask::from<4, 5>(); - static constexpr auto usart2_remap = hal::bit_mask::from<3>(); - static constexpr auto usart1_remap = hal::bit_mask::from<2>(); - static constexpr auto i2c1_remap = hal::bit_mask::from<1>(); - static constexpr auto spi1_remap = hal::bit_mask::from<0>(); -}; - -struct pin_remap2 -{ - static constexpr auto ptp_pps_remap = hal::bit_mask::from<30>(); - static constexpr auto tim2itr1_iremap = hal::bit_mask::from<29>(); - static constexpr auto spi3_remap = hal::bit_mask::from<28>(); - static constexpr auto swj_cfg = hal::bit_mask::from<26, 24>(); - static constexpr auto mii_rmii_sel = hal::bit_mask::from<23>(); - static constexpr auto can2_remap = hal::bit_mask::from<22>(); - static constexpr auto eth_remap = hal::bit_mask::from<21>(); - - static constexpr auto tim5ch4_iremap = hal::bit_mask::from<16>(); - static constexpr auto pd01_remap = hal::bit_mask::from<15>(); - static constexpr auto can1_remap = hal::bit_mask::from<14, 13>(); - static constexpr auto tim4_rempap = hal::bit_mask::from<12>(); - static constexpr auto tim3_rempap = hal::bit_mask::from<11, 10>(); - static constexpr auto tim2_rempap = hal::bit_mask::from<9, 8>(); - static constexpr auto tim1_rempap = hal::bit_mask::from<7, 6>(); - static constexpr auto usart3_remap = hal::bit_mask::from<5, 4>(); - static constexpr auto usart2_remap = hal::bit_mask::from<3>(); - static constexpr auto usart1_remap = hal::bit_mask::from<2>(); - static constexpr auto i2c1_remap = hal::bit_mask::from<1>(); - static constexpr auto spi1_remap = hal::bit_mask::from<0>(); -}; -} // namespace hal::stm32f1 diff --git a/src/stm32f1/power.cpp b/src/stm32f1/power.cpp deleted file mode 100644 index 4a50d7a..0000000 --- a/src/stm32f1/power.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -#include "power.hpp" -#include "rcc_reg.hpp" - -namespace hal::stm32f1 { -namespace { -struct rcc_register_info -{ - u32 volatile* reg; - hal::bit_mask mask; -}; - -rcc_register_info get_enable_register_info(peripheral p_peripheral) -{ - auto const peripheral_value = hal::value(p_peripheral); - auto const bus_number = peripheral_value / bus_id_offset; - auto const mask = bit_mask::from(peripheral_value % bus_id_offset); - switch (bus_number) { - case 0: - return { .reg = &rcc->ahbenr, .mask = mask }; - case 1: - return { .reg = &rcc->apb1enr, .mask = mask }; - case 2: - return { .reg = &rcc->apb2enr, .mask = mask }; - default: - hal::safe_throw(hal::argument_out_of_domain(nullptr)); - } -} - -rcc_register_info get_reset_register_info(peripheral p_peripheral) -{ - auto const peripheral_value = hal::value(p_peripheral); - auto const bus_number = peripheral_value / bus_id_offset; - auto const mask = bit_mask::from(peripheral_value % bus_id_offset); - switch (bus_number) { - case 0: - return { .reg = &rcc->ahbrstr, .mask = mask }; - case 1: - return { .reg = &rcc->apb1rstr, .mask = mask }; - case 2: - [[fallthrough]]; - default: - return { .reg = &rcc->apb2rstr, .mask = mask }; - } -} -} // namespace - -void power_on(peripheral p_peripheral) -{ - auto const info = get_enable_register_info(p_peripheral); - - if (hal::bit_extract(info.mask, *info.reg)) { - hal::safe_throw(hal::device_or_resource_busy(nullptr)); - } - - hal::bit_modify(*info.reg).set(info.mask); -} - -void power_off(peripheral p_peripheral) -{ - auto const info = get_enable_register_info(p_peripheral); - hal::bit_modify(*info.reg).clear(info.mask); -} - -bool is_on(peripheral p_peripheral) -{ - auto const info = get_enable_register_info(p_peripheral); - return hal::bit_extract(info.mask, *info.reg); -} - -void reset_peripheral(peripheral p_peripheral) -{ - auto const info = get_reset_register_info(p_peripheral); - hal::bit_modify(*info.reg).set(info.mask); - hal::bit_modify(*info.reg).clear(info.mask); -} - -} // namespace hal::stm32f1 diff --git a/src/stm32f1/power.hpp b/src/stm32f1/power.hpp deleted file mode 100644 index ec34d9c..0000000 --- a/src/stm32f1/power.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -namespace hal::stm32f1 { -/** - * @brief Power on the peripheral - * - * This API also acts as a resource overlap detector. If this API is called - * twice on the same peripheral, it will throw an exception. Only drivers with - * control over the entire peripheral should call this API for their respective - * peripheral. This allows this API to detect when two driver attempt to utilize - * the same resource. - * - * @throws hal::device_or_resource_busy - if the peripheral is already powered - * on, constituting a violation of the 1 peripheral manager per peripheral rule. - * @throws hal::argument_out_of_domain - if the peripheral's value is outside of - * the bounds of the enum class OR if there is on enable register for that - * peripheral. - */ -void power_on(peripheral p_peripheral); - -/** - * @brief Power off peripheral - * - * If the peripheral is already powered off, this does nothing. - */ -void power_off(peripheral p_peripheral); - -/** - * @brief Check if the peripheral is powered on - * - * @return true - peripheral is on - * @return false - peripheral is off - */ -[[nodiscard]] bool is_on(peripheral p_peripheral); - -/** - * @brief Resets the peripheral - * - * This will reset all the peripheral's registers to their reset/default values. - * - */ -void reset_peripheral(peripheral p_peripheral); -} // namespace hal::stm32f1 diff --git a/src/stm32f1/pwm.cpp b/src/stm32f1/pwm.cpp deleted file mode 100644 index 5a72a9d..0000000 --- a/src/stm32f1/pwm.cpp +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pin.hpp" -#include "power.hpp" - -namespace { -struct pin_information -{ - hal::u8 channel; - hal::stm32f1::pin_select pin_select; -}; - -constexpr pin_information determine_pin_info( - hal::stm32f1::timer_pins p_pin, - hal::stm32f1::peripheral p_peripheral) -{ - pin_information pin_info; - switch (p_pin) { - case hal::stm32f1::timer_pins::pa0: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 0; - pin_info.channel = 1; - break; - case hal::stm32f1::timer_pins::pa1: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 1; - pin_info.channel = 2; - break; - case hal::stm32f1::timer_pins::pa2: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 2; - pin_info.channel = - p_peripheral == hal::stm32f1::peripheral::timer9 ? 1 : 3; - break; - case hal::stm32f1::timer_pins::pa3: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 3; - pin_info.channel = - p_peripheral == hal::stm32f1::peripheral::timer9 ? 2 : 4; - break; - case hal::stm32f1::timer_pins::pa6: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 6; - pin_info.channel = 1; - break; - case hal::stm32f1::timer_pins::pa7: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 7; - pin_info.channel = - p_peripheral == hal::stm32f1::peripheral::timer14 ? 1 : 2; - break; - case hal::stm32f1::timer_pins::pb0: - pin_info.pin_select.port = 'B'; - pin_info.pin_select.pin = 0; - pin_info.channel = 3; - break; - case hal::stm32f1::timer_pins::pb1: - pin_info.pin_select.port = 'B'; - pin_info.pin_select.pin = 1; - pin_info.channel = 4; - break; - case hal::stm32f1::timer_pins::pb6: - pin_info.pin_select.port = 'B'; - pin_info.pin_select.pin = 6; - pin_info.channel = 1; - break; - case hal::stm32f1::timer_pins::pb7: - pin_info.pin_select.port = 'B'; - pin_info.pin_select.pin = 7; - pin_info.channel = 2; - break; - case hal::stm32f1::timer_pins::pb8: - pin_info.pin_select.port = 'B'; - pin_info.pin_select.pin = 8; - pin_info.channel = - p_peripheral == hal::stm32f1::peripheral::timer10 ? 1 : 3; - break; - case hal::stm32f1::timer_pins::pb9: - pin_info.pin_select.port = 'B'; - pin_info.pin_select.pin = 9; - pin_info.channel = - p_peripheral == hal::stm32f1::peripheral::timer11 ? 1 : 4; - break; - case hal::stm32f1::timer_pins::pa8: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 8; - pin_info.channel = 1; - break; - case hal::stm32f1::timer_pins::pa9: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 9; - pin_info.channel = 2; - break; - case hal::stm32f1::timer_pins::pa10: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 10; - pin_info.channel = 3; - break; - case hal::stm32f1::timer_pins::pa11: - pin_info.pin_select.port = 'A'; - pin_info.pin_select.pin = 11; - pin_info.channel = 4; - break; - case hal::stm32f1::timer_pins::pc6: - pin_info.pin_select.port = 'C'; - pin_info.pin_select.pin = 6; - pin_info.channel = 1; - break; - case hal::stm32f1::timer_pins::pc7: - pin_info.pin_select.port = 'C'; - pin_info.pin_select.pin = 7; - pin_info.channel = 2; - break; - case hal::stm32f1::timer_pins::pc8: - pin_info.pin_select.port = 'C'; - pin_info.pin_select.pin = 8; - pin_info.channel = 3; - break; - case hal::stm32f1::timer_pins::pc9: - pin_info.pin_select.port = 'C'; - pin_info.pin_select.pin = 9; - pin_info.channel = 4; - break; - case hal::stm32f1::timer_pins::pb14: - pin_info.pin_select.port = 'B'; - pin_info.pin_select.pin = 14; - pin_info.channel = 1; - break; - case hal::stm32f1::timer_pins::pb15: - pin_info.pin_select.port = 'B'; - pin_info.pin_select.pin = 15; - pin_info.channel = 2; - break; - default: - std::unreachable(); - } - return pin_info; -} -} // namespace - -namespace hal::stm32f1 { - -pwm_group_frequency::pwm_group_frequency(void* p_reg, - timer_manager_data* p_manager_data_ptr) - : m_pwm_frequency(unsafe{}, p_reg) - , m_manager_data_ptr(p_manager_data_ptr) -{ - m_manager_data_ptr->m_usage = timer_manager_data::usage::pwm_generator; - m_manager_data_ptr->m_resource_count++; -} - -pwm_group_frequency::pwm_group_frequency(pwm_group_frequency&& p_other) noexcept - : m_pwm_frequency(std::move(p_other.m_pwm_frequency)) -{ - m_manager_data_ptr = std::exchange(p_other.m_manager_data_ptr, nullptr); -} - -pwm_group_frequency& pwm_group_frequency::operator=( - pwm_group_frequency&& p_other) noexcept -{ - if (this == &p_other) { - return *this; - } - - m_pwm_frequency = std::move(p_other.m_pwm_frequency); - m_manager_data_ptr = std::exchange(p_other.m_manager_data_ptr, nullptr); - - return *this; -} - -pwm_group_frequency::~pwm_group_frequency() -{ - if (m_manager_data_ptr == nullptr) { - return; - } - - m_manager_data_ptr->m_resource_count--; - - if (m_manager_data_ptr->m_resource_count.load() == 0) { - m_manager_data_ptr->m_usage = timer_manager_data::usage::uninitialized; - reset_peripheral(m_manager_data_ptr->m_id); - } -} - -void pwm_group_frequency::driver_frequency(u32 p_frequency) -{ - return m_pwm_frequency.set_group_frequency({ - .pwm_frequency = p_frequency, - .timer_clock_frequency = - static_cast(stm32f1::frequency(m_manager_data_ptr->m_id)), - }); -} - -pwm16_channel::pwm16_channel(void* p_reg, - timer_manager_data* p_manager_data_ptr, - bool p_is_advanced, - timer_pins p_pin) - : m_pwm(unsafe{}) - , m_pin(p_pin) - , m_manager_data_ptr(p_manager_data_ptr) -{ - pin_information pin_info = - determine_pin_info(m_pin, m_manager_data_ptr->m_id); - configure_pin(pin_info.pin_select, push_pull_alternative_output); - - // a generic pwm class requires a pin and a channel in order to use the right - // registers, therefore we pass in the channel as an argument in the generic - // pwm class's constructor - m_pwm.initialize(unsafe{}, - p_reg, - { - .channel = pin_info.channel, - .is_advanced = p_is_advanced, - }); - - m_manager_data_ptr->m_usage = timer_manager_data::usage::pwm_generator; - m_manager_data_ptr->m_resource_count++; -} -pwm16_channel::pwm16_channel(pwm16_channel&& p_other) noexcept - : m_pwm(std::move(p_other.m_pwm)) -{ - m_pin = p_other.m_pin; - m_manager_data_ptr = std::exchange(p_other.m_manager_data_ptr, nullptr); -} - -pwm16_channel& pwm16_channel::operator=(pwm16_channel&& p_other) noexcept -{ - if (this == &p_other) { - return *this; - } - m_pwm = std::move(p_other.m_pwm); - m_pin = p_other.m_pin; - m_manager_data_ptr = std::exchange(p_other.m_manager_data_ptr, nullptr); - return *this; -} -pwm16_channel::~pwm16_channel() -{ - if (m_manager_data_ptr == nullptr) { - return; - } - - pin_information pin_info = - determine_pin_info(m_pin, m_manager_data_ptr->m_id); - reset_pin(pin_info.pin_select); - - m_manager_data_ptr->m_resource_count--; - - if (m_manager_data_ptr->m_resource_count.load() == 0) { - m_manager_data_ptr->m_usage = timer_manager_data::usage::uninitialized; - reset_peripheral(m_manager_data_ptr->m_id); - } -} - -u32 pwm16_channel::driver_frequency() -{ - return m_pwm.frequency( - static_cast(stm32f1::frequency(m_manager_data_ptr->m_id))); -} - -void pwm16_channel::driver_duty_cycle(u16 p_duty_cycle) -{ - m_pwm.duty_cycle(p_duty_cycle); -} - -pwm::pwm(void* p_reg, - timer_manager_data* p_manager_data_ptr, - bool p_is_advanced, - stm32f1::timer_pins p_pin) - : m_pwm(unsafe{}) - , m_pwm_frequency(unsafe{}, p_reg) - , m_pin(p_pin) - , m_manager_data_ptr(p_manager_data_ptr) -{ - pin_information pin_info = - determine_pin_info(m_pin, m_manager_data_ptr->m_id); - configure_pin(pin_info.pin_select, push_pull_alternative_output); - - // a generic pwm class requires a pin and a channel in order to use the right - // registers, therefore we pass in the channel as an argument in the generic - // pwm class's constructor - m_pwm.initialize(unsafe{}, - p_reg, - { - .channel = pin_info.channel, - .is_advanced = p_is_advanced, - }); - - m_manager_data_ptr->m_usage = timer_manager_data::usage::old_pwm; - m_manager_data_ptr->m_resource_count++; -} - -pwm::pwm(pwm&& p_other) noexcept - : m_pwm(std::move(p_other.m_pwm)) - , m_pwm_frequency(std::move(p_other.m_pwm_frequency)) - , m_pin(p_other.m_pin) - , m_manager_data_ptr(p_other.m_manager_data_ptr) -{ - m_manager_data_ptr = std::exchange(p_other.m_manager_data_ptr, nullptr); -} - -pwm& pwm::operator=(pwm&& p_other) noexcept -{ - if (this == &p_other) { - return *this; - } - - m_pwm = std::move(p_other.m_pwm); - m_pwm_frequency = std::move(p_other.m_pwm_frequency); - m_pin = p_other.m_pin; - m_manager_data_ptr = std::exchange(p_other.m_manager_data_ptr, nullptr); - - return *this; -} - -pwm::~pwm() -{ - if (m_manager_data_ptr == nullptr) { - return; - } - pin_information pin_info = - determine_pin_info(m_pin, m_manager_data_ptr->m_id); - reset_pin(pin_info.pin_select); - - m_manager_data_ptr->m_resource_count--; - - if (m_manager_data_ptr->m_resource_count.load() == 0) { - m_manager_data_ptr->m_usage = timer_manager_data::usage::uninitialized; - reset_peripheral(m_manager_data_ptr->m_id); - } -} - -void pwm::driver_frequency(hertz p_frequency) -{ - m_pwm_frequency.set_group_frequency({ - .pwm_frequency = static_cast(p_frequency), - .timer_clock_frequency = - static_cast(stm32f1::frequency(m_manager_data_ptr->m_id)), - }); -} - -void pwm::driver_duty_cycle(float p_duty_cycle) -{ - m_pwm.duty_cycle(p_duty_cycle); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/quadrature_encoder.cpp b/src/stm32f1/quadrature_encoder.cpp deleted file mode 100644 index d87d185..0000000 --- a/src/stm32f1/quadrature_encoder.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include -#include -#include - -#include "power.hpp" -#include "quadrature_encoder.hpp" -#include "stm32f1/pin.hpp" - -namespace hal::stm32f1 { - -int get_channel_from_pin(hal::stm32f1::timer_pins p_pin, - hal::stm32f1::peripheral p_select) -{ - // a generic pwm class requires a pin and a channel in order to use the right - // registers, therefore we pass in the channel as an argument in the generic - // pwm class's constructor - u8 channel = 0; - switch (p_pin) { - case timer_pins::pa0: - configure_pin({ .port = 'A', .pin = 0 }, input_float); - channel = 1; - break; - case timer_pins::pa1: - configure_pin({ .port = 'A', .pin = 1 }, input_float); - channel = 2; - break; - case timer_pins::pa2: - configure_pin({ .port = 'A', .pin = 2 }, input_float); - channel = p_select == peripheral::timer9 ? 1 : 3; - break; - case timer_pins::pa3: - configure_pin({ .port = 'A', .pin = 3 }, input_float); - channel = p_select == peripheral::timer9 ? 2 : 4; - break; - case timer_pins::pa6: - configure_pin({ .port = 'A', .pin = 6 }, input_float); - channel = 1; - break; - case timer_pins::pa7: - configure_pin({ .port = 'A', .pin = 7 }, input_float); - channel = p_select == peripheral::timer14 ? 1 : 2; - break; - case timer_pins::pb0: - configure_pin({ .port = 'B', .pin = 0 }, input_float); - channel = 3; - break; - case timer_pins::pb1: - configure_pin({ .port = 'B', .pin = 1 }, input_float); - channel = 4; - break; - case timer_pins::pb6: - configure_pin({ .port = 'B', .pin = 6 }, input_float); - channel = 1; - break; - case timer_pins::pb7: - configure_pin({ .port = 'B', .pin = 7 }, input_float); - channel = 2; - break; - case timer_pins::pb8: - configure_pin({ .port = 'B', .pin = 8 }, input_float); - channel = p_select == peripheral::timer10 ? 1 : 3; - break; - case timer_pins::pb9: - configure_pin({ .port = 'B', .pin = 9 }, input_float); - channel = p_select == peripheral::timer11 ? 1 : 4; - break; - case timer_pins::pa8: - configure_pin({ .port = 'A', .pin = 8 }, input_float); - channel = 1; - break; - case timer_pins::pa9: - configure_pin({ .port = 'A', .pin = 9 }, input_float); - channel = 2; - break; - case timer_pins::pa10: - configure_pin({ .port = 'A', .pin = 10 }, input_float); - channel = 3; - break; - case timer_pins::pa11: - configure_pin({ .port = 'A', .pin = 11 }, input_float); - channel = 4; - break; - case timer_pins::pc6: - configure_pin({ .port = 'C', .pin = 6 }, input_float); - channel = 1; - break; - case timer_pins::pc7: - configure_pin({ .port = 'C', .pin = 7 }, input_float); - channel = 2; - break; - case timer_pins::pc8: - configure_pin({ .port = 'C', .pin = 8 }, input_float); - channel = 3; - break; - case timer_pins::pc9: - configure_pin({ .port = 'C', .pin = 9 }, input_float); - channel = 4; - break; - case timer_pins::pb14: - configure_pin({ .port = 'B', .pin = 14 }, input_float); - channel = 1; - break; - case timer_pins::pb15: - configure_pin({ .port = 'B', .pin = 15 }, input_float); - channel = 2; - break; - default: - std::unreachable(); - } - return channel; -} -quadrature_encoder::quadrature_encoder(hal::stm32f1::timer_pins p_pin1, - hal::stm32f1::timer_pins p_pin2, - hal::stm32f1::peripheral p_select, - void* p_reg, - timer_manager_data* p_manager_data_ptr, - u32 p_pulses_per_rotation) - : m_encoder(hal::unsafe{}) - , m_manager_data_ptr(p_manager_data_ptr) -{ - u8 const channel_a = get_channel_from_pin(p_pin1, p_select); - u8 const channel_b = get_channel_from_pin(p_pin2, p_select); - if (channel_a >= 3 || channel_b >= 3) { - // only channels 1 and 2 are allowed for quadrature encoder mode. - hal::safe_throw(hal::operation_not_permitted(this)); - } - p_manager_data_ptr->m_usage = timer_manager_data::usage::quadrature_encoder; - m_encoder.initialize(hal::unsafe{}, - { .channel_a = channel_a, .channel_b = channel_b }, - p_reg, - p_pulses_per_rotation); -} - -quadrature_encoder::quadrature_encoder(quadrature_encoder&& p_other) noexcept - : m_encoder(std::move(p_other.m_encoder)) -{ - m_manager_data_ptr = std::exchange(p_other.m_manager_data_ptr, nullptr); -} - -quadrature_encoder& quadrature_encoder::operator=( - quadrature_encoder&& p_other) noexcept -{ - if (this == &p_other) { - return *this; - } - m_encoder = std::move(p_other.m_encoder); - m_manager_data_ptr = std::exchange(p_other.m_manager_data_ptr, nullptr); - return *this; -} -quadrature_encoder::read_t quadrature_encoder::driver_read() -{ - return m_encoder.read(); -} - -quadrature_encoder::~quadrature_encoder() -{ - if (m_manager_data_ptr) { - m_manager_data_ptr->m_usage = timer_manager_data::usage::uninitialized; - reset_peripheral(m_manager_data_ptr->m_id); - } -} - -} // namespace hal::stm32f1 diff --git a/src/stm32f1/quadrature_encoder.hpp b/src/stm32f1/quadrature_encoder.hpp deleted file mode 100644 index 8de7cfe..0000000 --- a/src/stm32f1/quadrature_encoder.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace hal::stm32f1 { -/** - * @brief This class implements the `hal::rotation_sensor` interface - * - * It gets an input from a quadrature encoder motor and measures the amount turn - * using 2 channels as input. - * - * Each Quadrature Encoder must use channel 1 and 2 from the same timer. - */ -class quadrature_encoder : public hal::rotation_sensor -{ -public: - quadrature_encoder(hal::stm32f1::timer_pins p_pin1, - hal::stm32f1::timer_pins p_pin2, - hal::stm32f1::peripheral p_select, - void* p_reg, - timer_manager_data* p_manager_data_ptr, - u32 p_pulses_per_rotation); - quadrature_encoder(quadrature_encoder const& p_other) = delete; - quadrature_encoder& operator=(quadrature_encoder const& p_other) = delete; - quadrature_encoder(quadrature_encoder&& p_other) noexcept; - quadrature_encoder& operator=(quadrature_encoder&& p_other) noexcept; - ~quadrature_encoder() override; - -private: - read_t driver_read() override; - hal::stm32_generic::quadrature_encoder m_encoder; - timer_manager_data* m_manager_data_ptr; -}; -} // namespace hal::stm32f1 diff --git a/src/stm32f1/rcc_reg.hpp b/src/stm32f1/rcc_reg.hpp deleted file mode 100644 index 646ce7a..0000000 --- a/src/stm32f1/rcc_reg.hpp +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -namespace hal::stm32f1 { -struct reset_and_clock_control_t -{ - u32 volatile cr; - u32 volatile cfgr; - u32 volatile cir; - u32 volatile apb2rstr; - u32 volatile apb1rstr; - u32 volatile ahbenr; - u32 volatile apb2enr; - u32 volatile apb1enr; - u32 volatile bdcr; - u32 volatile csr; - u32 volatile ahbrstr; - u32 volatile cfgr2; -}; - -constexpr uptr rcc_address = 0x40000000 + 0x20000 + 0x1000; - -/** - * @return reset_and_clock_control_t& - return reset_and_clock_control_t - * register. - */ -// NOLINTNEXTLINE(performance-no-int-to-ptr) -inline auto* rcc = reinterpret_cast(rcc_address); - -/// Bit masks for the CFGR register -struct clock_configuration -{ - /// Controls which clock signal is sent to the MCO pin - static constexpr auto mco = bit_mask::from<24, 26>(); - - /// Sets the USB clock divider - static constexpr auto usb_prescalar = bit_mask::from<22>(); - - /// Sets the PLL multiplier - static constexpr auto pll_mul = bit_mask::from<18, 21>(); - - /// If set to 1, will divide the HSE signal by 2 before sending to PLL - static constexpr auto hse_pre_divider = bit_mask::from<17>(); - - /// Sets which source the PLL will take as input - static constexpr auto pll_source = bit_mask::from<16>(); - - /// Sets the clock divider for the ADC peripherals - static constexpr auto adc_divider = bit_mask::from<14, 15>(); - - /// Sets the divider for peripherals on the APB2 bus - static constexpr auto apb_2_divider = bit_mask::from<11, 13>(); - - /// Sets the divider for peripherals on the APB1 bus - static constexpr auto apb_1_divider = bit_mask::from<8, 10>(); - - /// Sets the divider for peripherals on the AHB bus - static constexpr auto ahb_divider = bit_mask::from<4, 7>(); - - /// Used to check if the system clock has taken the new system clock - /// settings. - static constexpr auto system_clock_status = bit_mask::from<2, 3>(); - - /// Set which clock will be used for the system clock. - static constexpr auto system_clock_select = bit_mask::from<0, 1>(); - - static auto reg() - { - return hal::bit_modify(rcc->cfgr); - } -}; - -/// Bit masks for the CR register -struct clock_control -{ - /// Indicates if the PLL is enabled and ready - static constexpr auto pll_ready = bit_mask::from<25>(); - /// Used to enable the PLL - static constexpr auto pll_enable = bit_mask::from<24>(); - /// Indicates if the external oscillator is ready for use - static constexpr auto external_osc_ready = bit_mask::from<17>(); - /// Used to enable the external oscillator - static constexpr auto external_osc_enable = bit_mask::from<16>(); - - static auto reg() - { - return hal::bit_modify(rcc->cr); - } -}; - -/// Bitmasks for the BDCR register -struct rtc_register -{ - /// Will reset all clock states for the RTC - static constexpr auto backup_domain_reset = bit_mask::from<16>(); - /// Enables the RTC clock - static constexpr auto rtc_enable = bit_mask::from<15>(); - /// Selects the clock source for the RTC - static constexpr auto rtc_source_select = bit_mask::from<8, 9>(); - /// Indicates if the LSE is ready for use - static constexpr auto low_speed_osc_ready = bit_mask::from<1>(); - /// Used to enable the LSE - static constexpr auto low_speed_osc_enable = bit_mask::from<0>(); - - static auto reg() - { - return hal::bit_modify(rcc->bdcr); - } -}; -} // namespace hal::stm32f1 diff --git a/src/stm32f1/spi.cpp b/src/stm32f1/spi.cpp deleted file mode 100644 index 68393d7..0000000 --- a/src/stm32f1/spi.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include - -#include "pin.hpp" -#include "power.hpp" - -namespace hal::stm32f1 { -namespace { -inline void* peripheral_to_address(peripheral p_id) -{ - constexpr std::uintptr_t spi_reg1 = 0x4001'3000; - constexpr std::uintptr_t spi_reg2 = 0x4000'3800; - constexpr std::uintptr_t spi_reg3 = 0x4000'3C00; - - switch (p_id) { - case peripheral::spi1: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(spi_reg1); - case peripheral::spi2: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(spi_reg2); - case peripheral::spi3: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(spi_reg3); - default: - hal::safe_throw(hal::operation_not_supported(nullptr)); - } -} - -inline peripheral bus_number_to_peripheral(std::uint8_t p_bus_number) -{ - switch (p_bus_number) { - case 1: - return peripheral::spi1; - case 2: - return peripheral::spi2; - case 3: - return peripheral::spi3; - default: - hal::safe_throw(hal::operation_not_supported(nullptr)); - } -} -} // namespace - -spi::spi(std::uint8_t p_bus_number, spi::settings const& p_settings) - : m_peripheral_id(bus_number_to_peripheral(p_bus_number)) - , m_spi_driver(hal::unsafe{}, peripheral_to_address(m_peripheral_id)) -{ - // Datasheet: Chapter 4: Pin definition Table 9 - switch (m_peripheral_id) { - case peripheral::spi1: { - hal::bit_modify(alternative_function_io->mapr) - .clear(); - // clock - configure_pin({ .port = 'A', .pin = 5 }, push_pull_alternative_output); - // cipo - configure_pin({ .port = 'A', .pin = 6 }, input_float); - // copi - configure_pin({ .port = 'A', .pin = 7 }, push_pull_alternative_output); - break; - } - case peripheral::spi2: { - // clock - configure_pin({ .port = 'B', .pin = 13 }, push_pull_alternative_output); - // cipo - configure_pin({ .port = 'B', .pin = 14 }, input_float); - // copi - configure_pin({ .port = 'B', .pin = 15 }, push_pull_alternative_output); - break; - } - case peripheral::spi3: { - hal::bit_modify(alternative_function_io->mapr2) - .set(); - // clock - configure_pin({ .port = 'C', .pin = 10 }, push_pull_alternative_output); - // cipo - configure_pin({ .port = 'C', .pin = 11 }, input_float); - // copi - configure_pin({ .port = 'C', .pin = 12 }, push_pull_alternative_output); - break; - } - default: - // "Supported spi busses are 1-5!"; - hal::safe_throw(hal::operation_not_supported(this)); - } - - power_on(m_peripheral_id); - spi::driver_configure(p_settings); -} - -spi::~spi() -{ - power_off(m_peripheral_id); -} - -void spi::driver_configure(settings const& p_settings) -{ - m_spi_driver.configure(p_settings, frequency(m_peripheral_id)); -} - -void spi::driver_transfer(std::span p_data_out, - std::span p_data_in, - hal::byte p_filler) -{ - m_spi_driver.transfer(p_data_out, p_data_in, p_filler); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/timer.cpp b/src/stm32f1/timer.cpp deleted file mode 100644 index 476aca0..0000000 --- a/src/stm32f1/timer.cpp +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../stm32_generic/timer.hpp" -#include "power.hpp" -#include "quadrature_encoder.hpp" - -namespace hal::stm32f1 { - -// Advanced timer -inline void* timer1 = reinterpret_cast(0x4001'2C00); -// General purpose timers 2 - 5 -inline void* timer2 = reinterpret_cast(0x4000'0000); -inline void* timer3 = reinterpret_cast(0x4000'0400); -inline void* timer4 = reinterpret_cast(0x4000'0800); -inline void* timer5 = reinterpret_cast(0x4000'0C00); -// Advanced timer -inline void* timer8 = reinterpret_cast(0x4001'3400); -// General purpose timers 9 - 14 -inline void* timer9 = reinterpret_cast(0x4001'4C00); -inline void* timer10 = reinterpret_cast(0x4001'5000); -inline void* timer11 = reinterpret_cast(0x4001'5400); -inline void* timer12 = reinterpret_cast(0x4000'1800); -inline void* timer13 = reinterpret_cast(0x4000'1C00); -inline void* timer14 = reinterpret_cast(0x4000'2000); - -namespace { -void* peripheral_to_advanced_register(peripheral p_id) -{ - void* reg; - if (p_id == peripheral::timer1) { - reg = timer1; - } else { - reg = timer8; - } - return reg; -} - -void* peripheral_to_general_register(peripheral p_id) -{ - void* reg; - if (p_id == peripheral::timer2) { - reg = timer2; - } else if (p_id == peripheral::timer3) { - reg = timer3; - } else if (p_id == peripheral::timer4) { - reg = timer4; - } else if (p_id == peripheral::timer5) { - reg = timer5; - } else if (p_id == peripheral::timer9) { - reg = timer9; - } else if (p_id == peripheral::timer10) { - reg = timer10; - } else if (p_id == peripheral::timer11) { - reg = timer11; - } else if (p_id == peripheral::timer12) { - reg = timer12; - } else if (p_id == peripheral::timer13) { - reg = timer13; - } else { - reg = timer14; - } - return reg; -} -} // namespace - -timer::timer(void* p_reg, timer_manager_data* p_manager_data_ptr) - : m_timer(unsafe{}) - , m_manager_data_ptr(p_manager_data_ptr) -{ - // Captures the needed stm32f1 series interrupt data to be passed - auto peripheral_interrupt_params = setup_interrupt(); - - // Passes the stm32f1 series data to the generic stm32 timer object - m_timer.initialize(unsafe{}, - p_reg, - &stm32f1::initialize_interrupts, - peripheral_interrupt_params.irq, - peripheral_interrupt_params.handler); - - m_manager_data_ptr->m_usage = timer_manager_data::usage::callback_timer; -} - -timer::~timer() -{ - m_manager_data_ptr->m_usage = timer_manager_data::usage::uninitialized; - reset_peripheral(m_manager_data_ptr->m_id); -} - -bool timer::driver_is_running() -{ - return m_timer.is_running(); -} - -void timer::driver_cancel() -{ - m_timer.cancel(); -} - -void timer::driver_schedule(hal::callback p_callback, - hal::time_duration p_delay) -{ - m_callback = p_callback; - auto const timer_frequency = frequency(m_manager_data_ptr->m_id); - m_timer.schedule(p_delay, static_cast(timer_frequency)); -} - -timer::interrupt_params timer::setup_interrupt() -{ - // Create a lambda to call the interrupt() method - auto isr = [this]() { interrupt(); }; - - // A pointer to save the static_callable isr address to - interrupt_params peripheral_interrupt_params; - - // Determines IRQ and handler to use - switch (m_manager_data_ptr->m_id) { - case peripheral::timer1: - peripheral_interrupt_params.irq = - static_cast(irq::tim1_up); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer2: - peripheral_interrupt_params.irq = static_cast(irq::tim2); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer3: - peripheral_interrupt_params.irq = static_cast(irq::tim3); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer4: - peripheral_interrupt_params.irq = static_cast(irq::tim4); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer5: - peripheral_interrupt_params.irq = static_cast(irq::tim5); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer6: - peripheral_interrupt_params.irq = static_cast(irq::tim6); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer7: - peripheral_interrupt_params.irq = static_cast(irq::tim7); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer8: - peripheral_interrupt_params.irq = - static_cast(irq::tim8_up); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer9: - peripheral_interrupt_params.irq = - static_cast(irq::tim1_brk_tim9); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer10: // uses timer 1's interrupt vector - peripheral_interrupt_params.irq = - static_cast(irq::tim1_up_tim10); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer11: - peripheral_interrupt_params.irq = - static_cast(irq::tim1_trg_com_tim11); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer12: - peripheral_interrupt_params.irq = - static_cast(irq::tim8_brk_tim12); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer13: // uses timer 8's interrupt vector - peripheral_interrupt_params.irq = - static_cast(irq::tim8_up_tim13); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - case peripheral::timer14: - [[fallthrough]]; - default: - peripheral_interrupt_params.irq = - static_cast(irq::tim8_trg_com_tim14); - peripheral_interrupt_params.handler = - static_callable(isr).get_handler(); - break; - } - return peripheral_interrupt_params; -} - -void timer::handle_interrupt() -{ - void* reg = nullptr; - if (m_manager_data_ptr->m_id == peripheral::timer1 || - m_manager_data_ptr->m_id == peripheral::timer8) { - reg = peripheral_to_advanced_register(m_manager_data_ptr->m_id); - } else { - reg = peripheral_to_general_register(m_manager_data_ptr->m_id); - } - - static auto timer_reg = stm32_generic::get_timer_reg(reg); - - static constexpr auto update_interrupt_flag = hal::bit_mask::from(0); - bit_modify(timer_reg->status_register).clear(update_interrupt_flag); -} - -void timer::interrupt() -{ - if (m_callback) { - (*m_callback)(); - timer::handle_interrupt(); - } -} - -advanced_timer_manager::advanced_timer_manager(peripheral p_id) - : m_manager_data(p_id) -{ - power_on(m_manager_data.m_id); -} - -general_purpose_timer_manager::general_purpose_timer_manager(peripheral p_id) - : m_manager_data(p_id) -{ - power_on(m_manager_data.m_id); -} - -advanced_timer_manager::~advanced_timer_manager() -{ - power_off(m_manager_data.m_id); -} - -general_purpose_timer_manager::~general_purpose_timer_manager() -{ - power_off(m_manager_data.m_id); -} - -hal::stm32f1::timer advanced_timer_manager::acquire_timer() -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized) { - safe_throw(hal::device_or_resource_busy(this)); - } - - return { peripheral_to_advanced_register(m_manager_data.m_id), - &m_manager_data }; -} - -hal::stm32f1::timer general_purpose_timer_manager::acquire_timer() -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized) { - safe_throw(hal::device_or_resource_busy(this)); - } - - return { peripheral_to_general_register(m_manager_data.m_id), - &m_manager_data }; -} - -hal::stm32f1::pwm_group_frequency -advanced_timer_manager::acquire_pwm_group_frequency() -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized && - m_manager_data.current_usage() != - timer_manager_data::usage::pwm_generator) { - safe_throw(hal::device_or_resource_busy(this)); - } - - return { peripheral_to_advanced_register(m_manager_data.m_id), - &m_manager_data }; -} - -hal::stm32f1::pwm_group_frequency -general_purpose_timer_manager::acquire_pwm_group_frequency() -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized && - m_manager_data.current_usage() != - timer_manager_data::usage::pwm_generator) { - safe_throw(hal::device_or_resource_busy(this)); - } - - return { peripheral_to_general_register(m_manager_data.m_id), - &m_manager_data }; -} - -hal::stm32f1::pwm16_channel advanced_timer_manager::acquire_pwm16_channel( - timer_pins p_pin) -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized && - m_manager_data.current_usage() != - timer_manager_data::usage::pwm_generator) { - safe_throw(hal::device_or_resource_busy(this)); - } - - return { peripheral_to_advanced_register(m_manager_data.m_id), - &m_manager_data, - true, - p_pin }; -} - -hal::stm32f1::pwm16_channel -general_purpose_timer_manager::acquire_pwm16_channel(timer_pins p_pin) -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized && - m_manager_data.current_usage() != - timer_manager_data::usage::pwm_generator) { - safe_throw(hal::device_or_resource_busy(this)); - } - - return { peripheral_to_general_register(m_manager_data.m_id), - &m_manager_data, - false, - p_pin }; -} - -hal::stm32f1::pwm advanced_timer_manager::acquire_pwm(timer_pins p_pin) -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized && - m_manager_data.current_usage() != timer_manager_data::usage::old_pwm) { - safe_throw(hal::device_or_resource_busy(this)); - } - - return { peripheral_to_advanced_register(m_manager_data.m_id), - &m_manager_data, - true, - p_pin }; -} - -hal::stm32f1::pwm general_purpose_timer_manager::acquire_pwm(timer_pins p_pin) -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized && - m_manager_data.current_usage() != timer_manager_data::usage::old_pwm) { - safe_throw(hal::device_or_resource_busy(this)); - } - - return { peripheral_to_general_register(m_manager_data.m_id), - &m_manager_data, - false, - p_pin }; -} - -hal::v5::strong_ptr -general_purpose_timer_manager::acquire_quadrature_encoder( - std::pmr::polymorphic_allocator<> p_allocator, - timer_pins p_pin1, - timer_pins p_pin2, - u32 p_pulses_per_rotation) -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized) { - safe_throw(hal::device_or_resource_busy(this)); - } - return hal::v5::make_strong_ptr( - p_allocator, - p_pin1, - p_pin2, - m_manager_data.m_id, - peripheral_to_general_register(m_manager_data.m_id), - &m_manager_data, - p_pulses_per_rotation); -} - -hal::v5::strong_ptr -advanced_timer_manager::acquire_quadrature_encoder( - std::pmr::polymorphic_allocator<> p_allocator, - timer_pins p_pin1, - timer_pins p_pin2, - u32 p_pulses_per_rotation) -{ - if (m_manager_data.current_usage() != - timer_manager_data::usage::uninitialized) { - safe_throw(hal::device_or_resource_busy(this)); - } - return hal::v5::make_strong_ptr( - p_allocator, - p_pin1, - p_pin2, - m_manager_data.m_id, - peripheral_to_general_register(m_manager_data.m_id), - &m_manager_data, - p_pulses_per_rotation); -} - -// Tell the compiler which instances to generate -template class advanced_timer; -template class advanced_timer; -template class general_purpose_timer; -template class general_purpose_timer; -template class general_purpose_timer; -template class general_purpose_timer; -template class general_purpose_timer; -template class general_purpose_timer; -template class general_purpose_timer; -template class general_purpose_timer; -template class general_purpose_timer; -template class general_purpose_timer; -} // namespace hal::stm32f1 diff --git a/src/stm32f1/uart.cpp b/src/stm32f1/uart.cpp deleted file mode 100644 index 40bc20c..0000000 --- a/src/stm32f1/uart.cpp +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include -#include - -#include "dma.hpp" -#include "pin.hpp" -#include "power.hpp" -#include "usart_reg.hpp" - -namespace hal::stm32f1 { -namespace { -void configure_baud_rate(usart_t& p_usart, - peripheral p_peripheral, - serial::settings const& p_settings) -{ - auto const clock_frequency = frequency(p_peripheral); - float usart_divider = clock_frequency / (16.0f * p_settings.baud_rate); - - // Truncate off the decimal values - auto mantissa = static_cast(usart_divider); - // Subtract the whole number to leave just the decimal - auto fraction = usart_divider - static_cast(mantissa); - auto fractional_int = static_cast(std::roundf(fraction * 16)); - - if (fractional_int >= 16) { - mantissa = static_cast(mantissa + 1U); - fractional_int = 0; - } - - p_usart.baud_rate = hal::bit_value() - .insert(mantissa) - .insert(fractional_int) - .to(); -} - -void configure_format(usart_t& p_usart, serial::settings const& p_settings) -{ - constexpr auto parity_selection = bit_mask::from<9>(); - constexpr auto parity_control = bit_mask::from<10>(); - constexpr auto word_length = bit_mask::from<12>(); - constexpr auto stop = bit_mask::from<12, 13>(); - - bool parity_enable = (p_settings.parity != serial::settings::parity::none); - bool parity = (p_settings.parity == serial::settings::parity::odd); - bool double_stop = (p_settings.stop == serial::settings::stop_bits::two); - std::uint16_t stop_value = (double_stop) ? 0b10U : 0b00U; - - // Parity codes are: 0 for Even and 1 for Odd, thus the expression above - // sets the bool to TRUE when odd and zero when something else. This value - // is ignored if the parity is NONE since parity_enable will be zero. - - bit_modify(p_usart.control1) - .insert(parity_enable) - .insert(parity) - .insert(0U); - - bit_modify(p_usart.control2).insert(stop_value); -} -} // namespace - -uart::uart(hal::runtime, - std::uint8_t p_port, - std::span p_buffer, - serial::settings const& p_settings) - : uart(p_port, p_buffer, p_settings) -{ -} - -uart::uart(std::uint8_t p_port, - std::span p_buffer, - serial::settings const& p_settings) - : m_uart(nullptr) - , m_receive_buffer(p_buffer) - , m_read_index(0) - , m_dma(0) - , m_id{} -{ - if (p_buffer.size() > max_dma_length) { - hal::safe_throw(hal::operation_not_supported(this)); - } - - switch (p_port) { - case 1: - m_port_tx = 'A'; - m_pin_tx = 9; - m_port_rx = 'A'; - m_pin_rx = 10; - m_id = peripheral::usart1; - m_dma = 5; - break; - case 2: - m_port_tx = 'A'; - m_pin_tx = 2; - m_port_rx = 'A'; - m_pin_rx = 3; - m_dma = 6; - m_id = peripheral::usart2; - break; - case 3: - m_port_tx = 'B'; - m_pin_tx = 10; - m_port_rx = 'B'; - m_pin_rx = 11; - m_dma = 3; - m_id = peripheral::usart3; - break; - default: - hal::safe_throw(hal::operation_not_supported(this)); - } - - m_uart = reinterpret_cast(peripheral_to_register(m_id)); // NOLINT - - // Power on the usart/uart id - power_on(m_id); - - // Power on dma1 which has the usart channels - // TODO(): DMA1 is shared across multiple peripherals - if (not is_on(peripheral::dma1)) { - power_on(peripheral::dma1); - } - - auto& uart_reg = *to_usart(m_uart); - - // Setup RX DMA channel - auto const data_address = reinterpret_cast(&uart_reg.data); - auto const queue_address = reinterpret_cast(p_buffer.data()); - auto const data_address_int = static_cast(data_address); - auto const queue_address_int = static_cast(queue_address); - - dma::dma1->channel[m_dma - 1].transfer_amount = p_buffer.size(); - dma::dma1->channel[m_dma - 1].peripheral_address = data_address_int; - dma::dma1->channel[m_dma - 1].memory_address = queue_address_int; - dma::dma1->channel[m_dma - 1].configuration = uart_dma_settings1; - - // Setup UART Control Settings 1 - uart_reg.control1 = control_reg::control_settings1; - - // NOTE: We leave control settings 2 alone as it is for features beyond - // basic UART such as USART clock, USART port network (LIN), and other - // things. - - // Setup UART Control Settings 3 - uart_reg.control3 = control_reg::control_settings3; - - uart::driver_configure(p_settings); - - configure_pin({ .port = m_port_tx, .pin = m_pin_tx }, - push_pull_alternative_output); - configure_pin({ .port = m_port_rx, .pin = m_pin_rx }, input_pull_up); -} - -uart::~uart() -{ - reset_pin({ .port = m_port_tx, .pin = m_pin_tx }); - reset_pin({ .port = m_port_rx, .pin = m_pin_rx }); -} - -u32 uart::dma_cursor_position() -{ - u32 receive_amount = dma::dma1->channel[m_dma - 1].transfer_amount; - u32 write_position = m_receive_buffer.size() - receive_amount; - return write_position % m_receive_buffer.size(); -} - -void uart::driver_configure(serial::settings const& p_settings) -{ - auto& uart_reg = *to_usart(m_uart); - configure_baud_rate(uart_reg, m_id, p_settings); - configure_format(uart_reg, p_settings); -} - -serial::write_t uart::driver_write(std::span p_data) -{ - auto& uart_reg = *to_usart(m_uart); - - for (auto const& byte : p_data) { - while (not bit_extract(uart_reg.status)) { - continue; - } - // Load the next byte into the data register - uart_reg.data = byte; - } - - return { - .data = p_data, - }; -} - -serial::read_t uart::driver_read(std::span p_data) -{ - size_t count = 0; - - for (auto& byte : p_data) { - if (m_read_index == dma_cursor_position()) { - break; - } - byte = m_receive_buffer[m_read_index++]; - m_read_index = m_read_index % m_receive_buffer.size(); - count++; - } - - return { - .data = p_data.first(count), - .available = 1, - .capacity = m_receive_buffer.size(), - }; -} - -void uart::driver_flush() -{ - m_read_index = dma_cursor_position(); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/usart.cpp b/src/stm32f1/usart.cpp deleted file mode 100644 index d944100..0000000 --- a/src/stm32f1/usart.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "pin.hpp" -#include "power.hpp" -#include "usart_reg.hpp" - -namespace hal::stm32f1 { -namespace { -inline void configure_baud_rate(usart_t& p_usart, - u32 p_frequency, - serial::settings const& p_settings) -{ - float const usart_divider = - static_cast(p_frequency) / (16.0f * p_settings.baud_rate); - - // Truncate off the decimal values - auto mantissa = static_cast(usart_divider); - // Subtract the whole number to leave just the decimal - auto fraction = usart_divider - static_cast(mantissa); - auto fractional_int = static_cast(std::roundf(fraction * 16)); - - if (fractional_int >= 16) { - mantissa = static_cast(mantissa + 1U); - fractional_int = 0; - } - - p_usart.baud_rate = hal::bit_value() - .insert(mantissa) - .insert(fractional_int) - .to(); -} - -inline void configure_format(usart_t& p_usart, - serial::settings const& p_settings) -{ - constexpr auto parity_selection = bit_mask::from<9>(); - constexpr auto parity_control = bit_mask::from<10>(); - constexpr auto word_length = bit_mask::from<12>(); - constexpr auto stop = bit_mask::from<12, 13>(); - - bool parity_enable = (p_settings.parity != serial::settings::parity::none); - bool parity = (p_settings.parity == serial::settings::parity::odd); - bool double_stop = (p_settings.stop == serial::settings::stop_bits::two); - std::uint16_t stop_value = (double_stop) ? 0b10U : 0b00U; - - // Parity codes are: 0 for Even and 1 for Odd, thus the expression above - // sets the bool to TRUE when odd and zero when something else. This value - // is ignored if the parity is NONE since parity_enable will be zero. - - bit_modify(p_usart.control1) - .insert(parity_enable) - .insert(parity) - .insert(0U); - - bit_modify(p_usart.control2).insert(stop_value); -} -} // namespace - -usart_manager::usart_manager(peripheral p_select) - : m_reg(peripheral_to_register(p_select)) - , m_id(p_select) -{ - power_on(m_id); -} - -usart_manager::~usart_manager() -{ - power_off(m_id); -} - -usart_manager::serial usart_manager::acquire_serial( - std::span p_buffer, - hal::serial::settings const& p_settings) -{ - return { *this, p_buffer, p_settings }; -} - -usart_manager::serial::serial(usart_manager& p_usart_manager, - std::span p_buffer, - hal::serial::settings const& p_settings) - : m_usart_manager(&p_usart_manager) - , m_buffer(p_buffer) - , m_dma_channel(0) -{ - - if (p_buffer.size() > max_dma_length) { - hal::safe_throw(hal::operation_not_supported(this)); - } - - switch (m_usart_manager->m_id) { - case peripheral::usart1: - m_tx = { .port = 'A', .pin = 9 }; - m_rx = { .port = 'A', .pin = 10 }; - m_dma_channel = 5; - break; - case peripheral::usart2: - m_tx = { .port = 'A', .pin = 2 }; - m_rx = { .port = 'A', .pin = 3 }; - m_dma_channel = 6; - break; - case peripheral::usart3: - m_tx = { .port = 'B', .pin = 10 }; - m_rx = { .port = 'B', .pin = 11 }; - m_dma_channel = 3; - break; - default: - hal::safe_throw(hal::operation_not_supported(this)); - } - - // Power on dma1 which has the usart channels - // TODO(): DMA1 is shared across multiple peripherals - if (not is_on(peripheral::dma1)) { - power_on(peripheral::dma1); - } - - auto& uart_reg = *to_usart(m_usart_manager->m_reg); - - // Setup RX DMA channel - auto const data_address = reinterpret_cast(&uart_reg.data); - auto const queue_address = reinterpret_cast(p_buffer.data()); - auto const data_address_int = static_cast(data_address); - auto const queue_address_int = static_cast(queue_address); - - dma::dma1->channel[m_dma_channel - 1].transfer_amount = p_buffer.size(); - dma::dma1->channel[m_dma_channel - 1].peripheral_address = data_address_int; - dma::dma1->channel[m_dma_channel - 1].memory_address = queue_address_int; - dma::dma1->channel[m_dma_channel - 1].configuration = uart_dma_settings1; - - // Setup UART Control Settings 1 - uart_reg.control1 = control_reg::control_settings1; - - // NOTE: We leave control settings 2 alone as it is for features beyond - // basic UART such as USART clock, USART port network (LIN), and other - // things. - - // Setup UART Control Settings 3 - uart_reg.control3 = control_reg::control_settings3; - - serial::driver_configure(p_settings); - - configure_pin(m_tx, push_pull_alternative_output); - configure_pin(m_rx, input_pull_up); -} - -void usart_manager::serial::driver_configure( - hal::serial::settings const& p_settings) -{ - auto& uart_reg = *to_usart(m_usart_manager->m_reg); - auto const uart_freq = static_cast(frequency(m_usart_manager->m_id)); - configure_baud_rate(uart_reg, uart_freq, p_settings); - configure_format(uart_reg, p_settings); -} - -void usart_manager::serial::driver_write(std::span p_data) -{ - auto& uart_reg = *to_usart(m_usart_manager->m_reg); - - for (auto const& byte : p_data) { - while (not bit_extract(uart_reg.status)) { - continue; - } - // Load the next byte into the data register - uart_reg.data = byte; - } -} - -std::span usart_manager::serial::driver_receive_buffer() -{ - return m_buffer; -} - -std::size_t usart_manager::serial::driver_cursor() -{ - return m_buffer.size() - - dma::dma1->channel[m_dma_channel - 1].transfer_amount; -} - -usart_manager::serial::~serial() -{ - reset_pin(m_tx); - reset_pin(m_rx); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/usart_reg.hpp b/src/stm32f1/usart_reg.hpp deleted file mode 100644 index 0f1782e..0000000 --- a/src/stm32f1/usart_reg.hpp +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "dma.hpp" - -namespace hal::stm32f1 { - -/// Namespace for the status registers (SR) bit masks -struct status_reg // NOLINT -{ - /// Indicates if the transmit data register is empty and can be loaded with - /// another byte. - static constexpr auto transit_empty = hal::bit_mask::from<7>(); -}; - -/// Namespace for the control registers (CR1, CR3) bit masks and predefined -/// settings constants. -struct control_reg // NOLINT -{ - /// When this bit is cleared the USART prescalers and outputs are stopped - /// and the end of the current byte transfer in order to reduce power - /// consumption. (CR1) - static constexpr auto usart_enable = hal::bit_mask::from<13>(); - - /// Enables DMA receiver (CR3) - static constexpr auto dma_receiver_enable = hal::bit_mask::from<6>(); - - /// This bit enables the transmitter. (CR1) - static constexpr auto transmitter_enable = hal::bit_mask::from<3>(); - - /// This bit enables the receiver. (CR1) - static constexpr auto receive_enable = hal::bit_mask::from<2>(); - - /// Enable USART + Enable Receive + Enable Transmitter - static constexpr auto control_settings1 = - hal::bit_value(0UL) - .set() - .set() - .set() - .to(); - - /// Make sure that DMA is enabled for receive only - static constexpr auto control_settings3 = - hal::bit_value(0UL) - .set() - .to(); -}; - -/// Namespace for the baud rate (BRR) registers bit masks -struct baud_rate_reg // NOLINT -{ - /// Mantissa of USART DIV - static constexpr auto mantissa = hal::bit_mask::from<4, 15>(); - - /// Fraction of USART DIV - static constexpr auto fraction = hal::bit_mask::from<0, 3>(); -}; - -struct usart_t -{ - u32 volatile status; - u32 volatile data; - u32 volatile baud_rate; - u32 volatile control1; - u32 volatile control2; - u32 volatile control3; - u32 volatile guard_time_and_prescale; -}; - -inline hal::uptr peripheral_to_register(peripheral p_select) -{ - // See Chapter 3.3 "Memory" page 50 in RM0008 for these magic numbers - switch (p_select) { - case peripheral::usart1: - return 0x4001'3800; - case peripheral::usart2: - return 0x4000'4400; - case peripheral::usart3: - return 0x4000'4800; - case peripheral::uart4: - return 0x4000'4C00; - case peripheral::uart5: - return 0x4000'5000; - default: - hal::safe_throw(hal::argument_out_of_domain(nullptr)); - } -} - -static constexpr auto uart_dma_settings1 = - hal::bit_value() - .clear() - .clear() - .clear() - .clear() // Read from peripheral - .set() - .clear() - .set() - .clear() - .set() - .insert() // size = 8 bits - .insert() // size = 8 bits - .insert() // Low Medium [High] Very_High - .to(); - -inline usart_t* to_usart(void* p_uart) -{ - return reinterpret_cast(p_uart); -} -inline usart_t* to_usart(uptr p_uart) -{ - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(p_uart); -} -} // namespace hal::stm32f1 diff --git a/src/stm32f1/usb.cpp b/src/stm32f1/usb.cpp deleted file mode 100644 index 906164a..0000000 --- a/src/stm32f1/usb.cpp +++ /dev/null @@ -1,1064 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "power.hpp" -#include "stm32f1/pin.hpp" - -namespace hal::stm32f1 { -namespace { - -struct usb_endpoint_register_t -{ - struct [[gnu::packed]] bits - { - unsigned ea : 4; - unsigned stat_tx : 2; - unsigned dogtx : 1; - unsigned tx_complete : 1; - unsigned ep_kind : 1; - unsigned ep_type : 2; - unsigned setup : 1; - unsigned stat_rx : 2; - unsigned dtog_rx : 1; - unsigned rx_complete : 1; - }; - // Write operations should not use read-modify-write but write directly. - // Writing zeros to this register does not change - union - { - hal::u32 volatile EPR; - bits volatile bit; - }; -}; - -struct usb_reg_t -{ - /// Endpoint Registers - std::array EP; - /// Reserved Registers - std::array reserved; - /// Control Register - hal::u32 volatile CNTR; - /// Interrupt Status Register - hal::u32 volatile ISTR; - /// Frame Number Register - hal::u32 volatile FNR; - /// Device Address Register - hal::u32 volatile DADDR; - /// Buffer Table Address Register - hal::u32 volatile BTABLE; -}; - -// Ensure that CNTR is put in the correct location -static_assert(offsetof(usb_reg_t, CNTR) == 0x40); -static_assert(offsetof(usb_reg_t, BTABLE) == 0x50); - -inline auto* usb_reg = reinterpret_cast(0x4000'5C00); - -enum class endpoint_type : u8 -{ - bulk = 0b00, - control = 0b01, - iso = 0b10, - interrupt = 0b11, -}; - -struct control // NOLINT -{ - // FRES: Force USB Reset - static constexpr auto force_reset = bit_mask::from<0>(); - // PDWN: Power Down - static constexpr auto power_down = bit_mask::from<1>(); - // LPMODE: Low-power Mode - [[maybe_unused]] static constexpr auto low_power_mode = bit_mask::from<2>(); - // FSUSP: Force Suspend - [[maybe_unused]] static constexpr auto force_suspend = bit_mask::from<3>(); - // RESUME: Resume Request - [[maybe_unused]] static constexpr auto resume_request = bit_mask::from<4>(); - // ESOFM: Expected Start Of Frame Interrupt Mask - [[maybe_unused]] static constexpr auto expected_start_of_frame_interrupt = - bit_mask::from<8>(); - // SOFM: Start Of Frame Interrupt Mask - [[maybe_unused]] static constexpr auto start_of_frame_interrupt = - bit_mask::from<9>(); - // RESETM: USB Reset Interrupt Mask - static constexpr auto reset_interrupt = bit_mask::from<10>(); - // SUSPM: Suspend Mode Interrupt Mask - static constexpr auto suspend_mode_interrupt = bit_mask::from<11>(); - // WKUPM: Wakeup Interrupt Mask - static constexpr auto wakeup_interrupt = bit_mask::from<12>(); - // ERRM: Error Interrupt Mask - [[maybe_unused]] static constexpr auto error_interrupt = bit_mask::from<13>(); - // PMAOVRM: Packet Memory Area Over / Underrun Interrupt Mask - static constexpr auto packet_memory_interrupt = bit_mask::from<14>(); - // CTRM: Correct Transfer for Isochronous Endpoint Interrupt Mask - static constexpr auto correct_transfer_interrupt = bit_mask::from<15>(); -}; - -struct interrupt_status // NOLINT -{ - static constexpr auto endpoint_id = bit_mask::from<0, 3>(); - static constexpr auto direction = bit_mask::from<4>(); - // ESOF: Expected Start Of Frame - [[maybe_unused]] static constexpr auto expected_start_of_frame = - bit_mask::from<8>(); - // SOF: Start Of Frame - [[maybe_unused]] static constexpr auto start_of_frame = bit_mask::from<9>(); - // RESET: USB Reset Request - static constexpr auto reset_request = bit_mask::from<10>(); - // SUSP: Suspend Mode Request - static constexpr auto suspend_mode_request = bit_mask::from<11>(); - // WKUP: Wake Up - static constexpr auto wake_up = bit_mask::from<12>(); - // ERR: Error - static constexpr auto error = bit_mask::from<13>(); - // PMAOVR: Packet Memory Area Over / Underrun - static constexpr auto packet_memory_over_underrun = bit_mask::from<14>(); - // CTR: Correct Transfer - static constexpr auto correct_transfer = bit_mask::from<15>(); -}; - -struct frame_number // NOLINT -{ - // FN: Frame Number - [[maybe_unused]] static constexpr auto count = bit_mask::from<0, 10>(); - // LSOF: Lost SOF - [[maybe_unused]] static constexpr auto lost_sof = bit_mask::from<11>(); - // LCK: Lock - [[maybe_unused]] static constexpr auto lock = bit_mask::from<12>(); - // RXDM: Receive Data - Line Status - [[maybe_unused]] static constexpr auto receive_data_status = - bit_mask::from<13>(); - // RXDP: Transmit Data - Line Status - [[maybe_unused]] static constexpr auto transmit_data_status = - bit_mask::from<14>(); -}; - -struct device_address // NOLINT -{ - // ADD: Device Address - static constexpr auto address = bit_mask::from<0, 6>(); - // EF: Enable Function - static constexpr auto enable_function = bit_mask::from<7>(); -}; - -struct endpoint // NOLINT -{ - // EA: Endpoint Address - static constexpr auto address = bit_mask::from<0, 3>(); - // STAT_TX: Status Bits, for transmission transfers - static constexpr auto status_tx = bit_mask::from<4, 5>(); - // DTOG_TX: Data Toggle, for transmission transfers - [[maybe_unused]] static constexpr auto data_toggle_tx = bit_mask::from<6>(); - // CTR_TX: Correct Transfer for Transmission - static constexpr auto correct_transfer_tx = bit_mask::from<7>(); - // EP_KIND: Endpoint Kind - static constexpr auto kind = bit_mask::from<8>(); - // EP_TYPE: Endpoint Type - static constexpr auto type = bit_mask::from<9, 10>(); - // SETUP: Setup Transaction Completed - [[maybe_unused]] static constexpr auto setup_complete = bit_mask::from<11>(); - // STAT_RX: Status Bits, for reception transfers - static constexpr auto status_rx = bit_mask::from<12, 13>(); - // DTOG_RX: Data Toggle, for reception transfers - [[maybe_unused]] static constexpr auto data_toggle_rx = bit_mask::from<14>(); - // CTR_RX: Correct Transfer for Reception - static constexpr auto correct_transfer_rx = bit_mask::from<15>(); -}; - -struct block_table -{ - [[maybe_unused]] static constexpr std::array block_size_table{ - 2, - 32, - }; - // BL: Block size = 0 means 2 bytes, = 1 means 32 bytes - static constexpr auto block_size = bit_mask::from<15>(); - static constexpr auto number_of_blocks = bit_mask::from<14, 10>(); - static constexpr auto count = bit_mask::from<9, 0>(); -}; - -// Skipped: Buffer Descriptor Table (BDT) structure as it might vary depending -// on the specific STM32F103 variant - -usb_reg_t& reg() -{ - return *usb_reg; -} - -// Memory internal to USB used for sending and receiving packets to and from the -// HOST -constexpr std::size_t packet_buffer_sram_size = 512; -// Max number of endpoints -constexpr std::size_t max_endpoints = 8; -// Set the start of the buffer descriptor to the start of the packet buffer; -constexpr std::size_t buffer_descriptor_table_start = 0; -// Page 650 has an equation for the Reception byte count n for endpoint N. -// The equation is `[USB_BTABLE] + n*16 + 12` which also represents the furthest -// the BTABLE could ever be from the start of the packet buffer. Lets make that -// a reserved area of the packet buffer. The rest of the memory can be used for -// endpoint send and receive. -constexpr std::size_t buffer_descriptor_table_end = (max_endpoints * 16) + 12; - -constexpr std::size_t initial_packet_buffer_memory = - packet_buffer_sram_size - buffer_descriptor_table_end; - -/// TODO(#157): make this no longer fixed, maybe... -constexpr hal::u16 fixed_endpoint_size = 16; -constexpr auto endpoint_memory_size = fixed_endpoint_size; -constexpr auto block_number = fixed_endpoint_size / 2U; -constexpr auto rx_endpoint_count_mask = - hal::bit_value(0U) - .clear() // each number of blocks = 2x - .insert(block_number) - .to(); - -std::span usb_packet_buffer_sram() -{ - return { reinterpret_cast(0x4000'6000), packet_buffer_sram_size }; -} - -std::span usb_packet_buffer_sram_u32() -{ - // NOTE: dividing by the sizeof(hal::u16) is not a mistake. The memory is u16 - // addressable by the USB peripheral but u32 accessible/aligned for the - // processor. This means that each u16 has padding of an additional u16 that - // goes nowhere. The number of u16 blocks in this memory region is equal to - // the u32 blocks and thats why we do the division here. - return { reinterpret_cast(0x4000'6000), - packet_buffer_sram_size / sizeof(hal::u16) }; -} - -constexpr hal::u16 tx_endpoint_memory_address(std::size_t p_endpoint) -{ - auto const offset = (p_endpoint * 2) * endpoint_memory_size; - return buffer_descriptor_table_end + offset; -} - -constexpr hal::u16 rx_endpoint_memory_address(std::size_t p_endpoint) -{ - auto const offset = ((p_endpoint * 2) + 1) * endpoint_memory_size; - return buffer_descriptor_table_end + offset; -} - -struct buffer_descriptor_block -{ - u32 tx_address; - u32 tx_count; - u32 rx_address; - u32 rx_count; - - void setup_ctrl_descriptor() - { - tx_address = tx_endpoint_memory_address(0); - tx_count = 0; - - rx_address = rx_endpoint_memory_address(0); - rx_count = rx_endpoint_count_mask; - } - - void setup_in_endpoint_for(u8 p_endpoint) - { - tx_address = tx_endpoint_memory_address(p_endpoint); - tx_count = 0; - } - - void setup_out_endpoint_for(u8 p_endpoint) - { - rx_address = rx_endpoint_memory_address(p_endpoint); - rx_count = rx_endpoint_count_mask; - } - - std::size_t bytes_received() - { - auto const bytes_received = hal::bit_extract(rx_count); - return bytes_received; - } - - hal::u16 rx_address_offset() - { - return rx_address; - } - - std::span rx_span() - { - auto const offset = rx_address_offset() / sizeof(u16); - auto const size = bytes_received() / 2; - return usb_packet_buffer_sram_u32().subspan(offset, size); - } - - hal::u16 tx_address_offset() - { - return tx_address; - } - - std::span tx_span() - { - auto const offset = tx_address_offset() / sizeof(u16); - auto const size = fixed_endpoint_size / 2; - return usb_packet_buffer_sram_u32().subspan(offset, size); - } - - void set_count_tx(hal::u16 p_transfer_size) - { - tx_count = p_transfer_size; - } -}; - -buffer_descriptor_block& endpoint_descriptor_block(std::size_t p_endpoint) -{ - auto* buffer = usb_packet_buffer_sram().data(); - buffer += p_endpoint * sizeof(buffer_descriptor_block); - return *std::bit_cast(buffer); -} - -constexpr auto endpoint_invariant_mask = hal::bit_value(0U) - .insert(0xFUL) - .set() - .set() - .insert(0xFUL) - .set() - .to(); - -/// Same stat value for rx and tex -enum class stat : u8 -{ - // all reception requests addressed to this endpoint are ignored. - disabled = 0b00, - stall = 0b01, - nak = 0b10, - valid = 0b11, -}; - -/// Same stat value for rx and tex -enum class dtog : u8 -{ - // all reception requests addressed to this endpoint are ignored. - tog0 = 0b0, - tog1 = 0b1, -}; - -template -void set_endpoint_register_toggle(std::size_t p_endpoint, - enumeration auto p_enum_value) -{ - if (p_endpoint > reg().EP.size()) { - return; - } - - auto& endpoint_register = reg().EP[p_endpoint].EPR; - auto const reg_value = endpoint_register; - auto const masked_endpoint_reg = reg_value & endpoint_invariant_mask; - auto const desired_stat = hal::value(p_enum_value); - auto const current_stat = hal::bit_extract(reg_value); - auto const toggle_mask = current_stat ^ desired_stat; - auto const final_reg_value = hal::bit_value(masked_endpoint_reg) - .template insert(toggle_mask) - .template to(); - endpoint_register = final_reg_value; -} - -void set_rx_stat(std::size_t p_endpoint, stat p_stat) -{ - set_endpoint_register_toggle(p_endpoint, p_stat); -} - -void set_tx_stat(std::size_t p_endpoint, stat p_stat) -{ - set_endpoint_register_toggle(p_endpoint, p_stat); -} - -void set_endpoint_address_and_type(std::size_t p_endpoint, endpoint_type p_type) -{ - auto& endpoint_register = reg().EP[p_endpoint].EPR; - auto const reg_value = endpoint_register; - auto const masked_endpoint_reg = reg_value & endpoint_invariant_mask; - auto const final_reg_value = hal::bit_value(masked_endpoint_reg) - .insert(p_endpoint) - .insert(0U) - .insert(hal::value(p_type)) - .to(); - endpoint_register = final_reg_value; -} - -template -void clear_correct_transfer_for(u8 endpoint_id) -{ - auto& endpoint_reg = reg().EP[endpoint_id].EPR; - auto const endpoint_value = endpoint_reg; - auto masked_value = endpoint_value & endpoint_invariant_mask; - auto const masked_value_with_transfer_cleared = - hal::bit_value(masked_value).template clear().template to(); - endpoint_reg = masked_value_with_transfer_cleared; -} -} // namespace - -int error_detected_count = 0; - -void handle_bus_reset() -{ - // assignment to this register acts as an AND gate - reg().ISTR = 0; - reg().DADDR = 0u; // disable USB Function - - // Clear any previous information within the packet buffer - std::ranges::fill(usb_packet_buffer_sram(), 0); - - // Set the start of the buffer descriptor table to the start of the packet - // buffer. - reg().BTABLE = buffer_descriptor_table_start; - - endpoint_descriptor_block(0).setup_ctrl_descriptor(); - set_endpoint_address_and_type(0, endpoint_type::control); - set_rx_stat(0, stat::nak); - set_tx_stat(0, stat::nak); - - // Reset endpoints to disabled - for (std::size_t i = 1; i < reg().EP.size(); i++) { - reg().EP[i].EPR = i; - set_rx_stat(i, stat::disabled); - set_tx_stat(i, stat::disabled); - } - - hal::bit_modify(reg().CNTR) - .set(control::reset_interrupt) - .set(control::correct_transfer_interrupt) - .set(control::wakeup_interrupt) - .set(control::suspend_mode_interrupt) - .set(control::packet_memory_interrupt); - - hal::bit_modify(reg().DADDR).set(device_address::enable_function); -} - -// NOLINTNEXTLINE(bugprone-exception-escape) -void usb::interrupt_handler() noexcept -{ - auto& interrupt_reg = reg().ISTR; - - auto const interrupt_reg_value = interrupt_reg; - auto const endpoint_id = - hal::bit_extract(interrupt_reg_value); - auto const direction = - hal::bit_extract(interrupt_reg_value); - bool const transfer_completed = - hal::bit_extract(interrupt_reg_value); - - if (transfer_completed) { - if (direction == 0 /* meaning tx */) { - clear_correct_transfer_for(endpoint_id); - } else { - // Call callback using variant visitor - std::visit( - [](auto&& p_callback) { - using T = std::decay_t; - auto constexpr is_control_tag = - std::is_same_v>; - if constexpr (is_control_tag) { - p_callback(ctrl_receive_tag{}); - } else if constexpr (std::is_same_v< - T, - hal::callback>) { - p_callback(out_receive_tag{}); - } else { - static_assert(hal::error::invalid_option, - "USB RX Out callback visitor is non-exhaustive!"); - } - }, - m_out_callbacks[endpoint_id]); - - clear_correct_transfer_for(endpoint_id); - } - } - - bool const reset_request = - hal::bit_extract(interrupt_reg_value); - - if (reset_request) { - handle_bus_reset(); - } - - bool const error_detected = - hal::bit_extract(interrupt_reg_value); - - if (error_detected) { - // assignment to this register acts as an AND gate - interrupt_reg = ~(1U << interrupt_status::error.position); - error_detected_count++; - } - - bool const suspend_detected = - hal::bit_extract( - interrupt_reg_value); - - if (suspend_detected) { - // assignment to this register acts as an AND gate - interrupt_reg = ~(1U << interrupt_status::suspend_mode_request.position); - } - - bool const wake_detected = - hal::bit_extract(interrupt_reg_value); - - if (wake_detected) { - // assignment to this register acts as an AND gate - interrupt_reg = ~(1U << interrupt_status::wake_up.position); - } - - bool const overrun_detected = - hal::bit_extract( - interrupt_reg_value); - - if (overrun_detected) { - // assignment to this register acts as an AND gate - interrupt_reg = - ~(1U << interrupt_status::packet_memory_over_underrun.position); - } -} - -usb::usb(hal::v5::strong_ptr_only_token, - hal::v5::strong_ptr const& p_clock, - hal::time_duration p_write_timeout) - : m_clock(p_clock) - , m_write_timeout(p_write_timeout) - , m_available_endpoint_memory(initial_packet_buffer_memory) -{ - // USB already active by some other means - if (is_on(peripheral::usb) || is_on(peripheral::can1)) { - hal::safe_throw(hal::device_or_resource_busy(this)); - } - - auto const usb_frequency = frequency(peripheral::usb); - - if (not hal::equals(usb_frequency, 48.0_MHz)) { - hal::safe_throw(hal::operation_not_supported(nullptr)); - } - - // Clear any previous information within the packet buffer - std::ranges::fill(usb_packet_buffer_sram(), 0); - - auto handle = - static_callable([this] { interrupt_handler(); }); - // Only enable low priority because high priority is for iso endpoints which - // we do not support yet. - hal::stm32f1::initialize_interrupts(); - cortex_m::enable_interrupt(irq::usb_lp_can1_rx0, handle.get_handler()); - cortex_m::enable_interrupt(irq::usb_hp_can1_tx, handle.get_handler()); - - // Must be done prior to lifting power down bit - configure_pin({ .port = 'A', .pin = 11 }, push_pull_alternative_output); - configure_pin({ .port = 'A', .pin = 12 }, push_pull_alternative_output); - - power_on(peripheral::usb); - - using namespace std::chrono_literals; - - hal::delay(*p_clock, 1ms); - // Perform reset - hal::bit_modify(reg().CNTR) - .set(control::power_down) - .set(control::force_reset); - hal::delay(*p_clock, 1ms); - - // The USB peripheral sets this bit on system reset, we need to clear it to - // allow the device to power on. We must wait approximately 1us before we can - // proceed for the stm32f103. - hal::bit_modify(reg().CNTR).clear(control::power_down); - hal::delay(*p_clock, 1ms); - - // Clears everything including the force reset, enabling the USB device. - reg().CNTR = 0; - - handle_bus_reset(); -} - -usize usb::read_endpoint(u8 p_endpoint, - std::span p_buffer, - u16& p_bytes_read) -{ - auto const& ep = usb_reg->EP.at(p_endpoint); - auto const endpoint_stat = - static_cast(hal::bit_extract(ep.EPR)); - - // If the endpoint status is NAK, it means that we currently have a packet in - // the USB SRAM, and we haven't consumed all of the memory yet. We - // automatically move to VALID when all of the data has been consumed. - if (endpoint_stat != stat::nak) { - // return zero length buffer if the endpoint status is VALID meaning the - // endpoint is ready to receive data but hasn't received any data yet. - return 0; - } - - auto& descriptor = endpoint_descriptor_block(p_endpoint); - auto const bytes_remaining = descriptor.bytes_received() - p_bytes_read; - auto const bytes_to_copy = std::min(bytes_remaining, p_buffer.size()); - - // Offset the rx span by the number of bytes read divided by 2U (word size) - auto const rx_span = descriptor.rx_span(); - - for (size_t idx = 0; idx < bytes_to_copy; idx++) { - auto const offset_index = p_bytes_read + idx; - // Grab next word, divided by 2 to get the u16 word - auto const value = rx_span[offset_index / 2U]; - // Determine the shift based on if the index is odd or not - auto const shift = (offset_index & 1U) ? 8U : 0U; - auto const byte = (value >> shift) & 0xFF; - p_buffer[idx] = byte; - } - - p_bytes_read += bytes_to_copy; - - if (p_bytes_read == descriptor.bytes_received()) { - p_bytes_read = 0; - set_rx_stat(p_endpoint, stat::valid); - } - - return bytes_to_copy; -} - -void usb::wait_for_endpoint_transfer_completion(u8 p_endpoint) -{ - constexpr auto nak_u32_value = static_cast(stat::nak); - auto& endpoint_reg = reg().EP[p_endpoint].EPR; - auto endpoint_tx_status = hal::bit_extract(endpoint_reg); - auto const deadline = hal::future_deadline(*m_clock, m_write_timeout); - while (endpoint_tx_status != nak_u32_value) { - endpoint_tx_status = hal::bit_extract(endpoint_reg); - if (m_clock->uptime() >= deadline) { - hal::safe_throw(hal::timed_out(this)); - } - } -} - -void usb::fill_endpoint(hal::u8 p_endpoint, - std::span p_data, - hal::u16 p_max_length) -{ - auto& descriptor = endpoint_descriptor_block(p_endpoint); - auto const tx_span = descriptor.tx_span(); - - do { - while (descriptor.tx_count < p_max_length) { - if (p_data.empty()) { - return; - } - if (descriptor.tx_count & 0b1) { - hal::bit_modify(tx_span[descriptor.tx_count / 2U]) - .insert>(p_data[0]); - } else { - hal::bit_modify(tx_span[descriptor.tx_count / 2U]) - .insert>(p_data[0]); - } - p_data = p_data.subspan(1); - descriptor.tx_count++; - } - - // More data remaining, thus we need to ship this data off to the USB bus. - if (not p_data.empty()) { - set_tx_stat(p_endpoint, stat::valid); - wait_for_endpoint_transfer_completion(p_endpoint); - descriptor.tx_count = 0; - continue; - } - } while (not p_data.empty()); -} - -void usb::set_callback(hal::u8 p_endpoint, callback_variant_t const& p_callback) -{ - m_out_callbacks[p_endpoint] = p_callback; -} - -void usb::write_to_endpoint(u8 p_endpoint, std::span p_data) -{ - // We use a copy and not the reference to not modify the span. - fill_endpoint(p_endpoint, p_data, fixed_endpoint_size); -} - -void usb::flush_endpoint(u8 p_endpoint) -{ - // send whatever is in the USB buffer - set_tx_stat(p_endpoint, stat::valid); - wait_for_endpoint_transfer_completion(p_endpoint); - endpoint_descriptor_block(p_endpoint).tx_count = 0; -} - -usb::~usb() -{ - configure_pin({ .port = 'A', .pin = 11 }, input_pull_up); - configure_pin({ .port = 'A', .pin = 12 }, input_pull_up); - power_off(peripheral::usb); - cortex_m::disable_interrupt(irq::usb_lp_can1_rx0); - cortex_m::disable_interrupt(irq::usb_hp_can1_tx); -} - -/** - * @brief USB Control Endpoint Interface - * - * This class represents the control endpoint of a USB device. The control - * endpoint is crucial for USB communication as it handles device enumeration, - * configuration, and general control operations. - * - * Use cases: - * - Initiating USB connections - * - Handling USB enumeration process - * - Setting device addresses - * - Responding to standard USB requests - * - Sending and receiving control data - * - */ -class control_endpoint : public hal::v5::usb::control_endpoint -{ -public: - control_endpoint(hal::v5::strong_ptr const& p_usb) - : m_usb(p_usb) - { - set_rx_stat(0, stat::valid); - endpoint_descriptor_block(0).tx_count = 0; - } - - ~control_endpoint() override = default; - -private: - void flush() - { - m_usb->flush_endpoint(0); - set_tx_stat(0, stat::stall); - set_rx_stat(0, stat::valid); - } - - [[nodiscard]] hal::v5::usb::endpoint_info driver_info() const override - { - return { .size = fixed_endpoint_size, .number = 0, .stalled = false }; - } - - void driver_stall(bool p_should_stall) override - { - if (p_should_stall) { - set_rx_stat(0, stat::stall); - set_tx_stat(0, stat::stall); - } else { - set_rx_stat(0, stat::valid); - set_tx_stat(0, stat::nak); - } - } - - void driver_connect(bool p_should_connect) override - { - hal::bit_modify(reg().DADDR) - .insert(p_should_connect); - } - - void driver_set_address(u8 p_address) override - { - hal::bit_modify(reg().DADDR) - .set(device_address::enable_function) - .insert(p_address); - } - - void driver_write(hal::v5::scatter_span p_data_list) override - { - constexpr auto ctrl_endpoint = 0; - set_rx_stat(ctrl_endpoint, stat::stall); - set_tx_stat(ctrl_endpoint, stat::nak); - if (p_data_list.empty()) { - flush(); - } - for (auto const& data : p_data_list) { - m_usb->write_to_endpoint(ctrl_endpoint, data); - } - } - - usize driver_read(hal::v5::scatter_span p_data_list) override - { - usize total_memory = 0; - for (auto const& data : p_data_list) { - auto const data_copied = m_usb->read_endpoint(0, data, m_bytes_read); - - if (data_copied == 0) { - break; - } - - total_memory += data_copied; - } - return total_memory; - } - - void driver_on_receive( - callback const& p_callback) override - { - m_usb->m_out_callbacks[0] = p_callback; - } - - void driver_reset() override - { - reset(); - } - - hal::v5::strong_ptr m_usb; - u16 m_bytes_read = 0; -}; - -hal::v5::strong_ptr -acquire_usb_control_endpoint(std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_usb) -{ - return hal::v5::make_strong_ptr(p_allocator, p_usb); -} - -template -struct interface_select -{ - using type_in = std::conditional_t; - using type_out = std::conditional_t; - using type = std::conditional_t; -}; - -template -constexpr auto to_endpoint_type() -> endpoint_type -{ - constexpr auto is_interrupt = - std::is_same_v || - std::is_same_v; - constexpr auto is_bulk = - std::is_same_v || - std::is_same_v; - - static_assert( - is_bulk || is_interrupt, - "Only bulk and interrupt OUT endpoints are supported by this library"); - - if (is_interrupt) { - return endpoint_type::interrupt; - } - return endpoint_type::bulk; -}; - -/** - * @brief USB Interrupt IN Endpoint Interface - * - * This class represents an interrupt IN endpoint of a USB device. Interrupt - * IN endpoints are used for small, time-sensitive data transfers from the - * device to the host. - * - * Use cases: - * - Sending periodic status updates - * - Transmitting small amounts of data with guaranteed latency - * - Ideal for devices like keyboards, mice, or game controllers - */ - -template -class in_endpoint : public Interface -{ -public: - ~in_endpoint() override = default; - - in_endpoint(hal::v5::strong_ptr const& p_usb, u8 p_endpoint_number) - : m_usb(p_usb) - , m_endpoint_number(p_endpoint_number) - { - reset(); - } - -private: - void reset() - { - endpoint_descriptor_block(m_endpoint_number) - .setup_in_endpoint_for(m_endpoint_number); - set_endpoint_address_and_type(m_endpoint_number, - to_endpoint_type()); - set_rx_stat(m_endpoint_number, stat::nak); - } - - [[nodiscard]] bool stalled() const - { - return hal::bit_extract( - usb_reg->EP[m_endpoint_number].EPR) <= 0b01; - } - - void driver_write(hal::v5::scatter_span p_data_list) override - { - if (p_data_list.empty()) { - m_usb->flush_endpoint(m_endpoint_number); - } - for (auto const& data : p_data_list) { - m_usb->write_to_endpoint(m_endpoint_number, data); - } - } - - [[nodiscard]] hal::v5::usb::endpoint_info driver_info() const override - { - return { - .size = fixed_endpoint_size, - .number = static_cast(m_endpoint_number | (1U << 7U)), - .stalled = stalled(), - }; - } - - void driver_stall(bool p_should_stall) override - { - if (p_should_stall) { - set_tx_stat(m_endpoint_number, stat::stall); - } else { - set_tx_stat(m_endpoint_number, stat::nak); - } - } - - void driver_reset() override - { - reset(); - } - - hal::v5::strong_ptr m_usb; - u16 m_bytes_read = 0; - u8 m_endpoint_number; -}; - -template -class out_endpoint : public Interface -{ -public: - using rx_tag = typename Interface::on_receive_tag; - - ~out_endpoint() override = default; - - out_endpoint(hal::v5::strong_ptr const& p_usb, u8 p_endpoint_number) - : m_usb(p_usb) - , m_endpoint_number(p_endpoint_number) - { - reset(); - } - -private: - [[nodiscard]] bool stalled() const - { - return hal::bit_extract( - usb_reg->EP[m_endpoint_number].EPR) <= 0b01; - } - - void reset() - { - endpoint_descriptor_block(m_endpoint_number) - .setup_out_endpoint_for(m_endpoint_number); - set_endpoint_address_and_type(m_endpoint_number, - to_endpoint_type()); - set_rx_stat(m_endpoint_number, stat::valid); - } - - void driver_on_receive(hal::callback const& p_callback) override - { - m_usb->set_callback(m_endpoint_number, p_callback); - } - - [[nodiscard]] hal::v5::usb::endpoint_info driver_info() const override - { - return { - .size = fixed_endpoint_size, - .number = m_endpoint_number, - .stalled = stalled(), - }; - } - - void driver_stall(bool p_should_stall) override - { - if (p_should_stall) { - set_rx_stat(m_endpoint_number, stat::stall); - } else { - set_rx_stat(m_endpoint_number, stat::valid); - } - } - - usize driver_read(hal::v5::scatter_span p_data_list) override - { - usize total_memory = 0; - for (auto const& data : p_data_list) { - auto const data_copied = - m_usb->read_endpoint(m_endpoint_number, data, m_bytes_read); - - if (data_copied == 0) { - break; - } - - total_memory += data_copied; - } - return total_memory; - } - - void driver_reset() override - { - reset(); - } - - hal::v5::strong_ptr m_usb; - u16 m_bytes_read = 0; - u8 m_endpoint_number; -}; - -usb_interrupt_endpoint_pair acquire_usb_interrupt_endpoint( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_usb) -{ - auto const endpoint_assignment = p_usb->m_endpoints_allocated++; - - auto out = hal::v5::make_strong_ptr< - out_endpoint>( - p_allocator, p_usb, endpoint_assignment); - - auto in = - hal::v5::make_strong_ptr>( - p_allocator, p_usb, endpoint_assignment); - - return { .out = out, .in = in }; -} - -usb_bulk_endpoint_pair acquire_usb_bulk_endpoint( - std::pmr::polymorphic_allocator<> p_allocator, - hal::v5::strong_ptr const& p_usb) -{ - auto const endpoint_assignment = p_usb->m_endpoints_allocated++; - - auto out = - hal::v5::make_strong_ptr>( - p_allocator, p_usb, endpoint_assignment); - - auto in = - hal::v5::make_strong_ptr>( - p_allocator, p_usb, endpoint_assignment); - - return { .out = out, .in = in }; -} -} // namespace hal::stm32f1 diff --git a/src/stm32f40/output_pin.cpp b/src/stm32f40/output_pin.cpp deleted file mode 100644 index 033138f..0000000 --- a/src/stm32f40/output_pin.cpp +++ /dev/null @@ -1 +0,0 @@ -// This is placeholder diff --git a/src/stm32f411/clock.cpp b/src/stm32f411/clock.cpp deleted file mode 100644 index c1951ad..0000000 --- a/src/stm32f411/clock.cpp +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "flash_reg.hpp" -#include "rcc_reg.hpp" - -namespace hal::stm32f411 { - -namespace { -hal::hertz rtc_clock_rate = 0.0_Hz; // defaults to "no clock" -hal::hertz pll_clock_rate = 0.0_Hz; // undefined until pll is enabled -hal::hertz usb_otg_clock_rate = 0.0_Hz; // undefined until enabled -hal::hertz ahb_clock_rate = internal_high_speed_oscillator; -hal::hertz apb1_clock_rate = internal_high_speed_oscillator; -hal::hertz apb2_clock_rate = internal_high_speed_oscillator; -hal::hertz timer_apb1_clock_rate = internal_high_speed_oscillator; -hal::hertz timer_apb2_clock_rate = internal_high_speed_oscillator; -} // namespace - -void pll_valid(clock_tree const& p_clock_tree) -{ - /// Check if the VCO input can be set from 1_MHz to 2_MHz - if (p_clock_tree.pll.source == pll_source::high_speed_external) { - if (p_clock_tree.high_speed_external < 2.0_MHz || - p_clock_tree.high_speed_external > 63 * 2.0_MHz) { - hal::safe_throw(hal::argument_out_of_domain(nullptr)); - } - } - - /// Check the Output Frequency is within bound - if (p_clock_tree.pll.output > (100.0_MHz) || - p_clock_tree.pll.output < (100.0_MHz / 8)) { - hal::safe_throw(hal::argument_out_of_domain(nullptr)); - } -} - -// TODO (#62): Add I2S to the clock configs -// TODO (#66): Add 48_MHz to the clock configs -hertz configure_pll(clock_tree const& p_clock_tree) -{ - auto vco_in = 2.0_MHz; - float target = 0.0f; - float output_division_factor = 0; - if (p_clock_tree.pll.enable) { - uint8_t input_division_factor = 2U; - pll_valid(p_clock_tree); - - if (p_clock_tree.pll.source == pll_source::high_speed_external) { - if (p_clock_tree.high_speed_external < 4.0_MHz) { - vco_in = p_clock_tree.high_speed_external / 2; - bit_modify(rcc->pllconfig) - .insert(rcc_pllcnfg::input_division_factor, input_division_factor); - } else { - float division_factor = p_clock_tree.high_speed_external / 2.0_MHz; - if ((division_factor - std::floor(division_factor)) > 0) { - input_division_factor = static_cast(division_factor) + 1; - } else { - input_division_factor = static_cast(division_factor); - } - vco_in = p_clock_tree.high_speed_external / division_factor; - } - } else { - input_division_factor = 8; - } - bit_modify(rcc->pllconfig) - .insert(rcc_pllcnfg::input_division_factor, input_division_factor); - - /// 2 * (output_division_factor + 1) is to get the PLLP division factor - /// Page 105 on the RM0383 User Manual - while (p_clock_tree.pll.output * (2.0f * (output_division_factor + 1.0f)) < - 100.0_MHz) { - output_division_factor += 1; - } - - bit_modify(rcc->pllconfig) - .insert(rcc_pllcnfg::main_division_factor, - static_cast(output_division_factor)); - - /// 2 * (output_division_factor + 1) is to get the PLLN multiplicaiton - /// factor Page 105 on the RM0383 User Manual - target = p_clock_tree.pll.output * - (2.0f * (output_division_factor + 1.0f)) / vco_in; - bit_modify(rcc->pllconfig) - .insert(rcc_pllcnfg::main_multiplication_factor, - static_cast(target)); - - bit_modify(rcc->pllconfig) - .insert(rcc_pllcnfg::pll_source, value(p_clock_tree.pll.source)); - - bit_modify(rcc->cr).insert(rcc_cr::main_pll_enable, - p_clock_tree.pll.enable); - - while (!bit_extract(rcc->cr)) { - continue; - } - } - return (vco_in * std::floor(target) / - (2 * (std::floor(output_division_factor + 1.0f)))); -} - -void configure_clocks(clock_tree const& p_clock_tree) -{ - hal::hertz system_clock = 0.0_Hz; - - // ========================================================================= - // Step 1. Select internal clock source for everything. - // Make sure PLLs are not clock sources for everything. - // ========================================================================= - // Step 1.0 Turn on High speed external: - bit_modify(rcc->cr).set(rcc_cr::high_speed_internal_enable); - - // Step 1.1 Set SystemClock to HSI - bit_modify(rcc->config) - .insert(rcc_config::system_clock_switch, - value(system_clock_select::high_speed_internal)); - - // Step 1.4 Reset RTC clock registers - bit_modify(rcc->backup_domain_control) - .set(rcc_backup_domain_control::backup_domain_software_reset); - - // Manually clear the RTC reset bit - bit_modify(rcc->backup_domain_control) - .clear(rcc_backup_domain_control::backup_domain_software_reset); - - // ========================================================================= - // Step 2. Disable PLL and external clock sources - // ========================================================================= - // 2.1 Move the system_clock to HSI - bit_modify(rcc->config).clear(rcc_config::system_clock_switch); - - while (bit_extract(rcc->config)) { - continue; - } - - // 2.2 Disable LSE - bit_modify(rcc->backup_domain_control) - .clear(rcc_backup_domain_control::low_speed_external_enable); - - while (bit_extract( - rcc->backup_domain_control)) { - continue; - } - - // 2.3 Disable HSE - bit_modify(rcc->cr).clear(rcc_cr::high_speed_external_enable); - - while (bit_extract(rcc->cr)) { - continue; - } - - // 2.4 Disable PLL - bit_modify(rcc->cr).clear(rcc_cr::main_pll_enable); - while (bit_extract(rcc->cr)) { - continue; - } - - // ========================================================================= - // Step 3. Enable External Oscillators - // ========================================================================= - // 3.1 Enable HSE - if (p_clock_tree.high_speed_external > 4.0_MHz) { - bit_modify(rcc->cr).set(rcc_cr::high_speed_external_enable); - while (!bit_extract(rcc->cr)) { - continue; - } - } - - // 3.2 Enable LSE - if (p_clock_tree.low_speed_external > 1.0_MHz) { - bit_modify(rcc->backup_domain_control) - .clear(rcc_backup_domain_control::low_speed_external_enable); - - while (bit_extract( - rcc->backup_domain_control)) { - continue; - } - } - - // ========================================================================= - // Step 4. Set up PLL - // ========================================================================= - pll_clock_rate = configure_pll(p_clock_tree); - - // ========================================================================= - // Step 5. Setup peripheral dividers - // ========================================================================= - bit_modify(rcc->config) - .insert(rcc_config::ahb_prescalar, value(p_clock_tree.ahb.divider)) - .insert(rcc_config::apb1_prescalar, value(p_clock_tree.ahb.apb1.divider)) - .insert(rcc_config::apb2_prescalar, value(p_clock_tree.ahb.apb2.divider)); - // ========================================================================= - // Step 6. Setup flash access - // ========================================================================= - bit_modify(flash_config->access_control_reg) - .set(flash_acess_control::instruction_cache_en) - .set(flash_acess_control::data_cache_en) - .set(flash_acess_control::prefetch_cache_en) - .insert(flash_acess_control::latency, 2U); - - // ========================================================================= - // Step 7. Set System Clock and RTC Clock - // ========================================================================= - - std::uint32_t target_clock_source = value(p_clock_tree.system_clock); - bit_modify(rcc->config) - .insert(rcc_config::system_clock_switch, target_clock_source); - - while (bit_extract(rcc->config) != - target_clock_source) { - continue; - } - switch (p_clock_tree.system_clock) { - case system_clock_select::high_speed_internal: - system_clock = internal_high_speed_oscillator; - break; - case system_clock_select::high_speed_external: - system_clock = p_clock_tree.high_speed_external; - break; - case system_clock_select::pll: - system_clock = pll_clock_rate; - break; - } - bit_modify(rcc->backup_domain_control) - .insert(rcc_backup_domain_control::rtc_clock_source, - value(p_clock_tree.rtc.source)) - .insert(rcc_backup_domain_control::rtc_enable, p_clock_tree.rtc.enable); - - bit_modify(rcc->ahb2enr) - .insert(rcc_ahb2::usb_otg_en, p_clock_tree.usb_otg_clock_enable); - // ========================================================================= - // Step 8. Define the clock rates for the system - // ========================================================================= - - switch (p_clock_tree.ahb.divider) { - case ahb_divider::divide_by_1: - ahb_clock_rate = system_clock / 1; - break; - case ahb_divider::divide_by_2: - ahb_clock_rate = system_clock / 2; - break; - case ahb_divider::divide_by_4: - ahb_clock_rate = system_clock / 4; - break; - case ahb_divider::divide_by_8: - ahb_clock_rate = system_clock / 8; - break; - case ahb_divider::divide_by_16: - ahb_clock_rate = system_clock / 16; - break; - case ahb_divider::divide_by_64: - ahb_clock_rate = system_clock / 64; - break; - case ahb_divider::divide_by_128: - ahb_clock_rate = system_clock / 128; - break; - case ahb_divider::divide_by_256: - ahb_clock_rate = system_clock / 256; - break; - case ahb_divider::divide_by_512: - ahb_clock_rate = system_clock / 512; - break; - } - - switch (p_clock_tree.ahb.apb1.divider) { - case apb_divider::divide_by_1: - apb1_clock_rate = ahb_clock_rate / 1; - break; - case apb_divider::divide_by_2: - apb1_clock_rate = ahb_clock_rate / 2; - break; - case apb_divider::divide_by_4: - apb1_clock_rate = ahb_clock_rate / 4; - break; - case apb_divider::divide_by_8: - apb1_clock_rate = ahb_clock_rate / 8; - break; - case apb_divider::divide_by_16: - apb1_clock_rate = ahb_clock_rate / 16; - break; - } - - switch (p_clock_tree.ahb.apb2.divider) { - case apb_divider::divide_by_1: - apb2_clock_rate = ahb_clock_rate / 1; - break; - case apb_divider::divide_by_2: - apb2_clock_rate = ahb_clock_rate / 2; - break; - case apb_divider::divide_by_4: - apb2_clock_rate = ahb_clock_rate / 4; - break; - case apb_divider::divide_by_8: - apb2_clock_rate = ahb_clock_rate / 8; - break; - case apb_divider::divide_by_16: - apb2_clock_rate = ahb_clock_rate / 16; - break; - } - - switch (p_clock_tree.rtc.source) { - case rtc_source::no_clock: - rtc_clock_rate = 0.0_Hz; - break; - case rtc_source::low_speed_internal: - rtc_clock_rate = internal_low_speed_oscillator; - break; - case rtc_source::low_speed_external: - rtc_clock_rate = p_clock_tree.low_speed_external; - break; - case rtc_source::high_speed_external_divided_by_128: - rtc_clock_rate = p_clock_tree.high_speed_external / 128; - break; - } - - // This is set to the default Dedicated Clocks Configuration setting. Unsure - // how to create an api for this register - - switch (p_clock_tree.ahb.apb1.divider) { - case apb_divider::divide_by_1: - timer_apb1_clock_rate = apb1_clock_rate; - break; - default: - timer_apb1_clock_rate = apb1_clock_rate * 2; - break; - } - - switch (p_clock_tree.ahb.apb2.divider) { - case apb_divider::divide_by_1: - timer_apb2_clock_rate = apb2_clock_rate; - break; - default: - timer_apb2_clock_rate = apb2_clock_rate * 2; - break; - } - return; -} - -hal::hertz frequency(peripheral p_id) -{ - switch (p_id) { - // TODO #62: Add I2S to the clock configs - case peripheral::i2s: - return pll_clock_rate; - - // Arm Cortex running clock rate. - // This code does not utilize the /8 clock for the system timer, thus the - // clock rate for that subsystem is equal to the CPU running clock. - case peripheral::system_timer: - [[fallthrough]]; - case peripheral::cpu: - return ahb_clock_rate; - - // APB1 Timers - case peripheral::timer2: - [[fallthrough]]; - case peripheral::timer3: - [[fallthrough]]; - case peripheral::timer4: - [[fallthrough]]; - case peripheral::timer5: - return timer_apb1_clock_rate; - - // APB2 Timers - case peripheral::timer1: - [[fallthrough]]; - case peripheral::timer9: - [[fallthrough]]; - case peripheral::timer10: - [[fallthrough]]; - case peripheral::timer11: - return timer_apb2_clock_rate; - - default: { - auto id_bus = value(p_id) / bus_id_offset; - switch (id_bus * bus_id_offset) { - case ahb1_bus: - return ahb_clock_rate; - case ahb2_bus: - return usb_otg_clock_rate; - case apb1_bus: - return apb1_clock_rate; - case apb2_bus: - return apb2_clock_rate; - default: - return 0.0_Hz; - } - } - } - - return 0.0_Hz; -} - -void maximum_speed_using_internal_oscillator() -{ - configure_clocks(clock_tree{ - .high_speed_external = 0.0_Hz, - .low_speed_external = 0.0_Hz, - .pll = { .enable = true, - .source = pll_source::high_speed_internal, - .output = 100.0_MHz }, - .system_clock = system_clock_select::pll, - .ahb = { .divider = ahb_divider::divide_by_1, - .apb1 = { .divider = apb_divider::divide_by_2 }, - .apb2 = { .divider = apb_divider::divide_by_1, - .adc = { .divider = adc_divider::divide_by_2 } } - - } }); -} - -}; // namespace hal::stm32f411 diff --git a/src/stm32f411/dma.cpp b/src/stm32f411/dma.cpp deleted file mode 100644 index 8d02dfd..0000000 --- a/src/stm32f411/dma.cpp +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "dma.hpp" -#include "power.hpp" - -namespace hal::stm32f411 { -namespace { -std::array, 16> dma_callbacks{}; - -template -void handle_dma_interrupt() noexcept -{ - dma_callbacks[dma_index](); - /// DMA transfer masks are all on bits 5,11,21 and 27 - constexpr std::array const transfer_complete_mask = { - bit_mask::from<5>(), - bit_mask::from<11>(), - bit_mask::from<21>(), - bit_mask::from<27>(), - }; - - dma_config_t* dma_reg; - if constexpr (dma_index / 8) { - dma_reg = get_dma_reg(peripheral::dma1); - } else { - dma_reg = get_dma_reg(peripheral::dma2); - } - - constexpr auto stream_index = dma_index % 8; - constexpr auto mask_index = stream_index % 4; - constexpr auto complete_mask = transfer_complete_mask[mask_index]; - - if constexpr (stream_index < 4) { - bit_modify(dma_reg->interupt_status_clear_low).clear(complete_mask); - } else { - bit_modify(dma_reg->interupt_status_clear_high).clear(complete_mask); - } -} - -void initialize_dma(peripheral p_dma) -{ - power dma_power(p_dma); - if (dma_power.is_on()) { - return; - } - - dma_power.on(); - - initialize_interrupts(); - - hal::cortex_m::enable_interrupt(irq::dma1_channel0, handle_dma_interrupt<0>); - hal::cortex_m::enable_interrupt(irq::dma1_channel1, handle_dma_interrupt<1>); - hal::cortex_m::enable_interrupt(irq::dma1_channel2, handle_dma_interrupt<2>); - hal::cortex_m::enable_interrupt(irq::dma1_channel3, handle_dma_interrupt<3>); - hal::cortex_m::enable_interrupt(irq::dma1_channel4, handle_dma_interrupt<4>); - hal::cortex_m::enable_interrupt(irq::dma1_channel5, handle_dma_interrupt<5>); - hal::cortex_m::enable_interrupt(irq::dma1_channel6, handle_dma_interrupt<6>); - hal::cortex_m::enable_interrupt(irq::dma1_channel7, handle_dma_interrupt<7>); - hal::cortex_m::enable_interrupt(irq::dma2_channel0, handle_dma_interrupt<8>); - hal::cortex_m::enable_interrupt(irq::dma2_channel1, handle_dma_interrupt<9>); - hal::cortex_m::enable_interrupt(irq::dma2_channel2, handle_dma_interrupt<10>); - hal::cortex_m::enable_interrupt(irq::dma2_channel3, handle_dma_interrupt<11>); - hal::cortex_m::enable_interrupt(irq::dma2_channel4, handle_dma_interrupt<12>); - hal::cortex_m::enable_interrupt(irq::dma2_channel5, handle_dma_interrupt<13>); - hal::cortex_m::enable_interrupt(irq::dma2_channel6, handle_dma_interrupt<14>); - hal::cortex_m::enable_interrupt(irq::dma2_channel7, handle_dma_interrupt<15>); -} - -hal::atomic_spin_lock dma_spin_lock; -hal::basic_lock* dma_lock = &dma_spin_lock; - -// This is here for cost sizing -[[maybe_unused]] constexpr auto dma_memory_usage_size = - sizeof(dma_callbacks) + sizeof(dma_spin_lock) + sizeof(dma_lock); // NOLINT -} // namespace - -void set_dma_lock(hal::basic_lock& p_lock) -{ - dma_lock = &p_lock; -} - -dma_channel_stream_t set_available_stream( - peripheral p_dma, - std::span const& p_possible_streams) -{ - auto dma_addr = get_dma_reg(p_dma); - while (true) { - for (auto stream_candidate : p_possible_streams) { - if (bit_extract( - dma_addr->stream[stream_candidate.stream].configure) == - stream_candidate.channel) { - return stream_candidate; - } else if (bit_extract( - dma_addr->stream[stream_candidate.stream].configure) == 0) { - bit_modify(dma_addr->stream[stream_candidate.stream].configure) - .insert(dma_stream_config::channel_select, stream_candidate.channel); - return stream_candidate; - } - } - } -} - -dma_channel_stream_t setup_dma_transfer( - peripheral p_dma, - std::span const& p_possible_streams, - dma_settings_t const& p_configuration, - hal::callback p_interrupt_callback) -{ - auto dma_addr = get_dma_reg(p_dma); - - std::lock_guard take_dma_lock(*dma_lock); - initialize_dma(p_dma); - - auto selected_config = set_available_stream(p_dma, p_possible_streams); - auto& stream = dma_addr->stream[selected_config.stream]; - switch (p_configuration.transfer_type) { - case dma_transfer_type::peripheral_to_memory: - stream.peripheral_address = - std::bit_cast(p_configuration.source); - stream.memory_0_address = - std::bit_cast(p_configuration.destination); - break; - case dma_transfer_type::memory_to_peripheral: - stream.peripheral_address = - std::bit_cast(p_configuration.destination); - stream.memory_0_address = - std::bit_cast(p_configuration.source); - break; - default: - stream.peripheral_address = - std::bit_cast(p_configuration.source); - stream.memory_0_address = - std::bit_cast(p_configuration.destination); - } - - stream.transfer_count = p_configuration.transfer_length; - - bit_modify(dma_addr->stream[selected_config.stream].configure) - .insert(dma_stream_config::peripheral_flow_controller, - value(p_configuration.flow_controller)) - .insert(dma_stream_config::data_transfer_direction, - value(p_configuration.transfer_type)) - .insert(dma_stream_config::circular_mode_enable, - p_configuration.circular_mode) - .insert(dma_stream_config::peripheral_increment_mode, - p_configuration.peripheral_address_increment) - .insert(dma_stream_config::memory_increment_mode, - p_configuration.memory_address_increment) - .insert(dma_stream_config::peripheral_data_size, - value(p_configuration.peripheral_data_size)) - .insert(dma_stream_config::memory_data_size, - value(p_configuration.memory_data_size)) - .insert(dma_stream_config::priority_level, - value(p_configuration.priority_level)) - .insert(dma_stream_config::double_buffer_mode, - p_configuration.double_buffer_mode) - .insert(dma_stream_config::current_target, p_configuration.current_target) - .insert(dma_stream_config::peripheral_burst_transfer, - value(p_configuration.peripheral_burst_size)) - .insert(dma_stream_config::memory_burst_transfer, - value(p_configuration.memory_burst_size)) - .insert(dma_stream_config::channel_select, selected_config.channel); - - bit_modify(dma_addr->stream[selected_config.stream].fifo_control) - .clear(dma_fifo_config::direct_mode_disable); - - uint8_t callback_entry = 0; - if (p_dma == peripheral::dma1) { - callback_entry = selected_config.stream; - } else { - callback_entry = selected_config.stream + 8; - } - - dma_callbacks[callback_entry] = std::move(p_interrupt_callback); - - constexpr std::array const dma_intr_mask = { - bit_mask::from<5, 0>(), - bit_mask::from<11, 6>(), - bit_mask::from<21, 16>(), - bit_mask::from<27, 22>(), - }; - - auto complete_mask = dma_intr_mask[selected_config.stream % 4]; - constexpr uint8_t clear_data = 0x1D; - if (selected_config.stream < 4) { - bit_modify(dma_addr->interupt_status_clear_low) - .insert(complete_mask, clear_data); - } else { - bit_modify(dma_addr->interupt_status_clear_high) - .insert(complete_mask, clear_data); - } - - bit_modify(dma_addr->stream[selected_config.stream].configure) - .set(dma_stream_config::stream_enable); - return selected_config; -}; - -void set_dma_memory_transfer(dma p_dma, - std::span p_source, - std::span const p_destination) -{ - std::array all_streams = { - { { .stream = 0, .channel = 0 }, - { .stream = 1, .channel = 0 }, - { .stream = 2, .channel = 0 }, - { .stream = 3, .channel = 0 }, - { .stream = 4, .channel = 0 }, - { .stream = 5, .channel = 0 }, - { .stream = 6, .channel = 0 }, - { .stream = 7, .channel = 0 } } - }; - dma_settings_t dma_setting = { - .source = p_source.data(), - .destination = p_destination.data(), - - .transfer_length = p_source.size(), - .flow_controller = dma_flow_controller::dma_controls_flow, - .transfer_type = dma_transfer_type::memory_to_memory, - .circular_mode = false, - .peripheral_address_increment = true, - .memory_address_increment = true, - .peripheral_data_size = dma_transfer_size::byte, - .memory_data_size = dma_transfer_size::byte, - .priority_level = dma_priority_level::high, - .double_buffer_mode = false, - .current_target = false, - .peripheral_burst_size = dma_burst_size::single_transfer, - .memory_burst_size = dma_burst_size::single_transfer - }; - setup_dma_transfer( - static_cast(p_dma), all_streams, dma_setting, []() {}); -} -} // namespace hal::stm32f411 diff --git a/src/stm32f411/dma.hpp b/src/stm32f411/dma.hpp deleted file mode 100644 index 032db76..0000000 --- a/src/stm32f411/dma.hpp +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace hal::stm32f411 { -enum class dma_transfer_type : std::uint8_t -{ - peripheral_to_memory = 0b00U, - memory_to_peripheral = 0b01U, - memory_to_memory = 0b10U -}; -enum class dma_transfer_size : std::uint8_t -{ - byte = 0b00U, - half_word = 0b01U, - word = 0b10U -}; - -enum class dma_burst_size : std::uint8_t -{ - single_transfer = 0b00U, - transfer_4_beats = 0b01U, - transfer_8_beats = 0b10U, - transfer_16_beats = 0b11U, -}; - -enum class dma_priority_level : std::uint8_t -{ - low = 0b00U, - medium = 0b01U, - high = 0b10U, - very_high = 0b11U, -}; - -enum class dma_flow_controller : u8 -{ - dma_controls_flow = 0b0U, - peripheral_controls_flow = 0b1U, -}; - -struct dma_channel_stream_t -{ - uint8_t stream; - uint8_t channel; -}; -/// DMA register map -struct stream_config_t -{ - /// Offset 0x00: This register is used to configure the concerned stream - std::uint32_t volatile configure; - /// Offset 0x04: Number of data items to be transferred (0 up to 65535). This - /// register can be written only when the stream is disabled - std::uint32_t volatile transfer_count; - /// Offset 0x08: Base address of the peripheral data register from/to which - /// the data will be read/written. These bits are write-protected and can be - /// written only when bit EN = '0' in the DMA_SxCR register. - std::uintptr_t volatile peripheral_address; - /// Offset 0x0C: Base address of Memory area 0 from/to which the data will be - /// read/written - std::uintptr_t volatile memory_0_address; - /// Offset 0x10: Base address of Memory area 1 from/to which the data will be - /// read/written - std::uintptr_t volatile memory_1_address; - /// Offset 0x14: fifo_control - std::uint32_t volatile fifo_control; -}; -struct dma_config_t -{ - /// Offset 0x00: low interrupt status register - std::uint32_t volatile interupt_status_low; - /// Offset 0x04: high interrupt status register - std::uint32_t volatile interupt_status_high; - /// Offset 0x08: low interrupt flag clear register - std::uint32_t volatile interupt_status_clear_low; - /// Offset 0x0C: low interrupt flag clear register - std::uint32_t volatile interupt_status_clear_high; - std::array stream; -}; -struct dma_stream_config -{ - static constexpr auto stream_enable = bit_mask::from<0>(); - static constexpr auto direct_mode_error_interupt_enable = bit_mask::from<1>(); - static constexpr auto transfer_error_interupt_enable = bit_mask::from<2>(); - static constexpr auto half_transfer_interupt_enable = bit_mask::from<3>(); - static constexpr auto transfer_complete_interupt_enable = bit_mask::from<4>(); - /// 0: DMA is the flow controller - /// 1: peripheral is the flow controller - static constexpr auto peripheral_flow_controller = bit_mask::from<5>(); - /// 00: Peripheral-to-memory - /// 01: Memory-to-peripheral - /// 10: Memory-to-memory - static constexpr auto data_transfer_direction = bit_mask::from<7, 6>(); - static constexpr auto circular_mode_enable = bit_mask::from<8>(); - /// 0: peripheral address pointer is fixed - /// 1: peripheral address pointer is incremented after each data transfer (set - /// by peripheral_data_size) - static constexpr auto peripheral_increment_mode = bit_mask::from<9>(); - /// 0: memory address pointer is fixed - /// 1: memory address pointer is incremented after each data transfer (set by - /// memory_data_size) - static constexpr auto memory_increment_mode = bit_mask::from<10>(); - /// 00: byte (8-bit) - /// 01: half-word (16-bit) - /// 10: word (32-bit) - static constexpr auto peripheral_data_size = bit_mask::from<12, 11>(); - /// 00: byte (8-bit) - /// 01: half-word (16-bit) - /// 10: word (32-bit) - static constexpr auto memory_data_size = bit_mask::from<14, 13>(); - /// 0: The offset size for the peripheral address calculation is linked to the - /// PSIZE 1: The offset size for the peripheral address calculation is fixed - /// to 4 (32-bit alignment). - static constexpr auto peripheral_offset_size = bit_mask::from<15>(); - /// 00: Low - /// 01: Medium - /// 10: High - /// 11: Very high - static constexpr auto priority_level = bit_mask::from<17, 16>(); - /// 0: No buffer switching at the end of transfer - /// 1: Memory target switched at the end of the DMA transfer - static constexpr auto double_buffer_mode = bit_mask::from<18>(); - /// 0: The current target memory is Memory 0 (addressed by the DMA_SxM0AR - /// pointer) 1: The current target memory is Memory 1 (addressed by the - /// DMA_SxM1AR pointer) - static constexpr auto current_target = bit_mask::from<19>(); - /// 00: single transfer - /// 01: INCR4 (incremental burst of 4 beats) - /// 10: INCR8 (incremental burst of 8 beats) - /// 11: INCR16 (incremental burst of 16 beats) - static constexpr auto peripheral_burst_transfer = bit_mask::from<22, 21>(); - /// 00: single transfer - /// 01: INCR4 (incremental burst of 4 beats) - /// 10: INCR8 (incremental burst of 8 beats) - /// 11: INCR16 (incremental burst of 16 beats) - static constexpr auto memory_burst_transfer = bit_mask::from<24, 23>(); - /// Refer to Table 27(DMA 1) or 28(DMA 2) of the user manual for channel - /// selection - static constexpr auto channel_select = bit_mask::from<27, 25>(); -}; -struct dma_fifo_config -{ - static constexpr auto direct_mode_disable = bit_mask::from<2>(); - static constexpr auto threshold_select = bit_mask::from<1, 0>(); -}; -inline constexpr intptr_t ahb_base = 0x4002'0000UL; -inline constexpr intptr_t dma_base = ahb_base + 0x6000; -static inline dma_config_t* get_dma_reg(peripheral p_dma) -{ - auto const dma_index = - static_cast(hal::value(p_dma) - hal::value(peripheral::dma1)); - - // STM has dedicated memory blocks where every 2^10 is a new DMA register - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(dma_base + (dma_index << 10)); -} - -struct dma_settings_t -{ - void const volatile* source; - void volatile* destination; - - size_t transfer_length; - dma_flow_controller flow_controller; - dma_transfer_type transfer_type; - bool circular_mode; - bool peripheral_address_increment; - bool memory_address_increment; - dma_transfer_size peripheral_data_size; - dma_transfer_size memory_data_size; - dma_priority_level priority_level; - bool double_buffer_mode; - bool current_target; - dma_burst_size peripheral_burst_size; - dma_burst_size memory_burst_size; -}; - -/** - * @brief Setup and start a dma transfer - * - * @param p_configuration - dma configuration - * @param p_interrupt_callback - callback when dma has completed - */ - -dma_channel_stream_t setup_dma_transfer( - peripheral p_dma, - std::span const& p_possible_streams, - dma_settings_t const& p_configuration, - hal::callback p_interrupt_callback); - -} // namespace hal::stm32f411 diff --git a/src/stm32f411/flash_reg.hpp b/src/stm32f411/flash_reg.hpp deleted file mode 100644 index c3f241c..0000000 --- a/src/stm32f411/flash_reg.hpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -namespace hal::stm32f411 { -/// flash register map -struct flash_config_t -{ - /// Offset 0x00: The Flash access control register is used to enable/disable - /// the acceleration features and control the Flash memory access time - /// according to CPU frequency. - u32 volatile access_control_reg; - /// Offset 0x04: The Flash key register is used to allow access to the Flash - /// control register and so, to allow program and erase operations. - u32 volatile key_reg; - /// Offset 0x08: The Flash option key register is used to allow program and - /// erase operations in the user configuration sector. - u32 volatile option_key_reg; - /// Offset 0x0C: The Flash status register gives information on ongoing - /// program and erase operations. - u32 volatile status_reg; - /// Offset 0x10: The Flash control register is used to configure and start - /// Flash memory operations. - u32 volatile control_reg; - /// Offset 0x14: The FLASH_OPTCR register is used to modify the user option - /// bytes - u32 volatile optional_control_reg; -}; - -struct flash_acess_control -{ - static constexpr auto latency = bit_mask::from<3, 0>(); - static constexpr auto prefetch_cache_en = bit_mask::from<8>(); - static constexpr auto instruction_cache_en = bit_mask::from<9>(); - static constexpr auto data_cache_en = bit_mask::from<10>(); - static constexpr auto instruction_cache_reset = bit_mask::from<11>(); - static constexpr auto data_cache_reset = bit_mask::from<12>(); -}; - -inline auto* flash_config = reinterpret_cast(0x4002'3C00); -} // namespace hal::stm32f411 diff --git a/src/stm32f411/gpio_reg.hpp b/src/stm32f411/gpio_reg.hpp deleted file mode 100644 index 4370309..0000000 --- a/src/stm32f411/gpio_reg.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include -#include - -namespace hal::stm32f411 { -/// gpio peripheral register map -struct gpio_config_t -{ - /// Offset: 0x000 pin mode (00 = Input, 01 = Output, 10 = Alternate Function - /// mode, 11 Analog) (R/W) - u32 volatile pin_mode; - /// Offset: 0x004 output type(0 = output push-pull, 1 = output open-drain) - u32 volatile output_type; - /// Offset: 0x008 output speed(00 = Low speed, 01 = Medium speed, 10 = Fast - /// speed, 11 = High speed) - u32 volatile output_speed; - /// Offset: 0x00C port pull-up/pull-down (00 = no pull-up/pull-down, 01 = - /// pull-up, 10 pull-down) - u32 volatile pull_up_pull_down; - /// Offset: 0x010 port input data (RO) - u32 volatile input_data; - /// Offset: 0x014 port output data - u32 volatile output_data; - /// Offset: 0x018low port set (0 = no action, 1 = reset) - std::uint16_t volatile set; - /// Offset: 0x018high port reset (0 = no action, 1 = reset) - std::uint16_t volatile reset; - /// Offset: 0x01C config lock - u32 volatile lock; - /// Offset: 0x020 alternate function low (bits 0 - 7) - u32 volatile alt_function_low; - /// Offset: 0x024 alternate function high (bits 8 - 15) - u32 volatile alt_function_high; -}; - -inline constexpr intptr_t ahb_base = 0x4002'0000UL; - -static inline gpio_config_t* get_gpio_reg(hal::stm32f411::peripheral p_port) -{ - // STM has dedicated memory blocks where every 2^10 is a new gpio register - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(ahb_base + - (hal::value(p_port) << 10)); -} -} // namespace hal::stm32f411 diff --git a/src/stm32f411/i2c.cpp b/src/stm32f411/i2c.cpp deleted file mode 100644 index f8b0720..0000000 --- a/src/stm32f411/i2c.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estelland the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libhal-arm-mcu/interrupt.hpp" -#include "power.hpp" - -namespace hal::stm32f411 { -i2c_manager_impl::i2c_manager_impl(peripheral p_select) - : m_port(p_select) -{ - power(m_port).on(); -} - -i2c_manager_impl::~i2c_manager_impl() -{ - power(m_port).off(); -} -i2c_manager_impl::i2c i2c_manager_impl::acquire_i2c( - hal::i2c::settings const& p_settings) -{ - return { *this, p_settings }; -} - -i2c_manager_impl::i2c::i2c(i2c_manager_impl& p_manager, - i2c::settings const& p_settings) -{ - auto const i2c1 = reinterpret_cast(0x4000'5400); - auto const i2c2 = reinterpret_cast(0x4000'5800); - auto const i2c3 = reinterpret_cast(0x4000'5C00); - m_manager = &p_manager; - switch (p_manager.m_port) { - case peripheral::i2c1: { - m_i2c = stm32_generic::i2c(i2c1); - pin scl(peripheral::gpio_b, 6); - pin sda(peripheral::gpio_b, 7); - scl.function(pin::pin_function::alternate4) - .open_drain(true) - .resistor(pin_resistor::pull_up); - sda.function(pin::pin_function::alternate4) - .open_drain(true) - .resistor(pin_resistor::pull_up); - } break; - case peripheral::i2c2: { - m_i2c = stm32_generic::i2c(i2c2); - pin scl(peripheral::gpio_b, 10); - pin sda(peripheral::gpio_b, 11); - scl.function(pin::pin_function::alternate4) - .open_drain(true) - .resistor(hal::pin_resistor::pull_up); - sda.function(pin::pin_function::alternate4) - .open_drain(true) - .resistor(pin_resistor::pull_up); - } break; - case peripheral::i2c3: { - m_i2c = stm32_generic::i2c(i2c3); - pin scl(peripheral::gpio_a, 8); - pin sda(peripheral::gpio_c, 9); - scl.function(pin::pin_function::alternate4) - .open_drain(true) - .resistor(hal::pin_resistor::pull_up); - sda.function(pin::pin_function::alternate4) - .open_drain(true) - .resistor(pin_resistor::pull_up); - } break; - default: - hal::safe_throw(hal::operation_not_supported(this)); - } - i2c::driver_configure(p_settings); - stm32f411::initialize_interrupts(); - setup_interrupt(); -}; -void i2c_manager_impl::i2c::driver_transaction( - hal::byte p_address, - std::span p_data_out, - std::span p_data_in, - hal::function_ref p_timeout) -{ - m_i2c.transaction(p_address, p_data_out, p_data_in, p_timeout); -} - -i2c_manager_impl::i2c::~i2c() -{ - switch (m_manager->m_port) { - case peripheral::i2c1: - cortex_m::disable_interrupt(irq::i2c1_ev); - cortex_m::disable_interrupt(irq::i2c1_er); - break; - case peripheral::i2c2: - cortex_m::disable_interrupt(irq::i2c2_ev); - cortex_m::disable_interrupt(irq::i2c2_er); - break; - case peripheral::i2c3: - [[fallthrough]]; - default: - cortex_m::disable_interrupt(irq::i2c3_ev); - cortex_m::disable_interrupt(irq::i2c3_er); - break; - } - power(m_manager->m_port).off(); -} - -void i2c_manager_impl::i2c::driver_configure(settings const& p_settings) -{ - m_i2c.configure(p_settings, frequency(m_manager->m_port)); -} -void i2c_manager_impl::i2c::setup_interrupt() -{ - // Create a lambda to call the interrupt() method - auto event_isr = [this]() { m_i2c.handle_i2c_event(); }; - auto error_isr = [this]() { m_i2c.handle_i2c_error(); }; - - // A pointer to save the static_callable isr address to. - cortex_m::interrupt_pointer event_handler; - cortex_m::interrupt_pointer error_handler; - - switch (m_manager->m_port) { - case peripheral::i2c1: - event_handler = - static_callable(event_isr).get_handler(); - error_handler = - static_callable(error_isr).get_handler(); - // Enable interrupt service routine. - cortex_m::enable_interrupt(irq::i2c1_ev, event_handler); - cortex_m::enable_interrupt(irq::i2c1_er, error_handler); - break; - case peripheral::i2c2: - event_handler = - static_callable(event_isr).get_handler(); - error_handler = - static_callable(error_isr).get_handler(); - // Enable interrupt service routine. - cortex_m::enable_interrupt(irq::i2c2_ev, event_handler); - cortex_m::enable_interrupt(irq::i2c2_er, error_handler); - break; - case peripheral::i2c3: - [[fallthrough]]; - default: - event_handler = - static_callable(event_isr).get_handler(); - error_handler = - static_callable(error_isr).get_handler(); - // Enable interrupt service routine. - cortex_m::enable_interrupt(irq::i2c3_ev, event_handler); - cortex_m::enable_interrupt(irq::i2c3_er, error_handler); - break; - } -} -} // namespace hal::stm32f411 diff --git a/src/stm32f411/input_pin.cpp b/src/stm32f411/input_pin.cpp deleted file mode 100644 index c30611a..0000000 --- a/src/stm32f411/input_pin.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include "gpio_reg.hpp" - -namespace hal::stm32f411 { -input_pin::input_pin(hal::stm32f411::peripheral p_port, - std::uint8_t p_pin, - settings const& p_settings) - : m_port(p_port) - , m_pin(p_pin) -{ - input_pin::driver_configure(p_settings); -} - -void input_pin::driver_configure(settings const& p_settings) -{ - bit_mask pin_mode_mask = { .position = 2 * static_cast(m_pin), - .width = 2 }; - - bit_modify(get_gpio_reg(m_port)->pin_mode).clear(pin_mode_mask); - - pin(m_port, m_pin) - .function(pin::pin_function::input) - .open_drain(false) - .resistor(p_settings.resistor); -} - -bool input_pin::driver_level() -{ - bit_mask input_data_mask = { .position = static_cast(m_pin), - .width = 1 }; - auto pin_value = - bit_extract(input_data_mask, get_gpio_reg(m_port)->input_data); - return static_cast(pin_value); -} -} // namespace hal::stm32f411 diff --git a/src/stm32f411/interrupt.cpp b/src/stm32f411/interrupt.cpp deleted file mode 100644 index 9ebe5a1..0000000 --- a/src/stm32f411/interrupt.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include - -namespace hal::stm32f411 { -void initialize_interrupts() -{ - hal::cortex_m::initialize_interrupts(); -} -} // namespace hal::stm32f411 diff --git a/src/stm32f411/output_pin.cpp b/src/stm32f411/output_pin.cpp deleted file mode 100644 index 562adb1..0000000 --- a/src/stm32f411/output_pin.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include "gpio_reg.hpp" - -namespace hal::stm32f411 { - -output_pin::output_pin(hal::stm32f411::peripheral p_port, - std::uint8_t p_pin, - output_pin::settings p_settings) - : m_port(p_port) - , m_pin(p_pin) -{ - output_pin::driver_configure(p_settings); -} - -void output_pin::driver_configure(settings const& p_settings) -{ - bit_mask pin_mode_mask = { .position = 2 * static_cast(m_pin), - .width = 2 }; - bit_modify(get_gpio_reg(m_port)->pin_mode).insert(pin_mode_mask, 0b01U); - - pin(m_port, m_pin) - .function(pin::pin_function::output) - .open_drain(p_settings.open_drain) - .resistor(p_settings.resistor); -} - -void output_pin::driver_level(bool p_high) -{ - bit_mask set_bit = { .position = static_cast(m_pin), .width = 1 }; - if (p_high) { - get_gpio_reg(m_port)->set = bit_value(0U).set(set_bit).to(); - } else { - get_gpio_reg(m_port)->reset = - bit_value(0U).set(set_bit).to(); - } -} - -bool output_pin::driver_level() -{ - bit_mask output_data_mask = { .position = static_cast(m_pin), - .width = 1 }; - return bit_extract(output_data_mask, get_gpio_reg(m_port)->output_data); -} -} // namespace hal::stm32f411 diff --git a/src/stm32f411/pin.cpp b/src/stm32f411/pin.cpp deleted file mode 100644 index 295d713..0000000 --- a/src/stm32f411/pin.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include -#include -#include -#include - -#include "gpio_reg.hpp" -#include "power.hpp" -#include "rcc_reg.hpp" - -namespace hal::stm32f411 { - -pin::pin(peripheral p_port, std::uint8_t p_pin) noexcept - : m_port(p_port) - , m_pin(p_pin) -{ - power(p_port).on(); -} - -pin const& pin::function(pin_function p_function) const noexcept -{ - auto port_reg = get_gpio_reg(m_port); - bit_mask pin_mode_mask = { .position = static_cast(m_pin) * 2U, - .width = 2 }; - - switch (p_function) { - case pin_function::input: - bit_modify(port_reg->pin_mode).insert(pin_mode_mask, 0b00U); - break; - case pin_function::output: - bit_modify(port_reg->pin_mode).insert(pin_mode_mask, 0b01U); - break; - case pin_function::analog: - bit_modify(port_reg->pin_mode).insert(pin_mode_mask, 0b11U); - break; - default: - bit_modify(port_reg->pin_mode).insert(pin_mode_mask, 0b10U); - uint8_t alt_func = static_cast(p_function) - 3U; - bit_mask alt_func_mask = { .position = - (static_cast(m_pin) * 4U) % 32, - .width = 4 }; - if (m_pin < 8) { - bit_modify(port_reg->alt_function_low).insert(alt_func_mask, alt_func); - } else { - bit_modify(port_reg->alt_function_high).insert(alt_func_mask, alt_func); - } - break; - } - return *this; -} - -pin const& pin::resistor(hal::pin_resistor p_resistor) const noexcept -{ - // modify the pull_up_pull_down reg to the enumclass of p_registor - auto port_reg = get_gpio_reg(m_port); - bit_mask port_mask = { .position = 2 * static_cast(m_pin), - .width = 2 }; - switch (p_resistor) { - case pin_resistor::none: - hal::bit_modify(port_reg->pull_up_pull_down).insert(port_mask, 0b00U); - break; - case pin_resistor::pull_up: - hal::bit_modify(port_reg->pull_up_pull_down).insert(port_mask, 0b01U); - break; - case pin_resistor::pull_down: - hal::bit_modify(port_reg->pull_up_pull_down).insert(port_mask, 0b10U); - break; - default: - hal::bit_modify(port_reg->pull_up_pull_down).insert(port_mask, 0b00U); - break; - } - return *this; -} - -pin const& pin::open_drain(bool p_enable) const noexcept -{ - // modify output_type to p_enable - auto port_reg = get_gpio_reg(m_port); - bit_mask pin_mask = { .position = static_cast(m_pin), .width = 1 }; - - bit_modify(port_reg->output_type) - .insert(pin_mask, static_cast(p_enable)); - return *this; -} - -void pin::activate_mco_pc9(mco_source p_source) -{ - bit_modify(rcc->config) - .insert(rcc_config::mco2_clock_select, value(p_source)); - bit_modify(rcc->config).insert(rcc_config::mco2_prescaler, 0b110U); - pin a8(peripheral::gpio_c, 9); - a8.function(pin_function::alternate0); - - return; -} - -} // namespace hal::stm32f411 diff --git a/src/stm32f411/power.cpp b/src/stm32f411/power.cpp deleted file mode 100644 index eb27e4e..0000000 --- a/src/stm32f411/power.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include - -#include "power.hpp" -#include "rcc_reg.hpp" - -namespace hal::stm32f411 { -namespace { -uint32_t volatile* enable(uint32_t p_bus_index) -{ - switch (p_bus_index) { - case 0: - return &rcc->ahb1enr; - case 1: - return &rcc->ahb2enr; - case 2: - [[fallthrough]]; - case 3: - return &rcc->apb1enr; - case 4: - return &rcc->apb2enr; - case 5: - [[fallthrough]]; - default: - return nullptr; - } -} -} // namespace - -power::power(peripheral p_peripheral) -{ - auto peripheral_value = hal::value(p_peripheral); - auto bus_number = peripheral_value / bus_id_offset; - - m_bit_position = static_cast(peripheral_value % bus_id_offset); - m_enable_register = enable(static_cast(bus_number)); -} - -void power::on() -{ - if (m_enable_register) { - hal::bit_modify(*m_enable_register).set(bit_mask::from(m_bit_position)); - } -} - -bool power::is_on() -{ - if (m_enable_register) { - return hal::bit_extract(bit_mask::from(m_bit_position), *m_enable_register); - } - return true; -} - -void power::off() -{ - if (m_enable_register) { - hal::bit_modify(*m_enable_register).clear(bit_mask::from(m_bit_position)); - } -} -} // namespace hal::stm32f411 diff --git a/src/stm32f411/power.hpp b/src/stm32f411/power.hpp deleted file mode 100644 index fb22122..0000000 --- a/src/stm32f411/power.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include - -namespace hal::stm32f411 { - -/** - * @brief Power control for stm32f1xx peripherals - * - */ -class power -{ -public: - /** - * @brief Construct a new power control object - * - * @param p_peripheral - id of the peripheral to configure - */ - power(peripheral p_peripheral); - - /** - * @brief Power on the peripheral - * - */ - void on(); - - /** - * @brief Check if the peripheral is powered on - * - * @return true - peripheral is on - * @return false - peripheral is off - */ - [[nodiscard]] bool is_on(); - - /** - * @brief Power off peripheral - * - */ - void off(); - -private: - std::uint32_t volatile* m_enable_register = nullptr; - std::uint8_t m_bit_position = 0; -}; -} // namespace hal::stm32f411 diff --git a/src/stm32f411/rcc_reg.hpp b/src/stm32f411/rcc_reg.hpp deleted file mode 100644 index abf0b99..0000000 --- a/src/stm32f411/rcc_reg.hpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include - -namespace hal::stm32f411 { -struct reset_and_clock_control_t -{ - /// Offset: 0x00 Clock Control Register - std::uint32_t volatile cr; - /// Offset: 0x04 PLL config register - std::uint32_t volatile pllconfig; - /// Offset: 0x08 clock config register - std::uint32_t volatile config; - /// Offset: 0x0c clock interrupt register - std::uint32_t volatile cir; - /// Offset: 0x10 ahb1 peripheral reset register - std::uint32_t volatile ahb1rstr; - /// Offset: 0x14 ahb2 peripheral reset register - std::uint32_t volatile ahb2rstr; - - std::array volatile reserved0; - - /// Offset: 0x20 ahb1 peripheral reset register - std::uint32_t volatile apb1rstr; - /// Offset: 0x24 ahb2 peripheral reset register - std::uint32_t volatile apb2rstr; - - std::array volatile reserved1; - - /// Offset: 0x30 ahb1 clock enable register - std::uint32_t volatile ahb1enr; - /// Offset: 0x34 ahb1 clock enable register - std::uint32_t volatile ahb2enr; - - std::array volatile reserved2; - - /// Offset: 0x40 apb1 clock enable register - std::uint32_t volatile apb1enr; - /// Offset: 0x44 apb1 clock enable register - std::uint32_t volatile apb2enr; - - std::array volatile reserved3; - - /// Offset: 0x50 ahb1 peripheral clock enable in low power mode register - std::uint32_t volatile ahb1lpenr; - /// Offset: 0x54 ahb2 peripheral clock enable in low power mode register - std::uint32_t volatile ahb2lpenr; - - std::array volatile reserved4; - - /// Offset: 0x60 apb1 peripheral clock enable in low power mode register - std::uint32_t volatile apb1lpenr; - /// Offset: 0x64 apb2 peripheral clock enable in low power mode register - std::uint32_t volatile apb2lpenr; - - std::array volatile reserved5; - - /// Offset: 0x70 backup domain control register - std::uint32_t volatile backup_domain_control; - /// Offset: 0x74 control and status register - std::uint32_t volatile csr; - - std::array volatile reserved6; - - /// Offset: 0x80 spread spectrum clock generation register - std::uint32_t volatile sscgr; - /// Offset: 0x84 plli2s config register - std::uint32_t volatile plli2scfgr; - - std::uint32_t volatile reserved7; - - /// Offset 0x8c dedicated clocks configuration register - std::uint32_t volatile dckcfgr; -}; - -struct rcc_cr -{ - /// Internal high-speed clock enable. 0: OFF, 1: ON - static constexpr auto high_speed_internal_enable = bit_mask::from<0>(); - - /// Internal high-speed clock ready flag. 0: not ready, 1: ready - static constexpr auto high_speed_internal_ready = bit_mask::from<1>(); - - /// These bits provide an additional user-programmable trimming value that is - /// added to the HSICAL[7:0] bits. - static constexpr auto high_speed_internal_trim = bit_mask::from<7, 3>(); - - /// Internal high-speed clock calibration - static constexpr auto high_speed_internal_calibration = - bit_mask::from<15, 8>(); - - /// External high-speed clock enable. 0: OFF, 1: ON - static constexpr auto high_speed_external_enable = bit_mask::from<16>(); - - /// External high-speed clock ready flag. 0: not ready, 1: ready - static constexpr auto high_speed_external_ready = bit_mask::from<17>(); - - /// External high-speed clock bypass. 0: HSE oscillator not bypassed, 1: HSE - /// oscillator bypassed with an external clock - static constexpr auto high_speed_external_bypass = bit_mask::from<18>(); - - /// Clock security system enable - /// 0: Clock security system OFF (Clock detector OFF) - /// 1: Clock security system ON (Clock detector ON if HSE oscillator is - /// stable, OFF if not) - static constexpr auto clock_security_system_enable = bit_mask::from<19>(); - - /// 0: PLL OFF - /// 1: PLL ON - static constexpr auto main_pll_enable = bit_mask::from<24>(); - - /// 0: PLL unlocked - /// 1: PLL locked - static constexpr auto main_pll_ready = bit_mask::from<25>(); - - /// 0: PLLI2S OFF - /// 1: PLLI2S ON - static constexpr auto pll_i2s_enable = bit_mask::from<26>(); - - /// 0: PLLI2S unlocked - /// 1: PLLI2S locked - static constexpr auto pll_i2s_ready = bit_mask::from<27>(); -}; - -struct rcc_pllcnfg -{ - /// PLL Input Source divider. Must be 1 <= X <= 63 - /// VCO input = input / division_factor. VCO inp must be 1_MHz <= VCO <= 2_MHz - static constexpr auto input_division_factor = bit_mask::from<5, 0>(); - - /// Main PLL output multiplication factor. Must be 50 <= X <= 432 - /// VCO output = output * multiplication_factor. VCO out must be 100_MHz <= - /// VCO <= 432_MHz - static constexpr auto main_multiplication_factor = bit_mask::from<14, 6>(); - - /// Main PLL output division factor. division factor = 2 + 2X - /// Must not exceed 100_MHz - static constexpr auto main_division_factor = bit_mask::from<17, 16>(); - - /// PLL Input Source - static constexpr auto pll_source = bit_mask::from<22>(); - - /// PLL division factor for USB OTG FS, and SDIO clocks - /// USB OTG FS requires a 48 MHz clock to work - /// SDIO need a frequency lower than or equal to 48 MHz to work correctly - static constexpr auto usb_sdio_dividor = bit_mask::from<16>(); -}; - -struct rcc_config -{ - /// System clock switch - /// 00: HSI oscillator selected as system clock - /// 01: HSE oscillator selected as system clock - /// 10: PLL selected as system clock - /// 11: not allowed - static constexpr auto system_clock_switch = bit_mask::from<1, 0>(); - - /// System clock switch status - /// 00: HSI oscillator used as the system clock - /// 01: HSE oscillator used as the system clock - /// 10: PLL used as the system clock - /// 11: not applicable - static constexpr auto system_clock_switch_status = bit_mask::from<3, 2>(); - - /// AHB prescaler - /// if less than 7, not divided - /// else: (system clock frequency)/2**(n-7) - static constexpr auto ahb_prescalar = bit_mask::from<7, 4>(); - - /// APB1 prescaler (apb clock never exceeds 50MHz) - static constexpr auto apb1_prescalar = bit_mask::from<12, 10>(); - - /// APB1 prescaler (apb clock never exceeds 100MHz) - static constexpr auto apb2_prescalar = bit_mask::from<15, 13>(); - - /// HSE division factor for RTC clock - /// (1MHz must be inputed in) - /// RTC = HSE/n - static constexpr auto hse_division_for_rtc_clock = bit_mask::from<20, 16>(); - - /// Microcontroller clock output 1 (MCO1) - /// 00: HSI clock selected - /// 01: LSE oscillator selected - /// 10: HSE oscillator clock selected - /// 11: PLL clock selected - static constexpr auto mco1_clock_select = bit_mask::from<22, 21>(); - - /// I2S clock selection - /// 0: PLLI2S clock used as I2S clock source - /// 1: External clock mapped on the I2S_CKIN pin used as I2S clock source - static constexpr auto i2s_clock_selection = bit_mask::from<23>(); - - /// MCO1 prescaler - /// if: n < 4, no division - /// else: division by (n & 3) + 2 - static constexpr auto mco1_prescaler = bit_mask::from<26, 24>(); - - /// MCO2 prescaler - /// if: n < 4, no division - /// else: division by (n & 3) + 2 - static constexpr auto mco2_prescaler = bit_mask::from<29, 27>(); - - /// Microcontroller clock output 1 (MCO2) - /// 00: System clock (SYSCLK) selected - /// 01: PLLI2S clock selected - /// 10: HSE oscillator clock selected - /// 11: PLL clock selected - static constexpr auto mco2_clock_select = bit_mask::from<31, 30>(); -}; - -struct rcc_backup_domain_control -{ - /// 0: LSE clock OFF - /// 1: LSE clock ON - static constexpr auto low_speed_external_enable = bit_mask::from<0>(); - - /// 0: LSE clock not ready - /// 1: LSE clock ready - static constexpr auto low_speed_external_ready = bit_mask::from<1>(); - - /// 0: LSE oscillator not bypassed - /// 1: LSE oscillator bypassed - static constexpr auto low_speed_external_bypass = bit_mask::from<2>(); - - /// 0: LSE oscillator β€œlow power” mode selection - /// 1: LSE oscillator β€œhigh drive” mode selection - static constexpr auto low_speed_external_mode = bit_mask::from<3>(); - - /// 00: No clock - /// 01: LSE oscillator clock used as the RTC clock - /// 10: LSI oscillator clock used as the RTC clock - /// 11: HSE oscillator clock divided by hse_division_for_rtc_clock - static constexpr auto rtc_clock_source = bit_mask::from<9, 8>(); - - /// 0: RTC clock disabled - /// 1: RTC clock enabled - static constexpr auto rtc_enable = bit_mask::from<15>(); - - /// 0: Reset not activated - /// 1: Resets the entire Backup domain - static constexpr auto backup_domain_software_reset = bit_mask::from<16>(); -}; - -struct rcc_ahb2 -{ - static constexpr auto usb_otg_en = bit_mask::from<7>(); -}; - -// NOLINTBEGIN(performance-no-int-to-ptr) -/** - * @return reset_and_clock_control_t& - return reset_and_clock_control_t - * register. - */ -inline auto* rcc = - reinterpret_cast(0x40000000 + 0x20000 + 0x3800); -// NOLINTEND(performance-no-int-to-ptr) -} // namespace hal::stm32f411 diff --git a/src/stm32f411/spi.cpp b/src/stm32f411/spi.cpp deleted file mode 100644 index 1e47630..0000000 --- a/src/stm32f411/spi.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include - -#include -#include -#include -#include - -#include "power.hpp" - -namespace hal::stm32f411 { -namespace { -void* peripheral_to_address(peripheral p_id) -{ - constexpr std::uintptr_t apb1_base = 0x4000'0000UL; - constexpr std::uintptr_t apb2_base = 0x4001'0000UL; - constexpr std::uintptr_t spi_reg1 = apb2_base + 0x3000; - constexpr std::uintptr_t spi_reg2 = apb1_base + 0x3800; - constexpr std::uintptr_t spi_reg3 = apb1_base + 0x3C00; - constexpr std::uintptr_t spi_reg4 = apb2_base + 0x3400; - constexpr std::uintptr_t spi_reg5 = apb2_base + 0x5000; - - switch (p_id) { - case peripheral::spi1: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(spi_reg1); - case peripheral::spi2: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(spi_reg2); - case peripheral::spi3: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(spi_reg3); - case peripheral::spi4: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(spi_reg4); - case peripheral::spi5: - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(spi_reg5); - default: - hal::safe_throw(hal::operation_not_supported(nullptr)); - } -} - -inline peripheral bus_number_to_peripheral(std::uint8_t p_bus_number) -{ - switch (p_bus_number) { - case 1: - return peripheral::spi1; - case 2: - return peripheral::spi2; - case 3: - return peripheral::spi3; - case 4: - return peripheral::spi4; - case 5: - return peripheral::spi5; - default: - hal::safe_throw(hal::operation_not_supported(nullptr)); - } -} -} // namespace - -spi::spi(hal::runtime, - std::uint8_t p_bus_number, - spi::settings const& p_settings) - : m_peripheral_id(bus_number_to_peripheral(p_bus_number)) - , m_spi_driver(hal::unsafe{}, peripheral_to_address(m_peripheral_id)) -{ - // Datasheet: Chapter 4: Pin definition Table 9 - switch (m_peripheral_id) { - case peripheral::spi1: { - pin clock(peripheral::gpio_a, 5); - pin data_in(peripheral::gpio_a, 7); - pin data_out(peripheral::gpio_a, 6); - - clock.function(pin::pin_function::alternate5) - .open_drain(false) - .resistor(pin_resistor::none); - data_in.function(pin::pin_function::alternate5) - .open_drain(false) - .resistor(pin_resistor::none); - data_out.function(pin::pin_function::alternate5) - .open_drain(false) - .resistor(pin_resistor::none); - break; - } - case peripheral::spi2: { - pin clock(peripheral::gpio_b, 10); - pin data_in(peripheral::gpio_b, 14); - pin data_out(peripheral::gpio_b, 15); - - clock.function(pin::pin_function::alternate5) - .open_drain(false) - .resistor(pin_resistor::none); - data_in.function(pin::pin_function::alternate5) - .open_drain(false) - .resistor(pin_resistor::none); - data_out.function(pin::pin_function::alternate5) - .open_drain(false) - .resistor(pin_resistor::none); - break; - } - case peripheral::spi3: { - pin clock(peripheral::gpio_b, 3); - pin data_in(peripheral::gpio_b, 5); - pin data_out(peripheral::gpio_b, 4); - - clock.function(pin::pin_function::alternate7) - .open_drain(false) - .resistor(pin_resistor::none); - data_in.function(pin::pin_function::alternate7) - .open_drain(false) - .resistor(pin_resistor::none); - data_out.function(pin::pin_function::alternate7) - .open_drain(false) - .resistor(pin_resistor::none); - break; - } - case peripheral::spi4: { - pin clock(peripheral::gpio_b, 0); - pin data_in(peripheral::gpio_b, 8); - pin data_out(peripheral::gpio_a, 12); - - clock.function(pin::pin_function::alternate6) - .open_drain(false) - .resistor(pin_resistor::none); - data_in.function(pin::pin_function::alternate6) - .open_drain(false) - .resistor(pin_resistor::none); - data_out.function(pin::pin_function::alternate6) - .open_drain(false) - .resistor(pin_resistor::none); - break; - } - case peripheral::spi5: { - pin clock(peripheral::gpio_b, 3); - pin data_in(peripheral::gpio_b, 5); - pin data_out(peripheral::gpio_b, 4); - - clock.function(pin::pin::pin_function::alternate6) - .open_drain(false) - .resistor(pin_resistor::none); - data_in.function(pin::pin::pin_function::alternate6) - .open_drain(false) - .resistor(pin_resistor::none); - data_out.function(pin::pin_function::alternate6) - .open_drain(false) - .resistor(pin_resistor::none); - break; - } - default: - // "Supported spi busses are 1-5!"; - hal::safe_throw(hal::operation_not_supported(this)); - } - - power(m_peripheral_id).on(); - spi::driver_configure(p_settings); -} - -spi::~spi() -{ - power(m_peripheral_id).off(); -} - -void spi::driver_configure(settings const& p_settings) -{ - using namespace hal::literals; - // TODO(#16): replace input clock with a get_frequency instruction - m_spi_driver.configure(p_settings, 16.0_MHz); -} - -void spi::driver_transfer(std::span p_data_out, - std::span p_data_in, - hal::byte p_filler) -{ - m_spi_driver.transfer(p_data_out, p_data_in, p_filler); -} -} // namespace hal::stm32f411 diff --git a/src/stm32f411/uart.cpp b/src/stm32f411/uart.cpp deleted file mode 100644 index 65b0751..0000000 --- a/src/stm32f411/uart.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include -#include -#include -#include -#include -#include - -#include "dma.hpp" -#include "power.hpp" - -namespace hal::stm32f411 { -namespace { -inline auto usart1 = reinterpret_cast(0x4001'1000); -inline auto usart2 = reinterpret_cast(0x4000'4400); -inline auto usart6 = reinterpret_cast(0x4001'1400); -} // namespace -uart::uart(hal::runtime, - std::uint8_t p_port, - std::span p_buffer, - serial::settings const& p_settings) - : uart(p_port, p_buffer, p_settings) -{ -} - -uart::uart(std::uint8_t p_port, - std::span p_buffer, - serial::settings const& p_settings) - : m_stm32_uart(usart1, p_buffer) - , m_dma(peripheral::dma2) - , m_id{} -{ - if (p_buffer.size() > max_dma_length) { - hal::safe_throw(hal::operation_not_supported(this)); - } - std::array possible_streams; - switch (p_port) { - case 1: - m_id = peripheral::usart1; - m_dma = peripheral::dma2; - m_stm32_uart = stm32_generic::uart(usart1, p_buffer); - { - pin tx(peripheral::gpio_a, 9); - pin rx(peripheral::gpio_a, 10); - tx.function(pin::pin_function::alternate7) - .open_drain(false) - .resistor(pin_resistor::none); - rx.function(pin::pin_function::alternate7) - .open_drain(false) - .resistor(pin_resistor::none); - } - possible_streams = { dma_channel_stream_t{ .stream = 2, .channel = 4 }, - dma_channel_stream_t{ .stream = 5, .channel = 4 } }; - break; - case 2: - m_dma = peripheral::dma1; - m_id = peripheral::usart2; - m_stm32_uart = stm32_generic::uart(usart2, p_buffer); - { - pin tx(peripheral::gpio_a, 2); - pin rx(peripheral::gpio_a, 3); - tx.function(pin::pin_function::alternate7) - .open_drain(false) - .resistor(pin_resistor::none); - rx.function(pin::pin_function::alternate7) - .open_drain(false) - .resistor(pin_resistor::none); - } - possible_streams = { dma_channel_stream_t{ .stream = 5, .channel = 4 }, - dma_channel_stream_t{ .stream = 7, .channel = 6 } }; - break; - case 6: - m_dma = peripheral::dma2; - m_id = peripheral::usart6; - m_stm32_uart = stm32_generic::uart(usart6, p_buffer); - { - pin tx(peripheral::gpio_a, 11); - pin rx(peripheral::gpio_a, 12); - tx.function(pin::pin_function::alternate8) - .open_drain(false) - .resistor(pin_resistor::none); - rx.function(pin::pin_function::alternate8) - .open_drain(false) - .resistor(pin_resistor::none); - } - possible_streams = { dma_channel_stream_t{ .stream = 2, .channel = 5 }, - dma_channel_stream_t{ .stream = 1, .channel = 5 } }; - break; - default: - hal::safe_throw(hal::operation_not_supported(this)); - } - // Power on the usart/uart id - power(m_id).on(); - - /// configure dma here - dma_settings_t dma_setting = { - .source = m_stm32_uart.data_register(), - .destination = p_buffer.data(), - - .transfer_length = p_buffer.size(), - .flow_controller = dma_flow_controller::dma_controls_flow, - .transfer_type = dma_transfer_type::peripheral_to_memory, - .circular_mode = true, - .peripheral_address_increment = false, - .memory_address_increment = true, - .peripheral_data_size = dma_transfer_size::byte, - .memory_data_size = dma_transfer_size::byte, - .priority_level = dma_priority_level::high, - .double_buffer_mode = false, - .current_target = false, - .peripheral_burst_size = dma_burst_size::single_transfer, - .memory_burst_size = dma_burst_size::single_transfer - }; - - m_dma_stream = - setup_dma_transfer(m_dma, possible_streams, dma_setting, []() {}).stream; - driver_configure(p_settings); -} - -std::uint32_t uart::dma_cursor_position() -{ - std::uint32_t receive_amount = - get_dma_reg(m_dma)->stream[m_dma_stream].transfer_count; - std::uint32_t write_position = m_stm32_uart.buffer_size() - receive_amount; - return write_position % m_stm32_uart.buffer_size(); -} - -void uart::driver_configure(serial::settings const& p_settings) -{ - m_stm32_uart.configure(p_settings, frequency(m_id)); -} - -serial::read_t uart::driver_read(std::span p_data) -{ - return m_stm32_uart.uart_read(p_data, dma_cursor_position()); -} -serial::write_t uart::driver_write(std::span p_data) -{ - return m_stm32_uart.uart_write(p_data); -} - -void uart::driver_flush() -{ - m_stm32_uart.flush(dma_cursor_position()); -} -} // namespace hal::stm32f411 diff --git a/src/system_controller.cpp b/src/system_controller.cpp deleted file mode 100644 index 3edeb16..0000000 --- a/src/system_controller.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "system_controller_reg.hpp" - -#include - -namespace hal::cortex_m { -void initialize_floating_point_unit() -{ - scb->cpacr = scb->cpacr | ((0b11 << 10 * 2) | /* set CP10 Full Access */ - (0b11 << 11 * 2)); /* set CP11 Full Access */ -} - -void set_interrupt_vector_table_address(void* p_table_location) -{ - // Relocate the interrupt vector table the vector buffer. By default this - // will be set to the address of the start of flash memory for the MCU. - scb->vtor = reinterpret_cast(p_table_location); -} - -void* get_interrupt_vector_table_address() -{ - // Relocate the interrupt vector table the vector buffer. By default this - // will be set to the address of the start of flash memory for the MCU. - return reinterpret_cast(scb->vtor); // NOLINT -} - -void reset() -{ - // Value "0x5FA" must be written to the VECTKEY field [31:16] to confirm - // that this action is valid, otherwise the processor ignores the write - // command. - // Bit 2 is the SYSRESETREQ bit. - scb->aircr = (0x5FA << 16) | (1 << 2); - // System reset is asynchronous, so the code needs to wait. - hal::halt(); -} - -void wait_for_interrupt() -{ -#if defined(__arm__) - asm volatile("wfi"); -#endif -} - -void wait_for_event() -{ -#if defined(__arm__) - asm volatile("wfe"); -#endif -} - -bool debugger_connected() -{ -#if defined(__thumb2__) - // CoreDebug->DHCSR register (Cortex-M3/M4/M7/etc.) - uint32_t volatile* dhcsr = reinterpret_cast(0xE000EDF0); - // Bit 0 (C_DEBUGEN) indicates debugger is connected - return (*dhcsr & 0x00000001) != 0; -#else - return false; -#endif -} -} // namespace hal::cortex_m - -extern "C" -{ - // The implementation of LLVM calls a calls the breakpoint instruction - // unconditionally, preventing the program from proceeding past this point - // without a debugger connected with semihosting enabled. In order to get - // around this, we replace sys_semihost and check if a debugger is connected. - // If it is connected we call the breakpoint instruction with the appropriate - // input value 0xAB. Otherwise, return an error code. - int sys_semihost([[maybe_unused]] int p_reason, [[maybe_unused]] void* p_arg) - { - if (hal::cortex_m::debugger_connected()) { -#if defined(__thumb2__) - // Let the real semihost call happen (BKPT will work) - // Need to inline the BKPT instruction here - register int r0 asm("r0") = p_reason; - register void* r1 asm("r1") = p_arg; - asm volatile("bkpt 0xAB" : "=r"(r0) : "r"(r0), "r"(r1) : "memory"); - return r0; -#endif - } - return -1; // No debugger, return error - } - - char* sys_semihost_get_cmdline() - { - if (hal::cortex_m::debugger_connected()) { - // SYS_GET_CMDLINE is semihost operation 0x15 - static char cmdline[256]; - int result = sys_semihost(0x15, cmdline); - if (result == 0) { - return cmdline; - } - } - static char empty[] = ""; - return empty; - } -} diff --git a/src/system_controller_reg.hpp b/src/system_controller_reg.hpp deleted file mode 100644 index a819947..0000000 --- a/src/system_controller_reg.hpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include - -namespace hal::cortex_m { -/// Structure type to access the System Control Block (SCB). -struct scb_registers_t -{ - /// Offset: 0x000 (R/ ) CPUID Base Register - uint32_t const volatile cpuid; - /// Offset: 0x004 (R/W) Interrupt Control and State Register - uint32_t volatile icsr; - /// Offset: 0x008 (R/W) Vector Table Offset Register - intptr_t volatile vtor; - /// Offset: 0x00C (R/W) Application Interrupt and Reset Control Register - uint32_t volatile aircr; - /// Offset: 0x010 (R/W) System Control Register - uint32_t volatile scr; - /// Offset: 0x014 (R/W) Configuration Control Register - uint32_t volatile ccr; - /// Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 5) - std::array shp; - /// Offset: 0x024 (R/W) System Handler Control and State Register - uint32_t volatile shcsr; - /// Offset: 0x028 (R/W) Configurable Fault Status Register - uint32_t volatile cfsr; - /// Offset: 0x02C (R/W) HardFault Status Register - uint32_t volatile hfsr; - /// Offset: 0x030 (R/W) Debug Fault Status Register - uint32_t volatile dfsr; - /// Offset: 0x034 (R/W) MemManage Fault Address Register - uint32_t volatile mmfar; - /// Offset: 0x038 (R/W) BusFault Address Register - uint32_t volatile bfar; - /// Offset: 0x03C (R/W) Auxiliary Fault Status Register - uint32_t volatile afsr; - /// Offset: 0x040 (R/ ) Processor Feature Register - std::array const pfr; - /// Offset: 0x048 (R/ ) Debug Feature Register - uint32_t const volatile dfr; - /// Offset: 0x04C (R/ ) Auxiliary Feature Register - uint32_t const volatile adr; - /// Offset: 0x050 (R/ ) Memory Model Feature Register - std::array const mmfr; - /// Offset: 0x060 (R/ ) Instruction Set Attributes Register - std::array const isar; - /// Reserved 0 - std::array reserved0; - /// Offset: 0x088 (R/W) Coprocessor Access Control Register - uint32_t volatile cpacr; -}; - -/// System control block address -inline constexpr auto scb_address = static_cast(0xE000'ED00UL); - -/// @return auto* - Address of the Cortex M system control block register -inline auto* scb = - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(scb_address); -} // namespace hal::cortex_m diff --git a/src/systick_timer.cpp b/src/systick_timer.cpp deleted file mode 100644 index 5529e57..0000000 --- a/src/systick_timer.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "systick_timer_reg.hpp" - -namespace hal::cortex_m { - -void start() -{ - hal::bit_modify(sys_tick->control) - .set(); -} - -void stop() -{ - hal::bit_modify(sys_tick->control) - .clear(); -} - -systick_timer::systick_timer(hertz p_frequency, clock_source p_source) - : m_frequency(p_frequency) -{ - if (not interrupt_vector_table_initialized()) { - hal::safe_throw(hal::operation_not_permitted(this)); - } - register_cpu_frequency(p_frequency, p_source); -} - -void systick_timer::register_cpu_frequency(hertz p_frequency, - clock_source p_source) -{ - stop(); - m_frequency = p_frequency; - - // Since reloads only occur when the current_value falls from 1 to 0, - // setting this register directly to zero from any other number will disable - // reloading of the register and will stop the timer. - sys_tick->current_value = 0; - - auto control = hal::bit_value(0); - control.set(); - - if (p_source == clock_source::processor) { - control.set(); - } else { - control.clear(); - } - - // Disable the counter if it was previously enabled. - control.clear(); - - sys_tick->control = control.get(); -} - -systick_timer::~systick_timer() -{ - stop(); - disable_interrupt(event_number); -} - -bool systick_timer::driver_is_running() -{ - auto running_bit = static_cast( - hal::bit_extract( - sys_tick->control)); - return running_bit; -} - -void systick_timer::driver_cancel() -{ - // All that is needed is to stop the timer. When the timer is started again - // via `schedule()`, the timer value will be reloaded/reset. - stop(); -} - -void systick_timer::driver_schedule(hal::callback p_callback, - hal::time_duration p_delay) -{ - constexpr std::int64_t maximum = 0x00FFFFFF; - - auto cycle_count = cycles_per(m_frequency, p_delay); - if (cycle_count <= 1) { - cycle_count = 1; - } else if (cycle_count > maximum) { - throw std::errc::invalid_argument; - } - - // Stop the previously scheduled event - stop(); - - // Save the p_callback to the static_callable object's statically allocated - // callback function. The lifetime of this object exists for the duration of - // the program, so this will never become a dangling reference. - auto handler = static_callable(p_callback); - - // Enable interrupt service routine for SysTick and use this callback as the - // handler - enable_interrupt(event_number, handler.get_handler()); - - sys_tick->current_value = 0; - sys_tick->reload = static_cast(cycle_count); - - // Starting the timer will restart the count - start(); -} -} // namespace hal::cortex_m diff --git a/src/systick_timer_reg.hpp b/src/systick_timer_reg.hpp deleted file mode 100644 index d00e0a1..0000000 --- a/src/systick_timer_reg.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2024 - 2025 Khalil Estell and the libhal contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include -#include - -namespace hal::cortex_m { -/// @brief Structure type to access the System Timer (SysTick). -struct systick_register_t -{ - /// Offset: 0x000 (R/W) SysTick Control and Status Register - uint32_t volatile control; - /// Offset: 0x004 (R/W) SysTick Reload Value Register - uint32_t volatile reload; - /// Offset: 0x008 (R/W) SysTick Current Value Register - /// NOTE: Setting this value to anything will zero it out. Setting this zero - /// will NOT cause the SysTick interrupt to be fired. - uint32_t volatile current_value; - /// Offset: 0x00C (R/ ) SysTick Calibration Register - uint32_t const volatile calib; -}; - -/// Namespace containing the bit_mask objects that are used to manipulate the -/// ARM Cortex Mx SysTick Timer. -namespace systick_control_register { -/// When set to 1, takes the contents of the reload counter, writes it to -/// the current_value register and begins counting down to zero. Setting -/// this to zero stops the counter. Restarting the counter will restart the -/// count. -static constexpr auto enable_counter = hal::bit_mask::from<0>(); - -/// When SysTick timer's count goes from 1 to 0, if this bit is set, the -/// SysTick interrupt will fire. -static constexpr auto enable_interrupt = hal::bit_mask::from<1>(); - -/// If set to 0, clock source is external, if set to 1, clock source follows -/// the processor clock. -static constexpr auto clock_source = hal::bit_mask::from<2>(); - -/// Set to 1 when count falls from 1 to 0. This bit is cleared on the next -/// read of this register. -static constexpr auto count_flag = hal::bit_mask::from<16>(); -}; // namespace systick_control_register - -/// The address of the sys_tick register -inline constexpr auto systick_address = static_cast(0xE000'E010UL); -/// The IRQ number for the SysTick interrupt vector -inline constexpr u16 event_number = 15; - -/// @return auto* - Address of the ARM Cortex SysTick peripheral -inline auto* sys_tick = - // NOLINTNEXTLINE(performance-no-int-to-ptr) - reinterpret_cast(systick_address); -} // namespace hal::cortex_m diff --git a/src/rp/time.cpp b/src/time.cpp similarity index 100% rename from src/rp/time.cpp rename to src/time.cpp diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index 407d27c..1348a2e 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -15,7 +15,7 @@ cmake_minimum_required(VERSION 3.15) project(test_package LANGUAGES CXX) -find_package(libhal-arm-mcu CONFIG REQUIRED) +find_package(libhal-picosdk CONFIG REQUIRED) add_executable(test_package main.cpp) -target_link_libraries(test_package PRIVATE libhal::arm-mcu) +target_link_libraries(test_package PRIVATE libhal::picosdk) diff --git a/test_package/conanfile.py b/test_package/conanfile.py index a7f8f16..6f499ea 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -23,6 +23,4 @@ class TestPackageConan(ConanFile): python_requires_extend = "libhal-bootstrap.library_test_package" def requirements(self): - self.requires(self.tested_reference_str, options={ - 'use_default_linker_script': False - }) + self.requires(self.tested_reference_str)