Initial import

This commit is contained in:
arturo182 2021-11-03 19:26:43 +01:00
commit c9791af550
31 changed files with 2068 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build
*.txt.user

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "3rdparty/pico-sdk"]
path = 3rdparty/pico-sdk
url = https://github.com/raspberrypi/pico-sdk.git
[submodule "pico-sdk"]
path = pico-sdk
url = https://github.com/raspberrypi/pico-sdk.git

1
3rdparty/pico-sdk vendored Submodule

@ -0,0 +1 @@
Subproject commit bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7

20
CMakeLists.txt Normal file
View File

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.13)
set(PICO_PLATFORM "rp2040")
set(PICO_BOARD_HEADER_DIRS ${CMAKE_CURRENT_LIST_DIR}/boards)
include(3rdparty/pico-sdk/pico_sdk_init.cmake)
project(i2c_puppet)
pico_sdk_init()
add_subdirectory(app)
# binary info in flash
pico_set_program_name(i2c_puppet "I2C Puppet")
pico_set_program_version(i2c_puppet "0.1")
# printf targets
#pico_enable_stdio_usb(i2c_puppet 1)
pico_enable_stdio_uart(i2c_puppet 1)

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# I2C Puppet
git clone https://github.com/solderparty/i2c_puppet
cd i2c_puppet
git submodule update --init
cd 3rdparty/pico-sdk
git submodule update --init

30
app/CMakeLists.txt Normal file
View File

