I have been having repeated issues with calibration on multiple devices so I decided to dig into the code to figure out why and ended up making it a little more reliable.
Problem
When doing a fresh flash of the PIC, running calibration for the first time works great. However, I ran into an issue where I ripped the ribbon cable of my joystick (oops) and had to replace it. I figured I might as well recalibrate it just because, so re-ran the calibration on startup and noticed the stick stopped responding. Thinking that maybe I was doing the combination wrong (it's been a while since I've calibrated it), I attempted to do the same thing on another device I'm building. The same issue occurred for that one as well.
To verify that it wasn't something weird I was doing, I re-flashed both PICs, completely erasing them before I did (probably redundant since the flash performs an erase step). After running the calibration on a fresh flash, the sticks worked great. I then attempted another calibration and they broke again, and I couldn't get them to work until they were freshly flashed.
Troubleshooting
Suspecting that memory may be the culprit, I noticed that HEF (High Endurance Flash) was configured for the PIC rather than writing to regular old memory. HEF for PICs require that an entire block of flash is erased and written to, however, in this use case only 1 byte was being written to it at a time. This meant that anytime something was saved to HEF, it could eventually become garbage which seemed to be happening in my case. So I ended up modifying the address to write and read from EEPROM rather than HEF, as seen below.
C:
void eepromWrite(unsigned char address, char data) {
unsigned char interruptStatus = INTCONbits.GIE;
INTCONbits.GIE = 0;
NVMCON1bits.NVMREGS = 1;
NVMCON1bits.WREN = 1;
//NVMADRH = 0x80; <- HEF Address
NVMADRH = 0x00; //EEPROM address
NVMADRL = address;
NVMDATL = data;
nvmUnlock();
while (NVMCON1bits.WR);
NVMCON1bits.WREN = 0;
if (interruptStatus) INTCONbits.GIE = 1;
}
This change then resulted in another problem. After saving a value for the first time, on any subsequent calibration runs only the very last address that was written to would persist the value. This meant that every axis besides the very last one would read 0xFF. Not being an expert with the PIC, I did some research and found that sometimes, without delays between writes, the EEPROM might not be written to. I found this odd since the above code checks the WR bit, which will only be changed when the data has been written to memory.
I decided to try the delay anyway and it worked! Adding a small delay of 5ms between each right allowed each to finish and the data to be persisted as intended. This is definitely a brute forced approach to solving the problem but here is the code snippet of what I did.
C:
eepromWrite(SX_MIN_EEPROM, SX_MIN);
delay_ms(5); //seems excessive with writes checking the WR bit, but this makes writing more stable
eepromWrite(SX_MAX_EEPROM, SX_MAX);
delay_ms(5);
eepromWrite(SY_MIN_EEPROM, SY_MIN);
delay_ms(5);
eepromWrite(SY_MAX_EEPROM, SY_MAX);
delay_ms(5);
It's not the most elegant solution but it works, which is what I was aiming for. These changes allowed the calibration to work correctly each time it's run. I did make some other quality of life changes, such as only storing the values when the ranging made sense (basically as long as it's not dead center it will consider it good).