EEPROM write and read in new PIC18F-Q devices - example

Started by trastikata, Jun 10, 2026, 06:09 PM

Previous topic - Next topic

trastikata

Here's an example how to write and read EEPROM in new PIC18F-Q devices.

Device = 18F57Q83   
Declare Xtal = 64 

'Load some data to be written
Dim bTemp As Byte = 0xBC
 
'Verify command and DFM base in datasheet for corresponding PIC
Symbol EEPROM_WRITE_COMMAND = %00000011
Symbol EEPROM_READ_COMMAND =  %00000000
Symbol DFM_BASE = 0x38

Main:
    If EepromWrite(0,bTemp) = 0 Then    'If successfully written in EEPROM address 0
        bTemp = EepromRead(0)           'Read back EEPROM address 0
    EndIf
   
    End

'wAddress = EEPROM address to be written
'Result: 0 - write success
'        1 - write error
Proc EepromWrite(wAddress As Word, bData As Byte), Byte
    Dim bTempGIE As Byte

    Result = 0
    'Clear previous error flags
    NVMCON1.7 = 0
   
    'Address (DFM base + offset)
    NVMADRU = DFM_BASE
    NVMADRH = wAddress.Byte1
    NVMADRL = wAddress.Byte0
    'Data
    NVMDATL = bData
    'Select command
    NVMCON1 = EEPROM_WRITE_COMMAND
    'Disable GIE
    bTempGIE = INTCON0
    INTCON0.7 = 0
    'Unlock sequence
    NVMLOCK = 0x55
    NVMLOCK = 0xAA
    'Start
    NVMCON0.0 = 1
    'Wait to complete
    While NVMCON0.0 = 1 : Wend
    'Check for an error   
    If NVMCON1.7 = 1 Then      
        NVMCON1.7 = 0 
        Result = 1             
    EndIf
    'Restore GIE
    INTCON0 = bTempGIE  
    'Disable write
    NVMCON1 = 0
EndProc

'wAddress = EEPROM address to be read from
'Result: Data
Proc EepromRead(wAddress As Word), Byte
    Result = 0
    'Address (DFM base + offset)
    NVMADRU = DFM_BASE
    NVMADRH = wAddress.Byte1
    NVMADRL = wAddress.Byte0
    'Select command
    NVMCON1 = EEPROM_READ_COMMAND
    'Start
    NVMCON0.0 = 1
    'Wait to complete
    While NVMCON0.0 = 1 : Wend
    'Copy result
    Result = NVMDATL
EndProc

top204

Whoops.... That has just reminded me of my mistake for not altering the "EE_RW_TYPE" directive in the 18FxxQ35 device ppi files. Sorry!

Because microchip constantly change how peripherals work on devices, and constantly change SFR names and bit positions, the compiler has multiple library subroutines that can be used for a particular device family, and the one to use is held in the ppi file as a directive, as to what subroutine to use.

The new 18FxxQ35 devices use library subroutine version 3 for ERead and EWrite, but I forgot to change it in the ppi file, when I changed all the other types for the other peripherals on that particular family. However, I had the forethought to add Declares for a lot of the ppi directives, so until I update the ppi files, use the Declare:

Declare EEPROM_Type = 3

Or open the device's ppi file, 'with notepad and never a word processor', and change the text on line 20:

EE_RW_TYPE=1            ; The type of EEPROM read and write library to use for the device

to

EE_RW_TYPE=3            ; The type of EEPROM read and write library to use for the device

I'll check on other 18FxxQxx devices, that I may have forgotten to alter in the ppi file.

A full code listing to demonstrate the correction is below:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Write and read the on-board EEPROM on the 18FxxQ35 devices.
'
' Written for the Positron8 compiler by Les Johnson.
'
    Device = 18F26Q35                                               ' Tell the compiler what device to compile for   
    Declare Xtal = 64                                               ' Tell the compiler what frequency the device will be operating at (in MHz)
    Declare Auto_Heap_Arrays = On                                   ' Tell the compiler to create arrays above standard variables, so assembler code is more efficient
    Declare Auto_Heap_Strings = On                                  ' Tell the compiler to create Strings above standard variables, so assembler code is more efficient
    Declare Auto_Variable_Bank_Cross = On                           ' Tell the compiler to create any multi-byte variables in the same RAM bank. For more efficiency
