Initial working setup for SR22

This commit is contained in:
2023-03-28 00:12:55 +02:00
parent 56a129eeb5
commit 6b4104c955
6 changed files with 414 additions and 67 deletions

View File

@@ -10,12 +10,18 @@ project(rp2040_hid)
pico_sdk_init()
add_executable(rp2040_hid
src/buttons.c
src/main.c
src/rotary.c
src/sevenseg.c
src/statusleds.c
src/usb_config.c
)
# Add pico_stdlib library which aggregates commonly used features
target_link_libraries(rp2040_hid pico_stdlib pico_unique_id tinyusb_device tinyusb_board)
pico_generate_pio_header(rp2040_hid ${CMAKE_CURRENT_LIST_DIR}/src/sevenseg.pio)
pico_generate_pio_header(rp2040_hid ${CMAKE_CURRENT_LIST_DIR}/src/shift_in.pio)
pico_generate_pio_header(rp2040_hid ${CMAKE_CURRENT_LIST_DIR}/src/statusleds.pio)
target_link_libraries(rp2040_hid pico_stdlib pico_unique_id tinyusb_device tinyusb_board hardware_pio)
target_include_directories(rp2040_hid PRIVATE src)

View File

@@ -1,20 +1,165 @@
device = hid_open(0x1209, 1)
local bit = require("bit")
all_devs, number = create_HID_table()
local the_index
for k, v in pairs(all_devs) do
if v.vendor_id == 0x1209 and v.usage == 0 then
the_path = v.path
--print(v.path)
--print(v.usage)
end
end
local button_offset = 320
local button_hdg_sync = 1
local button_alt_sync = 7
local led_hdg = 0x0001
local led_apr = 0x0002
local led_nav = 0x0004
local led_lvl = 0x0008
local led_alt = 0x0010
local led_fd = 0x0020
local led_vnav = 0x0040
local led_ap = 0x0080
local led_vs = 0x0100
local led_at = 0x0200
local led_flc = 0x0400
local led_spd = 0x0800
local led_n1 = 0x1000
function table.clone(org)
return {table.unpack(org)}
end
function send_app_report(device, hdg, leds, alt, vs, as)
hid_write(device, 2, bit.band(hdg, 0xff), bit.arshift(hdg, 8), bit.band(leds, 0xff), bit.arshift(leds, 8),
bit.band(alt, 0xff), bit.arshift(alt, 8), bit.band(vs, 0xff), bit.arshift(vs, 8),
bit.band(as, 0xff), bit.arshift(as, 8))
end
function build_leds()
local val = 0
if status_hdg == 1 then
val = val + led_hdg
elseif status_hdg == 2 then
val = val + led_nav
end
if status_apr > 0 then
val = val + led_apr
end
if status_alt > 1 then
val = val + led_alt
end
if status_fd == 1 then
val = val + led_fd
elseif status_fd == 2 then
val = val + led_fd + led_ap
end
if status_vnav > 1 then
val = val + led_vnav
end
if status_altmode == 4 then
val = val + led_vs
elseif status_altmode == 5 then
val = val + led_flc
end
return val
end
if the_path ~= nil then
device = hid_open_path(the_path)
end
if device == nil then
print("No device!")
else
dataref("low_vac", "sim/cockpit2/annunciators/low_vacuum", "readable")
local prev_low_vac
function low_vac_indicator()
if low_vac ~= prev_low_vac then
if low_vac > 0 then
hid_write(device, 2, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
else
hid_write(device, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
hid_set_nonblocking(device, 1)
dataref("ap_hdg", "sim/cockpit2/autopilot/heading_dial_deg_mag_pilot", "writable")
dataref("mag_hdg", "sim/cockpit2/gauges/indicators/heading_AHARS_deg_mag_copilot")
dataref("ap_alt", "sim/cockpit2/autopilot/altitude_dial_ft", "writable")
dataref("cur_alt", "sim/cockpit2/gauges/indicators/altitude_ft_pilot")
dataref("ap_vs", "sim/cockpit2/autopilot/vvi_dial_fpm", "writable")
dataref("cur_vvi", "sim/cockpit2/gauges/indicators/vvi_fpm_pilot")
dataref("ap_as", "sim/cockpit2/autopilot/airspeed_dial_kts", "writable")
dataref("cur_ias", "sim/cockpit2/gauges/indicators/airspeed_kts_pilot")
dataref("status_alt", "sim/cockpit2/autopilot/altitude_hold_status")
dataref("status_apr", "sim/cockpit2/autopilot/approach_status")
dataref("status_hdg", "sim/cockpit2/autopilot/heading_mode")
dataref("status_fd", "sim/cockpit2/autopilot/flight_director_mode")
dataref("status_vnav", "sim/cockpit2/autopilot/vnav_status")
dataref("status_altmode", "sim/cockpit2/autopilot/altitude_mode")
send_app_report(device, math.floor(ap_hdg + 0.5), build_leds(), math.floor(ap_alt + 0.5), -2, -2)
local prev_status_altmode = 0
function usb_app()
local send_hdg = -1
local send_alt = -1
local send_vs = -1
local send_as = -1
local app_report = {0xffff, 0xffff, 0xffff, 0xffff}
local app_report_good = false
local loops = 100
-- read app report from hid device, discarding all reports except the latest one to prevent input lag
repeat
local tmp_cnt
local tmp = {}
tmp_cnt, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10] = hid_read(device, 11)
if tmp_cnt >= 11 and tmp[0] == 2 then
app_report = {bit.bor(tmp[1], bit.lshift(tmp[2], 8)),
bit.bor(tmp[5], bit.lshift(tmp[6], 8)),
bit.bor(tmp[7], bit.lshift(tmp[8], 8)),
bit.bor(tmp[9], bit.lshift(tmp[10], 8))}
app_report_good = true
end
prev_low_vac = low_vac
loops = loops - 1
until tmp_cnt == 0 or tmp_cnt == nil or loops == 0
-- autopilot heading
if button(button_offset + button_hdg_sync) and not last_button(button_offset + button_hdg_sync) then
mag_hdg_int = math.floor(mag_hdg + 0.5)
ap_hdg = mag_hdg_int
send_hdg = mag_hdg_int
elseif app_report_good and app_report[1] ~= 0xffff then
ap_hdg = app_report[1]
end
-- autopilot altitude
if button(button_offset + button_alt_sync) and not last_button(button_offset + button_alt_sync) then
cur_alt_int = math.floor(cur_alt + 0.5)
ap_alt = cur_alt_int
send_alt = cur_alt_int
end
if app_report_good and app_report[2] ~= 0xffff then
ap_alt = app_report[2]
end
-- vertical speed
if status_altmode == 4 and prev_status_altmode ~= 4 then
send_vs = math.floor(cur_vvi + 0.5)
elseif status_altmode ~= 4 and prev_status_altmode == 4 then
send_vs = -2
elseif app_report_good and app_report[3] ~= 0xffff then
if bit.band(app_report[3], 0x8000) ~= 0 then
ap_vs = -bit.band(bit.bnot(app_report[3]), 0x7fff) - 1
else
ap_vs = app_report[3]
end
end
-- airspeed
if status_altmode == 5 and prev_status_altmode ~= 5 then
if cur_ias < 64 then
send_as = 64
else
send_as = math.floor(cur_ias + 0.5)
end
elseif status_altmode ~= 5 and prev_status_altmode == 5 then
send_as = -2
elseif app_report_good and app_report[4] ~= 0xffff then
ap_as = app_report[4]
end
send_app_report(device, send_hdg, build_leds(), send_alt, send_vs, send_as)
prev_status_altmode = status_altmode
end
do_every_frame("low_vac_indicator()")
do_every_frame("usb_app()")
end

View File

@@ -1,11 +1,17 @@
#include "buttons.h"
#include "pinmap.h"
#include "rotary.h"
#include "sevenseg.h"
#include "statusleds.h"
#include <stdio.h>
#include <bsp/board.h>
#include <hardware/pio.h>
#include <pico/stdlib.h>
#include <tusb.h>
#include "usb_config.h"
#define BUTTON1_GPIO 2
#define LED1_GPIO 22
PIO pio = pio0;
static void hid_task(void);
@@ -18,20 +24,147 @@ __attribute__((packed)) struct button_report {
uint32_t buttons;
};
__attribute__((packed)) struct app_report {
int16_t hdg;
int16_t leds;
int16_t alt;
int16_t vs;
int16_t as;
};
static struct light_data the_light_data;
static bool show_vs = false, show_as = false;
static volatile unsigned hdg = 0;
static volatile unsigned alt = 0;
static volatile int vs = 0;
static volatile unsigned as = 0;
#define BUTTON_HDG_SYNC 1
#define BUTTON_ALT_SYNC 7
static void update_hdg_display(void)
{
sevenseg_set_digit(0, hdg / 100);
sevenseg_set_digit(1, hdg / 10 % 10);
sevenseg_set_digit(2, hdg % 10);
}
static void update_alt_display(void)
{
int val = alt;
for (int i = 0; i < 5; ++i) {
if (val || i == 0)
sevenseg_set_digit(7 - i, val % 10);
else
sevenseg_set_digit(7 - i, LED_BLANK);
val /= 10;
}
}
static void update_vs_display(void)
{
if (show_vs) {
int val = vs;
unsigned val_abs = val < 0 ? -val : val;
for (int i = 0; i < 3; ++i) {
if (val_abs || i == 0) {
sevenseg_set_digit(11 - i, val_abs % 10);
} else {
sevenseg_set_digit(11 - i, LED_BLANK);
}
val_abs /= 10;
}
if (val >= 1000 || val <= -1000) {
sevenseg_set_digit(8, (val_abs % 10) | (val < 0 ? LED_DECIMAL : 0));
} else if (val < 0) {
sevenseg_set_digit(8, LED_MINUS);
} else {
sevenseg_set_digit(8, LED_BLANK);
}
} else {
for (int i = 0; i < 4; ++i) {
sevenseg_set_digit(11 - i, LED_BLANK);
}
}
}
static void update_as_display(void)
{
if (show_as) {
int val = as;
for (int i = 0; i < 4; ++i) {
if (val || i == 0)
sevenseg_set_digit(15 - i, val % 10);
else
sevenseg_set_digit(15 - i, LED_BLANK);
val /= 10;
}
} else {
for (int i = 0; i < 4; ++i) {
sevenseg_set_digit(15 - i, LED_BLANK);
}
}
}
void rotary_event(int rot_ch, bool ccw)
{
/* if (ccw) */
/* rotary_events = (rotary_events & ~(0x3<<(rot_ch*2))) | (0x1<<(rot_ch*2)); */
/* else */
/* rotary_events = (rotary_events & ~(0x3<<(rot_ch*2))) | (0x3<<(rot_ch*2)); */
/* rotary_events |= 1<<(rot_ch*2 + ccw); */
/* sevenseg_set_digit(0, rot_ch); */
/* sevenseg_set_digit(1, ccw); */
if (rot_ch == 3) {
if (ccw) {
hdg = hdg == 0 ? 359 : hdg - 1;
} else {
hdg = hdg == 359 ? 0 : hdg + 1;
}
update_hdg_display();
} else if (rot_ch == 2) {
if (ccw) {
alt = alt == 0 ? 0 : ((alt + 99) / 100) * 100 - 100;
} else {
alt = alt == 50000 ? 50000 : (alt / 100) * 100 + 100;
}
update_alt_display();
} else if (rot_ch == 1) {
if (ccw) {
vs = vs == -5000 ? -5000 : vs - 100;
} else {
vs = vs == 5000 ? 5000 : vs + 100;
}
update_vs_display();
} else {
// TODO: knots / mach mode switch
if (ccw) {
as = as == 64 ? 64 : as - 1;
} else {
as = as == 205 ? 205 : as + 1;
}
update_as_display();
}
}
int main()
{
setup_default_uart();
tusb_init();
gpio_init(BUTTON1_GPIO);
gpio_set_dir(BUTTON1_GPIO, GPIO_IN);
gpio_pull_up(BUTTON1_GPIO);
rotary_init();
sevenseg_init();
buttons_init();
statusleds_init();
gpio_init(LED1_GPIO);
gpio_set_dir(LED1_GPIO, GPIO_OUT);
gpio_put(LED1_GPIO, true);
update_hdg_display();
update_alt_display();
update_vs_display();
update_as_display();
irq_set_enabled(PIO0_IRQ_1, true);
printf("Hello, world!\n");
while (1) {
@@ -41,6 +174,8 @@ int main()
return 0;
}
static bool send_heading_reports = true, send_alt_reports = true, send_vs_reports = false, send_as_reports = false;
static void hid_task(void)
{
// Poll every 1ms
@@ -51,18 +186,39 @@ static void hid_task(void)
return; // poll interval not elapsed
start_ms += interval_ms;
bool btn = !gpio_get(BUTTON1_GPIO);
if (start_ms % 10 == 0 && send_heading_reports) {
struct app_report report = { .hdg = -1, .alt = -1, .vs = -1, .as = -1 };
if (send_heading_reports)
report.hdg = hdg;
if (send_alt_reports)
report.alt = alt;
if (send_vs_reports)
report.vs = vs;
if (send_as_reports)
report.as = as;
if (tud_hid_ready())
tud_hid_n_report(ITF_NUM_HID, HID_REPORT_APP, &report, sizeof(report));
} else {
static int btns_prev = 0;
unsigned btns = buttons_read() & 0xfffff;
// Remote wakeup
if (tud_suspended() && btn) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
}
if ((btns >> BUTTON_HDG_SYNC) & 1 && !((btns_prev >> BUTTON_HDG_SYNC) & 1))
send_heading_reports = false;
if ((btns >> BUTTON_ALT_SYNC) & 1 && !((btns_prev >> BUTTON_ALT_SYNC) & 1))
send_alt_reports = false;
if (tud_hid_ready()) {
struct button_report report = { .buttons = btn ? GAMEPAD_BUTTON_0 : 0 };
tud_hid_n_report(ITF_NUM_HID, HID_REPORT_GAMEPAD, &report, sizeof(report));
btns_prev = btns;
// Remote wakeup
if (tud_suspended() && btns) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
}
if (tud_hid_ready()) {
struct button_report report = { .buttons = btns };
tud_hid_n_report(ITF_NUM_HID, HID_REPORT_GAMEPAD, &report, sizeof(report));
}
}
}
@@ -70,11 +226,45 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
uint16_t bufsize)
{
//printf("REPORT id %hhu type %d [0] %hhu size %hu\n", report_id, report_type, buffer[0], bufsize);
if (report_id == HID_REPORT_LIGHTS && report_type == HID_REPORT_TYPE_OUTPUT &&
bufsize >= sizeof(struct light_data)) {
size_t i = 0;
memcpy(&the_light_data, buffer, sizeof(the_light_data));
gpio_put(LED1_GPIO, the_light_data.leds[0] > 0x7f ? false : true);
if (report_id == HID_REPORT_APP && report_type == HID_REPORT_TYPE_OUTPUT && bufsize >= 2) {
struct app_report report;
memcpy(&report, buffer, sizeof(report));
if (report.hdg >= 0) {
hdg = report.hdg;
update_hdg_display();
send_heading_reports = true;
}
if (report.alt >= 0) {
alt = report.alt;
update_alt_display();
send_alt_reports = true;
}
if (report.vs != -1) {
if (report.vs == -2) {
// disable vs mode
show_vs = false;
send_vs_reports = false;
} else {
vs = report.vs;
show_vs = true;
send_vs_reports = true;
}
update_vs_display();
}
if (report.as != -1) {
if (report.as == -2) {
show_as = false;
send_as_reports = false;
} else {
as = report.as;
show_as = true;
send_as_reports = true;
}
update_as_display();
}
if (report.leds >= 0) {
statusleds_set(report.leds);
}
}
// echo back anything we received from host
@@ -111,17 +301,13 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t
if (report_type != HID_REPORT_TYPE_INPUT)
return 0;
switch (report_id) {
case HID_REPORT_LIGHTS:
act_len = sizeof(the_light_data) > reqlen ? reqlen : sizeof(the_light_data);
memcpy(buffer, &the_light_data, act_len);
return act_len;
case HID_REPORT_GAMEPAD: {
const bool btn = !gpio_get(BUTTON1_GPIO);
const struct button_report report = { .buttons = btn ? GAMEPAD_BUTTON_0 : 0 };
act_len = sizeof(report) > reqlen ? reqlen : sizeof(report);
memcpy(buffer, &report, act_len);
return act_len;
}
/* case HID_REPORT_GAMEPAD: { */
/* const bool btn = !gpio_get(BUTTON1_GPIO); */
/* const struct button_report report = { .buttons = btn ? GAMEPAD_BUTTON_0 : 0 }; */
/* act_len = sizeof(report) > reqlen ? reqlen : sizeof(report); */
/* memcpy(buffer, &report, act_len); */
/* return act_len; */
/* } */
default:
return 0;
}

View File

@@ -39,7 +39,7 @@ static void rotary_timer_event(uint alarm_num)
state[i] = rotary_state_idle;
if (rot_gpio[i * 2] && !rot_gpio[i * 2 + 1]) {
state[i] = rotary_state_A;
rotary_event(i, true);
rotary_event(i, false);
}
break;
case rotary_state_A:
@@ -47,7 +47,7 @@ static void rotary_timer_event(uint alarm_num)
state[i] = rotary_state_idle;
if (!rot_gpio[i * 2] && rot_gpio[i * 2 + 1]) {
state[i] = rotary_state_B;
rotary_event(i, false);
rotary_event(i, true);
}
break;
}

View File

@@ -6,7 +6,7 @@
#include <tusb.h>
/* clang-format off */
// Gamepad Report Descriptor Template with 32 buttons with following layout
// Gamepad Report Descriptor Template with 28 buttons with following layout
// | Button Map (4 bytes) |
#define RP2040_HID_REPORT_DESC_GAMEPAD(...) \
HID_USAGE_PAGE (HID_USAGE_PAGE_DESKTOP), \
@@ -14,10 +14,10 @@
HID_COLLECTION (HID_COLLECTION_APPLICATION), \
/* Report ID if any */ \
__VA_ARGS__ \
/* 32 bit Button Map */ \
/* 20 bit Button Map */ \
HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), \
HID_USAGE_MIN(1), \
HID_USAGE_MAX(32), \
HID_USAGE_MAX(20), \
HID_LOGICAL_MIN(0), \
HID_LOGICAL_MAX(1), \
HID_REPORT_COUNT(32), \
@@ -25,23 +25,33 @@
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
#define RP2040_HID_REPORT_DESC_LIGHTS(...) \
#define RP2040_HID_REPORT_DESC_APP(...) \
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
HID_USAGE(0x00), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
__VA_ARGS__ \
HID_REPORT_COUNT(16), /*16 button lights */ \
HID_REPORT_SIZE(8), \
HID_LOGICAL_MIN(0x00), \
HID_LOGICAL_MAX_N(0x00ff, 2), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(1), \
HID_USAGE_MAX(16), \
HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_USAGE_MIN(1), \
HID_USAGE_MAX(1), \
HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
HID_REPORT_COUNT(5), \
HID_REPORT_SIZE(16), \
HID_LOGICAL_MIN(-1), \
HID_LOGICAL_MAX(127), \
HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \
HID_USAGE_MIN(1), \
HID_USAGE_MAX(1), \
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
HID_COLLECTION_END
/* HID_REPORT_COUNT(16), /\*16 button lights *\/ \ */
/* HID_REPORT_SIZE(8), \ */
/* HID_LOGICAL_MIN(0x00), \ */
/* HID_LOGICAL_MAX_N(0x00ff, 2), \ */
/* HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \ */
/* HID_USAGE_MIN(1), \ */
/* HID_USAGE_MAX(16), \ */
/* HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ */
/* HID_USAGE_MIN(1), \ */
/* HID_USAGE_MAX(1), \ */
/* HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \ */
/* clang-format on */
@@ -129,7 +139,7 @@ uint8_t const *tud_descriptor_device_cb(void)
}
static uint8_t const desc_hid_report[] = { RP2040_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(HID_REPORT_GAMEPAD)),
RP2040_HID_REPORT_DESC_LIGHTS(HID_REPORT_ID(HID_REPORT_LIGHTS)) };
RP2040_HID_REPORT_DESC_APP(HID_REPORT_ID(HID_REPORT_APP)) };
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN)

View File

@@ -4,4 +4,4 @@
enum { ITF_NUM_HID, ITF_NUM_TOTAL };
enum { HID_REPORT_GAMEPAD = 1, HID_REPORT_LIGHTS };
enum { HID_REPORT_GAMEPAD = 1, HID_REPORT_APP };