Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

STK600 : Using SPI (Dataflash AT45DB041B)

SPI (Serial Peripheral Interface) Tutorial :

In the last tutorial we saw ADC of ATmega2560. In this tutorial we will see SPI in ATmega2560. ATmega2560 has SPI at PORTB. SPI is full duplex, 3-wire synchronous protocol with both Master and Slave operation. In case of STK600, it has dataflash (AT45DB041B) which has SPI interface with ATmega2560. In this tutorial we will see SPI interface with dataflash.

Important Note : For SPI we need clock speed more than 1 MHz, hence for SPI I will unprogram CKDIV8 fuse. So system clock frequency for SPI will be 8 MHz.

In our example ATmega2560 is Master and AT45DB041B is Slave. As ATmega2560 is Master we will use polling method here instead of interrupt.

Before moving to SPI initialization remember as in previous tutorials (USART, ADC) when USART is configured corresponding RXD and TXD pins override normal port pin functionality and same is the case with ADC i.e. when ADC is configured ADC0 pin of PORTF overrides it’s normal port functionality. But this is not the case with SPI. In case of SPI we need to define directions of pins. So SS, SCK and MOSI are defined as output. Only MISO is defined as input. This is achieved by writing bits of DDRB register.


DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(0<<DDB3);

SPI Initialization :

SPCR :

This register contains SPI interrupt enable bit, SPI enable bit, data order bit which decides order of data transmission, master or slave select bit, clock polarity and clock phase bits and SPI clock rate selection bits. We don’t want SPI in interrupt mode, data order bit will be 0 as we want MSB to be transmitted first. SPI operates in 4 modes Mode 0, 1, 2, 3 depending on values of CPHA and CPOL bits. AT45DB041B supports Mode 0 and Mode3 out of which it defaults to Mode 3 after Power on Reset. Hence we will use SPI Mode 3. Therefore both clock polarity and clock phase bits will be 1. And of course in order to use SPI, SPI enable bit should be 1. Prescaler for SPI clock will be 4.

SPCR|=(1<<SPE)|(1<<MSTR)|(1<<CPOL)|(1<<CPHA);

SPSR :

This register contains SPIF flag which is set after completion of serial transfer and interrupt is generated if SPIE is set. Write collision flag is set if SPDR register is written during SPI data transfer. This flag and SPIF flag are read only flags. It also contains SPI2X flag which when set doubles SPI clock frequency. We don’t need high speed SPI hence this bit will be zero.

SPSR=0x00;    //It can be skipped as all bits are 0.

SPDR :

This register is used for data transfer between register file and SPI shift register. Writing to the register initiates data transmission. Reading the register causes the Shift Register Receive buffer to be read.

This finishes our SPI initialization.

Atmel AT45DB041B is SPI compatible flash. It is 4-megabit flash with 2048 pages and 264 bytes per page. User can use all 264 bytes for storing data but alternatively out 264 bytes 8 bytes can be used for error detection purpose. Also it has 2 SRAM buffers (264 byte each) which allow receiving of data. It supports different page program operations as well as page/block erase operations. In this tutorial we will see few of write and read operations related to flash, like

Read Operations

Continuous Array Read

Main Memory Page Read

Buffer Read

Status Register Read

Write Operations

Buffer Write

Buffer to Main Memory Page Program with Built-in Erase

Main Memory Page Program Through Buffer

Page Erase

Main Memory Page to Buffer Transfer

The most important thing in case of dataflash is STATUS Register because it is the one from which we can get whether the dataflash is Busy or Ready. If you successfully read STATUS register then you can easily perform write and read operations on dataflash.

There is opcode for every operation with dataflash i.e. for read and write. In order to perform read or write operation one must follow sequence below.

  1. Wait until flash becomes ready.
  2. Once it is ready pull SS (Slave Select) line low.
  3. Send 8-bit opcode for particular operation.
  4. Then send necessary 4 reserved bits (if any), 11-bits of page address (if any), 9-bits starting address within that page (if any) and at last don’t care bytes (if any).
  5. After finishing this sequence you can read or write SPDR register depending on operation.
  6. Once you finish read/write then again pull SS line high.
  7. Remember, SS line once pulled low at the beginning of operation should be low throughout until the operation is finished. If you pull SS line high in middle of any operation it will terminate that operation.

