(ANSWERED) Move cursor to second row through I2C I/O expander

I tried searching but couldn't find an answer other than things I have tried so I hope someone on here can help.

Currently with my project I am learning about I2C and LCD's so I'm displaying info on a HD44780 based 16x2 LCD through a MCP23017 16-bit I/O expander. Up until now things have been rocky but Ive been able to get through the initial hurdles and can command and write to the display. The problem is I cant seem to get my cursor to set to the second row first column. On the web I've found multiple references to 0x40 being the magical number to get to line two as the HD44780 is made to control a 40x4 LCD, however I dont seem to be having any luck getting it to move the cursor to the second row... So any Ideas?

for reference here is my code: (albeit messy with different data types and such, binary seems to be working best for testing so far)

#include "Wire.h"

//define commands
#define initalize B00001100
#define clearHome B00000001
#define blinkCurs B00001111
#define line1Curs B10000000
#define line2Curs B11000000


/////////////////////////////////////////////////////////////////
//                           Setup                             //
/////////////////////////////////////////////////////////////////
void setup()
{
  beginI2C();  
  beginLCD();
}

/////////////////////////////////////////////////////////////////
//                           Loop                              //
/////////////////////////////////////////////////////////////////
void loop()
{
  for(int i; i<16; i++)
  {
    writeLCD(B00110000);// "0"
    delay(500);
    commandLCD(0x40);//WTF!?! should be row 2 column 1!
    delay(500);
    writeLCD(B00110000);// "0"
    delay(500);
    commandLCD(B10000000);
  }  
  commandLCD(B00000001);//clear LCD and home cursor
}

/////////////////////////////////////////////////////////////////
//                        Functions                            //
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
void beginI2C()
{
  Wire.begin(); // wake up I2C bus

  // setup addressing style
  Wire.beginTransmission(0x20);
  Wire.write(0x12);
  Wire.write(0x20); // use table 1.4 addressing
  Wire.endTransmission();

  // set I/O pins to outputs
  Wire.beginTransmission(0x20);
  Wire.write(0x00); // IODIRA register
  Wire.write(0x00); // set all of bank A to outputs
  Wire.write(0x00); // set all of bank B to outputs
  Wire.endTransmission();
}

/////////////////////////////////////////////////////////////////
void beginLCD()
{
  delay(50);  
  commandLCD(initalize);//init first attempt
  commandLCD(initalize);//init second attempt
  commandLCD(initalize);//init final attempt
  commandLCD(clearHome);//clear LCD and home cursor
  commandLCD(blinkCurs);//display blinking cursor
}

//////////////////////////////////////////////////////////////////
void commandLCD(byte data)
{
  Wire.beginTransmission(0x20);
  Wire.write(0x12); // A register
  Wire.write(data); // Bank A
  Wire.write(B00000001); // Bank B
  Wire.endTransmission();
  
  Wire.beginTransmission(0x20);
  Wire.write(0x12); // A register
  Wire.write(data); // Bank A
  Wire.write(B00000000); // Bank B
  Wire.endTransmission();
  delay(50);
}

//////////////////////////////////////////////////////////////////
void writeLCD(byte data)
{
  Wire.beginTransmission(0x20);
  Wire.write(0x12); // A register
  //     pins 76543210
  Wire.write(data); // Bank A
  Wire.write(B00000011); // Bank B
  Wire.endTransmission();
  delay(50);
  
  Wire.beginTransmission(0x20);
  Wire.write(0x12); // A register
  Wire.write(data); // Bank A
  Wire.write(B00000000); // Bank B
  Wire.endTransmission();
  delay(50);
}

and data sheets in case there needed

commandLCD(0x40);//WTF!?! should be row 2 column 1!

