News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

18F4685 access to SPI Flash Memory

Started by Dave-S, Jan 31, 2022, 08:18 PM

Previous topic - Next topic

Dave-S

Trying to write/read to a Microchip SST25VF080B flash memory but cannot get it to work.
Code below to just read the STATUS register.

Running the code just gives zero.
It appears from the scope output there is no clock output, but on the SO pin
there appears to be clock pulses.  I have used this board ok for IC2 and now
with those devices removed trying SPI.

In the ppi file it sets the IC2 but there is no setting for SPI.


Any ideas why this code does not work?  What iniates the clock start?

Thanks David

spi.png

Device = 18F4685

Config_Start
  OSC = HS      ;HS oscillator
  FCMEN = OFF   ;Fail-Safe Clock Monitor disabled
  IESO = OFF    ;Oscillator Switchover mode disabled
  PWRT = OFF    ;PWRT disabled
  BOREN = BOHW  ;Brown-out Reset enabled in hardware only (SBOREN is disabled)
  BORV = 3      ;VBOR set to 2.1V
  WDT = OFF     ;WDT disabled (control is placed on the SWDTEN bit)
  WDTPS = 32768 ;1:32768
  PBADEN = On   ;PORTB<4:0> pins are configured as analog input channels on Reset
  LPT1OSC = OFF ;Timer1 configured for higher power operation
  MCLRE = On    ;MCLR pin enabled; RE3 input pin disabled
  STVREN = On   ;Stack full/underflow will cause Reset
  LVP = OFF     ;Single-Supply ICSP disabled
  BBSIZ = 1024  ;1K words (2K bytes) Boot Block
  XINST = OFF   ;Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
  Debug = OFF   ;Background debugger disabled, RB6 and RB7 configured as general purpose I/O pins
  Cp0 = OFF     ;Block 0 (000800-003FFFh) not code-protected
  CP1 = OFF     ;Block 1 (004000-007FFFh) not code-protected
  CP2 = OFF     ;Block 2 (008000-00BFFFh) not code-protected
  CP3 = OFF     ;Block 3 (00C000-00FFFFh) not code-protected
  CP4 = OFF     ;Block 4 (010000-013FFFh) not code-protected
  CP5 = OFF     ;Block 5 (014000-017FFFh) not code-protected
  CPB = OFF     ;Boot block (000000-0007FFh) not code-protected
  CPD = OFF     ;Data EEPROM not code-protected
  WRT0 = OFF    ;Block 0 (000800-003FFFh) not write-protected
  WRT1 = OFF    ;Block 1 (004000-007FFFh) not write-protected
  WRT2 = OFF    ;Block 2 (008000-00BFFFh) not write-protected
  WRT3 = OFF    ;Block 3 (00C000-00FFFFh) not write-protected
  WRT4 = OFF    ;Block 4 (010000-013FFFh) not write-protected
  WRT5 = OFF    ;Block 5 (014000-017FFFh) not write-protected
  WRTC = OFF    ;Configuration registers (300000-3000FFh) not write-protected
  WRTB = OFF    ;Boot block (000000-0007FFh) not write-protected
  WRTD = OFF    ;Data EEPROM not write-protected
  EBTR0 = OFF   ;Block 0 (000800-003FFFh) not protected from table reads executed in other blocks
  EBTR1 = OFF   ;Block 1 (004000-007FFFh) not protected from table reads executed in other blocks
  EBTR2 = OFF   ;Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks
  EBTR3 = OFF   ;Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks
  EBTR4 = OFF   ;Block 4 (010000-013FFFh) not protected from table reads executed in other blocks
  EBTR5 = OFF   ;Block 5 (014000-017FFFh) not protected from table reads executed in other blocks
  EBTRB = OFF   ;Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End

;**** End of Fuse Configurator Settings ****
;-------------------------------------------------------------------------------
Declare Xtal = 20

SSPSTAT.7 = 0     'input data sampld at middle of data output
SSPSTAT.6 = 1     'transmit occurs on transition from active to idle clock stste

SSPCON1.7 = 0     'No collison
SSPCON1.6 = 0      'no overflow
SSPCON1.5 = 1       'enable SPI
SSPCON1.4 = 0      'idle clock low
SSPCON1.3 = 0     'Fosc/4
SSPCON1.2 = 1      'Fosc/64
SSPCON1.1 = 0      'Fosc/4
SSPCON1.0 = 0     'Fosc/4

