Subversion Repositories DashDisplay

Rev

Rev 71 | Rev 74 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
66 mjames 1
/*
2
 * display.cpp
3
 *
4
 *  Created on: 30 Nov 2020
5
 *      Author: mike
6
 */
7
 
8
#include "main.h"
9
#include "display.h"
10
#include "switches.h"
11
#include "nvram.h"
12
#include <cstring>
13
#include "libOLED/stm32_halDisplay.H"
14
#include "libOLED/fontclass.H"
15
#include "libOLED/displayDial.H"
16
#include "libPlx/displayInfo.H"
17
#include "libOLED/ap_math.h"
70 mjames 18
#include "libSmallPrintf/small_printf.h"
66 mjames 19
 
20
#include "splash.H"
21
 
22
namespace
23
{
24
        int const WIDTH = 128;
25
        int const HEIGHT = 64;
26
        int const DISPLAY_RAMWIDTH = 132;
27
}
28
 
29
uint8_t displayBuffer[2][dataSize(WIDTH, HEIGHT)];
30
 
31
stm32_halDisplay_t displays[MAX_DISPLAYS] =
32
        {stm32_halDisplay_t(WIDTH, HEIGHT, DISPLAY_RAMWIDTH, displayBuffer[0],
33
                                                &hspi1,
34
                                                SPI_CD_GPIO_Port,
35
                                                SPI_CD_Pin,
36
                                                SPI_RESET_GPIO_Port,
37
                                                SPI_RESET_Pin,
38
                                                SPI_NSS1_GPIO_Port,
39
                                                SPI_NSS1_Pin),
40
         stm32_halDisplay_t(WIDTH, HEIGHT,
41
                                                DISPLAY_RAMWIDTH,
42
                                                displayBuffer[1],
43
                                                &hspi1,
44
                                                SPI_CD_GPIO_Port,
45
                                                SPI_CD_Pin,
46
                                                SPI_RESET_GPIO_Port,
47
                                                SPI_RESET_Pin,
48
                                                SPI_NSS2_GPIO_Port,
49
                                                SPI_NSS2_Pin)};
50
 
51
displayDial_t dials[MAX_DISPLAYS] =
67 mjames 52
        {displayFullDial_t(displays[0]), displayFullDial_t(displays[1])};