So this is how one should send any command. As mentioned earlier each page of dataflash is 264 bytes long and both buffers in dataflash are also 264 bytes long. Hence for simplicity in every buffer/page write operation we will use numbers from 0 to 255 as data and last 8 bytes will be numbers from 0 to 7.

As you can see in the code below there are different functions to perform different operations on dataflash. Here we will see all operations one by one with corresponding function calls in main() function.

First of all we will erase all 2048 pages of dataflash.

for(unsigned int count=0; count<2048;count++)

      pg_erase(count);

Then we will see buffer write operation. In buffer write we will write same data but in reverse order.

buff_wr(0x00 );

Now buffer read operation.

buff_rd();

for(unsigned int count=0;count<264;count++)

      usart_tx(buff_rd_data[count]);

Data in buffer is valid as long as power is ON. Once you give POWER ON RESET the data In buffer vanishes. As both buffers are SRAM buffers.

Next is buffer to page program operation.

buff_to_pg_program(0x00);

In this operation data in buffer 1 is transferred to one of pages in main memory (in our case it is 0th page).

Now in order to cross check the result of above operation we will perform page read operation.

pg_read(0x00,0x00);

for(unsigned int count=0;count<264;count++)

      usart_tx(pg_rd_data[count]);

The data in page read operation should match with data in buffer. Then only buffer to page program operation is correct.

Next is page to buffer data transfer.

pg_to_buff(0x00);

In order to cross check result of this operation we will use buffer read operation.

buff_rd(0x00);

for(unsigned int count=0;count<264;count++)

      usart_tx(buff_rd_data[count]);

Again data on buffer should match with data on buffer then only page to buffer transfer is correct.

Next is page program through buffer operation.

pg_program_thru_buff(0x00,0x00);

In order to cross check result of page program operation we will use page read again.

pg_read(0x00,0x00);

for(unsigned int count=0;count<264;count++)

      usart_tx(pg_rd_data[count]);

Data from array should match with data on hyperterminal (or any other terminal) then only main memory page program operation is correct.

Last operation is page erase operation.

pg_erase(0x00);

Once this operation is performed then re-perform page read operation. If data before erasing page and data after erasing page mismatches then page erase operation is correct.

#include <avr/io.h>
#define ss_low() PORTB=(0<<PB0);                //macro to pull slave select low
#define ss_high() PORTB=(1<<PB0);                //macro to pull slave select high


unsigned char buff_rd_data[264];                //to store data read from buffer
unsigned char pg_rd_data[264];                    //to store data read from page

unsigned char data[264]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,
22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,
60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,
98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,
143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,
158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,
173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,
188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,
203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,
218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,
233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,
248,249,250,251,252,253,254,255,0,1,2,3,4,5,6,7};


void usart_init(void);
void usart_tx(unsigned char data);
void spi_init(void);
void spi_tx(unsigned char byte);
unsigned char spi_rx(void);
unsigned char get_status(void);
void buff_wr(int addr);                                    //function to write data into buffer 1
void buff_rd(int addr);                                    //function to read data from buffer 1
void buff_to_pg_program(int pg_addr);                    //function to program data from buffer to main memory page
void pg_to_buff(int pg_addr);                            //function to transfer data from main memory to buffer
void pg_program_thru_buff(int pg_addr,int buff_addr);    //function to program page through buffer
void pg_read(int pg_addr,int buff_addr);                //function to read data from page
void pg_erase(int pg_addr);                                //function to erase page


int main(void)
{
spi_init();

usart_init();

//use function calls here in proper sequence according to operation you want to perform

//Or use function calls as I have suggested above

while(1);
}


void usart_init()
{
UBRR1=0x33;                                        //baud rate 9600, % error 0.2 %

UCSR1B|=(1<<TXEN1);                                //enable transmitter

UCSR1C|=(1<<UCSZ11)|(1<<UCSZ10);                    //8-bit data, 1 stop bit, parity disabled, asynchronous operation
}


void usart_tx(unsigned char data)
{
while(!(UCSR1A & (1<<UDRE1)));                        //wait until transmit buffer becomes empty

UDR1=data;
}


