Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 130 additions & 29 deletions radio/src/targets/pl18/nb4p_key_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@
* GNU General Public License for more details.
*/

/*
* FlySky NB4+ Key and Trim Driver
*
* Hardware Configuration:
* - Navigation Keys: SW1 on ADC_RAW1 (PC.01/ADC1_IN11) - channel 4
* - Trim 1 (ST): ADC_RAW3 (PA.06/ADC1_IN6) - channel 6
* - Trim 2 (TH): ADC_RAW4 (PC.04/ADC1_IN14) - channel 7
*
* Key Mapping:
* - KEY_ENTER: Low ADC value (~0V when pressed)
* - KEY_EXIT: High ADC value (~3.3V when pressed)
*
* Trim Mapping (navigation via trim switches):
* - STD/STU: Up/Down navigation (trim 1)
* - THD/THU: Left/Right navigation (trim 2)
*
* This driver provides enhanced precision and reliability improvements
* over the original implementation.
*/

#include "hal/key_driver.h"

#include "stm32_hal_ll.h"
Expand All @@ -34,6 +54,18 @@

#define BOOTLOADER_KEYS 0x42

// Enhanced ADC thresholds for NB4+ (12-bit ADC: 0-4095 range)
// Optimized for better precision and noise immunity
#define KEY_ADC_LOW_THRESHOLD 350 // ENTER key threshold (improved from 512)
#define KEY_ADC_HIGH_THRESHOLD 3700 // EXIT key threshold (improved from 3584)
#define KEY_ADC_HYSTERESIS 50 // Hysteresis to prevent bouncing

#define TRIM_ADC_LOW_THRESHOLD 350 // Trim active low threshold
#define TRIM_ADC_MID_LOW 1300 // Trim mid-low boundary
#define TRIM_ADC_MID_HIGH 2700 // Trim mid-high boundary
#define TRIM_ADC_HIGH_THRESHOLD 3700 // Trim active high threshold
#define TRIM_ADC_HYSTERESIS 100 // Larger hysteresis for trims

