News:

;) This forum is the property of Proton software developers

Main Menu

CErase / CWrite on 16F15356

Started by Trikkitt, May 08, 2022, 11:01 PM

Previous topic - Next topic

Trikkitt


I'm trying to store some non-volatile data on this chip and failing.  I seem to recall having no luck with these commands on the 16F15325 which I used for another project, but the code that I have there is in assembler and quite honestly trying to recall assembler on this platform is going to be a ton of work that I was hoping to avoid (project deadline end of month and still tons to do).

All I need to do is store 1 bit in non-volatile memory, but can't get anything to do that.  Even getting the erase to work would be a start as right now that doesn't appear to be working.  I'm also getting myself confused with the fuses does WRTSAF_OFF mean the write protection is off or that the ability to write is off?  Currently my fuses look like this:

Config1 FEXTOSC_OFF, RSTOSC_HFINT32, CLKOUTEN_OFF, CSWEN_ON, FCMEN_OFF
Config2 MCLRE_OFF, PWRTE_OFF, LPBOREN_OFF, BOREN_ON, BORV_LO, ZCD_OFF, PPS1WAY_OFF, STVREN_ON
Config3 WDTCPS_WDTCPS_31, WDTE_OFF, WDTCWS_WDTCWS_7, WDTCCS_SC
Config4 BBSIZE_BB512, BBEN_OFF, SAFEN_OFF, WRTAPP_OFF, WRTB_OFF, WRTC_OFF, WRTSAF_OFF, LVP_OFF
Config5 CP_OFF

Does anyone have any examples that work out the box on this chip?

This is the current code that I've tried to run which doesn't appear to erase that address.  When I read it back it still has the data it was programmed with. The CRead command is working just fine and if I programme that address it responds appropriately, I just can't modify the content myself.

    CErase $3F80
    DelayMS 8
    TR=1
    CWrite $3F80,[TR]
    For TR=1 To 63
      CWrite $3F80+TR,[TR]
    Next
    ' Turn on global interrupts
    DelayMS 8

Compiler version 3.7.3.6.  I'm an occasional user (pick it up for a project maybe once every year or two, so this is probably out of date).

Many thanks


Trikkitt

#1
I guess I'm screwed then! :(  From everything I can find here it isn't fixed in the new versions of the compiler.  So not sure where to turn as I don't know what is going wrong and why it won't write.

John Drew

I wouldn't have thought that 3.7.0.6 would support that chip.
Might be time to lash out for the latest version
John

Trikkitt

Quote from: John Drew on May 14, 2022, 01:13 PMI wouldn't have thought that 3.7.0.6 would support that chip.
Might be time to lash out for the latest version
John

Oh it supports it just fine :) The programme I've written works perfectly on the chip with this one exception.  The problem is that CWrite seems totally broken even on the newer version from everything I read.  If it was a fixed thing and I knew it would work then it would certainly be a reason to upgrade.  I just don't bother upgrading something unless I've a reason to do so.

JonW

Don't these devices have the NVM block and need to unlock before use?

A while ago I was using the 18F24Q10 and I think these have the same NVM block and they need unlocking and writing too/Erasing carefully and I don't think the Cread and Cwrite commands work on this memory structure.

You will need to create procedures or subroutines to access the memory space in the exact procedure MChip state in the datasheet.

Here are some Subs that I know works on the 18F24Q10, you will need to add or modify the required variables in your setup

;********************************** DFM SUBROUTINES ******************************         

UNLOCK_DFM:                            ; UNLOCK DATA FLASH MEMORY (DFM)
        NVMCON2 = $55                  ; Required Writes
        NVMCON2 = $AA                  ; Required Wrtits
        NVMCON1.4 = 1                  ; Set WRITE CONTROL BIT
        Return

'*************************************************************************************
'
' Name:              "READ_FLASH"
' Passes:    DFM_OFFSET (REGISTER DFM_OFFSET TO READ)
' Returns:  DATA IN DFM_DAT
' Description          READS A SINGLE BYTE FROM DFM
'*************************************************************************************
READ_DFM:
        NVMCON0.7 = 1                        ; ENABLE  NVM
        DFM_ADD = E2START + DFM_OFFSET        ; GET DFM_OFFSET OF DFM STORE
        NVMADRL = DFM_ADD.Byte0              ; LOAD ADDRESSES AND DATA
        NVMADRH = DFM_ADD.Byte1
        NVMADRU = DFM_ADD.Byte2
        NVMCON1.0 = 1                        ; INITIATE NVM READ
        Nop
        DFM_DAT = NVMDAT                      ; TRANSFER TO RAM     
        NVMCON0.7 = 0                        ; DISABLE  NVM
        Return       
