cancel
Showing results for 
Search instead for 
Did you mean: 

sample code of stm32 to initialize LCD(newhaven display)

rwmao
Senior
Posted on October 29, 2015 at 22:47

I have been using LCD (glass on chip) made by newhaven display for a while. They provided sample code to initialize. However the code is mostly for microchip, not for STM32 hal drive.

Some people have been stuck on it. After I figured it out, I got the tricky skill to initialize it. Here I post it on forum. Hope some people can benifit from it.

LCD is NHD-C0216CiZ-FN-FBW-3V,

http://www.newhavendisplay.com/nhdc0216cizfnfbw3v-p-2html

Keil 5.16a, DFP2.6.

Code is generated by stm32cubemx, in which trigger is NOT used for i2c. Therefore the code is for polling mode(or blocking mode). I believe the trigger mode or DMA mode should work as long as you switch the transmit function.

Here is the tricky thing.

Initialization For ST7032i *

*****************************************************/ 
void
init_LCD() 
{ 
I2C_Start(); 
I2C_out(0x7C); 
I2C_out(0x00); 
I2C_out(0x38); 
delay(10); 
I2C_out(0x39); 
delay(10); 
I2C_out(0x14); 
I2C_out(0x78); 
I2C_out(0x5E); 
I2C_out(0x6D); 
I2C_out(0x0C); 
I2C_out(0x01); 
I2C_out(0x06); 
delay(10); 
I2C_stop(); 
} 
/*****************************************************/

The sample code first initialize the i2c bus, and then send many commands, and finally close the bus, I2c_stop().

While in STM32, the transmit function to send a data to the i2c will complete the whole loop(i2c start, i2c stop).

The tricky thing is that

once you close the loop, you have to send a

control byte

again before you send an actual command. Therefore you have to send two byte(one is control byte and the other is command byte)together each time you send a command to the LCD. If you understand this, you have the key for LCD.

here is typical code to send a command:

// send one command char to the i2c. 
// it is very important that command control byte must be sent first to indicate the following byte is a command 
uint32_t SendOneByteCommandToI2C(uint8_t commandChar, uint8_t i2c_address) 
{ 
uint32_t rnt; 
uint8_t command[2]; 
//send two bytes, one is to prepare command write, followed by lcd_clear command 
command[0]=LCDcmdControlByte; 
//indicating for command writing. it is 0x00 for newhaven lcd 
command[1]=commandChar;
//followed by command char 
rnt= SendCommandToI2C(command,2,LCDi2cAddr);TM_DelayMicros(5); 
return
rnt; 
}

I attached the complete code for LCD initialization.

