February 28, 2024, 06:28:50 PM

STM32-F103 problems with ADC and analog input

Started by minaise, May 11, 2015, 09:17:38 AM

Previous topic - Next topic

minaise

Hello
I have started to look into ARM and STM32 world and from ATMEGA it is very eyes opening to see how things really are working. I am playing with STM32F103 and using Eclipse ARM GCC OpenOCD toolchain.
I managed to control digital inputs and outputs and there is no problem with those but I am really struggling with analog inputs. I would just like to read one or several potentiometers values from AI but I am not able to do that. I have tried many samples (single- and multichannel) which could be found on internet and I think that generally I understand AI and ADC working principles but there are some issues.
I have included stm32f10x.h, stm32f10x_conf.h, stm32f10x_adc.h headers and it seems that I have correct paths configured but with some ADC functions the code just isn't compiling. For example ADC_ResetCalibration(ADC1) and other calibration functions but mainly ADC_DeInit(), ADC_Init(ADC1, &ADC_InitStructure) and ADC_Cmd(ADC1, ENABLE) are all giving undefined reference error. However when I comment those out, other ADC functions are accepted and code is compiling.
Hopefully I wrote it under correct topic. I have been dealing with it for so long time and I am getting out of ideas, what to try. Could somebody please give a hint, what should I do?

Stanimir5F

#1
Hello!

You said that you are using STM32F103 but I can't find anything about the board, so I tested the example with STM32-P103 rev.C. And also you mention Eclipse ARM GCC OpenOCD, so I'm not sure if this is going to be useful for you since I use IAR for ARM 6.30.7. So keep in mind that this code may not work due to differences in the libraries, so maybe you should edit it a little bit, replace some of the functions with different names etc.
This is only the main.c file:

/**
  ******************************************************************************
  * @file    ADC/ADC1_DMA/main.c
  * @author  MCD Application Team
  * @version V3.1.2
  * @date    09/28/2009
  * @brief   Main program body
  ******************************************************************************
  * @copy
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2009 STMicroelectronics</center></h2>
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>

/** @addtogroup STM32F10x_StdPeriph_Examples
  * @{
  */

/** @addtogroup ADC_ADC1_DMA
  * @{
  */


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ADC1_DR_Address    ((uint32_t)0x4001244C)

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
ADC_InitTypeDef ADC_InitStructure;
__IO uint16_t ADCConvertedValue;
ErrorStatus HSEStartUpStatus;
   
/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
 
/* Private functions ---------------------------------------------------------*/

/**
  * @brief   Main program
  * @param  None
  * @retval None
  */
int main(void)
{
  /* System clocks configuration ---------------------------------------------*/
  RCC_Configuration();

  /* GPIO configuration ------------------------------------------------------*/
  GPIO_Configuration();

  /* ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC1 regular channelX configuration */
  // here you choose the specific analog channel
  ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_55Cycles5);

  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);

  /* Enable ADC1 reset calibaration register */   
  ADC_ResetCalibration(ADC1);
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));

  /* Start ADC1 calibaration */
  ADC_StartCalibration(ADC1);
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));
     
  /* Start ADC1 Software Conversion */
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

  while (1)
  {
     int Value = ADC_GetConversionValue (ADC1);
     printf ("%d (%.2f%%)\n", Value, (float)Value*100.0/4095);
     for (volatile long int i=4000000; i; --i); // simple delay
  }
}

/**
  * @brief  Configures the different system clocks.
  * @param  None
  * @retval None
  */
void RCC_Configuration(void)
{
    /* RCC system reset(for debug purpose) */
  RCC_DeInit();

  /* Enable HSE */
  RCC_HSEConfig(RCC_HSE_ON);

  /* Wait till HSE is ready */
  HSEStartUpStatus = RCC_WaitForHSEStartUp();

  if(HSEStartUpStatus == SUCCESS)
  {
    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    /* Flash 2 wait state */
    FLASH_SetLatency(FLASH_Latency_2);
 
    /* HCLK = SYSCLK */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);
 
    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1);

    /* PCLK1 = HCLK/2 */
    RCC_PCLK1Config(RCC_HCLK_Div2);

    /* ADCCLK = PCLK2/4 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div4);
 
#ifndef STM32F10X_CL 
    /* PLLCLK = 8MHz * 7 = 56 MHz */
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7);

#else
    /* Configure PLLs *********************************************************/
    /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
    RCC_PREDIV2Config(RCC_PREDIV2_Div5);
    RCC_PLL2Config(RCC_PLL2Mul_8);

    /* Enable PLL2 */
    RCC_PLL2Cmd(ENABLE);

    /* Wait till PLL2 is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET)
    {}

    /* PLL configuration: PLLCLK = (PLL2 / 5) * 7 = 56 MHz */
    RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2, RCC_PREDIV1_Div5);
    RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_7);
