728x90

728x90

Sunday, February 15, 2026

ATMega644P SPI and MCP23S17 GPIO Expansion Example

Overview 

The MCP23S17 is an SPI 16-bit (two ports) bi-directional GPIO expansion chip. Its TWI version is the MCP23017. It's rich of functions, data direction, control, distinct input and output registers, programmable weak pull up resistors, interrupt capabilities etc. Its configuration setting is very similar to most of PIC micro-controller since it is produced by Microchip Technology.

It contains two 8-bit bi-directional port, GPA and GPB. Both of them are programmable with additional interrupt features. Its two interrupt flag pins correspond to each ports. 

ATMega644P SPI and MCP23S17 GPIO Expansion Example
MCP23S17-E/SP

 The SPI version of this chip is very similar to the I2C version.

ATMega644P SPI and MCP23S17 GPIO Expansion Example

Package Types

Its SPI interface is bi-directional since it's a general purpose I/O chip. Its four SPI pins are Chip Select(CS), Serial Clock(SCK), Serial In(SI) and Serial Out(SO). Its three optional address pins are A2:A0. We can connect them to GND to set the SPI slave write address to 0x40 and 0x41 for read address.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
Functional Block Diagram

 The programmer must configure the following registers to get a proper operation.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
Register Addressed

 By default we don't set the IOCON register the default bank is BANK0 ranges from 0x00 to 0x15. For example the IODIRB locates at 0x01 and its output register OLATB locates at 0x15.

ATMega644P SPI and MCP23S17 GPIO Expansion Example
SPI ADDRESSING REGISTERS

 The idle state of CS pin is high.

ATMega644 and MCP23S17 SPI Programming

The write operations contain three data bytes, slave write address, register address and data. For example to make GPIOA as output we must do the following step:

  1. Set CS pin low
  2. Send its slave address 0x40 (A2:A0=0)
  3. Send IODIRA address 0x00
  4. Send data direction 0x00 that is output direction
  5. Clear CS pin 

The read operation also contains three data bytes, slave read address, register address, any data and read operation.

 

 

 

Saturday, February 14, 2026

ATMega644P Serial Peripheral Interface (SPI) Tutorial

Overview

The Serial Peripheral Interface (SPI) is a high speed serial communication that commonly use three wires, serial clock, serial data and enable (master transmit only). Most of modern micro-controller has this serial communication interface. It is useful for board to board or to device communication due to wiring complexity compare to parallel port data transmission.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SN74HC595N LED Driving

SPI is popular among graphical display, Flash memory, I/O expansion chip, SRAM, EEPROM, real time clock chip etc. Its transmission and reception speed could reach up to several Mbits/s. These are some example of SPI devices:

  • DS3234 Real Time Clock
  • Nokia 5510 graphical LCD module
  • MCP23S17 I/O Expansion Chip
  • SN74HC595N Serial In Parallel Out Shift Registers
  • ILI9341/ST7785 240x320 TFT Display
  • ST7735 128x160 TFT Display
  • W25Q64JVSSIQ Flash Memory Chip
  • SD MMC Card etc. 
ATMega644P Serial Peripheral Interface (SPI) Tutorial
W25Q64JVSSIQ

 ATMega644P Serial Peripheral Interface (SPI) Tutorial

ATMega644P Serial Peripheral Interface (SPI) Tutorial
ILI9341/ST7785 240x320 TFT Display

 

Some earlier MCU doesn't have an SPI module inside for instance the AT89S52 or PIC16F84A. However the programmer could emulate a software SPI by using the bit-banging technique. This method is popular but it yield a lower speed data transmission and reception. 

Typically a system operation contains a master MCU that transmit and receive data from its connected SPI slave devices.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Single master to single slave: basic SPI wiring
 

The pin names of the SPI module in the AVR micro-controller are different from other MCU such as PIC. These pins are:

Abbr.NameDescription
SS
Slave Select
Active-low chip select signal from master to
enable communication with a specific slave device
SCLK
Serial Clock
Clock signal from master
MOSI
Master Out Slave In
Serial data output from master
MISO
Master In Slave Out
Serial data output from slave


The master MCU must select any connected SPI slave device via its slave select (SS) pin. One slave device has a unique SS pin. So multiple slave devices have many SS pins.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
A typical hardware setup using two shift registers to form an inter-chip circular buffer
The SS pin is active low or active high upon the specific device.

Its clock polarity is commonly positive (low to high). Somes device use a negative (high to low) clock polarity.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
 

However its clock polarity and phase are configured by user software that requires an understanding of technical detail of an MCU (Dedicated SPI Module).

The master MCU can communicates with many different SPI slave devices (Multidrop SPI bus) on a single bus using additional SS pins.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Multidrop SPI bus
 

