Last edited 6 months ago

How to structure the software to develop STM32Cube examples

Applicable for STM32MP15x lines

This article explains how to structure the software to develop an STM32Cube example.

1. Introduction[edit source]

The following code samples are extracted from the ADC_SingleConversion_TriggerTimer_DMA example and adapted for this article. ADC is used to convert a single channel at each timer trigger, then conversion data is transferred by DMA into an array, then a waveform is generate indefinitely (circular mode).

This example deals with the following resources and access attribute for each CPU in when used in production mode:

  • Resources fully managed by Arm® Cortex®-M4 (write/read access rights): ADC2, DAC1, TIM2, DMA2, RCC/PWR dedicated MCU registers
  • Resources fully Managed by Arm® Cortex®-A7 (write/read access rights): ETZPC, VREFBUF, I2C4 for PMIC configuration and RCC/PWR common registers
  • Shared resources managed by Arm® Cortex®-M4 and Arm® Cortex®-A7 with Hardware Semaphore: GPIOA and EXTI
  • Shared resources managed by Arm® Cortex®-M4 and Arm® Cortex®-A7: IPCC and HSEM
  • Resources managed by Arm® Cortex®-M4 with read access only: ETZPC, VREFBUF and RCC/PWR common registers (to calculate frequency for example)


In engineering mode : To improve successive debug session, it is recommended to add HAL_RCC_DeInit() before SystemClock_Config().

2. Recommended software structure[edit source]

2.1. main(void)[edit source]

Source code available in file main.c

int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */

  /* Initialize the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  if(IS_ENGINEERING_BOOT_MODE())
  {
    HAL_RCC_DeInit();
    /* Configure the system clock */
    SystemClock_Config();
  }
  /* USER CODE END Init */

  /* IPCC initialisation (used by OpenAMP)*/  
   MX_IPCC_Init();

  /* OpenAmp initialisation ---------------------------------*/
  if(!IS_ENGINEERING_BOOT_MODE())
  {
    MX_OPENAMP_Init(RPMSG_REMOTE, NULL);
  }

  /* Resource Manager Utility initialisation ---------------------------------*/
  MX_RESMGR_UTILITY_Init();

  /* USER CODE BEGIN SysInit */
  if(IS_ENGINEERING_BOOT_MODE())
  {
    /* Configure PMIC */
    BSP_PMIC_Init();
    BSP_PMIC_InitRegulators();

    /* Configure VREFBUF */
    __HAL_RCC_VREF_CLK_ENABLE();
    HAL_SYSCFG_VREFBUF_HighImpedanceConfig(SYSCFG_VREFBUF_HIGH_IMPEDANCE_DISABLE);
    HAL_SYSCFG_EnableVREFBUF();
  }
  /* USER CODE END SysInit */

/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_DAC1_Init();
  MX_TIM2_Init();
  MX_ADC2_Init();

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
  /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

2.2. HAL_MspInit(void)[edit source]

Source code available in file stm32mp1xx_hal_msp.c

void HAL_MspInit(void)
{
  /* USER CODE BEGIN MspInit 0 */

  /* USER CODE END MspInit 0 */

  /*HW semaphore Clock enable*/
  __HAL_RCC_HSEM_CLK_ENABLE();

  /* USER CODE BEGIN MspInit 1 */

  /* USER CODE END MspInit 1 */
}

2.3. HAL_MspDeInit(void)[edit source]

This function is not present for the ADC example.

In production mode, Arm® Cortex®-M4 firmware could be restarted without platform system reset and in that case, it is necessary to de-initialize the peripherals, clocks or interrupts used by Arm® Cortex®-M4 firmware before stopping the example.

For example , FreeRTOS_ThreadCreation example where the timer (TIM2) must be stopped in order to be able to restart the example.
This function of HAL_MspDeInit can be called following a stop notification from the Arm® Cortex®-A to the Arm® Cortex®-M of an imminent stop via CoproSync. In this case, the function CoproSync_ShutdownCb is called, and HAL_MspDeInit executed.


Source code available in file stm32mp1xx_hal_msp.c

