LCD Module

Description

This LCD Module is based on the New Haven Chip-On-Glass 128 x 32 pixel LCD. It operates at 3.3V and has an SPI interface running at a nominal 10MHz. The Module can be mounted using 4x40 screws. This project uses the GPIO and SPI tutorials.

I will make the PCB and BOM available or the fully made module when complete.

Pictures

Theory of operation

There were a few quirks I discovered about this LCD while trying to get it to work. As long as the SPI peripheral speed stays under 10MHz, the MCU does not need to check if the LCD is busy. The LCD expects a slave deselect after each byte sent whether or not it's data or a command. I didn't hardwire the reset pin to vcc. I connected it to an MCU pin so I could reset it before initialization. Also, when initializing the MCU the graphic memory needs to be put into a known state, as shown in the picture below.


LCD pinSAM4S Pin
Reset (active low)PA21
Slave Select (active low)PA20
Data or CMD PA19
MOSI PA13
SPI CLK PA14

Initialization Setup
  • Ensure SPI speed is below 10MHz
  • Hold reset pin low until just before initialization
  • Set the Bias to 1/9
  • Set ADC to normal
  • Set Display to reverse
  • Set the regulator resistor value
  • Set the power control, turn booster on, regulator on, and voltage follower on
  • Send the 'electronic volume' or contrast command
  • send the contrast value (0-63)
  • turn the display on
  • Set graphic memory to a known state

After initialization, to draw pixels, set page and column addresses and start sending bytes. 1 is pixel on and 0 is pixel off, unless the reverse option is set. As bytes are sent, the column will auto increment, but at the end of the 128 bits you will need to reset the column address back to 0. figure 4 on page 27 of the controller datasheet shows how page, column and line addresses work. datasheet

Schematic

BOM

RefDes Name Values Foot Print Quantity Manufacturer Number Digikey Part Number
PCB 1
C1, C2, C3, C4, C5 Capacitor 25V, 0.1uF, 5%, X7R 603 5 GRM188R71E104JA01D 490-9732-1-ND
C6, C7, C8 Capacitor 25V, 1uF, 10%, X7R 603 3 TMK107B7105KA-T 587-2984-1-ND
LCD1 LCD 128 x 32 pixels SPI Interface, 3V3 1 NHD-C12832A1Z-FSW-FBW-3V3 NHD-C12832A1Z-FSW-FBW-3V3-ND

Code

Here is main function as well as the LCD basic functions. The other include files are available as a zip download

Code Download

Main

#include "sam.h"
#include "NHD-C12832A1Z.h"
#include "SPI.h"
#include "systemclock.h"
#include "basic_uart.h"

uint8_t counter = 0; // counter for TC0
uint8_t manPosition = 0;
uint8_t manFrame = 0;

void timerInit(){
	//Setup for TC0 - ID 23, TIOA0 - PA0 peripheral B
	
	//enable interrupts in NVIC for TC0
	NVIC_EnableIRQ(TC0_IRQn);
	
	//PMC setup
	REG_PMC_PCER0 |= PMC_PCER0_PID23; 	//enable peripheral clock	for timer counter channel0
	
	//Interrupt Setup
	REG_TC0_CMR0 |= TC_CMR_TCCLKS_TIMER_CLOCK1; //mainclock div 2
	REG_TC0_IER0 |= TC_IER_COVFS; //enable couter overflow interrupt
	REG_TC0_CCR0 |= TC_CCR_CLKEN; //enable tc clock
	
	//PIO setup (not neccessary) because we won't use the pins
	
}

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();
	clock_init();	
	UART_Init();
	printString("uart init");
	//setup LCD pins (output)
	//give control to GPIO, not peripheral
	REG_PIOA_PER |= LCDDATACMDPIN | LCDSSPIN | LCDRESETPIN;
	//set as output
	REG_PIOA_OER |= LCDDATACMDPIN | LCDSSPIN | LCDRESETPIN;
	//set default state
	REG_PIOA_SODR |= PIO_SODR_P20;
	REG_PIOA_CODR |= PIO_CODR_P19 | PIO_CODR_P21;
	
	SPI_init();		
	NHD_init();	
	timerInit();
	//start our TC0
	REG_TC0_CCR0 |= TC_CCR_SWTRG;
	
    /* Replace with your application code */
    while (1) 
    {
		
		
    }
}


void TC0_Handler(void){
	
	uint32_t status = REG_TC0_SR0; //read status register - this clears interrupt flags
	if ((status & TC_SR_COVFS)>=1){
		//increment counter
		counter+=1;
		//printByte(counter);
	}
	
	if (counter>20){
		//reset counter
		counter=0;
		//Run Code
			//NHD_testPattern();
			NHD_clearScreen();
			NHD_displayMan(manPosition,manFrame);	
			manFrame++;
			manPosition+=5;
			if (manFrame >= 3){
				manFrame = 0;
			}
			if (manPosition >= 128){
				manPosition = 0;
			}
	}
}

NHD-C12832A1Z.h