The 0x40 refers to the address of the first character on the row. The command to set this address must have the high bit set. So, if everything else is correct (I haven't checked) then you should be using:

commandLCD(0xC0);//WTF!?! now it works!

Don

Seems like I must have deeper problems then, the cursor still is int showing up in the second row with 0xC0. Just disappearing as usual.. I will dig through the data sheet again and see if I find anything of use.

After a little bit of playing with it, I noticed that the cursor seems to be looping back to the home position when writing to the end of the row (since I cant see, I'm guessing it is reaching the end and looping). Any thoughts on if this may be a result of my initialization process, or putting it in this mode somehow?

I found my error, I didn't realize that I had to set the interface to a 2 row display. After doing so everything is working flawlessly. Regardless thanks for the help floresta.

I just took a look at your defines:

//define commands
#define initalize B00001100
#define clearHome B00000001
#define blinkCurs B00001111
#define line1Curs B10000000
#define line2Curs B11000000

-Your 'initialize' command looks incorrect to me. Since this is the command that you send three times to 'reset' the controller it should be 0x30 or B00110000.

-As you have seen you also need a 'function' command to set the number of lines. This would be 0x38 or B00111000. (You could also use this for 'initialize' since the controller ignores the lower four bits during reset). Make sure to send the 'function' command immediately after the three 'initialize' command and nowhere else in the program.

-Do you recognize 'line2Curs'?. It is 0x40 with the high bit set --> 0xC0, just what you should have been using before. Likewise 'line1Curs' is 0x00 with the high bit set --> 0x80.

Since you are dealing directly with the LCD controller (rather than using a library written by someone else) you might be interested in some more information about that controller. Follow the LCD Addressing and LCD Initialization links at http://web.alfredstate.edu/weimandn.

Don

I completely forgot to mention that I reversed the pin out from the I/O expander to the LCD because when I solder it together it will end up saving me a lot of time and effort. thats why the commands look wrong to you.

This is what I came up with to set the LCD to use 2 rows. and it seems to be working.(though IIRC the last two make no difference what state their in

#define interLeng B00111111

now I just use

#define line2Curs B11000000

and the cursor jumps to the second row :]

I'm not used to using hex so all these codes are troublesome for me to interpret, but I seem to be getting better the more I use it! Thanks for the info as well floresta, I'm sure it will be invaluable in the future.

... thats why the commands look wrong to you.

If I were you I would send the 'correct' commands and data their respective routines and then have those routines rearrange the bits before sending them on to the LCD via the MCP23017. Make sure to comment those manipulations so that you will understand why you did them when you look at your program several years from now.

This is what I came up with to set the LCD to use 2 rows. and it seems to be working.

#define interLeng B00111111

What does this magic number represent and how are you using it. It looks like a 'Function set' command but in that case why are bits 0, 1, and 2 set?

now I just use

#define line2Curs B11000000

This is the command to set the address to 0x40 as I pointed out in reply # 1.

address 40 in hex = 01000000 in binary
you create a command to set this address by setting the high bit ... and you get 11000000
11000000 in binary = C0 in hex

Don

I was thinking the same about reversing things after having that trouble. I will just go with the standard pin configuration from now on with this project to avoid future confusion.

#define interLeng B00111111

If you look at the datasheet for the HD44780 on page 24, table 6 there is a part called function set which is what I'm using to set the LCD into 2 row mode.

essentially B00111111
data bus 76543210

db5 is used to say your using the function set
db4 tells the LCD I'm sending in 8-bit
db3 tells the LCD how many lines
db2 says what size font
and the last 2 seem to be unused

so B00111100 would essential be the same I think, I have yet to test however

Your interpretation is correct. I just wanted to make sure that you knew that the lower three bits are normally set to '0'.

The font bit is normally set to '0' since none of the 2 or 4 line displays support the large font and only a few of the 1 line display support it either. The displays that do not support the large font seem to ignore this bit so you are OK. As far as bits 0 and 1 are concerned, normally programmers set unused bits to '0', but '1's don't hurt so you are OK there as well.

With the lower three bits set to '0' you would get a 'Function set' instruction of 00110000 (30 hex) for the very few 1-line displays that are actually configured as such and 00111000 (38 hex) for most 1-line displays and all 2 and 4 line displays.

For a 4-bit interface the corresponding values would be 00100000 (20 hex) and 00101000 (28 hex).

Don

[Edit]: I forgot about this:

I'm not used to using hex so all these codes are troublesome for me to interpret, but I seem to be getting better the more I use it!

In this case I feel that binary is more appropriate since the 'magic numbers' are bit mapped in the first place. In case you don't recognize the term it means that the individual bits have significance but the resulting magic number does not.

The Arduino interface allows you to use binary numbers whereas many 'C' compilers, for some unknown reason, do not. That is probably why the hex values are so frequently used and that is why I included them.

Just be thankful you aren't dealing with a compiler that requires Octal values. Your 00111111 would be 077 and 00110000 would be 060.