If multiple SPI slave devices with the same type connect to a single SPI bus we can use the Daisy chain configuration. For instance the SN74HC595N or SN74HC164 serial in parallel out shift registers.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Daisy chain configuration

 It is used for expanding a number of output ports for instance driving a dot matrix display.

 Currently there are a lot of newer method of SPI data transmission that is precise and high speed.

ATMega644P Serial Peripheral Interface

The SPI module of the ATMega644P has the following features:

    Full-duplex, Three-wire Synchronous Data Transfer
    Master or Slave Operation
    LSB First or MSB First Data Transfer
     Seven Programmable Bit Rates
    End of Transmission Interrupt Flag
    Write Collision Flag Protection
    Wake-up from Idle Mode
    Double Speed (CK/2) Master SPI Mode

Its operation mode and speed are configured in user program via its relevant special function registers.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SPI Block Diagram

 Its clock generator is divided form the MCU clock up to CK/2. Data is shifted in and out using its internal 8-bit shift register at each clock cycle of the SPI clock generator. It also generate interrupt flag whenever the shift registers is empty.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SPI Master-slave Interconnection

 These are its relevant registers:

  •   SPCR – SPI Control Register
ATMega644P Serial Peripheral Interface (SPI) Tutorial
SPCR – SPI Control Register
  •  SPSR – SPI Status Register 
ATMega644P Serial Peripheral Interface (SPI) Tutorial
SPSR – SPI Status Register
  • SPDR – SPI Data Register 
ATMega644P Serial Peripheral Interface (SPI) Tutorial
SPDR – SPI Data Register

The ATMega644P may operate in master transmit only, slave receive only or even full duplex (synchronous data transfer) depending on these registers.  For more detail please see the device datasheet. 

ATMega644P SPI Interfacing and Programming

We can configure the SPI module of the ATMega644P to operates in a specific mode depends on the worked application.

SPI Master Transmit Mode 

An SPI master transmits only is common. There are many SPI slave device that only need data reception such as the SN74HC595N or 74HC164 shift register chips. 

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SN74HC595N and SN74HC164N Shift Registers

These chips is very popular due to its availability, low cost, ease of control etc. The SN74HC595N is commonly found in output expanding application such as relays driving, LED driving especially the dot matrix display driver.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SN74HC595N Pin Diagram

In this example the ATMega644P operate in master mode to transmit data to the SN74HC595N shifter register chip. The output port of this chip can connects to LED, 7-Segment displays or even a character LCD.

There are sample code of using the SPI transmit in device data sheet in both Assembly and C program.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Sample Code

 

LED Driving

I have a DIY single chip SN74HC595N LED module that is very easy to make. However we can install them together on a single breadboard.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
A completed Soldering Board
ATMega644P Serial Peripheral Interface (SPI) Tutorial
Copper Side

 For more information about this DIY PCB see this post.

 The program is very simple.

  1. /*
  2. * 12-spi_74hc595_led.c
  3. *
  4. * Created: 2/14/2026 6:57:01 PM
  5. * Author : Admin
  6. */

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

  10. #define DDR_SPI DDRB
  11. #define PRT_SPI PORTB
  12. #define DD_MOSI 5
  13. #define DD_SCK 7
  14. #define DD_SS 4

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

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

  30. int main(void)
  31. {
  32. /* Replace with your application code */
  33. SPI_MasterInit();
  34. char i=0;
  35. while (1)
  36. {
  37. SPI_MasterTransmit(1<<i);
  38. PRT_SPI&=~(1<<DD_SS);
  39. _delay_us(100);
  40. PRT_SPI=(1<<DD_SS);
  41. _delay_us(100);
  42. i++;
  43. if(i>8) i=0;
  44. _delay_ms(100);
  45. }
  46. }


Schematic:

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SN74HC595N LED Driving

 AVR Experiment Board:

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SN74HC595N LED Driving

 

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SN74HC595N LED Driving

 

 

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SN74HC595N LED Driving

 The program just shift each bits of the SN74HC595N output port.

 

Seven Segments Display Driving

I made a single common cathode 7-Segment display that is driven by an SN74HC595N chip. So it's easy for the MCU to connect to this display using a little wires. 

Making A Single 74HC595 Seven Segments Driver For Arduino PIC And AVR
Fully Solder Board

 

