728x90

728x90

Tuesday, February 17, 2026

ATMega644P SPI SN74HC595N TC1604A LCD

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.

ATMega644P SPI SN74HC595N TC1604A LCD 

 

 ATMega644P SPI SN74HC595N TC1604A LCD

In this example the ATMega644P master SPI send HD44780 LCD data and command to the LCD via an SN74HC595N shift registers chip.

ATMega644P SPI SN74HC595N TC1604A LCD
A finished Assembling. SN74HC595N is soldered on-board. A 16x4 LCD stays at the top.

 

ATMega644P SPI SN74HC595N TC1604A LCD
Soldering side

 

ATMega644P SPI SN74HC595N TC1604A LCD
Schematic Diagram

 The original post uses Arduino Uno to control this LCD module.

Source Code "main.c":

  1. /*
  2. * 12-spi_74hc595_1604.c
  3. *
  4. * Created: 2/17/2026 3:27:49 PM
  5. * Author : Admin
  6. */

  7. #include <stdio.h>
  8. #include <avr/io.h>
  9. #include <util/delay.h>
  10. #define F_CPU 16000000UL

  11. #define DDR_SPI DDRB
  12. #define PRT_SPI PORTB
  13. #define DD_MOSI 5
  14. #define DD_MISO 6
  15. #define DD_SCK 7
  16. #define DD_SS 4

  17. void SPI_MasterInit(void)
  18. {
  19. /* Set MOSI and SCK output, all others input */
  20. DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
  21. /* Enable SPI, Master, set clock rate fck/16
  22. data is sample at the falling edge of SCK*/
  23. SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
  24. }

  25. void SPI_MasterTransmit(char cData)
  26. {
  27. /* Start transmission */
  28. SPDR = cData;
  29. /* Wait for transmission complete */
  30. while(!(SPSR & (1<<SPIF)))
  31. ;
  32. }

  33. void SPI_SlaveInit(void)
  34. {
  35. /* Set MISO output, all others input */
  36. DDR_SPI = (1<<DD_MISO);
  37. /* Enable SPI */
  38. SPCR = (1<<SPE);
  39. }

  40. char SPI_SlaveReceive(void)
  41. {
  42. /* Wait for reception complete */
  43. while(!(SPSR & (1<<SPIF)))
  44. ;
  45. /* Return Data Register */
  46. return SPDR;
  47. }

  48. /*TWI LCD Driver*/
  49. #define RS 1
  50. #define RW 2
  51. #define EN 3
  52. #define BL 0


  53. char backLight=0;

  54. void lcd_delay(unsigned int counts){
  55. for (unsigned int i=0;i<counts;i++);
  56. }

  57. void spi_lcd_command(char command){
  58. char data;
  59. data=command&0xF0;
  60. SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN));
  61. PRT_SPI&=~(1<<DD_SS);
  62. PRT_SPI|=(1<<DD_SS);
  63. SPI_MasterTransmit(data|(backLight<<BL));
  64. PRT_SPI&=~(1<<DD_SS);
  65. PRT_SPI|=(1<<DD_SS);
  66. data=command<<4;
  67. SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN));
  68. PRT_SPI&=~(1<<DD_SS);
  69. lcd_delay(10);
  70. PRT_SPI|=(1<<DD_SS);
  71. SPI_MasterTransmit(data|(backLight<<BL));
  72. PRT_SPI&=~(1<<DD_SS);
  73. PRT_SPI|=(1<<DD_SS);
  74. }

  75. void spi_lcd_data(char command){
  76. char data;
  77. data=command&0xF0;
  78. SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  79. PRT_SPI&=~(1<<DD_SS);
  80. PRT_SPI|=(1<<DD_SS);
  81. SPI_MasterTransmit(data|(backLight<<BL)|(1<<RS));
  82. PRT_SPI&=~(1<<DD_SS);
  83. PRT_SPI|=(1<<DD_SS);
  84. lcd_delay(50);
  85. data=command<<4;
  86. SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  87. PRT_SPI&=~(1<<DD_SS);
  88. PRT_SPI|=(1<<DD_SS);
  89. SPI_MasterTransmit(data|(backLight<<BL)|(1<<RS));
  90. PRT_SPI&=~(1<<DD_SS);
  91. PRT_SPI|=(1<<DD_SS);
  92. }

  93. void spi_lcd_xy(int8_t x, int8_t y){
  94. //16x2
  95. //char addr[]={0x80,0xC0};
  96. //16x4
  97. char addr[]={0x80,0xC0,0x90,0xD0};
  98. spi_lcd_command(addr[y-1]+x-1);
  99. }

  100. void spi_lcd_line_1(void){
  101. spi_lcd_command(0x80);
  102. }

  103. void spi_lcd_line_2(void){
  104. spi_lcd_command(0xC0);
  105. }

  106. void spi_lcd_line_3(void){
  107. spi_lcd_command(0x90);
  108. }

  109. void spi_lcd_line_4(void){
  110. spi_lcd_command(0xD0);
  111. }

  112. void spi_lcd_cursor_off(void){
  113. spi_lcd_command(0x0C);
  114. }

  115. void spi_lcd_text(char *txt){
  116. while(*txt) spi_lcd_data(*txt++);
  117. }

  118. void i2c_lcdClear(void){
  119. spi_lcd_command(0x01);
  120. _delay_ms(10);
  121. }

  122. void spi_lcd_init(void){
  123. SPI_MasterTransmit(0);
  124. lcd_delay(500);
  125. spi_lcd_command(0x33);
  126. lcd_delay(10);
  127. spi_lcd_command(0x32);
  128. lcd_delay(10);
  129. spi_lcd_command(0x28);
  130. lcd_delay(10);
  131. spi_lcd_command(0x0F);
  132. lcd_delay(10);
  133. spi_lcd_command(0x01);
  134. _delay_ms(10);
  135. spi_lcd_command(0x06);
  136. lcd_delay(10);
  137. }

  138. int main(void)
  139. {
  140. /* Replace with your application code */
  141. _delay_ms(1000);
  142. SPI_MasterInit();
  143. PRT_SPI|=(1<<DD_SS); //Set CS Pin High
  144. spi_lcd_init();
  145. spi_lcd_line_1();
  146. spi_lcd_text("ATMega644P SPI");
  147. spi_lcd_line_2();
  148. spi_lcd_text("SN74HC595N LCD");
  149. spi_lcd_line_3();
  150. spi_lcd_text("TC1604A-01(R)");
  151. spi_lcd_line_4();
  152. spi_lcd_text("Example Using C");
  153. unsigned char data[7],msg[16];
  154. while (1)
  155. {
  156. }
  157. }