#endif

    /* Enable PLL */
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while(RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }

/* Enable peripheral clocks --------------------------------------------------*/
  /* Enable DMA1 clock */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

  /* Enable ADC1 and GPIOC clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);
}

/**
  * @brief  Configures the different GPIO ports.
  * @param  None
  * @retval None
  */
void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Configure PC.04 (ADC Channel14) as analog input -------------------------*/
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
}

#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

/**
  * @}
  */

/**
  * @}
  */

/******************* (C) COPYRIGHT 2009 STMicroelectronics *****END OF FILE****/
May the Source be with You!

minaise

Hello Stanimir5F!

Thank you very much for the reply and the example!
It is very similar to samples which could be found on internet but still the issue remains, I have included even stm32f10x_adc.h but still when I start to compile the code, I will receive undefined reference to ADC_Cmd, ADC_GetCalibrationStatus, ADC_GetConversionValue, ADC_Init and other (total 9) ADC related functions.
I am getting out of ideas, why. (:

Stanimir5F

All the functions you mention are defined in the file "stm32f10x_adc.c", but once again this is for IAR... I don't know what is the name of the file (or functions) in Eclipse ARM GCC OpenOCD. I can give you the code of this file but then I think another error will appear due to the difference of the library. Maybe it's better to look examples made specifically for Eclipse.
May the Source be with You!

JohnS

You don't give an actual very short C sample that fails.  You also don't give the actual output showing what you did and what the errors are.  I think you could help yourself by doing both.  To me it's not even clear if you have compiler or linker errors.

John

minaise

Thank you both for the answers!
Yes, off course I will copy the code and output, I am total novice in the ARM field and I have put the tool-chain together with the tutorials and recommendations from internet.
For the code I took Stanimir5F sample and simplified it a bit:

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>
#include "stm32f10x_adc.h"


/** @addtogroup STM32F10x_StdPeriph_Examples
  * @{
  */

/** @addtogroup ADC_ADC1_DMA
  * @{
  */


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ADC1_DR_Address    ((uint32_t)0x4001244C)

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
ADC_InitTypeDef ADC_InitStructure;
__IO uint16_t ADCConvertedValue;
ErrorStatus HSEStartUpStatus;

/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief   Main program
  * @param  None
  * @retval None
  */
int main(void)
{
  /* System clocks configuration ---------------------------------------------*/
  RCC_Configuration();

  /* GPIO configuration ------------------------------------------------------*/
  GPIO_Configuration();

  /* ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC1 regular channelX configuration */
  // here you choose the specific analog channel
  ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_55Cycles5);

  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);

  /* Enable ADC1 reset calibaration register */
  ADC_ResetCalibration(ADC1);
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));

  /* Start ADC1 calibaration */
  ADC_StartCalibration(ADC1);
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));

  /* Start ADC1 Software Conversion */
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

  while (1)
  {
     int Value = ADC_GetConversionValue (ADC1);
  }
}

/**
  * @brief  Configures the different system clocks.
  * @param  None
  * @retval None
  */
void RCC_Configuration(void)
{
    /* RCC system reset(for debug purpose) */
  RCC_DeInit();

  /* Enable HSE */
  RCC_HSEConfig(RCC_HSE_ON);

  /* Wait till HSE is ready */
  HSEStartUpStatus = RCC_WaitForHSEStartUp();

  if(HSEStartUpStatus == SUCCESS)
  {
    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    /* Flash 2 wait state */
    FLASH_SetLatency(FLASH_Latency_2);

    /* HCLK = SYSCLK */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1);

    /* PCLK1 = HCLK/2 */
    RCC_PCLK1Config(RCC_HCLK_Div2);

    /* ADCCLK = PCLK2/4 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div4);

#ifndef STM32F10X_CL
    /* PLLCLK = 8MHz * 7 = 56 MHz */
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7);

#else
    /* Configure PLLs *********************************************************/
    /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
    RCC_PREDIV2Config(RCC_PREDIV2_Div5);
    RCC_PLL2Config(RCC_PLL2Mul_8);

    /* Enable PLL2 */
    RCC_PLL2Cmd(ENABLE);

    /* Wait till PLL2 is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET)
    {}

    /* PLL configuration: PLLCLK = (PLL2 / 5) * 7 = 56 MHz */
    RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2, RCC_PREDIV1_Div5);
    RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_7);
#endif

    /* Enable PLL */
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while(RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }

/* Enable peripheral clocks --------------------------------------------------*/
  /* Enable DMA1 clock */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

  /* Enable ADC1 and GPIOC clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);
}

/**
  * @brief  Configures the different GPIO ports.
  * @param  None
  * @retval None
  */
void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Configure PC.04 (ADC Channel14) as analog input -------------------------*/
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
}

