title: Sending Data From TI TM4C123GH6PM to an HD4478 LCD with a PCF8574T I2C Backpack
body: <p>I am trying to write a program that will enable me to send data from my TI microcontroller to the very common HD4478 LCD. Rather than utilizing a parallel pin setup, I thought it would be a good idea to try and use a serial setup, so the LCD has a PCF8574T I2C I/O expander backpack. I am fairly new to writing embedded programs, and this is my first time using any real serial wiring communication protocols (I2C/SPI) so I'm struggling a bit to get this to work. Before I explain my confusion, here are the datasheets for the 3 respective components:</p>
  <p>Microcontroller Data Sheet: <a href="https://www.ti.com/lit/ds/symlink/tm4c123gh6pm.pdf" rel="nofollow noreferrer">https://www.ti.com/lit/ds/symlink/tm4c123gh6pm.pdf</a></p>
  <p>LCD data sheet: <a href="https://circuitdigest.com/sites/default/files/HD44.." rel="nofollow noreferrer">https://circuitdigest.com/sites/default/files/HD44..</a>.</p>
  <p>I/O Expander Data Sheet: <a href="https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF.." rel="nofollow noreferrer">https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF..</a>.</p>
  <p>My main problem is, I have no real idea what I'm doing wrong, but I am assuming it's something with the way I am initializing the LCD to 4-bit mode. I am a bit confused by this initialization walkthrough on the LCD data sheet:</p>
  <p><a href="https://i.stack.imgur.com/G9Hb2.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/G9Hb2.png" alt="HD4478 Initialization" /></a></p>
  <p>And then This latter explanation that further explains how to initialize it to 4 bit mode, which is what I'll be using with my I/O expander:</p>
  <p><a href="https://i.stack.imgur.com/rPTm2.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/rPTm2.png" alt="enter image description here" /></a></p>
  <p>I don't think I quite grasp whether to send the initial function set Command to 4-bit mode in 8-bit mode or 4-bit mode, and when the exact changeover to 4-bit mode occurs in the initialization process.</p>
  <p>The way I transfer data over the data line is the 4-bit mode, so I am sending the upper nibble. and then lower nibble. This goes for both data bytes and command bytes. Below is my code. Currently flashing it and running simply has no effect on the LCD. I know that I am operating at the correct slave address since I do not get any flags present after the initial slave address transmit that would indicate any error. However, all of my commands in I2C_LCD_Enable and the latter command in main() that attempts to send a singular character to the LCD have no effect as well.</p>
  <pre><code>#include &quot;TM4C123.h&quot;   // Device header
  #include &quot;RTE_Components.h&quot;             // Component selection
  
  //data pin should be open drain in i2c modules
  
  
  #define DATA 1
  
  #define COMMAND 0
  
  //I can think of each Pin connected to command/control as a singular bit
  
  
  void GPIOE_enable(void);
  void I2C_enable(void);
  
  void I2C_LCD_enable(void);
  void I2C_transfer_byte(char byte,int mode);
  
  void delay_50_ms(void);
  
  uint32_t address_transfer_value;
  uint32_t data_value;
  
  #define E 0x04 //bit 2
  
  int main(){
      GPIOE_enable();
      I2C_enable();
      I2C_LCD_enable();
      I2C_transfer_byte(0x01,COMMAND);
      I2C_transfer_byte(0x80,COMMAND); //set cursor to first row
      delay_50_ms();
      I2C_transfer_byte('a',DATA);
  }
  
  //port E, pins pe4 and pe5, have the alternative function as acting the clock/data lines for I2C module 2
  void GPIOE_enable(void){
      SYSCTL-&gt;RCGCGPIO |= 0x10; //enable port E
      GPIOE-&gt;DIR |= 0x10 | 0x20;
      GPIOE-&gt;DEN |= 0x10 | 0x20; //enable pins pe4 and pe5
      GPIOE-&gt;AFSEL = 0x10 | 0x20; //enable pins Pe4 and Pe5 for their alternate function
      GPIOE-&gt;ODR |= 0x20; //pe5 is data pin, must be set to open drain
      GPIOE-&gt;PCTL |= (3 &lt;&lt; 16) | (3 &lt;&lt; 20);
  }
  
  void I2C_enable(void){
      SYSCTL-&gt;RCGCI2C |= 0x04;
      I2C2-&gt;MCR |= 0x10; //initialize I2C master
      GPIOE-&gt;PUR |= 0x10 | 0x20; //I pulled up the Clock and Data Lines because they were low: Not pulling them up won't allow them to transfer from their high to low states when transmission begins and the I2C-&gt;MCS &amp; 0x01 condition hangs forever
      I2C2-&gt;MTPR = 0x09; //see data sheet: This initializes SCL speed to 100k kbps
      I2C2-&gt;MSA = (0x27 &lt;&lt; 1); //see data sheet: This sets slave address and sets mode to TRANSMIT
      I2C2-&gt;MCS |= 0x07;
      while (I2C2-&gt;MCS &amp; 0x01);
  }
  
  //HD775 data sheet explains the initialization process on pages 24 and 42
  void I2C_LCD_enable(void){
  
      //not sure how to initialize quite yet...
      /*I2C2-&gt;MDR = 0x28; //this initializes 4-bit mode. This command, AND THIS COMMAND ONLY, takes only one write since it's still in 8-bit mode
      I2C2-&gt;MCS |= 0x07;
      while (I2C2-&gt;MCS &amp; 0x01);
      delay_50_ms();
      I2C2-&gt;MDR = (1 &lt;&lt; E); //set ENABLE to high
      I2C2-&gt;MCS |= 0x07;
      while (I2C2-&gt;MCS &amp; 0x01);
      delay_50_ms();
      I2C2-&gt;MDR = ~(1&lt;&lt;E); //set ENABLE to low
      I2C2-&gt;MCS |= 0x07;
      while (I2C2-&gt;MCS &amp; 0x01);
      delay_50_ms();*/
      I2C_transfer_byte(0x28,COMMAND);
      I2C_transfer_byte(0x06,COMMAND); //Move cursor right
      I2C_transfer_byte(0x01,COMMAND); //clear screen
      I2C_transfer_byte(0x0F,COMMAND); //turn display on
  }
      
      
      
  
  //the upper 4 bits are the data pins : D4, D5, D6, D7 (bits 0-3)
  //the lower 3 bits are: RS, RW, E
  //to send a command, we should set RS to 0 to select &quot;Command Mode&quot; on LCD
  //if mode is 0, or COMMAND, do a logical OR with 0, which will set RS, bit 0, to 0
  //if mode is 1, or DATA, do a logical or with 1, so RS, bit 0, is set to 1
  //we also need to pulse E, or enable to make sure any of these data/commands actually are executed
  //The E pin corresponds to bit 2, so I'll send each data byte with first E enabled, and then E set to to low, to pulse Enable
  //send upper nibble, pulse enable, send lower nibble, pulse enable
  void I2C_transfer_byte(char byte,int mode){
              
      char byte_shifted;
      char byte_upper_nibble;
      char byte_lower_nibble;
      byte_shifted = byte &lt;&lt; 4;
      byte_upper_nibble = byte &amp; 0xF0;        
      I2C2-&gt;MDR = (mode | (I2C2-&gt;MDR &amp; 0xF0)| byte_upper_nibble) | E; //set command to be most significant bit, and enable E
      I2C2-&gt;MCS |= 0x07;
      while (I2C2-&gt;MCS &amp; 0x01){
          data_value = I2C2-&gt;MBMON;
      }
      delay_50_ms();
      I2C2-&gt;MDR = (mode | (I2C2-&gt;MDR &amp; 0xF0)| byte_upper_nibble) &amp; ~E; //set command to be most significant bit, and disable E (pulsing E enables the command/data)
      I2C2-&gt;MCS |= 0x07;
      while (I2C2-&gt;MCS &amp; 0x01);
      delay_50_ms();
      byte_lower_nibble = byte_shifted &amp; 0xF0;
      I2C2-&gt;MDR = (mode | (I2C2-&gt;MDR &amp; 0x0F) | byte_lower_nibble) | E;
      I2C2-&gt;MCS |= 0x07;
      while (I2C2-&gt;MCS &amp; 0x01);
      delay_50_ms();
      I2C2-&gt;MDR = (mode | (I2C2-&gt;MDR &amp; 0x0F) | byte_lower_nibble) &amp; ~E;
      I2C2-&gt;MCS |= 0x07;
      while (I2C2-&gt;MCS &amp; 0x01);
      delay_50_ms();
  }
  
  
  
  //clock frequency is 1,000,000 cycles/second
  void delay_50_ms(void){
      SYSCTL-&gt;RCGCTIMER |= 0x01;
      TIMER0-&gt;CTL = 0;
      TIMER0-&gt;CFG |= 0x04;
      TIMER0-&gt;TAMR |= 0x01 | 0x10; //single shot mode, enable interrupt
      TIMER0-&gt;TAILR = 20000; //1,000,000 / 20,0000 = 50
      TIMER0-&gt;CTL = 0x01;
      while ((TIMER0-&gt;RIS &amp; 0x01) == 0);
  }
      
  
  </code></pre>
  <p>I am using the on-board 3.3V power supply from the TI board as my VCC supply.</p>