04.05.2011
Precyzyjne opóźnienia w połączeniu z trybami oszczędzania energii w STM32, część 1
List. 2. Implementacja funkcji opóźniających umieszczona w pliku sleep.c
#include "check.h" #include "clock.h" #include "sleep.h" #include #include #include #include #include #include /* Licznik dla opóźnień mikrosekundowych, zwiększany z częstotliwością 1 MHz */ #define US_TIM TIM2 #define US_TIM_IRQn TIM2_IRQn #define US_RCC_APB1Periph_TIM RCC_APB1Periph_TIM2 #define US_TIM_IRQHandler TIM2_IRQHandler /* Licznik dla opóźnień milisekundowych, zwiększany z częstotliwością 2 kHz */ #define MS_TIM TIM3 #define MS_TIM_IRQn TIM3_IRQn #define MS_RCC_APB1Periph_TIM RCC_APB1Periph_TIM3 #define MS_TIM_IRQHandler TIM3_IRQHandler static unsigned usCalibration = 0; void USleep(unsigned count) { if (count > usCalibration) count -= usCalibration; else if (count > 0) count = 1; /* minimalna możliwa wartość */ else return; /* count == 0 */ US_TIM->CNT = 0; US_TIM->CCR1 = count; US_TIM->CR1 |= TIM_CR1_CEN; do __WFE(); while (US_TIM->CR1 & TIM_CR1_CEN); } void US_TIM_IRQHandler(void) { if (TIM_GetITStatus(US_TIM, TIM_IT_CC1)) { TIM_ClearITPendingBit(US_TIM, TIM_IT_CC1); US_TIM->CR1 &= ~TIM_CR1_CEN; __SEV(); } } void MSleep(unsigned count) { if (count > 0) { MS_TIM->CNT = 0; MS_TIM->CCR1 = count << 1; /* tyka 2 kHz */ MS_TIM->CR1 |= TIM_CR1_CEN; do __WFE(); while (MS_TIM->CR1 & TIM_CR1_CEN); } } void MS_TIM_IRQHandler(void) { if (TIM_GetITStatus(MS_TIM, TIM_IT_CC1)) { TIM_ClearITPendingBit(MS_TIM, TIM_IT_CC1); MS_TIM->CR1 &= ~TIM_CR1_CEN; __SEV(); } } /* #define PWR_REGULATOR_MODE PWR_Regulator_ON */ #define PWR_REGULATOR_MODE PWR_Regulator_LowPower static unsigned hclkMHz; void Sleep(unsigned count) { if (count > 0) { /* Alarm zgłaszany jest w ostatnim cyklu oscylatora RTC odpowiadającego sekundzie, w której alarm należy zgłosić, więc count <= czas uśpienia < count + 1. */ RTC_WaitForLastTask(); RTC_SetAlarm(RTC_GetCounter() + count); RTC_WaitForLastTask(); do PWR_EnterSTOPMode(PWR_REGULATOR_MODE, PWR_STOPEntry_WFE); while (!RTC_GetFlagStatus(RTC_IT_ALR)); RTC_WaitForLastTask(); RTC_ClearITPendingBit(RTC_IT_ALR); /* Po obudzeniu trzeba przywrócić pracę oscylatorów. */ ClockConfigure(hclkMHz); } } void Standby(unsigned time) { PWR_WakeUpPinCmd(ENABLE); RTC_WaitForLastTask(); RTC_SetAlarm(RTC_GetCounter() + time); RTC_WaitForLastTask(); PWR_EnterSTANDBYMode(); } int SleepConfigure(unsigned freqMHz) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_OCInitTypeDef TIM_OCInitStruct; NVIC_InitTypeDef NVIC_InitStruct; EXTI_InitTypeDef EXTI_InitStruct; hclkMHz = freqMHz; /* Kalibracja 1 us + 120 cykle zegara */ usCalibration = 1U + (120U / freqMHz); RCC_APB1PeriphClockCmd(US_RCC_APB1Periph_TIM | MS_RCC_APB1Periph_TIM | RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); TIM_TimeBaseStructInit(&TIM_TimeBaseStruct); TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_OCStructInit(&TIM_OCInitStruct); TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_Timing; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; /* Konfiguruj licznik mikrosekundowy na 1 MHz. */ TIM_TimeBaseStruct.TIM_Prescaler = freqMHz - 1; TIM_TimeBaseInit(US_TIM, &TIM_TimeBaseStruct); TIM_OC1Init(US_TIM, &TIM_OCInitStruct); TIM_OC1PreloadConfig(US_TIM, TIM_OCPreload_Disable); NVIC_InitStruct.NVIC_IRQChannel = US_TIM_IRQn; NVIC_Init(&NVIC_InitStruct); TIM_ClearITPendingBit(US_TIM, TIM_IT_CC1); TIM_ITConfig(US_TIM, TIM_IT_CC1, ENABLE); /* Konfiguruj licznik milisekundowy na 2 kHz. */ TIM_TimeBaseStruct.TIM_Prescaler = freqMHz * 500 - 1; TIM_TimeBaseInit(MS_TIM, &TIM_TimeBaseStruct); TIM_OC1Init(MS_TIM, &TIM_OCInitStruct); TIM_OC1PreloadConfig(MS_TIM, TIM_OCPreload_Disable); NVIC_InitStruct.NVIC_IRQChannel = MS_TIM_IRQn; NVIC_Init(&NVIC_InitStruct); TIM_ClearITPendingBit(MS_TIM, TIM_IT_CC1); TIM_ITConfig(MS_TIM, TIM_IT_CC1, ENABLE); /* Konfiguruj licznik sekundowy. */ PWR_BackupAccessCmd(ENABLE); if (PWR_GetFlagStatus(PWR_FLAG_SB) != RESET) { /* Mikrokontroler obudził się ze stanu czuwania. Nie ma potrzeby ponownego konfigurowania RTC - konfiguracja jest zachowywana po wybudzeniu ze stanu czuwania. */ PWR_ClearFlag(PWR_FLAG_SB); RTC_WaitForSynchro(); } else { /* Mikrokontroler został zresetowany, konfiguruj RTC, aby był taktowany kwarcem 32768 Hz i tykał z okresem 1 s. */ RCC_LSEConfig(RCC_LSE_ON); active_check(RCC_GetFlagStatus(RCC_FLAG_LSERDY), 10000000); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); RTC_WaitForSynchro(); RTC_SetPrescaler(32767); RTC_WaitForLastTask(); } /* Konfiguruj linię 17 EXTI (alarm RTC), aby narastające zbocze ustawiało rejestr zdarzenia */ EXTI_StructInit(&EXTI_InitStruct); EXTI_InitStruct.EXTI_Line = EXTI_Line17; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Event; EXTI_Init(&EXTI_InitStruct); RTC_WaitForLastTask(); RTC_ClearITPendingBit(RTC_IT_ALR); return 0; }