├── .gitignore ├── doc ├── v2.jpg ├── pic.jpg ├── result.jpg ├── connector.jpg ├── perfboard1.jpg ├── perfboard2.jpg ├── perfboard3.jpg └── v2_enclosure.jpg ├── 3d_print ├── box.stl ├── cap.stl ├── usb-blaster.f3d └── README.md ├── src ├── ws2812.h ├── blaster.h ├── ws2812.c ├── CMakeLists.txt ├── ft245_eeprom.h ├── ws2812.pio ├── pico_sdk_import.cmake ├── tusb_config.h ├── blaster.c ├── usb_descriptors.c └── main.c ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | _deps 4 | cmake-* 5 | build 6 | .DS_Store -------------------------------------------------------------------------------- /doc/v2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/doc/v2.jpg -------------------------------------------------------------------------------- /doc/pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/doc/pic.jpg -------------------------------------------------------------------------------- /doc/result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/doc/result.jpg -------------------------------------------------------------------------------- /3d_print/box.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/3d_print/box.stl -------------------------------------------------------------------------------- /3d_print/cap.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/3d_print/cap.stl -------------------------------------------------------------------------------- /doc/connector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/doc/connector.jpg -------------------------------------------------------------------------------- /doc/perfboard1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/doc/perfboard1.jpg -------------------------------------------------------------------------------- /doc/perfboard2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/doc/perfboard2.jpg -------------------------------------------------------------------------------- /doc/perfboard3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/doc/perfboard3.jpg -------------------------------------------------------------------------------- /doc/v2_enclosure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/doc/v2_enclosure.jpg -------------------------------------------------------------------------------- /3d_print/usb-blaster.f3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisiseth/pico-usb-blaster/HEAD/3d_print/usb-blaster.f3d -------------------------------------------------------------------------------- /src/ws2812.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void ws2812_init(int pin); 6 | void ws2812_set(uint32_t color); -------------------------------------------------------------------------------- /src/blaster.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void blaster_reset(void); 6 | 7 | int blaster_process(uint8_t rxBuf[], int rxCount, uint8_t txBuf[]); 8 | 9 | -------------------------------------------------------------------------------- /src/ws2812.c: -------------------------------------------------------------------------------- 1 | #include "ws2812.h" 2 | #include "pico/stdlib.h" 3 | #include "hardware/pio.h" 4 | #include "ws2812.pio.h" 5 | 6 | #define WS2812_PIO pio1 7 | #define WS2812_SM 0 8 | #define WS2812_FREQ 800000 9 | 10 | void ws2812_init(int pin) 11 | { 12 | int offset = pio_add_program(WS2812_PIO, &ws2812_program); 13 | ws2812_program_init(WS2812_PIO, WS2812_SM, offset, pin, WS2812_FREQ, false); 14 | } 15 | 16 | void ws2812_set(uint32_t color) 17 | { 18 | pio_sm_put_blocking(WS2812_PIO, WS2812_SM, color); 19 | } -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | # initialize the SDK based on PICO_SDK_PATH 4 | # note: this must happen before project() 5 | include(pico_sdk_import.cmake) 6 | 7 | project(pico_usb_blaster) 8 | 9 | add_compile_options(-O3) 10 | add_compile_options(-Wall) 11 | 12 | # initialize the Raspberry Pi Pico SDK 13 | pico_sdk_init() 14 | 15 | # rest of your project 16 | 17 | add_executable(pico_usb_blaster 18 | main.c 19 | usb_descriptors.c 20 | blaster.c 21 | ws2812.c 22 | ) 23 | 24 | pico_generate_pio_header(pico_usb_blaster ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio) 25 | 26 | # Make sure TinyUSB can find tusb_config.h 27 | target_include_directories(pico_usb_blaster PUBLIC 28 | . 29 | ) 30 | 31 | target_link_libraries(pico_usb_blaster PUBLIC 32 | pico_stdlib 33 | pico_unique_id 34 | tinyusb_device 35 | tinyusb_board 36 | pico_platform 37 | hardware_pio 38 | ) 39 | 40 | pico_set_binary_type(pico_usb_blaster copy_to_ram) 41 | 42 | # create map/bin/hex/uf2 file in addition to ELF. 43 | pico_add_extra_outputs(pico_usb_blaster) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 thisiseth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ft245_eeprom.h: -------------------------------------------------------------------------------- 1 | // Reproduction of EEPROM from original "USB Blaster" 2 | // Derived from the code at http://sa89a.net/mp.cgi/ele/ub.htm 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define FT245_EEPROM_LENGTH (sizeof(FT245_EEPROM)/sizeof(FT245_EEPROM[0])) 9 | 10 | static uint8_t FT245_EEPROM[] = { 11 | 0x00, 0x00, 0xFB, 0x09, 0x01, 0x60, 0x00, 0x04, // 0x00 - 0x07 12 | 0x80, 0x28, 0x1C, 0x00, 0x10, 0x01, 0x94, 0x0E, // 0x08 - 0x0F 13 | 0xA2, 0x18, 0xBA, 0x12, 0x0E, 0x03, 0x41, 0x00, // 0x10 - 0x17 14 | 0x6C, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, // 0x18 - 0x1F 15 | 0x61, 0x00, 0x18, 0x03, 0x55, 0x00, 0x53, 0x00, // 0x20 - 0x27 16 | 0x42, 0x00, 0x2D, 0x00, 0x42, 0x00, 0x6C, 0x00, // 0x28 - 0x2F 17 | 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, // 0x30 - 0x37 18 | 0x72, 0x00, 0x12, 0x03, 0x30, 0x00, 0x30, 0x00, // 0x38 - 0x3F 19 | 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, // 0x40 - 0x47 20 | 0x30, 0x00, 0x30, 0x00, 0x01, 0x02, 0x03, 0x01, // 0x48 - 0x4F 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 - 0x57 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58 - 0x5F 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60 - 0x67 24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 - 0x6F 25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 - 0x77 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0xD0, // 0x78 - 0x7F 27 | }; -------------------------------------------------------------------------------- /src/ws2812.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | .pio_version 0 // only requires PIO version 0 7 | 8 | .program ws2812 9 | .side_set 1 10 | 11 | ; The following constants are selected for broad compatibility with WS2812, 12 | ; WS2812B, and SK6812 LEDs. Other constants may support higher bandwidths for 13 | ; specific LEDs, such as (7,10,8) for WS2812B LEDs. 14 | 15 | .define public T1 3 16 | .define public T2 3 17 | .define public T3 4 18 | 19 | .wrap_target 20 | bitloop: 21 | out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls 22 | jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse 23 | do_one: 24 | jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse 25 | do_zero: 26 | nop side 0 [T2 - 1] ; Or drive low, for a short pulse 27 | .wrap 28 | 29 | % c-sdk { 30 | #include "hardware/clocks.h" 31 | 32 | static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) 33 | { 34 | pio_gpio_init(pio, pin); 35 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); 36 | 37 | pio_sm_config c = ws2812_program_get_default_config(offset); 38 | sm_config_set_sideset_pins(&c, pin); 39 | sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); 40 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 41 | 42 | int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; 43 | float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); 44 | sm_config_set_clkdiv(&c, div); 45 | 46 | pio_sm_init(pio, sm, offset, &c); 47 | pio_sm_set_enabled(pio, sm, true); 48 | } 49 | %} 50 | -------------------------------------------------------------------------------- /3d_print/README.md: -------------------------------------------------------------------------------- 1 | ## WARNING 2 | I just decided to share my steps for completeness sake, not because i think this is a perfectly polished fool-proof design 3 | 4 | ## Circuit board 5 | ### Parts 6 | * RP2040-zero with headers 7 | * TXS0108E module with headers 8 | * Right angle IDC-10 male connector 9 | * 10-50k resistor (level-shifter OE pull-down) 10 | * Single/double-sided perfboard 11 | * Something to cut the perfboard with: i just score it with a knife on both sides, break and polish 12 | * Thin wires 13 | 14 | ### Assembly 15 | * **IMPORTANT!!!** I inverted the angle of the IDC-10 connector by pulling all pins, rotating the plastic housing and reinserting the pins back. 16 | So orientation cutoff on my rotated connector faces towards the perfboard. 17 | This allows using a normal straight-crimped ribbon cable and (maybe) simplifies the routing a bit, so you should either do the same or reroute the connector signals yourself. 18 | * Cut a 11 holes * 20 holes piece of a perfboard with a bit extra on short side for connector support: 19 | ![plot](/doc/connector.jpg) 20 | * Solder connector and headers to the top side. If your RP2040/TXS0108E has headers presoldered - solder the entire module, just watch for the orientation: 21 | ![plot](/doc/perfboard1.jpg) 22 | * Solder GND, VCCs, OE and OE pulldown resistor: 23 | ![plot](/doc/perfboard2.jpg) 24 | * Now a bit tricky part: all level-shifter's channels are equal and bidirectional, 25 | so which signal goes where does not matter, you just have to route them to correct RP2040 pins. 26 | Also there are 8 level-shifter channels, but only 7 data signals, so i used the remaining 1 to aid VCCB routing. 27 | My routing is below: 28 | ![plot](/doc/perfboard3.jpg) 29 | 30 | ### Firmware 31 | There is no prebuilt firmware for this setup, but it is pretty easy to build from sources. 32 | 33 | The changes required in blaster.c: 34 | * define TCK_DCLK_PIN 8 35 | * define LEVEL_SHIFTER_OE_PIN 15 36 | * undefine ACTIVE_LED_PIN 37 | * define ACTIVE_LED_WS2812_PIN 16 38 | * choose your standby and active LED colors 39 | 40 | ## Enclosure 41 | ### Parts 42 | * Assembled circuit board 43 | * Glue 44 | * Printed 'cap' part 45 | * Printed 'body' part 46 | * A piece of something as an LED light guide: i used a bit of translucent PETG filament 47 | 48 | ### Print settings 49 | * No supports 50 | * Layer height 0.2mm is fine 51 | * Print 'cap' part flat side down 52 | 53 | ### Assembly 54 | * Put the board into the 'body' part. It should sit tight on both length and width, so you may have to add spacers / adjust dimensions using F3D source 55 | * Check that the 'cap' part fits, the buttons are working etc. 56 | * Glue your light guide thing to the LED - hot glue gun is ideal for this 57 | * Pass the light guide through the 'cap' and glue the 'cap' to the 'body' part 58 | * Trim excess light guide 59 | * (optional) Add a tiny bit of glue to secure the lightguide to the cap 60 | 61 | The result should look like this: 62 | ![plot](/doc/result.jpg) 63 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /src/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 | #pragma once 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | //--------------------------------------------------------------------+ 33 | // Board Specific Configuration 34 | //--------------------------------------------------------------------+ 35 | 36 | // RHPort number used for device can be defined by board.mk, default to port 0 37 | #ifndef BOARD_TUD_RHPORT 38 | #define BOARD_TUD_RHPORT 0 39 | #endif 40 | 41 | // RHPort max operational speed can defined by board.mk 42 | #ifndef BOARD_TUD_MAX_SPEED 43 | #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED 44 | #endif 45 | 46 | //-------------------------------------------------------------------- 47 | // Common Configuration 48 | //-------------------------------------------------------------------- 49 | 50 | // defined by compiler flags for flexibility 51 | #ifndef CFG_TUSB_MCU 52 | #error CFG_TUSB_MCU must be defined 53 | #endif 54 | 55 | #ifndef CFG_TUSB_OS 56 | #define CFG_TUSB_OS OPT_OS_NONE 57 | #endif 58 | 59 | //pico buildsystem predefines CFG_TUSB_DEBUG to 0 60 | #undef CFG_TUSB_DEBUG 61 | #define CFG_TUSB_DEBUG 1 62 | 63 | #define CFG_BOARD_UART_BAUDRATE 921600 64 | 65 | // Enable Device stack 66 | #define CFG_TUD_ENABLED 1 67 | 68 | // Default is max speed that hardware controller could support with on-chip PHY 69 | #define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED 70 | 71 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 72 | * Tinyusb use follows macros to declare transferring memory so that they can be put 73 | * into those specific section. 74 | * e.g 75 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 76 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 77 | */ 78 | #ifndef CFG_TUSB_MEM_SECTION 79 | #define CFG_TUSB_MEM_SECTION 80 | #endif 81 | 82 | #ifndef CFG_TUSB_MEM_ALIGN 83 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 84 | #endif 85 | 86 | //-------------------------------------------------------------------- 87 | // DEVICE CONFIGURATION 88 | //-------------------------------------------------------------------- 89 | 90 | #ifndef CFG_TUD_ENDPOINT0_SIZE 91 | #define CFG_TUD_ENDPOINT0_SIZE 8 92 | #endif 93 | 94 | //------------- CLASS -------------// 95 | #define CFG_TUD_VENDOR 1 96 | 97 | #define CFG_TUD_VENDOR_EPSIZE 64 98 | 99 | // Vendor FIFO size of TX and RX 100 | // If zero: vendor endpoints will not be buffered 101 | #define CFG_TUD_VENDOR_RX_BUFSIZE 64 102 | #define CFG_TUD_VENDOR_TX_BUFSIZE 64 103 | 104 | #ifdef __cplusplus 105 | } 106 | #endif 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pico USB Blaster 2 | This firmware turns your Raspberry Pi Pico (or other RP2040/RP2350-based board) 3 | into an **Altera USB-blaster / Intel FPGA Download Cable**-compatible JTAG/AS/PS programmer usable with Quartus, OpenOCD, etc. 4 | 5 | The project relies only on **tinyUSB** stack, so it also should be also easily portable to any **tinyUSB**-capable device 6 | 7 | ## How-to 8 | ![plot](./doc/pic.jpg) 9 | 10 | ### Required parts 11 | * Raspberry Pi Pico or Pico 2 / another RP2040/RP2350 board 12 | * if non-3.3V operation required - adequate fast level shifter capable of [loading 1k TCK pulldown](https://www.intel.com/content/www/us/en/docs/programmable/683546/current/pull-up-and-pull-down-of-jtag-pins-during.html) 13 | * i tested TXS0108E i had, and while it works, i can't recommend it (if 1k pulldown is present), because TCK quickly drops to ~2v instead of steady 3.3v 14 | 15 | ### Flashing 16 | * Get latest release uf2 or build your own: 17 | ``` 18 | /src$ mkdir build 19 | /src$ cd build 20 | /src/build$ cmake .. 21 | /src/build$ make pico_usb_blaster 22 | ``` 23 | * Press BOOTSEL, connect USB and drop uf2 there 24 | 25 | ### Pinout 26 | Default TCK/DCLK pin is GP11 ([blaster.c](./src/blaster.c)) 27 | ``` 28 | #define TCK_DCLK_PIN 11 29 | ``` 30 | 31 | Other data pins for simplicity are mapped sequentially relative to TCK/DCLK 32 | ``` 33 | GPIO | I/O | name | JTAG | AS | PS 34 | ------+-----+-----------------+------+-----------+----------- 35 | 11 | O | TCK_DCLK | TCK | DCLK | DCLK 36 | 12 | O | TMS_nCONFIG | TMS | nCONFIG | nCONFIG 37 | 13 | O | nCE | - | nCE | - 38 | 14 | O | nCS | - | nCS | - 39 | 15 | O | TDI_ASDI | TDI | ASDI | DATA0 40 | 16 | I | TDO_CONF_DONE | TDO | CONF_DONE | CONF_DONE 41 | 17 | I | DATAOUT_nSTATUS | - | DATAOUT | nSTATUS 42 | ``` 43 | 44 | GP0 is debug UART TX, GP25 (onbooard LED) is used as an output enable / activity LED 45 | 46 | ### Drivers 47 | Follow the 'Installing the Intel FPGA Download Cable Driver' instruction from Intel website 48 | 49 | The device replicates the overall USB-Blaster USB descriptor structure and pretends to have the same VID&PID for the drivers to be compatible. 50 | The board-unique serial is provided by the **tinyUSB** bsp: 51 | ``` 52 | Bus 001 Device 003: ID 09fb:6001 Altera Blaster 53 | Device Descriptor: 54 | bLength 18 55 | bDescriptorType 1 56 | bcdUSB 1.10 57 | bDeviceClass 0 58 | bDeviceSubClass 0 59 | bDeviceProtocol 0 60 | bMaxPacketSize0 8 61 | idVendor 0x09fb Altera 62 | idProduct 0x6001 Blaster 63 | bcdDevice 4.00 64 | iManufacturer 1 Pico 65 | iProduct 2 USB Blaster 66 | iSerial 3 e6613893 67 | bNumConfigurations 1 68 | Configuration Descriptor: 69 | bLength 9 70 | bDescriptorType 2 71 | wTotalLength 0x0020 72 | bNumInterfaces 1 73 | bConfigurationValue 1 74 | iConfiguration 0 75 | bmAttributes 0x80 76 | (Bus Powered) 77 | MaxPower 150mA 78 | Interface Descriptor: 79 | bLength 9 80 | bDescriptorType 4 81 | bInterfaceNumber 0 82 | bAlternateSetting 0 83 | bNumEndpoints 2 84 | bInterfaceClass 255 Vendor Specific Class 85 | bInterfaceSubClass 255 Vendor Specific Subclass 86 | bInterfaceProtocol 255 Vendor Specific Protocol 87 | iInterface 0 88 | Endpoint Descriptor: 89 | bLength 7 90 | bDescriptorType 5 91 | bEndpointAddress 0x81 EP 1 IN 92 | bmAttributes 2 93 | Transfer Type Bulk 94 | Synch Type None 95 | Usage Type Data 96 | wMaxPacketSize 0x0040 1x 64 bytes 97 | bInterval 0 98 | Endpoint Descriptor: 99 | bLength 7 100 | bDescriptorType 5 101 | bEndpointAddress 0x02 EP 2 OUT 102 | bmAttributes 2 103 | Transfer Type Bulk 104 | Synch Type None 105 | Usage Type Data 106 | wMaxPacketSize 0x0040 1x 64 bytes 107 | bInterval 0 108 | Device Status: 0x0000 109 | (Bus Powered) 110 | ``` 111 | 112 | ### Usage 113 | Now you can program Intel/Altera JTAG/AS/PS devices with Quartus or use it as a generic JTAG-adapter (where USB-Blaster is supported) 114 | 115 | Unfortunately i can't confirm AS/PS operation, because i don't have such devices around, 116 | but looking at the protocol there should be no issues in theory 117 | 118 | ## Prettier version 119 | I found RP2040-zero board the perfect size for an assembled device and also added a TXS0108E (see 'Required parts' for concerns regarding this exact level-shifter) 120 | 121 | This setup uses onboard ws2812 rgb led (support included), pins 8-14 as data I/O and pin 15 as shifter output enable 122 | 123 | ![plot](./doc/v2.jpg) 124 | 125 | And a 3D-printable enclosure. Instructions, STLs and a Fusion 360 source file are [here](./3d_print/) 126 | 127 | ![plot](./doc/v2_enclosure.jpg) 128 | 129 | 130 | ## Credits 131 | * Protocol description, specs, FT245 EEPROM taken from 132 | [Teensy_Blaster](https://github.com/Memotech-Bill/Teensy_Blaster/), 133 | [usbd-blaster](https://github.com/sameer/usbd-blaster) and 20-year old forum posts 134 | * WS2812 PIO code taken from [pico-examples](https://github.com/raspberrypi/pico-examples) 135 | -------------------------------------------------------------------------------- /src/blaster.c: -------------------------------------------------------------------------------- 1 | #include "blaster.h" 2 | #include "pico/stdlib.h" 3 | #include "hardware/gpio.h" 4 | 5 | // change these according to your setup 6 | 7 | // if onboard LED / single external LED 8 | #define ACTIVE_LED_PIN PICO_DEFAULT_LED_PIN 9 | 10 | // for boards with WS2812 rgb 11 | //#define ACTIVE_LED_WS2812_PIN 16 12 | //#define ACTIVE_LED_WS2812_COLOR_OFF 0x00080000 //standby color, dim red 13 | //#define ACTIVE_LED_WS2812_COLOR_ON 0x08000000 //active color, dim green 14 | 15 | // if level shifter is used 16 | //#define LEVEL_SHIFTER_OE_PIN 15 17 | 18 | // pins are mapped sequentially starting from TCK_DCLK_PIN 19 | // 20 | // # I/O pin name 21 | // 11 O TCK_DCLK_PIN 22 | // 12 O TMS_nCONFIG_PIN 23 | // 13 O nCE_PIN 24 | // 14 O nCS_PIN 25 | // 15 O TDI_ASDI_PIN 26 | // 16 I TDO_CONF_DONE_PIN 27 | // 17 I DATAOUT_nSTATUS_PIN 28 | 29 | #define TCK_DCLK_PIN 11 30 | 31 | // ^^^^^^^^^^^^^ 32 | 33 | #define nCS_PIN (TCK_DCLK_PIN + 3) 34 | #define TDI_ASDI_PIN (TCK_DCLK_PIN + 4) 35 | #define TDO_CONF_DONE_PIN (TCK_DCLK_PIN + 5) 36 | #define DATAOUT_nSTATUS_PIN (TCK_DCLK_PIN + 6) 37 | 38 | #ifdef ACTIVE_LED_WS2812_PIN 39 | #include "ws2812.h" 40 | #if !defined(ACTIVE_LED_WS2812_COLOR_OFF) || !defined(ACTIVE_LED_WS2812_COLOR_ON) 41 | #error ws2812 colors not defined 42 | #endif 43 | #endif 44 | 45 | #define SHIFT_MODE_FLAG(b) (!!((b) & 0b10000000)) 46 | #define READ_FLAG(b) (!!((b) & 0b01000000)) 47 | #define OE_FLAG(b) (!!((b) & 0b00100000)) 48 | #define PAYLOAD(b) ((b) & 0b00111111) 49 | 50 | static bool initialized = false; 51 | static bool output_enabled = false; 52 | static int shift_bytes_left = 0; 53 | static bool shift_read_set; 54 | 55 | // rx/tx byte to signal relations in bitbang mode 56 | // 57 | // rx 58 | // bitmask | name | JTAG | AS | PS 59 | // --------+-----------------+------+-----------+----------- 60 | // 0x01 | TCK_DCLK | TCK | DCLK | DCLK 61 | // 0x02 | TMS_nCONFIG | TMS | nCONFIG | nCONFIG 62 | // 0x04 | nCE | - | nCE | - 63 | // 0x08 | nCS | - | nCS | - 64 | // 0x10 | TDI_ASDI | TDI | ASDI | DATA0 65 | // 0x20 | Output Enable/LED active 66 | // 67 | // tx 68 | // 0x01 | TDO_CONF_DONE | TDO | CONF_DONE | CONF_DONE 69 | // 0x02 | DATAOUT_nSTATUS | - | DATAOUT | nSTATUS 70 | 71 | static void blaster_init(void) 72 | { 73 | #ifdef ACTIVE_LED_PIN 74 | gpio_init(ACTIVE_LED_PIN); 75 | gpio_set_dir(ACTIVE_LED_PIN, true); 76 | #endif 77 | #ifdef ACTIVE_LED_WS2812_PIN 78 | ws2812_init(ACTIVE_LED_WS2812_PIN); 79 | ws2812_set(ACTIVE_LED_WS2812_COLOR_OFF); 80 | #endif 81 | 82 | gpio_init_mask(0b1111111U << TCK_DCLK_PIN); //init 7 pins as input starting from TCK 83 | 84 | #ifdef LEVEL_SHIFTER_OE_PIN 85 | gpio_init(LEVEL_SHIFTER_OE_PIN); 86 | gpio_set_dir(LEVEL_SHIFTER_OE_PIN, true); 87 | gpio_set_dir_masked(0b0011111U << TCK_DCLK_PIN, 0xFFFFFFFF); //always output if shifter is used 88 | #endif 89 | 90 | initialized = true; 91 | } 92 | 93 | // :) 94 | static inline void delay_5_cycles(void) 95 | { 96 | __asm__ volatile 97 | ( 98 | "nop\n\t" // 1 99 | "nop\n\t" // 2 100 | "nop\n\t" // 3 101 | "nop\n\t" // 4 102 | "nop" // 5 103 | ); 104 | } 105 | 106 | static inline void output_enable(bool enable) 107 | { 108 | if (output_enabled == enable) 109 | return; 110 | 111 | output_enabled = enable; 112 | 113 | #ifdef ACTIVE_LED_PIN 114 | gpio_put(ACTIVE_LED_PIN, enable); 115 | #endif 116 | #ifdef ACTIVE_LED_WS2812_PIN 117 | ws2812_set(enable ? ACTIVE_LED_WS2812_COLOR_ON : ACTIVE_LED_WS2812_COLOR_OFF); 118 | #endif 119 | 120 | #ifdef LEVEL_SHIFTER_OE_PIN 121 | gpio_put(LEVEL_SHIFTER_OE_PIN, enable); 122 | 123 | //~400ns 124 | delay_5_cycles(); 125 | delay_5_cycles(); 126 | delay_5_cycles(); 127 | delay_5_cycles(); 128 | delay_5_cycles(); 129 | delay_5_cycles(); 130 | delay_5_cycles(); 131 | delay_5_cycles(); 132 | #else 133 | gpio_set_dir_masked(0b0011111U << TCK_DCLK_PIN, enable ? 0xFFFFFFFF : 0); //lower 5 of 7 pins are output/high-z (input), remaining 2 are input only 134 | #endif 135 | } 136 | 137 | static inline uint8_t bitbang(uint8_t data) 138 | { 139 | uint8_t ret = (!!gpio_get(TDO_CONF_DONE_PIN)) | ((!!gpio_get(DATAOUT_nSTATUS_PIN)) << 1); 140 | delay_5_cycles(); 141 | gpio_put_masked(0b0011111U << TCK_DCLK_PIN, ((uint32_t)data) << TCK_DCLK_PIN); 142 | return ret; 143 | } 144 | 145 | static inline uint8_t shift(uint8_t data) 146 | { 147 | uint8_t ret = 0; 148 | bool nCS = gpio_get_out_level(nCS_PIN); 149 | 150 | for (int i = 0; i < 8; ++i) 151 | { 152 | gpio_put(TDI_ASDI_PIN, data & 0b1); 153 | 154 | ret >>= 1; 155 | 156 | if (gpio_get(nCS ? TDO_CONF_DONE_PIN : DATAOUT_nSTATUS_PIN)) 157 | ret |= 0b10000000; 158 | 159 | gpio_put(TCK_DCLK_PIN, true); 160 | delay_5_cycles(); 161 | delay_5_cycles(); 162 | gpio_put(TCK_DCLK_PIN, false); 163 | 164 | data >>= 1; 165 | } 166 | 167 | return ret; 168 | } 169 | 170 | void blaster_reset(void) 171 | { 172 | if (!initialized) 173 | blaster_init(); 174 | 175 | shift_bytes_left = 0; 176 | output_enable(false); 177 | gpio_put_masked(0b11111U << TCK_DCLK_PIN, 0); 178 | } 179 | 180 | int blaster_process(uint8_t rxBuf[], int rxCount, uint8_t txBuf[]) 181 | { 182 | int txCount = 0; 183 | 184 | for (int i = 0; i < rxCount; ++i) 185 | { 186 | uint8_t b = rxBuf[i]; 187 | 188 | if (shift_bytes_left > 0) // shift mode active 189 | { 190 | uint8_t input = shift(b); 191 | 192 | if (shift_read_set) 193 | { 194 | txBuf[txCount] = input; 195 | ++txCount; 196 | } 197 | 198 | --shift_bytes_left; 199 | } 200 | else if (SHIFT_MODE_FLAG(b)) // shift mode activated 201 | { 202 | shift_read_set = READ_FLAG(b); 203 | shift_bytes_left = PAYLOAD(b); 204 | gpio_put(TCK_DCLK_PIN, false); 205 | } 206 | else // bitbang mode 207 | { 208 | output_enable(OE_FLAG(b)); 209 | uint8_t input = bitbang(b); 210 | 211 | if (READ_FLAG(b)) 212 | { 213 | txBuf[txCount] = input; 214 | ++txCount; 215 | } 216 | } 217 | } 218 | 219 | return txCount; 220 | } 221 | -------------------------------------------------------------------------------- /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 "bsp/board_api.h" 27 | #include "tusb.h" 28 | 29 | //--------------------------------------------------------------------+ 30 | // Device Descriptors 31 | //--------------------------------------------------------------------+ 32 | tusb_desc_device_t const desc_device = 33 | { 34 | .bLength = sizeof(tusb_desc_device_t), 35 | .bDescriptorType = TUSB_DESC_DEVICE, 36 | .bcdUSB = 0x0110, // 1.10 37 | 38 | .bDeviceClass = TUSB_CLASS_UNSPECIFIED, 39 | .bDeviceSubClass = 0, 40 | .bDeviceProtocol = 0, 41 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 42 | 43 | .idVendor = 0x09fb, // Altera 44 | .idProduct = 0x6001, // Blaster 45 | .bcdDevice = 0x0400, // 4.00 46 | 47 | .iManufacturer = 0x01, 48 | .iProduct = 0x02, 49 | .iSerialNumber = 0x03, 50 | 51 | .bNumConfigurations = 0x01 52 | }; 53 | 54 | // Invoked when received GET DEVICE DESCRIPTOR 55 | // Application return pointer to descriptor 56 | uint8_t const *tud_descriptor_device_cb(void) 57 | { 58 | return (uint8_t const *)&desc_device; 59 | } 60 | 61 | //--------------------------------------------------------------------+ 62 | // Configuration Descriptor 63 | //--------------------------------------------------------------------+ 64 | enum 65 | { 66 | ITF_NUM_VENDOR, 67 | ITF_NUM_TOTAL 68 | }; 69 | 70 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_VENDOR_DESC_LEN) 71 | 72 | #define EPNUM_VENDOR_IN 1 73 | #define EPNUM_VENDOR_OUT 2 74 | 75 | // default vendor descriptor macro class subclass product is 255,0,0 when lsusb dump is 255,255,255 76 | // and EP order is different dunno if it is relevant 77 | #define TUD_BLASTER_VENDOR_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \ 78 | /* Interface */ \ 79 | 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, TUSB_CLASS_VENDOR_SPECIFIC, TUSB_CLASS_VENDOR_SPECIFIC, _stridx, \ 80 | 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0, /* Endpoint In */ \ 81 | 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 /* Endpoint Out */ 82 | 83 | uint8_t const desc_configuration[] = 84 | { 85 | // Config number, interface count, string index, total length, attribute, power in mA 86 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 150), 87 | 88 | // Interface number, string index, EP Out & IN address, EP size 89 | TUD_BLASTER_VENDOR_DESCRIPTOR(ITF_NUM_VENDOR, 0, EPNUM_VENDOR_OUT, 0x80 | EPNUM_VENDOR_IN, CFG_TUD_VENDOR_EPSIZE) 90 | }; 91 | 92 | // Invoked when received GET CONFIGURATION DESCRIPTOR 93 | // Application return pointer to descriptor 94 | // Descriptor contents must exist long enough for transfer to complete 95 | uint8_t const *tud_descriptor_configuration_cb(uint8_t index) 96 | { 97 | (void)index; // for multiple configurations 98 | return desc_configuration; 99 | } 100 | 101 | //--------------------------------------------------------------------+ 102 | // String Descriptors 103 | //--------------------------------------------------------------------+ 104 | 105 | // String Descriptor Index 106 | enum 107 | { 108 | STRID_LANGID = 0, 109 | STRID_MANUFACTURER, 110 | STRID_PRODUCT, 111 | STRID_SERIAL, 112 | }; 113 | 114 | // array of pointer to string descriptors 115 | char const *string_desc_arr[] = 116 | { 117 | (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) 118 | "Pico", // 1: Manufacturer 119 | "USB Blaster", // 2: Product 120 | NULL, // 3: Serial 121 | }; 122 | 123 | static uint16_t _desc_str[32 + 1]; 124 | 125 | // Invoked when received GET STRING DESCRIPTOR request 126 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 127 | uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) 128 | { 129 | (void)langid; 130 | size_t chr_count; 131 | 132 | switch (index) 133 | { 134 | case STRID_LANGID: 135 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 136 | chr_count = 1; 137 | break; 138 | 139 | case STRID_SERIAL: 140 | chr_count = board_usb_get_serial(_desc_str + 1, 8); 141 | 142 | for (int i = 1; i < chr_count + 1; ++i) 143 | if (_desc_str[i] >= u'A' && _desc_str[i] <= u'F') 144 | _desc_str[i] += 32; 145 | 146 | break; 147 | 148 | default: 149 | // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. 150 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors 151 | 152 | if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) 153 | return NULL; 154 | 155 | const char *str = string_desc_arr[index]; 156 | 157 | // Cap at max char 158 | chr_count = strlen(str); 159 | size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type 160 | if (chr_count > max_count) 161 | chr_count = max_count; 162 | 163 | // Convert ASCII string into UTF-16 164 | for (size_t i = 0; i < chr_count; i++) 165 | { 166 | _desc_str[1 + i] = str[i]; 167 | } 168 | break; 169 | } 170 | 171 | // first byte is length (including header), second byte is string type 172 | _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); 173 | 174 | return _desc_str; 175 | } 176 | -------------------------------------------------------------------------------- /src/main.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 27 | #include 28 | #include 29 | 30 | #include "bsp/board_api.h" 31 | #include "tusb.h" 32 | 33 | #include "hardware/clocks.h" 34 | #include "ft245_eeprom.h" 35 | #include "blaster.h" 36 | 37 | //--------------------------------------------------------------------+ 38 | // MACRO CONSTANT TYPEDEF PROTYPES 39 | //--------------------------------------------------------------------+ 40 | 41 | //#define TUD_USE_ONBOARD_LED 42 | 43 | /* Blink pattern 44 | * - 250 ms : device not mounted 45 | * - 1000 ms : device mounted 46 | * - 2500 ms : device is suspended 47 | */ 48 | enum 49 | { 50 | BLINK_NOT_MOUNTED = 250, 51 | BLINK_MOUNTED = 1000, 52 | BLINK_SUSPENDED = 2500, 53 | 54 | BLINK_ALWAYS_ON = UINT32_MAX, 55 | BLINK_ALWAYS_OFF = 0 56 | }; 57 | 58 | static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; 59 | 60 | //------------- prototypes -------------// 61 | static void led_blinking_task(void); 62 | static void vendor_task(void); 63 | 64 | /*------------- MAIN -------------*/ 65 | int main(void) 66 | { 67 | //120 mhz 68 | set_sys_clock_pll(1440000000, 6, 2); 69 | 70 | board_init(); 71 | 72 | // init device stack on configured roothub port 73 | tud_init(BOARD_TUD_RHPORT); 74 | 75 | if (board_init_after_tusb) 76 | board_init_after_tusb(); 77 | 78 | TU_LOG1("Device running\r\n"); 79 | 80 | while (1) 81 | { 82 | tud_task(); // tinyusb device task 83 | vendor_task(); 84 | led_blinking_task(); 85 | } 86 | } 87 | 88 | //--------------------------------------------------------------------+ 89 | // Device callbacks 90 | //--------------------------------------------------------------------+ 91 | 92 | // Invoked when device is mounted 93 | void tud_mount_cb(void) 94 | { 95 | blaster_reset(); 96 | blink_interval_ms = BLINK_MOUNTED; 97 | TU_LOG1("Device mounted\r\n"); 98 | } 99 | 100 | // Invoked when device is unmounted 101 | void tud_umount_cb(void) 102 | { 103 | blink_interval_ms = BLINK_NOT_MOUNTED; 104 | TU_LOG1("Device unmounted\r\n"); 105 | } 106 | 107 | // Invoked when usb bus is suspended 108 | // remote_wakeup_en : if host allow us to perform remote wakeup 109 | // Within 7ms, device must draw an average of current less than 2.5 mA from bus 110 | void tud_suspend_cb(bool remote_wakeup_en) 111 | { 112 | (void)remote_wakeup_en; 113 | blink_interval_ms = BLINK_SUSPENDED; 114 | TU_LOG1("Device suspended\r\n"); 115 | } 116 | 117 | // Invoked when usb bus is resumed 118 | void tud_resume_cb(void) 119 | { 120 | blaster_reset(); 121 | blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; 122 | TU_LOG1("Device resumed\r\n"); 123 | } 124 | 125 | void tud_reset_cb(void) 126 | { 127 | blaster_reset(); 128 | TU_LOG1("Device reset\r\n"); 129 | } 130 | 131 | //--------------------------------------------------------------------+ 132 | // Vendor control transfers 133 | //--------------------------------------------------------------------+ 134 | 135 | static bool handle_vendor_in_request(uint8_t rhport, tusb_control_request_t const *request) 136 | { 137 | uint8_t bRequest = request->bRequest; 138 | uint16_t wIndex = request->wIndex; 139 | uint16_t wLength = request->wLength; 140 | 141 | uint8_t response[2] = {0}; 142 | 143 | if (bRequest == 0x90) 144 | { 145 | uint16_t address = wIndex * 2; 146 | 147 | if ((address + 1) < FT245_EEPROM_LENGTH) 148 | { 149 | response[0] = FT245_EEPROM[address]; 150 | response[1] = FT245_EEPROM[address + 1]; 151 | } 152 | 153 | TU_LOG1("Vendor IN eeprom request wIndex: %d\r\n", wIndex); 154 | } 155 | else 156 | { 157 | response[0] = 0x36; 158 | response[1] = 0x83; 159 | 160 | TU_LOG1("Vendor IN unknown request bRequest: %d\r\n", bRequest); 161 | } 162 | 163 | uint16_t resp_length = (wLength < 2) ? wLength : 2; 164 | 165 | tud_control_xfer(rhport, request, response, resp_length); 166 | 167 | return true; 168 | } 169 | 170 | static bool handle_vendor_out_request(uint8_t rhport, tusb_control_request_t const *request) 171 | { 172 | if (request->wLength > 0) 173 | tud_control_xfer(rhport, request, NULL, 0); 174 | else 175 | tud_control_status(rhport, request); 176 | 177 | TU_LOG1("Vendor OUT request\r\n"); 178 | 179 | return true; 180 | } 181 | 182 | bool tud_control_request_cb(uint8_t rhport, tusb_control_request_t const *request) 183 | { 184 | if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) 185 | { 186 | if (request->bmRequestType_bit.direction == TUSB_DIR_IN) 187 | // Vendor IN request (Device to Host) 188 | return handle_vendor_in_request(rhport, request); 189 | 190 | // Vendor OUT request (Host to Device) 191 | return handle_vendor_out_request(rhport, request); 192 | } 193 | 194 | return false; 195 | } 196 | 197 | //--------------------------------------------------------------------+ 198 | // Vendor class 199 | //--------------------------------------------------------------------+ 200 | 201 | static uint32_t prev_tx_ms = 0; 202 | static uint8_t tx_buf[2 + 64*2] = { 0x31, 0x60 }; 203 | static int tx_ready = 0; 204 | 205 | static void vendor_task(void) 206 | { 207 | if (!tud_mounted()) 208 | { 209 | tx_ready = 0; 210 | return; 211 | } 212 | 213 | //each 64 bytes received can theoretically produce 64 payload bytes sent 214 | if (tud_vendor_available() && tx_ready <= 64) 215 | { 216 | uint8_t buf[64]; 217 | int count = tud_vendor_read(buf, sizeof(buf)); 218 | 219 | tx_ready += blaster_process(buf, count, tx_buf + 2 + tx_ready); 220 | } 221 | 222 | uint32_t now = board_millis(); 223 | 224 | if ((tx_ready + 2) >= 64 || (now - prev_tx_ms) >= 10) 225 | { 226 | int txCount = tx_ready > 62 ? 62 : tx_ready; 227 | 228 | //other option is to disable fifo completely i.e. don't send anything until it is completely empty 229 | if (tud_vendor_write_available() < (txCount + 2)) 230 | return; 231 | 232 | tud_vendor_write(tx_buf, txCount + 2); 233 | tud_vendor_write_flush(); 234 | 235 | prev_tx_ms = now; 236 | tx_ready -= txCount; 237 | 238 | if (tx_ready > 0) 239 | memcpy(tx_buf + 2, tx_buf + 2 + txCount, tx_ready); 240 | } 241 | } 242 | 243 | //--------------------------------------------------------------------+ 244 | // BLINKING TASK 245 | //--------------------------------------------------------------------+ 246 | 247 | #ifdef TUD_USE_ONBOARD_LED 248 | 249 | static void led_blinking_task(void) 250 | { 251 | static uint32_t start_ms = 0; 252 | static bool led_state = false; 253 | 254 | // Blink every interval ms 255 | if (board_millis() - start_ms < blink_interval_ms) 256 | return; // not enough time 257 | 258 | start_ms += blink_interval_ms; 259 | 260 | board_led_write(led_state); 261 | led_state = 1 - led_state; // toggle 262 | } 263 | 264 | #else 265 | static inline void led_blinking_task() {} 266 | #endif --------------------------------------------------------------------------------