How to Improve ADC Resolution

Thread Starter

R!f@@

Joined Apr 2, 2009
10,004
C:
#define TMR0reload 169
#define ADCreload 64
unsigned int ADC_Vtemp, ADC_V;
unsigned int ADC_Temp, Volts;
unsigned char ADC_Loop_Counter;

bit Display;

char *Vout = "00.00";

void interrupt(){
if(INTCON.T0IF){
  INTCON.T0IF = 0;
  TMR0 += TMR0reload;

  if(ADC_Loop_Counter){         // If loop counter is not zero
    ADC_Vtemp += ADC_Read(0);   // Read ADC and keep adding to accumulator(temp)
    ADC_Loop_Counter--;         // Decrement loop counter
   }
  if(ADC_Loop_Counter == 0){  // If loop counter is 0
      ADC_Vtemp = ADC_Temp;        // Copy accumulator
      ADC_Vtemp = 0;            // Clear accumulator
      ADC_Loop_Counter = ADCreload; // reload loop counter
      Display = 1;              // set conversion flag
   }

}
}

void main(){
OSCCON = 0x77;
ANSEL = 0x0F;
ANSELH = 0x20;
CM1CON0 = 0x07;
CM2CON0 = 0x07;
PORTA = 0;
PORTB = 0;
PORTC = 0;
TRISA = 0x3F;
TRISB = 0XF0;
TRISC = 0x07;
ADC_Init();
LCD_Init();
Lcd_Cmd(_LCD_CLEAR);
Lcd_Cmd(_LCD_CURSOR_OFF);

Display = 0;
ADC_Vtemp = 0;
ADC_V = 0;
ADC_Loop_Counter = ADCreload;
OPTION_REG = 0x81;   // Prescaler at 4
INTCON = 0;
PIE1 = 0;
PIE2 = 0;
INTCON.T0IF = 0;
TMR0 = 0;
INTCON.T0IE = 1;
INTCON.GIE = 1;
while(1){
  if(Display){
   Display = 0;

   Volts = (ADC_Temp *2929.6875)/65536;  // testing voltage scaling to 30V max

   Vout[0] = Volts/1000+48; // Extract 1000th place
   Vout[1] = (Volts/100)%10+48; // Extract 100th place
   Vout[3] = (Volts/10)%10+48; // Extract 10th place
   Vout[4] = (Volts/1)%10+48; // Extract 1st place
   Lcd_Out(1,10,Vout); // Send to LCD
   Lcd_Out(1,1,"Voltage:" ); // Send to LCD
   Lcd_Out(2,1,"Current:" ); // Send to LCD
  }
}
}

LCD reading remains unchanged does not respond to POT.
Every reset loads a new value in to voltage reading and it remains stuck.
I am lost here..sorry.
 

OBW0549

Joined Mar 2, 2015
3,566
The last forty or so posts in this thread have been a good reminder to me of why I have such a strong preference for assembly language: this stuff is so easy using assembler, it's not funny; and it's even easier on the dsPIC series of processors.
 

joeyd999

Joined Jun 6, 2011
6,305
C:
#define TMR0reload 169
#define ADCreload 64
unsigned int ADC_Vtemp, ADC_V;
unsigned int ADC_Temp, Volts;
unsigned char ADC_Loop_Counter;

bit Display;

char *Vout = "00.00";

void interrupt(){
if(INTCON.T0IF){
  INTCON.T0IF = 0;
  TMR0 += TMR0reload;

  if(ADC_Loop_Counter){         // If loop counter is not zero
    ADC_Vtemp += ADC_Read(0);   // Read ADC and keep adding to accumulator(temp)
    ADC_Loop_Counter--;         // Decrement loop counter
   }
  if(ADC_Loop_Counter == 0){  // If loop counter is 0
      ADC_Vtemp = ADC_Temp;        // Copy accumulator
      ADC_Vtemp = 0;            // Clear accumulator
      ADC_Loop_Counter = ADCreload; // reload loop counter
      Display = 1;              // set conversion flag
   }

}
}

