This is my second project using a PIC and second time using C, so I might be missing something obvious.
My problem is with the ADC module of my PIC24FJ128GB204.
I am using AN11 (PORTC1) as input, which is the battery voltage through a resistor divider. Input voltage to the pin is 2.12V during my tests.
The PIC gets Vcc from a 3.3V LDO which I use as reference. I don't really care too much about mV precision, so the LDO output is good enough.
My code (PRAGMA's in main.c):
- Code: Select all
// CONFIG1
#pragma config WDTPS = PS512 // Watchdog Timer Postscaler Select->1:512
#pragma config FWPSA = PR128 // WDT Prescaler Ratio Select->1:128
#pragma config WINDIS = OFF // Windowed WDT Disable->Standard Watchdog Timer
#pragma config FWDTEN = SWON // Watchdog Timer controlled by SWDTEN
#pragma config ICS = PGx1 // Emulator Pin Placement Select bits->Emulator functions are shared with PGEC1/PGED1
#pragma config LPCFG = OFF // Low power regulator control->Disabled - regardless of RETEN
#pragma config GWRP = OFF // General Segment Write Protect->Write to program memory allowed
#pragma config GCP = OFF // General Segment Code Protect->Code protection is disabled
#pragma config JTAGEN = OFF // JTAG Port Enable->Disabled
// CONFIG2
#pragma config POSCMD = NONE // Primary Oscillator Select->Primary Oscillator Disabled
#pragma config WDTCLK = LPRC // WDT Clock Source Select bits->WDT uses LPRC
#pragma config OSCIOFCN = ON // OSCO Pin Configuration->OSCO/CLKO/RA3 functions as port I/O (RA3)
#pragma config FCKSM = CSECMD // Clock Switching and Fail-Safe Clock Monitor Configuration bits->Clock switching is enabled, Fail-Safe Clock Monitor is disabled
#pragma config FNOSC = FRCPLL // Initial Oscillator Select->Fast RC Oscillator with PLL module (FRCPLL)
#pragma config ALTRB6 = APPEND // Alternate RB6 pin function enable bit->Append the RP6/ASCL1/PMPD6 functions of RB6 to RA1 pin functions
#pragma config ALTCMPI = CxINC_RB // Alternate Comparator Input bit->C1INC is on RB13, C2INC is on RB9 and C3INC is on RA0
#pragma config WDTCMX = WDTCLK // WDT Clock Source Select bits->WDT clock source is determined by the WDTCLK Configuration bits
#pragma config IESO = ON // Internal External Switchover->Enabled
// CONFIG3
#pragma config WPFP = WPFP127 // Write Protection Flash Page Segment Boundary->Page 127 (0x1FC00)
#pragma config SOSCSEL = ON // SOSC Selection bits->SOSC circuit selected
#pragma config WDTWIN = PS25_0 // Window Mode Watchdog Timer Window Width Select->Watch Dog Timer Window Width is 25 percent
#pragma config PLLSS = PLL_FRC // PLL Secondary Selection Configuration bit->PLL is fed by the on-chip Fast RC (FRC) oscillator
#pragma config BOREN = ON // Brown-out Reset Enable->Brown-out Reset Enable
#pragma config WPDIS = WPDIS // Segment Write Protection Disable->Disabled
#pragma config WPCFG = WPCFGDIS // Write Protect Configuration Page Select->Disabled
#pragma config WPEND = WPENDMEM // Segment Write Protection End Page Select->Write Protect from WPFP to the last page of memory
// CONFIG4
#pragma config DSWDTPS = DSWDTPS1F // Deep Sleep Watchdog Timer Postscale Select bits->1:68719476736 (25.7 Days)
#pragma config DSWDTOSC = LPRC // DSWDT Reference Clock Select->DSWDT uses LPRC as reference clock
#pragma config DSBOREN = ON // Deep Sleep BOR Enable bit->DSBOR Enabled
#pragma config DSWDTEN = ON // Deep Sleep Watchdog Timer Enable->DSWDT Enabled
#pragma config DSSWEN = ON // DSEN Bit Enable->Deep Sleep is controlled by the register bit DSEN
#pragma config PLLDIV = PLL6X // USB 96 MHz PLL Prescaler Select bits->6x PLL selected
#pragma config I2C1SEL = DISABLE // Alternate I2C1 enable bit->I2C1 uses SCL1 and SDA1 pins
#pragma config IOL1WAY = ON // PPS IOLOCK Set Only Once Enable bit->Once set, the IOLOCK bit cannot be cleared
Interrupt trap for ADC (in main.c):
- Code: Select all
void __attribute__ ( ( interrupt, no_auto_psv ) ) _ADC1Interrupt( void ){
IFS0bits.AD1IF=0;
Uart1SendString("Interrupt\r");
}
Relevant part of the main loop (in main.c):
(Flag500ms is an interrupt flag, Uart1SendString is a function that works, I know that I probably shouldn't send an Int as a string, but still need to find how to do this correctly)
- Code: Select all
if (Flag500ms == true){
Flag500ms = false;
Uart1SendString("Sampling...\r");
Batt_Raw = sampleBatt();
Uart1SendString("Value:\r");
Uart1SendString(Batt_Raw);
)
initADC (in hardware.c):
- Code: Select all
void initAdc (void){
/* Battery is connected through voltage divider (xxxR to batt, xxxR to GND) to pin RC1/AN11.
Pin is initialised in initPins function. */
/* ADC input */
_ANSC1 = 1;
_TRISC1 = 1;
AD1CON1bits.ADON = 0;
AD1CON1bits.ADSIDL = 0; //Continues operation in idle mode
AD1CON1bits.DMABM = 0; // Not used since not using DMA
AD1CON1bits.DMAEN = 0; // No extended features
AD1CON1bits.MODE12 = 1; // 12 bit operation
AD1CON1bits.FORM = 0; // Absolute decimal, unsigned, right justified
AD1CON1bits.SSRC = 0; // Clear SAMP bit to start conversion
AD1CON1bits.ASAM = 0; // Sample when SAMP is set manually
AD1CON1bits.SAMP = 0; // Don't sample yet
AD1CON1bits.DONE = 0; // no conversion yet
AD1CON2bits.PVCFG = 0; // Use AVdd as pos volt ref
AD1CON2bits.NVCFG0 = 0; // Use AVss as neg voltage ref
AD1CON2bits.OFFCAL = 0; // inputs are connected to normal inputs
AD1CON2bits.BUFREGEN = 0; // Buffer register is used as FIFO
AD1CON2bits.CSCNA = 0; // No scanning
AD1CON2bits.BUFS = 0; // Not used since BUFM = 0
AD1CON2bits.SMPI = 0; // Interrupt at completion of each sample (no interrupt needed)
AD1CON2bits.BUFM = 0; // Start filling buffer at ADC1BUF0
AD1CON2bits.ALTS = 0; // Always use Sample A
AD1CON3bits.ADRC = 0; // Clock derived from system clock
AD1CON3bits.PUMPEN = 0; // Charge pump is disabled
AD1CON3bits.SAMC = 0; // Auto sample is 0Tad (not used)
AD1CON3bits.ADCS = 0b10; // Tad = 4xTcy
//AD1CON4 is used to configure DMA
//AD1CON5 is used for Threshold detect
AD1CHS = 0xB; // select AN11/PORTC1 as POS inp, Vss as neg for CHAN A
AD1CON1bits.ADON = 1; // turn ADC ON
}
Function showing the issue (in hardware.c, called from main loop):
- Code: Select all
unsigned int sampleBatt( void ){
// #define FCY 8000000UL // Instruction cycle frequency, Hz - required for __delayXXX() to work
// #include <libpic30.h> // has __delay_ms() function
unsigned int wait, value = 0; // for for loop while sampling
AD1CON1bits.SAMP = 1; // start sampling
// __delay_ms(100);
for(wait=0; wait < 200; wait++){}; // give time to sample
AD1CON1bits.SAMP = 0; // stop sampling, start conversion
// Uart1SendString("Converting\r");
// __delay_ms(200);
while (!AD1CON1bits.DONE){} // wait for conversion to be finished
// for(wait=0; wait < 180000; wait++){}; // give time to sample
// __delay_ms(200);
value = ADC1BUF0;
Uart1SendString("Conversion finished\r");
return value;
}
So the issue is that the loop never gets past the
while (!AD1CON1bits.DONE){}
line. Watching the bit shows that it doesn't get set.
Replacing the line by __delay_ms(200); to force the function to continue results in a value of 0x00 in ADC1BUF0.
To me it seems like the conversion is not triggered, but I can't seem to find what I do wrong.