dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_max7456.c
Go to the documentation of this file.
1 
13 /*
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  * for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, see <http://www.gnu.org/licenses/>
26  *
27  * Additional note on redistribution: The copyright and license notices above
28  * must be maintained in each individual source file that is a derivative work
29  * of this source file; otherwise redistribution is prohibited.
30  */
31 
32 /* Imported from MultiOSD (https://github.com/UncleRus/MultiOSD/)
33  * Altered for use on STM32 Flight controllers by dRonin and fused with
34  * some existing MAX7456 code we had.
35  * Copyright (C) dRonin 2016
36  */
37 
38 /*
39  * This file is part of MultiOSD <https://github.com/UncleRus/MultiOSD>
40  *
41  * MultiOSD is free software: you can redistribute it and/or modify
42  * it under the terms of the GNU General Public License as published by
43  * the Free Software Foundation, either version 3 of the License, or
44  * (at your option) any later version.
45  *
46  * This program is distributed in the hope that it will be useful,
47  * but WITHOUT ANY WARRANTY; without even the implied warranty of
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49  * GNU General Public License for more details.
50  *
51  * You should have received a copy of the GNU General Public License
52  * along with this program. If not, see <http://www.gnu.org/licenses/>.
53  */
54 
55 #include <pios.h>
56 
57 #ifdef PIOS_INCLUDE_MAX7456
58 #include <pios_delay.h>
59 #include <pios_thread.h>
60 
61 #include "pios_max7456.h"
62 #include "pios_max7456_priv.h"
63 
64 #define MAX7456_DEFAULT_BRIGHTNESS 0x00
65 
66 #define MAX7456_MASK_PAL MAX7456_VM0_VSS_MASK
67 #define MAX7456_MASK_NTSC 0x00
68 
69 #define SYNC_INTERVAL_NTSC 33366
70 #define SYNC_INTERVAL_PAL 40000
71 
73 
74 struct max7456_dev_s {
75 #define MAX7456_MAGIC 0x36353437 /* '7456' */
76  uint32_t magic;
77  pios_spi_t spi_id;
78  uint32_t slave_id;
79 
80  uint8_t mode, right, bottom, hcenter, vcenter;
81 
82  uint8_t mask;
83  bool opened;
84 
85  bool force_mode;
86  uint8_t det_mode_fallback;
87 
88  uint32_t next_sync_expected;
89 };
90 
91 static bool poll_vsync_spi (max7456_dev_t dev);
92 
93 /* Max7456 says 100ns period (10MHz) is OK. But it may be off-board in
94  * some circumstances, so let's not push our luck.
95  */
96 #define MAX7456_SPI_SPEED 9000000
97 
98 static inline void chip_select(max7456_dev_t dev)
99 {
100  PIOS_SPI_ClaimBus(dev->spi_id);
101 
102  PIOS_SPI_RC_PinSet(dev->spi_id, dev->slave_id, false);
103 
104  PIOS_SPI_SetClockSpeed(dev->spi_id, MAX7456_SPI_SPEED);
105 }
106 
107 static inline void chip_unselect(max7456_dev_t dev)
108 {
109  PIOS_SPI_RC_PinSet(dev->spi_id, dev->slave_id, true);
110 
111  PIOS_SPI_ReleaseBus(dev->spi_id);
112 }
113 
114 // Write VM0[3] = 1 to enable the display of the OSD image.
115 // mode | autosync | vsync on | enable OSD
116 #define enable_osd(dev) do { write_register_sel(dev, MAX7456_REG_VM0, dev->mask | MAX7456_VM0_OSD_MASK | MAX7456_VM0_VS_MASK); } while (0)
117 #define disable_osd(dev) do { write_register_sel(dev, MAX7456_REG_VM0, 0); } while (0)
118 
119 // Would be nice to collapse these to single transactions through the spi
120 // layer.
121 static uint8_t read_register(max7456_dev_t dev, uint8_t reg)
122 {
123  PIOS_SPI_TransferByte(dev->spi_id, reg | 0x80);
124  return PIOS_SPI_TransferByte(dev->spi_id, 0xff);
125 }
126 
127 static uint8_t read_register_sel(max7456_dev_t dev, uint8_t reg)
128 {
129  uint8_t ret;
130 
131  chip_select(dev);
132  ret = read_register(dev, reg);
133  chip_unselect(dev);
134 
135  return ret;
136 }
137 
138 static void write_register(max7456_dev_t dev, uint8_t reg, uint8_t val)
139 {
140  PIOS_SPI_TransferByte(dev->spi_id, reg);
141  PIOS_SPI_TransferByte(dev->spi_id, val);
142 }
143 
144 static void write_register_sel(max7456_dev_t dev, uint8_t reg, uint8_t val)
145 {
146  chip_select(dev);
147  write_register(dev, reg, val);
148  chip_unselect(dev);
149 }
150 
151 static inline void set_mode(max7456_dev_t dev, uint8_t value)
152 {
153  dev->mode = value;
154 
155  uint8_t vm0;
156 
157  vm0 = read_register_sel(dev, MAX7456_REG_VM0);
158 
159  if (value == MAX7456_MODE_NTSC)
160  {
161  dev->mask = MAX7456_MASK_NTSC;
162  dev->right = MAX7456_NTSC_COLUMNS - 1;
163  dev->bottom = MAX7456_NTSC_ROWS - 1;
164  dev->hcenter = MAX7456_NTSC_HCENTER;
165  dev->vcenter = MAX7456_NTSC_VCENTER;
166 
168  } else {
169  PIOS_Assert(value == MAX7456_MODE_PAL);
170 
171  dev->mask = MAX7456_MASK_PAL;
172  dev->right = MAX7456_PAL_COLUMNS - 1;
173  dev->bottom = MAX7456_PAL_ROWS - 1;
174  dev->hcenter = MAX7456_PAL_HCENTER;
175  dev->vcenter = MAX7456_PAL_VCENTER;
176 
178  }
179 
180  write_register_sel(dev, MAX7456_REG_VM0, vm0);
181 }
182 
183 static inline void detect_mode(max7456_dev_t dev)
184 {
185  if (!dev->force_mode) {
186  // read STAT and auto detect video mode PAL/NTSC
187  uint8_t stat = read_register_sel(dev, MAX7456_REG_STAT);
188 
190  {
192  return;
193  }
194 
196  {
198  return;
199  }
200  }
201 
202  // Give up / guess as instructed
203  set_mode(dev, dev->det_mode_fallback);
204 }
205 
206 static void auto_black_level(max7456_dev_t dev, bool val)
207 {
208  uint8_t osdbl = read_register_sel(dev, MAX7456_REG_OSDBL);
209 
210  if (val)
212  else
214 
215  write_register_sel(dev, MAX7456_REG_OSDBL, osdbl);
216 }
217 
219 {
220  PIOS_Assert(dev->magic == MAX7456_MAGIC);
221 
222  uint32_t now = PIOS_DELAY_GetuS();
223  uint32_t sync_interval =
224  (dev->mode == MAX7456_MODE_NTSC) ? SYNC_INTERVAL_NTSC :
225  SYNC_INTERVAL_PAL;
226 
227  if (now > dev->next_sync_expected) {
228  // Oh. we maybe missed it. Update...
229  dev->next_sync_expected += sync_interval;
230  }
231 
232  if (now > dev->next_sync_expected) {
233  // We're *REALLY* late
234  dev->next_sync_expected = now + sync_interval;
235  }
236 
237  while (!poll_vsync_spi(dev)) {
238  now = PIOS_DELAY_GetuS();
239 
240  if (now + 200 > (dev->next_sync_expected + sync_interval)) {
241  /* We missed at least one vsync in this loop.
242  * Draw open-loop, hoping we're at about the right
243  * time.
244  */
245  break;
246  } else {
248  }
249  }
250 
251  dev->next_sync_expected = now + sync_interval;
252 }
253 
254 static void reset_hard(max7456_dev_t dev)
255 {
256  dev->next_sync_expected = 0;
257 
258  write_register_sel(dev, MAX7456_REG_VM0, MAX7456_VM0_SRB_MASK);
259 
260  PIOS_DELAY_WaituS(100);
261 
262  while(read_register_sel(dev, MAX7456_REG_VM0) & MAX7456_VM0_SRB_MASK);
263 
264  // Detect video mode
265  detect_mode(dev);
266 
267  auto_black_level(dev, true);
268 
269  uint8_t vm1 = 0;
274 
275  write_register_sel(dev, MAX7456_REG_VM1, vm1);
276 
277  enable_osd(dev);
278 
279  // set all rows to the same character brightness black/white level
280  uint8_t brightness = MAX7456_DEFAULT_BRIGHTNESS;
281 
282  for (uint8_t r = MAX7456_REG_RB0; r < MAX7456_REG_RB15; r++) {
283  write_register_sel(dev, r, brightness);
284  }
285 
286  PIOS_MAX7456_clear(dev);
287 }
288 
289 int PIOS_MAX7456_init(max7456_dev_t *dev_out,
290  pios_spi_t spi_handle, uint32_t slave_idx)
291 {
292  // Reset
293  max7456_dev_t dev = PIOS_malloc(sizeof(*dev));
294 
295  bzero(dev, sizeof(*dev));
296  dev->magic = MAX7456_MAGIC;
297  dev->spi_id = spi_handle;
298  dev->slave_id = slave_idx;
299  dev->force_mode = false;
300  dev->det_mode_fallback = MAX7456_MODE_PAL;
301 
302  reset_hard(dev);
303 
304  *dev_out = dev;
305 
306  return 0;
307 }
308 
309 void PIOS_MAX7456_set_mode(max7456_dev_t dev, bool force, uint8_t fallback)
310 {
311  dev->force_mode = force;
312  dev->det_mode_fallback = fallback;
313 
314  detect_mode(dev);
315 }
316 
318 {
319  PIOS_Assert(dev->magic == MAX7456_MAGIC);
320  PIOS_Assert(!dev->opened);
321 
322  uint8_t dmm;
323  dmm = read_register_sel(dev, MAX7456_REG_DMM);
324 
326 
327  write_register_sel(dev, MAX7456_REG_DMM, dmm);
328  PIOS_DELAY_WaituS(30);
329 
330  while (MAX7456_DMM_CLR_R(dmm) != MAX7456_DMM_CLR_READY) {
331  dmm = read_register_sel(dev, MAX7456_REG_DMM);
332  }
333 }
334 
335 void PIOS_MAX7456_upload_char (max7456_dev_t dev, uint8_t char_index,
336  const uint8_t *data)
337 {
338  PIOS_Assert(dev->magic == MAX7456_MAGIC);
339  disable_osd(dev);
340 
341  PIOS_DELAY_WaituS(10);
342 
343  // Write CMAH[7:0] = xxH to select the character (0–255) to be written
344  write_register_sel(dev, MAX7456_REG_CMAH, char_index);
345 
346  for (uint8_t i = 0; i < 54; i ++)
347  {
348  // Write CMAL[7:0] = xxH to select the 4-pixel byte (0–53) in the character to be written
349  write_register_sel(dev, MAX7456_REG_CMAL, i);
350  // Write CMDI[7:0] = xxH to set the pixel values of the selected part of the character
351  write_register_sel(dev, MAX7456_REG_CMDI, data[i]);
352  }
353 
354  // Write CMM[7:0] = 1010xxxx to write to the NVM array from the shadow RAM
355  write_register_sel(dev, MAX7456_REG_CMM, MAX7456_CMM_WRITE_NVM);
356 
357  /*
358  The character memory is busy for approximately 12ms during this operation.
359  STAT[5] can be read to verify that the NVM writing process is complete.
360  */
361 
362  while (MAX7456_STAT_CHMEM_R(
363  read_register_sel(dev, MAX7456_REG_STAT)) ==
366  }
367 
368  enable_osd(dev);
369 }
370 
371 void PIOS_MAX7456_download_char (max7456_dev_t dev, uint8_t char_index,
372  uint8_t *data)
373 {
374  PIOS_Assert(dev->magic == MAX7456_MAGIC);
375 
376  disable_osd(dev);
377 
378  PIOS_DELAY_WaituS(10);
379 
380  // Write CMAH[7:0] = xxH to select the character (0–255) to be read
381  write_register_sel(dev, MAX7456_REG_CMAH, char_index);
382 
383  // Write CMM[7:0] = 0101xxxx to read the character data from the NVM to the shadow RAM
384  write_register_sel(dev, MAX7456_REG_CMM, MAX7456_CMM_READ_NVM);
385  /*
386  * The character memory is busy for approximately 12ms during this operation.
387  * The Character Memory Mode register is cleared and STAT[5] is reset to 0 after
388  * the write operation has been completed.
389  *
390  * note: MPL: I don't see this being required in the datasheet for
391  * read operations. But it doesnt hurt.
392  */
393  while (MAX7456_STAT_CHMEM_R(
394  read_register_sel(dev, MAX7456_REG_STAT)) ==
397  }
398 
399  for (uint8_t i = 0; i < 54; i ++)
400  {
401  // Write CMAL[7:0] = xxH to select the 4-pixel byte (0–53) in the character to be read
402  write_register_sel(dev, MAX7456_REG_CMAL, i);
403  // Write CMDI[7:0] = xxH to get the pixel values of the selected part of the character
404  data[i] = read_register_sel(dev, MAX7456_REG_CMDO);
405  }
406 
407  enable_osd(dev);
408 }
409 
410 /* Assumes you have already selected */
411 static inline void set_offset (max7456_dev_t dev, uint8_t col, uint8_t row)
412 {
413  PIOS_Assert(dev->magic == MAX7456_MAGIC);
414 
415  if (col > 29) {
416  // Still will wrap to next line...
417  col = 29;
418  }
419 
420  uint16_t offset = (row * 30 + col) & 0x1ff;
421  write_register(dev, MAX7456_REG_DMAH, offset >> 8);
422  write_register(dev, MAX7456_REG_DMAL,(uint8_t) offset);
423 }
424 
425 static bool poll_vsync_spi (max7456_dev_t dev)
426 {
427  uint8_t status = read_register_sel(dev, MAX7456_REG_STAT);
428 
430 }
431 
433  uint8_t col, uint8_t row, uint8_t chr, uint8_t attr)
434 {
435  PIOS_Assert(dev->magic == MAX7456_MAGIC);
436 
437  PIOS_Assert(!dev->opened);
438 
439  chip_select(dev);
440  set_offset(dev, col, row);
441  write_register(dev, MAX7456_REG_DMM,(attr & 0x07) << 3);
442  write_register(dev, MAX7456_REG_DMDI, chr);
443  chip_unselect(dev);
444 }
445 
446 static void PIOS_MAX7456_open (max7456_dev_t dev, uint8_t col, uint8_t row,
447  uint8_t attr)
448 {
449  PIOS_Assert(dev->magic == MAX7456_MAGIC);
450 
451  PIOS_Assert(!dev->opened);
452 
453  dev->opened = true;
454 
455  chip_select(dev);
456  set_offset(dev, col > dev->right ? 0 : col, row > dev->bottom ? 0 : row);
457 
458  // 16 bits operating mode, char attributes, autoincrement
459  write_register(dev, MAX7456_REG_DMM, ((attr & 0x07) << 3) | 0x01);
460 }
461 
462 static void PIOS_MAX7456_close (max7456_dev_t dev)
463 {
464  PIOS_Assert(dev->magic == MAX7456_MAGIC);
465 
466  PIOS_Assert(dev->opened);
467 
468  // terminate autoincrement mode
470 
471  chip_unselect(dev);
472  dev->opened = false;
473 }
474 
475 #define valid_char(c) (c == MAX7456_DMDI_AUTOINCREMENT_STOP ? 0x00 : c)
476 void PIOS_MAX7456_puts(max7456_dev_t dev, uint8_t col, uint8_t row, const char *s, uint8_t attr)
477 {
478  PIOS_Assert(dev->magic == MAX7456_MAGIC);
479 
480  if (col == MAX7456_FMT_H_CENTER) {
481  col = ((MAX7456_COLUMNS - strlen(s)) / 2);
482  }
483  PIOS_MAX7456_open(dev, col, row, attr);
484  while (*s)
485  {
486  write_register(dev, MAX7456_REG_DMDI, valid_char(*s));
487  s++;
488  }
489  PIOS_MAX7456_close(dev);
490 }
491 
493  uint8_t *mode, uint8_t *right, uint8_t *bottom,
494  uint8_t *hcenter, uint8_t *vcenter)
495 {
496  PIOS_Assert(dev->magic == MAX7456_MAGIC);
497 
498  if (mode) {
499  *mode=dev->mode;
500  }
501 
502  if (right) {
503  *right=dev->right;
504  }
505 
506  if (bottom) {
507  *bottom=dev->bottom;
508  }
509 
510  if (hcenter) {
511  *hcenter=dev->hcenter;
512  }
513 
514  if (vcenter) {
515  *vcenter=dev->vcenter;
516  }
517 }
518 
519 int32_t PIOS_MAX7456_reset(max7456_dev_t dev)
520 {
521  uint8_t vm0 = read_register_sel(dev, MAX7456_REG_VM0);
522 
524 
525  write_register_sel(dev, MAX7456_REG_OSDBL, vm0);
526 
527  PIOS_DELAY_WaituS(100);
528 
529  while (MAX7456_VM0_SRB_R(vm0) == MAX7456_VM0_SRB_RESET) {
531 
532  vm0 = read_register_sel(dev, MAX7456_REG_VM0);
533  }
534 
535  return 0;
536 }
537 
539 {
540  bool rv = false;
541  uint8_t val = read_register_sel(dev, MAX7456_REG_VM0);
542  if ((MAX7456_VM0_OSD_MASK & val) == 0) {
543  reset_hard(dev);
544  rv = true;
545  }
546  return rv;
547 }
548 
549 #endif /* PIOS_INCLUDE_MAX7456 */
550 
#define MAX7456_PAL_COLUMNS
Definition: pios_max7456.h:48
#define MAX7456_DMM_CLR_READY
#define MAX7456_VM1_BGLVL_42
uint32_t PIOS_DELAY_GetuS()
Query the Delay timer for the current uS.
Definition: pios_delay.c:173
Main PiOS header to include all the compiled in PiOS options.
#define MAX7456_DMDI_AUTOINCREMENT_STOP
#define MAX7456_DMM_CLR_W(regval, val)
int32_t PIOS_SPI_RC_PinSet(pios_spi_t spi_dev, uint32_t slave_id, bool pin_value)
#define MAX7456_VM1_BGLVL_W(regval, val)
bool PIOS_MAX7456_stall_detect(max7456_dev_t dev)
Detects whether the OSD chip has stalled and attempts to restart it.
#define MAX7456_OSDBL_CTL_ENABLE
#define MAX7456_VM0_VSS_PAL
#define MAX7456_MODE_PAL
Definition: pios_max7456.h:40
int32_t PIOS_SPI_ClaimBus(pios_spi_t spi_dev)
#define MAX7456_PAL_HCENTER
Definition: pios_max7456.h:50
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
#define MAX7456_VM0_VSS_W(regval, val)
#define MAX7456_VM1_BTIME_W(regval, val)
#define MAX7456_VM0_VSS_NTSC
#define MAX7456_STAT_NTSC_TRUE
#define MAX7456_PAL_VCENTER
Definition: pios_max7456.h:51
struct max7456_dev_s * max7456_dev_t
Definition: pios_max7456.h:65
void PIOS_MAX7456_put(max7456_dev_t dev, uint8_t col, uint8_t row, uint8_t chr, uint8_t attr)
Sets a position of character memory.
uint8_t data[XFER_BYTES_PER_PACKET]
Definition: bl_messages.h:129
#define MAX7456_DMM_CLR_R(val)
int16_t status
Definition: main.c:61
#define MAX7456_STAT_VSYNC_R(val)
#define MAX7456_STAT_VSYNC_TRUE
void PIOS_MAX7456_set_mode(max7456_dev_t dev, bool force, uint8_t fallback)
Allows overriding the video mode used by OSD.
void PIOS_MAX7456_puts(max7456_dev_t dev, uint8_t col, uint8_t row, const char *s, uint8_t attr)
Sets a string into character memory.
#define MAX7456_MODE_NTSC
Definition: pios_max7456.h:41
#define MAX7456_STAT_PAL_TRUE
uint8_t PIOS_SPI_TransferByte(pios_spi_t spi_dev, uint8_t b)
int32_t PIOS_SPI_SetClockSpeed(pios_spi_t spi_dev, uint32_t speed)
void PIOS_MAX7456_get_extents(max7456_dev_t dev, uint8_t *mode, uint8_t *right, uint8_t *bottom, uint8_t *hcenter, uint8_t *vcenter)
Gets the extents of the screen.
#define MAX7456_VM0_SRB_R(val)
static void set_mode(charosd_state_t state, uint8_t video_std)
#define MAX7456_VM1_BGMODE_LOCAL
void PIOS_MAX7456_clear(max7456_dev_t dev)
Clear the screen.
uint8_t i
Definition: msp_messages.h:97
#define MAX7456_OSDBL_CTL_W(regval, val)
#define MAX7456_CMM_READ_NVM
enum channel_mode mode
Definition: pios_servo.c:58
#define MAX7456_VM0_OSD_MASK
void PIOS_MAX7456_download_char(max7456_dev_t dev, uint8_t char_index, uint8_t *data)
Download a character from the device.
uint16_t value
Definition: storm32bgc.c:155
#define MAX7456_OSDBL_CTL_DISABLE
#define MAX7456_VM1_BGMODE_W(regval, val)
uint32_t magic
#define MAX7456_VM0_SRB_MASK
void PIOS_Thread_Sleep(uint32_t time_ms)
Definition: pios_thread.c:229
#define MAX7456_CMM_WRITE_NVM
#define MAX7456_VM0_SRB_RESET
uint32_t offset
Definition: uavtalk_priv.h:51
#define MAX7456_STAT_NTSC_R(val)
#define MAX7456_FMT_H_CENTER
Definition: pios_max7456.h:63
#define MAX7456_NTSC_HCENTER
Definition: pios_max7456.h:55
#define MAX7456_VM1_BDUTY_13BT
#define MAX7456_STAT_PAL_R(val)
#define MAX7456_VM0_SRB_W(regval, val)
#define MAX7456_PAL_ROWS
Definition: pios_max7456.h:49
#define MAX7456_COLUMNS
Definition: pios_max7456.h:59
#define MAX7456_STAT_CHMEM_BUSY
void PIOS_MAX7456_upload_char(max7456_dev_t dev, uint8_t char_index, const uint8_t *data)
Upload a character to the device.
int32_t PIOS_SPI_ReleaseBus(pios_spi_t spi_dev)
#define MAX7456_DMM_CLR_CLEAR
#define MAX7456_VM1_BTIME_6FIELD
#define MAX7456_NTSC_COLUMNS
Definition: pios_max7456.h:53
#define MAX7456_VM1_BDUTY_W(regval, val)
#define PIOS_Assert(test)
Definition: pios_debug.h:52
int PIOS_MAX7456_init(max7456_dev_t *dev_out, pios_spi_t spi_handle, uint32_t slave_idx)
Allocate and initialise MAX7456 device.
void PIOS_MAX7456_wait_vsync()
#define MAX7456_NTSC_ROWS
Definition: pios_max7456.h:54
int32_t PIOS_DELAY_WaituS(uint32_t uS)
Definition: pios_delay.c:116
#define MAX7456_STAT_CHMEM_R(val)
#define MAX7456_NTSC_VCENTER
Definition: pios_max7456.h:56