void HAL_MspDeInit(void)
{
  /* USER CODE BEGIN MspDeInit 0 */

  /* USER CODE END MspDeInit 0 */

  /* Disable IRQ */
  HAL_NVIC_DisableIRQ(TIM2_IRQn);

  /* Disable SYSCFG clock */
  __HAL_RCC_SYSCFG_CLK_DISABLE();

  /* Disable TIM2 clock */
  __HAL_RCC_TIM2_CLK_DISABLE();

  /* USER CODE BEGIN MspDeInit 1 */

  /* USER CODE END MspDeInit 1 */
}

2.4. HAL_PPP_MspInit(PPP_HandleTypeDef *hppp)[edit source]

Source code available in file stm32mp1xx_hal_msp.c


Only HAL_ADC_MspInit() and HAL_DAC_MspInit() are shown to cover all cases (Clock source, GPIO, DMA, NVIC)


void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
  RCC_PeriphCLKInitTypeDef  PeriphClkInit;
  if(hadc->Instance==ADC2)
  {
  /* USER CODE BEGIN ADC2_MspInit 0 */
  if(IS_ENGINEERING_BOOT_MODE())
  {
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PER;
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
  }
  /* USER CODE END ADC2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_ADC12_CLK_ENABLE();
  
    /* ADC2 DMA Init */
    /* ADC2 Init */
    hdma_adc2.Instance = DMA2_Stream0;
    hdma_adc2.Init.Request = DMA_REQUEST_ADC2;
    hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc2.Init.Mode = DMA_CIRCULAR;
    hdma_adc2.Init.Priority = DMA_PRIORITY_LOW;
    hdma_adc2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_adc2) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc2);

    /* ADC2 interrupt Init */
    HAL_NVIC_SetPriority(ADC2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(ADC2_IRQn);
  /* USER CODE BEGIN ADC2_MspInit 1 */

  /* USER CODE END ADC2_MspInit 1 */
  }

}

Example of PERIPH_LOCK/PERIPH_UNLOCK usage for shared GPIOA resource below:

void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(hdac->Instance==DAC1)
  {
  /* USER CODE BEGIN DAC1_MspInit 0 */

  /* USER CODE END DAC1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_DAC12_CLK_ENABLE();
	
    /**DAC1 GPIO Configuration    
    PA4     ------> DAC1_OUT1 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    PERIPH_LOCK(GPIOA);
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    PERIPH_UNLOCK(GPIOA);

  /* USER CODE BEGIN DAC1_MspInit 1 */

  /* USER CODE END DAC1_MspInit 1 */
  }

}

2.5. HAL_PPP_MspDeInit(PPP_HandleTypeDef *hppp)[edit source]

In production mode, as Arm® Cortex®-M4 firmware can be restarted without platform system reset, It is recommended to de-initialize the peripheral. This is the case, for the ADC example : reset the peripheral, disable the GPIO Clock, disable the dma and the NVIC configuration.
This function of HAL_ADC_MspDeInit can be called before the HAL_ADC_MspInit to secure a clean state before the initialization.


Source code available in file stm32mp1xx_hal_msp.c


Only HAL_ADC_MspDeInit() is shown to cover all cases (Clock source, GPIO, DMA, NVIC)


void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc)
{
  /*##-1- Reset peripherals ##################################################*/
  __HAL_RCC_ADC12_FORCE_RESET()
  __HAL_RCC_ADC12_RELEASE_RESET()

  /*##-2- Disable peripherals and GPIO Clocks ################################*/
  /* De-initialize GPIO pin of the selected ADC channel */
  PERIPH_LOCK(GPIOA);
  HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4);
  PERIPH_UNLOCK(GPIOA);

  /*##-3- Disable the DMA ####################################################*/
  /* De-Initialize the DMA associated to the peripheral */
  if(hadc->DMA_Handle != NULL)
  {
    HAL_DMA_DeInit(hadc->DMA_Handle);
  }

  /*##-4- Disable the NVIC ###################################################*/
  /* Disable the NVIC configuration for ADC interrupt */
  HAL_NVIC_DisableIRQ(ADC2_IRQn);
  
  /* Disable the NVIC configuration for DMA interrupt */
  HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);

}

2.6. HAL_PPP_Init(PPP_HandleTypeDef *hppp)[edit source]

Source code available in file main.c