'
' Setup USART1
'
    Declare Hserial_Baud = 9600                                     ' Set the Baud rate for USART1
    Declare HSerout1_Pin = PORTC.6                                  ' Tell the compiler to setup pin PORTC.6 for USART1 Tx
    Declare HSerin1_Pin  = PORTC.5                                  ' Tell the compiler to setup pin PORTC.5 for USART1 Rx
    Declare Hserial1_Clear = On                                     ' Enable Error clearing on received bytes
'
' Correct a PPI value error for ERead and EWrite
'
    Declare EEPROM_Type = 3                                         ' Tell the compiler to use the version 3 library subroutine for on-board EEPROM with the 18FxxQ35 devices
'
' Create data within the on-board EEPROM area
'
MyEdata EData 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
'
' Create any global variables, constants and aliases here
'
    Dim bOffset As Byte                                             ' Holds the offset value for EEPROM reads and writes
    Dim Bytein  As Byte                                             ' Holds the value to read from the on-board EEPROM
    Dim Byteout As Byte                                             ' Holds the value to write to the on-board EEPROM

'-------------------------------------------------------------------------------------
' The main program starts here
' Write and read on-board EEPROM, and transmit the results to a serial terminal
'
Main:
    Setup()                                                         ' Setup the program and any peripherals 
'
' Write to EEPROM
'    
    HRSOut1Ln "\rWriting to EEPROM"
    Byteout = 100                                                   ' Load the initial value for the data to write toon-board EEPROM
    For bOffset = 0 To 6                                            ' Create a loop for the amount of writes
        EWrite MyEdata + bOffset, [Byteout]                         ' Write to on-board EEPROM
        HRSOut1Ln "Offset ", Dec2 bOffset, " will hold ", Dec Byteout ' Transmit the ASCII write values to a serial terminal
        Inc Byteout                                                 ' Increment the value to write
    Next                                                            ' Close the write loop
'
' Read from EEPROM
'   
    HRSOut1Ln "\rReading from EEPROM"
    For bOffset = 0 To 12                                           ' Create a loop for the amount of reads
        Bytein = ERead MyEdata + bOffset                            ' Read from on-board EEPROM
        HRSOut1Ln "Offset ", Dec2 bOffset, " holds ", Dec Bytein    ' Transmit the ASCII read values to a serial terminal
    Next                                                            ' Close the read loop  

'-----------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
'
' Any setups go here
'
EndProc