void main(){
OSCCON = 0x77;
ANSEL = 0x0F;
ANSELH = 0x20;
CM1CON0 = 0x07;
CM2CON0 = 0x07;
PORTA = 0;
PORTB = 0;
PORTC = 0;
TRISA = 0x3F;
TRISB = 0XF0;
TRISC = 0x07;
ADC_Init();
LCD_Init();
Lcd_Cmd(_LCD_CLEAR);
Lcd_Cmd(_LCD_CURSOR_OFF);

Display = 0;
ADC_Vtemp = 0;
ADC_V = 0;
ADC_Loop_Counter = ADCreload;
OPTION_REG = 0x81;   // Prescaler at 4
INTCON = 0;
PIE1 = 0;
PIE2 = 0;
INTCON.T0IF = 0;
TMR0 = 0;
INTCON.T0IE = 1;
INTCON.GIE = 1;
while(1){
  if(Display){
   Display = 0;

   Volts = (ADC_Temp *2929.6875)/65536;  // testing voltage scaling to 30V max

   Vout[0] = Volts/1000+48; // Extract 1000th place
   Vout[1] = (Volts/100)%10+48; // Extract 100th place
   Vout[3] = (Volts/10)%10+48; // Extract 10th place
   Vout[4] = (Volts/1)%10+48; // Extract 1st place
   Lcd_Out(1,10,Vout); // Send to LCD
   Lcd_Out(1,1,"Voltage:" ); // Send to LCD
   Lcd_Out(2,1,"Current:" ); // Send to LCD
  }
}
}

LCD reading remains unchanged does not respond to POT.
Every reset loads a new value in to voltage reading and it remains stuck.
I am lost here..sorry.
Well, since you are not writing your code the way I suggested, i.e. two interrupts, ignoring my comment regarding the extra unnecessary comparison, reloading TMR0 rather than just letting it roll over naturally, etc., there is little I am inclined to do to help you further.

I build my code in modules. I write a module and test it. Then I write other modules and test them. Then I tie them together with whatever support code is necessary, and test that. I don't think I ever wrote a whole program and then expected it to work.

Forget about the formulas and the LCD. Get the conversions working. Use the debugger (or simulator) to show intermediate results. Step through each line of your code and make sure it's doing what you expect it to do.

I'm not going to do it for you.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
10,004
I am sorry joeyd999. I am not very good at this. Coding is not easy to understand.
I had to look up type casting thing when u said it. I still don't get it somewhat.
Going through what you said.
2 interrupts? But I cannot see two interrupts.
But I can let TMR0 roll over without reloading.

I do not understand the "extra unnecessary comparison" part.

I will step thru the code using debugger and see where it is going wrong.
 

joeyd999

Joined Jun 6, 2011
6,305
I am sorry joeyd999. I am not very good at this. Coding is not easy to understand.
I had to look up type casting thing when u said it. I still don't get it somewhat.
This is one of the issues a C programmer always has to keep in mind. In .asm, there is no such thing as type casting. When different data types are mixed in a single expression, they all must be converted first to a common data type prior to the operation, and then the result cast to the destination variable data type. This can produce unexpected results.

2 interrupts? But I cannot see two interrupts.
Yes. As discussed earlier your timer0 ISR should simply activate the conversion by setting the A/D go bit in the proper ADCON register. The A/D ISR captures the data, switches the MUXs, and accumulates the results.

But I can let TMR0 roll over without reloading.
For simplicity, set the rollover time so that it equals your desired A/D sampling period.

I do not understand the "extra unnecessary comparison" part.
Line 16.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
10,004
Thank you for pointing things out.

I needed to figure out what I was doing wrong.
I decided not use interrupt and to find exactly what you were talking about. As interrupt is still new to me. I always thought I will never understand ISR. But with John's help I was able to figure it out up to some point and actually use it.

I changed the code. ISR is not used to figure out what went wrong and what joeyd999 was trying to teach me.

