For one pet project, I decided to use Microchip's PIC16LF1559. as it was close to perfect as far as my needs went in that particular scenario. So I got all parts, soldered it onto the board and it almost worked perfectly.
Single thing that gave me trouble was light detection. It seemed as if ADC converter was giving me nonsense. I check and rechecked my code multiple times as this was the first time I've dealt with this chip. It took me a while before I remembered to check errata sheet. And, lo and behold, there was an known issue: "AN20 is not internally connected to the ADC bus when AD2CON0's CHS bit select the pin." And guess which pin I decided to use for light sensor?
Initialization of ADC is something that will slightly vary with application but what follows is a reasonable starting point:
ADCOMCONbits.ADFM = 1; //right-justified
ADCOMCONbits.ADCS = 0b111; //Frc (clock derived from A/D RC oscillator)
ADCOMCONbits.ADPREF = 0b00; //Vref is Vdd
AD2CON0bits.CHS = 20; //select AN20
AAD2CHbits.CH20 = 1; //AN20 is connected to ADC2
This will nicely work around the issue by using AN20 on the secondary channel. Not an ideal situation but it was ok in my case as I had no other plans for secondary channel anyhow.
Reading a value is a bit different than when dealing with a usual, single-channel, setup:
uint16_t getAdcValue(void) {
AD2CON0bits.ADON = 1; //Turn on ADC
AD2CON0bits.GO = 1;
while (AD2CON0bits.GO_nDONE) { nop(); } //Wait for A/D convert complete
AD2CON0bits.ADON = 0; //Turn off ADC
if (AADSTATbits.AD2CONV) {
return AD2RES1;
} else {
return AD2RES0;
}
}
For my purpose it made sense to turn on/off ADC as needed - for some other scenarios it might be better just to leave it always on. But interesting bit comes when reading a value - ADC saves it interleaved between two registers: AD2RES0
and AD2RES1
. Code simply had to make sure to read correct one.
And this, slightly roundabout process, finally got the project working.