hd44780_I2Cexp.h 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. // vi:ts=4
  2. // ---------------------------------------------------------------------------
  3. // hd44780_I2Cexp.h - hd44780_I2Cexp i/o subclass for hd44780 library
  4. // Copyright (c) 2013-2018 Bill Perry
  5. // ---------------------------------------------------------------------------
  6. //
  7. // This file is part of the hd44780 library
  8. //
  9. // hd44780_I2Cexp is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU General Public License as published by
  11. // the Free Software Foundation version 3 of the License.
  12. //
  13. // hd44780_I2Cexp is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU General Public License
  19. // along with hd44780_I2Cexp. If not, see <http://www.gnu.org/licenses/>.
  20. //
  21. // ---------------------------------------------------------------------------
  22. //
  23. // It implements all the hd44780 library i/o methods to control an LCD based
  24. // on the Hitachi HD44780 and compatible chipsets using I2C extension
  25. // backpacks that use a simple I2C i/o expander chip.
  26. // Currently the PCF8574 or the MCP23008 are supported.
  27. //
  28. // The API functionality provided by this library class is compatible
  29. // with the API functionality of the Arduino LiquidCrystal library.
  30. //
  31. // The hd44780_I2Cexp constructor can specify all the parameters or let the
  32. // library auto configure itself.
  33. // hd44780_I2Cexp constructor can specifiy expander output bit assignments
  34. // or use pre-defined backpack board types
  35. // Some parameters can be left off when not used or to auto detect.
  36. //
  37. // examples:
  38. // hd44780_I2Cexp lcd; // autolocate/autoconfigure everything
  39. // hd44780_I2Cexp lcd(0x20); // autoconfigure for lcd at i2c address 0x20
  40. //
  41. // hd44780_I2Cexp lcdA; // autolocate/autoconfigure for lcd instance 0
  42. // hd44780_I2Cexp lcdB; // autolocate/autoconfigure for lcd instance 1
  43. //
  44. // hd44780_I2Cexp lcd(addr, chiptype, rs,[rw],en,d4,d5,d6,d7,bl,blpol);
  45. // hd44780_I2Cexp lcd(0x20, I2Cexp_MCP23008,1,2,3,4,5,6,7,HIGH); // no rw support
  46. // hd44780_I2Cexp lcd(0x27, I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH); // with rw support
  47. //
  48. // hd44780_I2Cexp lcd(addr, canned-entry);
  49. // hd44780_I2Cexp lcd(0x20, I2Cexp_BOARD_ADAFRUIT292); // specific backpack at 0x20
  50. // hd44780_I2Cexp lcd(0x38, I2Cexp_BOARD_SYDZ); // specific backpack at 0x38
  51. //
  52. // hd44780_I2Cexp lcdcanned-entry);
  53. // hd44780_I2Cexp lcd(I2Cexp_BOARD_SYDZ); // locate specific backpack
  54. //
  55. //
  56. // NOTES:
  57. // It is best to use autoconfigure if possible.
  58. // Intermixing autolocate and specific i2c addresss can create conflicts.
  59. // The library cannot autoconfigure the SYDZ backpack.
  60. // It will correctly identify the pin mapping but incorrectly determine
  61. // the backlight active level control.
  62. //
  63. // ---------------------------------------------------------------------------
  64. // History
  65. //
  66. // 2018.08.06 bperrybap - removed TinyWireM work around (TinyWireM was fixed)
  67. // 2017.12.23 bperrybap - added LiquidCrystal_I2C compatible constructor
  68. // 2017.05.12 bperrybap - now requires IDE 1.0.1 or newer
  69. // This is to work around TinyWireM library bugs
  70. // 2017.01.07 bperrybap - unknown address is now an address of zero
  71. // 2016.12.26 bperrybap - update comments for constructor usage
  72. // 2016.12.25 bperrybap - new constructor for canned entry with no address for auto locate
  73. // 2016.10.29 bperrybap - added sunrom canned entry
  74. // updated pcf8574 autoconfig comments
  75. // 2016.09.08 bperrybap - changed param order of iowrite() to match ioread()
  76. // 2016.08.06 bperrybap - changed iosend() to iowrite()
  77. // 2016.08.06 bperrybap - added ioread()
  78. // 2016.07.27 bperrybap - added return status for iosend()
  79. // 2016.07.21 bperrybap - merged all class code into header
  80. // 2016.07.20 bperrybap - merged into hd44780 library
  81. // 2016.06.15 bperrybap - added i2c probing delay for chipkit i2c issue
  82. // 2016.06.15 bperrybap - added getProp() diagnostic function
  83. // 2016.06.09 bperrybap - changed name from hd44780_IICexp to hd44780_I2Cexp
  84. // 2016.06.08 bperrybap - removed pre IDE 1.0 support
  85. // 2016.06.03 bperrybap - added smart execution delays
  86. // 2016.05.14 bperrybap - added support for multiple unknown address objects
  87. // 2016.04.01 bperrybap - added auto config & auto detect pin mapping
  88. // 2014.02.15 bperrybap - changed to use hd44780 base class library
  89. // 2013.06.01 bperrybap - initial creation
  90. //
  91. // @author Bill Perry - bperrybap@opensource.billsworld.billandterrie.com
  92. // ---------------------------------------------------------------------------
  93. #ifndef hd44780_I2Cexp_h
  94. #define hd44780_I2Cexp_h
  95. // A bug in TinyWireM is that requestFrom() returns incorrect status
  96. // so its return status can't be used. Instead the code will check the return
  97. // from Wire.read() which will return -1 if there no data was transfered.
  98. #if (ARDUINO < 101) && !defined(MPIDE)
  99. #error hd44780_I2Cexp i/o class requires Arduino 1.0.1 or later
  100. #endif
  101. // canned i2c board/backpack parameters
  102. // allows using:
  103. // hd44780_I2Cexp lcd(I2Cexp_BOARD_XXX); // auto locate
  104. // hd44780_I2Cexp lcd(i2c_address, I2Cexp_BOARD_XXX); // explicit i2c address
  105. // instead of specifying all individual parameters.
  106. // Note: some boards tie the LCD r/w line directly to ground
  107. // boards that have control of the LCD r/w line will be able to do reads from lcd.
  108. //
  109. // The underlying hd4480_I2Cexp constructors support
  110. // with or without r/w control, and with and without backlight control.
  111. // - If r/w control is not desired, simply leave off the r/w pin from the constructor.
  112. // - If backlight control is not desired/supported, simply leave off backlight pin and active level
  113. //
  114. // Since the library has to drive all 8 output pins, the boards that have
  115. // r/w tied to ground should use an unused output pin for the r/w signal
  116. // which will be set to LOW but ignored by the LCD on those boards.
  117. // This means that the unused pin on the i/o expander cannot be used as an input
  118. //
  119. // expType, rs[,rw],en,d4,d5,d6,d7[,bl, blpol]
  120. #define I2Cexp_BOARD_LCDXIO I2Cexp_PCF8574, 4,5,6,0,1,2,3 // ElectroFun default (no backlight control)
  121. #define I2Cexp_BOARD_LCDXIOnBL I2Cexp_PCF8574, 4,5,6,0,1,2,3,7,LOW // Electrofun & PNP transistor for BL
  122. #define I2Cexp_BOARD_MJKDZ I2Cexp_PCF8574, 6,5,4,0,1,2,3,7,LOW // mjkdz backpack
  123. #define I2Cexp_BOARD_GYI2CLCD I2Cexp_PCF8574, 6,5,4,0,1,2,3,7,LOW // GY-I2CLCD backpack
  124. #define I2Cexp_BOARD_LCM1602 I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,LOW // Robot Arduino LCM1602 backpack
  125. // (jumper forces backlight on)
  126. // these boards are all the same
  127. // and match/work with the hardcoded Arduino LiquidCrystal_I2C class
  128. #define I2Cexp_BOARD_YWROBOT I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH // YwRobot/DFRobot/SainSmart/funduino backpack
  129. #define I2Cexp_BOARD_DFROBOT I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH // YwRobot/DFRobot/SainSmart/funduino backpack
  130. #define I2Cexp_BOARD_SAINSMART I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH // YwRobot/DFRobot/SainSmart/funduino backpack
  131. #define I2Cexp_BOARD_FUNDUINO I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH // YwRobot/DFRobot/SainSmart/funduino backpack
  132. #define I2Cexp_BOARD_SUNROM I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH // YwRobot/DFRobot/SainSmart/funduino backpack
  133. // http://www.sunrom.com/p/i2c-lcd-backpack-pcf8574
  134. // not recommended
  135. #define I2Cexp_BOARD_SYDZ I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH // YwRobot/DFRobot/SainSmart/funduino backpack
  136. // SYDZ backpacks have a broken backlight circuit design.
  137. // the backlight active level can not be auto detected
  138. // It hooks the BL anode to the emitter rather than
  139. // hook the collector to the BL cathode.
  140. // The pullup on the base wont' be pulled low enough by
  141. // the backlight so the P3 pin will read high instead of low.
  142. // This breaks the autodetection.
  143. #define I2Cexp_BOARD_SY1622 I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH // This board uses a FET for backlight control
  144. // This breaks the autodetection.
  145. // MCP23008 based boards
  146. // Currently r/w control is disabled since most boards either can't do it, or have it disabled.
  147. #define I2Cexp_BOARD_ADAFRUIT292 I2Cexp_MCP23008,1,2,3,4,5,6,7,HIGH // Adafruit #292 i2c/SPI backpack in i2c mode (lcd RW grounded)
  148. // GP0 not connected to r/w so no ability to do LCD reads
  149. #define I2Cexp_BOARD_WIDEHK I2Cexp_MCP23008,4,7,0,1,2,3,6,HIGH // WIDE.HK mini backpack (lcd r/w hooked to GP5)
  150. #define I2Cexp_BOARD_LCDPLUG I2Cexp_MCP23008,4,6,0,1,2,3,7,HIGH // JeeLabs LCDPLUG (NOTE: NEVER use the SW jumper)
  151. // GP5 is hooked to s/w JP1 jumper, LCD RW is hardwired to gnd
  152. // So no ability to do LCD reads.
  153. #define I2Cexp_BOARD_EFREAK I2Cexp_MCP23008,7,6,5,4,3,2,1,HIGH // elecfreaks backpack. (lcd RW grounded)
  154. // very similar design to Adafruit board but uses different pin mapping
  155. // http://www.elecfreaks.com/store/i2ctwi-lcd1602-moduleblack-on-green-p-314.html
  156. // http://elecfreaks.com/store/download/datasheet/lcd/Char/IICshematic.pdf
  157. // http://www.elecfreaks.com/wiki/index.php?title=I2C/TWI_LCD1602_Module
  158. #define I2Cexp_BOARD_MLTBLUE I2Cexp_MCP23008,1,3,4,5,6,7,0,HIGH // i2c LCD MLT group "Blue Board" backpack
  159. // http://www.mlt-group.com/I2C-LCD-Blue-Board-for-Arduino
  160. // There is jumper on the board jp6 that controls how the
  161. // the board drives r/w.
  162. // It looks like by defualt r/w is wired to gnd and
  163. // can be changed by changing the solder jumper jp6.
  164. // however it isn't clear if that changes to GP2 or to Vcc.
  165. // It sounds like it changes to vcc with is REALLY dumb!
  166. //FIXME these can't go in the class unless they are referenced using the classname
  167. enum I2CexpType { I2Cexp_UNKNOWN, I2Cexp_PCF8574, I2Cexp_MCP23008 };
  168. class hd44780_I2Cexp : public hd44780
  169. {
  170. public:
  171. // ====================
  172. // === constructors ===
  173. // ====================
  174. // -- Automagic / auto-detect constructors --
  175. // Auto find next instance and auto config pin mapping
  176. hd44780_I2Cexp(){ _addr = 0; _expType = I2Cexp_UNKNOWN;}
  177. // Auto config specific i2c addr
  178. hd44780_I2Cexp(uint8_t addr){ _addr = addr; _expType = I2Cexp_UNKNOWN;}
  179. // Auto locate but with explicit config with r/w control and backlight control
  180. hd44780_I2Cexp(I2CexpType type, uint8_t rs, uint8_t rw, uint8_t en,
  181. uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7,
  182. uint8_t bl, uint8_t blLevel)
  183. {
  184. config(0, type, rs, rw, en, d4, d5, d6, d7, bl, blLevel); // auto locate i2c address
  185. }
  186. // -- undocumented LiquidCrystal_I2C compatible constructor
  187. // Note: auto locate i2c address is also supported by using address 0 (zero)
  188. // The init() function is also supported
  189. hd44780_I2Cexp(uint8_t addr, uint8_t cols, uint8_t rows) :
  190. hd44780(cols, rows), _addr(addr), _expType(I2Cexp_UNKNOWN) {}
  191. // -- Explicit constructors, specify address & pin mapping information --
  192. // constructor with r/w control without backlight control
  193. hd44780_I2Cexp(uint8_t i2c_addr, I2CexpType type, uint8_t rs, uint8_t rw, uint8_t en,
  194. uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7 )
  195. {
  196. config(i2c_addr, type, rs, rw, en, d4, d5, d6, d7);
  197. }
  198. // Constructor with r/w control with backlight control
  199. hd44780_I2Cexp(uint8_t i2c_addr, I2CexpType type, uint8_t rs, uint8_t rw, uint8_t en,
  200. uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7,
  201. uint8_t bl, uint8_t blLevel)
  202. {
  203. config(i2c_addr, type, rs, rw, en, d4, d5, d6, d7, bl, blLevel);
  204. }
  205. // Constructor without r/w control without backlight control
  206. hd44780_I2Cexp(uint8_t i2c_addr, I2CexpType type, uint8_t rs, uint8_t en,
  207. uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7 )
  208. {
  209. config(i2c_addr, type, rs, 0xff, en, d4, d5, d6, d7);
  210. }
  211. // Constructor without r/w control with backlight control
  212. hd44780_I2Cexp(uint8_t i2c_addr, I2CexpType type, uint8_t rs, uint8_t en,
  213. uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7,
  214. uint8_t bl, uint8_t blLevel)
  215. {
  216. config(i2c_addr, type, rs, 0xff, en, d4, d5, d6, d7, bl, blLevel);
  217. }
  218. // ============================================
  219. // === library specific diagnostic function ===
  220. // ============================================
  221. enum I2CexpProp
  222. {
  223. Prop_addr,
  224. Prop_expType,
  225. Prop_rs,
  226. Prop_rw,
  227. Prop_en,
  228. Prop_d4,
  229. Prop_d5,
  230. Prop_d6,
  231. Prop_d7,
  232. Prop_bl,
  233. Prop_blLevel,
  234. };
  235. int mask2bit(uint8_t mask)
  236. {
  237. for(uint8_t bit = 0; bit < 8; bit++)
  238. if(mask & (1 << bit))
  239. return(bit);
  240. // didn't find it, return error
  241. return(hd44780::RV_ENXIO);
  242. }
  243. int getProp(I2CexpProp propID)
  244. {
  245. switch(propID)
  246. {
  247. case Prop_addr:
  248. return(_addr);
  249. case Prop_expType:
  250. return((int)_expType);
  251. case Prop_rs:
  252. return(mask2bit(_rs));
  253. case Prop_rw:
  254. return(mask2bit(_rw));
  255. case Prop_en:
  256. return(mask2bit(_en));
  257. case Prop_d4:
  258. return(mask2bit(_d4));
  259. case Prop_d5:
  260. return(mask2bit(_d5));
  261. case Prop_d6:
  262. return(mask2bit(_d6));
  263. case Prop_d7:
  264. return(mask2bit(_d7));
  265. case Prop_bl:
  266. return(mask2bit(_bl));
  267. case Prop_blLevel:
  268. return(_blLevel);
  269. default:
  270. return(hd44780::RV_EINVAL);
  271. }
  272. }
  273. private:
  274. // ====================
  275. // === private data ===
  276. // ====================
  277. // expander pin mapping & state information
  278. uint8_t _addr; // I2C Address of the IO expander
  279. I2CexpType _expType; // I2C chip type used on the IO expander
  280. uint8_t _rs; // I2C chip IO pin mask for Register Select pin
  281. uint8_t _rw; // I2C chip IO pin mask for r/w pin
  282. uint8_t _en; // I2C chip IO pin mask for enable pin
  283. uint8_t _d4; // I2C chip IO pin mask for data d4 pin
  284. uint8_t _d5; // I2C chip IO pin mask for data d5 pin
  285. uint8_t _d6; // I2C chip IO pin mask for data d6 pin
  286. uint8_t _d7; // I2C chip IO pin mask for data d7 pin
  287. uint8_t _bl; // I2C chip IO pin mask for Backlight
  288. uint8_t _blLevel; // backlight active control level HIGH/LOW
  289. uint8_t _blCurState; // Current IO pin state mask for Backlight
  290. // ==================================================
  291. // === hd44780 i/o subclass virtual i/o functions ===
  292. // ==================================================
  293. // ioinit() - initialize the h/w
  294. // Returns non zero if initialization failed.
  295. //
  296. // can't be used from constructors because not everything is initalized yet.
  297. // (maybe interrupts or other library constructors)
  298. // if Wire library is used in constructor, it will hang.
  299. int ioinit()
  300. {
  301. int status = 0;
  302. // auto instance tracts inst number when creating multiple auto locate objects
  303. // this is static since it is for the entire class not per object.
  304. static uint8_t AutoInst;
  305. /*
  306. * First, initialize the i2c (Wire) library.
  307. * This really shouldn't be here
  308. * because Wire.begin() should only be called once, but
  309. * unfortunately, there is no way to know if anybody
  310. * else has called this.
  311. * I believe that it is unreasonable to require the the user
  312. * sketch code to do it, because all that should change between
  313. * hd44780 i/o interfaces should be the constructor
  314. * So we go ahead and call it here.
  315. */
  316. Wire.begin();
  317. // auto locate i2c expander and magically detect pin mappings
  318. if(!_addr) // locate next instance
  319. {
  320. _addr = LocateDevice(AutoInst++);
  321. }
  322. else
  323. {
  324. // check to see if device at specified address is really there
  325. Wire.beginTransmission(_addr);
  326. if(Wire.endTransmission())
  327. return(hd44780::RV_ENXIO);
  328. }
  329. if(!_addr) // locate next instance
  330. _addr = LocateDevice(AutoInst++);
  331. if(!_addr) // if we couldn't locate it, return error
  332. return(hd44780::RV_ENXIO);
  333. if(_expType == I2Cexp_UNKNOWN) // figure out expander chip if not told
  334. {
  335. _expType = IdentifyIOexp(_addr);
  336. if(_expType == I2Cexp_UNKNOWN) // coudn't figure it out?, return error
  337. return(hd44780::RV_EIO);
  338. if(_expType == I2Cexp_PCF8574)
  339. status = autocfg8574(); // go auto configure the pin mappings
  340. else
  341. status = autocfgMCP23008(); // go auto configure the pin mappings
  342. if(status)
  343. return(status);
  344. }
  345. // initialize IO expander chip
  346. Wire.beginTransmission(_addr);
  347. if(_expType == I2Cexp_MCP23008)
  348. {
  349. /*
  350. * First make sure to put chip into BYTE mode
  351. * BYTE mode is used to make a MCP23008 work more like PCF8574
  352. * In BYTE mode the address register does not increment so that
  353. * once you point it to GPIO you can write to it over and over again
  354. * within the same i2c connection by simply sending more bytes.
  355. * This is necessary as the code uses back to back writes to perform
  356. * the nibble updates as well as the toggling the enable signal.
  357. * This methodology offers significant performance gains.
  358. */
  359. Wire.write(5); // point to IOCON
  360. Wire.write(0x20);// disable sequential mode (enables BYTE mode)
  361. Wire.endTransmission();
  362. /*
  363. * Now set up output port
  364. */
  365. Wire.beginTransmission(_addr);
  366. Wire.write((uint8_t)0); // point to IODIR
  367. Wire.write((uint8_t)0); // all pins output
  368. Wire.endTransmission();
  369. /*
  370. * point chip to GPIO
  371. */
  372. Wire.beginTransmission(_addr);
  373. Wire.write(9); // point to GPIO
  374. }
  375. Wire.write((uint8_t)0); // Set the entire output port to LOW
  376. if( (status = Wire.endTransmission()) ) // assignment
  377. status = hd44780::RV_EIO;
  378. return ( status );
  379. }
  380. // ioread(type) - read a byte from LCD DDRAM
  381. //
  382. // returns:
  383. // success: 8 bit value read
  384. // failure: negative value: error or read not supported
  385. int ioread(hd44780::iotype type)
  386. {
  387. uint8_t gpioValue = _blCurState;
  388. uint8_t data = 0;
  389. int iodata;
  390. int rval = hd44780::RV_EIO;
  391. // If no address or expander type is unknown, then abort read w/error
  392. if(!_addr || _expType == I2Cexp_UNKNOWN)
  393. return(hd44780::RV_ENXIO);
  394. // reads for MCP23008 not yet supported
  395. if(_expType == I2Cexp_MCP23008)
  396. {
  397. return(hd44780::RV_ENOTSUP);
  398. }
  399. // check if reads supported
  400. if(!_rw)
  401. return(hd44780::RV_ENOTSUP);
  402. /*
  403. * ensure that previous LCD instruction finished.
  404. * There is a 45us offset since there will be at least 2 bytes
  405. * (the i2c address and the i/o expander data) transmitted over i2c
  406. * before the i/o expander i/o pins could be seen by the LCD.
  407. * (3 bytes on the MCP23008)
  408. * At 400Khz (max rate supported by the i/o expanders) 16 bits plus start
  409. * and stop bits is 45us.
  410. * So there is at least 45us of time overhead in the physical interface.
  411. */
  412. waitReady(-45);
  413. // put all the expander LCD data pins into input mode.
  414. // PCF8574 psuedo inputs use pullups so setting them to 1
  415. // makes them suitible for inputs.
  416. gpioValue |= _d4|_d5|_d6|_d7;
  417. // set RS based on type of read (data or status/cmd)
  418. if(type == hd44780::HD44780_IOdata)
  419. {
  420. gpioValue |= _rs; // RS high to read data reg
  421. }
  422. gpioValue |= _rw; // r/w high for reading
  423. // write all the bits to the expander port
  424. Wire.beginTransmission(_addr);
  425. Wire.write(gpioValue); // d4-d7 are inputs, RS, r/w high, E LOW
  426. if(Wire.endTransmission())
  427. goto returnStatus;
  428. // raise E to read the data.
  429. Wire.beginTransmission(_addr);
  430. Wire.write(gpioValue | _en); // Raises E
  431. if(Wire.endTransmission())
  432. goto returnStatus;
  433. // read the expander port to get the upper nibble of the byte
  434. Wire.requestFrom((int)_addr, 1);
  435. iodata = Wire.read();
  436. if(iodata < 0) // did we not receive a byte?
  437. goto returnStatus;
  438. Wire.beginTransmission(_addr);
  439. Wire.write(gpioValue); // lower E after reading nibble
  440. if(Wire.endTransmission())
  441. goto returnStatus;
  442. // map i/o expander port bits into upper nibble of byte
  443. if(iodata & _d4)
  444. data |= (1 << 4);
  445. if(iodata & _d5)
  446. data |= (1 << 5);
  447. if(iodata & _d6)
  448. data |= (1 << 6);
  449. if(iodata & _d7)
  450. data |= (1 << 7);
  451. Wire.beginTransmission(_addr);
  452. Wire.write(gpioValue | _en); // Raise E to read next nibble
  453. if(Wire.endTransmission())
  454. goto returnStatus;
  455. // read the expander port to get the lower nibble of the byte
  456. // We can't look at the return value from requestFrom() on the TineyWireM
  457. // library as it doesn't work like it is supposed to.
  458. // So we look at the return status from read() instead.
  459. Wire.requestFrom((int)_addr, 1);
  460. iodata = Wire.read();
  461. if(iodata < 0) // did we not receive a byte?
  462. goto returnStatus;
  463. Wire.beginTransmission(_addr);
  464. Wire.write(gpioValue); // lower E after reading nibble
  465. if(Wire.endTransmission())
  466. goto returnStatus;
  467. // map i/o expander port bits into lower nibble of byte
  468. if(iodata & _d4)
  469. data |= (1 << 0);
  470. if(iodata & _d5)
  471. data |= (1 << 1);
  472. if(iodata & _d6)
  473. data |= (1 << 2);
  474. if(iodata & _d7)
  475. data |= (1 << 3);
  476. rval = data;
  477. returnStatus:
  478. // try to put gpio port back to all outputs state with WR signal low for writes
  479. Wire.beginTransmission(_addr);
  480. Wire.write(_blCurState); // with E LOW
  481. if(Wire.endTransmission())
  482. rval = hd44780::RV_EIO;
  483. return(rval);
  484. }
  485. // iowrite(type, value) - send either command or data byte to lcd
  486. // returns zero on success, non zero on failure
  487. int iowrite(hd44780::iotype type, uint8_t value)
  488. {
  489. // If no address or expander type is unknown, then drop data
  490. if(!_addr || _expType == I2Cexp_UNKNOWN)
  491. return(hd44780::RV_ENXIO);
  492. /*
  493. * ensure that previous LCD instruction finished.
  494. * There is a 45us offset since there will be at least 2 bytes
  495. * (the i2c address and the i/o expander data) transmitted over i2c
  496. * before the i/o expander i/o pins could be seen by the LCD.
  497. * (3 bytes on the MCP23008)
  498. * At 400Khz (max rate supported by the i/o expanders) 16 bits plus start
  499. * and stop bits is 45us.
  500. * So there is at least 45us of time overhead in the physical interface.
  501. */
  502. waitReady(-45);
  503. // grab i2c bus
  504. Wire.beginTransmission(_addr);
  505. if(_expType == I2Cexp_MCP23008)
  506. {
  507. Wire.write(9); // point to GPIO
  508. }
  509. // send both nibbles in same i2c connection
  510. write4bits( (value >> 4), type ); // send upper nibble
  511. /*
  512. * "4 bit commands" are special.
  513. * They are used only during initalization and
  514. * are used to reliably get the LCD and host in nibble sync
  515. * with each other and to get the LCD into 4 bit mode.
  516. * When sending a "4 bit command" only the upper nibble of
  517. * of the 8 bit byte will presented on LCD signals D4-D7.
  518. */
  519. if(type != hd44780::HD44780_IOcmd4bit)
  520. {
  521. write4bits( (value & 0x0F), type); // lower nibble, if not 4bit cmd
  522. }
  523. if(Wire.endTransmission())
  524. return(hd44780::RV_EIO);
  525. return(hd44780::RV_ENOERR);
  526. }
  527. // iosetBacklight() - set backlight brightness
  528. // Since dimming is not supported, any non zero value
  529. // will turn on the backlight.
  530. int iosetBacklight(uint8_t dimvalue)
  531. {
  532. if(!_bl) // backlight control?
  533. return(hd44780::RV_ENOTSUP); // not backlight control support
  534. // dimvalue 0 is backlight off any other dimvalue is backlight on
  535. // configure backlight state mask according to active level
  536. if(((dimvalue) && (_blLevel == HIGH)) ||
  537. ((dimvalue == 0) && (_blLevel == LOW)))
  538. {
  539. _blCurState = _bl;
  540. }
  541. else
  542. {
  543. _blCurState = 0;
  544. }
  545. Wire.beginTransmission(_addr);
  546. if(_expType == I2Cexp_MCP23008)
  547. {
  548. Wire.write(9); // point to GPIO
  549. }
  550. Wire.write( _blCurState );
  551. if(Wire.endTransmission())
  552. return(hd44780::RV_EIO);
  553. return(hd44780::RV_ENOERR); // all is good
  554. }
  555. // ================================
  556. // === internal class functions ===
  557. // ================================
  558. // config() - save constructor parameters
  559. void config(uint8_t i2c_addr, I2CexpType i2c_type, uint8_t rs, uint8_t rw, uint8_t en,
  560. uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7,
  561. uint8_t bl=0xff, uint8_t blLevel=0xff )
  562. {
  563. // Save away config data into object
  564. _expType = i2c_type;
  565. _addr = i2c_addr;
  566. _rs = ( 1 << rs );
  567. if(rw < 8)
  568. _rw = (1 << rw);
  569. else
  570. _rw = 0; // no r/w control
  571. _en = ( 1 << en );
  572. // Initialise pin mapping
  573. _d4 = ( 1 << d4 );
  574. _d5 = ( 1 << d5 );
  575. _d6 = ( 1 << d6 );
  576. _d7 = ( 1 << d7 );
  577. if(bl < 8)
  578. _bl = ( 1 << bl );
  579. else
  580. _bl = 0; // no backlight control
  581. _blLevel = blLevel;
  582. // set default bl state to backlight on
  583. // if no _bl control, the _blCurState values will also be set to zero
  584. // so it doesn't turn on any other pins.
  585. //
  586. if(_bl && (blLevel == HIGH))
  587. _blCurState = _bl;
  588. else
  589. _blCurState = 0;
  590. }
  591. // LocateDevice() - Locate I2C expander device instance
  592. uint8_t LocateDevice(uint8_t instance)
  593. {
  594. uint8_t error, address;
  595. uint8_t locinst = 0;
  596. // 8 addresses for PCF8574 or MCP23008
  597. for(address = 0x20; address <= 0x27; address++ )
  598. {
  599. Wire.beginTransmission(address);
  600. error = Wire.endTransmission();
  601. // chipkit stuff screws up if you do beginTransmission() too fast
  602. // after an endTransmission()
  603. // below 20us will cause it to fail
  604. // ESP8286 needs to make sure WDT doesn't fire so we use delay()
  605. // The delay(1) is overkill and not needed for other chips, but it won't
  606. // hurt and the loop is only 8 addresses.
  607. delay(1);
  608. if(error == 0) // if no error we found something
  609. {
  610. if(locinst == instance)
  611. {
  612. #if 0
  613. if(IdentifyIOexp(address) == I2Cexp_UNKNOWN) // if we can't identify it, keep looking
  614. continue;
  615. #endif
  616. return(address);
  617. }
  618. locinst++;
  619. }
  620. }
  621. // 8 addresses for PCF8574A
  622. for(address = 0x38; address <= 0x3f; address++ )
  623. {
  624. Wire.beginTransmission(address);
  625. error = Wire.endTransmission();
  626. // chipkit stuff screws up if you do beginTransmission() too fast
  627. // after an endTransmission()
  628. // below 20us will cause it to fail.
  629. // ESP8286 needs to make sure WDT doesn't fire so we use delay()
  630. // The delay(1) is overkill and not needed for other chips, but it won't
  631. // hurt and the loop is only 8 addresses.
  632. delay(1);
  633. if(error == 0) // if no error we found something
  634. {
  635. if(locinst == instance)
  636. {
  637. #if 0
  638. if(IdentifyIOexp(address) == I2Cexp_UNKNOWN) // if we can't identify it, keep looking
  639. continue;
  640. #endif
  641. return(address);
  642. }
  643. locinst++;
  644. }
  645. }
  646. return(0); // could not locate expander instance
  647. }
  648. // IdentifyIOexp() - Identify I2C i/o expander device type
  649. // Currently PCF8574 or MCP23008
  650. I2CexpType IdentifyIOexp(uint8_t address)
  651. {
  652. int data;
  653. I2CexpType chiptype;
  654. /*
  655. * Identify PCF8574 vs MCP23008
  656. * On a PCF8574 1 bits turn on pullups and make the pin an input.
  657. * and 0 bits set the output pin to drive 0.
  658. * And a read always reads the port pins.
  659. *
  660. * Strategy:
  661. * - Write 0xff to MCP23008 IODIR reg (location 0) puts pins in input mode
  662. * - Point MCP23008 to IODIR register (location 0)
  663. * - Read 1 byte
  664. *
  665. * On a MCP23008 the read will return 0xff because it will read the
  666. * IODIR we just wrote
  667. * On a PCF8574 we should read a 0 since we last wrote zeros to all the
  668. * PORT bits
  669. *
  670. * NOTE:
  671. * The values 0xff and zero were carefully chosen so as to not create a bus
  672. * collision with the LCD data lines.
  673. * On a PCF8574 the 0xff will put all the port pins into input mode and the
  674. * the value 0x0 will ensure that the LCD is write mode even if the R/W
  675. * pin is connected to a PCF8574 pin both of these will ensure that the
  676. * PCF8574 is not ever driving a pin that
  677. * is connected to pin that the LCD is also driving.
  678. *
  679. * On a MCP23008, the write of 0xff to IODIR will put the port into input
  680. * mode.
  681. */
  682. /*
  683. * First try to write 0xff to MCP23008 IODIR
  684. * On a PCF8574 this will end up writing 0 and then ff to output port
  685. */
  686. Wire.beginTransmission(address);
  687. Wire.write((uint8_t) 0); // try to point to MCP23008 IODR
  688. Wire.write((uint8_t) 0xff); // try to write to MCP23008 IODR
  689. Wire.endTransmission();
  690. /*
  691. * Now try to point MCP23008 to IODIR for read
  692. * On a PCF8574 this will end up writing a 0 to the output port
  693. */
  694. Wire.beginTransmission(address);
  695. Wire.write((uint8_t) 0); // try to point to MCP23008 IODR
  696. Wire.endTransmission();
  697. /*
  698. * Now read a byte
  699. * On a MCP23008 we should read the 0xff we wrote to IODIR
  700. * On a PCF8574 we should read 0 since the output port was set to 0
  701. */
  702. Wire.requestFrom((int)address, 1);
  703. data = Wire.read();
  704. if(data == 0xff)
  705. {
  706. chiptype = I2Cexp_MCP23008;
  707. }
  708. else if(data == 0x00)
  709. {
  710. chiptype = I2Cexp_PCF8574;
  711. }
  712. else
  713. {
  714. chiptype = I2Cexp_UNKNOWN; // this shouldn't happen
  715. }
  716. return(chiptype);
  717. }
  718. /*
  719. * autocfg8574() - automagically figure out the pcf8574 pin mappings
  720. *
  721. * This code can auto detect 6 different 8574 backpack pin mappings.
  722. * This is all the known pin mappings.
  723. * Note: it does fail on the SYDZ bapack by incorrectly picking the backlight
  724. * active level because that backpack uses a pullup on the backlight transistor base.
  725. * SYDZ backpacks will have to be manually configured.
  726. *
  727. * Overview of how this works:
  728. *
  729. * When you set the i/o port to input (set port to 0xff) and read
  730. * the port, that the expander input pins with the en, rs, & di connections
  731. * will all float high from the expander input pullups.
  732. *
  733. * Also,
  734. * When there is no pullup/pulldown resistor on the backlight transistor base,
  735. * the 8574 bl pin as an input will be pulled to the direction of the emitter.
  736. * for active low backlights, the bl input pin will be high
  737. * for active high backlights, the bl input pin will be low.
  738. * When there is a pullup on the NPN base, it will still be pulled down low enough
  739. * to read as a low as long as the emitter is wired directly to ground.
  740. *
  741. * NOTE: While normally wiring an i/o pin directly to a NPN base when the emitter
  742. * is connected directly to ground would be bad when the i/o pin is driven high (it shorts),
  743. * It is not an issue with the PCF8574 since the PCF8574 does not drive i/o pins.
  744. * The PCF8574 will enable a pullup when the pin is set to "high". The path through
  745. * the transistor base, out the emitter to ground will pull the signal down to read as a low.
  746. *
  747. * All known 8574 backpacks appear to use either the upper or the lower
  748. * nibble for the data.
  749. * All the devices that use the upper nibble use the same bits
  750. * for rs (0), rw (1), and en (2), and backlight is bit 3
  751. *
  752. * The MAGIC is knowing how to use all this information to differenciate
  753. * between backpacks.
  754. *
  755. * The key is to attempt to do a bit of intelligent probing & "guessing"
  756. * We have to combine some initial observations of the pins when all signals
  757. * have a weak pullup on them and then
  758. * we have to actually guess which bit controls E, to try to
  759. * to get the LCD to stop driving the data lines. If the guess was correct,
  760. * since the 8574 has weak pullups, each data line will pull up.
  761. * If not, then we continue on with some other probing checks & guesses.
  762. *
  763. * Also of importance is that if RS should go low, the read will pull from
  764. * the status register instead of the data memory.
  765. * So if RS was lowered instead of E, then the 4 data bits will not all
  766. * float to 1s.
  767. *
  768. * So the summary:
  769. * If bits 0-2 are all 1s and bits 4-7 are 1s when bit 2 is off,
  770. * this determines that the upper 4 bits are lcd data lines and the lower 3 bits
  771. * are the lcd control lines with bit 2 being the E signal.
  772. * this means rs=0,rw=1,en=2,d4=4,d5=5,d6=6,d7=7,bl=3
  773. *
  774. * If bits 4-6 are all 1s and bits 0-3 are 1s when bit 4 is off
  775. * this determines that the lower 4 bits are lcd data lines and the upper 3 bits
  776. * are the lcd control lins with bit 4 being the E signal.
  777. * this means rs=6,rw=5,en=4,d4=0,d5=1,d6=2,d7=3,bl=7
  778. *
  779. * If bits 4-6 are all 1s and bits 0-3 are 1s when bit 6 is off
  780. * this determines that the lower 4 bits are lcd data lines and the upper 3 bits
  781. * are the lcd control lins with bit 6 being the E signal.
  782. * this means rs=4,rw=5,en=6,d4=0,d5=1,d6=2,d7=3,bl=7
  783. *
  784. * Anyting else, is "undetermined" - error
  785. *
  786. * Determiing backlight active level is easy as mentioned above.
  787. *
  788. * Auto active level detect fails when a FET is used.
  789. * Auto active level detect will also fail when a pullup resistor is used on
  790. * a NPN transistor base *AND* the BL anode is wired to the emitter instead wiring
  791. * the BL cathode to the colector.
  792. * With an FET there is no load betweent the 8574 bl pin and the FET gate,
  793. * so there is no way to distinguish this from a PNP emitter pulling up a
  794. * base pin.
  795. * When using a NPN transistor with a pullup, the pullup on the base will
  796. * not allow the base to be pulled in the direction of the emitter if the BL
  797. * anode is wired to the emitter.
  798. *
  799. *
  800. * As of now, the only known auto config failure is the SYDZ board since
  801. * it uses a pullup on the base and wired the backlight to the emitter instead
  802. * of the collector.
  803. * Because of this, the code will incorrecly pick active low backlight control.
  804. * Modifying the h/w to disable the pullup can be done, but is difficult due
  805. * to the way R1 resistors are used and the PCB layout.
  806. * It requires cuting a trace and dead bugging
  807. * a new resistor to connect to the base of the transistor.
  808. *
  809. * As a result the SYDZ backpack cannot use autoconfiguration.
  810. */
  811. int autocfg8574()
  812. {
  813. uint8_t data, data2;
  814. uint8_t rs, rw, en, d4, d5, d6, d7, bl, blLevel;
  815. // First put a 0xff in the output port
  816. Wire.beginTransmission(_addr);
  817. Wire.write((uint8_t) 0xff);
  818. Wire.endTransmission();
  819. // now read back from the port
  820. Wire.requestFrom((int)_addr, 1);
  821. data = Wire.read();
  822. // Turn off bit2 to attempt to see if en is bit 2,
  823. // if it is, it should change all lcd data bits to 1s
  824. Wire.beginTransmission(_addr);
  825. Wire.write((uint8_t) (~(1 << 2)) );
  826. Wire.endTransmission();
  827. // read back data
  828. Wire.requestFrom((int)_addr, 1);
  829. data2 = Wire.read();
  830. // If lower 3 bits are high and all 4 upper bits are high after clearing bit 2
  831. // then lower bits are control bits and upper bits are data bits
  832. // and bit2 was en.
  833. if( ((data & 0x7) == 7) && ((data2 & 0xf0) == 0xf0))
  834. {
  835. rs = 0; rw = 1; en = 2; d4 = 4; d5 = 5; d6 = 6; d7 = 7; bl = 3;
  836. if(data & 0x8) // bit 3 high so active low
  837. {
  838. // LCM1602
  839. // 0,1,2,4,5,6,7,3, LOW
  840. blLevel = LOW;
  841. }
  842. else
  843. {
  844. // YwRobot/DFRobot/SainSmart/funduino
  845. // 0,1,2,4,5,6,7,3, HIGH
  846. blLevel = HIGH;
  847. }
  848. }
  849. else if((data & 0x70) == 0x70)
  850. {
  851. // now we have either Electro fun LCDXIO or MJKDZ board
  852. // both use bit 7 for backlight control
  853. // Turn off the en bit which should change the data bits
  854. // bit 4 on the mjkdz is en so we try that bit
  855. Wire.beginTransmission(_addr);
  856. Wire.write((uint8_t) (~(1 << 4)) );
  857. Wire.endTransmission();
  858. // read back data
  859. Wire.requestFrom((int)_addr, 1);
  860. data2 = Wire.read();
  861. // look at data bits and see if they changed
  862. // if they changed to 0xf, then en was bit 4 and it is mjdkz
  863. // this will happen since when E is low the LCD is not
  864. // driving the bus pins so they will read as 1s since the
  865. // pullups in the PCF8574 will pull them up.
  866. if((data2 & 0xf) == 0xf)
  867. {
  868. // mjkdz
  869. rs = 6; rw = 5; en = 4; d4 = 0; d5 = 1; d6 = 2; d7 = 3; bl = 7;
  870. }
  871. else
  872. {
  873. // electrofun LCDXIO
  874. rs = 4; rw = 5; en = 6; d4 = 0; d5 = 1; d6 = 2; d7 = 3; bl = 7;
  875. }
  876. if(data & 0x80) // bit 7 high so active low
  877. {
  878. blLevel = LOW;
  879. }
  880. else
  881. {
  882. blLevel = HIGH;
  883. }
  884. }
  885. else
  886. {
  887. // couldn't figure it out
  888. return(hd44780::RV_ENOTSUP);
  889. }
  890. config(_addr, _expType, rs, rw, en, d4, d5, d6, d7, bl, blLevel);
  891. return(0);
  892. }
  893. /*
  894. * autocfgMCP23008() - automagically figure out the mcp23008 pin mappings
  895. *
  896. * - BOARD_ADAFRUIT292 - adafruit #292 I2C/"SPI"
  897. * - BOARD_WIDEHK - WideHK
  898. * - BOARD_LCDPLUG - Jeelabs LCDPlug
  899. * - BOARD_XXX - unknown vendor
  900. *
  901. * WARNING WARNING WARNING:
  902. * Because the MCP23008 has high drive and sink capabability on its i/o port
  903. * pins, it is possible that if this auto detection fails, that there could
  904. * be a bus contention between the MCP23008 and the LCD data lines.
  905. * Should this occur, it is possible that either one or both could be damaged.
  906. *
  907. * currently this code will not detect the LCDPLUG as it is similar to
  908. * adafruit #292 board.
  909. *
  910. * On the #292 board, the combination of the pullups with the 595 SR chip on
  911. * the data bus, and the r/w line hard wired to gnd, the data lines are all
  912. * pulled high, when the port is in input mode with pullups enabled.
  913. * GP0 is not stable without pullups as it is not connected to anything.
  914. * GP7 will pull down from the emitter of the NPN transistor.
  915. *
  916. * On the WIDEKH board,
  917. * GP6 should pull down from the emitter of the NPN transistor.
  918. * GP5 is wired to LCD RW signal
  919. *
  920. * On the BOARD_XXXX,
  921. * GP1 should pull down from the emitter of the NPN transistor.
  922. *
  923. * On the LCDPLUG,
  924. * GP7 will pull down from the emitter of the NPN transistor.
  925. * LCD RW signal is hardwired to ground.
  926. *
  927. * Given that the LCDPLUG is the only other board that uses GP7 for the
  928. * backlight control and all the known boards active HIGH control and GP7
  929. * is never used for a data line,
  930. * it is safe to assume that if GP7 is low then the board is either
  931. * LCDPLUG or ADAFRUIT292.
  932. * For now, if GP7 is low then assume the board is ADAFRUIT292
  933. *
  934. * Other untested probes:
  935. * If GP6 is low then the board is a WIDEHK
  936. * if GP1 is low then the board is the BOARD_XXX board
  937. * The BOARD_XXX may not work as GP1 is used for D5 on WIDEHK and LCDPLUG
  938. *
  939. *
  940. */
  941. int autocfgMCP23008()
  942. {
  943. uint8_t data;
  944. uint8_t rs, en, d4, d5, d6, d7, bl;
  945. uint8_t blLevel;
  946. /*
  947. * Now set up output port
  948. * Make no assumptions as to the state of IOCON BYTE mode
  949. */
  950. Wire.beginTransmission(_addr);
  951. Wire.write((uint8_t)0); // point to IODIR
  952. Wire.write(0xff); // all pins inputs
  953. Wire.endTransmission();
  954. Wire.beginTransmission(_addr);
  955. Wire.write(6); // point to GPPU
  956. Wire.write(0xff); // turn on pullups
  957. Wire.endTransmission();
  958. /*
  959. * read from the GPIO port
  960. */
  961. Wire.beginTransmission(_addr);
  962. Wire.write(9); // point to GPIO
  963. Wire.endTransmission();
  964. Wire.requestFrom((int)_addr, 1);
  965. data = Wire.read();
  966. blLevel = HIGH; // known boards are active HIGH bl
  967. if(data == 0x7f) // bit 7 low, and other control and data lines high
  968. {
  969. // board is either ADAFRUIT292 or LCDPLUG
  970. // for now, assume ADAFRUIT292
  971. rs = 1;
  972. // rw = 0; // not used
  973. en = 2;
  974. d4 = 3;
  975. d5 = 4;
  976. d6 = 5;
  977. d7 = 6;
  978. bl = 7;
  979. } else if(!(data & (1<<6))) // bit 6 low (untested)
  980. {
  981. // WIDEHK
  982. rs = 4;
  983. // rw = 5; // not used
  984. en = 7;
  985. d4 = 0;
  986. d5 = 1;
  987. d6 = 2;
  988. d7 = 3;
  989. bl = 6;
  990. } else if(!(data & (1<<1))) // bit 1 low (untested)
  991. {
  992. // BOARD_XXX
  993. rs = 7;
  994. // rw = 0; // not used
  995. en = 6;
  996. d4 = 5;
  997. d5 = 4;
  998. d6 = 3;
  999. d7 = 2;
  1000. bl = 1;
  1001. }
  1002. else
  1003. {
  1004. // could not identify board
  1005. return(hd44780::RV_ENOTSUP);
  1006. }
  1007. // currently writes are disabled for all MCP23008 devices
  1008. config(_addr, _expType, rs, 0xff, en, d4, d5, d6, d7, bl, blLevel);
  1009. return(0);
  1010. }
  1011. // write4bits - send a nibble to the LCD through i/o expander port
  1012. void write4bits(uint8_t value, hd44780::iotype type )
  1013. {
  1014. uint8_t gpioValue = _blCurState;
  1015. // convert the value to an i/o expander port value
  1016. // based on pin mappings
  1017. if(value & (1 << 0))
  1018. gpioValue |= _d4;
  1019. if(value & (1 << 1))
  1020. gpioValue |= _d5;
  1021. if(value & (1 << 2))
  1022. gpioValue |= _d6;
  1023. if(value & (1 << 3))
  1024. gpioValue |= _d7;
  1025. if(type == hd44780::HD44780_IOdata)
  1026. {
  1027. gpioValue |= _rs; // set RS high to send to data reg
  1028. }
  1029. // Cheat here by raising E at the same time as setting control lines
  1030. // This violates the spec but seems to work realiably.
  1031. Wire.write(gpioValue |_en); // with E HIGH
  1032. Wire.write(gpioValue); // with E LOW
  1033. }
  1034. }; // end of class definition
  1035. #endif