[pico-stuff] initial upload
This commit is contained in:
parent
a24a4dff09
commit
b473b13985
5
pico-stuff/.clang-format
Normal file
5
pico-stuff/.clang-format
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
UseTab: Always
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
24
pico-stuff/CMakeLists.txt
Normal file
24
pico-stuff/CMakeLists.txt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
project(sandbox)
|
||||||
|
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
add_executable(test)
|
||||||
|
|
||||||
|
target_sources(test PUBLIC
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/test.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make sure TinyUSB can find tusb_config.h
|
||||||
|
target_include_directories(test PUBLIC
|
||||||
|
${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
pico_enable_stdio_usb(test 1)
|
||||||
|
|
||||||
|
target_link_libraries(test PUBLIC pico_stdlib tinyusb_device pico_unique_id)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(test)
|
9
pico-stuff/README.md
Normal file
9
pico-stuff/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# raspberry pi pico playground
|
||||||
|
|
||||||
|
- `morse.c`: morse blinker (with a shell over serial to send messages to blink)
|
||||||
|
- `monstrosity.c`: driver code for random components i had laying around
|
||||||
|
- `test.c`: morse code HID keyboard (needs two buttons). can use vim with it
|
||||||
|
- `tablegen.py`: helper to generate keymaps for `test.c` morse
|
||||||
|
|
||||||
|
you will need to put the raspberry pi pico sdk somewhere and export `PICO_SDK_PATH` to point to it.
|
||||||
|
also, it could be useful to export `CMAKE_EXPORT_COMPILE_COMMANDS=1` to get a `compile_commands.json`.
|
154
pico-stuff/monstrosity.c
Normal file
154
pico-stuff/monstrosity.c
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "pico/stdio.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "hardware/irq.h"
|
||||||
|
#include "hardware/pwm.h"
|
||||||
|
|
||||||
|
const uint BOARD_LED_PIN = PICO_DEFAULT_LED_PIN;
|
||||||
|
const uint LED_PIN = 15;
|
||||||
|
const uint LED2_PIN = 17;
|
||||||
|
const uint BUTTON_PIN = 9;
|
||||||
|
const uint ROT_CLK = 11;
|
||||||
|
const uint ROT_DT = 10;
|
||||||
|
|
||||||
|
|
||||||
|
const uint DOT_PERIOD_MS = 100;
|
||||||
|
|
||||||
|
const char *morse_letters[] = {
|
||||||
|
".-", // A
|
||||||
|
"-...", // B
|
||||||
|
"-.-.", // C
|
||||||
|
"-..", // D
|
||||||
|
".", // E
|
||||||
|
"..-.", // F
|
||||||
|
"--.", // G
|
||||||
|
"....", // H
|
||||||
|
"..", // I
|
||||||
|
".---", // J
|
||||||
|
"-.-", // K
|
||||||
|
".-..", // L
|
||||||
|
"--", // M
|
||||||
|
"-.", // N
|
||||||
|
"---", // O
|
||||||
|
".--.", // P
|
||||||
|
"--.-", // Q
|
||||||
|
".-.", // R
|
||||||
|
"...", // S
|
||||||
|
"-", // T
|
||||||
|
"..-", // U
|
||||||
|
"...-", // V
|
||||||
|
".--", // W
|
||||||
|
"-..-", // X
|
||||||
|
"-.--", // Y
|
||||||
|
"--.." // Z
|
||||||
|
};
|
||||||
|
|
||||||
|
void put_morse_letter(uint led_pin, const char *pattern) {
|
||||||
|
for (; *pattern; ++pattern) {
|
||||||
|
gpio_put(led_pin, 1);
|
||||||
|
if (*pattern == '.')
|
||||||
|
sleep_ms(DOT_PERIOD_MS);
|
||||||
|
else
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 3);
|
||||||
|
gpio_put(led_pin, 0);
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 1);
|
||||||
|
}
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void put_morse_str(uint led_pin, char *str) {
|
||||||
|
for (; *str; ++str) {
|
||||||
|
if (*str >= 'A' && *str <= 'Z') {
|
||||||
|
put_morse_letter(led_pin, morse_letters[*str - 'A']);
|
||||||
|
} else if (*str >= 'a' && *str <= 'z') {
|
||||||
|
put_morse_letter(led_pin, morse_letters[*str - 'a']);
|
||||||
|
} else if (*str == ' ') {
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rotCt = 0;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
#ifndef PICO_DEFAULT_LED_PIN
|
||||||
|
#warning picoboard/blinky example requires a board with a regular LED
|
||||||
|
#else
|
||||||
|
gpio_set_function(LED2_PIN, GPIO_FUNC_PWM);
|
||||||
|
uint slice_num = pwm_gpio_to_slice_num(LED2_PIN);
|
||||||
|
pwm_config config = pwm_get_default_config();
|
||||||
|
pwm_config_set_clkdiv(&config, 4.f);
|
||||||
|
pwm_init(slice_num, &config, true);
|
||||||
|
|
||||||
|
|
||||||
|
gpio_init(ROT_CLK);
|
||||||
|
gpio_init(ROT_DT);
|
||||||
|
gpio_set_dir(ROT_CLK, GPIO_IN);
|
||||||
|
gpio_set_dir(ROT_DT, GPIO_IN);
|
||||||
|
|
||||||
|
gpio_init(BOARD_LED_PIN);
|
||||||
|
gpio_set_dir(BOARD_LED_PIN, GPIO_OUT);
|
||||||
|
|
||||||
|
gpio_init(LED_PIN);
|
||||||
|
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||||
|
|
||||||
|
gpio_init(BUTTON_PIN);
|
||||||
|
gpio_set_dir(BUTTON_PIN, GPIO_IN);
|
||||||
|
|
||||||
|
int rotClkState = gpio_get(ROT_CLK);
|
||||||
|
int lastClkState = rotClkState;
|
||||||
|
|
||||||
|
const int bufSz = 50;
|
||||||
|
char buf[bufSz];
|
||||||
|
int bufIdx = 0;
|
||||||
|
|
||||||
|
put_morse_str(LED_PIN, "hi");
|
||||||
|
put_morse_str(BOARD_LED_PIN, "fox");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
unsigned int c = getchar_timeout_us(20);
|
||||||
|
if (c != PICO_ERROR_TIMEOUT) {
|
||||||
|
printf("%c", c);
|
||||||
|
if (c == 0x0d) {
|
||||||
|
buf[bufIdx] = '\0';
|
||||||
|
printf("\n");
|
||||||
|
put_morse_str(LED_PIN, buf);
|
||||||
|
printf(">>> ");
|
||||||
|
bufIdx = 0;
|
||||||
|
} else if (bufIdx < bufSz-1) {
|
||||||
|
buf[bufIdx++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gpio_get(BUTTON_PIN)) {
|
||||||
|
gpio_put(BOARD_LED_PIN, 1);
|
||||||
|
} else {
|
||||||
|
gpio_put(BOARD_LED_PIN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
rotClkState = gpio_get(ROT_CLK);
|
||||||
|
if (lastClkState != rotClkState && rotClkState == 1) {
|
||||||
|
if (gpio_get(ROT_DT) != rotClkState) {
|
||||||
|
rotCt -= 10;
|
||||||
|
} else {
|
||||||
|
rotCt += 10;
|
||||||
|
}
|
||||||
|
if (rotCt >= 256) {
|
||||||
|
rotCt = 255;
|
||||||
|
} else if (rotCt < 0) {
|
||||||
|
rotCt = 0;
|
||||||
|
}
|
||||||
|
printf("%d\n", rotCt);
|
||||||
|
pwm_set_gpio_level(LED2_PIN, rotCt*rotCt);
|
||||||
|
}
|
||||||
|
lastClkState = rotClkState;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
100
pico-stuff/morse.c
Normal file
100
pico-stuff/morse.c
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "pico/stdio.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
|
||||||
|
const uint DOT_PERIOD_MS = 100;
|
||||||
|
|
||||||
|
const char *morse_letters[] = {
|
||||||
|
".-", // A
|
||||||
|
"-...", // B
|
||||||
|
"-.-.", // C
|
||||||
|
"-..", // D
|
||||||
|
".", // E
|
||||||
|
"..-.", // F
|
||||||
|
"--.", // G
|
||||||
|
"....", // H
|
||||||
|
"..", // I
|
||||||
|
".---", // J
|
||||||
|
"-.-", // K
|
||||||
|
".-..", // L
|
||||||
|
"--", // M
|
||||||
|
"-.", // N
|
||||||
|
"---", // O
|
||||||
|
".--.", // P
|
||||||
|
"--.-", // Q
|
||||||
|
".-.", // R
|
||||||
|
"...", // S
|
||||||
|
"-", // T
|
||||||
|
"..-", // U
|
||||||
|
"...-", // V
|
||||||
|
".--", // W
|
||||||
|
"-..-", // X
|
||||||
|
"-.--", // Y
|
||||||
|
"--.." // Z
|
||||||
|
};
|
||||||
|
|
||||||
|
void put_morse_letter(uint led_pin, const char *pattern) {
|
||||||
|
for (; *pattern; ++pattern) {
|
||||||
|
gpio_put(led_pin, 1);
|
||||||
|
if (*pattern == '.')
|
||||||
|
sleep_ms(DOT_PERIOD_MS);
|
||||||
|
else
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 3);
|
||||||
|
gpio_put(led_pin, 0);
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 1);
|
||||||
|
}
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void put_morse_str(uint led_pin, char *str) {
|
||||||
|
for (; *str; ++str) {
|
||||||
|
if (*str >= 'A' && *str <= 'Z') {
|
||||||
|
put_morse_letter(led_pin, morse_letters[*str - 'A']);
|
||||||
|
} else if (*str >= 'a' && *str <= 'z') {
|
||||||
|
put_morse_letter(led_pin, morse_letters[*str - 'a']);
|
||||||
|
} else if (*str == ' ') {
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
#ifndef PICO_DEFAULT_LED_PIN
|
||||||
|
#warning picoboard/blinky example requires a board with a regular LED
|
||||||
|
#else
|
||||||
|
// const uint LED_PIN = PICO_DEFAULT_LED_PIN;
|
||||||
|
const uint LED_PIN = 15;
|
||||||
|
gpio_init(LED_PIN);
|
||||||
|
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||||
|
|
||||||
|
const int bufSz = 50;
|
||||||
|
char buf[bufSz];
|
||||||
|
int bufIdx = 0;
|
||||||
|
|
||||||
|
put_morse_str(LED_PIN, "quick brown");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
unsigned int c = getchar_timeout_us(20);
|
||||||
|
if (c != PICO_ERROR_TIMEOUT) {
|
||||||
|
printf("%c", c);
|
||||||
|
if (c == 0x0d) {
|
||||||
|
buf[bufIdx] = '\0';
|
||||||
|
printf("\n");
|
||||||
|
put_morse_str(LED_PIN, buf);
|
||||||
|
printf(">>> ");
|
||||||
|
bufIdx = 0;
|
||||||
|
} else if (bufIdx < bufSz-1) {
|
||||||
|
buf[bufIdx++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
59
pico-stuff/pico_extras_import_optional.cmake
Normal file
59
pico-stuff/pico_extras_import_optional.cmake
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# This is a copy of <PICO_EXTRAS_PATH>/external/pico_extras_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate pico-extras
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH))
|
||||||
|
set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH})
|
||||||
|
message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT))
|
||||||
|
set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT PICO_EXTRAS_PATH)
|
||||||
|
if (PICO_EXTRAS_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_EXTRAS_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_extras
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-extras
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_extras)
|
||||||
|
message("Downloading Raspberry Pi Pico Extras")
|
||||||
|
FetchContent_Populate(pico_extras)
|
||||||
|
set(PICO_EXTRAS_PATH ${pico_extras_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras")
|
||||||
|
set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras)
|
||||||
|
message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}")
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (PICO_EXTRAS_PATH)
|
||||||
|
set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS")
|
||||||
|
set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable")
|
||||||
|
set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS")
|
||||||
|
|
||||||
|
get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_EXTRAS_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE)
|
||||||
|
add_subdirectory(${PICO_EXTRAS_PATH} pico_extras)
|
||||||
|
endif()
|
73
pico-stuff/pico_sdk_import.cmake
Normal file
73
pico-stuff/pico_sdk_import.cmake
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# 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 ()
|
||||||
|
# GIT_SUBMODULES_RECURSE was added in 3.17
|
||||||
|
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
GIT_SUBMODULES_RECURSE FALSE
|
||||||
|
)
|
||||||
|
else ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
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})
|
64
pico-stuff/tablegen.py
Normal file
64
pico-stuff/tablegen.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# generate a flat binary tree for morse decoding
|
||||||
|
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/71099952
|
||||||
|
encode_table = {
|
||||||
|
"A": ".-",
|
||||||
|
"B": "-...",
|
||||||
|
"C": "-.-.",
|
||||||
|
"D": "-..",
|
||||||
|
"E": ".",
|
||||||
|
"F": "..-.",
|
||||||
|
"G": "--.",
|
||||||
|
"H": "....",
|
||||||
|
"I": "..",
|
||||||
|
"J": ".---",
|
||||||
|
"K": "-.-",
|
||||||
|
"L": ".-..",
|
||||||
|
"M": "--",
|
||||||
|
"N": "-.",
|
||||||
|
"O": "---",
|
||||||
|
"P": ".--.",
|
||||||
|
"Q": "--.-",
|
||||||
|
"R": ".-.",
|
||||||
|
"S": "...",
|
||||||
|
"T": "-",
|
||||||
|
"U": "..-",
|
||||||
|
"V": "...-",
|
||||||
|
"W": ".--",
|
||||||
|
"X": "-..-",
|
||||||
|
"Y": "-.--",
|
||||||
|
"Z": "--..",
|
||||||
|
"0": "-----",
|
||||||
|
"1": ".----",
|
||||||
|
"2": "..---",
|
||||||
|
"3": "...--",
|
||||||
|
"4": "....-",
|
||||||
|
"5": ".....",
|
||||||
|
"6": "-....",
|
||||||
|
"7": "--...",
|
||||||
|
"8": "---..",
|
||||||
|
"9": "----.",
|
||||||
|
".": ".-.-.-",
|
||||||
|
",": "--..--",
|
||||||
|
"?": "..--..",
|
||||||
|
"\\n": "-..-..",
|
||||||
|
"\\b": ".-.-",
|
||||||
|
"\\e": "..--",
|
||||||
|
" ": "----",
|
||||||
|
}
|
||||||
|
|
||||||
|
maxLen = reduce(lambda x, y: max(x, y), [len(code) for code in encode_table.values()])
|
||||||
|
|
||||||
|
letters = ['*'] + ['*'] * 2**(maxLen + 1)
|
||||||
|
|
||||||
|
for sym, code in encode_table.items():
|
||||||
|
idx = 1
|
||||||
|
for lett in code:
|
||||||
|
idx *= 2
|
||||||
|
if lett == '-':
|
||||||
|
idx += 1;
|
||||||
|
letters[idx] = sym
|
||||||
|
|
||||||
|
print("".join(letters))
|
324
pico-stuff/test.c
Normal file
324
pico-stuff/test.c
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "pico/stdio.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "tusb.h"
|
||||||
|
#include "usb_descriptors.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define max(X, Y) ((X) > (Y) ? (X) : (Y))
|
||||||
|
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
|
||||||
|
|
||||||
|
const uint DOT_PERIOD_MS = 100;
|
||||||
|
// wait this time before registering a morse letter
|
||||||
|
const uint MORSE_WAIT_MS = DOT_PERIOD_MS * 3;
|
||||||
|
|
||||||
|
const char *morse_letters[] = {
|
||||||
|
".-", // A
|
||||||
|
"-...", // B
|
||||||
|
"-.-.", // C
|
||||||
|
"-..", // D
|
||||||
|
".", // E
|
||||||
|
"..-.", // F
|
||||||
|
"--.", // G
|
||||||
|
"....", // H
|
||||||
|
"..", // I
|
||||||
|
".---", // J
|
||||||
|
"-.-", // K
|
||||||
|
".-..", // L
|
||||||
|
"--", // M
|
||||||
|
"-.", // N
|
||||||
|
"---", // O
|
||||||
|
".--.", // P
|
||||||
|
"--.-", // Q
|
||||||
|
".-.", // R
|
||||||
|
"...", // S
|
||||||
|
"-", // T
|
||||||
|
"..-", // U
|
||||||
|
"...-", // V
|
||||||
|
".--", // W
|
||||||
|
"-..-", // X
|
||||||
|
"-.--", // Y
|
||||||
|
"--.." // Z
|
||||||
|
};
|
||||||
|
|
||||||
|
void put_morse_letter(uint led_pin, const char *pattern) {
|
||||||
|
for (; *pattern; ++pattern) {
|
||||||
|
gpio_put(led_pin, 1);
|
||||||
|
if (*pattern == '.')
|
||||||
|
sleep_ms(DOT_PERIOD_MS);
|
||||||
|
else
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 3);
|
||||||
|
gpio_put(led_pin, 0);
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 1);
|
||||||
|
}
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void put_morse_str(uint led_pin, char *str) {
|
||||||
|
for (; *str; ++str) {
|
||||||
|
if (*str >= 'A' && *str <= 'Z') {
|
||||||
|
put_morse_letter(led_pin, morse_letters[*str - 'A']);
|
||||||
|
} else if (*str >= 'a' && *str <= 'z') {
|
||||||
|
put_morse_letter(led_pin, morse_letters[*str - 'a']);
|
||||||
|
} else if (*str == ' ') {
|
||||||
|
sleep_ms(DOT_PERIOD_MS * 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodes a letter
|
||||||
|
char morseDecode(char *code) {
|
||||||
|
// flat binary tree (see gentable.py)
|
||||||
|
const char morseTable[] =
|
||||||
|
"**ETIANMSURWDKGOHVF\eL\bPJBXCYZQ* "
|
||||||
|
"54*3***2*******16*******7***8*90************?********.**************"
|
||||||
|
"\n**************,*************";
|
||||||
|
|
||||||
|
int tableIdx = 1;
|
||||||
|
|
||||||
|
for (; *code && tableIdx < sizeof(morseTable) / sizeof(char) - 1; code++) {
|
||||||
|
tableIdx *= 2;
|
||||||
|
if (*code == '-') {
|
||||||
|
tableIdx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return morseTable[tableIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint DEBOUNCE_CHECK_MS = 5;
|
||||||
|
const uint DEBOUNCE_PRESS_MS = 10;
|
||||||
|
const uint DEBOUNCE_RELEASE_MS = 50;
|
||||||
|
|
||||||
|
typedef struct DebounceState {
|
||||||
|
bool pressed;
|
||||||
|
bool changed;
|
||||||
|
|
||||||
|
// ms timestamp of last change
|
||||||
|
uint64_t lastChange;
|
||||||
|
// ms since last change
|
||||||
|
uint64_t sinceChange;
|
||||||
|
// ms timestamp of last check
|
||||||
|
uint64_t lastCheck;
|
||||||
|
// countdown timer
|
||||||
|
uint count;
|
||||||
|
} DebounceState;
|
||||||
|
|
||||||
|
// call this in the main loop
|
||||||
|
void debounce_get(bool rawState, DebounceState *state) {
|
||||||
|
uint time_ms = time_us_64() / 1000;
|
||||||
|
|
||||||
|
if (state->lastChange == 0)
|
||||||
|
state->lastChange = time_ms;
|
||||||
|
state->sinceChange = time_ms - state->lastChange;
|
||||||
|
|
||||||
|
state->changed = false;
|
||||||
|
|
||||||
|
if (time_ms - state->lastCheck < DEBOUNCE_CHECK_MS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->lastCheck = time_ms;
|
||||||
|
|
||||||
|
if (rawState == state->pressed) {
|
||||||
|
// set timer when state is stable
|
||||||
|
state->count = state->pressed ? DEBOUNCE_RELEASE_MS / DEBOUNCE_CHECK_MS
|
||||||
|
: DEBOUNCE_PRESS_MS / DEBOUNCE_CHECK_MS;
|
||||||
|
} else {
|
||||||
|
// process change
|
||||||
|
if (--state->count == 0) {
|
||||||
|
// timer expires
|
||||||
|
state->pressed = rawState;
|
||||||
|
state->changed = true;
|
||||||
|
state->lastChange = time_ms;
|
||||||
|
state->count = state->pressed
|
||||||
|
? DEBOUNCE_RELEASE_MS / DEBOUNCE_CHECK_MS
|
||||||
|
: DEBOUNCE_PRESS_MS / DEBOUNCE_CHECK_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_hid_report(uint8_t report_id, char key) {
|
||||||
|
if (!tud_ready())
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (report_id) {
|
||||||
|
case REPORT_ID_KEYBOARD: {
|
||||||
|
// counter for how many null reports to send
|
||||||
|
// idk why sending less makes it register as a continuous keypress
|
||||||
|
// sometimes
|
||||||
|
const uint NULL_REPORTS = 15;
|
||||||
|
static uint nullCounter = NULL_REPORTS;
|
||||||
|
if (key) {
|
||||||
|
uint8_t keycode[6] = {0};
|
||||||
|
if ('A' <= key && 'Z' >= key) {
|
||||||
|
keycode[0] = HID_KEY_A + (key - 'A');
|
||||||
|
} else if ('1' <= key && key <= '9') {
|
||||||
|
keycode[0] = HID_KEY_1 + (key - '1');
|
||||||
|
} else {
|
||||||
|
switch (key) {
|
||||||
|
case '0':
|
||||||
|
// 0 is after rather than before like in ASCII so special
|
||||||
|
// treatment here
|
||||||
|
keycode[0] = HID_KEY_0;
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
keycode[0] = HID_KEY_ENTER;
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
keycode[0] = HID_KEY_BACKSPACE;
|
||||||
|
break;
|
||||||
|
case '\e':
|
||||||
|
keycode[0] = HID_KEY_ESCAPE;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
keycode[0] = HID_KEY_SPACE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
|
||||||
|
nullCounter = NULL_REPORTS;
|
||||||
|
} else {
|
||||||
|
if (nullCounter > 0) {
|
||||||
|
nullCounter--;
|
||||||
|
// this print statement adds a necessary delay for the null reports
|
||||||
|
printf("sent null %d\n", nullCounter);
|
||||||
|
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when sent REPORT successfully to host
|
||||||
|
// Application can use this to send the next report
|
||||||
|
// Note: For composite reports, report[0] is report ID
|
||||||
|
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const *report,
|
||||||
|
uint16_t len) {
|
||||||
|
(void)instance;
|
||||||
|
(void)len;
|
||||||
|
|
||||||
|
uint8_t next_report_id = report[0] + 1;
|
||||||
|
|
||||||
|
if (next_report_id < REPORT_ID_COUNT) {
|
||||||
|
// send_hid_report(next_report_id, board_button_read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint PIN_DASH = 17;
|
||||||
|
const uint PIN_DOT = 16;
|
||||||
|
|
||||||
|
#define BUF_LEN 50
|
||||||
|
|
||||||
|
// return letter read
|
||||||
|
char morse_task(void) {
|
||||||
|
static DebounceState dotState = {0};
|
||||||
|
static DebounceState dashState = {0};
|
||||||
|
|
||||||
|
static char buf[BUF_LEN + 1];
|
||||||
|
buf[BUF_LEN] = '\0';
|
||||||
|
static uint bufIdx = 0;
|
||||||
|
|
||||||
|
debounce_get(!gpio_get(PIN_DOT), &dotState);
|
||||||
|
debounce_get(!gpio_get(PIN_DASH), &dashState);
|
||||||
|
|
||||||
|
uint64_t sincePress = min(dotState.sinceChange, dashState.sinceChange);
|
||||||
|
|
||||||
|
if (bufIdx < BUF_LEN) {
|
||||||
|
if (dotState.pressed && dotState.changed) {
|
||||||
|
buf[bufIdx++] = '.';
|
||||||
|
} else if (dashState.pressed && dashState.changed) {
|
||||||
|
buf[bufIdx++] = '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char c = '\0';
|
||||||
|
|
||||||
|
if (!dotState.pressed && !dashState.pressed) {
|
||||||
|
if (sincePress >= MORSE_WAIT_MS && bufIdx != 0) {
|
||||||
|
buf[bufIdx] = '\0';
|
||||||
|
c = morseDecode(buf);
|
||||||
|
printf("%c", c);
|
||||||
|
|
||||||
|
if (c == '\b') {
|
||||||
|
printf(" \b");
|
||||||
|
}
|
||||||
|
|
||||||
|
bufIdx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dotState.pressed || dashState.pressed) {
|
||||||
|
if (tud_suspended()) {
|
||||||
|
tud_remote_wakeup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when received SET_REPORT control request or
|
||||||
|
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
||||||
|
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id,
|
||||||
|
hid_report_type_t report_type, uint8_t const *buffer,
|
||||||
|
uint16_t bufsize) {
|
||||||
|
(void)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when received GET_REPORT control request
|
||||||
|
// Application must fill buffer report's content and return its length.
|
||||||
|
// Return zero will cause the stack to STALL request
|
||||||
|
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id,
|
||||||
|
hid_report_type_t report_type, uint8_t *buffer,
|
||||||
|
uint16_t reqlen) {
|
||||||
|
// TODO not Implemented
|
||||||
|
(void)instance;
|
||||||
|
(void)report_id;
|
||||||
|
(void)report_type;
|
||||||
|
(void)buffer;
|
||||||
|
(void)reqlen;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
stdio_init_all();
|
||||||
|
#ifndef PICO_DEFAULT_LED_PIN
|
||||||
|
#warning picoboard/blinky example requires a board with a regular LED
|
||||||
|
#else
|
||||||
|
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
|
||||||
|
gpio_init(LED_PIN);
|
||||||
|
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tusb_init();
|
||||||
|
|
||||||
|
put_morse_str(LED_PIN, "a");
|
||||||
|
|
||||||
|
gpio_init(PIN_DASH);
|
||||||
|
gpio_init(PIN_DOT);
|
||||||
|
gpio_set_dir(PIN_DASH, GPIO_IN);
|
||||||
|
gpio_pull_up(PIN_DASH);
|
||||||
|
gpio_set_dir(PIN_DOT, GPIO_IN);
|
||||||
|
gpio_pull_up(PIN_DOT);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
tud_task();
|
||||||
|
|
||||||
|
char c = morse_task();
|
||||||
|
send_hid_report(REPORT_ID_KEYBOARD, c);
|
||||||
|
}
|
||||||
|
}
|
118
pico-stuff/tusb_config.h
Normal file
118
pico-stuff/tusb_config.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_CONFIG_H_
|
||||||
|
#define _TUSB_CONFIG_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// COMMON CONFIGURATION
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
// defined by board.mk
|
||||||
|
#ifndef CFG_TUSB_MCU
|
||||||
|
#error CFG_TUSB_MCU must be defined
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||||
|
#ifndef BOARD_DEVICE_RHPORT_NUM
|
||||||
|
#define BOARD_DEVICE_RHPORT_NUM 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// RHPort max operational speed can defined by board.mk
|
||||||
|
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
|
||||||
|
#ifndef BOARD_DEVICE_RHPORT_SPEED
|
||||||
|
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
|
||||||
|
CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)
|
||||||
|
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
|
||||||
|
#else
|
||||||
|
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Device mode with rhport and speed defined by board.mk
|
||||||
|
#if BOARD_DEVICE_RHPORT_NUM == 0
|
||||||
|
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||||
|
#elif BOARD_DEVICE_RHPORT_NUM == 1
|
||||||
|
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||||
|
#else
|
||||||
|
#error "Incorrect RHPort configuration"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUSB_OS
|
||||||
|
#define CFG_TUSB_OS OPT_OS_NONE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||||
|
// #define CFG_TUSB_DEBUG 0
|
||||||
|
|
||||||
|
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||||
|
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||||
|
* into those specific section.
|
||||||
|
* e.g
|
||||||
|
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||||
|
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||||
|
*/
|
||||||
|
#ifndef CFG_TUSB_MEM_SECTION
|
||||||
|
#define CFG_TUSB_MEM_SECTION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUSB_MEM_ALIGN
|
||||||
|
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// DEVICE CONFIGURATION
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||||
|
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------- CLASS -------------//
|
||||||
|
#define CFG_TUD_HID 1
|
||||||
|
#define CFG_TUD_CDC 1
|
||||||
|
#define CFG_TUD_MSC 0
|
||||||
|
#define CFG_TUD_MIDI 0
|
||||||
|
#define CFG_TUD_VENDOR 0
|
||||||
|
|
||||||
|
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||||
|
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||||
|
|
||||||
|
// CDC FIFO size of TX and RX
|
||||||
|
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||||
|
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||||
|
|
||||||
|
// CDC Endpoint transfer buffer size, more is faster
|
||||||
|
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_CONFIG_H_ */
|
244
pico-stuff/usb_descriptors.c
Normal file
244
pico-stuff/usb_descriptors.c
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "pico/unique_id.h"
|
||||||
|
#include "tusb.h"
|
||||||
|
#include "usb_descriptors.h"
|
||||||
|
|
||||||
|
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||||
|
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||||
|
*
|
||||||
|
* Auto ProductID layout's Bitmap:
|
||||||
|
* [MSB] HID | MSC | CDC [LSB]
|
||||||
|
*/
|
||||||
|
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
|
||||||
|
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||||
|
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
|
||||||
|
|
||||||
|
#define USB_VID 0xCafe
|
||||||
|
#define USB_BCD 0x0200
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Device Descriptors
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
tusb_desc_device_t const desc_device =
|
||||||
|
{
|
||||||
|
.bLength = sizeof(tusb_desc_device_t),
|
||||||
|
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||||
|
.bcdUSB = USB_BCD,
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||||
|
|
||||||
|
.idVendor = USB_VID,
|
||||||
|
.idProduct = USB_PID,
|
||||||
|
.bcdDevice = 0x0100,
|
||||||
|
|
||||||
|
.iManufacturer = 0x01,
|
||||||
|
.iProduct = 0x02,
|
||||||
|
.iSerialNumber = 0x03,
|
||||||
|
|
||||||
|
.bNumConfigurations = 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invoked when received GET DEVICE DESCRIPTOR
|
||||||
|
// Application return pointer to descriptor
|
||||||
|
uint8_t const * tud_descriptor_device_cb(void)
|
||||||
|
{
|
||||||
|
return (uint8_t const *) &desc_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// HID Report Descriptor
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
uint8_t const desc_hid_report[] =
|
||||||
|
{
|
||||||
|
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD )),
|
||||||
|
TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(REPORT_ID_MOUSE )),
|
||||||
|
TUD_HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(REPORT_ID_CONSUMER_CONTROL )),
|
||||||
|
TUD_HID_REPORT_DESC_GAMEPAD ( HID_REPORT_ID(REPORT_ID_GAMEPAD ))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invoked when received GET HID REPORT DESCRIPTOR
|
||||||
|
// Application return pointer to descriptor
|
||||||
|
// Descriptor contents must exist long enough for transfer to complete
|
||||||
|
uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance)
|
||||||
|
{
|
||||||
|
(void) instance;
|
||||||
|
return desc_hid_report;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Configuration Descriptor
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ITF_NUM_HID,
|
||||||
|
ITF_NUM_CDC,
|
||||||
|
ITF_NUM_CDC_DATA,
|
||||||
|
ITF_NUM_TOTAL
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_CDC_DESC_LEN)
|
||||||
|
|
||||||
|
#define EPNUM_HID 0x84
|
||||||
|
|
||||||
|
#define EPNUM_CDC_NOTIF 0x81
|
||||||
|
#define EPNUM_CDC_OUT 0x02
|
||||||
|
#define EPNUM_CDC_IN 0x82
|
||||||
|
|
||||||
|
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, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||||
|
|
||||||
|
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||||
|
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 5),
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
};
|
||||||
|
|
||||||
|
#if TUD_OPT_HIGH_SPEED
|
||||||
|
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
|
||||||
|
|
||||||
|
// other speed configuration
|
||||||
|
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
|
||||||
|
|
||||||
|
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
|
||||||
|
tusb_desc_device_qualifier_t const desc_device_qualifier =
|
||||||
|
{
|
||||||
|
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||||
|
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||||
|
.bcdUSB = USB_BCD,
|
||||||
|
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
|
||||||
|
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||||
|
.bNumConfigurations = 0x01,
|
||||||
|
.bReserved = 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
|
||||||
|
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||||
|
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||||
|
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||||
|
uint8_t const* tud_descriptor_device_qualifier_cb(void)
|
||||||
|
{
|
||||||
|
return (uint8_t const*) &desc_device_qualifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||||
|
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||||
|
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||||
|
uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
|
||||||
|
{
|
||||||
|
(void) index; // for multiple configurations
|
||||||
|
|
||||||
|
// other speed config is basically configuration with type = OHER_SPEED_CONFIG
|
||||||
|
memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN);
|
||||||
|
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
|
||||||
|
|
||||||
|
// this example use the same configuration for both high and full speed mode
|
||||||
|
return desc_other_speed_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // highspeed
|
||||||
|
|
||||||
|
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||||
|
// Application return pointer to descriptor
|
||||||
|
// Descriptor contents must exist long enough for transfer to complete
|
||||||
|
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||||
|
{
|
||||||
|
(void) index; // for multiple configurations
|
||||||
|
|
||||||
|
// This example use the same configuration for both high and full speed mode
|
||||||
|
return desc_configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// String Descriptors
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// buffer to hold flash ID
|
||||||
|
char serial[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1];
|
||||||
|
|
||||||
|
// array of pointer to string descriptors
|
||||||
|
char const* string_desc_arr [] =
|
||||||
|
{
|
||||||
|
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||||
|
"dogeystamp", // 1: Manufacturer
|
||||||
|
"Morse-inator 2000", // 2: Product
|
||||||
|
serial, // 3: Serials, uses the flash ID
|
||||||
|
"Morse-inator 2000 Serial" // 4: CDC interface
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint16_t _desc_str[32];
|
||||||
|
|
||||||
|
// Invoked when received GET STRING DESCRIPTOR request
|
||||||
|
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||||
|
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||||
|
{
|
||||||
|
(void) langid;
|
||||||
|
|
||||||
|
uint8_t chr_count;
|
||||||
|
|
||||||
|
if ( index == 0)
|
||||||
|
{
|
||||||
|
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||||
|
chr_count = 1;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||||
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||||
|
|
||||||
|
if (index == 3) pico_get_unique_board_id_string(serial, sizeof(serial));
|
||||||
|
|
||||||
|
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
|
||||||
|
|
||||||
|
const char* str = string_desc_arr[index];
|
||||||
|
|
||||||
|
// Cap at max char
|
||||||
|
chr_count = strlen(str);
|
||||||
|
if ( chr_count > 31 ) chr_count = 31;
|
||||||
|
|
||||||
|
// 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] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
|
||||||
|
|
||||||
|
return _desc_str;
|
||||||
|
}
|
37
pico-stuff/usb_descriptors.h
Normal file
37
pico-stuff/usb_descriptors.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef USB_DESCRIPTORS_H_
|
||||||
|
#define USB_DESCRIPTORS_H_
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
REPORT_ID_KEYBOARD = 1,
|
||||||
|
REPORT_ID_MOUSE,
|
||||||
|
REPORT_ID_CONSUMER_CONTROL,
|
||||||
|
REPORT_ID_GAMEPAD,
|
||||||
|
REPORT_ID_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* USB_DESCRIPTORS_H_ */
|
Loading…
Reference in New Issue
Block a user