728x90

728x90

Tuesday, February 17, 2026

ATMega644P SPI and MCP23S17 LCD Matrix Keypad

In this post I use the TWI module of ATMega644P to interfaces with an MCP23017 GPIO expansion that able to control a character LCD and perform keypad scanning. However the SPI version MCP23S17 could do the same things like the TWI version MCP23017.

Using SPI the master MCU needs to four I/O pins unlike that TWI that use only two pins. However SPI offers a high speed data communication between master MCU and its slave devices.

MCP23S17 HD44780 LCD Driving

A single 8-bit port of this chip can control an HD44780 character LCD using LCD 4-bit data transfer mode similar to a direct control from an MCU output port.

ATMega644P SPI and MCP23S17 LCD Martix Keypad
Schematic

 Source Code "main.c":

  1. /*
  2. * 12-spi_mcp23s17_1602_kb.c
  3. *
  4. * Created: 2/17/2026 8:50:48 PM
  5. * Author : Admin
  6. */

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

  10. const char MCP23X17_W=0x40;
  11. const char MCP23X17_R=0x41;

  12. //IOCON.BANK=0
  13. enum BANK0{
  14. IODIRA=0,IODIRB,IPOLA,IPOLB,GPINTENA,GPINTENB,DEFVALA,
  15. DEFVALB,INTCONA,INTCONB,IOCON1,IOCON2,GPPUA,GPPUB,
  16. INTFA,INTFB,INTCAPA,INTCAPB,GPIOA,GPIOB,OLATA,OLATB
  17. };

  18. #define DDR_SPI DDRB
  19. #define PRT_SPI PORTB
  20. #define DD_MOSI 5
  21. #define DD_MISO 6
  22. #define DD_SCK 7
  23. #define DD_SS 4

  24. void SPI_MasterInit(void)
  25. {
  26. /* Set MOSI and SCK output, all others input */
  27. DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
  28. /* Enable SPI, Master, set clock rate fck/16 */
  29. SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
  30. }

  31. void SPI_MasterTransmit(char cData)
  32. {
  33. /* Start transmission */
  34. SPDR = cData;
  35. /* Wait for transmission complete */
  36. while(!(SPSR & (1<<SPIF)))
  37. ;
  38. }

  39. void SPI_SlaveInit(void)
  40. {
  41. /* Set MISO output, all others input */
  42. DDR_SPI = (1<<DD_MISO);
  43. /* Enable SPI */
  44. SPCR = (1<<SPE);
  45. }
  46. char SPI_SlaveReceive(void)
  47. {
  48. /* Wait for reception complete */
  49. while(!(SPSR & (1<<SPIF)))
  50. ;
  51. /* Return Data Register */
  52. return SPDR;
  53. }

  54. void mcp23s17_transmit(char address, char data){
  55. PRT_SPI&=~(1<<DD_SS);
  56. SPI_MasterTransmit(MCP23X17_W);
  57. SPI_MasterTransmit(address);
  58. SPI_MasterTransmit(data);
  59. PRT_SPI|=(1<<DD_SS);
  60. }

  61. char mcp23s17_receive(char address){
  62. PRT_SPI&=~(1<<DD_SS);
  63. SPI_MasterTransmit(MCP23X17_R);
  64. SPI_MasterTransmit(address);
  65. SPI_MasterTransmit(0x00);
  66. char data=SPI_SlaveReceive();
  67. PRT_SPI|=(1<<DD_SS);
  68. return data;
  69. }

  70. /*HD44780 LCD Section*/
  71. const char RS=0,RW=1,EN=2;
  72. char BLK_ON=0;

  73. const char LCD_PORT = OLATA;
  74. const char LCD_DIR = IODIRA;

  75. void lcd_command(uint8_t temp){
  76. uint8_t command,led;
  77. command=temp&0xF0;
  78. if(BLK_ON==1) led=0x08;
  79. else led=0;
  80. mcp23s17_transmit(LCD_PORT,command|led|(1<<EN));
  81. _delay_us(10);
  82. mcp23s17_transmit(LCD_PORT,command|led);
  83. _delay_us(25);
  84. command=temp<<4;
  85. mcp23s17_transmit(LCD_PORT,command|led|(1<<EN));
  86. _delay_us(10);
  87. mcp23s17_transmit(LCD_PORT,command|led);
  88. _delay_us(25);
  89. }

  90. void lcd_data(uint8_t temp){
  91. uint8_t data,led;
  92. data=temp&0xF0;
  93. if(BLK_ON==1) led=0x08;
  94. else led=0;
  95. mcp23s17_transmit(LCD_PORT,data|led|(1<<EN)|(1<<RS));
  96. _delay_us(10);
  97. mcp23s17_transmit(LCD_PORT,data|led|(1<<RS));
  98. _delay_us(25);
  99. data=temp<<4;
  100. mcp23s17_transmit(LCD_PORT,data|led|(1<<EN)|(1<<RS));
  101. _delay_us(10);
  102. mcp23s17_transmit(LCD_PORT,data|led|(1<<RS));
  103. _delay_us(25);
  104. }

  105. void lcd_xy(uint8_t x, uint8_t y){
  106. uint8_t cursor[]={0x80,0xC0};
  107. lcd_command(cursor[y-1]+x-1);
  108. }

  109. void lcd_line_1(void){
  110. lcd_command(0x80);
  111. }

  112. void lcd_line_2(void){
  113. lcd_command(0xC0);
  114. }

  115. void lcd_text(uint8_t *text){
  116. while(*text) lcd_data(*text++);
  117. }

  118. void lcd_clear(void){
  119. lcd_command(0x01);
  120. _delay_ms(5);
  121. }

  122. void lcd_init(void){
  123. BLK_ON=1;
  124. mcp23s17_transmit(LCD_DIR,0x00);
  125. lcd_command(0x33);
  126. _delay_us(10);
  127. lcd_command(0x32);
  128. _delay_us(10);
  129. lcd_command(0x28);
  130. _delay_us(10);
  131. lcd_command(0x0F);
  132. _delay_us(10);
  133. lcd_command(0x01);
  134. _delay_ms(5);
  135. lcd_command(0x06);
  136. _delay_us(10);
  137. }
  138. int main(void)
  139. {
  140. /* Replace with your application code */
  141. SPI_MasterInit();
  142. PRT_SPI|=(1<<DD_SS); //Set CS Pin High
  143. lcd_init();
  144. lcd_text("ATMega644P SPI");
  145. lcd_line_2();
  146. lcd_text("MCP23S17 GPIO");
  147. while (1)
  148. {
  149. }
  150. }