static void MX_ADC2_Init(void)
{

  if (ResMgr_Request(RESMGR_ID_ADC2, RESMGR_FLAGS_ACCESS_NORMAL | \
                   RESMGR_FLAGS_CPU_SLAVE , 0, NULL) != RESMGR_OK)
  {
    /* USER CODE BEGIN RESMGR_UTILITY_ADC2 */
    Error_Handler();
    /* USER CODE END RESMGR_UTILITY_ADC2 */
  }
  /* USER CODE BEGIN ADC2_Init 0 */

  /* USER CODE END ADC2_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC2_Init 1 */

  /* USER CODE END ADC2_Init 1 */
  /** Common config 
  */
  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV2;
  hadc2.Init.Resolution            = ADC_RESOLUTION_12B;
  hadc2.Init.ScanConvMode          = ADC_SCAN_DISABLE;
  hadc2.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait      = DISABLE;
  hadc2.Init.ContinuousConvMode    = DISABLE;
  hadc2.Init.NbrOfConversion       = 1;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.NbrOfDiscConversion   = 1;
  hadc2.Init.ExternalTrigConv      = ADC_EXTERNALTRIG_T2_TRGO;
  hadc2.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc2.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
  hadc2.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;
  hadc2.Init.OversamplingMode      = DISABLE;
  if (HAL_ADC_DeInit(&hadc2) != HAL_OK)                    {{Red|<--- will call HAL_ADC_MSPDeInit}}
  {
    /* ADC Deinitialization error */
    Error_Handler();
  }
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    /* ADC initialization error */
    Error_Handler();
  }

  /** Configure Regular Channel 
  */
  sConfig.Channel      = ADC_CHANNEL_16;    /* ADC channel selection */
  sConfig.Rank         = ADC_REGULAR_RANK_1;    /* ADC group regular rank in which is mapped the selected ADC channel */
  sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;    /* ADC channel sampling time */
  sConfig.SingleDiff   = ADC_SINGLE_ENDED;  /* ADC channel differential mode */
  sConfig.OffsetNumber = ADC_OFFSET_NONE;    /* ADC channel affected to offset number */
  sConfig.Offset       = 0;     /* Parameter discarded because offset correction is disabled */

  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC2_Init 2 */

  /* USER CODE END ADC2_Init 2 */

}

2.7. PERIPH_LOCK(__Periph__)[edit source]

PERIPH_LOCK/PERIPH_UNLOCK service is a mandatory user code service in production mode
This service is used and available in STM32CubeMP1 examples (src/inc directories) but it is not part of a dedicated component (HAL, Utilities, Middleware, CMSIS Device).

  • in the file lock_resource.h , the macro PERIPH_LOCK is exported for user code usage and mapped to an user function
#define PERIPH_LOCK(__Periph__)       Periph_Lock(__Periph__, LOCK_RESOURCE_TIMEOUT)
  • in the file lock_resource.c , the source code of Periph_Lock function using HSEM HAL
LockResource_Status_t Periph_Lock(void* Peripheral, uint32_t Timeout)
{
  uint32_t tickstart = 0U;
  LockResource_Status_t ret = LOCK_RESOURCE_STATUS_OK;

  /* Init tickstart for timeout management*/
  tickstart = HAL_GetTick();

  /* Try to Take HSEM  assigned to the Peripheral */
  while (HAL_HSEM_FastTake(GET_HSEM_SEM_INDEX(Peripheral)) != HAL_OK)
  {

    if ((Timeout == 0U) || ((HAL_GetTick() - tickstart) > Timeout))
    {
       ret = LOCK_RESOURCE_STATUS_TIMEOUT;
       Error_Handler();
    }
  }

  return ret;
}
  • Hardware semaphore index versus peripheral shall be aligned with software running on Arm® Cortex®-A
  • Below, the configuration done in the file lock_resource.c to work with OpenSTLinux
#define GET_HSEM_SEM_INDEX(__Peripheral__)   (uint8_t)(((GPIO_TypeDef *)(__Peripheral__) == (GPIOA))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOB))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOC))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOD))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOE))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOF))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOG))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOH))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOI))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOJ))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOK))? 0U :\
                                              ((GPIO_TypeDef *)(__Peripheral__) == (GPIOZ))? 0U :\
                                              ((EXTI_TypeDef *)(__Peripheral__) == (EXTI))?  1U : HSEM_SEMID_MAX + 1U)