/* spi_flash_02.c
* SPI-Kommunikation mit Flash-Speicher SST25VF016B
* 1.) Statusregister lesen
* 2.) Ein Byte auf Adresse 000101 schreiben
* 3.) Ein Byte von Adresse 000101 lesen
* 4.) Kompletten Flashspeicher löschen
* Controller: ATmega88 @ 8MHz
*/
#include <avr/io.h>
#define WP		(1<<PC5) // Write Protect disable @PC5
#define HOLD	(1<<PC4) // Stop serial communication disable @PC4
#define CE		(1<<PB2) // Chip Enable @PB2
#define MOSI	(1<<PB3) // Master Out Slave In @PB3
#define MISO	(1<<PB4) // Master In Slave Out @PB4
#define SCK		(1<<PB5) // Clock @PB5

uint8_t data, status_register=0;

void SPI_MasterInit(void)					/* Initiate the SPI-Master */
{
	DDRB = MOSI|SCK|CE;						// Set MOSI, SCK, CE output
	SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1);	// Enable SPI, Master, set clock rate fck/64
}

void SPI_MasterTransmit(uint8_t data)		/* Transmit data on MOSI */
{
	SPDR = data;							// Start transmission
	while(!(SPSR & (1<<SPIF)));				// Wait for transmission complete
}

uint8_t SPI_SlaveReceive(void)				/* Receive data from MISO */
{
	while(!(SPSR & (1<<SPIF)));				// Wait for reception complete
	return SPDR;							// Return SPI-Dataregister
}

uint8_t Read_Status_Register(void)			/* Read STATUS REGISTER */
{
	PORTB &= ~CE;							// CE = low (start communication)
	SPI_MasterTransmit(0x05);				// Send command 0x05 (Read the status register)
	SPI_MasterTransmit(0x00);				// Send one dummy byte
	status_register = SPI_SlaveReceive();	// Receive the value of the status register
	PORTB |= CE;							// CE = high (end of communication)
	return status_register;					// Return the value of the status register
}

void Enable_Write_Procedure(void) /* Activate write commands in correct sequence */
{
	/* Enable write status register EWSR */
	PORTB &= ~CE;					// CE = low (start communication)
	SPI_MasterTransmit(0x50);		// Send command 0x50 (Enable write status register)
	PORTB |= CE;					// CE = high (end of communication)
	
	/* Write status register WRSR */
	PORTB &= ~CE;					// CE = low (start communication)
	SPI_MasterTransmit(0x01);		// Send command 0x01 (Write status register)
	SPI_MasterTransmit(0x02);		// Send data 00000010 to clear BPx in status register and set WEL=1
	PORTB |= CE;					// CE = high (end of communication)
	
	/* Write enable WREN */
	PORTB &= ~CE;					// CE = low (start communication)
	SPI_MasterTransmit(0x06);		// Send command 0x06 (Set write enable WREN)
	PORTB |= CE;					// CE = high (end of communication)
}

/* Program one data byte */
void Byte_Program(uint8_t addressbyte1, uint8_t addressbyte2, uint8_t addressbyte3, uint8_t databyte)
{
	PORTB &= ~CE;						// CE = low (start communication)
	SPI_MasterTransmit(0x02);			// Send command 0x02 (program one data byte)
	SPI_MasterTransmit(addressbyte1);	// Send 1. Addressbyte (MSB first)
	SPI_MasterTransmit(addressbyte2);	// Send 2. Addressbyte
	SPI_MasterTransmit(addressbyte3);	// Send 3. Addressbyte
	SPI_MasterTransmit(databyte);		// Send databyte
	PORTB |= CE;						// CE = high (end of communication)
}

/* Write disable WRDI */
void Write_Disable(void) 
{
	PORTB &= ~CE;						// CE = low (start communication)
	SPI_MasterTransmit(0x04);			// WRDI (write disable) Send command 0x04
	PORTB |= CE;						// CE = high (end of communication)
}

/* Read memory */
uint8_t Read_Memory(uint8_t addressbyte1, uint8_t addressbyte2, uint8_t addressbyte3, uint8_t dummybyte)
{
	PORTB &= ~CE;						// CE = low (start communication)
	SPI_MasterTransmit(0x03);			// Send command 0x03 Read
	SPI_MasterTransmit(addressbyte1);	// Send 1. Addressbyte (MSB first)
	SPI_MasterTransmit(addressbyte2);	// Send 2. Addressbyte
	SPI_MasterTransmit(addressbyte3);	// Send 3. Addressbyte
	SPI_MasterTransmit(dummybyte);		// Send Dummybyte
	data = SPI_SlaveReceive();			// Receive the value of data
	PORTB |= CE;						// CE = high (end of communication)
	return data;						// Return the value of data
}

void Chip_Erase(void) /* Chip erase - Erase full memory array */
{
	PORTB &= ~CE;				// CE = low (start communication)
	SPI_MasterTransmit(0x60);	// Chip erase (Send command 0x60)
	PORTB |= CE;				// CE = high (end of communication)
}

int main(void)
{
	DDRC |= WP | HOLD;							// Set WP, HOLD output
	PORTC |= WP|HOLD;							// Set WP, HOLD high
	SPI_MasterInit();							// Init SPI Master
	status_register = Read_Status_Register();	// Read STATUS REGISTER
	status_register=0;							// set the variable to 0
	Enable_Write_Procedure();					// Enable a write process
	Byte_Program(0x00, 0x01, 0x01, 0xA7);		// Programm 1 databyte (A7) @ address 000101
	Write_Disable();							// Write disable WRDI
	data = Read_Memory(0x00, 0x01, 0x01, 0x00); // Read 1 databyte @ address 000101
	data = 0;
	Enable_Write_Procedure();					// Enable a write process
	Chip_Erase();								// Chip erase - Erase full memory array
	Write_Disable();							// Write disable WRDI
	
	while(1)
	{
		asm("NOP");
	}
}