Taranis LCD driver: removed some unnecessary lines and cosmetics
[opentx.git] / radio / src / targets / taranis / lcd_driver.cpp
blob4add2ed46c39b2efe213abae5ca0517e71f59796
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "../../opentx.h"
23 #define WriteData(x) AspiData(x)
24 #define WriteCommand(x) AspiCmd(x)
26 #if defined(REVPLUS)
27 #define CONTRAST_OFS 160
28 #define RESET_WAIT_DELAY_MS 300 //wait time after LCD reset before first command
29 #define WAIT_FOR_DMA_END() { while(lcd_busy) {}; }
30 #else
31 #define CONTRAST_OFS 5
32 #define RESET_WAIT_DELAY_MS 1300 //wait time after LCD reset before first command
33 #define WAIT_FOR_DMA_END()
34 #endif
36 bool lcdInitFinished = false;
37 void lcdInitFinish();
39 #if defined(REVPLUS)
41 // New hardware SPI driver for LCD
42 void initLcdSpi()
44 // APB1 clock / 2 = 133nS per clock
45 LCD_SPI->CR1 = 0 ; // Clear any mode error
46 LCD_SPI->CR1 = SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPOL | SPI_CR1_CPHA ;
47 LCD_SPI->CR2 = 0 ;
48 LCD_SPI->CR1 |= SPI_CR1_MSTR ; // Make sure in case SSM/SSI needed to be set first
49 LCD_SPI->CR1 |= SPI_CR1_SPE ;
51 configure_pins( LCD_GPIO_PIN_NCS, PIN_OUTPUT | PIN_PORTA | PIN_OS25) ;
52 configure_pins( LCD_GPIO_PIN_RST, PIN_OUTPUT | PIN_PORTD | PIN_OS25) ;
53 configure_pins( LCD_GPIO_PIN_A0, PIN_OUTPUT | PIN_PORTC | PIN_OS50) ;
54 configure_pins( LCD_GPIO_PIN_MOSI|LCD_GPIO_PIN_CLK, PIN_PORTC | PIN_OS50 | PIN_PER_6 | PIN_PERIPHERAL ) ;
56 LDC_DMA_Stream->CR &= ~DMA_SxCR_EN ; // Disable DMA
57 LCD_DMA->HIFCR = LCD_DMA_FLAGS ; // Write ones to clear bits
58 LDC_DMA_Stream->CR = DMA_SxCR_PL_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 ;
59 LDC_DMA_Stream->PAR = (uint32_t) &LCD_SPI->DR ;
60 LDC_DMA_Stream->M0AR = (uint32_t)displayBuf;
61 LDC_DMA_Stream->FCR = 0x05 ; //DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0 ;
62 LDC_DMA_Stream->NDTR = LCD_W*LCD_H/8*4 ;
64 NVIC_EnableIRQ(LCD_DMA_Stream_IRQn) ;
67 static void LCD_Init()
69 WriteCommand(0x2F); //Internal pump control
70 delay_ms(20);
71 WriteCommand(0x24); //Temperature compensation
72 WriteCommand(0xE9); //set bias=1/10
73 WriteCommand(0x81); //Set Vop
74 #if defined(BOOT)
75 AspiCmd(CONTRAST_OFS+25);
76 #else
77 AspiCmd(CONTRAST_OFS+g_eeGeneral.contrast);
78 #endif
79 WriteCommand(0xA2); //set line rate:28KLPS
80 WriteCommand(0x28); //set pannel loading
81 WriteCommand(0x40); //scroll line LSB
82 WriteCommand(0x50); //SCROLL LINE MSB
83 WriteCommand(0x89); //ram address control
84 WriteCommand(0xC0); //LCD mapping control
85 WriteCommand(0x04); //MX=0,MY=1
86 WriteCommand(0xD0); //DISPLAY PATTERN = 16-SCALE GRAY
87 WriteCommand(0xF1); //SET COM end
88 WriteCommand(0x3F); //64
90 WriteCommand(0xF8); //Set Window Program Disable.
92 WriteCommand(0xF5); //starting row address of RAM program window.PAGE1
93 WriteCommand(0x00);
94 WriteCommand(0xF7); //end row address of RAM program window.PAGE32
95 WriteCommand(0x1F);
96 WriteCommand(0xF4); //start column address of RAM program window.
97 WriteCommand(0x00);
98 WriteCommand(0xF6); //end column address of RAM program window.SEG212
99 WriteCommand(0xD3);
101 #else
102 static void LCD_Init()
104 AspiCmd(0x2B); //Panel loading set ,Internal VLCD.
105 delay_ms(20);
106 AspiCmd(0x25); //Temperature compensation curve definition: 0x25 = -0.05%/oC
107 AspiCmd(0xEA); //set bias=1/10 :Command table NO.27
108 AspiCmd(0x81); //Set Vop
109 #if defined(BOOT)
110 AspiCmd(CONTRAST_OFS+25);
111 #else
112 AspiCmd(CONTRAST_OFS+g_eeGeneral.contrast);
113 #endif
114 AspiCmd(0xA6); //inverse display off
115 AspiCmd(0xD1); //SET RGB:Command table NO.21 .SET RGB or BGR. D1=RGB
116 AspiCmd(0xD5); //set color mode 4K and 12bits :Command table NO.22
117 AspiCmd(0xA0); //line rates,25.2 Klps
118 AspiCmd(0xC8); //SET N-LINE INVERSION
119 AspiCmd(0x1D); //Disable NIV
120 AspiCmd(0xF1); //Set CEN
121 AspiCmd(0x3F); // 1/64DUTY
122 AspiCmd(0x84); //Disable Partial Display
123 AspiCmd(0xC4); //MY=1,MX=0
124 AspiCmd(0x89); //WA=1,column (CA) increment (+1) first until CA reaches CA boundary, then RA will increment by (+1).
126 AspiCmd(0xF8); //Set Window Program Enable ,inside modle
127 AspiCmd(0xF4); //starting column address of RAM program window.
128 AspiCmd(0x00);
129 AspiCmd(0xF5); //starting row address of RAM program window.
130 AspiCmd(0x60);
131 AspiCmd(0xF6); //ending column address of RAM program window.
132 AspiCmd(0x47);
133 AspiCmd(0xF7); //ending row address of RAM program window.
134 AspiCmd(0x9F);
136 #endif
138 void Set_Address(u8 x, u8 y)
140 WriteCommand(x&0x0F); //Set Column Address LSB CA[3:0]
141 WriteCommand((x>>4)|0x10); //Set Column Address MSB CA[7:4]
143 WriteCommand((y&0x0F)|0x60); //Set Row Address LSB RA [3:0]
144 WriteCommand(((y>>4)&0x0F)|0x70); //Set Row Address MSB RA [7:4]
147 #define LCD_WRITE_BIT(bit) \
148 if (bit) \
149 LCD_MOSI_HIGH(); \
150 else \
151 LCD_MOSI_LOW(); \
152 LCD_CLK_LOW(); \
153 LCD_CLK_LOW(); \
154 LCD_CLK_LOW(); \
155 LCD_CLK_HIGH(); \
156 LCD_CLK_HIGH();
158 #if defined(REVPLUS)
160 volatile bool lcd_busy;
162 #if !defined(LCD_DUAL_BUFFER)
163 void lcdRefreshWait()
165 WAIT_FOR_DMA_END();
167 #endif
169 void lcdRefresh(bool wait)
171 if (!lcdInitFinished) {
172 lcdInitFinish();
175 //wait if previous DMA transfer still active
176 WAIT_FOR_DMA_END();
177 lcd_busy = true;
179 Set_Address(0, 0);
181 LCD_NCS_LOW();
182 LCD_A0_HIGH();
184 LDC_DMA_Stream->CR &= ~DMA_SxCR_EN ; // Disable DMA
185 LCD_DMA->HIFCR = LCD_DMA_FLAGS ; // Write ones to clear bits
187 #if defined(LCD_DUAL_BUFFER)
188 //switch LCD buffer
189 LDC_DMA_Stream->M0AR = (uint32_t)displayBuf;
190 displayBuf = (displayBuf == displayBuf1) ? displayBuf2 : displayBuf1;
191 #endif
193 LDC_DMA_Stream->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA & TC interrupts
194 LCD_SPI->CR2 |= SPI_CR2_TXDMAEN ;
197 extern "C" void LCD_DMA_Stream_IRQHandler()
199 DEBUG_INTERRUPT(INT_LCD);
200 //clear interrupt flag
201 LDC_DMA_Stream->CR &= ~DMA_SxCR_TCIE ; // Stop interrupt
202 LCD_DMA->HIFCR |= LCD_DMA_FLAG_INT; // Clear interrupt flag
203 LCD_SPI->CR2 &= ~SPI_CR2_TXDMAEN ;
204 LDC_DMA_Stream->CR &= ~DMA_SxCR_EN ; // Disable DMA
206 while ( LCD_SPI->SR & SPI_SR_BSY ) {
207 /* Wait for SPI to finish sending data
208 The DMA TX End interrupt comes two bytes before the end of SPI transmission,
209 therefore we have to wait here.
212 LCD_NCS_HIGH();
213 lcd_busy = false;
216 #else // #if defined(REVPLUS)
217 void lcdRefresh()
219 if (!lcdInitFinished) {
220 lcdInitFinish();
223 for (uint32_t y=0; y<LCD_H; y++) {
224 uint8_t *p = &displayBuf[y/2 * LCD_W];
226 Set_Address(0, y);
227 AspiCmd(0xAF);
229 LCD_CLK_HIGH();
230 LCD_A0_HIGH();
231 LCD_NCS_LOW();
233 for (uint32_t x=0; x<LCD_W; x++) {
234 uint8_t b = p[x];
235 if (y & 1)
236 b >>= 4;
237 LCD_WRITE_BIT(b & 0x08);
238 LCD_WRITE_BIT(b & 0x04);
239 LCD_WRITE_BIT(b & 0x02);
240 LCD_WRITE_BIT(b & 0x01);
243 LCD_NCS_HIGH();
244 LCD_A0_HIGH();
246 WriteData(0);
249 #endif
251 void backlightInit()
253 GPIO_InitTypeDef GPIO_InitStructure;
255 #if defined(REV9E)
256 GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN_1|BACKLIGHT_GPIO_PIN_2;
257 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
258 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
259 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
260 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
261 GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure);
262 GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_1, BACKLIGHT_GPIO_AF_1);
263 GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_2, BACKLIGHT_GPIO_AF_1);
264 BACKLIGHT_TIMER->ARR = 100 ;
265 BACKLIGHT_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 50000 - 1; // 20us * 100 = 2ms => 500Hz
266 BACKLIGHT_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 ; // PWM
267 BACKLIGHT_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E ;
268 BACKLIGHT_TIMER->CCR2 = 0 ;
269 BACKLIGHT_TIMER->CCR1 = 100 ;
270 BACKLIGHT_TIMER->EGR = 0 ;
271 BACKLIGHT_TIMER->CR1 = TIM_CR1_CEN ; // Counter enable
272 #elif defined(REVPLUS)
273 GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN_1|BACKLIGHT_GPIO_PIN_2;
274 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
275 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
276 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
277 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
278 GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure);
279 GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_1, BACKLIGHT_GPIO_AF_1);
280 GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_2, BACKLIGHT_GPIO_AF_1);
281 BACKLIGHT_TIMER->ARR = 100 ;
282 BACKLIGHT_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 50000 - 1; // 20us * 100 = 2ms => 500Hz
283 BACKLIGHT_TIMER->CCMR1 = TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 ; // PWM
284 BACKLIGHT_TIMER->CCMR2 = TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 ; // PWM
285 BACKLIGHT_TIMER->CCER = TIM_CCER_CC4E | TIM_CCER_CC2E ;
286 BACKLIGHT_TIMER->CCR2 = 0 ;
287 BACKLIGHT_TIMER->CCR4 = 100 ;
288 BACKLIGHT_TIMER->EGR = 0 ;
289 BACKLIGHT_TIMER->CR1 = TIM_CR1_CEN ; // Counter enable
290 #else
291 GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN_1;
292 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
293 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
294 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
295 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
296 GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure);
297 GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource_1, BACKLIGHT_GPIO_AF_1);
298 BACKLIGHT_TIMER->ARR = 100 ;
299 BACKLIGHT_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 50000 - 1; // 20us * 100 = 2ms => 500Hz
300 BACKLIGHT_TIMER->CCMR1 = 0x60 ; // PWM
301 BACKLIGHT_TIMER->CCER = 1 ;
302 BACKLIGHT_TIMER->CCR1 = 80;
303 BACKLIGHT_TIMER->EGR = 0 ;
304 BACKLIGHT_TIMER->CR1 = 1 ;
305 #endif
308 /** Init the analog SPI GPIO
310 static void LCD_Hardware_Init()
312 GPIO_InitTypeDef GPIO_InitStructure;
314 /*!< Configure lcd CLK\ MOSI\ A0pin in output push-pull mode *************/
315 GPIO_InitStructure.GPIO_Pin = LCD_GPIO_PIN_MOSI | LCD_GPIO_PIN_CLK | LCD_GPIO_PIN_A0;
316 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
317 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
318 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
319 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
320 GPIO_Init(LCD_GPIO_SPI, &GPIO_InitStructure);
322 LCD_NCS_HIGH();
324 /*!< Configure lcd NCS pin in output push-pull mode ,PULLUP *************/
325 GPIO_InitStructure.GPIO_Pin = LCD_GPIO_PIN_NCS;
326 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
327 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
328 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
329 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
330 GPIO_Init(LCD_GPIO_NCS, &GPIO_InitStructure);
332 /*!< Configure lcd RST pin in output pushpull mode ,PULLUP *************/
333 GPIO_InitStructure.GPIO_Pin = LCD_GPIO_PIN_RST;
334 GPIO_Init(LCD_GPIO_RST, &GPIO_InitStructure);
338 Proper method for turning of LCD module. It must be used,
339 otherwise we might damage LCD crystals in the long run!
341 void lcdOff()
343 WAIT_FOR_DMA_END();
345 LCD Sleep mode is also good for draining capacitors and enables us
346 to re-init LCD without any delay
348 AspiCmd(0xAE); //LCD sleep
349 delay_ms(3); //wait for caps to drain
353 Starts LCD initialization routine. It should be called as
354 soon as possible after the reset because LCD takes a lot of
355 time to properly power-on.
357 Make sure that delay_ms() is functional before calling this function!
359 void lcdInit()
361 LCD_Hardware_Init();
363 if (WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) return; //no need to reset LCD module
365 //reset LCD module
366 LCD_RST_LOW();
367 delay_ms(1); // only 3 us needed according to data-sheet, we use 1 ms
368 LCD_RST_HIGH();
372 Finishes LCD initialization. It is called auto-magically when first LCD command is
373 issued by the other parts of the code.
375 void lcdInitFinish()
377 lcdInitFinished = true;
379 #if defined(REVPLUS)
380 initLcdSpi();
381 #endif
384 LCD needs longer time to initialize in low temperatures. The data-sheet
385 mentions a time of at least 150 ms. The delay of 1300 ms was obtained
386 experimentally. It was tested down to -10 deg Celsius.
388 The longer initialization time seems to only be needed for regular Taranis,
389 the Taranis Plus (9XE) has been tested to work without any problems at -18 deg Celsius.
390 Therefore the delay for T+ is lower.
392 If radio is reset by watchdog or boot-loader the wait is skipped, but the LCD
393 is initialized in any case.
395 This initialization is needed in case the user moved power switch to OFF and
396 then immediately to ON position, because lcdOff() was called. In any case the LCD
397 initialization (without reset) is also recommended by the data sheet.
400 if (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) {
401 #if !defined(BOOT)
402 while(g_tmr10ms < (RESET_WAIT_DELAY_MS/10)) {}; //wait measured from the power-on
403 #else
404 delay_ms(RESET_WAIT_DELAY_MS);
405 #endif
408 LCD_Init();
409 AspiCmd(0xAF); //dc2=1, IC into exit SLEEP MODE, dc3=1 gray=ON, dc4=1 Green Enhanc mode disabled
410 delay_ms(20); //needed for internal DC-DC converter startup
413 void lcdSetRefVolt(uint8_t val)
415 if (!lcdInitFinished) {
416 lcdInitFinish();
418 WAIT_FOR_DMA_END();
419 AspiCmd(0x81); //Set Vop
420 AspiCmd(val+CONTRAST_OFS); //0--255
423 #if defined(REV9E)
424 void turnBacklightOn(uint8_t level, uint8_t color)
426 BACKLIGHT_TIMER->CCR1 = ((100-level)*(20-color))/20;
427 BACKLIGHT_TIMER->CCR2 = ((100-level)*color)/20;
430 void turnBacklightOff(void)
432 BACKLIGHT_TIMER->CCR1 = 0;
433 BACKLIGHT_TIMER->CCR2 = 0;
435 #elif defined(REVPLUS)
436 void turnBacklightOn(uint8_t level, uint8_t color)
438 BACKLIGHT_TIMER->CCR4 = ((100-level)*(20-color))/20;
439 BACKLIGHT_TIMER->CCR2 = ((100-level)*color)/20;
442 void turnBacklightOff(void)
444 BACKLIGHT_TIMER->CCR4 = 0;
445 BACKLIGHT_TIMER->CCR2 = 0;
447 #endif