mirror of
https://github.com/grymoire/i2c_puppet-Linux.git
synced 2024-12-25 13:08:21 +01:00
Initial import
This commit is contained in:
commit
c9791af550
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build
|
||||
*.txt.user
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal 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
1
3rdparty/pico-sdk
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7
|
20
CMakeLists.txt
Normal file
20
CMakeLists.txt
Normal 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
7
README.md
Normal 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
30
app/CMakeLists.txt
Normal 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
6
app/app_config.h
Normal 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
22
app/backlight.c
Normal 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
6
app/backlight.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void backlight_sync(void);
|
||||
void backlight_init(void);
|
118
app/debug.c
Normal file
118
app/debug.c
Normal 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
3
app/debug.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void debug_init(void);
|
60
app/fifo.c
Normal file
60
app/fifo.c
Normal 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
15
app/fifo.h
Normal 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
279
app/gpioexp.c
Normal 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
20
app/gpioexp.h
Normal 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
106
app/interrupt.c
Normal 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
3
app/interrupt.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void interrupt_init(void);
|
385
app/keyboard.c
Normal file
385
app/keyboard.c
Normal 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
53
app/keyboard.h
Normal 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
62
app/main.c
Normal 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
210
app/puppet_i2c.c
Normal 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
5
app/puppet_i2c.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void puppet_i2c_sync_address(void);
|
||||
|
||||
void puppet_i2c_init(void);
|
73
app/reg.c
Normal file
73
app/reg.c
Normal 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
76
app/reg.h
Normal 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
128
app/touchpad.c
Normal 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, ®, 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
16
app/touchpad.h
Normal 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
31
app/tusb_config.h
Normal 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
147
app/usb.c
Normal 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
7
app/usb.h
Normal 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
117
app/usb_descriptors.c
Normal 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;
|
||||
}
|
54
boards/bbq20kbd_breakout.h
Normal file
54
boards/bbq20kbd_breakout.h
Normal 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) },
|
Loading…
Reference in New Issue
Block a user