Merge pull request #11494 from haslinghuis/dshot_gpio
[betaflight.git] / src / main / drivers / max7456.c
blob4129fea309a588fee93aa84c13b2d5826cc7b2b2
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
25 #include "platform.h"
27 #ifdef USE_MAX7456
29 #include "build/debug.h"
31 #include "pg/max7456.h"
32 #include "pg/vcd.h"
34 #include "drivers/bus_spi.h"
35 #include "drivers/dma.h"
36 #include "drivers/io.h"
37 #include "drivers/light_led.h"
38 #include "drivers/max7456.h"
39 #include "drivers/nvic.h"
40 #include "drivers/osd.h"
41 #include "drivers/osd_symbols.h"
42 #include "drivers/time.h"
45 // 10 MHz max SPI frequency
46 #define MAX7456_MAX_SPI_CLK_HZ 10000000
47 #define MAX7456_INIT_MAX_SPI_CLK_HZ 5000000
49 // DEBUG_MAX7456_SIGNAL
50 #define DEBUG_MAX7456_SIGNAL_MODEREG 0
51 #define DEBUG_MAX7456_SIGNAL_SENSE 1
52 #define DEBUG_MAX7456_SIGNAL_REINIT 2
53 #define DEBUG_MAX7456_SIGNAL_ROWS 3
55 // DEBUG_MAX7456_SPICLOCK
56 #define DEBUG_MAX7456_SPICLOCK_OVERCLOCK 0
57 #define DEBUG_MAX7456_SPICLOCK_DEVTYPE 1
58 #define DEBUG_MAX7456_SPICLOCK_DIVISOR 2
59 #define DEBUG_MAX7456_SPICLOCK_X100 3
61 // VM0 bits
62 #define VIDEO_BUFFER_DISABLE 0x01
63 #define MAX7456_RESET 0x02
64 #define VERTICAL_SYNC_NEXT_VSYNC 0x04
65 #define OSD_ENABLE 0x08
67 #define SYNC_MODE_AUTO 0x00
68 #define SYNC_MODE_INTERNAL 0x30
69 #define SYNC_MODE_EXTERNAL 0x20
71 #define VIDEO_MODE_PAL 0x40
72 #define VIDEO_MODE_NTSC 0x00
73 #define VIDEO_MODE_MASK 0x40
74 #define VIDEO_MODE_IS_PAL(val) (((val) & VIDEO_MODE_MASK) == VIDEO_MODE_PAL)
75 #define VIDEO_MODE_IS_NTSC(val) (((val) & VIDEO_MODE_MASK) == VIDEO_MODE_NTSC)
77 #define VIDEO_SIGNAL_DEBOUNCE_MS 100 // Time to wait for input to stabilize
79 // VM1 bits
81 // duty cycle is on_off
82 #define BLINK_DUTY_CYCLE_50_50 0x00
83 #define BLINK_DUTY_CYCLE_33_66 0x01
84 #define BLINK_DUTY_CYCLE_25_75 0x02
85 #define BLINK_DUTY_CYCLE_75_25 0x03
87 // blinking time
88 #define BLINK_TIME_0 0x00
89 #define BLINK_TIME_1 0x04
90 #define BLINK_TIME_2 0x08
91 #define BLINK_TIME_3 0x0C
93 // background mode brightness (percent)
94 #define BACKGROUND_BRIGHTNESS_0 0x00
95 #define BACKGROUND_BRIGHTNESS_7 0x01
96 #define BACKGROUND_BRIGHTNESS_14 0x02
97 #define BACKGROUND_BRIGHTNESS_21 0x03
98 #define BACKGROUND_BRIGHTNESS_28 0x04
99 #define BACKGROUND_BRIGHTNESS_35 0x05
100 #define BACKGROUND_BRIGHTNESS_42 0x06
101 #define BACKGROUND_BRIGHTNESS_49 0x07
103 #define BACKGROUND_MODE_GRAY 0x80
105 // STAT register bits
107 #define STAT_PAL 0x01
108 #define STAT_NTSC 0x02
109 #define STAT_LOS 0x04
110 #define STAT_NVR_BUSY 0x20
112 #define STAT_IS_PAL(val) ((val) & STAT_PAL)
113 #define STAT_IS_NTSC(val) ((val) & STAT_NTSC)
114 #define STAT_IS_LOS(val) ((val) & STAT_LOS)
116 #define VIN_IS_PAL(val) (!STAT_IS_LOS(val) && STAT_IS_PAL(val))
117 #define VIN_IS_NTSC(val) (!STAT_IS_LOS(val) && STAT_IS_NTSC(val))
119 // DMM register bits
120 #define DMM_AUTO_INC 0x01
122 // Kluege warning!
123 // There are occasions that NTSC is not detected even with !LOS (AB7456 specific?)
124 // When this happens, lower 3 bits of STAT register is read as zero.
125 // To cope with this case, this macro defines !LOS && !PAL as NTSC.
126 // Should be compatible with MAX7456 and non-problematic case.
128 #define VIN_IS_NTSC_alt(val) (!STAT_IS_LOS(val) && !STAT_IS_PAL(val))
130 #define MAX7456_SIGNAL_CHECK_INTERVAL_MS 1000 // msec
131 #define MAX7456_STALL_CHECK_INTERVAL_MS 1000 // msec
133 // DMM special bits
134 #define CLEAR_DISPLAY 0x04
135 #define CLEAR_DISPLAY_VERT 0x06
136 #define INVERT_PIXEL_COLOR 0x08
138 // Special address for terminating incremental write
139 #define END_STRING 0xff
141 #define MAX7456ADD_READ 0x80
142 #define MAX7456ADD_VM0 0x00 //0b0011100// 00 // 00 ,0011100
143 #define MAX7456ADD_VM1 0x01
144 #define MAX7456ADD_HOS 0x02
145 #define MAX7456ADD_VOS 0x03
146 #define MAX7456ADD_DMM 0x04
147 #define MAX7456ADD_DMAH 0x05
148 #define MAX7456ADD_DMAL 0x06
149 #define MAX7456ADD_DMDI 0x07
150 #define MAX7456ADD_CMM 0x08
151 #define MAX7456ADD_CMAH 0x09
152 #define MAX7456ADD_CMAL 0x0a
153 #define MAX7456ADD_CMDI 0x0b
154 #define MAX7456ADD_OSDM 0x0c
155 #define MAX7456ADD_RB0 0x10
156 #define MAX7456ADD_RB1 0x11
157 #define MAX7456ADD_RB2 0x12
158 #define MAX7456ADD_RB3 0x13
159 #define MAX7456ADD_RB4 0x14
160 #define MAX7456ADD_RB5 0x15
161 #define MAX7456ADD_RB6 0x16
162 #define MAX7456ADD_RB7 0x17
163 #define MAX7456ADD_RB8 0x18
164 #define MAX7456ADD_RB9 0x19
165 #define MAX7456ADD_RB10 0x1a
166 #define MAX7456ADD_RB11 0x1b
167 #define MAX7456ADD_RB12 0x1c
168 #define MAX7456ADD_RB13 0x1d
169 #define MAX7456ADD_RB14 0x1e
170 #define MAX7456ADD_RB15 0x1f
171 #define MAX7456ADD_OSDBL 0x6c
172 #define MAX7456ADD_STAT 0xA0
174 #define NVM_RAM_SIZE 54
175 #define WRITE_NVR 0xA0
177 // Device type
178 #define MAX7456_DEVICE_TYPE_MAX 0
179 #define MAX7456_DEVICE_TYPE_AT 1
181 #define CHARS_PER_LINE 30 // XXX Should be related to VIDEO_BUFFER_CHARS_*?
183 #define MAX7456_SUPPORTED_LAYER_COUNT (DISPLAYPORT_LAYER_BACKGROUND + 1)
185 typedef struct max7456Layer_s {
186 uint8_t buffer[VIDEO_BUFFER_CHARS_PAL];
187 } max7456Layer_t;
189 static max7456Layer_t displayLayers[MAX7456_SUPPORTED_LAYER_COUNT];
190 static displayPortLayer_e activeLayer = DISPLAYPORT_LAYER_FOREGROUND;
192 extDevice_t max7456Device;
193 extDevice_t *dev = &max7456Device;
195 static bool max7456DeviceDetected = false;
196 static uint16_t max7456SpiClockDiv;
198 uint16_t maxScreenSize = VIDEO_BUFFER_CHARS_PAL;
200 // We write everything to the active layer and then compare
201 // it with shadowBuffer to update only changed chars.
202 // This solution is faster then redrawing entire screen.
204 static uint8_t shadowBuffer[VIDEO_BUFFER_CHARS_PAL];
206 //Max bytes to update in one call to max7456DrawScreen()
208 #define MAX_BYTES2SEND 250
209 #define MAX_BYTES2SEND_POLLED 12
210 #define MAX_ENCODE_US 20
211 #define MAX_ENCODE_US_POLLED 10
213 static DMA_DATA uint8_t spiBuf[MAX_BYTES2SEND];
215 static uint8_t videoSignalCfg;
216 static uint8_t videoSignalReg = OSD_ENABLE; // OSD_ENABLE required to trigger first ReInit
217 static uint8_t displayMemoryModeReg = 0;
219 static uint8_t hosRegValue; // HOS (Horizontal offset register) value
220 static uint8_t vosRegValue; // VOS (Vertical offset register) value
222 static bool fontIsLoading = false;
224 static uint8_t max7456DeviceType;
226 static displayPortBackground_e deviceBackgroundType = DISPLAY_BACKGROUND_TRANSPARENT;
228 // previous states initialized outside the valid range to force update on first call
229 #define INVALID_PREVIOUS_REGISTER_STATE 255
230 static uint8_t previousBlackWhiteRegister = INVALID_PREVIOUS_REGISTER_STATE;
231 static uint8_t previousInvertRegister = INVALID_PREVIOUS_REGISTER_STATE;
233 static uint8_t *getLayerBuffer(displayPortLayer_e layer)
235 return displayLayers[layer].buffer;
238 static uint8_t *getActiveLayerBuffer(void)
240 return getLayerBuffer(activeLayer);
243 static void max7456SetRegisterVM1(void)
245 uint8_t backgroundGray = BACKGROUND_BRIGHTNESS_28; // this is the device default background gray level
246 uint8_t vm1Register = BLINK_TIME_1 | BLINK_DUTY_CYCLE_75_25; // device defaults
247 if (deviceBackgroundType != DISPLAY_BACKGROUND_TRANSPARENT) {
248 vm1Register |= BACKGROUND_MODE_GRAY;
249 switch (deviceBackgroundType) {
250 case DISPLAY_BACKGROUND_BLACK:
251 backgroundGray = BACKGROUND_BRIGHTNESS_0;
252 break;
253 case DISPLAY_BACKGROUND_LTGRAY:
254 backgroundGray = BACKGROUND_BRIGHTNESS_49;
255 break;
256 case DISPLAY_BACKGROUND_GRAY:
257 default:
258 backgroundGray = BACKGROUND_BRIGHTNESS_28;
259 break;
262 vm1Register |= (backgroundGray << 4);
263 spiWriteReg(dev, MAX7456ADD_VM1, vm1Register);
266 uint8_t max7456GetRowsCount(void)
268 return (videoSignalReg & VIDEO_MODE_PAL) ? VIDEO_LINES_PAL : VIDEO_LINES_NTSC;
271 // When clearing the shadow buffer we fill with 0 so that the characters will
272 // be flagged as changed when compared to the 0x20 used in the layer buffers.
273 static void max7456ClearShadowBuffer(void)
275 memset(shadowBuffer, 0, maxScreenSize);
278 // Buffer is filled with the whitespace character (0x20)
279 static void max7456ClearLayer(displayPortLayer_e layer)
281 memset(getLayerBuffer(layer), 0x20, VIDEO_BUFFER_CHARS_PAL);
284 void max7456ReInit(void)
286 uint8_t srdata = 0;
288 switch (videoSignalCfg) {
289 case VIDEO_SYSTEM_PAL:
290 videoSignalReg = VIDEO_MODE_PAL | OSD_ENABLE;
291 break;
293 case VIDEO_SYSTEM_NTSC:
294 videoSignalReg = VIDEO_MODE_NTSC | OSD_ENABLE;
295 break;
297 case VIDEO_SYSTEM_AUTO:
298 srdata = spiReadRegMsk(dev, MAX7456ADD_STAT);
300 if (VIN_IS_NTSC(srdata)) {
301 videoSignalReg = VIDEO_MODE_NTSC | OSD_ENABLE;
302 } else if (VIN_IS_PAL(srdata)) {
303 videoSignalReg = VIDEO_MODE_PAL | OSD_ENABLE;
304 } else {
305 // No valid input signal, fallback to default (XXX NTSC for now)
306 videoSignalReg = VIDEO_MODE_NTSC | OSD_ENABLE;
308 break;
311 if (videoSignalReg & VIDEO_MODE_PAL) { //PAL
312 maxScreenSize = VIDEO_BUFFER_CHARS_PAL;
313 } else { // NTSC
314 maxScreenSize = VIDEO_BUFFER_CHARS_NTSC;
317 // Set all rows to same charactor black/white level
318 previousBlackWhiteRegister = INVALID_PREVIOUS_REGISTER_STATE;
319 max7456Brightness(0, 2);
320 // Re-enable MAX7456 (last function call disables it)
322 // Make sure the Max7456 is enabled
323 spiWriteReg(dev, MAX7456ADD_VM0, videoSignalReg);
324 spiWriteReg(dev, MAX7456ADD_HOS, hosRegValue);
325 spiWriteReg(dev, MAX7456ADD_VOS, vosRegValue);
327 max7456SetRegisterVM1();
329 // Clear shadow to force redraw all screen
330 max7456ClearShadowBuffer();
333 void max7456PreInit(const max7456Config_t *max7456Config)
335 spiPreinitRegister(max7456Config->csTag, max7456Config->preInitOPU ? IOCFG_OUT_PP : IOCFG_IPU, 1);
338 // Here we init only CS and try to init MAX for first time.
339 // Also detect device type (MAX v.s. AT)
341 max7456InitStatus_e max7456Init(const max7456Config_t *max7456Config, const vcdProfile_t *pVcdProfile, bool cpuOverclock)
343 max7456DeviceDetected = false;
344 deviceBackgroundType = DISPLAY_BACKGROUND_TRANSPARENT;
346 // initialize all layers
347 for (unsigned i = 0; i < MAX7456_SUPPORTED_LAYER_COUNT; i++) {
348 max7456ClearLayer(i);
351 max7456HardwareReset();
353 if (!max7456Config->csTag || !spiSetBusInstance(dev, max7456Config->spiDevice)) {
354 return MAX7456_INIT_NOT_CONFIGURED;
357 dev->busType_u.spi.csnPin = IOGetByTag(max7456Config->csTag);
359 if (!IOIsFreeOrPreinit(dev->busType_u.spi.csnPin)) {
360 return MAX7456_INIT_NOT_CONFIGURED;
363 IOInit(dev->busType_u.spi.csnPin, OWNER_OSD_CS, 0);
364 IOConfigGPIO(dev->busType_u.spi.csnPin, SPI_IO_CS_CFG);
365 IOHi(dev->busType_u.spi.csnPin);
367 // Detect MAX7456 existence and device type. Do this at half the speed for safety.
369 // Detect MAX7456 and compatible device by reading OSDM (OSD Insertion MUX) register.
370 // This register is not modified in this driver, therefore ensured to remain at its default value (0x1B).
372 spiSetClkDivisor(dev, spiCalculateDivider(MAX7456_INIT_MAX_SPI_CLK_HZ));
374 // Write 0xff to conclude any current SPI transaction the MAX7456 is expecting
375 spiWrite(dev, END_STRING);
377 uint8_t osdm = spiReadRegMsk(dev, MAX7456ADD_OSDM);
379 if (osdm != 0x1B) {
380 IOConfigGPIO(dev->busType_u.spi.csnPin, IOCFG_IPU);
381 return MAX7456_INIT_NOT_FOUND;
384 // At this point, we can claim the ownership of the CS pin
385 max7456DeviceDetected = true;
386 IOInit(dev->busType_u.spi.csnPin, OWNER_OSD_CS, 0);
388 // Detect device type by writing and reading CA[8] bit at CMAL[6].
389 // This is a bit for accessing second half of character glyph storage, supported only by AT variant.
391 spiWriteReg(dev, MAX7456ADD_CMAL, (1 << 6)); // CA[8] bit
393 if (spiReadRegMsk(dev, MAX7456ADD_CMAL) & (1 << 6)) {
394 max7456DeviceType = MAX7456_DEVICE_TYPE_AT;
395 } else {
396 max7456DeviceType = MAX7456_DEVICE_TYPE_MAX;
399 #if defined(USE_OVERCLOCK)
400 // Determine SPI clock divisor based on config and the device type.
402 switch (max7456Config->clockConfig) {
403 case MAX7456_CLOCK_CONFIG_HALF:
404 max7456SpiClockDiv = spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ / 2);
405 break;
407 case MAX7456_CLOCK_CONFIG_NOMINAL:
408 default:
409 max7456SpiClockDiv = spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ);
410 break;
412 case MAX7456_CLOCK_CONFIG_DOUBLE:
413 max7456SpiClockDiv = spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ * 2);
414 break;
417 DEBUG_SET(DEBUG_MAX7456_SPICLOCK, DEBUG_MAX7456_SPICLOCK_OVERCLOCK, cpuOverclock);
418 DEBUG_SET(DEBUG_MAX7456_SPICLOCK, DEBUG_MAX7456_SPICLOCK_DEVTYPE, max7456DeviceType);
419 DEBUG_SET(DEBUG_MAX7456_SPICLOCK, DEBUG_MAX7456_SPICLOCK_DIVISOR, max7456SpiClockDiv);
420 DEBUG_SET(DEBUG_MAX7456_SPICLOCK, DEBUG_MAX7456_SPICLOCK_X100, spiCalculateClock(max7456SpiClockDiv) / 10000);
421 #else
422 UNUSED(max7456Config);
423 UNUSED(cpuOverclock);
424 max7456SpiClockDiv = spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ);
425 #endif
427 spiSetClkDivisor(dev, max7456SpiClockDiv);
429 // force soft reset on Max7456
430 spiWriteReg(dev, MAX7456ADD_VM0, MAX7456_RESET);
432 // Wait for 200us before polling for completion of reset
433 delayMicroseconds(200);
435 // Wait for reset to complete
436 while ((spiReadRegMsk(dev, MAX7456ADD_VM0) & MAX7456_RESET) != 0x00);
438 // Setup values to write to registers
439 videoSignalCfg = pVcdProfile->video_system;
440 hosRegValue = 32 - pVcdProfile->h_offset;
441 vosRegValue = 16 - pVcdProfile->v_offset;
443 // Real init will be made later when driver detect idle.
444 return MAX7456_INIT_OK;
448 * Sets inversion of black and white pixels.
450 void max7456Invert(bool invert)
452 if (invert) {
453 displayMemoryModeReg |= INVERT_PIXEL_COLOR;
454 } else {
455 displayMemoryModeReg &= ~INVERT_PIXEL_COLOR;
458 if (displayMemoryModeReg != previousInvertRegister) {
459 // clear the shadow buffer so all characters will be
460 // redrawn with the proper invert state
461 max7456ClearShadowBuffer();
462 previousInvertRegister = displayMemoryModeReg;
463 spiWriteReg(dev, MAX7456ADD_DMM, displayMemoryModeReg);
468 * Sets the brightness of black and white pixels.
470 * @param black Black brightness (0-3, 0 is darkest)
471 * @param white White brightness (0-3, 0 is darkest)
473 void max7456Brightness(uint8_t black, uint8_t white)
475 const uint8_t reg = (black << 2) | (3 - white);
477 if (reg != previousBlackWhiteRegister) {
478 previousBlackWhiteRegister = reg;
479 STATIC_DMA_DATA_AUTO uint8_t buf[32];
480 for (int i = MAX7456ADD_RB0, j = 0; i <= MAX7456ADD_RB15; i++) {
481 buf[j++] = i;
482 buf[j++] = reg;
484 spiReadWriteBuf(dev, buf, NULL, sizeof(buf));
488 void max7456ClearScreen(void)
490 max7456ClearLayer(activeLayer);
493 void max7456WriteChar(uint8_t x, uint8_t y, uint8_t c)
495 uint8_t *buffer = getActiveLayerBuffer();
496 if (x < CHARS_PER_LINE && y < VIDEO_LINES_PAL) {
497 buffer[y * CHARS_PER_LINE + x] = c;
501 void max7456Write(uint8_t x, uint8_t y, const char *buff)
503 if (y < VIDEO_LINES_PAL) {
504 uint8_t *buffer = getActiveLayerBuffer();
505 for (int i = 0; buff[i] && x + i < CHARS_PER_LINE; i++) {
506 buffer[y * CHARS_PER_LINE + x + i] = buff[i];
511 bool max7456LayerSupported(displayPortLayer_e layer)
513 if (layer == DISPLAYPORT_LAYER_FOREGROUND || layer == DISPLAYPORT_LAYER_BACKGROUND) {
514 return true;
515 } else {
516 return false;
520 bool max7456LayerSelect(displayPortLayer_e layer)
522 if (max7456LayerSupported(layer)) {
523 activeLayer = layer;
524 return true;
525 } else {
526 return false;
530 bool max7456LayerCopy(displayPortLayer_e destLayer, displayPortLayer_e sourceLayer)
532 if ((sourceLayer != destLayer) && max7456LayerSupported(sourceLayer) && max7456LayerSupported(destLayer)) {
533 memcpy(getLayerBuffer(destLayer), getLayerBuffer(sourceLayer), VIDEO_BUFFER_CHARS_PAL);
534 return true;
535 } else {
536 return false;
540 bool max7456DmaInProgress(void)
542 return spiIsBusy(dev);
545 bool max7456BuffersSynced(void)
547 for (int i = 0; i < maxScreenSize; i++) {
548 if (displayLayers[DISPLAYPORT_LAYER_FOREGROUND].buffer[i] != shadowBuffer[i]) {
549 return false;
552 return true;
555 bool max7456ReInitIfRequired(bool forceStallCheck)
557 static timeMs_t lastSigCheckMs = 0;
558 static timeMs_t videoDetectTimeMs = 0;
559 static uint16_t reInitCount = 0;
560 static timeMs_t lastStallCheckMs = MAX7456_STALL_CHECK_INTERVAL_MS / 2; // offset so that it doesn't coincide with the signal check
562 const timeMs_t nowMs = millis();
564 bool stalled = false;
565 if (forceStallCheck || (lastStallCheckMs + MAX7456_STALL_CHECK_INTERVAL_MS < nowMs)) {
566 lastStallCheckMs = nowMs;
568 // Write 0xff to conclude any current SPI transaction the MAX7456 is expecting
569 spiWrite(dev, END_STRING);
571 stalled = (spiReadRegMsk(dev, MAX7456ADD_VM0) != videoSignalReg);
574 if (stalled) {
575 max7456ReInit();
576 } else if ((videoSignalCfg == VIDEO_SYSTEM_AUTO)
577 && ((nowMs - lastSigCheckMs) > MAX7456_SIGNAL_CHECK_INTERVAL_MS)) {
579 // Write 0xff to conclude any current SPI transaction the MAX7456 is expecting
580 spiWrite(dev, END_STRING);
582 // Adjust output format based on the current input format.
584 const uint8_t videoSense = spiReadRegMsk(dev, MAX7456ADD_STAT);
586 DEBUG_SET(DEBUG_MAX7456_SIGNAL, DEBUG_MAX7456_SIGNAL_MODEREG, videoSignalReg & VIDEO_MODE_MASK);
587 DEBUG_SET(DEBUG_MAX7456_SIGNAL, DEBUG_MAX7456_SIGNAL_SENSE, videoSense & 0x7);
588 DEBUG_SET(DEBUG_MAX7456_SIGNAL, DEBUG_MAX7456_SIGNAL_ROWS, max7456GetRowsCount());
590 if (videoSense & STAT_LOS) {
591 videoDetectTimeMs = 0;
592 } else {
593 if ((VIN_IS_PAL(videoSense) && VIDEO_MODE_IS_NTSC(videoSignalReg))
594 || (VIN_IS_NTSC_alt(videoSense) && VIDEO_MODE_IS_PAL(videoSignalReg))) {
595 if (videoDetectTimeMs) {
596 if (millis() - videoDetectTimeMs > VIDEO_SIGNAL_DEBOUNCE_MS) {
597 max7456ReInit();
598 DEBUG_SET(DEBUG_MAX7456_SIGNAL, DEBUG_MAX7456_SIGNAL_REINIT, ++reInitCount);
600 } else {
601 // Wait for signal to stabilize
602 videoDetectTimeMs = millis();
607 lastSigCheckMs = nowMs;
610 return stalled;
613 // Return true if screen still being transferred
614 bool max7456DrawScreen(void)
616 static uint16_t pos = 0;
617 // This routine doesn't block so need to use static data
618 static busSegment_t segments[] = {
619 {.u.link = {NULL, NULL}, 0, true, NULL},
620 {.u.link = {NULL, NULL}, 0, true, NULL},
623 if (!fontIsLoading) {
624 uint8_t *buffer = getActiveLayerBuffer();
625 int spiBufIndex = 0;
626 int maxSpiBufStartIndex;
627 timeDelta_t maxEncodeTime;
628 bool setAddress = true;
629 bool autoInc = false;
630 int posLimit = pos + (maxScreenSize / 2);
632 maxSpiBufStartIndex = spiUseMOSI_DMA(dev) ? MAX_BYTES2SEND : MAX_BYTES2SEND_POLLED;
633 maxEncodeTime = spiUseMOSI_DMA(dev) ? MAX_ENCODE_US : MAX_ENCODE_US_POLLED;
635 // Abort for now if the bus is still busy
636 if (spiIsBusy(dev)) {
637 // Not finished yet
638 return true;
641 timeUs_t startTime = micros();
643 // Allow for an ESCAPE, a reset of DMM and a two byte MAX7456ADD_DMM command at end of buffer
644 maxSpiBufStartIndex -= 4;
646 // Initialise the transfer buffer
647 while ((spiBufIndex < maxSpiBufStartIndex) && (pos < posLimit) && (cmpTimeUs(micros(), startTime) < maxEncodeTime)) {
648 if (buffer[pos] != shadowBuffer[pos]) {
649 if (buffer[pos] == 0xff) {
650 buffer[pos] = ' ';
653 if (setAddress || !autoInc) {
654 if (buffer[pos + 1] != shadowBuffer[pos + 1]) {
655 // It's worth auto incrementing
656 spiBuf[spiBufIndex++] = MAX7456ADD_DMM;
657 spiBuf[spiBufIndex++] = displayMemoryModeReg | DMM_AUTO_INC;
658 autoInc = true;
659 } else {
660 // It's not worth auto incrementing
661 spiBuf[spiBufIndex++] = MAX7456ADD_DMM;
662 spiBuf[spiBufIndex++] = displayMemoryModeReg;
663 autoInc = false;
666 spiBuf[spiBufIndex++] = MAX7456ADD_DMAH;
667 spiBuf[spiBufIndex++] = pos >> 8;
668 spiBuf[spiBufIndex++] = MAX7456ADD_DMAL;
669 spiBuf[spiBufIndex++] = pos & 0xff;
671 setAddress = false;
674 spiBuf[spiBufIndex++] = MAX7456ADD_DMDI;
675 spiBuf[spiBufIndex++] = buffer[pos];
677 shadowBuffer[pos] = buffer[pos];
678 } else {
679 if (!setAddress) {
680 setAddress = true;
681 if (autoInc) {
682 spiBuf[spiBufIndex++] = MAX7456ADD_DMDI;
683 spiBuf[spiBufIndex++] = END_STRING;
688 if (++pos >= maxScreenSize) {
689 pos = 0;
690 break;
694 if (autoInc) {
695 if (!setAddress) {
696 spiBuf[spiBufIndex++] = MAX7456ADD_DMDI;
697 spiBuf[spiBufIndex++] = END_STRING;
700 spiBuf[spiBufIndex++] = MAX7456ADD_DMM;
701 spiBuf[spiBufIndex++] = displayMemoryModeReg;
704 if (spiBufIndex) {
705 segments[0].u.buffers.txData = spiBuf;
706 segments[0].len = spiBufIndex;
708 spiSequence(dev, &segments[0]);
710 // Non-blocking, so transfer still in progress if using DMA
714 return (pos != 0);
717 // should not be used when armed
718 void max7456RefreshAll(void)
720 max7456ReInitIfRequired(true);
721 while (max7456DrawScreen());
724 bool max7456WriteNvm(uint8_t char_address, const uint8_t *font_data)
726 if (!max7456DeviceDetected) {
727 return false;
730 // Block pending completion of any prior SPI access
731 spiWait(dev);
733 // disable display
734 fontIsLoading = true;
735 spiWriteReg(dev, MAX7456ADD_VM0, 0);
737 spiWriteReg(dev, MAX7456ADD_CMAH, char_address); // set start address high
739 for (int x = 0; x < 54; x++) {
740 spiWriteReg(dev, MAX7456ADD_CMAL, x); //set start address low
741 spiWriteReg(dev, MAX7456ADD_CMDI, font_data[x]);
742 #ifdef LED0_TOGGLE
743 LED0_TOGGLE;
744 #else
745 LED1_TOGGLE;
746 #endif
749 // Transfer 54 bytes from shadow ram to NVM
751 spiWriteReg(dev, MAX7456ADD_CMM, WRITE_NVR);
753 // Wait until bit 5 in the status register returns to 0 (12ms)
755 while ((spiReadRegMsk(dev, MAX7456ADD_STAT) & STAT_NVR_BUSY) != 0x00);
757 return true;
760 #ifdef MAX7456_NRST_PIN
761 static IO_t max7456ResetPin = IO_NONE;
762 #endif
764 void max7456HardwareReset(void)
766 #ifdef MAX7456_NRST_PIN
767 #define IO_RESET_CFG IO_CONFIG(GPIO_Mode_OUT, GPIO_Speed_2MHz, GPIO_OType_PP, GPIO_PuPd_DOWN)
769 max7456ResetPin = IOGetByTag(IO_TAG(MAX7456_NRST_PIN));
770 IOInit(max7456ResetPin, OWNER_OSD, 0);
771 IOConfigGPIO(max7456ResetPin, IO_RESET_CFG);
773 // RESET 50ms long pulse, followed by 100us pause
774 IOLo(max7456ResetPin);
775 delay(50);
776 IOHi(max7456ResetPin);
777 delayMicroseconds(100);
778 #else
779 // Allow device 50ms to powerup
780 delay(50);
781 #endif
784 bool max7456IsDeviceDetected(void)
786 return max7456DeviceDetected;
789 void max7456SetBackgroundType(displayPortBackground_e backgroundType)
791 deviceBackgroundType = backgroundType;
793 max7456SetRegisterVM1();
796 #endif // USE_MAX7456