-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Should be backwards compatible, except that commands ending in just newline (\n) are accepted in addition to commands ending in carriage return + newline (\r\n).
- Loading branch information
1 parent
3896b1a
commit 23fb9c1
Showing
7 changed files
with
414 additions
and
158 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,30 @@ | ||
add_executable(prawnblaster | ||
prawnblaster.cpp | ||
fast_serial.c | ||
) | ||
|
||
pico_generate_pio_header(prawnblaster ${CMAKE_CURRENT_LIST_DIR}/pseudoclock.pio) | ||
|
||
|
||
# Pull in our pico_stdlib which aggregates commonly used features | ||
target_link_libraries(prawnblaster pico_stdlib hardware_pio pico_multicore hardware_clocks hardware_dma) | ||
|
||
# enable usb output, disable uart output | ||
pico_enable_stdio_usb(prawnblaster 1) | ||
pico_enable_stdio_uart(prawnblaster 0) | ||
target_link_libraries(prawnblaster pico_stdlib hardware_pio pico_multicore pico_unique_id hardware_clocks hardware_dma tinyusb_device tinyusb_board) | ||
target_include_directories(prawnblaster PRIVATE .) | ||
|
||
# create map/bin/hex/uf2 file etc. | ||
pico_add_extra_outputs(prawnblaster) | ||
|
||
add_executable(prawnblasteroverclock | ||
prawnblaster.cpp | ||
fast_serial.c | ||
) | ||
|
||
pico_generate_pio_header(prawnblasteroverclock ${CMAKE_CURRENT_LIST_DIR}/pseudoclock.pio) | ||
|
||
set_target_properties(prawnblasteroverclock PROPERTIES COMPILE_DEFINITIONS PRAWNBLASTER_OVERCLOCK=1) | ||
|
||
# Pull in our pico_stdlib which aggregates commonly used features | ||
target_link_libraries(prawnblasteroverclock pico_stdlib hardware_pio pico_multicore hardware_clocks hardware_dma) | ||
|
||
# enable usb output, disable uart output | ||
pico_enable_stdio_usb(prawnblasteroverclock 1) | ||
pico_enable_stdio_uart(prawnblasteroverclock 0) | ||
target_link_libraries(prawnblasteroverclock pico_stdlib hardware_pio pico_multicore pico_unique_id hardware_clocks hardware_dma tinyusb_device tinyusb_board) | ||
target_include_directories(prawnblasteroverclock PRIVATE .) | ||
|
||
# create map/bin/hex/uf2 file etc. | ||
pico_add_extra_outputs(prawnblasteroverclock) | ||
pico_add_extra_outputs(prawnblasteroverclock) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
#include "tusb.h" | ||
#include "pico/unique_id.h" | ||
|
||
#include <stdarg.h> | ||
|
||
#include "fast_serial.h" | ||
|
||
/* | ||
Serial functions | ||
These are mostly thin wrappers around TinyUSB functions; | ||
they are provided to simplify the API. | ||
*/ | ||
|
||
// Read bytes (blocks until buffer_size is reached) | ||
uint32_t fast_serial_read(const char * buffer, uint32_t buffer_size){ | ||
uint32_t buffer_idx = 0; | ||
while(buffer_idx < buffer_size){ | ||
uint32_t buffer_avail = buffer_size - buffer_idx; | ||
uint32_t read_avail = fast_serial_read_available(); | ||
|
||
if(read_avail > 0){ | ||
if(buffer_avail > read_avail){ | ||
buffer_avail = read_avail; | ||
} | ||
|
||
buffer_idx += fast_serial_read_atomic(buffer + buffer_idx, buffer_avail); | ||
} | ||
|
||
fast_serial_task(); | ||
} | ||
return buffer_size; | ||
} | ||
|
||
// Read bytes until terminator reached (blocks until terminator or buffer_size is reached) | ||
uint32_t fast_serial_read_until(char * buffer, uint32_t buffer_size, char until){ | ||
uint32_t buffer_idx = 0; | ||
while(buffer_idx < buffer_size){ | ||
while(fast_serial_read_available() > 0){ | ||
int32_t next_char = tud_cdc_read_char(); | ||
|
||
buffer[buffer_idx] = next_char; | ||
buffer_idx++; | ||
if(next_char == until){ | ||
break; | ||
} | ||
} | ||
|
||
if(buffer_idx > 0 && buffer[buffer_idx-1] == until){ | ||
break; | ||
} | ||
fast_serial_task(); | ||
} | ||
return buffer_idx; | ||
} | ||
|
||
// Write bytes (without flushing, so limited to 64 bytes) | ||
uint32_t fast_serial_write(const char * buffer, uint32_t buffer_size){ | ||
uint32_t buffer_idx = 0; | ||
while(buffer_idx < buffer_size){ | ||
uint32_t write_avail = fast_serial_write_available(); | ||
|
||
if(write_avail > 0){ | ||
if(buffer_size - buffer_idx < write_avail){ | ||
write_avail = buffer_size - buffer_idx; | ||
} | ||
|
||
buffer_idx += fast_serial_write_atomic(buffer + buffer_idx, write_avail); | ||
} | ||
fast_serial_task(); | ||
fast_serial_write_flush(); | ||
} | ||
return buffer_size; | ||
} | ||
|
||
int fast_serial_printf(const char * format, ...){ | ||
va_list va; | ||
va_start(va, format); | ||
char printf_buffer[128]; | ||
int ret = vsnprintf(printf_buffer, 128, format, va); | ||
va_end(va); | ||
if(ret <= 0){ | ||
return ret; | ||
} | ||
return fast_serial_write(printf_buffer, strnlen(printf_buffer, 128)); | ||
} | ||
|
||
/* | ||
USB callbacks | ||
*/ | ||
|
||
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts){} | ||
void tud_cdc_rx_cb(uint8_t itf){} | ||
|
||
/* | ||
USB descriptor setup | ||
We use the same VID, PID and ID as the Pi Pico would normally use. | ||
*/ | ||
tusb_desc_device_t const desc_device = { | ||
.bLength = sizeof(tusb_desc_device_t), | ||
.bDescriptorType = TUSB_DESC_DEVICE, | ||
.bcdUSB = 0x0200, | ||
.bDeviceClass = TUSB_CLASS_MISC, | ||
.bDeviceSubClass = MISC_SUBCLASS_COMMON, | ||
.bDeviceProtocol = MISC_PROTOCOL_IAD, | ||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, | ||
|
||
.idVendor = 0x2E8A, | ||
.idProduct = 0x000A, | ||
.bcdDevice = 0x0100, | ||
|
||
.iManufacturer = 0x01, | ||
.iProduct = 0x02, | ||
.iSerialNumber = 0x03, | ||
|
||
.bNumConfigurations = 0x01 | ||
}; | ||
|
||
uint8_t const * tud_descriptor_device_cb(){ | ||
return (uint8_t const *) &desc_device; | ||
} | ||
|
||
enum{ | ||
ITF_NUM_CDC = 0, | ||
ITF_NUM_CDC_DATA, | ||
ITF_NUM_TOTAL | ||
}; | ||
|
||
#define EPNUM_CDC_NOTIF 0x81 | ||
#define EPNUM_CDC_OUT 0x02 | ||
#define EPNUM_CDC_IN 0x82 | ||
|
||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) | ||
|
||
uint8_t const desc_configuration[] = { | ||
// Config number, interface count, string index, total length, attribute, power in mA | ||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), | ||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size. | ||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64) | ||
}; | ||
|
||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index){ | ||
return desc_configuration; | ||
} | ||
|
||
enum { | ||
STRID_LANGID = 0, | ||
STRID_MANUFACTURER, | ||
STRID_PRODUCT, | ||
STRID_SERIAL, | ||
}; | ||
|
||
static char usb_serial_str[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1]; | ||
|
||
char const* string_desc_arr [] ={ | ||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) | ||
"Raspberry Pi", // 1: Manufacturer | ||
"Pico", // 2: Product | ||
usb_serial_str, // 3: Serials, should use chip ID | ||
"Board CDC", // 4: CDC Interface | ||
}; | ||
|
||
static uint16_t _desc_str[32]; | ||
|
||
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid){ | ||
uint8_t chr_count; | ||
|
||
if(!usb_serial_str[0]){ | ||
pico_get_unique_board_id_string(usb_serial_str, sizeof(usb_serial_str)); | ||
} | ||
|
||
if(index == 0){ | ||
memcpy(&_desc_str[1], string_desc_arr[0], 2); | ||
chr_count = 1; | ||
} | ||
else{ | ||
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 = (uint8_t) strlen(str); | ||
if ( chr_count > 31 ) chr_count = 31; | ||
|
||
// Convert ASCII string into UTF-16 | ||
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] = (uint16_t) ((TUSB_DESC_STRING << 8 ) | (2*chr_count + 2)); | ||
|
||
return _desc_str; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
Faster serial functions | ||
fast_serial_read/fast_serial_read_until are blocking functions | ||
designed to receive data over a USB serial connection as fast as possible | ||
(hopefully at the limit of the drivers). | ||
fast_serial_read is generally much faster (~4x) than fast_serial_read_until, | ||
as it can read larger blocks of data without having to scan for the terminating character. | ||
Therefore, it is recommended to use fixed size blocks for large transmissions. | ||
fast_serial_write is a blocking function | ||
designed to send data over a USB serial connection as fast as possible | ||
(again hopefully at the limit of the drivers). | ||
The remaining functions are thin wrappers around TinyUSB functions; | ||
they are provided to simplify the API. | ||
Basic usage: | ||
Call fast_serial_init() | ||
In the main processing loop, call fast_serial_task. | ||
Place calls to fast_serial_read/fast_serial_read_until and fast_serial_write where appropriate. | ||
*/ | ||
#include "tusb.h" | ||
|
||
// Initialize the USB stack | ||
static inline bool fast_serial_init(){ | ||
return tusb_init(); | ||
} | ||
|
||
// Get number of bytes available to read | ||
static inline uint32_t fast_serial_read_available(){ | ||
return tud_cdc_available(); | ||
} | ||
|
||
// Get number of bytes available to write | ||
static inline uint32_t fast_serial_write_available(){ | ||
return tud_cdc_write_available(); | ||
} | ||
|
||
// Read up to 64 bytes | ||
static inline uint32_t fast_serial_read_atomic(char * buffer, uint32_t buffer_size){ | ||
return tud_cdc_read(buffer, buffer_size); | ||
} | ||
|
||
// Read bytes (blocks until buffer_size is reached) | ||
uint32_t fast_serial_read(const char * buffer, uint32_t buffer_size); | ||
|
||
// Read bytes until terminator reached (blocks until terminator or buffer_size is reached) | ||
uint32_t fast_serial_read_until(char * buffer, uint32_t buffer_size, char until); | ||
|
||
// Clear read FIFO (without reading it) | ||
static inline void fast_serial_read_flush(){ | ||
tud_cdc_read_flush(); | ||
} | ||
|
||
// Write bytes (without flushing, so limited to 64 bytes) | ||
static inline uint32_t fast_serial_write_atomic(const char * buffer, uint32_t buffer_size){ | ||
return tud_cdc_write(buffer, buffer_size); | ||
} | ||
|
||
// Write bytes (without flushing) | ||
uint32_t fast_serial_write(const char * buffer, uint32_t buffer_size); | ||
|
||
// print via fast_serial_write | ||
int fast_serial_printf(const char * format, ...); | ||
|
||
// Force write of data. Returns number of bytes written. | ||
static inline uint32_t fast_serial_write_flush(){ | ||
return tud_cdc_write_flush(); | ||
} | ||
|
||
// Must be called regularly from main loop | ||
static inline void fast_serial_task(){ | ||
tud_task(); | ||
} |
Oops, something went wrong.