News:

Let's find out together what makes a PIC Tick!

Main Menu

Eeprom or SAF or????

Started by TimB, Jun 06, 2024, 07:12 AM

Previous topic - Next topic

TimB


Hi All

I recently decided on the pic16f15214 for a sensor project I thought it had Eeprom but looking in the data sheet it says nothing except use i2c to connect to bla bla bla Eeprom

So after ordering the board I'm now scrambling to find a part with Eeprom

The Pic12f675 has Eeprom but then I noticed other parts saying they use "high-endurance Program Flash Memory (PFM) in lieu of data
EEPROM"

Ok so back to the pic16f15214 and all it mentions is "Storage Area Flash (SAF) Block"

So my question is stick with eeprom device or struggle with overheads of SAF and block writing

Thanks

Tim

top204

#1
Below is an "experimental" set of procedures for reading and writing to NVM flash on a PIC16F15213 or PIC16F15214 device. I have converted a C template for the procedures, and as you can see, the NVM method that microchip adopted on some of its devices is truly dreadful ! Whoever thought it was a good idea to have to erase a block of flash just to write a single value to it is an utter imbecile:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Experimental NVM (None Volatile Memory) read and write procedures for a PIC16F213 or PIC16F214 device.
' Based upon the microchip code templates in C.
'
' Written for the Positron8 BASIC compiler by Les Johnson.
'
    Device = 16F15214                                   ' Tell the compiler what device to compile for
    Declare Xtal = 32                                   ' Tell the compiler what frequency the device is operating at (in MHz)
'
' Setup the EUSART
'
    Declare Hserial_Baud = 9600
    Declare HRSOut1_Pin = PORTA.0
'
' Create some defines for the NVM on the device
'
$define cFLASH_WRITE_BLOCKSIZE     _block               ' The write block zise (in 14-bit words)
$define cFLASH_ERASE_BLOCKSIZE     _erase               ' The erase block zise
$define cFLASH_END                 _code                ' The end address of Flash memory

$define cFLASH_WRITE_BLOCKSIZEMIN1 $eval (cFLASH_WRITE_BLOCKSIZE - 1)
$define cFLASH_ERASE_BLOCKSIZEMIN1 $eval (cFLASH_ERASE_BLOCKSIZE - 1)
$define cFLASH_ENDMIN1             $eval (cFLASH_END - 1)

$define cNVM_SIZE                  128                  ' The amount of NVM in the device
$define cNVM_START_ADDRESS         $eval ((cFLASH_END - 1) - cNVM_SIZE) ' The start address of NVM
'
' Global variables, constants and aliases can go here...
'
    Dim wValue As Word
    Dim wFlashAddress As Word
       
'---------------------------------------------------------------------------------------------------
' The main program starts here
'
Main:
    Setup()                                         ' Setup the program and any peripherals

    wFlashAddress = cNVM_START_ADDRESS              ' Load the address as the start of NVM
   
    wValue = Flash_ReadWord(wFlashAddress)          ' Read a value from NVM
    HRSOutLn "Value = ", Dec wValue                 ' Display the value on a serial terminal
   
    Flash_WriteWord(wFlashAddress, 1234)            ' Write a value to NVM
 
'---------------------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
    Osc_Int32MHz()                                          ' Setup the device to use the internal oscillator at 32MHz
'
' Any more setup code goes here
'
EndProc

'-------------------------------------------------------------------------------------
' Read a word from NVM
' Input     : pWriteAddr holds the address to read from
' Output    : Returns the value held in the location in Flash NVM
' Notes     : None
'
Proc Flash_ReadWord(pWriteAddr As Word), Word
    NVMADRL = pWriteAddr.Byte0
    NVMADRH = pWriteAddr.Byte1
    NVMCON1bits_NVMREGS = 0                                 ' Deselect Configuration space
    NVMCON1bits_RD = 1                                      ' Initiate Read
    DelayCS 1
    DelayCS 1
    Result.Byte0 = NVMDATL                                  ' \
    Result.Byte1 = NVMADRH                                  ' / Return the contents of NVMDATL\H
EndProc

