├── .github ├── FUNDING.yml └── workflows │ └── platformio-ci.yml ├── .gitignore ├── .vscode └── extensions.json ├── LICENSE.md ├── README.md ├── doc ├── bits-of-byte-3.png ├── can-msg-bytes.png ├── flash-sequence.svg ├── flash-squence.plantuml ├── mcp-can-boot-256.png └── mcp-can-boot-512.png ├── platformio.ini └── src ├── bootloader.cpp ├── bootloader.h ├── can.h ├── config.h ├── controllers.h ├── mcp2515.cpp └── mcp2515.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [crycode-de] 2 | custom: ['https://www.paypal.me/petercrycode'] 3 | -------------------------------------------------------------------------------- /.github/workflows/platformio-ci.yml: -------------------------------------------------------------------------------- 1 | name: PlatformIO CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | pio-env: [ATmega32,ATmega32U4,ATmega328P,ATmega328PB,ATmega64,ATmega644P,ATmega128,ATmega1284P,ATmega2560] 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Cache pip 18 | uses: actions/cache@v4 19 | with: 20 | path: ~/.cache/pip 21 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 22 | restore-keys: | 23 | ${{ runner.os }}-pip- 24 | 25 | - name: Cache PlatformIO 26 | uses: actions/cache@v4 27 | with: 28 | path: ~/.platformio 29 | key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} 30 | 31 | - name: Set up Python 32 | uses: actions/setup-python@v5 33 | with: 34 | python-version: '3.10' 35 | 36 | - name: Install PlatformIO 37 | run: | 38 | python -m pip install --upgrade pip 39 | pip install --upgrade platformio 40 | 41 | - name: Run PlatformIO for ${{ matrix.pio-env }} 42 | run: pio run -e ${{ matrix.pio-env }} 43 | 44 | - name: Archive build artifacts 45 | uses: actions/upload-artifact@v4 46 | with: 47 | name: ${{ matrix.pio-env }} 48 | path: | 49 | .pio/build/*/firmware.hex 50 | .pio/build/*/firmware.map 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .pioenvs 3 | .piolibdeps 4 | .clang_complete 5 | .gcc-flags.json 6 | .travis.yml 7 | .vscode/c_cpp_properties.json 8 | .vscode/launch.json 9 | include 10 | lib 11 | test 12 | build.log 13 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## Creative Commons 2 | 3 | # Attribution-NonCommercial-ShareAlike 4.0 International 4 | 5 | Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. 6 | 7 | ### Using Creative Commons Public Licenses 8 | 9 | Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. 10 | 11 | * __Considerations for licensors:__ Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors). 12 | 13 | * __Considerations for the public:__ By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees). 14 | 15 | ## Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License 16 | 17 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. 18 | 19 | ### Section 1 – Definitions. 20 | 21 | a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. 22 | 23 | b. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. 24 | 25 | c. __BY-NC-SA Compatible License__ means a license listed at [creativecommons.org/compatiblelicenses](http://creativecommons.org/compatiblelicenses), approved by Creative Commons as essentially the equivalent of this Public License. 26 | 27 | d. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. 28 | 29 | e. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. 30 | 31 | f. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. 32 | 33 | g. __License Elements__ means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike. 34 | 35 | h. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License. 36 | 37 | i. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. 38 | 39 | j. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License. 40 | 41 | k. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. 42 | 43 | l. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. 44 | 45 | m. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. 46 | 47 | n. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. 48 | 49 | ### Section 2 – Scope. 50 | 51 | a. ___License grant.___ 52 | 53 | 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: 54 | 55 | A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and 56 | 57 | B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. 58 | 59 | 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 60 | 61 | 3. __Term.__ The term of this Public License is specified in Section 6(a). 62 | 63 | 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 64 | 65 | 5. __Downstream recipients.__ 66 | 67 | A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. 68 | 69 | B. __Additional offer from the Licensor – Adapted Material.__ Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply. 70 | 71 | C. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 72 | 73 | 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). 74 | 75 | b. ___Other rights.___ 76 | 77 | 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 78 | 79 | 2. Patent and trademark rights are not licensed under this Public License. 80 | 81 | 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. 82 | 83 | ### Section 3 – License Conditions. 84 | 85 | Your exercise of the Licensed Rights is expressly made subject to the following conditions. 86 | 87 | a. ___Attribution.___ 88 | 89 | 1. If You Share the Licensed Material (including in modified form), You must: 90 | 91 | A. retain the following if it is supplied by the Licensor with the Licensed Material: 92 | 93 | i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); 94 | 95 | ii. a copyright notice; 96 | 97 | iii. a notice that refers to this Public License; 98 | 99 | iv. a notice that refers to the disclaimer of warranties; 100 | 101 | v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; 102 | 103 | B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and 104 | 105 | C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 106 | 107 | 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 108 | 109 | 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 110 | 111 | b. ___ShareAlike.___ 112 | 113 | In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 114 | 115 | 1. The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License. 116 | 117 | 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 118 | 119 | 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. 120 | 121 | ### Section 4 – Sui Generis Database Rights. 122 | 123 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: 124 | 125 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; 126 | 127 | b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and 128 | 129 | c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. 130 | 131 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. 132 | 133 | ### Section 5 – Disclaimer of Warranties and Limitation of Liability. 134 | 135 | a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__ 136 | 137 | b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__ 138 | 139 | c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. 140 | 141 | ### Section 6 – Term and Termination. 142 | 143 | a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. 144 | 145 | b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 146 | 147 | 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 148 | 149 | 2. upon express reinstatement by the Licensor. 150 | 151 | For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. 152 | 153 | c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. 154 | 155 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 156 | 157 | ### Section 7 – Other Terms and Conditions. 158 | 159 | a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. 160 | 161 | b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. 162 | 163 | ### Section 8 – Interpretation. 164 | 165 | a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. 166 | 167 | b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. 168 | 169 | c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. 170 | 171 | d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. 172 | 173 | > Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. 174 | > 175 | > Creative Commons may be contacted at creativecommons.org 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCP-CAN-Boot 2 | 3 | ![MCP-CAN-Boot logo](./doc/mcp-can-boot-256.png) 4 | 5 | CAN bus bootloader for **AVR microcontrollers** attached to an **MCP2515** CAN controller. 6 | 7 | **Tests:** ![Test and Release](https://github.com/crycode-de/mcp-can-boot/workflows/PlatformIO%20CI/badge.svg) 8 | 9 | ## Supported features 10 | 11 | * Flash the main application into a MCU (microcontroller unit) 12 | * Verify after flashing 13 | * Read the whole flash (excluding the bootloader area) 14 | * Erase the whole flash (excluding the bootloader area) 15 | * Unique 16bit IDs to identify the MCU to flash 16 | * Correctly handled disabling of the watchdog at startup to prevent bootloader loops when using the watchdog in the main application 17 | * Use Extended Frame Format (EFF, default) or Standard Frame Format (SFF) CAN-IDs 18 | * Very low impact on active CAN systems which enables to flash MCUs in active networks 19 | * Optional automatic detection of the used bitrate on the CAN bus 20 | 21 | ## Used frameworks and libraries 22 | 23 | *MCP-CAN-Boot* is made as a [PlatformIO](https://platformio.org/) project. 24 | It uses parts of the [Arduino](https://www.arduino.cc/) framework. 25 | 26 | For controlling the MCP2515 a modified version of the [Arduino MCP2515 CAN interface library](https://github.com/autowp/arduino-mcp2515) is used. 27 | 28 | ## Currently supported AVR controllers 29 | 30 | * [ATmega32](http://ww1.microchip.com/downloads/en/devicedoc/doc2503.pdf) 31 | * [ATmega328P](http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf) 32 | * [ATmega328PB](https://ww1.microchip.com/downloads/en/DeviceDoc/40001906A.pdf) 33 | * [ATmega32U4](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Datasheet.pdf) 34 | * [ATmega64](http://ww1.microchip.com/downloads/en/DeviceDoc/atmel-2490-8-bit-avr-microcontroller-atmega64-l_datasheet.pdf) 35 | * [ATmega644P](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42744-ATmega644P_Datasheet.pdf) 36 | * [ATmega128](http://ww1.microchip.com/downloads/en/DeviceDoc/doc2467.pdf) 37 | * [ATmega1284P](https://ww1.microchip.com/downloads/en/DeviceDoc/doc8059.pdf) 38 | * [ATmega2560](https://ww1.microchip.com/downloads/en/devicedoc/atmel-2549-8-bit-avr-microcontroller-atmega640-1280-1281-2560-2561_datasheet.pdf) 39 | 40 | ## Bootloader size 41 | 42 | This bootloader will fit into a 2048 words (4096 bytes) bootloader section. 43 | 44 | The fuse bits of the MCU have to be set correctly to a boot flash section size of 2048 words and the boot reset vector must be enabled (BOOTRST=0). 45 | 46 | ## Flashing the bootloader 47 | 48 | To flash the _MCP-CAN-Boot_ bootloader you need to use an ISP programmer. 49 | 50 | To upload the bootloader run `pio run --target upload --environment ATmega1284P` for example. 51 | 52 | Make sure to set the fuse bits correctly for a 2048 words bootloader and boot resest vector enabled. Otherwise the bootloader will not work. 53 | 54 | ### Set the fuse bits 55 | 56 | Examples for each supported controller are in `platformio.ini` (commented out by default). 57 | 58 | To set the fuse bits, enable the lines starting with `board_fuses.*` for your controller. _Always check/edit the fuses values to fit your needs!_ 59 | If you set the correct values, you may run `pio run --target fuses --environment ATmega1284P` to program the fuses on your controller. 60 | 61 | ## CAN bus communication 62 | 63 | The whole communication via the CAN bus uses only two CAN-IDs. 64 | 65 | *Defaults:* 66 | 67 | * `0x1FFFFF01` for messages from MCU to remote 68 | * `0x1FFFFF02` for messages from remote to MCU 69 | 70 | Using this two IDs nearly at the end of CAN-ID range with the lowest priority there will be almost none interference flashing an MCU in a active CAN system. 71 | 72 | Each CAN message consists of eight data bytes. 73 | The first four bytes are used for MCU identification, commands, data lengths and data identification. The other four bytes contain the data to read or write. 74 | 75 | ## Flash-App 76 | 77 | The official remote application for flashing the MCU using the CAN bus is written in [Node.js](https://nodejs.org/) and located in the [mcp-can-boot-flash-app repository](https://github.com/crycode-de/mcp-can-boot-flash-app). 78 | 79 | The flash-app is also available on *npm* as `mcp-can-boot-flash-app`. 80 | 81 | No need to install: Just run the flash-app using `npx` (this will take a moment): 82 | 83 | ``` 84 | npx mcp-can-boot-flash-app [...] 85 | ``` 86 | 87 | Or install it globally and run it if you need it more often: 88 | 89 | ``` 90 | npm install -g mcp-can-boot-flash-app 91 | mcp-can-boot-flash-app [...] 92 | ``` 93 | 94 | ### Flash-App parameters 95 | 96 | ``` 97 | --file, -f Hex file to flash [string] [required] 98 | --iface, -i CAN interface to use [string] [default: "can0"] 99 | --partno, -p Specific AVR device like in avrdude [string] [required] 100 | --mcuid, -m ID of the MCU bootloader [string] [required] 101 | -e Erase whole flash before flashing new data [boolean] 102 | -V Do not verify [boolean] 103 | -r Read flash and save to given file (no flashing!), optional 104 | with maximum address to read until [string] 105 | -F Force flashing, even if the bootloader version missmatched 106 | [boolean] 107 | --reset, -R CAN message to send on startup to reset the MCU 108 | (#{hex_data}) [string] 109 | --can-id-mcu CAN-ID for messages from MCU to remote 110 | [string] [default: 0x1FFFFF01] 111 | --can-id-remote CAN-ID for messages from remote to MCU 112 | [string] [default: 0x1FFFFF02] 113 | --sff Use Standad Frame Format (SFF) instead of the default 114 | Extended Frame Format (EFF) for the CAN-IDs [boolean] 115 | --ping Send a ping in the given interval (ms) to keep the bus active 116 | (should be used if the bootloader uses bitrate detection) 117 | [number] 118 | --help, -h Show help [boolean] 119 | ``` 120 | 121 | Example: 122 | 123 | ``` 124 | npx mcp-can-boot-flash-app -f firmware.hex -p m1284p -m 0x0042 125 | ``` 126 | 127 | ### Inofficial (thirdparty) flash applications 128 | 129 | * [AVR CAN flasher](https://github.com/Nerdiyde/AVR_CAN_flasher) - ESP32 port of the Flash-App 130 | 131 | ## How to reset the MCU by your main application 132 | 133 | You can use the watchdog to reset the MCU by your main application. 134 | 135 | To do so, you have to disable all interrupts, setup the watchdog for a very short time and enter an infinite loop like this: 136 | ```c 137 | #include // include watchdog functions 138 | 139 | cli(); // disable interrupts 140 | wdt_enable(WDTO_15MS); // watchdog timeout 15ms 141 | while(1); // wait for watchdog to reset mcu 142 | ``` 143 | 144 | This will reset your mcu and enter the bootloader. 145 | 146 | In the flash-app you may use the -R or --reset argument to send a CAN message which triggers the code above to do reset. 147 | 148 | ## How to read the reset cause (MCUSR/MCUCSR) by your main application 149 | 150 | _MCP-CAN-Boot_ clears the MCUSR/MCUCSR register on startup. This is needed for propper watchdog handling. 151 | The original MCUSR/MCUCSR value is stored in the R2 register by the bootloader which allows you to get it in your main application. 152 | 153 | To get the original value of the MCUSR/MCUCSR register by you main application, you have to read from the R2 register. 154 | 155 | ### Read MCUSR/MCUCSR from R2 into a global variable 156 | 157 | To add a globale variable `mcusr` containing the original value of the MCUSR/MCUCSR register just add the following 158 | code on top of your main application: 159 | 160 | ```cpp 161 | uint8_t mcusr __attribute__ ((section (".noinit"))); 162 | void getMCUSR(void) __attribute__((naked)) __attribute__((section(".init0"))); 163 | void getMCUSR(void) { 164 | __asm__ __volatile__ ( "mov %0, r2 \n" : "=r" (mcusr) : ); 165 | } 166 | ``` 167 | 168 | After this, the **global** variable `mcusr` is available and contains the value of the original MCUSR/MCUCSR register. 169 | You don't have to call `getMCUSR` manually! This done automatically by the `init0` section. 170 | 171 | ### Read MCUSR/MCUCSR from R2 into a local variable 172 | 173 | To read the value into a local variable (e.g. inside your `main` function) just add the following code in your function: 174 | 175 | ```cpp 176 | uint8_t mcusr; 177 | __asm__ __volatile__ ( "mov %0, r2 \n" : "=r" (mcusr) : ); 178 | ``` 179 | 180 | After this, the **local** variable `mcusr` is available and contains the value of the original MCUSR/MCUCSR register. 181 | 182 | ## Detailed description of the CAN messages 183 | 184 | Each CAN message has a fixed length of 8 byte. Unneeded bytes will be set to `0x00` and simply ignored. 185 | 186 | ![Bytes of a CAN message](./doc/can-msg-bytes.png) 187 | 188 | ### Control bytes 189 | 190 | The lower four bytes (0 to 3) are used as control bytes. 191 | 192 | #### Control byte 0 and 1 193 | 194 | These two bytes always contain the ID of the MCU (`MCU_ID` in `config.h`) to make sure the correct MCU will be flashed. 195 | The ID is written in big-endian format, so byte 0 is the MSB and byte 1 the LSB. 196 | 197 | #### Control byte 2 198 | 199 | The command which is used to identify the type of message. 200 | The commands are described below. 201 | 202 | #### Control byte 3 203 | 204 | Used to store the amount of the data bytes of the message and a part of the current flash address for verification. 205 | Therefor the three bits 5 to 7 of this byte represent the data length (zero to four bytes) and the bits 0 to 4 are the lower five bits of the current flash address. 206 | This byte will only be used in some commands and otherwise it will be set to `0x00`. 207 | 208 | ![Bits of byte 3](./doc/bits-of-byte-3.png) 209 | 210 | ### Data bytes 211 | 212 | The four data bytes 4 to 7 are set depending on the command. 213 | If they contain flash data the byte 3 of the CAN message will be set accordingly. 214 | 215 | ### Commands 216 | 217 | | Command | Binary | Direction | 218 | |--------------------------|--------------|---------------------------------| 219 | | Bootloader start | `0b00000010` | MCU to Remote | 220 | | Flash init | `0b00000110` | Remote to MCU | 221 | | Flash ready | `0b00000100` | MCU to Remote | 222 | | Flash set address | `0b00001010` | Remote to MCU | 223 | | Flash address error | `0b00001011` | MCU to Remote | 224 | | Flash data | `0b00001000` | Remote to MCU | 225 | | Flash data error | `0b00001101` | MCU to Remote | 226 | | Flash done | `0b00010000` | Remote to MCU | 227 | | Flash done verify | `0b01010000` | Remote to MCU and MCU to Remote | 228 | | Flash erase | `0b00100000` | Remote to MCU | 229 | | Flash read | `0b01000000` | Remote to MCU | 230 | | Flash read data | `0b01001000` | MCU to Remote | 231 | | Flash read address error | `0b01001011` | MCU to Remote | 232 | | Start app | `0b10000000` | Remote to MCU and MCU to Remote | 233 | | Ping | `0b00000000` | Remote to MCU | 234 | 235 | *Hint:* All flash addresses will always be the uint32_t byte address with the bytes ordered in big-endian format. 236 | 237 | #### Bootloader start 238 | 239 | The *bootloader start* command will be send by the MCU when the bootloader is started up to notify a possibly waiting flash application. 240 | 241 | The data bytes 4, 5 and 6 are set to the device signature 0, 1 and 2 of the AVR. This enables the remote flash application to identify the type of the MCU. 242 | 243 | Data byte 7 is set to the command set version of the bootloader to make sure bootloader and flash application are speaking the same language. 244 | 245 | After this message is send by the MCU the bootloader waits a limited amount of time (default 250ms, configurable via `TIMEOUT` in `config.h`) for the *flash init* command. 246 | It no *flash init* is received the bootloader will start main application. 247 | 248 | #### Flash init 249 | 250 | *Flash init* have to be send by the flash application to the MCU after *bootloader start* and before the bootloader starts the main application. 251 | This will enter the flashing mode of the bootloader and is the one and only command which must be timed correctly. 252 | 253 | The data bytes 4, 5 and 6 must be set to the correct device signature like in *bootloader start*. 254 | 255 | After the bootloader entered the flash mode the next flash address will be set to `0x0000` and a *flash ready* command will be send. 256 | 257 | #### Flash ready 258 | 259 | The *flash ready* command is send by the MCU when ever it is ready to receive the next flash data. 260 | 261 | The four data bytes will be set to the flash address where the next received flash data will be stored. 262 | If this is not the address where the data should be stored, the flash application may send a *flash set address* command. 263 | 264 | #### Flash set address 265 | 266 | *Flash set address* may be used by the flash application to tell the bootloader where the next flash data should be stored. 267 | 268 | If the address is set the MUC responds with a *flash ready* command. 269 | If the requested address is out range of the flash area the MCU responds with a *flash address error* command. 270 | 271 | #### Flash address error 272 | 273 | The bootloader responds with *flash address error* if some command from the flash application requested a flash address which is out of range of the flash area of the MCU. 274 | 275 | The triggering command will be discarded by the MCU. 276 | 277 | The four data bytes will be set to the flash end address which is maximum supported flash address by this MCU (without the bootloader section). 278 | 279 | It's up to the flash application to handle this error and let the bootloader know what to do next. 280 | 281 | #### Flash data 282 | 283 | *Flash data* is send from the flash application to the bootloader to transmit the content to flash. 284 | 285 | It can contain one to four data bytes. 286 | The number of data bytes must be set in bit 5 to 7 of the byte 3 (length and address part) of the CAN message. 287 | 288 | To verify the correct flash address between flash application and bootloader the bits 0 to 4 of the byte 3 must be set to the LSB five bits of the flash address where the data belongs to. 289 | 290 | If the data is accepted by the bootloader the flash address will be increased by the received data length and a *flash ready* command will be send. The flash application may directly send the next *flash data* after receiving the *flash ready* command. 291 | 292 | If the address part from byte 3 mismatches the expected flash address by the bootloader a *flash data error* command will be send with the four data bytes set to the expected flash address. 293 | 294 | The bootloader will respond with a *flash address error* if the data length will exceed the flash end address. 295 | 296 | #### Flash data error 297 | 298 | If the address part in byte 3 of a *flash data* command mismatches the expected flash address by the bootloader it will respond with a *flash data error*. The four data bytes will be set to the expected flash address. 299 | 300 | It's up to the flash application to handle this error and let the bootloader know what to do next. 301 | 302 | #### Flash done 303 | 304 | A *flash done* can be send by the flash application if all flash data is transmitted. 305 | The bootloader will write the remaining internal buffer to the flash and then start the main application. 306 | 307 | #### Flash done verify 308 | 309 | A *flash done verify* has to be send by the flash application if all flash data is transmit and the flash application wants to verify (read) the flash. 310 | The bootloader will write the remaining internal buffer to the flash, send a *flash done verify* back to the flash application and wait for *flash read* commands. 311 | 312 | #### Flash read 313 | 314 | Using the *flash read* command the flash application may read the current flash data from the MCU. 315 | 316 | In each *flash read* command the four data bytes must be set to the flash address to read from. 317 | If the flash address is behind the flash end address (without bootloader area) a *flash read address error* will be send back to flash application. 318 | 319 | There will always be four bytes read from the flash. In case of this exceeds the flash end address, the remaining bytes will be set to `0x00`. 320 | 321 | The bootloader will respond with a *flash read data* message. 322 | 323 | #### Flash read data 324 | 325 | The bootloader sends a *flash read data* command in response to a *flash read*. 326 | 327 | The data bytes will be set to flash data read from the MCU flash at the requested address. 328 | 329 | In byte 3 of the CAN message there will be set the length of the read flash data (bit 5 to 7) and the LSB address part of the read flash address (bit 0 to 5). 330 | 331 | Directly after receiving the *flash read data* the flash application may send the next *flash read* commend. 332 | 333 | If all needed flash data is read by the flash application it have to send a *start app* command to let the bootloader exit the flashing mode and start the main application. 334 | 335 | Additionally the flash application may continue to flash data using *flash set address* and *flash data* commands. 336 | 337 | #### Flash read address error 338 | 339 | A *flash address read error* will be send by the bootloader if a *flash read* tried to read data behind the flash end address (without bootloader section). 340 | 341 | It's up to the flash application to handle this error and let the bootloader know what to do next. 342 | 343 | #### Flash erase 344 | 345 | The *flash erase* command my be send by the flash application to let the bootloader erase the whole flash (without the bootloader section). 346 | 347 | When the erase is done the bootloader will set the next flash address to `0x0000` and respond with a *flash ready* command. 348 | 349 | The flash application may use *flash erase* before the first *flash data* command to ensure a clean MCU flash before flashing a new application. 350 | 351 | #### Start app 352 | 353 | The *start app* command will be send by the bootloader to the flash application if the bootloader is starting the main application from flashing mode. 354 | 355 | If the bootloader is starting the main application without entering flashing mode (normal MCU startup) no *start app* will be send. 356 | 357 | Additionally a *start app* command may be send at any time by the flash application to the bootloader while the bootloader is in flashing mode. This will stop the flashing process without writing anything else to the flash and start the main application. 358 | 359 | ### Communication example 360 | 361 | ![Communication example](./doc/flash-sequence.svg) 362 | 363 | ## Changelog 364 | 365 | ## 1.4.1 (2024-08-12) 366 | 367 | * Added support for _ATmega328PB_ 368 | 369 | ## 1.4.0 (2023-06-12) 370 | 371 | * Added support for _ATmega32U4_ 372 | * Optimized bootloader size (724 to 770 bytes smaller dending on used MCU) 373 | Thanks to [Dan Hankewycz (hankedan000)](https://github.com/hankedan000)! 374 | * Added bitrate detect feature (merged from it's own branch since it now fits into 2048 words bootloader section) 375 | 376 | ## 1.3.1 (2021-09-30) 377 | 378 | * Fixed variable overflow while flashing addresses bigger than 0xFFFF (Tanks to Cosmin-Andrei Popovici for reporting this issue!) 379 | * Updated readme and fixed some spelling issues 380 | 381 | ## 1.3.0 (2021-06-18) 382 | 383 | * Added config option to use SFF CAN-IDs instead of the default EFF CAN-IDs 384 | * Added some basic checks for the defined CAN-IDs at compile time 385 | 386 | ## 1.2.0 (2021-06-17) 387 | 388 | * Added storing MCUSR into R2 on bootup 389 | This makes it possible to find the MCU reset caused in run code. 390 | 391 | ## License 392 | 393 | **CC BY-NC-SA 4.0** 394 | 395 | [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/) 396 | 397 | Copyright (C) 2020-2024 Peter Müller [https://crycode.de](https://crycode.de) 398 | -------------------------------------------------------------------------------- /doc/bits-of-byte-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crycode-de/mcp-can-boot/b76cf316346f3fff8c818624dd2aa306d81f1807/doc/bits-of-byte-3.png -------------------------------------------------------------------------------- /doc/can-msg-bytes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crycode-de/mcp-can-boot/b76cf316346f3fff8c818624dd2aa306d81f1807/doc/can-msg-bytes.png -------------------------------------------------------------------------------- /doc/flash-sequence.svg: -------------------------------------------------------------------------------- 1 | BootloaderBootloaderFlash-AppFlash-AppBootloader startFlash initFlash ready (0x0000)Flash data (4 bytes)Flash ready (0x0004)Flash data (4 bytes)Flash ready (0x0008)transmit all flash dataFlash data (2 bytes)Flash ready (0x1F42)Flash done verifyFlash done verifyFlash read (0x0000)Flash read data (4 bytes)Flash read (0x0004)Flash read data (4 bytes)read all flash dataFlash read (0x1F40)Flash read data (4 bytes)Start appStart app -------------------------------------------------------------------------------- /doc/flash-squence.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | skinparam { 3 | ArrowColor #555555 4 | ParticipantBorderColor #000000 5 | ParticipantBackgroundColor #eeeeee 6 | ParticipantBorderThickness 1 7 | LifeLineBorderColor #cccccc 8 | monochrome true 9 | shadowing false 10 | } 11 | 12 | Bootloader -> "Flash-App" : Bootloader start 13 | Bootloader <-- "Flash-App" : Flash init 14 | Bootloader -> "Flash-App" : Flash ready (0x0000) 15 | Bootloader <-- "Flash-App" : Flash data (4 bytes) 16 | Bootloader -> "Flash-App" : Flash ready (0x0004) 17 | Bootloader <-- "Flash-App" : Flash data (4 bytes) 18 | Bootloader -> "Flash-App" : Flash ready (0x0008) 19 | ... transmit all flash data ... 20 | Bootloader <-- "Flash-App" : Flash data (2 bytes) 21 | Bootloader -> "Flash-App" : Flash ready (0x1F42) 22 | Bootloader <-- "Flash-App" : Flash done verify 23 | Bootloader -> "Flash-App" : Flash done verify 24 | Bootloader <-- "Flash-App" : Flash read (0x0000) 25 | Bootloader -> "Flash-App" : Flash read data (4 bytes) 26 | Bootloader <-- "Flash-App" : Flash read (0x0004) 27 | Bootloader -> "Flash-App" : Flash read data (4 bytes) 28 | ... read all flash data ... 29 | Bootloader <-- "Flash-App" : Flash read (0x1F40) 30 | Bootloader -> "Flash-App" : Flash read data (4 bytes) 31 | Bootloader <-- "Flash-App" : Start app 32 | Bootloader -> "Flash-App" : Start app 33 | @enduml 34 | -------------------------------------------------------------------------------- /doc/mcp-can-boot-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crycode-de/mcp-can-boot/b76cf316346f3fff8c818624dd2aa306d81f1807/doc/mcp-can-boot-256.png -------------------------------------------------------------------------------- /doc/mcp-can-boot-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crycode-de/mcp-can-boot/b76cf316346f3fff8c818624dd2aa306d81f1807/doc/mcp-can-boot-512.png -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | [platformio] 11 | ;default_envs = ATmega1284P 12 | 13 | [env] 14 | platform = atmelavr 15 | framework = arduino 16 | 17 | build_flags = 18 | -Os ; optimize for size 19 | -funsigned-char 20 | -funsigned-bitfields 21 | -fpack-struct 22 | -fshort-enums 23 | -fno-exceptions 24 | -fno-jump-tables 25 | -mrelax 26 | -Wl,--relax 27 | -Wl,--cref 28 | -Wl,-Map=$BUILD_DIR/firmware.map 29 | 30 | board_build.f_cpu = 16000000L 31 | 32 | upload_protocol = stk500v1 33 | upload_flags = 34 | -P$UPLOAD_PORT 35 | -b$UPLOAD_SPEED 36 | -e 37 | 38 | upload_port = COM5 39 | upload_speed = 19200 40 | 41 | 42 | [env:ATmega32] 43 | board = ATmega32 44 | build_flags = 45 | ${env.build_flags} 46 | -Wl,--section-start=.text=0x7000 ; 2048 words bootloader, 0x3800 * 2 47 | 48 | ;board_fuses.lfuse = 0x3F 49 | ;board_fuses.hfuse = 0xD8 50 | 51 | [env:ATmega32U4] 52 | board = micro ; there is no atmega32u4 board in platformio but arduino micro should be fine 53 | board_build.mcu = atmega32u4 54 | build_flags = 55 | ${env.build_flags} 56 | -Wl,--section-start=.text=0x7000 ; 2048 words bootloader, 0x3800 * 2 57 | 58 | ;board_fuses.lfuse = 0xFF 59 | ;board_fuses.hfuse = 0xD8 60 | ;board_fuses.efuse = 0xFC 61 | 62 | [env:ATmega328P] 63 | board = ATmega328P 64 | build_flags = 65 | ${env.build_flags} 66 | -Wl,--section-start=.text=0x7000 ; 2048 words bootloader, 0x3800 * 2 67 | 68 | ;board_fuses.lfuse = 0xFF 69 | ;board_fuses.hfuse = 0xD8 70 | ;board_fuses.efuse = 0xFC 71 | 72 | [env:ATmega328PB] 73 | board = ATmega328PB 74 | build_flags = 75 | ${env.build_flags} 76 | -Wl,--section-start=.text=0x7000 ; 2048 words bootloader, 0x3800 * 2 77 | 78 | ;board_fuses.lfuse = 0xFF 79 | ;board_fuses.hfuse = 0xD8 80 | ;board_fuses.efuse = 0xFC 81 | 82 | [env:ATmega64] 83 | board = ATmega64 84 | build_flags = 85 | ${env.build_flags} 86 | -Wl,--section-start=.text=0xF000 ; 2048 words bootloader, 0x7800 * 2 87 | 88 | ;board_fuses.lfuse = 0x3F 89 | ;board_fuses.hfuse = 0xDA 90 | ;board_fuses.efuse = 0xFF 91 | 92 | 93 | [env:ATmega644P] 94 | board = ATmega644P 95 | build_flags = 96 | ${env.build_flags} 97 | -Wl,--section-start=.text=0xF000 ; 2048 words bootloader, 0x7800 * 2 98 | 99 | ;board_fuses.lfuse = 0xFF 100 | ;board_fuses.hfuse = 0xDA 101 | ;board_fuses.efuse = 0xFC 102 | 103 | 104 | [env:ATmega128] 105 | board = ATmega128 106 | build_flags = 107 | ${env.build_flags} 108 | -Wl,--section-start=.text=0x01F000 ; 2048 words bootloader, 0xF800 * 2 109 | 110 | ;board_fuses.lfuse = 0x3F 111 | ;board_fuses.hfuse = 0xDA 112 | ;board_fuses.efuse = 0xFF 113 | 114 | 115 | [env:ATmega1284P] 116 | board = ATmega1284P 117 | build_flags = 118 | ${env.build_flags} 119 | -Wl,--section-start=.text=0x01F000 ; 2048 words bootloader, 0xF800 * 2 120 | 121 | ;board_fuses.lfuse = 0xFF 122 | ;board_fuses.hfuse = 0xDA 123 | ;board_fuses.efuse = 0xFC 124 | 125 | 126 | [env:ATmega2560] 127 | board = ATmega2560 128 | build_flags = 129 | ${env.build_flags} 130 | -Wl,--section-start=.text=0x03F000 ; 2048 words bootloader, 0x1F800 * 2 131 | 132 | ;board_fuses.lfuse = 0xFF 133 | ;board_fuses.hfuse = 0xDA 134 | ;board_fuses.efuse = 0xFC 135 | 136 | ; *** 137 | ; Additional environments as examples below 138 | ; *** 139 | 140 | ; ATmega328P with AVR ISP MK2 141 | [env:ATmega328P_via_AVRISP_mkII] 142 | board = ATmega328P 143 | build_flags = 144 | ${env.build_flags} 145 | -Wl,--section-start=.text=0x7000 ; 2048 words bootloader, 0x3800 * 2 146 | 147 | board_build.f_cpu = 16000000L 148 | 149 | board_fuses.lfuse = 0xFF 150 | board_fuses.hfuse = 0xD8 151 | board_fuses.efuse = 0xFC 152 | 153 | upload_protocol = custom 154 | upload_port = usb 155 | upload_flags = 156 | -C 157 | ; use "tool-avrdude-megaavr" for the atmelmegaavr platform 158 | ${platformio.packages_dir}/tool-avrdude/avrdude.conf 159 | -p 160 | $BOARD_MCU 161 | -P 162 | $UPLOAD_PORT 163 | -c 164 | stk500v2 ; avrispmkII 165 | upload_command = avrdude $UPLOAD_FLAGS -U flash:w:$SOURCE:i 166 | -------------------------------------------------------------------------------- /src/bootloader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MCP-CAN-Boot 3 | * 4 | * CAN bus bootloader for AVR microcontrollers attached to an MCP2515 CAN controller. 5 | * 6 | * Copyright (C) 2020-2024 Peter Müller (https://crycode.de) 7 | * License: CC BY-NC-SA 4.0 8 | */ 9 | 10 | #include "bootloader.h" 11 | 12 | // define macros for LED control if enabled 13 | #ifdef LED 14 | #define LED_ON LED_PORT |= (1 << LED) 15 | #define LED_OFF LED_PORT &= ~(1 << LED) 16 | #define LED_TOGGLE LED_PORT ^= (1 << LED) 17 | #define LED_INIT LED_DDR |= (1 << LED) 18 | #define LED_DEINIT LED_DDR &= ~(1< startTime + TIMEOUT) { 193 | startApp(); 194 | } 195 | 196 | // turn led on if time to turn is set and greater than current time 197 | if (ledTime != 0 && curTime >= ledTime) { 198 | LED_ON; 199 | ledTime = 0; 200 | } 201 | 202 | // try to get a message from the CAN controller 203 | if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) { 204 | // got a message... 205 | if (canMsg.can_id == 206 | #if CAN_EFF 207 | (CAN_ID_REMOTE_TO_MCU | CAN_EFF_FLAG) 208 | #else 209 | CAN_ID_REMOTE_TO_MCU 210 | #endif 211 | && canMsg.can_dlc == 8 212 | && canMsg.data[CAN_DATA_BYTE_MCU_ID_MSB] == MCU_ID_MSB 213 | && canMsg.data[CAN_DATA_BYTE_MCU_ID_LSB] == MCU_ID_LSB) { 214 | // ... and the message is for this bootloader 215 | 216 | // for all CAN messages to send in this block, can_dlc and MCU ID will 217 | // be set correctly by the incoming message, so we can save flash space 218 | // by not setting them again ... :-) 219 | 220 | // toggle the LED on each can message and set time to turn it on again 221 | // after 100ms of inactivity 222 | LED_TOGGLE; 223 | ledTime = curTime + 100; 224 | 225 | // set the can_id once to save flash space 226 | #if CAN_EFF 227 | canMsg.can_id = CAN_ID_MCU_TO_REMOTE | CAN_EFF_FLAG; 228 | #else 229 | canMsg.can_id = CAN_ID_MCU_TO_REMOTE; 230 | #endif 231 | 232 | if (!flashing) { 233 | // we are not in bootloading mode... only handle flash init messages 234 | if (canMsg.data[CAN_DATA_BYTE_CMD] == CMD_FLASH_INIT 235 | && canMsg.data[4] == SIGNATURE_0 236 | && canMsg.data[5] == SIGNATURE_1 237 | && canMsg.data[6] == SIGNATURE_2) { 238 | // init bootloading mode 239 | 240 | flashing = true; 241 | 242 | // send flash ready message 243 | prepMsg(CMD_FLASH_READY, 0x00, flashAddr); 244 | mcp2515.sendMessage(&canMsg); 245 | 246 | } 247 | 248 | } else { 249 | // we are in flashing mode... 250 | if (canMsg.data[CAN_DATA_BYTE_CMD] == CMD_FLASH_ERASE) { 251 | // erase flash 252 | flashAddr = 0; 253 | do { 254 | boot_page_erase(flashAddr); 255 | boot_spm_busy_wait(); 256 | flashAddr += SPM_PAGESIZE; 257 | } while (flashAddr < FLASHEND_BL); 258 | 259 | memset(flashBuffer, 0xFF, SPM_PAGESIZE); 260 | flashBufferDataCount = 0; 261 | flashPage = 0; 262 | flashBufferPos = 0; 263 | flashAddr = 0; 264 | 265 | prepMsg(CMD_FLASH_READY, 0x00, flashAddr); 266 | mcp2515.sendMessage(&canMsg); 267 | 268 | } else if (canMsg.data[CAN_DATA_BYTE_CMD] == CMD_FLASH_READ) { 269 | // read flash memory at given address 270 | uint32_t readFlashAddr = (uint32_t)canMsg.data[7] + ((uint32_t)canMsg.data[6] << 8) + ((uint32_t)canMsg.data[5] << 16) + ((uint32_t)canMsg.data[4] << 24); 271 | 272 | if (readFlashAddr > FLASHEND_BL) { 273 | // flash read after flash end 274 | prepMsg(CMD_FLASH_READ_ADDRESS_ERROR, 0x00, FLASHEND_BL); 275 | mcp2515.sendMessage(&canMsg); 276 | continue; 277 | } 278 | 279 | uint8_t len = 0; 280 | for (uint8_t i = 0; i < 4; i++) { 281 | if (readFlashAddr + i <= FLASHEND_BL) { 282 | // in flash area 283 | canMsg.data[4 + i] = pgm_read_byte_near(readFlashAddr + i); 284 | len++; 285 | } else { 286 | // not in flash area 287 | canMsg.data[4 + i] = 0x00; 288 | } 289 | } 290 | 291 | canMsg.data[CAN_DATA_BYTE_CMD] = CMD_FLASH_READ_DATA; 292 | canMsg.data[CAN_DATA_BYTE_LEN_AND_ADDR] = (len << 5) | (readFlashAddr & 0b00011111); // number of data bytes read and address part 293 | mcp2515.sendMessage(&canMsg); 294 | 295 | } else if (canMsg.data[CAN_DATA_BYTE_CMD] == CMD_FLASH_SET_ADDRESS) { 296 | // set the start address for flashing 297 | uint32_t newFlashAddr = (uint32_t)canMsg.data[7] + ((uint32_t)canMsg.data[6] << 8) + ((uint32_t)canMsg.data[5] << 16) + ((uint32_t)canMsg.data[4] << 24); 298 | uint16_t newFlashPage = newFlashAddr / SPM_PAGESIZE; 299 | uint16_t newFlashBufferPos = newFlashAddr % SPM_PAGESIZE; 300 | 301 | if (newFlashAddr > FLASHEND_BL) { 302 | // address cannot be flashed 303 | prepMsg(CMD_FLASH_ADDRESS_ERROR, 0x00, FLASHEND_BL); 304 | mcp2515.sendMessage(&canMsg); 305 | continue; 306 | } 307 | 308 | if (newFlashPage != flashPage && flashBufferDataCount > 0) { 309 | // new flash page and data in buffer to write to last flash page... 310 | // write data to flash page 311 | writeFlashPage(); 312 | } 313 | 314 | flashAddr = newFlashAddr; 315 | flashPage = newFlashPage; 316 | flashBufferPos = newFlashBufferPos; 317 | 318 | // send flash ready 319 | prepMsg(CMD_FLASH_READY, 0x00, flashAddr); 320 | mcp2515.sendMessage(&canMsg); 321 | 322 | } else if (canMsg.data[CAN_DATA_BYTE_CMD] == CMD_FLASH_DATA) { 323 | // data for flashing 324 | 325 | // check address part (lower 5 bits) 326 | if ((flashAddr & 0b00011111) != (canMsg.data[CAN_DATA_BYTE_LEN_AND_ADDR] & 0b00011111)) { 327 | // send flash data error with the exprected flash address 328 | prepMsg(CMD_FLASH_DATA_ERROR, 0x00, flashAddr); 329 | mcp2515.sendMessage(&canMsg); 330 | continue; 331 | } 332 | 333 | // data length can be up to 4 bytes 334 | uint8_t len = (canMsg.data[CAN_DATA_BYTE_LEN_AND_ADDR] >> 5); 335 | if ((flashAddr + len - 1) > FLASHEND_BL) { 336 | // address cannot be flashed 337 | prepMsg(CMD_FLASH_ADDRESS_ERROR, 0x00, FLASHEND_BL); 338 | mcp2515.sendMessage(&canMsg); 339 | continue; 340 | } 341 | for (uint8_t i = 0; i < len; i++) { 342 | flashBuffer[flashBufferPos++] = canMsg.data[4 + i]; 343 | flashBufferDataCount++; 344 | flashAddr++; 345 | if (flashBufferPos >= SPM_PAGESIZE) { 346 | // flash page is full... write it! 347 | writeFlashPage(); 348 | } 349 | } 350 | 351 | // send flash ready 352 | prepMsg(CMD_FLASH_READY, len, flashAddr); 353 | mcp2515.sendMessage(&canMsg); 354 | 355 | } else if (canMsg.data[CAN_DATA_BYTE_CMD] == CMD_FLASH_DONE) { 356 | // flashing done... 357 | if (flashBufferDataCount > 0) { 358 | // still data in flash buffer... write last page 359 | writeFlashPage(); 360 | } 361 | 362 | // send start app 363 | prepMsg(CMD_START_APP, 0x00, 0x00000000); 364 | mcp2515.sendMessage(&canMsg); 365 | 366 | // write value of local mcusr into R2 367 | #if MCUSR_TO_R2 368 | __asm__ __volatile__(" mov r2,%[mcusr_val] ;Move Between Registers \n\t" 369 | ::[mcusr_val] "r" (mcusr)); 370 | #endif 371 | 372 | // start the main application 373 | startApp(); 374 | 375 | } else if (canMsg.data[CAN_DATA_BYTE_CMD] == CMD_FLASH_DONE_VERIFY) { 376 | // flashing done and verify requested... 377 | if (flashBufferDataCount > 0) { 378 | // still data in flash buffer... write last page 379 | writeFlashPage(); 380 | } 381 | 382 | // send flash done verify back 383 | prepMsg(CMD_FLASH_DONE_VERIFY, 0x00, 0x00000000); 384 | mcp2515.sendMessage(&canMsg); 385 | 386 | } else if (canMsg.data[CAN_DATA_BYTE_CMD] == CMD_START_APP) { 387 | // just start the main application now 388 | prepMsg(CMD_START_APP, 0x00, 0x00000000); 389 | mcp2515.sendMessage(&canMsg); 390 | 391 | // write value of local mcusr into R2 392 | #if MCUSR_TO_R2 393 | __asm__ __volatile__(" mov r2,%[mcusr_val] ;Move Between Registers \n\t" 394 | ::[mcusr_val] "r" (mcusr)); 395 | #endif 396 | 397 | // start the main application 398 | startApp(); 399 | } 400 | } 401 | 402 | } 403 | } 404 | } 405 | } 406 | 407 | /** 408 | * Prepare data to send a CAN message with command, length and flash address. 409 | * Using this function whenever possible will save some flash space. 410 | */ 411 | void prepMsg (uint8_t cmd, uint8_t len, uint32_t flashAddr) { 412 | canMsg.data[CAN_DATA_BYTE_CMD] = cmd; 413 | canMsg.data[CAN_DATA_BYTE_LEN_AND_ADDR] = (len << 5) | (flashAddr & 0b00011111); 414 | canMsg.data[4] = (flashAddr >> 24) & 0xFF; 415 | canMsg.data[5] = (flashAddr >> 16) & 0xFF; 416 | canMsg.data[6] = (flashAddr >> 8) & 0xFF; 417 | canMsg.data[7] = flashAddr & 0xFF; 418 | } 419 | 420 | /** 421 | * Write data from current global flash buffer to the flash page. 422 | * After writing the global flash buffer will be emptied, the buffer position 423 | * will be reset to 0 and the flash page will be increased. 424 | */ 425 | void writeFlashPage () { 426 | boot_program_page(flashPage, flashBuffer); 427 | memset(flashBuffer, 0xFF, SPM_PAGESIZE); 428 | flashBufferDataCount = 0; 429 | flashPage++; 430 | flashBufferPos = 0; 431 | } 432 | 433 | /** 434 | * Write data from buffer to a flash page. 435 | * @param page Flash page number to write to. 436 | * @param buf Buffer containing the flash data for that page. 437 | */ 438 | void boot_program_page (uint16_t page, uint8_t *buf) { 439 | uint16_t i; 440 | uint8_t sreg; 441 | 442 | uint32_t addr = ((uint32_t)page) * SPM_PAGESIZE; // type cast of `page` to support addresses bigger than 0xFFFF 443 | 444 | // Disable interrupts 445 | sreg = SREG; 446 | cli(); 447 | 448 | eeprom_busy_wait(); 449 | 450 | boot_page_erase(addr); 451 | boot_spm_busy_wait(); // Wait until the memory is erased 452 | 453 | for (i=0; i (https://crycode.de) 7 | * License: CC BY-NC-SA 4.0 8 | */ 9 | 10 | #ifndef __MCP_CAN_BOOT_MAIN_H__ 11 | #define __MCP_CAN_BOOT_MAIN_H__ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "mcp2515.h" 19 | #include "config.h" 20 | #include "controllers.h" 21 | 22 | /** 23 | * Command set version of this bootloader. 24 | * Used to identify a possibly incompatible flash application on remote. 25 | */ 26 | #define BOOTLOADER_CMD_VERSION 0x01 27 | 28 | /* 29 | * Positions of fixed data parts in each bootloader CAN message. 30 | */ 31 | #define CAN_DATA_BYTE_MCU_ID_MSB 0 32 | #define CAN_DATA_BYTE_MCU_ID_LSB 1 33 | #define CAN_DATA_BYTE_CMD 2 34 | #define CAN_DATA_BYTE_LEN_AND_ADDR 3 35 | 36 | /* 37 | * CAN message commands definitions 38 | */ 39 | #define CMD_PING 0b00000000 // remote -> mcu 40 | #define CMD_ERROR 0b00000001 // mcu -> remote 41 | #define CMD_BOOTLOADER_START 0b00000010 // mcu -> remote 42 | #define CMD_FLASH_INIT 0b00000110 // remote -> mcu 43 | #define CMD_FLASH_READY 0b00000100 // mcu -> remote 44 | #define CMD_FLASH_SET_ADDRESS 0b00001010 // remote -> mcu 45 | #define CMD_FLASH_ADDRESS_ERROR 0b00001011 // mcu -> remote 46 | #define CMD_FLASH_DATA 0b00001000 // remote -> mcu 47 | #define CMD_FLASH_DATA_ERROR 0b00001101 // mcu -> remote 48 | #define CMD_FLASH_DONE 0b00010000 // remote -> mcu 49 | #define CMD_FLASH_DONE_VERIFY 0b01010000 // remote <-> mcu 50 | #define CMD_FLASH_ERASE 0b00100000 // remote -> mcu 51 | #define CMD_FLASH_READ 0b01000000 // remote -> mcu 52 | #define CMD_FLASH_READ_DATA 0b01001000 // mcu -> remote 53 | #define CMD_FLASH_READ_ADDRESS_ERROR 0b01001011 // mcu -> remote 54 | #define CMD_START_APP 0b10000000 // mcu <-> remote 55 | 56 | /* 57 | * Fixed definitions to be used in the code. 58 | */ 59 | #define MCU_ID_LSB (mcuId & 0xFF) 60 | #define MCU_ID_MSB ((mcuId >> 8) & 0xFF) 61 | #define FLASHEND_BL (FLASHEND - BOOTLOADER_SIZE) 62 | 63 | /* 64 | * Function declarations 65 | */ 66 | int main (); 67 | void prepMsg (uint8_t cmd, uint8_t len, uint32_t flashAddr); 68 | void writeFlashPage (); 69 | void boot_program_page (uint16_t page, uint8_t *buf); 70 | void startApp (); 71 | 72 | /* 73 | * Definition checks 74 | */ 75 | #ifdef LED 76 | #if !defined(LED_DDR) || !defined(LED_PORT) 77 | #error When using LED, also LED_DDR and LED_PORT must be defined! 78 | #endif 79 | #endif 80 | 81 | #ifdef MCP_CS 82 | #if !defined(MCP_CS_DDR) || !defined(MCP_CS_PORT) 83 | #error When using MCP_CS, also MCP_CS_DDR and MCP_CS_PORT must be defined! 84 | #endif 85 | 86 | #ifndef SET_SPI_SS_OUTPUT 87 | #warning You are using a custom CS pin and SPI_SS is not defined as an output which may lead to an unresponding bootloader. Please check config.h for SET_SPI_SS_OUTPUT or make sure your hardware will pull up SPI_SS externally. 88 | #endif 89 | #endif 90 | 91 | #if CAN_EFF 92 | #if CAN_ID_MCU_TO_REMOTE > 0x1FFFFFFF 93 | #error CAN_ID_MCU_TO_REMOTE is greater than 0x1FFFFFFF! Please check your config! 94 | #endif 95 | #if CAN_ID_REMOTE_TO_MCU > 0x1FFFFFFF 96 | #error CAN_ID_REMOTE_TO_MCU is greater than 0x1FFFFFFF! Please check your config! 97 | #endif 98 | #else 99 | #if CAN_ID_MCU_TO_REMOTE > 0x7FF 100 | #error CAN_EFF is not enabled and CAN_ID_MCU_TO_REMOTE is greater than 0x7FF! Please check your config! 101 | #endif 102 | #if CAN_ID_REMOTE_TO_MCU > 0x7FF 103 | #error CAN_EFF is not enabled and CAN_ID_REMOTE_TO_MCU is greater than 0x7FF! Please check your config! 104 | #endif 105 | #endif 106 | 107 | #ifdef CAN_KBPS_DETECT 108 | #if !defined(TIMEOUT_DETECT_CAN_KBPS) 109 | #error When using CAN_KBPS_DETECT, also TIMEOUT_DETECT_CAN_KBPS must be defined! 110 | #endif 111 | #endif 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/can.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CAN definitions 3 | * 4 | * Originally taken from https://github.com/autowp/arduino-mcp2515 5 | * 6 | */ 7 | 8 | #ifndef CAN_H_ 9 | #define CAN_H_ 10 | 11 | #include 12 | 13 | 14 | typedef unsigned char __u8; 15 | typedef unsigned short __u16; 16 | typedef unsigned long __u32; 17 | 18 | 19 | /* special address description flags for the CAN_ID */ 20 | #define CAN_EFF_FLAG 0x80000000UL /* EFF/SFF is set in the MSB */ 21 | #define CAN_RTR_FLAG 0x40000000UL /* remote transmission request */ 22 | #define CAN_ERR_FLAG 0x20000000UL /* error message frame */ 23 | 24 | /* valid bits in CAN ID for frame formats */ 25 | #define CAN_SFF_MASK 0x000007FFUL /* standard frame format (SFF) */ 26 | #define CAN_EFF_MASK 0x1FFFFFFFUL /* extended frame format (EFF) */ 27 | #define CAN_ERR_MASK 0x1FFFFFFFUL /* omit EFF, RTR, ERR flags */ 28 | 29 | /* 30 | * Controller Area Network Identifier structure 31 | * 32 | * bit 0-28 : CAN identifier (11/29 bit) 33 | * bit 29 : error message frame flag (0 = data frame, 1 = error message) 34 | * bit 30 : remote transmission request flag (1 = rtr frame) 35 | * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit) 36 | */ 37 | typedef __u32 canid_t; 38 | 39 | #define CAN_SFF_ID_BITS 11 40 | #define CAN_EFF_ID_BITS 29 41 | 42 | /* CAN payload length and DLC definitions according to ISO 11898-1 */ 43 | #define CAN_MAX_DLC 8 44 | #define CAN_MAX_DLEN 8 45 | 46 | struct can_frame { 47 | canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ 48 | __u8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */ 49 | __u8 data[CAN_MAX_DLEN] __attribute__((aligned(8))); 50 | }; 51 | 52 | #endif /* CAN_H_ */ 53 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MCP-CAN-Boot 3 | * 4 | * CAN bus bootloader for AVR microcontrollers attached to an MCP2515 CAN controller. 5 | * 6 | * Copyright (C) 2020-2024 Peter Müller (https://crycode.de) 7 | * License: CC BY-NC-SA 4.0 8 | * 9 | * Configuration 10 | */ 11 | 12 | #ifndef __MCP_CAN_BOOT_CONFIG_H__ 13 | #define __MCP_CAN_BOOT_CONFIG_H__ 14 | 15 | /** 16 | * The ID of the MCU to identify it in bootloader CAN messages. 17 | * You may use a fixed value or an expressions to get the ID from the EEPROM 18 | * which allows you to change the ID by your main application. 19 | * Range: 0x0000 to 0xFFFF 20 | */ 21 | #define MCU_ID 0x0042 22 | //#define MCU_ID eeprom_read_word((uint16_t*) 0x00) 23 | //#define MCU_ID eeprom_read_byte((uint8_t*) 0x00) 24 | 25 | /** 26 | * Timeout for the bootloader in milliseconds. 27 | * In this amount of time after MCU reset a "flash init" command must be 28 | * received via CAN to enter the bootloading mode. Otherwise the main 29 | * application will be started. 30 | */ 31 | #define TIMEOUT 250 32 | 33 | /** 34 | * Bitrate of the CAN bus. 35 | * CAN_5KBPS, CAN_10KBPS, CAN_20KBPS, CAN_31K25BPS, CAN_33KBPS, CAN_40KBPS, 36 | * CAN_50KBPS, CAN_80KBPS, CAN_83K3BPS, CAN_95KBPS, CAN_100KBPS, CAN_125KBPS, 37 | * CAN_200KBPS, CAN_250KBPS, CAN_500KBPS or CAN_1000KBPS 38 | */ 39 | #define CAN_KBPS CAN_500KBPS 40 | 41 | /** 42 | * Set multiple bitrates to let the bootloader try to detect the bitrate used on 43 | * the CAN bus. 44 | * For each set bitrate the MCP2515 will be set to this bitrate and into listen 45 | * only mode. Then the bootloader will wait for a defined timeout to receive a 46 | * valid message. If a message is received the bootloader will assume the 47 | * current bitrate as the bitrate to use. 48 | * If no bitrate could be detected, the fixed bitrate defined in CAN_KBPS will 49 | * be used. If not defined, only the fixed bitrate will be used. 50 | * Hint: If you use this together with the LED definition, you may need to use 51 | * a 4096 words bootloader section together with the *_4k PlatformIO envs 52 | * for some MCUs, but a 4096 words bootloader is not supported by all MCUs. 53 | * Hint: If you use this, the delay to boot the main application will be in 54 | * worst case the TIMEOUT set above plus the number of bitrates to detect 55 | * multiplied by the TIMEOUT_DETECT_CAN_KBPS. 56 | */ 57 | //#define CAN_KBPS_DETECT CAN_50KBPS, CAN_100KBPS, CAN_125KBPS, CAN_250KBPS, CAN_500KBPS 58 | 59 | /** 60 | * Timeout for each bitrate to detect a valid message in milliseconds. 61 | * In this time a valid message must be received to let the bootloader detect 62 | * the bitrate. 63 | * Only used if CAN_KBPS_DETECT is set. 64 | */ 65 | //#define TIMEOUT_DETECT_CAN_KBPS 100 66 | 67 | /** 68 | * Clock speed of the MCP2515 CAN controller. 69 | * MCP_8MHZ, MCP_16MHZ or MCP_20MHZ 70 | */ 71 | #define MCP_CLOCK MCP_16MHZ 72 | 73 | /** 74 | * Optional definition of a custom CS (chip select) pin for the MCP2515. 75 | * If not defined, the default SPI_SS pin will be used for chip select. 76 | */ 77 | //#define MCP_CS PORTB0 78 | //#define MCP_CS_DDR DDRB 79 | //#define MCP_CS_PORT PORTB 80 | 81 | /** 82 | * When using a custom CS pin, then it must be ensured that the SPI_SS pin is 83 | * defined as an output or externally pulled high. Otherwise the bootloader may 84 | * be unresponding because the controller may enter SPI slave mode. 85 | * Use the following definition to set SPI_SS as an output (if a custom CS pin 86 | * is used) with the given value (HIGH or LOW). 87 | */ 88 | //#define SET_SPI_SS_OUTPUT HIGH 89 | 90 | /** 91 | * Use the Extended Frame Format (EFF) for CAN-Messages. (8 hex chars) 92 | * Set to `false` to use the Standard Frame Format (SFF). (3 hex chars) 93 | */ 94 | #define CAN_EFF true 95 | 96 | /** 97 | * CAN-ID for bootloader message from MCU to remote. 98 | */ 99 | #define CAN_ID_MCU_TO_REMOTE 0x1FFFFF01UL 100 | //#define CAN_ID_MCU_TO_REMOTE 0x1F1 101 | 102 | /** 103 | * CAN-ID for bootloader message from remote to MCU. 104 | */ 105 | #define CAN_ID_REMOTE_TO_MCU 0x1FFFFF02UL 106 | //#define CAN_ID_REMOTE_TO_MCU 0x1F2 107 | 108 | /** 109 | * Optional definition of a LED port, which will be used to indicate 110 | * bootloader actions. 111 | */ 112 | //#define LED PORTA1 113 | //#define LED_DDR DDRA 114 | //#define LED_PORT PORTA 115 | 116 | /** 117 | * Store the MCU Status Register to Register 2 (R2). 118 | * The MCU Status Register provides information on which reset source 119 | * caused an MCU reset. 120 | * 121 | * Paste this code into your program (not the bootloader): 122 | * \code 123 | * uint8_t mcusr __attribute__ ((section (".noinit"))); 124 | * void getMCUSR(void) __attribute__((naked)) __attribute__((section(".init0"))); 125 | * void getMCUSR(void) { 126 | * __asm__ __volatile__ ( "mov %0, r2 \n" : "=r" (mcusr) : ); 127 | * } 128 | * \endcode 129 | * 130 | * To use: 131 | * \code 132 | * mcusr; // the MCU Status Register (global variable) 133 | * \endcode 134 | * 135 | * Or using a local variable: 136 | * \code 137 | * void main() { 138 | * uint8_t mcusr; 139 | * __asm__ __volatile__ ( "mov %0, r2 \n" : "=r" (mcusr) : ); 140 | * } 141 | * \endcode 142 | * 143 | * To use: 144 | * \code 145 | * mcusr; // the MCU Status Register (local variable in function main) 146 | * \endcode 147 | */ 148 | #define MCUSR_TO_R2 true 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /src/controllers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MCP-CAN-Boot 3 | * 4 | * CAN bus bootloader for AVR microcontrollers attached to an MCP2515 CAN controller. 5 | * 6 | * Copyright (C) 2020-2024 Peter Müller (https://crycode.de) 7 | * 8 | * License: CC BY-NC-SA 4.0 9 | * 10 | * 11 | * Controller specific definitions. 12 | */ 13 | 14 | #ifndef __MCP_CAN_BOOT_CONTROLLERS_H__ 15 | #define __MCP_CAN_BOOT_CONTROLLERS_H__ 16 | 17 | #include 18 | 19 | #define BOOTLOADER_SIZE 4096 20 | 21 | #if defined(__AVR_ATmega32__) 22 | #define IV_REG GICR 23 | 24 | #define SPI_DDR DDRB 25 | #define SPI_PORT PORTB 26 | #define SPI_SS 4 27 | #define SPI_MOSI 5 28 | #define SPI_MISO 6 29 | #define SPI_SCK 7 30 | 31 | #elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega2560__) 32 | #define IV_REG MCUCR 33 | 34 | #define SPI_DDR DDRB 35 | #define SPI_PORT PORTB 36 | #define SPI_SS 0 37 | #define SPI_MOSI 2 38 | #define SPI_MISO 3 39 | #define SPI_SCK 1 40 | 41 | #elif defined(__AVR_ATmega32U4__) 42 | #define IV_REG MCUCR 43 | 44 | #define SPI_DDR DDRB 45 | #define SPI_PORT PORTB 46 | #define SPI_SS 0 47 | #define SPI_MOSI 2 48 | #define SPI_MISO 3 49 | #define SPI_SCK 1 50 | 51 | #elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) 52 | #define IV_REG MCUCR 53 | 54 | #define SPI_DDR DDRB 55 | #define SPI_PORT PORTB 56 | #define SPI_SS 2 57 | #define SPI_MOSI 3 58 | #define SPI_MISO 4 59 | #define SPI_SCK 5 60 | 61 | #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) 62 | #define IV_REG MCUCR 63 | 64 | #define SPI_DDR DDRB 65 | #define SPI_PORT PORTB 66 | #define SPI_SS 4 67 | #define SPI_MOSI 5 68 | #define SPI_MISO 6 69 | #define SPI_SCK 7 70 | 71 | #else 72 | #error Unsupported MCU 73 | #endif 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/mcp2515.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MCP2515 CAN interface library 3 | * 4 | * Originally taken from https://github.com/autowp/arduino-mcp2515 5 | * 6 | * Modified by Peter Müller (https://crycode.de) for 7 | * MCP-CAN-Boot to use less flash space and work correctly within the 8 | * bootloader section. 9 | */ 10 | 11 | #include "mcp2515.h" 12 | 13 | const struct MCP2515::TXBn_REGS MCP2515::TXB[MCP2515::N_TXBUFFERS] = { 14 | {MCP_TXB0CTRL, MCP_TXB0SIDH, MCP_TXB0DATA}, 15 | {MCP_TXB1CTRL, MCP_TXB1SIDH, MCP_TXB1DATA}, 16 | {MCP_TXB2CTRL, MCP_TXB2SIDH, MCP_TXB2DATA} 17 | }; 18 | 19 | const struct MCP2515::RXBn_REGS MCP2515::RXB[N_RXBUFFERS] = { 20 | {MCP_RXB0CTRL, MCP_RXB0SIDH, MCP_RXB0DATA, CANINTF_RX0IF}, 21 | {MCP_RXB1CTRL, MCP_RXB1SIDH, MCP_RXB1DATA, CANINTF_RX1IF} 22 | }; 23 | 24 | 25 | 26 | MCP2515::MCP2515 () { } 27 | 28 | void MCP2515::init () { 29 | 30 | // set spi pins as output 31 | #if !defined(MCP_CS) || defined(SET_SPI_SS_OUTPUT) 32 | SPI_DDR |= ((1<= 16MHz ... SCK frequency = oscillator frequency / 32 64 | SPCR = ((1<> 8); 534 | canid = (uint16_t)(id >> 16); 535 | buffer[MCP_SIDL] = (uint8_t) (canid & 0x03); 536 | buffer[MCP_SIDL] += (uint8_t) ((canid & 0x1C) << 3); 537 | buffer[MCP_SIDL] |= TXB_EXIDE_MASK; 538 | buffer[MCP_SIDH] = (uint8_t) (canid >> 5); 539 | } else { 540 | buffer[MCP_SIDH] = (uint8_t) (canid >> 3); 541 | buffer[MCP_SIDL] = (uint8_t) ((canid & 0x07 ) << 5); 542 | buffer[MCP_EID0] = 0; 543 | buffer[MCP_EID8] = 0; 544 | } 545 | } 546 | 547 | MCP2515::ERROR MCP2515::setFilterMask(const MASK mask, const bool ext, const uint32_t ulData) { 548 | ERROR res = setConfigMode(); 549 | if (res != ERROR_OK) { 550 | return res; 551 | } 552 | 553 | uint8_t tbufdata[4]; 554 | prepareId(tbufdata, ext, ulData); 555 | 556 | REGISTER reg; 557 | switch (mask) { 558 | case MASK0: reg = MCP_RXM0SIDH; break; 559 | case MASK1: reg = MCP_RXM1SIDH; break; 560 | default: 561 | return ERROR_FAIL; 562 | } 563 | 564 | setRegisters(reg, tbufdata, 4); 565 | 566 | return ERROR_OK; 567 | } 568 | 569 | MCP2515::ERROR MCP2515::setFilter(const RXF num, const bool ext, const uint32_t ulData) { 570 | ERROR res = setConfigMode(); 571 | if (res != ERROR_OK) { 572 | return res; 573 | } 574 | 575 | REGISTER reg; 576 | 577 | switch (num) { 578 | case RXF0: reg = MCP_RXF0SIDH; break; 579 | case RXF1: reg = MCP_RXF1SIDH; break; 580 | case RXF2: reg = MCP_RXF2SIDH; break; 581 | case RXF3: reg = MCP_RXF3SIDH; break; 582 | case RXF4: reg = MCP_RXF4SIDH; break; 583 | case RXF5: reg = MCP_RXF5SIDH; break; 584 | default: 585 | return ERROR_FAIL; 586 | } 587 | 588 | uint8_t tbufdata[4]; 589 | prepareId(tbufdata, ext, ulData); 590 | setRegisters(reg, tbufdata, 4); 591 | 592 | return ERROR_OK; 593 | } 594 | 595 | MCP2515::ERROR MCP2515::sendMessage(const struct can_frame *frame) { 596 | if (frame->can_dlc > CAN_MAX_DLEN) { 597 | return ERROR_FAILTX; 598 | } 599 | 600 | const struct TXBn_REGS *txbuf = &TXB[0];// bootloader only uses tx0 601 | 602 | uint8_t data[13]; 603 | 604 | bool ext = (frame->can_id & CAN_EFF_FLAG); 605 | bool rtr = (frame->can_id & CAN_RTR_FLAG); 606 | uint32_t id = (frame->can_id & (ext ? CAN_EFF_MASK : CAN_SFF_MASK)); 607 | 608 | prepareId(data, ext, id); 609 | 610 | data[MCP_DLC] = rtr ? (frame->can_dlc | RTR_MASK) : frame->can_dlc; 611 | 612 | memcpy(&data[MCP_DATA], frame->data, frame->can_dlc); 613 | 614 | setRegisters(txbuf->SIDH, data, 5 + frame->can_dlc); 615 | 616 | modifyRegister(txbuf->CTRL, TXB_TXREQ, TXB_TXREQ); 617 | 618 | // Wait for transmission to complete or timeout (assumes we are not in one-shot 619 | // mode). The MCP2515 will try to transmit a frame until successful. It will 620 | // report errors along the way, but those are just informational. The chip will 621 | // only stop trying once we abort the request after the timeout. 622 | uint8_t ctrl = 0x00; 623 | uint8_t timeout = 255; 624 | do { 625 | ctrl = readRegister(txbuf->CTRL); 626 | } while ((ctrl & TXB_TXREQ) && (--timeout != 0)); 627 | 628 | if (timeout == 0) { 629 | // upon timeout, abort tx request and return error 630 | modifyRegister(txbuf->CTRL, TXB_TXREQ, 0); 631 | return ERROR_FAILTX; 632 | } 633 | return ERROR_OK; 634 | } 635 | 636 | MCP2515::ERROR MCP2515::readMessage(struct can_frame *frame) { 637 | uint8_t stat = getStatus(); 638 | if ( ! (stat & STAT_RX0IF)) { 639 | return ERROR_NOMSG; 640 | } 641 | 642 | const struct RXBn_REGS *rxb = &RXB[0];// bootloader only uses rx0 643 | 644 | uint8_t tbufdata[5]; 645 | 646 | readRegisters(rxb->SIDH, tbufdata, 5); 647 | 648 | uint32_t id = (tbufdata[MCP_SIDH]<<3) + (tbufdata[MCP_SIDL]>>5); 649 | 650 | if ( (tbufdata[MCP_SIDL] & TXB_EXIDE_MASK) == TXB_EXIDE_MASK ) { 651 | id = (id<<2) + (tbufdata[MCP_SIDL] & 0x03); 652 | id = (id<<8) + tbufdata[MCP_EID8]; 653 | id = (id<<8) + tbufdata[MCP_EID0]; 654 | id |= CAN_EFF_FLAG; 655 | } 656 | 657 | uint8_t dlc = (tbufdata[MCP_DLC] & DLC_MASK); 658 | if (dlc > CAN_MAX_DLEN) { 659 | return ERROR_FAIL; 660 | } 661 | 662 | uint8_t ctrl = readRegister(rxb->CTRL); 663 | if (ctrl & RXBnCTRL_RTR) { 664 | id |= CAN_RTR_FLAG; 665 | } 666 | 667 | frame->can_id = id; 668 | frame->can_dlc = dlc; 669 | 670 | readRegisters(rxb->DATA, frame->data, dlc); 671 | 672 | modifyRegister(MCP_CANINTF, rxb->CANINTF_RXnIF, 0); 673 | 674 | return ERROR_OK; 675 | } 676 | 677 | bool MCP2515::checkReceive(void) { 678 | uint8_t res = getStatus(); 679 | if ( res & STAT_RXIF_MASK ) { 680 | return true; 681 | } else { 682 | return false; 683 | } 684 | } 685 | 686 | bool MCP2515::checkError(void) { 687 | uint8_t eflg = getErrorFlags(); 688 | 689 | if ( eflg & EFLG_ERRORMASK ) { 690 | return true; 691 | } else { 692 | return false; 693 | } 694 | } 695 | 696 | uint8_t MCP2515::getErrorFlags(void) { 697 | return readRegister(MCP_EFLG); 698 | } 699 | 700 | void MCP2515::clearRXnOVRFlags(void) { 701 | modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0); 702 | } 703 | 704 | uint8_t MCP2515::getInterrupts(void) { 705 | return readRegister(MCP_CANINTF); 706 | } 707 | 708 | void MCP2515::clearInterrupts(void) { 709 | setRegister(MCP_CANINTF, 0); 710 | } 711 | 712 | uint8_t MCP2515::getInterruptMask(void) { 713 | return readRegister(MCP_CANINTE); 714 | } 715 | 716 | void MCP2515::clearTXInterrupts(void) { 717 | modifyRegister(MCP_CANINTF, (CANINTF_TX0IF | CANINTF_TX1IF | CANINTF_TX2IF), 0); 718 | } 719 | 720 | void MCP2515::clearRXnOVR(void) { 721 | uint8_t eflg = getErrorFlags(); 722 | if (eflg != 0) { 723 | clearRXnOVRFlags(); 724 | clearInterrupts(); 725 | //modifyRegister(MCP_CANINTF, CANINTF_ERRIF, 0); 726 | } 727 | } 728 | 729 | void MCP2515::clearMERR() { 730 | //modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0); 731 | //clearInterrupts(); 732 | modifyRegister(MCP_CANINTF, CANINTF_MERRF, 0); 733 | } 734 | 735 | void MCP2515::clearERRIF() { 736 | //modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0); 737 | //clearInterrupts(); 738 | modifyRegister(MCP_CANINTF, CANINTF_ERRIF, 0); 739 | } 740 | -------------------------------------------------------------------------------- /src/mcp2515.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MCP2515 CAN interface library 3 | * 4 | * Originally taken from https://github.com/autowp/arduino-mcp2515 5 | * 6 | * Modified by Peter Müller (https://crycode.de) for 7 | * MCP-CAN-Boot to use less flash space and work correctly within the 8 | * bootloader section. 9 | */ 10 | 11 | #ifndef _MCP2515_H_ 12 | #define _MCP2515_H_ 13 | 14 | #include 15 | #include "can.h" 16 | #include "config.h" 17 | #include "controllers.h" 18 | 19 | /* 20 | * Speed 8M 21 | */ 22 | #define MCP_8MHz_1000kBPS_CFG1 (0x00) 23 | #define MCP_8MHz_1000kBPS_CFG2 (0x80) 24 | #define MCP_8MHz_1000kBPS_CFG3 (0x80) 25 | 26 | #define MCP_8MHz_500kBPS_CFG1 (0x00) 27 | #define MCP_8MHz_500kBPS_CFG2 (0x90) 28 | #define MCP_8MHz_500kBPS_CFG3 (0x82) 29 | 30 | #define MCP_8MHz_250kBPS_CFG1 (0x00) 31 | #define MCP_8MHz_250kBPS_CFG2 (0xB1) 32 | #define MCP_8MHz_250kBPS_CFG3 (0x85) 33 | 34 | #define MCP_8MHz_200kBPS_CFG1 (0x00) 35 | #define MCP_8MHz_200kBPS_CFG2 (0xB4) 36 | #define MCP_8MHz_200kBPS_CFG3 (0x86) 37 | 38 | #define MCP_8MHz_125kBPS_CFG1 (0x01) 39 | #define MCP_8MHz_125kBPS_CFG2 (0xB1) 40 | #define MCP_8MHz_125kBPS_CFG3 (0x85) 41 | 42 | #define MCP_8MHz_100kBPS_CFG1 (0x01) 43 | #define MCP_8MHz_100kBPS_CFG2 (0xB4) 44 | #define MCP_8MHz_100kBPS_CFG3 (0x86) 45 | 46 | #define MCP_8MHz_80kBPS_CFG1 (0x01) 47 | #define MCP_8MHz_80kBPS_CFG2 (0xBF) 48 | #define MCP_8MHz_80kBPS_CFG3 (0x87) 49 | 50 | #define MCP_8MHz_50kBPS_CFG1 (0x03) 51 | #define MCP_8MHz_50kBPS_CFG2 (0xB4) 52 | #define MCP_8MHz_50kBPS_CFG3 (0x86) 53 | 54 | #define MCP_8MHz_40kBPS_CFG1 (0x03) 55 | #define MCP_8MHz_40kBPS_CFG2 (0xBF) 56 | #define MCP_8MHz_40kBPS_CFG3 (0x87) 57 | 58 | #define MCP_8MHz_33k3BPS_CFG1 (0x47) 59 | #define MCP_8MHz_33k3BPS_CFG2 (0xE2) 60 | #define MCP_8MHz_33k3BPS_CFG3 (0x85) 61 | 62 | #define MCP_8MHz_31k25BPS_CFG1 (0x07) 63 | #define MCP_8MHz_31k25BPS_CFG2 (0xA4) 64 | #define MCP_8MHz_31k25BPS_CFG3 (0x84) 65 | 66 | #define MCP_8MHz_20kBPS_CFG1 (0x07) 67 | #define MCP_8MHz_20kBPS_CFG2 (0xBF) 68 | #define MCP_8MHz_20kBPS_CFG3 (0x87) 69 | 70 | #define MCP_8MHz_10kBPS_CFG1 (0x0F) 71 | #define MCP_8MHz_10kBPS_CFG2 (0xBF) 72 | #define MCP_8MHz_10kBPS_CFG3 (0x87) 73 | 74 | #define MCP_8MHz_5kBPS_CFG1 (0x1F) 75 | #define MCP_8MHz_5kBPS_CFG2 (0xBF) 76 | #define MCP_8MHz_5kBPS_CFG3 (0x87) 77 | 78 | /* 79 | * speed 16M 80 | */ 81 | #define MCP_16MHz_1000kBPS_CFG1 (0x00) 82 | #define MCP_16MHz_1000kBPS_CFG2 (0xD0) 83 | #define MCP_16MHz_1000kBPS_CFG3 (0x82) 84 | 85 | #define MCP_16MHz_500kBPS_CFG1 (0x00) 86 | #define MCP_16MHz_500kBPS_CFG2 (0xF0) 87 | #define MCP_16MHz_500kBPS_CFG3 (0x86) 88 | 89 | #define MCP_16MHz_250kBPS_CFG1 (0x41) 90 | #define MCP_16MHz_250kBPS_CFG2 (0xF1) 91 | #define MCP_16MHz_250kBPS_CFG3 (0x85) 92 | 93 | #define MCP_16MHz_200kBPS_CFG1 (0x01) 94 | #define MCP_16MHz_200kBPS_CFG2 (0xFA) 95 | #define MCP_16MHz_200kBPS_CFG3 (0x87) 96 | 97 | #define MCP_16MHz_125kBPS_CFG1 (0x03) 98 | #define MCP_16MHz_125kBPS_CFG2 (0xF0) 99 | #define MCP_16MHz_125kBPS_CFG3 (0x86) 100 | 101 | #define MCP_16MHz_100kBPS_CFG1 (0x03) 102 | #define MCP_16MHz_100kBPS_CFG2 (0xFA) 103 | #define MCP_16MHz_100kBPS_CFG3 (0x87) 104 | 105 | #define MCP_16MHz_80kBPS_CFG1 (0x03) 106 | #define MCP_16MHz_80kBPS_CFG2 (0xFF) 107 | #define MCP_16MHz_80kBPS_CFG3 (0x87) 108 | 109 | #define MCP_16MHz_83k3BPS_CFG1 (0x03) 110 | #define MCP_16MHz_83k3BPS_CFG2 (0xBE) 111 | #define MCP_16MHz_83k3BPS_CFG3 (0x07) 112 | 113 | #define MCP_16MHz_50kBPS_CFG1 (0x07) 114 | #define MCP_16MHz_50kBPS_CFG2 (0xFA) 115 | #define MCP_16MHz_50kBPS_CFG3 (0x87) 116 | 117 | #define MCP_16MHz_40kBPS_CFG1 (0x07) 118 | #define MCP_16MHz_40kBPS_CFG2 (0xFF) 119 | #define MCP_16MHz_40kBPS_CFG3 (0x87) 120 | 121 | #define MCP_16MHz_33k3BPS_CFG1 (0x4E) 122 | #define MCP_16MHz_33k3BPS_CFG2 (0xF1) 123 | #define MCP_16MHz_33k3BPS_CFG3 (0x85) 124 | 125 | #define MCP_16MHz_20kBPS_CFG1 (0x0F) 126 | #define MCP_16MHz_20kBPS_CFG2 (0xFF) 127 | #define MCP_16MHz_20kBPS_CFG3 (0x87) 128 | 129 | #define MCP_16MHz_10kBPS_CFG1 (0x1F) 130 | #define MCP_16MHz_10kBPS_CFG2 (0xFF) 131 | #define MCP_16MHz_10kBPS_CFG3 (0x87) 132 | 133 | #define MCP_16MHz_5kBPS_CFG1 (0x3F) 134 | #define MCP_16MHz_5kBPS_CFG2 (0xFF) 135 | #define MCP_16MHz_5kBPS_CFG3 (0x87) 136 | 137 | /* 138 | * speed 20M 139 | */ 140 | #define MCP_20MHz_1000kBPS_CFG1 (0x00) 141 | #define MCP_20MHz_1000kBPS_CFG2 (0xD9) 142 | #define MCP_20MHz_1000kBPS_CFG3 (0x82) 143 | 144 | #define MCP_20MHz_500kBPS_CFG1 (0x00) 145 | #define MCP_20MHz_500kBPS_CFG2 (0xFA) 146 | #define MCP_20MHz_500kBPS_CFG3 (0x87) 147 | 148 | #define MCP_20MHz_250kBPS_CFG1 (0x41) 149 | #define MCP_20MHz_250kBPS_CFG2 (0xFB) 150 | #define MCP_20MHz_250kBPS_CFG3 (0x86) 151 | 152 | #define MCP_20MHz_200kBPS_CFG1 (0x01) 153 | #define MCP_20MHz_200kBPS_CFG2 (0xFF) 154 | #define MCP_20MHz_200kBPS_CFG3 (0x87) 155 | 156 | #define MCP_20MHz_125kBPS_CFG1 (0x03) 157 | #define MCP_20MHz_125kBPS_CFG2 (0xFA) 158 | #define MCP_20MHz_125kBPS_CFG3 (0x87) 159 | 160 | #define MCP_20MHz_100kBPS_CFG1 (0x04) 161 | #define MCP_20MHz_100kBPS_CFG2 (0xFA) 162 | #define MCP_20MHz_100kBPS_CFG3 (0x87) 163 | 164 | #define MCP_20MHz_83k3BPS_CFG1 (0x04) 165 | #define MCP_20MHz_83k3BPS_CFG2 (0xFE) 166 | #define MCP_20MHz_83k3BPS_CFG3 (0x87) 167 | 168 | #define MCP_20MHz_80kBPS_CFG1 (0x04) 169 | #define MCP_20MHz_80kBPS_CFG2 (0xFF) 170 | #define MCP_20MHz_80kBPS_CFG3 (0x87) 171 | 172 | #define MCP_20MHz_50kBPS_CFG1 (0x09) 173 | #define MCP_20MHz_50kBPS_CFG2 (0xFA) 174 | #define MCP_20MHz_50kBPS_CFG3 (0x87) 175 | 176 | #define MCP_20MHz_40kBPS_CFG1 (0x09) 177 | #define MCP_20MHz_40kBPS_CFG2 (0xFF) 178 | #define MCP_20MHz_40kBPS_CFG3 (0x87) 179 | 180 | #define MCP_20MHz_33k3BPS_CFG1 (0x0B) 181 | #define MCP_20MHz_33k3BPS_CFG2 (0xFF) 182 | #define MCP_20MHz_33k3BPS_CFG3 (0x87) 183 | 184 | enum CAN_CLOCK { 185 | MCP_20MHZ, 186 | MCP_16MHZ, 187 | MCP_8MHZ 188 | }; 189 | 190 | enum CAN_SPEED { 191 | CAN_5KBPS, 192 | CAN_10KBPS, 193 | CAN_20KBPS, 194 | CAN_31K25BPS, 195 | CAN_33KBPS, 196 | CAN_40KBPS, 197 | CAN_50KBPS, 198 | CAN_80KBPS, 199 | CAN_83K3BPS, 200 | CAN_95KBPS, 201 | CAN_100KBPS, 202 | CAN_125KBPS, 203 | CAN_200KBPS, 204 | CAN_250KBPS, 205 | CAN_500KBPS, 206 | CAN_1000KBPS 207 | }; 208 | 209 | enum CAN_CLKOUT { 210 | CLKOUT_DISABLE = -1, 211 | CLKOUT_DIV1 = 0x0, 212 | CLKOUT_DIV2 = 0x1, 213 | CLKOUT_DIV4 = 0x2, 214 | CLKOUT_DIV8 = 0x3, 215 | }; 216 | 217 | class MCP2515 218 | { 219 | public: 220 | enum ERROR { 221 | ERROR_OK = 0, 222 | ERROR_FAIL = 1, 223 | ERROR_ALLTXBUSY = 2, 224 | ERROR_FAILINIT = 3, 225 | ERROR_FAILTX = 4, 226 | ERROR_NOMSG = 5 227 | }; 228 | 229 | enum MASK { 230 | MASK0, 231 | MASK1 232 | }; 233 | 234 | enum RXF { 235 | RXF0 = 0, 236 | RXF1 = 1, 237 | RXF2 = 2, 238 | RXF3 = 3, 239 | RXF4 = 4, 240 | RXF5 = 5 241 | }; 242 | 243 | enum RXBn { 244 | RXB0 = 0, 245 | RXB1 = 1 246 | }; 247 | 248 | enum TXBn { 249 | TXB0 = 0, 250 | TXB1 = 1, 251 | TXB2 = 2 252 | }; 253 | 254 | enum /*class*/ CANINTF : uint8_t { 255 | CANINTF_RX0IF = 0x01, 256 | CANINTF_RX1IF = 0x02, 257 | CANINTF_TX0IF = 0x04, 258 | CANINTF_TX1IF = 0x08, 259 | CANINTF_TX2IF = 0x10, 260 | CANINTF_ERRIF = 0x20, 261 | CANINTF_WAKIF = 0x40, 262 | CANINTF_MERRF = 0x80 263 | }; 264 | 265 | enum /*class*/ EFLG : uint8_t { 266 | EFLG_RX1OVR = (1<<7), 267 | EFLG_RX0OVR = (1<<6), 268 | EFLG_TXBO = (1<<5), 269 | EFLG_TXEP = (1<<4), 270 | EFLG_RXEP = (1<<3), 271 | EFLG_TXWAR = (1<<2), 272 | EFLG_RXWAR = (1<<1), 273 | EFLG_EWARN = (1<<0) 274 | }; 275 | 276 | private: 277 | static const uint8_t CANCTRL_REQOP = 0xE0; 278 | static const uint8_t CANCTRL_ABAT = 0x10; 279 | static const uint8_t CANCTRL_OSM = 0x08; 280 | static const uint8_t CANCTRL_CLKEN = 0x04; 281 | static const uint8_t CANCTRL_CLKPRE = 0x03; 282 | 283 | enum /*class*/ CANCTRL_REQOP_MODE : uint8_t { 284 | CANCTRL_REQOP_NORMAL = 0x00, 285 | CANCTRL_REQOP_SLEEP = 0x20, 286 | CANCTRL_REQOP_LOOPBACK = 0x40, 287 | CANCTRL_REQOP_LISTENONLY = 0x60, 288 | CANCTRL_REQOP_CONFIG = 0x80, 289 | CANCTRL_REQOP_POWERUP = 0xE0 290 | }; 291 | 292 | static const uint8_t CANSTAT_OPMOD = 0xE0; 293 | static const uint8_t CANSTAT_ICOD = 0x0E; 294 | 295 | static const uint8_t CNF3_SOF = 0x80; 296 | 297 | static const uint8_t TXB_EXIDE_MASK = 0x08; 298 | static const uint8_t DLC_MASK = 0x0F; 299 | static const uint8_t RTR_MASK = 0x40; 300 | 301 | static const uint8_t RXBnCTRL_RXM_STD = 0x20; 302 | static const uint8_t RXBnCTRL_RXM_EXT = 0x40; 303 | static const uint8_t RXBnCTRL_RXM_STDEXT = 0x00; 304 | static const uint8_t RXBnCTRL_RXM_MASK = 0x60; 305 | static const uint8_t RXBnCTRL_RTR = 0x08; 306 | static const uint8_t RXB0CTRL_BUKT = 0x04; 307 | static const uint8_t RXB0CTRL_FILHIT_MASK = 0x03; 308 | static const uint8_t RXB1CTRL_FILHIT_MASK = 0x07; 309 | static const uint8_t RXB0CTRL_FILHIT = 0x00; 310 | static const uint8_t RXB1CTRL_FILHIT = 0x01; 311 | 312 | static const uint8_t MCP_SIDH = 0; 313 | static const uint8_t MCP_SIDL = 1; 314 | static const uint8_t MCP_EID8 = 2; 315 | static const uint8_t MCP_EID0 = 3; 316 | static const uint8_t MCP_DLC = 4; 317 | static const uint8_t MCP_DATA = 5; 318 | 319 | enum /*class*/ STAT : uint8_t { 320 | STAT_RX0IF = (1<<0), 321 | STAT_RX1IF = (1<<1) 322 | }; 323 | 324 | static const uint8_t STAT_RXIF_MASK = STAT_RX0IF | STAT_RX1IF; 325 | 326 | enum /*class*/ TXBnCTRL : uint8_t { 327 | TXB_ABTF = 0x40, 328 | TXB_MLOA = 0x20, 329 | TXB_TXERR = 0x10, 330 | TXB_TXREQ = 0x08, 331 | TXB_TXIE = 0x04, 332 | TXB_TXP = 0x03 333 | }; 334 | 335 | static const uint8_t EFLG_ERRORMASK = EFLG_RX1OVR 336 | | EFLG_RX0OVR 337 | | EFLG_TXBO 338 | | EFLG_TXEP 339 | | EFLG_RXEP; 340 | 341 | enum /*class*/ INSTRUCTION : uint8_t { 342 | INSTRUCTION_WRITE = 0x02, 343 | INSTRUCTION_READ = 0x03, 344 | INSTRUCTION_BITMOD = 0x05, 345 | INSTRUCTION_LOAD_TX0 = 0x40, 346 | INSTRUCTION_LOAD_TX1 = 0x42, 347 | INSTRUCTION_LOAD_TX2 = 0x44, 348 | INSTRUCTION_RTS_TX0 = 0x81, 349 | INSTRUCTION_RTS_TX1 = 0x82, 350 | INSTRUCTION_RTS_TX2 = 0x84, 351 | INSTRUCTION_RTS_ALL = 0x87, 352 | INSTRUCTION_READ_RX0 = 0x90, 353 | INSTRUCTION_READ_RX1 = 0x94, 354 | INSTRUCTION_READ_STATUS = 0xA0, 355 | INSTRUCTION_RX_STATUS = 0xB0, 356 | INSTRUCTION_RESET = 0xC0 357 | }; 358 | 359 | enum /*class*/ REGISTER : uint8_t { 360 | MCP_RXF0SIDH = 0x00, 361 | MCP_RXF0SIDL = 0x01, 362 | MCP_RXF0EID8 = 0x02, 363 | MCP_RXF0EID0 = 0x03, 364 | MCP_RXF1SIDH = 0x04, 365 | MCP_RXF1SIDL = 0x05, 366 | MCP_RXF1EID8 = 0x06, 367 | MCP_RXF1EID0 = 0x07, 368 | MCP_RXF2SIDH = 0x08, 369 | MCP_RXF2SIDL = 0x09, 370 | MCP_RXF2EID8 = 0x0A, 371 | MCP_RXF2EID0 = 0x0B, 372 | MCP_CANSTAT = 0x0E, 373 | MCP_CANCTRL = 0x0F, 374 | MCP_RXF3SIDH = 0x10, 375 | MCP_RXF3SIDL = 0x11, 376 | MCP_RXF3EID8 = 0x12, 377 | MCP_RXF3EID0 = 0x13, 378 | MCP_RXF4SIDH = 0x14, 379 | MCP_RXF4SIDL = 0x15, 380 | MCP_RXF4EID8 = 0x16, 381 | MCP_RXF4EID0 = 0x17, 382 | MCP_RXF5SIDH = 0x18, 383 | MCP_RXF5SIDL = 0x19, 384 | MCP_RXF5EID8 = 0x1A, 385 | MCP_RXF5EID0 = 0x1B, 386 | MCP_TEC = 0x1C, 387 | MCP_REC = 0x1D, 388 | MCP_RXM0SIDH = 0x20, 389 | MCP_RXM0SIDL = 0x21, 390 | MCP_RXM0EID8 = 0x22, 391 | MCP_RXM0EID0 = 0x23, 392 | MCP_RXM1SIDH = 0x24, 393 | MCP_RXM1SIDL = 0x25, 394 | MCP_RXM1EID8 = 0x26, 395 | MCP_RXM1EID0 = 0x27, 396 | MCP_CNF3 = 0x28, 397 | MCP_CNF2 = 0x29, 398 | MCP_CNF1 = 0x2A, 399 | MCP_CANINTE = 0x2B, 400 | MCP_CANINTF = 0x2C, 401 | MCP_EFLG = 0x2D, 402 | MCP_TXB0CTRL = 0x30, 403 | MCP_TXB0SIDH = 0x31, 404 | MCP_TXB0SIDL = 0x32, 405 | MCP_TXB0EID8 = 0x33, 406 | MCP_TXB0EID0 = 0x34, 407 | MCP_TXB0DLC = 0x35, 408 | MCP_TXB0DATA = 0x36, 409 | MCP_TXB1CTRL = 0x40, 410 | MCP_TXB1SIDH = 0x41, 411 | MCP_TXB1SIDL = 0x42, 412 | MCP_TXB1EID8 = 0x43, 413 | MCP_TXB1EID0 = 0x44, 414 | MCP_TXB1DLC = 0x45, 415 | MCP_TXB1DATA = 0x46, 416 | MCP_TXB2CTRL = 0x50, 417 | MCP_TXB2SIDH = 0x51, 418 | MCP_TXB2SIDL = 0x52, 419 | MCP_TXB2EID8 = 0x53, 420 | MCP_TXB2EID0 = 0x54, 421 | MCP_TXB2DLC = 0x55, 422 | MCP_TXB2DATA = 0x56, 423 | MCP_RXB0CTRL = 0x60, 424 | MCP_RXB0SIDH = 0x61, 425 | MCP_RXB0SIDL = 0x62, 426 | MCP_RXB0EID8 = 0x63, 427 | MCP_RXB0EID0 = 0x64, 428 | MCP_RXB0DLC = 0x65, 429 | MCP_RXB0DATA = 0x66, 430 | MCP_RXB1CTRL = 0x70, 431 | MCP_RXB1SIDH = 0x71, 432 | MCP_RXB1SIDL = 0x72, 433 | MCP_RXB1EID8 = 0x73, 434 | MCP_RXB1EID0 = 0x74, 435 | MCP_RXB1DLC = 0x75, 436 | MCP_RXB1DATA = 0x76 437 | }; 438 | 439 | static const uint32_t SPI_CLOCK = 10000000; // 10MHz 440 | 441 | static const int N_TXBUFFERS = 3; 442 | static const int N_RXBUFFERS = 2; 443 | 444 | static const struct TXBn_REGS { 445 | REGISTER CTRL; 446 | REGISTER SIDH; 447 | REGISTER DATA; 448 | } TXB[N_TXBUFFERS]; 449 | 450 | static const struct RXBn_REGS { 451 | REGISTER CTRL; 452 | REGISTER SIDH; 453 | REGISTER DATA; 454 | CANINTF CANINTF_RXnIF; 455 | } RXB[N_RXBUFFERS]; 456 | 457 | private: 458 | 459 | void startSPI(); 460 | void endSPI(); 461 | uint8_t transfer(uint8_t data); 462 | 463 | ERROR setMode(const CANCTRL_REQOP_MODE mode); 464 | 465 | uint8_t readRegister(const REGISTER reg); 466 | void readRegisters(const REGISTER reg, uint8_t values[], const uint8_t n); 467 | void setRegister(const REGISTER reg, const uint8_t value); 468 | void setRegisters(const REGISTER reg, const uint8_t values[], const uint8_t n); 469 | void modifyRegister(const REGISTER reg, const uint8_t mask, const uint8_t data); 470 | 471 | void prepareId(uint8_t *buffer, const bool ext, const uint32_t id); 472 | 473 | public: 474 | MCP2515(); 475 | void init(); 476 | ERROR reset(void); 477 | ERROR setConfigMode(); 478 | ERROR setListenOnlyMode(); 479 | ERROR setSleepMode(); 480 | ERROR setLoopbackMode(); 481 | ERROR setNormalMode(); 482 | ERROR setClkOut(const CAN_CLKOUT divisor); 483 | ERROR setBitrate(const CAN_SPEED canSpeed); 484 | ERROR setBitrate(const CAN_SPEED canSpeed, const CAN_CLOCK canClock); 485 | ERROR setFilterMask(const MASK num, const bool ext, const uint32_t ulData); 486 | ERROR setFilter(const RXF num, const bool ext, const uint32_t ulData); 487 | ERROR sendMessage(const struct can_frame *frame); 488 | ERROR readMessage(struct can_frame *frame); 489 | bool checkReceive(void); 490 | bool checkError(void); 491 | uint8_t getErrorFlags(void); 492 | void clearRXnOVRFlags(void); 493 | uint8_t getInterrupts(void); 494 | uint8_t getInterruptMask(void); 495 | void clearInterrupts(void); 496 | void clearTXInterrupts(void); 497 | uint8_t getStatus(void); 498 | void clearRXnOVR(void); 499 | void clearMERR(); 500 | void clearERRIF(); 501 | }; 502 | 503 | #endif 504 | --------------------------------------------------------------------------------