Hello,
I am using a DUE in order to utilize it's differential ADC to measure from a PCB meant to sense pH changes. However, my code below is not giving me the proper voltage difference when applying a voltage between the analog terminals A0 and A1. For example, if I apply 1.5V on one terminal and 2.5V on the other I do not get 1V (doesn't have to be exact).
Unfortunately, my experience with programming the SAM is terrible, I have never used it before, thus any help modifying the code is appreciated!
void setup()
{
adc_setup();
Serial.begin(9600);
}
void loop()
{
//float Diff67Value;
int16_t Diff67Value;
Diff67Value = ADC->ADC_LCDR;
float Diff = Diff67Value * 3.3/4095.0;
Serial.println(Diff);
//Serial.println(Diff67Value *3.3/4095);
//Serial.println(Diff67Value);
}
void adc_setup() {
adc_set_resolution(ADC, ADC_12_BITS);
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_FREERUN
| ADC_MR_ANACH_ALLOWED;
ADC->ADC_COR = ADC_COR_DIFF7 // Differential mode for channel 7 and channel 6 (6 - 7); Gain = 0.5;
| ADC_COR_DIFF6
| ADC_COR_OFF6 // Offset is mandatory
| ADC_COR_OFF7;
ADC->ADC_CHER |= ADC_CHER_CH6 | ADC_CHER_CH7; // Enable Channels 7,6 = A0,A1
}
This sketch should work better (1 KHz ADC conversions , ADC fully differential between A0(CH7) and A1(CH6)):
/***********************************************************************************************/
/* Differential mode for analog channels 6 and 7, hardware trigger by TCO channel 2 = TIOA2 */
/* ADC Conversion frequency = 1 KHz */
/***********************************************************************************************/
#define ADC_REFERENCE_VALUE (3.3)
#define ADC_12BIT_FULL_SCALE_VALUE (4095)
volatile uint16_t Result6_7;
volatile boolean Flag;
void setup()
{
Serial.begin(250000);
adc_setup();
tc_setup();
}
void loop()
{
float Result;
if (Flag)
{
Flag = false;
Result = Result6_7 * (float)ADC_REFERENCE_VALUE / (float)ADC_12BIT_FULL_SCALE_VALUE;
Serial.println(Result);
}
}
/************* Configure ADC function *******************/
void adc_setup() {
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN | // Hardware trigger select
ADC_MR_TRGSEL_ADC_TRIG1 | // Trigger by TIOA0 rising edge
ADC_MR_LOWRES_BITS_12 |
ADC_MR_SLEEP_NORMAL |
ADC_MR_PRESCAL(1) |
ADC_MR_STARTUP_SUT0 |
ADC_MR_SETTLING_AST9 |
ADC_MR_ANACH_ALLOWED |
ADC_MR_TRACKTIM(10) |
ADC_MR_TRANSFER(2) |
ADC_MR_USEQ_NUM_ORDER;
//Differential mode for channels 7 and 6 (6 - 7); Gain = 0.5
//AD7 will be automatically enabled for differential operation
ADC->ADC_COR = ADC_COR_DIFF6 |
ADC_COR_OFF6;
//ADC->ADC_ACR = ADC_ACR_IBCTL(0b01); // Only if 1 MHz > conversion frequency > 500 KHz
ADC->ADC_IER = ADC_IER_EOC6;
NVIC_EnableIRQ(ADC_IRQn);
ADC->ADC_CHER |= ADC_CHER_CH6 | ADC_CHER_CH7; // Enable Channels 7,6 = A0,A1
}
/************* Timer Counter 0 Channel 0 to generate PWM pulses thru TIOA0 ************/
void tc_setup() {
PMC->PMC_PCER0 |= PMC_PCER0_PID27; // Timer Counter 0 channel 0 IS TC0
TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // MCK/2, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA0 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA0 on RC compare match
TC0->TC_CHANNEL[0].TC_RC = 42000; //<********************* Frequency = (Mck/2)/TC_RC = 1KHz
TC0->TC_CHANNEL[0].TC_RA = 20; //<******************** Duty cycle = (TC_RA/TC_RC) * 100 %
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Reset TC0 counter and enable
}
void ADC_Handler(void)
{
//Result can be read either from the ADC->ADC_LCDR or from ADC->ADC_CDR[6],
//But EOC6 bit is cleard only after reading ADC-ADC_CDR[6] (contrary to what states the datasheet !)
//You want to read ADC_CDR to make the interrupt fired again and again
Result6_7 = ADC->ADC_CDR[6];
Flag = true;
}