M25P128Module

M25P128 Schematic

M25P128 Datasheet

Introduction

This module was created by Timothy Box and converted to SF by Ryan O'Hara to share with others the great datalogging potential of this little chip in conjunction with the SF compiler. This chip is accessed in a manner very similar to a Serial EEPROM. I did want to point out several distinct difference that could cause trouble for people.

1. Each sector of memory must have been previously erased before writing to that page. This means you cannot write to several address and then "backup" to rewrite to the first address. The entire sector must be erased before you can return rewrite to the first address.

2. When you use the WriteBytes command you can begin at any address within a sector/page. However, caution must be taken so that you do not write over a page boundary.

3. Finally, if you are using this chip in conjunction with a usb connection, when the BulkErase command is issued it can take a fair amount of time (30 seconds or so) to earse all the data memory. You must somehow poll the usb line to keep the usb connection alive when this command is used.

Code

// DEVICE AND CLOCK
Device = 18F2620
Clock =  40

// DEVICE FUSE CONFIG
Config
    OSC 	= HSPLL,	// HS Oscillator
    FCMEN	= OFF,		// Failsafe Clock Monitor Disabled
    IESO	= OFF,		// Int/Ext Oscillator Switch Over Disabled
    PWRT	= OFF,		// Power Up Timer Disabled
    BOREN	= OFF,		// Brownout Reset Disabled
    WDT         = OFF,		// Watchdog Timer Disabled
    MCLRE	= ON,		// MCLR Enabled
    WDTPS       = 256,          // 15000 x 4mS  = 60 seconds
    LVP         = OFF,          // Low_Voltage Programming
    PBADEN      = OFF           // PORTB Digital  

// OPTIONS
    #option WDT = True
    #option SHIFT_MAX = 8    

// INCLUDES
    Include "UTILS.BAS"
    Include "SUART.bas"
    Include "CONVERT.bas"
    Include "SHIFT.bas"
    Include "M25P128.BAS"

// ALIAS
    Dim LED_FIX As PORTB.2
    Dim LED_NOFIX As PORTB.3
    Dim IDX As LongWord

    Dim ID As LongWord
    Dim M25PData(256) As Byte

Sub SETUP()
    SetAllDigital()

    M25P128.Init()
    SetTX(PORTB.7)
    SetRX(PORTB.6)
    SetBaudrate(sbr38400)
    SetMode(umTrue)
End Sub
//------------------------------------------------------------------------------
SETUP()

// Serial Startup
For IDX = 0 To 4
    Toggle(LED_FIX)
    Toggle(LED_NOFIX)
    UART.Write("HELLO WORLD",13,10)
    DelayMS(1000)
Next

// Read ID
For IDX = 0 To 4
    ID = M25P128.ReadId()
    UART.Write("C - ",BinToStr(ID.BYTE0,8),"|",BinToStr(ID.BYTE1,8),"|",BinToStr(ID.BYTE2,8),13,10)
Next

// Erase the First Sector
M25P128.SectorErase(0)

// Write Some Data to RAM
For IDX = 0 To 255
    M25PData(IDX) = IDX
Next

// Write the Data to the M25P128
M25P128.WriteBytes(128,M25PData)

// Read 256 Bytes from Address 0
M25P128.ReadBytes(128,M25PData,256)

// Write the Data to the Serial Port   
UART.Write("READ BYTE - ")        
For IDX = 0 To 255
    UART.Write(DecToStr(M25PData(IDX)),",")
Next    
UART.Write(13,10,13,10)

End

Module

{
*****************************************************************************
*  Name    : M25P128.BAS                                                   
*  Author  : Ryan O'Hara                                                   
*  Notice  : Copyright (c) 2009  - OHARARP LLC                             
*          : All Rights Reserved                                           
*  Date    : 1/16/2009                                                     
*  Version : 1.0                                                           
*  Notes   : This code was originally developed by Tim Box and ported to SF                                                              
*          : by Ryan O'Hara
*          : This handy little chip provides up to 128Mb -> 16MB of Flash memory                                                              
*****************************************************************************
}
Module M25P128

// OPTIONS
    #option SHIFT_MAX = 8    

// IMPORT MODULES
    Include "SHIFT.BAS"