TRISC.5 = 0      'SO
TRISC.4 = 1      'SI
TRISC.3 = 0       'clock
TRISD.3 = 0       'Select  device

'SPI****EEPROM***************************************************************************
Symbol Data_SI = PORTC.4         'Data in
Symbol Data_SO = PORTC.5         'Data out
Symbol SPI_CLK = PORTC.3         'Clock
Symbol EEProm1_SE = PORTD.3       'EEPROM  select pin
Symbol R_MsbPost = 2          'highest bit first read data after sending clock. clock idles low
Symbol W_MsbFirst = 1         'highest Bit first, clock idles Low
Symbol R_address = $03         'read address
Symbol W_address = $02         'write address
Symbol Write_EN = $06           'enable write
Symbol Write_Dis = $04          'disable write

High EEProm1_SE      'select pin
Low SPI_CLK
 
'******************************************************************************************
All_Digital = True
DelayMS 500
'***************     MAIN    *****************************************************************
Main:
SerOut PORTD.1, 84, ["Main", 13]
 
 High PORTB.5        'micro running
 DelayMS 500
 Low PORTB.5
 
 '*********************************************************************************************************
DelayMS 500

 GoSub MemStat
 GoTo Main
'*******************************************************************************************************************
  '****Mem Stat********************************************************
 MemStat:
  Dim Mvar2 As Byte
  Mvar2 = 0
  'Low EEProm1_SE
  'SHOut Data_SO, SPI_CLK, W_MsbFirst, [Write_EN]         'enable write
  'High EEProm1_SE
 
  Low EEProm1_SE
  SHOut Data_SO, SPI_CLK, W_MsbFirst, [R_address, $05]      'set status address
  SHIn Data_SI, SPI_CLK, R_MsbPost, [Mvar2]                  'read satus
  High EEProm1_SE
 
  'Low EEProm1_SE
  'SHOut Data_SO, SPI_CLK, W_MsbFirst, [Write_Dis]           'disable write
 ' High EEProm1_SE
   
  SerOut PORTD.1, 84, ["EEprom Status = ", Hex Mvar2, 13]
 
 Return

m.kaviani

#1
there is data on the data line. do you have clock?

trastikata

Reading the status register is done by sending 0x05 and then clocking in 8 bits, READ opcode 0x03 is not required.

P.s. Don't forget to pull up the HOLD pin if not used in code.

tumbleweed

SHOut and SHIn are software-driven bit-banged commands... they don't use the SPI peripheral so if you want to use them remove all your SSPCON1 setup statements.

If you do want to use the MSSP peripheral, once you get it setup then a write to the SSPBUF register generates a transfer. Keep in mind that with SPI write and read occur at the same time, so after writing a byte you'll have read data in the SSPBUF.


Dave-S

Quote from: tumbleweed on Jan 31, 2022, 10:17 PMSHOut and SHIn are software-driven bit-banged commands... they don't use the SPI peripheral so if you want to use them remove all your SSPCON1 setup statements.

If you do want to use the MSSP peripheral, once you get it setup then a write to the SSPBUF register generates a transfer. Keep in mind that with SPI write and read occur at the same time, so after writing a byte you'll have read data in the SSPBUF.


Many thanks for the Reply, I original tried Shout and Shin on different pins because I had devices on the IC2 pins and it did not work.  I then tried it on the SPI pins and doest not work so added SSPCON1 setup statements

Are there any code examples how to do this writing/reading to the SSPBUF.

Thanks David




Stephen Moss

Here is the setup I used for configuring SPI in one of my Projects that may give you an indication of what to do, although register & bit names may differ on the device you are using...
Symbol SSPM0 = SSPCON1.0 ' Synchronous Serial Port Mode Select bits
Symbol SSPM1 = SSPCON1.1 ' Synchronous Serial Port Mode Select bits
Symbol SSPM2 = SSPCON1.2 ' Synchronous Serial Port Mode Select bits
Symbol SSPM3 = SSPCON1.3 ' Synchronous Serial Port Mode Select bits
Symbol CKP = SSPCON1.4  ' Clock Polarity Select bit
Symbol SSPEN = SSPCON1.5 ' Synchronous Serial Port Enable flag
Symbol SSPOV = SSPCON1.6 ' Receive Overflow Indicator flag
Symbol WCOL = SSPCON1.7  ' Write Collision Detect flag