'-------------------------------------------------------------------------------------
' Write a word to NVM
' Input     : pWriteAddr holds the address where to write the date too
'           : pValue holds the word to write
' Output    : None
' Notes     : None
'
Proc Flash_WriteWord(pWriteAddr As Word, pValue As Word)
Global Dim wFlash_RAM_Buffer[32] As Word Heap Shared        ' Storage for the NVM block
    Dim bStartAddr               As Word                    ' Holds the start address of the write
    Dim bOffset                  As Byte                    ' Holds the offset within the wFlash_RAM_Buffer array
    Dim bIndex                   As Byte                    ' Holds the index of the wFlash_RAM_Buffer array
    Symbol cWriteStart = cFLASH_ENDMIN1 ^ cFLASH_ERASE_BLOCKSIZEMIN1
   
    bStartAddr = pWriteAddr & cWriteStart
    bOffset = pWriteAddr & cFLASH_ERASE_BLOCKSIZEMIN1
'
' An entire row will be erased, so read and save the existing data first
'
    For bIndex = cFLASH_ERASE_BLOCKSIZEMIN1 To 0
        wFlash_RAM_Buffer[bIndex] = Flash_ReadWord((bStartAddr + bIndex))
    Next
    wFlash_RAM_Buffer[bOffset] = pValue                     ' Write at bOffset
    Flash_WriteBlock(bStartAddr)                            ' Writes wFlash_RAM_Buffer to current block
EndProc

'-------------------------------------------------------------------------------------
' Write a block of NVM
' Input     : pWriteAddr holds the address where to write the block of data
'           : Array wFlash_RAM_Buffer holds the data to write
' Output    : Returns 1 if the write was successful, else returns 0
' Notes     : On a PIC16F15213 and PIC16F15214 device, the block size is 32 words
'
Proc Flash_WriteBlock(pWriteAddr As Word), Bit     
Global Dim wFlash_RAM_Buffer[32] As Word Heap Shared        ' Storage for the NVM block
Global Dim bINTCON_Save          As Byte Heap Shared        ' Storage for the INTCON SFR
    Dim bStartAddr               As Word                    ' Holds the start address of the write
    Dim bIndex                   As Byte                    ' Holds the index of the wFlash_RAM_Buffer array
    Dim wTemp                    As bStartAddr              ' A temporary word variable
    Symbol cWriteStart = cFLASH_ENDMIN1 ^ cFLASH_ERASE_BLOCKSIZEMIN1
   
    bStartAddr = pWriteAddr & cWriteStart
    bINTCON_Save = INTCON                                   ' Save a copy of INTCON
'
' Flash write must start at the beginning of a row
'
    If pWriteAddr <> bStartAddr Then
        Result = 0                                          ' Return a value of 0
        ExitProc                                            ' Exit the procedure early
    EndIf
    INTCONbits_GIE = 0                                      ' Disable interrupts
    Flash_EraseBlock(pWriteAddr)                            ' Erase the NVM block
'
' Block write sequence
'
    NVMCON1bits_NVMREGS = 0                                 ' Deselect Configuration space
    NVMCON1bits_WREN = 1                                    ' Enable writes
    NVMCON1bits_LWLO = 1                                    ' Only load write latches
    For bIndex = 0 To cFLASH_WRITE_BLOCKSIZEMIN1
        NVMADRL = pWriteAddr.Byte0                          ' Load lower 8 bits of write address
        NVMADRH = pWriteAddr.Byte1                          ' Load upper 6 bits of write address
        '
        ' Load data in current address
        '
        wTemp = wFlash_RAM_Buffer[bIndex]                   ' Read an element of teh array into wTemp
        NVMDATL = wTemp.Byte0
        NVMDATH = wTemp.Byte1
        If bIndex = cFLASH_WRITE_BLOCKSIZEMIN1 Then         ' Is the last write occuring?
            NVMCON1bits_LWLO = 0                            ' Yes. So start Flash program memory write
        EndIf
        NVMCON2 = $55
        NVMCON2 = $AA
        NVMCON1bits_WR = 1
        DelayCS 1
        DelayCS 1
        Inc pWriteAddr
    Next
    NVMCON1bits_WREN = 0                                    ' Disable writes
    INTCON = bINTCON_Save                                   ' Restore INTCON
    Result = 1                                              ' Return a value of 1
