feat(radio): Senduwing H17 support (#7764)

Co-authored-by: 3djc <3djc@gh.com>
Co-authored-by: Risto <rotorman@users.noreply.github.com>
This commit is contained in:
3djc
2026-09-13 18:12:22 +10:00
committed by GitHub
co-authored by 3djc Risto
parent 65bb97dbcc
commit 244aab8919
46 changed files with 4473 additions and 23 deletions
+1
View File
@@ -143,6 +143,7 @@ body:
- RadioMaster TX16S / TX16S Mark II
- RadioMaster TX16S MK3
- RadioMaster Zorro
- Senduwing H17
- Other (Please specify below)
validations:
required: true
+2
View File
@@ -50,6 +50,7 @@ jobs:
- t22
- c14
- v12
- h17
container:
image: ghcr.io/edgetx/edgetx-dev:latest
volumes:
@@ -98,6 +99,7 @@ jobs:
- t22
- c14
- v12
- h17
container:
image: ghcr.io/edgetx/edgetx-dev:latest
volumes:
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
- id: color-4
target: [nb4p, st16, pa01, c14]
- id: color-5
target: [t15pro, tx16smk3, gx15]
target: [t15pro, tx16smk3, gx15, h17]
steps:
- name: Check out the repo
+1
View File
@@ -41,6 +41,7 @@ jobs:
- tx15;gx15
- t15pro
- v12
- h17
container:
image: ghcr.io/edgetx/edgetx-dev:latest
volumes:
+1 -1
View File
@@ -177,7 +177,7 @@ jobs:
> If you are using a STM32 F2 based radio, *stop right now*, this release is not for you! EdgeTX v2.11 is the last release series to support these radios. Check [this list](https://hackmd.io/@edgetx/B12oQyQKye) if you are unsure if this affects you. Because of this, EdgeTX v2.11 will continue to be supported alongside 2.12, 3.0, etc., with a focus on bug fixes.
> [!WARNING]
> Radios based on STM32H7 MCUs update differently to older radios. Do **not** use the "Flash via USB" option in EdgeTX Buddy with them (yet). To update radios such as the Flysky PA01 & ST16, Jumper T15 Pro, RM TX15, RM GX15 and RM TX16S MK3, please follow the instructions in the [EdgeTX Manual](https://manual.edgetx.org/installing-and-updating-edgetx/updating-your-stm32h7-radio).
> Radios based on STM32H7 MCUs update differently to older radios. Do **not** use the "Flash via USB" option in EdgeTX Buddy with them (yet). To update radios such as the Flysky PA01 & ST16, Jumper T15 Pro, RM TX15, RM GX15, RM TX16S MK3 and Senduwing H17, please follow the instructions in the [EdgeTX Manual](https://manual.edgetx.org/installing-and-updating-edgetx/updating-your-stm32h7-radio).
> [!NOTE]
> For MacOS users, Companion is now only compiled for MacOS 12 and above. Stay with v2.10.5 or earlier if you need support for an older version of MacOS.
+6
View File
@@ -339,6 +339,12 @@
"displayName": "RadioMaster Zorro",
"inherits": "firmware",
"cacheVariables": { "PCB": "X7", "PCBREV": "ZORRO" }
},
{
"name": "h17",
"displayName": "Senduwing H17",
"inherits": "firmware",
"cacheVariables": { "PCB": "H17" }
}
],
"buildPresets": [
+4 -2
View File
@@ -92,9 +92,11 @@ void BoardJson::afterLoadFixups(Board::Type board, InputsTable * inputs, Switche
{
// TODO json files do not contain gyro defs
// Radio cmake directive IMU is currently used
if (IS_TARANIS_XLITES(board) || IS_FAMILY_HORUS_OR_T16(board) ||
// H17 has no IMU
if ((IS_TARANIS_XLITES(board) || IS_FAMILY_HORUS_OR_T16(board) ||
IS_RADIOMASTER_TX15(board) || IS_RADIOMASTER_GX15(board) ||
IS_RADIOMASTER_TX16SMK3(board) || IS_FLYSKY_PA01(board)) {
IS_RADIOMASTER_TX16SMK3(board) || IS_FLYSKY_PA01(board)) &&
!IS_SENDUWING_H17(board)) {
if (getInputIndex(inputs, "TILT_X", Board::LVT_TAG) < 0) {
InputDefn defn;
defn.type = AIT_FLEX;
+12 -1
View File
@@ -173,6 +173,8 @@ uint32_t Boards::getFourCC(Type board)
return 0x4F78746F;
case BOARD_HELLORADIOSKY_V16:
return 0x4E78746F;
case BOARD_SENDUWING_H17:
return 0x5078746F;
default:
return 0;
}
@@ -237,6 +239,7 @@ int Boards::getEEpromSize(Board::Type board)
case BOARD_FLYSKY_NB4P:
case BOARD_FLYSKY_ST16:
case BOARD_IFLIGHT_COMMANDO14:
case BOARD_SENDUWING_H17:
case BOARD_FATFISH_F16:
case BOARD_HELLORADIOSKY_V12:
case BOARD_HELLORADIOSKY_V16:
@@ -305,6 +308,7 @@ int Boards::getFlashSize(Type board)
case BOARD_FLYSKY_NB4P:
case BOARD_FLYSKY_ST16: // 8MB SDRAM
case BOARD_IFLIGHT_COMMANDO14: // 8MB SDRAM
case BOARD_SENDUWING_H17: // 8MB SDRAM
case BOARD_FATFISH_F16:
case BOARD_HELLORADIOSKY_V12: // 8MB SDRAM
case BOARD_HELLORADIOSKY_V16:
@@ -425,7 +429,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
IS_RADIOMASTER_ZORRO(board) || IS_RADIOMASTER_TX15(board) ||
IS_JUMPER_T15PRO(board) || IS_FLYSKY_PA01(board) ||
IS_FLYSKY_ST16(board) || IS_RADIOMASTER_TX16SMK3(board) ||
IS_IFLIGHT_C14(board) || IS_RADIOMASTER_GX15(board));
IS_IFLIGHT_C14(board) || IS_RADIOMASTER_GX15(board) ||
IS_SENDUWING_H17(board));
default:
return getBoardJson(board)->getCapability(capability);
@@ -691,6 +696,8 @@ QString Boards::getBoardName(Board::Type board)
return "iFlight Commando 8";
case BOARD_IFLIGHT_COMMANDO14:
return "iFlight Commando 14";
case BOARD_SENDUWING_H17:
return "Senduwing H17";
case BOARD_FATFISH_F16:
return "Fatfish F16";
case BOARD_HELLORADIOSKY_V16:
@@ -815,6 +822,7 @@ int Boards::getDefaultInternalModules(Board::Type board)
case BOARD_RADIOMASTER_TX16SMK3:
case BOARD_IFLIGHT_COMMANDO8:
case BOARD_IFLIGHT_COMMANDO14:
case BOARD_SENDUWING_H17:
case BOARD_JUMPER_BUMBLEBEE:
case BOARD_JUMPER_T12MAX:
case BOARD_JUMPER_T14:
@@ -914,6 +922,9 @@ void Boards::getBattRange(Board::Type board, int& vmin, int& vmax, unsigned int&
case BOARD_FLYSKY_PA01:
BR(70, 86, 80)
break;
case BOARD_SENDUWING_H17:
BR(70, 86, 74)
break;
case BOARD_IFLIGHT_COMMANDO8:
BR(30, 42, 32)
break;
+7 -1
View File
@@ -102,6 +102,7 @@ namespace Board {
BOARD_HELLORADIOSKY_V14LCD,
BOARD_IFLIGHT_COMMANDO14,
BOARD_HELLORADIOSKY_V12,
BOARD_SENDUWING_H17,
BOARD_TYPE_COUNT,
BOARD_TYPE_MAX = BOARD_TYPE_COUNT - 1
};
@@ -772,6 +773,11 @@ inline bool IS_IFLIGHT_C14(Board::Type board)
return (board == Board::BOARD_IFLIGHT_COMMANDO14);
}
inline bool IS_SENDUWING_H17(Board::Type board)
{
return (board == Board::BOARD_SENDUWING_H17);
}
inline bool IS_FAMILY_PL18(Board::Type board)
{
return IS_FLYSKY_PL18(board) || IS_FLYSKY_PL18EV(board) || IS_FLYSKY_PL18U(board);
@@ -858,7 +864,7 @@ inline bool IS_FAMILY_HORUS_OR_T16(Board::Type board)
IS_FLYSKY_NV14(board)/*generally*/ || IS_FLYSKY_EL18(board)/*generally*/
|| IS_FAMILY_PL18(board) || IS_FLYSKY_ST16(board)/*generally*/ ||
IS_FLYSKY_PA01(board)/*generally*/ || IS_FLYSKY_NB4P(board)/*generally*/ ||
IS_IFLIGHT_C14(board)/*generally*/;
IS_IFLIGHT_C14(board)/*generally*/ || IS_SENDUWING_H17(board)/*generally*/;
}
inline bool HAS_LARGE_LCD(Board::Type board)
@@ -93,7 +93,8 @@ int OpenTxFirmware::getCapability(::Capability capability)
return id.contains("internalelrs") || IS_RADIOMASTER_TX12_MK2(board) ||
IS_IFLIGHT_COMMANDO8(board) || IS_RADIOMASTER_BOXER(board) ||
IS_RADIOMASTER_POCKET(board) || IS_JUMPER_T20(board) ||
IS_RADIOMASTER_MT12(board) || IS_RADIOMASTER_TX15(board) || IS_RADIOMASTER_GX15(board) || IS_JUMPER_T15PRO(board) || IS_JUMPER_T22(board);
IS_RADIOMASTER_MT12(board) || IS_RADIOMASTER_TX15(board) || IS_RADIOMASTER_GX15(board) || IS_JUMPER_T15PRO(board) || IS_JUMPER_T22(board) ||
IS_SENDUWING_H17(board);
case HasIntModuleFlySky:
return id.contains("afhds2a") || id.contains("afhds3") ||
IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FAMILY_PL18(board);
@@ -768,6 +769,12 @@ void registerOpenTxFirmwares()
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX + AFHDS2A + AFHDS3);
/* Senduwing H17 board */
firmware = new OpenTxFirmware(FIRMWAREID("h17"), Firmware::tr("Senduwing H17"), BOARD_SENDUWING_H17);
addOpenTxFrskyOptions(firmware);
addOpenTxRfOptions(firmware, FLEX);
registerOpenTxFirmware(firmware);
Firmware::sortRegisteredFirmwares();
Firmware::setDefaultVariant(Firmware::getFirmwareForFlavour("tx16s"));
Firmware::setCurrentVariant(Firmware::getDefaultVariant());
+1
View File
@@ -28,6 +28,7 @@ Selects the radio target.
| `T15PRO` | Jumper T15 Pro |
| `ST16` | Siyi ST16 |
| `PA01` | FrSky PA01 |
| `H17` | Senduwing H17 |
### `PCBREV`
+2 -1
View File
@@ -42,7 +42,8 @@
["RadioMaster TX15", "tx15-"],
["RadioMaster TX16S / TX16S Mark II", "tx16s-"],
["RadioMaster TX16S MK3", "tx16smk3-"],
["RadioMaster Zorro","zorro-"]
["RadioMaster Zorro","zorro-"],
["Senduwing H17", "h17-"]
],
"changelog": "- Major initial bug fixes"
}
+3 -1
View File
@@ -1,7 +1,7 @@
include(CMakeForceCompiler)
include(Bitmaps)
set(PCB_TYPES X9LITE X9LITES X7 XLITE XLITES X9D X9D+ X9E X10 X12S PL18 T15PRO ST16 PA01 TX15 GX15 TX16SMK3 T22 V12)
set(PCB_TYPES X9LITE X9LITES X7 XLITE XLITES X9D X9D+ X9E X10 X12S PL18 T15PRO ST16 PA01 TX15 GX15 TX16SMK3 T22 V12 H17)
set(RADIO_LANGUAGES CN CZ DA DE EN ES FI FR HE HU IT JP KO NL PL PT RU SE SK TW UA)
set(TTS_LANGUAGES CN CZ DA DE EN ES FI FR HE HU IT JP KO NL PL PT RU SE SK TW UA)
@@ -107,6 +107,8 @@ elseif(PCB STREQUAL GX15)
include(targets/gx15/CMakeLists.txt)
elseif(PCB STREQUAL TX16SMK3)
include(targets/tx16smk3/CMakeLists.txt)
elseif(PCB STREQUAL H17)
include(targets/h17/CMakeLists.txt)
elseif(PCB STREQUAL T22)
include(targets/t22/CMakeLists.txt)
elseif(PCB STREQUAL STM32H7S78_DK)
+476
View File
@@ -0,0 +1,476 @@
{
"adc_inputs": {
"adcs": [
{
"name": "MAIN",
"adc": "ADC1",
"dma": "DMA2",
"dma_channel": "LL_DMAMUX1_REQ_ADC1",
"dma_stream": "LL_DMA_STREAM_4",
"dma_stream_irq": "DMA2_Stream4_IRQn",
"dma_stream_irq_handler": "DMA2_Stream4_IRQHandler",
"sample_time": "LL_ADC_SAMPLINGTIME_8CYCLES_5"
},
{
"name": "EXT",
"adc": "ADC3",
"dma": "DMA2",
"dma_channel": "LL_DMAMUX1_REQ_ADC3",
"dma_stream": "LL_DMA_STREAM_0",
"dma_stream_irq": "DMA2_Stream0_IRQn",
"dma_stream_irq_handler": "DMA2_Stream0_IRQHandler",
"sample_time": "LL_ADC_SAMPLINGTIME_8CYCLES_5"
}
],
"inputs": [
{
"name": "LH",
"type": "STICK",
"adc": "MAIN",
"gpio": "GPIOA",
"pin": "LL_GPIO_PIN_6",
"channel": "LL_ADC_CHANNEL_3",
"inverted": true
},
{
"name": "LV",
"type": "STICK",
"adc": "MAIN",
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_3",
"channel": "LL_ADC_CHANNEL_13",
"inverted": true
},
{
"name": "RV",
"type": "STICK",
"adc": "MAIN",
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_4",
"channel": "LL_ADC_CHANNEL_4"
},
{
"name": "RH",
"type": "STICK",
"adc": "MAIN",
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_5",
"channel": "LL_ADC_CHANNEL_8",
"inverted": true
},
{
"name": "P1",
"type": "FLEX",
"adc": "MAIN",
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_1",
"channel": "LL_ADC_CHANNEL_11",
"label": "S1",
"short_label": "1",
"default": "POT"
},
{
"name": "SL1",
"type": "FLEX",
"adc": "EXT",
"gpio": "GPIOH",
"pin": "LL_GPIO_PIN_2",
"channel": "LL_ADC_CHANNEL_13",
"label": "S2",
"short_label": "2",
"default": "POT"
},
{
"name": "SL2",
"type": "FLEX",
"adc": "MAIN",
"gpio": "GPIOA",
"pin": "LL_GPIO_PIN_7",
"channel": "LL_ADC_CHANNEL_7",
"label": "S3",
"short_label": "3",
"default": "POT"
},
{
"name": "P3",
"type": "FLEX",
"adc": "MAIN",
"gpio": "GPIOB",
"pin": "LL_GPIO_PIN_1",
"channel": "LL_ADC_CHANNEL_5",
"label": "S4",
"short_label": "4",
"default": "POT"
},
{
"name": "P2",
"type": "FLEX",
"adc": "MAIN",
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_2",
"channel": "LL_ADC_CHANNEL_12",
"default": "MULTIPOS",
"label": "6POS",
"short_label": "6"
},
{
"name": "EXT1",
"type": "FLEX",
"adc": "MAIN",
"gpio": "GPIOA",
"pin": "LL_GPIO_PIN_1",
"channel": "LL_ADC_CHANNEL_17",
"default": "NONE",
"label": "EXT1",
"short_label": "E1"
},
{
"name": "EXT2",
"type": "FLEX",
"adc": "MAIN",
"gpio": "GPIOA",
"pin": "LL_GPIO_PIN_0",
"channel": "LL_ADC_CHANNEL_16",
"default": "NONE",
"label": "EXT2",
"short_label": "E2"
},
{
"name": "VBAT",
"type": "VBAT",
"adc": "EXT",
"gpio": "GPIOH",
"pin": "LL_GPIO_PIN_3",
"channel": "LL_ADC_CHANNEL_14"
},
{
"name": "RTC_BAT",
"type": "RTC_BAT",
"adc": "EXT",
"gpio": null,
"pin": null,
"channel": "LL_ADC_CHANNEL_VBAT"
}
]
},
"switches": [
{
"name": "SA",
"type": "3POS",
"flags": 0,
"inverted": true,
"default": "2POS",
"display": null,
"gpio_high": "GPIOB",
"pin_high": "LL_GPIO_PIN_0",
"gpio_low": "GPIOE",
"pin_low": "LL_GPIO_PIN_4",
"is_cfs": false
},
{
"name": "SB",
"type": "3POS",
"flags": 0,
"inverted": false,
"default": "3POS",
"display": null,
"gpio_high": "GPIOD",
"pin_high": "LL_GPIO_PIN_5",
"gpio_low": "GPIOJ",
"pin_low": "LL_GPIO_PIN_7",
"is_cfs": false
},
{
"name": "SC",
"type": "3POS",
"flags": 0,
"inverted": false,
"default": "3POS",
"display": null,
"gpio_high": "GPIOD",
"pin_high": "LL_GPIO_PIN_3",
"gpio_low": "GPIOJ",
"pin_low": "LL_GPIO_PIN_0",
"is_cfs": false
},
{
"name": "SD",
"type": "3POS",
"flags": 0,
"inverted": true,
"default": "2POS",
"display": null,
"gpio_high": "GPIOD",
"pin_high": "LL_GPIO_PIN_11",
"gpio_low": "GPIOH",
"pin_low": "LL_GPIO_PIN_15",
"is_cfs": false
},
{
"name": "SE",
"type": "3POS",
"flags": 0,
"inverted": false,
"default": "3POS",
"display": null,
"gpio_high": "GPIOE",
"pin_high": "LL_GPIO_PIN_6",
"gpio_low": "GPIOE",
"pin_low": "LL_GPIO_PIN_5",
"is_cfs": false
},
{
"name": "SF",
"type": "2POS",
"flags": 0,
"inverted": false,
"default": "2POS",
"display": null,
"gpio": "GPIOB",
"pin": "LL_GPIO_PIN_8",
"is_cfs": false
},
{
"name": "SG",
"type": "2POS",
"flags": 0,
"inverted": false,
"default": "TOGGLE",
"display": null,
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_6",
"is_cfs": false
},
{
"name": "SH",
"type": "2POS",
"flags": 0,
"inverted": false,
"default": "TOGGLE",
"display": null,
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_8",
"is_cfs": false
},
{
"name": "SI",
"type": "2POS",
"flags": 0,
"inverted": false,
"default": "2POS",
"display": null,
"gpio": "GPIOH",
"pin": "LL_GPIO_PIN_14",
"is_cfs": false
},
{
"name": "SJ",
"type": "2POS",
"flags": 0,
"inverted": false,
"default": "2POS",
"display": null,
"gpio": "GPIOH",
"pin": "LL_GPIO_PIN_13",
"is_cfs": false
}
],
"keys": [
{
"gpio": "GPIOG",
"pin": "LL_GPIO_PIN_3",
"active_low": true,
"key": "KEY_EXIT",
"name": "EXIT",
"label": "RTN"
},
{
"gpio": "GPIOG",
"pin": "LL_GPIO_PIN_12",
"active_low": true,
"key": "KEY_ENTER",
"name": "ENTER",
"label": "Enter"
},
{
"gpio": "GPIOG",
"pin": "LL_GPIO_PIN_7",
"active_low": true,
"key": "KEY_PAGEUP",
"name": "PAGEUP",
"label": "PAGE<"
},
{
"gpio": "GPIOA",
"pin": "LL_GPIO_PIN_8",
"active_low": true,
"key": "KEY_PAGEDN",
"name": "PAGEDN",
"label": "PAGE>"
},
{
"gpio": "GPIOE",
"pin": "LL_GPIO_PIN_3",
"active_low": true,
"key": "KEY_MODEL",
"name": "MDL",
"label": "MDL"
},
{
"gpio": "GPIOI",
"pin": "LL_GPIO_PIN_15",
"active_low": true,
"key": "KEY_TELE",
"name": "TELE",
"label": "TELE"
},
{
"gpio": "GPIOB",
"pin": "LL_GPIO_PIN_2",
"active_low": true,
"key": "KEY_SYS",
"name": "SYS",
"label": "SYS"
}
],
"trims": [
{
"name": "T1",
"dec": {
"gpio": "GPIOG",
"pin": "LL_GPIO_PIN_2",
"active_low": true
},
"inc": {
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_9",
"active_low": true
},
"active_low": true
},
{
"name": "T2",
"dec": {
"gpio": "GPIOB",
"pin": "LL_GPIO_PIN_12",
"active_low": true
},
"inc": {
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_10",
"active_low": true
},
"active_low": true
},
{
"name": "T3",
"dec": {
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_12",
"active_low": true
},
"inc": {
"gpio": "GPIOC",
"pin": "LL_GPIO_PIN_11",
"active_low": true
},
"active_low": true
},
{
"name": "T4",
"dec": {
"gpio": "GPIOD",
"pin": "LL_GPIO_PIN_2",
"active_low": true
},
"inc": {
"gpio": "GPIOI",
"pin": "LL_GPIO_PIN_9",
"active_low": true
},
"active_low": true
}
],
"display": {
"lcd_w": 480,
"lcd_h": 272,
"lcd_phys_w": 480,
"lcd_phys_h": 272,
"lcd_depth": 16,
"oled_screen": false,
"lcd_vertical_invert": true,
"ltdc_irq_prio": 4,
"dma_screen_irq_prio": 6
},
"backlight": {
"backlight_gpio": "GPIO_PIN(GPIOA, 10)",
"backlight_timer": "TIM1",
"backlight_timer_channel": "LL_TIM_CHANNEL_CH3",
"backlight_gpio_af": "GPIO_AF1",
"backlight_timer_freq": "(PERI2_FREQUENCY * TIMER_MULT_APB2)"
},
"haptic": {
"haptic_pwm": true,
"haptic_gpio": "GPIO_PIN(GPIOC, 7)",
"haptic_gpio_timer": "TIM3",
"haptic_gpio_af": "GPIO_AF2",
"haptic_timer_output_enable": "TIM_CCER_CC2E | TIM_CCER_CC2NE",
"haptic_timer_mode": "TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE",
"haptic_timer_compare_value": "HAPTIC_GPIO_TIMER->CCR2"
},
"leds": {
"status_leds": true,
"gpio_led_gpio_on": "gpio_set",
"gpio_led_gpio_off": "gpio_clear",
"led_red_gpio": "GPIO_PIN(GPIOI, 8)",
"led_green_gpio": "GPIO_PIN(GPIOI, 11)",
"led_blue_gpio": "GPIO_PIN(GPIOI, 10)"
},
"timers": {
"cpu_freq": 480000000,
"peri1_frequency": 120000000,
"peri2_frequency": 120000000,
"timer_mult_apb1": 2,
"timer_mult_apb2": 2,
"ms_timer": "TIM14",
"ms_timer_irqn": "TIM8_TRG_COM_TIM14_IRQn",
"ms_timer_irqhandler": "TIM8_TRG_COM_TIM14_IRQHandler",
"mixer_scheduler_timer": "TIM12",
"mixer_scheduler_timer_freq": "(PERI1_FREQUENCY * TIMER_MULT_APB1)",
"mixer_scheduler_timer_irqn": "TIM8_BRK_TIM12_IRQn",
"mixer_scheduler_timer_irqhandler": "TIM8_BRK_TIM12_IRQHandler"
},
"rotenc": {
"rotary_encoder_inverted": true,
"rotary_encoder_gpio_a": "GPIOI",
"rotary_encoder_gpio_pin_a": "LL_GPIO_PIN_7",
"rotary_encoder_gpio_b": "GPIOJ",
"rotary_encoder_gpio_pin_b": "LL_GPIO_PIN_8",
"rotary_encoder_position": "(((ROTARY_ENCODER_GPIO_A->IDR >> 7) & 0x01)|((ROTARY_ENCODER_GPIO_B->IDR >> 7) & 0x02))",
"rotary_encoder_exti_line1": "LL_EXTI_LINE_7",
"rotary_encoder_exti_line2": "LL_EXTI_LINE_8",
"exti": [
{ "irq": "EXTI9_5_IRQ", "priority": 5 }
],
"rotary_encoder_exti_port_a": "LL_SYSCFG_EXTI_PORTI",
"rotary_encoder_exti_port_b": "LL_SYSCFG_EXTI_PORTJ",
"rotary_encoder_exti_sys_line1": "LL_SYSCFG_EXTI_LINE7",
"rotary_encoder_exti_sys_line2": "LL_SYSCFG_EXTI_LINE8",
"rotary_encoder_timer": "TIM17",
"rotary_encoder_timer_irqn": "TIM17_IRQn",
"rotary_encoder_timer_irqhandler": "TIM17_IRQHandler"
},
"key_lock_combo": [
"KEY_SYS",
"KEY_MODEL"
],
"hardware": {
"has_audio_mute": true,
"has_ext_module_support": true,
"has_int_module_support": true,
"sport_max_baudrate": 400000,
"surface": false,
"cpu": "STM32H750xB",
"cpu_type": "STM32H7"
}
}
@@ -0,0 +1,78 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "hal/gpio.h"
#include "stm32_gpio.h"
#include "stm32_timer.h"
#include "stm32_pulse_driver.h"
#include "edgetx_types.h"
#include "board.h"
#include "globals.h"
static const stm32_pulse_timer_t _bl_timer = {
.GPIO = (gpio_t)BACKLIGHT_GPIO,
.GPIO_Alternate = BACKLIGHT_GPIO_AF,
.TIMx = BACKLIGHT_TIMER,
.TIM_Freq = BACKLIGHT_TIMER_FREQ,
.TIM_Channel = BACKLIGHT_TIMER_CHANNEL,
.TIM_IRQn = (IRQn_Type)-1,
.DMAx = nullptr,
.DMA_Stream = 0,
.DMA_Channel = 0,
.DMA_IRQn = (IRQn_Type)-1,
.DMA_TC_CallbackPtr = nullptr,
};
void backlightLowInit()
{
gpio_init(BACKLIGHT_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW);
gpio_clear(BACKLIGHT_GPIO);
}
void backlightInit()
{
stm32_pulse_init(&_bl_timer, 10000);
stm32_pulse_config_output(&_bl_timer, true, LL_TIM_OCMODE_PWM1, 100);
LL_TIM_SetAutoReload(_bl_timer.TIMx, 100);
LL_TIM_EnableCounter(_bl_timer.TIMx);
}
void backlightEnable(uint8_t dutyCycle)
{
stm32_pulse_set_cmp_val(&_bl_timer, dutyCycle);
}
void lcdOff() {
backlightEnable(0);
}
void lcdOn(){
backlightEnable(BACKLIGHT_LEVEL_MAX);
}
bool boardBacklightOn;
bool isBacklightEnabled()
{
return boardBacklightOn;
}
+212
View File
@@ -0,0 +1,212 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "stm32_adc.h"
#include "stm32_gpio.h"
#include "stm32_i2c_driver.h"
#include "stm32_hal.h"
#include "stm32_spi.h"
#include "flash_driver.h"
#include "extflash_driver.h"
#include "board.h"
#include "boards/generic_stm32/module_ports.h"
#include "hal/adc_driver.h"
#include "hal/flash_driver.h"
#include "hal/trainer_driver.h"
#include "hal/rotary_encoder.h"
#include "hal/switch_driver.h"
#include "hal/abnormal_reboot.h"
#include "hal/watchdog_driver.h"
#include "hal/usb_driver.h"
#include "hal/gpio.h"
#include "globals.h"
#include "sdcard.h"
#include "debug.h"
#include "keys.h"
#include "gyro.h"
#include "flysky_gimbal_driver.h"
#include "timers_driver.h"
#include "touch_driver.h"
#include <string.h>
// common ADC driver
extern const etx_hal_adc_driver_t _adc_driver;
void INTERNAL_MODULE_ON()
{
gpio_set(INTMODULE_PWR_GPIO);
}
void INTERNAL_MODULE_OFF()
{
gpio_clear(INTMODULE_PWR_GPIO);
}
void EXTERNAL_MODULE_ON()
{
gpio_set(EXTMODULE_PWR_GPIO);
}
void EXTERNAL_MODULE_OFF()
{
gpio_clear(EXTMODULE_PWR_GPIO);
}
void boardBLEarlyInit()
{
timersInit();
#if defined(USB_CHARGER)
usbChargerInit();
#endif
}
void boardBLPreJump()
{
ExtFLASH_Init();
SDRAM_Init();
// Stop 1ms timer
MS_TIMER->CR1 &= ~TIM_CR1_CEN;
}
void boardBLInit()
{
#if defined(ROTARY_ENCODER_NAVIGATION)
rotaryEncoderInit();
#endif
ExtFLASH_Init();
SDRAM_Init();
// register external FLASH for DFU
usbRegisterDFUMedia((void*)extflash_dfu_media);
// register internal & external FLASH for UF2
flashRegisterDriver(FLASH_BANK1_BASE, BOOTLOADER_SIZE, &stm32_flash_driver);
flashRegisterDriver(QSPI_BASE, 8 * 1024 * 1024, &extflash_driver);
}
void boardInit()
{
// enable interrupts
__enable_irq();
ledInit();
boardInitModulePorts();
pwrInit();
delaysInit();
timersInit();
#if !defined(POWER_LED_BLUE)
ledBlue();
#else
ledGreen();
#endif
ExtFLASH_InitRuntime();
// register internal & external FLASH for UF2
flashRegisterDriver(FLASH_BANK1_BASE, BOOTLOADER_SIZE, &stm32_flash_driver);
flashRegisterDriver(QSPI_BASE, QSPI_FLASH_SIZE, &extflash_driver);
#if defined(FLYSKY_GIMBAL)
auto inittime = flysky_gimbal_init();
if (inittime)
TRACE("Serial gimbal detected in %d ms", inittime);
else
TRACE("No serial gimbal detected");
#endif
usbInit();
#if !defined(DEBUG_SEGGER_RTT)
// This is needed to prevent radio from starting when usb is plugged to charge
if (!UNEXPECTED_SHUTDOWN() && usbPlugged()) {
while (usbPlugged() && !pwrPressed()) {
delay_ms(1000);
}
if (!pwrPressed()) {
pwrOff();
// Wait power to drain
while (true) {
}
}
}
#endif
keysInit();
switchInit();
rotaryEncoderInit();
touchPanelInit();
audioInit();
adcInit(&_adc_driver);
hapticInit();
rtcInit(); // RTC must be initialized before rambackupRestore() is called
}
extern void rtcDisableBackupReg();
void boardOff()
{
lcdOff();
while (pwrPressed()) {
WDG_RESET();
}
SysTick->CTRL = 0; // turn off systick
// Shutdown the Haptic
hapticDone();
rtcDisableBackupReg();
pwrOff();
// We reach here only in forced power situations, such as hw-debugging with external power
// Enter STM32 stop mode / deep-sleep
// Code snippet from ST Nucleo PWR_EnterStopMode example
#define PDMode 0x00000000U
#if defined(PWR_CR1_MRUDS) && defined(PWR_CR1_LPUDS) && defined(PWR_CR1_FPDS)
MODIFY_REG(PWR->CR1, (PWR_CR1_PDDS | PWR_CR1_LPDS | PWR_CR1_FPDS | PWR_CR1_LPUDS | PWR_CR1_MRUDS), PDMode);
#elif defined(PWR_CR_MRLVDS) && defined(PWR_CR_LPLVDS) && defined(PWR_CR_FPDS)
MODIFY_REG(PWR->CR1, (PWR_CR1_PDDS | PWR_CR1_LPDS | PWR_CR1_FPDS | PWR_CR1_LPLVDS | PWR_CR1_MRLVDS), PDMode);
#else
// MODIFY_REG(PWR->CR1, (PWR_CR1_P_PDDS| PWR_CR1_LPDS), PDMode);
#endif /* PWR_CR_MRUDS && PWR_CR_LPUDS && PWR_CR_FPDS */
/* Set SLEEPDEEP bit of Cortex System Control Register */
SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
// To avoid HardFault at return address, end in an endless loop
while (1) {
}
}
+250
View File
@@ -0,0 +1,250 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#ifndef _BOARD_H_
#define _BOARD_H_
#include "definitions.h"
#include "edgetx_constants.h"
#include "board_common.h"
#include "hal.h"
#include "hal/serial_port.h"
#include "hal/watchdog_driver.h"
#define FLASHSIZE 0x1000000
#define BOOTLOADER_SIZE 0x10000
#define BOOTLOADER_ADDRESS 0x08000000
#define FIRMWARE_ADDRESS 0x90000000
#define FIRMWARE_LEN(fsize) (fsize)
#define FIRMWARE_MAX_LEN FLASHSIZE
#define APP_START_ADDRESS (uint32_t)(FIRMWARE_ADDRESS)
#define BOOTLOADER_KEYS 0x42
#define MB *1024*1024
#define LUA_MEM_EXTRA_MAX (2 MB) // max allowed memory usage for Lua bitmaps (in bytes)
#define LUA_MEM_MAX (6 MB) // max allowed memory usage for complete Lua (in bytes), 0 means unlimited
extern uint16_t sessionTimer;
#define SLAVE_MODE() (g_model.trainerData.mode == TRAINER_MODE_SLAVE)
// Board driver
void boardInit();
void boardOff();
// CPU Unique ID
#define LEN_CPU_UID (3*8+2)
void getCPUUniqueID(char * s);
// Flash Write driver
#define FLASH_PAGESIZE 256
void unlockFlash();
void lockFlash();
void flashWrite(uint32_t * address, const uint32_t * buffer);
uint32_t isFirmwareStart(const uint8_t * buffer);
uint32_t isBootloaderStart(const uint8_t * buffer);
// SDRAM driver
extern "C" void SDRAM_Init();
// Pulses driver
#if !defined(SIMU)
void INTERNAL_MODULE_ON();
void INTERNAL_MODULE_OFF();
void EXTERNAL_MODULE_ON();
void EXTERNAL_MODULE_OFF();
#define EXTERNAL_MODULE_PWR_OFF EXTERNAL_MODULE_OFF
#define BLUETOOTH_MODULE_ON() gpio_clear(BLUETOOTH_ON_GPIO)
#define BLUETOOTH_MODULE_OFF() gpio_set(BLUETOOTH_ON_GPIO)
#define IS_INTERNAL_MODULE_ON() (false)
#define IS_EXTERNAL_MODULE_ON() (gpio_read(EXTMODULE_PWR_GPIO) ? 1 : 0)
#else
#define INTERNAL_MODULE_OFF()
#define INTERNAL_MODULE_ON()
#define EXTERNAL_MODULE_ON()
#define EXTERNAL_MODULE_OFF()
#define BLUETOOTH_MODULE_ON()
#define BLUETOOTH_MODULE_OFF()
#define IS_INTERNAL_MODULE_ON() (false)
#define IS_EXTERNAL_MODULE_ON() (false)
#endif // defined(SIMU)
#if defined(FUNCTION_SWITCHES)
#define NUM_FUNCTIONS_SWITCHES 6
#define NUM_FUNCTIONS_GROUPS 3
#define DEFAULT_FS_CONFIG \
(SWITCH_2POS << 10) + (SWITCH_2POS << 8) + (SWITCH_2POS << 6) + \
(SWITCH_2POS << 4) + (SWITCH_2POS << 2) + (SWITCH_2POS << 0)
#define DEFAULT_FS_GROUPS \
(1 << 10) + (1 << 8) + (1 << 6) + (1 << 4) + (1 << 2) + \
(1 << 0) // Set all FS to group 1 to act like a 6pos
#define DEFAULT_FS_STARTUP_CONFIG \
((FS_START_PREVIOUS << 10) + (FS_START_PREVIOUS << 8) + \
(FS_START_PREVIOUS << 6) + (FS_START_PREVIOUS << 4) + \
(FS_START_PREVIOUS << 2) + \
(FS_START_PREVIOUS << 0)) // keep last state by default
#else
#define NUM_FUNCTIONS_SWITCHES 0
#endif
#define NUM_TRIMS 8
#define DEFAULT_STICK_DEADZONE 2
#define BATTERY_WARN 74 // 7.4V
#define BATTERY_MIN 70 // 6.8V
#define BATTERY_MAX 86 // 8.6V
// --- MOSFET ---- R1 --- MCU
// |__ R2 --- GND
//
#define VBAT_DIV_R1 120 // kOhms
#define VBAT_DIV_R2 39 // kOhms
#define VBAT_MOSFET_DROP 0 // * 10mV
#if defined(__cplusplus) && !defined(SIMU)
extern "C" {
#endif
// Power driver
#define SOFT_PWR_CTRL
#define POWER_ON_DELAY 10 // 1s
void pwrInit();
void extModuleInit();
uint32_t pwrCheck();
uint32_t lowPowerCheck();
void pwrOn();
void pwrSoftReboot();
void pwrOff();
void pwrResetHandler();
bool pwrPressed();
bool pwrOffPressed();
#if defined(PWR_EXTRA_SWITCH_GPIO)
bool pwrForcePressed();
#else
#define pwrForcePressed() false
#endif
uint32_t pwrPressedDuration();;
const etx_serial_port_t* auxSerialGetPort(int port_nr);
#define AUX_SERIAL_POWER_ON()
#define AUX_SERIAL_POWER_OFF()
// LED driver
void ledInit();
void ledOff();
void ledRed();
void ledBlue();
void ledGreen();
// LCD driver
void lcdSetInitalFrameBuffer(void* fbAddress);
void lcdInit();
void lcdCopy(void * dest, void * src);
void lcdOff();
void lcdOn();
#define lcdRefreshWait(...)
// Backlight driver
#define BACKLIGHT_LEVEL_MAX 100
#define BACKLIGHT_FORCED_ON BACKLIGHT_LEVEL_MAX + 1
#define BACKLIGHT_LEVEL_MIN 35
extern bool boardBacklightOn;
void backlightLowInit( void );
void backlightInit();
void backlightEnable(uint8_t dutyCycle);
void backlightFullOn();
bool isBacklightEnabled();
#define BACKLIGHT_ENABLE() \
{ \
boardBacklightOn = true; \
backlightEnable(BACKLIGHT_LEVEL_MAX - currentBacklightBright); \
}
#define BACKLIGHT_DISABLE() \
{ \
boardBacklightOn = false; \
backlightEnable(((g_eeGeneral.blOffBright == BACKLIGHT_LEVEL_MIN) && \
(g_eeGeneral.backlightMode != e_backlight_mode_off)) \
? 0 \
: g_eeGeneral.blOffBright); \
}
#if defined(__cplusplus) && !defined(SIMU)
}
#endif
// Audio driver
void audioInit();
void audioConsumeCurrentBuffer();
#define audioDisableIrq() // interrupts must stay enabled on Horus
#define audioEnableIrq() // interrupts must stay enabled on Horus
#define setSampleRate(freq)
void setScaledVolume(uint8_t volume);
void setVolume(uint8_t volume);
int32_t getVolume();
#define VOLUME_LEVEL_MAX 23
#define VOLUME_LEVEL_DEF 12
// Telemetry driver
#define INTMODULE_FIFO_SIZE 512
#define TELEMETRY_FIFO_SIZE 512
// Haptic driver
void hapticInit();
void hapticDone();
void hapticOff();
void hapticOn(uint32_t pwmPercent);
// Second serial port driver
//#define AUX_SERIAL
#define DEBUG_BAUDRATE 115200
#define LUA_DEFAULT_BAUDRATE 115200
extern uint8_t currentTrainerMode;
void checkTrainerSettings();
// Touch panel driver
bool touchPanelEventOccured();
struct TouchState touchPanelRead();
struct TouchState getInternalTouchState();
// USB Charger
#if defined(USB_CHARGER)
void usbChargerInit();
bool usbChargerLed();
#endif
#endif // _BOARD_H_
@@ -0,0 +1,154 @@
#include "extflash_driver.h"
#include "stm32_hal.h"
#include "stm32_qspi.h"
#include "board.h"
#define QSPI_NOR_PAGE_SIZE 256
int32_t ExtFLASH_Init()
{
if (stm32_qspi_nor_init() != 0) {
return -1;
}
return stm32_qspi_nor_memory_mapped();
}
void ExtFLASH_InitRuntime() { stm32_qspi_no_init_runtime(); }
//
// ETX HAL flash interface
//
static uint32_t extflash_get_size_kb()
{
return QSPI_FLASH_SIZE / 1024;
}
static uint32_t extflash_get_sector(uint32_t address)
{
address -= QSPI_BASE;
return address / (64 * 1024);
}
static uint32_t extflash_get_sector_size(uint32_t sector)
{
(void)sector;
return (64 * 1024);
}
static int extflash_erase_sector(uint32_t address)
{
address -= QSPI_BASE;
return stm32_qspi_nor_erase_sector(address);
}
static int extflash_program(uint32_t address, void* data, uint32_t len)
{
address -= QSPI_BASE;
int ret = 0;
while (len > 0) {
uint32_t size = (len > QSPI_NOR_PAGE_SIZE) ? QSPI_NOR_PAGE_SIZE : len;
ret = stm32_qspi_nor_program(address, data, size);
if (ret != 0) break;
len -= size;
address += size;
data = (uint8_t*)data + size;
}
return ret;
}
static int extflash_read(uint32_t address, void* data, uint32_t len)
{
address -= QSPI_BASE;
return stm32_qspi_nor_read(address, data, len);
}
const etx_flash_driver_t extflash_driver = {
.get_size_kb = extflash_get_size_kb,
.get_sector = extflash_get_sector,
.get_sector_size = extflash_get_sector_size,
.erase_sector = extflash_erase_sector,
.program = extflash_program,
.read = extflash_read,
};
#if defined(BOOT)
//
// USB DFU interface
//
#include "usbd_dfu.h"
#define MEDIA_DESC_STR "@External Flash /0x90000000/128*064Kg"
static uint16_t extflash_dfu_init()
{
return USBD_OK;
}
static uint16_t extflash_dfu_deinit()
{
return USBD_OK;
}
static uint16_t extflash_dfu_erase(uint32_t address)
{
if (extflash_erase_sector(address) < 0) {
return USBD_FAIL;
}
return USBD_OK;
}
static uint16_t extflash_dfu_write(uint8_t* src, uint8_t* dest, uint32_t len)
{
if (extflash_program((intptr_t)dest, (void*)src, len) < 0) {
return USBD_FAIL;
}
return USBD_OK;
}
static uint8_t* extflash_dfu_read(uint8_t* src, uint8_t* dest, uint32_t len)
{
if (extflash_read((intptr_t)src, (void*)dest, len) < 0) {
return nullptr;
}
return dest;
}
static uint16_t extflash_dfu_get_status(uint32_t address, uint8_t cmd, uint8_t *buffer)
{
switch (cmd) {
case DFU_MEDIA_PROGRAM:
buffer[1] = 1;
buffer[2] = 0;
buffer[3] = 0;
break;
case DFU_MEDIA_ERASE:
default:
buffer[1] = 20;
buffer[2] = 0;
buffer[3] = 0;
break;
}
return USBD_OK;
}
const USBD_DFU_MediaTypeDef _extflash_dfu_media = {
.pStrDesc = (uint8_t*)MEDIA_DESC_STR,
.Init = extflash_dfu_init,
.DeInit = extflash_dfu_deinit,
.Erase = extflash_dfu_erase,
.Write = extflash_dfu_write,
.Read = extflash_dfu_read,
.GetStatus = extflash_dfu_get_status,
};
const void* extflash_dfu_media = &_extflash_dfu_media;
#endif // BOOT
@@ -0,0 +1,27 @@
/*
* Copyright (C) EdgeTX
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#pragma once
#include "hal/flash_driver.h"
extern const etx_flash_driver_t extflash_driver;
extern const void* extflash_dfu_media;
// init external FLASH hardware
int32_t ExtFLASH_Init();
// init only runtime structures
void ExtFLASH_InitRuntime();
@@ -0,0 +1,60 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "hal/gpio.h"
#include "stm32_gpio.h"
#include "stm32_timer.h"
#include "stm32_pulse_driver.h"
#include "board.h"
#define HAPTIC_TIMER_FREQ 200000000
static const stm32_pulse_timer_t _haptic_timer = {
.GPIO = (gpio_t)HAPTIC_GPIO,
.GPIO_Alternate = HAPTIC_GPIO_AF,
.TIMx = HAPTIC_GPIO_TIMER,
.TIM_Freq = HAPTIC_TIMER_FREQ,
.TIM_Channel = LL_TIM_CHANNEL_CH2,
.TIM_IRQn = (IRQn_Type)-1,
.DMAx = nullptr,
.DMA_Stream = 0,
.DMA_Channel = 0,
.DMA_IRQn = (IRQn_Type)-1,
.DMA_TC_CallbackPtr = nullptr,
};
void hapticInit(void)
{
stm32_pulse_init(&_haptic_timer, 10000);
stm32_pulse_config_output(&_haptic_timer, true, LL_TIM_OCMODE_PWM1, 0);
LL_TIM_SetAutoReload(_haptic_timer.TIMx, 100);
LL_TIM_EnableCounter(_haptic_timer.TIMx);
}
void hapticOn(uint32_t pwmPercent)
{
if (pwmPercent > 100) pwmPercent = 100;
stm32_pulse_set_cmp_val(&_haptic_timer, pwmPercent);
}
void hapticOff(void) { hapticOn(0); }
void hapticDone(void) { hapticOff(); }
@@ -0,0 +1,33 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "hal/key_driver.h"
#include "stm32_hal_ll.h"
#include "stm32_gpio_driver.h"
#include "stm32_i2c_driver.h"
#include "hal.h"
#include "delays_driver.h"
#include "keys.h"
#include "stm32_keys.inc"
#include "debug.h"
@@ -0,0 +1,371 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "stm32_hal_ll.h"
#include "stm32_hal.h"
#include "edgetx_types.h"
#include "dma2d.h"
#include "hal.h"
#include "delays_driver.h"
#include "debug.h"
#include "lcd.h"
#include "lcd_driver.h"
#include "board.h"
#include <lvgl/lvgl.h>
#include "hal/gpio.h"
#include "stm32_gpio.h"
#include "stm32_qspi.h"
uint8_t TouchControllerType = 0; // 0: other; 1: CST836U
static volatile uint16_t lcd_phys_w = LCD_PHYS_W;
static volatile uint16_t lcd_phys_h = LCD_PHYS_H;
static LTDC_HandleTypeDef hltdc;
static void* initialFrameBuffer = nullptr;
static volatile uint8_t _frame_addr_reloaded = 0;
#if defined(LCD_VERTICAL_INVERT)
// The H17 panel is mounted upside-down, exactly like the tx16s/horus panel.
// LTDC cannot rotate, so we software-rotate every refreshed area 180 into a
// dedicated back buffer and swap it in on the last flush of a frame. Mirrors
// radio/src/targets/horus/lcd_driver.cpp.
typedef uint16_t pixel_t;
static pixel_t _LCD_BUF_1[DISPLAY_BUFFER_SIZE] __SDRAM;
static pixel_t _LCD_BUF_2[DISPLAY_BUFFER_SIZE] __SDRAM;
static pixel_t _line_buffer[LCD_W];
static uint16_t* _front_buffer = _LCD_BUF_1;
static uint16_t* _back_buffer = _LCD_BUF_2;
// Copy 2 pixels at once to speed up a little
static void _copy_rotate_180(uint16_t* dst, uint16_t* src, const rect_t& copy_area)
{
coord_t x1 = LCD_W - copy_area.w - copy_area.x;
coord_t y1 = LCD_H - copy_area.h - copy_area.y;
src += copy_area.w - 2;
dst += (y1 + copy_area.h - 1) * LCD_W + x1;
for (auto line = 0; line < copy_area.h; line++) {
// invert line into _line_buffer first (SRAM)
auto px_dst = _line_buffer;
auto line_end = px_dst + (copy_area.w & ~1);
while (px_dst != line_end) {
uint32_t* px2_src = (uint32_t*)src;
*((uint32_t*)px_dst) = ((*px2_src & 0xFFFF0000) >> 16) | ((*px2_src & 0xFFFF) << 16);
src -= 2;
px_dst += 2;
}
if (copy_area.w & 1) {
*px_dst = *(src+1);
src--;
}
// ... and DMA back into SDRAM
DMACopyBitmap(dst, copy_area.w, 1, 0, 0,
_line_buffer, copy_area.w, 1, 0, 0,
copy_area.w, 1);
src += copy_area.w * 2;
dst -= LCD_W;
}
}
static void _rotate_area_180(lv_area_t& area)
{
lv_coord_t tmp_coord;
tmp_coord = area.y2;
area.y2 = LCD_H - area.y1 - 1;
area.y1 = LCD_H - tmp_coord - 1;
tmp_coord = area.x2;
area.x2 = LCD_W - area.x1 - 1;
area.x1 = LCD_W - tmp_coord - 1;
}
#endif
static void _update_frame_buffer_addr(uint16_t* buffer)
{
// given the data cache size, this is probably
// faster than cleaning by address
SCB_CleanDCache();
LTDC_Layer1->CFBAR = (uint32_t)buffer;
// reload shadow registers on vertical blank
_frame_addr_reloaded = 0;
LTDC->SRCR = LTDC_SRCR_VBR;
__HAL_LTDC_ENABLE_IT(&hltdc, LTDC_IT_LI);
// wait for reload
// TODO: replace through some smarter mechanism without busy wait
while(_frame_addr_reloaded == 0);
}
static void startLcdRefresh(lv_disp_drv_t *disp_drv, uint16_t *buffer,
const rect_t &copy_area)
{
#if defined(LCD_VERTICAL_INVERT)
_copy_rotate_180(_back_buffer, buffer, copy_area);
if (lv_disp_flush_is_last(disp_drv)) {
// swap back/front
if (_front_buffer == _LCD_BUF_1) {
_front_buffer = _LCD_BUF_2;
_back_buffer = _LCD_BUF_1;
} else {
_front_buffer = _LCD_BUF_1;
_back_buffer = _LCD_BUF_2;
}
// Trigger async refresh
_update_frame_buffer_addr(_front_buffer);
// Copy refreshed & rotated areas into new back buffer
uint16_t* src = _front_buffer;
uint16_t* dst = _back_buffer;
lv_disp_t* disp = _lv_refr_get_disp_refreshing();
for(int i = 0; i < disp->inv_p; i++) {
if(disp->inv_area_joined[i]) continue;
lv_area_t refr_area;
lv_area_copy(&refr_area, &disp->inv_areas[i]);
_rotate_area_180(refr_area);
auto area_w = refr_area.x2 - refr_area.x1 + 1;
auto area_h = refr_area.y2 - refr_area.y1 + 1;
DMACopyBitmap(dst, LCD_W, LCD_H, refr_area.x1, refr_area.y1,
src, LCD_W, LCD_H, refr_area.x1, refr_area.y1,
area_w, area_h);
}
}
#else
(void)disp_drv;
(void)copy_area;
_update_frame_buffer_addr(buffer);
#endif
// Since #7483 the shared LVGL flush path (gui/colorlcd/lcd.cpp) no longer
// signals completion after invoking the flush callback; the driver must call
// lcdFlushed() itself, otherwise LVGL's flush stays pending forever and the
// UI freezes on the first frame (e.g. stuck on the boot logo).
lcdFlushed();
}
// LTDC RGB666 data + sync lines (all on AF14). Layout matches the tx15/rm-h750
// panel wiring: PI12-14 (HSYNC/VSYNC/CLK), PJ1-6 (R2-R7), PJ9-11 + PK0-2
// (G2-G7), PJ14-15 + PK3-6 (B2-B7), PK7 (DE).
gpio_t _lcd_af_gpios[] = {
GPIO_PIN(GPIOI, 12), GPIO_PIN(GPIOI, 13), GPIO_PIN(GPIOI, 14),
GPIO_PIN(GPIOJ, 1), GPIO_PIN(GPIOJ, 2), GPIO_PIN(GPIOJ, 3),
GPIO_PIN(GPIOJ, 4), GPIO_PIN(GPIOJ, 5), GPIO_PIN(GPIOJ, 6),
GPIO_PIN(GPIOJ, 9), GPIO_PIN(GPIOJ, 10), GPIO_PIN(GPIOJ, 11),
GPIO_PIN(GPIOJ, 14), GPIO_PIN(GPIOJ, 15),
GPIO_PIN(GPIOK, 0), GPIO_PIN(GPIOK, 1), GPIO_PIN(GPIOK, 2),
GPIO_PIN(GPIOK, 3), GPIO_PIN(GPIOK, 4), GPIO_PIN(GPIOK, 5),
GPIO_PIN(GPIOK, 6), GPIO_PIN(GPIOK, 7),
};
static void LCD_AF_GPIOConfig(void)
{
for (unsigned i = 0; i < sizeof(_lcd_af_gpios) / sizeof(_lcd_af_gpios[0]); i++) {
gpio_init_af(_lcd_af_gpios[i], GPIO_AF14, GPIO_SPEED_FREQ_LOW);
}
}
static void lcdResetConfig(void)
{
LL_GPIO_InitTypeDef GPIO_InitStructure;
LL_GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.Pin = LCD_RESET_GPIO_PIN;
GPIO_InitStructure.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStructure.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(LCD_RESET_GPIO, &GPIO_InitStructure);
}
static void lcdReset() {
LCD_NRST_HIGH();
delay_ms(1);
LCD_NRST_LOW(); // RESET();
delay_ms(10);
LCD_NRST_HIGH();
delay_ms(100);
}
void LCD_Init_LTDC() {
hltdc.Instance = LTDC;
/* LTDC pixel clock is driven from PLL3R (see system_clock.c) */
/* LTDC Configuration *********************************************************/
/* Polarity configuration */
/* Initialize the horizontal synchronization polarity as active low */
hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
/* Initialize the vertical synchronization polarity as active low */
hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
/* Initialize the data enable polarity as active low */
hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
/* Initialize the pixel clock polarity as input pixel clock.
tx16s/horus drives this panel with IPC (non-inverted); jumper-h750 used
IIPC for a different panel. */
hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
/* Configure R,G,B component values for LCD background color */
hltdc.Init.Backcolor.Red = 0;
hltdc.Init.Backcolor.Green = 0;
hltdc.Init.Backcolor.Blue = 0;
/* Configure horizontal synchronization width */
hltdc.Init.HorizontalSync = HSW-1;
/* Configure vertical synchronization height */
hltdc.Init.VerticalSync = VSH-1;
/* Configure accumulated horizontal back porch */
hltdc.Init.AccumulatedHBP = HSW+HBP-1;
/* Configure accumulated vertical back porch */
hltdc.Init.AccumulatedVBP = VSH+VBP-1;
/* Configure accumulated active width */
hltdc.Init.AccumulatedActiveW = lcd_phys_w + HBP+HSW-1;
/* Configure accumulated active height */
hltdc.Init.AccumulatedActiveH = lcd_phys_h + VBP+VSH-1;
/* Configure total width */
hltdc.Init.TotalWidth = lcd_phys_w + HBP + HFP + HSW -1;
/* Configure total height */
hltdc.Init.TotalHeigh = lcd_phys_h + VBP + VFP+VSH-1;
HAL_LTDC_Init(&hltdc);
// Configure IRQ (line)
NVIC_SetPriority(LTDC_IRQn, LTDC_IRQ_PRIO);
NVIC_EnableIRQ(LTDC_IRQn);
// Trigger on last line
HAL_LTDC_ProgramLineEvent(&hltdc, lcd_phys_h);
__HAL_LTDC_ENABLE_IT(&hltdc, LTDC_IT_LI);
}
void LCD_LayerInit() {
auto& layer = hltdc.LayerCfg[0];
/* Windowing configuration */
layer.WindowX0 = 0;
layer.WindowX1 = lcd_phys_w;
layer.WindowY0 = 0;
layer.WindowY1 = lcd_phys_h;
/* Pixel Format configuration*/
layer.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
/* Alpha constant (255 totally opaque) */
layer.Alpha = 255;
/* Default Color configuration (configure A,R,G,B component values) */
layer.Backcolor.Blue = 0;
layer.Backcolor.Green = 0;
layer.Backcolor.Red = 0;
layer.Alpha0 = 0;
/* Configure blending factors */
layer.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
layer.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
layer.ImageWidth = lcd_phys_w;
layer.ImageHeight = lcd_phys_h;
/* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */
#if defined(LCD_VERTICAL_INVERT)
layer.FBStartAdress = (intptr_t)_LCD_BUF_1;
#else
layer.FBStartAdress = (intptr_t)initialFrameBuffer;
#endif
/* Initialize LTDC layer 1 */
HAL_LTDC_ConfigLayer(&hltdc, &hltdc.LayerCfg[0], 0);
/* dithering activation */
HAL_LTDC_EnableDither(&hltdc);
}
extern "C"
void lcdSetInitalFrameBuffer(void* fbAddress)
{
initialFrameBuffer = fbAddress;
}
const char* boardLcdType = "";
extern "C"
void lcdInit(void)
{
#if defined(LCD_VERTICAL_INVERT)
// Clear buffers first
memset(_LCD_BUF_1, 0, sizeof(_LCD_BUF_1));
memset(_LCD_BUF_2, 0, sizeof(_LCD_BUF_2));
#endif
/* Configure the LCD reset pin */
lcdResetConfig();
/* Reset the LCD --------------------------------------------------------*/
lcdReset();
/* Configure the LTDC RGB control pins */
LCD_AF_GPIOConfig();
TRACE("LCD INIT: parallel RGB (LTDC)");
boardLcdType = "RGB (LTDC)";
__HAL_RCC_LTDC_CLK_ENABLE();
LCD_Init_LTDC();
LCD_LayerInit();
// Enable LCD display
__HAL_LTDC_ENABLE(&hltdc);
lcdSetFlushCb(startLcdRefresh);
}
extern "C" void LTDC_IRQHandler(void)
{
__HAL_LTDC_CLEAR_FLAG(&hltdc, LTDC_FLAG_LI);
__HAL_LTDC_DISABLE_IT(&hltdc, LTDC_IT_LI);
_frame_addr_reloaded = 1;
}
@@ -0,0 +1,47 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#ifndef __LCD_DRIVER_H__
#define __LCD_DRIVER_H__
// H17 uses a parallel RGB (LTDC) panel with no MCU-side SPI configuration bus.
// Only the LTDC data/sync lines and the panel reset line (LCD_RESET_GPIO) are
// wired, so the SPI bit-bang init/read path used by the RadioMaster boards is
// intentionally absent here.
// Timings taken from the horus/tx16s driver, which drives this same panel.
// Values are in this board's raw-porch convention (lcd_driver.cpp subtracts 1
// and accumulates), and reproduce the exact LTDC register values horus programs:
// HSync=2 AccHBP=42 AccActiveW=522 TotalWidth=525
// VSync=10 AccVBP=12 AccActiveH=284 TotalHeigh=286
#define HBP ( 40 )
#define VBP ( 2 )
#define HSW ( 3 )
#define VSH ( 11 )
#define HFP ( 3 )
#define VFP ( 2 )
#define LCD_NRST_HIGH() LL_GPIO_SetOutputPin(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN)
#define LCD_NRST_LOW() LL_GPIO_ResetOutputPin(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN)
#endif
@@ -0,0 +1,216 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "hal/gpio.h"
#include "stm32_gpio.h"
#include "stm32_hal_ll.h"
#include "board.h"
#define SDRAM_MEMORY_WIDTH FMC_SDRAM_MEM_BUS_WIDTH_16
#define SDRAM_CAS_LATENCY FMC_SDRAM_CAS_LATENCY_3
#define SDCLOCK_PERIOD FMC_SDRAM_CLOCK_PERIOD_2
#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
#define SDRAM_READBURST FMC_SDRAM_RBURST_DISABLE
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_FULL_PAGE ((uint16_t)0x0007)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_1 ((uint16_t)0x0010)
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
#define GPIO_AF_FMC GPIO_AF12
extern "C" void SDRAM_GPIOConfig(void)
{
/*-- GPIOs Configuration -----------------------------------------------------*/
/*
*/
/* GPIOC configuration */
gpio_init_af(GPIO_PIN(GPIOC, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH); // FMC_SDNWE (RAM_WE)
/* GPIOD configuration */
gpio_init_af(GPIO_PIN(GPIOD, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOD, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOD, 8), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOD, 9), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOD, 10), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOD, 14), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOD, 15), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
/* GPIOE configuration */
gpio_init_af(GPIO_PIN(GPIOE, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 7), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 8), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 9), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 10), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 11), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 12), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 13), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 14), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOE, 15), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
/* GPIOF configuration */
gpio_init_af(GPIO_PIN(GPIOF, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 2), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 3), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 4), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 5), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 11), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 12), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 13), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 14), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOF, 15), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
/* GPIOG configuration */
gpio_init_af(GPIO_PIN(GPIOG, 0), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOG, 1), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOG, 4), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOG, 5), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOG, 8), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOG, 15), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
// NB: PH5 is USB_VBUS on this board, NOT FMC_SDNWE (write-enable is on PC0).
// Only PH6 (FMC_SDNE1 / RAM_CS) and PH7 (FMC_SDCKE1 / RAM_CKE) are FMC here.
gpio_init_af(GPIO_PIN(GPIOH, 6), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
gpio_init_af(GPIO_PIN(GPIOH, 7), GPIO_AF_FMC, GPIO_PIN_SPEED_VERY_HIGH);
}
extern "C" void SDRAM_InitSequence(void)
{
FMC_SDRAM_CommandTypeDef FMC_SDRAMCommandStructure;
uint32_t tmpr = 0;
/* Step 3 --------------------------------------------------------------------*/
/* Configure a clock configuration enable command */
FMC_SDRAMCommandStructure.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
FMC_SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
FMC_SDRAMCommandStructure.AutoRefreshNumber = 1;
FMC_SDRAMCommandStructure.ModeRegisterDefinition = 0;
/* Send the command */
FMC_SDRAM_SendCommand(FMC_Bank5_6_R, &FMC_SDRAMCommandStructure, 10);
/* Step 4 --------------------------------------------------------------------*/
/* Insert 1 ms delay */
delay_ms(100);
/* Step 5 --------------------------------------------------------------------*/
/* Configure a PALL (precharge all) command */
FMC_SDRAMCommandStructure.CommandMode = FMC_SDRAM_CMD_PALL;
FMC_SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
FMC_SDRAMCommandStructure.AutoRefreshNumber = 1;
FMC_SDRAMCommandStructure.ModeRegisterDefinition = 0;
/* Send the command */
FMC_SDRAM_SendCommand(FMC_Bank5_6_R, &FMC_SDRAMCommandStructure, 10);
/* Step 6 --------------------------------------------------------------------*/
/* Configure a Auto-Refresh command */
FMC_SDRAMCommandStructure.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
FMC_SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
FMC_SDRAMCommandStructure.AutoRefreshNumber = 8;
FMC_SDRAMCommandStructure.ModeRegisterDefinition = 0;
/* Send the command */
FMC_SDRAM_SendCommand(FMC_Bank5_6_R, &FMC_SDRAMCommandStructure, 10);
/* Step 7 --------------------------------------------------------------------*/
/* Program the external memory mode register */
tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2 |
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
SDRAM_MODEREG_CAS_LATENCY_3 |
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
/* Configure a load Mode register command*/
FMC_SDRAMCommandStructure.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
FMC_SDRAMCommandStructure.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
FMC_SDRAMCommandStructure.AutoRefreshNumber = 1;
FMC_SDRAMCommandStructure.ModeRegisterDefinition = tmpr;
/* Send the command */
FMC_SDRAM_SendCommand(FMC_Bank5_6_R, &FMC_SDRAMCommandStructure, 10);
/* Step 8 --------------------------------------------------------------------*/
/* Set the refresh rate counter */
/* (15.62 us x Freq) - 20 */
/* Set the device refresh counter */
FMC_SDRAM_SetAutoRefreshNumber(FMC_Bank5_6_R, 15);
FMC_SDRAM_ProgramRefreshRate(FMC_Bank5_6_R, 2479);
}
extern "C" void SDRAM_Init(void)
{
//delay funcion needed
delaysInit();
/* GPIO configuration for FMC SDRAM bank */
SDRAM_GPIOConfig();
/* Enable FMC clock */
LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_FMC);
/* FMC Configuration ---------------------------------------------------------*/
FMC_SDRAM_InitTypeDef FMC_SDRAMInitStructure;
FMC_SDRAM_TimingTypeDef FMC_SDRAMTimingInitStructure;
/* FMC SDRAM Bank configuration */
FMC_SDRAMTimingInitStructure.LoadToActiveDelay = 3; // tMRD
FMC_SDRAMTimingInitStructure.ExitSelfRefreshDelay = 11; // tXSR = 70ns
FMC_SDRAMTimingInitStructure.SelfRefreshTime = 7; // tRAS = 42ns
FMC_SDRAMTimingInitStructure.RowCycleDelay = 10; // tRC = 60ns
FMC_SDRAMTimingInitStructure.WriteRecoveryTime = 2; // tWR
FMC_SDRAMTimingInitStructure.RPDelay = 3; // tRP = 18ns
FMC_SDRAMTimingInitStructure.RCDDelay = 3; // tRCD = 18ns
/* FMC SDRAM control configuration */
FMC_SDRAMInitStructure.SDBank = FMC_SDRAM_BANK2;
FMC_SDRAMInitStructure.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
FMC_SDRAMInitStructure.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
FMC_SDRAMInitStructure.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
FMC_SDRAMInitStructure.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
FMC_SDRAMInitStructure.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
FMC_SDRAMInitStructure.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
FMC_SDRAMInitStructure.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_3;
FMC_SDRAMInitStructure.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
FMC_SDRAMInitStructure.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* FMC SDRAM bank initialization */
FMC_SDRAM_Init(FMC_Bank5_6_R, &FMC_SDRAMInitStructure);
FMC_SDRAM_Timing_Init(FMC_Bank5_6_R, &FMC_SDRAMTimingInitStructure, FMC_SDRAM_BANK2);
__FMC_ENABLE();
/* FMC SDRAM device initialization sequence */
SDRAM_InitSequence();
}
@@ -0,0 +1,118 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "stm32h7xx_ll_pwr.h"
#include "stm32h7xx_ll_rcc.h"
#include "stm32h7xx_ll_system.h"
#include "stm32h7xx_ll_bus.h"
#define BOOTSTRAP __attribute__((section(".bootstrap")))
BOOTSTRAP void SystemClock_Config(void)
{
/* Power Configuration */
LL_PWR_ConfigSupply(LL_PWR_LDO_SUPPLY);
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
while (LL_PWR_IsActiveFlag_VOS() == 0){
}
/* Enable HSE oscillator */
LL_RCC_HSE_Enable();
while (LL_RCC_HSE_IsReady() != 1) {
}
/* Set FLASH latency */
LL_FLASH_SetLatency(LL_FLASH_LATENCY_5);
/* Main PLL configuration and activation */
LL_RCC_PLL_SetSource(LL_RCC_PLLSOURCE_HSE);
LL_RCC_PLL1P_Enable();
LL_RCC_PLL1Q_Enable();
LL_RCC_PLL1R_Enable();
LL_RCC_PLL1FRACN_Disable();
LL_RCC_PLL1_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4);
LL_RCC_PLL1_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE);
// HSE = 48 MHz. M=12 -> 4 MHz PFD input (matches RANGE_2_4). VCO = 4*240 =
// 960 MHz. Outputs unchanged from the previous (out-of-range M=3/N=60) config:
// P=2 -> 480 MHz sysclk, Q=20 -> 48 MHz (USB/SPI), R=2 -> 480 MHz.
LL_RCC_PLL1_SetM(12);
LL_RCC_PLL1_SetN(240);
LL_RCC_PLL1_SetP(2);
LL_RCC_PLL1_SetQ(20);
LL_RCC_PLL1_SetR(2);
LL_RCC_PLL1_Enable();
while (LL_RCC_PLL1_IsReady() != 1) {
}
/* Set Sys & AHB & APB1 & APB2 & APB4 prescaler */
LL_RCC_SetSysPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_2);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2);
LL_RCC_SetAPB4Prescaler(LL_RCC_APB4_DIV_2);
/* Set PLL1 as System Clock Source */
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL1);
while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL1) {
}
/* Config & activate PLL2 */
LL_RCC_PLL2P_Enable();
LL_RCC_PLL2FRACN_Disable();
LL_RCC_PLL2_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4);
LL_RCC_PLL2_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE);
LL_RCC_PLL2_SetM(24);
LL_RCC_PLL2_SetN(96);
LL_RCC_PLL2_SetP(17); // 11.294 MHz
LL_RCC_PLL2_Enable();
while (LL_RCC_PLL2_IsReady() != 1) {
}
/* Config & activate PLL3 */
LL_RCC_PLL3P_Enable();
LL_RCC_PLL3Q_Enable();
LL_RCC_PLL3R_Enable();
LL_RCC_PLL3FRACN_Disable();
LL_RCC_PLL3_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4);
LL_RCC_PLL3_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE);
// HSE = 48 MHz. Previous M=1 gave a 48 MHz PFD input (3x over the 16 MHz PLL
// input max) so PLL3 could not lock -> no LTDC pixel clock. M=12 -> 4 MHz
// input (matches RANGE_2_4). VCO = 4*48 = 192 MHz, identical to before:
// P=2 -> 96 MHz, Q=2 -> 96 MHz, R=18 -> ~10.67 MHz LTDC pixel clock.
LL_RCC_PLL3_SetM(12);
LL_RCC_PLL3_SetN(48);
LL_RCC_PLL3_SetP(2);
LL_RCC_PLL3_SetQ(2);
LL_RCC_PLL3_SetR(18);
LL_RCC_PLL3_Enable();
while (LL_RCC_PLL3_IsReady() != 1) {
}
/* Enable SRAM1, SRAM2 & SRAM3 */
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_D2SRAM1 |
LL_AHB2_GRP1_PERIPH_D2SRAM2 |
LL_AHB2_GRP1_PERIPH_D2SRAM3);
/* Set periph clock sources */
LL_RCC_SetSPIClockSource(LL_RCC_SPI123_CLKSOURCE_PLL1Q);
LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLL1Q);
LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_CLKP);
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
void SystemClock_Config();
@@ -0,0 +1,31 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#pragma once
#include "touch.h"
extern void touchPanelDeInit();
extern bool touchPanelInit();
struct TouchState touchPanelRead();
bool touchPanelEventOccured();
struct TouchState getInternalTouchState();
@@ -0,0 +1,339 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "hal/gpio.h"
#include "hal/i2c_driver.h"
#include "stm32_gpio.h"
#include "stm32_hal_ll.h"
#include "stm32_hal.h"
#include "stm32_i2c_driver.h"
#include "stm32_gpio_driver.h"
#include "stm32_exti_driver.h"
#include "hal.h"
#include "timers_driver.h"
#include "tp_gt911.h"
#include "delays_driver.h"
#include "os/sleep.h"
#include "edgetx_types.h"
#include "debug.h"
#include <stdlib.h>
#include <string.h>
#define TP_GT911_ID "911"
bool touchGT911Flag = false;
volatile static bool touchEventOccured = false;
struct TouchData touchData;
uint16_t touchGT911fwver = 0;
uint32_t touchGT911hiccups = 0;
static tmr10ms_t downTime = 0;
static tmr10ms_t tapTime = 0;
static short tapCount = 0;
static TouchState internalTouchState = {};
static void _gt911_exti_isr(void)
{
touchEventOccured = true;
}
void I2C_Init_Radio(void)
{
TRACE("GT911 I2C Init");
if (i2c_init(TOUCH_I2C_BUS) < 0) {
TRACE("GT911 ERROR: i2c_init failed");
return;
}
}
bool I2C_GT911_WriteRegister(uint16_t reg, uint8_t *buf, uint8_t len)
{
uint8_t uAddrAndBuf[258];
uAddrAndBuf[0] = (uint8_t)((reg & 0xFF00) >> 8);
uAddrAndBuf[1] = (uint8_t)(reg & 0x00FF);
if (len > 0) {
for (int i = 0; i < len; i++) {
uAddrAndBuf[i + 2] = buf[i];
}
}
if (stm32_i2c_master_tx(TOUCH_I2C_BUS, GT911_I2C_ADDR, uAddrAndBuf, len + 2,
100) < 0) {
TRACE("I2C B1 ERROR: WriteRegister failed");
return false;
}
return true;
}
bool I2C_GT911_ReadRegister(uint16_t reg, uint8_t *buf, uint8_t len)
{
uint8_t uRegAddr[2];
uRegAddr[0] = (uint8_t)((reg & 0xFF00) >> 8);
uRegAddr[1] = (uint8_t)(reg & 0x00FF);
if (stm32_i2c_master_tx(TOUCH_I2C_BUS, GT911_I2C_ADDR, uRegAddr, 2, 10) < 0) {
TRACE("I2C B1 ERROR: ReadRegister write reg address failed");
return false;
}
if (stm32_i2c_master_rx(TOUCH_I2C_BUS, GT911_I2C_ADDR, buf, len, 100) < 0) {
TRACE("I2C B1 ERROR: ReadRegister read reg address failed");
return false;
}
return true;
}
void touchPanelDeInit(void)
{
gpio_int_disable(TOUCH_INT_GPIO);
touchGT911Flag = false;
}
uint8_t tp_gt911_cfgVer = GT911_CFG_NUMBER;
bool touchPanelInit(void)
{
uint8_t tmp[4] = {0};
if (touchGT911Flag) {
gpio_init_int(TOUCH_INT_GPIO, GPIO_IN_PU, GPIO_RISING, _gt911_exti_isr);
return true;
} else {
TRACE("Touchpanel init start ...");
gpio_init(TOUCH_RST_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW);
gpio_init(TOUCH_INT_GPIO, GPIO_OUT, GPIO_PIN_SPEED_LOW);
I2C_Init_Radio();
gpio_clear(TOUCH_RST_GPIO);
gpio_set(TOUCH_INT_GPIO);
delay_us(200);
gpio_set(TOUCH_RST_GPIO);
delay_ms(6);
gpio_clear(TOUCH_INT_GPIO);
delay_ms(55);
gpio_init(TOUCH_INT_GPIO, GPIO_IN_PU, GPIO_PIN_SPEED_LOW);
delay_ms(50);
TRACE("Reading Touch registry");
if (!I2C_GT911_ReadRegister(GT911_PRODUCT_ID_REG, tmp, 4)) {
TRACE("GT911 ERROR: Product ID read failed");
}
if (strcmp((char *)tmp, TP_GT911_ID) == 0) {
TRACE("GT911 chip detected");
tmp[0] = 0X02;
if (!I2C_GT911_WriteRegister(GT911_COMMAND_REG, tmp, 1)) {
TRACE("GT911 ERROR: write to control register failed");
}
if (!I2C_GT911_ReadRegister(GT911_CONFIG_REG, tmp, 1)) {
TRACE("GT911 ERROR: configuration register read failed");
}
TRACE("Chip config Ver:%x", tmp[0]);
if ((tp_gt911_cfgVer == 0) || (tmp[0] < tp_gt911_cfgVer)) { // Config ver
TRACE("Sending new config %d", GT911_CFG_NUMBER);
if (!I2C_GT911_ReadRegister(GT911_CONFIG_REG, tmp, 1)) {
TRACE("GT911 ERROR: configuration register read failed");
}
tp_gt911_cfgVer = tmp[0];
}
if (!I2C_GT911_ReadRegister(GT911_FIRMWARE_VERSION_REG, tmp, 2)) {
TRACE("GT911 ERROR: reading firmware version failed");
} else {
touchGT911fwver = (tmp[1] << 8) + tmp[0];
TRACE("GT911 FW version: %u", touchGT911fwver);
}
delay_ms(10);
tmp[0] = 0X00;
if (!I2C_GT911_WriteRegister(GT911_COMMAND_REG, tmp, 1)) { // end reset
TRACE("GT911 ERROR: write to command register failed");
}
touchGT911Flag = true;
gpio_init_int(TOUCH_INT_GPIO, GPIO_IN_PU, GPIO_RISING, _gt911_exti_isr);
return true;
}
TRACE("GT911 chip NOT FOUND");
return false;
}
}
bool I2C_ReInit(void)
{
TRACE("I2C B1 ReInit");
touchPanelDeInit();
if (stm32_i2c_deinit(TOUCH_I2C_BUS) < 0)
TRACE("I2C B1 ReInit - I2C DeInit failed");
// If DeInit fails, try to re-init anyway
if (!touchPanelInit()) {
TRACE("I2C B1 ReInit - touchPanelInit failed");
return false;
}
return true;
}
#if defined(SIMU) || defined(SEMIHOSTING) || defined(DEBUG)
static const char *event2str(uint8_t ev)
{
switch (ev) {
case TE_NONE:
return "NONE";
case TE_UP:
return "UP";
case TE_DOWN:
return "DOWN";
case TE_SLIDE_END:
return "SLIDE_END";
case TE_SLIDE:
return "SLIDE";
default:
return "UNKNOWN";
}
}
#endif
struct TouchState touchPanelRead()
{
uint8_t state = 0;
if (!touchEventOccured) return internalTouchState;
touchEventOccured = false;
uint32_t startReadStatus = timersGetMsTick();
do {
if (!I2C_GT911_ReadRegister(GT911_READ_XY_REG, &state, 1)) {
touchGT911hiccups++;
TRACE("GT911 I2C read XY error");
if (!I2C_ReInit()) TRACE("I2C B1 ReInit failed");
return internalTouchState;
}
if (state & 0x80u) {
// ready
break;
}
sleep_ms(1);
} while (timersGetMsTick() - startReadStatus < GT911_TIMEOUT);
internalTouchState.deltaX = 0;
internalTouchState.deltaY = 0;
TRACE("touch state = 0x%x", state);
if (state & 0x80u) {
uint8_t pointsCount = (state & 0x0Fu);
uint32_t now = timersGetMsTick();
internalTouchState.tapCount = 0;
if (pointsCount > 0 && pointsCount <= GT911_MAX_TP) {
if (!I2C_GT911_ReadRegister(GT911_READ_XY_REG + 1, touchData.data,
pointsCount * sizeof(TouchPoint))) {
touchGT911hiccups++;
TRACE("GT911 I2C data read error");
if (!I2C_ReInit()) TRACE("I2C B1 ReInit failed");
return internalTouchState;
}
#if defined(LCD_VERTICAL_INVERT)
// Panel (and thus the GT911) is mounted upside-down, matching the 180
// display rotation in lcd_driver.cpp. Flip the reported coordinates so
// touches line up with what is drawn.
for (uint8_t i = 0; i < pointsCount; i++) {
touchData.points[i].x = LCD_W - 1 - touchData.points[i].x;
touchData.points[i].y = LCD_H - 1 - touchData.points[i].y;
}
#endif
if (internalTouchState.event == TE_NONE ||
internalTouchState.event == TE_UP ||
internalTouchState.event == TE_SLIDE_END) {
internalTouchState.event = TE_DOWN;
internalTouchState.startX = internalTouchState.x =
touchData.points[0].x;
internalTouchState.startY = internalTouchState.y =
touchData.points[0].y;
downTime = now;
} else {
internalTouchState.deltaX =
touchData.points[0].x - internalTouchState.x;
internalTouchState.deltaY =
touchData.points[0].y - internalTouchState.y;
if (internalTouchState.event == TE_SLIDE ||
abs(internalTouchState.deltaX) >= SLIDE_RANGE ||
abs(internalTouchState.deltaY) >= SLIDE_RANGE) {
internalTouchState.event = TE_SLIDE;
internalTouchState.x = touchData.points[0].x;
internalTouchState.y = touchData.points[0].y;
}
}
} else {
if (internalTouchState.event == TE_SLIDE) {
internalTouchState.event = TE_SLIDE_END;
} else if (internalTouchState.event == TE_DOWN) {
internalTouchState.event = TE_UP;
if (now - downTime <= GT911_TAP_TIME) {
if (now - tapTime > GT911_TAP_TIME)
tapCount = 1;
else
tapCount++;
internalTouchState.tapCount = tapCount;
tapTime = now;
}
} else {
internalTouchState.event = TE_NONE;
}
}
}
uint8_t zero = 0;
if (!I2C_GT911_WriteRegister(GT911_READ_XY_REG, &zero, 1)) {
TRACE("GT911 ERROR: clearing XY register failed");
}
TRACE("touch event = %s", event2str(internalTouchState.event));
return internalTouchState;
}
bool touchPanelEventOccured()
{
return touchEventOccured;
}
TouchState getInternalTouchState()
{
return internalTouchState;
}
@@ -0,0 +1,83 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#pragma once
#include "touch.h"
#include "definitions.h"
#define HAS_TOUCH_PANEL() (touchGT911Flag == true)
extern bool touchGT911Flag;
extern uint16_t touchGT911fwver;
extern uint32_t touchGT911hiccups;
extern void touchPanelDeInit();
extern bool touchPanelInit();
struct TouchState touchPanelRead();
bool touchPanelEventOccured();
struct TouchState getInternalTouchState();
#define GT911_TIMEOUT 3 // 3ms
#define GT911_TAP_TIME 250 // 250 ms
#define GT911_MAX_TP 5
#define GT911_CFG_NUMBER 0x6D
//I2C
#define GT911_I2C_ADDR 0x14
//#define GT_CMD_WR 0x28
//#define GT_CMD_RD 0x29
//#define I2C_TIMEOUT_MAX 1000
#define GT911_COMMAND_REG 0x8040
#define GT911_CONFIG_REG 0x8047
#define GT911_COORDINATE_REG 0x804D
#define GT911_CONFIG_CHECKSUM_REG 0x80FF
#define GT911_PRODUCT_ID_REG 0x8140
#define GT911_FIRMWARE_VERSION_REG 0x8144
#define GT911_VENDOR_ID_REG 0x814A
#define GT911_READ_XY_REG 0x814E
#define GT911_TP1_REG 0x8150
#define GT911_TP2_REG 0x8158
#define GT911_TP3_REG 0x8160
#define GT911_TP4_REG 0x8168
#define GT911_TP5_REG 0x8170
PACK(typedef struct {
uint8_t track;
uint16_t x;
uint16_t y;
uint16_t size;
uint8_t reserved;
}) TouchPoint;
PACK(struct TouchData {
union
{
TouchPoint points[GT911_MAX_TP];
uint8_t data[GT911_MAX_TP * sizeof(TouchPoint)];
};
});
+3 -1
View File
@@ -82,6 +82,8 @@ static inline void check_struct()
CHKSIZE(RadioData, 1183);
#elif defined(RADIO_V12)
CHKSIZE(RadioData, 1180);
#elif defined(RADIO_H17)
CHKSIZE(RadioData, 1060);
#elif defined(COLORLCD)
#if defined(IMU)
CHKSIZE(RadioData, 1063);
@@ -116,7 +118,7 @@ static inline void check_struct()
CHKSIZE(ModelData, 7641);
#elif defined(RADIO_V12)
CHKSIZE(ModelData, 7640);
#elif defined(PCBC14)
#elif defined(PCBC14) || defined(RADIO_H17)
CHKSIZE(ModelData, 7573);
#elif defined(PCBPA01)
CHKSIZE(ModelData, 7582);
+1
View File
@@ -438,6 +438,7 @@ void generalDefault()
uint8_t defaultCalib[] = DEFAULT_6POS_CALIB;
StepsCalibData* calib = (StepsCalibData*)&g_eeGeneral.calib[DEFAULT_6POS_IDX];
calib->count = XPOTS_MULTIPOS_COUNT - 1;
for (int i = 0; i < 5; i++) {
calib->steps[i] = defaultCalib[i];
}
@@ -156,6 +156,79 @@ rect_t ViewMainDecoration::getWidgetsZone(bool showTopBar) const
return rect_t{x, y, w, h};
}
#if defined(MAIN_VIEW_FLEX_SLOTS)
// Explicit slot -> flex input map, for targets whose flex order differs
enum {
SLOT_BOTTOM_LEFT = 0,
SLOT_BOTTOM_CENTER,
SLOT_BOTTOM_RIGHT,
SLOT_LEFT,
SLOT_RIGHT,
SLOT_LEFT_2,
SLOT_RIGHT_2,
SLOT_COUNT
};
static const int8_t _flex_slots[SLOT_COUNT] = MAIN_VIEW_FLEX_SLOTS;
static int slotPot(uint8_t slot)
{
int pot = _flex_slots[slot];
if (pot < 0 || pot >= adcGetMaxInputs(ADC_INPUT_FLEX)) return -1;
return IS_POT_AVAILABLE(pot) ? pot : -1;
}
void ViewMainDecoration::createSliders(Window* ml, Window* mr, Window* bl, Window* bc, Window* br)
{
int pot;
if ((pot = slotPot(SLOT_BOTTOM_LEFT)) >= 0)
sliders[pot] = new MainViewHorizontalSlider(bl, pot);
if ((pot = slotPot(SLOT_BOTTOM_CENTER)) >= 0) {
if (IS_POT_MULTIPOS(pot)) {
has6POS = true;
sliders[pot] = new MainView6POS(bc, pot);
} else {
sliders[pot] = new MainViewHorizontalSlider(bc, pot);
}
}
if ((pot = slotPot(SLOT_BOTTOM_RIGHT)) >= 0)
sliders[pot] = new MainViewHorizontalSlider(br, pot);
int left = slotPot(SLOT_LEFT);
int right = slotPot(SLOT_RIGHT);
int left2 = slotPot(SLOT_LEFT_2);
int right2 = slotPot(SLOT_RIGHT_2);
if (left >= 0 || right >= 0 || left2 >= 0 || right2 >= 0) {
hasVerticalSliders = true;
auto leftPots = layoutBox(ml, LV_ALIGN_LEFT_MID, LV_FLEX_FLOW_COLUMN);
leftPots->setHeight(MainViewSlider::VERTICAL_SLIDERS_HEIGHT);
auto rightPots = layoutBox(mr, LV_ALIGN_RIGHT_MID, LV_FLEX_FLOW_COLUMN);
rightPots->setHeight(MainViewSlider::VERTICAL_SLIDERS_HEIGHT);
coord_t lsh = (left2 >= 0) ? MainViewSlider::VERTICAL_SLIDERS_HEIGHT / 2 : MainViewSlider::VERTICAL_SLIDERS_HEIGHT;
coord_t rsh = (right2 >= 0) ? MainViewSlider::VERTICAL_SLIDERS_HEIGHT / 2 : MainViewSlider::VERTICAL_SLIDERS_HEIGHT;
for (auto p : {left, left2}) {
if (p >= 0)
sliders[p] = new MainViewVerticalSlider(leftPots, rect_t{0, 0, MainViewSlider::SLIDER_BAR_SIZE, lsh}, p);
}
for (auto p : {right, right2}) {
if (p >= 0)
sliders[p] = new MainViewVerticalSlider(rightPots, rect_t{0, 0, MainViewSlider::SLIDER_BAR_SIZE, rsh}, p);
}
}
}
#else
void ViewMainDecoration::createSliders(Window* ml, Window* mr, Window* bl, Window* bc, Window* br)
{
int pot = 0;
@@ -238,6 +311,8 @@ void ViewMainDecoration::createSliders(Window* ml, Window* mr, Window* bl, Windo
}
}
#endif // MAIN_VIEW_FLEX_SLOTS
void ViewMainDecoration::createTrims(Window* ml, Window* mr, Window* bl, Window* br)
{
// Trim order TRIM_LH, TRIM_LV, TRIM_RV, TRIM_RH
+2
View File
@@ -50,6 +50,8 @@ elseif(PCB STREQUAL TX15)
set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_gx15.cpp)
elseif(PCB STREQUAL TX16SMK3)
set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_tx16smk3.cpp)
elseif(PCB STREQUAL H17)
set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_h17.cpp)
elseif(PCB STREQUAL X7)
if(PCBREV STREQUAL TPRO)
set(YAML_GEN_OUTPUT storage/yaml/yaml_datastructs_tpro.cpp)
@@ -46,6 +46,8 @@
#include "yaml_datastructs_tx16smk3.cpp"
#elif defined(PCBV12)
#include "yaml_datastructs_v12.cpp"
#elif defined(PCBH17)
#include "yaml_datastructs_h17.cpp"
#elif defined(RADIO_T22)
#include "yaml_datastructs_t22.cpp"
#elif defined(PCBPL18)
File diff suppressed because it is too large Load Diff
@@ -89,6 +89,29 @@ void audioUnmute()
}
#endif
// DAC output channel; a board on DAC1_OUT2 (PA5) sets AUDIO_DAC_CH 2 in hal.h
#ifndef AUDIO_DAC_CH
#define AUDIO_DAC_CH 1
#endif
#define _AUDIO_DAC_CAT(a, b) a##b
#define _AUDIO_DAC_XCAT(a, b) _AUDIO_DAC_CAT(a, b)
#define AUDIO_DAC_CHANNEL _AUDIO_DAC_XCAT(LL_DAC_CHANNEL_, AUDIO_DAC_CH)
#define AUDIO_DAC_DHR12L _AUDIO_DAC_XCAT(DHR12L, AUDIO_DAC_CH)
#define AUDIO_DAC_CR_EN _AUDIO_DAC_XCAT(DAC_CR_EN, AUDIO_DAC_CH)
#define AUDIO_DAC_CR_TEN _AUDIO_DAC_XCAT(DAC_CR_TEN, AUDIO_DAC_CH)
#define AUDIO_DAC_CR_DMAEN _AUDIO_DAC_XCAT(DAC_CR_DMAEN, AUDIO_DAC_CH)
#define AUDIO_DAC_SR_DMAUDR _AUDIO_DAC_XCAT(DAC_SR_DMAUDR, AUDIO_DAC_CH)
#define AUDIO_DAC_CLEAR_DMAUDR() _AUDIO_DAC_XCAT(LL_DAC_ClearFlag_DMAUDR, AUDIO_DAC_CH)(AUDIO_DAC)
// Channel-2 control/trigger bits sit 16 bits above the channel-1 ones
#if AUDIO_DAC_CH == 2
#define AUDIO_DAC_TRIGGER (DAC_TRIGGER << 16)
#else
#define AUDIO_DAC_TRIGGER DAC_TRIGGER
#endif
#if defined(STM32H5) || defined(STM32H7) || defined(STM32H7RS)
#define DAC_TRIGGER LL_DAC_TRIG_EXT_TIM6_TRGO
@@ -165,7 +188,7 @@ static void dac_dma_init()
dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
dmaInit.PeriphOrM2MSrcAddress = LL_DAC_DMA_GetRegAddr(
DAC1, LL_DAC_CHANNEL_1, LL_DAC_DMA_REG_DATA_12BITS_LEFT_ALIGNED);
AUDIO_DAC, AUDIO_DAC_CHANNEL, LL_DAC_DMA_REG_DATA_12BITS_LEFT_ALIGNED);
dmaInit.MemoryOrM2MDstAddress = (uintptr_t)_dma_buffer;
dmaInit.NbData = DMA_BUFFER_LEN;
#elif defined(STM32H7RS)
@@ -215,10 +238,10 @@ static void dac_start_dma()
LL_DMA_EnableStream(AUDIO_DMA, AUDIO_DMA_Stream);
// clear underrun flag
LL_DAC_ClearFlag_DMAUDR1(AUDIO_DAC);
AUDIO_DAC_CLEAR_DMAUDR();
// enable DAC
AUDIO_DAC->CR |= DAC_CR_EN1 | DAC_CR_DMAEN1;
AUDIO_DAC->CR |= AUDIO_DAC_CR_EN | AUDIO_DAC_CR_DMAEN;
}
void audioConsumeCurrentBuffer()
@@ -407,14 +430,14 @@ static void dac_periph_init()
gpio_init_analog(AUDIO_OUTPUT_GPIO);
// set data registre to silence
AUDIO_DAC->DHR12L1 = AUDIO_DATA_SILENCE;
AUDIO_DAC->AUDIO_DAC_DHR12L = AUDIO_DATA_SILENCE;
// clear underrun flag
AUDIO_DAC->SR = DAC_SR_DMAUDR1;
AUDIO_DAC->SR = AUDIO_DAC_SR_DMAUDR;
// use TIM6 TRGO as trigger
// enable DAC & channel 1 trigger
AUDIO_DAC->CR = DAC_CR_TEN1 | DAC_CR_EN1 | DAC_TRIGGER;
// enable DAC & channel trigger
AUDIO_DAC->CR = AUDIO_DAC_CR_TEN | AUDIO_DAC_CR_EN | AUDIO_DAC_TRIGGER;
}
// Configure DAC0
@@ -274,7 +274,11 @@ static DSTATUS sdio_initialize(BYTE lun)
{
#if defined(SD2_PRESENT_GPIO)
/* Define active SD */
if (gpio_read(SD2_PRESENT_GPIO) == 0) {
// SD_SDIO2_ONLY: no internal SDMMC1 to fall back to, always use the reader
#if !defined(SD_SDIO2_ONLY)
if (gpio_read(SD2_PRESENT_GPIO) == 0)
#endif
{
currentSD.periph = SDMMC2;
currentSD.D0 = SD2_SDIO_PIN_D0;
currentSD.AF_D0 = SD2_SDIO_AF_D0;
@@ -391,7 +395,12 @@ static DSTATUS sdio_status(BYTE lun)
{
DSTATUS stat = RES_OK;
#if defined(SD2_PRESENT_GPIO)
#if defined(SD_SDIO2_ONLY)
// Single SDMMC2 reader: card-detect is just the present pin
if (gpio_read(SD2_PRESENT_GPIO)) {
stat |= STA_NODISK;
}
#elif defined(SD2_PRESENT_GPIO)
if (currentSD.periph == SDMMC2 && gpio_read(SD2_PRESENT_GPIO)) {
stat |= STA_NODISK;
}
+191
View File
@@ -0,0 +1,191 @@
option(UNEXPECTED_SHUTDOWN "Enable the Unexpected Shutdown screen" ON)
option(PXX1 "PXX1 protocol support" ON)
option(PXX2 "PXX2 protocol support" ON)
option(AFHDS3 "AFHDS3 TX Module" ON)
option(MULTIMODULE "DIY Multiprotocol TX Module (https://github.com/pascallanger/DIY-Multiprotocol-TX-Module)" ON)
option(GHOST "Ghost TX Module" ON)
option(MODULE_SIZE_STD "Standard size TX Module" ON)
option(LUA_MIXER "Enable LUA mixer/model scripts support" ON)
option(DISK_CACHE "Enable SD card disk cache" ON)
option(BLUETOOTH "FrSky BT module support" OFF)
option(INTERNAL_GPS "Support for internal GPS" ON)
set(FIRMWARE_QSPI YES)
set(FIRMWARE_FORMAT_UF2 YES)
set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)")
set(CPU_TYPE STM32H7)
set(HSE_VALUE 48000000)
set(SDCARD YES)
set(STORAGE_MODELSLIST YES)
set(HAPTIC YES)
set(GUI_DIR colorlcd)
set(BITMAPS_DIR 480x272)
set(TARGET_DIR h17)
set(RTC_BACKUP_RAM YES)
set(PPM_LIMITS_SYMETRICAL YES)
set(USB_SERIAL ON CACHE BOOL "Enable USB serial (CDC)")
set(HARDWARE_EXTERNAL_MODULE YES)
set(INTERNAL_MODULE_AFHDS3 NO)
set(ROTARY_ENCODER YES)
set(INTERNAL_GPS_BAUDRATE "9600" CACHE STRING "Baud rate for internal GPS")
#option(STICK_DEAD_ZONE "Enable sticks dead zone" YES)
if(BLUETOOTH)
set(AUX2_SERIAL OFF)
else()
set(AUX2_SERIAL ON)
endif()
# for size report script
set(CPU_TYPE_FULL STM32H750xB)
set(TARGET_LINKER_DIR stm32h750_sdram)
set(TARGET_EXTRAM_START 0xD0000000)
set(SIZE_TARGET_MEM_DEFINE "MEM_SIZE_SDRAM1=8192")
set(FIRMWARE_STRIP_BOOTLOADER YES)
add_definitions(-DPCBH17 -DPCBHORUS)
add_definitions(-DSTM32_SUPPORT_32BIT_TIMERS)
add_definitions(-DFIRMWARE_QSPI)
add_definitions(-DFIRMWARE_FORMAT_UF2)
add_definitions(-DUSE_CUSTOM_EXTI_IRQ)
set(FLAVOUR h17)
add_definitions(-DRADIO_H17)
add_definitions(-DMANUFACTURER_SENDUWING)
# defines existing internal modules
set(INTERNAL_MODULES CRSF CACHE STRING "Internal modules")
set(DEFAULT_INTERNAL_MODULE CROSSFIRE CACHE STRING "Default internal module")
set(BITMAPS_TARGET bm480_bitmaps)
set(FONTS_TARGET x12_fonts)
set(LCD_DRIVER lcd_driver.cpp)
set(HARDWARE_TOUCH YES)
set(RADIO_DEPENDENCIES ${RADIO_DEPENDENCIES} ${BITMAPS_TARGET})
set(FIRMWARE_DEPENDENCIES datacopy)
set(SOFTWARE_KEYBOARD ON)
set(FLYSKY_GIMBAL OFF)
set(AUX_SERIAL YES)
set(AUX2_SERIAL YES)
add_definitions(
-DSTM32H750XBTx -DSTM32H750xx
-DSDRAM -DCCMRAM
-DHARDWARE_TOUCH -DHARDWARE_KEYS -DSOFTWARE_KEYBOARD
)
set(SDRAM ON)
add_definitions(-DSOFTWARE_VOLUME)
add_definitions(-DRTCLOCK)
add_definitions(-DPWR_BUTTON_${PWR_BUTTON})
add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE})
if(STICK_DEAD_ZONE)
add_definitions(-DSTICK_DEAD_ZONE)
endif()
if(NOT UNEXPECTED_SHUTDOWN)
add_definitions(-DNO_UNEXPECTED_SHUTDOWN)
endif()
if(DISK_CACHE)
set(SRC ${SRC} disk_cache.cpp)
add_definitions(-DDISK_CACHE)
endif()
if(INTERNAL_GPS)
message("-- Internal GPS enabled")
add_definitions(-DINTERNAL_GPS)
add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE})
set(SRC ${SRC} gps.cpp gps_nmea.cpp gps_ubx.cpp)
endif()
set(AFHDS3 ON)
# VCP CLI
set(ENABLE_SERIAL_PASSTHROUGH ON CACHE BOOL "Enable serial passthrough")
set(CLI ON CACHE BOOL "Enable CLI")
set(BOARD senduwing-h750)
set(BOARD_SRC_DIR boards/${BOARD})
include_directories(boards/${BOARD})
set(TARGET_SRC_DIR targets/${TARGET_DIR})
set(BOARD_COMMON_SRC
${BOARD_SRC_DIR}/board.cpp
${BOARD_SRC_DIR}/key_driver.cpp
${BOARD_SRC_DIR}/lcd_driver.cpp
${BOARD_SRC_DIR}/backlight_driver.cpp
${BOARD_SRC_DIR}/haptic_driver.cpp
${BOARD_SRC_DIR}/extflash_driver.cpp
targets/common/arm/stm32/abnormal_reboot.cpp
targets/common/arm/stm32/delays_driver.cpp
targets/common/arm/stm32/dma2d.cpp
targets/common/arm/stm32/flash_driver.cpp
targets/common/arm/stm32/pwr_driver.cpp
targets/common/arm/stm32/rotary_encoder_driver.cpp
targets/common/arm/stm32/rtc_driver.cpp
targets/common/arm/stm32/watchdog_driver.cpp
)
# Bootloader board library
add_library(board_bl OBJECT EXCLUDE_FROM_ALL
${BOARD_COMMON_SRC}
${BOARD_SRC_DIR}/sdram_driver.cpp
${RADIO_SRC_DIR}/gui/colorlcd/boot_menu.cpp
targets/common/arm/stm32/stm32_qspi.cpp
targets/common/arm/stm32/stm32_pulse_driver.cpp
)
add_dependencies(board_bl minimal_board_lib)
set(BOOTLOADER_SRC ${BOOTLOADER_SRC} $<TARGET_OBJECTS:board_bl>)
set(CMSIS_SRC ${BOARD_SRC_DIR}/system_clock.c)
# Firmware board library
add_library(board OBJECT EXCLUDE_FROM_ALL
${BOARD_COMMON_SRC}
${BOARD_SRC_DIR}/tp_gt911.cpp
targets/common/arm/stm32/audio_dac_driver.cpp
targets/common/arm/stm32/heartbeat_driver.cpp
targets/common/arm/stm32/mixer_scheduler_driver.cpp
targets/common/arm/stm32/module_timer_driver.cpp
targets/common/arm/stm32/stm32_pulse_driver.cpp
targets/common/arm/stm32/stm32_softserial_driver.cpp
targets/common/arm/stm32/stm32_qspi.cpp
targets/common/arm/stm32/stm32_switch_driver.cpp
targets/common/arm/stm32/trainer_driver.cpp
)
add_dependencies(board board_lib)
set(FIRMWARE_SRC ${FIRMWARE_SRC} $<TARGET_OBJECTS:board>)
if(BLUETOOTH)
target_sources(board PRIVATE targets/common/arm/stm32/bluetooth_driver.cpp)
endif()
include_directories(${RADIO_SRC_DIR}/fonts/colorlcd gui/${GUI_DIR} gui/${GUI_DIR}/layouts)
if(BOOTLOADER)
set(FIRMWARE_TARGET_SRC
${FIRMWARE_TARGET_SRC}
../../bootloader/loadboot.cpp
)
endif()
set(SRC
${SRC}
io/frsky_firmware_update.cpp
io/multi_firmware_update.cpp
)
if (MULTIMODULE)
add_definitions(-DMULTI_PROTOLIST)
set(SRC ${SRC}
io/multi_protolist.cpp
)
endif()
# Make malloc() thread-safe
add_definitions(-DTHREADSAFE_MALLOC)
+303
View File
@@ -0,0 +1,303 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
/*
Senduwing H17 - STM32H750XBH6
Hardware notes (vs RadioMaster TX15, on which the display/LTDC wiring is based):
- No I2C IO expander: switches, keys and trims are wired directly to GPIO
(see boards/hw_defs/h17.json which is the source of truth for those).
- Parallel RGB (LTDC) panel, no MCU-side SPI configuration bus.
- Analog audio through DAC1 + external amplifier with hardware mute.
DMA1
Stream0: AUDIO_DMA_Stream (DAC)
Stream1: INTMODULE_DMA_STREAM
Stream2:
Stream3: TELEMETRY_DMA_Stream_RX
Stream4:
Stream5: INTMODULE_RX_DMA_STREAM
Stream6: AUX2_SERIAL_DMA_RX_STREAM
Stream7: TELEMETRY_DMA_Stream_TX
DMA2
Stream0: ADC_EXT_DMA_STREAM (do not move)
Stream1: AUX_SERIAL_DMA_TX_STREAM
Stream2: AUX_SERIAL_DMA_RX_STREAM
Stream3: EXTMODULE_TIMER_DMA_STREAM
Stream4: ADC_DMA_STREAM (do not move)
Stream5: EXTMODULE_USART_RX_DMA_STREAM
Stream6: EXTMODULE_USART_TX_DMA_STREAM
Stream7: AUX2_SERIAL_DMA_TX_STREAM
TIM1: BACKLIGHT_TIMER
TIM3: HAPTIC_GPIO_TIMER
TIM5: EXTMODULE_TIMER
TIM6: AUDIO_TIMER
TIM8: TRAINER_TIMER
TIM12: MIXER_SCHEDULER_TIMER
TIM14: MS_TIMER
TIM17: ROTARY_ENCODER_TIMER
*/
#ifndef _HAL_H_
#define _HAL_H_
// Clocks, timers, LCD, backlight, haptic, status LEDs and rotenc: see h17.json
#include "hal_settings.h"
// ADC
#define ADC_VREF_PREC2 330
// P2 is an analog 6POS (FLEX_MULTIPOS), steps measured on H17 hardware.
// DEFAULT_6POS_IDX = calib index 8 (4 sticks + flex idx 4).
#define XPOS_CALIB_DEFAULT {0x5, 0xe, 0x17, 0x20, 0x29}
#define DEFAULT_6POS_CALIB {5, 14, 23, 32, 41}
#define DEFAULT_6POS_IDX 8
// Flex inputs are declared as S1,S2,S3,S4,6POS for the hardware pages; the
// main view slots { BL, BC, BR, left, right, left2, right2 } map back here.
#define MAIN_VIEW_FLEX_SLOTS { 0, 4, 3, 1, 2, 5, 6 }
// Power
#define PWR_SWITCH_GPIO GPIO_PIN(GPIOA, 4) // PWR_BUTTON
#define PWR_ON_GPIO GPIO_PIN(GPIOH, 12) // PWR_ON
// No USB charger on this board.
// S.Port update connector
#define HAS_SPORT_UPDATE_CONNECTOR() (false)
// Telemetry (internal module S.PORT / CRSF telemetry line on PA9)
#define TELEMETRY_SET_INPUT 0
#define TELEMETRY_TX_GPIO GPIO_PIN(GPIOA, 9)
#define TELEMETRY_RX_GPIO GPIO_UNDEF
#define TELEMETRY_USART USART1
#define TELEMETRY_USART_IRQn USART1_IRQn
#define TELEMETRY_DMA DMA1
#define TELEMETRY_DMA_Stream_TX LL_DMA_STREAM_7
#define TELEMETRY_DMA_Channel_TX LL_DMAMUX1_REQ_USART1_TX
#define TELEMETRY_DMA_TX_Stream_IRQ DMA1_Stream7_IRQn
#define TELEMETRY_DMA_TX_IRQHandler DMA1_Stream7_IRQHandler
#define TELEMETRY_DMA_Stream_RX LL_DMA_STREAM_3
#define TELEMETRY_DMA_Channel_RX LL_DMAMUX1_REQ_USART1_RX
#define TELEMETRY_USART_IRQHandler USART1_IRQHandler
// Software IRQ (Prio 5 -> FreeRTOS compatible)
#define TELEMETRY_USE_CUSTOM_EXTI
#define CUSTOM_EXTI_IRQ_NAME ETH_WKUP_IRQ
#define ETH_WKUP_IRQ_Priority 5
#define CUSTOM_EXTI_IRQ_LINE 86
#define TELEMETRY_RX_FRAME_EXTI_LINE CUSTOM_EXTI_IRQ_LINE
// USB
#define USB_GPIO GPIOA
#define USB_GPIO_VBUS GPIO_PIN(GPIOH, 5)
#define USB_GPIO_DM GPIO_PIN(GPIOA, 11)
#define USB_GPIO_DP GPIO_PIN(GPIOA, 12)
#define USB_GPIO_AF GPIO_AF10
// LCD - parallel RGB (LTDC), no SPI configuration bus (see lcd_driver.cpp)
#define LCD_RESET_GPIO GPIOJ
#define LCD_RESET_GPIO_PIN LL_GPIO_PIN_12
// QSPI Flash
#define QSPI_CLK_GPIO GPIO_PIN(GPIOF, 10)
#define QSPI_CLK_GPIO_AF GPIO_AF9
#define QSPI_CS_GPIO GPIO_PIN(GPIOG, 6)
#define QSPI_CS_GPIO_AF GPIO_AF10
#define QSPI_MISO_GPIO GPIO_PIN(GPIOF, 9)
#define QSPI_MISO_GPIO_AF GPIO_AF10
#define QSPI_MOSI_GPIO GPIO_PIN(GPIOF, 8)
#define QSPI_MOSI_GPIO_AF GPIO_AF10
#define QSPI_WP_GPIO GPIO_PIN(GPIOF, 7)
#define QSPI_WP_GPIO_AF GPIO_AF9
#define QSPI_HOLD_GPIO GPIO_PIN(GPIOF, 6)
#define QSPI_HOLD_GPIO_AF GPIO_AF9
#define QSPI_FLASH_SIZE 0x1000000
// SD (SDIO) - only the external reader on SDMMC2, no internal SDMMC1 device
#define SD_SDIO2_ONLY
#define SD_SDIO SDMMC1
#define SD_SDIO_CLK_DIV(fq) (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC) / (2 * fq))
#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) // Hz
#define SD2_PRESENT_GPIO GPIO_PIN(GPIOH, 8) // PH.08
#define SD2_SDIO SDMMC2
#define SD2_SDIO_PIN_D0 GPIO_PIN(GPIOB, 14)
#define SD2_SDIO_AF_D0 GPIO_AF9
#define SD2_SDIO_PIN_D1 GPIO_PIN(GPIOB, 15)
#define SD2_SDIO_AF_D1 GPIO_AF9
#define SD2_SDIO_PIN_D2 GPIO_PIN(GPIOG, 11)
#define SD2_SDIO_AF_D2 GPIO_AF10
#define SD2_SDIO_PIN_D3 GPIO_PIN(GPIOB, 4)
#define SD2_SDIO_AF_D3 GPIO_AF9
#define SD2_SDIO_PIN_CLK GPIO_PIN(GPIOD, 6)
#define SD2_SDIO_AF_CLK GPIO_AF11
#define SD2_SDIO_PIN_CMD GPIO_PIN(GPIOD, 7)
#define SD2_SDIO_AF_CMD GPIO_AF11
#define SD2_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) // Hz
#define STORAGE_USE_SDIO
// AUDIO (analog, DAC1 channel 2 / PA5 + external amp with hardware mute)
#define AUDIO_MUTE_GPIO GPIO_PIN(GPIOH, 10)
#define AUDIO_MUTE_DELAY 200 // ms
#define AUDIO_UNMUTE_DELAY 100 // ms
#define AUDIO_OUTPUT_GPIO GPIO_PIN(GPIOA, 5) // DAC1_OUT2
#define AUDIO_DAC DAC1
#define AUDIO_DAC_CH 2
#define AUDIO_DMA DMA1
#define AUDIO_DMA_Stream LL_DMA_STREAM_0
#define AUDIO_DMA_Channel LL_DMAMUX1_REQ_DAC1_CH2
#define AUDIO_TIM_IRQn TIM6_DAC_IRQn
#define AUDIO_TIM_IRQHandler TIM6_DAC_IRQHandler
#define AUDIO_DMA_Stream_IRQn DMA1_Stream0_IRQn
#define AUDIO_DMA_Stream_IRQHandler DMA1_Stream0_IRQHandler
#define AUDIO_TIMER TIM6
// I2C Bus (touch)
#define I2C_B1 I2C4
#define I2C_B1_SDA_GPIO GPIO_PIN(GPIOD, 13) // PD.13
#define I2C_B1_SCL_GPIO GPIO_PIN(GPIOD, 12) // PD.12
#define I2C_B1_GPIO_AF LL_GPIO_AF_4
#define I2C_B1_CLK_RATE 400000
// Internal Module (CRSF)
#define INTMODULE_PWR_GPIO GPIO_PIN(GPIOB, 13) // PB.13
#define INTMODULE_BOOTCMD_GPIO GPIO_PIN(GPIOH, 9) // PH.09
#define INTMODULE_BOOTCMD_DEFAULT 0
#define INTMODULE_TX_GPIO GPIO_PIN(GPIOG, 14)
#define INTMODULE_RX_GPIO GPIO_PIN(GPIOG, 9)
#define INTMODULE_USART USART6
#define INTMODULE_GPIO_AF LL_GPIO_AF_7
#define INTMODULE_USART_IRQn USART6_IRQn
#define INTMODULE_USART_IRQHandler USART6_IRQHandler
#define INTMODULE_DMA DMA1
#define INTMODULE_DMA_STREAM LL_DMA_STREAM_1
#define INTMODULE_DMA_STREAM_IRQ DMA1_Stream1_IRQn
#define INTMODULE_DMA_FLAG_TC DMA_FLAG_TCIF1
#define INTMODULE_DMA_CHANNEL LL_DMAMUX1_REQ_USART6_TX
#define INTMODULE_RX_DMA DMA1
#define INTMODULE_RX_DMA_STREAM LL_DMA_STREAM_5
#define INTMODULE_RX_DMA_CHANNEL LL_DMAMUX1_REQ_USART6_RX
#define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream5_IRQn
#define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream5_IRQHandler
// External Module
#define EXTMODULE
#define EXTMODULE_PULSES
#define EXTMODULE_PWR_GPIO GPIO_PIN(GPIOD, 4) // PD.04
#define EXTMODULE_TX_GPIO GPIO_PIN(GPIOA, 2) // TIM5_CH3
#define EXTMODULE_RX_GPIO GPIO_PIN(GPIOA, 3)
#define EXTMODULE_TX_GPIO_AF LL_GPIO_AF_3 // TIM5_CH3
#define EXTMODULE_TIMER TIM5
#define EXTMODULE_TIMER_32BITS
#define EXTMODULE_TIMER_Channel LL_TIM_CHANNEL_CH3
#define EXTMODULE_TIMER_IRQn TIM5_IRQn
#define EXTMODULE_TIMER_IRQHandler TIM5_IRQHandler
#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2)
#define EXTMODULE_TIMER_TX_GPIO_AF LL_GPIO_AF_2
//USART
#define EXTMODULE_USART USART2
#define EXTMODULE_USART_TX_DMA DMA2
#define EXTMODULE_USART_TX_DMA_CHANNEL LL_DMAMUX1_REQ_USART2_TX
#define EXTMODULE_USART_TX_DMA_STREAM LL_DMA_STREAM_6
#define EXTMODULE_USART_RX_DMA_CHANNEL LL_DMAMUX1_REQ_USART2_RX
#define EXTMODULE_USART_RX_DMA_STREAM LL_DMA_STREAM_5
#define EXTMODULE_USART_IRQHandler USART2_IRQHandler
#define EXTMODULE_USART_IRQn USART2_IRQn
//TIMER
#define EXTMODULE_TIMER_DMA_CHANNEL LL_DMAMUX1_REQ_TIM5_UP
#define EXTMODULE_TIMER_DMA DMA2
#define EXTMODULE_TIMER_DMA_STREAM LL_DMA_STREAM_3
#define EXTMODULE_TIMER_DMA_STREAM_IRQn DMA2_Stream3_IRQn
#define EXTMODULE_TIMER_DMA_IRQHandler DMA2_Stream3_IRQHandler
// Trainer Port
#define TRAINER_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA)
#define TRAINER_IN_GPIO GPIO_PIN(GPIOI, 6)
#define TRAINER_IN_TIMER_Channel LL_TIM_CHANNEL_CH2
#define TRAINER_OUT_GPIO GPIO_PIN(GPIOI, 5)
#define TRAINER_OUT_TIMER_Channel LL_TIM_CHANNEL_CH1
#define TRAINER_TIMER TIM8
#define TRAINER_TIMER_IRQn TIM8_CC_IRQn
#define TRAINER_TIMER_IRQHandler TIM8_CC_IRQHandler
#define TRAINER_GPIO_AF LL_GPIO_AF_3
#define TRAINER_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2)
#define TRAINER_DETECT_GPIO GPIO_PIN(GPIOH, 4)
// AUX ports
#define AUX_SERIAL_TX_GPIO GPIO_PIN(GPIOB, 6) // PB.06
#define AUX_SERIAL_RX_GPIO GPIO_PIN(GPIOB, 5) // PB.05
#define AUX_SERIAL_USART UART5
#define AUX_SERIAL_USART_IRQHandler UART5_IRQHandler
#define AUX_SERIAL_USART_IRQn UART5_IRQn
#define AUX_SERIAL_DMA_TX DMA2
#define AUX_SERIAL_DMA_TX_STREAM LL_DMA_STREAM_1
#define AUX_SERIAL_DMA_TX_CHANNEL LL_DMAMUX1_REQ_UART5_TX
#define AUX_SERIAL_DMA_RX DMA2
#define AUX_SERIAL_DMA_RX_STREAM LL_DMA_STREAM_2
#define AUX_SERIAL_DMA_RX_CHANNEL LL_DMAMUX1_REQ_UART5_RX
#define AUX_SERIAL_PWR_GPIO GPIO_PIN(GPIOB, 7) // PB.07
#define AUX2_SERIAL_TX_GPIO GPIO_PIN(GPIOB, 10) // PB.10
#define AUX2_SERIAL_RX_GPIO GPIO_PIN(GPIOB, 11) // PB.11
#define AUX2_SERIAL_USART USART3
#define AUX2_SERIAL_USART_IRQHandler USART3_IRQHandler
#define AUX2_SERIAL_USART_IRQn USART3_IRQn
#define AUX2_SERIAL_DMA_TX DMA2
#define AUX2_SERIAL_DMA_TX_STREAM LL_DMA_STREAM_7
#define AUX2_SERIAL_DMA_TX_CHANNEL LL_DMAMUX1_REQ_USART3_TX
#define AUX2_SERIAL_DMA_RX DMA1
#define AUX2_SERIAL_DMA_RX_STREAM LL_DMA_STREAM_6
#define AUX2_SERIAL_DMA_RX_CHANNEL LL_DMAMUX1_REQ_USART3_RX
#define AUX2_SERIAL_PWR_GPIO GPIO_PIN(GPIOC, 13) // PC.13
// Touch
#define TOUCH_I2C_BUS I2C_Bus_1
#define TOUCH_INT_GPIO GPIO_PIN(GPIOE, 2) // PE.02
#define TOUCH_RST_GPIO GPIO_PIN(GPIOJ, 13) // PJ.13
// TOUCH_INT_EXTI IRQ
#if !defined(USE_EXTI2_IRQ)
#define USE_EXTI2_IRQ
#define EXTI2_IRQ_Priority 9
#endif
// No I2C IO-expander here, so the rotenc exti list carries USE_EXTI9_5_IRQ.
#define LANDSCAPE_LCD true
#define PORTRAIT_LCD false
#define LANDSCAPE_LCD_SML false
#define LANDSCAPE_LCD_STD true
#define LANDSCAPE_LCD_LRG false
#define LSE_DRIVE_STRENGTH RCC_LSEDRIVE_HIGH
#endif // _HAL_H_
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#ifndef _USB_DESCRIPTOR_H_
#define _USB_DESCRIPTOR_H_
#define USB_NAME "Senduwing H17"
#define USB_MANUFACTURER 'S', 'E', 'N', 'D', ' ', ' ', ' ', ' ' /* 8 bytes */
#define USB_PRODUCT 'H', '1', '7', ' ', ' ', ' ', ' ', ' ' /* 8 Bytes */
#endif // _USB_DESCRIPTOR_H_
+74
View File
@@ -262,6 +262,80 @@ LEGACY_NAMES = [
}
}
},
{
"targets": {"h17"},
"inputs": {
"LH": {
"yaml": "Rud",
"lua": "rud",
"description": "Rudder"
},
"LV": {
"yaml": "Ele",
"lua": "ele",
"description": "Elevator"
},
"RV": {
"yaml": "Thr",
"lua": "thr",
"description": "Throttle"
},
"RH": {
"yaml": "Ail",
"lua": "ail",
"description": "Aileron"
},
"P1": {
"yaml": "S1",
"lua": "s1",
"label": "S1",
"short_label": "1",
"description": "Potentiometer S1"
},
"P2": {
"yaml": "6POS",
"lua": "6pos",
"label": "6POS",
"short_label": "6",
"description": "Multipos Switch"
},
"P3": {
"yaml": "S4",
"lua": "s4",
"label": "S4",
"short_label": "4",
"description": "Potentiometer S4"
},
"SL1": {
"yaml": "S2",
"lua": "s2",
"label": "S2",
"short_label": "2",
"description": "Potentiometer S2"
},
"SL2": {
"yaml": "S3",
"lua": "s3",
"label": "S3",
"short_label": "3",
"description": "Potentiometer S3"
},
"EXT1": {
"yaml": "EXT1",
"lua": "ext1",
"label": "EXT1",
"short_label": "E1",
"description": "Ext 1"
},
"EXT2": {
"yaml": "EXT2",
"lua": "ext2",
"label": "EXT2",
"short_label": "E2",
"description": "Ext 2"
}
}
},
{
"targets": {"x12s"},
"inputs": {
+3
View File
@@ -170,6 +170,9 @@ get_target_build_options() {
pa01)
BUILD_OPTIONS+="-DPCB=PA01"
;;
h17)
BUILD_OPTIONS+="-DPCB=H17"
;;
*)
echo "Unknown target: $target_name"
return 1
+1 -1
View File
@@ -226,7 +226,7 @@ else
tx12mk2 zorro v14 v14lcd
x7access x9dp2019 x9e
# colour
c14 el18 nb4p nv14 st16 pa01
c14 el18 h17 nb4p nv14 st16 pa01
pl18 pl18ev pl18u
t15 t15pro t16 t18 t22
tx15 gx15 tx16s tx16smk3 f16 v12 v16
+1 -1
View File
@@ -9,7 +9,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
: "${SRCDIR:=$(dirname "$(pwd)/$0")/..}"
: ${FLAVOR:="nv14;el18;pl18;pl18ev;pl18u;nb4p;st16;c14;pa01;t12;t12max;t15;t15pro;t22;t16;t18;t8;zorro;pocket;commando8;tlite;tpro;tprov2;tpros;bumblebee;t20;t20v2;t14;lr3pro;mt12;gx12;tx12;tx12mk2;boxer;tx16s;tx16smk3;x10;x10express;x12s;x7;x7access;x9d;x9dp;x9dp2019;x9e;x9lite;x9lites;xlite;xlites;f16;v16;v14;v14lcd;v12;tx15;gx15"}
: ${FLAVOR:="nv14;el18;pl18;pl18ev;pl18u;nb4p;st16;c14;pa01;t12;t12max;t15;t15pro;t22;t16;t18;t8;zorro;pocket;commando8;tlite;tpro;tprov2;tpros;bumblebee;t20;t20v2;t14;lr3pro;mt12;gx12;tx12;tx12mk2;boxer;tx16s;tx16smk3;x10;x10express;x12s;x7;x7access;x9d;x9dp;x9dp2019;x9e;x9lite;x9lites;xlite;xlites;f16;v16;v14;v14lcd;v12;tx15;gx15;h17"}
: ${COMMON_OPTIONS:="-DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_RULE_MESSAGES=OFF -Wno-dev -DCMAKE_MESSAGE_LOG_LEVEL=WARNING"}
# wipe build directory clean
+1 -1
View File
@@ -11,7 +11,7 @@ if [[ -n ${GCC_ARM} ]] ; then
export PATH=${GCC_ARM}:$PATH
fi
: ${FLAVOR:="x10;t15;pl18;nv14;pl18u;nb4p;x9d;x9dp2019;x9e;xlite;xlites;x7;tpro;t20;f16;gx12;st16;c14;pa01;tx15;gx15;t15pro;tx16smk3;t22;v12"}
: ${FLAVOR:="x10;t15;pl18;nv14;pl18u;nb4p;x9d;x9dp2019;x9e;xlite;xlites;x7;tpro;t20;f16;gx12;st16;c14;pa01;tx15;gx15;t15pro;tx16smk3;t22;v12;h17"}
: ${SRCDIR:="$(dirname "$SCRIPT_DIR")"}
: ${COMMON_OPTIONS:="-DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_RULE_MESSAGES=OFF -Wno-dev -DDISABLE_COMPANION=YES -DCMAKE_MESSAGE_LOG_LEVEL=WARNING"}
+176
View File
@@ -6535,5 +6535,181 @@
"side": "R"
}
]
},
{
"name": "Senduwing H17",
"wasm": "edgetx-h17-simulator.wasm",
"display": {
"w": 480,
"h": 272,
"depth": 16
},
"inputs": [
{
"name": "LH",
"type": "STICK",
"label": "LH"
},
{
"name": "LV",
"type": "STICK",
"label": "LV"
},
{
"name": "RV",
"type": "STICK",
"label": "RV"
},
{
"name": "RH",
"type": "STICK",
"label": "RH"
},
{
"name": "P1",
"type": "FLEX",
"label": "S1",
"default": "POT"
},
{
"name": "SL1",
"type": "FLEX",
"label": "S2",
"default": "POT"
},
{
"name": "SL2",
"type": "FLEX",
"label": "S3",
"default": "POT"
},
{
"name": "P3",
"type": "FLEX",
"label": "S4",
"default": "POT"
},
{
"name": "P2",
"type": "FLEX",
"label": "6POS",
"default": "MULTIPOS"
},
{
"name": "EXT1",
"type": "FLEX",
"label": "EXT1",
"default": "NONE"
},
{
"name": "EXT2",
"type": "FLEX",
"label": "EXT2",
"default": "NONE"
}
],
"switches": [
{
"name": "SA",
"type": "3POS",
"default": "2POS"
},
{
"name": "SB",
"type": "3POS",
"default": "3POS"
},
{
"name": "SC",
"type": "3POS",
"default": "3POS"
},
{
"name": "SD",
"type": "3POS",
"default": "2POS"
},
{
"name": "SE",
"type": "3POS",
"default": "3POS"
},
{
"name": "SF",
"type": "2POS",
"default": "2POS"
},
{
"name": "SG",
"type": "2POS",
"default": "TOGGLE"
},
{
"name": "SH",
"type": "2POS",
"default": "TOGGLE"
},
{
"name": "SI",
"type": "2POS",
"default": "2POS"
},
{
"name": "SJ",
"type": "2POS",
"default": "2POS"
}
],
"trims": [
{
"name": "T1"
},
{
"name": "T2"
},
{
"name": "T3"
},
{
"name": "T4"
}
],
"keys": [
{
"key": "KEY_MODEL",
"label": "MDL",
"side": "R"
},
{
"key": "KEY_SYS",
"label": "SYS",
"side": "L"
},
{
"key": "KEY_PAGEUP",
"label": "PAGE◀",
"side": "L"
},
{
"key": "KEY_PAGEDN",
"label": "PAGE▶",
"side": "R"
},
{
"key": "KEY_TELE",
"label": "TELE",
"side": "R"
},
{
"key": "KEY_EXIT",
"label": "RTN",
"side": "L"
},
{
"key": "KEY_ENTER",
"label": "Enter ⏎",
"side": "R"
}
]
}
]