spi_lcd_st7565r.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. /*
  2. * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the copyright holders nor the names of
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  22. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  23. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  25. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  26. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  27. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  29. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. *
  32. * For additional information see http://www.ethernut.de/
  33. *
  34. */
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <dev/gpio.h>
  38. #include <sys/timer.h>
  39. #include <sys/heap.h>
  40. #include <sys/nutdebug.h>
  41. #include <dev/spi_lcd_st7565r.h>
  42. #include <dev/framebuffer.h>
  43. /*!
  44. * \file dev/lcd_st7565r.c
  45. *
  46. * Driver for ST7565R LCD controller which provides functionality to
  47. * initialize the display and write data and commands to the LCD controller.
  48. *
  49. */
  50. #ifndef ST7565R_SPI_MODE
  51. #define ST7565R_SPI_MODE SPI_MODE_3
  52. #endif
  53. #ifndef ST7565R_SPI_RATE
  54. #define ST7565R_SPI_RATE 20000000
  55. #endif
  56. #define ST17565R_DEFAULT_WIDTH 128
  57. #define ST17565R_DEFAULT_HEIGHT 32
  58. #ifndef ST17565R_0_WIDTH
  59. #define ST17565R_0_WIDTH ST17565R_DEFAULT_WIDTH
  60. #endif
  61. #ifndef ST17565R_0_HEIGHT
  62. #define ST17565R_0_HEIGHT ST17565R_DEFAULT_HEIGHT
  63. #endif
  64. #ifndef ST17565R_1_WIDTH
  65. #define ST17565R_1_WIDTH ST17565R_DEFAULT_WIDTH
  66. #endif
  67. #ifndef ST17565R_1_HEIGHT
  68. #define ST17565R_1_HEIGHT ST17565R_DEFAULT_HEIGHT
  69. #endif
  70. #define min(a, b) ((a) < (b) ? (a) : (b))
  71. /*!
  72. * \brief Lock LCD controller SPI node from concurrent access
  73. *
  74. * \param dev Specifies LCD device
  75. *
  76. */
  77. int St7565rNodeLock(NUTDEVICE * dev)
  78. {
  79. NUTSPINODE *node;
  80. ST7565R_DCB *dcb;
  81. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  82. node = dev->dev_icb;
  83. NUTASSERT(node->node_dcb != NULL);
  84. dcb = (ST7565R_DCB *) node->node_dcb;
  85. return NutEventWait(&dcb->dcb_lock, NUT_WAIT_INFINITE);
  86. }
  87. /*!
  88. * \brief Unlock LCD controller SPI node from concurrent access
  89. *
  90. * \param dev Specifies LCD device
  91. *
  92. */
  93. void St7565rNodeUnlock(NUTDEVICE * dev)
  94. {
  95. NUTSPINODE *node;
  96. ST7565R_DCB *dcb;
  97. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  98. node = dev->dev_icb;
  99. NUTASSERT(node->node_dcb != NULL);
  100. dcb = (ST7565R_DCB *) node->node_dcb;
  101. NutEventPost(&dcb->dcb_lock);
  102. }
  103. /*!
  104. * \brief Send a command to the display controller
  105. *
  106. * Pull A0 low before writing to the controller.
  107. *
  108. * \param node Specifies the SPI node.
  109. * \param cmd Command code.
  110. *
  111. * \return 0 on success, -1 on errors.
  112. */
  113. static inline int St7565rWriteCmd(NUTSPINODE * node, uint8_t cmd)
  114. {
  115. ST7565R_DCB *dcb = (ST7565R_DCB *) node->node_dcb;
  116. NUTSPIBUS *bus = (NUTSPIBUS *) node->node_bus;
  117. int rc;
  118. GpioPinSetLow(dcb->a0_port, dcb->a0_pin);
  119. rc = (*bus->bus_alloc) (node, 0);
  120. if (rc == 0) {
  121. rc = (*bus->bus_transfer) (node, &cmd, NULL, 1);
  122. if (rc == 0) {
  123. (*bus->bus_wait) (node, NUT_WAIT_INFINITE);
  124. }
  125. (*bus->bus_release) (node);
  126. }
  127. return rc;
  128. }
  129. /*!
  130. * \brief Write data to the display controller
  131. *
  132. * Pull A0 high before writing to the controller.
  133. *
  134. * \param node Specifies the SPI node.
  135. * \param data data buffer
  136. * \param size Number of bytes to write
  137. *
  138. * \return 0 on success, -1 on errors.
  139. */
  140. static inline int St7565rWriteData(NUTSPINODE * node, uint8_t *data, size_t size)
  141. {
  142. ST7565R_DCB *dcb = (ST7565R_DCB *) node->node_dcb;
  143. NUTSPIBUS *bus = (NUTSPIBUS *) node->node_bus;
  144. int rc;
  145. GpioPinSetHigh(dcb->a0_port, dcb->a0_pin);
  146. rc = (*bus->bus_alloc) (node, 0);
  147. if (rc == 0) {
  148. rc = (*bus->bus_transfer) (node,data, NULL, size);
  149. if (rc == 0) {
  150. (*bus->bus_wait) (node, NUT_WAIT_INFINITE);
  151. }
  152. (*bus->bus_release) (node);
  153. }
  154. GpioPinSetLow(dcb->a0_port, dcb->a0_pin);
  155. return rc;
  156. }
  157. /*!
  158. * \brief Reset the LCD controller.
  159. *
  160. * Hard- or soft reset can be selected:
  161. * hard: Hard reset the LCD controller by pulling the reset line low
  162. * soft: Send soft reset command to the LCD controller
  163. *
  164. * \param node Specifies the SPI node.
  165. * \param hard 1: hard reset, 0: soft reset
  166. */
  167. static int St7565rReset(NUTSPINODE * node, int hard)
  168. {
  169. ST7565R_DCB *dcb = (ST7565R_DCB *) node->node_dcb;
  170. if (hard) {
  171. GpioPinSetHigh(dcb->reset_port, dcb->reset_pin);
  172. NutMicroDelay(10);
  173. GpioPinSetLow(dcb->reset_port, dcb->reset_pin);
  174. NutMicroDelay(10);
  175. GpioPinSetHigh(dcb->reset_port, dcb->reset_pin);
  176. NutMicroDelay(10);
  177. return 0;
  178. } else {
  179. return St7565rWriteCmd(node, ST7565R_CMD_RESET);
  180. }
  181. }
  182. /*!
  183. * \brief Enable / Disable the LCD sleep mode
  184. *
  185. * \param dev Specifies LCD device
  186. * \param sleep 1: enable sleep mode, 0: enter normal operation mode
  187. *
  188. * \return 0 on success, -1 on errors.
  189. */
  190. int St7565rSetMode(NUTDEVICE * dev, int sleep)
  191. {
  192. NUTSPINODE *node;
  193. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  194. node = dev->dev_icb;
  195. return St7565rWriteCmd(node, sleep ? ST7565R_CMD_SLEEP_MODE : ST7565R_CMD_NORMAL_MODE);
  196. }
  197. /*!
  198. * \brief Set page in display RAM
  199. *
  200. * Next the column address should be set.
  201. *
  202. * \param dev Specifies LCD device
  203. * \param address page address (range: 0..8)
  204. *
  205. * \return 0 on success, -1 on errors.
  206. */
  207. int St7565rSetPageAddress(NUTDEVICE * dev, uint8_t address)
  208. {
  209. NUTSPINODE *node;
  210. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  211. node = dev->dev_icb;
  212. return St7565rWriteCmd(node, ST7565R_CMD_PAGE_ADDRESS_SET(address & 0x0F));
  213. }
  214. /*!
  215. * \brief Set column in display RAM
  216. *
  217. * \param dev Specifies LCD device
  218. * \param address column address (range: 0..131)
  219. *
  220. * \return 0 on success, -1 on errors.
  221. */
  222. int St7565rSetColAddress(NUTDEVICE * dev, uint8_t address)
  223. {
  224. NUTSPINODE *node;
  225. int rc;
  226. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  227. node = dev->dev_icb;
  228. rc = St7565rWriteCmd(node, ST7565R_CMD_COLUMN_ADDRESS_SET_MSB(address >> 4));
  229. rc |= St7565rWriteCmd(node, ST7565R_CMD_COLUMN_ADDRESS_SET_LSB(address & 0x0F));
  230. return rc;
  231. }
  232. /*!
  233. * \brief Set display start draw line address
  234. *
  235. * Configure the line to start drawing on the LCD
  236. *
  237. * \param dev Specifies LCD device
  238. * \param address display start line address (range: 0..63)
  239. *
  240. * \return 0 on success, -1 on errors.
  241. */
  242. int St7565rSetDisplayStartLineAddress(NUTDEVICE * dev, uint8_t address)
  243. {
  244. NUTSPINODE *node;
  245. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  246. node = dev->dev_icb;
  247. return St7565rWriteCmd(node, ST7565R_CMD_START_LINE_SET(address & 0x3F));
  248. }
  249. /*!
  250. * \brief Turn on / off the display
  251. *
  252. * \param dev Specifies LCD device
  253. * \param enable 1: Turn on display, 0: turn off display
  254. *
  255. * \return 0 on success, -1 on errors.
  256. */
  257. int St7565rDisplayEnable(NUTDEVICE * dev, int enable)
  258. {
  259. NUTSPINODE *node;
  260. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  261. node = dev->dev_icb;
  262. return St7565rWriteCmd(node, enable ? ST7565R_CMD_DISPLAY_ON : ST7565R_CMD_DISPLAY_OFF);
  263. }
  264. /*!
  265. * \brief Set LCD contrast
  266. *
  267. * The contrast value will be set by modifying the LCD voltage. Out of range
  268. * settings might damage the LCD, therefor the contrast value is clamped to the
  269. * configured contrast range. (\ref ST7565R_DISPLAY_CONTRAST_MAX ... \ref ST7565R_DISPLAY_CONTRAST_MIN.)
  270. *
  271. * \param dev Specifies LCD device
  272. * \param contrast a number between 0 and 63 where the max values is given by
  273. * the LCD.
  274. *
  275. * \return 0 on success, -1 on errors.
  276. */
  277. int St7565rSetContrast(NUTDEVICE * dev, uint8_t contrast)
  278. {
  279. NUTSPINODE *node;
  280. int rc;
  281. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  282. node = dev->dev_icb;
  283. if (contrast < ST7565R_CONTRAST_MIN) {
  284. contrast = ST7565R_CONTRAST_MIN;
  285. }
  286. if (contrast > ST7565R_CONTRAST_MAX) {
  287. contrast = ST7565R_CONTRAST_MAX;
  288. }
  289. rc = St7565rWriteCmd(node, ST7565R_CMD_ELECTRONIC_VOLUME_MODE_SET);
  290. rc |= St7565rWriteCmd(node, ST7565R_CMD_ELECTRONIC_VOLUME(contrast));
  291. return rc;
  292. }
  293. /*!
  294. * \brief Invert LCD
  295. *
  296. * \param dev Specifies LCD device
  297. * \param enable 1: invert, 0: normal mode
  298. *
  299. * \return 0 on success, -1 on errors.
  300. */
  301. int St7565rDisplayInvert(NUTDEVICE * dev, int invert)
  302. {
  303. NUTSPINODE *node;
  304. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  305. node = dev->dev_icb;
  306. return St7565rWriteCmd(node, invert ? ST7565R_CMD_DISPLAY_REVERSE : ST7565R_CMD_DISPLAY_NORMAL);
  307. }
  308. /*!
  309. * \brief Set all pixels for debugging purposes
  310. *
  311. * Turn on all pixels for debugging purposes. This will not affect the LCD RAM
  312. *
  313. * \param dev Specifies LCD device
  314. * \param all_on 1: All pixels on, 0: normal mode, show content of the LCD RAM
  315. *
  316. * \return 0 on success, -1 on errors.
  317. */
  318. int St7565rDebugPixelsAllOn(NUTDEVICE * dev, int all_on)
  319. {
  320. NUTSPINODE *node;
  321. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  322. node = dev->dev_icb;
  323. return St7565rWriteCmd(node, all_on ? ST7565R_CMD_DISPLAY_ALL_POINTS_ON : ST7565R_CMD_DISPLAY_ALL_POINTS_OFF);
  324. }
  325. /*!
  326. * \brief Copy RAM Framebuffer to LCD
  327. *
  328. * \param dev Specifies LCD device
  329. *
  330. * \return 0 on success, -1 on errors.
  331. */
  332. int St7565rUpdateFb(NUTDEVICE * dev)
  333. {
  334. NUTSPINODE *node;
  335. FBINFO *dcb;
  336. size_t fb_size;
  337. size_t offset;
  338. int page;
  339. int rc = 0;
  340. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  341. node = dev->dev_icb;
  342. dcb = (FBINFO *) dev->dev_dcb;
  343. /* Calculate size of framebuffer in bytes. Each bit represents on pixel */
  344. fb_size = dcb->width * dcb->height / 8;
  345. for (offset = 0, page = 0; offset < fb_size; offset += 128, page ++) {
  346. rc |= St7565rWriteCmd(node, ST7565R_CMD_PAGE_ADDRESS_SET(page));
  347. rc |= St7565rWriteCmd(node, ST7565R_CMD_COLUMN_ADDRESS_SET_MSB(0));
  348. rc |= St7565rWriteCmd(node, ST7565R_CMD_COLUMN_ADDRESS_SET_LSB(0));
  349. rc |= St7565rWriteData(node, dcb->fb + offset, min(128, fb_size - offset));
  350. }
  351. return rc;
  352. }
  353. /*!
  354. * \brief Handle I/O controls for St7565 Framebuffer.
  355. *
  356. * \return 0.
  357. */
  358. static int St7565rIOCtl(NUTDEVICE * dev, int req, void *conf)
  359. {
  360. NUTASSERT (dev != NULL && dev->dev_icb != NULL);
  361. return 0;
  362. }
  363. /*!
  364. * \brief Write data to ST7565R framebuffer.
  365. *
  366. * Each write operation updates the LCD RAM with the shadow framebuffer
  367. *
  368. * \param fp Pointer to the NUTFILE struct
  369. * \param buffer Data to be written or NULL if the buffer shall be just
  370. * synced with the display.
  371. * \param len Number of bytes to write
  372. *
  373. * \return Number of bytes written.
  374. */
  375. static int St7565rWrite(NUTFILE * fp, const void *buffer, int len)
  376. {
  377. FBINFO *dcb;
  378. NUTASSERT(fp != NULL && fp != NUTFILE_EOF && fp->nf_dev != NULL);
  379. NUTASSERT(fp->nf_dev->dev_icb != NULL && fp->nf_dev->dev_dcb != NULL);
  380. dcb = fp->nf_dev->dev_dcb;
  381. if (buffer == NULL) {
  382. /* Flush the LCD shadow buffer / sync with LCD */
  383. St7565rUpdateFb(fp->nf_dev);
  384. return 0;
  385. }
  386. len = min(len, dcb->width * dcb->height / 8);
  387. if (dcb->fb != NULL) {
  388. int i, bcount;
  389. int bytes_per_line = dcb->width / 8;
  390. uint8_t *fb = dcb->fb;
  391. uint8_t *src = (uint8_t*) buffer;
  392. uint8_t y0, y1;
  393. /* The ST7565r LCD controller has a really brain dead memory layout.
  394. * Each byte in the framebuffer represents 8 vertical pixels.
  395. * The framebuffer is organized in several banks. On bank represents
  396. * width * 8 vertical pixels.
  397. *
  398. * The following loop re-orders a framebuffer content organized in the
  399. * "normal" layout (1 byte represents 8 horizontal pixels) to the LCD
  400. * controllers layout.
  401. */
  402. y1 = 0x01;
  403. y0 = ~y1;
  404. bcount = bytes_per_line;
  405. for (i = 0; i < len; i++) {
  406. bcount --;
  407. if (*src & 0x01) (*(fb++)) |= y1; else (*(fb++)) &= y0;
  408. if (*src & 0x02) (*(fb++)) |= y1; else (*(fb++)) &= y0;
  409. if (*src & 0x04) (*(fb++)) |= y1; else (*(fb++)) &= y0;
  410. if (*src & 0x08) (*(fb++)) |= y1; else (*(fb++)) &= y0;
  411. if (*src & 0x10) (*(fb++)) |= y1; else (*(fb++)) &= y0;
  412. if (*src & 0x20) (*(fb++)) |= y1; else (*(fb++)) &= y0;
  413. if (*src & 0x40) (*(fb++)) |= y1; else (*(fb++)) &= y0;
  414. if (*src & 0x80) (*(fb++)) |= y1; else (*(fb++)) &= y0;
  415. if (bcount == 0) {
  416. bcount = bytes_per_line;
  417. y1 = y1 << 1;
  418. if (y1 == 0) {
  419. y1 = 0x01;
  420. }
  421. y0 = ~y1;
  422. fb = &dcb->fb[((i+1) / dcb->width) * dcb->width];
  423. }
  424. src ++;
  425. }
  426. St7565rUpdateFb(fp->nf_dev);
  427. } else {
  428. return 0;
  429. }
  430. return len;
  431. }
  432. /*!
  433. * \brief Read data from ST7565R framebuffer.
  434. *
  435. * The Read operation does not sync the shadow framebuffer with the LCD RAM.
  436. * Read returns data from the shadow framebuffer.
  437. *
  438. * \param fp Pointer to the NUTFILE struct
  439. * \param buffer Pointer to the buffer that receives the data.
  440. * \param len Number of bytes to read
  441. *
  442. * \return Number of bytes read.
  443. */
  444. static int St7565rRead(NUTFILE * fp, void *buffer, int len)
  445. {
  446. FBINFO *dcb;
  447. NUTASSERT(fp != NULL && fp != NUTFILE_EOF && fp->nf_dev != NULL);
  448. NUTASSERT(fp->nf_dev->dev_icb != NULL && fp->nf_dev->dev_dcb != NULL);
  449. dcb = fp->nf_dev->dev_dcb;
  450. if ((dcb->fb != NULL) && (buffer != NULL)) {
  451. int i, bcount;
  452. int bytes_per_line = dcb->width / 8;
  453. uint8_t *fb = dcb->fb;
  454. uint8_t *dest = (uint8_t*) buffer;
  455. uint8_t y1;
  456. /* The ST7565r LCD controller has a really brain dead memory layout.
  457. * Each byte in the framebuffer represents 8 vertical pixels.
  458. * The framebuffer is organized in several banks. On bank represents
  459. * width * 8 vertical pixels.
  460. *
  461. * The following loop re-orders the LCD framebuffer layout to the
  462. * "normal" layout (1 byte represents 8 horizontal pixels) to the LCD
  463. * controllers layout and fills the read buffer.
  464. */
  465. len = min(len, dcb->width * dcb->height / 8);
  466. y1 = 0x01;
  467. bcount = bytes_per_line;
  468. for (i = 0; i < len; i++) {
  469. bcount --;
  470. *dest = 0;
  471. if ((*fb++) & y1) *dest |= 0x01;
  472. if ((*fb++) & y1) *dest |= 0x02;
  473. if ((*fb++) & y1) *dest |= 0x04;
  474. if ((*fb++) & y1) *dest |= 0x08;
  475. if ((*fb++) & y1) *dest |= 0x10;
  476. if ((*fb++) & y1) *dest |= 0x20;
  477. if ((*fb++) & y1) *dest |= 0x40;
  478. if ((*fb++) & y1) *dest |= 0x80;
  479. if (bcount == 0) {
  480. bcount = bytes_per_line;
  481. y1 = y1 << 1;
  482. if (y1 == 0) {
  483. y1 = 0x01;
  484. }
  485. fb = &dcb->fb[((i+1) / dcb->width) * dcb->width];
  486. }
  487. dest ++;
  488. }
  489. } else {
  490. return 0;
  491. }
  492. return len;
  493. }
  494. /*!
  495. * \brief Generate File Handle for ST7565R framebuffer device.
  496. *
  497. * \param dev Specifies the ST7565R LCD controller device.
  498. *
  499. * \return 0 on success or -1 in case of an error.
  500. */
  501. static NUTFILE *St7565rOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
  502. {
  503. NUTFILE *fp;
  504. NUTASSERT( dev != NULL);
  505. if ((fp = malloc(sizeof(NUTFILE))) == 0) {
  506. return NUTFILE_EOF;
  507. }
  508. fp->nf_fcb = 0;
  509. fp->nf_dev = dev;
  510. return fp;
  511. }
  512. /*!
  513. * \brief Close ST7565R framebuffer device.
  514. *
  515. * \return 0 if closed and was opened before, else -1.
  516. */
  517. static int St7565rClose(NUTFILE * fp)
  518. {
  519. if( fp != NULL) {
  520. free( fp);
  521. return 0;
  522. }
  523. return -1;
  524. }
  525. /*!
  526. * \brief Initialize the LCD controller device
  527. *
  528. * Call this function to initialize the hardware interface and the LCD
  529. * controller. When initialization is done the display is turned on and ready
  530. * to receive data.
  531. *
  532. * Compared to other SPI bus node devices (and other Nut/OS devices in general)
  533. * some settings have to be configured by the user before registering the
  534. * SPI node. These are:
  535. * - devSt7565rFb0.dev_dcb
  536. * - devSt7565rFb.dev_icb->node_dcb
  537. *
  538. * This routine is internally called by Nut/OS during device registration.
  539. *
  540. * The driver framework may call this function more than once.
  541. *
  542. * \param dev Specifies the ST7565R LCD controller device.
  543. *
  544. * \return 0 on success or -1 in case of an error
  545. */
  546. static int St7565rInit(NUTDEVICE * dev)
  547. {
  548. NUTSPINODE *node;
  549. FBINFO *dcb;
  550. ST7565R_DCB *node_dcb;
  551. int rc = 0;
  552. NUTASSERT(dev != NULL);
  553. NUTASSERT(dev->dev_icb != NULL);
  554. NUTASSERT(dev->dev_dcb != NULL);
  555. node = dev->dev_icb;
  556. node_dcb = (ST7565R_DCB *) node->node_dcb;
  557. node_dcb->dcb_lock = SIGNALED;
  558. dcb = dev->dev_dcb;
  559. if (dcb->fb != NULL) {
  560. /* The device is just initialised */
  561. return 0;
  562. }
  563. /* Allocate framebuffer memory, w*h / 8, as we have only 1 BPP */
  564. dcb->bpp = 1;
  565. dcb->fb = malloc(dcb->width * dcb->height / 8);
  566. if (dcb->fb == NULL) {
  567. return -1;
  568. }
  569. memset(dcb->fb, 0, dcb->width * dcb->height / 8);
  570. GpioPinConfigSet(node_dcb->a0_port, node_dcb->a0_pin, GPIO_CFG_OUTPUT);
  571. /* Configure A0 pin to command mode */
  572. GpioPinSetLow(node_dcb->a0_port, node_dcb->a0_pin);
  573. GpioPinConfigSet(node_dcb->reset_port, node_dcb->reset_pin, GPIO_CFG_OUTPUT);
  574. GpioPinSetHigh(node_dcb->reset_port, node_dcb->reset_pin);
  575. /* Apply hard reset of the LCD display controller */
  576. rc |= St7565rReset(node, 1);
  577. /* Turn on the display */
  578. rc |= St7565rDisplayEnable(dev, 0);
  579. /* Set the voltage bias ratio to 1/6 */
  580. rc |= St7565rWriteCmd(node, ST7565R_CMD_LCD_BIAS_1_DIV_6_DUTY33);
  581. /* Set column address mode to increasing */
  582. rc |= St7565rWriteCmd(node, ST7565R_CMD_ADC_NORMAL);
  583. /* Reverse the common mode scan direction COM31->COM0 */
  584. rc |= St7565rWriteCmd(node, ST7565R_CMD_REVERSE_SCAN_DIRECTION);
  585. /* Set voltage resistor ratio to 2 */
  586. rc |= St7565rWriteCmd(node, ST7565R_CMD_VOLTAGE_RESISTOR_RATIO_2);
  587. /* Set booster circuit, voltage regulator and voltage follower all to on */
  588. rc |= St7565rWriteCmd(node, ST7565R_CMD_POWER_CTRL_ALL_ON);
  589. /* Set the booster ratio to 2X,3X,4X */
  590. rc |= St7565rWriteCmd(node, ST7565R_CMD_BOOSTER_RATIO_SET);
  591. rc |= St7565rWriteCmd(node, ST7565R_CMD_BOOSTER_RATIO_2X_3X_4X);
  592. /* Set contrast to min value */
  593. rc |= St7565rSetContrast(dev, ST7565R_CONTRAST_MAX);
  594. rc |= St7565rSetDisplayStartLineAddress(dev, 0);
  595. /* Non-inverted display */
  596. rc |= St7565rDisplayInvert(dev, 0);
  597. /* Turn on the display */
  598. rc |= St7565rDisplayEnable(dev, 1);
  599. if (rc != 0) {
  600. free(dcb->fb);
  601. return -1;
  602. }
  603. return 0;
  604. }
  605. /*!
  606. * \brief First ST7565R LCD Controller SPI node implementation structure.
  607. */
  608. NUTSPINODE nodeSt7565r0 = {
  609. NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */
  610. NULL, /*!< \brief Pointer to the bus device driver specific settings, node_stat. */
  611. ST7565R_SPI_RATE, /*!< \brief Initial clock rate, node_rate. */
  612. ST7565R_SPI_MODE, /*!< \brief Initial mode, node_mode. */
  613. 8, /*!< \brief Initial data bits, node_bits. */
  614. 0, /*!< \brief Chip select, node_cs. */
  615. NULL, /*!< \brief Pointer to our private device control block, node_dcb. */
  616. };
  617. /*!
  618. * \brief 7seg device implementation structure.
  619. */
  620. NUTDEVICE devSt7565rFb0 = {
  621. NULL, /*!< \brief Pointer to next device, dev_next. */
  622. {'F', 'B', '0', 0, 0, 0, 0, 0, 0}, /*!< \brief Unique device name, dev_name. */
  623. IFTYP_FB, /*!< \brief Type of device, dev_type. */
  624. 0, /*!< \brief Base address, dev_base (not used). */
  625. 0, /*!< \brief First interrupt number, dev_irq (not used). */
  626. &nodeSt7565r0, /*!< \brief Interface control block, dev_icb. */
  627. 0, /*!< \brief Driver control block, dev_dcb. */
  628. St7565rInit, /*!< \brief Driver initialization routine, dev_init. */
  629. St7565rIOCtl, /*!< \brief Driver specific control function, dev_ioctl. */
  630. St7565rRead, /*!< \brief Read from device, dev_read. */
  631. St7565rWrite, /*!< \brief Write to device, dev_write. */
  632. #ifdef __HARVARD_ARCH__
  633. 0, /*!< \brief Write data from program space to device, dev_write_P. */
  634. #endif
  635. St7565rOpen, /*!< \brief Mount volume, dev_open. */
  636. St7565rClose, /*!< \brief Unmount volume, dev_close. */
  637. 0, /*!< \brief Request file size, dev_size. */
  638. 0, /*!< \brief Select, currently not implemented, dev_select. */
  639. };