Add support for encoder knob on MD-UV380

Added: qdec, a header only quadrature encoder library

Added: EXTI15_10_IRQHandler to handle the encoder knob

Changed: platform_init() for the MD-UV380 now configures the EXTI15_10
interrupt.

Changed: platform_getChSelector now returns signed int8

Changed: size of settings_t.valid went from 6 to 7
Without this change, pressing the macro button crashes the radio, I
haven't investigated and I don't remember how I found that solution.

Changed: meson.build includes the qdec library
replace/3348d2a8f3f43dc876bb0adbc3a32025541de502
Caleb Jamison 2021-04-02 07:45:32 -05:00 zatwierdzone przez silseva
rodzic b6eab0fde7
commit e5c5ee90f2
8 zmienionych plików z 159 dodań i 15 usunięć

Wyświetl plik

@ -0,0 +1,60 @@
enum QDECODER_EVENT {
QDECODER_EVENT_NONE = 0x00u,
QDECODER_EVENT_CW = 0x80u,
QDECODER_EVENT_CCW = 0x40u,
};
// state is internal... needs never be exposed to callers
enum QDECODER_STATE {
QDECODER_STATE_START = 0x00u,
QDECODER_STATE_CW_A = 0x01u,
QDECODER_STATE_CW_B = 0x02u,
QDECODER_STATE_CW_C = 0x03u,
QDECODER_STATE_MID = 0x04u,
QDECODER_STATE_CCW_A = 0x05u,
QDECODER_STATE_CCW_B = 0x06u,
QDECODER_STATE_CCW_C = 0x07u,
};
const uint8_t QDECODER_EVENT_BITMASK = 0xC0; // events are the most significant two bits
const uint8_t QDECODER_STATE_BITMASK = 0x07; // states are in the least significant three bits
const uint8_t HALF_STEP_STATE_TRANSITIONS[8][4] = {
// 0 = start
{QDECODER_STATE_MID, QDECODER_STATE_CW_A, QDECODER_STATE_CCW_A, QDECODER_STATE_START },
// 1 = CW_A
{QDECODER_EVENT_CW |
QDECODER_STATE_MID, QDECODER_STATE_CW_A, QDECODER_STATE_START, QDECODER_STATE_START },
// 2 = CW_B (unused in half-step mode)
{QDECODER_STATE_START, QDECODER_STATE_START, QDECODER_STATE_START, QDECODER_STATE_START },
// 3 = CW_C
{QDECODER_STATE_MID, QDECODER_STATE_MID, QDECODER_STATE_CW_C, QDECODER_STATE_START | QDECODER_EVENT_CW },
// 4 = mid
{QDECODER_STATE_MID, QDECODER_STATE_CCW_C, QDECODER_STATE_CW_C, QDECODER_STATE_START },
// 5 = CCW_A
{QDECODER_EVENT_CCW |
QDECODER_STATE_MID, QDECODER_STATE_START, QDECODER_STATE_CCW_A, QDECODER_STATE_START },
// 6 = CCW_B (unused in half-step mode)
{QDECODER_STATE_START, QDECODER_STATE_START, QDECODER_STATE_START, QDECODER_STATE_START },
// 7 = CCW_C
{QDECODER_STATE_MID, QDECODER_STATE_CCW_C, QDECODER_STATE_MID, QDECODER_STATE_START | QDECODER_EVENT_CCW},
};
const uint8_t FULL_STEP_STATE_TRANSITIONS[8][4] = {
// 0 = start
{QDECODER_STATE_START, QDECODER_STATE_CW_A, QDECODER_STATE_CCW_A, QDECODER_STATE_START },
// 1 = CW_A
{QDECODER_STATE_CW_B, QDECODER_STATE_CW_A, QDECODER_STATE_START, QDECODER_STATE_START },
// 2 = CW_B
{QDECODER_STATE_CW_B, QDECODER_STATE_CW_A, QDECODER_STATE_CW_C, QDECODER_STATE_START },
// 3 = CW_C
{QDECODER_STATE_CW_B, QDECODER_STATE_START, QDECODER_STATE_CW_C, QDECODER_STATE_START | QDECODER_EVENT_CW },
// 4 = mid (unused in full step mode ...)
{QDECODER_STATE_START, QDECODER_STATE_START, QDECODER_STATE_START, QDECODER_STATE_START },
// 5 = CCW_A
{QDECODER_STATE_CCW_B, QDECODER_STATE_START, QDECODER_STATE_CCW_A, QDECODER_STATE_START },
// 6 = CCW_B
{QDECODER_STATE_CCW_B, QDECODER_STATE_CCW_C, QDECODER_STATE_CCW_A, QDECODER_STATE_START },
// 7 = CCW_C
{QDECODER_STATE_CCW_B, QDECODER_STATE_CCW_C, QDECODER_STATE_START, QDECODER_STATE_START | QDECODER_EVENT_CCW },
};

Wyświetl plik

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 SimpleHacks ("Simple hacks for a simple life")
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

2
lib/qdec/readme 100644
Wyświetl plik

@ -0,0 +1,2 @@
This is a plain C header library lightly adapted from the SimpleHacks QDEC library.
Find the original source here: https://github.com/SimpleHacks/QDEC

Wyświetl plik

