I'm having a problem with the ADC on a PIC24F16KM202 I'm trying to trace down, using XC16 v1.31.
This firmware is reading from two analog channels. The main channel is a brightness control pot giving me an analog voltage on AN19 that I am converting to an LED brightness. The other is a thermal sensor (a MCP9700) on AN9.
Most of the time AN19 is the channel being read, I'm updating my temperature every second, switching the analog channel over to AN9 when I do so, and switching back to AN19 on the next read.
Here's the function I'm using for the switching and the read:
- Code: Select all
/* FUNCTION: Read_ADC
* PURPOSE: - Reads ADC on selected channel 16 times and returns average
* ARGS: - uint8_t channel - Number of the ADC channel to be read
* RETURNS: - uint16_t 10-Bit average of 16 ADC samples
*/
uint16_t Read_ADC(uint8_t channel){
uint32_t adc_val = 0;
uint16_t reading = 0;
uint8_t count = 0;
static uint8_t prev_ch = 0;
volatile uint16_t *adc_ptr;
AD1CHSbits.CH0NA = 0b000;
switch (channel){
case TEMP_ADC_CH:
AD1CHSbits.CH0SA = 0b01001; //AN9
break;
case BRT_CTRL_ADC_CH:
AD1CHSbits.CH0SA = 0b10011; //AN19
break;
default:
return 0;
break;
}
if (channel != prev_ch){
__delay_us(72); /*Analog input change requires a delay*/
prev_ch = channel;
}
adc_ptr = &ADC1BUF0;
IFS0bits.AD1IF = 0;
AD1CON1bits.ASAM = 1;
while (!IFS0bits.AD1IF){}; /*ADC interrupts after 16 readings (set by AD1CON2bits.SMPI)*/
AD1CON1bits.ASAM = 0;
for (count = 0; count < 16; count++){
reading = *adc_ptr;
adc_val = adc_val + *adc_ptr++;
}
adc_val = adc_val >> 4; //Right shift 4 bits to divide by 16
return (uint16_t) adc_val;
}
What I'm finding is that I am often (maybe 1/3 of the time? Sometimes more) getting some kind of effect between the channels. I confirmed this by printing out my ADC average whenever I call this function to read the thermal sensor.
- Code: Select all
/* FUNCTION: Get_Analog Temperature
* PURPOSE : Gets a temperature reading from analog thermal sensor
* ARGS : uint8_t channel -> Analog channel of sensor
* RETURNS : int16_t temperature -> Temperature in Degrees C
*****************************************************************/
float Get_Analog_Temperature(uint8_t channel){
uint16_t adc_raw;
float voltage;
float temperature;
adc_raw = Read_ADC(channel);
voltage = adc_raw * ADC_V_PER_COUNT;
temperature = ((voltage - MCP9700_OFFSET)*MCP9700_T_COEFF);
sprintf(buffer,"R:%d\r\n",adc_raw);
UART_Xmit(buffer);
return temperature;
}
The result of the UART transmission of this function is below:
R:414
R:414
R:452
R:414
R:414
R:452
R:414
R:452
R:452
R:414
R:414
R:414
R:452
414 is the correct reading for this channel for the voltage I am giving. 452 are the "bad" readings I'm getting that are throwing my averaging off. In this case this takes me from reading about 84C to 96C. I've noticed if I turn down the voltage coming in from the brightness pot to minimum (0V), these values swing to the other direction:
R:414
R:388
R:414
R:414
R:388
R:414
R:388
R:414
R:414
R:414
R:388
R:414
R:388
I've confirmed via multimeter and scope that the voltage on the AN9 pin is NOT changing, so it seems it must be internal to the PIC and is probably my configuration (I just can't seem to determine where). My ADC Init function is shown below:
- Code: Select all
void Init_ADC(void){
AD1CON1bits.ADSIDL = 1; /*Stop operation when idle*/
AD1CON1bits.MODE12 = 0; /*10-Bit operation*/
AD1CON1bits.FORM = 0b00; /*Result is unsigned integer*/
AD1CON1bits.SSRC = 0b0111; /*Auto-Clear the SAMP bit after sampling*/
AD1CON1bits.ASAM = 0; /*Sample Begins when SAMP is set*/
AD1CON2bits.PVCFG = 0b00; /*Positive reference voltage is VDD pin*/
AD1CON2bits.NVCFG0 = 0; /*Negative reference voltage is AVss*/
AD1CON2bits.BUFREGEN = 0; /*ADC Result buffer is a FIFO starting at AD1BUF0*/
AD1CON2bits.BUFM = 0; /*Fill buffer sequential starting at AD1BUF0*/
AD1CON2bits.SMPI = 0b10000; /*Interrupt on 16 samples*/
AD1CON3bits.ADRC = 0; /*ADC Clock derived from system clock FCY = 8MHz*/
AD1CON3bits.SAMC = 0b01000; /*Auto Sample time is (8 * TAD) = 16uS*/
AD1CON3bits.ADCS = 0b00010000; /*Conversion clock is (16 * TCY) = 2uS = TAD*/
IFS0bits.AD1IF = 0; /*Clear the ADC Interrupt flag*/
IEC0bits.AD1IE = 1;
AD1CON1bits.ADON = 1; /*ADC On*/
}
Anyone out there have suggestions for what I can try (or what I'm doing wrong) to get these two channels switching and reading properly?
Thanks in advance!