This time I just used the main();
Made a 64 increment counter.
Accumulated 2 ADC's simultaneously.
when counter ended, reset it, save the content to a static register, reset the accumulator and SET the flag to indicate " time to update LCD".

It works so far.
The Display refresh is still very very fast, not a problem to slow down.
I can see the display changing at the resolution I want, that is 10mv for "30.00V" Display.
Current works too.

I believe this was what I needed to know. Before it was 34mv Jump

C:
/*

#define TMR0reload 169
#define ADCreload 64
unsigned int ADC_Vtemp, ADC_Itemp;
unsigned int ADC_Voltage, Volts;
unsigned int ADC_Current, Amps;
unsigned char ADC_Loop_Counter;

bit Display;

char *Vout = "00.00";
char *Iout = "00.00";
/*
void interrupt(){
// if(INTCON.T0IF)
  INTCON.T0IF = 0;

  ADC_Vtemp = ADC_Read(0);
  ADC_V += ADC_Vtemp;
  ADC_Loop_Counter--;         // Decrement loop counter


}
*/
void main(){
OSCCON = 0x77;
ANSEL = 0x0F;
ANSELH = 0x20;
CM1CON0 = 0x07;
CM2CON0 = 0x07;
PORTA = 0;
PORTB = 0;
PORTC = 0;
TRISA = 0x3F;
TRISB = 0XF0;
TRISC = 0x07;
ADC_Init();
LCD_Init();
Lcd_Cmd(_LCD_CLEAR);
Lcd_Cmd(_LCD_CURSOR_OFF);

Display = 0;
ADC_Vtemp = 0;
ADC_Itemp = 0;
ADC_Voltage = 0;
ADC_Current = 0;
Amps = 0;
Volts = 0;
ADC_Loop_Counter = ADCreload;
OPTION_REG = 0x81;   // Prescaler at 4
INTCON = 0;
PIE1 = 0;
PIE2 = 0;
INTCON.T0IF = 0;
TMR0 = 0;
INTCON.T0IE = 1;
INTCON.GIE = 1;
while(1){
/*
unsigned int ADC_Vtemp, ADC_Itemp;
unsigned int ADC_Voltage, Volts;
unsigned int ADC_Current, Amps;
unsigned char ADC_Loop_Counter;
*/
  if(ADC_Loop_Counter >= 1){
     ADC_Vtemp += ADC_Read(0);
     ADC_Itemp += ADC_Read(2);
     --ADC_Loop_Counter;
  }
  if(ADC_Loop_Counter == 0){
     ADC_Loop_Counter = ADCreload;
     ADC_Voltage = ADC_Vtemp;
     ADC_Current = ADC_Itemp;
     ADC_Vtemp = 0;
     ADC_Itemp = 0;
     Display = 1;
  }
  if(Display){
     Display = 0;
     Lcd_Out(1,1,"Voltage:" ); // Send to LCD
     Lcd_Out(2,1,"Current:" ); // Send to LCD
   
     Volts = (ADC_Voltage * 2929.69)/65536;
     Vout[0] = Volts/1000+48; // Extract 1000th place
     Vout[1] = (Volts/100)%10+48; // Extract 100th place
     Vout[3] = (Volts/10)%10+48; // Extract 10th place
     Vout[4] = (Volts/1)%10+48; // Extract 1st place
     Lcd_Out(1,10,Vout); // Send to LCD
   
     Amps = (ADC_Current * 195.3125)/65536;
     Iout[0] = Amps/1000+48; // Extract 1000th place
     Iout[1] = (Amps/100)%10+48; // Extract 100th place
     Iout[3] = (Amps/10)%10+48; // Extract 10th place
     Iout[4] = (Amps/1)%10+48; // Extract 1st place
     Lcd_Out(2,10,Iout); // Send to LCD

  }
}
}
Is this what you mean by averaging ?
I know it is not how u wanted it, but this helps me to figure it out.
 
Last edited:
Top