Arduino Mode0 SPI Bit Bang and Bare Metal Hardware SPI

bare metal truck

Here are two additional versions of the SPI program from my previous post. The first of these programs use a “bare-metal” version of hardware SPI. The second is a bit-bang version using different pins.

How does the speed compare between the two versions? Not even close. The hardware SPI is running at 8MHz (half the system clock speed) and on average, transfers one byte in 2.438us. The bit-bang version takes about 12.56us to transfer a byte. I timed the period the CS pin is pulled low. Note, under HW SPI, the delay between CS going low and the first clock pulse is 0.8125Us, while in the bit-bang version the delay is approximately 2.125us (the y axis scale of the two screen captures is not the same).

Hardware SPI:
hardware spi

Bit Bang SPI:
bit bang spi

Bare-Metal Version:


//
//FM24CL64B SPI F-RAM
//64-Kbit
//
//using bare-metal hardware spi
//
/*
Arduino--Logic Conv--FRAM
D13------TXH/TXL-----6.SCK
D12------------------2.MISO
D11------TXH/TXL-----5.MOSI
D10------------------1.CS
3V3------LV
5V-------HV
GND------HV GND
GND------------------4.VSS
3V3------------------8.VCC
3V3------------------7.HOLD (tie to Vcc if not used)
3V3------[10KR]------1.CS
3.WP (active low, tie to Vcc if not used)
*/

#ifndef LSBFIRST
#define LSBFIRST 0
#endif
#ifndef MSBFIRST
#define MSBFIRST 1
#endif

#define CLOCK_DIV4 0x00
#define CLOCK_DIV16 0x01
#define CLOCK_DIV64 0x02
#define CLOCK_DIV128 0x03
#define CLOCK_DIV2 0x04
#define CLOCK_DIV8 0x05
#define CLOCK_DIV32 0x06
#define MODE0 0x00
#define MODE1 0x04
#define MODE2 0x08
#define MODE3 0x0C
#define MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR
#define CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR
#define CLOCKX2_MASK 0x01 // SPI2X = bit 0 on SPSR

//spi hardware transfer
inline static uint8_t SpiTransfer(uint8_t data) {
SPDR = data;
asm volatile("nop");
while (!(SPSR & _BV(SPIF)))
; // wait
return SPDR;
}

//SRAM opcodes
#define WREN 0b00000110 //set write enable latch
#define WRDI 0b00000100 //write disable
#define RDSR 0b00000101 //read status register
#define WRSR 0b00000001 //write status register
#define READ 0b00000011 //read memory data
#define WRITE 0b00000010 //write memory data

uint8_t SpiRAMRead8(uint16_t address) {
uint8_t read_byte;

PORTB &= ~(1<>8)&0xff));
SpiTransfer((char)address);
read_byte = SpiTransfer(0xff);
PORTB |= (1<<PORTB2); //set CS high
return read_byte;
}

void SpiRAMWrite8(uint16_t address, uint8_t data) {
PORTB &= ~(1<<PORTB2); //set CS low
SpiTransfer(WREN);
PORTB |= (1<<PORTB2); //set CS high
PORTB &= ~(1<>8)&0xff));
SpiTransfer((char)address);
SpiTransfer(data);
PORTB |= (1<<PORTB2); //set CS high
}

void setup(void) {
uint16_t addr;
uint8_t i, sreg;

Serial.begin(9600);
sreg = SREG;
noInterrupts();

//pin setup
pinMode(10, OUTPUT); //CS
pinMode(11, OUTPUT); //MOSI
pinMode(12, INPUT); //MISO
pinMode(13, OUTPUT); //SCK
PORTB |= (1<> 2) & CLOCKX2_MASK);

//test it
for (addr=0; addr<4; addr++) {
SpiRAMWrite8(addr, (uint8_t)addr);
Serial.print("Addr: ");
Serial.print(addr);
i = SpiRAMRead8(addr);
Serial.print(" | Read: ");
Serial.println((uint16_t)i);
}
}

void loop() { }

Bit Bang Version:

//
//FM24CL64B SPI F-RAM
//64-Kbit
//
//bit-bang
//
/*
Arduino--Logic Conv--FRAM
D7-------TXH/TXL-----6.SCK
D6-------------------2.MISO
D5-------TXH/TXL-----5.MOSI
D4-------------------1.CS
3V3------LV
5V-------HV
GND------HV GND
GND------------------4.VSS
3V3------------------8.VCC
3V3------------------7.HOLD (tie to Vcc if not used)
3V3------[10KR]------1.CS
3.WP (active low, tie to Vcc if not used)
*/

//bitbang
uint8_t SpiTransfer(uint8_t _data) {
for (uint8_t bit=0; bit<8; bit++) {
if (_data & 0x80) //set/clear mosi bit
PORTD |= (1<<PORTD5);
else
PORTD &= ~(1<<PORTD5);
_data <<= 1; //shift for next bit
if (PIND) //capture miso bit
_data |= (PIND & (1<<PORTD6)) != 0;
PORTD |= (1<<PORTD7); //pulse clock
asm volatile ("nop \n\t"); //pause
PORTD &= ~(1<<PORTD7);
}
return _data;
}

//SRAM opcodes
#define WREN 0b00000110 //set write enable latch
#define WRDI 0b00000100 //write disable
#define RDSR 0b00000101 //read status register
#define WRSR 0b00000001 //write status register
#define READ 0b00000011 //read memory data
#define WRITE 0b00000010 //write memory data

uint8_t SpiRAMRead8(uint16_t address) {
uint8_t read_byte;

PORTD &= ~(1<>8)&0xff));
SpiTransfer((char)address);
read_byte = SpiTransfer(0xff);
PORTD |= (1<<PORTD4); //set CS high
return read_byte;
}

void SpiRAMWrite8(uint16_t address, uint8_t data) {
PORTD &= ~(1<<PORTD4); //set CS low
SpiTransfer(WREN);
PORTD |= (1<<PORTD4); //set CS high
PORTD &= ~(1<>8)&0xff));
SpiTransfer((char)address);
SpiTransfer(data);
PORTD |= (1<<PORTD4); //set CS high
}

void setup(void) {
uint16_t addr;
uint8_t i, sreg;

Serial.begin(9600);
//configure pins
pinMode(4, OUTPUT); //CS
pinMode(5, OUTPUT); //MOSI
pinMode(6, INPUT); //MISO
pinMode(7, OUTPUT); //SCK
PORTD |= (1<<PORTD4); //set CS high
PORTD &= ~_BV(PORTD7); //set clock low

//test it
for (addr=0; addr<32; addr++) {
SpiRAMWrite8(addr, (uint8_t)addr);
Serial.print("Addr: ");
Serial.print(addr);
i = SpiRAMRead8(addr);
Serial.print(" | Read: ");
Serial.println((uint16_t)i);
}
}

void loop() { }

Advertisements

About Jim Eli

µC experimenter
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s