// DEFAULT MODULE OPTIONS - USER OPTIONS CAN OVERRIDE THESE VALUES...    
    #option M25P_CS   = PORTC.2                     // use 10k ohms pull-up resistor
    #option M25P_CLK  = PORTC.3                     // use 10k ohms pull-down resistor
    #option M25P_SDI  = PORTC.4                     // 
    #option M25P_SDO  = PORTC.5                     // 

    // Validate CS pin...
    #if IsOption(M25P_CS) And Not IsValidPortPin(M25P_CS) 
       #error M25P_CS, "Invalid option. CS must be a valid port pin."
    #endif

        // Validate CLK pin...
    #if IsOption(M25P_CLK) And Not IsValidPortPin(M25P_CLK) 
       #error M25P_CLK, "Invalid option. CLK must be a valid port pin."
    #endif

        // Validate SDI pin...
    #if IsOption(M25P_SDI) And Not IsValidPortPin(M25P_SDI) 
       #error M25P_SDI, "Invalid option. DI must be a valid port pin."
    #endif

        // Validate SDO pin...
    #if IsOption(M25P_SDO) And Not IsValidPortPin(M25P_SDO) 
       #error M25P_SDO, "Invalid option. DO must be a valid port pin."
    #endif           
// ======= M25P128 Instructions ==================================
Const 
    M25P_WREN = $06,               ' Write M25PEnable
    M25P_WRDI = $04,               ' Write M25PDisable
    M25P_RDID = $9F,               ' Read Identification
    M25P_RDSR = $05,               ' Read STATUS Register
    M25P_WRSR = $01,               ' Write STATUS Register
    M25P_Read = $03,               ' Read Data Bytes
    M25P_Read_Fast = $0B,          ' Read Data Bytes At Higher Speed
    M25P_PP = $02,                 ' PAGE Program 
    M25P_SE = $D8,                 ' Sector Erase
    M25P_BE = $C7,                 ' Bulk Erase
    M25P_Dummy = $FF               ' Dummy Byte for Fast Read

// Variables used with commands to the M25P128
    Dim M25PStatus As Byte
    Dim M25PIndex As Byte                                       ' Index Counter    
    Dim SRWD As M25PStatus.7                                    ' Status Register Write M25PDisable
    Dim BP2 As M25PStatus.4                                     ' The Block Protect (BP2, BP1, BP0) bits
    Dim BP1 As M25PStatus.3
    Dim BP0 As M25PStatus.2
    Dim WEL As M25PStatus.1                                     ' The Write M25PEnable Latch
    Dim WIP As M25PStatus.0                                     ' The Write In Progress

// Bring port and pin options into the module...   
    Dim CS As M25P_CS.M25P_CS@
    Dim CLK As M25P_CLK.M25P_CLK@   
    Dim SDI As M25P_SDI.M25P_SDI@
    Dim SDO As M25P_SDO.M25P_SDO@
{
****************************************************************************
* Name    : Disable(PRIVATE)                                              
* Purpose : Disable the M25P128                                           
****************************************************************************
}
Sub M25PDisable()
    High(CS)                                                    ' M25PDisable the chip again
End Sub
{
****************************************************************************
* Name    : Enable(PRIVATE)                                               
* Purpose : Enable the M25P128                                            
****************************************************************************
}    
Sub M25PEnable()
    Low(CS)                                                     ' M25PEnable the chip
End Sub
{
****************************************************************************
* Name    : Write(PRIVATE)                                                
* Purpose : Write M25PEnable the M25P128                                  
****************************************************************************
}
Sub WriteEnable()
    M25PEnable()                                                ' M25PEnable the chip 
    Shift.Out(MSB_FIRST,M25P_WREN,8)                            ' Send the WREN command
    M25PDisable()                                               ' M25PDisable the chip again
End Sub                                                         ' All done return
{
****************************************************************************
* Name    : WriteDisble(PRIVATE)                                          
* Purpose : Write M25PDisable the M25P128                                 
****************************************************************************
}   
Sub WriteDisable()    
    M25PEnable()                                                ' M25PEnable the chip 
    Shift.Out(MSB_FIRST,M25P_WRDI,8)                            ' Send the WRDI command
    M25PDisable()                                               ' M25PDisable the chip again
