M25P128Module
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