WCOL=0  'Clear Write collision Flag
SSPOV=0 'Clear Overflow Flag, Overflow only occurs if slave device
SSPEN=1 'Enable serial port
CKP=1  'Clock Idle state is high
SSPM3=0 : SSPM2=0 : SSPM1=1 : SSPM0=0  'SPI Mode, clock = Fosc/64 (1.6uS)

Symbol BF = SSPSTAT.0        ' Buffer Full Status flag
Symbol UA = SSPSTAT.1        ' Update Address (10-bit I2C mode only)
Symbol R_W = SSPSTAT.2      ' Read/Write bit information (I2C mode only)
Symbol S = SSPSTAT.3        ' I2C Start
Symbol I2C_STR = SSPSTAT.3  ' I2C Start
Symbol P = SSPSTAT.4        ' I2C Stop
Symbol I2C_STP = SSPSTAT.4  ' I2C Stop
Symbol D_A = SSPSTAT.5      ' Data/NOT Address bit (I2C mode only)
Symbol DATA_ADDR = SSPSTAT.5 ' Data/NOT Address bit (I2C mode only)
Symbol CKE = SSPSTAT.6      ' SPI Clock Edge Select
Symbol SMP = SSPSTAT.7      ' Sample bit

SMP=1 'Sample Data at end of output
CKE=0 'Data transmitted on Idle to Active state (Falling edge)
'Buffer Full status Flag cleared by write to SSPBUF
'UA, R_W, S, I2C_STR, P, I2C_STP, D_A, DATA_ADDR are not used


Here is some of the code I use to read the data...
Low Sel                        'Select the SPI device
SSPBUF=$00                'Command line idle (Clock in next byte)
Repeat Until BF = 1        'Wait until Buffer full interrupt - You will need an interrupt handler to clear this
XR = SSPBUF REV 8          'Rev 8 reversed bit order back to LSB first
SSPBUF=$00                'Command line idle (Clock in next byte)
Repeat Until BF = 1        'Wait until Buffer full interrupt
YR = SSPBUF REV 8          'Rev 8 reversed bit order back to LSB first
High Sel                  'Deselect the SPI device

One thing I noticed in your waveforms is that SPI enable is a very short pulse, I assume that is the same as my Device Select line which as you will notice above I activate it before starting transmission by writing the first byte to the SSPBUF and keep it active until after I have received the last byte of data.
It is very easy to make an error during configuration, make sure the bus clock speed, polarity, edge and idle state are correct for the device being connected is correct, double check you have the sample for the incoming data set to the correct point.

As Tumbleweed stated if using the MSSP module a write to the SSPBUF starts transmission, therefore you have to write a byte to the buffer for each byte you want to receive, also remember that the SSPBUF is circular in nature. When you write to the buffer the written byte is transmitted (shifted out like a PISO) MSB first, as each bit is shifted out the in coming data is shifted in to the LSB (like a SIPO) and so the first bit received ends up a the MSB of the SSPBUF (SSPBUF.7) and the last bit in the LSB (SSPBUF.0).
Depending on the device you are communication with that may be the wrong bit order so you also need to be certain of the bit order the device uses for Transmit and Receive as you may have to reverse the bit order of the incoming & outgoing bytes as I had to.

top204

#6
Using a device's MSSP peripheral for SPI is all down to the setup required. However, Microchip have a tendency to change how they are setup, so a read of the datasheet is always required, and then reading again and again to try and find the, not so obvious, querks required. :-) The SPI and I2C peripherals used to  be so tidy and easy to use, but change for the sake of change has made them now rather difficult to setup.

A few years ago, I was trying to interface to a SST25VF080B chip for a customer who wanted to record 30 seconds of audio then play it back in different modes for a toy, but I eventually gave up on it and used another flash chip because the timings on the SST25VF080B chip seems to be very critical, which is not what SPI is about, and I wasted a few days trying to get one of them to communicate back! On the logic analyser I could see things were working well and the lines were toggling nicely and were in the right order and the right value, but the chips, sometimes, refused to see it!!

The chip I chose as an alternative was an S25FL208K, because it was readily available and a lot less expensive than the Microchip device. The library code I used is listed below, and it is for a PIC18F26J11, so the MSSP peripheral will, most likely, need altering for other 18F devices. The SPI peripheral setup and read/write stayed the same as for the SST25VF080B, but the S25FL208K flash chip worked nicely first time and every time. :-) Hopefully it will help you or others.

