Compare commits
3 Commits
remote_gpi
...
cmsis_subm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2eeef92b89 | ||
|
|
8a0c487df3 | ||
|
|
2a421d1648 |
@@ -22,8 +22,6 @@ add_executable(debugprobe
|
||||
src/get_serial.c
|
||||
src/sw_dp_pio.c
|
||||
src/tusb_edpt_handler.c
|
||||
src/autobaud.c
|
||||
src/remote_gpio.c
|
||||
)
|
||||
|
||||
target_sources(debugprobe PRIVATE
|
||||
@@ -44,22 +42,9 @@ target_compile_options(debugprobe PRIVATE -Wall)
|
||||
|
||||
pico_generate_pio_header(debugprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe.pio)
|
||||
pico_generate_pio_header(debugprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe_oen.pio)
|
||||
pico_generate_pio_header(debugprobe ${CMAKE_CURRENT_LIST_DIR}/src/autobaud.pio)
|
||||
|
||||
target_include_directories(debugprobe PRIVATE src)
|
||||
|
||||
# add version
|
||||
add_custom_target(version
|
||||
${CMAKE_SOURCE_DIR}/get-version.sh
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_include_directories(debugprobe PRIVATE
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
)
|
||||
|
||||
add_dependencies(debugprobe version)
|
||||
|
||||
target_compile_definitions (debugprobe PRIVATE
|
||||
PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
|
||||
)
|
||||
@@ -90,9 +75,6 @@ target_link_libraries(debugprobe PRIVATE
|
||||
tinyusb_device
|
||||
tinyusb_board
|
||||
hardware_pio
|
||||
hardware_dma
|
||||
hardware_irq
|
||||
hardware_clocks
|
||||
FreeRTOS-Kernel
|
||||
FreeRTOS-Kernel-Heap1
|
||||
)
|
||||
|
||||
33
README.md
33
README.md
@@ -1,12 +1,11 @@
|
||||
# Debugprobe
|
||||
|
||||
Firmware source for the Raspberry Pi Debug Probe SWD/UART accessory. Can also be run on a Raspberry Pi Pico or Pico 2.
|
||||
Firmware source for the Raspberry Pi Debug Probe SWD/UART accessory. Can also be run on a Raspberry Pi Pico.
|
||||
|
||||
[Raspberry Pi Debug Probe product page](https://www.raspberrypi.com/products/debug-probe/)
|
||||
|
||||
[Raspberry Pi Pico product page](https://www.raspberrypi.com/products/raspberry-pi-pico/)
|
||||
|
||||
[Raspberry Pi Pico 2 product page](https://www.raspberrypi.com/products/raspberry-pi-pico-2/)
|
||||
|
||||
# Documentation
|
||||
|
||||
@@ -30,7 +29,7 @@ Then create and switch to the build directory:
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
If your environment doesn't contain `PICO_SDK_PATH`, then either add it to your environment variables with `export PICO_SDK_PATH=/path/to/sdk` or add `-DPICO_SDK_PATH=/path/to/sdk` to the arguments to CMake below.
|
||||
If your environment doesn't contain `PICO_SDK_PATH`, then either add it to your environment variables with `export PICO_SDK_PATH=/path/to/sdk` or add `PICO_SDK_PATH=/path/to/sdk` to the arguments to CMake below.
|
||||
|
||||
Run cmake and build the code:
|
||||
```
|
||||
@@ -39,41 +38,31 @@ Run cmake and build the code:
|
||||
```
|
||||
Done! You should now have a `debugprobe.uf2` that you can upload to your Debug Probe via the UF2 bootloader.
|
||||
|
||||
## Building for the Pico 1
|
||||
|
||||
If you want to create the version that runs on the Pico, then you need to invoke `cmake` in the sequence above with the `DEBUG_ON_PICO=ON` option:
|
||||
```
|
||||
cmake -DDEBUG_ON_PICO=ON ..
|
||||
```
|
||||
|
||||
This will build with the configuration for the Pico and call the output program `debugprobe_on_pico.uf2`, as opposed to `debugprobe.uf2` for the accessory hardware.
|
||||
|
||||
Note that if you first ran through the whole sequence to compile for the Debug Probe, then you don't need to start back at the top. You can just go back to the `cmake` step and start from there.
|
||||
|
||||
## Building for the Pico 2
|
||||
# Building for the Pico 2
|
||||
|
||||
If using an existing debugprobe clone:
|
||||
- You must completely regenerate your build directory, or use a different one.
|
||||
- You must also sync and update submodules.
|
||||
- You must also sync and update submodules as rp2350 needs a downstream FreeRTOS port for now.
|
||||
- `PICO_SDK_PATH` must point to a version 2.0.0 or greater install.
|
||||
|
||||
|
||||
```
|
||||
git submodule sync
|
||||
git submodule update --init --recursive
|
||||
git submodule update --init
|
||||
mkdir build-pico2
|
||||
cd build-pico2
|
||||
cmake -DDEBUG_ON_PICO=1 -DPICO_BOARD=pico2 ../
|
||||
cmake -DDEBUG_ON_PICO=1 -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 ../
|
||||
```
|
||||
This will build with the configuration for the Pico 2 and call the output program `debugprobe_on_pico2.uf2`.
|
||||
|
||||
# AutoBaud
|
||||
|
||||
Mode which automatically detects and sets the UART baud rate as data arrives.
|
||||
|
||||
To enable AutoBaud, configure the USB CDC port to the following custom baud rate:
|
||||
```
|
||||
9728 (0x2600)
|
||||
```
|
||||
> **Note:** Some Linux serial tools cannot set custom baud values. PuTTY on Windows and any terminal that supports arbitrary baud rates works.
|
||||
|
||||
Changing the baud rate to any other value disables AutoBaud.
|
||||
|
||||
# TODO
|
||||
- AutoBaud selection, as PIO is a capable frequency counter
|
||||
- Possibly include RTT support
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/sh
|
||||
if command -v git &>/dev/null
|
||||
then
|
||||
VERSION=$(git describe --exact-match --tags 2> /dev/null || git rev-parse --short HEAD)
|
||||
else
|
||||
VERSION="unknown"
|
||||
fi
|
||||
|
||||
if [ ! -e generated/probe ]
|
||||
then
|
||||
mkdir -p generated/probe
|
||||
fi
|
||||
|
||||
cat > generated/probe/version.h << EOF
|
||||
#ifndef _PROBE_VERSION_H
|
||||
#define _PROBE_VERSION_H
|
||||
|
||||
#define PROBE_VERSION "${VERSION}"
|
||||
|
||||
#endif
|
||||
EOF
|
||||
@@ -1,8 +0,0 @@
|
||||
TODO - add CMake package to build probe_gpio_api applet
|
||||
|
||||
In the meanwhile, this oneliner builds on the host platform:
|
||||
|
||||
gcc -static -Os probe_gpio_api.c -lusb-1.0 -ludev
|
||||
|
||||
requires libusb-1.0-dev and libudev-dev
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
#define PROBE_PIN_SWCLK (PROBE_PIN_OFFSET + 0) // 2
|
||||
#define PROBE_PIN_SWDIO (PROBE_PIN_OFFSET + 1) // 3
|
||||
// Target reset config
|
||||
#if false
|
||||
#define PROBE_PIN_RESET 1
|
||||
#endif
|
||||
|
||||
// UART config
|
||||
#define PROBE_UART_TX 4
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#include "remote_gpio.h"
|
||||
|
||||
|
||||
#define VENDOR_ID 0x2E8A // Raspberry Pi
|
||||
#define PRODUCT_ID 0x000c // Debugprobe
|
||||
#define BCDD_VER 0x0103
|
||||
#define NR_GPIOS 28 // Limit to RP2040/2350A for now
|
||||
#define GPIO_ALL 0xff
|
||||
|
||||
#define GET_STATE BMREQUEST_GPIO_GET
|
||||
#define SET_STATE BMREQUEST_GPIO_SET
|
||||
|
||||
#define GPIO_OUT 1
|
||||
#define GPIO_IN 0
|
||||
|
||||
static const char usage[] = {
|
||||
"Use: \n"
|
||||
" probe-gpio <get> [GPIO]\n"
|
||||
" probe-gpio <set> <GPIO> <OPTIONS>\n"
|
||||
"\n"
|
||||
" get: retrieve GPIO state\n"
|
||||
" if 'get' is specified with no further arguments, then the state of all gpios is returned.\n"
|
||||
"\n"
|
||||
" set: GPIO must be specified. One at a time, unlike raspi-gpio.\n"
|
||||
" OPTIONS: one of:\n"
|
||||
" op - drive GPIO output\n"
|
||||
" ip - drive GPIO input\n"
|
||||
" dl - drive low\n"
|
||||
" dh - drive high\n"
|
||||
" pu - pull-up\n"
|
||||
" pd - pull-down\n"
|
||||
" pn - pull none\n"
|
||||
};
|
||||
|
||||
const char* pull_state[] = {
|
||||
"NONE",
|
||||
"UP",
|
||||
"DOWN",
|
||||
"KEEPER"
|
||||
};
|
||||
|
||||
const char* dir_state[] = {
|
||||
"INPUT",
|
||||
"OUTPUT",
|
||||
};
|
||||
|
||||
static void print_gpio_state(int gpio, int level, int function, unsigned int dir, unsigned int pull)
|
||||
{
|
||||
fprintf(stdout, "GPIO %d: level=%d function=%d dir=%s pull=%s\n",
|
||||
gpio, level, function,
|
||||
dir <= 1 ? dir_state[dir] : "UNK",
|
||||
pull <= 3 ? pull_state[pull] : "UNK"
|
||||
);
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc, i = 0;
|
||||
struct libusb_device_handle *devh = NULL;
|
||||
struct libusb_device **dlist = NULL;
|
||||
struct libusb_context *ctx = NULL;
|
||||
struct libusb_device_descriptor desc = {0};
|
||||
int op;
|
||||
int gpio;
|
||||
int set_fn = -1;
|
||||
unsigned char dummy[4];
|
||||
unsigned char data[4] = {};
|
||||
uint32_t glevel, gfunction, gdir, gpull;
|
||||
|
||||
if (argc < 2 || argc > 4) {
|
||||
fprintf(stderr, "Need an operation to do.\n");
|
||||
fprintf(stderr, "%s", usage);
|
||||
return -1;
|
||||
}
|
||||
if (strcmp(argv[1], "get") == 0) {
|
||||
op = BMREQUEST_GPIO_GET;
|
||||
if (argc == 2)
|
||||
gpio = GPIO_ALL;
|
||||
else {
|
||||
gpio = strtol(argv[2], NULL, 10);
|
||||
if (gpio < 0 || gpio > 28) {
|
||||
fprintf(stderr, "gpio out of range: %d\n", gpio);
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[1], "set") == 0) {
|
||||
op = BMREQUEST_GPIO_SET;
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "need to specify gpio and operation\n");
|
||||
fprintf(stderr, "%s", usage);
|
||||
return -1;
|
||||
}
|
||||
gpio = strtol(argv[2], NULL, 10);
|
||||
if (gpio < 0 || gpio > 29) {
|
||||
fprintf(stderr, "gpio out of range: %d\n", gpio);
|
||||
return errno;
|
||||
}
|
||||
if (strcmp(argv[3], "op") == 0) {
|
||||
set_fn = GPIO_SET_DIR;
|
||||
data[0] = GPIO_OUT;
|
||||
} else if (strcmp(argv[3], "ip") == 0) {
|
||||
set_fn = GPIO_SET_DIR;
|
||||
data[0] = GPIO_IN;
|
||||
} else if (strcmp(argv[3], "dl") == 0) {
|
||||
set_fn = GPIO_PUT;
|
||||
data[0] = 0;
|
||||
} else if (strcmp(argv[3], "dh") == 0) {
|
||||
set_fn = GPIO_PUT;
|
||||
data[0] = 1;
|
||||
} else if (strcmp(argv[3], "pu") == 0) {
|
||||
set_fn = GPIO_SET_PULLS;
|
||||
data[0] = 1;
|
||||
} else if (strcmp(argv[3], "pd") == 0) {
|
||||
set_fn = GPIO_SET_PULLS;
|
||||
data[0] = 1 << 1;
|
||||
} else if (strcmp(argv[3], "pn") == 0) {
|
||||
set_fn = GPIO_SET_PULLS;
|
||||
data[0] = 0;
|
||||
} else {
|
||||
fprintf(stderr, "invalid operation specified\n");
|
||||
fprintf(stderr, "%s", usage);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Operation must be <set> or <get>\n");
|
||||
fprintf(stderr, "%s", usage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize libusb
|
||||
*/
|
||||
rc = libusb_init(&ctx);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc));
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
/* Set debugging output to max level.
|
||||
*/
|
||||
#if LIBUSB_API_VERSION >= 0x01000106
|
||||
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, 3);
|
||||
#else
|
||||
libusb_set_debug(ctx, 3);
|
||||
#endif
|
||||
rc = libusb_get_device_list(ctx, &dlist);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error retrieving device list: %s\n", libusb_error_name(i));
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
|
||||
/* Look for a specific device and open it.
|
||||
*/
|
||||
for (i = 0; i < rc; i++) {
|
||||
libusb_device *device = dlist[i];
|
||||
libusb_get_device_descriptor(device, &desc);
|
||||
if (desc.idVendor == VENDOR_ID && desc.idProduct == PRODUCT_ID) {
|
||||
//if(desc.bcdDevice == BCDD_VER) {
|
||||
//printf(stderr, "Found a Debug Probe version %04x - using it\n", desc.bcdDevice);
|
||||
break;
|
||||
// } else {
|
||||
//fprintf(stderr, "Found a Debug Probe version %04x - incompatible\n", desc.bcdDevice);
|
||||
// }
|
||||
}
|
||||
}
|
||||
if (i == rc) {
|
||||
fprintf(stderr, "Error: no compatible Debug Probes found - wrong fw?\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = libusb_open(dlist[i], &devh);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error: can't open device at address %u: %s\n", libusb_get_device_address(dlist[i]), libusb_error_name(rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
libusb_free_device_list(dlist, 1);
|
||||
|
||||
/* Does the it do remote gpio? */
|
||||
rc = libusb_control_transfer(devh, BMREQUEST_GPIO_GET, CTRL_REMOTE_GPIO_REQ, GPIO_GET_FUNCTION,
|
||||
0, (unsigned char *)&dummy, sizeof(dummy), 0);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error: probe doesn't understand REMOTE_GPIO access - wrong fw?\n");
|
||||
goto out;
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
if (op == BMREQUEST_GPIO_GET) {
|
||||
if (gpio == GPIO_ALL) {
|
||||
for (i = 0; i < NR_GPIOS; i++) {
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET,
|
||||
i, (unsigned char *)&glevel, sizeof(glevel), 3000); if (rc < 0) goto out;
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_FUNCTION,
|
||||
i, (unsigned char *)&gfunction, sizeof(gfunction), 3000); if (rc < 0) goto out;
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_DIR,
|
||||
i, (unsigned char *)&gdir, sizeof(gdir), 3000); if (rc < 0) goto out;
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_PULLS,
|
||||
i, (unsigned char *)&gpull, sizeof(gpull), 3000); if (rc < 0) goto out;
|
||||
print_gpio_state(i, glevel, gfunction, gdir, gpull);
|
||||
}
|
||||
} else {
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET,
|
||||
gpio, (unsigned char *)&glevel, sizeof(glevel), 3000); if (rc < 0) goto out;
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_FUNCTION,
|
||||
gpio, (unsigned char *)&gfunction, sizeof(gfunction), 3000); if (rc < 0) goto out;
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_DIR,
|
||||
gpio, (unsigned char *)&gdir, sizeof(gdir), 3000); if (rc < 0) goto out;
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_GET_PULLS,
|
||||
gpio, (unsigned char *)&gpull, sizeof(gpull), 3000); if (rc < 0) goto out;
|
||||
print_gpio_state(gpio, glevel, gfunction, gdir, gpull);
|
||||
}
|
||||
} else if (op == BMREQUEST_GPIO_SET) {
|
||||
// Need to init a gpio before SIO can do anything useful to it. This clobbers output enable, so avoid glitches
|
||||
rc = libusb_control_transfer(devh, BMREQUEST_GPIO_GET, CTRL_REMOTE_GPIO_REQ, GPIO_GET_FUNCTION,
|
||||
gpio, (unsigned char *)dummy, 4, 300); if (rc < 0) goto out;
|
||||
if (dummy[0] != 5) {
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, GPIO_INIT,
|
||||
gpio, (unsigned char *)data, 4, 3000); if (rc < 0) goto out;
|
||||
//printf("fn was %d, setting init\n", gfunction);
|
||||
}
|
||||
rc = libusb_control_transfer(devh, op, CTRL_REMOTE_GPIO_REQ, set_fn,
|
||||
gpio, (unsigned char *)data, 4, 3000); if (rc < 0) goto out;
|
||||
}
|
||||
|
||||
libusb_release_interface(devh, 0);
|
||||
|
||||
out:
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "libusb error: %s\n", libusb_error_name(rc));
|
||||
}
|
||||
if (devh)
|
||||
libusb_close(devh);
|
||||
libusb_exit(NULL);
|
||||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
#ifndef _REMOTE_GPIO_H_
|
||||
#define _REMOTE_GPIO_H_
|
||||
|
||||
#ifdef PICO_SDK_VERSION_MAJOR
|
||||
#include "tusb.h"
|
||||
|
||||
bool gpio_remote_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
#endif
|
||||
|
||||
/* Control interface definitions for exposing pico-sdk's GPIO API.
|
||||
* bmRequestType.direction - SET calls are Host to Device (0), GET calls are Device to Host (1)
|
||||
* bmRequestType.type = Vendor (2)
|
||||
* bmRequestType.recipient = Device (0)
|
||||
* (i.e. bmRequestType = 0x40 or 0xc0.)
|
||||
* bRequest = CTRL_REMOTE_GPIO_REQ (0x02)
|
||||
* wValue - GPIO API function indexed by enum here. Note that there are fewer get_ calls defined than set_ calls.
|
||||
* wIndex - for functions operating on a single GPIO, the GPIO number. For functions operating on all GPIOs in bulk (e.g gpio_get_all or gpio_dir_out_masked), set to zero.
|
||||
* wLength = 4. For SET calls, provide a le32 word of either the 2nd argument to a function call for a specific GPIO, or a mask of GPIOs to operate on in bulk.
|
||||
* Note: for GPIO_INIT and GPIO_DEINIT, the data stage still happens but is ignored.
|
||||
* For GET calls, returns a le32 of either the function call result, or the results of a bulk operation as a bitmask.
|
||||
**/
|
||||
|
||||
#define CTRL_REMOTE_GPIO_REQ 0x2
|
||||
#define BMREQUEST_GPIO_SET 0x40
|
||||
#define BMREQUEST_GPIO_GET 0xc0
|
||||
|
||||
typedef enum {
|
||||
GPIO_GET_FUNCTION,
|
||||
GPIO_GET_PULLS,
|
||||
GPIO_GET_INPUT_ENABLED,
|
||||
GPIO_GET_INPUT_HYST_ENABLED,
|
||||
GPIO_GET_SLEW_RATE,
|
||||
GPIO_GET_DRIVE_STRENGTH,
|
||||
GPIO_GET,
|
||||
GPIO_GET_ALL,
|
||||
GPIO_GET_OUT_LEVEL,
|
||||
GPIO_GET_DIR,
|
||||
GPIO_GET_MAX,
|
||||
} gpio_get_fns;
|
||||
|
||||
typedef enum {
|
||||
GPIO_SET_FUNCTION,
|
||||
GPIO_SET_PULLS,
|
||||
GPIO_SET_INPUT_ENABLED,
|
||||
GPIO_SET_INPUT_HYST_ENABLED,
|
||||
GPIO_SET_SLEW_RATE,
|
||||
GPIO_SET_DRIVE_STRENGTH,
|
||||
GPIO_PUT, // No idea why this isn't called gpio_set in the sdk
|
||||
GPIO_PUT_ALL,
|
||||
GPIO_SET_MASK,
|
||||
GPIO_CLR_MASK,
|
||||
GPIO_XOR_MASK,
|
||||
GPIO_SET_DIR_OUT_MASKED,
|
||||
GPIO_SET_DIR_IN_MASKED,
|
||||
GPIO_SET_DIR_ALL_BITS,
|
||||
GPIO_SET_DIR,
|
||||
GPIO_INIT,
|
||||
GPIO_INIT_MASK,
|
||||
GPIO_DEINIT,
|
||||
GPIO_SET_MAX,
|
||||
} gpio_set_fns;
|
||||
|
||||
#endif /* _REMOTE_GPIO_H_ */
|
||||
@@ -102,11 +102,9 @@
|
||||
*/
|
||||
|
||||
/* SMP port only */
|
||||
#define configNUMBER_OF_CORES 2
|
||||
#define configTICK_CORE 0
|
||||
#define configUSE_CORE_AFFINITY 1
|
||||
#define configNUM_CORES 1
|
||||
#define configTICK_CORE 1
|
||||
#define configRUN_MULTIPLE_PRIORITIES 1
|
||||
#define configUSE_PASSIVE_IDLE_HOOK 0
|
||||
|
||||
/* RP2040 specific */
|
||||
#define configSUPPORT_PICO_SYNC_INTEROP 1
|
||||
|
||||
387
src/autobaud.c
387
src/autobaud.c
@@ -1,387 +0,0 @@
|
||||
#include <pico/stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
#include "autobaud.h"
|
||||
#include "probe_config.h"
|
||||
#include "autobaud.pio.h"
|
||||
|
||||
// DMA buffer size
|
||||
#define BUF_SIZE 1024
|
||||
|
||||
// Size of hash table for sample occurrence counts
|
||||
#define HASH_TBL_SIZE 500
|
||||
|
||||
// Minimum sample occurrence ratio to consider a baud rate value valid
|
||||
#define MIN_FREQUENCY 0.05f
|
||||
|
||||
// PIO clock frequency in Hz
|
||||
#define PIO_CLOCK_FREQUENCY 125000000
|
||||
|
||||
// DMA IRQ for autobaud
|
||||
#define DMA_AUTOBAUD_IRQ 0
|
||||
|
||||
// Priority for DMA IRQ handler
|
||||
#define DMA_AUTOBAUD_IRQ_PRIORITY PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY
|
||||
|
||||
typedef struct {
|
||||
int key;
|
||||
int count;
|
||||
} Entry;
|
||||
|
||||
typedef struct {
|
||||
Entry *entries;
|
||||
size_t size;
|
||||
} HashTable;
|
||||
|
||||
// PIO instance
|
||||
static PIO pio;
|
||||
// PIO state machine
|
||||
static int sm = -1;
|
||||
// PIO program offset
|
||||
static int offset = -1;
|
||||
// UART RX GPIO 1 pin
|
||||
static const uint rx_pin = PROBE_UART_RX;
|
||||
|
||||
// Frequency hash table to store samples occurance
|
||||
static HashTable *freq_table;
|
||||
|
||||
// Estimated baud rate and validity
|
||||
static float baud;
|
||||
static float validity;
|
||||
|
||||
// shortest bit duration in PIO cycles
|
||||
static uint32_t min_cycles_count = UINT32_MAX;
|
||||
// longest bit duration in PIO cycles
|
||||
static uint32_t max_cycles_count = 0;
|
||||
|
||||
static uint32_t total_samples; // total samples seen
|
||||
static uint32_t bit_time_sum; // sum of 1-bit times
|
||||
static uint32_t bit_time_count; // total 1-bit times
|
||||
static uint32_t outlier_count; // total of 1-bit times outliers
|
||||
|
||||
// DMA channels to read RX PIO line
|
||||
static int ctrl_chan = -1;
|
||||
static int data_chan = -1;
|
||||
|
||||
// DMA ring buffer storing PIO RX FIFO data
|
||||
static uint32_t rx_buffer[BUF_SIZE] __attribute__((aligned(4096)));
|
||||
|
||||
// DMA control channel reads this value to reload transfer count
|
||||
static const uint32_t dma_reload_count = BUF_SIZE;
|
||||
|
||||
static uintptr_t last_write_addr = (uintptr_t)rx_buffer;
|
||||
static uintptr_t curr_write_addr = (uintptr_t)rx_buffer;
|
||||
|
||||
volatile bool autobaud_running = false;
|
||||
volatile bool autobaud_stopped = true;
|
||||
|
||||
// Queue to hold new baud rate estimates
|
||||
QueueHandle_t baudQueue;
|
||||
|
||||
TaskHandle_t autobaud_taskhandle;
|
||||
|
||||
|
||||
uint32_t hash(uint32_t x, size_t size) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3bu;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3bu;
|
||||
x = (x >> 16) ^ x;
|
||||
return x % size;
|
||||
}
|
||||
|
||||
HashTable *create_table(size_t size) {
|
||||
HashTable *table = malloc(sizeof(HashTable));
|
||||
if (!table) return NULL;
|
||||
table->size = size;
|
||||
table->entries = calloc(size, sizeof(Entry));
|
||||
if (!table->entries) {
|
||||
free(table);
|
||||
return NULL;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
void insert(HashTable *table, int key) {
|
||||
uint32_t idx = hash(key, table->size);
|
||||
while (table->entries[idx].key != 0) {
|
||||
if (table->entries[idx].key == key) {
|
||||
table->entries[idx].count++;
|
||||
return;
|
||||
}
|
||||
idx = (idx + 1) % table->size;
|
||||
}
|
||||
table->entries[idx].key = key;
|
||||
table->entries[idx].count = 1;
|
||||
}
|
||||
|
||||
int get_count(HashTable *table, int key) {
|
||||
uint32_t idx = hash(key, table->size);
|
||||
while (table->entries[idx].key != 0) {
|
||||
if (table->entries[idx].key == key) {
|
||||
return table->entries[idx].count;
|
||||
}
|
||||
idx = (idx + 1) % table->size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_table(HashTable *table) {
|
||||
free(table->entries);
|
||||
free(table);
|
||||
}
|
||||
|
||||
void __isr dma_handler() {
|
||||
// Clear DMA interrupt
|
||||
if ((data_chan >= 0) && dma_irqn_get_channel_status(DMA_AUTOBAUD_IRQ, data_chan)) {
|
||||
dma_irqn_acknowledge_channel(DMA_AUTOBAUD_IRQ, data_chan);
|
||||
}
|
||||
}
|
||||
|
||||
bool dma_configure(PIO pio, uint sm) {
|
||||
// Configure two DMA channels for continuous RX FIFO monitoring:
|
||||
// - data_chan: Continuously reads from PIO RX FIFO into circular buffer
|
||||
// - ctrl_chan: Triggers data_chan to restart when it completes a transfer
|
||||
ctrl_chan = dma_claim_unused_channel(true);
|
||||
if (ctrl_chan < 0) return false;
|
||||
data_chan = dma_claim_unused_channel(true);
|
||||
if (data_chan < 0) return false;
|
||||
|
||||
dma_channel_config data_cfg = dma_channel_get_default_config(data_chan);
|
||||
channel_config_set_transfer_data_size(&data_cfg, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&data_cfg, false);
|
||||
channel_config_set_write_increment(&data_cfg, true);
|
||||
channel_config_set_dreq(&data_cfg, pio_get_dreq(pio, sm, false)); // Trigger when PIO RX FIFO has data to read
|
||||
channel_config_set_chain_to(&data_cfg, ctrl_chan); // Chain to control channel when transfer completes
|
||||
channel_config_set_ring(&data_cfg, true, 12); // Ring buffer size 2^12 = 4096 bytes (1024 uint32_t)
|
||||
|
||||
dma_channel_configure(
|
||||
data_chan,
|
||||
&data_cfg,
|
||||
rx_buffer,
|
||||
&pio->rxf[sm],
|
||||
BUF_SIZE,
|
||||
false
|
||||
);
|
||||
|
||||
dma_channel_config ctrl_cfg = dma_channel_get_default_config(ctrl_chan);
|
||||
channel_config_set_transfer_data_size(&ctrl_cfg, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&ctrl_cfg, false);
|
||||
channel_config_set_write_increment(&ctrl_cfg, false);
|
||||
|
||||
dma_channel_configure(
|
||||
ctrl_chan,
|
||||
&ctrl_cfg,
|
||||
&(dma_hw->ch[data_chan].al1_transfer_count_trig), // Destination: data channel's transfer count register
|
||||
&dma_reload_count, // Source: reload transfer count value (BUF_SIZE)
|
||||
1,
|
||||
false
|
||||
);
|
||||
|
||||
// Enable DMA interrupt on data channel completion
|
||||
irq_add_shared_handler(dma_get_irq_num(DMA_AUTOBAUD_IRQ), dma_handler, DMA_AUTOBAUD_IRQ_PRIORITY);
|
||||
irq_set_enabled(dma_get_irq_num(DMA_AUTOBAUD_IRQ), true);
|
||||
dma_irqn_set_channel_enabled(DMA_AUTOBAUD_IRQ, data_chan, true);
|
||||
|
||||
dma_channel_start(data_chan);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare rounded integer parts of baud rates
|
||||
// Tolerance of 0.5% in detected versus set
|
||||
inline bool baud_changed(float new_baud, float baud) {
|
||||
uint32_t hi = (uint32_t)(baud * 1.005f);
|
||||
uint32_t lo = (uint32_t)(baud * 0.995f);
|
||||
uint32_t new = (uint32_t)new_baud;
|
||||
|
||||
return (new > hi || new < lo);
|
||||
}
|
||||
|
||||
// Processes new DMA samples read from the PIO RX FIFO. Each DMA sample
|
||||
// represents a timestamp (or cycle count) between edges on the input signal.
|
||||
// Accumulates these durations, filters noise, and estimates baud rate.
|
||||
uint estimate_baud_rate() {
|
||||
uint old_progress = total_samples;
|
||||
// Get current DMA write address
|
||||
curr_write_addr = dma_hw->ch[data_chan].write_addr;
|
||||
// Convert absolute addresses to buffer indices
|
||||
size_t curr_index = ((curr_write_addr) - (uintptr_t)rx_buffer) / sizeof(rx_buffer[0]);
|
||||
size_t last_index = (last_write_addr - (uintptr_t)rx_buffer) / sizeof(rx_buffer[0]);
|
||||
|
||||
for (size_t i = last_index; i != curr_index; i = (i + 1) % BUF_SIZE) {
|
||||
uint32_t raw = rx_buffer[i];
|
||||
uint32_t curr_cycles_count = (UINT32_MAX - raw) * 2;
|
||||
insert(freq_table, curr_cycles_count);
|
||||
|
||||
total_samples++;
|
||||
if (curr_cycles_count > max_cycles_count)
|
||||
max_cycles_count = curr_cycles_count;
|
||||
float freq = (float) get_count(freq_table, curr_cycles_count) / (float) total_samples;
|
||||
// if sample is seen at least 5% of all samples,
|
||||
// it is assumed it's not a noisy value
|
||||
if (freq < MIN_FREQUENCY) continue;
|
||||
if (curr_cycles_count < min_cycles_count) {
|
||||
min_cycles_count = curr_cycles_count;
|
||||
bit_time_sum = 0;
|
||||
bit_time_count = 0;
|
||||
outlier_count = 0;
|
||||
continue;
|
||||
}
|
||||
// If current duration is within +10% of min_cycles, treat it as a "1-bit period"
|
||||
if ((curr_cycles_count - min_cycles_count) < ((float)min_cycles_count * 0.1f)) {
|
||||
bit_time_sum += curr_cycles_count;
|
||||
bit_time_count++;
|
||||
// 1-bit period should not be less than 1/9th of the longest period
|
||||
if (curr_cycles_count < (max_cycles_count / 9))
|
||||
outlier_count++;
|
||||
// Calculate baud from average of 1-bit times
|
||||
float avg_bit_time = (float) bit_time_sum / (float) bit_time_count;
|
||||
float new_baud = PIO_CLOCK_FREQUENCY / avg_bit_time;
|
||||
// If baud has changed, send updated baud information to cdc_thread
|
||||
if (baud_changed(new_baud, baud)) {
|
||||
float completeness = 1.0f - expf(-(float) total_samples / 40.0f);
|
||||
float noise_ratio = (float) outlier_count / (float) bit_time_count;
|
||||
float consistency = 1.0f - fminf(noise_ratio * 2.0f, 1.0f);
|
||||
float validity = completeness * consistency;
|
||||
if (validity > 0.6f) {
|
||||
baud = new_baud;
|
||||
BaudInfo_t new_baud_info;
|
||||
new_baud_info.baud = (uint32_t)roundf(baud);
|
||||
new_baud_info.validity = validity;
|
||||
xQueueOverwrite(baudQueue, &new_baud_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
last_write_addr = curr_write_addr;
|
||||
return total_samples - old_progress;
|
||||
}
|
||||
|
||||
void autobaud_deinit() {
|
||||
// Disable DMA IRQ and channels
|
||||
if (data_chan >= 0) {
|
||||
dma_irqn_set_channel_enabled(DMA_AUTOBAUD_IRQ, data_chan, false);
|
||||
dma_irqn_acknowledge_channel(DMA_AUTOBAUD_IRQ, data_chan);
|
||||
dma_channel_unclaim(data_chan);
|
||||
data_chan = -1;
|
||||
}
|
||||
if (ctrl_chan >= 0) {
|
||||
dma_channel_unclaim(ctrl_chan);
|
||||
ctrl_chan = -1;
|
||||
}
|
||||
irq_remove_handler(dma_get_irq_num(DMA_AUTOBAUD_IRQ), dma_handler);
|
||||
if (!irq_has_shared_handler(dma_get_irq_num(DMA_AUTOBAUD_IRQ))) {
|
||||
irq_set_enabled(dma_get_irq_num(DMA_AUTOBAUD_IRQ), false);
|
||||
}
|
||||
|
||||
// Remove PIO program
|
||||
if (pio && (sm >= 0)) {
|
||||
pio_sm_set_enabled(pio, sm, false);
|
||||
pio_sm_unclaim(pio, sm);
|
||||
}
|
||||
if (pio && (offset >= 0)) {
|
||||
pio_remove_program(pio, &autobaud_program, offset);
|
||||
}
|
||||
|
||||
if (freq_table) {
|
||||
free_table(freq_table);
|
||||
freq_table = NULL;
|
||||
}
|
||||
if (baudQueue) {
|
||||
vQueueDelete(baudQueue);
|
||||
baudQueue = NULL;
|
||||
}
|
||||
|
||||
// Reset state
|
||||
pio = NULL;
|
||||
sm = offset = -1;
|
||||
baud = validity = 0.0f;
|
||||
min_cycles_count = UINT32_MAX;
|
||||
max_cycles_count = 0;
|
||||
total_samples = bit_time_sum = bit_time_count = outlier_count = 0;
|
||||
last_write_addr = (uintptr_t)rx_buffer;
|
||||
curr_write_addr = (uintptr_t)rx_buffer;
|
||||
}
|
||||
|
||||
bool autobaud_init() {
|
||||
pio = pio0;
|
||||
// Claim a free PIO state machine
|
||||
sm = pio_claim_unused_sm(pio, true);
|
||||
if (sm < 0)
|
||||
return false;
|
||||
// Add PIO program
|
||||
offset = pio_add_program(pio, &autobaud_program);
|
||||
if (offset < 0) {
|
||||
autobaud_deinit();
|
||||
return false;
|
||||
}
|
||||
float div = (float)clock_get_hz(clk_sys) / PIO_CLOCK_FREQUENCY;
|
||||
autobaud_program_init(pio, sm, offset, rx_pin, div);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
|
||||
// Create hash table to keep count of sample occurance
|
||||
freq_table = create_table(HASH_TBL_SIZE);
|
||||
if (!freq_table) {
|
||||
autobaud_deinit();
|
||||
return false;
|
||||
}
|
||||
// Create queue to send baud information to cdc_thread
|
||||
baudQueue = xQueueCreate(1, sizeof(BaudInfo_t));
|
||||
if (!baudQueue) {
|
||||
autobaud_deinit();
|
||||
return false;
|
||||
}
|
||||
// Set up DMA to continuously write PIO RX data into RAM
|
||||
if (!dma_configure(pio, sm)) {
|
||||
autobaud_deinit();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void autobaud_start() {
|
||||
xTaskNotify(autobaud_taskhandle, AUTOBAUD_CMD_START, eSetValueWithOverwrite);
|
||||
}
|
||||
|
||||
void autobaud_wait_stop() {
|
||||
while (!autobaud_stopped)
|
||||
xTaskNotify(autobaud_taskhandle, AUTOBAUD_CMD_STOP, eSetValueWithOverwrite);
|
||||
}
|
||||
|
||||
// FreeRTOS thread running if MAGIC_BAUD was set by host
|
||||
void autobaud_thread(void * param) {
|
||||
TickType_t wake = xTaskGetTickCount();
|
||||
uint32_t cmd = AUTOBAUD_CMD_NONE;
|
||||
uint processed;
|
||||
|
||||
while (true) {
|
||||
if (!autobaud_running) {
|
||||
// Idle state: thread is blocked here until start command is received
|
||||
xTaskNotifyWait(0, 0xFFFFFFFFu, &cmd, portMAX_DELAY);
|
||||
if (cmd == AUTOBAUD_CMD_START) {
|
||||
if (autobaud_init()) {
|
||||
autobaud_running = true;
|
||||
autobaud_stopped = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check if host requested autobaud termination
|
||||
if (xTaskNotifyWait(0, 0xFFFFFFFFu, &cmd, 0) == pdTRUE) {
|
||||
if (cmd == AUTOBAUD_CMD_STOP) {
|
||||
autobaud_running = false;
|
||||
autobaud_deinit();
|
||||
autobaud_stopped = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
processed = estimate_baud_rate();
|
||||
if (!processed)
|
||||
xTaskDelayUntil(&wake, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#ifndef AUTOBAUD_H
|
||||
#define AUTOBAUD_H
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "queue.h"
|
||||
#include "semphr.h"
|
||||
|
||||
#define MAGIC_BAUD 9728 // 0x2600
|
||||
|
||||
typedef enum {
|
||||
AUTOBAUD_CMD_NONE = 0,
|
||||
AUTOBAUD_CMD_START = 1,
|
||||
AUTOBAUD_CMD_STOP = 2,
|
||||
} autobaud_cmd_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t baud; // Estimated baud rate
|
||||
float validity; // Validity of the estimated baud rate
|
||||
} BaudInfo_t;
|
||||
|
||||
extern volatile bool autobaud_running;
|
||||
extern volatile bool autobaud_stopped;
|
||||
extern QueueHandle_t baudQueue;
|
||||
extern TaskHandle_t autobaud_taskhandle;
|
||||
|
||||
void autobaud_start(void);
|
||||
void autobaud_wait_stop(void);
|
||||
void autobaud_thread(void * param);
|
||||
|
||||
#endif
|
||||
@@ -1,42 +0,0 @@
|
||||
.program autobaud
|
||||
|
||||
.wrap_target
|
||||
|
||||
falling_edge:
|
||||
wait 0 pin 0 ; falling edge detected
|
||||
set x, 0 ; set cycle counter to 0
|
||||
mov x, ~x ; x = 0xFFFFFFFF, to decrement x
|
||||
|
||||
count_cycles:
|
||||
jmp pin rising_edge ; if line went high, save current cycle count
|
||||
jmp x-- count_cycles ; otherwise, decrement and enter loop again
|
||||
|
||||
rising_edge:
|
||||
mov isr, x ; save elapsed cycle count in ISR
|
||||
push noblock ; nonblocking push of the ISR content
|
||||
jmp falling_edge ; repeat sampling process
|
||||
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
|
||||
// Helper function (for use in C program) to initialize this PIO program
|
||||
void autobaud_program_init(PIO pio, uint sm, uint offset, uint rx_pin, float div) {
|
||||
|
||||
// Sets up state machine and wrap target. This function is automatically
|
||||
// generated in autobaud.pio.h.
|
||||
pio_sm_config c = autobaud_program_get_default_config(offset);
|
||||
|
||||
// No need to set PIO funcsel, as this program is receive-only
|
||||
sm_config_set_in_pins(&c, rx_pin);
|
||||
sm_config_set_jmp_pin(&c, rx_pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, rx_pin, 1, false);
|
||||
|
||||
// Set the clock divider for the state machine
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
// Load configuration and jump to start of the program
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
%}
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "tusb.h"
|
||||
#include "autobaud.h"
|
||||
|
||||
#include "probe_config.h"
|
||||
|
||||
@@ -51,8 +50,6 @@ static volatile uint tx_led_debounce;
|
||||
static uint rx_led_debounce;
|
||||
#endif
|
||||
|
||||
static BaudInfo_t baud_info;
|
||||
|
||||
void cdc_uart_init(void) {
|
||||
gpio_set_function(PROBE_UART_TX, GPIO_FUNC_UART);
|
||||
gpio_set_function(PROBE_UART_RX, GPIO_FUNC_UART);
|
||||
@@ -179,22 +176,6 @@ bool cdc_task(void)
|
||||
return keep_alive;
|
||||
}
|
||||
|
||||
void cdc_uart_set_baudrate(uint32_t baudrate) {
|
||||
/* Set the tick thread interval to the amount of time it takes to
|
||||
* fill up half a FIFO. Millis is too coarse for integer divide.
|
||||
*/
|
||||
uint32_t micros = (1000 * 1000 * 16 * 10) / MAX(baudrate, 1);
|
||||
interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ));
|
||||
debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS));
|
||||
probe_info("New baud rate %ld micros %ld interval %lu\n",
|
||||
baudrate, micros, interval);
|
||||
uart_deinit(PROBE_UART_INTERFACE);
|
||||
tud_cdc_write_clear();
|
||||
tud_cdc_read_flush();
|
||||
|
||||
uart_init(PROBE_UART_INTERFACE, baudrate);
|
||||
}
|
||||
|
||||
void cdc_thread(void *ptr)
|
||||
{
|
||||
BaseType_t delayed;
|
||||
@@ -205,38 +186,31 @@ void cdc_thread(void *ptr)
|
||||
keep_alive = cdc_task();
|
||||
if (!keep_alive) {
|
||||
delayed = xTaskDelayUntil(&last_wake, interval);
|
||||
if (delayed == pdFALSE)
|
||||
last_wake = xTaskGetTickCount();
|
||||
if (autobaud_running) {
|
||||
// Receive baud information from autobaud thread
|
||||
if (xQueueReceive(baudQueue, &baud_info, 0) == pdTRUE) {
|
||||
cdc_uart_set_baudrate(baud_info.baud);
|
||||
// Assume 8N1
|
||||
uart_set_format(PROBE_UART_INTERFACE, 8, 1, UART_PARITY_NONE);
|
||||
}
|
||||
}
|
||||
if (delayed == pdFALSE)
|
||||
last_wake = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding)
|
||||
{
|
||||
if (line_coding->bit_rate == MAGIC_BAUD) {
|
||||
if (!autobaud_running)
|
||||
autobaud_start();
|
||||
return;
|
||||
}
|
||||
else if (autobaud_running) {
|
||||
autobaud_wait_stop();
|
||||
}
|
||||
uart_parity_t parity;
|
||||
uint data_bits, stop_bits;
|
||||
|
||||
/* Set the tick thread interval to the amount of time it takes to
|
||||
* fill up half a FIFO. Millis is too coarse for integer divide.
|
||||
*/
|
||||
uint32_t micros = (1000 * 1000 * 16 * 10) / MAX(line_coding->bit_rate, 1);
|
||||
/* Modifying state, so park the thread before changing it. */
|
||||
if (tud_cdc_connected())
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
|
||||
cdc_uart_set_baudrate(line_coding->bit_rate);
|
||||
interval = MAX(1, micros / ((1000 * 1000) / configTICK_RATE_HZ));
|
||||
debounce_ticks = MAX(1, configTICK_RATE_HZ / (interval * DEBOUNCE_MS));
|
||||
probe_info("New baud rate %ld micros %ld interval %lu\n",
|
||||
line_coding->bit_rate, micros, interval);
|
||||
uart_deinit(PROBE_UART_INTERFACE);
|
||||
tud_cdc_write_clear();
|
||||
tud_cdc_read_flush();
|
||||
uart_init(PROBE_UART_INTERFACE, line_coding->bit_rate);
|
||||
|
||||
switch (line_coding->parity) {
|
||||
case CDC_LINE_CODING_PARITY_ODD:
|
||||
|
||||
25
src/main.c
25
src/main.c
@@ -41,12 +41,10 @@
|
||||
#include "probe_config.h"
|
||||
#include "probe.h"
|
||||
#include "cdc_uart.h"
|
||||
#include "autobaud.h"
|
||||
#include "get_serial.h"
|
||||
#include "tusb_edpt_handler.h"
|
||||
#include "DAP.h"
|
||||
#include "hardware/structs/usb.h"
|
||||
#include "remote_gpio.h"
|
||||
|
||||
// UART0 for debugprobe debug
|
||||
// UART1 for debugprobe to target device
|
||||
@@ -60,8 +58,6 @@ static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
|
||||
#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2)
|
||||
#define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1)
|
||||
|
||||
#define AUTOBAUD_TASK_PRIO (tskIDLE_PRIORITY + 1)
|
||||
|
||||
TaskHandle_t dap_taskhandle, tud_taskhandle, mon_taskhandle;
|
||||
|
||||
static int was_configured;
|
||||
@@ -195,9 +191,8 @@ extern uint8_t const desc_ms_os_20[];
|
||||
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
{
|
||||
// nothing to with Status stage
|
||||
if (stage == CONTROL_STAGE_ACK)
|
||||
return true;
|
||||
// nothing to with DATA & ACK stage
|
||||
if (stage != CONTROL_STAGE_SETUP) return true;
|
||||
|
||||
switch (request->bmRequestType_bit.type)
|
||||
{
|
||||
@@ -205,8 +200,6 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
|
||||
switch (request->bRequest)
|
||||
{
|
||||
case 1:
|
||||
if (stage != CONTROL_STAGE_SETUP)
|
||||
return true;
|
||||
if ( request->wIndex == 7 )
|
||||
{
|
||||
// Get Microsoft OS 2.0 compatible descriptor
|
||||
@@ -218,8 +211,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case CTRL_REMOTE_GPIO_REQ:
|
||||
return gpio_remote_req(rhport, stage, request);
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
@@ -238,9 +230,6 @@ void tud_suspend_cb(bool remote_wakeup_en)
|
||||
if (was_configured) {
|
||||
vTaskSuspend(uart_taskhandle);
|
||||
vTaskSuspend(dap_taskhandle);
|
||||
if (autobaud_running)
|
||||
autobaud_wait_stop();
|
||||
vTaskSuspend(autobaud_taskhandle);
|
||||
}
|
||||
/* slow down clk_sys for power saving ? */
|
||||
}
|
||||
@@ -251,7 +240,6 @@ void tud_resume_cb(void)
|
||||
if (was_configured) {
|
||||
vTaskResume(uart_taskhandle);
|
||||
vTaskResume(dap_taskhandle);
|
||||
vTaskResume(autobaud_taskhandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,10 +250,6 @@ void tud_unmount_cb(void)
|
||||
vTaskSuspend(dap_taskhandle);
|
||||
vTaskDelete(uart_taskhandle);
|
||||
vTaskDelete(dap_taskhandle);
|
||||
if (autobaud_running)
|
||||
autobaud_wait_stop();
|
||||
vTaskSuspend(autobaud_taskhandle);
|
||||
vTaskDelete(autobaud_taskhandle);
|
||||
was_configured = 0;
|
||||
}
|
||||
|
||||
@@ -277,9 +261,6 @@ void tud_mount_cb(void)
|
||||
xTaskCreate(cdc_thread, "UART", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIO, &uart_taskhandle);
|
||||
/* Lowest priority thread is debug - need to shuffle buffers before we can toggle swd... */
|
||||
xTaskCreate(dap_thread, "DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle);
|
||||
/* Autobaud detection using PIO as a frequency counter */
|
||||
xTaskCreate(autobaud_thread, "ABR", configMINIMAL_STACK_SIZE, NULL, AUTOBAUD_TASK_PRIO, &autobaud_taskhandle);
|
||||
vTaskCoreAffinitySet(autobaud_taskhandle, (1 << 1));
|
||||
was_configured = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "probe_config.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "probe/version.h"
|
||||
|
||||
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
void bi_decl_config()
|
||||
{
|
||||
bi_decl(bi_program_version_string(PROBE_VERSION));
|
||||
#ifdef PROBE_PIN_RESET
|
||||
bi_decl(bi_1pin_with_name(PROBE_PIN_RESET, "PROBE RESET"));
|
||||
#endif
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Raspberry Pi Ltd
|
||||
*
|
||||
* 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 <pico/stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "hardware/gpio.h"
|
||||
#include "probe_config.h"
|
||||
#include "remote_gpio.h"
|
||||
|
||||
/* tinyUSB gives you multiple callbacks, one per stage */
|
||||
static uint32_t data;
|
||||
|
||||
bool gpio_remote_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
/* Is the host paying attention ? */
|
||||
if (request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_DEVICE)
|
||||
return false;
|
||||
probe_info("remote_req wValue=0x%02x wIndex=0x%02x wLength=0x%02x dir=%d\n", request->wValue, request->wIndex, request->wLength, request->bmRequestType_bit.direction);
|
||||
if (request->wIndex >= NUM_BANK0_GPIOS)
|
||||
return false;
|
||||
switch (request->bmRequestType_bit.direction)
|
||||
{
|
||||
case TUSB_DIR_IN:
|
||||
{
|
||||
if(stage != CONTROL_STAGE_SETUP)
|
||||
return true;
|
||||
if (request->wLength != 4)
|
||||
return false;
|
||||
switch (request->wValue) {
|
||||
case GPIO_GET_FUNCTION:
|
||||
data = gpio_get_function(request->wIndex);
|
||||
break;
|
||||
case GPIO_GET_PULLS:
|
||||
data = (gpio_is_pulled_up(request->wIndex) ? 0x1 : 0);
|
||||
data |= (gpio_is_pulled_down(request->wIndex) ? 0x2 : 0);
|
||||
break;
|
||||
case GPIO_GET_INPUT_HYST_ENABLED:
|
||||
data = gpio_is_input_hysteresis_enabled(request->wIndex);
|
||||
break;
|
||||
case GPIO_GET_SLEW_RATE:
|
||||
data = gpio_get_slew_rate(request->wIndex);
|
||||
break;
|
||||
case GPIO_GET_DRIVE_STRENGTH:
|
||||
data = gpio_get_drive_strength(request->wIndex);
|
||||
break;
|
||||
case GPIO_GET:
|
||||
data = gpio_get(request->wIndex);
|
||||
break;
|
||||
case GPIO_GET_ALL:
|
||||
data = gpio_get_all();
|
||||
break;
|
||||
case GPIO_GET_OUT_LEVEL:
|
||||
data = gpio_get_out_level(request->wIndex);
|
||||
break;
|
||||
case GPIO_GET_DIR:
|
||||
data = gpio_get_dir(request->wIndex);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
probe_info("ctrl IN data 0x%08lx\n", data);
|
||||
return tud_control_xfer(rhport, request, &data, sizeof(data));
|
||||
}
|
||||
case TUSB_DIR_OUT:
|
||||
{
|
||||
/* Data phase should "always" happen */
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
return tud_control_xfer(rhport, request, &data, sizeof(data));
|
||||
} else if (stage == CONTROL_STAGE_DATA) {
|
||||
probe_info("data stage2 data= %08lx\n", data);
|
||||
switch (request->wValue) {
|
||||
case GPIO_SET_FUNCTION:
|
||||
gpio_set_function(request->wIndex, data);
|
||||
break;
|
||||
case GPIO_SET_PULLS:
|
||||
gpio_set_pulls(request->wIndex, !!(data & 0x1), !!(data & 0x2));
|
||||
break;
|
||||
case GPIO_SET_INPUT_ENABLED:
|
||||
gpio_set_input_enabled(request->wIndex, data);
|
||||
break;
|
||||
case GPIO_SET_INPUT_HYST_ENABLED:
|
||||
gpio_set_input_hysteresis_enabled(request->wIndex, data);
|
||||
break;
|
||||
case GPIO_SET_SLEW_RATE:
|
||||
gpio_set_slew_rate(request->wIndex, data);
|
||||
break;
|
||||
case GPIO_SET_DRIVE_STRENGTH:
|
||||
gpio_set_drive_strength(request->wIndex, data);
|
||||
break;
|
||||
case GPIO_PUT:
|
||||
gpio_put(request->wIndex, !!data);
|
||||
break;
|
||||
case GPIO_PUT_ALL:
|
||||
gpio_put_all(data);
|
||||
break;
|
||||
case GPIO_SET_MASK:
|
||||
gpio_set_mask(data);
|
||||
break;
|
||||
case GPIO_CLR_MASK:
|
||||
gpio_clr_mask(data);
|
||||
break;
|
||||
case GPIO_XOR_MASK:
|
||||
gpio_xor_mask(data);
|
||||
break;
|
||||
case GPIO_SET_DIR_OUT_MASKED:
|
||||
gpio_set_dir_out_masked(data);
|
||||
break;
|
||||
case GPIO_SET_DIR_IN_MASKED:
|
||||
gpio_set_dir_in_masked(data);
|
||||
break;
|
||||
case GPIO_SET_DIR_ALL_BITS:
|
||||
gpio_set_dir_all_bits(data);
|
||||
break;
|
||||
case GPIO_SET_DIR:
|
||||
gpio_set_dir(request->wIndex, !!data);
|
||||
break;
|
||||
case GPIO_INIT:
|
||||
gpio_init(request->wIndex);
|
||||
break;
|
||||
case GPIO_INIT_MASK:
|
||||
gpio_init_mask(data);
|
||||
break;
|
||||
case GPIO_DEINIT:
|
||||
gpio_deinit(request->wIndex);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -48,7 +48,7 @@ tusb_desc_device_t const desc_device =
|
||||
|
||||
.idVendor = 0x2E8A, // Pi
|
||||
.idProduct = 0x000c, // CMSIS-DAP Debug Probe
|
||||
.bcdDevice = 0x0223, // Version 02.23
|
||||
.bcdDevice = 0x0222, // Version 02.22
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
Reference in New Issue
Block a user