End Sub  
{
****************************************************************************
* Name    : ReadID                                                        
* Purpose : Read the 24 Bit Id of the M25P128                             
****************************************************************************
}        
Public Function ReadId() As LongWord    
    M25PEnable()                                                ' M25PEnable the chip 
    Shift.Out(MSB_FIRST,M25P_RDID,8)                            ' Send the RDID command
    ReadId.BYTE0 = Shift.In(MSB_POST,8)                         ' readin the 24 bit ID data
    ReadId.BYTE1 = Shift.In(MSB_POST,8)
    ReadId.BYTE2 = Shift.In(MSB_POST,8)                         ' M25PDisable the chip again
    M25PDisable()
End Function
{
****************************************************************************
* Name    : ReadStatus(PRIVATE)                                           
* Purpose : Read the Read Status Register of the the M25P128              
****************************************************************************
}    
Sub ReadStatus()
    M25PEnable()                                                ' M25PEnable the chip
    Shift.Out(MSB_FIRST,M25P_RDSR,8)                            ' Send the RDSR command
    M25PStatus = Shift.In(MSB_POST,8)
End Sub
{
****************************************************************************
* Name    : ReReadStatus(PRIVATE)                                     
* Purpose : ReRead the Read Status Register of the the M25P128            
****************************************************************************
}     
Sub ReReadStatus()
    M25PStatus = Shift.In(MSB_POST,8)
End Sub
{
****************************************************************************
* Name    : BulkErase                                                 
* Purpose : Erase the Entire M25P128 DATA - TAKES AWHILE TO OPERATE       
****************************************************************************
}     
Public Sub BulkErase() 
    ReadStatus()                                                ' Check its Not in the middle of a current Write
    While WIP = 1
        ReReadStatus() 
    Wend 
    M25PDisable()                                               ' M25PDisable To ensure the Read STATUS is stopped
    DelayUS(4)
    M25PEnable()                                                ' M25PEnable the chip again
    WriteEnable()
    M25PEnable()                                                ' M25PEnable the chip
    Shift.Out(MSB_FIRST,M25P_BE,8)                              ' Sent the Bulk erase instruction
    M25PDisable()                                               ' M25PDisable the chip To start the erase  
End Sub
{
****************************************************************************
* Name    : SectorErase                                               
* Purpose : Erase the One Sector of the M25P128 DATA                      
****************************************************************************
} 
Public Sub SectorErase(pM25PDataAddress As LongWord)
    ReadStatus()                                            ' Check its not in the middle of a current write
    While WIP = 1
        ReReadStatus() 
    Wend 
    M25PDisable()                                               ' M25PDisable to ensure the read status is stopped
    DelayUS(4)
    M25PEnable()                                                ' M25PEnable the chip again
    WriteEnable()
    M25PEnable()                                                ' M25PEnable the chip
    Shift.Out(MSB_FIRST,M25P_SE,8)                              ' Sent the Sector erase instruction
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE2,8)                ' Send the address
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE1,8)                ' The whole sector that the address is in will be 
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE0,8)                ' Erased
    M25PDisable()                                                ' M25PDisable the chip to start the erase
End Sub
{
****************************************************************************
* Name    : WriteStatus                                               
* Purpose : Make areas of the M25P128 Data read only                      
****************************************************************************
}
Sub WriteStatus(pM25PWriteByte As Byte)
    WriteEnable()
    M25PEnable()                                                ' M25PEnable the chip
    Shift.Out(MSB_FIRST,pM25PWriteByte,8)
    M25PDisable()                                               ' M25PDisable the chip again
End Sub          
{
****************************************************************************
* Name    : WriteByte                                                 
* Purpose : Write a single byte and the specified address                 
****************************************************************************
}
Public Sub WriteByte(pM25PDataAddress As LongWord,pM25PWriteByte As Byte)
    ReadStatus()                                            ' Check its not in the middle of a current write
    While WIP = 1   
        ReReadStatus()
    Wend
    M25PDisable()                                               ' M25PDisable to ensure the read status is stopped
    DelayUS(4)
    M25PEnable()                                                ' M25PEnable the chip again
    WriteEnable()                                               ' Write M25PEnable the device
    M25PEnable()                                                ' M25PEnable the device
    Shift.Out(MSB_FIRST,M25P_PP,8)                              ' Sent the Page Write instruction
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE2,8)               ' Send the address
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE1,8)
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE0,8)
    Shift.Out(MSB_FIRST,pM25PWriteByte,8)                       ' Finaly the data
    M25PDisable()                                               ' Finish the write by bringing the CS line high
