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)
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) {}; }
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()
36 bool lcdInitFinished
= false;
41 // New hardware SPI driver for LCD
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
;
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
71 WriteCommand(0x24); //Temperature compensation
72 WriteCommand(0xE9); //set bias=1/10
73 WriteCommand(0x81); //Set Vop
75 AspiCmd(CONTRAST_OFS
+25);
77 AspiCmd(CONTRAST_OFS
+g_eeGeneral
.contrast
);
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
94 WriteCommand(0xF7); //end row address of RAM program window.PAGE32
96 WriteCommand(0xF4); //start column address of RAM program window.
98 WriteCommand(0xF6); //end column address of RAM program window.SEG212
102 static void LCD_Init()
104 AspiCmd(0x2B); //Panel loading set ,Internal VLCD.
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
110 AspiCmd(CONTRAST_OFS
+25);
112 AspiCmd(CONTRAST_OFS
+g_eeGeneral
.contrast
);
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.
129 AspiCmd(0xF5); //starting row address of RAM program window.
131 AspiCmd(0xF6); //ending column address of RAM program window.
133 AspiCmd(0xF7); //ending row address of RAM program window.
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) \
160 volatile bool lcd_busy
;
162 #if !defined(LCD_DUAL_BUFFER)
163 void lcdRefreshWait()
169 void lcdRefresh(bool wait
)
171 if (!lcdInitFinished
) {
175 //wait if previous DMA transfer still active
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)
189 LDC_DMA_Stream
->M0AR
= (uint32_t)displayBuf
;
190 displayBuf
= (displayBuf
== displayBuf1
) ? displayBuf2
: displayBuf1
;
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.
216 #else // #if defined(REVPLUS)
219 if (!lcdInitFinished
) {
223 for (uint32_t y
=0; y
<LCD_H
; y
++) {
224 uint8_t *p
= &displayBuf
[y
/2 * LCD_W
];
233 for (uint32_t x
=0; x
<LCD_W
; x
++) {
237 LCD_WRITE_BIT(b
& 0x08);
238 LCD_WRITE_BIT(b
& 0x04);
239 LCD_WRITE_BIT(b
& 0x02);
240 LCD_WRITE_BIT(b
& 0x01);
253 GPIO_InitTypeDef GPIO_InitStructure
;
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
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 ;
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
);
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!
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!
363 if (WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) return; //no need to reset LCD module
367 delay_ms(1); // only 3 us needed according to data-sheet, we use 1 ms
372 Finishes LCD initialization. It is called auto-magically when first LCD command is
373 issued by the other parts of the code.
377 lcdInitFinished
= true;
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()) {
402 while(g_tmr10ms
< (RESET_WAIT_DELAY_MS
/10)) {}; //wait measured from the power-on
404 delay_ms(RESET_WAIT_DELAY_MS
);
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
) {
419 AspiCmd(0x81); //Set Vop
420 AspiCmd(val
+CONTRAST_OFS
); //0--255
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;