EndProc

'-------------------------------------------------------------------------------------
' Erase a block of NVM
' Input     : pStartAddr holds the address where to start the erase
' Output    : None
' Notes     : On a PIC16F15213 and PIC16F15214 device, the block size is 32 words
'
Proc Flash_EraseBlock(pStartAddr As Word)
Global Dim bINTCON_Save As Byte Heap Shared                 ' Storage for the INTCON SFR

    bINTCON_Save = INTCON                                   ' Save a copy of INTCON
    INTCONbits_GIE = 0                                      ' Disable interrupts
    NVMADRL = pStartAddr.Byte0                              ' Load lower 8 bits of erase address boundary
    NVMADRH = pStartAddr.Byte1                              ' Load upper 6 bits of erase address boundary
'
' Block erase sequence
'
    NVMCON1bits_NVMREGS = 0                                 ' Deselect Configuration space
    NVMCON1bits_FREE = 1                                    ' Specify an erase operation
    NVMCON1bits_WREN = 1                                    ' Allows erase cycles
'
' Start of required sequence to initiate erase
'
    NVMCON2 = $55
    NVMCON2 = $AA
    NVMCON1bits_WR = 1                                      ' Set WR bit to begin erase
    DelayCS 1
    DelayCS 1
    NVMCON1bits_WREN = 0                                    ' Disable writes
    INTCON = bINTCON_Save                                   ' Restore INTCON
EndProc

'---------------------------------------------------------------------------------------------------
' Setup the device to use the internal oscillator at 32MHz
' Input     : None
' Output    : None
' Notes     : May not need to be called when using the 'RSTOSC_HFINTOSC_32MHZ' config1 fuse
'
Proc Osc_Int32MHz()
    OSCEN = $00
    OSCFRQ = $05                                    ' FRQ is 32MHz
    OSCTUNE = $00
EndProc

'---------------------------------------------------------------------------------------------------
' Setup the configuration fuses for s PIC16F15213 or PIC16F15214 device to use its internal oscillator at 32MHz
' The OSC pins are set for standard I/O pins
'
    Config1 FEXTOSC_OFF,_                           ' External Oscillator not enabled
            RSTOSC_HFINTOSC_32MHZ,_                 ' HFINTOSC  is 32MHz
            CLKOUTEN_OFF,_                          ' CLKOUT function is disabled; I/O function on RA4
            VDDAR_HI                                ' Internal analog systems are calibrated for operation between VDD = 2.3V - 5.5V

    Config2 MCLRE_EXTMCLR,_                         ' MCLR pin is MCLR
            PWRTS_PWRT_OFF,_                        ' Power-up Timer is disabled
            WDTE_OFF,_                              ' WDT disabled. SEN is ignored
            BOREN_SBOREN,_                          ' Brown-out Reset enabled according to SBOREN bit
            BORV_LO,_                               ' Brown-out Reset Voltage (VBOR) set to 1.9V
            PPS1WAY_OFF,_                           ' The PPSLOCKED bit can be set and cleared as needed (unlocking sequence is required)
            STVREN_ON                               ' Stack Overflow or Underflow will cause a reset

    Config4 BBSIZE_BB512,_                          ' Boot Block Size 512 words
            BBEN_OFF,_                              ' Boot Block is disabled
            SAFEN_OFF,_                             ' SAF is disabled
            WRTAPP_OFF,_                            ' Application Block is not write-protected
            WRTB_OFF,_                              ' Boot Block is not write-protected
            WRTC_OFF,_                              ' Configuration Registers are not write-protected
            WRTSAF_OFF,_                            ' Storage Area Flash (SAF) is not write-protected
            LVP_OFF                                 ' High Voltage on MCLR/Vpp must be used for programming

    Config5 CP_OFF                                  ' User Program Flash Memory code protection is disabled

On a project many years ago, using the 18FxxJ devices that were the first to remove EEPROM and use NVM, I eventually gave up and placed an I2C EEPROM on the board because the NVM read and write was such a pain to use. I had to use the J device because that was the only device, at the time, that could run up to 48MHz. The rest had a maximum of 40MHz, and I needed the extra speed for the project.