'*************************************************************************************
'
' Name:                  "ERASE_FLASH"
' Passes:    DFM_OFFSET (REGISTER DFM_OFFSET TO READ)
' Returns:  DATA IN DFM_DAT
' Description          READS A SINGLE BYTE FROM DFM
'*************************************************************************************                     
ERASE_DFM:
      ;  DFM_ADD = E2START + DFM_OFFSET        ; GET DFM_OFFSET OF DFM STORE                       
        NVMADRL = DFM_ADD.Byte0                ; LOAD ADDRESSES AND DATA
        NVMADRH = DFM_ADD.Byte1
        NVMADRU = DFM_ADD.Byte2
        NVMDATL = $FF                          ; DFM CAN ONLY BE WRITTEN 1 BYTE AT A TIME
       
        Call UNLOCK_DFM                        ; UNLOCK SEQUENCE
        While NVMCON1.4 = 1:Wend              ; WAIT FOR WRITE TO COMPLETE       
        Return
 
 WRITE_DFM:
        DFM_ADD = E2START + DFM_OFFSET        ; GET DFM_OFFSET OF DFM STORE
        Call ERASE_DFM
        NVMADRL = DFM_ADD.Byte0                ; LOAD ADDRESSES AND DATA
        NVMADRH = DFM_ADD.Byte1         
        NVMADRU = DFM_ADD.Byte2
        NVMDATL = DFM_DAT                    ; DFM CAN ONLY BE WRITTEN 1 BYTE AT A TIME
        NVMCON0.7 = 1                        ; ENABLE  NVM
        Call UNLOCK_DFM                      ; UNLOCK SEQUENCE
        While NVMCON1.4 = 1:Wend              ; WAIT FOR WRITE TO COMPLETE
        NVMCON0.7 = 0                        ; DISABLE NVM 
 ;CHECK BYTE IS WRITTEN BY READING
        DFM_WRITE_ERROR = 0                  ; CLEAR DFM ERROR FLAG
        DFM_CHECK = DFM_DAT                  ; STORE CURRENT WRITTEN DFM DATA
        Call READ_DFM
        If DFM_DAT <> DFM_CHECK Then          ; IF READ HAS FAILED THEN SET DFM WRITE ERROR BIT
          DFM_WRITE_ERROR = 1
        $ifdef SERIAL_TEXT           
          HRSOut "DFM_WR_ERR",13,10
        $endif 
        EndIf   
        If DFM_WRITE_ERROR = 0 Then
        $ifdef SERIAL_TEXT       
          HRSOut "WR_OK",13,10
        $endif 
        EndIf       
        Return
'*************************************************************************************

JonW

Here are the Variable declarations

;DATA MEMORY REGISTERS

Dim DFM_DAT As Byte                     ; 16 BIT DATA REGISTER FOR DATA MEMORY
Dim DFM_ADD As Dword                    ; 32 BIT REGISTER FOR ADDRESS
Dim DFM_OFFSET As Byte                  ; DFM_OFFSET ADDRESS FOR E2   
Dim DFM_CHECK As Byte                   ; DFM WRITE CHECKING REGISTER
Dim DFM_LOOP As Byte                    ; USED IN SERIAL WRITE AND READ



Offsets and start of the block where I used NVM



Dim E2START As $310000                 ; START OF THE E2 DFM MEMORY (ADD DFM_OFFSET TO GET THE CORRECT ADDRESS)
Dim RX_OFFSET_MAX As 50                ; RX OFFSET LIMIT