'------------------------------------------------------------------------------------------------
' Setup the fuses to use the internal oscillator at 64MHz on PIC18FxxQ35 devices.
' OSC pins are general purpose I/O.
'
Config_Start
    FEXTOSC = Off                                                   ' External Oscillator not enabled
    RSTOSC = HFINTOSC_64MHZ                                         ' HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:1
    CPUHALT = On                                                    ' CPU holds after Reset (no user code execution). Requires CLBSU = 0
    CLKOUTEN = Off                                                  ' CLKOUT function is disabled
    PR1WAY = Off                                                    ' PRLOCKED bit can be set and cleared repeatedly
    BBEN = Off                                                      ' Boot block disabled
    CSWEN = On                                                      ' Writing to NOSC and NDIV is allowed
    FCMEN = On                                                      ' Fail-Safe Clock Monitor enabled
    FCMENP = On                                                     ' Fail-Safe Clock Monitor enabled. timer will flag FSCMP bit and OSFIF interrupt On EXTOSC failure
    FCMENS = On                                                     ' Fail-Safe Clock Monitor enabled. timer will flag FSCMP bit and OSFIF interrupt On SOSC failure
    CLBSU = Off                                                     ' The CLB will not be automatically configured during device configuration
    MCLRE = EXTMCLR                                                 ' If LVP = 0, MCLR pin is MCLR. If LVP = 1, RE3 pin function is MCLR
    PWRTS = PWRT_OFF                                                ' Power-Up Timer is disabled
    MVECEN = Off                                                    ' Interrupt contoller does not use vector table to prioritise interrupts
    IVT1WAY = On                                                    ' IVTLOCKED bit can be cleared and set only once
    LPBOREN = Off                                                   ' Low-Power BOR disabled
    BOREN = SBOREN                                                  ' Brown-out Reset enabled according to SBOREN
    BORV = VBOR_1P9                                                 ' Brown-out Reset Voltage (VBOR) set to 1.9V
    PPS1WAY = Off                                                   ' PPSLOCKED bit can be set and cleared repeatedly (subject to the unlock sequence)
    STVREN = On                                                     ' Stack full/underflow will cause Reset
    LVP = On                                                        ' Low voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignored
    XINST = Off                                                     ' Extended Instruction Set and Indexed Addressing Mode disabled
    ZCD1 = Off                                                      ' ZCD1 module is disabled. ZCD1 can be enabled by setting the ZCDSEN bit of ZCDCON
    WDTCPS = WDTCPS_2                                               ' Watchdog Timer Period Divider ratio is 1:128
    WDTE = Off                                                      ' Watchdog Timer Disabled. SEN is ignored
    WDTCWS = WDTCWS_7                                               ' Watchdog Timer window always open (100%). Software control. Keyed access not required
    WDTCCS = SC                                                     ' Watchdog Timer input clock Software Controlled
    BBSIZE = BBSIZE_128                                             ' Boot Block size is 128 words
    CLBSA = Default                                                 ' CLB Configuration Start Address (High Byte) default value
    WRTB = Off                                                      ' Boot Block not Write protected
    WRTC = Off                                                      ' Configuration registers not Write protected
    WRTD = Off                                                      ' Data EEPROM not Write protected
    WRTSAF = Off                                                    ' SAF not Write Protected
    WRTAPP = Off                                                    ' Application Block not write protected
    Cp = Off                                                        ' User Program Flash Memory code protection disabled
    CPD = Off                                                       ' Data EEPROM code protection disabled
Config_End

On the serial terminal, the above program will display:

Writing to EEPROM
Offset 00 will hold 100
Offset 01 will hold 101
Offset 02 will hold 102
Offset 03 will hold 103
Offset 04 will hold 104
Offset 05 will hold 105
Offset 06 will hold 106

Reading from EEPROM
Offset 00 holds 100
Offset 01 holds 101
Offset 02 holds 102
Offset 03 holds 103
Offset 04 holds 104
Offset 05 holds 105
Offset 06 holds 106
Offset 07 holds 8
Offset 08 holds 9
Offset 09 holds 10
Offset 10 holds 11
Offset 11 holds 12
Offset 12 holds 13


TimB


In case you want to bit bang it. Another version

'===============================================================================
'                          EEPROM HELPER FUNCTIONS
'===============================================================================
' These functions provide byte and word access to EEPROM
' PIC18F27Q10 uses NVM (Non-Volatile Memory) controller
' EEPROM base address: 0x310000
'===============================================================================

'-----------------------------------------------------------------------------
' EReadByte(pAddrs as word)
' Input     : pAddrs - EEPROM address (0-255 for this device)
' Output    : Byte value at address
' Notes     : Wrapper for EEPROM_Read
'-----------------------------------------------------------------------------
Proc EReadByte(pAddrs As Word),Byte

        Result = EEPROM_Read(pAddrs)

EndProc


'-----------------------------------------------------------------------------
' EReadWord(pAddrs as word)
' Input     : pAddrs - EEPROM address for LSB
' Output    : 16-bit word value (LSB at pAddrs, MSB at pAddrs+1)
' Notes     : Reads two consecutive bytes and assembles into word
'-----------------------------------------------------------------------------

