Correct usage of HAL functions in Arduino code

Post here all questions related to STM32 core if you can't find a relevant section!
kvv213
Posts: 38
Joined: Thu Dec 26, 2019 10:58 pm

Correct usage of HAL functions in Arduino code

Post by kvv213 »

Hello every one again!

I have a question according to a correct usage of HAL functions inside Arduino code.

For example, I'd like to use my custom board with STM32F103RE and use DMA processing of the chips ADC.

I have code that more or less works in cube code generated. And I took everything that concerns ADC from it and integrated into my arduino code:

Code: Select all

#include <Arduino.h>
#include "stm32f1xx_hal.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
unsigned short adc_value[8];
extern "C" void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {};

  /* Initializes the CPU, AHB and APB busses clocks */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler();
  }
  /* Initializes the CPU, AHB and APB busses clocks */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC | RCC_PERIPHCLK_USB;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
    Error_Handler();
  }
}
static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 8;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel 
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = ADC_REGULAR_RANK_3;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_4;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_6;
  sConfig.Rank = ADC_REGULAR_RANK_5;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_7;
  sConfig.Rank = ADC_REGULAR_RANK_6;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = ADC_REGULAR_RANK_7;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_9;
  sConfig.Rank = ADC_REGULAR_RANK_8;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
 HAL_ADCEx_Calibration_Start(&hadc1);
}
void setup() {
  HAL_Init();
  SystemClock_Config();
  MX_ADC1_Init();
  HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&adc_value, 8);
}
The code compiles but it falls into an infinite loop at the last string.

Actually it falls at this string (in stm32f1xx_hal_adc.c):
/* Set the DMA half transfer complete callback */
hadc->DMA_Handle->XferHalfCpltCallback = ADC_DMAHalfConvCplt;

I've googled for additional info but with no success. What can be wrong? I feel that there can be a problem with initialization of something (STM32 registers) but don't know what is the right way for HAL inside Arduino usage.
User avatar
fpiSTM
Posts: 1738
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: Correct usage of HAL functions in Arduino code

Post by fpiSTM »

Code: Select all

HAL_ADC_Init
will call

Code: Select all

HAL_ADC_MspInit
This HAL_ADC_MspInit is defined in the analog API of the core and use the pinmap feature to configure properly the GPIO.

I advise you to disable all Arduino analog using the 0HAL ADC.
To do this you can define:

Code: Select all

HAL_ADC_MODULE_ONLY
using the hal_conf_extra.h:

See https://github.com/stm32duino/wiki/wiki ... odule-only

Note that you do not need to call

Code: Select all

  HAL_Init();
  SystemClock_Config();
They are already called before.

Finally, I didn't see any DMA config ? (clock, IRQ,...)
kvv213
Posts: 38
Joined: Thu Dec 26, 2019 10:58 pm

Re: Correct usage of HAL functions in Arduino code

Post by kvv213 »

Did as you recommended :)

The result is the same. Infinite loop at:
/* Set the DMA half transfer complete callback */
hadc->DMA_Handle->XferHalfCpltCallback = ADC_DMAHalfConvCplt;

HAL_ADC_MODULE_ONLY enabled via -DHAL_ADC_MODULE_ONLY option to compiler.

The first time I indeed forgot about DMA settings. But its presence doesn't make any sense.

Code: Select all

#include <Arduino.h>
#include "stm32f1xx_hal.h"

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
unsigned short adc_value[8];


static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 8;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel 
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = ADC_REGULAR_RANK_3;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
	sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_4;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
  sConfig.Channel = ADC_CHANNEL_6;
  sConfig.Rank = ADC_REGULAR_RANK_5;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
  sConfig.Channel = ADC_CHANNEL_7;
  sConfig.Rank = ADC_REGULAR_RANK_6;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
  sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = ADC_REGULAR_RANK_7;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
	
  sConfig.Channel = ADC_CHANNEL_9;
  sConfig.Rank = ADC_REGULAR_RANK_8;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
 HAL_ADCEx_Calibration_Start(&hadc1);
}
static void MX_DMA_Init(void) 
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}

void setup() {
  // put your setup code here, to run once:
  Serial.setRx(UART_RX);
  Serial.setTx(UART_TX);
  Serial.begin(115200);  
  
  MX_DMA_Init();
  MX_ADC1_Init();
  
  HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&adc_value, 8);
  
  Serial.println("Go On!");  
}

void loop() {
  delay(1000);
  Serial.println("ADCs: "+((String)adc_value[0])+":"+(String)adc_value[1]+":"+(String)adc_value[2]+":"+(String)adc_value[3]+":"+(String)adc_value[4]+":"+(String)adc_value[4]+":"+(String)adc_value[6]+":"+(String)adc_value[7]);
}
User avatar
fpiSTM
Posts: 1738
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: Correct usage of HAL functions in Arduino code

Post by fpiSTM »

Well, it seems it is always incomplete, it misses the GPIO config, the ADC clock enable,...
And where the DMA1_Channel1_IRQHandler is defined?