void spi_init()
{
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(0<<DDB3);        //SS, SCK, MOSI as output pins and MISO as input

SPCR|=(1<<SPE)|(1<<MSTR)|(1<<CPOL)|(1<<CPHA);        //enable SPI, select Master, SPI Mode3, prescaler is 4 SPI frequency 2 MHz
}


void spi_tx(unsigned char byte)
{
SPDR=byte;

while(!(SPSR & (1<<SPIF)));                        //wait until transmission is completed
}


unsigned char spi_rx()
{
spi_tx(0x00);                                        //send dummy byte to read data

while(!(SPSR & (1<<SPIF)));                        //wait until reception is completed

return SPDR;
}


unsigned char get_status()
{
unsigned char status;

ss_high();

ss_low();

spi_tx(0xd7);                                        //opcode to read status register of EEPROM

status=spi_rx();                                    //Received the value of status register of EEPROM

status=status & 0x80;                                //AND with 80 to check whether dataflash Ready or Busy

ss_high();

return status;
}


void buff_wr(int addr)
{
while(get_status()!=0x80);

ss_high();

ss_low();

spi_tx(0x84);                                        //opcode to write buffer 1

spi_tx(0x00);

spi_tx((0x00|(addr>>8)));

spi_tx((unsigned char)addr);

for(int count=263;count>=0;count--)
{
spi_tx(data[count]);
}

ss_high();
}


void buff_rd(int addr)
{
while(get_status()!=0x80);

ss_high();

ss_low();

spi_tx(0xd4);                                        //opcode to read buffer 1

spi_tx(0x00);

spi_tx((0x00|(addr>>8)));

spi_tx((unsigned char)addr);

spi_tx(0x00);

for(unsigned int count=0;count<264;count++)
{
buff_rd_data[count]=spi_rx();
}

ss_high();
}


void buff_to_pg_program(int pg_addr)
{
while(get_status()!=0x80);

ss_high();

ss_low();

spi_tx(0x83);                                        //opcode for buffer 1 to main memory page with built in erase

spi_tx((0x00|(pg_addr>>7)));

spi_tx((unsigned char)((pg_addr<<1)|0x00));

spi_tx(0x00);

ss_high();
}


void pg_to_buff(int pg_addr)
{
while(get_status()!=0x80);

ss_high();

ss_low();

spi_tx(0x53);                                        //opcode to transfer data from page to buffer 1

spi_tx((0x00|(pg_addr>>7)));

spi_tx((unsigned char)((pg_addr<<1)|0x00));

spi_tx(0x00);

ss_high();
}


void pg_program_thru_buff(int pg_addr,int buff_addr)
{
while(get_status()!=0x80);

ss_high();

ss_low();

spi_tx(0x82);                                      //opcode to write page

spi_tx((0x00)|(pg_addr>>7));

spi_tx((unsigned char)((pg_addr<<1)|(buff_addr>>8)));

spi_tx((unsigned char)buff_addr);

for(unsigned int count=0;count<264;count++)
{
spi_tx(data[count]);
}

ss_high();
}


void pg_read(int pg_addr,int buff_addr)
{
while(get_status()!=0x80);

ss_high();

ss_low();

spi_tx(0xd2);                                        //opcode to read page

spi_tx((0x00|(pg_addr>>7)));

spi_tx((unsigned char)((pg_addr<<1)|(buff_addr>>8)));

spi_tx((unsigned char)buff_addr);

spi_tx(0x00);

spi_tx(0x00);

spi_tx(0x00);

spi_tx(0x00);

for(unsigned int count=0;count<264;count++)
{
pg_rd_data[count]=spi_rx();
}

ss_high();
}


void pg_erase(int pg_addr)
{
while(get_status()!=0x80);

ss_high();

ss_low();

spi_tx(0x81);                                        //opcode to erase page

spi_tx((0x00)|(pg_addr>>7));

spi_tx((unsigned char)((pg_addr<<1)|0x00));

spi_tx(0x00);

ss_high();
}

Thanks for reading. Don’t forget to share your views and questions as comments below.




This post first appeared on Embedded Tutorials, please read the originial post: here

Share the post

STK600 : Using SPI (Dataflash AT45DB041B)

×

Subscribe to Embedded Tutorials

Get updates delivered right to your inbox!

Thank you for your subscription

×