/* The output bit-order has to be:
0 LHL STD (Left equals down)
1 LHR STU
Expand All @@ -58,7 +90,7 @@ void keysInit()
#if defined(BOOT)
LL_GPIO_InitTypeDef pinInit;
LL_GPIO_StructInit(&pinInit);

pinInit.Pin = ADC_GPIO_PIN_RAW1;
pinInit.Mode = LL_GPIO_MODE_ANALOG;
pinInit.Pull = LL_GPIO_PULL_NO;
Expand Down Expand Up @@ -122,16 +154,37 @@ uint32_t readKeys()

#if defined(BOOT)
uint16_t value = _adcRead();
if (value >= 3584)
// Use enhanced thresholds for more reliable key detection in bootloader
if (value >= KEY_ADC_HIGH_THRESHOLD)
result |= 1 << KEY_EXIT;
else if (value < 512)
else if (value <= KEY_ADC_LOW_THRESHOLD)
result |= 1 << KEY_ENTER;
#else
uint16_t value = getAnalogValue(4);
if (value >= 3584)
result |= 1 << KEY_EXIT;
else if (value < 512)

// Enhanced key detection with hysteresis for debouncing
static uint8_t lastKeyState = 0; // 0=none, 1=enter, 2=exit

if (value <= KEY_ADC_LOW_THRESHOLD) {
result |= 1 << KEY_ENTER;
lastKeyState = 1;
}
else if (value >= KEY_ADC_HIGH_THRESHOLD) {
result |= 1 << KEY_EXIT;
lastKeyState = 2;
}
else {
// In neutral zone - apply hysteresis based on last state
if (lastKeyState == 1 && value <= (KEY_ADC_LOW_THRESHOLD + KEY_ADC_HYSTERESIS)) {
result |= 1 << KEY_ENTER; // Maintain ENTER if close to threshold
}
else if (lastKeyState == 2 && value >= (KEY_ADC_HIGH_THRESHOLD - KEY_ADC_HYSTERESIS)) {
result |= 1 << KEY_EXIT; // Maintain EXIT if close to threshold
}
else {
lastKeyState = 0; // Reset state when clearly in neutral zone
}
}
#endif

return result;
Expand All @@ -146,29 +199,77 @@ uint32_t readTrims()
if (value >= 1536 && value < 2560)
result = BOOTLOADER_KEYS;
#else
uint16_t tr1Val = getAnalogValue(6);
uint16_t tr2Val = getAnalogValue(7);
if (tr1Val < 500) // Physical TR1 Left
// result |= 1 << TR1L;
;
else if (tr1Val < 1500) // Physical TR1 Up
result |= 1 << STD;
else if (tr1Val < 2500) // Physical TR1 Right
// result |= 1 << TR1R;
;
else if (tr1Val < 3500) // Physical TR1 Down
result |= 1 << STU;
if (tr2Val < 500) // Physical TR2 Left
// result |= 1 << TR2L;
;
else if (tr2Val < 1500) // Physical TR2 Up
result |= 1 << THD;
else if (tr2Val < 2500) // Physical TR2 Right
// result |= 1 << TR2R;
;
else if (tr2Val < 3500) // Physical TR2 Down
result |= 1 << THU;
uint16_t tr1Val = getAnalogValue(6); // Trim 1 (ST) - Up/Down navigation
uint16_t tr2Val = getAnalogValue(7); // Trim 2 (TH) - Left/Right navigation

// Enhanced trim detection with hysteresis and dead zones
static uint8_t lastTrim1State = 0; // 0=neutral, 1=down, 2=up
static uint8_t lastTrim2State = 0; // 0=neutral, 1=left, 2=right

// Trim 1 (ST) processing - Up/Down navigation
if (tr1Val <= TRIM_ADC_LOW_THRESHOLD) {
// Left physical position -> not used for navigation
lastTrim1State = 0;
}
else if (tr1Val >= TRIM_ADC_LOW_THRESHOLD + TRIM_ADC_HYSTERESIS &&
tr1Val <= TRIM_ADC_MID_LOW) {
result |= 1 << STD; // Navigation DOWN
lastTrim1State = 1;
}
else if (tr1Val >= TRIM_ADC_MID_HIGH &&
tr1Val <= TRIM_ADC_HIGH_THRESHOLD - TRIM_ADC_HYSTERESIS) {
result |= 1 << STU; // Navigation UP
lastTrim1State = 2;
}
else if (tr1Val >= TRIM_ADC_HIGH_THRESHOLD) {
// Right physical position -> not used for navigation
lastTrim1State = 0;
}
else {
// In dead zone - apply hysteresis based on last state
if (lastTrim1State == 1 && tr1Val <= (TRIM_ADC_MID_LOW + TRIM_ADC_HYSTERESIS)) {
result |= 1 << STD; // Maintain DOWN if close to threshold
}
else if (lastTrim1State == 2 && tr1Val >= (TRIM_ADC_MID_HIGH - TRIM_ADC_HYSTERESIS)) {
result |= 1 << STU; // Maintain UP if close to threshold
}
else {
lastTrim1State = 0; // Reset to neutral
}
}

// Trim 2 (TH) processing - Left/Right navigation
if (tr2Val <= TRIM_ADC_LOW_THRESHOLD) {
// Left physical position -> not used for navigation
lastTrim2State = 0;
}
else if (tr2Val >= TRIM_ADC_LOW_THRESHOLD + TRIM_ADC_HYSTERESIS &&
tr2Val <= TRIM_ADC_MID_LOW) {
result |= 1 << THD; // Navigation LEFT
lastTrim2State = 1;
}
else if (tr2Val >= TRIM_ADC_MID_HIGH &&
tr2Val <= TRIM_ADC_HIGH_THRESHOLD - TRIM_ADC_HYSTERESIS) {
result |= 1 << THU; // Navigation RIGHT
lastTrim2State = 2;
}
else if (tr2Val >= TRIM_ADC_HIGH_THRESHOLD) {
// Right physical position -> not used for navigation
lastTrim2State = 0;
}
else {
// In dead zone - apply hysteresis based on last state
if (lastTrim2State == 1 && tr2Val <= (TRIM_ADC_MID_LOW + TRIM_ADC_HYSTERESIS)) {
result |= 1 << THD; // Maintain LEFT if close to threshold
}
else if (lastTrim2State == 2 && tr2Val >= (TRIM_ADC_MID_HIGH - TRIM_ADC_HYSTERESIS)) {
result |= 1 << THU; // Maintain RIGHT if close to threshold
}
else {
lastTrim2State = 0; // Reset to neutral
}
}
#endif

return result;
}
}
69 changes: 53 additions & 16 deletions radio/src/targets/pl18/nb4p_switch_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@
* GNU General Public License for more details.
*/