; DFM OFFSETS (E2 LOCATIONS)
Dim TX_DUTY_OFFSET As 0                 ; TXDUTY.HIGH,LOW BYTE 0,1   (ATTENUATOR INHOME CALIBRATION LEVEL)
Dim RX_DUTY_OFFSET As 2                 ; RXDUTY.HIGH,LOW BYTE 2,3 
Dim TX_CAL_OFFSET As 4                  ; TX CALIBRATION LEVEL  (THIS IS THE GOAL VALUE SET AT FACTORY)
Dim RX_CAL_OFFSET As 6                  ; RX CALIBRATION LEVEL  (THIS IS THE GOAL VALUE SET AT FACTORY)
Dim TEMP_OFFSET As 8                    ; DFM DFM_OFFSET FOR TEMPERATURE CALIRATION DFM_OFFSET
Dim TX_PWR_CAL_UPPER As 9               ; 9 10
Dim TX_PWR_CAL_LOGSLOPE As 11           ; 11 12
Dim RX_PWR_CAL_UPPER As 13              ; 13 14
Dim RX_PWR_CAL_LOGSLOPE As 15           ; 15 16
Dim RX_DUTY_ERROR_DFM   As 17           ; 17    DFM OFFSET FOR THE RECEIVE DUTY REGISTER
Dim LO_POWER_OFFSET     As 18           ; 18,19 LO ADC READING DURING FACTORY CALIBRATION HIGHBYTE, LOWBYTE
Dim CAL_STATUS_OFFSET   As 20           ; STORE THE PREVIOUS CALIBRATION STATUS
Dim LOCK_OFFSET         As 21           ; IF $55 THEN LOCK SERIAL, IF $00 THEN UNLOCK

Demo on how I called the Routines

'CAL TARGET                       
        DFM_OFFSET = TX_CAL_OFFSET
        Call READ_DFM         
        TX_CALVAL.HighByte = DFM_DAT                       ; GET BYTE FROM MEMORY
       
        Inc DFM_OFFSET                                       ; GET NEXT ADDRESS
        Call READ_DFM
        TX_CALVAL.LowByte = DFM_DAT                        ; GET BYTE FROM MEMORY
                   
        DFM_OFFSET = RX_CAL_OFFSET                                   
        Call READ_DFM
        RX_CALVAL.HighByte = DFM_DAT                       ; GET BYTE FROM MEMORY
       
        Inc DFM_OFFSET                                       ; GET NEXT ADDRESS
        Call READ_DFM
        RX_CALVAL.LowByte = DFM_DAT                        ; GET BYTE FROM MEMORY
       

Writes

'*************************************************************************************
'
' Name:                                 "UPDTAE _HOUR"
' Passes:               NOTHING
' Returns:              NOTHING
' Description           READS, INCREMENTS AND WRITES THE HOUR TIMER
'*************************************************************************************                     
UPDATE_HOUR:
                    DFM_OFFSET = HOUR_OFFSET
                    Call READ_DFM         
                    HOUR.HighByte = DFM_DAT                       ; GET BYTE FROM MEMORY   
                    Inc DFM_OFFSET
                    Call READ_DFM
                    HOUR.LowByte = DFM_DAT           
                    Inc HOUR                                      ; INCREMENT THE HOUR TIMER
WRITE_HOUR:                   
                    DFM_OFFSET = HOUR_OFFSET
                    DFM_DAT = HOUR.HighByte
                    Call WRITE_DFM
                    Inc DFM_OFFSET
                    DFM_DAT = HOUR.LowByte
                    Call WRITE_DFM   
                    Return

Trikkitt

Thank you.  I'll have a read and play around with this!

JonW

You should seriously consider upgrading to the latest version so you can then use procedures and get the latest bug fixes, you may also find you are helped faster by supporting the development ;)

Trikkitt

Quote from: JONW on May 18, 2022, 07:12 AMYou should seriously consider upgrading to the latest version so you can then use procedures and get the latest bug fixes, you may also find you are helped faster by supporting the development ;)

The thing is my usage comes and goes.  I'll finish this project (hobby project) and probably not touch the compiler for another year or two, only to find I need to pay to upgrade it again when I start my next hobby project because the device I'm using isn't supported.  So I've taken the approach of upgrading when I find a reason to.  Had the CWrite been something that was fixed in the newer version then that would be a reason I'd upgrade.

top204

#9
Looking at the library code for the Positron8 compiler and examining my notes, the CWrite and Cerase commands work with the newer enhanced 14-bit core devices, but the flash memory writing on devices is not as straightforward as it used to be in the early days of the flash PIC microcontrollers, because they must be in blocks and reside in block zone address'. If they are not working as expected, I will get into them and iron out what a specific device family requires, because Microchip change the requirements from device family to device family now for some "inexplicable" reason. :-( But now that the compiler has procedures, there is nothing stopping a user from reading the datasheet and creating their own flash memory libraries, and learning how a device operates.

