앞장에서 Backup Domain 엑세스를 통해 Standby모드에서 값을 유지하는 방법을 알아보았다.
이번장에서는 이에 더해 메인전원 Vdd에 전원이 차단되어 Vbat를 통해 전원을 공급받을 때 Backup Domain을 유지하는 방법에 대해 알아보자.
|
1. 준비물
이번장에서는 이때까지 사용하던 NUCLEO F429Zi/F439Zi 보드 대신 NUCLEO L433RC-P를 사용하도록 하겠다.
NUCLEO F429Zi/F439Zi 보드의 경우 Vbat핀이 디폴트로 Vdd에 연결되어 있어서 배터리를 붙이기 위해서는 SB156번을 끊고 거기에 배터리를 연결해야 한다.
이걸 끊었다 붙였다하기 귀찮아서 그냥 NUCLEO L433RC-P보드를 사용했다. NUCLEO L433RC-P보드는 Vbat핀이 외부 핀으로 나와 있으므로 작업하기가 편하다.(단, STM32L433에는 Backup SRAM이 없다.)
그리고 원형 2032수은전지를 사용하므로 건전지와 건전지 홀더도 하나 필요하다.
2. Programmable voltage detector(PVD)
STM32에는 Vdd가 지정된 Threshold값을 벗어나는 경우 이를 통지받을 수 있는 방법이 있다.
PWR_CR2 레지스터(STM32F시리즈의 경우 PWR_CR)의 PLS[2:0] 에 임계값을 지정하고 PVDE비트를 1로 설정하면 이 기능을 사용할 수 있다.
이때 PWR_SR2레지스터(STM32F시리즈의 경우 PWR_CSR)의 PVDO비트가 0으로 설정된 경우 Vdd가 PLS[2:0]에 설정한 Threshold값보다 커질 때, 그리고 PVDO가 1로 설정된 경우 Vdd가 PLS[2:0]에 설정한 Threshold값보다 낮아질 때 EXTI16을 통한 인터럽트로 통지받을 수 있다.
단, PWR_SR2레지스터는 읽기전용이므로 S/W에서 임의로 값을 수정할 수는 없고 EXTI16에 관련된 레지스터를 수정해서 이를 적용할 수 있다.
EXTI16을 Rising Edge검출로 설정하면 PVDO는 1, 즉 PVD Threshold > Vdd일때, Falling Edge검출로 설정하면 PVDO는 0, 즉 Vdd > PVD Threshold일때 인터럽트가 발생한다.
따라서 Vdd의 전압이 낮아지거나 끊어진 경우 EXTI16 인터럽트의 Callback함수에서 Backup Domain에 내용을 저장하도록 할 수 있다.
HAL 라이브러리에는 이들을 설정하는 함수가 존재하는데 이들 함수를 살펴보자.
// STM32L시리즈
HAL_StatusTypeDef HAL_PWR_ConfigPVD(PWR_PVDTypeDef *sConfigPVD)
// STM32F시리즈
void HAL_PWR_ConfigPVD(PWR_PVDTypeDef *sConfigPVD)
STM32L시리즈와 STM32F시리즈의 프로토타입이 약간 다르다. L시리즈에는 리턴값이 있다.
리턴값은 아래와 같이 정의되어 있다.(하지만 실제 함수 내부에서는 HAL_OK이외에 다른 에러를 리턴하는 로직은 없다)
typedef enum
{
HAL_OK = 0x00,
HAL_ERROR = 0x01,
HAL_BUSY = 0x02,
HAL_TIMEOUT = 0x03
} HAL_StatusTypeDef;
그리고 PWR_PVDTypeDef 구조체형식인 sConfigPVD파라미터는 동일한데 구조체와 PVDLevel, Mode의 정의는 다음과 같다.
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;
/** @defgroup PWR_PVD_detection_level Programmable Voltage Detection levels
* @{
*/
#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) */
/** @defgroup PWR_PVD_Mode PWR PVD interrupt and event mode
* @{
*/
#define PWR_PVD_MODE_NORMAL ((uint32_t)0x00000000) /*!< Basic mode is used */
#define PWR_PVD_MODE_IT_RISING ((uint32_t)0x00010001) /*!< External Interrupt Mode with Rising edge trigger detection */
#define PWR_PVD_MODE_IT_FALLING ((uint32_t)0x00010002) /*!< External Interrupt Mode with Falling edge trigger detection */
#define PWR_PVD_MODE_IT_RISING_FALLING ((uint32_t)0x00010003) /*!< External Interrupt Mode with Rising/Falling edge trigger detection */
#define PWR_PVD_MODE_EVENT_RISING ((uint32_t)0x00020001) /*!< Event Mode with Rising edge trigger detection */
#define PWR_PVD_MODE_EVENT_FALLING ((uint32_t)0x00020002) /*!< Event Mode with Falling edge trigger detection */
#define PWR_PVD_MODE_EVENT_RISING_FALLING ((uint32_t)0x00020003) /*!< Event Mode with Rising/Falling edge trigger detection */
그리고 PVDE비트를 1로 설정해서 PVD를 활성화시키는 함수는 다음과 같다.
void HAL_PWR_EnablePVD(void)
3. 구현하기
PLS[2:0]에서 설정한 Threshold값 이하로 전압이 떨어지면 EXTI16인터럽트가 발생한다고 했었다.
이 인터럽트를 사용하려면 CubeMX의 NVIC화면에서 아래와 같이 "PVD/PVM1/PVM2/PVM3/PVM4 Interrupt through EXTI lines 16/35/36/37/38"에 체크를 해야 한다.(STM32F시리즈의 경우 "PVD interrupt through EXTI line 16")
이렇게 하면 stm32l4xx_it.c파일(STM32F시리즈는 stm32f4xx_it.c)에 PVD_PVM_IRQHandler()함수가 만들어지고 우리는 아래 함수를 재정의해서 필요한 로직을 만들면 된다.
__weak void HAL_PWR_PVDCallback(void)
STM32L시리즈의 Backup Domain에는 Backup SRAM이 없다. 따라서 정보는 RTC Backup레지스터 32개에만 저장을 할 수 있다. STM32F시리즈를 사용한다면 앞장에서 설명한대로 둘다 사용할 수 있으므로 앞장을 참고하라.
RTC Backup레지스터 엑세스 방법은 앞장과 동일하다. 이 예제에서는 앞장의 내용대로 BKP0R에만 기록하는 함수를 그대로 사용한다.
레지스터 크기가 4바이트이므로 uiCount를 uint32_t로 선언해 이 값을 유지시킬 것이다.
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
// RTC Backup레지스터에 저장될 전역변수.
uint32_t uiCount = 0;
int __io_putchar(int ch)
{
// Write character to ITM ch.0
ITM_SendChar(ch);
return(ch);
}
// RTC Backup레지스터 BKP0R에 데이터 쓰기
void writeRTCBackupReg0(uint32_t uiValue)
{
HAL_PWR_EnableBkUpAccess();
RTC->BKP0R = uiValue;
HAL_PWR_DisableBkUpAccess();
}
// RTC Backup레지스터 BKP0R에서 데이터 읽기
uint32_t readRTCBackupReg0()
{
return RTC->BKP0R;
}
// EXTI16 Callback함수
void HAL_PWR_PVDCallback(void)
{
writeRTCBackupReg0(uiCount);
}
/* USER CODE END 0 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
PWR_PVDTypeDef config;
config.PVDLevel = PWR_PVDLEVEL_6; // Vdd Threshold값을 2.9V로 설정
config.Mode = PWR_PVD_MODE_IT_RISING; // Threshold값 이하로 떨어질때 인터럽트 발생(즉, PVDO=1로 설정)
HAL_PWR_ConfigPVD(&config); // PVD설정
HAL_PWR_EnablePVD(); // PVD Enable
uiCount = readRTCBackupReg0(); // RTC Backup레지스터에서 값읽기
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin);
printf(">> Count : %ld\n", uiCount++);
HAL_Delay(500);
}
/* USER CODE END 3 */
}
코딩후 Vbat핀과 GND핀에 배터리를 연결하고 F11키를 눌러 디버깅 모드로 실행하자.
SWV Console창에서 카운트가 증가하는걸 확인하고 디버그를 종료한 뒤 PC와 연결된 USB를 빼서 전원을 차단하자.
그리고 USB를 다시 연결하고 F11키를 눌러 디버깅 모드를 재개해보면 uiCount값이 그대로 유지됨을 볼 수 있다.
4. 마무리
3개의 장에 걸쳐 STM32의 전원관리에 대해 알아보았다.
저전력 모드로 진입하거나 깨어날 때의 처리 방법과 전원 모니터링 방법인 PVD에 관해서도 알아보았다.
사용방법은 어렵지 않으나 실제 프로젝트에서 정교하게 전원을 관리하는 건 생각을 많이 해야 할 부분이다. 제시된 방법을 토대로 각자 고민해보기 바란다.
'임베디드 > STM32' 카테고리의 다른 글
19. [STM32] Timer의 기초 (0) | 2024.11.19 |
---|---|
18. [STM32] Clock (0) | 2024.11.14 |
16. [STM32] Power - Backup Domain Access (0) | 2024.11.07 |
15. [STM32] Power - Low Power Mode (0) | 2024.11.04 |
14. [STM32] Interrupt 구현하기 (0) | 2024.10.10 |