ADC1 configuration

ChibiOS public support forum for topics related to the STMicroelectronics STM32 family of micro-controllers.

Moderator: RoccoMarco

User avatar
Nemesis2065
Posts: 20
Joined: Fri Nov 15, 2013 9:53 am

ADC1 configuration

Post by Nemesis2065 »

Hello,

I am currently working on the ADC1 on the STM32F4 discovery board. The goal is to get the temp info of the board on ADC1 and also connect some infrared telemeters (Sharp GP2D12).
For this I want to use the ADC1 : PC1 for the internal temp sensor and PC2, PC3, PC4 for the telemeters.

For test purpose, I want to put the RED led "ON" when the value on these pins is greater than 1500mV.
The problem is when I put 3V on the pins PC2, PC3 or PC4, it doesn't put the RED led "ON".
I send also the values on the Uart, but have some random values of the order of 40mV. (even if the pins are connected or not to 3V)

I tried different solutions from different examples, but still without success :
https://github.com/mabl/ChibiOS/blob/master/demos/ARMCM4-STM32F407-DISCOVERY/main.c
https://github.com/Mathias-L/STM32F4-Discovery-example-code/blob/master/myADC.c
https://github.com/nt4t/Chibios-STM32F051-DISCOVERY-adc-shell-example/blob/master/main.c

Could you tell me if my configuration is correct ?

My Eclipse project is modified compared to the ChibiOS project from the Demo. I have 2 files for the ADC config :

adc_cfg.c :

Code: Select all

adcsample_t ADC1_Samples[ADC_GRP1_NUM_CHANNELS * ADC_GRP1_BUF_DEPTH];

const ADCConversionGroup ADC1_grpcfg =
{
   FALSE,                              // set to TRUE if need circular buffer, set FALSE otherwise
   ADC_GRP1_NUM_CHANNELS,               // number of channels
   ADC1_Cbk,                            // callback function
   NULL,                               // CR1 register content
   // HW dependent part
   0,                                  // CR2 register content
   ADC_CR2_SWSTART,                      // SMRP1 register content
   ADC_SMPR1_SMP_SENSOR(ADC_SAMPLE_56) | ADC_SMPR1_SMP_AN12(ADC_SAMPLE_56) | ADC_SMPR1_SMP_AN13(ADC_SAMPLE_56) | ADC_SMPR1_SMP_AN14(ADC_SAMPLE_56),
   ADC_SQR1_NUM_CH(ADC_GRP1_NUM_CHANNELS),    // SQR1 register content. Set channel sequence length
   0,                                  // SQR2 register content
   ADC_SQR3_SQ1_N(ADC_CHANNEL_SENSOR) | ADC_SQR3_SQ2_N(ADC_CHANNEL_IN12) | ADC_SQR3_SQ3_N(ADC_CHANNEL_IN13) | ADC_SQR3_SQ4_N(ADC_CHANNEL_IN14)
};


adc_cfg.h :

Code: Select all

/****************************************************************************************************
 * DEFINES
 ****************************************************************************************************/
// Total number of channels to be sampled by a single ADC operation.
#define ADC_GRP1_NUM_CHANNELS   4

// Depth of the conversion buffer : channels are sampled four times each.
#define ADC_GRP1_BUF_DEPTH      4

/****************************************************************************************************
 * EXTERN
 ****************************************************************************************************/
extern const ADCConversionGroup ADC1_grpcfg;
extern adcsample_t ADC1_Samples[];

/****************************************************************************************************
 * PROTOTYPES
 ****************************************************************************************************/
void ADC1_Cbk(ADCDriver *adcp, adcsample_t *buffer, size_t n);



Then I have an initialization function called at the beginning of the main :
init.c :

Code: Select all

void Init_Drivers(void)
{
   halInit();
   chSysInit();

   /****************************************************************************************************
   * Initializes the ADC driver 1 for pins PC1,2,3,4 and enable the thermal sensor.
   ****************************************************************************************************/
   adcStart(&ADCD1, NULL);
   adcSTM32EnableTSVREFE();
   palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);      // IN11  [PC1]
   palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);      // IN12  [PC2]
   palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);      // IN13  [PC3]
   palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG);      // IN14  [PC4]
}


main.c :

Code: Select all

