So it turns out that ADCXX_INYY, means that XX - the type of ADC's that you can configure to (i.e. 12 = can configure to ADC1 or ADC2). YY represents that GPIO channel where you can configure ADC.
Also, you need to do ADC_CommonInitStructure only once.
Here is the new and improved version of the code for further convenience.
static volatile Int32U TimingDelay;
int i;
__IO Int16U Adcdata[3];
__IO float Adc1=0;
float Adc1_avg=0;
__IO float Adc2=0;
float Adc2_avg=0;
__IO float Adc3=0;
float Adc3_avg=0;
/**************************************************************************************/
void RCC_Configuration(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOE, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2 | RCC_APB2Periph_ADC3, ENABLE);
}
/**************************************************************************************/
void DelayResolution100us(Int32U Dly)
{
TimingDelay = Dly;
while(TimingDelay != 0);
}
/**
* @brief Decrements the TimingDelay variable.
* @param None
* @retval None
*/
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* ADC Channel 8 -> PB0 ADC12_IN8
ADC Channel 15 -> PC5 ADC12_IN15
ADC Channel 0 -> PA0 ADC123_IN0
*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/**************************************************************************************/
void ADC_Configuration(void)
{
ADC_DeInit();
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
/* ADC Common Init - COMMON - Do it once, do it first */
ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_RegSimult;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; // 2 half-words one by one, 1 then 2 then 3
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_6Cycles; //6 or 5?
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 1 Channel - why is it DISABLE?
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // Continuous Conversions
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
//ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // Ignored - why include it if ignored?
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure); // Mirror on ADC2
ADC_Init(ADC3, &ADC_InitStructure); // Mirror on ADC3
/* ADC3 regular channel 8 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_56Cycles); // PB0
/* ADC2 regular channel 5 configuration */
ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 1, ADC_SampleTime_56Cycles); // PC5
/* ADC3 regular channel 0 configuration */
ADC_RegularChannelConfig(ADC3, ADC_Channel_0, 1, ADC_SampleTime_56Cycles); // PA0
/* Enable DMA request after last transfer (Multi-ADC mode) */
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC2 */
ADC_Cmd(ADC2, ENABLE);
/* Enable ADC3 */
ADC_Cmd(ADC3, ENABLE);
ADC_DMACmd(ADC1, ENABLE); //enable ADC1 DMA since ADC1 is the master
}
/**************************************************************************************/
static void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA2_Stream0); //Set DMA registers to default values
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&Adcdata;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)0x40012308; // CDR_ADDRESS; Packed ADC1, ADC2
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 3; // Count of 16-bit words
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_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);
}
/**************************************************************************************/
int main(void)
{
ENTR_CRT_SECTION();
if(SysTick_Config(SystemCoreClock/10000))
{
while (1);
}
EXT_CRT_SECTION();
RCC_Configuration();
GPIO_Configuration();
ADC_Configuration();
DMA_Configuration();
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConv(ADC1);
/*Turn on Backlight*/
GLCD_Backlight(BACKLIGHT_ON);
GLCD_SetFont(&Terminal_9_12_6,0x000F00,0x00FF0);
GLCD_SetWindow(10,116,131,131);
/*-------------------Display Readings-----------------------------------------*/
GLCD_PowerUpInit((pInt8U)IAR_Logo.pPicStream);
GLCD_Backlight(BACKLIGHT_ON);
while(1){
/***** Compute Gain and Phase ****/
Adc3_avg = 0;
Adc2_avg = 0;
Adc1_avg = 0;
DelayResolution100us(10000);
for(i=1;i<=20;i++)
{
DelayResolution100us(10000);
Adc1=(((float)Adcdata[0]) * 3.3)/4096; //voltage value
Adc1_avg = Adc1_avg + Adc1; //average gain
// Adc2con = (Adcdata[1] & 0x0FFF);
Adc2=(((float)Adcdata[1]) * 3.3)/4096; //voltage
Adc2_avg = Adc2_avg + Adc2;//average phase
// Adc3con = (Adcdata[1] & 0x0FFF);
Adc3=(((float)Adcdata[2]) * 3.3)/4096; //voltage
Adc3_avg = Adc3_avg + Adc3;//average phase
}
Adc1_avg = Adc1_avg/20;
Adc2_avg = Adc2_avg/20;
Adc3_avg = Adc3_avg/20;
//Write to LCD
GLCD_TextSetPos(0,0);
GLCD_print("G:%.02f", Adc3_avg);
GLCD_TextSetPos(7,0);
GLCD_print("P1:%.02f", Adc2_avg);
GLCD_TextSetPos(17,0);
GLCD_print("P2:%.02f", Adc1_avg);
}; // Don't want to exit
}
the simplest would be to set up a timer and in the timer start the adc.
you can set the timer interrupt to the highest / higher priority.
In 8bit a 'library' is little more than some labels for registers,
plenty of libraries are used in the 8-bit world as well. you may be thinking about macros vs. libraries.
they seem much more commonly used in ARM...
due to their complexity and much steeper learning curve.
Should I just accept using the HAL from ST, try and restrict to the CMSIS or really try and continue to poke memory as in 8bit?
it is very difficult to NOT use cmsis to start up the device.
as to library, give it a shot and it may help you get started sooner / quicker.
I typically start with oem libraries but box them in a way that in the future I can replace them with my own code if I so desire.
with an oem library, you typically get quality code + working examples. they do come with a little bit overhead (1-2KB typical) but that's nothing for a typical ARM chip.
edit: here is what I typically do for all of my timer related routines -> they are identically structured but differently implemented on 8/16/32 chips:
ISR:
clear the flag;
_isrptr(); //execute the user handler
timer initialization (prescaler, period)
stop the timer
initialize the timer for user-specified prescaler and period
start the timer - interrupt still disabled
timer activation (isrptr)
_isrptr = isrptr; //install user handler
clear the flag
enable the interrupt
it allow the installation of a user-specified routine in the timer isr. in your case, that user installed routine could be turning on / off the adc, saving / processing the data, setting up a flag ...
this set of routines will be generic and can be ported from chip to chip. it can be implemented via libraries or you can roll your own - completely transparent to the user space code, meaning that you can switch between implementations freely without the need to change user code.
edit 2: here is an example. the following code,
tim1_init(1000, 1000); //run tim1 at 1000 prescaler + 1000 period = 1M
tim1_act(led_flp); //install user handler
runs on STM8S -> it sets up timer1 to overflow every 1M cycles and then on timer1 overflow it executes led_flp(), installed by tim1_act().
Substantially the same code runs on PIC, AVR, MSP430, PIC24, and various ARM chips. Some versions of it runs of ST's SPL (for STM8 and STM32), others run off Luminary's driverlib, and others run off hard coded code -> in the case of STM8, some off IAR's header files and others off the stm8s.h from the SPL, without actually using the SPL.
This is to show that the question of whether a vendor library should be used is quite moot, if you structure your code appropriately. To the user code, it doesn't make one bit of a difference whether those routines were implemented off a vendor library or rolled by modifying the registers. End of the day, it gets the job done, hopefully done right.
So instead of wasting time on if you should take a particular approach, just take one approach and make sure you do it right.
Best Answer
According to the datasheet, Your device has 3 separate ADC modules with 16 input channels (not 2 or 3 channels as suggested in the comments to your question).
From a snippet of your initialization code:
it appears that ADC1 & ADC2 are being used, and each of them is being configured to sample 2 different channels - so 4 in total.
ADC1 is sampling channel 1 & channel 3.
ADC2 is sampling channel 2 & channel 4.
See page 602 of the reference manual.
To work out the physical pins these channels refer to, we go back to the datasheet - The section of Table 16 on pages 71 & 72 tells us that channel 1 for all 3 ADC modules (ADC123_IN1) is on PC0/pin-8 of your STM32L476RG 64-pin package.
Similarly, channel 2 is PC1/pin-9 and channels 3 & 4 are PC2/pin-10 & PC3/pin-11.