Skip to content

Commit

Permalink
Digit Mode for frequency field (#1298)
Browse files Browse the repository at this point in the history
* Remove 'auto' step mode

* Support per-digit edits on the freq field.

* Swizzle instead of raw accessor

* Fix debug ui after swizzle
  • Loading branch information
kallanreed committed Jul 24, 2023
1 parent e2bca9a commit 3514a9a
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 96 deletions.
12 changes: 6 additions & 6 deletions firmware/application/apps/analog_audio_app.hpp
Expand Up @@ -48,9 +48,9 @@ class AMOptionsView : public View {

OptionsField options_config{
{3 * 8, 0 * 16},
6, // number of blanking characters
6, // Max option length
{
// using common messages from freqman.cpp
// Using common messages from freqman_ui.cpp
}};
};

Expand All @@ -65,9 +65,9 @@ class NBFMOptionsView : public View {
};
OptionsField options_config{
{3 * 8, 0 * 16},
4,
3, // Max option length
{
// using common messages from freqman.cpp
// Using common messages from freqman_ui.cpp
}};

Text text_squelch{
Expand All @@ -93,9 +93,9 @@ class WFMOptionsView : public View {
};
OptionsField options_config{
{3 * 8, 0 * 16},
4,
4, // Max option length
{
// using common messages from freqman.cpp
// Using common messages from freqman_ui.cpp
}};
};

Expand Down
10 changes: 5 additions & 5 deletions firmware/application/apps/ui_debug.cpp
Expand Up @@ -251,7 +251,7 @@ void ControlsSwitchesWidget::on_show() {

bool ControlsSwitchesWidget::on_key(const KeyEvent key) {
key_event_mask = 1 << toUType(key);
long_press_key_event_mask = switch_long_press_occurred((size_t)key) ? key_event_mask : 0;
long_press_key_event_mask = key_is_long_pressed(key) ? key_event_mask : 0;
return true;
}

Expand All @@ -264,9 +264,9 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
{32, 64, 16, 16}, // Down
{32, 0, 16, 16}, // Up
{32, 32, 16, 16}, // Select
{96, 0, 16, 16}, // Dfu
{16, 96, 16, 16}, // Encoder phase 0
{48, 96, 16, 16}, // Encoder phase 1
{96, 0, 16, 16}, // Dfu
{96, 64, 16, 16}, // Touch
}};

Expand All @@ -283,9 +283,9 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
{32 + 1, 64 + 1, 16 - 2, 16 - 2}, // Down
{32 + 1, 0 + 1, 16 - 2, 16 - 2}, // Up
{32 + 1, 32 + 1, 16 - 2, 16 - 2}, // Select
{96 + 1, 0 + 1, 16 - 2, 16 - 2}, // Dfu
{16 + 1, 96 + 1, 16 - 2, 16 - 2}, // Encoder phase 0
{48 + 1, 96 + 1, 16 - 2, 16 - 2}, // Encoder phase 1
{96 + 1, 0 + 1, 16 - 2, 16 - 2}, // Dfu
}};

auto switches_raw = control::debug::switches();
Expand Down Expand Up @@ -354,13 +354,13 @@ DebugControlsView::DebugControlsView(NavigationView& nav) {
});

button_done.on_select = [&nav](Button&) {
switches_long_press_enable(0);
set_switches_long_press_config(0);
nav.pop();
};

options_switches_mode.on_change = [this](size_t, OptionsField::value_t v) {
(void)v;
switches_long_press_enable(options_switches_mode.selected_index_value());
set_switches_long_press_config(options_switches_mode.selected_index_value());
};
}

