Compare commits
3 Commits
master
...
cmsis_subm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2eeef92b89 | ||
|
|
8a0c487df3 | ||
|
|
2a421d1648 |
@@ -22,7 +22,6 @@ add_executable(debugprobe
|
||||
src/get_serial.c
|
||||
src/sw_dp_pio.c
|
||||
src/tusb_edpt_handler.c
|
||||
src/autobaud.c
|
||||
)
|
||||
|
||||
target_sources(debugprobe PRIVATE
|
||||
@@ -43,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
|
||||
)
|
||||
@@ -89,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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
static 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:
|
||||
|
||||
14
src/main.c
14
src/main.c
@@ -41,7 +41,6 @@
|
||||
#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"
|
||||
@@ -59,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;
|
||||
@@ -233,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 ? */
|
||||
}
|
||||
@@ -246,7 +240,6 @@ void tud_resume_cb(void)
|
||||
if (was_configured) {
|
||||
vTaskResume(uart_taskhandle);
|
||||
vTaskResume(dap_taskhandle);
|
||||
vTaskResume(autobaud_taskhandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,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;
|
||||
}
|
||||
|
||||
@@ -272,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
|
||||
|
||||
@@ -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