#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif


And the console is giving me this sort of errors:

09:36:59 **** Build of configuration Default for project test ****
make all
".compiling"
arm-none-eabi-gcc -Wall -fno-common -c -g -mcpu=cortex-m3 -mthumb -g -O0 -I./ -I./lib/ -I./lib/inc/ -I./lib/inc/../../ -DTRACE_LEVEL=4 -o obj/main.o main.c
main.c: In function 'main':
main.c:76:10: warning: unused variable 'Value' [-Wunused-variable]
      int Value = ADC_GetConversionValue (ADC1);
          ^
arm-none-eabi-gcc -Wall -fno-common -c -g -mcpu=cortex-m3 -mthumb -g -O0 -I./ -I./lib/ -I./lib/inc/ -I./lib/inc/../../ -DTRACE_LEVEL=4 -o obj/system_stm32f10x.o system_stm32f10x.c
arm-none-eabi-gcc -Wall -fno-common -c -g -mcpu=cortex-m3 -mthumb -g -O0 -I./ -I./lib/ -I./lib/inc/ -I./lib/inc/../../ -DTRACE_LEVEL=4 -o obj/stm32f10x_it.o stm32f10x_it.c
".compiling libraries"
arm-none-eabi-gcc -Wall -fno-common -c -g -mcpu=cortex-m3 -mthumb -g -O0 -I./ -I./lib/ -I./lib/inc/ -I./lib/inc/../../ -DTRACE_LEVEL=4 -o obj/stm32f10x_gpio.o ./lib/src/stm32f10x_gpio.c
arm-none-eabi-gcc -Wall -fno-common -c -g -mcpu=cortex-m3 -mthumb -g -O0 -I./ -I./lib/ -I./lib/inc/ -I./lib/inc/../../ -DTRACE_LEVEL=4 -o obj/stm32f10x_rcc.o ./lib/src/stm32f10x_rcc.c
".assembling"
arm-none-eabi-as -g -mapcs-32 -o obj/startup_stm32f10x_md.o ./lib/startup_stm32f10x_md.s
"..linking"
arm-none-eabi-ld -g -v -nostartfiles -Map ./main.map -T./lib/stm32_flash.ld -o ./main.out obj/main.o obj/system_stm32f10x.o obj/stm32f10x_gpio.o obj/stm32f10x_rcc.o obj/stm32f10x_it.o obj/startup_stm32f10x_md.o libgcc.a
GNU ld (GNU Tools for ARM Embedded Processors) 2.24.0.20141128
obj/main.o: In function `main':
C:\ARMT\Projects\test/main.c:52: undefined reference to `ADC_Init'
C:\ARMT\Projects\test/main.c:56: undefined reference to `ADC_RegularChannelConfig'
C:\ARMT\Projects\test/main.c:59: undefined reference to `ADC_Cmd'
C:\ARMT\Projects\test/main.c:62: undefined reference to `ADC_ResetCalibration'
C:\ARMT\Projects\test/main.c:64: undefined reference to `ADC_GetResetCalibrationStatus'
C:\ARMT\Projects\test/main.c:67: undefined reference to `ADC_StartCalibration'
C:\ARMT\Projects\test/main.c:69: undefined reference to `ADC_GetCalibrationStatus'
C:\ARMT\Projects\test/main.c:72: undefined reference to `ADC_SoftwareStartConvCmd'
C:\ARMT\Projects\test/main.c:76: undefined reference to `ADC_GetConversionValue'
obj/main.o: In function `RCC_Configuration':
C:\ARMT\Projects\test/main.c:99: undefined reference to `FLASH_PrefetchBufferCmd'
C:\ARMT\Projects\test/main.c:102: undefined reference to `FLASH_SetLatency'
make: *** [main.out] Error 1

09:37:04 Build Finished (took 5s.383ms)

JohnS

Read the code to find what's wrong with main.c

You need to compile & link the adc C code, as the other lib/src files are.

John

minaise

It seems that I am blind and just can't see the cause of the problem because of being stuck with it for a long time.
I have read the main.c file and includes part several times. I have checked that I have stm32f10x_adc.h file in the project under lib/inc. Also that I have correct directories (project bath, /lib and /lib/inc) in project paths and symbols properties.
Unfortunately I don't understand what you mean by "compile & link the adc C code". By doing all the previous, it should be linked and compiled or am I missing something?

JohnS

#8
Look at what you posted.  See the lib/src/... things?  You need one for adc as well.

You might like to start with some basic tutorials about using make files, gcc (not for STM32, just using it at all), what compiling and linking are and so on.

John

minaise

JohnS, thank you very much for your answer and suggestion!
Being a self-learning dumb-fu.. I didn't know that I have to modify make file myself also and add all headers there manually. So now ADC and analog input is functioning nicely! 

JohnS