Then the 18FxxK devices came out a year or so later that could operate at 64MHz and had standard EEPROM in them, so I dropped the dreadful 18FxxJ series forever!

JonW

#2
This seems similar to the 16F15376, where you must calculate the boundary, erase the row, and rewrite. What would it have taken to put an HW write sequence into the device?  Have to agree that its ridiculous, slows processes and eats RAM

I know this works on the 16F15376

' CAN READ AND WRITE TO ANY SINGLE REGISTER IN THE DFM
' CAN READ THE DIA FOR CONSTANTS ETC
' EXAMPLE OF USE

' VAR = FLASHREAD(ADDRESS)          ' READS THE DFM AREA
' VAR = FLASHREADCFG(ADDRESS)       ' READS THE DIA AREA
' FLASHWRITE(ADDRESS, DATA)         ' WRITES TO A SINGLE LOCATION IN DFM

'----------------------------------------------------------------------------------------------
'----------------------------------------------------------------------------------------------
'NVMCONTROL

SYMBOL  NVMREGS     = NVMCON1.6             ' 1 = DIA, 0 = PFM
SYMBOL  NVMLWLO     = NVMCON1.5             ' LOAD WRITE LATCHES ONLY
SYMBOL  NVMFREE     = NVMCON1.4             ' PROGRAM FLASH MEMORY ERASE ENABLE
SYMBOL  NVMWRERR    = NVMCON1.3             ' PROGRAM/ERASE ERROR FLAG
SYMBOL  NVMWREN     = NVMCON1.2             ' PROGRAM/ERASE ENABLE BIT
SYMBOL  NVMWR       = NVMCON1.1             ' 1 = INITIATES A WRITE
SYMBOL  NVMRD       = NVMCON1.0             ' 1 = INITIATES A READ ON THE NEXT CYCLE

'NVM PROCS FOR 16F15376

(********************************************************************************************
* Title     :  FLASHREADMEM                                                                 *
* Input     :  FLASH ADDRESS                                                                *
* Output    :  FLASH CONTENT                                                                *
* Notes     :  READS FLASH MEM                                                              *
********************************************************************************************)
Proc FLASHREAD(ADDR As WORD),WORD
      NVMREGS = 0                             ' POINT TO FLASH
      NVMADRL = ADDR.Byte0                    ' LOAD THE POINTERS
      NVMADRH = ADDR.Byte1
      NVMRD = 1                               ' START THE READ
      NOP
      RESULT.HIGHBYTE = NVMDATH               ' RETURN THE DATA
      RESULT.LOWBYTE = NVMDATL
      RESULT = RESULT & %0011111111111111
EndProc

(********************************************************************************************
* Title     :  READFLASHCFG   MMMMMM                                                        *
* Input     :  FLASH ADDRESS                                                                *
* Output    :  FLASH CONTENT                                                                *
* Notes     :  READS DIA (CONFIG) AREA                                                      *
********************************************************************************************)
Proc FLASHREADCFG(ADDR As WORD),WORD
      NVMREGS = 1                             ' POINT TO CONFIG
      NVMADRL = ADDR.Byte0                    ' LOAD THE POINTERS
      NVMADRH = ADDR.Byte1
      NVMRD = 1                               ' START THE READ
      NOP
      RESULT.HIGHBYTE = NVMDATH               ' RETURN THE DATA
      RESULT.LOWBYTE = NVMDATL
EndProc

(********************************************************************************************
* Title     :  UNLOCKDFM (UNLOCK THE DATA FLASH)                                            *
* Input     :  FLASH ADDRESS                                                                *
* Output    :  FLASH CONTENT                                                                *
* Notes     :  26/27K42                                                                     *
********************************************************************************************)
Proc UNLOCKDFM()
        NVMCON2 = $55                         ' Required Writes
        NVMCON2 = $AA                         ' Required Wrtits
        NVMWR = 1                             ' Set WRITE CONTROL BIT
        NOP
        NOP
EndProc

