705 lines
18 KiB
C
705 lines
18 KiB
C
/*
|
|
* Copyright (C) 2019 Kai Ludwig, DG4KLU
|
|
* Copyright (C) 2019-2020 Alex, DL4LEX
|
|
* Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
|
|
* Daniel Caujolle-Bert, F1RMB
|
|
* Colin Durbridge, G4EML
|
|
*
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
|
|
* incorporation in a commercial product or incorporation into a product or project which allows commercial use.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
#include "io/keyboard.h"
|
|
#include "interfaces/pit.h"
|
|
#include "functions/settings.h"
|
|
#include "interfaces/gpio.h"
|
|
#include "interfaces/adc.h"
|
|
#include "io/buttons.h"
|
|
|
|
// Keyboard Keys
|
|
typedef struct
|
|
{
|
|
GPIO_TypeDef *GPIOPort;
|
|
uint16_t GPIOPin;
|
|
uint16_t Key;
|
|
} KeyboardKeySetting_t;
|
|
|
|
static char oldKeyboardCode;
|
|
static uint32_t keyDebounceScancode;
|
|
static int keyDebounceCounter;
|
|
static uint8_t keyState;
|
|
static char keypadAlphaKey;
|
|
static int keypadAlphaIndex;
|
|
volatile bool keypadAlphaEnable;
|
|
volatile bool keypadLocked = false;
|
|
|
|
#if defined(PLATFORM_MD2017)
|
|
// 360 turn is 8/9 pulses, but handling one pulse as an key event is
|
|
// a bit too reactive. Hence, increasing the pulse per event is a bit better.
|
|
// Since touching the trackball generate an unwanted event, 99% of the time is the non
|
|
// desired direction, at least (1 + trackballMotion) is considered as an event, and any
|
|
// pulse from other direction resets the pulse count.
|
|
static int32_t trackballPulsesPerEvent = TRACKBALL_FAST_MOTION;
|
|
#endif
|
|
|
|
/*
|
|
static const uint32_t keyMap[] = {
|
|
KEY_1, KEY_2, KEY_3, KEY_GREEN,
|
|
KEY_4, KEY_5, KEY_6, KEY_UP,
|
|
KEY_7, KEY_8, KEY_9, KEY_DOWN,
|
|
KEY_STAR, KEY_0, KEY_HASH, KEY_RED,
|
|
};
|
|
*/
|
|
|
|
#if defined(PLATFORM_MD2017)
|
|
volatile trackballData_t trackballData =
|
|
{
|
|
.Direction = '?',
|
|
.Count = 0
|
|
};
|
|
#else
|
|
volatile rotaryData_t rotaryData =
|
|
{
|
|
.lastB = (GPIO_PinState)(GPIO_PIN_SET + 1), // To be sure it will be different on the first run (Hackish ? Yeah !)
|
|
.Count = 0,
|
|
.Direction = 0
|
|
};
|
|
#endif
|
|
|
|
enum KEY_STATE
|
|
{
|
|
KEY_IDLE = 0,
|
|
KEY_DEBOUNCE,
|
|
KEY_PRESS,
|
|
KEY_WAITLONG,
|
|
KEY_REPEAT,
|
|
KEY_WAIT_RELEASED
|
|
};
|
|
|
|
|
|
static const char keypadAlphaMap[11][31] = {
|
|
"0 ",
|
|
"1.!,@-:?()~/[]#<>=*+$%'`&|_^{}",
|
|
"abc2ABC",
|
|
"def3DEF",
|
|
"ghi4GHI",
|
|
"jkl5JKL",
|
|
"mno6MNO",
|
|
"pqrs7PQRS",
|
|
"tuv8TUV",
|
|
"wxyz9WXYZ",
|
|
"*"
|
|
};
|
|
|
|
#define KEYBOARD_KEYS_PER_ROW 8U
|
|
|
|
static const struct
|
|
{
|
|
GPIO_TypeDef *GPIOCtrlPort;
|
|
uint16_t GPIOCtrlPin;
|
|
KeyboardKeySetting_t Rows[KEYBOARD_KEYS_PER_ROW];
|
|
} KeyboardMatrix[] =
|
|
#if defined(PLATFORM_MDUV380)
|
|
{
|
|
{
|
|
KEYPAD_ROW0_GPIO_Port,
|
|
KEYPAD_ROW0_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_1 },
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_2 },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_3 },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_4 },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_5 },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_6 },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_0 },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_STAR }
|
|
}
|
|
},
|
|
{
|
|
KEYPAD_ROW1_GPIO_Port,
|
|
KEYPAD_ROW1_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_GREEN },
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_FRONT_UP },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_FRONT_DOWN },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_7 },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_8 },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_9 },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_HASH },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_RED }
|
|
}
|
|
},
|
|
{
|
|
KEYPAD_ROW2_GPIO_Port,
|
|
KEYPAD_ROW2_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_NONE },
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_NONE },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_NONE },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_NONE },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_NONE },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_NONE },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_NONE },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_NONE }
|
|
}
|
|
}
|
|
};
|
|
#elif defined(PLATFORM_RT84_DM1701)
|
|
{
|
|
{
|
|
KEYPAD_ROW0_GPIO_Port,
|
|
KEYPAD_ROW0_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_1 },
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_4 },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_7 },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_STAR },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_FRONT_UP },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_RIGHT },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_LEFT },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_NONE }
|
|
}
|
|
},
|
|
{
|
|
KEYPAD_ROW1_GPIO_Port,
|
|
KEYPAD_ROW1_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_2 },
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_5 },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_8 },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_0 },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_FRONT_DOWN },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_RED },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_GREEN },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_NONE }
|
|
}
|
|
},
|
|
{
|
|
KEYPAD_ROW2_GPIO_Port,
|
|
KEYPAD_ROW2_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_3 },
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_6 },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_9 },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_HASH },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_NONE },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_NONE },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_NONE },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_NONE }
|
|
}
|
|
}
|
|
};
|
|
#elif defined(PLATFORM_MD2017)
|
|
{
|
|
{
|
|
KEYPAD_ROW0_GPIO_Port,
|
|
KEYPAD_ROW0_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_1 },
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_2 },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_3 },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_4 },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_5 },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_6 },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_0 },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_STAR }
|
|
}
|
|
},
|
|
{
|
|
KEYPAD_ROW1_GPIO_Port,
|
|
KEYPAD_ROW1_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_LEFT },//P1
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_GREEN },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_RED },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_7 },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_8 },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_9 },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_HASH },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_RIGHT }//P2
|
|
}
|
|
},
|
|
{
|
|
KEYPAD_ROW2_GPIO_Port,
|
|
KEYPAD_ROW2_Pin,
|
|
{
|
|
{ LCD_D0_GPIO_Port, LCD_D0_Pin, KEY_NONE },
|
|
{ LCD_D1_GPIO_Port, LCD_D1_Pin, KEY_NONE },
|
|
{ LCD_D2_GPIO_Port, LCD_D2_Pin, KEY_NONE },
|
|
{ LCD_D3_GPIO_Port, LCD_D3_Pin, KEY_NONE },
|
|
{ LCD_D4_GPIO_Port, LCD_D4_Pin, KEY_FRONT_DOWN },
|
|
{ LCD_D5_GPIO_Port, LCD_D5_Pin, KEY_NONE },
|
|
{ LCD_D6_GPIO_Port, LCD_D6_Pin, KEY_FRONT_UP },
|
|
{ LCD_D7_GPIO_Port, LCD_D7_Pin, KEY_NONE }
|
|
}
|
|
}
|
|
};
|
|
#endif
|
|
|
|
void keyboardInit(void)
|
|
{
|
|
//gpioInitKeyboard();
|
|
|
|
oldKeyboardCode = 0;
|
|
keyDebounceScancode = 0;
|
|
keyDebounceCounter = 0;
|
|
keypadAlphaEnable = false;
|
|
keypadAlphaIndex = 0;
|
|
keypadAlphaKey = 0;
|
|
keyState = KEY_IDLE;
|
|
keypadLocked = false;
|
|
#if defined(PLATFORM_MD2017)
|
|
trackballReset();
|
|
#endif
|
|
}
|
|
|
|
void keyboardReset(void)
|
|
{
|
|
oldKeyboardCode = 0;
|
|
keypadAlphaEnable = false;
|
|
keypadAlphaIndex = 0;
|
|
keypadAlphaKey = 0;
|
|
keyState = KEY_WAIT_RELEASED;
|
|
#if defined(PLATFORM_MD2017)
|
|
trackballReset();
|
|
#endif
|
|
}
|
|
|
|
uint32_t keyboardRead(void)
|
|
{
|
|
uint32_t result = KEY_NONE;
|
|
GPIO_InitTypeDef GPIO_InitStruct = { 0 };
|
|
uint32_t portD_pins = LCD_D0_Pin | LCD_D1_Pin | LCD_D2_Pin | LCD_D3_Pin;
|
|
uint32_t portE_pins = LCD_D4_Pin | LCD_D5_Pin | LCD_D6_Pin | LCD_D7_Pin;
|
|
|
|
|
|
|
|
// VK3KYY
|
|
// Extra code added to force drive the GPIO pins low, even though the FSMC should have left them in LOW
|
|
// Possibly errata in the STM silicon when changing from FSMC to GPIO use of the ports seems to be causing a glitch on some radios
|
|
// Which is momentarily driving the GPIO input pins High prior to them being read, which causes phantom key and button presses
|
|
|
|
HAL_GPIO_WritePin(GPIOD, portD_pins, GPIO_PIN_RESET);
|
|
HAL_GPIO_WritePin(GPIOE, portE_pins, GPIO_PIN_RESET);
|
|
|
|
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
|
|
|
|
GPIO_InitStruct.Pin = portD_pins;
|
|
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
|
|
|
GPIO_InitStruct.Pin = portE_pins;
|
|
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
|
|
|
|
for(volatile int xx = 0; xx < 10; xx++); // arbitrary settling delay
|
|
|
|
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
|
GPIO_InitStruct.Pin = portD_pins;
|
|
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
|
|
|
GPIO_InitStruct.Pin = portE_pins;
|
|
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
|
|
|
|
for (size_t i = 0; i < (sizeof(KeyboardMatrix) / sizeof(KeyboardMatrix[0])); i++)
|
|
{
|
|
//Set the Row Pin as Output
|
|
GPIO_InitStruct.Pin = KeyboardMatrix[i].GPIOCtrlPin;
|
|
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
HAL_GPIO_Init(KeyboardMatrix[i].GPIOCtrlPort, &GPIO_InitStruct);
|
|
|
|
//Set the row pin high to select that row of keys
|
|
HAL_GPIO_WritePin(KeyboardMatrix[i].GPIOCtrlPort, KeyboardMatrix[i].GPIOCtrlPin, GPIO_PIN_SET);
|
|
|
|
for(volatile int xx = 0; xx < 100; xx++); // arbitrary settling delay
|
|
|
|
for (size_t k = 0; k < KEYBOARD_KEYS_PER_ROW; k++)
|
|
{
|
|
if((KeyboardMatrix[i].Rows[k].Key != KEY_NONE) &&
|
|
(HAL_GPIO_ReadPin(KeyboardMatrix[i].Rows[k].GPIOPort, KeyboardMatrix[i].Rows[k].GPIOPin) == GPIO_PIN_SET))
|
|
{
|
|
result = KeyboardMatrix[i].Rows[k].Key;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//set the row pin back to floating. This prevents conflicts between multiple key presses.
|
|
GPIO_InitStruct.Pin = KeyboardMatrix[i].GPIOCtrlPin;
|
|
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
HAL_GPIO_Init(KeyboardMatrix[i].GPIOCtrlPort, &GPIO_InitStruct);
|
|
|
|
// Stop on first down key (we don't support multiple key presses).
|
|
if (result != KEY_NONE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool keyboardKeyIsDTMFKey(char key)
|
|
{
|
|
switch (key)
|
|
{
|
|
case KEY_0 ... KEY_9:
|
|
case KEY_STAR:
|
|
case KEY_HASH:
|
|
case KEY_A:
|
|
case KEY_B:
|
|
case KEY_C:
|
|
case KEY_D:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#if defined(PLATFORM_MD2017)
|
|
void trackballReset(void)
|
|
{
|
|
trackballData.Direction = '?';
|
|
trackballData.Count = 0;
|
|
}
|
|
|
|
void trackballSetMotion(bool fast)
|
|
{
|
|
trackballPulsesPerEvent = (fast ? TRACKBALL_FAST_MOTION : TRACKBALL_SLOW_MOTION);
|
|
trackballReset();
|
|
}
|
|
|
|
void trackballISR(uint16_t pin)
|
|
{
|
|
uint8_t x = '?';
|
|
|
|
switch (pin)
|
|
{
|
|
case TRACKBALL_UP_Pin:
|
|
x = 'U';
|
|
break;
|
|
|
|
case TRACKBALL_DOWN_Pin:
|
|
x = 'D';
|
|
break;
|
|
|
|
case TRACKBALL_LEFT_Pin:
|
|
x = 'L';
|
|
break;
|
|
|
|
case TRACKBALL_RIGHT_Pin:
|
|
x = 'R';
|
|
break;
|
|
|
|
default:
|
|
trackballData.Direction = '?';
|
|
trackballData.Count = 0;
|
|
break;
|
|
}
|
|
|
|
if (x != '?')
|
|
{
|
|
if (trackballData.Direction == x)
|
|
{
|
|
trackballData.Count++;
|
|
}
|
|
else
|
|
{
|
|
trackballData.Direction = x;
|
|
trackballData.Count = 1;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
void rotaryEncoderISR(void)
|
|
{
|
|
GPIO_PinState pinA = HAL_GPIO_ReadPin(ROTARY_SW_A_GPIO_Port, ROTARY_SW_A_Pin);
|
|
GPIO_PinState pinB = HAL_GPIO_ReadPin(ROTARY_SW_B_GPIO_Port, ROTARY_SW_B_Pin);
|
|
|
|
if (pinB != rotaryData.lastB)
|
|
{
|
|
rotaryData.lastB = pinB;
|
|
rotaryData.Direction = ((pinA == pinB) ? -1 : 1);
|
|
rotaryData.Count += rotaryData.Direction;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void keyboardCheckKeyEvent(keyboardCode_t *keys, int *event)
|
|
{
|
|
uint32_t scancode = 0;
|
|
char keycode = 0;
|
|
bool validKey;
|
|
int newAlphaKey;
|
|
uint32_t tmp_timer_keypad;
|
|
uint32_t keypadTimerLong = nonVolatileSettings.keypadTimerLong * 100;
|
|
uint32_t keypadTimerRepeat = nonVolatileSettings.keypadTimerRepeat * 100;
|
|
#if defined(PLATFORM_MD2017)
|
|
bool trackballEnabled = settingsIsOptionBitSet(BIT_TRACKBALL_ENABLED);
|
|
#endif
|
|
|
|
*event = EVENT_KEY_NONE;
|
|
keys->event = 0;
|
|
keys->key = KEY_NONE;
|
|
|
|
#if defined(PLATFORM_MD2017)
|
|
if (trackballEnabled &&
|
|
(trackballData.Count > (trackballPulsesPerEvent + 1))) // Valid trackball event are started after two consecutive pulses in the same direction
|
|
{
|
|
switch (trackballData.Direction)
|
|
{
|
|
case 'U':
|
|
keys->key = KEY_UP;
|
|
break;
|
|
|
|
case 'D':
|
|
keys->key = KEY_DOWN;
|
|
break;
|
|
|
|
case 'L':
|
|
keys->key = KEY_LEFT;
|
|
break;
|
|
|
|
case 'R':
|
|
keys->key = KEY_RIGHT;
|
|
break;
|
|
}
|
|
|
|
keys->event = KEY_MOD_UP | KEY_MOD_PRESS;
|
|
*event = EVENT_KEY_CHANGE;
|
|
trackballData.Count -= trackballPulsesPerEvent; // Only decrease one event at a time.
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (trackballEnabled == false)
|
|
{
|
|
trackballReset();
|
|
}
|
|
|
|
keycode = (char) keyboardRead();
|
|
scancode = keycode;
|
|
}
|
|
#else
|
|
if (rotaryData.Direction != 0)
|
|
{
|
|
keys->key = (rotaryData.Direction == 1) ? KEY_ROTARY_INCREMENT : KEY_ROTARY_DECREMENT;
|
|
keys->event = KEY_MOD_UP | KEY_MOD_PRESS; // Hack send both Up and Down events because the menus use KEY_MOD_PRESS but the VFO uses KEY_MOD_UP
|
|
*event = EVENT_KEY_CHANGE;
|
|
rotaryData.Direction = 0;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
keycode = (char) keyboardRead();
|
|
scancode = keycode;
|
|
}
|
|
#endif
|
|
|
|
validKey = true;
|
|
|
|
if (keyState > KEY_DEBOUNCE && !validKey)
|
|
{
|
|
keyState = KEY_WAIT_RELEASED;
|
|
}
|
|
|
|
switch (keyState)
|
|
{
|
|
case KEY_IDLE:
|
|
if (keycode != 0)
|
|
{
|
|
keyState = KEY_DEBOUNCE;
|
|
keyDebounceCounter = 0;
|
|
keyDebounceScancode = scancode;
|
|
oldKeyboardCode = 0;
|
|
}
|
|
taskENTER_CRITICAL();
|
|
tmp_timer_keypad = timer_keypad_timeout;
|
|
taskEXIT_CRITICAL();
|
|
|
|
if (tmp_timer_keypad == 0 && keypadAlphaKey != 0)
|
|
{
|
|
keys->key = keypadAlphaMap[keypadAlphaKey - 1][keypadAlphaIndex];
|
|
keys->event = KEY_MOD_PRESS;
|
|
*event = EVENT_KEY_CHANGE;
|
|
keypadAlphaKey = 0;
|
|
}
|
|
break;
|
|
case KEY_DEBOUNCE:
|
|
keyDebounceCounter++;
|
|
if (keyDebounceCounter > KEY_DEBOUNCE_COUNTER)
|
|
{
|
|
if (keyDebounceScancode == scancode)
|
|
{
|
|
oldKeyboardCode = keycode;
|
|
keyState = KEY_PRESS;
|
|
}
|
|
else
|
|
{
|
|
keyState = KEY_WAIT_RELEASED;
|
|
}
|
|
}
|
|
break;
|
|
case KEY_PRESS:
|
|
keys->key = keycode;
|
|
keys->event = KEY_MOD_DOWN | KEY_MOD_PRESS;
|
|
*event = EVENT_KEY_CHANGE;
|
|
|
|
taskENTER_CRITICAL();
|
|
timer_keypad = keypadTimerLong;
|
|
timer_keypad_timeout = 1000;
|
|
taskEXIT_CRITICAL();
|
|
keyState = KEY_WAITLONG;
|
|
|
|
if (keypadAlphaEnable)
|
|
{
|
|
newAlphaKey = 0;
|
|
if ((keycode >= '0') && (keycode <= '9'))
|
|
{
|
|
newAlphaKey = (keycode - '0') + 1;
|
|
}
|
|
else if (keycode == KEY_STAR)
|
|
{
|
|
newAlphaKey = 11;
|
|
}
|
|
|
|
if (keypadAlphaKey == 0)
|
|
{
|
|
if (newAlphaKey != 0)
|
|
{
|
|
keypadAlphaKey = newAlphaKey;
|
|
keypadAlphaIndex = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (newAlphaKey == keypadAlphaKey)
|
|
{
|
|
keypadAlphaIndex++;
|
|
if (keypadAlphaMap[keypadAlphaKey - 1][keypadAlphaIndex] == 0)
|
|
{
|
|
keypadAlphaIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (keypadAlphaKey != 0)
|
|
{
|
|
if (newAlphaKey == keypadAlphaKey)
|
|
{
|
|
keys->key = keypadAlphaMap[keypadAlphaKey - 1][keypadAlphaIndex];
|
|
keys->event = KEY_MOD_PREVIEW;
|
|
}
|
|
else
|
|
{
|
|
keys->key = keypadAlphaMap[keypadAlphaKey - 1][keypadAlphaIndex];
|
|
keys->event = KEY_MOD_PRESS;
|
|
*event = EVENT_KEY_CHANGE;
|
|
keypadAlphaKey = newAlphaKey;
|
|
keypadAlphaIndex = -1;
|
|
keyState = KEY_PRESS;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_WAITLONG:
|
|
if (keycode == 0)
|
|
{
|
|
keys->key = oldKeyboardCode;
|
|
keys->event = KEY_MOD_UP;
|
|
*event = EVENT_KEY_CHANGE;
|
|
keyState = KEY_IDLE;
|
|
}
|
|
else
|
|
{
|
|
taskENTER_CRITICAL();
|
|
tmp_timer_keypad = timer_keypad;
|
|
taskEXIT_CRITICAL();
|
|
|
|
if (tmp_timer_keypad == 0)
|
|
{
|
|
taskENTER_CRITICAL();
|
|
timer_keypad = keypadTimerRepeat;
|
|
taskEXIT_CRITICAL();
|
|
|
|
keys->key = keycode;
|
|
keys->event = KEY_MOD_LONG | KEY_MOD_DOWN;
|
|
*event = EVENT_KEY_CHANGE;
|
|
keyState = KEY_REPEAT;
|
|
}
|
|
}
|
|
break;
|
|
case KEY_REPEAT:
|
|
if (keycode == 0)
|
|
{
|
|
keys->key = oldKeyboardCode;
|
|
keys->event = KEY_MOD_LONG | KEY_MOD_UP;
|
|
*event = EVENT_KEY_CHANGE;
|
|
|
|
keyState = KEY_IDLE;
|
|
}
|
|
else
|
|
{
|
|
taskENTER_CRITICAL();
|
|
tmp_timer_keypad = timer_keypad;
|
|
taskEXIT_CRITICAL();
|
|
|
|
keys->key = keycode;
|
|
keys->event = KEY_MOD_LONG;
|
|
*event = EVENT_KEY_CHANGE;
|
|
|
|
if (tmp_timer_keypad == 0)
|
|
{
|
|
taskENTER_CRITICAL();
|
|
timer_keypad = keypadTimerRepeat;
|
|
taskEXIT_CRITICAL();
|
|
|
|
if ((keys->key == KEY_LEFT) || (keys->key == KEY_RIGHT)
|
|
|| (keys->key == KEY_UP) || (keys->key == KEY_DOWN) || (keys->key == KEY_FRONT_DOWN) || (keys->key == KEY_FRONT_UP))
|
|
{
|
|
keys->event = (KEY_MOD_LONG | KEY_MOD_PRESS);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_WAIT_RELEASED:
|
|
if (scancode == 0)
|
|
{
|
|
keyState = KEY_IDLE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
void buttonsFrontPanelDump(void)
|
|
{
|
|
uint16_t keys = buttonsFrontPanelRead();
|
|
|
|
char buffer[17];
|
|
snprintf(buffer, sizeof(buffer), "K: 0x%03X %ld", keys, rotaryData.Count);
|
|
displayPrintCentered(40, buffer, FONT_SIZE_3);
|
|
}
|
|
*/
|