Below is a set of procedures I have just created that seem to follow the rules for the new enhanced 14-bit core devices, according to the datasheets, and specifically the PIC16F15356 datasheet. The procedures read, write and erase flash memory. Remember, the flash memory address being written or erased cannot be anywhere in the device, it must reside in "block size" address boundaries. Also remember, it is words that are written to flash memory (max 14-bit value) and the block size for both write and erase on the PIC16F15356 is 32, so 32 words can be written in a single sequence. I gave up using the internal flash memory for writing many years ago because it is cumbersome and not safe over time, and a change of device requires a boat load of changes in the erase and write block sizes etc... I only use it for storing readable data because that works with single words from anywhere in flash memory and does not have a detrimental effect on the memory. Some devices have an erase block size many times greater than the write block size, so they are a waste of time for writable storage because a huge amount has to be read and stored and erased, then a single word changed and the whole block written back. Sometimes taking more RAM requirement that the device actually contains. :-)

Without support for the compilers they cannot be maintained, so your statement is a catch 22 for me, because I simply cannot afford to give "upgrades" away for free anymore, but I will always "update" the compilers for any anomalies or, usually, querks in devices for free. Unlike most other compilers. Crickey, the price of the compilers and the future "upgrades" are not exactly staggeringly high for what they are and what they do and what they contain. :-)

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A set of procedures to read, write and erase flash memory on the newer enhanced 14-bit core devices
' Written by Les Johnson for the Positron8 compiler.
'
    Device = 16F15356
    Declare Xtal = 8
   
Symbol cFlash_WriteBlocksize     = 32
Symbol cFlash_EraseBlocksize     = 32
Symbol cMax_FlashAddress         = _code
Symbol cFlash_WriteBlocksizeMin1 = (cFlash_WriteBlocksize - 1)
Symbol cFlash_EraseBlocksizeMin1 = (cFlash_EraseBlocksize - 1)
Symbol cFlash_End                = (cMax_FlashAddress - 1)
Symbol cFlash_EraseXored         = (cFlash_End ^ (cFlash_EraseBlocksize - 1))
'
' Create some variables for the flash memory routines
'
    Dim Flash_tGIEBitValue As Bit
    Dim Flash_wStorageArray[cFlash_WriteBlocksize] As Word Heap

    Dim NVMDATLH As NVMDATH
    Dim Flash_wNVMADR As NVMADRL.Word   ' Create a 16-bit SFR from SFRs NVMADRL\H
    Dim Flash_wNVMDAT As NVMDATL.Word   ' Create a 16-bit SFR from SFRs NVMDATL\H
'
' Create some compiler system variables for the procedures to use
'
    Dim PP1  As Byte System
    Dim PP1H As Byte System
    Dim PP2  As Byte System
    Dim PP2H As Byte System
    Dim PP3  As Byte System
    Dim PP3H As Byte System

    Dim Flash_wValue As PP1.Word
    Dim Flash_wAddr  As PP2.Word
    Dim Flash_wTemp  As PP3.Word
'
' Create a variable for the demo code
'
    Dim MyWord As Word

'-----------------------------------------------------------------------------
' The main program starts here
' Shows the requirements for the procedures below
'
Main:
    Flash_EraseBlock(cMax_FlashAddress - cFlash_WriteBlocksize)   
    Flash_WriteWord(cMax_FlashAddress - cFlash_WriteBlocksize, 1234)
    MyWord = Flash_ReadWord(cMax_FlashAddress - cFlash_WriteBlocksize)
   
    Flash_wStorageArray = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    Flash_WriteBlock(cMax_FlashAddress - cFlash_WriteBlocksize)

'-----------------------------------------------------------------------------
' Read a 14-bit word from flash memory
' Input     : pFlashAddr holds the address to read the value from
' Output    : Returns the value read from flash
' Notes     : Disables and enables interrupts (if they were already enabled)
'
Proc Flash_ReadWord(pFlashAddr As Flash_wAddr), Flash_wValue
    Flash_tGIEBitValue = INTCONbits_GIE         ' Save the interrupt enable bit
    INTCONbits_GIE = 0                          ' Disable interrupts
    NVMADRL = pFlashAddr.Byte0
    NVMADRH = pFlashAddr.Byte1
    NVMCON1bits_NVMREGS = 0                     ' Deselect configuration space
    NVMCON1bits_RD = 1                          ' Initiate Read
    DelayCS 1
    DelayCS 1
    INTCONbits_GIE = Flash_tGIEBitValue         ' Restore the interrupt enable bit
    Result = Flash_wNVMADR
EndProc

