Initial Release
This commit is contained in:
commit
f67a57d2ba
|
@ -0,0 +1,24 @@
|
||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(picoprobe)
|
||||||
|
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_executable(picoprobe
|
||||||
|
src/main.c
|
||||||
|
src/usb_descriptors.c
|
||||||
|
src/probe.c
|
||||||
|
src/cdc_uart.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set(DBG_PIN_COUNT=4)
|
||||||
|
|
||||||
|
pico_generate_pio_header(picoprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe.pio)
|
||||||
|
|
||||||
|
target_include_directories(picoprobe PRIVATE src)
|
||||||
|
|
||||||
|
target_link_libraries(picoprobe PRIVATE pico_stdlib tinyusb_device tinyusb_board hardware_pio)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(picoprobe)
|
|
@ -0,0 +1,64 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
# todo document
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading PICO SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include <pico/stdlib.h>
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
#include "picoprobe_config.h"
|
||||||
|
|
||||||
|
void cdc_uart_init(void) {
|
||||||
|
gpio_set_function(PICOPROBE_UART_TX, GPIO_FUNC_UART);
|
||||||
|
gpio_set_function(PICOPROBE_UART_RX, GPIO_FUNC_UART);
|
||||||
|
uart_init(uart1, 115200);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_UART_PKT 64
|
||||||
|
void cdc_task(void) {
|
||||||
|
uint8_t rx_buf[MAX_UART_PKT];
|
||||||
|
uint8_t tx_buf[MAX_UART_PKT];
|
||||||
|
|
||||||
|
// Consume uart fifo regardless even if not connected
|
||||||
|
uint rx_len = 0;
|
||||||
|
while(uart_is_readable(uart1) && (rx_len < MAX_UART_PKT)) {
|
||||||
|
rx_buf[rx_len++] = uart_getc(uart1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tud_cdc_connected()) {
|
||||||
|
// Do we have anything to display on the host's terminal?
|
||||||
|
if (rx_len) {
|
||||||
|
for (uint i = 0; i < rx_len; i++) {
|
||||||
|
tud_cdc_write_char(rx_buf[i]);
|
||||||
|
}
|
||||||
|
tud_cdc_write_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tud_cdc_available()) {
|
||||||
|
// Is there any data from the host for us to tx
|
||||||
|
uint tx_len = tud_cdc_read(tx_buf, sizeof(tx_buf));
|
||||||
|
uart_write_blocking(uart1, tx_buf, tx_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) {
|
||||||
|
picoprobe_info("New baud rate %d\n", line_coding->bit_rate);
|
||||||
|
uart_init(uart1, line_coding->bit_rate);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef CDC_UART_H
|
||||||
|
#define CDC_UART_H
|
||||||
|
|
||||||
|
void cdc_uart_init(void);
|
||||||
|
void cdc_task(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bsp/board.h"
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
#include "picoprobe_config.h"
|
||||||
|
#include "probe.h"
|
||||||
|
#include "cdc_uart.h"
|
||||||
|
|
||||||
|
// UART0 for Picoprobe debug
|
||||||
|
// UART1 for picoprobe to target device
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
|
||||||
|
board_init();
|
||||||
|
cdc_uart_init();
|
||||||
|
tusb_init();
|
||||||
|
probe_init();
|
||||||
|
|
||||||
|
picoprobe_info("Welcome to Picoprobe!\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
tud_task(); // tinyusb device task
|
||||||
|
cdc_task();
|
||||||
|
probe_task();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef PICOPROBE_H_
|
||||||
|
#define PICOPROBE_H_
|
||||||
|
|
||||||
|
#if true
|
||||||
|
#define picoprobe_info(format,args...) printf(format, ## args)
|
||||||
|
#else
|
||||||
|
#define picoprobe_info(format,...) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if false
|
||||||
|
#define picoprobe_debug(format,args...) printf(format, ## args)
|
||||||
|
#else
|
||||||
|
#define picoprobe_debug(format,...) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if false
|
||||||
|
#define picoprobe_dump(format,args...) printf(format, ## args)
|
||||||
|
#else
|
||||||
|
#define picoprobe_dump(format,...) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// PIO config
|
||||||
|
#define PROBE_SM 0
|
||||||
|
#define PROBE_PIN_OFFSET 2
|
||||||
|
#define PROBE_PIN_SWCLK PROBE_PIN_OFFSET + 0 // 2
|
||||||
|
#define PROBE_PIN_SWDIO PROBE_PIN_OFFSET + 1 // 3
|
||||||
|
|
||||||
|
// UART config
|
||||||
|
#define PICOPROBE_UART_TX 4
|
||||||
|
#define PICOPROBE_UART_RX 5
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,263 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pico/stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <hardware/clocks.h>
|
||||||
|
#include <hardware/gpio.h>
|
||||||
|
|
||||||
|
#include "picoprobe_config.h"
|
||||||
|
#include "probe.pio.h"
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
#define DIV_ROUND_UP(m, n) (((m) + (n) - 1) / (n))
|
||||||
|
|
||||||
|
// Only want to set / clear one gpio per event so go up in powers of 2
|
||||||
|
enum _dbg_pins {
|
||||||
|
DBG_PIN_WRITE = 1,
|
||||||
|
DBG_PIN_WRITE_WAIT = 2,
|
||||||
|
DBG_PIN_READ = 4,
|
||||||
|
DBG_PIN_PKT = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
CU_REGISTER_DEBUG_PINS(probe_timing)
|
||||||
|
|
||||||
|
// Uncomment to enable debug
|
||||||
|
//CU_SELECT_DEBUG_PINS(probe_timing)
|
||||||
|
|
||||||
|
#define PROBE_BUF_SIZE 8192
|
||||||
|
struct _probe {
|
||||||
|
// Total length
|
||||||
|
uint tx_len;
|
||||||
|
// Data back to host
|
||||||
|
uint8_t tx_buf[PROBE_BUF_SIZE];
|
||||||
|
|
||||||
|
// CMD / Data RX'd from
|
||||||
|
uint rx_len;
|
||||||
|
uint8_t rx_buf[PROBE_BUF_SIZE];
|
||||||
|
|
||||||
|
// PIO offset
|
||||||
|
uint offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct _probe probe;
|
||||||
|
|
||||||
|
enum PROBE_CMDS {
|
||||||
|
PROBE_INVALID = 0, // Invalid command
|
||||||
|
PROBE_WRITE_BITS = 1, // Host wants us to write bits
|
||||||
|
PROBE_READ_BITS = 2, // Host wants us to read bits
|
||||||
|
PROBE_SET_FREQ = 3, // Set TCK
|
||||||
|
PROBE_RESET = 4, // Reset all state
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) probe_cmd_hdr {
|
||||||
|
uint8_t id;
|
||||||
|
uint8_t cmd;
|
||||||
|
uint32_t bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) probe_pkt_hdr {
|
||||||
|
uint32_t total_packet_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
void probe_set_swclk_freq(uint freq_khz) {
|
||||||
|
picoprobe_info("Set swclk freq %dKHz\n", freq_khz);
|
||||||
|
uint clk_sys_freq_khz = clock_get_hz(clk_sys) / 1000;
|
||||||
|
// Worked out with saleae
|
||||||
|
uint32_t divider = clk_sys_freq_khz / freq_khz / 2;
|
||||||
|
pio_sm_set_clkdiv_int_frac(pio0, PROBE_SM, divider, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void probe_write_bits(uint bit_count, uint8_t data_byte) {
|
||||||
|
DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE);
|
||||||
|
pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1);
|
||||||
|
pio_sm_put_blocking(pio0, PROBE_SM, data_byte);
|
||||||
|
DEBUG_PINS_SET(probe_timing, DBG_PIN_WRITE_WAIT);
|
||||||
|
picoprobe_dump("Write %d bits 0x%x\n", bit_count, data_byte);
|
||||||
|
// Wait for pio to push garbage to rx fifo so we know it has finished sending
|
||||||
|
pio_sm_get_blocking(pio0, PROBE_SM);
|
||||||
|
DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE_WAIT);
|
||||||
|
DEBUG_PINS_CLR(probe_timing, DBG_PIN_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t probe_read_bits(uint bit_count) {
|
||||||
|
DEBUG_PINS_SET(probe_timing, DBG_PIN_READ);
|
||||||
|
pio_sm_put_blocking(pio0, PROBE_SM, bit_count - 1);
|
||||||
|
uint32_t data = pio_sm_get_blocking(pio0, PROBE_SM);
|
||||||
|
uint8_t data_shifted = data >> 24;
|
||||||
|
|
||||||
|
if (bit_count < 8) {
|
||||||
|
data_shifted = data_shifted >> 8-bit_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
picoprobe_dump("Read %d bits 0x%x (shifted 0x%x)\n", bit_count, data, data_shifted);
|
||||||
|
DEBUG_PINS_CLR(probe_timing, DBG_PIN_READ);
|
||||||
|
return data_shifted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void probe_read_mode(void) {
|
||||||
|
pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_in_posedge));
|
||||||
|
while(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void probe_write_mode(void) {
|
||||||
|
pio_sm_exec(pio0, PROBE_SM, pio_encode_jmp(probe.offset + probe_offset_out_negedge));
|
||||||
|
while(!(pio0->dbg_padoe & (1 << PROBE_PIN_SWDIO)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void probe_init() {
|
||||||
|
// Funcsel pins
|
||||||
|
pio_gpio_init(pio0, PROBE_PIN_SWCLK);
|
||||||
|
pio_gpio_init(pio0, PROBE_PIN_SWDIO);
|
||||||
|
// Make sure SWDIO has a pullup on it. Idle state is high
|
||||||
|
gpio_pull_up(PROBE_PIN_SWDIO);
|
||||||
|
|
||||||
|
uint offset = pio_add_program(pio0, &probe_program);
|
||||||
|
probe.offset = offset;
|
||||||
|
|
||||||
|
pio_sm_config sm_config = probe_program_get_default_config(offset);
|
||||||
|
|
||||||
|
// Set SWCLK as a sideset pin
|
||||||
|
sm_config_set_sideset_pins(&sm_config, PROBE_PIN_SWCLK);
|
||||||
|
|
||||||
|
// Set SWDIO offset
|
||||||
|
sm_config_set_out_pins(&sm_config, PROBE_PIN_SWDIO, 1);
|
||||||
|
sm_config_set_set_pins(&sm_config, PROBE_PIN_SWDIO, 1);
|
||||||
|
sm_config_set_in_pins(&sm_config, PROBE_PIN_SWDIO);
|
||||||
|
|
||||||
|
// Set SWD and SWDIO pins as output to start. This will be set in the sm
|
||||||
|
pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 2, true);
|
||||||
|
|
||||||
|
// shift output right, autopull off, autopull threshold
|
||||||
|
sm_config_set_out_shift(&sm_config, true, false, 0);
|
||||||
|
// shift input right as swd data is lsb first, autopush off
|
||||||
|
sm_config_set_in_shift(&sm_config, true, false, 0);
|
||||||
|
|
||||||
|
// Init SM with config
|
||||||
|
pio_sm_init(pio0, PROBE_SM, offset, &sm_config);
|
||||||
|
|
||||||
|
// Set up divisor
|
||||||
|
probe_set_swclk_freq(1000);
|
||||||
|
|
||||||
|
// Enable SM
|
||||||
|
pio_sm_set_enabled(pio0, PROBE_SM, 1);
|
||||||
|
|
||||||
|
// Jump to write program
|
||||||
|
probe_write_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void probe_handle_read(uint total_bits) {
|
||||||
|
picoprobe_debug("Read %d bits\n", total_bits);
|
||||||
|
probe_read_mode();
|
||||||
|
|
||||||
|
uint chunk;
|
||||||
|
uint bits = total_bits;
|
||||||
|
while (bits > 0) {
|
||||||
|
if (bits > 8) {
|
||||||
|
chunk = 8;
|
||||||
|
} else {
|
||||||
|
chunk = bits;
|
||||||
|
}
|
||||||
|
probe.tx_buf[probe.tx_len] = probe_read_bits(chunk);
|
||||||
|
probe.tx_len++;
|
||||||
|
// Decrement remaining bits
|
||||||
|
bits -= chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void probe_handle_write(uint8_t *data, uint total_bits) {
|
||||||
|
picoprobe_debug("Write %d bits\n", total_bits);
|
||||||
|
probe_write_mode();
|
||||||
|
|
||||||
|
uint chunk;
|
||||||
|
uint bits = total_bits;
|
||||||
|
while (bits > 0) {
|
||||||
|
if (bits > 8) {
|
||||||
|
chunk = 8;
|
||||||
|
} else {
|
||||||
|
chunk = bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
probe_write_bits(chunk, *data++);
|
||||||
|
bits -= chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void probe_prepare_read_header(struct probe_cmd_hdr *hdr) {
|
||||||
|
// We have a read so need to prefix the data with the cmd header
|
||||||
|
if (probe.tx_len == 0) {
|
||||||
|
// Reserve some space for probe_pkt_hdr
|
||||||
|
probe.tx_len += sizeof(struct probe_pkt_hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy((void*)&probe.tx_buf[probe.tx_len], hdr, sizeof(struct probe_cmd_hdr));
|
||||||
|
probe.tx_len += sizeof(struct probe_cmd_hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void probe_handle_pkt(void) {
|
||||||
|
uint8_t *pkt = &probe.rx_buf[0] + sizeof(struct probe_pkt_hdr);
|
||||||
|
uint remaining = probe.rx_len - sizeof(struct probe_pkt_hdr);
|
||||||
|
|
||||||
|
DEBUG_PINS_SET(probe_timing, DBG_PIN_PKT);
|
||||||
|
|
||||||
|
picoprobe_debug("Processing packet of length %d\n", probe.rx_len);
|
||||||
|
|
||||||
|
probe.tx_len = 0;
|
||||||
|
while (remaining) {
|
||||||
|
struct probe_cmd_hdr *hdr = (struct probe_cmd_hdr*)pkt;
|
||||||
|
uint data_bytes = DIV_ROUND_UP(hdr->bits, 8);
|
||||||
|
pkt += sizeof(struct probe_cmd_hdr);
|
||||||
|
remaining -= sizeof(struct probe_cmd_hdr);
|
||||||
|
|
||||||
|
if (hdr->cmd == PROBE_WRITE_BITS) {
|
||||||
|
uint8_t *data = pkt;
|
||||||
|
probe_handle_write(data, hdr->bits);
|
||||||
|
pkt += data_bytes;
|
||||||
|
remaining -= data_bytes;
|
||||||
|
} else if (hdr->cmd == PROBE_READ_BITS) {
|
||||||
|
probe_prepare_read_header(hdr);
|
||||||
|
probe_handle_read(hdr->bits);
|
||||||
|
} else if (hdr->cmd == PROBE_SET_FREQ) {
|
||||||
|
probe_set_swclk_freq(hdr->bits);
|
||||||
|
} else if (hdr->cmd == PROBE_RESET) {
|
||||||
|
// TODO: Is there anything to do after a reset?
|
||||||
|
// tx len and rx len should already be 0
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
probe.rx_len = 0;
|
||||||
|
|
||||||
|
if (probe.tx_len) {
|
||||||
|
// Fill in total packet length before sending
|
||||||
|
struct probe_pkt_hdr *tx_hdr = (struct probe_pkt_hdr*)&probe.tx_buf[0];
|
||||||
|
tx_hdr->total_packet_length = probe.tx_len;
|
||||||
|
tud_vendor_write(&probe.tx_buf[0], probe.tx_len);
|
||||||
|
picoprobe_debug("Picoprobe wrote %d response bytes\n", probe.tx_len);
|
||||||
|
}
|
||||||
|
probe.tx_len = 0;
|
||||||
|
|
||||||
|
DEBUG_PINS_CLR(probe_timing, DBG_PIN_PKT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// USB bits
|
||||||
|
void probe_task(void) {
|
||||||
|
if ( tud_vendor_available() ) {
|
||||||
|
uint count = tud_vendor_read(&probe.rx_buf[probe.rx_len], 64);
|
||||||
|
if (count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
probe.rx_len += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probe.rx_len >= sizeof(struct probe_pkt_hdr)) {
|
||||||
|
struct probe_pkt_hdr *pkt_hdr = (struct probe_pkt_hdr*)&probe.rx_buf[0];
|
||||||
|
if (pkt_hdr->total_packet_length == probe.rx_len) {
|
||||||
|
probe_handle_pkt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef PROBE_H_
|
||||||
|
#define PROBE_H_
|
||||||
|
|
||||||
|
void probe_task(void);
|
||||||
|
void probe_init(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,24 @@
|
||||||
|
.program probe
|
||||||
|
.side_set 1 opt
|
||||||
|
|
||||||
|
public out_negedge:
|
||||||
|
set pindirs, 1 side 0x0 ; Init OE clock 0
|
||||||
|
pull ; Pull number of bits to shift -1 from tx fifo and put into output shift register
|
||||||
|
mov x, osr ; mov bits to shift -1 from output shift register into x
|
||||||
|
pull ; Pull data to shift out
|
||||||
|
out_negedge_bitloop:
|
||||||
|
out pins, 1 side 0x0 ; clock data out on falling edge
|
||||||
|
jmp x-- out_negedge_bitloop side 0x1 ; data is present for posedge
|
||||||
|
set pins, 0 side 0x0 ; Drive data low
|
||||||
|
push ; Push to rx fifo just so processor knows when done
|
||||||
|
jmp out_negedge ; Wait for next transaction
|
||||||
|
|
||||||
|
public in_posedge:
|
||||||
|
set pindirs, 0 side 0x0 ; INIT IE clock 0
|
||||||
|
pull ; Pull number of bits to shift -1 from tx fifo and put into output shift register
|
||||||
|
mov x, osr ; mov bits to shift -1 from output shift register into x into x
|
||||||
|
in_posedge_bitloop:
|
||||||
|
in pins, 1 side 0x1 ; Generate posedge and read data
|
||||||
|
jmp x-- in_posedge_bitloop side 0x0 ;
|
||||||
|
push ; Push to rx fifo when done
|
||||||
|
jmp in_posedge ; Jump back to start
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_CONFIG_H_
|
||||||
|
#define _TUSB_CONFIG_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// COMMON CONFIGURATION
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
// defined by compiler flags for flexibility
|
||||||
|
#ifndef CFG_TUSB_MCU
|
||||||
|
#error CFG_TUSB_MCU must be defined
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
|
||||||
|
|
||||||
|
#ifndef CFG_TUSB_OS
|
||||||
|
#define CFG_TUSB_OS OPT_OS_PICO
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUSB_MEM_SECTION
|
||||||
|
#define CFG_TUSB_MEM_SECTION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUSB_MEM_ALIGN
|
||||||
|
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// DEVICE CONFIGURATION
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||||
|
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------- CLASS -------------//
|
||||||
|
#define CFG_TUD_HID 0
|
||||||
|
#define CFG_TUD_CDC 1
|
||||||
|
#define CFG_TUD_MSC 0
|
||||||
|
#define CFG_TUD_MIDI 0
|
||||||
|
#define CFG_TUD_VENDOR 1
|
||||||
|
|
||||||
|
#define CFG_TUD_CDC_RX_BUFSIZE 64
|
||||||
|
#define CFG_TUD_CDC_TX_BUFSIZE 64
|
||||||
|
|
||||||
|
#define CFG_TUD_VENDOR_RX_BUFSIZE 8192
|
||||||
|
#define CFG_TUD_VENDOR_TX_BUFSIZE 8192
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_CONFIG_H_ */
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Device Descriptors
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
tusb_desc_device_t const desc_device =
|
||||||
|
{
|
||||||
|
.bLength = sizeof(tusb_desc_device_t),
|
||||||
|
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||||
|
.bcdUSB = 0x0110, // // USB Specification version 1.1
|
||||||
|
.bDeviceClass = 0x00, // Each interface specifies its own
|
||||||
|
.bDeviceSubClass = 0x00, // Each interface specifies its own
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||||
|
|
||||||
|
.idVendor = 0x2E8A, // Pi
|
||||||
|
.idProduct = 0x0004, // Picoprobe
|
||||||
|
.bcdDevice = 0x0100, // Version 01.00
|
||||||
|
.iManufacturer = 0x01,
|
||||||
|
.iProduct = 0x02,
|
||||||
|
.iSerialNumber = 0x03,
|
||||||
|
.bNumConfigurations = 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invoked when received GET DEVICE DESCRIPTOR
|
||||||
|
// Application return pointer to descriptor
|
||||||
|
uint8_t const * tud_descriptor_device_cb(void)
|
||||||
|
{
|
||||||
|
return (uint8_t const *) &desc_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Configuration Descriptor
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ITF_NUM_CDC_COM,
|
||||||
|
ITF_NUM_CDC_DATA,
|
||||||
|
ITF_NUM_PROBE,
|
||||||
|
ITF_NUM_TOTAL
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CDC_NOTIFICATION_EP_NUM 0x81
|
||||||
|
#define CDC_DATA_OUT_EP_NUM 0x02
|
||||||
|
#define CDC_DATA_IN_EP_NUM 0x83
|
||||||
|
#define PROBE_OUT_EP_NUM 0x04
|
||||||
|
#define PROBE_IN_EP_NUM 0x85
|
||||||
|
|
||||||
|
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_VENDOR_DESC_LEN)
|
||||||
|
|
||||||
|
uint8_t const desc_configuration[] =
|
||||||
|
{
|
||||||
|
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||||
|
|
||||||
|
// Interface 0 + 1
|
||||||
|
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 0, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64),
|
||||||
|
|
||||||
|
// Interface 2
|
||||||
|
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||||
|
// Application return pointer to descriptor
|
||||||
|
// Descriptor contents must exist long enough for transfer to complete
|
||||||
|
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||||
|
{
|
||||||
|
(void) index; // for multiple configurations
|
||||||
|
return desc_configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// String Descriptors
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// array of pointer to string descriptors
|
||||||
|
char const* string_desc_arr [] =
|
||||||
|
{
|
||||||
|
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||||
|
"Raspberry Pi", // 1: Manufacturer
|
||||||
|
"Picoprobe", // 2: Product
|
||||||
|
"123456", // 3: Serials, should use chip ID
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint16_t _desc_str[32];
|
||||||
|
|
||||||
|
// Invoked when received GET STRING DESCRIPTOR request
|
||||||
|
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||||
|
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||||
|
{
|
||||||
|
(void) langid;
|
||||||
|
|
||||||
|
uint8_t chr_count;
|
||||||
|
|
||||||
|
if ( index == 0)
|
||||||
|
{
|
||||||
|
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||||
|
chr_count = 1;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Convert ASCII string into UTF-16
|
||||||
|
|
||||||
|
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
|
||||||
|
|
||||||
|
const char* str = string_desc_arr[index];
|
||||||
|
|
||||||
|
// Cap at max char
|
||||||
|
chr_count = strlen(str);
|
||||||
|
if ( chr_count > 31 ) chr_count = 31;
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<chr_count; i++)
|
||||||
|
{
|
||||||
|
_desc_str[1+i] = str[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first byte is length (including header), second byte is string type
|
||||||
|
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
|
||||||
|
|
||||||
|
return _desc_str;
|
||||||
|
}
|
Loading…
Reference in New Issue