#include "sam.h"
/*
SPI 10Mhz

explain RAM strucure, write sequence

Pin		Description
VCC		3V3
GND		Ground
DATA	SPI DATA
CLK		SPI CLOCK
D/C		Data or Command: 0: cmd, 1: data
SS		SLAVE SELECT: Active low
RES		RESET: Active low

*/
//ATSAM4S connection pins
#define LCDDATACMDPIN	PIO_CODR_P19
#define LCDSSPIN		PIO_CODR_P20
#define LCDRESETPIN		PIO_CODR_P21

//Define//
#define NHDCMD_DISPLAY_ON	0b10101111
#define NHDCMD_DISPLAY_OFF	0b10101110
#define NHDCMD_DISPLAY_START_LINE	0b01 << 6 // OR with line number, ie line 3 0b01000011
#define NHDCMD_PAGE_ADDRESS_SET		0b1011 << 4 // OR with page number
#define NHDCMD_COL_ADDRESS_SET		//????
/*
send two bytes
0001 MSNIBBLE
0000 LSNIBBLE
*/

#define NHDCMD_STATUS_READ	0b00000000 
/*
//send dummy, but read returned result
D7	BUSY	-	0: Not Busy, 1: Busy
D6	ADC		-	0: Reverse, 1: Normal
D5	ON/OFF	-	0: Dislay off, 1: Display on
D4	RESET	-	0: Operating State, 1: Reset in progress
*/

#define NHDCMD_ADC_ON	0b10100001
#define NHDCMD_ADC_OFF	0b10100000
/*
reverse column direction
*/


#define NHDCMD_DISPLAY_REVERSE_ON	0b10100111
#define NHDCMD_DISPLAY_REVERSE_OFF	0b10100110
/*
Invert colour
*/

#define NHDCMD_DISPLAY_ALL_POINTS_ON	0b10100101
#define NHDCMD_DISPLAY_ALL_POINTS_OFF	0b10100100
/*
Turns on all bits
*/

#define NHDCMD_LCD_BIAS_1_9		0b10100010
#define NHDCMD_LCD_BIAS_1_7		0b10100011
/*
Sets LCD bias
*/

#define NHDCMD_READ_MODIFY_WRITE	0b11100000
/*
used with END command unsure what it does
*/

#define NHDCMD_END	0b11101110
/*
clears NHDCMD_READ_MODIFY_WRITE
*/

#define NHDCMD_RESET	0b11100010
/*
reset LCD
*/

#define NHDCMD_COMMON_OUTPUT_MODE_SELECT_NORMAL		0b11000000
#define NHDCMD_COMMON_OUTPUT_MODE_SELECT_REVERSE	0b11001000
/*
COM output scan direction
*/

#define NHDCMD_POWER_CONTROL_SET	0b00101000 // OR with the following optionts
#define NHDCMD_BOOSTER_ON	0b100
#define NHDCMD_BOOSTER_OFF	0b000
#define NHDCMD_VOLTAGE_REGULATOR_ON	0b10
#define NHDCMD_VOLTAGE_REGULATOR_OFF	0b00
#define NHDCMD_VOLTAGE_FOLLOWER_ON	0b1
#define NHDCMD_VOLTAGE_FOLLOWER_OFF	0b0
/*
redo this
ie:
powerControlByte = NHDCMD_POWER_CONTROL_SET | NHDCMD_BOOSTER | NHCMD_VOLTAGE_REGULATOR_ON | NHCMD_VOLTAGE_FOLLOWER_OFF
*/

#define NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_MIN	0b00100000 //small
#define NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_1	0b00100001
#define NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_2	0b00100010
#define NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_3	0b00100011
#define NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_4	0b00100100
#define NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_5	0b00100101
#define NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_6	0b00100110
#define NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_MAX	0b00100111 //large

#define NHDCMD_ELECTRONIC_VOLUME	0b10000001
/*
not sure what this does
send command
send databyte
*/

#define NHD_PAGEADDRESS_0	0b10110000	
#define NHD_PAGEADDRESS_1	0b10110001	
#define NHD_PAGEADDRESS_2	0b10110010	
#define NHD_PAGEADDRESS_3	0b10110011	
#define NHD_PAGEADDRESS_4	0b10110100	
#define NHD_PAGEADDRESS_5	0b10110101
#define NHD_PAGEADDRESS_6	0b10110110	
#define NHD_PAGEADDRESS_7	0b10110111	
#define NHD_PAGEADDRESS_8	0b10111000	

#define NHDCMD_SLEEP_ON		0b10101100
#define NHDCMD_SLEEP_OFF	0b10101101
#define NHDCMD_INIT			0b00000000
/*
send two bytes one after the other
*/

//functions//
void NHD_init();//options as input

void NHD_slaveselect();
void NHD_slavedeselect();

void NHD_sendCommand(uint8_t command);

void NHD_sendData(uint8_t data);

uint8_t NHD_readData();//page and column address as input

void NHD_setPageAddress (uint8_t page);
void NHD_setColumnAddress (uint8_t column);

