News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

Procedure to write to the eeprom

Started by Peter Truman, Jun 14, 2023, 12:31 AM

Previous topic - Next topic

Peter Truman

Hi

I have a 'menu' type program that does a lot of eeprom writes. At the moment each write is preceded with a GIE=0 statement to turn off the interrupts, followed by a GIE=1 to turn them on again.

I thought this might be a good candidate for a procedure

Proc e_prom (W_Location As word, D_data As Dword)
GIE=0                                                                                       'turn of the interrupts
EWrite W_Location,[D_data]
GIE=1                                                                                       ;re enable the clock
EndProc

However, I am using a variety of variables, some are Bytes, some are words and some are Dwords, so I declared the 'data' argument as a Dword, because that is the biggest var I write.

My understanding is.... if I send the procedure a byte sized var, the procedure will write 3 bytes of 0b00 plus 1 byte of my value. Since I don't have enough memory to make every saved value a Dword I can't afford to be that inefficient.

If so, is there an easy (or any) way to fix this?

I thought it might be possible to send the procedure another byte sized var that tells it what size to write, something like this

'Proc to write to the eeprom
Proc E_prom (W_Location As Word, X_data As Dword, B_size As Byte)
Dim B_Data As Byte                                                                          'Local Vars
Dim W_Data As Word
Dim D_Data As Dword
Dim W_Working As Word

GIE=0                                                                                       'turn of the interrupts
Select type
    Case 0  'byte
        W_Working=X_data.Low.Word                                                            'extract the low word from the Dword
        B_Data=W_Working.LowByte                                                            'extract the low byte from the low word
        EWrite W_Location,[B_Data]                                                            'write a single byte
    Case 1  'word
        W_Working=X_data.Low.Word                                                            'extract the low word from the Dword
        W_Data=W_Working
        EWrite W_Location, [W_Data]                                                           'write a single word (2 bytes)       
    Case 2  'dword
        EWrite W_Location, [X_Data]                                                           'write the Dword (4 bytes)
EndSelect
GIE=1                                                                                       ;reenable the clock
EndProc

