 |
 |
| View previous topic :: View next topic |
| Author |
Message |
PICNick
Joined: 23 Dec 2025 Posts: 2 Location: United Kingdom
|
| #DEVICE WRITE_EEPROM=ASYNC Problem |
Posted: Tue Dec 23, 2025 7:48 am |
|
|
Good Afternoon,
This is my first time posting a question on this forum, so I apologise in advance if I break any rules.
I am currently programming a PIC18F57Q84 with a clock speed of 32MHz and using version 5.115 of the CCS compiler. I am having some trouble with the use of #device WRITE_EEPROM=ASYNC.
In isolation, I have found that #device WRITE_EEPROM=ASYNC works very well. I am using a timer interrupt which, when activated writes 1 byte to eeprom using write_eeprom() every 16ms until a total of 14 bytes have been written. The time taken for each pass through this interrupt is 5us compared with 10ms when the line #device WRITE_EEPROM=ASYNC is not included. I am not writing to eeprom from anywhere within the main program cycle. and I have verified that the write has been successful. So far so good.
Also within my program I have set aside a block of program memory to store some calibration and configuration data. This is done from within the main program cycle and not an interrupt. This also works well when the line #device WRITE_EEPROM=ASYNC is not included, so again, so far all good.
However when I include #device WRITE_EEPROM=ASYNC I am finding that my program locks up on the call to write_program_memory(). It remains frozen for 8 seconds before being restarted by the WDT. There are no lockups without #device WRITE_EEPROM=ASYNC, and everything works perfectly together, other than the writes to eeprom are too slow.
The writes to program memory and eeprom are both very infrequent, and I can confirm that they are not happening at the same time. Interrupts are disabled when the function write_program_memory() is called.
I can find very little information on #device WRITE_EEPROM=ASYNC, other than the warning not to write to eeprom from within and outside of an ISR.
I have tried commenting out all calls to write_eeprom() and compiling with only the functions to write_program_memory() but the code still freezes when it hits this function.
I could free up some space in eeprom for the calibration data so that I no longer need to call write program_memory() but I would rather not do this, especially as I will also be including a bootloader at some point.
Can anyone here provide any clues on what might be happening, or advise me on any workarounds.
Many Thanks,
Nick. |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 20012
|
|
Posted: Tue Dec 23, 2025 8:53 am |
|
|
The problem is you are thinking of these as two separate systems. They
are not. The NVM module handles both. Flash memory and EEPROM are
very similar, ir is just the the EEPROM is built to support single byte
writes, and has much better write life. Both use the same control logic in
the chip.
Read section 10 in the data sheet.
The async operation relies on setting up the pointers for the write and
then letting this complete while the code is still running. Trying to setup
the same registers to perform a write to the program flash while this is
happening, is just not going to work.....
 |
|
 |
PICNick
Joined: 23 Dec 2025 Posts: 2 Location: United Kingdom
|
|
Posted: Mon Jan 05, 2026 9:57 am |
|
|
Hi Ttelmah, Happy new year to you.
Thanks for taking the time to reply to my post. Sorry for the delay coming back to you, I was busy having a very merry Christmas.
After following your advice pointing me to section 10 of the device datasheet I have managed to write my own asynchronous write_eeprom function, which does not clash with my writes to program memory.
This function is based upon the example 10-10 on page 115.
Yes I know unsigned int1 is a little odd and I could use boolean or just int1, I just do it for code alignment.
| Code: | unsigned int1 ee_async_write ( unsigned int16 address, unsigned int8 data )
{
unsigned int1 success = true;
unsigned int8 gie_bit_value = GIE; // Save interrupt enable bit
if ( !EE_GO )
{
// Load NVMADR with the target address of the byte
NVMADRU = 0x38;
NVMADRH = address << 8;
NVMADRL = address;
NVMDATL = data; // Load NVMDAT with the desired value
NVMCON1 = 0x03; // Set the byte write command
GIE = 0; // Disable interrupts
// Unlock sequence
NVMLOCK = 0x55;
NVMLOCK = 0xAA;
EE_GO = 1; // Start byte write
// A write error occurred, must be cleared by software
if ( WR_ERR ) { success = false; WR_ERR = 0; }
GIE = gie_bit_value; // Restore interrupt enable bit
NVMCON1 = 0x00; // Disable writes to memory
}
else
{
success = false; // Trying to write too quickly
}
return ( success );
} |
Currently I am not validating that the byte has been written correctly, I may come back and do this at some point. It should be very easy if I use example 10-9
I am not waiting around polling the NVCON1.GO bit in this function ( EE_GO ) and I am also not using the NVM interrupt. I am spacing the writes out by 16ms using a timer and just to be sure, not allowing a write to take place if the GO bit is already set.
So to start recording my block of 14 bytes to eeprom, I just start a timer from within my state machine, on each tick a new byte is written. When the last byte has been written the timer is disabled. I do not allow a new block of data to be written while timer 3 is active. The process takes approximately 13us per byte. I am very happy with this compared with the 10ms I was getting with the write_eeprom() function, and I also don't need to use #DEVICE WRITE_EEPROM=ASYNC, breaking my write_program_memory()'s
| Code: | #INT_TIMER3
void ee_write_isr( void )
{
static unsigned int8 next_wrt = 0;
unsigned int1 success = 0;
switch ( next_wrt++ )
{
case 0: success = ee_async_write ( next_wrt_lctn, time_now.sec ); break;
case 1: success = ee_async_write ( ++next_wrt_lctn, time_now.min ); break;
case 2: success = ee_async_write ( ++next_wrt_lctn, time_now.hour ); break;
case 3: success = ee_async_write ( ++next_wrt_lctn, time_now.year ); break;
case 4: success = ee_async_write ( ++next_wrt_lctn, time_now.mon ); break;
case 5: success = ee_async_write ( ++next_wrt_lctn, time_now.day ); break;
......... more cases with my required storage values
default:
TMR_ASYNC_EE_STOP;
TMR_ASYNC_EE_RESET;
next_wrt = 0;
break;
}
// Handle success being false
} |
This was actually a really simple task when reading the datasheet.
Thanks! |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 20012
|
|
Posted: Mon Jan 05, 2026 11:40 am |
|
|
The Christmas sounds jolly.
Very well done.
Problem with the generic functions is that they all have to be generic,
so cannot optimise. Your version is better suited for what you want.
Hope New Year went well too.
 |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|