int main(void)
{
   Init_Drivers();

   /***** Threads Creation *************************************************************************/
   chThdCreateStatic(wa_OSRunning, sizeof(wa_OSRunning), NORMALPRIO, Thread_OSRunning, NULL);
   chThdCreateStatic(wa_Accelerometer, sizeof(wa_Accelerometer), NORMALPRIO, Thread_Accelerometer, NULL);
   chThdCreateStatic(wa_ADC1, sizeof(wa_ADC1),  NORMALPRIO, Thread_ADC1, NULL);
   /************************************************************************************************
}


For the ADC acquisition I use a thread and then the ADC callback function to handle the ADC samples (in main.c) :

Code: Select all

static WORKING_AREA(wa_ADC1,         128);

static msg_t Thread_ADC1(void *arg)
{
   UNUSED(arg);
   chRegSetThreadName("ADC1");

   while (TRUE)
   {
      chSysLockFromIsr();
      adcConvert(&ADCD1, &ADC1_grpcfg, ADC1_Samples, ADC_GRP1_BUF_DEPTH);
      chSysUnlockFromIsr();

      chprintf((BaseSequentialStream*) &SD3, "PC1 value : %d mV \r\n", ADC1_Samples[0]);
      chprintf((BaseSequentialStream*) &SD3, "PC2 value : %d mV \r\n", ADC1_Samples[1]);
      chprintf((BaseSequentialStream*) &SD3, "PC3 value : %d mV \r\n", ADC1_Samples[2]);
      chprintf((BaseSequentialStream*) &SD3, "PC4 value : %d mV \r\n", ADC1_Samples[3]);
   }
   return 0;
}

void ADC1_Cbk(ADCDriver *adcp, adcsample_t *buffer, size_t n)
{
   UNUSED(buffer);
   UNUSED(n);

   adcsample_t avg_PC1, avg_PC2, avg_PC3, avg_PC4;
   adcsample_t PC1_mV, PC2_mV, PC3_mV, PC4_mV;

   /* Note, only in the ADC_COMPLETE state because the ADC driver fires an   intermediate callback when the buffer is half full.*/
   if (adcp->state == ADC_COMPLETE)
   {
                // Calculates the average values from the ADC samples
      avg_PC1 = (ADC1_Samples[0] + ADC1_Samples[4] + ADC1_Samples[8]  + ADC1_Samples[12]) / 4;      // temp sensor
      avg_PC2 = (ADC1_Samples[1] + ADC1_Samples[5] + ADC1_Samples[9]  + ADC1_Samples[13]) / 4;
      avg_PC3 = (ADC1_Samples[2] + ADC1_Samples[6] + ADC1_Samples[10] + ADC1_Samples[14]) / 4;
      avg_PC4 = (ADC1_Samples[3] + ADC1_Samples[7] + ADC1_Samples[11] + ADC1_Samples[15]) / 4;

      // Conversion in volt
      PC1_mV = (avg_PC1/4095) * 3300;
      PC2_mV = (avg_PC2/4095) * 3300;
      PC3_mV = (avg_PC3/4095) * 3300;
      PC4_mV = (avg_PC4/4095) * 3300;

      if ( (PC1_mV > 1500) || (PC2_mV > 1500) || (PC3_mV > 1500) || (PC4_mV > 1500) )
      {
         palSetPad(GPIOD, LED_RED);
      }
      else
      {
         palClearPad(GPIOD, LED_RED);
      }
   }
}


Thanks in advance for your help !
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: ADC1 configuration

Post by Giovanni »

Hi,

I see one obvious error:

Code: Select all

      chSysLockFromIsr();
      adcConvert(&ADCD1, &ADC1_grpcfg, ADC1_Samples, ADC_GRP1_BUF_DEPTH);
      chSysUnlockFromIsr();


You are not in ISR context. This makes me think that you are not running with debug options enabled or this error would have been caught. Please enable all the debug support in chconf.h and look for errors.

I recommend you read the "Kernel Concepts" chapter in the documentation, especially the parts regarding the system states and the call restrictions.

Giovanni
User avatar
Nemesis2065
Posts: 20
Joined: Fri Nov 15, 2013 9:53 am

Re: ADC1 configuration

Post by Nemesis2065 »

Hi,

I have now corrected this :

Code: Select all

static msg_t Thread_ADC1(void *arg)
{
   UNUSED(arg);
   chRegSetThreadName("ADC1");

   while (TRUE)
   {
      adcConvert(&ADCD1, &ADC1_grpcfg, ADC1_Samples, ADC_GRP1_BUF_DEPTH);
               
      palClearPad(GPIOD, LED_GREEN);
      chThdSleepMilliseconds(300);
   }
   return 0;
}


Is it a correct way to call the function adcconvert in a thread, and then work on the sample in the callback, or I can do it directly in the thread ?
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: ADC1 configuration

Post by Giovanni »

You can do both things, use callbacks or wait adcConvert() to complete then process the data in the thread.

If you use the synchronous functions usually it is because you don't need callbacks, unless it is a circular conversion where, of course, callbacks are mandatory in order to process the conversion stream.

Giovanni
User avatar
Nemesis2065
Posts: 20
Joined: Fri Nov 15, 2013 9:53 am

Re: ADC1 configuration

Post by Nemesis2065 »

Hi,

I have reviewed all my ADC code but it's still not working.
I have modified the Thread for the ADC and I am not using the ADC callback :