$ifndef __S25FL208K__
$define __S25FL208K__
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' S25FL208K 8-Mbit/1024 kbyte flash memory chip interface for a PIC18F26J11 device
' Written by Les Johnson for the Positron8 BASIC compiler
'
$ifndef False
    $define False 0
$endif
$ifndef True
    $define True 1
$endif

$define cS25FL_AmountOfPages 4096           ' The amount of pages within the S25FL208K flash memory chip
$define cS25FL_PageSize      256            ' The size of the page within the S25FL208K flash memory chip
'
' S25FL208K commands
'
$define cCMD_WriteEnable        $06
$define cCMD_WriteDisable       $04
$define cCMD_Read_Status        $05         ' (S7-S0)
$define cCMD_Write_Status       $01         ' (S7-S0)
$define cCMD_Read_Data          $03         ' Required A23-A16, A15-A8, A7-A0 (D7-D0) (next byte) continuous
$define cCMD_Fast_Read          $0B         ' Required A23-A16 A15-A8, A7-A0, dummy byte, (D7-D0), (Next byte) continuous
$define cCMD_Page_Program       $02         ' Required A23-A16 A15-A8, A7-A0, (D7-D0), (Next byte) Up to 256 bytes
$define cCMD_Block_Erase        $D8         ' (64 kB) Required A23-A16, A15-A8, A7-A0
$define cCMD_Sector_Erase       $20         ' (4 kB) Required A23-A16, A15-A8, A7-A0
$define cCMD_Chip_Erase         $C7         ' Or can use $60
$define cCMD_Power_Down         $B9
$define cCMD_Release_Power_Down $AB         ' Also Device ID: Dummy Byte, Dummy Byte, Dummy Byte, (ID7-ID0)
$define cCMD_Device_ID          $90         ' Dummy byte, Dummy byte, $00, (M7-M0), (ID7-ID0)
$define cCMD_JEDEC_ID           $9F         ' (M7-M0)Manufacturer, (ID15-ID8) Memory Type, (ID7-ID0) Capacity
$define cDummy_Byte             $00
'
' S25FL208K Status register bits
'
$define cSRP_Bit 7                         
$define cREV_Bit 6                         
$define cBP3_Bit 5                         
$define cBP2_Bit 4                        
$define cBP1_Bit 3                         
$define cBP0_Bit 2                         
$define cWEL_Bit 1                         
$define cWIP_Bit 0                         
'
' Setup default SPI pins if they are not placed in the main program's listing
'
$ifndef SPI_CS_Pin
    $define SPI_CS_Pin PORTB.7              ' The CS pin to the S25FL208K flash memory chip
$endif

$ifndef SPI_SCK_Pin                         ' Connects to the SCK pin of the S25FL208K flash memory chip
    $define SPI_SCK_Pin PORTC.3
$endif

$ifndef SPI_SI_Pin                         ' Connects to the SO pin of the S25FL208K flash memory chip
    $define SPI_SI_Pin PORTC.4
$endif

$ifndef SPI_SO_Pin                         ' Connects to the SI pin of the S25FL208K flash memory chip
    $define SPI_SO_Pin PORTC.5
$endif
'
' Create some global variables for the library code
'
    Dim bStatusValue     As Byte                    ' Holds the value read from the status register of the flash memory chip
    Dim bManufacturer_ID As Byte                    ' Holds the manufacturer value read from the flash memory chip (should be $01)
    Dim bMemory_Type_ID  As Byte                    ' Holds the Memory Type value read from the flash memory chip (should be $13)

    Dim S25FL_wArrayAddress       As Word Access    ' Holds the address of the RAM array to read and write to
    Dim S25FL_bIndex              As Byte Access    ' Used for counting bytes within a page or array etc...
    Dim S25FL_wSectorBlock        As Word Access    ' Holds the sector or the block
    Dim S25FL_dSectorBlockAddress As Dword Access   ' Holds the sector or the block address

    Dim S25FL_wFSR0 As FSR0L.Word

'-----------------------------------------------------------------------------------------------
$define S25FL_CS_Enable() PinClear SPI_CS_Pin
$define S25FL_CS_Disable() PinSet SPI_CS_Pin

