├── .gitmodules ├── README.md ├── cmake └── version.txt ├── common ├── CMakeLists.txt ├── include │ └── common │ │ ├── crc8.h │ │ ├── protocol.h │ │ └── protocol │ │ ├── packet.h │ │ ├── request.h │ │ └── response.h └── src │ └── common │ ├── crc8.c │ └── protocol │ ├── common.c │ ├── common.h │ ├── packet.c │ ├── request.c │ └── response.c ├── firmware ├── arduino │ ├── CMakeLists.txt │ ├── build_dist.sh │ ├── burn.sh │ ├── dist │ │ ├── firmware_16mhz_19200bps.hex │ │ ├── firmware_16mhz_38400bps.hex │ │ ├── firmware_16mhz_500000bps.hex │ │ ├── firmware_8mhz_19200bps.hex │ │ ├── firmware_8mhz_38400bps.hex │ │ └── firmware_8mhz_500000bps.hex │ └── src │ │ └── main.c ├── common │ ├── CMakeLists.txt │ ├── include │ │ └── firmware │ │ │ └── programmer.h │ └── src │ │ └── firmware │ │ └── programmer.c └── rpi-pico │ ├── CMakeLists.txt │ ├── README.md │ ├── cmake │ └── pico_sdk_import.cmake │ ├── dist │ └── firmware.uf2 │ ├── include │ └── tusb_config.h │ └── src │ ├── main.c │ └── usb_descriptors.c ├── flashutil ├── CMakeLists.txt ├── etc │ └── chips.json ├── include │ └── flashutil │ │ ├── debug.h │ │ ├── entryPoint.h │ │ ├── exception.h │ │ ├── flash.h │ │ ├── flash │ │ ├── builder.h │ │ ├── registry.h │ │ ├── registry │ │ │ ├── reader.h │ │ │ └── reader │ │ │ │ └── json.h │ │ └── status.h │ │ ├── programmer.h │ │ ├── serial.h │ │ ├── serial │ │ └── hw.h │ │ ├── spi.h │ │ └── spi │ │ └── serial.h ├── src │ └── flashutil │ │ ├── debug.c │ │ ├── entryPoint.cpp │ │ ├── flash.cpp │ │ ├── flash │ │ ├── builder.cpp │ │ ├── registry.cpp │ │ ├── registry │ │ │ └── reader │ │ │ │ └── json.cpp │ │ └── status.cpp │ │ ├── main.cpp │ │ ├── programmer.cpp │ │ ├── serial │ │ └── hw.cpp │ │ ├── spi.cpp │ │ └── spi │ │ └── serial.cpp └── test │ └── test.sh ├── recipes ├── firmware │ ├── arduino │ │ └── CMakeLists.txt │ └── rpi_pico │ │ └── CMakeLists.txt ├── flashutil │ └── CMakeLists.txt └── tests │ └── CMakeLists.txt └── test ├── CMakeLists.txt └── src ├── common └── protocol │ ├── packet.cpp │ ├── request.cpp │ ├── response.cpp │ └── stack.cpp ├── firmware └── programmer.cpp └── flashutil ├── programmer.cpp ├── serialProgrammer.cpp └── serialProgrammer.h /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "firmware/rpi-zero/pico-sdk"] 2 | path = firmware/rpi-pico/pico-sdk 3 | url = https://github.com/raspberrypi/pico-sdk.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPI flash programmer. 2 | 3 | This project is an another SPI flash programmer. I need a SPI flash programmer to burn a flash chip to repair LCD monitor. As I hadn't any programmer I decided to make one by my own using Arduino nano. 4 | 5 | As a result a complete solution was implemented. 6 | 7 | ## Features. 8 | * supported modules: 9 | * Arduino nano compatible 10 | * RPi Pico (RP2040) 11 | * universal SPI flash programmer 12 | * Predefined chips: 13 | * MX25L2026E 14 | * MX25l2005A 15 | * MX25V16066 16 | * W25Q32 17 | * W25Q80 18 | * Generic chips 19 | * easy configurable with ``--flash-geometry`` parameter 20 | * easy configurable via ``chips.json`` 21 | * unprotect operation 22 | * erase/write verification 23 | * communication protocol secured by CRC checksum 24 | * high-speed SPI 25 | * 8MHz - Arduino 26 | * 60MHz - RPi Pico 27 | * high-speed USART/CDC 28 | * 0.5Mbaud - Arduino 29 | * 4Mbaud - RPi Pico 30 | 31 | ## Arduino 32 | 33 | ### Arduino connections. 34 | * D10 (PB2) ------ CS (chip select) 35 | * D11 (PB3) ------ MOSI (SPI data output) 36 | * D12 (PB4) ------ MISO (SPI data input) 37 | * D13 (PB5) ------ SCK (SPI clock) 38 | 39 | **NOTE** For communication with 3.3V chips simple resistor voltage divider can be used. In my case voltage divider using 1K and 2K resistors was enough. 40 | 41 | ## Flash connection. 42 | * WE (write enable) pin should be connected to ground 43 | 44 | ## Building and writing Arduino firmware. 45 | ``` 46 | mkdir build && cd build 47 | cmake -S ../recipes/firmware/arduino/ -B . 48 | make all upload_firmware 49 | ``` 50 | 51 | ## Writing precompiled images. 52 | 53 | **NOTE** Prebuilt firmware is available at ``firmware/arduino/dist/firmware__.hex`` 54 | 55 | ``` 56 | cd firmware/arduino 57 | ./burn.sh -m atmega328p -f dist/firmware_16mhz_19200bps.hex 58 | ``` 59 | 60 | By default, **burn.sh** scripts writes the firmware using **avrdude** via **arduino** programmer (arduino resistant bootloader). Programmer type, MCU and optional communication baudrate can be adjusted for particular hardware configuration. Please find the following syntax of burn.sh script: 61 | 62 | ``` 63 | Usage: 64 | ./burn.sh <-m mcu> [-P programmer] [-p port] [-b baudrate] [-m mcu] [-L lfuse] [-H hfuse] [-E efuse] [-f flash.hex] [-e e2prom.hex] 65 | ``` 66 | 67 | ## RPi-Pico 68 | 69 | ### Linux Environment preparation. 70 | 71 | ``` 72 | sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib 73 | ``` 74 | 75 | ### Building and writing firmware. 76 | 77 | ``` 78 | mkdir build; cd build 79 | cmake -S ../recipes/firmware/rpi_pico/ 80 | make 81 | ``` 82 | 83 | ### RPi-Pico connections. 84 | * 21 (GP16) ------ MISO (SPI data input) 85 | * 22 (GP17) ------ CS (chip select) 86 | * 24 (GP18) ------ SCK (SPI clock) 87 | * 25 (GP19) ------ MOSI (SPI data output) 88 | 89 | 90 | ## Building flashutil. 91 | ``` 92 | mkdir build && cd build 93 | cmake ../recipes/flashutil/ 94 | make all 95 | ``` 96 | 97 | ### Flash chips repository. 98 | There is a predefined list of flash chips added to this project at ``flashutil/etc/chips.json``. It contains declaractions (geomtry) of all chips mentioned in the 'Features' section. 99 | Other custom chips can be added by analogy without adding ``-g`` option to ``flash-util`` call. 100 | 101 | All size-related values are string values and optionally support binary metric modifiers such as ``Kib``, ``Mib``, ``Gib`` (representing kibibit, mebibit, gigibit), as well as ``KiB``, ``MiB``, ``GiB`` (representing kibibyte, mebibyte, gigibyte). 102 | 103 | ## Use cases 104 | * Print help and exit 105 | ``` 106 | flash-util -h 107 | ``` 108 | * Erase whole chip (without verification) 109 | ``` 110 | flash-util -s /dev/ttyUSB0 -R ../flashutil/etc/chips.json -e 111 | ``` 112 | * Erase whole chip (with verification) 113 | ``` 114 | flash-util -s /dev/ttyUSB0 -R ../flashutil/etc/chips.json -e -V 115 | ``` 116 | * Write image to chip (with verification) 117 | ``` 118 | flash-util -s /dev/ttyUSB0 -R ../flashutil/etc/chips.json -w -i /tmp/flash.src.bin -V 119 | ``` 120 | * Read whole chip 121 | ``` 122 | flash-util -s /dev/ttyUSB0 -R ../flashutil/etc/chips.json -r -o /tmp/flash.dst.bin 123 | ``` 124 | * Writing image to unknown chip 125 | ``` 126 | flash-util -s /dev/ttyUSB0 -e -w -i /tmp/flash.src.bin -V --flash-geometry 65536:64:4096:1024:fc 127 | ``` -------------------------------------------------------------------------------- /cmake/version.txt: -------------------------------------------------------------------------------- 1 | set(VERSION_MAJOR 1) 2 | set(VERSION_MINOR 0) 3 | set(VERSION_PATCH 0) -------------------------------------------------------------------------------- /common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(protocol) 3 | 4 | set(headers_path "${CMAKE_CURRENT_LIST_DIR}/include/") 5 | set(headers 6 | ${headers_path}/common/crc8.h 7 | ${headers_path}/common/protocol.h 8 | ${headers_path}/common/protocol/packet.h 9 | ${headers_path}/common/protocol/request.h 10 | ${headers_path}/common/protocol/response.h 11 | ) 12 | 13 | set(src_path "${CMAKE_CURRENT_LIST_DIR}/src/common") 14 | set(srcs 15 | ${src_path}/protocol/common.c 16 | ${src_path}/protocol/packet.c 17 | ${src_path}/protocol/request.c 18 | ${src_path}/protocol/response.c 19 | ${src_path}/crc8.c 20 | ) 21 | 22 | add_library(protocol 23 | ${headers} ${srcs} 24 | ) 25 | 26 | target_include_directories(protocol 27 | PUBLIC 28 | ${headers_path} 29 | ) -------------------------------------------------------------------------------- /common/include/common/crc8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common/crc8.h 3 | * 4 | * Created on: 27 lut 2022 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef COMMON_INCLUDE_CRC8_H_ 9 | #define COMMON_INCLUDE_CRC8_H_ 10 | 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | uint8_t crc8_getForByte(uint8_t byte, uint8_t polynomial, uint8_t start); 18 | 19 | uint8_t crc8_get(uint8_t *buffer, uint16_t bufferSize, uint8_t polynomial, uint8_t start); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif /* COMMON_INCLUDE_CRC8_H_ */ 26 | -------------------------------------------------------------------------------- /common/include/common/protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common/protocol.h 3 | * 4 | * Created on: 27 lut 2022 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FIRMWARE_INCLUDE_PROTOCOL_H_ 9 | #define FIRMWARE_INCLUDE_PROTOCOL_H_ 10 | 11 | #define PROTO_VERSION_MAJOR 1 12 | #define PROTO_VERSION_MINOR 0 13 | 14 | /* 15 | * CRC8 start value and polynomial definition. 16 | */ 17 | #define PROTO_CRC8_POLY 0xAB 18 | #define PROTO_CRC8_START 0x00 19 | 20 | #define PROTO_SYNC_NIBBLE_MASK 0xf0 21 | #define PROTO_SYNC_NIBBLE 0xd0 22 | 23 | #define PROTO_CMD_NIBBLE_MASK 0x07 24 | 25 | #define PROTO_FRAME_MIN_SIZE 5 26 | 27 | /* 28 | * VLEN field definition. 29 | * 30 | * VLEN is a variable length field containing unsigned integer value. It is: 31 | * - 1B length if the MSB bit is cleared. Allowed integer range is 0 - 127 32 | * - 2B length if the MSB bit is set. Allowed integer range is 0 - 32767 33 | */ 34 | 35 | /* 36 | * 1) Protocol frame. 37 | * 38 | * [ 8b ][ 1B ][ 1/2B ][ VLEN ] 39 | * [ 4b ][ 4b ][ ][ ][ ][ ][ ] 40 | * [ SYNC ][ CTRL ][ ID ][ VLEN ][ PLD ][...][ CRC8 ] 41 | * 42 | * SYNC: Synchronization byte. Always 0xd. 43 | * CTRL: [R...] command (request) or error code (response) MSB bit is reserved for future use. Should be set to 0. 44 | * ID: Command unique identifier. Used to recognize response frame. 45 | * VLEN: Length of payload data (does not include CRC8 field) 46 | * PLD: Frame payload 47 | * CRC8: Checksum of overall frame 48 | */ 49 | 50 | /* 51 | * 2) CMD_GET_INFO. 52 | * 53 | * This commands returns the following information: 54 | * - protocol version 55 | * - maximal payload size supported by protocol packet 56 | * 57 | * Request payload: 58 | * - No payload 59 | * 60 | * Response payload: 61 | * [ 4b ][ 4b ][ 1/2B ] 62 | * [ VER_MAJ ][ VER_MIN ][ PLD_SIZE ] 63 | */ 64 | #define PROTO_CMD_GET_INFO 0x0 65 | 66 | 67 | #define PROTO_SPI_TRANSFER_FLAG_KEEP_CS (1 << 0) 68 | 69 | /* 70 | * 3) CMD_SPI_TRANSFER 71 | * 72 | * [ 1B ][ 1/2B ][ TX_SIZE ][ 1/2B ][ 1/2B ] 73 | * [ FLAGS ][ TX_SIZE ][ TX_DATA ][ ... ][ RX_SKIP_SIZE ][ RX_SIZE ] 74 | */ 75 | #define PROTO_CMD_SPI_TRANSFER 0x1 76 | 77 | /* 78 | * Error codes. 79 | */ 80 | #define PROTO_NO_ERROR 0x00 81 | #define PROTO_ERROR_INVALID_CMD 0x01 82 | #define PROTO_ERROR_INVALID_LENGTH 0x02 83 | #define PROTO_ERROR_INVALID_CRC 0x03 84 | #define PROTO_ERROR_INVALID_PAYLOAD 0x04 85 | #define PROTO_ERROR_INVALID_MESSAGE 0x05 86 | 87 | #endif /* FIRMWARE_INCLUDE_PROTOCOL_H_ */ 88 | -------------------------------------------------------------------------------- /common/include/common/protocol/packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common/protocol/packet.h 3 | * 4 | * Created on: 23 lip 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef COMMON_PROTOCOL_PACKET_H_ 9 | #define COMMON_PROTOCOL_PACKET_H_ 10 | 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #define PROTO_PKT_DES_RET_IDLE 0 19 | #define PROTO_PKT_DES_RET_GET_ERROR_CODE(_v)((_v) & 0x7F) 20 | #define PROTO_PKT_DES_RET_SET_ERROR_CODE(_v)((_v) | 0x80) 21 | 22 | typedef struct _ProtoPkt { 23 | uint8_t code; 24 | uint8_t id; 25 | 26 | uint8_t *payload; 27 | uint16_t payloadSize; 28 | } ProtoPkt; 29 | 30 | 31 | typedef struct _ProtoPktDesCtx { 32 | uint8_t state; 33 | 34 | uint8_t id; 35 | uint8_t code; 36 | uint8_t crc; 37 | 38 | uint16_t dataSize; 39 | uint16_t dataRead; 40 | 41 | uint8_t *mem; 42 | uint16_t memSize; 43 | } ProtoPktDes; 44 | 45 | 46 | void proto_pkt_init (ProtoPkt *pkt, void *mem, uint16_t memSize, uint8_t code, uint8_t id); 47 | bool proto_pkt_prepare(ProtoPkt *pkt, void *mem, uint16_t memSize, uint16_t payloadSize); 48 | uint16_t proto_pkt_encode (ProtoPkt *pkt, void *mem, uint16_t memSize); 49 | 50 | void proto_pkt_dec_setup(ProtoPktDes *ctx, uint8_t *buffer, uint16_t bufferSize); 51 | 52 | uint8_t proto_pkt_dec_putByte(ProtoPktDes *ctx, uint8_t byte, ProtoPkt *pkt); 53 | 54 | void proto_pkt_dec_reset(ProtoPktDes *ctx); 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | 60 | #endif /* COMMON_PROTOCOL_PACKET_H_ */ 61 | -------------------------------------------------------------------------------- /common/include/common/protocol/request.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common/protocol/request.h 3 | * 4 | * Created on: 23 lip 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef COMMON_PROTOCOL_REQUEST_H_ 9 | #define COMMON_PROTOCOL_REQUEST_H_ 10 | 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | typedef struct _ProtoReqGetInfo { 19 | 20 | } ProtoReqGetInfo; 21 | 22 | 23 | typedef struct _ProtoReqTransfer { 24 | uint8_t *txBuffer; 25 | uint16_t txBufferSize; 26 | 27 | uint16_t rxBufferSize; 28 | uint16_t rxSkipSize; 29 | 30 | uint8_t flags; 31 | } ProtoReqTransfer; 32 | 33 | 34 | typedef struct _ProtoReq { 35 | uint8_t cmd; 36 | 37 | union { 38 | ProtoReqGetInfo getInfo; 39 | ProtoReqTransfer transfer; 40 | } request; 41 | } ProtoReq; 42 | 43 | 44 | void proto_req_init (ProtoReq *request, void *memory, uint16_t memorySize, uint8_t cmd); 45 | void proto_req_assign(ProtoReq *request, void *memory, uint16_t memorySize); 46 | uint16_t proto_req_encode(ProtoReq *request, void *memory, uint16_t memorySize); 47 | uint16_t proto_req_decode(ProtoReq *request, void *memory, uint16_t memorySize); 48 | 49 | uint16_t proto_req_getPayloadSize(ProtoReq *request); 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | 55 | #endif /* COMMON_PROTOCOL_REQUEST_H_ */ 56 | -------------------------------------------------------------------------------- /common/include/common/protocol/response.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common/protocol/response.h 3 | * 4 | * Created on: 23 lip 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef COMMON_PROTOCOL_RESPONSE_H_ 9 | #define COMMON_PROTOCOL_RESPONSE_H_ 10 | 11 | #include 12 | #include 13 | 14 | #include "common/protocol/packet.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct _ProtoResError { 21 | uint8_t code; 22 | } ProtoResError; 23 | 24 | 25 | typedef struct _ProtoResGetInfo { 26 | /// Protocol version 27 | struct { 28 | uint8_t major; 29 | uint8_t minor; 30 | } version; 31 | 32 | /// Maximal supported size of packet. 33 | uint16_t packetSize; 34 | } ProtoResGetInfo; 35 | 36 | 37 | typedef struct _ProtoResTransfer { 38 | uint8_t *rxBuffer; 39 | uint16_t rxBufferSize; 40 | } ProtoResTransfer; 41 | 42 | 43 | typedef struct _ProtoRes { 44 | uint8_t cmd; 45 | 46 | union { 47 | ProtoResGetInfo getInfo; 48 | ProtoResTransfer transfer; 49 | } response; 50 | } ProtoRes; 51 | 52 | 53 | void proto_res_init (ProtoRes *response, void *memory, uint16_t memorySize, uint8_t cmd); 54 | void proto_res_assign(ProtoRes *response, void *memory, uint16_t memorySize); 55 | uint16_t proto_res_encode(ProtoRes *response, void *memory, uint16_t memorySize); 56 | uint16_t proto_res_decode(ProtoRes *response, void *memory, uint16_t memorySize); 57 | 58 | uint16_t proto_res_getPayloadSize(ProtoRes *response); 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif /* COMMON_PROTOCOL_RESPONSE_H_ */ 65 | -------------------------------------------------------------------------------- /common/src/common/crc8.c: -------------------------------------------------------------------------------- 1 | #include "common/crc8.h" 2 | 3 | 4 | uint8_t crc8_getForByte(uint8_t byte, uint8_t polynomial, uint8_t start) { 5 | uint8_t remainder = start; 6 | 7 | remainder ^= byte; 8 | 9 | { 10 | uint8_t bit; 11 | 12 | for (bit = 0; bit < 8; bit++) { 13 | if (remainder & 0x01) { 14 | remainder = (remainder >> 1) ^ polynomial; 15 | 16 | } else { 17 | remainder = (remainder >> 1); 18 | } 19 | 20 | } 21 | } 22 | 23 | return remainder; 24 | } 25 | 26 | 27 | uint8_t crc8_get(uint8_t *buffer, uint16_t bufferSize, uint8_t polynomial, uint8_t start) { 28 | uint8_t remainder = start; 29 | uint16_t byte; 30 | 31 | // Perform modulo-2 division, a byte at a time. 32 | for (byte = 0; byte < bufferSize; ++byte) { 33 | remainder = crc8_getForByte(buffer[byte], polynomial, remainder); 34 | } 35 | 36 | return remainder; 37 | } 38 | -------------------------------------------------------------------------------- /common/src/common/protocol/common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | 4 | uint8_t proto_int_val_length_estimate(uint16_t val) { 5 | if (val > 127) { 6 | return 2; 7 | } 8 | 9 | return 1; 10 | } 11 | 12 | 13 | uint8_t proto_int_val_length_probe(uint8_t byte) { 14 | if (byte & 0x80) { 15 | return 2; 16 | } 17 | 18 | return 1; 19 | } 20 | 21 | 22 | uint16_t proto_int_val_decode(uint8_t val[2]) { 23 | uint16_t ret = val[0]; 24 | 25 | if (proto_int_val_length_probe(ret) == 2) { 26 | ret = ((uint16_t)(ret & 0x7f) << 8) | val[1]; 27 | } 28 | 29 | return ret; 30 | } 31 | 32 | 33 | uint8_t proto_int_val_encode(uint16_t len, uint8_t val[2]) { 34 | uint8_t ret = proto_int_val_length_estimate(len); 35 | 36 | if (ret == 1) { 37 | val[0] = len; 38 | 39 | } else { 40 | val[0] = 0x80 | (len >> 8); 41 | val[1] = len & 0xff; 42 | } 43 | 44 | return ret; 45 | } 46 | -------------------------------------------------------------------------------- /common/src/common/protocol/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common/protocol/common.h 3 | * 4 | * Created on: 23 lip 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | 9 | #ifndef COMMON_PROTOCOL_COMMON_H_ 10 | #define COMMON_PROTOCOL_COMMON_H_ 11 | 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #define PROTO_INT_VAL_MAX 0x7fff 19 | 20 | uint8_t proto_int_val_length_estimate(uint16_t val); 21 | 22 | uint8_t proto_int_val_length_probe(uint8_t byte); 23 | 24 | uint16_t proto_int_val_decode(uint8_t val[2]); 25 | 26 | uint8_t proto_int_val_encode(uint16_t len, uint8_t val[2]); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif /* COMMON_PROTOCOL_COMMON_H_ */ 33 | -------------------------------------------------------------------------------- /common/src/common/protocol/packet.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common/protocol.h" 5 | #include "common/protocol/packet.h" 6 | #include "common/crc8.h" 7 | 8 | #include "common.h" 9 | 10 | #define CMD_UNKNOWN 0 11 | #define ID_UNKNOWN 0 12 | 13 | 14 | typedef enum _State { 15 | STATE_WAIT_SYNC, 16 | STATE_ID, 17 | STATE_VLEN_HI, 18 | STATE_VLEN_LO, 19 | STATE_CHECK_PAYLOAD, 20 | STATE_PAYLOAD, 21 | STATE_CRC, 22 | STATE_CMD_RDY 23 | } State; 24 | 25 | 26 | uint16_t _getMaxPayloadSize(uint16_t memSize) { 27 | uint8_t overhead = 3; 28 | 29 | if (memSize <= overhead) { 30 | return 0; 31 | } 32 | 33 | if (proto_int_val_length_estimate(memSize - overhead) == 2) { 34 | overhead++; 35 | } 36 | 37 | return memSize - overhead; 38 | } 39 | 40 | 41 | void proto_pkt_init(ProtoPkt *pkt, void *mem, uint16_t memSize, uint8_t code, uint8_t id) { 42 | pkt->code = code; 43 | pkt->id = id; 44 | 45 | pkt->payloadSize = _getMaxPayloadSize(memSize); 46 | pkt->payload = NULL; 47 | } 48 | 49 | 50 | bool proto_pkt_prepare(ProtoPkt *pkt, void *mem, uint16_t memSize, uint16_t payloadSize) { 51 | bool ret = true; 52 | 53 | if (payloadSize != pkt->payloadSize) { 54 | if (payloadSize) { 55 | if (payloadSize > _getMaxPayloadSize(memSize)) { 56 | ret = false; 57 | 58 | } else { 59 | pkt->payloadSize = payloadSize; 60 | pkt->payload = ((uint8_t *) mem) + 2 + proto_int_val_length_estimate(pkt->payloadSize); 61 | } 62 | 63 | } else { 64 | pkt->payloadSize = 0; 65 | pkt->payload = NULL; 66 | } 67 | } 68 | 69 | return ret; 70 | } 71 | 72 | 73 | uint16_t proto_pkt_encode(ProtoPkt *pkt, void *mem, uint16_t memSize) { 74 | uint16_t ret = 0; 75 | 76 | { 77 | uint8_t *buff = (uint8_t *) mem; 78 | 79 | buff[ret++] = PROTO_SYNC_NIBBLE | pkt->code; 80 | buff[ret++] = pkt->id; 81 | 82 | ret += proto_int_val_encode(pkt->payloadSize, buff + ret); 83 | ret += pkt->payloadSize; 84 | 85 | buff[ret] = crc8_get(buff, ret, PROTO_CRC8_POLY, PROTO_CRC8_START); 86 | }; 87 | 88 | return ret + 1; 89 | } 90 | 91 | 92 | void proto_pkt_dec_setup(ProtoPktDes *ctx, uint8_t *buffer, uint16_t bufferSize) { 93 | ctx->mem = buffer; 94 | ctx->memSize = bufferSize; 95 | 96 | proto_pkt_dec_reset(ctx); 97 | } 98 | 99 | 100 | uint8_t proto_pkt_dec_putByte(ProtoPktDes *ctx, uint8_t byte, ProtoPkt *request) { 101 | uint8_t error = PROTO_NO_ERROR; 102 | 103 | switch (ctx->state) { 104 | case STATE_WAIT_SYNC: 105 | { 106 | if ((byte & PROTO_SYNC_NIBBLE_MASK) == PROTO_SYNC_NIBBLE) { 107 | ctx->code = PROTO_CMD_NIBBLE_MASK & byte; 108 | ctx->state = STATE_ID; 109 | ctx->crc = crc8_getForByte(byte, PROTO_CRC8_POLY, PROTO_CRC8_START); 110 | } 111 | } 112 | break; 113 | 114 | case STATE_ID: 115 | { 116 | ctx->id = byte; 117 | ctx->state = STATE_VLEN_HI; 118 | ctx->crc = crc8_getForByte(byte, PROTO_CRC8_POLY, ctx->crc); 119 | } 120 | break; 121 | 122 | case STATE_VLEN_HI: 123 | { 124 | if (proto_int_val_length_probe(byte) == 1) { 125 | ctx->dataSize = byte; 126 | 127 | if (ctx->dataSize == 0) { 128 | ctx->state = STATE_CRC; 129 | 130 | } else { 131 | ctx->state = STATE_CHECK_PAYLOAD; 132 | } 133 | 134 | } else { 135 | ctx->mem[0] = byte; 136 | 137 | ctx->state = STATE_VLEN_LO; 138 | } 139 | 140 | ctx->crc = crc8_getForByte(byte, PROTO_CRC8_POLY, ctx->crc); 141 | } 142 | break; 143 | 144 | case STATE_VLEN_LO: 145 | { 146 | ctx->mem[1] = byte; 147 | 148 | ctx->dataSize = proto_int_val_decode(ctx->mem); 149 | 150 | ctx->state = STATE_CHECK_PAYLOAD; 151 | 152 | ctx->crc = crc8_getForByte(byte, PROTO_CRC8_POLY, ctx->crc); 153 | } 154 | break; 155 | 156 | case STATE_PAYLOAD: 157 | { 158 | ((uint8_t *) ctx->mem)[ctx->dataRead++] = byte; 159 | 160 | if (ctx->dataRead == ctx->dataSize) { 161 | ctx->state = STATE_CRC; 162 | } 163 | 164 | ctx->crc = crc8_getForByte(byte, PROTO_CRC8_POLY, ctx->crc); 165 | } 166 | break; 167 | 168 | case STATE_CRC: 169 | { 170 | if (ctx->crc != byte) { 171 | error = PROTO_ERROR_INVALID_CRC; 172 | 173 | } else { 174 | request->code = ctx->code; 175 | request->id = ctx->id; 176 | 177 | request->payloadSize = ctx->dataSize; 178 | if (request->payloadSize) { 179 | request->payload = ctx->mem; 180 | 181 | } else { 182 | request->payload = NULL; 183 | } 184 | 185 | ctx->state = STATE_CMD_RDY; 186 | } 187 | } 188 | break; 189 | } 190 | 191 | if (ctx->state == STATE_CHECK_PAYLOAD) { 192 | if (ctx->dataSize > ctx->memSize) { 193 | error = PROTO_ERROR_INVALID_LENGTH; 194 | 195 | } else { 196 | ctx->state = STATE_PAYLOAD; 197 | } 198 | } 199 | 200 | { 201 | uint8_t ret = PROTO_PKT_DES_RET_IDLE; 202 | 203 | if (error != PROTO_NO_ERROR) { 204 | ret = PROTO_PKT_DES_RET_SET_ERROR_CODE(error); 205 | 206 | } else if (ctx->state == STATE_CMD_RDY) { 207 | ret = PROTO_PKT_DES_RET_SET_ERROR_CODE(error); 208 | } 209 | 210 | if (ret != PROTO_PKT_DES_RET_IDLE) { 211 | proto_pkt_dec_reset(ctx); 212 | } 213 | 214 | return ret; 215 | } 216 | } 217 | 218 | 219 | void proto_pkt_dec_reset(ProtoPktDes *ctx) { 220 | ctx->state = STATE_WAIT_SYNC; 221 | ctx->crc = PROTO_CRC8_START; 222 | ctx->id = ID_UNKNOWN; 223 | ctx->code = CMD_UNKNOWN; 224 | ctx->dataRead = 0; 225 | ctx->dataSize = 0; 226 | } 227 | -------------------------------------------------------------------------------- /common/src/common/protocol/request.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common/protocol.h" 4 | #include "common/protocol/request.h" 5 | 6 | #include "common.h" 7 | 8 | 9 | #define PTR_U8(x) ((uint8_t *)(x)) 10 | 11 | 12 | void proto_req_init(ProtoReq *request, void *memory, uint16_t memorySize, uint8_t cmd) { 13 | request->cmd = cmd; 14 | 15 | switch (request->cmd) { 16 | case PROTO_CMD_SPI_TRANSFER: 17 | { 18 | ProtoReqTransfer *t = &request->request.transfer; 19 | 20 | uint8_t overhead = 7; 21 | 22 | if (memorySize > 7) { 23 | t->txBufferSize = memorySize - overhead; 24 | 25 | } else { 26 | t->txBufferSize = 0; 27 | } 28 | 29 | t->txBuffer = NULL; 30 | t->rxBufferSize = 0; 31 | t->rxBufferSize = 0; 32 | t->rxSkipSize = 0; 33 | } 34 | break; 35 | 36 | default: 37 | break; 38 | } 39 | } 40 | 41 | 42 | uint16_t proto_req_getPayloadSize(ProtoReq *request) { 43 | uint16_t ret = 0; 44 | 45 | switch (request->cmd) { 46 | case PROTO_CMD_SPI_TRANSFER: 47 | { 48 | ProtoReqTransfer *t = &request->request.transfer; 49 | 50 | ret = 51 | 1 + 52 | proto_int_val_length_estimate(t->txBufferSize) + 53 | t->txBufferSize + 54 | proto_int_val_length_estimate(t->rxBufferSize) + 55 | proto_int_val_length_estimate(t->rxSkipSize); 56 | } 57 | break; 58 | 59 | default: 60 | break; 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | 67 | void proto_req_assign(ProtoReq *request, void *memory, uint16_t memorySize) { 68 | switch (request->cmd) { 69 | case PROTO_CMD_SPI_TRANSFER: 70 | { 71 | ProtoReqTransfer *t = &request->request.transfer; 72 | 73 | if (t->txBufferSize) { 74 | t->txBuffer = PTR_U8(memory) + (1 + proto_int_val_length_estimate(t->txBufferSize)); 75 | 76 | } else { 77 | t->txBuffer = NULL; 78 | } 79 | } 80 | break; 81 | 82 | default: 83 | break; 84 | } 85 | } 86 | 87 | 88 | uint16_t proto_req_encode(ProtoReq *request, void *memory, uint16_t memorySize) { 89 | uint16_t ret = 0; 90 | 91 | switch (request->cmd) { 92 | case PROTO_CMD_GET_INFO: 93 | { 94 | } 95 | break; 96 | 97 | case PROTO_CMD_SPI_TRANSFER: 98 | { 99 | ProtoReqTransfer *t = &request->request.transfer; 100 | 101 | PTR_U8(memory)[ret++] = t->flags; 102 | 103 | ret += proto_int_val_encode(t->txBufferSize, PTR_U8(memory) + ret); 104 | 105 | ret += t->txBufferSize; 106 | 107 | ret += proto_int_val_encode(t->rxSkipSize, PTR_U8(memory) + ret); 108 | ret += proto_int_val_encode(t->rxBufferSize, PTR_U8(memory) + ret); 109 | } 110 | break; 111 | 112 | default: 113 | { 114 | 115 | } 116 | break; 117 | } 118 | 119 | return ret; 120 | } 121 | 122 | 123 | uint16_t proto_req_decode(ProtoReq *request, void *memory, uint16_t memorySize) { 124 | uint16_t ret = 0; 125 | 126 | switch (request->cmd) { 127 | case PROTO_CMD_GET_INFO: 128 | { 129 | } 130 | break; 131 | 132 | case PROTO_CMD_SPI_TRANSFER: 133 | { 134 | ProtoReqTransfer *t = &request->request.transfer; 135 | 136 | t->flags = PTR_U8(memory)[ret++]; 137 | t->txBuffer = NULL; 138 | t->txBufferSize = proto_int_val_decode(PTR_U8(memory) + ret); ret += proto_int_val_length_estimate(t->txBufferSize); 139 | 140 | ret += t->txBufferSize; 141 | 142 | t->rxSkipSize = proto_int_val_decode(PTR_U8(memory) + ret); ret += proto_int_val_length_estimate(t->rxSkipSize);; 143 | t->rxBufferSize = proto_int_val_decode(PTR_U8(memory) + ret); ret += proto_int_val_length_estimate(t->rxBufferSize); 144 | } 145 | break; 146 | 147 | default: 148 | break; 149 | } 150 | 151 | return ret; 152 | } 153 | -------------------------------------------------------------------------------- /common/src/common/protocol/response.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common/protocol.h" 4 | #include "common/protocol/response.h" 5 | 6 | #include "common.h" 7 | 8 | 9 | #define PTR_U8(x) ((uint8_t *)(x)) 10 | 11 | 12 | void proto_res_init(ProtoRes *response, void *memory, uint16_t memorySize, uint8_t cmd) { 13 | uint8_t overHead = 1; 14 | 15 | response->cmd = cmd; 16 | 17 | switch (cmd) { 18 | case PROTO_CMD_SPI_TRANSFER: 19 | { 20 | ProtoResTransfer *t = &response->response.transfer; 21 | 22 | if (memorySize > overHead) { 23 | t->rxBufferSize = memorySize - overHead; 24 | } 25 | 26 | t->rxBuffer = NULL; 27 | } 28 | break; 29 | 30 | default: 31 | break; 32 | } 33 | } 34 | 35 | 36 | uint16_t proto_res_getPayloadSize(ProtoRes *response) { 37 | uint16_t ret = 0; 38 | 39 | switch (response->cmd) { 40 | case PROTO_CMD_GET_INFO: 41 | { 42 | ret = 1 + proto_int_val_length_estimate(response->response.getInfo.packetSize); 43 | } 44 | break; 45 | 46 | case PROTO_CMD_SPI_TRANSFER: 47 | { 48 | ProtoResTransfer *t = &response->response.transfer; 49 | 50 | ret = t->rxBufferSize; 51 | } 52 | break; 53 | 54 | default: 55 | break; 56 | } 57 | 58 | return ret; 59 | } 60 | 61 | 62 | void proto_res_assign(ProtoRes *response, void *memory, uint16_t memorySize) { 63 | switch (response->cmd) { 64 | case PROTO_CMD_GET_INFO: 65 | { 66 | } 67 | break; 68 | 69 | case PROTO_CMD_SPI_TRANSFER: 70 | { 71 | ProtoResTransfer *t = &response->response.transfer; 72 | 73 | if (t->rxBufferSize) { 74 | t->rxBuffer = PTR_U8(memory); 75 | 76 | } else { 77 | t->rxBuffer = NULL; 78 | } 79 | } 80 | break; 81 | 82 | default: 83 | { 84 | 85 | } 86 | break; 87 | } 88 | } 89 | 90 | 91 | uint16_t proto_res_encode(ProtoRes *response, void *memory, uint16_t memorySize) { 92 | uint16_t ret = 0; 93 | 94 | switch (response->cmd) { 95 | case PROTO_CMD_GET_INFO: 96 | { 97 | ProtoResGetInfo *info = &response->response.getInfo; 98 | 99 | PTR_U8(memory)[ret++] = (info->version.major << 4) | (info->version.minor & 0x0f); 100 | 101 | ret += proto_int_val_encode(info->packetSize, PTR_U8(memory) + ret); 102 | } 103 | break; 104 | 105 | case PROTO_CMD_SPI_TRANSFER: 106 | { 107 | ProtoResTransfer *t = &response->response.transfer; 108 | 109 | ret += t->rxBufferSize; 110 | } 111 | break; 112 | 113 | default: 114 | break; 115 | } 116 | 117 | return ret; 118 | } 119 | 120 | 121 | uint16_t proto_res_decode(ProtoRes *response, void *memory, uint16_t memorySize) { 122 | uint16_t ret = 0; 123 | 124 | switch (response->cmd) { 125 | case PROTO_CMD_GET_INFO: 126 | { 127 | ProtoResGetInfo *info = &response->response.getInfo; 128 | 129 | info->version.major = PTR_U8(memory)[ret] >> 4; 130 | info->version.minor = PTR_U8(memory)[ret++] & 0x0f; 131 | info->packetSize = proto_int_val_decode(PTR_U8(memory) + ret); 132 | 133 | ret += proto_int_val_length_estimate(info->packetSize); 134 | } 135 | break; 136 | 137 | case PROTO_CMD_SPI_TRANSFER: 138 | { 139 | ProtoResTransfer *t = &response->response.transfer; 140 | 141 | t->rxBuffer = NULL; 142 | t->rxBufferSize = memorySize; 143 | 144 | ret += t->rxBufferSize; 145 | } 146 | break; 147 | 148 | default: 149 | break; 150 | } 151 | 152 | return ret; 153 | } 154 | -------------------------------------------------------------------------------- /firmware/arduino/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | set(DEFAULT_AVR_UPLOADTOOL_PORT /dev/ttyUSB0) 4 | set(DEFAULT_AVR_UPLOADTOOL_BAUDRATE 57600) 5 | set(DEFAULT_AVR_PROGRAMMER arduino) 6 | 7 | set(DEFAULT_FW_F_CPU 16000000) 8 | set(DEFAULT_FW_BAUD 500000) 9 | 10 | find_program(AVR_CC avr-gcc REQUIRED) 11 | find_program(AVR_CXX avr-g++ REQUIRED) 12 | find_program(AVR_OBJCOPY avr-objcopy REQUIRED) 13 | find_program(AVR_SIZE_TOOL avr-size REQUIRED) 14 | find_program(AVR_OBJDUMP avr-objdump REQUIRED) 15 | 16 | set(CMAKE_SYSTEM_NAME Generic) 17 | set(CMAKE_SYSTEM_PROCESSOR avr) 18 | set(CMAKE_C_COMPILER ${AVR_CC}) 19 | set(CMAKE_CXX_COMPILER ${AVR_CXX}) 20 | 21 | project(arduino-firmare) 22 | 23 | find_program(AVR_UPLOADTOOL avrdude) 24 | 25 | set(AVR_MCU atmega328p) 26 | set(AVR_L_FUSE 0xF7) 27 | set(AVR_H_FUSE 0xD9) 28 | set(AVR_E_FUSE 0xFF) 29 | 30 | set(BURN_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/burn.sh) 31 | 32 | include(${CMAKE_CURRENT_LIST_DIR}/../../common/CMakeLists.txt) 33 | include(${CMAKE_CURRENT_LIST_DIR}/../common/CMakeLists.txt) 34 | 35 | if(NOT AVR_UPLOADTOOL_PORT) 36 | set(AVR_UPLOADTOOL_PORT ${DEFAULT_AVR_UPLOADTOOL_PORT} CACHE STRING "Set default upload tool port: ${DEFAULT_AVR_UPLOADTOOL_PORT}") 37 | endif() 38 | 39 | if(NOT AVR_UPLOADTOOL_BAUDRATE) 40 | set(AVR_UPLOADTOOL_BAUDRATE ${DEFAULT_AVR_UPLOADTOOL_BAUDRATE} CACHE STRING "Set default programmer baudrate: ${DEFAULT_AVR_UPLOADTOOL_BAUDRATE}") 41 | endif() 42 | 43 | if(NOT AVR_PROGRAMMER) 44 | set(AVR_PROGRAMMER ${DEFAULT_AVR_PROGRAMMER} CACHE STRING "Set default programmer hardware model: ${DEFAULT_AVR_PROGRAMMER}") 45 | endif() 46 | 47 | if(NOT FW_F_CPU) 48 | set(FW_F_CPU ${DEFAULT_FW_F_CPU} CACHE STRING "Set default cpu frequency: ${DEFAULT_FW_F_CPU}") 49 | endif() 50 | 51 | if(NOT FW_BAUD) 52 | set(FW_BAUD ${DEFAULT_FW_BAUD} CACHE STRING "Set default serial baudrate: ${DEFAULT_FW_BAUD}") 53 | endif() 54 | 55 | set(BURN_SCRIPT_BASE_OPTIONS -m ${AVR_MCU} -P ${AVR_PROGRAMMER} -p ${AVR_UPLOADTOOL_PORT}) 56 | 57 | if(AVR_UPLOADTOOL_BAUDRATE) 58 | set(BURN_SCRIPT_BASE_OPTIONS ${BURN_SCRIPT_BASE_OPTIONS} -b ${AVR_UPLOADTOOL_BAUDRATE}) 59 | endif() 60 | 61 | set(AVR_SIZE_ARGS -C;--mcu=${AVR_MCU}) 62 | 63 | set(EXECUTABLE_NAME firmware) 64 | 65 | set(elf_file ${EXECUTABLE_NAME}.elf) 66 | set(hex_file ${EXECUTABLE_NAME}.hex) 67 | set(lst_file ${EXECUTABLE_NAME}.lst) 68 | set(map_file ${EXECUTABLE_NAME}.map) 69 | set(eeprom_image ${EXECUTABLE_NAME}-eeprom.hex) 70 | 71 | add_definitions("-DF_CPU=${FW_F_CPU}") 72 | add_definitions("-mmcu=${AVR_MCU}") 73 | 74 | add_definitions("-std=c99") 75 | 76 | add_definitions("-O2") 77 | add_definitions("-mrelax") 78 | 79 | add_definitions("-Wall") 80 | add_definitions("-Wundef") 81 | add_definitions("-Warray-bounds") 82 | add_definitions("-Wformat") 83 | add_definitions("-Wmissing-braces") 84 | add_definitions("-Wreturn-type") 85 | 86 | add_definitions("-fshort-enums") 87 | add_definitions("-fpack-struct") 88 | add_definitions("-fno-split-wide-types") 89 | add_definitions("-ffunction-sections") 90 | add_definitions("-fdata-sections") 91 | 92 | add_definitions("-DBAUD=${FW_BAUD}") 93 | 94 | link_libraries("-mmcu=${AVR_MCU} -Wl,--gc-sections -mrelax -Wl,-Map,${map_file}") 95 | 96 | set(src_path "${CMAKE_CURRENT_LIST_DIR}/src/") 97 | 98 | add_executable(${elf_file} 99 | ${src_path}/main.c 100 | ) 101 | 102 | target_link_libraries(${elf_file} 103 | PRIVATE 104 | firmware-common 105 | protocol 106 | ) 107 | 108 | add_custom_command( 109 | OUTPUT 110 | ${hex_file} 111 | COMMAND 112 | ${AVR_OBJCOPY} -j .text -j .data -O ihex ${elf_file} ${hex_file} 113 | COMMAND 114 | ${AVR_SIZE_TOOL} ${AVR_SIZE_ARGS} ${elf_file} 115 | DEPENDS 116 | ${elf_file} 117 | ) 118 | 119 | add_custom_command( 120 | OUTPUT 121 | ${lst_file} 122 | COMMAND 123 | ${AVR_OBJDUMP} -d ${elf_file} > ${lst_file} 124 | DEPENDS 125 | ${elf_file} 126 | ) 127 | 128 | add_custom_command( 129 | OUTPUT 130 | ${eeprom_image} 131 | COMMAND 132 | ${AVR_OBJCOPY} -j .eeprom --set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0 --no-change-warnings -O ihex ${elf_file} ${eeprom_image} 133 | DEPENDS 134 | ${elf_file} 135 | ) 136 | 137 | add_custom_target( 138 | ${EXECUTABLE_NAME} 139 | ALL 140 | DEPENDS 141 | ${hex_file} ${lst_file} ${eeprom_image} 142 | ) 143 | 144 | add_custom_target( 145 | upload_${EXECUTABLE_NAME} 146 | ${BURN_SCRIPT} ${BURN_SCRIPT_BASE_OPTIONS} -f ${hex_file} 147 | 148 | DEPENDS 149 | ${hex_file} 150 | 151 | COMMENT 152 | "Uploading ${hex_file} to ${AVR_MCU} using ${AVR_PROGRAMMER}" 153 | ) 154 | 155 | add_custom_target( 156 | upload_${EXECUTABLE_NAME}_eeprom 157 | ${BURN_SCRIPT} ${BURN_SCRIPT_BASE_OPTIONS} -e ${eeprom_image} 158 | 159 | DEPENDS 160 | ${eeprom_image} 161 | 162 | COMMENT 163 | "Uploading ${eeprom_image} to ${AVR_MCU} using ${AVR_PROGRAMMER}" 164 | ) 165 | 166 | 167 | add_custom_target( 168 | set_fuses 169 | ${BURN_SCRIPT} ${BURN_SCRIPT_BASE_OPTIONS} -L ${AVR_L_FUSE} -H ${AVR_H_FUSE} -E ${AVR_E_FUSE} 170 | 171 | COMMENT 172 | "Setup: High Fuse: ${AVR_H_FUSE} Low Fuse: ${AVR_L_FUSE}, Extended Fuse: ${AVR_E_FUSE}" 173 | ) -------------------------------------------------------------------------------- /firmware/arduino/build_dist.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 6 | 7 | VERBOSE=0 8 | 9 | function build() { 10 | local fcpu=$1; shift 11 | local baud=$1; shift 12 | local outDir=$1; shift 13 | 14 | rm -rf build; mkdir build 15 | pushd build 16 | cmake ../ -DFW_F_CPU=${fcpu} -DFW_BAUD=${baud} 17 | VERBOSE=${VERBOSE} make -j8 clean all 18 | cp -f firmware.hex ${outDir}/firmware_$(( fcpu / 1000000 ))mhz_${baud}bps.hex 19 | popd 20 | rm -rf build; 21 | } 22 | 23 | build 16000000 500000 ${SCRIPT_DIR}/dist 24 | build 16000000 38400 ${SCRIPT_DIR}/dist 25 | build 16000000 19200 ${SCRIPT_DIR}/dist 26 | 27 | build 8000000 500000 ${SCRIPT_DIR}/dist 28 | build 8000000 38400 ${SCRIPT_DIR}/dist 29 | build 8000000 19200 ${SCRIPT_DIR}/dist 30 | -------------------------------------------------------------------------------- /firmware/arduino/burn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | AVR_UPLOAD_TOOL=avrdude 4 | AVR_UPLOAD_PORT=/dev/ttyUSB0 5 | AVR_UPLOAD_BAUDRATE= 6 | AVR_UPLOAD_PROGRAMMER=arduino 7 | AVR_UPLOAD_MCU= 8 | 9 | AVR_UPLOAD_HFUSE= 10 | AVR_UPLOAD_LFUSE= 11 | AVR_UPLOAD_EFUSE= 12 | AVR_UPLOAD_FIRMWARE= 13 | 14 | function usage() { 15 | echo "Usage:" 16 | echo " $0 <-m mcu> [-P programmer] [-p port] [-b baudrate] [-m mcu] [-L lfuse] [-H hfuse] [-E efuse] [-f flash.hex] [-e e2prom.hex]" 17 | 18 | exit 1 19 | } 20 | 21 | function fail() { 22 | echo $@ 23 | exit 1 24 | } 25 | 26 | 27 | while getopts "hp:b:m:P:L:H:E:f:e:" arg; do 28 | case $arg in 29 | p) 30 | AVR_UPLOAD_PORT=$OPTARG 31 | ;; 32 | 33 | P) 34 | AVR_UPLOAD_PROGRAMMER=$OPTARG 35 | ;; 36 | 37 | b) 38 | AVR_UPLOAD_BAUDRATE=$OPTARG 39 | ;; 40 | 41 | m) 42 | AVR_UPLOAD_MCU=$OPTARG 43 | ;; 44 | 45 | L) 46 | AVR_UPLOAD_LFUSE=$OPTARG 47 | ;; 48 | 49 | H) 50 | AVR_UPLOAD_HFUSE=$OPTARG 51 | ;; 52 | 53 | E) 54 | AVR_UPLOAD_EFUSE=$OPTARG 55 | ;; 56 | 57 | f) 58 | AVR_UPLOAD_FIRMWARE=$OPTARG 59 | ;; 60 | 61 | e) 62 | AVR_UPLOAD_E2PROM=$OPTARG 63 | ;; 64 | 65 | *) 66 | usage 67 | ;; 68 | esac 69 | done 70 | 71 | AVR_UPLOADTOOL_OPTS="" 72 | 73 | if [ "x$AVR_UPLOAD_PORT" = "x" ]; then 74 | fail "AVR_UPLOAD_PORT is empty!" 75 | else 76 | AVR_UPLOADTOOL_OPTS+=" -P $AVR_UPLOAD_PORT" 77 | fi 78 | 79 | if [ "x$AVR_UPLOAD_BAUDRATE" != "x" ]; then 80 | AVR_UPLOADTOOL_OPTS+=" -b $AVR_UPLOAD_BAUDRATE" 81 | fi 82 | 83 | if [ "x$AVR_UPLOAD_PROGRAMMER" = "x" ]; then 84 | fail "AVR_UPLOAD_PROGRAMMER is empty!" 85 | else 86 | AVR_UPLOADTOOL_OPTS+=" -c $AVR_UPLOAD_PROGRAMMER" 87 | fi 88 | 89 | if [ "x$AVR_UPLOAD_MCU" = "x" ]; then 90 | fail "AVR_UPLOAD_MCU is empty!" 91 | else 92 | AVR_UPLOADTOOL_OPTS+=" -p $AVR_UPLOAD_MCU" 93 | fi 94 | 95 | if [ "x$AVR_UPLOAD_LFUSE" != "x" ]; then 96 | AVR_UPLOADTOOL_OPTS+=" -U lfuse:w:${AVR_UPLOAD_LFUSE}:m" 97 | fi 98 | 99 | if [ "x$AVR_UPLOAD_HFUSE" != "x" ]; then 100 | AVR_UPLOADTOOL_OPTS+=" -U hfuse:w:${AVR_UPLOAD_HFUSE}:m" 101 | fi 102 | 103 | if [ "x$AVR_UPLOAD_EFUSE" != "x" ]; then 104 | AVR_UPLOADTOOL_OPTS+=" -U efuse:w:${AVR_UPLOAD_EFUSE}:m" 105 | fi 106 | 107 | if [ "x$AVR_UPLOAD_FIRMWARE" != "x" ]; then 108 | AVR_UPLOADTOOL_OPTS+=" -U flash:w:${AVR_UPLOAD_FIRMWARE}" 109 | fi 110 | 111 | if [ "x$AVR_UPLOAD_E2PROM" != "x" ]; then 112 | AVR_UPLOADTOOL_OPTS+=" -U eeprom:w:${AVR_UPLOAD_E2PROM}" 113 | fi 114 | 115 | cmd="$AVR_UPLOAD_TOOL $AVR_UPLOADTOOL_OPTS" 116 | 117 | echo "Executing '$cmd'" 118 | 119 | eval $cmd -------------------------------------------------------------------------------- /firmware/arduino/dist/firmware_16mhz_19200bps.hex: -------------------------------------------------------------------------------- 1 | :1000000033C0000041C000003FC000003DC0000000 2 | :100010003BC0000039C0000037C0000035C0000000 3 | :1000200033C0000031C000002FC000002DC0000010 4 | :100030002BC0000029C0000027C0000025C0000020 5 | :1000400023C0000021C000001FC000001DC0000030 6 | :100050001BC0000019C0000017C0000015C0000040 7 | :1000600013C0000011C0000011241FBECFEFD8E064 8 | :10007000DEBFCDBF23E0A0E0B1E001C01D92A03003 9 | :10008000B207E1F77AD039C4BBCF6115710569F0C9 10 | :10009000FC01680F791F81919091C00095FFFCCF02 11 | :1000A0008093C600E617F707B1F70895EF92FF9225 12 | :1000B0001F93CF93DF93FC018081813009F047C00B 13 | :1000C0002A98C581D68183819481278130852C0F20 14 | :1000D0003D1F2817390708F442C0232BA9F140E03F 15 | :1000E00050E01FEF48175907C0F5A181B281A40F56 16 | :1000F000B51F8C918EBD0DB407FEFDCFA781B085D5 17 | :10010000209799F04A175B0780F02EB5CA018A1B29 18 | :100110009B0BDB011196ED90FC901297D701A80F75 19 | :10012000B91F2C932197A781B0854F5F5F4F8381C3 20 | :100130009481258136812A0F3B1F2817390708F43F 21 | :100140009C014217530770F2818580FF2A9ADF9144 22 | :10015000CF911F91FF90EF9008951EBDCCCF9C01D1 23 | :10016000BCCF229A2A9A239A259A8DB58E7F8DBD6F 24 | :1001700081E08DBD80E58CBD0895CF93DF93CDB731 25 | :10018000DEB766970FB6F894DEBF0FBECDBF1092F4 26 | :10019000C50083E38093C40086E08093C200809111 27 | :1001A000C10088618093C100DCDFE12CF12C05E403 28 | :1001B00010E026E530E040E052E060E071E0CE0182 29 | :1001C000019640D000E010E08091C00087FD0DC096 30 | :1001D0000F5F1F4F00368AEE1807B1F7CE01019668 31 | :1001E00009D18091C00087FFF3CF6091C600CE0196 32 | :1001F00001963DD000E010E0E7CFCF92DF92EF9282 33 | :10020000FF920F93CF93DF937C016B01EA01FB0117 34 | :1002100040815181FC016081718109812881CE0179 35 | :1002200002D1F60140815181F7016081718120E0A6 36 | :1002300030E0CE01DF91CF910F91FF90EF90DF90F2 37 | :10024000CF900EC1EF92FF920F931F93FC01718329 38 | :10025000608353834283318B208B138B028BF58A0F 39 | :10026000E48A04961F910F91FF90EF9072C1CF9294 40 | :10027000DF92EF92FF920F931F93CF93DF93CDB74F 41 | :10028000DEB765970FB6F894DEBF0FBECDBF7C0119 42 | :10029000AE01455F5F4F04966AD1882319F18F77CD 43 | :1002A00081F1AE01455F5F4FB7016E5F7F4FC701C0 44 | :1002B000A4DFD70152960D911C9153975496CD907F 45 | :1002C000DC90559712964D915C9113976D917C91AE 46 | :1002D000CE010B9612D1A601BC01F701808191815C 47 | :1002E000F801099565960FB6F894DEBF0FBECDBF35 48 | :1002F000DF91CF911F910F91FF90EF90DF90CF9002 49 | :1003000008954F8558896D857E852B85CE01019690 50 | :10031000DAD14F8558896D857E85CE0101960BD245 51 | :100320004F8558896D857E85CE010196E1D11B856B 52 | :10033000D70112964D915C9113976D917C910C852C 53 | :1003400020E0CE010B966FD04F8558896D857E8554 54 | :10035000212FCE01419633D28981882309F441C0EF 55 | :100360008130A1F1AE01455F5F4FB7016E5F7F4FF6 56 | :10037000C70143DF8B8581119CCFCE0141962DD2E1 57 | :10038000D70112964D915C9113976D917C919C01D0 58 | :10039000CE010B9665D04F8558896D857E85CE013F 59 | :1003A00041962FD2F701448955890088F189E02DC3 60 | :1003B000BE016F5E7F4FCE01019609954F8558892A 61 | :1003C0006D857E85CE0141962AD273CF8E819F8125 62 | :1003D0002C893D892817390708F4C4CF9D8B8C8B55 63 | :1003E000C9CF81E08A8B1B8AF701828193819D8B23 64 | :1003F0008C8BC0CF04965DC10F931F93CF93DF9377 65 | :10040000EC012397CE014FD2823031F0CE01DF9143 66 | :10041000CF911F910F9108952197CE01DF91CF9138 67 | :100420001F910F9108950F93CF93DF93EC012883D1 68 | :1004300009834430510550F480E090E09D838C8323 69 | :100440001B821A82DF91CF910F910895CA01D4DFE8 70 | :100450009D838C831B821A82DF91CF910F91089527 71 | :10046000EF92FF920F931F93CF93DF93FC01848150 72 | :1004700095818217930719F121153105A1F08901A2 73 | :10048000CA017B01EF0144305105A8F580E090E0FE 74 | :1004900080179107E0F480E0DF91CF911F910F91D9 75 | :1004A000FF90EF900895158214821382128281E0EA 76 | :1004B000DF91CF911F910F91FF90EF90089581E010 77 | :1004C000DF91CF911F910F91FF90EF9008951D83C1 78 | :1004D0000C83C801E8D1682F70E06E5F7F4F6E0D0E 79 | :1004E0007F1D7B836A8381E0DF91CF911F910F9104 80 | :1004F000FF90EF90089580DFCBCFEF92FF920F93A4 81 | :100500001F93CF93DF93EC018B018881806DFB01FA 82 | :100510008083898181836E5F7F4F8C819D81E0D153 83 | :100520000C80DD81C02D2296C80FD11D7801EC0E04 84 | :10053000FD1E20E04BEABE01C8019DD1F70180837A 85 | :10054000CE010196DF91CF911F910F91FF90EF9017 86 | :100550000895FC0171876087538742871082138258 87 | :1005600011821282178216821582148208951F93B7 88 | :10057000CF93DF93EC01162F8881823009F47BC082 89 | :1005800028F18530D1F1863009F47FC0833009F439 90 | :100590005CC0843059F52C813D818A859B8582170A 91 | :1005A000930768F482E818821B8219821A821F82DC 92 | :1005B0001E821D821C82DF91CF911F91089585E0DC 93 | :1005C000888380E0DF91CF911F910895882309F4FB 94 | :1005D00047C08130F1F6698382E088834B816BEA02 95 | :1005E000812F3CD18B8388818430A9F2873059F1E7 96 | :1005F00080E0DF91CF911F910895288539858E8104 97 | :100600009F81AC014F5F5F4F5F834E83F901E80F1D 98 | :10061000F91F60832E813F818C819D8128173907C6 99 | :10062000E9F686E08883DACF8A81FA0180838981BE 100 | :1006300081838C819D8195838483892BA9F1888511 101 | :1006400099859383828380E8AECFE885F9856183BD 102 | :100650008885998534D19D838C8384E08883BECF3F 103 | :10066000862F807F803D21F6862F87708A8381E0E8 104 | :10067000888340E0B4CF862F1DD1813059F0E885C2 105 | :10068000F985108383E08883A9CF8B81681761F295 106 | :1006900083E889CF812F90E09D838C83892B09F497 107 | :1006A000C0CF84E088839ACFFA011382128280E857 108 | :1006B0007ACFFC0110821382118212821782168275 109 | :1006C000158214820895FC012083213059F44830AA 110 | :1006D000510548F414821382128211821682158207 111 | :1006E0001086178208954750510954834383F4CFED 112 | :1006F0000F931F93CF93DF93FC018081813031F4FE 113 | :1007000083819481009739F412821182DF91CF9115 114 | :100710001F910F9108958B01EF01C5D0682F70E0F4 115 | :100720006F5F7F4F600F711F7A836983DF91CF9175 116 | :100730001F910F910895EF92FF920F931F93CF9304 117 | :10074000DF93FC012081213089F57B018C01FB01C5 118 | :100750002191CF01F801218712821182B0D0F801D6 119 | :1007600094838383A0D0F801C381D4812196C80FDC 120 | :10077000D11DC7018C0F9D1FA2D0F80190878783E0 121 | :1007800092D0C80FD11DC7018C0F9D1F98D0F801C2 122 | :100790009683858388D09E01280F311DC901DF9182 123 | :1007A000CF911F910F91FF90EF90089580E090E01E 124 | :1007B000DF91CF911F910F91FF90EF900895FC0171 125 | :1007C0002083213049F44230510520F04150510935 126 | :1007D00054834383128211820895FC0120812223D5 127 | :1007E00049F0213021F4FC0183819481089580E057 128 | :1007F00090E00895FC018381948155D090E00196AA 129 | :100800000895FC018081813031F483819481892BAA 130 | :1008100019F4128211820895728361830895DC01B4 131 | :100820002C91222349F0213021F4FC018381948111 132 | :10083000089580E090E00895FC018181B0E18B9FF4 133 | :10084000C001112492819F70892BDB018D93BD0122 134 | :100850008381948145D090E001960895842798E0A3 135 | :1008600004C086958627915029F080FDFACF8695A1 136 | :100870009150D9F708956115710599F0FC01680F41 137 | :10088000791F8191282798E004C02695242791504C 138 | :1008900029F020FDFACF26959150D9F7E617F707F2 139 | :1008A00081F7822F08958038910510F082E0089535 140 | :1008B00081E00895881F8827881F8F5F0895FC01B5 141 | :1008C000208127FD03C0822F90E008952F7730E02C 142 | :1008D000322F2227FC018181A901482BCA010895EA 143 | :1008E0008038910530F09068FB019083818382E02D 144 | :0E08F0000895FB01808381E00895F894FFCF06 145 | :00000001FF 146 | -------------------------------------------------------------------------------- /firmware/arduino/dist/firmware_16mhz_38400bps.hex: -------------------------------------------------------------------------------- 1 | :1000000033C0000041C000003FC000003DC0000000 2 | :100010003BC0000039C0000037C0000035C0000000 3 | :1000200033C0000031C000002FC000002DC0000010 4 | :100030002BC0000029C0000027C0000025C0000020 5 | :1000400023C0000021C000001FC000001DC0000030 6 | :100050001BC0000019C0000017C0000015C0000040 7 | :1000600013C0000011C0000011241FBECFEFD8E064 8 | :10007000DEBFCDBF23E0A0E0B1E001C01D92A03003 9 | :10008000B207E1F77AD039C4BBCF6115710569F0C9 10 | :10009000FC01680F791F81919091C00095FFFCCF02 11 | :1000A0008093C600E617F707B1F70895EF92FF9225 12 | :1000B0001F93CF93DF93FC018081813009F047C00B 13 | :1000C0002A98C581D68183819481278130852C0F20 14 | :1000D0003D1F2817390708F442C0232BA9F140E03F 15 | :1000E00050E01FEF48175907C0F5A181B281A40F56 16 | :1000F000B51F8C918EBD0DB407FEFDCFA781B085D5 17 | :10010000209799F04A175B0780F02EB5CA018A1B29 18 | :100110009B0BDB011196ED90FC901297D701A80F75 19 | :10012000B91F2C932197A781B0854F5F5F4F8381C3 20 | :100130009481258136812A0F3B1F2817390708F43F 21 | :100140009C014217530770F2818580FF2A9ADF9144 22 | :10015000CF911F91FF90EF9008951EBDCCCF9C01D1 23 | :10016000BCCF229A2A9A239A259A8DB58E7F8DBD6F 24 | :1001700081E08DBD80E58CBD0895CF93DF93CDB731 25 | :10018000DEB766970FB6F894DEBF0FBECDBF1092F4 26 | :10019000C50089E18093C40086E08093C20080910D 27 | :1001A000C10088618093C100DCDFE12CF12C05E403 28 | :1001B00010E026E530E040E052E060E071E0CE0182 29 | :1001C000019640D000E010E08091C00087FD0DC096 30 | :1001D0000F5F1F4F00368AEE1807B1F7CE01019668 31 | :1001E00009D18091C00087FFF3CF6091C600CE0196 32 | :1001F00001963DD000E010E0E7CFCF92DF92EF9282 33 | :10020000FF920F93CF93DF937C016B01EA01FB0117 34 | :1002100040815181FC016081718109812881CE0179 35 | :1002200002D1F60140815181F7016081718120E0A6 36 | :1002300030E0CE01DF91CF910F91FF90EF90DF90F2 37 | :10024000CF900EC1EF92FF920F931F93FC01718329 38 | :10025000608353834283318B208B138B028BF58A0F 39 | :10026000E48A04961F910F91FF90EF9072C1CF9294 40 | :10027000DF92EF92FF920F931F93CF93DF93CDB74F 41 | :10028000DEB765970FB6F894DEBF0FBECDBF7C0119 42 | :10029000AE01455F5F4F04966AD1882319F18F77CD 43 | :1002A00081F1AE01455F5F4FB7016E5F7F4FC701C0 44 | :1002B000A4DFD70152960D911C9153975496CD907F 45 | :1002C000DC90559712964D915C9113976D917C91AE 46 | :1002D000CE010B9612D1A601BC01F701808191815C 47 | :1002E000F801099565960FB6F894DEBF0FBECDBF35 48 | :1002F000DF91CF911F910F91FF90EF90DF90CF9002 49 | :1003000008954F8558896D857E852B85CE01019690 50 | :10031000DAD14F8558896D857E85CE0101960BD245 51 | :100320004F8558896D857E85CE010196E1D11B856B 52 | :10033000D70112964D915C9113976D917C910C852C 53 | :1003400020E0CE010B966FD04F8558896D857E8554 54 | :10035000212FCE01419633D28981882309F441C0EF 55 | :100360008130A1F1AE01455F5F4FB7016E5F7F4FF6 56 | :10037000C70143DF8B8581119CCFCE0141962DD2E1 57 | :10038000D70112964D915C9113976D917C919C01D0 58 | :10039000CE010B9665D04F8558896D857E85CE013F 59 | :1003A00041962FD2F701448955890088F189E02DC3 60 | :1003B000BE016F5E7F4FCE01019609954F8558892A 61 | :1003C0006D857E85CE0141962AD273CF8E819F8125 62 | :1003D0002C893D892817390708F4C4CF9D8B8C8B55 63 | :1003E000C9CF81E08A8B1B8AF701828193819D8B23 64 | :1003F0008C8BC0CF04965DC10F931F93CF93DF9377 65 | :10040000EC012397CE014FD2823031F0CE01DF9143 66 | :10041000CF911F910F9108952197CE01DF91CF9138 67 | :100420001F910F9108950F93CF93DF93EC012883D1 68 | :1004300009834430510550F480E090E09D838C8323 69 | :100440001B821A82DF91CF910F910895CA01D4DFE8 70 | :100450009D838C831B821A82DF91CF910F91089527 71 | :10046000EF92FF920F931F93CF93DF93FC01848150 72 | :1004700095818217930719F121153105A1F08901A2 73 | :10048000CA017B01EF0144305105A8F580E090E0FE 74 | :1004900080179107E0F480E0DF91CF911F910F91D9 75 | :1004A000FF90EF900895158214821382128281E0EA 76 | :1004B000DF91CF911F910F91FF90EF90089581E010 77 | :1004C000DF91CF911F910F91FF90EF9008951D83C1 78 | :1004D0000C83C801E8D1682F70E06E5F7F4F6E0D0E 79 | :1004E0007F1D7B836A8381E0DF91CF911F910F9104 80 | :1004F000FF90EF90089580DFCBCFEF92FF920F93A4 81 | :100500001F93CF93DF93EC018B018881806DFB01FA 82 | :100510008083898181836E5F7F4F8C819D81E0D153 83 | :100520000C80DD81C02D2296C80FD11D7801EC0E04 84 | :10053000FD1E20E04BEABE01C8019DD1F70180837A 85 | :10054000CE010196DF91CF911F910F91FF90EF9017 86 | :100550000895FC0171876087538742871082138258 87 | :1005600011821282178216821582148208951F93B7 88 | :10057000CF93DF93EC01162F8881823009F47BC082 89 | :1005800028F18530D1F1863009F47FC0833009F439 90 | :100590005CC0843059F52C813D818A859B8582170A 91 | :1005A000930768F482E818821B8219821A821F82DC 92 | :1005B0001E821D821C82DF91CF911F91089585E0DC 93 | :1005C000888380E0DF91CF911F910895882309F4FB 94 | :1005D00047C08130F1F6698382E088834B816BEA02 95 | :1005E000812F3CD18B8388818430A9F2873059F1E7 96 | :1005F00080E0DF91CF911F910895288539858E8104 97 | :100600009F81AC014F5F5F4F5F834E83F901E80F1D 98 | :10061000F91F60832E813F818C819D8128173907C6 99 | :10062000E9F686E08883DACF8A81FA0180838981BE 100 | :1006300081838C819D8195838483892BA9F1888511 101 | :1006400099859383828380E8AECFE885F9856183BD 102 | :100650008885998534D19D838C8384E08883BECF3F 103 | :10066000862F807F803D21F6862F87708A8381E0E8 104 | :10067000888340E0B4CF862F1DD1813059F0E885C2 105 | :10068000F985108383E08883A9CF8B81681761F295 106 | :1006900083E889CF812F90E09D838C83892B09F497 107 | :1006A000C0CF84E088839ACFFA011382128280E857 108 | :1006B0007ACFFC0110821382118212821782168275 109 | :1006C000158214820895FC012083213059F44830AA 110 | :1006D000510548F414821382128211821682158207 111 | :1006E0001086178208954750510954834383F4CFED 112 | :1006F0000F931F93CF93DF93FC018081813031F4FE 113 | :1007000083819481009739F412821182DF91CF9115 114 | :100710001F910F9108958B01EF01C5D0682F70E0F4 115 | :100720006F5F7F4F600F711F7A836983DF91CF9175 116 | :100730001F910F910895EF92FF920F931F93CF9304 117 | :10074000DF93FC012081213089F57B018C01FB01C5 118 | :100750002191CF01F801218712821182B0D0F801D6 119 | :1007600094838383A0D0F801C381D4812196C80FDC 120 | :10077000D11DC7018C0F9D1FA2D0F80190878783E0 121 | :1007800092D0C80FD11DC7018C0F9D1F98D0F801C2 122 | :100790009683858388D09E01280F311DC901DF9182 123 | :1007A000CF911F910F91FF90EF90089580E090E01E 124 | :1007B000DF91CF911F910F91FF90EF900895FC0171 125 | :1007C0002083213049F44230510520F04150510935 126 | :1007D00054834383128211820895FC0120812223D5 127 | :1007E00049F0213021F4FC0183819481089580E057 128 | :1007F00090E00895FC018381948155D090E00196AA 129 | :100800000895FC018081813031F483819481892BAA 130 | :1008100019F4128211820895728361830895DC01B4 131 | :100820002C91222349F0213021F4FC018381948111 132 | :10083000089580E090E00895FC018181B0E18B9FF4 133 | :10084000C001112492819F70892BDB018D93BD0122 134 | :100850008381948145D090E001960895842798E0A3 135 | :1008600004C086958627915029F080FDFACF8695A1 136 | :100870009150D9F708956115710599F0FC01680F41 137 | :10088000791F8191282798E004C02695242791504C 138 | :1008900029F020FDFACF26959150D9F7E617F707F2 139 | :1008A00081F7822F08958038910510F082E0089535 140 | :1008B00081E00895881F8827881F8F5F0895FC01B5 141 | :1008C000208127FD03C0822F90E008952F7730E02C 142 | :1008D000322F2227FC018181A901482BCA010895EA 143 | :1008E0008038910530F09068FB019083818382E02D 144 | :0E08F0000895FB01808381E00895F894FFCF06 145 | :00000001FF 146 | -------------------------------------------------------------------------------- /firmware/arduino/dist/firmware_16mhz_500000bps.hex: -------------------------------------------------------------------------------- 1 | :1000000033C0000041C000003FC000003DC0000000 2 | :100010003BC0000039C0000037C0000035C0000000 3 | :1000200033C0000031C000002FC000002DC0000010 4 | :100030002BC0000029C0000027C0000025C0000020 5 | :1000400023C0000021C000001FC000001DC0000030 6 | :100050001BC0000019C0000017C0000015C0000040 7 | :1000600013C0000011C0000011241FBECFEFD8E064 8 | :10007000DEBFCDBF23E0A0E0B1E001C01D92A03003 9 | :10008000B207E1F77AD039C4BBCF6115710569F0C9 10 | :10009000FC01680F791F81919091C00095FFFCCF02 11 | :1000A0008093C600E617F707B1F70895EF92FF9225 12 | :1000B0001F93CF93DF93FC018081813009F047C00B 13 | :1000C0002A98C581D68183819481278130852C0F20 14 | :1000D0003D1F2817390708F442C0232BA9F140E03F 15 | :1000E00050E01FEF48175907C0F5A181B281A40F56 16 | :1000F000B51F8C918EBD0DB407FEFDCFA781B085D5 17 | :10010000209799F04A175B0780F02EB5CA018A1B29 18 | :100110009B0BDB011196ED90FC901297D701A80F75 19 | :10012000B91F2C932197A781B0854F5F5F4F8381C3 20 | :100130009481258136812A0F3B1F2817390708F43F 21 | :100140009C014217530770F2818580FF2A9ADF9144 22 | :10015000CF911F91FF90EF9008951EBDCCCF9C01D1 23 | :10016000BCCF229A2A9A239A259A8DB58E7F8DBD6F 24 | :1001700081E08DBD80E58CBD0895CF93DF93CDB731 25 | :10018000DEB766970FB6F894DEBF0FBECDBF1092F4 26 | :10019000C50081E08093C40086E08093C200809116 27 | :1001A000C10088618093C100DCDFE12CF12C05E403 28 | :1001B00010E026E530E040E052E060E071E0CE0182 29 | :1001C000019640D000E010E08091C00087FD0DC096 30 | :1001D0000F5F1F4F00368AEE1807B1F7CE01019668 31 | :1001E00009D18091C00087FFF3CF6091C600CE0196 32 | :1001F00001963DD000E010E0E7CFCF92DF92EF9282 33 | :10020000FF920F93CF93DF937C016B01EA01FB0117 34 | :1002100040815181FC016081718109812881CE0179 35 | :1002200002D1F60140815181F7016081718120E0A6 36 | :1002300030E0CE01DF91CF910F91FF90EF90DF90F2 37 | :10024000CF900EC1EF92FF920F931F93FC01718329 38 | :10025000608353834283318B208B138B028BF58A0F 39 | :10026000E48A04961F910F91FF90EF9072C1CF9294 40 | :10027000DF92EF92FF920F931F93CF93DF93CDB74F 41 | :10028000DEB765970FB6F894DEBF0FBECDBF7C0119 42 | :10029000AE01455F5F4F04966AD1882319F18F77CD 43 | :1002A00081F1AE01455F5F4FB7016E5F7F4FC701C0 44 | :1002B000A4DFD70152960D911C9153975496CD907F 45 | :1002C000DC90559712964D915C9113976D917C91AE 46 | :1002D000CE010B9612D1A601BC01F701808191815C 47 | :1002E000F801099565960FB6F894DEBF0FBECDBF35 48 | :1002F000DF91CF911F910F91FF90EF90DF90CF9002 49 | :1003000008954F8558896D857E852B85CE01019690 50 | :10031000DAD14F8558896D857E85CE0101960BD245 51 | :100320004F8558896D857E85CE010196E1D11B856B 52 | :10033000D70112964D915C9113976D917C910C852C 53 | :1003400020E0CE010B966FD04F8558896D857E8554 54 | :10035000212FCE01419633D28981882309F441C0EF 55 | :100360008130A1F1AE01455F5F4FB7016E5F7F4FF6 56 | :10037000C70143DF8B8581119CCFCE0141962DD2E1 57 | :10038000D70112964D915C9113976D917C919C01D0 58 | :10039000CE010B9665D04F8558896D857E85CE013F 59 | :1003A00041962FD2F701448955890088F189E02DC3 60 | :1003B000BE016F5E7F4FCE01019609954F8558892A 61 | :1003C0006D857E85CE0141962AD273CF8E819F8125 62 | :1003D0002C893D892817390708F4C4CF9D8B8C8B55 63 | :1003E000C9CF81E08A8B1B8AF701828193819D8B23 64 | :1003F0008C8BC0CF04965DC10F931F93CF93DF9377 65 | :10040000EC012397CE014FD2823031F0CE01DF9143 66 | :10041000CF911F910F9108952197CE01DF91CF9138 67 | :100420001F910F9108950F93CF93DF93EC012883D1 68 | :1004300009834430510550F480E090E09D838C8323 69 | :100440001B821A82DF91CF910F910895CA01D4DFE8 70 | :100450009D838C831B821A82DF91CF910F91089527 71 | :10046000EF92FF920F931F93CF93DF93FC01848150 72 | :1004700095818217930719F121153105A1F08901A2 73 | :10048000CA017B01EF0144305105A8F580E090E0FE 74 | :1004900080179107E0F480E0DF91CF911F910F91D9 75 | :1004A000FF90EF900895158214821382128281E0EA 76 | :1004B000DF91CF911F910F91FF90EF90089581E010 77 | :1004C000DF91CF911F910F91FF90EF9008951D83C1 78 | :1004D0000C83C801E8D1682F70E06E5F7F4F6E0D0E 79 | :1004E0007F1D7B836A8381E0DF91CF911F910F9104 80 | :1004F000FF90EF90089580DFCBCFEF92FF920F93A4 81 | :100500001F93CF93DF93EC018B018881806DFB01FA 82 | :100510008083898181836E5F7F4F8C819D81E0D153 83 | :100520000C80DD81C02D2296C80FD11D7801EC0E04 84 | :10053000FD1E20E04BEABE01C8019DD1F70180837A 85 | :10054000CE010196DF91CF911F910F91FF90EF9017 86 | :100550000895FC0171876087538742871082138258 87 | :1005600011821282178216821582148208951F93B7 88 | :10057000CF93DF93EC01162F8881823009F47BC082 89 | :1005800028F18530D1F1863009F47FC0833009F439 90 | :100590005CC0843059F52C813D818A859B8582170A 91 | :1005A000930768F482E818821B8219821A821F82DC 92 | :1005B0001E821D821C82DF91CF911F91089585E0DC 93 | :1005C000888380E0DF91CF911F910895882309F4FB 94 | :1005D00047C08130F1F6698382E088834B816BEA02 95 | :1005E000812F3CD18B8388818430A9F2873059F1E7 96 | :1005F00080E0DF91CF911F910895288539858E8104 97 | :100600009F81AC014F5F5F4F5F834E83F901E80F1D 98 | :10061000F91F60832E813F818C819D8128173907C6 99 | :10062000E9F686E08883DACF8A81FA0180838981BE 100 | :1006300081838C819D8195838483892BA9F1888511 101 | :1006400099859383828380E8AECFE885F9856183BD 102 | :100650008885998534D19D838C8384E08883BECF3F 103 | :10066000862F807F803D21F6862F87708A8381E0E8 104 | :10067000888340E0B4CF862F1DD1813059F0E885C2 105 | :10068000F985108383E08883A9CF8B81681761F295 106 | :1006900083E889CF812F90E09D838C83892B09F497 107 | :1006A000C0CF84E088839ACFFA011382128280E857 108 | :1006B0007ACFFC0110821382118212821782168275 109 | :1006C000158214820895FC012083213059F44830AA 110 | :1006D000510548F414821382128211821682158207 111 | :1006E0001086178208954750510954834383F4CFED 112 | :1006F0000F931F93CF93DF93FC018081813031F4FE 113 | :1007000083819481009739F412821182DF91CF9115 114 | :100710001F910F9108958B01EF01C5D0682F70E0F4 115 | :100720006F5F7F4F600F711F7A836983DF91CF9175 116 | :100730001F910F910895EF92FF920F931F93CF9304 117 | :10074000DF93FC012081213089F57B018C01FB01C5 118 | :100750002191CF01F801218712821182B0D0F801D6 119 | :1007600094838383A0D0F801C381D4812196C80FDC 120 | :10077000D11DC7018C0F9D1FA2D0F80190878783E0 121 | :1007800092D0C80FD11DC7018C0F9D1F98D0F801C2 122 | :100790009683858388D09E01280F311DC901DF9182 123 | :1007A000CF911F910F91FF90EF90089580E090E01E 124 | :1007B000DF91CF911F910F91FF90EF900895FC0171 125 | :1007C0002083213049F44230510520F04150510935 126 | :1007D00054834383128211820895FC0120812223D5 127 | :1007E00049F0213021F4FC0183819481089580E057 128 | :1007F00090E00895FC018381948155D090E00196AA 129 | :100800000895FC018081813031F483819481892BAA 130 | :1008100019F4128211820895728361830895DC01B4 131 | :100820002C91222349F0213021F4FC018381948111 132 | :10083000089580E090E00895FC018181B0E18B9FF4 133 | :10084000C001112492819F70892BDB018D93BD0122 134 | :100850008381948145D090E001960895842798E0A3 135 | :1008600004C086958627915029F080FDFACF8695A1 136 | :100870009150D9F708956115710599F0FC01680F41 137 | :10088000791F8191282798E004C02695242791504C 138 | :1008900029F020FDFACF26959150D9F7E617F707F2 139 | :1008A00081F7822F08958038910510F082E0089535 140 | :1008B00081E00895881F8827881F8F5F0895FC01B5 141 | :1008C000208127FD03C0822F90E008952F7730E02C 142 | :1008D000322F2227FC018181A901482BCA010895EA 143 | :1008E0008038910530F09068FB019083818382E02D 144 | :0E08F0000895FB01808381E00895F894FFCF06 145 | :00000001FF 146 | -------------------------------------------------------------------------------- /firmware/arduino/dist/firmware_8mhz_19200bps.hex: -------------------------------------------------------------------------------- 1 | :1000000033C0000041C000003FC000003DC0000000 2 | :100010003BC0000039C0000037C0000035C0000000 3 | :1000200033C0000031C000002FC000002DC0000010 4 | :100030002BC0000029C0000027C0000025C0000020 5 | :1000400023C0000021C000001FC000001DC0000030 6 | :100050001BC0000019C0000017C0000015C0000040 7 | :1000600013C0000011C0000011241FBECFEFD8E064 8 | :10007000DEBFCDBF23E0A0E0B1E001C01D92A03003 9 | :10008000B207E1F77AD039C4BBCF6115710569F0C9 10 | :10009000FC01680F791F81919091C00095FFFCCF02 11 | :1000A0008093C600E617F707B1F70895EF92FF9225 12 | :1000B0001F93CF93DF93FC018081813009F047C00B 13 | :1000C0002A98C581D68183819481278130852C0F20 14 | :1000D0003D1F2817390708F442C0232BA9F140E03F 15 | :1000E00050E01FEF48175907C0F5A181B281A40F56 16 | :1000F000B51F8C918EBD0DB407FEFDCFA781B085D5 17 | :10010000209799F04A175B0780F02EB5CA018A1B29 18 | :100110009B0BDB011196ED90FC901297D701A80F75 19 | :10012000B91F2C932197A781B0854F5F5F4F8381C3 20 | :100130009481258136812A0F3B1F2817390708F43F 21 | :100140009C014217530770F2818580FF2A9ADF9144 22 | :10015000CF911F91FF90EF9008951EBDCCCF9C01D1 23 | :10016000BCCF229A2A9A239A259A8DB58E7F8DBD6F 24 | :1001700081E08DBD80E58CBD0895CF93DF93CDB731 25 | :10018000DEB766970FB6F894DEBF0FBECDBF1092F4 26 | :10019000C50089E18093C40086E08093C20080910D 27 | :1001A000C10088618093C100DCDFE12CF12C05E403 28 | :1001B00010E026E530E040E052E060E071E0CE0182 29 | :1001C000019640D000E010E08091C00087FD0DC096 30 | :1001D0000F5F1F4F00368AEE1807B1F7CE01019668 31 | :1001E00009D18091C00087FFF3CF6091C600CE0196 32 | :1001F00001963DD000E010E0E7CFCF92DF92EF9282 33 | :10020000FF920F93CF93DF937C016B01EA01FB0117 34 | :1002100040815181FC016081718109812881CE0179 35 | :1002200002D1F60140815181F7016081718120E0A6 36 | :1002300030E0CE01DF91CF910F91FF90EF90DF90F2 37 | :10024000CF900EC1EF92FF920F931F93FC01718329 38 | :10025000608353834283318B208B138B028BF58A0F 39 | :10026000E48A04961F910F91FF90EF9072C1CF9294 40 | :10027000DF92EF92FF920F931F93CF93DF93CDB74F 41 | :10028000DEB765970FB6F894DEBF0FBECDBF7C0119 42 | :10029000AE01455F5F4F04966AD1882319F18F77CD 43 | :1002A00081F1AE01455F5F4FB7016E5F7F4FC701C0 44 | :1002B000A4DFD70152960D911C9153975496CD907F 45 | :1002C000DC90559712964D915C9113976D917C91AE 46 | :1002D000CE010B9612D1A601BC01F701808191815C 47 | :1002E000F801099565960FB6F894DEBF0FBECDBF35 48 | :1002F000DF91CF911F910F91FF90EF90DF90CF9002 49 | :1003000008954F8558896D857E852B85CE01019690 50 | :10031000DAD14F8558896D857E85CE0101960BD245 51 | :100320004F8558896D857E85CE010196E1D11B856B 52 | :10033000D70112964D915C9113976D917C910C852C 53 | :1003400020E0CE010B966FD04F8558896D857E8554 54 | :10035000212FCE01419633D28981882309F441C0EF 55 | :100360008130A1F1AE01455F5F4FB7016E5F7F4FF6 56 | :10037000C70143DF8B8581119CCFCE0141962DD2E1 57 | :10038000D70112964D915C9113976D917C919C01D0 58 | :10039000CE010B9665D04F8558896D857E85CE013F 59 | :1003A00041962FD2F701448955890088F189E02DC3 60 | :1003B000BE016F5E7F4FCE01019609954F8558892A 61 | :1003C0006D857E85CE0141962AD273CF8E819F8125 62 | :1003D0002C893D892817390708F4C4CF9D8B8C8B55 63 | :1003E000C9CF81E08A8B1B8AF701828193819D8B23 64 | :1003F0008C8BC0CF04965DC10F931F93CF93DF9377 65 | :10040000EC012397CE014FD2823031F0CE01DF9143 66 | :10041000CF911F910F9108952197CE01DF91CF9138 67 | :100420001F910F9108950F93CF93DF93EC012883D1 68 | :1004300009834430510550F480E090E09D838C8323 69 | :100440001B821A82DF91CF910F910895CA01D4DFE8 70 | :100450009D838C831B821A82DF91CF910F91089527 71 | :10046000EF92FF920F931F93CF93DF93FC01848150 72 | :1004700095818217930719F121153105A1F08901A2 73 | :10048000CA017B01EF0144305105A8F580E090E0FE 74 | :1004900080179107E0F480E0DF91CF911F910F91D9 75 | :1004A000FF90EF900895158214821382128281E0EA 76 | :1004B000DF91CF911F910F91FF90EF90089581E010 77 | :1004C000DF91CF911F910F91FF90EF9008951D83C1 78 | :1004D0000C83C801E8D1682F70E06E5F7F4F6E0D0E 79 | :1004E0007F1D7B836A8381E0DF91CF911F910F9104 80 | :1004F000FF90EF90089580DFCBCFEF92FF920F93A4 81 | :100500001F93CF93DF93EC018B018881806DFB01FA 82 | :100510008083898181836E5F7F4F8C819D81E0D153 83 | :100520000C80DD81C02D2296C80FD11D7801EC0E04 84 | :10053000FD1E20E04BEABE01C8019DD1F70180837A 85 | :10054000CE010196DF91CF911F910F91FF90EF9017 86 | :100550000895FC0171876087538742871082138258 87 | :1005600011821282178216821582148208951F93B7 88 | :10057000CF93DF93EC01162F8881823009F47BC082 89 | :1005800028F18530D1F1863009F47FC0833009F439 90 | :100590005CC0843059F52C813D818A859B8582170A 91 | :1005A000930768F482E818821B8219821A821F82DC 92 | :1005B0001E821D821C82DF91CF911F91089585E0DC 93 | :1005C000888380E0DF91CF911F910895882309F4FB 94 | :1005D00047C08130F1F6698382E088834B816BEA02 95 | :1005E000812F3CD18B8388818430A9F2873059F1E7 96 | :1005F00080E0DF91CF911F910895288539858E8104 97 | :100600009F81AC014F5F5F4F5F834E83F901E80F1D 98 | :10061000F91F60832E813F818C819D8128173907C6 99 | :10062000E9F686E08883DACF8A81FA0180838981BE 100 | :1006300081838C819D8195838483892BA9F1888511 101 | :1006400099859383828380E8AECFE885F9856183BD 102 | :100650008885998534D19D838C8384E08883BECF3F 103 | :10066000862F807F803D21F6862F87708A8381E0E8 104 | :10067000888340E0B4CF862F1DD1813059F0E885C2 105 | :10068000F985108383E08883A9CF8B81681761F295 106 | :1006900083E889CF812F90E09D838C83892B09F497 107 | :1006A000C0CF84E088839ACFFA011382128280E857 108 | :1006B0007ACFFC0110821382118212821782168275 109 | :1006C000158214820895FC012083213059F44830AA 110 | :1006D000510548F414821382128211821682158207 111 | :1006E0001086178208954750510954834383F4CFED 112 | :1006F0000F931F93CF93DF93FC018081813031F4FE 113 | :1007000083819481009739F412821182DF91CF9115 114 | :100710001F910F9108958B01EF01C5D0682F70E0F4 115 | :100720006F5F7F4F600F711F7A836983DF91CF9175 116 | :100730001F910F910895EF92FF920F931F93CF9304 117 | :10074000DF93FC012081213089F57B018C01FB01C5 118 | :100750002191CF01F801218712821182B0D0F801D6 119 | :1007600094838383A0D0F801C381D4812196C80FDC 120 | :10077000D11DC7018C0F9D1FA2D0F80190878783E0 121 | :1007800092D0C80FD11DC7018C0F9D1F98D0F801C2 122 | :100790009683858388D09E01280F311DC901DF9182 123 | :1007A000CF911F910F91FF90EF90089580E090E01E 124 | :1007B000DF91CF911F910F91FF90EF900895FC0171 125 | :1007C0002083213049F44230510520F04150510935 126 | :1007D00054834383128211820895FC0120812223D5 127 | :1007E00049F0213021F4FC0183819481089580E057 128 | :1007F00090E00895FC018381948155D090E00196AA 129 | :100800000895FC018081813031F483819481892BAA 130 | :1008100019F4128211820895728361830895DC01B4 131 | :100820002C91222349F0213021F4FC018381948111 132 | :10083000089580E090E00895FC018181B0E18B9FF4 133 | :10084000C001112492819F70892BDB018D93BD0122 134 | :100850008381948145D090E001960895842798E0A3 135 | :1008600004C086958627915029F080FDFACF8695A1 136 | :100870009150D9F708956115710599F0FC01680F41 137 | :10088000791F8191282798E004C02695242791504C 138 | :1008900029F020FDFACF26959150D9F7E617F707F2 139 | :1008A00081F7822F08958038910510F082E0089535 140 | :1008B00081E00895881F8827881F8F5F0895FC01B5 141 | :1008C000208127FD03C0822F90E008952F7730E02C 142 | :1008D000322F2227FC018181A901482BCA010895EA 143 | :1008E0008038910530F09068FB019083818382E02D 144 | :0E08F0000895FB01808381E00895F894FFCF06 145 | :00000001FF 146 | -------------------------------------------------------------------------------- /firmware/arduino/dist/firmware_8mhz_38400bps.hex: -------------------------------------------------------------------------------- 1 | :1000000033C0000041C000003FC000003DC0000000 2 | :100010003BC0000039C0000037C0000035C0000000 3 | :1000200033C0000031C000002FC000002DC0000010 4 | :100030002BC0000029C0000027C0000025C0000020 5 | :1000400023C0000021C000001FC000001DC0000030 6 | :100050001BC0000019C0000017C0000015C0000040 7 | :1000600013C0000011C0000011241FBECFEFD8E064 8 | :10007000DEBFCDBF23E0A0E0B1E001C01D92A03003 9 | :10008000B207E1F77AD039C4BBCF6115710569F0C9 10 | :10009000FC01680F791F81919091C00095FFFCCF02 11 | :1000A0008093C600E617F707B1F70895EF92FF9225 12 | :1000B0001F93CF93DF93FC018081813009F047C00B 13 | :1000C0002A98C581D68183819481278130852C0F20 14 | :1000D0003D1F2817390708F442C0232BA9F140E03F 15 | :1000E00050E01FEF48175907C0F5A181B281A40F56 16 | :1000F000B51F8C918EBD0DB407FEFDCFA781B085D5 17 | :10010000209799F04A175B0780F02EB5CA018A1B29 18 | :100110009B0BDB011196ED90FC901297D701A80F75 19 | :10012000B91F2C932197A781B0854F5F5F4F8381C3 20 | :100130009481258136812A0F3B1F2817390708F43F 21 | :100140009C014217530770F2818580FF2A9ADF9144 22 | :10015000CF911F91FF90EF9008951EBDCCCF9C01D1 23 | :10016000BCCF229A2A9A239A259A8DB58E7F8DBD6F 24 | :1001700081E08DBD80E58CBD0895CF93DF93CDB731 25 | :10018000DEB766970FB6F894DEBF0FBECDBF1092F4 26 | :10019000C5008CE08093C40086E08093C20080910B 27 | :1001A000C10088618093C100DCDFE12CF12C05E403 28 | :1001B00010E026E530E040E052E060E071E0CE0182 29 | :1001C000019640D000E010E08091C00087FD0DC096 30 | :1001D0000F5F1F4F00368AEE1807B1F7CE01019668 31 | :1001E00009D18091C00087FFF3CF6091C600CE0196 32 | :1001F00001963DD000E010E0E7CFCF92DF92EF9282 33 | :10020000FF920F93CF93DF937C016B01EA01FB0117 34 | :1002100040815181FC016081718109812881CE0179 35 | :1002200002D1F60140815181F7016081718120E0A6 36 | :1002300030E0CE01DF91CF910F91FF90EF90DF90F2 37 | :10024000CF900EC1EF92FF920F931F93FC01718329 38 | :10025000608353834283318B208B138B028BF58A0F 39 | :10026000E48A04961F910F91FF90EF9072C1CF9294 40 | :10027000DF92EF92FF920F931F93CF93DF93CDB74F 41 | :10028000DEB765970FB6F894DEBF0FBECDBF7C0119 42 | :10029000AE01455F5F4F04966AD1882319F18F77CD 43 | :1002A00081F1AE01455F5F4FB7016E5F7F4FC701C0 44 | :1002B000A4DFD70152960D911C9153975496CD907F 45 | :1002C000DC90559712964D915C9113976D917C91AE 46 | :1002D000CE010B9612D1A601BC01F701808191815C 47 | :1002E000F801099565960FB6F894DEBF0FBECDBF35 48 | :1002F000DF91CF911F910F91FF90EF90DF90CF9002 49 | :1003000008954F8558896D857E852B85CE01019690 50 | :10031000DAD14F8558896D857E85CE0101960BD245 51 | :100320004F8558896D857E85CE010196E1D11B856B 52 | :10033000D70112964D915C9113976D917C910C852C 53 | :1003400020E0CE010B966FD04F8558896D857E8554 54 | :10035000212FCE01419633D28981882309F441C0EF 55 | :100360008130A1F1AE01455F5F4FB7016E5F7F4FF6 56 | :10037000C70143DF8B8581119CCFCE0141962DD2E1 57 | :10038000D70112964D915C9113976D917C919C01D0 58 | :10039000CE010B9665D04F8558896D857E85CE013F 59 | :1003A00041962FD2F701448955890088F189E02DC3 60 | :1003B000BE016F5E7F4FCE01019609954F8558892A 61 | :1003C0006D857E85CE0141962AD273CF8E819F8125 62 | :1003D0002C893D892817390708F4C4CF9D8B8C8B55 63 | :1003E000C9CF81E08A8B1B8AF701828193819D8B23 64 | :1003F0008C8BC0CF04965DC10F931F93CF93DF9377 65 | :10040000EC012397CE014FD2823031F0CE01DF9143 66 | :10041000CF911F910F9108952197CE01DF91CF9138 67 | :100420001F910F9108950F93CF93DF93EC012883D1 68 | :1004300009834430510550F480E090E09D838C8323 69 | :100440001B821A82DF91CF910F910895CA01D4DFE8 70 | :100450009D838C831B821A82DF91CF910F91089527 71 | :10046000EF92FF920F931F93CF93DF93FC01848150 72 | :1004700095818217930719F121153105A1F08901A2 73 | :10048000CA017B01EF0144305105A8F580E090E0FE 74 | :1004900080179107E0F480E0DF91CF911F910F91D9 75 | :1004A000FF90EF900895158214821382128281E0EA 76 | :1004B000DF91CF911F910F91FF90EF90089581E010 77 | :1004C000DF91CF911F910F91FF90EF9008951D83C1 78 | :1004D0000C83C801E8D1682F70E06E5F7F4F6E0D0E 79 | :1004E0007F1D7B836A8381E0DF91CF911F910F9104 80 | :1004F000FF90EF90089580DFCBCFEF92FF920F93A4 81 | :100500001F93CF93DF93EC018B018881806DFB01FA 82 | :100510008083898181836E5F7F4F8C819D81E0D153 83 | :100520000C80DD81C02D2296C80FD11D7801EC0E04 84 | :10053000FD1E20E04BEABE01C8019DD1F70180837A 85 | :10054000CE010196DF91CF911F910F91FF90EF9017 86 | :100550000895FC0171876087538742871082138258 87 | :1005600011821282178216821582148208951F93B7 88 | :10057000CF93DF93EC01162F8881823009F47BC082 89 | :1005800028F18530D1F1863009F47FC0833009F439 90 | :100590005CC0843059F52C813D818A859B8582170A 91 | :1005A000930768F482E818821B8219821A821F82DC 92 | :1005B0001E821D821C82DF91CF911F91089585E0DC 93 | :1005C000888380E0DF91CF911F910895882309F4FB 94 | :1005D00047C08130F1F6698382E088834B816BEA02 95 | :1005E000812F3CD18B8388818430A9F2873059F1E7 96 | :1005F00080E0DF91CF911F910895288539858E8104 97 | :100600009F81AC014F5F5F4F5F834E83F901E80F1D 98 | :10061000F91F60832E813F818C819D8128173907C6 99 | :10062000E9F686E08883DACF8A81FA0180838981BE 100 | :1006300081838C819D8195838483892BA9F1888511 101 | :1006400099859383828380E8AECFE885F9856183BD 102 | :100650008885998534D19D838C8384E08883BECF3F 103 | :10066000862F807F803D21F6862F87708A8381E0E8 104 | :10067000888340E0B4CF862F1DD1813059F0E885C2 105 | :10068000F985108383E08883A9CF8B81681761F295 106 | :1006900083E889CF812F90E09D838C83892B09F497 107 | :1006A000C0CF84E088839ACFFA011382128280E857 108 | :1006B0007ACFFC0110821382118212821782168275 109 | :1006C000158214820895FC012083213059F44830AA 110 | :1006D000510548F414821382128211821682158207 111 | :1006E0001086178208954750510954834383F4CFED 112 | :1006F0000F931F93CF93DF93FC018081813031F4FE 113 | :1007000083819481009739F412821182DF91CF9115 114 | :100710001F910F9108958B01EF01C5D0682F70E0F4 115 | :100720006F5F7F4F600F711F7A836983DF91CF9175 116 | :100730001F910F910895EF92FF920F931F93CF9304 117 | :10074000DF93FC012081213089F57B018C01FB01C5 118 | :100750002191CF01F801218712821182B0D0F801D6 119 | :1007600094838383A0D0F801C381D4812196C80FDC 120 | :10077000D11DC7018C0F9D1FA2D0F80190878783E0 121 | :1007800092D0C80FD11DC7018C0F9D1F98D0F801C2 122 | :100790009683858388D09E01280F311DC901DF9182 123 | :1007A000CF911F910F91FF90EF90089580E090E01E 124 | :1007B000DF91CF911F910F91FF90EF900895FC0171 125 | :1007C0002083213049F44230510520F04150510935 126 | :1007D00054834383128211820895FC0120812223D5 127 | :1007E00049F0213021F4FC0183819481089580E057 128 | :1007F00090E00895FC018381948155D090E00196AA 129 | :100800000895FC018081813031F483819481892BAA 130 | :1008100019F4128211820895728361830895DC01B4 131 | :100820002C91222349F0213021F4FC018381948111 132 | :10083000089580E090E00895FC018181B0E18B9FF4 133 | :10084000C001112492819F70892BDB018D93BD0122 134 | :100850008381948145D090E001960895842798E0A3 135 | :1008600004C086958627915029F080FDFACF8695A1 136 | :100870009150D9F708956115710599F0FC01680F41 137 | :10088000791F8191282798E004C02695242791504C 138 | :1008900029F020FDFACF26959150D9F7E617F707F2 139 | :1008A00081F7822F08958038910510F082E0089535 140 | :1008B00081E00895881F8827881F8F5F0895FC01B5 141 | :1008C000208127FD03C0822F90E008952F7730E02C 142 | :1008D000322F2227FC018181A901482BCA010895EA 143 | :1008E0008038910530F09068FB019083818382E02D 144 | :0E08F0000895FB01808381E00895F894FFCF06 145 | :00000001FF 146 | -------------------------------------------------------------------------------- /firmware/arduino/dist/firmware_8mhz_500000bps.hex: -------------------------------------------------------------------------------- 1 | :1000000033C0000041C000003FC000003DC0000000 2 | :100010003BC0000039C0000037C0000035C0000000 3 | :1000200033C0000031C000002FC000002DC0000010 4 | :100030002BC0000029C0000027C0000025C0000020 5 | :1000400023C0000021C000001FC000001DC0000030 6 | :100050001BC0000019C0000017C0000015C0000040 7 | :1000600013C0000011C0000011241FBECFEFD8E064 8 | :10007000DEBFCDBF23E0A0E0B1E001C01D92A03003 9 | :10008000B207E1F77AD038C4BBCF6115710569F0CA 10 | :10009000FC01680F791F81919091C00095FFFCCF02 11 | :1000A0008093C600E617F707B1F70895EF92FF9225 12 | :1000B0001F93CF93DF93FC018081813009F047C00B 13 | :1000C0002A98C581D68183819481278130852C0F20 14 | :1000D0003D1F2817390708F442C0232BA9F140E03F 15 | :1000E00050E01FEF48175907C0F5A181B281A40F56 16 | :1000F000B51F8C918EBD0DB407FEFDCFA781B085D5 17 | :10010000209799F04A175B0780F02EB5CA018A1B29 18 | :100110009B0BDB011196ED90FC901297D701A80F75 19 | :10012000B91F2C932197A781B0854F5F5F4F8381C3 20 | :100130009481258136812A0F3B1F2817390708F43F 21 | :100140009C014217530770F2818580FF2A9ADF9144 22 | :10015000CF911F91FF90EF9008951EBDCCCF9C01D1 23 | :10016000BCCF229A2A9A239A259A8DB58E7F8DBD6F 24 | :1001700081E08DBD80E58CBD0895CF93DF93CDB731 25 | :10018000DEB766970FB6F894DEBF0FBECDBF1092F4 26 | :10019000C5001092C40086E08093C2008091C10027 27 | :1001A00088618093C100DDDFE12CF12C05E410E0D3 28 | :1001B00026E530E040E052E060E071E0CE010196DB 29 | :1001C00040D000E010E08091C00087FD0DC00F5FBF 30 | :1001D0001F4F00368AEE1807B1F7CE01019609D1FC 31 | :1001E0008091C00087FFF3CF6091C600CE010196D9 32 | :1001F0003DD000E010E0E7CFCF92DF92EF92FF9288 33 | :100200000F93CF93DF937C016B01EA01FB014081E7 34 | :100210005181FC016081718109812881CE0102D167 35 | :10022000F60140815181F7016081718120E030E069 36 | :10023000CE01DF91CF910F91FF90EF90DF90CF90A3 37 | :100240000EC1EF92FF920F931F93FC0171836083A5 38 | :1002500053834283318B208B138B028BF58AE48A84 39 | :1002600004961F910F91FF90EF9072C1CF92DF9291 40 | :10027000EF92FF920F931F93CF93DF93CDB7DEB72B 41 | :1002800065970FB6F894DEBF0FBECDBF7C01AE01FF 42 | :10029000455F5F4F04966AD1882319F18F7781F10A 43 | :1002A000AE01455F5F4FB7016E5F7F4FC701A4DFAF 44 | :1002B000D70152960D911C9153975496CD90DC9096 45 | :1002C000559712964D915C9113976D917C91CE014B 46 | :1002D0000B9612D1A601BC01F70180819181F80132 47 | :1002E000099565960FB6F894DEBF0FBECDBFDF91BE 48 | :1002F000CF911F910F91FF90EF90DF90CF900895D5 49 | :100300004F8558896D857E852B85CE010196DAD182 50 | :100310004F8558896D857E85CE0101960BD24F851C 51 | :1003200058896D857E85CE010196E1D11B85D70167 52 | :1003300012964D915C9113976D917C910C8520E004 53 | :10034000CE010B966FD04F8558896D857E85212F04 54 | :10035000CE01419633D28981882309F441C081308E 55 | :10036000A1F1AE01455F5F4FB7016E5F7F4FC701DF 56 | :1003700043DF8B8581119CCFCE0141962DD2D701D1 57 | :1003800012964D915C9113976D917C919C01CE01D9 58 | :100390000B9665D04F8558896D857E85CE01419637 59 | :1003A0002FD2F701448955890088F189E02DBE01DB 60 | :1003B0006F5E7F4FCE01019609954F8558896D85F7 61 | :1003C0007E85CE0141962AD273CF8E819F812C8962 62 | :1003D0003D892817390708F4C4CF9D8B8C8BC9CF72 63 | :1003E00081E08A8B1B8AF701828193819D8B8C8BA4 64 | :1003F000C0CF04965DC10F931F93CF93DF93EC01A1 65 | :100400002397CE014FD2823031F0CE01DF91CF91D0 66 | :100410001F910F9108952197CE01DF91CF911F91E8 67 | :100420000F9108950F93CF93DF93EC0128830983F5 68 | :100430004430510550F480E090E09D838C831B8212 69 | :100440001A82DF91CF910F910895CA01D4DF9D8365 70 | :100450008C831B821A82DF91CF910F910895EF92C6 71 | :10046000FF920F931F93CF93DF93FC0184819581BB 72 | :100470008217930719F121153105A1F08901CA01ED 73 | :100480007B01EF0144305105A8F580E090E0801732 74 | :100490009107E0F480E0DF91CF911F910F91FF90E1 75 | :1004A000EF900895158214821382128281E0DF9109 76 | :1004B000CF911F910F91FF90EF90089581E0DF9110 77 | :1004C000CF911F910F91FF90EF9008951D830C83A2 78 | :1004D000C801E8D1682F70E06E5F7F4F6E0D7F1D01 79 | :1004E0007B836A8381E0DF91CF911F910F91FF9011 80 | :1004F000EF90089580DFCBCFEF92FF920F931F9381 81 | :10050000CF93DF93EC018B018881806DFB018083A9 82 | :10051000898181836E5F7F4F8C819D81E0D10C80CA 83 | :10052000DD81C02D2296C80FD11D7801EC0EFD1E75 84 | :1005300020E04BEABE01C8019DD1F7018083CE01C6 85 | :100540000196DF91CF911F910F91FF90EF90089549 86 | :10055000FC01718760875387428710821382118262 87 | :100560001282178216821582148208951F93CF93E8 88 | :10057000DF93EC01162F8881823009F47BC028F1CB 89 | :100580008530D1F1863009F47FC0833009F45CC036 90 | :10059000843059F52C813D818A859B85821793078C 91 | :1005A00068F482E818821B8219821A821F821E82D6 92 | :1005B0001D821C82DF91CF911F91089585E0888371 93 | :1005C00080E0DF91CF911F910895882309F447C0FF 94 | :1005D0008130F1F6698382E088834B816BEA812F59 95 | :1005E0003CD18B8388818430A9F2873059F180E037 96 | :1005F000DF91CF911F910895288539858E819F8144 97 | :10060000AC014F5F5F4F5F834E83F901E80FF91F25 98 | :1006100060832E813F818C819D8128173907E9F6FF 99 | :1006200086E08883DACF8A81FA0180838981818399 100 | :100630008C819D8195838483892BA9F188859985F7 101 | :100640009383828380E8AECFE885F98561838885CE 102 | :10065000998534D19D838C8384E08883BECF862F97 103 | :10066000807F803D21F6862F87708A8381E0888392 104 | :1006700040E0B4CF862F1DD1813059F0E885F9854F 105 | :10068000108383E08883A9CF8B81681761F283E8A8 106 | :1006900089CF812F90E09D838C83892B09F4C0CF73 107 | :1006A00084E088839ACFFA011382128280E87ACF9D 108 | :1006B000FC01108213821182128217821682158227 109 | :1006C00014820895FC012083213059F448305105EB 110 | :1006D00048F41482138212821182168215821086C7 111 | :1006E000178208954750510954834383F4CF0F93E1 112 | :1006F0001F93CF93DF93FC018081813031F483819C 113 | :100700009481009739F412821182DF91CF911F9169 114 | :100710000F9108958B01EF01C5D0682F70E06F5FD6 115 | :100720007F4F600F711F7A836983DF91CF911F9193 116 | :100730000F910895EF92FF920F931F93CF93DF9342 117 | :10074000FC012081213089F57B018C01FB01219185 118 | :10075000CF01F801218712821182B0D0F801948371 119 | :100760008383A0D0F801C381D4812196C80FD11D05 120 | :10077000C7018C0F9D1FA2D0F8019087878392D06C 121 | :10078000C80FD11DC7018C0F9D1F98D0F80196830B 122 | :10079000858388D09E01280F311DC901DF91CF913B 123 | :1007A0001F910F91FF90EF90089580E090E0DF910E 124 | :1007B000CF911F910F91FF90EF900895FC0120833E 125 | :1007C000213049F44230510520F041505109548301 126 | :1007D0004383128211820895FC012081222349F073 127 | :1007E000213021F4FC0183819481089580E090E020 128 | :1007F0000895FC018381948155D090E0019608957D 129 | :10080000FC018081813031F483819481892B19F43A 130 | :10081000128211820895728361830895DC012C9104 131 | :10082000222349F0213021F4FC0183819481089531 132 | :1008300080E090E00895FC018181B0E18B9FC001D0 133 | :10084000112492819F70892BDB018D93BD018381DF 134 | :10085000948145D090E001960895842798E004C0E3 135 | :1008600086958627915029F080FDFACF8695915084 136 | :10087000D9F708956115710599F0FC01680F791F8A 137 | :100880008191282798E004C026952427915029F0CB 138 | :1008900020FDFACF26959150D9F7E617F70781F793 139 | :1008A000822F08958038910510F082E0089581E04C 140 | :1008B0000895881F8827881F8F5F0895FC01208175 141 | :1008C00027FD03C0822F90E008952F7730E0322F6C 142 | :1008D0002227FC018181A901482BCA010895803893 143 | :1008E000910530F09068FB019083818382E0089548 144 | :0C08F000FB01808381E00895F894FFCFA5 145 | :00000001FF 146 | -------------------------------------------------------------------------------- /firmware/arduino/src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * main.c 3 | * 4 | * Created on: 27 lut 2022 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #include 9 | 10 | #include "common/protocol.h" 11 | 12 | #include "firmware/programmer.h" 13 | 14 | #ifndef NULL 15 | #define NULL ((void *) 0) 16 | #endif 17 | 18 | #define CONCAT_MACRO(port, letter) port ## letter 19 | #define DECLARE_PORT(port) CONCAT_MACRO(PORT, port) 20 | #define DECLARE_DDR(port) CONCAT_MACRO(DDR, port) 21 | #define DECLARE_PIN(port) CONCAT_MACRO(PIN, port) 22 | 23 | #define PIO_SET_HIGH(bank, pin) { DECLARE_PORT(bank) |= _BV(DECLARE_PIN(pin)); } 24 | #define PIO_SET_LOW(bank, pin) { DECLARE_PORT(bank) &= ~_BV(DECLARE_PIN(pin)); } 25 | 26 | #define PIO_TOGGLE(bank, pin) { DECLARE_PORT(bank) ^= _BV(DECLARE_PIN(pin)); } 27 | 28 | #define PIO_IS_HIGH(bank, pin) (bit_is_set(DECLARE_PIN(bank), DECLARE_PIN(pin))) 29 | #define PIO_IS_LOW(bank, pin) (! bit_is_set(DECLARE_PIN(bank), DECLARE_PIN(pin))) 30 | 31 | #define PIO_SET_OUTPUT(bank, pin) { DECLARE_DDR(bank) |= _BV(DECLARE_PIN(pin)); } 32 | #define PIO_SET_INPUT(bank, pin) { DECLARE_DDR(bank) &= ~(_BV(DECLARE_PIN(pin))); } 33 | 34 | /************************ 35 | * UART 36 | */ 37 | 38 | #if BAUD >= 1000000 39 | #if F_CPU == 8000000 40 | #define F_CPU_PRESCALER 8 41 | #else 42 | #define F_CPU_PRESCALER 16 43 | #endif 44 | #else 45 | #define F_CPU_PRESCALER 16 46 | #endif 47 | 48 | 49 | #define UART_BAUD_REG (((F_CPU / F_CPU_PRESCALER) / BAUD) - 1) 50 | 51 | #define _waitForTransmit() while (! (UCSR0A & _BV(UDRE0))); 52 | 53 | void uart_initialize() { 54 | // Configure usart 55 | UBRR0H = ((UART_BAUD_REG) >> 8); 56 | UBRR0L = ((UART_BAUD_REG) & 0x00FF); 57 | 58 | #if F_CPU_PRESCALER == 8 59 | UCSR0A |= _BV(U2X0); 60 | #endif 61 | 62 | // 8bit, 1bit stop, no parity 63 | UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); 64 | 65 | // enable 66 | UCSR0B |= (_BV(TXEN0) | _BV(RXEN0)); 67 | } 68 | 69 | 70 | void uart_send(char c) { 71 | _waitForTransmit(); 72 | 73 | UDR0 = c; 74 | } 75 | 76 | 77 | char uart_poll() { 78 | if (UCSR0A & _BV(RXC0)) { 79 | return 1; 80 | } 81 | 82 | return 0; 83 | } 84 | 85 | 86 | uint8_t uart_get() { 87 | return UDR0; 88 | } 89 | 90 | 91 | /************************ 92 | * SPI 93 | */ 94 | 95 | #define SPI_CS_BANK B 96 | #define SPI_CS_PIN 2 97 | 98 | #define CS_LOW() PIO_SET_LOW(SPI_CS_BANK, SPI_CS_PIN) 99 | #define CS_HIGH() PIO_SET_HIGH(SPI_CS_BANK, SPI_CS_PIN) 100 | 101 | #define SPI_WAIT() { while (! (SPSR & _BV(SPIF))) {} } 102 | 103 | #define _max(x, y) ((x) < (y) ? (y) : (x)) 104 | 105 | void spi_initialize() { 106 | PIO_SET_OUTPUT(SPI_CS_BANK, SPI_CS_PIN); 107 | CS_HIGH(); 108 | 109 | PIO_SET_OUTPUT(B, 3); // MOSI 110 | PIO_SET_OUTPUT(B, 5); // SCK 111 | 112 | // clear double speed 113 | SPSR &= ~_BV(SPI2X); 114 | 115 | // Enable SPI, Master, clock rate f_osc/2, MODE 0 116 | SPSR = _BV(SPI2X); 117 | SPCR = _BV(SPE) | _BV(MSTR); 118 | } 119 | 120 | 121 | static void _programmerRequestCallback(ProtoReq *request, ProtoRes *response, void *callbackData) { 122 | switch (request->cmd) { 123 | case PROTO_CMD_SPI_TRANSFER: 124 | { 125 | ProtoReqTransfer *req = &request->request.transfer; 126 | ProtoResTransfer *res = &response->response.transfer; 127 | 128 | CS_LOW(); 129 | { 130 | uint16_t toRecv = req->rxBufferSize; 131 | 132 | for (uint16_t i = 0; i < _max(req->txBufferSize, req->rxSkipSize + req->rxBufferSize); i++) { 133 | if (i < req->txBufferSize) { 134 | SPDR = req->txBuffer[i]; 135 | 136 | } else { 137 | SPDR = 0xff; 138 | } 139 | 140 | SPI_WAIT(); 141 | 142 | if (toRecv) { 143 | if (i >= req->rxSkipSize) { 144 | res->rxBuffer[i - req->rxSkipSize] = SPDR; 145 | 146 | toRecv--; 147 | } 148 | } 149 | } 150 | } 151 | if ((req->flags & PROTO_SPI_TRANSFER_FLAG_KEEP_CS) == 0) { 152 | CS_HIGH(); 153 | } 154 | } 155 | break; 156 | 157 | default: 158 | break; 159 | } 160 | } 161 | 162 | 163 | static void _programmerResponseCallback(uint8_t *buffer, uint16_t bufferSize, void *callbackData) { 164 | for (uint16_t i = 0; i < bufferSize; i++) { 165 | uart_send(buffer[i]); 166 | } 167 | } 168 | 169 | 170 | #define DATA_BUFFER_SIZE 512 171 | 172 | static uint8_t _dataBuffer[DATA_BUFFER_SIZE]; 173 | 174 | 175 | int main(int argc, char *argv[]) { 176 | Programmer programmer; 177 | 178 | uart_initialize(); 179 | spi_initialize(); 180 | 181 | programmer_setup( 182 | &programmer, 183 | _dataBuffer, 184 | DATA_BUFFER_SIZE, 185 | _programmerRequestCallback, 186 | _programmerResponseCallback, 187 | NULL 188 | ); 189 | 190 | { 191 | uint16_t idleCounter = 0; 192 | 193 | while (1) { 194 | if (! uart_poll()) { 195 | if (++idleCounter == 60000) { 196 | programmer_reset(&programmer); 197 | } 198 | 199 | } else { 200 | idleCounter = 0; 201 | 202 | programmer_putByte(&programmer, uart_get()); 203 | } 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /firmware/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(firmware-common) 4 | 5 | set(src_path "${CMAKE_CURRENT_LIST_DIR}/src/") 6 | set(srcs 7 | ${src_path}/firmware/programmer.c 8 | ) 9 | 10 | set(headers_path "${CMAKE_CURRENT_LIST_DIR}/include/") 11 | set(headers 12 | ${headers_path}/firmware/programmer.h 13 | ) 14 | 15 | add_library(firmware-common ${headers} ${srcs}) 16 | 17 | target_include_directories(firmware-common 18 | PUBLIC 19 | ${headers_path} 20 | PRIVATE 21 | $ 22 | ) -------------------------------------------------------------------------------- /firmware/common/include/firmware/programmer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firmware/programmer.h 3 | * 4 | * Created on: 28 lip 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #include "common/protocol/request.h" 9 | #include "common/protocol/response.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | typedef void (*ProgrammerRequestCallback)(ProtoReq *request, ProtoRes *response, void *callbackData); 16 | typedef void (*ProgrammerResponseCallback)(uint8_t *buffer, uint16_t bufferSize, void *callbackData); 17 | 18 | typedef struct _Programmer { 19 | uint8_t *mem; 20 | uint16_t memSize; 21 | 22 | ProtoPktDes packetDeserializer; 23 | 24 | ProgrammerRequestCallback requestCallback; 25 | ProgrammerResponseCallback responseCallback; 26 | void *callbackData; 27 | } Programmer; 28 | 29 | 30 | void programmer_setup( 31 | Programmer *programmer, 32 | uint8_t *memory, 33 | uint16_t memorySize, 34 | ProgrammerRequestCallback requestCallback, 35 | ProgrammerResponseCallback responseCallback, 36 | void *callbackData 37 | ); 38 | 39 | void programmer_putByte(Programmer *programmer, uint8_t byte); 40 | 41 | void programmer_reset(Programmer *programmer); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | -------------------------------------------------------------------------------- /firmware/common/src/firmware/programmer.c: -------------------------------------------------------------------------------- 1 | #include "common/protocol.h" 2 | 3 | #include "firmware/programmer.h" 4 | 5 | 6 | void programmer_setup( 7 | Programmer *programmer, 8 | uint8_t *memory, 9 | uint16_t memorySize, 10 | ProgrammerRequestCallback requestCallback, 11 | ProgrammerResponseCallback responseCallback, 12 | void *callbackData 13 | ) { 14 | programmer->mem = memory; 15 | programmer->memSize = memorySize; 16 | 17 | programmer->requestCallback = requestCallback; 18 | programmer->responseCallback = responseCallback; 19 | programmer->callbackData = callbackData; 20 | 21 | proto_pkt_dec_setup( 22 | &programmer->packetDeserializer, 23 | programmer->mem, 24 | programmer->memSize 25 | ); 26 | } 27 | 28 | 29 | static void _sendError(Programmer *programmer, ProtoPkt *packet, ProtoRes *response, uint8_t errorCode) { 30 | proto_pkt_init(packet, programmer->mem, programmer->memSize, packet->code, packet->id); 31 | proto_pkt_prepare(packet, programmer->mem, programmer->memSize, 0); 32 | } 33 | 34 | 35 | void programmer_putByte(Programmer *programmer, uint8_t byte) { 36 | ProtoPkt packet; 37 | 38 | uint8_t ret = proto_pkt_dec_putByte(&programmer->packetDeserializer, byte, &packet); 39 | 40 | if (ret != PROTO_PKT_DES_RET_IDLE) { 41 | ProtoRes response; 42 | 43 | do { 44 | ProtoReq request; 45 | uint8_t packetCmd; 46 | 47 | if (PROTO_PKT_DES_RET_GET_ERROR_CODE(ret) != PROTO_NO_ERROR) { 48 | _sendError(programmer, &packet, &response, PROTO_PKT_DES_RET_GET_ERROR_CODE(ret)); 49 | break; 50 | } 51 | 52 | // Parse, assign request to coming packet 53 | proto_req_init (&request, packet.payload, packet.payloadSize, packet.code); 54 | proto_req_decode(&request, packet.payload, packet.payloadSize); 55 | proto_req_assign(&request, packet.payload, packet.payloadSize); 56 | 57 | packetCmd = packet.code; 58 | 59 | // Start preparation of response 60 | proto_pkt_init(&packet, programmer->mem, programmer->memSize, PROTO_NO_ERROR, packet.id); 61 | proto_res_init(&response, packet.payload, packet.payloadSize, packetCmd); 62 | 63 | switch (request.cmd) { 64 | case PROTO_CMD_GET_INFO: 65 | { 66 | ProtoResGetInfo *res = &response.response.getInfo; 67 | 68 | res->version.major = PROTO_VERSION_MAJOR; 69 | res->version.minor = PROTO_VERSION_MINOR; 70 | 71 | res->packetSize = programmer->memSize; 72 | } 73 | break; 74 | 75 | case PROTO_CMD_SPI_TRANSFER: 76 | { 77 | ProtoResTransfer *res = &response.response.transfer; 78 | 79 | if (res->rxBufferSize < request.request.transfer.rxBufferSize) { 80 | _sendError(programmer, &packet, &response, PROTO_ERROR_INVALID_MESSAGE); 81 | 82 | } else { 83 | res->rxBufferSize = request.request.transfer.rxBufferSize; 84 | } 85 | } 86 | break; 87 | 88 | default: 89 | _sendError(programmer, &packet, &response, PROTO_ERROR_INVALID_CMD); 90 | break; 91 | } 92 | 93 | if (packet.code != PROTO_NO_ERROR) { 94 | break; 95 | } 96 | 97 | proto_pkt_prepare(&packet, programmer->mem, programmer->memSize, proto_res_getPayloadSize(&response)); 98 | 99 | proto_res_assign(&response, packet.payload, packet.payloadSize); 100 | { 101 | programmer->requestCallback(&request, &response, programmer->callbackData); 102 | } 103 | proto_res_encode(&response, packet.payload, packet.payloadSize); 104 | 105 | } while (0); 106 | 107 | programmer->responseCallback( 108 | programmer->mem, proto_pkt_encode(&packet, programmer->mem, programmer->memSize), programmer->callbackData 109 | ); 110 | } 111 | } 112 | 113 | 114 | void programmer_reset(Programmer *programmer) { 115 | proto_pkt_dec_reset(&programmer->packetDeserializer); 116 | } 117 | -------------------------------------------------------------------------------- /firmware/rpi-pico/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | set(BASE_DIR ${CMAKE_CURRENT_LIST_DIR}) 4 | 5 | set(PICO_SDK_PATH ${BASE_DIR}/pico-sdk) 6 | 7 | # Pull in SDK (must be before project) 8 | include(${BASE_DIR}/cmake/pico_sdk_import.cmake) 9 | 10 | # include(pico_extras_import_optional.cmake) 11 | 12 | project(firmware_pico C CXX ASM) 13 | set(CMAKE_C_STANDARD 11) 14 | set(CMAKE_CXX_STANDARD 17) 15 | 16 | if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0") 17 | message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") 18 | endif() 19 | 20 | # Initialize the SDK 21 | pico_sdk_init() 22 | 23 | add_compile_options( 24 | -Wall 25 | -Wno-format # int != int32_t as far as the compiler is concerned because gcc has int32_t as long int 26 | -Wno-unused-function # we have some for the docs that aren't called 27 | -Wno-maybe-uninitialized 28 | ) 29 | 30 | add_executable(firmware 31 | ${BASE_DIR}/src/main.c 32 | ${BASE_DIR}/src/usb_descriptors.c 33 | ) 34 | 35 | target_include_directories(firmware PRIVATE 36 | ${BASE_DIR}/include 37 | ) 38 | 39 | target_link_libraries(firmware 40 | PRIVATE 41 | pico_stdlib 42 | hardware_uart 43 | hardware_pio 44 | hardware_spi 45 | tinyusb_device 46 | tinyusb_board 47 | 48 | firmware-common 49 | protocol 50 | ) 51 | 52 | pico_add_extra_outputs(firmware) -------------------------------------------------------------------------------- /firmware/rpi-pico/README.md: -------------------------------------------------------------------------------- 1 | * OS environment preparation 2 | 3 | ``` 4 | sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib 5 | ``` 6 | 7 | * Project initialization 8 | 9 | ``` 10 | git submodule init 11 | git submodule update 12 | cd firmware/rpi-zero/pico-sdk 13 | git submodule update --init 14 | ``` 15 | 16 | ``` 17 | sudo usbreset cafe:4001 18 | ``` 19 | -------------------------------------------------------------------------------- /firmware/rpi-pico/cmake/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | # GIT_SUBMODULES_RECURSE was added in 3.17 33 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 34 | FetchContent_Declare( 35 | pico_sdk 36 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 37 | GIT_TAG master 38 | GIT_SUBMODULES_RECURSE FALSE 39 | ) 40 | else () 41 | FetchContent_Declare( 42 | pico_sdk 43 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 44 | GIT_TAG master 45 | ) 46 | endif () 47 | 48 | if (NOT pico_sdk) 49 | message("Downloading Raspberry Pi Pico SDK") 50 | FetchContent_Populate(pico_sdk) 51 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 52 | endif () 53 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 54 | else () 55 | message(FATAL_ERROR 56 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 57 | ) 58 | endif () 59 | endif () 60 | 61 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 62 | if (NOT EXISTS ${PICO_SDK_PATH}) 63 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 64 | endif () 65 | 66 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 67 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 68 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 69 | endif () 70 | 71 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 72 | 73 | include(${PICO_SDK_INIT_CMAKE_FILE}) 74 | -------------------------------------------------------------------------------- /firmware/rpi-pico/dist/firmware.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bielskij/spi-flash-programmer/120808b3f41fd50b8621a9c16b237c03ae22007a/firmware/rpi-pico/dist/firmware.uf2 -------------------------------------------------------------------------------- /firmware/rpi-pico/include/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | // COMMON CONFIGURATION 35 | //-------------------------------------------------------------------- 36 | 37 | // defined by board.mk 38 | #ifndef CFG_TUSB_MCU 39 | #error CFG_TUSB_MCU must be defined 40 | #endif 41 | 42 | // RHPort number used for device can be defined by board.mk, default to port 0 43 | #ifndef BOARD_DEVICE_RHPORT_NUM 44 | #define BOARD_DEVICE_RHPORT_NUM 0 45 | #endif 46 | 47 | // RHPort max operational speed can defined by board.mk 48 | // Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed 49 | #ifndef BOARD_DEVICE_RHPORT_SPEED 50 | #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ 51 | CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X) 52 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED 53 | #else 54 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED 55 | #endif 56 | #endif 57 | 58 | // Device mode with rhport and speed defined by board.mk 59 | #if BOARD_DEVICE_RHPORT_NUM == 0 60 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 61 | #elif BOARD_DEVICE_RHPORT_NUM == 1 62 | #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 63 | #else 64 | #error "Incorrect RHPort configuration" 65 | #endif 66 | 67 | #ifndef CFG_TUSB_OS 68 | #define CFG_TUSB_OS OPT_OS_NONE 69 | #endif 70 | 71 | // CFG_TUSB_DEBUG is defined by compiler in DEBUG build 72 | // #define CFG_TUSB_DEBUG 0 73 | 74 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 75 | * Tinyusb use follows macros to declare transferring memory so that they can be put 76 | * into those specific section. 77 | * e.g 78 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 79 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 80 | */ 81 | #ifndef CFG_TUSB_MEM_SECTION 82 | #define CFG_TUSB_MEM_SECTION 83 | #endif 84 | 85 | #ifndef CFG_TUSB_MEM_ALIGN 86 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 87 | #endif 88 | 89 | //-------------------------------------------------------------------- 90 | // DEVICE CONFIGURATION 91 | //-------------------------------------------------------------------- 92 | 93 | #ifndef CFG_TUD_ENDPOINT0_SIZE 94 | #define CFG_TUD_ENDPOINT0_SIZE 64 95 | #endif 96 | 97 | //------------- CLASS -------------// 98 | #define CFG_TUD_HID 0 99 | #define CFG_TUD_CDC 1 100 | #define CFG_TUD_MSC 0 101 | #define CFG_TUD_MIDI 0 102 | #define CFG_TUD_VENDOR 0 103 | 104 | // CDC FIFO size of TX and RX 105 | #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 106 | #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 107 | 108 | // CDC Endpoint transfer buffer size, more is faster 109 | #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | 115 | #endif /* _TUSB_CONFIG_H_ */ 116 | -------------------------------------------------------------------------------- /firmware/rpi-pico/src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * main.c 3 | * 4 | * Created on: 29 mar 2023 5 | * Author: bielski.j@gmail.com 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "pico/stdlib.h" 12 | #include "hardware/spi.h" 13 | 14 | #include "tusb.h" 15 | 16 | #include "common/protocol.h" 17 | #include "firmware/programmer.h" 18 | 19 | #define _max(x, y) ((x) < (y) ? (y) : (x)) 20 | 21 | #define CDC_CHANNEL 0 22 | 23 | #define PROGRAMMER_MEMORY_POOL_SIZE 384 24 | 25 | static uint8_t _programmerMemoryPool[PROGRAMMER_MEMORY_POOL_SIZE] = { 0 }; 26 | static Programmer programmer; 27 | 28 | static void _spiCsCallback(bool assert) { 29 | asm volatile("nop \n nop \n nop"); // FIXME 30 | if (assert) { 31 | gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 0); 32 | 33 | } else { 34 | gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1); 35 | } 36 | asm volatile("nop \n nop \n nop"); // FIXME 37 | } 38 | 39 | 40 | static void _programmerResponseCallback(uint8_t *buffer, uint16_t bufferSize, void *callbackData) { 41 | for (uint16_t i = 0; i < bufferSize; i++) { 42 | while (tud_cdc_n_write_char(CDC_CHANNEL, buffer[i]) == 0) { 43 | tud_task(); 44 | } 45 | } 46 | 47 | tud_cdc_n_write_flush(CDC_CHANNEL); 48 | } 49 | 50 | 51 | static void _programmerRequestCallback(ProtoReq *request, ProtoRes *response, void *callbackData) { 52 | switch (request->cmd) { 53 | case PROTO_CMD_SPI_TRANSFER: 54 | { 55 | ProtoReqTransfer *req = &request->request.transfer; 56 | ProtoResTransfer *res = &response->response.transfer; 57 | 58 | _spiCsCallback(true); 59 | { 60 | uint16_t toRecv = req->rxBufferSize; 61 | 62 | for (uint16_t i = 0; i < _max(req->txBufferSize, req->rxSkipSize + req->rxBufferSize); i++) { 63 | while (! spi_is_writable(spi_default)) {} 64 | 65 | if (i < req->txBufferSize) { 66 | spi_get_hw(spi_default)->dr = req->txBuffer[i]; 67 | 68 | } else { 69 | spi_get_hw(spi_default)->dr = 0xff; 70 | } 71 | 72 | while (! spi_is_readable(spi_default)) {} 73 | 74 | if (toRecv) { 75 | if (i >= req->rxSkipSize) { 76 | res->rxBuffer[i - req->rxSkipSize] = spi_get_hw(spi_default)->dr; 77 | 78 | toRecv--; 79 | 80 | } else { 81 | (void) spi_get_hw(spi_default)->dr; 82 | } 83 | 84 | } else { 85 | (void) spi_get_hw(spi_default)->dr; 86 | } 87 | } 88 | } 89 | if ((req->flags & PROTO_SPI_TRANSFER_FLAG_KEEP_CS) == 0) { 90 | _spiCsCallback(false); 91 | } 92 | } 93 | break; 94 | 95 | default: 96 | break; 97 | } 98 | } 99 | 100 | 101 | static void _cdcTask(void) { 102 | if (tud_cdc_n_available(CDC_CHANNEL)) { 103 | uint8_t buf[CFG_TUD_CDC_EP_BUFSIZE]; 104 | 105 | uint32_t count = tud_cdc_n_read(CDC_CHANNEL, buf, sizeof(buf)); 106 | 107 | for (uint32_t i = 0; i < count; i++) { 108 | programmer_putByte(&programmer, buf[i]); 109 | } 110 | } 111 | } 112 | 113 | 114 | int main() { 115 | board_init(); 116 | tusb_init(); 117 | 118 | spi_init(spi_default, 60 * 1000 * 1000); 119 | 120 | gpio_set_function(PICO_DEFAULT_SPI_RX_PIN , GPIO_FUNC_SPI); 121 | gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI); 122 | gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI); 123 | 124 | gpio_init(PICO_DEFAULT_SPI_CSN_PIN); 125 | gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1); 126 | gpio_set_dir(PICO_DEFAULT_SPI_CSN_PIN, GPIO_OUT); 127 | 128 | programmer_setup( 129 | &programmer, 130 | _programmerMemoryPool, 131 | PROGRAMMER_MEMORY_POOL_SIZE, 132 | _programmerRequestCallback, 133 | _programmerResponseCallback, 134 | NULL 135 | ); 136 | 137 | while (1) { 138 | tud_task(); 139 | 140 | _cdcTask(); 141 | } 142 | 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /firmware/rpi-pico/src/usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "tusb.h" 27 | 28 | /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. 29 | * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. 30 | * 31 | * Auto ProductID layout's Bitmap: 32 | * [MSB] MIDI | HID | MSC | CDC [LSB] 33 | */ 34 | #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) 35 | #define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ 36 | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) 37 | 38 | #define USB_VID 0xCafe 39 | #define USB_BCD 0x0200 40 | 41 | //--------------------------------------------------------------------+ 42 | // Device Descriptors 43 | //--------------------------------------------------------------------+ 44 | tusb_desc_device_t const desc_device = 45 | { 46 | .bLength = sizeof(tusb_desc_device_t), 47 | .bDescriptorType = TUSB_DESC_DEVICE, 48 | .bcdUSB = USB_BCD, 49 | 50 | // Use Interface Association Descriptor (IAD) for CDC 51 | // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) 52 | .bDeviceClass = TUSB_CLASS_MISC, 53 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 54 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 55 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 56 | 57 | .idVendor = USB_VID, 58 | .idProduct = USB_PID, 59 | .bcdDevice = 0x0100, 60 | 61 | .iManufacturer = 0x01, 62 | .iProduct = 0x02, 63 | .iSerialNumber = 0x03, 64 | 65 | .bNumConfigurations = 0x01 66 | }; 67 | 68 | // Invoked when received GET DEVICE DESCRIPTOR 69 | // Application return pointer to descriptor 70 | uint8_t const * tud_descriptor_device_cb(void) 71 | { 72 | return (uint8_t const *) &desc_device; 73 | } 74 | 75 | //--------------------------------------------------------------------+ 76 | // Configuration Descriptor 77 | //--------------------------------------------------------------------+ 78 | enum 79 | { 80 | ITF_NUM_CDC_0 = 0, 81 | ITF_NUM_CDC_0_DATA, 82 | ITF_NUM_TOTAL 83 | }; 84 | 85 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN) 86 | 87 | #if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX 88 | // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number 89 | // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... 90 | #define EPNUM_CDC_0_NOTIF 0x81 91 | #define EPNUM_CDC_0_OUT 0x02 92 | #define EPNUM_CDC_0_IN 0x82 93 | 94 | #elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X 95 | // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT 96 | // e.g EP1 OUT & EP1 IN cannot exist together 97 | #define EPNUM_CDC_0_NOTIF 0x81 98 | #define EPNUM_CDC_0_OUT 0x02 99 | #define EPNUM_CDC_0_IN 0x83 100 | 101 | #elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X 102 | // FT9XX doesn't support a same endpoint number with different direction IN and OUT 103 | // e.g EP1 OUT & EP1 IN cannot exist together 104 | #define EPNUM_CDC_0_NOTIF 0x81 105 | #define EPNUM_CDC_0_OUT 0x02 106 | #define EPNUM_CDC_0_IN 0x83 107 | 108 | #else 109 | #define EPNUM_CDC_0_NOTIF 0x81 110 | #define EPNUM_CDC_0_OUT 0x02 111 | #define EPNUM_CDC_0_IN 0x82 112 | #endif 113 | 114 | uint8_t const desc_fs_configuration[] = 115 | { 116 | // Config number, interface count, string index, total length, attribute, power in mA 117 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), 118 | 119 | // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. 120 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64), 121 | }; 122 | 123 | #if TUD_OPT_HIGH_SPEED 124 | // Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration 125 | 126 | uint8_t const desc_hs_configuration[] = 127 | { 128 | // Config number, interface count, string index, total length, attribute, power in mA 129 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), 130 | 131 | // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. 132 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 512), 133 | }; 134 | 135 | // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed 136 | tusb_desc_device_qualifier_t const desc_device_qualifier = 137 | { 138 | .bLength = sizeof(tusb_desc_device_t), 139 | .bDescriptorType = TUSB_DESC_DEVICE, 140 | .bcdUSB = USB_BCD, 141 | 142 | .bDeviceClass = TUSB_CLASS_MISC, 143 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 144 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 145 | 146 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 147 | .bNumConfigurations = 0x01, 148 | .bReserved = 0x00 149 | }; 150 | 151 | // Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request 152 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. 153 | // device_qualifier descriptor describes information about a high-speed capable device that would 154 | // change if the device were operating at the other speed. If not highspeed capable stall this request. 155 | uint8_t const* tud_descriptor_device_qualifier_cb(void) 156 | { 157 | return (uint8_t const*) &desc_device_qualifier; 158 | } 159 | 160 | // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request 161 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 162 | // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa 163 | uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) 164 | { 165 | (void) index; // for multiple configurations 166 | 167 | // if link speed is high return fullspeed config, and vice versa 168 | return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration; 169 | } 170 | 171 | #endif // highspeed 172 | 173 | // Invoked when received GET CONFIGURATION DESCRIPTOR 174 | // Application return pointer to descriptor 175 | // Descriptor contents must exist long enough for transfer to complete 176 | uint8_t const * tud_descriptor_configuration_cb(uint8_t index) 177 | { 178 | (void) index; // for multiple configurations 179 | 180 | #if TUD_OPT_HIGH_SPEED 181 | // Although we are highspeed, host may be fullspeed. 182 | return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; 183 | #else 184 | return desc_fs_configuration; 185 | #endif 186 | } 187 | 188 | //--------------------------------------------------------------------+ 189 | // String Descriptors 190 | //--------------------------------------------------------------------+ 191 | 192 | // array of pointer to string descriptors 193 | char const* string_desc_arr [] = 194 | { 195 | (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) 196 | "TinyUSB", // 1: Manufacturer 197 | "SPI Flash Programmer", // 2: Product 198 | "123456", // 3: Serials, should use chip ID 199 | "SPI CDC", // 4: CDC Interface 200 | }; 201 | 202 | static uint16_t _desc_str[32]; 203 | 204 | // Invoked when received GET STRING DESCRIPTOR request 205 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 206 | uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) 207 | { 208 | (void) langid; 209 | 210 | uint8_t chr_count; 211 | 212 | if ( index == 0) 213 | { 214 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 215 | chr_count = 1; 216 | }else 217 | { 218 | // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. 219 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors 220 | 221 | if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; 222 | 223 | const char* str = string_desc_arr[index]; 224 | 225 | // Cap at max char 226 | chr_count = (uint8_t) strlen(str); 227 | if ( chr_count > 31 ) chr_count = 31; 228 | 229 | // Convert ASCII string into UTF-16 230 | for(uint8_t i=0; i 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | 20 | typedef enum _DebugLevel { 21 | DEBUG_LEVEL_NONE, 22 | 23 | DEBUG_LEVEL_ERROR, 24 | DEBUG_LEVEL_WARINNG, 25 | DEBUG_LEVEL_INFO, 26 | DEBUG_LEVEL_DEBUG, 27 | DEBUG_LEVEL_TRACE, 28 | 29 | DEBUG_LEVEL_LAST = DEBUG_LEVEL_TRACE 30 | } DebugLevel; 31 | 32 | 33 | #define ERROR(...) { debug_log(DEBUG_LEVEL_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__); } 34 | #define WARN(...) { debug_log(DEBUG_LEVEL_WARINNG, __FILE__, __LINE__, __func__, __VA_ARGS__); } 35 | #define INFO(...) { debug_log(DEBUG_LEVEL_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__); } 36 | #define DEBUG(...) { debug_log(DEBUG_LEVEL_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__); } 37 | #define TRACE(...) { debug_log(DEBUG_LEVEL_TRACE, __FILE__, __LINE__, __func__, __VA_ARGS__); } 38 | 39 | #define OUT(...) { debug_out(__VA_ARGS__); } 40 | 41 | #define HEX(level, title, buffer, bufferSize) do { \ 42 | TRACE("Dumping memory at %p of size %zd bytes (%s)", buffer, bufferSize, title); \ 43 | debug_dumpBuffer(level, buffer, bufferSize, 16, 0, 6); \ 44 | } while (0); 45 | 46 | void debug_initialize(); 47 | 48 | void debug_setLevel(DebugLevel level); 49 | 50 | void debug_log(DebugLevel level, const char *fileName, int lineNo, const char *functionName, const char *fmt, ...); 51 | void debug_out(const char *fmt, ...); 52 | 53 | void debug_dumpBuffer(DebugLevel level, uint8_t *buffer, uint32_t bufferSize, uint32_t lineLength, uint32_t offset, uint32_t indent); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | 59 | #endif /* FLASHUTIL_DEBUG_H_ */ 60 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/entryPoint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/entryPoint.h 3 | * 4 | * Created on: 17 oct 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_ENTRYPOINT_H_ 9 | #define FLASHUTIL_ENTRYPOINT_H_ 10 | 11 | #include 12 | 13 | #include "flashutil/flash/registry.h" 14 | #include "flashutil/flash.h" 15 | #include "flashutil/spi.h" 16 | 17 | namespace flashutil { 18 | class EntryPoint { 19 | public: 20 | enum class Mode { 21 | NONE, 22 | 23 | CHIP, 24 | BLOCK, 25 | SECTOR 26 | }; 27 | 28 | enum class Operation { 29 | NO_OPERATION, 30 | 31 | READ, 32 | WRITE, 33 | ERASE, 34 | UNLOCK 35 | }; 36 | 37 | struct Parameters { 38 | int index; 39 | 40 | Mode mode; 41 | Operation operation; 42 | 43 | bool omitRedundantWrites; 44 | bool verify; 45 | 46 | Flash *flashInfo; 47 | std::istream *inStream; 48 | std::ostream *outStream; 49 | 50 | std::function beforeExecution; 51 | std::function afterExecution; 52 | 53 | Parameters() { 54 | this->index = 0; 55 | this->mode = Mode::NONE; 56 | this->operation = Operation::NO_OPERATION; 57 | this->omitRedundantWrites = false; 58 | this->verify = false; 59 | this->inStream = nullptr; 60 | this->outStream = nullptr; 61 | this->flashInfo = nullptr; 62 | } 63 | 64 | bool isValid() const; 65 | }; 66 | 67 | public: 68 | static void call(Spi &spi, const FlashRegistry ®istry, const Flash &flashGeometry, const std::vector ¶meters); 69 | static void call(Spi &spi, const FlashRegistry ®istry, const Flash &flashGeometry, const Parameters ¶meters); 70 | 71 | private: 72 | EntryPoint(); 73 | EntryPoint(const EntryPoint &); 74 | }; 75 | } 76 | 77 | #endif /* FLASHUTIL_ENTRYPOINT_H_ */ 78 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/exception.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/exception.h 3 | * 4 | * Created on: 7 mar 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_EXCEPTION_H_ 9 | #define FLASHUTIL_EXCEPTION_H_ 10 | 11 | #include 12 | #include 13 | 14 | 15 | class Exception : public std::runtime_error { 16 | public: 17 | Exception(const std::string &msg) : std::runtime_error(msg) { 18 | 19 | } 20 | 21 | Exception(const std::string &file, int line, const std::string &msg) : 22 | std::runtime_error(file + ":" + std::to_string(line) + " " + msg) 23 | { 24 | } 25 | }; 26 | 27 | #define throw_Exception(msg) do { throw Exception(__FILE__, __LINE__, msg); } while (0); 28 | 29 | #endif /* FLASHUTIL_EXCEPTION_H_ */ 30 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/flash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/flash.h 3 | * 4 | * Created on: 8 wrz 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | 9 | #ifndef FLASHUTIL_FLASH_H_ 10 | #define FLASHUTIL_FLASH_H_ 11 | 12 | #include 13 | #include 14 | 15 | class Flash { 16 | public: 17 | Flash(); 18 | Flash(const std::string &name, const std::vector &jedecId, size_t blockSize, size_t nblocks, size_t sectorSize, size_t nSectors, uint8_t protectMask); 19 | 20 | const std::string &getPartNumber() const; 21 | void setPartNumber(const std::string &partNumber); 22 | 23 | const std::string &getManufacturer() const; 24 | void setManufacturer(const std::string &manufacturer); 25 | 26 | const std::vector &getId() const; 27 | void setId(const std::vector &id); 28 | 29 | size_t getBlockSize() const; 30 | void setBlockSize(size_t size); 31 | 32 | size_t getBlockCount() const; 33 | void setBlockCount(size_t count); 34 | 35 | size_t getSectorSize() const; 36 | void setSectorSize(size_t size); 37 | 38 | size_t getSectorCount() const; 39 | void setSectorCount(size_t count); 40 | 41 | size_t getPageSize() const; 42 | void setPageSize(size_t size); 43 | 44 | size_t getPageCount() const; 45 | void setPageCount(size_t count); 46 | 47 | uint8_t getProtectMask() const; 48 | void setProtectMask(uint8_t mask); 49 | 50 | size_t getSize() const; 51 | void setSize(size_t size); 52 | 53 | void setGeometry(const Flash &other); 54 | 55 | bool isIdValid() const; 56 | bool isGeometryValid() const; 57 | bool isValid() const; 58 | 59 | private: 60 | std::string partNumber; 61 | std::string manufacturer; 62 | std::vector id; 63 | size_t blockSize; 64 | size_t blockCount; 65 | size_t sectorSize; 66 | size_t sectorCount; 67 | size_t pageSize; 68 | size_t pageCount; 69 | uint8_t protectMask; 70 | }; 71 | 72 | 73 | #endif /* FLASHUTIL_FLASH_H_ */ 74 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/flash/builder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/flash/registry.h 3 | * 4 | * Created on: 8 wrz 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_FLASH_BUILDER_H_ 9 | #define FLASHUTIL_FLASH_BUILDER_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "flashutil/flash.h" 16 | 17 | class FlashBuilder { 18 | public: 19 | FlashBuilder(); 20 | 21 | FlashBuilder &setName(const std::string &name); 22 | FlashBuilder &setJedecId(const std::vector &jedec); 23 | FlashBuilder &setBlockSize(std::size_t size); 24 | FlashBuilder &setBlockCount(std::size_t count); 25 | FlashBuilder &setSectorSize(std::size_t size); 26 | FlashBuilder &setSectorCount(std::size_t count); 27 | FlashBuilder &setPageSize(std::size_t size); 28 | FlashBuilder &setPageCount(std::size_t count); 29 | FlashBuilder &setSize(std::size_t size); 30 | FlashBuilder &setProtectMask(uint8_t mask); 31 | FlashBuilder &reset(); 32 | 33 | Flash build(); 34 | 35 | private: 36 | Flash flash; 37 | }; 38 | 39 | #endif /* FLASHUTIL_FLASH_BUILDER_H_ */ 40 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/flash/registry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/flash/registry.h 3 | * 4 | * Created on: 8 sep 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_FLASH_REGISTRY_H_ 9 | #define FLASHUTIL_FLASH_REGISTRY_H_ 10 | 11 | #include "flashutil/flash.h" 12 | 13 | 14 | class FlashRegistry { 15 | public: 16 | FlashRegistry(); 17 | 18 | void clear(); 19 | void addFlash(const Flash &flash); 20 | 21 | const Flash &getById(const std::vector &id) const; 22 | const std::vector &getAll() const; 23 | 24 | private: 25 | std::vector _flashes; 26 | }; 27 | 28 | 29 | #endif /* FLASHUTIL_FLASH_REGISTRY_H_ */ 30 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/flash/registry/reader.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flash/registry/reader.h 3 | * 4 | * Created on: 09 nov 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_FLASH_REGISTRY_READER_H_ 9 | #define FLASHUTIL_FLASH_REGISTRY_READER_H_ 10 | 11 | #include 12 | 13 | #include "flashutil/flash/registry.h" 14 | 15 | class FlashRegistryReader { 16 | public: 17 | FlashRegistryReader() { 18 | } 19 | 20 | virtual ~FlashRegistryReader() { 21 | } 22 | 23 | public: 24 | virtual void read(std::istream &stream, FlashRegistry ®istry) = 0; 25 | }; 26 | 27 | 28 | 29 | #endif /* FLASHUTIL_FLASH_REGISTRY_READER_H_ */ 30 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/flash/registry/reader/json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flash/registry/reader/json.h 3 | * 4 | * Created on: 09 nov 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_FLASH_REGISTRY_READER_JSON_H_ 9 | #define FLASHUTIL_FLASH_REGISTRY_READER_JSON_H_ 10 | 11 | #include "flashutil/flash/registry/reader.h" 12 | 13 | 14 | class FlashRegistryJsonReader : public FlashRegistryReader { 15 | public: 16 | FlashRegistryJsonReader(); 17 | 18 | void read(std::istream &stream, FlashRegistry ®istry) override; 19 | }; 20 | 21 | #endif /* FLASHUTIL_FLASH_REGISTRY_READER_JSON_H_ */ 22 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/flash/status.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/flash/status.h 3 | * 4 | * Created on: 1 nov 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_FLASH_STATUS_H_ 9 | #define FLASHUTIL_FLASH_STATUS_H_ 10 | 11 | #include 12 | 13 | class FlashStatus { 14 | public: 15 | FlashStatus(); 16 | FlashStatus(uint8_t status); 17 | 18 | bool isWriteEnableLatchSet() const; 19 | void setWriteEnableLatch(bool set); 20 | 21 | bool isWriteInProgress() const; 22 | void setBusy(bool busy); 23 | 24 | uint8_t getRegisterValue() const; 25 | void setRegisterValue(uint8_t value); 26 | 27 | bool isProtected(uint8_t mask) const; 28 | void unprotect(uint8_t mask); 29 | 30 | private: 31 | uint8_t _reg; 32 | }; 33 | 34 | #endif /* FLASHUTIL_FLASH_STATUS_H_ */ 35 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/programmer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/programmer.h 3 | * 4 | * Created on: 8 wrz 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_PROGRAMMER_H_ 9 | #define FLASHUTIL_PROGRAMMER_H_ 10 | 11 | #include 12 | 13 | #include "spi.h" 14 | #include "flashutil/flash/registry.h" 15 | #include "flashutil/flash/status.h" 16 | 17 | 18 | class Programmer { 19 | public: 20 | Programmer(Spi &spiDev, const FlashRegistry *registry); 21 | virtual ~Programmer(); 22 | 23 | void begin(const Flash *defaultGeometry); 24 | void end(); 25 | 26 | void eraseChip(); 27 | 28 | void eraseBlockByAddress(uint32_t address); 29 | void eraseBlockByNumber(int blockNo); 30 | 31 | void eraseSectorByAddress(uint32_t addres); 32 | void eraseSectorByNumber(int sectorNo); 33 | 34 | void writePage(uint32_t address, const std::vector &page); 35 | std::vector read(uint32_t address, size_t size); 36 | 37 | const Flash &getFlashInfo() const; 38 | 39 | FlashStatus getFlashStatus(); 40 | FlashStatus setFlashStatus(const FlashStatus &status); 41 | 42 | private: 43 | void verifyFlashInfoAreaByAddress(uint32_t address, size_t size, size_t alignment); 44 | void verifyFlashInfoBlockNo(int blockNo); 45 | void verifyFlashInfoSectorNo(int sectorNo); 46 | 47 | FlashStatus waitForWIPClearance(int timeoutMs); 48 | 49 | void cmdEraseChip(); 50 | void cmdEraseBlock(uint32_t address); 51 | void cmdEraseSector(uint32_t address); 52 | void cmdGetInfo(std::vector &id); 53 | void cmdGetStatus(FlashStatus &status); 54 | void cmdWriteStatus(const FlashStatus &status); 55 | void cmdWriteEnable(); 56 | void cmdWritePage(uint32_t address, const std::vector &page); 57 | void cmdFlashReadBegin(uint32_t address); 58 | 59 | private: 60 | Flash _flashInfo; 61 | const FlashRegistry *_flashRegistry; 62 | bool _spiAttached; 63 | 64 | Spi &_spi; 65 | }; 66 | 67 | 68 | #endif /* FLASHUTIL_PROGRAMMER_H_ */ 69 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/serial.h 3 | * 4 | * Created on: 6 mar 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_SERIAL_H_ 9 | #define FLASHUTIL_SERIAL_H_ 10 | 11 | #include 12 | #include 13 | 14 | class Serial { 15 | public: 16 | virtual ~Serial() { 17 | } 18 | 19 | public: 20 | virtual void write(void *buffer, std::size_t bufferSize, int timeoutMs) = 0; 21 | virtual void read(void *buffer, std::size_t bufferSize, int timeoutMs) = 0; 22 | }; 23 | 24 | #endif /* FLASHUTIL_SERIAL_H_ */ 25 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/serial/hw.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/serial/hw.h 3 | * 4 | * Created on: 6 mar 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_SERIAL_HW_H_ 9 | #define FLASHUTIL_SERIAL_HW_H_ 10 | 11 | #include 12 | #include 13 | 14 | #include "flashutil/serial.h" 15 | 16 | class HwSerial : public Serial { 17 | public: 18 | HwSerial(const std::string &serialPath, int baud); 19 | ~HwSerial(); 20 | 21 | public: 22 | void write(void *buffer, std::size_t bufferSize, int timeoutMs) override; 23 | void read(void *buffer, std::size_t bufferSize, int timeoutMs) override; 24 | 25 | private: 26 | void _flush(); 27 | 28 | private: 29 | class Impl; 30 | 31 | std::unique_ptr self; 32 | }; 33 | 34 | #endif /* FLASHUTIL_SERIAL_HW_H_ */ 35 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/spi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/spi.h 3 | * 4 | * Created on: 6 mar 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_SPI_H_ 9 | #define FLASHUTIL_SPI_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | class Spi { 17 | public: 18 | class Message { 19 | public: 20 | class Flags { 21 | public: 22 | Flags() { 23 | this->reset(); 24 | } 25 | 26 | Flags &chipDeselect(bool deselect) { 27 | this->_chipDeselect = deselect; return *this; 28 | } 29 | 30 | bool chipDeselect() const { 31 | return this->_chipDeselect; 32 | } 33 | 34 | Flags &reset() { 35 | this->chipDeselect(true); return *this; 36 | } 37 | 38 | private: 39 | bool _chipDeselect; 40 | }; 41 | 42 | class SendOpts { 43 | public: 44 | SendOpts &byte(uint8_t byte); 45 | SendOpts &data(const uint8_t *data, std::size_t dataSize); 46 | SendOpts &dummy(); 47 | 48 | std::size_t getBytes() const; 49 | const uint8_t *data() const; 50 | 51 | SendOpts &reset(); 52 | 53 | private: 54 | std::vector _data; 55 | }; 56 | 57 | class RecvOpts { 58 | public: 59 | RecvOpts &skip(std::size_t count = 0); 60 | RecvOpts &bytes(std::size_t count = 0); 61 | 62 | std::size_t getSkips() const; 63 | std::size_t getBytes() const; 64 | std::set getSkipMap() const; 65 | 66 | std::vector &data(); 67 | 68 | RecvOpts &reset(); 69 | 70 | private: 71 | std::vector _data; 72 | std::map _skips; 73 | }; 74 | 75 | public: 76 | Message(); 77 | virtual ~Message(); 78 | 79 | public: 80 | SendOpts &send(); 81 | RecvOpts &recv(); 82 | Flags &flags(); 83 | 84 | Message &reset(); 85 | 86 | private: 87 | Flags _flags; 88 | SendOpts _sendOpts; 89 | RecvOpts _recvOpts; 90 | }; 91 | 92 | class Messages { 93 | public: 94 | Messages(); 95 | 96 | Message &add(); 97 | Message &at(std::size_t pos); 98 | std::size_t count() const; 99 | 100 | private: 101 | std::vector _msgs; 102 | }; 103 | 104 | class Config { 105 | public: 106 | Config() { 107 | } 108 | }; 109 | 110 | class Capabilities { 111 | public: 112 | Capabilities() { 113 | } 114 | }; 115 | 116 | public: 117 | virtual ~Spi() {} 118 | 119 | virtual void transfer(Messages &msgs) = 0; 120 | virtual void chipSelect(bool select) = 0; 121 | 122 | virtual Config getConfig() = 0; 123 | virtual void setConfig(const Config &config) = 0; 124 | 125 | virtual const Capabilities &getCapabilities() const = 0; 126 | virtual void attach() = 0; 127 | virtual void detach() = 0; 128 | 129 | protected: 130 | Spi() {} 131 | }; 132 | 133 | #endif /* FLASHUTIL_SPI_H_ */ 134 | -------------------------------------------------------------------------------- /flashutil/include/flashutil/spi/serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flashutil/spi/serial.h 3 | * 4 | * Created on: 6 mar 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #ifndef FLASHUTIL_SPI_SERIAL_H_ 9 | #define FLASHUTIL_SPI_SERIAL_H_ 10 | 11 | #include 12 | #include 13 | 14 | #include "flashutil/spi.h" 15 | #include "flashutil/serial.h" 16 | 17 | class SerialSpi : public Spi { 18 | public: 19 | SerialSpi(Serial &serial); 20 | ~SerialSpi(); 21 | 22 | void transfer(Messages &msgs) override; 23 | void chipSelect(bool select) override; 24 | 25 | Config getConfig() override; 26 | void setConfig(const Config &config) override; 27 | 28 | const Capabilities &getCapabilities() const override; 29 | void attach() override; 30 | void detach() override; 31 | 32 | private: 33 | class Impl; 34 | 35 | std::unique_ptr self; 36 | }; 37 | 38 | #endif /* FLASHUTIL_SPI_SERIAL_H_ */ 39 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "flashutil/debug.h" 5 | 6 | #define STRING_LEN_MAX 16 7 | 8 | static DebugLevel _level = DEBUG_LEVEL_ERROR; 9 | 10 | 11 | void debug_initialize() { 12 | 13 | } 14 | 15 | 16 | void debug_setLevel(DebugLevel level) { 17 | _level = level; 18 | } 19 | 20 | 21 | void debug_out(const char *fmt, ...) { 22 | { 23 | va_list ap; 24 | 25 | va_start(ap, fmt); 26 | { 27 | vfprintf(stderr, fmt, ap); 28 | } 29 | va_end(ap); 30 | } 31 | 32 | fprintf(stderr, "\n"); 33 | } 34 | 35 | 36 | void debug_log(DebugLevel level, const char *fileName, int lineNo, const char *functionName, const char *fmt, ...) { 37 | if (level <= _level) { 38 | char *levelString = "-----"; 39 | char *levelColor = NULL; 40 | 41 | switch (level) { 42 | case DEBUG_LEVEL_NONE: 43 | levelString = "NONE "; 44 | break; 45 | 46 | case DEBUG_LEVEL_ERROR: 47 | levelString = "ERROR"; 48 | levelColor = "\x1b[31m"; 49 | break; 50 | 51 | case DEBUG_LEVEL_WARINNG: 52 | levelString = "WARN "; 53 | levelColor = "\x1b[33m"; 54 | break; 55 | 56 | case DEBUG_LEVEL_INFO: 57 | levelString = "INFO "; 58 | levelColor = "\x1b[32m"; 59 | break; 60 | 61 | case DEBUG_LEVEL_DEBUG: 62 | levelString = "DEBUG"; 63 | levelColor = "\x1b[34m"; 64 | break; 65 | 66 | case DEBUG_LEVEL_TRACE: 67 | levelString = "TRACE"; 68 | levelColor = "\x1b[36m"; 69 | break; 70 | } 71 | 72 | { 73 | size_t strLen; 74 | 75 | strLen = strlen(fileName); 76 | if (strLen > STRING_LEN_MAX) { 77 | fileName += (strLen - STRING_LEN_MAX); 78 | } 79 | 80 | strLen = strlen(functionName); 81 | if (strLen > STRING_LEN_MAX) { 82 | functionName += (strLen - STRING_LEN_MAX); 83 | } 84 | } 85 | 86 | fprintf(stderr, "%s[%s] <%s:%5d> %s%16s(): ", 87 | levelColor != NULL ? levelColor : "", 88 | levelString, 89 | fileName, 90 | lineNo, 91 | levelColor != NULL ? "\x1b[0m" : "", 92 | functionName 93 | ); 94 | 95 | { 96 | va_list ap; 97 | 98 | va_start(ap, fmt); 99 | { 100 | vfprintf(stderr, fmt, ap); 101 | } 102 | va_end(ap); 103 | } 104 | 105 | fprintf(stderr, "\n"); 106 | } 107 | } 108 | 109 | 110 | void debug_dumpBuffer(DebugLevel level, uint8_t *buffer, uint32_t bufferSize, uint32_t lineLength, uint32_t offset, uint32_t indent) { 111 | if (level <= _level) { 112 | if (bufferSize > 0) { 113 | uint32_t i, j; 114 | 115 | char asciiBuffer[lineLength + 1]; 116 | 117 | for (i = 0; i < bufferSize; i++) { 118 | if ((i % lineLength) == 0) { 119 | if (i != 0) { 120 | printf(" %s\n", asciiBuffer); 121 | } 122 | 123 | for (j = 0; j < indent; j++) { 124 | printf(" "); 125 | } 126 | 127 | printf("%04x: ", i + offset); 128 | } 129 | 130 | printf(" %02x", buffer[i]); 131 | 132 | if (! isprint(buffer[i]) || buffer[i] == '\n' || buffer[i] == '\r') { 133 | asciiBuffer[i % lineLength] = '.'; 134 | } else { 135 | asciiBuffer[i % lineLength] = buffer[i]; 136 | } 137 | 138 | asciiBuffer[(i % lineLength) + 1] = '\0'; 139 | } 140 | 141 | while ((i % 16) != 0) { 142 | printf(" "); 143 | i++; 144 | } 145 | 146 | printf(" %s", asciiBuffer); 147 | } 148 | 149 | printf("\n"); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/flash.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * main.c 3 | * 4 | * Created on: 11 wrz 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #include "flashutil/flash.h" 9 | 10 | 11 | Flash::Flash() { 12 | this->blockSize = 0; 13 | this->blockCount = 0; 14 | this->sectorSize = 0; 15 | this->sectorCount = 0; 16 | this->pageSize = 256; 17 | this->pageCount = 0; 18 | this->protectMask = 0; 19 | } 20 | 21 | 22 | Flash::Flash(const std::string &name, const std::vector &jedecId, size_t blockSize, size_t nblocks, size_t sectorSize, size_t nSectors, uint8_t protectMask) { 23 | this->setPartNumber(name); 24 | this->setId(jedecId); 25 | this->setBlockSize(blockSize); 26 | this->setBlockCount(nblocks); 27 | this->setSectorSize(sectorSize); 28 | this->setSectorCount(nSectors); 29 | this->setProtectMask(protectMask); 30 | 31 | this->setPageCount(this->getSize() / this->getPageSize()); 32 | } 33 | 34 | 35 | const std::string &Flash::getPartNumber() const { 36 | return this->partNumber; 37 | } 38 | 39 | 40 | void Flash::setPartNumber(const std::string &partNumber) { 41 | this->partNumber = partNumber; 42 | } 43 | 44 | 45 | const std::string &Flash::getManufacturer() const { 46 | return this->manufacturer; 47 | } 48 | 49 | 50 | void Flash::setManufacturer(const std::string &manufacturer) { 51 | this->manufacturer = manufacturer; 52 | } 53 | 54 | 55 | const std::vector &Flash::getId() const { 56 | return this->id; 57 | } 58 | 59 | 60 | void Flash::setId(const std::vector &id) { 61 | this->id = id; 62 | } 63 | 64 | 65 | size_t Flash::getBlockSize() const { 66 | return this->blockSize; 67 | } 68 | 69 | 70 | void Flash::setBlockSize(size_t size) { 71 | this->blockSize = size; 72 | } 73 | 74 | 75 | size_t Flash::getBlockCount() const { 76 | return this->blockCount; 77 | } 78 | 79 | 80 | void Flash::setBlockCount(size_t count) { 81 | this->blockCount = count; 82 | } 83 | 84 | 85 | size_t Flash::getSectorSize() const { 86 | return this->sectorSize; 87 | } 88 | 89 | 90 | void Flash::setSectorSize(size_t size) { 91 | this->sectorSize = size; 92 | } 93 | 94 | 95 | size_t Flash::getSectorCount() const { 96 | return this->sectorCount; 97 | } 98 | 99 | 100 | void Flash::setSectorCount(size_t count) { 101 | this->sectorCount = count; 102 | } 103 | 104 | 105 | size_t Flash::getPageSize() const { 106 | return this->pageSize; 107 | } 108 | 109 | 110 | void Flash::setPageSize(size_t size) { 111 | this->pageSize = size; 112 | } 113 | 114 | 115 | size_t Flash::getPageCount() const { 116 | return this->pageCount; 117 | } 118 | 119 | 120 | void Flash::setPageCount(size_t count) { 121 | this->pageCount = count; 122 | } 123 | 124 | 125 | uint8_t Flash::getProtectMask() const { 126 | return this->protectMask; 127 | } 128 | 129 | 130 | void Flash::setProtectMask(uint8_t mask) { 131 | this->protectMask = mask; 132 | } 133 | 134 | 135 | size_t Flash::getSize() const { 136 | if (this->blockCount != 0 && this->blockSize != 0) { 137 | return this->blockCount * this->blockSize; 138 | 139 | } else if (this->sectorCount != 0 && this->sectorSize != 0) { 140 | return this->sectorCount * this->sectorSize; 141 | } 142 | 143 | return 0; 144 | } 145 | 146 | 147 | void Flash::setSize(size_t size) { 148 | if (this->blockSize > 0) { 149 | this->blockCount = size / this->blockSize; 150 | } 151 | 152 | if (this->sectorSize > 0) { 153 | this->sectorCount = size / this->sectorSize; 154 | } 155 | 156 | if (this->pageSize > 0) { 157 | this->pageCount = size / this->pageSize; 158 | } 159 | } 160 | 161 | 162 | void Flash::setGeometry(const Flash &other) { 163 | this->setBlockCount(other.getBlockCount()); 164 | this->setBlockSize (other.getBlockSize()); 165 | 166 | this->setSectorCount(other.getSectorCount()); 167 | this->setSectorSize (other.getSectorSize()); 168 | 169 | this->setPageCount(other.getPageCount()); 170 | this->setPageSize (other.getPageSize()); 171 | 172 | this->setProtectMask(other.getProtectMask()); 173 | } 174 | 175 | 176 | bool Flash::isIdValid() const { 177 | for (auto b : this->id) { 178 | if (b != 0x00 && b != 0xff) { 179 | return true; 180 | } 181 | } 182 | 183 | return false; 184 | } 185 | 186 | 187 | bool Flash::isGeometryValid() const { 188 | if ( 189 | this->getBlockCount() == 0 || 190 | this->getBlockSize() == 0 || 191 | this->getSectorCount() == 0 || 192 | this->getSectorSize() == 0 193 | ) { 194 | return false; 195 | } 196 | 197 | return true; 198 | } 199 | 200 | 201 | bool Flash::isValid() const { 202 | if (! this->isIdValid()) { 203 | return false; 204 | } 205 | 206 | return this->isGeometryValid(); 207 | } 208 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/flash/builder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * flash/builder.cpp 3 | * 4 | * Created on: 11 wrz 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #include "flashutil/flash/builder.h" 9 | 10 | 11 | FlashBuilder::FlashBuilder() : flash() { 12 | 13 | } 14 | 15 | 16 | FlashBuilder &FlashBuilder::setName(const std::string &name) { 17 | this->flash.setPartNumber(name); 18 | return *this; 19 | } 20 | 21 | 22 | FlashBuilder &FlashBuilder::setJedecId(const std::vector &jedec) { 23 | this->flash.setId(jedec); 24 | return *this; 25 | } 26 | 27 | 28 | FlashBuilder &FlashBuilder::setBlockSize(std::size_t size) { 29 | this->flash.setBlockSize(size); 30 | return *this; 31 | } 32 | 33 | 34 | FlashBuilder &FlashBuilder::setBlockCount(std::size_t count) { 35 | this->flash.setBlockCount(count); 36 | return *this; 37 | } 38 | 39 | 40 | FlashBuilder &FlashBuilder::setSectorSize(std::size_t size) { 41 | this->flash.setSectorSize(size); 42 | return *this; 43 | } 44 | 45 | 46 | FlashBuilder &FlashBuilder::setSectorCount(std::size_t count) { 47 | this->flash.setSectorCount(count); 48 | return *this; 49 | } 50 | 51 | 52 | FlashBuilder &FlashBuilder::setPageSize(std::size_t size) { 53 | this->flash.setPageSize(size); 54 | return *this; 55 | } 56 | 57 | 58 | FlashBuilder &FlashBuilder::setPageCount(std::size_t count) { 59 | this->flash.setPageCount(count); 60 | return *this; 61 | } 62 | 63 | 64 | FlashBuilder &FlashBuilder::setSize(std::size_t size) { 65 | if (this->flash.getBlockSize()) { 66 | this->flash.setBlockCount(size / this->flash.getBlockSize()); 67 | 68 | } 69 | 70 | if (this->flash.getSectorSize()) { 71 | this->flash.setSectorCount(size / this->flash.getSectorSize()); 72 | } 73 | 74 | if (this->flash.getPageSize()) { 75 | this->flash.setPageCount(size / this->flash.getPageSize()); 76 | } 77 | 78 | return *this; 79 | } 80 | 81 | 82 | FlashBuilder &FlashBuilder::setProtectMask(uint8_t mask) { 83 | this->flash.setProtectMask(mask); 84 | return *this; 85 | } 86 | 87 | 88 | FlashBuilder &FlashBuilder::reset() { 89 | this->flash = Flash(); 90 | return *this; 91 | } 92 | 93 | 94 | Flash FlashBuilder::build() { 95 | return this->flash; 96 | } 97 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/flash/registry.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * flash/registry.cpp 3 | * 4 | * Created on: 11 wrz 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #include 9 | 10 | #include "flashutil/flash/registry.h" 11 | #include "flashutil/flash/builder.h" 12 | 13 | 14 | FlashRegistry::FlashRegistry() { 15 | 16 | } 17 | 18 | 19 | void FlashRegistry::clear() { 20 | this->_flashes.clear(); 21 | } 22 | 23 | 24 | void FlashRegistry::addFlash(const Flash &flash) { 25 | this->_flashes.push_back(flash); 26 | } 27 | 28 | 29 | const Flash &FlashRegistry::getById(const std::vector &id) const { 30 | for (const auto &f : this->_flashes) { 31 | if (f.getId() == id) { 32 | return f; 33 | } 34 | } 35 | 36 | throw std::runtime_error("Flash info was not found in local repository!"); 37 | } 38 | 39 | 40 | const std::vector &FlashRegistry::getAll() const { 41 | return this->_flashes; 42 | } 43 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/flash/registry/reader/json.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "flashutil/flash/registry/reader/json.h" 4 | 5 | using json = nlohmann::json; 6 | 7 | 8 | //static uint32_t _parseNumber(const std::string &str) { 9 | // 10 | //} 11 | 12 | static uint32_t _parseNumber(const json::const_reference &t) { 13 | if (t.is_number()) { 14 | return t.get(); 15 | 16 | } else if (t.is_string()) { 17 | std::string value = t.get(); 18 | uint32_t multiplier = 1; 19 | 20 | if (value.length() >= 3) { 21 | char unit = *value.rbegin(); 22 | 23 | if (std::tolower(unit) == 'b') { 24 | char c = std::tolower(*value.rbegin()); 25 | 26 | bool binary = false; 27 | 28 | c = std::tolower(*(value.rbegin() + 1)); 29 | 30 | if (c == 'i') { 31 | c = std::tolower(*(value.rbegin() + 2)); 32 | 33 | binary = true; 34 | } 35 | 36 | switch (c) { 37 | case 'k': multiplier = binary ? (1024) : (1000); break; 38 | case 'm': multiplier = binary ? (1024 * 1024) : (1000 * 1000); break; 39 | case 'g': multiplier = binary ? (1024 * 1024 * 1024) : (1000 * 1000 * 1000); break; 40 | 41 | default: 42 | throw std::runtime_error("Not supported size suffix!"); 43 | } 44 | 45 | if (unit == 'b') { 46 | multiplier /= 8; 47 | } 48 | } 49 | } 50 | 51 | return std::stoul(value, nullptr, 0) * multiplier; 52 | 53 | } else { 54 | throw std::runtime_error("Not supported JSON value!"); 55 | } 56 | } 57 | 58 | 59 | FlashRegistryJsonReader::FlashRegistryJsonReader() { 60 | } 61 | 62 | 63 | void FlashRegistryJsonReader::read(std::istream &stream, FlashRegistry ®istry) { 64 | auto json = json::parse(stream); 65 | 66 | registry.clear(); 67 | 68 | for (const auto &item : json.items()) { 69 | Flash flash; 70 | 71 | { 72 | const auto &definition = item.value(); 73 | 74 | flash.setPartNumber (definition["part_number"]); 75 | flash.setManufacturer(definition["manufacturer"]); 76 | 77 | { 78 | std::vector jedecId; 79 | 80 | { 81 | const auto &jedecIdArray = definition["jedec_id"]; 82 | 83 | for (const auto &id : jedecIdArray.items()) { 84 | jedecId.push_back(_parseNumber(id.value())); 85 | } 86 | } 87 | 88 | flash.setId(jedecId); 89 | } 90 | 91 | { 92 | const auto &geometry = definition["geometry"]; 93 | 94 | flash.setBlockSize (_parseNumber(geometry["block_size"])); 95 | flash.setSectorSize(_parseNumber(geometry["sector_size"])); 96 | flash.setPageSize (_parseNumber(geometry["page_size"])); 97 | flash.setSize (_parseNumber(geometry["size"])); 98 | } 99 | 100 | flash.setProtectMask(_parseNumber(definition["unprotect_mask"])); 101 | } 102 | 103 | registry.addFlash(flash); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/flash/status.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * status.cpp 3 | * 4 | * Created on: 1 lis 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | 8 | #include "flashutil/flash/status.h" 9 | 10 | // TODO: Each chip should define mask for those flags 11 | #define STATUS_FLAG_WLE 0x02 12 | #define STATUS_FLAG_WIP 0x01 13 | 14 | 15 | FlashStatus::FlashStatus() : FlashStatus(0) { 16 | } 17 | 18 | 19 | FlashStatus::FlashStatus(uint8_t status) { 20 | this->_reg = status; 21 | } 22 | 23 | bool FlashStatus::isWriteEnableLatchSet() const { 24 | return (this->_reg & STATUS_FLAG_WLE) != 0; 25 | } 26 | 27 | void FlashStatus::setWriteEnableLatch(bool set) { 28 | this->_reg |= STATUS_FLAG_WLE; 29 | } 30 | 31 | bool FlashStatus::isWriteInProgress() const { 32 | return (this->_reg & STATUS_FLAG_WIP) != 0; 33 | } 34 | 35 | void FlashStatus::setBusy(bool busy) { 36 | this->_reg |= STATUS_FLAG_WIP; 37 | } 38 | 39 | uint8_t FlashStatus::getRegisterValue() const { 40 | return this->_reg; 41 | } 42 | 43 | 44 | void FlashStatus::setRegisterValue(uint8_t value) { 45 | this->_reg = value; 46 | } 47 | 48 | 49 | bool FlashStatus::isProtected(uint8_t mask) const { 50 | return (this->_reg & mask) != 0; 51 | } 52 | 53 | 54 | void FlashStatus::unprotect(uint8_t mask) { 55 | this->_reg &= ~mask; 56 | } 57 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/programmer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * programmer.cpp 3 | * 4 | * Created on: 11 wrz 2023 5 | * Author: Jaroslaw Bielski (bielski.j@gmail.com) 6 | */ 7 | #include 8 | #include 9 | #include 10 | 11 | #include "flashutil/programmer.h" 12 | #include "flashutil/exception.h" 13 | #include "flashutil/flash/builder.h" 14 | #include "flashutil/debug.h" 15 | 16 | #define ERASE_CHIP_TIMEOUT_MS (5 * 60 * 1000) 17 | #define ERASE_BLOCK_TIMEOUT_MS 10000 18 | #define ERASE_SECTOR_TIMEOUT_MS 500 19 | #define WRITE_STATUS_TIMEOUT_MS 200 20 | #define WRITE_BYTE_TIMEOUT_MS 100 21 | #define WRITE_PAGE_TIMEOUT_MS 200 22 | 23 | 24 | Programmer::Programmer(Spi &spiDev, const FlashRegistry *registry) : _spi(spiDev) { 25 | this->_flashRegistry = registry; 26 | this->_spiAttached = false; 27 | } 28 | 29 | 30 | Programmer::~Programmer() { 31 | this->end(); 32 | } 33 | 34 | 35 | void Programmer::begin(const Flash *defaultGeometry) { 36 | auto &f = this->_flashInfo; 37 | 38 | TRACE("call, geometry: %p", defaultGeometry); 39 | 40 | this->_spi.attach(); 41 | this->_spiAttached = true; 42 | 43 | { 44 | std::vector id; 45 | 46 | this->cmdGetInfo(id); 47 | 48 | f.setId(id); 49 | f.setPartNumber("Unknown chip"); 50 | 51 | if (defaultGeometry != nullptr) { 52 | f.setGeometry(*defaultGeometry); 53 | } 54 | 55 | if (f.isIdValid()) { 56 | if (this->_flashRegistry != nullptr) { 57 | try { 58 | f = this->_flashRegistry->getById(id); 59 | } catch (const std::exception &) {} 60 | } 61 | 62 | if (! f.isGeometryValid()) { 63 | INFO("Detected flash chip of ID %02x, %02x, %02x - its geometry is unknown", id[0], id[1], id[2]); 64 | } 65 | 66 | } else { 67 | throw_Exception("No flash device detected!"); 68 | } 69 | } 70 | } 71 | 72 | 73 | void Programmer::end() { 74 | TRACE(("call")); 75 | 76 | if (this->_spiAttached) { 77 | this->_spiAttached = false; 78 | this->_spi.detach(); 79 | } 80 | 81 | this->_flashInfo = Flash(); 82 | } 83 | 84 | 85 | const Flash &Programmer::getFlashInfo() const { 86 | return this->_flashInfo; 87 | } 88 | 89 | 90 | FlashStatus Programmer::getFlashStatus() { 91 | FlashStatus status; 92 | 93 | this->cmdGetStatus(status); 94 | 95 | return status; 96 | } 97 | 98 | 99 | FlashStatus Programmer::setFlashStatus(const FlashStatus &status) { 100 | this->cmdWriteEnable(); 101 | this->cmdWriteStatus(status); 102 | 103 | return this->waitForWIPClearance(WRITE_STATUS_TIMEOUT_MS); 104 | } 105 | 106 | 107 | static void _verifyCommon(const Flash &info) { 108 | if (! info.isValid()) { 109 | throw std::runtime_error("Flash info is incomplete or invalid!"); 110 | } 111 | } 112 | 113 | 114 | void Programmer::verifyFlashInfoAreaByAddress(uint32_t address, size_t size, size_t alignment) { 115 | _verifyCommon(this->_flashInfo); 116 | 117 | if (address + size > this->_flashInfo.getSize()) { 118 | throw std::runtime_error("Address is out of bound!"); 119 | } 120 | 121 | if (alignment > 0) { 122 | if ((address % alignment) != 0) { 123 | throw std::runtime_error("Address " + std::to_string(address) + " is not a multiple of " + std::to_string(alignment) + "!"); 124 | } 125 | } 126 | } 127 | 128 | 129 | void Programmer::verifyFlashInfoBlockNo(int blockNo) { 130 | _verifyCommon(this->_flashInfo); 131 | } 132 | 133 | 134 | void Programmer::verifyFlashInfoSectorNo(int sectorNo) { 135 | _verifyCommon(this->_flashInfo); 136 | } 137 | 138 | 139 | FlashStatus Programmer::waitForWIPClearance(int timeoutMs) { 140 | FlashStatus ret; 141 | 142 | ret.setBusy(true); 143 | 144 | while (ret.isWriteInProgress() && (timeoutMs > 0)) { 145 | uint8_t statusReg; 146 | 147 | this->cmdGetStatus(ret); 148 | 149 | if (ret.isWriteInProgress()) { 150 | int interval = std::min(timeoutMs, 10); 151 | 152 | { 153 | struct timespec tv; 154 | 155 | tv.tv_sec = (interval / 1000); 156 | tv.tv_nsec = (interval % 1000) * 1000000LL; 157 | 158 | nanosleep(&tv, nullptr); 159 | } 160 | 161 | timeoutMs -= interval; 162 | } 163 | } 164 | 165 | if (ret.isWriteInProgress()) { 166 | throw std::runtime_error("Waiting for WIP flag clearance has timed out!"); 167 | } 168 | 169 | return ret; 170 | } 171 | 172 | 173 | void Programmer::eraseChip() { 174 | TRACE(("call")); 175 | 176 | _verifyCommon(this->_flashInfo); 177 | 178 | this->cmdWriteEnable(); 179 | this->cmdEraseChip(); 180 | 181 | this->waitForWIPClearance(ERASE_CHIP_TIMEOUT_MS); 182 | } 183 | 184 | 185 | void Programmer::eraseBlockByAddress(uint32_t address) { 186 | TRACE("call"); 187 | 188 | this->verifyFlashInfoAreaByAddress(address, this->_flashInfo.getBlockSize(), this->_flashInfo.getBlockSize()); 189 | 190 | this->cmdWriteEnable(); 191 | this->cmdEraseBlock(address); 192 | 193 | this->waitForWIPClearance(ERASE_BLOCK_TIMEOUT_MS); 194 | } 195 | 196 | 197 | void Programmer::eraseBlockByNumber(int blockNo) { 198 | this->eraseBlockByAddress(blockNo * this->_flashInfo.getBlockSize()); 199 | } 200 | 201 | 202 | void Programmer::eraseSectorByAddress(uint32_t address) { 203 | TRACE("call"); 204 | 205 | this->verifyFlashInfoAreaByAddress(address, this->_flashInfo.getSectorSize(), this->_flashInfo.getSectorSize()); 206 | 207 | this->cmdWriteEnable(); 208 | this->cmdEraseSector(address); 209 | 210 | this->waitForWIPClearance(ERASE_SECTOR_TIMEOUT_MS); 211 | } 212 | 213 | 214 | void Programmer::eraseSectorByNumber(int sectorNo) { 215 | this->eraseSectorByAddress(sectorNo * this->_flashInfo.getSectorSize()); 216 | } 217 | 218 | 219 | void Programmer::writePage(uint32_t address, const std::vector &page) { 220 | TRACE("call, address %08x, buffer: %p, size: %zd", address, page.data(), page.size()); 221 | 222 | this->verifyFlashInfoAreaByAddress(address, page.size(), this->_flashInfo.getPageSize()); 223 | 224 | this->cmdWriteEnable(); 225 | this->cmdWritePage(address, page); 226 | 227 | this->waitForWIPClearance(ERASE_SECTOR_TIMEOUT_MS); 228 | } 229 | 230 | 231 | std::vector Programmer::read(uint32_t address, size_t size) { 232 | this->verifyFlashInfoAreaByAddress(address, size, 1); 233 | 234 | TRACE("call, address %08x, size: %zd", address, size); 235 | 236 | this->cmdFlashReadBegin(address); 237 | 238 | { 239 | Spi::Messages msgs; 240 | 241 | { 242 | auto &msg = msgs.add(); 243 | 244 | msg.recv() 245 | .bytes(size); 246 | } 247 | 248 | _spi.transfer(msgs); 249 | 250 | return std::move(msgs.at(0).recv().data()); 251 | } 252 | } 253 | 254 | 255 | void Programmer::cmdEraseChip() { 256 | Spi::Messages msgs; 257 | 258 | TRACE(("call")); 259 | 260 | { 261 | auto &msg = msgs.add(); 262 | 263 | msg.send() 264 | .byte(0xc7); // Chip erase (CE) 265 | } 266 | 267 | _spi.transfer(msgs); 268 | } 269 | 270 | 271 | void Programmer::cmdEraseBlock(uint32_t address) { 272 | Spi::Messages msgs; 273 | 274 | TRACE(("call")); 275 | 276 | { 277 | auto &msg = msgs.add(); 278 | 279 | msg.send() 280 | .byte(0xD8) // Block erase (BE) 281 | 282 | .byte((address >> 16) & 0xff) 283 | .byte((address >> 8) & 0xff) 284 | .byte((address >> 0) & 0xff); 285 | } 286 | 287 | _spi.transfer(msgs); 288 | } 289 | 290 | 291 | void Programmer::cmdEraseSector(uint32_t address) { 292 | Spi::Messages msgs; 293 | 294 | TRACE(("call")); 295 | 296 | { 297 | auto &msg = msgs.add(); 298 | 299 | msg.send() 300 | .byte(0x20) // Sector erase (SE) 301 | 302 | .byte((address >> 16) & 0xff) 303 | .byte((address >> 8) & 0xff) 304 | .byte((address >> 0) & 0xff); 305 | } 306 | 307 | _spi.transfer(msgs); 308 | } 309 | 310 | 311 | void Programmer::cmdGetInfo(std::vector &id) { 312 | Spi::Messages msgs; 313 | 314 | TRACE(("call")); 315 | 316 | id.clear(); 317 | 318 | { 319 | auto &msg = msgs.add(); 320 | 321 | msg.send() 322 | .byte(0x9f); 323 | 324 | msg.recv() 325 | .skip(1) 326 | .bytes(3); 327 | } 328 | 329 | _spi.transfer(msgs); 330 | 331 | { 332 | auto &recv = msgs.at(0).recv(); 333 | 334 | id.push_back(recv.data().at(0)); 335 | id.push_back(recv.data().at(1)); 336 | id.push_back(recv.data().at(2)); 337 | } 338 | } 339 | 340 | 341 | void Programmer::cmdGetStatus(FlashStatus &status) { 342 | Spi::Messages msgs; 343 | 344 | TRACE(("call")); 345 | 346 | { 347 | auto &msg = msgs.add(); 348 | 349 | msg.send() 350 | .byte(0x05); // RDSR 351 | 352 | msg.recv() 353 | .skip(1) 354 | .bytes(1); 355 | } 356 | 357 | _spi.transfer(msgs); 358 | 359 | status = FlashStatus(msgs.at(0).recv().data().at(0)); 360 | } 361 | 362 | 363 | void Programmer::cmdWriteEnable() { 364 | Spi::Messages msgs; 365 | 366 | TRACE(("call")); 367 | 368 | { 369 | auto &msg = msgs.add(); 370 | 371 | msg.send() 372 | .byte(0x06); // WREN 373 | } 374 | 375 | _spi.transfer(msgs); 376 | } 377 | 378 | 379 | void Programmer::cmdWritePage(uint32_t address, const std::vector &page) { 380 | Spi::Messages msgs; 381 | 382 | TRACE(("call")); 383 | 384 | { 385 | auto &msg = msgs.add(); 386 | 387 | msg.send() 388 | .byte(0x02) // PP 389 | 390 | .byte((address >> 16) & 0xff) 391 | .byte((address >> 8) & 0xff) 392 | .byte((address >> 0) & 0xff) 393 | 394 | .data(page.data(), page.size()); 395 | } 396 | 397 | _spi.transfer(msgs); 398 | } 399 | 400 | 401 | void Programmer::cmdWriteStatus(const FlashStatus &status) { 402 | Spi::Messages msgs; 403 | 404 | { 405 | auto &msg = msgs.add(); 406 | 407 | msg.send() 408 | .byte(0x01) // WRSR 409 | .byte(status.getRegisterValue()); 410 | } 411 | 412 | _spi.transfer(msgs); 413 | } 414 | 415 | 416 | void Programmer::cmdFlashReadBegin(uint32_t address) { 417 | Spi::Messages msgs; 418 | 419 | TRACE(("call")); 420 | 421 | { 422 | auto &msg = msgs.add(); 423 | 424 | msg.send() 425 | .byte(0x03) // Read data (READ) 426 | 427 | .byte((address >> 16) & 0xff) // Address 428 | .byte((address >> 8) & 0xff) 429 | .byte((address >> 0) & 0xff); 430 | 431 | msg.flags() 432 | .chipDeselect(false); 433 | } 434 | 435 | _spi.transfer(msgs); 436 | } 437 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/serial/hw.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #define BOOST_BIND_GLOBAL_PLACEHOLDERS 9 | #include 10 | 11 | #include "flashutil/serial/hw.h" 12 | 13 | #include "flashutil/exception.h" 14 | #include "flashutil/debug.h" 15 | 16 | 17 | enum class Result { 18 | IN_PROGRESS, 19 | SUCCESS, 20 | ERROR, 21 | TIMEOUT 22 | }; 23 | 24 | 25 | struct HwSerial::Impl { 26 | boost::asio::io_service service; 27 | boost::asio::serial_port serial; 28 | 29 | boost::asio::deadline_timer timeoutTimer; 30 | Result readResult; 31 | 32 | void onCompleted(const boost::system::error_code &errorCode, const size_t bytesTransferred) { 33 | TRACE("Read: %zd bytes (%s)", bytesTransferred, errorCode.message().c_str()); 34 | 35 | if (errorCode) { 36 | if (errorCode != boost::asio::error::operation_aborted) { 37 | this->timeoutTimer.cancel(); 38 | 39 | this->readResult = Result::ERROR; 40 | } 41 | 42 | } else { 43 | this->readResult = Result::SUCCESS; 44 | 45 | this->timeoutTimer.cancel(); 46 | } 47 | } 48 | 49 | void onTimedOut(const boost::system::error_code &errorCode) { 50 | if (errorCode != boost::asio::error::operation_aborted) { 51 | this->serial.cancel(); 52 | 53 | this->readResult = Result::TIMEOUT; 54 | } 55 | } 56 | 57 | Impl(const std::string &serialPort) : service(), serial(service, serialPort), timeoutTimer(service) { 58 | this->readResult = Result::SUCCESS; 59 | } 60 | }; 61 | 62 | 63 | HwSerial::HwSerial(const std::string &serialPath, int baud) { 64 | this->self.reset(new Impl(serialPath)); 65 | 66 | INFO("Device %s has been successfully opened!", serialPath.c_str()); 67 | 68 | { 69 | boost::asio::serial_port &s = self->serial; 70 | 71 | s.set_option(boost::asio::serial_port::baud_rate(baud)); 72 | s.set_option(boost::asio::serial_port::character_size(8)); 73 | s.set_option(boost::asio::serial_port::flow_control(boost::asio::serial_port::flow_control::none)); 74 | s.set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::none)); 75 | s.set_option(boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::one)); 76 | } 77 | 78 | #if defined(__unix__) 79 | { 80 | int fd = self->serial.lowest_layer().native_handle(); 81 | 82 | struct termios options; 83 | 84 | if (tcgetattr(fd, &options) != 0) { 85 | throw_Exception("Unable to get serial port options!"); 86 | } 87 | 88 | options.c_cflag &= ~HUPCL; // Lower modem control lines after last process closes the device - prevent form arduino reset on next call 89 | 90 | // raw transmission 91 | options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 92 | options.c_oflag &= ~OPOST; 93 | 94 | options.c_cc[VMIN] = 1; 95 | 96 | if (tcsetattr(fd, TCSANOW, &options) != 0) { 97 | throw_Exception("Unable to set serial port options!"); 98 | } 99 | } 100 | #endif 101 | 102 | this->_flush(); 103 | } 104 | 105 | 106 | HwSerial::~HwSerial() { 107 | 108 | } 109 | 110 | 111 | void HwSerial::write(void *buffer, std::size_t bufferSize, int timeoutMs) { 112 | if (self->serial.write_some(boost::asio::buffer(buffer, bufferSize)) != bufferSize) { 113 | throw_Exception("Cannot write data to the output!"); 114 | } 115 | } 116 | 117 | 118 | void HwSerial::_flush() { 119 | #if defined(__unix__) 120 | int fd = self->serial.lowest_layer().native_handle(); 121 | 122 | tcflush(fd, TCIOFLUSH); 123 | #endif 124 | } 125 | 126 | 127 | void HwSerial::read(void *buffer, std::size_t bufferSize, int timeoutMs) { 128 | self->service.restart(); 129 | 130 | if (timeoutMs != 0) { 131 | self->timeoutTimer.expires_from_now(boost::posix_time::milliseconds(timeoutMs)); 132 | 133 | } else { 134 | self->timeoutTimer.expires_from_now(boost::posix_time::hours(100000)); 135 | } 136 | 137 | self->timeoutTimer.async_wait( 138 | boost::bind( 139 | &Impl::onTimedOut, 140 | self.get(), 141 | boost::asio::placeholders::error 142 | ) 143 | ); 144 | 145 | self->readResult = Result::IN_PROGRESS; 146 | 147 | boost::asio::async_read( 148 | self->serial, 149 | boost::asio::buffer(buffer, bufferSize), 150 | boost::bind( 151 | &Impl::onCompleted, 152 | self.get(), 153 | boost::asio::placeholders::error, 154 | boost::asio::placeholders::bytes_transferred 155 | ) 156 | ); 157 | 158 | self->service.run(); 159 | 160 | switch (self->readResult) { 161 | case Result::IN_PROGRESS: 162 | break; 163 | 164 | case Result::ERROR: 165 | { 166 | self->timeoutTimer.cancel(); 167 | self->serial.cancel(); 168 | 169 | this->_flush(); 170 | 171 | throw_Exception("Error occurred while reading data from serial port!"); 172 | } 173 | break; 174 | 175 | case Result::SUCCESS: 176 | { 177 | self->timeoutTimer.cancel(); 178 | } 179 | break; 180 | 181 | case Result::TIMEOUT: 182 | { 183 | WARN("RESULT_TIMEOUT"); 184 | 185 | self->serial.cancel(); 186 | 187 | this->_flush(); 188 | 189 | throw_Exception("Timeout occurred while waiting on data"); 190 | } 191 | break; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/spi.cpp: -------------------------------------------------------------------------------- 1 | #include "flashutil/spi.h" 2 | 3 | 4 | Spi::Message::SendOpts &Spi::Message::SendOpts::byte(uint8_t byte) { 5 | this->_data.push_back(byte); 6 | 7 | return *this; 8 | } 9 | 10 | 11 | Spi::Message::SendOpts &Spi::Message::SendOpts::data(const uint8_t *data, std::size_t dataSize) { 12 | std::copy(data, data + dataSize, std::back_inserter(this->_data)); 13 | 14 | return *this; 15 | } 16 | 17 | 18 | Spi::Message::SendOpts &Spi::Message::SendOpts::dummy() { 19 | this->_data.push_back(0xff); 20 | 21 | return *this; 22 | } 23 | 24 | 25 | std::size_t Spi::Message::SendOpts::getBytes() const { 26 | return this->_data.size(); 27 | } 28 | 29 | 30 | const uint8_t *Spi::Message::SendOpts::data() const { 31 | return this->_data.data(); 32 | } 33 | 34 | 35 | Spi::Message::SendOpts &Spi::Message::SendOpts::reset() { 36 | this->_data.clear(); 37 | 38 | return *this; 39 | } 40 | 41 | 42 | Spi::Message::RecvOpts &Spi::Message::RecvOpts::skip(std::size_t count) { 43 | std::size_t pos = this->_data.size(); 44 | 45 | for (auto &skip : this->_skips) { 46 | pos += skip.second; 47 | } 48 | 49 | this->_skips[pos] = count; 50 | 51 | return *this; 52 | } 53 | 54 | 55 | Spi::Message::RecvOpts &Spi::Message::RecvOpts::bytes(std::size_t count) { 56 | this->_data.reserve(this->_data.size() + count); 57 | 58 | while (count--) { 59 | this->_data.push_back(0xff); 60 | } 61 | 62 | return *this; 63 | } 64 | 65 | 66 | std::size_t Spi::Message::RecvOpts::getSkips() const { 67 | std::size_t ret = 0; 68 | 69 | for (auto &p : this->_skips) { 70 | ret += p.second; 71 | } 72 | 73 | return ret; 74 | } 75 | 76 | 77 | std::set Spi::Message::RecvOpts::getSkipMap() const { 78 | std::set ret; 79 | 80 | for (const auto &skip : this->_skips) { 81 | for (std::size_t i = 0; i < skip.second; i++) { 82 | ret.insert(i + skip.first); 83 | } 84 | } 85 | 86 | return ret; 87 | } 88 | 89 | 90 | std::size_t Spi::Message::RecvOpts::getBytes() const { 91 | return this->_data.size(); 92 | } 93 | 94 | 95 | std::vector &Spi::Message::RecvOpts::data() { 96 | return this->_data; 97 | } 98 | 99 | 100 | Spi::Message::RecvOpts &Spi::Message::RecvOpts::reset() { 101 | this->_data.clear(); 102 | this->_skips.clear(); 103 | 104 | return *this; 105 | } 106 | 107 | 108 | Spi::Message::Message() { 109 | this->reset(); 110 | } 111 | 112 | 113 | Spi::Message::~Message() { 114 | 115 | } 116 | 117 | 118 | Spi::Message::SendOpts &Spi::Message::send() { 119 | return this->_sendOpts; 120 | } 121 | 122 | 123 | Spi::Message::RecvOpts &Spi::Message::recv() { 124 | return this->_recvOpts; 125 | } 126 | 127 | 128 | Spi::Message::Flags &Spi::Message::flags() { 129 | return this->_flags; 130 | } 131 | 132 | 133 | Spi::Message &Spi::Message::reset() { 134 | this->_recvOpts.reset(); 135 | this->_sendOpts.reset(); 136 | this->_flags.reset(); 137 | 138 | return *this; 139 | } 140 | 141 | 142 | Spi::Messages::Messages() { 143 | 144 | } 145 | 146 | 147 | Spi::Message &Spi::Messages::add() { 148 | this->_msgs.push_back(Spi::Message()); 149 | 150 | return *this->_msgs.rbegin(); 151 | } 152 | 153 | 154 | Spi::Message &Spi::Messages::at(std::size_t pos) { 155 | return this->_msgs.at(pos); 156 | } 157 | 158 | 159 | std::size_t Spi::Messages::count() const { 160 | return this->_msgs.size(); 161 | } 162 | -------------------------------------------------------------------------------- /flashutil/src/flashutil/spi/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common/crc8.h" 5 | #include "common/protocol.h" 6 | #include "common/protocol/packet.h" 7 | #include "common/protocol/request.h" 8 | #include "common/protocol/response.h" 9 | 10 | #include "flashutil/exception.h" 11 | #include "flashutil/spi/serial.h" 12 | 13 | #include "flashutil/debug.h" 14 | 15 | 16 | #define TIMEOUT_MS 1000 17 | 18 | #define TRANSFER_DATA_BLOCK_SIZE ((size_t) 251) 19 | 20 | 21 | struct SerialProxy : public Serial { 22 | public: 23 | SerialProxy(Serial &serial) : _serial(serial) { 24 | } 25 | 26 | virtual void write(void *buffer, std::size_t bufferSize, int timeoutMs) override { 27 | this->_serial.write(buffer, bufferSize, timeoutMs); 28 | } 29 | 30 | virtual void read(void *buffer, std::size_t bufferSize, int timeoutMs) override { 31 | this->_serial.read(buffer, bufferSize, timeoutMs); 32 | } 33 | 34 | private: 35 | Serial &_serial; 36 | }; 37 | 38 | 39 | struct SerialSpi::Impl { 40 | std::unique_ptr serial; 41 | Config config; 42 | uint8_t id; 43 | Capabilities capabilities; 44 | 45 | std::vector packetBuffer; 46 | size_t txSize; 47 | size_t rxSize; 48 | 49 | Impl(Serial &serial) : packetBuffer(32) { 50 | this->serial.reset(new SerialProxy(serial)); 51 | 52 | this->init(true); 53 | } 54 | 55 | void init(bool attached) { 56 | this->id = 0; 57 | } 58 | 59 | void transfer(Messages &msgs) { 60 | for (size_t i = 0; i < msgs.count(); i++) { 61 | auto &msg = msgs.at(i); 62 | 63 | size_t rxSize = msg.recv().getBytes(); 64 | size_t txSize = msg.send().getBytes(); 65 | size_t rxSkip = msg.recv().getSkips(); 66 | 67 | size_t txWritten = 0; 68 | size_t rxWritten = 0; 69 | 70 | DEBUG("rxSize: %zd, txSize: %zd, skipSize: %zd", rxSize, txSize, rxSkip); 71 | 72 | while (rxSize > 0 || txSize > 0 || rxSkip > 0) { 73 | executeCmd( 74 | PROTO_CMD_SPI_TRANSFER, 75 | 76 | [&rxSize, &txSize, &rxSkip, &msg](ProtoReq &request, ProtoRes &response) { 77 | ProtoReqTransfer &t = request.request.transfer; 78 | 79 | t.txBufferSize = std::min((size_t) t.txBufferSize, txSize); 80 | txSize -= t.txBufferSize; 81 | 82 | if (txSize == 0) { 83 | t.rxSkipSize = rxSkip; 84 | rxSkip = 0; 85 | 86 | t.rxBufferSize = std::min((size_t) response.response.transfer.rxBufferSize, rxSize); 87 | rxSize -= t.rxBufferSize; 88 | 89 | } else { 90 | size_t remain = std::min(rxSkip, (size_t) t.txBufferSize); 91 | 92 | t.rxSkipSize = remain; 93 | rxSkip -= remain; 94 | 95 | if (t.txBufferSize > t.rxSkipSize) { 96 | remain = std::min(rxSize, (size_t) t.txBufferSize - t.rxSkipSize); 97 | 98 | t.rxBufferSize = remain; 99 | rxSize -= remain; 100 | } 101 | } 102 | 103 | // Apply flags 104 | if (! msg.flags().chipDeselect()) { 105 | t.flags |= PROTO_SPI_TRANSFER_FLAG_KEEP_CS; 106 | } 107 | 108 | if (rxSize > 0 || txSize > 0 || rxSkip > 0) { 109 | t.flags |= PROTO_SPI_TRANSFER_FLAG_KEEP_CS; 110 | } 111 | }, 112 | 113 | [&txWritten, &msg](ProtoReq &request) { 114 | ProtoReqTransfer &t = request.request.transfer; 115 | 116 | memcpy(t.txBuffer, msg.send().data() + txWritten, t.txBufferSize); 117 | 118 | txWritten += t.txBufferSize; 119 | }, 120 | 121 | [&rxWritten, &msg](const ProtoRes &response) { 122 | const ProtoResTransfer &t = response.response.transfer; 123 | 124 | std::copy(t.rxBuffer, t.rxBuffer + t.rxBufferSize, msg.recv().data().begin() + rxWritten); 125 | 126 | rxWritten += t.rxBufferSize; 127 | }, 128 | 129 | TIMEOUT_MS 130 | ); 131 | } 132 | } 133 | } 134 | 135 | 136 | void chipSelect(bool select) { 137 | ProtoRes response; 138 | 139 | this->executeCmd(PROTO_CMD_SPI_TRANSFER, [select](ProtoReq &req, ProtoRes &res) { 140 | ProtoReqTransfer &t = req.request.transfer; 141 | 142 | t.flags = select ? PROTO_SPI_TRANSFER_FLAG_KEEP_CS : 0; 143 | t.rxBufferSize = 0; 144 | t.rxSkipSize = 0; 145 | t.txBuffer = nullptr; 146 | t.txBufferSize = 0; 147 | 148 | }, {}, {}, TIMEOUT_MS); 149 | } 150 | 151 | 152 | Config getConfig() { 153 | return this->config; 154 | } 155 | 156 | 157 | void setConfig(const Config &config) { 158 | this->config = config; 159 | } 160 | 161 | const Capabilities &getCapabilities() const { 162 | return this->capabilities; 163 | } 164 | 165 | void executeCmd( 166 | uint8_t cmd, 167 | std::function requestPrepareCallback, 168 | std::function requestFillCallback, 169 | std::function responseDataCallback, 170 | int timeout 171 | ) { 172 | uint8_t *packetBuffer = this->packetBuffer.data(); 173 | uint16_t packetBufferSize = this->packetBuffer.size(); 174 | uint16_t packetBufferWritten; 175 | 176 | ProtoPkt packet; 177 | ProtoRes response; 178 | 179 | proto_pkt_init(&packet, packetBuffer, packetBufferSize, cmd, ++this->id); 180 | 181 | { 182 | ProtoReq request; 183 | 184 | proto_req_init(&request, packet.payload, packet.payloadSize, packet.code); 185 | proto_res_init(&response, packet.payload, packet.payloadSize, packet.code); 186 | 187 | if (requestPrepareCallback) { 188 | requestPrepareCallback(request, response); 189 | } 190 | 191 | proto_pkt_prepare(&packet, packetBuffer, packetBufferSize, proto_req_getPayloadSize(&request)); 192 | 193 | proto_req_assign(&request, packet.payload, packet.payloadSize); 194 | { 195 | if (requestFillCallback) { 196 | requestFillCallback(request); 197 | } 198 | } 199 | proto_req_encode(&request, packet.payload, packet.payloadSize); 200 | } 201 | 202 | packetBufferWritten = proto_pkt_encode(&packet, packetBuffer, packetBufferSize); 203 | 204 | HEX(DEBUG_LEVEL_TRACE, "Packet buffer", packetBuffer, packetBufferWritten); 205 | 206 | this->serial->write(packetBuffer, packetBufferWritten, TIMEOUT_MS); 207 | 208 | { 209 | ProtoPktDes decoder; 210 | 211 | proto_pkt_dec_setup(&decoder, packetBuffer, packetBufferSize); 212 | 213 | { 214 | uint8_t decRet; 215 | 216 | do { 217 | uint8_t byte; 218 | 219 | this->serial->read(&byte, 1, TIMEOUT_MS); 220 | 221 | decRet = proto_pkt_dec_putByte(&decoder, byte, &packet); 222 | 223 | if (decRet != PROTO_PKT_DES_RET_IDLE) { 224 | if (PROTO_PKT_DES_RET_GET_ERROR_CODE(decRet) != PROTO_NO_ERROR) { 225 | throw_Exception("Protocol error! " + PROTO_PKT_DES_RET_GET_ERROR_CODE(decRet)); 226 | } 227 | 228 | if (packet.id != this->id) { 229 | throw_Exception("Protocol error! ID does not match!"); 230 | } 231 | 232 | proto_res_init (&response, packet.payload, packet.payloadSize, cmd); 233 | proto_res_decode(&response, packet.payload, packet.payloadSize); 234 | proto_res_assign(&response, packet.payload, packet.payloadSize); 235 | 236 | if (responseDataCallback) { 237 | responseDataCallback(response); 238 | } 239 | } 240 | } while (decRet == PROTO_PKT_DES_RET_IDLE); 241 | } 242 | } 243 | } 244 | 245 | void attach() { 246 | executeCmd(PROTO_CMD_GET_INFO, {}, {}, [this](const ProtoRes &response) { 247 | DEBUG("version %hhu.%hhu, payload size: %hu", response.response.getInfo.version.major, response.response.getInfo.version.minor, response.response.getInfo.packetSize); 248 | 249 | this->packetBuffer = std::vector(response.response.getInfo.packetSize); 250 | }, TIMEOUT_MS); 251 | 252 | // Be sure CS pin is released. 253 | this->chipSelect(false); 254 | } 255 | 256 | 257 | void detach() { 258 | // Be sure CS pin is released. 259 | this->chipSelect(false); 260 | } 261 | }; 262 | 263 | 264 | SerialSpi::SerialSpi(Serial &serial) { 265 | this->self.reset(new SerialSpi::Impl(serial)); 266 | } 267 | 268 | 269 | SerialSpi::~SerialSpi() { 270 | 271 | } 272 | 273 | 274 | void SerialSpi::chipSelect(bool select) { 275 | this->self->chipSelect(select); 276 | } 277 | 278 | 279 | Spi::Config SerialSpi::getConfig() { 280 | return self->getConfig(); 281 | } 282 | 283 | 284 | void SerialSpi::setConfig(const Config &config) { 285 | self->setConfig(config); 286 | } 287 | 288 | 289 | void SerialSpi::transfer(Messages &msgs) { 290 | self->transfer(msgs); 291 | } 292 | 293 | 294 | const Spi::Capabilities &SerialSpi::getCapabilities() const { 295 | return self->getCapabilities(); 296 | } 297 | 298 | 299 | void SerialSpi::attach() { 300 | self->attach(); 301 | } 302 | 303 | 304 | void SerialSpi::detach() { 305 | self->detach(); 306 | } 307 | -------------------------------------------------------------------------------- /flashutil/test/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | SERIAL_PORT=${SERIAL_PORT:-/dev/ttyUSB0} 6 | SERIAL_BAUD=${SERIAL_BAUD:-500000} 7 | 8 | FLASH_UTIL="${FLASH_UTIL:-../build/flash-util}" 9 | 10 | PAGE_SIZE=256 11 | 12 | TEST_BLOCK_FIRST=0 13 | TEST_BLOCK_LAST= 14 | TEST_SECTOR_FIRST=0 15 | TEST_SECTOR_LAST= 16 | TEST_PAGE_FIRST=0 17 | TEST_PAGE_LAST= 18 | 19 | QUIET="> /dev/null 2>&1" 20 | 21 | if [ "${V}" = "1" ]; then 22 | QUIET= 23 | fi 24 | 25 | function log() { 26 | echo -e "##### log: $@" 27 | } 28 | 29 | function cleanup() { 30 | echo "Cleaning up" 31 | 32 | return 0 33 | if [ "x${FLASH_TEST_PATH}" != "x" ]; then 34 | rm -f ${FLASH_TEST_PATH} 35 | fi 36 | 37 | if [ "x${BLOCK_TEST_PATH}" != "x" ]; then 38 | rm -f ${BLOCK_TEST_PATH} 39 | fi 40 | 41 | if [ "x${SECTOR_TEST_PATH}" != "x" ]; then 42 | rm -f ${SECTOR_TEST_PATH} 43 | fi 44 | 45 | if [ "x${PAGE_TEST_PATH}" != "x" ]; then 46 | rm -f ${PAGE_TEST_PATH} 47 | fi 48 | 49 | if [ "x${TMP_PATH}" != "x" ]; then 50 | rm -f ${TMP_PATH} 51 | fi 52 | } 53 | 54 | FLASH_UTIL_READER_PARAM="-s ${SERIAL_PORT} --serial-baud ${SERIAL_BAUD}" 55 | 56 | function callCmd() { 57 | local cmd="$@" 58 | 59 | log "calling \"$cmd\"" 60 | 61 | eval "$cmd ${QUIET} 2>&1" 62 | } 63 | 64 | function callFlashUtil() { 65 | local cmd="${FLASH_UTIL} ${FLASH_UTIL_READER_PARAM} $@" 66 | 67 | callCmd "$cmd" 68 | } 69 | 70 | UTIL_OUT=$(QUIET= callFlashUtil) 71 | 72 | FLASH_PAGE_SIZE=256 73 | FLASH_SIZE=$(echo "${UTIL_OUT}" | sed -n "s/^.*size: \([0-9]\+\)B.*$/\1/p") 74 | FLASH_BLOCKS=$(echo "${UTIL_OUT}" | sed -n "s/^.*blocks: \([0-9]\+\) .*$/\1/p") 75 | FLASH_SECTORS=$(echo "${UTIL_OUT}" | sed -n "s/^.*sectors: \([0-9]\+\) .*$/\1/p") 76 | FLASH_PAGES=$(( ${FLASH_SIZE} / ${FLASH_PAGE_SIZE} )) 77 | FLASH_BLOCK_SIZE=$(( ${FLASH_SIZE} / ${FLASH_BLOCKS} )) 78 | FLASH_SECTOR_SIZE=$(( ${FLASH_SIZE} / ${FLASH_SECTORS} )) 79 | 80 | FLASH_SECTORS_PER_BLOCK=$(( ${FLASH_SECTORS} / ${FLASH_BLOCKS} )) 81 | FLASH_PAGES_PER_SECTOR=$(( ${FLASH_SECTOR_SIZE} / ${FLASH_PAGE_SIZE} )) 82 | 83 | TEST_BLOCK_LAST=$(( ${FLASH_BLOCKS} - 1 )) 84 | TEST_SECTOR_LAST=$(( ${FLASH_SECTORS_PER_BLOCK} - 1 )) 85 | TEST_PAGE_LAST=$(( ${FLASH_PAGES_PER_SECTOR} - 1 )) 86 | 87 | echo -e "\n##" 88 | echo -e "## FLASH_SIZE: ${FLASH_SIZE} Bytes" 89 | echo -e "## FLASH_BLOCKS: ${FLASH_BLOCKS}" 90 | echo -e "## FLASH_SECTORS: ${FLASH_SECTORS}" 91 | echo -e "## FLASH_PAGES: ${FLASH_PAGES}" 92 | echo -e "## BLOCK_SIZE: ${FLASH_BLOCK_SIZE}" 93 | echo -e "## SECTOR_SIZE: ${FLASH_SECTOR_SIZE}" 94 | echo -e "## PAGE_SIZE: ${FLASH_PAGE_SIZE}" 95 | echo -e "##" 96 | echo -e "## SECTORS_PER_BLOCK ${FLASH_SECTORS_PER_BLOCK}" 97 | echo -e "## PAGES_PER_SECTOR ${FLASH_PAGES_PER_SECTOR}" 98 | echo -e "##" 99 | echo -e "## TEST_BLOCKS: ${TEST_BLOCK_FIRST}, ${TEST_BLOCK_LAST}" 100 | echo -e "## TEST_SECTORS: ${TEST_SECTOR_FIRST}, ${TEST_SECTOR_LAST}" 101 | echo -e "## TEST_PAGES: ${TEST_PAGE_FIRST}, ${TEST_PAGE_LAST}" 102 | echo -e "##\n" 103 | 104 | trap cleanup EXIT 105 | 106 | FLASH_TEST_PATH=$(mktemp) 107 | BLOCK_TEST_PATH=$(mktemp) 108 | SECTOR_TEST_PATH=$(mktemp) 109 | PAGE_TEST_PATH=$(mktemp) 110 | 111 | TMP_PATH=$(mktemp) 112 | 113 | head -c ${FLASH_PAGE_SIZE} /dev/urandom > ${PAGE_TEST_PATH} 114 | head -c ${FLASH_SECTOR_SIZE} /dev/urandom > ${SECTOR_TEST_PATH} 115 | head -c ${FLASH_BLOCK_SIZE} /dev/urandom > ${BLOCK_TEST_PATH} 116 | head -c ${FLASH_SIZE} /dev/urandom > ${FLASH_TEST_PATH} 117 | 118 | function test_erase_write_block() { 119 | log "\n------------------------- TEST, BLOCK: erase, write, read" 120 | 121 | local blocks=( ${TEST_BLOCK_FIRST} ${TEST_BLOCK_LAST} ) 122 | 123 | for block in ${blocks[@]}; do 124 | log "Erasing, writing and verifying test block $block" 125 | 126 | rm -f ${TMP_PATH} 127 | 128 | callFlashUtil --erase-block ${block} --no-redudant-cycles 129 | callFlashUtil --write-block ${block} -i ${BLOCK_TEST_PATH} -V 130 | callFlashUtil --read-block ${block} -o ${TMP_PATH} 131 | 132 | if ! diff ${BLOCK_TEST_PATH} ${TMP_PATH}; then 133 | log "\nReference sector\n" 134 | hexdump -C ${SECTOR_TEST_PATH} 135 | log "\nRead sector\n" 136 | hexdump -C ${TMP_PATH} 137 | return 1 138 | fi 139 | done 140 | } 141 | 142 | function test_erase_write_sector() { 143 | log "\n------------------------- TEST, SECTOR: erase, write, read" 144 | 145 | local block=${TEST_BLOCK_FIRST} 146 | local sectors=(${TEST_SECTOR_FIRST} ${TEST_SECTOR_LAST}) 147 | 148 | callFlashUtil --erase-block ${block} --no-redudant-cycles 149 | 150 | for sector in ${sectors[@]}; do 151 | local dstSector=$(( ${block} * ${FLASH_SECTORS_PER_BLOCK} + ${sector} )) 152 | 153 | log "Erasing, writing and verifying test sector $dstSector" 154 | 155 | rm -f ${TMP_PATH} 156 | 157 | callFlashUtil --erase-sector ${dstSector} --no-redudant-cycles 158 | callFlashUtil --write-sector ${dstSector} -i ${SECTOR_TEST_PATH} -V 159 | callFlashUtil --read-sector ${dstSector} -o ${TMP_PATH} 160 | 161 | if ! diff ${SECTOR_TEST_PATH} ${TMP_PATH}; then 162 | log "\nReference sector\n" 163 | hexdump -C ${SECTOR_TEST_PATH} 164 | log "\nRead sector\n" 165 | hexdump -C ${TMP_PATH} 166 | return 1 167 | fi 168 | done 169 | 170 | log "Verifying whole block" 171 | { 172 | head -c ${FLASH_BLOCK_SIZE} /dev/zero | tr "\000" "\377" > ${BLOCK_TEST_PATH} 173 | 174 | cat ${SECTOR_TEST_PATH} | dd of=${BLOCK_TEST_PATH} bs=1 conv=notrunc seek=$(( ${block} * ${FLASH_BLOCK_SIZE} + ${TEST_SECTOR_FIRST} * ${FLASH_SECTOR_SIZE} )) 175 | cat ${SECTOR_TEST_PATH} | dd of=${BLOCK_TEST_PATH} bs=1 conv=notrunc seek=$(( ${block} * ${FLASH_BLOCK_SIZE} + ${TEST_SECTOR_LAST} * ${FLASH_SECTOR_SIZE} )) 176 | } 177 | 178 | callFlashUtil --read-block ${block} -o ${TMP_PATH} 179 | 180 | if ! diff ${BLOCK_TEST_PATH} ${TMP_PATH}; then 181 | log "\nReference block\n" 182 | hexdump -C ${SECTOR_TEST_PATH} 183 | log "\nRead block\n" 184 | hexdump -C ${TMP_PATH} 185 | return 1 186 | fi 187 | } 188 | 189 | function test_erase_write_flash() { 190 | log "\n------------------------- TEST, FLASH: erase, write, read" 191 | 192 | rm -f ${TMP_PATH} 193 | 194 | log "Erasing whole chip" 195 | callFlashUtil --erase --no-redudant-cycles 196 | 197 | log "Writing test image" 198 | callFlashUtil --write -i ${FLASH_TEST_PATH} -V 199 | 200 | log "Reading whole chip" 201 | callFlashUtil --read -o ${TMP_PATH} 202 | 203 | diff ${FLASH_TEST_PATH} ${TMP_PATH} 204 | } 205 | 206 | test_erase_write_block 207 | test_erase_write_sector 208 | test_erase_write_flash 209 | 210 | log "SUCCESS" 211 | -------------------------------------------------------------------------------- /recipes/firmware/arduino/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | include(../../../firmware/arduino/CMakeLists.txt) 4 | 5 | project(recipe-firmware-arduino) -------------------------------------------------------------------------------- /recipes/firmware/rpi_pico/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(Git QUIET) 4 | 5 | if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/../../../.git") 6 | # Update submodules as needed 7 | option(GIT_SUBMODULE "Check submodules during build" ON) 8 | if(GIT_SUBMODULE) 9 | message(STATUS "Submodule update") 10 | execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive 11 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 12 | RESULT_VARIABLE GIT_SUBMOD_RESULT) 13 | if(NOT GIT_SUBMOD_RESULT EQUAL "0") 14 | message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") 15 | endif() 16 | endif() 17 | endif() 18 | 19 | if(NOT EXISTS "${CMAKE_SOURCE_DIR}/../../../firmware/rpi-pico/pico-sdk/README.md") 20 | message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") 21 | endif() 22 | 23 | include(${CMAKE_SOURCE_DIR}/../../../firmware/rpi-pico/CMakeLists.txt) 24 | 25 | project(recipe-firmware-rpi-pico) 26 | 27 | include(../../../common/CMakeLists.txt) 28 | include(../../../firmware/common/CMakeLists.txt) -------------------------------------------------------------------------------- /recipes/flashutil/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project(recipe-flashutil) 4 | 5 | include(../../common/CMakeLists.txt) 6 | include(../../flashutil/CMakeLists.txt) -------------------------------------------------------------------------------- /recipes/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project(recipe-test) 4 | 5 | include(../../common/CMakeLists.txt) 6 | include(../../firmware/common/CMakeLists.txt) 7 | include(../../flashutil/CMakeLists.txt) 8 | include(../../test/CMakeLists.txt) -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(unit_tests_project) 3 | 4 | # GoogleTest requires at least C++14 5 | set(CMAKE_CXX_STANDARD 14) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | include(FetchContent) 9 | FetchContent_Declare( 10 | googletest 11 | URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip 12 | ) 13 | # For Windows: Prevent overriding the parent project's compiler/linker settings 14 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 15 | FetchContent_MakeAvailable(googletest) 16 | 17 | enable_testing() 18 | 19 | set(src_path "${CMAKE_CURRENT_LIST_DIR}/src") 20 | 21 | file(GLOB_RECURSE TEST_FILES ${src_path}/*.cpp) 22 | 23 | add_executable(unit_tests 24 | ${TEST_FILES} 25 | ) 26 | 27 | target_include_directories(unit_tests 28 | PRIVATE 29 | ${CMAKE_CURRENT_LIST_DIR}/include 30 | ) 31 | 32 | target_link_libraries(unit_tests 33 | PRIVATE 34 | GTest::gtest_main 35 | firmware-common 36 | protocol 37 | flashutil 38 | ) 39 | 40 | include(GoogleTest) 41 | gtest_discover_tests(unit_tests) -------------------------------------------------------------------------------- /test/src/common/protocol/packet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common/protocol.h" 4 | #include "common/protocol/packet.h" 5 | 6 | 7 | #define STRING_PAYLOAD_SHORT "this is a sample payload string" 8 | #define STRING_PAYLOAD_LONG \ 9 | "long payload, long payload, long payload, long payload, long payload, long payload, long payload, long payload" \ 10 | "long payload, long payload, long payload, long payload, long payload, long payload, long payload, long payload" \ 11 | "long payload, long payload, long payload, long payload, long payload, long payload, long payload, long payload" \ 12 | "long payload, long payload, long payload, long payload, long payload, long payload, long payload, long payload" 13 | 14 | 15 | #define MEM_SIZE 512 16 | 17 | 18 | TEST(common_protocol, packet_no_payload) { 19 | std::vector txBuffer(MEM_SIZE, 0); 20 | size_t txBufferWritten; 21 | 22 | { 23 | ProtoPkt pkt; 24 | 25 | proto_pkt_init(&pkt, txBuffer.data(), txBuffer.size(), PROTO_CMD_GET_INFO, 0x12); 26 | 27 | ASSERT_EQ(pkt.code, PROTO_CMD_GET_INFO); 28 | ASSERT_EQ(pkt.id, 0x12); 29 | ASSERT_EQ(pkt.payload, nullptr); 30 | ASSERT_NE(pkt.payloadSize, 0); 31 | 32 | ASSERT_TRUE(proto_pkt_prepare(&pkt, txBuffer.data(), txBuffer.size(), 0)); 33 | 34 | ASSERT_EQ(pkt.payload, nullptr); 35 | ASSERT_EQ(pkt.payloadSize, 0); 36 | 37 | txBufferWritten = proto_pkt_encode(&pkt, txBuffer.data(), txBuffer.size()); 38 | 39 | ASSERT_GT(txBufferWritten, 0); 40 | } 41 | 42 | { 43 | uint8_t rxBuffer[MEM_SIZE] = { 0 }; 44 | 45 | ProtoPktDes ctx; 46 | ProtoPkt pkt; 47 | 48 | proto_pkt_dec_setup(&ctx, rxBuffer, sizeof(rxBuffer)); 49 | 50 | pkt.payload = (uint8_t *) 0x1234; 51 | pkt.payloadSize = 1231; 52 | 53 | size_t i; 54 | for (i = 0; i < txBufferWritten; i++) { 55 | auto ret = proto_pkt_dec_putByte(&ctx, txBuffer[i], &pkt); 56 | 57 | if (ret != PROTO_PKT_DES_RET_IDLE) { 58 | ASSERT_EQ(PROTO_PKT_DES_RET_GET_ERROR_CODE(ret), PROTO_NO_ERROR); 59 | 60 | ASSERT_EQ(pkt.code, PROTO_CMD_GET_INFO); 61 | ASSERT_EQ(pkt.id, 0x12); 62 | 63 | ASSERT_EQ(pkt.payload, nullptr); 64 | ASSERT_EQ(pkt.payloadSize, 0); 65 | break; 66 | } 67 | } 68 | 69 | ASSERT_NE(i, txBufferWritten); 70 | } 71 | } 72 | 73 | 74 | TEST(common_protocol, packet_short_payload) { 75 | std::vector txBuffer(MEM_SIZE, 0); 76 | size_t txBufferWritten; 77 | 78 | { 79 | ProtoPkt pkt; 80 | 81 | std::string payload = STRING_PAYLOAD_SHORT; 82 | 83 | proto_pkt_init(&pkt, txBuffer.data(), txBuffer.size(), PROTO_CMD_GET_INFO, 0x12); 84 | 85 | ASSERT_EQ(pkt.payload, nullptr); 86 | ASSERT_NE(pkt.payloadSize, 0); 87 | 88 | ASSERT_TRUE(proto_pkt_prepare(&pkt, txBuffer.data(), txBuffer.size(), payload.length())); 89 | 90 | ASSERT_NE(pkt.payload, nullptr); 91 | ASSERT_NE(pkt.payloadSize, 0); 92 | 93 | memcpy(pkt.payload, payload.data(), payload.length()); 94 | 95 | txBufferWritten = proto_pkt_encode(&pkt, txBuffer.data(), txBuffer.size()); 96 | 97 | ASSERT_GT(txBufferWritten, 0); 98 | } 99 | 100 | { 101 | uint8_t rxBuffer[MEM_SIZE] = { 0 }; 102 | 103 | ProtoPktDes ctx; 104 | ProtoPkt pkt; 105 | 106 | proto_pkt_dec_setup(&ctx, rxBuffer, sizeof(rxBuffer)); 107 | 108 | pkt.payload = (uint8_t *) 0x1234; 109 | pkt.payloadSize = 1231; 110 | 111 | for (size_t i = 0; i < sizeof(txBuffer); i++) { 112 | auto ret = proto_pkt_dec_putByte(&ctx, txBuffer[i], &pkt); 113 | 114 | if (ret != PROTO_PKT_DES_RET_IDLE) { 115 | ASSERT_EQ(PROTO_PKT_DES_RET_GET_ERROR_CODE(ret), PROTO_NO_ERROR); 116 | 117 | ASSERT_EQ(pkt.code, PROTO_CMD_GET_INFO); 118 | ASSERT_EQ(pkt.id, 0x12); 119 | 120 | ASSERT_EQ(std::string((char *)pkt.payload, pkt.payloadSize), STRING_PAYLOAD_SHORT); 121 | } 122 | } 123 | } 124 | } 125 | 126 | 127 | TEST(common_protocol, packet_long_payload) { 128 | std::vector txBuffer(MEM_SIZE, 0); 129 | size_t txBufferWritten; 130 | 131 | { 132 | ProtoPkt pkt; 133 | 134 | std::string payload = STRING_PAYLOAD_LONG; 135 | 136 | proto_pkt_init(&pkt, txBuffer.data(), txBuffer.size(), PROTO_CMD_GET_INFO, 0x12); 137 | 138 | ASSERT_EQ(pkt.payload, nullptr); 139 | ASSERT_NE(pkt.payloadSize, 0); 140 | 141 | ASSERT_TRUE(proto_pkt_prepare(&pkt, txBuffer.data(), txBuffer.size(), payload.length())); 142 | 143 | ASSERT_NE(pkt.payload, nullptr); 144 | ASSERT_NE(pkt.payloadSize, 0); 145 | 146 | memcpy(pkt.payload, payload.data(), payload.length()); 147 | 148 | txBufferWritten = proto_pkt_encode(&pkt, txBuffer.data(), txBuffer.size()); 149 | 150 | ASSERT_GT(txBufferWritten, 0); 151 | } 152 | 153 | { 154 | uint8_t rxBuffer[MEM_SIZE] = { 0 }; 155 | 156 | ProtoPktDes ctx; 157 | ProtoPkt pkt; 158 | 159 | proto_pkt_dec_setup(&ctx, rxBuffer, sizeof(rxBuffer)); 160 | 161 | pkt.payload = (uint8_t *) 0x1234; 162 | pkt.payloadSize = 1231; 163 | 164 | for (size_t i = 0; i < sizeof(txBuffer); i++) { 165 | auto ret = proto_pkt_dec_putByte(&ctx, txBuffer[i], &pkt); 166 | 167 | if (ret != PROTO_PKT_DES_RET_IDLE) { 168 | ASSERT_EQ(PROTO_PKT_DES_RET_GET_ERROR_CODE(ret), PROTO_NO_ERROR); 169 | 170 | ASSERT_EQ(pkt.code, PROTO_CMD_GET_INFO); 171 | ASSERT_EQ(pkt.id, 0x12); 172 | 173 | ASSERT_EQ(std::string((char *)pkt.payload, pkt.payloadSize), STRING_PAYLOAD_LONG); 174 | } 175 | } 176 | } 177 | } 178 | 179 | 180 | TEST(common_protocol, packet_error_payload_too_long) { 181 | std::vector txBuffer(MEM_SIZE, 0); 182 | size_t txBufferWritten; 183 | 184 | // Request bigger than memory buffer 185 | { 186 | ProtoPkt pkt; 187 | 188 | proto_pkt_init(&pkt, txBuffer.data(), txBuffer.size(), PROTO_CMD_GET_INFO, 0x12); 189 | 190 | ASSERT_FALSE(proto_pkt_prepare(&pkt, txBuffer.data(), txBuffer.size(), txBuffer.size() - 1)); 191 | ASSERT_TRUE (proto_pkt_prepare(&pkt, txBuffer.data(), txBuffer.size(), txBuffer.size() - PROTO_FRAME_MIN_SIZE)); 192 | 193 | txBufferWritten = proto_pkt_encode(&pkt, txBuffer.data(), txBuffer.size()); 194 | 195 | ASSERT_GT(txBufferWritten, 0); 196 | } 197 | 198 | { 199 | uint8_t rxBuffer[MEM_SIZE - PROTO_FRAME_MIN_SIZE - 1] = { 0 }; 200 | 201 | ProtoPktDes ctx; 202 | ProtoPkt pkt; 203 | 204 | proto_pkt_dec_setup(&ctx, rxBuffer, sizeof(rxBuffer)); 205 | 206 | pkt.payload = (uint8_t *) 0x1234; 207 | pkt.payloadSize = 1231; 208 | 209 | for (size_t i = 0; i < sizeof(txBuffer); i++) { 210 | auto ret = proto_pkt_dec_putByte(&ctx, txBuffer[i], &pkt); 211 | 212 | if (ret != PROTO_PKT_DES_RET_IDLE) { 213 | ASSERT_EQ(PROTO_PKT_DES_RET_GET_ERROR_CODE(ret), PROTO_ERROR_INVALID_LENGTH); 214 | 215 | ASSERT_EQ(pkt.code, PROTO_CMD_GET_INFO); 216 | ASSERT_EQ(pkt.id, 0x12); 217 | } 218 | } 219 | } 220 | } 221 | 222 | 223 | TEST(common_protocol, packet_error_invalid_crc) { 224 | std::vector txBuffer(MEM_SIZE, 0); 225 | size_t txBufferWritten; 226 | 227 | { 228 | ProtoPkt pkt; 229 | 230 | proto_pkt_init(&pkt, txBuffer.data(), txBuffer.size(), PROTO_CMD_GET_INFO, 0x12); 231 | 232 | ASSERT_EQ(pkt.payload, nullptr); 233 | ASSERT_NE(pkt.payloadSize, 0); 234 | 235 | ASSERT_TRUE(proto_pkt_prepare(&pkt, txBuffer.data(), txBuffer.size(), 0)); 236 | 237 | ASSERT_EQ(pkt.payload, nullptr); 238 | ASSERT_EQ(pkt.payloadSize, 0); 239 | 240 | txBufferWritten = proto_pkt_encode(&pkt, txBuffer.data(), txBuffer.size()); 241 | 242 | ASSERT_GT(txBufferWritten, 0); 243 | 244 | txBuffer[txBufferWritten - 1] = 0xff; 245 | } 246 | 247 | { 248 | uint8_t rxBuffer[MEM_SIZE - PROTO_FRAME_MIN_SIZE - 1] = { 0 }; 249 | 250 | ProtoPktDes ctx; 251 | ProtoPkt pkt; 252 | 253 | proto_pkt_dec_setup(&ctx, rxBuffer, sizeof(rxBuffer)); 254 | 255 | pkt.payload = (uint8_t *) 0x1234; 256 | pkt.payloadSize = 1231; 257 | 258 | for (size_t i = 0; i < sizeof(txBuffer); i++) { 259 | auto ret = proto_pkt_dec_putByte(&ctx, txBuffer[i], &pkt); 260 | 261 | if (ret != PROTO_PKT_DES_RET_IDLE) { 262 | ASSERT_EQ(PROTO_PKT_DES_RET_GET_ERROR_CODE(ret), PROTO_ERROR_INVALID_CRC); 263 | 264 | ASSERT_EQ(pkt.code, PROTO_CMD_GET_INFO); 265 | ASSERT_EQ(pkt.id, 0x12); 266 | } 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /test/src/common/protocol/request.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common/protocol.h" 4 | #include "common/protocol/packet.h" 5 | #include "common/protocol/request.h" 6 | 7 | 8 | struct RequestTestParameters { 9 | RequestTestParameters( 10 | uint8_t cmd, uint8_t id, 11 | std::function prepareReq, 12 | std::function fillReq, 13 | std::function validateReq 14 | ) { 15 | this->cmd = cmd; 16 | this->id = id; 17 | this->prepareReq = prepareReq; 18 | this->fillReq = fillReq; 19 | this->validateReq = validateReq; 20 | } 21 | 22 | uint8_t cmd; 23 | uint8_t id; 24 | 25 | std::function prepareReq; 26 | std::function fillReq; 27 | std::function validateReq; 28 | }; 29 | 30 | 31 | class RequestTestWithParameter : public testing::TestWithParam { 32 | }; 33 | 34 | 35 | TEST_P(RequestTestWithParameter, common_protocol_request) { 36 | std::vector txBuffer(1024, 0); 37 | std::vector rxBuffer(1024, 0); 38 | uint16_t txBufferWritten; 39 | 40 | ProtoPkt pkt; 41 | 42 | proto_pkt_init(&pkt, txBuffer.data(), txBuffer.size(), GetParam().cmd, GetParam().id); 43 | 44 | { 45 | ProtoReq req; 46 | 47 | proto_req_init(&req, pkt.payload, pkt.payloadSize, pkt.code); 48 | 49 | if (GetParam().prepareReq) { 50 | GetParam().prepareReq(req); 51 | } 52 | 53 | proto_pkt_prepare(&pkt, txBuffer.data(), txBuffer.size(), proto_req_getPayloadSize(&req)); 54 | 55 | { 56 | proto_req_assign(&req, pkt.payload, pkt.payloadSize); 57 | { 58 | if (GetParam().fillReq) { 59 | GetParam().fillReq(req); 60 | } 61 | } 62 | ASSERT_EQ(proto_req_encode(&req, pkt.payload, pkt.payloadSize), pkt.payloadSize); 63 | } 64 | 65 | txBufferWritten = proto_pkt_encode(&pkt, txBuffer.data(), txBuffer.size()); 66 | 67 | ASSERT_GT(txBufferWritten, 0); 68 | } 69 | 70 | { 71 | ProtoPktDes decoder; 72 | 73 | proto_pkt_dec_setup(&decoder, rxBuffer.data(), rxBuffer.size()); 74 | 75 | uint16_t i; 76 | for (i = 0; i < txBufferWritten; i++) { 77 | auto ret = proto_pkt_dec_putByte(&decoder, txBuffer[i], &pkt); 78 | 79 | if (ret != PROTO_PKT_DES_RET_IDLE) { 80 | ProtoReq req; 81 | 82 | ASSERT_EQ(PROTO_PKT_DES_RET_GET_ERROR_CODE(ret), PROTO_NO_ERROR); 83 | 84 | ASSERT_EQ(pkt.code, GetParam().cmd); 85 | ASSERT_EQ(pkt.id, GetParam().id); 86 | 87 | proto_req_init(&req, pkt.payload, pkt.payloadSize, pkt.code); 88 | 89 | if (proto_req_getPayloadSize(&req)) { 90 | ASSERT_NE(pkt.payload, nullptr); 91 | ASSERT_NE(pkt.payloadSize, 0); 92 | 93 | ASSERT_NE(proto_req_decode(&req, pkt.payload, pkt.payloadSize), 0); 94 | 95 | } else { 96 | ASSERT_EQ(pkt.payload, nullptr); 97 | ASSERT_EQ(pkt.payloadSize, 0); 98 | 99 | ASSERT_EQ(proto_req_decode(&req, pkt.payload, pkt.payloadSize), 0); 100 | } 101 | 102 | proto_req_assign(&req, pkt.payload, pkt.payloadSize); 103 | 104 | ASSERT_EQ(req.cmd, GetParam().cmd); 105 | ASSERT_EQ(pkt.id, GetParam().id); 106 | 107 | if (GetParam().validateReq) { 108 | GetParam().validateReq(req); 109 | } 110 | break; 111 | } 112 | } 113 | 114 | ASSERT_NE(i, txBufferWritten); 115 | } 116 | } 117 | 118 | 119 | INSTANTIATE_TEST_SUITE_P(common_protocol_request, RequestTestWithParameter, testing::Values( 120 | RequestTestParameters( 121 | PROTO_CMD_GET_INFO, 0x45, 122 | 123 | {}, 124 | {}, 125 | {} 126 | ), 127 | 128 | RequestTestParameters( 129 | PROTO_CMD_SPI_TRANSFER, 0x45, 130 | 131 | [](ProtoReq &req) { 132 | auto &t = req.request.transfer; 133 | 134 | t.flags = 0xa5; 135 | 136 | t.txBufferSize = 10; 137 | t.rxBufferSize = 12; 138 | t.rxSkipSize = 2; 139 | }, 140 | 141 | [](ProtoReq &req) { 142 | auto &t = req.request.transfer; 143 | 144 | t.txBuffer[0] = 0x10; 145 | t.txBuffer[1] = 0x20; 146 | }, 147 | 148 | [](ProtoReq &req) { 149 | auto &t = req.request.transfer; 150 | 151 | ASSERT_EQ(t.flags, 0xa5); 152 | ASSERT_EQ(t.txBufferSize, 10); 153 | ASSERT_EQ(t.rxBufferSize, 12); 154 | ASSERT_EQ(t.rxSkipSize, 2); 155 | 156 | ASSERT_TRUE(t.txBuffer != NULL); 157 | 158 | ASSERT_EQ(t.txBuffer[0], 0x10); 159 | ASSERT_EQ(t.txBuffer[1], 0x20); 160 | } 161 | ) 162 | )); 163 | -------------------------------------------------------------------------------- /test/src/common/protocol/response.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common/protocol.h" 4 | #include "common/protocol/response.h" 5 | #include "common/protocol/packet.h" 6 | 7 | 8 | struct ResponseTestParameters { 9 | ResponseTestParameters( 10 | uint8_t cmd, uint8_t code, uint8_t id, 11 | std::function prepareRes, 12 | std::function fillRes, 13 | std::function validateRes 14 | ) { 15 | this->cmd = cmd; 16 | this->code = code; 17 | this->id = id; 18 | this->prepareRes = prepareRes; 19 | this->fillRes = fillRes; 20 | this->validateRes = validateRes; 21 | } 22 | 23 | uint8_t cmd; 24 | uint8_t code; 25 | uint8_t id; 26 | 27 | std::function prepareRes; 28 | std::function fillRes; 29 | std::function validateRes; 30 | }; 31 | 32 | 33 | class ResponseTestWithParameter : public testing::TestWithParam { 34 | }; 35 | 36 | 37 | TEST_P(ResponseTestWithParameter, common_protocol_response) { 38 | std::vector txBuffer(1024, 0); 39 | std::vector rxBuffer(1024, 0); 40 | size_t txBufferWritten; 41 | 42 | 43 | ProtoPkt pkt; 44 | 45 | proto_pkt_init(&pkt, txBuffer.data(), txBuffer.size(), GetParam().code, GetParam().id); 46 | 47 | { 48 | ProtoRes res; 49 | 50 | proto_res_init(&res, pkt.payload, pkt.payloadSize, GetParam().cmd); 51 | 52 | if (GetParam().prepareRes) { 53 | GetParam().prepareRes(res); 54 | } 55 | 56 | ASSERT_TRUE(proto_pkt_prepare(&pkt, txBuffer.data(), txBuffer.size(), proto_res_getPayloadSize(&res))); 57 | 58 | { 59 | proto_res_assign(&res, pkt.payload, pkt.payloadSize); 60 | { 61 | if (GetParam().fillRes) { 62 | GetParam().fillRes(res); 63 | } 64 | } 65 | ASSERT_EQ(proto_res_encode(&res, pkt.payload, pkt.payloadSize), pkt.payloadSize); 66 | 67 | txBufferWritten = proto_pkt_encode(&pkt, txBuffer.data(), txBuffer.size()); 68 | } 69 | 70 | ASSERT_GT(txBufferWritten, 0); 71 | } 72 | 73 | { 74 | ProtoPktDes decoder; 75 | 76 | proto_pkt_dec_setup(&decoder, rxBuffer.data(), txBuffer.size()); 77 | 78 | uint16_t i; 79 | for (i = 0; i < txBufferWritten; i++) { 80 | auto ret = proto_pkt_dec_putByte(&decoder, txBuffer[i], &pkt); 81 | 82 | if (ret != PROTO_PKT_DES_RET_IDLE) { 83 | ProtoRes res; 84 | 85 | ASSERT_EQ(PROTO_PKT_DES_RET_GET_ERROR_CODE(ret), PROTO_NO_ERROR); 86 | 87 | ASSERT_EQ(pkt.code, GetParam().code); 88 | ASSERT_EQ(pkt.id, GetParam().id); 89 | 90 | proto_res_init(&res, pkt.payload, pkt.payloadSize, GetParam().cmd); 91 | 92 | if (proto_res_getPayloadSize(&res)) { 93 | ASSERT_NE(pkt.payload, nullptr); 94 | ASSERT_NE(pkt.payloadSize, 0); 95 | 96 | ASSERT_NE(proto_res_decode(&res, pkt.payload, pkt.payloadSize), 0); 97 | 98 | } else { 99 | ASSERT_EQ(pkt.payload, nullptr); 100 | ASSERT_EQ(pkt.payloadSize, 0); 101 | 102 | ASSERT_EQ(proto_res_decode(&res, pkt.payload, pkt.payloadSize), 0); 103 | } 104 | 105 | proto_res_assign(&res, pkt.payload, pkt.payloadSize); 106 | 107 | ASSERT_EQ(res.cmd, GetParam().cmd); 108 | ASSERT_EQ(pkt.id, GetParam().id); 109 | 110 | if (GetParam().validateRes) { 111 | GetParam().validateRes(pkt, res); 112 | } 113 | break; 114 | } 115 | } 116 | 117 | ASSERT_NE(i, txBufferWritten); 118 | } 119 | } 120 | 121 | 122 | INSTANTIATE_TEST_SUITE_P(common_protocol_response, ResponseTestWithParameter, testing::Values( 123 | ResponseTestParameters( 124 | PROTO_CMD_GET_INFO, PROTO_NO_ERROR, 0x45, 125 | 126 | {}, 127 | {}, 128 | {} 129 | ), 130 | 131 | ResponseTestParameters( 132 | PROTO_CMD_SPI_TRANSFER, PROTO_NO_ERROR, 0x45, 133 | 134 | [](ProtoRes &res) { 135 | auto &t = res.response.transfer; 136 | 137 | t.rxBufferSize = 12; 138 | }, 139 | 140 | [](ProtoRes &res) { 141 | auto &t = res.response.transfer; 142 | 143 | t.rxBuffer[0] = 0x10; 144 | t.rxBuffer[1] = 0x20; 145 | }, 146 | 147 | [](ProtoPkt &pkt, ProtoRes &res) { 148 | auto &t = res.response.transfer; 149 | 150 | ASSERT_EQ(t.rxBufferSize, 12); 151 | 152 | ASSERT_TRUE(t.rxBuffer != NULL); 153 | 154 | ASSERT_EQ(t.rxBuffer[0], 0x10); 155 | ASSERT_EQ(t.rxBuffer[1], 0x20); 156 | } 157 | ), 158 | 159 | ResponseTestParameters( 160 | PROTO_CMD_GET_INFO, PROTO_ERROR_INVALID_CRC, 0x45, 161 | 162 | {}, 163 | {}, 164 | [](ProtoPkt &pkt, ProtoRes &res) { 165 | ASSERT_EQ(pkt.code, PROTO_ERROR_INVALID_CRC); 166 | } 167 | ) 168 | )); 169 | -------------------------------------------------------------------------------- /test/src/common/protocol/stack.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common/protocol.h" 4 | #include "common/protocol/request.h" 5 | #include "common/protocol/response.h" 6 | 7 | 8 | TEST(common_protocol, request_getInfo) { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /test/src/firmware/programmer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common/protocol.h" 4 | #include "flashutil/debug.h" 5 | #include "firmware/programmer.h" 6 | 7 | 8 | static void _deserializeResponse(uint8_t *buffer, uint16_t bufferSize, uint8_t cmd, ProtoPkt &pkt, ProtoRes &res) { 9 | ProtoPktDes des; 10 | 11 | proto_pkt_dec_setup(&des, buffer, bufferSize); 12 | 13 | for (uint16_t i = 0; i < bufferSize; i++) { 14 | auto ret = proto_pkt_dec_putByte(&des, buffer[i], &pkt); 15 | if (ret != PROTO_PKT_DES_RET_IDLE) { 16 | proto_res_init(&res, pkt.payload, pkt.payloadSize, cmd); 17 | 18 | proto_res_decode(&res, pkt.payload, pkt.payloadSize); 19 | proto_res_assign(&res, pkt.payload, pkt.payloadSize); 20 | } 21 | } 22 | } 23 | 24 | 25 | static void _requestGetInfoCallback(ProtoReq *request, ProtoRes *response, void *callbackData) { 26 | uint8_t *data = (uint8_t *) callbackData; 27 | 28 | ASSERT_TRUE(data != NULL); 29 | 30 | ASSERT_EQ(request->cmd, PROTO_CMD_GET_INFO); 31 | 32 | *data |= (1 << 0); 33 | } 34 | 35 | 36 | static void _responseGetInfoCallback(uint8_t *buffer, uint16_t bufferSize, void *callbackData) { 37 | uint8_t *data = (uint8_t *) callbackData; 38 | 39 | ASSERT_TRUE(data != NULL); 40 | 41 | { 42 | ProtoPkt pkt; 43 | ProtoRes res; 44 | 45 | _deserializeResponse(buffer, bufferSize, PROTO_CMD_GET_INFO, pkt, res); 46 | 47 | ASSERT_EQ(res.cmd, PROTO_CMD_GET_INFO); 48 | 49 | ASSERT_EQ(res.response.getInfo.version.major, PROTO_VERSION_MAJOR); 50 | ASSERT_EQ(res.response.getInfo.version.minor, PROTO_VERSION_MINOR); 51 | ASSERT_GT(res.response.getInfo.packetSize, 0); 52 | } 53 | 54 | *data |= (1 << 1); 55 | } 56 | 57 | 58 | TEST(firmware_programmer, proto_getInfo) { 59 | std::vector buffer(1024, 0); 60 | 61 | { 62 | Programmer prog; 63 | 64 | uint8_t callbackData = 0; 65 | 66 | programmer_setup( 67 | &prog, buffer.data(), buffer.size(), _requestGetInfoCallback, _responseGetInfoCallback, &callbackData 68 | ); 69 | 70 | { 71 | std::vector reqBuffer(1024, 0); 72 | uint16_t reqWritten; 73 | 74 | { 75 | ProtoPkt pkt; 76 | 77 | proto_pkt_init(&pkt, reqBuffer.data(), reqBuffer.size(), PROTO_CMD_GET_INFO, 0x05); 78 | 79 | { 80 | ProtoReq req; 81 | 82 | proto_req_init (&req, pkt.payload, pkt.payloadSize, pkt.code); 83 | proto_req_assign(&req, pkt.payload, pkt.payloadSize); 84 | proto_req_encode(&req, pkt.payload, pkt.payloadSize); 85 | 86 | ASSERT_TRUE(proto_pkt_prepare(&pkt, reqBuffer.data(), reqBuffer.size(), proto_req_getPayloadSize(&req))); 87 | } 88 | 89 | reqWritten = proto_pkt_encode(&pkt, reqBuffer.data(), reqBuffer.size()); 90 | } 91 | 92 | for (int i = 0; i < reqWritten; i++) { 93 | programmer_putByte(&prog, reqBuffer[i]); 94 | } 95 | } 96 | 97 | ASSERT_EQ(callbackData, 0x03); 98 | } 99 | } 100 | 101 | 102 | static void _requestTransferCallback(ProtoReq *request, ProtoRes *response, void *callbackData) { 103 | uint8_t *data = (uint8_t *) callbackData; 104 | 105 | ASSERT_TRUE(data != NULL); 106 | 107 | ASSERT_EQ(request->cmd, PROTO_CMD_SPI_TRANSFER); 108 | 109 | { 110 | ProtoReqTransfer &t = request->request.transfer; 111 | 112 | ASSERT_EQ(t.flags, PROTO_SPI_TRANSFER_FLAG_KEEP_CS); 113 | ASSERT_EQ(t.rxBufferSize, 128); 114 | ASSERT_EQ(t.rxSkipSize, 32); 115 | ASSERT_EQ(t.txBufferSize, 32); 116 | 117 | ASSERT_TRUE(t.txBuffer != NULL); 118 | 119 | for (uint16_t i = 0; i < t.txBufferSize; i++) { 120 | ASSERT_EQ(t.txBuffer[i], i); 121 | } 122 | } 123 | 124 | { 125 | ProtoResTransfer &t = response->response.transfer; 126 | 127 | for (uint16_t i = 0; i < t.rxBufferSize; i++) { 128 | t.rxBuffer[i] = 255 - i; 129 | } 130 | } 131 | 132 | *data |= (1 << 0); 133 | } 134 | 135 | 136 | static void _responseTransferCallback(uint8_t *buffer, uint16_t bufferSize, void *callbackData) { 137 | uint8_t *data = (uint8_t *) callbackData; 138 | 139 | ASSERT_TRUE(data != NULL); 140 | 141 | { 142 | ProtoPkt pkt; 143 | ProtoRes res; 144 | 145 | _deserializeResponse(buffer, bufferSize, PROTO_CMD_SPI_TRANSFER, pkt, res); 146 | 147 | ASSERT_EQ(res.cmd, PROTO_CMD_SPI_TRANSFER); 148 | ASSERT_EQ(pkt.code, PROTO_NO_ERROR); 149 | 150 | { 151 | ProtoResTransfer &t = res.response.transfer; 152 | 153 | ASSERT_EQ(t.rxBufferSize, 128); 154 | ASSERT_TRUE(t.rxBuffer != NULL); 155 | 156 | for (uint16_t i = 0; i < t.rxBufferSize; i++) { 157 | ASSERT_EQ(t.rxBuffer[i], 255 - i); 158 | } 159 | } 160 | } 161 | 162 | *data |= (1 << 1); 163 | } 164 | 165 | 166 | TEST(firmware_programmer, proto_transfer) { 167 | std::vector buffer(1024, 0); 168 | 169 | { 170 | Programmer prog; 171 | 172 | uint8_t callbackData = 0; 173 | 174 | programmer_setup( 175 | &prog, buffer.data(), buffer.size(), _requestTransferCallback, _responseTransferCallback, &callbackData 176 | ); 177 | 178 | { 179 | std::vector reqBuffer(1024, 0); 180 | uint16_t reqWritten; 181 | 182 | { 183 | ProtoPkt pkt; 184 | 185 | proto_pkt_init(&pkt, reqBuffer.data(), reqBuffer.size(), PROTO_CMD_SPI_TRANSFER, 0x5); 186 | 187 | { 188 | ProtoReq req; 189 | 190 | proto_req_init(&req, pkt.payload, pkt.payloadSize, pkt.code); 191 | 192 | req.request.transfer.rxBufferSize = 128; 193 | req.request.transfer.txBufferSize = 32; 194 | req.request.transfer.rxSkipSize = 32; 195 | req.request.transfer.flags = PROTO_SPI_TRANSFER_FLAG_KEEP_CS; 196 | 197 | proto_pkt_prepare(&pkt, reqBuffer.data(), reqBuffer.size(), proto_req_getPayloadSize(&req)); 198 | 199 | proto_req_assign(&req, pkt.payload, pkt.payloadSize); 200 | { 201 | ASSERT_TRUE(req.request.transfer.txBuffer != NULL); 202 | 203 | for (uint16_t i = 0; i < req.request.transfer.txBufferSize; i++) { 204 | req.request.transfer.txBuffer[i] = i; 205 | } 206 | } 207 | proto_req_encode(&req, pkt.payload, pkt.payloadSize); 208 | } 209 | 210 | reqWritten = proto_pkt_encode(&pkt, reqBuffer.data(), reqBuffer.size()); 211 | } 212 | 213 | for (int i = 0; i < reqWritten; i++) { 214 | programmer_putByte(&prog, reqBuffer[i]); 215 | } 216 | } 217 | 218 | ASSERT_EQ(callbackData, 0x03); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /test/src/flashutil/programmer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flashutil/spi/serial.h" 6 | #include "flashutil/serial/hw.h" 7 | #include "flashutil/programmer.h" 8 | #include "flashutil/entryPoint.h" 9 | #include "flashutil/flash/registry/reader/json.h" 10 | #include "flashutil/debug.h" 11 | 12 | #include "serialProgrammer.h" 13 | 14 | #define PAGE_SIZE (16) 15 | #define PAGE_COUNT (32) 16 | #define PAGES_PER_SECTOR (2) 17 | 18 | #define SECTOR_COUNT (PAGE_COUNT / PAGES_PER_SECTOR) 19 | #define SECTOR_SIZE (PAGE_SIZE * PAGES_PER_SECTOR) 20 | #define SECTORS_PER_BLOCK (4) 21 | 22 | #define BLOCK_COUNT (SECTOR_COUNT / SECTORS_PER_BLOCK) 23 | #define BLOCK_SIZE (SECTOR_SIZE * SECTORS_PER_BLOCK) 24 | 25 | #define PAYLOAD_SIZE 13 26 | 27 | 28 | static FlashRegistry &getFlashRegistry() { 29 | static FlashRegistry registry; 30 | 31 | { 32 | FlashRegistryJsonReader reader; 33 | 34 | std::string path = __FILE__; 35 | 36 | path = path.substr(0, path.rfind('/')); 37 | 38 | std::ifstream stream(path + "/../../../flashutil/etc/chips.json"); 39 | 40 | reader.read(stream, registry); 41 | } 42 | 43 | return registry; 44 | } 45 | 46 | 47 | static std::unique_ptr createSerial(Flash &info) { 48 | const char *path = getenv("TEST_SERIAL_PATH"); 49 | 50 | if (path != nullptr) { 51 | int baudInt = 115200; 52 | 53 | { 54 | const char *baud = getenv("TEST_SERIAL_BAUD"); 55 | 56 | if (baud != nullptr) { 57 | baudInt = std::stoi(baud); 58 | } 59 | } 60 | 61 | return std::make_unique(path, baudInt); 62 | 63 | } else { 64 | info.setId({ 0x01, 0x02, 0x03 }); 65 | 66 | info.setBlockSize (BLOCK_SIZE); 67 | info.setBlockCount (BLOCK_COUNT); 68 | info.setSectorSize (SECTOR_SIZE); 69 | info.setSectorCount(SECTOR_COUNT); 70 | info.setPageSize (PAGE_SIZE); 71 | info.setPageCount (PAGE_COUNT); 72 | 73 | info.setProtectMask(0x8c); 74 | 75 | return std::make_unique(info, PAYLOAD_SIZE); 76 | } 77 | } 78 | 79 | 80 | TEST(flashutil_entry_point, detect_flash) { 81 | Flash flashInfo; 82 | 83 | std::unique_ptr serial = createSerial(flashInfo); 84 | std::unique_ptr spi = std::make_unique(*serial.get()); 85 | 86 | { 87 | flashutil::EntryPoint::Parameters params; 88 | 89 | params.operation = flashutil::EntryPoint::Operation::NO_OPERATION; 90 | params.mode = flashutil::EntryPoint::Mode::CHIP; 91 | params.flashInfo = &flashInfo; 92 | 93 | flashutil::EntryPoint::call(*spi.get(), getFlashRegistry(), flashInfo, params); 94 | 95 | ASSERT_NE(flashInfo.getSize(), 0); 96 | } 97 | } 98 | 99 | 100 | TEST(flashutil_entry_point, write_erase_block) { 101 | Flash flashInfo; 102 | 103 | auto serial = createSerial(flashInfo); 104 | 105 | std::unique_ptr spi = std::make_unique(*serial.get()); 106 | 107 | debug_setLevel(DebugLevel::DEBUG_LEVEL_INFO); 108 | 109 | { 110 | std::stringstream block1Stream; 111 | std::stringstream block2Stream; 112 | std::stringstream block3Stream; 113 | std::ostringstream blockReadStream; 114 | 115 | std::vector operations; 116 | 117 | { 118 | flashutil::EntryPoint::Parameters params; 119 | 120 | params.operation = flashutil::EntryPoint::Operation::NO_OPERATION; 121 | params.mode = flashutil::EntryPoint::Mode::CHIP; 122 | params.flashInfo = &flashInfo; 123 | 124 | params.afterExecution = [&block1Stream, &block2Stream, &block3Stream](const flashutil::EntryPoint::Parameters ¶ms) { 125 | size_t size = params.flashInfo->getBlockSize(); 126 | 127 | for (int i = 0; i < size; i++) { 128 | block1Stream << (char)(i + 1); 129 | block2Stream << (char)(i + 2); 130 | block3Stream << (char)(i + 3); 131 | } 132 | }; 133 | 134 | operations.push_back(params); 135 | } 136 | 137 | // unlock 138 | { 139 | flashutil::EntryPoint::Parameters params; 140 | 141 | params.operation = flashutil::EntryPoint::Operation::UNLOCK; 142 | params.mode = flashutil::EntryPoint::Mode::CHIP; 143 | params.omitRedundantWrites = true; 144 | params.verify = true; 145 | 146 | operations.push_back(params); 147 | } 148 | 149 | // Erase 150 | { 151 | flashutil::EntryPoint::Parameters params; 152 | 153 | params.operation = flashutil::EntryPoint::Operation::ERASE; 154 | params.mode = flashutil::EntryPoint::Mode::BLOCK; 155 | params.omitRedundantWrites = true; 156 | params.verify = true; 157 | 158 | params.index = 0; 159 | operations.push_back(params); 160 | 161 | params.index = 1; 162 | operations.push_back(params); 163 | 164 | params.index = 2; 165 | operations.push_back(params); 166 | } 167 | 168 | // Write 169 | { 170 | flashutil::EntryPoint::Parameters params; 171 | 172 | params.operation = flashutil::EntryPoint::Operation::WRITE; 173 | params.mode = flashutil::EntryPoint::Mode::BLOCK; 174 | params.omitRedundantWrites = true; 175 | params.verify = true; 176 | 177 | params.index = 0; 178 | params.inStream = &block1Stream; 179 | operations.push_back(params); 180 | 181 | params.index = 1; 182 | params.inStream = &block2Stream; 183 | operations.push_back(params); 184 | 185 | params.index = 2; 186 | params.inStream = &block3Stream; 187 | operations.push_back(params); 188 | } 189 | 190 | // Erase 1 in the middle 191 | { 192 | flashutil::EntryPoint::Parameters params; 193 | 194 | params.operation = flashutil::EntryPoint::Operation::ERASE; 195 | params.mode = flashutil::EntryPoint::Mode::BLOCK; 196 | params.omitRedundantWrites = true; 197 | params.verify = true; 198 | 199 | params.index = 1; 200 | operations.push_back(params); 201 | } 202 | 203 | // Read 204 | { 205 | flashutil::EntryPoint::Parameters params; 206 | 207 | params.operation = flashutil::EntryPoint::Operation::READ; 208 | params.mode = flashutil::EntryPoint::Mode::BLOCK; 209 | params.outStream = &blockReadStream; 210 | 211 | params.beforeExecution = [&blockReadStream](const flashutil::EntryPoint::Parameters &) { 212 | blockReadStream.str(""); 213 | }; 214 | 215 | { 216 | params.index = 0; 217 | params.afterExecution = [&block1Stream, &blockReadStream](const flashutil::EntryPoint::Parameters ¶ms) { 218 | ASSERT_EQ(block1Stream.str(), blockReadStream.str()); 219 | }; 220 | 221 | operations.push_back(params); 222 | } 223 | 224 | { 225 | params.index = 2; 226 | params.afterExecution = [&block3Stream, &blockReadStream](const flashutil::EntryPoint::Parameters ¶ms) { 227 | ASSERT_EQ(block3Stream.str(), blockReadStream.str()); 228 | }; 229 | 230 | operations.push_back(params); 231 | } 232 | 233 | { 234 | params.index = 1; 235 | params.afterExecution = [&blockReadStream](const flashutil::EntryPoint::Parameters ¶ms) { 236 | for (auto c : blockReadStream.str()) { 237 | ASSERT_EQ(c, (char)0xff); 238 | } 239 | }; 240 | 241 | operations.push_back(params); 242 | } 243 | } 244 | 245 | flashutil::EntryPoint::call(*spi.get(), getFlashRegistry(), flashInfo, operations); 246 | } 247 | } 248 | 249 | 250 | TEST(flashutil_entry_point, write_program_whole) { 251 | Flash flashInfo; 252 | 253 | auto serial = createSerial(flashInfo); 254 | 255 | std::unique_ptr spi = std::make_unique(*serial.get()); 256 | 257 | size_t chipSize = 0; 258 | 259 | debug_setLevel(DebugLevel::DEBUG_LEVEL_INFO); 260 | 261 | { 262 | flashutil::EntryPoint::Parameters params; 263 | 264 | params.operation = flashutil::EntryPoint::Operation::NO_OPERATION; 265 | params.mode = flashutil::EntryPoint::Mode::CHIP; 266 | params.flashInfo = &flashInfo; 267 | 268 | flashutil::EntryPoint::call(*spi.get(), getFlashRegistry(), flashInfo, params); 269 | 270 | chipSize = flashInfo.getSize(); 271 | } 272 | 273 | ASSERT_GT(chipSize, 0); 274 | 275 | std::cout << "Testing on flash with size: " << std::to_string(chipSize) << std::endl; 276 | 277 | { 278 | std::vector operations; 279 | 280 | std::string randomData; 281 | std::stringstream readData; 282 | 283 | for (size_t i = 0; i < chipSize; i++) { 284 | randomData.push_back(rand() % 0xff); 285 | } 286 | 287 | std::stringstream srcData(randomData); 288 | 289 | // unlock 290 | { 291 | flashutil::EntryPoint::Parameters params; 292 | 293 | params.operation = flashutil::EntryPoint::Operation::UNLOCK; 294 | params.mode = flashutil::EntryPoint::Mode::CHIP; 295 | params.omitRedundantWrites = true; 296 | params.verify = true; 297 | 298 | operations.push_back(params); 299 | } 300 | 301 | // Erase 302 | { 303 | flashutil::EntryPoint::Parameters params; 304 | 305 | params.operation = flashutil::EntryPoint::Operation::ERASE; 306 | params.mode = flashutil::EntryPoint::Mode::CHIP; 307 | params.omitRedundantWrites = false; 308 | params.verify = true; 309 | 310 | operations.push_back(params); 311 | } 312 | 313 | // Write 314 | { 315 | flashutil::EntryPoint::Parameters params; 316 | 317 | params.operation = flashutil::EntryPoint::Operation::WRITE; 318 | params.mode = flashutil::EntryPoint::Mode::CHIP; 319 | params.omitRedundantWrites = false; 320 | params.verify = true; 321 | params.inStream = &srcData; 322 | params.index = 0; 323 | 324 | operations.push_back(params); 325 | } 326 | 327 | // Read 328 | { 329 | flashutil::EntryPoint::Parameters params; 330 | 331 | params.operation = flashutil::EntryPoint::Operation::READ; 332 | params.mode = flashutil::EntryPoint::Mode::CHIP; 333 | params.outStream = &readData; 334 | params.index = 0; 335 | 336 | operations.push_back(params); 337 | } 338 | 339 | flashutil::EntryPoint::call(*spi.get(), getFlashRegistry(), flashInfo, operations); 340 | 341 | ASSERT_EQ(randomData, readData.str()); 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /test/src/flashutil/serialProgrammer.h: -------------------------------------------------------------------------------- 1 | #ifndef FLASHUTIL_DUMMYSPI_H_ 2 | #define FLASHUTIL_DUMMYSPI_H_ 3 | 4 | #include "flashutil/spi.h" 5 | #include "flashutil/spi/serial.h" 6 | #include "flashutil/flash.h" 7 | 8 | 9 | class SerialProgrammer : public Serial { 10 | public: 11 | SerialProgrammer(const Flash &flashInfo, size_t transferSize); 12 | virtual ~SerialProgrammer(); 13 | 14 | virtual void write(void *buffer, std::size_t bufferSize, int timeoutMs) override; 15 | virtual void read(void *buffer, std::size_t bufferSize, int timeoutMs) override; 16 | 17 | private: 18 | class Impl; 19 | 20 | std::unique_ptr _self; 21 | }; 22 | 23 | #endif /* FLASHUTIL_DUMMYSPI_H_ */ 24 | --------------------------------------------------------------------------------