0
\$\begingroup\$

I am new to the microcontroller world and am using a dsPIC33EP256MC506 controller and dsPICDEM -2 MCLV development board for a project. I am trying to read the value of the potentiometer that is on the dev board, but am having trouble. I just want to be able to run the program and move turn the potentiometer and see the program output the value. I know that I have to use ADC ports somehow to do this, but I'm not exactly sure how to use it. Documentation has not been very helpful on how to do this in the program either. If anyone can direct me to documentation that uses ADC to control the potentiometer or help me get started with this that would be great.

Also, does anyone know where this value will be displayed? It makes sense to me that it would be displayed in the Variables output tab, but no values seem to be populating for any variable.

Microcontroller: dsPIC33EP256MC506

Development Board: dsPICDEM -2 MCLV

Programmer/Debugger: REAL ICE

Compiler: MPLAB X - XC16

Thanks again!

#include <p33Exxxx.h>
/****************************CONFIGURATION****************************/

_FOSCSEL(FNOSC_FRC);
_FOSC(FCKSM_CSECMD & POSCMD_XT & OSCIOFNC_OFF & IOL1WAY_OFF);
_FWDT(FWDTEN_OFF);
_FPOR(ALTI2C1_ON & ALTI2C2_ON);
_FICD(ICS_PGD1 & JTAGEN_OFF);

void initAdc1(void);
void Delay_us(unsigned int);
int  ADCValue, i;
int  main(void)
{
// Configure the device PLL to obtain 40 MIPS operation. The crystal frequency is 8 MHz.
// Divide 8 MHz by 2, multiply by 40 and divide by 2. This results in Fosc of 80 MHz.
// The CPU clock frequency is Fcy = Fosc/2 = 40 MHz.
PLLFBD = 38;                    /* M  = 40 */
CLKDIVbits.PLLPOST = 0;         /* N1 = 2  */
CLKDIVbits.PLLPRE = 0;          /* N2 = 2  */
OSCTUN = 0;
/* Initiate Clock Switch to Primary Oscillator with PLL (NOSC = 0x3) */
__builtin_write_OSCCONH(0x03);
__builtin_write_OSCCONL(0x01);
while (OSCCONbits.COSC != 0x3);
while (_LOCK == 0);             /* Wait for PLL lock at 40 MIPS */
initAdc1();
while(1)
{
    AD1CON1bits.SAMP = 1;         // Start sampling
    Delay_us(10);                 // Wait for sampling time (10 us)
    AD1CON1bits.SAMP = 0;         // Start the conversion
    while (!AD1CON1bits.DONE);    // Wait for the conversion to complete
    ADCValue = ADC1BUF0;          // Read the ADC conversion result
}
}
    void initAdc1(void) {
     /* Set port configuration */ 
     ANSELA = ANSELB = ANSELC  = ANSELE = 0x0000;
    //ANSELBbits.ANSB5 = 1;           // Ensure AN5/RB5 is analog
    ANSELBbits.ANSB0 = 1;
    ANSELAbits.ANSA0 = 1;

// A0 and B0 are inputs
TRISAbits.TRISA0 = 1;
TRISBbits.TRISB0 = 1;

/* Initialize and enable ADC module */

AD1CON1 = 0x0000;
AD1CON2 = 0x0000;
AD1CON3 = 0x000F;
AD1CON4 = 0x0000;
AD1CHS0 = 0x0000;
// 0000 0000 0000 0101
AD1CHS123 = 0x0000;
AD1CSSH = 0x0000;
AD1CSSL = 0x0000;
AD1CON1bits.ADON = 1;
Delay_us(20);
}
void Delay_us(unsigned int delay)
{
for (i = 0; i < delay; i++)
{
    __asm__ volatile ("repeat #39");
    __asm__ volatile ("nop");
}
}
\$\endgroup\$
  • \$\begingroup\$ The dsPIC documentation breaks down into 3 categories. The data sheet which has all the specifics of the chip. The reference manual which covers the family of processor giving how things function but lacking the specific detail that the data sheet has. There is a reference manual that covers every different module inside. The final is example code that is provided on Microchips website. \$\endgroup\$ – vini_i Oct 30 '16 at 2:13
