Support feather board, move to broken out source

This commit is contained in:
Justin C. Miller
2025-02-15 14:27:26 -08:00
parent 7a0cfebf52
commit 3a8fa7d08f
18 changed files with 411 additions and 105 deletions

View File

@@ -22,7 +22,7 @@ include(pico_sdk_import.cmake)
project(EDMFD C CXX) project(EDMFD C CXX)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
set(PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS 3000) set(PICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS 3000)
pico_sdk_init() pico_sdk_init()
@@ -35,18 +35,36 @@ add_compile_options(
if (CMAKE_C_COMPILER_ID STREQUAL "GNU") if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wno-maybe-uninitialized) add_compile_options(-Wno-maybe-uninitialized)
add_compile_options(-ffile-prefix-map=${CMAKE_SOURCE_DIR}/src/=)
endif() endif()
include_directories(src)
add_library(logging STATIC
src/logging/log.cc
)
target_include_directories(logging PRIVATE src)
add_executable(edmfd) add_executable(edmfd)
target_include_directories(edmfd PUBLIC src)
target_sources(edmfd PUBLIC target_sources(edmfd PUBLIC
src/blink.cc src/edmfd/blink.cc
src/hid.cc src/edmfd/hid.cc
src/main.cc src/edmfd/main.cc
src/usb_descriptors.cc src/edmfd/mcp23017.cc
src/vendor.cc src/edmfd/screen.cc
src/edmfd/usb_descriptors.cc
src/edmfd/vendor.cc
) )
pico_enable_stdio_usb(edmfd 1) pico_enable_stdio_uart(edmfd 1)
pico_enable_stdio_uart(edmfd 0)
pico_add_extra_outputs(edmfd) pico_add_extra_outputs(edmfd)
target_link_libraries(edmfd PUBLIC pico_stdlib pico_unique_id tinyusb_device tinyusb_board) target_link_libraries(edmfd PUBLIC
logging
pico_stdlib
pico_stdio_uart
pico_unique_id
hardware_i2c
tinyusb_device
tinyusb_board
)

163
src/edmfd/main.cc Normal file
View File