'-----------------------------------------------------------------------------------------------
' Open the SPI peripheral to talk to the S25FL208K flash memory chip on a PIC18F26J11 device
' Input     : None
' Output    : None
' Notes     : SPI_FOSC_16, MODE_00, SMPMID
'
Proc S25FL_SPI_Open()
    SSPSTATbits_SMP = 0           ' Input data sampled at middle of data output time
    SSPSTATbits_CKE = 1           ' Output data changes on clock transition from active to idle
    PinOutput SPI_SCK_Pin         ' Make the CLK pin an output
    PinInput SPI_SI_Pin           ' Make the Din pin an input
    PinOutput SPI_SO_Pin          ' Make the Dout pin an output
    SSPCON1 = %00100001           ' Enable SPI Master mode. Clock = FOSC/16
EndProc

'-----------------------------------------------------------------------------------
' Write a single Byte to the SPI bus on a PIC18F26J11 device
' Input     : Single 8-bit variable for SPI bus.
' Output    : Input data can be picked up by reading SSPBUF
' Notes     : None
'
$define S25FL_hWriteByte(pDataOut) '
    SSPSTATBits_BF = 0             '
    PIR1bits_SSPIF = 0             '
    SSPBUF = pDataOut              '
    While PIR1bits_SSPIF = 0: Wend

'-----------------------------------------------------------------------------------
' Read a single Byte from the SPI bus on a PIC18F26J11 device
' Input     : None
' Output    : Contents of SSPBUF register
' Notes     : None
'
$define S25FL_hReadByte(pResult)    '
    WREG = SSPBUF                   '
    PIR1bits_SSPIF = 0              '
    SSPBUF = $FF                    '
    While PIR1bits_SSPIF = 0: Wend  '
    pResult = SSPBUF

'-----------------------------------------------------------------------------------
' Write a Word to the SPI bus
' Input     : 16-bit variable for SPI bus.
' Output    : None
' Notes     : Writes are Least Siginificant Byte first
'
$define S25FL_hWriteWord(pDataOut)   '
    S25FL_hWriteByte(pDataOut.Byte0) '
    S25FL_hWriteByte(pDataOut.Byte1)

'-----------------------------------------------------------------------------------
' Read a Word from the SPI bus
' Input     : None
' Output    : pResult
' Notes     : Reads are Least Siginificant Byte first
'
$define S25FL_hReadWord(pResult)    '
    S25FL_hReadByte(pResult.Byte0)  '
    S25FL_hReadByte(pResult.Byte1)

'-------------------------------------------------------------------------------------------
' Initialise the SPI interface
' Input     : None
' Output    : None
' Notes     : None
'
Proc S25FL_Setup()
    DelayMS 50
    PinHigh SPI_CS_Pin                      ' Disable the SPI interface
    S25FL_SPI_Open()                        ' Setup the SPI peripheral to talk to the flash memory chip
EndProc

'-------------------------------------------------------------------------------------------
' Set Write Enable on the S25FL208K flash memory chip
' Input     : None
' Output    : None
' Notes     : None
'
Proc S25FL_Enable_Writes()
    S25FL_CS_Enable()
    S25FL_hWriteByte(cCMD_WriteEnable)
    S25FL_CS_Disable()
EndProc

'-------------------------------------------------------------------------------------------
' Clear Write Enable on the S25FL208K flash memory chip
' Input     : None
' Output    : None
' Notes     : None
'
Proc S25FL_Disable_Writes()
    S25FL_CS_Enable()
    S25FL_hWriteByte(cCMD_WriteDisable)
    S25FL_CS_Disable()
EndProc

'-------------------------------------------------------------------------------------------
' Read the status register from the S25FL208K flash memory chip
' Input     : None
' Output    : pResult holds the status value
' Notes     : Status Register Bit Locations:
'           : R7  R6  R5  R4  R3  R2  R1  R0
'           : SRP REV BP3 BP2 BP1 BP0 WEL WIP
'           :   When the WIP bit is set to 1, the device is busy performing an operation.
'           :   When the bit is cleared to 0, no operation is in progress.
'           :   The WEL bit is cleared to 0 at the end of any successful program, write, or erase operation.
'           :   After a power down/power up sequence, hardware reset, or software reset, the Write Enable Latch is set to a 0.
'           :   BP3, BP2, BP1 and BP0 are Block Protect Bits.
'           :   REV is Reserved Bits for future use
'           :   When the SRP bit is set to a 0 state (factory default) the WP# pin has no control over status register.
'           :   When the SRP pin is set to a 1, the Write Status Register instruction is locked out while the WP# pin is low.
'           :   When the WP# pin is high the Write Status Register instruction is allowed.
'
$define S25FL_ReadStatus(pResult)      '
    S25FL_CS_Enable()                  '
    S25FL_hWriteByte(cCMD_Read_Status) '
    S25FL_hReadByte(pResult)           '
    S25FL_CS_Disable()

