Skip to main content
cht35
Associate
January 29, 2016
Question

Multiple Channel ADC on STM32 L1 Discovery with CooCox

  • January 29, 2016
  • 15 replies
  • 2767 views
Posted on January 29, 2016 at 13:46

Hello STM users,

I have been thrown in 'at the deep' using an STM32L152 in a commercial product design. I have inherited some code which uses the CooCox CooIDE for development and an STM32 L1 discovery board which uses the same IC. I am trying to get 4 channels of ADC acquisition working, either polled or ideally DMA/Interrupt driven. I've been through a few days of trawling the web now but can't seem to find an example which works in this environment. Most of my problems stem from differences in target IC for the examples I have found online. This si my first outing with STM, I'm used to Microchip.

Does anyone have a straight up example of multiple channel ADC on the L1 discovery board that may be compiled under CooCox?

Warmest regards from cold Mid Wales,

Peter

my CoIDE version is Version: 1.7.8

#discovery-l1-adc-multiple
This topic has been closed for replies.

15 replies

Tesla DeLorean
Guru
January 29, 2016
Posted on January 29, 2016 at 14:41

This is a single up example, to do multiple, you select scan mode, tell it the number of channels, enumerate them with ranking.

[DEAD LINK /public/STe2ecommunities/mcu/Lists/STM32Discovery/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/STM32Discovery/STM32%20ADC%20DMA%20Circular%20Mode&FolderCTID=0x01200200770978C69A1141439FE559EB459D75800084C20D8867EAD444A5987D47BE638E0F&currentviews=225]https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Discovery/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/STM32Discovery/STM32%20ADC%20DMA%20Circular%20Mode&FolderCTID=0x01200200770978C69A1141439FE559EB459D75800084C20D8867EAD444A5987D47BE638E0F¤tviews=225 CooCox has historically not called SystemInit() properly, in CMSIS implementations it is normally called before main() and the runtime initialization. For things that expect clocks to be working as expected you might need to call SystemInit() as you enter main(), to use more general examples. I might well have other examples, but the stuff is reasonable portable. You'd have to address the pin configuration and channels, but something like this

/* ADC Configuration */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // Multiple channels
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // Continuously back-to-back
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 4;
ADC_Init(ADC1,&ADC_InitStructure);
/* ADC1 regular channel 1 for PA1 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_192Cycles);
/* ADC1 regular channel 2 for PA2 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_192Cycles);
/* ADC1 regular channel 3 for PA3 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_192Cycles);
/* ADC1 regular channel 4 for PA4 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 4, ADC_SampleTime_192Cycles);

Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
qwer.asdf
Senior
January 29, 2016
Posted on January 29, 2016 at 14:45

What library are you/they using? Standard Peripherals Library (

http://www.st.com/web/en/catalog/tools/PF257913

) or

http://www.st.com/web/en/catalog/tools/PF260821

or maybe something other? You can say it from the library file names, i.e. if it's stm32l1xx_adc.c then it's StdPeriph, if the filenames are like stm32l1xx_hal_adc.c then it's HAL.

In any case, you can find examples in the official packages of those libraries (linked above).

cht35
cht35Author
Associate
January 29, 2016
Posted on January 29, 2016 at 14:54

Thanks Clive I shall have a gander at this and see what gives.

cht35
cht35Author
Associate
January 29, 2016
Posted on January 29, 2016 at 14:56

The libraries I am using are the Std Periph ones. I'll take a look at the links you suggest thank you. Peter

cht35
cht35Author
Associate
January 29, 2016
Posted on January 29, 2016 at 16:17

Hello Clive,

I am having progress, I can see values changing when I ground each of the pins however the grounded pin doesn't always match the indicated value on my output.

Its very possible that I haven't got my head round the GPIO to Analog channel mapping.

From the STM32L15x datasheet I found mappings of ADC_IN10 to PC0 etc. and am attempting to acquire the analog values on PC0 - 3.

I can see where GPIO Pin number is mapped but am unclear as to whether the initial 

line in GPIO_Init:

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);

is where the Pin number is connected with the PCx ports rather than PAx for example.

Am I getting this part wrong?

Many thanks for your suggestions so far, I had the single acquire going already and had tried the multiple but neglected this line:

 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;

which seems to have sprung the multiple convert into life.

Here's my code so far in the quest:

#include ''stm32l1xx.h''

#include ''stm32l1xx_gpio.h''

#include ''stm32l1xx_rcc.h''

#include ''stm32l1xx_exti.h''

#include ''stm32l1xx_syscfg.h''

#include ''stm32l1xx_adc.h''

ADC_InitTypeDef ADC_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

__IO uint16_t ADCdata[4] = { 0, 0, 0, 0};

void GPIO_Config(void);

void ADC_Config(void);

int main()

{

 GPIO_Config();

      ADC_Config();

 while(1)

 {

 ADCdata[0] = ADC_GetConversionValue(ADC1);

 printf(''ADC0 IS %d   '',ADCdata[0]);

 ADCdata[1] = ADC_GetConversionValue(ADC1);

 printf(''ADC1 IS %d   '',ADCdata[1]);

 ADCdata[2] = ADC_GetConversionValue(ADC1);

 printf(''ADC2 IS %d   '',ADCdata[2]);

 ADCdata[3] = ADC_GetConversionValue(ADC1);

 printf(''ADC3 IS %d \n'',ADCdata[3]);

}

}

void GPIO_Config(void)

{

 /* Enable The HSI (16Mhz) */

 RCC_HSICmd(ENABLE);

 /* Enable the GPIOC Clock */

 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);

 /* Configure GPIO pins to Analog mode */

 GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 ;

 GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AN;

 GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;

 GPIO_Init(GPIOA, &GPIO_InitStructure);

 /* Check that HSI oscillator is ready */

 while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);

}

