1 Introduction Non-volatile memory is often used in embedded applications for non-volatile data storage. EEPROM and FRAM are commonly used for that purpose because they are easy to read and write in comparison with flash devices. EEPROM and FRAM are small in size, and usually in serial interface. Some devices provide SPI (Serial Peripheral Interface) in addition to I/O interface. Therefore, data are clocked in or out serially. FRAM has advantages over EEPROM, because it features rapid writes and much higher write cycles. Here is an example how to write a SPI driver for Ramtron FM25CL64.
2 Hardware Interface
FM25CL64 supports two types of hardware interface, SPI interface and I/O interface. The former consists of four wires; the latter consists of four or three wires in which SI and SO are combined. Four signals for SPI are SPICS, SPICLK, MOSI, and MISO. Figure 1 demonstrates that FM25CL64 interfaces with Analog Device ADSP-21369 via SPI.
 Figure 1 FM25CL64 interfaces with Analog Device ADSP-21369 via SPI
Figure 2 demonstrates that FM25CL64 interfaces with a microcontroller via three I/O pins. It is pretty simple to emulate the serial interface using I/O pins. Note that in this interface, the SI and SO can be multiplexed into one bi-directional I/O pin to save the number of pins.
 Figure 2 FM25CL64 interfaces with a microcontroller via three I/O pins
3 Chip Select Signal Selection
Any flag signal FLAG14-0 of ADSP-21369 could be used for SPICS, however, only FLAG3-0 can be controlled by SPICTL register, which means that toggling of flags can be done automatically when SPICTL. CPHASE = 0, or manually (by your code) when SPICTL. CPHASE = 1. If you use any one of FLAG14-3, you must toggle it through software. By default, MOSI, MISO, and SPICLK are multiplexed with other functions on pin DPI_01, DPI_02, and DPI_03 respectively, while FLAG3-0 used as SPIFLG3-0 are routed to DPI_08 - _05 respectively. However, those SPI signals can be routed to other DPI pins, in which case, toggling flags must be done by software.
In the following example, signals are routed as shown in figure 1, i.e., MOSI -> DPI_01 MISO -> DPI_02 SPICLK -> DPI_03 SPICS -> DPI_04
4 Driver Programming
Driver programming includes FRAM_Open(), FRAM_Write(), FRAM_Read(), and FRAM_Close() functions.
4.1 FRAM_Open
FRAM_Open() function includes signal routing, the configuration of SPI control register, baud rate register.
4.1.1 Signal Routing
Routing a signal of peripheral module to an external pin can be done easily with SRU routing macros. Please note that the output pin buffer shall be enabled, while input pin buffer shall be disabled.
/********************************************************************************* * Function: DSP_SRU_SPI * * Desc: This function configures SRU for SPI interface * * Params: None * * Returns: boolean * * Notes: * *********************************************************************************/ bool DSP_SRU_SPI(void) { //set DPI_01 as SPI MOSI SRU2(SPI_MOSI_O, DPI_PB01_I); SRU2(HIGH, DPI_PBEN01_I);
//set DPI_02 as SPI MISO SRU2(DPI_PB02_O, SPI_MISO_I); SRU2(LOW, DPI_PBEN02_I);
//set DPI_03 as SPI CLK SRU2(SPI_CLK_O, DPI_PB03_I); SRU2(HIGH, DPI_PBEN03_I);
//set DPI_04 and FLAG0 as SPI CS SRU2(SPI_FLG0_O, DPI_PB04_I); SRU2(HIGH, DPI_PBEN04_I);
return true; }
4.1.2 SPI Control and Baud Rate Register Configuration
SPI Baud rate is configured by setting a divisor value to SPIBAUD register. Baud rate = fpclk / (4 x divisor)
Where fpclk – peripheral clock which is ½ of core clock
In our example, fpclk = 160 MHz, and the FRAM can run at 20 MHz, so we have divisor value 2. Please note that the bit[15:1] of SPIBAUD register are used for the divisor value, which means that the register value is twice as much as the divisor value when you look at the register value from the IDE.
Write word length, transfer format, and so on appropriately to the control register. To control Ramtron FRAM, the clock polarity shall be set to active low. In order to read and to write any number of bytes, the chip select signal shall be active during the entire buffer transmission, it is easier to use core driven approach to control the chip select signal, so we set SPICTL.CPHASE = 1. 4.1.3 Source Code
/******************************************************************************************* * Function: FRAM_Open * * Desc: This function sets up hardware SPI interface and configures control registers * * Params: None * * Returns: true if opening the port successfully, false otherwise * * Notes: None * *******************************************************************************************/ bool FRAM_Open(void) { int temp;
ST_SPICTL spictl;
//set up hardware interface DSP_SRU_SPI();
//SPI DMA is not used SPIDMAC.ALL = 0;
//set SPI baud rate SPIBAUD.BIT.BAUD_DIVISOR = FRAM_BAUD_RATE_DIVISOR;
//configure flag0 as chip select SPIFLG.BIT.DS0EN = 1;
//configure SPI control register spictl.ALL = 0; spictl.BIT.TIMOD = 1; //mode 1 spictl.BIT.GM = 1; //new data overwrites previous one spictl.BIT.ISSEN = 0; //disable SPIDS to free that pin for chip select spictl.BIT.SPIEN = 1; //SPI enable spictl.BIT.SPIMS = 1; //master spictl.BIT.OPD = 0; //open drain mode disabled as we use 1 (master) - 1 (slave) connection spictl.BIT.SENDZ = 1; //send zeros spictl.BIT.WL = 0; //word width 8 spictl.BIT.MSBF = 1; //MSB first spictl.BIT.CLKPL = 1; //clock polarity active low spictl.BIT.CPHASE = 1; //control chip select manually spictl.BIT.ILPBK = 0; //internal loop back disabled SPICTL.ALL = spictl.ALL;
//write status register to disable write protection WriteFramStatusRegister(FARM_INIT_STATUS);
//read status register temp = ReadFramStatusRegister();
if (temp == FARM_INIT_STATUS) { return true; } else { return false; } }
4.2 FRAM_Write
FRAM_Write() function writes a number of words to FRAM memory. The sequence of writing a word is write enable first, followed by write command, two bytes address, and the data to be written. There are two notes worth mentioning.
4.2.1 Write Enable
Write Enable command must be sent as a separate command from other writing or reading command, which means that the chip select signal must be in separate cycle. It won’t work if the Write Enable command is followed by a writing command in one chip select signal.
4.2.2 SPISTAT.RXS
In master mode, the SPI generates clock signals on SPICLK pin during a transmission, in which data is shifted out of MOSI and shifted in from MISO simultaneously, no matter whether a slave device is sending out data. During a transmission, SPIRX is loaded with whatever on MISO; SPISTAT.RXS is set after the transmission is complete. So SPIRX must be read or flushed to clear the RXS bit. If the data is what you wait for, get the data; if not, discard the data.
4.2.3 Write Wave Form
In the wave form, signal 0 is the /CS, signal 1 is SPICLK, signal 2 is MOSI, and signal 4 is MISO.