Code: Select all

static msg_t Thread_ADC1(void *arg)
{
   UNUSED(arg);
   chRegSetThreadName("ADC1");

   while (TRUE)
   {
      adcConvert(&ADCD1, &ADC1_grpcfg, ADC1_Samples, ADC_GRP1_BUF_DEPTH);
      //adcStartConversion(&ADCD1, &ADC1_grpcfg, ADC1_Samples, ADC_GRP1_BUF_DEPTH);

      // Calculates the average values from the ADC samples
      g_avg_PC1 = (ADC1_Samples[0] + ADC1_Samples[4] + ADC1_Samples[8]  + ADC1_Samples[12]) / 4;
      g_avg_PC2 = (ADC1_Samples[1] + ADC1_Samples[5] + ADC1_Samples[9]  + ADC1_Samples[13]) / 4;
      g_avg_PC3 = (ADC1_Samples[2] + ADC1_Samples[6] + ADC1_Samples[10] + ADC1_Samples[14]) / 4;
      g_avg_PC4 = (ADC1_Samples[3] + ADC1_Samples[7] + ADC1_Samples[11] + ADC1_Samples[15]) / 4;

      // Conversion in volt
      g_PC1_mV = (g_avg_PC1/4095) * 3300;
      g_PC2_mV = (g_avg_PC2/4095) * 3300;
      g_PC3_mV = (g_avg_PC3/4095) * 3300;
      g_PC4_mV = (g_avg_PC4/4095) * 3300;

      palSetPad(GPIOD, LED_GREEN);
      chThdSleepMilliseconds(250);
      palClearPad(GPIOD, LED_GREEN);
      chThdSleepMilliseconds(250);
   }
   return 0;
}


I'm running another thread only for the chprintf function for no having problem inside the ADC function :

Code: Select all

 static msg_t Thread_Debug_Printf(void *arg)
{
   UNUSED(arg);
   chRegSetThreadName("Debug_Printf");

   while (TRUE)
   {
      if(g_EnablePrintf == TRUE)
      {
         chprintf((BaseSequentialStream*) &SD3, "Sample[0]= %d - avg_PC1= %d - PC1_mV= %d \r\n", ADC1_Samples[0], g_avg_PC1, g_PC1_mV);
         chprintf((BaseSequentialStream*) &SD3, "Sample[1]= %d - avg_PC2= %d - PC2_mV= %d \r\n", ADC1_Samples[1], g_avg_PC2, g_PC2_mV);
         chprintf((BaseSequentialStream*) &SD3, "Sample[2]= %d - avg_PC3= %d - PC3_mV= %d \r\n", ADC1_Samples[2], g_avg_PC3, g_PC3_mV);
         chprintf((BaseSequentialStream*) &SD3, "Sample[3]= %d - avg_PC4= %d - PC4_mV= %d \r\n", ADC1_Samples[3], g_avg_PC4, g_PC4_mV);

         palSetPad(GPIOD, LED_BLUE);
         chThdSleepMilliseconds(500);
         palClearPad(GPIOD, LED_BLUE);
         chThdSleepMilliseconds(500);
      }
   }
   return 0;
}


I kept the same configuration and the same init as the first post.
But I am getting this :
Image

I really do not understand my mistake, is my configuration correct ?
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: ADC1 configuration

Post by Giovanni »

Are pins configured in analog mode?

Giovanni
User avatar
Nemesis2065
Posts: 20
Joined: Fri Nov 15, 2013 9:53 am

Re: ADC1 configuration

Post by Nemesis2065 »

Yes, I do it during the initialization :

Code: Select all

/****************************************************************************************************
* Initializes the ADC driver 1 for pins PC1,2,3,4 and enable the thermal sensor.
****************************************************************************************************/

adcStart(&ADCD1, NULL);
adcSTM32EnableTSVREFE();
palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);      // IN11  [PC1]
palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);      // IN12  [PC2]
palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);      // IN13  [PC3]
palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG);      // IN14  [PC4]
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: ADC1 configuration

Post by Giovanni »

Try connecting your pins to VDD/GND and verify if the values are correct (~0, ~4095).

Giovanni
Djuro
Posts: 3
Joined: Fri Oct 26, 2012 4:57 pm

Re: ADC1 configuration

Post by Djuro »

In these mathematical operation: g_PC1_mV = (g_avg_PC1/4095) * 3300; take care about numeric type data (int, double, long, float).
Unappropriated int,double,long value in dividend will result in lost part or full value in result.
User avatar
Nemesis2065
Posts: 20
Joined: Fri Nov 15, 2013 9:53 am

Re: ADC1 configuration

Post by Nemesis2065 »

I already tried to connect to VDD/GND but I have the same "random" values (see image)
g_PC1_mV and g_avg_PC1 are float type, so this should be ok.
Post Reply