First prototype
This commit is contained in:
parent
c4c96cc03c
commit
4d952cb1d4
13 changed files with 1742 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
**/build/
|
||||
tool/
|
||||
.cache/
|
||||
.vscode/
|
||||
43
README.md
43
README.md
|
|
@ -1,2 +1,45 @@
|
|||
# Pico-PIO-USB
|
||||
|
||||
USB FS host using PIO of raspberry pi pico (RP2040).
|
||||
|
||||
You can add additional USB port to RP2040.
|
||||
|
||||
🚧 **This library is WIP. API may be changed drastically in future.** 🚧
|
||||
|
||||
## Demo
|
||||
|
||||
https://user-images.githubusercontent.com/43873124/146642806-bdf34af6-4342-4a95-bfca-229cdc4bdca2.mp4
|
||||
|
||||
## Project status
|
||||
|
||||
|Planned Features|Status|
|
||||
|-|-|
|
||||
|FS Host|✔|
|
||||
|LS Host|🚧|
|
||||
|Hub support|🚧|
|
||||
|FS Device|🚧|
|
||||
|
||||
## Examples
|
||||
|
||||
`capture_hid_report.c` is a sample program which print HID reports received from device.
|
||||
|
||||
```bash
|
||||
cd example
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
# Copy UF2 file to RPiPico and open serial port
|
||||
```
|
||||
|
||||
Another sample program for split keyboard with QMK
|
||||
|
||||
[https://github.com/sekigon-gonnoc/qmk_firmware/tree/rp2040/keyboards/pico_pico_usb](https://github.com/sekigon-gonnoc/qmk_firmware/tree/rp2040/keyboards/pico_pico_usb)
|
||||
|
||||
## Resource Usage
|
||||
|
||||
- Two PIO
|
||||
- One PIO is for USB transmitter using 22 instruction and one state machine
|
||||
- Another PIO is for USB receiver using 32 instruction (No space for other program)
|
||||
- Two GPIO for D+/D- (Series 22ohm resitors are better)
|
||||
- 10KB ROM and RAM
|
||||
|
|
|
|||
22
example/CMakeLists.txt
Executable file
22
example/CMakeLists.txt
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
cmake_minimum_required(VERSION 3.13)
|
||||
include (pico_sdk_import.cmake)
|
||||
project(pio_usb)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
set(target_name capture_hid_report)
|
||||
add_executable(${target_name})
|
||||
|
||||
pico_enable_stdio_usb(${target_name} 1)
|
||||
|
||||
pico_generate_pio_header(${target_name} ${CMAKE_CURRENT_LIST_DIR}/../usb_tx.pio)
|
||||
pico_generate_pio_header(${target_name} ${CMAKE_CURRENT_LIST_DIR}/../usb_rx.pio)
|
||||
|
||||
target_sources(${target_name} PRIVATE capture_hid_report.c ../pio_usb.c ../usb_crc.c)
|
||||
target_link_options(${target_name} PRIVATE -Xlinker --print-memory-usage)
|
||||
target_compile_options(${target_name} PRIVATE -Wall -Wextra)
|
||||
target_include_directories(${target_name} PRIVATE ../)
|
||||
|
||||
target_link_libraries(${target_name} PRIVATE pico_stdlib pico_multicore hardware_pio hardware_dma)
|
||||
pico_add_extra_outputs(${target_name})
|
||||
|
||||
64
example/capture_hid_report.c
Normal file
64
example/capture_hid_report.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "pico/bootrom.h"
|
||||
|
||||
#include "pio_usb.h"
|
||||
|
||||
static usb_device_t *usb_device = NULL;
|
||||
|
||||
void core1_main() {
|
||||
sleep_ms(10);
|
||||
|
||||
// To run USB SOF interrupt in core1, create alarm pool in core1.
|
||||
static pio_usb_configuration_t config = PIO_USB_DEFAULT_CONFIG;
|
||||
config.alarm_pool = (void*)alarm_pool_create(2, 1);
|
||||
usb_device = pio_usb_init(&config);
|
||||
|
||||
while (true) {
|
||||
pio_usb_task();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// default 125MHz is not appropreate. Sysclock should be multiple of 12MHz.
|
||||
set_sys_clock_khz(120000, true);
|
||||
|
||||
stdio_init_all();
|
||||
printf("hello!");
|
||||
|
||||
sleep_ms(10);
|
||||
|
||||
multicore_reset_core1();
|
||||
// all USB task run in core1
|
||||
multicore_launch_core1(core1_main);
|
||||
|
||||
while (true) {
|
||||
if (usb_device != NULL && usb_device->enumerated) {
|
||||
// Print received packet to EPs
|
||||
for (int ep_idx = 0; ep_idx < PIO_USB_EP_CNT; ep_idx++) {
|
||||
endpoint_t *ep = &usb_device->endpoint[ep_idx];
|
||||
|
||||
if ((ep->ep_num & EP_IN) && ep->new_data_flag) {
|
||||
// Copy received data to local buffer
|
||||
uint8_t temp[64], len;
|
||||
len = ep->packet_len;
|
||||
memcpy(temp, (const void*)ep->buffer, len);
|
||||
// notify to USB task
|
||||
ep->new_data_flag = false;
|
||||
|
||||
printf("%04x:%04x EP 0x%02x:\t", usb_device->vid, usb_device->pid,
|
||||
ep->ep_num);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
printf("%02x ", temp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
sleep_us(10);
|
||||
}
|
||||
}
|
||||
62
example/pico_sdk_import.cmake
Executable file
62
example/pico_sdk_import.cmake
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
# 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()
|
||||
|
||||
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 Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
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 Raspberry Pi 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
|
||||
"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 Raspberry Pi Pico SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
||||
898
pio_usb.c
Normal file
898
pio_usb.c
Normal file
|
|
@ -0,0 +1,898 @@
|
|||
/**
|
||||
* Copyright (c) 2021 sekigon-gonnoc
|
||||
*/
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("-O3")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h> // memcpy
|
||||
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "pio_usb.h"
|
||||
#include "usb_definitions.h"
|
||||
#include "usb_crc.h"
|
||||
#include "usb_tx.pio.h"
|
||||
#include "usb_rx.pio.h"
|
||||
|
||||
#define UNUSED_PARAMETER(x) (void)x
|
||||
|
||||
#define IRQ_TX_COMP_MASK (1 << usb_tx_IRQ_COMP)
|
||||
#define IRQ_RX_COMP_MASK (1 << IRQ_RX_EOP)
|
||||
#define IRQ_RX_ALL_MASK ((1 << IRQ_RX_EOP) | (1 << IRQ_RX_BS_ERR))
|
||||
|
||||
PIO pio_usb_tx; // colud not set to static
|
||||
static volatile uint sm_tx;
|
||||
static volatile uint tx_ch;
|
||||
|
||||
PIO pio_usb_rx; // colud not set to static
|
||||
static volatile uint sm_rx;
|
||||
static volatile uint offset_rx;
|
||||
static volatile uint offset_eop;
|
||||
static volatile uint rx_reset_instr;
|
||||
|
||||
static volatile uint pin_dp;
|
||||
static volatile uint pin_dm;
|
||||
|
||||
static usb_device_t usb_device;
|
||||
|
||||
static usb_setup_packet_t get_device_descriptor_report = GET_DEVICE_DESCRIPTOR_REQ_DEFAULT;
|
||||
static usb_setup_packet_t get_configuration_descriptor_report = GET_CONFIGURATION_DESCRIPTOR_REQ_DEFAULT;
|
||||
static usb_setup_packet_t set_usb_configuration_report = SET_CONFIGURATION_REQ_DEFAULT;
|
||||
static usb_setup_packet_t set_address_request = SET_ADDRESS_REQ_DEFAULT;
|
||||
|
||||
static usb_setup_packet_t get_hid_report_descrpitor_request = GET_HID_REPORT_DESCRIPTOR_DEFAULT;
|
||||
static usb_setup_packet_t set_hid_idle_request = SET_HID_IDLE_REQ_DEFAULT;
|
||||
|
||||
static uint8_t usb_rx_buffer[128];
|
||||
|
||||
static void __not_in_flash_func(usb_transfer)(uint8_t *data, uint16_t len) {
|
||||
dma_channel_transfer_from_buffer_now(tx_ch, data, len);
|
||||
|
||||
pio_sm_set_enabled(pio_usb_tx, sm_tx, true);
|
||||
pio0->irq |= IRQ_TX_COMP_MASK; // clear complete flag
|
||||
|
||||
while ((pio0->irq & IRQ_TX_COMP_MASK) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pio_sm_set_enabled(pio_usb_tx, sm_tx, false);
|
||||
dma_channel_abort(tx_ch);
|
||||
}
|
||||
|
||||
void __no_inline_not_in_flash_func(send_setup_packet)(uint8_t addr, uint8_t ep_num) {
|
||||
static uint8_t packet[] = {USB_SYNC, USB_PID_SETUP, 0, 0};
|
||||
uint16_t dat = ((uint16_t)(ep_num & 0xf) << 7) | (addr & 0x7f);
|
||||
uint8_t crc = calc_usb_crc5(dat);
|
||||
packet[2] = dat & 0xff;
|
||||
packet[3] = (crc << 3) | ((dat >> 8) & 0x1f);
|
||||
|
||||
usb_transfer(packet, sizeof(packet));
|
||||
}
|
||||
|
||||
void __no_inline_not_in_flash_func(send_out_token)(uint8_t addr, uint8_t ep_num) {
|
||||
static uint8_t packet[] = {USB_SYNC, USB_PID_OUT, 0, 0};
|
||||
uint16_t dat = ((uint16_t)(ep_num & 0xf) << 7) | (addr & 0x7f);
|
||||
uint8_t crc = calc_usb_crc5(dat);
|
||||
packet[2] = dat & 0xff;
|
||||
packet[3] = (crc << 3) | ((dat >> 8) & 0x1f);
|
||||
|
||||
usb_transfer(packet, sizeof(packet));
|
||||
}
|
||||
|
||||
static void __no_inline_not_in_flash_func(send_ack)(void) {
|
||||
static uint8_t data[] = {USB_SYNC, USB_PID_ACK};
|
||||
// usb_transfer(data, sizeof(data));
|
||||
dma_channel_transfer_from_buffer_now(tx_ch, data, 2);
|
||||
|
||||
pio_sm_set_enabled(pio_usb_tx, sm_tx, true);
|
||||
pio0->irq |= IRQ_TX_COMP_MASK; // clear complete flag
|
||||
|
||||
while ((pio0->irq & IRQ_TX_COMP_MASK) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pio_sm_set_enabled(pio_usb_tx, sm_tx, false);
|
||||
dma_channel_abort(tx_ch);
|
||||
}
|
||||
|
||||
void __no_inline_not_in_flash_func(send_nak)(void) {
|
||||
uint8_t data[] = {USB_SYNC, USB_PID_NAK};
|
||||
usb_transfer(data, sizeof(data));
|
||||
}
|
||||
|
||||
static void __no_inline_not_in_flash_func(prepare_receive)(void) {
|
||||
pio_sm_set_enabled(pio_usb_rx, sm_rx, false);
|
||||
pio_sm_clear_fifos(pio_usb_rx, sm_rx);
|
||||
pio_sm_restart(pio_usb_rx, sm_rx);
|
||||
pio_sm_exec(pio_usb_rx, sm_rx, rx_reset_instr);
|
||||
}
|
||||
|
||||
static void __no_inline_not_in_flash_func(start_receive)(void) {
|
||||
pio_usb_rx->ctrl |= (1 << sm_rx);
|
||||
pio_usb_rx->irq |= IRQ_RX_ALL_MASK;
|
||||
}
|
||||
|
||||
void __no_inline_not_in_flash_func(data_transfer)(uint8_t * tx_data_address, uint8_t tx_data_len)
|
||||
{
|
||||
prepare_receive();
|
||||
usb_transfer(tx_data_address, tx_data_len);
|
||||
start_receive();
|
||||
}
|
||||
|
||||
void __no_inline_not_in_flash_func(control_setup_transfer)(uint8_t * tx_data_address, uint8_t tx_data_len)
|
||||
{
|
||||
prepare_receive();
|
||||
|
||||
send_setup_packet(usb_device.address, 0);
|
||||
usb_transfer(tx_data_address, tx_data_len);
|
||||
|
||||
start_receive();
|
||||
}
|
||||
|
||||
void __no_inline_not_in_flash_func(control_out_transfer)(uint8_t * tx_data_address, uint8_t tx_data_len)
|
||||
{
|
||||
prepare_receive();
|
||||
|
||||
send_out_token(usb_device.address, 0);
|
||||
usb_transfer(tx_data_address, tx_data_len);
|
||||
|
||||
start_receive();
|
||||
}
|
||||
|
||||
packet_info_t __no_inline_not_in_flash_func(calc_in_token)(uint8_t addr, uint8_t ep_num) {
|
||||
static uint8_t packet[] = {USB_SYNC, USB_PID_IN, 0, 0};
|
||||
uint16_t dat = ((uint16_t)(ep_num & 0xf) << 7) | (addr & 0x7f);
|
||||
uint8_t crc = calc_usb_crc5(dat);
|
||||
packet[2] = dat & 0xff;
|
||||
packet[3] = (crc << 3) | ((dat >> 8) & 0x1f);
|
||||
|
||||
packet_info_t packet_info = {.tx_address = packet, .tx_length = sizeof(packet)};
|
||||
|
||||
return packet_info;
|
||||
}
|
||||
|
||||
static void __no_inline_not_in_flash_func(wait_handshake)(void) {
|
||||
int16_t t = 120;
|
||||
int16_t idx = 0;
|
||||
|
||||
while (t--) {
|
||||
if (pio_sm_get_rx_fifo_level(pio_usb_rx, sm_rx)) {
|
||||
uint8_t data = pio_sm_get(pio_usb_rx, sm_rx) >> 24;
|
||||
usb_rx_buffer[idx++] = data;
|
||||
if (idx == 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t > 0) {
|
||||
while ((pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
pio_sm_set_enabled(pio_usb_rx, sm_rx, false);
|
||||
}
|
||||
|
||||
static int __no_inline_not_in_flash_func(usb_out_transaction)(uint8_t addr, endpoint_t * ep) {
|
||||
int res = -1;
|
||||
|
||||
prepare_receive();
|
||||
send_out_token(addr, ep->ep_num & 0xf);
|
||||
usb_transfer((uint8_t*)ep->buffer, ep->packet_len);
|
||||
start_receive();
|
||||
|
||||
wait_handshake();
|
||||
|
||||
if (usb_rx_buffer[1] == USB_PID_ACK) {
|
||||
res = 0;
|
||||
ep->data_id ^= 1;
|
||||
ep->new_data_flag = false;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
pio_sm_set_enabled(pio_usb_rx, sm_rx, false);
|
||||
usb_rx_buffer[0] = 0;
|
||||
usb_rx_buffer[1] = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __no_inline_not_in_flash_func(receive_packet_and_ack)(void) {
|
||||
uint16_t crc = 0xffff;
|
||||
uint16_t crc_prev = 0xffff;
|
||||
uint16_t crc_prev2 = 0xffff;
|
||||
uint16_t crc_receive = 0xffff;
|
||||
bool crc_match = false;
|
||||
int16_t t = 120;
|
||||
uint16_t idx = 0;
|
||||
|
||||
while (t--) {
|
||||
if (pio_sm_get_rx_fifo_level(pio_usb_rx, sm_rx)) {
|
||||
uint8_t data = pio_sm_get(pio_usb_rx, sm_rx) >> 24;
|
||||
usb_rx_buffer[idx++] = data;
|
||||
if (idx == 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// timing critical start
|
||||
if (t > 0) {
|
||||
while ((pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) {
|
||||
if (pio_sm_get_rx_fifo_level(pio_usb_rx, sm_rx)) {
|
||||
uint8_t data = pio_sm_get(pio_usb_rx, sm_rx) >> 24;
|
||||
crc_prev2 = crc_prev;
|
||||
crc_prev = crc;
|
||||
crc = update_usb_crc16(crc, data);
|
||||
usb_rx_buffer[idx++] = data;
|
||||
crc_receive = (crc_receive >> 8) | (data << 8);
|
||||
crc_match = ((crc_receive ^ 0xffff) == crc_prev2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idx >= 4 && crc_match) {
|
||||
send_ack();
|
||||
// timing critical end
|
||||
return idx - 4;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __no_inline_not_in_flash_func(usb_in_transaction)(uint8_t addr, endpoint_t * ep) {
|
||||
int res = -1;
|
||||
|
||||
uint8_t expect_token = ep->data_id == 0 ? USB_PID_DATA0 : USB_PID_DATA1;
|
||||
packet_info_t in_token;
|
||||
in_token = calc_in_token(addr, ep->ep_num & 0x0f);
|
||||
data_transfer(in_token.tx_address, in_token.tx_length);
|
||||
|
||||
int receive_len = receive_packet_and_ack();
|
||||
if (receive_len >= 0) {
|
||||
if (usb_rx_buffer[1] == expect_token) {
|
||||
memcpy((void *)(ep->buffer), &usb_rx_buffer[2], receive_len);
|
||||
ep->packet_len = receive_len;
|
||||
ep->data_id ^= 1;
|
||||
ep->new_data_flag = true;
|
||||
}
|
||||
res = 0;
|
||||
} else {
|
||||
res = -1;
|
||||
if ((pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) {
|
||||
res = -2;
|
||||
}
|
||||
}
|
||||
|
||||
pio_sm_set_enabled(pio_usb_rx, sm_rx, false);
|
||||
usb_rx_buffer[0] = 0;
|
||||
usb_rx_buffer[1] = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __no_inline_not_in_flash_func(usb_control_out_transfer)(control_pipe_t * pipe) {
|
||||
int res = 0;
|
||||
|
||||
if (pipe->stage == STAGE_SETUP) {
|
||||
control_setup_transfer(pipe->setup_packet.tx_address, pipe->setup_packet.tx_length);
|
||||
wait_handshake();
|
||||
|
||||
if (usb_rx_buffer[0] == USB_SYNC && usb_rx_buffer[1] == USB_PID_ACK) {
|
||||
if (pipe->out_data_packet.tx_address != NULL) {
|
||||
pipe->stage = STAGE_OUT;
|
||||
} else {
|
||||
pipe->stage = STAGE_STATUS;
|
||||
}
|
||||
usb_rx_buffer[1] = 0; // reset buffer
|
||||
}
|
||||
} else if (pipe->stage == STAGE_OUT) {
|
||||
control_out_transfer(pipe->out_data_packet.tx_address,
|
||||
pipe->out_data_packet.tx_length);
|
||||
wait_handshake();
|
||||
if (usb_rx_buffer[0] == USB_SYNC && usb_rx_buffer[1] == USB_PID_ACK) {
|
||||
pipe->stage = STAGE_STATUS;
|
||||
}
|
||||
usb_rx_buffer[1] = 0;
|
||||
} else if (pipe->stage == STAGE_STATUS) {
|
||||
packet_info_t in_token;
|
||||
in_token = calc_in_token(usb_device.address, 0);
|
||||
data_transfer(in_token.tx_address, in_token.tx_length);
|
||||
|
||||
volatile int16_t t = 120;
|
||||
volatile int16_t idx = 0;
|
||||
while (t--) {
|
||||
if (pio_sm_get_rx_fifo_level(pio_usb_rx, sm_rx)) {
|
||||
usb_rx_buffer[idx++] = pio_sm_get(pio_usb_rx, sm_rx) >> 24;
|
||||
if (idx == 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t > 0) {
|
||||
while ((pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) {
|
||||
if (pio_sm_get_rx_fifo_level(pio_usb_rx, sm_rx)) {
|
||||
usb_rx_buffer[idx++] = pio_sm_get(pio_usb_rx, sm_rx) >> 24;
|
||||
}
|
||||
}
|
||||
|
||||
if (usb_rx_buffer[1] == USB_PID_DATA1) {
|
||||
send_ack();
|
||||
pipe->stage = STAGE_COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
pio_sm_set_enabled(pio_usb_rx, sm_rx, false);
|
||||
usb_rx_buffer[1] = 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint8_t __no_inline_not_in_flash_func(usb_control_in_transfer)(control_pipe_t * pipe) {
|
||||
static uint8_t data_in_num = 1;
|
||||
static int16_t buffer_idx = 0;
|
||||
|
||||
uint8_t expect_token = data_in_num == 1 ? USB_PID_DATA1 : USB_PID_DATA0;
|
||||
|
||||
if (pipe->stage == STAGE_SETUP) {
|
||||
control_setup_transfer(pipe->setup_packet.tx_address, pipe->setup_packet.tx_length);
|
||||
|
||||
wait_handshake();
|
||||
|
||||
if (usb_rx_buffer[0] == USB_SYNC && usb_rx_buffer[1] == USB_PID_ACK) {
|
||||
pipe->stage = STAGE_IN;
|
||||
usb_rx_buffer[1] = 0; // reset buffer
|
||||
data_in_num = 1;
|
||||
buffer_idx = 0;
|
||||
}
|
||||
} else if (pipe->stage == STAGE_IN) {
|
||||
packet_info_t in_token;
|
||||
in_token = calc_in_token(usb_device.address, 0);
|
||||
data_transfer(in_token.tx_address, in_token.tx_length);
|
||||
|
||||
int receive_len = receive_packet_and_ack();
|
||||
|
||||
if (receive_len >= 0) {
|
||||
if (usb_rx_buffer[1] == expect_token) {
|
||||
memcpy((void *)(pipe->rx_buffer + buffer_idx), &usb_rx_buffer[2],
|
||||
receive_len);
|
||||
buffer_idx += receive_len;
|
||||
|
||||
if (pipe->request_length <= buffer_idx) {
|
||||
pipe->stage = STAGE_STATUS;
|
||||
}
|
||||
|
||||
data_in_num ^= 1; // togle data num
|
||||
}
|
||||
}
|
||||
|
||||
pio_sm_set_enabled(pio_usb_rx, sm_rx, false);
|
||||
usb_rx_buffer[1] = 0; // reset buffer
|
||||
}
|
||||
|
||||
else if (pipe->stage == STAGE_STATUS) {
|
||||
uint8_t status_packet[] = {USB_SYNC, USB_PID_DATA1, 0, 0};
|
||||
control_out_transfer(status_packet, sizeof(status_packet));
|
||||
|
||||
wait_handshake();
|
||||
|
||||
if (usb_rx_buffer[0] == USB_SYNC && usb_rx_buffer[1] == USB_PID_ACK) {
|
||||
pipe->stage = STAGE_COMPLETE;
|
||||
usb_rx_buffer[1] = 0; // reset buffer
|
||||
} else if (usb_rx_buffer[0] == USB_SYNC && usb_rx_buffer[1] == USB_PID_NAK) {
|
||||
pipe->stage = STAGE_STATUS;
|
||||
usb_rx_buffer[1] = 0; // reset buffer
|
||||
} else {
|
||||
pipe->stage = STAGE_ERROR;
|
||||
usb_rx_buffer[1] = 0; // reset buffer
|
||||
}
|
||||
}
|
||||
|
||||
return pipe->stage;
|
||||
}
|
||||
|
||||
static bool __no_inline_not_in_flash_func(connection_check)(void) {
|
||||
if (gpio_get(pin_dp) == 0 && gpio_get(pin_dm) == 0) {
|
||||
busy_wait_us_32(1);
|
||||
|
||||
if (gpio_get(pin_dp) == 0 && gpio_get(pin_dm) == 0) {
|
||||
// device disconnect
|
||||
if (usb_device.connected) {
|
||||
usb_device.connected = false;
|
||||
usb_device.event = EVENT_DISCONNECT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __no_inline_not_in_flash_func(sof_timer)(repeating_timer_t *_rt) {
|
||||
static uint8_t sof_packet[4] = {USB_SYNC, USB_PID_SOF, 0x00, 0x10};
|
||||
static uint8_t sof_count = 0;
|
||||
static uint8_t reset_count = 0;
|
||||
UNUSED_PARAMETER(_rt);
|
||||
|
||||
if (usb_device.connected && connection_check()) {
|
||||
|
||||
usb_transfer(sof_packet, sizeof(sof_packet));
|
||||
|
||||
switch (usb_device.control_pipe.operation)
|
||||
{
|
||||
case CONTROL_NONE:
|
||||
break;
|
||||
case CONTROL_IN:
|
||||
usb_control_in_transfer(&usb_device.control_pipe);
|
||||
if (usb_device.control_pipe.stage == STAGE_COMPLETE)
|
||||
{
|
||||
usb_device.control_pipe.stage = STAGE_SETUP;
|
||||
usb_device.control_pipe.operation = CONTROL_COMPLETE;
|
||||
}
|
||||
else if (usb_device.control_pipe.stage == STAGE_ERROR)
|
||||
{
|
||||
usb_device.control_pipe.stage = STAGE_SETUP;
|
||||
usb_device.control_pipe.operation = CONTROL_ERROR;
|
||||
}
|
||||
break;
|
||||
case CONTROL_OUT:
|
||||
usb_control_out_transfer(&usb_device.control_pipe);
|
||||
if (usb_device.control_pipe.stage == STAGE_COMPLETE)
|
||||
{
|
||||
usb_device.control_pipe.stage = STAGE_SETUP;
|
||||
usb_device.control_pipe.operation = CONTROL_COMPLETE;
|
||||
}
|
||||
else if (usb_device.control_pipe.stage == STAGE_ERROR)
|
||||
{
|
||||
usb_device.control_pipe.stage = STAGE_SETUP;
|
||||
usb_device.control_pipe.operation = CONTROL_ERROR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (int ep_idx = 0; ep_idx < PIO_USB_EP_CNT; ep_idx++) {
|
||||
endpoint_t * ep = &usb_device.endpoint[ep_idx];
|
||||
if (!ep->ep_num || !ep->is_interrupt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ep->interval_counter > 0) {
|
||||
ep->interval_counter--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ep->ep_num & EP_IN) {
|
||||
int res = usb_in_transaction(usb_device.address, ep);
|
||||
ep->interval_counter = ep->interval - 1;
|
||||
if (res == 0) {
|
||||
// ep->interval_counter = ep->interval;
|
||||
} else if (res <= -2) {
|
||||
// fatal
|
||||
usb_device.connected = false;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (ep->new_data_flag) {
|
||||
int res = usb_out_transaction(usb_device.address, ep);
|
||||
ep->interval_counter = ep->interval - 1;
|
||||
if (res == 0) {
|
||||
ep->interval_counter = ep->interval - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
const uint pin_tx = 0;
|
||||
if (reset_count == 0 && gpio_get(pin_dp) == 1 && gpio_get(pin_dm) == 0) {
|
||||
reset_count = 80;
|
||||
}
|
||||
|
||||
if (reset_count > 3) {
|
||||
pio_sm_set_pins_with_mask(pio_usb_tx, sm_tx, (0b00 << pin_tx),
|
||||
(0b11u << pin_tx));
|
||||
pio_sm_set_pindirs_with_mask(pio_usb_tx, sm_tx, (0b11u << pin_tx),
|
||||
(0b11u << pin_tx));
|
||||
} else if (reset_count > 2) {
|
||||
pio_sm_set_pindirs_with_mask(pio_usb_tx, sm_tx, (0b00u << pin_tx),
|
||||
(0b11u << pin_tx));
|
||||
} else if (reset_count > 0) {
|
||||
if (gpio_get(pin_dp) == 1 && gpio_get(pin_dm) == 0) {
|
||||
if (!usb_device.connected){
|
||||
usb_device.connected = true;
|
||||
usb_device.event = EVENT_CONNECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reset_count) {
|
||||
reset_count--;
|
||||
}
|
||||
}
|
||||
|
||||
sof_count = (sof_count + 1) & 0x1f;
|
||||
sof_packet[2] = sof_count & 0xff;
|
||||
sof_packet[3] = (calc_usb_crc5(sof_count) << 3) | (sof_count >> 8);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void update_packet_crc16(usb_setup_packet_t * packet) {
|
||||
uint16_t crc16 = calc_usb_crc16(&packet->request_type,
|
||||
sizeof(*packet) - 4);
|
||||
packet->crc16[0] = crc16 & 0xff;
|
||||
packet->crc16[1] = crc16 >> 8;
|
||||
}
|
||||
|
||||
static int __no_inline_not_in_flash_func(control_out_protocol)(
|
||||
uint8_t *setup_data, uint16_t setup_length, uint8_t *out_data,
|
||||
uint16_t out_length) {
|
||||
int res = 0;
|
||||
|
||||
if (usb_device.control_pipe.operation == CONTROL_NONE) {
|
||||
usb_device.control_pipe.setup_packet.tx_address = setup_data;
|
||||
usb_device.control_pipe.setup_packet.tx_length = setup_length;
|
||||
usb_device.control_pipe.out_data_packet.tx_address = out_data;
|
||||
usb_device.control_pipe.out_data_packet.tx_length = out_length;
|
||||
usb_device.control_pipe.operation = CONTROL_OUT;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint64_t timeout = 5000 * 1000; // 5s
|
||||
uint64_t start_time = time_us_64();
|
||||
while (usb_device.control_pipe.operation == CONTROL_OUT &&
|
||||
time_us_64() - start_time < timeout) {
|
||||
if (!usb_device.connected) {
|
||||
usb_device.control_pipe.operation = CONTROL_NONE;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (time_us_64() - start_time >= timeout) {
|
||||
printf("control out[timeout]\n");
|
||||
res = -2;
|
||||
} else if (usb_device.control_pipe.operation == CONTROL_ERROR) {
|
||||
printf("control out[error]\n");
|
||||
res = -1;
|
||||
} else if (usb_device.control_pipe.operation == CONTROL_COMPLETE) {
|
||||
printf("control out[complete]\n");
|
||||
res = 0;
|
||||
}
|
||||
usb_device.control_pipe.operation = CONTROL_NONE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __no_inline_not_in_flash_func(control_in_protocol)(uint8_t *data,
|
||||
uint16_t length) {
|
||||
int res = 0;
|
||||
if (usb_device.control_pipe.operation == CONTROL_NONE) {
|
||||
usb_device.control_pipe.setup_packet.tx_address = data;
|
||||
usb_device.control_pipe.setup_packet.tx_length = length;
|
||||
usb_device.control_pipe.operation = CONTROL_IN;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint64_t timeout = 5000 * 1000; // 5s
|
||||
uint64_t start_time = time_us_64();
|
||||
while (usb_device.control_pipe.operation == CONTROL_IN &&
|
||||
time_us_64() - start_time < timeout) {
|
||||
if (!usb_device.connected) {
|
||||
usb_device.control_pipe.operation = CONTROL_NONE;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (time_us_64() - start_time >= timeout) {
|
||||
printf("control in[timeout]\n");
|
||||
res = -2;
|
||||
} else if (usb_device.control_pipe.operation == CONTROL_ERROR) {
|
||||
printf("control in[error]\n");
|
||||
res = -1;
|
||||
} else if (usb_device.control_pipe.operation == CONTROL_COMPLETE) {
|
||||
printf("control in[complete]\n");
|
||||
res = 0;
|
||||
}
|
||||
usb_device.control_pipe.operation = CONTROL_NONE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int __no_inline_not_in_flash_func(pio_usb_set_out_data)(endpoint_t *ep,
|
||||
const uint8_t *buffer,
|
||||
uint8_t len) {
|
||||
if (ep->new_data_flag || !ep->is_interrupt || (ep->ep_num & EP_IN)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ep->buffer[0] = USB_SYNC;
|
||||
ep->buffer[1] = ep->data_id == 0 ? USB_PID_DATA0 : USB_PID_DATA1;
|
||||
memcpy((uint8_t *)&ep->buffer[2], buffer, len);
|
||||
uint16_t crc = calc_usb_crc16(buffer, len);
|
||||
ep->buffer[2 + len] = crc & 0xff;
|
||||
ep->buffer[2 + len + 1] = (crc >> 8) & 0xff;
|
||||
ep->packet_len = len + 4;
|
||||
|
||||
ep->new_data_flag = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enumerate_device(void) {
|
||||
int res = 0;
|
||||
usb_device.control_pipe.request_length = 18;
|
||||
update_packet_crc16(&get_device_descriptor_report);
|
||||
res = control_in_protocol((uint8_t *)&get_device_descriptor_report,
|
||||
sizeof(get_device_descriptor_report));
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const device_descriptor_t * desc = (device_descriptor_t*)usb_device.control_pipe.rx_buffer;
|
||||
usb_device.vid = desc->vid[0] | (desc->vid[1] << 8);
|
||||
usb_device.pid = desc->pid[0] | (desc->pid[1] << 8);
|
||||
|
||||
set_address_request.value_lsb = 1;
|
||||
set_address_request.value_msb = 0;
|
||||
update_packet_crc16(&set_address_request);
|
||||
res = control_out_protocol((uint8_t *)&set_address_request,
|
||||
sizeof(set_address_request), NULL, 0);
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
usb_device.address = 1;
|
||||
|
||||
get_configuration_descriptor_report.length_lsb = 9;
|
||||
get_configuration_descriptor_report.length_msb = 0;
|
||||
usb_device.control_pipe.request_length = 9;
|
||||
update_packet_crc16(&get_configuration_descriptor_report);
|
||||
res = control_in_protocol((uint8_t *)&get_configuration_descriptor_report,
|
||||
sizeof(get_configuration_descriptor_report));
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
get_configuration_descriptor_report.length_lsb =
|
||||
((configuration_descriptor_t *)(usb_device.control_pipe.rx_buffer))
|
||||
->total_length_lsb;
|
||||
get_configuration_descriptor_report.length_msb =
|
||||
((configuration_descriptor_t *)(usb_device.control_pipe.rx_buffer))
|
||||
->total_length_msb;
|
||||
usb_device.control_pipe.request_length =
|
||||
get_configuration_descriptor_report.length_lsb |
|
||||
(get_configuration_descriptor_report.index_msb << 8);
|
||||
update_packet_crc16(&get_configuration_descriptor_report);
|
||||
res = control_in_protocol((uint8_t *)&get_configuration_descriptor_report,
|
||||
sizeof(get_configuration_descriptor_report));
|
||||
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
uint8_t configuration_descrptor_data[512];
|
||||
int16_t configuration_descrptor_length =
|
||||
usb_device.control_pipe.request_length > 512
|
||||
? 512
|
||||
: usb_device.control_pipe.request_length;
|
||||
memcpy(configuration_descrptor_data,
|
||||
(const void *)usb_device.control_pipe.rx_buffer,
|
||||
configuration_descrptor_length);
|
||||
|
||||
set_usb_configuration_report.value_lsb =
|
||||
((configuration_descriptor_t *)(usb_device.control_pipe.rx_buffer))
|
||||
->configuration_value;
|
||||
update_packet_crc16(&set_usb_configuration_report);
|
||||
res = control_out_protocol((uint8_t *)&set_usb_configuration_report,
|
||||
sizeof(set_usb_configuration_report), NULL, 0);
|
||||
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
volatile uint8_t ep_idx = 0;
|
||||
volatile uint8_t interface = 0;
|
||||
volatile uint8_t class = 0;
|
||||
uint8_t *descriptor = configuration_descrptor_data;
|
||||
while (configuration_descrptor_length > 0) {
|
||||
switch (descriptor[1]) {
|
||||
case DESC_TYPE_INTERFACE: {
|
||||
const interface_descriptor_t *d =
|
||||
(const interface_descriptor_t *)descriptor;
|
||||
printf(
|
||||
"inum:%d, altsetting:%d, numep:%d, iclass:%d, isubclass:%d, "
|
||||
"iprotcol:%d, iface:%d\n",
|
||||
d->inum, d->altsetting, d->numep, d->iclass, d->isubclass,
|
||||
d->iprotocol, d->iface);
|
||||
interface = d->inum;
|
||||
class = d->iclass;
|
||||
} break;
|
||||
case DESC_TYPE_ENDPOINT: {
|
||||
const endpoint_descriptor_t *d =
|
||||
(const endpoint_descriptor_t *)descriptor;
|
||||
printf("\t\t\tepaddr:0x%02x, attr:%d, size:%d, interval:%d\n", d->epaddr,
|
||||
d->attr, d->max_size[0] | (d->max_size[1] << 8), d->interval);
|
||||
|
||||
if (class == CLASS_HID && d->attr == EP_ATTR_INTERRUPT) {
|
||||
volatile endpoint_t *ep = &usb_device.endpoint[ep_idx];
|
||||
ep->interval = d->interval;
|
||||
ep->interval_counter = 0;
|
||||
ep->size = d->max_size[0] | (d->max_size[1] << 8);
|
||||
ep->is_interrupt = true;
|
||||
ep->ep_num = d->epaddr;
|
||||
ep_idx++;
|
||||
}
|
||||
} break;
|
||||
case DESC_TYPE_HID: {
|
||||
const hid_descriptor_t *d = (const hid_descriptor_t *)descriptor;
|
||||
printf(
|
||||
"\tbcdHID:%x.%x, country:%d, desc num:%d, desc_type:%d, "
|
||||
"desc_size:%d\n",
|
||||
d->bcd_hid[1], d->bcd_hid[0], d->contry_code, d->num_desc,
|
||||
d->desc_type, d->desc_len[0] | (d->desc_len[1] << 8));
|
||||
|
||||
set_hid_idle_request.value_lsb = interface;
|
||||
set_hid_idle_request.value_msb = 0;
|
||||
update_packet_crc16(&set_hid_idle_request);
|
||||
control_out_protocol((uint8_t *)&set_hid_idle_request,
|
||||
sizeof(set_hid_idle_request), NULL, 0);
|
||||
|
||||
get_hid_report_descrpitor_request.index_lsb = interface;
|
||||
get_hid_report_descrpitor_request.index_msb = 0;
|
||||
get_hid_report_descrpitor_request.length_lsb = d->desc_len[0];
|
||||
get_hid_report_descrpitor_request.length_msb = d->desc_len[1];
|
||||
uint16_t desc_len = d->desc_len[0] | (d->desc_len[1] << 8);
|
||||
usb_device.control_pipe.request_length = desc_len;
|
||||
update_packet_crc16(&get_hid_report_descrpitor_request);
|
||||
control_in_protocol((uint8_t *)&get_hid_report_descrpitor_request,
|
||||
sizeof(get_hid_report_descrpitor_request));
|
||||
printf("\t\tReport descriptor:");
|
||||
for (int i = 0; i < desc_len; i++) {
|
||||
printf("%02x ", usb_device.control_pipe.rx_buffer[i]);
|
||||
}
|
||||
printf("\n");
|
||||
stdio_flush();
|
||||
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
configuration_descrptor_length -= descriptor[0];
|
||||
descriptor += descriptor[0];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void configure_tx_channel(uint8_t ch, PIO pio, uint sm) {
|
||||
dma_channel_config conf = dma_channel_get_default_config(ch);
|
||||
|
||||
channel_config_set_read_increment(&conf, true);
|
||||
channel_config_set_write_increment(&conf, false);
|
||||
channel_config_set_transfer_data_size(&conf, DMA_SIZE_8);
|
||||
channel_config_set_dreq(&conf, pio_get_dreq(pio, sm, true));
|
||||
|
||||
dma_channel_set_config(ch, &conf, false);
|
||||
dma_channel_set_write_addr(ch, &pio->txf[sm], false);
|
||||
}
|
||||
|
||||
static repeating_timer_t sof_rt;
|
||||
static pio_usb_configuration_t current_config;
|
||||
static bool timer_active;
|
||||
usb_device_t *pio_usb_init(const pio_usb_configuration_t *c) {
|
||||
pio_usb_tx = c->pio_tx_num == 0 ? pio0 : pio1;
|
||||
sm_tx = c->sm_tx;
|
||||
tx_ch = c->tx_ch;
|
||||
pio_usb_rx = c->pio_rx_num == 0 ? pio0 : pio1;
|
||||
sm_rx = c->sm_rx;
|
||||
pin_dp = c->pin_dp;
|
||||
pin_dm = c->pin_dp + 1;
|
||||
uint offset_tx = pio_add_program(pio_usb_tx, &usb_tx_program);
|
||||
usb_tx_program_init(pio_usb_tx, sm_tx, offset_tx, pin_dp);
|
||||
|
||||
configure_tx_channel(tx_ch, pio_usb_tx, sm_tx);
|
||||
|
||||
if (c->debug_pin_rx < 0) {
|
||||
offset_rx = pio_add_program(pio_usb_rx, &usb_rx_program);
|
||||
} else {
|
||||
offset_rx = pio_add_program(pio_usb_rx, &usb_rx_debug_program);
|
||||
}
|
||||
usb_rx_program_init(pio_usb_rx, sm_rx, offset_rx, pin_dp, c->debug_pin_rx);
|
||||
rx_reset_instr = pio_encode_jmp(offset_rx);
|
||||
|
||||
if (c->debug_pin_eop < 0) {
|
||||
offset_eop = pio_add_program(pio_usb_rx, &eop_detect_program);
|
||||
} else {
|
||||
offset_eop = pio_add_program(pio_usb_rx, &eop_detect_debug_program);
|
||||
}
|
||||
eop_detect_program_init(pio_usb_rx, c->sm_eop, offset_eop, pin_dp,
|
||||
c->debug_pin_eop);
|
||||
|
||||
if (c->alarm_pool != NULL) {
|
||||
alarm_pool_add_repeating_timer_us(c->alarm_pool, -1000, sof_timer, NULL,
|
||||
&sof_rt);
|
||||
} else {
|
||||
add_repeating_timer_us(-1000, sof_timer, NULL, &sof_rt);
|
||||
}
|
||||
|
||||
current_config = *c;
|
||||
timer_active = true;
|
||||
|
||||
return &usb_device;
|
||||
}
|
||||
|
||||
static volatile bool cancel_timer;
|
||||
static volatile bool start_timer;
|
||||
static uint32_t int_stat;
|
||||
|
||||
void pio_usb_stop(void) {
|
||||
cancel_timer = true;
|
||||
while (cancel_timer) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
void pio_usb_restart(void) {
|
||||
start_timer = true;
|
||||
while (start_timer) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
void __no_inline_not_in_flash_func(pio_usb_task)(void) {
|
||||
if (usb_device.event == EVENT_CONNECT) {
|
||||
usb_device.event = EVENT_NONE;
|
||||
printf("Connected\n");
|
||||
int res = enumerate_device();
|
||||
if (res == 0) {
|
||||
usb_device.enumerated = true;
|
||||
} else {
|
||||
usb_device.connected = false;
|
||||
usb_device.event = EVENT_DISCONNECT;
|
||||
}
|
||||
} else if (usb_device.event == EVENT_DISCONNECT) {
|
||||
usb_device.event = EVENT_NONE;
|
||||
printf("Disconnect\n");
|
||||
memset(&usb_device, 0, sizeof(usb_device));
|
||||
usb_device.enumerated = false;
|
||||
}
|
||||
|
||||
if (cancel_timer) {
|
||||
int_stat = save_and_disable_interrupts();
|
||||
|
||||
cancel_repeating_timer(&sof_rt);
|
||||
memset(&usb_device, 0, sizeof(usb_device));
|
||||
|
||||
timer_active = false;
|
||||
cancel_timer = false;
|
||||
}
|
||||
|
||||
if (start_timer && !timer_active) {
|
||||
if (current_config.alarm_pool != NULL) {
|
||||
alarm_pool_add_repeating_timer_us(current_config.alarm_pool, -1000,
|
||||
sof_timer, NULL, &sof_rt);
|
||||
} else {
|
||||
add_repeating_timer_us(-1000, sof_timer, NULL, &sof_rt);
|
||||
}
|
||||
|
||||
restore_interrupts(int_stat);
|
||||
timer_active = true;
|
||||
start_timer = false;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC pop_options
|
||||
11
pio_usb.h
Normal file
11
pio_usb.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "pio_usb_configuration.h"
|
||||
#include "usb_definitions.h"
|
||||
|
||||
usb_device_t *pio_usb_init(const pio_usb_configuration_t *c);
|
||||
void pio_usb_task(void);
|
||||
int pio_usb_set_out_data(endpoint_t *ep, const uint8_t *buffer, uint8_t len);
|
||||
void pio_usb_stop(void);
|
||||
void pio_usb_restart(void);
|
||||
38
pio_usb_configuration.h
Normal file
38
pio_usb_configuration.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
typedef struct {
|
||||
uint8_t pin_dp;
|
||||
uint8_t pio_tx_num;
|
||||
uint8_t sm_tx;
|
||||
uint8_t tx_ch;
|
||||
uint8_t pio_rx_num;
|
||||
uint8_t sm_rx;
|
||||
uint8_t sm_eop;
|
||||
void* alarm_pool;
|
||||
int8_t debug_pin_rx;
|
||||
int8_t debug_pin_eop;
|
||||
} pio_usb_configuration_t;
|
||||
|
||||
#define PIO_USB_DP_PIN_DEFAULT 0
|
||||
#define PIO_USB_DM_PIN_DEFAULT (PIO_USB_DP_PIN_DEFAULT + 1)
|
||||
|
||||
#define PIO_USB_TX_DEFAULT 0
|
||||
#define PIO_SM_USB_TX_DEFAULT 0
|
||||
#define PIO_USB_DMA_TX_DEFAULT 0
|
||||
|
||||
#define PIO_USB_RX_DEFAULT 1
|
||||
#define PIO_SM_USB_RX_DEFAULT 0
|
||||
#define PIO_SM_USB_EOP_DEFAULT 1
|
||||
|
||||
#define PIO_USB_DEBUG_PIN_NONE (-1)
|
||||
|
||||
#define PIO_USB_DEFAULT_CONFIG \
|
||||
{ \
|
||||
PIO_USB_DP_PIN_DEFAULT, PIO_USB_TX_DEFAULT, PIO_SM_USB_TX_DEFAULT, \
|
||||
PIO_USB_DMA_TX_DEFAULT, PIO_USB_RX_DEFAULT, PIO_SM_USB_RX_DEFAULT, \
|
||||
PIO_SM_USB_EOP_DEFAULT, NULL, PIO_USB_DEBUG_PIN_NONE, \
|
||||
PIO_USB_DEBUG_PIN_NONE \
|
||||
}
|
||||
|
||||
#define PIO_USB_EP_CNT 16
|
||||
63
usb_crc.c
Normal file
63
usb_crc.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
#include "usb_crc.h"
|
||||
|
||||
uint8_t calc_usb_crc5(uint16_t data) {
|
||||
const uint8_t crc5_tbl[32] = {
|
||||
0x00, 0x0b, 0x16, 0x1d, 0x05, 0x0e, 0x13, 0x18, 0x0a, 0x01,
|
||||
0x1c, 0x17, 0x0f, 0x04, 0x19, 0x12, 0x14, 0x1f, 0x02, 0x09,
|
||||
0x11, 0x1a, 0x07, 0x0c, 0x1e, 0x15, 0x08, 0x03, 0x1b, 0x10,
|
||||
};
|
||||
|
||||
data = data ^ 0x1f;
|
||||
|
||||
const uint8_t lsb = (data >> 1) & 0x1f;
|
||||
const uint8_t msb = (data >> 6) & 0x1f;
|
||||
|
||||
uint8_t crc = (data & 1) ? 0x14 : 0x00;
|
||||
crc = crc5_tbl[(lsb ^ crc)];
|
||||
crc = crc5_tbl[(msb ^ crc)];
|
||||
|
||||
return crc ^ 0x1f;
|
||||
}
|
||||
|
||||
// Place to RAM
|
||||
const uint16_t __not_in_flash("crc_tbl") crc16_tbl[256] = {
|
||||
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601,
|
||||
0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0,
|
||||
0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81,
|
||||
0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941,
|
||||
0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01,
|
||||
0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0,
|
||||
0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081,
|
||||
0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
|
||||
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00,
|
||||
0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0,
|
||||
0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981,
|
||||
0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41,
|
||||
0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700,
|
||||
0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0,
|
||||
0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281,
|
||||
0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
|
||||
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01,
|
||||
0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1,
|
||||
0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80,
|
||||
0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541,
|
||||
0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101,
|
||||
0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0,
|
||||
0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481,
|
||||
0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
|
||||
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801,
|
||||
0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1,
|
||||
0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581,
|
||||
0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341,
|
||||
0x4100, 0x81c1, 0x8081, 0x4040};
|
||||
|
||||
uint16_t calc_usb_crc16(const uint8_t *data, uint16_t len) {
|
||||
uint16_t crc = 0xffff;
|
||||
|
||||
for (int idx = 0; idx < len; idx++) {
|
||||
crc = (crc >> 8) ^ crc16_tbl[(crc ^ data[idx]) & 0xff];
|
||||
}
|
||||
|
||||
return crc ^ 0xffff;
|
||||
}
|
||||
15
usb_crc.h
Normal file
15
usb_crc.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
// Calc CRC5-USB of 11bit data
|
||||
uint8_t calc_usb_crc5(uint16_t data);
|
||||
// Calc CRC16-USB of array
|
||||
uint16_t calc_usb_crc16(const uint8_t *data, uint16_t len);
|
||||
|
||||
extern const uint16_t crc16_tbl[256];
|
||||
static inline uint16_t __time_critical_func(update_usb_crc16)(uint16_t crc, uint8_t data) {
|
||||
crc = (crc >> 8) ^ crc16_tbl[(crc ^ data) & 0xff];
|
||||
return crc;
|
||||
}
|
||||
225
usb_definitions.h
Normal file
225
usb_definitions.h
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pio_usb_configuration.h"
|
||||
|
||||
typedef enum {
|
||||
CONTROL_NONE,
|
||||
CONTROL_IN,
|
||||
CONTROL_OUT,
|
||||
CONTROL_COMPLETE,
|
||||
CONTROL_ERROR,
|
||||
} control_transfer_operation_t;
|
||||
|
||||
typedef enum {
|
||||
EP_IN = 0x80,
|
||||
EP_OUT = 0x00,
|
||||
} ep_type_t;
|
||||
|
||||
typedef enum {
|
||||
STAGE_SETUP,
|
||||
STAGE_IN,
|
||||
STAGE_OUT,
|
||||
STAGE_STATUS,
|
||||
STAGE_COMPLETE,
|
||||
STAGE_ERROR
|
||||
} setup_transfer_stage_t;
|
||||
typedef enum {
|
||||
STAGE_DEVICE,
|
||||
STAGE_CONFIG,
|
||||
STAGE_CONFIG2,
|
||||
STAGE_INTERFACE,
|
||||
STAGE_ENDPOINT
|
||||
} usb_communication_stage_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *tx_address;
|
||||
uint8_t tx_length;
|
||||
} packet_info_t;
|
||||
|
||||
typedef struct {
|
||||
volatile uint8_t rx_buffer[512];
|
||||
volatile packet_info_t setup_packet;
|
||||
volatile packet_info_t out_data_packet;
|
||||
volatile int16_t request_length;
|
||||
volatile control_transfer_operation_t operation;
|
||||
volatile setup_transfer_stage_t stage;
|
||||
} control_pipe_t;
|
||||
|
||||
typedef struct {
|
||||
volatile uint8_t ep_num;
|
||||
volatile uint8_t size;
|
||||
volatile uint8_t buffer[64 + 4];
|
||||
volatile uint8_t packet_len;
|
||||
volatile bool new_data_flag;
|
||||
volatile bool is_interrupt;
|
||||
volatile uint8_t interval;
|
||||
volatile uint8_t interval_counter;
|
||||
volatile uint8_t data_id; // data0 or data1
|
||||
} endpoint_t;
|
||||
|
||||
typedef enum {
|
||||
EVENT_NONE,
|
||||
EVENT_CONNECT,
|
||||
EVENT_DISCONNECT,
|
||||
} usb_device_event_t;
|
||||
|
||||
typedef struct {
|
||||
volatile bool connected;
|
||||
volatile bool enumerated;
|
||||
volatile usb_device_event_t event;
|
||||
volatile uint8_t address;
|
||||
volatile uint16_t vid;
|
||||
volatile uint16_t pid;
|
||||
control_pipe_t control_pipe;
|
||||
endpoint_t endpoint[PIO_USB_EP_CNT];
|
||||
} usb_device_t;
|
||||
|
||||
enum {
|
||||
USB_SYNC = 0x80,
|
||||
USB_PID_OUT = 0xe1,
|
||||
USB_PID_IN = 0x69,
|
||||
USB_PID_SOF = 0xa5,
|
||||
USB_PID_SETUP = 0x2d,
|
||||
USB_PID_DATA0 = 0xc3,
|
||||
USB_PID_DATA1 = 0x4b,
|
||||
USB_PID_ACK = 0xd2,
|
||||
USB_PID_NAK = 0x5a,
|
||||
USB_PID_STALL = 0x1e,
|
||||
USB_CRC16_PLACE = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
DESC_TYPE_STRING = 0x03,
|
||||
DESC_TYPE_INTERFACE = 0x04,
|
||||
DESC_TYPE_ENDPOINT = 0x05,
|
||||
DESC_TYPE_HID = 0x21,
|
||||
};
|
||||
|
||||
enum {
|
||||
CLASS_HID = 0x03,
|
||||
};
|
||||
|
||||
enum {
|
||||
EP_ATTR_INTERRUPT = 0x03,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t sync;
|
||||
uint8_t pid;
|
||||
uint8_t request_type;
|
||||
uint8_t request;
|
||||
uint8_t value_lsb;
|
||||
uint8_t value_msb;
|
||||
uint8_t index_lsb;
|
||||
uint8_t index_msb;
|
||||
uint8_t length_lsb;
|
||||
uint8_t length_msb;
|
||||
uint8_t crc16[2];
|
||||
} usb_setup_packet_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t length;
|
||||
uint8_t type;
|
||||
uint8_t bcd_usb[2];
|
||||
uint8_t class;
|
||||
uint8_t subclass;
|
||||
uint8_t protocol;
|
||||
uint8_t max_packet_size;
|
||||
uint8_t vid[2];
|
||||
uint8_t pid[2];
|
||||
uint8_t bcd_device[2];
|
||||
uint8_t manufacture;
|
||||
uint8_t product;
|
||||
uint8_t serial;
|
||||
uint8_t num_configu;
|
||||
} device_descriptor_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t length;
|
||||
uint8_t type;
|
||||
uint8_t string;
|
||||
} string_descriptor_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t length;
|
||||
uint8_t type;
|
||||
uint8_t inum;
|
||||
uint8_t altsetting;
|
||||
uint8_t numep;
|
||||
uint8_t iclass;
|
||||
uint8_t isubclass;
|
||||
uint8_t iprotocol;
|
||||
uint8_t iface;
|
||||
} interface_descriptor_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t length;
|
||||
uint8_t type;
|
||||
uint8_t epaddr;
|
||||
uint8_t attr;
|
||||
uint8_t max_size[2];
|
||||
uint8_t interval;
|
||||
} endpoint_descriptor_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t length;
|
||||
uint8_t type;
|
||||
uint8_t bcd_hid[2];
|
||||
uint8_t contry_code;
|
||||
uint8_t num_desc;
|
||||
uint8_t desc_type;
|
||||
uint8_t desc_len[2];
|
||||
} hid_descriptor_t;
|
||||
|
||||
typedef struct configuration_descriptor_tag {
|
||||
uint8_t length;
|
||||
uint8_t type;
|
||||
uint8_t total_length_lsb;
|
||||
uint8_t total_length_msb;
|
||||
uint8_t num_interfaces;
|
||||
uint8_t configuration_value;
|
||||
uint8_t configuration;
|
||||
uint8_t attributes;
|
||||
uint8_t max_power;
|
||||
} configuration_descriptor_t;
|
||||
|
||||
#define GET_DEVICE_DESCRIPTOR_REQ_DEFAULT \
|
||||
{ \
|
||||
USB_SYNC, USB_PID_DATA0, 0x80, 0x06, 0, 0x01, 0, 0, 0x12, 0, { \
|
||||
USB_CRC16_PLACE, USB_CRC16_PLACE \
|
||||
} \
|
||||
}
|
||||
#define GET_CONFIGURATION_DESCRIPTOR_REQ_DEFAULT \
|
||||
{ \
|
||||
USB_SYNC, USB_PID_DATA0, 0x80, 0x06, 0, 0x02, 0, 0, 0x09, 0, { \
|
||||
USB_CRC16_PLACE, USB_CRC16_PLACE \
|
||||
} \
|
||||
}
|
||||
#define SET_CONFIGURATION_REQ_DEFAULT \
|
||||
{ \
|
||||
USB_SYNC, USB_PID_DATA0, 0x00, 0x09, 0, 0, 0, 0, 0, 0, { \
|
||||
USB_CRC16_PLACE, USB_CRC16_PLACE \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SET_HID_IDLE_REQ_DEFAULT \
|
||||
{ \
|
||||
USB_SYNC, USB_PID_DATA0, 0x21, 0x0A, 0, 0, 0, 0, 0, 0, { \
|
||||
USB_CRC16_PLACE, USB_CRC16_PLACE \
|
||||
} \
|
||||
}
|
||||
#define GET_HID_REPORT_DESCRIPTOR_DEFAULT \
|
||||
{ \
|
||||
USB_SYNC, USB_PID_DATA0, 0x81, 0x06, 0, 0x22, 0, 0, 0xff, 0, { \
|
||||
USB_CRC16_PLACE, USB_CRC16_PLACE \
|
||||
} \
|
||||
}
|
||||
#define SET_ADDRESS_REQ_DEFAULT \
|
||||
{ \
|
||||
USB_SYNC, USB_PID_DATA0, 0, 0x5, 0x02, 0, 0, 0, 0, 0, { \
|
||||
USB_CRC16_PLACE, USB_CRC16_PLACE \
|
||||
} \
|
||||
}
|
||||
211
usb_rx.pio
Normal file
211
usb_rx.pio
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
|
||||
; Copyright (c) 2021 sekigon-gonnoc
|
||||
; USB FS NRZI receiver
|
||||
; Run 48 MHz, autopull
|
||||
|
||||
.define public IRQ_RX_BS_ERR 1 ; bit stuffinc error
|
||||
.define public IRQ_RX_EOP 2 ; eop detect flag
|
||||
|
||||
.define J 0b01
|
||||
.define K 0b10
|
||||
.define SE0 0b00
|
||||
.define BR 5 ; bit repeat counter
|
||||
|
||||
.define db0 0
|
||||
.define db1 1
|
||||
|
||||
; USB FS receive program run at 96MHz
|
||||
; shift to left, autopull enable
|
||||
; pin0: dp, pin1: dm
|
||||
; bit is captured from only dp. dm is used to detect eop
|
||||
.program usb_rx
|
||||
|
||||
.wrap_target
|
||||
start:
|
||||
set y, BR
|
||||
wait 0 pin 0
|
||||
|
||||
read1:
|
||||
jmp PIN J1
|
||||
K1:
|
||||
set x, 0 [1]
|
||||
in x, 1 [1]
|
||||
set y, BR
|
||||
jmp read2 [1]
|
||||
J1:
|
||||
set x, 1 [2]
|
||||
in x, 1
|
||||
jmp y-- read1 [2]
|
||||
set y, BR
|
||||
jmp PIN bs_err
|
||||
jmp read2 [5] ; ignore bitstuff
|
||||
|
||||
read2:
|
||||
jmp PIN J2
|
||||
K2:
|
||||
set x, 1 [3]
|
||||
in x, 1 [1]
|
||||
jmp y-- read2
|
||||
set y, BR
|
||||
jmp read1 [6] ; ignore bitstuff
|
||||
J2:
|
||||
set x, 0 [3]
|
||||
in x, 1
|
||||
set y, BR
|
||||
jmp read1
|
||||
eop:
|
||||
bs_err:
|
||||
irq wait IRQ_RX_BS_ERR
|
||||
.wrap
|
||||
|
||||
|
||||
; USB receive program run at 96MHz with debug pin
|
||||
; shift to left, autopull enable
|
||||
; pin0: dp, pin1: dm
|
||||
; bit is captured from only dp. dm is used to detect eop
|
||||
.program usb_rx_debug
|
||||
.side_set 1
|
||||
|
||||
.wrap_target
|
||||
start:
|
||||
set y, BR side db0
|
||||
wait 0 pin 0 side db0
|
||||
|
||||
read1:
|
||||
jmp PIN J1 side db1
|
||||
K1:
|
||||
;mov x, pins side db1 ; "miv x, pins" does not work as intend
|
||||
;jmp !x eop side db1; SE0
|
||||
|
||||
set x, 0 [1] side db0
|
||||
in x, 1 [1] side db1
|
||||
set y, BR side db1
|
||||
jmp read2 [1] side db1
|
||||
J1:
|
||||
set x, 1 [2] side db0
|
||||
in x, 1 side db1
|
||||
jmp y-- read1 [2] side db1
|
||||
set y, BR side db1
|
||||
jmp PIN bs_err side db1
|
||||
jmp read2 [5] side db1; ignore bitstuff
|
||||
|
||||
read2:
|
||||
jmp PIN J2 side db1
|
||||
K2:
|
||||
; mov x, pins side db1 ; "miv x, pins" does not work as intend
|
||||
; jmp !x eop side db1; SE0
|
||||
|
||||
set x, 1 [3] side db0
|
||||
in x, 1 [1] side db1
|
||||
jmp y-- read2 side db1
|
||||
set y, BR side db1
|
||||
jmp read1 [6] side db1 ; ignore bitstuff
|
||||
J2:
|
||||
set x, 0 [3] side db0
|
||||
in x, 1 side db1
|
||||
set y, BR side db1
|
||||
jmp read1 side db1
|
||||
eop:
|
||||
bs_err:
|
||||
irq wait IRQ_RX_BS_ERR side db0
|
||||
.wrap
|
||||
|
||||
; EOP detect program
|
||||
; run at 96MHz
|
||||
; autopull disable
|
||||
.program eop_detect
|
||||
|
||||
.wrap_target
|
||||
start:
|
||||
set y, 1
|
||||
mov isr, null
|
||||
wait_se0:
|
||||
in pins, 2 ; read dp/dm
|
||||
mov x, isr
|
||||
jmp x-- start ; check se0
|
||||
jmp y-- wait_se0
|
||||
irq wait IRQ_RX_EOP ; eop is detected
|
||||
.wrap
|
||||
|
||||
; EOP detect program with debug out
|
||||
; run at 96MHz
|
||||
; autopull disable
|
||||
.program eop_detect_debug
|
||||
.side_set 1
|
||||
|
||||
.wrap_target
|
||||
start:
|
||||
set y, 1 side db0;
|
||||
mov isr, null side db0
|
||||
wait_se0:
|
||||
in pins, 2 side db0 ; read dp/dm
|
||||
mov x, isr side db0
|
||||
jmp x-- start side db0 ; check se0
|
||||
jmp y-- wait_se0 side db0 ;
|
||||
irq wait IRQ_RX_EOP side db1 ; eop is detected
|
||||
.wrap
|
||||
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
static inline void usb_rx_program_init(PIO pio, uint sm, uint offset, uint pin_dp, int pin_debug) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin_dp, 2, false);
|
||||
gpio_pull_down(pin_dp);
|
||||
gpio_pull_down(pin_dp + 1); // dm
|
||||
|
||||
pio_sm_config c;
|
||||
|
||||
if (pin_debug < 0) {
|
||||
c = usb_rx_program_get_default_config(offset);
|
||||
} else {
|
||||
c = usb_rx_debug_program_get_default_config(offset);
|
||||
|
||||
pio_sm_set_pins_with_mask(pio, sm, 0, 1 << pin_debug);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, 1 << pin_debug, 1 << pin_debug);
|
||||
pio_gpio_init(pio, pin_debug);
|
||||
sm_config_set_sideset_pins(&c, pin_debug);
|
||||
}
|
||||
|
||||
sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN
|
||||
sm_config_set_jmp_pin(&c, pin_dp); // for JMP
|
||||
|
||||
// Shift to right, autopull enabled, 8bit
|
||||
sm_config_set_in_shift(&c, true, true, 8);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
|
||||
// Run at 96Mhz
|
||||
// system clock should be multiple of 12MHz
|
||||
float div = (float)clock_get_hz(clk_sys) / (96000000);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, false);
|
||||
}
|
||||
|
||||
static inline void eop_detect_program_init(PIO pio, uint sm, uint offset,
|
||||
uint pin_dp, int pin_debug) {
|
||||
pio_sm_config c;
|
||||
|
||||
if (pin_debug < 0) {
|
||||
c = eop_detect_program_get_default_config(offset);
|
||||
} else {
|
||||
c = eop_detect_debug_program_get_default_config(offset);
|
||||
pio_sm_set_pins_with_mask(pio, sm, 0, 1 << pin_debug);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, 1 << pin_debug, 1 << pin_debug);
|
||||
pio_gpio_init(pio, pin_debug);
|
||||
sm_config_set_sideset_pins(&c, pin_debug);
|
||||
}
|
||||
|
||||
sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN
|
||||
sm_config_set_jmp_pin(&c, pin_dp); // for JMP
|
||||
|
||||
sm_config_set_in_shift(&c, false, false, 8);
|
||||
float div = (float)clock_get_hz(clk_sys) / (96000000);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
%}
|
||||
85
usb_tx.pio
Normal file
85
usb_tx.pio
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
; Copyright (c) 2021 sekigon-gonnoc
|
||||
; USB FS NRZI transmitter
|
||||
; Run 48 MHz, autopull
|
||||
|
||||
.program usb_tx
|
||||
.side_set 2 opt
|
||||
|
||||
.define J 0b01
|
||||
.define K 0b10
|
||||
.define SE0 0b00
|
||||
.define BR 5 ; bit repeat limit
|
||||
.define public IRQ_COMP 0 ; complete flag bit
|
||||
|
||||
start:
|
||||
set y, BR side J
|
||||
set pindirs, 0b11
|
||||
|
||||
.wrap_target
|
||||
check_eop1:
|
||||
jmp !osre load_bit1
|
||||
nop [1]
|
||||
send_eop:
|
||||
nop side SE0 [3]
|
||||
nop [3]
|
||||
nop side J
|
||||
set pindirs, 0b00
|
||||
irq wait IRQ_COMP
|
||||
jmp start
|
||||
load_bit1:
|
||||
out x, 1
|
||||
jmp !x low1
|
||||
high1:
|
||||
jmp y-- check_eop1 side J
|
||||
nop [2] ; bit stuffing
|
||||
low1:
|
||||
set y BR side K
|
||||
|
||||
check_eop2:
|
||||
jmp !osre load_bit2
|
||||
jmp send_eop [1]
|
||||
load_bit2:
|
||||
out x, 1
|
||||
jmp !x low2
|
||||
high2:
|
||||
jmp y-- check_eop2 side K
|
||||
nop [2] ; bit stuffing
|
||||
low2:
|
||||
set y BR side J
|
||||
.wrap
|
||||
|
||||
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
static inline void usb_tx_program_init(PIO pio, uint sm, uint offset,
|
||||
uint pin_dp) {
|
||||
pio_sm_set_pins_with_mask(pio, sm, (0b01 << pin_dp), (0b11 << pin_dp));
|
||||
|
||||
gpio_pull_down(pin_dp);
|
||||
gpio_pull_down(pin_dp + 1); // dm
|
||||
pio_gpio_init(pio, pin_dp);
|
||||
pio_gpio_init(pio, pin_dp + 1); // dm
|
||||
|
||||
pio_sm_config c = usb_tx_program_get_default_config(offset);
|
||||
|
||||
// shifts to left, autopull, 8bit
|
||||
sm_config_set_out_shift(&c, true, true, 8);
|
||||
|
||||
sm_config_set_out_pins(&c, pin_dp, 2);
|
||||
sm_config_set_set_pins(&c, pin_dp, 2);
|
||||
sm_config_set_sideset_pins(&c, pin_dp);
|
||||
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
// run at 48MHz
|
||||
// clk_sys should be multiply of 12MHz
|
||||
float div = (float)clock_get_hz(clk_sys) / (48000000UL);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
%}
|
||||
Loading…
Reference in a new issue