Making A Single 74HC595 Seven Segments Driver For Arduino PIC And AVR
Copper Side After I Soldered.

 Source Code:

  1. /*
  2. * 12-spi_74hc595_7.c
  3. *
  4. * Created: 2/14/2026 9:42:04 PM
  5. * Author : Admin
  6. */

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

  10. //Common Anode
  11. const char ca_7[16]={0xC0, 0xF9, 0xA4, 0xB0,0x99, 0x92,
  12. 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xA7, 0xA1, 0x86, 0x8E};
  13. //Common Cathode
  14. const char cc_7[16]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,
  15. 0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};

  16. #define DDR_SPI DDRB
  17. #define PRT_SPI PORTB
  18. #define DD_MOSI 5
  19. #define DD_SCK 7
  20. #define DD_SS 4

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

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

  36. int main(void)
  37. {
  38. /* Replace with your application code */
  39. SPI_MasterInit();
  40. char i=0;
  41. while (1)
  42. {
  43. SPI_MasterTransmit(ca_7[i]);
  44. PRT_SPI&=~(1<<DD_SS);
  45. _delay_us(100);
  46. PRT_SPI=(1<<DD_SS);
  47. _delay_us(100);
  48. i++;
  49. if(i>15) i=0;
  50. _delay_ms(500);
  51. }
  52. }


Schematic:

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Seven Segments Display Driving

 

Master Transmits and Slave Receives

An MCU operates in master or slave depends on software configuration. In this example one ATMega644P operates in master transmit only mode while another one operates in slave receive only. 

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Code examples show how to initialize the SPI as a Slave and how to perform a
simple reception.

 

For master MCU the Slave Select (SS or PB4) pin (output mode) goes low whenever it start sending data out. For slave MCU whenever this pin (input mode) goes low data starts shift into its shift register at each clock cycle of SPI data transmission. However this pin is select-able by user software during transmission.

Source Code "main.c" for master MCU:

  1. /*
  2. * 12-spi_master_slave.c
  3. *
  4. * Created: 2/14/2026 11:01:07 PM
  5. * Author : Admin
  6. */

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

  10. //Common Anode
  11. const char ca_7[16]={0xC0, 0xF9, 0xA4, 0xB0,0x99, 0x92,
  12. 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xA7, 0xA1, 0x86, 0x8E};
  13. //Common Cathode
  14. const char cc_7[16]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,
  15. 0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};

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

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

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

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

  52. int main(void)
  53. {
  54. /* Replace with your application code */
  55. SPI_MasterInit();
  56. char i=0;
  57. while (1)
  58. {
  59. SPI_MasterTransmit(1<<i);
  60. _delay_ms(500);
  61. i++;
  62. if(i>7) i=0;
  63. _delay_ms(500);
  64. }
  65. }


Source Code "main.c" for slave MCU: 

  1. /*
  2. * 12-spi_slave.c
  3. *
  4. * Created: 2/14/2026 11:05:48 PM
  5. * Author : Admin
  6. */

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

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

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

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

  31. void SPI_SlaveInit(void)
  32. {
  33. /* Set MISO output, all others input */
  34. DDR_SPI &= ~(1<<DD_MISO);
  35. /* Enable SPI */
  36. SPCR = (1<<SPE);
  37. }
  38. char SPI_SlaveReceive(void)
  39. {
  40. /* Wait for reception complete */
  41. while(!(SPSR & (1<<SPIF)))
  42. ;
  43. /* Return Data Register */
  44. return SPDR;
  45. }

  46. int main(void)
  47. {
  48. /* Replace with your application code */

  49. SPI_SlaveInit();
  50. DDRC=0xFF;
  51. char i=0;
  52. while (1)
  53. {
  54. PORTC=SPI_SlaveReceive();
  55. _delay_ms(500);
  56. }
  57. }



Schematic:

 

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Schematic

I have only one ATMega644 chip so I just simulate it using a simulator.

Here the master MCU read the ADC input with a variable voltage, convert it into voltage data before sending to its slave MCU. Thee slave MCU keep listening to the master receiving the voltage data and it will present on two 7-Segment displays.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Schematic

The contains three data bytes, slave adress (0xC1), decimal voltage value and fraction point.

Source Code "main.c" for master MCU:

  1. /*
  2. * 12-spi_master_slave.c
  3. *
  4. * Created: 2/14/2026 11:01:07 PM
  5. * Author : Admin
  6. */

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

  10. //Common Anode
  11. const char ca_7[16]={0xC0, 0xF9, 0xA4, 0xB0,0x99, 0x92,
  12. 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xA7, 0xA1, 0x86, 0x8E};
  13. //Common Cathode
  14. const char cc_7[16]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,
  15. 0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};

  16. const char slave_address=0xC1;

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

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

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

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

  53. int main(void)
  54. {
  55. /* Replace with your application code */
  56. SPI_MasterInit();
  57. //Turn on the ADC module
  58. ADCSRA=(1<<ADEN);
  59. //Select ADC0
  60. ADMUX=0;
  61. char i=0;
  62. long temp=0;
  63. while (1)
  64. {
  65. //Start the conversion
  66. ADCSRA|=(1<<ADSC);
  67. //Wait for the completion
  68. while((ADCSRA&(1<<ADSC))==1);
  69. //Read the result
  70. temp=ADCL+(ADCH<<8);
  71. temp=temp*50/1023;
  72. //Send data bytes
  73. PRT_SPI&=~(1<<DD_SS);
  74. SPI_MasterTransmit(slave_address);
  75. SPI_MasterTransmit(cc_7[temp/10]|0x80);
  76. SPI_MasterTransmit(cc_7[temp%10]);
  77. PRT_SPI|=(1<<DD_SS);
  78. _delay_ms(1000);
  79. }
  80. }


