List. 2. Implementacja funkcji opóźniających umieszczona w pliku sleep.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
#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; } |