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.
23 #define LCD_CONTRAST_OFFSET 160
24 #define RESET_WAIT_DELAY_MS 300 // Wait time after LCD reset before first command
25 #define WAIT_FOR_DMA_END() do { } while (lcd_busy)
27 #define LCD_NCS_HIGH() LCD_NCS_GPIO->BSRRL = LCD_NCS_GPIO_PIN
28 #define LCD_NCS_LOW() LCD_NCS_GPIO->BSRRH = LCD_NCS_GPIO_PIN
30 #define LCD_A0_HIGH() LCD_SPI_GPIO->BSRRL = LCD_A0_GPIO_PIN
31 #define LCD_A0_LOW() LCD_SPI_GPIO->BSRRH = LCD_A0_GPIO_PIN
33 #define LCD_RST_HIGH() LCD_RST_GPIO->BSRRL = LCD_RST_GPIO_PIN
34 #define LCD_RST_LOW() LCD_RST_GPIO->BSRRH = LCD_RST_GPIO_PIN
36 bool lcdInitFinished
= false;
39 void lcdWriteCommand(uint8_t byte
)
43 while ((SPI3
->SR
& SPI_SR_TXE
) == 0) {
46 (void)SPI3
->DR
; // Clear receive
48 while ((SPI3
->SR
& SPI_SR_RXNE
) == 0) {
54 void lcdHardwareInit()
56 GPIO_InitTypeDef GPIO_InitStructure
;
58 // APB1 clock / 2 = 133nS per clock
59 LCD_SPI
->CR1
= 0; // Clear any mode error
60 LCD_SPI
->CR1
= SPI_CR1_SSM
| SPI_CR1_SSI
| SPI_CR1_CPOL
| SPI_CR1_CPHA
;
62 LCD_SPI
->CR1
|= SPI_CR1_MSTR
; // Make sure in case SSM/SSI needed to be set first
63 LCD_SPI
->CR1
|= SPI_CR1_SPE
;
67 GPIO_InitStructure
.GPIO_Pin
= LCD_NCS_GPIO_PIN
;
68 GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_OUT
;
69 GPIO_InitStructure
.GPIO_OType
= GPIO_OType_PP
;
70 GPIO_InitStructure
.GPIO_Speed
= GPIO_Speed_25MHz
;
71 GPIO_InitStructure
.GPIO_PuPd
= GPIO_PuPd_NOPULL
;
72 GPIO_Init(LCD_NCS_GPIO
, &GPIO_InitStructure
);
74 GPIO_InitStructure
.GPIO_Pin
= LCD_RST_GPIO_PIN
;
75 GPIO_Init(LCD_RST_GPIO
, &GPIO_InitStructure
);
77 GPIO_InitStructure
.GPIO_Pin
= LCD_A0_GPIO_PIN
;
78 GPIO_InitStructure
.GPIO_Speed
= GPIO_Speed_50MHz
;
79 GPIO_Init(LCD_SPI_GPIO
, &GPIO_InitStructure
);
81 GPIO_InitStructure
.GPIO_Pin
= LCD_CLK_GPIO_PIN
| LCD_MOSI_GPIO_PIN
;
82 GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_AF
;
83 GPIO_Init(LCD_SPI_GPIO
, &GPIO_InitStructure
);
85 GPIO_PinAFConfig(LCD_SPI_GPIO
, LCD_MOSI_GPIO_PinSource
, LCD_GPIO_AF
);
86 GPIO_PinAFConfig(LCD_SPI_GPIO
, LCD_CLK_GPIO_PinSource
, LCD_GPIO_AF
);
88 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_EN
; // Disable DMA
89 LCD_DMA
->HIFCR
= LCD_DMA_FLAGS
; // Write ones to clear bits
90 LCD_DMA_Stream
->CR
= DMA_SxCR_PL_0
| DMA_SxCR_MINC
| DMA_SxCR_DIR_0
;
91 LCD_DMA_Stream
->PAR
= (uint32_t)&LCD_SPI
->DR
;
93 LCD_DMA_Stream
->NDTR
= LCD_W
;
95 LCD_DMA_Stream
->M0AR
= (uint32_t)displayBuf
;
96 LCD_DMA_Stream
->NDTR
= LCD_W
*LCD_H
/8*4;
98 LCD_DMA_Stream
->FCR
= 0x05; // DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0;
100 NVIC_EnableIRQ(LCD_DMA_Stream_IRQn
);
106 lcdWriteCommand(0xe2); // (14) Soft reset
107 lcdWriteCommand(0xa1); // Set seg
108 lcdWriteCommand(0xc0); // Set com
109 lcdWriteCommand(0xf8); // Set booster
110 lcdWriteCommand(0x00); // 5x
111 lcdWriteCommand(0xa3); // Set bias=1/6
112 lcdWriteCommand(0x22); // Set internal rb/ra=5.0
113 lcdWriteCommand(0x2f); // All built-in power circuits on
114 lcdWriteCommand(0x81); // Set contrast
115 lcdWriteCommand(0x36); // Set Vop
116 lcdWriteCommand(0xa6); // Set display mode
121 lcdWriteCommand(0x2F); // Internal pump control
123 lcdWriteCommand(0x24); // Temperature compensation
124 lcdWriteCommand(0xE9); // Set bias=1/10
125 lcdWriteCommand(0x81); // Set Vop
127 lcdWriteCommand(LCD_CONTRAST_OFFSET
+LCD_CONTRAST_DEFAULT
);
129 lcdWriteCommand(LCD_CONTRAST_OFFSET
+g_eeGeneral
.contrast
);
131 lcdWriteCommand(0xA2); // Set line rate: 28KLPS
132 lcdWriteCommand(0x28); // Set panel loading
133 lcdWriteCommand(0x40); // Scroll line LSB
134 lcdWriteCommand(0x50); // Scroll line MSB
135 lcdWriteCommand(0x89); // RAM address control
136 lcdWriteCommand(0xC0); // LCD mapping control
137 lcdWriteCommand(0x04); // MX=0, MY=1
138 lcdWriteCommand(0xD0); // Display pattern = 16-SCALE GRAY
139 lcdWriteCommand(0xF1); // Set COM end
140 lcdWriteCommand(0x3F); // 64
142 lcdWriteCommand(0xF8); // Set Window Program Disable.
144 lcdWriteCommand(0xF5); // Start row address of RAM program window. PAGE1
145 lcdWriteCommand(0x00);
146 lcdWriteCommand(0xF7); // End row address of RAM program window. PAGE32
147 lcdWriteCommand(0x1F);
148 lcdWriteCommand(0xF4); // Start column address of RAM program window.
149 lcdWriteCommand(0x00);
150 lcdWriteCommand(0xF6); // End column address of RAM program window. SEG212
151 lcdWriteCommand(0xD3);
154 void lcdWriteAddress(uint8_t x
, uint8_t y
)
156 lcdWriteCommand(x
& 0x0F); // Set Column Address LSB CA[3:0]
157 lcdWriteCommand((x
>>4) | 0x10); // Set Column Address MSB CA[7:4]
159 lcdWriteCommand((y
&0x0F) | 0x60); // Set Row Address LSB RA [3:0]
160 lcdWriteCommand(((y
>>4) & 0x0F) | 0x70); // Set Row Address MSB RA [7:4]
164 volatile bool lcd_busy
;
166 #if !defined(LCD_DUAL_BUFFER)
167 void lcdRefreshWait()
173 void lcdRefresh(bool wait
)
175 if (!lcdInitFinished
) {
180 uint8_t * p
= displayBuf
;
181 for (uint8_t y
=0; y
< 8; y
++, p
+=LCD_W
) {
182 lcdWriteCommand(0x10); // Column addr 0
183 lcdWriteCommand(0xB0 | y
); // Page addr y
184 lcdWriteCommand(0x04);
190 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_EN
; // Disable DMA
191 LCD_DMA
->HIFCR
= LCD_DMA_FLAGS
; // Write ones to clear bits
192 LCD_DMA_Stream
->M0AR
= (uint32_t)p
;
193 LCD_DMA_Stream
->CR
|= DMA_SxCR_EN
| DMA_SxCR_TCIE
; // Enable DMA & TC interrupts
194 LCD_SPI
->CR2
|= SPI_CR2_TXDMAEN
;
202 // Wait if previous DMA transfer still active
206 lcdWriteAddress(0, 0);
211 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_EN
; // Disable DMA
212 LCD_DMA
->HIFCR
= LCD_DMA_FLAGS
; // Write ones to clear bits
214 #if defined(LCD_DUAL_BUFFER)
216 LCD_DMA_Stream
->M0AR
= (uint32_t)displayBuf
;
217 displayBuf
= (displayBuf
== displayBuf1
) ? displayBuf2
: displayBuf1
;
220 LCD_DMA_Stream
->CR
|= DMA_SxCR_EN
| DMA_SxCR_TCIE
; // Enable DMA & TC interrupts
221 LCD_SPI
->CR2
|= SPI_CR2_TXDMAEN
;
225 extern "C" void LCD_DMA_Stream_IRQHandler()
227 DEBUG_INTERRUPT(INT_LCD
);
229 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_TCIE
; // Stop interrupt
230 LCD_DMA
->HIFCR
|= LCD_DMA_FLAG_INT
; // Clear interrupt flag
231 LCD_SPI
->CR2
&= ~SPI_CR2_TXDMAEN
;
232 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_EN
; // Disable DMA
234 while (LCD_SPI
->SR
& SPI_SR_BSY
) {
235 /* Wait for SPI to finish sending data
236 The DMA TX End interrupt comes two bytes before the end of SPI transmission,
237 therefore we have to wait here.
245 Proper method for turning of LCD module. It must be used,
246 otherwise we might damage LCD crystals in the long run!
253 LCD Sleep mode is also good for draining capacitors and enables us
254 to re-init LCD without any delay
256 lcdWriteCommand(0xAE); // LCD sleep
257 delay_ms(3); // Wait for caps to drain
266 delay_ms(1); // Only 3 us needed according to data-sheet, we use 1 ms
272 Starts LCD initialization routine. It should be called as
273 soon as possible after the reset because LCD takes a lot of
274 time to properly power-on.
276 Make sure that delay_ms() is functional before calling this function!
282 if (IS_LCD_RESET_NEEDED()) {
288 Finishes LCD initialization. It is called auto-magically when first LCD command is
289 issued by the other parts of the code.
293 lcdInitFinished
= true;
296 LCD needs longer time to initialize in low temperatures. The data-sheet
297 mentions a time of at least 150 ms. The delay of 1300 ms was obtained
298 experimentally. It was tested down to -10 deg Celsius.
300 The longer initialization time seems to only be needed for regular Taranis,
301 the Taranis Plus (9XE) has been tested to work without any problems at -18 deg Celsius.
302 Therefore the delay for T+ is lower.
304 If radio is reset by watchdog or boot-loader the wait is skipped, but the LCD
305 is initialized in any case.
307 This initialization is needed in case the user moved power switch to OFF and
308 then immediately to ON position, because lcdOff() was called. In any case the LCD
309 initialization (without reset) is also recommended by the data sheet.
312 if (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) {
314 while (g_tmr10ms
< (RESET_WAIT_DELAY_MS
/10)); // wait measured from the power-on
316 delay_ms(RESET_WAIT_DELAY_MS
);
321 lcdWriteCommand(0xAF); // dc2=1, IC into exit SLEEP MODE, dc3=1 gray=ON, dc4=1 Green Enhanc mode disabled
322 delay_ms(20); // needed for internal DC-DC converter startup
325 void lcdSetRefVolt(uint8_t val
)
327 if (!lcdInitFinished
) {
335 lcdWriteCommand(0x81); // Set Vop
336 lcdWriteCommand(val
+LCD_CONTRAST_OFFSET
); // 0-255