SPI Test 1 (Serial Flash Memory Test)
Connection Diagram
Functions
Most of functions required to interface with a serial flash memory device(S25FL208K) are implemented in <S25FL208K_hal.c> file.
A demo program <SPI_Serial_Flash_S25FL208K.c> was developed to test the functions implemented in <S25FL208K_hal.c> file.
How to test
1. Connect the serial flash memory device to a arduino nano board. Refer to the above connection diagram.
2. Connect the arduino nano USB connector to a laptop or desktop USB connector.
3. Run a terminal program on PC and set the communication parameters as following:
     9600 bps, 8 data bit, no parity, one stop bit
4. Main menu will be dispalyed on the terminal screen as below. User can select one of ten test functions and see the test result.
User can select one of ten test functions and see the test result.
Demo Program to Test the Library Functions
SPI_Serial_Flash_S25FL208K.c
/************************************************************ SPI interface with Spansion Serial Flash Memory S25FL208K Date : 2015-03-06 Author : J.M. Cho Chip type : ATmega328P AVR Core Clock frequency: 16.000000 MHz PD7 --> CS#(1) PB5(SCK) --> SCK(6) PB4(MISO) <-- SO(2) PB3(MOSI) --> SI(5) ----------------------------------------------------- <USART Spec> 9600, 8, N, 1 ----------------------------------------------------- <Compiler Settings> To support unsigned long int (uint32_t) CodeVision setting should be changed as following: (Top Menu) Project -> Configure -> C Compiler tab (In the C Compiler tab) (s)printf Features: long, width, precision (s)scanf Features: long, width *****************************************************/ #include <mega328p.h> #include <stdio.h> #include <S25FL208K_hal.h> #include <SPI_Serial_Flash_S25FL208K.h> const char msg_byte_data_req[] = "Enter 1-byte value in HEX (0x00-0xFF): "; const char msg_3byte_addr_req[] = "Enter 3-byte address in HEX (0x000000-0xFFFFFF): "; const char msg_3byte_block_addr_req[] = "Enter 3-byte BLOCK start address in HEX (0x000000-0xFFFFFF): "; const char msg_3byte_sector_addr_req[] = "Enter 3-byte SECTOR start address in HEX (0x000000-0xFFFFFF): "; void main(void) { uint8_t buf[256] = {0, }; // initialize 256 byte of buf with zero int sel, ret_val; uint32_t addr; uint8_t temp; init_system(); smem_read_JEDEC_ID(buf); // Screen shows you 01, 40, 14 printf("\n\rManufacturer ID=%02X, Memory Type ID=%02X, Capacity ID=%02X\n\r", buf[0], buf[1], buf[2]); while (1) { sel = display_menu(); switch (sel) { // Read ID case 1: smem_read_JEDEC_ID(buf); printf("\n\rManufacturer ID=0x%02X, Memory Type ID=0x%02X, Capacity ID=0x%02X\n\r", buf[0], buf[1], buf[2]); break; // Read Status Register case 2: temp = smem_read_status_register(); printf("\n\rStatus Register Value=0x%02X\n\r", temp); break; // Read Data Page case 3: addr = display_menu_mem_read(); // get memory address to read addr &= 0xFFFF00; // convert to page start address smem_read_data_page(buf, addr); // read one page display_buf(buf, addr); // display it break; // Write Status Register case 4: temp = display_menu_stat_reg(); // get 8-bit value to be written smem_write_status_register(temp); // write it to status reg break; // Write One Byte case 5: addr = display_menu_one_byte(buf); // get 8-bit data and 24-bit address smem_write_data(buf, addr, 1); // write one byte to flash break; // Write One Page case 6: addr = display_menu_one_page(buf); // get 8-bit data and 24-bit address smem_write_data(buf, addr, 256); // write one page to flash break; // Chip Erase case 7: printf("\n\rWait...it takes few seconds. "); smem_erase_chip(); printf("\n\rChip erased.\n\r"); break; // Block Erase case 8: addr = display_menu_block_erase(); smem_erase_block(addr); printf("\n\rBlock erased.\n\r"); break; // Sector Erase case 9: addr = display_menu_sector_erase(); smem_erase_sector(addr); printf("\n\rSector erased.\n\r"); break; // Chip Blank Check case 0: printf("\n\rWait...it takes few seconds. "); ret_val = smem_chip_blank_check(); if (ret_val == SMEM_BLANK) printf("\n\rChip is BLANK.\n\r"); else printf("\n\rChip is NOT blank.\n\r"); break; } } } void init_system(void) { // Crystal Oscillator division factor: 1 #pragma optsize- CLKPR=0x80; CLKPR=0x00; #ifdef _OPTIMIZE_SIZE_ #pragma optsize+ #endif // Input/Output Ports initialization // Port B initialization // Func7=In Func6=In Func5=Out Func4=In Func3=Out Func2=In Func1=In Func0=In // State7=T State6=T State5=0 State4=T State3=0 State2=T State1=T State0=T // *********************************************************************** // Important Note on SS(PB2) // ------------------------- // SS(PB2) is not used as a Slave Select signal in this example - instead, // PD7 is used as a Chip Select signal for the Serial Flash Memory chip. // However, PB2 should be set to ONE(1) regardless of its data direction. // If PB2 is set to ZERO(0), // SPIF flag is never set to ONE after transmission of the second byte. // Refer to 19.3.2 (page 165) of the datasheet. // // Conclusion: // Use PB2 as an SS control signal for your first SPI device. // *********************************************************************** PORTB = 0x04; // MUST write 1 to PB2 (SS) DDRB = 0x2C; // Port D initialization // Func7=Out Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In // State7=1 State6=T State5=T State4=T State3=T State2=T State1=T State0=T // PD7 is used as a Chip Select signal for the Serial Flash Memory chip. PORTD = 0x80; DDRD = 0x80; // USART initialization // Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: On // USART Transmitter: On // USART0 Mode: Asynchronous // USART Baud Rate: 9600 UCSR0A=0x00; UCSR0B=0x18; UCSR0C=0x06; UBRR0H=0x00; UBRR0L=0x67; // SPI initialization // SPI Type: Master // SPI Clock Rate: 8 MHz (SYSCLK / 2) // SPI Clock Phase: Cycle Start // SPI Clock Polarity: Low // SPI Data Order: MSB First SPCR=0x50; // 0101 0000 SPSR=0x01; } uint32_t display_menu_sector_erase(void) { uint32_t addr; // get address to be written addr = get_24bit_addr(msg_3byte_sector_addr_req); return addr; } uint32_t display_menu_block_erase(void) { uint32_t addr; // get address to be written addr = get_24bit_addr(msg_3byte_block_addr_req); return addr; } // Get 8-bit data to be written in the page // On Return: // t[]: filled with user input byte // return value: start address of a page uint32_t display_menu_one_page(uint8_t t[]) { uint32_t addr; int i; uint8_t data; // get data to be written data = get_byte(msg_byte_data_req); // fill in the buffer with user data for (i=0; i<256; i++) { t[i] = data; } // get address to be written addr = get_24bit_addr(msg_3byte_addr_req); return addr; } uint32_t display_menu_one_byte(uint8_t t[]) { uint32_t addr; // get data to be written t[0] = get_byte(msg_byte_data_req); // get address to be written addr = get_24bit_addr(msg_3byte_addr_req); return addr; } // get three bytes of address from user uint32_t get_24bit_addr(char msg[]) { long int addr_val; uint32_t addr; char res; do { printf("\n\r%s", msg); res = scanf("%lx", &addr_val); } while (res != 1 || addr_val > 0xFFFFFF || addr_val < 0); addr = (uint32_t)(addr_val & 0xFFFFFF); return addr; } // get one byte of data from user uint8_t get_byte(char msg[]) { int data; char res; uint8_t ret_val; do { printf("\n\r%s", msg); res = scanf("%x", &data); } while (res != 1 || data > 0xff || data < 0); ret_val = (uint8_t)(data & 0xFF); return ret_val; } // get one byte of data to be written in status register uint8_t display_menu_stat_reg(void) { uint8_t ans; ans = get_byte(msg_byte_data_req); return ans; } //int display_menu_mem_read(void) uint32_t display_menu_mem_read(void) { uint32_t addr; // get address to be written addr = get_24bit_addr(msg_3byte_addr_req); return addr; } // dispaly main menu and get user selection int display_menu(void) { int ans = 0; do { printf("\n\rSerial Flash Memory (S25FL208K) Test Menu (Ver. %d.%d)\n\r\n\r", VERSION_MAJOR, VERSION_MINOR); printf("\t1. Read ID\n\r"); printf("\t2. Read Status Register\n\r"); printf("\t3. Read Data Page\n\r\n\r"); printf("\t4. Write Status Register\n\r"); printf("\t5. Write One Byte\n\r"); printf("\t6. Write One Page\n\r\n\r"); printf("\t7. Chip Erase\n\r"); printf("\t8. Block (64 KB) Erase\n\r"); printf("\t9. Sector (4 KB) Erase\n\r\n\r"); printf("\t0. Chip Blank Check\n\r\n\r"); printf("\n\rSelect a test function (0-9): "); scanf("%d", &ans); } while (ans < 0 || ans > 9); return ans; } // Display 256 bytes stored in buf[256] void display_buf(uint8_t buf[], uint32_t addr) { int i; for (i=0; i<256; i++) { if (i%8 == 0) { if (i%16 == 0) printf("\n\r%06lX: ", addr+i); else printf("- "); } printf("%02X ", buf[i]); } printf("\n\r"); } // end of file
SPI_Serial_Flash_S25FL208K.h
// // SPI_Serial_Flash_S25FL208K.h // int display_menu(void); uint32_t display_menu_mem_read(void); uint8_t display_menu_stat_reg(void); uint32_t display_menu_one_byte(uint8_t t[]); uint32_t display_menu_one_page(uint8_t t[]); uint32_t display_menu_block_erase(void); uint32_t display_menu_sector_erase(void); int smem_chip_blank_check(void); void display_buf(uint8_t buf[], uint32_t addr); uint8_t get_byte(char msg[]); uint32_t get_24bit_addr(char msg[]); void init_system(void); // end of file
Implemented Library Functions
S25FL208K_hal.c
/******************************************************************** SPI interface function with Spansion Serial Flash Memory S25FL208K Date : 2015-03-06 Author : J.M. Cho Chip type : ATmega328P AVR Core Clock frequency: 16.000000 MHz PD7 --> CS#(1) PB5(SCK) --> SCK(6) PB4(MISO) <-- SO(2) PB3(MOSI) --> SI(5) ------------------------------------------------------------------- <Compiler Settings> To support unsigned long int (uint32_t) CodeVision setting should be changed as following: (Top Menu) Project -> Configure -> C Compiler tab (In the C Compiler tab) (s)printf Features: long, width, precision (s)scanf Features: long, width ********************************************************************/ #include <mega328p.h> #include <S25FL208K_hal.h> // user defined header file // Erase a sector (4 kbytes) void smem_erase_sector(uint32_t addr) { // Enable write smem_write_enable(); // Set #CS LOW smem_assert_cs(); // Send a command SPDR = CMD_SECT_ERASE; while (!(SPSR & 0x80)); // Set A23-A16 address SPDR = addr >> 16; while (!(SPSR & (1 << SPIF))); // Set A15-A8 address SPDR = addr >> 8; while (!(SPSR & (1 << SPIF))); // Set A7-A0 address SPDR = addr & 0xFF; while (!(SPSR & (1 << SPIF))); // Set #CS HIGH smem_negate_cs(); // wait for completeness of the given command while (smem_read_status_register() & STAT_BUSY_BIT); } // Erase a block (64 kbytes) void smem_erase_block(uint32_t addr) { // Enable write smem_write_enable(); // Set #CS LOW smem_assert_cs(); // Send a command SPDR = CMD_BLK_ERASE; while (!(SPSR & 0x80)); // Set A23-A16 address SPDR = addr >> 16; while (!(SPSR & (1 << SPIF))); // Set A15-A8 address SPDR = addr >> 8; while (!(SPSR & (1 << SPIF))); // Set A7-A0 address SPDR = addr & 0xFF; while (!(SPSR & (1 << SPIF))); // Set #CS HIGH smem_negate_cs(); // wait for completeness of the given command while (smem_read_status_register() & STAT_BUSY_BIT); } // Erase whole chip void smem_erase_chip(void) { // Enable write smem_write_enable(); // Set #CS LOW smem_assert_cs(); // Send a command SPDR = CMD_CHIP_ERASE; while (!(SPSR & 0x80)); // Set #CS HIGH smem_negate_cs(); // wait for completeness of the given command while (smem_read_status_register() & STAT_BUSY_BIT); } // Read status register and return it uint8_t smem_read_status_register(void) { // Set #CS LOW smem_assert_cs(); // Send Read Status Register command (0x05, p.16) SPDR = CMD_RD_STAT_REG; while (!(SPSR & 0x80)); // Read in Status Register value SPDR = 0x00; while (!(SPSR & (1 << SPIF))); // Set #CS HIGH smem_negate_cs(); return SPDR; } // Write Status Register void smem_write_status_register(uint8_t stat) { // Enable write smem_write_enable(); // Set #CS LOW smem_assert_cs(); // Send Write Status Register command (0x01, p.17) SPDR = CMD_WR_STAT_REG; while (!(SPSR & 0x80)); // Write value to Status Register SPDR = stat; while (!(SPSR & 0x80)); // Set #CS HIGH smem_negate_cs(); // wait for completeness of flash program while (smem_read_status_register() & STAT_BUSY_BIT); } // Set Write Enable Latch bit to 1 void smem_write_enable(void) { // Set #CS LOW smem_assert_cs(); // Send Write Enable command (0x06, p.15) SPDR = CMD_WR_ENABLE; while (!(SPSR & 0x80)); // Set #CS HIGH smem_negate_cs(); } void smem_write_data(uint8_t buf[], uint32_t addr, int count) { int i; // Enable write smem_write_enable(); // Set #CS LOW smem_assert_cs(); // Send a command SPDR = CMD_WR_PAGE; while (!(SPSR & 0x80)); // Set A23-A16 address SPDR = addr >> 16; while (!(SPSR & (1 << SPIF))); // Set A15-A8 address SPDR = addr >> 8; while (!(SPSR & (1 << SPIF))); // Set A7-A0 address SPDR = addr & 0xFF; while (!(SPSR & (1 << SPIF))); for (i=0; i<count; i++) { // write data SPDR = buf[i]; while (!(SPSR & (1 << SPIF))); } // Set #CS HIGH smem_negate_cs(); // wait for completeness of flash program while (smem_read_status_register() & STAT_BUSY_BIT); } void smem_read_JEDEC_ID(uint8_t t[]) { // Set #CS LOW smem_assert_cs(); // Send RDID command (0x9F, p.26 and 27) SPDR = CMD_RD_ID; while (!(SPSR & 0x80)); // Read Manufacturer ID SPDR = 0x00; while (!(SPSR & (1 << SPIF))); t[0] = SPDR; // Read Memory Type ID SPDR = 0x00; while (!(SPSR & (1 << SPIF))); t[1] = SPDR; // Read Capacity ID SPDR = 0x00; while (!(SPSR & (1 << SPIF))); t[2] = SPDR; // Set #CS HIGH smem_negate_cs(); } // Read 256 bytes of data from serial flash memory // Refer to Section 8.5 (page 18) of datasheet void smem_read_data_page(uint8_t t[], uint32_t addr) { int i; // Set #CS LOW smem_assert_cs(); // Send Data Read command (0x03, p.18) SPDR = CMD_RD_DATA; while (!(SPSR & 0x80)); // Set A23-A16 address SPDR = addr >> 16; while (!(SPSR & (1 << SPIF))); // Set A15-A8 address SPDR = addr >> 8; while (!(SPSR & (1 << SPIF))); // Set A7-A0 address SPDR = addr & 0xFF; while (!(SPSR & (1 << SPIF))); for (i=0; i<256; i++) { // Read data SPDR = 0x00; while (!(SPSR & (1 << SPIF))); t[i] = SPDR; } // Set #CS HIGH smem_negate_cs(); } // chip blank check // On return: // 1: Blank, 0:Not Blank int smem_chip_blank_check(void) { uint32_t addr; uint8_t data; // Set #CS LOW smem_assert_cs(); // Send Data Read command (0x03, p.18) SPDR = CMD_RD_DATA; while (!(SPSR & 0x80)); // Set A23-A16 address SPDR = 0; while (!(SPSR & (1 << SPIF))); // Set A15-A8 address SPDR = 0; while (!(SPSR & (1 << SPIF))); // Set A7-A0 address SPDR = 0; while (!(SPSR & (1 << SPIF))); for (addr=0; addr<SMEM_LAST_ADDR; addr++) { // Read data SPDR = 0x00; while (!(SPSR & (1 << SPIF))); data = SPDR; if (data != 0xff) return SMEM_NOT_BLANK; } // Set #CS HIGH smem_negate_cs(); return SMEM_BLANK; } void smem_assert_cs(void) { PORTD &= ~SMEM_CS; } void smem_negate_cs(void) { PORTD |= SMEM_CS; } // end of file
S25FL208K_hal.h
// // S25FL208K_hal.h // #include <def.h> void smem_read_JEDEC_ID(uint8_t t[]); void smem_write_enable(void); uint8_t smem_read_status_register(void); void smem_write_status_register(uint8_t stat); void smem_read_data_page(uint8_t t[], uint32_t addr); void smem_write_data(uint8_t buf[], uint32_t addr, int count); void smem_erase_sector(uint32_t addr); void smem_erase_block(uint32_t addr); void smem_erase_chip(void); int smem_chip_blank_check(void); void smem_assert_cs(void); void smem_negate_cs(void); // end of file 
def.h
// // def.h // // software version #define VERSION_MAJOR 1 #define VERSION_MINOR 0 // Serial Flash Memory used by the following function. // smem_chip_blank_check() #define SMEM_LAST_ADDR 0x0FFFFF #define SMEM_BLANK 1 #define SMEM_NOT_BLANK 0 // Chip Selection signal for Serial Flash Memory #define SMEM_CS 0x80 // Chip Select, PD7 // Command used by Serial Flash Memory #define CMD_RD_ID 0x9F #define CMD_RD_DATA 0x03 #define CMD_WR_ENABLE 0x06 #define CMD_WR_PAGE 0x02 #define CMD_RD_STAT_REG 0x05 #define CMD_WR_STAT_REG 0x01 #define CMD_SECT_ERASE 0x20 #define CMD_BLK_ERASE 0xD8 #define CMD_CHIP_ERASE 0xC7 // BUSY(WIP) bit position in status register #define STAT_BUSY_BIT 0x01 typedef unsigned char uint8_t; typedef unsigned int uint16_t; typedef unsigned long int uint32_t; // end of file