portapack-mayhem/firmware/application/irq_controls.cpp
Jared Boone 8b02e40602 Move touch ADC data collection to M0.
...so it continues when M4 is shut down.

It's not as pretty as using DMA, but it's far simpler, even if it involves letting the ADC run continuously and taking the last samples even if not synchronizing to the phase of the sampling of the channels.
2016-07-24 15:31:53 -07:00

214 lines
5.9 KiB
C++

/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "irq_controls.hpp"
#include "ch.h"
#include "hal.h"
#include "event_m0.hpp"
#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;
static std::array<Debounce, 7> switch_debounce;
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
* when changing potential on the resistive touch pad. Among other things, I
* see blips of noise when sampling the MAX2837 RSSI signal. And when the
* radio is off (RSSI signal is not driven?), the signal floats a LOT when the
* touch panel potentials are changing.
*
* Ideally, scan only for pressure until a touch is detected. Then scan X/Y.
* Noise will only occur when the panel is being touched. Not ideal, but
* an acceptable improvement.
*/
static std::array<portapack::IO::TouchPinsConfig, 11> touch_pins_configs {
/* State machine will pause here until touch is detected. */
portapack::IO::TouchPinsConfig::WaitTouch,
portapack::IO::TouchPinsConfig::SensePressure,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
portapack::IO::TouchPinsConfig::SensePressure,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
};
static touch::Frame temp_frame;
static touch::Frame touch_frame;
static uint32_t touch_debounce = 0;
static uint32_t touch_debounce_mask = (1U << 1) - 1;
static bool touch_detected = false;
static bool touch_cycle = false;
static bool touch_update() {
const auto samples = touch::adc::get();
const auto current_phase = touch_pins_configs[touch_phase];
switch(current_phase) {
case portapack::IO::TouchPinsConfig::WaitTouch:
{
/* Debounce touches. */
const bool touch_raw = (samples.yp < touch::touch_threshold) && (samples.yn < touch::touch_threshold);
touch_debounce = (touch_debounce << 1) | (touch_raw ? 1U : 0U);
touch_detected = ((touch_debounce & touch_debounce_mask) == touch_debounce_mask);
if( !touch_detected && !touch_cycle ) {
return false;
}
}
break;
case portapack::IO::TouchPinsConfig::SensePressure:
temp_frame.pressure += samples;
break;
case portapack::IO::TouchPinsConfig::SenseX:
temp_frame.x += samples;
break;
case portapack::IO::TouchPinsConfig::SenseY:
temp_frame.y += samples;
break;
default:
break;
}
touch_phase++;
if( touch_phase >= touch_pins_configs.size() ) {
/* New iteration, calculate values and flag touch event */
touch_phase = 0;
temp_frame.touch = touch_detected;
touch_cycle = touch_detected;
touch_frame = temp_frame;
temp_frame = {};
return true;
} else {
return false;
}
}
static bool switches_update(const uint8_t switches_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((switches_raw >> i) & 1);
}
return switch_changed;
}
static bool encoder_read() {
const auto delta = encoder.update(
switch_debounce[5].state(),
switch_debounce[6].state()
);
if( delta != 0 ) {
encoder_position += delta;
return true;;
} else {
return false;
}
}
void timer0_callback(GPTDriver* const) {
eventmask_t event_mask = 0;
if( touch_update() ) event_mask |= EVT_MASK_TOUCH;
const auto switches_raw = portapack::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;
}
/* Signal event loop */
if( event_mask ) {
chSysLockFromIsr();
EventDispatcher::events_flag_isr(event_mask);
chSysUnlockFromIsr();
}
}
/* TODO: Refactor some/all of this to appropriate shared headers? */
static constexpr uint32_t timer0_count_f = 1000000;
static constexpr uint32_t timer0_prescaler_ratio = (base_m0_clk_f / timer0_count_f);
static constexpr uint32_t ui_interrupt_rate = 1000;
static constexpr uint32_t timer0_match_count = timer0_count_f / ui_interrupt_rate;
/* GPT driver refers to configuration structure during runtime, so make sure
* it sticks around.
*/
static GPTConfig timer0_config {
.callback = timer0_callback,
.pr = timer0_prescaler_ratio - 1,
};
void controls_init() {
touch::adc::start();
/* GPT timer 0 is used to scan user interface controls -- touch screen,
* navigation switches.
*/
gptStart(&GPTD1, &timer0_config);
gptStartContinuous(&GPTD1, timer0_match_count);
}
SwitchesState get_switches_state() {
SwitchesState result;
for(size_t i=0; i<result.size(); i++) {
// TODO: Ignore multiple keys at the same time?
result[i] = switch_debounce[i].state();
}
return result;
}
EncoderPosition get_encoder_position() {
return encoder_position;
}
touch::Frame get_touch_frame() {
return touch_frame;
}