This article explains how to structure the software to develop an STM32Cube example.
1. Introduction[edit | 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 | edit source]
2.1. main(void)[edit | 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 | 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 | 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 | 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 | 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 | 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 | 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)