uint32_t SendCommandToI2C(uint8_t *pData, uint16_t size, uint8_t i2c_address) 
{ 
if
(HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)i2c_address, pData, size, 5000)!= HAL_OK) 
{ 
/* Error_Handler() function is called when Timeout error occurs. 
When Acknowledge failure occurs (Slave don't acknowledge it's address) 
Master restarts communication */
if
(HAL_I2C_GetError(&hi2c1) != HAL_I2C_ERROR_AF) 
{ 
errorcode=HAL_I2C_GetError(&hi2c1); 
sprintf(errormsg,
''Error occurs for LCD_clear. code=%d''
,errorcode); 
Error_Handler(errormsg); 
return
errorcode; 
} 
} 
return
0; 
} 
// send one command char to the i2c. 
// it is very important that command control byte must be sent first to indicate the following byte is a command 
uint32_t SendOneByteCommandToI2C(uint8_t commandChar, uint8_t i2c_address) 
{ 
uint32_t rnt; 
uint8_t command[2]; 
//send two bytes, one is to prepare command write, followed by lcd_clear command 
command[0]=LCDcmdControlByte; 
//indicating for command writing 
command[1]=commandChar;
//followed by command char 
rnt= SendCommandToI2C(command,2,LCDi2cAddr);TM_DelayMicros(5); 
return
rnt; 
} 
// **************************** i2c polling definition ************** 
//LCD Display Section (for NHD-C0216CiZ-FN-FBW-3V) 
/*****************************************************/
//********************************************************* 
/* write a 
byte
to the LCD 
in
8 bit mode */ 
//****************************************** 
// Clear and home the LCD 
void
lcd_clear(
void
) 
{ 
SendOneByteCommandToI2C(0x01,LCDi2cAddr);TM_DelayMicros(5); 
} 
//*************************************** 
// Go to the specified position 
void
lcd_goto(unsigned 
char
pos) 
{ 
uint8_t command[2]; 
//send two bytes, one is to prepare command write, followed by lcd_clear command 
command[0]=LCDcmdControlByte; 
//indicating for command writing 
command[1]=0x80+pos; 
//for pos<=15 
if
(pos>15) command[1]=0xC0+pos-16;
//2nd line. the first letter of 2nd line is 0xC0=0x80+0x40 
SendCommandToI2C(command,2,LCDi2cAddr);TM_DelayMicros(5); 
} 
//******************************************* 
// write a string of chars to the LCD 
void
lcd_puts(
const
char
* s, unsigned 
char
pos) 
{ 
lcd_goto(pos); 
//go to the starting point 
while
(*s) 
{ 
if
(pos >31) 
break
; 
//can't be larger than 31 
if
(pos == 16) 
{ 
lcd_goto(pos); 
} 
//go to the second line. 
lcdwritech(*s++); 
pos++; 
//cursor needs to update because it will shift when key in one char. 
} 
TM_DelayMillis(1); 
} 
//*************************************** 
//write a single char 
void
lcdwritech(
const
unsigned 
char
ch) 
{ 
uint8_t command[2]; 
//send two bytes, one is to prepare command write, followed by lcd_clear command 
command[0]=LCDDataControlByte; 
//indicating for command writing 
command[1]=(uint8_t)ch; 
SendCommandToI2C(command,2,LCDi2cAddr);TM_DelayMicros(5); 
} 
/**************************************************** 
* Initialization For ST7032i * 
*****************************************************/ 
void
InitNHDLCD() 
{ 
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); 
//enable the LCD. We use PB0 to control the reset pin of LCD 
// 
TM_DelayMillis(60); 
// *************** highly required, because it requires a little bit for booting up 
SendOneByteCommandToI2C(0x38,LCDi2cAddr); 
//wake up 
TM_DelayMillis(20); 
SendOneByteCommandToI2C(0x39,LCDi2cAddr); 
// Function set; 8 bit, 2 lines, Instruction table 1 
TM_DelayMillis(20); 
SendOneByteCommandToI2C(0x14,LCDi2cAddr);TM_DelayMillis(2); 
// Bias Set; BS 1/5; 2 lines 
SendOneByteCommandToI2C(0x70,LCDi2cAddr);TM_DelayMillis(2); 
//contrast 
SendOneByteCommandToI2C(0x5E,LCDi2cAddr);TM_DelayMillis(2); 
SendOneByteCommandToI2C(0x6D,LCDi2cAddr); TM_DelayMillis(2); 
// Voltage follower and gain share 
TM_DelayMillis(1); 
SendOneByteCommandToI2C(0x0c ,LCDi2cAddr); TM_DelayMillis(2); 
//SendOneByteCommandToI2C(0x70, LCDi2cAddr); // contrast . maximum light 
SendOneByteCommandToI2C(0x01,LCDi2cAddr); TM_DelayMillis(2); 
SendOneByteCommandToI2C(0x06,LCDi2cAddr); TM_DelayMillis(2); 
//the following is for testing only. it works 
strcpy(displayBuffer,
''UltrasonicNozzleFreq=199KHz.''
); 
lcd_puts(displayBuffer,0); 
//initial value 
} 
//************************************** 
// Hide Cursor. Indicating the display mode 
void
HideCursor(
void
) 
{ 
SendOneByteCommandToI2C(0x0c,LCDi2cAddr); 
} 
//*************************************** 
// Show cursor. Indicating the edit mode 
void
ShowCursor(unsigned 
char
cursor) 
{ 
SendOneByteCommandToI2C(0x0F,LCDi2cAddr); 
}

Also I attached the code for i2c initialization.

Actually not did anything revision. It was created by cubemx.

Low level Initialization of i2c defined in hal_map.c

/* USER CODE BEGIN I2C1_MspInit 0 */
/* USER CODE END I2C1_MspInit 0 */
/**I2C1 GPIO Configuration 
PB6 ------> I2C1_SCL 
PB7 ------> I2C1_SDA 
*/
//original setting 
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; 
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; 
//open drain, needs pull up resistor 
GPIO_InitStruct.Pull = GPIO_NOPULL;
// GPIO_PULLUP is also OK. We have a pull up resistor connected outside, so either one is OK 
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; 
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; 
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); 
// Peripheral clock enable 
__I2C1_CLK_ENABLE();

initialization code defined in main.c

/* I2C1 init function */
void
MX_I2C1_Init(
void
) 
{ 
hi2c1.Instance = I2C1; 
hi2c1.Init.ClockSpeed = 100000; 
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; 
hi2c1.Init.OwnAddress1 = 20; 
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; 
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED; 
hi2c1.Init.OwnAddress2 = 0; 
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED; 
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED; 
HAL_I2C_Init(&hi2c1); 
}

5 REPLIES 5
pradeepsysargus
Associate II
Posted on January 31, 2017 at 11:39

I am trying to go through code, I think there is a mistake in the function void 

lcd_goto().

You have given start address in DDRAM as 0x80, when I checked ur LCD data sheet, the first line start address is 0x00 and the second line start address is 0x40.

Please verify if possible.

Thanks,

pradeepsysargus
Associate II
Posted on January 31, 2017 at 13:48