Is this a sensible way to approach this or am I missing something simple. (all to avoid adding GIE=0 and GIE=1 every time I want to write to the eeprom.

Thanks in advance

 

Peter Truman

Ok - I tied this and it seems to work ok

Minor changes - W_working=X_Data.word0

I don't think there is any direct way to extract the lowest byte from a Dword without the intermediate step?

Any simpler ways of doing this?

Thanks

trastikata

Quote from: Peter Truman on Jun 14, 2023, 01:07 AMI don't think there is any direct way to extract the lowest byte from a Dword without the intermediate step?
Any simpler ways of doing this?

You can use individual bytes in a word/dword/Float directly - MyVariable.Byte0, MyVariable.Byte1, MyVariable.Byte2, MyVariable.Byte3 

top204

Below is my take on procedures to write and read on-board EEPROM using your single write procedure method with a parameter as the size of data to write:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Demonstrate procedures that will write and read a device's on-board EEPROM
'
' Written by Les Johnson for the Positron8 compiler.
'
    Device = 18F26K22                                   ' Tell the compiler what device to compile for
    Declare Xtal = 16                                   ' Tell the compiler what frequency the device will be operating at (in MHz)
'
' Setup USART1
'   
    Declare Hserial1_Baud = 9600
    Declare HSerout_Pin = PORTB.6
  
$if _eeprom = 0                                         ' Does the device contain any EEPROM?
    $error "The device does not contain any on-board EEPROM" ' No. So produce an error message
$endif                        
'
' Create constants for the size of value to write to EEPROM, used as parameter names
'
$define cEByte_Size  8                                  ' Indicates that the integer data to write to on-board EEPROM is 8-bits in size
$define cEWord_Size  16                                 ' Indicates that the integer data to write to on-board EEPROM is 16-bits in size
$define cEDword_Size 32                                 ' Indicates that the integer data to write to on-board EEPROM is 32-bits in size
'
' Create some global variables for some procedures to use as parameters and returns, in order to save some RAM
' when several of them are used in a program's listing.
'
$if _eeprom > 255                                       ' Does the device contain more that 255 bytes of on-board EEPROM?
    Dim EEPROM_XOffset       As Word                    ' Yes. So make the offset parameter variable a 16-bit type
$else                                                   ' Otherwise... The device contains 255 bytes or less of on-board EEPROM...
    Dim EEPROM_XOffset       As Byte                    ' So... Make the offset parameter variable an 8-bit type
$endif
    Dim EEPROM_fResult       As Float                   ' Used to hold a floating point return
    Dim EEPROM_dResult       As EEPROM_fResult.Dword    ' Aliased to hold a 32-bit integer return
    Dim EEPROM_wResult       As EEPROM_fResult.Word0    ' Aliased to hold a 16-bit integer return
    Dim EEPROM_bResult       As EEPROM_fResult.Byte0    ' Aliased to hold an 8-bit integer return
    Dim EEPROM_tIntsEnabled  As Bit                     ' Holds 1 if global interrupts were enabled
'
' Clear data elements in on-board EEPROM at programming time, for the demo
'
EE_Data8  EData 0
EE_Data16 EData 0, 0
EE_Data32 EData 0, 0, 0, 0
'
' Create some variables for the demo program
'
    Dim MyDword As Dword
    Dim MyWord  As Word
    Dim MyByte  As Byte
   
'-------------------------------------------------------------------------------------------
' The main program starts here
' Write and read on-board EEPROM using custom made procedures.
'
Main:
'
' Write to on-board EEPROM
'
    EEPROM_Write(EE_Data8, 123, cEByte_Size)        ' Write an 8-bit integer value to on-board EEPROM
    EEPROM_Write(EE_Data16, 1234, cEWord_Size)      ' Write a 16-bit integer value to on-board EEPROM
    EEPROM_Write(EE_Data32, 1234567, cEDword_Size)  ' Write a 32-bit integer value to on-board EEPROM
'
' Read from on-board EEPROM
'       
    MyByte = EEPROM_Read8(EE_Data8)                 ' Read an 8-bit integer value from on-board EEPROM
    MyWord = EEPROM_Read16(EE_Data16)               ' Read a 16-bit integer value from on-board EEPROM
    MyDword = EEPROM_Read32(EE_Data32)              ' Read a 32-bit integer value from on-board EEPROM
'
' Display the values read from on-board EEPROM on a serial terminal
'   
    HRSOutLn "Offset: ", Dec EE_Data8, " Holds: ", Dec MyByte
    HRSOutLn "Offset: ", Dec EE_Data16, " Holds: ", Dec MyWord
    HRSOutLn "Offset: ", Dec EE_Data32, " Holds: ", Dec MyDword

'-------------------------------------------------------------------------------------------
' Write an integer value to on-board EEPROM
' Input     : pOffset holds the offset address within on-board EEPROM
'           : pData holds the integer data to write to on-board EEPROM
'           : pSize holds the size of the data to write to on-board EEPROM. i.e. 8, 16, 32
' Output    : None
' Notes     : Only writes to EEPROM if the EEPROM element/s contain a different value to what is being written to it
'           : This saves a little on the wear and tear of the on-board EEPROM
'           : Disables then re-enables global interrupts, if they were enabled
'
Proc EEPROM_Write(pOffset As EEPROM_XOffset, pData As Dword, pSize As Byte)   
    Dim dTemp As Dword                              ' Create a 32-bit variable to read the on-board EEPROM's value into
    Dim wTemp As dTemp.Word0                        ' Alias a 16-bit variable to read the on-board EEPROM's value into
    Dim bTemp As dTemp.Byte0                        ' Alias an 8-bit variable to read the on-board EEPROM's value into
   
    EEPROM_tIntsEnabled = 0                         ' Default to global interrupts disabled
    If INTCONbits_GIE = 1 Then                      ' Are global interrupts enabled?
        INTCONbits_GIE = 0                          ' Yes. So disable global interrupts
        EEPROM_tIntsEnabled = 1                     ' Indicate that they were enabled
    EndIf

    Select pSize                                    ' What size is the data to be written to on-board EEPROM?
        Case cEByte_Size                            ' Is it an 8-bit integer value to write?
            bTemp = ERead pOffset                   ' Yes. So read what the element contains
            If bTemp <> pData.Byte0 Then            ' Does the element already contain what is being written?
                EWrite pOffset,[pData.Byte0]        ' No, So write an 8-bit value to on-board EEPROM
            EndIf
       
        Case cEWord_Size                            ' Is it a 16-bit integer value to write?
            wTemp = ERead pOffset                   ' Yes. So read what the elements contain
            If wTemp <> pData.Word0 Then            ' Do the elements already contain what is being written?
                EWrite pOffset, [pData.Word0]       ' No, So write a 16-bit value to on-board EEPROM     
            EndIf
       
        Case cEDword_Size                           ' Is it a 32-bit integer value to write?
            dTemp = ERead pOffset                   ' Yes. So read what the elements contain
            If dTemp <> pData Then                  ' Do the elements already contain what is being written?
                EWrite pOffset, [pData]             ' No, So write a 32-bit value to on-board EEPROM
            EndIf
    EndSelect
    If EEPROM_tIntsEnabled = 1 Then                 ' Were global interrupts enabled?
        INTCONbits_GIE = 1                          ' Yes. So re-enable global interrupts
    EndIf
EndProc

'-------------------------------------------------------------------------------------------
' Read an 8-bit integer value from on-board EEPROM
' Input     : pOffset holds the offset address within on-board EEPROM
' Output    : Returns the 8-bit integer value read from on-board EEPROM
' Notes     : Disables then re-enables global interrupts, if they were enabled
'
Proc EEPROM_Read8(pOffset As EEPROM_XOffset), EEPROM_bResult
    EEPROM_tIntsEnabled = 0                         ' Default to global interrupts disabled
    If INTCONbits_GIE = 1 Then                      ' Are global interrupts enabled?
        INTCONbits_GIE = 0                          ' Yes. So disable global interrupts
        EEPROM_tIntsEnabled = 1                     ' Indicate that they were enabled
    EndIf

    Result = ERead pOffset                          ' Read an 8-bit integer value from EEPROM

    If EEPROM_tIntsEnabled = 1 Then                 ' Were global interrupts enabled?
        INTCONbits_GIE = 1                          ' Yes. So re-enable global interrupts
    EndIf
EndProc

'-------------------------------------------------------------------------------------------
' Read a 16-bit integer value from on-board EEPROM
' Input     : pOffset holds the offset address within on-board EEPROM
' Output    : Returns the 16-bit integer value read from on-board EEPROM
' Notes     : Disables then re-enables global interrupts, if they were enabled
'
Proc EEPROM_Read16(pOffset As EEPROM_XOffset), EEPROM_wResult
    EEPROM_tIntsEnabled = 0                         ' Default to global interrupts disabled
    If INTCONbits_GIE = 1 Then                      ' Are global interrupts enabled?
        INTCONbits_GIE = 0                          ' Yes. So disable global interrupts
        EEPROM_tIntsEnabled = 1                     ' Indicate that they were enabled
    EndIf

    Result = ERead pOffset                          ' Read a 16-bit integer value from EEPROM

    If EEPROM_tIntsEnabled = 1 Then                 ' Were global interrupts enabled?
        INTCONbits_GIE = 1                          ' Yes. So re-enable global interrupts
    EndIf
EndProc
       
'-------------------------------------------------------------------------------------------
' Read a 32-bit integer value from on-board EEPROM
' Input     : pOffset holds the offset address within on-board EEPROM
' Output    : Returns the 32-bit integer value read from on-board EEPROM
' Notes     : Disables then re-enables global interrupts, if they were enabled
'
Proc EEPROM_Read32(pOffset As EEPROM_XOffset), EEPROM_dResult
    EEPROM_tIntsEnabled = 0                         ' Default to global interrupts disabled
    If INTCONbits_GIE = 1 Then                      ' Are global interrupts enabled?
        INTCONbits_GIE = 0                          ' Yes. So disable global interrupts
        EEPROM_tIntsEnabled = 1                     ' Indicate that they were enabled
    EndIf

    Result = ERead pOffset                          ' Read a 32-bit integer value from EEPROM

    If EEPROM_tIntsEnabled = 1 Then                 ' Were global interrupts enabled?
        INTCONbits_GIE = 1                          ' Yes. So re-enable global interrupts
    EndIf
EndProc 

'-------------------------------------------------------------------------------------------
' Read a floating value from on-board EEPROM
' Input     : pOffset holds the offset address within on-board EEPROM
' Output    : Returns the floating point value read from on-board EEPROM
' Notes     : Disables then re-enables global interrupts, if they were enabled
'
Proc EEPROM_ReadFloat(pOffset As EEPROM_XOffset), EEPROM_fResult
    EEPROM_tIntsEnabled = 0                         ' Default to global interrupts disabled
    If INTCONbits_GIE = 1 Then                      ' Are global interrupts enabled?
        INTCONbits_GIE = 0                          ' Yes. So disable global interrupts
        EEPROM_tIntsEnabled = 1                     ' Indicate that they were enabled
    EndIf

    Result = ERead pOffset                          ' Read an 8-bit value from EEPROM

    If EEPROM_tIntsEnabled = 1 Then                 ' Were global interrupts enabled?
        INTCONbits_GIE = 1                          ' Yes. So re-enable global interrupts
    EndIf
EndProc

As can be seen in the code listing above, the EEPROM write only occurs if the EPPROM's element/s do not already contain what is being written. This simplistic method can save an EEPROM's element/s from being weakened if too many writes are made that contain the same value. It could be extended so it looks at each element within a multiple element value, and only writes to an individual element if its particular value is different.

On a serial terminal, the above demo program will display:

Offset: 0 Holds: 123
Offset: 1 Holds: 1234
Offset: 3 Holds: 1234567


The procedures and their associated preprocessor directives could easily be made into a library include file for use with many device types. The only thing that may need changing is the INTCON SFR, because microcohip, in their wisdom, changed its name to INTCON0 on some devices with more INTCONx SFRs.


Peter Truman

Thanks Les

I'm interested in the use of the preprocessor directives (this is quite new to me). There is much I don't understand. For example $if _eeprom = 0                                         ' Does the device contain any EEPROM?
    $error "The device does not contain any on-board EEPROM" ' No. So produce an error message
$endif   

I don't see how this command is able to interrogate the PIC to determine how much eeprom is available (as a simple example) - is this information available to the code via a datasheet linked to the IDE? Or is there something that does actually explore the chip itself?

The code itself is relatively clear but I do get hung up on bits I don't understand (such as the pre processor directives) - I am looking at the manual but it is a bit above me at the moment.

Happy to report that my implementation does appear to work - since I have a fixed relationship between the size of the var and the location in which it it stored, These value are only changed when the user changes them in a (tightly constrained) menu - so no worries with wearing out memory locations.

Thanks
Peter

Peter Truman

Quote from: trastikata on Jun 14, 2023, 03:43 AM
Quote from: Peter Truman on Jun 14, 2023, 01:07 AMI don't think there is any direct way to extract the lowest byte from a Dword without the intermediate step?
Any simpler ways of doing this?

You can use individual bytes in a word/dword/Float directly - MyVariable.Byte0, MyVariable.Byte1, MyVariable.Byte2, MyVariable.Byte3 

Thanks for that - I'll play with the code a bit - that would be a tad more efficient

Stephen Moss

Quote from: Peter Truman on Jun 15, 2023, 01:08 AMI'm interested in the use of the preprocessor directives (this is quite new to me). There is much I don't understand. For example
Code Select Expand
$if _eeprom = 0                                         ' Does the device contain any EEPROM?
    $error "The device does not contain any on-board EEPROM" ' No. So produce an error message
$endif   

I don't see how this command is able to interrogate the PIC to determine how much eeprom is available (as a simple example) - is this information available to the code via a datasheet linked to the IDE? Or is there something that does actually explore the chip itself?
I am sure the Pre-processor is a useful and powerful feature, but like you I don't really understand it enough to make use it.
But in answer to your question, there are two files that define the device (.PPI & .DEF) located in the Includes folder of the installation, I would think it will be getting the information from one of them.

top204

#7
See pages 474 and 475 in the Positron8 compiler's manual for more details about the preprocessor. They are in the section named: "Using the Preprocessor".

The preprocessor is a seperate program that the compiler's loader calls before compilation takes place, that scans the code listing and removes or keeps sections of code depending on comparisons, before it calls the compiler with the finalised code listing. All the preprocessor's directives are in a device's ".def" file, which can be found at: "C:\Program Files (x86)\ProtonIDE\PDS\Includes\Defs\", and are generated from a device's ".ppi" file when I create it.

The preprocessor also has the ability to create, what they call, meta-macros in preprocessor jargon, that are also setup with the $define directive.


Peter Truman

I see - I'm looking at the 18F25K22 .def file (the PIC I'm currently using)

I see lots of data regarding amount of EEprom etc.

So I just added HRSOut  "eeprom",Dec5 _eeprom,13 to my current project (just because it's open in the IDE) and printed the result> eeprom 256

So - that's a whole new avenue opened up that I will need to learn now that I'm retired (sort of) :o

Many thanks
Peter