void ADC_Config(void)

{

  /* ADC1 Configuration ------------------------------------------------------*/

  /* Enable ADC1 clock */

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

  /* Setup ADC Structure */

  ADC_StructInit(&ADC_InitStructure);

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

  ADC_InitStructure.ADC_ScanConvMode = ENABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfConversion = 4;

  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC channels configuration */

  ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_192Cycles);

  ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_192Cycles);

  ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 3, ADC_SampleTime_192Cycles);

  ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 4, ADC_SampleTime_192Cycles);

  /* Enable ADC1 */

  ADC_Cmd(ADC1, ENABLE);

  /* Wait until ADC1 ON status */

  while (ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET)

  {

  }

  /* Start ADC1 Software Conversion */

  ADC_SoftwareStartConv(ADC1);

  /* Wait until ADC end of conversion */

  while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)

  {

  }

}

cht35
cht35Author
Associate
January 29, 2016
Posted on January 29, 2016 at 16:41

duh!  Guess who just spotted where the connection is - in the GPIO Init of couirse where I haveGPIOA should be GPIOC.

onward...
cht35
cht35Author
Associate
January 29, 2016
Posted on January 29, 2016 at 16:45

Here is the output I am getting at present with PC0 only grounded and the other pins floating, almost seems like the number of channels being converted isn't correct, or I am not interpreting the acquired data correctly, or my GPIO setup is still wrong.

ADC0 IS 2088   ADC1 IS 1809   ADC2 IS 1813   ADC3 IS 1204 

ADC0 IS 1191   ADC1 IS 1626   ADC2 IS 1822   ADC3 IS 1621 

ADC0 IS 0   ADC1 IS 1206   ADC2 IS 1219   ADC3 IS 0 

ADC0 IS 1   ADC1 IS 1622   ADC2 IS 0   ADC3 IS 1802 

ADC0 IS 1198   ADC1 IS 1816   ADC2 IS 1209   ADC3 IS 0 

ADC0 IS 1798   ADC1 IS 0   ADC2 IS 1625   ADC3 IS 1824 

ADC0 IS 1635   ADC1 IS 1   ADC2 IS 1213   ADC3 IS 1625 

ADC0 IS 0   ADC1 IS 1211   ADC2 IS 1213   ADC3 IS 1 

ADC0 IS 8   ADC1 IS 1214   ADC2 IS 1621   ADC3 IS 1621

Tesla DeLorean
Guru
January 29, 2016
Posted on January 29, 2016 at 17:23

You can't do this, you have to use DMA for multiple channels

ADCdata[0] = ADC_GetConversionValue(ADC1);
printf(''ADC0 IS %d '',ADCdata[0]);
ADCdata[1] = ADC_GetConversionValue(ADC1);
printf(''ADC1 IS %d '',ADCdata[1]);
ADCdata[2] = ADC_GetConversionValue(ADC1);
printf(''ADC2 IS %d '',ADCdata[2]);
ADCdata[3] = ADC_GetConversionValue(ADC1);
printf(''ADC3 IS %d 
'',ADCdata[3]);