Source Code "main.c" for slave MCU: 

  1. /*
  2. * 12-spi_slave.c
  3. *
  4. * Created: 2/14/2026 11:05:48 PM
  5. * Author : Admin
  6. */

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

  10. const char slave_address=0xC1;

  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. struct spi_data{
  48. char address;
  49. char voltage;
  50. char fraction;
  51. };
  52. int main(void)
  53. {
  54. /* Replace with your application code */

  55. SPI_SlaveInit();
  56. DDRD=0xFF;
  57. DDRA=0xFF;
  58. DDRC=0xFF;
  59. struct spi_data _data;
  60. while (1)
  61. {
  62. _data.address=SPI_SlaveReceive();
  63. _data.voltage=SPI_SlaveReceive();
  64. _data.fraction=SPI_SlaveReceive();
  65. if (_data.address!=slave_address)
  66. {
  67. _data.voltage=_data.fraction=0x71;
  68. }
  69. PORTA=_data.voltage;
  70. PORTC=_data.fraction;
  71. }
  72. }



Full-Duplex Mode

The ATMega644P able to operate in full-duplex mode (three-wire synchronous data transfer). It means that during data transmission it also has data reception at the same time.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
SPI Master-slave Interconnection

For master MCU data is shifted out via MOSI pin and shifted in via MISO pin during transmission. For slave MCU data is shifted in via MOSI pin and shifted out via MISO pin during reception. The Slave Select (SS) pin is ignore here because it's controlled by SPI hardware.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Schematic and Program Simulation

 

In this example a master ATMega644P send PORTD input data to its slave ATMega644P device. At slave side received data will show on PORTC and it send back PORTD input data to its master MCU. The master MCU show received data from slave device on PORTC.

ATMega644P Serial Peripheral Interface (SPI) Tutorial
Simulate Program Wave Form

 

Master MCU Source Code "main.c":

  1. /*
  2. * master.c
  3. *
  4. * Created: 2/15/2026 4:38:04 PM
  5. * Author : Admin
  6. */

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

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

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

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

  31. void SPI_SlaveInit(void)
  32. {
  33. /* Set MISO output, all others input */
  34. DDR_SPI = (1<<DD_MISO);
  35. /* Enable SPI */
  36. SPCR = (1<<SPE);
  37. }
  38. char SPI_SlaveReceive(void)
  39. {
  40. /* Wait for reception complete */
  41. while(!(SPSR & (1<<SPIF)))
  42. ;
  43. /* Return Data Register */
  44. return SPDR;
  45. }

  46. int main(void)
  47. {
  48. /* Replace with your application code */
  49. SPI_MasterInit();
  50. DDRD=0x00;
  51. DDRC=0xFF;
  52. PORTD=0xFF;
  53. while (1)
  54. {
  55. //Send data bytes
  56. //PRT_SPI&=~(1<<DD_SS);
  57. SPI_MasterTransmit(PIND);
  58. PORTC=SPI_SlaveReceive();
  59. //PRT_SPI|=(1<<DD_SS);
  60. _delay_ms(100);
  61. }
  62. }



Slave MCU Source Code "main.c": 

  1. /*
  2. * slave.c
  3. *
  4. * Created: 2/15/2026 4:39:00 PM
  5. * Author : Admin
  6. */

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

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

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

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

  31. void SPI_SlaveInit(void)
  32. {
  33. /* Set MISO output, all others input */
  34. DDR_SPI = (1<<DD_MISO);
  35. /* Enable SPI */
  36. SPCR = (1<<SPE);
  37. }
  38. char SPI_SlaveReceive(void)
  39. {
  40. /* Wait for reception complete */
  41. while(!(SPSR & (1<<SPIF)))
  42. ;
  43. /* Return Data Register */
  44. return SPDR;
  45. }

  46. int main(void)
  47. {
  48. /* Replace with your application code */
  49. SPI_SlaveInit();
  50. DDRD=0x00;
  51. DDRC=0xFF;
  52. PIND=0xFF;
  53. while (1)
  54. {
  55. PORTC=SPI_SlaveReceive();
  56. SPI_MasterTransmit(PIND);
  57. _delay_ms(10);
  58. }
  59. }


At this time I only one AVR device so I can not test it in physical hardware.


 

  

320x50

Search This Blog

tyro-728x90