What I want to accomplish
So I want to accomplish the following:
I have 3 FreeRTOS-Threads which all shall read one of 3 (5) channels of my ADC. I want to poll the ADC. The Threads then enter the read value into a FreeRTOS-queue.
My code so far
I have the following functions:
ADC initialisation
void MX_ADC_Init(void)
{
hadc.Instance = ADC;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4;
hadc.Init.ScanConvMode = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.LowPowerAutoWait = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
for(int ch = 0; ch < GPIO_AI_COUNT; ch++)
{
ADC_Select_Ch(ch);
}
}
GPIO initialisation
GPIO_InitTypeDef GpioInitStruct = {0};
GpioInitStruct.Pin = GPIO_AI1_PIN | GPIO_AI2_PIN | GPIO_AI3_PIN | GPIO_AI4_PIN | GPIO_AI5_PIN;
GpioInitStruct.Pull = GPIO_NOPULL;
GpioInitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GpioInitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOB, &GpioInitStruct);
Where the GPIO_AI2_PIN
definition is defined as:
/* Analog Inputs ----------------------------------------------------------- */
#define GPIO_AI_COUNT 5
#define GPIO_AI1_PIN GPIO_PIN_3
#define GPIO_AI1_PORT GPIOB
#define GPIO_AI1_CH ADC_CHANNEL_2 /* ADC_IN2, Datasheet P. 51 */
#define GPIO_AI2_PIN GPIO_PIN_4
#define GPIO_AI2_PORT GPIOB
#define GPIO_AI2_CH ADC_CHANNEL_3 /* ADC_IN3, Datasheet P. 51 */
#define GPIO_AI3_PIN GPIO_PIN_14
#define GPIO_AI3_PORT GPIOB
#define GPIO_AI3_CH ADC_CHANNEL_1 /* ADC_IN1, Datasheet P. 55 */
#define GPIO_AI4_PIN GPIO_PIN_13
#define GPIO_AI4_PORT GPIOB
#define GPIO_AI4_CH ADC_CHANNEL_0 /* ADC_IN0, Datasheet P. 55 */
#define GPIO_AI5_PIN GPIO_PIN_2
#define GPIO_AI5_PORT GPIOB
#define GPIO_AI5_CH ADC_CHANNEL_4 /* ADC_IN4, Datasheet P. 54 */
Changing channel
void ADC_Select_Ch(uint8_t channelNb)
{
adcConf.Rank = ADC_RANKS[channelNb];
adcConf.Channel = GPIO_AI_CH[channelNb];
adcConf.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc, &adcConf) != HAL_OK)
{
Error_Handler();
}
}
Where ADC_RANKS
and GPIO_AI_CH
are static arrays of the channels and ranks I want to use. The ranks increase with every channel.
Reading a channel
uint32_t ADC_Read_Ch(uint8_t channelNb)
{
uint32_t adc_value = 0;
ADC_Select_Ch(channelNb);
HAL_ADC_Start(&hadc);
if(HAL_OK == HAL_ADC_PollForConversion(&hadc, ADC_CONVERSION_TIMEOUT))
{
adc_value = HAL_ADC_GetValue(&hadc);
}
HAL_ADC_Stop(&hadc);
printf("Ch%d / %x) %d\r\n", channelNb, adcConf.Channel, adc_value);
return adc_value;
}
The problem
No matter what I try, the ADC only ever reads in the channel before the last channel I defined. Every time a conversion happens, the method HAL_ADC_GetValue(...)
returns only the value of one channel, one, which I haven't even "selected" with my method.
What I've tried so far
I tried several different things:
- Change NumberOfConversions
- Change ScanMode, ContinuousConvMode, Overrun, EOCSelection, etc.
- Use only Rank "1" when choosing a channel
- Not use
HAL_ADC_Stop(...)
, that however resulted in a failure (error handler was called) - Using the read functions etc. in the
main()
, not in a FreeRTOS thread - this also resulted in only one channel being read. - Change GPIO setup
- Make the
adcConfig
global and public, so that maybe the config is shared among the channel selections. - Different clock settings
- "Disabling" all other channels but the one I want to use (*)
- Several other things which I've already forgotten
There seems to be one big thing I completely miss. Most of the examples are with one of the STM32Fxx microcontrollers, so maybe the ADC hardware is not the same and I can't do it this way. However, since I am using HAL, I should be able to do it this way. It would be weird, if it wouldn't be somehow the same across different uC families.
I really want to use polling, and ask one channel of the ADC by using some kind of channel selection, so that I can read them in different FreeRTOS tasks.
Disabling channels
I tried "disabling" channels but the one I've used with this function:
void ADC_Select_Ch(uint8_t channelNb)
{
for(int ch = 0; ch < GPIO_AI_COUNT; ch++)
{
adcConf.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
adcConf.Channel = GPIO_AI_CH[ch];
adcConf.Rank = ADC_RANK_NONE;
if (HAL_ADC_ConfigChannel(&hadc, &adcConf) != HAL_OK)
{
Error_Handler();
}
}
adcConf.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
adcConf.Channel = GPIO_AI_CH[channelNb];
if (HAL_ADC_ConfigChannel(&hadc, &adcConf) != HAL_OK)
{
Error_Handler();
}
}
Can anyone help me? I'm really stuck, and the Reference Manual does not provide a good "guide" on how to use it. Only technical information, lol.
Thank you!