Expand Down
4 changes: 2 additions & 2 deletions firmware/application/hw/debounce.cpp
Expand Up @@ -84,8 +84,8 @@ bool Debounce::feed(const uint8_t bit) {
// Button is being held down and long_press support is enabled for this key:
// if LONG_PRESS_DELAY is reached then finally report that switch is pressed and set flag
// indicating it was a LONG press
// (note that repease_support and long_press support are mutually exclusive)
if (held_time_ == LONG_PRESS_DELAY) {
// (note that repeat_support and long_press support are mutually exclusive)
if (held_time_ >= LONG_PRESS_DELAY) {
long_press_occurred_ = true;
pulse_upon_release_ = 0;
held_time_ = 0;
Expand Down
8 changes: 6 additions & 2 deletions firmware/application/hw/debounce.hpp
Expand Up @@ -31,7 +31,7 @@
// # of timer0 ticks before a held button starts being counted as repeated presses
#define REPEAT_INITIAL_DELAY 250
#define REPEAT_SUBSEQUENT_DELAY 92
#define LONG_PRESS_DELAY 1000
#define LONG_PRESS_DELAY 800

class Debounce {
public:
Expand All @@ -45,7 +45,11 @@ class Debounce {
repeat_enabled_ = true;
}

void set_long_press_support(bool v) {
bool get_long_press_enabled() const {
return long_press_enabled_;
}

void set_long_press_enabled(bool v) {
long_press_enabled_ = v;
}

Expand Down
123 changes: 80 additions & 43 deletions firmware/application/irq_controls.cpp
Expand Up @@ -22,32 +22,33 @@
#include "irq_controls.hpp"

#include "ch.h"
#include "hal.h"

#include "debounce.hpp"
#include "encoder.hpp"
#include "event_m0.hpp"

#include "hal.h"
#include "touch.hpp"
#include "touch_adc.hpp"
#include "encoder.hpp"
#include "debounce.hpp"
#include "utility.hpp"

#include <cstdint>
#include <array>

#include "portapack_io.hpp"

#include "hackrf_hal.hpp"

using namespace hackrf::one;
using namespace portapack;

static Thread* thread_controls_event = NULL;

static std::array<Debounce, 8> switch_debounce;
// Index with the Switch enum.
static std::array<Debounce, 6> switch_debounce;
static std::array<Debounce, 2> encoder_debounce;

static Encoder encoder;
static_assert(std::size(switch_debounce) == toUType(Switch::Dfu) + 1);

static Encoder encoder;
static volatile uint32_t encoder_position{0};

static volatile uint32_t touch_phase{0};

/* TODO: Change how touch scanning works. It produces a decent amount of noise
Expand All @@ -60,11 +61,11 @@ static volatile uint32_t touch_phase{0};
* Noise will only occur when the panel is being touched. Not ideal, but
* an acceptable improvement.
*/
static std::array<portapack::IO::TouchPinsConfig, 3> touch_pins_configs{
static std::array<IO::TouchPinsConfig, 3> touch_pins_configs{
/* State machine will pause here until touch is detected. */
portapack::IO::TouchPinsConfig::SensePressure,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
IO::TouchPinsConfig::SensePressure,
IO::TouchPinsConfig::SenseX,
IO::TouchPinsConfig::SenseY,
};

static touch::Frame temp_frame;
Expand All @@ -80,7 +81,7 @@ static bool touch_update() {
const auto current_phase = touch_pins_configs[touch_phase];

switch (current_phase) {
case portapack::IO::TouchPinsConfig::SensePressure: {
case IO::TouchPinsConfig::SensePressure: {
const auto z1 = samples.xp - samples.xn;
const auto z2 = samples.yp - samples.yn;
const auto touch_raw = (z1 > touch::touch_threshold) || (z2 > touch::touch_threshold);
Expand All @@ -94,11 +95,11 @@ static bool touch_update() {
}
} break;

case portapack::IO::TouchPinsConfig::SenseX:
case IO::TouchPinsConfig::SenseX:
temp_frame.x += samples;
break;

case portapack::IO::TouchPinsConfig::SenseY:
case IO::TouchPinsConfig::SenseY:
temp_frame.y += samples;
break;

Expand All @@ -122,25 +123,57 @@ static bool touch_update() {

static uint8_t switches_raw = 0;

/* The raw data is not packed in a way that makes looping over it easy.
* One option would be an accessor helper (RawSwitch). Another option
* is to swizzle the bits into a friendlier order. */
// /* Type to access the bits in the raw switch data. */
// struct RawSwitch {
// const uint8_t raw_{0};

// uint8_t right() const { return (raw_ >> 0) & 1; }
// uint8_t left() const { return (raw_ >> 1) & 1; }
// uint8_t down() const { return (raw_ >> 2) & 1; }
// uint8_t up() const { return (raw_ >> 3) & 1; }
// uint8_t select() const { return (raw_ >> 4) & 1; }
// uint8_t rot_a() const { return (raw_ >> 5) & 1; }
// uint8_t rot_b() const { return (raw_ >> 6) & 1; }
// uint8_t dfu() const { return (raw_ >> 7) & 1; }};

static uint8_t swizzle_raw(uint8_t raw) {
return (raw & 0x1F) | // Keep the bottom 5 bits the same.
((raw >> 2) & 0x20) | // Shift the DFU bit down to bit 6.
((raw << 1) & 0xC0); // Shift the encoder bits up to be 7 & 8.
}

static bool switches_update(const uint8_t raw) {
// TODO: Only fire event on press, not release?
bool switch_changed = false;
for (size_t i = 0; i < switch_debounce.size(); i++) {
switch_changed |= switch_debounce[i].feed((raw >> i) & 1);

for (size_t i = 0; i < switch_debounce.size(); ++i) {
uint8_t bit = (raw >> i) & 0x01;
switch_changed |= switch_debounce[i].feed(bit);
}

return switch_changed;
}

static bool encoder_update(const uint8_t raw) {
bool encoder_changed = false;

encoder_changed |= encoder_debounce[0].feed((raw >> 6) & 0x01);
encoder_changed |= encoder_debounce[1].feed((raw >> 7) & 0x01);

return encoder_changed;
}

static bool encoder_read() {
const auto delta = encoder.update(
switch_debounce[5].state(),
switch_debounce[6].state());
encoder_debounce[0].state(),
encoder_debounce[1].state());

if (delta != 0) {
encoder_position += delta;
return true;
;
} else {
return false;
}
Expand All @@ -149,11 +182,13 @@ static bool encoder_read() {
void timer0_callback(GPTDriver* const) {
eventmask_t event_mask = 0;
if (touch_update()) event_mask |= EVT_MASK_TOUCH;
switches_raw = portapack::io.io_update(touch_pins_configs[touch_phase]);
if (switches_update(switches_raw)) {

switches_raw = swizzle_raw(io.io_update(touch_pins_configs[touch_phase]));
if (switches_update(switches_raw))
event_mask |= EVT_MASK_SWITCHES;
if (encoder_read()) event_mask |= EVT_MASK_ENCODER;
}

if (encoder_update(switches_raw) && encoder_read())
event_mask |= EVT_MASK_ENCODER;

/* Signal event loop */
if (event_mask) {
Expand Down Expand Up @@ -189,37 +224,39 @@ void controls_init() {
gptStartContinuous(&GPTD1, timer0_match_count);

// Enable repeat for directional switches only
for (size_t i = 0; i < 4; i++)
switch_debounce[i].enable_repeat();
for (auto i = Switch::Right; i <= Switch::Up; incr(i))
switch_debounce[toUType(i)].enable_repeat();
}

SwitchesState get_switches_state() {
SwitchesState result;

// Right, Left, Down, Up, & Select switches
for (size_t i = 0; i < result.size() - 1; i++) {
// TODO: Ignore multiple keys at the same time?
// TODO: Ignore multiple keys at the same time?
for (size_t i = 0; i < result.size(); i++)
result[i] = switch_debounce[i].state();
}

// Grab Dfu switch from bit 7 and return in bit 5 so that all switches are grouped together in this 6-bit result
result[(size_t)Switch::Dfu] = switch_debounce[7].state();
return result;
}

// Configure which switches support long press (note those switches will not support Repeat function)
void switches_long_press_enable(SwitchesState switches_long_press_enabled) {
// Right, Left, Down, Up, & Select switches
for (size_t i = 0; i < switches_long_press_enabled.size() - 1; i++) {
switch_debounce[i].set_long_press_support(switches_long_press_enabled[i]);
}
/* Gets the long press enabled state for all the switches. */
SwitchesState get_switches_long_press_config() {
SwitchesState result;

for (size_t i = 0; i < result.size(); i++)
result[i] = switch_debounce[i].get_long_press_enabled();

return result;
}

// Dfu switch
switch_debounce[7].set_long_press_support(switches_long_press_enabled[(size_t)Switch::Dfu]);
/* Configures which switches support long press.
* NB: those switches will not support Repeat function. */
void set_switches_long_press_config(SwitchesState switch_config) {
for (size_t i = 0; i < switch_config.size(); i++)
switch_debounce[i].set_long_press_enabled(switch_config[i]);
}

bool switch_long_press_occurred(size_t v) {
return (v == (size_t)Switch::Dfu) ? switch_debounce[7].long_press_occurred() : switch_debounce[v].long_press_occurred();
bool switch_is_long_pressed(Switch s) {
return switch_debounce[toUType(s)].long_press_occurred();
}

EncoderPosition get_encoder_position() {
Expand Down
13 changes: 8 additions & 5 deletions firmware/application/irq_controls.hpp
Expand Up @@ -28,25 +28,28 @@
#include "touch.hpp"

// Must be same values as in ui::KeyEvent
enum class Switch {
// TODO: Why not just reuse one or the other?
enum class Switch : uint8_t {
Right = 0,
Left = 1,
Down = 2,
Up = 3,
Sel = 4,
Dfu = 5,
Dfu = 5
};

// Index with the Switch enum.
using SwitchesState = std::bitset<6>;

using EncoderPosition = uint32_t;

void controls_init();
SwitchesState get_switches_state();
EncoderPosition get_encoder_position();
touch::Frame get_touch_frame();
void switches_long_press_enable(SwitchesState v);
bool switch_long_press_occurred(size_t v);

SwitchesState get_switches_long_press_config();
void set_switches_long_press_config(SwitchesState switch_config);
bool switch_is_long_pressed(Switch s);

namespace control {
namespace debug {
Expand Down

0 comments on commit 3514a9a

Please sign in to comment.