4.2.4 Source Code
/******************************************************************************************* * Function: FRAM_Write * * Desc: This function writes a buffer to FRAM device * * Params: * int startAddr - address in FRAM to start the writes at * int length - number of elements to write, in this case bytes * int *pData - pointer to data buffer * * Returns: bool - true if writing is successful, false otherwise * * Notes: None * *******************************************************************************************/ bool FRAM_Write( int startAddr, int length, int *pData ) { int i; int absAddr;
absAddr = FRAM_START_ADDR + startAddr;
//write enable command EnableFramWrite();
//assert CS AssertSPI_CS();
//write command WriteWordToSPI(FRAM_WRITE);
//high byte address WriteWordToSPI(absAddr >> 8);
//low byte address WriteWordToSPI(absAddr);
//write data to FRAM sequentially for (i = 0; i < length; i++) { WriteWordToSPI(*pData++); }
//de-assert CS DeassertSPI_CS();
return true; }
4.3 FRAM_Read
FRAM_Read() function read a number of data from FRAM memory. The sequence of reading a word is read command first, followed by two bytes of address, then a dummy write to generate 8 clocks to clock out data bits, which are shifted to RXSPI register.
4.3.1 Read Wave Form

4.3.2 Source Code
/******************************************************************************************* * Function: FRAM_Read * * Desc: This function reads data from FRAM device to a buffer * * Params: * int startAddr - address in FRAM to start the reading at * int length - number of bytes to read * int *pData - pointer to data buffer * * Returns: bool - true if reading is successful, false otherwise * * Notes: None * *******************************************************************************************/ bool FRAM_Read( int startAddr, int length, int *pData ) { int i; int absAddr;
absAddr = FRAM_START_ADDR + startAddr;
//assert CS AssertSPI_CS();
//read command WriteWordToSPI(FRAM_READ);
//high byte address WriteWordToSPI(absAddr >> 8);
//low byte address WriteWordToSPI(absAddr);
//read the buffer up to NVM_BUFFER_SIZE items for (i = 0; i < length; i++) { //dummy write to generate 8 clks to clock data into SPIRX *pData++ = WriteWordToSPI(0); }
//de-assert CS DeassertSPI_CS();
return true; }
4.4 FRAM_Close
FRAM_Close() function disables the SPI module to close the driver.
4.4.1 Source Code
/******************************************************************************************* * Function: FRAM_Close * * Desc: This function closes FRAM device driver. * * Params: None * * Returns: bool - true if successful, false otherwise * * Notes: None * *******************************************************************************************/ bool FRAM_Close(void) { SPICTL.BIT.SPIEN = 0;
return true; }
4.5 Write/Read Status Register
Before wring to FRAM memory, an appropriate Write Protection command shall be written to the Status register to set up the protection area. According to the tables above, write 0x80 to the status register if you chose no protection. You shall read back the status register and get the same value 0x80 if the /WP pin is at high level. Here is the code to write/read the status register.
4.5.1 Write Status Register /******************************************************************************************* * Function: WriteFramStatusRegister * * Desc: This function writes a word to the 8-bit status register of the FRAM * * Params: int status - status value * * Returns: bool - true if successful, false otherwise * * Notes: None * *******************************************************************************************/ bool WriteFramStatusRegister(int status) { //write enable command EnableFramWrite();
//assert CS AssertSPI_CS();
//write status register WriteWordToSPI(FRAM_WRSR);
//write status register WriteWordToSPI(status);
//de-assert CS DeassertSPI_CS();
return true; }
4.5.2 Read Status Register /******************************************************************************************* * Function: ReadFramStatusRegister * * Desc: This function reads the 8-bit status register from the FRAM * * Params: None * * Returns: Status of the register * * Notes: None * *******************************************************************************************/ int ReadFramStatusRegister(void) { int status;
//assert CS AssertSPI_CS();
//read status register WriteWordToSPI(FRAM_RDSR);
//write status register with dummy word status = WriteWordToSPI(0);
//de-assert CS DeassertSPI_CS();
return status; }
4.6 Complete Source Code Download
The complete source code of the driver can be downloaded here.
ES_FRAM.c ES_FRAM.h
|