Reputation: 89
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:
Microcontroller Data Sheet: https://www.ti.com/lit/ds/symlink/tm4c123gh6pm.pdf
LCD data sheet: https://circuitdigest.com/sites/default/files/HD44...
I/O Expander Data Sheet: https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF...
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:
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:
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.
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.
#include "TM4C123.h" // Device header
#include "RTE_Components.h" // 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->RCGCGPIO |= 0x10; //enable port E
GPIOE->DIR |= 0x10 | 0x20;
GPIOE->DEN |= 0x10 | 0x20; //enable pins pe4 and pe5
GPIOE->AFSEL = 0x10 | 0x20; //enable pins Pe4 and Pe5 for their alternate function
GPIOE->ODR |= 0x20; //pe5 is data pin, must be set to open drain
GPIOE->PCTL |= (3 << 16) | (3 << 20);
}
void I2C_enable(void){
SYSCTL->RCGCI2C |= 0x04;
I2C2->MCR |= 0x10; //initialize I2C master
GPIOE->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->MCS & 0x01 condition hangs forever
I2C2->MTPR = 0x09; //see data sheet: This initializes SCL speed to 100k kbps
I2C2->MSA = (0x27 << 1); //see data sheet: This sets slave address and sets mode to TRANSMIT
I2C2->MCS |= 0x07;
while (I2C2->MCS & 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->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->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = (1 << E); //set ENABLE to high
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = ~(1<<E); //set ENABLE to low
I2C2->MCS |= 0x07;
while (I2C2->MCS & 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 "Command Mode" 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 << 4;
byte_upper_nibble = byte & 0xF0;
I2C2->MDR = (mode | (I2C2->MDR & 0xF0)| byte_upper_nibble) | E; //set command to be most significant bit, and enable E
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01){
data_value = I2C2->MBMON;
}
delay_50_ms();
I2C2->MDR = (mode | (I2C2->MDR & 0xF0)| byte_upper_nibble) & ~E; //set command to be most significant bit, and disable E (pulsing E enables the command/data)
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
byte_lower_nibble = byte_shifted & 0xF0;
I2C2->MDR = (mode | (I2C2->MDR & 0x0F) | byte_lower_nibble) | E;
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = (mode | (I2C2->MDR & 0x0F) | byte_lower_nibble) & ~E;
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
}
//clock frequency is 1,000,000 cycles/second
void delay_50_ms(void){
SYSCTL->RCGCTIMER |= 0x01;
TIMER0->CTL = 0;
TIMER0->CFG |= 0x04;
TIMER0->TAMR |= 0x01 | 0x10; //single shot mode, enable interrupt
TIMER0->TAILR = 20000; //1,000,000 / 20,0000 = 50
TIMER0->CTL = 0x01;
while ((TIMER0->RIS & 0x01) == 0);
}
I am using the on-board 3.3V power supply from the TI board as my VCC supply.
Upvotes: 1
Views: 262
Reputation: 125
I think before discussing that the LCD is not working properly, I want to know whether I2C is working properly and whether the Address PIN define.
The address pin of I/O Expander IC is all connected to HIGH
, so your Slave address is 0x27
(I just want to make sure whether this part is okay because if there is a problem, there will be problems in the subsequent tests)
If I2C communication work normally, the I/O Expander IC control should be correct.
( Send some commands to see if the IC outputs according to your commands)
If the I/O expander IC work abnormally, maybe you can check the I2C signal use a logic analyzer or oscilloscope to check whether the I2C signal of the MCU is correct. (Slave Address, ACK...etc)
If the above of them is correct, we can start to check the LCD part!
Reminder: Your hyperlink is failed for the LCD and I/O Expander IC Data Sheet.
For the part of Initializing by internal reset circuit
It means that when you supply power to the LCD, it will perform the actions 1~4 below.
During this period, you can use an oscilloscope or logic analyzer to measure the BF pin. It should be HIGH, always When your VCC rises to 4.5V, BF will continue for another 10ms before pulling LOW.
But if VCC does not rise to 4.5V, it must be initialized through MPU.
I saw at the end of the article
I am using the on-board 3.3V power supply from the TI board as my VCC supply.
Does your LCD use 3.3V as the power supply? If yes, you shall initialize LCD by MPU. (But I suggest using the 5V to supply that can reduce the debug time.)
If using the internal initialize function, the LCD setting shall be like below:
So if you want to change to 4-bit
mode, need to use the Function set
Instruction to set again. (Datasheet P.28)
The signal transmission part must be tested according to the timing diagram
It takes 4 steps to send a command
Use the Example of the HD44780U Datasheet for testing (Datasheet P.42)
Step 6, need to be divided into 2 times to send.
And it will show the 'H' on the LCD.
Upvotes: 1