'-----------------------------------------------------------------------------
' Write a 14-bit word to flash memory
' Input     : pFlashAddr holds the address to write the value too
'           : pValue holds the 14-bit value to write to flash
' Output    : None
' Notes     : Stores the flash memory block in the global array "Flash_wStorageArray"
'
Proc Flash_WriteWord(pFlashAddr As Flash_wAddr, pValue As Flash_wValue)   
    Dim bOffset As Byte = pFlashAddr & cFlash_EraseBlocksizeMin1
    Dim bIndex As Byte
    Flash_wTemp = (pFlashAddr & cFlash_EraseXored)
'
' The flash block will be erased, so read and save the existing data first
'
    For bIndex = 0 To cFlash_EraseBlocksizeMin1
        Flash_wStorageArray[bIndex] = Flash_ReadWord(Flash_wTemp + bIndex)
    Next
    Flash_wStorageArray[bOffset] = pValue               ' Write at bOffset within the storage array
    Flash_WriteBlock(Flash_wTemp)                       ' Write "Flash_wStorageArray" to flash memory
EndProc

'-----------------------------------------------------------------------------
' Write a RAM block to the flash memory block
' Input     : The global array "Flash_wStorageArray" holds the data to write to the flash block
' Output    : Returns 1 if sucessful, otherwise returns 0
' Notes     : Disables and enables interrupts (if they were already enabled)
'
Proc Flash_WriteBlock(pFlashAddr As Flash_wAddr), Bit
    Dim bIndex As Byte  
'
' Flash write must start at the beginning of a row
'
    If pFlashAddr <> Flash_wTemp Then
        Result = 0
        ExitProc
    EndIf
    Flash_wTemp = (pFlashAddr & cFlash_EraseXored)
    Flash_tGIEBitValue = INTCONbits_GIE                 ' Save the interrupt enable bit
    INTCONbits_GIE = 0                                  ' Disable interrupts
    Flash_EraseBlock(pFlashAddr)                        ' Erase the flash memory block

    NVMCON1bits_NVMREGS = 0                             ' Deselect configuration space
    NVMCON1bits_WREN = 1                                ' Enable writes
    NVMCON1bits_LWLO = 1                                ' Only load write latches
'
' Write the storage array to the flash memory block
'
    For bIndex = 0 To cFlash_WriteBlocksizeMin1
        NVMADRL = pFlashAddr.Byte0                      ' Load lower 8 bits of write address
        NVMADRH = pFlashAddr.Byte1                      ' Load upper 6 bits of write address
        Flash_wNVMDAT = Flash_wStorageArray[bIndex]     ' Write to the flash memory
        If bIndex = cFlash_WriteBlocksizeMin1 Then      ' Has the block size been reached?
            NVMCON1bits_LWLO = 0                        ' Yes. So start the Flash program memory write
        EndIf
        NVMCON2 = $55                                   ' \
        NVMCON2 = $AA                                   ' |
        NVMCON1bits_WR = 1                              ' | Required sequence for write
        DelayCS 1                                       ' |
        DelayCS 1                                       ' /
        Inc pFlashAddr
    Next
    NVMCON1bits_WREN = 0                                ' Disable writes
    INTCONbits_GIE = Flash_tGIEBitValue                 ' Restore the interrupt enable bit
    Result = 1
EndProc

'-----------------------------------------------------------------------------
' Erase a block of flash memory
' Input     : pStartAddr holds the address to start the erase
' Output    : None
' Notes     : Disables and enables interrupts (if they were already enabled)
'
Proc Flash_EraseBlock(pStartAddr As Flash_wAddr)
    Flash_tGIEBitValue = INTCONbits_GIE             ' Save the interrupt enable bit
    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
    NVMCON1bits_NVMREGS = 0                         ' Deselect configuration space
    NVMCON1bits_FREE = 1                            ' Setup for an erase
    NVMCON1bits_WREN = 1                            ' Allow an erase to happen
    NVMCON2 = $55                                   ' \
    NVMCON2 = $AA                                   ' |
    NVMCON1bits_WR = 1                              ' | Required sequence for erase
    DelayCS 1                                       ' |
    DelayCS 1                                       ' /
    NVMCON1bits_WREN = 0                            ' Disable writes
    INTCONbits_GIE = Flash_tGIEBitValue             ' Restore the interrupt enable bit
EndProc

The above code has not been tested on a device, but it shows how the flash memory works and can be adjusted by the user for any other devices that use the same mechanism, but with different block sizes. Because it has not been tested, I cannot answer any questions concerning it, because I cannot afford to buy "all" the new devices for testing. See what I mean about "catch 22"?