End Sub
{
****************************************************************************
* Name    : WriteBytes                                                
* Purpose : Write up to 255 bytes from the  specified address             
* CAUTION : OVERLAPPING A PAGE BOUNDARY CAN CAUSE SERIOUS ERRORS          
****************************************************************************
}    
Public Sub WriteBytes(pM25PDataAddress As LongWord,ByRef pM25PDataOut() As Byte)
    ReadStatus()                                                ' Check its not in the middle of a current write
    While WIP = 1
        ReReadStatus()
    Wend
        M25PDisable()                                           ' M25PDisable to ensure the read status is stopped
    DelayUS(4)
    M25PEnable()                                                ' M25PEnable the chip again
    WriteEnable()                                               ' Write M25PEnable the device
    M25PEnable()                                                ' M25PEnable the device
    Shift.Out(MSB_FIRST,M25P_PP,8)                              ' Sent the Page Write instruction
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE2,8)               ' Send the address
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE1,8)
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE0,8)

    For M25PIndex = 0 To Bound(pM25PDataOut)                    ' Via FRS0 send all the data requested
        Shift.Out(MSB_FIRST,pM25PDataOut(M25PIndex),8)
    Next 
    M25PDisable()                                               ' Finish the write by bringing the CS line high
End Sub        
{
****************************************************************************
* Name    : ReadByte                                                  
* Purpose : Read a single byte at the specified address                 
****************************************************************************
}
Public Function ReadByte(pM25PDataAddress As LongWord) As Byte
    ReadStatus()                                                ' Check its not in the middle of a current write
    While WIP = 1
        ReReadStatus() 
    Wend 
    M25PDisable()                                               ' M25PDisable to ensure the read status is stopped
    DelayUS(4)
    M25PEnable()                                                ' M25PEnable the chip again   
    Shift.Out(MSB_FIRST,M25P_Read,8)                            ' Sent the data read instruction
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE2,8)               ' Send the address
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE1,8)
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE0,8)
    ReadByte = Shift.In(MSB_POST,8)                             ' Read the byte in
    M25PDisable()                                                 
End Function
{
****************************************************************************
* Name    : ReadBytes                                                 
* Purpose : Read a multiple bytes byte at the specified address           
****************************************************************************
}    
Public Sub ReadBytes(pM25PDataAddress As LongWord,ByRef pM25PDataIn() As Byte,pBytesToRead As LongWord)
    If pBytesToRead > Bound(pM25PDataIn()) Then                 'Check to make sure you do not overfill the array
        pBytesToRead = Bound(pM25PDataIn())
    EndIf

    ReadStatus()                                                ' Check its not in the middle of a current write
    While WIP = 1
        ReReadStatus() 
    Wend 
    M25PDisable()                                               ' M25PDisable to ensure the read status is stopped
    DelayUS(4)
    M25PEnable()                                                ' M25PEnable the chip again   
    Shift.Out(MSB_FIRST,M25P_Read_Fast,8)                       ' Sent the data read instruction
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE2,8)               ' Send the address
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE1,8)
    Shift.Out(MSB_FIRST,pM25PDataAddress.BYTE0,8)
    Shift.Out(MSB_FIRST,M25P_Dummy,8)

    For M25PIndex = 0 To pBytesToRead - 1                       
        pM25PDataIn(M25PIndex) = Shift.In(MSB_POST,8)
    Next 
    M25PDisable()                                                 
End Sub
{
****************************************************************************
* Name    : Init                                                          
* Purpose : Setup the Port Pins                                           
****************************************************************************
}
Public Sub Init()
    Output(CS)
    Shift.SetClock(CLK,TRUE) //initialise shiftOUT clock pin, IDLE HIGH
    Shift.SetInput(SDI)
    Shift.SetOutput(SDO) //initialise shiftOUT pin 
End Sub