[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]
PVD (Power Voltage Detector):
PVD jest to wewnętrzny blok w który zostały wyposażone wszystkie mikrokontrolery STM32Fxxxx. Pozwala na generowanie przerwania, po przekroczeniu zdefiniowanych progów napięć. Progi wyzwalania zdefiniowane od napięcia 2.0V do 2.9V z krokiem około 0.1V.
Przerwania może być generowane w dwóch kierunkach. Kiedy wartość napięcia spadnie poniżej danego progu lub/i gdy napięcie wzrośnie ponad zadaną wartość.
Linia PWD jest wewnętrznie podłączona do linii EXTI16.
Definicja poszczególnych poziomów umieszczona w bibliotekach STM wygląda następująco:
- #define PWR_PVDLEVEL_0 PWR_CR2_PLS_LEV0 /*!< PVD threshold around 2.0 V */
- #define PWR_PVDLEVEL_1 PWR_CR2_PLS_LEV1 /*!< PVD threshold around 2.2 V */
- #define PWR_PVDLEVEL_2 PWR_CR2_PLS_LEV2 /*!< PVD threshold around 2.4 V */
- #define PWR_PVDLEVEL_3 PWR_CR2_PLS_LEV3 /*!< PVD threshold around 2.5 V */
- #define PWR_PVDLEVEL_4 PWR_CR2_PLS_LEV4 /*!< PVD threshold around 2.6 V */
- #define PWR_PVDLEVEL_5 PWR_CR2_PLS_LEV5 /*!< PVD threshold around 2.8 V */
- #define PWR_PVDLEVEL_6 PWR_CR2_PLS_LEV6 /*!< PVD threshold around 2.9 V */
- #define PWR_PVDLEVEL_7 PWR_CR2_PLS_LEV7 /*!< External input analog voltage (compared internally to VREFINT) or 3.0V*/
Próg PWR_PDLEVEL_7 może się różnić w zależności od rodziny układów.
Dane z poszczególnymi progami wyzwalania przechowywane są w rejestrze PWR->CR.
Za PVD odpowiedzialne są bity 7-5 (poziom wyzwalania) oraz 4 (uruchomienie układu sprawdzania):
Gdy napięcie jest mniejsze niż próg wyzwalania to następuje ustawienie bit 2 (PVDO) w rejestrze PWR_CSR. Bit ten jest utrzymywany na 0 gdy napięcie będzie powyżej progu wyzwalania. Dodatkowo należy pamiętać, że w trybie Standby Mode PVD jest wyłączone:
Uruchomienie PVD w CubeMx:
W tej części procedura uruchomienia sprowadza się jedynie do uruchomienia przerwania wyzwalanego od PVD:
Kod HAL:
Po wygenerowaniu projektu w kodzie zostają umieszczone tylko funkcje odpowiedzialne za obsługę przerwania:
- void PVD_IRQHandler(void) //stm32f4xx_it.c
- {
- /* USER CODE BEGIN PVD_IRQn 0 */
- /* USER CODE END PVD_IRQn 0 */
- HAL_PWR_PVD_IRQHandler();
- /* USER CODE BEGIN PVD_IRQn 1 */
- /* USER CODE END PVD_IRQn 1 */
- }
- void HAL_PWR_PVD_IRQHandler(void) //stm32f4xx_hal_pwr.c
- {
- /* Check PWR Exti flag */
- if(__HAL_PWR_PVD_EXTI_GET_FLAG() != RESET)
- {
- /* PWR PVD interrupt user callback */
- HAL_PWR_PVDCallback();
- /* Clear PWR Exti pending bit */
- __HAL_PWR_PVD_EXTI_CLEAR_FLAG();
- }
- }
Do programu należy jeszcze dorzucić ustawienie oraz uruchomienie PVD, uruchomienie przerwania od PVD.
Domyślnie w rejestrze PWR_CR bity PLS ora PVDE ustawione są na 0.
W programie wykorzystującym biblioteki HAL'a do konfiguracji można użyć struktury zdefiniowanej w pliku stm32f4xx_hal_pwr.h
- typedef struct
- {
- uint32_t PVDLevel; /*!< PVDLevel: Specifies the PVD detection level.
- This parameter can be a value of @ref PWR_PVD_detection_level */
- uint32_t Mode; /*!< Mode: Specifies the operating mode for the selected pins.
- This parameter can be a value of @ref PWR_PVD_Mode */
- }PWR_PVDTypeDef;
Uruchomienie PVD wraz z przerwaniem oraz poziomem wyzwalania wygląda następująco:
- void PVD_Enable(PVD_Level_t Level, PVD_Inter_Trigger_t Trigger)
- {
- PWR_PVDTypeDef PVD_Configuration;
- __HAL_RCC_PWR_CLK_ENABLE();
- HAL_NVIC_SetPriority(PVD_IRQn, PVD_NVIC_PRIORITY, PVD_NVIC_SUBPRIORITY);
- HAL_NVIC_EnableIRQ(PVD_IRQn);
- PVD_Configuration.PVDLevel = (uint32_t)Level;
- PVD_Configuration.Mode = (uint32_t)Trigger;
- HAL_PWR_ConfigPVD(&PVD_Configuration);
- HAL_PWR_EnablePVD();
- }
W powyższej funkcji uruchomiony zostaje zegar PWR, ustawione priorytety przerwań od PVD, uruchomienie przerwania od PVD, konfiguracja poziomów i trybu wyzwalania. Na samym końcu należy włączyć blok
Ustawienie PVD zdefiniowane w bibliotece HAL'a wygląda następująco:
- void HAL_PWR_ConfigPVD(PWR_PVDTypeDef *sConfigPVD)
- {
- /* Check the parameters */
- assert_param(IS_PWR_PVD_LEVEL(sConfigPVD->PVDLevel));
- assert_param(IS_PWR_PVD_MODE(sConfigPVD->Mode));
- /* Set PLS[7:5] bits according to PVDLevel value */
- MODIFY_REG(PWR->CR, PWR_CR_PLS, sConfigPVD->PVDLevel);
- /* Clear any previous config. Keep it clear if no event or IT mode is selected */
- __HAL_PWR_PVD_EXTI_DISABLE_EVENT();
- __HAL_PWR_PVD_EXTI_DISABLE_IT();
- __HAL_PWR_PVD_EXTI_DISABLE_RISING_EDGE();
- __HAL_PWR_PVD_EXTI_DISABLE_FALLING_EDGE();
- /* Configure interrupt mode */
- if((sConfigPVD->Mode & PVD_MODE_IT) == PVD_MODE_IT)
- {
- __HAL_PWR_PVD_EXTI_ENABLE_IT();
- }
- /* Configure event mode */
- if((sConfigPVD->Mode & PVD_MODE_EVT) == PVD_MODE_EVT)
- {
- __HAL_PWR_PVD_EXTI_ENABLE_EVENT();
- }
- /* Configure the edge */
- if((sConfigPVD->Mode & PVD_RISING_EDGE) == PVD_RISING_EDGE)
- {
- __HAL_PWR_PVD_EXTI_ENABLE_RISING_EDGE();
- }
- if((sConfigPVD->Mode & PVD_FALLING_EDGE) == PVD_FALLING_EDGE)
- {
- __HAL_PWR_PVD_EXTI_ENABLE_FALLING_EDGE();
- }
- }
Kod LL:
Tutaj należało wprowadzić kilka zmian tak aby więcej operacji była wykonywana bezpośrednio na rejestrach.
Funkcja uruchamiająca PVD z zadanymi parametrami:
- void PVD_Enable(PVD_Level_t Level, PVD_Inter_Trigger_t Trigger)
- {
- LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
- NVIC_SetPriority(PVD_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),PVD_NVIC_PRIORITY, PVD_NVIC_SUBPRIORITY));
- NVIC_EnableIRQ(PVD_IRQn);
- LL_PWR_SetPVDLevel((uint32_t)Level);
- //Clear prev config
- (EXTI->EMR &= ~(((uint32_t)EXTI_IMR_MR16)));
- (EXTI->IMR &= ~(((uint32_t)EXTI_IMR_MR16)));
- CLEAR_BIT(EXTI->RTSR, ((uint32_t)EXTI_IMR_MR16));
- CLEAR_BIT(EXTI->FTSR, ((uint32_t)EXTI_IMR_MR16));
- /* Configure interrupt mode */
- (EXTI->IMR |= (((uint32_t)EXTI_IMR_MR16)));
- /* Configure the edge */
- if(Trigger == PVD_Trigger_Rising)
- {
- SET_BIT(EXTI->RTSR, ((uint32_t)EXTI_IMR_MR16)); //Rising edge
- }
- else if(Trigger == PVD_Trigger_Falling)
- {
- SET_BIT(EXTI->FTSR, ((uint32_t)EXTI_IMR_MR16)); //Falling edge
- }
- else //both edge
- {
- SET_BIT(EXTI->RTSR, ((uint32_t)EXTI_IMR_MR16)); //Rising edge
- SET_BIT(EXTI->FTSR, ((uint32_t)EXTI_IMR_MR16)); //Falling edge
- }
- //Enable PVD
- LL_PWR_EnablePVD();
- }
Obsługa przerwania wygląda następująco:
- void PVD_IRQHandler(void)
- {
- if ((EXTI->PR & (((uint32_t)EXTI_IMR_MR16))) != RESET)
- {
- PVD_Handler(PWR_Get_PVDOutput());
- (EXTI->PR = (((uint32_t)EXTI_IMR_MR16)));
- }
- }
Biblioteki opisane w tym poście można pobrać z dysku Google pod tym linkiem.
[1] https://www.st.com/resource/en/reference_manual/dm00031020-stm32f405-415-stm32f407-417-stm32f427-437-and-stm32f429-439-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf
[2] https://www.st.com/resource/en/datasheet/dm00037051.pdf
Dokumentacja:
[1] https://www.st.com/resource/en/reference_manual/dm00031020-stm32f405-415-stm32f407-417-stm32f427-437-and-stm32f429-439-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf
[2] https://www.st.com/resource/en/datasheet/dm00037051.pdf