void NHD_clearScreen(); //resets all bits

void NHD_testPattern ();
void NHD_displayMan (uint8_t x, uint8_t frame);

NHD-C12832A1Z.c

#include "NHD-C12832A1Z.h"
#include "spi.h"
#include "lcd_images.h"



void NHD_init(){
	REG_PIOA_SODR |= PIO_SODR_P21; // reset pin high to stop reset
	NHD_sendCommand(NHDCMD_LCD_BIAS_1_9);
	NHD_sendCommand(NHDCMD_ADC_OFF);
	NHD_sendCommand(NHDCMD_COMMON_OUTPUT_MODE_SELECT_REVERSE);
	
	NHD_sendCommand(NHDCMD_VOLTAGE_REGULATOR_RESISTOR_RATIO_SET_1);
	NHD_sendCommand(NHDCMD_POWER_CONTROL_SET | NHDCMD_BOOSTER_ON | NHDCMD_VOLTAGE_REGULATOR_ON | NHDCMD_VOLTAGE_FOLLOWER_ON);
	NHD_sendCommand(NHDCMD_ELECTRONIC_VOLUME);
	NHD_sendCommand(0x2f);
	NHD_sendCommand(NHDCMD_DISPLAY_ON);
}

void NHD_slaveselect(){
	REG_PIOA_CODR |= PIO_CODR_P20;
	
}

void NHD_slavedeselect(){
	REG_PIOA_SODR |= LCDSSPIN;
}

void NHD_sendCommand(uint8_t command){
	REG_PIOA_CODR |= PIO_CODR_P20;
	REG_PIOA_CODR |= PIO_CODR_P19;
	SPI_byteExchange(command);
	REG_PIOA_SODR |= PIO_SODR_P20;
}

void NHD_sendData(uint8_t data){
	REG_PIOA_CODR |= PIO_CODR_P20;
	REG_PIOA_SODR |= PIO_SODR_P19;
	SPI_byteExchange(data);
	REG_PIOA_SODR |= PIO_SODR_P20;
}

void NHD_setPageAddress (uint8_t page){
	if (page == 0){
		NHD_sendCommand(NHD_PAGEADDRESS_0);
	}
	else if (page == 1){
		NHD_sendCommand(NHD_PAGEADDRESS_1);
	}
	else if (page == 2){
		NHD_sendCommand(NHD_PAGEADDRESS_2);
	}
	else if (page == 3){
		NHD_sendCommand(NHD_PAGEADDRESS_3);
	}
}

void NHD_setColumnAddress (uint8_t column){
	uint8_t colMSB = 0b00010000 | (column>>4);
	uint8_t colLSB = 0b00000000 | (column & 0b00001111);
	NHD_sendCommand(colMSB);
	NHD_sendCommand(colLSB);
}


void NHD_clearScreen(){
	NHD_sendCommand(0b01000000);
	for (uint8_t i=0; i<4; i++){
		NHD_setPageAddress(i);
		NHD_setColumnAddress (0);
		for (uint8_t j=0; j<128;j++){
			NHD_sendData(0b00000000);
		}
	}
}

void NHD_testPattern (){
	NHD_sendCommand(0b01000000);
	NHD_sendCommand(NHD_PAGEADDRESS_3);
	NHD_setColumnAddress (0);
	for (uint8_t i=0; i<8; i++){
		for (uint8_t x=0; x<8;x++){
			NHD_sendData(0b00000000);
		}
		for (uint8_t x=0; x<8;x++){
			NHD_sendData(0b11111111);
		}
	}
	
	NHD_sendCommand(NHD_PAGEADDRESS_2);
	NHD_setColumnAddress (0);
	for (uint8_t i=0; i<8; i++){
		for (uint8_t x=0; x<8;x++){
			NHD_sendData(0b11111111);
		}
		for (uint8_t x=0; x<8;x++){
			NHD_sendData(0b00000000);
		}
	}
	
	NHD_sendCommand(NHD_PAGEADDRESS_1);
	NHD_setColumnAddress (0);
	for (uint8_t i=0; i<8; i++){
		for (uint8_t x=0; x<8;x++){
			NHD_sendData(0b00000000);
		}
		for (uint8_t x=0; x<8;x++){
			NHD_sendData(0b11111111);
		}
	}
	
	NHD_sendCommand(NHD_PAGEADDRESS_0);
	NHD_setColumnAddress (0);
	for (uint8_t i=0; i<8; i++){
		for (uint8_t x=0; x<8;x++){
			NHD_sendData(0b11111111);
		}
		for (uint8_t x=0; x<8;x++){
			NHD_sendData(0b00000000);
		}
	}
	
}

void NHD_displayMan (uint8_t x, uint8_t frame){
	NHD_sendCommand(0b01000000);
	for (uint8_t i=0; i<4; i++){
		NHD_setPageAddress(i);
		NHD_setColumnAddress (x);
		for (uint8_t j=0; j<32;j++){
			NHD_sendData(runningMan[frame][j + (i*32)]);
		}
	}
}