Various fixes around Companion trainer mode (#7116)
[opentx.git] / radio / src / targets / taranis / top_lcd_driver.cpp
blob743d0784d13aaf550443a1e53f65c5091ca093a0
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 CS1_HIGH() TOPLCD_GPIO->BSRRL = TOPLCD_GPIO_PIN_CS1
24 #define CS1_LOW() TOPLCD_GPIO->BSRRH = TOPLCD_GPIO_PIN_CS1
25 #define CS2_HIGH() TOPLCD_GPIO->BSRRL = TOPLCD_GPIO_PIN_CS2
26 #define CS2_LOW() TOPLCD_GPIO->BSRRH = TOPLCD_GPIO_PIN_CS2
27 #define WR_HIGH() TOPLCD_GPIO->BSRRL = TOPLCD_GPIO_PIN_WR
28 #define WR_LOW() TOPLCD_GPIO->BSRRH = TOPLCD_GPIO_PIN_WR
29 #define DATA_HIGH() TOPLCD_GPIO->BSRRL = TOPLCD_GPIO_PIN_DATA
30 #define DATA_LOW() TOPLCD_GPIO->BSRRH = TOPLCD_GPIO_PIN_DATA
31 #define BL_ON() TOPLCD_GPIO->BSRRL = TOPLCD_GPIO_PIN_BL
32 #define BL_OFF() TOPLCD_GPIO->BSRRH = TOPLCD_GPIO_PIN_BL
34 const uint8_t TimeLCDsegs[] = { 0xAF, 0x06, 0x6D, 0x4F, 0xC6, 0xCB, 0xEB, 0x0E, 0xEF, 0xCF };
35 const uint8_t RssiLCDsegs[] = { 0xFA, 0x60, 0xBC, 0xF4, 0x66, 0xD6, 0xDE, 0x70, 0xFE, 0xF6 };
36 const uint8_t OpTimeLCDsegs[] = { 0x5F, 0x06, 0x6B, 0x2F, 0x36, 0x3D, 0x7D, 0x07, 0x7F, 0x3F };
38 uint8_t Ht1621Data1[11] = { 0 };
39 uint8_t Ht1621Data2[7] = { 0 };
41 void delay1_7us()
43 delay_01us(17);
46 void ht1621SendCommand(uint8_t chip, unsigned char command)
48 if (chip)
49 CS2_LOW();
50 else
51 CS1_LOW();
53 delay1_7us();
54 WR_LOW(); //PRESENT 100 COMMAND CODE
55 delay1_7us();
56 DATA_HIGH();
57 delay1_7us();
58 WR_HIGH();
59 delay1_7us();
60 WR_LOW();
61 delay1_7us();
62 DATA_LOW();
63 delay1_7us();
64 WR_HIGH();
65 delay1_7us();
67 WR_LOW();
68 delay1_7us();
69 DATA_LOW();
70 delay1_7us();
71 WR_HIGH();
72 delay1_7us();
74 for (int i=0; i<8; i++) {
75 WR_LOW();
76 delay1_7us();
77 if ((command & 0x80) !=0) {
78 DATA_HIGH();
79 delay1_7us();
81 else {
82 DATA_LOW();
83 delay1_7us();
85 WR_HIGH();
86 delay1_7us();
87 command = command << 1;
89 WR_LOW();
90 delay1_7us();
91 WR_HIGH();
92 delay1_7us();
94 if (chip)
95 CS2_HIGH();
96 else
97 CS1_HIGH();
99 delay1_7us();
102 void ht1621WrData(uint8_t data, uint8_t count)
104 while (count) {
105 WR_LOW();
106 if (data & 0x80)
107 DATA_HIGH();
108 else
109 DATA_LOW();
110 delay1_7us();
111 WR_HIGH();
112 delay1_7us();
113 data <<= 1;
114 count -= 1;
118 void ht1621WrAllData(uint8_t chip, uint8_t *pData)
120 int len;
121 if (chip) {
122 len = sizeof(Ht1621Data2);
123 CS2_LOW();
125 else {
126 len = sizeof(Ht1621Data1);
127 CS1_LOW();
129 delay1_7us();
130 ht1621WrData(0xa0, 3);
131 ht1621WrData(0, 6); // HT1621 6 bit,left 2 bit;
132 for (int i=0; i<len; i++) {
133 ht1621WrData(*pData++, 8);
135 if (chip)
136 CS2_HIGH();
137 else
138 CS1_HIGH();
139 delay1_7us();
142 #if 0
143 void initTimerTopLcd()
145 // Timer12
146 RCC->APB1ENR |= RCC_APB1ENR_TIM12EN; // Enable clock
147 TIM12->ARR = 17; // 1.7uS
148 TIM12->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 10000000 - 1; // 0.1uS from 30MHz
149 TIM12->CCER = 0;
150 TIM12->CCMR1 = 0;
151 TIM12->EGR = 0;
152 TIM12->CR1 = 5;
153 // TIM12->DIER |= 1;
154 NVIC_SetPriority(TIM8_BRK_TIM12_IRQn, 2 );
155 NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);
158 extern "C" void TIM8_BRK_TIM12_IRQHandler()
160 struct t_top_lcd_control *pc;
161 TIM12->SR &= ~TIM_SR_UIF;
162 pc = &TopLcdControl;
164 if ( pc->state == TOP_LCD_CKLOW ) {
165 WR_LOW(); //PRESENT 100 COMMAND CODE
166 if ((*pc->data & 0x80) !=0) {
167 DATA_HIGH();
169 else {
170 DATA_LOW();
172 *pc->data <<= 1;
173 pc->state = TOP_LCD_CKHI;
175 else if ( pc->state == TOP_LCD_CKHI ) {
176 WR_HIGH();
177 if ( --pc->count == 0 ) {
178 pc->state = TOP_LCD_END;
180 else {
181 pc->state = TOP_LCD_CKLOW;
184 else if (pc->state == TOP_LCD_END) {
185 if (pc->chip) {
186 CS2_HIGH();
187 pc->state = TOP_LCD_IDLE;
188 TIM12->DIER &= ~1;
190 else {
191 CS1_HIGH();
192 pc->chip = 1;
193 pc->data = pc->data2;
194 pc->count = pc->count2;
195 pc->state = TOP_LCD_IDLE;
198 else if ( pc->state == TOP_LCD_IDLE ) {
199 WR_HIGH();
200 if ( pc->chip ) CS2_LOW(); else CS1_LOW();
201 pc->state = TOP_LCD_1CKLOW;
203 else if ( pc->state == TOP_LCD_1CKLOW ) {
204 WR_LOW();
205 DATA_HIGH(); // First 1 bit
206 pc->state = TOP_LCD_1CKHI;
208 else if ( pc->state == TOP_LCD_1CKHI ) {
209 WR_HIGH();
210 if ( --pc->count == 0 ) {
211 pc->state = TOP_LCD_END;
213 else {
214 pc->state = TOP_LCD_CKLOW;
218 #endif
220 void toplcdInit()
222 RCC_AHB1PeriphClockCmd(TOPLCD_RCC_AHB1Periph, ENABLE);
223 GPIO_InitTypeDef GPIO_InitStructure;
224 GPIO_InitStructure.GPIO_Pin = TOPLCD_GPIO_PIN_DATA | TOPLCD_GPIO_PIN_WR |TOPLCD_GPIO_PIN_BL|TOPLCD_GPIO_PIN_CS1|TOPLCD_GPIO_PIN_CS2;
225 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
226 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
227 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
228 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
229 GPIO_Init(TOPLCD_GPIO, &GPIO_InitStructure);
231 BL_ON();
233 ht1621SendCommand(0, 0x03);
234 ht1621SendCommand(0, 0x01);
235 ht1621SendCommand(0, 0x29);
237 ht1621SendCommand(1, 0x03);
238 ht1621SendCommand(1, 0x01);
239 ht1621SendCommand(1, 0x29);
241 toplcdRefreshStart();
242 toplcdRefreshEnd();
245 void toplcdOff()
247 BL_OFF();
248 toplcdRefreshStart();
249 toplcdRefreshEnd();
252 void setTopFirstTimer(int32_t value)
254 Ht1621Data1[2] |= 0x10; // ":"
256 if (value < 0) {
257 if (BLINK_ON_PHASE) {
258 return;
260 else {
261 value = -value;
265 div_t qr = div((int)value, 60);
266 uint32_t r = qr.rem;
267 qr = div(qr.quot, 10);
269 // Limit to 99 min 99 sec to avoid breaking display
270 if (qr.quot > 9) {
271 qr.quot = 9;
272 qr.rem = 9;
273 r = 99;
276 Ht1621Data1[0] |= TimeLCDsegs[qr.quot];
277 Ht1621Data1[1] |= TimeLCDsegs[qr.rem];
278 qr = div(r, 10);
279 Ht1621Data1[2] |= TimeLCDsegs[qr.quot];
280 Ht1621Data1[3] |= TimeLCDsegs[qr.rem];
283 void setTopRssiValue(uint32_t rssi)
285 div_t qr = div(rssi, 10);
286 uint32_t r = 0;
287 if (qr.quot > 9) {
288 r = 1;
289 qr.quot -= 10;
291 Ht1621Data2[2] |= r ? RssiLCDsegs[1] : 0;
292 Ht1621Data2[1] |= (r || qr.quot) ? RssiLCDsegs[qr.quot] : 0;
293 Ht1621Data2[0] |= RssiLCDsegs[qr.rem];
296 void setTopRssiBar(uint32_t rssi)
298 if (rssi > 42)
299 Ht1621Data2[2] |= 1;
300 if (rssi > 45)
301 Ht1621Data2[1] |= 1;
302 if (rssi > 50)
303 Ht1621Data2[0] |= 1;
304 if (rssi > 60)
305 Ht1621Data2[3] |= 0x10;
306 if (rssi > 70)
307 Ht1621Data2[3] |= 0x20;
308 if (rssi > 80)
309 Ht1621Data2[3] |= 0x40;
310 if (rssi > 90)
311 Ht1621Data2[3] |= 0x80;
314 void setTopRssi(uint32_t rssi)
316 Ht1621Data1[4] |= 0x10; // "RSSI"
317 Ht1621Data2[3] |= 0x01; // "dB"
318 Ht1621Data2[4] |= 0x01; // Horizontal line
319 setTopRssiValue(rssi);
320 setTopRssiBar(rssi);
323 void setTopBatteryState(int state, uint8_t blinking)
325 if (!blinking || BLINK_ON_PHASE) {
326 // since the border can not be turned off,
327 // we blink the first bar even if the actual value is zero
328 if (blinking && state < 1) {
329 state = 1;
331 Ht1621Data1[4] |= 0x40; // Battery border // TODO this is not working for me, the border is ALWAYS on
332 if (state > 0)
333 Ht1621Data1[7] |= 0x80;
334 if (state > 2)
335 Ht1621Data1[9] |= 0x80;
336 if (state > 4)
337 Ht1621Data1[5] |= 0x80;
338 if (state > 6)
339 Ht1621Data1[10] |= 0x80;
340 if (state > 8)
341 Ht1621Data1[4] |= 0x80;
345 void setTopBatteryValue(uint32_t volts)
347 div_t qr;
348 uint32_t r = 0;
349 uint32_t segs;
350 qr = div(volts, 10);
351 if (qr.quot > 9) {
352 r = 1;
353 qr.quot -= 10;
355 segs = r ? RssiLCDsegs[1] : 0;
356 Ht1621Data2[3] |= ((segs & 7) << 1);
357 Ht1621Data2[4] |= segs & 0xF0;
358 segs = (r || qr.quot) ? RssiLCDsegs[qr.quot] : 0;
359 Ht1621Data2[4] |= (segs & 0x0E);
360 Ht1621Data2[5] |= segs & 0xF0;
361 segs = RssiLCDsegs[qr.rem];
362 Ht1621Data2[5] |= (segs & 0x0E) | 1; // Decimal point
363 Ht1621Data2[6] |= segs & 0xF0;
364 Ht1621Data1[4] |= 0x20; // "V"
367 void setTopSecondTimer(uint32_t value)
369 div_t qr;
370 uint32_t segs;
371 uint32_t segs2;
373 qr = div(value, 60);
374 uint32_t secs = qr.rem;
375 qr = div(qr.quot, 60);
376 uint32_t mins = qr.rem;
377 uint32_t hours = min(qr.quot, 99);
379 qr = div(secs, 10);
380 segs = OpTimeLCDsegs[qr.rem];
381 Ht1621Data1[4] |= (segs & 0x0F) | 0x70;
382 segs &= 0x70;
383 segs2 = OpTimeLCDsegs[qr.quot];
384 Ht1621Data1[5] |= segs | (segs2 & 0x0F);
386 qr = div(mins, 10);
387 segs = OpTimeLCDsegs[qr.rem];
388 segs2 &= 0x70;
389 Ht1621Data1[6] |= segs2 | (segs & 0x0F) | 0x80; // ":"
390 segs &= 0x70;
391 segs2 = OpTimeLCDsegs[qr.quot];
392 Ht1621Data1[7] |= segs | (segs2 & 0x0F);
394 qr = div(hours, 10);
395 segs = OpTimeLCDsegs[qr.rem];
396 segs2 &= 0x70;
397 Ht1621Data1[8] |= segs2 | (segs & 0x0F) | 0x80; // ":"
398 segs &= 0x70;
399 segs2 = OpTimeLCDsegs[qr.quot];
400 Ht1621Data1[9] |= segs | (segs2 & 0x0F);
401 Ht1621Data1[10] |= segs2 & 0xF0;
403 Ht1621Data1[3] |= 0x10; // "Operation Time"
406 void toplcdRefreshStart()
408 memset(Ht1621Data1, 0, sizeof(Ht1621Data1));
409 memset(Ht1621Data2, 0, sizeof(Ht1621Data2));
412 void toplcdRefreshEnd()
414 ht1621WrAllData(0, Ht1621Data1);
415 ht1621WrAllData(1, Ht1621Data2);