By default it fallback to Default_Handler:

Code: Select all

  .thumb_set DMA1_Channel1_IRQHandler,Default_Handler
which is an infinite loop ;)
kvv213
Posts: 38
Joined: Thu Dec 26, 2019 10:58 pm

Re: Correct usage of HAL functions in Arduino code

Post by kvv213 »

I may be wrong, but as I understand the code:

HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

it enables DMA1_Channel1_IRQn IRQ-handler. That is defined at stm32f103xe.h driver as

DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 global Interrupt */

This code works if I compile it under Keil. But not under Arduino. Or I understand this wrong?
User avatar
fpiSTM
Posts: 1738
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: Correct usage of HAL functions in Arduino code

Post by fpiSTM »

Yes this enable the IRQ, but when the IRQ raised what should be done ?
kvv213
Posts: 38
Joined: Thu Dec 26, 2019 10:58 pm

Re: Correct usage of HAL functions in Arduino code

Post by kvv213 »

fpiSTM wrote: Mon Jan 06, 2020 1:55 pm Yes this enable the IRQ, but when the IRQ raised what should be done ?
In my understanding the following should happen:

1. Chip does some ADC measurments in infinite cycle.
2. When it measures enough it rises interrupt "TCIE: Transfer complete interrupt enable"
3. And in that interrupt it should populate my array adc_value.
4. When I need I can query my array and receive fresh values.

As I understand the problem is somewhere around "TCIE: Transfer complete interrupt enable". It is not defined or defined in a wrong way.

My problem is that I don't understand where I need to define the handler for this interrupt and why Cube code works without this. Arduino code is almost 100% copy of the cube code. :?

Added the following code:

Code: Select all

void DMA1_Channel1_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_adc1);
}
No success. Infinite loop at the same position for Half Transfer Complete call.... Should I define the handler for it somehow?
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: Correct usage of HAL functions in Arduino code

Post by ag123 »

HAL codes are complicated, i messed a little into it in VSCode
apparently when i typed HAL_ADC, VSCode didn't even find the completions,
the code stacks are very large and deep due to the multitude of supported skus, and the sheer set of features
good luck for the effort, i'd let go for now didn't quite get started with adc as well
perhaps i'd install Cube as well so that i'd have a reference of working codes
i'm wondering if somehow i need to set the include paths in the VSCode c++ extension, so far hit a blank as well
:lol:

i'm messing with my f401 pill board as that is what i've on my table.reading the ref manual
DMA_SCxR (dma configuration) register has 2 flags TCIE - transfer complete interrupt enable, HTIE - half transfer interrupt enable
so presumably both interrupt calls the same irq handler?
then in DMA_LISR and DMA_HISR interrupt status register there are 2 more flags TCIFx and HTIFx and one'd need to read and clear that flag when you are done handling the interrupt
it seemes cube patched a default interrupt handler so that it looks like everything is working
hope it helps, i'm way far from even reaching dma
can't find the codes for the vector table the actual definition itself
but do take note of this particular piece during system init
there is a particular define VECT_TAB_SRAM
https://github.com/stm32duino/Arduino_C ... 4xx.c#L198
if that is defined, my guess is it sets up the vector table in sram, otherwise in flash
what i do not understand is if it is

VECTOR_TAB -> HAL_handler -> user adc interrupt hook
or
VECTOR_TAB -> user adc interrupt hook

if it is the 2nd case, my guess is setting the hook would throw a hardfault. it is flash not sram.
for the 2nd case, define VECT_TAB_SRAM may solve the issue.
while the 1st case, it is actually better as that should point to a fixed HAL function while the hal function calls user code, presumably from a pointer in sram

add: found VECTOR_TAB in the start up assembly code
https://github.com/stm32duino/Arduino_C ... 1xc.s#L312
so try to define VECT_TAB_SRAM to see if it helps to resolve the issue

then the next big catch is would you after all need to call
VECTOR_TAB->DMA1_Stream0_IRQHandler = &MyDMAIRQHandler
(it seemed that would read g_pfnVectors->DMA1_Stream0_IRQHandler = &MyDMAIRQHandler; )
and i'm not sure if a interrupt vector can call a regular C or C++ function / method after all
:lol:
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: Correct usage of HAL functions in Arduino code

Post by ag123 »

hi fpistm,

i noted something strange in the codes for analogRead().

analogRead() seem to call a function adc_read_value()
https://github.com/stm32duino/Arduino_C ... log.c#L164

i found the declaration in the headers, but it seemed it isn't implemented. i.e. a search or reference jump did not find the definition
I'm not too sure if it is after all true
Last edited by ag123 on Tue Jan 07, 2020 3:06 pm, edited 1 time in total.
User avatar
fpiSTM
Posts: 1738
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: Correct usage of HAL functions in Arduino code

Post by fpiSTM »

I've answered you :mrgreen:
If the functions was not defined this simply not build as the linker will not find any reference.
Post Reply

Return to “General discussion”