pocket HID: let PIOs handle the LED backlight; introduce hue/value setting via trackball;

This commit is contained in:
Lukas F. Hartmann 2023-01-12 15:42:40 +01:00
parent edd32e9df1
commit 71d93c3ab6
4 changed files with 356 additions and 104 deletions

View file

@ -28,7 +28,7 @@ target_include_directories(${PROJECT} PUBLIC
# in hw/bsp/FAMILY/family.cmake for details.
family_configure_device_example(${PROJECT})
target_link_libraries(${PROJECT} PRIVATE pico_stdlib pico_multicore tinyusb_device tinyusb_board hardware_pwm hardware_i2c)
target_link_libraries(${PROJECT} PRIVATE pico_stdlib pico_multicore tinyusb_device tinyusb_board hardware_pwm hardware_i2c hardware_pio)
target_compile_definitions(${PROJECT} PUBLIC
PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64

View file

@ -40,6 +40,8 @@
#include "usb_descriptors.h"
#include "oled.h"
#include "ws2812.pio.h"
#define KBD_VARIANT_QWERTY_US
#define KBD_COLS 12
#define KBD_ROWS 6
@ -81,9 +83,15 @@
static uint8_t pressed_scancodes[MAX_SCANCODES] = {0,0,0,0,0,0};
static int pressed_keys = 0;
static volatile uint32_t led_value = 0;
void hid_task(void);
void led_set(uint32_t rgb);
void led_task(uint32_t rgb);
void led_core(void);
void led_mod_hue(int d);
void led_mod_brightness(int d);
void led_set_brightness(int b);
void led_cycle_hue();
int process_keyboard(uint8_t* resulting_scancodes);
#define UART_ID uart1
@ -124,17 +132,17 @@ void on_uart_rx() {
if (c == '\n') {
// TODO hack
if (uart_rx_i>6) {
gfx_clear();
//gfx_poke_str(0, 3, uart_rx_line);
gfx_clear();
//gfx_poke_str(0, 3, uart_rx_line);
// cut off after 4 digits
uart_rx_line[4] = 0;
float percentage = ((float)atoi(uart_rx_line))/(float)10.0;
char batinfo[32];
sprintf(batinfo, " %.1f%%", (double)percentage);
insert_bat_icon(batinfo, 0, percentage);
gfx_poke_str(0, 0, batinfo);
gfx_flush();
char batinfo[32];
sprintf(batinfo, " %.1f%%", (double)percentage);
insert_bat_icon(batinfo, 0, percentage);
gfx_poke_str(0, 0, batinfo);
gfx_flush();
}
uart_rx_i = 0;
}
@ -241,9 +249,14 @@ int main(void)
buf[1] = 0x01;
i2c_write_blocking(i2c0, ADDR_SENSOR, buf, 2, false);
//multicore_launch_core1(led_core);
led_task(0x000000);
PIO pio = pio0;
int sm = 0;
uint offset = pio_add_program(pio, &ws2812_program);
ws2812_program_init(pio, sm, offset, PIN_LEDS, 800000, false);
led_set_brightness(0x80);
gfx_init(false);
anim_hello();
@ -292,7 +305,7 @@ void tud_resume_cb(void)
int __attribute__((optimize("Os"))) delay300ns() {
// ~300ns
asm volatile(
"mov r0, #6\n" // 1 cycle (was 10)
"mov r0, #9\n" // 1 cycle (was 10)
"loop1: sub r0, r0, #1\n" // 1 cycle
"bne loop1\n" // 2 cycles if loop taken, 1 if not
);
@ -402,62 +415,23 @@ int process_keyboard(uint8_t* resulting_scancodes) {
// Is circle key pressed?
// FIXME HACK
if (keycode == KEY_COMPOSE && (matrix_state[0]
|| matrix_state[1]
|| matrix_state[2]
|| matrix_state[3]
|| matrix_state[4]
|| matrix_state[5]
|| matrix_state[6]
|| matrix_state[7]
|| matrix_state[8]
|| matrix_state[11]
if (keycode == KEY_COMPOSE &&
( matrix_state[1]
|| matrix_state[10]
) && !command_sent) {
if (matrix_state[0]) {
// ESC
if (matrix_state[1]) {
// 1
//remote_turn_som_power_on();
uart_puts(UART_ID, "1p\r\n");
led_task(0x010101);
uart_puts(UART_ID, "\r\n1p\r\n");
led_set_brightness(10);
command_sent = 1;
//anim_hello();
} else if (matrix_state[11]) {
} else if (matrix_state[10]) {
//remote_turn_som_power_off();
led_task(0x000000);
uart_puts(UART_ID, "0p\r\n");
led_set_brightness(0);
uart_puts(UART_ID, "\r\n0p\r\n");
command_sent = 1;
} else if (matrix_state[1]) {
// 1
led_task(0x444444);
command_sent = 1;
} else if (matrix_state[2]) {
// 2
led_task(0x222222);
command_sent = 1;
} else if (matrix_state[3]) {
// 3
led_task(0x111111);
command_sent = 1;
} else if (matrix_state[4]) {
// 4
led_task(0x010101);
command_sent = 1;
} else if (matrix_state[5]) {
// 5
led_task(0x220022);
command_sent = 1;
} else if (matrix_state[6]) {
// 6
led_task(0x110011);
command_sent = 1;
} else if (matrix_state[7]) {
// 7
led_task(0x000022);
command_sent = 1;
} else if (matrix_state[8]) {
// 8
led_task(0x000000);
command_sent = 1;
}
}
} else if (keycode == HID_KEY_EXECUTE) {
fn_key = 1;
//active_matrix = matrix_fn;
@ -592,6 +566,17 @@ static void send_hid_report(uint8_t report_id)
} else {
tud_hid_mouse_report(REPORT_ID_MOUSE, (uint8_t)buttons, -2*nx, -2*ny, 0, 0);
}
// HACK hue/value wheel
if (matrix_state[KBD_COLS*4+0]) {
if (ny) {
led_mod_brightness(ny);
}
if (nx) {
led_mod_hue(nx);
}
}
} else {
//if (buttons != prev_buttons) {
tud_hid_mouse_report(REPORT_ID_MOUSE, (uint8_t)buttons, 0, 0, 0, 0);
@ -655,47 +640,15 @@ static void send_hid_report(uint8_t report_id)
}
}
int led_counter = 0;
int rgb_channel = 0;
int rgb_val = 0;
void led_task(uint32_t color) {
uint32_t pixel_grb = color;
void led_task(uint32_t rgb) {
uint32_t color = rgb;
for (int j=0; j<4; j++) {
for (int y=0; y<6; y++) {
int w = 12;
if (y==5) w = 4;
for (int x=0; x<w; x++) {
uint32_t bits = color;
for (int i=23; i>=0; i--) {
if (bits & (1<<23)) {
// one
gpio_put(PIN_LEDS, 1);
delay300ns();
delay300ns();
delay300ns();
gpio_put(PIN_LEDS, 0);
// ~600ms delay
delay300ns();
delay300ns();
} else {
// zero
gpio_put(PIN_LEDS, 1);
delay300ns();
gpio_put(PIN_LEDS, 0);
// ~1.2ms delay
delay300ns();
delay300ns();
delay300ns();
delay300ns();
//delay300ns();
}
bits <<= 1;
}
}
}
for (int y=0; y<6; y++) {
int w = 12;
if (y==5) w = 4;
for (int x=0; x<w; x++) {
pio_sm_put_blocking(pio0, 0, pixel_grb << 8u);
}
}
}
@ -724,9 +677,109 @@ void hid_task(void)
}
}
void led_core() {
int led_brightness = 0;
int led_hue = 0;
typedef struct RgbColor
{
unsigned char r;
unsigned char g;
unsigned char b;
} RgbColor;
typedef struct HsvColor
{
unsigned char h;
unsigned char s;
unsigned char v;
} HsvColor;
RgbColor HsvToRgb(HsvColor hsv)
{
RgbColor rgb;
unsigned char region, remainder, p, q, t;
if (hsv.s == 0)
{
rgb.r = hsv.v;
rgb.g = hsv.v;
rgb.b = hsv.v;
return rgb;
}
region = hsv.h / 43;
remainder = (hsv.h - (region * 43)) * 6;
p = (hsv.v * (255 - hsv.s)) >> 8;
q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
rgb.r = hsv.v; rgb.g = t; rgb.b = p;
break;
case 1:
rgb.r = q; rgb.g = hsv.v; rgb.b = p;
break;
case 2:
rgb.r = p; rgb.g = hsv.v; rgb.b = t;
break;
case 3:
rgb.r = p; rgb.g = q; rgb.b = hsv.v;
break;
case 4:
rgb.r = t; rgb.g = p; rgb.b = hsv.v;
break;
default:
rgb.r = hsv.v; rgb.g = p; rgb.b = q;
break;
}
return rgb;
}
void led_set(uint32_t rgb) {
led_task(rgb);
}
void led_set_hsv() {
HsvColor hsv;
RgbColor rgb;
hsv.h = led_hue;
hsv.s = 0xff;
hsv.v = led_brightness;
rgb = HsvToRgb(hsv);
led_set((rgb.r<<16)|(rgb.g<<8)|(rgb.b));
}
void led_mod_brightness(int d) {
led_brightness+=d;
if (led_brightness>0xff) led_brightness = 0xff;
if (led_brightness<0) led_brightness = 0;
led_set_hsv();
}
void led_mod_hue(int d) {
led_hue+=d;
if (led_hue>0xff) led_hue = 0xff;
if (led_hue<0) led_hue = 0;
led_set_hsv();
}
void led_set_brightness(int b) {
led_brightness = b;
led_set_hsv();
}
void led_cycle_hue() {
led_hue++;
if (led_hue>0xff) led_hue = 0;
led_set_hsv();
}
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID

View file

@ -0,0 +1,85 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.program ws2812
.side_set 1
.define public T1 2
.define public T2 5
.define public T3 3
.lang_opt python sideset_init = pico.PIO.OUT_HIGH
.lang_opt python out_init = pico.PIO.OUT_HIGH
.lang_opt python out_shiftdir = 1
.wrap_target
bitloop:
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
do_one:
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
do_zero:
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
.wrap
% c-sdk {
#include "hardware/clocks.h"
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
pio_sm_config c = ws2812_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}
.program ws2812_parallel
.define public T1 2
.define public T2 5
.define public T3 3
.wrap_target
out x, 32
mov pins, !null [T1-1]
mov pins, x [T2-1]
mov pins, null [T3-2]
.wrap
% c-sdk {
#include "hardware/clocks.h"
static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) {
for(uint i=pin_base; i<pin_base+pin_count; i++) {
pio_gpio_init(pio, i);
}
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);
pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_out_pins(&c, pin_base, pin_count);
sm_config_set_set_pins(&c, pin_base, pin_count);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

View file

@ -0,0 +1,114 @@
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
// ------ //
// ws2812 //
// ------ //
#define ws2812_wrap_target 0
#define ws2812_wrap 3
#define ws2812_T1 2
#define ws2812_T2 5
#define ws2812_T3 3
static const uint16_t ws2812_program_instructions[] = {
// .wrap_target
0x6221, // 0: out x, 1 side 0 [2]
0x1123, // 1: jmp !x, 3 side 1 [1]
0x1400, // 2: jmp 0 side 1 [4]
0xa442, // 3: nop side 0 [4]
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program ws2812_program = {
.instructions = ws2812_program_instructions,
.length = 4,
.origin = -1,
};
static inline pio_sm_config ws2812_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + ws2812_wrap_target, offset + ws2812_wrap);
sm_config_set_sideset(&c, 1, false, false);
return c;
}
#include "hardware/clocks.h"
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
pio_sm_config c = ws2812_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
#endif
// --------------- //
// ws2812_parallel //
// --------------- //
#define ws2812_parallel_wrap_target 0
#define ws2812_parallel_wrap 3
#define ws2812_parallel_T1 2
#define ws2812_parallel_T2 5
#define ws2812_parallel_T3 3
static const uint16_t ws2812_parallel_program_instructions[] = {
// .wrap_target
0x6020, // 0: out x, 32
0xa10b, // 1: mov pins, !null [1]
0xa401, // 2: mov pins, x [4]
0xa103, // 3: mov pins, null [1]
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program ws2812_parallel_program = {
.instructions = ws2812_parallel_program_instructions,
.length = 4,
.origin = -1,
};
static inline pio_sm_config ws2812_parallel_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + ws2812_parallel_wrap_target, offset + ws2812_parallel_wrap);
return c;
}
#include "hardware/clocks.h"
static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) {
for(uint i=pin_base; i<pin_base+pin_count; i++) {
pio_gpio_init(pio, i);
}
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);
pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_out_pins(&c, pin_base, pin_count);
sm_config_set_set_pins(&c, pin_base, pin_count);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
#endif