@@ -0,0 +1,163 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/i2c.h"
#include "tusb_config.h"
#include "bsp/board_api.h"
#include "tusb.h"
#include "logging/log.hh"
#include "blink.hh"
#include "hid.hh"
#include "mcp23017.hh"
extern "C" {
// TinyUSB callbacks
void tud_mount_cb();
void tud_unmount_cb();
void tud_suspend_cb(bool);
void tud_resume_cb();
}
// I2C reserves some addresses for special purposes. We exclude these from the scan.
// These are any addresses of the form 000 0xxx or 111 1xxx
bool reserved_addr(uint8_t addr) {
return (addr & 0x78) == 0 || (addr & 0x78) == 0x78;
}
void setup_i2c()
{
i2c_init(i2c_default, 100 * 1000);
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
// Make the I2C pins available to picotool
bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));
}
int write_mcp(uint8_t addr, uint8_t reg, uint8_t value, const char *regname)
{
uint8_t txdata[2] = { reg, value };
int ret = i2c_write_blocking(i2c_default, addr, txdata, 2, false);
if (ret)
printf("Error %02x writing %02x to %02x:%s", ret, value, addr, regname);
else
printf("Wrote %02x to %02x:%s", value, addr, regname);
return ret;
}
void setup_mcp()
{
// A7 is button, B0 is LED
static constexpr uint8_t addr = 0x20;
if (write_mcp(addr, 0x00, 0xff, "IODIRA")) return;
if (write_mcp(addr, 0x10, 0x00, "IODIRB")) return;
if (write_mcp(addr, 0x02, 0xff, "GPINTENA")) return;
if (write_mcp(addr, 0x06, 0xff, "GPPUA")) return;
}
void i2c_scan()
{
printf("\nI2C Bus Scan\n");
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
for (int addr = 0; addr < (1 << 7); ++addr) {
if (addr % 16 == 0) {
printf("%02x ", addr);
}
// Perform a 1-byte dummy read from the probe address. If a slave
// acknowledges this address, the function returns the number of bytes
// transferred. If the address byte is ignored, the function returns
// -1.
// Skip over any reserved addresses.
int ret;
uint8_t rxdata;
if (reserved_addr(addr))
ret = PICO_ERROR_GENERIC;
else
ret = i2c_read_blocking(i2c_default, addr, &rxdata, 1, false);
printf(ret < 0 ? "." : "@");
printf(addr % 16 == 15 ? "\n" : " ");
}
printf("Done.\n");
}
int main(void)
{
setup_default_uart();
stdio_init_all();
puts("\n");
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
board_init();
log::info("board initialized");
tud_init(BOARD_TUD_RHPORT);
if (board_init_after_tusb)
board_init_after_tusb();
log::info("tinyusb initialized");
setup_i2c();
log::info("i2c initialized");
using mcp23017::reg;
mcp23017::gpios gpios {0x20};
uint8_t iocon = 0xba;
gpios.read(reg::iocona, &iocon);
gpios.read(reg::iodira, &iocon);
/*
gpios.write(reg::iodira, 0xff);
gpios.write(reg::gpintena, 0xff);
gpios.write(reg::gppua, 0xff);
*/
while (1) {
uint32_t time = board_millis();
tud_task(); // tinyusb device task
blink::task(time);
hid::task(time);
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb() {
blink::set_pattern(blink::pattern::mounted);
}
// Invoked when device is unmounted
void tud_umount_cb() {
blink::set_pattern(blink::pattern::not_mounted);
}
// Invoked when usb bus is suspended
// argumen : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool) {
blink::set_pattern(blink::pattern::suspended);
}
// Invoked when usb bus is resumed
void tud_resume_cb() {
blink::pattern pat = tud_mounted() ? blink::pattern::mounted : blink::pattern::not_mounted;
blink::set_pattern(pat);
}

60
src/edmfd/mcp23017.cc Normal file
View File

@@ -0,0 +1,60 @@
#include <stdio.h>
#include "hardware/i2c.h"
#include "pico/stdlib.h"
#include "logging/log.hh"
#include "mcp23017.hh"
namespace mcp23017 {
static const char * regnames[] = {
"iodira", "iodirb",
"ipola", "ipolb",
"gpintena", "gpintenb",
"defvala", "defvalb",
"intcona", "intconb",
"iocona", "ioconb",
"gppua", "gppub",
"intfa", "intfb",
"intcapa", "intcapb",
"gpioa", "gpiob",
"olata", "olatb",
};
gpios::gpios(uint8_t addr) :
m_addr {addr}
{}
int
gpios::read(reg r, uint8_t *value)
{
uint8_t txdata = uint8_t(r);
int ret = i2c_write_burst_blocking(i2c_default, m_addr, &txdata, 1);
if (ret < 0) {
log::error("writing read address {%02x:%s}: %d", m_addr, regnames[uint8_t(r)], ret);
return ret;
}
ret = i2c_read_blocking(i2c_default, m_addr, value, 1, false);
if (ret < 0) {
log::error("reading {%02x:%s}: %d", m_addr, regnames[uint8_t(r)], ret);
} else {
log::info("read {%02x:%s}: 0x%02x", m_addr, regnames[uint8_t(r)], *value);
}
return ret;
}
int
gpios::write(reg r, uint8_t value)
{
uint8_t txdata[2] = { uint8_t(r), value };
int ret = i2c_write_blocking(i2c_default, m_addr, txdata, sizeof(txdata), false);
if (ret != PICO_OK) {
log::error("writing {%02x:%s}: %d", m_addr, regnames[uint8_t(r)], ret);
} else {
log::info("wrote {%02x:%s}: 0x%02x", m_addr, regnames[uint8_t(r)], value);
}
return ret;
}
} // namespace mcp23017

31
src/edmfd/mcp23017.hh Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
namespace mcp23017 {
enum class reg : uint8_t {
iodira, iodirb,
ipola, ipolb,
gpintena, gpintenb,
defvala, defvalb,
intcona, intconb,
iocona, ioconb,
gppua, gppub,
intfa, intfb,
intcapa, intcapb,
gpioa, gpiob,
olata, olatb,
};
class gpios
{
public:
gpios(uint8_t addr);
int read(reg r, uint8_t *value);
int write(reg r, uint8_t value);
private:
uint8_t m_addr;
};
} // namespace mcp23017

9
src/edmfd/proto.cc Normal file
View File

@@ -0,0 +1,9 @@
#include "proto.hh"
namespace proto {
void parse(const uint8_t *buffer, unsigned len) {
}
} // namespace proto

8
src/edmfd/proto.hh Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <stdint.h>
namespace proto {
void parse(const uint8_t *buffer, unsigned len);
} // namespace proto

14
src/edmfd/screen.cc Normal file
View File

@@ -0,0 +1,14 @@
#include "screen.hh"
namespace screen {
palette g_palette = {0};
void set_color(unsigned index, color_t color) {
if (index >= num_colors) return;
g_palette[index] = color;
}
const palette & get_colors() { return g_palette; }
} // namespace screen

15
src/edmfd/screen.hh Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include <array>
#include <stdint.h>
namespace screen {
inline constexpr unsigned color_bits = 4;
inline constexpr unsigned num_colors = 1 << color_bits;
using color_t = uint16_t;
using palette = std::array<color_t, num_colors>;
void set_color(unsigned index, color_t color);
const palette & get_colors();
} // namepsace screen

View File

@@ -11,7 +11,7 @@
* [MSB] HID | MSC | CDC [LSB] * [MSB] HID | MSC | CDC [LSB]
*/ */
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x0000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ #define USB_PID (0x0200 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
#define USB_VID 0x4a33 // J3 in ascii #define USB_VID 0x4a33 // J3 in ascii
@@ -123,12 +123,12 @@ uint8_t const desc_configuration[config_total_len] = {
// Config number, interface count, string index, total length, attribute, power in mA // Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, uint8_t(interface::_count), 0, config_total_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), TUD_CONFIG_DESCRIPTOR(1, uint8_t(interface::_count), 0, config_total_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & IN address, EP size
TUD_VENDOR_DESCRIPTOR(uint8_t(interface::vendor), uint8_t(strings::interface), uint8_t(endpoint::vendor_out), uint8_t(endpoint::vendor_in), 64),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR(uint8_t(interface::HID), 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), uint8_t(endpoint::HID), CFG_TUD_HID_EP_BUFSIZE, 5), TUD_HID_DESCRIPTOR(uint8_t(interface::HID), 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), uint8_t(endpoint::HID), CFG_TUD_HID_EP_BUFSIZE, 5),
// Interface number, string index, EP Out & IN address, EP size
TUD_VENDOR_DESCRIPTOR(uint8_t(interface::vendor), uint8_t(strings::interface), uint8_t(endpoint::vendor_out), uint8_t(endpoint::vendor_in), 64),
// CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. // CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
//TUD_CDC_DESCRIPTOR(uint8_t(interface::CDC), 4, uint8_t(endpoint::CDCnotify), 8, uint8_t(endpoint::CDCout), uint8_t(endpoint::CDCin), 64) //TUD_CDC_DESCRIPTOR(uint8_t(interface::CDC), 4, uint8_t(endpoint::CDCnotify), 8, uint8_t(endpoint::CDCout), uint8_t(endpoint::CDCin), 64)
}; };

