Page 1 of 1
Assistance needed with 18F27Q10 Sector Writes
Posted: Sat Jul 10, 2021 7:34 pm
by bernardj
Hi
I have been battling to get the PFM sector write functionality going on the 18F27Q10. The chip supports writing to program memory using either words or sectors. I successfully managed the writing to PFM using words (2 bytes) without hassles. Different story though with writing by sector.
Simplifying my code to the following code segment, it results in no change of the sector at address $10000, even though I have tried to implement the datasheet's ASM example as accurately as possible. I expect to see a whole sector filled with value 12 (arbitrary number), but all bytes remain at $FF (caused by the sector erase).
Code protection is off in the configuration settings.
Any help would be appreciated, thanks very much!!
Regards
Bernard
Code: Select all
Sub writeSector()
Dim writeAddress as LongWord
Dim i as Word
INTCON = 0
writeAddress = $10000
NVMCON0.7 = 1
NVMADRU = writeAddress.byte2
NVMADRH = writeAddress.byte1
NVMADRL = writeAddress.byte0
NVMCON2 = $BB
NVMCON2 = $44
NVMCON1.1 = 1 'SECRD
while NVMCON1.1 = 1
wend
NVMCON2 = $CC
NVMCON2 = $33
NVMCON1.6 = 1 'SECER
while NVMCON1.6 = 1
wend
if NVMCON0.4 = 1 then ' if error occurred
UART.Write("Err 1",13,10)
endif
TBLPTRU = writeAddress.byte2
TBLPTRH = writeAddress.byte1
TBLPTRL = writeAddress.byte0
for i = 0 to 255
TABLAT = 12
asm
TBLWT*+
end asm
next
NVMCON2 = $DD
NVMCON2 = $22
NVMCON1.5 = 1 'SECWR
while NVMCON1.5 = 1
wend
if NVMCON0.4 = 1 then ' if error occurred
UART.Write("Err 2",13,10)
endif
end sub
Re: Assistance needed with 18F27Q10 Sector Writes
Posted: Sun Jul 11, 2021 12:01 am
by Jerry Messina
Hi Bernard,
I think in order to use the sector write commands you need to use the special Buffer Ram locations, which you can find the address of in the 18F27Q10.bas device file...
Code: Select all
#define _nvm_bufferram_start = $0D00 // nvm buffer ram start address
#define _nvm_bufferram_end = $0DFF // nvm buffer ram end address
I haven't actually tried that, so tomorrow I'll see if I can piece together an example and test it.
Re: Assistance needed with 18F27Q10 Sector Writes
Posted: Sun Jul 11, 2021 7:35 am
by bernardj
Thanks very much, sir.
My understanding is that using TBLPTR is used for program memory access. If one must use a special RAM area for sector writes, how do I access that RAM address - with TBLPTR as well? I would love to see your approach to the solution - I am sure I am going to learn something new
.
Thanks very much in advance
Regards
Bernard
Re: Assistance needed with 18F27Q10 Sector Writes
Posted: Sun Jul 11, 2021 3:39 pm
by Jerry Messina
Here's an example using the buffer ram to work with pfm sectors.
Supposedly you can use the TABLEPTR to load the ram too, but it just seems to make more sense to me to use it as ram since that's where it's located.
On the Q10 the buffer ram is a 256-byte block reserved for nvm sector operations. You can read it, change its contents, and write it pretty much like normal ram.
I tested this with a 25Q10, but it should work with the others as well. Just change the PFM_ADDR const to match (or set the variable pfmAddr)
Code: Select all
// Q10 PFM sector operations using BufferRam
device = 18F27Q10
clock = 64
include "intosc.bas"
include "usart.bas"
// 18FxxQ family nvm BufferRam definitions (from device file)
const BUF_RAM_ADDR = _nvm_bufferram_start
const BUF_RAM_SIZE = _nvm_bufferram_end - _nvm_bufferram_start + 1
dim buffer_ram(BUF_RAM_SIZE) as byte absolute (BUF_RAM_ADDR)
// NVMCON0 register bits
const
NVMEN = 7,
NVMERR = 4
// NVMCON1 register bits
const
SECER = 6,
SECWR = 5,
WR = 4,
SECRD = 1,
RD = 0
// global INTCON IE register bit
const GIE = 7
// pfm sector to test (device-dependant)
const PFM_ADDR = $10000
//---------------------------
// PFM NVM sector functions
//---------------------------
// read pfm sector at 'addr' into buffer_ram. returns state of NVMERR
public function ReadSector(addr as longword) as bit
dim t_intcon as byte ' copy of INTCON
' get current intr enable and disable interrupts
t_intcon = INTCON
INTCON.bits(GIE) = 0
' clear BufferRam so we can see results of sector read (optional)
clear(buffer_ram)
' read sector contents into BufferRam
NVMADRU = addr.byte2
NVMADRH = addr.byte1
NVMADRL = addr.byte0
' enable nvm operations
NVMCON0.bits(NVMEN) = 1
' SECRD unlock
NVMCON2 = $BB
NVMCON2 = $44
' sector read
NVMCON1.bits(SECRD) = 1
while (NVMCON1.bits(SECRD) = 1)
end while
' buffer_ram should now contain current sector data
' return state of NVMERR flag (remains set... user must clear)
result = NVMCON0.bits(NVMERR)
' disable sector operations
NVMCON0.bits(NVMEN) = 0
' restore GIE intr bit setting
if (t_intcon.bits(GIE) = 1) then
INTCON.bits(GIE) = 1
end if
end function
// erase pfm sector at 'addr'. returns state of NVMERR
public function EraseSector(addr as longword) as bit
dim t_intcon as byte ' copy of INTCON
' get current intr enable and disable interrupts
t_intcon = INTCON
INTCON.bits(GIE) = 0
' set sector addr to erase
NVMADRU = addr.byte2
NVMADRH = addr.byte1
NVMADRL = addr.byte0
' enable nvm operations
NVMCON0.bits(NVMEN) = 1
' SECER unlock
NVMCON2 = $CC
NVMCON2 = $33
' sector erase
NVMCON1.bits(SECER) = 1
while (NVMCON1.bits(SECER) = 1)
end while
' return state of NVMERR flag (remains set... user must clear)
result = NVMCON0.bits(NVMERR)
' disable sector operations
NVMCON0.bits(NVMEN) = 0
' restore GIE intr bit setting
if (t_intcon.bits(GIE) = 1) then
INTCON.bits(GIE) = 1
end if
end function
// write buffer_ram to pfm sector at 'addr'. returns state of NVMERR
public function WriteSector(addr as longword) as bit
dim t_intcon as byte ' copy of INTCON
' get current intr enable and disable interrupts
t_intcon = INTCON
INTCON.bits(GIE) = 0
' set sector addr to write
NVMADRU = addr.byte2
NVMADRH = addr.byte1
NVMADRL = addr.byte0
' enable nvm operations
NVMCON0.bits(NVMEN) = 1
' SECWR unlock
NVMCON2 = $DD
NVMCON2 = $22
' sector write
NVMCON1.bits(SECWR) = 1
while (NVMCON1.bits(SECWR) = 1)
end while
' return state of NVMERR flag (remains set... user must clear)
result = NVMCON0.bits(NVMERR)
' disable sector operations
NVMCON0.bits(NVMEN) = 0
' restore GIE intr bit setting
if (t_intcon.bits(GIE) = 1) then
INTCON.bits(GIE) = 1
end if
end function
//---------------------------
// main program
//---------------------------
dim pfmAddr as longword
dim nvmStat as bit
dim ix as byte
main:
' set nvm params
pfmAddr = PFM_ADDR
' read current sector into BufferRam
nvmStat = ReadSector(pfmAddr)
' buffer_ram should now contain current sector data
if (nvmStat = 1) then
USART.Write("ReadSector Err",13,10)
NVMCON0.bits(NVMERR) = 0 ' user must clear error flag
endif
' erase sector
nvmStat = EraseSector(pfmAddr)
if (nvmStat = 1) then
USART.Write("EraseSector Err",13,10)
NVMCON0.bits(NVMERR) = 0 ' user must clear error flag
endif
' verify erase by re-reading sector
nvmStat = ReadSector(pfmAddr)
' BufferRam should now contain sector data (all FF)
for ix = 0 to bound(buffer_ram)
if (buffer_ram(ix) <> $FF) then
USART.Write("EraseSector Verify Err",13,10)
endif
next
' fill BufferRam with contents to write (0-255 in this example)
for ix = 0 to bound(buffer_ram)
buffer_ram(ix) = ix
next
' write sector
nvmStat = WriteSector(pfmAddr)
if (nvmStat = 1) then
USART.Write("WriteSector Err",13,10)
NVMCON0.bits(NVMERR) = 0 ' user must clear error flag
endif
' verify write by re-reading sector
nvmStat = ReadSector(pfmAddr)
' BufferRam should now contain sector data (0-255)
for ix = 0 to bound(buffer_ram)
if (buffer_ram(ix) <> ix) then
USART.Write("WriteSector Verify Err",13,10)
endif
next
while (true)
end while
end
Re: Assistance needed with 18F27Q10 Sector Writes
Posted: Sun Jul 11, 2021 10:16 pm
by bernardj
Hi Jerry
Again, I bestow onto thee tons of gratitude and appreciation! Thanks very much for the example. Your experience and aptitude shows clearly again.
Your interpretation of the datasheet is exceptional. The datasheet is clearly very misleading, if not downright just incorrect, with its inclusion of the use of TBLPTR for the sector writes. If one replaces the TBLPTR code with yours as in your example, the sector writes works perfectly. I also tried populating the sector RAM with FSR and POSTINC, which also works.
Any way, thanks again. Without your guidance, I would not have been able to resolve this!! As expected, I did learn something new
.
Best wishes!
Bernard
Re: Assistance needed with 18F27Q10 Sector Writes
Posted: Mon Jul 12, 2021 10:18 am
by Jerry Messina
The datasheet is clearly very misleading, if not downright just incorrect, with its inclusion of the use of TBLPTR for the sector writes.
I agree, it is confusing. I think if you're only writing a byte or two then using the TBLPTR method to load the write latches is probably simpler.
The thing that caught my eye was this statement in the datasheet:
Data can be written directly into PFM one 16-bit word at a time using the NVMADR and NVMCON1 controls or as a full sector from sector RAM,
which is also referred to as the holding registers. These 8-bit registers are located in the RAM bank following the last GPR RAM bank.
The holding registers are directly accessible as any other SFR/GPR register and also may be loaded via sequential writes using the TABLAT and TBLPTR registers.
The datasheet doesn't talk much about the BufferRam and its use. When I first did the device files for the Q10 I couldn't figure out what this section in RAM was for until I saw that paragraph.