Proc EReadWord(pAddrs As Word),Word

        Result.Byte0 = EEPROM_Read(pAddrs)  ' Read LSB
        Inc pAddrs
        Result.Byte1 = EEPROM_Read(pAddrs)  ' Read MSB


EndProc


'-----------------------------------------------------------------------------
' EWriteByte(paddress as word, pvalue as byte)
' Input     : paddress - EEPROM address
'           : pvalue - Byte value to write
' Output    : None
' Notes     : Wrapper for EEPROM_Write
'-----------------------------------------------------------------------------

Proc EWriteByte(paddress As Word, pvalue As Byte)


        EEPROM_Write(paddress, pvalue)


EndProc

'-----------------------------------------------------------------------------
' EWriteWord(paddress as word, pvalue as word)
' Input     : paddress - EEPROM address for LSB
'           : pvalue - 16-bit word to write
' Output    : None
' Notes     : Writes LSB at paddress, MSB at paddress+1
'-----------------------------------------------------------------------------

Proc EWriteWord(paddress As Word, pvalue As Word)


        EEPROM_Write(paddress, pvalue.Byte0)    ' Write LSB
        Inc paddress
        EEPROM_Write(paddress, pvalue.Byte1)    ' Write MSB


EndProc


'-----------------------------------------------------------------------------
' EEPROM_Read(pAddr as word)
' Input     : pAddr - EEPROM offset address (0-255)
' Output    : Byte value at address
' Operation : Uses NVM controller to read from EEPROM
'           : EEPROM base address = 0x310000
'           : Full address = 0x310000 + pAddr
' Notes     : PIC18F27Q10 specific implementation
'-----------------------------------------------------------------------------
Proc EEPROM_Read(pAddr As Word), Byte

    NVMCON0bits_NVMEN = 1               ' Enable NVM module

    '--- Set 24-bit Address ---
    ' EEPROM starts at 0x310000
    NVMADRL = pAddr.Byte0               ' Low byte of offset
    NVMADRH = pAddr.Byte1               ' High byte of offset
    NVMADRU = 0x31                      ' Upper byte (EEPROM region)

    '--- Initiate Read ---
    NVMCON1bits_RD = 1                  ' Start read cycle

    Nop                                 ' Wait for read to complete
    Nop
    Nop

    '--- Read Data ---
    Result = NVMDATL                    ' Read result from data register

EndProc



'-----------------------------------------------------------------------------
' EEPROM_Write(paddress as word, pvalue as byte)
' Input     : paddress - EEPROM offset address (0-255)
'           : pvalue - Byte value to write
' Output    : None
' Operation : Uses NVM controller to write to EEPROM
'           : Executes required unlock sequence (0x55, 0xAA)
'           : Waits for write cycle to complete
' Notes     : Write takes ~5ms to complete
'           : PIC18F27Q10 specific implementation
'---------------------------------------------------------------------------

Proc EEPROM_Write(paddress As Word, pvalue As Byte)

        NVMCON0.7 = 1                   ' Enable NVM/EEPROM

        '--- 1. Load NVM Address ---
        ' EEPROM base = 0x310000
        NVMADRU = 0x31                  ' Upper byte (EEPROM region)
        NVMADRH = paddress.Byte1        ' High byte of offset
        NVMADRL = paddress.Byte0        ' Low byte of offset

        '--- 2. Load Data to Write ---
        NVMDATL = pvalue

        '--- 3. Setup NVMCON for EEPROM Write ---
        NVMCON0bits_NVMEN = 1           ' Enable NVM module


        '--- 5. Required Unlock Sequence ---
        ' CRITICAL: Must execute without interruption
        NVMCON2 = 0x55                  ' First unlock key
        NVMCON2 = 0xAA                  ' Second unlock key

        '--- 6. Start Write Cycle ---
        NVMCON1bits_WR = 1              ' Trigger write

        '--- 7. Wait for Write Completion ---
        ' Hardware clears WR bit when done (~5ms)
        While NVMCON1bits_WR = 1
        Wend


EndProc