diff --git a/.github/workflows/build_cyphal.yml b/.github/workflows/build_cyphal.yml new file mode 100644 index 0000000..01fd41f --- /dev/null +++ b/.github/workflows/build_cyphal.yml @@ -0,0 +1,29 @@ +name: build_cyphal +on: [push] +jobs: + build_cyphal: + runs-on: ubuntu-22.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + fetch-depth: 0 + + - name: Install dependencies + run: ./scripts/tools/install_for_ubuntu.sh --yes + + - run: make generate_dsdl + - run: make cyphal + + - name: Archive .bin file + uses: actions/upload-artifact@v3 + with: + name: cyphal_firmware.bin + path: build/obj/example.bin + + - name: Archive .elf file + uses: actions/upload-artifact@v3 + with: + name: cyphal_firmware.elf + path: build/obj/example.elf diff --git a/.github/workflows/build_dronecan.yml b/.github/workflows/build_dronecan.yml new file mode 100644 index 0000000..60fe415 --- /dev/null +++ b/.github/workflows/build_dronecan.yml @@ -0,0 +1,27 @@ +name: build_dronecan +on: [push] +jobs: + build_dronecan: + runs-on: ubuntu-22.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.ACCESS_TOKEN }} + submodules: recursive + fetch-depth: 0 + + - uses: actions/checkout@v3 + with: + repository: RaccoonlabDev/libsqcan + path: 'Libs/libsqcan' + token: ${{ secrets.ACCESS_TOKEN }} + fetch-depth: 0 + + - name: Checkout libsqcan + run: cd Libs/libsqcan && git checkout dd10256 + + - name: Install dependencies + run: ./scripts/tools/install_for_ubuntu.sh --yes + + - run: make dronecan diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..abd8cd8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +build_dsdl/ +.vscode/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..802f5fd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "Libs/Cyphal"] + path = Libs/Cyphal + url = git@github.com:RaccoonlabDev/libcanard_stm32_cyphal.git +[submodule "Libs/libparams"] + path = Libs/libparams + url = git@github.com:PonomarevDA/libparams.git +[submodule "scripts/tools"] + path = scripts/tools + url = git@github.com:PonomarevDA/tools.git +[submodule "Libs/stm32-cube-project"] + path = Libs/stm32-cube-project + url = https://github.com/RaccoonLabHardware/mini-v2-software.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..74d3b07 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,134 @@ +cmake_minimum_required(VERSION 3.15.3) +project(example CXX C ASM) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(PLATFORM bxcan) +set(libparamsPath ${CMAKE_CURRENT_LIST_DIR}/Libs/libparams) +set(stm32cubeMxProjectPath ${CMAKE_CURRENT_LIST_DIR}/Libs/stm32-cube-project) + +if(USE_DRONECAN) + include(Libs/libsqcan/CMakeLists.txt) + set(libsSourceCode + ${libparamsPath}/libparams/rom.c + ${libparamsPath}/libparams/storage.c + ${libparamsPath}/platform_specific/stm32f103/flash_driver.c + ${dronecanSources} + ) + set(libsHeaders + ${libparamsPath}/libparams/ + ${libparamsPath}/platform_specific/stm32f103/ + ${dronecanHeaders} + ) + include(Src/dronecan_application/CMakeLists.txt) +else() + add_definitions(-DBXCAN_MAX_IFACE_INDEX=0) + include(Libs/Cyphal/CMakeLists.txt) + set(libsSourceCode ${CYPHAL_SRC}) + set(libsHeaders + Libs/Cyphal/Cyphal + Libs/Cyphal/Libs/libcanard/libcanard + ${libparamsPath}/libparams + Libs/Cyphal/Libs/o1heap/o1heap + build/nunavut_out + ) + set(cyphalRegisters ${CMAKE_CURRENT_LIST_DIR}/Libs/Cyphal/Cyphal/params.yaml) + include(Src/cyphal_application/CMakeLists.txt) + list(APPEND applicationSourceCode + build/src/params.cpp + build/src/string_params.cpp + ) +endif() + + +set(TOOLCHAIN_PREFIX arm-none-eabi-) +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) +set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) +set(CMAKE_CXX_STANDARD 20) + +FILE(GLOB coreSources ${stm32cubeMxProjectPath}/Core/Src/*) +FILE(GLOB driversSources ${stm32cubeMxProjectPath}/Drivers/STM32F1xx_HAL_Driver/Src/*.c*) + +set(gitRelatedHeaders + build/src +) + +set(stm32CubeMxGeneratedFiles + ${coreSources} + ${driversSources} + ${stm32cubeMxProjectPath}/startup_stm32f103xb.s +) +set(stm32CubeMxHeaders + ${stm32cubeMxProjectPath}/Core/Inc + ${stm32cubeMxProjectPath}/Drivers/CMSIS/Device/ST/STM32F1xx/Include + ${stm32cubeMxProjectPath}/Drivers/CMSIS/Include + ${stm32cubeMxProjectPath}/Drivers/STM32F1xx_HAL_Driver/Inc + ${stm32cubeMxProjectPath}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy +) + +set(EXECUTABLE ${PROJECT_NAME}.out) +add_executable(${EXECUTABLE} + ${libparams} + ${stm32CubeMxGeneratedFiles} + ${libsSourceCode} + ${applicationSourceCode} +) +target_compile_definitions(${EXECUTABLE} PRIVATE + -DUSE_HAL_DRIVER + -DSTM32F103xB +) + +include_directories(${libsHeaders}) + +target_include_directories(${EXECUTABLE} PRIVATE + ${gitRelatedHeaders} + ${stm32CubeMxHeaders} + ${applicationHeaders} +) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-volatile") + +target_compile_options(${EXECUTABLE} PRIVATE + -mcpu=cortex-m3 + -mthumb + -fdata-sections + -ffunction-sections + -lc -lm -lnosys + -specs=nano.specs + -Wall + --specs=nosys.specs +) + +target_link_options(${EXECUTABLE} PRIVATE + -T${stm32cubeMxProjectPath}/STM32F103T8Ux_FLASH.ld + -mcpu=cortex-m3 + -mthumb + --specs=nosys.specs + -specs=nano.specs + -lc + -lm + -lnosys + -Wl,-Map=${PROJECT_NAME}.map,--cref + -Wl,--gc-sections +) + +if(NOT USE_DRONECAN) + execute_process( + COMMAND ${CMAKE_CURRENT_LIST_DIR}/scripts/prebuild_cyphal.sh ${cyphalRegisters} + ) +endif() + +add_custom_command(TARGET ${EXECUTABLE} + POST_BUILD + COMMAND arm-none-eabi-size ${EXECUTABLE} +) + +add_custom_command(TARGET ${EXECUTABLE} + POST_BUILD + COMMAND arm-none-eabi-objcopy -O ihex ${EXECUTABLE} ${PROJECT_NAME}.hex + COMMAND arm-none-eabi-objcopy -O binary ${EXECUTABLE} ${PROJECT_NAME}.bin + COMMAND arm-none-eabi-objcopy -I binary -O elf32-little ${EXECUTABLE} ${PROJECT_NAME}.elf +) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Libs/Cyphal b/Libs/Cyphal new file mode 160000 index 0000000..b512c61 --- /dev/null +++ b/Libs/Cyphal @@ -0,0 +1 @@ +Subproject commit b512c61c5b647580b5e70e057e8c06468ed6ef15 diff --git a/Libs/libparams b/Libs/libparams new file mode 160000 index 0000000..92fe0ee --- /dev/null +++ b/Libs/libparams @@ -0,0 +1 @@ +Subproject commit 92fe0ee1dc11ddb52e98afc39e52a5013d2fe434 diff --git a/Libs/stm32-cube-project b/Libs/stm32-cube-project new file mode 160000 index 0000000..dd64a69 --- /dev/null +++ b/Libs/stm32-cube-project @@ -0,0 +1 @@ +Subproject commit dd64a6993f5364fb88b151d1e256a786f6c19fe1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7957763 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +# Copyright (c) 2022-2023 Dmitry Ponomarev +# Distributed under the MIT License, available in the file LICENSE. +# Author: Dmitry Ponomarev + + +BUILD_DIR:=build +BUILD_OBJ_DIR:=build/obj + +cyphal: clean autogenerate_git_related_headers + mkdir -p ${BUILD_OBJ_DIR} + cd ${BUILD_OBJ_DIR} && cmake ../.. && make + +dronecan: clean autogenerate_git_related_headers + mkdir -p ${BUILD_OBJ_DIR} + cd ${BUILD_OBJ_DIR} && cmake -DUSE_DRONECAN=1 ../.. && make + +upload: + ./scripts/tools/stm32/flash.sh ${BUILD_OBJ_DIR}/example.bin + +autogenerate_git_related_headers: + mkdir -p ${BUILD_DIR}/src + ./scripts/tools/stm32/generate_git_hash.sh ${BUILD_DIR}/src + ./scripts/tools/stm32/generate_software_version.sh ${BUILD_DIR}/src + +generate_dsdl: + mkdir -p ${BUILD_DIR}/nunavut_out + ./Libs/Cyphal/scripts/nnvg_generate_c_headers.sh + +clean: + -rm -fR ${BUILD_OBJ_DIR}/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e3034c --- /dev/null +++ b/README.md @@ -0,0 +1,120 @@ +# Mini v2 node custom application + +This repository introduces a simple Cyphal application written for the [RL Mini v2](https://docs.raccoonlab.co/guide/can_pwm/can_pwm_mini_v2.html) and [RL Micro](https://docs.raccoonlab.co/guide/can_pwm/can_pwm_micro.html) nodes. Based on this project you can write your custom application if the original CAN-PWM convertor application doesn't suit your requirements. + +| View | Top view | Bot view | +| ---- | --- | ------ | +| drawing | drawing | drawing| + +The default capabilities of the node are shown on the picture below: + +drawing + +## 1. Brief hardware description + +The node has 6 user pins. By default 4 of them are configured as PWM and 2 of them as UART RX, but you can change the configuration to support I2C, ADC, GPIO or something else. + +Please, refer to the [Mini v2 hardware](https://docs.raccoonlab.co/guide/can_pwm/can_pwm_mini_v2.html#pinout) page for the details. + +Below you can see a brief description of hardware capabilities: + +drawing + +## 2. STM32CubeMX + +The pinout configuration is based on the [STM32CubeMX](https://www.st.com/en/development-tools/stm32cubemx.html) generated project: [Libs/mini_v2](https://github.com/RaccoonLabHardware/mini_v2_ioc) with the following configuration: + +drawing + +If you need to use custom pinout configuration, it is recommended to use either [STM32CubeMX](https://www.st.com/en/development-tools/stm32cubemx.html) or [STM32CubeIDE](https://www.st.com/en/development-tools/stm32cubeide.html) to modify .ioc file and regenerate your custom project. + +## 3. Prerequisites + +You are expected to use the following software: +- (optional) [STM32CubeMX](https://www.st.com/en/development-tools/stm32cubemx.html) or [STM32CubeIDE](https://www.st.com/en/development-tools/stm32cubeide.html), +- [Yukon GUI](https://github.com/OpenCyphal-Garage/yukon) and [Yakut CLI](https://github.com/OpenCyphal/yakut) for Cyphal, +- [gui_tool](https://dronecan.github.io/GUI_Tool/Overview/) for DroneCAN. + +Hardware requirements: +- Mini v2 or Micro node +- STM32 programmer and CAN-sniffer (for example [RL sniffer and programmer](https://docs.raccoonlab.co/guide/programmer_sniffer/)) + +## 4. Usage + +The project is based on the CMake build system, but it is suggested to interract with [Makefile](Makefile). This is just a wrapper under CMake, useful for its target autocompletion. + +**Step 1. Clone the repository with submodules** + +```bash +git clone https://github.com/RaccoonlabDev/mini_v2_node --recursive +cd mini_v2_node +git submodule update --init --recursive +``` + +**Step 2. Connect Sniffer and Programmer to Mini v2 node.** + +An example of connection scheme suitable for bench test for Mini v2 node and RL Programmer-Sniffer is shown below: + +drawing + +You can also use other sniffer and programmers. For details refer to: [Programmer usage](https://docs.raccoonlab.co/guide/programmer_sniffer/programmer.html) and [Sniffer usage](https://docs.raccoonlab.co/guide/programmer_sniffer/sniffer.html#_4-1-cyphal-usage) pages. + +**Step 3. Build the project and upload the firmware** + +```bash +make generate_dsdl # you need to call it only before the first build +make cyphal +make upload # it works only with RaccoonLab sniffer-programmer yet +``` + +As a short form, you can build and upload the firmware with a single command: + +```bash +make cyphal upload +``` + +A few details about how the build process works: +- `make generate_dsdl` calls the script to generate C++ headers for Cyphal data types serialization using nunavut. The output goes into [build/compile_output](build/compile_output) and [build/nunavut_out](build/nunavut_out) folders. It is expected that you doesn't often change DSDL, so you typically need to call it only once. +- `make cyphal` before the actual build process generates a few files as well. Specifically, it generates: + - [build/src/git_software_version.h](build/src/git_software_version.h) that has info about the software version based on latest git tag, + - [build/src/git_hash.h](build/src/git_hash.h) with info about the current commit, + - C++ source and header files with parameters array and enums based on all associated yaml files with registers (you can find generated files in the same folder: [build/src](build/src)), + - [Src/cyphal_application/README.md](Src/cyphal_application/README.md) with info about the supported interface and not port-related registers. + +**Step 4. Setup the environment and run Yukon** + +Try the command below or use the official yukon/yakut instructions. + +```bash +source scripts/init.sh +~/Download/Yukon +``` + +Please, refer to the [Mini node docs](https://docs.raccoonlab.co/guide/can_pwm/can_pwm_cyphal.html). +It has a detailed steps about how to perform bench testing of the node. + +**Q&A** + +If you are strugguling with the software building, please refer to the build workflow [.github/workflows/build.yml](.github/workflows/build.yml) for a hint. If it doesn't help, you can open an issue. + +## 5. Customization + +The peripherals are initialised in [Libs/stm32-cube-project/Core/Src/main.c](https://github.com/RaccoonLabHardware/mini-v2-software/blob/main/Core/Src/main.c), which are automatically generated based on the configuratation file [can_pwm_v2.ioc](https://github.com/RaccoonLabHardware/mini-v2-software/blob/main/can_pwm_v2.ioc) file. If you want to use a different peripheral configuration, you should update can_pwm_v2.ioc with STM32CubeIDE or STM32CubeMX. + +The main application is started in [Src/cyphal_application/application.cpp](Src/cyphal_application/application.cpp). +By default it just blinks the RGB LED, subscribes to the setpoint topic to control PWM1 and publishes a feedback with the latest applied setpoint. Note, that the application is as simple as possible: it controls only a single PWM and doesn't have safety features like TTL, but you are free to extend it as you want. + +You may consider [Src/cyphal_application/setpoint](Src/cyphal_application/setpoint) and [Src/cyphal_application/feedback](Src/cyphal_application/feedback) as examples of Cyphal-subscriber and Cyphal-publisher. If you create subjects by inhereting from CyphalPublisher and CyphalSubscriber, it will automatically add all topics to the port.List array, so the node is able to advertise its capabilities. This approach doesn't automatically create registers, so you need to add them in yaml file as shown in the examples. + +When you add your custom module, don't forget to add source file and path to the file with registers to [Src/cyphal_application/CMakeLists.txt](Src/cyphal_application/CMakeLists.txt). + +You can also easily create custom Integer and String registers. An example is shown in [Src/cyphal_application/params.yaml](Src/cyphal_application/params.yaml). + +## 6. Reference + +The project has a few dependencies which are attached to the repository as submodules. They are: + +- [Libs/mini_v2](https://github.com/RaccoonLabHardware/mini_v2_ioc) is a project generated with the STM32CubeMX. It is based on .ioc file corresponded to the default firmware of the Mini v2 node. You may only need to change it if you want to use an a different peripheral configuration. +- [Libs/Cyphal](https://github.com/RaccoonlabDev/libcanard_cyphal_application) is a general-purpose application based on the [Cyphal libcanard](https://github.com/OpenCyphal/libcanard), [o1heap](https://github.com/pavel-kirienko/o1heap) and other libraries with minimal required features to start and some features related to UDRAL/DS015. +- [Src/libparams](https://github.com/PonomarevDA/libparams) is a simple library with ROM driver implementation that allows to store configuration parameters in persistent memory. +- [not yet] Src/libsqcan is a general-purpose application based on [DroneCAN libcanard](https://github.com/dronecan/libcanard) with minimal required features to start. The DroneCAN part is not publically released yet. diff --git a/Src/cyphal_application/CMakeLists.txt b/Src/cyphal_application/CMakeLists.txt new file mode 100644 index 0000000..9a29483 --- /dev/null +++ b/Src/cyphal_application/CMakeLists.txt @@ -0,0 +1,19 @@ +set(applicationSourceCode + Src/periphery/pwm/pwm.cpp + Src/periphery/led/led.cpp + + Src/cyphal_application/setpoint/setpoint.cpp + Src/cyphal_application/feedback/feedback.cpp + Src/cyphal_application/application.cpp +) +set(applicationHeaders + Src + Src/cyphal_application + Libs/Cyphal +) + +list(APPEND cyphalRegisters + ${CMAKE_CURRENT_LIST_DIR}/params.yaml + ${CMAKE_CURRENT_LIST_DIR}/setpoint/params.yaml + ${CMAKE_CURRENT_LIST_DIR}/feedback/params.yaml +) diff --git a/Src/cyphal_application/README.md b/Src/cyphal_application/README.md new file mode 100644 index 0000000..4c4a0e6 --- /dev/null +++ b/Src/cyphal_application/README.md @@ -0,0 +1,19 @@ +The node has the following interface: + +| № | Type | Message | Topic name | +| -- | ---- | ------- | ----------- | +| 1 | sub | reg.udral.service.actuator.common.sp.Vector31 | setpoint | {'type': 'Port', 'data_type': 'reg.udral.service.actuator.common.sp.Vector31', 'enum_base': 'PARAM_SUB_SETPOINT'}| +| 2 | pub | reg.udral.service.actuator.common.Feedback.0.1 | feedback | {'type': 'Port', 'data_type': 'reg.udral.service.actuator.common.Feedback.0.1', 'enum_base': 'PARAM_PUB_FEEDBACK_1'}| + +The node has the following registers: + +| № | Register name | Description | +| -- | ----------------------- | ----------- | +| 1 | uavcan.node.id | Defines a node-ID. Allowed values [0,127]. | +| 2 | system.name | Defines custom node name. If empty, the node will use the default name. | +| 3 | uavcan.node.description | User/integrator-defined, human-readable description of this specific node. | +| 4 | example.integer | Just an integer register example | +| 5 | example.string | Just a string register example | + +> This docs was automatically generated. Do not edit it manually. + diff --git a/Src/cyphal_application/application.cpp b/Src/cyphal_application/application.cpp new file mode 100644 index 0000000..6f65a4d --- /dev/null +++ b/Src/cyphal_application/application.cpp @@ -0,0 +1,46 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022-2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "application.hpp" +#include "cyphal.hpp" +#include "cyphal_registers.hpp" +#include "main.h" +#include "string_params.hpp" +#include "params.hpp" +#include "setpoint/setpoint.hpp" +#include "feedback/feedback.hpp" +#include "periphery/led/led.hpp" + +void init_persistent_storage() { + paramsInit(static_cast(IntParamsIndexes::INTEGER_PARAMS_AMOUNT), NUM_OF_STR_PARAMS); + paramsLoadFromFlash(); + + auto node_name_param_idx = static_cast(IntParamsIndexes::INTEGER_PARAMS_AMOUNT); + paramsSetStringValue(node_name_param_idx, 19, (const uint8_t*)"co.raccoonlab.mini"); +} + +void application_entry_point() { + LedPeriphery::reset(); + init_persistent_storage(); + cyphal::NodeGetInfoSubscriber::setHardwareVersion(2, 1); + + cyphal::Cyphal cyphal; + int init_res = cyphal.init(); + + SetpointSubscriber setpoint(&cyphal); + init_res |= setpoint.init(); + + FeedbackPublisher feedback(&cyphal); + init_res |= feedback.init(); + + while (true) { + auto led_color = (init_res >= 0) ? LedColor::BLUE_COLOR : LedColor::RED_COLOR; + LedPeriphery::toggle(led_color); + + cyphal.process(); + + auto crnt_time_ms = HAL_GetTick(); + feedback.process(crnt_time_ms); + } +} diff --git a/Src/cyphal_application/application.hpp b/Src/cyphal_application/application.hpp new file mode 100644 index 0000000..c5e47b0 --- /dev/null +++ b/Src/cyphal_application/application.hpp @@ -0,0 +1,18 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022-2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef SRC_APPLICATION_HPP_ +#define SRC_APPLICATION_HPP_ + +#ifdef __cplusplus +extern "C" { +#endif + +void application_entry_point(); + +#ifdef __cplusplus +} +#endif + +#endif // SRC_APPLICATION_HPP_ diff --git a/Src/cyphal_application/feedback/feedback.cpp b/Src/cyphal_application/feedback/feedback.cpp new file mode 100644 index 0000000..db07ba8 --- /dev/null +++ b/Src/cyphal_application/feedback/feedback.cpp @@ -0,0 +1,43 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "feedback.hpp" +#include +#include "reg/udral/service/actuator/common/Feedback_0_1.h" +#include "cyphal.hpp" +#include "params.hpp" +#include "periphery/pwm/pwm.hpp" + +int8_t FeedbackPublisher::init() { + return 0; +} + +void FeedbackPublisher::process(uint32_t crnt_time_ms) { + if (_prev_pub_ts_ms + 1000 > crnt_time_ms) { + return; + } + + publish_msg(crnt_time_ms); +} + +void FeedbackPublisher::publish_msg(uint32_t crnt_time_ms) { + _prev_pub_ts_ms = crnt_time_ms; + + reg_udral_service_actuator_common_Feedback_0_1 msg; + + msg.heartbeat.health.value = uavcan_node_Health_1_0_NOMINAL; + msg.heartbeat.readiness.value = reg_udral_service_common_Readiness_0_1_ENGAGED; + + uint32_t pwm_ccr_reg_value = PwmPeriphery::get_duration(PwmPin::PWM_1); + uint32_t pwm_duration = std::clamp(pwm_ccr_reg_value, 1000ul, 2000ul); + msg.demand_factor_pct = (pwm_duration - 1000) / 10; + setPortId(paramsGetIntegerValue(PARAM_PUB_FEEDBACK_1_ID)); + + uint8_t buffer[reg_udral_service_actuator_common_Feedback_0_1_EXTENT_BYTES_]; + size_t buffer_size = reg_udral_service_actuator_common_Feedback_0_1_EXTENT_BYTES_; + int32_t result = reg_udral_service_actuator_common_Feedback_0_1_serialize_(&msg, buffer, &buffer_size); + if (NUNAVUT_SUCCESS == result) { + push(buffer_size, buffer); + } +} diff --git a/Src/cyphal_application/feedback/feedback.hpp b/Src/cyphal_application/feedback/feedback.hpp new file mode 100644 index 0000000..560126e --- /dev/null +++ b/Src/cyphal_application/feedback/feedback.hpp @@ -0,0 +1,28 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022-2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef SRC_CYPHAL_APPLICATION_FEEDBACK_HPP_ +#define SRC_CYPHAL_APPLICATION_FEEDBACK_HPP_ + +#include "cyphal_publishers.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +class FeedbackPublisher: public cyphal::CyphalPublisher { +public: + FeedbackPublisher(cyphal::Cyphal* driver_) : CyphalPublisher(driver_, 0) {}; + int8_t init(); + void process(uint32_t crnt_time_ms); + void publish_msg(uint32_t crnt_time_ms); +private: + uint32_t _prev_pub_ts_ms = 0; +}; + +#ifdef __cplusplus +} +#endif + +#endif // SRC_CYPHAL_APPLICATION_FEEDBACK_HPP_ diff --git a/Src/cyphal_application/feedback/params.yaml b/Src/cyphal_application/feedback/params.yaml new file mode 100644 index 0000000..67e6c5d --- /dev/null +++ b/Src/cyphal_application/feedback/params.yaml @@ -0,0 +1,9 @@ +# This is a short form that generates Integer & String registers related to the subscriber with PORT_NAME=feedback: +# - Integer register with name `uavcan.pub.feedback.id` +# - String register with name `uavcan.pub.feedback.type` +# The generated registers have proper flags, data type and min, max and default values. +# They correspond the standard: https://github.com/OpenCyphal/public_regulated_data_types/blob/master/uavcan/register/384.Access.1.0.dsdl +uavcan.pub.feedback: + type: Port + data_type: reg.udral.service.actuator.common.Feedback.0.1 + enum_base: PARAM_PUB_FEEDBACK_1 diff --git a/Src/cyphal_application/params.yaml b/Src/cyphal_application/params.yaml new file mode 100644 index 0000000..976743d --- /dev/null +++ b/Src/cyphal_application/params.yaml @@ -0,0 +1,15 @@ +example.integer: + type: Integer + note: Just an integer register example + enum: PARAM_EXAMPLE_INTEGER + flags: mutable + default: 42 + min: 0 + max: 255 + +example.string: + type: String + note: Just a string register example + enum: PARAM_EXAMPLE_STRING + flags: mutable + default: "default_string_value" diff --git a/Src/cyphal_application/setpoint/params.yaml b/Src/cyphal_application/setpoint/params.yaml new file mode 100644 index 0000000..ea4f9e7 --- /dev/null +++ b/Src/cyphal_application/setpoint/params.yaml @@ -0,0 +1,9 @@ +# This is a short form that generates Integer & String registers related to the subscriber with PORT_NAME=setpoint: +# - Integer register with name `uavcan.pub.setpoint.id` +# - String register with name `uavcan.pub.setpoint.type` +# The generated registers have proper flags, data type and min, max and default values. +# They correspond the standard: https://github.com/OpenCyphal/public_regulated_data_types/blob/master/uavcan/register/384.Access.1.0.dsdl +uavcan.sub.setpoint: + type: Port + data_type: reg.udral.service.actuator.common.sp.Vector31 + enum_base: PARAM_SUB_SETPOINT diff --git a/Src/cyphal_application/setpoint/setpoint.cpp b/Src/cyphal_application/setpoint/setpoint.cpp new file mode 100644 index 0000000..7afc54e --- /dev/null +++ b/Src/cyphal_application/setpoint/setpoint.cpp @@ -0,0 +1,43 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "setpoint.hpp" +#include +#include "reg/udral/service/actuator/common/sp/Vector31_0_1.h" +#include "cyphal.hpp" +#include "params.hpp" +#include "periphery/pwm/pwm.hpp" + +SetpointSubscriber::SetpointSubscriber(cyphal::Cyphal* driver_) : + CyphalSubscriber(driver_, 0) { +}; + +int8_t SetpointSubscriber::init() { + PwmPeriphery::init(PwmPin::PWM_1); + PwmPeriphery::init(PwmPin::PWM_2); + PwmPeriphery::init(PwmPin::PWM_3); + PwmPeriphery::init(PwmPin::PWM_4); + + port_id = paramsGetIntegerValue(IntParamsIndexes::PARAM_SUB_SETPOINT_ID); + if (driver->subscribe(this, + reg_udral_service_actuator_common_sp_Vector31_0_1_EXTENT_BYTES_, + cyphal::CanardTransferKindMessage) < 0) { + return -1; + } + + return 0; +} + +void SetpointSubscriber::callback(const cyphal::CanardRxTransfer& transfer) { + const uint8_t* payload = static_cast(transfer.payload); + size_t payload_len = transfer.payload_size; + reg_udral_service_actuator_common_sp_Vector31_0_1 msg; + if (reg_udral_service_actuator_common_sp_Vector31_0_1_deserialize_(&msg, payload, &payload_len) < 0) { + return; + } + + float setpoint_clamped = std::clamp(msg.value[0], 0.0f, 1.0f); + uint32_t pwm_duration = setpoint_clamped * 1000 + 1000; + PwmPeriphery::set_duration(PwmPin::PWM_1, pwm_duration); +} diff --git a/Src/cyphal_application/setpoint/setpoint.hpp b/Src/cyphal_application/setpoint/setpoint.hpp new file mode 100644 index 0000000..7b669ee --- /dev/null +++ b/Src/cyphal_application/setpoint/setpoint.hpp @@ -0,0 +1,26 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022-2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef SRC_CYPHAL_APPLICATION_SETPOINT_SETPOINT_HPP_ +#define SRC_CYPHAL_APPLICATION_SETPOINT_SETPOINT_HPP_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cyphal_subscribers.hpp" + +class SetpointSubscriber: public cyphal::CyphalSubscriber { +public: + SetpointSubscriber(cyphal::Cyphal* driver); + int8_t init(); +private: + void callback(const cyphal::CanardRxTransfer& transfer) override; +}; + +#ifdef __cplusplus +} +#endif + +#endif // SRC_CYPHAL_APPLICATION_SETPOINT_SETPOINT_HPP_ diff --git a/Src/dronecan_application/CMakeLists.txt b/Src/dronecan_application/CMakeLists.txt new file mode 100644 index 0000000..9a38dfa --- /dev/null +++ b/Src/dronecan_application/CMakeLists.txt @@ -0,0 +1,11 @@ +set(applicationSourceCode + Src/periphery/pwm/pwm.cpp + Src/periphery/led/led.cpp + + Src/dronecan_application/application.cpp + Src/dronecan_application/params.cpp +) +set(applicationHeaders + Src + Src/dronecan_application +) diff --git a/Src/dronecan_application/application.cpp b/Src/dronecan_application/application.cpp new file mode 100644 index 0000000..94de966 --- /dev/null +++ b/Src/dronecan_application/application.cpp @@ -0,0 +1,31 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "application.hpp" +#include "uavcan.h" +#include "uavcan_application.h" +#include "main.h" +#include "params.hpp" +#include "string_params.hpp" +#include "storage.h" + +void application_entry_point() { + paramsInit(static_cast(IntParamsIndexes::INTEGER_PARAMS_AMOUNT), NUM_OF_STR_PARAMS); + paramsLoadFromFlash(); + + HAL_GPIO_WritePin(INTERNAL_LED_BLUE_GPIO_Port, INTERNAL_LED_BLUE_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(INTERNAL_LED_GREEN_GPIO_Port, INTERNAL_LED_GREEN_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(INTERNAL_LED_RED_GPIO_Port, INTERNAL_LED_RED_Pin, GPIO_PIN_SET); + + uavcanInitApplication(42); + + while(true) { + GPIO_PinState state = (HAL_GetTick() % 1000 > 500) ? GPIO_PIN_SET : GPIO_PIN_RESET; + HAL_GPIO_WritePin(INTERNAL_LED_BLUE_GPIO_Port, INTERNAL_LED_BLUE_Pin, state); + + canardSpinNodeStatus(); + uavcanProcessSending(); + uavcanProcessReceiving(); + } +} diff --git a/Src/dronecan_application/application.hpp b/Src/dronecan_application/application.hpp new file mode 100644 index 0000000..c5e47b0 --- /dev/null +++ b/Src/dronecan_application/application.hpp @@ -0,0 +1,18 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022-2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef SRC_APPLICATION_HPP_ +#define SRC_APPLICATION_HPP_ + +#ifdef __cplusplus +extern "C" { +#endif + +void application_entry_point(); + +#ifdef __cplusplus +} +#endif + +#endif // SRC_APPLICATION_HPP_ diff --git a/Src/dronecan_application/params.cpp b/Src/dronecan_application/params.cpp new file mode 100644 index 0000000..2686769 --- /dev/null +++ b/Src/dronecan_application/params.cpp @@ -0,0 +1,17 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "params.hpp" +#include "string_params.hpp" +#include "storage.h" + +IntegerDesc_t integer_desc_pool[] = { + {"id", 0, 127, 50}, +}; +IntegerParamValue_t integer_values_pool[sizeof(integer_desc_pool) / sizeof(IntegerDesc_t)]; + +StringDesc_t __attribute__((weak)) string_desc_pool[NUM_OF_STR_PARAMS] = { + {"name", "", false}, +}; +StringParamValue_t string_values_pool[sizeof(string_desc_pool) / sizeof(StringDesc_t)]; diff --git a/Src/dronecan_application/params.hpp b/Src/dronecan_application/params.hpp new file mode 100644 index 0000000..cf14573 --- /dev/null +++ b/Src/dronecan_application/params.hpp @@ -0,0 +1,16 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef PARAMS_HPP_ +#define PARAMS_HPP_ + +#include "storage.h" + +enum IntParamsIndexes : ParamIndex_t { + ID, + + INTEGER_PARAMS_AMOUNT +}; + +#endif // PARAMS_HPP_ diff --git a/Src/dronecan_application/string_params.hpp b/Src/dronecan_application/string_params.hpp new file mode 100644 index 0000000..d0fb53e --- /dev/null +++ b/Src/dronecan_application/string_params.hpp @@ -0,0 +1,6 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#pragma once +#define NUM_OF_STR_PARAMS 1 diff --git a/Src/periphery/led/led.cpp b/Src/periphery/led/led.cpp new file mode 100644 index 0000000..6dbd1f1 --- /dev/null +++ b/Src/periphery/led/led.cpp @@ -0,0 +1,41 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022-2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "led.hpp" +#include "main.h" + + +void LedPeriphery::reset() { + HAL_GPIO_WritePin(INTERNAL_LED_BLUE_GPIO_Port, INTERNAL_LED_BLUE_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(INTERNAL_LED_GREEN_GPIO_Port, INTERNAL_LED_GREEN_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(INTERNAL_LED_RED_GPIO_Port, INTERNAL_LED_RED_Pin, GPIO_PIN_SET); +} + +void LedPeriphery::toggle(LedColor led_color) { + auto crnt_time_ms = HAL_GetTick(); + GPIO_PinState state = (crnt_time_ms % 1000 > 500) ? GPIO_PIN_SET : GPIO_PIN_RESET; + + switch (led_color) { + case LedColor::RED_COLOR: + HAL_GPIO_WritePin(INTERNAL_LED_RED_GPIO_Port, INTERNAL_LED_RED_Pin, state); + HAL_GPIO_WritePin(INTERNAL_LED_GREEN_GPIO_Port, INTERNAL_LED_GREEN_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(INTERNAL_LED_BLUE_GPIO_Port, INTERNAL_LED_BLUE_Pin, GPIO_PIN_SET); + break; + + case LedColor::GREEN_COLOR: + HAL_GPIO_WritePin(INTERNAL_LED_RED_GPIO_Port, INTERNAL_LED_RED_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(INTERNAL_LED_GREEN_GPIO_Port, INTERNAL_LED_GREEN_Pin, state); + HAL_GPIO_WritePin(INTERNAL_LED_BLUE_GPIO_Port, INTERNAL_LED_BLUE_Pin, GPIO_PIN_SET); + break; + + case LedColor::BLUE_COLOR: + HAL_GPIO_WritePin(INTERNAL_LED_RED_GPIO_Port, INTERNAL_LED_RED_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(INTERNAL_LED_GREEN_GPIO_Port, INTERNAL_LED_GREEN_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(INTERNAL_LED_BLUE_GPIO_Port, INTERNAL_LED_BLUE_Pin, state); + break; + + default: + break; + } +} diff --git a/Src/periphery/led/led.hpp b/Src/periphery/led/led.hpp new file mode 100644 index 0000000..93361c1 --- /dev/null +++ b/Src/periphery/led/led.hpp @@ -0,0 +1,31 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022-2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef SRC_APPLICATION_PERIPHERY_LED_LED_HPP_ +#define SRC_APPLICATION_PERIPHERY_LED_LED_HPP_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum class LedColor { + RED_COLOR, + GREEN_COLOR, + BLUE_COLOR, + COLORS_AMOUNT, +}; + +class LedPeriphery { +public: + static void reset(); + static void toggle(LedColor led_color); +}; + +#ifdef __cplusplus +} +#endif + +#endif // SRC_APPLICATION_PERIPHERY_LED_LED_HPP_ diff --git a/Src/periphery/pwm/pwm.cpp b/Src/periphery/pwm/pwm.cpp new file mode 100644 index 0000000..46f8b00 --- /dev/null +++ b/Src/periphery/pwm/pwm.cpp @@ -0,0 +1,97 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022-2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "pwm.hpp" +#include "main.h" + +extern TIM_HandleTypeDef htim3; +extern TIM_HandleTypeDef htim4; + +int8_t PwmPeriphery::init(PwmPin pwm_pin) { + switch (pwm_pin) { + case PwmPin::PWM_1: + if (HAL_OK != HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2)) { + return -1; + } + TIM4->CCR2 = 1000; + break; + + case PwmPin::PWM_2: + if (HAL_OK != HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1)) { + return -1; + } + TIM4->CCR1 = 1000; + break; + + case PwmPin::PWM_3: + if (HAL_OK != HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1)) { + return -1; + } + TIM3->CCR1 = 1000; + break; + + case PwmPin::PWM_4: + if (HAL_OK != HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2)) { + return -1; + } + TIM3->CCR2 = 1000; + break; + + default: + return -1; + } + + return 0; +} + +void PwmPeriphery::set_duration(const PwmPin pwm_pin, uint32_t duration_us) { + switch (pwm_pin) { + case PwmPin::PWM_1: + TIM4->CCR2 = duration_us; + break; + + case PwmPin::PWM_2: + TIM4->CCR1 = duration_us; + break; + + case PwmPin::PWM_3: + TIM3->CCR1 = duration_us; + break; + + case PwmPin::PWM_4: + TIM3->CCR2 = duration_us; + break; + + default: + break; + } +} + +uint32_t PwmPeriphery::get_duration(PwmPin pwm_pin) { + uint32_t pwm_duration; + + switch (pwm_pin) { + case PwmPin::PWM_1: + pwm_duration = TIM4->CCR2; + break; + + case PwmPin::PWM_2: + pwm_duration = TIM4->CCR1; + break; + + case PwmPin::PWM_3: + pwm_duration = TIM3->CCR1; + break; + + case PwmPin::PWM_4: + pwm_duration = TIM3->CCR2; + break; + + default: + pwm_duration = 0; + break; + } + + return pwm_duration; +} diff --git a/Src/periphery/pwm/pwm.hpp b/Src/periphery/pwm/pwm.hpp new file mode 100644 index 0000000..cf7e5f2 --- /dev/null +++ b/Src/periphery/pwm/pwm.hpp @@ -0,0 +1,37 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2023 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef SRC_APPLICATION_PERIPHERY_PWM_HPP_ +#define SRC_APPLICATION_PERIPHERY_PWM_HPP_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @note PWM pinout related to RaccoonLab Mini v2 node + */ +enum class PwmPin { + PWM_1, // PB7 + PWM_2, // PB6 + PWM_3, // PB4 + PWM_4, // PB5 + PWM_AMOUNT, +}; + + +class PwmPeriphery { +public: + static int8_t init(PwmPin pin); + static void set_duration(const PwmPin pin, uint32_t duration_us); + static uint32_t get_duration(PwmPin pin); +}; + +#ifdef __cplusplus +} +#endif + +#endif // SRC_APPLICATION_PERIPHERY_PWM_HPP_ diff --git a/assets/connection.png b/assets/connection.png new file mode 100644 index 0000000..013c897 Binary files /dev/null and b/assets/connection.png differ diff --git a/assets/yukon.png b/assets/yukon.png new file mode 100644 index 0000000..8f52715 Binary files /dev/null and b/assets/yukon.png differ diff --git a/scripts/init.sh b/scripts/init.sh new file mode 100755 index 0000000..b954047 --- /dev/null +++ b/scripts/init.sh @@ -0,0 +1,14 @@ +#!/bin/bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +REPOSITORY_PATH="$(dirname "$SCRIPT_DIR")" +INIT_SCRIPT_PATH=$SCRIPT_DIR/tools/cyphal/init.sh +DSDL_DIR="$REPOSITORY_PATH/Libs/Cyphal/Libs/public_regulated_data_types" + +export REG_DATA_TYPE_PATH="$DSDL_DIR/uavcan $DSDL_DIR/reg" +export YAKUT_COMPILE_OUTPUT="$REPOSITORY_PATH/build/compile_output" + +if [ "${BASH_SOURCE[0]}" -ef "$0" ]; then + $INIT_SCRIPT_PATH +else + source $INIT_SCRIPT_PATH +fi diff --git a/scripts/prebuild_cyphal.sh b/scripts/prebuild_cyphal.sh new file mode 100755 index 0000000..24ce6da --- /dev/null +++ b/scripts/prebuild_cyphal.sh @@ -0,0 +1,13 @@ +#!/bin/bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +REPOSITORY_PATH="$(dirname "$SCRIPT_DIR")" +BUILD_DIR=${REPOSITORY_PATH}/build/src/ + +${REPOSITORY_PATH}/Libs/libparams/scripts/params_generate_array_from_yaml.py \ + ${BUILD_DIR} \ + c++ \ + params \ + $@ + +cd $REPOSITORY_PATH/Src/cyphal_application +${REPOSITORY_PATH}/Libs/libparams/scripts/generate_docs.py $@ diff --git a/scripts/tools b/scripts/tools new file mode 160000 index 0000000..00d6015 --- /dev/null +++ b/scripts/tools @@ -0,0 +1 @@ +Subproject commit 00d60151dee570f0edcbe5c6cb7a822ada1e01c6