diff --git a/radio/src/targets/taranis/backlight_driver.cpp b/radio/src/targets/taranis/backlight_driver.cpp new file mode 100644 index 000000000..4aabbdf56 --- /dev/null +++ b/radio/src/targets/taranis/backlight_driver.cpp @@ -0,0 +1,109 @@ +/* + * 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 "opentx.h" + +#if defined(PCBX9E) +void backlightInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN_1|BACKLIGHT_GPIO_PIN_2; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure); + GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_1, BACKLIGHT_GPIO_AF_1); + GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_2, BACKLIGHT_GPIO_AF_1); + BACKLIGHT_TIMER->ARR = 100 ; + BACKLIGHT_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 50000 - 1; // 20us * 100 = 2ms => 500Hz + BACKLIGHT_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 ; // PWM + BACKLIGHT_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E ; + BACKLIGHT_TIMER->CCR2 = 0 ; + BACKLIGHT_TIMER->CCR1 = 100 ; + BACKLIGHT_TIMER->EGR = 0 ; + BACKLIGHT_TIMER->CR1 = TIM_CR1_CEN ; // Counter enable +} + +void turnBacklightOn(uint8_t level, uint8_t color) +{ + BACKLIGHT_TIMER->CCR1 = ((100-level)*(20-color))/20; + BACKLIGHT_TIMER->CCR2 = ((100-level)*color)/20; +} + +void turnBacklightOff(void) +{ + BACKLIGHT_TIMER->CCR1 = 0; + BACKLIGHT_TIMER->CCR2 = 0; +} +#elif defined(PCBX9DP) +void backlightInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN_1|BACKLIGHT_GPIO_PIN_2; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure); + GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_1, BACKLIGHT_GPIO_AF_1); + GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_2, BACKLIGHT_GPIO_AF_1); + BACKLIGHT_TIMER->ARR = 100 ; + BACKLIGHT_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 50000 - 1; // 20us * 100 = 2ms => 500Hz + BACKLIGHT_TIMER->CCMR1 = TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 ; // PWM + BACKLIGHT_TIMER->CCMR2 = TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 ; // PWM + BACKLIGHT_TIMER->CCER = TIM_CCER_CC4E | TIM_CCER_CC2E ; + BACKLIGHT_TIMER->CCR2 = 0 ; + BACKLIGHT_TIMER->CCR4 = 100 ; + BACKLIGHT_TIMER->EGR = 0 ; + BACKLIGHT_TIMER->CR1 = TIM_CR1_CEN ; // Counter enable +} + +void turnBacklightOn(uint8_t level, uint8_t color) +{ + BACKLIGHT_TIMER->CCR4 = ((100-level)*(20-color))/20; + BACKLIGHT_TIMER->CCR2 = ((100-level)*color)/20; +} + +void turnBacklightOff(void) +{ + BACKLIGHT_TIMER->CCR4 = 0; + BACKLIGHT_TIMER->CCR2 = 0; +} +#else +void backlightInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN_1; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure); + GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_1, BACKLIGHT_GPIO_AF_1); + BACKLIGHT_TIMER->ARR = 100 ; + BACKLIGHT_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 50000 - 1; // 20us * 100 = 2ms => 500Hz + BACKLIGHT_TIMER->CCMR1 = 0x60 ; // PWM + BACKLIGHT_TIMER->CCER = 1 ; + BACKLIGHT_TIMER->CCR1 = 80; + BACKLIGHT_TIMER->EGR = 0 ; + BACKLIGHT_TIMER->CR1 = 1 ; +} +#endif diff --git a/radio/src/targets/taranis/lcd_driver_aspi.cpp b/radio/src/targets/taranis/lcd_driver_aspi.cpp new file mode 100644 index 000000000..5ec3a5a70 --- /dev/null +++ b/radio/src/targets/taranis/lcd_driver_aspi.cpp @@ -0,0 +1,301 @@ +/* + * 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 "opentx.h" + +#define CONTRAST_OFS 5 +#define RESET_WAIT_DELAY_MS 1300 // Wait time after LCD reset before first command + +bool lcdInitFinished = false; +void lcdInitFinish(); + +#define LCD_NCS_HIGH() LCD_NCS_GPIO->BSRRL = LCD_NCS_GPIO_PIN +#define LCD_NCS_LOW() LCD_NCS_GPIO->BSRRH = LCD_NCS_GPIO_PIN + +#define LCD_A0_HIGH() LCD_SPI_GPIO->BSRRL = LCD_A0_GPIO_PIN +#define LCD_A0_LOW() LCD_SPI_GPIO->BSRRH = LCD_A0_GPIO_PIN + +#define LCD_RST_HIGH() LCD_RST_GPIO->BSRRL = LCD_RST_GPIO_PIN +#define LCD_RST_LOW() LCD_RST_GPIO->BSRRH = LCD_RST_GPIO_PIN + +#define LCD_CLK_HIGH() LCD_SPI_GPIO->BSRRL = LCD_CLK_GPIO_PIN +#define LCD_CLK_LOW() LCD_SPI_GPIO->BSRRH = LCD_CLK_GPIO_PIN + +#define LCD_MOSI_HIGH() LCD_SPI_GPIO->BSRRL = LCD_MOSI_GPIO_PIN +#define LCD_MOSI_LOW() LCD_SPI_GPIO->BSRRH = LCD_MOSI_GPIO_PIN + +void lcdWriteCommand(uint8_t command) +{ + int i = 8; + LCD_A0_LOW(); + + LCD_CLK_HIGH(); + LCD_CLK_HIGH(); + LCD_NCS_LOW(); + + while (i--) { + LCD_CLK_LOW(); + + if (command & 0x80) + LCD_MOSI_HIGH(); + else + LCD_MOSI_LOW(); + + command <<= 1; + + LCD_CLK_LOW(); + LCD_CLK_LOW(); + + LCD_CLK_HIGH(); + LCD_CLK_HIGH(); + } + + LCD_NCS_HIGH(); + LCD_A0_HIGH(); +} + +void lcdWriteData(uint8_t data) +{ + int i = 8; + + LCD_CLK_HIGH(); + LCD_CLK_HIGH(); + LCD_A0_HIGH(); + LCD_NCS_LOW(); + + while (i--) { + LCD_CLK_LOW(); + if (data & 0x80) + LCD_MOSI_HIGH(); + else + LCD_MOSI_LOW(); + + data <<= 1; + + LCD_CLK_LOW(); + LCD_CLK_LOW(); + + LCD_CLK_HIGH(); + LCD_CLK_HIGH(); + } + + LCD_NCS_HIGH(); + LCD_A0_HIGH(); +} + +void lcdStart() +{ + lcdWriteCommand(0x2B); // Panel loading set, Internal VLCD. + delay_ms(20); + lcdWriteCommand(0x25); // Temperature compensation curve definition: 0x25 = -0.05%/oC + lcdWriteCommand(0xEA); // Set bias=1/10: Command table NO.27 + lcdWriteCommand(0x81); // Set Vop +#if defined(BOOT) + lcdWriteCommand(CONTRAST_OFS+25); +#else + lcdWriteCommand(CONTRAST_OFS+g_eeGeneral.contrast); +#endif + lcdWriteCommand(0xA6); // Inverse display off + lcdWriteCommand(0xD1); // Set RGB: Command table NO.21, D1=RGB + lcdWriteCommand(0xD5); // Set color mode 4K and 12bits: Command table NO.22 + lcdWriteCommand(0xA0); // Line rates, 25.2 Klps + lcdWriteCommand(0xC8); // Set N-LINE inversion + lcdWriteCommand(0x1D); // Disable NIV + lcdWriteCommand(0xF1); // Set CEN + lcdWriteCommand(0x3F); // 1/64 DUTY + lcdWriteCommand(0x84); // Disable Partial Display + lcdWriteCommand(0xC4); // MY=1, MX=0 + lcdWriteCommand(0x89); // WA=1, column (CA) increment (+1) first until CA reaches CA boundary, then RA will increment by (+1) + + lcdWriteCommand(0xF8); // Set Window Program Enable, inside modle + lcdWriteCommand(0xF4); // Start column address of RAM program window. + lcdWriteCommand(0x00); + lcdWriteCommand(0xF5); // Start row address of RAM program window. + lcdWriteCommand(0x60); + lcdWriteCommand(0xF6); // End column address of RAM program window. + lcdWriteCommand(0x47); + lcdWriteCommand(0xF7); // End row address of RAM program window. + lcdWriteCommand(0x9F); +} + +void lcdWriteAddress(uint8_t x, uint8_t y) +{ + lcdWriteCommand(x&0x0F); // Set Column Address LSB CA[3:0] + lcdWriteCommand((x>>4) | 0x10); // Set Column Address MSB CA[7:4] + + lcdWriteCommand((y&0x0F) | 0x60); // Set Row Address LSB RA [3:0] + lcdWriteCommand(((y>>4) & 0x0F) | 0x70); // Set Row Address MSB RA [7:4] +} + +#define LCD_WRITE_BIT(bit) \ + if (bit) \ + LCD_MOSI_HIGH(); \ + else \ + LCD_MOSI_LOW(); \ + LCD_CLK_LOW(); \ + LCD_CLK_LOW(); \ + LCD_CLK_LOW(); \ + LCD_CLK_HIGH(); \ + LCD_CLK_HIGH(); + +void lcdRefresh() +{ + if (!lcdInitFinished) { + lcdInitFinish(); + } + + for (uint8_t y=0; y>= 4; + LCD_WRITE_BIT(b & 0x08); + LCD_WRITE_BIT(b & 0x04); + LCD_WRITE_BIT(b & 0x02); + LCD_WRITE_BIT(b & 0x01); + } + + LCD_NCS_HIGH(); + LCD_A0_HIGH(); + + lcdWriteData(0); + } +} + +void lcdHardwareInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + + // Configure CLK, MOSI and A0 pins in output push-pull mode + GPIO_InitStructure.GPIO_Pin = LCD_MOSI_GPIO_PIN | LCD_CLK_GPIO_PIN | LCD_A0_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(LCD_SPI_GPIO, &GPIO_InitStructure); + + LCD_NCS_HIGH(); + + // Configure NCS pin in output push-pull mode with PULLUP + GPIO_InitStructure.GPIO_Pin = LCD_NCS_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(LCD_NCS_GPIO, &GPIO_InitStructure); + + // Configure RST pin in output push-pull mode with PULLUP + GPIO_InitStructure.GPIO_Pin = LCD_RST_GPIO_PIN; + GPIO_Init(LCD_RST_GPIO, &GPIO_InitStructure); +} + +/* + Proper method for turning of LCD module. It must be used, + otherwise we might damage LCD crystals in the long run! +*/ +void lcdOff() +{ + /* + LCD Sleep mode is also good for draining capacitors and enables us + to re-init LCD without any delay + */ + lcdWriteCommand(0xAE); //LCD sleep + delay_ms(3); //wait for caps to drain +} + +void lcdReset() +{ + LCD_RST_LOW(); + delay_ms(1); // only 3 us needed according to data-sheet, we use 1 ms + LCD_RST_HIGH(); +} + +/* + Starts LCD initialization routine. It should be called as + soon as possible after the reset because LCD takes a lot of + time to properly power-on. + + Make sure that delay_ms() is functional before calling this function! +*/ +void lcdInit() +{ + lcdHardwareInit(); + + if (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) { + lcdReset(); + } +} + +/* + Finishes LCD initialization. It is called auto-magically when first LCD command is + issued by the other parts of the code. +*/ +void lcdInitFinish() +{ + lcdInitFinished = true; + + /* + LCD needs longer time to initialize in low temperatures. The data-sheet + mentions a time of at least 150 ms. The delay of 1300 ms was obtained + experimentally. It was tested down to -10 deg Celsius. + + The longer initialization time seems to only be needed for regular Taranis, + the Taranis Plus (9XE) has been tested to work without any problems at -18 deg Celsius. + Therefore the delay for T+ is lower. + + If radio is reset by watchdog or boot-loader the wait is skipped, but the LCD + is initialized in any case. + + This initialization is needed in case the user moved power switch to OFF and + then immediately to ON position, because lcdOff() was called. In any case the LCD + initialization (without reset) is also recommended by the data sheet. + */ + + if (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) { +#if !defined(BOOT) + while (g_tmr10ms < (RESET_WAIT_DELAY_MS/10)); // Wait measured from the power-on +#else + delay_ms(RESET_WAIT_DELAY_MS); +#endif + } + + lcdStart(); + lcdWriteCommand(0xAF); // dc2=1, IC into exit SLEEP MODE, dc3=1 gray=ON, dc4=1 Green Enhanc mode disabled + delay_ms(20); // Needed for internal DC-DC converter startup +} + +void lcdSetRefVolt(uint8_t val) +{ + if (!lcdInitFinished) { + lcdInitFinish(); + } + + lcdWriteCommand(0x81); // Set Vop + lcdWriteCommand(val+CONTRAST_OFS); // 0-255 +}