Last edited 10 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)