AVR Hardware Experiment:

ATMega644P SPI SN74HC595N TC1604A LCD
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. 

ATMega644P SPI SN74HC595N TC1604A LCD
AVR ATMega644P Experiment Board

 Source Code "main.c":

  1. /*
  2. * ds1307_1604.c
  3. *
  4. * Created: 2/19/2026 2:09:32 PM
  5. * Author : Admin
  6. */

  7. #include <stdio.h>
  8. #include <avr/io.h>
  9. #include <util/delay.h>
  10. #define F_CPU 16000000UL

  11. #define DDR_SPI DDRB
  12. #define PRT_SPI PORTB
  13. #define DD_MOSI 5
  14. #define DD_MISO 6
  15. #define DD_SCK 7
  16. #define DD_SS 4

  17. void SPI_MasterInit(void)
  18. {
  19. /* Set MOSI and SCK output, all others input */
  20. DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
  21. /* Enable SPI, Master, set clock rate fck/16 */
  22. SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
  23. }

  24. void SPI_MasterTransmit(char cData)
  25. {
  26. /* Start transmission */
  27. SPDR = cData;
  28. /* Wait for transmission complete */
  29. while(!(SPSR & (1<<SPIF)))
  30. ;
  31. }

  32. void SPI_SlaveInit(void)
  33. {
  34. /* Set MISO output, all others input */
  35. DDR_SPI = (1<<DD_MISO);
  36. /* Enable SPI */
  37. SPCR = (1<<SPE);
  38. }

  39. char SPI_SlaveReceive(void)
  40. {
  41. /* Wait for reception complete */
  42. while(!(SPSR & (1<<SPIF)))
  43. ;
  44. /* Return Data Register */
  45. return SPDR;
  46. }

  47. /*TWI LCD Driver*/
  48. #define RS 1
  49. #define RW 2
  50. #define EN 3
  51. #define BL 0


  52. char backLight=0;

  53. void lcd_delay(unsigned int counts){
  54. for (unsigned int i=0;i<counts;i++);
  55. }

  56. void spi_lcd_command(char command){
  57. char data;
  58. data=command&0xF0;
  59. SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN));
  60. PRT_SPI&=~(1<<DD_SS);
  61. PRT_SPI|=(1<<DD_SS);
  62. SPI_MasterTransmit(data|(backLight<<BL));
  63. PRT_SPI&=~(1<<DD_SS);
  64. PRT_SPI|=(1<<DD_SS);
  65. data=command<<4;
  66. SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN));
  67. PRT_SPI&=~(1<<DD_SS);
  68. lcd_delay(10);
  69. PRT_SPI|=(1<<DD_SS);
  70. SPI_MasterTransmit(data|(backLight<<BL));
  71. PRT_SPI&=~(1<<DD_SS);
  72. PRT_SPI|=(1<<DD_SS);
  73. }

  74. void spi_lcd_data(char command){
  75. char data;
  76. data=command&0xF0;
  77. SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  78. PRT_SPI&=~(1<<DD_SS);
  79. PRT_SPI|=(1<<DD_SS);
  80. SPI_MasterTransmit(data|(backLight<<BL)|(1<<RS));
  81. PRT_SPI&=~(1<<DD_SS);
  82. PRT_SPI|=(1<<DD_SS);
  83. lcd_delay(50);
  84. data=command<<4;
  85. SPI_MasterTransmit(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  86. PRT_SPI&=~(1<<DD_SS);
  87. PRT_SPI|=(1<<DD_SS);
  88. SPI_MasterTransmit(data|(backLight<<BL)|(1<<RS));
  89. PRT_SPI&=~(1<<DD_SS);
  90. PRT_SPI|=(1<<DD_SS);
  91. }

  92. void spi_lcd_xy(int8_t x, int8_t y){
  93. //16x2
  94. //char addr[]={0x80,0xC0};
  95. //16x4
  96. char addr[]={0x80,0xC0,0x90,0xD0};
  97. spi_lcd_command(addr[y-1]+x-1);
  98. }

  99. void spi_lcd_line_1(void){
  100. spi_lcd_command(0x80);
  101. }

  102. void spi_lcd_line_2(void){
  103. spi_lcd_command(0xC0);
  104. }

  105. void spi_lcd_line_3(void){
  106. spi_lcd_command(0x90);
  107. }

  108. void spi_lcd_line_4(void){
  109. spi_lcd_command(0xD0);
  110. }

  111. void spi_lcd_cursor_off(void){
  112. spi_lcd_command(0x0C);
  113. }

  114. void spi_lcd_text(char *txt){
  115. while(*txt) spi_lcd_data(*txt++);
  116. }

  117. void spi_lcd_clear(void){
  118. spi_lcd_command(0x01);
  119. _delay_ms(10);
  120. }

  121. void spi_lcd_init(void){
  122. SPI_MasterTransmit(0);
  123. lcd_delay(500);
  124. spi_lcd_command(0x33);
  125. lcd_delay(10);
  126. spi_lcd_command(0x32);
  127. lcd_delay(10);
  128. spi_lcd_command(0x28);
  129. lcd_delay(10);
  130. spi_lcd_command(0x0F);
  131. lcd_delay(10);
  132. spi_lcd_command(0x01);
  133. _delay_ms(10);
  134. spi_lcd_command(0x06);
  135. lcd_delay(10);
  136. }

  137. // DS1307 RTC Routine

  138. const char DS1307_W=0xD0;
  139. const char DS1307_R=0xD1;
  140. void rtc_init(void){
  141. char rtc[8]={0x30,0x10,0x21,0x04,0x11,0x02,0x26,1<<4};
  142. for (char i=0;i<8;i++)
  143. {
  144. twi_start();
  145. //D0 is DS1307 Write Address
  146. twi_write(DS1307_W);
  147. //Select Control Register
  148. twi_write(i);
  149. //Enable SQWE bit blinks at 1 Hz
  150. twi_write(rtc[i]);
  151. twi_stop();
  152. _delay_ms(10);
  153. }
  154. }

  155. char rtc[7], msg[16];
  156. void rtc_read(void){
  157. for(char i=0;i<7;i++){
  158. /*Second Register*/
  159. twi_start();
  160. twi_write(DS1307_W);
  161. /*Select Second register*/
  162. twi_write(i);
  163. twi_stop();
  164. _delay_ms(10);
  165. twi_start();
  166. twi_write(DS1307_R);
  167. rtc[i]=twi_read();
  168. twi_stop();
  169. _delay_ms(10);
  170. }
  171. }

  172. int main(void)
  173. {
  174. /* Replace with your application code */
  175. SPI_MasterInit();
  176. PRT_SPI|=(1<<DD_SS); //Set CS Pin High
  177. spi_lcd_init();
  178. spi_lcd_line_1();
  179. spi_lcd_text("ATMega644P SPI");
  180. spi_lcd_line_2();
  181. spi_lcd_text("SN74HC595N LCD");
  182. spi_lcd_line_3();
  183. spi_lcd_text("TC1604A-01(R)");
  184. spi_lcd_line_4();
  185. spi_lcd_text("Real Time Clock");
  186. unsigned char data[7],msg[16];
  187. //rtc_init();
  188. _delay_ms(5000);
  189. spi_lcd_clear();
  190. spi_lcd_cursor_off();
  191. while (1)
  192. {
  193. rtc_read();
  194. spi_lcd_line_1();
  195. spi_lcd_text("DS1307 RTC Chip");
  196. spi_lcd_line_2();
  197. spi_lcd_line_2();
  198. spi_lcd_text("Software TWI");
  199. spi_lcd_line_3();
  200. sprintf(msg,"Time: %02X:%02X:%02X",rtc[2],rtc[1],rtc[0]);
  201. spi_lcd_text(msg);
  202. spi_lcd_line_4();
  203. sprintf(msg,"Date: %02X/%02X/20%02X",rtc[4],rtc[5],rtc[6]);
  204. spi_lcd_text(msg);
  205. }
  206. }







Its software TWI driver is place in separate file.

Source Code "twi.c": 

  1. /*
  2. * twi.c
  3. *
  4. * Created: 2/19/2026 2:13:04 PM
  5. * Author: Admin
  6. */

  7. #include <avr/io.h>

  8. #define TWI_PORT PORTC
  9. #define TWI_SDA_IN PINC
  10. #define TWI_DIR DDRC

  11. const char SDA=1;
  12. const char SCL=0;
  13. const char PULSE=10;

  14. void delay_counts(unsigned int count){
  15. for(unsigned int i=0;i<count;i++);
  16. }

  17. void twi_start(void){
  18. TWI_DIR|=(1<<SDA)|(1<<SCL);
  19. TWI_PORT|=(1<<SDA)|(1<<SCL);
  20. delay_counts(PULSE);
  21. TWI_PORT&=~(1<<SDA);
  22. delay_counts(PULSE);
  23. TWI_PORT&=~(1<<SCL);
  24. delay_counts(PULSE);
  25. }

  26. void twi_stop(void){
  27. TWI_PORT&=~(1<<SCL);
  28. TWI_PORT&=~(1<<SDA);
  29. TWI_PORT|=(1<<SCL);
  30. delay_counts(PULSE);
  31. TWI_PORT|=(1<<SDA);
  32. delay_counts(PULSE);
  33. TWI_PORT|=(1<<SDA)|(1<<SCL);
  34. delay_counts(PULSE);
  35. }

  36. void twi_write(char data){
  37. char temp=0;
  38. TWI_DIR|=(1<<SDA)|(1<<SCL);
  39. for (unsigned char i=0;i<9;i++)
  40. {
  41. TWI_PORT&=~(1<<SCL);
  42. delay_counts(PULSE);
  43. if(i<8){
  44. temp=data&0x80;
  45. if(temp==0) TWI_PORT&=~(1<<SDA);
  46. else TWI_PORT|=(1<<SDA);
  47. }
  48. else{
  49. TWI_PORT&=~(1<<SDA);
  50. TWI_DIR&=~(1<<SDA);
  51. while(TWI_SDA_IN&(1<<SDA)==0);
  52. }
  53. TWI_PORT|=(1<<SCL);
  54. delay_counts(PULSE);
  55. data<<=1;
  56. }
  57. TWI_DIR|=(1<<SDA)|(1<<SCL);
  58. }

  59. char twi_read(void){
  60. char temp=0,data=0;
  61. TWI_DIR&=~(1<<SDA);
  62. for (unsigned char i=0;i<9;i++)
  63. {
  64. TWI_PORT&=~(1<<SCL);
  65. delay_counts(PULSE);
  66. if(i<8){
  67. /*
  68. data<<=1;
  69. temp=TWI_SDA_IN&(1<<SDA);
  70. if(temp==(1<<SDA)) data|=1;
  71. else data|=0;
  72. */
  73. temp=TWI_SDA_IN&(1<<SDA);
  74. temp>>=1;
  75. data|=(temp<<(7-i));
  76. }
  77. else{
  78. while((TWI_SDA_IN&(1<<SDA))==0);
  79. }
  80. TWI_PORT|=(1<<SCL);
  81. delay_counts(PULSE);
  82. }
  83. return data;
  84. }


Schematic and Simulation:

ATMega644P SPI SN74HC595N TC1604A LCD
Schematic

 AVR ATmega644P Experiment Board:

It works fine without error on my AVR Experiment Board.

ATMega644P SPI SN74HC595N TC1604A LCD 

 

ATMega644P SPI SN74HC595N TC1604A LCD 

 

 

 

 

No comments:

Post a Comment

320x50

Search This Blog

tyro-728x90