(********************************************************************************************
* Title     :  FLASHWRITE                                                                   *
* Input     :  FLASH ADDRESS, BYTE TO WRITE                                                 *
* Output    :  NONE                                                                         *
* Notes     :  UPDATE 1 BYTE IN DFM (CAN WRITE TO ANY LOCATION)                             *
*********************************************************************************************)
Proc FLASHWRITE(ADDR As WORD, B2LOAD As WORD)

     Dim b_ROWs          As word              ' NEED A TEMPORY BYTE TO STORE THE ADDR.BYTE0
     DIM b_BYTE          as byte
     DIM b_LDL1          as byte
     Dim b_FLASH[32]     as word

     B_ROWs = (ADDR/32)                       ' GET THE 32 BYTE BOUNDARY ROW
     b_rows = b_rows * 32                     ' GET START ADDRESS
     b_byte = addr - (b_rows)                 ' GET BYTE INDEX TO REPLACE


     FOR B_LDL1 = 0 to 31                     ' READ FLASH INTO RAM
         B_FLASH[B_LDL1] = FLASHREAD(B_ROWs + B_LDL1)
     Next

     B_FLASH[B_BYTE] = B2LOAD                 ' UPDATE THE BYTE TO CHANGE

     ERASEROW(B_ROWs)                         ' ERASE THE 32 BYTE BLOCK (ROW)

     FOR B_LDL1 = 0 to 30                     ' RELOAD LATCHES
        LOADLATCH(B_ROWS,B_FLASH[B_LDL1],1)
        INC B_ROWS
     Next
        LOADLATCH(B_ROWS,B_FLASH[31],0)       ' FINAL LATCH AND WRITE
     WHILE NVMWR = 1:Wend                     ' WAIT FOR WRITE TO OCCUR IF IN A FAST LOOP
     NVMWREN = 0                              ' DISABLE WRITES
EndProc

(********************************************************************************************
* Title     :  LOADLATCH                                                                    *
* Input     :  FLASH ADDRESS, WORD TO LOAD, LATCH OR WRITE                                  *
* Output    :  NONE                                                                         *
* Notes     :  UPDATE 1 BYTE IN DFM (CAN WRITE TO ANY LOCATION)                             *
*********************************************************************************************)
proc LOADLATCH(ADDR AS WORD,DAT AS WORD, LATCH AS BIT)
        dim  b_GIESTORE AS BIT

        b_GIESTORE = INTCON.7
        NVMDATH = DAT.HIGHBYTE
        NVMDATL = DAT.LOWBYTE
        NVMADRH = ADDR.HIGHBYTE
        NVMADRL = ADDR.LOWBYTE

        NVMREGS = 0
        NVMLWLO = LATCH
        NVMFREE = 0
        NVMWREN = 1
        NOP
        UNLOCKDFM()
        INTCON.7 = b_GIESTORE    ' RETURN INT STATUS
EndProc

(********************************************************************************************
* Title     :  ERASEROW                                                                     *
* Input     :  FLASH ADDRESS                                                                *
* Output    :  NONE                                                                         *
* Notes     :  ERASES 32BYTE ROW AT ADDR                                                    *
*********************************************************************************************)
Proc   ERASEROW(ADDR As word)   ' KEEP THIS PROC SEPERATE SO WE CAN ERASE ANY 64-BYTE BLOCK

       NVMADRL = ADDR.Byte0
       NVMADRH = ADDR.Byte1

       NVMREGS = 0              ' ACCESS FLASH MEMORY
       NVMWREN = 1              ' ENABLE WRITE TO MEM
       NVMFREE = 1              ' ERASE BLOCK
       UNLOCKDFM()              ' UNLOCK DFM
       Nop                      ' REQUIRED
       WHILE NVMFREE = 1:WEND
       NVMWREN = 0              ' DISABLE WRITE TO MEM

EndProc

TimB


Thanks JonW and Les

Really appreciated. I need a bit of ram for a ring buffer so have to look at these new(er) devices but am worried about the extra taken for the read/write buffer. But then realised that as I'm only saving 2 bytes I can not bother reading it first and just clear and rewrite.

Anycase saved me loads of reading and testing

Many many thanks

Tim