├── .clang-format ├── .github └── workflows │ ├── build-stm32.yml │ ├── clang-format.yml │ ├── pytest.yml │ └── unittests.yml ├── .gitignore ├── .gitmodules ├── LICENSE.md ├── PROTOCOL.markdown ├── README.markdown ├── boot_arg.h ├── bootloader.c ├── bootloader.h ├── can_datagram.c ├── can_datagram.h ├── can_interface.h ├── client ├── .gitignore ├── README.md ├── cvra_bootloader │ ├── __init__.py │ ├── bootloader_flash.py │ ├── can │ │ ├── __init__.py │ │ ├── adapters.py │ │ ├── datagram.py │ │ ├── frame.py │ │ └── pcap.py │ ├── change_id.py │ ├── commands.py │ ├── page.py │ ├── read_config.py │ ├── run_application.py │ ├── utils.py │ └── write_config.py ├── setup.py └── tests │ ├── __init__.py │ ├── adapters │ ├── __init__.py │ └── test_socketcan.py │ ├── test_can_datagrams.py │ ├── test_change_id_tool.py │ ├── test_commands.py │ ├── test_config_read_tool.py │ ├── test_config_write_tool.py │ ├── test_datagram_reader.py │ ├── test_flashtool.py │ ├── test_pagination.py │ ├── test_pcap_file.py │ ├── test_run_application.py │ ├── test_utils.py │ └── tests_msgpack.py ├── command.c ├── command.h ├── config.c ├── config.h ├── flash_writer.h ├── package.yml ├── platform.h ├── platform ├── actuator-board │ ├── .gitignore │ ├── Makefile │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ └── platform.h ├── can-io-board │ ├── .gitignore │ ├── Makefile │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ └── platform.h ├── mcu │ ├── armv7-m │ │ ├── boot.s │ │ ├── boot_arg.c │ │ ├── timeout_timer.c │ │ ├── timeout_timer.h │ │ └── vector_table.c │ ├── stm32f1 │ │ ├── can_interface.c │ │ └── flash_writer.c │ ├── stm32f3 │ │ ├── can_interface.c │ │ └── flash_writer.c │ └── stm32f4 │ │ ├── can_interface.c │ │ └── flash_writer.c ├── motor-board-v1 │ ├── .gitignore │ ├── Makefile │ ├── flash.mk │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ └── platform.h ├── nucleo-board-stm32f103rb │ ├── .gdbinit │ ├── .gitignore │ ├── Makefile │ ├── flash.mk │ ├── gdb.init │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ ├── platform.h │ └── readme.txt ├── nucleo-board-stm32f334r8 │ ├── Makefile │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ └── platform.h ├── olimex-e407 │ ├── .gitignore │ ├── Makefile │ ├── flash.mk │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ └── platform.h ├── rc-board-v1 │ ├── .gitignore │ ├── Makefile │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ ├── platform.h │ └── tools.mk ├── sensor-board │ ├── .gitignore │ ├── Makefile │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ └── platform.h └── uwb-beacon │ ├── .gitignore │ ├── Makefile │ ├── include.jinja │ ├── linkerscript.ld │ ├── oocd.cfg │ ├── platform.c │ └── platform.h ├── tests ├── can_datagram_tests.cpp ├── config_commands_tests.cpp ├── config_tests.cpp ├── datagrams_command_tests.cpp ├── flash_command_tests.cpp ├── integration_tests.cpp └── mocks │ ├── boot_arg.cpp │ ├── can_interface_mock.cpp │ ├── can_interface_mock.h │ ├── flash_writer_mock.cpp │ ├── platform_mock.c │ ├── platform_mock.h │ └── timeout_mock.c └── timeout.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: WebKit 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: false 10 | AlignTrailingComments: false 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: Inline 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: false 23 | BreakBeforeBraces: Custom 24 | BraceWrapping: 25 | AfterClass: false 26 | AfterControlStatement: false 27 | AfterEnum: false 28 | AfterFunction: true 29 | AfterNamespace: false 30 | AfterObjCDeclaration: false 31 | AfterStruct: false 32 | AfterUnion: false 33 | AfterExternBlock: false 34 | BeforeCatch: false 35 | BeforeElse: false 36 | IndentBraces: false 37 | SplitEmptyFunction: true 38 | SplitEmptyRecord: true 39 | SplitEmptyNamespace: true 40 | BreakBeforeBinaryOperators: NonAssignment 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeComma 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 0 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DisableFormat: false 55 | ExperimentalAutoDetectBinPacking: false 56 | FixNamespaceComments: true 57 | ForEachMacros: 58 | - MESSAGEBUS_TOPIC_FOREACH 59 | - TEST_GROUP 60 | IncludeBlocks: Preserve 61 | IncludeCategories: 62 | - Regex: '^<' 63 | Priority: 1 64 | - Regex: '^("|<)(uavcan/|eigen/|lwip/|gfx.h|ff.h)' 65 | Priority: 2 66 | - Regex: '^"(arm-cortex-tools|can-bootloader|chibios-syscalls|cmp|cmp_mem_access|crc|error|filter|goap|msgbus|parameter|parameter_flash_storage|pid|quadramp|raft|serial-datagram|simplerpc|timestamp|trace|version)' 67 | Priority: 3 68 | - Regex: '^"(blocking_detection_manager|control_system_manager|math|obstacle_avoidance|position_manager|robot_system|trajectory_manager)/' 69 | Priority: 4 70 | - Regex: '^"(protobuf)/' 71 | Priority: 5 72 | - Regex: '.*' 73 | Priority: 6 74 | IncludeIsMainRegex: '(Test)?$' 75 | IndentCaseLabels: true 76 | IndentPPDirectives: None 77 | IndentWidth: 4 78 | IndentWrappedFunctionNames: false 79 | JavaScriptQuotes: Leave 80 | JavaScriptWrapImports: true 81 | KeepEmptyLinesAtTheStartOfBlocks: false 82 | MacroBlockBegin: '' 83 | MacroBlockEnd: '' 84 | MaxEmptyLinesToKeep: 1 85 | NamespaceIndentation: None 86 | ObjCBinPackProtocolList: Auto 87 | ObjCBlockIndentWidth: 4 88 | ObjCSpaceAfterProperty: true 89 | ObjCSpaceBeforeProtocolList: true 90 | PenaltyBreakAssignment: 2 91 | PenaltyBreakBeforeFirstCallParameter: 19 92 | PenaltyBreakComment: 300 93 | PenaltyBreakFirstLessLess: 120 94 | PenaltyBreakString: 1000 95 | PenaltyExcessCharacter: 1000000 96 | PenaltyReturnTypeOnItsOwnLine: 60 97 | DerivePointerAlignment: false 98 | PointerAlignment: Left 99 | ReflowComments: true 100 | SortIncludes: false 101 | SortUsingDeclarations: true 102 | SpaceAfterCStyleCast: false 103 | SpaceAfterTemplateKeyword: true 104 | SpaceBeforeAssignmentOperators: true 105 | # SpaceBeforeCpp11BracedList: false 106 | SpaceBeforeCtorInitializerColon: true 107 | SpaceBeforeInheritanceColon: true 108 | SpaceBeforeParens: ControlStatements 109 | SpaceBeforeRangeBasedForLoopColon: true 110 | SpaceInEmptyParentheses: false 111 | SpacesBeforeTrailingComments: 1 112 | SpacesInAngles: false 113 | SpacesInContainerLiterals: false 114 | SpacesInCStyleCastParentheses: false 115 | SpacesInParentheses: false 116 | SpacesInSquareBrackets: false 117 | Standard: Cpp11 118 | TabWidth: 8 119 | UseTab: Never 120 | --- 121 | Language: Proto 122 | IndentWidth: 4 123 | UseTab: Never 124 | ... 125 | 126 | -------------------------------------------------------------------------------- /.github/workflows/build-stm32.yml: -------------------------------------------------------------------------------- 1 | name: STM32 builds 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | board: 9 | - actuator-board 10 | - can-io-board 11 | - motor-board-v1 12 | - nucleo-board-stm32f103rb 13 | - nucleo-board-stm32f334r8 14 | - olimex-e407 15 | - rc-board-v1 16 | - sensor-board 17 | - uwb-beacon 18 | steps: 19 | - uses: actions/checkout@v2 20 | with: 21 | submodules: 'recursive' 22 | - name: Run packager 23 | uses: docker://antoinealb/cvra-ci:latest 24 | with: 25 | args: bash -c "packager" 26 | - name: Monkeypatch python3 27 | run: | 28 | cd $GITHUB_WORKSPACE/libopencm3 29 | sed -i s/python/python3/ scripts/irq2nvic_h 30 | - name: Build libopencm3 31 | uses: docker://antoinealb/cvra-ci:latest 32 | with: 33 | args: bash -c "cd /github/workspace/libopencm3 && make" 34 | - name: Build 35 | uses: docker://antoinealb/cvra-ci:latest 36 | with: 37 | args: bash -c "cd /github/workspace/platform/${{ matrix.board }} && make" 38 | -------------------------------------------------------------------------------- /.github/workflows/clang-format.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | name: clang-format Code Formatter 3 | jobs: 4 | lint: 5 | name: clang-format Code Formatter 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Clang Code Formatter 9 | uses: antoinealb/clang-format-action@master 10 | env: 11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 12 | -------------------------------------------------------------------------------- /.github/workflows/pytest.yml: -------------------------------------------------------------------------------- 1 | name: Python tests 2 | on: [push] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | python: [3.7, 3.8, 3.9, 3.10, 3.11] 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Install Python 3 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: ${{ matrix.python }} 16 | - name: Install dependencies 17 | run: | 18 | cd client 19 | python -m pip install --upgrade pip 20 | python setup.py install 21 | - name: Run tests 22 | run: | 23 | cd client 24 | python -m unittest discover 25 | -------------------------------------------------------------------------------- /.github/workflows/unittests.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | with: 9 | submodules: 'recursive' 10 | - name: Install prerequisites 11 | run: | 12 | sudo apt-get update -y && sudo apt-get install -y python3-pip python3-dev python3-setuptools libcpputest-dev build-essential cmake 13 | sudo pip3 install msgpack-python==0.4.8 PyYAML==3.11 cvra-packager~=1.0.0 14 | - name: Run packager 15 | run: | 16 | cd $GITHUB_WORKSPACE 17 | packager 18 | - name: Build 19 | run: | 20 | cd $GITHUB_WORKSPACE 21 | mkdir build 22 | cd build 23 | cmake .. 24 | make 25 | - name: Test 26 | run: | 27 | cd $GITHUB_WORKSPACE/build 28 | ./tests 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files generated by packager (https://github.com/cvra/packager) 2 | platform/*/include.mk 3 | 4 | # Editor swap files 5 | *.swp 6 | 7 | # Eclipse files 8 | .project 9 | .cproject 10 | .settings 11 | .metadata 12 | 13 | # Dependency files 14 | *.d 15 | 16 | # Object files 17 | *.o 18 | *.ko 19 | *.obj 20 | *.elf 21 | 22 | # Libraries 23 | *.lib 24 | *.a 25 | 26 | # Shared objects (inc. Windows DLLs) 27 | *.dll 28 | *.so 29 | *.so.* 30 | *.dylib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | *.i*86 37 | *.x86_64 38 | *.hex 39 | *.bin 40 | 41 | # Aux output 42 | *.lst 43 | *.map 44 | *.size.txt 45 | 46 | # Build scripts and directories 47 | CMakeLists.txt 48 | build/* 49 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libopencm3"] 2 | path = libopencm3 3 | url = https://github.com/nuft/libopencm3.git 4 | [submodule "dependencies/crc"] 5 | path = dependencies/crc 6 | url = https://github.com/cvra/CRC.git 7 | [submodule "dependencies/test-runner"] 8 | path = dependencies/test-runner 9 | url = https://github.com/cvra/test-runner.git 10 | [submodule "dependencies/cmp"] 11 | path = dependencies/cmp 12 | url = https://github.com/cvra/cmp 13 | [submodule "dependencies/cmp_mem_access"] 14 | path = dependencies/cmp_mem_access 15 | url = https://github.com/cvra/cmp_mem_access.git 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015, Club Vaudois de Robotique Autonome (CVRA) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /PROTOCOL.markdown: -------------------------------------------------------------------------------- 1 | # Bootloader protocol 2 | 3 | ## Design goals 4 | 5 | * The bootloader might coexist with higher level protocols (UAVCAN) and its operation must not be disturbed. 6 | * It must handle lost CAN frames gracefully. 7 | * The protocol must be open to extension. 8 | 9 | ## CAN Transport layer 10 | It will not be really needed to have a complicated transport layer here. 11 | Using standard frames over extended frames guarantees that the bootloader protocol always win the arbitration. 12 | CAN is pretty robust so packet drops will be really infrequent and will be detected when doing the CRC of the whole flash. 13 | We will never send packet out of order so no sequence number is needed. 14 | 15 | Multicast is done by having a list of ID who are adressed at the beginning of a datagram. 16 | Unicast is just a special case of multicast with only one node in the list. 17 | 18 | There is one bit to indicate if this message is the first of a datagram (1) or if it is following a start marker (0). 19 | 20 | The format of the CAN message ID is : 21 | 22 | * bits 10..8: 3 reserved bits, set to zero for bootloader frames 23 | * bit 7: 1 bit for start of datagram bit (1 if first frame in datagram, 0 otherwise) 24 | * bits 6..0: bits for the source ID 25 | 26 | *Note:* The first 3 bits of the ID are dominant (0) for bootloader frames. It has therefore highest priority on the bus. 27 | 28 | ## CAN Datagram format 29 | The CAN datagram layer has the following resposibilities : 30 | 31 | * Adressing 32 | * Ensure data integrity (CRC) 33 | 34 | The datagram format is the following : 35 | 36 | 1. Protocol version (currently 0x01): 1 byte 37 | 2. CRC32 of the whole datagram : 4 bytes 38 | 3. Destination ID list length (m) : 1 byte 39 | 4. Destination IDs : m bytes 40 | 5. Data length : 4 bytes, MSB first 41 | 6. Data: n bytes 42 | 43 | # Command format 44 | 45 | The command format is simple : 46 | 47 | +-------+------+---+-------+-------+ 48 | |Version|Command|Param1|...|ParamN | 49 | +-------+------+---+-------+-------+ 50 | 51 | The protocol version is simply an integer that increments everytime a change in the command set is done. 52 | The command interpreter silently ignores commands with the wrong version number to avoid misinterpreting packets. 53 | Command and parameters are encoded using MessagePack [1] because it is efficient and extendable. 54 | The command and its parameters are written in a MessagePack array so we can know the command count. 55 | Some commands will require an answer for example when writing the application CRC back to the programmer. 56 | They should simply send the MessagePack encoded response on the bus. 57 | 58 | ## Standard commands 59 | 60 | 1. Jump to application (0x01). No parameters. Simply starts the application code. 61 | 2. CRC flash region (0x02). 2 parameters : start adress and length of the region we want to check. Returns the CRC32 of this region. 62 | 3. Erase flash page (0x03). Parameters : Page address, device class (string). Returns: True if successful. 63 | 4. Write flash (0x04). Parameters : Start adress, device class (string) and sequence of bytes to write. Returns: True if successful. 64 | 5. Ping (0x05). Parameters: None. Returns: True if bootloader is ready to accept a command. 65 | 6. Read flash (0x06). Parameters : Start adress and length. Returns sequence of read bytes 66 | 7. Update config (0x07). The only parameters is a MessagePack map containing the configuration values to update. If a config value is not in its parameters, it will not be changed. Returns: True if successful. 67 | 8. Save config to flash (0x08). Returns: True if successful. 68 | 9. Read current config (0x09). No parameters. Writes back a messagepack map containing the bootloader config. 69 | 70 | *Note:* Adresses (pointers) in the arguments are represented as 64 bits integers. 71 | 64 bits was chosen to allow tests to run on 64 bits platforms too. 72 | 73 | ## Multicast write 74 | When using multicast write the recommended way is the following : 75 | 76 | 1. Write to all boards using multicast write command. 77 | 2. Wait with a timeout for the command to finish. 78 | 3. Continue only with boards that successfully finished. 79 | 4. Verify the written data via CRC command. 80 | 5. Reflash the boards that failed during above process. (optional) 81 | 82 | # References 83 | [1] MessagePack specifications : https://github.com/msgpack/msgpack/blob/master/spec.md 84 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Bootloader protocol 2 | 3 | ![Python tests](https://github.com/cvra/can-bootloader/workflows/Python%20tests/badge.svg) 4 | 5 | ![Unit tests](https://github.com/cvra/can-bootloader/workflows/Unit%20tests/badge.svg) 6 | 7 | ![STM32 builds](https://github.com/cvra/can-bootloader/workflows/STM32%20builds/badge.svg) 8 | 9 | This repository contains the code used for the bootloader running on every microcontroller in our robots. 10 | It allows us to quickly update the firmware on all (>20) boards quickly and without disassembly or additional electrical connections. 11 | 12 | # Config pages 13 | 14 | The bootloader code is followed by two pages of bootloader config. 15 | The two pages are for redundancy and are checked by a CRC32 placed at the beginning of a page. 16 | The bootloader checks the configuration CRC at boot. 17 | If an invalid page is detected, its content is replaced by the redundant page. 18 | 19 | After the bootloader has updated one of the two configuratio pages, it verifies it before proceeding to the second one. 20 | This ensures that there is always a valid configuration page to prevent bricking a board. 21 | 22 | The config contains the following informations, stored as a messagepack map: 23 | 24 | * ID: Unique node identifier, ranging from 1 to 127. 25 | * name: Human readable name describing the board (ex: "arms.left.shoulder"). Max length: 64 26 | * device_class: Board model (ex: "CVRA.MotorController.v1"). Max length: 64 27 | * application_crc: Application CRC. If the CRC matches the bootloader will automatically boot into it after a timeout. 28 | * application_size: Needed for CRC calculation. 29 | * update_count: Firmware update counter. Used for diagnostics and lifespan estimation. 30 | 31 | # Performance considerations 32 | 33 | Assuming : 34 | 35 | * CAN speed is 1 Mb/s 36 | * CAN overhead is about 50% 37 | * Protocol overhead + data drop is about 10%. 38 | The size of a write chunk might affect this since invalid data is dropped by chunk. 39 | * Binary size : 1 MB 40 | * The bottleneck is in the CAN network, not in the CRC computation or in the flash write speed. 41 | * The time to check the CRC of each board can be neglected. 42 | * We use multicast write commands to lower bandwith usage. 43 | 44 | We can flash a whole board (1MB) in about 20 seconds. 45 | If all board in the robot run the same firmware, this is the time required to do a full system update! 46 | 47 | # Safety features 48 | The bootloader is expected to be one of the safest part of the robot firmware. 49 | Correcting a bug in the bootloader could be very complicated, requiring disassembly of the robot in the worst cases. 50 | Therefore, when implementing the bootloader or the associated protocol, the following safety points must be taken into account: 51 | 52 | * The bootloader must *never* erase itself or its configuration page. 53 | * It should never write to flash if the device class does not match. Doing so might result in the wrong firmware being written to the board, which is dangerous. 54 | * If the application CRC does not match, the bootloader should not boot it. 55 | * On power up the bootloader should wait enough time for the user to input commands before jumping to the application code. 56 | 57 | # Building 58 | 59 | 1. Run [CVRA's packager script](https://github.com/cvra/packager): `packager`. 60 | 2. Build libopencm3: `pushd libopencm3 && make && popd`. 61 | 3. Build your desired platform: `cd platform/motor-board-v1 && make`. 62 | 4. Flash the resulting binary to your board: `make flash`. 63 | -------------------------------------------------------------------------------- /boot_arg.h: -------------------------------------------------------------------------------- 1 | #ifndef BOOT_ARG_H 2 | #define BOOT_ARG_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | #define BOOT_ARG_START_BOOTLOADER 0x00 11 | #define BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT 0x01 12 | #define BOOT_ARG_START_APPLICATION 0x02 13 | #define BOOT_ARG_START_ST_BOOTLOADER 0x03 14 | 15 | void reboot_system(uint8_t arg); 16 | 17 | #ifdef __cplusplus 18 | } 19 | #endif 20 | 21 | #endif // BOOT_ARG_H 22 | -------------------------------------------------------------------------------- /bootloader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include "command.h" 6 | #include "can_datagram.h" 7 | #include "config.h" 8 | #include "boot_arg.h" 9 | #include "timeout.h" 10 | #include "can_interface.h" 11 | #include "flash_writer.h" 12 | 13 | #define BUFFER_SIZE FLASH_PAGE_SIZE + 128 14 | #define DEFAULT_ID 0x01 15 | #define CAN_SEND_RETRIES 100 16 | #define CAN_RECEIVE_TIMEOUT 1000 17 | 18 | uint8_t output_buf[BUFFER_SIZE]; 19 | uint8_t data_buf[BUFFER_SIZE]; 20 | uint8_t addr_buf[128]; 21 | 22 | const command_t commands[] = { 23 | {.index = 1, .callback = command_jump_to_application}, 24 | {.index = 2, .callback = command_crc_region}, 25 | {.index = 3, .callback = command_erase_flash_page}, 26 | {.index = 4, .callback = command_write_flash}, 27 | {.index = 5, .callback = command_ping}, 28 | {.index = 6, .callback = command_read_flash}, 29 | {.index = 7, .callback = command_config_update}, 30 | {.index = 8, .callback = command_config_write_to_flash}, 31 | {.index = 9, .callback = command_config_read}}; 32 | 33 | static void return_datagram(uint8_t source_id, uint8_t dest_id, uint8_t* data, size_t len) 34 | { 35 | can_datagram_t dt; 36 | uint8_t dest_nodes[1]; 37 | uint8_t buf[8]; 38 | 39 | can_datagram_init(&dt); 40 | can_datagram_set_address_buffer(&dt, dest_nodes); 41 | can_datagram_set_data_buffer(&dt, data, len); 42 | dt.destination_nodes[0] = dest_id; 43 | dt.destination_nodes_len = 1; 44 | dt.data_len = len; 45 | dt.crc = can_datagram_compute_crc(&dt); 46 | 47 | bool start_of_datagram = true; 48 | while (true) { 49 | uint8_t dlc = can_datagram_output_bytes(&dt, (char*)buf, sizeof(buf)); 50 | 51 | if (dlc == 0) { 52 | break; 53 | } 54 | 55 | if (start_of_datagram) { 56 | if (!can_interface_send_message(source_id | ID_START_MASK, buf, dlc, 57 | CAN_SEND_RETRIES)) { 58 | break; // failed 59 | } 60 | start_of_datagram = false; 61 | } else { 62 | if (!can_interface_send_message(source_id, buf, dlc, CAN_SEND_RETRIES)) { 63 | break; // failed 64 | } 65 | } 66 | } 67 | } 68 | 69 | void bootloader_main(int arg) 70 | { 71 | bool timeout_active = !(arg == BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 72 | 73 | flash_init(); 74 | 75 | bootloader_config_t config; 76 | if (config_is_valid(memory_get_config1_addr(), CONFIG_PAGE_SIZE)) { 77 | config = config_read(memory_get_config1_addr(), CONFIG_PAGE_SIZE); 78 | } else if (config_is_valid(memory_get_config2_addr(), CONFIG_PAGE_SIZE)) { 79 | config = config_read(memory_get_config2_addr(), CONFIG_PAGE_SIZE); 80 | } else { 81 | // exact behaviour at invalid config is not yet defined. 82 | strcpy(config.device_class, PLATFORM_DEVICE_CLASS); 83 | strcpy(config.board_name, "foobar2000"); 84 | config.ID = DEFAULT_ID; 85 | config.application_crc = 0xDEADC0DE; 86 | config.application_size = 0; 87 | config.update_count = 1; 88 | } 89 | 90 | can_datagram_t dt; 91 | can_datagram_init(&dt); 92 | can_datagram_set_address_buffer(&dt, addr_buf); 93 | can_datagram_set_data_buffer(&dt, data_buf, sizeof(data_buf)); 94 | can_datagram_start(&dt); 95 | 96 | uint8_t data[8]; 97 | uint32_t id; 98 | uint8_t len; 99 | while (true) { 100 | if (timeout_active && timeout_reached()) { 101 | command_jump_to_application(0, NULL, NULL, &config); 102 | } 103 | 104 | if (!can_interface_read_message(&id, data, &len, CAN_RECEIVE_TIMEOUT)) { 105 | continue; 106 | } 107 | 108 | if ((id & ID_START_MASK) != 0) { 109 | can_datagram_start(&dt); 110 | } 111 | 112 | int i; 113 | for (i = 0; i < len; i++) { 114 | can_datagram_input_byte(&dt, data[i]); 115 | } 116 | 117 | if (can_datagram_is_complete(&dt)) { 118 | if (can_datagram_is_valid(&dt)) { 119 | timeout_active = false; 120 | int i; 121 | for (i = 0; i < dt.destination_nodes_len; i++) { 122 | if (dt.destination_nodes[i] == config.ID) { 123 | timeout_active = false; 124 | break; 125 | } 126 | } 127 | if (i != dt.destination_nodes_len) { 128 | // we were addressed 129 | len = protocol_execute_command((char*)dt.data, dt.data_len, 130 | &commands[0], sizeof(commands) / sizeof(command_t), 131 | (char*)output_buf, sizeof(output_buf), &config); 132 | 133 | if (len > 0) { 134 | uint8_t return_id = id & ~ID_START_MASK; 135 | return_datagram(config.ID, return_id, output_buf, (size_t)len); 136 | } 137 | } 138 | } 139 | can_datagram_start(&dt); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /bootloader.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef BOOTLOADER_H 3 | #define BOOTLOADER_H 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | void bootloader_main(int arg); 10 | 11 | #ifdef __cplusplus 12 | } 13 | #endif 14 | 15 | #endif /* BOOTLOADER_H */ 16 | -------------------------------------------------------------------------------- /can_datagram.h: -------------------------------------------------------------------------------- 1 | #ifndef CAN_DATAGRAM_H 2 | #define CAN_DATAGRAM_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | #define CAN_DATAGRAM_VERSION 1 13 | 14 | #define ID_START_MASK (1 << 7) 15 | 16 | typedef struct { 17 | int protocol_version; 18 | uint32_t crc; 19 | 20 | uint8_t destination_nodes_len; 21 | uint8_t* destination_nodes; 22 | 23 | uint32_t data_len; 24 | uint8_t* data; 25 | 26 | int _crc_bytes_read; 27 | int _crc_bytes_written; 28 | int _data_length_bytes_read; 29 | int _data_length_bytes_written; 30 | uint8_t _destination_nodes_read; 31 | uint8_t _destination_nodes_written; 32 | uint16_t _data_bytes_read; 33 | uint16_t _data_bytes_written; 34 | uint16_t _data_buffer_size; 35 | int _reader_state; 36 | int _writer_state; 37 | } can_datagram_t; 38 | 39 | /** Sets the structure field to default values. */ 40 | void can_datagram_init(can_datagram_t* dt); 41 | 42 | /** Sets the buffer to use to store destination addresses. */ 43 | void can_datagram_set_address_buffer(can_datagram_t* dt, uint8_t* buf); 44 | 45 | /** Sets the buffer to use for data storage. */ 46 | void can_datagram_set_data_buffer(can_datagram_t* dt, uint8_t* buf, size_t buf_size); 47 | 48 | /** Inputs a byte into the datagram. */ 49 | void can_datagram_input_byte(can_datagram_t* dt, uint8_t val); 50 | 51 | /** Returns true if the datagram is complete (all data were read). */ 52 | bool can_datagram_is_complete(can_datagram_t* dt); 53 | 54 | /** Returns true if the datagram is valid (complete and CRC match). */ 55 | bool can_datagram_is_valid(can_datagram_t* dt); 56 | 57 | /** Signals to the parser that we are at the start of a datagram. 58 | * 59 | * The start of datagram comes from the Message ID (physical layer). 60 | */ 61 | void can_datagram_start(can_datagram_t* dt); 62 | 63 | /** Encodes the datagram in the buffer. */ 64 | int can_datagram_output_bytes(can_datagram_t* dt, char* buffer, size_t buffer_len); 65 | 66 | /** Computes the CRC32 of the datagram. */ 67 | uint32_t can_datagram_compute_crc(can_datagram_t* dt); 68 | 69 | /** Returns true if the ID has the start of datagram field set. */ 70 | bool can_datagram_id_start_is_set(unsigned int id); 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /can_interface.h: -------------------------------------------------------------------------------- 1 | #ifndef CAN_INTERFACE_H 2 | #define CAN_INTERFACE_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | /** Reads a message from the CAN interface. 12 | * @param [out] id A pointer to the variable where the message ID will be stored. 13 | * @param [out] message A buffer where the CAN message will be stored. Should be large enough to contain 8 bytes. 14 | * @param [out] length A pointer to the message length. 15 | * @param [in] retries The number of retries until timeout. 16 | * @returns true if read was successful. 17 | */ 18 | bool can_interface_read_message(uint32_t* id, uint8_t* message, uint8_t* length, uint32_t retries); 19 | 20 | /** Sends a message via the CAN interface. 21 | * @param [in] id The CAN ID to address. 22 | * @param [in] message The message data. 23 | * @param [in] length The message length. 24 | * @param [in] retries The number of retries until timeout. 25 | * @returns true if write was successful. 26 | */ 27 | bool can_interface_send_message(uint32_t id, uint8_t* message, uint8_t length, uint32_t retries); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Bootloader client 2 | 3 | This repository contains the code for the client running on the host. 4 | In the end it will be able to update the whole robot using a CAN to serial link. 5 | The serial stream can be implemented over a standard UART connexion or a TCP stream. 6 | 7 | # Installation 8 | Here we install dependencies in a virtual environment to avoid cluttering system install. 9 | An alternative way is to use your system package manager to install dependencies. 10 | Installing system wide packages via pip is *not a good idea*. 11 | 12 | ```sh 13 | # Create virtual env 14 | virtualenv --python=python3 venv 15 | source env/bin/activate 16 | 17 | # if you use fish, run this instead: 18 | # source env/bin/activate.fish 19 | 20 | # Install required packages 21 | python setup.py install 22 | ``` 23 | 24 | ## Development mode 25 | If you are working on the bootloader client code you might want to use `python setup.py develop`. 26 | Instead of copying the code into the python path it symlinks it, meaning you can edit your code and run the modified version without running `setup.py` again. 27 | 28 | To run the tests, run `python -m unittest discover` after installing the dependencies. 29 | 30 | # Builtin tools 31 | These are the tools for talking with the bootloader on the host. 32 | They can all run with `-h/--help`, so use that to know how arguments works. 33 | 34 | * `bootloader_flash`: Used to program target boards. 35 | * `bootloader_read_config`: Used to read the config from a bunch of boards and dump it as JSON. 36 | * `bootloader_change_id`: Used to change a single device ID *Use it carefully.* 37 | * `bootloader_write_config`: Used to change board config, such as device class, name and so on. 38 | -------------------------------------------------------------------------------- /client/cvra_bootloader/__init__.py: -------------------------------------------------------------------------------- 1 | from . import commands, page, utils 2 | -------------------------------------------------------------------------------- /client/cvra_bootloader/can/__init__.py: -------------------------------------------------------------------------------- 1 | from .datagram import * 2 | from .frame import * 3 | -------------------------------------------------------------------------------- /client/cvra_bootloader/can/adapters.py: -------------------------------------------------------------------------------- 1 | import cvra_bootloader.can 2 | import socket 3 | import struct 4 | import serial 5 | from queue import Queue 6 | import threading 7 | 8 | 9 | class SocketCANConnection: 10 | # See for format 11 | CAN_FRAME_FMT = "=IB3x8s" 12 | CAN_FRAME_SIZE = struct.calcsize(CAN_FRAME_FMT) 13 | 14 | def __init__(self, interface, read_timeout=1): 15 | """ 16 | Initiates a CAN connection on the given interface (e.g. 'can0'). 17 | """ 18 | # Creates a raw CAN connection and binds it to the given interface. 19 | self.socket = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW) 20 | 21 | self.socket.bind((interface,)) 22 | self.socket.settimeout(read_timeout) 23 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 4096) 24 | 25 | def send_frame(self, frame): 26 | data = frame.data.ljust(8, b"\x00") 27 | data = struct.pack(self.CAN_FRAME_FMT, frame.id, len(frame.data), data) 28 | 29 | self.socket.send(data) 30 | 31 | def receive_frame(self): 32 | try: 33 | frame, _ = self.socket.recvfrom(self.CAN_FRAME_SIZE) 34 | except socket.timeout: 35 | return None 36 | can_id, can_dlc, data = struct.unpack(self.CAN_FRAME_FMT, frame) 37 | 38 | return cvra_bootloader.can.Frame(id=can_id, data=data[:can_dlc]) 39 | 40 | 41 | class SerialCANConnection: 42 | """ 43 | Implements the slcan API. 44 | """ 45 | 46 | MIN_MSG_LEN = len("t1230") 47 | 48 | def __init__(self, port, read_timeout=1): 49 | self.port = port 50 | self.timeout = read_timeout 51 | 52 | self.rx_queue = Queue() 53 | t = threading.Thread(target=self.spin) 54 | t.daemon = True 55 | t.start() 56 | 57 | self.send_command("S8") 58 | # bitrate 1Mbit 59 | self.send_command("O") 60 | # open device 61 | port.reset_input_buffer() 62 | 63 | def spin(self): 64 | part = "" 65 | while True: 66 | part += self.port.read(100).decode("ascii") 67 | 68 | if part.startswith("\r"): 69 | part.lstrip("\r") 70 | 71 | if "\r" not in part: 72 | continue 73 | 74 | data = part.split("\r") 75 | data, part = data[:-1], data[-1] 76 | 77 | for frame in data: 78 | if frame is None: 79 | continue 80 | frame = self.decode_frame(frame) 81 | if frame: 82 | self.rx_queue.put(frame) 83 | 84 | def send_command(self, cmd): 85 | cmd += "\r" 86 | cmd = cmd.encode("ascii") 87 | self.port.write(cmd) 88 | 89 | def decode_frame(self, msg): 90 | if len(msg) < self.MIN_MSG_LEN: 91 | return None 92 | 93 | cmd, msg = msg[0], msg[1:] 94 | if cmd == "T": 95 | extended = True 96 | id_len = 8 97 | elif cmd == "t": 98 | extended = False 99 | id_len = 3 100 | else: 101 | return None 102 | 103 | if len(msg) < id_len + 1: 104 | return None 105 | can_id = int(msg[0:id_len], 16) 106 | msg = msg[id_len:] 107 | data_len = int(msg[0]) 108 | msg = msg[1:] 109 | if len(msg) < 2 * data_len: 110 | return None 111 | data = [int(msg[i : i + 2], 16) for i in range(0, 2 * data_len, 2)] 112 | 113 | return can.Frame( 114 | id=can_id, data=bytearray(data), data_length=data_len, extended=extended 115 | ) 116 | 117 | def encode_frame(self, frame): 118 | if frame.extended: 119 | cmd = "T" 120 | can_id = "{:08x}".format(frame.id) 121 | else: 122 | cmd = "t" 123 | can_id = "{:03x}".format(frame.id) 124 | 125 | length = "{:x}".format(frame.data_length) 126 | 127 | data = "" 128 | for b in frame.data: 129 | data += "{:02x}".format(b) 130 | return cmd + can_id + length + data 131 | 132 | def send_frame(self, frame): 133 | cmd = self.encode_frame(frame) 134 | self.send_command(cmd) 135 | 136 | def receive_frame(self): 137 | try: 138 | # according to the datasheet, erasing a sector from an stm32f407 139 | # can take up to 4 seconds. Therefore we need to wait a bit 140 | return self.rx_queue.get(True, self.timeout) 141 | except: 142 | return None 143 | -------------------------------------------------------------------------------- /client/cvra_bootloader/can/datagram.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from zlib import crc32 3 | from .frame import * 4 | 5 | DATAGRAM_VERSION = 1 6 | START_OF_DATAGRAM_MASK = 1 << 7 7 | 8 | 9 | class VersionMismatchError(RuntimeError): 10 | """ 11 | Error raised when attempting to decode a datagram with the wrong version. 12 | """ 13 | 14 | pass 15 | 16 | 17 | class CRCMismatchError(RuntimeError): 18 | """ 19 | Error raised when attempting to decode a datagram with the wrong CRC. 20 | """ 21 | 22 | pass 23 | 24 | 25 | def is_start_of_datagram(frame): 26 | """ 27 | Returns true if the given frame has the start of datagram marker. 28 | """ 29 | return bool(frame.id & START_OF_DATAGRAM_MASK) 30 | 31 | 32 | def encode_datagram(data, destinations): 33 | """ 34 | Encodes the given data and destination list to form a complete datagram. 35 | This datagram can then be cut into CAN messages by datagram_to_frames. 36 | """ 37 | 38 | version = struct.pack("B", DATAGRAM_VERSION) 39 | addresses = bytes([len(destinations)] + destinations) 40 | dt = struct.pack(">I", len(data)) + data 41 | crc = struct.pack(">I", crc32(addresses + dt)) 42 | 43 | return version + crc + addresses + dt 44 | 45 | 46 | def decode_datagram(data): 47 | """ 48 | Decode the given datagram. 49 | Returns a tuple containing the data and the list of destination if the datagram is complete and valid. 50 | Returns None if the datagram is incomplete. 51 | 52 | Raise an exception if the datagram is invalid (wrong version or wrong CRC). 53 | """ 54 | 55 | try: 56 | version, data = int(data[0]), data[1:] 57 | if version != DATAGRAM_VERSION: 58 | raise VersionMismatchError 59 | 60 | header_format = ">IB" 61 | header_len = struct.calcsize(header_format) 62 | header, data = data[0:header_len], data[header_len:] 63 | crc, dst_len = struct.unpack(header_format, header) 64 | 65 | # Decodes the destination list 66 | destination_format = "{}s".format(dst_len) 67 | destinations, data = data[:dst_len], data[dst_len:] 68 | destinations = list(struct.unpack(destination_format, destinations)[0]) 69 | 70 | data_len, data = data[:4], data[4:] 71 | data_len = struct.unpack(">I", data_len)[0] 72 | 73 | # If we did not receive all data yet 74 | if data_len != len(data): 75 | return None 76 | 77 | addresses = bytes([len(destinations)] + destinations) 78 | dt = struct.pack(">I", len(data)) + data 79 | 80 | if crc32(addresses + dt) != crc: 81 | raise CRCMismatchError 82 | 83 | except struct.error: 84 | # This means that we have not enough bytes to decode somewhere 85 | return None 86 | 87 | return data, destinations 88 | 89 | 90 | def datagram_to_frames(datagram, source): 91 | """ 92 | Transforms a raw datagram into CAN frames. 93 | """ 94 | start_bit = START_OF_DATAGRAM_MASK 95 | 96 | while len(datagram) > 8: 97 | data, datagram = datagram[:8], datagram[8:] 98 | 99 | yield Frame(id=start_bit + source, data=data) 100 | 101 | start_bit = 0 102 | 103 | yield Frame(id=start_bit + source, data=datagram) 104 | -------------------------------------------------------------------------------- /client/cvra_bootloader/can/frame.py: -------------------------------------------------------------------------------- 1 | class Frame: 2 | """ 3 | A single CAN frame. 4 | """ 5 | 6 | def __init__( 7 | self, id=0, data=None, extended=False, transmission_request=False, data_length=0 8 | ): 9 | 10 | if data is None: 11 | data = bytes() 12 | 13 | if len(data) > 8: 14 | raise ValueError 15 | 16 | self.id = id 17 | 18 | self.data = data 19 | self.data_length = data_length 20 | 21 | if len(self.data) > 0: 22 | self.data_length = len(self.data) 23 | 24 | self.transmission_request = transmission_request 25 | self.extended = extended 26 | 27 | def __eq__(self, other): 28 | return self.id == other.id and self.data == other.data 29 | 30 | def __str__(self): 31 | if self.extended: 32 | frame = "{:08X}".format(self.id) 33 | else: 34 | frame = "{:03X}".format(self.id) 35 | frame += " [{}]".format(self.data_length) 36 | if not self.transmission_request: 37 | for b in self.data: 38 | frame += " {:02X}".format(int(b)) 39 | return frame 40 | -------------------------------------------------------------------------------- /client/cvra_bootloader/can/pcap.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | # See https://wiki.wireshark.org/Development/LibpcapFileFormat 4 | PCAP_HDR_FORMAT = "=LHHlLLL" 5 | PCAP_PKT_HDR_FMT = "=4L" 6 | SOCKETCAN_HDR_FMT = "!LBxxx" 7 | 8 | 9 | def write_header(outfile): 10 | magic = 0xA1B2C3D4 11 | ver_maj, ver_min = 2, 4 12 | thiszone = 0 13 | sigfigs = 0 14 | snaplen = 65535 15 | network = 227 # SocketCAN 16 | 17 | hdr = struct.pack( 18 | PCAP_HDR_FORMAT, magic, ver_maj, ver_min, thiszone, sigfigs, snaplen, network 19 | ) 20 | outfile.write(hdr) 21 | 22 | 23 | def _write_packet_header(outfile, timestamp, packet_length): 24 | # ts_sec, ts_usec = int(timestamp), int(1000000 * 25 | ts_sec = int(timestamp) 26 | ts_usec = int(1000000 * (timestamp - ts_sec)) 27 | 28 | hdr = struct.pack(PCAP_PKT_HDR_FMT, ts_sec, ts_usec, packet_length, packet_length) 29 | outfile.write(hdr) 30 | 31 | 32 | def write_frame(outfile, timestamp, frame): 33 | _write_packet_header(outfile, timestamp, frame.data_length + 8) 34 | 35 | # Check http://www.tcpdump.org/linktypes/LINKTYPE_CAN_SOCKETCAN.html 36 | id_with_flags = frame.id 37 | 38 | if frame.extended: 39 | id_with_flags |= 0x80000000 40 | 41 | if frame.transmission_request: 42 | id_with_flags |= 0x40000000 43 | 44 | data = struct.pack(SOCKETCAN_HDR_FMT, id_with_flags, frame.data_length) + frame.data 45 | outfile.write(data) 46 | -------------------------------------------------------------------------------- /client/cvra_bootloader/change_id.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from cvra_bootloader import commands, utils 3 | 4 | 5 | def parse_commandline_args(): 6 | parser = utils.ConnectionArgumentParser(description="Change a single node ID.") 7 | parser.add_argument("old", type=int, help="Old device ID") 8 | parser.add_argument("new", type=int, help="New device ID") 9 | 10 | return parser.parse_args() 11 | 12 | 13 | def main(): 14 | args = parse_commandline_args() 15 | connection = utils.open_connection(args) 16 | 17 | config = {"ID": args.new} 18 | utils.write_command_retry( 19 | connection, commands.encode_update_config(config), [args.old] 20 | ) 21 | utils.write_command_retry(connection, commands.encode_save_config(), [args.new]) 22 | 23 | 24 | if __name__ == "__main__": 25 | main() 26 | -------------------------------------------------------------------------------- /client/cvra_bootloader/commands.py: -------------------------------------------------------------------------------- 1 | from msgpack import Packer 2 | 3 | COMMAND_SET_VERSION = 2 4 | 5 | 6 | class CommandType: 7 | JumpToMain = 1 8 | CRCReginon = 2 9 | Erase = 3 10 | Write = 4 11 | Ping = 5 12 | Read = 6 13 | UpdateConfig = 7 14 | SaveConfig = 8 15 | ReadConfig = 9 16 | 17 | 18 | def encode_command(command_code, *arguments): 19 | """ 20 | Encodes a command of the given type with given arguments. 21 | """ 22 | p = Packer(use_bin_type=True) 23 | obj = list(arguments) 24 | return p.pack(COMMAND_SET_VERSION) + p.pack(command_code) + p.pack(obj) 25 | 26 | 27 | def encode_crc_region(address, length): 28 | """ 29 | Encodes the command to request the CRC of a region in flash. 30 | """ 31 | return encode_command(CommandType.CRCReginon, address, length) 32 | 33 | 34 | def encode_erase_flash_page(address, device_class): 35 | """ 36 | Encodes the command to erase the flash page at given address. 37 | """ 38 | return encode_command(CommandType.Erase, address, device_class) 39 | 40 | 41 | def encode_write_flash(data, address, device_class): 42 | """ 43 | Encodes the command to write the given data at the given address in a 44 | messagepack byte object. 45 | """ 46 | return encode_command(CommandType.Write, address, device_class, data) 47 | 48 | 49 | def encode_read_flash(aderess, length): 50 | """ 51 | Encodes the command to read the flash at given address. 52 | """ 53 | return encode_command(CommandType.Read, address, length) 54 | 55 | 56 | def encode_update_config(data): 57 | """ 58 | Encodes the command to update the config from given MessagePack data. 59 | """ 60 | return encode_command(CommandType.UpdateConfig, data) 61 | 62 | 63 | def encode_save_config(): 64 | """ 65 | Encodes the command to save the config to flash. 66 | """ 67 | return encode_command(CommandType.SaveConfig) 68 | 69 | 70 | def encode_jump_to_main(): 71 | """ 72 | Encodes the command to jump to application using MessagePack. 73 | """ 74 | return encode_command(CommandType.JumpToMain) 75 | 76 | 77 | def encode_read_config(): 78 | """ 79 | Encodes the read config command. 80 | """ 81 | return encode_command(CommandType.ReadConfig) 82 | 83 | 84 | def encode_ping(): 85 | """ 86 | Encodes a ping command. 87 | """ 88 | return encode_command(CommandType.Ping) 89 | -------------------------------------------------------------------------------- /client/cvra_bootloader/page.py: -------------------------------------------------------------------------------- 1 | def slice_into_pages(data, page_size): 2 | """ 3 | Slices data into chunks that are at max page_size big. 4 | """ 5 | while len(data) > page_size: 6 | yield data[:page_size] 7 | data = data[page_size:] 8 | yield data 9 | -------------------------------------------------------------------------------- /client/cvra_bootloader/read_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from cvra_bootloader import commands, utils 3 | import msgpack 4 | import json 5 | 6 | 7 | def parse_commandline_args(): 8 | """ 9 | Parses the program commandline arguments. 10 | """ 11 | DESCRIPTION = "Read board configs and dumps to JSON" 12 | parser = utils.ConnectionArgumentParser(description=DESCRIPTION) 13 | parser.add_argument( 14 | "ids", metavar="DEVICEID", nargs="*", type=int, help="Device IDs to query" 15 | ) 16 | 17 | parser.add_argument( 18 | "-a", "--all", help="Try to scan the whole bus.", action="store_true" 19 | ) 20 | 21 | return parser.parse_args() 22 | 23 | 24 | def main(): 25 | args = parse_commandline_args() 26 | connection = utils.open_connection(args) 27 | 28 | if args.all: 29 | scan_queue = list() 30 | 31 | # Broadcast ping 32 | utils.write_command(connection, commands.encode_ping(), list(range(1, 128))) 33 | reader = utils.read_can_datagrams(connection) 34 | while True: 35 | dt = next(reader) 36 | 37 | if dt is None: # Timeout 38 | break 39 | 40 | _, _, src = dt 41 | scan_queue.append(src) 42 | 43 | else: 44 | scan_queue = args.ids 45 | 46 | # Broadcast ask for config 47 | configs = utils.write_command_retry( 48 | connection, commands.encode_read_config(), scan_queue 49 | ) 50 | 51 | for id, raw_config in configs.items(): 52 | configs[id] = msgpack.unpackb(raw_config, encoding="ascii") 53 | 54 | print(json.dumps(configs, indent=4, sort_keys=True)) 55 | 56 | 57 | if __name__ == "__main__": 58 | main() 59 | -------------------------------------------------------------------------------- /client/cvra_bootloader/run_application.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from cvra_bootloader import commands, utils 3 | 4 | 5 | def parse_commandline_args(): 6 | parser = utils.ConnectionArgumentParser( 7 | description="Send a jump to application command." 8 | ) 9 | parser.add_argument( 10 | "ids", 11 | metavar="DEVICEID", 12 | nargs="*", 13 | type=int, 14 | help="Device IDs to send jump command", 15 | ) 16 | parser.add_argument( 17 | "-a", "--all", help="Try to scan all network.", action="store_true" 18 | ) 19 | 20 | return parser.parse_args() 21 | 22 | 23 | def main(): 24 | args = parse_commandline_args() 25 | connection = utils.open_connection(args) 26 | if args.all is True: 27 | ids = list(range(1, 128)) 28 | else: 29 | ids = args.ids 30 | 31 | utils.write_command(connection, commands.encode_jump_to_main(), ids) 32 | 33 | 34 | if __name__ == "__main__": 35 | main() 36 | -------------------------------------------------------------------------------- /client/cvra_bootloader/write_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import json 5 | 6 | from cvra_bootloader import utils 7 | 8 | 9 | def parse_commandline_args(): 10 | """ 11 | Parses the program commandline arguments. 12 | Args must be an array containing all arguments. 13 | """ 14 | 15 | epilog = """ 16 | The configuration file must contained a JSON-encoded map. Example: "{"name":"foo"}". 17 | """ 18 | 19 | parser = utils.ConnectionArgumentParser( 20 | description="Update config (key/value pairs) on a board", epilog=epilog 21 | ) 22 | parser.add_argument( 23 | "-c", 24 | "--config", 25 | help="JSON file to load config from (default stdin)", 26 | type=open, 27 | default=sys.stdin, 28 | dest="file", 29 | ) 30 | parser.add_argument( 31 | "ids", metavar="DEVICEID", nargs="+", type=int, help="Device IDs to flash" 32 | ) 33 | 34 | return parser.parse_args() 35 | 36 | 37 | def main(): 38 | args = parse_commandline_args() 39 | config = json.loads(args.file.read()) 40 | 41 | if "ID" in config.keys(): 42 | print("This tool cannot be used to change node IDs.") 43 | print("Use bootloader_change_id.py instead.") 44 | sys.exit(1) 45 | 46 | connection = utils.open_connection(args) 47 | utils.config_update_and_save(connection, config, args.ids) 48 | 49 | 50 | if __name__ == "__main__": 51 | main() 52 | -------------------------------------------------------------------------------- /client/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="cvra-bootloader", 5 | version="2.2.0", 6 | description="Client for the CVRA CAN bootloader", 7 | author="Club Vaudois de Robotique Autonome", 8 | author_email="info@cvra.ch", 9 | url="https://github.com/cvra/can-bootloader", 10 | license="BSD", 11 | packages=["cvra_bootloader"], 12 | classifiers=[ 13 | "Development Status :: 5 - Production/Stable", 14 | "Intended Audience :: Developers", 15 | "Topic :: Software Development :: Embedded Systems", 16 | "License :: OSI Approved :: BSD License", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Programming Language :: Python :: 3.4", 19 | "Programming Language :: Python :: 3.5", 20 | ], 21 | install_requires=[ 22 | "msgpack-python", 23 | "pyserial", 24 | "progressbar2", 25 | ], 26 | entry_points={ 27 | "console_scripts": [ 28 | "bootloader_flash=cvra_bootloader.bootloader_flash:main", 29 | "bootloader_change_id=cvra_bootloader.change_id:main", 30 | "bootloader_read_config=cvra_bootloader.read_config:main", 31 | "bootloader_run_app=cvra_bootloader.run_application:main", 32 | "bootloader_write_config=cvra_bootloader.write_config:main", 33 | ], 34 | }, 35 | ) 36 | -------------------------------------------------------------------------------- /client/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvra/can-bootloader/1e90b1d16ed1d2d248d81b0e0e1f7be7a2aac750/client/tests/__init__.py -------------------------------------------------------------------------------- /client/tests/adapters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cvra/can-bootloader/1e90b1d16ed1d2d248d81b0e0e1f7be7a2aac750/client/tests/adapters/__init__.py -------------------------------------------------------------------------------- /client/tests/adapters/test_socketcan.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | try: 4 | from unittest.mock import * 5 | except ImportError: 6 | from mock import * 7 | 8 | 9 | import socket 10 | import struct 11 | from cvra_bootloader.can.adapters import SocketCANConnection 12 | from cvra_bootloader.can import Frame 13 | 14 | # SocketCAN compatibility shims for OSX 15 | try: 16 | from socket import AF_CAN 17 | except ImportError: 18 | socket.AF_CAN = "AF_CAN" 19 | socket.CAN_RAW = "CAN_RAW" 20 | 21 | 22 | @patch("socket.socket") 23 | class SocketCANTestCase(TestCase): 24 | def test_can_open_connection(self, socket_create): 25 | """ 26 | Checks that we can correctly open a connection. 27 | """ 28 | SocketCANConnection("vcan0") 29 | 30 | socket_create.assert_any_call(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW) 31 | 32 | socket_create.return_value.bind.assert_any_call(("vcan0",)) 33 | socket_create.return_value.settimeout.assert_any_call(ANY) 34 | socket_create.return_value.setsockopt.assert_any_call( 35 | socket.SOL_SOCKET, socket.SO_SNDBUF, 4096 36 | ) 37 | 38 | def test_can_send_frame(self, socket_create): 39 | s = SocketCANConnection("vcan0") 40 | s.socket = Mock() 41 | 42 | frame = Frame(id=42, data=bytes((0xDE, 0xAD, 0xBE, 0xEF))) 43 | 44 | # See for format 45 | # Data field must be zero padded 46 | can_frame = struct.pack( 47 | "=IB3x8s", 42, 4, bytes((0xDE, 0xAD, 0xBE, 0xEF, 0x0, 0x0, 0x0, 0x0)) 48 | ) 49 | 50 | s.send_frame(frame) 51 | 52 | s.socket.send.assert_any_call(can_frame) 53 | 54 | def test_receive_frame(self, socket_create): 55 | s = SocketCANConnection("vcan0") 56 | s.socket = Mock() 57 | 58 | expected_frame = Frame(id=42, data=bytes((0xDE, 0xAD, 0xBE, 0xEF))) 59 | 60 | can_frame = struct.pack( 61 | "=IB3x8s", 42, 4, bytes((0xDE, 0xAD, 0xBE, 0xEF, 0x0, 0x0, 0x0, 0x0)) 62 | ) 63 | 64 | s.socket.recvfrom.return_value = (can_frame, ("vcan0")) 65 | 66 | actual_frame = s.receive_frame() 67 | 68 | self.assertEqual(expected_frame, actual_frame) 69 | 70 | def test_receive_frame_timeout(self, socket_create): 71 | s = SocketCANConnection("vcan0") 72 | s.socket = Mock() 73 | s.socket.recvfrom.side_effect = [socket.timeout] 74 | 75 | s.receive_frame() 76 | -------------------------------------------------------------------------------- /client/tests/test_change_id_tool.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | try: 4 | from unittest.mock import * 5 | except ImportError: 6 | from mock import * 7 | 8 | import sys 9 | from cvra_bootloader.change_id import main 10 | import cvra_bootloader.commands 11 | 12 | 13 | class BootloaderChangeIdTestCase(unittest.TestCase): 14 | @patch("cvra_bootloader.utils.write_command_retry") 15 | @patch("cvra_bootloader.utils.open_connection") 16 | def test_integration(self, open_connection, write_command): 17 | sys.argv = "test.py -p /dev/ttyUSB0 1 2".split() 18 | open_connection.return_value = object() 19 | 20 | main() 21 | 22 | command = cvra_bootloader.commands.encode_update_config({"ID": 2}) 23 | write_command.assert_any_call(open_connection.return_value, command, [1]) 24 | 25 | command = cvra_bootloader.commands.encode_save_config() 26 | write_command.assert_any_call(open_connection.return_value, command, [2]) 27 | -------------------------------------------------------------------------------- /client/tests/test_commands.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from cvra_bootloader.commands import * 3 | from msgpack import Unpacker 4 | 5 | 6 | class ProtocolVersionTestCase(unittest.TestCase): 7 | """ 8 | This testcase checks that the command set version is correctly handled. 9 | """ 10 | 11 | def test_has_correct_protocol_version(self): 12 | """ 13 | Checks that the command encoding function works corectly. 14 | """ 15 | raw_packet = encode_command(command_code=10) 16 | 17 | unpacker = Unpacker() 18 | unpacker.feed(raw_packet) 19 | 20 | version, *_ = list(unpacker) 21 | self.assertEqual(2, version) 22 | 23 | 24 | class WriteCommandTestCase(unittest.TestCase): 25 | """ 26 | This testcase checks if the command used to write to a page functions correctly. 27 | 28 | See README.markdown for more information on the command format. 29 | """ 30 | 31 | def setUp(self): 32 | address = 0xDEADBEEF 33 | data = bytes(range(4)) 34 | device = "dummy" 35 | 36 | raw_packet = encode_write_flash(data, address, device) 37 | 38 | unpacker = Unpacker() 39 | unpacker.feed(raw_packet) 40 | # Discards command set version 41 | self.command = list(unpacker)[1:] 42 | 43 | def test_command_has_correct_index(self): 44 | """ 45 | Checks that the command index is correct. It should be 0x3. 46 | """ 47 | index = self.command[0] 48 | self.assertEqual(CommandType.Write, index) 49 | 50 | def test_command_has_correct_address(self): 51 | """ 52 | Checks that the address is put at the correct place in the command. 53 | """ 54 | address = self.command[1][0] 55 | self.assertEqual(0xDEADBEEF, address) 56 | 57 | def test_device_class(self): 58 | """ 59 | Checks that the third element of the command is the device class. 60 | """ 61 | device = self.command[1][1].decode("ascii") 62 | self.assertEqual("dummy", device) 63 | 64 | def test_device_data(self): 65 | """ 66 | Checks that the last item is the data. 67 | """ 68 | data = self.command[1][2] 69 | self.assertEqual(bytes([0, 1, 2, 3]), data) 70 | 71 | def test_write_command_use_binary_type(self): 72 | """ 73 | Checks that the write command uses bin type. 74 | """ 75 | raw_packet = encode_write_flash(bytes([12]), address=1, device_class="dummy") 76 | 77 | # 0xc4 is binary type marker 78 | self.assertEqual(raw_packet[-3], 0xC4) 79 | 80 | # 1 is length 81 | self.assertEqual(raw_packet[-2], 1) 82 | 83 | # Finally data 84 | self.assertEqual(raw_packet[-1], 12) 85 | 86 | 87 | class EraseCommandTestCase(unittest.TestCase): 88 | """ 89 | Tests for the erase flash page command. 90 | """ 91 | 92 | def setUp(self): 93 | address = 0xFA1AFE1 94 | device = "LivewareProblem" 95 | 96 | raw_packet = encode_erase_flash_page(address, device) 97 | 98 | unpacker = Unpacker() 99 | unpacker.feed(raw_packet) 100 | self.command = list(unpacker)[1:] 101 | 102 | def test_erase_command_index(self): 103 | """ 104 | Checks that the index for the command is correct. 105 | """ 106 | self.assertEqual(self.command[0], CommandType.Erase) 107 | 108 | def test_erase_command_address(self): 109 | """ 110 | Checks that the page addres is correct. 111 | """ 112 | self.assertEqual(self.command[1][0], 0xFA1AFE1) 113 | 114 | def test_erase_command_device(self): 115 | """ 116 | Checks that the device name is correct. 117 | """ 118 | self.assertEqual(self.command[1][1].decode("ascii"), "LivewareProblem") 119 | 120 | 121 | class JumpToApplicationMainTestCase(unittest.TestCase): 122 | """ 123 | Tests for the jump to application main command. 124 | """ 125 | 126 | def setUp(self): 127 | raw_packet = encode_jump_to_main() 128 | unpacker = Unpacker() 129 | unpacker.feed(raw_packet) 130 | self.command = list(unpacker)[1:] 131 | 132 | def test_jump_cmd_index(self): 133 | """ 134 | Checks that the index for the command is correct. 135 | """ 136 | self.assertEqual(self.command[0], CommandType.JumpToMain) 137 | 138 | 139 | class PingTestCase(unittest.TestCase): 140 | """ 141 | Checks that the ping command is properly encoded. 142 | """ 143 | 144 | def setUp(self): 145 | raw_packet = encode_ping() 146 | unpacker = Unpacker() 147 | unpacker.feed(raw_packet) 148 | self.command = list(unpacker)[1:] 149 | 150 | def test_ping(self): 151 | self.assertEqual(self.command[0], 5) 152 | -------------------------------------------------------------------------------- /client/tests/test_config_read_tool.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | try: 4 | from unittest.mock import * 5 | except ImportError: 6 | from mock import * 7 | 8 | from msgpack import * 9 | 10 | from cvra_bootloader.read_config import main 11 | from cvra_bootloader.commands import * 12 | import sys 13 | import json 14 | 15 | 16 | class ReadConfigToolTestCase(unittest.TestCase): 17 | @patch("cvra_bootloader.utils.write_command_retry") 18 | @patch("cvra_bootloader.utils.write_command") 19 | @patch("cvra_bootloader.utils.open_connection") 20 | @patch("builtins.print") 21 | def test_integration( 22 | self, print_mock, open_conn, write_command, write_command_retry 23 | ): 24 | sys.argv = "test.py -p /dev/ttyUSB0 0 1 2".split() 25 | configs = [{"id": i} for i in range(3)] 26 | 27 | write_command_retry.return_value = {i: packb(configs[i]) for i in range(3)} 28 | 29 | open_conn.return_value = object() 30 | 31 | main() 32 | 33 | write_command_retry.assert_any_call( 34 | open_conn.return_value, encode_read_config(), [0, 1, 2] 35 | ) 36 | 37 | all_configs = {i: configs[i] for i in range(3)} 38 | 39 | print_mock.assert_any_call(json.dumps(all_configs, indent=4, sort_keys=True)) 40 | 41 | @patch("cvra_bootloader.utils.open_connection") 42 | @patch("cvra_bootloader.utils.write_command_retry") 43 | @patch("cvra_bootloader.utils.write_command") 44 | @patch("cvra_bootloader.utils.read_can_datagrams") 45 | @patch("builtins.print") 46 | def test_network_discovery( 47 | self, 48 | print_mock, 49 | read_can_datagram, 50 | write_command, 51 | write_command_retry, 52 | open_conn, 53 | ): 54 | """ 55 | Checks if we can perform a whole network discovery. 56 | """ 57 | sys.argv = "test.py -p /dev/ttyUSB0 --all".split() 58 | 59 | # The first two board answers the ping 60 | board_answers = [(b"", [0], i) for i in range(1, 3)] + [None] 61 | 62 | read_can_datagram.return_value = iter(board_answers) 63 | 64 | write_command_retry.return_value = {i: packb({"id": i}) for i in range(1, 3)} 65 | 66 | main() 67 | write_command.assert_any_call( 68 | open_conn.return_value, encode_ping(), list(range(1, 128)) 69 | ) 70 | -------------------------------------------------------------------------------- /client/tests/test_config_write_tool.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | try: 4 | from unittest.mock import * 5 | except ImportError: 6 | from mock import * 7 | 8 | from cvra_bootloader.write_config import main 9 | from io import StringIO 10 | import sys 11 | 12 | 13 | class WriteConfigToolTestCase(unittest.TestCase): 14 | @patch("cvra_bootloader.utils.config_update_and_save") 15 | @patch("cvra_bootloader.utils.open_connection") 16 | @patch("builtins.open") 17 | def test_integration(self, open_mock, open_conn, config_save): 18 | sys.argv = "test.py -c test.json -p /dev/ttyUSB0 1 2 3".split() 19 | config_file = '{"foo":12}' 20 | 21 | open_mock.return_value = StringIO(config_file) 22 | open_conn.return_value = object() 23 | 24 | main() 25 | 26 | open_mock.assert_any_call("test.json") 27 | config_save.assert_any_call(open_conn.return_value, {"foo": 12}, [1, 2, 3]) 28 | 29 | @patch("builtins.open") 30 | @patch("builtins.print") 31 | def test_fails_on_ID_change(self, print_mock, open_mock): 32 | """ 33 | Checks that this tool refuses to change a Node ID. 34 | """ 35 | sys.argv = "test.py -c nonexisting.json -p /dev/ttyUSB0 1 2 3".split() 36 | config_file = '{"ID":11}' 37 | 38 | open_mock.return_value = StringIO(config_file) 39 | 40 | with self.assertRaises(SystemExit): 41 | main() 42 | -------------------------------------------------------------------------------- /client/tests/test_datagram_reader.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | try: 4 | from unittest.mock import Mock 5 | except ImportError: 6 | from mock import Mock 7 | 8 | import cvra_bootloader.can 9 | from cvra_bootloader.utils import read_can_datagrams 10 | 11 | 12 | class CANDatagramReaderTestCase(unittest.TestCase): 13 | """ 14 | This testcase groups all tests related to reading a datagram from the bus. 15 | """ 16 | 17 | def test_read_can_datagram(self): 18 | """ 19 | Tests reading a complete CAN datagram from the bus. 20 | """ 21 | data = "Hello world".encode("ascii") 22 | # Encapsulates it in a CAN datagram 23 | data = cvra_bootloader.can.encode_datagram(data, destinations=[1]) 24 | 25 | # Slice the datagram in frames 26 | frames = list(cvra_bootloader.can.datagram_to_frames(data, source=42)) 27 | 28 | # Prepares a pseudo CAN adapter 29 | fdesc = Mock() 30 | fdesc.receive_frame.side_effect = frames 31 | 32 | reader = read_can_datagrams(fdesc) 33 | 34 | # Read a CAN datagram from that pseudofile 35 | dt, dst, src = next(reader) 36 | 37 | self.assertEqual(dt.decode("ascii"), "Hello world") 38 | self.assertEqual(dst, [1]) 39 | self.assertEqual(src, 42) 40 | 41 | def test_drop_extended_frames(self): 42 | """ 43 | Checks that we drop extended frames 44 | """ 45 | data = "Hello world".encode("ascii") 46 | # Encapsulates it in a CAN datagram 47 | data = cvra_bootloader.can.encode_datagram(data, destinations=[1]) 48 | 49 | # Slice the datagram in frames 50 | frames = list(cvra_bootloader.can.datagram_to_frames(data, source=42)) 51 | 52 | # Add an extended frame, with an annoying ID 53 | id = frames[0].id 54 | frames = [ 55 | cvra_bootloader.can.Frame(extended=True, data=bytes([1, 2, 3]), id=id) 56 | ] + frames 57 | 58 | # Prepares a pseudo CAN adapter 59 | fdesc = Mock() 60 | fdesc.receive_frame.side_effect = frames 61 | 62 | reader = read_can_datagrams(fdesc) 63 | 64 | # Read a CAN datagram from that pseudofile 65 | dt, dst, src = next(reader) 66 | 67 | self.assertEqual(dt.decode("ascii"), "Hello world") 68 | self.assertEqual(dst, [1]) 69 | self.assertEqual(src, 42) 70 | 71 | def test_read_can_interleaved_datagrams(self): 72 | """ 73 | Tests reading two interleaved CAN datagrams together. 74 | """ 75 | 76 | data = "Hello world".encode("ascii") 77 | 78 | # Encapsulates it in a CAN datagram 79 | data = cvra_bootloader.can.encode_datagram(data, destinations=[1]) 80 | 81 | # Slice the datagram in frames 82 | frames = [ 83 | cvra_bootloader.can.datagram_to_frames(data, source=i) for i in range(2) 84 | ] 85 | 86 | # Interleave frames 87 | frames = [x for t in zip(*frames) for x in t] 88 | 89 | # Prepares a pseudo CAN adapter 90 | fdesc = Mock() 91 | fdesc.receive_frame.side_effect = frames 92 | 93 | decode = read_can_datagrams(fdesc) 94 | 95 | # Read the two datagrams 96 | _, _, src = next(decode) 97 | self.assertEqual(0, src) 98 | _, _, src = next(decode) 99 | self.assertEqual(1, src) 100 | 101 | def test_read_several_datagrams_from_src(self): 102 | """ 103 | Checks if we can read several datagrams from the same source. 104 | """ 105 | frames = [] 106 | 107 | for i in range(2): 108 | # Encapsulates it in a CAN datagram 109 | dt = cvra_bootloader.can.encode_datagram(bytes(), destinations=[i]) 110 | frames += list(cvra_bootloader.can.datagram_to_frames(dt, source=1)) 111 | 112 | # Prepares a pseudo CAN adapter 113 | fdesc = Mock() 114 | fdesc.receive_frame.side_effect = frames 115 | 116 | decode = read_can_datagrams(fdesc) 117 | 118 | # Read a CAN datagram from that pseudofile 119 | _, dst, _ = next(decode) 120 | self.assertEqual([0], dst) 121 | 122 | _, dst, _ = next(decode) 123 | self.assertEqual([1], dst) 124 | 125 | def test_read_can_datagram_timeout(self): 126 | """ 127 | Tests reading a datagram with a timeout (read_datagram returns None). 128 | """ 129 | # Prepares a pseudo CAN adapter that always timeout 130 | fdesc = Mock() 131 | fdesc.receive_frame.return_value = None 132 | 133 | reader = read_can_datagrams(fdesc) 134 | 135 | # Check that the timeout is returned 136 | self.assertIsNone(next(reader)) 137 | 138 | def test_recover_from_timeout(self): 139 | """ 140 | Tests reading a CAN datagram after a timeout. 141 | """ 142 | data = "Hello world".encode("ascii") 143 | # Encapsulates it in a CAN datagram 144 | data = cvra_bootloader.can.encode_datagram(data, destinations=[1]) 145 | 146 | # Slice the datagram in frames 147 | frames = list(cvra_bootloader.can.datagram_to_frames(data, source=42)) 148 | 149 | fdesc = Mock() 150 | 151 | # Insert a frame receive timeout 152 | fdesc.receive_frame.side_effect = frames[:1] + [None] + frames[1:] 153 | 154 | reader = read_can_datagrams(fdesc) 155 | 156 | # Check that the timeout is reported 157 | a = next(reader) 158 | self.assertIsNone(a) 159 | 160 | # Now try to read the real CAN datagram 161 | dt, dst, src = next(reader) 162 | 163 | self.assertEqual(dt.decode("ascii"), "Hello world") 164 | self.assertEqual(dst, [1]) 165 | self.assertEqual(src, 42) 166 | -------------------------------------------------------------------------------- /client/tests/test_pagination.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from cvra_bootloader.page import * 3 | 4 | 5 | class PaginationTestCase(unittest.TestCase): 6 | def test_smaller_than_page_is_yielded(self): 7 | """ 8 | Tests that a page smaller than the page size is yielded entierly. 9 | """ 10 | b = bytes([1]) 11 | p = slice_into_pages(b, page_size=4) 12 | self.assertEqual(next(p), b) 13 | 14 | def test_can_cut_into_subpages(self): 15 | """ 16 | Tests that a page is split into subpages. 17 | """ 18 | b = bytes(range(17)) 19 | p = slice_into_pages(b, page_size=4) 20 | 21 | self.assertEqual(next(p), bytes(range(0, 4))) 22 | self.assertEqual(next(p), bytes(range(4, 8))) 23 | self.assertEqual(next(p), bytes(range(8, 12))) 24 | self.assertEqual(next(p), bytes(range(12, 16))) 25 | self.assertEqual(next(p), bytes([16])) 26 | -------------------------------------------------------------------------------- /client/tests/test_pcap_file.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import io 3 | import struct 4 | from cvra_bootloader.can.frame import Frame 5 | from cvra_bootloader.can.pcap import * 6 | from socket import ntohl 7 | 8 | # needed to expose private api 9 | from cvra_bootloader.can.pcap import _write_packet_header 10 | 11 | 12 | class PCAPTest(unittest.TestCase): 13 | def test_write_file_header(self): 14 | f = io.BytesIO() 15 | write_header(f) 16 | 17 | header = struct.unpack("=LHHlLLL", f.getvalue()) 18 | self.assertEqual(0xA1B2C3D4, header[0], "bad magic header") 19 | self.assertEqual(2, header[1], "bad version major") 20 | self.assertEqual(4, header[2], "bad version minor") 21 | self.assertEqual(0, header[3], "timezone is not GMT") 22 | # Sigfigs is not really used 23 | self.assertEqual(65535, header[5], "bad snapshot length") 24 | self.assertEqual(227, header[6], "network type is not socketcan") 25 | 26 | def test_write_packet_header(self): 27 | f = io.BytesIO() 28 | ts = 1000.5 29 | packet_length = 10 30 | _write_packet_header(f, ts, packet_length) 31 | 32 | header = struct.unpack("=4L", f.getvalue()) 33 | 34 | self.assertEqual(1000, header[0], "Invalid ts_sec") 35 | self.assertEqual(500000, header[1], "Invalid ts_usec") 36 | self.assertEqual(10, header[2], "Invalid incl_len") 37 | self.assertEqual(10, header[3], "Invalid orig_len") 38 | 39 | def test_write_frame(self): 40 | f = io.BytesIO() 41 | ts = 1000.5 42 | data = "hello".encode() 43 | frame = Frame(0xCAFE, data, extended=True, transmission_request=True) 44 | 45 | write_frame(f, ts, frame) 46 | 47 | fmt = PCAP_PKT_HDR_FMT + "LBxxx" + "5s" 48 | data = struct.unpack(fmt, f.getvalue()) 49 | 50 | # Check CAN ID (see http://www.tcpdump.org/linktypes/LINKTYPE_CAN_SOCKETCAN.html) 51 | expected_id = 0x40000000 + 0x80000000 + 0xCAFE 52 | 53 | # The ID is stored in network byte order so we need to convert it to 54 | # host byte order before sending it 55 | self.assertEqual(data[4], ntohl(expected_id), "wrong can id") 56 | self.assertEqual(data[5], 5, "wrong frame length") 57 | -------------------------------------------------------------------------------- /client/tests/test_run_application.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | try: 4 | from unittest.mock import * 5 | except ImportError: 6 | from mock import * 7 | 8 | import sys 9 | 10 | from cvra_bootloader.run_application import main 11 | 12 | from cvra_bootloader import commands 13 | 14 | 15 | class BootloaderRunApplicationTestCase(TestCase): 16 | @patch("cvra_bootloader.utils.write_command") 17 | @patch("cvra_bootloader.utils.open_connection") 18 | def test_integration(self, open_connection, write_command): 19 | sys.argv = "test.py -p /dev/ttyUSB0 1 2".split() 20 | open_connection.return_value = object() 21 | 22 | main() 23 | 24 | command = commands.encode_jump_to_main() 25 | write_command.assert_any_call(open_connection.return_value, command, [1, 2]) 26 | -------------------------------------------------------------------------------- /client/tests/tests_msgpack.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from msgpack import * 4 | 5 | 6 | class MessagePackTestCase(unittest.TestCase): 7 | """ 8 | This is not really a comprehensive test suite for messagepack but instead a 9 | way to learn how to use the api. 10 | """ 11 | 12 | def test_can_pack_fixarray(self): 13 | """ 14 | Checks that we can pack a fix array (len(array) < 16). 15 | """ 16 | data = [1, 2, 3] 17 | expected = bytes([0x93, 1, 2, 3]) 18 | self.assertEqual(expected, packb(data)) 19 | 20 | def test_can_pack_bytes(self): 21 | """ 22 | Checks that we can use binary types. By default msgpack uses str types 23 | for bytes() so we need to use a Packer object correctly configured. 24 | """ 25 | packer = Packer(use_bin_type=True) 26 | data = bytes([0, 1, 2, 3]) 27 | 28 | # Format is 0xc4, lenght, data 29 | expected = bytes([0xC4, 4, 0, 1, 2, 3]) 30 | self.assertEqual(expected, packer.pack(data)) 31 | 32 | def test_can_unpack_multiple_values(self): 33 | """ 34 | Checks that we can unpack a stream of value as used in the command format. 35 | """ 36 | packer = Packer(use_bin_type=True) 37 | 38 | # Creates command stream 39 | data = packb(1) + packb([1, 2, 3]) 40 | 41 | # Stream deserializes it 42 | a = Unpacker() 43 | a.feed(data) 44 | 45 | self.assertEqual(list(a), [1, [1, 2, 3]]) 46 | -------------------------------------------------------------------------------- /command.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMAND_H_ 2 | #define COMMAND_H_ 3 | 4 | #include 5 | #include 6 | #include "config.h" 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /** Invalid command format. 13 | * 14 | * Means that the command format is invalid, for example if the command index 15 | * is not an integer. */ 16 | #define ERR_INVALID_COMMAND 1 17 | 18 | /** Command index could not be found in command table. */ 19 | #define ERR_COMMAND_NOT_FOUND 2 20 | 21 | /** Protocol command set version doesn't match received one. */ 22 | #define ERR_INVALID_COMMAND_SET_VERSION 3 23 | 24 | /** Version of the protocol command set. */ 25 | #define COMMAND_SET_VERSION 2 26 | 27 | typedef struct { 28 | /** Command ID */ 29 | uint8_t index; 30 | /** The callback function for this entry. 31 | * 32 | * @param [in] argc Argument count 33 | * @param [in] arg_context MessagePack context containing the parameters. 34 | * @param [in] out_context MessagePack context in which the output data should be written. 35 | * @param [in] config A bootloader config instance. 36 | */ 37 | void (*callback)(int, cmp_ctx_t*, cmp_ctx_t*, bootloader_config_t* config); 38 | } command_t; 39 | 40 | /** Parses a datagram data field and executes the correct function. 41 | * @param [in] data The raw data to parse. 42 | * @param [in] data_len Length of data. 43 | * @param [in] commands A list of all possible commands. 44 | * @param [in] command_len Length of the commands array. 45 | * @param [in] config A pointer to a bootloader configuration structure that will be passed to the commands. 46 | * @param [out] out_buf A buffer where the command can place its results, which will be sent back to the client. 47 | * @param [in] out_len Length of out_buf. 48 | * @returns The amount of bytes written to output_buffer on successful command completion. 49 | * @returns A negative error code if the command execution encountered an error. 50 | */ 51 | int protocol_execute_command(char* data, size_t data_len, const command_t* commands, int command_len, char* out_buf, size_t out_len, bootloader_config_t* config); 52 | 53 | /** Command used to erase a flash page. 54 | * 55 | * @note Should not be called directly but be a part of the commands given to protocol_execute_command. 56 | */ 57 | void command_erase_flash_page(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 58 | 59 | /** Command used to write to a flash page. 60 | * 61 | * @note Should not be called directly but be a part of the commands given to protocol_execute_command. 62 | */ 63 | void command_write_flash(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 64 | 65 | /** Command used to read from flash. 66 | * 67 | * @note Should not be called directly but be a part of the commands given to protocol_execute_command. 68 | */ 69 | void command_read_flash(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 70 | 71 | /** Command used to compute the CRC of a flash page. */ 72 | void command_crc_region(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 73 | 74 | /** Command used to jump to the application code. 75 | * 76 | * @note Should not be called directly but be a part of the commands given to protocol_execute_command. 77 | */ 78 | void command_jump_to_application(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 79 | 80 | /** Command used to update entries of confit in RAM. */ 81 | void command_config_update(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 82 | 83 | /** Command used to write config to flash. */ 84 | void command_config_write_to_flash(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 85 | 86 | /** Reads the current config and sends as MessagePack encoded map. */ 87 | void command_config_read(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 88 | 89 | /** Ping command. Simply replies with true. */ 90 | void command_ping(int argc, cmp_ctx_t* args, cmp_ctx_t* out, bootloader_config_t* config); 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "config.h" 5 | 6 | static uint32_t config_calculate_crc(void* page, size_t page_size) 7 | { 8 | return crc32(0, (uint8_t*)page + 4, page_size - 4); 9 | } 10 | 11 | bool config_is_valid(void* page, size_t page_size) 12 | { 13 | uint32_t crc = 0; 14 | uint8_t* p = page; 15 | crc |= p[0] << 24; 16 | crc |= p[1] << 16; 17 | crc |= p[2] << 8; 18 | crc |= p[3] << 0; 19 | return crc == config_calculate_crc(page, page_size); 20 | } 21 | 22 | void config_write(void* buffer, bootloader_config_t* config, size_t buffer_size) 23 | { 24 | cmp_ctx_t context; 25 | cmp_mem_access_t cma; 26 | uint8_t* p = buffer; 27 | 28 | cmp_mem_access_init(&context, &cma, &p[4], buffer_size - 4); 29 | config_write_messagepack(&context, config); 30 | 31 | uint32_t crc = config_calculate_crc(buffer, buffer_size); 32 | p[0] = ((crc >> 24) & 0xff); 33 | p[1] = ((crc >> 16) & 0xff); 34 | p[2] = ((crc >> 8) & 0xff); 35 | p[3] = (crc & 0xff); 36 | } 37 | 38 | void config_write_messagepack(cmp_ctx_t* context, bootloader_config_t* config) 39 | { 40 | cmp_write_map(context, 6); 41 | 42 | cmp_write_str(context, "ID", 2); 43 | cmp_write_u8(context, config->ID); 44 | 45 | cmp_write_str(context, "name", 4); 46 | cmp_write_str(context, config->board_name, strlen(config->board_name)); 47 | 48 | cmp_write_str(context, "device_class", 12); 49 | cmp_write_str(context, config->device_class, strlen(config->device_class)); 50 | 51 | cmp_write_str(context, "application_crc", 15); 52 | cmp_write_uint(context, config->application_crc); 53 | 54 | cmp_write_str(context, "application_size", 16); 55 | cmp_write_uint(context, config->application_size); 56 | 57 | cmp_write_str(context, "update_count", 12); 58 | cmp_write_uint(context, config->update_count); 59 | } 60 | 61 | bootloader_config_t config_read(void* buffer, size_t buffer_size) 62 | { 63 | bootloader_config_t result; 64 | 65 | cmp_ctx_t context; 66 | cmp_mem_access_t cma; 67 | 68 | cmp_mem_access_ro_init(&context, &cma, (uint8_t*)buffer + 4, buffer_size - 4); 69 | config_update_from_serialized(&result, &context); 70 | 71 | return result; 72 | } 73 | 74 | void config_update_from_serialized(bootloader_config_t* config, cmp_ctx_t* context) 75 | { 76 | uint32_t key_count = 0; 77 | char key[64]; 78 | uint32_t key_len; 79 | 80 | cmp_read_map(context, &key_count); 81 | 82 | while (key_count--) { 83 | key_len = sizeof key; 84 | cmp_read_str(context, key, &key_len); 85 | key[key_len] = 0; 86 | 87 | if (!strcmp("ID", key)) { 88 | cmp_read_uchar(context, &config->ID); 89 | } 90 | 91 | if (!strcmp("name", key)) { 92 | uint32_t name_len = 65; 93 | cmp_read_str(context, config->board_name, &name_len); 94 | } 95 | 96 | if (!strcmp("device_class", key)) { 97 | uint32_t name_len = 65; 98 | cmp_read_str(context, config->device_class, &name_len); 99 | } 100 | 101 | if (!strcmp("application_crc", key)) { 102 | cmp_read_uint(context, &config->application_crc); 103 | } 104 | 105 | if (!strcmp("application_size", key)) { 106 | cmp_read_uint(context, &config->application_size); 107 | } 108 | 109 | if (!strcmp("update_count", key)) { 110 | cmp_read_uint(context, &config->update_count); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct { 13 | uint8_t ID; /**< Node ID */ 14 | char board_name[64 + 1]; /**< Node human readable name, eg: 'arms.left.shoulder'. */ 15 | char device_class[64 + 1]; /**< Node device class example : 'CVRA.motorboard.v1'*/ 16 | uint32_t application_crc; 17 | uint32_t application_size; 18 | uint32_t update_count; 19 | } bootloader_config_t; 20 | 21 | /** Returns true if the given config page is valid. */ 22 | bool config_is_valid(void* page, size_t page_size); 23 | 24 | /** Serializes the configuration to the buffer. 25 | * 26 | * It must be done on a RAM buffer because we will modify it non sequentially, to add CRC. 27 | */ 28 | void config_write(void* buffer, bootloader_config_t* config, size_t buffer_size); 29 | 30 | /** Serializes the config into a messagepack map. */ 31 | void config_write_messagepack(cmp_ctx_t* context, bootloader_config_t* config); 32 | 33 | bootloader_config_t config_read(void* buffer, size_t buffer_size); 34 | 35 | /** Updates the config from the keys found in the messagepack map. 36 | * 37 | * @note Keys not in the MessagePack map are left unchanged. 38 | */ 39 | void config_update_from_serialized(bootloader_config_t* config, cmp_ctx_t* context); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /flash_writer.h: -------------------------------------------------------------------------------- 1 | #ifndef FLASH_WRITER_H 2 | #define FLASH_WRITER_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** Initializes anything needed before flash can be accessed */ 12 | void flash_init(void); 13 | 14 | /** Unlocks the flash for programming. */ 15 | void flash_writer_unlock(void); 16 | 17 | /** Locks the flash */ 18 | void flash_writer_lock(void); 19 | 20 | /** Erases the flash page at given address. */ 21 | void flash_writer_page_erase(void* page); 22 | 23 | /** Writes data to given location in flash. */ 24 | void flash_writer_page_write(void* page, void* data, size_t len); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif /* FLASH_WRITER_H */ 31 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | depends: 2 | - test-runner 3 | - cmp_mem_access 4 | - crc 5 | 6 | tests: 7 | - tests/can_datagram_tests.cpp 8 | - tests/mocks/flash_writer_mock.cpp 9 | - tests/mocks/can_interface_mock.cpp 10 | - tests/mocks/boot_arg.cpp 11 | - tests/config_tests.cpp 12 | - tests/flash_command_tests.cpp 13 | - tests/config_commands_tests.cpp 14 | - tests/datagrams_command_tests.cpp 15 | - tests/integration_tests.cpp 16 | - tests/mocks/platform_mock.c 17 | - tests/mocks/timeout_mock.c 18 | 19 | source: 20 | - can_datagram.c 21 | - command.c 22 | - config.c 23 | - bootloader.c 24 | - dependencies/cmp/cmp.c 25 | 26 | target.armv7-m: 27 | - platform/mcu/armv7-m/boot_arg.c 28 | - platform/mcu/armv7-m/vector_table.c 29 | - platform/mcu/armv7-m/boot.s 30 | - platform/mcu/armv7-m/timeout_timer.c 31 | 32 | target.stm32f3: 33 | - platform/mcu/stm32f3/flash_writer.c 34 | - platform/mcu/stm32f3/can_interface.c 35 | 36 | target.stm32f4: 37 | - platform/mcu/stm32f4/flash_writer.c 38 | - platform/mcu/stm32f4/can_interface.c 39 | 40 | target.stm32f1: 41 | - platform/mcu/stm32f1/flash_writer.c 42 | - platform/mcu/stm32f1/can_interface.c 43 | 44 | include_directories: 45 | - ./ 46 | 47 | templates: 48 | platform/rc-board-v1/include.jinja: 'platform/rc-board-v1/include.mk' 49 | platform/motor-board-v1/include.jinja: 'platform/motor-board-v1/include.mk' 50 | platform/olimex-e407/include.jinja: 'platform/olimex-e407/include.mk' 51 | platform/can-io-board/include.jinja: 'platform/can-io-board/include.mk' 52 | platform/nucleo-board-stm32f103rb/include.jinja: 'platform/nucleo-board-stm32f103rb/include.mk' 53 | platform/nucleo-board-stm32f334r8/include.jinja: 'platform/nucleo-board-stm32f334r8/include.mk' 54 | platform/uwb-beacon/include.jinja: 'platform/uwb-beacon/include.mk' 55 | platform/sensor-board/include.jinja: 'platform/sensor-board/include.mk' 56 | platform/actuator-board/include.jinja: 'platform/actuator-board/include.mk' 57 | -------------------------------------------------------------------------------- /platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | // example sizes 12 | #define PLATFORM_DEVICE_CLASS "test-device" 13 | #define FLASH_PAGE_SIZE 256 14 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 15 | 16 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 17 | 18 | void* memory_get_app_addr(void); 19 | void* memory_get_config1_addr(void); 20 | void* memory_get_config2_addr(void); 21 | size_t memory_get_app_size(void); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | #endif /* PLATFORM_H */ 28 | -------------------------------------------------------------------------------- /platform/actuator-board/.gitignore: -------------------------------------------------------------------------------- 1 | include.mk 2 | build 3 | -------------------------------------------------------------------------------- /platform/actuator-board/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # settings 3 | ################################################################################ 4 | 5 | PROJNAME = bootloader 6 | 7 | # where to place the build files 8 | BUILD_DIR = build 9 | 10 | # platform specific source files 11 | PLATFORM_CSRC = platform.c 12 | 13 | PROJ_ROOT = ../.. 14 | PLATFORM_PATH = platform/actuator-board/ 15 | 16 | # dont show compiler calls 17 | QUIET = 1 18 | 19 | # 20 | # low level settings 21 | ################################################################################ 22 | 23 | include include.mk 24 | 25 | CSRC += $(addprefix $(PLATFORM_PATH), $(PLATFORM_CSRC)) 26 | 27 | # libraries & includes 28 | INCDIR = ./ $(PROJ_ROOT)/libopencm3/include $(PROJ_ROOT)/ 29 | INCDIR += $(addprefix $(PROJ_ROOT)/, $(PACKAGER_INCDIR)) 30 | 31 | LIBS = -lc -lnosys 32 | LIBS += -L$(PROJ_ROOT)/libopencm3/lib 33 | LIBS += -lopencm3_stm32f3 34 | 35 | LDSCRIPT = ./linkerscript.ld 36 | 37 | # defines 38 | DEFS += -DSTM32F3 39 | 40 | # Cortex-M4f 41 | CFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 42 | LFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 43 | 44 | # C & C++ compiler flags 45 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 46 | CFLAGS += $(OPTIMIZATION) -g -Wall -Wextra -Wno-unused-parameter 47 | CFLAGS += $(DEFS) -fomit-frame-pointer -MD 48 | CFLAGS += $(addprefix -I, $(INCDIR)) 49 | # C only flags 50 | CCFLAGS += -Wstrict-prototypes 51 | # C++ only flags 52 | CXXFLAGS += -fno-rtti -fno-exceptions -fno-unwind-tables 53 | CXXFLAGS += -fno-use-cxa-atexit 54 | # Linker flags 55 | LFLAGS += $(LIBS) -T$(LDSCRIPT) -nostartfiles -Wl,-Map=$(PROJNAME).map 56 | LFLAGS += -Wl,--gc-sections 57 | LFLAGS += -lc_nano 58 | 59 | CCFLAGS += $(CFLAGS) 60 | CXXFLAGS += $(CFLAGS) 61 | ASMFLAGS += $(CFLAGS) 62 | 63 | COBJS = $(addprefix $(BUILD_DIR)/, $(CSRC:.c=.o)) 64 | ASMOBJS = $(addprefix $(BUILD_DIR)/, $(ASMSRC:.s=.o)) 65 | CXXOBJS = $(addprefix $(BUILD_DIR)/, $(CXXSRC:.cpp=.o)) 66 | 67 | OBJS = $(COBJS) $(ASMOBJS) $(CXXOBJS) 68 | 69 | # gcc optimization level 70 | OPTIMIZATION = -Os 71 | 72 | CC = arm-none-eabi-gcc 73 | CXX = arm-none-eabi-g++ 74 | AS = arm-none-eabi-gcc -x assembler-with-cpp 75 | LD = arm-none-eabi-g++ 76 | AR = arm-none-eabi-ar 77 | OC = arm-none-eabi-objcopy 78 | OD = arm-none-eabi-objdump 79 | NM = arm-none-eabi-nm 80 | SZ = arm-none-eabi-size 81 | 82 | MKDIR = mkdir -p 83 | 84 | COLOR = \033[1;31m 85 | COLOR_CLEAR = \033[0m 86 | PRINT = @printf "$(COLOR)%s$(COLOR_CLEAR)\n" 87 | 88 | ifeq ($(QUIET),1) 89 | Q = @ 90 | endif 91 | 92 | # 93 | # targets: 94 | ################################################################################ 95 | 96 | .PHONY: all 97 | all: $(PROJNAME).bin $(PROJNAME).lst $(PROJNAME).size.txt Makefile 98 | $(PRINT) "> done: $(PROJNAME)" 99 | @ $(SZ) $(PROJNAME).elf 100 | 101 | .PHONY: clean 102 | clean: 103 | $(Q)-rm -f $(OBJS) 104 | $(Q)-rm -f $(OBJS:.o=.lst) 105 | $(Q)-rm -f $(OBJS:.o=.d) 106 | $(Q)-rm -f $(PROJNAME).elf 107 | $(Q)-rm -f $(PROJNAME).bin 108 | $(Q)-rm -f $(PROJNAME).lst 109 | $(Q)-rm -f $(PROJNAME).map 110 | $(Q)-rm -f $(PROJNAME).size.txt 111 | 112 | .PHONY: rebuild 113 | rebuild: clean all 114 | 115 | # make targets for flashing with openocd 116 | .PHONY: flash 117 | flash: all 118 | openocd -f oocd.cfg -c "program $(PROJNAME).elf verify reset" -c "shutdown" 119 | 120 | .PHONY: reset 121 | reset: 122 | openocd -f oocd.cfg -c "init" -c "reset" -c "shutdown" 123 | 124 | # 125 | # file targets: 126 | ################################################################################ 127 | 128 | # binary 129 | $(PROJNAME).bin: $(PROJNAME).elf 130 | $(PRINT) "> copying" 131 | $(Q) $(OC) -Obinary -j .text -j .rodata -j .data $(PROJNAME).elf $(PROJNAME).bin 132 | 133 | # assembly listing 134 | $(PROJNAME).lst: $(PROJNAME).elf 135 | $(PRINT) "> generating assembly listing" 136 | $(Q) $(OD) -D -h $(PROJNAME).elf > $(PROJNAME).lst 137 | 138 | # linked elf-object 139 | $(PROJNAME).elf: $(OBJS) $(LDSCRIPT) 140 | $(PRINT) "> linking" 141 | $(Q) $(LD) -o $(PROJNAME).elf $(OBJS) $(LFLAGS) 142 | 143 | # object from c 144 | $(COBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.c Makefile 145 | $(Q) $(MKDIR) $(shell dirname ${@}) 146 | $(PRINT) "> compiling ("$<")" 147 | $(Q) $(CC) $(CCFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 148 | 149 | # object from asm 150 | $(ASMOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.s Makefile 151 | $(Q) $(MKDIR) $(shell dirname ${@}) 152 | $(PRINT) "> assembling ("$<")" 153 | $(Q) $(AS) $(ASMFLAGS) -c ${<} -o ${@} 154 | 155 | # object from c++ 156 | $(CXXOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.cpp Makefile 157 | $(Q) $(MKDIR) $(shell dirname ${@}) 158 | $(PRINT) "> compiling ("$<")" 159 | $(Q) $(CXX) $(CXXFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 160 | 161 | # space usage 162 | $(PROJNAME).size.txt: $(PROJNAME).elf 163 | $(PRINT) "> calculating space usage" 164 | $(Q)$(SZ) $(PROJNAME).elf > $(PROJNAME).size.txt 165 | $(Q)$(NM) --size-sort --print-size -S $(PROJNAME).elf >> $(PROJNAME).size.txt 166 | 167 | # include the dependencies for all objects 168 | # (generated by the -MD compiler-flag) 169 | # -include $(OBJS:.o=.d) 170 | -------------------------------------------------------------------------------- /platform/actuator-board/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f3 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/actuator-board/oocd.cfg: -------------------------------------------------------------------------------- 1 | telnet_port 4444 2 | gdb_port 3333 3 | 4 | # interface configuration # 5 | source [find interface/stlink-v2.cfg] 6 | transport select hla_swd 7 | adapter_khz 1000 8 | 9 | # target configuration # 10 | source [find target/stm32f3x.cfg] 11 | -------------------------------------------------------------------------------- /platform/actuator-board/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "platform.h" 10 | 11 | #define GPIOB_LED GPIO0 12 | #define GPIOA_CAN_RX GPIO11 13 | #define GPIOA_CAN_TX GPIO12 14 | 15 | // page buffer used by config commands. 16 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 17 | 18 | void can_interface_init(void) 19 | { 20 | rcc_periph_clock_enable(RCC_CAN); 21 | 22 | /* 23 | STM32 CAN on 36MHz configured APB1 peripheral clock 24 | 36MHz / 2 -> 18MHz 25 | 18MHz / (1tq + 10tq + 7tq) = 1MHz => 1Mbit 26 | */ 27 | can_init(CAN, // Interface 28 | false, // Time triggered communication mode. 29 | true, // Automatic bus-off management. 30 | false, // Automatic wakeup mode. 31 | false, // No automatic retransmission. 32 | false, // Receive FIFO locked mode. 33 | true, // Transmit FIFO priority. 34 | CAN_BTR_SJW_1TQ, // Resynchronization time quanta jump width 35 | CAN_BTR_TS1_10TQ, // Time segment 1 time quanta width 36 | CAN_BTR_TS2_7TQ, // Time segment 2 time quanta width 37 | 2, // Prescaler 38 | false, // Loopback 39 | false); // Silent 40 | 41 | // filter to match any standard id 42 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 43 | can_filter_id_mask_32bit_init( 44 | CAN, 45 | 0, // filter nr 46 | 0, // id: only std id, no rtr 47 | 6 | (7 << 29), // mask: match only std id[10:8] = 0 (bootloader frames) 48 | 0, // assign to fifo0 49 | true // enable 50 | ); 51 | } 52 | 53 | void fault_handler(void) 54 | { 55 | // while(1); // debug 56 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 57 | } 58 | 59 | typedef struct { 60 | uint8_t pllpre; 61 | uint8_t pll; 62 | uint8_t pllsrc; 63 | uint32_t flash_config; 64 | uint8_t hpre; 65 | uint8_t ppre1; 66 | uint8_t ppre2; 67 | uint8_t power_save; 68 | uint32_t apb1_frequency; 69 | uint32_t apb2_frequency; 70 | } my_clock_scale_t; 71 | 72 | // clock config for external HSE 16MHz cristal 73 | static const my_clock_scale_t clock_72mhz = { 74 | .pllpre = RCC_CFGR2_PREDIV_HSE_IN_PLL_DIV_2, 75 | .pll = RCC_CFGR_PLLMUL_PLL_IN_CLK_X9, 76 | .pllsrc = RCC_CFGR_PLLSRC_HSE_PREDIV, 77 | .hpre = RCC_CFGR_HPRE_DIV_NONE, 78 | .ppre1 = RCC_CFGR_PPRE1_DIV_2, 79 | .ppre2 = RCC_CFGR_PPRE2_DIV_NONE, 80 | .power_save = 1, 81 | .flash_config = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2WS, 82 | .apb1_frequency = 36000000, 83 | .apb2_frequency = 72000000, 84 | }; 85 | 86 | static inline void rcc_set_main_pll_hse(uint32_t pll) 87 | { 88 | RCC_CFGR = (~RCC_CFGR_PLLMUL_MASK & RCC_CFGR) | (pll << RCC_CFGR_PLLMUL_SHIFT) | RCC_CFGR_PLLSRC; 89 | } 90 | 91 | static inline void rcc_clock_setup_hse(const my_clock_scale_t* clock) 92 | { 93 | /* Enable internal high-speed oscillator. */ 94 | rcc_osc_on(HSE); 95 | rcc_wait_for_osc_ready(HSE); 96 | /* Select HSE as SYSCLK source. */ 97 | rcc_set_sysclk_source(RCC_CFGR_SW_HSE); 98 | rcc_wait_for_sysclk_status(HSE); 99 | 100 | rcc_osc_off(PLL); 101 | rcc_wait_for_osc_not_ready(PLL); 102 | rcc_set_pll_source(clock->pllsrc); 103 | rcc_set_main_pll_hse(clock->pll); 104 | RCC_CFGR2 = (clock->pllpre << RCC_CFGR2_PREDIV_SHIFT); 105 | /* Enable PLL oscillator and wait for it to stabilize. */ 106 | rcc_osc_on(PLL); 107 | rcc_wait_for_osc_ready(PLL); 108 | 109 | rcc_set_hpre(clock->hpre); 110 | rcc_set_ppre2(clock->ppre2); 111 | rcc_set_ppre1(clock->ppre1); 112 | /* Configure flash settings. */ 113 | flash_set_ws(clock->flash_config); 114 | /* Select PLL as SYSCLK source. */ 115 | rcc_set_sysclk_source(RCC_CFGR_SW_PLL); 116 | /* Wait for PLL clock to be selected. */ 117 | rcc_wait_for_sysclk_status(PLL); 118 | 119 | /* Set the peripheral clock frequencies used. */ 120 | rcc_apb1_frequency = clock->apb1_frequency; 121 | rcc_apb2_frequency = clock->apb2_frequency; 122 | } 123 | 124 | void platform_main(int arg) 125 | { 126 | rcc_clock_setup_hse(&clock_72mhz); 127 | 128 | rcc_periph_clock_enable(RCC_GPIOA); 129 | rcc_periph_clock_enable(RCC_GPIOB); 130 | 131 | // CAN pin 132 | gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIOA_CAN_RX | GPIOA_CAN_TX); 133 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOA_CAN_RX | GPIOA_CAN_TX); 134 | gpio_set_af(GPIOA, GPIO_AF9, GPIOA_CAN_RX | GPIOA_CAN_TX); 135 | 136 | // LED on 137 | gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOB_LED); 138 | gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOB_LED); 139 | gpio_set(GPIOB, GPIOB_LED); 140 | 141 | // configure timeout of 10000 milliseconds 142 | timeout_timer_init(72000000, 10000); 143 | 144 | can_interface_init(); 145 | 146 | bootloader_main(arg); 147 | 148 | reboot_system(BOOT_ARG_START_BOOTLOADER); 149 | } 150 | -------------------------------------------------------------------------------- /platform/actuator-board/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define PLATFORM_DEVICE_CLASS "actuator-board" 12 | #define FLASH_PAGE_SIZE 2048 13 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 14 | 15 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 16 | 17 | // symbols defined in linkerscript 18 | extern int application_address, application_size, config_page1, config_page2; 19 | 20 | static inline void* memory_get_app_addr(void) 21 | { 22 | return (void*)&application_address; 23 | } 24 | 25 | static inline size_t memory_get_app_size(void) 26 | { 27 | return (size_t)&application_size; 28 | } 29 | 30 | static inline void* memory_get_config1_addr(void) 31 | { 32 | return (void*)&config_page1; 33 | } 34 | 35 | static inline void* memory_get_config2_addr(void) 36 | { 37 | return (void*)&config_page2; 38 | } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif /* PLATFORM_H */ 45 | -------------------------------------------------------------------------------- /platform/can-io-board/.gitignore: -------------------------------------------------------------------------------- 1 | include.mk 2 | build 3 | -------------------------------------------------------------------------------- /platform/can-io-board/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # settings 3 | ################################################################################ 4 | 5 | PROJNAME = bootloader 6 | 7 | # where to place the build files 8 | BUILD_DIR = build 9 | 10 | # platform specific source files 11 | PLATFORM_CSRC = platform.c 12 | 13 | PROJ_ROOT = ../.. 14 | PLATFORM_PATH = platform/can-io-board/ 15 | 16 | # dont show compiler calls 17 | QUIET = 1 18 | 19 | # 20 | # low level settings 21 | ################################################################################ 22 | 23 | include include.mk 24 | 25 | CSRC += $(addprefix $(PLATFORM_PATH), $(PLATFORM_CSRC)) 26 | 27 | # libraries & includes 28 | INCDIR = ./ $(PROJ_ROOT)/libopencm3/include $(PROJ_ROOT)/ 29 | INCDIR += $(addprefix $(PROJ_ROOT)/, $(PACKAGER_INCDIR)) 30 | 31 | LIBS = -lc -lnosys 32 | LIBS += -L$(PROJ_ROOT)/libopencm3/lib 33 | LIBS += -lopencm3_stm32f3 34 | 35 | LDSCRIPT = ./linkerscript.ld 36 | 37 | # defines 38 | DEFS += -DSTM32F3 39 | 40 | # Cortex-M4f 41 | CFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 42 | LFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 43 | 44 | # C & C++ compiler flags 45 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 46 | CFLAGS += $(OPTIMIZATION) -g -Wall -Wextra -Wno-unused-parameter 47 | CFLAGS += $(DEFS) -fomit-frame-pointer -MD 48 | CFLAGS += $(addprefix -I, $(INCDIR)) 49 | # C only flags 50 | CCFLAGS += -Wstrict-prototypes 51 | # C++ only flags 52 | CXXFLAGS += -fno-rtti -fno-exceptions -fno-unwind-tables 53 | CXXFLAGS += -fno-use-cxa-atexit 54 | # Linker flags 55 | LFLAGS += $(LIBS) -T$(LDSCRIPT) -nostartfiles -Wl,-Map=$(PROJNAME).map 56 | LFLAGS += -Wl,--gc-sections 57 | LFLAGS += -lc_nano 58 | 59 | CCFLAGS += $(CFLAGS) 60 | CXXFLAGS += $(CFLAGS) 61 | ASMFLAGS += $(CFLAGS) 62 | 63 | COBJS = $(addprefix $(BUILD_DIR)/, $(CSRC:.c=.o)) 64 | ASMOBJS = $(addprefix $(BUILD_DIR)/, $(ASMSRC:.s=.o)) 65 | CXXOBJS = $(addprefix $(BUILD_DIR)/, $(CXXSRC:.cpp=.o)) 66 | 67 | OBJS = $(COBJS) $(ASMOBJS) $(CXXOBJS) 68 | 69 | # gcc optimization level 70 | OPTIMIZATION = -Os 71 | 72 | CC = arm-none-eabi-gcc 73 | CXX = arm-none-eabi-g++ 74 | AS = arm-none-eabi-gcc -x assembler-with-cpp 75 | LD = arm-none-eabi-g++ 76 | AR = arm-none-eabi-ar 77 | OC = arm-none-eabi-objcopy 78 | OD = arm-none-eabi-objdump 79 | NM = arm-none-eabi-nm 80 | SZ = arm-none-eabi-size 81 | 82 | MKDIR = mkdir -p 83 | 84 | COLOR = \033[1;31m 85 | COLOR_CLEAR = \033[0m 86 | PRINT = @printf "$(COLOR)%s$(COLOR_CLEAR)\n" 87 | 88 | ifeq ($(QUIET),1) 89 | Q = @ 90 | endif 91 | 92 | # 93 | # targets: 94 | ################################################################################ 95 | 96 | .PHONY: all 97 | all: $(PROJNAME).bin $(PROJNAME).lst $(PROJNAME).size.txt Makefile 98 | $(PRINT) "> done: $(PROJNAME)" 99 | @ $(SZ) $(PROJNAME).elf 100 | 101 | .PHONY: clean 102 | clean: 103 | $(Q)-rm -f $(OBJS) 104 | $(Q)-rm -f $(OBJS:.o=.lst) 105 | $(Q)-rm -f $(OBJS:.o=.d) 106 | $(Q)-rm -f $(PROJNAME).elf 107 | $(Q)-rm -f $(PROJNAME).bin 108 | $(Q)-rm -f $(PROJNAME).lst 109 | $(Q)-rm -f $(PROJNAME).map 110 | $(Q)-rm -f $(PROJNAME).size.txt 111 | 112 | .PHONY: rebuild 113 | rebuild: clean all 114 | 115 | # make targets for flashing with openocd 116 | .PHONY: flash 117 | flash: all 118 | openocd -f oocd.cfg -c "program $(PROJNAME).elf verify reset" -c "shutdown" 119 | 120 | .PHONY: reset 121 | reset: 122 | openocd -f oocd.cfg -c "init" -c "reset" -c "shutdown" 123 | 124 | # 125 | # file targets: 126 | ################################################################################ 127 | 128 | # binary 129 | $(PROJNAME).bin: $(PROJNAME).elf 130 | $(PRINT) "> copying" 131 | $(Q) $(OC) -Obinary -j .text -j .rodata -j .data $(PROJNAME).elf $(PROJNAME).bin 132 | 133 | # assembly listing 134 | $(PROJNAME).lst: $(PROJNAME).elf 135 | $(PRINT) "> generating assembly listing" 136 | $(Q) $(OD) -D -h $(PROJNAME).elf > $(PROJNAME).lst 137 | 138 | # linked elf-object 139 | $(PROJNAME).elf: $(OBJS) $(LDSCRIPT) 140 | $(PRINT) "> linking" 141 | $(Q) $(LD) -o $(PROJNAME).elf $(OBJS) $(LFLAGS) 142 | 143 | # object from c 144 | $(COBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.c Makefile 145 | $(Q) $(MKDIR) $(shell dirname ${@}) 146 | $(PRINT) "> compiling ("$<")" 147 | $(Q) $(CC) $(CCFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 148 | 149 | # object from asm 150 | $(ASMOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.s Makefile 151 | $(Q) $(MKDIR) $(shell dirname ${@}) 152 | $(PRINT) "> assembling ("$<")" 153 | $(Q) $(AS) $(ASMFLAGS) -c ${<} -o ${@} 154 | 155 | # object from c++ 156 | $(CXXOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.cpp Makefile 157 | $(Q) $(MKDIR) $(shell dirname ${@}) 158 | $(PRINT) "> compiling ("$<")" 159 | $(Q) $(CXX) $(CXXFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 160 | 161 | # space usage 162 | $(PROJNAME).size.txt: $(PROJNAME).elf 163 | $(PRINT) "> calculating space usage" 164 | $(Q)$(SZ) $(PROJNAME).elf > $(PROJNAME).size.txt 165 | $(Q)$(NM) --size-sort --print-size -S $(PROJNAME).elf >> $(PROJNAME).size.txt 166 | 167 | # include the dependencies for all objects 168 | # (generated by the -MD compiler-flag) 169 | # -include $(OBJS:.o=.d) 170 | -------------------------------------------------------------------------------- /platform/can-io-board/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f3 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/can-io-board/oocd.cfg: -------------------------------------------------------------------------------- 1 | telnet_port 4444 2 | gdb_port 3333 3 | 4 | # interface configuration # 5 | source [find interface/stlink-v2.cfg] 6 | transport select hla_swd 7 | adapter_khz 1000 8 | 9 | # target configuration # 10 | source [find target/stm32f3x.cfg] 11 | -------------------------------------------------------------------------------- /platform/can-io-board/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "platform.h" 10 | 11 | #define GPIOA_LED GPIO4 12 | #define GPIOB_CAN_EN GPIO0 13 | #define GPIOA_CAN_RX GPIO11 14 | #define GPIOA_CAN_TX GPIO12 15 | 16 | // page buffer used by config commands. 17 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 18 | 19 | void can_interface_init(void) 20 | { 21 | rcc_periph_clock_enable(RCC_CAN); 22 | 23 | /* 24 | STM32F3 CAN on 36MHz configured APB1 peripheral clock 25 | 36MHz / 2 -> 18MHz 26 | 18MHz / (1tq + 10tq + 7tq) = 1MHz => 1Mbit 27 | */ 28 | can_init(CAN, // Interface 29 | false, // Time triggered communication mode. 30 | true, // Automatic bus-off management. 31 | false, // Automatic wakeup mode. 32 | false, // No automatic retransmission. 33 | false, // Receive FIFO locked mode. 34 | true, // Transmit FIFO priority. 35 | CAN_BTR_SJW_1TQ, // Resynchronization time quanta jump width 36 | CAN_BTR_TS1_10TQ, // Time segment 1 time quanta width 37 | CAN_BTR_TS2_7TQ, // Time segment 2 time quanta width 38 | 2, // Prescaler 39 | false, // Loopback 40 | false); // Silent 41 | 42 | // filter to match any standard id 43 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 44 | can_filter_id_mask_32bit_init( 45 | CAN, 46 | 0, // filter nr 47 | 0, // id: only std id, no rtr 48 | 6 | (7 << 29), // mask: match only std id[10:8] = 0 (bootloader frames) 49 | 0, // assign to fifo0 50 | true // enable 51 | ); 52 | } 53 | 54 | void fault_handler(void) 55 | { 56 | // while(1); // debug 57 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 58 | } 59 | 60 | typedef struct { 61 | uint8_t pllpre; 62 | uint8_t pll; 63 | uint8_t pllsrc; 64 | uint32_t flash_config; 65 | uint8_t hpre; 66 | uint8_t ppre1; 67 | uint8_t ppre2; 68 | uint8_t power_save; 69 | uint32_t apb1_frequency; 70 | uint32_t apb2_frequency; 71 | } my_clock_scale_t; 72 | 73 | // clock config for external HSE 16MHz cristal 74 | static const my_clock_scale_t clock_72mhz = { 75 | .pllpre = RCC_CFGR2_PREDIV_HSE_IN_PLL_DIV_2, 76 | .pll = RCC_CFGR_PLLMUL_PLL_IN_CLK_X9, 77 | .pllsrc = RCC_CFGR_PLLSRC_HSE_PREDIV, 78 | .hpre = RCC_CFGR_HPRE_DIV_NONE, 79 | .ppre1 = RCC_CFGR_PPRE1_DIV_2, 80 | .ppre2 = RCC_CFGR_PPRE2_DIV_NONE, 81 | .power_save = 1, 82 | .flash_config = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2WS, 83 | .apb1_frequency = 36000000, 84 | .apb2_frequency = 72000000, 85 | }; 86 | 87 | static inline void rcc_set_main_pll_hse(uint32_t pll) 88 | { 89 | RCC_CFGR = (~RCC_CFGR_PLLMUL_MASK & RCC_CFGR) | (pll << RCC_CFGR_PLLMUL_SHIFT) | RCC_CFGR_PLLSRC; 90 | } 91 | 92 | static inline void rcc_clock_setup_hse(const my_clock_scale_t* clock) 93 | { 94 | /* Enable internal high-speed oscillator. */ 95 | rcc_osc_on(HSE); 96 | rcc_wait_for_osc_ready(HSE); 97 | /* Select HSE as SYSCLK source. */ 98 | rcc_set_sysclk_source(RCC_CFGR_SW_HSE); 99 | rcc_wait_for_sysclk_status(HSE); 100 | 101 | rcc_osc_off(PLL); 102 | rcc_wait_for_osc_not_ready(PLL); 103 | rcc_set_pll_source(clock->pllsrc); 104 | rcc_set_main_pll_hse(clock->pll); 105 | RCC_CFGR2 = (clock->pllpre << RCC_CFGR2_PREDIV_SHIFT); 106 | /* Enable PLL oscillator and wait for it to stabilize. */ 107 | rcc_osc_on(PLL); 108 | rcc_wait_for_osc_ready(PLL); 109 | 110 | rcc_set_hpre(clock->hpre); 111 | rcc_set_ppre2(clock->ppre2); 112 | rcc_set_ppre1(clock->ppre1); 113 | /* Configure flash settings. */ 114 | flash_set_ws(clock->flash_config); 115 | /* Select PLL as SYSCLK source. */ 116 | rcc_set_sysclk_source(RCC_CFGR_SW_PLL); 117 | /* Wait for PLL clock to be selected. */ 118 | rcc_wait_for_sysclk_status(PLL); 119 | 120 | /* Set the peripheral clock frequencies used. */ 121 | rcc_apb1_frequency = clock->apb1_frequency; 122 | rcc_apb2_frequency = clock->apb2_frequency; 123 | } 124 | 125 | void platform_main(int arg) 126 | { 127 | rcc_clock_setup_hse(&clock_72mhz); 128 | 129 | rcc_periph_clock_enable(RCC_GPIOA); 130 | rcc_periph_clock_enable(RCC_GPIOB); 131 | 132 | // CAN pin 133 | gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIOA_CAN_RX | GPIOA_CAN_TX); 134 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOA_CAN_RX | GPIOA_CAN_TX); 135 | gpio_set_af(GPIOA, GPIO_AF9, GPIOA_CAN_RX | GPIOA_CAN_TX); 136 | 137 | // enable CAN transceiver 138 | gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOB_CAN_EN); 139 | gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOB_CAN_EN); 140 | gpio_clear(GPIOB, GPIOB_CAN_EN); 141 | 142 | // LED on 143 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOA_LED); 144 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOA_LED); 145 | gpio_set(GPIOA, GPIOA_LED); 146 | 147 | // configure timeout of 10000 milliseconds 148 | timeout_timer_init(72000000, 10000); 149 | 150 | can_interface_init(); 151 | 152 | bootloader_main(arg); 153 | 154 | reboot_system(BOOT_ARG_START_BOOTLOADER); 155 | } 156 | -------------------------------------------------------------------------------- /platform/can-io-board/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define PLATFORM_DEVICE_CLASS "can-io-board" 12 | #define FLASH_PAGE_SIZE 2048 13 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 14 | 15 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 16 | 17 | // symbols defined in linkerscript 18 | extern int application_address, application_size, config_page1, config_page2; 19 | 20 | static inline void* memory_get_app_addr(void) 21 | { 22 | return (void*)&application_address; 23 | } 24 | 25 | static inline size_t memory_get_app_size(void) 26 | { 27 | return (size_t)&application_size; 28 | } 29 | 30 | static inline void* memory_get_config1_addr(void) 31 | { 32 | return (void*)&config_page1; 33 | } 34 | 35 | static inline void* memory_get_config2_addr(void) 36 | { 37 | return (void*)&config_page2; 38 | } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif /* PLATFORM_H */ 45 | -------------------------------------------------------------------------------- /platform/mcu/armv7-m/boot.s: -------------------------------------------------------------------------------- 1 | @ boot code for bootloader 2 | @ This is the first code executed after reset. It checks the start of RAM for a 3 | @ 7 byte magic value followed a 1 byte argument. 4 | @ boot arguments: 5 | @ 0 : bootloader, with timeout (default) 6 | @ 1 : bootloader, without timeout 7 | @ 2 : application, RAM content is not altered 8 | @ 3 : internal ST bootloader from system memeory 9 | @ 10 | @ This is has several purposes: 11 | @ - Start the bootloader with an argument (such as disable the timeout) 12 | @ - Start the application from the bootloader itself without having to manually 13 | @ reset every peripheral that was used. 14 | @ - The application can omit the bootloader after a software reset to preserve 15 | @ the RAM contents (with exception to the first two words). 16 | @ - Start the internal bootloader from ST to flash via UART/CAN/USB_DFU... 17 | 18 | .extern bootloader_startup 19 | .extern application_address 20 | 21 | .equ RAM_START, 0x20000000 22 | .equ SCB_VTOR, 0xE000ED08 23 | 24 | .equ magic_value_lo, 0x01234567 25 | .equ magic_value_hi, 0x0089abcd 26 | 27 | .thumb 28 | .thumb_func 29 | .syntax unified 30 | .section .text 31 | .global reset_handler 32 | reset_handler: 33 | ldr r5, =RAM_START 34 | ldr r1, [r5, #0] @ load magic value from RAM 35 | ldr r2, [r5, #4] 36 | ldr r4, =0x00FFFFFF 37 | and r2, r2, r4 38 | 39 | eor r0, r0 @ clear argument register 40 | 41 | ldr r3, =magic_value_lo 42 | cmp r1, r3 43 | bne bootloader_startup 44 | ldr r3, =magic_value_hi 45 | cmp r2, r3 46 | bne bootloader_startup 47 | 48 | @ magic value was correct 49 | ldrb r0, [r5, #7] @ load argument byte 50 | 51 | eor r1, r1 52 | str r1, [r5, #0] @ clear ram_start 53 | str r1, [r5, #4] 54 | 55 | cmp r0, #2 56 | beq _app_jmp 57 | cmp r0, #3 58 | beq _st_bootloader 59 | @ default: launch bootloader, argument r0 60 | b bootloader_startup 61 | 62 | _app_jmp: 63 | ldr r0, =application_address 64 | ldr r1, =SCB_VTOR 65 | str r0, [r1] @ relocate vector table 66 | dsb 67 | ldr sp, [r0, #0] @ initialize stack pointer 68 | ldr r0, [r0, #4] @ load reset address 69 | bx r0 @ jump to application 70 | 71 | _st_bootloader: 72 | ldr r0, =0x1FFF0000 73 | ldr sp, [r0, #0] 74 | ldr r0, [r0, #4] 75 | bx r0 76 | -------------------------------------------------------------------------------- /platform/mcu/armv7-m/boot_arg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define BOOT_ARG_MAGIC_VALUE_LO 0x01234567 6 | #define BOOT_ARG_MAGIC_VALUE_HI 0x0089abcd 7 | 8 | void reboot_system(uint8_t arg) 9 | { 10 | uint32_t* ram_start = (uint32_t*)0x20000000; 11 | 12 | ram_start[0] = BOOT_ARG_MAGIC_VALUE_LO; 13 | ram_start[1] = BOOT_ARG_MAGIC_VALUE_HI | (arg << 24); 14 | 15 | scb_reset_system(); 16 | } 17 | -------------------------------------------------------------------------------- /platform/mcu/armv7-m/timeout_timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "timeout_timer.h" 3 | 4 | static uint32_t timer_freq; 5 | static uint32_t timeout_in_millisec; 6 | 7 | bool timeout_reached(void) 8 | { 9 | static uint32_t sys_ticks = 0; 10 | sys_ticks += STK_RVR_RELOAD - systick_get_value(); 11 | if (systick_get_countflag()) { 12 | sys_ticks += STK_RVR_RELOAD + 1; 13 | } 14 | STK_CVR = STK_RVR_RELOAD; 15 | return timeout_in_millisec < sys_ticks / (timer_freq / 1000); 16 | } 17 | 18 | void timeout_timer_init(uint32_t f_cpu, uint32_t timeout_ms) 19 | { 20 | timer_freq = f_cpu; 21 | timeout_in_millisec = timeout_ms; 22 | systick_counter_enable(); 23 | systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); 24 | systick_set_reload(STK_RVR_RELOAD); 25 | } 26 | -------------------------------------------------------------------------------- /platform/mcu/armv7-m/timeout_timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMEOUT_TIMER_H 2 | #define TIMEOUT_TIMER_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | /** Configures the timeout hardware timer. 11 | * @param [in] f_cpu The CPU frequency, with which the timer runs. 12 | * @param [in] timeout_ms The timeout in milliseconds. 13 | */ 14 | void timeout_timer_init(uint32_t f_cpu, uint32_t timeout_ms); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif /* TIMEOUT_TIMER_H */ -------------------------------------------------------------------------------- /platform/mcu/armv7-m/vector_table.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern uint32_t _sbss; 4 | extern uint32_t _ebss; 5 | extern uint32_t _sdata; 6 | extern uint32_t _edata; 7 | extern uint32_t _ldata; 8 | extern uint32_t _eram; 9 | 10 | extern void platform_main(int); 11 | extern void fault_handler(void); 12 | extern void reset_handler(void); 13 | 14 | __attribute__((section(".vectors"))) void (*const vector_table[])(void) = { 15 | (void*)&_eram, 16 | reset_handler, 17 | fault_handler, // nmi_handler 18 | fault_handler, // hard_fault_handler 19 | fault_handler, // mem_manage_handler 20 | fault_handler, // bus_fault_handler 21 | fault_handler, // usage_fault_handler 22 | }; 23 | 24 | void __attribute__((naked)) bootloader_startup(int arg) 25 | { 26 | volatile uint32_t *p_ram, *p_flash; 27 | // clear .bss section 28 | p_ram = &_sbss; 29 | while (p_ram < &_ebss) { 30 | *p_ram++ = 0; 31 | } 32 | // copy .data section from flash to ram 33 | p_flash = &_ldata; 34 | p_ram = &_sdata; 35 | while (p_ram < &_edata) { 36 | *p_ram++ = *p_flash++; 37 | } 38 | 39 | platform_main(arg); 40 | 41 | while (1) 42 | ; 43 | } 44 | -------------------------------------------------------------------------------- /platform/mcu/stm32f1/can_interface.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | bool can_interface_read_message(uint32_t* id, uint8_t* message, uint8_t* length, uint32_t retries) 5 | { 6 | uint32_t fid; 7 | uint8_t len; 8 | bool ext, rtr; 9 | 10 | while (retries-- != 0 && (CAN_RF0R(CAN1) & CAN_RF0R_FMP0_MASK) == 0) 11 | ; 12 | 13 | if ((CAN_RF0R(CAN1) & CAN_RF0R_FMP0_MASK) == 0) { 14 | return false; 15 | } 16 | 17 | can_receive( 18 | CAN1, // canport 19 | 0, // fifo 20 | true, // release 21 | id, // can id 22 | &ext, // extended id 23 | &rtr, // transmission request 24 | &fid, // filter id 25 | &len, // length 26 | message); 27 | 28 | *length = len; 29 | 30 | return true; 31 | } 32 | 33 | bool can_interface_send_message(uint32_t id, uint8_t* message, uint8_t length, uint32_t retries) 34 | { 35 | do { 36 | can_transmit( 37 | CAN1, // canport 38 | id, // can id 39 | false, // extended id 40 | false, // request transmit 41 | length, // data length 42 | message // data 43 | ); 44 | 45 | while ((CAN_TSR(CAN1) & CAN_TSR_RQCP0) == 0) 46 | ; 47 | 48 | if ((CAN_TSR(CAN1) & CAN_TSR_TXOK0)) { 49 | return true; // can ok 50 | } 51 | } while (retries-- > 0); 52 | 53 | return false; 54 | } 55 | -------------------------------------------------------------------------------- /platform/mcu/stm32f1/flash_writer.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "flash_writer.h" 4 | 5 | #if !defined(FLASH_PROGRAM_SIZE) 6 | // flash parallel program size, depends on VCC 7 | // 0 (8-bit), 1 (16-bit), 2 (32-bit), 3 (64-bit) 8 | #define FLASH_PROGRAM_SIZE 0 // default: 8-bit 9 | #endif 10 | 11 | void flash_init(void) 12 | { 13 | } 14 | 15 | void flash_writer_unlock(void) 16 | { 17 | flash_unlock(); 18 | } 19 | 20 | void flash_writer_lock(void) 21 | { 22 | flash_lock(); 23 | } 24 | 25 | void flash_writer_page_erase(void* page) 26 | { 27 | flash_wait_for_last_operation(); 28 | 29 | FLASH_CR |= FLASH_CR_PER; 30 | FLASH_AR = (uint32_t)page; 31 | FLASH_CR |= FLASH_CR_STRT; 32 | 33 | flash_wait_for_last_operation(); 34 | 35 | FLASH_CR &= ~FLASH_CR_PER; 36 | } 37 | 38 | static void flash_write_half_word(uint16_t* flash, uint16_t half_word) 39 | { 40 | /* select flash programming */ 41 | FLASH_CR |= FLASH_CR_PG; 42 | 43 | /* perform half-word write */ 44 | *flash = half_word; 45 | 46 | flash_wait_for_last_operation(); 47 | } 48 | void flash_writer_page_write(void* page, void* data, size_t len) 49 | { 50 | uint8_t* bytes = (uint8_t*)data; 51 | uint16_t* flash = (uint16_t*)page; 52 | uint16_t half_word; 53 | 54 | flash_wait_for_last_operation(); 55 | 56 | size_t count; 57 | for (count = len; count > 1; count -= 2) { 58 | half_word = *bytes++; 59 | half_word |= (uint16_t)*bytes++ << 8; 60 | 61 | flash_write_half_word(flash++, half_word); 62 | } 63 | 64 | if (count == 1) { 65 | half_word = *bytes; 66 | /* preserve value of adjacent byte */ 67 | half_word |= (uint16_t)(*(uint8_t*)flash) << 8; 68 | 69 | flash_write_half_word(flash, half_word); 70 | } 71 | 72 | /* reset flags */ 73 | FLASH_CR &= ~FLASH_CR_PG; 74 | FLASH_SR |= FLASH_SR_EOP; 75 | } 76 | -------------------------------------------------------------------------------- /platform/mcu/stm32f3/can_interface.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | bool can_interface_read_message(uint32_t* id, uint8_t* message, uint8_t* length, uint32_t retries) 5 | { 6 | uint32_t fid; 7 | uint8_t len; 8 | bool ext, rtr; 9 | 10 | while (retries-- != 0 && (CAN_RF0R(CAN) & CAN_RF0R_FMP0_MASK) == 0) 11 | ; 12 | 13 | if ((CAN_RF0R(CAN) & CAN_RF0R_FMP0_MASK) == 0) { 14 | return false; 15 | } 16 | 17 | can_receive( 18 | CAN, // canport 19 | 0, // fifo 20 | true, // release 21 | id, // can id 22 | &ext, // extended id 23 | &rtr, // transmission request 24 | &fid, // filter id 25 | &len, // length 26 | message); 27 | 28 | *length = len; 29 | 30 | return true; 31 | } 32 | 33 | bool can_interface_send_message(uint32_t id, uint8_t* message, uint8_t length, uint32_t retries) 34 | { 35 | do { 36 | can_transmit( 37 | CAN, // canport 38 | id, // can id 39 | false, // extended id 40 | false, // request transmit 41 | length, // data length 42 | message // data 43 | ); 44 | 45 | while ((CAN_TSR(CAN) & CAN_TSR_RQCP0) == 0) 46 | ; 47 | 48 | if ((CAN_TSR(CAN) & CAN_TSR_TXOK0)) { 49 | return true; // can ok 50 | } 51 | } while (retries-- > 0); 52 | 53 | return false; 54 | } 55 | -------------------------------------------------------------------------------- /platform/mcu/stm32f3/flash_writer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "flash_writer.h" 3 | 4 | void flash_init(void) 5 | { 6 | } 7 | 8 | void flash_writer_unlock(void) 9 | { 10 | flash_unlock(); 11 | } 12 | 13 | void flash_writer_lock(void) 14 | { 15 | flash_lock(); 16 | } 17 | 18 | void flash_writer_page_erase(void* page) 19 | { 20 | flash_wait_for_last_operation(); 21 | 22 | FLASH_CR |= FLASH_CR_PER; 23 | FLASH_AR = (uint32_t)page; 24 | FLASH_CR |= FLASH_CR_STRT; 25 | 26 | flash_wait_for_last_operation(); 27 | 28 | FLASH_CR &= ~FLASH_CR_PER; 29 | } 30 | 31 | static void flash_write_half_word(uint16_t* flash, uint16_t half_word) 32 | { 33 | /* select flash programming */ 34 | FLASH_CR |= FLASH_CR_PG; 35 | 36 | /* perform half-word write */ 37 | *flash = half_word; 38 | 39 | flash_wait_for_last_operation(); 40 | } 41 | 42 | /* page address is assumed to be half-word aligned */ 43 | void flash_writer_page_write(void* page, void* data, size_t len) 44 | { 45 | uint8_t* bytes = (uint8_t*)data; 46 | uint16_t* flash = (uint16_t*)page; 47 | uint16_t half_word; 48 | 49 | flash_wait_for_last_operation(); 50 | 51 | size_t count; 52 | for (count = len; count > 1; count -= 2) { 53 | half_word = *bytes++; 54 | half_word |= (uint16_t)*bytes++ << 8; 55 | 56 | flash_write_half_word(flash++, half_word); 57 | } 58 | 59 | if (count == 1) { 60 | half_word = *bytes; 61 | /* preserve value of adjacent byte */ 62 | half_word |= (uint16_t)(*(uint8_t*)flash) << 8; 63 | 64 | flash_write_half_word(flash, half_word); 65 | } 66 | 67 | /* reset flags */ 68 | FLASH_CR &= ~FLASH_CR_PG; 69 | FLASH_SR |= FLASH_SR_EOP; 70 | } 71 | -------------------------------------------------------------------------------- /platform/mcu/stm32f4/can_interface.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | bool can_interface_read_message(uint32_t* id, uint8_t* message, uint8_t* length, uint32_t retries) 5 | { 6 | uint32_t fid; 7 | uint8_t len; 8 | bool ext, rtr; 9 | 10 | while (retries-- != 0 && (CAN_RF0R(CAN1) & CAN_RF0R_FMP0_MASK) == 0) 11 | ; 12 | 13 | if ((CAN_RF0R(CAN1) & CAN_RF0R_FMP0_MASK) == 0) { 14 | return false; 15 | } 16 | 17 | can_receive( 18 | CAN1, // canport 19 | 0, // fifo 20 | true, // release 21 | id, // can id 22 | &ext, // extended id 23 | &rtr, // transmission request 24 | &fid, // filter id 25 | &len, // length 26 | message); 27 | 28 | *length = len; 29 | 30 | return true; 31 | } 32 | 33 | bool can_interface_send_message(uint32_t id, uint8_t* message, uint8_t length, uint32_t retries) 34 | { 35 | do { 36 | can_transmit( 37 | CAN1, // canport 38 | id, // can id 39 | false, // extended id 40 | false, // request transmit 41 | length, // data length 42 | message // data 43 | ); 44 | 45 | while ((CAN_TSR(CAN1) & CAN_TSR_RQCP0) == 0) 46 | ; 47 | 48 | if ((CAN_TSR(CAN1) & CAN_TSR_TXOK0)) { 49 | return true; // can ok 50 | } 51 | } while (retries-- > 0); 52 | 53 | return false; 54 | } 55 | -------------------------------------------------------------------------------- /platform/mcu/stm32f4/flash_writer.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "flash_writer.h" 4 | 5 | #if !defined(FLASH_PROGRAM_SIZE) 6 | // flash parallel program size, depends on VCC 7 | // 0 (8-bit), 1 (16-bit), 2 (32-bit), 3 (64-bit) 8 | #define FLASH_PROGRAM_SIZE 0 // default: 8-bit 9 | #endif 10 | 11 | #define SECTOR_COUNT 24 12 | 13 | static int sector_erased[SECTOR_COUNT]; 14 | 15 | static uint8_t flash_addr_to_sector(uint32_t addr) 16 | { 17 | uint8_t sector; 18 | uint32_t offset = addr & 0xFFFFFF; 19 | if (offset < 0x10000) { 20 | sector = offset / 0x4000; // 16K sectors, 0x08000000 to 0x0800FFFF 21 | } else if (offset < 0x20000) { 22 | sector = 3 + offset / 0x10000; // 64K sector, 0x08010000 to 0x0801FFFF 23 | } else { 24 | sector = 4 + offset / 0x20000; // 128K sectors, 0x08010000 to 0x080FFFFF 25 | } 26 | if (addr >= 0x08100000) { 27 | // Bank 2, same layout, starting at 0x08100000 28 | sector += 12; 29 | } 30 | return sector; 31 | } 32 | 33 | void flash_init(void) 34 | { 35 | for (int i = 0; i < SECTOR_COUNT; i++) { 36 | sector_erased[i] = 0; 37 | } 38 | } 39 | 40 | void flash_writer_unlock(void) 41 | { 42 | flash_unlock(); 43 | } 44 | 45 | void flash_writer_lock(void) 46 | { 47 | flash_lock(); 48 | } 49 | 50 | void flash_writer_page_erase(void* page) 51 | { 52 | uint8_t sector = flash_addr_to_sector((uint32_t)page); 53 | if (sector_erased[sector] == 0) { 54 | sector_erased[sector] = 1; 55 | flash_erase_sector(sector, FLASH_PROGRAM_SIZE); 56 | } 57 | } 58 | 59 | void flash_writer_page_write(void* page, void* data, size_t len) 60 | { 61 | uint8_t sector = flash_addr_to_sector((uint32_t)page); 62 | sector_erased[sector] = 0; 63 | flash_program((uint32_t)page, data, len); 64 | } 65 | -------------------------------------------------------------------------------- /platform/motor-board-v1/.gitignore: -------------------------------------------------------------------------------- 1 | include.mk 2 | build 3 | -------------------------------------------------------------------------------- /platform/motor-board-v1/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # settings 3 | ################################################################################ 4 | 5 | PROJNAME = bootloader 6 | 7 | # where to place the build files 8 | BUILD_DIR = build 9 | 10 | # platform specific source files 11 | PLATFORM_CSRC = platform.c 12 | 13 | PROJ_ROOT = ../.. 14 | PLATFORM_PATH = platform/motor-board-v1/ 15 | 16 | # dont show compiler calls 17 | QUIET = 1 18 | 19 | # 20 | # low level settings 21 | ################################################################################ 22 | 23 | include include.mk 24 | 25 | CSRC += $(addprefix $(PLATFORM_PATH), $(PLATFORM_CSRC)) 26 | 27 | # libraries & includes 28 | INCDIR = ./ $(PROJ_ROOT)/libopencm3/include $(PROJ_ROOT)/ 29 | INCDIR += $(addprefix $(PROJ_ROOT)/, $(PACKAGER_INCDIR)) 30 | 31 | LIBS = -lc -lnosys 32 | LIBS += -L$(PROJ_ROOT)/libopencm3/lib 33 | LIBS += -lopencm3_stm32f3 34 | 35 | LDSCRIPT = ./linkerscript.ld 36 | 37 | # defines 38 | DEFS += -DSTM32F3 39 | 40 | # Cortex-M4f 41 | CFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 42 | LFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 43 | 44 | # C & C++ compiler flags 45 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 46 | CFLAGS += $(OPTIMIZATION) -g -Wall -Wextra -Wno-unused-parameter 47 | CFLAGS += $(DEFS) -fomit-frame-pointer -MD 48 | CFLAGS += $(addprefix -I, $(INCDIR)) 49 | # C only flags 50 | CCFLAGS += -Wstrict-prototypes 51 | # C++ only flags 52 | CXXFLAGS += -fno-rtti -fno-exceptions -fno-unwind-tables 53 | CXXFLAGS += -fno-use-cxa-atexit 54 | # Linker flags 55 | LFLAGS += $(LIBS) -T$(LDSCRIPT) -nostartfiles -Wl,-Map=$(PROJNAME).map 56 | LFLAGS += -Wl,--gc-sections 57 | LFLAGS += -lc_nano 58 | 59 | CCFLAGS += $(CFLAGS) 60 | CXXFLAGS += $(CFLAGS) 61 | ASMFLAGS += $(CFLAGS) 62 | 63 | COBJS = $(addprefix $(BUILD_DIR)/, $(CSRC:.c=.o)) 64 | ASMOBJS = $(addprefix $(BUILD_DIR)/, $(ASMSRC:.s=.o)) 65 | CXXOBJS = $(addprefix $(BUILD_DIR)/, $(CXXSRC:.cpp=.o)) 66 | 67 | OBJS = $(COBJS) $(ASMOBJS) $(CXXOBJS) 68 | 69 | # gcc optimization level 70 | OPTIMIZATION = -Os 71 | 72 | CC = arm-none-eabi-gcc 73 | CXX = arm-none-eabi-g++ 74 | AS = arm-none-eabi-gcc -x assembler-with-cpp 75 | LD = arm-none-eabi-g++ 76 | AR = arm-none-eabi-ar 77 | OC = arm-none-eabi-objcopy 78 | OD = arm-none-eabi-objdump 79 | NM = arm-none-eabi-nm 80 | SZ = arm-none-eabi-size 81 | 82 | MKDIR = mkdir -p 83 | 84 | COLOR = \033[1;31m 85 | COLOR_CLEAR = \033[0m 86 | PRINT = @printf "$(COLOR)%s$(COLOR_CLEAR)\n" 87 | 88 | ifeq ($(QUIET),1) 89 | Q = @ 90 | endif 91 | 92 | # 93 | # targets: 94 | ################################################################################ 95 | 96 | .PHONY: all 97 | all: $(PROJNAME).bin $(PROJNAME).lst $(PROJNAME).size.txt Makefile 98 | $(PRINT) "> done: $(PROJNAME)" 99 | @ $(SZ) $(PROJNAME).elf 100 | 101 | .PHONY: clean 102 | clean: 103 | $(Q)-rm -f $(OBJS) 104 | $(Q)-rm -f $(OBJS:.o=.lst) 105 | $(Q)-rm -f $(OBJS:.o=.d) 106 | $(Q)-rm -f $(PROJNAME).elf 107 | $(Q)-rm -f $(PROJNAME).bin 108 | $(Q)-rm -f $(PROJNAME).lst 109 | $(Q)-rm -f $(PROJNAME).map 110 | $(Q)-rm -f $(PROJNAME).size.txt 111 | 112 | .PHONY: rebuild 113 | rebuild: clean all 114 | 115 | -include flash.mk 116 | 117 | # 118 | # file targets: 119 | ################################################################################ 120 | 121 | # binary 122 | $(PROJNAME).bin: $(PROJNAME).elf 123 | $(PRINT) "> copying" 124 | $(Q) $(OC) -Obinary -j .text -j .rodata -j .data $(PROJNAME).elf $(PROJNAME).bin 125 | 126 | # assembly listing 127 | $(PROJNAME).lst: $(PROJNAME).elf 128 | $(PRINT) "> generating assembly listing" 129 | $(Q) $(OD) -D -h $(PROJNAME).elf > $(PROJNAME).lst 130 | 131 | # linked elf-object 132 | $(PROJNAME).elf: $(OBJS) $(LDSCRIPT) 133 | $(PRINT) "> linking" 134 | $(Q) $(LD) -o $(PROJNAME).elf $(OBJS) $(LFLAGS) 135 | 136 | # object from c 137 | $(COBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.c Makefile 138 | $(Q) $(MKDIR) $(shell dirname ${@}) 139 | $(PRINT) "> compiling ("$<")" 140 | $(Q) $(CC) $(CCFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 141 | 142 | # object from asm 143 | $(ASMOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.s Makefile 144 | $(Q) $(MKDIR) $(shell dirname ${@}) 145 | $(PRINT) "> assembling ("$<")" 146 | $(Q) $(AS) $(ASMFLAGS) -c ${<} -o ${@} 147 | 148 | # object from c++ 149 | $(CXXOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.cpp Makefile 150 | $(Q) $(MKDIR) $(shell dirname ${@}) 151 | $(PRINT) "> compiling ("$<")" 152 | $(Q) $(CXX) $(CXXFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 153 | 154 | # space usage 155 | $(PROJNAME).size.txt: $(PROJNAME).elf 156 | $(PRINT) "> calculating space usage" 157 | $(Q)$(SZ) $(PROJNAME).elf > $(PROJNAME).size.txt 158 | $(Q)$(NM) --size-sort --print-size -S $(PROJNAME).elf >> $(PROJNAME).size.txt 159 | 160 | # include the dependencies for all objects 161 | # (generated by the -MD compiler-flag) 162 | # -include $(OBJS:.o=.d) 163 | -------------------------------------------------------------------------------- /platform/motor-board-v1/flash.mk: -------------------------------------------------------------------------------- 1 | 2 | # make targets for flashing with openocd 3 | 4 | .PHONY: flash 5 | flash: all 6 | openocd -f oocd.cfg -c "program $(PROJNAME).elf verify reset" -c "shutdown" 7 | 8 | .PHONY: reset 9 | reset: 10 | openocd -f oocd.cfg -c "init" -c "reset" -c "shutdown" 11 | -------------------------------------------------------------------------------- /platform/motor-board-v1/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f3 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/motor-board-v1/oocd.cfg: -------------------------------------------------------------------------------- 1 | telnet_port 4444 2 | gdb_port 3333 3 | 4 | # interface configuration # 5 | source [find interface/stlink-v2.cfg] 6 | transport select hla_swd 7 | adapter_khz 1000 8 | 9 | # target configuration # 10 | source [find target/stm32f3x.cfg] 11 | 12 | -------------------------------------------------------------------------------- /platform/motor-board-v1/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "platform.h" 10 | 11 | #define GPIOA_LED GPIO1 12 | #define GPIOA_CAN_SPEED GPIO4 13 | #define GPIOB_CAN_RX GPIO8 14 | #define GPIOB_CAN_TX GPIO9 15 | 16 | // page buffer used by config commands. 17 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 18 | 19 | void can_interface_init(void) 20 | { 21 | rcc_periph_clock_enable(RCC_CAN); 22 | 23 | /* 24 | STM32F3 CAN on 36MHz configured APB1 peripheral clock 25 | 36MHz / 2 -> 18MHz 26 | 18MHz / (1tq + 10tq + 7tq) = 1MHz => 1Mbit 27 | */ 28 | can_init(CAN, // Interface 29 | false, // Time triggered communication mode. 30 | true, // Automatic bus-off management. 31 | false, // Automatic wakeup mode. 32 | false, // No automatic retransmission. 33 | false, // Receive FIFO locked mode. 34 | true, // Transmit FIFO priority. 35 | CAN_BTR_SJW_1TQ, // Resynchronization time quanta jump width 36 | CAN_BTR_TS1_10TQ, // Time segment 1 time quanta width 37 | CAN_BTR_TS2_7TQ, // Time segment 2 time quanta width 38 | 2, // Prescaler 39 | false, // Loopback 40 | false); // Silent 41 | 42 | // filter to match any standard id 43 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 44 | can_filter_id_mask_32bit_init( 45 | CAN, 46 | 0, // filter nr 47 | 0, // id: only std id, no rtr 48 | 6 | (7 << 29), // mask: match only std id[10:8] = 0 (bootloader frames) 49 | 0, // assign to fifo0 50 | true // enable 51 | ); 52 | } 53 | 54 | void fault_handler(void) 55 | { 56 | // while(1); // debug 57 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 58 | } 59 | 60 | typedef struct { 61 | uint8_t pllpre; 62 | uint8_t pll; 63 | uint8_t pllsrc; 64 | uint32_t flash_config; 65 | uint8_t hpre; 66 | uint8_t ppre1; 67 | uint8_t ppre2; 68 | uint8_t power_save; 69 | uint32_t apb1_frequency; 70 | uint32_t apb2_frequency; 71 | } my_clock_scale_t; 72 | 73 | // clock config for external HSE 16MHz cristal 74 | static const my_clock_scale_t clock_72mhz = { 75 | .pllpre = RCC_CFGR2_PREDIV_HSE_IN_PLL_DIV_2, 76 | .pll = RCC_CFGR_PLLMUL_PLL_IN_CLK_X9, 77 | .pllsrc = RCC_CFGR_PLLSRC_HSE_PREDIV, 78 | .hpre = RCC_CFGR_HPRE_DIV_NONE, 79 | .ppre1 = RCC_CFGR_PPRE1_DIV_2, 80 | .ppre2 = RCC_CFGR_PPRE2_DIV_NONE, 81 | .power_save = 1, 82 | .flash_config = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2WS, 83 | .apb1_frequency = 36000000, 84 | .apb2_frequency = 72000000, 85 | }; 86 | 87 | static inline void rcc_set_main_pll_hse(uint32_t pll) 88 | { 89 | RCC_CFGR = (~RCC_CFGR_PLLMUL_MASK & RCC_CFGR) | (pll << RCC_CFGR_PLLMUL_SHIFT) | RCC_CFGR_PLLSRC; 90 | } 91 | 92 | static inline void rcc_clock_setup_hse(const my_clock_scale_t* clock) 93 | { 94 | /* Enable internal high-speed oscillator. */ 95 | rcc_osc_on(HSE); 96 | rcc_wait_for_osc_ready(HSE); 97 | /* Select HSE as SYSCLK source. */ 98 | rcc_set_sysclk_source(RCC_CFGR_SW_HSE); 99 | rcc_wait_for_sysclk_status(HSE); 100 | 101 | rcc_osc_off(PLL); 102 | rcc_wait_for_osc_not_ready(PLL); 103 | rcc_set_pll_source(clock->pllsrc); 104 | rcc_set_main_pll_hse(clock->pll); 105 | RCC_CFGR2 = (clock->pllpre << RCC_CFGR2_PREDIV_SHIFT); 106 | /* Enable PLL oscillator and wait for it to stabilize. */ 107 | rcc_osc_on(PLL); 108 | rcc_wait_for_osc_ready(PLL); 109 | 110 | rcc_set_hpre(clock->hpre); 111 | rcc_set_ppre2(clock->ppre2); 112 | rcc_set_ppre1(clock->ppre1); 113 | /* Configure flash settings. */ 114 | flash_set_ws(clock->flash_config); 115 | /* Select PLL as SYSCLK source. */ 116 | rcc_set_sysclk_source(RCC_CFGR_SW_PLL); 117 | /* Wait for PLL clock to be selected. */ 118 | rcc_wait_for_sysclk_status(PLL); 119 | 120 | /* Set the peripheral clock frequencies used. */ 121 | rcc_apb1_frequency = clock->apb1_frequency; 122 | rcc_apb2_frequency = clock->apb2_frequency; 123 | } 124 | 125 | void platform_main(int arg) 126 | { 127 | rcc_clock_setup_hse(&clock_72mhz); 128 | 129 | rcc_periph_clock_enable(RCC_GPIOA); 130 | rcc_periph_clock_enable(RCC_GPIOB); 131 | 132 | // CAN pin 133 | gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIOB_CAN_RX | GPIOB_CAN_TX); 134 | gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOB_CAN_RX | GPIOB_CAN_TX); 135 | gpio_set_af(GPIOB, GPIO_AF9, GPIOB_CAN_RX | GPIOB_CAN_TX); 136 | 137 | // enable CAN transceiver 138 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOA_CAN_SPEED); 139 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOA_CAN_SPEED); 140 | gpio_clear(GPIOA, GPIOA_CAN_SPEED); 141 | 142 | // LED on 143 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOA_LED); 144 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOA_LED); 145 | gpio_clear(GPIOA, GPIOA_LED); 146 | 147 | // configure timeout of 10000 milliseconds 148 | timeout_timer_init(72000000, 10000); 149 | 150 | can_interface_init(); 151 | 152 | bootloader_main(arg); 153 | 154 | reboot_system(BOOT_ARG_START_BOOTLOADER); 155 | } 156 | -------------------------------------------------------------------------------- /platform/motor-board-v1/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define PLATFORM_DEVICE_CLASS "motor-board-v1" 12 | #define FLASH_PAGE_SIZE 2048 13 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 14 | 15 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 16 | 17 | // symbols defined in linkerscript 18 | extern int application_address, application_size, config_page1, config_page2; 19 | 20 | static inline void* memory_get_app_addr(void) 21 | { 22 | return (void*)&application_address; 23 | } 24 | 25 | static inline size_t memory_get_app_size(void) 26 | { 27 | return (size_t)&application_size; 28 | } 29 | 30 | static inline void* memory_get_config1_addr(void) 31 | { 32 | return (void*)&config_page1; 33 | } 34 | 35 | static inline void* memory_get_config2_addr(void) 36 | { 37 | return (void*)&config_page2; 38 | } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif /* PLATFORM_H */ 45 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/.gdbinit: -------------------------------------------------------------------------------- 1 | target remote localhost:3333 2 | monitor reset halt 3 | load 4 | break fault_handler 5 | info break 6 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/.gitignore: -------------------------------------------------------------------------------- 1 | include.mk 2 | build 3 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # settings 3 | ################################################################################ 4 | 5 | PROJNAME = bootloader 6 | 7 | # where to place the build files 8 | BUILD_DIR = build 9 | 10 | # platform specific source files 11 | PLATFORM_CSRC = platform.c 12 | 13 | PROJ_ROOT = ../.. 14 | PLATFORM_PATH = platform/nucleo-board-stm32f103rb/ 15 | 16 | # dont show compiler calls 17 | QUIET = 1 18 | 19 | # 20 | # low level settings 21 | ################################################################################ 22 | 23 | include include.mk 24 | 25 | CSRC += $(addprefix $(PLATFORM_PATH), $(PLATFORM_CSRC)) 26 | 27 | # libraries & includes 28 | INCDIR = ./ $(PROJ_ROOT)/libopencm3/include $(PROJ_ROOT)/ 29 | INCDIR += $(addprefix $(PROJ_ROOT)/, $(PACKAGER_INCDIR)) 30 | 31 | LIBS = -lc -lnosys 32 | LIBS += -L$(PROJ_ROOT)/libopencm3/lib 33 | LIBS += -lopencm3_stm32f1 34 | 35 | LDSCRIPT = ./linkerscript.ld 36 | 37 | # defines 38 | DEFS += -DSTM32F1 39 | 40 | # Cortex-M3 41 | CFLAGS += -mthumb -mcpu=cortex-m3 -march=armv7-m 42 | LFLAGS += -mthumb -mcpu=cortex-m3 -march=armv7-m 43 | 44 | # C & C++ compiler flags 45 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 46 | CFLAGS += $(OPTIMIZATION) -g -Wall -Wextra -Wno-unused-parameter 47 | CFLAGS += $(DEFS) -fomit-frame-pointer -MD 48 | CFLAGS += $(addprefix -I, $(INCDIR)) 49 | # C only flags 50 | CCFLAGS += -Wstrict-prototypes 51 | # C++ only flags 52 | CXXFLAGS += -fno-rtti -fno-exceptions -fno-unwind-tables 53 | CXXFLAGS += -fno-use-cxa-atexit 54 | # Linker flags 55 | LFLAGS += $(LIBS) -T$(LDSCRIPT) -nostartfiles -Wl,-Map=$(PROJNAME).map 56 | LFLAGS += -Wl,--gc-sections 57 | 58 | CCFLAGS += $(CFLAGS) 59 | CXXFLAGS += $(CFLAGS) 60 | ASMFLAGS += $(CFLAGS) 61 | 62 | COBJS = $(addprefix $(BUILD_DIR)/, $(CSRC:.c=.o)) 63 | ASMOBJS = $(addprefix $(BUILD_DIR)/, $(ASMSRC:.s=.o)) 64 | CXXOBJS = $(addprefix $(BUILD_DIR)/, $(CXXSRC:.cpp=.o)) 65 | 66 | OBJS = $(COBJS) $(ASMOBJS) $(CXXOBJS) 67 | 68 | # gcc optimization level 69 | OPTIMIZATION = -Os 70 | 71 | CC = arm-none-eabi-gcc 72 | CXX = arm-none-eabi-g++ 73 | AS = arm-none-eabi-gcc -x assembler-with-cpp 74 | LD = arm-none-eabi-g++ 75 | AR = arm-none-eabi-ar 76 | OC = arm-none-eabi-objcopy 77 | OD = arm-none-eabi-objdump 78 | NM = arm-none-eabi-nm 79 | SZ = arm-none-eabi-size 80 | 81 | MKDIR = mkdir -p 82 | 83 | COLOR = \033[1;31m 84 | COLOR_CLEAR = \033[0m 85 | PRINT = @printf "$(COLOR)%s$(COLOR_CLEAR)\n" 86 | 87 | ifeq ($(QUIET),1) 88 | Q = @ 89 | endif 90 | 91 | # 92 | # targets: 93 | ################################################################################ 94 | 95 | .PHONY: all 96 | all: $(PROJNAME).bin $(PROJNAME).lst $(PROJNAME).size.txt Makefile 97 | $(PRINT) "> done: $(PROJNAME)" 98 | @ $(SZ) $(PROJNAME).elf 99 | 100 | .PHONY: clean 101 | clean: 102 | $(Q)-rm -f $(OBJS) 103 | $(Q)-rm -f $(OBJS:.o=.lst) 104 | $(Q)-rm -f $(OBJS:.o=.d) 105 | $(Q)-rm -f $(PROJNAME).elf 106 | $(Q)-rm -f $(PROJNAME).bin 107 | $(Q)-rm -f $(PROJNAME).lst 108 | $(Q)-rm -f $(PROJNAME).map 109 | $(Q)-rm -f $(PROJNAME).size.txt 110 | 111 | .PHONY: rebuild 112 | rebuild: clean all 113 | 114 | # make targets for flashing with openocd 115 | .PHONY: flash 116 | flash: all 117 | openocd -f oocd.cfg -c "program $(PROJNAME).elf verify reset" -c "shutdown" 118 | 119 | .PHONY: reset 120 | reset: 121 | openocd -f oocd.cfg -c "init" -c "reset" -c "shutdown" 122 | 123 | # 124 | # file targets: 125 | ################################################################################ 126 | 127 | # binary 128 | $(PROJNAME).bin: $(PROJNAME).elf 129 | $(PRINT) "> copying" 130 | $(Q) $(OC) -Obinary -j .text -j .rodata -j .data $(PROJNAME).elf $(PROJNAME).bin 131 | 132 | # assembly listing 133 | $(PROJNAME).lst: $(PROJNAME).elf 134 | $(PRINT) "> generating assembly listing" 135 | $(Q) $(OD) -D -h $(PROJNAME).elf > $(PROJNAME).lst 136 | 137 | # linked elf-object 138 | $(PROJNAME).elf: $(OBJS) $(LDSCRIPT) 139 | $(PRINT) "> linking" 140 | $(Q) $(LD) -o $(PROJNAME).elf $(OBJS) $(LFLAGS) 141 | 142 | # object from c 143 | $(COBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.c Makefile 144 | $(Q) $(MKDIR) $(shell dirname ${@}) 145 | $(PRINT) "> compiling ("$<")" 146 | $(Q) $(CC) $(CCFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 147 | 148 | # object from asm 149 | $(ASMOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.s Makefile 150 | $(Q) $(MKDIR) $(shell dirname ${@}) 151 | $(PRINT) "> assembling ("$<")" 152 | $(Q) $(AS) $(ASMFLAGS) -c ${<} -o ${@} 153 | 154 | # object from c++ 155 | $(CXXOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.cpp Makefile 156 | $(Q) $(MKDIR) $(shell dirname ${@}) 157 | $(PRINT) "> compiling ("$<")" 158 | $(Q) $(CXX) $(CXXFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 159 | 160 | # space usage 161 | $(PROJNAME).size.txt: $(PROJNAME).elf 162 | $(PRINT) "> calculating space usage" 163 | $(Q)$(SZ) $(PROJNAME).elf > $(PROJNAME).size.txt 164 | $(Q)$(NM) --size-sort --print-size -S $(PROJNAME).elf >> $(PROJNAME).size.txt 165 | 166 | # include the dependencies for all objects 167 | # (generated by the -MD compiler-flag) 168 | # -include $(OBJS:.o=.d) 169 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/flash.mk: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/gdb.init: -------------------------------------------------------------------------------- 1 | target remote localhost:3333 2 | monitor reset halt 3 | load 4 | break fault_handler 5 | info break 6 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f1 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/oocd.cfg: -------------------------------------------------------------------------------- 1 | # This is an ST NUCLEO F103RB board with a single STM32F103RBT6 chip. 2 | # http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/LN1847/PF259875 3 | 4 | source [find interface/stlink-v2-1.cfg] 5 | 6 | transport select hla_swd 7 | 8 | source [find target/stm32f1x.cfg] 9 | 10 | reset_config srst_only 11 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "platform.h" 10 | 11 | // page buffer used by config commands. 12 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 13 | 14 | void can_interface_init(void) 15 | { 16 | rcc_periph_clock_enable(RCC_CAN1); 17 | 18 | /* 19 | STM32F3 CAN1 on 18MHz configured APB1 peripheral clock 20 | 18MHz / 2 -> 9MHz 21 | 9MHz / (1tq + 10tq + 7tq) = 500kHz => 500kbit 22 | */ 23 | can_init(CAN1, // Interface 24 | false, // Time triggered communication mode. 25 | true, // Automatic bus-off management. 26 | false, // Automatic wakeup mode. 27 | false, // No automatic retransmission. 28 | false, // Receive FIFO locked mode. 29 | true, // Transmit FIFO priority. 30 | CAN_BTR_SJW_1TQ, // Resynchronization time quanta jump width 31 | CAN_BTR_TS1_10TQ, // Time segment 1 time quanta width 32 | CAN_BTR_TS2_7TQ, // Time segment 2 time quanta width 33 | 2, // Prescaler 34 | false, // Loopback 35 | false); // Silent 36 | 37 | // filter to match any standard id 38 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 39 | can_filter_id_mask_32bit_init( 40 | CAN1, 41 | 0, // filter nr 42 | 0, // id: only std id, no rtr 43 | 6 | (7 << 29), // mask: match only std id[10:8] = 0 (bootloader frames) 44 | 0, // assign to fifo0 45 | true // enable 46 | ); 47 | } 48 | 49 | void fault_handler(void) 50 | { 51 | // while(1); // debug 52 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 53 | } 54 | void rcc_clock_setup_in_hsi_out_36mhz(void) 55 | { 56 | /* Enable internal high-speed oscillator. */ 57 | rcc_osc_on(HSI); 58 | rcc_wait_for_osc_ready(HSI); 59 | 60 | /* Select HSI as SYSCLK source. */ 61 | rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK); 62 | 63 | /* 64 | * Set prescalers for AHB, ADC, ABP1, ABP2. 65 | * Do this before touching the PLL (TODO: why?). 66 | */ 67 | rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV); /*Set.36MHz Max.72MHz */ 68 | rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV6); /*Set. 6MHz Max.14MHz */ 69 | rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2); /*Set.18MHz Max.36MHz */ 70 | rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV); /*Set.36MHz Max.72MHz */ 71 | 72 | /* 73 | * Sysclk runs with 48MHz -> 1 waitstates. 74 | * 0WS from 0-24MHz 75 | * 1WS from 24-48MHz 76 | * 2WS from 48-72MHz 77 | */ 78 | flash_set_ws(FLASH_ACR_LATENCY_1WS); 79 | 80 | /* 81 | * Set the PLL multiplication factor to 12. 82 | * 8MHz (internal) * 9 (multiplier) / 2 (PLLSRC_HSI_CLK_DIV2) = 36MHz 83 | */ 84 | rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL9); 85 | 86 | /* Select HSI/2 as PLL source. */ 87 | rcc_set_pll_source(RCC_CFGR_PLLSRC_HSI_CLK_DIV2); 88 | 89 | /* Enable PLL oscillator and wait for it to stabilize. */ 90 | rcc_osc_on(PLL); 91 | rcc_wait_for_osc_ready(PLL); 92 | 93 | /* Select PLL as SYSCLK source. */ 94 | rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK); 95 | 96 | /* Set the peripheral clock frequencies used */ 97 | rcc_apb1_frequency = 18000000; 98 | rcc_apb2_frequency = 36000000; 99 | } 100 | void platform_main(int arg) 101 | { 102 | /* 103 | //If external 8MHz on PC14/PC15 104 | rcc_clock_setup_in_hse_8mhz_out_72mhz();*/ 105 | //Else internal clock 106 | rcc_clock_setup_in_hsi_out_36mhz(); 107 | 108 | //Activate PORTA 109 | rcc_periph_clock_enable(RCC_GPIOA); 110 | rcc_periph_clock_enable(RCC_AFIO); 111 | 112 | // CAN pin 113 | /* init PA12 (TX) to Alternativ function output */ 114 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_CAN1_TX); //TX output 115 | /* init PA11 (RX) to input pull up */ 116 | gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_CAN1_RX); //RX input pull up/down 117 | gpio_set(GPIOA, GPIO_CAN1_RX); //pull up 118 | /* Remap the can to pin PA11 and PA12 , sould already be by default */ 119 | gpio_primary_remap(AFIO_MAPR_SWJ_CFG_FULL_SWJ, AFIO_MAPR_CAN1_REMAP_PORTA); //Can sur port A pin 11/12 120 | 121 | // LED on 122 | /*init the PA5 port to output in PushPull mode at maxspeed */ 123 | gpio_set_mode(PORT_LED2, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, PIN_LED2); //led output 124 | 125 | //Blinking led on PA5 (build in led in the nucleo-stm32f103rb) 126 | /* 127 | uint32_t i=0,j=0; 128 | for(i=0;i<11;i++) 129 | { 130 | for(j=0;j<1000000;j++) 131 | { 132 | asm("nop"); 133 | } 134 | gpio_toggle(PORT_LED2,PIN_LED2); 135 | } 136 | for(j=0;j<10000000;j++) 137 | { 138 | asm("nop"); 139 | } 140 | gpio_set(PORT_LED2,PIN_LED2);*/ 141 | 142 | // configure timeout of 10000 milliseconds on a 36Mhz 143 | timeout_timer_init(36000000, 10000); 144 | can_interface_init(); 145 | bootloader_main(arg); 146 | 147 | reboot_system(BOOT_ARG_START_BOOTLOADER); 148 | } 149 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define PLATFORM_DEVICE_CLASS "nucleo-board-stm32f103rb" 12 | #define FLASH_PAGE_SIZE 0x0400 // 1K 13 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 14 | #define PIN_LED2 GPIO5 15 | #define PORT_LED2 GPIOA 16 | 17 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 18 | 19 | // symbols defined in linkerscript 20 | extern int application_address, application_size, config_page1, config_page2; 21 | 22 | static inline void* memory_get_app_addr(void) 23 | { 24 | return (void*)&application_address; 25 | } 26 | 27 | static inline size_t memory_get_app_size(void) 28 | { 29 | return (size_t)&application_size; 30 | } 31 | 32 | static inline void* memory_get_config1_addr(void) 33 | { 34 | return (void*)&config_page1; 35 | } 36 | 37 | static inline void* memory_get_config2_addr(void) 38 | { 39 | return (void*)&config_page2; 40 | } 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif /* PLATFORM_H */ 47 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f103rb/readme.txt: -------------------------------------------------------------------------------- 1 | For st development board nucleo64-STM32F103RB : http://www.st.com/en/evaluation-tools/nucleo-f103rb.html 2 | Internal clock used at 36MHz with PLL (could be faster) 3 | CAN bitrate : 500000 4 | LED set UP : LED2 on the board (ugly code in comment to have a blink led at the begining og the bootloader (just to test if it uploaded corectly, DO NOT use it unless you have trouble to make it work) 5 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f334r8/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f3 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f334r8/oocd.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # OpenOCD configuration file 3 | # for ST Nucleo boards with a STM32F3 series microcontroller 4 | # 5 | 6 | source [find "interface/stlink-v2-1.cfg"] 7 | 8 | transport select hla_swd 9 | 10 | source [find "target/stm32f3x.cfg"] 11 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f334r8/platform.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include "platform.h" 15 | 16 | // page buffer used by config commands. 17 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 18 | 19 | void led_init() 20 | { 21 | #if (GPIO_PORT_LED2 == GPIOA) 22 | rcc_periph_clock_enable(RCC_GPIOA); 23 | #endif 24 | #if (GPIO_PORT_LED2 == GPIOB) 25 | rcc_periph_clock_enable(RCC_GPIOB); 26 | #endif 27 | 28 | gpio_mode_setup( 29 | GPIO_PORT_LED2, 30 | GPIO_MODE_OUTPUT, 31 | GPIO_PUPD_NONE, 32 | GPIO_PIN_LED2); 33 | gpio_set_output_options( 34 | GPIO_PORT_LED2, 35 | GPIO_OTYPE_PP, 36 | GPIO_OSPEED_2MHZ, 37 | GPIO_PIN_LED2); 38 | gpio_set( 39 | GPIO_PORT_LED2, 40 | GPIO_PIN_LED2); 41 | } 42 | 43 | void led_blink() 44 | { 45 | // Blink onboard led 46 | for (uint32_t i = 0; i < 3; i++) { 47 | for (uint32_t j = 0; j < 150000; j++) { 48 | asm("nop"); 49 | } 50 | gpio_toggle(GPIO_PORT_LED2, GPIO_PIN_LED2); 51 | } 52 | } 53 | 54 | void can_interface_init(void) 55 | { 56 | // Enable clock to GPIO port A and B 57 | #if (GPIO_PORT_CAN_TX == GPIOA) || (GPIO_PORT_CAN_RX == GPIOA) || (GPIO_PORT_CAN_ENABLE == GPIOA) 58 | rcc_periph_clock_enable(RCC_GPIOA); 59 | #endif 60 | #if (GPIO_PORT_CAN_TX == GPIOB) || (GPIO_PORT_CAN_RX == GPIOB) || (GPIO_PORT_CAN_ENABLE == GPIOB) 61 | rcc_periph_clock_enable(RCC_GPIOB); 62 | #endif 63 | 64 | // Enable clock to CAN peripheral 65 | rcc_periph_clock_enable(RCC_CAN); 66 | 67 | // Setup CAN pins 68 | gpio_mode_setup( 69 | GPIO_PORT_CAN_TX, 70 | GPIO_MODE_AF, 71 | GPIO_PUPD_PULLUP, 72 | GPIO_PIN_CAN_TX); 73 | gpio_set_af( 74 | GPIO_PORT_CAN_TX, 75 | GPIO_AF_CAN, 76 | GPIO_PIN_CAN_TX); 77 | 78 | gpio_mode_setup( 79 | GPIO_PORT_CAN_RX, 80 | GPIO_MODE_AF, 81 | GPIO_PUPD_PULLUP, 82 | GPIO_PIN_CAN_RX); 83 | gpio_set_af( 84 | GPIO_PORT_CAN_RX, 85 | GPIO_AF_CAN, 86 | GPIO_PIN_CAN_RX); 87 | 88 | #ifdef USE_CAN_ENABLE 89 | gpio_mode_setup( 90 | GPIO_PORT_CAN_ENABLE, 91 | GPIO_MODE_OUTPUT, 92 | GPIO_PUPD_NONE, 93 | GPIO_PIN_CAN_ENABLE); 94 | gpio_set_output_options( 95 | GPIO_PORT_CAN_ENABLE, 96 | GPIO_OTYPE_PP, 97 | GPIO_OSPEED_2MHZ, 98 | GPIO_PIN_CAN_ENABLE); 99 | #ifndef CAN_ENABLE_INVERTED 100 | gpio_set( 101 | GPIO_PORT_CAN_ENABLE, 102 | GPIO_PIN_CAN_ENABLE); 103 | #else 104 | gpio_clear( 105 | GPIO_PORT_CAN_ENABLE, 106 | GPIO_PIN_CAN_ENABLE); 107 | #endif // CAN_ENABLE_INVERTED 108 | #endif // USE_CAN_ENABLE 109 | 110 | /* 111 | STM32F3 CAN1 on 18MHz configured APB1 peripheral clock 112 | 18MHz / 2 -> 9MHz 113 | 9MHz / (1tq + 10tq + 7tq) = 500kHz => 500kbit 114 | */ 115 | can_init(CAN, // Interface 116 | false, // Time triggered communication mode. 117 | true, // Automatic bus-off management. 118 | false, // Automatic wakeup mode. 119 | false, // No automatic retransmission. 120 | false, // Receive FIFO locked mode. 121 | true, // Transmit FIFO priority. 122 | CAN_BTR_SJW_1TQ, // Resynchronization time quanta jump width 123 | CAN_BTR_TS1_10TQ, // Time segment 1 time quanta width 124 | CAN_BTR_TS2_7TQ, // Time segment 2 time quanta width 125 | 2, // Prescaler 126 | false, // Loopback 127 | false); // Silent 128 | 129 | // filter to match any standard id 130 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 131 | can_filter_id_mask_32bit_init( 132 | CAN, 133 | 0, // filter nr 134 | 0, // id: only std id, no rtr 135 | 6 | (7 << 29), // mask: match only std id[10:8] = 0 (bootloader frames) 136 | 0, // assign to fifo0 137 | true // enable 138 | ); 139 | 140 | // nvic_enable_irq(NVIC_IRQ_CAN); 141 | } 142 | 143 | void fault_handler(void) 144 | { 145 | // while(1); // debug 146 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 147 | } 148 | 149 | void rcc_clock_setup_in_hsi_out_36mhz(void) 150 | { 151 | static clock_scale_t clock_36mhz = 152 | { 153 | pll: RCC_CFGR_PLLMUL_PLL_IN_CLK_X9, 154 | pllsrc: RCC_CFGR_PLLSRC_HSI_DIV2, 155 | hpre: RCC_CFGR_HPRE_DIV_NONE, 156 | ppre1: RCC_CFGR_PPRE1_DIV_2, 157 | ppre2: RCC_CFGR_PPRE2_DIV_NONE, 158 | power_save: 1, 159 | flash_config: FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_1WS, 160 | apb1_frequency: 18000000, 161 | apb2_frequency: 36000000, 162 | }; 163 | 164 | rcc_clock_setup_hsi(&clock_36mhz); 165 | } 166 | 167 | void platform_main(int arg) 168 | { 169 | /* 170 | // If external 8MHz present on PF0/PF1 171 | rcc_clock_setup_in_hse_8mhz_out_72mhz(); 172 | */ 173 | // Otherwise use internal RC oscillator 174 | rcc_clock_setup_in_hsi_out_36mhz(); 175 | 176 | // Initialize the onboard LED 177 | led_init(); 178 | 179 | // Blink onboard LED to indicate platform startup 180 | led_blink(); 181 | 182 | // Configure timeout of 10000 milliseconds (assuming 36 Mhz system clock, see above) 183 | timeout_timer_init(36000000, 10000); 184 | can_interface_init(); 185 | bootloader_main(arg); 186 | 187 | reboot_system(BOOT_ARG_START_BOOTLOADER); 188 | } 189 | -------------------------------------------------------------------------------- /platform/nucleo-board-stm32f334r8/platform.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Platform support for the STM32-Nucleo64 Board F334R8 3 | * http://www.st.com/en/evaluation-tools/nucleo-f334r8.html 4 | */ 5 | 6 | #ifndef PLATFORM_H 7 | #define PLATFORM_H 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #include 14 | #include 15 | 16 | #define PLATFORM_DEVICE_CLASS "nucleo-board-stm32f334r8" 17 | #define FLASH_PAGE_SIZE 0x0800 // 2K 18 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 19 | 20 | // Onboard LED 21 | #define GPIO_PORT_LED2 GPIOA 22 | #define GPIO_PIN_LED2 GPIO5 23 | 24 | // CAN pins 25 | #define GPIO_PORT_CAN_RX GPIOB 26 | #define GPIO_PIN_CAN_RX GPIO8 27 | #define GPIO_PORT_CAN_TX GPIOB 28 | #define GPIO_PIN_CAN_TX GPIO9 29 | #define GPIO_AF_CAN GPIO_AF9 30 | 31 | /* 32 | * Many CAN transceivers have an enable input, 33 | * which needs to be driven HIGH or LOW in order 34 | * for the transceiver to become operational. 35 | * Define USE_CAN_ENABLE to set the pin 36 | * defined below to HIGH upon startup. 37 | * Additionally define CAN_ENABLE_INVERTED 38 | * to drive it LOW instead. 39 | */ 40 | //#define USE_CAN_ENABLE 41 | //#define CAN_ENABLE_INVERTED 42 | 43 | #ifdef USE_CAN_ENABLE 44 | #define GPIO_PORT_CAN_ENABLE GPIOA 45 | #define GPIO_PIN_CAN_ENABLE GPIO8 46 | #endif 47 | 48 | // Import symbols from linker script 49 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 50 | extern int application_address, application_size, config_page1, config_page2; 51 | 52 | static inline void* memory_get_app_addr(void) 53 | { 54 | return (void*)&application_address; 55 | } 56 | 57 | static inline size_t memory_get_app_size(void) 58 | { 59 | return (size_t)&application_size; 60 | } 61 | 62 | static inline void* memory_get_config1_addr(void) 63 | { 64 | return (void*)&config_page1; 65 | } 66 | 67 | static inline void* memory_get_config2_addr(void) 68 | { 69 | return (void*)&config_page2; 70 | } 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif /* PLATFORM_H */ 77 | -------------------------------------------------------------------------------- /platform/olimex-e407/.gitignore: -------------------------------------------------------------------------------- 1 | include.mk 2 | build 3 | -------------------------------------------------------------------------------- /platform/olimex-e407/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # settings 3 | ################################################################################ 4 | 5 | PROJNAME = bootloader 6 | 7 | # where to place the build files 8 | BUILD_DIR = build 9 | 10 | # platform specific source files 11 | PLATFORM_CSRC = platform.c 12 | 13 | PROJ_ROOT = ../.. 14 | PLATFORM_PATH = platform/olimex-e407/ 15 | 16 | # dont show compiler calls 17 | QUIET = 1 18 | 19 | # 20 | # low level settings 21 | ################################################################################ 22 | 23 | include include.mk 24 | 25 | CSRC += $(addprefix $(PLATFORM_PATH), $(PLATFORM_CSRC)) 26 | 27 | # libraries & includes 28 | INCDIR = ./ $(PROJ_ROOT)/libopencm3/include $(PROJ_ROOT)/ 29 | INCDIR += $(addprefix $(PROJ_ROOT)/, $(PACKAGER_INCDIR)) 30 | 31 | LIBS = -lc -lnosys 32 | LIBS += -L$(PROJ_ROOT)/libopencm3/lib 33 | LIBS += -lopencm3_stm32f4 34 | 35 | LDSCRIPT = ./linkerscript.ld 36 | 37 | # defines 38 | DEFS += -DSTM32F4 39 | DEFS += -DFLASH_PROGRAM_SIZE=2 # faster flash erase 40 | 41 | # Cortex-M4f 42 | CFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 43 | LFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 44 | 45 | # C & C++ compiler flags 46 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 47 | CFLAGS += $(OPTIMIZATION) -g -Wall -Wextra -Wno-unused-parameter 48 | CFLAGS += $(DEFS) -fomit-frame-pointer -MD 49 | CFLAGS += $(addprefix -I, $(INCDIR)) 50 | # C only flags 51 | CCFLAGS += -Wstrict-prototypes 52 | # C++ only flags 53 | CXXFLAGS += -fno-rtti -fno-exceptions -fno-unwind-tables 54 | CXXFLAGS += -fno-use-cxa-atexit 55 | # Linker flags 56 | LFLAGS += $(LIBS) -T$(LDSCRIPT) -nostartfiles -Wl,-Map=$(PROJNAME).map 57 | LFLAGS += -Wl,--gc-sections 58 | 59 | CCFLAGS += $(CFLAGS) 60 | CXXFLAGS += $(CFLAGS) 61 | ASMFLAGS += $(CFLAGS) 62 | 63 | COBJS = $(addprefix $(BUILD_DIR)/, $(CSRC:.c=.o)) 64 | ASMOBJS = $(addprefix $(BUILD_DIR)/, $(ASMSRC:.s=.o)) 65 | CXXOBJS = $(addprefix $(BUILD_DIR)/, $(CXXSRC:.cpp=.o)) 66 | 67 | OBJS = $(COBJS) $(ASMOBJS) $(CXXOBJS) 68 | 69 | # gcc optimization level 70 | OPTIMIZATION = -Os 71 | 72 | CC = arm-none-eabi-gcc 73 | CXX = arm-none-eabi-g++ 74 | AS = arm-none-eabi-gcc -x assembler-with-cpp 75 | LD = arm-none-eabi-g++ 76 | AR = arm-none-eabi-ar 77 | OC = arm-none-eabi-objcopy 78 | OD = arm-none-eabi-objdump 79 | NM = arm-none-eabi-nm 80 | SZ = arm-none-eabi-size 81 | 82 | MKDIR = mkdir -p 83 | 84 | COLOR = \033[1;31m 85 | COLOR_CLEAR = \033[0m 86 | PRINT = @printf "$(COLOR)%s$(COLOR_CLEAR)\n" 87 | 88 | ifeq ($(QUIET),1) 89 | Q = @ 90 | endif 91 | 92 | # 93 | # targets: 94 | ################################################################################ 95 | 96 | .PHONY: all 97 | all: $(PROJNAME).bin $(PROJNAME).lst $(PROJNAME).size.txt Makefile 98 | $(PRINT) "> done: $(PROJNAME)" 99 | @ $(SZ) $(PROJNAME).elf 100 | 101 | .PHONY: clean 102 | clean: 103 | $(Q)-rm -f $(OBJS) 104 | $(Q)-rm -f $(OBJS:.o=.lst) 105 | $(Q)-rm -f $(OBJS:.o=.d) 106 | $(Q)-rm -f $(PROJNAME).elf 107 | $(Q)-rm -f $(PROJNAME).bin 108 | $(Q)-rm -f $(PROJNAME).lst 109 | $(Q)-rm -f $(PROJNAME).map 110 | $(Q)-rm -f $(PROJNAME).size.txt 111 | 112 | .PHONY: rebuild 113 | rebuild: clean all 114 | 115 | # make targets for flashing with openocd 116 | .PHONY: flash 117 | flash: all 118 | openocd -f oocd.cfg -c "program $(PROJNAME).elf verify reset" -c "shutdown" 119 | 120 | .PHONY: reset 121 | reset: 122 | openocd -f oocd.cfg -c "init" -c "reset" -c "shutdown" 123 | 124 | # 125 | # file targets: 126 | ################################################################################ 127 | 128 | # binary 129 | $(PROJNAME).bin: $(PROJNAME).elf 130 | $(PRINT) "> copying" 131 | $(Q) $(OC) -Obinary -j .text -j .rodata -j .data $(PROJNAME).elf $(PROJNAME).bin 132 | 133 | # assembly listing 134 | $(PROJNAME).lst: $(PROJNAME).elf 135 | $(PRINT) "> generating assembly listing" 136 | $(Q) $(OD) -D -h $(PROJNAME).elf > $(PROJNAME).lst 137 | 138 | # linked elf-object 139 | $(PROJNAME).elf: $(OBJS) $(LDSCRIPT) 140 | $(PRINT) "> linking" 141 | $(Q) $(LD) -o $(PROJNAME).elf $(OBJS) $(LFLAGS) 142 | 143 | # object from c 144 | $(COBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.c Makefile 145 | $(Q) $(MKDIR) $(shell dirname ${@}) 146 | $(PRINT) "> compiling ("$<")" 147 | $(Q) $(CC) $(CCFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 148 | 149 | # object from asm 150 | $(ASMOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.s Makefile 151 | $(Q) $(MKDIR) $(shell dirname ${@}) 152 | $(PRINT) "> assembling ("$<")" 153 | $(Q) $(AS) $(ASMFLAGS) -c ${<} -o ${@} 154 | 155 | # object from c++ 156 | $(CXXOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.cpp Makefile 157 | $(Q) $(MKDIR) $(shell dirname ${@}) 158 | $(PRINT) "> compiling ("$<")" 159 | $(Q) $(CXX) $(CXXFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 160 | 161 | # space usage 162 | $(PROJNAME).size.txt: $(PROJNAME).elf 163 | $(PRINT) "> calculating space usage" 164 | $(Q)$(SZ) $(PROJNAME).elf > $(PROJNAME).size.txt 165 | $(Q)$(NM) --size-sort --print-size -S $(PROJNAME).elf >> $(PROJNAME).size.txt 166 | 167 | # include the dependencies for all objects 168 | # (generated by the -MD compiler-flag) 169 | # -include $(OBJS:.o=.d) 170 | -------------------------------------------------------------------------------- /platform/olimex-e407/flash.mk: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /platform/olimex-e407/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f4 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/olimex-e407/oocd.cfg: -------------------------------------------------------------------------------- 1 | telnet_port 4444 2 | gdb_port 3333 3 | 4 | # interface configuration # 5 | source [find interface/ftdi/dp_busblaster.cfg] 6 | 7 | # comment out for BusBlaster without modified VID/PID # 8 | ftdi_vid_pid 0x0403 0xbb02 9 | ftdi_device_desc "BusBlasterV2" 10 | 11 | adapter_khz 1000 12 | 13 | # target configuration # 14 | source [find target/stm32f4x.cfg] 15 | 16 | -------------------------------------------------------------------------------- /platform/olimex-e407/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "platform.h" 10 | 11 | #define GPIOC_LED GPIO13 12 | #define GPIOD_CAN1_RX GPIO0 13 | #define GPIOD_CAN1_TX GPIO1 14 | 15 | // page buffer used by config commands. 16 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 17 | 18 | void can_interface_init(void) 19 | { 20 | rcc_periph_clock_enable(RCC_CAN1); 21 | 22 | /* 23 | STM32F3 CAN1 on 42MHz configured APB1 peripheral clock 24 | 42MHz / 2 -> 21MHz 25 | 21MHz / (1tq + 12tq + 8tq) = 1MHz => 1Mbit 26 | */ 27 | can_init(CAN1, // Interface 28 | false, // Time triggered communication mode. 29 | true, // Automatic bus-off management. 30 | false, // Automatic wakeup mode. 31 | false, // No automatic retransmission. 32 | false, // Receive FIFO locked mode. 33 | true, // Transmit FIFO priority. 34 | CAN_BTR_SJW_4TQ, // Resynchronization time quanta jump width 35 | CAN_BTR_TS1_12TQ, // Time segment 1 time quanta width 36 | CAN_BTR_TS2_8TQ, // Time segment 2 time quanta width 37 | 2, // Prescaler 38 | false, // Loopback 39 | false); // Silent 40 | 41 | // filter to match any standard id 42 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 43 | can_filter_id_mask_32bit_init( 44 | CAN1, 45 | 0, // filter nr 46 | 0, // id: only std id, no rtr 47 | 6 | (7 << 29), // mask: match only std id[10:8] = 0 (bootloader frames) 48 | 0, // assign to fifo0 49 | true // enable 50 | ); 51 | } 52 | 53 | void fault_handler(void) 54 | { 55 | // while(1); // debug 56 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 57 | } 58 | 59 | void platform_main(int arg) 60 | { 61 | rcc_clock_setup_hse_3v3(&hse_12mhz_3v3[CLOCK_3V3_168MHZ]); 62 | 63 | rcc_periph_clock_enable(RCC_GPIOD); 64 | rcc_periph_clock_enable(RCC_GPIOC); 65 | 66 | // CAN pin 67 | gpio_mode_setup(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIOD_CAN1_RX | GPIOD_CAN1_TX); 68 | gpio_set_output_options(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOD_CAN1_RX | GPIOD_CAN1_TX); 69 | gpio_set_af(GPIOD, GPIO_AF9, GPIOD_CAN1_RX | GPIOD_CAN1_TX); 70 | 71 | // LED on 72 | gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOC_LED); 73 | gpio_set_output_options(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOC_LED); 74 | gpio_clear(GPIOC, GPIOC_LED); 75 | 76 | // configure timeout of 10000 milliseconds 77 | timeout_timer_init(168000000, 10000); 78 | 79 | can_interface_init(); 80 | 81 | bootloader_main(arg); 82 | 83 | reboot_system(BOOT_ARG_START_BOOTLOADER); 84 | } 85 | -------------------------------------------------------------------------------- /platform/olimex-e407/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define PLATFORM_DEVICE_CLASS "olimex-e407" 12 | #define FLASH_PAGE_SIZE 0x4000 // 16K 13 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 14 | 15 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 16 | 17 | // symbols defined in linkerscript 18 | extern int application_address, application_size, config_page1, config_page2; 19 | 20 | static inline void* memory_get_app_addr(void) 21 | { 22 | return (void*)&application_address; 23 | } 24 | 25 | static inline size_t memory_get_app_size(void) 26 | { 27 | return (size_t)&application_size; 28 | } 29 | 30 | static inline void* memory_get_config1_addr(void) 31 | { 32 | return (void*)&config_page1; 33 | } 34 | 35 | static inline void* memory_get_config2_addr(void) 36 | { 37 | return (void*)&config_page2; 38 | } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif /* PLATFORM_H */ 45 | -------------------------------------------------------------------------------- /platform/rc-board-v1/.gitignore: -------------------------------------------------------------------------------- 1 | include.mk 2 | build 3 | -------------------------------------------------------------------------------- /platform/rc-board-v1/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # settings 3 | ################################################################################ 4 | 5 | PROJNAME = bootloader 6 | 7 | # where to place the build files 8 | BUILD_DIR = build 9 | 10 | # platform specific source files 11 | PLATFORM_CSRC = platform.c 12 | 13 | PROJ_ROOT = ../.. 14 | PLATFORM_PATH = platform/rc-board-v1/ 15 | 16 | # dont show compiler calls 17 | QUIET = 1 18 | 19 | # 20 | # low level settings 21 | ################################################################################ 22 | 23 | include include.mk 24 | 25 | CSRC += $(addprefix $(PLATFORM_PATH), $(PLATFORM_CSRC)) 26 | 27 | # libraries & includes 28 | INCDIR = ./ $(PROJ_ROOT)/libopencm3/include $(PROJ_ROOT)/ 29 | INCDIR += $(addprefix $(PROJ_ROOT)/, $(PACKAGER_INCDIR)) 30 | 31 | LIBS = -lc -lnosys 32 | LIBS += -L$(PROJ_ROOT)/libopencm3/lib 33 | LIBS += -lopencm3_stm32f3 34 | 35 | LDSCRIPT = ./linkerscript.ld 36 | 37 | # defines 38 | DEFS += -DSTM32F3 -DSTM32F3 39 | 40 | # Cortex-M4f 41 | CFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 42 | LFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 43 | 44 | # C & C++ compiler flags 45 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 46 | CFLAGS += $(OPTIMIZATION) -g -Wall -Wextra -Wno-unused-parameter 47 | CFLAGS += $(DEFS) -fomit-frame-pointer -MD 48 | CFLAGS += $(addprefix -I, $(INCDIR)) 49 | # C only flags 50 | CCFLAGS += -Wstrict-prototypes 51 | # C++ only flags 52 | CXXFLAGS += -fno-rtti -fno-exceptions -fno-unwind-tables 53 | CXXFLAGS += -fno-use-cxa-atexit 54 | # Linker flags 55 | LFLAGS += $(LIBS) -T$(LDSCRIPT) -nostartfiles -Wl,-Map=$(PROJNAME).map 56 | LFLAGS += -Wl,--gc-sections 57 | 58 | CCFLAGS += $(CFLAGS) 59 | CXXFLAGS += $(CFLAGS) 60 | ASMFLAGS += $(CFLAGS) 61 | 62 | COBJS = $(addprefix $(BUILD_DIR)/, $(CSRC:.c=.o)) 63 | ASMOBJS = $(addprefix $(BUILD_DIR)/, $(ASMSRC:.s=.o)) 64 | CXXOBJS = $(addprefix $(BUILD_DIR)/, $(CXXSRC:.cpp=.o)) 65 | 66 | OBJS = $(COBJS) $(ASMOBJS) $(CXXOBJS) 67 | 68 | # gcc optimization level 69 | OPTIMIZATION = -Os 70 | 71 | CC = arm-none-eabi-gcc 72 | CXX = arm-none-eabi-g++ 73 | AS = arm-none-eabi-gcc -x assembler-with-cpp 74 | LD = arm-none-eabi-g++ 75 | AR = arm-none-eabi-ar 76 | OC = arm-none-eabi-objcopy 77 | OD = arm-none-eabi-objdump 78 | NM = arm-none-eabi-nm 79 | SZ = arm-none-eabi-size 80 | 81 | MKDIR = mkdir -p 82 | 83 | COLOR = \033[1;31m 84 | COLOR_CLEAR = \033[0m 85 | PRINT = @printf "$(COLOR)%s$(COLOR_CLEAR)\n" 86 | 87 | ifeq ($(QUIET),1) 88 | Q = @ 89 | endif 90 | 91 | # 92 | # targets: 93 | ################################################################################ 94 | 95 | .PHONY: all 96 | all: $(PROJNAME).bin $(PROJNAME).lst $(PROJNAME).size.txt Makefile 97 | $(PRINT) "> done: $(PROJNAME)" 98 | @ $(SZ) $(PROJNAME).elf 99 | 100 | .PHONY: clean 101 | clean: 102 | $(Q)-rm -f $(OBJS) 103 | $(Q)-rm -f $(OBJS:.o=.lst) 104 | $(Q)-rm -f $(OBJS:.o=.d) 105 | $(Q)-rm -f $(PROJNAME).elf 106 | $(Q)-rm -f $(PROJNAME).bin 107 | $(Q)-rm -f $(PROJNAME).lst 108 | $(Q)-rm -f $(PROJNAME).map 109 | $(Q)-rm -f $(PROJNAME).size.txt 110 | 111 | .PHONY: rebuild 112 | rebuild: clean all 113 | 114 | -include tools.mk 115 | 116 | # 117 | # file targets: 118 | ################################################################################ 119 | 120 | # binary 121 | $(PROJNAME).bin: $(PROJNAME).elf 122 | $(PRINT) "> copying" 123 | $(Q) $(OC) -Obinary -j .text -j .rodata -j .data $(PROJNAME).elf $(PROJNAME).bin 124 | 125 | # assembly listing 126 | $(PROJNAME).lst: $(PROJNAME).elf 127 | $(PRINT) "> generating assembly listing" 128 | $(Q) $(OD) -D -h $(PROJNAME).elf > $(PROJNAME).lst 129 | 130 | # linked elf-object 131 | $(PROJNAME).elf: $(OBJS) $(LDSCRIPT) 132 | $(PRINT) "> linking" 133 | $(Q) $(LD) -o $(PROJNAME).elf $(OBJS) $(LFLAGS) 134 | 135 | # object from c 136 | $(COBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.c Makefile 137 | $(Q) $(MKDIR) $(shell dirname ${@}) 138 | $(PRINT) "> compiling ("$<")" 139 | $(Q) $(CC) $(CCFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 140 | 141 | # object from asm 142 | $(ASMOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.s Makefile 143 | $(Q) $(MKDIR) $(shell dirname ${@}) 144 | $(PRINT) "> assembling ("$<")" 145 | $(Q) $(AS) $(ASMFLAGS) -c ${<} -o ${@} 146 | 147 | # object from c++ 148 | $(CXXOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.cpp Makefile 149 | $(Q) $(MKDIR) $(shell dirname ${@}) 150 | $(PRINT) "> compiling ("$<")" 151 | $(Q) $(CXX) $(CXXFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 152 | 153 | # space usage 154 | $(PROJNAME).size.txt: $(PROJNAME).elf 155 | $(PRINT) "> calculating space usage" 156 | $(Q)$(SZ) $(PROJNAME).elf > $(PROJNAME).size.txt 157 | $(Q)$(NM) --size-sort --print-size -S $(PROJNAME).elf >> $(PROJNAME).size.txt 158 | 159 | # include the dependencies for all objects 160 | # (generated by the -MD compiler-flag) 161 | # -include $(OBJS:.o=.d) 162 | -------------------------------------------------------------------------------- /platform/rc-board-v1/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f3 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/rc-board-v1/oocd.cfg: -------------------------------------------------------------------------------- 1 | telnet_port 4444 2 | gdb_port 3333 3 | 4 | # interface configuration # 5 | 6 | source [find interface/ftdi/dp_busblaster.cfg] 7 | # source [find interface/ftdi/olimex-arm-usb-ocd.cfg] 8 | 9 | # target configuration # 10 | 11 | source [find target/stm32f3x.cfg] 12 | 13 | adapter_khz 100 14 | -------------------------------------------------------------------------------- /platform/rc-board-v1/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "platform.h" 10 | 11 | // page buffer used by config commands. 12 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 13 | 14 | void can_interface_init(void) 15 | { 16 | rcc_periph_clock_enable(RCC_CAN); 17 | rcc_periph_clock_enable(RCC_GPIOA); 18 | 19 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO8); 20 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIO8); 21 | gpio_clear(GPIOA, GPIO8); 22 | 23 | gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); 24 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIO11 | GPIO12); 25 | gpio_set_af(GPIOA, GPIO_AF9, GPIO11 | GPIO12); 26 | 27 | /* 28 | STM32F3 CAN on 36MHz configured APB1 peripheral clock 29 | 36MHz / 2 -> 18MHz 30 | 18MHz / (1tq + 10tq + 7tq) = 1MHz => 1Mbit 31 | */ 32 | can_init(CAN, // Interface 33 | false, // Time triggered communication mode. 34 | true, // Automatic bus-off management. 35 | false, // Automatic wakeup mode. 36 | false, // No automatic retransmission. 37 | false, // Receive FIFO locked mode. 38 | true, // Transmit FIFO priority. 39 | CAN_BTR_SJW_1TQ, // Resynchronization time quanta jump width 40 | CAN_BTR_TS1_10TQ, // Time segment 1 time quanta width 41 | CAN_BTR_TS2_7TQ, // Time segment 2 time quanta width 42 | 2, // Prescaler 43 | false, // Loopback 44 | false); // Silent 45 | 46 | // filter to match any standard id 47 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 48 | can_filter_id_mask_32bit_init( 49 | CAN, 50 | 0, // filter nr 51 | 0, // id: only std id, no rtr 52 | 6, // mask: macth any std id 53 | 0, // assign to fifo0 54 | true // enable 55 | ); 56 | } 57 | 58 | void fault_handler(void) 59 | { 60 | gpio_set(GPIOC, GPIO15); 61 | 62 | while (1) 63 | ; // debug 64 | 65 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 66 | } 67 | 68 | typedef struct { 69 | uint8_t pllpre; 70 | uint8_t pll; 71 | uint8_t pllsrc; 72 | uint32_t flash_config; 73 | uint8_t hpre; 74 | uint8_t ppre1; 75 | uint8_t ppre2; 76 | uint8_t power_save; 77 | uint32_t apb1_frequency; 78 | uint32_t apb2_frequency; 79 | } my_clock_scale_t; 80 | 81 | // clock config for external HSE 16MHz cristal 82 | static const my_clock_scale_t clock_72mhz = { 83 | .pllpre = RCC_CFGR2_PREDIV_HSE_IN_PLL_DIV_2, 84 | .pll = RCC_CFGR_PLLMUL_PLL_IN_CLK_X9, 85 | .pllsrc = RCC_CFGR_PLLSRC_HSE_PREDIV, 86 | .hpre = RCC_CFGR_HPRE_DIV_NONE, 87 | .ppre1 = RCC_CFGR_PPRE1_DIV_2, 88 | .ppre2 = RCC_CFGR_PPRE2_DIV_NONE, 89 | .power_save = 1, 90 | .flash_config = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2WS, 91 | .apb1_frequency = 36000000, 92 | .apb2_frequency = 72000000, 93 | }; 94 | 95 | static inline void rcc_set_main_pll_hse(uint32_t pll) 96 | { 97 | RCC_CFGR = (~RCC_CFGR_PLLMUL_MASK & RCC_CFGR) | (pll << RCC_CFGR_PLLMUL_SHIFT) | RCC_CFGR_PLLSRC; 98 | } 99 | 100 | static inline void rcc_clock_setup_hse(const my_clock_scale_t* clock) 101 | { 102 | /* Enable internal high-speed oscillator. */ 103 | rcc_osc_on(HSE); 104 | rcc_wait_for_osc_ready(HSE); 105 | /* Select HSE as SYSCLK source. */ 106 | rcc_set_sysclk_source(RCC_CFGR_SW_HSE); 107 | rcc_wait_for_sysclk_status(HSE); 108 | 109 | rcc_osc_off(PLL); 110 | rcc_wait_for_osc_not_ready(PLL); 111 | rcc_set_pll_source(clock->pllsrc); 112 | rcc_set_main_pll_hse(clock->pll); 113 | RCC_CFGR2 = (clock->pllpre << RCC_CFGR2_PREDIV_SHIFT); 114 | /* Enable PLL oscillator and wait for it to stabilize. */ 115 | rcc_osc_on(PLL); 116 | rcc_wait_for_osc_ready(PLL); 117 | 118 | rcc_set_hpre(clock->hpre); 119 | rcc_set_ppre2(clock->ppre2); 120 | rcc_set_ppre1(clock->ppre1); 121 | /* Configure flash settings. */ 122 | flash_set_ws(clock->flash_config); 123 | /* Select PLL as SYSCLK source. */ 124 | rcc_set_sysclk_source(RCC_CFGR_SW_PLL); 125 | /* Wait for PLL clock to be selected. */ 126 | rcc_wait_for_sysclk_status(PLL); 127 | 128 | /* Set the peripheral clock frequencies used. */ 129 | rcc_apb1_frequency = clock->apb1_frequency; 130 | rcc_apb2_frequency = clock->apb2_frequency; 131 | } 132 | 133 | void platform_main(int arg) 134 | { 135 | rcc_clock_setup_hse(&clock_72mhz); 136 | 137 | // LEDs 138 | rcc_periph_clock_enable(RCC_GPIOC); 139 | gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO13 | GPIO14 | GPIO15); 140 | gpio_set_output_options(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIO13 | GPIO14 | GPIO15); 141 | 142 | // white led on 143 | gpio_set(GPIOC, GPIO13); 144 | 145 | // configure timeout of 3000 milliseconds 146 | timeout_timer_init(72000000, 3000); 147 | 148 | can_interface_init(); 149 | 150 | bootloader_main(arg); 151 | 152 | reboot_system(BOOT_ARG_START_BOOTLOADER); 153 | } 154 | -------------------------------------------------------------------------------- /platform/rc-board-v1/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define PLATFORM_DEVICE_CLASS "rc-baord-v1" 12 | #define FLASH_PAGE_SIZE 2048 13 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 14 | 15 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 16 | 17 | // symbols defined in linkerscript 18 | extern int application_address, application_size, config_page1, config_page2; 19 | 20 | static inline void* memory_get_app_addr(void) 21 | { 22 | return (void*)&application_address; 23 | } 24 | 25 | static inline size_t memory_get_app_size(void) 26 | { 27 | return (size_t)&application_size; 28 | } 29 | 30 | static inline void* memory_get_config1_addr(void) 31 | { 32 | return (void*)&config_page1; 33 | } 34 | 35 | static inline void* memory_get_config2_addr(void) 36 | { 37 | return (void*)&config_page2; 38 | } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif /* PLATFORM_H */ 45 | -------------------------------------------------------------------------------- /platform/rc-board-v1/tools.mk: -------------------------------------------------------------------------------- 1 | 2 | # make targets for flashing with openocd 3 | 4 | .PHONY: flash 5 | flash: all 6 | openocd -f oocd.cfg -c "program $(PROJNAME).elf verify reset" 7 | 8 | .PHONY: reset 9 | reset: 10 | openocd -f oocd.cfg -c "init" -c "reset" -c "shutdown" 11 | 12 | # old: command 13 | # openocd -f oocd.cfg -c init -c targets -c "reset halt" -c "flash write_image erase $(PROJNAME).elf" -c "verify_image $(PROJNAME).elf" -c shutdown 14 | -------------------------------------------------------------------------------- /platform/sensor-board/.gitignore: -------------------------------------------------------------------------------- 1 | include.mk 2 | build 3 | -------------------------------------------------------------------------------- /platform/sensor-board/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # settings 3 | ################################################################################ 4 | 5 | PROJNAME = bootloader 6 | 7 | # where to place the build files 8 | BUILD_DIR = build 9 | 10 | # platform specific source files 11 | PLATFORM_CSRC = platform.c 12 | 13 | PROJ_ROOT = ../.. 14 | PLATFORM_PATH = platform/sensor-board/ 15 | 16 | # dont show compiler calls 17 | QUIET = 1 18 | 19 | # 20 | # low level settings 21 | ################################################################################ 22 | 23 | include include.mk 24 | 25 | CSRC += $(addprefix $(PLATFORM_PATH), $(PLATFORM_CSRC)) 26 | 27 | # libraries & includes 28 | INCDIR = ./ $(PROJ_ROOT)/libopencm3/include $(PROJ_ROOT)/ 29 | INCDIR += $(addprefix $(PROJ_ROOT)/, $(PACKAGER_INCDIR)) 30 | 31 | LIBS = -lc -lnosys 32 | LIBS += -L$(PROJ_ROOT)/libopencm3/lib 33 | LIBS += -lopencm3_stm32f3 34 | 35 | LDSCRIPT = ./linkerscript.ld 36 | 37 | # defines 38 | DEFS += -DSTM32F3 39 | 40 | # Cortex-M4f 41 | CFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 42 | LFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 43 | 44 | # C & C++ compiler flags 45 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 46 | CFLAGS += $(OPTIMIZATION) -g -Wall -Wextra -Wno-unused-parameter 47 | CFLAGS += $(DEFS) -fomit-frame-pointer -MD 48 | CFLAGS += $(addprefix -I, $(INCDIR)) 49 | # C only flags 50 | CCFLAGS += -Wstrict-prototypes 51 | # C++ only flags 52 | CXXFLAGS += -fno-rtti -fno-exceptions -fno-unwind-tables 53 | CXXFLAGS += -fno-use-cxa-atexit 54 | # Linker flags 55 | LFLAGS += $(LIBS) -T$(LDSCRIPT) -nostartfiles -Wl,-Map=$(PROJNAME).map 56 | LFLAGS += -Wl,--gc-sections 57 | LFLAGS += -lc_nano 58 | 59 | CCFLAGS += $(CFLAGS) 60 | CXXFLAGS += $(CFLAGS) 61 | ASMFLAGS += $(CFLAGS) 62 | 63 | COBJS = $(addprefix $(BUILD_DIR)/, $(CSRC:.c=.o)) 64 | ASMOBJS = $(addprefix $(BUILD_DIR)/, $(ASMSRC:.s=.o)) 65 | CXXOBJS = $(addprefix $(BUILD_DIR)/, $(CXXSRC:.cpp=.o)) 66 | 67 | OBJS = $(COBJS) $(ASMOBJS) $(CXXOBJS) 68 | 69 | # gcc optimization level 70 | OPTIMIZATION = -Os 71 | 72 | CC = arm-none-eabi-gcc 73 | CXX = arm-none-eabi-g++ 74 | AS = arm-none-eabi-gcc -x assembler-with-cpp 75 | LD = arm-none-eabi-g++ 76 | AR = arm-none-eabi-ar 77 | OC = arm-none-eabi-objcopy 78 | OD = arm-none-eabi-objdump 79 | NM = arm-none-eabi-nm 80 | SZ = arm-none-eabi-size 81 | 82 | MKDIR = mkdir -p 83 | 84 | COLOR = \033[1;31m 85 | COLOR_CLEAR = \033[0m 86 | PRINT = @printf "$(COLOR)%s$(COLOR_CLEAR)\n" 87 | 88 | ifeq ($(QUIET),1) 89 | Q = @ 90 | endif 91 | 92 | # 93 | # targets: 94 | ################################################################################ 95 | 96 | .PHONY: all 97 | all: $(PROJNAME).bin $(PROJNAME).lst $(PROJNAME).size.txt Makefile 98 | $(PRINT) "> done: $(PROJNAME)" 99 | @ $(SZ) $(PROJNAME).elf 100 | 101 | .PHONY: clean 102 | clean: 103 | $(Q)-rm -f $(OBJS) 104 | $(Q)-rm -f $(OBJS:.o=.lst) 105 | $(Q)-rm -f $(OBJS:.o=.d) 106 | $(Q)-rm -f $(PROJNAME).elf 107 | $(Q)-rm -f $(PROJNAME).bin 108 | $(Q)-rm -f $(PROJNAME).lst 109 | $(Q)-rm -f $(PROJNAME).map 110 | $(Q)-rm -f $(PROJNAME).size.txt 111 | 112 | .PHONY: rebuild 113 | rebuild: clean all 114 | 115 | # make targets for flashing with openocd 116 | .PHONY: flash 117 | flash: all 118 | openocd -f oocd.cfg -c "program $(PROJNAME).elf verify reset" -c "shutdown" 119 | 120 | .PHONY: reset 121 | reset: 122 | openocd -f oocd.cfg -c "init" -c "reset" -c "shutdown" 123 | 124 | # 125 | # file targets: 126 | ################################################################################ 127 | 128 | # binary 129 | $(PROJNAME).bin: $(PROJNAME).elf 130 | $(PRINT) "> copying" 131 | $(Q) $(OC) -Obinary -j .text -j .rodata -j .data $(PROJNAME).elf $(PROJNAME).bin 132 | 133 | # assembly listing 134 | $(PROJNAME).lst: $(PROJNAME).elf 135 | $(PRINT) "> generating assembly listing" 136 | $(Q) $(OD) -D -h $(PROJNAME).elf > $(PROJNAME).lst 137 | 138 | # linked elf-object 139 | $(PROJNAME).elf: $(OBJS) $(LDSCRIPT) 140 | $(PRINT) "> linking" 141 | $(Q) $(LD) -o $(PROJNAME).elf $(OBJS) $(LFLAGS) 142 | 143 | # object from c 144 | $(COBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.c Makefile 145 | $(Q) $(MKDIR) $(shell dirname ${@}) 146 | $(PRINT) "> compiling ("$<")" 147 | $(Q) $(CC) $(CCFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 148 | 149 | # object from asm 150 | $(ASMOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.s Makefile 151 | $(Q) $(MKDIR) $(shell dirname ${@}) 152 | $(PRINT) "> assembling ("$<")" 153 | $(Q) $(AS) $(ASMFLAGS) -c ${<} -o ${@} 154 | 155 | # object from c++ 156 | $(CXXOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.cpp Makefile 157 | $(Q) $(MKDIR) $(shell dirname ${@}) 158 | $(PRINT) "> compiling ("$<")" 159 | $(Q) $(CXX) $(CXXFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 160 | 161 | # space usage 162 | $(PROJNAME).size.txt: $(PROJNAME).elf 163 | $(PRINT) "> calculating space usage" 164 | $(Q)$(SZ) $(PROJNAME).elf > $(PROJNAME).size.txt 165 | $(Q)$(NM) --size-sort --print-size -S $(PROJNAME).elf >> $(PROJNAME).size.txt 166 | 167 | # include the dependencies for all objects 168 | # (generated by the -MD compiler-flag) 169 | # -include $(OBJS:.o=.d) 170 | -------------------------------------------------------------------------------- /platform/sensor-board/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f3 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/sensor-board/oocd.cfg: -------------------------------------------------------------------------------- 1 | telnet_port 4444 2 | gdb_port 3333 3 | 4 | # interface configuration # 5 | source [find interface/stlink-v2.cfg] 6 | transport select hla_swd 7 | adapter_khz 1000 8 | 9 | # target configuration # 10 | source [find target/stm32f3x.cfg] 11 | -------------------------------------------------------------------------------- /platform/sensor-board/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "platform.h" 10 | 11 | #define GPIOA_LED GPIO0 12 | #define GPIOA_CAN_DIS GPIO8 13 | #define GPIOA_CAN_RX GPIO11 14 | #define GPIOA_CAN_TX GPIO12 15 | 16 | // page buffer used by config commands. 17 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 18 | 19 | void can_interface_init(void) 20 | { 21 | rcc_periph_clock_enable(RCC_CAN); 22 | 23 | /* 24 | STM32F3 CAN on 36MHz configured APB1 peripheral clock 25 | 36MHz / 2 -> 18MHz 26 | 18MHz / (1tq + 10tq + 7tq) = 1MHz => 1Mbit 27 | */ 28 | can_init(CAN, // Interface 29 | false, // Time triggered communication mode. 30 | true, // Automatic bus-off management. 31 | false, // Automatic wakeup mode. 32 | false, // No automatic retransmission. 33 | false, // Receive FIFO locked mode. 34 | true, // Transmit FIFO priority. 35 | CAN_BTR_SJW_1TQ, // Resynchronization time quanta jump width 36 | CAN_BTR_TS1_10TQ, // Time segment 1 time quanta width 37 | CAN_BTR_TS2_7TQ, // Time segment 2 time quanta width 38 | 2, // Prescaler 39 | false, // Loopback 40 | false); // Silent 41 | 42 | // filter to match any standard id 43 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 44 | can_filter_id_mask_32bit_init( 45 | CAN, 46 | 0, // filter nr 47 | 0, // id: only std id, no rtr 48 | 6 | (7 << 29), // mask: match only std id[10:8] = 0 (bootloader frames) 49 | 0, // assign to fifo0 50 | true // enable 51 | ); 52 | } 53 | 54 | void fault_handler(void) 55 | { 56 | // while(1); // debug 57 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 58 | } 59 | 60 | typedef struct { 61 | uint8_t pllpre; 62 | uint8_t pll; 63 | uint8_t pllsrc; 64 | uint32_t flash_config; 65 | uint8_t hpre; 66 | uint8_t ppre1; 67 | uint8_t ppre2; 68 | uint8_t power_save; 69 | uint32_t apb1_frequency; 70 | uint32_t apb2_frequency; 71 | } my_clock_scale_t; 72 | 73 | // clock config for external HSE 16MHz cristal 74 | static const my_clock_scale_t clock_72mhz = { 75 | .pllpre = RCC_CFGR2_PREDIV_HSE_IN_PLL_DIV_2, 76 | .pll = RCC_CFGR_PLLMUL_PLL_IN_CLK_X9, 77 | .pllsrc = RCC_CFGR_PLLSRC_HSE_PREDIV, 78 | .hpre = RCC_CFGR_HPRE_DIV_NONE, 79 | .ppre1 = RCC_CFGR_PPRE1_DIV_2, 80 | .ppre2 = RCC_CFGR_PPRE2_DIV_NONE, 81 | .power_save = 1, 82 | .flash_config = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2WS, 83 | .apb1_frequency = 36000000, 84 | .apb2_frequency = 72000000, 85 | }; 86 | 87 | static inline void rcc_set_main_pll_hse(uint32_t pll) 88 | { 89 | RCC_CFGR = (~RCC_CFGR_PLLMUL_MASK & RCC_CFGR) | (pll << RCC_CFGR_PLLMUL_SHIFT) | RCC_CFGR_PLLSRC; 90 | } 91 | 92 | static inline void rcc_clock_setup_hse(const my_clock_scale_t* clock) 93 | { 94 | /* Enable internal high-speed oscillator. */ 95 | rcc_osc_on(HSE); 96 | rcc_wait_for_osc_ready(HSE); 97 | /* Select HSE as SYSCLK source. */ 98 | rcc_set_sysclk_source(RCC_CFGR_SW_HSE); 99 | rcc_wait_for_sysclk_status(HSE); 100 | 101 | rcc_osc_off(PLL); 102 | rcc_wait_for_osc_not_ready(PLL); 103 | rcc_set_pll_source(clock->pllsrc); 104 | rcc_set_main_pll_hse(clock->pll); 105 | RCC_CFGR2 = (clock->pllpre << RCC_CFGR2_PREDIV_SHIFT); 106 | /* Enable PLL oscillator and wait for it to stabilize. */ 107 | rcc_osc_on(PLL); 108 | rcc_wait_for_osc_ready(PLL); 109 | 110 | rcc_set_hpre(clock->hpre); 111 | rcc_set_ppre2(clock->ppre2); 112 | rcc_set_ppre1(clock->ppre1); 113 | /* Configure flash settings. */ 114 | flash_set_ws(clock->flash_config); 115 | /* Select PLL as SYSCLK source. */ 116 | rcc_set_sysclk_source(RCC_CFGR_SW_PLL); 117 | /* Wait for PLL clock to be selected. */ 118 | rcc_wait_for_sysclk_status(PLL); 119 | 120 | /* Set the peripheral clock frequencies used. */ 121 | rcc_apb1_frequency = clock->apb1_frequency; 122 | rcc_apb2_frequency = clock->apb2_frequency; 123 | } 124 | 125 | void platform_main(int arg) 126 | { 127 | rcc_clock_setup_hse(&clock_72mhz); 128 | 129 | rcc_periph_clock_enable(RCC_GPIOA); 130 | rcc_periph_clock_enable(RCC_GPIOB); 131 | 132 | // CAN pin 133 | gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIOA_CAN_RX | GPIOA_CAN_TX); 134 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOA_CAN_RX | GPIOA_CAN_TX); 135 | gpio_set_af(GPIOA, GPIO_AF9, GPIOA_CAN_RX | GPIOA_CAN_TX); 136 | 137 | // enable CAN transceiver 138 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOA_CAN_DIS); 139 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOA_CAN_DIS); 140 | gpio_clear(GPIOA, GPIOA_CAN_DIS); 141 | 142 | // LED on 143 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOA_LED); 144 | gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOA_LED); 145 | gpio_set(GPIOA, GPIOA_LED); 146 | 147 | // configure timeout of 10000 milliseconds 148 | timeout_timer_init(72000000, 10000); 149 | 150 | can_interface_init(); 151 | 152 | bootloader_main(arg); 153 | 154 | reboot_system(BOOT_ARG_START_BOOTLOADER); 155 | } 156 | -------------------------------------------------------------------------------- /platform/sensor-board/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define PLATFORM_DEVICE_CLASS "sensor-board" 12 | #define FLASH_PAGE_SIZE 2048 13 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 14 | 15 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 16 | 17 | // symbols defined in linkerscript 18 | extern int application_address, application_size, config_page1, config_page2; 19 | 20 | static inline void* memory_get_app_addr(void) 21 | { 22 | return (void*)&application_address; 23 | } 24 | 25 | static inline size_t memory_get_app_size(void) 26 | { 27 | return (size_t)&application_size; 28 | } 29 | 30 | static inline void* memory_get_config1_addr(void) 31 | { 32 | return (void*)&config_page1; 33 | } 34 | 35 | static inline void* memory_get_config2_addr(void) 36 | { 37 | return (void*)&config_page2; 38 | } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif /* PLATFORM_H */ 45 | -------------------------------------------------------------------------------- /platform/uwb-beacon/.gitignore: -------------------------------------------------------------------------------- 1 | include.mk 2 | build 3 | -------------------------------------------------------------------------------- /platform/uwb-beacon/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # settings 3 | ################################################################################ 4 | 5 | PROJNAME = bootloader 6 | 7 | # where to place the build files 8 | BUILD_DIR = build 9 | 10 | # platform specific source files 11 | PLATFORM_CSRC = platform.c 12 | 13 | PROJ_ROOT = ../.. 14 | PLATFORM_PATH = platform/uwb-beacon/ 15 | 16 | # dont show compiler calls 17 | QUIET = 1 18 | 19 | # 20 | # low level settings 21 | ################################################################################ 22 | 23 | include include.mk 24 | 25 | CSRC += $(addprefix $(PLATFORM_PATH), $(PLATFORM_CSRC)) 26 | 27 | # libraries & includes 28 | INCDIR = ./ $(PROJ_ROOT)/libopencm3/include $(PROJ_ROOT)/ 29 | INCDIR += $(addprefix $(PROJ_ROOT)/, $(PACKAGER_INCDIR)) 30 | 31 | LIBS = -lc -lnosys 32 | LIBS += -L$(PROJ_ROOT)/libopencm3/lib 33 | LIBS += -lopencm3_stm32f4 34 | 35 | LDSCRIPT = ./linkerscript.ld 36 | 37 | # defines 38 | DEFS += -DSTM32F4 39 | DEFS += -DFLASH_PROGRAM_SIZE=2 # faster flash erase 40 | 41 | # Cortex-M4f 42 | CFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 43 | LFLAGS += -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16 44 | 45 | # C & C++ compiler flags 46 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 47 | CFLAGS += $(OPTIMIZATION) -g -Wall -Wextra -Wno-unused-parameter 48 | CFLAGS += $(DEFS) -fomit-frame-pointer -MD 49 | CFLAGS += $(addprefix -I, $(INCDIR)) 50 | # C only flags 51 | CCFLAGS += -Wstrict-prototypes 52 | # C++ only flags 53 | CXXFLAGS += -fno-rtti -fno-exceptions -fno-unwind-tables 54 | CXXFLAGS += -fno-use-cxa-atexit 55 | # Linker flags 56 | LFLAGS += $(LIBS) -T$(LDSCRIPT) -nostartfiles -Wl,-Map=$(PROJNAME).map 57 | LFLAGS += -Wl,--gc-sections 58 | 59 | CCFLAGS += $(CFLAGS) 60 | CXXFLAGS += $(CFLAGS) 61 | ASMFLAGS += $(CFLAGS) 62 | 63 | COBJS = $(addprefix $(BUILD_DIR)/, $(CSRC:.c=.o)) 64 | ASMOBJS = $(addprefix $(BUILD_DIR)/, $(ASMSRC:.s=.o)) 65 | CXXOBJS = $(addprefix $(BUILD_DIR)/, $(CXXSRC:.cpp=.o)) 66 | 67 | OBJS = $(COBJS) $(ASMOBJS) $(CXXOBJS) 68 | 69 | # gcc optimization level 70 | OPTIMIZATION = -Os 71 | 72 | CC = arm-none-eabi-gcc 73 | CXX = arm-none-eabi-g++ 74 | AS = arm-none-eabi-gcc -x assembler-with-cpp 75 | LD = arm-none-eabi-g++ 76 | AR = arm-none-eabi-ar 77 | OC = arm-none-eabi-objcopy 78 | OD = arm-none-eabi-objdump 79 | NM = arm-none-eabi-nm 80 | SZ = arm-none-eabi-size 81 | 82 | MKDIR = mkdir -p 83 | 84 | COLOR = \033[1;31m 85 | COLOR_CLEAR = \033[0m 86 | PRINT = @printf "$(COLOR)%s$(COLOR_CLEAR)\n" 87 | 88 | ifeq ($(QUIET),1) 89 | Q = @ 90 | endif 91 | 92 | # 93 | # targets: 94 | ################################################################################ 95 | 96 | .PHONY: all 97 | all: $(PROJNAME).bin $(PROJNAME).lst $(PROJNAME).size.txt Makefile 98 | $(PRINT) "> done: $(PROJNAME)" 99 | @ $(SZ) $(PROJNAME).elf 100 | 101 | .PHONY: clean 102 | clean: 103 | $(Q)-rm -f $(OBJS) 104 | $(Q)-rm -f $(OBJS:.o=.lst) 105 | $(Q)-rm -f $(OBJS:.o=.d) 106 | $(Q)-rm -f $(PROJNAME).elf 107 | $(Q)-rm -f $(PROJNAME).bin 108 | $(Q)-rm -f $(PROJNAME).lst 109 | $(Q)-rm -f $(PROJNAME).map 110 | $(Q)-rm -f $(PROJNAME).size.txt 111 | 112 | .PHONY: rebuild 113 | rebuild: clean all 114 | 115 | # make targets for flashing with openocd 116 | .PHONY: flash 117 | flash: all 118 | openocd -f oocd.cfg -c "program $(PROJNAME).elf verify reset" -c "shutdown" 119 | 120 | .PHONY: dfu 121 | dfu: $(PROJNAME).bin 122 | # 0483:df11 is the USB Vendor ID:Product ID pair for an STM32 in DFU 123 | # bootloader mode. It can be found by running lsusb under Linux. 124 | dfu-util -a 0 -d 0483:df11 --dfuse-address 0x08000000 -D $(PROJNAME).bin 125 | 126 | .PHONY: reset 127 | reset: 128 | openocd -f oocd.cfg -c "init" -c "reset" -c "shutdown" 129 | 130 | # 131 | # file targets: 132 | ################################################################################ 133 | 134 | # binary 135 | $(PROJNAME).bin: $(PROJNAME).elf 136 | $(PRINT) "> copying" 137 | $(Q) $(OC) -Obinary -j .text -j .rodata -j .data $(PROJNAME).elf $(PROJNAME).bin 138 | 139 | # assembly listing 140 | $(PROJNAME).lst: $(PROJNAME).elf 141 | $(PRINT) "> generating assembly listing" 142 | $(Q) $(OD) -D -h $(PROJNAME).elf > $(PROJNAME).lst 143 | 144 | # linked elf-object 145 | $(PROJNAME).elf: $(OBJS) $(LDSCRIPT) 146 | $(PRINT) "> linking" 147 | $(Q) $(LD) -o $(PROJNAME).elf $(OBJS) $(LFLAGS) 148 | 149 | # object from c 150 | $(COBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.c Makefile 151 | $(Q) $(MKDIR) $(shell dirname ${@}) 152 | $(PRINT) "> compiling ("$<")" 153 | $(Q) $(CC) $(CCFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 154 | 155 | # object from asm 156 | $(ASMOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.s Makefile 157 | $(Q) $(MKDIR) $(shell dirname ${@}) 158 | $(PRINT) "> assembling ("$<")" 159 | $(Q) $(AS) $(ASMFLAGS) -c ${<} -o ${@} 160 | 161 | # object from c++ 162 | $(CXXOBJS): $(BUILD_DIR)/%.o : $(PROJ_ROOT)/%.cpp Makefile 163 | $(Q) $(MKDIR) $(shell dirname ${@}) 164 | $(PRINT) "> compiling ("$<")" 165 | $(Q) $(CXX) $(CXXFLAGS) -Wa,-ahlms=$(@:.o=.lst) -o ${@} -c ${<} 166 | 167 | # space usage 168 | $(PROJNAME).size.txt: $(PROJNAME).elf 169 | $(PRINT) "> calculating space usage" 170 | $(Q)$(SZ) $(PROJNAME).elf > $(PROJNAME).size.txt 171 | $(Q)$(NM) --size-sort --print-size -S $(PROJNAME).elf >> $(PROJNAME).size.txt 172 | 173 | # include the dependencies for all objects 174 | # (generated by the -MD compiler-flag) 175 | # -include $(OBJS:.o=.d) 176 | -------------------------------------------------------------------------------- /platform/uwb-beacon/include.jinja: -------------------------------------------------------------------------------- 1 | 2 | {% for dir in include_directories %} 3 | PACKAGER_INCDIR += {{ dir }} 4 | {% endfor %} 5 | 6 | {% for file in target['armv7-m'] + target.stm32f4 + source %} 7 | {% if file.endswith('.c') %}CSRC += {{ file }} 8 | {% elif file.endswith('.s') %}ASMSRC += {{ file }} 9 | {% elif file.endswith('.cpp') %}CXXSRC += {{ file }} 10 | {% endif %}{% endfor %} 11 | -------------------------------------------------------------------------------- /platform/uwb-beacon/oocd.cfg: -------------------------------------------------------------------------------- 1 | telnet_port 4444 2 | gdb_port 3333 3 | 4 | # interface configuration # 5 | source [find interface/stlink-v2.cfg] 6 | transport select hla_swd 7 | adapter_khz 200 8 | 9 | # target configuration # 10 | source [find target/stm32f4x.cfg] 11 | 12 | -------------------------------------------------------------------------------- /platform/uwb-beacon/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "platform.h" 12 | 13 | #define GPIOB_LED_ERROR GPIO0 14 | #define GPIOB_LED_DEBUG GPIO1 15 | /* LED_STATUS is different between the two board revisions so dont use it */ 16 | 17 | #define GPIOB_LED_ALL (GPIOB_LED_ERROR | GPIOB_LED_DEBUG) 18 | 19 | #define GPIOB_CAN1_RX GPIO8 20 | #define GPIOB_CAN1_TX GPIO9 21 | 22 | // page buffer used by config commands. 23 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 24 | 25 | void can_interface_init(void) 26 | { 27 | rcc_periph_clock_enable(RCC_CAN1); 28 | 29 | /* 30 | STM32F4 CAN1 on 42MHz configured APB1 peripheral clock 31 | 42MHz / 2 -> 21MHz 32 | 21MHz / (1tq + 12tq + 8tq) = 1MHz => 1Mbit 33 | */ 34 | can_init(CAN1, // Interface 35 | false, // Time triggered communication mode. 36 | true, // Automatic bus-off management. 37 | false, // Automatic wakeup mode. 38 | false, // No automatic retransmission. 39 | false, // Receive FIFO locked mode. 40 | true, // Transmit FIFO priority. 41 | CAN_BTR_SJW_4TQ, // Resynchronization time quanta jump width 42 | CAN_BTR_TS1_12TQ, // Time segment 1 time quanta width 43 | CAN_BTR_TS2_8TQ, // Time segment 2 time quanta width 44 | 2, // Prescaler 45 | false, // Loopback 46 | false); // Silent 47 | 48 | // filter to match any standard id 49 | // mask bits: 0 = Don't care, 1 = mute match corresponding id bit 50 | can_filter_id_mask_32bit_init( 51 | CAN1, 52 | 0, // filter nr 53 | 0, // id: only std id, no rtr 54 | 6 | (7 << 29), // mask: match only std id[10:8] = 0 (bootloader frames) 55 | 0, // assign to fifo0 56 | true // enable 57 | ); 58 | } 59 | 60 | void fault_handler(void) 61 | { 62 | gpio_clear(GPIOB, GPIOB_LED_ALL); 63 | gpio_set(GPIOB, GPIOB_LED_ERROR); 64 | // while(1); // debug 65 | reboot_system(BOOT_ARG_START_BOOTLOADER_NO_TIMEOUT); 66 | } 67 | 68 | static void delay(int n) 69 | { 70 | for (volatile int i = 0; i < n; i++) { 71 | asm volatile("nop"); 72 | } 73 | } 74 | 75 | static void reboot_blink(void) 76 | { 77 | /* arbitrary period obtained by trial and error. No units. */ 78 | const int period = 700 * 1000; 79 | for (int i = 0; i < 10; i++) { 80 | gpio_clear(GPIOB, GPIOB_LED_ALL); 81 | gpio_set(GPIOB, GPIOB_LED_DEBUG); 82 | delay(period); 83 | 84 | gpio_clear(GPIOB, GPIOB_LED_ALL); 85 | gpio_set(GPIOB, GPIOB_LED_ERROR); 86 | delay(period); 87 | } 88 | gpio_set(GPIOB, GPIOB_LED_ALL); 89 | } 90 | 91 | void platform_main(int arg) 92 | { 93 | rcc_clock_setup_hse_3v3(&hse_16mhz_3v3[CLOCK_3V3_168MHZ]); 94 | 95 | rcc_periph_clock_enable(RCC_GPIOB); 96 | 97 | // CAN pin 98 | gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIOB_CAN1_RX | GPIOB_CAN1_TX); 99 | gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, 100 | GPIOB_CAN1_RX | GPIOB_CAN1_TX); 101 | gpio_set_af(GPIOB, GPIO_AF9, GPIOB_CAN1_RX | GPIOB_CAN1_TX); 102 | 103 | // LED on 104 | gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIOB_LED_ALL); 105 | gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, GPIOB_LED_ALL); 106 | gpio_set(GPIOB, GPIOB_LED_ERROR); 107 | 108 | // Signal entering the bootloader 109 | reboot_blink(); 110 | 111 | // configure timeout of 10000 milliseconds 112 | timeout_timer_init(168000000, 10000); 113 | 114 | can_interface_init(); 115 | 116 | bootloader_main(arg); 117 | 118 | reboot_system(BOOT_ARG_START_BOOTLOADER); 119 | } 120 | -------------------------------------------------------------------------------- /platform/uwb-beacon/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #define PLATFORM_DEVICE_CLASS "uwb-beacon-rev-1" 12 | #define FLASH_PAGE_SIZE 0x4000 // 16K 13 | #define CONFIG_PAGE_SIZE FLASH_PAGE_SIZE 14 | 15 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 16 | 17 | // symbols defined in linkerscript 18 | extern int application_address, application_size, config_page1, config_page2; 19 | 20 | static inline void* memory_get_app_addr(void) 21 | { 22 | return (void*)&application_address; 23 | } 24 | 25 | static inline size_t memory_get_app_size(void) 26 | { 27 | return (size_t)&application_size; 28 | } 29 | 30 | static inline void* memory_get_config1_addr(void) 31 | { 32 | return (void*)&config_page1; 33 | } 34 | 35 | static inline void* memory_get_config2_addr(void) 36 | { 37 | return (void*)&config_page2; 38 | } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif /* PLATFORM_H */ 45 | -------------------------------------------------------------------------------- /tests/config_commands_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../flash_writer.h" 7 | #include "../command.h" 8 | #include "mocks/platform_mock.h" 9 | 10 | TEST_GROUP (ConfigCommandTestGroup) { 11 | cmp_mem_access_t write_cma; 12 | cmp_mem_access_t read_cma; 13 | cmp_ctx_t write_ctx; 14 | cmp_ctx_t read_ctx; 15 | char data[1024]; 16 | char out_data[1024]; 17 | bootloader_config_t config; 18 | 19 | void setup() 20 | { 21 | cmp_mem_access_init(&write_ctx, &write_cma, data, sizeof data); 22 | cmp_mem_access_init(&read_ctx, &read_cma, out_data, sizeof out_data); 23 | memset(data, 0, sizeof data); 24 | memset(out_data, 0, sizeof out_data); 25 | memset(&config, 0, sizeof config); 26 | } 27 | }; 28 | 29 | TEST(ConfigCommandTestGroup, CanChangeNodeID) 30 | { 31 | cmp_write_map(&write_ctx, 1); 32 | 33 | cmp_write_str(&write_ctx, "ID", 2); 34 | cmp_write_u8(&write_ctx, 42); 35 | 36 | cmp_mem_access_set_pos(&write_cma, 0); 37 | 38 | command_config_update(1, &write_ctx, &read_ctx, &config); 39 | 40 | CHECK_EQUAL(42, config.ID); 41 | 42 | // check return value 43 | bool ret = false; 44 | cmp_mem_access_set_pos(&read_cma, 0); 45 | cmp_read_bool(&read_ctx, &ret); 46 | CHECK_TRUE(ret); 47 | } 48 | 49 | TEST(ConfigCommandTestGroup, CanReadConfig) 50 | { 51 | bootloader_config_t read_config; 52 | memset(&read_config, 0, sizeof(read_config)); 53 | 54 | config.ID = 42; 55 | command_config_read(0, NULL, &write_ctx, &config); 56 | 57 | cmp_mem_access_set_pos(&write_cma, 0); 58 | 59 | config_update_from_serialized(&read_config, &write_ctx); 60 | 61 | // Just check that the first field matches. 62 | // The rest is tested in ConfigSerializationTest 63 | CHECK_EQUAL(config.ID, read_config.ID); 64 | } 65 | -------------------------------------------------------------------------------- /tests/config_tests.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "CppUTestExt/MockSupport.h" 3 | #include 4 | #include "../config.h" 5 | 6 | TEST_GROUP (ConfigTest) { 7 | char config_buffer[1024]; 8 | bootloader_config_t config, result; 9 | 10 | void setup(void) 11 | { 12 | memset(config_buffer, 0, sizeof(config_buffer)); 13 | memset(&config, 0, sizeof(bootloader_config_t)); 14 | memset(&result, 0, sizeof(bootloader_config_t)); 15 | } 16 | 17 | void config_read_and_write() 18 | { 19 | config_write(config_buffer, &config, sizeof config_buffer); 20 | result = config_read(config_buffer, sizeof config_buffer); 21 | } 22 | }; 23 | 24 | TEST(ConfigTest, WritesValidConfigCRC) 25 | { 26 | config.ID = 0x42; 27 | 28 | config_write(config_buffer, &config, sizeof config_buffer); 29 | 30 | CHECK_TRUE(config_is_valid(config_buffer, sizeof(config_buffer))); 31 | } 32 | 33 | TEST(ConfigTest, DetectsCorruptedConfig) 34 | { 35 | config.ID = 0x42; 36 | 37 | config_write(config_buffer, &config, sizeof config_buffer); 38 | 39 | config_buffer[42] ^= 0x2A; 40 | 41 | CHECK_FALSE(config_is_valid(config_buffer, sizeof(config_buffer))); 42 | } 43 | 44 | TEST(ConfigTest, CanSerializeNodeID) 45 | { 46 | config.ID = 0x12; 47 | 48 | config_read_and_write(); 49 | 50 | CHECK_EQUAL(config.ID, result.ID); 51 | } 52 | 53 | TEST(ConfigTest, CanSerializeNodeName) 54 | { 55 | strncpy(config.board_name, "test.dummy", 64); 56 | 57 | config_read_and_write(); 58 | 59 | STRCMP_EQUAL(config.board_name, result.board_name); 60 | } 61 | 62 | TEST(ConfigTest, CanSerializeNodeDeviceClass) 63 | { 64 | strncpy(config.device_class, "CVRA.dummy.v1", 64); 65 | 66 | config_read_and_write(); 67 | 68 | STRCMP_EQUAL(config.device_class, result.device_class); 69 | } 70 | 71 | TEST(ConfigTest, CanSerializeApplicationCRC) 72 | { 73 | config.application_crc = 0xdeadbeef; 74 | 75 | config_read_and_write(); 76 | 77 | CHECK_EQUAL(0xdeadbeef, result.application_crc); 78 | } 79 | 80 | TEST(ConfigTest, CanSerializeApplicationSize) 81 | { 82 | config.application_size = 42; 83 | 84 | config_read_and_write(); 85 | 86 | CHECK_EQUAL(42, result.application_size); 87 | } 88 | 89 | TEST(ConfigTest, CanSerializeUpdateCount) 90 | { 91 | config.update_count = 23; 92 | 93 | config_read_and_write(); 94 | 95 | CHECK_EQUAL(23, result.update_count); 96 | } 97 | -------------------------------------------------------------------------------- /tests/mocks/boot_arg.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "CppUTestExt/MockSupport.h" 3 | 4 | extern "C" { 5 | #include "../../boot_arg.h" 6 | } 7 | 8 | void reboot_system(uint8_t arg) 9 | { 10 | mock().actualCall("reboot").withIntParameter("arg", arg); 11 | } 12 | -------------------------------------------------------------------------------- /tests/mocks/can_interface_mock.cpp: -------------------------------------------------------------------------------- 1 | #include "../../can_interface.h" 2 | #include "CppUTest/TestHarness.h" 3 | #include "CppUTestExt/MockSupport.h" 4 | 5 | #include 6 | 7 | static uint32_t expected_id; 8 | static uint8_t expected_len; 9 | uint8_t expected_msg[8]; 10 | 11 | bool can_interface_read_message(uint32_t* id, uint8_t* message, uint8_t* length, uint32_t retries) 12 | { 13 | mock("can").actualCall("read").withOutputParameter("id", id).withOutputParameter("message", message).withOutputParameter("length", length); 14 | return true; 15 | } 16 | 17 | bool can_interface_send_message(uint32_t id, uint8_t* message, uint8_t length, uint32_t retries) 18 | { 19 | mock("can").actualCall("send").withParameter("id", id).withParameter("message", message).withParameter("length", length); 20 | return true; 21 | } 22 | 23 | void can_mock_message(uint32_t message_id, uint8_t* msg, uint8_t message_len) 24 | { 25 | expected_id = message_id; 26 | expected_len = message_len; 27 | memcpy(expected_msg, msg, message_len); 28 | 29 | mock("can").expectOneCall("read").withOutputParameterReturning("id", &expected_id, sizeof(uint32_t)).withOutputParameterReturning("message", expected_msg, message_len).withOutputParameterReturning("length", &expected_len, sizeof(uint8_t)); 30 | } 31 | 32 | TEST_GROUP (CanInterfaceMockTestGroup) { 33 | uint32_t id; 34 | uint8_t msg[8]; 35 | 36 | void teardown() 37 | { 38 | mock().checkExpectations(); 39 | mock().clear(); 40 | } 41 | }; 42 | 43 | TEST(CanInterfaceMockTestGroup, CanGetNodeID) 44 | { 45 | uint8_t len; 46 | 47 | can_mock_message(42, msg, 0); 48 | 49 | can_interface_read_message(&id, msg, &len, 1); 50 | 51 | CHECK_EQUAL(42, id); 52 | } 53 | 54 | TEST(CanInterfaceMockTestGroup, CanGetMessageLength) 55 | { 56 | uint8_t len; 57 | 58 | can_mock_message(42, (uint8_t*)"hello", 5); 59 | 60 | can_interface_read_message(&id, msg, &len, 1); 61 | CHECK_EQUAL(5, len); 62 | } 63 | 64 | TEST(CanInterfaceMockTestGroup, CanGetMessage) 65 | { 66 | uint8_t len; 67 | 68 | can_mock_message(42, (uint8_t*)"hello", 5); 69 | 70 | can_interface_read_message(&id, msg, &len, 1); 71 | STRCMP_EQUAL("hello", (char*)msg); 72 | } 73 | 74 | TEST(CanInterfaceMockTestGroup, CanOutputMessage) 75 | { 76 | uint8_t msg[] = {1, 2, 3}; 77 | mock("can").expectOneCall("send").withIntParameter("length", 3).withParameter("message", msg).withIntParameter("id", 0x42); 78 | can_interface_send_message(0x42, msg, 3, 1); 79 | } 80 | -------------------------------------------------------------------------------- /tests/mocks/can_interface_mock.h: -------------------------------------------------------------------------------- 1 | #ifndef CAN_INTERFACE_MOCK_H 2 | #define CAN_INTERFACE_MOCK_H 3 | 4 | #include 5 | #include 6 | 7 | void can_mock_message(uint32_t message_id, uint8_t* msg, uint8_t message_len); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /tests/mocks/flash_writer_mock.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../flash_writer.h" 3 | #include "CppUTest/TestHarness.h" 4 | #include "CppUTestExt/MockSupport.h" 5 | 6 | // This symbol is supposed to be provided by the linker 7 | extern "C" { 8 | int app_start; 9 | } 10 | 11 | void flash_init(void) 12 | { 13 | } 14 | 15 | void flash_writer_unlock(void) 16 | { 17 | mock("flash").actualCall("unlock"); 18 | } 19 | 20 | void flash_writer_lock(void) 21 | { 22 | mock("flash").actualCall("lock"); 23 | } 24 | 25 | void flash_writer_page_erase(void* adress) 26 | { 27 | mock("flash").actualCall("page_erase").withPointerParameter("adress", adress); 28 | } 29 | 30 | void flash_writer_page_write(void* page, void* data, size_t len) 31 | { 32 | mock("flash").actualCall("page_write").withPointerParameter("page_adress", page).withIntParameter("size", len); 33 | 34 | memcpy(page, data, len); 35 | } 36 | 37 | /* This TEST_GROUP contains the tests to check that the mock flash functions 38 | * are working properly. */ 39 | TEST_GROUP (FlashWriterMockTestGroup) { 40 | void teardown() 41 | { 42 | mock().checkExpectations(); 43 | mock().clear(); 44 | } 45 | }; 46 | 47 | TEST(FlashWriterMockTestGroup, CanUnlock) 48 | { 49 | mock("flash").expectOneCall("unlock"); 50 | flash_writer_unlock(); 51 | } 52 | 53 | TEST(FlashWriterMockTestGroup, CanLock) 54 | { 55 | mock("flash").expectOneCall("lock"); 56 | flash_writer_lock(); 57 | } 58 | 59 | TEST(FlashWriterMockTestGroup, CanErasePage) 60 | { 61 | mock("flash").expectOneCall("page_erase").withPointerParameter("adress", (void*)0xdeadbeef); 62 | 63 | flash_writer_page_erase((void*)0xdeadbeef); 64 | } 65 | 66 | TEST(FlashWriterMockTestGroup, CanWritePage) 67 | { 68 | char buf[30]; 69 | char data[] = "Hello"; 70 | 71 | mock("flash").expectOneCall("page_write").withPointerParameter("page_adress", buf).withIntParameter("size", strlen(data) + 1); 72 | 73 | flash_writer_page_write(buf, data, strlen(data) + 1); 74 | 75 | STRCMP_EQUAL(data, buf); 76 | } 77 | -------------------------------------------------------------------------------- /tests/mocks/platform_mock.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint8_t memory_mock_config1[CONFIG_PAGE_SIZE]; 4 | uint8_t memory_mock_config2[CONFIG_PAGE_SIZE]; 5 | uint8_t memory_mock_app[42]; 6 | 7 | uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 8 | 9 | void* memory_get_app_addr(void) 10 | { 11 | return &memory_mock_app[0]; 12 | } 13 | 14 | void* memory_get_config1_addr(void) 15 | { 16 | return &memory_mock_config1[0]; 17 | } 18 | 19 | void* memory_get_config2_addr(void) 20 | { 21 | return &memory_mock_config2[0]; 22 | } 23 | 24 | size_t memory_get_app_size(void) 25 | { 26 | return sizeof(memory_mock_app); 27 | } 28 | -------------------------------------------------------------------------------- /tests/mocks/platform_mock.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_MOCK_H 2 | #define PLATFORM_MOCK_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | extern uint8_t memory_mock_config1[CONFIG_PAGE_SIZE]; 11 | extern uint8_t memory_mock_config2[CONFIG_PAGE_SIZE]; 12 | extern uint8_t memory_mock_app[42]; 13 | 14 | extern uint8_t config_page_buffer[CONFIG_PAGE_SIZE]; 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif /* PLATFORM_MOCK_H */ 21 | -------------------------------------------------------------------------------- /tests/mocks/timeout_mock.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | bool timeout_reached(void) 4 | { 5 | return false; 6 | } 7 | -------------------------------------------------------------------------------- /timeout.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMEOUT_H 2 | #define TIMEOUT_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | bool timeout_reached(void); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #endif /* TIMEOUT_H */ --------------------------------------------------------------------------------