View File

@@ -14,8 +14,8 @@ enum class strings : uint8_t {
_count _count
}; };
enum class interface : uint8_t { vendor, HID, _count }; enum class interface : uint8_t { HID, vendor, _count };
enum class endpoint : uint8_t { HID = 0x81, vendor_out = 0x02, vendor_in = 0x82 }; enum class endpoint : uint8_t { HID = 0x81, vendor_out = 0x02, vendor_in = 0x83 };
enum class vendor_req : uint8_t { enum class vendor_req : uint8_t {
microsoft = 1, microsoft = 1,

View File

@@ -2,6 +2,7 @@
#include "class/vendor/vendor_device.h" #include "class/vendor/vendor_device.h"
#include "blink.hh" #include "blink.hh"
#include "screen.hh"
#include "usb_descriptors.hh" #include "usb_descriptors.hh"
extern "C" { extern "C" {
@@ -30,11 +31,34 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
// Invoked when received new data // Invoked when received new data
void tud_vendor_rx_cb(uint8_t, uint8_t const* buffer, uint16_t bufsize) { void tud_vendor_rx_cb(uint8_t, uint8_t const* buffer, uint16_t bufsize) {
tud_vendor_write(buffer, bufsize); if (!bufsize) return;
switch (buffer[0]) {
case 0x02: { // set colors
for (unsigned i = 1; i < bufsize-3u; i += 3u) {
unsigned index = buffer[i];
screen::color_t color =
*reinterpret_cast<const screen::color_t*>(&buffer[i+1]);
screen::set_color(index, color);
}
break;
}
case 0x03: {// get colors
const screen::palette &pal = screen::get_colors();
tud_vendor_write(&pal, sizeof(pal));
tud_vendor_write_flush();
break;
}
default:
break;
}
tud_vendor_read_flush(); tud_vendor_read_flush();
} }
// Invoked when last rx (tx?) transfer finished // Invoked when last rx (tx?) transfer finished
void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) { void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) {
tud_vendor_write_flush();
} }

26
src/logging/log.cc Normal file
View File

@@ -0,0 +1,26 @@
#include <stdarg.h>
#include <stdio.h>
#include "log.hh"
namespace logging {
namespace {
const char *level_strings[] = {
nullptr,
"\e[90mTRACE\e[0m",
"\e[94mDEBUG\e[0m",
"\e[92mINFO\e[0m",
"\e[93mWARN\e[0m",
"\e[91mERROR\e[0m",
};
}
void log(level lv, const format &fmt, ...) {
printf("%20s:%-3d: %5s ", fmt.loc.file_name(), fmt.loc.line(), level_strings[int(lv)]);
va_list args;
va_start(args, fmt);
vprintf(fmt.fmt, args);
va_end(args);
putchar('\n');
}
} // namespace log

26
src/logging/log.hh Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include <source_location>
namespace logging {
enum class level { none, trace, debug, info, warn, error };
class format
{
public:
format() = default;
format(const char *fmt, const std::source_location &loc = std::source_location::current()) :
fmt {fmt}, loc {loc} {}
const char *fmt;
const std::source_location &loc;
};
void log(level lv, const format &fmt, ...);
inline void trace(const format &fmt, auto&&... args) { log(level::trace, fmt, args...); }
inline void debug(const format &fmt, auto&&... args) { log(level::debug, fmt, args...); }
inline void info (const format &fmt, auto&&... args) { log(level::info, fmt, args...); }
inline void warn (const format &fmt, auto&&... args) { log(level::warn, fmt, args...); }
inline void error(const format &fmt, auto&&... args) { log(level::error, fmt, args...); }
} // namespace logging
namespace log = logging;

View File

@@ -1,88 +0,0 @@
/*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tusb_config.h"
#include "bsp/board_api.h"
#include "tusb.h"
#include "blink.hh"
#include "hid.hh"
extern "C" {
// TinyUSB callbacks
void tud_mount_cb();
void tud_unmount_cb();
void tud_suspend_cb(bool);
void tud_resume_cb();
}
extern "C"
int main(void)
{
board_init();
tud_init(BOARD_TUD_RHPORT);
if (board_init_after_tusb)
board_init_after_tusb();
while (1) {
uint32_t time = board_millis();
tud_task(); // tinyusb device task
blink::task(time);
hid::task(time);
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb() {
blink::set_pattern(blink::pattern::mounted);
}
// Invoked when device is unmounted
void tud_umount_cb() {
blink::set_pattern(blink::pattern::not_mounted);
}
// Invoked when usb bus is suspended
// argumen : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool) {
blink::set_pattern(blink::pattern::suspended);
}
// Invoked when usb bus is resumed
void tud_resume_cb() {
blink::pattern pat = tud_mounted() ? blink::pattern::mounted : blink::pattern::not_mounted;
blink::set_pattern(pat);
}