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 #if defined(RADIO_T12)
24 #define LCD_CONTRAST_OFFSET -10
26 #define LCD_CONTRAST_OFFSET 160
28 #define RESET_WAIT_DELAY_MS 300 // Wait time after LCD reset before first command
29 #define WAIT_FOR_DMA_END() do { } while (lcd_busy)
31 #define LCD_NCS_HIGH() LCD_NCS_GPIO->BSRRL = LCD_NCS_GPIO_PIN
32 #define LCD_NCS_LOW() LCD_NCS_GPIO->BSRRH = LCD_NCS_GPIO_PIN
34 #define LCD_A0_HIGH() LCD_SPI_GPIO->BSRRL = LCD_A0_GPIO_PIN
35 #define LCD_A0_LOW() LCD_SPI_GPIO->BSRRH = LCD_A0_GPIO_PIN
37 #define LCD_RST_HIGH() LCD_RST_GPIO->BSRRL = LCD_RST_GPIO_PIN
38 #define LCD_RST_LOW() LCD_RST_GPIO->BSRRH = LCD_RST_GPIO_PIN
40 bool lcdInitFinished
= false;
43 void lcdWriteCommand(uint8_t byte
)
47 while ((SPI3
->SR
& SPI_SR_TXE
) == 0) {
50 (void)SPI3
->DR
; // Clear receive
52 while ((SPI3
->SR
& SPI_SR_RXNE
) == 0) {
58 void lcdHardwareInit()
60 GPIO_InitTypeDef GPIO_InitStructure
;
62 // APB1 clock / 2 = 133nS per clock
63 LCD_SPI
->CR1
= 0; // Clear any mode error
64 LCD_SPI
->CR1
= SPI_CR1_SSM
| SPI_CR1_SSI
| SPI_CR1_CPOL
| SPI_CR1_CPHA
;
66 LCD_SPI
->CR1
|= SPI_CR1_MSTR
; // Make sure in case SSM/SSI needed to be set first
67 LCD_SPI
->CR1
|= SPI_CR1_SPE
;
71 GPIO_InitStructure
.GPIO_Pin
= LCD_NCS_GPIO_PIN
;
72 GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_OUT
;
73 GPIO_InitStructure
.GPIO_OType
= GPIO_OType_PP
;
74 GPIO_InitStructure
.GPIO_Speed
= GPIO_Speed_25MHz
;
75 GPIO_InitStructure
.GPIO_PuPd
= GPIO_PuPd_NOPULL
;
76 GPIO_Init(LCD_NCS_GPIO
, &GPIO_InitStructure
);
78 GPIO_InitStructure
.GPIO_Pin
= LCD_RST_GPIO_PIN
;
79 GPIO_Init(LCD_RST_GPIO
, &GPIO_InitStructure
);
81 GPIO_InitStructure
.GPIO_Pin
= LCD_A0_GPIO_PIN
;
82 GPIO_InitStructure
.GPIO_Speed
= GPIO_Speed_50MHz
;
83 GPIO_Init(LCD_SPI_GPIO
, &GPIO_InitStructure
);
85 GPIO_InitStructure
.GPIO_Pin
= LCD_CLK_GPIO_PIN
| LCD_MOSI_GPIO_PIN
;
86 GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_AF
;
87 GPIO_Init(LCD_SPI_GPIO
, &GPIO_InitStructure
);
89 GPIO_PinAFConfig(LCD_SPI_GPIO
, LCD_MOSI_GPIO_PinSource
, LCD_GPIO_AF
);
90 GPIO_PinAFConfig(LCD_SPI_GPIO
, LCD_CLK_GPIO_PinSource
, LCD_GPIO_AF
);
92 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_EN
; // Disable DMA
93 LCD_DMA
->HIFCR
= LCD_DMA_FLAGS
; // Write ones to clear bits
94 LCD_DMA_Stream
->CR
= DMA_SxCR_PL_0
| DMA_SxCR_MINC
| DMA_SxCR_DIR_0
;
95 LCD_DMA_Stream
->PAR
= (uint32_t)&LCD_SPI
->DR
;
97 LCD_DMA_Stream
->NDTR
= LCD_W
;
99 LCD_DMA_Stream
->M0AR
= (uint32_t)displayBuf
;
100 LCD_DMA_Stream
->NDTR
= LCD_W
*LCD_H
/8*4;
102 LCD_DMA_Stream
->FCR
= 0x05; // DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0;
104 NVIC_EnableIRQ(LCD_DMA_Stream_IRQn
);
110 #if defined(RADIO_T12)
111 // Jumper has the screen inverted.
112 lcdWriteCommand(0xe2); // (14) Soft reset
113 lcdWriteCommand(0xa0); // Set seg
114 lcdWriteCommand(0xc8); // Set com
115 lcdWriteCommand(0xf8); // Set booster
116 lcdWriteCommand(0x00); // 5x
117 lcdWriteCommand(0xa3); // Set bias=1/6
118 lcdWriteCommand(0x22); // Set internal rb/ra=5.0
119 lcdWriteCommand(0x2f); // All built-in power circuits on
120 lcdWriteCommand(0x24); // Power control set
121 lcdWriteCommand(0x81); // Set contrast
122 lcdWriteCommand(0x0A); // Set Vop
123 lcdWriteCommand(0xa6); // Set display mode
125 lcdWriteCommand(0xe2); // (14) Soft reset
126 lcdWriteCommand(0xa1); // Set seg
127 lcdWriteCommand(0xc0); // Set com
128 lcdWriteCommand(0xf8); // Set booster
129 lcdWriteCommand(0x00); // 5x
130 lcdWriteCommand(0xa3); // Set bias=1/6
131 lcdWriteCommand(0x22); // Set internal rb/ra=5.0
132 lcdWriteCommand(0x2f); // All built-in power circuits on
133 lcdWriteCommand(0x81); // Set contrast
134 lcdWriteCommand(0x36); // Set Vop
135 lcdWriteCommand(0xa6); // Set display mode
141 lcdWriteCommand(0x2F); // Internal pump control
143 lcdWriteCommand(0x24); // Temperature compensation
144 lcdWriteCommand(0xE9); // Set bias=1/10
145 lcdWriteCommand(0x81); // Set Vop
147 lcdWriteCommand(LCD_CONTRAST_OFFSET
+LCD_CONTRAST_DEFAULT
);
149 lcdWriteCommand(LCD_CONTRAST_OFFSET
+g_eeGeneral
.contrast
);
151 lcdWriteCommand(0xA2); // Set line rate: 28KLPS
152 lcdWriteCommand(0x28); // Set panel loading
153 lcdWriteCommand(0x40); // Scroll line LSB
154 lcdWriteCommand(0x50); // Scroll line MSB
155 lcdWriteCommand(0x89); // RAM address control
156 lcdWriteCommand(0xC0); // LCD mapping control
157 lcdWriteCommand(0x04); // MX=0, MY=1
158 lcdWriteCommand(0xD0); // Display pattern = 16-SCALE GRAY
159 lcdWriteCommand(0xF1); // Set COM end
160 lcdWriteCommand(0x3F); // 64
162 lcdWriteCommand(0xF8); // Set Window Program Disable.
164 lcdWriteCommand(0xF5); // Start row address of RAM program window. PAGE1
165 lcdWriteCommand(0x00);
166 lcdWriteCommand(0xF7); // End row address of RAM program window. PAGE32
167 lcdWriteCommand(0x1F);
168 lcdWriteCommand(0xF4); // Start column address of RAM program window.
169 lcdWriteCommand(0x00);
170 lcdWriteCommand(0xF6); // End column address of RAM program window. SEG212
171 lcdWriteCommand(0xD3);
174 void lcdWriteAddress(uint8_t x
, uint8_t y
)
176 lcdWriteCommand(x
& 0x0F); // Set Column Address LSB CA[3:0]
177 lcdWriteCommand((x
>>4) | 0x10); // Set Column Address MSB CA[7:4]
179 lcdWriteCommand((y
&0x0F) | 0x60); // Set Row Address LSB RA [3:0]
180 lcdWriteCommand(((y
>>4) & 0x0F) | 0x70); // Set Row Address MSB RA [7:4]
184 volatile bool lcd_busy
;
186 #if !defined(LCD_DUAL_BUFFER)
187 void lcdRefreshWait()
193 void lcdRefresh(bool wait
)
195 if (!lcdInitFinished
) {
200 uint8_t * p
= displayBuf
;
201 for (uint8_t y
=0; y
< 8; y
++, p
+=LCD_W
) {
202 lcdWriteCommand(0x10); // Column addr 0
203 lcdWriteCommand(0xB0 | y
); // Page addr y
204 #if !defined(RADIO_T12)
205 lcdWriteCommand(0x04);
212 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_EN
; // Disable DMA
213 LCD_DMA
->HIFCR
= LCD_DMA_FLAGS
; // Write ones to clear bits
214 LCD_DMA_Stream
->M0AR
= (uint32_t)p
;
215 LCD_DMA_Stream
->CR
|= DMA_SxCR_EN
| DMA_SxCR_TCIE
; // Enable DMA & TC interrupts
216 LCD_SPI
->CR2
|= SPI_CR2_TXDMAEN
;
224 // Wait if previous DMA transfer still active
228 lcdWriteAddress(0, 0);
233 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_EN
; // Disable DMA
234 LCD_DMA
->HIFCR
= LCD_DMA_FLAGS
; // Write ones to clear bits
236 #if defined(LCD_DUAL_BUFFER)
238 LCD_DMA_Stream
->M0AR
= (uint32_t)displayBuf
;
239 displayBuf
= (displayBuf
== displayBuf1
) ? displayBuf2
: displayBuf1
;
242 LCD_DMA_Stream
->CR
|= DMA_SxCR_EN
| DMA_SxCR_TCIE
; // Enable DMA & TC interrupts
243 LCD_SPI
->CR2
|= SPI_CR2_TXDMAEN
;
247 extern "C" void LCD_DMA_Stream_IRQHandler()
249 DEBUG_INTERRUPT(INT_LCD
);
251 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_TCIE
; // Stop interrupt
252 LCD_DMA
->HIFCR
|= LCD_DMA_FLAG_INT
; // Clear interrupt flag
253 LCD_SPI
->CR2
&= ~SPI_CR2_TXDMAEN
;
254 LCD_DMA_Stream
->CR
&= ~DMA_SxCR_EN
; // Disable DMA
256 while (LCD_SPI
->SR
& SPI_SR_BSY
) {
257 /* Wait for SPI to finish sending data
258 The DMA TX End interrupt comes two bytes before the end of SPI transmission,
259 therefore we have to wait here.
267 Proper method for turning of LCD module. It must be used,
268 otherwise we might damage LCD crystals in the long run!
275 LCD Sleep mode is also good for draining capacitors and enables us
276 to re-init LCD without any delay
278 lcdWriteCommand(0xAE); // LCD sleep
279 delay_ms(3); // Wait for caps to drain
288 delay_ms(1); // Only 3 us needed according to data-sheet, we use 1 ms
294 Starts LCD initialization routine. It should be called as
295 soon as possible after the reset because LCD takes a lot of
296 time to properly power-on.
298 Make sure that delay_ms() is functional before calling this function!
304 if (IS_LCD_RESET_NEEDED()) {
310 Finishes LCD initialization. It is called auto-magically when first LCD command is
311 issued by the other parts of the code.
314 #if (defined(PCBX9DP) && PCBREV >= 2019) || defined(PCBX7ACCESS)
315 #define LCD_DELAY_NEEDED() true
317 #define LCD_DELAY_NEEDED() (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE())
322 lcdInitFinished
= true;
325 LCD needs longer time to initialize in low temperatures. The data-sheet
326 mentions a time of at least 150 ms. The delay of 1300 ms was obtained
327 experimentally. It was tested down to -10 deg Celsius.
329 The longer initialization time seems to only be needed for regular Taranis,
330 the Taranis Plus (9XE) has been tested to work without any problems at -18 deg Celsius.
331 Therefore the delay for T+ is lower.
333 If radio is reset by watchdog or boot-loader the wait is skipped, but the LCD
334 is initialized in any case.
336 This initialization is needed in case the user moved power switch to OFF and
337 then immediately to ON position, because lcdOff() was called. In any case the LCD
338 initialization (without reset) is also recommended by the data sheet.
341 if (LCD_DELAY_NEEDED()) {
343 while (g_tmr10ms
< (RESET_WAIT_DELAY_MS
/ 10)); // wait measured from the power-on
345 delay_ms(RESET_WAIT_DELAY_MS
);
350 lcdWriteCommand(0xAF); // dc2=1, IC into exit SLEEP MODE, dc3=1 gray=ON, dc4=1 Green Enhanc mode disabled
351 delay_ms(20); // needed for internal DC-DC converter startup
354 void lcdSetRefVolt(uint8_t val
)
356 if (!lcdInitFinished
) {
364 lcdWriteCommand(0x81); // Set Vop
365 lcdWriteCommand(val
+LCD_CONTRAST_OFFSET
); // 0-255