@ -55,8 +55,12 @@ minmea_src = ['lib/minmea/minmea.c']
minmea_inc = ['lib/minmea/include']
## QDEC, a very simple, header only, quadrature decoding library
qdec_inc = ['lib/qdec/include']
src = openrtx_src + minmea_src
inc = openrtx_inc + rtos_inc + minmea_inc
inc = openrtx_inc + rtos_inc + minmea_inc + qdec_inc
##
## Definitions

Wyświetl plik

@ -101,7 +101,7 @@ float platform_getVolumeLevel();
/**
* This function reads and returns the current channel selector level.
*/
uint8_t platform_getChSelector();
int8_t platform_getChSelector();
/**
* This function reads and returns the current PTT status.

Wyświetl plik

@ -23,7 +23,7 @@
typedef struct
{
uint8_t valid[6]; // Should contain "OPNRTX" in a valid settings_t
uint8_t valid[7]; // Should contain "OPNRTX" in a valid settings_t
uint8_t brightness;
uint8_t contrast;
int8_t utc_timezone;

Wyświetl plik

@ -53,14 +53,22 @@ keyboard_t kbd_getKeys()
keyboard_t keys = 0;
/* Use absolute position knob to emulate left and right buttons */
static uint8_t old_pos = 0;
uint8_t new_pos = platform_getChSelector();
static int8_t old_pos = 0;
int8_t new_pos = platform_getChSelector();
if (old_pos != new_pos)
{
if (new_pos < old_pos)
int8_t diff = old_pos - new_pos;
if (diff < 0)
keys |= KEY_LEFT;
else
else if (diff > 0)
keys |= KEY_RIGHT;
else
{
if (old_pos < 0)
keys |= KEY_LEFT;
else
keys |= KEY_RIGHT;
}
old_pos = new_pos;
}

Wyświetl plik

@ -25,9 +25,11 @@
#include <calibInfo_MDx.h>
#include <interfaces/nvmem.h>
#include <interfaces/rtc.h>
#include <qdec.h>
mduv3x0Calib_t calibration;
hwInfo_t hwInfo;
static int8_t knob_pos = 0;
#ifdef ENABLE_BKLIGHT_DIMMING
void _Z29TIM1_TRG_COM_TIM11_IRQHandlerv()
@ -46,6 +48,42 @@ void _Z29TIM1_TRG_COM_TIM11_IRQHandlerv()
}
#endif
/*
* Note that this interrupt handler currently assumes only the encoder will
* ever cause this interrupt to fire
*/
void _Z20EXTI15_10_IRQHandlerv()
{
/* State storage */
static uint8_t last_state = 0;
/* Read curent pin state */
uint8_t pin_state = gpio_readPin(CH_SELECTOR_1)<<1 | gpio_readPin(CH_SELECTOR_0);
/* Look up next state */
uint8_t next_state = HALF_STEP_STATE_TRANSITIONS[last_state][pin_state];
/* update state for next call */
last_state = next_state & QDECODER_STATE_BITMASK;
/* Mask out events to switch on */
uint8_t event = next_state & QDECODER_EVENT_BITMASK;
/* Update file global knob_pos variable */
switch (event)
{
case QDECODER_EVENT_CW:
knob_pos++;
break;
case QDECODER_EVENT_CCW:
knob_pos--;
break;
default:
break;
}
/* Clear pin change flags */
EXTI->PR = EXTI_PR_PR11 | EXTI_PR_PR14;
}
void platform_init()
{
/* Configure GPIOs */
@ -55,8 +93,19 @@ void platform_init()
gpio_setMode(LCD_BKLIGHT, OUTPUT);
gpio_clearPin(LCD_BKLIGHT);
gpio_setMode(CH_SELECTOR_0, INPUT);
gpio_setMode(CH_SELECTOR_1, INPUT);
gpio_setMode(CH_SELECTOR_0, INPUT_PULL_UP);
gpio_setMode(CH_SELECTOR_1, INPUT_PULL_UP);
EXTI->IMR |= EXTI_IMR_MR11 | EXTI_IMR_MR14;
EXTI->RTSR |= EXTI_RTSR_TR11 | EXTI_RTSR_TR14;
EXTI->FTSR |= EXTI_FTSR_TR11 | EXTI_FTSR_TR14;
SYSCFG->EXTICR[2] |= SYSCFG_EXTICR3_EXTI11_PB;
SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI14_PE;
NVIC_ClearPendingIRQ(EXTI15_10_IRQn);
NVIC_SetPriority(EXTI15_10_IRQn, 15);
NVIC_EnableIRQ(EXTI15_10_IRQn);
gpio_setMode(PTT_SW, INPUT_PULL_UP);
@ -150,13 +199,13 @@ float platform_getVolumeLevel()
return adc1_getMeasurement(1);
}
uint8_t platform_getChSelector()
int8_t platform_getChSelector()
{
static const uint8_t rsPositions[] = { 1, 4, 2, 3};
int pos = gpio_readPin(CH_SELECTOR_0)
| (gpio_readPin(CH_SELECTOR_1) << 1);
return rsPositions[pos];
/*
* The knob_pos variable is set in the EXTI15_10 interrupt handler
* this is safe because interrupt nesting is not allowed.
*/
return knob_pos;
}
bool platform_getPttStatus()