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)
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/>.
29 #include "build/debug.h"
31 #include "pg/max7456.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
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
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
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))
120 #define DMM_AUTO_INC 0x01
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
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
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
];
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
;
197 static volatile bool max7456ActiveDma
= false;
199 uint16_t maxScreenSize
= VIDEO_BUFFER_CHARS_PAL
;
201 // We write everything to the active layer and then compare
202 // it with shadowBuffer to update only changed chars.
203 // This solution is faster then redrawing entire screen.
205 static uint8_t shadowBuffer
[VIDEO_BUFFER_CHARS_PAL
];
207 //Max bytes to update in one call to max7456DrawScreen()
209 #define MAX_BYTES2SEND 250
210 #define MAX_BYTES2SEND_POLLED 12
211 #define MAX_ENCODE_US 20
212 #define MAX_ENCODE_US_POLLED 10
214 static DMA_DATA
uint8_t spiBuf
[MAX_BYTES2SEND
];
216 static uint8_t videoSignalCfg
;
217 static uint8_t videoSignalReg
= OSD_ENABLE
; // OSD_ENABLE required to trigger first ReInit
218 static uint8_t displayMemoryModeReg
= 0;
220 static uint8_t hosRegValue
; // HOS (Horizontal offset register) value
221 static uint8_t vosRegValue
; // VOS (Vertical offset register) value
223 static bool fontIsLoading
= false;
225 static uint8_t max7456DeviceType
;
227 static displayPortBackground_e deviceBackgroundType
= DISPLAY_BACKGROUND_TRANSPARENT
;
229 // previous states initialized outside the valid range to force update on first call
230 #define INVALID_PREVIOUS_REGISTER_STATE 255
231 static uint8_t previousBlackWhiteRegister
= INVALID_PREVIOUS_REGISTER_STATE
;
232 static uint8_t previousInvertRegister
= INVALID_PREVIOUS_REGISTER_STATE
;
234 static uint8_t *getLayerBuffer(displayPortLayer_e layer
)
236 return displayLayers
[layer
].buffer
;
239 static uint8_t *getActiveLayerBuffer(void)
241 return getLayerBuffer(activeLayer
);
244 static void max7456SetRegisterVM1(void)
246 uint8_t backgroundGray
= BACKGROUND_BRIGHTNESS_28
; // this is the device default background gray level
247 uint8_t vm1Register
= BLINK_TIME_1
| BLINK_DUTY_CYCLE_75_25
; // device defaults
248 if (deviceBackgroundType
!= DISPLAY_BACKGROUND_TRANSPARENT
) {
249 vm1Register
|= BACKGROUND_MODE_GRAY
;
250 switch (deviceBackgroundType
) {
251 case DISPLAY_BACKGROUND_BLACK
:
252 backgroundGray
= BACKGROUND_BRIGHTNESS_0
;
254 case DISPLAY_BACKGROUND_LTGRAY
:
255 backgroundGray
= BACKGROUND_BRIGHTNESS_49
;
257 case DISPLAY_BACKGROUND_GRAY
:
259 backgroundGray
= BACKGROUND_BRIGHTNESS_28
;
263 vm1Register
|= (backgroundGray
<< 4);
264 spiWriteReg(dev
, MAX7456ADD_VM1
, vm1Register
);
267 uint8_t max7456GetRowsCount(void)
269 return (videoSignalReg
& VIDEO_MODE_PAL
) ? VIDEO_LINES_PAL
: VIDEO_LINES_NTSC
;
272 // When clearing the shadow buffer we fill with 0 so that the characters will
273 // be flagged as changed when compared to the 0x20 used in the layer buffers.
274 static void max7456ClearShadowBuffer(void)
276 memset(shadowBuffer
, 0, maxScreenSize
);
279 // Buffer is filled with the whitespace character (0x20)
280 static void max7456ClearLayer(displayPortLayer_e layer
)
282 memset(getLayerBuffer(layer
), 0x20, VIDEO_BUFFER_CHARS_PAL
);
285 void max7456ReInit(void)
289 switch (videoSignalCfg
) {
290 case VIDEO_SYSTEM_PAL
:
291 videoSignalReg
= VIDEO_MODE_PAL
| OSD_ENABLE
;
294 case VIDEO_SYSTEM_NTSC
:
295 videoSignalReg
= VIDEO_MODE_NTSC
| OSD_ENABLE
;
298 case VIDEO_SYSTEM_AUTO
:
299 srdata
= spiReadRegMsk(dev
, MAX7456ADD_STAT
);
301 if (VIN_IS_NTSC(srdata
)) {
302 videoSignalReg
= VIDEO_MODE_NTSC
| OSD_ENABLE
;
303 } else if (VIN_IS_PAL(srdata
)) {
304 videoSignalReg
= VIDEO_MODE_PAL
| OSD_ENABLE
;
306 // No valid input signal, fallback to default (PAL)
307 videoSignalReg
= VIDEO_MODE_PAL
| OSD_ENABLE
;
312 if (videoSignalReg
& VIDEO_MODE_PAL
) { //PAL
313 maxScreenSize
= VIDEO_BUFFER_CHARS_PAL
;
315 maxScreenSize
= VIDEO_BUFFER_CHARS_NTSC
;
318 // Set all rows to same charactor black/white level
319 previousBlackWhiteRegister
= INVALID_PREVIOUS_REGISTER_STATE
;
320 max7456Brightness(0, 2);
321 // Re-enable MAX7456 (last function call disables it)
323 // Make sure the Max7456 is enabled
324 spiWriteReg(dev
, MAX7456ADD_VM0
, videoSignalReg
);
325 spiWriteReg(dev
, MAX7456ADD_HOS
, hosRegValue
);
326 spiWriteReg(dev
, MAX7456ADD_VOS
, vosRegValue
);
328 max7456SetRegisterVM1();
330 // Clear shadow to force redraw all screen
331 max7456ClearShadowBuffer();
334 void max7456PreInit(const max7456Config_t
*max7456Config
)
336 spiPreinitRegister(max7456Config
->csTag
, max7456Config
->preInitOPU
? IOCFG_OUT_PP
: IOCFG_IPU
, 1);
339 // Here we init only CS and try to init MAX for first time.
340 // Also detect device type (MAX v.s. AT)
342 max7456InitStatus_e
max7456Init(const max7456Config_t
*max7456Config
, const vcdProfile_t
*pVcdProfile
, bool cpuOverclock
)
344 max7456DeviceDetected
= false;
345 deviceBackgroundType
= DISPLAY_BACKGROUND_TRANSPARENT
;
347 // initialize all layers
348 for (unsigned i
= 0; i
< MAX7456_SUPPORTED_LAYER_COUNT
; i
++) {
349 max7456ClearLayer(i
);
352 max7456HardwareReset();
354 if (!max7456Config
->csTag
|| !spiSetBusInstance(dev
, max7456Config
->spiDevice
)) {
355 return MAX7456_INIT_NOT_CONFIGURED
;
358 dev
->busType_u
.spi
.csnPin
= IOGetByTag(max7456Config
->csTag
);
360 if (!IOIsFreeOrPreinit(dev
->busType_u
.spi
.csnPin
)) {
361 return MAX7456_INIT_NOT_CONFIGURED
;
364 IOInit(dev
->busType_u
.spi
.csnPin
, OWNER_OSD_CS
, 0);
365 IOConfigGPIO(dev
->busType_u
.spi
.csnPin
, SPI_IO_CS_CFG
);
366 IOHi(dev
->busType_u
.spi
.csnPin
);
368 // Detect MAX7456 existence and device type. Do this at half the speed for safety.
370 // Detect MAX7456 and compatible device by reading OSDM (OSD Insertion MUX) register.
371 // This register is not modified in this driver, therefore ensured to remain at its default value (0x1B).
373 spiSetClkDivisor(dev
, spiCalculateDivider(MAX7456_INIT_MAX_SPI_CLK_HZ
));
375 // Write 0xff to conclude any current SPI transaction the MAX7456 is expecting
376 spiWrite(dev
, END_STRING
);
378 uint8_t osdm
= spiReadRegMsk(dev
, MAX7456ADD_OSDM
);
381 IOConfigGPIO(dev
->busType_u
.spi
.csnPin
, IOCFG_IPU
);
382 return MAX7456_INIT_NOT_FOUND
;
385 // At this point, we can claim the ownership of the CS pin
386 max7456DeviceDetected
= true;
387 IOInit(dev
->busType_u
.spi
.csnPin
, OWNER_OSD_CS
, 0);
389 // Detect device type by writing and reading CA[8] bit at CMAL[6].
390 // This is a bit for accessing second half of character glyph storage, supported only by AT variant.
392 spiWriteReg(dev
, MAX7456ADD_CMAL
, (1 << 6)); // CA[8] bit
394 if (spiReadRegMsk(dev
, MAX7456ADD_CMAL
) & (1 << 6)) {
395 max7456DeviceType
= MAX7456_DEVICE_TYPE_AT
;
397 max7456DeviceType
= MAX7456_DEVICE_TYPE_MAX
;
400 #if defined(USE_OVERCLOCK)
401 // Determine SPI clock divisor based on config and the device type.
403 switch (max7456Config
->clockConfig
) {
404 case MAX7456_CLOCK_CONFIG_HALF
:
405 max7456SpiClockDiv
= spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ
/ 2);
408 case MAX7456_CLOCK_CONFIG_NOMINAL
:
410 max7456SpiClockDiv
= spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ
);
413 case MAX7456_CLOCK_CONFIG_DOUBLE
:
414 max7456SpiClockDiv
= spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ
* 2);
418 DEBUG_SET(DEBUG_MAX7456_SPICLOCK
, DEBUG_MAX7456_SPICLOCK_OVERCLOCK
, cpuOverclock
);
419 DEBUG_SET(DEBUG_MAX7456_SPICLOCK
, DEBUG_MAX7456_SPICLOCK_DEVTYPE
, max7456DeviceType
);
420 DEBUG_SET(DEBUG_MAX7456_SPICLOCK
, DEBUG_MAX7456_SPICLOCK_DIVISOR
, max7456SpiClockDiv
);
421 DEBUG_SET(DEBUG_MAX7456_SPICLOCK
, DEBUG_MAX7456_SPICLOCK_X100
, spiCalculateClock(max7456SpiClockDiv
) / 10000);
423 UNUSED(max7456Config
);
424 UNUSED(cpuOverclock
);
425 max7456SpiClockDiv
= spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ
);
428 spiSetClkDivisor(dev
, max7456SpiClockDiv
);
430 // force soft reset on Max7456
431 spiWriteReg(dev
, MAX7456ADD_VM0
, MAX7456_RESET
);
433 // Wait for 200us before polling for completion of reset
434 delayMicroseconds(200);
436 // Wait for reset to complete
437 while ((spiReadRegMsk(dev
, MAX7456ADD_VM0
) & MAX7456_RESET
) != 0x00);
439 // Setup values to write to registers
440 videoSignalCfg
= pVcdProfile
->video_system
;
441 hosRegValue
= 32 - pVcdProfile
->h_offset
;
442 vosRegValue
= 16 - pVcdProfile
->v_offset
;
444 // Real init will be made later when driver detect idle.
445 return MAX7456_INIT_OK
;
449 * Sets inversion of black and white pixels.
451 void max7456Invert(bool invert
)
454 displayMemoryModeReg
|= INVERT_PIXEL_COLOR
;
456 displayMemoryModeReg
&= ~INVERT_PIXEL_COLOR
;
459 if (displayMemoryModeReg
!= previousInvertRegister
) {
460 // clear the shadow buffer so all characters will be
461 // redrawn with the proper invert state
462 max7456ClearShadowBuffer();
463 previousInvertRegister
= displayMemoryModeReg
;
464 spiWriteReg(dev
, MAX7456ADD_DMM
, displayMemoryModeReg
);
469 * Sets the brightness of black and white pixels.
471 * @param black Black brightness (0-3, 0 is darkest)
472 * @param white White brightness (0-3, 0 is darkest)
474 void max7456Brightness(uint8_t black
, uint8_t white
)
476 const uint8_t reg
= (black
<< 2) | (3 - white
);
478 if (reg
!= previousBlackWhiteRegister
) {
479 previousBlackWhiteRegister
= reg
;
480 STATIC_DMA_DATA_AUTO
uint8_t buf
[32];
481 for (int i
= MAX7456ADD_RB0
, j
= 0; i
<= MAX7456ADD_RB15
; i
++) {
485 spiReadWriteBuf(dev
, buf
, NULL
, sizeof(buf
));
489 void max7456ClearScreen(void)
491 max7456ClearLayer(activeLayer
);
494 void max7456WriteChar(uint8_t x
, uint8_t y
, uint8_t c
)
496 uint8_t *buffer
= getActiveLayerBuffer();
497 if (x
< CHARS_PER_LINE
&& y
< VIDEO_LINES_PAL
) {
498 buffer
[y
* CHARS_PER_LINE
+ x
] = c
;
502 void max7456Write(uint8_t x
, uint8_t y
, const char *text
)
504 if (y
< VIDEO_LINES_PAL
) {
505 uint8_t *buffer
= getActiveLayerBuffer();
506 const uint32_t bufferYOffset
= y
* CHARS_PER_LINE
;
507 for (int i
= 0, bufferXOffset
= x
; text
[i
] && bufferXOffset
< CHARS_PER_LINE
; i
++, bufferXOffset
++) {
508 buffer
[bufferYOffset
+ bufferXOffset
] = text
[i
];
513 bool max7456LayerSupported(displayPortLayer_e layer
)
515 if (layer
== DISPLAYPORT_LAYER_FOREGROUND
|| layer
== DISPLAYPORT_LAYER_BACKGROUND
) {
522 bool max7456LayerSelect(displayPortLayer_e layer
)
524 if (max7456LayerSupported(layer
)) {
532 bool max7456LayerCopy(displayPortLayer_e destLayer
, displayPortLayer_e sourceLayer
)
534 if ((sourceLayer
!= destLayer
) && max7456LayerSupported(sourceLayer
) && max7456LayerSupported(destLayer
)) {
535 memcpy(getLayerBuffer(destLayer
), getLayerBuffer(sourceLayer
), VIDEO_BUFFER_CHARS_PAL
);
542 bool max7456DmaInProgress(void)
544 return max7456ActiveDma
;
547 bool max7456BuffersSynced(void)
549 for (int i
= 0; i
< maxScreenSize
; i
++) {
550 if (displayLayers
[DISPLAYPORT_LAYER_FOREGROUND
].buffer
[i
] != shadowBuffer
[i
]) {
557 bool max7456ReInitIfRequired(bool forceStallCheck
)
559 static timeMs_t lastSigCheckMs
= 0;
560 static timeMs_t videoDetectTimeMs
= 0;
561 static uint16_t reInitCount
= 0;
562 static timeMs_t lastStallCheckMs
= MAX7456_STALL_CHECK_INTERVAL_MS
/ 2; // offset so that it doesn't coincide with the signal check
564 const timeMs_t nowMs
= millis();
566 bool stalled
= false;
567 if (forceStallCheck
|| (lastStallCheckMs
+ MAX7456_STALL_CHECK_INTERVAL_MS
< nowMs
)) {
568 lastStallCheckMs
= nowMs
;
570 // Write 0xff to conclude any current SPI transaction the MAX7456 is expecting
571 spiWrite(dev
, END_STRING
);
573 stalled
= (spiReadRegMsk(dev
, MAX7456ADD_VM0
) != videoSignalReg
);
578 } else if ((videoSignalCfg
== VIDEO_SYSTEM_AUTO
)
579 && ((nowMs
- lastSigCheckMs
) > MAX7456_SIGNAL_CHECK_INTERVAL_MS
)) {
581 // Write 0xff to conclude any current SPI transaction the MAX7456 is expecting
582 spiWrite(dev
, END_STRING
);
584 // Adjust output format based on the current input format.
586 const uint8_t videoSense
= spiReadRegMsk(dev
, MAX7456ADD_STAT
);
588 DEBUG_SET(DEBUG_MAX7456_SIGNAL
, DEBUG_MAX7456_SIGNAL_MODEREG
, videoSignalReg
& VIDEO_MODE_MASK
);
589 DEBUG_SET(DEBUG_MAX7456_SIGNAL
, DEBUG_MAX7456_SIGNAL_SENSE
, videoSense
& 0x7);
590 DEBUG_SET(DEBUG_MAX7456_SIGNAL
, DEBUG_MAX7456_SIGNAL_ROWS
, max7456GetRowsCount());
592 if (videoSense
& STAT_LOS
) {
593 videoDetectTimeMs
= 0;
595 if ((VIN_IS_PAL(videoSense
) && VIDEO_MODE_IS_NTSC(videoSignalReg
))
596 || (VIN_IS_NTSC_alt(videoSense
) && VIDEO_MODE_IS_PAL(videoSignalReg
))) {
597 if (videoDetectTimeMs
) {
598 if (millis() - videoDetectTimeMs
> VIDEO_SIGNAL_DEBOUNCE_MS
) {
600 DEBUG_SET(DEBUG_MAX7456_SIGNAL
, DEBUG_MAX7456_SIGNAL_REINIT
, ++reInitCount
);
603 // Wait for signal to stabilize
604 videoDetectTimeMs
= millis();
609 lastSigCheckMs
= nowMs
;
615 // Called in ISR context
616 busStatus_e
max7456_callbackReady(uint32_t arg
)
620 max7456ActiveDma
= false;
625 // Return true if screen still being transferred
626 bool max7456DrawScreen(void)
628 static uint16_t pos
= 0;
629 // This routine doesn't block so need to use static data
630 static busSegment_t segments
[] = {
631 {.u
.link
= {NULL
, NULL
}, 0, true, max7456_callbackReady
},
632 {.u
.link
= {NULL
, NULL
}, 0, true, NULL
},
635 if (!fontIsLoading
) {
636 uint8_t *buffer
= getActiveLayerBuffer();
638 int maxSpiBufStartIndex
;
639 timeDelta_t maxEncodeTime
;
640 bool setAddress
= true;
641 bool autoInc
= false;
642 int posLimit
= pos
+ (maxScreenSize
/ 2);
644 maxSpiBufStartIndex
= spiUseSDO_DMA(dev
) ? MAX_BYTES2SEND
: MAX_BYTES2SEND_POLLED
;
645 maxEncodeTime
= spiUseSDO_DMA(dev
) ? MAX_ENCODE_US
: MAX_ENCODE_US_POLLED
;
647 // Abort for now if the bus is still busy
648 if (spiIsBusy(dev
)) {
653 timeUs_t startTime
= micros();
655 // Allow for an ESCAPE, a reset of DMM and a two byte MAX7456ADD_DMM command at end of buffer
656 maxSpiBufStartIndex
-= 4;
658 // Initialise the transfer buffer
659 while ((spiBufIndex
< maxSpiBufStartIndex
) && (pos
< posLimit
) && (cmpTimeUs(micros(), startTime
) < maxEncodeTime
)) {
660 if (buffer
[pos
] != shadowBuffer
[pos
]) {
661 if (buffer
[pos
] == 0xff) {
665 if (setAddress
|| !autoInc
) {
666 if (buffer
[pos
+ 1] != shadowBuffer
[pos
+ 1]) {
667 // It's worth auto incrementing
668 spiBuf
[spiBufIndex
++] = MAX7456ADD_DMM
;
669 spiBuf
[spiBufIndex
++] = displayMemoryModeReg
| DMM_AUTO_INC
;
672 // It's not worth auto incrementing
673 spiBuf
[spiBufIndex
++] = MAX7456ADD_DMM
;
674 spiBuf
[spiBufIndex
++] = displayMemoryModeReg
;
678 spiBuf
[spiBufIndex
++] = MAX7456ADD_DMAH
;
679 spiBuf
[spiBufIndex
++] = pos
>> 8;
680 spiBuf
[spiBufIndex
++] = MAX7456ADD_DMAL
;
681 spiBuf
[spiBufIndex
++] = pos
& 0xff;
686 spiBuf
[spiBufIndex
++] = MAX7456ADD_DMDI
;
687 spiBuf
[spiBufIndex
++] = buffer
[pos
];
689 shadowBuffer
[pos
] = buffer
[pos
];
694 spiBuf
[spiBufIndex
++] = MAX7456ADD_DMDI
;
695 spiBuf
[spiBufIndex
++] = END_STRING
;
700 if (++pos
>= maxScreenSize
) {
708 spiBuf
[spiBufIndex
++] = MAX7456ADD_DMDI
;
709 spiBuf
[spiBufIndex
++] = END_STRING
;
712 spiBuf
[spiBufIndex
++] = MAX7456ADD_DMM
;
713 spiBuf
[spiBufIndex
++] = displayMemoryModeReg
;
717 segments
[0].u
.buffers
.txData
= spiBuf
;
718 segments
[0].len
= spiBufIndex
;
720 max7456ActiveDma
= true;
722 spiSequence(dev
, &segments
[0]);
724 // Non-blocking, so transfer still in progress if using DMA
731 // should not be used when armed
732 void max7456RefreshAll(void)
734 max7456ReInitIfRequired(true);
735 while (max7456DrawScreen());
738 bool max7456WriteNvm(uint8_t char_address
, const uint8_t *font_data
)
740 if (!max7456DeviceDetected
) {
744 // Block pending completion of any prior SPI access
748 fontIsLoading
= true;
749 spiWriteReg(dev
, MAX7456ADD_VM0
, 0);
751 spiWriteReg(dev
, MAX7456ADD_CMAH
, char_address
); // set start address high
753 for (int x
= 0; x
< 54; x
++) {
754 spiWriteReg(dev
, MAX7456ADD_CMAL
, x
); //set start address low
755 spiWriteReg(dev
, MAX7456ADD_CMDI
, font_data
[x
]);
763 // Transfer 54 bytes from shadow ram to NVM
765 spiWriteReg(dev
, MAX7456ADD_CMM
, WRITE_NVR
);
767 // Wait until bit 5 in the status register returns to 0 (12ms)
769 while ((spiReadRegMsk(dev
, MAX7456ADD_STAT
) & STAT_NVR_BUSY
) != 0x00);
774 #ifdef MAX7456_NRST_PIN
775 static IO_t max7456ResetPin
= IO_NONE
;
778 void max7456HardwareReset(void)
780 #ifdef MAX7456_NRST_PIN
781 #define IO_RESET_CFG IO_CONFIG(GPIO_Mode_OUT, GPIO_Speed_2MHz, GPIO_OType_PP, GPIO_PuPd_DOWN)
783 max7456ResetPin
= IOGetByTag(IO_TAG(MAX7456_NRST_PIN
));
784 IOInit(max7456ResetPin
, OWNER_OSD
, 0);
785 IOConfigGPIO(max7456ResetPin
, IO_RESET_CFG
);
787 // RESET 50ms long pulse, followed by 100us pause
788 IOLo(max7456ResetPin
);
790 IOHi(max7456ResetPin
);
791 delayMicroseconds(100);
793 // Allow device 50ms to powerup
798 bool max7456IsDeviceDetected(void)
800 return max7456DeviceDetected
;
803 void max7456SetBackgroundType(displayPortBackground_e backgroundType
)
805 deviceBackgroundType
= backgroundType
;
807 max7456SetRegisterVM1();
810 #endif // USE_MAX7456