1
\$\begingroup\$

You are not waiting for conversion to complete.

Algorithm for ADC operations is as follows:

  1. Initialize ADC and Pin for Analog channel
  2. Start the conversion
  3. Wait for conversion to complete
  4. Read the result buffer when conversion is complete
  5. Go to step 3

In your case, you are not waiting or reading the result buffer. Datasheet is helpful enough to understand the registers.

\$\endgroup\$
1
\$\begingroup\$

First, you need to sit down and read the documentation. For someone new, the documentation for the A/D may take a couple hours to go over and digest. Realize up front it's going to take some investment of time. Shortcuts just cause you to waste much more time later, as you are seeing.

For the 16 bit chips (dsPIC and PIC 24), there are two parts to the documentation. The details of any peripheral are described in depth in the Family Reference Manual chapter for that peripheral. Go to the product page for the particular part you are using to get the FRM chapter that applies to the particular version of the A/D your chip has. Read that. Completely. No shortcuts.

The datasheet for the part then gives the particular parameters for the A/D that was described generally in the FRM. This includes how many A/Ds the part has, what the minimum time for TAD is, how many and which analog channels are implemented, etc.

Now that you understand the A/D hardware, you think about how to use it within your application. These peripherals are very flexible, so any one implementation will only use a subset of the possible capabilities or modes.

Since you just want to get A/D readings from foreground code on request (not a good long term strategy, but acceptable for a quick demo), set up the A/D to auto-sample after conversion completed. That way you only need to set one bit to get the next reading. To get each reading:

  1. Make sure enough time has elapsed since the previous conversion for a sufficiently long sample time.

  2. Set the bit to start a new conversion.

  3. Wait for the conversion to finish.

  4. Read the A/D result.

Don't ever do this:

AD1CON1 = 0x0000;
AD1CON2 = 0x0000;
AD1CON3 = 0x000F;
AD1CON4 = 0x0000;
AD1CHS0 = 0x0000;
// 0000 0000 0000 0101
AD1CHS123 = 0x0000;
AD1CSSH = 0x0000;
AD1CSSL = 0x0000;

Hard coding values like this without any indication what they mean is grossly irresponsible programming. Not only are these just numbers without any hint what the bits mean, but here isn't even a comment on the assignment lines! The one comment there is just gives a number without context and makes no sense.

Taking shortcuts with code commenting, just like shortcuts reading the documentation, is false economy. Do it right. It will save you time. And, asking others to look at undocumented code is just plain rude.

\$\endgroup\$
0
\$\begingroup\$

You also need to detect when the data is ready for reading, i.e. when the ADC Interrupt Flag tells you. You either have a custom interrupt handler of the form void _ISR _NOPSV _ADC1Interrupt(void), or you wait for the flag to be set in main(), for example if(_AD1IF){ /read from buffer/}.

It looks like you don't have an interrupt enabled, which would go along the lines _ADC1IE = 1;. If I had more time I could help further, best place to start is with the Datasheet, there will be specific datasheets for ADC.

\$\endgroup\$
  • \$\begingroup\$ Thanks Mike, really appreciate you taking the time to help out. I updated my code with what I've been working on. I can read the value of the analog pin by setting a voltage. However, I can only do this in simulator. How can I connect this analog pin to the potentiometer? Do you know how I can give the potentiometer on the physical board voltage so that I can run on the board and see pot value when I move the knob? Thanks again for your support. \$\endgroup\$ – KP123 Oct 31 '16 at 17:40
  • \$\begingroup\$ @KP123 To safely connect the potentiometer to the analogue pin, you need to know the maximum input voltage for the ADC. If it is the same as the supply voltage for the board then you can use V+ connected to an outer pin of the pot, 0V connected the other outer pin of the pot, and the variable voltage will be present on the centre pin of the pot. I suggest using at least a 10 kOhm pot as there will be current flowing through it all the time that the circuit is powered. \$\endgroup\$ – Andrew Morton Oct 31 '16 at 21:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.