MEAM.Design - ATmega32 Programming - Serial Peripheral Interface (SPI)
This code (reading from a serial SPI connection) will be running on a Raspberry Pi MCU. On that device, I believe I need to enable the kernel module spibcm2708. It sounds like you are telling me that in doing this, Linux will automatically create a /dev/whatever file. SPI (Serial Peripheral Interface) is an interface bus commonly used for communication with flash memory, sensors, real-time clocks (RTCs), analog-to-digital converters, and more. The Serial Peripheral Interface (SPI) bus was developed by Motorola to provide full-duplex synchronous serial communication between master and slave devices.
Overview
The ATmega32U4 has a number of serial communications modules, including one that conforms to the SPI standard. This port has full-duplex communication over three wires, can be used as either a master or a slave, has a configurable bit rate, and received packets can generate interrupts.
Configuration
To configure the SPI module, you need to disable power reduction, configure a few I/O pins, set a clock prescaler, select the SPI mode, (optionally) enable interrupts, and finally enable the SPI subsystem. It is also possible to modify many of the details of SPI operation, including the order in which bytes will be shifted between the devices, the clock polarity, and the clock phase - see the full datasheet for more information. Once enabled, the steps that you will take will depend upon whether you are configuring the device as a master or a slave. Herein we are going to assume that the ATmega32U4 is operating as the master device.
1. Disable Power Reduction
If the device has been put into a low-power mode, you must bring it back to full power to allow use of the SPI module. This is done by setting the PRSPI bit in PRR0 register.
2. Configure I/O Pins
The SPI lines consist of SCLK (Serial CLock), MOSI (Master-Out, Slave-In), MISO (Master-In, Slave-Out), and the optional SS (Slave Select). These lines are multiplexed onto the lower half of Port B. To configure as the master device, the data direction registers should be set as:
SPI function | Port pin | direction |
MISO | B3 | input |
MOSI | B2 | output |
SCLK | B1 | output |
SS | B0 | output (input for slave) |
3. Set the clock prescaler
The SPI clock is prescaled by the SPR1 and SPR0 bits in SPCR register, where
SPCR: SPR1 | SPCR: SPR0 | |
0 | 0 | /4 |
0 | 1 | /16 |
1 | 0 | /64 |
1 | 1 | /128 |
4. Select either master or slave mode
To place the SPI module into Master mode, the MSTR bit in the SPCR register should be set to 1.
(Note - If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.)
5. (Optionally) Enable interrupts
To enable interrupts from the SPI module, set the SPIE bit in the SPCR register to 1, and be sure to enable global interrupts, as explained here. Interrupts will point to SPI_STC_vect when a serial transfer is complete.
6. Enable the module
To enable SPI, set the SPE bit in the SPCR register to 1.
Usage
The SPI system operates by shifting data between two registers, one on the master and one on the slave.
1. To begin a new serial transfer, you must first pull the SS line low using clear(PORTB,0);
2. With SS low, write 8-bits of data into the SPDR register. The SPI module will recognize that you've written new data, and it will shift the packet over the slave, while simultaneously shifting in a packet from the slave.
3. Once the 8-bit transfer is complete, the SPIF bit in SPSR will go high (and if enabled, the SPI interrupt will be generated). The SPIF interrupt flag can be cleared be either executing the ISR or by first reading the SPIF bit then by either reading from or writing to SPDR.
4. If you are interested in the data returned from the slave, you can read it rom the SPDR register.
5. To transfer additional bytes, repeat steps 2-4.
6. To end transmission, return the SS line high using set(PORTB,0);
Sample Code
This sample code should enable the SPI module, transfer 0xAA to the slave, and read whatever is returned by the slave.
Serial Program Ing (spi) Enable
char outgoing = 0xAA;
char incoming;
//disable power reduction to allow SPI:
clear(PRR0,PRSPI);
// MOSI, SCLK, and SS as output:
DDRB |= (1<<DDRB2) | (1<<DDRB1) | (1<<DDRB0);
// Enable SPI, Master mode, prescaler = /128:
SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0);
// send SS low to begin transmission
clear(PORTB,0);
// send a byte to the slave
SPDR = outgoing;
// wait for transmission to finish
while(!check(SPSR,SPIF));
// read the byte returned from the slave
incoming = SPDR;
// send SS high to end transmission
set(PORTB,0);