'-------------------------------------------------------------------------------------------
' Wait for the S25FL208K flash memory chip to be ready
' Input     : None
' Output    : None
' Notes     : Reads the WIP bit from the status register and exits when it is set to 0
'           : It may need a timeout for the loop, just in case
'
Proc S25FL_hWaitForReady()
    Do
        S25FL_ReadStatus(WREG)
        If WREG.cWIP_Bit = 0 Then ExitProc
    Loop
EndProc

'-------------------------------------------------------------------------------------------
' Read the device ID from the S25FL208K flash memory chip
' Input     : None
' Output    : bManufacturer_ID holds the manufacturer code ($01)
'           : bMemory_Type_ID holds the memory type value ($13)
' Notes     : None
'
Proc S25FL_Read_DeviceID()
    S25FL_CS_Enable()                                       ' Enable the SPI interface
    S25FL_hWriteByte(cCMD_Device_ID)
    S25FL_hWriteByte($00)
    S25FL_hWriteByte($00)
    S25FL_hWriteByte($00)
    S25FL_hReadByte(bManufacturer_ID)
    S25FL_hReadByte(bMemory_Type_ID)
    S25FL_CS_Disable()                                      ' Disable the SPI interface
EndProc

'-------------------------------------------------------------------------------------------
' Erase a 4K sector from the S25FL208K flash memory chip
' Input     : pSector holds the 4K byte sector to erase
' Output    : None
' Notes     : Erasure takes 50 milliseconds
'
Proc S25FL_Erase_4K(pSector As S25FL_wSectorBlock)
    S25FL_dSectorBlockAddress = pSector * 4096              ' Calculate the address of the 4K sector to erase
    S25FL_Enable_Writes()                                   ' Enable writing to the flash memory chip
    S25FL_CS_Enable()                                       ' Enable the SPI interface
    S25FL_hWriteByte(cCMD_Sector_Erase)                     ' Send the command for a sector erase (4K bytes)
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte2)       ' \
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte1)       ' |  Send the sector address
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte0)       ' /
    S25FL_CS_Disable()                                      ' Disable the SPI interface
    S25FL_hWaitForReady()                                   ' Wait for the sector to be erased
EndProc

'-------------------------------------------------------------------------------------------
' Erase a 64K block from the S25FL208K flash memory chip
' Input     : pBlock holds the 64K byte block to erase
' Output    : None
' Notes     : Erasure takes 500 milliseconds
'
Proc S25FL_Erase_64K(pBlock As S25FL_wSectorBlock)
    S25FL_dSectorBlockAddress = pBlock * 65536              ' Calculate the address of the 64K block to erase
    S25FL_Enable_Writes()                                   ' Enable writing to the flash memory chip
    S25FL_CS_Enable()                                       ' Enable the SPI interface
    S25FL_hWriteByte(cCMD_Block_Erase)                      ' Send the command for a block erase (64K bytes)
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte2)       ' \
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte1)       ' |  Send the block address
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte0)       ' /
    S25FL_CS_Disable()                                      ' Disable the SPI interface
    S25FL_hWaitForReady()                                   ' Wait for the block to be erased
EndProc

'-------------------------------------------------------------------------------------------
' Erase all the memory from the S25FL208K flash memory chip
' Input     : None
' Output    : None
' Notes     : Erasure takes 7 seconds
'
Proc S25FL_Erase_Chip()
    S25FL_Enable_Writes()                                   ' Enable writing to the flash memory chip
    S25FL_CS_Enable()                                       ' Enable the SPI interface
    S25FL_hWriteByte(cCMD_Chip_Erase)                       ' Send the command to erase the flash memory chip
    S25FL_CS_Disable()                                      ' Disable the SPI interface
    S25FL_hWaitForReady()                                   ' Wait for the chip to be erased
EndProc