66 mjames 53
#if defined __cplusplus
54
extern "C"
55
{
56
#endif
57
        static void
58
        showMinMax(display_t &display, uint8_t dp_pos, int16_t int_min,
59
                           uint16_t int_max)
60
        {
61
                const char padding[] = "      ";
62
                // left justified display of minimum
63
                int8_t width = display.fontSigDigits(small_font, 0, 0, 0, dp_pos, int_min, WHITE);
64
                // pad with spaces if fewer than 6 characters are used.
65
                if (width != 6)
66
                        display.printString(small_font, padding, 6 - width, WHITE);
67
 
68
                display.gotoxy(0, 8);
69
                display.printString(small_font, "Min", 3, WHITE);
70
 
71
                // right justified display of maximum
72
                width = display.fontSigDigits(small_font, 120, 0, 1, dp_pos, int_max, WHITE);
73
                // right justified display of maximum : pad spaces to left
74
                if (width != 6)
75
                        display.printString(small_font, padding, 6 - width, WHITE);
76
 
77
                display.gotoxy(110, 8);
78
                display.printString(small_font, "Max", 3, WHITE);
79
        }
80
 
81
        void
82
        cc_init()
83
        {
84
                for (auto i = 0; i < MAX_DISPLAYS; i++)
85
                {
86
                        display_t &display = displays[i];
87
                        if (i == 0)
88
                                display.reset();
89
                        display.init();
90
                        display.clearDisplay(BLACK);
91
                        displaySplash(display);
92
                        display.gotoxy(8, 32);
93
                        display.printString(large_font, i == 0 ? "1" : "2", 1, BLACK);
94
                        display.display();
95
                }
96
 
97
                HAL_Delay(1000);
98
 
99
                for (auto i = 0; i < MAX_DISPLAYS; i++)
100
                {
101
                        display_t &display = displays[i];
102
                        display.clearDisplay(BLACK);
103
                        display.setPixelMode(WHITE);
104
                        display.display();
105
                        context_t &context = contexts[i];
71 mjames 106
                        context.dial_timer = 500; // enough time to see at least one frame of PLX before NVRAM check
66 mjames 107
                        context.dial1 = -1;
108
                        context.OldObservation = -1;
109
                        context.OldObservationIndex = -1;
110
                }
111
        }
112
 
113
        // Check to see if there is an observation/instance in the dynamic data array
114
        // that matches the current observation/instance in the NVRAM
115
 
116
        void
117
        cc_check_nvram(int dialIndex)
118
        {
119
                if (dialIndex < 0 && dialIndex > MAX_DISPLAYS)
120
                        return;
70 mjames 121
                // algorithm only works when there is a vector of observations
122
 
66 mjames 123
                context_t &context = contexts[dialIndex];
124
 
125
                // check for timer timeout on consistent timer
126
 
127
                if (context.dial_timer)
128
                {
129
                        context.dial_timer--;
130
                        if (context.dial_timer == 0)
131
                        {
132
                                context.dial_timer = DialTimeout;
133
                                int i;
134
                                // use dialIndex+1 as tag for data : always non-zero.
135
                                nvram_info_t *dial_nvram = find_nvram_data(dialIndex + 1);
136
 
137
                                if (dial_nvram && context.knobPos < 0)
138
                                {
70 mjames 139
                                        for (i = 0; i < MAXRDG; i++)
140
                                                if (isValid(i) && (Info[i].observation == dial_nvram->data.observation) && (Info[i].instance == dial_nvram->data.instance))
66 mjames 141
                                                {
142
                                                        context.knobPos = i;
143
                                                        return;
144
                                                }
145
                                }
146
                                if (context.knobPos == -1)
147
                                        context.knobPos = dialIndex; // timed out , not in NVRAM, use a default
148
 
70 mjames 149
                                // dont save dial info for invalid data
150
                                if (!isValid(context.knobPos))
151
                                        return;
66 mjames 152
                                // is this a change since the last timeout ?
70 mjames 153
 
66 mjames 154
                                if (!dial_nvram || (Info[context.knobPos].observation != dial_nvram->data.observation) || (Info[context.knobPos].instance != dial_nvram->data.instance))
155
                                {
156
 
157
                                        // store the observation and instance in the NVRAM, not dial position.
158
                                        nvram_info_t curr_val;
159
                                        curr_val.data.observation = Info[context.knobPos].observation;
160
                                        curr_val.data.instance = Info[context.knobPos].instance;
161
                                        curr_val.data.tag = dialIndex + 1;
162
 
163
                                        write_nvram_data(curr_val);
164
                                }
165
                        }
166
                }
167
        }
168
 
169
        int
170
        cc_display(int dialIndex, int suppressIndex)
171
 
172
        {
72 mjames 173
 
174
 
66 mjames 175
                if (dialIndex < 0 && dialIndex > MAX_DISPLAYS)
176
                        return -1;
177
                context_t &context = contexts[dialIndex];
178
                displayDial_t &dial = dials[dialIndex];
179
                stm32_halDisplay_t &display = displays[dialIndex];
180
                int itemIndex = context.knobPos;
181
                char buff[10];
182
                int i;
70 mjames 183
                char *msg;
184
                int len;
72 mjames 185
                // check for startup phase 
186
                if (itemIndex < 0)
187
                {
188
                        display.clearDisplay(BLACK);
189
                        i = small_sprintf(buff, "Wait");
190
                        display.gotoxy(64 - i * 4, 48);
191
                        display.printString(large_font, buff, i, WHITE);
66 mjames 192
 
72 mjames 193
                        display.display();
194
                        return -1;
195
                }
196
 
197
 
198
 
66 mjames 199
                // check for item suppression
200
                if (itemIndex == suppressIndex)
201
                {
202
                        context.dial1 = -1;
203
                        context.OldObservation = -1;
204
                        context.OldObservationIndex = -1;
205
 
70 mjames 206
                        display.clearDisplay(BLACK);
207
                        i = small_sprintf(buff, "Supp-%02d", itemIndex);
208
                        display.gotoxy(64 - i * 4, 48);
209
                        display.printString(large_font, buff, i, WHITE);
210
 
66 mjames 211
                        display.display();
212
                        return -1; // we suppressed this display
213
                }
214
 
70 mjames 215
                // check for item validity 
216
                if(!isValid(itemIndex))
217
                {
218
                        context.dial1 = -1;
219
                        context.OldObservation = -1;
220
                        context.OldObservationIndex = -1;
221
                        display.clearDisplay(BLACK);
222
                        i = small_sprintf(buff, "Inval-%02d", itemIndex);
223
                        display.gotoxy(64 - i * 4, 48);
224
                        display.printString(large_font, buff, i, WHITE);
225
 
226
                        display.display();
227
                        return itemIndex;
228
                }
229
 
66 mjames 230
                // clear startup display off the screen
231
                if (context.OldObservation == -1)
232
                        display.clearDisplay(BLACK);
233
 
234
                int DataVal = Info[itemIndex].data; // data reading
235
                int Observation = Info[itemIndex].observation;
236
                int ObservationIndex = Info[itemIndex].instance;
237
                // now to convert the readings and format strings
238
                // find out limits
239
 
240
                // if the user presses the dial then reset min/max to current value
241
                if (push_pos[dialIndex] == 1)
242
                {
243
                        Info[itemIndex].Max = DataVal;
244
                        Info[itemIndex].Min = DataVal; // 12 bit max value
245
                }
246
 
247
                // detect change in observation being displayed, reset the dial
248
                if (Observation < PLX_MAX_OBS)
249
                {
250
                        if (Observation != context.OldObservation || ObservationIndex != context.OldObservationIndex)
251
                        {
252
 
70 mjames 253
                                display.clearDisplay(BLACK);
66 mjames 254
                                dial.draw_scale(DisplayInfo[Observation].Low,
255
                                                                DisplayInfo[Observation].High, 12, 1,
256
                                                                DisplayInfo[Observation].TickScale);
257
 
258
                                dial.draw_limits();
259
 
260
                                msg = DisplayInfo[Observation].name;
261
                                len = 7;
262
                                int len1 = ObservationIndex > 0 ? len - 1 : len;
263
                                for (i = 0; i < len1 && msg[i]; i++)
264
                                {
265
                                        buff[i] = msg[i];
266
                                }
267
                                if (ObservationIndex > 0 && i < len)
268
                                {
269
                                        buff[i++] = ObservationIndex + '1';
270
                                }
271
 
272
                                display.gotoxy(64 - i * 4, 48);
273
                                display.printString(large_font, buff, i, WHITE);
274
 
275
                                context.OldObservation = Observation;
276
                                context.OldObservationIndex = ObservationIndex;
277
                                context.dial1 = -1; // do not display old needle, cleared screen
278
                                display.display();
279
                        }
280
                }
281
 
282
                if (Info[itemIndex].updated)
283
                {
284
                        Info[itemIndex].updated = 0;
285
 
286
                        double max_rdg;
287
                        double min_rdg;
288
                        double cur_rdg;
289
                        int int_rdg;
290
                        int int_max;
291
                        int int_min;
292
 
293
                        max_rdg = ConveriMFDRaw2Data((enum PLX_Observations)Observation, DisplayInfo[Observation].Units,
294
                                                                                 Info[itemIndex].Max);
295
                        min_rdg = ConveriMFDRaw2Data((enum PLX_Observations)Observation, DisplayInfo[Observation].Units,
296
                                                                                 Info[itemIndex].Min);
297
                        cur_rdg = ConveriMFDRaw2Data((enum PLX_Observations)Observation, DisplayInfo[Observation].Units,
298
                                                                                 Info[itemIndex].data);
299
                        int dp_pos; // where to print the decimal place
300
                        float scale = 1.0;
301
                        switch (DisplayInfo[Observation].DP)
302
                        {
303
                        default:
304
                        case 0:
305
                                scale = 1.0;
306
                                dp_pos = display_t::NO_DECIMAL;
307
                                break;
308
                        case 1:
309
                                scale = 10.0;
310
                                dp_pos = 1;
311
                                break;
312
                        case 2:
313
                                scale = 100.0;
314
                                dp_pos = 2;
315
                                break;
316
                        }
317
                        int_rdg = (int)(cur_rdg * scale);
318
                        int_max = (int)(max_rdg * scale);
319
                        int_min = (int)(min_rdg * scale);
320
 
321
                        cur_rdg -= DisplayInfo[Observation].Low;
322
                        cur_rdg = ap_math::SINE_STEPS * cur_rdg / (DisplayInfo[Observation].High - DisplayInfo[Observation].Low);
323
 
324
                        context.dial0 = (int)cur_rdg;
325
 
326
                        display.gotoxy(32, 28);
327
                        display.fontDigits(large_font, 4, dp_pos, int_rdg, WHITE);
328
 
329
                        display.printString(small_font, DisplayInfo[Observation].suffix,
330
                                                                strlen(DisplayInfo[Observation].suffix));
331
                        display.printString(small_font, "    ",
332
                                                                3 - strlen(DisplayInfo[Observation].suffix));
333
                        // print value overlaid by needle
334
 
335
                        /* old needle un-draw */
336
                        if (context.dial1 >= 0)
337
                        {
338
                                dial.draw_needle(context.dial1);
339
                        }
340
                        dial.draw_needle(context.dial0);
341
                        context.dial1 = context.dial0;
342
                        showMinMax(display, dp_pos, int_min, int_max);
343
                }
344
                else
345
                {
346
                        if (Info[itemIndex].lastUpdated && ((HAL_GetTick() - Info[itemIndex].lastUpdated) > 1000))
347
                        {
348
                                context.OldObservation = -1;     // force a redraw of axes on next entry point
349
                                Info[itemIndex].lastUpdated = 0; // and stop further timeouts.
350
                        }
351
                }
352
 
353
                display.gotoxy(0, 32);
354
 
355
                // display BT connection status
356
                display.printString(small_font, btConnected() ? "\x81" : " ", 1);
357
 
358
                display.display();
359
 
360
                return itemIndex;
361
        }
362
}