/*
* FlySky NB4+ Switch Driver
*
* Hardware mapping:
* - SW1: ADC_RAW1 (PC.01) - ADC1_IN11 - getAnalogValue(4)
* - SW2: ADC_RAW2 (PC.00) - ADC1_IN10 - getAnalogValue(5) (multiplexed)
* - SW3: ADC_RAW2 (PC.00) - ADC1_IN10 - getAnalogValue(5) (multiplexed)
* - SW4: Not available in current hardware revision
*
* ADC Values (12-bit: 0-4095):
* - Switch UP (released): ~4095 (pulled high)
* - Switch DOWN (pressed): ~0 (pulled low)
* - Multiplexed switches use voltage divider for detection
*/

#include "hal/switch_driver.h"
#include "stm32_switch_driver.h"
//#include "stm32_gpio_driver.h"
Expand All @@ -30,32 +45,54 @@

#include <stdlib.h>

static const char _switch_names[][4] = {"SW2", "SW3"};
// NB4P switch mapping based on hardware ADC channels:
// - SW1: ADC_RAW1 (channel 4) - PC.01
// - SW2, SW3: ADC_RAW2 (channel 5) - PC.00 (multiplexed)
static const char _switch_names[][4] = {"SW1", "SW2", "SW3", "SW4"};

// ADC thresholds for reliable switch detection (12-bit ADC: 0-4095 range)
// Improved thresholds with hysteresis to prevent false triggers
#define SW_ADC_LOW_THRESHOLD 400 // Below this = switch active (was 512)
#define SW_ADC_MID_LOW 1400 // Mid-low range for multiplexed switches
#define SW_ADC_MID_HIGH 2650 // Mid-high range for multiplexed switches
#define SW_ADC_HIGH_THRESHOLD 3500 // Above this = switch active (was 3584)

SwitchHwPos boardSwitchGetPosition(uint8_t idx)
{
if (idx <= 1) {
// uint16_t swVal = getAnalogValue(4);
// SW1 is on ADC channel 4 (RAW1)
if (idx == 0) {
uint16_t swVal = getAnalogValue(4);
return (swVal < SW_ADC_LOW_THRESHOLD) ? SWITCH_HW_DOWN : SWITCH_HW_UP;
}

// SW2, SW3 are multiplexed on ADC channel 5 (RAW2)
else if (idx <= 2) {
uint16_t swVal = getAnalogValue(5);
if (idx == 0 && (swVal >= 3584 || (swVal >= 1536 && swVal < 2560))) {
return SWITCH_HW_DOWN;
}
if (idx == 1 && (swVal < 512 || (swVal >= 1536 && swVal < 2560))) {
return SWITCH_HW_DOWN;

if (idx == 1) { // SW2
// SW2 active when ADC in low range or mid-low range
return (swVal < SW_ADC_LOW_THRESHOLD ||
(swVal >= SW_ADC_MID_LOW && swVal < SW_ADC_MID_HIGH)) ?
SWITCH_HW_DOWN : SWITCH_HW_UP;
}
/* } else if (idx <= 3) {
uint16_t swVal = getAnalogValue(5);
if (idx == 3 && (swVal < 512 || (swVal >= 1536 && swVal < 2560))) {
return SWITCH_HW_DOWN;
else { // SW3 (idx == 2)
// SW3 active when ADC in high range or mid-high range
return (swVal > SW_ADC_HIGH_THRESHOLD ||
(swVal >= SW_ADC_MID_LOW && swVal < SW_ADC_MID_HIGH)) ?
SWITCH_HW_DOWN : SWITCH_HW_UP;
}
if (idx == 2 && (swVal >= 3584 || (swVal >= 1536 && swVal < 2560))) {
return SWITCH_HW_DOWN;
}*/
}

// SW4 - Future expansion (currently not implemented in hardware)
else if (idx == 3) {
// For future hardware revision that might add SW4
return SWITCH_HW_UP; // Default to UP position
}

return SWITCH_HW_UP;
}

const char* boardSwitchGetName(uint8_t idx)
{
{
return _switch_names[idx];
}
Loading