SparkFun_Alphanumeric_Display.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. /******************************************************************************
  2. SparkFun_Alphanumeric_Display.cpp
  3. SparkFun Alphanumeric Display Library Source File
  4. Priyanka Makin @ SparkFun Electronics
  5. Original Creation Date: February 25, 2020
  6. https://github.com/sparkfun/SparkFun_Alphanumeric_Display_Arduino_Library
  7. Updated May 2, 2020 by Gaston Williams to add defineChar function
  8. Pickup a board here: https://sparkle.sparkfun.com/sparkle/storefront_products/16391
  9. This file implements all functions of the HT16K33 class. Functions here range
  10. from printing to one or more Alphanumeric Displays, changing the display settings, and writing/
  11. reading the RAM of the HT16K33.
  12. The Holtek HT16K33 seems to be susceptible to address changes intra-sketch. The ADR pins
  13. are muxed with the ROW and COM drivers so as semgents are turned on/off that affect
  14. the ADR1/ADR0 pins the address has been seen to change. The best way around this is
  15. to do a isConnected check before updateRAM() is sent to the driver IC.
  16. Development environment specifics:
  17. IDE: Arduino 1.8.9
  18. Hardware Platform: Arduino Uno
  19. Alphanumeric Display Breakout Version: 1.0.0
  20. This code is beerware; if you see me (or any other SparkFun employee) at the
  21. local, and you've found our code helpful, please buy us a round!
  22. Distributed as-is; no warranty is given.
  23. ******************************************************************************/
  24. #include <SparkFun_Alphanumeric_Display.h>
  25. /*--------------------------- Character Map ----------------------------------*/
  26. #define SFE_ALPHANUM_UNKNOWN_CHAR 95
  27. //This is the lookup table of segments for various characters
  28. //For AVR architecture, use PROGMEM
  29. #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
  30. #include <avr/pgmspace.h>
  31. static const uint16_t PROGMEM alphanumeric_segs[96]{
  32. #else
  33. static const uint16_t alphanumeric_segs[96]{
  34. #endif
  35. // nmlkjihgfedcba
  36. 0b00000000000000, // ' ' (space)
  37. 0b00001000001000, // '!'
  38. 0b00001000000010, // '"'
  39. 0b01001101001110, // '#'
  40. 0b01001101101101, // '$'
  41. 0b10010000100100, // '%'
  42. 0b00110011011001, // '&'
  43. 0b00001000000000, // '''
  44. 0b00000000111001, // '('
  45. 0b00000000001111, // ')'
  46. 0b11111010000000, // '*'
  47. 0b01001101000000, // '+'
  48. 0b10000000000000, // ','
  49. 0b00000101000000, // '-'
  50. 0b00000000000000, // '.'
  51. 0b10010000000000, // '/'
  52. 0b00000000111111, // '0'
  53. 0b00010000000110, // '1'
  54. 0b00000101011011, // '2'
  55. 0b00000101001111, // '3'
  56. 0b00000101100110, // '4'
  57. 0b00000101101101, // '5'
  58. 0b00000101111101, // '6'
  59. 0b01010000000001, // '7'
  60. 0b00000101111111, // '8'
  61. 0b00000101100111, // '9'
  62. 0b00000000000000, // ':'
  63. 0b10001000000000, // ';'
  64. 0b00110000000000, // '<'
  65. 0b00000101001000, // '='
  66. 0b01000010000000, // '>'
  67. 0b01000100000011, // '?'
  68. 0b00001100111011, // '@'
  69. 0b00000101110111, // 'A'
  70. 0b01001100001111, // 'B'
  71. 0b00000000111001, // 'C'
  72. 0b01001000001111, // 'D'
  73. 0b00000101111001, // 'E'
  74. 0b00000101110001, // 'F'
  75. 0b00000100111101, // 'G'
  76. 0b00000101110110, // 'H'
  77. 0b01001000001001, // 'I'
  78. 0b00000000011110, // 'J'
  79. 0b00110001110000, // 'K'
  80. 0b00000000111000, // 'L'
  81. 0b00010010110110, // 'M'
  82. 0b00100010110110, // 'N'
  83. 0b00000000111111, // 'O'
  84. 0b00000101110011, // 'P'
  85. 0b00100000111111, // 'Q'
  86. 0b00100101110011, // 'R'
  87. 0b00000110001101, // 'S'
  88. 0b01001000000001, // 'T'
  89. 0b00000000111110, // 'U'
  90. 0b10010000110000, // 'V'
  91. 0b10100000110110, // 'W'
  92. 0b10110010000000, // 'X'
  93. 0b01010010000000, // 'Y'
  94. 0b10010000001001, // 'Z'
  95. 0b00000000111001, // '['
  96. 0b00100010000000, // '\'
  97. 0b00000000001111, // ']'
  98. 0b10100000000000, // '^'
  99. 0b00000000001000, // '_'
  100. 0b00000010000000, // '`'
  101. 0b00000101011111, // 'a'
  102. 0b00100001111000, // 'b'
  103. 0b00000101011000, // 'c'
  104. 0b10000100001110, // 'd'
  105. 0b00000001111001, // 'e'
  106. 0b00000001110001, // 'f'
  107. 0b00000110001111, // 'g'
  108. 0b00000101110100, // 'h'
  109. 0b01000000000000, // 'i'
  110. 0b00000000001110, // 'j'
  111. 0b01111000000000, // 'k'
  112. 0b01001000000000, // 'l'
  113. 0b01000101010100, // 'm'
  114. 0b00100001010000, // 'n'
  115. 0b00000101011100, // 'o'
  116. 0b00010001110001, // 'p'
  117. 0b00100101100011, // 'q'
  118. 0b00000001010000, // 'r'
  119. 0b00000110001101, // 's'
  120. 0b00000001111000, // 't'
  121. 0b00000000011100, // 'u'
  122. 0b10000000010000, // 'v'
  123. 0b10100000010100, // 'w'
  124. 0b10110010000000, // 'x'
  125. 0b00001100001110, // 'y'
  126. 0b10010000001001, // 'z'
  127. 0b10000011001001, // '{'
  128. 0b01001000000000, // '|'
  129. 0b00110100001001, // '}'
  130. 0b00000101010010, // '~'
  131. 0b11111111111111, // Unknown character (DEL or RUBOUT)
  132. };
  133. /*--------------------------- Device Status----------------------------------*/
  134. bool HT16K33::begin(uint8_t addressDisplayOne, uint8_t addressDisplayTwo, uint8_t addressDisplayThree, uint8_t addressDisplayFour, TwoWire &wirePort)
  135. {
  136. _deviceAddressDisplayOne = addressDisplayOne; // grab the address of the alphanumeric
  137. _deviceAddressDisplayTwo = addressDisplayTwo;
  138. _deviceAddressDisplayThree = addressDisplayThree;
  139. _deviceAddressDisplayFour = addressDisplayFour;
  140. if (_deviceAddressDisplayFour != DEFAULT_NOTHING_ATTACHED)
  141. numberOfDisplays = 4;
  142. else if (_deviceAddressDisplayThree != DEFAULT_NOTHING_ATTACHED)
  143. numberOfDisplays = 3;
  144. else if (_deviceAddressDisplayTwo != DEFAULT_NOTHING_ATTACHED)
  145. numberOfDisplays = 2;
  146. else
  147. numberOfDisplays = 1;
  148. //TODO: malloc more displayRAM
  149. _i2cPort = &wirePort; // Remember the user's setting
  150. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  151. {
  152. if (isConnected(i) == false)
  153. {
  154. return false;
  155. }
  156. // if (checkDeviceID(i) == false)
  157. // {
  158. // Serial.println(i);
  159. // Serial.println("Hello, I've failed checkDeviceID()");
  160. // return false;
  161. // }
  162. delay(100);
  163. }
  164. if (initialize() == false)
  165. {
  166. return false;
  167. }
  168. if (clear() == false) // Clear all displays
  169. {
  170. return false;
  171. }
  172. displayContent[4 * 4] = '\0'; // Terminate the array because we are doing direct prints
  173. return true;
  174. }
  175. // Check that the display is responding on the I2C bus
  176. // The Holtek IC sometimes fails to respond. This attempts multiple times before giving up.
  177. bool HT16K33::isConnected(uint8_t displayNumber)
  178. {
  179. uint8_t triesBeforeGiveup = 5;
  180. for (uint8_t x = 0; x < triesBeforeGiveup; x++)
  181. {
  182. _i2cPort->beginTransmission(lookUpDisplayAddress(displayNumber));
  183. if (_i2cPort->endTransmission() == 0)
  184. {
  185. return true;
  186. }
  187. delay(100);
  188. }
  189. return false;
  190. }
  191. // Run through initialization procedure for each display connected on the bus
  192. bool HT16K33::initialize()
  193. {
  194. // Turn on system clock of all displays
  195. if (enableSystemClock() == false)
  196. {
  197. return false;
  198. }
  199. // Set brightness of all displays to full brightness
  200. if (setBrightness(15) == false)
  201. {
  202. return false;
  203. }
  204. // Turn blinking off for all displays
  205. if (setBlinkRate(ALPHA_BLINK_RATE_NOBLINK) == false)
  206. {
  207. return false;
  208. }
  209. // Turn on all displays
  210. if (displayOn() == false)
  211. {
  212. return false;
  213. }
  214. return true;
  215. }
  216. // Turn on the system oscillator for all displays on the I2C bus
  217. bool HT16K33::enableSystemClock()
  218. {
  219. bool status = true;
  220. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  221. {
  222. if (enableSystemClockSingle(i) == false)
  223. status = false;
  224. }
  225. return status;
  226. }
  227. // Turn off the system oscillator for all displays on the bus
  228. bool HT16K33::disableSystemClock()
  229. {
  230. bool status = true;
  231. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  232. {
  233. if (disableSystemClockSingle(i) == false)
  234. status = false;
  235. }
  236. return status;
  237. }
  238. // Turn on the system oscillator for normal operation mode
  239. bool HT16K33::enableSystemClockSingle(uint8_t displayNumber)
  240. {
  241. uint8_t dataToWrite = ALPHA_CMD_SYSTEM_SETUP | 1; // Enable system clock
  242. bool status = writeRAM(lookUpDisplayAddress(displayNumber), dataToWrite);
  243. delay(1); // Allow display to start
  244. return (status);
  245. }
  246. // Turn off the system oscillator for standby mode
  247. bool HT16K33::disableSystemClockSingle(uint8_t displayNumber)
  248. {
  249. uint8_t dataToWrite = ALPHA_CMD_SYSTEM_SETUP | 0; // Standby mode
  250. return (writeRAM(lookUpDisplayAddress(displayNumber), dataToWrite));
  251. }
  252. // This function connects the display number to its coressponding address
  253. uint8_t HT16K33::lookUpDisplayAddress(uint8_t displayNumber)
  254. {
  255. switch (displayNumber)
  256. {
  257. case 1:
  258. return _deviceAddressDisplayOne;
  259. break;
  260. case 2:
  261. return _deviceAddressDisplayTwo;
  262. break;
  263. case 3:
  264. return _deviceAddressDisplayThree;
  265. break;
  266. case 4:
  267. return _deviceAddressDisplayFour;
  268. break;
  269. }
  270. return 0; // We shouldn't get here
  271. }
  272. /*-------------------------- Display configuration functions ---------------------------*/
  273. // Turn off all segments of all displays connected to bus
  274. bool HT16K33::clear()
  275. {
  276. // Clear the displayRAM array
  277. for (uint8_t i = 0; i < 16 * numberOfDisplays; i++)
  278. displayRAM[i] = 0;
  279. digitPosition = 0;
  280. return (updateDisplay());
  281. }
  282. // This function sets the brightness of all displays on the bus.
  283. // Duty cycle valid between 0 (1/16 brightness) and 15 (full brightness)
  284. bool HT16K33::setBrightness(uint8_t duty)
  285. {
  286. bool status = true;
  287. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  288. {
  289. if (setBrightnessSingle(i, duty) == false)
  290. status = false;
  291. }
  292. return status;
  293. }
  294. // Set the brightness of a single display
  295. // Duty cycle valid between 0 (1/16 brightness) and 15 (full brightness)
  296. bool HT16K33::setBrightnessSingle(uint8_t displayNumber, uint8_t duty)
  297. {
  298. if (duty > 15) // Error check
  299. duty = 15;
  300. uint8_t dataToWrite = ALPHA_CMD_DIMMING_SETUP | duty;
  301. return (writeRAM(lookUpDisplayAddress(displayNumber), dataToWrite));
  302. }
  303. // Set the blink rate of all displays on the bus
  304. // Parameter "rate" in Hz
  305. // Valid options for "rate" are defined by datasheet: 2.0, 1.0, or 0.5 Hz
  306. // Any other input to this function will result in steady alphanumeric display
  307. bool HT16K33::setBlinkRate(float rate)
  308. {
  309. bool status = true;
  310. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  311. {
  312. if (setBlinkRateSingle(i, rate) == false)
  313. status = false;
  314. }
  315. return status;
  316. }
  317. // Set the blink rate of a single display on the bus
  318. // Parameter "rate" is in Hz
  319. // Valid options for "rate" are defined by datasheet: 2.0, 1.0, or 0.5 Hz
  320. // Any other input to this function will result in steady alphanumeric display
  321. bool HT16K33::setBlinkRateSingle(uint8_t displayNumber, float rate)
  322. {
  323. if (rate == 2.0)
  324. {
  325. blinkRate = ALPHA_BLINK_RATE_2HZ;
  326. }
  327. else if (rate == 1.0)
  328. {
  329. blinkRate = ALPHA_BLINK_RATE_1HZ;
  330. }
  331. else if (rate == 0.5)
  332. {
  333. blinkRate = ALPHA_BLINK_RATE_0_5HZ;
  334. }
  335. //default to no blink
  336. else
  337. {
  338. blinkRate = ALPHA_BLINK_RATE_NOBLINK;
  339. }
  340. uint8_t dataToWrite = ALPHA_CMD_DISPLAY_SETUP | (blinkRate << 1) | displayOnOff;
  341. return (writeRAM(lookUpDisplayAddress(displayNumber), dataToWrite));
  342. }
  343. // Turn a single alphanumeric display on
  344. bool HT16K33::displayOnSingle(uint8_t displayNumber)
  345. {
  346. return setDisplayOnOff(displayNumber, true);
  347. }
  348. // Turn a single alphanumeric display off
  349. bool HT16K33::displayOffSingle(uint8_t displayNumber)
  350. {
  351. return setDisplayOnOff(displayNumber, false);
  352. }
  353. // Set or clear the display on/off bit of a given display number
  354. bool HT16K33::setDisplayOnOff(uint8_t displayNumber, bool turnOnDisplay)
  355. {
  356. if (turnOnDisplay) {
  357. displayOnOff = ALPHA_DISPLAY_ON;
  358. }
  359. else {
  360. displayOnOff = ALPHA_DISPLAY_OFF;
  361. }
  362. uint8_t dataToWrite = ALPHA_CMD_DISPLAY_SETUP | (blinkRate << 1) | displayOnOff;
  363. return (writeRAM(lookUpDisplayAddress(displayNumber), dataToWrite));
  364. }
  365. // Turn on all displays on the I2C bus
  366. bool HT16K33::displayOn()
  367. {
  368. bool status = true;
  369. displayOnOff = ALPHA_DISPLAY_ON;
  370. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  371. {
  372. if (displayOnSingle(i) == false)
  373. status = false;
  374. }
  375. return status;
  376. }
  377. // Turn off all displays on the I2C bus
  378. bool HT16K33::displayOff()
  379. {
  380. bool status = true;
  381. displayOnOff = ALPHA_DISPLAY_OFF;
  382. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  383. {
  384. if (displayOffSingle(i) == false)
  385. status = false;
  386. }
  387. return status;
  388. }
  389. // Turn the decimal point on for a single display
  390. bool HT16K33::decimalOnSingle(uint8_t displayNumber, bool updateNow)
  391. {
  392. return setDecimalOnOff(displayNumber, true, updateNow);
  393. }
  394. // Turn the decimal point off for a single display
  395. bool HT16K33::decimalOffSingle(uint8_t displayNumber, bool updateNow)
  396. {
  397. return setDecimalOnOff(displayNumber, false, updateNow);
  398. }
  399. // Set or clear the decimal on/off bit
  400. bool HT16K33::setDecimalOnOff(uint8_t displayNumber, bool turnOnDecimal, bool updateNow)
  401. {
  402. uint8_t adr = 0x03;
  403. uint8_t dat;
  404. if (turnOnDecimal == true)
  405. {
  406. decimalOnOff = ALPHA_DECIMAL_ON;
  407. dat = 0x01;
  408. }
  409. else
  410. {
  411. decimalOnOff = ALPHA_DECIMAL_OFF;
  412. dat = 0x00;
  413. }
  414. displayRAM[adr + (displayNumber - 1) * 16] = displayRAM[adr + (displayNumber - 1) * 16] | dat;
  415. if(updateNow)
  416. {
  417. return updateDisplay();
  418. }
  419. else
  420. {
  421. return true;
  422. }
  423. }
  424. // Turn the decimal on for all displays on bus
  425. bool HT16K33::decimalOn()
  426. {
  427. bool status = true;
  428. decimalOnOff = ALPHA_DECIMAL_ON;
  429. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  430. {
  431. if (decimalOnSingle(i) == false)
  432. status = false;
  433. }
  434. return status;
  435. }
  436. // Turn the decimal point off for all displays on bus
  437. bool HT16K33::decimalOff()
  438. {
  439. bool status = true;
  440. decimalOnOff = ALPHA_DECIMAL_OFF;
  441. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  442. {
  443. if (decimalOffSingle(i) == false)
  444. status = false;
  445. }
  446. return status;
  447. }
  448. // Turn the colon on for a single display
  449. bool HT16K33::colonOnSingle(uint8_t displayNumber, bool updateNow)
  450. {
  451. return setColonOnOff(displayNumber, true, updateNow);
  452. }
  453. // Turn the colon off for a single display
  454. bool HT16K33::colonOffSingle(uint8_t displayNumber, bool updateNow)
  455. {
  456. return setColonOnOff(displayNumber, false, updateNow);
  457. }
  458. // Set or clear the colon on/off bit
  459. bool HT16K33::setColonOnOff(uint8_t displayNumber, bool turnOnColon, bool updateNow)
  460. {
  461. uint8_t adr = 0x01;
  462. uint8_t dat;
  463. if (turnOnColon == true)
  464. {
  465. colonOnOff = ALPHA_COLON_ON;
  466. dat = 0x01;
  467. }
  468. else
  469. {
  470. colonOnOff = ALPHA_COLON_OFF;
  471. dat = 0x00;
  472. }
  473. displayRAM[adr + (displayNumber - 1) * 16] = displayRAM[adr + (displayNumber - 1) * 16] | dat;
  474. if(updateNow)
  475. {
  476. return updateDisplay();
  477. }
  478. else
  479. {
  480. return true;
  481. }
  482. }
  483. // Turn the colon on for all displays on the bus
  484. bool HT16K33::colonOn()
  485. {
  486. bool status = true;
  487. colonOnOff = ALPHA_COLON_ON;
  488. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  489. {
  490. if (colonOnSingle(i) == false)
  491. status = false;
  492. }
  493. return status;
  494. }
  495. // Turn the colon off for all displays on the bus
  496. bool HT16K33::colonOff()
  497. {
  498. bool status = true;
  499. colonOnOff = ALPHA_COLON_OFF;
  500. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  501. {
  502. if (colonOffSingle(i) == false)
  503. status = false;
  504. }
  505. return status;
  506. }
  507. /*---------------------------- Light up functions ---------------------------------*/
  508. // Given a segment and a digit, set the matching bit within the RAM of the Holtek RAM set
  509. void HT16K33::illuminateSegment(uint8_t segment, uint8_t digit)
  510. {
  511. uint8_t com;
  512. uint8_t row;
  513. com = segment - 'A'; // Convert the segment letter back to a number
  514. if (com > 6)
  515. com -= 7;
  516. if (segment == 'I')
  517. com = 0;
  518. if (segment == 'H')
  519. com = 1;
  520. row = digit % 4; // Convert digit (1 to 16) back to a relative position on a given digit on display
  521. if (segment > 'G')
  522. row += 4;
  523. uint8_t offset = digit / 4 * 16;
  524. uint8_t adr = com * 2 + offset;
  525. // Determine the address
  526. if (row > 7)
  527. adr++;
  528. // Determine the data bit
  529. if (row > 7)
  530. row -= 8;
  531. uint8_t dat = 1 << row;
  532. displayRAM[adr] = displayRAM[adr] | dat;
  533. }
  534. // Given a binary set of segments and a digit, store this data into the RAM array
  535. void HT16K33::illuminateChar(uint16_t segmentsToTurnOn, uint8_t digit)
  536. {
  537. for (uint8_t i = 0; i < 14; i++) // There are 14 segments on this display
  538. {
  539. if ((segmentsToTurnOn >> i) & 0b1)
  540. illuminateSegment('A' + i, digit); // Convert the segment number to a letter
  541. }
  542. }
  543. // Print a character, for a given digit, on display
  544. void HT16K33::printChar(uint8_t displayChar, uint8_t digit)
  545. {
  546. // moved alphanumeric_segs array to PROGMEM
  547. uint16_t characterPosition = 65532;
  548. // space
  549. if (displayChar == ' ')
  550. characterPosition = 0;
  551. // Printable Symbols -- Between first character ! and last character ~
  552. else if (displayChar >= '!' && displayChar <= '~')
  553. {
  554. characterPosition = displayChar - '!' + 1;
  555. }
  556. uint8_t dispNum = digitPosition / 4;
  557. // Take care of special characters by turning correct segment on
  558. if (characterPosition == 14) // '.'
  559. decimalOnSingle(dispNum+1, false);
  560. if (characterPosition == 26) // ':'
  561. colonOnSingle(dispNum+1, false);
  562. if (characterPosition == 65532) // unknown character
  563. characterPosition = SFE_ALPHANUM_UNKNOWN_CHAR;
  564. uint16_t segmentsToTurnOn = getSegmentsToTurnOn(characterPosition);
  565. illuminateChar(segmentsToTurnOn, digit);
  566. }
  567. // Update the list to define a new segments display for a particular character
  568. bool HT16K33::defineChar(uint8_t displayChar, uint16_t segmentsToTurnOn)
  569. {
  570. bool result = false;
  571. // Check to see if character is within range of displayable ASCII characters
  572. if (displayChar >= '!' && displayChar <= '~')
  573. {
  574. // Get the index of character in table and update its 14-bit segment value
  575. uint16_t characterPosition = displayChar - '!' + 1;
  576. // Create a new character definition
  577. struct CharDef * pNewCharDef = (CharDef *)calloc(1, sizeof(CharDef));
  578. // Set the position to the table index
  579. pNewCharDef -> position = characterPosition;
  580. // Mask the segment value to 14 bits only
  581. pNewCharDef -> segments = segmentsToTurnOn & 0x3FFF;
  582. // New definition always goes at the end of the list
  583. pNewCharDef -> next = NULL;
  584. // If list is empty set it to the new item
  585. if (pCharDefList == NULL)
  586. {
  587. pCharDefList = pNewCharDef;
  588. }
  589. else
  590. {
  591. // Otherwise go to the end of the list and add it there
  592. struct CharDef * pTail = pCharDefList;
  593. while(pTail->next != NULL)
  594. {
  595. pTail = pTail->next;
  596. }
  597. pTail->next = pNewCharDef;
  598. }
  599. // We added the definition so we're all good
  600. result = true;
  601. }
  602. return result;
  603. }
  604. // Get the character map from the definition list or default table
  605. uint16_t HT16K33::getSegmentsToTurnOn(uint8_t charPos)
  606. {
  607. uint16_t segments = 0;
  608. // pointer to a defined character in list
  609. struct CharDef * pDefChar = pCharDefList;
  610. // Search the chacters list for a match
  611. while(pDefChar && (pDefChar->position != charPos))
  612. {
  613. pDefChar = pDefChar -> next;
  614. }
  615. // If we found a match return that value
  616. if (pDefChar != NULL)
  617. {
  618. segments = pDefChar -> segments;
  619. }
  620. // Otherwise get the value from the table
  621. else
  622. {
  623. segments = pgm_read_word_near(alphanumeric_segs + charPos);
  624. }
  625. return segments;
  626. }
  627. /*
  628. * Write a byte to the display.
  629. * Required for overloading the Print function.
  630. */
  631. size_t HT16K33::write(uint8_t b)
  632. {
  633. // If user wants to print '.' or ':', don't increment the digitPosition!
  634. if (b == '.' || b == ':')
  635. printChar(b, 0);
  636. else
  637. {
  638. printChar(b, digitPosition++);
  639. digitPosition %= (numberOfDisplays * 4); // Convert displays to number of digits
  640. }
  641. return (updateDisplay()); // Send RAM buffer over I2C bus
  642. }
  643. /*
  644. * Write a character buffer to the display.
  645. * Required for overloading the Print function.
  646. */
  647. size_t HT16K33::write(const uint8_t *buffer, size_t size)
  648. {
  649. size_t n = size;
  650. uint8_t buff;
  651. // Clear the displayRAM array
  652. for (uint8_t i = 0; i < 16 * numberOfDisplays; i++)
  653. displayRAM[i] = 0;
  654. digitPosition = 0;
  655. while (size--)
  656. {
  657. buff = *buffer++;
  658. // For special characters like '.' or ':', do not increment the digitPosition
  659. if (buff == '.')
  660. printChar('.', 0);
  661. else if (buff == ':')
  662. printChar(':', 0);
  663. else
  664. {
  665. printChar(buff, digitPosition);
  666. displayContent[digitPosition] = buff; // Record to internal array
  667. digitPosition++;
  668. digitPosition %= (numberOfDisplays * 4);
  669. }
  670. }
  671. updateDisplay(); // Send RAM buffer over I2C bus
  672. return n;
  673. }
  674. /*
  675. * Write a string to the display.
  676. * Required for overloading the Print function.
  677. */
  678. size_t HT16K33::write(const char *str)
  679. {
  680. if (str == NULL)
  681. return 0;
  682. return write((const uint8_t *)str, strlen(str));
  683. }
  684. // Push the contents of displayRAM out to the various displays in 16 byte chunks
  685. bool HT16K33::updateDisplay()
  686. {
  687. bool status = true;
  688. for (uint8_t i = 1; i <= numberOfDisplays; i++)
  689. {
  690. if (writeRAM(lookUpDisplayAddress(i), 0, (uint8_t *)(displayRAM + ((i-1) * 16)), 16) == false)
  691. {
  692. //Serial.print("updateDisplay fail at display 0x");
  693. //Serial.println(lookUpDisplayAddress(i), HEX);
  694. status = false;
  695. }
  696. }
  697. return status;
  698. }
  699. // Shift the display content to the right one digit
  700. bool HT16K33::shiftRight(uint8_t shiftAmt)
  701. {
  702. for (uint8_t x = (4 * numberOfDisplays) - shiftAmt; x >= shiftAmt; x--)
  703. {
  704. displayContent[x] = displayContent[x - shiftAmt];
  705. }
  706. // Clear the leading characters
  707. for (uint8_t x = 0; x < shiftAmt; x++)
  708. {
  709. if (x + shiftAmt > (4 * numberOfDisplays))
  710. break; // Error check
  711. displayContent[0 + x] = ' ';
  712. }
  713. return (print(displayContent));
  714. }
  715. // Shift the display content to the left one digit
  716. bool HT16K33::shiftLeft(uint8_t shiftAmt)
  717. {
  718. for (int x = 0; x < 4 * numberOfDisplays; x++)
  719. {
  720. if (x + shiftAmt > (4 * numberOfDisplays))
  721. break; // Error check
  722. displayContent[x] = displayContent[x + shiftAmt];
  723. }
  724. // Clear the trailing characters
  725. for (int x = 0; x < shiftAmt; x++)
  726. {
  727. if (4 * numberOfDisplays - 1 - x < 0)
  728. break; //Error check
  729. displayContent[4 * numberOfDisplays - 1 - x] = ' ';
  730. }
  731. return (print(displayContent));
  732. }
  733. /*----------------------- Internal I2C Abstraction -----------------------------*/
  734. bool HT16K33::readRAM(uint8_t address, uint8_t reg, uint8_t *buff, uint8_t buffSize)
  735. {
  736. uint8_t displayNum = 1;
  737. if (address == _deviceAddressDisplayTwo)
  738. displayNum = 2;
  739. else if (address == _deviceAddressDisplayThree)
  740. displayNum = 3;
  741. else if (address == _deviceAddressDisplayFour)
  742. displayNum = 4;
  743. isConnected(displayNum); // Wait until display is ready
  744. _i2cPort->beginTransmission(address);
  745. _i2cPort->write(reg);
  746. _i2cPort->endTransmission(false);
  747. if (_i2cPort->requestFrom(address, buffSize) > 0)
  748. {
  749. for (uint8_t i = 0; i < buffSize; i++)
  750. buff[i] = _i2cPort->read();
  751. return true;
  752. }
  753. return false;
  754. }
  755. // Write the contents of the RAM array out to the Holtek IC
  756. bool HT16K33::writeRAM(uint8_t address, uint8_t reg, uint8_t *buff, uint8_t buffSize)
  757. {
  758. uint8_t displayNum = 1;
  759. if (address == _deviceAddressDisplayTwo)
  760. displayNum = 2;
  761. else if (address == _deviceAddressDisplayThree)
  762. displayNum = 3;
  763. else if (address == _deviceAddressDisplayFour)
  764. displayNum = 4;
  765. isConnected(displayNum); //Wait until display is ready
  766. _i2cPort->beginTransmission(address);
  767. _i2cPort->write(reg);
  768. for (uint8_t i = 0; i < buffSize; i++)
  769. _i2cPort->write(buff[i]);
  770. if (_i2cPort->endTransmission() == 0)
  771. return true;
  772. return false;
  773. }
  774. // Write a single byte to the display. This is often a command byte.
  775. // The address of the data to write is contained in the first four bits of dataToWrite
  776. bool HT16K33::writeRAM(uint8_t address, uint8_t dataToWrite)
  777. {
  778. uint8_t temp = 0;
  779. return (writeRAM(address, dataToWrite, (uint8_t *)&temp, 0));
  780. }