micknfld
Joined: 31 Jan 2006 Posts: 6
|
Sampling with ADC |
Posted: Thu Mar 16, 2006 1:40 pm |
|
|
I am having troubles sampling multiple voltages with the ADC of a 16F877. I was initially sampling within a timer interrupt but saw on this board that I could use ADC_READ_ONLY and ADC_START_ONLY to not delay within the interrupt. I can get it working when I sample only one analog port but when I try to sample two ports my values are wrong. For example, when I apply 5V to A2 and 0V to A3 then the corresponding ADC outputs are 345 and 34 respectively. They should be 1023 and 0. If I apply the same voltage to both A2 and A3 then the ADC displays the correct values. It seems as if one analog port is affecting the other. Is there a problem with not having enough delay between switching analog ports and sampling them? I even tried putting a 20us delay at the end of my timer interrupt. This improved it a little but it was still not correct. The timer interrupt is called 125 times per second. So I sample one channel 62 times per second and the other channel 63 times per second and then take the average. Here is my code:
Code: | #include<16f877.h>
#fuses HS,NOWDT,NOPROTECT
#device ADC=10
#use delay(clock=20000000)
#use rs232(BAUD=9600,XMIT=PIN_C6,RCV=PIN_C7)
#opt 9
#ORG 0x1F00,0x1FFF {} //reserve top 255 bytes of memory for the bootloader
#include <lcd.c>
//-----------------------------------------------------------------------------------------
#define INTERRUPTS_PER_SECOND 125 // 125 interrupts per second
int32 seconds = 0; // A running seconds counter
long interrupt_count; // Number of interrupts left before a second has elapsed
long CT1_Sample = 0; // Save average sample
long CT2_Sample = 0; // Save average sample
long CT1_Sample_temp = 0; // Save instantaneous sample
long CT2_Sample_temp = 0; // Save instantaneous sample
boolean update;
void clock_isr();
void print_sample();
void reset();
//-----------------------------------------------------------------------------------------------------
// #INT_TIMER2
//-----------------------------------------------------------------------------------------------------
#INT_TIMER2
void clock_isr() {
if (interrupt_count%2 == 0){ // Happens 62 times per second
CT1_Sample_temp += read_adc(ADC_READ_ONLY); // Sample CT1
set_adc_channel(3); // Change ADC channel to sample CT2
}else{ // Happens 63 times per second
CT2_Sample_temp += read_adc(ADC_READ_ONLY); // Sample CT2
set_adc_channel(2); // Change ADC channel to sample CT1
}
if(--interrupt_count == 0) { // One second reached.
++seconds;
interrupt_count = INTERRUPTS_PER_SECOND;
CT1_Sample = (long)(CT1_Sample_temp/62); // Calculate average
CT2_Sample = (long)(CT2_Sample_temp/63);
if (seconds%2 == 0){
update=true; // To call print_sample()
}
CT1_Sample_temp = 0;
CT2_Sample_temp = 0;
}
read_adc(ADC_START_ONLY);
}
//-----------------------------------------------------------------------------------------------------
// print_sample()
//-----------------------------------------------------------------------------------------------------
void print_sample(){
printf("CT1 Sample: %lu \n\r", CT1_Sample);
printf("CT2 Sample: %lu \n\r", CT2_Sample);
}
//-----------------------------------------------------------------------------------------------------
// reset()
//-----------------------------------------------------------------------------------------------------
void reset(){
update = false;
interrupt_count = INTERRUPTS_PER_SECOND;
CT1_Sample_temp = 0;
CT2_Sample_temp = 0;
CT1_Sample = 0;
CT2_Sample = 0;
// Setup ACD Ports
SETUP_ADC_PORTS(ALL_ANALOG); // A0 A1 A2 A3 A5 E0 E1 E2 Ref=Vdd
setup_adc( ADC_CLOCK_INTERNAL );
set_adc_channel(2);
delay_us(20);
read_adc(ADC_START_ONLY);
// Setup Timer2 to count every second
set_timer2(0);
setup_timer_2(T2_DIV_BY_16, 250, 10); // setup_timer_2(mode, period, postscale)
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
}
//-----------------------------------------------------------------------------------------------------
// main()
//-----------------------------------------------------------------------------------------------------
void main(){
reset();
printf("\n\rPrint Analog Samples\n\r");
while(true){
if (update==true){
update=false;
print_sample();
}
}
}
|
Here is the code I used without using ADC_READ_ONLY and ADC_START_ONLY.
Code: |
#include<16f877.h>
#fuses HS,NOWDT,NOPROTECT
#device ADC=10
#use delay(clock=20000000)
#use rs232(BAUD=9600,XMIT=PIN_C6,RCV=PIN_C7)
#opt 9
#ORG 0x1F00,0x1FFF {} //reserve top 255 bytes of memory for the bootloader
#include <lcd.c>
//-----------------------------------------------------------------------------------------
#define INTERRUPTS_PER_SECOND 125 // 125 interrupts per second
int32 seconds = 0; // A running seconds counter
long interrupt_count; // Number of interrupts left before a second has elapsed
int32 CT1_Sample = 0; // Save instantaneous sample
int32 CT2_Sample = 0; // Save instantaneous sample
boolean update;
void clock_isr();
void print_sample();
void reset();
//-----------------------------------------------------------------------------------------------------
// #INT_TIMER2
//-----------------------------------------------------------------------------------------------------
#INT_TIMER2
void clock_isr() {
if(--interrupt_count == 0) { // One second reached.
++seconds;
interrupt_count = INTERRUPTS_PER_SECOND;
CT1_Sample = 0;
CT2_Sample = 0;
set_adc_channel(2);
delay_us(20);
CT1_Sample = read_adc();
set_adc_channel(3);
delay_us(20);
CT2_Sample = read_adc();
if (seconds%2 == 0){
update=true;
}
}
}
//-----------------------------------------------------------------------------------------------------
// print_sample()
//-----------------------------------------------------------------------------------------------------
void print_sample(){
printf("CT1 Sample: %lu \n\r", CT1_Sample);
printf("CT2 Sample: %lu \n\r", CT2_Sample);
}
//-----------------------------------------------------------------------------------------------------
// reset()
//-----------------------------------------------------------------------------------------------------
void reset(){
update = false;
interrupt_count = INTERRUPTS_PER_SECOND;
// Setup ACD Ports
SETUP_ADC_PORTS(ALL_ANALOG); // A0 A1 A3 Ref=Vdd
setup_adc( ADC_CLOCK_INTERNAL );
// Setup Timer2 to count every second
set_timer2(0);
setup_timer_2(T2_DIV_BY_16, 250, 10); // setup_timer_2(mode, period, postscale)
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
}
//-----------------------------------------------------------------------------------------------------
// main()
//-----------------------------------------------------------------------------------------------------
void main(){
reset();
printf("\n\rs - Print Analog Samples\n\r");
while(true){
if (update==true){
update=false;
print_sample();
}
}
}
|
This code worked but I've read on this forum that it is not good to sample the ADC from within the interrupt and I wanted to get rid of the delays within the interrupt. The CCS manual says that you should have a 10us delay after switching analog ports but I took out the delay and it still works. Any ideas? |
|