'-------------------------------------------------------------------------------------------
' Write to a 256 page on the S25FL208K flash memory chip
' Input     : pBuffAddr holds the address of the array to write to the flash memory chip
'           : pPage holds the page to write to the flash memory chip
' Output    : None
' Notes     : Takes 1.5ms for the page write to occur
'
Proc S25FL_WritePage(ByRef pBuffAddr As S25FL_wArrayAddress, pPage As S25FL_wSectorBlock)
    pBuffAddr = pPage * 256                                 ' Calculate the page to write to as an address
    S25FL_Enable_Writes()                                   ' Enable writing to the flash memory chip
    S25FL_CS_Enable()                                       ' Enable the SPI interface
    S25FL_hWriteByte(cCMD_Page_Program)                     ' Send the command to write a 256 byte page to the flash memory chip
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte2)       ' \
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte1)       ' | Send the address
    S25FL_hWriteByte($00)                                   ' /

    S25FL_wFSR0 = pBuffAddr                                 ' Load FSR0L\H with the address of the array to read from
    For S25FL_bIndex = 255 DownTo 0                         ' Create a 256 byte loop to write to the page
        S25FL_hWriteByte(POSTINC0)                          ' Write a byte to the page
    Next                                                    ' Close the loop
    S25FL_CS_Disable()                                      ' Disable the SPI interface
    S25FL_hWaitForReady()                                   ' Wait for the page to be written
EndProc

'-------------------------------------------------------------------------------------------
' Read from a 256 page on the S25FL208K flash memory chip
' Input     : pPage holds the page to read from the flash memory chip
' Output    : pBuffAddr holds the address of the array to read into from the flash memory chip
' Notes     : None
'
Proc S25FL_ReadPage(ByRef pBuffAddr As S25FL_wArrayAddress, pPage As S25FL_wSectorBlock)
    pBuffAddr = pPage * 256                                 ' Calculate the page to read from, as an address
    S25FL_CS_Enable()                                       ' Enable the SPI interface
    S25FL_hWriteByte(cCMD_Read_Data)                        ' Send the command to read a 256 byte page from the flash memory chip
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte2)       ' \
    S25FL_hWriteByte(S25FL_dSectorBlockAddress.Byte1)       ' / Send the address
    S25FL_hWriteByte($00)                                   ' And the byte within the page

    S25FL_wFSR0 = pBuffAddr                                 ' Load FSR0L\H with the address of the array to fill with the data
    For S25FL_bIndex = 255 DownTo 0                         ' Create a 256 byte loop to read from the page
        S25FL_hReadByte(POSTINC0)                           ' Read a byte from the flash chip and load it into the array
    Next                                                    ' Close the loop
    S25FL_CS_Disable()                                      ' Disable the SPI interface
EndProc

'-------------------------------------------------------------------------------------------
'
_S25FL208K_Main_:
    S25FL_Setup()                                           ' Setup the device S25FL208K

$endif  ' __S25FL208K__

tumbleweed

Here are some common SPI terms you'll run across -

MOSI - master out, slave in (PIC master mode: SDO)
MISO - master in, slave out (PIC master mode: SDI)

SPI MODE
SPI uses a "mode" setting to describe the clock and data relationship
In the original SPI documentation the bits were called CPOL and CPHA, which on the PIC are different and are CKP and CKE

Most documentation will specify a mode (ie "Mode 0"), but sometimes it's shown using the notation (CPOL,CPHA) , ie "(0,0)"

SPI Mode     | PIC      |
terminology  | ctrl bits|
(CPOL,CPHA)  | CKP CKE  | Description
-------------+----------+-------------------------------------------
Mode 0 (0,0) |  0  1    | clk idle low, MOSI changes on falling edge
Mode 1 (0,1) |  0  0    | clk idle low, MOSI changes on rising edge
Mode 2 (1,0) |  1  1    | clk idle high, MOSI changes on rising edge
Mode 3 (1,1) |  1  0    | clk idle high, MOSI changes on falling edge
CKP and CKE are usually found in the SSPCON1 and SSPSTAT registers.

The most common is Mode 0 (CKP=0, CKE=1), but you need to check the datasheet for the SPI device
In my experience it's unusual for an SPI device to use a different bit order, but as Steven pointed out it's always a good idea to verify it.

And finally, if your pic has slewrate control on the IO port pins then set it to fast mode,
Many new devices default to slow, which won't work well for the faster MHz rates.
 