And use the DMA interrupts to provide EOC, also the data is going to change faster than you can print it to a terminal. So you'd need to take a snap shot of the measurements, and then print them. Now if you need 4 measurements in a polled loop, don't use continuous mode, leave the DMA in circular, and then trigger the set of 4 conversions, and wait for DMA TC to flag (vs ADC EOC)
Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
Tesla DeLorean
Guru
January 29, 2016
Posted on January 29, 2016 at 17:25

See the complete example in the thread I cited, and merge in the second fragment in place of the single up configuration, along with the pin configurations.

Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
Tesla DeLorean
Guru
January 29, 2016
Posted on January 29, 2016 at 17:41

Quick blind 4 UP

// sourcer32@gmail.com - STM32L15x ADC DMA Demo (Four Channel, PA6,PA7,PB0 and PB1)
#include ''stm32l1xx.h''
// Thousand samples for each of the 4 channels, interrupting every 500 groups
#define SAMPLES (1000 * 4)
volatile uint16_t ADC_ConvertedValues[SAMPLES];
//******************************************************************************
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*!< 
At
this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
file (startup_stm32l1xx_xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32l1xx.c file
*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB, ENABLE);
/* Configure PB6 (BLUE LED) and PB7 (GREEN LED) */
GPIO_InitStructure.GPIO_Pin
= 
GPIO_Pin_6
| GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode
= 
GPIO_Mode_OUT
;
GPIO_InitStructure.GPIO_OType
= 
GPIO_OType_PP
;
GPIO_InitStructure.GPIO_PuPd
= 
GPIO_PuPd_NOPULL
;
GPIO_InitStructure.GPIO_Speed
= 
GPIO_Speed_2MHz
;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIOB->ODR &= ~GPIO_Pin_7;
GPIOB->ODR |= GPIO_Pin_6;
/* PA6 ADC_IN6 clash Linear Touch Sensor */
/* PA7 ADC_IN7 clash Linear Touch Sensor */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* PB0 ADC_IN8 clash Linear Touch Sensor */
/* PB1 ADC_IN9 clash Linear Touch Sensor */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable The HSI (16Mhz) */
RCC_HSICmd(ENABLE);
/* Check that HSI oscillator is ready */
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
/* ADC1 Configuration ------------------------------------------------------*/
/* Enable ADC1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* Deinitialize ADC */
ADC_DeInit(ADC1);
ADC_CommonStructInit(&ADC_CommonInitStructure);
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
ADC_CommonInit(&ADC_CommonInitStructure);
/* ADC Configuration */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // Multiple channels
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // Continuously back-to-back, not triggered
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; // Not triggered
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; // Not used, valid placeholder
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 4;
ADC_Init(ADC1,&ADC_InitStructure);
/* ADC1 regular channel 6 for PA6 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_192Cycles);
/* ADC1 regular channel 7 for PA7 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 2, ADC_SampleTime_192Cycles);
/* ADC1 regular channel 8 for PB0 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 3, ADC_SampleTime_192Cycles);
/* ADC1 regular channel 9 for PB1 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 4, ADC_SampleTime_192Cycles);
/* Enable ADC1 Power Down during Delay */
ADC_PowerDownCmd(ADC1, ADC_PowerDown_Idle_Delay, ENABLE);
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* DMA1 channel1 configuration */
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValues[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = SAMPLES;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA Stream Half / Transfer Complete interrupt */
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC | DMA_IT_HT, ENABLE);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
/* Enable the request after last transfer for DMA Circular mode */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Wait until ADC1 ON status */
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS) == RESET);
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConv(ADC1);
/* Infinite loop */
while(1);
}
//******************************************************************************
void DMA1_Channel1_IRQHandler(void)
{
/* Test on DMA Channel Half Transfer interrupt */
if (DMA_GetITStatus(DMA1_IT_HT1))
{
/* Clear DMA Channel Half Transfer interrupt pending bit */
DMA_ClearITPendingBit(DMA1_IT_HT1);
/* Toggle BLUE LED: Half Transfer */
GPIOB->ODR ^= GPIO_Pin_6; // Toggle
// Add code here to process first half of buffer (ping)
}
/* Test on DMA Channel Transfer Complete interrupt */
if (DMA_GetITStatus(DMA1_IT_TC1))
{
/* Clear DMA Channel Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA1_IT_TC1);
/* Toggle GREEN LED: Transfer Complete */
GPIOB->ODR ^= GPIO_Pin_7; // Toggle
// Add code here to process second half of buffer (pong)
}
}
//******************************************************************************
#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

'', file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
//******************************************************************************

Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..