We can control a larger display for instance a 20x4 character LCD with additional display address modification.

MCP23S17 HD44780 LCD and Matrix Keypad

There is one I/O port left, the GPB. So I added a 4x4 matrix keypad to this port. The result of found key scanning will show on a 16x2 character LCD connects to GPA.

ATMega644P SPI and MCP23S17 LCD Martix Keypad
MCP23S17 HD44780 LCD and Matrix Keypad

 Source Code "main.c":

  1. /*
  2. * lcd_keyboard.c
  3. *
  4. * Created: 2/17/2026 11:07:07 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. /*SPI Setting*/

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

  18. void SPI_MasterInit(void)
  19. {
  20. /* Set MOSI and SCK output, all others input */
  21. DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
  22. /* Enable SPI, Master, set clock rate fck/16 */
  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. /*MCP23S17 Setting*/
  49. const char MCP23X17_W=0x40;
  50. const char MCP23X17_R=0x41;

  51. //IOCON.BANK=0
  52. enum BANK0{
  53. IODIRA=0,IODIRB,IPOLA,IPOLB,GPINTENA,GPINTENB,DEFVALA,
  54. DEFVALB,INTCONA,INTCONB,IOCON1,IOCON2,GPPUA,GPPUB,
  55. INTFA,INTFB,INTCAPA,INTCAPB,GPIOA,GPIOB,OLATA,OLATB
  56. };

  57. void mcp23s17_transmit(char address, char data){
  58. PRT_SPI&=~(1<<DD_SS);
  59. SPI_MasterTransmit(MCP23X17_W);
  60. SPI_MasterTransmit(address);
  61. SPI_MasterTransmit(data);
  62. PRT_SPI|=(1<<DD_SS);
  63. }

  64. char mcp23s17_receive(char address){
  65. PRT_SPI&=~(1<<DD_SS);
  66. SPI_MasterTransmit(MCP23X17_R);
  67. SPI_MasterTransmit(address);
  68. SPI_MasterTransmit(0x00);
  69. char data=SPI_SlaveReceive();
  70. PRT_SPI|=(1<<DD_SS);
  71. return data;
  72. }


  73. /*HD44780 LCD Section*/
  74. const char RS=0,RW=1,EN=2;
  75. char BLK_ON=0;

  76. const char LCD_PORT = OLATA;
  77. const char LCD_DIR = IODIRA;

  78. void lcd_command(uint8_t temp){
  79. uint8_t command,led;
  80. command=temp&0xF0;
  81. if(BLK_ON==1) led=0x08;
  82. else led=0;
  83. mcp23s17_transmit(LCD_PORT,command|led|(1<<EN));
  84. mcp23s17_transmit(LCD_PORT,command|led);
  85. command=temp<<4;
  86. mcp23s17_transmit(LCD_PORT,command|led|(1<<EN));
  87. mcp23s17_transmit(LCD_PORT,command|led);
  88. }

  89. void lcd_data(uint8_t temp){
  90. uint8_t data,led;
  91. data=temp&0xF0;
  92. if(BLK_ON==1) led=0x08;
  93. else led=0;
  94. mcp23s17_transmit(LCD_PORT,data|led|(1<<EN)|(1<<RS));
  95. mcp23s17_transmit(LCD_PORT,data|led|(1<<RS));
  96. data=temp<<4;
  97. mcp23s17_transmit(LCD_PORT,data|led|(1<<EN)|(1<<RS));
  98. mcp23s17_transmit(LCD_PORT,data|led|(1<<RS));
  99. }

  100. void lcd_xy(uint8_t x, uint8_t y){
  101. uint8_t cursor[]={0x80,0xC0};
  102. lcd_command(cursor[y-1]+x-1);
  103. }

  104. void lcd_line_1(void){
  105. lcd_command(0x80);
  106. }

  107. void lcd_line_2(void){
  108. lcd_command(0xC0);
  109. }

  110. void lcd_text(uint8_t *text){
  111. while(*text) lcd_data(*text++);
  112. }

  113. void lcd_clear(void){
  114. lcd_command(0x01);
  115. _delay_ms(5);
  116. }

  117. void lcd_init(void){
  118. BLK_ON=1;
  119. mcp23s17_transmit(LCD_DIR,0x00);
  120. lcd_command(0x33);
  121. _delay_us(10);
  122. lcd_command(0x32);
  123. _delay_us(10);
  124. lcd_command(0x28);
  125. _delay_us(10);
  126. lcd_command(0x0F);
  127. _delay_us(10);
  128. lcd_command(0x01);
  129. _delay_ms(5);
  130. lcd_command(0x06);
  131. _delay_us(10);
  132. }

  133. const char key_16[4][4]={'1','2','3','A',
  134. '4','5','6','B',
  135. '7','8','9','C',
  136. '*','0','#','D'};

  137. char key_scan(void){
  138. char data=0,temp,key;
  139. for(char i=0;i<4;i++){
  140. data=0xFF;
  141. data&=~(1<<i);
  142. mcp23s17_transmit(OLATB,data);
  143. _delay_ms(5);
  144. data=mcp23s17_receive(GPIOB);
  145. data&=0xF0;
  146. if((data&0x10)==0) {temp=key_16[i][0]; break;}
  147. else if((data&0x20)==0){temp=key_16[i][1]; break;}
  148. else if((data&0x40)==0){temp=key_16[i][2]; break;}
  149. else if((data&0x80)==0){temp=key_16[i][3]; break;}
  150. else temp=0;
  151. _delay_ms(10);
  152. }
  153. return temp;
  154. }

  155. void key_init(void){
  156. mcp23s17_transmit(IODIRB,0xF0);
  157. mcp23s17_transmit(GPPUB,0xF0);
  158. }

  159. int main(void)
  160. {
  161. /* Replace with your application code */
  162. SPI_MasterInit();
  163. PRT_SPI|=(1<<DD_SS);
  164. lcd_init();
  165. key_init();
  166. lcd_line_1(); lcd_text("ATMega644P SPI");
  167. lcd_line_2(); lcd_text("MCP23S17 LCD");
  168. char temp,charCount=0,newLine=0,line=1;
  169. _delay_ms(5000);
  170. lcd_clear();
  171. while (1)
  172. {
  173. temp=key_scan();
  174. if(temp!=0){
  175. lcd_data(temp);
  176. charCount++;
  177. _delay_ms(500);
  178. }
  179. if(charCount>16){
  180. newLine=1;
  181. charCount=0;
  182. line+=1;
  183. }
  184. if(newLine){
  185. newLine=0;
  186. if(line==2) lcd_xy(1,2);
  187. else{
  188. lcd_xy(1,1);
  189. lcd_command(0x01);
  190. _delay_ms(5);
  191. line=1;
  192. }
  193. }
  194. }
  195. }


For a full tutorial list of ATMega644P using C in Microchip Studio IDE please see this page.

 

 

 

No comments:

Post a Comment

320x50

Search This Blog

tyro-728x90