JonW

#8
The bit bashed shin and shout commands work well but it seems you cant even get the correct output from the pin, so my suggestion would be  to go back over the code and see if you can toggle the associated port pin first and see if there is another peripheral blocking the port.

Dave-S

Many ThanKs for all the replies, I will work through them and let you know how I get on.

David

Dave-S

Hi,
Have finally got this to work, the S25FL208K Les recommended was no longer available and the alternatives CYPRESS - INFINEON TECHNOLOGIES suggested were not available until the end of the year.

I got from Microchip forum a C code example which CYPRESS - INFINEON TECHNOLOGIES technical had
supplied them and converted to Proton.

The SST25VF080B has some funny quirks, you can only write to an address if it has been erased, this made it very difficult in trying to get it to work. Datasheet states if WP pin is high it will egnore WP settings in th Status REG, but it did not and I had to clear them first.

I had already started to try using several 1025 KB IC2 mem chip and using a Pic with 2 IC2 think I may go that root because the SST25VF080B is a 3.3 volt device and the other devices (compass, 3 axis accelerometer and wind sensor) I am using are 5 volts, I did try using a level shifter but it did not seem to work, also I think the coding will be much simpler.

Thanks for your advice David

trastikata

Quote from: Dave-S on Feb 07, 2022, 02:19 PMThe SST25VF080B has some funny quirks, you can only write to an address if it has been erased, this made it very difficult in trying to get it to work.

Hello Dave-S,

This is how normally FLASH memory works.

I never had any issues with Microchip's flash memory, but you need to read the datasheets carefully. Flash memory ICs, in most of the cases as storage, offer many advantages over the 1025/1024 EEPROM, not to mention capacity options - code is not much bigger or more difficult than the EEPROM.

Regarding the memory endurance - keep in mind that writing a single byte in EEPROM rewrites the entire page, whereas in FLASH you write in whole pages only.

Regards

Dave-S

HI,
The SST25VF080B you can write 1 byte to it, but to programme multiple bytes requires
quite a few lines of code and keep sending more ytes rather than just feeding from an array.

This is the instruction from data sheet.

Prior to any write operation, the Write-Enable (WREN) instruction must be executed. Initiate the AAI Word Program instruction by executing an 8-bit command, ADH, followed by address bits [A23-A0]. Following the addresses, two bytes of data are input sequentially, each one from MSB (Bit 7) to LSB (Bit 0). The first byte of data (D0) is programmed into the initial address [A23-A1] with A0=0, the second byte of Data (D1) is  programmed into the initial address [A23-A1] with A0=1. CE# must be driven high before executing the AAI Word Program instruction. Check the BUSY status before entering the next valid command. Once the device indicates it is no longer busy, data for the next two sequential addresses may be programmed, followed
by the next two, and so on.

So can you only send two at a time?auto-write.png

David

tumbleweed

QuoteSo can you only send two at a time?
Yes. The 25VF only has byte write and word write operations.

If you want to be able to write a larger hunk of data at a time then switch to the SST26VF series... they have page write operations.

There are differences between serial flash and serial EEPROM. Serial flash tends to work in sectors, while serial EEPROM can work in bytes.
With flash, you have to erase sectors (which can be rather large, several K bytes) before writing data to them.
With serial EEPROM you can typically do byte operations without having to erase first.

Serial EEPROM come in smaller densities, are typ available with I2C or SPI, and can usually operate up to 5V.
Serial flash devices tend to be SPI-only and 3.3V max. There are exceptions, but generally that's what you'll find.

If you're looking for an 8Mbit device (1MByte) with I2C and 5V operation you may have a hard time.

Dave-S

The SST25VF080B you can write to the entire memory, which I now have working and is ideal for my project of Sun Tracking data for every 5 minutes each day for the year.

4.4.4 AUTO ADDRESS INCREMENT (AAI)
WORD-PROGRAM
The AAI program instruction allows multiple bytes of
data to be programmed without re-issuing the next
sequential address location. This feature decreases
total programming time when multiple bytes or entire
memory array is to be programmed.

David

trastikata

Quote from: tumbleweed on Feb 08, 2022, 12:02 PMYes. The 25VF only has byte write and word write operations.

Dave-S, every day I learn something new, didn't know about the 1-byte write capable flash memory, thanks.