@ -0,0 +1,30 @@
add_executable(i2c_puppet
backlight.c
debug.c
fifo.c
gpioexp.c
puppet_i2c.c
interrupt.c
keyboard.c
main.c
reg.c
touchpad.c
usb.c
usb_descriptors.c
)
add_compile_options(-Wall -Wextra -Wpedantic)
target_include_directories(i2c_puppet PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(i2c_puppet
cmsis_core
hardware_i2c
hardware_pwm
pico_bootsel_via_double_reset
pico_stdlib
tinyusb_device
)
# create map/bin/hex/uf2 file in addition to elf
pico_add_extra_outputs(i2c_puppet)

6
app/app_config.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define KEY_FIFO_SIZE 31 // number of keys in the public FIFO

22
app/backlight.c Normal file
View File

@ -0,0 +1,22 @@
#include "backlight.h"
#include "reg.h"
#include <hardware/pwm.h>
#include <pico/stdlib.h>
void backlight_sync(void)
{
pwm_set_gpio_level(PIN_BKL, reg_get_value(REG_ID_BKL) * 0x80);
}
void backlight_init(void)
{
gpio_set_function(PIN_BKL, GPIO_FUNC_PWM);
const uint slice_num = pwm_gpio_to_slice_num(PIN_BKL);
pwm_config config = pwm_get_default_config();
pwm_init(slice_num, &config, true);
backlight_sync();
}

6
app/backlight.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
void backlight_sync(void);
void backlight_init(void);

118
app/debug.c Normal file
View File

@ -0,0 +1,118 @@
#include "debug.h"
#include "app_config.h"
#include "gpioexp.h"
#include "keyboard.h"
#include "reg.h"
#include "touchpad.h"
#include "usb.h"
#include <pico/mutex.h>
#include <pico/stdio/driver.h>
#include <pico/stdlib.h>
#include <stdio.h>
#include <tusb.h>
#define PICO_STDIO_USB_STDOUT_TIMEOUT_US 500000
static void key_cb(char key, enum key_state state)
{
printf("key: 0x%02X/%d/%c, state: %d, bkl: %d\r\n", key, key, key, state, reg_get_value(REG_ID_BKL));
}
static struct key_callback key_callback =
{
.func = key_cb
};
static void key_lock_cb(bool caps_changed, bool num_changed)
{
printf("lock, caps_c: %d, caps: %d, num_c: %d, num: %d\r\n",
caps_changed, keyboard_get_capslock(),
num_changed, keyboard_get_numlock());
}
static struct key_lock_callback key_lock_callback =
{
.func = key_lock_cb
};
static void touch_cb(int8_t x, int8_t y)
{
printf("%s: x: %d, y: %d !\r\n", __func__, x, y);
}
static struct touch_callback touch_callback =
{
.func = touch_cb
};
static void gpioexp_cb(uint8_t gpio, uint8_t gpio_idx)
{
printf("gpioexp, pin: %d, idx: %d\r\n", gpio, gpio_idx);
}
static struct gpioexp_callback gpioexp_callback =
{
.func = gpioexp_cb
};
// copied from pico_stdio_usb in the SDK
static void usb_out_chars(const char *buf, int length)
{
static uint64_t last_avail_time;
uint32_t owner;
if (!mutex_try_enter(usb_get_mutex(), &owner)) {
if (owner == get_core_num())
return;
mutex_enter_blocking(usb_get_mutex());
}
if (tud_cdc_connected()) {
for (int i = 0; i < length;) {
int n = length - i;
int avail = tud_cdc_write_available();
if (n > avail) n = avail;
if (n) {
int n2 = tud_cdc_write(buf + i, n);
tud_task();
tud_cdc_write_flush();
i += n2;
last_avail_time = time_us_64();
} else {
tud_task();
tud_cdc_write_flush();
if (!tud_cdc_connected() ||
(!tud_cdc_write_available() && time_us_64() > last_avail_time + PICO_STDIO_USB_STDOUT_TIMEOUT_US)) {
break;
}
}
}
} else {
// reset our timeout
last_avail_time = 0;
}
mutex_exit(usb_get_mutex());
}
static struct stdio_driver stdio_usb =
{
.out_chars = usb_out_chars,
#if PICO_STDIO_ENABLE_CRLF_SUPPORT
.crlf_enabled = PICO_STDIO_DEFAULT_CRLF
#endif
};
void debug_init(void)
{
stdio_init_all();
stdio_set_driver_enabled(&stdio_usb, true);
printf("I2C Puppet SW v%d.%d\r\n", VERSION_MAJOR, VERSION_MINOR);
keyboard_add_key_callback(&key_callback);
keyboard_add_lock_callback(&key_lock_callback);
touchpad_add_touch_callback(&touch_callback);
gpioexp_add_int_callback(&gpioexp_callback);
}

3
app/debug.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void debug_init(void);

60
app/fifo.c Normal file
View File

@ -0,0 +1,60 @@
#include "app_config.h"
#include "fifo.h"
static struct
{
struct fifo_item fifo[KEY_FIFO_SIZE];
uint8_t count;
uint8_t read_idx;
uint8_t write_idx;
} self;
uint8_t fifo_count(void)
{
return self.count;
}
void fifo_flush(void)
{
self.write_idx = 0;
self.read_idx = 0;
self.count = 0;
}
bool fifo_enqueue(const struct fifo_item item)
{
if (self.count >= KEY_FIFO_SIZE)
return false;
self.fifo[self.write_idx++] = item;
self.write_idx %= KEY_FIFO_SIZE;
++self.count;
return true;
}
void fifo_enqueue_force(const struct fifo_item item)
{
if (fifo_enqueue(item))
return;
self.fifo[self.write_idx++] = item;
self.write_idx %= KEY_FIFO_SIZE;
self.read_idx++;
self.read_idx %= KEY_FIFO_SIZE;
}
struct fifo_item fifo_dequeue(void)
{
struct fifo_item item = { 0 };
if (self.count == 0)
return item;
item = self.fifo[self.read_idx++];
self.read_idx %= KEY_FIFO_SIZE;
--self.count;
return item;
}

15
app/fifo.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include "keyboard.h"
struct fifo_item
{
char key;
enum key_state state;
};
uint8_t fifo_count(void);
void fifo_flush(void);
bool fifo_enqueue(const struct fifo_item item);
void fifo_enqueue_force(const struct fifo_item item);
struct fifo_item fifo_dequeue(void);

279
app/gpioexp.c Normal file
View File

@ -0,0 +1,279 @@
#include "gpioexp.h"
#include "reg.h"
#include <pico/stdlib.h>
#include <stdio.h>
static struct
{
struct gpioexp_callback *callbacks;
} self;
static void set_dir(uint8_t gpio, uint8_t gpio_idx, uint8_t dir)
{
#ifndef NDEBUG
printf("%s: gpio: %d, gpio_idx: %d, dir: %d\r\n", __func__, gpio, gpio_idx, dir);
#endif
gpio_init(gpio);
if (dir == DIR_INPUT) {
if (reg_is_bit_set(REG_ID_PUE, (1 << gpio_idx))) {
if (reg_is_bit_set(REG_ID_PUD, (1 << gpio_idx)) == PUD_UP) {
gpio_is_pulled_up(gpio);
} else {
gpio_is_pulled_down(gpio);
}
} else {
gpio_disable_pulls(gpio);
}
gpio_set_dir(gpio, GPIO_IN);
gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);
reg_set_bit(REG_ID_DIR, (1 << gpio_idx));
} else {
gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, false);
gpio_set_dir(gpio, GPIO_OUT);
reg_clear_bit(REG_ID_DIR, (1 << gpio_idx));
}
}
void gpioexp_gpio_irq(uint gpio, uint32_t events)
{
(void)gpio;
(void)events;
#define CALLBACK(bit) \
if (gpio == PIN_GPIOEXP ## bit) { \
struct gpioexp_callback *cb = self.callbacks; \
while (cb) { \
cb->func(PIN_GPIOEXP ## bit, bit); \
cb = cb->next; \
} \
return; \
}
#ifdef PIN_GPIOEXP0
CALLBACK(0)
#endif
#ifdef PIN_GPIOEXP1
CALLBACK(1)
#endif
#ifdef PIN_GPIOEXP2
CALLBACK(2)
#endif
#ifdef PIN_GPIOEXP3
CALLBACK(3)
#endif
#ifdef PIN_GPIOEXP4
CALLBACK(4)
#endif
#ifdef PIN_GPIOEXP5
CALLBACK(5)
#endif
#ifdef PIN_GPIOEXP6
CALLBACK(6)
#endif
#ifdef PIN_GPIOEXP7
CALLBACK(7)
#endif
}
void gpioexp_update_dir(uint8_t new_dir)
{
#ifndef NDEBUG
printf("%s: dir: 0x%02X\r\n", __func__, new_dir);
#endif
const uint8_t old_dir = reg_get_value(REG_ID_DIR);
(void)old_dir; // Shut up warning in case no GPIOs configured
#define UPDATE_DIR(bit) \
if ((old_dir & (1 << bit)) != (new_dir & (1 << bit))) \
set_dir(PIN_GPIOEXP ## bit, bit, (new_dir & (1 << bit)) != 0);
#ifdef PIN_GPIOEXP0
UPDATE_DIR(0)
#endif
#ifdef PIN_GPIOEXP1
UPDATE_DIR(1)
#endif
#ifdef PIN_GPIOEXP2
UPDATE_DIR(2)
#endif
#ifdef PIN_GPIOEXP3
UPDATE_DIR(3)
#endif
#ifdef PIN_GPIOEXP4
UPDATE_DIR(4)
#endif
#ifdef PIN_GPIOEXP5
UPDATE_DIR(5)
#endif
#ifdef PIN_GPIOEXP6
UPDATE_DIR(6)
#endif
#ifdef PIN_GPIOEXP7
UPDATE_DIR(7)
#endif
}
void gpioexp_update_pue_pud(uint8_t new_pue, uint8_t new_pud)
{
#ifndef NDEBUG
printf("%s: pue: 0x%02X, pud: 0x%02X\r\n", __func__, new_pue, new_pud);
#endif
const uint8_t old_pue = reg_get_value(REG_ID_PUE);
const uint8_t old_pud = reg_get_value(REG_ID_PUD);
// Shut up warnings in case no GPIOs configured
(void)old_pue;
(void)old_pud;
reg_set_value(REG_ID_PUE, new_pue);
reg_set_value(REG_ID_PUD, new_pud);
#define UPDATE_PULL(bit) \
if (((old_pue & (1 << bit)) != (new_pue & (1 << bit))) || \
((old_pud & (1 << bit)) != (new_pud & (1 << bit)))) { \
set_dir(PIN_GPIOEXP ## bit, bit, reg_is_bit_set(REG_ID_DIR, (1 << bit))); \
}
#ifdef PIN_GPIOEXP0
UPDATE_PULL(0)
#endif
#ifdef PIN_GPIOEXP1
UPDATE_PULL(1)
#endif
#ifdef PIN_GPIOEXP2
UPDATE_PULL(2)
#endif
#ifdef PIN_GPIOEXP3
UPDATE_PULL(3)
#endif
#ifdef PIN_GPIOEXP4
UPDATE_PULL(4)
#endif
#ifdef PIN_GPIOEXP5
UPDATE_PULL(5)
#endif
#ifdef PIN_GPIOEXP6
UPDATE_PULL(6)
#endif
#ifdef PIN_GPIOEXP7
UPDATE_PULL(7)
#endif
}
void gpioexp_set_value(uint8_t value)
{
#ifndef NDEBUG
printf("%s: value: 0x%02X\r\n", __func__, value);
#endif
#define SET_VALUE(bit) \
if (reg_is_bit_set(REG_ID_DIR, (1 << bit)) == DIR_OUTPUT) { \
gpio_put(PIN_GPIOEXP ## bit, (value & (1 << bit))); \
}
#ifdef PIN_GPIOEXP0
SET_VALUE(0)
#endif
#ifdef PIN_GPIOEXP1
SET_VALUE(1)
#endif
#ifdef PIN_GPIOEXP2
SET_VALUE(2)
#endif
#ifdef PIN_GPIOEXP3
SET_VALUE(3)
#endif
#ifdef PIN_GPIOEXP4
SET_VALUE(4)
#endif
#ifdef PIN_GPIOEXP5
SET_VALUE(5)
#endif
#ifdef PIN_GPIOEXP6
SET_VALUE(6)
#endif
#ifdef PIN_GPIOEXP7
SET_VALUE(7)
#endif
}
uint8_t gpioexp_get_value(void)
{
uint8_t value = 0;
#define GET_VALUE(bit) \
value |= (gpio_get(PIN_GPIOEXP ## bit) << bit);
// if (reg_is_bit_set(REG_ID_DIR, (1 << bit)) == DIR_INPUT) { \
// value |= (port_pin_get_input_level(PIN_GPIOEXP ## bit) << bit); \
// } else { \
// value |= (port_pin_get_output_level(PIN_GPIOEXP ## bit) << bit); \
// }
#ifdef PIN_GPIOEXP0
GET_VALUE(0)
#endif
#ifdef PIN_GPIOEXP1
GET_VALUE(1)
#endif
#ifdef PIN_GPIOEXP2
GET_VALUE(2)
#endif
#ifdef PIN_GPIOEXP3
GET_VALUE(3)
#endif
#ifdef PIN_GPIOEXP4
GET_VALUE(4)
#endif
#ifdef PIN_GPIOEXP5
GET_VALUE(5)
#endif
#ifdef PIN_GPIOEXP6
GET_VALUE(6)
#endif
#ifdef PIN_GPIOEXP7
GET_VALUE(7)
#endif
return value;
}
void gpioexp_add_int_callback(struct gpioexp_callback *callback)
{
// first callback
if (!self.callbacks) {
self.callbacks = callback;
return;
}
// find last and insert after
struct gpioexp_callback *cb = self.callbacks;
while (cb->next)
cb = cb->next;
cb->next = callback;
}
void gpioexp_init(void)
{
// Configure all to inputs
gpioexp_update_dir(0xFF);
}

20
app/gpioexp.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <sys/types.h>
struct gpioexp_callback
{
void (*func)(uint8_t gpio, uint8_t gpio_idx);
struct gpioexp_callback *next;
};
void gpioexp_gpio_irq(uint gpio, uint32_t events);
void gpioexp_update_dir(uint8_t dir);
void gpioexp_update_pue_pud(uint8_t pue, uint8_t pud);
void gpioexp_set_value(uint8_t value);
uint8_t gpioexp_get_value(void);
void gpioexp_add_int_callback(struct gpioexp_callback *callback);
void gpioexp_init(void);

106
app/interrupt.c Normal file
View File

@ -0,0 +1,106 @@
#include "interrupt.h"
#include "app_config.h"
#include "gpioexp.h"
#include "keyboard.h"
#include "reg.h"
#include "touchpad.h"
#include <pico/stdlib.h>
static void key_cb(char key, enum key_state state)
{
(void)key;
(void)state;
if (!reg_is_bit_set(REG_ID_CFG, CFG_KEY_INT))
return;
reg_set_bit(REG_ID_INT, INT_KEY);
gpio_put(PIN_INT, 0);
busy_wait_ms(reg_get_value(REG_ID_IND));
gpio_put(PIN_INT, 1);
}
static struct key_callback key_callback =
{
.func = key_cb
};
static void key_lock_cb(bool caps_changed, bool num_changed)
{
bool do_int = false;
if (caps_changed && reg_is_bit_set(REG_ID_CFG, CFG_CAPSLOCK_INT)) {
reg_set_bit(REG_ID_INT, INT_CAPSLOCK);
do_int = true;
}
if (num_changed && reg_is_bit_set(REG_ID_CFG, CFG_NUMLOCK_INT)) {
reg_set_bit(REG_ID_INT, INT_NUMLOCK);
do_int = true;
}
if (do_int) {
gpio_put(PIN_INT, 0);
busy_wait_ms(reg_get_value(REG_ID_IND));
gpio_put(PIN_INT, 1);
}
}
static struct key_lock_callback key_lock_callback =
{
.func = key_lock_cb
};
static void touch_cb(int8_t x, int8_t y)
{
(void)x;
(void)y;
if (!reg_is_bit_set(REG_ID_CF2, CF2_TOUCH_INT))
return;
reg_set_bit(REG_ID_INT, INT_TOUCH);
gpio_put(PIN_INT, 0);
busy_wait_ms(reg_get_value(REG_ID_IND));
gpio_put(PIN_INT, 1);
}
static struct touch_callback touch_callback =
{
.func = touch_cb
};
static void gpioexp_cb(uint8_t gpio, uint8_t gpio_idx)
{
(void)gpio;
if (!reg_is_bit_set(REG_ID_GIC, (1 << gpio_idx)))
return;
reg_set_bit(REG_ID_INT, INT_GPIO);
reg_set_bit(REG_ID_GIN, (1 << gpio_idx));
gpio_put(PIN_INT, 0);
busy_wait_ms(reg_get_value(REG_ID_IND));
gpio_put(PIN_INT, 1);
}
static struct gpioexp_callback gpioexp_callback =
{
.func = gpioexp_cb
};
void interrupt_init(void)
{
gpio_init(PIN_INT);
gpio_set_dir(PIN_INT, GPIO_OUT);
gpio_pull_up(PIN_INT);
gpio_put(PIN_INT, true);
keyboard_add_key_callback(&key_callback);
keyboard_add_lock_callback(&key_lock_callback);
touchpad_add_touch_callback(&touch_callback);
gpioexp_add_int_callback(&gpioexp_callback);
}

3
app/interrupt.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void interrupt_init(void);

385
app/keyboard.c Normal file
View File

@ -0,0 +1,385 @@
#include "app_config.h"
#include "fifo.h"
#include "keyboard.h"
#include "reg.h"
#include <pico/stdlib.h>
#define LIST_SIZE 10 // size of the list keeping track of all the pressed keys
enum mod
{
MOD_NONE = 0,
MOD_SYM,
MOD_ALT,
MOD_SHL,
MOD_SHR,
MOD_LAST,
};
struct entry
{
char chr;
char symb;
enum mod mod;
};
struct list_item
{
const struct entry *p_entry;
uint32_t hold_start_time;
enum key_state state;
bool mods[MOD_LAST];
};
static const uint8_t row_pins[NUM_OF_ROWS] =
{
PINS_ROWS
};
static const uint8_t col_pins[NUM_OF_COLS] =
{
PINS_COLS
};
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static const struct entry kbd_entries[][NUM_OF_COLS] =
{
{ { KEY_JOY_CENTER }, { 'W', '1' }, { 'G', '/' }, { 'S', '4' }, { 'L', '"' }, { 'H' , ':' } },
{ { }, { 'Q', '#' }, { 'R', '3' }, { 'E', '2' }, { 'O', '+' }, { 'U', '_' } },
{ { KEY_BTN_LEFT1 }, { '~', '0' }, { 'F', '6' }, { .mod = MOD_SHL }, { 'K', '\'' }, { 'J', ';' } },
{ { }, { ' ', '\t' }, { 'C', '9' }, { 'Z', '7' }, { 'M', '.' }, { 'N', ',' } },
{ { KEY_BTN_LEFT2 }, { .mod = MOD_SYM }, { 'T', '(' }, { 'D', '5' }, { 'I', '-' }, { 'Y', ')' } },
{ { KEY_BTN_RIGHT1 }, { .mod = MOD_ALT }, { 'V', '?' }, { 'X', '8' }, { '$', '`' }, { 'B', '!' } },
{ { }, { 'A', '*' }, { .mod = MOD_SHR }, { 'P', '@' }, { '\b' }, { '\n', '|' } },
};
#if NUM_OF_BTNS > 0
static const struct entry btn_entries[NUM_OF_BTNS] =
{
BTN_KEYS
};
static const uint8_t btn_pins[NUM_OF_BTNS] =
{
PINS_BTNS
};
#endif
#pragma GCC diagnostic pop
static struct
{
struct key_lock_callback *lock_callbacks;
struct key_callback *key_callbacks;
struct list_item list[LIST_SIZE];
uint32_t last_process_time;
bool mods[MOD_LAST];
bool capslock_changed;
bool capslock;
bool numlock_changed;
bool numlock;
} self;
static void transition_to(struct list_item * const p_item, const enum key_state next_state)
{
const struct entry * const p_entry = p_item->p_entry;
p_item->state = next_state;
if (!self.key_callbacks || !p_entry)
return;
char chr = p_entry->chr;
switch (p_entry->mod) {
case MOD_ALT:
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
chr = KEY_MOD_ALT;
break;
case MOD_SHL:
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
chr = KEY_MOD_SHL;
break;
case MOD_SHR:
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
chr = KEY_MOD_SHR;
break;
case MOD_SYM:
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
chr = KEY_MOD_SYM;
break;
default:
{
if (reg_is_bit_set(REG_ID_CFG, CFG_USE_MODS)) {
const bool shift = (self.mods[MOD_SHL] || self.mods[MOD_SHR]) | self.capslock;
const bool alt = self.mods[MOD_ALT] | self.numlock;
if (alt) {
chr = p_entry->symb;
} else if (!shift && (chr >= 'A' && chr <= 'Z')) {
chr = (chr + ' ');
}
}
break;
}
}
if (chr != 0) {
const struct fifo_item item = { chr, next_state };
if (!fifo_enqueue(item)) {
if (reg_is_bit_set(REG_ID_CFG, CFG_OVERFLOW_INT)) {
reg_set_bit(REG_ID_INT, INT_OVERFLOW);
}
if (reg_is_bit_set(REG_ID_CFG, CFG_OVERFLOW_ON))
fifo_enqueue_force(item);
}
struct key_callback *cb = self.key_callbacks;
while (cb) {
cb->func(chr, next_state);
cb = cb->next;
}
}
}
static void next_item_state(struct list_item * const p_item, const bool pressed)
{
switch (p_item->state) {
case KEY_STATE_IDLE:
if (pressed) {
if (p_item->p_entry->mod != MOD_NONE)
self.mods[p_item->p_entry->mod] = true;
if (!self.capslock_changed && self.mods[MOD_SHR] && self.mods[MOD_ALT]) {
self.capslock = true;
self.capslock_changed = true;
}
if (!self.numlock_changed && self.mods[MOD_SHL] && self.mods[MOD_ALT]) {
self.numlock = true;
self.numlock_changed = true;
}
if (!self.capslock_changed && (self.mods[MOD_SHL] || self.mods[MOD_SHR])) {
self.capslock = false;
self.capslock_changed = true;
}
if (!self.numlock_changed && (self.mods[MOD_SHL] || self.mods[MOD_SHR])) {
self.numlock = false;
self.numlock_changed = true;
}
if (!self.mods[MOD_ALT]) {
self.capslock_changed = false;
self.numlock_changed = false;
}
if (self.lock_callbacks && (self.capslock_changed || self.numlock_changed)) {
struct key_lock_callback *cb = self.lock_callbacks;
while (cb) {
cb->func(self.capslock_changed, self.numlock_changed);
cb = cb->next;
}
}
transition_to(p_item, KEY_STATE_PRESSED);
p_item->hold_start_time = to_ms_since_boot(get_absolute_time());
}
break;
case KEY_STATE_PRESSED:
if ((to_ms_since_boot(get_absolute_time()) - p_item->hold_start_time) > (reg_get_value(REG_ID_HLD) * 10)) {
transition_to(p_item, KEY_STATE_HOLD);
} else if(!pressed) {
transition_to(p_item, KEY_STATE_RELEASED);
}
break;
case KEY_STATE_HOLD:
if (!pressed)
transition_to(p_item, KEY_STATE_RELEASED);
break;
case KEY_STATE_RELEASED:
{
if (p_item->p_entry->mod != MOD_NONE)
self.mods[p_item->p_entry->mod] = false;
p_item->p_entry = NULL;
transition_to(p_item, KEY_STATE_IDLE);
break;
}
}
}
void keyboard_task(void)
{
if ((to_ms_since_boot(get_absolute_time()) - self.last_process_time) <= reg_get_value(REG_ID_FRQ))
return;
for (uint32_t c = 0; c < NUM_OF_COLS; ++c) {
gpio_pull_up(col_pins[c]);
gpio_put(col_pins[c], 0);
gpio_set_dir(col_pins[c], GPIO_OUT);
for (uint32_t r = 0; r < NUM_OF_ROWS; ++r) {
const bool pressed = (gpio_get(row_pins[r]) == 0);
const int32_t key_idx = (int32_t)((r * NUM_OF_COLS) + c);
int32_t list_idx = -1;
for (int32_t i = 0; i < LIST_SIZE; ++i) {
if (self.list[i].p_entry != &((const struct entry*)kbd_entries)[key_idx])
continue;
list_idx = i;
break;
}
if (list_idx > -1) {
next_item_state(&self.list[list_idx], pressed);
continue;
}
if (!pressed)
continue;
for (uint32_t i = 0 ; i < LIST_SIZE; ++i) {
if (self.list[i].p_entry != NULL)
continue;
self.list[i].p_entry = &((const struct entry*)kbd_entries)[key_idx];
self.list[i].state = KEY_STATE_IDLE;
next_item_state(&self.list[i], pressed);
break;
}
}
gpio_put(col_pins[c], 1);
gpio_disable_pulls(col_pins[c]);
gpio_set_dir(col_pins[c], GPIO_IN);
}
#if NUM_OF_BTNS > 0
for (uint32_t b = 0; b < NUM_OF_BTNS; ++b) {
const bool pressed = (gpio_get(btn_pins[b]) == 0);
int32_t list_idx = -1;
for (int32_t i = 0; i < LIST_SIZE; ++i) {
if (self.list[i].p_entry != &((const struct entry*)btn_entries)[b])
continue;
list_idx = i;
break;
}
if (list_idx > -1) {
next_item_state(&self.list[list_idx], pressed);
continue;
}
if (!pressed)
continue;
for (uint32_t i = 0 ; i < LIST_SIZE; ++i) {
if (self.list[i].p_entry != NULL)
continue;
self.list[i].p_entry = &((const struct entry*)btn_entries)[b];
self.list[i].state = KEY_STATE_IDLE;
next_item_state(&self.list[i], pressed);
break;
}
}
#endif
self.last_process_time = to_ms_since_boot(get_absolute_time());
}
void keyboard_add_key_callback(struct key_callback *callback)
{
// first callback
if (!self.key_callbacks) {
self.key_callbacks = callback;
return;
}
// find last and insert after
struct key_callback *cb = self.key_callbacks;
while (cb->next)
cb = cb->next;
cb->next = callback;
}
void keyboard_add_lock_callback(struct key_lock_callback *callback)
{
// first callback
if (!self.lock_callbacks) {
self.lock_callbacks = callback;
return;
}
// find last and insert after
struct key_lock_callback *cb = self.lock_callbacks;
while (cb->next)
cb = cb->next;
cb->next = callback;
}
bool keyboard_get_capslock(void)
{
return self.capslock;
}
bool keyboard_get_numlock(void)
{
return self.numlock;
}
void keyboard_init(void)
{
for (int i = 0; i < MOD_LAST; ++i)
self.mods[i] = false;
// Rows
for (uint32_t i = 0; i < NUM_OF_ROWS; ++i) {
gpio_init(row_pins[i]);
gpio_pull_up(row_pins[i]);
gpio_set_dir(row_pins[i], GPIO_IN);
}
// Cols
for(uint32_t i = 0; i < NUM_OF_COLS; ++i) {
gpio_init(col_pins[i]);
gpio_set_dir(col_pins[i], GPIO_IN);
}
// Btns
#if NUM_OF_BTNS > 0
for(uint32_t i = 0; i < NUM_OF_BTNS; ++i) {
gpio_init(btn_pins[i]);
gpio_set_dir(btn_pins[i], GPIO_IN);
gpio_pull_up(btn_pins[i]);
}
#endif
}

53
app/keyboard.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
enum key_state
{
KEY_STATE_IDLE = 0,
KEY_STATE_PRESSED,
KEY_STATE_HOLD,
KEY_STATE_RELEASED,
};
#define KEY_JOY_UP 0x01
#define KEY_JOY_DOWN 0x02
#define KEY_JOY_LEFT 0x03
#define KEY_JOY_RIGHT 0x04
#define KEY_JOY_CENTER 0x05
#define KEY_BTN_LEFT1 0x06
#define KEY_BTN_RIGHT1 0x07
// 0x08 - BACKSPACE
// 0x09 - TAB
// 0x0A - NEW LINE
// 0x0D - CARRIAGE RETURN
#define KEY_BTN_LEFT2 0x11
#define KEY_BTN_RIGHT2 0x12
#define KEY_MOD_ALT 0x1A
#define KEY_MOD_SHL 0x1B
#define KEY_MOD_SHR 0x1C
#define KEY_MOD_SYM 0x1D
struct key_callback
{
void (*func)(char, enum key_state);
struct key_callback *next;
};
struct key_lock_callback
{
void (*func)(bool, bool);
struct key_lock_callback *next;
};
void keyboard_task(void);
void keyboard_add_key_callback(struct key_callback *callback);
void keyboard_add_lock_callback(struct key_lock_callback *callback);
bool keyboard_get_capslock(void);
bool keyboard_get_numlock(void);
void keyboard_init(void);

62
app/main.c Normal file
View File

@ -0,0 +1,62 @@
#include <pico/stdlib.h>
#include <stdio.h>
#include <tusb.h>
#include "backlight.h"
#include "debug.h"
#include "gpioexp.h"
#include "interrupt.h"
#include "keyboard.h"
#include "puppet_i2c.h"
#include "reg.h"
#include "touchpad.h"
#include "usb.h"
// since the SDK doesn't support per-GPIO irq, we use this global irq and forward it
static void gpio_irq(uint gpio, uint32_t events)
{
// printf("%s: gpio %d, events 0x%02X\r\n", __func__, gpio, events);
touchpad_gpio_irq(gpio, events);
gpioexp_gpio_irq(gpio, events);
}
// TODO: Microphone
int main(void)
{
// The here order is important because it determines callback call order
usb_init();
#ifndef NDEBUG
debug_init();
#endif
reg_init();
backlight_init();
gpioexp_init();
keyboard_init();
touchpad_init();
interrupt_init();
puppet_i2c_init();
// For now, the `gpio` param is ignored and all enabled GPIOs generate the irq
gpio_set_irq_enabled_with_callback(0xFF, 0, true, &gpio_irq);
#ifndef NDEBUG
printf("Starting main loop\r\n");
#endif
while (true) {
keyboard_task();
// tud_task();
}
return 0;
}

210
app/puppet_i2c.c Normal file
View File

@ -0,0 +1,210 @@
#include "puppet_i2c.h"
#include "app_config.h"
#include "backlight.h"
#include "fifo.h"
#include "gpioexp.h"
#include "keyboard.h"
#include "reg.h"
#include <hardware/i2c.h>
#include <hardware/irq.h>
#include <pico/stdlib.h>
#include <RP2040.h>
#include <stdio.h>
#define WRITE_MASK (1 << 7)
#define REG_ID_INVALID 0x00
static i2c_inst_t *i2c_instances[2] = { i2c0, i2c1 };
static struct
{
i2c_inst_t *i2c;
struct
{
uint8_t reg;
uint8_t data;
} read_buffer;
uint8_t write_buffer[2];
uint8_t write_len;
} self;
static void process_read(void)
{
const bool is_write = (self.read_buffer.reg & WRITE_MASK);
const uint8_t reg = (self.read_buffer.reg & ~WRITE_MASK);
// printf("read complete, is_write: %d, reg: 0x%02X\r\n", is_write, reg);
switch (reg) {
// common R/W registers
case REG_ID_CFG:
case REG_ID_INT:
case REG_ID_DEB:
case REG_ID_FRQ:
case REG_ID_BKL:
case REG_ID_BK2:
case REG_ID_GIC:
case REG_ID_GIN:
case REG_ID_HLD:
case REG_ID_ADR:
case REG_ID_IND:
case REG_ID_CF2:
{
if (is_write) {
reg_set_value(reg, self.read_buffer.data);
switch (reg) {
case REG_ID_BKL:
case REG_ID_BK2:
backlight_sync();
break;
case REG_ID_ADR:
puppet_i2c_sync_address();
break;
default:
break;
}
} else {
self.write_buffer[0] = reg_get_value(reg);
self.write_len = sizeof(uint8_t);
}
break;
}
// special R/W registers
case REG_ID_DIR: // gpio direction
case REG_ID_PUE: // gpio input pull enable
case REG_ID_PUD: // gpio input pull direction
{
if (is_write) {
switch (reg) {
case REG_ID_DIR:
gpioexp_update_dir(self.read_buffer.data);
break;
case REG_ID_PUE:
gpioexp_update_pue_pud(self.read_buffer.data, reg_get_value(REG_ID_PUD));
break;
case REG_ID_PUD:
gpioexp_update_pue_pud(reg_get_value(REG_ID_PUE), self.read_buffer.data);
break;
}
} else {
self.write_buffer[0] = reg_get_value(reg);
self.write_len = sizeof(uint8_t);
}
break;
}
case REG_ID_GIO: // gpio value
{
if (is_write) {
gpioexp_set_value(self.read_buffer.data);
} else {
self.write_buffer[0] = gpioexp_get_value();
self.write_len = sizeof(uint8_t);
}
break;
}
// read-only registers
case REG_ID_TOX:
case REG_ID_TOY:
self.write_buffer[0] = reg_get_value(reg);
self.write_len = sizeof(uint8_t);
reg_set_value(reg, 0);
break;
case REG_ID_VER:
self.write_buffer[0] = VER_VAL;
self.write_len = sizeof(uint8_t);
break;
case REG_ID_KEY:
self.write_buffer[0] = fifo_count();
self.write_buffer[0] |= keyboard_get_numlock() ? KEY_NUMLOCK : 0x00;
self.write_buffer[0] |= keyboard_get_capslock() ? KEY_CAPSLOCK : 0x00;
self.write_len = sizeof(uint8_t);
break;
case REG_ID_FIF:
{
const struct fifo_item item = fifo_dequeue();
self.write_buffer[0] = (uint8_t)item.state;
self.write_buffer[1] = (uint8_t)item.key;
self.write_len = sizeof(uint8_t) * 2;
break;
}
case REG_ID_RST:
NVIC_SystemReset();
break;
}
}
static void irq_handler(void)
{
// the controller sent data
if (self.i2c->hw->intr_stat & I2C_IC_INTR_MASK_M_RX_FULL_BITS) {
if (self.read_buffer.reg == REG_ID_INVALID) {
self.read_buffer.reg = self.i2c->hw->data_cmd & 0xff;
if (self.read_buffer.reg & WRITE_MASK) {
// it'sq a reg write, we need to wait for the second byte before we process
return;
}
} else {
self.read_buffer.data = self.i2c->hw->data_cmd & 0xff;
}
process_read();
// ready for the next operation
self.read_buffer.reg = REG_ID_INVALID;
return;
}
// the controller requested a read
if (self.i2c->hw->intr_stat & I2C_IC_INTR_MASK_M_RD_REQ_BITS) {
i2c_write_raw_blocking(self.i2c, self.write_buffer, self.write_len);
self.i2c->hw->clr_rd_req;
return;
}
}
void puppet_i2c_sync_address(void)
{
i2c_set_slave_mode(self.i2c, true, reg_get_value(REG_ID_ADR));
}
void puppet_i2c_init(void)
{
// determine the instance based on SCL pin, hope you didn't screw up the SDA pin!
self.i2c = i2c_instances[(PIN_PUPPET_SCL / 2) % 2];
i2c_init(self.i2c, 100 * 1000);
puppet_i2c_sync_address();
gpio_set_function(PIN_PUPPET_SDA, GPIO_FUNC_I2C);
gpio_pull_up(PIN_PUPPET_SDA);
gpio_set_function(PIN_PUPPET_SCL, GPIO_FUNC_I2C);
gpio_pull_up(PIN_PUPPET_SCL);
// irq when the controller sends data, and when it requests a read
self.i2c->hw->intr_mask = I2C_IC_INTR_MASK_M_RD_REQ_BITS | I2C_IC_INTR_MASK_M_RX_FULL_BITS;
const int irq = I2C0_IRQ + i2c_hw_index(self.i2c);
irq_set_exclusive_handler(irq, irq_handler);
irq_set_enabled(irq, true);
}

5
app/puppet_i2c.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
void puppet_i2c_sync_address(void);
void puppet_i2c_init(void);

73
app/reg.c Normal file
View File

@ -0,0 +1,73 @@
#include "reg.h"
#include "touchpad.h"
#include <stdio.h>
static struct
{
uint8_t regs[REG_ID_LAST];
} self;
static void touch_cb(int8_t x, int8_t y)
{
self.regs[REG_ID_TOX] = x;
self.regs[REG_ID_TOY] = y;
}
static struct touch_callback touch_callback =
{
.func = touch_cb
};
uint8_t reg_get_value(enum reg_id reg)
{
return self.regs[reg];
}
void reg_set_value(enum reg_id reg, uint8_t value)
{
#ifndef NDEBUG
printf("%s: reg: 0x%02X, val: 0x%02X\r\n", __func__, reg, value);
#endif
self.regs[reg] = value;
}
bool reg_is_bit_set(enum reg_id reg, uint8_t bit)
{
return self.regs[reg] & bit;
}
void reg_set_bit(enum reg_id reg, uint8_t bit)
{
#ifndef NDEBUG
printf("%s: reg: 0x%02X, bit: %d\r\n", __func__, reg, bit);
#endif
self.regs[reg] |= bit;
}
void reg_clear_bit(enum reg_id reg, uint8_t bit)
{
#ifndef NDEBUG
printf("%s: reg: 0x%02X, bit: %d\r\n", __func__, reg, bit);
#endif
self.regs[reg] &= ~bit;
}
void reg_init(void)
{
self.regs[REG_ID_CFG] = CFG_OVERFLOW_INT | CFG_KEY_INT | CFG_USE_MODS;
self.regs[REG_ID_BKL] = 255;
self.regs[REG_ID_DEB] = 10;
self.regs[REG_ID_FRQ] = 10; // ms
self.regs[REG_ID_BK2] = 255;
self.regs[REG_ID_PUD] = 0xFF;
self.regs[REG_ID_HLD] = 30; // 10ms units
self.regs[REG_ID_ADR] = 0x1F;
self.regs[REG_ID_IND] = 1; // ms
self.regs[REG_ID_CF2] = CF2_TOUCH_INT | CF2_USB_KEYB_ON | CF2_USB_MOUSE_ON;
touchpad_add_touch_callback(&touch_callback);
}

76
app/reg.h Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
enum reg_id
{
REG_ID_VER = 0x01, // fw version
REG_ID_CFG = 0x02, // config
REG_ID_INT = 0x03, // interrupt status
REG_ID_KEY = 0x04, // key status
REG_ID_BKL = 0x05, // backlight
REG_ID_DEB = 0x06, // key debounce cfg (not implemented)
REG_ID_FRQ = 0x07, // key poll freq cfg
REG_ID_RST = 0x08, // trigger a reset
REG_ID_FIF = 0x09, // key fifo
REG_ID_BK2 = 0x0A, // backlight 2
REG_ID_DIR = 0x0B, // gpio direction
REG_ID_PUE = 0x0C, // gpio input pull enable
REG_ID_PUD = 0x0D, // gpio input pull direction
REG_ID_GIO = 0x0E, // gpio value
REG_ID_GIC = 0x0F, // gpio interrupt config
REG_ID_GIN = 0x10, // gpio interrupt status
REG_ID_HLD = 0x11, // key hold time cfg (in 10ms units)
REG_ID_ADR = 0x12, // i2c puppet address
REG_ID_IND = 0x13, // interrupt pin assert duration
REG_ID_CF2 = 0x14, // config 2
REG_ID_TOX = 0x15, // touch delta x
REG_ID_TOY = 0x16, // touch delta y
REG_ID_LAST,
};
#define CFG_OVERFLOW_ON (1 << 0) // Should new FIFO entries overwrite oldest ones if FIFO is full
#define CFG_OVERFLOW_INT (1 << 1) // Should FIFO overflow generate an interrupt
#define CFG_CAPSLOCK_INT (1 << 2) // Should toggling caps lock generate interrupts
#define CFG_NUMLOCK_INT (1 << 3) // Should toggling num lock generate interrupts
#define CFG_KEY_INT (1 << 4) // Should key events generate interrupts
#define CFG_PANIC_INT (1 << 5) // Not implemented
#define CFG_REPORT_MODS (1 << 6) // Should Alt, Sym and Shifts be reported as well
#define CFG_USE_MODS (1 << 7) // Should Alt, Sym and Shifts modify the keys reported
#define CF2_TOUCH_INT (1 << 0) // Should touch events generate interrupts
#define CF2_USB_KEYB_ON (1 << 1) // Should key events be sent over USB HID
#define CF2_USB_MOUSE_ON (1 << 2) // Should touch events be sent over USB HID
// TODO? CF2_STICKY_MODS // Pressing and releasing a mod affects next key pressed
#define INT_OVERFLOW (1 << 0)
#define INT_CAPSLOCK (1 << 1)
#define INT_NUMLOCK (1 << 2)
#define INT_KEY (1 << 3)
#define INT_PANIC (1 << 4)
#define INT_GPIO (1 << 5)
#define INT_TOUCH (1 << 6)
// Future me: If we need more INT_*, add a INT2 and use (1 << 7) here as indicator that the info is in INT2
#define KEY_CAPSLOCK (1 << 5) // Caps lock status
#define KEY_NUMLOCK (1 << 6) // Num lock status
#define KEY_COUNT_MASK 0x1F
#define DIR_OUTPUT 0
#define DIR_INPUT 1
#define PUD_DOWN 0
#define PUD_UP 1
#define VER_VAL ((VERSION_MAJOR << 4) | (VERSION_MINOR << 0))
uint8_t reg_get_value(enum reg_id reg);
void reg_set_value(enum reg_id reg, uint8_t value);
bool reg_is_bit_set(enum reg_id reg, uint8_t bit);
void reg_set_bit(enum reg_id reg, uint8_t bit);
void reg_clear_bit(enum reg_id reg, uint8_t bit);
void reg_init(void);

128
app/touchpad.c Normal file
View File

@ -0,0 +1,128 @@
#include "touchpad.h"
#include <hardware/i2c.h>
#include <pico/binary_info.h>
#include <pico/stdlib.h>
#include <stdio.h>
#define DEV_ADDR 0x3B
#define REG_PID 0x00
#define REG_REV 0x01
#define REG_MOTION 0x02
#define REG_DELTA_X 0x03
#define REG_DELTA_Y 0x04
#define REG_DELTA_XY_H 0x05
#define REG_CONFIG 0x11
#define REG_OBSERV 0x2E
#define REG_MBURST 0x42
#define BIT_MOTION_MOT (1 << 7)
#define BIT_MOTION_OVF (1 << 4)
#define BIT_CONFIG_HIRES (1 << 7)
#define BIT_OBSERV_RUN (0 << 6)
#define BIT_OBSERV_REST1 (1 << 6)
#define BIT_OBSERV_REST2 (2 << 6)
#define BIT_OBSERV_REST3 (3 << 6)
static i2c_inst_t *i2c_instances[2] = { i2c0, i2c1 };
static struct
{
struct touch_callback *callbacks;
i2c_inst_t *i2c;
} self;
static uint8_t read_register8(uint8_t reg)
{
uint8_t val;
i2c_write_blocking(self.i2c, DEV_ADDR, &reg, sizeof(reg), true);
i2c_read_blocking(self.i2c, DEV_ADDR, &val, sizeof(val), false);
return val;
}
//static void write_register8(uint8_t reg, uint8_t val)
//{
// uint8_t buffer[2] = { reg, val };
// i2c_write_blocking(self.i2c, DEV_ADDR, buffer, sizeof(buffer), false);
//}
void touchpad_gpio_irq(uint gpio, uint32_t events)
{
if (gpio != PIN_TP_MOTION)
return;
if (!(events & GPIO_IRQ_EDGE_FALL))
return;
const uint8_t motion = read_register8(REG_MOTION);
if (motion & BIT_MOTION_MOT) {
int8_t x = read_register8(REG_DELTA_X);
int8_t y = read_register8(REG_DELTA_Y);
x = ((x < 127) ? x : (x - 256)) * -1;
y = ((y < 127) ? y : (y - 256));
if (self.callbacks) {
struct touch_callback *cb = self.callbacks;
while (cb) {
cb->func(x, y);
cb = cb->next;
}
}
}
}
void touchpad_add_touch_callback(struct touch_callback *callback)
{
// first callback
if (!self.callbacks) {
self.callbacks = callback;
return;
}
// find last and insert after
struct touch_callback *cb = self.callbacks;
while (cb->next)
cb = cb->next;
cb->next = callback;
}
void touchpad_init(void)
{
// determine the instance based on SCL pin, hope you didn't screw up the SDA pin!
self.i2c = i2c_instances[(PIN_SCL / 2) % 2];
i2c_init(self.i2c, 100 * 1000);
gpio_set_function(PIN_SDA, GPIO_FUNC_I2C);
gpio_pull_up(PIN_SDA);
gpio_set_function(PIN_SCL, GPIO_FUNC_I2C);
gpio_pull_up(PIN_SCL);
// Make the I2C pins available to picotool
bi_decl(bi_2pins_with_func(PIN_SDA, PIN_SCL, GPIO_FUNC_I2C));
gpio_init(PIN_TP_SHUTDOWN);
gpio_set_dir(PIN_TP_SHUTDOWN, GPIO_OUT);
gpio_put(PIN_TP_SHUTDOWN, 0);
gpio_init(PIN_TP_MOTION);
gpio_set_dir(PIN_TP_MOTION, GPIO_IN);
gpio_set_irq_enabled(PIN_TP_MOTION, GPIO_IRQ_EDGE_FALL, true);
gpio_init(PIN_TP_RESET);
gpio_set_dir(PIN_TP_RESET, GPIO_OUT);
gpio_put(PIN_TP_RESET, 0);
sleep_ms(100);
gpio_put(PIN_TP_RESET, 1);
}

16
app/touchpad.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <stdbool.h>
#include <sys/types.h>
struct touch_callback
{
void (*func)(int8_t, int8_t);
struct touch_callback *next;
};
void touchpad_gpio_irq(uint gpio, uint32_t events);
void touchpad_add_touch_callback(struct touch_callback *callback);
void touchpad_init(void);

31
app/tusb_config.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
enum
{
USB_ITF_KEYBOARD = 0,
USB_ITF_MOUSE,
USB_ITF_CDC,
USB_ITF_CDC2,
USB_ITF_MAX,
};
#define BOARD_DEVICE_RHPORT_NUM 0
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
#define CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#define CFG_TUD_ENDPOINT0_SIZE 64
#define CFG_TUD_HID 2
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
#define CFG_TUD_HID_EP_BUFSIZE 8
#define CFG_TUD_CDC_RX_BUFSIZE 256
#define CFG_TUD_CDC_TX_BUFSIZE 256

147
app/usb.c Normal file
View File

@ -0,0 +1,147 @@
#include "usb.h"
#include "keyboard.h"
#include "touchpad.h"
#include "reg.h"
#include <hardware/irq.h>
#include <pico/mutex.h>
#include <tusb.h>
#define USB_LOW_PRIORITY_IRQ 31
#define USB_TASK_INTERVAL_US 1000
static struct
{
mutex_t mutex;
bool mouse_moved;
uint8_t mouse_btn;
} self;
// TODO: Should mods always be sent?
// TODO: What about Ctrl?
// TODO: What should L1, L2, R1, R2 do
// TODO: Should touch send arrow keys as an option?
static void low_priority_worker_irq(void)
{
if (mutex_try_enter(&self.mutex, NULL)) {
tud_task();
mutex_exit(&self.mutex);
}
}
static int64_t timer_task(alarm_id_t id, void *user_data)
{
(void)id;
(void)user_data;
irq_set_pending(USB_LOW_PRIORITY_IRQ);
return USB_TASK_INTERVAL_US;
}
static void key_cb(char key, enum key_state state)
{
if (tud_hid_n_ready(USB_ITF_KEYBOARD) && reg_is_bit_set(REG_ID_CF2, CF2_USB_KEYB_ON)) {
uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE };
uint8_t keycode[6] = { 0 };
uint8_t modifier = 0;
if (state == KEY_STATE_PRESSED) {
if (conv_table[(int)key][0])
modifier = KEYBOARD_MODIFIER_LEFTSHIFT;
keycode[0] = conv_table[(int)key][1];
// Fixup: Enter instead of Return
if (key == '\n')
keycode[0] = HID_KEY_ENTER;
}
tud_hid_n_keyboard_report(USB_ITF_KEYBOARD, 0, modifier, keycode);
}
if (tud_hid_n_ready(USB_ITF_MOUSE) && reg_is_bit_set(REG_ID_CF2, CF2_USB_MOUSE_ON)) {
if (key == KEY_JOY_CENTER) {
if (state == KEY_STATE_PRESSED) {
self.mouse_btn = MOUSE_BUTTON_LEFT;
self.mouse_moved = false;
tud_hid_n_mouse_report(USB_ITF_MOUSE, 0, MOUSE_BUTTON_LEFT, 0, 0, 0, 0);
} else if ((state == KEY_STATE_HOLD) && !self.mouse_moved) {
self.mouse_btn = MOUSE_BUTTON_RIGHT;
tud_hid_n_mouse_report(USB_ITF_MOUSE, 0, MOUSE_BUTTON_RIGHT, 0, 0, 0, 0);
} else if (state == KEY_STATE_RELEASED) {
self.mouse_btn = 0x00;
tud_hid_n_mouse_report(USB_ITF_MOUSE, 0, 0x00, 0, 0, 0, 0);
}
}
}
}
static struct key_callback key_callback =
{
.func = key_cb
};
static void touch_cb(int8_t x, int8_t y)
{
if (!tud_hid_n_ready(USB_ITF_MOUSE) || !reg_is_bit_set(REG_ID_CF2, CF2_USB_MOUSE_ON))
return;
self.mouse_moved = true;
tud_hid_n_mouse_report(USB_ITF_MOUSE, 0, self.mouse_btn, x, y, 0, 0);
}
static struct touch_callback touch_callback =
{
.func = touch_cb
};
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
// TODO not Implemented
(void)itf;
(void)report_id;
(void)report_type;
(void)buffer;
(void)reqlen;
printf("%s: itf: %d, report id: %d, type: %d, len: %d\r\n", __func__, itf, report_id, report_type, reqlen);
return 0;
}
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
// TODO set LED based on CAPLOCK, NUMLOCK etc...
(void)itf;
(void)report_id;
(void)report_type;
(void)buffer;
(void)bufsize;
printf("%s: itf: %d, report id: %d, type: %d, size: %d\r\n", __func__, itf, report_id, report_type, bufsize);
}
void usb_init(void)
{
tusb_init();
keyboard_add_key_callback(&key_callback);
touchpad_add_touch_callback(&touch_callback);
// create a new interrupt to call tud_task, and trigger that irq from a timer
irq_set_exclusive_handler(USB_LOW_PRIORITY_IRQ, low_priority_worker_irq);
irq_set_enabled(USB_LOW_PRIORITY_IRQ, true);
mutex_init(&self.mutex);
add_alarm_in_us(USB_TASK_INTERVAL_US, timer_task, NULL, true);
}
mutex_t *usb_get_mutex(void)
{
return &self.mutex;
}

7
app/usb.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
typedef struct mutex mutex_t;
mutex_t *usb_get_mutex(void);
void usb_init(void);

117
app/usb_descriptors.c Normal file
View File

@ -0,0 +1,117 @@
#include <tusb.h>
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_HID_DESC_LEN + TUD_CDC_DESC_LEN)
#define EPNUM_HID_KEYBOARD 0x81
#define EPNUM_HID_MOUSE 0x82
#define EPNUM_CDC_CMD 0x83
#define EPNUM_CDC_IN 0x84
#define EPNUM_CDC_OUT 0x02
#define CDC_CMD_MAX_SIZE 8
#define CDC_IN_OUT_MAX_SIZE 64
static uint16_t temp_string[32];
char const *string_descriptors[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"Solder Party", // 1: Manufacturer
USB_PRODUCT, // 2: Product
"123456", // 3: Serials, should use chip ID
"Keyboard Interface", // 4: Interface 1 String
"Mouse Interface", // 5: Interface 2 String
"Board CDC", // 6: Interface 3 String
};
tusb_desc_device_t const device_descriptor =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = 0x1000,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
uint8_t const hid_keyboard_descriptor[] =
{
TUD_HID_REPORT_DESC_KEYBOARD()
};
uint8_t const hid_mouse_descriptor[] =
{
TUD_HID_REPORT_DESC_MOUSE()
};
uint8_t const config_descriptor[] =
{
TUD_CONFIG_DESCRIPTOR(1, USB_ITF_MAX, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
TUD_HID_DESCRIPTOR(USB_ITF_KEYBOARD, 4, HID_ITF_PROTOCOL_NONE, sizeof(hid_keyboard_descriptor), EPNUM_HID_KEYBOARD, CFG_TUD_HID_EP_BUFSIZE, 10),
TUD_HID_DESCRIPTOR(USB_ITF_MOUSE, 5, HID_ITF_PROTOCOL_NONE, sizeof(hid_mouse_descriptor), EPNUM_HID_MOUSE, CFG_TUD_HID_EP_BUFSIZE, 10),
TUD_CDC_DESCRIPTOR(USB_ITF_CDC, 6, EPNUM_CDC_CMD, CDC_CMD_MAX_SIZE, EPNUM_CDC_OUT, EPNUM_CDC_IN, CDC_IN_OUT_MAX_SIZE),
};
uint8_t const *tud_descriptor_device_cb(void)
{
return (uint8_t const*)&device_descriptor;
}
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf)
{
if (itf == USB_ITF_KEYBOARD)
return hid_keyboard_descriptor;
if (itf == USB_ITF_MOUSE)
return hid_mouse_descriptor;
return NULL;
}
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
(void) index;
return config_descriptor;
}
uint16_t const *tud_descriptor_string_cb(uint8_t idx, uint16_t langid)
{
(void) langid;
if (idx == 0) {
temp_string[0] = (TUSB_DESC_STRING << 8 ) | (2 * sizeof(uint16_t));
memcpy(&temp_string[1], string_descriptors[0], 2);
return temp_string;
}
if (!(idx < sizeof(string_descriptors) / sizeof(string_descriptors[0])))
return NULL;
const char *str = string_descriptors[idx];
uint8_t size = strlen(str);
if (size > 31)
size = 31;
// Convert ASCII string into UTF-16
for(uint8_t i = 0; i < size; ++i)
temp_string[1 + i] = str[i];
temp_string[0] = (TUSB_DESC_STRING << 8 ) | ((size + 1) * sizeof(uint16_t));
return temp_string;
}

View File

@ -0,0 +1,54 @@
#pragma once
#define USB_PID 0x4009
#define USB_VID 0xABBA
#define USB_PRODUCT "BBQ20KBD"
#define PIN_INT 0
#define PIN_BKL 25
#define PIN_SDA 18
#define PIN_SCL 23
#define PIN_TP_RESET 16
#define PIN_TP_MOTION 22
#define PIN_TP_SHUTDOWN 24
#define PIN_PUPPET_SDA 28
#define PIN_PUPPET_SCL 29
#define NUM_OF_ROWS 7
#define PINS_ROWS \
1, \
2, \
3, \
4, \
5, \
6, \
7
#define NUM_OF_COLS 6
#define PINS_COLS \
8, \
9, \
14, \
13, \
12, \
11
#define NUM_OF_BTNS 1
#define PINS_BTNS \
10,
#define BTN_KEYS \
{ KEY_BTN_RIGHT2 },
#define PIN_GPIOEXP0 15
#define PIN_GPIOEXP1 17
#define PIN_GPIOEXP2 19
#define PIN_GPIOEXP3 21
#define PIN_GPIOEXP4 26
#define PICO_DEFAULT_UART 1
#define PICO_DEFAULT_UART_TX_PIN 20
//{ MP_ROM_QSTR(MP_QSTR_MIC), MP_ROM_PTR(&pin_GPIO27) },