The SN74HC595N is an output expansion chip that could drive any devices, LED, relay or even writing data to LCD. A character LCD is easily controlled by this chip in write mode. A single chip SN74HC595N can control the HD44780 using its 4-bit data transfer mode.
In this example the ATMega644P master SPI send HD44780 LCD data and command to the LCD via an SN74HC595N shift registers chip.
![]() |
| A finished Assembling. SN74HC595N is soldered on-board. A 16x4 LCD stays at the top. |
![]() |
| Soldering side |
![]() |
| Schematic Diagram |
The original post uses Arduino Uno to control this LCD module.
Source Code "main.c":
- /*
- * 12-spi_74hc595_1604.c
- *
- * Created: 2/17/2026 3:27:49 PM
- * Author : Admin
- */
- #include <stdio.h>
- #include <avr/io.h>
- #include <util/delay.h>
- #define F_CPU 16000000UL
- #define DDR_SPI DDRB
- #define PRT_SPI PORTB
- #define DD_MOSI 5
- #define DD_MISO 6
- #define DD_SCK 7
- #define DD_SS 4
- void SPI_MasterInit(void)
- {
- /* Set MOSI and SCK output, all others input */
- DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
- /* Enable SPI, Master, set clock rate fck/16
- data is sample at the falling edge of SCK*/
- SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
- }
- void SPI_MasterTransmit(char cData)
- {
- /* Start transmission */
- SPDR = cData;
- /* Wait for transmission complete */
- while(!(SPSR & (1<<SPIF)))
- ;
- }
- void SPI_SlaveInit(void)
- {
- /* Set MISO output, all others input */
- DDR_SPI = (1<<DD_MISO);
- /* Enable SPI */
- SPCR = (1<<SPE);
- }
- char SPI_SlaveReceive(void)
- {
- /* Wait for reception complete */
- while(!(SPSR & (1<<SPIF)))
- ;
- /* Return Data Register */
- return SPDR;
- }
- /*TWI LCD Driver*/
- #define RS 1
- #define RW 2
- #define EN 3
- #define BL 0
- char backLight=0;
- void lcd_delay(unsigned int counts){
- for (unsigned int i=0;i<counts;i++);
- }
- void spi_lcd_command(char command){
- char data;
- data=command&0xF0;
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- SPI_MasterTransmit(data|(backLight<<BL));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- data=command<<4;
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN));
- PRT_SPI&=~(1<<DD_SS);
- lcd_delay(10);
- PRT_SPI|=(1<<DD_SS);
- SPI_MasterTransmit(data|(backLight<<BL));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- }
- void spi_lcd_data(char command){
- char data;
- data=command&0xF0;
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN)|(1<<RS));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<RS));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- lcd_delay(50);
- data=command<<4;
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN)|(1<<RS));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<RS));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- }
- void spi_lcd_xy(int8_t x, int8_t y){
- //16x2
- //char addr[]={0x80,0xC0};
- //16x4
- char addr[]={0x80,0xC0,0x90,0xD0};
- spi_lcd_command(addr[y-1]+x-1);
- }
- void spi_lcd_line_1(void){
- spi_lcd_command(0x80);
- }
- void spi_lcd_line_2(void){
- spi_lcd_command(0xC0);
- }
- void spi_lcd_line_3(void){
- spi_lcd_command(0x90);
- }
- void spi_lcd_line_4(void){
- spi_lcd_command(0xD0);
- }
- void spi_lcd_cursor_off(void){
- spi_lcd_command(0x0C);
- }
- void spi_lcd_text(char *txt){
- while(*txt) spi_lcd_data(*txt++);
- }
- void i2c_lcdClear(void){
- spi_lcd_command(0x01);
- _delay_ms(10);
- }
- void spi_lcd_init(void){
- SPI_MasterTransmit(0);
- lcd_delay(500);
- spi_lcd_command(0x33);
- lcd_delay(10);
- spi_lcd_command(0x32);
- lcd_delay(10);
- spi_lcd_command(0x28);
- lcd_delay(10);
- spi_lcd_command(0x0F);
- lcd_delay(10);
- spi_lcd_command(0x01);
- _delay_ms(10);
- spi_lcd_command(0x06);
- lcd_delay(10);
- }
- int main(void)
- {
- /* Replace with your application code */
- _delay_ms(1000);
- SPI_MasterInit();
- PRT_SPI|=(1<<DD_SS); //Set CS Pin High
- spi_lcd_init();
- spi_lcd_line_1();
- spi_lcd_text("ATMega644P SPI");
- spi_lcd_line_2();
- spi_lcd_text("SN74HC595N LCD");
- spi_lcd_line_3();
- spi_lcd_text("TC1604A-01(R)");
- spi_lcd_line_4();
- spi_lcd_text("Example Using C");
- unsigned char data[7],msg[16];
- while (1)
- {
- }
- }
AVR Hardware Experiment:
| Tested ATMega644P |
For a full tutorial list of ATMega644P using C in Microchip Studio IDE please see this page.
On-Board DS1307 Real Time Clock
I use a software TWI I wrote to read date and time from the on-board DS1307 RTC that will show on this SPI LCD.
| AVR ATMega644P Experiment Board |
Source Code "main.c":
- /*
- * ds1307_1604.c
- *
- * Created: 2/19/2026 2:09:32 PM
- * Author : Admin
- */
- #include <stdio.h>
- #include <avr/io.h>
- #include <util/delay.h>
- #define F_CPU 16000000UL
- #define DDR_SPI DDRB
- #define PRT_SPI PORTB
- #define DD_MOSI 5
- #define DD_MISO 6
- #define DD_SCK 7
- #define DD_SS 4
- void SPI_MasterInit(void)
- {
- /* Set MOSI and SCK output, all others input */
- DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
- /* Enable SPI, Master, set clock rate fck/16 */
- SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
- }
- void SPI_MasterTransmit(char cData)
- {
- /* Start transmission */
- SPDR = cData;
- /* Wait for transmission complete */
- while(!(SPSR & (1<<SPIF)))
- ;
- }
- void SPI_SlaveInit(void)
- {
- /* Set MISO output, all others input */
- DDR_SPI = (1<<DD_MISO);
- /* Enable SPI */
- SPCR = (1<<SPE);
- }
- char SPI_SlaveReceive(void)
- {
- /* Wait for reception complete */
- while(!(SPSR & (1<<SPIF)))
- ;
- /* Return Data Register */
- return SPDR;
- }
- /*TWI LCD Driver*/
- #define RS 1
- #define RW 2
- #define EN 3
- #define BL 0
- char backLight=0;
- void lcd_delay(unsigned int counts){
- for (unsigned int i=0;i<counts;i++);
- }
- void spi_lcd_command(char command){
- char data;
- data=command&0xF0;
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- SPI_MasterTransmit(data|(backLight<<BL));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- data=command<<4;
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN));
- PRT_SPI&=~(1<<DD_SS);
- lcd_delay(10);
- PRT_SPI|=(1<<DD_SS);
- SPI_MasterTransmit(data|(backLight<<BL));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- }
- void spi_lcd_data(char command){
- char data;
- data=command&0xF0;
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN)|(1<<RS));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<RS));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- lcd_delay(50);
- data=command<<4;
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN)|(1<<RS));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- SPI_MasterTransmit(data|(backLight<<BL)|(1<<RS));
- PRT_SPI&=~(1<<DD_SS);
- PRT_SPI|=(1<<DD_SS);
- }
- void spi_lcd_xy(int8_t x, int8_t y){
- //16x2
- //char addr[]={0x80,0xC0};
- //16x4
- char addr[]={0x80,0xC0,0x90,0xD0};
- spi_lcd_command(addr[y-1]+x-1);
- }
- void spi_lcd_line_1(void){
- spi_lcd_command(0x80);
- }
- void spi_lcd_line_2(void){
- spi_lcd_command(0xC0);
- }
- void spi_lcd_line_3(void){
- spi_lcd_command(0x90);
- }
- void spi_lcd_line_4(void){
- spi_lcd_command(0xD0);
- }
- void spi_lcd_cursor_off(void){
- spi_lcd_command(0x0C);
- }
- void spi_lcd_text(char *txt){
- while(*txt) spi_lcd_data(*txt++);
- }
- void spi_lcd_clear(void){
- spi_lcd_command(0x01);
- _delay_ms(10);
- }
- void spi_lcd_init(void){
- SPI_MasterTransmit(0);
- lcd_delay(500);
- spi_lcd_command(0x33);
- lcd_delay(10);
- spi_lcd_command(0x32);
- lcd_delay(10);
- spi_lcd_command(0x28);
- lcd_delay(10);
- spi_lcd_command(0x0F);
- lcd_delay(10);
- spi_lcd_command(0x01);
- _delay_ms(10);
- spi_lcd_command(0x06);
- lcd_delay(10);
- }
- // DS1307 RTC Routine
- const char DS1307_W=0xD0;
- const char DS1307_R=0xD1;
- void rtc_init(void){
- char rtc[8]={0x30,0x10,0x21,0x04,0x11,0x02,0x26,1<<4};
- for (char i=0;i<8;i++)
- {
- twi_start();
- //D0 is DS1307 Write Address
- twi_write(DS1307_W);
- //Select Control Register
- twi_write(i);
- //Enable SQWE bit blinks at 1 Hz
- twi_write(rtc[i]);
- twi_stop();
- _delay_ms(10);
- }
- }
- char rtc[7], msg[16];
- void rtc_read(void){
- for(char i=0;i<7;i++){
- /*Second Register*/
- twi_start();
- twi_write(DS1307_W);
- /*Select Second register*/
- twi_write(i);
- twi_stop();
- _delay_ms(10);
- twi_start();
- twi_write(DS1307_R);
- rtc[i]=twi_read();
- twi_stop();
- _delay_ms(10);
- }
- }
- int main(void)
- {
- /* Replace with your application code */
- SPI_MasterInit();
- PRT_SPI|=(1<<DD_SS); //Set CS Pin High
- spi_lcd_init();
- spi_lcd_line_1();
- spi_lcd_text("ATMega644P SPI");
- spi_lcd_line_2();
- spi_lcd_text("SN74HC595N LCD");
- spi_lcd_line_3();
- spi_lcd_text("TC1604A-01(R)");
- spi_lcd_line_4();
- spi_lcd_text("Real Time Clock");
- unsigned char data[7],msg[16];
- //rtc_init();
- _delay_ms(5000);
- spi_lcd_clear();
- spi_lcd_cursor_off();
- while (1)
- {
- rtc_read();
- spi_lcd_line_1();
- spi_lcd_text("DS1307 RTC Chip");
- spi_lcd_line_2();
- spi_lcd_line_2();
- spi_lcd_text("Software TWI");
- spi_lcd_line_3();
- sprintf(msg,"Time: %02X:%02X:%02X",rtc[2],rtc[1],rtc[0]);
- spi_lcd_text(msg);
- spi_lcd_line_4();
- sprintf(msg,"Date: %02X/%02X/20%02X",rtc[4],rtc[5],rtc[6]);
- spi_lcd_text(msg);
- }
- }
Its software TWI driver is place in separate file.
Source Code "twi.c":
- /*
- * twi.c
- *
- * Created: 2/19/2026 2:13:04 PM
- * Author: Admin
- */
- #include <avr/io.h>
- #define TWI_PORT PORTC
- #define TWI_SDA_IN PINC
- #define TWI_DIR DDRC
- const char SDA=1;
- const char SCL=0;
- const char PULSE=10;
- void delay_counts(unsigned int count){
- for(unsigned int i=0;i<count;i++);
- }
- void twi_start(void){
- TWI_DIR|=(1<<SDA)|(1<<SCL);
- TWI_PORT|=(1<<SDA)|(1<<SCL);
- delay_counts(PULSE);
- TWI_PORT&=~(1<<SDA);
- delay_counts(PULSE);
- TWI_PORT&=~(1<<SCL);
- delay_counts(PULSE);
- }
- void twi_stop(void){
- TWI_PORT&=~(1<<SCL);
- TWI_PORT&=~(1<<SDA);
- TWI_PORT|=(1<<SCL);
- delay_counts(PULSE);
- TWI_PORT|=(1<<SDA);
- delay_counts(PULSE);
- TWI_PORT|=(1<<SDA)|(1<<SCL);
- delay_counts(PULSE);
- }
- void twi_write(char data){
- char temp=0;
- TWI_DIR|=(1<<SDA)|(1<<SCL);
- for (unsigned char i=0;i<9;i++)
- {
- TWI_PORT&=~(1<<SCL);
- delay_counts(PULSE);
- if(i<8){
- temp=data&0x80;
- if(temp==0) TWI_PORT&=~(1<<SDA);
- else TWI_PORT|=(1<<SDA);
- }
- else{
- TWI_PORT&=~(1<<SDA);
- TWI_DIR&=~(1<<SDA);
- while(TWI_SDA_IN&(1<<SDA)==0);
- }
- TWI_PORT|=(1<<SCL);
- delay_counts(PULSE);
- data<<=1;
- }
- TWI_DIR|=(1<<SDA)|(1<<SCL);
- }
- char twi_read(void){
- char temp=0,data=0;
- TWI_DIR&=~(1<<SDA);
- for (unsigned char i=0;i<9;i++)
- {
- TWI_PORT&=~(1<<SCL);
- delay_counts(PULSE);
- if(i<8){
- /*
- data<<=1;
- temp=TWI_SDA_IN&(1<<SDA);
- if(temp==(1<<SDA)) data|=1;
- else data|=0;
- */
- temp=TWI_SDA_IN&(1<<SDA);
- temp>>=1;
- data|=(temp<<(7-i));
- }
- else{
- while((TWI_SDA_IN&(1<<SDA))==0);
- }
- TWI_PORT|=(1<<SCL);
- delay_counts(PULSE);
- }
- return data;
- }
Schematic and Simulation:
| Schematic |
AVR ATmega644P Experiment Board:
It works fine without error on my AVR Experiment Board.



No comments:
Post a Comment