I have an LCD based on ST7036i controller. The LCD is NHD-C0220BIZ-FS(RGB)-FBW-3VM.

http://www.newhavendisplay.com/specs/NHD-C0220BiZ-FSRGB-FBW-3VM.pdf

 

Following is the code so far:

#define LCD_ADDRESS 0x78

uint8_t commands[2] = { 0x00, 0x00 };

uint8_t data[2] = { 0x40, 0x00};

HAL_StatusTypeDef halstatus = 0;

void Init_LCD()

{

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5, GPIO_PIN_SET); // make LCD_RST high to start LCD module

HAL_Delay(60);

halstatus = HAL_I2C_IsDeviceReady(&hi2c1,LCD_ADDRESS,2,100);

printf('halstatus1 = %d\r\n',halstatus);

commands[1] = 0x38;

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus2 = %d\r\n',halstatus);

//HAL_Delay(20);

HAL_Delay(60);

commands[1] = 0x39;

halstatus = HAL_I2C_Master_Transmit(&hi2c1,LCD_ADDRESS, commands, 2, 100);

printf('halstatus3 = %d\r\n',halstatus);

HAL_Delay(60);

commands[1] = 0x14;

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus4 = %d\r\n', halstatus);

HAL_Delay(60);

commands[1] = 0x78;

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus5 = %d\r\n', halstatus);

HAL_Delay(60);

commands[1] = 0x5e;

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus6 = %d\r\n', halstatus);

//HAL_Delay(2);

HAL_Delay(60);

commands[1] = 0x6d;

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus7 = %d\r\n', halstatus);

HAL_Delay(60);

commands[1] = 0x0c;

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus8= %d\r\n', halstatus);

//HAL_Delay(2);

HAL_Delay(60);

commands[1] = 0x01;

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus9 = %d\r\n',halstatus);

HAL_Delay(60);

commands[1] = 0x06;

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus10 = %d\r\n', halstatus);

HAL_Delay(60);

}

void Show_LCD()

{

commands[1] = 0x80;        // First character position of first Line 

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, commands, 2, 100);

printf('halstatus11 = %d\r\n',halstatus);

HAL_Delay(60);

data[1] = 'a';   // 'a' character.

halstatus = HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDRESS, data, 2, 100);

printf('halstatus12 = %d\r\n', halstatus);

HAL_Delay(60);

}

void main()

{

// HAL initializations here

Init_LCD();

Show_LCD();

while(1)

{ }

}

I was looking to see a character 'a' (0x61 in hex) in the second line start position.

It does not up anything..

The output of the program is:

halstatus1 = 0

halstatus2 = 0

halstatus3 = 1

halstatus4= 0

halstatus5= 0

halstatus6= 0

halstatus7= 1

halstatus8= 0

halstatus9= 1

halstatus10= 0

halstatus11= 0

halstatus12= 1

I am getting HAL_ERROR or 1 value at 0x39 byte, 0x6d byte, 0x01 byte and 0x61 byte.

Why I am getting this error, please let me know.

Edit: The problem is now solved, I edited the above code to a working model. Although some command seem to fail, I am getting character 'a' at the first digit position of the first line, which it should be.

Thanks,

Posted on February 01, 2017 at 01:55

the reason it is 0x80 is that The highest bit is always 1 when set DDRAM, page 7 of manual.

Posted on February 01, 2017 at 02:14

I believe your problem is initialization. 

it needs some delay for the fist three command.

I would recommend just copying my code to test because those codes are in using with me. Very reliable.

Of course, you need to change i2c address.

If you didnt see anything on LCD, step in to check what is wrong.

Hardware: check every pin to make sure it is solid connection. Very often, the weird thing you may encounter actually comes from the hardware bug. For example, sometime ago, the power supply in one board not able to provide enough current. But I didn't notice that. The thing is that the code works. But after I add one more trival sentence somewhere the program completely changed,out of logic. It looks it is caused by software, actually not. After I fixed the power supply problem, the problem in software is gone. 

I have some bad experience for LCD too. The connection looks OK but not really good.

Check the power supply of the LCD. supply voltage in your case is 3.3v. 

Then software problem. Do you have a working code for i2c? Suppose i2c is not really hard to use. If it is not i2c problem, step in and debug. 

Jamesarm
Associate II
Posted on November 22, 2017 at 00:19

Ok, for those that have used the NHD-C0220Biz-FS(RGB)-FBW-3VM I have a stupid question. My display is not working and I verified the STM32 code is correct and even probe all the i2c addresses and see my i/o expander but never the display. I was verifying everything and I initially thought it was odd the numbering on the LCD is 8..1 from left to right instead of 1..8. So, I have RST on the far right pin and C1- on the far left pin. I am thinking the manufacture documentation is wrong. I found a schematic symbol and matching footprint on EasyEDA for the same display and it has C1- on the far right (opposite from what the manf. documentation shows). Can someone verify the pin layout on this display?